summaryrefslogtreecommitdiff
path: root/Userland
diff options
context:
space:
mode:
authorAli Mohammad Pur <ali.mpfard@gmail.com>2021-05-01 01:08:51 +0430
committerLinus Groh <mail@linusgroh.de>2021-05-13 19:44:32 +0100
commit4d9246ac9decfd22c3f6ceee0a90877f38679ca2 (patch)
tree7c225a55d26d4f0e569df016a5b2c5d501e9ab55 /Userland
parent2b755f1fbfd96efb9e053780a332b68371a17e6e (diff)
downloadserenity-4d9246ac9decfd22c3f6ceee0a90877f38679ca2.zip
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. :^)
Diffstat (limited to 'Userland')
-rw-r--r--Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.cpp295
-rw-r--r--Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.h391
-rw-r--r--Userland/Libraries/LibWasm/AbstractMachine/Configuration.cpp77
-rw-r--r--Userland/Libraries/LibWasm/AbstractMachine/Configuration.h49
-rw-r--r--Userland/Libraries/LibWasm/AbstractMachine/Interpreter.cpp23
-rw-r--r--Userland/Libraries/LibWasm/AbstractMachine/Interpreter.h17
-rw-r--r--Userland/Libraries/LibWasm/CMakeLists.txt3
-rw-r--r--Userland/Utilities/wasm.cpp84
8 files changed, 938 insertions, 1 deletions
diff --git a/Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.cpp b/Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.cpp
new file mode 100644
index 0000000000..c22d60d379
--- /dev/null
+++ b/Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.cpp
@@ -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));
+}
+
+}
diff --git a/Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.h b/Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.h
new file mode 100644
index 0000000000..f97b747a1f
--- /dev/null
+++ b/Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.h
@@ -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;
+};
+
+}
diff --git a/Userland/Libraries/LibWasm/AbstractMachine/Configuration.cpp b/Userland/Libraries/LibWasm/AbstractMachine/Configuration.cpp
new file mode 100644
index 0000000000..6666f8598f
--- /dev/null
+++ b/Userland/Libraries/LibWasm/AbstractMachine/Configuration.cpp
@@ -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) };
+}
+
+}
diff --git a/Userland/Libraries/LibWasm/AbstractMachine/Configuration.h b/Userland/Libraries/LibWasm/AbstractMachine/Configuration.h
new file mode 100644
index 0000000000..c8a944dffc
--- /dev/null
+++ b/Userland/Libraries/LibWasm/AbstractMachine/Configuration.h
@@ -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;
+};
+
+}
diff --git a/Userland/Libraries/LibWasm/AbstractMachine/Interpreter.cpp b/Userland/Libraries/LibWasm/AbstractMachine/Interpreter.cpp
new file mode 100644
index 0000000000..da4148b2e7
--- /dev/null
+++ b/Userland/Libraries/LibWasm/AbstractMachine/Interpreter.cpp
@@ -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));
+}
+
+}
diff --git a/Userland/Libraries/LibWasm/AbstractMachine/Interpreter.h b/Userland/Libraries/LibWasm/AbstractMachine/Interpreter.h
new file mode 100644
index 0000000000..2c3426fc84
--- /dev/null
+++ b/Userland/Libraries/LibWasm/AbstractMachine/Interpreter.h
@@ -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&);
+};
+
+}
diff --git a/Userland/Libraries/LibWasm/CMakeLists.txt b/Userland/Libraries/LibWasm/CMakeLists.txt
index 0c2ef0754f..ae57176a39 100644
--- a/Userland/Libraries/LibWasm/CMakeLists.txt
+++ b/Userland/Libraries/LibWasm/CMakeLists.txt
@@ -1,4 +1,7 @@
set(SOURCES
+ AbstractMachine/AbstractMachine.cpp
+ AbstractMachine/Configuration.cpp
+ AbstractMachine/Interpreter.cpp
Parser/Parser.cpp
Printer/Printer.cpp
)
diff --git a/Userland/Utilities/wasm.cpp b/Userland/Utilities/wasm.cpp
index f583741aee..2f8516ec82 100644
--- a/Userland/Utilities/wasm.cpp
+++ b/Userland/Utilities/wasm.cpp
@@ -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;
}