// 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/arguments-inl.h"
|
#include "src/elements.h"
|
#include "src/heap/factory.h"
|
#include "src/heap/heap-inl.h"
|
#include "src/messages.h"
|
#include "src/objects-inl.h"
|
#include "src/objects/js-array-buffer-inl.h"
|
#include "src/runtime/runtime-utils.h"
|
#include "src/runtime/runtime.h"
|
|
namespace v8 {
|
namespace internal {
|
|
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 ReadOnlyRoots(isolate).undefined_value();
|
}
|
if (array_buffer->backing_store() == nullptr) {
|
CHECK_EQ(Smi::kZero, array_buffer->byte_length());
|
return ReadOnlyRoots(isolate).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 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_TypedArrayGetLength) {
|
HandleScope scope(isolate);
|
DCHECK_EQ(1, args.length());
|
CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, holder, 0);
|
return holder->length();
|
}
|
|
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()), isolate);
|
switch (array->type()) {
|
#define TYPED_ARRAY_SORT(Type, type, TYPE, ctype) \
|
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());
|
}
|
|
// 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(isolate, 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
|