@Override public void process(Node externs, Node root) { Node initCode = null; if (!initCodeSource.isEmpty()) { Node initCodeRoot = compiler.parseSyntheticCode("template:init", initCodeSource); if (initCodeRoot != null && initCodeRoot.getFirstChild() != null) { initCode = initCodeRoot.removeChildren(); } else { return; // parse failure } } NodeTraversal.traverseEs6(compiler, root, new RemoveCallback(declarationsToRemove)); NodeTraversal.traverseEs6(compiler, root, new InstrumentCallback()); if (!appNameSetter.isEmpty()) { Node call = IR.call(IR.name(appNameSetter), IR.string(appNameStr)); call.putBooleanProp(Node.FREE_CALL, true); Node expr = IR.exprResult(call); Node addingRoot = compiler.getNodeForCodeInsertion(null); addingRoot.addChildrenToFront(expr.useSourceInfoIfMissingFromForTree(addingRoot)); compiler.reportCodeChange(); } if (initCode != null) { Node addingRoot = compiler.getNodeForCodeInsertion(null); addingRoot.addChildrenToFront(initCode); compiler.reportCodeChange(); } }
/** * Adds an interface for the given ClassDefinition to externs. This allows generated setter * functions for read-only properties to avoid renaming altogether. * * @see https://www.polymer-project.org/0.8/docs/devguide/properties.html#read-only */ private void addInterfaceExterns( final ClassDefinition cls, List<MemberDefinition> readOnlyProps) { Node block = IR.block(); String interfaceName = getInterfaceName(cls); Node fnNode = IR.function(IR.name(""), IR.paramList(), IR.block()); Node varNode = IR.var(NodeUtil.newQName(compiler, interfaceName), fnNode); JSDocInfoBuilder info = new JSDocInfoBuilder(true); info.recordInterface(); varNode.setJSDocInfo(info.build()); block.addChildToBack(varNode); appendPropertiesToBlock(cls, block, interfaceName + ".prototype."); for (MemberDefinition prop : readOnlyProps) { // Add all _set* functions to avoid renaming. String propName = prop.name.getString(); String setterName = "_set" + propName.substring(0, 1).toUpperCase() + propName.substring(1); Node setterExprNode = IR.exprResult(NodeUtil.newQName(compiler, interfaceName + ".prototype." + setterName)); JSDocInfoBuilder setterInfo = new JSDocInfoBuilder(true); JSTypeExpression propType = getTypeFromProperty(prop); setterInfo.recordParameter(propName, propType); setterExprNode.getFirstChild().setJSDocInfo(setterInfo.build()); block.addChildToBack(setterExprNode); } Node parent = polymerElementExterns.getParent(); Node stmts = block.removeChildren(); parent.addChildrenToBack(stmts); compiler.reportCodeChange(); }
/** * Duplicates the PolymerElement externs with a different element base class if needed. For * example, if the base class is HTMLInputElement, then a class PolymerInputElement will be added. * If the element does not extend a native HTML element, this method is a no-op. */ private void appendPolymerElementExterns(final ClassDefinition cls) { if (!nativeExternsAdded.add(cls.nativeBaseElement)) { return; } Node block = IR.block(); Node baseExterns = polymerElementExterns.cloneTree(); String polymerElementType = getPolymerElementType(cls); baseExterns.getFirstChild().setString(polymerElementType); String elementType = tagNameMap.get(cls.nativeBaseElement); JSTypeExpression elementBaseType = new JSTypeExpression(new Node(Token.BANG, IR.string(elementType)), VIRTUAL_FILE); JSDocInfoBuilder baseDocs = JSDocInfoBuilder.copyFrom(baseExterns.getJSDocInfo()); baseDocs.changeBaseType(elementBaseType); baseExterns.setJSDocInfo(baseDocs.build()); block.addChildToBack(baseExterns); for (Node baseProp : polymerElementProps) { Node newProp = baseProp.cloneTree(); Node newPropRootName = NodeUtil.getRootOfQualifiedName(newProp.getFirstChild().getFirstChild()); newPropRootName.setString(polymerElementType); block.addChildToBack(newProp); } Node parent = polymerElementExterns.getParent(); Node stmts = block.removeChildren(); parent.addChildrenAfter(stmts, polymerElementExterns); compiler.reportCodeChange(); }
public void process(Node externs, Node root) { Node initCode = null; if (initCodeSource.length() != 0) { Node initCodeRoot = compiler.parseSyntheticCode(templateFilename + ":init", initCodeSource); if (initCodeRoot != null && initCodeRoot.getFirstChild() != null) { initCode = initCodeRoot.removeChildren(); } else { return; // parse failure } } NodeTraversal.traverse(compiler, root, new RemoveCallback(declarationsToRemove)); NodeTraversal.traverse(compiler, root, new InstrumentCallback()); if (appNameSetter.length() != 0) { Node call = new Node( Token.CALL, Node.newString(Token.NAME, appNameSetter), Node.newString(appNameStr)); Node expr = new Node(Token.EXPR_RESULT, call); Node addingRoot = compiler.getNodeForCodeInsertion(null); addingRoot.addChildrenToFront(expr); compiler.reportCodeChange(); } if (initCode != null) { Node addingRoot = compiler.getNodeForCodeInsertion(null); addingRoot.addChildrenToFront(initCode); compiler.reportCodeChange(); } }
void replaceNodeInPlace(Node n, Node replacement) { Node parent = n.getParent(); if (n.hasChildren()) { Node children = n.removeChildren(); replacement.addChildrenToFront(children); } parent.replaceChild(n, replacement); }
private void rewriteGoogDefineClass(Node exprRoot, final ClassDefinition cls) { // For simplicity add everything into a block, before adding it to the AST. Node block = IR.block(); // remove the original jsdoc info if it was attached to the value. cls.constructor.value.setJSDocInfo(null); if (exprRoot.isVar()) { // example: var ctr = function(){} Node var = IR.var(cls.name.cloneTree(), cls.constructor.value).srcref(exprRoot); JSDocInfo mergedClassInfo = mergeJsDocFor(cls, var); var.setJSDocInfo(mergedClassInfo); block.addChildToBack(var); } else { // example: ns.ctr = function(){} Node assign = IR.assign(cls.name.cloneTree(), cls.constructor.value) .srcref(exprRoot) .setJSDocInfo(cls.constructor.info); JSDocInfo mergedClassInfo = mergeJsDocFor(cls, assign); assign.setJSDocInfo(mergedClassInfo); Node expr = IR.exprResult(assign).srcref(exprRoot); block.addChildToBack(expr); } if (cls.superClass != null) { // example: goog.inherits(ctr, superClass) block.addChildToBack( fixupSrcref( IR.exprResult( IR.call( NodeUtil.newQName(compiler, "goog.inherits").srcrefTree(cls.superClass), cls.name.cloneTree(), cls.superClass.cloneTree()) .srcref(cls.superClass)))); } for (MemberDefinition def : cls.staticProps) { // remove the original jsdoc info if it was attached to the value. def.value.setJSDocInfo(null); // example: ctr.prop = value block.addChildToBack( fixupSrcref( IR.exprResult( fixupSrcref( IR.assign( IR.getprop( cls.name.cloneTree(), IR.string(def.name.getString()).srcref(def.name)) .srcref(def.name), def.value)) .setJSDocInfo(def.info)))); // Handle inner class definitions. maybeRewriteClassDefinition(block.getLastChild()); } for (MemberDefinition def : cls.props) { // remove the original jsdoc info if it was attached to the value. def.value.setJSDocInfo(null); // example: ctr.prototype.prop = value block.addChildToBack( fixupSrcref( IR.exprResult( fixupSrcref( IR.assign( IR.getprop( fixupSrcref( IR.getprop( cls.name.cloneTree(), IR.string("prototype").srcref(def.name))), IR.string(def.name.getString()).srcref(def.name)) .srcref(def.name), def.value)) .setJSDocInfo(def.info)))); // Handle inner class definitions. maybeRewriteClassDefinition(block.getLastChild()); } if (cls.classModifier != null) { // Inside the modifier function, replace references to the argument // with the class name. // function(cls) { cls.Foo = bar; } // becomes // function(cls) { theClassName.Foo = bar; } // The cls parameter is unused, but leave it there so that it // matches the JsDoc. // TODO(tbreisacher): Add a warning if the param is shadowed or reassigned. Node argList = cls.classModifier.getFirstChild().getNext(); Node arg = argList.getFirstChild(); final String argName = arg.getString(); NodeTraversal.traverse( compiler, cls.classModifier.getLastChild(), new AbstractPostOrderCallback() { @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isName() && n.getString().equals(argName)) { parent.replaceChild(n, cls.name.cloneTree()); } } }); block.addChildToBack( IR.exprResult( fixupFreeCall( IR.call(cls.classModifier, cls.name.cloneTree()).srcref(cls.classModifier))) .srcref(cls.classModifier)); } Node parent = exprRoot.getParent(); Node stmts = block.removeChildren(); parent.addChildrenAfter(stmts, exprRoot); parent.removeChild(exprRoot); compiler.reportCodeChange(); }
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(); }
private void visitSuper(Node node, Node parent) { Node enclosing = parent; Node potentialCallee = node; if (!parent.isCall()) { enclosing = parent.getParent(); potentialCallee = parent; } if (!enclosing.isCall() || enclosing.getFirstChild() != potentialCallee) { cannotConvertYet(node, "Only calls to super or to a method of super are supported."); return; } Node clazz = NodeUtil.getEnclosingClass(node); if (clazz == null) { compiler.report(JSError.make(node, NO_SUPERTYPE)); return; } if (NodeUtil.getClassNameNode(clazz) == null) { // Unnamed classes of the form: // f(class extends D { ... }); // give the problem that there is no name to be used in the call to goog.base for the // translation of super calls. // This will throw an error when the class is processed. return; } Node enclosingMemberDef = NodeUtil.getEnclosingClassMember(node); if (enclosingMemberDef.isStaticMember()) { Node superName = clazz.getFirstChild().getNext(); if (!superName.isQualifiedName()) { // This has already been reported, just don't need to continue processing the class. return; } Node callTarget; potentialCallee.detachFromParent(); if (potentialCallee == node) { // of the form super() potentialCallee = IR.getprop(superName.cloneTree(), IR.string(enclosingMemberDef.getString())); enclosing.putBooleanProp(Node.FREE_CALL, false); } else { // of the form super.method() potentialCallee.replaceChild(node, superName.cloneTree()); } callTarget = IR.getprop(potentialCallee, IR.string("call")); enclosing.addChildToFront(callTarget); enclosing.addChildAfter(IR.thisNode(), callTarget); enclosing.useSourceInfoIfMissingFromForTree(enclosing); compiler.reportCodeChange(); return; } String methodName; Node callName = enclosing.removeFirstChild(); if (callName.isSuper()) { methodName = enclosingMemberDef.getString(); } else { methodName = callName.getLastChild().getString(); } Node baseCall = baseCall(clazz, methodName, enclosing.removeChildren()) .useSourceInfoIfMissingFromForTree(enclosing); enclosing.getParent().replaceChild(enclosing, baseCall); compiler.reportCodeChange(); }