LibJS: Allow and check for detached ArrayBuffers

This is required by the specification and will be used for the
$262.detachArrayBuffer method in test262.
This commit is contained in:
Idan Horowitz 2021-06-10 22:44:17 +03:00 committed by Linus Groh
parent 7d6db3f09b
commit 8527f00065
Notes: sideshowbarker 2024-07-18 12:28:14 +09:00
6 changed files with 46 additions and 11 deletions

View file

@ -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())
{
}

View file

@ -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;
};
}

View file

@ -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());
}
}

View file

@ -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") \

View file

@ -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();

View file

@ -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());
}