/* * Copyright (c) 2021, Ali Mohammad Pur * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include #include #include #include #include #include #include #include #include namespace Wasm { template using NativeIntegralType = Conditional>>>; template using NativeFloatingType = Conditional>; template typename SetSign, typename ElementType = SetSign>> using NativeVectorType __attribute__((vector_size(N * sizeof(ElementType)))) = ElementType; template> using NativeFloatingVectorType __attribute__((vector_size(N * sizeof(ElementType)))) = ElementType; template typename SetSign> using Native128ByteVectorOf = NativeVectorType; enum class ParseError { UnexpectedEof, UnknownInstruction, ExpectedFloatingImmediate, ExpectedIndex, ExpectedKindTag, ExpectedSignedImmediate, ExpectedSize, ExpectedValueOrTerminator, InvalidImmediate, InvalidIndex, InvalidInput, InvalidModuleMagic, InvalidModuleVersion, InvalidSize, InvalidTag, InvalidType, HugeAllocationRequested, OutOfMemory, SectionSizeMismatch, InvalidUtf8, DuplicateSection, SectionOutOfOrder, }; ByteString parse_error_to_byte_string(ParseError); template using ParseResult = ErrorOr; AK_TYPEDEF_DISTINCT_ORDERED_ID(size_t, TypeIndex); AK_TYPEDEF_DISTINCT_ORDERED_ID(size_t, FunctionIndex); AK_TYPEDEF_DISTINCT_ORDERED_ID(size_t, TableIndex); AK_TYPEDEF_DISTINCT_ORDERED_ID(size_t, ElementIndex); AK_TYPEDEF_DISTINCT_ORDERED_ID(size_t, MemoryIndex); AK_TYPEDEF_DISTINCT_ORDERED_ID(size_t, LocalIndex); AK_TYPEDEF_DISTINCT_ORDERED_ID(size_t, GlobalIndex); AK_TYPEDEF_DISTINCT_ORDERED_ID(size_t, LabelIndex); AK_TYPEDEF_DISTINCT_ORDERED_ID(size_t, DataIndex); AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(u64, InstructionPointer, Arithmetic, Comparison, Flags, Increment); ParseError with_eof_check(Stream const& stream, ParseError error_if_not_eof); template struct GenericIndexParser { static ParseResult parse(Stream& stream) { auto value_or_error = stream.read_value>(); if (value_or_error.is_error()) return with_eof_check(stream, ParseError::ExpectedIndex); size_t value = value_or_error.release_value(); return T { value }; } }; class ReconsumableStream : public Stream { public: explicit ReconsumableStream(Stream& stream) : m_stream(stream) { } void unread(ReadonlyBytes data) { m_buffer.append(data.data(), data.size()); } private: virtual ErrorOr read_some(Bytes bytes) override { auto original_bytes = bytes; size_t bytes_read_from_buffer = 0; if (!m_buffer.is_empty()) { auto read_size = min(bytes.size(), m_buffer.size()); m_buffer.span().slice(0, read_size).copy_to(bytes); bytes = bytes.slice(read_size); for (size_t i = 0; i < read_size; ++i) m_buffer.take_first(); bytes_read_from_buffer = read_size; } return original_bytes.trim(TRY(m_stream.read_some(bytes)).size() + bytes_read_from_buffer); } virtual bool is_eof() const override { return m_buffer.is_empty() && m_stream.is_eof(); } virtual ErrorOr discard(size_t count) override { size_t bytes_discarded_from_buffer = 0; if (!m_buffer.is_empty()) { auto read_size = min(count, m_buffer.size()); for (size_t i = 0; i < read_size; ++i) m_buffer.take_first(); bytes_discarded_from_buffer = read_size; } return m_stream.discard(count - bytes_discarded_from_buffer); } virtual ErrorOr write_some(ReadonlyBytes) override { return Error::from_errno(EBADF); } virtual bool is_open() const override { return m_stream.is_open(); } virtual void close() override { m_stream.close(); } Stream& m_stream; Vector m_buffer; }; // https://webassembly.github.io/spec/core/bikeshed/#value-types%E2%91%A2 class ValueType { public: enum Kind { I32, I64, F32, F64, V128, FunctionReference, ExternReference, }; explicit ValueType(Kind kind) : m_kind(kind) { } bool operator==(ValueType const&) const = default; auto is_reference() const { return m_kind == ExternReference || m_kind == FunctionReference; } auto is_vector() const { return m_kind == V128; } auto is_numeric() const { return !is_reference() && !is_vector(); } auto kind() const { return m_kind; } static ParseResult parse(Stream& stream); static ByteString kind_name(Kind kind) { switch (kind) { case I32: return "i32"; case I64: return "i64"; case F32: return "f32"; case F64: return "f64"; case V128: return "v128"; case FunctionReference: return "funcref"; case ExternReference: return "externref"; } VERIFY_NOT_REACHED(); } private: Kind m_kind; }; // https://webassembly.github.io/spec/core/bikeshed/#result-types%E2%91%A2 class ResultType { public: explicit ResultType(Vector types) : m_types(move(types)) { } auto const& types() const { return m_types; } static ParseResult parse(Stream& stream); private: Vector m_types; }; // https://webassembly.github.io/spec/core/bikeshed/#function-types%E2%91%A4 class FunctionType { public: FunctionType(Vector parameters, Vector results) : m_parameters(move(parameters)) , m_results(move(results)) { } auto& parameters() const { return m_parameters; } auto& results() const { return m_results; } static ParseResult parse(Stream& stream); private: Vector m_parameters; Vector m_results; }; // https://webassembly.github.io/spec/core/bikeshed/#limits%E2%91%A5 class Limits { public: explicit Limits(u32 min, Optional max = {}) : m_min(min) , m_max(move(max)) { } auto min() const { return m_min; } auto& max() const { return m_max; } bool is_subset_of(Limits other) const { return m_min >= other.min() && (!other.max().has_value() || (m_max.has_value() && *m_max <= *other.max())); } static ParseResult parse(Stream& stream); private: u32 m_min { 0 }; Optional m_max; }; // https://webassembly.github.io/spec/core/bikeshed/#memory-types%E2%91%A4 class MemoryType { public: explicit MemoryType(Limits limits) : m_limits(move(limits)) { } auto& limits() const { return m_limits; } static ParseResult parse(Stream& stream); private: Limits m_limits; }; // https://webassembly.github.io/spec/core/bikeshed/#table-types%E2%91%A4 class TableType { public: explicit TableType(ValueType element_type, Limits limits) : m_element_type(element_type) , m_limits(move(limits)) { VERIFY(m_element_type.is_reference()); } auto& limits() const { return m_limits; } auto& element_type() const { return m_element_type; } static ParseResult parse(Stream& stream); private: ValueType m_element_type; Limits m_limits; }; // https://webassembly.github.io/spec/core/bikeshed/#global-types%E2%91%A4 class GlobalType { public: GlobalType(ValueType type, bool is_mutable) : m_type(type) , m_is_mutable(is_mutable) { } auto& type() const { return m_type; } auto is_mutable() const { return m_is_mutable; } static ParseResult parse(Stream& stream); private: ValueType m_type; bool m_is_mutable { false }; }; // https://webassembly.github.io/spec/core/bikeshed/#binary-blocktype class BlockType { public: enum Kind { Empty, Type, Index, }; BlockType() : m_kind(Empty) , m_empty(0) { } explicit BlockType(ValueType type) : m_kind(Type) , m_value_type(type) { } explicit BlockType(TypeIndex index) : m_kind(Index) , m_type_index(index) { } auto kind() const { return m_kind; } auto& value_type() const { VERIFY(kind() == Type); return m_value_type; } auto& type_index() const { VERIFY(kind() == Index); return m_type_index; } static ParseResult parse(Stream& stream); private: Kind m_kind { Empty }; union { ValueType m_value_type; TypeIndex m_type_index; u8 m_empty; }; }; // https://webassembly.github.io/spec/core/bikeshed/#binary-instr // https://webassembly.github.io/spec/core/bikeshed/#reference-instructions%E2%91%A6 // https://webassembly.github.io/spec/core/bikeshed/#parametric-instructions%E2%91%A6 // https://webassembly.github.io/spec/core/bikeshed/#variable-instructions%E2%91%A6 // https://webassembly.github.io/spec/core/bikeshed/#table-instructions%E2%91%A6 // https://webassembly.github.io/spec/core/bikeshed/#memory-instructions%E2%91%A6 // https://webassembly.github.io/spec/core/bikeshed/#numeric-instructions%E2%91%A6 class Instruction { public: explicit Instruction(OpCode opcode) : m_opcode(opcode) , m_arguments(static_cast(0)) { } struct TableElementArgs { ElementIndex element_index; TableIndex table_index; }; struct TableTableArgs { TableIndex lhs; TableIndex rhs; }; struct StructuredInstructionArgs { BlockType block_type; InstructionPointer end_ip; Optional else_ip; }; struct TableBranchArgs { Vector labels; LabelIndex default_; }; struct IndirectCallArgs { TypeIndex type; TableIndex table; }; struct MemoryArgument { u32 align; u32 offset; MemoryIndex memory_index { 0 }; }; struct MemoryAndLaneArgument { MemoryArgument memory; u8 lane; }; struct LaneIndex { u8 lane; }; // Proposal "multi-memory" struct MemoryCopyArgs { MemoryIndex src_index; MemoryIndex dst_index; }; struct MemoryInitArgs { DataIndex data_index; MemoryIndex memory_index; }; struct MemoryIndexArgument { MemoryIndex memory_index; }; struct ShuffleArgument { explicit ShuffleArgument(u8 (&lanes)[16]) : lanes { lanes[0], lanes[1], lanes[2], lanes[3], lanes[4], lanes[5], lanes[6], lanes[7], lanes[8], lanes[9], lanes[10], lanes[11], lanes[12], lanes[13], lanes[14], lanes[15] } { } u8 lanes[16]; }; template explicit Instruction(OpCode opcode, T argument) : m_opcode(opcode) , m_arguments(move(argument)) { } static ParseResult parse(Stream& stream); auto& opcode() const { return m_opcode; } auto& arguments() const { return m_arguments; } auto& arguments() { return m_arguments; } private: OpCode m_opcode { 0 }; Variant< BlockType, DataIndex, ElementIndex, FunctionIndex, GlobalIndex, IndirectCallArgs, LabelIndex, LaneIndex, LocalIndex, MemoryArgument, MemoryAndLaneArgument, MemoryCopyArgs, MemoryIndexArgument, MemoryInitArgs, StructuredInstructionArgs, ShuffleArgument, TableBranchArgs, TableElementArgs, TableIndex, TableTableArgs, ValueType, Vector, double, float, i32, i64, u128, u8> // Empty state m_arguments; }; struct SectionId { public: enum class SectionIdKind : u8 { Custom, Type, Import, Function, Table, Memory, Global, Export, Start, Element, DataCount, Code, Data, }; explicit SectionId(SectionIdKind kind) : m_kind(kind) { } SectionIdKind kind() const { return m_kind; } static ParseResult parse(Stream& stream); private: SectionIdKind m_kind; }; class CustomSection { public: CustomSection(ByteString name, ByteBuffer contents) : m_name(move(name)) , m_contents(move(contents)) { } auto& name() const { return m_name; } auto& contents() const { return m_contents; } static ParseResult parse(Stream& stream); private: ByteString m_name; ByteBuffer m_contents; }; class TypeSection { public: TypeSection() = default; explicit TypeSection(Vector types) : m_types(move(types)) { } auto& types() const { return m_types; } static ParseResult parse(Stream& stream); private: Vector m_types; }; class ImportSection { public: class Import { public: using ImportDesc = Variant; Import(ByteString module, ByteString name, ImportDesc description) : m_module(move(module)) , m_name(move(name)) , m_description(move(description)) { } auto& module() const { return m_module; } auto& name() const { return m_name; } auto& description() const { return m_description; } static ParseResult parse(Stream& stream); private: template static ParseResult parse_with_type(auto&& stream, auto&& module, auto&& name) { auto result = TRY(T::parse(stream)); return Import { module, name, result }; } ByteString m_module; ByteString m_name; ImportDesc m_description; }; public: ImportSection() = default; explicit ImportSection(Vector imports) : m_imports(move(imports)) { } auto& imports() const { return m_imports; } static ParseResult parse(Stream& stream); private: Vector m_imports; }; class FunctionSection { public: FunctionSection() = default; explicit FunctionSection(Vector types) : m_types(move(types)) { } auto& types() const { return m_types; } static ParseResult parse(Stream& stream); private: Vector m_types; }; class TableSection { public: class Table { public: explicit Table(TableType type) : m_type(move(type)) { } auto& type() const { return m_type; } static ParseResult parse(Stream& stream); private: TableType m_type; }; public: TableSection() = default; explicit TableSection(Vector
tables) : m_tables(move(tables)) { } auto& tables() const { return m_tables; } static ParseResult parse(Stream& stream); private: Vector
m_tables; }; class MemorySection { public: class Memory { public: explicit Memory(MemoryType type) : m_type(move(type)) { } auto& type() const { return m_type; } static ParseResult parse(Stream& stream); private: MemoryType m_type; }; public: MemorySection() = default; explicit MemorySection(Vector memories) : m_memories(move(memories)) { } auto& memories() const { return m_memories; } static ParseResult parse(Stream& stream); private: Vector m_memories; }; class Expression { public: explicit Expression(Vector instructions) : m_instructions(move(instructions)) { } auto& instructions() const { return m_instructions; } static ParseResult parse(Stream& stream, Optional size_hint = {}); private: Vector m_instructions; }; class GlobalSection { public: class Global { public: explicit Global(GlobalType type, Expression expression) : m_type(move(type)) , m_expression(move(expression)) { } auto& type() const { return m_type; } auto& expression() const { return m_expression; } static ParseResult parse(Stream& stream); private: GlobalType m_type; Expression m_expression; }; public: GlobalSection() = default; explicit GlobalSection(Vector entries) : m_entries(move(entries)) { } auto& entries() const { return m_entries; } static ParseResult parse(Stream& stream); private: Vector m_entries; }; class ExportSection { private: using ExportDesc = Variant; public: class Export { public: explicit Export(ByteString name, ExportDesc description) : m_name(move(name)) , m_description(move(description)) { } auto& name() const { return m_name; } auto& description() const { return m_description; } static ParseResult parse(Stream& stream); private: ByteString m_name; ExportDesc m_description; }; ExportSection() = default; explicit ExportSection(Vector entries) : m_entries(move(entries)) { } auto& entries() const { return m_entries; } static ParseResult parse(Stream& stream); private: Vector m_entries; }; class StartSection { public: class StartFunction { public: explicit StartFunction(FunctionIndex index) : m_index(index) { } auto& index() const { return m_index; } static ParseResult parse(Stream& stream); private: FunctionIndex m_index; }; StartSection() = default; explicit StartSection(Optional func) : m_function(move(func)) { } auto& function() const { return m_function; } static ParseResult parse(Stream& stream); private: Optional m_function; }; class ElementSection { public: struct Active { TableIndex index; Expression expression; }; struct Declarative { }; struct Passive { }; struct Element { static ParseResult parse(Stream&); ValueType type; Vector init; Variant mode; }; ElementSection() = default; explicit ElementSection(Vector segs) : m_segments(move(segs)) { } auto& segments() const { return m_segments; } static ParseResult parse(Stream& stream); private: Vector m_segments; }; class Locals { public: explicit Locals(u32 n, ValueType type) : m_n(n) , m_type(type) { } // Yikes... auto n() const { return m_n; } auto& type() const { return m_type; } static ParseResult parse(Stream& stream); private: u32 m_n { 0 }; ValueType m_type; }; class CodeSection { public: // https://webassembly.github.io/spec/core/bikeshed/#binary-func class Func { public: explicit Func(Vector locals, Expression body) : m_locals(move(locals)) , m_body(move(body)) { } auto& locals() const { return m_locals; } auto& body() const { return m_body; } static ParseResult parse(Stream& stream, size_t size_hint); private: Vector m_locals; Expression m_body; }; class Code { public: explicit Code(u32 size, Func func) : m_size(size) , m_func(move(func)) { } auto size() const { return m_size; } auto& func() const { return m_func; } static ParseResult parse(Stream& stream); private: u32 m_size { 0 }; Func m_func; }; CodeSection() = default; explicit CodeSection(Vector funcs) : m_functions(move(funcs)) { } auto& functions() const { return m_functions; } static ParseResult parse(Stream& stream); private: Vector m_functions; }; class DataSection { public: class Data { public: struct Passive { Vector init; }; struct Active { Vector init; MemoryIndex index; Expression offset; }; using Value = Variant; explicit Data(Value value) : m_value(move(value)) { } auto& value() const { return m_value; } static ParseResult parse(Stream& stream); private: Value m_value; }; DataSection() = default; explicit DataSection(Vector data) : m_data(move(data)) { } auto& data() const { return m_data; } static ParseResult parse(Stream& stream); private: Vector m_data; }; class DataCountSection { public: DataCountSection() = default; explicit DataCountSection(Optional count) : m_count(move(count)) { } auto& count() const { return m_count; } static ParseResult parse(Stream& stream); private: Optional m_count; }; class Module : public RefCounted , public Weakable { public: enum class ValidationStatus { Unchecked, Invalid, Valid, }; static constexpr Array wasm_magic { 0, 'a', 's', 'm' }; static constexpr Array wasm_version { 1, 0, 0, 0 }; Module() = default; auto& custom_sections() { return m_custom_sections; } auto& custom_sections() const { return m_custom_sections; } auto& type_section() const { return m_type_section; } auto& type_section() { return m_type_section; } auto& import_section() const { return m_import_section; } auto& import_section() { return m_import_section; } auto& function_section() { return m_function_section; } auto& function_section() const { return m_function_section; } auto& table_section() { return m_table_section; } auto& table_section() const { return m_table_section; } auto& memory_section() { return m_memory_section; } auto& memory_section() const { return m_memory_section; } auto& global_section() { return m_global_section; } auto& global_section() const { return m_global_section; } auto& export_section() { return m_export_section; } auto& export_section() const { return m_export_section; } auto& start_section() { return m_start_section; } auto& start_section() const { return m_start_section; } auto& element_section() { return m_element_section; } auto& element_section() const { return m_element_section; } auto& code_section() { return m_code_section; } auto& code_section() const { return m_code_section; } auto& data_section() { return m_data_section; } auto& data_section() const { return m_data_section; } auto& data_count_section() { return m_data_count_section; } auto& data_count_section() const { return m_data_count_section; } void set_validation_status(ValidationStatus status, Badge) { set_validation_status(status); } ValidationStatus validation_status() const { return m_validation_status; } StringView validation_error() const { return *m_validation_error; } void set_validation_error(ByteString error) { m_validation_error = move(error); } static ParseResult> parse(Stream& stream); private: void set_validation_status(ValidationStatus status) { m_validation_status = status; } Vector m_custom_sections; TypeSection m_type_section; ImportSection m_import_section; FunctionSection m_function_section; TableSection m_table_section; MemorySection m_memory_section; GlobalSection m_global_section; ExportSection m_export_section; StartSection m_start_section; ElementSection m_element_section; CodeSection m_code_section; DataSection m_data_section; DataCountSection m_data_count_section; ValidationStatus m_validation_status { ValidationStatus::Unchecked }; Optional m_validation_error; }; }