diff options
author | Idan Horowitz <idan.horowitz@gmail.com> | 2022-12-01 22:17:43 +0200 |
---|---|---|
committer | Linus Groh <mail@linusgroh.de> | 2022-12-02 13:09:15 +0100 |
commit | 9e693304ff4caac4c6e3882a2979f3fdf89f20e4 (patch) | |
tree | bbdc2cf0e78cd7cb8d4ce7af79ada831139abe36 /Userland/Libraries/LibJS | |
parent | fee65f6453186e936e5bd1c4c29e48e4f2e31e99 (diff) | |
download | serenity-9e693304ff4caac4c6e3882a2979f3fdf89f20e4.zip |
LibJS: Implement Set.prototype.intersection
Diffstat (limited to 'Userland/Libraries/LibJS')
4 files changed, 95 insertions, 0 deletions
diff --git a/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h b/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h index d24ba563e3..1d20867a85 100644 --- a/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h +++ b/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h @@ -287,6 +287,7 @@ namespace JS { P(inLeapYear) \ P(input) \ P(instant) \ + P(intersection) \ P(is) \ P(isArray) \ P(isExtensible) \ diff --git a/Userland/Libraries/LibJS/Runtime/SetPrototype.cpp b/Userland/Libraries/LibJS/Runtime/SetPrototype.cpp index d29b51c5c8..c102db34df 100644 --- a/Userland/Libraries/LibJS/Runtime/SetPrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/SetPrototype.cpp @@ -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; +} + } diff --git a/Userland/Libraries/LibJS/Runtime/SetPrototype.h b/Userland/Libraries/LibJS/Runtime/SetPrototype.h index d3cc3ac401..77452ec30b 100644 --- a/Userland/Libraries/LibJS/Runtime/SetPrototype.h +++ b/Userland/Libraries/LibJS/Runtime/SetPrototype.h @@ -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); }; diff --git a/Userland/Libraries/LibJS/Tests/builtins/Set/Set.prototype.intersection.js b/Userland/Libraries/LibJS/Tests/builtins/Set/Set.prototype.intersection.js new file mode 100644 index 0000000000..98ce3f1c96 --- /dev/null +++ b/Userland/Libraries/LibJS/Tests/builtins/Set/Set.prototype.intersection.js @@ -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()); + } +}); |