From 8527f00065eac52673048d4958c4463d0c3a6e97 Mon Sep 17 00:00:00 2001 From: Idan Horowitz Date: Thu, 10 Jun 2021 22:44:17 +0300 Subject: LibJS: Allow and check for detached ArrayBuffers This is required by the specification and will be used for the $262.detachArrayBuffer method in test262. --- Userland/Libraries/LibJS/Runtime/ArrayBuffer.cpp | 2 ++ Userland/Libraries/LibJS/Runtime/ArrayBuffer.h | 11 +++++++++-- .../Libraries/LibJS/Runtime/ArrayBufferPrototype.cpp | 18 ++++++++++++++---- Userland/Libraries/LibJS/Runtime/ErrorTypes.h | 1 + Userland/Libraries/LibJS/Runtime/TypedArray.cpp | 15 ++++++++++++--- .../Libraries/LibJS/Runtime/TypedArrayPrototype.cpp | 10 ++++++++-- 6 files changed, 46 insertions(+), 11 deletions(-) (limited to 'Userland') diff --git a/Userland/Libraries/LibJS/Runtime/ArrayBuffer.cpp b/Userland/Libraries/LibJS/Runtime/ArrayBuffer.cpp index a34955dd9c..52356cc8c5 100644 --- a/Userland/Libraries/LibJS/Runtime/ArrayBuffer.cpp +++ b/Userland/Libraries/LibJS/Runtime/ArrayBuffer.cpp @@ -22,12 +22,14 @@ ArrayBuffer* ArrayBuffer::create(GlobalObject& global_object, ByteBuffer* buffer ArrayBuffer::ArrayBuffer(size_t byte_size, Object& prototype) : Object(prototype) , m_buffer(ByteBuffer::create_zeroed(byte_size)) + , m_detach_key(js_undefined()) { } ArrayBuffer::ArrayBuffer(ByteBuffer* buffer, Object& prototype) : Object(prototype) , m_buffer(buffer) + , m_detach_key(js_undefined()) { } diff --git a/Userland/Libraries/LibJS/Runtime/ArrayBuffer.h b/Userland/Libraries/LibJS/Runtime/ArrayBuffer.h index db2e13b605..1fd2e6798f 100644 --- a/Userland/Libraries/LibJS/Runtime/ArrayBuffer.h +++ b/Userland/Libraries/LibJS/Runtime/ArrayBuffer.h @@ -27,17 +27,24 @@ public: ByteBuffer& buffer() { return buffer_impl(); } const ByteBuffer& buffer() const { return buffer_impl(); } + Value detach_key() const { return m_detach_key; } + void detach_buffer() { m_buffer = Empty {}; } + bool is_detached() const { return m_buffer.has(); } + private: ByteBuffer& buffer_impl() { ByteBuffer* ptr { nullptr }; - m_buffer.visit([&](auto* pointer) { ptr = pointer; }, [&](auto& value) { ptr = &value; }); + m_buffer.visit([&](Empty) { VERIFY_NOT_REACHED(); }, [&](auto* pointer) { ptr = pointer; }, [&](auto& value) { ptr = &value; }); return *ptr; } const ByteBuffer& buffer_impl() const { return const_cast(this)->buffer_impl(); } - Variant m_buffer; + Variant m_buffer; + // The various detach related members of ArrayBuffer are not used by any ECMA262 functionality, + // but are required to be available for the use of various harnesses like the Test262 test runner. + Value m_detach_key; }; } diff --git a/Userland/Libraries/LibJS/Runtime/ArrayBufferPrototype.cpp b/Userland/Libraries/LibJS/Runtime/ArrayBufferPrototype.cpp index 6a9dfb80b2..5a039b65d4 100644 --- a/Userland/Libraries/LibJS/Runtime/ArrayBufferPrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/ArrayBufferPrototype.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2020, Linus Groh + * Copyright (c) 2021, Idan Horowitz * * SPDX-License-Identifier: BSD-2-Clause */ @@ -52,7 +53,10 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayBufferPrototype::slice) return {}; // FIXME: Check for shared buffer - // FIXME: Check for detached buffer + if (array_buffer_object->is_detached()) { + vm.throw_exception(global_object, ErrorType::DetachedArrayBuffer); + return {}; + } auto length = array_buffer_object->byte_length(); @@ -95,7 +99,10 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayBufferPrototype::slice) auto* new_array_buffer_object = static_cast(&new_array_buffer.as_object()); // FIXME: Check for shared buffer - // FIXME: Check for detached buffer + if (new_array_buffer_object->is_detached()) { + vm.throw_exception(global_object, ErrorType::SpeciesConstructorReturned, "a detached ArrayBuffer"); + return {}; + } if (same_value(new_array_buffer_object, array_buffer_object)) { vm.throw_exception(global_object, ErrorType::SpeciesConstructorReturned, "same ArrayBuffer instance"); return {}; @@ -120,9 +127,12 @@ JS_DEFINE_NATIVE_GETTER(ArrayBufferPrototype::byte_length_getter) auto array_buffer_object = array_buffer_object_from(vm, global_object); if (!array_buffer_object) return {}; + // FIXME: Check for shared buffer - // FIXME: Check for detached buffer - return Value((double)array_buffer_object->byte_length()); + if (array_buffer_object->is_detached()) + return Value(0); + + return Value(array_buffer_object->byte_length()); } } diff --git a/Userland/Libraries/LibJS/Runtime/ErrorTypes.h b/Userland/Libraries/LibJS/Runtime/ErrorTypes.h index 014436c121..e73b7d5b3b 100644 --- a/Userland/Libraries/LibJS/Runtime/ErrorTypes.h +++ b/Userland/Libraries/LibJS/Runtime/ErrorTypes.h @@ -24,6 +24,7 @@ M(ConvertUndefinedToObject, "Cannot convert undefined to object") \ M(DescChangeNonConfigurable, "Cannot change attributes of non-configurable property '{}'") \ M(DescWriteNonWritable, "Cannot write to non-writable property '{}'") \ + M(DetachedArrayBuffer, "ArrayBuffer is detached") \ M(DivisionByZero, "Division by zero") \ M(GetCapabilitiesExecutorCalledMultipleTimes, "GetCapabilitiesExecutor was called multiple times") \ M(InOperatorWithObject, "'in' operator must be used on an object") \ diff --git a/Userland/Libraries/LibJS/Runtime/TypedArray.cpp b/Userland/Libraries/LibJS/Runtime/TypedArray.cpp index a9bfef012d..462ac7c1f3 100644 --- a/Userland/Libraries/LibJS/Runtime/TypedArray.cpp +++ b/Userland/Libraries/LibJS/Runtime/TypedArray.cpp @@ -33,7 +33,12 @@ static void initialize_typed_array_from_array_buffer(GlobalObject& global_object if (vm.exception()) return; } - // FIXME: 8. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. + + if (array_buffer.is_detached()) { + vm.throw_exception(global_object, ErrorType::DetachedArrayBuffer); + return; + } + auto buffer_byte_length = array_buffer.byte_length(); Checked new_byte_length; if (length.is_undefined()) { @@ -81,8 +86,12 @@ static void initialize_typed_array_from_typed_array(GlobalObject& global_object, if (vm.exception()) return; - // FIXME: 4. If IsDetachedBuffer(src_data) is true, throw a TypeError exception. - VERIFY(src_array.viewed_array_buffer()); + auto* source_array_buffer = src_array.viewed_array_buffer(); + VERIFY(source_array_buffer); + if (source_array_buffer->is_detached()) { + vm.throw_exception(global_object, ErrorType::DetachedArrayBuffer); + return; + } auto src_array_length = src_array.array_length(); auto dest_element_size = dest_array.element_size(); diff --git a/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.cpp b/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.cpp index 03f2dfa5b8..63c4da2158 100644 --- a/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.cpp @@ -50,6 +50,10 @@ JS_DEFINE_NATIVE_GETTER(TypedArrayPrototype::length_getter) auto typed_array = typed_array_from(vm, global_object); if (!typed_array) return {}; + auto* array_buffer = typed_array->viewed_array_buffer(); + VERIFY(array_buffer); + if (array_buffer->is_detached()) + return Value(0); return Value(typed_array->array_length()); } @@ -95,7 +99,8 @@ JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::byte_length_getter) return {}; auto* array_buffer = typed_array->viewed_array_buffer(); VERIFY(array_buffer); - // FIXME: If array_buffer is detached, return 0. + if (array_buffer->is_detached()) + return Value(0); return Value(typed_array->byte_length()); } @@ -107,7 +112,8 @@ JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::byte_offset_getter) return {}; auto* array_buffer = typed_array->viewed_array_buffer(); VERIFY(array_buffer); - // FIXME: If array_buffer is detached, return 0. + if (array_buffer->is_detached()) + return Value(0); return Value(typed_array->byte_offset()); } -- cgit v1.2.3