mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-09-19 15:58:56 +00:00
LibJS: Update specification steps for the Set Methods proposal
It is now Stage 4 and has been merged into the main ECMA-262 spec:
a78d504
This commit is contained in:
parent
55b4ef7915
commit
2dbd71d54b
Notes:
sideshowbarker
2024-07-16 23:51:07 +09:00
Author: https://github.com/trflynn89
Commit: 2dbd71d54b
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/560
4 changed files with 432 additions and 408 deletions
|
@ -4,7 +4,9 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibJS/Runtime/KeyedCollections.h>
|
||||
#include <LibJS/Runtime/Set.h>
|
||||
#include <LibJS/Runtime/ValueInlines.h>
|
||||
|
||||
namespace JS {
|
||||
|
||||
|
@ -43,4 +45,60 @@ void Set::visit_edges(Cell::Visitor& visitor)
|
|||
visitor.visit(m_values);
|
||||
}
|
||||
|
||||
// 24.2.1.2 GetSetRecord ( obj ), https://tc39.es/ecma262/#sec-getsetrecord
|
||||
ThrowCompletionOr<SetRecord> get_set_record(VM& vm, Value value)
|
||||
{
|
||||
// 1. If obj is not an Object, throw a TypeError exception.
|
||||
if (!value.is_object())
|
||||
return vm.throw_completion<TypeError>(ErrorType::NotAnObject, value.to_string_without_side_effects());
|
||||
auto const& object = value.as_object();
|
||||
|
||||
// 2. Let rawSize be ? Get(obj, "size").
|
||||
auto raw_size = TRY(object.get(vm.names.size));
|
||||
|
||||
// 3. Let numSize be ? ToNumber(rawSize).
|
||||
auto number_size = TRY(raw_size.to_number(vm));
|
||||
|
||||
// 4. NOTE: If rawSize is undefined, then numSize will be NaN.
|
||||
// 5. If numSize is NaN, throw a TypeError exception.
|
||||
if (number_size.is_nan())
|
||||
return vm.throw_completion<TypeError>(ErrorType::NumberIsNaN, "size"sv);
|
||||
|
||||
// 6. Let intSize be ! ToIntegerOrInfinity(numSize).
|
||||
auto integer_size = MUST(number_size.to_integer_or_infinity(vm));
|
||||
|
||||
// 7. If intSize < 0, throw a RangeError exception.
|
||||
if (integer_size < 0)
|
||||
return vm.throw_completion<RangeError>(ErrorType::NumberIsNegative, "size"sv);
|
||||
|
||||
// 8. Let has be ? Get(obj, "has").
|
||||
auto has = TRY(object.get(vm.names.has));
|
||||
|
||||
// 9. If IsCallable(has) is false, throw a TypeError exception.
|
||||
if (!has.is_function())
|
||||
return vm.throw_completion<TypeError>(ErrorType::NotAFunction, has.to_string_without_side_effects());
|
||||
|
||||
// 10. Let keys be ? Get(obj, "keys").
|
||||
auto keys = TRY(object.get(vm.names.keys));
|
||||
|
||||
// 11. If IsCallable(keys) is false, throw a TypeError exception.
|
||||
if (!keys.is_function())
|
||||
return vm.throw_completion<TypeError>(ErrorType::NotAFunction, keys.to_string_without_side_effects());
|
||||
|
||||
// 12. Return a new Set Record { [[SetObject]]: obj, [[Size]]: intSize, [[Has]]: has, [[Keys]]: keys }.
|
||||
return SetRecord { .set_object = object, .size = integer_size, .has = has.as_function(), .keys = keys.as_function() };
|
||||
}
|
||||
|
||||
// 24.2.1.3 SetDataHas ( setData, value ), https://tc39.es/ecma262/#sec-setdatahas
|
||||
bool set_data_has(NonnullGCPtr<Set> set_data, Value value)
|
||||
{
|
||||
// NOTE: We do not need to implement SetDataIndex, as we do not implement the use of empty slots in Set. But we do
|
||||
// need to match its behavior of always canonicalizing the provided value.
|
||||
value = canonicalize_keyed_collection_key(value);
|
||||
|
||||
// 1. If SetDataIndex(setData, value) is not-found, return false.
|
||||
// 2. Return true.
|
||||
return set_data->set_has(value);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -47,4 +47,15 @@ private:
|
|||
GCPtr<Map> m_values;
|
||||
};
|
||||
|
||||
// 24.2.1.1 Set Records, https://tc39.es/ecma262/#sec-set-records
|
||||
struct SetRecord {
|
||||
NonnullGCPtr<Object const> set_object; // [[SetObject]]
|
||||
double size { 0 }; // [[Size]
|
||||
NonnullGCPtr<FunctionObject> has; // [[Has]]
|
||||
NonnullGCPtr<FunctionObject> keys; // [[Keys]]
|
||||
};
|
||||
|
||||
ThrowCompletionOr<SetRecord> get_set_record(VM&, Value);
|
||||
bool set_data_has(NonnullGCPtr<Set>, Value);
|
||||
|
||||
}
|
||||
|
|
|
@ -32,25 +32,25 @@ void SetPrototype::initialize(Realm& realm)
|
|||
define_native_function(realm, vm.names.add, add, 1, attr);
|
||||
define_native_function(realm, vm.names.clear, clear, 0, attr);
|
||||
define_native_function(realm, vm.names.delete_, delete_, 1, attr);
|
||||
define_native_function(realm, vm.names.difference, difference, 1, attr);
|
||||
define_native_function(realm, vm.names.entries, entries, 0, attr);
|
||||
define_native_function(realm, vm.names.forEach, for_each, 1, attr);
|
||||
define_native_function(realm, vm.names.has, has, 1, attr);
|
||||
define_native_function(realm, vm.names.values, values, 0, attr);
|
||||
define_native_function(realm, vm.names.union_, union_, 1, attr);
|
||||
define_native_function(realm, vm.names.intersection, intersection, 1, attr);
|
||||
define_native_function(realm, vm.names.difference, difference, 1, attr);
|
||||
define_native_function(realm, vm.names.symmetricDifference, symmetric_difference, 1, attr);
|
||||
define_native_function(realm, vm.names.isDisjointFrom, is_disjoint_from, 1, attr);
|
||||
define_native_function(realm, vm.names.isSubsetOf, is_subset_of, 1, attr);
|
||||
define_native_function(realm, vm.names.isSupersetOf, is_superset_of, 1, attr);
|
||||
define_native_function(realm, vm.names.isDisjointFrom, is_disjoint_from, 1, attr);
|
||||
define_native_accessor(realm, vm.names.size, size_getter, {}, Attribute::Configurable);
|
||||
define_native_function(realm, vm.names.symmetricDifference, symmetric_difference, 1, attr);
|
||||
define_native_function(realm, vm.names.union_, union_, 1, attr);
|
||||
define_native_function(realm, vm.names.values, values, 0, attr);
|
||||
|
||||
define_direct_property(vm.names.keys, get_without_side_effects(vm.names.values), attr);
|
||||
|
||||
// 24.2.3.11 Set.prototype [ @@iterator ] ( ), https://tc39.es/ecma262/#sec-set.prototype-@@iterator
|
||||
// 24.2.3.18 Set.prototype [ @@iterator ] ( ), https://tc39.es/ecma262/#sec-set.prototype-@@iterator
|
||||
define_direct_property(vm.well_known_symbol_iterator(), get_without_side_effects(vm.names.values), attr);
|
||||
|
||||
// 24.2.3.12 Set.prototype [ @@toStringTag ], https://tc39.es/ecma262/#sec-set.prototype-@@tostringtag
|
||||
// 24.2.3.19 Set.prototype [ @@toStringTag ], https://tc39.es/ecma262/#sec-set.prototype-@@tostringtag
|
||||
define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, vm.names.Set.as_string()), Attribute::Configurable);
|
||||
}
|
||||
|
||||
|
@ -111,7 +111,75 @@ JS_DEFINE_NATIVE_FUNCTION(SetPrototype::delete_)
|
|||
return Value(set->set_remove(value));
|
||||
}
|
||||
|
||||
// 24.2.3.5 Set.prototype.entries ( ), https://tc39.es/ecma262/#sec-set.prototype.entries
|
||||
// 24.2.4.5 Set.prototype.difference ( other ), https://tc39.es/ecma262/#sec-set.prototype.difference
|
||||
JS_DEFINE_NATIVE_FUNCTION(SetPrototype::difference)
|
||||
{
|
||||
// 1. Let O be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(O, [[SetData]]).
|
||||
auto set = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Let otherRec be ? GetSetRecord(other).
|
||||
auto other_record = TRY(get_set_record(vm, vm.argument(0)));
|
||||
|
||||
// 4. Let resultSetData be a copy of O.[[SetData]].
|
||||
auto result = set->copy();
|
||||
|
||||
// 5. If SetDataSize(O.[[SetData]]) ≤ otherRec.[[Size]], then
|
||||
if (set->set_size() <= other_record.size) {
|
||||
// a. Let thisSize be the number of elements in O.[[SetData]].
|
||||
// b. Let index be 0.
|
||||
// c. Repeat, while index < thisSize,
|
||||
for (auto const& element : *set) {
|
||||
// i. Let e be resultSetData[index].
|
||||
// ii. If e is not EMPTY, then
|
||||
// 1. Let inOther be ToBoolean(? Call(otherRec.[[Has]], otherRec.[[SetObject]], « e »)).
|
||||
auto in_other = TRY(call(vm, *other_record.has, other_record.set_object, element.key)).to_boolean();
|
||||
|
||||
// 2. If inOther is true, then
|
||||
if (in_other) {
|
||||
// a. Set resultSetData[index] to EMPTY.
|
||||
result->set_remove(element.key);
|
||||
}
|
||||
|
||||
// iii. Set index to index + 1.
|
||||
}
|
||||
}
|
||||
// 6. Else,
|
||||
else {
|
||||
// a. Let keysIter be ? GetIteratorFromMethod(otherRec.[[SetObject]], otherRec.[[Keys]]).
|
||||
auto keys_iterator = TRY(get_iterator_from_method(vm, other_record.set_object, other_record.keys));
|
||||
|
||||
// b. Let next be NOT-STARTED.
|
||||
Optional<Value> next;
|
||||
|
||||
// c. Repeat, while next is not DONE,
|
||||
do {
|
||||
// i. Set next to ? IteratorStepValue(keysIter).
|
||||
next = TRY(iterator_step_value(vm, keys_iterator));
|
||||
|
||||
// ii. If next is not DONE, then
|
||||
if (next.has_value()) {
|
||||
// 1. Set next to CanonicalizeKeyedCollectionKey(next).
|
||||
next = canonicalize_keyed_collection_key(*next);
|
||||
|
||||
// 2. Let valueIndex be SetDataIndex(resultSetData, next).
|
||||
// 3. If valueIndex is not NOT-FOUND, then
|
||||
if (result->set_has(*next)) {
|
||||
// a. Set resultSetData[valueIndex] to EMPTY.
|
||||
result->set_remove(*next);
|
||||
}
|
||||
}
|
||||
} while (next.has_value());
|
||||
}
|
||||
|
||||
// 7. Let result be OrdinaryObjectCreate(%Set.prototype%, « [[SetData]] »).
|
||||
// 8. Set result.[[SetData]] to resultSetData.
|
||||
|
||||
// 9. Return result.
|
||||
return result;
|
||||
}
|
||||
|
||||
// 24.2.3.6 Set.prototype.entries ( ), https://tc39.es/ecma262/#sec-set.prototype.entries
|
||||
JS_DEFINE_NATIVE_FUNCTION(SetPrototype::entries)
|
||||
{
|
||||
auto& realm = *vm.current_realm();
|
||||
|
@ -123,7 +191,7 @@ JS_DEFINE_NATIVE_FUNCTION(SetPrototype::entries)
|
|||
return SetIterator::create(realm, set, Object::PropertyKind::KeyAndValue);
|
||||
}
|
||||
|
||||
// 24.2.3.6 Set.prototype.forEach ( callbackfn [ , thisArg ] ), https://tc39.es/ecma262/#sec-set.prototype.foreach
|
||||
// 24.2.3.7 Set.prototype.forEach ( callbackfn [ , thisArg ] ), https://tc39.es/ecma262/#sec-set.prototype.foreach
|
||||
JS_DEFINE_NATIVE_FUNCTION(SetPrototype::for_each)
|
||||
{
|
||||
auto callback_fn = vm.argument(0);
|
||||
|
@ -159,7 +227,7 @@ JS_DEFINE_NATIVE_FUNCTION(SetPrototype::for_each)
|
|||
return js_undefined();
|
||||
}
|
||||
|
||||
// 24.2.3.7 Set.prototype.has ( value ), https://tc39.es/ecma262/#sec-set.prototype.has
|
||||
// 24.2.3.8 Set.prototype.has ( value ), https://tc39.es/ecma262/#sec-set.prototype.has
|
||||
JS_DEFINE_NATIVE_FUNCTION(SetPrototype::has)
|
||||
{
|
||||
auto value = vm.argument(0);
|
||||
|
@ -177,20 +245,229 @@ JS_DEFINE_NATIVE_FUNCTION(SetPrototype::has)
|
|||
return Value(set->set_has(value));
|
||||
}
|
||||
|
||||
// 24.2.3.10 Set.prototype.values ( ), https://tc39.es/ecma262/#sec-set.prototype.values
|
||||
JS_DEFINE_NATIVE_FUNCTION(SetPrototype::values)
|
||||
// 24.2.4.9 Set.prototype.intersection ( other ), https://tc39.es/ecma262/#sec-set.prototype.intersection
|
||||
JS_DEFINE_NATIVE_FUNCTION(SetPrototype::intersection)
|
||||
{
|
||||
auto& realm = *vm.current_realm();
|
||||
|
||||
// 1. Let S be the this value.
|
||||
// NOTE: CreateSetIterator checks the presence of a [[SetData]] slot, so we can do this here.
|
||||
// 1. Let O be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(O, [[SetData]]).
|
||||
auto set = TRY(typed_this_object(vm));
|
||||
|
||||
// 2. Return ? CreateSetIterator(S, value).
|
||||
return SetIterator::create(realm, set, Object::PropertyKind::Value);
|
||||
// 3. Let otherRec be ? GetSetRecord(other).
|
||||
auto other_record = TRY(get_set_record(vm, vm.argument(0)));
|
||||
|
||||
// 4. Let resultSetData be a new empty List.
|
||||
auto result = Set::create(realm);
|
||||
|
||||
// 5. If SetDataSize(O.[[SetData]]) ≤ otherRec.[[Size]], then
|
||||
if (set->set_size() <= other_record.size) {
|
||||
// a. Let thisSize be the number of elements in O.[[SetData]].
|
||||
// b. Let index be 0.
|
||||
// c. Repeat, while index < thisSize,
|
||||
for (auto const& element : *set) {
|
||||
// i. Let e be O.[[SetData]][index].
|
||||
// ii. Set index to index + 1.
|
||||
// iii. If e is not empty, then
|
||||
// 1. Let inOther be ToBoolean(? Call(otherRec.[[Has]], otherRec.[[SetObject]], « e »)).
|
||||
auto in_other = TRY(call(vm, *other_record.has, other_record.set_object, element.key)).to_boolean();
|
||||
|
||||
// 2. If inOther is true, then
|
||||
if (in_other) {
|
||||
// a. NOTE: It is possible for earlier calls to otherRec.[[Has]] to remove and re-add an element of O.[[SetData]], which can cause the same element to be visited twice during this iteration.
|
||||
// b. If SetDataHas(resultSetData, e) is false, then
|
||||
if (!set_data_has(result, element.key)) {
|
||||
// i. Append e to resultSetData.
|
||||
result->set_add(element.key);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. NOTE: The number of elements in O.[[SetData]] may have increased during execution of otherRec.[[Has]].
|
||||
// 4. Set thisSize to the number of elements in O.[[SetData]].
|
||||
}
|
||||
}
|
||||
// 6. Else,
|
||||
else {
|
||||
// a. Let keysIter be ? GetIteratorFromMethod(otherRec.[[SetObject]], otherRec.[[Keys]]).
|
||||
auto keys_iterator = TRY(get_iterator_from_method(vm, other_record.set_object, other_record.keys));
|
||||
|
||||
// b. Let next be NOT-STARTED.
|
||||
Optional<Value> next;
|
||||
|
||||
// c. Repeat, while next is not DONE,
|
||||
do {
|
||||
// i. Set next to ? IteratorStepValue(keysIter).
|
||||
next = TRY(iterator_step_value(vm, keys_iterator));
|
||||
|
||||
// ii. If next is not DONE, then
|
||||
if (next.has_value()) {
|
||||
// 1. Set next to CanonicalizeKeyedCollectionKey(next).
|
||||
next = canonicalize_keyed_collection_key(*next);
|
||||
|
||||
// 2. Let inThis be SetDataHas(O.[[SetData]], next).
|
||||
auto in_this = set_data_has(set, *next);
|
||||
|
||||
// 3. If inThis is true, then
|
||||
if (in_this) {
|
||||
// a. NOTE: Because other is an arbitrary object, it is possible for its "keys" iterator to produce the same value more than once.
|
||||
|
||||
// b. If SetDataHas(resultSetData, next) is false, then
|
||||
if (!set_data_has(result, *next)) {
|
||||
// i. Append next to resultSetData.
|
||||
result->set_add(*next);
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (next.has_value());
|
||||
}
|
||||
|
||||
// 7. Let result be OrdinaryObjectCreate(%Set.prototype%, « [[SetData]] »).
|
||||
// 8. Set result.[[SetData]] to resultSetData.
|
||||
|
||||
// 9. Return result.
|
||||
return result;
|
||||
}
|
||||
|
||||
// 24.2.3.9 get Set.prototype.size, https://tc39.es/ecma262/#sec-get-set.prototype.size
|
||||
// 24.2.4.10 Set.prototype.isDisjointFrom ( other ), https://tc39.es/ecma262/#sec-set.prototype.isdisjointfrom
|
||||
JS_DEFINE_NATIVE_FUNCTION(SetPrototype::is_disjoint_from)
|
||||
{
|
||||
// 1. Let O be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(O, [[SetData]]).
|
||||
auto set = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Let otherRec be ? GetSetRecord(other).
|
||||
auto other_record = TRY(get_set_record(vm, vm.argument(0)));
|
||||
|
||||
// 4. If SetDataSize(O.[[SetData]]) ≤ otherRec.[[Size]], then
|
||||
if (set->set_size() <= other_record.size) {
|
||||
// a. Let thisSize be the number of elements in O.[[SetData]].
|
||||
// b. Let index be 0.
|
||||
// c. Repeat, while index < thisSize,
|
||||
for (auto const& element : *set) {
|
||||
// i. Let e be O.[[SetData]][index].
|
||||
// ii. Set index to index + 1.
|
||||
// iii. If e is not empty, then
|
||||
// 1. Let inOther be ToBoolean(? Call(otherRec.[[Has]], otherRec.[[SetObject]], « e »)).
|
||||
auto in_other = TRY(call(vm, *other_record.has, other_record.set_object, element.key)).to_boolean();
|
||||
|
||||
// 2. If inOther is true, return false.
|
||||
if (in_other)
|
||||
return false;
|
||||
|
||||
// 3. NOTE: The number of elements in O.[[SetData]] may have increased during execution of otherRec.[[Has]].
|
||||
// 4. Set thisSize to the number of elements in O.[[SetData]].
|
||||
}
|
||||
}
|
||||
// 5. Else,
|
||||
else {
|
||||
// a. Let keysIter be ? GetIteratorFromMethod(otherRec.[[SetObject]], otherRec.[[Keys]]).
|
||||
auto keys_iterator = TRY(get_iterator_from_method(vm, other_record.set_object, other_record.keys));
|
||||
|
||||
// b. Let next be NOT-STARTED.
|
||||
Optional<Value> next;
|
||||
|
||||
// c. Repeat, while next is not DONE,
|
||||
do {
|
||||
// i. Set next to ? IteratorStepValue(keysIter).
|
||||
next = TRY(iterator_step_value(vm, keys_iterator));
|
||||
|
||||
// ii. If next is not DONE, then
|
||||
if (next.has_value()) {
|
||||
// 1. If SetDataHas(O.[[SetData]], next) is true, then
|
||||
if (set_data_has(set, *next)) {
|
||||
// a. Perform ? IteratorClose(keysIter, NormalCompletion(UNUSED)).
|
||||
TRY(iterator_close(vm, keys_iterator, normal_completion({})));
|
||||
|
||||
// b. Return false.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} while (next.has_value());
|
||||
}
|
||||
|
||||
// 6. Return true.
|
||||
return true;
|
||||
}
|
||||
|
||||
// 24.2.4.11 Set.prototype.isSubsetOf ( other ), https://tc39.es/ecma262/#sec-set.prototype.issubsetof
|
||||
JS_DEFINE_NATIVE_FUNCTION(SetPrototype::is_subset_of)
|
||||
{
|
||||
// 1. Let O be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(O, [[SetData]]).
|
||||
auto set = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Let otherRec be ? GetSetRecord(other).
|
||||
auto other_record = TRY(get_set_record(vm, vm.argument(0)));
|
||||
|
||||
// 4. If SetDataSize(O.[[SetData]]) > otherRec.[[Size]], return false.
|
||||
if (set->set_size() > other_record.size)
|
||||
return false;
|
||||
|
||||
// 5. Let thisSize be the number of elements in O.[[SetData]].
|
||||
// 6. Let index be 0.
|
||||
// 7. Repeat, while index < thisSize,
|
||||
for (auto const& element : *set) {
|
||||
// a. Let e be O.[[SetData]][index].
|
||||
// b. Set index to index + 1.
|
||||
// c. If e is not empty, then
|
||||
// i. Let inOther be ToBoolean(? Call(otherRec.[[Has]], otherRec.[[SetObject]], « e »)).
|
||||
auto in_other = TRY(call(vm, *other_record.has, other_record.set_object, element.key)).to_boolean();
|
||||
|
||||
// ii. If inOther is false, return false.
|
||||
if (!in_other)
|
||||
return false;
|
||||
|
||||
// iii. NOTE: The number of elements in O.[[SetData]] may have increased during execution of otherRec.[[Has]].
|
||||
// iv. Set thisSize to the number of elements in O.[[SetData]].
|
||||
}
|
||||
|
||||
// 8. Return true.
|
||||
return true;
|
||||
}
|
||||
|
||||
// 24.2.4.12 Set.prototype.isSupersetOf ( other ), https://tc39.es/ecma262/#sec-set.prototype.issupersetof
|
||||
JS_DEFINE_NATIVE_FUNCTION(SetPrototype::is_superset_of)
|
||||
{
|
||||
// 1. Let O be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(O, [[SetData]]).
|
||||
auto set = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Let otherRec be ? GetSetRecord(other).
|
||||
auto other_record = TRY(get_set_record(vm, vm.argument(0)));
|
||||
|
||||
// 4. If SetDataSize(O.[[SetData]]) < otherRec.[[Size]], return false.
|
||||
if (set->set_size() < other_record.size)
|
||||
return false;
|
||||
|
||||
// 5. Let keysIter be ? GetIteratorFromMethod(otherRec.[[SetObject]], otherRec.[[Keys]]).
|
||||
auto keys_iterator = TRY(get_iterator_from_method(vm, other_record.set_object, other_record.keys));
|
||||
|
||||
// 6. Let next be NOT-STARTED.
|
||||
Optional<Value> next;
|
||||
|
||||
// 7. Repeat, while next is not DONE,
|
||||
do {
|
||||
// a. Set next to ? IteratorStepValue(keysIter).
|
||||
next = TRY(iterator_step_value(vm, keys_iterator));
|
||||
|
||||
// b. If next is not DONE, then
|
||||
if (next.has_value()) {
|
||||
// i. If SetDataHas(O.[[SetData]], next) is false, then
|
||||
if (!set_data_has(set, *next)) {
|
||||
// 1. Perform ? IteratorClose(keysIter, NormalCompletion(UNUSED)).
|
||||
TRY(iterator_close(vm, keys_iterator, normal_completion({})));
|
||||
|
||||
// 2. Return false.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} while (next.has_value());
|
||||
|
||||
// 8. Return true.
|
||||
return true;
|
||||
}
|
||||
|
||||
// 24.2.3.14 get Set.prototype.size, https://tc39.es/ecma262/#sec-get-set.prototype.size
|
||||
JS_DEFINE_NATIVE_FUNCTION(SetPrototype::size_getter)
|
||||
{
|
||||
// 1. Let S be the this value.
|
||||
|
@ -206,255 +483,7 @@ JS_DEFINE_NATIVE_FUNCTION(SetPrototype::size_getter)
|
|||
return Value(count);
|
||||
}
|
||||
|
||||
// 8 Set Records, https://tc39.es/proposal-set-methods/#sec-set-records
|
||||
struct SetRecord {
|
||||
NonnullGCPtr<Object const> set; // [[Set]]
|
||||
double size { 0 }; // [[Size]
|
||||
NonnullGCPtr<FunctionObject> has; // [[Has]]
|
||||
NonnullGCPtr<FunctionObject> keys; // [[Keys]]
|
||||
};
|
||||
|
||||
// 9 GetSetRecord ( obj ), https://tc39.es/proposal-set-methods/#sec-getsetrecord
|
||||
static ThrowCompletionOr<SetRecord> get_set_record(VM& vm, Value value)
|
||||
{
|
||||
// 1. If obj is not an Object, throw a TypeError exception.
|
||||
if (!value.is_object())
|
||||
return vm.throw_completion<TypeError>(ErrorType::NotAnObject, value.to_string_without_side_effects());
|
||||
auto const& object = value.as_object();
|
||||
|
||||
// 2. Let rawSize be ? Get(obj, "size").
|
||||
auto raw_size = TRY(object.get(vm.names.size));
|
||||
|
||||
// 3. Let numSize be ? ToNumber(rawSize).
|
||||
auto number_size = TRY(raw_size.to_number(vm));
|
||||
|
||||
// 4. NOTE: If rawSize is undefined, then numSize will be NaN.
|
||||
// 5. If numSize is NaN, throw a TypeError exception.
|
||||
if (number_size.is_nan())
|
||||
return vm.throw_completion<TypeError>(ErrorType::NumberIsNaN, "size"sv);
|
||||
|
||||
// 6. Let intSize be ! ToIntegerOrInfinity(numSize).
|
||||
auto integer_size = MUST(number_size.to_integer_or_infinity(vm));
|
||||
|
||||
// 7. If intSize < 0, throw a RangeError exception.
|
||||
if (integer_size < 0)
|
||||
return vm.throw_completion<RangeError>(ErrorType::NumberIsNegative, "size"sv);
|
||||
|
||||
// 8. Let has be ? Get(obj, "has").
|
||||
auto has = TRY(object.get(vm.names.has));
|
||||
|
||||
// 9. If IsCallable(has) is false, throw a TypeError exception.
|
||||
if (!has.is_function())
|
||||
return vm.throw_completion<TypeError>(ErrorType::NotAFunction, has.to_string_without_side_effects());
|
||||
|
||||
// 10. Let keys be ? Get(obj, "keys").
|
||||
auto keys = TRY(object.get(vm.names.keys));
|
||||
|
||||
// 11. If IsCallable(keys) is false, throw a TypeError exception.
|
||||
if (!keys.is_function())
|
||||
return vm.throw_completion<TypeError>(ErrorType::NotAFunction, keys.to_string_without_side_effects());
|
||||
|
||||
// 12. Return a new Set Record { [[Set]]: obj, [[Size]]: intSize, [[Has]]: has, [[Keys]]: keys }.
|
||||
return SetRecord { .set = object, .size = integer_size, .has = has.as_function(), .keys = keys.as_function() };
|
||||
}
|
||||
|
||||
// 1 Set.prototype.union ( other ), https://tc39.es/proposal-set-methods/#sec-set.prototype.union
|
||||
JS_DEFINE_NATIVE_FUNCTION(SetPrototype::union_)
|
||||
{
|
||||
// 1. Let O be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(O, [[SetData]]).
|
||||
auto set = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Let otherRec be ? GetSetRecord(other).
|
||||
auto other_record = TRY(get_set_record(vm, vm.argument(0)));
|
||||
|
||||
// 4. Let keysIter be ? GetIteratorFromMethod(otherRec.[[Set]], otherRec.[[Keys]]).
|
||||
auto keys_iterator = TRY(get_iterator_from_method(vm, other_record.set, other_record.keys));
|
||||
|
||||
// 5. Let resultSetData be a copy of O.[[SetData]].
|
||||
auto result = set->copy();
|
||||
|
||||
// 6. Let next be true.
|
||||
auto next = true;
|
||||
|
||||
// 7. Repeat, while next is not false,
|
||||
while (next) {
|
||||
// a. Set next to ? IteratorStep(keysIter).
|
||||
auto iterator_result = TRY(iterator_step(vm, keys_iterator));
|
||||
next = iterator_result;
|
||||
|
||||
// b. If next is not false, then
|
||||
if (next) {
|
||||
// i. Let nextValue be ? IteratorValue(next).
|
||||
auto next_value = TRY(iterator_value(vm, *iterator_result));
|
||||
|
||||
// ii. If nextValue is -0𝔽, set nextValue to +0𝔽.
|
||||
if (next_value.is_negative_zero())
|
||||
next_value = Value(0);
|
||||
|
||||
// iii. If SetDataHas(resultSetData, nextValue) is false, then
|
||||
// 1. Append nextValue to resultSetData.
|
||||
result->set_add(next_value);
|
||||
}
|
||||
}
|
||||
|
||||
// 8. Let result be OrdinaryObjectCreate(%Set.prototype%, « [[SetData]] »).
|
||||
// 9. Set result.[[SetData]] to resultSetData.
|
||||
|
||||
// 10. Return result.
|
||||
return result;
|
||||
}
|
||||
|
||||
// 2 Set.prototype.intersection ( other ), https://tc39.es/proposal-set-methods/#sec-set.prototype.intersection
|
||||
JS_DEFINE_NATIVE_FUNCTION(SetPrototype::intersection)
|
||||
{
|
||||
auto& realm = *vm.current_realm();
|
||||
|
||||
// 1. Let O be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(O, [[SetData]]).
|
||||
auto set = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Let otherRec be ? GetSetRecord(other).
|
||||
auto other_record = TRY(get_set_record(vm, vm.argument(0)));
|
||||
|
||||
// 4. Let resultSetData be a new empty List.
|
||||
auto result = Set::create(realm);
|
||||
|
||||
// 5. Let thisSize be the number of elements in O.[[SetData]].
|
||||
auto this_size = set->set_size();
|
||||
|
||||
// 6. If thisSize ≤ otherRec.[[Size]], then
|
||||
if (this_size <= other_record.size) {
|
||||
// a. For each element e of O.[[SetData]], do
|
||||
for (auto& element : *set) {
|
||||
// i. If e is not empty, then
|
||||
// 1. Let inOther be ToBoolean(? Call(otherRec.[[Has]], otherRec.[[Set]], « e »)).
|
||||
auto in_other = TRY(call(vm, *other_record.has, other_record.set, element.key)).to_boolean();
|
||||
// 2. If inOther is true, then
|
||||
if (in_other) {
|
||||
// a. Append e to resultSetData.
|
||||
result->set_add(element.key);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 7. Else,
|
||||
else {
|
||||
// a. Let keysIter be ? GetIteratorFromMethod(otherRec.[[Set]], otherRec.[[Keys]]).
|
||||
auto keys_iterator = TRY(get_iterator_from_method(vm, other_record.set, other_record.keys));
|
||||
|
||||
// b. Let next be true.
|
||||
auto next = true;
|
||||
|
||||
// c. Repeat, while next is not false,
|
||||
while (next) {
|
||||
// i. Set next to ? IteratorStep(keysIter).
|
||||
auto iterator_result = TRY(iterator_step(vm, keys_iterator));
|
||||
next = iterator_result;
|
||||
|
||||
// ii. If next is not false, then
|
||||
if (next) {
|
||||
// 1. Let nextValue be ? IteratorValue(next).
|
||||
auto next_value = TRY(iterator_value(vm, *iterator_result));
|
||||
|
||||
// 2. If nextValue is -0𝔽, set nextValue to +0𝔽.
|
||||
if (next_value.is_negative_zero())
|
||||
next_value = Value(0);
|
||||
|
||||
// 3. NOTE: Because other is an arbitrary object, it is possible for its "keys" iterator to produce the same value more than once.
|
||||
// 4. Let alreadyInResult be SetDataHas(resultSetData, nextValue).
|
||||
// 5. Let inThis be SetDataHas(O.[[SetData]], nextValue).
|
||||
auto in_this = set->set_has(next_value);
|
||||
// 6. If alreadyInResult is false and inThis is true, then
|
||||
if (in_this) {
|
||||
// a. Append nextValue to resultSetData.
|
||||
result->set_add(next_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// d. NOTE: It is possible for resultSetData not to be a subset of O.[[SetData]] at this point because arbitrary code may have been executed by the iterator, including code which modifies O.[[SetData]].
|
||||
|
||||
// e. Sort the elements of resultSetData so that all elements which are also in O.[[SetData]] are ordered as they are in O.[[SetData]], and any additional elements are moved to the end of the list in the same order as they were before sorting resultSetData.
|
||||
// FIXME: This is not possible with the current underlying m_values implementation
|
||||
}
|
||||
|
||||
// 8. Let result be OrdinaryObjectCreate(%Set.prototype%, « [[SetData]] »).
|
||||
// 9. Set result.[[SetData]] to resultSetData.
|
||||
|
||||
// 10. Return result.
|
||||
return result;
|
||||
}
|
||||
|
||||
// 3 Set.prototype.difference ( other ), https://tc39.es/proposal-set-methods/#sec-set.prototype.difference
|
||||
JS_DEFINE_NATIVE_FUNCTION(SetPrototype::difference)
|
||||
{
|
||||
// 1. Let O be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(O, [[SetData]]).
|
||||
auto set = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Let otherRec be ? GetSetRecord(other).
|
||||
auto other_record = TRY(get_set_record(vm, vm.argument(0)));
|
||||
|
||||
// 4. Let resultSetData be a copy of O.[[SetData]].
|
||||
auto result = set->copy();
|
||||
|
||||
// 5. Let thisSize be the number of elements in O.[[SetData]].
|
||||
auto this_size = set->set_size();
|
||||
|
||||
// 6. If thisSize ≤ otherRec.[[Size]], then
|
||||
if (this_size <= other_record.size) {
|
||||
// a. For each element e of resultSetData, do
|
||||
for (auto& element : *set) {
|
||||
// i. If e is not empty, then
|
||||
// 1. Let inOther be ToBoolean(? Call(otherRec.[[Has]], otherRec.[[Set]], « e »)).
|
||||
auto in_other = TRY(call(vm, *other_record.has, other_record.set, element.key)).to_boolean();
|
||||
// 2. If inOther is true, then
|
||||
if (in_other) {
|
||||
// a. Remove e from resultSetData.
|
||||
result->set_remove(element.key);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 7. Else,
|
||||
else {
|
||||
// a. Let keysIter be ? GetIteratorFromMethod(otherRec.[[Set]], otherRec.[[Keys]]).
|
||||
auto keys_iterator = TRY(get_iterator_from_method(vm, other_record.set, other_record.keys));
|
||||
|
||||
// b. Let next be true.
|
||||
auto next = true;
|
||||
|
||||
// c. Repeat, while next is not false,
|
||||
while (next) {
|
||||
// i. Set next to ? IteratorStep(keysIter).
|
||||
auto iterator_result = TRY(iterator_step(vm, keys_iterator));
|
||||
next = iterator_result;
|
||||
|
||||
// ii. If next is not false, then
|
||||
if (next) {
|
||||
// 1. Let nextValue be ? IteratorValue(next).
|
||||
auto next_value = TRY(iterator_value(vm, *iterator_result));
|
||||
|
||||
// 2. If nextValue is -0𝔽, set nextValue to +0𝔽.
|
||||
if (next_value.is_negative_zero())
|
||||
next_value = Value(0);
|
||||
|
||||
// 3. If SetDataHas(resultSetData, nextValue) is true, then
|
||||
if (result->set_has(next_value)) {
|
||||
// a. Remove nextValue from resultSetData.
|
||||
result->set_remove(next_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 8. Let result be OrdinaryObjectCreate(%Set.prototype%, « [[SetData]] »).
|
||||
// 9. Set result.[[SetData]] to resultSetData.
|
||||
|
||||
// 10. Return result.
|
||||
return result;
|
||||
}
|
||||
|
||||
// 4 Set.prototype.symmetricDifference ( other ), https://tc39.es/proposal-set-methods/#sec-set.prototype.symmetricdifference
|
||||
// 24.2.4.15 Set.prototype.symmetricDifference ( other ), https://tc39.es/ecma262/#sec-set.prototype.symmetricdifference
|
||||
JS_DEFINE_NATIVE_FUNCTION(SetPrototype::symmetric_difference)
|
||||
{
|
||||
// 1. Let O be the this value.
|
||||
|
@ -464,43 +493,43 @@ JS_DEFINE_NATIVE_FUNCTION(SetPrototype::symmetric_difference)
|
|||
// 3. Let otherRec be ? GetSetRecord(other).
|
||||
auto other_record = TRY(get_set_record(vm, vm.argument(0)));
|
||||
|
||||
// 4. Let keysIter be ? GetIteratorFromMethod(otherRec.[[Set]], otherRec.[[Keys]]).
|
||||
auto keys_iterator = TRY(get_iterator_from_method(vm, other_record.set, other_record.keys));
|
||||
// 4. Let keysIter be ? GetIteratorFromMethod(otherRec.[[SetObject]], otherRec.[[Keys]]).
|
||||
auto keys_iterator = TRY(get_iterator_from_method(vm, other_record.set_object, other_record.keys));
|
||||
|
||||
// 5. Let resultSetData be a copy of O.[[SetData]].
|
||||
auto result = set->copy();
|
||||
|
||||
// 6. Let next be true.
|
||||
auto next = true;
|
||||
// 6. Let next be NOT-STARTED.
|
||||
Optional<Value> next;
|
||||
|
||||
// 7. Repeat, while next is not false,
|
||||
while (next) {
|
||||
// a. Set next to ? IteratorStep(keysIter).
|
||||
auto iterator_result = TRY(iterator_step(vm, keys_iterator));
|
||||
next = iterator_result;
|
||||
// 7. Repeat, while next is not DONE,
|
||||
do {
|
||||
// a. Set next to ? IteratorStepValue(keysIter).
|
||||
next = TRY(iterator_step_value(vm, keys_iterator));
|
||||
|
||||
// b. If next is not false, then
|
||||
if (next) {
|
||||
// i. Let nextValue be ? IteratorValue(next).
|
||||
auto next_value = TRY(iterator_value(vm, *iterator_result));
|
||||
// b. If next is not DONE, then
|
||||
if (next.has_value()) {
|
||||
// i. Set next to CanonicalizeKeyedCollectionKey(next).
|
||||
next = canonicalize_keyed_collection_key(*next);
|
||||
|
||||
// ii. If nextValue is -0𝔽, set nextValue to +0𝔽.
|
||||
if (next_value.is_negative_zero())
|
||||
next_value = Value(0);
|
||||
// ii. Let resultIndex be SetDataIndex(resultSetData, next).
|
||||
// iii. If resultIndex is not-found, let alreadyInResult be false. Otherwise let alreadyInResult be true.
|
||||
auto already_in_result = result->set_has(*next);
|
||||
|
||||
// iii. Let inResult be SetDataHas(resultSetData, nextValue).
|
||||
// iv. If SetDataHas(O.[[SetData]], nextValue) is true, then
|
||||
if (set->set_has(next_value)) {
|
||||
// 1. If inResult is true, remove nextValue from resultSetData.
|
||||
result->set_remove(next_value);
|
||||
// iv. If SetDataHas(O.[[SetData]], next) is true, then
|
||||
if (set_data_has(set, *next)) {
|
||||
// 1. If alreadyInResult is true, set resultSetData[resultIndex] to empty.
|
||||
if (already_in_result)
|
||||
result->set_remove(*next);
|
||||
}
|
||||
// v. Else,
|
||||
else {
|
||||
// 1. If inResult is false, append nextValue to resultSetData.
|
||||
result->set_add(next_value);
|
||||
// 1. If alreadyInResult is false, append next to resultSetData.
|
||||
if (!already_in_result)
|
||||
result->set_add(*next);
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (next.has_value());
|
||||
|
||||
// 8. Let result be OrdinaryObjectCreate(%Set.prototype%, « [[SetData]] »).
|
||||
// 9. Set result.[[SetData]] to resultSetData.
|
||||
|
@ -509,8 +538,8 @@ JS_DEFINE_NATIVE_FUNCTION(SetPrototype::symmetric_difference)
|
|||
return result;
|
||||
}
|
||||
|
||||
// 5 Set.prototype.isSubsetOf ( other ), https://tc39.es/proposal-set-methods/#sec-set.prototype.issubsetof
|
||||
JS_DEFINE_NATIVE_FUNCTION(SetPrototype::is_subset_of)
|
||||
// 24.2.4.16 Set.prototype.union ( other ), https://tc39.es/ecma262/#sec-set.prototype.union
|
||||
JS_DEFINE_NATIVE_FUNCTION(SetPrototype::union_)
|
||||
{
|
||||
// 1. Let O be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(O, [[SetData]]).
|
||||
|
@ -519,124 +548,51 @@ JS_DEFINE_NATIVE_FUNCTION(SetPrototype::is_subset_of)
|
|||
// 3. Let otherRec be ? GetSetRecord(other).
|
||||
auto other_record = TRY(get_set_record(vm, vm.argument(0)));
|
||||
|
||||
// 4. Let thisSize be the number of elements in O.[[SetData]].
|
||||
auto this_size = set->set_size();
|
||||
// 4. Let keysIter be ? GetIteratorFromMethod(otherRec.[[SetObject]], otherRec.[[Keys]]).
|
||||
auto keys_iterator = TRY(get_iterator_from_method(vm, other_record.set_object, other_record.keys));
|
||||
|
||||
// 5. If thisSize > otherRec.[[Size]], return false.
|
||||
if (this_size > other_record.size)
|
||||
return false;
|
||||
// 5. Let resultSetData be a copy of O.[[SetData]].
|
||||
auto result = set->copy();
|
||||
|
||||
// 6. For each element e of O.[[SetData]], do
|
||||
for (auto& element : *set) {
|
||||
// a. Let inOther be ToBoolean(? Call(otherRec.[[Has]], otherRec.[[Set]], « e »)).
|
||||
auto in_other = TRY(call(vm, *other_record.has, other_record.set, element.key)).to_boolean();
|
||||
// 6. Let next be NOT-STARTED.
|
||||
Optional<Value> next;
|
||||
|
||||
// b. If inOther is false, return false.
|
||||
if (!in_other)
|
||||
return false;
|
||||
}
|
||||
// 7. Repeat, while next is not DONE,
|
||||
do {
|
||||
// a. Set next to ? IteratorStepValue(keysIter).
|
||||
next = TRY(iterator_step_value(vm, keys_iterator));
|
||||
|
||||
// 7. Return true.
|
||||
return true;
|
||||
}
|
||||
// b. If next is not DONE, then
|
||||
if (next.has_value()) {
|
||||
// i. Set next to CanonicalizeKeyedCollectionKey(next).
|
||||
next = canonicalize_keyed_collection_key(*next);
|
||||
|
||||
// 6 Set.prototype.isSupersetOf ( other ), https://tc39.es/proposal-set-methods/#sec-set.prototype.issupersetof
|
||||
JS_DEFINE_NATIVE_FUNCTION(SetPrototype::is_superset_of)
|
||||
{
|
||||
// 1. Let O be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(O, [[SetData]]).
|
||||
auto set = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Let otherRec be ? GetSetRecord(other).
|
||||
auto other_record = TRY(get_set_record(vm, vm.argument(0)));
|
||||
|
||||
// 4. Let thisSize be the number of elements in O.[[SetData]].
|
||||
auto this_size = set->set_size();
|
||||
|
||||
// 5. If thisSize < otherRec.[[Size]], return false.
|
||||
if (this_size < other_record.size)
|
||||
return false;
|
||||
|
||||
// 6. Let keysIter be ? GetIteratorFromMethod(otherRec.[[Set]], otherRec.[[Keys]]).
|
||||
auto keys_iterator = TRY(get_iterator_from_method(vm, other_record.set, other_record.keys));
|
||||
|
||||
// 7. Let next be true.
|
||||
auto next = true;
|
||||
|
||||
// 8. Repeat, while next is not false,
|
||||
while (next) {
|
||||
// a. Set next to ? IteratorStep(keysIter).
|
||||
auto iterator_result = TRY(iterator_step(vm, keys_iterator));
|
||||
next = iterator_result;
|
||||
|
||||
// b. If next is not false, then
|
||||
if (next) {
|
||||
// i. Let nextValue be ? IteratorValue(next).
|
||||
auto next_value = TRY(iterator_value(vm, *iterator_result));
|
||||
|
||||
// ii. If SetDataHas(O.[[SetData]], nextValue) is false, return false.
|
||||
if (!set->set_has(next_value))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 9. Return true.
|
||||
return true;
|
||||
}
|
||||
|
||||
// 7 Set.prototype.isDisjointFrom ( other ), https://tc39.es/proposal-set-methods/#sec-set.prototype.isdisjointfrom
|
||||
JS_DEFINE_NATIVE_FUNCTION(SetPrototype::is_disjoint_from)
|
||||
{
|
||||
// 1. Let O be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(O, [[SetData]]).
|
||||
auto set = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Let otherRec be ? GetSetRecord(other).
|
||||
auto other_record = TRY(get_set_record(vm, vm.argument(0)));
|
||||
|
||||
// 4. Let thisSize be the number of elements in O.[[SetData]].
|
||||
auto this_size = set->set_size();
|
||||
|
||||
// 5. If thisSize ≤ otherRec.[[Size]], then
|
||||
if (this_size <= other_record.size) {
|
||||
// a. For each element e of O.[[SetData]], do
|
||||
for (auto& element : *set) {
|
||||
// i. If e is not empty, then
|
||||
// 1. Let inOther be ToBoolean(? Call(otherRec.[[Has]], otherRec.[[Set]], « e »)).
|
||||
auto in_other = TRY(call(vm, *other_record.has, other_record.set, element.key)).to_boolean();
|
||||
// 2. If inOther is true, return false.
|
||||
if (in_other)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// 6. Else,
|
||||
else {
|
||||
// a. Let keysIter be ? GetIteratorFromMethod(otherRec.[[Set]], otherRec.[[Keys]]).
|
||||
auto keys_iterator = TRY(get_iterator_from_method(vm, other_record.set, other_record.keys));
|
||||
|
||||
// b. Let next be true.
|
||||
auto next = true;
|
||||
|
||||
// c. Repeat, while next is not false,
|
||||
while (next) {
|
||||
// i. Set next to ? IteratorStep(keysIter).
|
||||
auto iterator_result = TRY(iterator_step(vm, keys_iterator));
|
||||
next = iterator_result;
|
||||
|
||||
// ii. If next is not false, then
|
||||
if (next) {
|
||||
// 1. Let nextValue be ? IteratorValue(next).
|
||||
auto next_value = TRY(iterator_value(vm, *iterator_result));
|
||||
|
||||
// 2. If SetDataHas(O.[[SetData]], nextValue) is true, return false.
|
||||
if (set->set_has(next_value))
|
||||
return false;
|
||||
// ii. If SetDataHas(resultSetData, next) is false, then
|
||||
if (!set_data_has(result, *next)) {
|
||||
// 1. Append next to resultSetData.
|
||||
result->set_add(*next);
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (next.has_value());
|
||||
|
||||
// 7. Return true.
|
||||
return true;
|
||||
// 8. Let result be OrdinaryObjectCreate(%Set.prototype%, « [[SetData]] »).
|
||||
// 9. Set result.[[SetData]] to resultSetData.
|
||||
|
||||
// 10. Return result.
|
||||
return result;
|
||||
}
|
||||
|
||||
// 24.2.3.17 Set.prototype.values ( ), https://tc39.es/ecma262/#sec-set.prototype.values
|
||||
JS_DEFINE_NATIVE_FUNCTION(SetPrototype::values)
|
||||
{
|
||||
auto& realm = *vm.current_realm();
|
||||
|
||||
// 1. Let S be the this value.
|
||||
// NOTE: CreateSetIterator checks the presence of a [[SetData]] slot, so we can do this here.
|
||||
auto set = TRY(typed_this_object(vm));
|
||||
|
||||
// 2. Return ? CreateSetIterator(S, value).
|
||||
return SetIterator::create(realm, set, Object::PropertyKind::Value);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -25,19 +25,18 @@ private:
|
|||
JS_DECLARE_NATIVE_FUNCTION(add);
|
||||
JS_DECLARE_NATIVE_FUNCTION(clear);
|
||||
JS_DECLARE_NATIVE_FUNCTION(delete_);
|
||||
JS_DECLARE_NATIVE_FUNCTION(difference);
|
||||
JS_DECLARE_NATIVE_FUNCTION(entries);
|
||||
JS_DECLARE_NATIVE_FUNCTION(for_each);
|
||||
JS_DECLARE_NATIVE_FUNCTION(has);
|
||||
JS_DECLARE_NATIVE_FUNCTION(values);
|
||||
JS_DECLARE_NATIVE_FUNCTION(union_);
|
||||
JS_DECLARE_NATIVE_FUNCTION(intersection);
|
||||
JS_DECLARE_NATIVE_FUNCTION(difference);
|
||||
JS_DECLARE_NATIVE_FUNCTION(symmetric_difference);
|
||||
JS_DECLARE_NATIVE_FUNCTION(is_disjoint_from);
|
||||
JS_DECLARE_NATIVE_FUNCTION(is_subset_of);
|
||||
JS_DECLARE_NATIVE_FUNCTION(is_superset_of);
|
||||
JS_DECLARE_NATIVE_FUNCTION(is_disjoint_from);
|
||||
|
||||
JS_DECLARE_NATIVE_FUNCTION(size_getter);
|
||||
JS_DECLARE_NATIVE_FUNCTION(symmetric_difference);
|
||||
JS_DECLARE_NATIVE_FUNCTION(union_);
|
||||
JS_DECLARE_NATIVE_FUNCTION(values);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue