From b204977efb10184aa010949b9e4a817bfcad24f7 Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Thu, 17 Jul 2025 09:49:11 -0400 Subject: [PATCH] LibJS: Allow creating shared ArrayBuffer objects more easily This will allow structured deserialization in LibWeb to create shared buffers without having to go through CreateSharedByteDataBlock. That will let us pass in the underlying ByteBuffer, rather than having to allocate a second buffer via CreateSharedByteDataBlock and memcpy the data into it. --- Libraries/LibJS/Runtime/ArrayBuffer.cpp | 31 +++++++++++++++---------- Libraries/LibJS/Runtime/ArrayBuffer.h | 10 ++++---- 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/Libraries/LibJS/Runtime/ArrayBuffer.cpp b/Libraries/LibJS/Runtime/ArrayBuffer.cpp index bf12087e231..e9d89e590bf 100644 --- a/Libraries/LibJS/Runtime/ArrayBuffer.cpp +++ b/Libraries/LibJS/Runtime/ArrayBuffer.cpp @@ -13,35 +13,42 @@ namespace JS { GC_DEFINE_ALLOCATOR(ArrayBuffer); -ThrowCompletionOr> ArrayBuffer::create(Realm& realm, size_t byte_length) +static GC::Ref prototype_for_shared_state(Realm& realm, DataBlock::Shared is_shared) +{ + return is_shared == DataBlock::Shared::No + ? realm.intrinsics().array_buffer_prototype() + : realm.intrinsics().shared_array_buffer_prototype(); +} + +ThrowCompletionOr> ArrayBuffer::create(Realm& realm, size_t byte_length, DataBlock::Shared is_shared) { auto buffer = ByteBuffer::create_zeroed(byte_length); if (buffer.is_error()) return realm.vm().throw_completion(ErrorType::NotEnoughMemoryToAllocate, byte_length); - return realm.create(buffer.release_value(), realm.intrinsics().array_buffer_prototype()); + return realm.create(buffer.release_value(), is_shared, prototype_for_shared_state(realm, is_shared)); } -GC::Ref ArrayBuffer::create(Realm& realm, ByteBuffer buffer) +GC::Ref ArrayBuffer::create(Realm& realm, ByteBuffer buffer, DataBlock::Shared is_shared) { - return realm.create(move(buffer), realm.intrinsics().array_buffer_prototype()); + return realm.create(move(buffer), is_shared, prototype_for_shared_state(realm, is_shared)); } -GC::Ref ArrayBuffer::create(Realm& realm, ByteBuffer* buffer) +GC::Ref ArrayBuffer::create(Realm& realm, ByteBuffer* buffer, DataBlock::Shared is_shared) { - return realm.create(buffer, realm.intrinsics().array_buffer_prototype()); + return realm.create(buffer, is_shared, prototype_for_shared_state(realm, is_shared)); } -ArrayBuffer::ArrayBuffer(ByteBuffer buffer, Object& prototype) +ArrayBuffer::ArrayBuffer(ByteBuffer buffer, DataBlock::Shared is_shared, Object& prototype) : Object(ConstructWithPrototypeTag::Tag, prototype) - , m_data_block(DataBlock { move(buffer), DataBlock::Shared::No }) + , m_data_block(DataBlock { move(buffer), is_shared }) , m_detach_key(js_undefined()) { } -ArrayBuffer::ArrayBuffer(ByteBuffer* buffer, Object& prototype) +ArrayBuffer::ArrayBuffer(ByteBuffer* buffer, DataBlock::Shared is_shared, Object& prototype) : Object(ConstructWithPrototypeTag::Tag, prototype) - , m_data_block(DataBlock { buffer, DataBlock::Shared::No }) + , m_data_block(DataBlock { buffer, is_shared }) , m_detach_key(js_undefined()) { } @@ -156,7 +163,7 @@ ThrowCompletionOr allocate_array_buffer(VM& vm, FunctionObject& co } // 4. Let obj be ? OrdinaryCreateFromConstructor(constructor, "%ArrayBuffer.prototype%", slots). - auto obj = TRY(ordinary_create_from_constructor(vm, constructor, &Intrinsics::array_buffer_prototype, nullptr)); + auto obj = TRY(ordinary_create_from_constructor(vm, constructor, &Intrinsics::array_buffer_prototype, nullptr, DataBlock::Shared::No)); // 5. Let block be ? CreateByteDataBlock(byteLength). auto block = TRY(create_byte_data_block(vm, byte_length)); @@ -305,7 +312,7 @@ ThrowCompletionOr> get_array_buffer_max_byte_length_option(VM& ThrowCompletionOr> allocate_shared_array_buffer(VM& vm, FunctionObject& constructor, size_t byte_length) { // 1. Let obj be ? OrdinaryCreateFromConstructor(constructor, "%SharedArrayBuffer.prototype%", « [[ArrayBufferData]], [[ArrayBufferByteLength]] »). - auto obj = TRY(ordinary_create_from_constructor(vm, constructor, &Intrinsics::shared_array_buffer_prototype, nullptr)); + auto obj = TRY(ordinary_create_from_constructor(vm, constructor, &Intrinsics::shared_array_buffer_prototype, nullptr, DataBlock::Shared::Yes)); // 2. Let block be ? CreateSharedByteDataBlock(byteLength). auto block = TRY(create_shared_byte_data_block(vm, byte_length)); diff --git a/Libraries/LibJS/Runtime/ArrayBuffer.h b/Libraries/LibJS/Runtime/ArrayBuffer.h index 337cc429ebe..05aaf634c8f 100644 --- a/Libraries/LibJS/Runtime/ArrayBuffer.h +++ b/Libraries/LibJS/Runtime/ArrayBuffer.h @@ -59,9 +59,9 @@ class JS_API ArrayBuffer : public Object { GC_DECLARE_ALLOCATOR(ArrayBuffer); public: - static ThrowCompletionOr> create(Realm&, size_t); - static GC::Ref create(Realm&, ByteBuffer); - static GC::Ref create(Realm&, ByteBuffer*); + static ThrowCompletionOr> create(Realm&, size_t, DataBlock::Shared = DataBlock::Shared::No); + static GC::Ref create(Realm&, ByteBuffer, DataBlock::Shared = DataBlock::Shared::No); + static GC::Ref create(Realm&, ByteBuffer*, DataBlock::Shared = DataBlock::Shared::No); virtual ~ArrayBuffer() override = default; @@ -132,8 +132,8 @@ public: Value get_modify_set_value(size_t byte_index, Value value, ReadWriteModifyFunction operation, bool is_little_endian = true); private: - ArrayBuffer(ByteBuffer buffer, Object& prototype); - ArrayBuffer(ByteBuffer* buffer, Object& prototype); + ArrayBuffer(ByteBuffer buffer, DataBlock::Shared, Object& prototype); + ArrayBuffer(ByteBuffer* buffer, DataBlock::Shared, Object& prototype); virtual void visit_edges(Visitor&) override;