/** 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-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; }