Java Code Examples for com.google.javascript.rhino.Node#addChildBefore()

The following examples show how to use com.google.javascript.rhino.Node#addChildBefore() . You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example 1
Source File: ExpressionDecomposer.java    From astor with GNU General Public License v2.0 6 votes vote down vote up
/**
 * Extract the specified expression from its parent expression.
 * @see #canExposeExpression
 */
void moveExpression(Node expression) {
  String resultName = getResultValueName();
  Node injectionPoint = findInjectionPoint(expression);
  Preconditions.checkNotNull(injectionPoint);
  Node injectionPointParent = injectionPoint.getParent();
  Preconditions.checkNotNull(injectionPointParent);
  Preconditions.checkState(NodeUtil.isStatementBlock(injectionPointParent));

  // Replace the expression with a reference to the new name.
  Node expressionParent = expression.getParent();
  expressionParent.replaceChild(
      expression, IR.name(resultName));

  // Re-add the expression at the appropriate place.
  Node newExpressionRoot = NodeUtil.newVarNode(resultName, expression);
  injectionPointParent.addChildBefore(newExpressionRoot, injectionPoint);
  compiler.reportCodeChange();
}
 
Example 2
Source File: Normalize.java    From astor with GNU General Public License v2.0 6 votes vote down vote up
/**
 * Split a var node such as:
 *   var a, b;
 * into individual statements:
 *   var a;
 *   var b;
 * @param n The whose children we should inspect.
 */
private void splitVarDeclarations(Node n) {
  for (Node next, c = n.getFirstChild(); c != null; c = next) {
    next = c.getNext();
    if (c.isVar()) {
      if (assertOnChange && !c.hasChildren()) {
        throw new IllegalStateException("Empty VAR node.");
      }

      while (c.getFirstChild() != c.getLastChild()) {
        Node name = c.getFirstChild();
        c.removeChild(name);
        Node newVar = IR.var(name).srcref(n);
        n.addChildBefore(newVar, c);
        reportCodeChange("VAR with multiple children");
      }
    }
  }
}
 
Example 3
Source File: Closure_102_Normalize_s.java    From coming with MIT License 6 votes vote down vote up
/**
 * Split a var node such as:
 *   var a, b;
 * into individual statements:
 *   var a;
 *   var b;
 * @param n The whose children we should inspect.
 */
private void splitVarDeclarations(Node n) {
  for (Node next, c = n.getFirstChild(); c != null; c = next) {
    next = c.getNext();
    if (c.getType() == Token.VAR) {
      if (assertOnChange && !c.hasChildren()) {
        throw new IllegalStateException("Empty VAR node.");
      }

      while (c.getFirstChild() != c.getLastChild()) {
        Node name = c.getFirstChild();
        c.removeChild(name);
        Node newVar = new Node(Token.VAR, name, n.getLineno(), n.getCharno());
        n.addChildBefore(newVar, c);
        reportCodeChange("VAR with multiple children");
      }
    }
  }
}
 
Example 4
Source File: FunctionToBlockMutator.java    From astor with GNU General Public License v2.0 5 votes vote down vote up
/**
 * Replace the 'return' statement with its child expression.
 *   "return foo()" becomes "{foo(); break;}" or
 *      "{resultName = foo(); break;}"
 *   "return" becomes {break;} or "{resultName = void 0;break;}".
 */
private static Node replaceReturnWithBreak(Node current, Node parent,
    String resultName, String labelName) {

  if (current.isFunction()
      || current.isExprResult()) {
    // Don't recurse into functions definitions, and expressions can't
    // contain RETURN nodes.
    return current;
  }

  if (current.isReturn()) {
    Preconditions.checkState(NodeUtil.isStatementBlock(parent));

    Node resultNode = getReplacementReturnStatement(current, resultName);
    Node breakNode = IR.breakNode(IR.labelName(labelName));

    // Replace the node in parent, and reset current to the first new child.
    breakNode.copyInformationFromForTree(current);
    parent.replaceChild(current, breakNode);
    if (resultNode != null) {
      resultNode.copyInformationFromForTree(current);
      parent.addChildBefore(resultNode, breakNode);
    }
    current = breakNode;
  } else {
    for (Node c = current.getFirstChild(); c != null; c = c.getNext()) {
      // c may be replaced.
      c = replaceReturnWithBreak(c, current, resultName, labelName);
    }
  }

  return current;
}
 
Example 5
Source File: Closure_102_Normalize_t.java    From coming with MIT License 5 votes vote down vote up
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
  switch (n.getType()) {
    case Token.WHILE:
      if (CONVERT_WHILE_TO_FOR) {
        Node expr = n.getFirstChild();
        n.setType(Token.FOR);
        n.addChildBefore(new Node(Token.EMPTY), expr);
        n.addChildAfter(new Node(Token.EMPTY), expr);
        reportCodeChange("WHILE node");
      }
      break;
  }
}
 
Example 6
Source File: Closure_79_Normalize_t.java    From coming with MIT License 5 votes vote down vote up
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
  switch (n.getType()) {
    case Token.WHILE:
      if (CONVERT_WHILE_TO_FOR) {
        Node expr = n.getFirstChild();
        n.setType(Token.FOR);
        Node empty = new Node(Token.EMPTY);
        empty.copyInformationFrom(n);
        n.addChildBefore(empty, expr);
        n.addChildAfter(empty.cloneNode(), expr);
        reportCodeChange("WHILE node");
      }
      break;

    case Token.FUNCTION:
      normalizeFunctionDeclaration(n);
      break;

    case Token.NAME:
    case Token.STRING:
    case Token.GET:
    case Token.SET:
      if (!compiler.getLifeCycleStage().isNormalizedObfuscated()) {
        annotateConstantsByConvention(n, parent);
      }
      break;
  }
}
 
