LibWasm+wasm: Remove the debug interpreter
This is largely unused (only in wasm.cpp) A future reimplementation can bring it back as a separate interpreter class that embeds the current bytecode interpreter.
This commit is contained in:
parent
4e2845847b
commit
998454028c
2 changed files with 2 additions and 309 deletions
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
namespace Wasm {
|
||||
|
||||
struct BytecodeInterpreter : public Interpreter {
|
||||
struct BytecodeInterpreter final : public Interpreter {
|
||||
explicit BytecodeInterpreter(StackInfo const& stack_info)
|
||||
: m_stack_info(stack_info)
|
||||
{
|
||||
|
|
@ -106,18 +106,4 @@ protected:
|
|||
StackInfo const& m_stack_info;
|
||||
};
|
||||
|
||||
struct DebuggerBytecodeInterpreter : public BytecodeInterpreter {
|
||||
DebuggerBytecodeInterpreter(StackInfo const& stack_info)
|
||||
: BytecodeInterpreter(stack_info)
|
||||
{
|
||||
}
|
||||
virtual ~DebuggerBytecodeInterpreter() override = default;
|
||||
|
||||
Function<bool(Configuration&, InstructionPointer&, Instruction const&)> pre_interpret_hook;
|
||||
Function<bool(Configuration&, InstructionPointer&, Instruction const&, Interpreter const&)> post_interpret_hook;
|
||||
|
||||
private:
|
||||
void interpret_instruction(Configuration&, InstructionPointer&, Instruction const&);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,28 +24,16 @@
|
|||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
|
||||
RefPtr<Line::Editor> g_line_editor;
|
||||
static OwnPtr<Stream> g_stdout {};
|
||||
static OwnPtr<Wasm::Printer> g_printer {};
|
||||
static bool g_continue { false };
|
||||
static void (*old_signal)(int);
|
||||
static StackInfo g_stack_info;
|
||||
static Wasm::DebuggerBytecodeInterpreter g_interpreter(g_stack_info);
|
||||
static Wasm::BytecodeInterpreter g_interpreter(g_stack_info);
|
||||
|
||||
struct ParsedValue {
|
||||
Wasm::Value value;
|
||||
Wasm::ValueType type;
|
||||
};
|
||||
|
||||
static void sigint_handler(int)
|
||||
{
|
||||
if (!g_continue) {
|
||||
signal(SIGINT, old_signal);
|
||||
kill(getpid(), SIGINT);
|
||||
}
|
||||
g_continue = false;
|
||||
}
|
||||
|
||||
static Optional<u128> convert_to_uint(StringView string)
|
||||
{
|
||||
if (string.is_empty())
|
||||
|
|
@ -259,239 +247,6 @@ static ErrorOr<ParsedValue> parse_value(StringView spec)
|
|||
return Error::from_string_literal("Invalid value");
|
||||
}
|
||||
|
||||
static bool post_interpret_hook(Wasm::Configuration&, Wasm::InstructionPointer& ip, Wasm::Instruction const& instr, Wasm::Interpreter const& interpreter)
|
||||
{
|
||||
if (interpreter.did_trap()) {
|
||||
g_continue = false;
|
||||
warnln("Trapped when executing ip={}", ip);
|
||||
g_printer->print(instr);
|
||||
warnln("Trap reason: {}", interpreter.trap().format());
|
||||
const_cast<Wasm::Interpreter&>(interpreter).clear_trap();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool pre_interpret_hook(Wasm::Configuration& config, Wasm::InstructionPointer& ip, Wasm::Instruction const& instr)
|
||||
{
|
||||
static bool always_print_stack = false;
|
||||
static bool always_print_instruction = false;
|
||||
if (always_print_stack)
|
||||
config.dump_stack();
|
||||
if (always_print_instruction) {
|
||||
g_stdout->write_until_depleted(ByteString::formatted("{:0>4} ", ip.value())).release_value_but_fixme_should_propagate_errors();
|
||||
g_printer->print(instr);
|
||||
}
|
||||
if (g_continue)
|
||||
return true;
|
||||
g_stdout->write_until_depleted(ByteString::formatted("{:0>4} ", ip.value())).release_value_but_fixme_should_propagate_errors();
|
||||
g_printer->print(instr);
|
||||
ByteString last_command = "";
|
||||
for (;;) {
|
||||
auto result = g_line_editor->get_line("> ");
|
||||
if (result.is_error()) {
|
||||
return false;
|
||||
}
|
||||
auto str = result.release_value();
|
||||
g_line_editor->add_to_history(str);
|
||||
if (str.is_empty())
|
||||
str = last_command;
|
||||
else
|
||||
last_command = str;
|
||||
auto args = str.split_view(' ');
|
||||
if (args.is_empty())
|
||||
continue;
|
||||
auto& cmd = args[0];
|
||||
if (cmd.is_one_of("h", "help")) {
|
||||
warnln("Wasm shell commands");
|
||||
warnln("Toplevel:");
|
||||
warnln("- [s]tep Run one instruction");
|
||||
warnln("- next Alias for step");
|
||||
warnln("- [c]ontinue Execute until a trap or the program exit point");
|
||||
warnln("- [p]rint <args...> Print various things (see section on print)");
|
||||
warnln("- call <fn> <args...> Call the function <fn> with the given arguments");
|
||||
warnln("- set <args...> Set shell option (see section on settings)");
|
||||
warnln("- unset <args...> Unset shell option (see section on settings)");
|
||||
warnln("- [h]elp Print this help");
|
||||
warnln();
|
||||
warnln("Print:");
|
||||
warnln("- print [s]tack Print the contents of the stack, including frames and labels");
|
||||
warnln("- print [[m]em]ory <index> Print the contents of the memory identified by <index>");
|
||||
warnln("- print [[i]nstr]uction Print the current instruction");
|
||||
warnln("- print [[f]unc]tion <index> Print the function identified by <index>");
|
||||
warnln();
|
||||
warnln("Settings:");
|
||||
warnln("- set print stack Make the shell print the stack on every instruction executed");
|
||||
warnln("- set print [instr]uction Make the shell print the instruction that will be executed next");
|
||||
warnln();
|
||||
continue;
|
||||
}
|
||||
if (cmd.is_one_of("s", "step", "next")) {
|
||||
return true;
|
||||
}
|
||||
if (cmd.is_one_of("p", "print")) {
|
||||
if (args.size() < 2) {
|
||||
warnln("Print what?");
|
||||
continue;
|
||||
}
|
||||
auto& what = args[1];
|
||||
if (what.is_one_of("s", "stack")) {
|
||||
config.dump_stack();
|
||||
continue;
|
||||
}
|
||||
if (what.is_one_of("m", "mem", "memory")) {
|
||||
if (args.size() < 3) {
|
||||
warnln("print what memory?");
|
||||
continue;
|
||||
}
|
||||
auto value = args[2].to_number<u64>();
|
||||
if (!value.has_value()) {
|
||||
warnln("invalid memory index {}", args[2]);
|
||||
continue;
|
||||
}
|
||||
auto mem = config.store().get(Wasm::MemoryAddress(value.value()));
|
||||
if (!mem) {
|
||||
warnln("invalid memory index {} (not found)", args[2]);
|
||||
continue;
|
||||
}
|
||||
warnln("{:>32hex-dump}", mem->data().bytes());
|
||||
continue;
|
||||
}
|
||||
if (what.is_one_of("i", "instr", "instruction")) {
|
||||
g_printer->print(instr);
|
||||
continue;
|
||||
}
|
||||
if (what.is_one_of("f", "func", "function")) {
|
||||
if (args.size() < 3) {
|
||||
warnln("print what function?");
|
||||
continue;
|
||||
}
|
||||
auto value = args[2].to_number<u64>();
|
||||
if (!value.has_value()) {
|
||||
warnln("invalid function index {}", args[2]);
|
||||
continue;
|
||||
}
|
||||
auto fn = config.store().get(Wasm::FunctionAddress(value.value()));
|
||||
if (!fn) {
|
||||
warnln("invalid function index {} (not found)", args[2]);
|
||||
continue;
|
||||
}
|
||||
if (auto* fn_value = fn->get_pointer<Wasm::HostFunction>()) {
|
||||
warnln("Host function at {:p}", &fn_value->function());
|
||||
continue;
|
||||
}
|
||||
if (auto* fn_value = fn->get_pointer<Wasm::WasmFunction>()) {
|
||||
g_printer->print(fn_value->code());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cmd == "call"sv) {
|
||||
if (args.size() < 2) {
|
||||
warnln("call what?");
|
||||
continue;
|
||||
}
|
||||
Optional<Wasm::FunctionAddress> address;
|
||||
auto index = args[1].to_number<u64>();
|
||||
if (index.has_value()) {
|
||||
address = config.frame().module().functions()[index.value()];
|
||||
} else {
|
||||
auto& name = args[1];
|
||||
for (auto& export_ : config.frame().module().exports()) {
|
||||
if (export_.name() == name) {
|
||||
if (auto addr = export_.value().get_pointer<Wasm::FunctionAddress>()) {
|
||||
address = *addr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!address.has_value()) {
|
||||
failed_to_find:;
|
||||
warnln("Could not find a function {}", args[1]);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto fn = config.store().get(*address);
|
||||
if (!fn)
|
||||
goto failed_to_find;
|
||||
|
||||
auto type = fn->visit([&](auto& value) { return value.type(); });
|
||||
if (type.parameters().size() + 2 != args.size()) {
|
||||
warnln("Expected {} arguments for call, but found only {}", type.parameters().size(), args.size() - 2);
|
||||
continue;
|
||||
}
|
||||
Vector<ParsedValue> values_to_push;
|
||||
Vector<Wasm::Value> values;
|
||||
auto ok = true;
|
||||
for (size_t index = 2; index < args.size(); ++index) {
|
||||
auto r = parse_value(args[index]);
|
||||
if (r.is_error()) {
|
||||
warnln("Failed to parse argument {}: {}", args[index], r.error());
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
values_to_push.append(r.release_value());
|
||||
}
|
||||
if (!ok)
|
||||
continue;
|
||||
for (auto& param : type.parameters()) {
|
||||
auto v = values_to_push.take_last();
|
||||
if (v.type != param) {
|
||||
warnln("Type mismatch in argument: expected {}, but got {}", Wasm::ValueType::kind_name(param.kind()), Wasm::ValueType::kind_name(v.type.kind()));
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
values.append(v.value);
|
||||
}
|
||||
if (!ok)
|
||||
continue;
|
||||
|
||||
Wasm::Result result { Wasm::Trap::from_string("") };
|
||||
{
|
||||
Wasm::BytecodeInterpreter::CallFrameHandle handle { g_interpreter, config };
|
||||
result = config.call(g_interpreter, *address, move(values));
|
||||
}
|
||||
if (result.is_trap()) {
|
||||
warnln("Execution trapped: {}", result.trap().format());
|
||||
} else {
|
||||
if (!result.values().is_empty())
|
||||
warnln("Returned:");
|
||||
size_t index = 0;
|
||||
for (auto& value : result.values()) {
|
||||
g_stdout->write_until_depleted(" -> "sv.bytes()).release_value_but_fixme_should_propagate_errors();
|
||||
g_printer->print(value, type.results()[index]);
|
||||
++index;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (cmd.is_one_of("set", "unset")) {
|
||||
auto value = !cmd.starts_with('u');
|
||||
if (args.size() < 3) {
|
||||
warnln("(un)set what (to what)?");
|
||||
continue;
|
||||
}
|
||||
if (args[1] == "print"sv) {
|
||||
if (args[2] == "stack"sv)
|
||||
always_print_stack = value;
|
||||
else if (args[2].is_one_of("instr", "instruction"))
|
||||
always_print_instruction = value;
|
||||
else
|
||||
warnln("Unknown print category '{}'", args[2]);
|
||||
continue;
|
||||
}
|
||||
warnln("Unknown set category '{}'", args[1]);
|
||||
continue;
|
||||
}
|
||||
if (cmd.is_one_of("c", "continue")) {
|
||||
g_continue = true;
|
||||
return true;
|
||||
}
|
||||
warnln("Command not understood: {}", cmd);
|
||||
}
|
||||
}
|
||||
|
||||
static RefPtr<Wasm::Module> parse(StringView filename)
|
||||
{
|
||||
auto result = Core::MappedFile::map(filename);
|
||||
|
|
@ -520,9 +275,7 @@ ErrorOr<int> ladybird_main(Main::Arguments arguments)
|
|||
StringView filename;
|
||||
bool print = false;
|
||||
bool attempt_instantiate = false;
|
||||
bool debug = false;
|
||||
bool export_all_imports = false;
|
||||
bool shell_mode = false;
|
||||
bool wasi = false;
|
||||
ByteString exported_function_to_execute;
|
||||
Vector<ParsedValue> values_to_push;
|
||||
|
|
@ -532,12 +285,10 @@ ErrorOr<int> ladybird_main(Main::Arguments arguments)
|
|||
|
||||
Core::ArgsParser parser;
|
||||
parser.add_positional_argument(filename, "File name to parse", "file");
|
||||
parser.add_option(debug, "Open a debugger", "debug", 'd');
|
||||
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");
|
||||
parser.add_option(shell_mode, "Launch a REPL in the module's context (implies -i)", "shell", 's');
|
||||
parser.add_option(wasi, "Enable WASI", "wasi", 'w');
|
||||
parser.add_option(Core::ArgsParser::Option {
|
||||
.argument_mode = Core::ArgsParser::OptionArgumentMode::Required,
|
||||
|
|
@ -586,20 +337,6 @@ ErrorOr<int> ladybird_main(Main::Arguments arguments)
|
|||
parser.add_positional_argument(args_if_wasi, "Arguments to pass to the WASI module", "args", Core::ArgsParser::Required::No);
|
||||
parser.parse(arguments);
|
||||
|
||||
if (shell_mode) {
|
||||
debug = true;
|
||||
attempt_instantiate = true;
|
||||
}
|
||||
|
||||
if (!shell_mode && debug && exported_function_to_execute.is_empty()) {
|
||||
warnln("Debug what? (pass -e fn)");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (debug || shell_mode) {
|
||||
old_signal = signal(SIGINT, sigint_handler);
|
||||
}
|
||||
|
||||
if (!exported_function_to_execute.is_empty())
|
||||
attempt_instantiate = true;
|
||||
|
||||
|
|
@ -646,12 +383,6 @@ ErrorOr<int> ladybird_main(Main::Arguments arguments)
|
|||
}
|
||||
|
||||
Core::EventLoop main_loop;
|
||||
if (debug) {
|
||||
g_line_editor = Line::Editor::construct();
|
||||
g_interpreter.pre_interpret_hook = pre_interpret_hook;
|
||||
g_interpreter.post_interpret_hook = post_interpret_hook;
|
||||
}
|
||||
|
||||
// First, resolve the linked modules
|
||||
Vector<NonnullOwnPtr<Wasm::ModuleInstance>> linked_instances;
|
||||
Vector<NonnullRefPtr<Wasm::Module>> linked_modules;
|
||||
|
|
@ -752,21 +483,6 @@ ErrorOr<int> ladybird_main(Main::Arguments arguments)
|
|||
}
|
||||
auto module_instance = result.release_value();
|
||||
|
||||
auto launch_repl = [&] {
|
||||
Wasm::Configuration config { machine.store() };
|
||||
Wasm::Expression expression { {} };
|
||||
config.set_frame(Wasm::Frame {
|
||||
*module_instance,
|
||||
Vector<Wasm::Value> {},
|
||||
expression,
|
||||
0,
|
||||
});
|
||||
Wasm::Instruction instr { Wasm::Instructions::nop };
|
||||
Wasm::InstructionPointer ip { 0 };
|
||||
g_continue = false;
|
||||
pre_interpret_hook(config, ip, instr);
|
||||
};
|
||||
|
||||
auto print_func = [&](auto const& address) {
|
||||
Wasm::FunctionInstance* fn = machine.store().get(address);
|
||||
g_stdout->write_until_depleted(ByteString::formatted("- Function with address {}, ptr = {}\n", address.value(), fn)).release_value_but_fixme_should_propagate_errors();
|
||||
|
|
@ -790,11 +506,6 @@ ErrorOr<int> ladybird_main(Main::Arguments arguments)
|
|||
}
|
||||
}
|
||||
|
||||
if (shell_mode) {
|
||||
launch_repl();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!exported_function_to_execute.is_empty()) {
|
||||
Optional<Wasm::FunctionAddress> run_address;
|
||||
Vector<Wasm::Value> values;
|
||||
|
|
@ -835,10 +546,6 @@ ErrorOr<int> ladybird_main(Main::Arguments arguments)
|
|||
}
|
||||
|
||||
auto result = machine.invoke(g_interpreter, run_address.value(), move(values));
|
||||
|
||||
if (debug)
|
||||
launch_repl();
|
||||
|
||||
if (result.is_trap()) {
|
||||
auto trap_reason = result.trap().format();
|
||||
if (trap_reason.starts_with("exit:"sv))
|
||||
|
|
|
|||
Loading…
Reference in a new issue