void apply() { if (NodeUtil.isFunctionDeclaration(node)) { traverseFunction(node, scope); } else { for (Node child = node.getFirstChild(); child != null; child = child.getNext()) { traverseNode(child, node, scope); } } }
@Override public void visit(NodeTraversal t, Node n, Node parent) { if (!n.isFunction()) { return; } int id = functionNames.getFunctionId(n); if (id < 0) { // Function node was added during compilation; don't instrument. return; } String name = functionNames.getFunctionName(n); if (!reportFunctionName.isEmpty()) { Node body = n.getLastChild(); Node call = IR.call( IR.name(reportFunctionName), IR.number(id), IR.string(name), IR.name("arguments")); call.putBooleanProp(Node.FREE_CALL, true); Node expr = IR.exprResult(call); expr.useSourceInfoFromForTree(n); body.addChildToFront(expr); compiler.reportCodeChange(); } if (!reportFunctionExitName.isEmpty()) { (new InstrumentReturns(id, name)).process(n); } if (!definedFunctionName.isEmpty()) { Node call = IR.call(IR.name(definedFunctionName), IR.number(id), IR.string(name)); call.putBooleanProp(Node.FREE_CALL, true); call.useSourceInfoFromForTree(n); Node expr = NodeUtil.newExpr(call); Node addingRoot = null; if (NodeUtil.isFunctionDeclaration(n)) { JSModule module = t.getModule(); addingRoot = compiler.getNodeForCodeInsertion(module); addingRoot.addChildToFront(expr); } else { Node beforeChild = n; for (Node ancestor : n.getAncestors()) { Token type = ancestor.getType(); if (type == Token.BLOCK || type == Token.SCRIPT) { addingRoot = ancestor; break; } beforeChild = ancestor; } addingRoot.addChildBefore(expr, beforeChild); } compiler.reportCodeChange(); } }
public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() != Token.FUNCTION) { return; } int id = functionNames.getFunctionId(n); if (id < 0) { // Function node was added during compilation; don't instrument. return; } if (reportFunctionName.length() != 0) { Node body = n.getFirstChild().getNext().getNext(); Node call = new Node( Token.CALL, Node.newString(Token.NAME, reportFunctionName), Node.newNumber(id)); Node expr = new Node(Token.EXPR_RESULT, call); body.addChildToFront(expr); compiler.reportCodeChange(); } if (reportFunctionExitName.length() != 0) { Node body = n.getFirstChild().getNext().getNext(); (new InstrumentReturns(id)).process(body); } if (definedFunctionName.length() != 0) { Node call = new Node( Token.CALL, Node.newString(Token.NAME, definedFunctionName), Node.newNumber(id)); Node expr = NodeUtil.newExpr(call); Node addingRoot = null; if (NodeUtil.isFunctionDeclaration(n)) { JSModule module = t.getModule(); addingRoot = compiler.getNodeForCodeInsertion(module); addingRoot.addChildToFront(expr); } else { Node beforeChild = n; for (Node ancestor : n.getAncestors()) { int type = ancestor.getType(); if (type == Token.BLOCK || type == Token.SCRIPT) { addingRoot = ancestor; break; } beforeChild = ancestor; } addingRoot.addChildBefore(expr, beforeChild); } compiler.reportCodeChange(); } }
/** Checks that the function is used legally. */ private static void checkFunctionUse(NodeTraversal t, Node n) { if (NodeUtil.isFunctionDeclaration(n) && !NodeUtil.isHoistedFunctionDeclaration(n)) { t.report(n, BAD_FUNCTION_DECLARATION); } }
/** * Traverses everything in the current scope and marks variables that are referenced. * * <p>During traversal, we identify subtrees that will only be referenced if their enclosing * variables are referenced. Instead of traversing those subtrees, we create a continuation for * them, and traverse them lazily. */ private void traverseNode(Node n, Node parent, Scope scope) { int type = n.getType(); Var var = null; switch (type) { case Token.FUNCTION: // If this function is a removable var, then create a continuation // for it instead of traversing immediately. if (NodeUtil.isFunctionDeclaration(n)) { var = scope.getVar(n.getFirstChild().getString()); } if (var != null && isRemovableVar(var)) { continuations.put(var, new Continuation(n, scope)); } else { traverseFunction(n, scope); } return; case Token.ASSIGN: Assign maybeAssign = Assign.maybeCreateAssign(n); if (maybeAssign != null) { // Put this in the assign map. It might count as a reference, // but we won't know that until we have an index of all assigns. var = scope.getVar(maybeAssign.nameNode.getString()); if (var != null) { assignsByVar.put(var, maybeAssign); assignsByNode.put(maybeAssign.nameNode, maybeAssign); if (isRemovableVar(var) && !maybeAssign.mayHaveSecondarySideEffects) { // If the var is unreferenced and performing this assign has // no secondary side effects, then we can create a continuation // for it instead of traversing immediately. continuations.put(var, new Continuation(n, scope)); return; } } } break; case Token.CALL: // Look for calls to inheritance-defining calls (such as goog.inherits). SubclassRelationship subclassRelationship = codingConvention.getClassesDefinedByCall(n); if (subclassRelationship != null) { Var subclassVar = scope.getVar(subclassRelationship.subclassName); // Don't try to track the inheritance calls for non-globals. It would // be more correct to only not track when the subclass does not // reference a constructor, but checking that it is a global is // easier and mostly the same. if (subclassVar != null && subclassVar.isGlobal() && !referenced.contains(subclassVar)) { // Save a reference to the EXPR node. inheritsCalls.put(subclassVar, parent); continuations.put(subclassVar, new Continuation(n, scope)); return; } } break; case Token.NAME: var = scope.getVar(n.getString()); if (parent.getType() == Token.VAR) { Node value = n.getFirstChild(); if (value != null && var != null && isRemovableVar(var) && !NodeUtil.mayHaveSideEffects(value)) { // If the var is unreferenced and creating its value has no side // effects, then we can create a continuation for it instead // of traversing immediately. continuations.put(var, new Continuation(n, scope)); return; } } else { // If arguments is escaped, we just assume the worst and continue // on all the parameters. if ("arguments".equals(n.getString()) && scope.isLocal()) { Node lp = scope.getRootNode().getFirstChild().getNext(); for (Node a = lp.getFirstChild(); a != null; a = a.getNext()) { markReferencedVar(scope.getVar(a.getString())); } } // All name references that aren't declarations or assigns // are references to other vars. if (var != null) { // If that var hasn't already been marked referenced, then // start tracking it. If this is an assign, do nothing // for now. if (isRemovableVar(var)) { if (!assignsByNode.containsKey(n)) { markReferencedVar(var); } } else { markReferencedVar(var); } } } break; } for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { traverseNode(c, n, scope); } }