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