mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 07:30:19 +00:00
LibJS: Introduce the CanonicalizeKeyedCollectionKey AO
This is an editorial change in the ECMA-262 spec. See: https://github.com/tc39/ecma262/commit/30257dd
This commit is contained in:
parent
d4a7cfb68f
commit
55b4ef7915
Notes:
sideshowbarker
2024-07-17 11:33:34 +09:00
Author: https://github.com/trflynn89 Commit: https://github.com/LadybirdBrowser/ladybird/commit/55b4ef7915 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/560
7 changed files with 85 additions and 34 deletions
|
@ -148,6 +148,7 @@ set(SOURCES
|
|||
Runtime/IteratorPrototype.cpp
|
||||
Runtime/JSONObject.cpp
|
||||
Runtime/JobCallback.cpp
|
||||
Runtime/KeyedCollections.cpp
|
||||
Runtime/Map.cpp
|
||||
Runtime/MapConstructor.cpp
|
||||
Runtime/MapIterator.cpp
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <LibJS/Runtime/FunctionObject.h>
|
||||
#include <LibJS/Runtime/GlobalObject.h>
|
||||
#include <LibJS/Runtime/Iterator.h>
|
||||
#include <LibJS/Runtime/KeyedCollections.h>
|
||||
#include <LibJS/Runtime/PrivateEnvironment.h>
|
||||
#include <LibJS/Runtime/Value.h>
|
||||
|
||||
|
@ -276,9 +277,8 @@ ThrowCompletionOr<GroupsType> group_by(VM& vm, Value items, Value callback_funct
|
|||
// i. Assert: keyCoercion is zero.
|
||||
static_assert(IsSame<KeyType, void>);
|
||||
|
||||
// ii. If key is -0𝔽, set key to +0𝔽.
|
||||
if (key.value().is_negative_zero())
|
||||
key = Value(0);
|
||||
// ii. Set key to CanonicalizeKeyedCollectionKey(key).
|
||||
key = canonicalize_keyed_collection_key(key.value());
|
||||
|
||||
add_value_to_keyed_group(vm, groups, make_handle(key.release_value()), value);
|
||||
}
|
||||
|
|
22
Userland/Libraries/LibJS/Runtime/KeyedCollections.cpp
Normal file
22
Userland/Libraries/LibJS/Runtime/KeyedCollections.cpp
Normal file
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Tim Flynn <trflynn89@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibJS/Runtime/KeyedCollections.h>
|
||||
|
||||
namespace JS {
|
||||
|
||||
// 24.5.1 CanonicalizeKeyedCollectionKey ( key ), https://tc39.es/ecma262/#sec-canonicalizekeyedcollectionkey
|
||||
Value canonicalize_keyed_collection_key(Value key)
|
||||
{
|
||||
// 1. If key is -0𝔽, return +0𝔽.
|
||||
if (key.is_negative_zero())
|
||||
return Value { 0.0 };
|
||||
|
||||
// 2. Return key.
|
||||
return key;
|
||||
}
|
||||
|
||||
}
|
15
Userland/Libraries/LibJS/Runtime/KeyedCollections.h
Normal file
15
Userland/Libraries/LibJS/Runtime/KeyedCollections.h
Normal file
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Tim Flynn <trflynn89@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibJS/Runtime/Value.h>
|
||||
|
||||
namespace JS {
|
||||
|
||||
Value canonicalize_keyed_collection_key(Value);
|
||||
|
||||
}
|
|
@ -7,6 +7,7 @@
|
|||
#include <AK/HashMap.h>
|
||||
#include <AK/TypeCasts.h>
|
||||
#include <LibJS/Runtime/AbstractOperations.h>
|
||||
#include <LibJS/Runtime/KeyedCollections.h>
|
||||
#include <LibJS/Runtime/MapIterator.h>
|
||||
#include <LibJS/Runtime/MapPrototype.h>
|
||||
|
||||
|
@ -66,11 +67,14 @@ JS_DEFINE_NATIVE_FUNCTION(MapPrototype::delete_)
|
|||
// 2. Perform ? RequireInternalSlot(M, [[MapData]]).
|
||||
auto map = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Set key to CanonicalizeKeyedCollectionKey(key).
|
||||
key = canonicalize_keyed_collection_key(key);
|
||||
|
||||
// 3. For each Record { [[Key]], [[Value]] } p of M.[[MapData]], do
|
||||
// a. If p.[[Key]] is not empty and SameValueZero(p.[[Key]], key) is true, then
|
||||
// i. Set p.[[Key]] to empty.
|
||||
// ii. Set p.[[Value]] to empty.
|
||||
// iii. Return true.
|
||||
// a. If p.[[Key]] is not empty and SameValue(p.[[Key]], key) is true, then
|
||||
// i. Set p.[[Key]] to empty.
|
||||
// ii. Set p.[[Value]] to empty.
|
||||
// iii. Return true.
|
||||
// 4. Return false.
|
||||
return Value(map->map_remove(key));
|
||||
}
|
||||
|
@ -131,11 +135,13 @@ JS_DEFINE_NATIVE_FUNCTION(MapPrototype::get)
|
|||
// 2. Perform ? RequireInternalSlot(M, [[MapData]]).
|
||||
auto map = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Set key to CanonicalizeKeyedCollectionKey(key).
|
||||
key = canonicalize_keyed_collection_key(key);
|
||||
|
||||
// 3. For each Record { [[Key]], [[Value]] } p of M.[[MapData]], do
|
||||
// a. If p.[[Key]] is not empty and SameValueZero(p.[[Key]], key) is true, return p.[[Value]].
|
||||
auto result = map->map_get(key);
|
||||
if (result.has_value())
|
||||
return result.value();
|
||||
// a. If p.[[Key]] is not empty and SameValue(p.[[Key]], key) is true, return p.[[Value]].
|
||||
if (auto result = map->map_get(key); result.has_value())
|
||||
return result.release_value();
|
||||
|
||||
// 4. Return undefined.
|
||||
return js_undefined();
|
||||
|
@ -150,8 +156,11 @@ JS_DEFINE_NATIVE_FUNCTION(MapPrototype::has)
|
|||
// 2. Perform ? RequireInternalSlot(M, [[MapData]]).
|
||||
auto map = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Set key to CanonicalizeKeyedCollectionKey(key).
|
||||
key = canonicalize_keyed_collection_key(key);
|
||||
|
||||
// 3. For each Record { [[Key]], [[Value]] } p of M.[[MapData]], do
|
||||
// a. If p.[[Key]] is not empty and SameValueZero(p.[[Key]], key) is true, return true.
|
||||
// a. If p.[[Key]] is not empty and SameValue(p.[[Key]], key) is true, return true.
|
||||
// 4. Return false.
|
||||
return map->map_has(key);
|
||||
}
|
||||
|
@ -178,14 +187,13 @@ JS_DEFINE_NATIVE_FUNCTION(MapPrototype::set)
|
|||
// 2. Perform ? RequireInternalSlot(M, [[MapData]]).
|
||||
auto map = TRY(typed_this_object(vm));
|
||||
|
||||
// 4. If key is -0𝔽, set key to +0𝔽.
|
||||
if (key.is_negative_zero())
|
||||
key = Value(0);
|
||||
// 3. Set key to CanonicalizeKeyedCollectionKey(key).
|
||||
key = canonicalize_keyed_collection_key(key);
|
||||
|
||||
// 3. For each Record { [[Key]], [[Value]] } p of M.[[MapData]], do
|
||||
// a. If p.[[Key]] is not empty and SameValueZero(p.[[Key]], key) is true, then
|
||||
// i. Set p.[[Value]] to value.
|
||||
// ii. Return M.
|
||||
// 4. For each Record { [[Key]], [[Value]] } p of M.[[MapData]], do
|
||||
// a. If p.[[Key]] is not empty and SameValue(p.[[Key]], key) is true, then
|
||||
// i. Set p.[[Value]] to value.
|
||||
// ii. Return M.
|
||||
// 5. Let p be the Record { [[Key]]: key, [[Value]]: value }.
|
||||
// 6. Append p to M.[[MapData]].
|
||||
map->map_set(key, value);
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <LibJS/Runtime/AbstractOperations.h>
|
||||
#include <LibJS/Runtime/FunctionObject.h>
|
||||
#include <LibJS/Runtime/Iterator.h>
|
||||
#include <LibJS/Runtime/KeyedCollections.h>
|
||||
#include <LibJS/Runtime/SetIterator.h>
|
||||
#include <LibJS/Runtime/SetPrototype.h>
|
||||
#include <LibJS/Runtime/ValueInlines.h>
|
||||
|
@ -62,12 +63,11 @@ JS_DEFINE_NATIVE_FUNCTION(SetPrototype::add)
|
|||
// 2. Perform ? RequireInternalSlot(S, [[SetData]]).
|
||||
auto set = TRY(typed_this_object(vm));
|
||||
|
||||
// 4. If value is -0𝔽, set value to +0𝔽.
|
||||
if (value.is_negative_zero())
|
||||
value = Value(0);
|
||||
// 3. Set value to CanonicalizeKeyedCollectionKey(value).
|
||||
value = canonicalize_keyed_collection_key(value);
|
||||
|
||||
// 3. For each element e of S.[[SetData]], do
|
||||
// a. If e is not empty and SameValueZero(e, value) is true, then
|
||||
// 4. For each element e of S.[[SetData]], do
|
||||
// a. If e is not empty and SameValue(e, value) is true, then
|
||||
// i. Return S.
|
||||
// 5. Append value to S.[[SetData]].
|
||||
set->set_add(value);
|
||||
|
@ -100,11 +100,14 @@ JS_DEFINE_NATIVE_FUNCTION(SetPrototype::delete_)
|
|||
// 2. Perform ? RequireInternalSlot(S, [[SetData]]).
|
||||
auto set = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. For each element e of S.[[SetData]], do
|
||||
// a. If e is not empty and SameValueZero(e, value) is true, then
|
||||
// 3. Set value to CanonicalizeKeyedCollectionKey(value).
|
||||
value = canonicalize_keyed_collection_key(value);
|
||||
|
||||
// 4. For each element e of S.[[SetData]], do
|
||||
// a. If e is not empty and SameValue(e, value) is true, then
|
||||
// i. Replace the element of S.[[SetData]] whose value is e with an element whose value is empty.
|
||||
// ii. Return true.
|
||||
// 4. Return false.
|
||||
// 5. Return false.
|
||||
return Value(set->set_remove(value));
|
||||
}
|
||||
|
||||
|
@ -165,9 +168,12 @@ JS_DEFINE_NATIVE_FUNCTION(SetPrototype::has)
|
|||
// 2. Perform ? RequireInternalSlot(S, [[SetData]]).
|
||||
auto set = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. For each element e of S.[[SetData]], do
|
||||
// a. If e is not empty and SameValueZero(e, value) is true, return true.
|
||||
// 4. Return false.
|
||||
// 3. Set value to CanonicalizeKeyedCollectionKey(value).
|
||||
value = canonicalize_keyed_collection_key(value);
|
||||
|
||||
// 4. For each element e of S.[[SetData]], do
|
||||
// a. If e is not empty and SameValue(e, value) is true, return true.
|
||||
// 5. Return false.
|
||||
return Value(set->set_has(value));
|
||||
}
|
||||
|
||||
|
|
|
@ -25,21 +25,20 @@ struct ValueTraits : public Traits<Value> {
|
|||
if (value.is_bigint())
|
||||
return value.as_bigint().big_integer().hash();
|
||||
|
||||
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())
|
||||
if (value.is_nan())
|
||||
value = js_nan();
|
||||
|
||||
return u64_hash(value.encoded()); // FIXME: Is this the best way to hash pointers, doubles & ints?
|
||||
}
|
||||
|
||||
static bool equals(Value const a, Value const b)
|
||||
{
|
||||
return same_value_zero(a, b);
|
||||
return same_value(a, b);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue