diff options
author | Luke Wilde <lukew@serenityos.org> | 2021-10-15 01:47:54 +0100 |
---|---|---|
committer | Linus Groh <mail@linusgroh.de> | 2021-10-15 01:59:09 +0100 |
commit | 09536f5f48b7459f04200f1dc6d10220ab53fed8 (patch) | |
tree | 13020f2dbd5ac64d0b232c796b9c2afeb9121499 | |
parent | 3ad159537e6e59760ed6b2ac2feba3fbcd8481af (diff) | |
download | serenity-09536f5f48b7459f04200f1dc6d10220ab53fed8.zip |
LibJS: Fix typo in LHS Object and RHS BigInt loosely equals check
Step 12 was using `lhs.is_bigint()` instead of `rhs.is_bigint()`,
meaning you got:
```js
1n == Object(1n); // true
Object(1n) == 1n; // false
```
This also adds spec comments to is_loosely_equal.
-rw-r--r-- | Userland/Libraries/LibJS/Runtime/Value.cpp | 33 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Tests/builtins/BigInt/bigint-basic.js | 15 |
2 files changed, 45 insertions, 3 deletions
diff --git a/Userland/Libraries/LibJS/Runtime/Value.cpp b/Userland/Libraries/LibJS/Runtime/Value.cpp index db5646c589..a5888068af 100644 --- a/Userland/Libraries/LibJS/Runtime/Value.cpp +++ b/Userland/Libraries/LibJS/Runtime/Value.cpp @@ -1389,53 +1389,79 @@ bool is_loosely_equal(GlobalObject& global_object, Value lhs, Value rhs) { auto& vm = global_object.vm(); - if (same_type_for_equality(lhs, rhs)) + // 1. If Type(x) is the same as Type(y), then + if (same_type_for_equality(lhs, rhs)) { + // a. Return IsStrictlyEqual(x, y). return is_strictly_equal(lhs, rhs); + } + // 2. If x is null and y is undefined, return true. + // 3. If x is undefined and y is null, return true. if (lhs.is_nullish() && rhs.is_nullish()) return true; - // B.3.7.2 Changes to IsLooselyEqual, https://tc39.es/ecma262/#sec-IsHTMLDDA-internal-slot-aec + // 4. NOTE: This step is replaced in section B.3.6.2. + // B.3.6.2 Changes to IsLooselyEqual, https://tc39.es/ecma262/#sec-IsHTMLDDA-internal-slot-aec + // 1. If Type(x) is Object and x has an [[IsHTMLDDA]] internal slot and y is either null or undefined, return true. if (lhs.is_object() && lhs.as_object().is_htmldda() && rhs.is_nullish()) return true; + + // 2. If x is either null or undefined and Type(y) is Object and y has an [[IsHTMLDDA]] internal slot, return true. if (lhs.is_nullish() && rhs.is_object() && rhs.as_object().is_htmldda()) return true; + // == End of B.3.6.2 == + + // 5. If Type(x) is Number and Type(y) is String, return IsLooselyEqual(x, ! ToNumber(y)). if (lhs.is_number() && rhs.is_string()) return is_loosely_equal(global_object, lhs, rhs.to_number(global_object)); + // 6. If Type(x) is String and Type(y) is Number, return IsLooselyEqual(! ToNumber(x), y). if (lhs.is_string() && rhs.is_number()) return is_loosely_equal(global_object, lhs.to_number(global_object), rhs); + // 7. If Type(x) is BigInt and Type(y) is String, then if (lhs.is_bigint() && rhs.is_string()) { auto& rhs_string = rhs.as_string().string(); + // a. Let n be ! StringToBigInt(y). + // b. If n is NaN, return false. if (!is_valid_bigint_value(rhs_string)) return false; + // c. Return IsLooselyEqual(x, n). return is_loosely_equal(global_object, lhs, js_bigint(vm, Crypto::SignedBigInteger::from_base(10, rhs_string))); } + // 8. If Type(x) is String and Type(y) is BigInt, return IsLooselyEqual(y, x). if (lhs.is_string() && rhs.is_bigint()) return is_loosely_equal(global_object, rhs, lhs); + // 9. If Type(x) is Boolean, return IsLooselyEqual(! ToNumber(x), y). if (lhs.is_boolean()) return is_loosely_equal(global_object, lhs.to_number(global_object), rhs); + // 10. If Type(y) is Boolean, return IsLooselyEqual(x, ! ToNumber(y)). if (rhs.is_boolean()) return is_loosely_equal(global_object, lhs, rhs.to_number(global_object)); + // 11. If Type(x) is either String, Number, BigInt, or Symbol and Type(y) is Object, return IsLooselyEqual(x, ? ToPrimitive(y)). if ((lhs.is_string() || lhs.is_number() || lhs.is_bigint() || lhs.is_symbol()) && rhs.is_object()) { auto rhs_primitive = TRY_OR_DISCARD(rhs.to_primitive(global_object)); return is_loosely_equal(global_object, lhs, rhs_primitive); } - if (lhs.is_object() && (rhs.is_string() || rhs.is_number() || lhs.is_bigint() || rhs.is_symbol())) { + // 12. If Type(x) is Object and Type(y) is either String, Number, BigInt, or Symbol, return IsLooselyEqual(? ToPrimitive(x), y). + if (lhs.is_object() && (rhs.is_string() || rhs.is_number() || rhs.is_bigint() || rhs.is_symbol())) { auto lhs_primitive = TRY_OR_DISCARD(lhs.to_primitive(global_object)); return is_loosely_equal(global_object, lhs_primitive, rhs); } + // 13. If Type(x) is BigInt and Type(y) is Number, or if Type(x) is Number and Type(y) is BigInt, then if ((lhs.is_bigint() && rhs.is_number()) || (lhs.is_number() && rhs.is_bigint())) { + // a. If x or y are any of NaN, +∞𝔽, or -∞𝔽, return false. if (lhs.is_nan() || lhs.is_infinity() || rhs.is_nan() || rhs.is_infinity()) return false; + + // b. If ℝ(x) = ℝ(y), return true; otherwise return false. if ((lhs.is_number() && !lhs.is_integral_number()) || (rhs.is_number() && !rhs.is_integral_number())) return false; if (lhs.is_number()) @@ -1444,6 +1470,7 @@ bool is_loosely_equal(GlobalObject& global_object, Value lhs, Value rhs) return Crypto::SignedBigInteger { rhs.to_i32(global_object) } == lhs.as_bigint().big_integer(); } + // 14. Return false. return false; } diff --git a/Userland/Libraries/LibJS/Tests/builtins/BigInt/bigint-basic.js b/Userland/Libraries/LibJS/Tests/builtins/BigInt/bigint-basic.js index 8208de83c3..c3c9e60aa4 100644 --- a/Userland/Libraries/LibJS/Tests/builtins/BigInt/bigint-basic.js +++ b/Userland/Libraries/LibJS/Tests/builtins/BigInt/bigint-basic.js @@ -73,6 +73,21 @@ describe("correct behavior", () => { const b = 704179908449526267977309288010258n; expect(a == a).toBeTrue(); expect(a == b).toBeFalse(); + + expect(0n == Object(0n)).toBeTrue(); + expect(Object(0n) == 0n).toBeTrue(); + expect(0n != Object(0n)).toBeFalse(); + expect(Object(0n) != 0n).toBeFalse(); + + expect(1n == Object(1n)).toBeTrue(); + expect(Object(1n) == 1n).toBeTrue(); + expect(1n != Object(1n)).toBeFalse(); + expect(Object(1n) != 1n).toBeFalse(); + + expect(1n == Object(2n)).toBeFalse(); + expect(Object(2n) == 1n).toBeFalse(); + expect(1n != Object(2n)).toBeTrue(); + expect(Object(2n) != 1n).toBeTrue(); }); test("strong equality operators", () => { |