summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h1
-rw-r--r--Userland/Libraries/LibJS/Runtime/Set.cpp12
-rw-r--r--Userland/Libraries/LibJS/Runtime/Set.h2
-rw-r--r--Userland/Libraries/LibJS/Runtime/SetPrototype.cpp48
-rw-r--r--Userland/Libraries/LibJS/Runtime/SetPrototype.h1
-rw-r--r--Userland/Libraries/LibJS/Tests/builtins/Set/Set.prototype.union.js12
6 files changed, 76 insertions, 0 deletions
diff --git a/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h b/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h
index 8012c68bd1..d24ba563e3 100644
--- a/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h
+++ b/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h
@@ -581,6 +581,7 @@ struct CommonPropertyNames {
PropertyKey register_ { "register", PropertyKey::StringMayBeNumber::No };
PropertyKey return_ { "return", PropertyKey::StringMayBeNumber::No };
PropertyKey throw_ { "throw", PropertyKey::StringMayBeNumber::No };
+ PropertyKey union_ { "union", PropertyKey::StringMayBeNumber::No };
PropertyKey xor_ { "xor", PropertyKey::StringMayBeNumber::No };
PropertyKey inputAlias { "$_", PropertyKey::StringMayBeNumber::No };
PropertyKey lastMatchAlias { "$&", PropertyKey::StringMayBeNumber::No };
diff --git a/Userland/Libraries/LibJS/Runtime/Set.cpp b/Userland/Libraries/LibJS/Runtime/Set.cpp
index 9d6b7e57a3..120ac92f3f 100644
--- a/Userland/Libraries/LibJS/Runtime/Set.cpp
+++ b/Userland/Libraries/LibJS/Runtime/Set.cpp
@@ -23,6 +23,18 @@ void Set::initialize(Realm& realm)
m_values = Map::create(realm);
}
+NonnullGCPtr<Set> Set::copy() const
+{
+ auto& vm = this->vm();
+ auto& realm = *vm.current_realm();
+ // FIXME: This is very inefficient, but there's no better way to do this at the moment, as the underlying Map
+ // implementation of m_values uses a non-copyable RedBlackTree.
+ auto* result = Set::create(realm);
+ for (auto const& entry : *this)
+ result->set_add(entry.key);
+ return *result;
+}
+
void Set::visit_edges(Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
diff --git a/Userland/Libraries/LibJS/Runtime/Set.h b/Userland/Libraries/LibJS/Runtime/Set.h
index 05523aee96..d855389bdc 100644
--- a/Userland/Libraries/LibJS/Runtime/Set.h
+++ b/Userland/Libraries/LibJS/Runtime/Set.h
@@ -36,6 +36,8 @@ public:
auto begin() { return m_values->begin(); }
auto end() const { return m_values->end(); }
+ NonnullGCPtr<Set> copy() const;
+
private:
explicit Set(Object& prototype);
diff --git a/Userland/Libraries/LibJS/Runtime/SetPrototype.cpp b/Userland/Libraries/LibJS/Runtime/SetPrototype.cpp
index 96b5a53543..d29b51c5c8 100644
--- a/Userland/Libraries/LibJS/Runtime/SetPrototype.cpp
+++ b/Userland/Libraries/LibJS/Runtime/SetPrototype.cpp
@@ -32,6 +32,7 @@ void SetPrototype::initialize(Realm& realm)
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_accessor(realm, vm.names.size, size_getter, {}, Attribute::Configurable);
define_direct_property(vm.names.keys, get_without_side_effects(vm.names.values), attr);
@@ -184,4 +185,51 @@ static ThrowCompletionOr<Iterator> get_keys_iterator(VM& vm, SetRecord const& se
return Iterator { .iterator = &keys_iterator.as_object(), .next_method = next_method, .done = false };
}
+// 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 ? GetKeysIterator(otherRec).
+ auto keys_iterator = TRY(get_keys_iterator(vm, other_record));
+
+ // 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;
+}
+
}
diff --git a/Userland/Libraries/LibJS/Runtime/SetPrototype.h b/Userland/Libraries/LibJS/Runtime/SetPrototype.h
index 824bf91d5d..d3cc3ac401 100644
--- a/Userland/Libraries/LibJS/Runtime/SetPrototype.h
+++ b/Userland/Libraries/LibJS/Runtime/SetPrototype.h
@@ -28,6 +28,7 @@ private:
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(size_getter);
};
diff --git a/Userland/Libraries/LibJS/Tests/builtins/Set/Set.prototype.union.js b/Userland/Libraries/LibJS/Tests/builtins/Set/Set.prototype.union.js
new file mode 100644
index 0000000000..d0469bcfb7
--- /dev/null
+++ b/Userland/Libraries/LibJS/Tests/builtins/Set/Set.prototype.union.js
@@ -0,0 +1,12 @@
+test("basic functionality", () => {
+ expect(Set.prototype.union).toHaveLength(1);
+
+ const set1 = new Set(["a", "b", "c"]);
+ const set2 = new Set(["b", "c", "d"]);
+ const union1to2 = set1.union(set2);
+ const union2to1 = set2.union(set1);
+ for (const unionSet of [union1to2, union2to1]) {
+ expect(unionSet).toHaveSize(4);
+ ["a", "b", "c", "d"].forEach(value => expect(unionSet.has(value)).toBeTrue());
+ }
+});