/** Try to fold comparison nodes, e.g == */
  private Node tryFoldComparison(Node n, Node left, Node right) {
    TernaryValue result = evaluateComparison(n.getType(), left, right, shouldUseTypes);
    if (result == TernaryValue.UNKNOWN) {
      return n;
    }

    Node newNode = NodeUtil.booleanNode(result.toBoolean(true));
    n.getParent().replaceChild(n, newNode);
    reportCodeChange();

    return newNode;
  }
 @Override
 public TernaryValue testForEquality(JSType that) {
   TernaryValue result = null;
   for (JSType t : alternatesWithoutStucturalTyping) {
     TernaryValue test = t.testForEquality(that);
     if (result == null) {
       result = test;
     } else if (!result.equals(test)) {
       return UNKNOWN;
     }
   }
   return result;
 }
 /** 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());
   }
 }
  /** Try to fold a AND/OR node. */
  private Node tryFoldAndOr(Node n, Node left, Node right) {
    Node parent = n.getParent();

    Node result = null;

    Token type = n.getType();

    TernaryValue leftVal = NodeUtil.getImpureBooleanValue(left);

    if (leftVal != TernaryValue.UNKNOWN) {
      boolean lval = leftVal.toBoolean(true);

      // (TRUE || x) => TRUE (also, (3 || x) => 3)
      // (FALSE && x) => FALSE
      if (lval && type == Token.OR || !lval && type == Token.AND) {
        result = left;

      } else if (!mayHaveSideEffects(left)) {
        // (FALSE || x) => x
        // (TRUE && x) => x
        result = right;
      } else {
        // Left side may have side effects, but we know its boolean value.
        // e.g. true_with_sideeffects || foo() => true_with_sideeffects, foo()
        // or: false_with_sideeffects && foo() => false_with_sideeffects, foo()
        // This, combined with PeepholeRemoveDeadCode, helps reduce expressions
        // like "x() || false || z()".
        n.detachChildren();
        result = IR.comma(left, right);
      }
    }

    // Note: Right hand side folding is handled by
    // PeepholeMinimizeConditions#tryMinimizeCondition

    if (result != null) {
      // Fold it!
      n.detachChildren();
      parent.replaceChild(n, result);
      reportCodeChange();

      return result;
    } else {
      return n;
    }
  }
  private Node tryFoldUnaryOperator(Node n) {
    Preconditions.checkState(n.hasOneChild(), n);

    Node left = n.getFirstChild();
    Node parent = n.getParent();

    if (left == null) {
      return n;
    }

    TernaryValue leftVal = NodeUtil.getPureBooleanValue(left);
    if (leftVal == TernaryValue.UNKNOWN) {
      return n;
    }

    switch (n.getType()) {
      case NOT:
        // Don't fold !0 and !1 back to false.
        if (late && left.isNumber()) {
          double numValue = left.getDouble();
          if (numValue == 0 || numValue == 1) {
            return n;
          }
        }
        Node replacementNode = NodeUtil.booleanNode(!leftVal.toBoolean(true));
        parent.replaceChild(n, replacementNode);
        reportCodeChange();
        return replacementNode;
      case POS:
        if (NodeUtil.isNumericResult(left)) {
          // POS does nothing to numeric values.
          parent.replaceChild(n, left.detachFromParent());
          reportCodeChange();
          return left;
        }
        return n;
      case NEG:
        if (left.isName()) {
          if (left.getString().equals("Infinity")) {
            // "-Infinity" is valid and a literal, don't modify it.
            return n;
          } else if (left.getString().equals("NaN")) {
            // "-NaN" is "NaN".
            n.removeChild(left);
            parent.replaceChild(n, left);
            reportCodeChange();
            return left;
          }
        }

        if (left.isNumber()) {
          double negNum = -left.getDouble();

          Node negNumNode = IR.number(negNum);
          parent.replaceChild(n, negNumNode);
          reportCodeChange();
          return negNumNode;
        } else {
          // left is not a number node, so do not replace, but warn the
          // user because they can't be doing anything good
          report(NEGATING_A_NON_NUMBER_ERROR, left);
          return n;
        }
      case BITNOT:
        try {
          double val = left.getDouble();
          if (val >= Integer.MIN_VALUE && val <= Integer.MAX_VALUE) {
            int intVal = (int) val;
            if (intVal == val) {
              Node notIntValNode = IR.number(~intVal);
              parent.replaceChild(n, notIntValNode);
              reportCodeChange();
              return notIntValNode;
            } else {
              report(FRACTIONAL_BITWISE_OPERAND, left);
              return n;
            }
          } else {
            report(BITWISE_OPERAND_OUT_OF_RANGE, left);
            return n;
          }
        } catch (UnsupportedOperationException ex) {
          // left is not a number node, so do not replace, but warn the
          // user because they can't be doing anything good
          report(NEGATING_A_NON_NUMBER_ERROR, left);
          return n;
        }
      default:
        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;
  }