array-map.tq 9.67 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_map {
6
  transitioning javascript builtin
7 8 9 10
  ArrayMapLoopEagerDeoptContinuation(
      js-implicit context: Context, receiver: Object)(
      callback: Object, thisArg: Object, array: Object, initialK: Object,
      length: Object): Object {
11 12 13 14 15 16 17
    // All continuation points in the optimized filter implementation are
    // after the ToObject(O) call that ensures we are dealing with a
    // JSReceiver.
    //
    // Also, this great mass of casts is necessary because the signature
    // of Torque javascript builtins requires Object type for all parameters
    // other than {context}.
18 19 20 21 22
    const jsreceiver = Cast<JSReceiver>(receiver) otherwise unreachable;
    const callbackfn = Cast<Callable>(callback) otherwise unreachable;
    const outputArray = Cast<JSReceiver>(array) otherwise unreachable;
    const numberK = Cast<Number>(initialK) otherwise unreachable;
    const numberLength = Cast<Number>(length) otherwise unreachable;
23 24 25

    return ArrayMapLoopContinuation(
        jsreceiver, callbackfn, thisArg, outputArray, jsreceiver, numberK,
26
        numberLength);
27 28 29
  }

  transitioning javascript builtin
30 31 32 33
  ArrayMapLoopLazyDeoptContinuation(
      js-implicit context: Context, receiver: Object)(
      callback: Object, thisArg: Object, array: Object, initialK: Object,
      length: Object, result: Object): Object {
34 35 36
    // All continuation points in the optimized filter implementation are
    // after the ToObject(O) call that ensures we are dealing with a
    // JSReceiver.
37 38 39 40 41
    const jsreceiver = Cast<JSReceiver>(receiver) otherwise unreachable;
    const callbackfn = Cast<Callable>(callback) otherwise unreachable;
    const outputArray = Cast<JSReceiver>(array) otherwise unreachable;
    let numberK = Cast<Number>(initialK) otherwise unreachable;
    const numberLength = Cast<Number>(length) otherwise unreachable;
42 43 44 45 46 47 48

    // This custom lazy deopt point is right after the callback. map() needs
    // to pick up at the next step, which is setting the callback result in
    // the output array. After incrementing k, we can glide into the loop
    // continuation builtin.

    // iii. Perform ? CreateDataPropertyOrThrow(A, Pk, mappedValue).
49
    FastCreateDataProperty(outputArray, numberK, result);
50 51 52 53 54 55

    // 7d. Increase k by 1.
    numberK = numberK + 1;

    return ArrayMapLoopContinuation(
        jsreceiver, callbackfn, thisArg, outputArray, jsreceiver, numberK,
56
        numberLength);
57 58 59
  }

  transitioning builtin ArrayMapLoopContinuation(implicit context: Context)(
60
      _receiver: JSReceiver, callbackfn: Callable, thisArg: Object,
61 62
      array: JSReceiver, o: JSReceiver, initialK: Number,
      length: Number): Object {
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
    // 6. Let k be 0.
    // 7. Repeat, while k < len
    for (let k: Number = initialK; k < length; k++) {
      // 7a. Let Pk be ! ToString(k).
      // k is guaranteed to be a positive integer, hence ToString is
      // side-effect free and HasProperty/GetProperty do the conversion inline.

      // 7b. Let kPresent be ? HasProperty(O, Pk).
      const kPresent: Boolean = HasProperty_Inline(o, k);

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

        // ii. Let mapped_value be ? Call(callbackfn, T, kValue, k, O).
        const mappedValue: Object =
            Call(context, callbackfn, thisArg, kValue, k, o);

        // iii. Perform ? CreateDataPropertyOrThrow(A, Pk, mapped_value).
83
        FastCreateDataProperty(array, k, mappedValue);
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
      }

      // 7d. Increase k by 1. (done by the loop).
    }

    // 8. Return A.
    return array;
  }

  struct Vector {
    ReportSkippedElement() {
      this.skippedElements = true;
    }

    CreateJSArray(implicit context: Context)(validLength: Smi): JSArray {
99
      const length: Smi = this.fixedArray.length;
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
      assert(validLength <= length);
      let kind: ElementsKind = PACKED_SMI_ELEMENTS;
      if (!this.onlySmis) {
        if (this.onlyNumbers) {
          kind = PACKED_DOUBLE_ELEMENTS;
        } else {
          kind = PACKED_ELEMENTS;
        }
      }

      if (this.skippedElements || validLength < length) {
        // We also need to create a holey output array if we are
        // bailing out of the fast path partway through the array.
        // This is indicated by {validLength} < {length}.
        // Who knows if the bailout condition will continue to fill in
        // every element?
        kind = FastHoleyElementsKind(kind);
      }

119
      const map: Map = LoadJSArrayElementsMap(kind, LoadNativeContext(context));
120 121 122 123 124 125
      let a: JSArray;

      if (IsDoubleElementsKind(kind)) {
        // We need to allocate and copy.
        // First, initialize the elements field before allocation to prevent
        // heap corruption.
126 127
        const elements: FixedDoubleArray = AllocateFixedDoubleArrayWithHoles(
            SmiUntag(length), kAllowLargeObjectAllocation);
128
        a = NewJSArray(map, this.fixedArray);
129
        for (let i: Smi = 0; i < validLength; i++) {
130
          typeswitch (this.fixedArray.objects[i]) {
131
            case (n: Number): {
132
              elements.floats[i] = Convert<float64>(n);
133 134 135 136 137 138 139 140 141
            }
            case (h: HeapObject): {
              assert(h == Hole);
            }
          }
        }
        a.elements = elements;
      } else {
        // Simply install the given fixedArray in {vector}.
142
        a = NewJSArray(map, this.fixedArray);
143 144 145 146 147 148 149 150 151 152
      }

      // Paranoia. the FixedArray now "belongs" to JSArray {a}.
      this.fixedArray = kEmptyFixedArray;
      return a;
    }

    StoreResult(implicit context: Context)(index: Smi, result: Object) {
      typeswitch (result) {
        case (s: Smi): {
153
          this.fixedArray.objects[index] = s;
154 155 156
        }
        case (s: HeapNumber): {
          this.onlySmis = false;
157
          this.fixedArray.objects[index] = s;
158 159 160 161
        }
        case (s: HeapObject): {
          this.onlySmis = false;
          this.onlyNumbers = false;
162
          this.fixedArray.objects[index] = s;
163 164 165 166 167 168 169 170 171 172
        }
      }
    }

    fixedArray: FixedArray;
    onlySmis: bool;         // initially true.
    onlyNumbers: bool;      // initially true.
    skippedElements: bool;  // initially false.
  }

