Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 4e45ad2

Browse files
author
Esben Sparre Andreasen
committed
JS: generalize inter procedural IIFE type inference
1 parent 44ae7b6 commit 4e45ad2

2 files changed

Lines changed: 172 additions & 88 deletions

File tree

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

Lines changed: 72 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -7,69 +7,6 @@
77
import javascript
88
import AbstractValuesImpl
99

10-
/**
11-
* Flow analysis for immediately-invoked function expressions (IIFEs).
12-
*/
13-
private class IifeReturnFlow extends DataFlow::AnalyzedValueNode {
14-
ImmediatelyInvokedFunctionExpr iife;
15-
16-
IifeReturnFlow() {
17-
astNode = (CallExpr)iife.getInvocation()
18-
}
19-
20-
override AbstractValue getALocalValue() {
21-
result = getAReturnValue(iife)
22-
}
23-
}
24-
25-
/**
26-
* Gets a return value for the immediately-invoked function expression `f`.
27-
*/
28-
private AbstractValue getAReturnValue(ImmediatelyInvokedFunctionExpr f) {
29-
// explicit return value
30-
result = f.getAReturnedExpr().analyze().getALocalValue()
31-
or
32-
// implicit return value
33-
(
34-
// either because execution of the function may terminate normally
35-
mayReturnImplicitly(f)
36-
or
37-
// or because there is a bare `return;` statement
38-
exists (ReturnStmt ret | ret = f.getAReturnStmt() | not exists(ret.getExpr()))
39-
) and
40-
result = getDefaultReturnValue(f)
41-
}
42-
43-
44-
/**
45-
* Holds if the execution of function `f` may complete normally without
46-
* encountering a `return` or `throw` statement.
47-
*
48-
* Note that this is an overapproximation, that is, the predicate may hold
49-
* of functions that cannot actually complete normally, since it does not
50-
* account for `finally` blocks and does not check reachability.
51-
*/
52-
private predicate mayReturnImplicitly(Function f) {
53-
exists (ConcreteControlFlowNode final |
54-
final.getContainer() = f and
55-
final.isAFinalNode() and
56-
not final instanceof ReturnStmt and
57-
not final instanceof ThrowStmt
58-
)
59-
}
60-
61-
/**
62-
* Gets the default return value for immediately-invoked function expression `f`,
63-
* that is, the value that `f` returns if its execution terminates without
64-
* encountering an explicit `return` statement.
65-
*/
66-
private AbstractValue getDefaultReturnValue(ImmediatelyInvokedFunctionExpr f) {
67-
if f.isGenerator() or f.isAsync() then
68-
result = TAbstractOtherObject()
69-
else
70-
result = TAbstractUndefined()
71-
}
72-
7310
/**
7411
* Flow analysis for `this` expressions inside functions.
7512
*/
@@ -190,3 +127,75 @@ private class AnalyzedThisInPropertyFunction extends AnalyzedThisExpr {
190127
}
191128
}
192129

