blob: 7fab051cbf6d1c26f238e53f06f0b9e9e3cd618a [file] [log] [blame]
// Copyright 2014 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/common/message-template.h"
#include "src/execution/arguments-inl.h"
#include "src/heap/factory.h"
#include "src/heap/heap-inl.h"
#include "src/logging/counters.h"
#include "src/objects/elements.h"
#include "src/objects/js-array-buffer-inl.h"
#include "src/objects/objects-inl.h"
#include "src/runtime/runtime-utils.h"
#include "src/runtime/runtime.h"
namespace v8 {
namespace internal {
RUNTIME_FUNCTION(Runtime_ArrayBufferDetach) {
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
Handle<Object> argument = args.at(0);
// This runtime function is exposed in ClusterFuzz and as such has to
// support arbitrary arguments.
if (!argument->IsJSArrayBuffer()) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kNotTypedArray));
}
Handle<JSArrayBuffer> array_buffer = Handle<JSArrayBuffer>::cast(argument);
if (!array_buffer->is_detachable()) {
return ReadOnlyRoots(isolate).undefined_value();
}
if (array_buffer->backing_store() == nullptr) {
CHECK_EQ(0, array_buffer->byte_length());
return ReadOnlyRoots(isolate).undefined_value();
}
// Shared array buffers should never be detached.
CHECK(!array_buffer->is_shared());
DCHECK(!array_buffer->is_external());
void* backing_store = array_buffer->backing_store();
size_t byte_length = array_buffer->byte_length();
array_buffer->set_is_external(true);
isolate->heap()->UnregisterArrayBuffer(*array_buffer);
array_buffer->Detach();
isolate->array_buffer_allocator()->Free(backing_store, byte_length);
return ReadOnlyRoots(isolate).undefined_value();
}
RUNTIME_FUNCTION(Runtime_TypedArrayCopyElements) {
HandleScope scope(isolate);
DCHECK_EQ(3, args.length());
CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, target, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, source, 1);
CONVERT_NUMBER_ARG_HANDLE_CHECKED(length_obj, 2);
size_t length;
CHECK(TryNumberToSize(*length_obj, &length));
ElementsAccessor* accessor = target->GetElementsAccessor();
return accessor->CopyElements(source, target, length);
}
RUNTIME_FUNCTION(Runtime_TypedArrayGetBuffer) {
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, holder, 0);
return *holder->GetBuffer();
}
namespace {
template <typename T>
bool CompareNum(T x, T y) {
if (x < y) {
return true;
} else if (x > y) {
return false;
} else if (!std::is_integral<T>::value) {
double _x = x, _y = y;
if (x == 0 && x == y) {
/* -0.0 is less than +0.0 */
return std::signbit(_x) && !std::signbit(_y);
} else if (!std::isnan(_x) && std::isnan(_y)) {
/* number is less than NaN */
return true;
}
}
return false;
}
} // namespace
RUNTIME_FUNCTION(Runtime_TypedArraySortFast) {
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
// Validation is handled in the Torque builtin.
CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, array, 0);
DCHECK(!array->WasDetached());
size_t length = array->length();
if (length <= 1) return *array;
// In case of a SAB, the data is copied into temporary memory, as
// std::sort might crash in case the underlying data is concurrently
// modified while sorting.
CHECK(array->buffer().IsJSArrayBuffer());
Handle<JSArrayBuffer> buffer(JSArrayBuffer::cast(array->buffer()), isolate);
const bool copy_data = buffer->is_shared();
Handle<ByteArray> array_copy;
if (copy_data) {
const size_t bytes = array->byte_length();
// TODO(szuend): Re-check this approach once support for larger typed
// arrays has landed.
CHECK_LE(bytes, INT_MAX);
array_copy = isolate->factory()->NewByteArray(static_cast<int>(bytes));
std::memcpy(static_cast<void*>(array_copy->GetDataStartAddress()),
static_cast<void*>(array->DataPtr()), bytes);
}
DisallowHeapAllocation no_gc;
switch (array->type()) {
#define TYPED_ARRAY_SORT(Type, type, TYPE, ctype) \
case kExternal##Type##Array: { \
ctype* data = \
copy_data \
? reinterpret_cast<ctype*>(array_copy->GetDataStartAddress()) \
: static_cast<ctype*>(array->DataPtr()); \
if (kExternal##Type##Array == kExternalFloat64Array || \
kExternal##Type##Array == kExternalFloat32Array) { \
if (COMPRESS_POINTERS_BOOL && alignof(ctype) > kTaggedSize) { \
/* TODO(ishell, v8:8875): See UnalignedSlot<T> for details. */ \
std::sort(UnalignedSlot<ctype>(data), \
UnalignedSlot<ctype>(data + length), CompareNum<ctype>); \
} else { \
std::sort(data, data + length, CompareNum<ctype>); \
} \
} else { \
if (COMPRESS_POINTERS_BOOL && alignof(ctype) > kTaggedSize) { \
/* TODO(ishell, v8:8875): See UnalignedSlot<T> for details. */ \
std::sort(UnalignedSlot<ctype>(data), \
UnalignedSlot<ctype>(data + length)); \
} else { \
std::sort(data, data + length); \
} \
} \
break; \
}
TYPED_ARRAYS(TYPED_ARRAY_SORT)
#undef TYPED_ARRAY_SORT
}
if (copy_data) {
DCHECK(!array_copy.is_null());
const size_t bytes = array->byte_length();
std::memcpy(static_cast<void*>(array->DataPtr()),
static_cast<void*>(array_copy->GetDataStartAddress()), bytes);
}
return *array;
}
// 22.2.3.23 %TypedArray%.prototype.set ( overloaded [ , offset ] )
RUNTIME_FUNCTION(Runtime_TypedArraySet) {
HandleScope scope(isolate);
Handle<JSTypedArray> target = args.at<JSTypedArray>(0);
Handle<Object> obj = args.at(1);
Handle<Smi> offset = args.at<Smi>(2);
DCHECK(!target->WasDetached()); // Checked in TypedArrayPrototypeSet.
DCHECK(!obj->IsJSTypedArray()); // Should be handled by CSA.
DCHECK_LE(0, offset->value());
const uint32_t uint_offset = static_cast<uint32_t>(offset->value());
if (obj->IsNumber()) {
// For number as a first argument, throw TypeError
// instead of silently ignoring the call, so that
// users know they did something wrong.
// (Consistent with Firefox and Blink/WebKit)
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kInvalidArgument));
}
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, obj,
Object::ToObject(isolate, obj));
Handle<Object> len;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, len,
Object::GetProperty(isolate, obj, isolate->factory()->length_string()));
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, len,
Object::ToLength(isolate, len));
if (uint_offset + len->Number() > target->length()) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewRangeError(MessageTemplate::kTypedArraySetSourceTooLarge));
}
uint32_t int_l;
CHECK(DoubleToUint32IfEqualToSelf(len->Number(), &int_l));
Handle<JSReceiver> source = Handle<JSReceiver>::cast(obj);
ElementsAccessor* accessor = target->GetElementsAccessor();
return accessor->CopyElements(source, target, int_l, uint_offset);
}
} // namespace internal
} // namespace v8