LibWeb: Cache the WebAssembly objects that we hand out to JS

The spec requires this behaviour, and it's generally faster to do this
instead of re-resolving and re-creating the instances anyway.
This commit is contained in:
Ali Mohammad Pur 2021-06-21 04:12:15 +04:30 committed by Ali Mohammad Pur
parent a256997064
commit e523e530fc
Notes: sideshowbarker 2024-07-18 11:57:13 +09:00
2 changed files with 52 additions and 7 deletions

View File

@ -35,8 +35,24 @@ void WebAssemblyObject::initialize(JS::GlobalObject& global_object)
NonnullOwnPtrVector<WebAssemblyObject::CompiledWebAssemblyModule> WebAssemblyObject::s_compiled_modules;
NonnullOwnPtrVector<Wasm::ModuleInstance> WebAssemblyObject::s_instantiated_modules;
Vector<WebAssemblyObject::ModuleCache> WebAssemblyObject::s_module_caches;
WebAssemblyObject::GlobalModuleCache WebAssemblyObject::s_global_cache;
Wasm::AbstractMachine WebAssemblyObject::s_abstract_machine;
void WebAssemblyObject::visit_edges(Visitor& visitor)
{
Base::visit_edges(visitor);
for (auto& entry : s_global_cache.function_instances)
visitor.visit(entry.value);
for (auto& module_cache : 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);
}
}
JS_DEFINE_NATIVE_FUNCTION(WebAssemblyObject::validate)
{
// FIXME: Implement this once module validation is implemented in LibWasm.
@ -231,6 +247,7 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyObject::instantiate)
}
s_instantiated_modules.append(instance_result.release_value());
s_module_caches.empend();
promise->fulfill(vm.heap().allocate<WebAssemblyInstanceObject>(global_object, global_object, s_instantiated_modules.size() - 1));
return promise;
}
@ -318,8 +335,10 @@ JS::NativeFunction* create_native_function(Wasm::FunctionAddress address, String
{
Optional<Wasm::FunctionType> type;
WebAssemblyObject::s_abstract_machine.store().get(address)->visit([&](const auto& value) { type = value.type(); });
// FIXME: Cache these.
return JS::NativeFunction::create(
if (auto entry = WebAssemblyObject::s_global_cache.function_instances.get(address); entry.has_value())
return *entry;
auto function = JS::NativeFunction::create(
global_object,
name,
[address, type = type.release_value()](JS::VM& vm, JS::GlobalObject& global_object) -> JS::Value {
@ -355,6 +374,9 @@ JS::NativeFunction* create_native_function(Wasm::FunctionAddress address, String
return JS::Array::create_from(global_object, result_values);
});
WebAssemblyObject::s_global_cache.function_instances.set(address, function);
return function;
}
void WebAssemblyInstanceObject::initialize(JS::GlobalObject& global_object)
@ -364,16 +386,24 @@ void WebAssemblyInstanceObject::initialize(JS::GlobalObject& global_object)
VERIFY(!m_exports_object);
m_exports_object = JS::Object::create(global_object, nullptr);
auto& instance = this->instance();
auto& cache = this->cache();
for (auto& export_ : instance.exports()) {
export_.value().visit(
[&](const Wasm::FunctionAddress& address) {
auto function = create_native_function(address, export_.name(), global_object);
m_exports_object->define_property(export_.name(), function);
auto object = cache.function_instances.get(address);
if (!object.has_value()) {
object = create_native_function(address, export_.name(), global_object);
cache.function_instances.set(address, *object);
}
m_exports_object->define_property(export_.name(), *object);
},
[&](const Wasm::MemoryAddress& address) {
// FIXME: Cache this.
auto memory = heap().allocate<WebAssemblyMemoryObject>(global_object, global_object, address);
m_exports_object->define_property(export_.name(), memory);
auto object = cache.memory_instances.get(address);
if (!object.has_value()) {
object = heap().allocate<WebAssemblyMemoryObject>(global_object, global_object, address);
cache.memory_instances.set(address, *object);
}
m_exports_object->define_property(export_.name(), *object);
},
[&](const auto&) {
// FIXME: Implement other exports!

View File

@ -13,6 +13,8 @@
namespace Web::Bindings {
class WebAssemblyMemoryObject;
class WebAssemblyObject final : public JS::Object {
JS_OBJECT(WebAssemblyObject, JS::Object);
@ -21,6 +23,8 @@ public:
virtual void initialize(JS::GlobalObject&) override;
virtual ~WebAssemblyObject() override = default;
virtual void visit_edges(Cell::Visitor&) override;
struct CompiledWebAssemblyModule {
explicit CompiledWebAssemblyModule(Wasm::Module&& module)
: module(move(module))
@ -34,8 +38,18 @@ public:
// 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<Wasm::FunctionAddress, JS::Function*> function_instances;
HashMap<Wasm::MemoryAddress, WebAssemblyMemoryObject*> memory_instances;
};
struct GlobalModuleCache {
HashMap<Wasm::FunctionAddress, JS::NativeFunction*> function_instances;
};
static NonnullOwnPtrVector<CompiledWebAssemblyModule> s_compiled_modules;
static NonnullOwnPtrVector<Wasm::ModuleInstance> s_instantiated_modules;
static Vector<ModuleCache> s_module_caches;
static GlobalModuleCache s_global_cache;
static Wasm::AbstractMachine s_abstract_machine;
@ -69,6 +83,7 @@ public:
size_t index() const { return m_index; }
Wasm::ModuleInstance& instance() const { return WebAssemblyObject::s_instantiated_modules.at(m_index); }
auto& cache() { return WebAssemblyObject::s_module_caches.at(m_index); }
void visit_edges(Cell::Visitor&) override;