@Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.isObjectLit()) { normalizeObjectLiteralAnnotations(n); } return true; }
/** * Expand a jQuery.expandedEach call * * <p>Expanded jQuery.expandedEach calls will replace the GETELEM nodes of a property assignment * with GETPROP nodes to allow for renaming. */ private void maybeExpandJqueryEachCall(NodeTraversal t, Node n) { Node objectToLoopOver = n.getChildAtIndex(1); if (objectToLoopOver == null) { return; } Node callbackFunction = objectToLoopOver.getNext(); if (callbackFunction == null || !callbackFunction.isFunction()) { return; } // Run the peephole optimizations on the first argument to handle // cases like ("a " + "b").split(" ") peepholePasses.process(null, n.getChildAtIndex(1)); // Create a reference tree Node nClone = n.cloneTree(); objectToLoopOver = nClone.getChildAtIndex(1); // Check to see if the first argument is something we recognize and can // expand. if (!objectToLoopOver.isObjectLit() && !(objectToLoopOver.isArrayLit() && isArrayLitValidForExpansion(objectToLoopOver))) { t.report(n, JQUERY_UNABLE_TO_EXPAND_INVALID_LIT_ERROR, (String) null); return; } // Find all references to the callback function arguments List<Node> keyNodeReferences = new ArrayList<>(); List<Node> valueNodeReferences = new ArrayList<>(); new NodeTraversal( compiler, new FindCallbackArgumentReferences( callbackFunction, keyNodeReferences, valueNodeReferences, objectToLoopOver.isArrayLit())) .traverseInnerNode( NodeUtil.getFunctionBody(callbackFunction), callbackFunction, t.getScope()); if (keyNodeReferences.isEmpty()) { // We didn't do anything useful ... t.report(n, JQUERY_USELESS_EACH_EXPANSION, (String) null); return; } Node fncBlock = tryExpandJqueryEachCall( t, nClone, callbackFunction, keyNodeReferences, valueNodeReferences); if (fncBlock != null && fncBlock.hasChildren()) { replaceOriginalJqueryEachCall(n, fncBlock); } else { // We didn't do anything useful ... t.report(n, JQUERY_USELESS_EACH_EXPANSION, (String) null); } }
/** Try to fold array-length. e.g [1, 2, 3].length ==> 3, [x, y].length ==> 2 */ private Node tryFoldGetProp(Node n, Node left, Node right) { Preconditions.checkArgument(n.isGetProp()); if (left.isObjectLit()) { return tryFoldObjectPropAccess(n, left, right); } if (right.isString() && right.getString().equals("length")) { int knownLength = -1; switch (left.getType()) { case ARRAYLIT: if (mayHaveSideEffects(left)) { // Nope, can't fold this, without handling the side-effects. return n; } knownLength = left.getChildCount(); break; case STRING: knownLength = left.getString().length(); break; default: // Not a foldable case, forget it. return n; } Preconditions.checkState(knownLength != -1); Node lengthNode = IR.number(knownLength); n.getParent().replaceChild(n, lengthNode); reportCodeChange(); return lengthNode; } return n; }
private void normalizeObjectLiteralAnnotations(Node objlit) { Preconditions.checkState(objlit.isObjectLit()); for (Node key = objlit.getFirstChild(); key != null; key = key.getNext()) { Node value = key.getFirstChild(); normalizeObjectLiteralKeyAnnotations(objlit, key, value); } }
/** Add an @this annotation to all functions in the objLit. */ private void addTypesToFunctions(Node objLit, String thisType) { Preconditions.checkState(objLit.isObjectLit()); for (Node keyNode : objLit.children()) { Node value = keyNode.getLastChild(); if (value != null && value.isFunction()) { JSDocInfoBuilder fnDoc = JSDocInfoBuilder.maybeCopyFrom(keyNode.getJSDocInfo()); fnDoc.recordThisType( new JSTypeExpression(new Node(Token.BANG, IR.string(thisType)), VIRTUAL_FILE)); keyNode.setJSDocInfo(fnDoc.build()); } } // Add @this and @return to default property values. for (MemberDefinition property : extractProperties(objLit)) { if (!property.value.isObjectLit()) { continue; } if (hasShorthandAssignment(property.value)) { compiler.report(JSError.make(property.value, POLYMER_SHORTHAND_NOT_SUPPORTED)); return; } Node defaultValue = NodeUtil.getFirstPropMatchingKey(property.value, "value"); if (defaultValue == null || !defaultValue.isFunction()) { continue; } Node defaultValueKey = defaultValue.getParent(); JSDocInfoBuilder fnDoc = JSDocInfoBuilder.maybeCopyFrom(defaultValueKey.getJSDocInfo()); fnDoc.recordThisType( new JSTypeExpression(new Node(Token.BANG, IR.string(thisType)), VIRTUAL_FILE)); fnDoc.recordReturnType(getTypeFromProperty(property)); defaultValueKey.setJSDocInfo(fnDoc.build()); } }
@Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.OBJECTLIT: for (Node child : n.children()) { if (child.isComputedProp()) { visitObjectWithComputedProperty(n, parent); break; } } break; case Token.MEMBER_DEF: if (parent.isObjectLit()) { visitMemberDefInObjectLit(n, parent); } break; case Token.FOR_OF: visitForOf(n, parent); break; case Token.SUPER: visitSuper(n, parent); break; case Token.STRING_KEY: visitStringKey(n); break; case Token.CLASS: for (Node member = n.getLastChild().getFirstChild(); member != null; member = member.getNext()) { if (member.isGetterDef() || member.isSetterDef() || member.getBooleanProp(Node.COMPUTED_PROP_GETTER) || member.getBooleanProp(Node.COMPUTED_PROP_SETTER)) { cannotConvert(member, "getters or setters in class definitions"); return; } } visitClass(n, parent); break; case Token.ARRAYLIT: case Token.NEW: case Token.CALL: for (Node child : n.children()) { if (child.isSpread()) { visitArrayLitOrCallWithSpread(n, parent); break; } } break; case Token.TEMPLATELIT: Es6TemplateLiterals.visitTemplateLiteral(t, n); break; case Token.ARRAY_PATTERN: visitArrayPattern(t, n, parent); break; case Token.OBJECT_PATTERN: visitObjectPattern(t, n, parent); break; } }
private boolean hasShorthandAssignment(Node objLit) { Preconditions.checkState(objLit.isObjectLit()); for (Node property : objLit.children()) { if (property.isStringKey() && !property.hasChildren()) { return true; } } return false; }
private void visitObjectWithComputedProperty(Node obj, Node parent) { Preconditions.checkArgument(obj.isObjectLit()); List<Node> props = new ArrayList<>(); Node currElement = obj.getFirstChild(); while (currElement != null) { if (currElement.getBooleanProp(Node.COMPUTED_PROP_GETTER) || currElement.getBooleanProp(Node.COMPUTED_PROP_SETTER)) { cannotConvertYet(currElement, "computed getter/setter"); return; } else if (currElement.isGetterDef() || currElement.isSetterDef()) { currElement = currElement.getNext(); } else { Node nextNode = currElement.getNext(); obj.removeChild(currElement); props.add(currElement); currElement = nextNode; } } String objName = FRESH_COMP_PROP_VAR + compiler.getUniqueNameIdSupplier().get(); props = Lists.reverse(props); Node result = IR.name(objName); for (Node propdef : props) { if (propdef.isComputedProp()) { Node propertyExpression = propdef.removeFirstChild(); Node value = propdef.removeFirstChild(); result = IR.comma(IR.assign(IR.getelem(IR.name(objName), propertyExpression), value), result); } else { if (!propdef.hasChildren()) { Node name = IR.name(propdef.getString()).useSourceInfoIfMissingFrom(propdef); propdef.addChildToBack(name); } Node val = propdef.removeFirstChild(); propdef.setType(Token.STRING); int type = propdef.isQuotedString() ? Token.GETELEM : Token.GETPROP; Node access = new Node(type, IR.name(objName), propdef); result = IR.comma(IR.assign(access, val), result); } } Node statement = obj; while (!NodeUtil.isStatement(statement)) { statement = statement.getParent(); } result.useSourceInfoIfMissingFromForTree(obj); parent.replaceChild(obj, result); Node var = IR.var(IR.name(objName), obj); var.useSourceInfoIfMissingFromForTree(statement); statement.getParent().addChildBefore(var, statement); compiler.reportCodeChange(); }
/** * Check that Node n is a call to one of the jQuery.extend methods that we can expand. Valid calls * are single argument calls where the first argument is an object literal or two argument calls * where the first argument is a name and the second argument is an object literal. */ public static boolean isJqueryExtendCall(Node n, String qname, AbstractCompiler compiler) { if (JQUERY_EXTEND_NAMES.contains(qname)) { Node firstArgument = n.getNext(); if (firstArgument == null) { return false; } Node secondArgument = firstArgument.getNext(); if ((firstArgument.isObjectLit() && secondArgument == null) || (firstArgument.isName() || NodeUtil.isGet(firstArgument) && !NodeUtil.mayHaveSideEffects(firstArgument, compiler) && secondArgument != null && secondArgument.isObjectLit() && secondArgument.getNext() == null)) { return true; } } return false; }
private boolean isContainedInGoogDefineClass(Node n) { while (n != null) { n = n.getParent(); if (n.isCall()) { if (isGoogDefineClass(n)) { return true; } } else if (!n.isObjectLit() && !n.isStringKey()) { break; } } return false; }
/** Remove useless calls: Object.defineProperties(o, {}) -> o */ private Node tryFoldCall(Node n) { Preconditions.checkArgument(n.isCall()); if (NodeUtil.isObjectDefinePropertiesDefinition(n)) { Node srcObj = n.getLastChild(); if (srcObj.isObjectLit() && !srcObj.hasChildren()) { Node parent = n.getParent(); Node destObj = n.getSecondChild().detachFromParent(); parent.replaceChild(n, destObj); reportCodeChange(); } } return n; }
/** Strip property type annotations and add suppress checkTypes and globalThis on functions. */ private void suppressBehavior(Node behaviorValue) { if (behaviorValue == null) { compiler.report(JSError.make(behaviorValue, POLYMER_UNQUALIFIED_BEHAVIOR)); return; } if (behaviorValue.isArrayLit()) { for (Node child : behaviorValue.children()) { suppressBehavior(child); } } else if (behaviorValue.isObjectLit()) { stripPropertyTypes(behaviorValue); addBehaviorSuppressions(behaviorValue); } }
/** * Makes sure that the keys for listeners and hostAttributes blocks are quoted to avoid renaming. */ private void quoteListenerAndHostAttributeKeys(Node objLit) { Preconditions.checkState(objLit.isObjectLit()); for (Node keyNode : objLit.children()) { if (keyNode.isComputedProp()) { continue; } if (!keyNode.getString().equals("listeners") && !keyNode.getString().equals("hostAttributes")) { continue; } for (Node keyToQuote : keyNode.getFirstChild().children()) { keyToQuote.setQuotedString(); } } }
/** Try to fold array-element. e.g [1, 2, 3][10]; */ private Node tryFoldGetElem(Node n, Node left, Node right) { Preconditions.checkArgument(n.isGetElem()); if (left.isObjectLit()) { return tryFoldObjectPropAccess(n, left, right); } if (left.isArrayLit()) { return tryFoldArrayAccess(n, left, right); } if (left.isString()) { return tryFoldStringArrayAccess(n, left, right); } return n; }
ClassDefinition( Node target, Node descriptor, JSDocInfo classInfo, MemberDefinition constructor, String nativeBaseElement, List<MemberDefinition> props, List<BehaviorDefinition> behaviors) { this.target = target; Preconditions.checkState(descriptor.isObjectLit()); this.descriptor = descriptor; this.constructor = constructor; this.nativeBaseElement = nativeBaseElement; this.props = props; this.behaviors = behaviors; }
@Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isFunction()) { checkFunctionUse(t, n); } else if (n.isAssign()) { checkAssignment(t, n); } else if (n.isDelProp()) { checkDelete(t, n); } else if (n.isObjectLit()) { checkObjectLiteralOrClass(t, n); } else if (n.isClass()) { checkObjectLiteralOrClass(t, n.getLastChild()); } else if (n.isWith()) { checkWith(t, n); } }
/** * @return A list of functions from a behavior which should be copied to the element prototype. */ private List<MemberDefinition> getBehaviorFunctionsToCopy(Node behaviorObjLit) { Preconditions.checkState(behaviorObjLit.isObjectLit()); ImmutableList.Builder<MemberDefinition> functionsToCopy = ImmutableList.builder(); for (Node keyNode : behaviorObjLit.children()) { if ((keyNode.isStringKey() && keyNode.getFirstChild().isFunction() || keyNode.isMemberFunctionDef()) && !behaviorNamesNotToCopy.contains(keyNode.getString())) { functionsToCopy.add( new MemberDefinition( NodeUtil.getBestJSDocInfo(keyNode), keyNode, keyNode.getFirstChild())); } } return functionsToCopy.build(); }
/** * Updates the first initialization (a.k.a "declaration") of a global name that occurs at a VAR * node. See comment for {@link #updateObjLitOrFunctionDeclaration}. * * @param n An object representing a global name (e.g. "a") */ private void updateObjLitOrFunctionDeclarationAtVarNode(Name n, boolean canCollapseChildNames) { if (!canCollapseChildNames) { return; } Ref ref = n.getDeclaration(); String name = ref.node.getString(); Node rvalue = ref.node.getFirstChild(); Node varNode = ref.node.getParent(); Node grandparent = varNode.getParent(); boolean isObjLit = rvalue.isObjectLit(); int numChanges = 0; if (isObjLit) { numChanges += declareVarsForObjLitValues( n, name, rvalue, varNode, grandparent.getChildBefore(varNode), grandparent); } numChanges += addStubsForUndeclaredProperties(n, name, grandparent, varNode); if (isObjLit && n.canEliminate()) { varNode.removeChild(ref.node); if (!varNode.hasChildren()) { grandparent.removeChild(varNode); } numChanges++; // Clear out the object reference, since we've eliminated it from the // parse tree. ref.node = null; } if (numChanges > 0) { compiler.reportCodeChange(); } }
/** Switches all "this.$.foo" to "this.$['foo']". */ private void switchDollarSignPropsToBrackets(Node objLit) { Preconditions.checkState(objLit.isObjectLit()); for (Node keyNode : objLit.children()) { Node value = keyNode.getFirstChild(); if (value != null && value.isFunction()) { NodeUtil.visitPostOrder( value.getLastChild(), new NodeUtil.Visitor() { @Override public void visit(Node n) { if (n.isString() && n.getString().equals("$") && n.getParent().isGetProp() && n.getParent().getParent().isGetProp()) { Node dollarChildProp = n.getParent().getParent(); dollarChildProp.setType(Token.GETELEM); compiler.reportCodeChange(); } } }, Predicates.<Node>alwaysTrue()); } } }
/** * Validates the class definition and if valid, destructively extracts the class definition from * the AST. */ private ClassDefinition extractClassDefinition(Node targetName, Node callNode) { JSDocInfo classInfo = NodeUtil.getBestJSDocInfo(targetName); // name = goog.defineClass(superClass, {...}, [modifier, ...]) Node superClass = NodeUtil.getArgumentForCallOrNew(callNode, 0); if (superClass == null || (!superClass.isNull() && !superClass.isQualifiedName())) { compiler.report(JSError.make(callNode, GOOG_CLASS_SUPER_CLASS_NOT_VALID)); return null; } if (NodeUtil.isNullOrUndefined(superClass) || superClass.matchesQualifiedName("Object")) { superClass = null; } Node description = NodeUtil.getArgumentForCallOrNew(callNode, 1); if (description == null || !description.isObjectLit() || !validateObjLit(description)) { // report bad class definition compiler.report(JSError.make(callNode, GOOG_CLASS_DESCRIPTOR_NOT_VALID)); return null; } int paramCount = callNode.getChildCount() - 1; if (paramCount > 2) { compiler.report(JSError.make(callNode, GOOG_CLASS_UNEXPECTED_PARAMS)); return null; } Node constructor = extractProperty(description, "constructor"); if (classInfo != null && classInfo.isInterface()) { if (constructor != null) { compiler.report(JSError.make(description, GOOG_CLASS_CONSTRUCTOR_ON_INTERFACE)); return null; } } else if (constructor == null) { // report missing constructor compiler.report(JSError.make(description, GOOG_CLASS_CONSTRUCTOR_MISSING)); return null; } if (constructor == null) { constructor = IR.function( IR.name("").srcref(callNode), IR.paramList().srcref(callNode), IR.block().srcref(callNode)); constructor.srcref(callNode); } JSDocInfo info = NodeUtil.getBestJSDocInfo(constructor); Node classModifier = null; Node statics = null; Node staticsProp = extractProperty(description, "statics"); if (staticsProp != null) { if (staticsProp.isObjectLit() && validateObjLit(staticsProp)) { statics = staticsProp; } else if (staticsProp.isFunction()) { classModifier = staticsProp; } else { compiler.report(JSError.make(staticsProp, GOOG_CLASS_STATICS_NOT_VALID)); return null; } } if (statics == null) { statics = IR.objectlit(); } // Ok, now rip apart the definition into its component pieces. // Remove the "special" property key nodes. maybeDetach(constructor.getParent()); maybeDetach(statics.getParent()); if (classModifier != null) { maybeDetach(classModifier.getParent()); } ClassDefinition def = new ClassDefinition( targetName, classInfo, maybeDetach(superClass), new MemberDefinition(info, null, maybeDetach(constructor)), objectLitToList(maybeDetach(statics)), objectLitToList(description), maybeDetach(classModifier)); return def; }
/** * In the AST that Rhino gives us, it needs to make a distinction between JsDoc on the object * literal node and JsDoc on the object literal value. For example, * * <pre> * var x = { * / JSDOC / * a: 'b', * c: / JSDOC / 'd' * }; * </pre> * * But in few narrow cases (in particular, function literals), it's a lot easier for us if the * doc is attached to the value. */ private static void normalizeObjectLiteralKeyAnnotations(Node objlit, Node key, Node value) { Preconditions.checkState(objlit.isObjectLit()); if (key.getJSDocInfo() != null && value.isFunction()) { value.setJSDocInfo(key.getJSDocInfo()); } }
private Node tryFoldObjectPropAccess(Node n, Node left, Node right) { Preconditions.checkArgument(NodeUtil.isGet(n)); if (!left.isObjectLit() || !right.isString()) { return n; } if (NodeUtil.isAssignmentTarget(n)) { // If GETPROP/GETELEM is used as assignment target the object literal is // acting as a temporary we can't fold it here: // "{a:x}.a += 1" is not "x += 1" return n; } // find the last definition in the object literal Node key = null; Node value = null; for (Node c = left.getFirstChild(); c != null; c = c.getNext()) { if (c.getString().equals(right.getString())) { switch (c.getType()) { case SETTER_DEF: continue; case GETTER_DEF: case STRING_KEY: if (value != null && mayHaveSideEffects(value)) { // The previously found value had side-effects return n; } key = c; value = key.getFirstChild(); break; default: throw new IllegalStateException(); } } else if (mayHaveSideEffects(c.getFirstChild())) { // We don't handle the side-effects here as they might need a temporary // or need to be reordered. return n; } } // Didn't find a definition of the name in the object literal, it might // be coming from the Object prototype if (value == null) { return n; } if (value.isFunction() && NodeUtil.referencesThis(value)) { // 'this' may refer to the object we are trying to remove return n; } Node replacement = value.detachFromParent(); if (key.isGetterDef()) { replacement = IR.call(replacement); replacement.putBooleanProp(Node.FREE_CALL, true); } n.getParent().replaceChild(n, replacement); reportCodeChange(); return n; }
/** * Validates the class definition and if valid, destructively extracts the class definition from * the AST. */ private ClassDefinition extractClassDefinition(Node callNode) { Node descriptor = NodeUtil.getArgumentForCallOrNew(callNode, 0); if (descriptor == null || !descriptor.isObjectLit()) { // report bad class definition compiler.report(JSError.make(callNode, POLYMER_DESCRIPTOR_NOT_VALID)); return null; } int paramCount = callNode.getChildCount() - 1; if (paramCount != 1) { compiler.report(JSError.make(callNode, POLYMER_UNEXPECTED_PARAMS)); return null; } Node elName = NodeUtil.getFirstPropMatchingKey(descriptor, "is"); if (elName == null) { compiler.report(JSError.make(callNode, POLYMER_MISSING_IS)); return null; } String elNameString = CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL, elName.getString()); elNameString += "Element"; Node target; if (NodeUtil.isNameDeclaration(callNode.getParent().getParent())) { target = IR.name(callNode.getParent().getString()); } else if (callNode.getParent().isAssign()) { target = callNode.getParent().getFirstChild().cloneTree(); } else { target = IR.name(elNameString); } target.useSourceInfoIfMissingFrom(callNode); JSDocInfo classInfo = NodeUtil.getBestJSDocInfo(target); JSDocInfo ctorInfo = null; Node constructor = NodeUtil.getFirstPropMatchingKey(descriptor, "factoryImpl"); if (constructor == null) { constructor = IR.function(IR.name(""), IR.paramList(), IR.block()); constructor.useSourceInfoFromForTree(callNode); } else { ctorInfo = NodeUtil.getBestJSDocInfo(constructor); } Node baseClass = NodeUtil.getFirstPropMatchingKey(descriptor, "extends"); String nativeBaseElement = baseClass == null ? null : baseClass.getString(); Node behaviorArray = NodeUtil.getFirstPropMatchingKey(descriptor, "behaviors"); List<BehaviorDefinition> behaviors = extractBehaviors(behaviorArray); List<MemberDefinition> allProperties = new LinkedList<>(); for (BehaviorDefinition behavior : behaviors) { overwriteMembersIfPresent(allProperties, behavior.props); } overwriteMembersIfPresent(allProperties, extractProperties(descriptor)); ClassDefinition def = new ClassDefinition( target, descriptor, classInfo, new MemberDefinition(ctorInfo, null, constructor), nativeBaseElement, allProperties, behaviors); return def; }
/** * 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(); }
/** * Updates the first initialization (a.k.a "declaration") of a global name that occurs at an * ASSIGN node. See comment for {@link #updateObjLitOrFunctionDeclaration}. * * @param n An object representing a global name (e.g. "a", "a.b.c") * @param alias The flattened name for {@code n} (e.g. "a", "a$b$c") */ private void updateObjLitOrFunctionDeclarationAtAssignNode( Name n, String alias, boolean canCollapseChildNames) { // NOTE: It's important that we don't add additional nodes // (e.g. a var node before the exprstmt) because the exprstmt might be // the child of an if statement that's not inside a block). Ref ref = n.getDeclaration(); Node rvalue = ref.node.getNext(); Node varNode = new Node(Token.VAR); Node varParent = ref.node.getAncestor(3); Node grandparent = ref.node.getAncestor(2); boolean isObjLit = rvalue.isObjectLit(); boolean insertedVarNode = false; if (isObjLit && n.canEliminate()) { // Eliminate the object literal altogether. varParent.replaceChild(grandparent, varNode); ref.node = null; insertedVarNode = true; } else if (!n.isSimpleName()) { // Create a VAR node to declare the name. if (rvalue.isFunction()) { checkForHosedThisReferences(rvalue, n.docInfo, n); } ref.node.getParent().removeChild(rvalue); Node nameNode = NodeUtil.newName(compiler, alias, ref.node.getAncestor(2), n.getFullName()); JSDocInfo info = NodeUtil.getBestJSDocInfo(ref.node.getParent()); if (ref.node.getLastChild().getBooleanProp(Node.IS_CONSTANT_NAME) || (info != null && info.isConstant())) { nameNode.putBooleanProp(Node.IS_CONSTANT_NAME, true); } if (info != null) { varNode.setJSDocInfo(info); } varNode.addChildToBack(nameNode); nameNode.addChildToFront(rvalue); varParent.replaceChild(grandparent, varNode); // Update the node ancestry stored in the reference. ref.node = nameNode; insertedVarNode = true; } if (canCollapseChildNames) { if (isObjLit) { declareVarsForObjLitValues( n, alias, rvalue, varNode, varParent.getChildBefore(varNode), varParent); } addStubsForUndeclaredProperties(n, alias, varParent, varNode); } if (insertedVarNode) { if (!varNode.hasChildren()) { varParent.removeChild(varNode); } compiler.reportCodeChange(); } }