/**
  * Removes all CALL nodes in the given TweakInfos, replacing calls to getter functions with the
  * tweak's default value.
  */
 private boolean stripAllCalls(Map<String, TweakInfo> tweakInfos) {
   for (TweakInfo tweakInfo : tweakInfos.values()) {
     boolean isRegistered = tweakInfo.isRegistered();
     for (TweakFunctionCall functionCall : tweakInfo.functionCalls) {
       Node callNode = functionCall.callNode;
       Node parent = callNode.getParent();
       if (functionCall.tweakFunc.isGetterFunction()) {
         Node newValue;
         if (isRegistered) {
           newValue = tweakInfo.getDefaultValueNode().cloneNode();
         } else {
           // When we find a getter of an unregistered tweak, there has
           // already been a warning about it, so now just use a default
           // value when stripping.
           TweakFunction registerFunction = functionCall.tweakFunc.registerFunction;
           newValue = registerFunction.createDefaultValueNode();
         }
         parent.replaceChild(callNode, newValue);
       } else {
         Node voidZeroNode = IR.voidNode(IR.number(0).srcref(callNode)).srcref(callNode);
         parent.replaceChild(callNode, voidZeroNode);
       }
     }
   }
   return !tweakInfos.isEmpty();
 }
    /** Replace the current assign with its right hand side. */
    void remove() {
      Node parent = assignNode.getParent();
      if (mayHaveSecondarySideEffects) {
        Node replacement = assignNode.getLastChild().detachFromParent();

        // Aggregate any expressions in GETELEMs.
        for (Node current = assignNode.getFirstChild();
            current.getType() != Token.NAME;
            current = current.getFirstChild()) {
          if (current.getType() == Token.GETELEM) {
            replacement =
                new Node(Token.COMMA, current.getLastChild().detachFromParent(), replacement);
            replacement.copyInformationFrom(current);
          }
        }

        parent.replaceChild(assignNode, replacement);
      } else {
        Node gramps = parent.getParent();
        if (parent.getType() == Token.EXPR_RESULT) {
          gramps.removeChild(parent);
        } else {
          parent.replaceChild(assignNode, assignNode.getLastChild().detachFromParent());
        }
      }
    }
  /** 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.
  }
 @Override
 public void visit(NodeTraversal t, Node n, Node parent) {
   if (n.isThis()) {
     Node name = IR.name(THIS_VAR).srcref(n);
     parent.replaceChild(n, name);
     changedThis = true;
   } else if (n.isName() && n.getString().equals("arguments")) {
     Node name = IR.name(ARGUMENTS_VAR).srcref(n);
     parent.replaceChild(n, name);
     changedArguments = true;
   }
 }
  /**
   * Expressions such as [foo() + 'a' + 'b'] generate parse trees where no node has two const
   * children ((foo() + 'a') + 'b'), so tryFoldAdd() won't fold it -- tryFoldLeftChildAdd() will
   * (for Strings). Specifically, it folds Add expressions where: - The left child is also and add
   * expression - The right child is a constant value - The left child's right child is a STRING
   * constant.
   */
  private Node tryFoldChildAddString(Node n, Node left, Node right) {

    if (NodeUtil.isLiteralValue(right, false) && left.isAdd()) {

      Node ll = left.getFirstChild();
      Node lr = ll.getNext();

      // Left's right child MUST be a string. We would not want to fold
      // foo() + 2 + 'a' because we don't know what foo() will return, and
      // therefore we don't know if left is a string concat, or a numeric add.
      if (lr.isString()) {
        String leftString = NodeUtil.getStringValue(lr);
        String rightString = NodeUtil.getStringValue(right);
        if (leftString != null && rightString != null) {
          left.removeChild(ll);
          String result = leftString + rightString;
          n.replaceChild(left, ll);
          n.replaceChild(right, IR.string(result));
          reportCodeChange();
          return n;
        }
      }
    }

    if (NodeUtil.isLiteralValue(left, false) && right.isAdd()) {

      Node rl = right.getFirstChild();
      Node rr = right.getLastChild();

      // Left's right child MUST be a string. We would not want to fold
      // foo() + 2 + 'a' because we don't know what foo() will return, and
      // therefore we don't know if left is a string concat, or a numeric add.
      if (rl.isString()) {
        String leftString = NodeUtil.getStringValue(left);
        String rightString = NodeUtil.getStringValue(rl);
        if (leftString != null && rightString != null) {
          right.removeChild(rr);
          String result = leftString + rightString;
          n.replaceChild(right, rr);
          n.replaceChild(left, IR.string(result));
          reportCodeChange();
          return n;
        }
      }
    }

    return n;
  }
    /**
     * Rename or remove labels.
     *
     * @param node The label node.
     * @param parent The parent of the label node.
     */
    private void visitLabel(Node node, Node parent) {
      Node nameNode = node.getFirstChild();
      Preconditions.checkState(nameNode != null);
      String name = nameNode.getString();
      LabelInfo li = getLabelInfo(name);
      // This is a label...
      if (li.referenced || !removeUnused) {
        String newName = getNameForId(li.id);
        if (!name.equals(newName)) {
          // ... and it is used, give it the short name.
          nameNode.setString(newName);
          compiler.reportCodeChange();
        }
      } else {
        // ... and it is not referenced, just remove it.
        Node newChild = node.getLastChild();
        node.removeChild(newChild);
        parent.replaceChild(node, newChild);
        if (newChild.isBlock()) {
          NodeUtil.tryMergeBlock(newChild);
        }
        compiler.reportCodeChange();
      }

      // Remove the label from the current stack of labels.
      namespaceStack.peek().renameMap.remove(name);
    }
  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();
  }
  /**
   * 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();
  }
  /**
   * Inline a function that fulfills the requirements of canInlineReferenceDirectly into the call
   * site, replacing only the CALL node.
   */
  private Node inlineReturnValue(Node callNode, Node fnNode) {
    Node block = fnNode.getLastChild();
    Node callParentNode = callNode.getParent();

    // NOTE: As the normalize pass guarantees globals aren't being
    // shadowed and an expression can't introduce new names, there is
    // no need to check for conflicts.

    // Create an argName -> expression map, checking for side effects.
    Map<String, Node> argMap =
        FunctionArgumentInjector.getFunctionCallParameterMap(
            fnNode, callNode, this.safeNameIdSupplier);

    Node newExpression;
    if (!block.hasChildren()) {
      Node srcLocation = block;
      newExpression = NodeUtil.newUndefinedNode(srcLocation);
    } else {
      Node returnNode = block.getFirstChild();
      Preconditions.checkArgument(returnNode.getType() == Token.RETURN);

      // Clone the return node first.
      Node safeReturnNode = returnNode.cloneTree();
      Node inlineResult = FunctionArgumentInjector.inject(safeReturnNode, null, argMap);
      Preconditions.checkArgument(safeReturnNode == inlineResult);
      newExpression = safeReturnNode.removeFirstChild();
    }

    callParentNode.replaceChild(callNode, newExpression);
    return newExpression;
  }
 private Node tryReduceVoid(Node n) {
   Node child = n.getFirstChild();
   if ((!child.isNumber() || child.getDouble() != 0.0) && !mayHaveSideEffects(n)) {
     n.replaceChild(child, IR.number(0));
     reportCodeChange();
   }
   return n;
 }
 void replaceNodeInPlace(Node n, Node replacement) {
   Node parent = n.getParent();
   if (n.hasChildren()) {
     Node children = n.removeChildren();
     replacement.addChildrenToFront(children);
   }
   parent.replaceChild(n, replacement);
 }
