LibWasm: Add basic support for module instantiation and execution stubs

This adds very basic support for module instantiation/allocation, as
well as a stub for an interpreter (and executions APIs).
The 'wasm' utility is further expanded to instantiate, and attempt
executing the first non-imported function in the module.
Note that as the execution is a stub, the expected result is a zero.
Regardless, this will allow future commits to implement the JS
WebAssembly API. :^)
This commit is contained in:
Ali Mohammad Pur 2021-05-01 01:08:51 +04:30 committed by Linus Groh
parent 2b755f1fbf
commit 4d9246ac9d
Notes: sideshowbarker 2024-07-18 18:13:02 +09:00
8 changed files with 938 additions and 1 deletions

View file

@ -0,0 +1,295 @@
/*
* Copyright (c) 2021, Ali Mohammad Pur <mpfard@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWasm/AbstractMachine/AbstractMachine.h>
#include <LibWasm/AbstractMachine/Configuration.h>
#include <LibWasm/Types.h>
namespace Wasm {
Optional<FunctionAddress> Store::allocate(ModuleInstance& module, const Module::Function& function)
{
FunctionAddress address { m_functions.size() };
if (function.type().value() > module.types().size())
return {};
auto& type = module.types()[function.type().value()];
m_functions.empend(WasmFunction { type, module, function });
return address;
}
Optional<FunctionAddress> Store::allocate(const HostFunction& function)
{
FunctionAddress address { m_functions.size() };
m_functions.empend(HostFunction { function });
return address;
}
Optional<TableAddress> Store::allocate(const TableType& type)
{
TableAddress address { m_tables.size() };
Vector<Optional<Reference>> elements;
elements.resize(type.limits().min());
m_tables.empend(TableInstance { type, move(elements) });
return address;
}
Optional<MemoryAddress> Store::allocate(const MemoryType& type)
{
MemoryAddress address { m_memories.size() };
m_memories.empend(MemoryInstance { type });
return address;
}
Optional<GlobalAddress> Store::allocate(const GlobalType& type, Value value)
{
GlobalAddress address { m_globals.size() };
m_globals.append(GlobalInstance { move(value), type.is_mutable() });
return address;
}
FunctionInstance* Store::get(FunctionAddress address)
{
auto value = address.value();
if (m_functions.size() <= value)
return nullptr;
return &m_functions[value];
}
TableInstance* Store::get(TableAddress address)
{
auto value = address.value();
if (m_tables.size() <= value)
return nullptr;
return &m_tables[value];
}
MemoryInstance* Store::get(MemoryAddress address)
{
auto value = address.value();
if (m_memories.size() <= value)
return nullptr;
return &m_memories[value];
}
GlobalInstance* Store::get(GlobalAddress address)
{
auto value = address.value();
if (m_globals.size() <= value)
return nullptr;
return &m_globals[value];
}
InstantiationResult AbstractMachine::instantiate(const Module& module, Vector<ExternValue> externs)
{
Optional<InstantiationResult> instantiation_result;
module.for_each_section_of_type<TypeSection>([&](const TypeSection& section) {
m_module_instance.types() = section.types();
});
// TODO: Validate stuff
Vector<Value> global_values;
ModuleInstance auxiliary_instance;
// FIXME: Check that imports/extern match
for (auto& entry : externs) {
if (auto* ptr = entry.get_pointer<GlobalAddress>())
auxiliary_instance.globals().append(*ptr);
}
module.for_each_section_of_type<GlobalSection>([&](auto& global_section) {
for (auto& entry : global_section.entries()) {
auto frame = make<Frame>(
auxiliary_instance,
Vector<Value> {},
entry.expression(),
1);
Configuration config { m_store };
config.set_frame(move(frame));
auto result = config.execute();
// What if this traps?
if (result.is_trap())
instantiation_result = InstantiationError { "Global value construction trapped" };
else
global_values.append(result.values().first());
}
});
if (auto result = allocate_all(module, externs, global_values); result.is_error()) {
return result.error();
}
module.for_each_section_of_type<ElementSection>([&](const ElementSection& element_section) {
auto frame = make<Frame>(
m_module_instance,
Vector<Value> {},
element_section.function().offset(),
1);
Configuration config { m_store };
config.set_frame(move(frame));
auto result = config.execute();
// What if this traps?
VERIFY(!result.is_trap());
size_t offset = 0;
result.values().first().value().visit(
[&](const auto& value) { offset = value; },
[&](const FunctionAddress&) { VERIFY_NOT_REACHED(); },
[&](const ExternAddress&) { VERIFY_NOT_REACHED(); });
// FIXME: Module::get(*Index)
auto table_address = m_module_instance.tables().at(element_section.function().table().value());
if (auto table_instance = m_store.get(table_address)) {
auto& init = element_section.function().init();
for (size_t i = 0; i < init.size(); ++i)
table_instance->elements()[offset + i] = Reference { Reference::Func { init[i].value() } }; // HACK!
}
});
module.for_each_section_of_type<DataSection>([&](const DataSection& data_section) {
for (auto& segment : data_section.data()) {
segment.value().visit(
[&](const DataSection::Data::Active& data) {
auto frame = make<Frame>(
m_module_instance,
Vector<Value> {},
data.offset,
1);
Configuration config { m_store };
config.set_frame(move(frame));
auto result = config.execute();
size_t offset = 0;
result.values().first().value().visit(
[&](const auto& value) { offset = value; },
[&](const FunctionAddress&) { instantiation_result = InstantiationError { "Data segment offset returned an address" }; },
[&](const ExternAddress&) { instantiation_result = InstantiationError { "Data segment offset returned an address" }; });
if (instantiation_result.has_value() && instantiation_result->is_error())
return;
if (m_module_instance.memories().size() <= data.index.value()) {
instantiation_result = InstantiationError { String::formatted("Data segment referenced out-of-bounds memory ({}) of max {} entries", data.index.value(), m_module_instance.memories().size()) };
return;
}
auto address = m_module_instance.memories()[data.index.value()];
if (auto instance = m_store.get(address)) {
if (instance->type().limits().max().value_or(data.init.size() + offset + 1) <= data.init.size() + offset) {
instantiation_result = InstantiationError { String::formatted("Data segment attempted to write to out-of-bounds memory ({}) of max {} bytes", data.init.size() + offset, instance->type().limits().max().value()) };
return;
}
instance->grow(data.init.size() + offset - instance->size());
instance->data().overwrite(offset, data.init.data(), data.init.size());
}
},
[&](const DataSection::Data::Passive&) {
// FIXME: What do we do here?
});
}
});
module.for_each_section_of_type<StartSection>([&](auto&) {
instantiation_result = InstantiationError { "Start section not yet implemented" };
// FIXME: Invoke the start function.
});
return instantiation_result.value_or({});
}
InstantiationResult AbstractMachine::allocate_all(const Module& module, Vector<ExternValue>& externs, Vector<Value>& global_values)
{
Optional<InstantiationResult> result;
for (auto& entry : externs) {
entry.visit(
[&](const FunctionAddress& address) { m_module_instance.functions().append(address); },
[&](const TableAddress& address) { m_module_instance.tables().append(address); },
[&](const MemoryAddress& address) { m_module_instance.memories().append(address); },
[&](const GlobalAddress& address) { m_module_instance.globals().append(address); });
}
// FIXME: What if this fails?
for (auto& func : module.functions()) {
auto address = m_store.allocate(m_module_instance, func);
VERIFY(address.has_value());
m_module_instance.functions().append(*address);
}
module.for_each_section_of_type<TableSection>([&](const TableSection& section) {
for (auto& table : section.tables()) {
auto table_address = m_store.allocate(table.type());
VERIFY(table_address.has_value());
m_module_instance.tables().append(*table_address);
}
});
module.for_each_section_of_type<MemorySection>([&](const MemorySection& section) {
for (auto& memory : section.memories()) {
auto memory_address = m_store.allocate(memory.type());
VERIFY(memory_address.has_value());
m_module_instance.memories().append(*memory_address);
}
});
module.for_each_section_of_type<GlobalSection>([&](const GlobalSection& section) {
size_t index = 0;
for (auto& entry : section.entries()) {
auto address = m_store.allocate(entry.type(), global_values[index]);
VERIFY(address.has_value());
m_module_instance.globals().append(*address);
index++;
}
});
module.for_each_section_of_type<ExportSection>([&](const ExportSection& section) {
for (auto& entry : section.entries()) {
Variant<FunctionAddress, TableAddress, MemoryAddress, GlobalAddress, Empty> address { Empty {} };
entry.description().visit(
[&](const FunctionIndex& index) {
if (m_module_instance.functions().size() > index.value())
address = FunctionAddress { m_module_instance.functions()[index.value()] };
else
dbgln("Failed to export '{}', the exported address ({}) was out of bounds (min: 0, max: {})", entry.name(), index.value(), m_module_instance.functions().size());
},
[&](const TableIndex& index) {
if (m_module_instance.tables().size() > index.value())
address = TableAddress { m_module_instance.tables()[index.value()] };
else
dbgln("Failed to export '{}', the exported address ({}) was out of bounds (min: 0, max: {})", entry.name(), index.value(), m_module_instance.tables().size());
},
[&](const MemoryIndex& index) {
if (m_module_instance.memories().size() > index.value())
address = MemoryAddress { m_module_instance.memories()[index.value()] };
else
dbgln("Failed to export '{}', the exported address ({}) was out of bounds (min: 0, max: {})", entry.name(), index.value(), m_module_instance.memories().size());
},
[&](const GlobalIndex& index) {
if (m_module_instance.globals().size() > index.value())
address = GlobalAddress { m_module_instance.globals()[index.value()] };
else
dbgln("Failed to export '{}', the exported address ({}) was out of bounds (min: 0, max: {})", entry.name(), index.value(), m_module_instance.globals().size());
});
if (address.has<Empty>()) {
result = InstantiationError { "An export could not be resolved" };
continue;
}
m_module_instance.exports().append(ExportInstance {
entry.name(),
move(address).downcast<FunctionAddress, TableAddress, MemoryAddress, GlobalAddress>(),
});
}
});
return result.value_or({});
}
Result AbstractMachine::invoke(FunctionAddress address, Vector<Value> arguments)
{
return Configuration { m_store }.call(address, move(arguments));
}
}

View file

@ -0,0 +1,391 @@
/*
* Copyright (c) 2021, Ali Mohammad Pur <mpfard@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/OwnPtr.h>
#include <AK/Result.h>
#include <LibWasm/Types.h>
namespace Wasm {
struct InstantiationError {
String error { "Unknown error" };
};
using InstantiationResult = Result<void, InstantiationError>;
TYPEDEF_DISTINCT_NUMERIC_GENERAL(u64, true, true, false, false, false, true, FunctionAddress);
TYPEDEF_DISTINCT_NUMERIC_GENERAL(u64, true, true, false, false, false, true, ExternAddress);
TYPEDEF_DISTINCT_NUMERIC_GENERAL(u64, true, true, false, false, false, true, TableAddress);
TYPEDEF_DISTINCT_NUMERIC_GENERAL(u64, true, true, false, false, false, true, GlobalAddress);
TYPEDEF_DISTINCT_NUMERIC_GENERAL(u64, true, true, false, false, false, true, MemoryAddress);
// FIXME: These should probably be made generic/virtual if/when we decide to do something more
// fancy than just a dumb interpreter.
class Value {
public:
using AnyValueType = Variant<i32, i64, float, double, FunctionAddress, ExternAddress>;
explicit Value(AnyValueType value)
: m_value(move(value))
, m_type(ValueType::I32)
{
if (m_value.has<i32>())
m_type = ValueType { ValueType::I32 };
else if (m_value.has<i64>())
m_type = ValueType { ValueType::I64 };
else if (m_value.has<float>())
m_type = ValueType { ValueType::F32 };
else if (m_value.has<double>())
m_type = ValueType { ValueType::F64 };
else if (m_value.has<FunctionAddress>())
m_type = ValueType { ValueType::FunctionReference };
else if (m_value.has<ExternAddress>())
m_type = ValueType { ValueType::ExternReference };
else
VERIFY_NOT_REACHED();
}
template<typename T>
requires(sizeof(T) <= sizeof(u64)) explicit Value(ValueType type, T raw_value)
: m_value(0)
, m_type(type)
{
switch (type.kind()) {
case ValueType::Kind::ExternReference:
m_value = ExternAddress { bit_cast<u64>(raw_value) };
break;
case ValueType::Kind::FunctionReference:
m_value = FunctionAddress { bit_cast<u64>(raw_value) };
break;
case ValueType::Kind::I32:
m_value = static_cast<i32>(bit_cast<i64>(raw_value));
break;
case ValueType::Kind::I64:
m_value = static_cast<i64>(bit_cast<u64>(raw_value));
break;
case ValueType::Kind::F32:
m_value = static_cast<float>(bit_cast<double>(raw_value));
break;
case ValueType::Kind::F64:
m_value = bit_cast<double>(raw_value);
break;
default:
VERIFY_NOT_REACHED();
}
}
Value(const Value& value)
: m_value(AnyValueType { value.m_value })
, m_type(value.m_type)
{
}
Value(Value&& value)
: m_value(move(value.m_value))
, m_type(move(value.m_type))
{
}
auto& type() const { return m_type; }
auto& value() const { return m_value; }
private:
AnyValueType m_value;
ValueType m_type;
};
struct Trap {
// Empty value type
};
class Result {
public:
explicit Result(Vector<Value> values)
: m_values(move(values))
{
}
Result(Trap)
: m_is_trap(true)
{
}
auto& values() const { return m_values; }
auto is_trap() const { return m_is_trap; }
private:
Vector<Value> m_values;
bool m_is_trap { false };
};
using ExternValue = Variant<FunctionAddress, TableAddress, MemoryAddress, GlobalAddress>;
class ExportInstance {
public:
explicit ExportInstance(String name, ExternValue value)
: m_name(move(name))
, m_value(move(value))
{
}
auto& name() const { return m_name; }
auto& value() const { return m_value; }
private:
String m_name;
ExternValue m_value;
};
class ModuleInstance {
public:
explicit ModuleInstance(
Vector<FunctionType> types, Vector<FunctionAddress> function_addresses, Vector<TableAddress> table_addresses,
Vector<MemoryAddress> memory_addresses, Vector<GlobalAddress> global_addresses, Vector<ExportInstance> exports)
: m_types(move(types))
, m_functions(move(function_addresses))
, m_tables(move(table_addresses))
, m_memories(move(memory_addresses))
, m_globals(move(global_addresses))
, m_exports(move(exports))
{
}
ModuleInstance() = default;
auto& types() const { return m_types; }
auto& functions() const { return m_functions; }
auto& tables() const { return m_tables; }
auto& memories() const { return m_memories; }
auto& globals() const { return m_globals; }
auto& exports() const { return m_exports; }
auto& types() { return m_types; }
auto& functions() { return m_functions; }
auto& tables() { return m_tables; }
auto& memories() { return m_memories; }
auto& globals() { return m_globals; }
auto& exports() { return m_exports; }
private:
Vector<FunctionType> m_types;
Vector<FunctionAddress> m_functions;
Vector<TableAddress> m_tables;
Vector<MemoryAddress> m_memories;
Vector<GlobalAddress> m_globals;
Vector<ExportInstance> m_exports;
};
class WasmFunction {
public:
explicit WasmFunction(const FunctionType& type, const ModuleInstance& module, const Module::Function& code)
: m_type(type)
, m_module(module)
, m_code(code)
{
}
auto& type() const { return m_type; }
auto& module() const { return m_module; }
auto& code() const { return m_code; }
private:
const FunctionType& m_type;
const ModuleInstance& m_module;
const Module::Function& m_code;
};
class HostFunction {
public:
explicit HostFunction(FlatPtr ptr, const FunctionType& type)
: m_ptr(ptr)
, m_type(type)
{
}
auto ptr() const { return m_ptr; }
auto& type() const { return m_type; }
private:
FlatPtr m_ptr { 0 };
const FunctionType& m_type;
};
using FunctionInstance = Variant<WasmFunction, HostFunction>;
class Reference {
public:
struct Null {
ValueType type;
};
struct Func {
FunctionAddress address;
};
struct Extern {
ExternAddress address;
};
using RefType = Variant<Null, Func, Extern>;
explicit Reference(RefType ref)
: m_ref(move(ref))
{
}
auto& ref() const { return m_ref; }
private:
RefType m_ref;
};
class TableInstance {
public:
explicit TableInstance(const TableType& type, Vector<Optional<Reference>> elements)
: m_elements(move(elements))
, m_type(type)
{
}
auto& elements() const { return m_elements; }
auto& elements() { return m_elements; }
auto& type() const { return m_type; }
private:
Vector<Optional<Reference>> m_elements;
const TableType& m_type;
};
class MemoryInstance {
public:
explicit MemoryInstance(const MemoryType& type)
: m_type(type)
{
grow(m_type.limits().min());
}
auto& type() const { return m_type; }
auto size() const { return m_size; }
auto& data() const { return m_data; }
auto& data() { return m_data; }
void grow(size_t new_size) { m_data.grow(new_size); }
private:
const MemoryType& m_type;
size_t m_size { 0 };
ByteBuffer m_data;
};
class GlobalInstance {
public:
explicit GlobalInstance(Value value, bool is_mutable)
: m_mutable(is_mutable)
, m_value(move(value))
{
}
auto is_mutable() const { return m_mutable; }
auto& value() const { return m_value; }
private:
bool m_mutable { false };
Value m_value;
};
class Store {
public:
Store() = default;
Optional<FunctionAddress> allocate(ModuleInstance& module, const Module::Function& function);
Optional<FunctionAddress> allocate(const HostFunction&);
Optional<TableAddress> allocate(const TableType&);
Optional<MemoryAddress> allocate(const MemoryType&);
Optional<GlobalAddress> allocate(const GlobalType&, Value);
FunctionInstance* get(FunctionAddress);
TableInstance* get(TableAddress);
MemoryInstance* get(MemoryAddress);
GlobalInstance* get(GlobalAddress);
private:
Vector<FunctionInstance> m_functions;
Vector<TableInstance> m_tables;
Vector<MemoryInstance> m_memories;
Vector<GlobalInstance> m_globals;
};
class Label {
public:
explicit Label(InstructionPointer continuation)
: m_continuation(continuation)
{
}
auto continuation() const { return m_continuation; }
private:
InstructionPointer m_continuation;
};
class Frame {
AK_MAKE_NONCOPYABLE(Frame);
public:
explicit Frame(const ModuleInstance& module, Vector<Value> locals, const Expression& expression, size_t arity)
: m_module(module)
, m_locals(move(locals))
, m_expression(expression)
, m_arity(arity)
{
}
auto& module() const { return m_module; }
auto& locals() const { return m_locals; }
auto& expression() const { return m_expression; }
auto arity() const { return m_arity; }
private:
const ModuleInstance& m_module;
Vector<Value> m_locals;
const Expression& m_expression;
size_t m_arity { 0 };
};
class Stack {
public:
using EntryType = Variant<NonnullOwnPtr<Value>, NonnullOwnPtr<Label>, NonnullOwnPtr<Frame>>;
Stack() = default;
[[nodiscard]] bool is_empty() const { return m_data.is_empty(); }
void push(EntryType entry) { m_data.append(move(entry)); }
auto pop() { return m_data.take_last(); }
auto& last() { return m_data.last(); }
auto size() const { return m_data.size(); }
auto& entries() const { return m_data; }
private:
Vector<EntryType> m_data;
};
class AbstractMachine {
public:
explicit AbstractMachine() = default;
// Load and instantiate a module, and link it into this interpreter.
InstantiationResult instantiate(const Module&, Vector<ExternValue>);
Result invoke(FunctionAddress, Vector<Value>);
auto& module_instance() const { return m_module_instance; }
auto& module_instance() { return m_module_instance; }
auto& store() const { return m_store; }
auto& store() { return m_store; }
private:
InstantiationResult allocate_all(const Module&, Vector<ExternValue>&, Vector<Value>& global_values);
ModuleInstance m_module_instance;
Store m_store;
};
}

View file

@ -0,0 +1,77 @@
/*
* Copyright (c) 2021, Ali Mohammad Pur <mpfard@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWasm/AbstractMachine/Configuration.h>
#include <LibWasm/AbstractMachine/Interpreter.h>
namespace Wasm {
Optional<Label> Configuration::nth_label(size_t i)
{
for (auto& entry : m_stack.entries()) {
if (auto ptr = entry.get_pointer<NonnullOwnPtr<Label>>()) {
if (i == 0)
return **ptr;
--i;
}
}
return {};
}
Result Configuration::call(FunctionAddress address, Vector<Value> arguments)
{
auto* function = m_store.get(address);
if (!function)
return Trap {};
if (auto* wasm_function = function->get_pointer<WasmFunction>()) {
Vector<Value> locals;
locals.ensure_capacity(arguments.size() + wasm_function->code().locals().size());
for (auto& value : arguments)
locals.append(Value { value });
for (auto& type : wasm_function->code().locals())
locals.empend(type, 0ull);
auto frame = make<Frame>(
wasm_function->module(),
move(locals),
wasm_function->code().body(),
wasm_function->type().results().size());
set_frame(move(frame));
return execute();
}
// It better be a host function, else something is really wrong.
auto& host_function = function->get<HostFunction>();
auto result = bit_cast<HostFunctionType>(host_function.ptr())(m_store, arguments);
auto count = host_function.type().results().size();
if (count == 0)
return Result { Vector<Value> {} };
if (count == 1)
return Result { Vector<Value> { Value { host_function.type().results().first(), result } } };
TODO();
}
Result Configuration::execute()
{
Interpreter interpreter;
interpreter.interpret(*this);
Vector<NonnullOwnPtr<Value>> results;
for (size_t i = 0; i < m_current_frame->arity(); ++i)
results.append(move(stack().pop().get<NonnullOwnPtr<Value>>()));
auto label = stack().pop();
// ASSERT: label == current frame
if (!label.has<NonnullOwnPtr<Label>>())
return Trap {};
Vector<Value> results_moved;
results_moved.ensure_capacity(results.size());
for (auto& entry : results)
results_moved.unchecked_append(move(*entry));
return Result { move(results_moved) };
}
}

View file

@ -0,0 +1,49 @@
/*
* Copyright (c) 2021, Ali Mohammad Pur <mpfard@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibWasm/AbstractMachine/AbstractMachine.h>
namespace Wasm {
typedef u64 (*HostFunctionType)(Store&, Vector<Value>&);
class Configuration {
public:
explicit Configuration(Store& store)
: m_store(store)
{
}
Optional<Label> nth_label(size_t);
void set_frame(NonnullOwnPtr<Frame> frame)
{
m_current_frame = frame.ptr();
m_stack.push(move(frame));
m_stack.push(make<Label>(m_current_frame->expression().instructions().size() - 1));
}
auto& frame() const { return m_current_frame; }
auto& frame() { return m_current_frame; }
auto& ip() const { return m_ip; }
auto& ip() { return m_ip; }
auto& depth() const { return m_depth; }
auto& depth() { return m_depth; }
auto& stack() const { return m_stack; }
auto& stack() { return m_stack; }
Result call(FunctionAddress, Vector<Value> arguments);
Result execute();
private:
Store& m_store;
Frame* m_current_frame { nullptr };
Stack m_stack;
size_t m_depth { 0 };
InstructionPointer m_ip;
};
}

View file

@ -0,0 +1,23 @@
/*
* Copyright (c) 2021, Ali Mohammad Pur <mpfard@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWasm/AbstractMachine/AbstractMachine.h>
#include <LibWasm/AbstractMachine/Configuration.h>
#include <LibWasm/AbstractMachine/Interpreter.h>
#include <LibWasm/Opcode.h>
namespace Wasm {
void Interpreter::interpret(Configuration& configuration)
{
// FIXME: Interpret stuff
dbgln("FIXME: Interpret stuff!");
// Push some dummy values
for (size_t i = 0; i < configuration.frame()->arity(); ++i)
configuration.stack().push(make<Value>(0));
}
}

View file

@ -0,0 +1,17 @@
/*
* Copyright (c) 2021, Ali Mohammad Pur <mpfard@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibWasm/AbstractMachine/Configuration.h>
namespace Wasm {
struct Interpreter {
void interpret(Configuration&);
};
}

View file

@ -1,4 +1,7 @@
set(SOURCES
AbstractMachine/AbstractMachine.cpp
AbstractMachine/Configuration.cpp
AbstractMachine/Interpreter.cpp
Parser/Parser.cpp
Printer/Printer.cpp
)

View file

@ -7,6 +7,7 @@
#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>
@ -14,12 +15,19 @@ int main(int argc, char* argv[])
{
const char* filename = nullptr;
bool print = false;
bool attempt_instantiate = false;
bool attemp_execute = false;
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(attemp_execute, "Attempt to execute a function from the module (implies -i)", "execute", 'e');
parser.parse(argc, argv);
if (attemp_execute)
attempt_instantiate = true;
auto result = Core::File::open(filename, Core::OpenMode::ReadOnly);
if (result.is_error()) {
warnln("Failed to open {}: {}", filename, result.error());
@ -34,11 +42,85 @@ int main(int argc, char* argv[])
return 2;
}
if (print) {
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;
auto result = machine.instantiate(parse_result.value(), {});
if (result.is_error()) {
warnln("Module instantiation failed: {}", result.error().error);
return 1;
}
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 : machine.module_instance().functions()) {
print_func(address);
}
}
if (attemp_execute) {
Optional<Wasm::FunctionAddress> run_address;
Vector<Wasm::Value> values;
// Pick a function that takes no args :P
for (auto& address : machine.module_instance().functions()) {
auto fn = machine.store().get(address);
if (!fn)
continue;
if (auto ptr = fn->get_pointer<Wasm::WasmFunction>()) {
const Wasm::FunctionType& ty = ptr->type();
for (auto& param : ty.parameters()) {
values.append(Wasm::Value { param, 0ull });
}
run_address = address;
break;
}
}
if (!run_address.has_value()) {
warnln("No nullary function, sorry :(");
return 1;
}
outln("Executing ");
print_func(*run_address);
outln();
auto result = machine.invoke(run_address.value(), move(values));
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;
}