JS: improve tests for interprocedural type inference

This commit is contained in:
Esben Sparre Andreasen
2018-09-06 15:49:05 +02:00
parent 28f3b686a7
commit 8f3497a7bf
24 changed files with 278 additions and 12 deletions

View File

@@ -0,0 +1,13 @@
(function(){
let x = source();
function nest1() {
function nest2(){
function nest3(v){
sink(v);
}
return nest3;
}
return nest2;
}
nest1()()(x);
});

View File

@@ -1,6 +1,9 @@
| tst.js:1:1:1:16 | (function(){})() | tst.js:1:2:1:13 | function(){} | file://:0:0:0:0 | undefined |
| tst.js:2:1:2:25 | (functi ... n; })() | tst.js:2:2:2:22 | functio ... turn; } | file://:0:0:0:0 | undefined |
| tst.js:3:1:3:30 | (functi ... e; })() | tst.js:3:2:3:27 | functio ... true; } | file://:0:0:0:0 | true |
| tst.js:4:1:4:51 | (functi ... e; })() | tst.js:4:2:4:48 | functio ... alse; } | file://:0:0:0:0 | false |
| tst.js:4:1:4:51 | (functi ... e; })() | tst.js:4:2:4:48 | functio ... alse; } | file://:0:0:0:0 | true |
| tst.js:5:1:5:27 | (functi ... x; })() | tst.js:5:2:5:24 | functio ... rn x; } | file://:0:0:0:0 | indefinite value (global) |
| tst.js:4:1:4:16 | (function(){})() | file://:0:0:0:0 | undefined |
| tst.js:5:1:5:25 | (functi ... n; })() | file://:0:0:0:0 | undefined |
| tst.js:6:1:6:30 | (functi ... e; })() | file://:0:0:0:0 | true |
| tst.js:7:1:7:57 | (functi ... e; })() | file://:0:0:0:0 | false |
| 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:23:5:23:8 | f4() | tst.js:21:16:21:30 | function f5 |
| tst.js:30:5:30:8 | f6() | tst.js:26:16:28:9 | function f7 |

View File

@@ -1,4 +1,4 @@
import javascript
from CallWithAnalyzedReturnFlow call
select call, call.getACallee(), call.getACallee().getAReturnValue()
select call, call.getAValue()

View File

@@ -0,0 +1,41 @@
| tst.js:1:18:1:38 | require ... odule') | file://:0:0:0:0 | indefinite value (call) |
| tst.js:2:16:2:36 | require ... odule') | file://:0:0:0:0 | indefinite value (call) |
| tst.js:2:16:2:36 | require ... odule') | myModule.js:1:1:4:0 | exports object of module myModule |
| tst.js:2:16:2:36 | require ... odule') | myModule.js:1:18:1:30 | anonymous function |
| tst.js:4:1:4:16 | (function(){})() | file://:0:0:0:0 | undefined |
| tst.js:5:1:5:25 | (functi ... n; })() | file://:0:0:0:0 | undefined |
| tst.js:6:1:6:30 | (functi ... e; })() | file://:0:0:0:0 | true |
| tst.js:7:1:7:57 | (functi ... e; })() | file://:0:0:0:0 | false |
| 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: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: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: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: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: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) |
| tst.js:54:5:54:27 | someMod ... ethod() | file://:0:0:0:0 | indefinite value (call) |
| tst.js:56:5:56:14 | myModule() | file://:0:0:0:0 | indefinite value (call) |
| 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: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) |

View File

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

View File

@@ -0,0 +1,3 @@
module.exports = function() {}
module.exports.myMyMethod1 = function(){}
module.exports.myMyMethod2 = function(){ return function(){} }

View File

@@ -1,5 +1,90 @@
var someModule = require('someModule'),
myModule = require('./myModule');
(function(){})();
(function(){ return; })();
(function(){ return true; })();
(function(){ if (x) return true; return false; })();
(function(){ return x; })();
(function(){ if (unknown) return true; return false; })();
(function(){ return unknown; })();
(function(){
function f1(){}
f1();
function f2(){}
f2();
f2();
var o1 = { f3: function f3(){} };
o1.f3();
function f4(){
return function f5(){}
}
f4()();
function f6(){
return function f7(){
return function f8(){}
}
}
f6()()();
var f9 = unknown();
f9();
var f10 = unknown? function (){}: function (){};
f10();
var f11 = unknown? function (){ return 42; }: function (){ return "foo"; };
f11();
var f12 = unknown? function (){}: unknown();
f12();
var f13 = unknown? function (){}: 42;
f13(); // the call may crash, but the eventual return value is known!
var f14 = unknown? (unknown? function (){ return 42}: function (){ return "foo"; }): 42;
f14(); // the call may crash, but the eventual return value is known!
someFunction();
someObject.someMethod();
someModule();
someModule.someMethod();
myModule();
myModule.myMethod1();
myModule.myMethod2().myMethod3();
var f15 = unknown? function (){}: undefined;
f15(); // the call may crash, but the eventual return value is known!
var f16 = unknown? function (){}: {};
f16(); // the call may crash, but the eventual return value is known!
for (var f of { f: function(){} }) {
var f17 = unknown? f: function(){}
f17();
}
for (var f of unknown) {
var f18 = unknown? f: function(){};
f18();
}
var f19 = undefined;
f19();
var f20 = unknown? {}: undefined;
f20();
(function () {
var getF = function(){}
var f = getF();
(function () {
f();
});
});
});

