Skip to content

Commit b57cfc9

Browse files
authored
Merge pull request github#3804 from aschackmull/dataflow/dispatch-refactor
Dataflow: Refactor dispatch with call context.
2 parents d297ce2 + 0bd81eb commit b57cfc9

File tree

8 files changed

+278
-161
lines changed

8 files changed

+278
-161
lines changed

cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowDispatch.qll

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -53,26 +53,13 @@ private predicate functionSignature(Function f, string qualifiedName, int nparam
5353
}
5454

5555
/**
56-
* Holds if the call context `ctx` reduces the set of viable dispatch
57-
* targets of `ma` in `c`.
56+
* Holds if the set of viable implementations that can be called by `call`
57+
* might be improved by knowing the call context.
5858
*/
59-
predicate reducedViableImplInCallContext(Call call, Function f, Call ctx) { none() }
59+
predicate mayBenefitFromCallContext(Call call, Function f) { none() }
6060

6161
/**
62-
* Gets a viable dispatch target of `ma` in the context `ctx`. This is
63-
* restricted to those `ma`s for which the context makes a difference.
62+
* Gets a viable dispatch target of `call` in the context `ctx`. This is
63+
* restricted to those `call`s for which a context might make a difference.
6464
*/
65-
Function prunedViableImplInCallContext(Call call, Call ctx) { none() }
66-
67-
/**
68-
* Holds if flow returning from `m` to `ma` might return further and if
69-
* this path restricts the set of call sites that can be returned to.
70-
*/
71-
predicate reducedViableImplInReturn(Function f, Call call) { none() }
72-
73-
/**
74-
* Gets a viable dispatch target of `ma` in the context `ctx`. This is
75-
* restricted to those `ma`s and results for which the return flow from the
76-
* result to `ma` restricts the possible context `ctx`.
77-
*/
78-
Function prunedViableImplInCallContextReverse(Call call, Call ctx) { none() }
65+
Function viableImplInCallContext(Call call, Call ctx) { none() }

cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,67 @@ private module Cached {
330330
import Final
331331
}
332332

333+
import FlowThrough
334+
335+
cached
336+
private module DispatchWithCallContext {
337+
/**
338+
* Holds if the call context `ctx` reduces the set of viable run-time
339+
* dispatch targets of call `call` in `c`.
340+
*/
341+
cached
342+
predicate reducedViableImplInCallContext(DataFlowCall call, DataFlowCallable c, DataFlowCall ctx) {
343+
exists(int tgts, int ctxtgts |
344+
mayBenefitFromCallContext(call, c) and
345+
c = viableCallable(ctx) and
346+
ctxtgts = count(viableImplInCallContext(call, ctx)) and
347+
tgts = strictcount(viableCallable(call)) and
348+
ctxtgts < tgts
349+
)
350+
}
351+
352+
/**
353+
* Gets a viable run-time dispatch target for the call `call` in the
354+
* context `ctx`. This is restricted to those calls for which a context
355+
* makes a difference.
356+
*/
357+
cached
358+
DataFlowCallable prunedViableImplInCallContext(DataFlowCall call, DataFlowCall ctx) {
359+
result = viableImplInCallContext(call, ctx) and
360+
reducedViableImplInCallContext(call, _, ctx)
361+
}
362+
363+
/**
364+
* Holds if flow returning from callable `c` to call `call` might return
365+
* further and if this path restricts the set of call sites that can be
366+
* returned to.
367+
*/
368+
cached
369+
predicate reducedViableImplInReturn(DataFlowCallable c, DataFlowCall call) {
370+
exists(int tgts, int ctxtgts |
371+
mayBenefitFromCallContext(call, _) and
372+
c = viableCallable(call) and
373+
ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContext(call, ctx)) and
374+
tgts = strictcount(DataFlowCall ctx | viableCallable(ctx) = call.getEnclosingCallable()) and
375+
ctxtgts < tgts
376+
)
377+
}
378+
379+
/**
380+
* Gets a viable run-time dispatch target for the call `call` in the
381+
* context `ctx`. This is restricted to those calls and results for which
382+
* the return flow from the result to `call` restricts the possible context
383+
* `ctx`.
384+
*/
385+
cached
386+
DataFlowCallable prunedViableImplInCallContextReverse(DataFlowCall call, DataFlowCall ctx) {
387+
result = viableImplInCallContext(call, ctx) and
388+
reducedViableImplInReturn(result, call)
389+
}
390+
}
391+
392+
import DispatchWithCallContext
393+
333394
/**
334395
* Holds if `p` can flow to the pre-update node associated with post-update
335396
* node `n`, in the same callable, using only value-preserving steps.
@@ -371,8 +432,6 @@ private module Cached {
371432
store(node1, tc.getContent(), node2, contentType, tc.getContainerType())
372433
}
373434

374-
import FlowThrough
375-
376435
/**
377436
* Holds if the call context `call` either improves virtual dispatch in
378437
* `callable` or if it allows us to prune unreachable nodes in `callable`.

cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowDispatch.qll

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -226,28 +226,13 @@ private predicate functionSignature(Function f, string qualifiedName, int nparam
226226
}
227227

228228
/**
229-
* Holds if the call context `ctx` reduces the set of viable dispatch
230-
* targets of `ma` in `c`.
229+
* Holds if the set of viable implementations that can be called by `call`
230+
* might be improved by knowing the call context.
231231
*/
232-
predicate reducedViableImplInCallContext(CallInstruction call, Function f, CallInstruction ctx) {
233-
none()
234-
}
235-
236-
/**
237-
* Gets a viable dispatch target of `ma` in the context `ctx`. This is
238-
* restricted to those `ma`s for which the context makes a difference.
239-
*/
240-
Function prunedViableImplInCallContext(CallInstruction call, CallInstruction ctx) { none() }
241-
242-
/**
243-
* Holds if flow returning from `m` to `ma` might return further and if
244-
* this path restricts the set of call sites that can be returned to.
245-
*/
246-
predicate reducedViableImplInReturn(Function f, CallInstruction call) { none() }
232+
predicate mayBenefitFromCallContext(CallInstruction call, Function f) { none() }
247233

