Kaynağa Gözat

LibWasm: Start implementing a basic WebAssembly binary format parser

This can currently parse a really simple module.
Note that it cannot parse the DataCount section, and it's still missing
almost all of the instructions.
This commit also adds a 'wasm' test utility that tries to parse a given
webassembly binary file.
It currently does nothing but exit when the parse fails, but it's a
start :^)
Ali Mohammad Pur 4 yıl önce
ebeveyn
işleme
aa4d8d26b9

+ 5 - 0
AK/Debug.h.in

@@ -394,6 +394,10 @@
 #cmakedefine01 UPDATE_COALESCING_DEBUG
 #endif
 
+#ifndef WASM_BINPARSER_DEBUG
+#cmakedefine01 WASM_BINPARSER_DEBUG
+#endif
+
 #ifndef WINDOWMANAGER_DEBUG
 #cmakedefine01 WINDOWMANAGER_DEBUG
 #endif
@@ -409,3 +413,4 @@
 #ifndef WSSCREEN_DEBUG
 #cmakedefine01 WSSCREEN_DEBUG
 #endif
+

+ 1 - 0
Meta/CMake/all_the_debug_macros.cmake

@@ -175,6 +175,7 @@ set(RSA_PARSE_DEBUG ON)
 set(LINE_EDITOR_DEBUG ON)
 set(LANGUAGE_SERVER_DEBUG ON)
 set(GL_DEBUG ON)
+set(WASM_BINPARSER_DEBUG ON)
 
 # False positive: DEBUG is a flag but it works differently.
 # set(DEBUG ON)

+ 1 - 0
Userland/Libraries/CMakeLists.txt

@@ -39,6 +39,7 @@ add_subdirectory(LibThread)
 add_subdirectory(LibTLS)
 add_subdirectory(LibTTF)
 add_subdirectory(LibVT)
+add_subdirectory(LibWasm)
 add_subdirectory(LibWeb)
 add_subdirectory(LibWebSocket)
 add_subdirectory(LibX86)

+ 6 - 0
Userland/Libraries/LibWasm/CMakeLists.txt

@@ -0,0 +1,6 @@
+set(SOURCES
+    Parser/Parser.cpp
+)
+
+serenity_lib(LibWasm wasm)
+target_link_libraries(LibWasm LibC LibCore)

+ 37 - 0
Userland/Libraries/LibWasm/Constants.h

@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2021, Ali Mohammad Pur <mpfard@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/Types.h>
+
+namespace Wasm::Constants {
+
+// Value
+static constexpr auto i32_tag = 0x7f;
+static constexpr auto i64_tag = 0x7e;
+static constexpr auto f32_tag = 0x7d;
+static constexpr auto f64_tag = 0x7c;
+static constexpr auto function_reference_tag = 0x70;
+static constexpr auto extern_reference_tag = 0x6f;
+
+// Function
+static constexpr auto function_signature_tag = 0x60;
+
+// Global
+static constexpr auto const_tag = 0x00;
+static constexpr auto var_tag = 0x01;
+
+// Block
+static constexpr auto empty_block_tag = 0x40;
+
+// Import section
+static constexpr auto extern_function_tag = 0x00;
+static constexpr auto extern_table_tag = 0x01;
+static constexpr auto extern_memory_tag = 0x02;
+static constexpr auto extern_global_tag = 0x03;
+
+}

+ 202 - 0
Userland/Libraries/LibWasm/Opcode.h

