srctree

Robin Linden parent 3b48d483 ffe78ba3
wasm: Implement parsing data sections containing passive data

inlinesplit
wasm/byte_code_parser.cpp added: 110, removed: 5, total 105
@@ -72,6 +72,15 @@ std::optional<std::uint32_t> parse(std::istream &is) {
return v ? std::optional{*v} : std::nullopt;
}
 
template<>
std::optional<std::byte> parse(std::istream &is) {
std::byte b{};
if (!is.read(reinterpret_cast<char *>(&b), sizeof(b))) {
return std::nullopt;
}
return b;
}
 
// https://webassembly.github.io/spec/core/binary/types.html
template<>
std::optional<ValueType> parse(std::istream &is) {
@@ -267,6 +276,29 @@ std::optional<CodeEntry> parse(std::istream &is) {
};
}
 
// https://webassembly.github.io/spec/core/binary/modules.html#binary-codesec
template<>
std::optional<DataSection::Data> parse(std::istream &is) {
auto type = Leb128<std::uint32_t>::decode_from(is);
if (!type) {
return std::nullopt;
}
 
static constexpr std::uint32_t kPassiveDataTag = 1;
 
if (*type == kPassiveDataTag) {
// TODO(robinlinden): We can read more than 1 byte at a time to speed this up.
auto init = parse_vector<std::byte>(is);
if (!init) {
return std::nullopt;
}
 
return DataSection::Data{DataSection::PassiveData{.data = *std::move(init)}};
}
 
return std::nullopt;
}
 
// https://webassembly.github.io/spec/core/binary/modules.html#binary-import
template<>
std::optional<Import> parse(std::istream &is) {
@@ -452,6 +484,14 @@ std::optional<CodeSection> parse_code_section(std::istream &is) {
return std::nullopt;
}
 
std::optional<DataSection> parse_data_section(std::istream &is) {
if (auto data_entries = parse_vector<DataSection::Data>(is)) {
return DataSection{.data = *std::move(data_entries)};
}
 
return std::nullopt;
}
 
} // namespace
 
tl::expected<Module, ModuleParseError> ByteCodeParser::parse_module(std::istream &is) {
@@ -592,6 +632,12 @@ tl::expected<Module, ModuleParseError> ByteCodeParser::parse_module(std::istream
return tl::unexpected{ModuleParseError::InvalidCodeSection};
}
break;
case SectionId::Data:
module.data_section = parse_data_section(is);
if (!module.data_section) {
return tl::unexpected{ModuleParseError::InvalidDataSection};
}
break;
case SectionId::DataCount: {
auto count = Leb128<std::uint32_t>::decode_from(is);
if (!count) {
 
wasm/byte_code_parser.h added: 110, removed: 5, total 105
@@ -31,6 +31,7 @@ enum class ModuleParseError : std::uint8_t {
InvalidExportSection,
InvalidStartSection,
InvalidCodeSection,
InvalidDataSection,
InvalidDataCountSection,
UnhandledSection,
};
@@ -67,6 +68,8 @@ constexpr std::string_view to_string(ModuleParseError e) {
return "Invalid start section";
case ModuleParseError::InvalidCodeSection:
return "Invalid code section";
case ModuleParseError::InvalidDataSection:
return "Invalid data section";
case ModuleParseError::InvalidDataCountSection:
return "Invalid data count section";
case ModuleParseError::UnhandledSection:
 
wasm/byte_code_parser_test.cpp added: 110, removed: 5, total 105
@@ -14,6 +14,7 @@
 
#include <algorithm>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <iterator>
#include <sstream>
@@ -74,6 +75,7 @@ void parse_error_to_string_tests() {
expect_eq(wasm::to_string(ModuleParseError::InvalidExportSection), "Invalid export section");
expect_eq(wasm::to_string(ModuleParseError::InvalidStartSection), "Invalid start section");
expect_eq(wasm::to_string(ModuleParseError::InvalidCodeSection), "Invalid code section");
expect_eq(wasm::to_string(ModuleParseError::InvalidDataSection), "Invalid data section");
expect_eq(wasm::to_string(ModuleParseError::InvalidDataCountSection), "Invalid data count section");
expect_eq(wasm::to_string(ModuleParseError::UnhandledSection), "Unhandled section");
 
@@ -663,6 +665,43 @@ void code_section_tests() {
});
}
 
void data_tests() {
using wasm::DataSection;
 
etest::test("data section, passive data, everything's fine", [] {
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Data, {1, 1, 3, 1, 2, 3})).value();
expect_eq(module.data_section,
DataSection{.data{DataSection::PassiveData{{std::byte{1}, std::byte{2}, std::byte{3}}}}});
});
 
etest::test("data section, passive data, 2 datas", [] {
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Data, {2, 1, 1, 37, 1, 1, 42})).value();
expect_eq(module.data_section,
DataSection{
.data{DataSection::PassiveData{{std::byte{37}}}, DataSection::PassiveData{{std::byte{42}}}}});
});
 
etest::test("data section, passive data, eof", [] {
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Data, {1, 1, 3, 1, 2}));
expect_eq(module, tl::unexpected{wasm::ModuleParseError::InvalidDataSection});
});
 
etest::test("data section, unhandled type", [] {
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Data, {1, 5}));
expect_eq(module, tl::unexpected{wasm::ModuleParseError::InvalidDataSection});
});
 
etest::test("data section, missing type", [] {
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Data, {1}));
expect_eq(module, tl::unexpected{wasm::ModuleParseError::InvalidDataSection});
});
 
etest::test("data section, empty", [] {
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::Data, {0})).value();
expect_eq(module.data_section, DataSection{});
});
}
 
void data_count_tests() {
etest::test("data count section, 42", [] {
auto module = ByteCodeParser::parse_module(make_module_bytes(SectionId::DataCount, {42})).value();
@@ -729,6 +768,7 @@ int main() {
export_section_tests();
start_section_tests();
code_section_tests();
data_tests();
data_count_tests();
 
return etest::run_all_tests();
 
wasm/wasm.h added: 110, removed: 5, total 105
@@ -8,6 +8,7 @@
#include "wasm/instructions.h"
#include "wasm/types.h"
 
#include <cstddef>
#include <cstdint>
#include <optional>
#include <string>
@@ -139,6 +140,21 @@ struct CodeSection {
[[nodiscard]] bool operator==(CodeSection const &) const = default;
};
 
// https://webassembly.github.io/spec/core/binary/modules.html#data-section
struct DataSection {
// TODO(robinlinden): Active data.
struct PassiveData {
std::vector<std::byte> data{};
 
[[nodiscard]] bool operator==(PassiveData const &) const = default;
};
 
using Data = std::variant<PassiveData>;
std::vector<Data> data{};
 
[[nodiscard]] bool operator==(DataSection const &) const = default;
};
 
struct DataCountSection {
std::uint32_t count{};
 
@@ -159,7 +175,7 @@ struct Module {
std::optional<StartSection> start_section{};
// TODO(robinlinden): element_section
std::optional<CodeSection> code_section{};
// TODO(robinlinden): data_section
std::optional<DataSection> data_section{};
std::optional<DataCountSection> data_count_section{};
 
[[nodiscard]] bool operator==(Module const &) const = default;