array-slice.tq 8.38 KB
Newer Older
1 2 3 4
// 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.

5
namespace array {
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
macro HandleSimpleArgumentsSlice(
    context: NativeContext, 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(ElementsKind::HOLEY_ELEMENTS, context);
  const result: JSArray =
      AllocateJSArray(ElementsKind::HOLEY_ELEMENTS, arrayMap, count, count);
  const newElements: FixedArray =
      Cast<FixedArray>(result.elements) otherwise Bailout;
  CopyElements(
      ElementsKind::PACKED_ELEMENTS, newElements, 0, sourceElements,
      Convert<intptr>(start), Convert<intptr>(count));
  return result;
}
29

30 31 32 33 34 35 36 37 38
macro HandleFastAliasedSloppyArgumentsSlice(
    context: NativeContext, 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;
39
  const parameterMapLength: Smi = sloppyElements.length;
40 41 42 43 44

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

50
  const argumentsContext: Context = sloppyElements.context;
51 52 53 54 55 56 57 58 59 60 61 62

  const arrayMap: Map =
      LoadJSArrayElementsMap(ElementsKind::HOLEY_ELEMENTS, context);
  const result: JSArray =
      AllocateJSArray(ElementsKind::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) {
63
    const e: Object = sloppyElements.mapped_entries[current];
64
    const newElement = UnsafeCast<(JSAny | TheHole)>(
65
        e != TheHole ? argumentsContext.elements[UnsafeCast<Smi>(e)] :
66 67 68
                       unmappedElements.objects[current]);
    // It is safe to skip the write barrier here because resultElements was
    // allocated together with result in a folded allocation.
69
    // TODO(turbofan): The verification of this fails at the moment due to
70 71 72
    // missing load elimination.
    StoreFixedArrayElement(
        resultElements, indexOut++, newElement, UNSAFE_SKIP_WRITE_BARRIER);
73 74
  }

75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
  // 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(
      ElementsKind::PACKED_ELEMENTS, resultElements, Convert<intptr>(indexOut),
      unmappedElements, Convert<intptr>(unmappedFrom),
      Convert<intptr>(restCount));
  return result;
}

macro HandleFastSlice(
    context: NativeContext, o: JSAny, startNumber: Number,
    countNumber: Number): JSArray
    labels Bailout {
  const start: Smi = Cast<Smi>(startNumber) otherwise Bailout;
  const count: Smi = Cast<Smi>(countNumber) otherwise Bailout;
92
  dcheck(start >= 0);
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114

  try {
    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: JSStrictArgumentsObject): {
        goto HandleSimpleArgumentsSlice(a);
      }
      case (a: JSSloppyArgumentsObject): {
        const map: Map = a.map;
        if (IsFastAliasedArgumentsMap(map)) {
          return HandleFastAliasedSloppyArgumentsSlice(context, a, start, count)
              otherwise Bailout;
        } else if (IsSloppyArgumentsMap(map)) {
115 116
          goto HandleSimpleArgumentsSlice(a);
        }
117 118 119 120
        goto Bailout;
      }
      case (JSAny): {
        goto Bailout;
121
      }
122
    }
123 124 125
  } label HandleSimpleArgumentsSlice(a: JSArgumentsObjectWithLength) {
    return HandleSimpleArgumentsSlice(context, a, start, count)
        otherwise Bailout;
126
  }
127
}
128

129 130 131 132 133 134
// https://tc39.github.io/ecma262/#sec-array.prototype.slice
transitioning javascript builtin
ArrayPrototypeSlice(
    js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
  // 1. Let O be ? ToObject(this value).
  const o: JSReceiver = ToObject_Inline(context, receiver);
135

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

139 140 141
  // 3. Let relativeStart be ? ToInteger(start).
  const start: JSAny = arguments[0];
  const relativeStart: Number = ToInteger_Inline(start);
142

143 144 145 146
  // 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);
147

148 149 150 151
  // 5. If end is undefined, let relativeEnd be len;
  //    else let relativeEnd be ? ToInteger(end).
  const end: JSAny = arguments[1];
  const relativeEnd: Number = end == Undefined ? len : ToInteger_Inline(end);
152

153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
  // Handle array cloning case if the receiver is a fast array. In the case
  // where relativeStart is 0 but start is not the SMI zero (e.g., start is an
  // object whose valueOf returns 0) we must not call CloneFastJSArray. This is
  // because CloneFastArray reloads the array length, and the ToInteger above
  // might have called user code which changed it. Thus, calling
  // CloneFastJSArray here is safe only if we know ToInteger didn't call user
  // code.

  // This logic should be in sync with ArrayPrototypeSlice (to a reasonable
  // degree). This is because CloneFastJSArray produces arrays which are
  // potentially COW. If there's a discrepancy, TF generates code which produces
  // a COW array and then expects it to be non-COW (or the other way around) ->
  // immediate deopt.
  if ((start == Undefined || TaggedEqual(start, SmiConstant(0))) &&
      end == Undefined) {
168 169 170 171 172 173 174 175 176
    typeswitch (receiver) {
      case (a: FastJSArrayForCopy): {
        return CloneFastJSArray(context, a);
      }
      case (JSAny): {
      }
    }
  }

177 178 179 180
  // 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);
181

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

185 186 187 188 189 190
  dcheck(0 <= k);
  dcheck(k <= len);
  dcheck(0 <= final);
  dcheck(final <= len);
  dcheck(0 <= count);
  dcheck(count <= len);
191

192 193 194 195
  try {
    return HandleFastSlice(context, o, k, count)
        otherwise Slow;
  } label Slow {}
196

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

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

203 204 205 206
  // 10. Repeat, while k < final
  while (k < final) {
    // a. Let Pk be ! ToString(k).
    const pK: Number = k;
207

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

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

216 217
      // ii. Perform ? CreateDataPropertyOrThrow(A, ! ToString(n), kValue).
      FastCreateDataProperty(a, n, kValue);
218 219
    }

220 221
    // d. Increase k by 1.
    k++;
222

223 224
    // e. Increase n by 1.
    n++;
225
  }
226 227 228 229 230 231 232

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

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