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