Skip to content

Commit 5d1a592

Browse files
committed
C#: Reimplement flow-summary compilation
1 parent 444e607 commit 5d1a592

25 files changed

+988
-956
lines changed

csharp/ql/src/semmle/code/csharp/dataflow/CallContext.qll

Lines changed: 1 addition & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,7 @@ cached
1111
private newtype TCallContext =
1212
TEmptyCallContext() or
1313
TArgNonDelegateCallContext(Expr arg) { exists(DispatchCall dc | arg = dc.getArgument(_)) } or
14-
TArgDelegateCallContext(DelegateCall dc, int i) { exists(dc.getArgument(i)) } or
15-
TDelegateToLibraryCallableArgCallContext(DelegateArgumentToLibraryCallable arg, int i) {
16-
exists(arg.getDelegateType().getParameter(i))
17-
}
14+
TArgDelegateCallContext(DelegateCall dc, int i) { exists(dc.getArgument(i)) }
1815

1916
/**
2017
* A call context.
@@ -79,31 +76,3 @@ class DelegateCallArgumentCallContext extends ArgumentCallContext, TArgDelegateC
7976

8077
override Location getLocation() { result = dc.getArgument(arg).getLocation() }
8178
}
82-
83-
/**
84-
* An argument of a call to a delegate supplied to a library callable,
85-
* identified by the delegate argument itself.
86-
*
87-
* For example, in `x.Select(y => y)` the call to the supplied delegate
88-
* that happens inside the library callable `Select` is not available
89-
* in the database, so the delegate argument `y => y` is used to
90-
* represent the call.
91-
*/
92-
class DelegateArgumentToLibraryCallableArgumentContext extends ArgumentCallContext,
93-
TDelegateToLibraryCallableArgCallContext {
94-
Expr delegate;
95-
int arg;
96-
97-
DelegateArgumentToLibraryCallableArgumentContext() {
98-
this = TDelegateToLibraryCallableArgCallContext(delegate, arg)
99-
}
100-
101-
override predicate isArgument(Expr call, int i) {
102-
call = delegate and
103-
i = arg
104-
}
105-
106-
override string toString() { result = "argument " + arg + " of " + delegate.toString() }
107-
108-
override Location getLocation() { result = delegate.getLocation() }
109-
}

csharp/ql/src/semmle/code/csharp/dataflow/LibraryTypeDataFlow.qll

Lines changed: 1 addition & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -199,27 +199,13 @@ class CallableFlowSink extends TCallableFlowSink {
199199

200200
/** Gets the sink of flow for call `c`, if any. */
201201
Expr getSink(Call c) { none() }
202-
203-
/**
204-
* Gets the type of the sink for call `c`. Unlike `getSink()`, this is defined
205-
* for all flow sink specifications.
206-
*/
207-
Type getSinkType(Call c) { result = this.getSink(c).getType() }
208202
}
209203

210204
/** A flow sink specification: (method call) qualifier. */
211205
class CallableFlowSinkQualifier extends CallableFlowSink, TCallableFlowSinkQualifier {
212206
override string toString() { result = "qualifier" }
213207

214-
override Expr getSink(Call c) {
215-
result = c.getChild(-1)
216-
or
217-
// E.g. `new Dictionary<int, string>{ {0, "a"}, {1, "b"} }`
218-
result.(CollectionInitializer).getAnElementInitializer() = c
219-
or
220-
// E.g. `new Dictionary<int, string>() { [0] = "a", [1] = "b" }`
221-
result.(ObjectInitializer).getAMemberInitializer().getLValue() = c
222-
}
208+
override Expr getSink(Call c) { result = c.getChild(-1) }
223209
}
224210

225211
/** A flow sink specification: return value. */
@@ -253,8 +239,6 @@ class CallableFlowSinkArg extends CallableFlowSink, TCallableFlowSinkArg {
253239
// The uses of the `i`th argument are the actual sinks
254240
none()
255241
}
256-
257-
override Type getSinkType(Call c) { result = this.getArgument(c).getType() }
258242
}
259243

