JS: Add call-graph test for accessor calls

This commit is contained in:
Asger Feldthaus
2022-04-07 12:17:54 +02:00
parent 7d5c80433d
commit c9db6201ef
3 changed files with 74 additions and 12 deletions

View File

@@ -1,5 +1,5 @@
spuriousCallee
missingCallee
| constructor-field.ts:40:5:40:14 | f3.build() | constructor-field.ts:13:3:13:12 | build() {} | -1 |
| constructor-field.ts:71:1:71:11 | bf3.build() | constructor-field.ts:13:3:13:12 | build() {} | -1 |
| constructor-field.ts:40:5:40:14 | f3.build() | constructor-field.ts:13:3:13:12 | build() {} | -1 | calls |
| constructor-field.ts:71:1:71:11 | bf3.build() | constructor-field.ts:13:3:13:12 | build() {} | -1 | calls |
badAnnotation

View File

@@ -25,16 +25,28 @@ class AnnotatedFunction extends Function {
}
/** A function annotated with `calls:NAME` */
class AnnotatedCall extends InvokeExpr {
class AnnotatedCall extends DataFlow::SourceNode {
string calls;
string kind;
AnnotatedCall() { calls = getAnnotation(this, "calls") }
AnnotatedCall() {
this instanceof DataFlow::InvokeNode and
calls = getAnnotation(this.asExpr(), kind) and
kind = "calls"
or
this instanceof DataFlow::PropRef and
calls = getAnnotation(this.getAstNode(), kind) and
kind = "callsAccessor"
}
string getCallTargetName() { result = calls }
AnnotatedFunction getAnExpectedCallee() { result.getCalleeName() = getCallTargetName() }
AnnotatedFunction getAnExpectedCallee(string kind_) {
result.getCalleeName() = getCallTargetName() and
kind = kind_
}
int getBoundArgs() { result = getAnnotation(this, "boundArgs").toInt() }
int getBoundArgs() { result = getAnnotation(this.getAstNode(), "boundArgs").toInt() }
int getBoundArgsOrMinusOne() {
result = getBoundArgs()
@@ -42,25 +54,31 @@ class AnnotatedCall extends InvokeExpr {
not exists(getBoundArgs()) and
result = -1
}
string getKind() {
result = kind
}
}
predicate callEdge(AnnotatedCall call, AnnotatedFunction target, int boundArgs) {
FlowSteps::calls(call.flow(), target) and boundArgs = -1
FlowSteps::calls(call, target) and boundArgs = -1
or
FlowSteps::callsBound(call.flow(), target, boundArgs)
FlowSteps::callsBound(call, target, boundArgs)
}
query predicate spuriousCallee(AnnotatedCall call, AnnotatedFunction target, int boundArgs) {
query predicate spuriousCallee(AnnotatedCall call, AnnotatedFunction target, int boundArgs, string kind) {
callEdge(call, target, boundArgs) and
kind = call.getKind() and
not (
target = call.getAnExpectedCallee() and
target = call.getAnExpectedCallee(kind) and
boundArgs = call.getBoundArgsOrMinusOne()
)
}
query predicate missingCallee(AnnotatedCall call, AnnotatedFunction target, int boundArgs) {
query predicate missingCallee(AnnotatedCall call, AnnotatedFunction target, int boundArgs, string kind) {
not callEdge(call, target, boundArgs) and
target = call.getAnExpectedCallee() and
kind = call.getKind() and
target = call.getAnExpectedCallee(kind) and
boundArgs = call.getBoundArgsOrMinusOne()
}

View File

@@ -0,0 +1,44 @@
import 'dummy';
let obj = {
/** name:obj.f.get */
get f() {},
/** name:obj.f.set */
set f(x) {}
};
/** callsAccessor:obj.f.get */
obj.f;
/** callsAccessor:obj.f.set */
obj.f = 1;
class C {
/** name:C.f.get */
static get f() {}
/** name:C.f.set */
static set f(x) {}
}
/** callsAccessor:C.f.get */
C.f;
/** callsAccessor:C.f.set */
C.f = 1;
class D {
/** name:D.f.get */
get f() {}
/** name:D.f.set */
set f(x) {}
}
/** callsAccessor:D.f.get */
new D().f;
/** callsAccessor:D.f.set */
new D().f = 1;