• 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(): void {
101    this.skippedElements = true;
102  }
103
104  macro CreateJSArray(implicit context: Context)(validLength: Smi): JSArray {
105    const length: Smi = this.fixedArray.length;
106    dcheck(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)(
157      index: Smi, result: JSAny): void {
158    typeswitch (result) {
159      case (s: Smi): {
160        this.fixedArray.objects[index] = s;
161      }
162      case (s: HeapNumber): {
163        this.onlySmis = false;
164        this.fixedArray.objects[index] = s;
165      }
166      case (s: JSAnyNotNumber): {
167        this.onlySmis = false;
168        this.onlyNumbers = false;
169        this.fixedArray.objects[index] = s;
170      }
171    }
172  }
173
174  fixedArray: FixedArray;
175  onlySmis: bool;         // initially true.
176  onlyNumbers: bool;      // initially true.
177  skippedElements: bool;  // initially false.
178}
179
180macro NewVector(implicit context: Context)(length: Smi): Vector {
181  const fixedArray = length > 0 ?
182      AllocateFixedArrayWithHoles(
183          SmiUntag(length), AllocationFlag::kAllowLargeObjectAllocation) :
184      kEmptyFixedArray;
185  return Vector{
186    fixedArray,
187    onlySmis: true,
188    onlyNumbers: true,
189    skippedElements: false
190  };
191}
192
193transitioning macro FastArrayMap(implicit context: Context)(
194    fastO: FastJSArrayForRead, len: Smi, callbackfn: Callable,
195    thisArg: JSAny): JSArray
196    labels Bailout(JSArray, Smi) {
197  let k: Smi = 0;
198  let fastOW = NewFastJSArrayForReadWitness(fastO);
199  let vector = NewVector(len);
200
201  // Build a fast loop over the smi array.
202  // 7. Repeat, while k < len.
203  try {
204    for (; k < len; k++) {
205      fastOW.Recheck() otherwise goto PrepareBailout(k);
206
207      // Ensure that we haven't walked beyond a possibly updated length.
208      if (k >= fastOW.Get().length) goto PrepareBailout(k);
209
210      try {
211        const value: JSAny = fastOW.LoadElementNoHole(k)
212            otherwise FoundHole;
213        const result: JSAny =
214            Call(context, callbackfn, thisArg, value, k, fastOW.Get());
215        vector.StoreResult(k, result);
216      } label FoundHole {
217        // Our output array must necessarily be holey because of holes in
218        // the input array.
219        vector.ReportSkippedElement();
220      }
221    }
222  } label PrepareBailout(k: Smi) deferred {
223    // Transform {vector} into a JSArray and bail out.
224    goto Bailout(vector.CreateJSArray(k), k);
225  }
226
227  return vector.CreateJSArray(len);
228}
229
230// https://tc39.github.io/ecma262/#sec-array.prototype.map
231transitioning javascript builtin
232ArrayMap(
233    js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
234  try {
235    RequireObjectCoercible(receiver, 'Array.prototype.map');
236
237    // 1. Let O be ? ToObject(this value).
238    const o: JSReceiver = ToObject_Inline(context, receiver);
239
240    // 2. Let len be ? ToLength(? Get(O, "length")).
241    const len: Number = GetLengthProperty(o);
242
243    // 3. If IsCallable(callbackfn) is false, throw a TypeError exception.
244    if (arguments.length == 0) goto TypeError;
245
246    const callbackfn = Cast<Callable>(arguments[0]) otherwise TypeError;
247
248    // 4. If thisArg is present, let T be thisArg; else let T be undefined.
249    const thisArg: JSAny = arguments[1];
250
251    let array: JSReceiver;
252    let k: Number = 0;
253    try {
254      // 5. Let A be ? ArraySpeciesCreate(O, len).
255      if (IsArraySpeciesProtectorCellInvalid()) goto SlowSpeciesCreate;
256      const o: FastJSArrayForRead = Cast<FastJSArrayForRead>(receiver)
257          otherwise SlowSpeciesCreate;
258      const smiLength: Smi = Cast<Smi>(len)
259          otherwise SlowSpeciesCreate;
260
261      return FastArrayMap(o, smiLength, callbackfn, thisArg)
262          otherwise Bailout;
263    } label SlowSpeciesCreate {
264      array = ArraySpeciesCreate(context, receiver, len);
265    } label Bailout(output: JSArray, kValue: Smi) deferred {
266      array = output;
267      k = kValue;
268    }
269
270    return ArrayMapLoopContinuation(o, callbackfn, thisArg, array, o, k, len);
271  } label TypeError deferred {
272    ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]);
273  }
274}
275}
276