mirror of
https://github.com/SerenityOS/serenity.git
synced 2025-01-22 17:31:58 -05:00
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 :^)
This commit is contained in:
parent
56a6d7924e
commit
aa4d8d26b9
10 changed files with 1991 additions and 0 deletions
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
Userland/Libraries/LibWasm/CMakeLists.txt
Normal file
6
Userland/Libraries/LibWasm/CMakeLists.txt
Normal file
|
@ -0,0 +1,6 @@
|
|||
set(SOURCES
|
||||
Parser/Parser.cpp
|
||||
)
|
||||
|
||||
serenity_lib(LibWasm wasm)
|
||||
target_link_libraries(LibWasm LibC LibCore)
|
37
Userland/Libraries/LibWasm/Constants.h
Normal file
37
Userland/Libraries/LibWasm/Constants.h
Normal file
|
@ -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
Userland/Libraries/LibWasm/Opcode.h
Normal file
202
Userland/Libraries/LibWasm/Opcode.h
Normal file
|
@ -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
Userland/Libraries/LibWasm/Parser/Parser.cpp
Normal file
772
Userland/Libraries/LibWasm/Parser/Parser.cpp
Normal file
|
@ -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
Userland/Libraries/LibWasm/Types.h
Normal file
932
Userland/Libraries/LibWasm/Types.h
Normal file
|
@ -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;
|
||||
};
|
||||
}
|
|
@ -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
Userland/Utilities/wasm.cpp
Normal file
34
Userland/Utilities/wasm.cpp
Normal file
|
@ -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;
|
||||
}
|
Loading…
Reference in a new issue