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();
    }
  }
Example #2
0
  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;
  }
Example #3
0
 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));
    }
  }
Example #7
0
 @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");
  }
Example #10
0
  @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));
 }