Add backward dataflow edges through fluent function invocations.

This means that much as obj.getA().setB(...) already has a side-effect on `obj`, all three setters in obj.setA(...).setB(...).setC(...) will have a side-effect on `obj`.
This commit is contained in:
Chris Smowton
2021-02-19 13:59:03 +00:00
parent 37baf77b93
commit fadbb32bd6
5 changed files with 102 additions and 1 deletions

View File

@@ -223,7 +223,10 @@ private predicate isAdditionalFlowStep(
* Holds if data can flow in one local step from `node1` to `node2`.
*/
private predicate localFlowStep(Node node1, Node node2, Configuration config) {
simpleLocalFlowStep(node1, node2) and
(
simpleLocalFlowStep(node1, node2) or
reverseStepThroughInputOutputAlias(node1, node2)
) and
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and

View File

@@ -415,6 +415,22 @@ private module Cached {
store(node1, tc.getContent(), node2, contentType, tc.getContainerType())
}
/**
* Holds if data can flow from `fromNode` to `toNode` because they are the post-update
* nodes of some function output and input respectively, where the output and input
* are aliases. A typical example is a function returning `this`, implementing a fluent
* interface.
*/
cached
predicate reverseStepThroughInputOutputAlias(Node fromNode, Node toNode) {
exists(Node fromPre, Node toPre |
fromPre = fromNode.(PostUpdateNode).getPreUpdateNode() and
toPre = toNode.(PostUpdateNode).getPreUpdateNode()
|
argumentValueFlowsThrough(toPre, TReadStepTypesNone(), fromPre)
)
}
/**
* Holds if the call context `call` either improves virtual dispatch in
* `callable` or if it allows us to prune unreachable nodes in `callable`.