array-lastindexof.tq 5.03 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
  macro LoadWithHoleCheck<Elements: type>(
7
      elements: FixedArrayBase, index: Smi): Object
8
      labels IfHole;
9

10 11
  LoadWithHoleCheck<FixedArray>(implicit context: Context)(
      elements: FixedArrayBase, index: Smi): Object
12
      labels IfHole {
13
    const elements: FixedArray = UnsafeCast<FixedArray>(elements);
14 15 16 17 18
    const element: Object = elements[index];
    if (element == Hole) goto IfHole;
    return element;
  }

19 20
  LoadWithHoleCheck<FixedDoubleArray>(implicit context: Context)(
      elements: FixedArrayBase, index: Smi): Object
21
      labels IfHole {
22
    const elements: FixedDoubleArray = UnsafeCast<FixedDoubleArray>(elements);
23
    const element: float64 = LoadDoubleWithHoleCheck(elements, index)
24
        otherwise IfHole;
25 26 27
    return AllocateHeapNumberWithValue(element);
  }

28
  macro FastArrayLastIndexOf<Elements: type>(
29
      context: Context, array: JSArray, from: Smi, searchElement: Object): Smi {
30 31
    const elements: FixedArrayBase = array.elements;
    let k: Smi = from;
32 33 34 35 36 37 38 39 40

    // Bug(898785): Due to side-effects in the evaluation of `fromIndex`
    // the {from} can be out-of-bounds here, so we need to clamp {k} to
    // the {elements} length. We might be reading holes / hole NaNs still
    // due to that, but those will be ignored below.
    if (k >= elements.length) {
      k = elements.length - 1;
    }

41 42 43
    while (k >= 0) {
      try {
        const element: Object = LoadWithHoleCheck<Elements>(elements, k)
44
            otherwise Hole;
45 46 47

        const same: Boolean = StrictEqual(searchElement, element);
        if (same == True) {
48
          assert(Is<FastJSArray>(array));
49 50 51 52 53 54 55 56
          return k;
        }
      }
      label Hole {}  // Do nothing for holes.

      --k;
    }

57
    assert(Is<FastJSArray>(array));
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
    return -1;
  }

  macro GetFromIndex(
      context: Context, length: Number,
      arguments: constexpr Arguments): Number {
    // 4. If fromIndex is present, let n be ? ToInteger(fromIndex);
    //    else let n be len - 1.
    const n: Number = arguments.length < 2 ?
        length - 1 :
        ToInteger_Inline(context, arguments[1], kTruncateMinusZero);

    // 5. If n >= 0, then.
    let k: Number = SmiConstant(0);
    if (n >= 0) {
      // a. If n is -0, let k be +0; else let k be min(n, len - 1).
      // If n was -0 it got truncated to 0.0, so taking the minimum is fine.
75
      k = Min(n, length - 1);
76 77 78 79 80 81 82 83
    } else {
      // a. Let k be len + n.
      k = length + n;
    }
    return k;
  }

  macro TryFastArrayLastIndexOf(
84 85
      context: Context, receiver: JSReceiver, searchElement: Object,
      from: Number): Object
86
      labels Slow {
87 88
    const array: FastJSArray = Cast<FastJSArray>(receiver) otherwise Slow;
    const length: Smi = array.length;
89 90
    if (length == 0) return SmiConstant(-1);

91
    const fromSmi: Smi = Cast<Smi>(from) otherwise Slow;
92 93 94
    const kind: ElementsKind = array.map.elements_kind;
    if (IsFastSmiOrTaggedElementsKind(kind)) {
      return FastArrayLastIndexOf<FixedArray>(
95
          context, array, fromSmi, searchElement);
96 97 98
    }
    assert(IsDoubleElementsKind(kind));
    return FastArrayLastIndexOf<FixedDoubleArray>(
99
        context, array, fromSmi, searchElement);
100 101
  }

102
  transitioning macro GenericArrayLastIndexOf(
103 104 105
      context: Context, object: JSReceiver, searchElement: Object,
      from: Number): Object {
    let k: Number = from;
106 107 108 109

    // 7. Repeat, while k >= 0.
    while (k >= 0) {
      // a. Let kPresent be ? HasProperty(O, ! ToString(k)).
110
      const kPresent: Boolean = HasProperty(object, k);
111 112

      // b. If kPresent is true, then.
113
      if (kPresent == True) {
114
        // i. Let elementK be ? Get(O, ! ToString(k)).
115
        const element: Object = GetProperty(object, k);
116 117 118

        // ii. Let same be the result of performing Strict Equality Comparison
        //     searchElement === elementK.
119
        const same: Boolean = StrictEqual(searchElement, element);
120 121 122 123 124 125 126 127 128 129 130 131 132 133

        // iii. If same is true, return k.
        if (same == True) return k;
      }

      // c. Decrease k by 1.
      --k;
    }

    // 8. Return -1.
    return SmiConstant(-1);
  }

  // https://tc39.github.io/ecma262/#sec-array.prototype.lastIndexOf
134
  transitioning javascript builtin ArrayPrototypeLastIndexOf(
135
      context: Context, receiver: Object, ...arguments): Object {
136 137 138 139
    // 1. Let O be ? ToObject(this value).
    const object: JSReceiver = ToObject_Inline(context, receiver);

    // 2. Let len be ? ToLength(? Get(O, "length")).
140
    const length: Number = GetLengthProperty(object);
141 142 143 144 145 146 147 148 149

    // 3. If len is 0, return -1.
    if (length == SmiConstant(0)) return SmiConstant(-1);

    // Step 4 - 6.
    const from: Number = GetFromIndex(context, length, arguments);

    const searchElement: Object = arguments[0];

150
    try {
151
      return TryFastArrayLastIndexOf(context, object, searchElement, from)
152
          otherwise Baseline;
153 154
    }
    label Baseline {
155
      return GenericArrayLastIndexOf(context, object, searchElement, from);
156 157 158
    }
  }
}