// 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 { kKeys, kValues, kEntries } extern class JSArrayIterator extends JSObject { iterated_object: JSReceiver; // [next_index]: The [[ArrayIteratorNextIndex]] inobject property. // The next_index is always a positive integer, and it points to // the next index that is to be returned by this iterator. It's // possible range is fixed depending on the [[iterated_object]]: // // 1. For JSArray's the next_index is always in Unsigned32 // range, and when the iterator reaches the end it's set // to kMaxUInt32 to indicate that this iterator should // never produce values anymore even if the "length" // property of the JSArray changes at some later point. // 2. For JSTypedArray's the next_index is always in // UnsignedSmall range, and when the iterator terminates // it's set to Smi::kMaxValue. // 3. For all other JSReceiver's it's always between 0 and // kMaxSafeInteger, and the latter value is used to mark // termination. // // It's important that for 1. and 2. the value fits into the // Unsigned32 range (UnsignedSmall is a subset of Unsigned32), // since we use this knowledge in the fast-path for the array // iterator next calls in TurboFan (in the JSCallReducer) to // keep the index in Word32 representation. This invariant is // checked in JSArrayIterator::JSArrayIteratorVerify(). 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; } @doNotGenerateCast extern class JSArrayConstructor extends JSFunction generates 'TNode<JSFunction>'; 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 FastJSArrayForCopy when the global IsConcatSpreadableProtector is not // invalidated. transient type FastJSArrayForConcat extends FastJSArrayForCopy; // 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(): void 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): void { 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)(): void labels Failed { EnsureArrayPushable(this.map) otherwise Failed; array::EnsureWriteableFastElements(this.unstable); this.arrayIsPushable = true; } macro ChangeLength(newLength: Smi): void { dcheck(this.arrayIsPushable); this.unstable.length = newLength; } macro Push(value: JSAny): void labels Failed { dcheck(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 { dcheck( 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): void { dcheck(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(): void 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) }; }