/** * 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; }
/** Try to fold {@code left instanceof right} into {@code true} or {@code false}. */ private Node tryFoldInstanceof(Node n, Node left, Node right) { Preconditions.checkArgument(n.isInstanceOf()); // TODO(johnlenz) Use type information if available to fold // instanceof. if (NodeUtil.isLiteralValue(left, true) && !mayHaveSideEffects(right)) { Node replacementNode = null; if (NodeUtil.isImmutableValue(left)) { // Non-object types are never instances. replacementNode = IR.falseNode(); } else if (right.isName() && "Object".equals(right.getString())) { replacementNode = IR.trueNode(); } if (replacementNode != null) { n.getParent().replaceChild(n, replacementNode); reportCodeChange(); return replacementNode; } } return n; }
/** * Look at all the property assigns to all variables. These may or may not count as references. * For example, <code> * var x = {}; * x.foo = 3; // not a reference. * var y = foo(); * y.foo = 3; // is a reference. * </code> Interpreting assigments could mark a variable as referenced that wasn't referenced * before, in order to keep it alive. Because we find references by lazily traversing subtrees, * marking a variable as referenced could trigger new traversals of new subtrees, which could find * new references. * * <p>Therefore, this interpretation needs to be run to a fixed point. */ private void interpretAssigns() { boolean changes = false; do { changes = false; // We can't use traditional iterators and iterables for this list, // because our lazily-evaluated continuations will modify it while // we traverse it. for (int current = 0; current < maybeUnreferenced.size(); current++) { Var var = maybeUnreferenced.get(current); if (referenced.contains(var)) { maybeUnreferenced.remove(current); current--; } else { boolean assignedToUnknownValue = false; boolean hasPropertyAssign = false; if (var.getParentNode().getType() == Token.VAR && !NodeUtil.isForIn(var.getParentNode().getParent())) { Node value = var.getInitialValue(); assignedToUnknownValue = value != null && !NodeUtil.isLiteralValue(value, true); } else { // This was initialized to a function arg or a catch param // or a for...in variable. assignedToUnknownValue = true; } for (Assign assign : assignsByVar.get(var)) { if (assign.isPropertyAssign) { hasPropertyAssign = true; } else if (!NodeUtil.isLiteralValue(assign.assignNode.getLastChild(), true)) { assignedToUnknownValue = true; } } if (assignedToUnknownValue && hasPropertyAssign) { changes = markReferencedVar(var) || changes; maybeUnreferenced.remove(current); current--; } } } } while (changes); }
private Node tryFoldAdd(Node node, Node left, Node right) { Preconditions.checkArgument(node.isAdd()); if (NodeUtil.mayBeString(node, shouldUseTypes)) { if (NodeUtil.isLiteralValue(left, false) && NodeUtil.isLiteralValue(right, false)) { // '6' + 7 return tryFoldAddConstantString(node, left, right); } else { // a + 7 or 6 + a return tryFoldChildAddString(node, left, right); } } else { // Try arithmetic add Node result = tryFoldArithmeticOp(node, left, right); if (result != node) { return result; } return tryFoldLeftChildOp(node, left, right); } }
/** * Folds 'typeof(foo)' if foo is a literal, e.g. typeof("bar") --> "string" typeof(6) --> "number" */ private Node tryFoldTypeof(Node originalTypeofNode) { Preconditions.checkArgument(originalTypeofNode.isTypeOf()); Node argumentNode = originalTypeofNode.getFirstChild(); if (argumentNode == null || !NodeUtil.isLiteralValue(argumentNode, true)) { return originalTypeofNode; } String typeNameString = null; switch (argumentNode.getType()) { case FUNCTION: typeNameString = "function"; break; case STRING: typeNameString = "string"; break; case NUMBER: typeNameString = "number"; break; case TRUE: case FALSE: typeNameString = "boolean"; break; case NULL: case OBJECTLIT: case ARRAYLIT: typeNameString = "object"; break; case VOID: typeNameString = "undefined"; break; case NAME: // We assume here that programs don't change the value of the // keyword undefined to something other than the value undefined. if ("undefined".equals(argumentNode.getString())) { typeNameString = "undefined"; } break; } if (typeNameString != null) { Node newNode = IR.string(typeNameString); originalTypeofNode.getParent().replaceChild(originalTypeofNode, newNode); reportCodeChange(); return newNode; } return originalTypeofNode; }