Example 7
Source File: Closure_72_FunctionToBlockMutator_t.java    From coming with MIT License 5 votes vote down vote up
/**
 * Replace the 'return' statement with its child expression.
 *   "return foo()" becomes "{foo(); break;}" or
 *      "{resultName = foo(); break;}"
 *   "return" becomes {break;} or "{resultName = void 0;break;}".
 */
private static Node replaceReturnWithBreak(Node current, Node parent,
    String resultName, String labelName) {

  if (current.getType() == Token.FUNCTION
      || current.getType() == Token.EXPR_RESULT) {
    // Don't recurse into functions definitions, and expressions can't
    // contain RETURN nodes.
    return current;
  }

  if (current.getType() == Token.RETURN) {
    Preconditions.checkState(NodeUtil.isStatementBlock(parent));

    Node resultNode = getReplacementReturnStatement(current, resultName);
    Node name = Node.newString(Token.LABEL_NAME, labelName);
    Node breakNode = new Node(Token.BREAK, name);

    // Replace the node in parent, and reset current to the first new child.
    breakNode.copyInformationFromForTree(current);
    parent.replaceChild(current, breakNode);
    if (resultNode != null) {
      resultNode.copyInformationFromForTree(current);
      parent.addChildBefore(resultNode, breakNode);
    }
    current = breakNode;
  } else {
    for (Node c = current.getFirstChild(); c != null; c = c.getNext()) {
      // c may be replaced.
      c = replaceReturnWithBreak(c, current, resultName, labelName);
    }
  }

  return current;
}
 
Example 8
Source File: TypeConversionPass.java    From clutz with MIT License 5 votes vote down vote up
/** Adds a field node before the first method node in classMembers */
private void addFieldToClassMembers(Node classMembers, Node field) {
  for (Node n : classMembers.children()) {
    if (n.isMemberFunctionDef()) {
      classMembers.addChildBefore(field, n);
      return;
    }
  }
  classMembers.addChildToBack(field);
}
 
Example 9
Source File: Closure_72_FunctionToBlockMutator_s.java    From coming with MIT License 5 votes vote down vote up
/**
 * Replace the 'return' statement with its child expression.
 *   "return foo()" becomes "{foo(); break;}" or
 *      "{resultName = foo(); break;}"
 *   "return" becomes {break;} or "{resultName = void 0;break;}".
 */
private static Node replaceReturnWithBreak(Node current, Node parent,
    String resultName, String labelName) {

  if (current.getType() == Token.FUNCTION
      || current.getType() == Token.EXPR_RESULT) {
    // Don't recurse into functions definitions, and expressions can't
    // contain RETURN nodes.
    return current;
  }

  if (current.getType() == Token.RETURN) {
    Preconditions.checkState(NodeUtil.isStatementBlock(parent));

    Node resultNode = getReplacementReturnStatement(current, resultName);
    Node name = Node.newString(Token.LABEL_NAME, labelName);
    Node breakNode = new Node(Token.BREAK, name);

    // Replace the node in parent, and reset current to the first new child.
    breakNode.copyInformationFromForTree(current);
    parent.replaceChild(current, breakNode);
    if (resultNode != null) {
      resultNode.copyInformationFromForTree(current);
      parent.addChildBefore(resultNode, breakNode);
    }
    current = breakNode;
  } else {
    for (Node c = current.getFirstChild(); c != null; c = c.getNext()) {
      // c may be replaced.
      c = replaceReturnWithBreak(c, current, resultName, labelName);
    }
  }

  return current;
}
 
Example 10
Source File: Closure_89_CollapseProperties_t.java    From coming with MIT License 4 votes vote down vote up
/**
 * Declares global variables to serve as aliases for the values in an object
 * literal, optionally removing all of the object literal's keys and values.
 *
 * @param alias The object literal's flattened name (e.g. "a$b$c")
 * @param objlit The OBJLIT node
 * @param varNode The VAR node to which new global variables should be added
 *     as children
 * @param nameToAddAfter The child of {@code varNode} after which new
 *     variables should be added (may be null)
 * @param varParent {@code varNode}'s parent
 * @return The number of variables added
 */
