Skip to content

Commit fbe8f75

Browse files
authored
Merge pull request #8038 from michaelnebel/csharp/gvn-cfecomparison
C#: Refactor Structural Comparison for Control Flow Elements.
2 parents 275902d + d0cb984 commit fbe8f75

File tree

4 files changed

+446
-160
lines changed

4 files changed

+446
-160
lines changed

csharp/ql/lib/semmle/code/csharp/commons/StructuralComparison.qll

Lines changed: 179 additions & 160 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,36 @@
55

66
import csharp
77

8+
abstract private class GvnKind extends TGvnKind {
9+
abstract string toString();
10+
}
11+
12+
private class GvnKindExpr extends GvnKind, TGvnKindExpr {
13+
private int kind;
14+
15+
GvnKindExpr() { this = TGvnKindExpr(kind) }
16+
17+
override string toString() { result = "Expr(" + kind + ")" }
18+
}
19+
20+
private class GvnKindStmt extends GvnKind, TGvnKindStmt {
21+
private int kind;
22+
23+
GvnKindStmt() { this = TGvnKindStmt(kind) }
24+
25+
override string toString() { result = "Stmt(" + kind + ")" }
26+
}
27+
28+
private class GvnKindDeclaration extends GvnKind, TGvnKindDeclaration {
29+
private int kind;
30+
private boolean isTargetThis;
31+
private Declaration d;
32+
33+
GvnKindDeclaration() { this = TGvnKindDeclaration(kind, isTargetThis, d) }
34+
35+
override string toString() { result = "Expr(" + kind + ")," + isTargetThis + "," + d }
36+
}
37+
838
/** Gets the declaration referenced by the expression `e`, if any. */
939
private Declaration referenceAttribute(Expr e) {
1040
result = e.(MethodCall).getTarget()
@@ -14,17 +44,156 @@ private Declaration referenceAttribute(Expr e) {
1444
result = e.(Access).getTarget()
1545
}
1646

17-
/** Gets the AST node kind element `e`. */
18-
private int elementKind(ControlFlowElement e) {
19-
expressions(e, result, _)
47+
/** Gets a Boolean indicating whether the target of the expression `e` is `this`. */
48+
private boolean isTargetThis(Expr e) {
49+
result = true and e.(MemberAccess).targetIsThisInstance()
2050
or
21-
exists(int k | statements(e, k) | result = -k)
51+
result = false and not e.(MemberAccess).targetIsThisInstance()
52+
}
53+
54+
/**
55+
* A global value number (GVN) for a control flow element.
56+
*
57+
* GVNs are used to map control flow elements to a representation that
58+
* omits location information, that is, two elements that are structurally
59+
* equal will be mapped to the same GVN.
60+
*/
61+
class Gvn extends TGvn {
62+
/** Gets the string representation of this global value number. */
63+
string toString() { none() }
64+
}
65+
66+
private class ConstantGvn extends Gvn, TConstantGvn {
67+
override string toString() { this = TConstantGvn(result) }
68+
}
69+
70+
private class GvnNil extends Gvn, TGvnNil {
71+
private GvnKind kind;
72+
73+
GvnNil() { this = TGvnNil(kind) }
74+
75+
override string toString() { result = "(kind:" + kind + ")" }
76+
}
77+
78+
private class GvnCons extends Gvn, TGvnCons {
79+
private Gvn head;
80+
private Gvn tail;
81+
82+
GvnCons() { this = TGvnCons(head, tail) }
83+
84+
override string toString() { result = "(" + head + " :: " + tail + ")" }
2285
}
2386

24-
private int getNumberOfActualChildren(ControlFlowElement e) {
25-
if e.(MemberAccess).targetIsThisInstance()
26-
then result = e.getNumberOfChildren() - 1
27-
else result = e.getNumberOfChildren()
87+
pragma[noinline]
88+
private predicate gvnKindDeclaration(Expr e, int kind, boolean isTargetThis, Declaration d) {
89+
isTargetThis = isTargetThis(e) and
90+
d = referenceAttribute(e) and
91+
expressions(e, kind, _)
92+
}
93+
94+
/**
95+
* Gets the `GvnKind` of the element `cfe`.
96+
*
97+
* In case `cfe` is a reference attribute, we encode the entire declaration and whether
98+
* the target is semantically equivalent to `this`.
99+
*/
100+
private GvnKind getGvnKind(ControlFlowElement cfe) {
101+
exists(int kind, boolean isTargetThis, Declaration d |
102+
gvnKindDeclaration(cfe, kind, isTargetThis, d) and
103+
result = TGvnKindDeclaration(kind, isTargetThis, d)
104+
)
105+
or
106+
exists(int kind |
107+
not exists(referenceAttribute(cfe)) and
108+
expressions(cfe, kind, _) and
109+
result = TGvnKindExpr(kind)
110+
or
111+
statements(cfe, kind) and
112+
result = TGvnKindStmt(kind)
113+
)
114+
}
115+
116+
private Gvn toGvn(ControlFlowElement cfe, GvnKind kind, int index) {
117+
kind = getGvnKind(cfe) and
118+
result = TGvnNil(kind) and
119+
index = -1
120+
or
121+
exists(Gvn head, Gvn tail |
122+
toGvnCons(cfe, kind, index, head, tail) and
123+
result = TGvnCons(head, tail)
124+
)
125+
}
126+
127+
private int getNumberOfActualChildren(ControlFlowElement cfe) {
128+
if cfe.(MemberAccess).targetIsThisInstance()
129+
then result = cfe.getNumberOfChildren() - 1
130+
else result = cfe.getNumberOfChildren()
131+
}
132+
133+
private ControlFlowElement getRankedChild(ControlFlowElement cfe, int rnk) {
134+
result =
135+
rank[rnk + 1](ControlFlowElement child, int j |
136+
child = cfe.getChild(j) and
137+
(
138+
j >= 0
139+
or
140+
j = -1 and not cfe.(MemberAccess).targetIsThisInstance()
141+
)
142+
|
143+
child order by j
144+
)
145+
}
146+
147+
pragma[noinline]
148+
private Gvn toGvnChild(ControlFlowElement cfe, int index) {
149+
result = toGvn(getRankedChild(cfe, index))
150+
}
151+
152+
pragma[noinline]
153+
private predicate toGvnCons(ControlFlowElement cfe, GvnKind kind, int index, Gvn head, Gvn tail) {
154+
tail = toGvn(cfe, kind, index - 1) and
155+
head = toGvnChild(cfe, index)
156+
}
157+
158+
cached
159+
private module Cached {
160+
cached
161+
newtype TGvnKind =
162+
TGvnKindExpr(int kind) { expressions(_, kind, _) } or
163+
TGvnKindStmt(int kind) { statements(_, kind) } or
164+
TGvnKindDeclaration(int kind, boolean thisTarget, Declaration d) {
165+
exists(Expr e |
166+
d = referenceAttribute(e) and thisTarget = isTargetThis(e) and expressions(e, kind, _)
167+
)
168+
}
169+
170+
cached
171+
newtype TGvn =
172+
TConstantGvn(string s) { s = any(Expr e).getValue() } or
173+
TGvnNil(GvnKind gkind) or
174+
TGvnCons(Gvn head, Gvn tail) { toGvnCons(_, _, _, head, tail) }
175+
176+
/** Gets the global value number of the element `cfe`. */
177+
cached
178+
Gvn toGvnCached(ControlFlowElement cfe) {
179+
result = TConstantGvn(cfe.(Expr).getValue())
180+
or
181+
not exists(cfe.(Expr).getValue()) and
182+
exists(GvnKind kind, int index |
183+
result = toGvn(cfe, kind, index - 1) and
184+
index = getNumberOfActualChildren(cfe)
185+
)
186+
}
187+
}
188+
189+
private import Cached
190+
191+
predicate toGvn = toGvnCached/1;
192+
193+
pragma[inline]
194+
private predicate sameGvn(ControlFlowElement x, ControlFlowElement y) {
195+
pragma[only_bind_into](toGvn(pragma[only_bind_out](x))) =
196+
pragma[only_bind_into](toGvn(pragma[only_bind_out](y)))
28197
}
29198

30199
/**
@@ -57,87 +226,12 @@ abstract class StructuralComparisonConfiguration extends string {
57226
*/
58227
abstract predicate candidate(ControlFlowElement x, ControlFlowElement y);
59228

60-
private predicate candidateInternal(ControlFlowElement x, ControlFlowElement y) {
61-
candidate(x, y)
62-
or
63-
exists(ControlFlowElement xParent, ControlFlowElement yParent, int i |
64-
candidateInternalChild(xParent, i, x, yParent)
65-
|
66-
y = yParent.getChild(i)
67-
)
68-
}
69-
70-
pragma[noinline]
71-
private predicate candidateInternalChild(
72-
ControlFlowElement x, int i, ControlFlowElement xChild, ControlFlowElement y
73-
) {
74-
candidateInternal(x, y) and
75-
xChild = x.getChild(i)
76-
}
77-
78-
private predicate sameByValue(Expr x, Expr y) { sameByValueAux(x, y, y.getValue()) }
79-
80-
pragma[nomagic]
81-
private predicate sameByValueAux(Expr x, Expr y, string value) {
82-
candidateInternal(x, y) and
83-
value = x.getValue()
84-
}
85-
86-
private ControlFlowElement getRankedChild(ControlFlowElement cfe, int rnk, int i) {
87-
(candidateInternal(cfe, _) or candidateInternal(_, cfe)) and
88-
i =
89-
rank[rnk](int j |
90-
exists(ControlFlowElement child | child = cfe.getChild(j) |
91-
not (j = -1 and cfe.(MemberAccess).targetIsThisInstance())
92-
)
93-
) and
94-
result = cfe.getChild(i)
95-
}
96-
97-
pragma[nomagic]
98-
private predicate sameByStructure0(
99-
ControlFlowElement x, ControlFlowElement y, int elementKind, int children
100-
) {
101-
candidateInternal(x, y) and
102-
elementKind = elementKind(x) and
103-
children = getNumberOfActualChildren(x) and
104-
not (x.(Expr).hasValue() and y.(Expr).hasValue())
105-
}
106-
107-
pragma[nomagic]
108-
private predicate sameByStructure(ControlFlowElement x, ControlFlowElement y, int i) {
109-
i = 0 and
110-
// At least one of `x` and `y` must not have a value, they must have
111-
// the same kind, and the same number of children
112-
sameByStructure0(x, y, elementKind(y), getNumberOfActualChildren(y)) and
113-
// If one of them has a reference attribute, they should both reference
114-
// the same node
115-
(exists(referenceAttribute(x)) implies referenceAttribute(x) = referenceAttribute(y)) and
116-
// x is a member access on `this` iff y is
117-
(x.(MemberAccess).targetIsThisInstance() implies y.(MemberAccess).targetIsThisInstance()) and
118-
(y.(MemberAccess).targetIsThisInstance() implies x.(MemberAccess).targetIsThisInstance())
119-
or
120-
exists(int j | sameByStructure(x, y, i - 1) |
121-
sameInternal(getRankedChild(x, i, j), getRankedChild(y, i, j))
122-
)
123-
}
124-
125-
pragma[nomagic]
126-
private predicate sameInternal(ControlFlowElement x, ControlFlowElement y) {
127-
sameByValue(x, y)
128-
or
129-
sameByStructure(x, y, getNumberOfActualChildren(x))
130-
}
131-
132229
/**
133230
* Holds if elements `x` and `y` structurally equal. `x` and `y` must be
134231
* flagged as candidates for structural equality, that is,
135232
* `candidate(x, y)` must hold.
136233
*/
137-
predicate same(ControlFlowElement x, ControlFlowElement y) {
138-
candidate(x, y) and
139-
sameInternal(x, y)
140-
}
234+
predicate same(ControlFlowElement x, ControlFlowElement y) { candidate(x, y) and sameGvn(x, y) }
141235
}
142236

143237
/**
@@ -183,86 +277,11 @@ module Internal {
183277
*/
184278
abstract predicate candidate(ControlFlowElement x, ControlFlowElement y);
185279

186-
private predicate candidateInternal(ControlFlowElement x, ControlFlowElement y) {
187-
candidate(x, y)
188-
or
189-
exists(ControlFlowElement xParent, ControlFlowElement yParent, int i |
190-
candidateInternalChild(xParent, i, x, yParent)
191-
|
192-
y = yParent.getChild(i)
193-
)
194-
}
195-
196-
pragma[noinline]
197-
private predicate candidateInternalChild(
198-
ControlFlowElement x, int i, ControlFlowElement xChild, ControlFlowElement y
199-
) {
200-
candidateInternal(x, y) and
201-
xChild = x.getChild(i)
202-
}
203-
204-
private predicate sameByValue(Expr x, Expr y) { sameByValueAux(x, y, y.getValue()) }
205-
206-
pragma[nomagic]
207-
private predicate sameByValueAux(Expr x, Expr y, string value) {
208-
candidateInternal(x, y) and
209-
value = x.getValue()
210-
}
211-
212-
private ControlFlowElement getRankedChild(ControlFlowElement cfe, int rnk, int i) {
213-
(candidateInternal(cfe, _) or candidateInternal(_, cfe)) and
214-
i =
215-
rank[rnk](int j |
216-
exists(ControlFlowElement child | child = cfe.getChild(j) |
217-
not (j = -1 and cfe.(MemberAccess).targetIsThisInstance())
218-
)
219-
) and
220-
result = cfe.getChild(i)
221-
}
222-
223-
pragma[nomagic]
224-
private predicate sameByStructure0(
225-
ControlFlowElement x, ControlFlowElement y, int elementKind, int children
226-
) {
227-
candidateInternal(x, y) and
228-
elementKind = elementKind(x) and
229-
children = getNumberOfActualChildren(x) and
230-
not (x.(Expr).hasValue() and y.(Expr).hasValue())
231-
}
232-
233-
pragma[nomagic]
234-
private predicate sameByStructure(ControlFlowElement x, ControlFlowElement y, int i) {
235-
i = 0 and
236-
// At least one of `x` and `y` must not have a value, they must have
237-
// the same kind, and the same number of children
238-
sameByStructure0(x, y, elementKind(y), getNumberOfActualChildren(y)) and
239-
// If one of them has a reference attribute, they should both reference
240-
// the same node
241-
(exists(referenceAttribute(x)) implies referenceAttribute(x) = referenceAttribute(y)) and
242-
// x is a member access on `this` iff y is
243-
(x.(MemberAccess).targetIsThisInstance() implies y.(MemberAccess).targetIsThisInstance()) and
244-
(y.(MemberAccess).targetIsThisInstance() implies x.(MemberAccess).targetIsThisInstance())
245-
or
246-
exists(int j | sameByStructure(x, y, i - 1) |
247-
sameInternal(getRankedChild(x, i, j), getRankedChild(y, i, j))
248-
)
249-
}
250-
251-
pragma[nomagic]
252-
private predicate sameInternal(ControlFlowElement x, ControlFlowElement y) {
253-
sameByValue(x, y)
254-
or
255-
sameByStructure(x, y, getNumberOfActualChildren(x))
256-
}
257-
258280
/**
259281
* Holds if elements `x` and `y` structurally equal. `x` and `y` must be
260282
* flagged as candidates for structural equality, that is,
261283
* `candidate(x, y)` must hold.
262284
*/
263-
predicate same(ControlFlowElement x, ControlFlowElement y) {
264-
candidate(x, y) and
265-
sameInternal(x, y)
266-
}
285+
predicate same(ControlFlowElement x, ControlFlowElement y) { candidate(x, y) and sameGvn(x, y) }
267286
}
268287
}

0 commit comments

Comments
 (0)