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(); } }
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 addParamsToMethod( Set<String> closures, Node callNode, boolean thisChanged, String thisParam) { for (String closure : closures) { Node paramNode = Node.newString(Token.NAME, closure); callNode.addChildrenToBack(paramNode); } if (thisChanged) { callNode.addChildrenToBack(Node.newString(Token.NAME, thisParam)); } }
public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() != Token.FUNCTION) { return; } int id = functionNames.getFunctionId(n); if (id < 0) { // Function node was added during compilation; don't instrument. return; } if (reportFunctionName.length() != 0) { Node body = n.getFirstChild().getNext().getNext(); Node call = new Node( Token.CALL, Node.newString(Token.NAME, reportFunctionName), Node.newNumber(id)); Node expr = new Node(Token.EXPR_RESULT, call); body.addChildToFront(expr); compiler.reportCodeChange(); } if (reportFunctionExitName.length() != 0) { Node body = n.getFirstChild().getNext().getNext(); (new InstrumentReturns(id)).process(body); } if (definedFunctionName.length() != 0) { Node call = new Node( Token.CALL, Node.newString(Token.NAME, definedFunctionName), Node.newNumber(id)); Node expr = NodeUtil.newExpr(call); Node addingRoot = null; if (NodeUtil.isFunctionDeclaration(n)) { JSModule module = t.getModule(); addingRoot = compiler.getNodeForCodeInsertion(module); addingRoot.addChildToFront(expr); } else { Node beforeChild = n; for (Node ancestor : n.getAncestors()) { int type = ancestor.getType(); if (type == Token.BLOCK || type == Token.SCRIPT) { addingRoot = ancestor; break; } beforeChild = ancestor; } addingRoot.addChildBefore(expr, beforeChild); } compiler.reportCodeChange(); } }
/** * Notice that "call" and "bind" have the same argument signature, except that all the arguments * of "bind" (except the first) are optional. */ private FunctionType getCallOrBindSignature(boolean isCall) { boolean isBind = !isCall; FunctionBuilder builder = new FunctionBuilder(registry) .withReturnType(isCall ? getReturnType() : getBindReturnType(-1)) .withTemplateKeys(getTemplateTypeMap().getTemplateKeys()); Node origParams = getParametersNode(); if (origParams != null) { Node params = origParams.cloneTree(); Node thisTypeNode = Node.newString(Token.NAME, "thisType"); thisTypeNode.setJSType(registry.createOptionalNullableType(getTypeOfThis())); params.addChildToFront(thisTypeNode); if (isBind) { // The arguments of bind() are unique in that they are all // optional but not undefinable. for (Node current = thisTypeNode.getNext(); current != null; current = current.getNext()) { current.setOptionalArg(true); } } else if (isCall) { // The first argument of call() is optional iff all the arguments // are optional. It's sufficient to check the first argument. Node firstArg = thisTypeNode.getNext(); if (firstArg == null || firstArg.isOptionalArg() || firstArg.isVarArgs()) { thisTypeNode.setOptionalArg(true); } } builder.withParamsNode(params); } return builder.build(); }
@SuppressWarnings("unchecked") public void testParse() { Node a = Node.newString(Token.NAME, "a"); a.addChildToFront(Node.newString(Token.NAME, "b")); List<ParserResult> testCases = ImmutableList.of( new ParserResult("3;", createScript(new Node(Token.EXPR_RESULT, Node.newNumber(3.0)))), new ParserResult("var a = b;", createScript(new Node(Token.VAR, a))), new ParserResult( "\"hell\\\no\\ world\\\n\\\n!\"", createScript( new Node(Token.EXPR_RESULT, Node.newString(Token.STRING, "hello world!"))))); for (ParserResult testCase : testCases) { assertNodeEquality(testCase.node, parse(testCase.code)); } }
@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(); } }
/** Infer the parameter types from the doc info alone. */ FunctionTypeBuilder inferParameterTypes(JSDocInfo info) { // Create a fake args parent. Node lp = new Node(Token.LP); for (String name : info.getParameterNames()) { lp.addChildToBack(Node.newString(Token.NAME, name)); } return inferParameterTypes(lp, info); }
public void testPreserveAnnotatedName() { Node root = new Node(Token.SCRIPT); Node name = Node.newString("foo"); name.putProp(Node.ORIGINALNAME_PROP, "bar"); root.addChildToBack(name); NodeTraversal.traverseEs6(new Compiler(), root, new SourceInformationAnnotator("", false)); assertEquals(name.getProp(Node.ORIGINALNAME_PROP), "bar"); }
@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 } } } }
@Override public void process(Node externs, Node root) { FindExportableNodes findExportableNodes = new FindExportableNodes(compiler); NodeTraversal.traverse(compiler, root, findExportableNodes); Map<String, GenerateNodeContext> exports = findExportableNodes.getExports(); for (Map.Entry<String, GenerateNodeContext> entry : exports.entrySet()) { String export = entry.getKey(); GenerateNodeContext context = entry.getValue(); // 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.getType() == Token.GETPROP) { parent = node.getFirstChild().getQualifiedName(); if (node.getFirstChild().getType() == Token.GETPROP && getPropertyName(node.getFirstChild()).equals(PROTOTYPE_PROPERTY)) { grandparent = node.getFirstChild().getFirstChild().getQualifiedName(); } } boolean useExportSymbol = true; if (grandparent != null && exports.containsKey(grandparent)) { useExportSymbol = false; } else if (parent != null && exports.containsKey(parent)) { useExportSymbol = false; } Node call; if (useExportSymbol) { // exportSymbol(publicPath, object); call = new Node( Token.CALL, NodeUtil.newQualifiedNameNode(exportSymbolFunction, context.getNode(), export)); call.addChildToBack(Node.newString(export)); call.addChildToBack(NodeUtil.newQualifiedNameNode(export, context.getNode(), export)); } else { // exportProperty(object, publicName, symbol); String property = getPropertyName(node); call = new Node( Token.CALL, new Node[] { NodeUtil.newQualifiedNameNode( exportPropertyFunction, context.getNode(), exportPropertyFunction), NodeUtil.newQualifiedNameNode(parent, context.getNode(), exportPropertyFunction), Node.newString(property), NodeUtil.newQualifiedNameNode(export, context.getNode(), exportPropertyFunction) }); } Node expression = new Node(Token.EXPR_RESULT, call); annotate(expression); // It's important that any class-building calls (goog.inherits) // come right after the class definition, so move the export after that. Node insertionPoint = context.getContextNode().getNext(); CodingConvention convention = compiler.getCodingConvention(); while (insertionPoint != null && NodeUtil.isExprCall(insertionPoint) && convention.getClassesDefinedByCall(insertionPoint.getFirstChild()) != null) { insertionPoint = insertionPoint.getNext(); } if (insertionPoint == null) { context.getScriptNode().addChildToBack(expression); } else { context.getScriptNode().addChildBefore(expression, insertionPoint); } 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; }
private Node newReportFunctionExitNode() { return new Node( Token.CALL, Node.newString(Token.NAME, reportFunctionExitName), Node.newNumber(functionId)); }