srctree

Robin Linden parent ff62f33f de5f4467
wasm: Implement parsing the code section

inlinesplit
wasm/wasm.cpp added: 183, removed: 3, total 180
@@ -7,6 +7,7 @@
#include "wasm/leb128.h"
 
#include <algorithm>
#include <cassert>
#include <cstdint>
#include <istream>
#include <optional>
@@ -121,6 +122,55 @@ std::optional<Export> parse(std::istream &is) {
};
}
 
// https://webassembly.github.io/spec/core/binary/modules.html#binary-codesec
template<>
std::optional<CodeEntry::Local> parse(std::istream &is) {
auto count = Leb128<std::uint32_t>::decode_from(is);
if (!count) {
return std::nullopt;
}
 
auto type = parse<ValueType>(is);
if (!type) {
return std::nullopt;
}
 
return CodeEntry::Local{
.count = *count,
.type = *type,
};
}
 
// https://webassembly.github.io/spec/core/binary/modules.html#binary-codesec
template<>
std::optional<CodeEntry> parse(std::istream &is) {
auto size = Leb128<std::uint32_t>::decode_from(is);
if (!size) {
return std::nullopt;
}
 
auto cursor_before_locals = is.tellg();
 
auto locals = parse_vector<CodeEntry::Local>(is);
if (!locals) {
return std::nullopt;
}
 
auto bytes_consumed_by_locals = is.tellg() - cursor_before_locals;
assert(bytes_consumed_by_locals >= 0);
 
std::vector<std::uint8_t> code;
code.resize(*size - bytes_consumed_by_locals);
if (!is.read(reinterpret_cast<char *>(code.data()), code.size())) {
return std::nullopt;
}
 
return CodeEntry{
.code = std::move(code),
.locals = *std::move(locals),
};
}
 
// https://webassembly.github.io/spec/core/bikeshed/#binary-vec
template<typename T>
std::optional<std::vector<T>> parse_vector(std::istream &is) {
@@ -248,4 +298,17 @@ std::optional<ExportSection> Module::export_section() const {
return std::nullopt;
}
 
std::optional<CodeSection> Module::code_section() const {
auto content = get_section_data(sections, SectionId::Code);
if (!content) {
return std::nullopt;
}
 
if (auto code_entries = parse_vector<CodeEntry>(std::stringstream{*std::move(content)})) {
return CodeSection{.entries = *std::move(code_entries)};
}
 
return std::nullopt;
}
 
} // namespace wasm
 
wasm/wasm.h added: 183, removed: 3, total 180
@@ -99,6 +99,26 @@ struct ExportSection {
[[nodiscard]] bool operator==(ExportSection const &) const = default;
};
 
// https://webassembly.github.io/spec/core/binary/modules.html#binary-codesec
struct CodeEntry {
struct Local {
std::uint32_t count{};
ValueType type{};
 
[[nodiscard]] bool operator==(Local const &) const = default;
};
 
std::vector<std::uint8_t> code{};
std::vector<Local> locals{};
 
[[nodiscard]] bool operator==(CodeEntry const &) const = default;
};
struct CodeSection {
std::vector<CodeEntry> entries{};
 
[[nodiscard]] bool operator==(CodeSection const &) const = default;
};
 
// https://webassembly.github.io/spec/core/bikeshed/#modules
struct Module {
static std::optional<Module> parse_from(std::istream &&is) { return parse_from(is); }
@@ -109,6 +129,7 @@ struct Module {
std::optional<TypeSection> type_section() const;
std::optional<FunctionSection> function_section() const;
std::optional<ExportSection> export_section() const;
std::optional<CodeSection> code_section() const;
 
[[nodiscard]] bool operator==(Module const &) const = default;
};
 
wasm/wasm_test.cpp added: 183, removed: 3, total 180
@@ -243,6 +243,101 @@ void type_section_tests() {
});
}
 
void code_section_tests() {
etest::test("code section, non-existent", [] {
auto module = wasm::Module{};
expect_eq(module.code_section(), std::nullopt);
});
 
etest::test("code section, missing type data", [] {
auto module = wasm::Module{.sections{wasm::Section{
.id = wasm::SectionId::Code,
.content{},
}}};
 
expect_eq(module.code_section(), std::nullopt);
});
 
etest::test("code section, empty", [] {
auto module = wasm::Module{.sections{wasm::Section{
.id = wasm::SectionId::Code,
.content{0},
}}};
 
expect_eq(module.code_section(), wasm::CodeSection{});
});
 
etest::test("code section, missing data after count", [] {
auto module = wasm::Module{.sections{wasm::Section{
.id = wasm::SectionId::Code,
.content{1},
}}};
 
expect_eq(module.code_section(), std::nullopt);
});
 
etest::test("code section, missing local count", [] {
auto module = wasm::Module{.sections{wasm::Section{
.id = wasm::SectionId::Code,
.content{1, 1, 1},
}}};
 
expect_eq(module.code_section(), std::nullopt);
});
 
etest::test("code section, missing local type", [] {
auto module = wasm::Module{.sections{wasm::Section{
.id = wasm::SectionId::Code,
.content{1, 1, 1, 1},
}}};
 
expect_eq(module.code_section(), std::nullopt);
});
 
etest::test("code section, not enough data", [] {
auto module = wasm::Module{.sections{wasm::Section{
.id = wasm::SectionId::Code,
.content{1, 6, 1, 1, 0x7f, 4, 4},
}}};
 
expect_eq(module.code_section(), std::nullopt);
});
 
etest::test("code section, one entry", [] {
auto module = wasm::Module{.sections{wasm::Section{
.id = wasm::SectionId::Code,
.content{1, 6, 1, 1, 0x7f, 4, 4, 4},
}}};
 
wasm::CodeSection expected{.entries{
wasm::CodeEntry{
.code{4, 4, 4},
.locals{{1, wasm::ValueType::Int32}},
},
}};
expect_eq(module.code_section(), expected);
});
 
etest::test("code section, two entries", [] {
auto module = wasm::Module{.sections{wasm::Section{
.id = wasm::SectionId::Code,
.content{2, 6, 1, 1, 0x7f, 4, 4, 4, 9, 2, 5, 0x7e, 6, 0x7d, 7, 8, 9, 10},
}}};
 
wasm::CodeSection expected{.entries{
wasm::CodeEntry{
.code{4, 4, 4},
.locals{{1, wasm::ValueType::Int32}},
},
wasm::CodeEntry{
.code{7, 8, 9, 10},
.locals{{5, wasm::ValueType::Int64}, {6, wasm::ValueType::Float32}},
},
}};
expect_eq(module.code_section(), expected);
});
}
 
} // namespace
 
int main() {
@@ -316,6 +411,7 @@ int main() {
type_section_tests();
function_section_tests();
export_section_tests();
code_section_tests();
 
return etest::run_all_tests();
}