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

module array {
6 7 8 9 10 11 12 13 14 15
  macro GetLengthProperty(context: Context, o: Object): Number {
    if (BranchIfFastJSArray(o, context)) {
      let a: JSArray = unsafe_cast<JSArray>(o);
      return a.length_fast;
    } else
      deferred {
        return ToLength_Inline(context, GetProperty(context, o, 'length'));
      }
  }

16
  macro FastArraySplice(
17
      context: Context, args: constexpr Arguments, o: Object,
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
      originalLengthNumber: Number, actualStartNumber: Number, insertCount: Smi,
      actualDeleteCountNumber: Number): Object
  labels Bailout {
    let originalLength: Smi = cast<Smi>(originalLengthNumber) otherwise Bailout;
    let actualStart: Smi = cast<Smi>(actualStartNumber) otherwise Bailout;
    let actualDeleteCount: Smi =
        cast<Smi>(actualDeleteCountNumber) otherwise Bailout;
    let lengthDelta: Smi = insertCount - actualDeleteCount;
    let newLength: Smi = originalLength + lengthDelta;

    let a: JSArray = cast<JSArray>(o) otherwise Bailout;

    let map: Map = a.map;
    if (!IsPrototypeInitialArrayPrototype(context, map)) goto Bailout;
    if (IsNoElementsProtectorCellInvalid()) goto Bailout;
33
    if (IsArraySpeciesProtectorCellInvalid()) goto Bailout;
34 35

    // Fast path only works on fast elements kind and with writable length.
36
    let elementsKind: ElementsKind = EnsureArrayPushable(map) otherwise Bailout;
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
    if (!IsFastElementsKind(elementsKind)) goto Bailout;

    // For now, only support non-double fast elements
    if (!IsFastSmiOrTaggedElementsKind(elementsKind)) goto Bailout;

    if (IsFastSmiElementsKind(elementsKind)) {
      for (let e: Object of args [2: ]) {
        if (TaggedIsNotSmi(e)) goto Bailout;
      }
    }

    // Make sure that the length hasn't been changed by side-effect.
    let length: Smi = cast<Smi>(a.length) otherwise Bailout;
    if (originalLength != length) goto Bailout;

    let deletedResult: JSArray =
        ExtractFastJSArray(context, a, actualStart, actualDeleteCount);

    if (newLength == 0) {
      a.elements = kEmptyFixedArray;
      a.length = 0;
      return deletedResult;
    }

    let elements: FixedArray = cast<FixedArray>(a.elements) otherwise Bailout;
    let elementsMap: Map = elements.map;

    // If the source is a COW array or the spliced array is larger then the
    // source array, then allocate a new FixedArray to hold the result.
    let newElements: FixedArray = elements;
    if ((elementsMap == kCOWMap) || (lengthDelta > 0)) {
      newElements = ExtractFixedArray(
          elements, 0, actualStart, newLength, kAllFixedArrays);
      newElements.map = elementsMap;
      a.elements = newElements;
    }

74 75 76
    // Double check that the array is still in fast elements mode
    assert(IsFastSmiElementsKind(a.map.elements_kind));

77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
    // Copy over inserted elements.
    let k: Smi = actualStart;
    if (insertCount > 0) {
      for (let e: Object of args [2: ]) {
        newElements[k++] = e;
      }
    }

    // Copy over elements after deleted elements.
    let count: Smi = length - actualStart - actualDeleteCount;
    while (count > 0) {
      let e: Object = elements[k - lengthDelta];
      newElements[k++] = e;
      count--;
    }

    // Fill rest of spliced FixedArray with the hole, but only if the
    // destination FixedArray is the original array's, since otherwise the array
    // is pre-filled with holes.
    if (elements == newElements) {
      let limit: Smi = elements.length;
      while (k < limit) {
99
        newElements[k++] = Hole;
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
      }
    }

    // Update the array's length after all the FixedArray shuffling is done.
    a.length = newLength;

    return deletedResult;
  }

