/** 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());
   }
 }
 /** http://www.ecma-international.org/ecma-262/6.0/#sec-abstract-equality-comparison */
 private static TernaryValue tryAbstractEqualityComparison(
     Node left, Node right, boolean useTypes) {
   // Evaluate based on the general type.
   ValueType leftValueType = NodeUtil.getKnownValueType(left);
   ValueType rightValueType = NodeUtil.getKnownValueType(right);
   if (leftValueType != ValueType.UNDETERMINED && rightValueType != ValueType.UNDETERMINED) {
     // Delegate to strict equality comparison for values of the same type.
     if (leftValueType == rightValueType) {
       return tryStrictEqualityComparison(left, right, useTypes);
     }
     if ((leftValueType == ValueType.NULL && rightValueType == ValueType.VOID)
         || (leftValueType == ValueType.VOID && rightValueType == ValueType.NULL)) {
       return TernaryValue.TRUE;
     }
     if ((leftValueType == ValueType.NUMBER && rightValueType == ValueType.STRING)
         || rightValueType == ValueType.BOOLEAN) {
       Double rv = NodeUtil.getNumberValue(right, useTypes);
       return rv == null
           ? TernaryValue.UNKNOWN
           : tryAbstractEqualityComparison(left, IR.number(rv), useTypes);
     }
     if ((leftValueType == ValueType.STRING && rightValueType == ValueType.NUMBER)
         || leftValueType == ValueType.BOOLEAN) {
       Double lv = NodeUtil.getNumberValue(left, useTypes);
       return lv == null
           ? TernaryValue.UNKNOWN
           : tryAbstractEqualityComparison(IR.number(lv), right, useTypes);
     }
     if ((leftValueType == ValueType.STRING || leftValueType == ValueType.NUMBER)
         && rightValueType == ValueType.OBJECT) {
       return TernaryValue.UNKNOWN;
     }
     if (leftValueType == ValueType.OBJECT
         && (rightValueType == ValueType.STRING || rightValueType == ValueType.NUMBER)) {
       return TernaryValue.UNKNOWN;
     }
     return TernaryValue.FALSE;
   }
   // In general, the rest of the cases cannot be folded.
   return TernaryValue.UNKNOWN;
 }
  /**
   * 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 void tryConvertToNumber(Node n) {
    switch (n.getType()) {
      case NUMBER:
        // Nothing to do
        return;
      case AND:
      case OR:
      case COMMA:
        tryConvertToNumber(n.getLastChild());
        return;
      case HOOK:
        tryConvertToNumber(n.getSecondChild());
        tryConvertToNumber(n.getLastChild());
        return;
      case NAME:
        if (!NodeUtil.isUndefined(n)) {
          return;
        }
        break;
    }

    Double result = NodeUtil.getNumberValue(n, shouldUseTypes);
    if (result == null) {
      return;
    }

    double value = result;

    Node replacement = NodeUtil.numberNode(value, n);
    if (replacement.isEquivalentTo(n)) {
      return;
    }

    n.getParent().replaceChild(n, replacement);
    reportCodeChange();
  }
  /** Try to fold arithmetic binary operators */
  private Node performArithmeticOp(Token opType, Node left, Node right) {
    // Unlike other operations, ADD operands are not always converted
    // to Number.
    if (opType == Token.ADD
        && (NodeUtil.mayBeString(left, shouldUseTypes)
            || NodeUtil.mayBeString(right, shouldUseTypes))) {
      return null;
    }

    double result;

    // TODO(johnlenz): Handle NaN with unknown value. BIT ops convert NaN
    // to zero so this is a little awkward here.

    Double lValObj = NodeUtil.getNumberValue(left, shouldUseTypes);
    if (lValObj == null) {
      return null;
    }
    Double rValObj = NodeUtil.getNumberValue(right, shouldUseTypes);
    if (rValObj == null) {
      return null;
    }

    double lval = lValObj;
    double rval = rValObj;

    switch (opType) {
      case BITAND:
        result = NodeUtil.toInt32(lval) & NodeUtil.toInt32(rval);
        break;
      case BITOR:
        result = NodeUtil.toInt32(lval) | NodeUtil.toInt32(rval);
        break;
      case BITXOR:
        result = NodeUtil.toInt32(lval) ^ NodeUtil.toInt32(rval);
        break;
      case ADD:
        result = lval + rval;
        break;
      case SUB:
        result = lval - rval;
        break;
      case MUL:
        result = lval * rval;
        break;
      case MOD:
        if (rval == 0) {
          return null;
        }
        result = lval % rval;
        break;
      case DIV:
        if (rval == 0) {
          return null;
        }
        result = lval / rval;
        break;
      default:
        throw new Error("Unexpected arithmetic operator");
    }

    // TODO(johnlenz): consider removing the result length check.
    // length of the left and right value plus 1 byte for the operator.
    if ((String.valueOf(result).length()
                <= String.valueOf(lval).length() + String.valueOf(rval).length() + 1

            // Do not try to fold arithmetic for numbers > 2^53. After that
            // point, fixed-point math starts to break down and become inaccurate.
            && Math.abs(result) <= MAX_FOLD_NUMBER)
        || Double.isNaN(result)
        || result == Double.POSITIVE_INFINITY
        || result == Double.NEGATIVE_INFINITY) {
      return NodeUtil.numberNode(result, null);
    }
    return null;
  }
  /** 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;
  }