class PureFunctionIdentifier.FunctionAnalyzer {
@Override
public void visit(NodeTraversal traversal, Node node, Node parent) {
if (inExterns) {
return;
}
if (!NodeUtil.nodeTypeMayHaveSideEffects(node)
&& node.getType() != Token.RETURN) {
return;
}
if (NodeUtil.isCall(node) || NodeUtil.isNew(node)) {
allFunctionCalls.add(node);
}
Node enclosingFunction = traversal.getEnclosingFunction();
if (enclosingFunction != null) {
FunctionInformation sideEffectInfo =
functionSideEffectMap.get(enclosingFunction);
Preconditions.checkNotNull(sideEffectInfo);
if (NodeUtil.isAssignmentOp(node)) {
visitAssignmentOrUnaryOperator(
sideEffectInfo, traversal.getScope(),
node, node.getFirstChild(), node.getLastChild());
} else {
switch(node.getType()) {
case Token.CALL:
case Token.NEW:
visitCall(sideEffectInfo, node);
break;
case Token.DELPROP:
case Token.DEC:
case Token.INC:
visitAssignmentOrUnaryOperator(
sideEffectInfo, traversal.getScope(),
node, node.getFirstChild(), null);
break;
case Token.NAME:
// Variable definition are not side effects.
// Just check that the name appears in the context of a
// variable declaration.
Preconditions.checkArgument(
NodeUtil.isVarDeclaration(node));
Node value = node.getFirstChild();
// Assignment to local, if the value isn't a safe local value,
// new object creation or literal or known primitive result
// value, add it to the local blacklist.
if (value != null && !NodeUtil.evaluatesToLocalValue(value)) {
Scope scope = traversal.getScope();
Var var = scope.getVar(node.getString());
sideEffectInfo.blacklistLocal(var);
}
break;
case Token.THROW:
visitThrow(sideEffectInfo);
break;
case Token.RETURN:
if (node.hasChildren()
&& !NodeUtil.evaluatesToLocalValue(node.getFirstChild())) {
sideEffectInfo.setTaintsReturn();
}
break;
default:
throw new IllegalArgumentException(
"Unhandled side effect node type " +
Token.name(node.getType()));
}
}
}
}
}