Skip to content

Commit 2681b88

Browse files
committed
C#: Update Pre SSA library to use parameterized module
1 parent 8725bf0 commit 2681b88

File tree

5 files changed

+150
-939
lines changed

5 files changed

+150
-939
lines changed

config/identical-files.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -462,7 +462,6 @@
462462
],
463463
"SSA C#": [
464464
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/SsaImplCommon.qll",
465-
"csharp/ql/lib/semmle/code/csharp/controlflow/internal/pressa/SsaImplCommon.qll",
466465
"ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImplCommon.qll",
467466
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplCommon.qll",
468467
"swift/ql/lib/codeql/swift/dataflow/internal/SsaImplCommon.qll"

csharp/ql/lib/semmle/code/csharp/controlflow/internal/PreSsa.qll

Lines changed: 141 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,158 @@ import csharp
66
* scope variables.
77
*/
88
module PreSsa {
9-
import pressa.SsaImplSpecific
10-
private import pressa.SsaImplCommon as SsaImpl
9+
private import AssignableDefinitions
10+
private import semmle.code.csharp.controlflow.internal.ControlFlowGraphImpl
11+
private import semmle.code.csharp.controlflow.internal.PreBasicBlocks as PreBasicBlocks
12+
private import semmle.code.csharp.dataflow.internal.SsaImplCommon as SsaImplCommon
13+
14+
private predicate definitionAt(
15+
AssignableDefinition def, SsaInput::BasicBlock bb, int i, SsaInput::SourceVariable v
16+
) {
17+
bb.getElement(i) = def.getExpr() and
18+
v = def.getTarget() and
19+
// In cases like `(x, x) = (0, 1)`, we discard the first (dead) definition of `x`
20+
not exists(TupleAssignmentDefinition first, TupleAssignmentDefinition second | first = def |
21+
second.getAssignment() = first.getAssignment() and
22+
second.getEvaluationOrder() > first.getEvaluationOrder() and
23+
second.getTarget() = v
24+
)
25+
or
26+
def.(ImplicitParameterDefinition).getParameter() = v and
27+
exists(Callable c | v = c.getAParameter() |
28+
scopeFirst(c, bb) and
29+
i = -1
30+
)
31+
}
32+
33+
predicate implicitEntryDef(Callable c, SsaInput::BasicBlock bb, SsaInput::SourceVariable v) {
34+
not v instanceof LocalScopeVariable and
35+
c = v.getACallable() and
36+
scopeFirst(c, bb)
37+
}
38+
39+
module SsaInput implements SsaImplCommon::InputSig {
40+
class BasicBlock = PreBasicBlocks::PreBasicBlock;
41+
42+
BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { result.immediatelyDominates(bb) }
43+
44+
BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() }
45+
46+
class ExitBasicBlock extends BasicBlock {
47+
ExitBasicBlock() { scopeLast(_, this.getLastElement(), _) }
48+
}
49+
50+
/** Holds if `a` is assigned in non-constructor callable `c`. */
51+
pragma[nomagic]
52+
private predicate assignableDefinition(Assignable a, Callable c) {
53+
exists(AssignableDefinition def | def.getTarget() = a |
54+
c = def.getEnclosingCallable() and
55+
not c instanceof Constructor
56+
)
57+
}
58+
59+
/** Holds if `a` is accessed in callable `c`. */
60+
pragma[nomagic]
61+
private predicate assignableAccess(Assignable a, Callable c) {
62+
exists(AssignableAccess aa | aa.getTarget() = a | c = aa.getEnclosingCallable())
63+
}
64+
65+
pragma[nomagic]
66+
private predicate assignableNoCapturing(Assignable a, Callable c) {
67+
assignableAccess(a, c) and
68+
/*
69+
* The code below is equivalent to
70+
* ```ql
71+
* not exists(Callable other | assignableDefinition(a, other) | other != c)
72+
* ```
73+
* but it avoids a Cartesian product in the compiler generated antijoin
74+
* predicate.
75+
*/
76+
77+
(
78+
not assignableDefinition(a, _)
79+
or
80+
c = unique(Callable c0 | assignableDefinition(a, c0) | c0)
81+
)
82+
}
83+
84+
pragma[noinline]
85+
private predicate assignableNoComplexQualifiers(Assignable a) {
86+
forall(QualifiableExpr qe | qe.(AssignableAccess).getTarget() = a | qe.targetIsThisInstance())
87+
}
88+
89+
/**
90+
* A simple assignable. Either a local scope variable or a field/property
91+
* that behaves like a local scope variable.
92+
*/
93+
class SourceVariable extends Assignable {
94+
private Callable c;
95+
96+
SourceVariable() {
97+
(
98+
this instanceof LocalScopeVariable
99+
or
100+
this = any(Field f | not f.isVolatile())
101+
or
102+
this = any(TrivialProperty tp | not tp.isOverridableOrImplementable())
103+
) and
104+
assignableNoCapturing(this, c) and
105+
assignableNoComplexQualifiers(this)
106+
}
107+
108+
/** Gets a callable in which this simple assignable can be analyzed. */
109+
Callable getACallable() { result = c }
110+
}
111+
112+
predicate variableWrite(BasicBlock bb, int i, SourceVariable v, boolean certain) {
113+
exists(AssignableDefinition def |
114+
definitionAt(def, bb, i, v) and
115+
if def.getTargetAccess().isRefArgument() then certain = false else certain = true
116+
)
117+
or
118+
exists(Callable c |
119+
implicitEntryDef(c, bb, v) and
120+
i = -1 and
121+
certain = true
122+
)
123+
}
124+
125+
predicate variableRead(BasicBlock bb, int i, SourceVariable v, boolean certain) {
126+
exists(AssignableRead read |
127+
read = bb.getElement(i) and
128+
read.getTarget() = v and
129+
certain = true
130+
)
131+
or
132+
v =
133+
any(LocalScopeVariable lsv |
134+
lsv.getCallable() = bb.(ExitBasicBlock).getEnclosingCallable() and
135+
i = bb.length() and
136+
(lsv.isRef() or v.(Parameter).isOut()) and
137+
certain = false
138+
)
139+
}
140+
}
141+
142+
private module SsaImpl = SsaImplCommon::Make<SsaInput>;
11143

12144
class Definition extends SsaImpl::Definition {
13145
final AssignableRead getARead() {
14-
exists(BasicBlock bb, int i |
146+
exists(SsaInput::BasicBlock bb, int i |
15147
SsaImpl::ssaDefReachesRead(_, this, bb, i) and
16148
result = bb.getElement(i)
17149
)
18150
}
19151

20152
final AssignableDefinition getDefinition() {
21-
exists(BasicBlock bb, int i, SourceVariable v |
153+
exists(SsaInput::BasicBlock bb, int i, SsaInput::SourceVariable v |
22154
this.definesAt(v, bb, i) and
23155
definitionAt(result, bb, i, v)
24156
)
25157
}
26158

27159
final AssignableRead getAFirstRead() {
28-
exists(BasicBlock bb1, int i1, BasicBlock bb2, int i2 |
160+
exists(SsaInput::BasicBlock bb1, int i1, SsaInput::BasicBlock bb2, int i2 |
29161
this.definesAt(_, bb1, i1) and
30162
SsaImpl::adjacentDefRead(this, bb1, i1, bb2, i2) and
31163
result = bb2.getElement(i2)
@@ -42,14 +174,14 @@ module PreSsa {
42174
not result instanceof PhiNode
43175
}
44176

45-
final predicate isLiveAtEndOfBlock(BasicBlock bb) {
177+
final predicate isLiveAtEndOfBlock(SsaInput::BasicBlock bb) {
46178
SsaImpl::ssaDefReachesEndOfBlock(bb, this, _)
47179
}
48180

49181
Location getLocation() {
50182
result = this.getDefinition().getLocation()
51183
or
52-
exists(Callable c, BasicBlock bb, SourceVariable v |
184+
exists(Callable c, SsaInput::BasicBlock bb, SsaInput::SourceVariable v |
53185
this.definesAt(v, bb, -1) and
54186
implicitEntryDef(c, bb, v) and
55187
result = c.getLocation()
@@ -64,9 +196,9 @@ module PreSsa {
64196
}
65197

66198
predicate adjacentReadPairSameVar(AssignableRead read1, AssignableRead read2) {
67-
exists(BasicBlock bb1, int i1, BasicBlock bb2, int i2 |
199+
exists(SsaInput::BasicBlock bb1, int i1, SsaInput::BasicBlock bb2, int i2 |
68200
read1 = bb1.getElement(i1) and
69-
variableRead(bb1, i1, _, true) and
201+
SsaInput::variableRead(bb1, i1, _, true) and
70202
SsaImpl::adjacentDefRead(_, bb1, i1, bb2, i2) and
71203
read2 = bb2.getElement(i2)
72204
)

0 commit comments

Comments
 (0)