130+
/**
131+
* Holds if the execution of function `f` may complete normally without
132+
* encountering a `return` or `throw` statement.
133+
*
134+
* Note that this is an overapproximation, that is, the predicate may hold
135+
* of functions that cannot actually complete normally, since it does not
136+
* account for `finally` blocks and does not check reachability.
137+
*/
138+
predicate mayReturnImplicitly(Function f) {
139+
exists (ConcreteControlFlowNode final |
140+
final.getContainer() = f and
141+
final.isAFinalNode() and
142+
not final instanceof ReturnStmt and
143+
not final instanceof ThrowStmt
144+
)
145+
}
146+
147+
/**
148+
* A call with inter-procedural type inference for the return value.
149+
*/
150+
abstract class CallWithAnalyzedReturnFlow extends DataFlow::CallNode, DataFlow::AnalyzedValueNode {
151+
152+
/**
153+
* Gets a called function.
154+
*/
155+
abstract Function getAFunction();
156+
157+
/**
158+
* Gets a return value for this call.
159+
*/
160+
AbstractValue getAReturnValue() {
161+
exists (Function f | f = getAFunction() |
162+
if f.isGenerator() or f.isAsync() then
163+
result = TAbstractOtherObject()
164+
else (
165+
// explicit return value
166+
result = f.getAReturnedExpr().analyze().getALocalValue()
167+
or
168+
// implicit return value
169+
(
170+
// either because execution of the function may terminate normally
171+
mayReturnImplicitly(f)
172+
or
173+
// or because there is a bare `return;` statement
174+
exists (ReturnStmt ret | ret = f.getAReturnStmt() | not exists(ret.getExpr()))
175+
) and
176+
result = TAbstractUndefined()
177+
)
178+
)
179+
}
180+
181+
override AbstractValue getALocalValue() {
182+
result = getAReturnValue()
183+
}
184+
}
185+
186+
/**
187+
* Flow analysis for the return value of IIFEs.
188+
*/
189+
private class IIFEWithAnalyzedReturnFlow extends CallWithAnalyzedReturnFlow {
190+
191+
ImmediatelyInvokedFunctionExpr iife;
192+
193+
IIFEWithAnalyzedReturnFlow() {
194+
this.asExpr() = iife.getInvocation()
195+
}
196+
197+
override Function getAFunction() {
198+
result = iife
199+
}
200+
201+
}

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

Lines changed: 100 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -112,47 +112,43 @@ class AnalyzedVarDef extends VarDef {
112112
}
113113

