/** Try to fold an ADD node with constant operands */
  private Node tryFoldAddConstantString(Node n, Node left, Node right) {
    if (left.isString() || right.isString() || left.isArrayLit() || right.isArrayLit()) {
      // Add strings.
      String leftString = NodeUtil.getStringValue(left);
      String rightString = NodeUtil.getStringValue(right);
      if (leftString != null && rightString != null) {
        Node newStringNode = IR.string(leftString + rightString);
        n.getParent().replaceChild(n, newStringNode);
        reportCodeChange();
        return newStringNode;
      }
    }

    return n;
  }
  /**
   * 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;
  }
 /** http://www.ecma-international.org/ecma-262/6.0/#sec-abstract-relational-comparison */
 private static TernaryValue tryAbstractRelationalComparison(
     Node left, Node right, boolean useTypes, boolean willNegate) {
   // First, try to evaluate based on the general type.
   ValueType leftValueType = NodeUtil.getKnownValueType(left);
   ValueType rightValueType = NodeUtil.getKnownValueType(right);
   if (leftValueType != ValueType.UNDETERMINED && rightValueType != ValueType.UNDETERMINED) {
     if (leftValueType == ValueType.STRING && rightValueType == ValueType.STRING) {
       String lv = NodeUtil.getStringValue(left);
       String rv = NodeUtil.getStringValue(right);
       if (lv != null && rv != null) {
         // In JS, browsers parse \v differently. So do not compare strings if one contains \v.
         if (lv.indexOf('\u000B') != -1 || rv.indexOf('\u000B') != -1) {
           return TernaryValue.UNKNOWN;
         } else {
           return TernaryValue.forBoolean(lv.compareTo(rv) < 0);
         }
       } else if (left.isTypeOf()
           && right.isTypeOf()
           && left.getFirstChild().isName()
           && right.getFirstChild().isName()
           && left.getFirstChild().getString().equals(right.getFirstChild().getString())) {
         // Special case: `typeof a < typeof a` is always false.
         return TernaryValue.FALSE;
       }
     }
   }
   // Then, try to evaluate based on the value of the node. Try comparing as numbers.
   Double lv = NodeUtil.getNumberValue(left, useTypes);
   Double rv = NodeUtil.getNumberValue(right, useTypes);
   if (lv == null || rv == null) {
     // Special case: `x < x` is always false.
     //
     // TODO(moz): If we knew the named value wouldn't be NaN, it would be nice to handle
     // LE and GE. We should use type information if available here.
     if (!willNegate && left.isName() && right.isName()) {
       if (left.getString().equals(right.getString())) {
         return TernaryValue.FALSE;
       }
     }
     return TernaryValue.UNKNOWN;
   }
   if (Double.isNaN(lv) || Double.isNaN(rv)) {
     return TernaryValue.forBoolean(willNegate);
   } else {
     return TernaryValue.forBoolean(lv.doubleValue() < rv.doubleValue());
   }
 }
  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;
  }
  /** http://www.ecma-international.org/ecma-262/6.0/#sec-strict-equality-comparison */
  private static TernaryValue tryStrictEqualityComparison(Node left, Node right, boolean useTypes) {
    // First, try to evaluate based on the general type.
    ValueType leftValueType = NodeUtil.getKnownValueType(left);
    ValueType rightValueType = NodeUtil.getKnownValueType(right);
    if (leftValueType != ValueType.UNDETERMINED && rightValueType != ValueType.UNDETERMINED) {
      // Strict equality can only be true for values of the same type.
      if (leftValueType != rightValueType) {
        return TernaryValue.FALSE;
      }
      switch (leftValueType) {
        case VOID:
        case NULL:
          return TernaryValue.TRUE;
        case NUMBER:
          {
            if (NodeUtil.isNaN(left)) {
              return TernaryValue.FALSE;
            }
            if (NodeUtil.isNaN(right)) {
              return TernaryValue.FALSE;
            }
            Double lv = NodeUtil.getNumberValue(left, useTypes);
            Double rv = NodeUtil.getNumberValue(right, useTypes);
            if (lv != null && rv != null) {
              return TernaryValue.forBoolean(lv.doubleValue() == rv.doubleValue());
            }
            break;
          }
        case STRING:
          {
            String lv = NodeUtil.getStringValue(left);
            String rv = NodeUtil.getStringValue(right);
            if (lv != null && rv != null) {
              // In JS, browsers parse \v differently. So do not consider strings
              // equal if one contains \v.
              if (lv.indexOf('\u000B') != -1 || rv.indexOf('\u000B') != -1) {
                return TernaryValue.UNKNOWN;
              } else {
                return lv.equals(rv) ? TernaryValue.TRUE : TernaryValue.FALSE;
              }
            } else if (left.isTypeOf()
                && right.isTypeOf()
                && left.getFirstChild().isName()
                && right.getFirstChild().isName()
                && left.getFirstChild().getString().equals(right.getFirstChild().getString())) {
              // Special case, typeof a == typeof a is always true.
              return TernaryValue.TRUE;
            }
            break;
          }
        case BOOLEAN:
          {
            TernaryValue lv = NodeUtil.getPureBooleanValue(left);
            TernaryValue rv = NodeUtil.getPureBooleanValue(right);
            return lv.and(rv).or(lv.not().and(rv.not()));
          }
        default: // Symbol and Object cannot be folded in the general case.
          return TernaryValue.UNKNOWN;
      }
    }

    // Then, try to evaluate based on the value of the node. There's only one special case:
    // Any strict equality comparison against NaN returns false.
    if (NodeUtil.isNaN(left) || NodeUtil.isNaN(right)) {
      return TernaryValue.FALSE;
    }
    return TernaryValue.UNKNOWN;
  }