View File

@@ -0,0 +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: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: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) |

View File

@@ -0,0 +1,5 @@
import javascript
from DataFlow::CallNode c, int i, DataFlow::Node arg
where arg = c.getArgument(i)
select arg, i, arg.analyze().getAValue()

View File

@@ -0,0 +1,13 @@
(function(){
function getKnown() { return true; }
function getUnknown() { return UNKNOWN; }
f(true, UNKNOWN, getKnown(), getUnknown(), getKnown, getUnknown);
function f(known, unknown, gotKnown, gotUnknown, getKnown_indirect, getUnknown_indirect) {
DUMP(getKnown(), getUnknown());
DUMP(getKnown_indirect, getUnknown_indirect);
DUMP(known, unknown, gotKnown, gotUnknown, getKnown_indirect(), getUnknown_indirect())
}
});

View File

@@ -18,6 +18,7 @@
| src/iterated-handlers.js:4:2:4:22 | functio ... res){} |
| src/middleware-attacher-getter.js:4:17:4:36 | function(req, res){} |
| src/middleware-attacher-getter.js:19:19:19:38 | function(req, res){} |
| src/middleware-attacher-getter.js:29:32:29:51 | function(req, res){} |
| src/middleware-attacher.js:3:13:3:32 | function(req, res){} |
| src/nodejs.js:3:19:3:38 | function(req, res){} |
| src/nodejs.js:5:22:5:41 | function(req, res){} |

View File

@@ -14,6 +14,7 @@
| src/handler-in-property.js:5:14:5:33 | function(req, res){} |
| src/handler-in-property.js:12:18:12:37 | function(req, res){} |
| src/middleware-attacher-getter.js:4:17:4:36 | function(req, res){} |
| src/middleware-attacher-getter.js:19:19:19:38 | function(req, res){} |
| src/middleware-attacher.js:3:13:3:32 | function(req, res){} |
| src/nodejs.js:3:19:3:38 | function(req, res){} |
| src/nodejs.js:8:14:8:33 | function(req, res){} |

View File

@@ -18,6 +18,7 @@
| src/iterated-handlers.js:4:2:4:22 | functio ... res){} |
| src/middleware-attacher-getter.js:4:17:4:36 | function(req, res){} |
| src/middleware-attacher-getter.js:19:19:19:38 | function(req, res){} |
| src/middleware-attacher-getter.js:29:32:29:51 | function(req, res){} |
| src/middleware-attacher.js:3:13:3:32 | function(req, res){} |
| src/nodejs.js:3:19:3:38 | function(req, res){} |
| src/nodejs.js:5:22:5:41 | function(req, res){} |

View File

@@ -18,6 +18,7 @@
| src/iterated-handlers.js:5:5:5:14 | app.use(h) |
| src/middleware-attacher-getter.js:4:9:4:37 | app.use ... res){}) |
| src/middleware-attacher-getter.js:14:9:14:18 | app.use(h) |
| src/middleware-attacher-getter.js:24:9:24:18 | app.use(h) |
| src/middleware-attacher.js:3:5:3:33 | app.use ... res){}) |
| src/nodejs.js:3:1:3:39 | http.cr ... res){}) |
| src/nodejs.js:8:1:8:34 | createS ... res){}) |

View File

@@ -18,6 +18,7 @@
| src/iterated-handlers.js:5:5:5:14 | app.use(h) |
| src/middleware-attacher-getter.js:4:9:4:37 | app.use ... res){}) |
| src/middleware-attacher-getter.js:14:9:14:18 | app.use(h) |
| src/middleware-attacher-getter.js:24:9:24:18 | app.use(h) |
| src/middleware-attacher.js:3:5:3:33 | app.use ... res){}) |
| src/nodejs.js:3:1:3:39 | http.cr ... res){}) |
| src/nodejs.js:5:1:5:42 | unknown ... res){}) |

