JS: introduce TypeInferredCalleeWithAnalyzedReturnFlow

This commit is contained in:
Esben Sparre Andreasen
2018-09-06 15:53:43 +02:00
parent fef3573152
commit a07c094437
13 changed files with 161 additions and 19 deletions

View File

@@ -756,7 +756,7 @@ module DataFlow {
/**
* A data flow node representing an explicit (that is, non-reflective) function call.
*/
private class ExplicitCallNode extends CallNodeDef, ExplicitInvokeNode {
class ExplicitCallNode extends CallNodeDef, ExplicitInvokeNode {
override CallExpr astNode;
}

View File

@@ -143,6 +143,23 @@ abstract class CallWithAnalyzedReturnFlow extends DataFlow::AnalyzedValueNode {
}
}
/**
* A call with inter-procedural type inference for the return value.
*
* Unlike `CallWithAnalyzedReturnFlow`, this only contributes to `getAValue()`, not `getALocalValue()`.
*/
abstract class CallWithNonLocalAnalyzedReturnFlow extends DataFlow::AnalyzedValueNode {
/**
* Gets a called function.
*/
abstract AnalyzedFunction getACallee();
override AbstractValue getAValue() {
result = getACallee().getAReturnValue()
}
}
/**
* Flow analysis for the return value of IIFEs.
*/
@@ -210,4 +227,30 @@ private class LocalFunctionCallWithAnalyzedReturnFlow extends CallWithAnalyzedRe
result = f.analyze()
}
}
}
pragma[noinline]
private predicate hasExplicitDefiniteCallee(DataFlow::Impl::ExplicitCallNode call, DataFlow::AnalyzedNode callee) {
callee = call.getCalleeNode() and
not callee.getALocalValue().isIndefinite(_)
}
/**
* Enables inter-procedural type inference for the return value of a call to a type-inferred callee.
*/
private class TypeInferredCalleeWithAnalyzedReturnFlow extends CallWithNonLocalAnalyzedReturnFlow {
DataFlow::FunctionNode fun;
TypeInferredCalleeWithAnalyzedReturnFlow() {
exists (DataFlow::AnalyzedNode calleeNode |
hasExplicitDefiniteCallee(this, calleeNode) and
calleeNode.getALocalValue().(AbstractFunction).getFunction().flow() = fun
)
}
override AnalyzedFunction getACallee() {
result = fun
}
}

View File

@@ -1,3 +1,4 @@
| advanced-callgraph.js:2:13:2:20 | source() | advanced-callgraph.js:6:22:6:22 | v |
| partialCalls.js:4:17:4:24 | source() | partialCalls.js:17:14:17:14 | x |
| partialCalls.js:4:17:4:24 | source() | partialCalls.js:20:14:20:14 | y |
| partialCalls.js:4:17:4:24 | source() | partialCalls.js:30:14:30:20 | x.value |

View File

@@ -5,5 +5,19 @@
| tst.js:7:1:7:57 | (functi ... e; })() | file://:0:0:0:0 | true |
| tst.js:8:1:8:33 | (functi ... n; })() | file://:0:0:0:0 | indefinite value (global) |
| tst.js:11:5:11:8 | f1() | file://:0:0:0:0 | undefined |
| tst.js:14:5:14:8 | f2() | file://:0:0:0:0 | undefined |
| tst.js:15:5:15:8 | f2() | file://:0:0:0:0 | undefined |
| tst.js:23:5:23:8 | f4() | tst.js:21:16:21:30 | function f5 |
| tst.js:23:5:23:10 | f4()() | file://:0:0:0:0 | undefined |
| tst.js:30:5:30:8 | f6() | tst.js:26:16:28:9 | function f7 |
| tst.js:30:5:30:10 | f6()() | tst.js:27:20:27:34 | function f8 |
| tst.js:36:5:36:9 | f10() | file://:0:0:0:0 | undefined |
| tst.js:39:5:39:9 | f11() | file://:0:0:0:0 | non-empty, non-numeric string |
| tst.js:39:5:39:9 | f11() | file://:0:0:0:0 | non-zero value |
| tst.js:45:5:45:9 | f13() | file://:0:0:0:0 | undefined |
| tst.js:48:5:48:9 | f14() | file://:0:0:0:0 | non-empty, non-numeric string |
| tst.js:48:5:48:9 | f14() | file://:0:0:0:0 | non-zero value |
| tst.js:61:5:61:9 | f15() | file://:0:0:0:0 | undefined |
| tst.js:64:5:64:9 | f16() | file://:0:0:0:0 | undefined |
| tst.js:84:17:84:22 | getF() | file://:0:0:0:0 | undefined |
| tst.js:89:17:89:22 | getG() | file://:0:0:0:0 | undefined |

