/* * Copyright (c) 2021, Ali Mohammad Pur * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include #include #include namespace Wasm { class Configuration; struct InstantiationError { String error { "Unknown error" }; }; struct LinkError { enum OtherErrors { InvalidImportedModule, }; Vector missing_imports; Vector other_errors; }; 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; explicit Value(AnyValueType value) : m_value(move(value)) , m_type(ValueType::I32) { if (m_value.has()) m_type = ValueType { ValueType::I32 }; else if (m_value.has()) m_type = ValueType { ValueType::I64 }; else if (m_value.has()) m_type = ValueType { ValueType::F32 }; else if (m_value.has()) m_type = ValueType { ValueType::F64 }; else if (m_value.has()) m_type = ValueType { ValueType::FunctionReference }; else if (m_value.has()) m_type = ValueType { ValueType::ExternReference }; else VERIFY_NOT_REACHED(); } template 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(raw_value) }; break; case ValueType::Kind::FunctionReference: m_value = FunctionAddress { bit_cast(raw_value) }; break; case ValueType::Kind::I32: m_value = static_cast(bit_cast(raw_value)); break; case ValueType::Kind::I64: m_value = static_cast(bit_cast(raw_value)); break; case ValueType::Kind::F32: m_value = static_cast(bit_cast(raw_value)); break; case ValueType::Kind::F64: m_value = bit_cast(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)) { } Value& operator=(Value&& value) { m_value = move(value.m_value); m_type = move(value.m_type); return *this; } template Optional to() { Optional result; m_value.visit( [&](auto value) { if constexpr (IsSame) result = value; }, [&](const FunctionAddress& address) { if constexpr (IsSame) result = address; }, [&](const ExternAddress& address) { if constexpr (IsSame) result = address; }); return result; } 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 values) : m_values(move(values)) { } Result(Trap) : m_is_trap(true) { } auto& values() const { return m_values; } auto& values() { return m_values; } auto is_trap() const { return m_is_trap; } private: Vector m_values; bool m_is_trap { false }; }; using ExternValue = Variant; 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 types, Vector function_addresses, Vector table_addresses, Vector memory_addresses, Vector global_addresses, Vector 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 m_types; Vector m_functions; Vector m_tables; Vector m_memories; Vector m_globals; Vector 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: FunctionType m_type; const ModuleInstance& m_module; const Module::Function& m_code; }; class HostFunction { public: explicit HostFunction(AK::Function&)> function, const FunctionType& type) : m_function(move(function)) , m_type(type) { } auto& function() { return m_function; } auto& type() const { return m_type; } private: AK::Function&)> m_function; FunctionType m_type; }; using FunctionInstance = Variant; class Reference { public: struct Null { ValueType type; }; struct Func { FunctionAddress address; }; struct Extern { ExternAddress address; }; using RefType = Variant; 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> 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> m_elements; const TableType& m_type; }; class MemoryInstance { public: explicit MemoryInstance(const MemoryType& type) : m_type(type) { grow(m_type.limits().min() * Constants::page_size); } auto& type() const { return m_type; } auto size() const { return m_size; } auto& data() const { return m_data; } auto& data() { return m_data; } bool grow(size_t size_to_grow) { if (size_to_grow == 0) return true; auto new_size = m_data.size() + size_to_grow; if (m_type.limits().max().value_or(new_size) < new_size) return false; auto previous_size = m_size; m_data.grow(new_size); m_size = new_size; // The spec requires that we zero out everything on grow __builtin_memset(m_data.offset_pointer(previous_size), 0, size_to_grow); return true; } 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; } void set_value(Value value) { VERIFY(is_mutable()); m_value = move(value); } private: bool m_mutable { false }; Value m_value; }; class Store { public: Store() = default; Optional allocate(ModuleInstance& module, const Module::Function& function); Optional allocate(HostFunction&&); Optional allocate(const TableType&); Optional allocate(const MemoryType&); Optional allocate(const GlobalType&, Value); FunctionInstance* get(FunctionAddress); TableInstance* get(TableAddress); MemoryInstance* get(MemoryAddress); GlobalInstance* get(GlobalAddress); private: Vector m_functions; Vector m_tables; Vector m_memories; Vector m_globals; }; class Label { public: explicit Label(size_t arity, InstructionPointer continuation) : m_arity(arity) , m_continuation(continuation) { } auto continuation() const { return m_continuation; } auto arity() const { return m_arity; } private: size_t m_arity { 0 }; InstructionPointer m_continuation; }; class Frame { AK_MAKE_NONCOPYABLE(Frame); public: explicit Frame(const ModuleInstance& module, Vector 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& locals() { return m_locals; } auto& expression() const { return m_expression; } auto arity() const { return m_arity; } private: const ModuleInstance& m_module; Vector m_locals; const Expression& m_expression; size_t m_arity { 0 }; }; class Stack { public: using EntryType = Variant, NonnullOwnPtr