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 LoadElement<ElementsAccessor : type extends ElementsKind, T: type>(
7    elements: FixedArrayBase, index: Smi): T;
8
9LoadElement<array::FastPackedSmiElements, Smi>(implicit context: Context)(
10    elements: FixedArrayBase, index: Smi): Smi {
11  const elements: FixedArray = UnsafeCast<FixedArray>(elements);
12  return UnsafeCast<Smi>(elements.objects[index]);
13}
14
15LoadElement<array::FastPackedObjectElements, JSAny>(implicit context: Context)(
16    elements: FixedArrayBase, index: Smi): JSAny {
17  const elements: FixedArray = UnsafeCast<FixedArray>(elements);
18  return UnsafeCast<JSAny>(elements.objects[index]);
19}
20
21LoadElement<array::FastPackedDoubleElements, float64>(
22    implicit context: Context)(elements: FixedArrayBase, index: Smi): float64 {
23  const elements: FixedDoubleArray = UnsafeCast<FixedDoubleArray>(elements);
24  // This macro is only used for PACKED_DOUBLE, loading the hole should
25  // be impossible.
26  return elements.floats[index].Value() otherwise unreachable;
27}
28
29macro StoreElement<ElementsAccessor : type extends ElementsKind, T: type>(
30    implicit context: Context)(
31    elements: FixedArrayBase, index: Smi, value: T): void;
32
33StoreElement<array::FastPackedSmiElements, Smi>(implicit context: Context)(
34    elements: FixedArrayBase, index: Smi, value: Smi): void {
35  const elems: FixedArray = UnsafeCast<FixedArray>(elements);
36  StoreFixedArrayElement(elems, index, value);
37}
38
39StoreElement<array::FastPackedObjectElements, JSAny>(implicit context: Context)(
40    elements: FixedArrayBase, index: Smi, value: JSAny): void {
41  const elements: FixedArray = UnsafeCast<FixedArray>(elements);
42  elements.objects[index] = value;
43}
44
45StoreElement<array::FastPackedDoubleElements, float64>(
46    implicit context: Context)(
47    elements: FixedArrayBase, index: Smi, value: float64): void {
48  const elems: FixedDoubleArray = UnsafeCast<FixedDoubleArray>(elements);
49  StoreFixedDoubleArrayElement(elems, index, value);
50}
51
52// Fast-path for all PACKED_* elements kinds. These do not need to check
53// whether a property is present, so we can simply swap them using fast
54// FixedArray loads/stores.
55macro FastPackedArrayReverse<Accessor: type, T: type>(
56    implicit context: Context)(elements: FixedArrayBase, length: Smi): void {
57  let lower: Smi = 0;
58  let upper: Smi = length - 1;
59
60  while (lower < upper) {
61    const lowerValue: T = LoadElement<Accessor, T>(elements, lower);
62    const upperValue: T = LoadElement<Accessor, T>(elements, upper);
63    StoreElement<Accessor>(elements, lower, upperValue);
64    StoreElement<Accessor>(elements, upper, lowerValue);
65    ++lower;
66    --upper;
67  }
68}
69
70transitioning macro GenericArrayReverse(
71    context: Context, receiver: JSAny): JSAny {
72  // 1. Let O be ? ToObject(this value).
73  const object: JSReceiver = ToObject_Inline(context, receiver);
74
75  // 2. Let len be ? ToLength(? Get(O, "length")).
76  const length: Number = GetLengthProperty(object);
77
78  // 3. Let middle be floor(len / 2).
79  // 4. Let lower be 0.
80  // 5. Repeat, while lower != middle.
81  //   a. Let upper be len - lower - 1.
82
83  // Instead of calculating the middle value, we simply initialize upper
84  // with len - 1 and decrement it after each iteration.
85  let lower: Number = 0;
86  let upper: Number = length - 1;
87
88  while (lower < upper) {
89    let lowerValue: JSAny = Undefined;
90    let upperValue: JSAny = Undefined;
91
92    // b. Let upperP be ! ToString(upper).
93    // c. Let lowerP be ! ToString(lower).
94    // d. Let lowerExists be ? HasProperty(O, lowerP).
95    const lowerExists: Boolean = HasProperty(object, lower);
96
97    // e. If lowerExists is true, then.
98    if (lowerExists == True) {
99      // i. Let lowerValue be ? Get(O, lowerP).
100      lowerValue = GetProperty(object, lower);
101    }
102
103    // f. Let upperExists be ? HasProperty(O, upperP).
104    const upperExists: Boolean = HasProperty(object, upper);
105
106    // g. If upperExists is true, then.
107    if (upperExists == True) {
108      // i. Let upperValue be ? Get(O, upperP).
109      upperValue = GetProperty(object, upper);
110    }
111
112    // h. If lowerExists is true and upperExists is true, then
113    if (lowerExists == True && upperExists == True) {
114      // i. Perform ? Set(O, lowerP, upperValue, true).
115      SetProperty(object, lower, upperValue);
116
117      // ii. Perform ? Set(O, upperP, lowerValue, true).
118      SetProperty(object, upper, lowerValue);
119    } else if (lowerExists == False && upperExists == True) {
120      // i. Perform ? Set(O, lowerP, upperValue, true).
121      SetProperty(object, lower, upperValue);
122
123      // ii. Perform ? DeletePropertyOrThrow(O, upperP).
124      DeleteProperty(object, upper, LanguageMode::kStrict);
125    } else if (lowerExists == True && upperExists == False) {
126      // i. Perform ? DeletePropertyOrThrow(O, lowerP).
127      DeleteProperty(object, lower, LanguageMode::kStrict);
128
129      // ii. Perform ? Set(O, upperP, lowerValue, true).
130      SetProperty(object, upper, lowerValue);
131    }
132
133    // l. Increase lower by 1.
134    ++lower;
135    --upper;
136  }
137
138  // 6. Return O.
139  return object;
140}
141
142macro TryFastPackedArrayReverse(implicit context: Context)(receiver: JSAny):
143    void labels Slow {
144  const array: FastJSArray = Cast<FastJSArray>(receiver) otherwise Slow;
145
146  const kind: ElementsKind = array.map.elements_kind;
147  if (kind == ElementsKind::PACKED_SMI_ELEMENTS) {
148    array::EnsureWriteableFastElements(array);
149    FastPackedArrayReverse<array::FastPackedSmiElements, Smi>(
150        array.elements, array.length);
151  } else if (kind == ElementsKind::PACKED_ELEMENTS) {
152    array::EnsureWriteableFastElements(array);
153    FastPackedArrayReverse<array::FastPackedObjectElements, JSAny>(
154        array.elements, array.length);
155  } else if (kind == ElementsKind::PACKED_DOUBLE_ELEMENTS) {
156    FastPackedArrayReverse<array::FastPackedDoubleElements, float64>(
157        array.elements, array.length);
158  } else {
159    goto Slow;
160  }
161}
162
163// https://tc39.github.io/ecma262/#sec-array.prototype.reverse
164transitioning javascript builtin ArrayPrototypeReverse(
165    js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
166  try {
167    TryFastPackedArrayReverse(receiver) otherwise Baseline;
168    return receiver;
169  } label Baseline {
170    return GenericArrayReverse(context, receiver);
171  }
172}
173}
174