// 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 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 array_buffer = Handle::cast(argument); if (!array_buffer->is_neuterable()) { 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 neutered. 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->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 bool CompareNum(T x, T y) { if (x < y) { return true; } else if (x > y) { return false; } else if (!std::is_integral::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 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 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(elements->DataPtr()); \ if (kExternal##Type##Array == kExternalFloat64Array || \ kExternal##Type##Array == kExternalFloat32Array) \ std::sort(data, data + length, CompareNum); \ 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 target = args.at(0); Handle obj = args.at(1); Handle offset = args.at(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(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 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 source = Handle::cast(obj); ElementsAccessor* accessor = target->GetElementsAccessor(); return accessor->CopyElements(source, target, int_l, uint_offset); } } // namespace internal } // namespace v8