summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibJS
diff options
context:
space:
mode:
authorIdan Horowitz <idan.horowitz@gmail.com>2022-12-01 22:17:43 +0200
committerLinus Groh <mail@linusgroh.de>2022-12-02 13:09:15 +0100
commit9e693304ff4caac4c6e3882a2979f3fdf89f20e4 (patch)
treebbdc2cf0e78cd7cb8d4ce7af79ada831139abe36 /Userland/Libraries/LibJS
parentfee65f6453186e936e5bd1c4c29e48e4f2e31e99 (diff)
downloadserenity-9e693304ff4caac4c6e3882a2979f3fdf89f20e4.zip
LibJS: Implement Set.prototype.intersection
Diffstat (limited to 'Userland/Libraries/LibJS')
-rw-r--r--Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h1
-rw-r--r--Userland/Libraries/LibJS/Runtime/SetPrototype.cpp81
-rw-r--r--Userland/Libraries/LibJS/Runtime/SetPrototype.h1
-rw-r--r--Userland/Libraries/LibJS/Tests/builtins/Set/Set.prototype.intersection.js12
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());
+ }
+});