/** 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(); } }
/** * @param n The node in question. * @param cfgNode The node to add * @param conditional true if the definition is not always executed. */ private void computeMustDef(Node n, Node cfgNode, MustDef output, boolean conditional) { switch (n.getType()) { case Token.BLOCK: case Token.FUNCTION: return; case Token.WHILE: case Token.DO: case Token.IF: computeMustDef(NodeUtil.getConditionExpression(n), cfgNode, output, conditional); return; case Token.FOR: if (!NodeUtil.isForIn(n)) { computeMustDef(NodeUtil.getConditionExpression(n), cfgNode, output, conditional); } else { // for(x in y) {...} Node lhs = n.getFirstChild(); Node rhs = lhs.getNext(); if (NodeUtil.isVar(lhs)) { lhs = lhs.getLastChild(); // for(var x in y) {...} } if (NodeUtil.isName(lhs)) { addToDefIfLocal(lhs.getString(), cfgNode, rhs, output); } } return; case Token.AND: case Token.OR: computeMustDef(n.getFirstChild(), cfgNode, output, conditional); computeMustDef(n.getLastChild(), cfgNode, output, true); return; case Token.HOOK: computeMustDef(n.getFirstChild(), cfgNode, output, conditional); computeMustDef(n.getFirstChild().getNext(), cfgNode, output, true); computeMustDef(n.getLastChild(), cfgNode, output, true); return; case Token.VAR: for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (c.hasChildren()) { computeMustDef(c.getFirstChild(), cfgNode, output, conditional); addToDefIfLocal(c.getString(), conditional ? null : cfgNode, c.getFirstChild(), output); } } return; default: if (NodeUtil.isAssignmentOp(n)) { if (NodeUtil.isName(n.getFirstChild())) { Node name = n.getFirstChild(); computeMustDef(name.getNext(), cfgNode, output, conditional); addToDefIfLocal( name.getString(), conditional ? null : cfgNode, n.getLastChild(), output); return; } else if (NodeUtil.isGet(n.getFirstChild())) { // Treat all assignments to arguments as redefining the // parameters itself. Node obj = n.getFirstChild().getFirstChild(); if (NodeUtil.isName(obj) && "arguments".equals(obj.getString())) { // TODO(user): More accuracy can be introduced // ie: We know exactly what arguments[x] is if x is a constant // number. escapeParameters(output); } } } if (NodeUtil.isName(n) && "arguments".equals(n.getString())) { escapeParameters(output); } // DEC and INC actually defines the variable. if (n.getType() == Token.DEC || n.getType() == Token.INC) { Node target = n.getFirstChild(); if (NodeUtil.isName(target)) { addToDefIfLocal(target.getString(), conditional ? null : cfgNode, null, output); return; } } for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { computeMustDef(c, cfgNode, output, conditional); } } }