@@ -6,26 +6,158 @@ import csharp
6
6
* scope variables.
7
7
*/
8
8
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 > ;
11
143
12
144
class Definition extends SsaImpl:: Definition {
13
145
final AssignableRead getARead ( ) {
14
- exists ( BasicBlock bb , int i |
146
+ exists ( SsaInput :: BasicBlock bb , int i |
15
147
SsaImpl:: ssaDefReachesRead ( _, this , bb , i ) and
16
148
result = bb .getElement ( i )
17
149
)
18
150
}
19
151
20
152
final AssignableDefinition getDefinition ( ) {
21
- exists ( BasicBlock bb , int i , SourceVariable v |
153
+ exists ( SsaInput :: BasicBlock bb , int i , SsaInput :: SourceVariable v |
22
154
this .definesAt ( v , bb , i ) and
23
155
definitionAt ( result , bb , i , v )
24
156
)
25
157
}
26
158
27
159
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 |
29
161
this .definesAt ( _, bb1 , i1 ) and
30
162
SsaImpl:: adjacentDefRead ( this , bb1 , i1 , bb2 , i2 ) and
31
163
result = bb2 .getElement ( i2 )
@@ -42,14 +174,14 @@ module PreSsa {
42
174
not result instanceof PhiNode
43
175
}
44
176
45
- final predicate isLiveAtEndOfBlock ( BasicBlock bb ) {
177
+ final predicate isLiveAtEndOfBlock ( SsaInput :: BasicBlock bb ) {
46
178
SsaImpl:: ssaDefReachesEndOfBlock ( bb , this , _)
47
179
}
48
180
49
181
Location getLocation ( ) {
50
182
result = this .getDefinition ( ) .getLocation ( )
51
183
or
52
- exists ( Callable c , BasicBlock bb , SourceVariable v |
184
+ exists ( Callable c , SsaInput :: BasicBlock bb , SsaInput :: SourceVariable v |
53
185
this .definesAt ( v , bb , - 1 ) and
54
186
implicitEntryDef ( c , bb , v ) and
55
187
result = c .getLocation ( )
@@ -64,9 +196,9 @@ module PreSsa {
64
196
}
65
197
66
198
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 |
68
200
read1 = bb1 .getElement ( i1 ) and
69
- variableRead ( bb1 , i1 , _, true ) and
201
+ SsaInput :: variableRead ( bb1 , i1 , _, true ) and
70
202
SsaImpl:: adjacentDefRead ( _, bb1 , i1 , bb2 , i2 ) and
71
203
read2 = bb2 .getElement ( i2 )
72
204
)
0 commit comments