  // https://tc39.github.io/ecma262/#sec-array.prototype.splice
  javascript builtin ArraySpliceTorque(
      context: Context, receiver: Object, ...arguments): Object {
    // 1. Let O be ? ToObject(this value).
    let o: Object = ToObject(context, receiver);

    // 2. Let len be ? ToLength(? Get(O, "length")).
116
    let len: Number = GetLengthProperty(context, o);
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

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

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

    let insertCount: Smi;
    let actualDeleteCount: Number;
    // 5. If the Number of actual arguments is 0, then
    if (arguments.length == 0) {
      // a. Let insertCount be 0.
      insertCount = 0;
      // b. Let actualDeleteCount be 0.
      actualDeleteCount = 0;
      // 6. Else if the Number of actual arguments is 1, then
    } else if (arguments.length == 1) {
      // a. Let insertCount be 0.
      insertCount = 0;
      // b. Let actualDeleteCount be len - actualStart.
      actualDeleteCount = len - actualStart;
      // 7. Else,
    } else {
      // a. Let insertCount be the Number of actual arguments minus 2.
      insertCount = convert<Smi>(arguments.length) - 2;
      // b. Let dc be ? ToInteger(deleteCount).
      let deleteCount: Object = arguments[1];
      let dc: Number = ToInteger_Inline(context, deleteCount);
      // c. Let actualDeleteCount be min(max(dc, 0), len - actualStart).
      actualDeleteCount = min(max(dc, 0), len - actualStart);
    }

    // 8. If len + insertCount - actualDeleteCount > 2^53-1, throw a
    //    Bailout exception.
    if (len + insertCount - actualDeleteCount > kMaxSafeInteger) {
157
      ThrowRangeError(context, kInvalidArrayLength);
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
    }

    try {
      return FastArraySplice(
          context, arguments, o, len, actualStart, insertCount,
          actualDeleteCount) otherwise Bailout;
    }
    label Bailout {}
    // If the fast case fails, just continue with the slow, correct,
    // spec-compliant case.

    // 9. Let A be ? ArraySpeciesCreate(O, actualDeleteCount).
    let a: Object = ArraySpeciesCreate(context, o, actualDeleteCount);

    // 10. Let k be 0.
    let k: Number = 0;

    // 11. Repeat, while k < actualDeleteCount
    while (k < actualDeleteCount) {
      // a. Let from be ! ToString(actualStart + k).
      let from: String = ToString_Inline(context, actualStart + k);

      // b. Let fromPresent be ? HasProperty(O, from).
      let fromPresent: Oddball =
          HasPropertyObject(o, from, context, kHasProperty);

      // c. If fromPresent is true, then
185
      if (fromPresent == True) {
186 187 188 189 190 191 192 193 194 195 196 197
        // i. Let fromValue be ? Get(O, from).
        let fromValue: Object = GetProperty(context, o, from);

        // ii. Perform ? CreateDataPropertyOrThrow(A, ! ToString(k), fromValue).
        CreateDataProperty(context, a, ToString_Inline(context, k), fromValue);
      }

      // d. Increment k by 1.
      k = k + 1;
    }

    // 12. Perform ? Set(A, "length", actualDeleteCount, true).
198
    SetProperty(context, a, 'length', actualDeleteCount, kStrict);
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223

    // 13. Let items be a List whose elements are, in left-to-right order,
    //     the portion of the actual argument list starting with the third
    //     argument. The list is empty if fewer than three arguments were
    //     passed.
    // 14. Let itemCount be the Number of elements in items.
    let itemCount: Number = insertCount;

    // 15. If itemCount < actualDeleteCount, then
    if (itemCount < actualDeleteCount) {
      // a. Let k be actualStart.
      let k: Number = actualStart;

      // b. Repeat, while k < (len - actualDeleteCount)
      while (k < (len - actualDeleteCount)) {
        // i. Let from be ! ToString(k + actualDeleteCount).
        let from: String = ToString_Inline(context, k + actualDeleteCount);
        // ii. Let to be ! ToString(k + itemCount).
        let to: String = ToString_Inline(context, k + itemCount);

        // iii. Let fromPresent be ? HasProperty(O, from).
        let fromPresent: Oddball =
            HasPropertyObject(o, from, context, kHasProperty);

        // iv. If fromPresent is true, then
224
        if (fromPresent == True) {
225 226 227 228
          // 1. Let fromValue be ? Get(O, from).
          let fromValue: Object = GetProperty(context, o, from);

          // 2. Perform ? Set(O, to, fromValue, true).
229
          SetProperty(context, o, to, fromValue, kStrict);
230 231 232 233

          // v. Else fromPresent is false,
        } else {
          // 1. Perform ? DeletePropertyOrThrow(O, to).
234
          DeleteProperty(context, o, to, kStrict);
235 236 237 238 239 240 241 242 243 244
        }
        // vi. Increase k by 1.
        k = k + 1;
      }

      // c. Let k be len.
      k = len;
      // d. Repeat, while k > (len - actualDeleteCount + itemCount)
      while (k > (len - actualDeleteCount + itemCount)) {
        // i. Perform ? DeletePropertyOrThrow(O, ! ToString(k - 1)).
245
        DeleteProperty(context, o, ToString_Inline(context, k - 1), kStrict);
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267

        // ii. Decrease k by 1.
        k = k - 1;
      }
      // 16. Else if itemCount > actualDeleteCount, then
    } else if (itemCount > actualDeleteCount) {
      // a. Let k be (len - actualDeleteCount).
      let k: Number = len - actualDeleteCount;

      // b. Repeat, while k > actualStart
      while (k > actualStart) {
        // i. Let from be ! ToString(k + actualDeleteCount - 1).
        let from: String = ToString_Inline(context, k + actualDeleteCount - 1);

        // ii. Let to be ! ToString(k + itemCount - 1).
        let to: String = ToString_Inline(context, k + itemCount - 1);

        // iii. Let fromPresent be ? HasProperty(O, from).
        let fromPresent: Oddball =
            HasPropertyObject(o, from, context, kHasProperty);

        // iv. If fromPresent is true, then
268
        if (fromPresent == True) {
269 270 271 272
          // 1. Let fromValue be ? Get(O, from).
          let fromValue: Object = GetProperty(context, o, from);

          // 2. Perform ? Set(O, to, fromValue, true).
273
          SetProperty(context, o, to, fromValue, kStrict);
274 275 276 277

          // v. Else fromPresent is false,
        } else {
          // 1. Perform ? DeletePropertyOrThrow(O, to).
278
          DeleteProperty(context, o, to, kStrict);
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294
        }

        // vi. Decrease k by 1.
        k = k - 1;
      }
    }

    // 17. Let k be actualStart.
    k = actualStart;

    // 18. Repeat, while items is not empty
    //   a. Remove the first element from items and let E be the value of that
    //   element.
    if (arguments.length > 2) {
      for (let e: Object of arguments [2: ]) {
        // b. Perform ? Set(O, ! ToString(k), E, true).
295
        SetProperty(context, o, ToString_Inline(context, k), e, kStrict);
296 297 298 299 300 301 302 303 304

        // c. Increase k by 1.
        k = k + 1;
      }
    }

    // 19. Perform ? Set(O, "length", len - actualDeleteCount + itemCount,
    // true).
    SetProperty(
305
        context, o, 'length', len - actualDeleteCount + itemCount, kStrict);
306 307 308 309

    return a;
  }
}