Example #12
0
 @Override
 public void visit(NodeTraversal t, Node n, Node parent) {
   if (n.isThis()) {
     Node name = IR.name(THIS_VAR).srcref(n);
     parent.replaceChild(n, name);
     changed = true;
   }
 }
Example #13
0
 /**
  * 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();
 }
Example #14
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();
   }
 }
  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();
  }
 @Override
 protected void aliasNode(Node throwNode, Node parent) {
   Node name =
       NodeUtil.newName(
           compiler.getCodingConvention(), getAliasName(), throwNode, getAliasName());
   Node aliasCall = IR.call(name, throwNode.removeFirstChild());
   aliasCall.putBooleanProp(Node.FREE_CALL, true);
   Node exprResult = IR.exprResult(aliasCall);
   parent.replaceChild(throwNode, exprResult);
 }
  /** 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();
  }
Example #18
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
          }
        }
    }
  }
Example #19
0
 @Override
 public void visit(NodeTraversal t, Node n, Node parent) {
   if (n.isCall()) {
     Node target = n.getFirstChild();
     // TODO(johnlenz): add this to the coding convention
     // so we can remove goog.reflect.sinkValue as well.
     if (target.isName() && target.getString().equals(PROTECTOR_FN)) {
       Node expr = n.getLastChild();
       n.detachChildren();
       parent.replaceChild(n, expr);
     }
   }
 }
  /**
   * 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;
  }
  /** Remove useless calls: Object.defineProperties(o, {}) -> o */
  private Node tryFoldCall(Node n) {
    Preconditions.checkArgument(n.isCall());

    if (NodeUtil.isObjectDefinePropertiesDefinition(n)) {
      Node srcObj = n.getLastChild();
      if (srcObj.isObjectLit() && !srcObj.hasChildren()) {
        Node parent = n.getParent();
        Node destObj = n.getSecondChild().detachFromParent();
        parent.replaceChild(n, destObj);
        reportCodeChange();
      }
    }
    return n;
  }
  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();
  }
  /**
   * Expressions such as [foo() * 10 * 20] generate parse trees where no node has two const children
   * ((foo() * 10) * 20), so performArithmeticOp() won't fold it -- tryFoldLeftChildOp() will.
   * Specifically, it folds associative expressions where: - The left child is also an associative
   * expression of the same time. - The right child is a constant NUMBER constant. - The left
   * child's right child is a NUMBER constant.
   */
  private Node tryFoldLeftChildOp(Node n, Node left, Node right) {
    Token opType = n.getType();
    Preconditions.checkState(
        (NodeUtil.isAssociative(opType) && NodeUtil.isCommutative(opType)) || n.isAdd());

    Preconditions.checkState(!n.isAdd() || !NodeUtil.mayBeString(n, shouldUseTypes));

    // Use getNumberValue to handle constants like "NaN" and "Infinity"
    // other values are converted to numbers elsewhere.
    Double rightValObj = NodeUtil.getNumberValue(right, shouldUseTypes);
    if (rightValObj != null && left.getType() == opType) {
      Preconditions.checkState(left.getChildCount() == 2);

      Node ll = left.getFirstChild();
      Node lr = ll.getNext();

      Node valueToCombine = ll;
      Node replacement = performArithmeticOp(opType, valueToCombine, right);
      if (replacement == null) {
        valueToCombine = lr;
        replacement = performArithmeticOp(opType, valueToCombine, right);
      }
      if (replacement != null) {
        // Remove the child that has been combined
        left.removeChild(valueToCombine);
        // Replace the left op with the remaining child.
        n.replaceChild(left, left.removeFirstChild());
        // New "-Infinity" node need location info explicitly
        // added.
        replacement.useSourceInfoIfMissingFromForTree(right);
        n.replaceChild(right, replacement);
        reportCodeChange();
      }
    }

    return n;
  }
  /** Collapse VARs and EXPR_RESULT node into FOR loop initializers where possible. */
  private void maybeCollapseIntoForStatements(Node n, Node parent) {
    // Only SCRIPT, BLOCK, and LABELs can have FORs that can be collapsed into.
    // LABELs are not supported here.
    if (parent == null || !NodeUtil.isStatementBlock(parent)) {
      return;
    }

    // Is the current node something that can be in a for loop initializer?
    if (!NodeUtil.isExpressionNode(n) && !NodeUtil.isVar(n)) {
      return;
    }

    // Is the next statement a valid FOR?
    Node nextSibling = n.getNext();
    if (nextSibling != null
        && nextSibling.getType() == Token.FOR
        && !NodeUtil.isForIn(nextSibling)
        && nextSibling.getFirstChild().getType() == Token.EMPTY) {

      // Does the current node contain an in operator?  If so, embedding
      // the expression in a for loop can cause some Javascript parsers (such
      // as the Playstation 3's browser based on Access's NetFront
      // browser) to fail to parse the code.
      // See bug 1778863 for details.
      if (NodeUtil.containsType(n, Token.IN)) {
        return;
      }

      // Move the current node into the FOR loop initializer.
      Node forNode = nextSibling;
      Node oldInitializer = forNode.getFirstChild();
      parent.removeChild(n);

      Node newInitializer;
      if (NodeUtil.isVar(n)) {
        newInitializer = n;
      } else {
        // Extract the expression from EXPR_RESULT node.
        Preconditions.checkState(n.hasOneChild());
        newInitializer = n.getFirstChild();
        n.removeChild(newInitializer);
      }

      forNode.replaceChild(oldInitializer, newInitializer);

      compiler.reportCodeChange();
    }
  }