private int declareVarsForObjLitValues(
    Name objlitName, String alias, Node objlit, Node varNode,
    Node nameToAddAfter, Node varParent) {
  int numVars = 0;
  int arbitraryNameCounter = 0;
  boolean discardKeys = !objlitName.shouldKeepKeys();

  for (Node key = objlit.getFirstChild(), nextKey; key != null;
       key = nextKey) {
    Node value = key.getFirstChild();
    nextKey = key.getNext();

    // We generate arbitrary names for keys that aren't valid JavaScript
    // identifiers, since those keys are never referenced. (If they were,
    // this object literal's child names wouldn't be collapsible.) The only
    // reason that we don't eliminate them entirely is the off chance that
    // their values are expressions that have side effects.
    boolean isJsIdentifier = key.getType() != Token.NUMBER &&
                             TokenStream.isJSIdentifier(key.getString());
    String propName = isJsIdentifier ?
        key.getString() : String.valueOf(++arbitraryNameCounter);
    String propAlias = appendPropForAlias(alias, propName);
    String qName = objlitName.fullName() + '.' + propName;

    Node refNode = null;
    if (discardKeys) {
      objlit.removeChild(key);
      value.detachFromParent();
    } else {
      // Substitute a reference for the value.
      refNode = Node.newString(Token.NAME, propAlias);
      if (key.getBooleanProp(Node.IS_CONSTANT_NAME)) {
        refNode.putBooleanProp(Node.IS_CONSTANT_NAME, true);
      }

      key.replaceChild(value, refNode);
    }

    // Declare the collapsed name as a variable with the original value.
    Node nameNode = Node.newString(Token.NAME, propAlias);
    nameNode.addChildToFront(value);
    if (key.getBooleanProp(Node.IS_CONSTANT_NAME)) {
      nameNode.putBooleanProp(Node.IS_CONSTANT_NAME, true);
    }
    Node newVar = new Node(Token.VAR, nameNode)
        .copyInformationFromForTree(key);
    if (nameToAddAfter != null) {
      varParent.addChildAfter(newVar, nameToAddAfter);
    } else {
      varParent.addChildBefore(newVar, varNode);
    }
    compiler.reportCodeChange();
    nameToAddAfter = newVar;

    if (isJsIdentifier) {
      // Update the global name's node ancestry if it hasn't already been
      // done. (Duplicate keys in an object literal can bring us here twice
      // for the same global name.)
      Name p = nameMap.get(qName);
      if (p != null) {
        if (!discardKeys) {
          Ref newAlias =
              p.declaration.cloneAndReclassify(Ref.Type.ALIASING_GET);
          newAlias.node = refNode;
          p.addRef(newAlias);
        }

        p.declaration.node = nameNode;

        if (value.getType() == Token.FUNCTION) {
          checkForHosedThisReferences(value, value.getJSDocInfo(), p);
        }
      }
    }

    numVars++;
  }
  return numVars;
}
 
Example 11
Source File: Closure_89_CollapseProperties_t.java    From coming with MIT License 4 votes vote down vote up
/**
 * Updates the initial assignment to a collapsible property at global scope
 * by changing it to a variable declaration (e.g. a.b = 1 -> var a$b = 1).
 * The property's value may either be a primitive or an object literal or
 * function whose properties aren't collapsible.
 *
 * @param alias The flattened property name (e.g. "a$b")
 * @param refName The name for the reference being updated.
 * @param ref An object containing information about the assignment getting
 *     updated
 */
private void updateSimpleDeclaration(String alias, Name refName, Ref ref) {
  Node rvalue = ref.node.getNext();
  Node parent = ref.node.getParent();
  Node gramps = parent.getParent();
  Node greatGramps = gramps.getParent();
  Node greatGreatGramps = greatGramps.getParent();

  if (rvalue != null && rvalue.getType() == Token.FUNCTION) {
    checkForHosedThisReferences(rvalue, refName.docInfo, refName);
  }

  // Create the new alias node.
  Node nameNode = NodeUtil.newName(
      compiler.getCodingConvention(), alias, gramps.getFirstChild(),
      refName.fullName());
  NodeUtil.copyNameAnnotations(ref.node.getLastChild(), nameNode);

  if (gramps.getType() == Token.EXPR_RESULT) {
    // BEFORE: a.b.c = ...;
    //   exprstmt
    //     assign
    //       getprop
    //         getprop
    //           name a
    //           string b
    //         string c
    //       NODE
    // AFTER: var a$b$c = ...;
    //   var
    //     name a$b$c
    //       NODE

    // Remove the rvalue (NODE).
    parent.removeChild(rvalue);
    nameNode.addChildToFront(rvalue);

    Node varNode = new Node(Token.VAR, nameNode);
    greatGramps.replaceChild(gramps, varNode);
  } else {
    // This must be a complex assignment.
    Preconditions.checkNotNull(ref.getTwin());

    // BEFORE:
    // ... (x.y = 3);
    //
    // AFTER:
    // var x$y;
    // ... (x$y = 3);

    Node current = gramps;
    Node currentParent = gramps.getParent();
    for (; currentParent.getType() != Token.SCRIPT &&
           currentParent.getType() != Token.BLOCK;
         current = currentParent,
         currentParent = currentParent.getParent()) {}

    // Create a stub variable declaration right
    // before the current statement.
    Node stubVar = new Node(Token.VAR, nameNode.cloneTree())
        .copyInformationFrom(nameNode);
    currentParent.addChildBefore(stubVar, current);

    parent.replaceChild(ref.node, nameNode);
  }

  compiler.reportCodeChange();
}
 
Example 12
Source File: CollapseProperties.java    From astor with GNU General Public License v2.0 4 votes vote down vote up
/**
 * Updates the initial assignment to a collapsible property at global scope
 * by changing it to a variable declaration (e.g. a.b = 1 -> var a$b = 1).
 * The property's value may either be a primitive or an object literal or
 * function whose properties aren't collapsible.
 *
 * @param alias The flattened property name (e.g. "a$b")
 * @param refName The name for the reference being updated.
 * @param ref An object containing information about the assignment getting
 *     updated
 */
