@@ -2,6 +2,8 @@
//
// SPDX-License-Identifier: BSD-2-Clause
#include "wasm/byte_code_parser.h"
#include "wasm/wasm.h"
#include "etest/etest.h"
@@ -21,6 +23,7 @@
using namespace std::literals;
using etest::expect_eq;
using wasm::ByteCodeParser;
namespace {
@@ -53,24 +56,24 @@ std::stringstream make_module_bytes(SectionId id, std::vector<std::uint8_t> cons
void export_section_tests() {
etest::test("export section, missing export count", [] {
auto module = wasm::Module::parse_from(make_module_bytes(SectionId::Export, {}));
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Export, {}));
expect_eq(module, tl::unexpected{wasm::ModuleParseError::InvalidExportSection});
});
etest::test("export section, missing export after count", [] {
auto module = wasm::Module::parse_from(make_module_bytes(SectionId::Export, {1}));
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Export, {1}));
expect_eq(module, tl::unexpected{wasm::ModuleParseError::InvalidExportSection});
});
etest::test("export section, empty", [] {
auto module = wasm::Module::parse_from(make_module_bytes(SectionId::Export, {0})).value();
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Export, {0})).value();
expect_eq(module.export_section, wasm::ExportSection{});
});
etest::test("export section, one", [] {
std::vector<std::uint8_t> content{1, 2, 'h', 'i', static_cast<std::uint8_t>(wasm::Export::Type::Function), 5};
auto module = wasm::Module::parse_from(make_module_bytes(SectionId::Export, content)).value();
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Export, content)).value();
expect_eq(module.export_section,
wasm::ExportSection{.exports{wasm::Export{"hi", wasm::Export::Type::Function, 5}}});
});
@@ -91,7 +94,7 @@ void export_section_tests() {
2,
};
auto module = wasm::Module::parse_from(make_module_bytes(SectionId::Export, content)).value();
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Export, content)).value();
expect_eq(module.export_section,
wasm::ExportSection{.exports{
wasm::Export{"hi", wasm::Export::Type::Function, 5},
@@ -100,74 +103,74 @@ void export_section_tests() {
});
etest::test("export section, missing name", [] {
auto module = wasm::Module::parse_from(make_module_bytes(SectionId::Export, {1, 2}));
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Export, {1, 2}));
expect_eq(module, tl::unexpected{wasm::ModuleParseError::InvalidExportSection});
});
etest::test("export section, missing type", [] {
auto module = wasm::Module::parse_from(make_module_bytes(SectionId::Export, {1, 1, 'a'}));
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Export, {1, 1, 'a'}));
expect_eq(module, tl::unexpected{wasm::ModuleParseError::InvalidExportSection});
});
etest::test("export section, missing index", [] {
auto module = wasm::Module::parse_from(make_module_bytes(SectionId::Export, {1, 1, 'a', 1}));
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Export, {1, 1, 'a', 1}));
expect_eq(module, tl::unexpected{wasm::ModuleParseError::InvalidExportSection});
});
}
void start_section_tests() {
etest::test("start section, missing start", [] {
auto module = wasm::Module::parse_from(make_module_bytes(SectionId::Start, {}));
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Start, {}));
expect_eq(module, tl::unexpected{wasm::ModuleParseError::InvalidStartSection});
});
etest::test("start section, excellent", [] {
auto module = wasm::Module::parse_from(make_module_bytes(SectionId::Start, {42})).value();
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Start, {42})).value();
expect_eq(module.start_section, wasm::StartSection{.start = 42});
});
}
void function_section_tests() {
etest::test("function section, missing data", [] {
auto module = wasm::Module::parse_from(make_module_bytes(SectionId::Function, {}));
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Function, {}));
expect_eq(module, tl::unexpected{wasm::ModuleParseError::InvalidFunctionSection});
});
etest::test("function section, empty", [] {
auto module = wasm::Module::parse_from(make_module_bytes(SectionId::Function, {0})).value();
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Function, {0})).value();
expect_eq(module.function_section, wasm::FunctionSection{});
});
etest::test("function section, missing type indices after count", [] {
auto module = wasm::Module::parse_from(make_module_bytes(SectionId::Function, {1}));
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Function, {1}));
expect_eq(module, tl::unexpected{wasm::ModuleParseError::InvalidFunctionSection});
});
etest::test("function section, good one", [] {
auto module = wasm::Module::parse_from(make_module_bytes(SectionId::Function, {2, 9, 13})).value();
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Function, {2, 9, 13})).value();
expect_eq(module.function_section, wasm::FunctionSection{.type_indices{9, 13}});
});
}
void table_section_tests() {
etest::test("table section, missing data", [] {
auto module = wasm::Module::parse_from(make_module_bytes(SectionId::Table, {}));
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Table, {}));
expect_eq(module, tl::unexpected{wasm::ModuleParseError::InvalidTableSection});
});
etest::test("table section, empty", [] {
auto module = wasm::Module::parse_from(make_module_bytes(SectionId::Table, {0})).value();
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Table, {0})).value();
expect_eq(module.table_section, wasm::TableSection{});
});
etest::test("table section, no element type", [] {
auto module = wasm::Module::parse_from(make_module_bytes(SectionId::Table, {1}));
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Table, {1}));
expect_eq(module, tl::unexpected{wasm::ModuleParseError::InvalidTableSection});
});
etest::test("table section, invalid element type", [] {
constexpr std::uint8_t kInt32Type = 0x7f;
auto module = wasm::Module::parse_from(make_module_bytes(SectionId::Table, {1, kInt32Type}));
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Table, {1, kInt32Type}));
expect_eq(module, tl::unexpected{wasm::ModuleParseError::InvalidTableSection});
});
@@ -175,22 +178,23 @@ void table_section_tests() {
static constexpr std::uint8_t kExtRefType = 0x6f;
etest::test("table section, missing limits", [] {
auto module = wasm::Module::parse_from(make_module_bytes(SectionId::Table, {1, kFuncRefType}));
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Table, {1, kFuncRefType}));
expect_eq(module, tl::unexpected{wasm::ModuleParseError::InvalidTableSection});
});
etest::test("table section, invalid has_max in limits", [] {
auto module = wasm::Module::parse_from(make_module_bytes(SectionId::Table, {1, kFuncRefType, 4}));
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Table, {1, kFuncRefType, 4}));
expect_eq(module, tl::unexpected{wasm::ModuleParseError::InvalidTableSection});
});
etest::test("table section, missing min in limits", [] {
auto module = wasm::Module::parse_from(make_module_bytes(SectionId::Table, {1, kFuncRefType, 0}));
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Table, {1, kFuncRefType, 0}));
expect_eq(module, tl::unexpected{wasm::ModuleParseError::InvalidTableSection});
});
etest::test("table section, only min", [] {
auto module = wasm::Module::parse_from(make_module_bytes(SectionId::Table, {1, kFuncRefType, 0, 42})).value();
auto module =
ByteCodeParser::parse_module(make_module_bytes(SectionId::Table, {1, kFuncRefType, 0, 42})).value();
expect_eq(module.table_section,
wasm::TableSection{.tables{
wasm::TableType{
@@ -201,13 +205,13 @@ void table_section_tests() {
});
etest::test("table section, missing max in limits", [] {
auto module = wasm::Module::parse_from(make_module_bytes(SectionId::Table, {1, kExtRefType, 1, 42}));
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Table, {1, kExtRefType, 1, 42}));
expect_eq(module, tl::unexpected{wasm::ModuleParseError::InvalidTableSection});
});
etest::test("table section, min and max", [] {
auto module =
wasm::Module::parse_from(make_module_bytes(SectionId::Table, {1, kExtRefType, 1, 42, 42})).value();
ByteCodeParser::parse_module(make_module_bytes(SectionId::Table, {1, kExtRefType, 1, 42, 42})).value();
expect_eq(module.table_section,
wasm::TableSection{.tables{
wasm::TableType{
@@ -220,32 +224,32 @@ void table_section_tests() {
void memory_section_tests() {
etest::test("memory section, missing data", [] {
auto module = wasm::Module::parse_from(make_module_bytes(SectionId::Memory, {}));
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Memory, {}));
expect_eq(module, tl::unexpected{wasm::ModuleParseError::InvalidMemorySection});
});
etest::test("memory section, empty", [] {
auto module = wasm::Module::parse_from(make_module_bytes(SectionId::Memory, {0})).value();
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Memory, {0})).value();
expect_eq(module.memory_section, wasm::MemorySection{});
});
etest::test("memory section, missing limits", [] {
auto module = wasm::Module::parse_from(make_module_bytes(SectionId::Memory, {1}));
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Memory, {1}));
expect_eq(module, tl::unexpected{wasm::ModuleParseError::InvalidMemorySection});
});
etest::test("memory section, invalid has_max in limits", [] {
auto module = wasm::Module::parse_from(make_module_bytes(SectionId::Memory, {1, 4}));
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Memory, {1, 4}));
expect_eq(module, tl::unexpected{wasm::ModuleParseError::InvalidMemorySection});
});
etest::test("memory section, missing min in limits", [] {
auto module = wasm::Module::parse_from(make_module_bytes(SectionId::Memory, {1, 0}));
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Memory, {1, 0}));
expect_eq(module, tl::unexpected{wasm::ModuleParseError::InvalidMemorySection});
});
etest::test("memory section, only min", [] {
auto module = wasm::Module::parse_from(make_module_bytes(SectionId::Memory, {1, 0, 42})).value();
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Memory, {1, 0, 42})).value();
expect_eq(module.memory_section,
wasm::MemorySection{.memories{
wasm::MemType{.min = 42},
@@ -253,12 +257,12 @@ void memory_section_tests() {
});
etest::test("memory section, missing max in limits", [] {
auto module = wasm::Module::parse_from(make_module_bytes(SectionId::Memory, {1, 1, 42}));
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Memory, {1, 1, 42}));
expect_eq(module, tl::unexpected{wasm::ModuleParseError::InvalidMemorySection});
});
etest::test("memory section, min and max", [] {
auto module = wasm::Module::parse_from(make_module_bytes(SectionId::Memory, {1, 1, 42, 42})).value();
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Memory, {1, 1, 42, 42})).value();
expect_eq(module.memory_section,
wasm::MemorySection{.memories{
wasm::Limits{.min = 42, .max = 42},
@@ -266,7 +270,8 @@ void memory_section_tests() {
});
etest::test("memory section, two memories", [] {
auto module = wasm::Module::parse_from(make_module_bytes(SectionId::Memory, {2, 1, 4, 51, 1, 19, 84})).value();
auto module =
ByteCodeParser::parse_module(make_module_bytes(SectionId::Memory, {2, 1, 4, 51, 1, 19, 84})).value();
expect_eq(module.memory_section,
wasm::MemorySection{.memories{
wasm::Limits{.min = 4, .max = 51},
@@ -277,44 +282,44 @@ void memory_section_tests() {
void type_section_tests() {
etest::test("type section, missing type data", [] {
auto module = wasm::Module::parse_from(make_module_bytes(SectionId::Type, {}));
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Type, {}));
expect_eq(module, tl::unexpected{wasm::ModuleParseError::InvalidTypeSection});
});
etest::test("type section, empty", [] {
auto module = wasm::Module::parse_from(make_module_bytes(SectionId::Type, {0})).value();
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Type, {0})).value();
expect_eq(module.type_section, wasm::TypeSection{});
});
etest::test("type section, missing type after count", [] {
auto module = wasm::Module::parse_from(make_module_bytes(SectionId::Type, {1}));
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Type, {1}));
expect_eq(module, tl::unexpected{wasm::ModuleParseError::InvalidTypeSection});
});
etest::test("type section, bad magic in function type", [] {
auto module = wasm::Module::parse_from(make_module_bytes(SectionId::Type, {1, 0x59}));
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Type, {1, 0x59}));
expect_eq(module, tl::unexpected{wasm::ModuleParseError::InvalidTypeSection});
});
etest::test("type section, one type with no parameters and no results", [] {
auto module = wasm::Module::parse_from(make_module_bytes(SectionId::Type, {1, 0x60, 0, 0})).value();
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Type, {1, 0x60, 0, 0})).value();
expect_eq(module.type_section, wasm::TypeSection{.types{wasm::FunctionType{}}});
});
etest::test("type section, eof in parameter parsing", [] {
auto module = wasm::Module::parse_from(make_module_bytes(SectionId::Type, {1, 0x60, 1}));
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Type, {1, 0x60, 1}));
expect_eq(module, tl::unexpected{wasm::ModuleParseError::InvalidTypeSection});
});
etest::test("type section, eof in result parsing", [] {
auto module = wasm::Module::parse_from(make_module_bytes(SectionId::Type, {1, 0x60, 0, 1}));
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Type, {1, 0x60, 0, 1}));
expect_eq(module, tl::unexpected{wasm::ModuleParseError::InvalidTypeSection});
});
etest::test("type section, two types", [] {
constexpr std::uint8_t kInt32Byte = 0x7f;
constexpr std::uint8_t kFloat64Byte = 0x7c;
auto module = wasm::Module::parse_from(
auto module = ByteCodeParser::parse_module(
make_module_bytes(
SectionId::Type, {2, 0x60, 0, 1, kInt32Byte, 0x60, 2, kInt32Byte, kInt32Byte, 1, kFloat64Byte}))
.value();
@@ -333,7 +338,7 @@ void type_section_tests() {
});
etest::test("type section, all types", [] {
auto module = wasm::Module::parse_from(
auto module = ByteCodeParser::parse_module(
make_module_bytes(SectionId::Type, {1, 0x60, 7, 0x7f, 0x7e, 0x7d, 0x7c, 0x7b, 0x70, 0x6f, 0}))
.value();
@@ -356,44 +361,45 @@ void type_section_tests() {
});
etest::test("type section, invalid value type", [] {
auto module = wasm::Module::parse_from(make_module_bytes(SectionId::Type, {1, 0x60, 0, 1, 0x10}));
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Type, {1, 0x60, 0, 1, 0x10}));
expect_eq(module, tl::unexpected{wasm::ModuleParseError::InvalidTypeSection});
});
}
void code_section_tests() {
etest::test("code section, missing type data", [] {
auto module = wasm::Module::parse_from(make_module_bytes(SectionId::Code, {}));
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Code, {}));
expect_eq(module, tl::unexpected{wasm::ModuleParseError::InvalidCodeSection});
});
etest::test("code section, empty", [] {
auto module = wasm::Module::parse_from(make_module_bytes(SectionId::Code, {0})).value();
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Code, {0})).value();
expect_eq(module.code_section, wasm::CodeSection{});
});
etest::test("code section, missing data after count", [] {
auto module = wasm::Module::parse_from(make_module_bytes(SectionId::Code, {1}));
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Code, {1}));
expect_eq(module, tl::unexpected{wasm::ModuleParseError::InvalidCodeSection});
});
etest::test("code section, missing local count", [] {
auto module = wasm::Module::parse_from(make_module_bytes(SectionId::Code, {1, 1, 1}));
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Code, {1, 1, 1}));
expect_eq(module, tl::unexpected{wasm::ModuleParseError::InvalidCodeSection});
});
etest::test("code section, missing local type", [] {
auto module = wasm::Module::parse_from(make_module_bytes(SectionId::Code, {1, 1, 1, 1}));
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Code, {1, 1, 1, 1}));
expect_eq(module, tl::unexpected{wasm::ModuleParseError::InvalidCodeSection});
});
etest::test("code section, not enough data", [] {
auto module = wasm::Module::parse_from(make_module_bytes(SectionId::Code, {1, 6, 1, 1, 0x7f, 4, 4}));
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Code, {1, 6, 1, 1, 0x7f, 4, 4}));
expect_eq(module, tl::unexpected{wasm::ModuleParseError::InvalidCodeSection});
});
etest::test("code section, one entry", [] {
auto module = wasm::Module::parse_from(make_module_bytes(SectionId::Code, {1, 6, 1, 1, 0x7f, 4, 4, 4})).value();
auto module =
ByteCodeParser::parse_module(make_module_bytes(SectionId::Code, {1, 6, 1, 1, 0x7f, 4, 4, 4})).value();
wasm::CodeSection expected{.entries{
wasm::CodeEntry{
@@ -405,7 +411,7 @@ void code_section_tests() {
});
etest::test("code section, two entries", [] {
auto module = wasm::Module::parse_from(
auto module = ByteCodeParser::parse_module(
make_module_bytes(SectionId::Code, {2, 6, 1, 1, 0x7f, 4, 4, 4, 9, 2, 5, 0x7e, 6, 0x7d, 7, 8, 9, 10}))
.value();
@@ -429,40 +435,41 @@ void code_section_tests() {
int main() {
etest::test("invalid magic", [] {
auto wasm_bytes = std::stringstream{"hello"};
expect_eq(wasm::Module::parse_from(wasm_bytes), tl::unexpected{wasm::ModuleParseError::InvalidMagic});
expect_eq(ByteCodeParser::parse_module(wasm_bytes), tl::unexpected{wasm::ModuleParseError::InvalidMagic});
});
etest::test("unsupported version", [] {
auto wasm_bytes = std::stringstream{"\0asm\2\0\0\0"s};
expect_eq(wasm::Module::parse_from(wasm_bytes), tl::unexpected{wasm::ModuleParseError::UnsupportedVersion});
expect_eq(ByteCodeParser::parse_module(wasm_bytes), tl::unexpected{wasm::ModuleParseError::UnsupportedVersion});
});
// https://webassembly.github.io/spec/core/syntax/modules.html
// Each of the vectors – and thus the entire module – may be empty
etest::test("empty module", [] {
auto wasm_bytes = std::stringstream{"\0asm\1\0\0\0"s};
expect_eq(wasm::Module::parse_from(std::move(wasm_bytes)), wasm::Module{});
expect_eq(ByteCodeParser::parse_module(std::move(wasm_bytes)), wasm::Module{});
});
etest::test("invalid section id", [] {
auto wasm_bytes = std::stringstream{"\0asm\1\0\0\0\x0d"s};
expect_eq(wasm::Module::parse_from(std::move(wasm_bytes)),
expect_eq(ByteCodeParser::parse_module(std::move(wasm_bytes)),
tl::unexpected{wasm::ModuleParseError::InvalidSectionId});
});
etest::test("missing size", [] {
auto wasm_bytes = std::stringstream{"\0asm\1\0\0\0\0"s};
expect_eq(
wasm::Module::parse_from(std::move(wasm_bytes)), tl::unexpected{wasm::ModuleParseError::UnexpectedEof});
expect_eq(ByteCodeParser::parse_module(std::move(wasm_bytes)),
tl::unexpected{wasm::ModuleParseError::UnexpectedEof});
});
etest::test("invalid size", [] {
auto wasm_bytes = std::stringstream{"\0asm\1\0\0\0\0\x80\x80\x80\x80\x80\x80"s};
expect_eq(wasm::Module::parse_from(std::move(wasm_bytes)), tl::unexpected{wasm::ModuleParseError::InvalidSize});
expect_eq(ByteCodeParser::parse_module(std::move(wasm_bytes)),
tl::unexpected{wasm::ModuleParseError::InvalidSize});
});
etest::test("unhandled section", [] {
expect_eq(wasm::Module::parse_from(make_module_bytes(SectionId::Custom, {})),
expect_eq(ByteCodeParser::parse_module(make_module_bytes(SectionId::Custom, {})),
tl::unexpected{wasm::ModuleParseError::UnhandledSection});
});