/** * Attempt to inline an global alias of a global name. This requires that the name is well * defined: assigned unconditionally, assigned exactly once. It is assumed that, the name for * which it is an alias must already meet these same requirements. * * @param alias The alias to inline * @return Whether the alias was inlined. */ private boolean inlineGlobalAliasIfPossible(Name name, Ref alias, GlobalNamespace namespace) { // Ensure that the alias is assigned to global name at that the // declaration. Node aliasParent = alias.node.getParent(); if (aliasParent.isAssign() && NodeUtil.isExecutedExactlyOnce(aliasParent) // We special-case for constructors here, to inline constructor aliases // more aggressively in global scope. // We do this because constructor properties are always collapsed, // so we want to inline the aliases also to avoid breakages. || aliasParent.isName() && name.isConstructor()) { Node lvalue = aliasParent.isName() ? aliasParent : aliasParent.getFirstChild(); if (!lvalue.isQualifiedName()) { return false; } name = namespace.getSlot(lvalue.getQualifiedName()); if (name != null && name.isInlinableGlobalAlias()) { Set<AstChange> newNodes = new LinkedHashSet<>(); List<Ref> refs = new ArrayList<>(name.getRefs()); for (Ref ref : refs) { switch (ref.type) { case SET_FROM_GLOBAL: continue; case DIRECT_GET: case ALIASING_GET: Node newNode = alias.node.cloneTree(); Node node = ref.node; node.getParent().replaceChild(node, newNode); newNodes.add(new AstChange(ref.module, ref.scope, newNode)); name.removeRef(ref); break; default: throw new IllegalStateException(); } } rewriteAliasProps(name, alias.node, 0, newNodes); // 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; }
/** * Extracts all Behaviors from an array recursively. The array must be an array literal whose * value is known at compile-time. Entries in the array can be object literals or array literals * (of other behaviors). Behavior names must be global, fully qualified names. * * @see https://github.com/Polymer/polymer/blob/0.8-preview/PRIMER.md#behaviors * @return A list of all {@code BehaviorDefinitions} in the array. */ private List<BehaviorDefinition> extractBehaviors(Node behaviorArray) { if (behaviorArray == null) { return ImmutableList.of(); } if (!behaviorArray.isArrayLit()) { compiler.report(JSError.make(behaviorArray, POLYMER_INVALID_BEHAVIOR_ARRAY)); return ImmutableList.of(); } ImmutableList.Builder<BehaviorDefinition> behaviors = ImmutableList.builder(); for (Node behaviorName : behaviorArray.children()) { if (behaviorName.isObjectLit()) { this.switchDollarSignPropsToBrackets(behaviorName); this.quoteListenerAndHostAttributeKeys(behaviorName); behaviors.add( new BehaviorDefinition( extractProperties(behaviorName), getBehaviorFunctionsToCopy(behaviorName), getNonPropertyMembersToCopy(behaviorName), !NodeUtil.isInFunction(behaviorName))); continue; } Name behaviorGlobalName = globalNames.getSlot(behaviorName.getQualifiedName()); boolean isGlobalDeclaration = true; if (behaviorGlobalName == null) { compiler.report(JSError.make(behaviorName, POLYMER_UNQUALIFIED_BEHAVIOR)); continue; } Ref behaviorDeclaration = behaviorGlobalName.getDeclaration(); // Use any set as a backup declaration, even if it's local. if (behaviorDeclaration == null) { List<Ref> behaviorRefs = behaviorGlobalName.getRefs(); for (Ref ref : behaviorRefs) { if (ref.isSet()) { isGlobalDeclaration = false; behaviorDeclaration = ref; break; } } } if (behaviorDeclaration == null) { compiler.report(JSError.make(behaviorName, POLYMER_UNQUALIFIED_BEHAVIOR)); continue; } Node behaviorDeclarationNode = behaviorDeclaration.getNode(); JSDocInfo behaviorInfo = NodeUtil.getBestJSDocInfo(behaviorDeclarationNode); if (behaviorInfo == null || !behaviorInfo.isPolymerBehavior()) { compiler.report(JSError.make(behaviorDeclarationNode, POLYMER_UNANNOTATED_BEHAVIOR)); } Node behaviorValue = NodeUtil.getRValueOfLValue(behaviorDeclarationNode); if (behaviorValue == null) { compiler.report(JSError.make(behaviorName, POLYMER_UNQUALIFIED_BEHAVIOR)); } else if (behaviorValue.isArrayLit()) { // Individual behaviors can also be arrays of behaviors. Parse them recursively. behaviors.addAll(extractBehaviors(behaviorValue)); } else if (behaviorValue.isObjectLit()) { this.switchDollarSignPropsToBrackets(behaviorValue); this.quoteListenerAndHostAttributeKeys(behaviorValue); behaviors.add( new BehaviorDefinition( extractProperties(behaviorValue), getBehaviorFunctionsToCopy(behaviorValue), getNonPropertyMembersToCopy(behaviorValue), isGlobalDeclaration)); } else { compiler.report(JSError.make(behaviorName, POLYMER_UNQUALIFIED_BEHAVIOR)); } } return behaviors.build(); }