srctree

Robin Linden parent 30ddb6cf 6809b060
wasm: Implement parsing the import section

inlinesplit
wasm/byte_code_parser.cpp added: 156, removed: 5, total 151
@@ -260,6 +260,53 @@ std::optional<CodeEntry> parse(std::istream &is) {
};
}
 
// https://webassembly.github.io/spec/core/binary/modules.html#binary-import
template<>
std::optional<Import> parse(std::istream &is) {
auto module = parse<std::string>(is);
if (!module) {
return std::nullopt;
}
 
auto name = parse<std::string>(is);
if (!name) {
return std::nullopt;
}
 
std::uint8_t kind{};
if (!is.read(reinterpret_cast<char *>(&kind), sizeof(kind))) {
return std::nullopt;
}
 
std::optional<Import::Description> desc{};
switch (kind) {
case 0x00:
desc = parse<TypeIdx>(is);
break;
case 0x01:
desc = parse<TableType>(is);
break;
case 0x02:
desc = parse<MemType>(is);
break;
case 0x03:
desc = parse<GlobalType>(is);
break;
default:
break;
}
 
if (!desc) {
return std::nullopt;
}
 
return Import{
.module = *std::move(module),
.name = *std::move(name),
.description = *std::move(desc),
};
}
 
// https://webassembly.github.io/spec/core/binary/conventions.html#vectors
template<typename T>
std::optional<std::vector<T>> parse_vector(std::istream &is) {
@@ -295,6 +342,14 @@ std::optional<TypeSection> parse_type_section(std::istream &is) {
return std::nullopt;
}
 
std::optional<ImportSection> parse_import_section(std::istream &is) {
if (auto maybe_imports = parse_vector<Import>(is)) {
return ImportSection{.imports = *std::move(maybe_imports)};
}
 
return std::nullopt;
}
 
std::optional<FunctionSection> parse_function_section(std::istream &is) {
if (auto maybe_type_indices = parse_vector<TypeIdx>(is)) {
return FunctionSection{.type_indices = *std::move(maybe_type_indices)};
@@ -418,6 +473,12 @@ tl::expected<Module, ModuleParseError> ByteCodeParser::parse_module(std::istream
return tl::unexpected{ModuleParseError::InvalidTypeSection};
}
break;
case SectionId::Import:
module.import_section = parse_import_section(is);
if (!module.import_section) {
return tl::unexpected{ModuleParseError::InvalidImportSection};
}
break;
case SectionId::Function:
module.function_section = parse_function_section(is);
if (!module.function_section) {
 
wasm/byte_code_parser.h added: 156, removed: 5, total 151
@@ -20,6 +20,7 @@ enum class ModuleParseError {
InvalidSectionId,
InvalidSize,
InvalidTypeSection,
InvalidImportSection,
InvalidFunctionSection,
InvalidTableSection,
InvalidMemorySection,
@@ -44,6 +45,8 @@ constexpr std::string_view to_string(ModuleParseError e) {
return "Invalid section size";
case ModuleParseError::InvalidTypeSection:
return "Invalid type section";
case ModuleParseError::InvalidImportSection:
return "Invalid import section";
case ModuleParseError::InvalidFunctionSection:
return "Invalid function section";
case ModuleParseError::InvalidTableSection:
 
wasm/byte_code_parser_test.cpp added: 156, removed: 5, total 151
@@ -65,6 +65,7 @@ void parse_error_to_string_tests() {
expect_eq(wasm::to_string(ModuleParseError::InvalidSectionId), "Invalid section id");
expect_eq(wasm::to_string(ModuleParseError::InvalidSize), "Invalid section size");
expect_eq(wasm::to_string(ModuleParseError::InvalidTypeSection), "Invalid type section");
expect_eq(wasm::to_string(ModuleParseError::InvalidImportSection), "Invalid import section");
expect_eq(wasm::to_string(ModuleParseError::InvalidFunctionSection), "Invalid function section");
expect_eq(wasm::to_string(ModuleParseError::InvalidTableSection), "Invalid table section");
expect_eq(wasm::to_string(ModuleParseError::InvalidMemorySection), "Invalid memory section");
@@ -472,6 +473,73 @@ void type_section_tests() {
});
}
 
void import_section_tests() {
etest::test("import section, missing import count", [] {
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Import, {}));
expect_eq(module, tl::unexpected{wasm::ModuleParseError::InvalidImportSection});
});
 
etest::test("import section, empty", [] {
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Import, {0})).value();
expect_eq(module.import_section, wasm::ImportSection{});
});
 
etest::test("import section, missing module name", [] {
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Import, {1}));
expect_eq(module, tl::unexpected{wasm::ModuleParseError::InvalidImportSection});
});
 
etest::test("import section, missing field name", [] {
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Import, {1, 1, 'a'}));
expect_eq(module, tl::unexpected{wasm::ModuleParseError::InvalidImportSection});
});
 
etest::test("import section, missing import type", [] {
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Import, {1, 1, 'a', 1, 'b'}));
expect_eq(module, tl::unexpected{wasm::ModuleParseError::InvalidImportSection});
});
 
etest::test("import section, invalid import type", [] {
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Import, {1, 1, 'a', 1, 'b', 5}));
expect_eq(module, tl::unexpected{wasm::ModuleParseError::InvalidImportSection});
});
 
etest::test("import section, func", [] {
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Import, {1, 1, 'a', 1, 'b', 0, 42}));
expect_eq(module.value().import_section,
wasm::ImportSection{.imports{wasm::Import{"a", "b", wasm::TypeIdx{42}}}});
});
 
etest::test("import section, table", [] {
auto module =
ByteCodeParser::parse_module(make_module_bytes(SectionId::Import, {1, 1, 'a', 1, 'b', 1, 0x70, 0, 42}));
expect_eq(module.value().import_section,
wasm::ImportSection{.imports{
wasm::Import{"a", "b", wasm::TableType{wasm::ValueType::FunctionReference, {42}}},
}});
});
 
etest::test("import section, mem", [] {
auto module =
ByteCodeParser::parse_module(make_module_bytes(SectionId::Import, {1, 1, 'a', 1, 'b', 2, 1, 12, 13}));
expect_eq(module.value().import_section,
wasm::ImportSection{.imports{
wasm::Import{"a", "b", wasm::MemType{.min = 12, .max = 13}},
}});
});
 
etest::test("import section, global", [] {
auto module =
ByteCodeParser::parse_module(make_module_bytes(SectionId::Import, {1, 1, 'a', 1, 'b', 3, 0x7f, 0}));
expect_eq(module.value().import_section,
wasm::ImportSection{.imports{wasm::Import{
"a",
"b",
wasm::GlobalType{wasm::ValueType::Int32, wasm::GlobalType::Mutability::Const},
}}});
});
}
 
void code_section_tests() {
etest::test("code section, missing type data", [] {
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Code, {}));
@@ -586,6 +654,7 @@ int main() {
 
parse_error_to_string_tests();
type_section_tests();
import_section_tests();
function_section_tests();
table_section_tests();
memory_section_tests();
 
wasm/wasm.h added: 156, removed: 5, total 151
@@ -11,6 +11,7 @@
#include <cstdint>
#include <optional>
#include <string>
#include <variant>
#include <vector>
 
namespace wasm {
@@ -35,6 +36,16 @@ struct Global {
[[nodiscard]] bool operator==(Global const &) const = default;
};
 
struct Import {
using Description = std::variant<TypeIdx, TableType, MemType, GlobalType>;
 
std::string module{};
std::string name{};
Description description{};
 
[[nodiscard]] bool operator==(Import const &) const = default;
};
 
// https://webassembly.github.io/spec/core/binary/modules.html#type-section
struct TypeSection {
std::vector<FunctionType> types;
@@ -42,6 +53,13 @@ struct TypeSection {
[[nodiscard]] bool operator==(TypeSection const &) const = default;
};
 
// https://webassembly.github.io/spec/core/binary/modules.html#import-section
struct ImportSection {
std::vector<Import> imports;
 
[[nodiscard]] bool operator==(ImportSection const &) const = default;
};
 
// https://webassembly.github.io/spec/core/binary/modules.html#function-section
struct FunctionSection {
std::vector<TypeIdx> type_indices;
@@ -118,7 +136,7 @@ struct CodeSection {
struct Module {
// TODO(robinlinden): custom_sections
std::optional<TypeSection> type_section{};
// TODO(robinlinden): import_section
std::optional<ImportSection> import_section{};
std::optional<FunctionSection> function_section{};
std::optional<TableSection> table_section{};
std::optional<MemorySection> memory_section{};