View File

@@ -1,7 +1,7 @@
| src/bound-handler.js:4:1:4:28 | functio ... res){} | A `RouteHandlerCandidate` that did not get promoted to `RouteHandler`, and it is not used in a `RouteSetupCandidate`. |
| src/bound-handler.js:9:12:9:31 | function(req, res){} | A `RouteHandlerCandidate` that did not get promoted to `RouteHandler`, and it is not used in a `RouteSetupCandidate`. |
| src/iterated-handlers.js:4:2:4:22 | functio ... res){} | A `RouteHandlerCandidate` that did not get promoted to `RouteHandler`, and it is not used in a `RouteSetupCandidate`. |
| src/middleware-attacher-getter.js:19:19:19:38 | function(req, res){} | A `RouteHandlerCandidate` that did not get promoted to `RouteHandler`, and it is not used in a `RouteSetupCandidate`. |
| src/middleware-attacher-getter.js:29:32:29:51 | function(req, res){} | A `RouteHandlerCandidate` that did not get promoted to `RouteHandler`, and it is not used in a `RouteSetupCandidate`. |
| src/route-objects.js:7:19:7:38 | function(req, res){} | A `RouteHandlerCandidate` that did not get promoted to `RouteHandler`, and it is not used in a `RouteSetupCandidate`. |
| src/route-objects.js:8:12:10:5 | (req, res) {\\n\\n } | A `RouteHandlerCandidate` that did not get promoted to `RouteHandler`, and it is not used in a `RouteSetupCandidate`. |
| src/route-objects.js:20:16:22:9 | (req, r ... } | A `RouteHandlerCandidate` that did not get promoted to `RouteHandler`, and it is not used in a `RouteSetupCandidate`. |

View File

@@ -7,7 +7,7 @@ function getAttacher1 (app) {
var app = express();
getAttacher1(app);
confuse(getAttacher2); // disable the type inference
confuse(getAttacher2); // attempt to disable the type inference
function getAttacher2 (app) {
return function(h) {
@@ -17,4 +17,13 @@ function getAttacher2 (app) {
var app = express();
getAttacher2(app)(function(req, res){});
confuse(getAttacher2); // disable the type inference
confuse(getAttacher2); // attempt to disable the type inference
function getAttacher3 (app) {
return function(h) {
app.use(h);
};
}
var app = express();
(unknown || getAttacher3)(app)(function(req, res){}); // confuse the type inference

View File

@@ -1,3 +1,4 @@
| 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 |
| 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

@@ -0,0 +1,32 @@
(function(){
function getUnknown() { return UNKNOWN; }
function getKnown() { return "foo"; }
function f(known, unknown, gotKnown, gotUnknown, getKnown_indirect, getUnknown_indirect) {
// disable the whitelist
known = known; unknown = unknown; gotKnown = gotKnown; gotUnknown = gotUnknown;
known === 42;
known == 42;
gotKnown === 42;
gotKnown == 42;
getKnown() === 42;
getKnown() == 42;
getKnown_indirect() === 42;
getKnown_indirect() == 42;
unknown === 42;
unknown == 42;
gotUnknown === 42;
gotUnknown == 42;
getUnknown() === 42;
getUnknown() == 42;
getUnknown_indirect() === 42;
getUnknown_indirect() == 42;
}
f("foo", UNKNOWN, getKnown(), getUnknown(), getKnown, getUnknown);
});

View File

@@ -9,3 +9,4 @@
| tst.js:73:5:73:5 | x | This expression will be implicitly converted from undefined to number. |
| 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. |

View File

@@ -94,4 +94,17 @@ function l() {
}
});
(function(){
function f() {
}
f()|0;
unknown()|0;
function g() {
}
g()|0;
g();
});
// semmle-extractor-options: --experimental

View File

@@ -0,0 +1,17 @@
(function () {
var getF = function(){}
var f = getF();
(function () {
f();
});
});
(function () {
var getF = unknown
if (false) {
var f = getF();
(function () {
f();
});
}
});

View File

@@ -1,3 +1,4 @@
| SuspiciousPropAccess.js:4:10:4:21 | result.value | The base expression of this property access is always undefined. |
| tst.js:32:32:32:38 | a(1)[0] | The base expression of this property access is always null. |
| tst.ts:19:3:19:5 | x.p | The base expression of this property access is always undefined. |
| typeassertion.ts:14:3:14:9 | z.field | The base expression of this property access is always null. |

View File

@@ -27,3 +27,7 @@
v1.p;
}
});
(function(){
function a(){return null;} a(1)[0];
});