@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(); } }
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); }
@Override /** Create the alias declaration (e.g. var $$ALIAS_VOID=void 0;). */ protected void insertAliasDeclaration(Node codeRoot) { Node varNode = new Node(Token.VAR); Node value = IR.voidNode(IR.number(0)); Node name = NodeUtil.newName(compiler.getCodingConvention(), getAliasName(), varNode, getAliasName()); name.addChildToBack(value); varNode.addChildToBack(name); codeRoot.addChildrenToFront(varNode); }
private void addInstaller(Node sourceNode, String function) { // Find the module InputId inputId = sourceNode.getInputId(); CompilerInput input = inputId != null ? compiler.getInput(inputId) : null; JSModule module = input != null ? input.getModule() : null; InjectedInstaller injected = new InjectedInstaller(module, function); if (installers.add(injected)) { changed = true; Node installer = compiler.parseSyntheticCode(function).removeChildren(); installer.useSourceInfoIfMissingFromForTree(sourceNode); Node enclosingScript = NodeUtil.getEnclosingScript(sourceNode); enclosingScript.addChildrenToFront(installer); } }
/** Add blocks to IF, WHILE, DO, etc. */ private void normalizeBlocks(Node n) { if (NodeUtil.isControlStructure(n) && !n.isLabel() && !n.isSwitch()) { for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (NodeUtil.isControlStructureCodeBlock(n, c) && !c.isBlock()) { Node newBlock = IR.block().srcref(n); n.replaceChild(c, newBlock); newBlock.setIsAddedBlock(true); if (!c.isEmpty()) { newBlock.addChildrenToFront(c); } c = newBlock; reportChange(); } } } }
/** * Expand jQuery.extend (and derivative) calls into direct object assignments Example: * jQuery.extend(obj1, {prop1: val1, prop2: val2}) -> obj1.prop1 = val1; obj1.prop2 = val2; */ private void maybeExpandJqueryExtendCall(Node n) { Node callTarget = n.getFirstChild(); Node objectToExtend = callTarget.getNext(); // first argument Node extendArg = objectToExtend.getNext(); // second argument boolean ensureObjectDefined = true; if (extendArg == null) { // Only one argument was specified, so extend jQuery namespace extendArg = objectToExtend; objectToExtend = callTarget.getFirstChild(); ensureObjectDefined = false; } else if (objectToExtend.isGetProp() && (objectToExtend.getLastChild().getString().equals("prototype") || convention.isPrototypeAlias(objectToExtend))) { ensureObjectDefined = false; } // Check for an empty object literal if (!extendArg.hasChildren()) { return; } // Since we are expanding jQuery.extend calls into multiple statements, // encapsulate the new statements in a new block. Node fncBlock = IR.block().srcref(n); if (ensureObjectDefined) { Node assignVal = IR.or(objectToExtend.cloneTree(), IR.objectlit().srcref(n)).srcref(n); Node assign = IR.assign(objectToExtend.cloneTree(), assignVal).srcref(n); fncBlock.addChildrenToFront(IR.exprResult(assign).srcref(n)); } while (extendArg.hasChildren()) { Node currentProp = extendArg.removeFirstChild(); currentProp.setType(Token.STRING); Node propValue = currentProp.removeFirstChild(); Node newProp; if (currentProp.isQuotedString()) { newProp = IR.getelem(objectToExtend.cloneTree(), currentProp).srcref(currentProp); } else { newProp = IR.getprop(objectToExtend.cloneTree(), currentProp).srcref(currentProp); } Node assignNode = IR.assign(newProp, propValue).srcref(currentProp); fncBlock.addChildToBack(IR.exprResult(assignNode).srcref(currentProp)); } // Check to see if the return value is used. If not, replace the original // call with new block. Otherwise, wrap the statements in an // immediately-called anonymous function. if (n.getParent().isExprResult()) { Node parent = n.getParent(); parent.getParent().replaceChild(parent, fncBlock); } else { Node targetVal; if ("jQuery.prototype".equals(objectToExtend.getQualifiedName())) { // When extending the jQuery prototype, return the jQuery namespace. // This is not commonly used. targetVal = objectToExtend.removeFirstChild(); } else { targetVal = objectToExtend.detachFromParent(); } fncBlock.addChildToBack(IR.returnNode(targetVal).srcref(targetVal)); Node fnc = IR.function(IR.name("").srcref(n), IR.paramList().srcref(n), fncBlock).srcref(n); // add an explicit "call" statement so that we can maintain // the same reference for "this" Node newCallTarget = IR.getprop(fnc, IR.string("call").srcref(n)).srcref(n); n.replaceChild(callTarget, newCallTarget); n.putBooleanProp(Node.FREE_CALL, false); // remove any other pre-existing call arguments while (newCallTarget.getNext() != null) { n.removeChildAfter(newCallTarget); } n.addChildToBack(IR.thisNode().srcref(n)); } 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(); }
/** * Inline a function which fulfills the requirements of canInlineReferenceAsStatementBlock into * the call site, replacing the parent expression. */ private Node inlineFunction(Node callNode, Node fnNode, String fnName) { Node parent = callNode.getParent(); Node grandParent = parent.getParent(); // TODO(johnlenz): Consider storing the callSite classification in the // reference object and passing it in here. CallSiteType callSiteType = classifyCallSite(callNode); Preconditions.checkArgument(callSiteType != CallSiteType.UNSUPPORTED); // Store the name for the result. This will be used to // replace "return expr" statements with "resultName = expr" // to replace String resultName = null; boolean needsDefaultReturnResult = true; switch (callSiteType) { case SIMPLE_ASSIGNMENT: resultName = parent.getFirstChild().getString(); break; case VAR_DECL_SIMPLE_ASSIGNMENT: resultName = parent.getString(); break; case SIMPLE_CALL: resultName = null; // "foo()" doesn't need a result. needsDefaultReturnResult = false; break; case EXPRESSION: resultName = getUniqueResultName(); needsDefaultReturnResult = false; // The intermediary result already // has the default value. break; case DECOMPOSABLE_EXPRESSION: throw new IllegalStateException( "Decomposable expressions must decomposed before inlining."); default: throw new IllegalStateException("Unexpected call site type."); } boolean isCallInLoop = NodeUtil.isWithinLoop(callNode); FunctionToBlockMutator mutator = new FunctionToBlockMutator(compiler, this.safeNameIdSupplier); Node newBlock = mutator.mutate( fnName, fnNode, callNode, resultName, needsDefaultReturnResult, isCallInLoop); // TODO(nicksantos): Create a common mutation function that // can replace either a VAR name assignment, assignment expression or // a EXPR_RESULT. Node greatGrandParent = grandParent.getParent(); switch (callSiteType) { case VAR_DECL_SIMPLE_ASSIGNMENT: // Remove the call from the name node. parent.removeChild(parent.getFirstChild()); Preconditions.checkState(parent.getFirstChild() == null); // Add the call, after the VAR. greatGrandParent.addChildAfter(newBlock, grandParent); break; case SIMPLE_ASSIGNMENT: // The assignment is now part of the inline function so // replace it completely. Preconditions.checkState(NodeUtil.isExpressionNode(grandParent)); greatGrandParent.replaceChild(grandParent, newBlock); break; case SIMPLE_CALL: // If nothing is looking at the result just replace the call. Preconditions.checkState(NodeUtil.isExpressionNode(parent)); grandParent.replaceChild(parent, newBlock); break; case EXPRESSION: // TODO(johnlenz): Maybe change this so that movable and decomposable // expressions are handled the same way: The call is moved and // then handled by one the three basic cases, rather than // introducing a new case. Node injectionPoint = ExpressionDecomposer.findInjectionPoint(callNode); Preconditions.checkNotNull(injectionPoint); Node injectionPointParent = injectionPoint.getParent(); Preconditions.checkNotNull(injectionPointParent); Preconditions.checkState(NodeUtil.isStatementBlock(injectionPointParent)); // Declare the intermediate result name. newBlock.addChildrenToFront( NodeUtil.newVarNode(resultName, null).copyInformationFromForTree(callNode)); // Inline the function before the selected injection point (before // the call). injectionPointParent.addChildBefore(newBlock, injectionPoint); // Replace the call site with a reference to the intermediate // result name. parent.replaceChild(callNode, Node.newString(Token.NAME, resultName)); break; default: throw new IllegalStateException("Unexpected call site type."); } return newBlock; }