/** * 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); } }
private void validateImport(Node n) { validateEs6Feature("import statement", n); validateNodeType(Token.IMPORT, n); validateChildCount(n); if (n.getFirstChild().isName()) { validateName(n.getFirstChild()); } else { validateNodeType(Token.EMPTY, n.getFirstChild()); } Node secondChild = n.getChildAtIndex(1); switch (secondChild.getType()) { case Token.IMPORT_SPECS: validateImportSpecifiers(secondChild); break; case Token.IMPORT_STAR: validateNonEmptyString(secondChild); break; default: validateNodeType(Token.EMPTY, secondChild); } validateString(n.getChildAtIndex(2)); }
/** * Finds set of all defs in function given (defSites). Adds parameters of function to this set: * defSites. Finds all variables that are referenced in the function given (alf.names). * windowProps are all global properties available. Closures = alf.names - defSites - windowProps * * @param n root node of the function who's closures are to be found * @return set of closure names */ private Set<String> findClosures(Node n) { Set<String> closureVars = null; SimpleDefinitionFinder defFinder = new SimpleDefinitionFinder(compiler); defFinder.process(externs, n.getLastChild()); Collection<DefinitionSite> defSites = defFinder.getDefinitionSites(); Set<String> localDefs = new HashSet<String>(); for (DefinitionSite site : defSites) { if (site.node.getType() == Token.GETPROP) continue; String def = site.node.getString(); if (def.length() > 0) { localDefs.add(def); } } // adding params to function as defs Node origParamNode = n.getChildAtIndex(1); for (int i = 0; i < origParamNode.getChildCount(); i++) { String temp = origParamNode.getChildAtIndex(i).getString(); localDefs.add(temp); } // System.out.println("\nPrinting LOCAL def sites:" + defs); /*SimpleDefinitionFinder defFinder1 = new SimpleDefinitionFinder(compiler); defFinder1.process(externs, root); Collection<DefinitionSite> defSites1 = defFinder1.getDefinitionSites(); Set<String> defs1 = new HashSet<String>(); for(DefinitionSite site1: defSites1){ if (site1.node.getType() == Token.GETPROP) continue; String def = site1.node.getString(); if (def.length() > 0){ defs1.add(def); } } System.out.println("\nPrinting Global def sites:" + defs1);*/ AllNamesFinder alf = new AllNamesFinder(compiler); NodeTraversal.traverse(compiler, n.getLastChild(), alf); // System.out.println("all names: " + alf.names); closureVars = alf.names; closureVars.removeAll(localDefs); closureVars.removeAll(Props.windowProps); closureVars.remove( "this"); // since 'this' is later modified to $$_self we don't need to consider this as // closure return closureVars; }
@Override public void visit(NodeTraversal t, Node n, Node parent) { // System.out.println(n); switch (n.getType()) { case Token.FUNCTION: Node functionNameNode = n.getFirstChild(); String currFunctionName = functionNameNode.getString(); if (!anonymizedFnNodes.contains(n)) { if (currFunctionName.length() == 0) { Set<String> closures = findClosures(n); // System.out.println("closures" + closures); n.detachFromParent(); // detach anonymous function from. // change all references to 'this' in method to $$_self boolean thisChanged = changeThisTo$$_self(n); // clone the parameters of modified $$$anonym(originalParams..., closures..) so that // we can use it for parameters of function call below Node clonedorigParamNode = n.getChildAtIndex(1).cloneTree(); clonedorigParamNode.setType(Token.LP); // parent // give 'n' a name and attach to end // e.g. function $$anonym(originalParams..., closures..) String anonymName = "$$anonym" + anonymCount++; Node funcNameNode = Node.newString(Token.NAME, anonymName); n.replaceChild(n.getFirstChild(), funcNameNode); Node parametersNode = n.getChildAtIndex(1); addParamsToMethod(closures, parametersNode, thisChanged, "$$_self"); root.getFirstChild().addChildrenToBack(n); // replace original anonymous call to new anonymous function that closes over // params and then makes a call to our named function Node newAnonymNode = createAnonymWithParamCall(closures, anonymName, clonedorigParamNode, thisChanged); parent.addChildrenToBack(newAnonymNode); // add to list of anonymized nodes so that we can ignore it on second pass anonymizedFnNodes.add(newAnonymNode); compiler.reportCodeChange(); } else { // named function just find closures and add as parameter // TODO } } } }
private void validateFunctionStatement(Node n) { validateNodeType(Token.FUNCTION, n); validateChildCount(n); validateName(n.getFirstChild()); validateParameters(n.getChildAtIndex(1)); validateBlock(n.getLastChild()); }
private void validateForOf(Node n) { validateNodeType(Token.FOR_OF, n); validateChildCount(n); validateVarOrAssignmentTarget(n.getFirstChild()); validateExpression(n.getChildAtIndex(1)); validateBlock(n.getLastChild()); }
private void validateTry(Node n) { validateNodeType(Token.TRY, n); validateChildCountIn(n, 2, 3); validateBlock(n.getFirstChild()); boolean seenCatchOrFinally = false; // Validate catch Node catches = n.getChildAtIndex(1); validateNodeType(Token.BLOCK, catches); validateMaximumChildCount(catches, 1); if (catches.hasChildren()) { validateCatch(catches.getFirstChild()); seenCatchOrFinally = true; } // Validate finally if (n.getChildCount() == 3) { validateBlock(n.getLastChild()); seenCatchOrFinally = true; } if (!seenCatchOrFinally) { violation("Missing catch or finally for try statement.", n); } }
String nextString() { if (paramNames != null) { return paramNames.next(); } index++; return params.getChildAtIndex(index).getString(); }
private Node createAnonymWithParamCall( Set<String> closures, String anonymName, Node clonedorigParamNode, boolean thisChanged) { Node newAnonymNode = new Node(Token.FUNCTION); Node blockNode = new Node(Token.BLOCK); newAnonymNode.addChildrenToBack(Node.newString(Token.NAME, "")); newAnonymNode.addChildrenToBack(clonedorigParamNode); newAnonymNode.addChildrenToBack(blockNode); // to block add call with params as closure varibles Node returnNode = new Node(Token.RETURN); Node callNode = new Node(Token.CALL); callNode.addChildrenToBack(Node.newString(Token.NAME, anonymName)); // first add all original params to this call for (int i = 0; i < clonedorigParamNode.getChildCount(); i++) { Node temp = clonedorigParamNode.getChildAtIndex(i).cloneTree(); callNode.addChildrenToBack(temp); } addParamsToMethod(closures, callNode, thisChanged, "this"); returnNode.addChildrenToBack(callNode); blockNode.addChildrenToBack(returnNode); return newAnonymNode; }
private void validateFor(Node n) { validateNodeType(Token.FOR, n); if (NodeUtil.isForIn(n)) { // FOR-IN validateChildCount(n, 3); validateVarOrAssignmentTarget(n.getFirstChild()); validateExpression(n.getChildAtIndex(1)); } else { // FOR validateChildCount(n, 4); validateVarOrOptionalExpression(n.getFirstChild()); validateOptionalExpression(n.getChildAtIndex(1)); validateOptionalExpression(n.getChildAtIndex(2)); } validateBlock(n.getLastChild()); }
private void validateIf(Node n) { validateNodeType(Token.IF, n); validateChildCountIn(n, 2, 3); validateExpression(n.getFirstChild()); validateBlock(n.getChildAtIndex(1)); if (n.getChildCount() == 3) { validateBlock(n.getLastChild()); } }
/** Processes trailing default and rest parameters. */ private void visitParamList(Node paramList, Node function) { Node insertSpot = null; Node block = function.getLastChild(); for (int i = 0; i < paramList.getChildCount(); i++) { Node param = paramList.getChildAtIndex(i); if (param.isDefaultValue()) { Node nameOrPattern = param.removeFirstChild(); Node defaultValue = param.removeFirstChild(); Node newParam = nameOrPattern.isName() ? nameOrPattern : IR.name(DESTRUCTURING_TEMP_VAR + (destructuringVarCounter++)); Node lhs = nameOrPattern.cloneTree(); Node rhs = defaultValueHook(newParam.cloneTree(), defaultValue); Node newStatement = nameOrPattern.isName() ? IR.exprResult(IR.assign(lhs, rhs)) : IR.var(lhs, rhs); newStatement.useSourceInfoIfMissingFromForTree(param); block.addChildAfter(newStatement, insertSpot); insertSpot = newStatement; paramList.replaceChild(param, newParam); newParam.setOptionalArg(true); compiler.reportCodeChange(); } else if (param.isRest()) { // rest parameter param.setType(Token.NAME); param.setVarArgs(true); // Transpile to: param = [].slice.call(arguments, i); Node newArr = IR.exprResult( IR.assign( IR.name(param.getString()), IR.call( IR.getprop( IR.getprop(IR.arraylit(), IR.string("slice")), IR.string("call")), IR.name("arguments"), IR.number(i)))); block.addChildAfter(newArr.useSourceInfoIfMissingFromForTree(param), insertSpot); compiler.reportCodeChange(); } else if (param.isDestructuringPattern()) { String tempVarName = DESTRUCTURING_TEMP_VAR + (destructuringVarCounter++); paramList.replaceChild(param, IR.name(tempVarName)); Node newDecl = IR.var(param, IR.name(tempVarName)); block.addChildAfter(newDecl, insertSpot); insertSpot = newDecl; } } // For now, we are running transpilation before type-checking, so we'll // need to make sure changes don't invalidate the JSDoc annotations. // Therefore we keep the parameter list the same length and only initialize // the values if they are set to undefined. }
private void validateExport(Node n) { validateNodeType(Token.EXPORT, n); if (n.getBooleanProp(Node.EXPORT_ALL_FROM)) { // export * from "mod" validateChildCount(n, 2); validateNodeType(Token.EMPTY, n.getFirstChild()); validateString(n.getChildAtIndex(1)); } else if (n.getBooleanProp(Node.EXPORT_DEFAULT)) { // export default foo = 2 validateChildCount(n, 1); validateExpression(n.getFirstChild()); } else { validateChildCountIn(n, 1, 2); if (n.getFirstChild().getType() == Token.EXPORT_SPECS) { validateExportSpecifiers(n.getFirstChild()); } else { validateStatement(n.getFirstChild()); } if (n.getChildCount() == 2) { validateString(n.getChildAtIndex(1)); } } }
private void validateObjectLitSetKey(Node n) { validateNodeType(Token.SETTER_DEF, n); validateChildCount(n); validateObjectLiteralKeyName(n); Node function = n.getFirstChild(); validateFunctionExpression(function); // objlit set functions must be nameless, and must have 1 parameter. if (!function.getFirstChild().getString().isEmpty()) { violation("Expected unnamed function expression.", n); } Node functionParams = function.getChildAtIndex(1); if (!functionParams.hasOneChild()) { violation("set methods must have exactly one parameter.", n); } }
private void validateTemplateLit(Node n) { validateEs6Feature("template literal", n); validateNodeType(Token.TEMPLATELIT, n); if (!n.hasChildren()) { return; } for (int i = 0; i < n.getChildCount(); i++) { Node child = n.getChildAtIndex(i); // If the first child is not a STRING, this is a tagged template. if (i == 0 && !child.isString()) { validateExpression(child); } else if (child.isString()) { validateString(child); } else { validateTemplateLitSub(child); } } }
private void validateFunctionExpression(Node n) { validateNodeType(Token.FUNCTION, n); validateChildCount(n); validateParameters(n.getChildAtIndex(1)); if (n.isArrowFunction()) { validateEs6Feature("arrow functions", n); validateEmptyName(n.getFirstChild()); if (n.getLastChild().getType() == Token.BLOCK) { validateBlock(n.getLastChild()); } else { validateExpression(n.getLastChild()); } } else { validateOptionalName(n.getFirstChild()); validateBlock(n.getLastChild()); } }
@Override public void visit(NodeTraversal t, Node n, Node parent) { if (isBehavior(n)) { if (!n.isVar() && !n.isAssign()) { compiler.report(JSError.make(n, POLYMER_UNQUALIFIED_BEHAVIOR)); return; } // Add @nocollapse. JSDocInfoBuilder newDocs = JSDocInfoBuilder.maybeCopyFrom(n.getJSDocInfo()); newDocs.recordNoCollapse(); n.setJSDocInfo(newDocs.build()); Node behaviorValue = n.getChildAtIndex(1); if (n.isVar()) { behaviorValue = n.getFirstChild().getFirstChild(); } suppressBehavior(behaviorValue); } }
/** Processes trailing default and rest parameters. */ private void visitParamList(Node paramList, Node function) { Node insertSpot = null; Node block = function.getLastChild(); for (int i = 0; i < paramList.getChildCount(); i++) { Node param = paramList.getChildAtIndex(i); if (param.hasChildren()) { // default parameter param.setOptionalArg(true); Node defaultValue = param.removeFirstChild(); // Transpile to: param === undefined && (param = defaultValue); Node name = IR.name(param.getString()); Node undefined = IR.name("undefined"); Node stm = IR.exprResult( IR.and(IR.sheq(name, undefined), IR.assign(name.cloneNode(), defaultValue))); block.addChildAfter(stm.useSourceInfoIfMissingFromForTree(param), insertSpot); insertSpot = stm; compiler.reportCodeChange(); } else if (param.isRest()) { // rest parameter param.setType(Token.NAME); param.setVarArgs(true); // Transpile to: param = [].slice.call(arguments, i); Node newArr = IR.exprResult( IR.assign( IR.name(param.getString()), IR.call( IR.getprop( IR.getprop(IR.arraylit(), IR.string("slice")), IR.string("call")), IR.name("arguments"), IR.number(i)))); block.addChildAfter(newArr.useSourceInfoIfMissingFromForTree(param), insertSpot); compiler.reportCodeChange(); } } // For now, we are running transpilation before type-checking, so we'll // need to make sure changes don't invalidate the JSDoc annotations. // Therefore we keep the parameter list the same length and only initialize // the values if they are set to undefined. }
private void transformLoopClosure() { if (loopObjectMap.isEmpty()) { return; } for (Node loopNode : loopObjectMap.keySet()) { // Introduce objects to reflect the captured scope variables. // Fields are initially left as undefined to avoid cases like: // var $jscomp$loop$0 = {i: 0, j: $jscomp$loop$0.i} // They are initialized lazily by changing declarations into assignments // later. LoopObject object = loopObjectMap.get(loopNode); Node objectLit = IR.objectlit(); Node objectLitNextIteration = IR.objectlit(); for (Var var : object.vars) { objectLit.addChildToBack(IR.stringKey(var.name, IR.name("undefined"))); objectLitNextIteration.addChildToBack( IR.stringKey(var.name, IR.getprop(IR.name(object.name), IR.string(var.name)))); } Node updateLoopObject = IR.assign(IR.name(object.name), objectLitNextIteration); loopNode .getParent() .addChildBefore( IR.var(IR.name(object.name), objectLit).useSourceInfoIfMissingFromForTree(loopNode), loopNode); if (NodeUtil.isVanillaFor(loopNode)) { // For // The initializer is pulled out and placed prior to the loop. Node initializer = loopNode.getFirstChild(); loopNode.replaceChild(initializer, IR.empty()); if (!initializer.isEmpty()) { loopNode.getParent().addChildBefore(initializer, loopNode); } Node increment = loopNode.getChildAtIndex(2); if (increment.isEmpty()) { loopNode.replaceChild( increment, updateLoopObject.useSourceInfoIfMissingFromForTree(loopNode)); } else { Node placeHolder = IR.empty(); loopNode.replaceChild(increment, placeHolder); loopNode.replaceChild( placeHolder, IR.comma(updateLoopObject, increment).useSourceInfoIfMissingFromForTree(loopNode)); } } else if (loopNode.isDo()) { // do-while, put at the end of the block loopNode .getFirstChild() .addChildToBack( IR.exprResult(updateLoopObject).useSourceInfoIfMissingFromForTree(loopNode)); } else { // For-in, for-of or while, put at the end of the block loopNode .getLastChild() .addChildToBack( IR.exprResult(updateLoopObject).useSourceInfoIfMissingFromForTree(loopNode)); } // For captured variables, change declarations to assignments on the // corresponding field of the introduced object. Rename all references // accordingly. for (Var var : object.vars) { for (Node reference : referenceMap.get(var)) { // For-of and for-in declarations are not altered, since they are // used as temporary variables for assignment. if (NodeUtil.isEnhancedFor(loopNode) && loopNode.getFirstChild() == reference.getParent()) { loopNode .getLastChild() .addChildToFront( IR.exprResult( IR.assign( IR.getprop(IR.name(object.name), IR.string(var.name)), IR.name(var.name))) .useSourceInfoIfMissingFromForTree(reference)); } else { if (NodeUtil.isNameDeclaration(reference.getParent())) { Node declaration = reference.getParent(); Node grandParent = declaration.getParent(); // Normalize: "let i = 0, j = 0;" becomes "let i = 0; let j = 0;" while (declaration.getChildCount() > 1) { Node name = declaration.getLastChild(); grandParent.addChildAfter( IR.declaration(name.detachFromParent(), declaration.getType()) .useSourceInfoIfMissingFromForTree(declaration), declaration); } // Change declaration to assignment, or just drop it if there's // no initial value. if (reference.hasChildren()) { declaration = reference.getParent(); // Might have changed now Node newReference = IR.name(var.name); Node replacement = IR.exprResult(IR.assign(newReference, reference.removeFirstChild())) .useSourceInfoIfMissingFromForTree(declaration); grandParent.replaceChild(declaration, replacement); reference = newReference; } else { grandParent.removeChild(declaration); } } // Change reference to GETPROP. reference .getParent() .replaceChild( reference, IR.getprop(IR.name(object.name), IR.string(var.name)) .useSourceInfoIfMissingFromForTree(reference)); } } } } // Create wrapper functions and call them. for (Node function : functionLoopObjectsMap.keySet()) { Node returnNode = IR.returnNode(); Collection<LoopObject> objects = functionLoopObjectsMap.get(function); Node[] objectNames = new Node[objects.size()]; Node[] objectNamesForCall = new Node[objects.size()]; int i = 0; for (LoopObject object : objects) { objectNames[i] = IR.name(object.name); objectNamesForCall[i] = IR.name(object.name); i++; } Node iife = IR.function(IR.name(""), IR.paramList(objectNames), IR.block(returnNode)); Node call = IR.call(iife, objectNamesForCall); call.putBooleanProp(Node.FREE_CALL, true); function .getParent() .replaceChild(function, call.useSourceInfoIfMissingFromForTree(function)); returnNode.addChildToFront(function); } }
Node getNode() { if (paramNames != null) { return null; } return params.getChildAtIndex(index); }
private Node tryExpandJqueryEachCall( NodeTraversal t, Node n, Node callbackFunction, List<Node> keyNodes, List<Node> valueNodes) { Node callTarget = n.getFirstChild(); Node objectToLoopOver = callTarget.getNext(); // New block to contain the expanded statements Node fncBlock = IR.block().srcref(callTarget); boolean isValidExpansion = true; // Expand the jQuery.expandedEach call Node key = objectToLoopOver.getFirstChild(), val = null; for (int i = 0; key != null; key = key.getNext(), i++) { if (key != null) { if (objectToLoopOver.isArrayLit()) { // Arrays have a value of their index number val = IR.number(i).srcref(key); } else { val = key.getFirstChild(); } } // Keep track of the replaced nodes so we can reset the tree List<Node> newKeys = new ArrayList<>(); List<Node> newValues = new ArrayList<>(); List<Node> origGetElems = new ArrayList<>(); List<Node> newGetProps = new ArrayList<>(); // Replace all of the key nodes with the prop name for (int j = 0; j < keyNodes.size(); j++) { Node origNode = keyNodes.get(j); Node ancestor = origNode.getParent(); Node newNode = IR.string(key.getString()).srcref(key); newKeys.add(newNode); ancestor.replaceChild(origNode, newNode); // Walk up the tree to see if the key is used in a GETELEM // assignment while (ancestor != null && !NodeUtil.isStatement(ancestor) && !ancestor.isGetElem()) { ancestor = ancestor.getParent(); } // Convert GETELEM nodes to GETPROP nodes so that they can be // renamed or removed. if (ancestor != null && ancestor.isGetElem()) { Node propObject = ancestor; while (propObject.isGetProp() || propObject.isGetElem()) { propObject = propObject.getFirstChild(); } Node ancestorClone = ancestor.cloneTree(); // Run the peephole passes to handle cases such as // obj['lit' + key] = val; peepholePasses.process(null, ancestorClone.getChildAtIndex(1)); Node prop = ancestorClone.getChildAtIndex(1); if (prop.isString() && NodeUtil.isValidPropertyName(LanguageMode.ECMASCRIPT3, prop.getString())) { Node target = ancestorClone.getFirstChild(); Node newGetProp = IR.getprop(target.detachFromParent(), prop.detachFromParent()); newGetProps.add(newGetProp); origGetElems.add(ancestor); ancestor.getParent().replaceChild(ancestor, newGetProp); } else { if (prop.isString() && !NodeUtil.isValidPropertyName(LanguageMode.ECMASCRIPT3, prop.getString())) { t.report(n, JQUERY_UNABLE_TO_EXPAND_INVALID_NAME_ERROR, prop.getString()); } isValidExpansion = false; } } } if (isValidExpansion) { // Replace all of the value nodes with the prop value for (int j = 0; val != null && j < valueNodes.size(); j++) { Node origNode = valueNodes.get(j); Node newNode = val.cloneTree(); newValues.add(newNode); origNode.getParent().replaceChild(origNode, newNode); } // Wrap the new tree in an anonymous function call Node fnc = IR.function( IR.name("").srcref(key), IR.paramList().srcref(key), callbackFunction.getChildAtIndex(2).cloneTree()) .srcref(key); Node call = IR.call(fnc).srcref(key); call.putBooleanProp(Node.FREE_CALL, true); fncBlock.addChildToBack(IR.exprResult(call).srcref(call)); } // Reset the source tree for (int j = 0; j < newGetProps.size(); j++) { newGetProps.get(j).getParent().replaceChild(newGetProps.get(j), origGetElems.get(j)); } for (int j = 0; j < newKeys.size(); j++) { newKeys.get(j).getParent().replaceChild(newKeys.get(j), keyNodes.get(j)); } for (int j = 0; j < newValues.size(); j++) { newValues.get(j).getParent().replaceChild(newValues.get(j), valueNodes.get(j)); } if (!isValidExpansion) { return null; } } return fncBlock; }
private void rewritePolymerClass(Node exprRoot, final ClassDefinition cls, NodeTraversal t) { // Add {@code @lends} to the object literal. Node call = exprRoot.getFirstChild(); if (call.isAssign()) { call = call.getChildAtIndex(1); } else if (call.isName()) { call = call.getFirstChild(); } Node objLit = cls.descriptor; if (hasShorthandAssignment(objLit)) { compiler.report(JSError.make(objLit, POLYMER_SHORTHAND_NOT_SUPPORTED)); return; } JSDocInfoBuilder objLitDoc = new JSDocInfoBuilder(true); objLitDoc.recordLends(cls.target.getQualifiedName() + ".prototype"); objLit.setJSDocInfo(objLitDoc.build()); this.addTypesToFunctions(objLit, cls.target.getQualifiedName()); this.switchDollarSignPropsToBrackets(objLit); this.quoteListenerAndHostAttributeKeys(objLit); // For simplicity add everything into a block, before adding it to the AST. Node block = IR.block(); if (cls.nativeBaseElement != null) { this.appendPolymerElementExterns(cls); } JSDocInfoBuilder constructorDoc = this.getConstructorDoc(cls); // Remove the original constructor JS docs from the objlit. Node ctorKey = cls.constructor.value.getParent(); if (ctorKey != null) { ctorKey.removeProp(Node.JSDOC_INFO_PROP); } if (cls.target.isGetProp()) { // foo.bar = Polymer({...}); Node assign = IR.assign(cls.target.cloneTree(), cls.constructor.value.cloneTree()); assign.setJSDocInfo(constructorDoc.build()); Node exprResult = IR.exprResult(assign); block.addChildToBack(exprResult); } else { // var foo = Polymer({...}); OR Polymer({...}); Node var = IR.var(cls.target.cloneTree(), cls.constructor.value.cloneTree()); var.setJSDocInfo(constructorDoc.build()); block.addChildToBack(var); } appendPropertiesToBlock(cls, block, cls.target.getQualifiedName() + ".prototype."); appendBehaviorMembersToBlock(cls, block); List<MemberDefinition> readOnlyProps = parseReadOnlyProperties(cls, block); addInterfaceExterns(cls, readOnlyProps); removePropertyDocs(objLit); block.useSourceInfoIfMissingFromForTree(exprRoot); Node stmts = block.removeChildren(); Node parent = exprRoot.getParent(); // If the call to Polymer() is not in the global scope and the assignment target is not // namespaced (which likely means it's exported to the global scope), put the type declaration // into the global scope at the start of the current script. // // This avoids unknown type warnings which are a result of the compiler's poor understanding of // types declared inside IIFEs or any non-global scope. We should revisit this decision after // moving to the new type inference system which should be able to infer these types better. if (!t.getScope().isGlobal() && !cls.target.isGetProp()) { Node scriptNode = NodeUtil.getEnclosingScript(exprRoot); scriptNode.addChildrenToFront(stmts); } else { Node beforeRoot = parent.getChildBefore(exprRoot); if (beforeRoot == null) { parent.addChildrenToFront(stmts); } else { parent.addChildrenAfter(stmts, beforeRoot); } } if (exprRoot.isVar()) { Node assignExpr = varToAssign(exprRoot); parent.replaceChild(exprRoot, assignExpr); } compiler.reportCodeChange(); }