mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-06-16 15:21:56 +00:00
LibWeb: Support creation of shared memory in WebAssembly API
Add support for shared memory creation in WebAssembly memory API. This API is needed for WPT tests that use shared array buffers. Import related WPT tests.
This commit is contained in:
parent
6ec06a01a2
commit
b03138cbff
Notes:
github-actions[bot]
2024-12-08 21:11:34 +00:00
Author: https://github.com/zetslief
Commit: b03138cbff
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/2828
Reviewed-by: https://github.com/ADKaster
Reviewed-by: https://github.com/alimpfard ✅
13 changed files with 516 additions and 16 deletions
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
#include <LibJS/Runtime/Realm.h>
|
||||
#include <LibJS/Runtime/SharedArrayBufferConstructor.h>
|
||||
#include <LibJS/Runtime/VM.h>
|
||||
#include <LibWasm/Types.h>
|
||||
#include <LibWeb/Bindings/Intrinsics.h>
|
||||
|
@ -21,6 +22,13 @@ WebIDL::ExceptionOr<GC::Ref<Memory>> Memory::construct_impl(JS::Realm& realm, Me
|
|||
{
|
||||
auto& vm = realm.vm();
|
||||
|
||||
// https://webassembly.github.io/threads/js-api/index.html#dom-memory-memory
|
||||
// 4. Let share be shared if descriptor["shared"] is true and unshared otherwise.
|
||||
// 5. If share is shared and maximum is empty, throw a TypeError exception.
|
||||
auto shared = descriptor.shared.value_or(false);
|
||||
if (shared && !descriptor.maximum.has_value())
|
||||
return vm.throw_completion<JS::TypeError>("Maximum has to be specified for shared memory."sv);
|
||||
|
||||
Wasm::Limits limits { descriptor.initial, move(descriptor.maximum) };
|
||||
Wasm::MemoryType memory_type { move(limits) };
|
||||
|
||||
|
@ -29,7 +37,8 @@ WebIDL::ExceptionOr<GC::Ref<Memory>> Memory::construct_impl(JS::Realm& realm, Me
|
|||
if (!address.has_value())
|
||||
return vm.throw_completion<JS::TypeError>("Wasm Memory allocation failed"sv);
|
||||
|
||||
auto memory_object = realm.create<Memory>(realm, *address);
|
||||
auto memory_object = realm.create<Memory>(realm, *address, shared ? Shared::Yes : Shared::No);
|
||||
|
||||
cache.abstract_machine().store().get(*address)->successful_grow_hook = [memory_object] {
|
||||
MUST(memory_object->reset_the_memory_buffer());
|
||||
};
|
||||
|
@ -37,9 +46,10 @@ WebIDL::ExceptionOr<GC::Ref<Memory>> Memory::construct_impl(JS::Realm& realm, Me
|
|||
return memory_object;
|
||||
}
|
||||
|
||||
Memory::Memory(JS::Realm& realm, Wasm::MemoryAddress address)
|
||||
Memory::Memory(JS::Realm& realm, Wasm::MemoryAddress address, Shared shared)
|
||||
: Bindings::PlatformObject(realm)
|
||||
, m_address(address)
|
||||
, m_shared(shared)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -74,7 +84,9 @@ WebIDL::ExceptionOr<u32> Memory::grow(u32 delta)
|
|||
return previous_size;
|
||||
}
|
||||
|
||||
// https://webassembly.github.io/spec/js-api/#reset-the-memory-buffer
|
||||
// https://webassembly.github.io/spec/js-api/#refresh-the-memory-buffer
|
||||
// FIXME: `refresh-the-memory-buffer` is a global abstract operation.
|
||||
// Implement it as a static function to align with the spec.
|
||||
WebIDL::ExceptionOr<void> Memory::reset_the_memory_buffer()
|
||||
{
|
||||
if (!m_buffer)
|
||||
|
@ -83,10 +95,16 @@ WebIDL::ExceptionOr<void> Memory::reset_the_memory_buffer()
|
|||
auto& vm = this->vm();
|
||||
auto& realm = *vm.current_realm();
|
||||
|
||||
MUST(JS::detach_array_buffer(vm, *m_buffer, JS::PrimitiveString::create(vm, "WebAssembly.Memory"_string)));
|
||||
if (m_buffer->is_fixed_length()) {
|
||||
// https://webassembly.github.io/threads/js-api/index.html#refresh-the-memory-buffer
|
||||
// 1. If IsSharedArrayBuffer(buffer) is false,
|
||||
if (!m_buffer->is_shared_array_buffer()) {
|
||||
// 1. Perform ! DetachArrayBuffer(buffer, "WebAssembly.Memory").
|
||||
MUST(JS::detach_array_buffer(vm, *m_buffer, JS::PrimitiveString::create(vm, "WebAssembly.Memory"_string)));
|
||||
}
|
||||
}
|
||||
|
||||
auto buffer = TRY(create_a_memory_buffer(vm, realm, m_address));
|
||||
m_buffer = buffer;
|
||||
m_buffer = TRY(create_a_fixed_length_memory_buffer(vm, realm, m_address, m_shared));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
@ -98,21 +116,41 @@ WebIDL::ExceptionOr<GC::Ref<JS::ArrayBuffer>> Memory::buffer() const
|
|||
auto& realm = *vm.current_realm();
|
||||
|
||||
if (!m_buffer)
|
||||
m_buffer = TRY(create_a_memory_buffer(vm, realm, m_address));
|
||||
m_buffer = TRY(create_a_fixed_length_memory_buffer(vm, realm, m_address, m_shared));
|
||||
|
||||
return GC::Ref(*m_buffer);
|
||||
}
|
||||
|
||||
// https://webassembly.github.io/spec/js-api/#create-a-memory-buffer
|
||||
WebIDL::ExceptionOr<GC::Ref<JS::ArrayBuffer>> Memory::create_a_memory_buffer(JS::VM& vm, JS::Realm& realm, Wasm::MemoryAddress address)
|
||||
// https://webassembly.github.io/spec/js-api/#create-a-fixed-length-memory-buffer
|
||||
WebIDL::ExceptionOr<GC::Ref<JS::ArrayBuffer>> Memory::create_a_fixed_length_memory_buffer(JS::VM& vm, JS::Realm& realm, Wasm::MemoryAddress address, Shared shared)
|
||||
{
|
||||
auto& context = Detail::get_cache(realm);
|
||||
auto* memory = context.abstract_machine().store().get(address);
|
||||
if (!memory)
|
||||
return vm.throw_completion<JS::RangeError>("Could not find the memory instance"sv);
|
||||
|
||||
auto array_buffer = JS::ArrayBuffer::create(realm, &memory->data());
|
||||
array_buffer->set_detach_key(JS::PrimitiveString::create(vm, "WebAssembly.Memory"_string));
|
||||
JS::ArrayBuffer* array_buffer;
|
||||
// https://webassembly.github.io/threads/js-api/index.html#create-a-fixed-length-memory-buffer
|
||||
// 3. If share is shared,
|
||||
if (shared == Shared::Yes) {
|
||||
// 1. Let block be a Shared Data Block which is identified with the underlying memory of memaddr.
|
||||
auto bytes = memory->data();
|
||||
|
||||
// 2. Let buffer be a new SharedArrayBuffer with the internal slots [[ArrayBufferData]] and [[ArrayBufferByteLength]].
|
||||
array_buffer = TRY(JS::allocate_shared_array_buffer(vm, realm.intrinsics().shared_array_buffer_constructor(), bytes.size()));
|
||||
bytes.span().copy_to(array_buffer->buffer().span());
|
||||
// 3. FIXME: Set buffer.[[ArrayBufferData]] to block.
|
||||
// 4. FIXME: Set buffer.[[ArrayBufferByteLength]] to the length of block.
|
||||
|
||||
// 5. Perform ! SetIntegrityLevel(buffer, "frozen").
|
||||
MUST(array_buffer->set_integrity_level(JS::Object::IntegrityLevel::Frozen));
|
||||
}
|
||||
|
||||
// 4. Otherwise,
|
||||
else {
|
||||
array_buffer = JS::ArrayBuffer::create(realm, &memory->data());
|
||||
array_buffer->set_detach_key(JS::PrimitiveString::create(vm, "WebAssembly.Memory"_string));
|
||||
}
|
||||
|
||||
return GC::Ref(*array_buffer);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue