From 23fd8bfd691477fc5991c85c834abd741841b828 Mon Sep 17 00:00:00 2001 From: Ali Mohammad Pur Date: Fri, 4 Jun 2021 03:33:10 +0430 Subject: Userland/wasm: Replace manual noop export with an automatic one Instead of manually specifying the types and names of imports to stub out, `--export-noop` can be used to export stub functions for any unresolved function import. This makes running and debugging random wasm files much easier. --- Userland/Utilities/wasm.cpp | 116 +++++++++++++++----------------------------- 1 file changed, 39 insertions(+), 77 deletions(-) (limited to 'Userland') diff --git a/Userland/Utilities/wasm.cpp b/Userland/Utilities/wasm.cpp index bea9796b4f..89dc21e404 100644 --- a/Userland/Utilities/wasm.cpp +++ b/Userland/Utilities/wasm.cpp @@ -265,10 +265,10 @@ int main(int argc, char* argv[]) bool print = false; bool attempt_instantiate = false; bool debug = false; + bool export_all_imports = false; String exported_function_to_execute; Vector values_to_push; Vector modules_to_link_in; - HashMap exported_host_functions; Core::ArgsParser parser; parser.add_positional_argument(filename, "File name to parse", "file"); @@ -276,6 +276,7 @@ int main(int argc, char* argv[]) parser.add_option(print, "Print the parsed module", "print", 'p'); parser.add_option(attempt_instantiate, "Attempt to instantiate the module", "instantiate", 'i'); parser.add_option(exported_function_to_execute, "Attempt to execute the named exported function from the module (implies -i)", "execute", 'e', "name"); + parser.add_option(export_all_imports, "Export noop functions corresponding to imports", "export-noop", 0); parser.add_option(Core::ArgsParser::Option { .requires_argument = true, .help_string = "Extra modules to link with, use to resolve imports", @@ -304,71 +305,6 @@ int main(int argc, char* argv[]) return false; }, }); - parser.add_option(Core::ArgsParser::Option { - .requires_argument = true, - .help_string = "Export a noop function that returns default-initialised values (module!name::t,t,t...:t,t,t...)", - .long_name = "export-noop", - .short_name = 0, - .value_name = "module!name:atypes:rtypes", - .accept_value = [&](const char* str) -> bool { - GenericLexer lexer { str }; - auto name = lexer.consume_until("::"); - auto parts = name.split_view('!'); - if (parts.size() != 2) { - warnln("Expected a two-part name module!name, got '{}'", name); - return false; - } - auto module_name = parts[0]; - auto export_name = parts[1]; - - auto arg_types = lexer.consume_until(":").split_view(','); - auto ret_types = lexer.consume_all().split_view(','); - Vector argument_types, return_types; - for (auto& name : arg_types) { - if (name == "i32") { - argument_types.empend(Wasm::ValueType::Kind::I32); - } else if (name == "i64") { - argument_types.empend(Wasm::ValueType::Kind::I64); - } else if (name == "f32") { - argument_types.empend(Wasm::ValueType::Kind::F32); - } else if (name == "f64") { - argument_types.empend(Wasm::ValueType::Kind::F64); - } else { - warnln("Unknown type '{}'", name); - return false; - } - } - for (auto& name : ret_types) { - if (name == "i32") { - return_types.empend(Wasm::ValueType::Kind::I32); - } else if (name == "i64") { - return_types.empend(Wasm::ValueType::Kind::I64); - } else if (name == "f32") { - return_types.empend(Wasm::ValueType::Kind::F32); - } else if (name == "f64") { - return_types.empend(Wasm::ValueType::Kind::F64); - } else { - warnln("Unknown type '{}'", name); - return false; - } - } - - Wasm::FunctionType function_type { argument_types, return_types }; - exported_host_functions.set( - Wasm::Linker::Name { module_name, export_name, function_type }, - Wasm::HostFunction { - [&](auto&, auto&) -> Wasm::Result { - Vector values; - values.ensure_capacity(return_types.size()); - for (auto& type : return_types) - values.empend(type, 0ull); - return Wasm::Result { move(values) }; - }, - function_type }); - - return true; - }, - }); parser.parse(argc, argv); if (debug && exported_function_to_execute.is_empty()) { @@ -384,6 +320,9 @@ int main(int argc, char* argv[]) attempt_instantiate = true; auto parse_result = parse(filename); + if (!parse_result.has_value()) + return 1; + if (print && !attempt_instantiate) { auto out_stream = Core::OutputFileStream::standard_output(); Wasm::Printer printer(out_stream); @@ -425,20 +364,43 @@ int main(int argc, char* argv[]) linked_instances.append(instantiation_result.release_value()); } - HashMap exports; - for (auto& entry : exported_host_functions) { - auto address = machine.store().allocate(move(entry.value)); - if (!address.has_value()) { - warnln("Could not export {}/{}", entry.key.module, entry.key.name); - return 1; - } - exports.set(entry.key, *address); - } - Wasm::Linker linker { parse_result.value() }; for (auto& instance : linked_instances) linker.link(instance); - linker.link(exports); + + if (export_all_imports) { + HashMap exports; + for (auto& entry : linker.unresolved_imports()) { + if (!entry.type.has()) + continue; + auto type = parse_result.value().type(entry.type.get()); + auto address = machine.store().allocate(Wasm::HostFunction( + [name = entry.name, type = type](auto&, auto& arguments) -> Wasm::Result { + StringBuilder argument_builder; + bool first = true; + for (auto& argument : arguments) { + DuplexMemoryStream stream; + Wasm::Printer { stream }.print(argument); + if (first) + first = false; + else + argument_builder.append(", "sv); + argument_builder.append(StringView(stream.copy_into_contiguous_buffer()).trim_whitespace()); + } + dbgln("[wasm runtime] Stub function {} was called with the following arguments: {}", name, argument_builder.to_string()); + Vector result; + result.ensure_capacity(type.results().size()); + for (auto& result_type : type.results()) + result.append(Wasm::Value { result_type, 0ull }); + return Wasm::Result { move(result) }; + }, + type)); + exports.set(entry, *address); + } + + linker.link(exports); + } + auto link_result = linker.finish(); if (link_result.is_error()) { warnln("Linking main module failed"); -- cgit v1.2.3