Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 1434b10

Browse files
committed
C#: Speedup structural comparison library
1 parent 08e7499 commit 1434b10

10 files changed

Lines changed: 100 additions & 72 deletions

File tree

csharp/ql/src/Concurrency/UnsafeLazyInitialization.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import semmle.code.csharp.commons.StructuralComparison
1717
class DoubleCheckedLock extends StructuralComparisonConfiguration {
1818
DoubleCheckedLock() { this = "double checked lock" }
1919

20-
override predicate candidate(Element x, Element y) {
20+
override predicate candidate(ControlFlowElement x, ControlFlowElement y) {
2121
exists(IfStmt unlockedIf, IfStmt lockedIf, LockStmt lock |
2222
x = unlockedIf.getCondition() and
2323
y = lockedIf.getCondition() and

csharp/ql/src/Language Abuse/MissedTernaryOpportunity.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import semmle.code.csharp.commons.StructuralComparison
1515
class StructuralComparisonConfig extends StructuralComparisonConfiguration {
1616
StructuralComparisonConfig() { this = "MissedTernaryOpportunity" }
1717

18-
override predicate candidate(Element x, Element y) {
18+
override predicate candidate(ControlFlowElement x, ControlFlowElement y) {
1919
exists(IfStmt is, AssignExpr ae1 |
2020
ae1 = is.getThen().stripSingletonBlocks().(ExprStmt).getExpr()
2121
|

csharp/ql/src/Language Abuse/UselessIsBeforeAs.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import semmle.code.csharp.commons.StructuralComparison
1616
class StructuralComparisonConfig extends StructuralComparisonConfiguration {
1717
StructuralComparisonConfig() { this = "UselessIsBeforeAs" }
1818

19-
override predicate candidate(Element x, Element y) {
19+
override predicate candidate(ControlFlowElement x, ControlFlowElement y) {
2020
exists(IfStmt is, AsExpr ae, IsTypeExpr ie |
2121
ie = is.getCondition().getAChild*() and
2222
ae.getTargetType() = ie.getCheckedType() and

csharp/ql/src/Language Abuse/UselessNullCoalescingExpression.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import semmle.code.csharp.commons.StructuralComparison
1717
class StructuralComparisonConfig extends StructuralComparisonConfiguration {
1818
StructuralComparisonConfig() { this = "UselessNullCoalescingExpression" }
1919

20-
override predicate candidate(Element x, Element y) {
20+
override predicate candidate(ControlFlowElement x, ControlFlowElement y) {
2121
exists(NullCoalescingExpr nce |
2222
x.(Access) = nce.getLeftOperand() and
2323
y.(Access) = nce.getRightOperand().getAChildExpr*()

csharp/ql/src/Likely Bugs/NestedLoopsSameVariable.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import semmle.code.csharp.commons.StructuralComparison as SC
1919
class NestedForConditions extends SC::StructuralComparisonConfiguration {
2020
NestedForConditions() { this = "Compare nested for conditions" }
2121

22-
override predicate candidate(Element e1, Element e2) {
22+
override predicate candidate(ControlFlowElement e1, ControlFlowElement e2) {
2323
exists(NestedForLoopSameVariable nested |
2424
e1 = nested.getInnerForStmt().getCondition() and
2525
e2 = nested.getOuterForStmt().getCondition()

csharp/ql/src/Likely Bugs/SelfAssignment.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import semmle.code.csharp.commons.StructuralComparison
1616
class StructuralComparisonConfig extends StructuralComparisonConfiguration {
1717
StructuralComparisonConfig() { this = "SelfAssignment" }
1818

19-
override predicate candidate(Element x, Element y) {
19+
override predicate candidate(ControlFlowElement x, ControlFlowElement y) {
2020
exists(AssignExpr ae |
2121
// Member initializers are never self-assignments, in particular
2222
// not initializers such as `new C { F = F };`

csharp/ql/src/Performance/UseTryGetValue.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import semmle.code.csharp.controlflow.Guards as G
1616
class SameElement extends StructuralComparisonConfiguration {
1717
SameElement() { this = "Same element" }
1818

19-
override predicate candidate(Element e1, Element e2) {
19+
override predicate candidate(ControlFlowElement e1, ControlFlowElement e2) {
2020
exists(MethodCall mc, IndexerRead access |
2121
mc.getTarget().hasName("ContainsKey") and
2222
access.getQualifier().(G::GuardedExpr).isGuardedBy(mc, mc.getQualifier(), _) and

csharp/ql/src/semmle/code/csharp/commons/Constants.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ private module ConstantComparisonOperation {
108108
private class StructuralComparisonConfig extends StructuralComparison::StructuralComparisonConfiguration {
109109
StructuralComparisonConfig() { this = "CompareIdenticalValues" }
110110

111-
override predicate candidate(Element x, Element y) {
111+
override predicate candidate(ControlFlowElement x, ControlFlowElement y) {
112112
exists(ComparisonTest ct |
113113
x = ct.getFirstArgument() and
114114
y = ct.getSecondArgument()

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

Lines changed: 91 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@ private Declaration referenceAttribute(Expr e) {
1515
}
1616

1717
/** Gets the AST node kind element `e`. */
18-
private int elementKind(Element e) {
18+
private int elementKind(ControlFlowElement e) {
1919
expressions(e, result, _)
2020
or
2121
exists(int k | statements(e, k) | result = -k)
2222
}
2323

24-
private int getNumberOfActualChildren(Element e) {
24+
private int getNumberOfActualChildren(ControlFlowElement e) {
2525
if e.(MemberAccess).targetIsThisInstance()
2626
then result = e.getNumberOfChildren() - 1
2727
else result = e.getNumberOfChildren()
@@ -55,21 +55,25 @@ abstract class StructuralComparisonConfiguration extends string {
5555
* In fact, not including the symmetrically implied fact will save
5656
* half the computation time on the structural comparison.
5757
*/
58-
abstract predicate candidate(Element x, Element y);
58+
abstract predicate candidate(ControlFlowElement x, ControlFlowElement y);
5959

60-
private predicate candidateInternal(Element x, Element y) {
61-
(
62-
candidate(x, y)
63-
or
64-
exists(Element xParent, Element yParent, int i | candidateInternal(xParent, yParent) |
65-
hasChild(xParent, i, x) and
66-
hasChild(yParent, i, y)
67-
)
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)
6867
)
6968
}
7069

7170
pragma[noinline]
72-
private predicate hasChild(Element e, int i, Element child) { child = e.getChild(i) }
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+
}
7377

7478
private predicate sameByValue(Expr x, Expr y) { sameByValueAux(x, y, y.getValue()) }
7579

@@ -79,47 +83,57 @@ abstract class StructuralComparisonConfiguration extends string {
7983
value = x.getValue()
8084
}
8185

86+
private ControlFlowElement getRankedChild(ControlFlowElement cfe, int rnk, int i) {
87+
(candidateInternal(cfe, _) or candidateInternal(_, cfe)) and
88+
i = rank[rnk](int j |
89+
exists(ControlFlowElement child | child = cfe.getChild(j) |
90+
not (j = -1 and cfe.(MemberAccess).targetIsThisInstance())
91+
)
92+
) and
93+
result = cfe.getChild(i)
94+
}
95+
8296
pragma[nomagic]
83-
private predicate sameByStructure(Element x, Element y) {
97+
private predicate sameByStructure0(
98+
ControlFlowElement x, ControlFlowElement y, int elementKind, int children
99+
) {
100+
candidateInternal(x, y) and
101+
elementKind = elementKind(x) and
102+
children = getNumberOfActualChildren(x) and
103+
not (x.(Expr).hasValue() and y.(Expr).hasValue())
104+
}
105+
106+
pragma[nomagic]
107+
private predicate sameByStructure(ControlFlowElement x, ControlFlowElement y, int i) {
108+
i = 0 and
84109
// At least one of `x` and `y` must not have a value, they must have
85110
// the same kind, and the same number of children
86-
sameByStructureCandidate(x, y, elementKind(y), getNumberOfActualChildren(y)) and
111+
sameByStructure0(x, y, elementKind(y), getNumberOfActualChildren(y)) and
87112
// If one of them has a reference attribute, they should both reference
88113
// the same node
89114
(exists(referenceAttribute(x)) implies referenceAttribute(x) = referenceAttribute(y)) and
90115
// x is a member access on `this` iff y is
91116
(x.(MemberAccess).targetIsThisInstance() implies y.(MemberAccess).targetIsThisInstance()) and
92-
(y.(MemberAccess).targetIsThisInstance() implies x.(MemberAccess).targetIsThisInstance()) and
93-
// All of their corresponding children must be structurally equal
94-
forall(int i, Element xc |
95-
xc = x.getChild(i) and
96-
// exclude `this` qualifier, which has been checked above
97-
not (i = -1 and x.(MemberAccess).targetIsThisInstance())
98-
|
99-
sameInternal(xc, y.getChild(i))
117+
(y.(MemberAccess).targetIsThisInstance() implies x.(MemberAccess).targetIsThisInstance())
118+
or
119+
exists(int j | sameByStructure(x, y, i - 1) |
120+
sameInternal(getRankedChild(x, i, j), getRankedChild(y, i, j))
100121
)
101122
}
102123

103-
private predicate sameByStructureCandidate(Element x, Element y, int elementKind, int children) {
104-
candidateInternal(x, y) and
105-
elementKind = elementKind(x) and
106-
children = getNumberOfActualChildren(x) and
107-
not (x.(Expr).hasValue() and y.(Expr).hasValue())
108-
}
109-
110124
pragma[nomagic]
111-
private predicate sameInternal(Element x, Element y) {
125+
private predicate sameInternal(ControlFlowElement x, ControlFlowElement y) {
112126
sameByValue(x, y)
113127
or
114-
sameByStructure(x, y)
128+
sameByStructure(x, y, getNumberOfActualChildren(x))
115129
}
116130

117131
/**
118132
* Holds if elements `x` and `y` structurally equal. `x` and `y` must be
119133
* flagged as candidates for structural equality, that is,
120134
* `candidate(x, y)` must hold.
121135
*/
122-
predicate same(Element x, Element y) {
136+
predicate same(ControlFlowElement x, ControlFlowElement y) {
123137
candidate(x, y) and
124138
sameInternal(x, y)
125139
}
@@ -166,21 +180,25 @@ module Internal {
166180
* In fact, not including the symmetrically implied fact will save
167181
* half the computation time on the structural comparison.
168182
*/
169-
abstract predicate candidate(Element x, Element y);
170-
171-
private predicate candidateInternal(Element x, Element y) {
172-
(
173-
candidate(x, y)
174-
or
175-
exists(Element xParent, Element yParent, int i | candidateInternal(xParent, yParent) |
176-
hasChild(xParent, i, x) and
177-
hasChild(yParent, i, y)
178-
)
183+
abstract predicate candidate(ControlFlowElement x, ControlFlowElement y);
184+
185+
private predicate candidateInternal(ControlFlowElement x, ControlFlowElement y) {
186+
candidate(x, y)
187+
or
188+
exists(ControlFlowElement xParent, ControlFlowElement yParent, int i |
189+
candidateInternalChild(xParent, i, x, yParent)
190+
|
191+
y = yParent.getChild(i)
179192
)
180193
}
181194

182195
pragma[noinline]
183-
private predicate hasChild(Element e, int i, Element child) { child = e.getChild(i) }
196+
private predicate candidateInternalChild(
197+
ControlFlowElement x, int i, ControlFlowElement xChild, ControlFlowElement y
198+
) {
199+
candidateInternal(x, y) and
200+
xChild = x.getChild(i)
201+
}
184202

185203
private predicate sameByValue(Expr x, Expr y) { sameByValueAux(x, y, y.getValue()) }
186204

@@ -190,47 +208,57 @@ module Internal {
190208
value = x.getValue()
191209
}
192210

211+
private ControlFlowElement getRankedChild(ControlFlowElement cfe, int rnk, int i) {
212+
(candidateInternal(cfe, _) or candidateInternal(_, cfe)) and
213+
i = rank[rnk](int j |
214+
exists(ControlFlowElement child | child = cfe.getChild(j) |
215+
not (j = -1 and cfe.(MemberAccess).targetIsThisInstance())
216+
)
217+
) and
218+
result = cfe.getChild(i)
219+
}
220+
221+
pragma[nomagic]
222+
private predicate sameByStructure0(
223+
ControlFlowElement x, ControlFlowElement y, int elementKind, int children
224+
) {
225+
candidateInternal(x, y) and
226+
elementKind = elementKind(x) and
227+
children = getNumberOfActualChildren(x) and
228+
not (x.(Expr).hasValue() and y.(Expr).hasValue())
229+
}
230+
193231
pragma[nomagic]
194-
private predicate sameByStructure(Element x, Element y) {
232+
private predicate sameByStructure(ControlFlowElement x, ControlFlowElement y, int i) {
233+
i = 0 and
195234
// At least one of `x` and `y` must not have a value, they must have
196235
// the same kind, and the same number of children
197-
sameByStructureCandidate(x, y, elementKind(y), getNumberOfActualChildren(y)) and
236+
sameByStructure0(x, y, elementKind(y), getNumberOfActualChildren(y)) and
198237
// If one of them has a reference attribute, they should both reference
199238
// the same node
200239
(exists(referenceAttribute(x)) implies referenceAttribute(x) = referenceAttribute(y)) and
201240
// x is a member access on `this` iff y is
202241
(x.(MemberAccess).targetIsThisInstance() implies y.(MemberAccess).targetIsThisInstance()) and
203-
(y.(MemberAccess).targetIsThisInstance() implies x.(MemberAccess).targetIsThisInstance()) and
204-
// All of their corresponding children must be structurally equal
205-
forall(int i, Element xc |
206-
xc = x.getChild(i) and
207-
// exclude `this` qualifier, which has been checked above
208-
not (i = -1 and x.(MemberAccess).targetIsThisInstance())
209-
|
210-
sameInternal(xc, y.getChild(i))
242+
(y.(MemberAccess).targetIsThisInstance() implies x.(MemberAccess).targetIsThisInstance())
243+
or
244+
exists(int j | sameByStructure(x, y, i - 1) |
245+
sameInternal(getRankedChild(x, i, j), getRankedChild(y, i, j))
211246
)
212247
}
213248

214-
private predicate sameByStructureCandidate(Element x, Element y, int elementKind, int children) {
215-
candidateInternal(x, y) and
216-
elementKind = elementKind(x) and
217-
children = getNumberOfActualChildren(x) and
218-
not (x.(Expr).hasValue() and y.(Expr).hasValue())
219-
}
220-
221249
pragma[nomagic]
222-
private predicate sameInternal(Element x, Element y) {
250+
private predicate sameInternal(ControlFlowElement x, ControlFlowElement y) {
223251
sameByValue(x, y)
224252
or
225-
sameByStructure(x, y)
253+
sameByStructure(x, y, getNumberOfActualChildren(x))
226254
}
227255

228256
/**
229257
* Holds if elements `x` and `y` structurally equal. `x` and `y` must be
230258
* flagged as candidates for structural equality, that is,
231259
* `candidate(x, y)` must hold.
232260
*/
233-
predicate same(Element x, Element y) {
261+
predicate same(ControlFlowElement x, ControlFlowElement y) {
234262
candidate(x, y) and
235263
sameInternal(x, y)
236264
}

csharp/ql/src/semmle/code/csharp/controlflow/Guards.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1200,7 +1200,7 @@ module Internal {
12001200
private class ConditionOnExprComparisonConfig extends InternalStructuralComparisonConfiguration {
12011201
ConditionOnExprComparisonConfig() { this = "ConditionOnExprComparisonConfig" }
12021202

1203-
override predicate candidate(Element x, Element y) {
1203+
override predicate candidate(ControlFlowElement x, ControlFlowElement y) {
12041204
exists(BasicBlock bb, Declaration d |
12051205
candidateAux(x, d, bb) and
12061206
y = any(AccessOrCallExpr e |

0 commit comments

Comments
 (0)