blob: 614852f444aa125c89071df3857de52b8f6c9bc1 [file] [log] [blame]
// Copyright 2019 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.
#include 'src/builtins/builtins-typed-array-gen.h'
namespace typed_array {
const kBuiltinNameSort: constexpr string = '%TypedArray%.prototype.sort';
extern runtime TypedArraySortFast(Context, JSAny): JSTypedArray;
transitioning macro CallCompare(
implicit context: Context, array: JSTypedArray, comparefn: Callable)(
a: JSAny, b: JSAny): Number {
// a. Let v be ? ToNumber(? Call(comparefn, undefined, x, y)).
const v: Number = ToNumber_Inline(Call(context, comparefn, Undefined, a, b));
// b. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
if (IsDetachedBuffer(array.buffer)) {
ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameSort);
}
// c. If v is NaN, return +0.
if (NumberIsNaN(v)) return 0;
// d. return v.
return v;
}
// Merges two sorted runs [from, middle) and [middle, to)
// from "source" into "target".
transitioning macro
TypedArrayMerge(
implicit context: Context, array: JSTypedArray, comparefn: Callable)(
source: FixedArray, from: uintptr, middle: uintptr, to: uintptr,
target: FixedArray) {
let left: uintptr = from;
let right: uintptr = middle;
for (let targetIndex: uintptr = from; targetIndex < to; ++targetIndex) {
if (left < middle && right >= to) {
// If the left run has elements, but the right does not, we take
// from the left.
target.objects[targetIndex] = source.objects[left++];
} else if (left < middle) {
// If both have elements, we need to compare.
const leftElement = UnsafeCast<JSAny>(source.objects[left]);
const rightElement = UnsafeCast<JSAny>(source.objects[right]);
if (CallCompare(leftElement, rightElement) <= 0) {
target.objects[targetIndex] = leftElement;
left++;
} else {
target.objects[targetIndex] = rightElement;
right++;
}
} else {
// No elements on the left, but the right does, so we take
// from the right.
assert(left == middle);
target.objects[targetIndex] = source.objects[right++];
}
}
}
transitioning builtin
TypedArrayMergeSort(implicit context: Context)(
source: FixedArray, from: uintptr, to: uintptr, target: FixedArray,
array: JSTypedArray, comparefn: Callable): JSAny {
assert(to - from > 1);
const middle: uintptr = from + ((to - from) >>> 1);
// On the next recursion step source becomes target and vice versa.
// This saves the copy of the relevant range from the original
// array into a work array on each recursion step.
if (middle - from > 1) {
TypedArrayMergeSort(target, from, middle, source, array, comparefn);
}
if (to - middle > 1) {
TypedArrayMergeSort(target, middle, to, source, array, comparefn);
}
TypedArrayMerge(source, from, middle, to, target);
return Undefined;
}
// https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.sort
transitioning javascript builtin TypedArrayPrototypeSort(
js-implicit context: NativeContext,
receiver: JSAny)(...arguments): JSTypedArray {
// 1. If comparefn is not undefined and IsCallable(comparefn) is false,
// throw a TypeError exception.
const comparefnObj: JSAny = arguments[0];
if (comparefnObj != Undefined && !Is<Callable>(comparefnObj)) {
ThrowTypeError(MessageTemplate::kBadSortComparisonFunction, comparefnObj);
}
// 2. Let obj be the this value.
const obj: JSAny = receiver;
// 3. Let buffer be ? ValidateTypedArray(obj).
// ValidateTypedArray currently returns the array, not the ViewBuffer.
const array: JSTypedArray =
ValidateTypedArray(context, obj, kBuiltinNameSort);
// 4. Let len be obj.[[ArrayLength]].
const len: uintptr = array.length;
// Arrays of length 1 or less are considered sorted.
if (len < 2) return array;
// Default sorting is done in C++ using std::sort
if (comparefnObj == Undefined) {
return TypedArraySortFast(context, obj);
}
// Throw rather than crash if the TypedArray's size exceeds max FixedArray
// size (which we need below).
// TODO(4153): Consider redesigning the sort implementation such that we
// don't have such a limit.
if (len > kFixedArrayMaxLength) {
ThrowTypeError(MessageTemplate::kTypedArrayTooLargeToSort);
}
const comparefn: Callable =
Cast<Callable>(comparefnObj) otherwise unreachable;
const accessor: TypedArrayAccessor =
GetTypedArrayAccessor(array.elements_kind);
// Prepare the two work arrays. All numbers are converted to tagged
// objects first, and merge sorted between the two FixedArrays.
// The result is then written back into the JSTypedArray.
const work1: FixedArray = AllocateZeroedFixedArray(Convert<intptr>(len));
const work2: FixedArray = AllocateZeroedFixedArray(Convert<intptr>(len));
for (let i: uintptr = 0; i < len; ++i) {
const element: Numeric = accessor.LoadNumeric(array, i);
work1.objects[i] = element;
work2.objects[i] = element;
}
TypedArrayMergeSort(work2, 0, len, work1, array, comparefn);
// work1 contains the sorted numbers. Write them back.
for (let i: uintptr = 0; i < len; ++i) {
accessor.StoreNumeric(
context, array, i, UnsafeCast<Numeric>(work1.objects[i]));
}
return array;
}
}