LibJS: Implement Set.prototype.symmetricDifference

This commit is contained in:
Idan Horowitz 2022-12-01 22:44:04 +02:00 committed by Linus Groh
parent be8329d5f6
commit e359eeabe8
4 changed files with 70 additions and 0 deletions

View file

@ -488,6 +488,7 @@ namespace JS {
P(sup) \
P(supportedLocalesOf) \
P(supportedValuesOf) \
P(symmetricDifference) \
P(tan) \
P(tanh) \
P(test) \

View file

@ -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;
}
}

View file

@ -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);
};

View file

@ -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());
}
});