JavaScript: Teach API graphs to handle promisify.

Following a suggestion by Asger, we track use nodes through calls to `promisify`. When we see a call to a promisified function, we introduce a new synthetic API-graph node representing the callback argument synthesised by the promisification, and track the result of the call to an `await` (or other promise resolution), which is then considered to be a use of the first parameter of the synthetic callback (the zeroth parameter being an error code, which we do not model yet).
This commit is contained in:
Max Schaefer
2020-11-23 10:22:14 +00:00
parent 59abcd6dae
commit ecab17a626
5 changed files with 86 additions and 10 deletions

View File

@@ -206,7 +206,8 @@ module API {
this = Impl::MkClassInstance(result) or
this = Impl::MkUse(result) or
this = Impl::MkDef(result) or
this = Impl::MkAsyncFuncResult(result)
this = Impl::MkAsyncFuncResult(result) or
this = Impl::MkSyntheticCallbackArg(_, result)
}
/**
@@ -389,12 +390,16 @@ module API {
MkCanonicalNameUse(CanonicalName n) {
not n.isRoot() and
isUsed(n)
} or
MkSyntheticCallbackArg(DataFlow::Node src, DataFlow::InvokeNode nd) {
trackUseNode(src, true).flowsTo(nd.getCalleeNode())
}
class TDef = MkModuleDef or TNonModuleDef;
class TNonModuleDef =
MkModuleExport or MkClassInstance or MkAsyncFuncResult or MkDef or MkCanonicalNameDef;
MkModuleExport or MkClassInstance or MkAsyncFuncResult or MkDef or MkCanonicalNameDef or
MkSyntheticCallbackArg;
class TUse = MkModuleUse or MkModuleImport or MkUse or MkCanonicalNameUse;
@@ -523,8 +528,8 @@ module API {
* The receiver is considered to be argument -1.
*/
private predicate argumentPassing(TApiNode base, int i, DataFlow::Node arg) {
exists(DataFlow::SourceNode use, DataFlow::SourceNode pred |
use(base, use) and pred = trackUseNode(use)
exists(DataFlow::Node use, DataFlow::SourceNode pred |
use(base, use) and pred = trackUseNode(use, _)
|
arg = pred.getAnInvocation().getArgument(i)
or
@@ -609,6 +614,12 @@ module API {
lbl = Label::instance() and
ref = getANodeWithType(tn)
)
or
exists(DataFlow::InvokeNode call |
base = MkSyntheticCallbackArg(_, call) and
lbl = Label::parameter(1) and
ref = awaited(call)
)
)
}
@@ -672,21 +683,34 @@ module API {
)
}
private DataFlow::SourceNode trackUseNode(DataFlow::SourceNode nd, DataFlow::TypeTracker t) {
private DataFlow::SourceNode trackUseNode(
DataFlow::SourceNode nd, boolean promisified, DataFlow::TypeTracker t
) {
t.start() and
use(_, nd) and
result = nd
result = nd and
promisified = false
or
exists(DataFlow::TypeTracker t2 | result = trackUseNode(nd, t2).track(t2, t))
exists(DataFlow::CallNode promisify |
promisify = API::moduleImport(["util", "bluebird"]).getMember("promisify").getACall()
|
trackUseNode(nd, false, t.continue()).flowsTo(promisify.getArgument(0)) and
promisified = true and
result = promisify
)
or
exists(DataFlow::TypeTracker t2 | result = trackUseNode(nd, promisified, t2).track(t2, t))
}
private DataFlow::SourceNode trackUseNode(DataFlow::SourceNode nd, boolean promisified) {
result = trackUseNode(nd, promisified, DataFlow::TypeTracker::end())
}
/**
* Gets a node that is inter-procedurally reachable from `nd`, which is a use of some node.
*/
cached
DataFlow::SourceNode trackUseNode(DataFlow::SourceNode nd) {
result = trackUseNode(nd, DataFlow::TypeTracker::end())
}
DataFlow::SourceNode trackUseNode(DataFlow::SourceNode nd) { result = trackUseNode(nd, false) }
private DataFlow::SourceNode trackDefNode(DataFlow::Node nd, DataFlow::TypeBackTracker t) {
t.start() and
@@ -714,6 +738,17 @@ module API {
result = trackDefNode(nd, DataFlow::TypeBackTracker::end())
}
private DataFlow::SourceNode awaited(DataFlow::InvokeNode call, DataFlow::TypeTracker t) {
t.startInPromise() and
exists(MkSyntheticCallbackArg(_, call))
or
exists(DataFlow::TypeTracker t2 | result = awaited(call, t2).track(t2, t))
}
private DataFlow::Node awaited(DataFlow::InvokeNode call) {
result = awaited(call, DataFlow::TypeTracker::end())
}
private DataFlow::SourceNode getANodeWithType(TypeName tn) {
exists(string moduleName, string typeName |
tn.hasQualifiedName(moduleName, typeName) and
@@ -774,6 +809,12 @@ module API {
lbl = Label::return() and
succ = MkAsyncFuncResult(f)
)
or
exists(DataFlow::SourceNode src, DataFlow::InvokeNode call |
use(pred, src) and
lbl = Label::parameter(call.getNumArgument()) and
succ = MkSyntheticCallbackArg(src, call)
)
}
/**