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(); } }
@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(); } }
/** Applies optimizations to all previously marked nodes. */ public void applyChanges() { for (Node n : toRemove) { n.getParent().removeChild(n); compiler.reportCodeChange(); } for (Node n : toReplaceWithZero) { n.getParent().replaceChild(n, Node.newNumber(0).copyInformationFrom(n)); compiler.reportCodeChange(); } }
/** 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 addExportMethod( Map<String, GenerateNodeContext> exports, String export, GenerateNodeContext context) { CodingConvention convention = compiler.getCodingConvention(); // Emit the proper CALL expression. // This is an optimization to avoid exporting everything as a symbol // because exporting a property is significantly simpler/faster. // Only export the property if the parent is being exported or // if the parent is "prototype" and the grandparent is being exported. String parent = null; String grandparent = null; Node node = context.getNode().getFirstChild(); if (node.isGetProp()) { Node parentNode = node.getFirstChild(); parent = parentNode.getQualifiedName(); if (parentNode.isGetProp() && parentNode.getLastChild().getString().equals(PROTOTYPE_PROPERTY)) { grandparent = parentNode.getFirstChild().getQualifiedName(); } } boolean useExportSymbol = true; if (grandparent != null && exports.containsKey(grandparent)) { // grandparent is only set for properties exported off a prototype obj. useExportSymbol = false; } else if (parent != null && exports.containsKey(parent)) { useExportSymbol = false; } Node call; if (useExportSymbol) { // exportSymbol(publicPath, object); call = IR.call( NodeUtil.newQualifiedNameNode( convention, exportSymbolFunction, context.getNode(), export), IR.string(export), NodeUtil.newQualifiedNameNode(convention, export, context.getNode(), export)); } else { // exportProperty(object, publicName, symbol); String property = getPropertyName(node); call = IR.call( NodeUtil.newQualifiedNameNode( convention, exportPropertyFunction, context.getNode(), exportPropertyFunction), NodeUtil.newQualifiedNameNode( convention, parent, context.getNode(), exportPropertyFunction), IR.string(property), NodeUtil.newQualifiedNameNode( convention, export, context.getNode(), exportPropertyFunction)); } Node expression = IR.exprResult(call); annotate(expression); addStatement(context, expression); compiler.reportCodeChange(); }
/** * Removes unreferenced arguments from a function declaration and when possible the function's * callSites. * * @param fnScope The scope inside the function */ private void removeUnreferencedFunctionArgs(Scope fnScope) { // TODO(johnlenz): Update type registry for function signature changes. Node function = fnScope.getRootNode(); Preconditions.checkState(function.getType() == Token.FUNCTION); if (NodeUtil.isGetOrSetKey(function.getParent())) { // The parameters object literal setters can not be removed. return; } Node argList = getFunctionArgList(function); boolean modifyCallers = modifyCallSites && callSiteOptimizer.canModifyCallers(function); if (!modifyCallers) { // Strip unreferenced args off the end of the function declaration. Node lastArg; while ((lastArg = argList.getLastChild()) != null) { Var var = fnScope.getVar(lastArg.getString()); if (!referenced.contains(var)) { Preconditions.checkNotNull(var == null); argList.removeChild(lastArg); compiler.reportCodeChange(); } else { break; } } } else { callSiteOptimizer.optimize(fnScope, referenced); } }
/** * Replaces a GETPROP a.b.c with a NAME a$b$c. * * @param alias A flattened prefix name (e.g. "a$b") * @param n The GETPROP node corresponding to the original name (e.g. "a.b") * @param parent {@code n}'s parent * @param originalName String version of the property name. */ private void flattenNameRef(String alias, Node n, Node parent, String originalName) { Preconditions.checkArgument( n.isGetProp(), "Expected GETPROP, found %s. Node: %s", Token.name(n.getType()), n); // BEFORE: // getprop // getprop // name a // string b // string c // AFTER: // name a$b$c Node ref = NodeUtil.newName(compiler, alias, n, originalName); NodeUtil.copyNameAnnotations(n.getLastChild(), ref); if (parent.isCall() && n == parent.getFirstChild()) { // The node was a call target, we are deliberately flatten these as // we node the "this" isn't provided by the namespace. Mark it as such: parent.putBooleanProp(Node.FREE_CALL, true); } TypeI type = n.getTypeI(); if (type != null) { ref.setTypeI(type); } parent.replaceChild(n, ref); 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(); }
private void visitForOf(Node node, Node parent) { Node variable = node.removeFirstChild(); Node iterable = node.removeFirstChild(); Node body = node.removeFirstChild(); Node iterName = IR.name(ITER_BASE + compiler.getUniqueNameIdSupplier().get()); Node getNext = IR.call(IR.getprop(iterName.cloneTree(), IR.string("next"))); String variableName = variable.isName() ? variable.getQualifiedName() : variable.getFirstChild().getQualifiedName(); // var or let Node iterResult = IR.name(ITER_RESULT + variableName); Node makeIter = IR.call(NodeUtil.newQualifiedNameNode(compiler.getCodingConvention(), MAKE_ITER), iterable); Node init = IR.var(iterName.cloneTree(), makeIter); Node initIterResult = iterResult.cloneTree(); initIterResult.addChildToFront(getNext.cloneTree()); init.addChildToBack(initIterResult); Node cond = IR.not(IR.getprop(iterResult.cloneTree(), IR.string("done"))); Node incr = IR.assign(iterResult.cloneTree(), getNext.cloneTree()); body.addChildToFront( IR.var(IR.name(variableName), IR.getprop(iterResult.cloneTree(), IR.string("value")))); Node newFor = IR.forNode(init, cond, incr, body); newFor.useSourceInfoIfMissingFromForTree(node); parent.replaceChild(node, newFor); 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(); }
/** @param marker The marker to add synthetic blocks for. */ private void addBlocks(Marker marker) { // Add block around the template section so that it looks like this: // BLOCK (synthetic) // START // BLOCK (synthetic) // BODY // END // This prevents the start or end markers from mingling with the code // in the block body. Node originalParent = marker.endMarker.getParent(); Node outerBlock = IR.block(); outerBlock.setIsSyntheticBlock(true); originalParent.addChildBefore(outerBlock, marker.startMarker); Node innerBlock = IR.block(); innerBlock.setIsSyntheticBlock(true); // Move everything after the start Node up to the end Node into the inner // block. moveSiblingExclusive(originalParent, innerBlock, marker.startMarker, marker.endMarker); // Add the start node. outerBlock.addChildToBack(originalParent.removeChildAfter(outerBlock)); // Add the inner block outerBlock.addChildToBack(innerBlock); // and finally the end node. outerBlock.addChildToBack(originalParent.removeChildAfter(outerBlock)); compiler.reportCodeChange(); }
private void addExtern(String export) { Node propstmt = IR.exprResult(IR.getprop(qualifiedNameNode("Object.prototype"), IR.string(export))); NodeUtil.setDebugInformation(propstmt, getSynthesizedExternsRoot(), export); getSynthesizedExternsRoot().addChildToBack(propstmt); compiler.reportCodeChange(); }
/** * Adds global variable "stubs" for any properties of a global name that are only set in a local * scope or read but never set. * * @param n An object representing a global name (e.g. "a", "a.b.c") * @param alias The flattened name of the object whose properties we are adding stubs for (e.g. * "a$b$c") * @param parent The node to which new global variables should be added as children * @param addAfter The child of after which new variables should be added * @return The number of variables added */ private int addStubsForUndeclaredProperties(Name n, String alias, Node parent, Node addAfter) { Preconditions.checkState(n.canCollapseUnannotatedChildNames()); Preconditions.checkArgument(NodeUtil.isStatementBlock(parent)); Preconditions.checkNotNull(addAfter); if (n.props == null) { return 0; } int numStubs = 0; for (Name p : n.props) { if (p.needsToBeStubbed()) { String propAlias = appendPropForAlias(alias, p.getBaseName()); Node nameNode = IR.name(propAlias); Node newVar = IR.var(nameNode).useSourceInfoIfMissingFromForTree(addAfter); parent.addChildAfter(newVar, addAfter); addAfter = newVar; numStubs++; compiler.reportCodeChange(); // Determine if this is a constant var by checking the first // reference to it. Don't check the declaration, as it might be null. if (p.getRefs().get(0).node.getLastChild().getBooleanProp(Node.IS_CONSTANT_NAME)) { nameNode.putBooleanProp(Node.IS_CONSTANT_NAME, true); } } } return numStubs; }
@Override public void process(Node externs, Node root) { assignmentLog = new StringBuilder(); // Do variable reference counting. NodeTraversal.traverse(compiler, externs, new ProcessVars(true)); NodeTraversal.traverse(compiler, root, new ProcessVars(false)); // Make sure that new names don't overlap with extern names. reservedNames.addAll(externNames); // Rename vars, sorted by frequency of occurrence to minimize code size. SortedSet<Assignment> varsByFrequency = new TreeSet<Assignment>(FREQUENCY_COMPARATOR); varsByFrequency.addAll(assignments.values()); if (shouldShadow) { new ShadowVariables(compiler, assignments, varsByFrequency, pseudoNameMap) .process(externs, root); } // First try to reuse names from an earlier compilation. if (prevUsedRenameMap != null) { reusePreviouslyUsedVariableMap(); } // Assign names, sorted by descending frequency to minimize code size. assignNames(varsByFrequency); boolean changed = false; // Rename the globals! for (Node n : globalNameNodes) { String newName = getNewGlobalName(n); // Note: if newName is null, then oldName is an extern. if (newName != null) { n.setString(newName); changed = true; } } // Rename the locals! int count = 0; for (Node n : localNameNodes) { String newName = getNewLocalName(n); if (newName != null) { n.setString(newName); changed = true; } count++; } if (changed) { compiler.reportCodeChange(); } // Lastly, write the name assignments to the debug log. compiler.addToDebugLog("JS var assignments:\n" + assignmentLog); assignmentLog = null; }
/** Converts extended object literal {a} to {a:a}. */ private void visitStringKey(Node n) { if (!n.hasChildren()) { Node name = IR.name(n.getString()); name.useSourceInfoIfMissingFrom(n); n.addChildToBack(name); compiler.reportCodeChange(); } }
/** Converts extended object literal {a} to {a:a}. */ private void visitStringKey(Node n) { if (!n.hasChildren()) { Node name = IR.name(n.getString()); name.copyInformationFrom(n); n.addChildToBack(name); compiler.reportCodeChange(); } }
@Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.THIS: parent.replaceChild(n, Node.newString(Token.NAME, "$$_self")); thisChanged = true; compiler.reportCodeChange(); } }
@Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { Traverser traverser = new Traverser(); NodeTraversal.traverseEs6(compiler, scriptRoot, traverser); if (traverser.changed) { compiler.needsEs6Runtime = true; compiler.reportCodeChange(); } }
/** * Processes array literals or calls containing spreads. Eg.: [1, 2, ...x, 4, 5] => [1, * 2].concat(x, [4, 5]); Eg.: f(...arr) => f.apply(null, arr) Eg.: new F(...args) => new * Function.prototype.bind.apply(F, [].concat(args)) */ private void visitArrayLitOrCallWithSpread(Node node, Node parent) { Preconditions.checkArgument(node.isCall() || node.isArrayLit() || node.isNew()); List<Node> groups = new ArrayList<>(); Node currGroup = null; Node callee = node.isArrayLit() ? null : node.removeFirstChild(); Node currElement = node.removeFirstChild(); while (currElement != null) { if (currElement.isSpread()) { if (currGroup != null) { groups.add(currGroup); currGroup = null; } groups.add(currElement.removeFirstChild()); } else { if (currGroup == null) { currGroup = IR.arraylit(); } currGroup.addChildToBack(currElement); } currElement = node.removeFirstChild(); } if (currGroup != null) { groups.add(currGroup); } Node result = null; Node joinedGroups = IR.call( IR.getprop(IR.arraylit(), IR.string("concat")), groups.toArray(new Node[groups.size()])); if (node.isArrayLit()) { result = joinedGroups; } else if (node.isCall()) { if (NodeUtil.mayHaveSideEffects(callee) && callee.isGetProp()) { Node statement = node; while (!NodeUtil.isStatement(statement)) { statement = statement.getParent(); } Node freshVar = IR.name(FRESH_SPREAD_VAR + freshSpreadVarCounter++); Node n = IR.var(freshVar.cloneTree()); n.useSourceInfoIfMissingFromForTree(statement); statement.getParent().addChildBefore(n, statement); callee.addChildToFront(IR.assign(freshVar.cloneTree(), callee.removeFirstChild())); result = IR.call(IR.getprop(callee, IR.string("apply")), freshVar, joinedGroups); } else { Node context = callee.isGetProp() ? callee.getFirstChild().cloneTree() : IR.nullNode(); result = IR.call(IR.getprop(callee, IR.string("apply")), context, joinedGroups); } } else { Node bindApply = NodeUtil.newQualifiedNameNode( compiler.getCodingConvention(), "Function.prototype.bind.apply"); result = IR.newNode(bindApply, callee, joinedGroups); } result.useSourceInfoIfMissingFromForTree(node); parent.replaceChild(node, result); compiler.reportCodeChange(); }
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(); }
/** Update all of the nodes with a reference to the corresponding alias node. */ public void doAlias(AbstractCompiler compiler) { if (isAliased) { for (Map.Entry<Node, Node> entry : nodes.entrySet()) { Node n = entry.getKey(); Node parent = entry.getValue(); aliasNode(n, parent); compiler.reportCodeChange(); } } }
/** Flattens a stub declaration. This is mostly a hack to support legacy users. */ private void flattenSimpleStubDeclaration(Name name, String alias) { Ref ref = Iterables.getOnlyElement(name.getRefs()); Node nameNode = NodeUtil.newName(compiler, alias, ref.node, name.getFullName()); Node varNode = IR.var(nameNode).useSourceInfoIfMissingFrom(nameNode); Preconditions.checkState(ref.node.getParent().isExprResult()); Node parent = ref.node.getParent(); Node grandparent = parent.getParent(); grandparent.replaceChild(parent, varNode); compiler.reportCodeChange(); }
@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 addExtern() { Node name = IR.name(PROTECTOR_FN); name.putBooleanProp(Node.IS_CONSTANT_NAME, true); Node var = IR.var(name); // Add "@noalias" so we can strip the method when AliasExternals is enabled. JSDocInfoBuilder builder = new JSDocInfoBuilder(false); builder.recordNoAlias(); var.setJSDocInfo(builder.build(var)); CompilerInput input = compiler.getSynthesizedExternsInput(); input.getAstRoot(compiler).addChildrenToBack(var); compiler.reportCodeChange(); }
/** * Inject alias nodes into the global scope. e.g. var * $$ALIAS_NULL=null,$$ALIAS_TRUE=true,$$ALIAS_FALSE=false;. */ private void addAliasNodes(Node codeRoot) { boolean codeChanged = false; for (AliasSpecification spec : aliasSpecifications) { if (spec.maybeInsertAliasDeclarationIntoParseTree(codeRoot)) { codeChanged = true; } } if (codeChanged) { compiler.reportCodeChange(); } }
/** 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. }
/** * 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; }
private void varify() { if (!blockScopedDeclarations.isEmpty()) { for (Node n : blockScopedDeclarations) { if (n.isConst()) { JSDocInfoBuilder builder = JSDocInfoBuilder.maybeCopyFrom(n.getJSDocInfo()); builder.recordConstancy(); JSDocInfo info = builder.build(); n.setJSDocInfo(info); } n.setType(Token.VAR); } compiler.reportCodeChange(); } }
private void visitForOf(Node node, Node parent) { Node variable = node.removeFirstChild(); Node iterable = node.removeFirstChild(); Node body = node.removeFirstChild(); Node iterName = IR.name(ITER_BASE + compiler.getUniqueNameIdSupplier().get()); Node getNext = IR.call(IR.getprop(iterName.cloneTree(), IR.string("next"))); String variableName; int declType; if (variable.isName()) { declType = Token.NAME; variableName = variable.getQualifiedName(); } else { Preconditions.checkState( NodeUtil.isNameDeclaration(variable), "Expected var, let, or const. Got %s", variable); declType = variable.getType(); variableName = variable.getFirstChild().getQualifiedName(); } Node iterResult = IR.name(ITER_RESULT + variableName); Node makeIter = IR.call(NodeUtil.newQName(compiler, MAKE_ITER), iterable); compiler.needsEs6Runtime = true; Node init = IR.var(iterName.cloneTree(), makeIter); Node initIterResult = iterResult.cloneTree(); initIterResult.addChildToFront(getNext.cloneTree()); init.addChildToBack(initIterResult); Node cond = IR.not(IR.getprop(iterResult.cloneTree(), IR.string("done"))); Node incr = IR.assign(iterResult.cloneTree(), getNext.cloneTree()); Node declarationOrAssign; if (declType == Token.NAME) { declarationOrAssign = IR.exprResult( IR.assign( IR.name(variableName), IR.getprop(iterResult.cloneTree(), IR.string("value")))); } else { declarationOrAssign = new Node(declType, IR.name(variableName)); declarationOrAssign .getFirstChild() .addChildToBack(IR.getprop(iterResult.cloneTree(), IR.string("value"))); } body.addChildToFront(declarationOrAssign); Node newFor = IR.forNode(init, cond, incr, body); newFor.useSourceInfoIfMissingFromForTree(node); parent.replaceChild(node, newFor); compiler.reportCodeChange(); }
/** * Protect side-effect free nodes by making them parameters to a extern function call. This call * will be removed after all the optimizations passes have run. */ private void protectSideEffects() { if (!problemNodes.isEmpty()) { addExtern(); for (Node n : problemNodes) { Node name = IR.name(PROTECTOR_FN).srcref(n); name.putBooleanProp(Node.IS_CONSTANT_NAME, true); Node replacement = IR.call(name).srcref(n); replacement.putBooleanProp(Node.FREE_CALL, true); n.getParent().replaceChild(n, replacement); replacement.addChildToBack(n); } compiler.reportCodeChange(); } }