summaryrefslogtreecommitdiff
path: root/deps/v8/src/builtins/array-slice.tq
blob: 615b4b70730fd4bddbcecac80cdd4070972846b3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

namespace array {
  macro HandleSimpleArgumentsSlice(
      context: Context, args: JSArgumentsObjectWithLength, start: Smi,
      count: Smi): JSArray
      labels Bailout {
    // If the resulting array doesn't fit in new space, use the slow path.
    if (count >= kMaxNewSpaceFixedArrayElements) goto Bailout;

    const end: Smi = start + count;
    const sourceElements: FixedArray =
        Cast<FixedArray>(args.elements) otherwise Bailout;
    if (SmiAbove(end, sourceElements.length)) goto Bailout;

    const arrayMap: Map = LoadJSArrayElementsMap(HOLEY_ELEMENTS, context);
    const result: JSArray =
        AllocateJSArray(HOLEY_ELEMENTS, arrayMap, count, count);
    const newElements: FixedArray =
        Cast<FixedArray>(result.elements) otherwise Bailout;
    CopyElements(
        PACKED_ELEMENTS, newElements, 0, sourceElements, Convert<intptr>(start),
        Convert<intptr>(count));
    return result;
  }

  macro HandleFastAliasedSloppyArgumentsSlice(
      context: Context, args: JSArgumentsObjectWithLength, start: Smi,
      count: Smi): JSArray
      labels Bailout {
    // If the resulting array doesn't fit in new space, use the slow path.
    if (count >= kMaxNewSpaceFixedArrayElements) goto Bailout;

    const sloppyElements: SloppyArgumentsElements =
        Cast<SloppyArgumentsElements>(args.elements) otherwise Bailout;
    const sloppyElementsLength: Smi = sloppyElements.length;
    const parameterMapLength: Smi =
        sloppyElementsLength - kSloppyArgumentsParameterMapStart;

    // Check to make sure that the extraction will not access outside the
    // defined arguments
    const end: Smi = start + count;
    const unmappedElements: FixedArray =
        Cast<FixedArray>(sloppyElements[kSloppyArgumentsArgumentsIndex])
        otherwise Bailout;
    const unmappedElementsLength: Smi = unmappedElements.length;
    if (SmiAbove(end, unmappedElementsLength)) goto Bailout;

    const argumentsContext: Context =
        UnsafeCast<Context>(sloppyElements[kSloppyArgumentsContextIndex]);

    const arrayMap: Map = LoadJSArrayElementsMap(HOLEY_ELEMENTS, context);
    const result: JSArray =
        AllocateJSArray(HOLEY_ELEMENTS, arrayMap, count, count);

    let indexOut: Smi = 0;
    const resultElements: FixedArray = UnsafeCast<FixedArray>(result.elements);
    const to: Smi = SmiMin(parameterMapLength, end);

    // Fill in the part of the result that map to context-mapped parameters.
    for (let current: Smi = start; current < to; ++current) {
      const e: Object =
          sloppyElements[current + kSloppyArgumentsParameterMapStart];
      const newElement: Object = e != Hole ?
          argumentsContext[UnsafeCast<Smi>(e)] :
          unmappedElements[current];
      StoreFixedArrayElementSmi(
          resultElements, indexOut++, newElement, SKIP_WRITE_BARRIER);
    }

    // Fill in the rest of the result that contains the unmapped parameters
    // above the formal parameters.
    const unmappedFrom: Smi = SmiMin(SmiMax(parameterMapLength, start), end);
    const restCount: Smi = end - unmappedFrom;
    CopyElements(
        PACKED_ELEMENTS, resultElements, Convert<intptr>(indexOut),
        unmappedElements, Convert<intptr>(unmappedFrom),
        Convert<intptr>(restCount));
    return result;
  }

