/** For each node, update the block stack and reference collection as appropriate. */ @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isName() || n.isRest() || (n.isStringKey() && parent.isObjectPattern() && !n.hasChildren())) { Var v; if (n.getString().equals("arguments")) { v = t.getScope().getArgumentsVar(); } else { v = t.getScope().getVar(n.getString()); } if (v != null) { if (varFilter.apply(v)) { addReference(v, new Reference(n, t, peek(blockStack))); } if (v.getParentNode() != null && NodeUtil.isHoistedFunctionDeclaration(v.getParentNode()) && // If we're only traversing a narrow scope, do not try to climb outside. (narrowScope == null || narrowScope.getDepth() <= v.getScope().getDepth())) { outOfBandTraversal(v); } } } if (isBlockBoundary(n, parent)) { pop(blockStack); } }
private void outOfBandTraversal(Var v) { if (startedFunctionTraverse.contains(v)) { return; } startedFunctionTraverse.add(v); Node fnNode = v.getParentNode(); // Replace the block stack with a new one. This algorithm only works // because we know hoisted functions cannot be inside loops. It will have to // change if we ever do general function continuations. Preconditions.checkState(NodeUtil.isHoistedFunctionDeclaration(fnNode)); Scope containingScope = v.getScope(); // This is tricky to compute because of the weird traverseAtScope call for // CollapseProperties. List<BasicBlock> newBlockStack = null; if (containingScope.isGlobal()) { newBlockStack = new ArrayList<>(); newBlockStack.add(blockStack.get(0)); } else { for (int i = 0; i < blockStack.size(); i++) { if (blockStack.get(i).root == containingScope.getRootNode()) { newBlockStack = new ArrayList<>(blockStack.subList(0, i + 1)); } } } Preconditions.checkNotNull(newBlockStack); List<BasicBlock> oldBlockStack = blockStack; blockStack = newBlockStack; NodeTraversal outOfBandTraversal = new NodeTraversal(compiler, this); outOfBandTraversal.traverseFunctionOutOfBand(fnNode, containingScope); blockStack = oldBlockStack; finishedFunctionTraverse.add(v); }