Repository navigation
Expand file tree
/
Copy pathSSA.qll
More file actions
237 lines (206 loc) · 8 KB
/
Copy pathSSA.qll
File metadata and controls
237 lines (206 loc) · 8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
/**
* Provides classes and predicates for SSA representation (Static Single Assignment form).
*
* An SSA variable consists of the pair of a `SsaSourceVariable` and a
* `ControlFlowNode` at which it is defined. Each SSA variable is defined
* either by a phi node, an implicit initial value (for parameters and fields),
* an explicit update, or an implicit update (for fields).
* An implicit update occurs either at a `Call` that might modify a field, or
* at another update that can update the qualifier of a field.
*/
overlay[local?]
module;
import java
private import internal.SsaImpl
import internal.SsaImpl::Ssa as Ssa
import Ssa
/**
* A fully qualified variable in the context of a `Callable` in which it is
* accessed.
*
* This is either a local variable or a fully qualified field, `q.f1.f2....fn`,
* where the base qualifier `q` is either `this`, a local variable, or a type
* in case `f1` is static.
*/
class SsaSourceVariable extends TSsaSourceVariable {
/** Gets the variable corresponding to this `SsaSourceVariable`. */
Variable getVariable() {
this = TLocalVar(_, result) or
this = TPlainField(_, result) or
this = TEnclosingField(_, result, _) or
this = TQualifiedField(_, _, result)
}
/**
* Gets an access of this `SsaSourceVariable`. This access is within
* `this.getEnclosingCallable()`. Note that `LocalScopeVariable`s that are
* accessed from nested callables are therefore associated with several
* `SsaSourceVariable`s.
*/
cached
VarAccess getAnAccess() {
exists(LocalScopeVariable v, Callable c |
this = TLocalVar(c, v) and result = v.getAnAccess() and result.getEnclosingCallable() = c
)
or
exists(Field f, Callable c | fieldAccessInCallable(result, f, c) |
(result.(FieldAccess).isOwnFieldAccess() or f.isStatic()) and
this = TPlainField(c, f)
or
exists(RefType t |
this = TEnclosingField(c, f, t) and result.(FieldAccess).isEnclosingFieldAccess(t)
)
or
exists(SsaSourceVariable q |
result.getQualifier() = q.getAnAccess() and this = TQualifiedField(c, q, f)
)
)
}
/** Gets the `Callable` in which this `SsaSourceVariable` is defined. */
Callable getEnclosingCallable() {
this = TLocalVar(result, _) or
this = TPlainField(result, _) or
this = TEnclosingField(result, _, _) or
this = TQualifiedField(result, _, _)
}
/** Gets a textual representation of this `SsaSourceVariable`. */
string toString() {
exists(LocalScopeVariable v, Callable c | this = TLocalVar(c, v) |
if c = v.getCallable()
then result = v.getName()
else result = c.getName() + "(..)." + v.getName()
)
or
result = this.(SsaSourceField).ppQualifier() + "." + this.getVariable().toString()
}
/**
* Gets the first access to `this` in terms of source code location. This is
* used as the representative location for named fields that otherwise would
* not have a specific source code location.
*/
private VarAccess getFirstAccess() {
result =
min(this.getAnAccess() as a
order by
a.getLocation().getStartLine(), a.getLocation().getStartColumn()
)
}
/** Gets the source location for this element. */
Location getLocation() {
exists(LocalScopeVariable v | this = TLocalVar(_, v) and result = v.getLocation())
or
this instanceof SsaSourceField and result = this.getFirstAccess().getLocation()
}
/** Gets the type of this variable. */
Type getType() { result = this.getVariable().getType() }
/** Gets the qualifier, if any. */
SsaSourceVariable getQualifier() { this = TQualifiedField(_, result, _) }
/** Gets an SSA variable that has this variable as its underlying source variable. */
SsaDefinition getAnSsaVariable() { result.getSourceVariable() = this }
}
/**
* A fully qualified field in the context of a `Callable` in which it is
* accessed.
*/
class SsaSourceField extends SsaSourceVariable {
SsaSourceField() {
this = TPlainField(_, _) or this = TEnclosingField(_, _, _) or this = TQualifiedField(_, _, _)
}
/** Gets the field corresponding to this named field. */
Field getField() { result = this.getVariable() }
/** Gets a string representation of the qualifier. */
string ppQualifier() {
exists(Field f | this = TPlainField(_, f) |
if f.isStatic() then result = f.getDeclaringType().getQualifiedName() else result = "this"
)
or
exists(RefType t | this = TEnclosingField(_, _, t) | result = t.toString() + ".this")
or
exists(SsaSourceVariable q | this = TQualifiedField(_, q, _) | result = q.toString())
}
/** Holds if the field itself or any of the fields part of the qualifier are volatile. */
predicate isVolatile() {
this.getField().isVolatile() or
this.getQualifier().(SsaSourceField).isVolatile()
}
}
/** An SSA definition in a closure that captures a variable. */
class SsaCapturedDefinition extends SsaImplicitEntryDefinition {
SsaCapturedDefinition() { captures(this, _) }
override string toString() { result = "SSA capture def(" + this.getSourceVariable() + ")" }
/** Holds if this definition captures the value of `capturedvar`. */
predicate captures(SsaDefinition capturedvar) { captures(this, capturedvar) }
/**
* Gets a definition that ultimately defines the captured variable and is not itself a phi node.
*/
SsaDefinition getAnUltimateCapturedDefinition() {
exists(SsaDefinition capturedvar |
captures(this, capturedvar) and result = capturedvar.getAnUltimateDefinition()
)
}
}
/**
* An SSA definition representing the potential definition of a variable
* via a call.
*/
class SsaImplicitCallDefinition extends SsaImplicitWrite {
SsaImplicitCallDefinition() { isNonLocal(this) and not hasQualifierUpdate(this) }
override string toString() { result = "SSA call def(" + this.getSourceVariable() + ")" }
/**
* Gets a reachable `FieldWrite` that might represent this ssa update, if any.
*/
overlay[global]
FieldWrite getANonLocalUpdate() { result = getANonLocalUpdate(this) }
}
/** An SSA definition due to an update of the qualifier. */
class SsaImplicitQualifierDefinition extends SsaImplicitWrite {
SsaImplicitQualifierDefinition() { hasQualifierUpdate(this) }
override string toString() { result = "SSA qualifier def(" + this.getSourceVariable() + ")" }
}
/**
* Gets an access of the SSA source variable underlying this SSA variable
* that can be reached from this SSA variable without passing through any
* other uses, but potentially through phi nodes and uncertain implicit
* updates.
*/
VarRead ssaGetAFirstUse(SsaDefinition def) { firstUse(def, result) }
private predicate hasQualifierUpdate(SsaImplicitWrite def) {
exists(SsaWriteDefinition qdef, BasicBlock bb, int i |
qdef.definesAt(def.getSourceVariable().getQualifier(), bb, i) and
def.definesAt(_, bb, i) and
not qdef instanceof SsaImplicitEntryDefinition
)
}
/**
* Gets a reachable `FieldWrite` that might represent this ssa update, if any.
*/
overlay[global]
private FieldWrite getANonLocalUpdate(SsaImplicitWrite calldef) {
exists(SsaSourceField f, Callable setter |
relevantFieldUpdate(setter, f.getField(), result) and
defUpdatesNamedField(calldef, f, setter)
)
}
overlay[global]
private predicate isNonLocalImpl(SsaImplicitWrite calldef) { exists(getANonLocalUpdate(calldef)) }
private predicate isNonLocal(SsaImplicitWrite calldef) = forceLocal(isNonLocalImpl/1)(calldef)
private class RefTypeCastingExpr extends CastingExpr {
RefTypeCastingExpr() {
this.getType() instanceof RefType and
not this instanceof SafeCastExpr
}
}
/**
* Gets an expression that has the same value as the given SSA variable.
*
* The `VarAccess` represents the access to `v` that `result` has the same value as.
*/
Expr sameValue(SsaDefinition v, VarAccess va) {
result = v.getARead() and result = va
or
result.(AssignExpr).getDest() = va and result = v.(SsaExplicitWrite).getDefiningExpr()
or
result.(AssignExpr).getSource() = sameValue(v, va)
or
result.(RefTypeCastingExpr).getExpr() = sameValue(v, va)
}
import SsaPublic