blob: 85fb2d21739da13a32569b9ae53a9f157689be26 [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/runtime/runtime-utils.h"
#include "src/arguments.h"
#include "src/elements.h"
#include "src/factory.h"
#include "src/messages.h"
#include "src/objects-inl.h"
#include "src/runtime/runtime.h"
namespace v8 {
namespace internal {
RUNTIME_FUNCTION(Runtime_ArrayBufferGetByteLength) {
SealHandleScope shs(isolate);
DCHECK_EQ(1, args.length());
CONVERT_ARG_CHECKED(JSArrayBuffer, holder, 0);
return holder->byte_length();
}
RUNTIME_FUNCTION(Runtime_ArrayBufferNeuter) {
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_neuterable()) {
return isolate->heap()->undefined_value();
}
if (array_buffer->backing_store() == nullptr) {
CHECK_EQ(Smi::kZero, array_buffer->byte_length());
return isolate->heap()->undefined_value();
}
// Shared array buffers should never be neutered.
CHECK(!array_buffer->is_shared());
DCHECK(!array_buffer->is_external());
void* backing_store = array_buffer->backing_store();
size_t byte_length = NumberToSize(array_buffer->byte_length());
array_buffer->set_is_external(true);
isolate->heap()->UnregisterArrayBuffer(*array_buffer);
array_buffer->Neuter();
isolate->array_buffer_allocator()->Free(backing_store, byte_length);
return isolate->heap()->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(JSReceiver, 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);
}
#define BUFFER_VIEW_GETTER(Type, getter, accessor) \
RUNTIME_FUNCTION(Runtime_##Type##Get##getter) { \
HandleScope scope(isolate); \
DCHECK_EQ(1, args.length()); \
CONVERT_ARG_HANDLE_CHECKED(JS##Type, holder, 0); \
return holder->accessor(); \
}
BUFFER_VIEW_GETTER(ArrayBufferView, ByteLength, byte_length)
BUFFER_VIEW_GETTER(ArrayBufferView, ByteOffset, byte_offset)
BUFFER_VIEW_GETTER(TypedArray, Length, length)
#undef BUFFER_VIEW_GETTER
RUNTIME_FUNCTION(Runtime_ArrayBufferViewWasNeutered) {
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
return isolate->heap()->ToBoolean(JSTypedArray::cast(args[0])->WasNeutered());
}
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());
CONVERT_ARG_HANDLE_CHECKED(Object, target_obj, 0);
Handle<JSTypedArray> array;
const char* method = "%TypedArray%.prototype.sort";
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, array, JSTypedArray::Validate(isolate, target_obj, method));
// This line can be removed when JSTypedArray::Validate throws
// if array.[[ViewedArrayBuffer]] is neutered(v8:4648)
if (V8_UNLIKELY(array->WasNeutered())) return *array;
size_t length = array->length_value();
if (length <= 1) return *array;
Handle<FixedTypedArrayBase> elements(
FixedTypedArrayBase::cast(array->elements()));
switch (array->type()) {
#define TYPED_ARRAY_SORT(Type, type, TYPE, ctype, size) \
case kExternal##Type##Array: { \
ctype* data = static_cast<ctype*>(elements->DataPtr()); \
if (kExternal##Type##Array == kExternalFloat64Array || \
kExternal##Type##Array == kExternalFloat32Array) \
std::sort(data, data + length, CompareNum<ctype>); \
else \
std::sort(data, data + length); \
break; \
}
TYPED_ARRAYS(TYPED_ARRAY_SORT)
#undef TYPED_ARRAY_SORT
}
return *array;
}
RUNTIME_FUNCTION(Runtime_IsTypedArray) {
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
return isolate->heap()->ToBoolean(args[0]->IsJSTypedArray());
}
RUNTIME_FUNCTION(Runtime_IsSharedTypedArray) {
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
return isolate->heap()->ToBoolean(
args[0]->IsJSTypedArray() &&
JSTypedArray::cast(args[0])->GetBuffer()->is_shared());
}
RUNTIME_FUNCTION(Runtime_IsSharedIntegerTypedArray) {
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
if (!args[0]->IsJSTypedArray()) {
return isolate->heap()->false_value();
}
Handle<JSTypedArray> obj(JSTypedArray::cast(args[0]));
return isolate->heap()->ToBoolean(obj->GetBuffer()->is_shared() &&
obj->type() != kExternalFloat32Array &&
obj->type() != kExternalFloat64Array &&
obj->type() != kExternalUint8ClampedArray);
}
RUNTIME_FUNCTION(Runtime_IsSharedInteger32TypedArray) {
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
if (!args[0]->IsJSTypedArray()) {
return isolate->heap()->false_value();
}
Handle<JSTypedArray> obj(JSTypedArray::cast(args[0]));
return isolate->heap()->ToBoolean(obj->GetBuffer()->is_shared() &&
obj->type() == kExternalInt32Array);
}
RUNTIME_FUNCTION(Runtime_TypedArraySpeciesCreateByLength) {
HandleScope scope(isolate);
DCHECK_EQ(args.length(), 2);
Handle<JSTypedArray> exemplar = args.at<JSTypedArray>(0);
Handle<Object> length = args.at(1);
int argc = 1;
ScopedVector<Handle<Object>> argv(argc);
argv[0] = length;
Handle<JSTypedArray> result_array;
// TODO(tebbi): Pass correct method name.
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, result_array,
JSTypedArray::SpeciesCreate(isolate, exemplar, argc, argv.start(), ""));
return *result_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->WasNeutered()); // 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(obj, isolate->factory()->length_string()));
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, len,
Object::ToLength(isolate, len));
if (uint_offset + len->Number() > target->length_value()) {
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