/** Traverses a branch. */ private void traverseBranch(Node n, Node parent) { int type = n.getType(); if (type == Token.SCRIPT) { inputId = n.getInputId(); sourceName = getSourceName(n); } curNode = n; if (!callback.shouldTraverse(this, n, parent)) { return; } if (type == Token.FUNCTION) { traverseFunction(n, parent); } else if (useBlockScope && NodeUtil.createsBlockScope(n)) { traverseBlockScope(n); } else { for (Node child = n.getFirstChild(); child != null; ) { // child could be replaced, in which case our child node // would no longer point to the true next Node next = child.getNext(); traverseBranch(child, n); child = next; } } curNode = n; callback.visit(this, n, parent); }
@Override public Node getAstRoot(AbstractCompiler compiler) { Node root = ast.getAstRoot(compiler); // The root maybe null if the AST can not be created. if (root != null) { Preconditions.checkState(root.isScript()); Preconditions.checkNotNull(root.getInputId()); } return root; }
/** Scans and gather variables declarations under a Node */ private void scanVars(Node n) { switch (n.getType()) { case Token.VAR: // Declare all variables. e.g. var x = 1, y, z; for (Node child = n.getFirstChild(); child != null; ) { Node next = child.getNext(); declareVar(child); child = next; } return; case Token.FUNCTION: if (NodeUtil.isFunctionExpression(n)) { return; } String fnName = n.getFirstChild().getString(); if (fnName.isEmpty()) { // This is invalid, but allow it so the checks can catch it. return; } declareVar(n.getFirstChild()); return; // should not examine function's children case Token.CATCH: Preconditions.checkState(n.getChildCount() == 2); Preconditions.checkState(n.getFirstChild().isName()); // the first child is the catch var and the second child // is the code block final Node var = n.getFirstChild(); final Node block = var.getNext(); declareVar(var); scanVars(block); return; // only one child to scan case Token.SCRIPT: inputId = n.getInputId(); Preconditions.checkNotNull(inputId); break; } // Variables can only occur in statement-level nodes, so // we only need to traverse children in a couple special cases. if (NodeUtil.isControlStructure(n) || NodeUtil.isStatementBlock(n)) { for (Node child = n.getFirstChild(); child != null; ) { Node next = child.getNext(); scanVars(child); child = next; } } }
private void addInstaller(Node sourceNode, String function) { // Find the module InputId inputId = sourceNode.getInputId(); CompilerInput input = inputId != null ? compiler.getInput(inputId) : null; JSModule module = input != null ? input.getModule() : null; InjectedInstaller injected = new InjectedInstaller(module, function); if (installers.add(injected)) { changed = true; Node installer = compiler.parseSyntheticCode(function).removeChildren(); installer.useSourceInfoIfMissingFromForTree(sourceNode); Node enclosingScript = NodeUtil.getEnclosingScript(sourceNode); enclosingScript.addChildrenToFront(installer); } }
/** * Updates the internal reference map based on the provided parameters. If {@code scriptRoot} is * not SCRIPT, it basically replaces the internal map with the new one, otherwise it replaces all * the information associated to the given script. * * @param refMapPatch The reference map result of a {@link ReferenceCollectingCallback} pass which * might be collected from the whole AST or just a sub-tree associated to a SCRIPT node. * @param root AST sub-tree root on which reference collection was done. */ void updateGlobalVarReferences(Map<Var, ReferenceCollection> refMapPatch, Node root) { if (refMap == null || !root.isScript()) { resetGlobalVarReferences(refMapPatch); return; } InputId inputId = root.getInputId(); Preconditions.checkNotNull(inputId); // Note there are two assumptions here (i) the order of compiler inputs // has not changed and (ii) all references are in the order they appear // in AST (this is enforced in ReferenceCollectionCallback). removeScriptReferences(inputId); for (Entry<Var, ReferenceCollection> entry : refMapPatch.entrySet()) { Var var = entry.getKey(); if (var.isGlobal()) { replaceReferences(var.getName(), inputId, entry.getValue()); } } }
private void addThisVar(NodeTraversal t) { Scope scope = t.getScope(); if (scope.isDeclared(THIS_VAR, false)) { return; } Node parent = t.getScopeRoot(); if (parent.isFunction()) { // Add the new node at the beginning of the function body. parent = parent.getLastChild(); } if (parent.isSyntheticBlock()) { // Add the new node inside the SCRIPT node instead of the // synthetic block that contains it. parent = parent.getFirstChild(); } Node name = IR.name(THIS_VAR).srcref(parent); Node thisVar = IR.var(name, IR.thisNode().srcref(parent)); thisVar.srcref(parent); parent.addChildToFront(thisVar); scope.declare(THIS_VAR, name, null, compiler.getInput(parent.getInputId())); }
private void addVarDecls(NodeTraversal t, boolean addThis, boolean addArguments) { Scope scope = t.getScope(); if (scope.isDeclared(THIS_VAR, false)) { addThis = false; } if (scope.isDeclared(ARGUMENTS_VAR, false)) { addArguments = false; } Node parent = t.getScopeRoot(); if (parent.isFunction()) { // Add the new node at the beginning of the function body. parent = parent.getLastChild(); } if (parent.isSyntheticBlock() && parent.getFirstChild().isScript()) { // Add the new node inside the SCRIPT node instead of the // synthetic block that contains it. parent = parent.getFirstChild(); } CompilerInput input = compiler.getInput(parent.getInputId()); if (addArguments) { Node name = IR.name(ARGUMENTS_VAR).srcref(parent); Node argumentsVar = IR.var(name, IR.name("arguments").srcref(parent)); argumentsVar.srcref(parent); parent.addChildToFront(argumentsVar); scope.declare(ARGUMENTS_VAR, name, null, input); } if (addThis) { Node name = IR.name(THIS_VAR).srcref(parent); Node thisVar = IR.var(name, IR.thisNode().srcref(parent)); thisVar.srcref(parent); parent.addChildToFront(thisVar); scope.declare(THIS_VAR, name, null, input); } }
private void validateHasInputId(Node n) { InputId inputId = n.getInputId(); if (inputId == null) { violation("Missing 'input id' annotation.", n); } }