blob: 31ac745125e6c2b18f98403ebf68e667d90da413 [file] [log] [blame]
// Copyright 2018 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.
#include 'src/builtins/builtins-collections-gen.h'
namespace collections {
const kSetPrototypeValues: constexpr BuiltinsName
generates 'Builtin::kSetPrototypeValues';
const kSetPrototypeHas: constexpr BuiltinsName
generates 'Builtin::kSetPrototypeHas';
const kMapPrototypeKeys: constexpr BuiltinsName
generates 'Builtin::kMapPrototypeKeys';
const kMapPrototypeHas: constexpr BuiltinsName
generates 'Builtin::kMapPrototypeHas';
@export
struct SetRecord {
// SetRecord.[[Set]]
object: JSReceiver;
// SetRecord.[[Size]]
// a non-negative integer or +∞
size: Number;
// SetRecord.[[Has]]
has: JSAny;
// SetRecord.[[Keys]]
keys: JSAny;
}
extern macro CodeStubAssembler::CloneFixedArray(
FixedArrayBase, constexpr ExtractFixedArrayFlag): FixedArrayBase;
extern macro CollectionsBuiltinsAssembler::AddToSetTable(
implicit context: Context)(OrderedHashSet, Object, String): OrderedHashSet;
extern macro CollectionsBuiltinsAssembler::SetTableHasKey(
implicit context: Context)(Object, Object): bool;
// Direct iteration helpers.
@export
struct KeyIndexPair {
key: JSAny;
index: intptr;
}
extern macro CollectionsBuiltinsAssembler::NextKeyIndexPairUnmodifiedTable(
OrderedHashMap, int32, int32, intptr): KeyIndexPair labels Done;
extern macro CollectionsBuiltinsAssembler::NextKeyIndexPairUnmodifiedTable(
OrderedHashSet, int32, int32, intptr): KeyIndexPair labels Done;
// The underlying table must not be resized during iteration!
struct UnmodifiedOrderedHashSetIterator {
macro Next(): JSAny labels Done {
this.current = NextKeyIndexPairUnmodifiedTable(
this.table, this.numBuckets, this.usedCapacity, this.current.index)
otherwise Done;
return this.current.key;
}
const table: OrderedHashSet;
const numBuckets: int32;
const usedCapacity: int32;
current: KeyIndexPair;
}
extern macro CollectionsBuiltinsAssembler::NextKeyIndexPair(
OrderedHashMap, intptr): KeyIndexPair labels Done;
extern macro CollectionsBuiltinsAssembler::NextKeyIndexPair(
OrderedHashSet, intptr): KeyIndexPair labels Done;
// The underlying table can be resized during iteration.
struct OrderedHashSetIterator {
macro Next(): JSAny labels Done {
this.current = NextKeyIndexPair(this.table, this.current.index)
otherwise Done;
return this.current.key;
}
const table: OrderedHashSet;
current: KeyIndexPair;
}
macro LoadOrderedHashTableMetadata(
table: OrderedHashMap|OrderedHashSet, fieldIndex: constexpr int32): int32 {
return Convert<int32>(UnsafeCast<Smi>(table.objects[fieldIndex]));
}
const kOrderedHashSetNumberOfBucketsIndex:
constexpr int32 generates 'OrderedHashSet::NumberOfBucketsIndex()';
const kOrderedHashSetNumberOfElementsIndex:
constexpr int32 generates 'OrderedHashSet::NumberOfElementsIndex()';
const kOrderedHashSetNumberOfDeletedElementsIndex: constexpr int32
generates 'OrderedHashSet::NumberOfDeletedElementsIndex()';
macro NewUnmodifiedOrderedHashSetIterator(table: OrderedHashSet):
UnmodifiedOrderedHashSetIterator {
const numBuckets =
LoadOrderedHashTableMetadata(table, kOrderedHashSetNumberOfBucketsIndex);
const numElements =
LoadOrderedHashTableMetadata(table, kOrderedHashSetNumberOfElementsIndex);
const numDeleted = LoadOrderedHashTableMetadata(
table, kOrderedHashSetNumberOfDeletedElementsIndex);
const usedCapacity = numElements + numDeleted;
return UnmodifiedOrderedHashSetIterator{
table: table,
numBuckets: numBuckets,
usedCapacity: usedCapacity,
current: KeyIndexPair {
key: Undefined, index: 0
}
};
}
macro NewOrderedHashSetIterator(table: OrderedHashSet): OrderedHashSetIterator {
return OrderedHashSetIterator{
table: table,
current: KeyIndexPair {
key: Undefined, index: 0
}
};
}
@export
struct KeyValueIndexTuple {
key: JSAny;
value: JSAny;
index: intptr;
}
extern macro CollectionsBuiltinsAssembler::NextKeyValueIndexTuple(
OrderedHashMap, int32, int32, intptr): KeyValueIndexTuple labels Done;
// The underlying table must not be resized during iteration!
struct OrderedHashMapIterator {
macro Next(): KeyValuePair labels Done {
this.current = NextKeyValueIndexTuple(
this.table, this.numBuckets, this.usedCapacity, this.current.index)
otherwise Done;
return KeyValuePair{key: this.current.key, value: this.current.value};
}
const table: OrderedHashMap;
const numBuckets: int32;
const usedCapacity: int32;
current: KeyValueIndexTuple;
}
const kOrderedHashMapNumberOfBucketsIndex:
constexpr int32 generates 'OrderedHashMap::NumberOfBucketsIndex()';
const kOrderedHashMapNumberOfElementsIndex:
constexpr int32 generates 'OrderedHashMap::NumberOfElementsIndex()';
const kOrderedHashMapNumberOfDeletedElementsIndex: constexpr int32
generates 'OrderedHashMap::NumberOfDeletedElementsIndex()';
macro NewOrderedHashMapIterator(table: OrderedHashMap): OrderedHashMapIterator {
const numBuckets =
LoadOrderedHashTableMetadata(table, kOrderedHashMapNumberOfBucketsIndex);
const numElements =
LoadOrderedHashTableMetadata(table, kOrderedHashMapNumberOfElementsIndex);
const numDeleted = LoadOrderedHashTableMetadata(
table, kOrderedHashMapNumberOfDeletedElementsIndex);
const usedCapacity = numElements + numDeleted;
return OrderedHashMapIterator{
table: table,
numBuckets: numBuckets,
usedCapacity: usedCapacity,
current: KeyValueIndexTuple {
key: Undefined, value: Undefined, index: 0
}
};
}
@export
macro LoadKeyValuePairNoSideEffects(implicit context: Context)(o: JSAny):
KeyValuePair labels MayHaveSideEffects {
typeswitch (o) {
case (a: FastJSArray): {
const length: Smi = a.length;
typeswitch (a.elements) {
case (elements: FixedArray): {
return KeyValuePair{
key: length > 0 ? array::LoadElementOrUndefined(elements, 0) :
Undefined,
value: length > 1 ? array::LoadElementOrUndefined(elements, 1) :
Undefined
};
}
case (elements: FixedDoubleArray): {
return KeyValuePair{
key: length > 0 ? array::LoadElementOrUndefined(elements, 0) :
Undefined,
value: length > 1 ? array::LoadElementOrUndefined(elements, 1) :
Undefined
};
}
case (FixedArrayBase): deferred {
unreachable;
}
}
}
case (JSAny): {
goto MayHaveSideEffects;
}
}
}
@export
transitioning macro LoadKeyValuePair(implicit context: Context)(o: JSAny):
KeyValuePair {
try {
return LoadKeyValuePairNoSideEffects(o) otherwise Generic;
} label Generic {
const o = Cast<JSReceiver>(o)
otherwise ThrowTypeError(MessageTemplate::kIteratorValueNotAnObject, o);
return KeyValuePair{
key: GetProperty(o, Convert<Smi>(0)),
value: GetProperty(o, Convert<Smi>(1))
};
}
}
// https://tc39.es/proposal-set-methods/#sec-getsetrecord
transitioning macro GetSetRecord(implicit context: Context)(
obj: JSAny, methodName: constexpr string): SetRecord {
// 1. If obj is not an Object, throw a TypeError exception.
const obj = Cast<JSReceiver>(obj)
otherwise ThrowTypeError(MessageTemplate::kArgumentIsNonObject, methodName);
// 2. Let rawSize be ? Get(obj, "size").
const rawSize = GetProperty(obj, kSizeString);
// 3. Let numSize be ? ToNumber(rawSize).
const numSize = ToNumber_Inline(rawSize);
if (NumberIsNaN(numSize)) {
// 4. NOTE: If rawSize is undefined, then numSize will be NaN.
// 5. If numSize is NaN, throw a TypeError exception.
ThrowTypeError(MessageTemplate::kSizeIsNaN);
}
// 6. Let intSize be ! ToIntegerOrInfinity(numSize).
const intSize = ToInteger_Inline(numSize);
// 7. Let has be ? Get(obj, "has").
let has = GetProperty(obj, kHasString);
// 8. If IsCallable(has) is false, throw a TypeError exception.
has = Cast<Callable>(has)
otherwise ThrowCalledNonCallable(kHasString);
// 9. Let keys be ? Get(obj, "keys").
let keys = GetProperty(obj, kKeysString);
// 10. If IsCallable(keys) is false, throw a TypeError exception.
keys = Cast<Callable>(keys)
otherwise ThrowCalledNonCallable(kKeysString);
// 11. Return a new Set Record { [[Set]]: obj, [[Size]]: intSize, [[Has]]:
// has, [[Keys]]: keys }.
return SetRecord{object: obj, size: intSize, has: has, keys: keys};
}
// https://tc39.es/proposal-set-methods/#sec-getkeysiterator
transitioning macro GetKeysIterator(implicit context: Context)(
set: JSReceiver, keys: Callable): iterator::IteratorRecord {
// 1. Let keysIter be ? Call(setRec.[[Keys]], setRec.[[Set]]).
const keysIter = Call(context, keys, set);
// 2. If keysIter is not an Object, throw a TypeError exception.
const keysIterObj = Cast<JSReceiver>(keysIter)
otherwise ThrowTypeError(MessageTemplate::kKeysMethodInvalid);
// 3. Let nextMethod be ? Get(keysIter, "next").
const nextMethod = GetProperty(keysIter, kNextString);
// 4. If IsCallable(nextMethod) is false, throw a TypeError exception.
Cast<Callable>(nextMethod)
otherwise ThrowCalledNonCallable(kNextString);
// 5. Return a new Iterator Record { [[Iterator]]: keysIter, [[NextMethod]]:
// nextMethod, [[Done]]: false }.
return iterator::IteratorRecord{object: keysIterObj, next: nextMethod};
}
macro CheckSetRecordIsJSSet(setRecord: SetRecord):
void labels HasUserProvidedMethods {
const keys =
Cast<JSFunction>(setRecord.keys) otherwise HasUserProvidedMethods;
const has = Cast<JSFunction>(setRecord.has) otherwise HasUserProvidedMethods;
if (!(TaggedEqual(
keys.shared_function_info.function_data,
SmiConstant(kSetPrototypeValues)) &&
TaggedEqual(
has.shared_function_info.function_data,
SmiConstant(kSetPrototypeHas))))
goto HasUserProvidedMethods;
}
macro CheckSetRecordIsJSMap(setRecord: SetRecord):
void labels HasUserProvidedMethods {
const keys =
Cast<JSFunction>(setRecord.keys) otherwise HasUserProvidedMethods;
const has = Cast<JSFunction>(setRecord.has) otherwise HasUserProvidedMethods;
if (!(TaggedEqual(
keys.shared_function_info.function_data,
SmiConstant(kMapPrototypeKeys)) &&
TaggedEqual(
has.shared_function_info.function_data,
SmiConstant(kMapPrototypeHas))))
goto HasUserProvidedMethods;
}
}