diff --git a/Libraries/LibWeb/WebAssembly/Instance.cpp b/Libraries/LibWeb/WebAssembly/Instance.cpp index 833cdb9f7d0..48ddaba28d0 100644 --- a/Libraries/LibWeb/WebAssembly/Instance.cpp +++ b/Libraries/LibWeb/WebAssembly/Instance.cpp @@ -71,10 +71,11 @@ void Instance::initialize(JS::Realm& realm) m_exports->define_direct_property(name, *object, JS::default_attributes); }, [&](Wasm::MemoryAddress const& address) { - Optional> object = m_memory_instances.get(address); + Optional> object = cache.get_memory_instance(address); if (!object.has_value()) { + // FIXME: Once LibWasm implements the threads/atomics proposal, the shared-ness should be + // obtained from the Wasm::MemoryInstance's type. object = realm.create(realm, address, Memory::Shared::No); - m_memory_instances.set(address, *object); } m_exports->define_direct_property(name, *object, JS::default_attributes); @@ -98,7 +99,6 @@ void Instance::visit_edges(Visitor& visitor) Base::visit_edges(visitor); visitor.visit(m_exports); visitor.visit(m_function_instances); - visitor.visit(m_memory_instances); visitor.visit(m_table_instances); } diff --git a/Libraries/LibWeb/WebAssembly/Instance.h b/Libraries/LibWeb/WebAssembly/Instance.h index 8cfc378481d..36b96830728 100644 --- a/Libraries/LibWeb/WebAssembly/Instance.h +++ b/Libraries/LibWeb/WebAssembly/Instance.h @@ -36,7 +36,6 @@ private: GC::Ref m_exports; NonnullOwnPtr m_module_instance; HashMap> m_function_instances; - HashMap> m_memory_instances; HashMap> m_table_instances; }; diff --git a/Libraries/LibWeb/WebAssembly/Memory.cpp b/Libraries/LibWeb/WebAssembly/Memory.cpp index ac9ad8e0cdd..e2dc04cd486 100644 --- a/Libraries/LibWeb/WebAssembly/Memory.cpp +++ b/Libraries/LibWeb/WebAssembly/Memory.cpp @@ -49,15 +49,37 @@ Memory::Memory(JS::Realm& realm, Wasm::MemoryAddress address, Shared shared) { auto& cache = Detail::get_cache(realm); - cache.abstract_machine().store().get(address)->successful_grow_hook = [this] { - MUST(reset_the_memory_buffer()); + cache.abstract_machine().store().get(address)->successful_grow_hook = [realm = GC::Ref(realm), address] { + refresh_the_memory_buffer(realm->vm(), realm, address); }; } +// https://webassembly.github.io/spec/js-api/#initialize-a-memory-object void Memory::initialize(JS::Realm& realm) { WEB_SET_PROTOTYPE_FOR_INTERFACE_WITH_CUSTOM_NAME(Memory, WebAssembly.Memory); Base::initialize(realm); + + auto& vm = realm.vm(); + + // https://webassembly.github.io/spec/js-api/#initialize-a-memory-object + // 1. Let map be the surrounding agent’s associated Memory object cache. + // 2. Assert: map[memaddr] doesn’t exist. + auto& cache = Detail::get_cache(realm); + auto exists = cache.memory_instances().contains(m_address); + VERIFY(!exists); + + // 3. Let buffer be the result of creating a fixed length memory buffer from memaddr. + auto buffer = create_a_fixed_length_memory_buffer(vm, realm, m_address, m_shared); + + // 4. Set memory.[[Memory]] to memaddr. + // NOTE: This is already set by the Memory constructor. + + // 5. Set memory.[[BufferObject]] to buffer. + m_buffer = buffer; + + // 6. Set map[memaddr] to memory. + cache.add_memory_instance(m_address, *this); } void Memory::visit_edges(Visitor& visitor) @@ -73,62 +95,58 @@ WebIDL::ExceptionOr Memory::grow(u32 delta) auto& context = Detail::get_cache(realm()); auto* memory = context.abstract_machine().store().get(address()); - if (!memory) - return vm.throw_completion("Could not find the memory instance to grow"sv); + VERIFY(memory); auto previous_size = memory->size() / Wasm::Constants::page_size; if (!memory->grow(delta * Wasm::Constants::page_size, Wasm::MemoryInstance::GrowType::No, Wasm::MemoryInstance::InhibitGrowCallback::Yes)) return vm.throw_completion("Memory.grow() grows past the stated limit of the memory instance"sv); - TRY(reset_the_memory_buffer()); + refresh_the_memory_buffer(vm, realm(), m_address); return previous_size; } // 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 Memory::reset_the_memory_buffer() +void Memory::refresh_the_memory_buffer(JS::VM& vm, JS::Realm& realm, Wasm::MemoryAddress address) { - if (!m_buffer) - return {}; + // 1. Let map be the surrounding agent’s associated Memory object cache. + // 2. Assert: map[memaddr] exists. + // 3. Let memory be map[memaddr]. + auto& cache = Detail::get_cache(realm); + auto memory = cache.get_memory_instance(address); + VERIFY(memory.has_value()); - auto& vm = this->vm(); - auto& realm = *vm.current_realm(); + // 4. Let buffer be memory.[[BufferObject]]. + auto& buffer = memory.value()->m_buffer; - if (m_buffer->is_fixed_length()) { + // 5. If IsFixedLengthArrayBuffer(buffer) is true, + if (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()) { + if (!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))); + MUST(JS::detach_array_buffer(vm, *buffer, JS::PrimitiveString::create(vm, "WebAssembly.Memory"_string))); } + + // 2. Let newBuffer be the result of creating a fixed length memory buffer from memaddr. + // 3. Set memory.[[BufferObject]] to newBuffer. } - m_buffer = TRY(create_a_fixed_length_memory_buffer(vm, realm, m_address, m_shared)); - - return {}; + buffer = create_a_fixed_length_memory_buffer(vm, realm, address, memory.value()->m_shared); } // https://webassembly.github.io/spec/js-api/#dom-memory-buffer WebIDL::ExceptionOr> Memory::buffer() const { - auto& vm = this->vm(); - auto& realm = *vm.current_realm(); - - if (!m_buffer) - 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-fixed-length-memory-buffer -WebIDL::ExceptionOr> Memory::create_a_fixed_length_memory_buffer(JS::VM& vm, JS::Realm& realm, Wasm::MemoryAddress address, Shared shared) +GC::Ref 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("Could not find the memory instance"sv); + VERIFY(memory); JS::ArrayBuffer* array_buffer; // https://webassembly.github.io/threads/js-api/index.html#create-a-fixed-length-memory-buffer diff --git a/Libraries/LibWeb/WebAssembly/Memory.h b/Libraries/LibWeb/WebAssembly/Memory.h index e3cd97aeb70..dd85e307e43 100644 --- a/Libraries/LibWeb/WebAssembly/Memory.h +++ b/Libraries/LibWeb/WebAssembly/Memory.h @@ -46,8 +46,8 @@ private: virtual void initialize(JS::Realm&) override; virtual void visit_edges(Visitor&) override; - WebIDL::ExceptionOr reset_the_memory_buffer(); - static WebIDL::ExceptionOr> create_a_fixed_length_memory_buffer(JS::VM&, JS::Realm&, Wasm::MemoryAddress, Shared shared); + static void refresh_the_memory_buffer(JS::VM&, JS::Realm&, Wasm::MemoryAddress); + static GC::Ref create_a_fixed_length_memory_buffer(JS::VM&, JS::Realm&, Wasm::MemoryAddress, Shared shared); Wasm::MemoryAddress m_address; Shared m_shared { Shared::No }; diff --git a/Libraries/LibWeb/WebAssembly/WebAssembly.cpp b/Libraries/LibWeb/WebAssembly/WebAssembly.cpp index b797b5de875..4f3b99b2bab 100644 --- a/Libraries/LibWeb/WebAssembly/WebAssembly.cpp +++ b/Libraries/LibWeb/WebAssembly/WebAssembly.cpp @@ -63,6 +63,7 @@ void visit_edges(JS::Object& object, JS::Cell::Visitor& visitor) visitor.visit(cache.imported_objects()); visitor.visit(cache.extern_values()); visitor.visit(cache.global_instances()); + visitor.visit(cache.memory_instances()); cache.abstract_machine().visit_external_resources({ .visit_trap = [&visitor](Wasm::ExternallyManagedTrap const& trap) { auto& completion = trap.unsafe_external_object_as(); visitor.visit(completion.value()); diff --git a/Libraries/LibWeb/WebAssembly/WebAssembly.h b/Libraries/LibWeb/WebAssembly/WebAssembly.h index ed40f742dc1..7c693394dd0 100644 --- a/Libraries/LibWeb/WebAssembly/WebAssembly.h +++ b/Libraries/LibWeb/WebAssembly/WebAssembly.h @@ -55,15 +55,18 @@ public: m_inverse_extern_values.set(value, address); } void add_global_instance(Wasm::GlobalAddress address, GC::Ptr global) { m_global_instances.set(address, global); } + void add_memory_instance(Wasm::MemoryAddress address, GC::Ptr memory) { m_memory_instances.set(address, memory); } Optional> get_function_instance(Wasm::FunctionAddress address) { return m_function_instances.get(address); } Optional get_extern_value(Wasm::ExternAddress address) { return m_extern_values.get(address); } Optional> get_global_instance(Wasm::GlobalAddress address) { return m_global_instances.get(address); } + Optional> get_memory_instance(Wasm::MemoryAddress address) { return m_memory_instances.get(address); } HashMap> const& function_instances() const { return m_function_instances; } HashMap const& extern_values() const { return m_extern_values; } HashMap const& inverse_extern_values() const { return m_inverse_extern_values; } HashMap> const& global_instances() const { return m_global_instances; } + HashMap> const& memory_instances() const { return m_memory_instances; } HashTable> const& imported_objects() const { return m_imported_objects; } Wasm::AbstractMachine& abstract_machine() { return m_abstract_machine; } @@ -72,6 +75,7 @@ private: HashMap m_extern_values; HashMap m_inverse_extern_values; HashMap> m_global_instances; + HashMap> m_memory_instances; Vector> m_compiled_modules; HashTable> m_imported_objects; Wasm::AbstractMachine m_abstract_machine;