/** @param globalScope a new Global Scope to replace the scope of references with. */ public void updateReferencesWithGlobalScope(Scope globalScope) { for (ReferenceCollection collection : refMap.values()) { List<Reference> newRefs = new ArrayList<>(collection.references.size()); for (Reference ref : collection.references) { if (ref.getScope() != globalScope) { newRefs.add(ref.cloneWithNewScope(globalScope)); } else { newRefs.add(ref); } } collection.references = newRefs; } }
/** * Finds the range of references associated to {@code sourceName}. Note that even if there is no * sourceName references the returned information can be used to decide where to insert new * sourceName refs. */ private SourceRefRange findSourceRefRange(List<Reference> refList, InputId inputId) { Preconditions.checkNotNull(inputId); // TODO(bashir): We can do binary search here, but since this is fast enough // right now, we just do a linear search for simplicity. int lastBefore = -1; int firstAfter = refList.size(); int index = 0; Preconditions.checkState(inputOrder.containsKey(inputId), inputId.getIdName()); int sourceInputOrder = inputOrder.get(inputId); for (Reference ref : refList) { Preconditions.checkNotNull(ref.getInputId()); int order = inputOrder.get(ref.getInputId()); if (order < sourceInputOrder) { lastBefore = index; } else if (order > sourceInputOrder) { firstAfter = index; break; } index++; } return new SourceRefRange(refList, lastBefore, firstAfter); }
private boolean inlineAliasIfPossible(Name name, Ref alias, GlobalNamespace namespace) { // Ensure that the alias is assigned to a local variable at that // variable's declaration. If the alias's parent is a NAME, // then the NAME must be the child of a VAR node, and we must // be in a VAR assignment. Node aliasParent = alias.node.getParent(); if (aliasParent.isName()) { // Ensure that the local variable is well defined and never reassigned. Scope scope = alias.scope; String aliasVarName = aliasParent.getString(); Var aliasVar = scope.getVar(aliasVarName); ReferenceCollectingCallback collector = new ReferenceCollectingCallback( compiler, ReferenceCollectingCallback.DO_NOTHING_BEHAVIOR, Predicates.equalTo(aliasVar)); collector.processScope(scope); ReferenceCollection aliasRefs = collector.getReferences(aliasVar); Set<AstChange> newNodes = new LinkedHashSet<>(); if (aliasRefs.isWellDefined() && aliasRefs.firstReferenceIsAssigningDeclaration()) { if (!aliasRefs.isAssignedOnceInLifetime()) { // Static properties of constructors are always collapsed. // So, if a constructor is aliased and its properties are accessed from // the alias, we would like to inline the alias here to access the // properties correctly. // But if the aliased variable is assigned more than once, we can't // inline, so we warn. if (name.isConstructor()) { boolean accessPropsAfterAliasing = false; for (Reference ref : aliasRefs.references) { if (ref.getNode().getParent().isGetProp()) { accessPropsAfterAliasing = true; break; } } if (accessPropsAfterAliasing) { compiler.report(JSError.make(aliasParent, UNSAFE_CTOR_ALIASING, aliasVarName)); } } return false; } // The alias is well-formed, so do the inlining now. int size = aliasRefs.references.size(); for (int i = 1; i < size; i++) { ReferenceCollectingCallback.Reference aliasRef = aliasRefs.references.get(i); Node newNode = alias.node.cloneTree(); aliasRef.getParent().replaceChild(aliasRef.getNode(), newNode); newNodes.add(new AstChange(getRefModule(aliasRef), aliasRef.getScope(), newNode)); } // just set the original alias to null. aliasParent.replaceChild(alias.node, IR.nullNode()); compiler.reportCodeChange(); // Inlining the variable may have introduced new references // to descendants of {@code name}. So those need to be collected now. namespace.scanNewNodes(newNodes); return true; } } return false; }