1// Copyright 2019 the V8 project authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5extern enum IterationKind extends uint31 { kKeys, kValues, kEntries } 6 7extern class JSArrayIterator extends JSObject { 8 iterated_object: JSReceiver; 9 10 // [next_index]: The [[ArrayIteratorNextIndex]] inobject property. 11 // The next_index is always a positive integer, and it points to 12 // the next index that is to be returned by this iterator. It's 13 // possible range is fixed depending on the [[iterated_object]]: 14 // 15 // 1. For JSArray's the next_index is always in Unsigned32 16 // range, and when the iterator reaches the end it's set 17 // to kMaxUInt32 to indicate that this iterator should 18 // never produce values anymore even if the "length" 19 // property of the JSArray changes at some later point. 20 // 2. For JSTypedArray's the next_index is always in 21 // UnsignedSmall range, and when the iterator terminates 22 // it's set to Smi::kMaxValue. 23 // 3. For all other JSReceiver's it's always between 0 and 24 // kMaxSafeInteger, and the latter value is used to mark 25 // termination. 26 // 27 // It's important that for 1. and 2. the value fits into the 28 // Unsigned32 range (UnsignedSmall is a subset of Unsigned32), 29 // since we use this knowledge in the fast-path for the array 30 // iterator next calls in TurboFan (in the JSCallReducer) to 31 // keep the index in Word32 representation. This invariant is 32 // checked in JSArrayIterator::JSArrayIteratorVerify(). 33 next_index: Number; 34 35 kind: SmiTagged<IterationKind>; 36} 37 38// Perform CreateArrayIterator (ES #sec-createarrayiterator). 39@export 40macro CreateArrayIterator(implicit context: NativeContext)( 41 array: JSReceiver, kind: constexpr IterationKind): JSArrayIterator { 42 return new JSArrayIterator{ 43 map: *NativeContextSlot(ContextSlot::INITIAL_ARRAY_ITERATOR_MAP_INDEX), 44 properties_or_hash: kEmptyFixedArray, 45 elements: kEmptyFixedArray, 46 iterated_object: array, 47 next_index: 0, 48 kind: SmiTag<IterationKind>(kind) 49 }; 50} 51 52extern class JSArray extends JSObject { 53 macro IsEmpty(): bool { 54 return this.length == 0; 55 } 56 length: Number; 57} 58 59@doNotGenerateCast 60extern class JSArrayConstructor extends JSFunction 61 generates 'TNode<JSFunction>'; 62 63macro NewJSArray(implicit context: Context)( 64 map: Map, elements: FixedArrayBase): JSArray { 65 return new JSArray{ 66 map, 67 properties_or_hash: kEmptyFixedArray, 68 elements, 69 length: elements.length 70 }; 71} 72 73macro NewJSArray(implicit context: Context)(): JSArray { 74 return new JSArray{ 75 map: GetFastPackedSmiElementsJSArrayMap(), 76 properties_or_hash: kEmptyFixedArray, 77 elements: kEmptyFixedArray, 78 length: 0 79 }; 80} 81 82// A HeapObject with a JSArray map, and either fast packed elements, or fast 83// holey elements when the global NoElementsProtector is not invalidated. 84transient type FastJSArray extends JSArray; 85 86// A HeapObject with a JSArray map, and either fast packed elements, or fast 87// holey elements or frozen, sealed elements when the global NoElementsProtector 88// is not invalidated. 89transient type FastJSArrayForRead extends JSArray; 90 91// A FastJSArray when the global ArraySpeciesProtector is not invalidated. 92transient type FastJSArrayForCopy extends FastJSArray; 93 94// A FastJSArrayForCopy when the global IsConcatSpreadableProtector is not 95// invalidated. 96transient type FastJSArrayForConcat extends FastJSArrayForCopy; 97 98// A FastJSArray when the global ArrayIteratorProtector is not invalidated. 99transient type FastJSArrayWithNoCustomIteration extends FastJSArray; 100 101// A FastJSArrayForRead when the global ArrayIteratorProtector is not 102// invalidated. 103transient type FastJSArrayForReadWithNoCustomIteration extends 104 FastJSArrayForRead; 105 106extern macro AllocateJSArray( 107 constexpr ElementsKind, Map, intptr, Smi, 108 constexpr AllocationFlag): JSArray; 109extern macro AllocateJSArray(constexpr ElementsKind, Map, intptr, Smi): JSArray; 110extern macro AllocateJSArray(constexpr ElementsKind, Map, Smi, Smi): JSArray; 111extern macro AllocateJSArray(Map, FixedArrayBase, Smi): JSArray; 112 113macro LoadElementNoHole<T : type extends FixedArrayBase>( 114 a: JSArray, index: Smi): JSAny 115 labels IfHole; 116 117LoadElementNoHole<FixedArray>(implicit context: Context)( 118 a: JSArray, index: Smi): JSAny 119 labels IfHole { 120 const elements: FixedArray = 121 Cast<FixedArray>(a.elements) otherwise unreachable; 122 const e = UnsafeCast<(JSAny | TheHole)>(elements.objects[index]); 123 typeswitch (e) { 124 case (TheHole): { 125 goto IfHole; 126 } 127 case (e: JSAny): { 128 return e; 129 } 130 } 131} 132 133LoadElementNoHole<FixedDoubleArray>(implicit context: Context)( 134 a: JSArray, index: Smi): JSAny 135 labels IfHole { 136 const elements: FixedDoubleArray = 137 Cast<FixedDoubleArray>(a.elements) otherwise unreachable; 138 const e: float64 = elements.floats[index].Value() otherwise IfHole; 139 return AllocateHeapNumberWithValue(e); 140} 141 142extern builtin ExtractFastJSArray(Context, JSArray, Smi, Smi): JSArray; 143 144extern macro MoveElements( 145 constexpr ElementsKind, FixedArrayBase, intptr, intptr, intptr): void; 146macro TorqueMoveElementsSmi( 147 elements: FixedArray, dstIndex: intptr, srcIndex: intptr, 148 count: intptr): void { 149 MoveElements( 150 ElementsKind::HOLEY_SMI_ELEMENTS, elements, dstIndex, srcIndex, count); 151} 152macro TorqueMoveElements( 153 elements: FixedArray, dstIndex: intptr, srcIndex: intptr, 154 count: intptr): void { 155 MoveElements( 156 ElementsKind::HOLEY_ELEMENTS, elements, dstIndex, srcIndex, count); 157} 158macro TorqueMoveElements( 159 elements: FixedDoubleArray, dstIndex: intptr, srcIndex: intptr, 160 count: intptr): void { 161 MoveElements( 162 ElementsKind::HOLEY_DOUBLE_ELEMENTS, elements, dstIndex, srcIndex, count); 163} 164 165extern macro CopyElements( 166 constexpr ElementsKind, FixedArrayBase, intptr, FixedArrayBase, intptr, 167 intptr): void; 168macro TorqueCopyElements( 169 dstElements: FixedArray, dstIndex: intptr, srcElements: FixedArray, 170 srcIndex: intptr, count: intptr): void { 171 CopyElements( 172 ElementsKind::HOLEY_ELEMENTS, dstElements, dstIndex, srcElements, 173 srcIndex, count); 174} 175macro TorqueCopyElements( 176 dstElements: FixedDoubleArray, dstIndex: intptr, 177 srcElements: FixedDoubleArray, srcIndex: intptr, count: intptr): void { 178 CopyElements( 179 ElementsKind::HOLEY_DOUBLE_ELEMENTS, dstElements, dstIndex, srcElements, 180 srcIndex, count); 181} 182 183extern builtin CloneFastJSArray(Context, FastJSArrayForCopy): JSArray; 184 185struct FastJSArrayWitness { 186 macro Get(): FastJSArray { 187 return this.unstable; 188 } 189 190 macro Recheck(): void labels CastError { 191 if (this.stable.map != this.map) goto CastError; 192 // We don't need to check elements kind or whether the prototype 193 // has changed away from the default JSArray prototype, because 194 // if the map remains the same then those properties hold. 195 // 196 // However, we have to make sure there are no elements in the 197 // prototype chain. 198 if (IsNoElementsProtectorCellInvalid()) goto CastError; 199 this.unstable = %RawDownCast<FastJSArray>(this.stable); 200 } 201 202 macro LoadElementNoHole(implicit context: Context)(k: Smi): JSAny 203 labels FoundHole { 204 if (this.hasDoubles) { 205 return LoadElementNoHole<FixedDoubleArray>(this.unstable, k) 206 otherwise FoundHole; 207 } else { 208 return LoadElementNoHole<FixedArray>(this.unstable, k) 209 otherwise FoundHole; 210 } 211 } 212 213 macro StoreHole(k: Smi): void { 214 if (this.hasDoubles) { 215 const elements = Cast<FixedDoubleArray>(this.unstable.elements) 216 otherwise unreachable; 217 elements.floats[k] = kDoubleHole; 218 } else { 219 const elements = Cast<FixedArray>(this.unstable.elements) 220 otherwise unreachable; 221 elements.objects[k] = TheHole; 222 } 223 } 224 225 macro LoadElementOrUndefined(implicit context: Context)(k: Smi): JSAny { 226 try { 227 return this.LoadElementNoHole(k) otherwise FoundHole; 228 } label FoundHole { 229 return Undefined; 230 } 231 } 232 233 macro EnsureArrayPushable(implicit context: Context)(): void labels Failed { 234 EnsureArrayPushable(this.map) otherwise Failed; 235 array::EnsureWriteableFastElements(this.unstable); 236 this.arrayIsPushable = true; 237 } 238 239 macro ChangeLength(newLength: Smi): void { 240 dcheck(this.arrayIsPushable); 241 this.unstable.length = newLength; 242 } 243 244 macro Push(value: JSAny): void labels Failed { 245 dcheck(this.arrayIsPushable); 246 if (this.hasDoubles) { 247 BuildAppendJSArray( 248 ElementsKind::HOLEY_DOUBLE_ELEMENTS, this.unstable, value) 249 otherwise Failed; 250 } else if (this.hasSmis) { 251 BuildAppendJSArray(ElementsKind::HOLEY_SMI_ELEMENTS, this.unstable, value) 252 otherwise Failed; 253 } else { 254 dcheck( 255 this.map.elements_kind == ElementsKind::HOLEY_ELEMENTS || 256 this.map.elements_kind == ElementsKind::PACKED_ELEMENTS); 257 BuildAppendJSArray(ElementsKind::HOLEY_ELEMENTS, this.unstable, value) 258 otherwise Failed; 259 } 260 } 261 262 macro MoveElements(dst: intptr, src: intptr, length: intptr): void { 263 dcheck(this.arrayIsPushable); 264 if (this.hasDoubles) { 265 const elements: FixedDoubleArray = 266 Cast<FixedDoubleArray>(this.unstable.elements) 267 otherwise unreachable; 268 TorqueMoveElements(elements, dst, src, length); 269 } else { 270 const elements: FixedArray = Cast<FixedArray>(this.unstable.elements) 271 otherwise unreachable; 272 if (this.hasSmis) { 273 TorqueMoveElementsSmi(elements, dst, src, length); 274 } else { 275 TorqueMoveElements(elements, dst, src, length); 276 } 277 } 278 } 279 280 const stable: JSArray; 281 unstable: FastJSArray; 282 const map: Map; 283 const hasDoubles: bool; 284 const hasSmis: bool; 285 arrayIsPushable: bool; 286} 287 288macro NewFastJSArrayWitness(array: FastJSArray): FastJSArrayWitness { 289 const kind = array.map.elements_kind; 290 return FastJSArrayWitness{ 291 stable: array, 292 unstable: array, 293 map: array.map, 294 hasDoubles: IsDoubleElementsKind(kind), 295 hasSmis: 296 IsElementsKindLessThanOrEqual(kind, ElementsKind::HOLEY_SMI_ELEMENTS), 297 arrayIsPushable: false 298 }; 299} 300 301struct FastJSArrayForReadWitness { 302 macro Get(): FastJSArrayForRead { 303 return this.unstable; 304 } 305 306 macro Recheck(): void labels CastError { 307 if (this.stable.map != this.map) goto CastError; 308 // We don't need to check elements kind or whether the prototype 309 // has changed away from the default JSArray prototype, because 310 // if the map remains the same then those properties hold. 311 // 312 // However, we have to make sure there are no elements in the 313 // prototype chain. 314 if (IsNoElementsProtectorCellInvalid()) goto CastError; 315 this.unstable = %RawDownCast<FastJSArrayForRead>(this.stable); 316 } 317 318 macro LoadElementNoHole(implicit context: Context)(k: Smi): JSAny 319 labels FoundHole { 320 if (this.hasDoubles) { 321 return LoadElementNoHole<FixedDoubleArray>(this.unstable, k) 322 otherwise FoundHole; 323 } else { 324 return LoadElementNoHole<FixedArray>(this.unstable, k) 325 otherwise FoundHole; 326 } 327 } 328 329 const stable: JSArray; 330 unstable: FastJSArrayForRead; 331 const map: Map; 332 const hasDoubles: bool; 333} 334 335macro NewFastJSArrayForReadWitness(array: FastJSArrayForRead): 336 FastJSArrayForReadWitness { 337 const kind = array.map.elements_kind; 338 return FastJSArrayForReadWitness{ 339 stable: array, 340 unstable: array, 341 map: array.map, 342 hasDoubles: IsDoubleElementsKind(kind) 343 }; 344} 345