diff --git a/Userland/Libraries/LibJS/Runtime/ValueTraits.h b/Userland/Libraries/LibJS/Runtime/ValueTraits.h index 14e2dfd23c..3922d3b779 100644 --- a/Userland/Libraries/LibJS/Runtime/ValueTraits.h +++ b/Userland/Libraries/LibJS/Runtime/ValueTraits.h @@ -1,6 +1,7 @@ /* * Copyright (c) 2020-2021, Andreas Kling * Copyright (c) 2020-2021, Linus Groh + * Copyright (c) 2020-2022, Idan Horowitz * * SPDX-License-Identifier: BSD-2-Clause */ @@ -23,6 +24,13 @@ struct ValueTraits : public Traits { if (value.is_negative_zero()) value = Value(0); + // In the IEEE 754 standard a NaN value is encoded as any value from 0x7ff0000000000001 to 0x7fffffffffffffff, + // with the least significant bits (referred to as the 'payload') carrying some kind of diagnostic information + // indicating the source of the NaN. Since ECMA262 does not differentiate between different kinds of NaN values, + // Sets and Maps must not differentiate between them either. + // This is achieved by replacing any NaN value by a canonical qNaN. + else if (value.is_nan()) + value = js_nan(); return u64_hash(value.encoded()); // FIXME: Is this the best way to hash pointers, doubles & ints? } diff --git a/Userland/Libraries/LibJS/Tests/builtins/Map/Map.prototype.get.js b/Userland/Libraries/LibJS/Tests/builtins/Map/Map.prototype.get.js index 1b99f81cb6..160b1787f9 100644 --- a/Userland/Libraries/LibJS/Tests/builtins/Map/Map.prototype.get.js +++ b/Userland/Libraries/LibJS/Tests/builtins/Map/Map.prototype.get.js @@ -9,3 +9,12 @@ test("basic functionality", () => { expect(map.get("a")).toBe(0); expect(map.get("d")).toBe(undefined); }); + +test("NaN differentiation", () => { + const map = new Map(); + map.set(NaN, "a"); + + expect(map.get(0 / 0)).toBe("a"); + expect(map.get(0 * Infinity)).toBe("a"); + expect(map.get(Infinity - Infinity)).toBe("a"); +}); diff --git a/Userland/Libraries/LibJS/Tests/builtins/Set/Set.prototype.has.js b/Userland/Libraries/LibJS/Tests/builtins/Set/Set.prototype.has.js index 3a797d025d..4edf31a7ff 100644 --- a/Userland/Libraries/LibJS/Tests/builtins/Set/Set.prototype.has.js +++ b/Userland/Libraries/LibJS/Tests/builtins/Set/Set.prototype.has.js @@ -11,3 +11,12 @@ test("basic functionality", () => { expect(set.has(1)).toBeTrue(); expect(set.has("serenity")).toBeFalse(); }); + +test("NaN differentiation", () => { + const set = new Set(); + set.add(NaN); + + expect(set.has(0 / 0)).toBeTrue(); + expect(set.has(0 * Infinity)).toBeTrue(); + expect(set.has(Infinity - Infinity)).toBeTrue(); +});