private void updateSimpleDeclaration(String alias, Name refName, Ref ref) {
  Node rvalue = ref.node.getNext();
  Node parent = ref.node.getParent();
  Node gramps = parent.getParent();
  Node greatGramps = gramps.getParent();
  Node greatGreatGramps = greatGramps.getParent();

  if (rvalue != null && rvalue.isFunction()) {
    checkForHosedThisReferences(rvalue, refName.docInfo, refName);
  }

  // Create the new alias node.
  Node nameNode = NodeUtil.newName(
      compiler.getCodingConvention(), alias, gramps.getFirstChild(),
      refName.getFullName());
  NodeUtil.copyNameAnnotations(ref.node.getLastChild(), nameNode);

  if (gramps.isExprResult()) {
    // BEFORE: a.b.c = ...;
    //   exprstmt
    //     assign
    //       getprop
    //         getprop
    //           name a
    //           string b
    //         string c
    //       NODE
    // AFTER: var a$b$c = ...;
    //   var
    //     name a$b$c
    //       NODE

    // Remove the r-value (NODE).
    parent.removeChild(rvalue);
    nameNode.addChildToFront(rvalue);

    Node varNode = IR.var(nameNode);
    greatGramps.replaceChild(gramps, varNode);
  } else {
    // This must be a complex assignment.
    Preconditions.checkNotNull(ref.getTwin());

    // BEFORE:
    // ... (x.y = 3);
    //
    // AFTER:
    // var x$y;
    // ... (x$y = 3);

    Node current = gramps;
    Node currentParent = gramps.getParent();
    for (; !currentParent.isScript() &&
           !currentParent.isBlock();
         current = currentParent,
         currentParent = currentParent.getParent()) {}

    // Create a stub variable declaration right
    // before the current statement.
    Node stubVar = IR.var(nameNode.cloneTree())
        .copyInformationFrom(nameNode);
    currentParent.addChildBefore(stubVar, current);

    parent.replaceChild(ref.node, nameNode);
  }

  compiler.reportCodeChange();
}
 
Example 13
Source File: Closure_89_CollapseProperties_s.java    From coming with MIT License 4 votes vote down vote up
/**
 * Updates the initial assignment to a collapsible property at global scope
 * by changing it to a variable declaration (e.g. a.b = 1 -> var a$b = 1).
 * The property's value may either be a primitive or an object literal or
 * function whose properties aren't collapsible.
 *
 * @param alias The flattened property name (e.g. "a$b")
 * @param refName The name for the reference being updated.
 * @param ref An object containing information about the assignment getting
 *     updated
 */
private void updateSimpleDeclaration(String alias, Name refName, Ref ref) {
  Node rvalue = ref.node.getNext();
  Node parent = ref.node.getParent();
  Node gramps = parent.getParent();
  Node greatGramps = gramps.getParent();
  Node greatGreatGramps = greatGramps.getParent();


  // Create the new alias node.
  Node nameNode = NodeUtil.newName(
      compiler.getCodingConvention(), alias, gramps.getFirstChild(),
      refName.fullName());
  NodeUtil.copyNameAnnotations(ref.node.getLastChild(), nameNode);

  if (gramps.getType() == Token.EXPR_RESULT) {
    // BEFORE: a.b.c = ...;
    //   exprstmt
    //     assign
    //       getprop
    //         getprop
    //           name a
    //           string b
    //         string c
    //       NODE
    // AFTER: var a$b$c = ...;
    //   var
    //     name a$b$c
    //       NODE

    // Remove the rvalue (NODE).
    parent.removeChild(rvalue);
    nameNode.addChildToFront(rvalue);

    Node varNode = new Node(Token.VAR, nameNode);
    greatGramps.replaceChild(gramps, varNode);
  } else {
    // This must be a complex assignment.
    Preconditions.checkNotNull(ref.getTwin());

    // BEFORE:
    // ... (x.y = 3);
    //
    // AFTER:
    // var x$y;
    // ... (x$y = 3);

    Node current = gramps;
    Node currentParent = gramps.getParent();
    for (; currentParent.getType() != Token.SCRIPT &&
           currentParent.getType() != Token.BLOCK;
         current = currentParent,
         currentParent = currentParent.getParent()) {}

    // Create a stub variable declaration right
    // before the current statement.
    Node stubVar = new Node(Token.VAR, nameNode.cloneTree())
        .copyInformationFrom(nameNode);
    currentParent.addChildBefore(stubVar, current);

    parent.replaceChild(ref.node, nameNode);
  }

  compiler.reportCodeChange();
}
 
Example 14
Source File: Closure_130_CollapseProperties_t.java    From coming with MIT License 4 votes vote down vote up
/**
 * Declares global variables to serve as aliases for the values in an object
 * literal, optionally removing all of the object literal's keys and values.
 *
 * @param alias The object literal's flattened name (e.g. "a$b$c")
 * @param objlit The OBJLIT node
 * @param varNode The VAR node to which new global variables should be added
 *     as children
 * @param nameToAddAfter The child of {@code varNode} after which new
 *     variables should be added (may be null)
 * @param varParent {@code varNode}'s parent
 * @return The number of variables added
 */
