/** * 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; }
private Node tryFoldAssign(Node n, Node left, Node right) { Preconditions.checkArgument(n.isAssign()); if (!late) { return n; } // Tries to convert x = x + y -> x += y; if (!right.hasChildren() || right.getSecondChild() != right.getLastChild()) { // RHS must have two children. return n; } if (mayHaveSideEffects(left)) { return n; } Node newRight; if (areNodesEqualForInlining(left, right.getFirstChild())) { newRight = right.getLastChild(); } else if (NodeUtil.isCommutative(right.getType()) && areNodesEqualForInlining(left, right.getLastChild())) { newRight = right.getFirstChild(); } else { return n; } Token newType = null; switch (right.getType()) { case ADD: newType = Token.ASSIGN_ADD; break; case BITAND: newType = Token.ASSIGN_BITAND; break; case BITOR: newType = Token.ASSIGN_BITOR; break; case BITXOR: newType = Token.ASSIGN_BITXOR; break; case DIV: newType = Token.ASSIGN_DIV; break; case LSH: newType = Token.ASSIGN_LSH; break; case MOD: newType = Token.ASSIGN_MOD; break; case MUL: newType = Token.ASSIGN_MUL; break; case RSH: newType = Token.ASSIGN_RSH; break; case SUB: newType = Token.ASSIGN_SUB; break; case URSH: newType = Token.ASSIGN_URSH; break; default: return n; } Node newNode = new Node(newType, left.detachFromParent(), newRight.detachFromParent()); n.getParent().replaceChild(n, newNode); reportCodeChange(); return newNode; }