Skip to content

Commit ab5fc9f

Browse files
committed
JS: Implement viableImplInCallContext
1 parent ff7bc7c commit ab5fc9f

File tree

2 files changed

+41
-4
lines changed

2 files changed

+41
-4
lines changed

javascript/ql/lib/semmle/javascript/dataflow/internal/DataFlowPrivate.qll

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1089,17 +1089,54 @@ DataFlowCallable viableCallable(DataFlowCall node) {
10891089
result.asSourceCallableNotExterns() = node.asImpliedLambdaCall()
10901090
}
10911091

1092+
private DataFlowCall getACallOnThis(DataFlow::ClassNode cls) {
1093+
result.asOrdinaryCall() = cls.getAReceiverNode().getAPropertyRead().getACall()
1094+
or
1095+
result.asAccessorCall() = cls.getAReceiverNode().getAPropertyRead()
1096+
or
1097+
result.asPartialCall().getACallbackNode() = cls.getAReceiverNode().getAPropertyRead()
1098+
}
1099+
1100+
private predicate downwardCall(DataFlowCall call) {
1101+
exists(DataFlow::ClassNode cls |
1102+
call = getACallOnThis(cls) and
1103+
viableCallable(call).asSourceCallable() =
1104+
cls.getADirectSubClass+().getAnInstanceMember().getFunction()
1105+
)
1106+
}
1107+
10921108
/**
10931109
* Holds if the set of viable implementations that can be called by `call`
10941110
* might be improved by knowing the call context.
10951111
*/
1096-
predicate mayBenefitFromCallContext(DataFlowCall call) { none() }
1112+
predicate mayBenefitFromCallContext(DataFlowCall call) { downwardCall(call) }
1113+
1114+
/** Gets the type of the receiver of `call`. */
1115+
private DataFlowType getThisArgumentType(DataFlowCall call) {
1116+
exists(DataFlow::Node node |
1117+
isArgumentNodeImpl(node, call, MkThisParameter()) and
1118+
result = getNodeType(node)
1119+
)
1120+
}
1121+
1122+
/** Gets the type of the 'this' parameter of `call`. */
1123+
private DataFlowType getThisParameterType(DataFlowCallable callable) {
1124+
exists(DataFlow::Node node |
1125+
isParameterNodeImpl(node, callable, MkThisParameter()) and
1126+
result = getNodeType(node)
1127+
)
1128+
}
10971129

10981130
/**
10991131
* Gets a viable dispatch target of `call` in the context `ctx`. This is
11001132
* restricted to those `call`s for which a context might make a difference.
11011133
*/
1102-
DataFlowCallable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) { none() }
1134+
DataFlowCallable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) {
1135+
mayBenefitFromCallContext(call) and
1136+
result = viableCallable(call) and
1137+
viableCallable(ctx) = call.getEnclosingCallable() and
1138+
compatibleTypes(getThisArgumentType(ctx), getThisParameterType(result))
1139+
}
11031140

11041141
bindingset[node, fun]
11051142
pragma[inline_late]

javascript/ql/test/library-tests/TripleDot/subclass.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class Subclass1 extends Base {
1111
this.baseMethod(source("sub1"));
1212
}
1313
subclassMethod(x) {
14-
sink(x); // $ hasValueFlow=sub1 SPURIOUS: hasValueFlow=sub2
14+
sink(x); // $ hasValueFlow=sub1
1515
}
1616
}
1717

@@ -20,7 +20,7 @@ class Subclass2 extends Base {
2020
this.baseMethod(source("sub2"));
2121
}
2222
subclassMethod(x) {
23-
sink(x); // $ hasValueFlow=sub2 SPURIOUS: hasValueFlow=sub1
23+
sink(x); // $ hasValueFlow=sub2
2424
}
2525
}
2626

0 commit comments

Comments
 (0)