private int declareVarsForObjLitValues(
    Name objlitName, String alias, Node objlit, Node varNode,
    Node nameToAddAfter, Node varParent) {
  int numVars = 0;
  int arbitraryNameCounter = 0;
  boolean discardKeys = !objlitName.shouldKeepKeys();

  for (Node key = objlit.getFirstChild(), nextKey; key != null;
       key = nextKey) {
    Node value = key.getFirstChild();
    nextKey = key.getNext();

    // A get or a set can not be rewritten as a VAR.
    if (key.isGetterDef() || key.isSetterDef()) {
      continue;
    }

    // We generate arbitrary names for keys that aren't valid JavaScript
    // identifiers, since those keys are never referenced. (If they were,
    // this object literal's child names wouldn't be collapsible.) The only
    // reason that we don't eliminate them entirely is the off chance that
    // their values are expressions that have side effects.
    boolean isJsIdentifier = !key.isNumber() &&
                             TokenStream.isJSIdentifier(key.getString());
    String propName = isJsIdentifier ?
        key.getString() : String.valueOf(++arbitraryNameCounter);

    // If the name cannot be collapsed, skip it.
    String qName = objlitName.getFullName() + '.' + propName;
    Name p = nameMap.get(qName);
    if (p != null && !p.canCollapse()) {
      continue;
    }

    String propAlias = appendPropForAlias(alias, propName);
    Node refNode = null;
    if (discardKeys) {
      objlit.removeChild(key);
      value.detachFromParent();
    } else {
      // Substitute a reference for the value.
      refNode = IR.name(propAlias);
      if (key.getBooleanProp(Node.IS_CONSTANT_NAME)) {
        refNode.putBooleanProp(Node.IS_CONSTANT_NAME, true);
      }

      key.replaceChild(value, refNode);
    }

    // Declare the collapsed name as a variable with the original value.
    Node nameNode = IR.name(propAlias);
    nameNode.addChildToFront(value);
    if (key.getBooleanProp(Node.IS_CONSTANT_NAME)) {
      nameNode.putBooleanProp(Node.IS_CONSTANT_NAME, true);
    }
    Node newVar = IR.var(nameNode)
        .copyInformationFromForTree(key);
    if (nameToAddAfter != null) {
      varParent.addChildAfter(newVar, nameToAddAfter);
    } else {
      varParent.addChildBefore(newVar, varNode);
    }
    compiler.reportCodeChange();
    nameToAddAfter = newVar;

    // Update the global name's node ancestry if it hasn't already been
    // done. (Duplicate keys in an object literal can bring us here twice
    // for the same global name.)
    if (isJsIdentifier && p != null) {
      if (!discardKeys) {
        Ref newAlias =
            p.getDeclaration().cloneAndReclassify(Ref.Type.ALIASING_GET);
        newAlias.node = refNode;
        p.addRef(newAlias);
      }

      p.getDeclaration().node = nameNode;

      if (value.isFunction()) {
        checkForHosedThisReferences(value, value.getJSDocInfo(), p);
      }
    }

    numVars++;
  }
  return numVars;
}
 
Example 15
Source File: Closure_130_CollapseProperties_t.java    From coming with MIT License 4 votes vote down vote up
/**
 * Updates the initial assignment to a collapsible property at global scope
 * by changing it to a variable declaration (e.g. a.b = 1 -> var a$b = 1).
 * The property's value may either be a primitive or an object literal or
 * function whose properties aren't collapsible.
 *
 * @param alias The flattened property name (e.g. "a$b")
 * @param refName The name for the reference being updated.
 * @param ref An object containing information about the assignment getting
 *     updated
 */
private void updateSimpleDeclaration(String alias, Name refName, Ref ref) {
  Node rvalue = ref.node.getNext();
  Node parent = ref.node.getParent();
  Node gramps = parent.getParent();
  Node greatGramps = gramps.getParent();

  if (rvalue != null && rvalue.isFunction()) {
    checkForHosedThisReferences(rvalue, refName.docInfo, refName);
  }

  // Create the new alias node.
  Node nameNode = NodeUtil.newName(
      compiler.getCodingConvention(), alias, gramps.getFirstChild(),
      refName.getFullName());
  NodeUtil.copyNameAnnotations(ref.node.getLastChild(), nameNode);

  if (gramps.isExprResult()) {
    // BEFORE: a.b.c = ...;
    //   exprstmt
    //     assign
    //       getprop
    //         getprop
    //           name a
    //           string b
    //         string c
    //       NODE
    // AFTER: var a$b$c = ...;
    //   var
    //     name a$b$c
    //       NODE

    // Remove the r-value (NODE).
    parent.removeChild(rvalue);
    nameNode.addChildToFront(rvalue);

    Node varNode = IR.var(nameNode);
    greatGramps.replaceChild(gramps, varNode);
  } else {
    // This must be a complex assignment.
    Preconditions.checkNotNull(ref.getTwin());

    // BEFORE:
    // ... (x.y = 3);
    //
    // AFTER:
    // var x$y;
    // ... (x$y = 3);

    Node current = gramps;
    Node currentParent = gramps.getParent();
    for (; !currentParent.isScript() &&
           !currentParent.isBlock();
         current = currentParent,
         currentParent = currentParent.getParent()) {}

    // Create a stub variable declaration right
    // before the current statement.
    Node stubVar = IR.var(nameNode.cloneTree())
        .copyInformationFrom(nameNode);
    currentParent.addChildBefore(stubVar, current);

    parent.replaceChild(ref.node, nameNode);
  }

  compiler.reportCodeChange();
}
 
Example 16
Source File: Closure_130_CollapseProperties_s.java    From coming with MIT License 4 votes vote down vote up
/**
 * Declares global variables to serve as aliases for the values in an object
 * literal, optionally removing all of the object literal's keys and values.
 *
 * @param alias The object literal's flattened name (e.g. "a$b$c")
 * @param objlit The OBJLIT node
 * @param varNode The VAR node to which new global variables should be added
 *     as children
 * @param nameToAddAfter The child of {@code varNode} after which new
 *     variables should be added (may be null)
 * @param varParent {@code varNode}'s parent
 * @return The number of variables added
 */
