Skip to content

Commit 7d99230

Browse files
committed
JS: Fix perf issue from overriding isIncomplete
1 parent e586837 commit 7d99230

File tree

4 files changed

+59
-42
lines changed

4 files changed

+59
-42
lines changed

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import javascript
2222
private import internal.CallGraphs
2323
private import internal.FlowSteps as FlowSteps
2424
private import internal.DataFlowNode
25+
private import internal.AnalyzedParameters
2526

2627
module DataFlow {
2728
/**
@@ -1527,9 +1528,13 @@ module DataFlow {
15271528
e instanceof TaggedTemplateExpr
15281529
or
15291530
e instanceof Parameter and
1530-
not localArgumentPassing(_, e)
1531+
not localArgumentPassing(_, e) and
1532+
not isAnalyzedParameter(e) and
1533+
not e.(Parameter).isRestParameter()
15311534
)
15321535
or
1536+
nd.(AnalyzedParameter).hasIncompleteness(cause)
1537+
or
15331538
nd.asExpr() instanceof ExternalModuleReference and
15341539
cause = "import"
15351540
or
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
private import javascript
2+
private import VariableTypeInference
3+
4+
/**
5+
* Holds if `p` is analyzed precisely by the type inference.
6+
*/
7+
pragma[nomagic]
8+
predicate isAnalyzedParameter(Parameter p) {
9+
exists(FunctionWithAnalyzedParameters f, int parmIdx | p = f.getParameter(parmIdx) |
10+
// we cannot track flow into rest parameters
11+
not p.(Parameter).isRestParameter()
12+
)
13+
}
14+
15+
/**
16+
* A parameter whose value is propagated interprocedurally.
17+
*/
18+
class AnalyzedParameter extends AnalyzedValueNode {
19+
override Parameter astNode;
20+
21+
AnalyzedParameter() { isAnalyzedParameter(astNode) }
22+
23+
FunctionWithAnalyzedParameters getFunction() { astNode = result.getAParameter() }
24+
25+
override AbstractValue getALocalValue() {
26+
exists(DataFlow::AnalyzedNode pred |
27+
getFunction().argumentPassing(astNode, pred.asExpr()) and
28+
result = pred.getALocalValue()
29+
)
30+
or
31+
not getFunction().mayReceiveArgument(astNode) and
32+
result = TAbstractUndefined()
33+
or
34+
result = astNode.getDefault().analyze().getALocalValue()
35+
}
36+
37+
/**
38+
* Whether this node should be considered incomplete with the given cause.
39+
*
40+
* For performance reasons, this is not an override of `isIncomplete`, but is
41+
* explicitly included in that predicate.
42+
*/
43+
predicate hasIncompleteness(DataFlow::Incompleteness cause) {
44+
getFunction().isIncomplete(cause)
45+
or
46+
not getFunction().argumentPassing(astNode, _) and
47+
getFunction().mayReceiveArgument(astNode) and
48+
cause = "call"
49+
}
50+
}

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

Lines changed: 3 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
private import javascript
88
private import AbstractValuesImpl
9+
private import AnalyzedParameters
910
private import semmle.javascript.dataflow.InferredTypes
1011
private import semmle.javascript.dataflow.Refinements
1112

@@ -140,41 +141,6 @@ class AnalyzedVarDef extends VarDef {
140141
TopLevel getTopLevel() { result = this.(ASTNode).getTopLevel() }
141142
}
142143

143-
private predicate isAnalyzedParameter(Parameter p) {
144-
exists(FunctionWithAnalyzedParameters f, int parmIdx | p = f.getParameter(parmIdx) |
145-
// we cannot track flow into rest parameters
146-
not p.(Parameter).isRestParameter()
147-
)
148-
}
149-
150-
private class AnalyzedParameter extends AnalyzedValueNode {
151-
override Parameter astNode;
152-
153-
AnalyzedParameter() { isAnalyzedParameter(astNode) }
154-
155-
FunctionWithAnalyzedParameters getFunction() { astNode = result.getAParameter() }
156-
157-
override AbstractValue getALocalValue() {
158-
exists(DataFlow::AnalyzedNode pred |
159-
getFunction().argumentPassing(astNode, pred.asExpr()) and
160-
result = pred.getALocalValue()
161-
)
162-
or
163-
not getFunction().mayReceiveArgument(astNode) and
164-
result = TAbstractUndefined()
165-
or
166-
result = astNode.getDefault().analyze().getALocalValue()
167-
}
168-
169-
override predicate isIncomplete(DataFlow::Incompleteness cause) {
170-
getFunction().isIncomplete(cause)
171-
or
172-
not getFunction().argumentPassing(astNode, _) and
173-
getFunction().mayReceiveArgument(astNode) and
174-
cause = "call"
175-
}
176-
}
177-
178144
/**
179145
* Flow analysis for simple parameters of selected functions.
180146
*/
@@ -193,8 +159,6 @@ private class AnalyzedRestParameter extends AnalyzedValueNode {
193159
AnalyzedRestParameter() { astNode.(Parameter).isRestParameter() }
194160

195161
override AbstractValue getALocalValue() { result = TAbstractOtherObject() }
196-
197-
override predicate isIncomplete(DataFlow::Incompleteness cause) { none() }
198162
}
199163

200164
/**
@@ -679,7 +643,7 @@ abstract class FunctionWithAnalyzedParameters extends Function {
679643
* Holds if `p` is a parameter of this function and `arg` is
680644
* the corresponding argument.
681645
*/
682-
abstract predicate argumentPassing(SimpleParameter p, Expr arg);
646+
abstract predicate argumentPassing(Parameter p, Expr arg);
683647

684648
/**
685649
* Holds if `p` is a parameter of this function that may receive a value from an argument.
@@ -699,7 +663,7 @@ abstract private class CallWithAnalyzedParameters extends FunctionWithAnalyzedPa
699663
*/
700664
abstract DataFlow::InvokeNode getAnInvocation();
701665

702-
override predicate argumentPassing(SimpleParameter p, Expr arg) {
666+
override predicate argumentPassing(Parameter p, Expr arg) {
703667
exists(DataFlow::InvokeNode invk, int argIdx | invk = getAnInvocation() |
704668
p = getParameter(argIdx) and
705669
not p.isRestParameter() and

javascript/ql/test/library-tests/DataFlow/incomplete.expected

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
| eval.js:3:3:3:16 | eval("x = 23") | call |
55
| eval.js:3:3:3:16 | exceptional return of eval("x = 23") | call |
66
| sources.js:1:1:1:12 | exceptional return of new (x => x) | call |
7-
| sources.js:1:6:1:6 | x | call |
87
| sources.js:1:6:1:11 | exceptional return of anonymous function | call |
98
| sources.js:3:1:5:6 | exceptional return of (functi ... \\n})(23) | call |
109
| sources.js:3:2:5:1 | exceptional return of anonymous function | call |
@@ -20,7 +19,6 @@
2019
| tst2.ts:8:3:8:5 | A.x | heap |
2120
| tst2.ts:11:11:11:13 | A.x | heap |
2221
| tst2.ts:13:26:13:29 | List | global |
23-
| tst2.ts:13:39:13:38 | args | call |
2422
| tst2.ts:13:39:13:38 | exceptional return of default constructor of class StringList | call |
2523
| tst2.ts:13:39:13:38 | exceptional return of super(...args) | call |
2624
| tst2.ts:13:39:13:38 | super | call |

0 commit comments

Comments
 (0)