mirror of
https://github.com/RGBCube/serenity
synced 2025-05-28 13:55:06 +00:00

This will simply "link" any given module instances and produce a list of external values that can be used to instantiate a module. Note that this is extremely basic and cannot resolve circular dependencies, and depends on the instance order.
216 lines
8.2 KiB
C++
216 lines
8.2 KiB
C++
/*
|
|
* Copyright (c) 2021, Ali Mohammad Pur <mpfard@serenityos.org>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#include <LibCore/ArgsParser.h>
|
|
#include <LibCore/File.h>
|
|
#include <LibCore/FileStream.h>
|
|
#include <LibWasm/AbstractMachine/AbstractMachine.h>
|
|
#include <LibWasm/Printer/Printer.h>
|
|
#include <LibWasm/Types.h>
|
|
|
|
static Optional<Wasm::Module> parse(const StringView& filename)
|
|
{
|
|
auto result = Core::File::open(filename, Core::OpenMode::ReadOnly);
|
|
if (result.is_error()) {
|
|
warnln("Failed to open {}: {}", filename, result.error());
|
|
return {};
|
|
}
|
|
|
|
auto stream = Core::InputFileStream(result.release_value());
|
|
auto parse_result = Wasm::Module::parse(stream);
|
|
if (parse_result.is_error()) {
|
|
warnln("Something went wrong, either the file is invalid, or there's a bug with LibWasm!");
|
|
warnln("The parse error was {}", Wasm::parse_error_to_string(parse_result.error()));
|
|
return {};
|
|
}
|
|
return parse_result.release_value();
|
|
}
|
|
|
|
static void print_link_error(const Wasm::LinkError& error)
|
|
{
|
|
for (const auto& missing : error.missing_imports)
|
|
warnln("Missing import '{}'", missing);
|
|
}
|
|
|
|
int main(int argc, char* argv[])
|
|
{
|
|
const char* filename = nullptr;
|
|
bool print = false;
|
|
bool attempt_instantiate = false;
|
|
String exported_function_to_execute;
|
|
Vector<u64> values_to_push;
|
|
Vector<String> modules_to_link_in;
|
|
|
|
Core::ArgsParser parser;
|
|
parser.add_positional_argument(filename, "File name to parse", "file");
|
|
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(Core::ArgsParser::Option {
|
|
.requires_argument = true,
|
|
.help_string = "Extra modules to link with, use to resolve imports",
|
|
.long_name = "link",
|
|
.short_name = 'l',
|
|
.value_name = "file",
|
|
.accept_value = [&](const char* str) {
|
|
if (auto v = StringView { str }; !v.is_empty()) {
|
|
modules_to_link_in.append(v);
|
|
return true;
|
|
}
|
|
return false;
|
|
},
|
|
});
|
|
parser.add_option(Core::ArgsParser::Option {
|
|
.requires_argument = true,
|
|
.help_string = "Supply arguments to the function (default=0) (expects u64, casts to required type)",
|
|
.long_name = "arg",
|
|
.short_name = 0,
|
|
.value_name = "u64",
|
|
.accept_value = [&](const char* str) -> bool {
|
|
if (auto v = StringView { str }.to_uint<u64>(); v.has_value()) {
|
|
values_to_push.append(v.value());
|
|
return true;
|
|
}
|
|
return false;
|
|
},
|
|
});
|
|
parser.parse(argc, argv);
|
|
|
|
if (!exported_function_to_execute.is_empty())
|
|
attempt_instantiate = true;
|
|
|
|
auto parse_result = parse(filename);
|
|
if (print && !attempt_instantiate) {
|
|
auto out_stream = Core::OutputFileStream::standard_output();
|
|
Wasm::Printer printer(out_stream);
|
|
printer.print(parse_result.value());
|
|
}
|
|
|
|
if (attempt_instantiate) {
|
|
Wasm::AbstractMachine machine;
|
|
// First, resolve the linked modules
|
|
NonnullOwnPtrVector<Wasm::ModuleInstance> linked_instances;
|
|
Vector<Wasm::Module> linked_modules;
|
|
for (auto& name : modules_to_link_in) {
|
|
auto parse_result = parse(name);
|
|
if (!parse_result.has_value()) {
|
|
warnln("Failed to parse linked module '{}'", name);
|
|
return 1;
|
|
}
|
|
linked_modules.append(parse_result.release_value());
|
|
Wasm::Linker linker { linked_modules.last() };
|
|
for (auto& instance : linked_instances)
|
|
linker.link(instance);
|
|
auto link_result = linker.finish();
|
|
if (link_result.is_error()) {
|
|
warnln("Linking imported module '{}' failed", name);
|
|
print_link_error(link_result.error());
|
|
return 1;
|
|
}
|
|
auto instantiation_result = machine.instantiate(linked_modules.last(), link_result.release_value());
|
|
if (instantiation_result.is_error()) {
|
|
warnln("Instantiation of imported module '{}' failed: {}", name, instantiation_result.error().error);
|
|
return 1;
|
|
}
|
|
linked_instances.append(instantiation_result.release_value());
|
|
}
|
|
|
|
Wasm::Linker linker { parse_result.value() };
|
|
for (auto& instance : linked_instances)
|
|
linker.link(instance);
|
|
auto link_result = linker.finish();
|
|
if (link_result.is_error()) {
|
|
warnln("Linking main module failed");
|
|
print_link_error(link_result.error());
|
|
return 1;
|
|
}
|
|
auto result = machine.instantiate(parse_result.value(), link_result.release_value());
|
|
if (result.is_error()) {
|
|
warnln("Module instantiation failed: {}", result.error().error);
|
|
return 1;
|
|
}
|
|
auto module_instance = result.release_value();
|
|
|
|
auto stream = Core::OutputFileStream::standard_output();
|
|
auto print_func = [&](const auto& address) {
|
|
Wasm::FunctionInstance* fn = machine.store().get(address);
|
|
stream.write(String::formatted("- Function with address {}, ptr = {}\n", address.value(), fn).bytes());
|
|
if (fn) {
|
|
stream.write(String::formatted(" wasm function? {}\n", fn->has<Wasm::WasmFunction>()).bytes());
|
|
fn->visit(
|
|
[&](const Wasm::WasmFunction& func) {
|
|
Wasm::Printer printer { stream, 3 };
|
|
stream.write(" type:\n"sv.bytes());
|
|
printer.print(func.type());
|
|
stream.write(" code:\n"sv.bytes());
|
|
printer.print(func.code());
|
|
},
|
|
[](const Wasm::HostFunction&) {});
|
|
}
|
|
};
|
|
if (print) {
|
|
// Now, let's dump the functions!
|
|
for (auto& address : module_instance->functions()) {
|
|
print_func(address);
|
|
}
|
|
}
|
|
|
|
if (!exported_function_to_execute.is_empty()) {
|
|
Optional<Wasm::FunctionAddress> run_address;
|
|
Vector<Wasm::Value> values;
|
|
for (auto& entry : module_instance->exports()) {
|
|
if (entry.name() == exported_function_to_execute) {
|
|
if (auto addr = entry.value().get_pointer<Wasm::FunctionAddress>())
|
|
run_address = *addr;
|
|
}
|
|
}
|
|
if (!run_address.has_value()) {
|
|
warnln("No such exported function, sorry :(");
|
|
return 1;
|
|
}
|
|
|
|
auto instance = machine.store().get(*run_address);
|
|
VERIFY(instance);
|
|
|
|
if (instance->has<Wasm::HostFunction>()) {
|
|
warnln("Exported function is a host function, cannot run that yet");
|
|
return 1;
|
|
}
|
|
|
|
for (auto& param : instance->get<Wasm::WasmFunction>().type().parameters()) {
|
|
if (values_to_push.is_empty())
|
|
values.append(Wasm::Value { param, 0ull });
|
|
else
|
|
values.append(Wasm::Value { param, values_to_push.take_last() });
|
|
}
|
|
|
|
if (print) {
|
|
outln("Executing ");
|
|
print_func(*run_address);
|
|
outln();
|
|
}
|
|
|
|
auto result = machine.invoke(run_address.value(), move(values));
|
|
if (result.is_trap())
|
|
warnln("Execution trapped!");
|
|
if (!result.values().is_empty())
|
|
warnln("Returned:");
|
|
for (auto& value : result.values()) {
|
|
value.value().visit(
|
|
[&](const auto& value) {
|
|
if constexpr (requires { value.value(); })
|
|
out(" -> addr{} ", value.value());
|
|
else
|
|
out(" -> {} ", value);
|
|
});
|
|
Wasm::Printer printer { stream };
|
|
printer.print(value.type());
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|