1// Copyright 2018 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5namespace array {
6macro LoadWithHoleCheck<Elements : type extends FixedArrayBase>(
7    elements: FixedArrayBase, index: Smi): JSAny
8    labels IfHole;
9
10LoadWithHoleCheck<FixedArray>(implicit context: Context)(
11    elements: FixedArrayBase, index: Smi): JSAny
12    labels IfHole {
13  const elements: FixedArray = UnsafeCast<FixedArray>(elements);
14  const element: Object = elements.objects[index];
15  if (element == TheHole) goto IfHole;
16  return UnsafeCast<JSAny>(element);
17}
18
19LoadWithHoleCheck<FixedDoubleArray>(implicit context: Context)(
20    elements: FixedArrayBase, index: Smi): JSAny
21    labels IfHole {
22  const elements: FixedDoubleArray = UnsafeCast<FixedDoubleArray>(elements);
23  const element: float64 = elements.floats[index].Value() otherwise IfHole;
24  return AllocateHeapNumberWithValue(element);
25}
26
27macro FastArrayLastIndexOf<Elements : type extends FixedArrayBase>(
28    context: Context, array: JSArray, from: Smi, searchElement: JSAny): Smi {
29  const elements: FixedArrayBase = array.elements;
30  let k: Smi = from;
31
32  // Bug(898785): Due to side-effects in the evaluation of `fromIndex`
33  // the {from} can be out-of-bounds here, so we need to clamp {k} to
34  // the {elements} length. We might be reading holes / hole NaNs still
35  // due to that, but those will be ignored below.
36  if (k >= elements.length) {
37    k = elements.length - 1;
38  }
39
40  while (k >= 0) {
41    try {
42      const element: JSAny = LoadWithHoleCheck<Elements>(elements, k)
43          otherwise Hole;
44
45      const same: Boolean = StrictEqual(searchElement, element);
46      if (same == True) {
47        assert(Is<FastJSArray>(array));
48        return k;
49      }
50    } label Hole {}  // Do nothing for holes.
51
52    --k;
53  }
54
55  assert(Is<FastJSArray>(array));
56  return -1;
57}
58
59transitioning macro
60GetFromIndex(context: Context, length: Number, arguments: Arguments): Number {
61  // 4. If fromIndex is present, let n be ? ToInteger(fromIndex);
62  //    else let n be len - 1.
63  const n: Number =
64      arguments.length < 2 ? length - 1 : ToInteger_Inline(arguments[1]);
65
66  // 5. If n >= 0, then.
67  let k: Number = SmiConstant(0);
68  if (n >= 0) {
69    // a. If n is -0, let k be +0; else let k be min(n, len - 1).
70    // If n was -0 it got truncated to 0.0, so taking the minimum is fine.
71    k = Min(n, length - 1);
72  } else {
73    // a. Let k be len + n.
74    k = length + n;
75  }
76  return k;
77}
78
79macro TryFastArrayLastIndexOf(
80    context: Context, receiver: JSReceiver, searchElement: JSAny,
81    from: Number): JSAny
82    labels Slow {
83  const array: FastJSArray = Cast<FastJSArray>(receiver) otherwise Slow;
84  const length: Smi = array.length;
85  if (length == 0) return SmiConstant(-1);
86
87  const fromSmi: Smi = Cast<Smi>(from) otherwise Slow;
88  const kind: ElementsKind = array.map.elements_kind;
89  if (IsFastSmiOrTaggedElementsKind(kind)) {
90    return FastArrayLastIndexOf<FixedArray>(
91        context, array, fromSmi, searchElement);
92  }
93  assert(IsDoubleElementsKind(kind));
94  return FastArrayLastIndexOf<FixedDoubleArray>(
95      context, array, fromSmi, searchElement);
96}
97
98transitioning macro GenericArrayLastIndexOf(
99    context: Context, object: JSReceiver, searchElement: JSAny,
100    from: Number): JSAny {
101  let k: Number = from;
102
103  // 7. Repeat, while k >= 0.
104  while (k >= 0) {
105    // a. Let kPresent be ? HasProperty(O, ! ToString(k)).
106    const kPresent: Boolean = HasProperty(object, k);
107
108    // b. If kPresent is true, then.
109    if (kPresent == True) {
110      // i. Let elementK be ? Get(O, ! ToString(k)).
111      const element: JSAny = GetProperty(object, k);
112
113      // ii. Let same be the result of performing Strict Equality Comparison
114      //     searchElement === elementK.
115      const same: Boolean = StrictEqual(searchElement, element);
116
117      // iii. If same is true, return k.
118      if (same == True) return k;
119    }
120
121    // c. Decrease k by 1.
122    --k;
123  }
124
125  // 8. Return -1.
126  return SmiConstant(-1);
127}
128
129// https://tc39.github.io/ecma262/#sec-array.prototype.lastIndexOf
130transitioning javascript builtin ArrayPrototypeLastIndexOf(
131    js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
132  // 1. Let O be ? ToObject(this value).
133  const object: JSReceiver = ToObject_Inline(context, receiver);
134
135  // 2. Let len be ? ToLength(? Get(O, "length")).
136  const length: Number = GetLengthProperty(object);
137
138  // 3. If len is 0, return -1.
139  if (length == SmiConstant(0)) return SmiConstant(-1);
140
141  // Step 4 - 6.
142  const from: Number = GetFromIndex(context, length, arguments);
143
144  const searchElement: JSAny = arguments[0];
145
146  try {
147    return TryFastArrayLastIndexOf(context, object, searchElement, from)
148        otherwise Baseline;
149  } label Baseline {
150    return GenericArrayLastIndexOf(context, object, searchElement, from);
151  }
152}
153}
154