  macro HandleFastSlice(
      context: Context, o: Object, startNumber: Number,
      countNumber: Number): JSArray
      labels Bailout {
    const start: Smi = Cast<Smi>(startNumber) otherwise Bailout;
    const count: Smi = Cast<Smi>(countNumber) otherwise Bailout;
    assert(start >= 0);

    typeswitch (o) {
      case (a: FastJSArrayForCopy): {
        // It's possible to modify the array length from a valueOf
        // callback between the original array length read and this
        // point. That can change the length of the array backing store,
        // in the worst case, making it smaller than the region that needs
        // to be copied out. Therefore, re-check the length before calling
        // the appropriate fast path. See regress-785804.js
        if (SmiAbove(start + count, a.length)) goto Bailout;
        return ExtractFastJSArray(context, a, start, count);
      }
      case (a: JSArgumentsObjectWithLength): {
        const nativeContext: NativeContext = LoadNativeContext(context);
        const map: Map = a.map;
        if (IsFastAliasedArgumentsMap(map)) {
          return HandleFastAliasedSloppyArgumentsSlice(context, a, start, count)
              otherwise Bailout;
        } else if (IsStrictArgumentsMap(map) || IsSloppyArgumentsMap(map)) {
          return HandleSimpleArgumentsSlice(context, a, start, count)
              otherwise Bailout;
        }
      }
      case (Object): {
      }
    }
    goto Bailout;
  }

  // https://tc39.github.io/ecma262/#sec-array.prototype.slice
  transitioning javascript builtin
  ArrayPrototypeSlice(context: Context, receiver: Object, ...arguments):
      Object {
    // Handle array cloning case if the receiver is a fast array.
    if (arguments.length == 0) {
      typeswitch (receiver) {
        case (a: FastJSArrayForCopy): {
          return CloneFastJSArray(context, a);
        }
        case (Object): {
        }
      }
    }

    // 1. Let O be ? ToObject(this value).
    const o: JSReceiver = ToObject_Inline(context, receiver);

    // 2. Let len be ? ToLength(? Get(O, "length")).
    const len: Number = GetLengthProperty(o);

    // 3. Let relativeStart be ? ToInteger(start).
    const start: Object = arguments[0];
    const relativeStart: Number = ToInteger_Inline(context, start);

    // 4. If relativeStart < 0, let k be max((len + relativeStart), 0);
    //    else let k be min(relativeStart, len).
    let k: Number = relativeStart < 0 ? Max((len + relativeStart), 0) :
                                        Min(relativeStart, len);

    // 5. If end is undefined, let relativeEnd be len;
    //    else let relativeEnd be ? ToInteger(end).
    const end: Object = arguments[1];
    const relativeEnd: Number =
        end == Undefined ? len : ToInteger_Inline(context, end);

    // 6. If relativeEnd < 0, let final be max((len + relativeEnd), 0);
    //    else let final be min(relativeEnd, len).
    const final: Number =
        relativeEnd < 0 ? Max((len + relativeEnd), 0) : Min(relativeEnd, len);

    // 7. Let count be max(final - k, 0).
    const count: Number = Max(final - k, 0);

    assert(0 <= k);
    assert(k <= len);
    assert(0 <= final);
    assert(final <= len);
    assert(0 <= count);
    assert(count <= len);

    try {
      return HandleFastSlice(context, o, k, count) otherwise Slow;
    }
    label Slow {}

    // 8. Let A be ? ArraySpeciesCreate(O, count).
    const a: JSReceiver = ArraySpeciesCreate(context, o, count);

    // 9. Let n be 0.
    let n: Number = 0;

    // 10. Repeat, while k < final
    while (k < final) {
      // a. Let Pk be ! ToString(k).
      let pK: Number = k;

      // b. Let kPresent be ? HasProperty(O, Pk).
      const fromPresent: Boolean = HasProperty(o, pK);

      // c. If kPresent is true, then
      if (fromPresent == True) {
        // i. Let kValue be ? Get(O, Pk).
        const kValue: Object = GetProperty(o, pK);

        // ii. Perform ? CreateDataPropertyOrThrow(A, ! ToString(n), kValue).
        CreateDataProperty(a, n, kValue);
      }

      // d. Increase k by 1.
      k++;

      // e. Increase n by 1.
      n++;
    }

    // 11. Perform ? Set(A, "length", n, true).
    SetProperty(a, kLengthString, n);

    // 12. Return A.
    return a;
  }
}