• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2018 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
5namespace array {
6
7// Continuation for lazy deopt triggered by allocation of the result array.
8transitioning javascript builtin
9ArrayMapPreLoopLazyDeoptContinuation(
10    js-implicit context: NativeContext, receiver: JSAny)(
11    callback: JSAny, thisArg: JSAny, length: JSAny, result: JSAny): JSAny {
12  const jsreceiver = Cast<JSReceiver>(receiver) otherwise unreachable;
13  const outputArray = Cast<JSReceiver>(result) otherwise unreachable;
14  const numberLength = Cast<Number>(length) otherwise unreachable;
15
16  const callbackfn = Cast<Callable>(callback)
17      otherwise ThrowTypeError(MessageTemplate::kCalledNonCallable, callback);
18  return ArrayMapLoopContinuation(
19      jsreceiver, callbackfn, thisArg, outputArray, jsreceiver, kZero,
20      numberLength);
21}
22
23transitioning javascript builtin
24ArrayMapLoopEagerDeoptContinuation(
25    js-implicit context: NativeContext, receiver: JSAny)(
26    callback: JSAny, thisArg: JSAny, array: JSAny, initialK: JSAny,
27    length: JSAny): JSAny {
28  const jsreceiver = Cast<JSReceiver>(receiver) otherwise unreachable;
29  const callbackfn = Cast<Callable>(callback) otherwise unreachable;
30  const outputArray = Cast<JSReceiver>(array) otherwise unreachable;
31  const numberK = Cast<Number>(initialK) otherwise unreachable;
32  const numberLength = Cast<Number>(length) otherwise unreachable;
33
34  return ArrayMapLoopContinuation(
35      jsreceiver, callbackfn, thisArg, outputArray, jsreceiver, numberK,
36      numberLength);
37}
38
39transitioning javascript builtin
40ArrayMapLoopLazyDeoptContinuation(
41    js-implicit context: NativeContext, receiver: JSAny)(
42    callback: JSAny, thisArg: JSAny, array: JSAny, initialK: JSAny,
43    length: JSAny, result: JSAny): JSAny {
44  const jsreceiver = Cast<JSReceiver>(receiver) otherwise unreachable;
45  const callbackfn = Cast<Callable>(callback) otherwise unreachable;
46  const outputArray = Cast<JSReceiver>(array) otherwise unreachable;
47  let numberK = Cast<Number>(initialK) otherwise unreachable;
48  const numberLength = Cast<Number>(length) otherwise unreachable;
49
50  // This custom lazy deopt point is right after the callback. The continuation
51  // needs to pick up at the next step, which is setting the callback result in
52  // the output array. After incrementing k, we can glide into the loop
53  // continuation builtin.
54
55  // iii. Perform ? CreateDataPropertyOrThrow(A, Pk, mappedValue).
56  FastCreateDataProperty(outputArray, numberK, result);
57
58  // 7d. Increase k by 1.
59  numberK = numberK + 1;
60
61  return ArrayMapLoopContinuation(
62      jsreceiver, callbackfn, thisArg, outputArray, jsreceiver, numberK,
63      numberLength);
64}
65
66transitioning builtin ArrayMapLoopContinuation(implicit context: Context)(
67    _receiver: JSReceiver, callbackfn: Callable, thisArg: JSAny,
68    array: JSReceiver, o: JSReceiver, initialK: Number, length: Number): JSAny {
69  // 6. Let k be 0.
70  // 7. Repeat, while k < len
71  for (let k: Number = initialK; k < length; k++) {
72    // 7a. Let Pk be ! ToString(k).
73    // k is guaranteed to be a positive integer, hence ToString is
74    // side-effect free and HasProperty/GetProperty do the conversion inline.
75
76    // 7b. Let kPresent be ? HasProperty(O, Pk).
77    const kPresent: Boolean = HasProperty_Inline(o, k);
78
79    // 7c. If kPresent is true, then:
80    if (kPresent == True) {
81      //  i. Let kValue be ? Get(O, Pk).
82      const kValue: JSAny = GetProperty(o, k);
83
84      // ii. Let mapped_value be ? Call(callbackfn, T, kValue, k, O).
85      const mappedValue: JSAny =
86          Call(context, callbackfn, thisArg, kValue, k, o);
87
88      // iii. Perform ? CreateDataPropertyOrThrow(A, Pk, mapped_value).
89      FastCreateDataProperty(array, k, mappedValue);
90    }
91
92    // 7d. Increase k by 1. (done by the loop).
93  }
94
95  // 8. Return A.
96  return array;
97}
98
99struct Vector {
100  macro ReportSkippedElement() {
101    this.skippedElements = true;
102  }
103
104  macro CreateJSArray(implicit context: Context)(validLength: Smi): JSArray {
105    const length: Smi = this.fixedArray.length;
106    assert(validLength <= length);
107    let kind: ElementsKind = ElementsKind::PACKED_SMI_ELEMENTS;
108    if (!this.onlySmis) {
109      if (this.onlyNumbers) {
110        kind = ElementsKind::PACKED_DOUBLE_ELEMENTS;
111      } else {
112        kind = ElementsKind::PACKED_ELEMENTS;
113      }
114    }
115
116    if (this.skippedElements || validLength < length) {
117      // We also need to create a holey output array if we are
118      // bailing out of the fast path partway through the array.
119      // This is indicated by {validLength} < {length}.
120      // Who knows if the bailout condition will continue to fill in
121      // every element?
122      kind = FastHoleyElementsKind(kind);
123    }
124
125    const map: Map = LoadJSArrayElementsMap(kind, LoadNativeContext(context));
126    let a: JSArray;
127
128    if (IsDoubleElementsKind(kind)) {
129      // We need to allocate and copy.
130      // First, initialize the elements field before allocation to prevent
131      // heap corruption.
132      const elements: FixedDoubleArray = AllocateFixedDoubleArrayWithHoles(
133          SmiUntag(length), AllocationFlag::kAllowLargeObjectAllocation);
134      a = NewJSArray(map, this.fixedArray);
135      for (let i: Smi = 0; i < validLength; i++) {
136        typeswitch (
137            UnsafeCast<(Number | TheHole)>(this.fixedArray.objects[i])) {
138          case (n: Number): {
139            elements.floats[i] = Convert<float64_or_hole>(n);
140          }
141          case (TheHole): {
142          }
143        }
144      }
145      a.elements = elements;
146    } else {
147      // Simply install the given fixedArray in {vector}.
148      a = NewJSArray(map, this.fixedArray);
149    }
150
151    // Paranoia. the FixedArray now "belongs" to JSArray {a}.
152    this.fixedArray = kEmptyFixedArray;
153    return a;
154  }
155
156  macro StoreResult(implicit context: Context)(index: Smi, result: JSAny) {
157    typeswitch (result) {
158      case (s: Smi): {
159        this.fixedArray.objects[index] = s;
160      }
161      case (s: HeapNumber): {
162        this.onlySmis = false;
163        this.fixedArray.objects[index] = s;
164      }
165      case (s: JSAnyNotNumber): {
166        this.onlySmis = false;
167        this.onlyNumbers = false;
168        this.fixedArray.objects[index] = s;
169      }
170    }
171  }
172
173  fixedArray: FixedArray;
174  onlySmis: bool;         // initially true.
175  onlyNumbers: bool;      // initially true.
176  skippedElements: bool;  // initially false.
177}
178
179macro NewVector(implicit context: Context)(length: Smi): Vector {
180  const fixedArray = length > 0 ?
181      AllocateFixedArrayWithHoles(
182          SmiUntag(length), AllocationFlag::kAllowLargeObjectAllocation) :
183      kEmptyFixedArray;
184  return Vector{
185    fixedArray,
186    onlySmis: true,
187    onlyNumbers: true,
188    skippedElements: false
189  };
190}
191
192transitioning macro FastArrayMap(implicit context: Context)(
193    fastO: FastJSArrayForRead, len: Smi, callbackfn: Callable,
194    thisArg: JSAny): JSArray
195    labels Bailout(JSArray, Smi) {
196  let k: Smi = 0;
197  let fastOW = NewFastJSArrayForReadWitness(fastO);
198  let vector = NewVector(len);
199
200  // Build a fast loop over the smi array.
201  // 7. Repeat, while k < len.
202  try {
203    for (; k < len; k++) {
204      fastOW.Recheck() otherwise goto PrepareBailout(k);
205
206      // Ensure that we haven't walked beyond a possibly updated length.
207      if (k >= fastOW.Get().length) goto PrepareBailout(k);
208
209      try {
210        const value: JSAny = fastOW.LoadElementNoHole(k)
211            otherwise FoundHole;
212        const result: JSAny =
213            Call(context, callbackfn, thisArg, value, k, fastOW.Get());
214        vector.StoreResult(k, result);
215      } label FoundHole {
216        // Our output array must necessarily be holey because of holes in
217        // the input array.
218        vector.ReportSkippedElement();
219      }
220    }
221  } label PrepareBailout(k: Smi) deferred {
222    // Transform {vector} into a JSArray and bail out.
223    goto Bailout(vector.CreateJSArray(k), k);
224  }
225
226  return vector.CreateJSArray(len);
227}
228
229// https://tc39.github.io/ecma262/#sec-array.prototype.map
230transitioning javascript builtin
231ArrayMap(
232    js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
233  try {
234    RequireObjectCoercible(receiver, 'Array.prototype.map');
235
236    // 1. Let O be ? ToObject(this value).
237    const o: JSReceiver = ToObject_Inline(context, receiver);
238
239    // 2. Let len be ? ToLength(? Get(O, "length")).
240    const len: Number = GetLengthProperty(o);
241
242    // 3. If IsCallable(callbackfn) is false, throw a TypeError exception.
243    if (arguments.length == 0) goto TypeError;
244
245    const callbackfn = Cast<Callable>(arguments[0]) otherwise TypeError;
246
247    // 4. If thisArg is present, let T be thisArg; else let T be undefined.
248    const thisArg: JSAny = arguments[1];
249
250    let array: JSReceiver;
251    let k: Number = 0;
252    try {
253      // 5. Let A be ? ArraySpeciesCreate(O, len).
254      if (IsArraySpeciesProtectorCellInvalid()) goto SlowSpeciesCreate;
255      const o: FastJSArrayForRead = Cast<FastJSArrayForRead>(receiver)
256          otherwise SlowSpeciesCreate;
257      const smiLength: Smi = Cast<Smi>(len)
258          otherwise SlowSpeciesCreate;
259
260      return FastArrayMap(o, smiLength, callbackfn, thisArg)
261          otherwise Bailout;
262    } label SlowSpeciesCreate {
263      array = ArraySpeciesCreate(context, receiver, len);
264    } label Bailout(output: JSArray, kValue: Smi) deferred {
265      array = output;
266      k = kValue;
267    }
268
269    return ArrayMapLoopContinuation(o, callbackfn, thisArg, array, o, k, len);
270  } label TypeError deferred {
271    ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]);
272  }
273}
274}
275