// 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. module array { macro LoadElement<ElementsAccessor : type, T : type>( elements: FixedArrayBase, index: Smi): T; LoadElement<FastPackedSmiElements, Smi>( elements: FixedArrayBase, index: Smi): Smi { const elems: FixedArray = unsafe_cast<FixedArray>(elements); return unsafe_cast<Smi>(elems[index]); } LoadElement<FastPackedObjectElements, Object>( elements: FixedArrayBase, index: Smi): Object { const elems: FixedArray = unsafe_cast<FixedArray>(elements); return elems[index]; } LoadElement<FastPackedDoubleElements, float64>( elements: FixedArrayBase, index: Smi): float64 { try { const elems: FixedDoubleArray = unsafe_cast<FixedDoubleArray>(elements); return LoadDoubleWithHoleCheck(elems, index) otherwise Hole; } label Hole { // This macro is only used for PACKED_DOUBLE, loading the hole should // be impossible. unreachable; } } macro StoreElement<ElementsAccessor : type, T : type>( elements: FixedArrayBase, index: Smi, value: T); StoreElement<FastPackedSmiElements, Smi>( elements: FixedArrayBase, index: Smi, value: Smi) { const elems: FixedArray = unsafe_cast<FixedArray>(elements); StoreFixedArrayElementSmi(elems, index, value, SKIP_WRITE_BARRIER); } StoreElement<FastPackedObjectElements, Object>( elements: FixedArrayBase, index: Smi, value: Object) { const elems: FixedArray = unsafe_cast<FixedArray>(elements); elems[index] = value; } StoreElement<FastPackedDoubleElements, float64>( elements: FixedArrayBase, index: Smi, value: float64) { const elems: FixedDoubleArray = unsafe_cast<FixedDoubleArray>(elements); assert(value == Float64SilenceNaN(value)); StoreFixedDoubleArrayElementWithSmiIndex(elems, index, value); } // Fast-path for all PACKED_* elements kinds. These do not need to check // whether a property is present, so we can simply swap them using fast // FixedArray loads/stores. macro FastPackedArrayReverse<Accessor : type, T : type>( elements: FixedArrayBase, length: Smi) { let lower: Smi = 0; let upper: Smi = length - 1; while (lower < upper) { const lower_value: T = LoadElement<Accessor, T>(elements, lower); const upper_value: T = LoadElement<Accessor, T>(elements, upper); StoreElement<Accessor, T>(elements, lower, upper_value); StoreElement<Accessor, T>(elements, upper, lower_value); ++lower; --upper; } } macro GenericArrayReverse(context: Context, receiver: Object): Object { // 1. Let O be ? ToObject(this value). const object: JSReceiver = ToObject_Inline(context, receiver); // 2. Let len be ? ToLength(? Get(O, "length")). const length: Number = GetLengthProperty(context, object); // 3. Let middle be floor(len / 2). // 4. Let lower be 0. // 5. Repeat, while lower != middle. // a. Let upper be len - lower - 1. // Instead of calculating the middle value, we simply initialize upper // with len - 1 and decrement it after each iteration. let lower: Number = 0; let upper: Number = length - 1; while (lower < upper) { let lower_value: Object = Undefined; let upper_value: Object = Undefined; // b. Let upperP be ! ToString(upper). // c. Let lowerP be ! ToString(lower). // d. Let lowerExists be ? HasProperty(O, lowerP). const lower_exists: Boolean = HasProperty(context, object, lower); // e. If lowerExists is true, then. if (lower_exists == True) { // i. Let lowerValue be ? Get(O, lowerP). lower_value = GetProperty(context, object, lower); } // f. Let upperExists be ? HasProperty(O, upperP). const upper_exists: Boolean = HasProperty(context, object, upper); // g. If upperExists is true, then. if (upper_exists == True) { // i. Let upperValue be ? Get(O, upperP). upper_value = GetProperty(context, object, upper); } // h. If lowerExists is true and upperExists is true, then if (lower_exists == True && upper_exists == True) { // i. Perform ? Set(O, lowerP, upperValue, true). SetProperty(context, object, lower, upper_value); // ii. Perform ? Set(O, upperP, lowerValue, true). SetProperty(context, object, upper, lower_value); } else if (lower_exists == False && upper_exists == True) { // i. Perform ? Set(O, lowerP, upperValue, true). SetProperty(context, object, lower, upper_value); // ii. Perform ? DeletePropertyOrThrow(O, upperP). DeleteProperty(context, object, upper, kStrict); } else if (lower_exists == True && upper_exists == False) { // i. Perform ? DeletePropertyOrThrow(O, lowerP). DeleteProperty(context, object, lower, kStrict); // ii. Perform ? Set(O, upperP, lowerValue, true). SetProperty(context, object, upper, lower_value); } // l. Increase lower by 1. ++lower; --upper; } // 6. Return O. return object; } macro EnsureWriteableFastElements(array: JSArray) { const elements: FixedArrayBase = array.elements; if (elements.map != kCOWMap) return; // There are no COW *_DOUBLE_ELEMENTS arrays, so we are allowed to always // extract FixedArrays and don't have to worry about FixedDoubleArrays. assert(IsFastSmiOrTaggedElementsKind(array.map.elements_kind)); const length: Smi = array.length_fast; array.elements = ExtractFixedArray( unsafe_cast<FixedArray>(elements), 0, length, length, kFixedArrays); } macro TryFastPackedArrayReverse(receiver: Object) labels Slow { const array: JSArray = cast<JSArray>(receiver) otherwise Slow; EnsureWriteableFastElements(array); assert(array.elements.map != kCOWMap); const kind: ElementsKind = array.map.elements_kind; if (kind == PACKED_SMI_ELEMENTS) { FastPackedArrayReverse<FastPackedSmiElements, Smi>( array.elements, array.length_fast); } else if (kind == PACKED_ELEMENTS) { FastPackedArrayReverse<FastPackedObjectElements, Object>( array.elements, array.length_fast); } else if (kind == PACKED_DOUBLE_ELEMENTS) { FastPackedArrayReverse<FastPackedDoubleElements, float64>( array.elements, array.length_fast); } else { goto Slow; } } // https://tc39.github.io/ecma262/#sec-array.prototype.reverse javascript builtin ArrayPrototypeReverse( context: Context, receiver: Object, ...arguments): Object { try { TryFastPackedArrayReverse(receiver) otherwise Baseline; return receiver; } label Baseline { return GenericArrayReverse(context, receiver); } } }