173 174 175 176 177
  macro NewVector(implicit context: Context)(length: Smi): Vector {
    const fixedArray = length > 0 ?
        AllocateFixedArrayWithHoles(
            SmiUntag(length), kAllowLargeObjectAllocation) :
        kEmptyFixedArray;
178 179 180 181 182 183
    return Vector{
      fixedArray,
      onlySmis: true,
      onlyNumbers: true,
      skippedElements: false
    };
184 185
  }

186
  transitioning macro FastArrayMap(implicit context: Context)(
187
      fastO: FastJSArrayForRead, len: Smi, callbackfn: Callable,
188
      thisArg: Object): JSArray
189 190
      labels Bailout(JSArray, Smi) {
    let k: Smi = 0;
191
    let fastOW = NewFastJSArrayForReadWitness(fastO);
192
    let vector = NewVector(len);
193 194 195

    // Build a fast loop over the smi array.
    // 7. Repeat, while k < len.
196
    try {
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
      for (; k < len; k++) {
        fastOW.Recheck() otherwise goto PrepareBailout(k);

        // Ensure that we haven't walked beyond a possibly updated length.
        if (k >= fastOW.Get().length) goto PrepareBailout(k);

        try {
          const value: Object = fastOW.LoadElementNoHole(k)
              otherwise FoundHole;
          const result: Object =
              Call(context, callbackfn, thisArg, value, k, fastOW.Get());
          vector.StoreResult(k, result);
        }
        label FoundHole {
          // Our output array must necessarily be holey because of holes in
          // the input array.
          vector.ReportSkippedElement();
        }
215 216
      }
    }
217 218
    label PrepareBailout(k: Smi) deferred {
      // Transform {vector} into a JSArray and bail out.
219 220 221 222 223 224 225 226
      goto Bailout(vector.CreateJSArray(k), k);
    }

    return vector.CreateJSArray(len);
  }

  // https://tc39.github.io/ecma262/#sec-array.prototype.map
  transitioning javascript builtin
227 228
  ArrayMap(js-implicit context: Context, receiver: Object)(...arguments):
      Object {
229 230 231 232 233 234 235 236 237 238 239 240
    try {
      if (IsNullOrUndefined(receiver)) goto NullOrUndefinedError;

      // 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. If IsCallable(callbackfn) is false, throw a TypeError exception.
      if (arguments.length == 0) goto TypeError;

241
      const callbackfn = Cast<Callable>(arguments[0]) otherwise TypeError;
242 243 244 245 246 247 248 249 250

      // 4. If thisArg is present, let T be thisArg; else let T be undefined.
      const thisArg: Object = arguments.length > 1 ? arguments[1] : Undefined;

      let array: JSReceiver;
      let k: Number = 0;
      try {
        // 5. Let A be ? ArraySpeciesCreate(O, len).
        if (IsArraySpeciesProtectorCellInvalid()) goto SlowSpeciesCreate;
251
        const o: FastJSArrayForRead = Cast<FastJSArrayForRead>(receiver)
252 253 254 255 256 257 258 259 260 261 262 263 264 265 266
            otherwise SlowSpeciesCreate;
        const smiLength: Smi = Cast<Smi>(len)
            otherwise SlowSpeciesCreate;

        return FastArrayMap(o, smiLength, callbackfn, thisArg)
            otherwise Bailout;
      }
      label SlowSpeciesCreate {
        array = ArraySpeciesCreate(context, receiver, len);
      }
      label Bailout(output: JSArray, kValue: Smi) deferred {
        array = output;
        k = kValue;
      }

267
      return ArrayMapLoopContinuation(o, callbackfn, thisArg, array, o, k, len);
268 269
    }
    label TypeError deferred {
270
      ThrowTypeError(kCalledNonCallable, arguments[0]);
271 272
    }
    label NullOrUndefinedError deferred {
273
      ThrowTypeError(kCalledOnNullOrUndefined, 'Array.prototype.map');
274 275 276
    }
  }
}