View File

@@ -1,4 +1,6 @@
import javascript
from CallWithAnalyzedReturnFlow call
from DataFlow::AnalyzedValueNode call
where call instanceof CallWithAnalyzedReturnFlow or
call instanceof CallWithNonLocalAnalyzedReturnFlow
select call, call.getAValue()

View File

@@ -0,0 +1,64 @@
| tst.js:1:18:1:24 | require | file://:0:0:0:0 | indefinite value (heap) |
| tst.js:2:16:2:22 | require | file://:0:0:0:0 | indefinite value (heap) |
| tst.js:4:1:4:14 | (function(){}) | tst.js:4:2:4:13 | anonymous function |
| tst.js:5:1:5:23 | (functi ... urn; }) | tst.js:5:2:5:22 | anonymous function |
| tst.js:6:1:6:28 | (functi ... rue; }) | tst.js:6:2:6:27 | anonymous function |
| tst.js:7:1:7:55 | (functi ... lse; }) | tst.js:7:2:7:54 | anonymous function |
| tst.js:8:1:8:31 | (functi ... own; }) | tst.js:8:2:8:30 | anonymous function |
| tst.js:11:5:11:6 | f1 | tst.js:10:5:10:19 | function f1 |
| tst.js:14:5:14:6 | f2 | tst.js:13:5:13:19 | function f2 |
| tst.js:15:5:15:6 | f2 | tst.js:13:5:13:19 | function f2 |
| tst.js:18:5:18:9 | o1.f3 | file://:0:0:0:0 | indefinite value (heap) |
| tst.js:18:5:18:9 | o1.f3 | tst.js:17:20:17:34 | function f3 |
| tst.js:23:5:23:6 | f4 | tst.js:20:5:22:5 | function f4 |
| tst.js:23:5:23:8 | f4() | tst.js:21:16:21:30 | function f5 |
| tst.js:30:5:30:6 | f6 | tst.js:25:5:29:5 | function f6 |
| tst.js:30:5:30:8 | f6() | tst.js:26:16:28:9 | function f7 |
| tst.js:30:5:30:10 | f6()() | tst.js:27:20:27:34 | function f8 |
| tst.js:32:14:32:20 | unknown | file://:0:0:0:0 | indefinite value (global) |
| tst.js:33:5:33:6 | f9 | file://:0:0:0:0 | indefinite value (call) |
| tst.js:36:5:36:7 | f10 | tst.js:35:24:35:36 | anonymous function |
| tst.js:36:5:36:7 | f10 | tst.js:35:39:35:51 | anonymous function |
| tst.js:39:5:39:7 | f11 | tst.js:38:24:38:48 | anonymous function |
| tst.js:39:5:39:7 | f11 | tst.js:38:51:38:78 | anonymous function |
| tst.js:41:39:41:45 | unknown | file://:0:0:0:0 | indefinite value (global) |
| tst.js:42:5:42:7 | f12 | file://:0:0:0:0 | indefinite value (call) |
| tst.js:42:5:42:7 | f12 | tst.js:41:24:41:36 | anonymous function |
| tst.js:45:5:45:7 | f13 | file://:0:0:0:0 | non-zero value |
| tst.js:45:5:45:7 | f13 | tst.js:44:24:44:36 | anonymous function |
| tst.js:48:5:48:7 | f14 | file://:0:0:0:0 | non-zero value |
| tst.js:48:5:48:7 | f14 | tst.js:47:34:47:56 | anonymous function |
| tst.js:48:5:48:7 | f14 | tst.js:47:59:47:86 | anonymous function |
| tst.js:50:5:50:16 | someFunction | file://:0:0:0:0 | indefinite value (global) |
| tst.js:51:5:51:25 | someObj ... eMethod | file://:0:0:0:0 | indefinite value (global) |
| tst.js:51:5:51:25 | someObj ... eMethod | file://:0:0:0:0 | indefinite value (heap) |
| tst.js:53:5:53:14 | someModule | file://:0:0:0:0 | indefinite value (call) |
| tst.js:53:5:53:14 | someModule | file://:0:0:0:0 | undefined |
| tst.js:54:5:54:25 | someMod ... eMethod | file://:0:0:0:0 | indefinite value (call) |
| tst.js:54:5:54:25 | someMod ... eMethod | file://:0:0:0:0 | indefinite value (heap) |
| tst.js:56:5:56:12 | myModule | file://:0:0:0:0 | indefinite value (call) |
| tst.js:56:5:56:12 | myModule | file://:0:0:0:0 | undefined |
| tst.js:56:5:56:12 | myModule | myModule.js:1:1:4:0 | exports object of module myModule |
| tst.js:56:5:56:12 | myModule | myModule.js:1:18:1:30 | anonymous function |
| tst.js:57:5:57:22 | myModule.myMethod1 | file://:0:0:0:0 | indefinite value (call) |
| tst.js:57:5:57:22 | myModule.myMethod1 | file://:0:0:0:0 | indefinite value (heap) |
| tst.js:58:5:58:22 | myModule.myMethod2 | file://:0:0:0:0 | indefinite value (call) |
| tst.js:58:5:58:22 | myModule.myMethod2 | file://:0:0:0:0 | indefinite value (heap) |
| tst.js:58:5:58:34 | myModul ... Method3 | file://:0:0:0:0 | indefinite value (call) |
| tst.js:58:5:58:34 | myModul ... Method3 | file://:0:0:0:0 | indefinite value (heap) |
| tst.js:61:5:61:7 | f15 | file://:0:0:0:0 | undefined |
| tst.js:61:5:61:7 | f15 | tst.js:60:24:60:36 | anonymous function |
| tst.js:64:5:64:7 | f16 | tst.js:63:24:63:36 | anonymous function |
| tst.js:64:5:64:7 | f16 | tst.js:63:39:63:40 | object literal |
| tst.js:68:9:68:11 | f17 | file://:0:0:0:0 | indefinite value (heap) |
| tst.js:68:9:68:11 | f17 | tst.js:67:31:67:42 | anonymous function |
| tst.js:73:9:73:11 | f18 | file://:0:0:0:0 | indefinite value (heap) |
| tst.js:73:9:73:11 | f18 | tst.js:72:31:72:42 | anonymous function |
| tst.js:77:5:77:7 | f19 | file://:0:0:0:0 | undefined |
| tst.js:80:5:80:7 | f20 | file://:0:0:0:0 | undefined |
| tst.js:80:5:80:7 | f20 | tst.js:79:24:79:25 | object literal |
| tst.js:84:17:84:20 | getF | tst.js:83:20:83:31 | function getF |
| tst.js:86:13:86:13 | f | file://:0:0:0:0 | indefinite value (call) |
| tst.js:86:13:86:13 | f | file://:0:0:0:0 | undefined |
| tst.js:89:17:89:20 | getG | tst.js:88:9:88:25 | function getG |
| tst.js:91:13:91:13 | g | file://:0:0:0:0 | undefined |

