diff --git a/Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.cpp b/Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.cpp index d270a0e1b5..f6ebbc0ed6 100644 --- a/Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.cpp +++ b/Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.cpp @@ -21,10 +21,10 @@ Optional Store::allocate(ModuleInstance& module, const Module:: return address; } -Optional Store::allocate(const HostFunction& function) +Optional Store::allocate(HostFunction&& function) { FunctionAddress address { m_functions.size() }; - m_functions.empend(HostFunction { function }); + m_functions.empend(HostFunction { move(function) }); return address; } diff --git a/Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.h b/Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.h index ca09458ed2..01ffc09cb8 100644 --- a/Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.h +++ b/Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.h @@ -6,6 +6,7 @@ #pragma once +#include #include #include #include @@ -14,6 +15,8 @@ namespace Wasm { +class Configuration; + struct InstantiationError { String error { "Unknown error" }; }; @@ -235,17 +238,17 @@ private: class HostFunction { public: - explicit HostFunction(FlatPtr ptr, const FunctionType& type) - : m_ptr(ptr) + explicit HostFunction(AK::Function&)> function, const FunctionType& type) + : m_function(move(function)) , m_type(type) { } - auto ptr() const { return m_ptr; } + auto& function() { return m_function; } auto& type() const { return m_type; } private: - FlatPtr m_ptr { 0 }; + AK::Function&)> m_function; FunctionType m_type; }; @@ -352,7 +355,7 @@ public: Store() = default; Optional allocate(ModuleInstance& module, const Module::Function& function); - Optional allocate(const HostFunction&); + Optional allocate(HostFunction&&); Optional allocate(const TableType&); Optional allocate(const MemoryType&); Optional allocate(const GlobalType&, Value); @@ -464,6 +467,12 @@ public: // Link a bunch of qualified values, also matches 'module name'. void link(const HashMap&); + auto& unresolved_imports() + { + populate(); + return m_unresolved_imports; + } + AK::Result, LinkError> finish(); private: diff --git a/Userland/Libraries/LibWasm/AbstractMachine/Configuration.cpp b/Userland/Libraries/LibWasm/AbstractMachine/Configuration.cpp index bc7e6c1f42..09894d3469 100644 --- a/Userland/Libraries/LibWasm/AbstractMachine/Configuration.cpp +++ b/Userland/Libraries/LibWasm/AbstractMachine/Configuration.cpp @@ -47,13 +47,7 @@ Result Configuration::call(FunctionAddress address, Vector arguments) // It better be a host function, else something is really wrong. auto& host_function = function->get(); - auto result = bit_cast(host_function.ptr())(m_store, arguments); - auto count = host_function.type().results().size(); - if (count == 0) - return Result { Vector {} }; - if (count == 1) - return Result { Vector { Value { host_function.type().results().first(), result } } }; - TODO(); + return host_function.function()(*this, arguments); } Result Configuration::execute() diff --git a/Userland/Libraries/LibWasm/AbstractMachine/Configuration.h b/Userland/Libraries/LibWasm/AbstractMachine/Configuration.h index 56caf7423b..f20c006438 100644 --- a/Userland/Libraries/LibWasm/AbstractMachine/Configuration.h +++ b/Userland/Libraries/LibWasm/AbstractMachine/Configuration.h @@ -10,8 +10,6 @@ namespace Wasm { -typedef u64 (*HostFunctionType)(Store&, Vector&); - class Configuration { public: explicit Configuration(Store& store) diff --git a/Userland/Libraries/LibWasm/Types.h b/Userland/Libraries/LibWasm/Types.h index 9906035715..9134af6272 100644 --- a/Userland/Libraries/LibWasm/Types.h +++ b/Userland/Libraries/LibWasm/Types.h @@ -998,6 +998,16 @@ public: auto& sections() const { return m_sections; } auto& functions() const { return m_functions; } + auto& type(TypeIndex index) const + { + const FunctionType* type = nullptr; + for_each_section_of_type([&](const TypeSection& section) { + type = §ion.types().at(index.value()); + }); + + VERIFY(type != nullptr); + return *type; + } template void for_each_section_of_type(Callback&& callback) const diff --git a/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObject.cpp b/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObject.cpp index bd7257c764..1fcf7ef73c 100644 --- a/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObject.cpp +++ b/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObject.cpp @@ -13,6 +13,10 @@ namespace Web::Bindings { +static JS::NativeFunction* create_native_function(Wasm::FunctionAddress address, String name, JS::GlobalObject& global_object); +static JS::Value to_js_value(Wasm::Value& wasm_value, JS::GlobalObject& global_object); +static Optional to_webassembly_value(JS::Value value, const Wasm::ValueType& type, JS::GlobalObject& global_object); + WebAssemblyObject::WebAssemblyObject(JS::GlobalObject& global_object) : Object(*global_object.object_prototype()) { @@ -89,16 +93,20 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyObject::instantiate) { // FIXME: This shouldn't block! auto buffer = vm.argument(0).to_object(global_object); - JS::Value rejection_value; - if (vm.exception()) { - rejection_value = vm.exception()->value(); - vm.clear_exception(); - } auto promise = JS::Promise::create(global_object); - if (!rejection_value.is_empty()) { - promise->reject(rejection_value); + auto take_exception_and_reject_if_needed = [&] { + if (vm.exception()) { + auto rejection_value = vm.exception()->value(); + vm.clear_exception(); + promise->reject(rejection_value); + return true; + } + + return false; + }; + + if (take_exception_and_reject_if_needed()) return promise; - } const Wasm::Module* module { nullptr }; if (is(buffer) || is(buffer)) { @@ -117,25 +125,81 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyObject::instantiate) } VERIFY(module); - HashMap import_values; + Wasm::Linker linker { *module }; + HashMap resolved_imports; auto import_argument = vm.argument(1); if (!import_argument.is_undefined()) { [[maybe_unused]] auto import_object = import_argument.to_object(global_object); - if (vm.exception()) { - rejection_value = vm.exception()->value(); - vm.clear_exception(); - } - auto promise = JS::Promise::create(global_object); - if (!rejection_value.is_empty()) { - promise->reject(rejection_value); + if (take_exception_and_reject_if_needed()) return promise; + + dbgln("Trying to resolve stuff because import object was specified"); + for (const Wasm::Linker::Name& import_name : linker.unresolved_imports()) { + dbgln("Trying to resolve {}::{}", import_name.module, import_name.name); + auto value = import_object->get(import_name.module); + if (vm.exception()) + break; + auto object = value.to_object(global_object); + if (vm.exception()) + break; + + auto import_ = object->get(import_name.name); + if (vm.exception()) + break; + import_name.type.visit( + [&](Wasm::TypeIndex index) { + 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::MarkedValueList argument_values { vm.heap() }; + for (auto& entry : arguments) + argument_values.append(to_js_value(entry, global_object)); + + auto result = vm.call(function, JS::js_undefined(), move(argument_values)); + if (vm.exception()) { + vm.clear_exception(); + return Wasm::Trap(); + } + if (type.results().is_empty()) + return Wasm::Result { Vector {} }; + + if (type.results().size() == 1) { + auto value = to_webassembly_value(result, type.results().first(), global_object); + if (!value.has_value()) + return Wasm::Trap {}; + + return Wasm::Result { Vector { value.release_value() } }; + } + + // FIXME: Multiple returns + TODO(); + }, + 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 } }); + }, + [&](const auto&) { + // FIXME: Implement these. + }); } - // FIXME: Populate the import values. + if (take_exception_and_reject_if_needed()) + return promise; } - Wasm::Linker linker { *module }; - linker.link(import_values); + linker.link(resolved_imports); auto link_result = linker.finish(); if (link_result.is_error()) { // FIXME: Throw a LinkError. @@ -149,6 +213,7 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyObject::instantiate) auto instance_result = s_abstract_machine.instantiate(*module, link_result.release_value()); if (instance_result.is_error()) { + // FIXME: Throw a LinkError instead. auto error = JS::TypeError::create(global_object, instance_result.error().error); promise->reject(error); return promise; @@ -171,9 +236,7 @@ WebAssemblyInstanceObject::WebAssemblyInstanceObject(JS::GlobalObject& global_ob { } -static JS::NativeFunction* create_native_function(Wasm::FunctionAddress address, String name, JS::GlobalObject& global_object); - -static JS::Value to_js_value(Wasm::Value& wasm_value, JS::GlobalObject& global_object) +JS::Value to_js_value(Wasm::Value& wasm_value, JS::GlobalObject& global_object) { switch (wasm_value.type().kind()) { case Wasm::ValueType::I64: @@ -194,7 +257,7 @@ static JS::Value to_js_value(Wasm::Value& wasm_value, JS::GlobalObject& global_o VERIFY_NOT_REACHED(); } -static Optional to_webassembly_value(JS::Value value, const Wasm::ValueType& type, JS::GlobalObject& global_object) +Optional to_webassembly_value(JS::Value value, const Wasm::ValueType& type, JS::GlobalObject& global_object) { static Crypto::SignedBigInteger two_64 = "1"_sbigint.shift_left(64); auto& vm = global_object.vm();