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 {
6macro HandleSimpleArgumentsSlice(
7    context: NativeContext, args: JSArgumentsObjectWithLength, start: Smi,
8    count: Smi): JSArray
9    labels Bailout {
10  // If the resulting array doesn't fit in new space, use the slow path.
11  if (count >= kMaxNewSpaceFixedArrayElements) goto Bailout;
12
13  const end: Smi = start + count;
14  const sourceElements: FixedArray =
15      Cast<FixedArray>(args.elements) otherwise Bailout;
16  if (SmiAbove(end, sourceElements.length)) goto Bailout;
17
18  const arrayMap: Map =
19      LoadJSArrayElementsMap(ElementsKind::HOLEY_ELEMENTS, context);
20  const result: JSArray =
21      AllocateJSArray(ElementsKind::HOLEY_ELEMENTS, arrayMap, count, count);
22  const newElements: FixedArray =
23      Cast<FixedArray>(result.elements) otherwise Bailout;
24  CopyElements(
25      ElementsKind::PACKED_ELEMENTS, newElements, 0, sourceElements,
26      Convert<intptr>(start), Convert<intptr>(count));
27  return result;
28}
29
30macro HandleFastAliasedSloppyArgumentsSlice(
31    context: NativeContext, args: JSArgumentsObjectWithLength, start: Smi,
32    count: Smi): JSArray
33    labels Bailout {
34  // If the resulting array doesn't fit in new space, use the slow path.
35  if (count >= kMaxNewSpaceFixedArrayElements) goto Bailout;
36
37  const sloppyElements: SloppyArgumentsElements =
38      Cast<SloppyArgumentsElements>(args.elements) otherwise Bailout;
39  const parameterMapLength: Smi = sloppyElements.length;
40
41  // Check to make sure that the extraction will not access outside the
42  // defined arguments
43  const end: Smi = start + count;
44  const unmappedElements: FixedArray =
45      Cast<FixedArray>(sloppyElements.arguments)
46      otherwise Bailout;
47  const unmappedElementsLength: Smi = unmappedElements.length;
48  if (SmiAbove(end, unmappedElementsLength)) goto Bailout;
49
50  const argumentsContext: Context = sloppyElements.context;
51
52  const arrayMap: Map =
53      LoadJSArrayElementsMap(ElementsKind::HOLEY_ELEMENTS, context);
54  const result: JSArray =
55      AllocateJSArray(ElementsKind::HOLEY_ELEMENTS, arrayMap, count, count);
56
57  let indexOut: Smi = 0;
58  const resultElements: FixedArray = UnsafeCast<FixedArray>(result.elements);
59  const to: Smi = SmiMin(parameterMapLength, end);
60
61  // Fill in the part of the result that map to context-mapped parameters.
62  for (let current: Smi = start; current < to; ++current) {
63    const e: Object = sloppyElements.mapped_entries[current];
64    const newElement = UnsafeCast<(JSAny | TheHole)>(
65        e != TheHole ? argumentsContext.elements[UnsafeCast<Smi>(e)] :
66                       unmappedElements.objects[current]);
67    // It is safe to skip the write barrier here because resultElements was
68    // allocated together with result in a folded allocation.
69    // TODO(tebbi): The verification of this fails at the moment due to
70    // missing load elimination.
71    StoreFixedArrayElement(
72        resultElements, indexOut++, newElement, UNSAFE_SKIP_WRITE_BARRIER);
73  }
74
75  // Fill in the rest of the result that contains the unmapped parameters
76  // above the formal parameters.
77  const unmappedFrom: Smi = SmiMin(SmiMax(parameterMapLength, start), end);
78  const restCount: Smi = end - unmappedFrom;
79  CopyElements(
80      ElementsKind::PACKED_ELEMENTS, resultElements, Convert<intptr>(indexOut),
81      unmappedElements, Convert<intptr>(unmappedFrom),
82      Convert<intptr>(restCount));
83  return result;
84}
85
86macro HandleFastSlice(
87    context: NativeContext, o: JSAny, startNumber: Number,
88    countNumber: Number): JSArray
89    labels Bailout {
90  const start: Smi = Cast<Smi>(startNumber) otherwise Bailout;
91  const count: Smi = Cast<Smi>(countNumber) otherwise Bailout;
92  assert(start >= 0);
93
94  try {
95    typeswitch (o) {
96      case (a: FastJSArrayForCopy): {
97        // It's possible to modify the array length from a valueOf
98        // callback between the original array length read and this
99        // point. That can change the length of the array backing store,
100        // in the worst case, making it smaller than the region that needs
101        // to be copied out. Therefore, re-check the length before calling
102        // the appropriate fast path. See regress-785804.js
103        if (SmiAbove(start + count, a.length)) goto Bailout;
104        return ExtractFastJSArray(context, a, start, count);
105      }
106      case (a: JSStrictArgumentsObject): {
107        goto HandleSimpleArgumentsSlice(a);
108      }
109      case (a: JSSloppyArgumentsObject): {
110        const map: Map = a.map;
111        if (IsFastAliasedArgumentsMap(map)) {
112          return HandleFastAliasedSloppyArgumentsSlice(context, a, start, count)
113              otherwise Bailout;
114        } else if (IsSloppyArgumentsMap(map)) {
115          goto HandleSimpleArgumentsSlice(a);
116        }
117        goto Bailout;
118      }
119      case (JSAny): {
120        goto Bailout;
121      }
122    }
123  } label HandleSimpleArgumentsSlice(a: JSArgumentsObjectWithLength) {
124    return HandleSimpleArgumentsSlice(context, a, start, count)
125        otherwise Bailout;
126  }
127}
128
129// https://tc39.github.io/ecma262/#sec-array.prototype.slice
130transitioning javascript builtin
131ArrayPrototypeSlice(
132    js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
133  // Handle array cloning case if the receiver is a fast array.
134  if (arguments.length == 0) {
135    typeswitch (receiver) {
136      case (a: FastJSArrayForCopy): {
137        return CloneFastJSArray(context, a);
138      }
139      case (JSAny): {
140      }
141    }
142  }
143
144  // 1. Let O be ? ToObject(this value).
145  const o: JSReceiver = ToObject_Inline(context, receiver);
146
147  // 2. Let len be ? ToLength(? Get(O, "length")).
148  const len: Number = GetLengthProperty(o);
149
150  // 3. Let relativeStart be ? ToInteger(start).
151  const start: JSAny = arguments[0];
152  const relativeStart: Number = ToInteger_Inline(start);
153
154  // 4. If relativeStart < 0, let k be max((len + relativeStart), 0);
155  //    else let k be min(relativeStart, len).
156  let k: Number = relativeStart < 0 ? Max((len + relativeStart), 0) :
157                                      Min(relativeStart, len);
158
159  // 5. If end is undefined, let relativeEnd be len;
160  //    else let relativeEnd be ? ToInteger(end).
161  const end: JSAny = arguments[1];
162  const relativeEnd: Number = end == Undefined ? len : ToInteger_Inline(end);
163
164  // 6. If relativeEnd < 0, let final be max((len + relativeEnd), 0);
165  //    else let final be min(relativeEnd, len).
166  const final: Number =
167      relativeEnd < 0 ? Max((len + relativeEnd), 0) : Min(relativeEnd, len);
168
169  // 7. Let count be max(final - k, 0).
170  const count: Number = Max(final - k, 0);
171
172  assert(0 <= k);
173  assert(k <= len);
174  assert(0 <= final);
175  assert(final <= len);
176  assert(0 <= count);
177  assert(count <= len);
178
179  try {
180    return HandleFastSlice(context, o, k, count)
181        otherwise Slow;
182  } label Slow {}
183
184  // 8. Let A be ? ArraySpeciesCreate(O, count).
185  const a: JSReceiver = ArraySpeciesCreate(context, o, count);
186
187  // 9. Let n be 0.
188  let n: Number = 0;
189
190  // 10. Repeat, while k < final
191  while (k < final) {
192    // a. Let Pk be ! ToString(k).
193    const pK: Number = k;
194
195    // b. Let kPresent be ? HasProperty(O, Pk).
196    const fromPresent: Boolean = HasProperty(o, pK);
197
198    // c. If kPresent is true, then
199    if (fromPresent == True) {
200      // i. Let kValue be ? Get(O, Pk).
201      const kValue: JSAny = GetProperty(o, pK);
202
203      // ii. Perform ? CreateDataPropertyOrThrow(A, ! ToString(n), kValue).
204      FastCreateDataProperty(a, n, kValue);
205    }
206
207    // d. Increase k by 1.
208    k++;
209
210    // e. Increase n by 1.
211    n++;
212  }
213
214  // 11. Perform ? Set(A, "length", n, true).
215  SetProperty(a, kLengthString, n);
216
217  // 12. Return A.
218  return a;
219}
220}
221