View File

@@ -0,0 +1,4 @@
import javascript
from DataFlow::InvokeNode call
select call.getCalleeNode(), call.getCalleeNode().analyze().getAValue()

View File

@@ -9,22 +9,24 @@
| tst.js:7:1:7:57 | (functi ... e; })() | file://:0:0:0:0 | true |
| tst.js:8:1:8:33 | (functi ... n; })() | file://:0:0:0:0 | indefinite value (global) |
| tst.js:11:5:11:8 | f1() | file://:0:0:0:0 | undefined |
| tst.js:14:5:14:8 | f2() | file://:0:0:0:0 | indefinite value (call) |
| tst.js:15:5:15:8 | f2() | file://:0:0:0:0 | indefinite value (call) |
| tst.js:14:5:14:8 | f2() | file://:0:0:0:0 | undefined |
| tst.js:15:5:15:8 | f2() | file://:0:0:0:0 | undefined |
| tst.js:18:5:18:11 | o1.f3() | file://:0:0:0:0 | indefinite value (call) |
| tst.js:23:5:23:8 | f4() | tst.js:21:16:21:30 | function f5 |
| tst.js:23:5:23:10 | f4()() | file://:0:0:0:0 | indefinite value (call) |
| tst.js:23:5:23:10 | f4()() | file://:0:0:0:0 | undefined |
| tst.js:30:5:30:8 | f6() | tst.js:26:16:28:9 | function f7 |
| tst.js:30:5:30:10 | f6()() | file://:0:0:0:0 | indefinite value (call) |
| tst.js:30:5:30:10 | f6()() | tst.js:27:20:27:34 | function f8 |
| tst.js:30:5:30:12 | f6()()() | file://:0:0:0:0 | indefinite value (call) |
| tst.js:32:14:32:22 | unknown() | file://:0:0:0:0 | indefinite value (call) |
| tst.js:33:5:33:8 | f9() | file://:0:0:0:0 | indefinite value (call) |
| tst.js:36:5:36:9 | f10() | file://:0:0:0:0 | indefinite value (call) |
| tst.js:39:5:39:9 | f11() | file://:0:0:0:0 | indefinite value (call) |
| tst.js:36:5:36:9 | f10() | file://:0:0:0:0 | undefined |
| tst.js:39:5:39:9 | f11() | file://:0:0:0:0 | non-empty, non-numeric string |
| tst.js:39:5:39:9 | f11() | file://:0:0:0:0 | non-zero value |
| tst.js:41:39:41:47 | unknown() | file://:0:0:0:0 | indefinite value (call) |
| tst.js:42:5:42:9 | f12() | file://:0:0:0:0 | indefinite value (call) |
| tst.js:45:5:45:9 | f13() | file://:0:0:0:0 | indefinite value (call) |
| tst.js:48:5:48:9 | f14() | file://:0:0:0:0 | indefinite value (call) |
| tst.js:45:5:45:9 | f13() | file://:0:0:0:0 | undefined |
| tst.js:48:5:48:9 | f14() | file://:0:0:0:0 | non-empty, non-numeric string |
| tst.js:48:5:48:9 | f14() | file://:0:0:0:0 | non-zero value |
| tst.js:50:5:50:18 | someFunction() | file://:0:0:0:0 | indefinite value (call) |
| tst.js:51:5:51:27 | someObj ... ethod() | file://:0:0:0:0 | indefinite value (call) |
| tst.js:53:5:53:16 | someModule() | file://:0:0:0:0 | indefinite value (call) |
@@ -33,9 +35,13 @@
| tst.js:57:5:57:24 | myModule.myMethod1() | file://:0:0:0:0 | indefinite value (call) |
| tst.js:58:5:58:24 | myModule.myMethod2() | file://:0:0:0:0 | indefinite value (call) |
| tst.js:58:5:58:36 | myModul ... thod3() | file://:0:0:0:0 | indefinite value (call) |
| tst.js:61:5:61:9 | f15() | file://:0:0:0:0 | indefinite value (call) |
| tst.js:64:5:64:9 | f16() | file://:0:0:0:0 | indefinite value (call) |
| tst.js:61:5:61:9 | f15() | file://:0:0:0:0 | undefined |
| tst.js:64:5:64:9 | f16() | file://:0:0:0:0 | undefined |
| tst.js:68:9:68:13 | f17() | file://:0:0:0:0 | indefinite value (call) |
| tst.js:73:9:73:13 | f18() | file://:0:0:0:0 | indefinite value (call) |
| tst.js:77:5:77:9 | f19() | file://:0:0:0:0 | indefinite value (call) |
| tst.js:80:5:80:9 | f20() | file://:0:0:0:0 | indefinite value (call) |
| tst.js:84:17:84:22 | getF() | file://:0:0:0:0 | undefined |
| tst.js:86:13:86:15 | f() | file://:0:0:0:0 | indefinite value (call) |
| tst.js:89:17:89:22 | getG() | file://:0:0:0:0 | undefined |
| tst.js:91:13:91:15 | g() | file://:0:0:0:0 | indefinite value (call) |

