diff options
-rw-r--r-- | Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h | 1 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Runtime/Set.cpp | 12 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Runtime/Set.h | 2 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Runtime/SetPrototype.cpp | 48 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Runtime/SetPrototype.h | 1 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Tests/builtins/Set/Set.prototype.union.js | 12 |
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()); + } +}); |