private int declareVarsForObjLitValues(
    Name objlitName, String alias, Node objlit, Node varNode,
    Node nameToAddAfter, Node varParent) {
  int numVars = 0;
  int arbitraryNameCounter = 0;
  boolean discardKeys = !objlitName.shouldKeepKeys();

  for (Node key = objlit.getFirstChild(), nextKey; key != null;
       key = nextKey) {
    Node value = key.getFirstChild();
    nextKey = key.getNext();

    // A get or a set can not be rewritten as a VAR.
    if (key.isGetterDef() || key.isSetterDef()) {
      continue;
    }

    // We generate arbitrary names for keys that aren't valid JavaScript
    // identifiers, since those keys are never referenced. (If they were,
    // this object literal's child names wouldn't be collapsible.) The only
    // reason that we don't eliminate them entirely is the off chance that
    // their values are expressions that have side effects.
    boolean isJsIdentifier = !key.isNumber() &&
                             TokenStream.isJSIdentifier(key.getString());
    String propName = isJsIdentifier ?
        key.getString() : String.valueOf(++arbitraryNameCounter);

    // If the name cannot be collapsed, skip it.
    String qName = objlitName.getFullName() + '.' + propName;
    Name p = nameMap.get(qName);
    if (p != null && !p.canCollapse()) {
      continue;
    }

    String propAlias = appendPropForAlias(alias, propName);
    Node refNode = null;
    if (discardKeys) {
      objlit.removeChild(key);
      value.detachFromParent();
    } else {
      // Substitute a reference for the value.
      refNode = IR.name(propAlias);
      if (key.getBooleanProp(Node.IS_CONSTANT_NAME)) {
        refNode.putBooleanProp(Node.IS_CONSTANT_NAME, true);
      }

      key.replaceChild(value, refNode);
    }

    // Declare the collapsed name as a variable with the original value.
    Node nameNode = IR.name(propAlias);
    nameNode.addChildToFront(value);
    if (key.getBooleanProp(Node.IS_CONSTANT_NAME)) {
      nameNode.putBooleanProp(Node.IS_CONSTANT_NAME, true);
    }
    Node newVar = IR.var(nameNode)
        .copyInformationFromForTree(key);
    if (nameToAddAfter != null) {
      varParent.addChildAfter(newVar, nameToAddAfter);
    } else {
      varParent.addChildBefore(newVar, varNode);
    }
    compiler.reportCodeChange();
    nameToAddAfter = newVar;

    // Update the global name's node ancestry if it hasn't already been
    // done. (Duplicate keys in an object literal can bring us here twice
    // for the same global name.)
    if (isJsIdentifier && p != null) {
      if (!discardKeys) {
        Ref newAlias =
            p.getDeclaration().cloneAndReclassify(Ref.Type.ALIASING_GET);
        newAlias.node = refNode;
        p.addRef(newAlias);
      }

      p.getDeclaration().node = nameNode;

      if (value.isFunction()) {
        checkForHosedThisReferences(value, value.getJSDocInfo(), p);
      }
    }

    numVars++;
  }
  return numVars;
}
 
Example 17
Source File: Closure_89_CollapseProperties_s.java    From coming with MIT License 4 votes vote down vote up
/**
 * Declares global variables to serve as aliases for the values in an object
 * literal, optionally removing all of the object literal's keys and values.
 *
 * @param alias The object literal's flattened name (e.g. "a$b$c")
 * @param objlit The OBJLIT node
 * @param varNode The VAR node to which new global variables should be added
 *     as children
 * @param nameToAddAfter The child of {@code varNode} after which new
 *     variables should be added (may be null)
 * @param varParent {@code varNode}'s parent
 * @return The number of variables added
 */
private int declareVarsForObjLitValues(
    Name objlitName, String alias, Node objlit, Node varNode,
    Node nameToAddAfter, Node varParent) {
  int numVars = 0;
  int arbitraryNameCounter = 0;
  boolean discardKeys = !objlitName.shouldKeepKeys();

  for (Node key = objlit.getFirstChild(), nextKey; key != null;
       key = nextKey) {
    Node value = key.getFirstChild();
    nextKey = key.getNext();

    // We generate arbitrary names for keys that aren't valid JavaScript
    // identifiers, since those keys are never referenced. (If they were,
    // this object literal's child names wouldn't be collapsible.) The only
    // reason that we don't eliminate them entirely is the off chance that
    // their values are expressions that have side effects.
    boolean isJsIdentifier = key.getType() != Token.NUMBER &&
                             TokenStream.isJSIdentifier(key.getString());
    String propName = isJsIdentifier ?
        key.getString() : String.valueOf(++arbitraryNameCounter);
    String propAlias = appendPropForAlias(alias, propName);
    String qName = objlitName.fullName() + '.' + propName;

    Node refNode = null;
    if (discardKeys) {
      objlit.removeChild(key);
      value.detachFromParent();
    } else {
      // Substitute a reference for the value.
      refNode = Node.newString(Token.NAME, propAlias);
      if (key.getBooleanProp(Node.IS_CONSTANT_NAME)) {
        refNode.putBooleanProp(Node.IS_CONSTANT_NAME, true);
      }

      key.replaceChild(value, refNode);
    }

    // Declare the collapsed name as a variable with the original value.
    Node nameNode = Node.newString(Token.NAME, propAlias);
    nameNode.addChildToFront(value);
    if (key.getBooleanProp(Node.IS_CONSTANT_NAME)) {
      nameNode.putBooleanProp(Node.IS_CONSTANT_NAME, true);
    }
    Node newVar = new Node(Token.VAR, nameNode)
        .copyInformationFromForTree(key);
    if (nameToAddAfter != null) {
      varParent.addChildAfter(newVar, nameToAddAfter);
    } else {
      varParent.addChildBefore(newVar, varNode);
    }
    compiler.reportCodeChange();
    nameToAddAfter = newVar;

    if (isJsIdentifier) {
      // Update the global name's node ancestry if it hasn't already been
      // done. (Duplicate keys in an object literal can bring us here twice
      // for the same global name.)
      Name p = nameMap.get(qName);
      if (p != null) {
        if (!discardKeys) {
          Ref newAlias =
              p.declaration.cloneAndReclassify(Ref.Type.ALIASING_GET);
          newAlias.node = refNode;
          p.addRef(newAlias);
        }

        p.declaration.node = nameNode;

        if (value.getType() == Token.FUNCTION) {
          checkForHosedThisReferences(value, value.getJSDocInfo(), p);
        }
      }
    }

    numVars++;
  }
  return numVars;
}
 
