Skip to content

Commit b8b8e2b

Browse files
authored
Merge pull request github#16054 from asgerf/js/call-graph-improvement2
JS: more implied receiver steps
2 parents 0cfac60 + 22b56a4 commit b8b8e2b

File tree

2 files changed

+42
-0
lines changed

2 files changed

+42
-0
lines changed

javascript/ql/lib/semmle/javascript/dataflow/internal/CallGraphs.qll

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,20 @@ module CallGraph {
279279
StepSummary::step(getAnAllocationSiteRef(node), result, objectWithMethodsStep())
280280
}
281281

282+
/**
283+
* Holds if `function` flows to a property of `host` via non-local data flow.
284+
*/
285+
pragma[nomagic]
286+
private predicate complexMethodInstallation(
287+
DataFlow::SourceNode host, DataFlow::FunctionNode function
288+
) {
289+
not function = getAMethodOnObject(_) and
290+
exists(DataFlow::TypeTracker t |
291+
getAFunctionReference(function, 0, t) = host.getAPropertySource() and
292+
t.start() // require call bit to be false
293+
)
294+
}
295+
282296
/**
283297
* Holds if `pred` is assumed to flow to `succ` because a method is stored on an object that is assumed
284298
* to be the receiver of calls to that method.
@@ -291,9 +305,18 @@ module CallGraph {
291305
*/
292306
cached
293307
predicate impliedReceiverStep(DataFlow::SourceNode pred, DataFlow::SourceNode succ) {
308+
// To avoid double-recursion, we handle either complex flow for the host object, or for the function, but not both.
294309
exists(DataFlow::SourceNode host |
310+
// Complex flow for the host object
295311
pred = getAnAllocationSiteRef(host) and
296312
succ = getAMethodOnObject(host).getReceiver()
313+
or
314+
// Complex flow for the function
315+
exists(DataFlow::FunctionNode function |
316+
complexMethodInstallation(host, function) and
317+
pred = host and
318+
succ = function.getReceiver()
319+
)
297320
)
298321
}
299322
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import 'dummy';
2+
3+
function fooFactoryFactory() {
4+
return function fooFactory() {
5+
return function foo() {
6+
/** calls:F.member */
7+
this.member();
8+
}
9+
}
10+
}
11+
12+
function F() {
13+
this.foo = fooFactoryFactory()();
14+
}
15+
16+
/** name:F.member */
17+
F.prototype.member = function() {
18+
return 42;
19+
};

0 commit comments

Comments
 (0)