1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 21:27:45 +00:00

LibJS: Implement Set.prototype.intersection

This commit is contained in:
Idan Horowitz 2022-12-01 22:17:43 +02:00 committed by Linus Groh
parent fee65f6453
commit 9e693304ff
4 changed files with 95 additions and 0 deletions

View file

@ -287,6 +287,7 @@ namespace JS {
P(inLeapYear) \
P(input) \
P(instant) \
P(intersection) \
P(is) \
P(isArray) \
P(isExtensible) \

View file

@ -33,6 +33,7 @@ void SetPrototype::initialize(Realm& realm)
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_accessor(realm, vm.names.size, size_getter, {}, Attribute::Configurable);
define_direct_property(vm.names.keys, get_without_side_effects(vm.names.values), attr);
@ -232,4 +233,84 @@ JS_DEFINE_NATIVE_FUNCTION(SetPrototype::union_)
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 ? GetKeysIterator(otherRec).
auto keys_iterator = TRY(get_keys_iterator(vm, other_record));
// 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;
}
}

View file

@ -29,6 +29,7 @@ private:
JS_DECLARE_NATIVE_FUNCTION(has);
JS_DECLARE_NATIVE_FUNCTION(values);
JS_DECLARE_NATIVE_FUNCTION(union_);
JS_DECLARE_NATIVE_FUNCTION(intersection);
JS_DECLARE_NATIVE_FUNCTION(size_getter);
};

View file

@ -0,0 +1,12 @@
test("basic functionality", () => {
expect(Set.prototype.intersection).toHaveLength(1);
const set1 = new Set(["a", "b", "c"]);
const set2 = new Set(["b", "c", "d", "e"]);
const intersection1to2 = set1.intersection(set2);
const intersection2to1 = set2.intersection(set1);
for (const intersectionSet of [intersection1to2, intersection2to1]) {
expect(intersectionSet).toHaveSize(2);
["b", "c"].forEach(value => expect(intersectionSet.has(value)).toBeTrue());
}
});