• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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