summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibWeb/WebAssembly
diff options
context:
space:
mode:
authorTimothy Flynn <trflynn89@pm.me>2023-03-16 14:11:21 -0400
committerLinus Groh <mail@linusgroh.de>2023-03-16 19:39:17 +0000
commit8bba53bdff1ae5d0e3a635360835f5043b9e2658 (patch)
tree0634332ee600774b9a79b7d939d7197748e76f48 /Userland/Libraries/LibWeb/WebAssembly
parent57646c86ef02e420d3874616429f4ccf790de355 (diff)
downloadserenity-8bba53bdff1ae5d0e3a635360835f5043b9e2658.zip
LibWeb: Remove remaining WebAssemblyObject data to the namespace file
Diffstat (limited to 'Userland/Libraries/LibWeb/WebAssembly')
-rw-r--r--Userland/Libraries/LibWeb/WebAssembly/Instance.cpp10
-rw-r--r--Userland/Libraries/LibWeb/WebAssembly/Memory.cpp8
-rw-r--r--Userland/Libraries/LibWeb/WebAssembly/Module.cpp6
-rw-r--r--Userland/Libraries/LibWeb/WebAssembly/Table.cpp18
-rw-r--r--Userland/Libraries/LibWeb/WebAssembly/WebAssembly.cpp337
-rw-r--r--Userland/Libraries/LibWeb/WebAssembly/WebAssembly.h39
-rw-r--r--Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObject.cpp328
-rw-r--r--Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObject.h54
8 files changed, 384 insertions, 416 deletions
diff --git a/Userland/Libraries/LibWeb/WebAssembly/Instance.cpp b/Userland/Libraries/LibWeb/WebAssembly/Instance.cpp
index bdc4828b59..87d4da7151 100644
--- a/Userland/Libraries/LibWeb/WebAssembly/Instance.cpp
+++ b/Userland/Libraries/LibWeb/WebAssembly/Instance.cpp
@@ -16,7 +16,7 @@
#include <LibWeb/WebAssembly/Memory.h>
#include <LibWeb/WebAssembly/Module.h>
#include <LibWeb/WebAssembly/Table.h>
-#include <LibWeb/WebAssembly/WebAssemblyObject.h>
+#include <LibWeb/WebAssembly/WebAssembly.h>
namespace Web::WebAssembly {
@@ -27,7 +27,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<Instance>> Instance::construct_impl(JS::Rea
auto& vm = realm.vm();
- auto index = TRY(Bindings::WebAssemblyObject::instantiate_module(vm, module.module()));
+ auto index = TRY(Detail::instantiate_module(vm, module.module()));
return MUST_OR_THROW_OOM(vm.heap().allocate<Instance>(realm, realm, index));
}
@@ -45,15 +45,15 @@ JS::ThrowCompletionOr<void> Instance::initialize(JS::Realm& realm)
MUST_OR_THROW_OOM(Base::initialize(realm));
set_prototype(&Bindings::ensure_web_prototype<Bindings::InstancePrototype>(realm, "WebAssembly.Instance"sv));
- auto& instance = *Bindings::WebAssemblyObject::s_instantiated_modules[m_index];
- auto& cache = Bindings::WebAssemblyObject::s_module_caches.at(m_index);
+ auto& instance = *Detail::s_instantiated_modules[m_index];
+ auto& cache = Detail::s_module_caches.at(m_index);
for (auto& export_ : instance.exports()) {
TRY(export_.value().visit(
[&](Wasm::FunctionAddress const& address) -> JS::ThrowCompletionOr<void> {
Optional<JS::GCPtr<JS::FunctionObject>> object = cache.function_instances.get(address);
if (!object.has_value()) {
- object = Bindings::create_native_function(vm, address, export_.name());
+ object = Detail::create_native_function(vm, address, export_.name());
cache.function_instances.set(address, *object);
}
diff --git a/Userland/Libraries/LibWeb/WebAssembly/Memory.cpp b/Userland/Libraries/LibWeb/WebAssembly/Memory.cpp
index a23fb0c95f..a07a1dc244 100644
--- a/Userland/Libraries/LibWeb/WebAssembly/Memory.cpp
+++ b/Userland/Libraries/LibWeb/WebAssembly/Memory.cpp
@@ -10,7 +10,7 @@
#include <LibWasm/Types.h>
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/WebAssembly/Memory.h>
-#include <LibWeb/WebAssembly/WebAssemblyObject.h>
+#include <LibWeb/WebAssembly/WebAssembly.h>
namespace Web::WebAssembly {
@@ -21,7 +21,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<Memory>> Memory::construct_impl(JS::Realm&
Wasm::Limits limits { descriptor.initial, move(descriptor.maximum) };
Wasm::MemoryType memory_type { move(limits) };
- auto address = Bindings::WebAssemblyObject::s_abstract_machine.store().allocate(memory_type);
+ auto address = Detail::s_abstract_machine.store().allocate(memory_type);
if (!address.has_value())
return vm.throw_completion<JS::TypeError>("Wasm Memory allocation failed"sv);
@@ -47,7 +47,7 @@ WebIDL::ExceptionOr<u32> Memory::grow(u32 delta)
{
auto& vm = this->vm();
- auto* memory = Bindings::WebAssemblyObject::s_abstract_machine.store().get(address());
+ auto* memory = Detail::s_abstract_machine.store().get(address());
if (!memory)
return vm.throw_completion<JS::RangeError>("Could not find the memory instance to grow"sv);
@@ -64,7 +64,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::ArrayBuffer>> Memory::buffer() const
auto& vm = this->vm();
auto& realm = *vm.current_realm();
- auto* memory = Bindings::WebAssemblyObject::s_abstract_machine.store().get(address());
+ auto* memory = Detail::s_abstract_machine.store().get(address());
if (!memory)
return vm.throw_completion<JS::RangeError>("Could not find the memory instance"sv);
diff --git a/Userland/Libraries/LibWeb/WebAssembly/Module.cpp b/Userland/Libraries/LibWeb/WebAssembly/Module.cpp
index efbafa5961..d02d945f78 100644
--- a/Userland/Libraries/LibWeb/WebAssembly/Module.cpp
+++ b/Userland/Libraries/LibWeb/WebAssembly/Module.cpp
@@ -10,7 +10,7 @@
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/Bindings/ModulePrototype.h>
#include <LibWeb/WebAssembly/Module.h>
-#include <LibWeb/WebAssembly/WebAssemblyObject.h>
+#include <LibWeb/WebAssembly/WebAssembly.h>
namespace Web::WebAssembly {
@@ -18,7 +18,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<Module>> Module::construct_impl(JS::Realm&
{
auto& vm = realm.vm();
- auto index = TRY(Bindings::parse_module(vm, bytes.cell()));
+ auto index = TRY(Detail::parse_module(vm, bytes.cell()));
return MUST_OR_THROW_OOM(vm.heap().allocate<Module>(realm, realm, index));
}
@@ -38,7 +38,7 @@ JS::ThrowCompletionOr<void> Module::initialize(JS::Realm& realm)
Wasm::Module const& Module::module() const
{
- return Bindings::WebAssemblyObject::s_compiled_modules.at(index())->module;
+ return Detail::s_compiled_modules.at(index())->module;
}
}
diff --git a/Userland/Libraries/LibWeb/WebAssembly/Table.cpp b/Userland/Libraries/LibWeb/WebAssembly/Table.cpp
index 0a7c3b6c45..5f86a5deff 100644
--- a/Userland/Libraries/LibWeb/WebAssembly/Table.cpp
+++ b/Userland/Libraries/LibWeb/WebAssembly/Table.cpp
@@ -10,7 +10,7 @@
#include <LibWasm/Types.h>
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/WebAssembly/Table.h>
-#include <LibWeb/WebAssembly/WebAssemblyObject.h>
+#include <LibWeb/WebAssembly/WebAssembly.h>
namespace Web::WebAssembly {
@@ -30,7 +30,7 @@ static JS::ThrowCompletionOr<Wasm::Value> value_to_reference(JS::VM& vm, JS::Val
{
if (value.is_undefined())
return Wasm::Value(reference_type, 0ull);
- return Bindings::to_webassembly_value(vm, value, reference_type);
+ return Detail::to_webassembly_value(vm, value, reference_type);
}
WebIDL::ExceptionOr<JS::NonnullGCPtr<Table>> Table::construct_impl(JS::Realm& realm, TableDescriptor& descriptor, JS::Value value)
@@ -43,12 +43,12 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<Table>> Table::construct_impl(JS::Realm& re
Wasm::Limits limits { descriptor.initial, move(descriptor.maximum) };
Wasm::TableType table_type { reference_type, move(limits) };
- auto address = Bindings::WebAssemblyObject::s_abstract_machine.store().allocate(table_type);
+ auto address = Detail::s_abstract_machine.store().allocate(table_type);
if (!address.has_value())
return vm.throw_completion<JS::TypeError>("Wasm Table allocation failed"sv);
auto const& reference = reference_value.value().get<Wasm::Reference>();
- auto& table = *Bindings::WebAssemblyObject::s_abstract_machine.store().get(*address);
+ auto& table = *Detail::s_abstract_machine.store().get(*address);
for (auto& element : table.elements())
element = reference;
@@ -74,7 +74,7 @@ WebIDL::ExceptionOr<u32> Table::grow(u32 delta, JS::Value value)
{
auto& vm = this->vm();
- auto* table = Bindings::WebAssemblyObject::s_abstract_machine.store().get(address());
+ auto* table = Detail::s_abstract_machine.store().get(address());
if (!table)
return vm.throw_completion<JS::RangeError>("Could not find the memory table to grow"sv);
@@ -94,7 +94,7 @@ WebIDL::ExceptionOr<JS::Value> Table::get(u32 index) const
{
auto& vm = this->vm();
- auto* table = Bindings::WebAssemblyObject::s_abstract_machine.store().get(address());
+ auto* table = Detail::s_abstract_machine.store().get(address());
if (!table)
return vm.throw_completion<JS::RangeError>("Could not find the memory table"sv);
@@ -106,7 +106,7 @@ WebIDL::ExceptionOr<JS::Value> Table::get(u32 index) const
return JS::js_undefined();
Wasm::Value wasm_value { ref.value() };
- return Bindings::to_js_value(vm, wasm_value);
+ return Detail::to_js_value(vm, wasm_value);
}
// https://webassembly.github.io/spec/js-api/#dom-table-set
@@ -114,7 +114,7 @@ WebIDL::ExceptionOr<void> Table::set(u32 index, JS::Value value)
{
auto& vm = this->vm();
- auto* table = Bindings::WebAssemblyObject::s_abstract_machine.store().get(address());
+ auto* table = Detail::s_abstract_machine.store().get(address());
if (!table)
return vm.throw_completion<JS::RangeError>("Could not find the memory table"sv);
@@ -134,7 +134,7 @@ WebIDL::ExceptionOr<u32> Table::length() const
{
auto& vm = this->vm();
- auto* table = Bindings::WebAssemblyObject::s_abstract_machine.store().get(address());
+ auto* table = Detail::s_abstract_machine.store().get(address());
if (!table)
return vm.throw_completion<JS::RangeError>("Could not find the memory table"sv);
diff --git a/Userland/Libraries/LibWeb/WebAssembly/WebAssembly.cpp b/Userland/Libraries/LibWeb/WebAssembly/WebAssembly.cpp
index f5445f08bd..4ca72447d4 100644
--- a/Userland/Libraries/LibWeb/WebAssembly/WebAssembly.cpp
+++ b/Userland/Libraries/LibWeb/WebAssembly/WebAssembly.cpp
@@ -5,28 +5,44 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
+#include <AK/MemoryStream.h>
#include <AK/ScopeGuard.h>
+#include <LibJS/Runtime/Array.h>
+#include <LibJS/Runtime/ArrayBuffer.h>
+#include <LibJS/Runtime/BigInt.h>
+#include <LibJS/Runtime/DataView.h>
+#include <LibJS/Runtime/IteratorOperations.h>
#include <LibJS/Runtime/NativeFunction.h>
#include <LibJS/Runtime/Object.h>
#include <LibJS/Runtime/Promise.h>
+#include <LibJS/Runtime/ThrowableStringBuilder.h>
+#include <LibJS/Runtime/TypedArray.h>
#include <LibJS/Runtime/VM.h>
-#include <LibWasm/AbstractMachine/AbstractMachine.h>
#include <LibWasm/AbstractMachine/Validator.h>
#include <LibWeb/WebAssembly/Instance.h>
#include <LibWeb/WebAssembly/Memory.h>
#include <LibWeb/WebAssembly/Module.h>
#include <LibWeb/WebAssembly/Table.h>
#include <LibWeb/WebAssembly/WebAssembly.h>
-#include <LibWeb/WebAssembly/WebAssemblyObject.h>
namespace Web::WebAssembly {
+namespace Detail {
+
+Vector<NonnullOwnPtr<CompiledWebAssemblyModule>> s_compiled_modules;
+Vector<NonnullOwnPtr<Wasm::ModuleInstance>> s_instantiated_modules;
+Vector<ModuleCache> s_module_caches;
+GlobalModuleCache s_global_cache;
+Wasm::AbstractMachine s_abstract_machine;
+
+}
+
void visit_edges(JS::Cell::Visitor& visitor)
{
- for (auto& entry : Bindings::WebAssemblyObject::s_global_cache.function_instances)
+ for (auto& entry : Detail::s_global_cache.function_instances)
visitor.visit(entry.value);
- for (auto& module_cache : Bindings::WebAssemblyObject::s_module_caches) {
+ 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)
@@ -43,17 +59,17 @@ bool validate(JS::VM& vm, JS::Handle<JS::Object>& 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 = Bindings::parse_module(vm, bytes.cell());
+ auto maybe_module = Detail::parse_module(vm, bytes.cell());
// 3. If module is error, return false.
if (maybe_module.is_error())
return false;
// Drop the module from the cache, we're never going to refer to it.
- ScopeGuard drop_from_cache { [&] { (void)Bindings::WebAssemblyObject::s_compiled_modules.take_last(); } };
+ 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 (Bindings::WebAssemblyObject::s_abstract_machine.validate(Bindings::WebAssemblyObject::s_compiled_modules[maybe_module.value()]->module).is_error())
+ if (Detail::s_abstract_machine.validate(Detail::s_compiled_modules[maybe_module.value()]->module).is_error())
return false;
// 4. Return true.
@@ -66,7 +82,7 @@ WebIDL::ExceptionOr<JS::Value> compile(JS::VM& vm, JS::Handle<JS::Object>& bytes
auto& realm = *vm.current_realm();
// FIXME: This shouldn't block!
- auto module = Bindings::parse_module(vm, bytes.cell());
+ auto module = Detail::parse_module(vm, bytes.cell());
auto promise = JS::Promise::create(realm);
if (module.is_error()) {
@@ -88,7 +104,7 @@ WebIDL::ExceptionOr<JS::Value> instantiate(JS::VM& vm, JS::Handle<JS::Object>& b
auto& realm = *vm.current_realm();
// FIXME: This shouldn't block!
- auto module = Bindings::parse_module(vm, bytes.cell());
+ auto module = Detail::parse_module(vm, bytes.cell());
auto promise = JS::Promise::create(realm);
if (module.is_error()) {
@@ -96,13 +112,13 @@ WebIDL::ExceptionOr<JS::Value> instantiate(JS::VM& vm, JS::Handle<JS::Object>& b
return promise;
}
- auto const& compiled_module = Bindings::WebAssemblyObject::s_compiled_modules.at(module.release_value())->module;
- auto result = Bindings::WebAssemblyObject::instantiate_module(vm, compiled_module);
+ auto const& compiled_module = Detail::s_compiled_modules.at(module.release_value())->module;
+ auto result = Detail::instantiate_module(vm, compiled_module);
if (result.is_error()) {
promise->reject(*result.release_error().value());
} else {
- auto module_object = MUST_OR_THROW_OOM(vm.heap().allocate<Module>(realm, realm, Bindings::WebAssemblyObject::s_compiled_modules.size() - 1));
+ auto module_object = MUST_OR_THROW_OOM(vm.heap().allocate<Module>(realm, realm, Detail::s_compiled_modules.size() - 1));
auto instance_object = MUST_OR_THROW_OOM(vm.heap().allocate<Instance>(realm, realm, result.release_value()));
auto object = JS::Object::create(realm, nullptr);
@@ -124,7 +140,7 @@ WebIDL::ExceptionOr<JS::Value> instantiate(JS::VM& vm, Module const& module_obje
auto promise = JS::Promise::create(realm);
auto const& compiled_module = module_object.module();
- auto result = Bindings::WebAssemblyObject::instantiate_module(vm, compiled_module);
+ auto result = Detail::instantiate_module(vm, compiled_module);
if (result.is_error()) {
promise->reject(*result.release_error().value());
@@ -136,4 +152,299 @@ WebIDL::ExceptionOr<JS::Value> instantiate(JS::VM& vm, Module const& module_obje
return promise;
}
+namespace Detail {
+
+JS::ThrowCompletionOr<size_t> instantiate_module(JS::VM& vm, Wasm::Module const& module)
+{
+ Wasm::Linker linker { module };
+ HashMap<Wasm::Linker::Name, Wasm::ExternValue> resolved_imports;
+ auto import_argument = vm.argument(1);
+ if (!import_argument.is_undefined()) {
+ auto* import_object = TRY(import_argument.to_object(vm));
+ dbgln("Trying to resolve stuff because import object was specified");
+ for (Wasm::Linker::Name const& import_name : linker.unresolved_imports()) {
+ dbgln("Trying to resolve {}::{}", import_name.module, import_name.name);
+ auto value_or_error = import_object->get(import_name.module);
+ if (value_or_error.is_error())
+ break;
+ auto value = value_or_error.release_value();
+ auto object_or_error = value.to_object(vm);
+ if (object_or_error.is_error())
+ break;
+ auto* object = object_or_error.release_value();
+ auto import_or_error = object->get(import_name.name);
+ if (import_or_error.is_error())
+ break;
+ auto import_ = import_or_error.release_value();
+ TRY(import_name.type.visit(
+ [&](Wasm::TypeIndex index) -> JS::ThrowCompletionOr<void> {
+ dbgln("Trying to resolve a function {}::{}, type index {}", import_name.module, import_name.name, index.value());
+ auto& type = module.type(index);
+ // FIXME: IsCallable()
+ if (!import_.is_function())
+ return {};
+ auto& function = import_.as_function();
+ // FIXME: If this is a function created by create_native_function(),
+ // just extract its address and resolve to that.
+ Wasm::HostFunction host_function {
+ [&](auto&, auto& arguments) -> Wasm::Result {
+ JS::MarkedVector<JS::Value> argument_values { vm.heap() };
+ for (auto& entry : arguments)
+ argument_values.append(to_js_value(vm, entry));
+
+ auto result = TRY(JS::call(vm, function, JS::js_undefined(), move(argument_values)));
+ if (type.results().is_empty())
+ return Wasm::Result { Vector<Wasm::Value> {} };
+
+ if (type.results().size() == 1)
+ return Wasm::Result { Vector<Wasm::Value> { TRY(to_webassembly_value(vm, result, type.results().first())) } };
+
+ auto method = TRY(result.get_method(vm, vm.names.iterator));
+ if (method == JS::js_undefined())
+ return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotIterable, TRY_OR_THROW_OOM(vm, result.to_string_without_side_effects()));
+
+ auto values = TRY(JS::iterable_to_list(vm, result, method));
+
+ if (values.size() != type.results().size())
+ return vm.throw_completion<JS::TypeError>(DeprecatedString::formatted("Invalid number of return values for multi-value wasm return of {} objects", type.results().size()));
+
+ Vector<Wasm::Value> wasm_values;
+ TRY_OR_THROW_OOM(vm, wasm_values.try_ensure_capacity(values.size()));
+
+ size_t i = 0;
+ for (auto& value : values)
+ wasm_values.append(TRY(to_webassembly_value(vm, value, type.results()[i++])));
+
+ return Wasm::Result { move(wasm_values) };
+ },
+ type
+ };
+ auto address = s_abstract_machine.store().allocate(move(host_function));
+ dbgln("Resolved to {}", address->value());
+ // FIXME: LinkError instead.
+ VERIFY(address.has_value());
+
+ resolved_imports.set(import_name, Wasm::ExternValue { Wasm::FunctionAddress { *address } });
+ return {};
+ },
+ [&](Wasm::GlobalType const& type) -> JS::ThrowCompletionOr<void> {
+ Optional<Wasm::GlobalAddress> address;
+ // https://webassembly.github.io/spec/js-api/#read-the-imports step 5.1
+ if (import_.is_number() || import_.is_bigint()) {
+ if (import_.is_number() && type.type().kind() == Wasm::ValueType::I64) {
+ // FIXME: Throw a LinkError instead.
+ return vm.throw_completion<JS::TypeError>("LinkError: Import resolution attempted to cast a Number to a BigInteger"sv);
+ }
+ if (import_.is_bigint() && type.type().kind() != Wasm::ValueType::I64) {
+ // FIXME: Throw a LinkError instead.
+ return vm.throw_completion<JS::TypeError>("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);
+ } else {
+ // FIXME: https://webassembly.github.io/spec/js-api/#read-the-imports step 5.2
+ // if v implements Global
+ // let globaladdr be v.[[Global]]
+
+ // FIXME: Throw a LinkError instead
+ return vm.throw_completion<JS::TypeError>("LinkError: Invalid value for global type"sv);
+ }
+
+ resolved_imports.set(import_name, Wasm::ExternValue { *address });
+ return {};
+ },
+ [&](Wasm::MemoryType const&) -> JS::ThrowCompletionOr<void> {
+ if (!import_.is_object() || !is<WebAssembly::Memory>(import_.as_object())) {
+ // FIXME: Throw a LinkError instead
+ return vm.throw_completion<JS::TypeError>("LinkError: Expected an instance of WebAssembly.Memory for a memory import"sv);
+ }
+ auto address = static_cast<WebAssembly::Memory const&>(import_.as_object()).address();
+ resolved_imports.set(import_name, Wasm::ExternValue { address });
+ return {};
+ },
+ [&](Wasm::TableType const&) -> JS::ThrowCompletionOr<void> {
+ if (!import_.is_object() || !is<WebAssembly::Table>(import_.as_object())) {
+ // FIXME: Throw a LinkError instead
+ return vm.throw_completion<JS::TypeError>("LinkError: Expected an instance of WebAssembly.Table for a table import"sv);
+ }
+ auto address = static_cast<WebAssembly::Table const&>(import_.as_object()).address();
+ resolved_imports.set(import_name, Wasm::ExternValue { address });
+ return {};
+ },
+ [&](auto const&) -> JS::ThrowCompletionOr<void> {
+ // FIXME: Implement these.
+ dbgln("Unimplemented import of non-function attempted");
+ return vm.throw_completion<JS::TypeError>("LinkError: Not Implemented"sv);
+ }));
+ }
+ }
+
+ linker.link(resolved_imports);
+ auto link_result = linker.finish();
+ if (link_result.is_error()) {
+ // FIXME: Throw a LinkError.
+ JS::ThrowableStringBuilder builder(vm);
+ MUST_OR_THROW_OOM(builder.append("LinkError: Missing "sv));
+ MUST_OR_THROW_OOM(builder.join(' ', link_result.error().missing_imports));
+ return vm.throw_completion<JS::TypeError>(MUST_OR_THROW_OOM(builder.to_string()));
+ }
+
+ auto instance_result = s_abstract_machine.instantiate(module, link_result.release_value());
+ if (instance_result.is_error()) {
+ // FIXME: Throw a LinkError instead.
+ return vm.throw_completion<JS::TypeError>(instance_result.error().error);
+ }
+
+ s_instantiated_modules.append(instance_result.release_value());
+ s_module_caches.empend();
+ return s_instantiated_modules.size() - 1;
+}
+
+JS::ThrowCompletionOr<size_t> parse_module(JS::VM& vm, JS::Object* buffer_object)
+{
+ ReadonlyBytes data;
+ if (is<JS::ArrayBuffer>(buffer_object)) {
+ auto& buffer = static_cast<JS::ArrayBuffer&>(*buffer_object);
+ data = buffer.buffer();
+ } else if (is<JS::TypedArrayBase>(buffer_object)) {
+ auto& buffer = static_cast<JS::TypedArrayBase&>(*buffer_object);
+ data = buffer.viewed_array_buffer()->buffer().span().slice(buffer.byte_offset(), buffer.byte_length());
+ } else if (is<JS::DataView>(buffer_object)) {
+ auto& buffer = static_cast<JS::DataView&>(*buffer_object);
+ data = buffer.viewed_array_buffer()->buffer().span().slice(buffer.byte_offset(), buffer.byte_length());
+ } else {
+ return vm.throw_completion<JS::TypeError>("Not a BufferSource"sv);
+ }
+ FixedMemoryStream stream { data };
+ auto module_result = Wasm::Module::parse(stream);
+ if (module_result.is_error()) {
+ // FIXME: Throw CompileError instead.
+ return vm.throw_completion<JS::TypeError>(Wasm::parse_error_to_deprecated_string(module_result.error()));
+ }
+
+ if (auto validation_result = s_abstract_machine.validate(module_result.value()); validation_result.is_error()) {
+ // FIXME: Throw CompileError instead.
+ return vm.throw_completion<JS::TypeError>(validation_result.error().error_string);
+ }
+
+ s_compiled_modules.append(make<CompiledWebAssemblyModule>(module_result.release_value()));
+ return s_compiled_modules.size() - 1;
+}
+
+JS::NativeFunction* create_native_function(JS::VM& vm, Wasm::FunctionAddress address, DeprecatedString const& name)
+{
+ auto& realm = *vm.current_realm();
+ Optional<Wasm::FunctionType> 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())
+ return *entry;
+
+ auto function = JS::NativeFunction::create(
+ realm,
+ name,
+ [address, type = type.release_value()](JS::VM& vm) -> JS::ThrowCompletionOr<JS::Value> {
+ auto& realm = *vm.current_realm();
+ Vector<Wasm::Value> values;
+ values.ensure_capacity(type.parameters().size());
+
+ // Grab as many values as needed and convert them.
+ size_t index = 0;
+ 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));
+ // FIXME: Use the convoluted mapping of errors defined in the spec.
+ if (result.is_trap())
+ return vm.throw_completion<JS::TypeError>(TRY_OR_THROW_OOM(vm, String::formatted("Wasm execution trapped (WIP): {}", result.trap().reason)));
+
+ if (result.values().is_empty())
+ return JS::js_undefined();
+
+ if (result.values().size() == 1)
+ return to_js_value(vm, result.values().first());
+
+ return JS::Value(JS::Array::create_from<Wasm::Value>(realm, result.values(), [&](Wasm::Value value) {
+ return to_js_value(vm, value);
+ }));
+ });
+
+ s_global_cache.function_instances.set(address, function);
+ return function;
+}
+
+JS::ThrowCompletionOr<Wasm::Value> to_webassembly_value(JS::VM& vm, JS::Value value, Wasm::ValueType const& type)
+{
+ static ::Crypto::SignedBigInteger two_64 = "1"_sbigint.shift_left(64);
+
+ switch (type.kind()) {
+ case Wasm::ValueType::I64: {
+ auto bigint = TRY(value.to_bigint(vm));
+ auto value = bigint->big_integer().divided_by(two_64).remainder;
+ VERIFY(value.unsigned_value().trimmed_length() <= 2);
+ i64 integer = static_cast<i64>(value.unsigned_value().to_u64());
+ if (value.is_negative())
+ integer = -integer;
+ return Wasm::Value { integer };
+ }
+ case Wasm::ValueType::I32: {
+ auto _i32 = TRY(value.to_i32(vm));
+ return Wasm::Value { static_cast<i32>(_i32) };
+ }
+ case Wasm::ValueType::F64: {
+ auto number = TRY(value.to_double(vm));
+ return Wasm::Value { static_cast<double>(number) };
+ }
+ case Wasm::ValueType::F32: {
+ auto number = TRY(value.to_double(vm));
+ return Wasm::Value { static_cast<float>(number) };
+ }
+ case Wasm::ValueType::FunctionReference:
+ case Wasm::ValueType::NullFunctionReference: {
+ if (value.is_null())
+ return Wasm::Value { Wasm::ValueType(Wasm::ValueType::NullExternReference), 0ull };
+
+ if (value.is_function()) {
+ auto& function = value.as_function();
+ for (auto& entry : s_global_cache.function_instances) {
+ if (entry.value == &function)
+ return Wasm::Value { Wasm::Reference { Wasm::Reference::Func { entry.key } } };
+ }
+ }
+
+ return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "Exported function");
+ }
+ case Wasm::ValueType::ExternReference:
+ case Wasm::ValueType::NullExternReference:
+ TODO();
+ }
+
+ VERIFY_NOT_REACHED();
+}
+
+JS::Value to_js_value(JS::VM& vm, Wasm::Value& wasm_value)
+{
+ auto& realm = *vm.current_realm();
+ switch (wasm_value.type().kind()) {
+ case Wasm::ValueType::I64:
+ return realm.heap().allocate<JS::BigInt>(realm, ::Crypto::SignedBigInteger { wasm_value.to<i64>().value() }).release_allocated_value_but_fixme_should_propagate_errors();
+ case Wasm::ValueType::I32:
+ return JS::Value(wasm_value.to<i32>().value());
+ case Wasm::ValueType::F64:
+ return JS::Value(wasm_value.to<double>().value());
+ case Wasm::ValueType::F32:
+ return JS::Value(static_cast<double>(wasm_value.to<float>().value()));
+ case Wasm::ValueType::FunctionReference:
+ // FIXME: What's the name of a function reference that isn't exported?
+ return create_native_function(vm, wasm_value.to<Wasm::Reference::Func>().value().address, "FIXME_IHaveNoIdeaWhatThisShouldBeCalled");
+ case Wasm::ValueType::NullFunctionReference:
+ return JS::js_null();
+ case Wasm::ValueType::ExternReference:
+ case Wasm::ValueType::NullExternReference:
+ TODO();
+ }
+ VERIFY_NOT_REACHED();
+}
+
+}
+
}
diff --git a/Userland/Libraries/LibWeb/WebAssembly/WebAssembly.h b/Userland/Libraries/LibWeb/WebAssembly/WebAssembly.h
index 782f1d73a2..d3ff02e7c7 100644
--- a/Userland/Libraries/LibWeb/WebAssembly/WebAssembly.h
+++ b/Userland/Libraries/LibWeb/WebAssembly/WebAssembly.h
@@ -10,7 +10,9 @@
#include <AK/Optional.h>
#include <LibJS/Forward.h>
#include <LibJS/Heap/Handle.h>
+#include <LibJS/Runtime/Completion.h>
#include <LibJS/Runtime/Value.h>
+#include <LibWasm/AbstractMachine/AbstractMachine.h>
#include <LibWeb/Bindings/ExceptionOrUtils.h>
#include <LibWeb/Forward.h>
@@ -24,4 +26,41 @@ WebIDL::ExceptionOr<JS::Value> compile(JS::VM&, JS::Handle<JS::Object>& bytes);
WebIDL::ExceptionOr<JS::Value> instantiate(JS::VM&, JS::Handle<JS::Object>& bytes, Optional<JS::Handle<JS::Object>>& import_object);
WebIDL::ExceptionOr<JS::Value> instantiate(JS::VM&, Module const& module_object, Optional<JS::Handle<JS::Object>>& import_object);
+namespace Detail {
+
+JS::ThrowCompletionOr<size_t> instantiate_module(JS::VM&, Wasm::Module const&);
+JS::ThrowCompletionOr<size_t> parse_module(JS::VM&, JS::Object* buffer);
+JS::NativeFunction* create_native_function(JS::VM&, Wasm::FunctionAddress address, DeprecatedString const& name);
+JS::ThrowCompletionOr<Wasm::Value> 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 {
+ explicit CompiledWebAssemblyModule(Wasm::Module&& module)
+ : module(move(module))
+ {
+ }
+
+ 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<Wasm::FunctionAddress, JS::GCPtr<JS::FunctionObject>> function_instances;
+ HashMap<Wasm::MemoryAddress, JS::GCPtr<WebAssembly::Memory>> memory_instances;
+ HashMap<Wasm::TableAddress, JS::GCPtr<WebAssembly::Table>> table_instances;
+};
+struct GlobalModuleCache {
+ HashMap<Wasm::FunctionAddress, JS::GCPtr<JS::NativeFunction>> function_instances;
+};
+
+extern Vector<NonnullOwnPtr<CompiledWebAssemblyModule>> s_compiled_modules;
+extern Vector<NonnullOwnPtr<Wasm::ModuleInstance>> s_instantiated_modules;
+extern Vector<ModuleCache> s_module_caches;
+extern GlobalModuleCache s_global_cache;
+extern Wasm::AbstractMachine s_abstract_machine;
+
+}
+
}
diff --git a/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObject.cpp b/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObject.cpp
deleted file mode 100644
index 135600f6c7..0000000000
--- a/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObject.cpp
+++ /dev/null
@@ -1,328 +0,0 @@
-/*
- * Copyright (c) 2021, Ali Mohammad Pur <mpfard@serenityos.org>
- *
- * SPDX-License-Identifier: BSD-2-Clause
- */
-
-#include <AK/MemoryStream.h>
-#include <AK/ScopeGuard.h>
-#include <LibJS/Runtime/Array.h>
-#include <LibJS/Runtime/ArrayBuffer.h>
-#include <LibJS/Runtime/BigInt.h>
-#include <LibJS/Runtime/DataView.h>
-#include <LibJS/Runtime/IteratorOperations.h>
-#include <LibJS/Runtime/ThrowableStringBuilder.h>
-#include <LibJS/Runtime/TypedArray.h>
-#include <LibWasm/AbstractMachine/Interpreter.h>
-#include <LibWasm/AbstractMachine/Validator.h>
-#include <LibWeb/Bindings/InstancePrototype.h>
-#include <LibWeb/Bindings/Intrinsics.h>
-#include <LibWeb/Bindings/MemoryPrototype.h>
-#include <LibWeb/Bindings/ModulePrototype.h>
-#include <LibWeb/Bindings/TablePrototype.h>
-#include <LibWeb/WebAssembly/Instance.h>
-#include <LibWeb/WebAssembly/Memory.h>
-#include <LibWeb/WebAssembly/Module.h>
-#include <LibWeb/WebAssembly/Table.h>
-#include <LibWeb/WebAssembly/WebAssemblyObject.h>
-
-namespace Web::Bindings {
-
-Vector<NonnullOwnPtr<WebAssemblyObject::CompiledWebAssemblyModule>> WebAssemblyObject::s_compiled_modules;
-Vector<NonnullOwnPtr<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;
-
-JS::ThrowCompletionOr<size_t> parse_module(JS::VM& vm, JS::Object* buffer_object)
-{
- ReadonlyBytes data;
- if (is<JS::ArrayBuffer>(buffer_object)) {
- auto& buffer = static_cast<JS::ArrayBuffer&>(*buffer_object);
- data = buffer.buffer();
- } else if (is<JS::TypedArrayBase>(buffer_object)) {
- auto& buffer = static_cast<JS::TypedArrayBase&>(*buffer_object);
- data = buffer.viewed_array_buffer()->buffer().span().slice(buffer.byte_offset(), buffer.byte_length());
- } else if (is<JS::DataView>(buffer_object)) {
- auto& buffer = static_cast<JS::DataView&>(*buffer_object);
- data = buffer.viewed_array_buffer()->buffer().span().slice(buffer.byte_offset(), buffer.byte_length());
- } else {
- return vm.throw_completion<JS::TypeError>("Not a BufferSource"sv);
- }
- FixedMemoryStream stream { data };
- auto module_result = Wasm::Module::parse(stream);
- if (module_result.is_error()) {
- // FIXME: Throw CompileError instead.
- return vm.throw_completion<JS::TypeError>(Wasm::parse_error_to_deprecated_string(module_result.error()));
- }
-
- if (auto validation_result = WebAssemblyObject::s_abstract_machine.validate(module_result.value()); validation_result.is_error()) {
- // FIXME: Throw CompileError instead.
- return vm.throw_completion<JS::TypeError>(validation_result.error().error_string);
- }
-
- WebAssemblyObject::s_compiled_modules.append(make<WebAssemblyObject::CompiledWebAssemblyModule>(module_result.release_value()));
- return WebAssemblyObject::s_compiled_modules.size() - 1;
-}
-
-JS::ThrowCompletionOr<size_t> WebAssemblyObject::instantiate_module(JS::VM& vm, Wasm::Module const& module)
-{
- Wasm::Linker linker { module };
- HashMap<Wasm::Linker::Name, Wasm::ExternValue> resolved_imports;
- auto import_argument = vm.argument(1);
- if (!import_argument.is_undefined()) {
- auto* import_object = TRY(import_argument.to_object(vm));
- dbgln("Trying to resolve stuff because import object was specified");
- for (Wasm::Linker::Name const& import_name : linker.unresolved_imports()) {
- dbgln("Trying to resolve {}::{}", import_name.module, import_name.name);
- auto value_or_error = import_object->get(import_name.module);
- if (value_or_error.is_error())
- break;
- auto value = value_or_error.release_value();
- auto object_or_error = value.to_object(vm);
- if (object_or_error.is_error())
- break;
- auto* object = object_or_error.release_value();
- auto import_or_error = object->get(import_name.name);
- if (import_or_error.is_error())
- break;
- auto import_ = import_or_error.release_value();
- TRY(import_name.type.visit(
- [&](Wasm::TypeIndex index) -> JS::ThrowCompletionOr<void> {
- dbgln("Trying to resolve a function {}::{}, type index {}", import_name.module, import_name.name, index.value());
- auto& type = module.type(index);
- // FIXME: IsCallable()
- if (!import_.is_function())
- return {};
- auto& function = import_.as_function();
- // FIXME: If this is a function created by create_native_function(),
- // just extract its address and resolve to that.
- Wasm::HostFunction host_function {
- [&](auto&, auto& arguments) -> Wasm::Result {
- JS::MarkedVector<JS::Value> argument_values { vm.heap() };
- for (auto& entry : arguments)
- argument_values.append(to_js_value(vm, entry));
-
- auto result = TRY(JS::call(vm, function, JS::js_undefined(), move(argument_values)));
- if (type.results().is_empty())
- return Wasm::Result { Vector<Wasm::Value> {} };
-
- if (type.results().size() == 1)
- return Wasm::Result { Vector<Wasm::Value> { TRY(to_webassembly_value(vm, result, type.results().first())) } };
-
- auto method = TRY(result.get_method(vm, vm.names.iterator));
- if (method == JS::js_undefined())
- return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotIterable, TRY_OR_THROW_OOM(vm, result.to_string_without_side_effects()));
-
- auto values = TRY(JS::iterable_to_list(vm, result, method));
-
- if (values.size() != type.results().size())
- return vm.throw_completion<JS::TypeError>(DeprecatedString::formatted("Invalid number of return values for multi-value wasm return of {} objects", type.results().size()));
-
- Vector<Wasm::Value> wasm_values;
- TRY_OR_THROW_OOM(vm, wasm_values.try_ensure_capacity(values.size()));
-
- size_t i = 0;
- for (auto& value : values)
- wasm_values.append(TRY(to_webassembly_value(vm, value, type.results()[i++])));
-
- return Wasm::Result { move(wasm_values) };
- },
- type
- };
- auto address = s_abstract_machine.store().allocate(move(host_function));
- dbgln("Resolved to {}", address->value());
- // FIXME: LinkError instead.
- VERIFY(address.has_value());
-
- resolved_imports.set(import_name, Wasm::ExternValue { Wasm::FunctionAddress { *address } });
- return {};
- },
- [&](Wasm::GlobalType const& type) -> JS::ThrowCompletionOr<void> {
- Optional<Wasm::GlobalAddress> address;
- // https://webassembly.github.io/spec/js-api/#read-the-imports step 5.1
- if (import_.is_number() || import_.is_bigint()) {
- if (import_.is_number() && type.type().kind() == Wasm::ValueType::I64) {
- // FIXME: Throw a LinkError instead.
- return vm.throw_completion<JS::TypeError>("LinkError: Import resolution attempted to cast a Number to a BigInteger"sv);
- }
- if (import_.is_bigint() && type.type().kind() != Wasm::ValueType::I64) {
- // FIXME: Throw a LinkError instead.
- return vm.throw_completion<JS::TypeError>("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);
- } else {
- // FIXME: https://webassembly.github.io/spec/js-api/#read-the-imports step 5.2
- // if v implements Global
- // let globaladdr be v.[[Global]]
-
- // FIXME: Throw a LinkError instead
- return vm.throw_completion<JS::TypeError>("LinkError: Invalid value for global type"sv);
- }
-
- resolved_imports.set(import_name, Wasm::ExternValue { *address });
- return {};
- },
- [&](Wasm::MemoryType const&) -> JS::ThrowCompletionOr<void> {
- if (!import_.is_object() || !is<WebAssembly::Memory>(import_.as_object())) {
- // FIXME: Throw a LinkError instead
- return vm.throw_completion<JS::TypeError>("LinkError: Expected an instance of WebAssembly.Memory for a memory import"sv);
- }
- auto address = static_cast<WebAssembly::Memory const&>(import_.as_object()).address();
- resolved_imports.set(import_name, Wasm::ExternValue { address });
- return {};
- },
- [&](Wasm::TableType const&) -> JS::ThrowCompletionOr<void> {
- if (!import_.is_object() || !is<WebAssembly::Table>(import_.as_object())) {
- // FIXME: Throw a LinkError instead
- return vm.throw_completion<JS::TypeError>("LinkError: Expected an instance of WebAssembly.Table for a table import"sv);
- }
- auto address = static_cast<WebAssembly::Table const&>(import_.as_object()).address();
- resolved_imports.set(import_name, Wasm::ExternValue { address });
- return {};
- },
- [&](auto const&) -> JS::ThrowCompletionOr<void> {
- // FIXME: Implement these.
- dbgln("Unimplemented import of non-function attempted");
- return vm.throw_completion<JS::TypeError>("LinkError: Not Implemented"sv);
- }));
- }
- }
-
- linker.link(resolved_imports);
- auto link_result = linker.finish();
- if (link_result.is_error()) {
- // FIXME: Throw a LinkError.
- JS::ThrowableStringBuilder builder(vm);
- MUST_OR_THROW_OOM(builder.append("LinkError: Missing "sv));
- MUST_OR_THROW_OOM(builder.join(' ', link_result.error().missing_imports));
- return vm.throw_completion<JS::TypeError>(MUST_OR_THROW_OOM(builder.to_string()));
- }
-
- auto instance_result = s_abstract_machine.instantiate(module, link_result.release_value());
- if (instance_result.is_error()) {
- // FIXME: Throw a LinkError instead.
- return vm.throw_completion<JS::TypeError>(instance_result.error().error);
- }
-
- s_instantiated_modules.append(instance_result.release_value());
- s_module_caches.empend();
- return s_instantiated_modules.size() - 1;
-}
-
-JS::Value to_js_value(JS::VM& vm, Wasm::Value& wasm_value)
-{
- auto& realm = *vm.current_realm();
- switch (wasm_value.type().kind()) {
- case Wasm::ValueType::I64:
- return realm.heap().allocate<JS::BigInt>(realm, ::Crypto::SignedBigInteger { wasm_value.to<i64>().value() }).release_allocated_value_but_fixme_should_propagate_errors();
- case Wasm::ValueType::I32:
- return JS::Value(wasm_value.to<i32>().value());
- case Wasm::ValueType::F64:
- return JS::Value(wasm_value.to<double>().value());
- case Wasm::ValueType::F32:
- return JS::Value(static_cast<double>(wasm_value.to<float>().value()));
- case Wasm::ValueType::FunctionReference:
- // FIXME: What's the name of a function reference that isn't exported?
- return create_native_function(vm, wasm_value.to<Wasm::Reference::Func>().value().address, "FIXME_IHaveNoIdeaWhatThisShouldBeCalled");
- case Wasm::ValueType::NullFunctionReference:
- return JS::js_null();
- case Wasm::ValueType::ExternReference:
- case Wasm::ValueType::NullExternReference:
- TODO();
- }
- VERIFY_NOT_REACHED();
-}
-
-JS::ThrowCompletionOr<Wasm::Value> to_webassembly_value(JS::VM& vm, JS::Value value, Wasm::ValueType const& type)
-{
- static ::Crypto::SignedBigInteger two_64 = "1"_sbigint.shift_left(64);
-
- switch (type.kind()) {
- case Wasm::ValueType::I64: {
- auto bigint = TRY(value.to_bigint(vm));
- auto value = bigint->big_integer().divided_by(two_64).remainder;
- VERIFY(value.unsigned_value().trimmed_length() <= 2);
- i64 integer = static_cast<i64>(value.unsigned_value().to_u64());
- if (value.is_negative())
- integer = -integer;
- return Wasm::Value { integer };
- }
- case Wasm::ValueType::I32: {
- auto _i32 = TRY(value.to_i32(vm));
- return Wasm::Value { static_cast<i32>(_i32) };
- }
- case Wasm::ValueType::F64: {
- auto number = TRY(value.to_double(vm));
- return Wasm::Value { static_cast<double>(number) };
- }
- case Wasm::ValueType::F32: {
- auto number = TRY(value.to_double(vm));
- return Wasm::Value { static_cast<float>(number) };
- }
- case Wasm::ValueType::FunctionReference:
- case Wasm::ValueType::NullFunctionReference: {
- if (value.is_null())
- return Wasm::Value { Wasm::ValueType(Wasm::ValueType::NullExternReference), 0ull };
-
- if (value.is_function()) {
- auto& function = value.as_function();
- for (auto& entry : WebAssemblyObject::s_global_cache.function_instances) {
- if (entry.value == &function)
- return Wasm::Value { Wasm::Reference { Wasm::Reference::Func { entry.key } } };
- }
- }
-
- return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "Exported function");
- }
- case Wasm::ValueType::ExternReference:
- case Wasm::ValueType::NullExternReference:
- TODO();
- }
-
- VERIFY_NOT_REACHED();
-}
-
-JS::NativeFunction* create_native_function(JS::VM& vm, Wasm::FunctionAddress address, DeprecatedString const& name)
-{
- auto& realm = *vm.current_realm();
- Optional<Wasm::FunctionType> type;
- WebAssemblyObject::s_abstract_machine.store().get(address)->visit([&](auto const& value) { type = value.type(); });
- if (auto entry = WebAssemblyObject::s_global_cache.function_instances.get(address); entry.has_value())
- return *entry;
-
- auto function = JS::NativeFunction::create(
- realm,
- name,
- [address, type = type.release_value()](JS::VM& vm) -> JS::ThrowCompletionOr<JS::Value> {
- auto& realm = *vm.current_realm();
- Vector<Wasm::Value> values;
- values.ensure_capacity(type.parameters().size());
-
- // Grab as many values as needed and convert them.
- size_t index = 0;
- for (auto& type : type.parameters())
- values.append(TRY(to_webassembly_value(vm, vm.argument(index++), type)));
-
- auto result = WebAssemblyObject::s_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<JS::TypeError>(TRY_OR_THROW_OOM(vm, String::formatted("Wasm execution trapped (WIP): {}", result.trap().reason)));
-
- if (result.values().is_empty())
- return JS::js_undefined();
-
- if (result.values().size() == 1)
- return to_js_value(vm, result.values().first());
-
- return JS::Value(JS::Array::create_from<Wasm::Value>(realm, result.values(), [&](Wasm::Value value) {
- return to_js_value(vm, value);
- }));
- });
-
- WebAssemblyObject::s_global_cache.function_instances.set(address, function);
- return function;
-}
-
-}
diff --git a/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObject.h b/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObject.h
deleted file mode 100644
index 9f627fbbe6..0000000000
--- a/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObject.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (c) 2021, Ali Mohammad Pur <mpfard@serenityos.org>
- *
- * SPDX-License-Identifier: BSD-2-Clause
- */
-
-#pragma once
-
-#include <LibJS/Runtime/Object.h>
-#include <LibWasm/AbstractMachine/AbstractMachine.h>
-#include <LibWeb/Forward.h>
-
-namespace Web::Bindings {
-
-JS::ThrowCompletionOr<size_t> parse_module(JS::VM&, JS::Object* buffer);
-JS::NativeFunction* create_native_function(JS::VM&, Wasm::FunctionAddress address, DeprecatedString const& name);
-JS::Value to_js_value(JS::VM&, Wasm::Value& wasm_value);
-JS::ThrowCompletionOr<Wasm::Value> to_webassembly_value(JS::VM&, JS::Value value, Wasm::ValueType const& type);
-
-class WebAssemblyObject final {
-public:
- static JS::ThrowCompletionOr<size_t> instantiate_module(JS::VM&, Wasm::Module const&);
-
- struct CompiledWebAssemblyModule {
- explicit CompiledWebAssemblyModule(Wasm::Module&& module)
- : module(move(module))
- {
- }
-
- 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<Wasm::FunctionAddress, JS::GCPtr<JS::FunctionObject>> function_instances;
- HashMap<Wasm::MemoryAddress, JS::GCPtr<WebAssembly::Memory>> memory_instances;
- HashMap<Wasm::TableAddress, JS::GCPtr<WebAssembly::Table>> table_instances;
- };
- struct GlobalModuleCache {
- HashMap<Wasm::FunctionAddress, JS::GCPtr<JS::NativeFunction>> function_instances;
- };
-
- static Vector<NonnullOwnPtr<CompiledWebAssemblyModule>> s_compiled_modules;
- static Vector<NonnullOwnPtr<Wasm::ModuleInstance>> s_instantiated_modules;
- static Vector<ModuleCache> s_module_caches;
- static GlobalModuleCache s_global_cache;
-
- static Wasm::AbstractMachine s_abstract_machine;
-};
-
-}