Example 18
Source File: ExpressionDecomposer.java    From astor with GNU General Public License v2.0 4 votes vote down vote up
/**
 * @param expr The expression to extract.
 * @param injectionPoint The node before which to added the extracted
 *     expression.
 * @return The extract statement node.
 */
private Node extractExpression(Node expr, Node injectionPoint) {
  Node parent = expr.getParent();

  boolean isLhsOfAssignOp = NodeUtil.isAssignmentOp(parent)
      && !parent.isAssign()
      && parent.getFirstChild() == expr;

  Node firstExtractedNode = null;

  // Expressions on the LHS of an assignment-op must have any possible
  // side-effects extracted as the value must be duplicated:
  //    next().foo += 2;
  // becomes:
  //    var t1 = next();
  //    t1.foo = t1.foo + 2;
  if (isLhsOfAssignOp && NodeUtil.isGet(expr)) {
    for (Node n : expr.children()) {
      if (!n.isString() && !isConstantName(n, knownConstants)) {
        Node extractedNode = extractExpression(n, injectionPoint);
        if (firstExtractedNode == null) {
          firstExtractedNode = extractedNode;
        }
      }
    }
  }

  // The temp is known to be constant.
  String tempName = getTempConstantValueName();
  Node replacementValueNode = IR.name(tempName).srcref(expr);

  Node tempNameValue;

  // If it is ASSIGN_XXX, keep the assignment in place and extract the
  // original value of the LHS operand.
  if (isLhsOfAssignOp) {
    Preconditions.checkState(expr.isName() || NodeUtil.isGet(expr));
    // Transform "x += 2" into "x = temp + 2"
    Node opNode = new Node(NodeUtil.getOpFromAssignmentOp(parent))
        .copyInformationFrom(parent);

    Node rightOperand = parent.getLastChild();

    parent.setType(Token.ASSIGN);
    parent.replaceChild(rightOperand, opNode);
    opNode.addChildToFront(replacementValueNode);
    opNode.addChildToBack(rightOperand);

    // The original expression is still being used, so make a clone.
    tempNameValue = expr.cloneTree();
  } else {
    // Replace the expression with the temporary name.
    parent.replaceChild(expr, replacementValueNode);

    // Keep the original node so that CALL expressions can still be found
    // and inlined properly.
    tempNameValue = expr;
  }

  // Re-add the expression in the declaration of the temporary name.
  Node tempVarNode = NodeUtil.newVarNode(tempName, tempNameValue);

  Node injectionPointParent = injectionPoint.getParent();
  injectionPointParent.addChildBefore(tempVarNode, injectionPoint);

  if (firstExtractedNode == null) {
    firstExtractedNode = tempVarNode;
  }
  return firstExtractedNode;
}
 
Example 19
Source File: ExpressionDecomposer.java    From astor with GNU General Public License v2.0 4 votes vote down vote up
/**
 *
 * @param expr The conditional expression to extract.
 * @param injectionPoint The before which extracted expression, would be
 *     injected.
 * @param needResult  Whether the result of the expression is required.
 * @return The node that contains the logic of the expression after
 *     extraction.
 */