260244
private predicate isCollectionType(ValueOrRefType t) {
@@ -312,16 +296,6 @@ class CallableFlowSinkDelegateArg extends CallableFlowSink, TCallableFlowSinkDel
312296
// The uses of the `j`th parameter are the actual sinks
313297
none()
314298
}
315-
316-
override Type getSinkType(Call c) {
317-
result =
318-
c
319-
.getArgument(delegateIndex)
320-
.(DelegateArgumentToLibraryCallable)
321-
.getDelegateType()
322-
.getParameter(parameterIndex)
323-
.getType()
324-
}
325299
}
326300

327301
/** A specification of data flow for a library (non-source code) type. */

csharp/ql/src/semmle/code/csharp/dataflow/SSA.qll

Lines changed: 48 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1018,7 +1018,7 @@ module Ssa {
10181018
private predicate intraInstanceCallEdge(Callable c1, InstanceCallable c2) {
10191019
exists(Call c |
10201020
c.getEnclosingCallable() = c1 and
1021-
c2 = getARuntimeTarget(c) and
1021+
c2 = getARuntimeTarget(c, _) and
10221022
c.(QualifiableExpr).targetIsLocalInstance()
10231023
)
10241024
}
@@ -1034,15 +1034,28 @@ module Ssa {
10341034
* the SSA library and `Call.getARuntimeTarget()` mutually recursive), and
10351035
*
10361036
* (3) indirect calls to delegates via calls to library callables are included.
1037+
*
1038+
* The Boolean `libraryDelegateCall` indicates whether `c` is a call to a library
1039+
* method and the result is a delegate passed to `c`. For example, in
1040+
*
1041+
* ```csharp
1042+
* Lazy<int> M1()
1043+
* {
1044+
* return new Lazy<int>(M2);
1045+
* }
1046+
* ```
1047+
*
1048+
* the constructor call `new Lazy<int>(M2)` includes `M2` as a target.
10371049
*/
1038-
Callable getARuntimeTarget(Call c) {
1050+
Callable getARuntimeTarget(Call c, boolean libraryDelegateCall) {
10391051
// Non-delegate call: use dispatch library
10401052
exists(DispatchCall dc | dc.getCall() = c |
1041-
result = dc.getADynamicTarget().getSourceDeclaration()
1053+
result = dc.getADynamicTarget().getSourceDeclaration() and
1054+
libraryDelegateCall = false
10421055
)
10431056
or
10441057
// Delegate call: use simple analysis
1045-
result = SimpleDelegateAnalysis::getARuntimeDelegateTarget(c)
1058+
result = SimpleDelegateAnalysis::getARuntimeDelegateTarget(c, libraryDelegateCall)
10461059
}
10471060

10481061
private module SimpleDelegateAnalysis {
@@ -1055,12 +1068,14 @@ module Ssa {
10551068
* Either `c` is a delegate call and `e` is the qualifier, or `c` is a call to
10561069
* a library callable and `e` is a delegate argument.
10571070
*/
1058-
private predicate delegateCall(Call c, Expr e) {
1059-
c = any(DelegateCall dc | e = dc.getDelegateExpr())
1071+
private predicate delegateCall(Call c, Expr e, boolean libraryDelegateCall) {
1072+
c = any(DelegateCall dc | e = dc.getDelegateExpr()) and
1073+
libraryDelegateCall = false
10601074
or
10611075
c.getTarget().fromLibrary() and
10621076
e = c.getAnArgument() and
1063-
e.getType() instanceof SystemLinqExpressions::DelegateExtType
1077+
e.getType() instanceof SystemLinqExpressions::DelegateExtType and
1078+
libraryDelegateCall = true
10641079
}
10651080

10661081
/** Holds if expression `e` is a delegate creation for callable `c` of type `t`. */
@@ -1090,7 +1105,7 @@ module Ssa {
10901105
callable = call.getTarget() or
10911106
callable = call.getTarget().(Method).getAnOverrider+() or
10921107
callable = call.getTarget().(Method).getAnUltimateImplementor() or
1093-
callable = getARuntimeDelegateTarget(call)
1108+
callable = getARuntimeDelegateTarget(call, false)
10941109
)
10951110
or
10961111
pred = succ.(DelegateCreation).getArgument()
@@ -1110,7 +1125,7 @@ module Ssa {
11101125
}
11111126

11121127
private predicate reachesDelegateCall(Expr e) {
1113-
delegateCall(_, e)
1128+
delegateCall(_, e, _)
11141129
or
11151130
exists(Expr mid | reachesDelegateCall(mid) | delegateFlowStep(e, mid))
11161131
}
@@ -1128,12 +1143,14 @@ module Ssa {
11281143
}
11291144

11301145
/** Gets a run-time target for the delegate call `c`. */
1131-
Callable getARuntimeDelegateTarget(Call c) { delegateCall(c, delegateCallSource(result)) }
1146+
Callable getARuntimeDelegateTarget(Call c, boolean libraryDelegateCall) {
1147+
delegateCall(c, delegateCallSource(result), libraryDelegateCall)
1148+
}
11321149
}
11331150

11341151
/** Holds if `(c1,c2)` is an edge in the call graph. */
11351152
predicate callEdge(Callable c1, Callable c2) {
1136-
exists(Call c | c.getEnclosingCallable() = c1 | getARuntimeTarget(c) = c2)
1153+
exists(Call c | c.getEnclosingCallable() = c1 and c2 = getARuntimeTarget(c, _))
11371154
}
11381155

11391156
/**
@@ -1179,7 +1196,7 @@ module Ssa {
11791196
pragma[noinline]
11801197
predicate callAt(BasicBlock bb, int i, Call call) {
11811198
bb.getNode(i) = call.getAControlFlowNode() and
1182-
getARuntimeTarget(call).hasBody()
1199+
getARuntimeTarget(call, _).hasBody()
11831200
}
11841201

11851202
/**
@@ -1201,7 +1218,7 @@ module Ssa {
12011218
private predicate pruneFromLeft(Callable c) {
12021219
exists(Call call, TrackedFieldOrProp f |
12031220
updateCandidate(_, _, f, call) and
1204-
c = getARuntimeTarget(call) and
1221+
c = getARuntimeTarget(call, _) and
12051222
generalSetter(_, f.getAssignable(), _)
12061223
)
12071224
or
@@ -1238,7 +1255,7 @@ module Ssa {
12381255
updateCandidate(_, _, tfp, call) and
12391256
fp = tfp.getAssignable() and
12401257
generalSetter(_, fp, _) and
1241-
c1 = getARuntimeTarget(call)
1258+
c1 = getARuntimeTarget(call, _)
12421259
}
12431260

12441261
pragma[noinline]
@@ -1279,7 +1296,7 @@ module Ssa {
12791296
Call call, TrackedFieldOrProp tfp, Callable setter
12801297
) {
12811298
updateCandidate(_, _, tfp, call) and
1282-
setsOwnFieldOrPropTransitive(getARuntimeTarget(call), tfp.getAssignable(), setter)
1299+
setsOwnFieldOrPropTransitive(getARuntimeTarget(call, _), tfp.getAssignable(), setter)
12831300
}
12841301

12851302
private predicate updatesNamedFieldOrPropPossiblyLive(
@@ -1447,7 +1464,7 @@ module Ssa {
14471464
private predicate pruneFromLeft(Callable c) {
14481465
exists(Call call, CapturedWrittenLocalScopeSourceVariable v |
14491466
updateCandidate(_, _, v, call) and
1450-
c = getARuntimeTarget(call) and
1467+
c = getARuntimeTarget(call, _) and
14511468
relevantDefinition(_, v.getAssignable(), _)
14521469
)
14531470
or
@@ -1485,12 +1502,12 @@ module Ssa {
14851502
pragma[noinline]
14861503
private predicate updatesCapturedVariablePrefix(
14871504
Call call, CapturedWrittenLocalScopeSourceVariable v, PrunedCallable c,
1488-
CapturedWrittenLocalScopeVariable captured
1505+
CapturedWrittenLocalScopeVariable captured, boolean libraryDelegateCall
14891506
) {
14901507
updateCandidate(_, _, v, call) and
14911508
captured = v.getAssignable() and
14921509
relevantDefinitionProj(_, captured) and
1493-
c = getARuntimeTarget(call)
1510+
c = getARuntimeTarget(call, libraryDelegateCall)
14941511
}
14951512

14961513
/**
@@ -1505,11 +1522,13 @@ module Ssa {
15051522
Call call, CapturedWrittenLocalScopeSourceVariable v, PrunedCallable writer,
15061523
boolean additionalCalls
15071524
) {
1508-
exists(PrunedCallable c, CapturedWrittenLocalScopeVariable captured |
1509-
updatesCapturedVariablePrefix(call, v, c, captured) and
1525+
exists(
1526+
PrunedCallable c, CapturedWrittenLocalScopeVariable captured, boolean libraryDelegateCall
1527+
|
1528+
updatesCapturedVariablePrefix(call, v, c, captured, libraryDelegateCall) and
15101529
relevantDefinitionProj(writer, captured) and
15111530
(
1512-
c = writer and additionalCalls = false
1531+
c = writer and additionalCalls = libraryDelegateCall
15131532
or
15141533
callEdgePrunedPlus(c, writer) and additionalCalls = true
15151534
)
@@ -1667,7 +1686,7 @@ module Ssa {
16671686
private predicate pruneFromLeft(Callable c) {
16681687
exists(Call call, CapturedReadLocalScopeSourceVariable v |
16691688
implicitReadCandidate(_, _, call.getAControlFlowNode(), v) and
1670-
c = getARuntimeTarget(call)
1689+
c = getARuntimeTarget(call, _)
16711690
)
16721691
or
16731692
exists(Callable mid | pruneFromLeft(mid) | callEdge(mid, c))
@@ -1702,12 +1721,12 @@ module Ssa {
17021721
pragma[noinline]
17031722
private predicate readsCapturedVariablePrefix(
17041723
ControlFlow::Node call, CapturedReadLocalScopeSourceVariable v, PrunedCallable c,
1705-
CapturedReadLocalScopeVariable captured
1724+
CapturedReadLocalScopeVariable captured, boolean libraryDelegateCall
17061725
) {
17071726
implicitReadCandidate(_, _, call, v) and
17081727
captured = v.getAssignable() and
17091728
capturerReads(_, captured) and
1710-
c = getARuntimeTarget(call.getElement())
1729+
c = getARuntimeTarget(call.getElement(), libraryDelegateCall)
17111730
}
17121731

17131732
/**
@@ -1722,11 +1741,13 @@ module Ssa {
17221741
ControlFlow::Nodes::ElementNode call, CapturedReadLocalScopeSourceVariable v, Callable reader,
17231742
boolean additionalCalls
17241743
) {
1725-
exists(PrunedCallable c, CapturedReadLocalScopeVariable captured |
1726-
readsCapturedVariablePrefix(call, v, c, captured) and
1744+
exists(
1745+
PrunedCallable c, CapturedReadLocalScopeVariable captured, boolean libraryDelegateCall
1746+
|
1747+
readsCapturedVariablePrefix(call, v, c, captured, libraryDelegateCall) and
17271748
capturerReads(reader, captured) and
17281749
(
1729-
c = reader and additionalCalls = false
1750+
c = reader and additionalCalls = libraryDelegateCall
17301751
or
17311752
callEdgePrunedPlus(c, reader) and additionalCalls = true
17321753
)
@@ -1765,7 +1786,6 @@ module Ssa {
17651786
def.getSourceVariable().getAssignable() = lsv
17661787
|
17671788
lsv = v.getAssignable() and
1768-
adef = def.getAPossibleDefinition() and
17691789
bb.getNode(i) = adef.getAControlFlowNode() and
17701790
updatesCapturedVariable(def.getCall(), _, adef, additionalCalls)
17711791
)

0 commit comments

Comments
 (0)