From e359eeabe88ab1d1c932eeab6b04824890e5492f Mon Sep 17 00:00:00 2001 From: Idan Horowitz Date: Thu, 1 Dec 2022 22:44:04 +0200 Subject: [PATCH] LibJS: Implement Set.prototype.symmetricDifference --- .../LibJS/Runtime/CommonPropertyNames.h | 1 + .../Libraries/LibJS/Runtime/SetPrototype.cpp | 56 +++++++++++++++++++ .../Libraries/LibJS/Runtime/SetPrototype.h | 1 + .../Set/Set.prototype.symmetricDifference.js | 12 ++++ 4 files changed, 70 insertions(+) create mode 100644 Userland/Libraries/LibJS/Tests/builtins/Set/Set.prototype.symmetricDifference.js diff --git a/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h b/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h index c3089dc953..0e981d587a 100644 --- a/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h +++ b/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h @@ -488,6 +488,7 @@ namespace JS { P(sup) \ P(supportedLocalesOf) \ P(supportedValuesOf) \ + P(symmetricDifference) \ P(tan) \ P(tanh) \ P(test) \ diff --git a/Userland/Libraries/LibJS/Runtime/SetPrototype.cpp b/Userland/Libraries/LibJS/Runtime/SetPrototype.cpp index 4a930076df..9057d80519 100644 --- a/Userland/Libraries/LibJS/Runtime/SetPrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/SetPrototype.cpp @@ -35,6 +35,7 @@ void SetPrototype::initialize(Realm& realm) define_native_function(realm, vm.names.union_, union_, 1, attr); define_native_function(realm, vm.names.intersection, intersection, 1, attr); define_native_function(realm, vm.names.difference, difference, 1, attr); + define_native_function(realm, vm.names.symmetricDifference, symmetric_difference, 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); @@ -383,4 +384,59 @@ JS_DEFINE_NATIVE_FUNCTION(SetPrototype::difference) return result; } +// 4 Set.prototype.symmetricDifference ( other ), https://tc39.es/proposal-set-methods/#sec-set.prototype.symmetricdifference +JS_DEFINE_NATIVE_FUNCTION(SetPrototype::symmetric_difference) +{ + // 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. Let inResult be SetDataHas(resultSetData, nextValue). + // iv. If SetDataHas(O.[[SetData]], nextValue) is true, then + if (set->set_has(next_value)) { + // 1. If inResult is true, remove nextValue from resultSetData. + result->set_remove(next_value); + } + // v. Else, + else { + // 1. If inResult is false, 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 b002127bc5..b5431e23ef 100644 --- a/Userland/Libraries/LibJS/Runtime/SetPrototype.h +++ b/Userland/Libraries/LibJS/Runtime/SetPrototype.h @@ -31,6 +31,7 @@ private: JS_DECLARE_NATIVE_FUNCTION(union_); JS_DECLARE_NATIVE_FUNCTION(intersection); JS_DECLARE_NATIVE_FUNCTION(difference); + JS_DECLARE_NATIVE_FUNCTION(symmetric_difference); JS_DECLARE_NATIVE_FUNCTION(size_getter); }; diff --git a/Userland/Libraries/LibJS/Tests/builtins/Set/Set.prototype.symmetricDifference.js b/Userland/Libraries/LibJS/Tests/builtins/Set/Set.prototype.symmetricDifference.js new file mode 100644 index 0000000000..ef54b70b86 --- /dev/null +++ b/Userland/Libraries/LibJS/Tests/builtins/Set/Set.prototype.symmetricDifference.js @@ -0,0 +1,12 @@ +test("basic functionality", () => { + expect(Set.prototype.symmetricDifference).toHaveLength(1); + + const set1 = new Set(["a", "b", "c"]); + const set2 = new Set(["b", "c", "d", "e"]); + const symmetricDifference1to2 = set1.symmetricDifference(set2); + const symmetricDifference2to1 = set2.symmetricDifference(set1); + for (const symmetricDifferenceSet of [symmetricDifference1to2, symmetricDifference2to1]) { + expect(symmetricDifferenceSet).toHaveSize(3); + ["a", "d", "e"].forEach(value => expect(symmetricDifferenceSet.has(value)).toBeTrue()); + } +});