@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) 2021, Ali Mohammad Pur <mpfard@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/DistinctNumeric.h>
+
+namespace Wasm {
+
+TYPEDEF_DISTINCT_ORDERED_ID(u8, OpCode);
+
+namespace Instructions {
+
+static constexpr OpCode unreachable = 0x00,
+                        nop = 0x01,
+                        block = 0x02,
+                        loop = 0x03,
+                        if_ = 0x04,
+                        br = 0x0c,
+                        br_if = 0x0d,
+                        br_table = 0x0e,
+                        return_ = 0x0f,
+                        call = 0x10,
+                        call_indirect = 0x11,
+                        drop = 0x1a,
+                        select = 0x1b,
+                        select_typed = 0x1c,
+                        local_get = 0x20,
+                        local_set = 0x21,
+                        local_tee = 0x22,
+                        global_get = 0x23,
+                        global_set = 0x24,
+                        table_get = 0x25,
+                        table_set = 0x26,
+                        i32_load = 0x28,
+                        i64_load = 0x29,
+                        f32_load = 0x2a,
+                        f64_load = 0x2b,
+                        i32_load8_s = 0x2c,
+                        i32_load8_u = 0x2d,
+                        i32_load16_s = 0x2e,
+                        i32_load16_u = 0x2f,
+                        i64_load8_s = 0x30,
+                        i64_load8_u = 0x31,
+                        i64_load16_s = 0x32,
+                        i64_load16_u = 0x33,
+                        i64_load32_s = 0x34,
+                        i64_load32_u = 0x35,
+                        i32_store = 0x36,
+                        i64_store = 0x37,
+                        f32_store = 0x38,
+                        f64_store = 0x39,
+                        i32_store8 = 0x3a,
+                        i32_store16 = 0x3b,
+                        i64_store8 = 0x3c,
+                        i64_store16 = 0x3d,
+                        i64_store32 = 0x3e,
+                        memory_size = 0x3f,
+                        memory_grow = 0x40,
+                        i32_const = 0x41,
+                        i64_const = 0x42,
+                        f32_const = 0x43,
+                        f64_const = 0x44,
+                        i32_eqz = 0x45,
+                        i32_eq = 0x46,
+                        i32_ne = 0x47,
+                        i32_lts = 0x48,
+                        i32_ltu = 0x49,
+                        i32_gts = 0x4a,
+                        i32_gtu = 0x4b,
+                        i32_les = 0x4c,
+                        i32_leu = 0x4d,
+                        i32_ges = 0x4e,
+                        i32_geu = 0x4f,
+                        i64_eqz = 0x50,
+                        i64_eq = 0x51,
+                        i64_ne = 0x52,
+                        i64_lts = 0x53,
+                        i64_ltu = 0x54,
+                        i64_gts = 0x55,
+                        i64_gtu = 0x56,
+                        i64_les = 0x57,
+                        i64_leu = 0x58,
+                        i64_ges = 0x59,
+                        i64_geu = 0x5a,
+                        f32_eq = 0x5b,
+                        f32_ne = 0x5c,
+                        f32_lt = 0x5d,
+                        f32_gt = 0x5e,
+                        f32_le = 0x5f,
+                        f32_ge = 0x60,
+                        f64_eq = 0x61,
+                        f64_ne = 0x62,
+                        f64_lt = 0x63,
+                        f64_gt = 0x64,
+                        f64_le = 0x65,
+                        f64_ge = 0x66,
+                        i32_clz = 0x67,
+                        i32_ctz = 0x68,
+                        i32_popcnt = 0x69,
+                        i32_add = 0x6a,
+                        i32_sub = 0x6b,
+                        i32_mul = 0x6c,
+                        i32_divs = 0x6d,
+                        i32_divu = 0x6e,
+                        i32_rems = 0x6f,
+                        i32_remu = 0x70,
+                        i32_and = 0x71,
+                        i32_or = 0x72,
+                        i32_xor = 0x73,
+                        i32_shl = 0x74,
+                        i32_shrs = 0x75,
+                        i32_shru = 0x76,
+                        i32_rotl = 0x77,
+                        i32_rotr = 0x78,
+                        i64_clz = 0x79,
+                        i64_ctz = 0x7a,
+                        i64_popcnt = 0x7b,
+                        i64_add = 0x7c,
+                        i64_sub = 0x7d,
+                        i64_mul = 0x7e,
+                        i64_divs = 0x7f,
+                        i64_divu = 0x80,
+                        i64_rems = 0x81,
+                        i64_remu = 0x82,
+                        i64_and = 0x83,
+                        i64_or = 0x84,
+                        i64_xor = 0x85,
+                        i64_shl = 0x86,
+                        i64_shrs = 0x87,
+                        i64_shru = 0x88,
+                        i64_rotl = 0x89,
+                        i64_rotr = 0x8a,
+                        f32_abs = 0x8b,
+                        f32_neg = 0x8c,
+                        f32_ceil = 0x8d,
+                        f32_floor = 0x8e,
+                        f32_trunc = 0x8f,
+                        f32_nearest = 0x90,
+                        f32_sqrt = 0x91,
+                        f32_add = 0x92,
+                        f32_sub = 0x93,
+                        f32_mul = 0x94,
+                        f32_div = 0x95,
+                        f32_min = 0x96,
+                        f32_max = 0x97,
+                        f32_copysign = 0x98,
+                        f64_abs = 0x99,
+                        f64_neg = 0x9a,
+                        f64_ceil = 0x9b,
+                        f64_floor = 0x9c,
+                        f64_trunc = 0x9d,
+                        f64_nearest = 0x9e,
+                        f64_sqrt = 0x9f,
+                        f64_add = 0xa0,
+                        f64_sub = 0xa1,
+                        f64_mul = 0xa2,
+                        f64_div = 0xa3,
+                        f64_min = 0xa4,
+                        f64_max = 0xa5,
+                        f64_copysign = 0xa6,
+                        i32_wrap_i64 = 0xa7,
+                        i32_trunc_sf32 = 0xa8,
+                        i32_trunc_uf32 = 0xa9,
+                        i32_trunc_sf64 = 0xaa,
+                        i32_trunc_uf64 = 0xab,
+                        i64_extend_si32 = 0xac,
+                        i64_extend_ui32 = 0xad,
+                        i64_trunc_sf32 = 0xae,
+                        i64_trunc_uf32 = 0xaf,
+                        i64_trunc_sf64 = 0xb0,
+                        i64_trunc_uf64 = 0xb1,
+                        f32_convert_si32 = 0xb2,
+                        f32_convert_ui32 = 0xb3,
+                        f32_convert_si64 = 0xb4,
+                        f32_convert_ui64 = 0xb5,
+                        f32_demote_f64 = 0xb6,
+                        f64_convert_si32 = 0xb7,
+                        f64_convert_ui32 = 0xb8,
+                        f64_convert_si64 = 0xb9,
+                        f64_convert_ui64 = 0xba,
+                        f64_promote_f32 = 0xbb,
+                        i32_reinterpret_f32 = 0xbc,
+                        i64_reinterpret_f64 = 0xbd,
+                        f32_reinterpret_i32 = 0xbe,
+                        f64_reinterpret_i64 = 0xbf,
+                        table_init = 0xfc,
+                        table_drop = 0xfc,
+                        table_copy = 0xfc,
+                        table_grow = 0xfc,
+                        table_size = 0xfc,
+                        table_fill = 0xfc,
+                        memory_init = 0xfc,
+                        data_drop = 0xfc,
+                        memory_copy = 0xfc,
+                        memory_fill = 0xfc;
+}
+
+}

+ 772 - 0
Userland/Libraries/LibWasm/Parser/Parser.cpp