private Node extractConditional(
    Node expr, Node injectionPoint, boolean needResult) {
  Node parent = expr.getParent();
  String tempName = getTempValueName();

  // Break down the conditional.
  Node first = expr.getFirstChild();
  Node second = first.getNext();
  Node last = expr.getLastChild();

  // Isolate the children nodes.
  expr.detachChildren();

  // Transform the conditional to an IF statement.
  Node cond = null;
  Node trueExpr = IR.block().srcref(expr);
  Node falseExpr = IR.block().srcref(expr);
  switch (expr.getType()) {
    case Token.HOOK:
      // a = x?y:z --> if (x) {a=y} else {a=z}
      cond = first;
      trueExpr.addChildToFront(NodeUtil.newExpr(
          buildResultExpression(second, needResult, tempName)));
      falseExpr.addChildToFront(NodeUtil.newExpr(
          buildResultExpression(last, needResult, tempName)));
      break;
    case Token.AND:
      // a = x&&y --> if (a=x) {a=y} else {}
      cond = buildResultExpression(first, needResult, tempName);
      trueExpr.addChildToFront(NodeUtil.newExpr(
          buildResultExpression(last, needResult, tempName)));
      break;
    case Token.OR:
      // a = x||y --> if (a=x) {} else {a=y}
      cond = buildResultExpression(first, needResult, tempName);
      falseExpr.addChildToFront(NodeUtil.newExpr(
          buildResultExpression(last, needResult, tempName)));
      break;
    default:
      // With a valid tree we should never get here.
      throw new IllegalStateException("Unexpected.");
  }

  Node ifNode;
  if (falseExpr.hasChildren()) {
    ifNode = IR.ifNode(cond, trueExpr, falseExpr);
  } else {
    ifNode = IR.ifNode(cond, trueExpr);
  }
  ifNode.copyInformationFrom(expr);

  if (needResult) {
    Node tempVarNode = NodeUtil.newVarNode(tempName, null)
        .copyInformationFromForTree(expr);
    Node injectionPointParent = injectionPoint.getParent();
    injectionPointParent.addChildBefore(tempVarNode, injectionPoint);
    injectionPointParent.addChildAfter(ifNode, tempVarNode);

    // Replace the expression with the temporary name.
    Node replacementValueNode = IR.name(tempName);
    parent.replaceChild(expr, replacementValueNode);
  } else {
    // Only conditionals that are the direct child of an expression statement
    // don't need results, for those simply replace the expression statement.
    Preconditions.checkArgument(parent.isExprResult());
    Node gramps = parent.getParent();
    gramps.replaceChild(parent, ifNode);
  }

  return ifNode;
}
 
Example 20
Source File: CollapseProperties.java    From astor with GNU General Public License v2.0 4 votes vote down vote up
/**
 * Declares global variables to serve as aliases for the values in an object
 * literal, optionally removing all of the object literal's keys and values.
 *
 * @param alias The object literal's flattened name (e.g. "a$b$c")
 * @param objlit The OBJLIT node
 * @param varNode The VAR node to which new global variables should be added
 *     as children
 * @param nameToAddAfter The child of {@code varNode} after which new
 *     variables should be added (may be null)
 * @param varParent {@code varNode}'s parent
 * @return The number of variables added
 */
private int declareVarsForObjLitValues(
    Name objlitName, String alias, Node objlit, Node varNode,
    Node nameToAddAfter, Node varParent) {
  int numVars = 0;
  int arbitraryNameCounter = 0;
  boolean discardKeys = !objlitName.shouldKeepKeys();

  for (Node key = objlit.getFirstChild(), nextKey; key != null;
       key = nextKey) {
    Node value = key.getFirstChild();
    nextKey = key.getNext();

    // A get or a set can not be rewritten as a VAR.
    if (key.isGetterDef() || key.isSetterDef()) {
      continue;
    }

    // We generate arbitrary names for keys that aren't valid JavaScript
    // identifiers, since those keys are never referenced. (If they were,
    // this object literal's child names wouldn't be collapsible.) The only
    // reason that we don't eliminate them entirely is the off chance that
    // their values are expressions that have side effects.
    boolean isJsIdentifier = !key.isNumber() &&
                             TokenStream.isJSIdentifier(key.getString());
    String propName = isJsIdentifier ?
        key.getString() : String.valueOf(++arbitraryNameCounter);

    // If the name cannot be collapsed, skip it.
    String qName = objlitName.getFullName() + '.' + propName;
    Name p = nameMap.get(qName);
    if (p != null && !p.canCollapse()) {
      continue;
    }

    String propAlias = appendPropForAlias(alias, propName);
    Node refNode = null;
    if (discardKeys) {
      objlit.removeChild(key);
      value.detachFromParent();
    } else {
      // Substitute a reference for the value.
      refNode = IR.name(propAlias);
      if (key.getBooleanProp(Node.IS_CONSTANT_NAME)) {
        refNode.putBooleanProp(Node.IS_CONSTANT_NAME, true);
      }

      key.replaceChild(value, refNode);
    }

    // Declare the collapsed name as a variable with the original value.
    Node nameNode = IR.name(propAlias);
    nameNode.addChildToFront(value);
    if (key.getBooleanProp(Node.IS_CONSTANT_NAME)) {
      nameNode.putBooleanProp(Node.IS_CONSTANT_NAME, true);
    }
    Node newVar = IR.var(nameNode)
        .copyInformationFromForTree(key);
    if (nameToAddAfter != null) {
      varParent.addChildAfter(newVar, nameToAddAfter);
    } else {
      varParent.addChildBefore(newVar, varNode);
    }
    compiler.reportCodeChange();
    nameToAddAfter = newVar;

    // Update the global name's node ancestry if it hasn't already been
    // done. (Duplicate keys in an object literal can bring us here twice
    // for the same global name.)
    if (isJsIdentifier && p != null) {
      if (!discardKeys) {
        Ref newAlias =
            p.getDeclaration().cloneAndReclassify(Ref.Type.ALIASING_GET);
        newAlias.node = refNode;
        p.addRef(newAlias);
      }

      p.getDeclaration().node = nameNode;

      if (value.isFunction()) {
        checkForHosedThisReferences(value, value.getJSDocInfo(), p);
      }
    }

    numVars++;
  }
  return numVars;
}