mirror of
https://github.com/RGBCube/serenity
synced 2025-05-20 14:55:08 +00:00
LibWasm: Add execution hooks and a debugger mode to the wasm tool
This is useful for debugging *our* implementation of wasm :P
This commit is contained in:
parent
f740667fa1
commit
ba5da79617
8 changed files with 299 additions and 4 deletions
|
@ -113,6 +113,8 @@ InstantiationResult AbstractMachine::instantiate(const Module& module, Vector<Ex
|
||||||
entry.expression(),
|
entry.expression(),
|
||||||
1);
|
1);
|
||||||
Configuration config { m_store };
|
Configuration config { m_store };
|
||||||
|
config.pre_interpret_hook = &pre_interpret_hook;
|
||||||
|
config.post_interpret_hook = &post_interpret_hook;
|
||||||
config.set_frame(move(frame));
|
config.set_frame(move(frame));
|
||||||
auto result = config.execute();
|
auto result = config.execute();
|
||||||
// What if this traps?
|
// What if this traps?
|
||||||
|
@ -143,6 +145,8 @@ InstantiationResult AbstractMachine::instantiate(const Module& module, Vector<Ex
|
||||||
data.offset,
|
data.offset,
|
||||||
1);
|
1);
|
||||||
Configuration config { m_store };
|
Configuration config { m_store };
|
||||||
|
config.pre_interpret_hook = &pre_interpret_hook;
|
||||||
|
config.post_interpret_hook = &post_interpret_hook;
|
||||||
config.set_frame(move(frame));
|
config.set_frame(move(frame));
|
||||||
auto result = config.execute();
|
auto result = config.execute();
|
||||||
size_t offset = 0;
|
size_t offset = 0;
|
||||||
|
@ -281,7 +285,10 @@ Optional<InstantiationError> AbstractMachine::allocate_all(const Module& module,
|
||||||
|
|
||||||
Result AbstractMachine::invoke(FunctionAddress address, Vector<Value> arguments)
|
Result AbstractMachine::invoke(FunctionAddress address, Vector<Value> arguments)
|
||||||
{
|
{
|
||||||
return Configuration { m_store }.call(address, move(arguments));
|
Configuration configuration { m_store };
|
||||||
|
configuration.pre_interpret_hook = &pre_interpret_hook;
|
||||||
|
configuration.post_interpret_hook = &post_interpret_hook;
|
||||||
|
return configuration.call(address, move(arguments));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Linker::link(const ModuleInstance& instance)
|
void Linker::link(const ModuleInstance& instance)
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
namespace Wasm {
|
namespace Wasm {
|
||||||
|
|
||||||
class Configuration;
|
class Configuration;
|
||||||
|
struct Interpreter;
|
||||||
|
|
||||||
struct InstantiationError {
|
struct InstantiationError {
|
||||||
String error { "Unknown error" };
|
String error { "Unknown error" };
|
||||||
|
@ -445,6 +446,9 @@ public:
|
||||||
auto& store() const { return m_store; }
|
auto& store() const { return m_store; }
|
||||||
auto& store() { return m_store; }
|
auto& store() { return m_store; }
|
||||||
|
|
||||||
|
Function<bool(Configuration&, InstructionPointer&, const Instruction&)> pre_interpret_hook;
|
||||||
|
Function<bool(Configuration&, InstructionPointer&, const Instruction&, const Interpreter&)> post_interpret_hook;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Optional<InstantiationError> allocate_all(const Module&, ModuleInstance&, Vector<ExternValue>&, Vector<Value>& global_values);
|
Optional<InstantiationError> allocate_all(const Module&, ModuleInstance&, Vector<ExternValue>&, Vector<Value>& global_values);
|
||||||
Store m_store;
|
Store m_store;
|
||||||
|
|
|
@ -53,6 +53,9 @@ Result Configuration::call(FunctionAddress address, Vector<Value> arguments)
|
||||||
Result Configuration::execute()
|
Result Configuration::execute()
|
||||||
{
|
{
|
||||||
Interpreter interpreter;
|
Interpreter interpreter;
|
||||||
|
interpreter.pre_interpret_hook = pre_interpret_hook;
|
||||||
|
interpreter.post_interpret_hook = post_interpret_hook;
|
||||||
|
|
||||||
interpreter.interpret(*this);
|
interpreter.interpret(*this);
|
||||||
if (interpreter.did_trap())
|
if (interpreter.did_trap())
|
||||||
return Trap {};
|
return Trap {};
|
||||||
|
|
|
@ -40,6 +40,9 @@ public:
|
||||||
|
|
||||||
void dump_stack();
|
void dump_stack();
|
||||||
|
|
||||||
|
Function<bool(Configuration&, InstructionPointer&, const Instruction&)>* pre_interpret_hook { nullptr };
|
||||||
|
Function<bool(Configuration&, InstructionPointer&, const Instruction&, const Interpreter&)>* post_interpret_hook { nullptr };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Store& m_store;
|
Store& m_store;
|
||||||
Frame* m_current_frame { nullptr };
|
Frame* m_current_frame { nullptr };
|
||||||
|
|
|
@ -126,6 +126,8 @@ void Interpreter::call_address(Configuration& configuration, FunctionAddress add
|
||||||
args.prepend(move(*configuration.stack().pop().get<NonnullOwnPtr<Value>>()));
|
args.prepend(move(*configuration.stack().pop().get<NonnullOwnPtr<Value>>()));
|
||||||
}
|
}
|
||||||
Configuration function_configuration { configuration.store() };
|
Configuration function_configuration { configuration.store() };
|
||||||
|
function_configuration.pre_interpret_hook = pre_interpret_hook;
|
||||||
|
function_configuration.post_interpret_hook = post_interpret_hook;
|
||||||
function_configuration.depth() = configuration.depth() + 1;
|
function_configuration.depth() = configuration.depth() + 1;
|
||||||
auto result = function_configuration.call(address, move(args));
|
auto result = function_configuration.call(address, move(args));
|
||||||
if (result.is_trap()) {
|
if (result.is_trap()) {
|
||||||
|
@ -338,8 +340,25 @@ Vector<NonnullOwnPtr<Value>> Interpreter::pop_values(Configuration& configuratio
|
||||||
void Interpreter::interpret(Configuration& configuration, InstructionPointer& ip, const Instruction& instruction)
|
void Interpreter::interpret(Configuration& configuration, InstructionPointer& ip, const Instruction& instruction)
|
||||||
{
|
{
|
||||||
dbgln_if(WASM_TRACE_DEBUG, "Executing instruction {} at ip {}", instruction_name(instruction.opcode()), ip.value());
|
dbgln_if(WASM_TRACE_DEBUG, "Executing instruction {} at ip {}", instruction_name(instruction.opcode()), ip.value());
|
||||||
if constexpr (WASM_TRACE_DEBUG)
|
|
||||||
configuration.dump_stack();
|
if (pre_interpret_hook && *pre_interpret_hook) {
|
||||||
|
auto result = pre_interpret_hook->operator()(configuration, ip, instruction);
|
||||||
|
if (!result) {
|
||||||
|
m_do_trap = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ScopeGuard guard { [&] {
|
||||||
|
if (post_interpret_hook && *post_interpret_hook) {
|
||||||
|
auto result = post_interpret_hook->operator()(configuration, ip, instruction, *this);
|
||||||
|
if (!result) {
|
||||||
|
m_do_trap = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} };
|
||||||
|
|
||||||
switch (instruction.opcode().value()) {
|
switch (instruction.opcode().value()) {
|
||||||
case Instructions::unreachable.value():
|
case Instructions::unreachable.value():
|
||||||
m_do_trap = true;
|
m_do_trap = true;
|
||||||
|
|
|
@ -13,6 +13,10 @@ namespace Wasm {
|
||||||
struct Interpreter {
|
struct Interpreter {
|
||||||
void interpret(Configuration&);
|
void interpret(Configuration&);
|
||||||
bool did_trap() const { return m_do_trap; }
|
bool did_trap() const { return m_do_trap; }
|
||||||
|
void clear_trap() { m_do_trap = false; }
|
||||||
|
|
||||||
|
Function<bool(Configuration&, InstructionPointer&, const Instruction&)>* pre_interpret_hook { nullptr };
|
||||||
|
Function<bool(Configuration&, InstructionPointer&, const Instruction&, const Interpreter&)>* post_interpret_hook { nullptr };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void interpret(Configuration&, InstructionPointer&, const Instruction&);
|
void interpret(Configuration&, InstructionPointer&, const Instruction&);
|
||||||
|
|
|
@ -56,4 +56,4 @@ target_link_libraries(unzip LibArchive LibCompress)
|
||||||
target_link_libraries(zip LibArchive LibCompress LibCrypto)
|
target_link_libraries(zip LibArchive LibCompress LibCrypto)
|
||||||
target_link_libraries(cpp-parser LibCpp LibGUI)
|
target_link_libraries(cpp-parser LibCpp LibGUI)
|
||||||
target_link_libraries(PreprocessorTest LibCpp LibGUI)
|
target_link_libraries(PreprocessorTest LibCpp LibGUI)
|
||||||
target_link_libraries(wasm LibWasm)
|
target_link_libraries(wasm LibWasm LibLine)
|
||||||
|
|
|
@ -7,9 +7,232 @@
|
||||||
#include <LibCore/ArgsParser.h>
|
#include <LibCore/ArgsParser.h>
|
||||||
#include <LibCore/File.h>
|
#include <LibCore/File.h>
|
||||||
#include <LibCore/FileStream.h>
|
#include <LibCore/FileStream.h>
|
||||||
|
#include <LibLine/Editor.h>
|
||||||
#include <LibWasm/AbstractMachine/AbstractMachine.h>
|
#include <LibWasm/AbstractMachine/AbstractMachine.h>
|
||||||
|
#include <LibWasm/AbstractMachine/Interpreter.h>
|
||||||
#include <LibWasm/Printer/Printer.h>
|
#include <LibWasm/Printer/Printer.h>
|
||||||
#include <LibWasm/Types.h>
|
#include <LibWasm/Types.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
RefPtr<Line::Editor> g_line_editor;
|
||||||
|
static auto g_stdout = Core::OutputFileStream::standard_error();
|
||||||
|
static Wasm::Printer g_printer { g_stdout };
|
||||||
|
static bool g_continue { false };
|
||||||
|
static void (*old_signal)(int);
|
||||||
|
|
||||||
|
static void print_buffer(ReadonlyBytes buffer, int split)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < buffer.size(); ++i) {
|
||||||
|
if (split > 0) {
|
||||||
|
if (i % split == 0 && i) {
|
||||||
|
printf(" ");
|
||||||
|
for (size_t j = i - split; j < i; ++j) {
|
||||||
|
auto ch = buffer[j];
|
||||||
|
printf("%c", ch >= 32 && ch <= 127 ? ch : '.'); // silly hack
|
||||||
|
}
|
||||||
|
puts("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("%02x ", buffer[i]);
|
||||||
|
}
|
||||||
|
puts("");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sigint_handler(int)
|
||||||
|
{
|
||||||
|
if (!g_continue) {
|
||||||
|
signal(SIGINT, old_signal);
|
||||||
|
kill(getpid(), SIGINT);
|
||||||
|
}
|
||||||
|
g_continue = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool post_interpret_hook(Wasm::Configuration&, Wasm::InstructionPointer&, const Wasm::Instruction&, const Wasm::Interpreter& interpreter)
|
||||||
|
{
|
||||||
|
if (interpreter.did_trap()) {
|
||||||
|
g_continue = false;
|
||||||
|
const_cast<Wasm::Interpreter&>(interpreter).clear_trap();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool pre_interpret_hook(Wasm::Configuration& config, Wasm::InstructionPointer& ip, const Wasm::Instruction& 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(String::formatted("{:0>4} ", ip.value()).bytes());
|
||||||
|
g_printer.print(instr);
|
||||||
|
}
|
||||||
|
if (g_continue)
|
||||||
|
return true;
|
||||||
|
g_stdout.write(String::formatted("{:0>4} ", ip.value()).bytes());
|
||||||
|
g_printer.print(instr);
|
||||||
|
String 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("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_uint<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;
|
||||||
|
}
|
||||||
|
print_buffer(mem->data(), 32);
|
||||||
|
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_uint<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_uint<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<u64> values_to_push;
|
||||||
|
Vector<Wasm::Value> values;
|
||||||
|
for (size_t index = 2; index < args.size(); ++index)
|
||||||
|
values_to_push.append(args[index].to_uint().value_or(0));
|
||||||
|
for (auto& param : type.parameters())
|
||||||
|
values.append(Wasm::Value { param, values_to_push.take_last() });
|
||||||
|
|
||||||
|
auto result = config.call(*address, move(values));
|
||||||
|
if (result.is_trap())
|
||||||
|
warnln("Execution trapped!");
|
||||||
|
if (!result.values().is_empty())
|
||||||
|
warnln("Returned:");
|
||||||
|
for (auto& value : result.values()) {
|
||||||
|
auto str = value.value().visit(
|
||||||
|
[&](const auto& value) {
|
||||||
|
if constexpr (requires { value.value(); })
|
||||||
|
return String::formatted(" -> addr{} ", value.value());
|
||||||
|
else
|
||||||
|
return String::formatted(" -> {} ", value);
|
||||||
|
});
|
||||||
|
g_stdout.write(str.bytes());
|
||||||
|
g_printer.print(value.type());
|
||||||
|
}
|
||||||
|
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 Optional<Wasm::Module> parse(const StringView& filename)
|
static Optional<Wasm::Module> parse(const StringView& filename)
|
||||||
{
|
{
|
||||||
|
@ -40,12 +263,14 @@ int main(int argc, char* argv[])
|
||||||
const char* filename = nullptr;
|
const char* filename = nullptr;
|
||||||
bool print = false;
|
bool print = false;
|
||||||
bool attempt_instantiate = false;
|
bool attempt_instantiate = false;
|
||||||
|
bool debug = false;
|
||||||
String exported_function_to_execute;
|
String exported_function_to_execute;
|
||||||
Vector<u64> values_to_push;
|
Vector<u64> values_to_push;
|
||||||
Vector<String> modules_to_link_in;
|
Vector<String> modules_to_link_in;
|
||||||
|
|
||||||
Core::ArgsParser parser;
|
Core::ArgsParser parser;
|
||||||
parser.add_positional_argument(filename, "File name to parse", "file");
|
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(print, "Print the parsed module", "print", 'p');
|
||||||
parser.add_option(attempt_instantiate, "Attempt to instantiate the module", "instantiate", 'i');
|
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(exported_function_to_execute, "Attempt to execute the named exported function from the module (implies -i)", "execute", 'e', "name");
|
||||||
|
@ -79,6 +304,15 @@ int main(int argc, char* argv[])
|
||||||
});
|
});
|
||||||
parser.parse(argc, argv);
|
parser.parse(argc, argv);
|
||||||
|
|
||||||
|
if (debug && exported_function_to_execute.is_empty()) {
|
||||||
|
warnln("Debug what? (pass -e fn)");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (debug) {
|
||||||
|
old_signal = signal(SIGINT, sigint_handler);
|
||||||
|
}
|
||||||
|
|
||||||
if (!exported_function_to_execute.is_empty())
|
if (!exported_function_to_execute.is_empty())
|
||||||
attempt_instantiate = true;
|
attempt_instantiate = true;
|
||||||
|
|
||||||
|
@ -91,6 +325,12 @@ int main(int argc, char* argv[])
|
||||||
|
|
||||||
if (attempt_instantiate) {
|
if (attempt_instantiate) {
|
||||||
Wasm::AbstractMachine machine;
|
Wasm::AbstractMachine machine;
|
||||||
|
Core::EventLoop main_loop;
|
||||||
|
if (debug) {
|
||||||
|
g_line_editor = Line::Editor::construct();
|
||||||
|
machine.pre_interpret_hook = pre_interpret_hook;
|
||||||
|
machine.post_interpret_hook = post_interpret_hook;
|
||||||
|
}
|
||||||
// First, resolve the linked modules
|
// First, resolve the linked modules
|
||||||
NonnullOwnPtrVector<Wasm::ModuleInstance> linked_instances;
|
NonnullOwnPtrVector<Wasm::ModuleInstance> linked_instances;
|
||||||
Vector<Wasm::Module> linked_modules;
|
Vector<Wasm::Module> linked_modules;
|
||||||
|
@ -194,6 +434,21 @@ int main(int argc, char* argv[])
|
||||||
}
|
}
|
||||||
|
|
||||||
auto result = machine.invoke(run_address.value(), move(values));
|
auto result = machine.invoke(run_address.value(), move(values));
|
||||||
|
|
||||||
|
if (debug) {
|
||||||
|
Wasm::Configuration config { machine.store() };
|
||||||
|
auto frame = make<Wasm::Frame>(
|
||||||
|
*module_instance,
|
||||||
|
Vector<Wasm::Value> {},
|
||||||
|
instance->get<Wasm::WasmFunction>().code().body(),
|
||||||
|
1);
|
||||||
|
config.set_frame(move(frame));
|
||||||
|
const Wasm::Instruction instr { Wasm::Instructions::nop };
|
||||||
|
Wasm::InstructionPointer ip { 0 };
|
||||||
|
g_continue = false;
|
||||||
|
pre_interpret_hook(config, ip, instr);
|
||||||
|
}
|
||||||
|
|
||||||
if (result.is_trap())
|
if (result.is_trap())
|
||||||
warnln("Execution trapped!");
|
warnln("Execution trapped!");
|
||||||
if (!result.values().is_empty())
|
if (!result.values().is_empty())
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue