Merge pull request #18783 from asgerf/js/downward-calls

JS: Resolve calls downward in class hierarchy
This commit is contained in:
Asger F
2025-02-20 09:01:58 +01:00
committed by GitHub
9 changed files with 212 additions and 20 deletions

View File

@@ -12,4 +12,4 @@ accessorCall
| accessors.js:44:1:44:9 | new D().f | accessors.js:37:8:37:13 | (x) {} |
| accessors.js:48:1:48:5 | obj.f | accessors.js:5:8:5:12 | () {} |
| accessors.js:51:1:51:3 | C.f | accessors.js:19:15:19:19 | () {} |
| accessors.js:54:1:54:9 | new D().f | accessors.js:34:8:34:12 | () {} |
| accessors.js:55:1:55:3 | d.f | accessors.js:34:8:34:12 | () {} |

View File

@@ -58,20 +58,23 @@ class AnnotatedCall extends DataFlow::Node {
string getKind() { result = kind }
}
predicate callEdge(AnnotatedCall call, AnnotatedFunction target, int boundArgs) {
predicate callEdge(AnnotatedCall call, Function target, int boundArgs) {
FlowSteps::calls(call, target) and boundArgs = -1
or
FlowSteps::callsBound(call, target, boundArgs)
}
query predicate spuriousCallee(
AnnotatedCall call, AnnotatedFunction target, int boundArgs, string kind
) {
query predicate spuriousCallee(AnnotatedCall call, Function target, int boundArgs, string kind) {
callEdge(call, target, boundArgs) and
kind = call.getKind() and
not (
target = call.getAnExpectedCallee(kind) and
boundArgs = call.getBoundArgsOrMinusOne()
) and
(
target instanceof AnnotatedFunction
or
call.getCallTargetName() = "NONE"
)
}

View File

@@ -50,5 +50,6 @@ obj.f();
/** calls:NONE */
C.f();
const d = new D();
/** calls:NONE */
new D().f();
d.f();

View File

@@ -0,0 +1,60 @@
import 'dummy';
class Base {
workInBase() {
/** calls:methodInBase */
this.methodInBase();
/** calls:methodInSub1 calls:methodInSub2 */
this.methodInSub();
/** calls:overriddenInSub0 calls:overriddenInSub1 calls:overriddenInSub2 */
this.overriddenInSub();
}
/** name:methodInBase */
methodInBase() {
/** calls:methodInSub1 calls:methodInSub2 */
this.methodInSub();
}
/** name:overriddenInSub0 */
overriddenInSub() {
}
}
class Subclass1 extends Base {
workInSub() {
/** calls:methodInBase */
this.methodInBase();
/** calls:overriddenInSub1 */
this.overriddenInSub();
}
/** name:methodInSub1 */
methodInSub() {
}
/** name:overriddenInSub1 */
overriddenInSub() {
}
}
class Subclass2 extends Base {
workInSub() {
/** calls:methodInBase */
this.methodInBase();
/** calls:overriddenInSub2 */
this.overriddenInSub();
}
/** name:methodInSub2 */
methodInSub() {
}
/** name:overriddenInSub2 */
overriddenInSub() {
}
}

View File

@@ -56,6 +56,7 @@ test_getAFunctionValue
| classes.js:3:10:5:5 | () {\\n ... ;\\n } | classes.js:3:10:5:5 | () {\\n ... ;\\n } |
| classes.js:7:6:9:5 | () {\\n ... ;\\n } | classes.js:7:6:9:5 | () {\\n ... ;\\n } |
| classes.js:8:7:8:16 | this.hello | classes.js:3:10:5:5 | () {\\n ... ;\\n } |
| classes.js:8:7:8:16 | this.hello | classes.js:13:10:15:5 | () {\\n ... ;\\n } |
| classes.js:12:3:16:3 | B | classes.js:12:21:12:20 | (...arg ... rgs); } |
| classes.js:12:3:16:3 | class B ... }\\n } | classes.js:12:21:12:20 | (...arg ... rgs); } |
| classes.js:12:19:12:19 | A | classes.js:2:11:2:10 | () {} |
@@ -447,6 +448,7 @@ test_getACallee
| a.js:3:1:3:5 | bar() | b.js:2:8:2:24 | function bar() {} |
| a.js:4:1:4:5 | qux() | c.js:2:8:2:24 | function bar() {} |
| classes.js:8:7:8:18 | this.hello() | classes.js:3:10:5:5 | () {\\n ... ;\\n } |
| classes.js:8:7:8:18 | this.hello() | classes.js:13:10:15:5 | () {\\n ... ;\\n } |
| classes.js:12:21:12:20 | super(...args) | classes.js:2:11:2:10 | () {} |
| classes.js:18:3:18:9 | new B() | classes.js:12:21:12:20 | (...arg ... rgs); } |
| classes.js:18:3:18:17 | new B().hello() | classes.js:13:10:15:5 | () {\\n ... ;\\n } |

View File

@@ -0,0 +1,34 @@
import 'dummy';
class Base {
baseMethod(x) {
this.subclassMethod(x);
}
}
class Subclass1 extends Base {
work() {
this.baseMethod(source("sub1"));
}
subclassMethod(x) {
sink(x); // $ hasValueFlow=sub1
}
}
class Subclass2 extends Base {
work() {
this.baseMethod(source("sub2"));
}
subclassMethod(x) {
sink(x); // $ hasValueFlow=sub2
}
}
class Subclass3 extends Base {
work() {
this.baseMethod("safe");
}
subclassMethod(x) {
sink(x);
}
}