blob: 50b41c1c0b7861515bac20c03fac385c942f5ee2 [file] [log] [blame]
// Copyright 2023 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
namespace collections {
// https://tc39.es/proposal-set-methods/#sec-set.prototype.symmetricdifference
@incrementUseCounter('v8::Isolate::kSetMethods')
transitioning javascript builtin SetPrototypeSymmetricDifference(
js-implicit context: NativeContext, receiver: JSAny)(other: JSAny): JSSet {
const methodName: constexpr string = 'Set.prototype.symmetricDifference';
const fastIteratorResultMap = GetIteratorResultMap();
// 1. Let O be the this value.
// 2. Perform ? RequireInternalSlot(O, [[SetData]]).
const o = Cast<JSSet>(receiver) otherwise
ThrowTypeError(
MessageTemplate::kIncompatibleMethodReceiver, methodName, receiver);
// 3. Let otherRec be ? GetSetRecord(other).
let otherRec = GetSetRecord(other, methodName);
// 4. Let keysIter be ? GetKeysIterator(otherRec).
let keysIter =
GetKeysIterator(otherRec.object, UnsafeCast<Callable>(otherRec.keys));
// 5. Let resultSetData be a copy of O.[[SetData]].
let table = NewStableBackingTableWitness(o);
const resultSetData = Cast<OrderedHashSet>(
CloneFixedArray(table.GetTable(), ExtractFixedArrayFlag::kFixedArrays))
otherwise unreachable;
let resultAndNumberOfElements = OrderedHashSetAndNumberOfElements{
setData: resultSetData,
numberOfElements: UnsafeCast<Smi>(
resultSetData.objects[kOrderedHashSetNumberOfElementsIndex])
};
try {
typeswitch (other) {
case (otherSet: JSSetWithNoCustomIteration): {
CheckSetRecordHasJSSetMethods(otherRec) otherwise SlowPath;
const otherTable = NewStableBackingTableWitness(otherSet);
let otherIterator = collections::NewUnmodifiedOrderedHashSetIterator(
otherTable.GetTable());
while (true) {
const nextValue = otherIterator.Next() otherwise Done;
resultAndNumberOfElements = FastSymmetricDifference(
nextValue, table, resultAndNumberOfElements, methodName);
}
}
case (otherMap: JSMapWithNoCustomIteration): {
CheckSetRecordHasJSMapMethods(otherRec) otherwise SlowPath;
const otherTable = NewStableBackingTableWitness(otherMap);
let otherIterator = collections::NewUnmodifiedOrderedHashMapIterator(
otherTable.GetTable());
while (true) {
const nextValue = otherIterator.Next() otherwise Done;
resultAndNumberOfElements = FastSymmetricDifference(
nextValue.key, table, resultAndNumberOfElements, methodName);
}
}
case (JSAny): {
goto SlowPath;
}
}
} label SlowPath {
// 6. Let next be true.
let nextRecord: JSReceiver;
// 7. Repeat, while next is not false,
while (true) {
// a. Set next to ? IteratorStep(keysIter).
nextRecord = iterator::IteratorStep(keysIter, fastIteratorResultMap)
otherwise Done;
// b. If next is not false, then
// i. Let nextValue be ? IteratorValue(next).
let nextValue =
iterator::IteratorValue(nextRecord, fastIteratorResultMap);
// ii. If nextValue is -0𝔽, set nextValue to +0𝔽.
nextValue = collections::NormalizeNumberKey(nextValue);
// iii. Let inResult be SetDataHas(resultSetData, nextValue).
const inResult =
TableHasKey(resultAndNumberOfElements.setData, nextValue);
// iv. If SetDataHas(O.[[SetData]], nextValue) is true, then
table.ReloadTable();
if (table.HasKey(nextValue)) {
// 1. If inResult is true, remove nextValue from resultSetData.
if (inResult) {
resultAndNumberOfElements.numberOfElements =
DeleteFromSetTable(resultAndNumberOfElements.setData, nextValue)
otherwise unreachable;
}
} else {
// v. Else,
// 1. If inResult is false, append nextValue to resultSetData.
if (!inResult) {
resultAndNumberOfElements.setData = AddToSetTable(
resultAndNumberOfElements.setData, nextValue, methodName);
resultAndNumberOfElements.numberOfElements++;
}
}
}
} label Done {
const shrunk = ShrinkOrderedHashSetIfNeeded(
resultAndNumberOfElements.numberOfElements,
resultAndNumberOfElements.setData);
return new JSSet{
map: *NativeContextSlot(ContextSlot::JS_SET_MAP_INDEX),
properties_or_hash: kEmptyFixedArray,
elements: kEmptyFixedArray,
table: shrunk
};
}
unreachable;
}
// This macro gets the nextValue in other table and normalize it. If the
// nextValue exists in the receiver table, it will be removed. Otherwise
// it will be added to the resultSetData.
struct OrderedHashSetAndNumberOfElements {
setData: OrderedHashSet;
numberOfElements: Smi;
}
macro FastSymmetricDifference(
implicit context: Context)(nextValue: JSAny,
table: StableJSSetBackingTableWitness,
resultSetDataAndNumberOfElements: OrderedHashSetAndNumberOfElements,
methodName: constexpr string): OrderedHashSetAndNumberOfElements {
let key = nextValue;
let resultSetData = resultSetDataAndNumberOfElements.setData;
let numberOfElements = resultSetDataAndNumberOfElements.numberOfElements;
// ii. If nextValue is -0𝔽, set nextValue to +0𝔽.
key = collections::NormalizeNumberKey(key);
// iii. Let inResult be SetDataHas(resultSetData, nextValue).
const inResult = TableHasKey(resultSetData, key);
// iv. If SetDataHas(O.[[SetData]], nextValue) is true, then
dcheck(inResult == table.HasKey(key));
// 1. If inResult is true, remove nextValue from resultSetData.
if (inResult) {
numberOfElements = DeleteFromSetTable(resultSetData, key)
otherwise unreachable;
} else {
// v. Else,
// 1. If inResult is false, append nextValue to resultSetData.
resultSetData = AddToSetTable(resultSetData, key, methodName);
numberOfElements++;
}
return OrderedHashSetAndNumberOfElements{
setData: resultSetData,
numberOfElements: numberOfElements
};
}
}