Example #25
0
 /** 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();
       }
     }
   }
 }
  /** Try to fold a AND/OR node. */
  private Node tryFoldAndOr(Node n, Node left, Node right) {
    Node parent = n.getParent();

    Node result = null;

    Token type = n.getType();

    TernaryValue leftVal = NodeUtil.getImpureBooleanValue(left);

    if (leftVal != TernaryValue.UNKNOWN) {
      boolean lval = leftVal.toBoolean(true);

      // (TRUE || x) => TRUE (also, (3 || x) => 3)
      // (FALSE && x) => FALSE
      if (lval && type == Token.OR || !lval && type == Token.AND) {
        result = left;

      } else if (!mayHaveSideEffects(left)) {
        // (FALSE || x) => x
        // (TRUE && x) => x
        result = right;
      } else {
        // Left side may have side effects, but we know its boolean value.
        // e.g. true_with_sideeffects || foo() => true_with_sideeffects, foo()
        // or: false_with_sideeffects && foo() => false_with_sideeffects, foo()
        // This, combined with PeepholeRemoveDeadCode, helps reduce expressions
        // like "x() || false || z()".
        n.detachChildren();
        result = IR.comma(left, right);
      }
    }

    // Note: Right hand side folding is handled by
    // PeepholeMinimizeConditions#tryMinimizeCondition

    if (result != null) {
      // Fold it!
      n.detachChildren();
      parent.replaceChild(n, result);
      reportCodeChange();

      return result;
    } else {
      return n;
    }
  }
  private void maybeReplaceJqueryPrototypeAlias(Node n) {
    // Check to see if this is the assignment of the original alias.
    // If so, leave it intact.
    if (NodeUtil.isLValue(n)) {
      Node maybeAssign = n.getParent();
      while (!NodeUtil.isStatement(maybeAssign) && !maybeAssign.isAssign()) {
        maybeAssign = maybeAssign.getParent();
      }

      if (maybeAssign.isAssign()) {
        maybeAssign = maybeAssign.getParent();
        if (maybeAssign.isBlock() || maybeAssign.isScript() || NodeUtil.isStatement(maybeAssign)) {
          return;
        }
      }
    }

    Node fn = n.getLastChild();
    if (fn != null) {
      n.replaceChild(fn, IR.string("prototype").srcref(fn));
      compiler.reportCodeChange();
    }
  }
  private Node tryFoldInForcedStringContext(Node n) {
    // For now, we only know how to fold ctors.
    Preconditions.checkArgument(n.isNew());

    Node objectType = n.getFirstChild();
    if (!objectType.isName()) {
      return n;
    }

    if (objectType.getString().equals("String")) {
      Node value = objectType.getNext();
      String stringValue = null;
      if (value == null) {
        stringValue = "";
      } else {
        if (!NodeUtil.isImmutableValue(value)) {
          return n;
        }

        stringValue = NodeUtil.getStringValue(value);
      }

      if (stringValue == null) {
        return n;
      }

      Node parent = n.getParent();
      Node newString = IR.string(stringValue);

      parent.replaceChild(n, newString);
      newString.useSourceInfoIfMissingFrom(parent);
      reportCodeChange();

      return newString;
    }
    return n;
  }
  private void replaceOriginalJqueryEachCall(Node n, Node expandedBlock) {
    // Check to see if the return value of the original jQuery.expandedEach
    // call is used. If so, we need to wrap each loop expansion in an anonymous
    // function and return the original objectToLoopOver.
    if (n.getParent().isExprResult()) {
      Node parent = n.getParent();
      Node grandparent = parent.getParent();
      Node insertAfter = parent;
      while (expandedBlock.hasChildren()) {
        Node child = expandedBlock.getFirstChild().detachFromParent();
        grandparent.addChildAfter(child, insertAfter);
        insertAfter = child;
      }
      grandparent.removeChild(parent);
    } else {
      // Return the original object
      Node callTarget = n.getFirstChild();
      Node objectToLoopOver = callTarget.getNext();

      objectToLoopOver.detachFromParent();
      Node ret = IR.returnNode(objectToLoopOver).srcref(callTarget);
      expandedBlock.addChildToBack(ret);

      // Wrap all of the expanded loop calls in a new anonymous function
      Node fnc =
          IR.function(
              IR.name("").srcref(callTarget), IR.paramList().srcref(callTarget), expandedBlock);
      n.replaceChild(callTarget, fnc);
      n.putBooleanProp(Node.FREE_CALL, true);

      // remove any other pre-existing call arguments
      while (fnc.getNext() != null) {
        n.removeChildAfter(fnc);
      }
    }
    compiler.reportCodeChange();
  }
 @Override
 protected void aliasNode(Node n, Node parent) {
   Node aliasNode =
       NodeUtil.newName(compiler.getCodingConvention(), getAliasName(), n, getAliasName());
   parent.replaceChild(n, aliasNode);
 }