class PureFunctionIdentifier {
/**
* Propagate side effect information by building a graph based on
* call site information stored in FunctionInformation and the
* DefinitionProvider and then running GraphReachability to
* determine the set of functions that have side effects.
*/
private void propagateSideEffects() {
// Nodes are function declarations; Edges are function call sites.
DiGraph<FunctionInformation, Node> sideEffectGraph =
LinkedDirectedGraph.createWithoutAnnotations();
// create graph nodes
for (FunctionInformation functionInfo : functionSideEffectMap.values()) {
sideEffectGraph.createNode(functionInfo);
}
// add connections to called functions and side effect root.
for (FunctionInformation functionInfo : functionSideEffectMap.values()) {
if (!functionInfo.mayHaveSideEffects()) {
continue;
}
for (Node callSite : functionInfo.getCallsInFunctionBody()) {
Node callee = callSite.getFirstChild();
Collection<Definition> defs =
getCallableDefinitions(definitionProvider, callee);
if (defs == null) {
// Definition set is not complete or eligible. Possible
// causes include:
// * "callee" is not of type NAME or GETPROP.
// * One or more definitions are not functions.
// * One or more definitions are complex.
// (e.i. return value of a call that returns a function).
functionInfo.setTaintsUnknown();
break;
}
for (Definition def : defs) {
Node defValue = def.getRValue();
FunctionInformation dep = functionSideEffectMap.get(defValue);
Preconditions.checkNotNull(dep);
sideEffectGraph.connect(dep, callSite, functionInfo);
}
}
}
// Propagate side effect information to a fixed point.
FixedPointGraphTraversal.newTraversal(new SideEffectPropagationCallback())
.computeFixedPoint(sideEffectGraph);
// Mark remaining functions "pure".
for (FunctionInformation functionInfo : functionSideEffectMap.values()) {
if (functionInfo.mayBePure()) {
functionInfo.setIsPure();
}
}
}
}