Provides the actual implementation of API graphs, cached for performance.
Ideally, we’d like nodes to correspond to (global) access paths, with edge labels corresponding to extending the access path by one element. We also want to be able to map nodes to their definitions and uses in the data-flow graph, and this should happen modulo (inter-procedural) data flow.
This, however, is not easy to implement, since access paths can have unbounded length and we need some way of recognizing cycles to avoid non-termination. Unfortunately, expressing a condition like “this node hasn’t been involved in constructing any predecessor of this node in the API graph” without negative recursion is tricky.
So instead most nodes are directly associated with a data-flow node, representing either a use or a definition of an API component. This ensures that we only have a finite number of nodes. However, we can now have multiple nodes with the same access path, which are essentially indistinguishable for a client of the API.
On the other hand, a single node can have multiple access paths (which is, of course, unavoidable). We pick as canonical the alphabetically least access path with shortest length.
Import path
import javascript
Predicates
distanceFromRoot | Gets the shortest distance from the root to |
edge | Holds if there is an edge from |
getAPromisifiedInvocation | Gets a call to a promisified function represented by |
rhs | Holds if |
rhs | Holds if |
trackDefNode | Gets a node that inter-procedurally flows into |
trackUseNode | Gets a node that is inter-procedurally reachable from |
use | Holds if |
use | Holds if |