248234
/**
249-
* Gets a viable dispatch target of `ma` in the context `ctx`. This is
250-
* restricted to those `ma`s and results for which the return flow from the
251-
* result to `ma` restricts the possible context `ctx`.
235+
* Gets a viable dispatch target of `call` in the context `ctx`. This is
236+
* restricted to those `call`s for which a context might make a difference.
252237
*/
253-
Function prunedViableImplInCallContextReverse(CallInstruction call, CallInstruction ctx) { none() }
238+
Function viableImplInCallContext(CallInstruction call, CallInstruction ctx) { none() }

cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,67 @@ private module Cached {
330330
import Final
331331
}
332332

333+
import FlowThrough
334+
335+
cached
336+
private module DispatchWithCallContext {
337+
/**
338+
* Holds if the call context `ctx` reduces the set of viable run-time
339+
* dispatch targets of call `call` in `c`.
340+
*/
341+
cached
342+
predicate reducedViableImplInCallContext(DataFlowCall call, DataFlowCallable c, DataFlowCall ctx) {
343+
exists(int tgts, int ctxtgts |
344+
mayBenefitFromCallContext(call, c) and
345+
c = viableCallable(ctx) and
346+
ctxtgts = count(viableImplInCallContext(call, ctx)) and
347+
tgts = strictcount(viableCallable(call)) and
348+
ctxtgts < tgts
349+
)
350+
}
351+
352+
/**
353+
* Gets a viable run-time dispatch target for the call `call` in the
354+
* context `ctx`. This is restricted to those calls for which a context
355+
* makes a difference.
356+
*/
357+
cached
358+
DataFlowCallable prunedViableImplInCallContext(DataFlowCall call, DataFlowCall ctx) {
359+
result = viableImplInCallContext(call, ctx) and
360+
reducedViableImplInCallContext(call, _, ctx)
361+
}
362+
363+
/**
364+
* Holds if flow returning from callable `c` to call `call` might return
365+
* further and if this path restricts the set of call sites that can be
366+
* returned to.
367+
*/
368+
cached
369+
predicate reducedViableImplInReturn(DataFlowCallable c, DataFlowCall call) {
370+
exists(int tgts, int ctxtgts |
371+
mayBenefitFromCallContext(call, _) and
372+
c = viableCallable(call) and
373+
ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContext(call, ctx)) and
374+
tgts = strictcount(DataFlowCall ctx | viableCallable(ctx) = call.getEnclosingCallable()) and
375+
ctxtgts < tgts
376+
)
377+
}
378+
379+
/**
380+
* Gets a viable run-time dispatch target for the call `call` in the
381+
* context `ctx`. This is restricted to those calls and results for which
382+
* the return flow from the result to `call` restricts the possible context
383+
* `ctx`.
384+
*/
385+
cached
386+
DataFlowCallable prunedViableImplInCallContextReverse(DataFlowCall call, DataFlowCall ctx) {
387+
result = viableImplInCallContext(call, ctx) and
388+
reducedViableImplInReturn(result, call)
389+
}
390+
}
391+
392+
import DispatchWithCallContext
393+
333394
/**
334395
* Holds if `p` can flow to the pre-update node associated with post-update
335396
* node `n`, in the same callable, using only value-preserving steps.
@@ -371,8 +432,6 @@ private module Cached {
371432
store(node1, tc.getContent(), node2, contentType, tc.getContainerType())
372433
}
373434

374-
import FlowThrough
375-
376435
/**
377436
* Holds if the call context `call` either improves virtual dispatch in
378437
* `callable` or if it allows us to prune unreachable nodes in `callable`.

csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll

Lines changed: 9 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@ private predicate transitiveCapturedCallTarget(ControlFlow::Nodes::ElementNode c
6767

6868
cached
6969
private module Cached {
70-
private import CallContext
7170
private import semmle.code.csharp.Caching
7271

7372
cached
@@ -108,6 +107,12 @@ private module Cached {
108107
/** Gets a viable run-time target for the call `call`. */
109108
cached
110109
DataFlowCallable viableImpl(DataFlowCall call) { result = call.getARuntimeTarget() }
110+
}
111+
112+
import Cached
113+
114+
private module DispatchImpl {
115+
private import CallContext
111116

112117
/**
113118
* Gets a viable run-time target for the delegate call `call`, requiring
@@ -123,7 +128,7 @@ private module Cached {
123128
* call is a delegate call, or if the qualifier accesses a parameter of
124129
* the enclosing callable `c` (including the implicit `this` parameter).
125130
*/
126-
private predicate mayBenefitFromCallContext(DataFlowCall call, Callable c) {
131+
predicate mayBenefitFromCallContext(DataFlowCall call, Callable c) {
127132
c = call.getEnclosingCallable() and
128133
(
129134
exists(CallContext cc | exists(viableDelegateCallable(call, cc)) |
@@ -134,26 +139,11 @@ private module Cached {
134139
)
135140
}
136141

137-
/**
138-
* Holds if the call context `ctx` reduces the set of viable run-time
139-
* targets of call `call` in `c`.
140-
*/
141-
cached
142-
predicate reducedViableImplInCallContext(DataFlowCall call, DataFlowCallable c, DataFlowCall ctx) {
143-
exists(int tgts, int ctxtgts |
144-
mayBenefitFromCallContext(call, c) and
145-
c = viableCallable(ctx) and
146-
ctxtgts = count(viableImplInCallContext(call, ctx)) and
147-
tgts = strictcount(viableImpl(call)) and
148-
ctxtgts < tgts
149-
)
150-
}
151-
152142
/**
153143
* Gets a viable dispatch target of `call` in the context `ctx`. This is
154144
* restricted to those `call`s for which a context might make a difference.
155145
*/
156-
private DotNet::Callable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) {
146+
DotNet::Callable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) {
157147
exists(ArgumentCallContext cc | result = viableDelegateCallable(call, cc) |
158148
cc.isArgument(ctx.getExpr(), _)
159149
)
@@ -164,47 +154,9 @@ private module Cached {
164154
.getDispatchCall()
165155
.getADynamicTargetInCallContext(ctx.(NonDelegateDataFlowCall).getDispatchCall())
166156
}
167-
168-
/**
169-
* Gets a viable run-time target for the call `call` in the context
170-
* `ctx`. This is restricted to those call nodes for which a context
171-
* might make a difference.
172-
*/
173-
cached
174-
DotNet::Callable prunedViableImplInCallContext(DataFlowCall call, DataFlowCall ctx) {
175-
result = viableImplInCallContext(call, ctx) and
176-
reducedViableImplInCallContext(call, _, ctx)
177-
}
178-
179-
/**
180-
* Holds if flow returning from callable `c` to call `call` might return
181-
* further and if this path restricts the set of call sites that can be
182-
* returned to.
183-
*/
184-
cached
185-
predicate reducedViableImplInReturn(DataFlowCallable c, DataFlowCall call) {
186-
exists(int tgts, int ctxtgts |
187-
mayBenefitFromCallContext(call, _) and
188-
c = viableImpl(call) and
189-
ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContext(call, ctx)) and
190-
tgts = strictcount(DataFlowCall ctx | viableCallable(ctx) = call.getEnclosingCallable()) and
191-
ctxtgts < tgts
192-
)
193-
}
194-
195-
/**
196-
* Gets a viable run-time target for the call `call` in the context `ctx`.
197-
* This is restricted to those call nodes and results for which the return
198-
* flow from the result to `call` restricts the possible context `ctx`.
199-
*/
200-
cached
201-
DataFlowCallable prunedViableImplInCallContextReverse(DataFlowCall call, DataFlowCall ctx) {
202-
result = viableImplInCallContext(call, ctx) and
203-
reducedViableImplInReturn(result, call)
204-
}
205157
}
206158

207-
import Cached
159+
import DispatchImpl
208160

209161
/**
210162
* Gets a node that can read the value returned from `call` with return kind

0 commit comments

Comments
 (0)