array-filter.tq 7.54 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
transitioning javascript builtin
ArrayFilterLoopEagerDeoptContinuation(
    js-implicit context: NativeContext, receiver: JSAny)(
    callback: JSAny, thisArg: JSAny, array: JSAny, initialK: JSAny,
    length: JSAny, initialTo: JSAny): JSAny {
  // 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 JSAny type for all parameters
  // other than {context}.
  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 numberTo = Cast<Number>(initialTo) otherwise unreachable;
  const numberLength = Cast<Number>(length) otherwise unreachable;

  return ArrayFilterLoopContinuation(
      jsreceiver, callbackfn, thisArg, outputArray, jsreceiver, numberK,
      numberLength, numberTo);
}
29

30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
transitioning javascript builtin
ArrayFilterLoopLazyDeoptContinuation(
    js-implicit context: NativeContext, receiver: JSAny)(
    callback: JSAny, thisArg: JSAny, array: JSAny, initialK: JSAny,
    length: JSAny, valueK: JSAny, initialTo: JSAny, result: JSAny): JSAny {
  // All continuation points in the optimized filter implementation are
  // after the ToObject(O) call that ensures we are dealing with a
  // JSReceiver.
  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;
  let numberTo = Cast<Number>(initialTo) otherwise unreachable;
  const numberLength = Cast<Number>(length) otherwise unreachable;

  // This custom lazy deopt point is right after the callback. filter() needs
  // to pick up at the next step, which is setting the callback
  // result in the output array. After incrementing k and to, we can glide
  // into the loop continuation builtin.
  if (ToBoolean(result)) {
    FastCreateDataProperty(outputArray, numberTo, valueK);
    numberTo = numberTo + 1;
52 53
  }

54
  numberK = numberK + 1;
55

56 57 58 59
  return ArrayFilterLoopContinuation(
      jsreceiver, callbackfn, thisArg, outputArray, jsreceiver, numberK,
      numberLength, numberTo);
}
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
transitioning builtin ArrayFilterLoopContinuation(implicit context: Context)(
    _receiver: JSReceiver, callbackfn: Callable, thisArg: JSAny,
    array: JSReceiver, o: JSReceiver, initialK: Number, length: Number,
    initialTo: Number): JSAny {
  let to: Number = initialTo;
  // 5. Let k be 0.
  // 6. Repeat, while k < len
  for (let k: Number = initialK; k < length; k++) {
    // 6a. 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.

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

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

      // 6c. ii. Perform ? Call(callbackfn, T, <kValue, k, O>).
      const result: JSAny = Call(context, callbackfn, thisArg, kValue, k, o);

      // iii. If selected is true, then...
      if (ToBoolean(result)) {
        // 1. Perform ? CreateDataPropertyOrThrow(A, ToString(to), kValue).
        FastCreateDataProperty(array, to, kValue);
        // 2. Increase to by 1.
        to = to + 1;
90 91
      }
    }
92 93

    // 6d. Increase k by 1. (done by the loop).
94
  }
95 96
  return array;
}
97

98 99
transitioning macro FastArrayFilter(implicit context: Context)(
    fastO: FastJSArray, len: Smi, callbackfn: Callable, thisArg: JSAny,
100
    output: FastJSArray): void labels
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
Bailout(Number, Number) {
  let k: Smi = 0;
  let to: Smi = 0;
  let fastOW = NewFastJSArrayWitness(fastO);
  let fastOutputW = NewFastJSArrayWitness(output);

  fastOutputW.EnsureArrayPushable() otherwise goto Bailout(k, to);

  // Build a fast loop over the array.
  for (; k < len; k++) {
    fastOW.Recheck() otherwise goto Bailout(k, to);

    // Ensure that we haven't walked beyond a possibly updated length.
    if (k >= fastOW.Get().length) goto Bailout(k, to);
    const value: JSAny = fastOW.LoadElementNoHole(k) otherwise continue;
    const result: JSAny =
        Call(context, callbackfn, thisArg, value, k, fastOW.Get());
    if (ToBoolean(result)) {
      try {
        // Since the call to {callbackfn} is observable, we can't
        // use the Bailout label until we've successfully stored.
        // Hence the {SlowStore} label.
        fastOutputW.Recheck() otherwise SlowStore;
        if (fastOutputW.Get().length != to) goto SlowStore;
        fastOutputW.Push(value) otherwise SlowStore;
      } label SlowStore {
        FastCreateDataProperty(fastOutputW.stable, to, value);
128
      }
129
      to = to + 1;
130 131
    }
  }
132
}
133

134 135 136 137 138 139 140 141 142 143 144 145
// This method creates a 0-length array with the ElementsKind of the
// receiver if possible, otherwise, bails out. It makes sense for the
// caller to know that the slow case needs to be invoked.
macro FastFilterSpeciesCreate(implicit context: Context)(receiver: JSReceiver):
    JSReceiver labels Slow {
  const len: Smi = 0;
  if (IsArraySpeciesProtectorCellInvalid()) goto Slow;
  const o = Cast<FastJSArray>(receiver) otherwise Slow;
  const newMap: Map =
      LoadJSArrayElementsMap(o.map.elements_kind, LoadNativeContext(context));
  return AllocateJSArray(ElementsKind::PACKED_SMI_ELEMENTS, newMap, len, len);
}
146

147 148 149 150 151 152
// https://tc39.github.io/ecma262/#sec-array.prototype.filter
transitioning javascript builtin
ArrayFilter(
    js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
  try {
    RequireObjectCoercible(receiver, 'Array.prototype.filter');
153

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

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

160 161 162 163 164 165 166 167 168
    // 3. If IsCallable(callbackfn) is false, throw a TypeError exception.
    if (arguments.length == 0) {
      goto TypeError;
    }
    const callbackfn = Cast<Callable>(arguments[0]) otherwise TypeError;

    // 4. If thisArg is present, let T be thisArg; else let T be undefined.
    const thisArg: JSAny = arguments[1];
    let output: JSReceiver;
169

170 171 172 173 174
    // Special cases.
    let k: Number = 0;
    let to: Number = 0;
    try {
      output = FastFilterSpeciesCreate(o) otherwise SlowSpeciesCreate;
175 176

      try {
177 178 179 180 181 182 183 184 185 186 187
        const smiLen: Smi = Cast<Smi>(len) otherwise goto Bailout(k, to);
        const fastOutput =
            Cast<FastJSArray>(output) otherwise goto Bailout(k, to);
        const fastO = Cast<FastJSArray>(o) otherwise goto Bailout(k, to);

        FastArrayFilter(fastO, smiLen, callbackfn, thisArg, fastOutput)
            otherwise Bailout;
        return output;
      } label Bailout(kValue: Number, toValue: Number) deferred {
        k = kValue;
        to = toValue;
188
      }
189 190
    } label SlowSpeciesCreate {
      output = ArraySpeciesCreate(context, receiver, 0);
191
    }
192 193 194 195 196

    return ArrayFilterLoopContinuation(
        o, callbackfn, thisArg, output, o, k, len, to);
  } label TypeError deferred {
    ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]);
197 198
  }
}
199
}