Forráskód Böngészése

LibJS: Allow and check for detached ArrayBuffers

This is required by the specification and will be used for the
$262.detachArrayBuffer method in test262.
Idan Horowitz 4 éve
szülő
commit
8527f00065

+ 2 - 0
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())
 {
 }
 

+ 9 - 2
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<Empty>(); }
+
 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<ArrayBuffer*>(this)->buffer_impl(); }
 
-    Variant<ByteBuffer, ByteBuffer*> m_buffer;
+    Variant<Empty, ByteBuffer, ByteBuffer*> 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;
 };
 
 }

+ 14 - 4
Userland/Libraries/LibJS/Runtime/ArrayBufferPrototype.cpp

@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2020, Linus Groh <linusg@serenityos.org>
+ * Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
  *
  * 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<TypeError>(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<ArrayBuffer*>(&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<TypeError>(global_object, ErrorType::SpeciesConstructorReturned, "a detached ArrayBuffer");
+        return {};
+    }
     if (same_value(new_array_buffer_object, array_buffer_object)) {
         vm.throw_exception<TypeError>(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());
 }
 
 }

+ 1 - 0
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")                                                                  \

+ 12 - 3
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<TypeError>(global_object, ErrorType::DetachedArrayBuffer);
+        return;
+    }
+
     auto buffer_byte_length = array_buffer.byte_length();
     Checked<size_t> 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<TypeError>(global_object, ErrorType::DetachedArrayBuffer);
+        return;
+    }
 
     auto src_array_length = src_array.array_length();
     auto dest_element_size = dest_array.element_size();

+ 8 - 2
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());
 }