View File

@@ -85,6 +85,11 @@ var someModule = require('someModule'),
(function () {
f();
});
function getG(){}
var g = getG();
(function () {
g();
});
});
});

View File

@@ -1,16 +1,16 @@
| tst.js:6:7:6:10 | true | 0 | file://:0:0:0:0 | true |
| tst.js:6:13:6:19 | UNKNOWN | 1 | file://:0:0:0:0 | indefinite value (global) |
| tst.js:6:22:6:31 | getKnown() | 2 | file://:0:0:0:0 | indefinite value (call) |
| tst.js:6:34:6:45 | getUnknown() | 3 | file://:0:0:0:0 | indefinite value (call) |
| tst.js:6:22:6:31 | getKnown() | 2 | file://:0:0:0:0 | true |
| tst.js:6:34:6:45 | getUnknown() | 3 | file://:0:0:0:0 | indefinite value (global) |
| tst.js:6:48:6:55 | getKnown | 4 | tst.js:2:5:2:40 | function getKnown |
| tst.js:6:58:6:67 | getUnknown | 5 | tst.js:4:5:4:45 | function getUnknown |
| tst.js:9:14:9:23 | getKnown() | 0 | file://:0:0:0:0 | indefinite value (call) |
| tst.js:9:26:9:37 | getUnknown() | 1 | file://:0:0:0:0 | indefinite value (call) |
| tst.js:9:14:9:23 | getKnown() | 0 | file://:0:0:0:0 | true |
| tst.js:9:26:9:37 | getUnknown() | 1 | file://:0:0:0:0 | indefinite value (global) |
| tst.js:10:14:10:30 | getKnown_indirect | 0 | tst.js:2:5:2:40 | function getKnown |
| tst.js:10:33:10:51 | getUnknown_indirect | 1 | tst.js:4:5:4:45 | function getUnknown |
| tst.js:11:14:11:18 | known | 0 | file://:0:0:0:0 | true |
| tst.js:11:21:11:27 | unknown | 1 | file://:0:0:0:0 | indefinite value (global) |
| tst.js:11:30:11:37 | gotKnown | 2 | file://:0:0:0:0 | indefinite value (call) |
| tst.js:11:40:11:49 | gotUnknown | 3 | file://:0:0:0:0 | indefinite value (call) |
| tst.js:11:52:11:70 | getKnown_indirect() | 4 | file://:0:0:0:0 | indefinite value (call) |
| tst.js:11:73:11:93 | getUnkn ... irect() | 5 | file://:0:0:0:0 | indefinite value (call) |
| tst.js:11:52:11:70 | getKnown_indirect() | 4 | file://:0:0:0:0 | true |
| tst.js:11:73:11:93 | getUnkn ... irect() | 5 | file://:0:0:0:0 | indefinite value (global) |

