JS: Implement viableImplInCallContext

This commit is contained in:
Asger F
2025-02-14 13:25:19 +01:00
parent ff7bc7c25e
commit ab5fc9f4d7
2 changed files with 41 additions and 4 deletions

View File

@@ -1089,17 +1089,54 @@ DataFlowCallable viableCallable(DataFlowCall node) {
result.asSourceCallableNotExterns() = node.asImpliedLambdaCall()
}
private DataFlowCall getACallOnThis(DataFlow::ClassNode cls) {
result.asOrdinaryCall() = cls.getAReceiverNode().getAPropertyRead().getACall()
or
result.asAccessorCall() = cls.getAReceiverNode().getAPropertyRead()
or
result.asPartialCall().getACallbackNode() = cls.getAReceiverNode().getAPropertyRead()
}
private predicate downwardCall(DataFlowCall call) {
exists(DataFlow::ClassNode cls |
call = getACallOnThis(cls) and
viableCallable(call).asSourceCallable() =
cls.getADirectSubClass+().getAnInstanceMember().getFunction()
)
}
/**
* Holds if the set of viable implementations that can be called by `call`
* might be improved by knowing the call context.
*/
predicate mayBenefitFromCallContext(DataFlowCall call) { none() }
predicate mayBenefitFromCallContext(DataFlowCall call) { downwardCall(call) }
/** Gets the type of the receiver of `call`. */
private DataFlowType getThisArgumentType(DataFlowCall call) {
exists(DataFlow::Node node |
isArgumentNodeImpl(node, call, MkThisParameter()) and
result = getNodeType(node)
)
}
/** Gets the type of the 'this' parameter of `call`. */
private DataFlowType getThisParameterType(DataFlowCallable callable) {
exists(DataFlow::Node node |
isParameterNodeImpl(node, callable, MkThisParameter()) and
result = getNodeType(node)
)
}
/**
* Gets a viable dispatch target of `call` in the context `ctx`. This is
* restricted to those `call`s for which a context might make a difference.
*/
DataFlowCallable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) { none() }
DataFlowCallable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) {
mayBenefitFromCallContext(call) and
result = viableCallable(call) and
viableCallable(ctx) = call.getEnclosingCallable() and
compatibleTypes(getThisArgumentType(ctx), getThisParameterType(result))
}
bindingset[node, fun]
pragma[inline_late]

View File

@@ -11,7 +11,7 @@ class Subclass1 extends Base {
this.baseMethod(source("sub1"));
}
subclassMethod(x) {
sink(x); // $ hasValueFlow=sub1 SPURIOUS: hasValueFlow=sub2
sink(x); // $ hasValueFlow=sub1
}
}
@@ -20,7 +20,7 @@ class Subclass2 extends Base {
this.baseMethod(source("sub2"));
}
subclassMethod(x) {
sink(x); // $ hasValueFlow=sub2 SPURIOUS: hasValueFlow=sub1
sink(x); // $ hasValueFlow=sub2
}
}