Skip to content

Commit f474fdd

Browse files
authored
Merge pull request #731 from xiemaisi/js/performance-fiddling
Approved by asger-semmle, esben-semmle
2 parents 9219214 + 8951eae commit f474fdd

File tree

4 files changed

+88
-52
lines changed

4 files changed

+88
-52
lines changed

javascript/ql/src/LanguageFeatures/InconsistentNew.ql

Lines changed: 7 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -28,41 +28,20 @@ predicate guardsAgainstMissingNew(Function f) {
2828
)
2929
}
3030

31-
/**
32-
* Holds if `callee` is a function that may be invoked at callsite `cs`,
33-
* where `imprecision` is a heuristic measure of how likely it is that `callee`
34-
* is only suggested as a potential callee due to imprecise analysis of global
35-
* variables and is not, in fact, a viable callee at all.
36-
*/
37-
predicate calls(DataFlow::InvokeNode cs, Function callee, int imprecision) {
38-
callee = cs.getACallee() and
39-
(
40-
// if global flow was used to derive the callee, we may be imprecise
41-
if cs.isIndefinite("global")
42-
then
43-
// callees within the same file are probably genuine
44-
callee.getFile() = cs.getFile() and imprecision = 0
45-
or
46-
// calls to global functions declared in an externs file are fairly
47-
// safe as well
48-
callee.inExternsFile() and imprecision = 1
49-
or
50-
// otherwise we make worst-case assumptions
51-
imprecision = 2
52-
else
53-
// no global flow, so no imprecision
54-
imprecision = 0
55-
)
56-
}
57-
5831
/**
5932
* Gets a function that may be invoked at `cs`, preferring callees that
6033
* are less likely to be derived due to analysis imprecision and excluding
6134
* whitelisted call sites and callees. Additionally, `isNew` is bound to
6235
* `true` if `cs` is a `new` expression, and to `false` otherwise.
6336
*/
6437
Function getALikelyCallee(DataFlow::InvokeNode cs, boolean isNew) {
65-
calls(cs, result, min(int p | calls(cs, _, p))) and
38+
result = min(Function callee, int imprecision |
39+
callee = cs.getACallee(imprecision)
40+
|
41+
callee
42+
order by
43+
imprecision
44+
) and
6645
not cs.isUncertain() and
6746
not whitelistedCall(cs) and
6847
not whitelistedCallee(result) and

javascript/ql/src/Statements/UselessConditional.ql

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,27 @@ import semmle.javascript.RestrictedLocations
1717
import semmle.javascript.dataflow.Refinements
1818
import semmle.javascript.DefensiveProgramming
1919

20+
/**
21+
* Gets the unique definition of `v`.
22+
*
23+
* If `v` has no definitions or more than one, or if its single definition
24+
* is a destructuring assignment, this predicate is undefined.
25+
*/
26+
VarDef getSingleDef(Variable v) {
27+
strictcount(VarDef vd | vd.getAVariable() = v) = 1 and
28+
result.getTarget() = v.getAReference()
29+
}
30+
2031
/**
2132
* Holds if variable `v` looks like a symbolic constant, that is, it is assigned
2233
* exactly once, either in a `const` declaration or with a constant initializer.
2334
*
2435
* We do not consider conditionals to be useless if they check a symbolic constant.
2536
*/
2637
predicate isSymbolicConstant(Variable v) {
27-
// defined exactly once
28-
count(VarDef vd | vd.getAVariable() = v) = 1 and
29-
// the definition is either a `const` declaration or it assigns a constant to it
30-
exists(VarDef vd | vd.getAVariable() = v and count(vd.getAVariable()) = 1 |
31-
vd.(VariableDeclarator).getDeclStmt() instanceof ConstDeclStmt or
38+
exists(VarDef vd | vd = getSingleDef(v) |
39+
vd.(VariableDeclarator).getDeclStmt() instanceof ConstDeclStmt
40+
or
3241
isConstant(vd.getSource())
3342
)
3443
}

javascript/ql/src/semmle/javascript/dataflow/Nodes.qll

Lines changed: 66 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -78,21 +78,20 @@ class InvokeNode extends DataFlow::SourceNode {
7878
}
7979

8080
/** Gets an abstract value representing possible callees of this call site. */
81-
cached
82-
AbstractValue getACalleeValue() { result = impl.getCalleeNode().analyze().getAValue() }
83-
84-
/** Gets a potential callee based on dataflow analysis results. */
85-
private Function getACalleeFromDataflow() {
86-
result = getACalleeValue().(AbstractCallable).getFunction()
87-
}
81+
AbstractValue getACalleeValue() { result = InvokeNode::getACalleeValue(this) }
8882

8983
/** Gets a potential callee of this call site. */
90-
Function getACallee() {
91-
result = getACalleeFromDataflow()
92-
or
93-
not exists(getACalleeFromDataflow()) and
94-
result = impl.(DataFlow::Impl::ExplicitInvokeNode).asExpr().(InvokeExpr).getResolvedCallee()
95-
}
84+
Function getACallee() { result = InvokeNode::getACallee(this) }
85+
86+
/**
87+
* Gets a callee of this call site where `imprecision` is a heuristic measure of how
88+
* likely it is that `callee` is only suggested as a potential callee due to
89+
* imprecise analysis of global variables and is not, in fact, a viable callee at all.
90+
*
91+
* Callees with imprecision zero, in particular, have either been derived without
92+
* considering global variables, or are calls to a global variable within the same file.
93+
*/
94+
Function getACallee(int imprecision) { result = InvokeNode::getACallee(this, imprecision) }
9695

9796
/**
9897
* Holds if the approximation of possible callees for this call site is
@@ -133,6 +132,60 @@ class InvokeNode extends DataFlow::SourceNode {
133132
predicate isUncertain() { isImprecise() or isIncomplete() }
134133
}
135134

135+
/** Auxiliary module used to cache a few related predicates together. */
136+
cached
137+
private module InvokeNode {
138+
/** Gets an abstract value representing possible callees of `invk`. */
139+
cached
140+
AbstractValue getACalleeValue(InvokeNode invk) {
141+
result = invk.getCalleeNode().analyze().getAValue()
142+
}
143+
144+
/** Gets a potential callee of `invk` based on dataflow analysis results. */
145+
private Function getACalleeFromDataflow(InvokeNode invk) {
146+
result = getACalleeValue(invk).(AbstractCallable).getFunction()
147+
}
148+
149+
/** Gets a potential callee of `invk`. */
150+
cached
151+
Function getACallee(InvokeNode invk) {
152+
result = getACalleeFromDataflow(invk)
153+
or
154+
not exists(getACalleeFromDataflow(invk)) and
155+
result = invk.(DataFlow::Impl::ExplicitInvokeNode).asExpr().(InvokeExpr).getResolvedCallee()
156+
}
157+
158+
/**
159+
* Gets a callee of `invk` where `imprecision` is a heuristic measure of how
160+
* likely it is that `callee` is only suggested as a potential callee due to
161+
* imprecise analysis of global variables and is not, in fact, a viable callee at all.
162+
*
163+
* Callees with imprecision zero, in particular, have either been derived without
164+
* considering global variables, or are calls to a global variable within the same file.
165+
*/
166+
cached
167+
Function getACallee(InvokeNode invk, int imprecision) {
168+
result = getACallee(invk) and
169+
(
170+
// if global flow was used to derive the callee, we may be imprecise
171+
if invk.isIndefinite("global")
172+
then
173+
// callees within the same file are probably genuine
174+
result.getFile() = invk.getFile() and imprecision = 0
175+
or
176+
// calls to global functions declared in an externs file are fairly
177+
// safe as well
178+
result.inExternsFile() and imprecision = 1
179+
or
180+
// otherwise we make worst-case assumptions
181+
imprecision = 2
182+
else
183+
// no global flow, so no imprecision
184+
imprecision = 0
185+
)
186+
}
187+
}
188+
136189
/** A data flow node corresponding to a function call without `new`. */
137190
class CallNode extends InvokeNode {
138191
override DataFlow::Impl::CallNodeDef impl;

javascript/ql/src/semmle/javascript/dataflow/internal/FlowSteps.qll

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,7 @@ predicate shouldTrackProperties(AbstractValue obj) {
2020
/**
2121
* Holds if `invk` may invoke `f`.
2222
*/
23-
predicate calls(DataFlow::InvokeNode invk, Function f) {
24-
if invk.isIndefinite("global")
25-
then (
26-
f = invk.getACallee() and f.getFile() = invk.getFile()
27-
) else f = invk.getACallee()
28-
}
23+
predicate calls(DataFlow::InvokeNode invk, Function f) { f = invk.getACallee(0) }
2924

3025
/**
3126
* Holds if `invk` may invoke `f` indirectly through the given `callback` argument.

0 commit comments

Comments
 (0)