114114
/**
115-
* Flow analysis for simple IIFE parameters.
116-
*/
117-
private class AnalyzedIIFEParameter extends AnalyzedVarDef, @vardecl {
118-
AnalyzedIIFEParameter() {
119-
exists (ImmediatelyInvokedFunctionExpr iife, int parmIdx |
120-
this = iife.getParameter(parmIdx) |
121-
// we cannot track flow into rest parameters...
122-
not this.(Parameter).isRestParameter() and
123-
// ...nor flow out of spread arguments
124-
exists (int argIdx | argIdx = parmIdx + iife.getArgumentOffset() |
125-
not iife.isSpreadArgument([0..argIdx])
126-
)
115+
* Flow analysis for simple parameters of selected functions.
116+
*/
117+
private class AnalyzedParameter extends AnalyzedVarDef, @vardecl {
118+
AnalyzedParameter() {
119+
exists (FunctionWithAnalyzedParameters f, int parmIdx |
120+
this = f.getParameter(parmIdx) |
121+
// we cannot track flow into rest parameters
122+
not this.(Parameter).isRestParameter()
127123
)
128124
}
129125

130-
/** Gets the IIFE this is a parameter of. */
131-
ImmediatelyInvokedFunctionExpr getIIFE() {
126+
/** Gets the function this is a parameter of. */
127+
FunctionWithAnalyzedParameters getFunction() {
132128
this = result.getAParameter()
133129
}
134130

135131
override DataFlow::AnalyzedNode getRhs() {
136-
getIIFE().argumentPassing(this, result.asExpr()) or
132+
getFunction().argumentPassing(this, result.asExpr()) or
137133
result = this.(Parameter).getDefault().analyze()
138134
}
139135

140136
override AbstractValue getAnRhsValue() {
141-
result = AnalyzedVarDef.super.getAnRhsValue() or
142-
not getIIFE().argumentPassing(this, _) and result = TAbstractUndefined()
137+
result = AnalyzedVarDef.super.getAnRhsValue()
138+
or
139+
not getFunction().mayReceiveArgument(this) and
140+
result = TAbstractUndefined()
143141
}
144142

145143
override predicate isIncomplete(DataFlow::Incompleteness cause) {
146-
exists (ImmediatelyInvokedFunctionExpr iife | iife = getIIFE() |
147-
// if the IIFE has a name and that name is referenced, we conservatively
148-
// assume that there may be other calls than the direct one
149-
exists (iife.getVariable().getAnAccess()) and cause = "call" or
150-
// if the IIFE is non-strict and its `arguments` object is accessed, we
151-
// also assume that there may be other calls (through `arguments.callee`)
152-
not iife.isStrict() and
153-
exists (iife.getArgumentsVariable().getAnAccess()) and cause = "call"
144+
getFunction().isIncomplete(cause) or
145+
(
146+
not getFunction().argumentPassing(this, _) and
147+
getFunction().mayReceiveArgument(this) and
148+
cause = "call"
154149
)
155150
}
151+
156152
}
157153

158154
/**
@@ -650,3 +646,82 @@ private class NamespaceExportVarFlow extends DataFlow::AnalyzedValueNode {
650646

651647
override AbstractValue getALocalValue() { result = TIndefiniteAbstractValue("namespace") }
652648
}
649+
650+
/**
651+
* A function with inter-procedural type inference for its parameters.
652+
*/
653+
abstract class FunctionWithAnalyzedParameters extends Function {
654+
655+
/**
656+
* Holds if `p` is a parameter of this function and `arg` is
657+
* the corresponding argument.
658+
*/
659+
abstract predicate argumentPassing(SimpleParameter p, Expr arg);
660+
661+
/**
662+
* Holds if `p` is a parameter of this function that may receive a value from an argument.
663+
*/
664+
abstract predicate mayReceiveArgument(Parameter p);
665+
666+
/**
667+
* Holds if flow analysis results for the parameters may be incomplete
668+
* due to the given `cause`.
669+
*/
670+
abstract predicate isIncomplete(DataFlow::Incompleteness cause);
671+
672+
}
673+
674+
private abstract class CallWithAnalyzedParameters extends FunctionWithAnalyzedParameters {
675+
676+
/**
677+
* Gets an invocation of this function.
678+
*/
679+
abstract DataFlow::InvokeNode getAnInvocation();
680+
681+
override predicate argumentPassing(SimpleParameter p, Expr arg) {
682+
exists (DataFlow::InvokeNode invk, int argIdx |
683+
invk = getAnInvocation() |
684+
p = getParameter(argIdx) and not p.isRestParameter() and
685+
arg = invk.getArgument(argIdx).asExpr()
686+
)
687+
}
688+
689+
override predicate mayReceiveArgument(Parameter p) {
690+
exists (DataFlow::InvokeNode invk, int argIdx |
691+
invk = getAnInvocation() and
692+
p = getParameter(argIdx) |
693+
exists (invk.getArgument(argIdx))
694+
or
695+
invk.asExpr().(InvokeExpr).isSpreadArgument([0..argIdx])
696+
)
697+
}
698+
699+
}
700+
701+
/**
702+
* Flow analysis for simple parameters of IIFEs.
703+
*/
704+
private class IIFEWithAnalyzedParameters extends CallWithAnalyzedParameters {
705+
706+
ImmediatelyInvokedFunctionExpr iife;
707+
708+
IIFEWithAnalyzedParameters() {
709+
this = iife and
710+
iife.getInvocationKind() = "direct"
711+
}
712+
713+
override DataFlow::InvokeNode getAnInvocation() {
714+
result = iife.getInvocation().flow()
715+
}
716+
717+
override predicate isIncomplete(DataFlow::Incompleteness cause) {
718+
// if the IIFE has a name and that name is referenced, we conservatively
719+
// assume that there may be other calls than the direct one
720+
exists (iife.getVariable().getAnAccess()) and cause = "call" or
721+
// if the IIFE is non-strict and its `arguments` object is accessed, we
722+
// also assume that there may be other calls (through `arguments.callee`)
723+
not iife.isStrict() and
724+
exists (iife.getArgumentsVariable().getAnAccess()) and cause = "call"
725+
}
726+
727+
}

0 commit comments

Comments
 (0)