/** 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); }
private boolean isShadowed(String name, Scope scope) { Var nameVar = scope.getVar(name); return nameVar != null && nameVar.getScope() != this.startingScope; }
@Override public void visit(NodeTraversal t, Node n, Node parent) { if (!NodeUtil.isReferenceName(n)) { return; } String name = n.getString(); Scope referencedIn = t.getScope(); Var var = referencedIn.getVar(name); if (var == null) { return; } if (!var.isLet() && !var.isConst()) { return; } // Traverse nodes up from let/const declaration: // If we hit a function or the root before a loop - Not a loop closure. // if we hit a loop first - maybe loop closure. Scope declaredIn = var.getScope(); Node loopNode = null; for (Scope s = declaredIn; ; s = s.getParent()) { Node scopeRoot = s.getRootNode(); if (NodeUtil.isLoopStructure(s.getRootNode())) { loopNode = scopeRoot; break; } else if (scopeRoot.getParent() != null && NodeUtil.isLoopStructure(scopeRoot.getParent())) { loopNode = scopeRoot.getParent(); break; } else if (s.isFunctionBlockScope() || s.isGlobal()) { return; } } referenceMap.put(var, n); // Traverse scopes from reference scope to declaration scope. // If we hit a function - loop closure detected. for (Scope s = referencedIn; s != declaredIn; s = s.getParent()) { if (s.isFunctionBlockScope()) { Node function = s.getRootNode().getParent(); if (functionHandledMap.containsEntry(function, name)) { return; } functionHandledMap.put(function, name); if (!loopObjectMap.containsKey(loopNode)) { loopObjectMap.put( loopNode, new LoopObject(LOOP_OBJECT_NAME + "$" + compiler.getUniqueNameIdSupplier().get())); } LoopObject object = loopObjectMap.get(loopNode); object.vars.add(var); functionLoopObjectsMap.put(function, object); return; } } }