Skip to content

Commit d8395b7

Browse files
authored
Merge pull request #1539 from taus-semmle/python-controlflownode-getchild-performance-hotfix
Python: Fix bad join ordering in `ControlFlowNode::getAChild()`.
2 parents a6b7f2d + 4ddebb9 commit d8395b7

File tree

7 files changed

+78
-32
lines changed

7 files changed

+78
-32
lines changed

python/ql/src/analysis/DefinitionTracking.qll

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -229,14 +229,18 @@ private predicate method_callsite_defn(MethodCallsiteRefinement def, Definition
229229
pragma [noinline]
230230
private predicate module_and_name_for_import_star(ModuleObject mod, string name, ImportStarRefinement def) {
231231
exists(ImportStarNode im_star |
232-
im_star = def.getDefiningNode() |
233-
name = def.getSourceVariable().getName() and
234-
im_star.getModule().refersTo(mod) and
232+
module_and_name_for_import_star_helper(mod, name, im_star, def) and
235233
mod.exports(name)
236234
)
237235
}
238236

239-
/** Holds if `def` is technically a defn of `var`, but the `from ... import *` does not in fact define `var` */
237+
pragma [noinline]
238+
private predicate module_and_name_for_import_star_helper(ModuleObject mod, string name, ImportStarNode im_star, ImportStarRefinement def) {
239+
im_star = def.getDefiningNode() and
240+
im_star.getModule().refersTo(mod) and
241+
name = def.getSourceVariable().getName()
242+
}
243+
/** Holds if `def` is technically a defn of `var`, but the `from ... import *` does not in fact define `var` */
240244
pragma [noinline]
241245
private predicate variable_not_redefined_by_import_star(EssaVariable var, ImportStarRefinement def) {
242246
var = def.getInput() and

python/ql/src/semmle/dataflow/SsaCompute.qll

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,12 @@ private cached module SsaComputeImpl {
267267

268268
cached module SsaDefinitionsImpl {
269269

270+
pragma [noinline]
271+
private predicate reachesEndOfBlockRec(SsaSourceVariable v, BasicBlock defbb, int defindex, BasicBlock b) {
272+
exists(BasicBlock idom | reachesEndOfBlock(v, defbb, defindex, idom) |
273+
idom = b.getImmediateDominator()
274+
)
275+
}
270276
/**
271277
* The SSA definition of `v` at `def` reaches the end of a basic block `b`, at
272278
* which point it is still live, without crossing another SSA definition of `v`.
@@ -277,13 +283,10 @@ private cached module SsaComputeImpl {
277283
(
278284
defbb = b and SsaComputeImpl::ssaDefReachesRank(v, defbb, defindex, SsaComputeImpl::lastRank(v, b))
279285
or
280-
exists(BasicBlock idom |
281-
idom = b.getImmediateDominator() and
282-
// It is sufficient to traverse the dominator graph, cf. discussion above.
283-
reachesEndOfBlock(v, defbb, defindex, idom) and
284-
not SsaComputeImpl::ssaDef(v, b)
286+
// It is sufficient to traverse the dominator graph, cf. discussion above.
287+
reachesEndOfBlockRec(v, defbb, defindex, b) and
288+
not SsaComputeImpl::ssaDef(v, b)
285289
)
286-
)
287290
}
288291

289292
/**

python/ql/src/semmle/python/Exprs.qll

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,7 @@ class FloatLiteral extends Num {
399399

400400
FloatLiteral() {
401401
not this instanceof ImaginaryLiteral and
402-
exists(string n | n = this.getN() | n.charAt(_) = "." or n.charAt(_) = "e" or n.charAt(_) = "E")
402+
this.getN().regexpMatch(".*[.eE].*")
403403
}
404404

405405
float getValue() {
@@ -427,15 +427,15 @@ class FloatLiteral extends Num {
427427

428428
/** An imaginary numeric constant, such as `3j` */
429429
class ImaginaryLiteral extends Num {
430+
private float value;
430431

431432
ImaginaryLiteral() {
432-
exists(string n | n = this.getN() | n.charAt(_) = "j")
433+
value = this.getN().regexpCapture("(.+)j.*", 1).toFloat()
433434
}
434435

435436
/** Gets the value of this constant as a floating point value */
436437
float getValue() {
437-
exists(string s, int j | s = this.getN() and s.charAt(j) = "j" |
438-
result = s.prefix(j).toFloat())
438+
result = value
439439
}
440440

441441
override string toString() {

python/ql/src/semmle/python/Flow.qll

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -402,14 +402,14 @@ class ControlFlowNode extends @py_flow_node {
402402
}
403403

404404
ControlFlowNode getAChild() {
405-
result = this.getExprChild() and
406-
result.getBasicBlock().dominates(this.getBasicBlock())
405+
result = this.getExprChild(this.getBasicBlock())
407406
}
408407

409408
/* join-ordering helper for `getAChild() */
410409
pragma [noinline]
411-
private ControlFlowNode getExprChild() {
410+
private ControlFlowNode getExprChild(BasicBlock dom) {
412411
this.getNode().(Expr).getAChildNode() = result.getNode() and
412+
result.getBasicBlock().dominates(dom) and
413413
not this instanceof UnaryExprNode
414414
}
415415

python/ql/src/semmle/python/dataflow/SsaDefinitions.qll

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -146,8 +146,13 @@ class NonLocalVariable extends PythonSsaSourceVariable {
146146
this.(LocalVariable).getScope().getEntryNode() = result
147147
}
148148

149+
pragma [noinline]
150+
Scope scope_as_local_variable() {
151+
result = this.(LocalVariable).getScope()
152+
}
153+
149154
override CallNode redefinedAtCallSite() {
150-
result.getScope().getScope*() = this.(LocalVariable).getScope()
155+
result.getScope().getScope*() = this.scope_as_local_variable()
151156
}
152157

153158
}
@@ -173,7 +178,7 @@ class ClassLocalVariable extends PythonSsaSourceVariable {
173178
class BuiltinVariable extends PythonSsaSourceVariable {
174179

175180
BuiltinVariable() {
176-
this instanceof GlobalVariable and
181+
this instanceof GlobalVariable and
177182
not exists(this.(Variable).getAStore()) and
178183
not this.(Variable).getId() = "__name__" and
179184
not this.(Variable).getId() = "__package__" and
@@ -207,13 +212,21 @@ class ModuleVariable extends PythonSsaSourceVariable {
207212
)
208213
}
209214

210-
override ControlFlowNode getAnImplicitUse() {
215+
pragma [noinline]
216+
CallNode global_variable_callnode() {
217+
result.getScope() = this.(GlobalVariable).getScope()
218+
}
219+
220+
pragma[noinline]
221+
ImportMemberNode global_variable_import() {
211222
result.getScope() = this.(GlobalVariable).getScope() and
212-
(
213-
result instanceof CallNode
214-
or
215-
import_from_dot_in_init(result.(ImportMemberNode).getModule(this.getName()))
216-
)
223+
import_from_dot_in_init(result.(ImportMemberNode).getModule(this.getName()))
224+
}
225+
226+
override ControlFlowNode getAnImplicitUse() {
227+
result = global_variable_callnode()
228+
or
229+
result = global_variable_import()
217230
or
218231
exists(ImportTimeScope scope |
219232
scope.entryEdge(result, _) |
@@ -292,8 +305,13 @@ class EscapingGlobalVariable extends ModuleVariable {
292305
result = this.innerScope().getEntryNode()
293306
}
294307

308+
pragma [noinline]
309+
Scope scope_as_global_variable() {
310+
result = this.(GlobalVariable).getScope()
311+
}
312+
295313
override CallNode redefinedAtCallSite() {
296-
result.(CallNode).getScope().getScope*() = this.(GlobalVariable).getScope()
314+
result.(CallNode).getScope().getScope*() = this.scope_as_global_variable()
297315
}
298316

299317
}
@@ -324,8 +342,13 @@ class SpecialSsaSourceVariable extends PythonSsaSourceVariable {
324342
this.getScope().getEntryNode() = result
325343
}
326344

345+
pragma [noinline]
346+
Scope scope_as_global_variable() {
347+
result = this.(GlobalVariable).getScope()
348+
}
349+
327350
override CallNode redefinedAtCallSite() {
328-
result.(CallNode).getScope().getScope*() = this.(GlobalVariable).getScope()
351+
result.(CallNode).getScope().getScope*() = this.scope_as_global_variable()
329352
}
330353

331354
}

python/ql/src/semmle/python/pointsto/PointsTo.qll

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -500,12 +500,17 @@ cached module PointsToInternal {
500500
pointsTo(def.getValue(), context, value, origin)
501501
}
502502

503+
pragma [nomagic]
504+
private predicate sequence_index_points_to(ControlFlowNode f, PointsToContext context, SequenceObjectInternal sequence, ObjectInternal value, int index) {
505+
pointsTo(f, context, sequence, _) and
506+
value = sequence.getItem(index)
507+
}
508+
503509
pragma [noinline]
504510
private predicate multi_assignment_points_to(MultiAssignmentDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin) {
505511
exists(int index, ControlFlowNode rhs, SequenceObjectInternal sequence |
506512
def.indexOf(index, rhs) and
507-
pointsTo(rhs, context, sequence, _) and
508-
value = sequence.getItem(index) and
513+
sequence_index_points_to(rhs, context, sequence, value, index) and
509514
origin = def.getDefiningNode()
510515
)
511516
}
@@ -662,12 +667,18 @@ private module InterModulePointsTo {
662667
pragma [noinline]
663668
private EssaVariable ssa_variable_for_module_attribute(ImportMemberNode f, PointsToContext context) {
664669
exists(string name, ModuleObjectInternal mod, Module m |
665-
mod.getSourceModule() = m and m = f.getEnclosingModule() and m = result.getScope() and
670+
mod.getSourceModule() = m and m = result.getScope() and
666671
PointsToInternal::pointsTo(f.getModule(name), context, mod, _) and
667-
result.getSourceVariable().getName() = name and result.getAUse() = f
672+
result = ssa_variable_for_module_attribute_helper(f, name, m)
668673
)
669674
}
670675

676+
pragma [noinline]
677+
private EssaVariable ssa_variable_for_module_attribute_helper(ImportMemberNode f, string name, Module m) {
678+
result.getSourceVariable().getName() = name and result.getAUse() = f
679+
and m = f.getEnclosingModule()
680+
}
681+
671682
/* Helper for implicit_submodule_points_to */
672683
private ModuleObjectInternal getModule(ImplicitSubModuleDefinition def) {
673684
exists(PackageObjectInternal package |

python/ql/src/semmle/python/types/Extensions.qll

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,12 @@ class ReModulePointToExtension extends PointsToExtension {
142142
sre_constants.attribute("SRE_FLAG_" + name, value, orig) and
143143
origin = orig.asCfgNodeOrHere(this)
144144
)
145-
and context.appliesTo(this)
145+
and pointsTo_helper(context)
146+
}
147+
148+
pragma [noinline]
149+
private predicate pointsTo_helper(Context context) {
150+
context.appliesTo(this)
146151
}
147152

148153
}

0 commit comments

Comments
 (0)