blob: a4d4b9d356bed826dbb182a534fe699f44ca6c3e [file] [log] [blame]
// Copyright 2019 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.
extern enum IterationKind extends uint31
constexpr 'IterationKind' { kKeys, kValues, kEntries }
extern class JSArrayIterator extends JSObject {
iterated_object: JSReceiver;
next_index: Number;
kind: SmiTagged<IterationKind>;
}
// Perform CreateArrayIterator (ES #sec-createarrayiterator).
@export
macro CreateArrayIterator(implicit context: NativeContext)(
array: JSReceiver, kind: constexpr IterationKind): JSArrayIterator {
return new JSArrayIterator{
map: *NativeContextSlot(ContextSlot::INITIAL_ARRAY_ITERATOR_MAP_INDEX),
properties_or_hash: kEmptyFixedArray,
elements: kEmptyFixedArray,
iterated_object: array,
next_index: 0,
kind: SmiTag<IterationKind>(kind)
};
}
extern class JSArray extends JSObject {
macro IsEmpty(): bool {
return this.length == 0;
}
length: Number;
}
macro NewJSArray(implicit context: Context)(
map: Map, elements: FixedArrayBase): JSArray {
return new JSArray{
map,
properties_or_hash: kEmptyFixedArray,
elements,
length: elements.length
};
}
macro NewJSArray(implicit context: Context)(): JSArray {
return new JSArray{
map: GetFastPackedSmiElementsJSArrayMap(),
properties_or_hash: kEmptyFixedArray,
elements: kEmptyFixedArray,
length: 0
};
}
// A HeapObject with a JSArray map, and either fast packed elements, or fast
// holey elements when the global NoElementsProtector is not invalidated.
transient type FastJSArray extends JSArray;
// A HeapObject with a JSArray map, and either fast packed elements, or fast
// holey elements or frozen, sealed elements when the global NoElementsProtector
// is not invalidated.
transient type FastJSArrayForRead extends JSArray;
// A FastJSArray when the global ArraySpeciesProtector is not invalidated.
transient type FastJSArrayForCopy extends FastJSArray;
// A FastJSArray when the global ArrayIteratorProtector is not invalidated.
transient type FastJSArrayWithNoCustomIteration extends FastJSArray;
// A FastJSArrayForRead when the global ArrayIteratorProtector is not
// invalidated.
transient type FastJSArrayForReadWithNoCustomIteration extends
FastJSArrayForRead;
extern macro AllocateJSArray(
constexpr ElementsKind, Map, intptr, Smi,
constexpr AllocationFlag): JSArray;
extern macro AllocateJSArray(constexpr ElementsKind, Map, intptr, Smi): JSArray;
extern macro AllocateJSArray(constexpr ElementsKind, Map, Smi, Smi): JSArray;
extern macro AllocateJSArray(Map, FixedArrayBase, Smi): JSArray;
macro LoadElementNoHole<T : type extends FixedArrayBase>(
a: JSArray, index: Smi): JSAny
labels IfHole;
LoadElementNoHole<FixedArray>(implicit context: Context)(
a: JSArray, index: Smi): JSAny
labels IfHole {
const elements: FixedArray =
Cast<FixedArray>(a.elements) otherwise unreachable;
const e = UnsafeCast<(JSAny | TheHole)>(elements.objects[index]);
typeswitch (e) {
case (TheHole): {
goto IfHole;
}
case (e: JSAny): {
return e;
}
}
}
LoadElementNoHole<FixedDoubleArray>(implicit context: Context)(
a: JSArray, index: Smi): JSAny
labels IfHole {
const elements: FixedDoubleArray =
Cast<FixedDoubleArray>(a.elements) otherwise unreachable;
const e: float64 = elements.floats[index].Value() otherwise IfHole;
return AllocateHeapNumberWithValue(e);
}
extern builtin ExtractFastJSArray(Context, JSArray, Smi, Smi): JSArray;
extern macro MoveElements(
constexpr ElementsKind, FixedArrayBase, intptr, intptr, intptr): void;
macro TorqueMoveElementsSmi(
elements: FixedArray, dstIndex: intptr, srcIndex: intptr,
count: intptr): void {
MoveElements(
ElementsKind::HOLEY_SMI_ELEMENTS, elements, dstIndex, srcIndex, count);
}
macro TorqueMoveElements(
elements: FixedArray, dstIndex: intptr, srcIndex: intptr,
count: intptr): void {
MoveElements(
ElementsKind::HOLEY_ELEMENTS, elements, dstIndex, srcIndex, count);
}
macro TorqueMoveElements(
elements: FixedDoubleArray, dstIndex: intptr, srcIndex: intptr,
count: intptr): void {
MoveElements(
ElementsKind::HOLEY_DOUBLE_ELEMENTS, elements, dstIndex, srcIndex, count);
}
extern macro CopyElements(
constexpr ElementsKind, FixedArrayBase, intptr, FixedArrayBase, intptr,
intptr): void;
macro TorqueCopyElements(
dstElements: FixedArray, dstIndex: intptr, srcElements: FixedArray,
srcIndex: intptr, count: intptr): void {
CopyElements(
ElementsKind::HOLEY_ELEMENTS, dstElements, dstIndex, srcElements,
srcIndex, count);
}
macro TorqueCopyElements(
dstElements: FixedDoubleArray, dstIndex: intptr,
srcElements: FixedDoubleArray, srcIndex: intptr, count: intptr): void {
CopyElements(
ElementsKind::HOLEY_DOUBLE_ELEMENTS, dstElements, dstIndex, srcElements,
srcIndex, count);
}
extern builtin CloneFastJSArray(Context, FastJSArrayForCopy): JSArray;
struct FastJSArrayWitness {
macro Get(): FastJSArray {
return this.unstable;
}
macro Recheck() labels CastError {
if (this.stable.map != this.map) goto CastError;
// We don't need to check elements kind or whether the prototype
// has changed away from the default JSArray prototype, because
// if the map remains the same then those properties hold.
//
// However, we have to make sure there are no elements in the
// prototype chain.
if (IsNoElementsProtectorCellInvalid()) goto CastError;
this.unstable = %RawDownCast<FastJSArray>(this.stable);
}
macro LoadElementNoHole(implicit context: Context)(k: Smi): JSAny
labels FoundHole {
if (this.hasDoubles) {
return LoadElementNoHole<FixedDoubleArray>(this.unstable, k)
otherwise FoundHole;
} else {
return LoadElementNoHole<FixedArray>(this.unstable, k)
otherwise FoundHole;
}
}
macro StoreHole(k: Smi) {
if (this.hasDoubles) {
const elements = Cast<FixedDoubleArray>(this.unstable.elements)
otherwise unreachable;
elements.floats[k] = kDoubleHole;
} else {
const elements = Cast<FixedArray>(this.unstable.elements)
otherwise unreachable;
elements.objects[k] = TheHole;
}
}
macro LoadElementOrUndefined(implicit context: Context)(k: Smi): JSAny {
try {
return this.LoadElementNoHole(k) otherwise FoundHole;
} label FoundHole {
return Undefined;
}
}
macro EnsureArrayPushable(implicit context: Context)() labels Failed {
EnsureArrayPushable(this.map) otherwise Failed;
array::EnsureWriteableFastElements(this.unstable);
this.arrayIsPushable = true;
}
macro ChangeLength(newLength: Smi) {
assert(this.arrayIsPushable);
this.unstable.length = newLength;
}
macro Push(value: JSAny) labels Failed {
assert(this.arrayIsPushable);
if (this.hasDoubles) {
BuildAppendJSArray(
ElementsKind::HOLEY_DOUBLE_ELEMENTS, this.unstable, value)
otherwise Failed;
} else if (this.hasSmis) {
BuildAppendJSArray(ElementsKind::HOLEY_SMI_ELEMENTS, this.unstable, value)
otherwise Failed;
} else {
assert(
this.map.elements_kind == ElementsKind::HOLEY_ELEMENTS ||
this.map.elements_kind == ElementsKind::PACKED_ELEMENTS);
BuildAppendJSArray(ElementsKind::HOLEY_ELEMENTS, this.unstable, value)
otherwise Failed;
}
}
macro MoveElements(dst: intptr, src: intptr, length: intptr) {
assert(this.arrayIsPushable);
if (this.hasDoubles) {
const elements: FixedDoubleArray =
Cast<FixedDoubleArray>(this.unstable.elements)
otherwise unreachable;
TorqueMoveElements(elements, dst, src, length);
} else {
const elements: FixedArray = Cast<FixedArray>(this.unstable.elements)
otherwise unreachable;
if (this.hasSmis) {
TorqueMoveElementsSmi(elements, dst, src, length);
} else {
TorqueMoveElements(elements, dst, src, length);
}
}
}
const stable: JSArray;
unstable: FastJSArray;
const map: Map;
const hasDoubles: bool;
const hasSmis: bool;
arrayIsPushable: bool;
}
macro NewFastJSArrayWitness(array: FastJSArray): FastJSArrayWitness {
const kind = array.map.elements_kind;
return FastJSArrayWitness{
stable: array,
unstable: array,
map: array.map,
hasDoubles: IsDoubleElementsKind(kind),
hasSmis:
IsElementsKindLessThanOrEqual(kind, ElementsKind::HOLEY_SMI_ELEMENTS),
arrayIsPushable: false
};
}
struct FastJSArrayForReadWitness {
macro Get(): FastJSArrayForRead {
return this.unstable;
}
macro Recheck() labels CastError {
if (this.stable.map != this.map) goto CastError;
// We don't need to check elements kind or whether the prototype
// has changed away from the default JSArray prototype, because
// if the map remains the same then those properties hold.
//
// However, we have to make sure there are no elements in the
// prototype chain.
if (IsNoElementsProtectorCellInvalid()) goto CastError;
this.unstable = %RawDownCast<FastJSArrayForRead>(this.stable);
}
macro LoadElementNoHole(implicit context: Context)(k: Smi): JSAny
labels FoundHole {
if (this.hasDoubles) {
return LoadElementNoHole<FixedDoubleArray>(this.unstable, k)
otherwise FoundHole;
} else {
return LoadElementNoHole<FixedArray>(this.unstable, k)
otherwise FoundHole;
}
}
const stable: JSArray;
unstable: FastJSArrayForRead;
const map: Map;
const hasDoubles: bool;
}
macro NewFastJSArrayForReadWitness(array: FastJSArrayForRead):
FastJSArrayForReadWitness {
const kind = array.map.elements_kind;
return FastJSArrayForReadWitness{
stable: array,
unstable: array,
map: array.map,
hasDoubles: IsDoubleElementsKind(kind)
};
}