From 6d4b8bde55c3f4b02d54ad0fc5450f0b442d4927 Mon Sep 17 00:00:00 2001 From: Tim Ledbetter Date: Thu, 25 Apr 2024 19:09:34 +0100 Subject: [PATCH] LibWeb: Isolate WebAssembly cache by global object This change moves WebAssembly related data that was previously globally accessible into the `WebAssemblyCache` object and creates one of these per global object. This ensures that WebAssembly data cannot be accessed across realms. --- .../BindingsGenerator/IDLGenerators.cpp | 2 +- .../Libraries/LibWeb/WebAssembly/Instance.cpp | 29 +++--- .../Libraries/LibWeb/WebAssembly/Instance.h | 9 +- .../Libraries/LibWeb/WebAssembly/Memory.cpp | 11 ++- .../Libraries/LibWeb/WebAssembly/Module.cpp | 13 +-- .../Libraries/LibWeb/WebAssembly/Module.h | 8 +- .../Libraries/LibWeb/WebAssembly/Table.cpp | 17 ++-- .../LibWeb/WebAssembly/WebAssembly.cpp | 99 +++++++++---------- .../LibWeb/WebAssembly/WebAssembly.h | 49 ++++----- 9 files changed, 121 insertions(+), 116 deletions(-) diff --git a/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp b/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp index d34ef4b6216..a3d12ccbce4 100644 --- a/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp @@ -3830,7 +3830,7 @@ void @namespace_class@::initialize(JS::Realm& realm) void @namespace_class@::visit_edges(JS::Cell::Visitor& visitor) { Base::visit_edges(visitor); - @name@::visit_edges(visitor); + @name@::visit_edges(*this, visitor); } )~~~"); } diff --git a/Userland/Libraries/LibWeb/WebAssembly/Instance.cpp b/Userland/Libraries/LibWeb/WebAssembly/Instance.cpp index 6bf56018c62..75ccaed4d5c 100644 --- a/Userland/Libraries/LibWeb/WebAssembly/Instance.cpp +++ b/Userland/Libraries/LibWeb/WebAssembly/Instance.cpp @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include @@ -29,14 +28,14 @@ WebIDL::ExceptionOr> Instance::construct_impl(JS::Rea auto& vm = realm.vm(); - auto index = TRY(Detail::instantiate_module(vm, module.module())); - return vm.heap().allocate(realm, realm, index); + auto module_instance = TRY(Detail::instantiate_module(vm, module.compiled_module()->module)); + return vm.heap().allocate(realm, realm, move(module_instance)); } -Instance::Instance(JS::Realm& realm, size_t index) +Instance::Instance(JS::Realm& realm, NonnullOwnPtr module_instance) : Bindings::PlatformObject(realm) , m_exports(Object::create(realm, nullptr)) - , m_index(index) + , m_module_instance(move(module_instance)) { } @@ -47,34 +46,31 @@ void Instance::initialize(JS::Realm& realm) Base::initialize(realm); WEB_SET_PROTOTYPE_FOR_INTERFACE_WITH_CUSTOM_NAME(Instance, WebAssembly.Instance); - auto& instance = *Detail::s_instantiated_modules[m_index]; - auto& cache = Detail::s_module_caches.at(m_index); - - for (auto& export_ : instance.exports()) { + for (auto& export_ : m_module_instance->exports()) { export_.value().visit( [&](Wasm::FunctionAddress const& address) { - Optional> object = cache.function_instances.get(address); + Optional> object = m_function_instances.get(address); if (!object.has_value()) { object = Detail::create_native_function(vm, address, export_.name()); - cache.function_instances.set(address, *object); + m_function_instances.set(address, *object); } m_exports->define_direct_property(export_.name(), *object, JS::default_attributes); }, [&](Wasm::MemoryAddress const& address) { - Optional> object = cache.memory_instances.get(address); + Optional> object = m_memory_instances.get(address); if (!object.has_value()) { object = heap().allocate(realm, realm, address); - cache.memory_instances.set(address, *object); + m_memory_instances.set(address, *object); } m_exports->define_direct_property(export_.name(), *object, JS::default_attributes); }, [&](Wasm::TableAddress const& address) { - Optional> object = cache.table_instances.get(address); + Optional> object = m_table_instances.get(address); if (!object.has_value()) { object = heap().allocate(realm, realm, address); - cache.table_instances.set(address, *object); + m_table_instances.set(address, *object); } m_exports->define_direct_property(export_.name(), *object, JS::default_attributes); @@ -91,6 +87,9 @@ 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/Userland/Libraries/LibWeb/WebAssembly/Instance.h b/Userland/Libraries/LibWeb/WebAssembly/Instance.h index c657a3e70e6..1d5d13d3576 100644 --- a/Userland/Libraries/LibWeb/WebAssembly/Instance.h +++ b/Userland/Libraries/LibWeb/WebAssembly/Instance.h @@ -11,8 +11,10 @@ #include #include #include +#include #include #include +#include namespace Web::WebAssembly { @@ -26,13 +28,16 @@ public: Object const* exports() const { return m_exports.ptr(); } private: - Instance(JS::Realm&, size_t index); + Instance(JS::Realm&, NonnullOwnPtr); virtual void initialize(JS::Realm&) override; virtual void visit_edges(Visitor&) override; JS::NonnullGCPtr m_exports; - size_t m_index { 0 }; + NonnullOwnPtr m_module_instance; + HashMap> m_function_instances; + HashMap> m_memory_instances; + HashMap> m_table_instances; }; } diff --git a/Userland/Libraries/LibWeb/WebAssembly/Memory.cpp b/Userland/Libraries/LibWeb/WebAssembly/Memory.cpp index 894edb8e480..2cdc2a5f868 100644 --- a/Userland/Libraries/LibWeb/WebAssembly/Memory.cpp +++ b/Userland/Libraries/LibWeb/WebAssembly/Memory.cpp @@ -23,12 +23,13 @@ WebIDL::ExceptionOr> Memory::construct_impl(JS::Realm& Wasm::Limits limits { descriptor.initial, move(descriptor.maximum) }; Wasm::MemoryType memory_type { move(limits) }; - auto address = Detail::s_abstract_machine.store().allocate(memory_type); + auto& cache = Detail::get_cache(realm); + auto address = cache.abstract_machine().store().allocate(memory_type); if (!address.has_value()) return vm.throw_completion("Wasm Memory allocation failed"sv); auto memory_object = vm.heap().allocate(realm, realm, *address); - Detail::s_abstract_machine.store().get(*address)->successful_grow_hook = [memory_object] { + cache.abstract_machine().store().get(*address)->successful_grow_hook = [memory_object] { MUST(memory_object->reset_the_memory_buffer()); }; @@ -58,7 +59,8 @@ WebIDL::ExceptionOr Memory::grow(u32 delta) { auto& vm = this->vm(); - auto* memory = Detail::s_abstract_machine.store().get(address()); + 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); @@ -103,7 +105,8 @@ WebIDL::ExceptionOr> Memory::buffer() const // https://webassembly.github.io/spec/js-api/#create-a-memory-buffer WebIDL::ExceptionOr> Memory::create_a_memory_buffer(JS::VM& vm, JS::Realm& realm, Wasm::MemoryAddress address) { - auto* memory = Detail::s_abstract_machine.store().get(address); + 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); diff --git a/Userland/Libraries/LibWeb/WebAssembly/Module.cpp b/Userland/Libraries/LibWeb/WebAssembly/Module.cpp index 83abf4043d6..086ed2f38f0 100644 --- a/Userland/Libraries/LibWeb/WebAssembly/Module.cpp +++ b/Userland/Libraries/LibWeb/WebAssembly/Module.cpp @@ -21,13 +21,13 @@ WebIDL::ExceptionOr> Module::construct_impl(JS::Realm& { auto& vm = realm.vm(); - auto index = TRY(Detail::parse_module(vm, bytes->raw_object())); - return vm.heap().allocate(realm, realm, index); + auto compiled_module = TRY(Detail::parse_module(vm, bytes->raw_object())); + return vm.heap().allocate(realm, realm, move(compiled_module)); } -Module::Module(JS::Realm& realm, size_t index) +Module::Module(JS::Realm& realm, NonnullRefPtr compiled_module) : Bindings::PlatformObject(realm) - , m_index(index) + , m_compiled_module(move(compiled_module)) { } @@ -37,9 +37,4 @@ void Module::initialize(JS::Realm& realm) WEB_SET_PROTOTYPE_FOR_INTERFACE_WITH_CUSTOM_NAME(Module, WebAssembly.Module); } -Wasm::Module const& Module::module() const -{ - return Detail::s_compiled_modules.at(index())->module; -} - } diff --git a/Userland/Libraries/LibWeb/WebAssembly/Module.h b/Userland/Libraries/LibWeb/WebAssembly/Module.h index 2786919fdd2..e4339edbab0 100644 --- a/Userland/Libraries/LibWeb/WebAssembly/Module.h +++ b/Userland/Libraries/LibWeb/WebAssembly/Module.h @@ -13,6 +13,7 @@ #include #include #include +#include namespace Web::WebAssembly { @@ -23,15 +24,14 @@ class Module : public Bindings::PlatformObject { public: static WebIDL::ExceptionOr> construct_impl(JS::Realm&, JS::Handle& bytes); - size_t index() const { return m_index; } - Wasm::Module const& module() const; + NonnullRefPtr compiled_module() const { return m_compiled_module; } private: - Module(JS::Realm&, size_t index); + Module(JS::Realm&, NonnullRefPtr); virtual void initialize(JS::Realm&) override; - size_t m_index { 0 }; + NonnullRefPtr m_compiled_module; }; } diff --git a/Userland/Libraries/LibWeb/WebAssembly/Table.cpp b/Userland/Libraries/LibWeb/WebAssembly/Table.cpp index 986b75a12d7..2775947335a 100644 --- a/Userland/Libraries/LibWeb/WebAssembly/Table.cpp +++ b/Userland/Libraries/LibWeb/WebAssembly/Table.cpp @@ -45,12 +45,13 @@ WebIDL::ExceptionOr> Table::construct_impl(JS::Realm& re Wasm::Limits limits { descriptor.initial, move(descriptor.maximum) }; Wasm::TableType table_type { reference_type, move(limits) }; - auto address = Detail::s_abstract_machine.store().allocate(table_type); + auto& cache = Detail::get_cache(realm); + auto address = cache.abstract_machine().store().allocate(table_type); if (!address.has_value()) return vm.throw_completion("Wasm Table allocation failed"sv); auto const& reference = reference_value.value().get(); - auto& table = *Detail::s_abstract_machine.store().get(*address); + auto& table = *cache.abstract_machine().store().get(*address); for (auto& element : table.elements()) element = reference; @@ -74,7 +75,8 @@ WebIDL::ExceptionOr Table::grow(u32 delta, JS::Value value) { auto& vm = this->vm(); - auto* table = Detail::s_abstract_machine.store().get(address()); + auto& cache = Detail::get_cache(realm()); + auto* table = cache.abstract_machine().store().get(address()); if (!table) return vm.throw_completion("Could not find the memory table to grow"sv); @@ -94,7 +96,8 @@ WebIDL::ExceptionOr Table::get(u32 index) const { auto& vm = this->vm(); - auto* table = Detail::s_abstract_machine.store().get(address()); + auto& cache = Detail::get_cache(realm()); + auto* table = cache.abstract_machine().store().get(address()); if (!table) return vm.throw_completion("Could not find the memory table"sv); @@ -114,7 +117,8 @@ WebIDL::ExceptionOr Table::set(u32 index, JS::Value value) { auto& vm = this->vm(); - auto* table = Detail::s_abstract_machine.store().get(address()); + auto& cache = Detail::get_cache(realm()); + auto* table = cache.abstract_machine().store().get(address()); if (!table) return vm.throw_completion("Could not find the memory table"sv); @@ -134,7 +138,8 @@ WebIDL::ExceptionOr Table::length() const { auto& vm = this->vm(); - auto* table = Detail::s_abstract_machine.store().get(address()); + auto& cache = Detail::get_cache(realm()); + auto* table = cache.abstract_machine().store().get(address()); if (!table) return vm.throw_completion("Could not find the memory table"sv); diff --git a/Userland/Libraries/LibWeb/WebAssembly/WebAssembly.cpp b/Userland/Libraries/LibWeb/WebAssembly/WebAssembly.cpp index e2fdbae9706..46a110481b3 100644 --- a/Userland/Libraries/LibWeb/WebAssembly/WebAssembly.cpp +++ b/Userland/Libraries/LibWeb/WebAssembly/WebAssembly.cpp @@ -30,26 +30,21 @@ namespace Web::WebAssembly { namespace Detail { -Vector> s_compiled_modules; -Vector> s_instantiated_modules; -Vector s_module_caches; -GlobalModuleCache s_global_cache; -Wasm::AbstractMachine s_abstract_machine; +HashMap, WebAssemblyCache> s_caches; + +WebAssemblyCache& get_cache(JS::Realm& realm) +{ + return s_caches.ensure(realm.global_object()); +} } -void visit_edges(JS::Cell::Visitor& visitor) +void visit_edges(JS::Object& object, JS::Cell::Visitor& visitor) { - for (auto& entry : Detail::s_global_cache.function_instances) - visitor.visit(entry.value); - - for (auto& module_cache : Detail::s_module_caches) { - for (auto& entry : module_cache.function_instances) - visitor.visit(entry.value); - for (auto& entry : module_cache.memory_instances) - visitor.visit(entry.value); - for (auto& entry : module_cache.table_instances) - visitor.visit(entry.value); + auto& global_object = HTML::relevant_global_object(object); + if (auto maybe_cache = Detail::s_caches.get(global_object); maybe_cache.has_value()) { + auto& cache = maybe_cache.release_value(); + visitor.visit(cache.function_instances()); } } @@ -60,17 +55,16 @@ bool validate(JS::VM& vm, JS::Handle& bytes) // Note: There's no need to copy the bytes here as the buffer data cannot change while we're compiling the module. // 2. Compile stableBytes as a WebAssembly module and store the results as module. - auto maybe_module = Detail::parse_module(vm, bytes->raw_object()); + auto module_or_error = Detail::parse_module(vm, bytes->raw_object()); // 3. If module is error, return false. - if (maybe_module.is_error()) + if (module_or_error.is_error()) return false; - // Drop the module from the cache, we're never going to refer to it. - ScopeGuard drop_from_cache { [&] { (void)Detail::s_compiled_modules.take_last(); } }; - // 3 continued - our "compile" step is lazy with validation, explicitly do the validation. - if (Detail::s_abstract_machine.validate(Detail::s_compiled_modules[maybe_module.value()]->module).is_error()) + auto compiled_module = module_or_error.release_value(); + auto& cache = Detail::get_cache(*vm.current_realm()); + if (cache.abstract_machine().validate(compiled_module->module).is_error()) return false; // 4. Return true. @@ -83,13 +77,13 @@ WebIDL::ExceptionOr compile(JS::VM& vm, JS::Handleraw_object()); + auto compiled_module_or_error = Detail::parse_module(vm, bytes->raw_object()); auto promise = JS::Promise::create(realm); - if (module.is_error()) { - promise->reject(*module.release_error().value()); + if (compiled_module_or_error.is_error()) { + promise->reject(*compiled_module_or_error.release_error().value()); } else { - auto module_object = vm.heap().allocate(realm, realm, module.release_value()); + auto module_object = vm.heap().allocate(realm, realm, compiled_module_or_error.release_value()); promise->fulfill(module_object); } @@ -105,21 +99,21 @@ WebIDL::ExceptionOr instantiate(JS::VM& vm, JS::Handleraw_object()); + auto compiled_module_or_error = Detail::parse_module(vm, bytes->raw_object()); auto promise = JS::Promise::create(realm); - if (module.is_error()) { - promise->reject(*module.release_error().value()); + if (compiled_module_or_error.is_error()) { + promise->reject(*compiled_module_or_error.release_error().value()); return promise; } - auto const& compiled_module = Detail::s_compiled_modules.at(module.release_value())->module; - auto result = Detail::instantiate_module(vm, compiled_module); + auto compiled_module = compiled_module_or_error.release_value(); + auto result = Detail::instantiate_module(vm, compiled_module->module); if (result.is_error()) { promise->reject(*result.release_error().value()); } else { - auto module_object = vm.heap().allocate(realm, realm, Detail::s_compiled_modules.size() - 1); + auto module_object = vm.heap().allocate(realm, realm, move(compiled_module)); auto instance_object = vm.heap().allocate(realm, realm, result.release_value()); auto object = JS::Object::create(realm, nullptr); @@ -140,8 +134,8 @@ WebIDL::ExceptionOr instantiate(JS::VM& vm, Module const& module_obje auto& realm = *vm.current_realm(); auto promise = JS::Promise::create(realm); - auto const& compiled_module = module_object.module(); - auto result = Detail::instantiate_module(vm, compiled_module); + auto const& compiled_module = module_object.compiled_module(); + auto result = Detail::instantiate_module(vm, compiled_module->module); if (result.is_error()) { promise->reject(*result.release_error().value()); @@ -155,11 +149,12 @@ WebIDL::ExceptionOr instantiate(JS::VM& vm, Module const& module_obje namespace Detail { -JS::ThrowCompletionOr instantiate_module(JS::VM& vm, Wasm::Module const& module) +JS::ThrowCompletionOr> instantiate_module(JS::VM& vm, Wasm::Module const& module) { Wasm::Linker linker { module }; HashMap resolved_imports; auto import_argument = vm.argument(1); + auto& cache = get_cache(*vm.current_realm()); if (!import_argument.is_undefined()) { auto import_object = TRY(import_argument.to_object(vm)); dbgln("Trying to resolve stuff because import object was specified"); @@ -220,7 +215,7 @@ JS::ThrowCompletionOr instantiate_module(JS::VM& vm, Wasm::Module const& }, type }; - auto address = s_abstract_machine.store().allocate(move(host_function)); + auto address = cache.abstract_machine().store().allocate(move(host_function)); dbgln("Resolved to {}", address->value()); // FIXME: LinkError instead. VERIFY(address.has_value()); @@ -241,7 +236,7 @@ JS::ThrowCompletionOr instantiate_module(JS::VM& vm, Wasm::Module const& return vm.throw_completion("LinkError: Import resolution attempted to cast a BigInteger to a Number"sv); } auto cast_value = TRY(to_webassembly_value(vm, import_, type.type())); - address = s_abstract_machine.store().allocate({ type.type(), false }, cast_value); + address = cache.abstract_machine().store().allocate({ type.type(), false }, cast_value); } else { // FIXME: https://webassembly.github.io/spec/js-api/#read-the-imports step 5.2 // if v implements Global @@ -290,18 +285,16 @@ JS::ThrowCompletionOr instantiate_module(JS::VM& vm, Wasm::Module const& return vm.throw_completion(MUST(builder.to_string())); } - auto instance_result = s_abstract_machine.instantiate(module, link_result.release_value()); + auto instance_result = cache.abstract_machine().instantiate(module, link_result.release_value()); if (instance_result.is_error()) { // FIXME: Throw a LinkError instead. return vm.throw_completion(instance_result.error().error); } - s_instantiated_modules.append(instance_result.release_value()); - s_module_caches.empend(); - return s_instantiated_modules.size() - 1; + return instance_result.release_value(); } -JS::ThrowCompletionOr parse_module(JS::VM& vm, JS::Object* buffer_object) +JS::ThrowCompletionOr> parse_module(JS::VM& vm, JS::Object* buffer_object) { ReadonlyBytes data; if (is(buffer_object)) { @@ -333,21 +326,23 @@ JS::ThrowCompletionOr parse_module(JS::VM& vm, JS::Object* buffer_object return vm.throw_completion(Wasm::parse_error_to_byte_string(module_result.error())); } - if (auto validation_result = s_abstract_machine.validate(module_result.value()); validation_result.is_error()) { + auto& cache = get_cache(*vm.current_realm()); + if (auto validation_result = cache.abstract_machine().validate(module_result.value()); validation_result.is_error()) { // FIXME: Throw CompileError instead. return vm.throw_completion(validation_result.error().error_string); } - - s_compiled_modules.append(make(module_result.release_value())); - return s_compiled_modules.size() - 1; + auto compiled_module = make_ref_counted(module_result.release_value()); + cache.add_compiled_module(compiled_module); + return compiled_module; } JS::NativeFunction* create_native_function(JS::VM& vm, Wasm::FunctionAddress address, ByteString const& name) { auto& realm = *vm.current_realm(); Optional type; - s_abstract_machine.store().get(address)->visit([&](auto const& value) { type = value.type(); }); - if (auto entry = s_global_cache.function_instances.get(address); entry.has_value()) + auto& cache = get_cache(realm); + cache.abstract_machine().store().get(address)->visit([&](auto const& value) { type = value.type(); }); + if (auto entry = cache.get_function_instance(address); entry.has_value()) return *entry; auto function = JS::NativeFunction::create( @@ -363,7 +358,8 @@ JS::NativeFunction* create_native_function(JS::VM& vm, Wasm::FunctionAddress add for (auto& type : type.parameters()) values.append(TRY(to_webassembly_value(vm, vm.argument(index++), type))); - auto result = s_abstract_machine.invoke(address, move(values)); + auto& cache = get_cache(realm); + auto result = cache.abstract_machine().invoke(address, move(values)); // FIXME: Use the convoluted mapping of errors defined in the spec. if (result.is_trap()) return vm.throw_completion(TRY_OR_THROW_OOM(vm, String::formatted("Wasm execution trapped (WIP): {}", result.trap().reason))); @@ -379,7 +375,7 @@ JS::NativeFunction* create_native_function(JS::VM& vm, Wasm::FunctionAddress add })); }); - s_global_cache.function_instances.set(address, function); + cache.add_function_instance(address, function); return function; } @@ -416,7 +412,8 @@ JS::ThrowCompletionOr to_webassembly_value(JS::VM& vm, JS::Value va if (value.is_function()) { auto& function = value.as_function(); - for (auto& entry : s_global_cache.function_instances) { + auto& cache = get_cache(*vm.current_realm()); + for (auto& entry : cache.function_instances()) { if (entry.value == &function) return Wasm::Value { Wasm::Reference { Wasm::Reference::Func { entry.key } } }; } diff --git a/Userland/Libraries/LibWeb/WebAssembly/WebAssembly.h b/Userland/Libraries/LibWeb/WebAssembly/WebAssembly.h index 543a963f9f4..10429f34898 100644 --- a/Userland/Libraries/LibWeb/WebAssembly/WebAssembly.h +++ b/Userland/Libraries/LibWeb/WebAssembly/WebAssembly.h @@ -18,7 +18,7 @@ namespace Web::WebAssembly { -void visit_edges(JS::Cell::Visitor&); +void visit_edges(JS::Object&, JS::Cell::Visitor&); bool validate(JS::VM&, JS::Handle& bytes); WebIDL::ExceptionOr compile(JS::VM&, JS::Handle& bytes); @@ -27,14 +27,7 @@ WebIDL::ExceptionOr instantiate(JS::VM&, JS::Handle instantiate(JS::VM&, Module const& module_object, Optional>& import_object); namespace Detail { - -JS::ThrowCompletionOr instantiate_module(JS::VM&, Wasm::Module const&); -JS::ThrowCompletionOr parse_module(JS::VM&, JS::Object* buffer); -JS::NativeFunction* create_native_function(JS::VM&, Wasm::FunctionAddress address, ByteString const& name); -JS::ThrowCompletionOr to_webassembly_value(JS::VM&, JS::Value value, Wasm::ValueType const& type); -JS::Value to_js_value(JS::VM&, Wasm::Value& wasm_value); - -struct CompiledWebAssemblyModule { +struct CompiledWebAssemblyModule : public RefCounted { explicit CompiledWebAssemblyModule(Wasm::Module&& module) : module(move(module)) { @@ -43,23 +36,31 @@ struct CompiledWebAssemblyModule { Wasm::Module module; }; -// FIXME: These should just be members of the module (instance) object, but the module needs to stick -// around while its instance is alive so ideally this would be a refcounted object, shared between -// WebAssemblyModuleObject's and WebAssemblyInstantiatedModuleObject's. -struct ModuleCache { - HashMap> function_instances; - HashMap> memory_instances; - HashMap> table_instances; -}; -struct GlobalModuleCache { - HashMap> function_instances; +class WebAssemblyCache { +public: + void add_compiled_module(NonnullRefPtr module) { m_compiled_modules.append(module); } + void add_function_instance(Wasm::FunctionAddress address, JS::GCPtr function) { m_function_instances.set(address, function); } + + Optional> get_function_instance(Wasm::FunctionAddress address) { return m_function_instances.get(address); } + + HashMap> function_instances() const { return m_function_instances; } + Wasm::AbstractMachine& abstract_machine() { return m_abstract_machine; } + +private: + HashMap> m_function_instances; + Vector> m_compiled_modules; + Wasm::AbstractMachine m_abstract_machine; }; -extern Vector> s_compiled_modules; -extern Vector> s_instantiated_modules; -extern Vector s_module_caches; -extern GlobalModuleCache s_global_cache; -extern Wasm::AbstractMachine s_abstract_machine; +WebAssemblyCache& get_cache(JS::Realm&); + +JS::ThrowCompletionOr> instantiate_module(JS::VM&, Wasm::Module const&); +JS::ThrowCompletionOr> parse_module(JS::VM&, JS::Object* buffer); +JS::NativeFunction* create_native_function(JS::VM&, Wasm::FunctionAddress address, ByteString const& name); +JS::ThrowCompletionOr to_webassembly_value(JS::VM&, JS::Value value, Wasm::ValueType const& type); +JS::Value to_js_value(JS::VM&, Wasm::Value& wasm_value); + +extern HashMap, WebAssemblyCache> s_caches; }