Skip to content

Commit 3b28bdb

Browse files
committed
JS: Rewrite AnalyzedThisInArrayIterationFunction
1 parent f942e69 commit 3b28bdb

File tree

2 files changed

+32
-18
lines changed

2 files changed

+32
-18
lines changed

javascript/ql/src/semmle/javascript/StandardLibrary.qll

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -46,33 +46,26 @@ class DirectEval extends CallExpr {
4646
}
4747

4848
/**
49-
* Flow analysis for `this` expressions inside a function that is called with
50-
* `Array.prototype.map` or a similar Array function that binds `this`.
51-
*
52-
* However, since the function could be invoked in another way, we additionally
53-
* still infer the ordinary abstract value.
49+
* Models `Array.prototype.map` and friends as partial invocations that pass their second
50+
* argument as the receiver to the callback.
5451
*/
55-
private class AnalyzedThisInArrayIterationFunction extends AnalyzedNode, DataFlow::ThisNode {
56-
AnalyzedNode thisSource;
57-
58-
AnalyzedThisInArrayIterationFunction() {
59-
exists(DataFlow::MethodCallNode bindingCall, string name |
52+
private class ArrayIterationCallbackAsPartialInvoke extends DataFlow::PartialInvokeNode::Range, DataFlow::MethodCallNode {
53+
ArrayIterationCallbackAsPartialInvoke() {
54+
getNumArgument() = 2 and
55+
// Filter out library methods named 'forEach' etc
56+
not DataFlow::moduleImport(_).flowsTo(getReceiver()) and
57+
exists(string name | name = getMethodName() |
6058
name = "filter" or
6159
name = "forEach" or
6260
name = "map" or
6361
name = "some" or
6462
name = "every"
65-
|
66-
name = bindingCall.getMethodName() and
67-
2 = bindingCall.getNumArgument() and
68-
getBinder() = bindingCall.getCallback(0) and
69-
thisSource = bindingCall.getArgument(1)
7063
)
7164
}
7265

73-
override AbstractValue getALocalValue() {
74-
result = thisSource.getALocalValue() or
75-
result = AnalyzedNode.super.getALocalValue()
66+
override DataFlow::Node getBoundReceiver(DataFlow::Node callback) {
67+
callback = getArgument(0) and
68+
result = getArgument(1)
7669
}
7770
}
7871

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,3 +274,24 @@ private class TypeInferredMethodWithAnalyzedReturnFlow extends CallWithNonLocalA
274274

275275
override AnalyzedFunction getACallee() { result = fun }
276276
}
277+
278+
/**
279+
* Propagates receivers into locally defined callbacks of partial invocations.
280+
*/
281+
private class AnalyzedThisInPartialInvokeCallback extends AnalyzedNode, DataFlow::ThisNode {
282+
DataFlow::PartialInvokeNode call;
283+
DataFlow::Node receiver;
284+
285+
AnalyzedThisInPartialInvokeCallback() {
286+
exists(DataFlow::Node callbackArg |
287+
receiver = call.getBoundReceiver(callbackArg) and
288+
getBinder().flowsTo(callbackArg)
289+
)
290+
}
291+
292+
override AbstractValue getALocalValue() {
293+
result = receiver.analyze().getALocalValue()
294+
or
295+
result = AnalyzedNode.super.getALocalValue()
296+
}
297+
}

0 commit comments

Comments
 (0)