View File

@@ -1,4 +1,6 @@
| interprocedural.js:11:9:11:13 | known | Variable 'known' is of type string, but it is compared to $@ of type number. | interprocedural.js:11:19:11:20 | 42 | an expression |
| interprocedural.js:15:9:15:18 | getKnown() | This expression is of type string, but it is compared to $@ of type number. | interprocedural.js:15:24:15:25 | 42 | an expression |
| interprocedural.js:17:9:17:27 | getKnown_indirect() | This expression is of type string, but it is compared to $@ of type number. | interprocedural.js:17:33:17:34 | 42 | an expression |
| tst.js:2:5:2:17 | typeof window | This expression is of type string, but it is compared to $@ of type undefined. | tst.js:2:23:2:31 | undefined | 'undefined' |
| tst.js:10:28:10:34 | "Hello" | This expression is of type string, but it is compared to $@ of type number. | tst.js:10:39:10:39 | 0 | an expression |
| tst.js:20:1:20:4 | null | This expression is of type null, but it is compared to $@ of type number. | tst.js:20:9:20:9 | 0 | an expression |

View File

@@ -10,3 +10,4 @@
| tst.js:79:19:79:22 | name | This expression will be implicitly converted from undefined to string. |
| tst.js:85:3:85:3 | x | This expression will be implicitly converted from undefined to number. |
| tst.js:100:5:100:7 | f() | This expression will be implicitly converted from undefined to number. |
| tst.js:106:5:106:7 | g() | This expression will be implicitly converted from undefined to number. |