@@ -0,0 +1,772 @@
+/*
+ * Copyright (c) 2021, Ali Mohammad Pur <mpfard@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <AK/LEB128.h>
+#include <AK/ScopeLogger.h>
+#include <LibWasm/Types.h>
+
+namespace Wasm {
+
+template<typename T>
+static ParseResult<Vector<T>> parse_vector(InputStream& stream)
+{
+    ScopeLogger<WASM_BINPARSER_DEBUG> logger;
+    size_t count;
+    if (!LEB128::read_unsigned(stream, count))
+        return ParseError::InvalidInput;
+
+    Vector<T> entries;
+    for (size_t i = 0; i < count; ++i) {
+        if constexpr (IsSame<T, size_t>) {
+            size_t value;
+            if (!LEB128::read_unsigned(stream, value))
+                return ParseError::InvalidInput;
+            entries.append(value);
+        } else if constexpr (IsSame<T, ssize_t>) {
+            ssize_t value;
+            if (!LEB128::read_signed(stream, value))
+                return ParseError::InvalidInput;
+            entries.append(value);
+        } else if constexpr (IsSame<T, u8>) {
+            entries.resize(count);
+            if (!stream.read_or_error({ entries.data(), entries.size() }))
+                return ParseError::InvalidInput;
+            break; // Note: We read this all in one go!
+        } else {
+            auto result = T::parse(stream);
+            if (result.is_error())
+                return result.error();
+            entries.append(result.release_value());
+        }
+    }
+
+    return entries;
+}
+
+static ParseResult<String> parse_name(InputStream& stream)
+{
+    ScopeLogger<WASM_BINPARSER_DEBUG> logger;
+    auto data = parse_vector<u8>(stream);
+    if (data.is_error())
+        return data.error();
+
+    return String::copy(data.value());
+}
+
+template<typename T>
+struct ParseUntilAnyOfResult {
+    u8 terminator { 0 };
+    Vector<T> values;
+};
+template<typename T, typename... Args>
+static ParseResult<ParseUntilAnyOfResult<T>> parse_until_any_of(InputStream& stream, Args... terminators) requires(requires(InputStream& stream) { T::parse(stream); })
+{
+    ScopeLogger<WASM_BINPARSER_DEBUG> logger;
+    ReconsumableStream new_stream { stream };
+    ParseUntilAnyOfResult<T> result;
+    for (;;) {
+        u8 byte;
+        new_stream >> byte;
+        if (new_stream.has_any_error())
+            return ParseError::InvalidInput;
+
+        if ((... || (byte == terminators))) {
+            result.terminator = byte;
+            return result;
+        }
+
+        new_stream.unread({ &byte, 1 });
+        auto parse_result = T::parse(new_stream);
+        if (parse_result.is_error())
+            return parse_result.error();
+
+        result.values.append(parse_result.release_value());
+    }
+}
+
+ParseResult<ValueType> ValueType::parse(InputStream& stream)
+{
+    ScopeLogger<WASM_BINPARSER_DEBUG> logger("ValueType");
+    u8 tag;
+    stream >> tag;
+    if (stream.has_any_error())
+        return ParseError::InvalidInput;
+    switch (tag) {
+    case Constants::i32_tag:
+        return ValueType(I32);
+    case Constants::i64_tag:
+        return ValueType(I64);
+    case Constants::f32_tag:
+        return ValueType(F32);
+    case Constants::f64_tag:
+        return ValueType(F64);
+    case Constants::function_reference_tag:
+        return ValueType(FunctionReference);
+    case Constants::extern_reference_tag:
+        return ValueType(ExternReference);
+    default:
+        return ParseError::InvalidInput;
+    }
+}
+
+ParseResult<ResultType> ResultType::parse(InputStream& stream)
+{
+    ScopeLogger<WASM_BINPARSER_DEBUG> logger("ResultType");
+    auto types = parse_vector<ValueType>(stream);
+    if (types.is_error())
+        return types.error();
+    return ResultType { types.release_value() };
+}
+
+ParseResult<FunctionType> FunctionType::parse(InputStream& stream)
+{
+    ScopeLogger<WASM_BINPARSER_DEBUG> logger("FunctionType");
+    u8 tag;
+    stream >> tag;
+    if (stream.has_any_error())
+        return ParseError::InvalidInput;
+
+    if (tag != Constants::function_signature_tag) {
+        dbgln("Expected 0x60, but found 0x{:x}", tag);
+        return ParseError::InvalidInput;
+    }
+
+    auto parameters_result = parse_vector<ValueType>(stream);
+    if (parameters_result.is_error())
+        return parameters_result.error();
+    auto results_result = parse_vector<ValueType>(stream);
+    if (results_result.is_error())
+        return results_result.error();
+
+    return FunctionType { parameters_result.release_value(), results_result.release_value() };
+}
+
+ParseResult<Limits> Limits::parse(InputStream& stream)
+{
+    ScopeLogger<WASM_BINPARSER_DEBUG> logger("Limits");
+    u8 flag;
+    stream >> flag;
+    if (stream.has_any_error())
+        return ParseError::InvalidInput;
+
+    if (flag > 1)
+        return ParseError::InvalidInput;
+
+    size_t min;
+    if (!LEB128::read_unsigned(stream, min))
+        return ParseError::InvalidInput;
+
+    Optional<u32> max;
+    if (flag) {
+        size_t value;
+        if (LEB128::read_unsigned(stream, value))
+            return ParseError::InvalidInput;
+        max = value;
+    }
+
+    return Limits { static_cast<u32>(min), move(max) };
+}
+
+ParseResult<MemoryType> MemoryType::parse(InputStream& stream)
+{
+    ScopeLogger<WASM_BINPARSER_DEBUG> logger("MemoryType");
+    auto limits_result = Limits::parse(stream);
+    if (limits_result.is_error())
+        return limits_result.error();
+    return MemoryType { limits_result.release_value() };
+}
+
+ParseResult<TableType> TableType::parse(InputStream& stream)
+{
+    ScopeLogger<WASM_BINPARSER_DEBUG> logger("TableType");
+    auto type_result = ValueType::parse(stream);
+    if (type_result.is_error())
+        return type_result.error();
+    if (!type_result.value().is_reference())
+        return ParseError::InvalidInput;
+    auto limits_result = Limits::parse(stream);
+    if (limits_result.is_error())
+        return limits_result.error();
+    return TableType { type_result.release_value(), limits_result.release_value() };
+}
+
+ParseResult<GlobalType> GlobalType::parse(InputStream& stream)
+{
+    ScopeLogger<WASM_BINPARSER_DEBUG> logger("GlobalType");
+    auto type_result = ValueType::parse(stream);
+    if (type_result.is_error())
+        return type_result.error();
+    u8 mutable_;
+    stream >> mutable_;
+
+    if (stream.has_any_error())
+        return ParseError::InvalidInput;
+
+    if (mutable_ > 1)
+        return ParseError::InvalidInput;
+
+    return GlobalType { type_result.release_value(), mutable_ == 0x01 };
+}
+
+ParseResult<BlockType> BlockType::parse(InputStream& stream)
+{
+    ScopeLogger<WASM_BINPARSER_DEBUG> logger("BlockType");
+    u8 kind;
+    stream >> kind;
+    if (stream.has_any_error())
+        return ParseError::InvalidInput;
+    if (kind == Constants::empty_block_tag)
+        return BlockType {};
+
+    {
+        InputMemoryStream value_stream { ReadonlyBytes { &kind, 1 } };
+        if (auto value_type = ValueType::parse(value_stream); !value_type.is_error())
+            return BlockType { value_type.release_value() };
+    }
+
+    ReconsumableStream new_stream { stream };
+    new_stream.unread({ &kind, 1 });
+
+    ssize_t index_value;
+    if (!LEB128::read_signed(new_stream, index_value))
+        return ParseError::InvalidInput;
+
+    if (index_value < 0)
+        return ParseError::InvalidInput;
+
+    return BlockType { TypeIndex(index_value) };
+}
+
+ParseResult<Instruction> Instruction::parse(InputStream& stream)
+{
+    ScopeLogger<WASM_BINPARSER_DEBUG> logger("Instruction");
+    u8 byte;
+    stream >> byte;
+    if (stream.has_any_error())
+        return ParseError::InvalidInput;
+    OpCode opcode { byte };
+
+    if (opcode == Instructions::block || opcode == Instructions::loop || opcode == Instructions::if_) {
+        auto block_type = BlockType::parse(stream);
+        if (block_type.is_error())
+            return block_type.error();
+        auto result = parse_until_any_of<Instruction>(stream, 0x0b, 0x05);
+        if (result.is_error())
+            return result.error();
+
+        if (result.value().terminator == 0x0b) {
+            // block/loop/if without else
+            NonnullOwnPtrVector<Instruction> instructions;
+            for (auto& entry : result.value().values)
+                instructions.append(make<Instruction>(move(entry)));
+
+            return Instruction { opcode, BlockAndInstructionSet { block_type.release_value(), move(instructions) } };
+        }
+
+        VERIFY(result.value().terminator == 0x05);
+        NonnullOwnPtrVector<Instruction> left_instructions, right_instructions;
+        for (auto& entry : result.value().values)
+            left_instructions.append(make<Instruction>(move(entry)));
+        // if with else
+        {
+            auto result = parse_until_any_of<Instruction>(stream, 0x0b);
+            if (result.is_error())
+                return result.error();
+
+            for (auto& entry : result.value().values)
+                right_instructions.append(make<Instruction>(move(entry)));
+
+            return Instruction { opcode, BlockAndTwoInstructionSets { block_type.release_value(), move(left_instructions), move(right_instructions) } };
+        }
+    }
+
+    // FIXME: Parse all the other instructions
+    TODO();
+}
+
+ParseResult<CustomSection> CustomSection::parse(InputStream& stream)
+{
+    ScopeLogger<WASM_BINPARSER_DEBUG> logger("CustomSection");
+    auto name = parse_name(stream);
+    if (name.is_error())
+        return name.error();
+
+    auto data_buffer = ByteBuffer::create_uninitialized(64);
+    while (!stream.has_any_error() && !stream.unreliable_eof()) {
+        char buf[16];
+        auto size = stream.read({ buf, 16 });
+        if (size == 0)
+            break;
+        data_buffer.append(buf, size);
+    }
+
+    return CustomSection(name.release_value(), move(data_buffer));
+}
+
+ParseResult<TypeSection> TypeSection::parse(InputStream& stream)
+{
+    ScopeLogger<WASM_BINPARSER_DEBUG> logger("TypeSection");
+    auto types = parse_vector<FunctionType>(stream);
+    if (types.is_error())
+        return types.error();
+    return TypeSection { types.release_value() };
+}
+
+ParseResult<ImportSection::Import> ImportSection::Import::parse(InputStream& stream)
+{
+    ScopeLogger<WASM_BINPARSER_DEBUG> logger("Import");
+    auto module = parse_name(stream);
+    if (module.is_error())
+        return module.error();
+    auto name = parse_name(stream);
+    if (name.is_error())
+        return name.error();
+    u8 tag;
+    stream >> tag;
+    if (stream.has_any_error())
+        return ParseError::InvalidInput;
+
+    switch (tag) {
+    case Constants::extern_function_tag: {
+        size_t index;
+        if (!LEB128::read_unsigned(stream, index))
+            return ParseError::InvalidInput;
+        return Import { module.release_value(), name.release_value(), TypeIndex { index } };
+    }
+    case Constants::extern_table_tag:
+        return parse_with_type<TableType>(stream, module, name);
+    case Constants::extern_memory_tag:
+        return parse_with_type<MemoryType>(stream, module, name);
+    case Constants::extern_global_tag:
+        return parse_with_type<GlobalType>(stream, module, name);
+    default:
+        return ParseError::InvalidInput;
+    }
+}
+
+ParseResult<ImportSection> ImportSection::parse(InputStream& stream)
+{
+    ScopeLogger<WASM_BINPARSER_DEBUG> logger("ImportSection");
+    auto imports = parse_vector<Import>(stream);
+    if (imports.is_error())
+        return imports.error();
+    return ImportSection { imports.release_value() };
+}
+
+ParseResult<FunctionSection> FunctionSection::parse(InputStream& stream)
+{
+    ScopeLogger<WASM_BINPARSER_DEBUG> logger("FunctionSection");
+    auto indices = parse_vector<size_t>(stream);
+    if (indices.is_error())
+        return indices.error();
+
+    Vector<TypeIndex> typed_indices;
+    typed_indices.resize(indices.value().size());
+    for (auto entry : indices.value())
+        typed_indices.append(entry);
+
+    return FunctionSection { move(typed_indices) };
+}
+
+ParseResult<TableSection::Table> TableSection::Table::parse(InputStream& stream)
+{
+    ScopeLogger<WASM_BINPARSER_DEBUG> logger("Table");
+    auto type = TableType::parse(stream);
+    if (type.is_error())
+        return type.error();
+    return Table { type.release_value() };
+}
+
+ParseResult<TableSection> TableSection::parse(InputStream& stream)
+{
+    ScopeLogger<WASM_BINPARSER_DEBUG> logger("TableSection");
+    auto tables = parse_vector<Table>(stream);
+    if (tables.is_error())
+        return tables.error();
+    return TableSection { tables.release_value() };
+}
+
+ParseResult<MemorySection::Memory> MemorySection::Memory::parse(InputStream& stream)
+{
+    ScopeLogger<WASM_BINPARSER_DEBUG> logger("Memory");
+    auto type = MemoryType::parse(stream);
+    if (type.is_error())
+        return type.error();
+    return Memory { type.release_value() };
+}
+
+ParseResult<MemorySection> MemorySection::parse(InputStream& stream)
+{
+    ScopeLogger<WASM_BINPARSER_DEBUG> logger("MemorySection");
+    auto memorys = parse_vector<Memory>(stream);
+    if (memorys.is_error())
+        return memorys.error();
+    return MemorySection { memorys.release_value() };
+}
+
+ParseResult<Expression> Expression::parse(InputStream& stream)
+{
+    ScopeLogger<WASM_BINPARSER_DEBUG> logger("Expression");
+    auto instructions = parse_until_any_of<Instruction>(stream, 0x0b);
+    if (instructions.is_error())
+        return instructions.error();
+
+    return Expression { move(instructions.value().values) };
+}
+
+ParseResult<GlobalSection::Global> GlobalSection::Global::parse(InputStream& stream)
+{
+    ScopeLogger<WASM_BINPARSER_DEBUG> logger("Global");
+    auto type = GlobalType::parse(stream);
+    if (type.is_error())
+        return type.error();
+    auto exprs = Expression::parse(stream);
+    if (exprs.is_error())
+        return exprs.error();
+    return Global { type.release_value(), exprs.release_value() };
+}
+
+ParseResult<GlobalSection> GlobalSection::parse(InputStream& stream)
+{
+    ScopeLogger<WASM_BINPARSER_DEBUG> logger("GlobalSection");
+    auto result = parse_vector<Global>(stream);
+    if (result.is_error())
+        return result.error();
+    return GlobalSection { result.release_value() };
+}
+
+ParseResult<ExportSection::Export> ExportSection::Export::parse(InputStream& stream)
+{
+    ScopeLogger<WASM_BINPARSER_DEBUG> logger("Export");
+    auto name = parse_name(stream);
+    if (name.is_error())
+        return name.error();
+    u8 tag;
+    stream >> tag;
+    if (stream.has_any_error())
+        return ParseError::InvalidInput;
+
+    size_t index;
+    if (!LEB128::read_unsigned(stream, index))
+        return ParseError::InvalidInput;
+
+    switch (tag) {
+    case Constants::extern_function_tag:
+        return Export { name.release_value(), ExportDesc { FunctionIndex { index } } };
+    case Constants::extern_table_tag:
+        return Export { name.release_value(), ExportDesc { TableIndex { index } } };
+    case Constants::extern_memory_tag:
+        return Export { name.release_value(), ExportDesc { MemoryIndex { index } } };
+    case Constants::extern_global_tag:
+        return Export { name.release_value(), ExportDesc { GlobalIndex { index } } };
+    default:
+        return ParseError::InvalidInput;
+    }
+}
+
+ParseResult<ExportSection> ExportSection::parse(InputStream& stream)
+{
+    ScopeLogger<WASM_BINPARSER_DEBUG> logger("ExportSection");
+    auto result = parse_vector<Export>(stream);
+    if (result.is_error())
+        return result.error();
+    return ExportSection { result.release_value() };
+}
+
+ParseResult<StartSection::StartFunction> StartSection::StartFunction::parse(InputStream& stream)
+{
+    ScopeLogger<WASM_BINPARSER_DEBUG> logger("StartFunction");
+    size_t index;
+    if (!LEB128::read_unsigned(stream, index))
+        return ParseError::InvalidInput;
+    return StartFunction { FunctionIndex { index } };
+}
+
+ParseResult<StartSection> StartSection::parse(InputStream& stream)
+{
+    ScopeLogger<WASM_BINPARSER_DEBUG> logger("StartSection");
+    auto result = StartFunction::parse(stream);
+    if (result.is_error())
+        return result.error();
+    return StartSection { result.release_value() };
+}
+
+ParseResult<ElementSection::Element> ElementSection::Element::parse(InputStream& stream)
+{
+    ScopeLogger<WASM_BINPARSER_DEBUG> logger("Element");
+    size_t table_index;
+    if (!LEB128::read_unsigned(stream, table_index))
+        return ParseError::InvalidInput;
+    auto offset = Expression::parse(stream);
+    if (offset.is_error())
+        return offset.error();
+    auto init = parse_vector<size_t>(stream);
+    if (init.is_error())
+        return init.error();
+
+    Vector<FunctionIndex> typed_init;
+    typed_init.ensure_capacity(init.value().size());
+    for (auto entry : init.value())
+        typed_init.unchecked_append(entry);
+
+    return Element { TableIndex { table_index }, offset.release_value(), move(typed_init) };
+}
+
+ParseResult<ElementSection> ElementSection::parse(InputStream& stream)
+{
+    ScopeLogger<WASM_BINPARSER_DEBUG> logger("ElementSection");
+    auto result = Element::parse(stream);
+    if (result.is_error())
+        return result.error();
+    return ElementSection { result.release_value() };
+}
+
+ParseResult<Locals> Locals::parse(InputStream& stream)
+{
+    ScopeLogger<WASM_BINPARSER_DEBUG> logger("Locals");
+    size_t count;
+    if (!LEB128::read_unsigned(stream, count))
+        return ParseError::InvalidInput;
+    // TODO: Disallow too many entries.
+    auto type = ValueType::parse(stream);
+    if (type.is_error())
+        return type.error();
+
+    return Locals { static_cast<u32>(count), type.release_value() };
+}
+
+ParseResult<Func> Func::parse(InputStream& stream)
+{
+    ScopeLogger<WASM_BINPARSER_DEBUG> logger("Func");
+    auto locals = parse_vector<Locals>(stream);
+    if (locals.is_error())
+        return locals.error();
+    auto body = Expression::parse(stream);
+    if (body.is_error())
+        return body.error();
+    return Func { locals.release_value(), body.release_value() };
+}
+
+ParseResult<CodeSection::Code> CodeSection::Code::parse(InputStream& stream)
+{
+    ScopeLogger<WASM_BINPARSER_DEBUG> logger("Code");
+    size_t size;
+    if (!LEB128::read_unsigned(stream, size))
+        return ParseError::InvalidInput;
+
+    auto constrained_stream = ConstrainedStream { stream, size };
+    auto func = Func::parse(constrained_stream);
+    if (func.is_error())
+        return func.error();
+
+    return Code { static_cast<u32>(size), func.release_value() };
+}
+
+ParseResult<CodeSection> CodeSection::parse(InputStream& stream)
+{
+    ScopeLogger<WASM_BINPARSER_DEBUG> logger("CodeSection");
+    auto result = parse_vector<Code>(stream);
+    if (result.is_error())
+        return result.error();
+    return CodeSection { result.release_value() };
+}
+
+ParseResult<DataSection::Data> DataSection::Data::parse(InputStream& stream)
+{
+    ScopeLogger<WASM_BINPARSER_DEBUG> logger("Data");
+    u8 tag;
+    stream >> tag;
+    if (stream.has_any_error())
+        return ParseError::InvalidInput;
+
+    if (tag > 0x02)
+        return ParseError::InvalidInput;
+
+    if (tag == 0x00) {
+        auto expr = Expression::parse(stream);
+        if (expr.is_error())
+            return expr.error();
+        auto init = parse_vector<u8>(stream);
+        if (init.is_error())
+            return init.error();
+        return Data { Active { init.release_value(), { 0 }, expr.release_value() } };
+    }
+    if (tag == 0x01) {
+        auto init = parse_vector<u8>(stream);
+        if (init.is_error())
+            return init.error();
+        return Data { Passive { init.release_value() } };
+    }
+    if (tag == 0x02) {
+        size_t index;
+        stream >> index;
+        if (stream.has_any_error())
+            return ParseError::InvalidInput;
+        auto expr = Expression::parse(stream);
+        if (expr.is_error())
+            return expr.error();
+        auto init = parse_vector<u8>(stream);
+        if (init.is_error())
+            return init.error();
+        return Data { Active { init.release_value(), { index }, expr.release_value() } };
+    }
+    VERIFY_NOT_REACHED();
+}
+
+ParseResult<DataSection> DataSection::parse(InputStream& stream)
+{
+    ScopeLogger<WASM_BINPARSER_DEBUG> logger("DataSection");
+    auto data = parse_vector<Data>(stream);
+    if (data.is_error())
+        return data.error();
+
+    return DataSection { data.release_value() };
+}
+
+ParseResult<DataCountSection> DataCountSection::parse([[maybe_unused]] InputStream& stream)
+{
+    ScopeLogger<WASM_BINPARSER_DEBUG> logger("DataCountSection");
+    // FIXME: Implement parsing optional values!
+    return ParseError::InvalidInput;
+}
+
+ParseResult<Module> Module::parse(InputStream& stream)
+{
+    ScopeLogger<WASM_BINPARSER_DEBUG> logger("Module");
+    u8 buf[4];
+    if (!stream.read_or_error({ buf, 4 }))
+        return ParseError::InvalidInput;
+    if (Bytes { buf, 4 } != wasm_magic.span())
+        return ParseError::InvalidInput;
+
+    if (!stream.read_or_error({ buf, 4 }))
+        return ParseError::InvalidInput;
+    if (Bytes { buf, 4 } != wasm_version.span())
+        return ParseError::InvalidInput;
+
+    Vector<AnySection> sections;
+    for (;;) {
+        u8 section_id;
+        stream >> section_id;
+        if (stream.unreliable_eof()) {
+            stream.handle_any_error();
+            break;
+        }
+        if (stream.has_any_error())
+            return ParseError::InvalidInput;
+
+        size_t section_size;
+        if (!LEB128::read_unsigned(stream, section_size))
+            return ParseError::InvalidInput;
+
+        auto section_stream = ConstrainedStream { stream, section_size };
+
+        switch (section_id) {
+        case CustomSection::section_id: {
+            if (auto section = CustomSection::parse(section_stream); !section.is_error()) {
+                sections.append(section.release_value());
+                continue;
+            } else {
+                return section.error();
+            }
+        }
+        case TypeSection::section_id: {
+            if (auto section = TypeSection::parse(section_stream); !section.is_error()) {
+                sections.append(section.release_value());
+                continue;
+            } else {
+                return section.error();
+            }
+        }
+        case ImportSection::section_id: {
+            if (auto section = ImportSection::parse(section_stream); !section.is_error()) {
+                sections.append(section.release_value());
+                continue;
+            } else {
+                return section.error();
+            }
+        }
+        case FunctionSection::section_id: {
+            if (auto section = FunctionSection::parse(section_stream); !section.is_error()) {
+                sections.append(section.release_value());
+                continue;
+            } else {
+                return section.error();
+            }
+        }
+        case TableSection::section_id: {
+            if (auto section = TableSection::parse(section_stream); !section.is_error()) {
+                sections.append(section.release_value());
+                continue;
+            } else {
+                return section.error();
+            }
+        }
+        case MemorySection::section_id: {
+            if (auto section = MemorySection::parse(section_stream); !section.is_error()) {
+                sections.append(section.release_value());
+                continue;
+            } else {
+                return section.error();
+            }
+        }
+        case GlobalSection::section_id: {
+            if (auto section = GlobalSection::parse(section_stream); !section.is_error()) {
+                sections.append(section.release_value());
+                continue;
+            } else {
+                return section.error();
+            }
+        }
+        case ExportSection::section_id: {
+            if (auto section = ExportSection::parse(section_stream); !section.is_error()) {
+                sections.append(section.release_value());
+                continue;
+            } else {
+                return section.error();
+            }
+        }
+        case StartSection::section_id: {
+            if (auto section = StartSection::parse(section_stream); !section.is_error()) {
+                sections.append(section.release_value());
+                continue;
+            } else {
+                return section.error();
+            }
+        }
+        case ElementSection::section_id: {
+            if (auto section = ElementSection::parse(section_stream); !section.is_error()) {
+                sections.append(section.release_value());
+                continue;
+            } else {
+                return section.error();
+            }
+        }
+        case CodeSection::section_id: {
+            if (auto section = CodeSection::parse(section_stream); !section.is_error()) {
+                sections.append(section.release_value());
+                continue;
+            } else {
+                return section.error();
+            }
+        }
+        case DataSection::section_id: {
+            if (auto section = DataSection::parse(section_stream); !section.is_error()) {
+                sections.append(section.release_value());
+                continue;
+            } else {
+                return section.error();
+            }
+        }
+        default:
+            return ParseError::InvalidInput;
+        }
+    }
+
+    return Module { move(sections) };
+}
+
+}

+ 932 - 0
Userland/Libraries/LibWasm/Types.h

@@ -0,0 +1,932 @@
+/*
+ * Copyright (c) 2021, Ali Mohammad Pur <mpfard@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/Debug.h>
+#include <AK/DistinctNumeric.h>
+#include <AK/MemoryStream.h>
+#include <AK/NonnullOwnPtrVector.h>
+#include <AK/Result.h>
+#include <AK/String.h>
+#include <AK/Variant.h>
+#include <LibWasm/Constants.h>
+#include <LibWasm/Opcode.h>
+
+namespace Wasm {
+
+enum class ParseError {
+    // FIXME: More descriptive errors!
+    InvalidInput,
+};
+
+template<typename T>
+using ParseResult = Result<T, ParseError>;
+
+TYPEDEF_DISTINCT_ORDERED_ID(size_t, TypeIndex);
+TYPEDEF_DISTINCT_ORDERED_ID(size_t, FunctionIndex);
+TYPEDEF_DISTINCT_ORDERED_ID(size_t, TableIndex);
+TYPEDEF_DISTINCT_ORDERED_ID(size_t, MemoryIndex);
+TYPEDEF_DISTINCT_ORDERED_ID(size_t, LocalIndex);
+TYPEDEF_DISTINCT_ORDERED_ID(size_t, GlobalIndex);
+TYPEDEF_DISTINCT_ORDERED_ID(size_t, LabelIndex);
+TYPEDEF_DISTINCT_ORDERED_ID(size_t, DataIndex);
+
+class ReconsumableStream : public InputStream {
+public:
+    explicit ReconsumableStream(InputStream& stream)
+        : m_stream(stream)
+    {
+    }
+
+    void unread(ReadonlyBytes data) { m_buffer.append(data.data(), data.size()); }
+
+private:
+    size_t read(Bytes bytes) override
+    {
+        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 m_stream.read(bytes) + bytes_read_from_buffer;
+    }
+    bool unreliable_eof() const override
+    {
+        return m_buffer.is_empty() && m_stream.unreliable_eof();
+    }
+    bool read_or_error(Bytes bytes) override
+    {
+        if (read(bytes))
+            return true;
+        set_recoverable_error();
+        return false;
+    }
+    bool discard_or_error(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_or_error(count - bytes_discarded_from_buffer);
+    }
+
+    InputStream& m_stream;
+    Vector<u8, 8> m_buffer;
+};
+
+class ConstrainedStream : public InputStream {
+public:
+    explicit ConstrainedStream(InputStream& stream, size_t size)
+        : m_stream(stream)
+        , m_bytes_left(size)
+    {
+    }
+
+private:
+    size_t read(Bytes bytes) override
+    {
+        auto to_read = min(m_bytes_left, bytes.size());
+        auto nread = m_stream.read(bytes.slice(0, to_read));
+        m_bytes_left -= nread;
+        return nread;
+    }
+    bool unreliable_eof() const override
+    {
+        return m_bytes_left == 0 || m_stream.unreliable_eof();
+    }
+    bool read_or_error(Bytes bytes) override
+    {
+        if (read(bytes))
+            return true;
+        set_recoverable_error();
+        return false;
+    }
+    bool discard_or_error(size_t count) override
+    {
+        auto to_discard = min(m_bytes_left, count);
+        if (m_stream.discard_or_error(to_discard))
+            m_bytes_left -= to_discard;
+        return to_discard;
+    }
+
+    InputStream& m_stream;
+    size_t m_bytes_left { 0 };
+};
+
+// https://webassembly.github.io/spec/core/bikeshed/#value-types%E2%91%A2
+class ValueType {
+public:
+    enum Kind {
+        I32,
+        I64,
+        F32,
+        F64,
+        FunctionReference,
+        ExternReference,
+    };
+
+    explicit ValueType(Kind kind)
+        : m_kind(kind)
+    {
+    }
+
+    auto is_reference() const { return m_kind == ExternReference || m_kind == FunctionReference; }
+    auto is_numeric() const { return !is_reference(); }
+    auto kind() const { return m_kind; }
+
+    static ParseResult<ValueType> parse(InputStream& stream);
+
+    static String kind_name(Kind kind)
+    {
+        switch (kind) {
+        case I32:
+            return "i32";
+        case I64:
+            return "i64";
+        case F32:
+            return "f32";
+        case F64:
+            return "f64";
+        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<ValueType> types)
+        : m_types(move(types))
+    {
+    }
+
+    const auto& types() const { return m_types; }
+
+    static ParseResult<ResultType> parse(InputStream& stream);
+
+private:
+    Vector<ValueType> m_types;
+};
+
+// https://webassembly.github.io/spec/core/bikeshed/#function-types%E2%91%A4
+class FunctionType {
+public:
+    FunctionType(Vector<ValueType> parameters, Vector<ValueType> results)
+        : m_parameters(move(parameters))
+        , m_results(move(results))
+    {
+    }
+
+    auto& parameters() const { return m_parameters; }
+    auto& results() const { return m_results; }
+
+    static ParseResult<FunctionType> parse(InputStream& stream);
+
+private:
+    Vector<ValueType> m_parameters;
+    Vector<ValueType> m_results;
+};
+
+// https://webassembly.github.io/spec/core/bikeshed/#limits%E2%91%A5
+class Limits {
+public:
+    explicit Limits(u32 min, Optional<u32> max = {})
+        : m_min(min)
+        , m_max(move(max))
+    {
+    }
+
+    auto min() const { return m_min; }
+    auto& max() const { return m_max; }
+
+    static ParseResult<Limits> parse(InputStream& stream);
+
+private:
+    u32 m_min { 0 };
+    Optional<u32> 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<MemoryType> parse(InputStream& 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<TableType> parse(InputStream& 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<GlobalType> parse(InputStream& 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<BlockType> parse(InputStream& 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<u8>(0))
+    {
+    }
+
+    struct TableElementArgs {
+        TableIndex index;
+        ValueType element_type;
+    };
+
+    struct TableTableArgs {
+        TableIndex lhs;
+        TableIndex rhs;
+    };
+
+    struct BlockAndInstructionSet {
+        BlockType block_type;
+        NonnullOwnPtrVector<Instruction> instructions;
+    };
+
+    struct BlockAndTwoInstructionSets {
+        BlockType block_type;
+        NonnullOwnPtrVector<Instruction> left_instructions;
+        NonnullOwnPtrVector<Instruction> right_instructions;
+    };
+
+    struct TableBranchArgs {
+        Vector<LabelIndex> labels;
+        LabelIndex default_;
+    };
+
+    struct IndirectCallArgs {
+        TypeIndex type;
+        TableIndex table;
+    };
+
+    struct MemoryArgument {
+        u32 align;
+        u32 offset;
+    };
+
+    template<typename T>
+    explicit Instruction(OpCode opcode, T argument)
+        : m_opcode(opcode)
+        , m_arguments(move(argument))
+    {
+    }
+
+    static ParseResult<Instruction> parse(InputStream& stream);
+
+private:
+    OpCode m_opcode { 0 };
+    // clang-format off
+    Variant<
+        BlockAndInstructionSet,
+        BlockAndTwoInstructionSets,
+        DataIndex,
+        FunctionIndex,
+        IndirectCallArgs,
+        LabelIndex,
+        MemoryArgument,
+        TableBranchArgs,
+        TableElementArgs,
+        TableIndex,
+        TableTableArgs,
+        ValueType,
+        Vector<ValueType>,
+        double,
+        float,
+        i32,
+        i64,
+        u8 // Empty state
+    > m_arguments;
+    // clang-format on
+};
+
+class CustomSection {
+public:
+    static constexpr u8 section_id = 0;
+
+    CustomSection(String 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<CustomSection> parse(InputStream& stream);
+
+private:
+    String m_name;
+    ByteBuffer m_contents;
+};
+
+class TypeSection {
+public:
+    static constexpr u8 section_id = 1;
+
+    explicit TypeSection(Vector<FunctionType> types)
+        : m_types(move(types))
+    {
+    }
+
+    auto& types() const { return m_types; }
+
+    static ParseResult<TypeSection> parse(InputStream& stream);
+
+private:
+    Vector<FunctionType> m_types;
+};
+
+class ImportSection {
+private:
+    class Import {
+    public:
+        using ImportDesc = Variant<TypeIndex, TableType, MemoryType, GlobalType>;
+        Import(String module, String 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<Import> parse(InputStream& stream);
+
+    private:
+        template<typename T>
+        static ParseResult<Import> parse_with_type(auto&& stream, auto&& module, auto&& name)
+        {
+            auto result = T::parse(stream);
+            if (result.is_error())
+                return result.error();
+            return Import { module.release_value(), name.release_value(), result.release_value() };
+        };
+
+        String m_module;
+        String m_name;
+        ImportDesc m_description;
+    };
+
+public:
+    static constexpr u8 section_id = 2;
+
+    explicit ImportSection(Vector<Import> imports)
+        : m_imports(move(imports))
+    {
+    }
+
+    auto& imports() const { return m_imports; }
+
+    static ParseResult<ImportSection> parse(InputStream& stream);
+
+private:
+    Vector<Import> m_imports;
+};
+
+class FunctionSection {
+public:
+    static constexpr u8 section_id = 3;
+
+    explicit FunctionSection(Vector<TypeIndex> types)
+        : m_types(move(types))
+    {
+    }
+
+    auto& types() const { return m_types; }
+
+    static ParseResult<FunctionSection> parse(InputStream& stream);
+
+private:
+    Vector<TypeIndex> m_types;
+};
+
+class TableSection {
+private:
+    class Table {
+    public:
+        explicit Table(TableType type)
+            : m_type(move(type))
+        {
+        }
+
+        auto& type() const { return m_type; }
+
+        static ParseResult<Table> parse(InputStream& stream);
+
+    private:
+        TableType m_type;
+    };
+
+public:
+    static constexpr u8 section_id = 4;
+
+    explicit TableSection(Vector<Table> tables)
+        : m_tables(move(tables))
+    {
+    }
+
+    auto& tables() const { return m_tables; };
+
+    static ParseResult<TableSection> parse(InputStream& stream);
+
+private:
+    Vector<Table> m_tables;
+};
+
+class MemorySection {
+private:
+    class Memory {
+    public:
+        explicit Memory(MemoryType type)
+            : m_type(move(type))
+        {
+        }
+
+        auto& type() const { return m_type; }
+
+        static ParseResult<Memory> parse(InputStream& stream);
+
+    private:
+        MemoryType m_type;
+    };
+
+public:
+    static constexpr u8 section_id = 5;
+
+    explicit MemorySection(Vector<Memory> memorys)
+        : m_memories(move(memorys))
+    {
+    }
+
+    auto& memories() const { return m_memories; }
+
+    static ParseResult<MemorySection> parse(InputStream& stream);
+
+private:
+    Vector<Memory> m_memories;
+};
+
+class Expression {
+public:
+    explicit Expression(Vector<Instruction> instructions)
+        : m_instructions(move(instructions))
+    {
+    }
+
+    auto& instructions() const { return m_instructions; }
+
+    static ParseResult<Expression> parse(InputStream& stream);
+
+private:
+    Vector<Instruction> m_instructions;
+};
+
+class GlobalSection {
+private:
+    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<Global> parse(InputStream& stream);
+
+    private:
+        GlobalType m_type;
+        Expression m_expression;
+    };
+
+public:
+    static constexpr u8 section_id = 6;
+
+    explicit GlobalSection(Vector<Global> entries)
+        : m_entries(move(entries))
+    {
+    }
+
+    auto& entries() const { return m_entries; }
+
+    static ParseResult<GlobalSection> parse(InputStream& stream);
+
+private:
+    Vector<Global> m_entries;
+};
+
+class ExportSection {
+private:
+    using ExportDesc = Variant<FunctionIndex, TableIndex, MemoryIndex, GlobalIndex>;
+    class Export {
+    public:
+        explicit Export(String 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<Export> parse(InputStream& stream);
+
+    private:
+        String m_name;
+        ExportDesc m_description;
+    };
+
+public:
+    static constexpr u8 section_id = 7;
+
+    explicit ExportSection(Vector<Export> entries)
+        : m_entries(move(entries))
+    {
+    }
+
+    auto& entries() const { return m_entries; }
+
+    static ParseResult<ExportSection> parse(InputStream& stream);
+
+private:
+    Vector<Export> m_entries;
+};
+
+class StartSection {
+private:
+    class StartFunction {
+    public:
+        explicit StartFunction(FunctionIndex index)
+            : m_index(index)
+        {
+        }
+
+        auto& index() const { return m_index; }
+
+        static ParseResult<StartFunction> parse(InputStream& stream);
+
+    private:
+        FunctionIndex m_index;
+    };
+
+public:
+    static constexpr u8 section_id = 8;
+
+    explicit StartSection(StartFunction func)
+        : m_function(move(func))
+    {
+    }
+
+    auto& function() const { return m_function; }
+
+    static ParseResult<StartSection> parse(InputStream& stream);
+
+private:
+    StartFunction m_function;
+};
+
+class ElementSection {
+private:
+    class Element {
+    public:
+        explicit Element(TableIndex table, Expression expr, Vector<FunctionIndex> init)
+            : m_table(table)
+            , m_offset(move(expr))
+            , m_init(move(init))
+        {
+        }
+
+        auto& table() const { return m_table; }
+        auto& offset() const { return m_offset; }
+        auto& init() const { return m_init; }
+
+        static ParseResult<Element> parse(InputStream& stream);
+
+    private:
+        TableIndex m_table;
+        Expression m_offset;
+        Vector<FunctionIndex> m_init;
+    };
+
+public:
+    static constexpr u8 section_id = 9;
+
+    explicit ElementSection(Element func)
+        : m_function(move(func))
+    {
+    }
+
+    auto& function() const { return m_function; }
+
+    static ParseResult<ElementSection> parse(InputStream& stream);
+
+private:
+    Element m_function;
+};
+
+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<Locals> parse(InputStream& stream);
+
+private:
+    u32 m_n { 0 };
+    ValueType m_type;
+};
+
+// https://webassembly.github.io/spec/core/bikeshed/#binary-func
+class Func {
+public:
+    explicit Func(Vector<Locals> 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<Func> parse(InputStream& stream);
+
+private:
+    Vector<Locals> m_locals;
+    Expression m_body;
+};
+
+class CodeSection {
+private:
+    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<Code> parse(InputStream& stream);
+
+    private:
+        u32 m_size { 0 };
+        Func m_func;
+    };
+
+public:
+    static constexpr u8 section_id = 10;
+
+    explicit CodeSection(Vector<Code> funcs)
+        : m_functions(move(funcs))
+    {
+    }
+
+    auto& functions() const { return m_functions; }
+
+    static ParseResult<CodeSection> parse(InputStream& stream);
+
+private:
+    Vector<Code> m_functions;
+};
+
+class DataSection {
+private:
+    class Data {
+        struct Passive {
+            Vector<u8> init;
+        };
+        struct Active {
+            Vector<u8> init;
+            MemoryIndex index;
+            Expression offset;
+        };
+        using Value = Variant<Passive, Active>;
+
+    public:
+        explicit Data(Value value)
+            : m_value(move(value))
+        {
+        }
+
+        auto& value() const { return m_value; }
+
+        static ParseResult<Data> parse(InputStream& stream);
+
+    private:
+        Value m_value;
+    };
+
+public:
+    static constexpr u8 section_id = 11;
+
+    explicit DataSection(Vector<Data> data)
+        : m_data(move(data))
+    {
+    }
+
+    auto& data() const { return m_data; }
+
+    static ParseResult<DataSection> parse(InputStream& stream);
+
+private:
+    Vector<Data> m_data;
+};
+
+class DataCountSection {
+public:
+    static constexpr u8 section_id = 12;
+
+    explicit DataCountSection(Optional<u32> count)
+        : m_count(move(count))
+    {
+    }
+
+    auto& count() const { return m_count; }
+
+    static ParseResult<DataCountSection> parse(InputStream& stream);
+
+private:
+    Optional<u32> m_count;
+};
+
+class Module {
+private:
+    class Function {
+    public:
+        explicit Function(TypeIndex type, Vector<ValueType> local_types, Expression body)
+            : m_type(type)
+            , m_local_types(move(local_types))
+            , m_body(move(body))
+        {
+        }
+
+        auto& type() const { return m_type; }
+        auto& locals() const { return m_local_types; }
+        auto& body() const { return m_body; }
+
+    private:
+        TypeIndex m_type;
+        Vector<ValueType> m_local_types;
+        Expression m_body;
+    };
+
+    using AnySection = Variant<
+        CustomSection,
+        TypeSection,
+        ImportSection,
+        FunctionSection,
+        TableSection,
+        MemorySection,
+        GlobalSection,
+        ExportSection,
+        StartSection,
+        ElementSection,
+        CodeSection,
+        DataSection>;
+
+    static constexpr Array<u8, 4> wasm_magic { 0, 'a', 's', 'm' };
+    static constexpr Array<u8, 4> wasm_version { 1, 0, 0, 0 };
+
+public:
+    explicit Module(Vector<AnySection> sections)
+        : m_sections(move(sections))
+    {
+    }
+
+    static ParseResult<Module> parse(InputStream& stream);
+
+private:
+    Vector<AnySection> m_sections;
+};
+}

+ 1 - 0
Userland/Utilities/CMakeLists.txt

@@ -56,3 +56,4 @@ target_link_libraries(unzip LibArchive LibCompress)
 target_link_libraries(zip LibArchive LibCompress LibCrypto)
 target_link_libraries(CppParserTest LibCpp LibGUI)
 target_link_libraries(PreprocessorTest LibCpp LibGUI)
+target_link_libraries(wasm LibWasm)

+ 34 - 0
Userland/Utilities/wasm.cpp

@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2021, Ali Mohammad Pur <mpfard@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <LibCore/ArgsParser.h>
+#include <LibCore/File.h>
+#include <LibCore/FileStream.h>
+#include <LibWasm/Types.h>
+
+int main(int argc, char* argv[])
+{
+    const char* filename = nullptr;
+
+    Core::ArgsParser parser;
+    parser.add_positional_argument(filename, "File name to parse", "file");
+    parser.parse(argc, argv);
+
+    auto result = Core::File::open(filename, Core::IODevice::OpenMode::ReadOnly);
+    if (result.is_error()) {
+        warnln("Failed to open {}: {}", filename, result.error());
+        return 1;
+    }
+
+    auto stream = Core::InputFileStream(result.release_value());
+    auto parse_result = Wasm::Module::parse(stream);
+    if (parse_result.is_error()) {
+        warnln("Something went wrong, either the file is invalid, or there's a bug with LibWasm!");
+        return 2;
+    }
+
+    return 0;
+}