/** 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;
  }
 private static void reportIfNaN(NodeTraversal t, Node n) {
   if (NodeUtil.isNaN(n)) {
     t.getCompiler().report(t.makeError(n.getParent(), SUSPICIOUS_COMPARISON_WITH_NAN));
   }
 }