LibWasm: Introduce TRY_READ for parser

Convenience macro akin to `TRY` for reading from a stream.
This commit is contained in:
Diego Frias 2024-07-27 17:21:19 -07:00 committed by Ali Mohammad Pur
parent dc52998341
commit 9800ef6047
Notes: github-actions[bot] 2024-07-28 11:03:38 +00:00

View file

@ -16,6 +16,18 @@
namespace Wasm { namespace Wasm {
#define TRY_READ(stream, type, error) \
({ \
/* Ignore -Wshadow to allow nesting the macro. */ \
AK_IGNORE_DIAGNOSTIC("-Wshadow", \
auto&& _temporary_result = stream.read_value<type>()); \
static_assert(!::AK::Detail::IsLvalueReference<decltype(_temporary_result.release_value())>, \
"Do not return a reference from a fallible expression"); \
if (_temporary_result.is_error()) [[unlikely]] \
return with_eof_check(stream, error); \
_temporary_result.release_value(); \
})
ParseError with_eof_check(Stream const& stream, ParseError error_if_not_eof) ParseError with_eof_check(Stream const& stream, ParseError error_if_not_eof)
{ {
if (stream.is_eof()) if (stream.is_eof())
@ -90,11 +102,7 @@ static ParseResult<ByteString> parse_name(Stream& stream)
ParseResult<ValueType> ValueType::parse(Stream& stream) ParseResult<ValueType> ValueType::parse(Stream& stream)
{ {
ScopeLogger<WASM_BINPARSER_DEBUG> logger("ValueType"sv); ScopeLogger<WASM_BINPARSER_DEBUG> logger("ValueType"sv);
auto tag_or_error = stream.read_value<u8>(); auto tag = TRY_READ(stream, u8, ParseError::ExpectedKindTag);
if (tag_or_error.is_error())
return with_eof_check(stream, ParseError::ExpectedKindTag);
auto tag = tag_or_error.release_value();
switch (tag) { switch (tag) {
case Constants::i32_tag: case Constants::i32_tag:
@ -112,7 +120,7 @@ ParseResult<ValueType> ValueType::parse(Stream& stream)
case Constants::extern_reference_tag: case Constants::extern_reference_tag:
return ValueType(ExternReference); return ValueType(ExternReference);
default: default:
return with_eof_check(stream, ParseError::InvalidTag); return ParseError::InvalidTag;
} }
} }
@ -126,11 +134,7 @@ ParseResult<ResultType> ResultType::parse(Stream& stream)
ParseResult<FunctionType> FunctionType::parse(Stream& stream) ParseResult<FunctionType> FunctionType::parse(Stream& stream)
{ {
ScopeLogger<WASM_BINPARSER_DEBUG> logger("FunctionType"sv); ScopeLogger<WASM_BINPARSER_DEBUG> logger("FunctionType"sv);
auto tag_or_error = stream.read_value<u8>(); auto tag = TRY_READ(stream, u8, ParseError::ExpectedKindTag);
if (tag_or_error.is_error())
return with_eof_check(stream, ParseError::ExpectedKindTag);
auto tag = tag_or_error.release_value();
if (tag != Constants::function_signature_tag) { if (tag != Constants::function_signature_tag) {
dbgln("Expected 0x60, but found {:#x}", tag); dbgln("Expected 0x60, but found {:#x}", tag);
@ -146,11 +150,7 @@ ParseResult<FunctionType> FunctionType::parse(Stream& stream)
ParseResult<Limits> Limits::parse(Stream& stream) ParseResult<Limits> Limits::parse(Stream& stream)
{ {
ScopeLogger<WASM_BINPARSER_DEBUG> logger("Limits"sv); ScopeLogger<WASM_BINPARSER_DEBUG> logger("Limits"sv);
auto flag_or_error = stream.read_value<u8>(); auto flag = TRY_READ(stream, u8, ParseError::ExpectedKindTag);
if (flag_or_error.is_error())
return with_eof_check(stream, ParseError::ExpectedKindTag);
auto flag = flag_or_error.release_value();
if (flag > 1) if (flag > 1)
return with_eof_check(stream, ParseError::InvalidTag); return with_eof_check(stream, ParseError::InvalidTag);
@ -183,7 +183,7 @@ ParseResult<TableType> TableType::parse(Stream& stream)
ScopeLogger<WASM_BINPARSER_DEBUG> logger("TableType"sv); ScopeLogger<WASM_BINPARSER_DEBUG> logger("TableType"sv);
auto type_result = TRY(ValueType::parse(stream)); auto type_result = TRY(ValueType::parse(stream));
if (!type_result.is_reference()) if (!type_result.is_reference())
return with_eof_check(stream, ParseError::InvalidType); return ParseError::InvalidType;
auto limits_result = TRY(Limits::parse(stream)); auto limits_result = TRY(Limits::parse(stream));
return TableType { type_result, limits_result }; return TableType { type_result, limits_result };
} }
@ -192,12 +192,7 @@ ParseResult<GlobalType> GlobalType::parse(Stream& stream)
{ {
ScopeLogger<WASM_BINPARSER_DEBUG> logger("GlobalType"sv); ScopeLogger<WASM_BINPARSER_DEBUG> logger("GlobalType"sv);
auto type_result = TRY(ValueType::parse(stream)); auto type_result = TRY(ValueType::parse(stream));
auto mutable_ = TRY_READ(stream, u8, ParseError::ExpectedKindTag);
auto mutable_or_error = stream.read_value<u8>();
if (mutable_or_error.is_error())
return with_eof_check(stream, ParseError::ExpectedKindTag);
auto mutable_ = mutable_or_error.release_value();
if (mutable_ > 1) if (mutable_ > 1)
return with_eof_check(stream, ParseError::InvalidTag); return with_eof_check(stream, ParseError::InvalidTag);
@ -208,11 +203,8 @@ ParseResult<GlobalType> GlobalType::parse(Stream& stream)
ParseResult<BlockType> BlockType::parse(Stream& stream) ParseResult<BlockType> BlockType::parse(Stream& stream)
{ {
ScopeLogger<WASM_BINPARSER_DEBUG> logger("BlockType"sv); ScopeLogger<WASM_BINPARSER_DEBUG> logger("BlockType"sv);
auto kind_or_error = stream.read_value<u8>(); auto kind = TRY_READ(stream, u8, ParseError::ExpectedKindTag);
if (kind_or_error.is_error())
return with_eof_check(stream, ParseError::ExpectedKindTag);
auto kind = kind_or_error.release_value();
if (kind == Constants::empty_block_tag) if (kind == Constants::empty_block_tag)
return BlockType {}; return BlockType {};
@ -227,10 +219,7 @@ ParseResult<BlockType> BlockType::parse(Stream& stream)
// FIXME: should be an i33. Right now, we're missing a potential last bit at // FIXME: should be an i33. Right now, we're missing a potential last bit at
// the end. See https://webassembly.github.io/spec/core/binary/instructions.html#binary-blocktype // the end. See https://webassembly.github.io/spec/core/binary/instructions.html#binary-blocktype
auto index_value_or_error = new_stream.read_value<LEB128<i32>>(); i32 index_value = TRY_READ(new_stream, LEB128<i32>, ParseError::ExpectedIndex);
if (index_value_or_error.is_error())
return with_eof_check(stream, ParseError::ExpectedIndex);
i32 index_value = index_value_or_error.release_value();
if (index_value < 0) { if (index_value < 0) {
dbgln("Invalid type index {}", index_value); dbgln("Invalid type index {}", index_value);
@ -243,11 +232,7 @@ ParseResult<BlockType> BlockType::parse(Stream& stream)
ParseResult<Instruction> Instruction::parse(Stream& stream) ParseResult<Instruction> Instruction::parse(Stream& stream)
{ {
ScopeLogger<WASM_BINPARSER_DEBUG> logger("Instruction"sv); ScopeLogger<WASM_BINPARSER_DEBUG> logger("Instruction"sv);
auto byte_or_error = stream.read_value<u8>(); auto byte = TRY_READ(stream, u8, ParseError::ExpectedKindTag);
if (byte_or_error.is_error())
return with_eof_check(stream, ParseError::ExpectedKindTag);
auto byte = byte_or_error.release_value();
OpCode opcode { byte }; OpCode opcode { byte };
@ -307,27 +292,18 @@ ParseResult<Instruction> Instruction::parse(Stream& stream)
case Instructions::i64_store16.value(): case Instructions::i64_store16.value():
case Instructions::i64_store32.value(): { case Instructions::i64_store32.value(): {
// op (align [multi-memory: memindex] offset) // op (align [multi-memory: memindex] offset)
auto align_or_error = stream.read_value<LEB128<u32>>(); u32 align = TRY_READ(stream, LEB128<u32>, ParseError::InvalidInput);
if (align_or_error.is_error())
return with_eof_check(stream, ParseError::InvalidInput);
size_t align = align_or_error.release_value();
// Proposal "multi-memory", if bit 6 of alignment is set, then a memory index follows the alignment. // Proposal "multi-memory", if bit 6 of alignment is set, then a memory index follows the alignment.
size_t memory_index = 0; auto memory_index = 0;
if ((align & 0x40) != 0) { if ((align & 0x40) != 0) {
align &= ~0x40; align &= ~0x40;
auto memory_index_or_error = stream.read_value<LEB128<u32>>(); memory_index = TRY_READ(stream, LEB128<u32>, ParseError::InvalidInput);
if (memory_index_or_error.is_error())
return with_eof_check(stream, ParseError::InvalidInput);
memory_index = memory_index_or_error.release_value();
} }
auto offset_or_error = stream.read_value<LEB128<u32>>(); auto offset = TRY_READ(stream, LEB128<u32>, ParseError::InvalidInput);
if (offset_or_error.is_error())
return with_eof_check(stream, ParseError::InvalidInput);
size_t offset = offset_or_error.release_value();
return Instruction { opcode, MemoryArgument { static_cast<u32>(align), static_cast<u32>(offset), MemoryIndex(memory_index) } }; return Instruction { opcode, MemoryArgument { align, offset, MemoryIndex(memory_index) } };
} }
case Instructions::local_get.value(): case Instructions::local_get.value():
case Instructions::local_set.value(): case Instructions::local_set.value():
@ -343,48 +319,31 @@ ParseResult<Instruction> Instruction::parse(Stream& stream)
case Instructions::memory_size.value(): case Instructions::memory_size.value():
case Instructions::memory_grow.value(): { case Instructions::memory_grow.value(): {
// op [multi-memory: memindex]|0x00 // op [multi-memory: memindex]|0x00
auto memory_index = TRY_READ(stream, u8, ParseError::ExpectedKindTag);
auto memory_index_or_error = stream.read_value<u8>();
if (memory_index_or_error.is_error())
return with_eof_check(stream, ParseError::ExpectedKindTag);
auto memory_index = memory_index_or_error.release_value();
return Instruction { opcode, MemoryIndexArgument { MemoryIndex(memory_index) } }; return Instruction { opcode, MemoryIndexArgument { MemoryIndex(memory_index) } };
} }
case Instructions::i32_const.value(): { case Instructions::i32_const.value(): {
auto value_or_error = stream.read_value<LEB128<i32>>(); auto value = TRY_READ(stream, LEB128<i32>, ParseError::ExpectedSignedImmediate);
if (value_or_error.is_error())
return with_eof_check(stream, ParseError::ExpectedSignedImmediate);
i32 value = value_or_error.release_value();
return Instruction { opcode, value }; return Instruction { opcode, value };
} }
case Instructions::i64_const.value(): { case Instructions::i64_const.value(): {
// op literal // op literal
auto value_or_error = stream.read_value<LEB128<i64>>(); auto value = TRY_READ(stream, LEB128<i64>, ParseError::ExpectedSignedImmediate);
if (value_or_error.is_error())
return with_eof_check(stream, ParseError::ExpectedSignedImmediate);
i64 value = value_or_error.release_value();
return Instruction { opcode, value }; return Instruction { opcode, value };
} }
case Instructions::f32_const.value(): { case Instructions::f32_const.value(): {
// op literal // op literal
auto value_or_error = stream.read_value<LittleEndian<u32>>(); auto value = TRY_READ(stream, LittleEndian<u32>, ParseError::ExpectedFloatingImmediate);
if (value_or_error.is_error())
return with_eof_check(stream, ParseError::ExpectedFloatingImmediate);
auto value = value_or_error.release_value();
auto floating = bit_cast<float>(static_cast<u32>(value)); auto floating = bit_cast<float>(static_cast<u32>(value));
return Instruction { opcode, floating }; return Instruction { opcode, floating };
} }
case Instructions::f64_const.value(): { case Instructions::f64_const.value(): {
// op literal // op literal
auto value_or_error = stream.read_value<LittleEndian<u64>>(); auto value = TRY_READ(stream, LittleEndian<u64>, ParseError::ExpectedFloatingImmediate);
if (value_or_error.is_error())
return with_eof_check(stream, ParseError::ExpectedFloatingImmediate);
auto value = value_or_error.release_value();
auto floating = bit_cast<double>(static_cast<u64>(value)); auto floating = bit_cast<double>(static_cast<u64>(value));
return Instruction { opcode, floating }; return Instruction { opcode, floating };
@ -549,10 +508,7 @@ ParseResult<Instruction> Instruction::parse(Stream& stream)
case 0xfc: case 0xfc:
case 0xfd: { case 0xfd: {
// These are multibyte instructions. // These are multibyte instructions.
auto selector_or_error = stream.read_value<LEB128<u32>>(); auto selector = TRY_READ(stream, LEB128<u32>, ParseError::InvalidInput);
if (selector_or_error.is_error())
return with_eof_check(stream, ParseError::InvalidInput);
u32 selector = selector_or_error.release_value();
OpCode full_opcode = static_cast<u64>(opcode.value()) << 56 | selector; OpCode full_opcode = static_cast<u64>(opcode.value()) << 56 | selector;
switch (full_opcode.value()) { switch (full_opcode.value()) {
@ -569,11 +525,7 @@ ParseResult<Instruction> Instruction::parse(Stream& stream)
auto index = TRY(GenericIndexParser<DataIndex>::parse(stream)); auto index = TRY(GenericIndexParser<DataIndex>::parse(stream));
// Proposal "multi-memory", literal 0x00 is replaced with a memory index. // Proposal "multi-memory", literal 0x00 is replaced with a memory index.
auto memory_index_or_error = stream.read_value<u8>(); auto memory_index = TRY_READ(stream, u8, ParseError::InvalidInput);
if (memory_index_or_error.is_error())
return with_eof_check(stream, ParseError::InvalidInput);
auto memory_index = memory_index_or_error.release_value();
return Instruction { full_opcode, MemoryInitArgs { index, MemoryIndex(memory_index) } }; return Instruction { full_opcode, MemoryInitArgs { index, MemoryIndex(memory_index) } };
} }
@ -586,21 +538,14 @@ ParseResult<Instruction> Instruction::parse(Stream& stream)
MemoryIndex indices[] = { 0, 0 }; MemoryIndex indices[] = { 0, 0 };
for (size_t i = 0; i < 2; ++i) { for (size_t i = 0; i < 2; ++i) {
auto memory_index_or_error = stream.read_value<u8>(); auto memory_index = TRY_READ(stream, u8, ParseError::InvalidInput);
if (memory_index_or_error.is_error()) indices[i] = memory_index;
return with_eof_check(stream, ParseError::InvalidInput);
indices[i] = memory_index_or_error.release_value();
} }
return Instruction { full_opcode, MemoryCopyArgs { indices[1], indices[0] } }; return Instruction { full_opcode, MemoryCopyArgs { indices[1], indices[0] } };
} }
case Instructions::memory_fill.value(): { case Instructions::memory_fill.value(): {
// Proposal "multi-memory", literal 0x00 is replaced with a memory index. // Proposal "multi-memory", literal 0x00 is replaced with a memory index.
auto memory_index_or_error = stream.read_value<u8>(); auto memory_index = TRY_READ(stream, u8, ParseError::InvalidInput);
if (memory_index_or_error.is_error())
return with_eof_check(stream, ParseError::InvalidInput);
auto memory_index = memory_index_or_error.release_value();
return Instruction { full_opcode, MemoryIndexArgument { MemoryIndex { memory_index } } }; return Instruction { full_opcode, MemoryIndexArgument { MemoryIndex { memory_index } } };
} }
case Instructions::table_init.value(): { case Instructions::table_init.value(): {
@ -638,27 +583,18 @@ ParseResult<Instruction> Instruction::parse(Stream& stream)
case Instructions::v128_load64_zero.value(): case Instructions::v128_load64_zero.value():
case Instructions::v128_store.value(): { case Instructions::v128_store.value(): {
// op (align [multi-memory memindex] offset) // op (align [multi-memory memindex] offset)
auto align_or_error = stream.read_value<LEB128<u32>>(); u32 align = TRY_READ(stream, LEB128<u32>, ParseError::ExpectedIndex);
if (align_or_error.is_error())
return with_eof_check(stream, ParseError::ExpectedIndex);
size_t align = align_or_error.release_value();
// Proposal "multi-memory", if bit 6 of alignment is set, then a memory index follows the alignment. // Proposal "multi-memory", if bit 6 of alignment is set, then a memory index follows the alignment.
size_t memory_index = 0; auto memory_index = 0;
if ((align & 0x20) != 0) { if ((align & 0x20) != 0) {
align &= ~0x20; align &= ~0x20;
auto memory_index_or_error = stream.read_value<LEB128<u32>>(); memory_index = TRY_READ(stream, LEB128<u32>, ParseError::InvalidInput);
if (memory_index_or_error.is_error())
return with_eof_check(stream, ParseError::InvalidInput);
memory_index = memory_index_or_error.release_value();
} }
auto offset_or_error = stream.read_value<LEB128<u32>>(); auto offset = TRY_READ(stream, LEB128<u32>, ParseError::ExpectedIndex);
if (offset_or_error.is_error())
return with_eof_check(stream, ParseError::ExpectedIndex);
size_t offset = offset_or_error.release_value();
return Instruction { full_opcode, MemoryArgument { static_cast<u32>(align), static_cast<u32>(offset), MemoryIndex(memory_index) } }; return Instruction { full_opcode, MemoryArgument { align, offset, MemoryIndex(memory_index) } };
} }
case Instructions::v128_load8_lane.value(): case Instructions::v128_load8_lane.value():
case Instructions::v128_load16_lane.value(): case Instructions::v128_load16_lane.value():
@ -669,48 +605,31 @@ ParseResult<Instruction> Instruction::parse(Stream& stream)
case Instructions::v128_store32_lane.value(): case Instructions::v128_store32_lane.value():
case Instructions::v128_store64_lane.value(): { case Instructions::v128_store64_lane.value(): {
// op (align [multi-memory: memindex] offset) (index) // op (align [multi-memory: memindex] offset) (index)
auto align_or_error = stream.read_value<LEB128<u32>>(); u32 align = TRY_READ(stream, LEB128<u32>, ParseError::ExpectedIndex);
if (align_or_error.is_error())
return with_eof_check(stream, ParseError::ExpectedIndex);
size_t align = align_or_error.release_value();
// Proposal "multi-memory", if bit 6 of alignment is set, then a memory index follows the alignment. // Proposal "multi-memory", if bit 6 of alignment is set, then a memory index follows the alignment.
size_t memory_index = 0; auto memory_index = 0;
if ((align & 0x20) != 0) { if ((align & 0x20) != 0) {
align &= ~0x20; align &= ~0x20;
auto memory_index_or_error = stream.read_value<LEB128<u32>>(); memory_index = TRY_READ(stream, LEB128<u32>, ParseError::InvalidInput);
if (memory_index_or_error.is_error())
return with_eof_check(stream, ParseError::InvalidInput);
memory_index = memory_index_or_error.release_value();
} }
auto offset_or_error = stream.read_value<LEB128<u32>>(); auto offset = TRY_READ(stream, LEB128<u32>, ParseError::ExpectedIndex);
if (offset_or_error.is_error()) auto index = TRY_READ(stream, u8, ParseError::InvalidInput);
return with_eof_check(stream, ParseError::ExpectedIndex);
size_t offset = offset_or_error.release_value();
auto index_or_error = stream.read_value<u8>(); return Instruction { full_opcode, MemoryAndLaneArgument { { align, offset, MemoryIndex(memory_index) }, index } };
if (index_or_error.is_error())
return with_eof_check(stream, ParseError::InvalidInput);
auto index = index_or_error.release_value();
return Instruction { full_opcode, MemoryAndLaneArgument { { static_cast<u32>(align), static_cast<u32>(offset), MemoryIndex(memory_index) }, index } };
} }
case Instructions::v128_const.value(): { case Instructions::v128_const.value(): {
// op (literal:16) // op (literal:16)
auto value_or_error = stream.read_value<LittleEndian<u128>>(); auto value = TRY_READ(stream, LittleEndian<u128>, ParseError::InvalidImmediate);
if (value_or_error.is_error()) return Instruction { full_opcode, value };
return with_eof_check(stream, ParseError::InvalidImmediate);
return Instruction { full_opcode, value_or_error.release_value() };
} }
case Instructions::i8x16_shuffle.value(): { case Instructions::i8x16_shuffle.value(): {
// op 16x(lane) // op 16x(lane)
u8 lanes[16]; u8 lanes[16];
for (size_t i = 0; i < 16; ++i) { for (size_t i = 0; i < 16; ++i) {
auto value_or_error = stream.read_value<u8>(); auto value = TRY_READ(stream, u8, ParseError::InvalidInput);
if (value_or_error.is_error()) lanes[i] = value;
return with_eof_check(stream, ParseError::InvalidInput);
lanes[i] = value_or_error.release_value();
} }
return Instruction { full_opcode, ShuffleArgument(lanes) }; return Instruction { full_opcode, ShuffleArgument(lanes) };
} }
@ -729,10 +648,7 @@ ParseResult<Instruction> Instruction::parse(Stream& stream)
case Instructions::f64x2_extract_lane.value(): case Instructions::f64x2_extract_lane.value():
case Instructions::f64x2_replace_lane.value(): { case Instructions::f64x2_replace_lane.value(): {
// op (lane) // op (lane)
auto lane_or_error = stream.read_value<u8>(); auto lane = TRY_READ(stream, u8, ParseError::InvalidInput);
if (lane_or_error.is_error())
return with_eof_check(stream, ParseError::InvalidInput);
auto lane = lane_or_error.release_value();
return Instruction { full_opcode, LaneIndex { lane } }; return Instruction { full_opcode, LaneIndex { lane } };
} }
case Instructions::i8x16_swizzle.value(): case Instructions::i8x16_swizzle.value():
@ -961,7 +877,7 @@ ParseResult<CustomSection> CustomSection::parse(Stream& stream)
if (size == 0) if (size == 0)
break; break;
if (data_buffer.try_append(buf, size).is_error()) if (data_buffer.try_append(buf, size).is_error())
return with_eof_check(stream, ParseError::HugeAllocationRequested); return ParseError::HugeAllocationRequested;
} }
return CustomSection(name, move(data_buffer)); return CustomSection(name, move(data_buffer));
@ -979,11 +895,7 @@ ParseResult<ImportSection::Import> ImportSection::Import::parse(Stream& stream)
ScopeLogger<WASM_BINPARSER_DEBUG> logger("Import"sv); ScopeLogger<WASM_BINPARSER_DEBUG> logger("Import"sv);
auto module = TRY(parse_name(stream)); auto module = TRY(parse_name(stream));
auto name = TRY(parse_name(stream)); auto name = TRY(parse_name(stream));
auto tag_or_error = stream.read_value<u8>(); auto tag = TRY_READ(stream, u8, ParseError::ExpectedKindTag);
if (tag_or_error.is_error())
return with_eof_check(stream, ParseError::ExpectedKindTag);
auto tag = tag_or_error.release_value();
switch (tag) { switch (tag) {
case Constants::extern_function_tag: { case Constants::extern_function_tag: {
@ -997,7 +909,7 @@ ParseResult<ImportSection::Import> ImportSection::Import::parse(Stream& stream)
case Constants::extern_global_tag: case Constants::extern_global_tag:
return parse_with_type<GlobalType>(stream, module, name); return parse_with_type<GlobalType>(stream, module, name);
default: default:
return with_eof_check(stream, ParseError::InvalidTag); return ParseError::InvalidTag;
} }
} }
@ -1110,16 +1022,9 @@ ParseResult<ExportSection::Export> ExportSection::Export::parse(Stream& stream)
{ {
ScopeLogger<WASM_BINPARSER_DEBUG> logger("Export"sv); ScopeLogger<WASM_BINPARSER_DEBUG> logger("Export"sv);
auto name = TRY(parse_name(stream)); auto name = TRY(parse_name(stream));
auto tag_or_error = stream.read_value<u8>(); auto tag = TRY_READ(stream, u8, ParseError::ExpectedKindTag);
if (tag_or_error.is_error())
return with_eof_check(stream, ParseError::ExpectedKindTag);
auto tag = tag_or_error.release_value(); auto index = TRY_READ(stream, LEB128<u32>, ParseError::ExpectedIndex);
auto index_or_error = stream.read_value<LEB128<u32>>();
if (index_or_error.is_error())
return with_eof_check(stream, ParseError::ExpectedIndex);
size_t index = index_or_error.release_value();
switch (tag) { switch (tag) {
case Constants::extern_function_tag: case Constants::extern_function_tag:
@ -1131,7 +1036,7 @@ ParseResult<ExportSection::Export> ExportSection::Export::parse(Stream& stream)
case Constants::extern_global_tag: case Constants::extern_global_tag:
return Export { name, ExportDesc { GlobalIndex { index } } }; return Export { name, ExportDesc { GlobalIndex { index } } };
default: default:
return with_eof_check(stream, ParseError::InvalidTag); return ParseError::InvalidTag;
} }
} }
@ -1159,11 +1064,7 @@ ParseResult<StartSection> StartSection::parse(Stream& stream)
ParseResult<ElementSection::Element> ElementSection::Element::parse(Stream& stream) ParseResult<ElementSection::Element> ElementSection::Element::parse(Stream& stream)
{ {
ScopeLogger<WASM_BINPARSER_DEBUG> logger("Element"sv); ScopeLogger<WASM_BINPARSER_DEBUG> logger("Element"sv);
auto tag_or_error = stream.read_value<LEB128<u32>>(); auto tag = TRY_READ(stream, LEB128<u32>, ParseError::ExpectedKindTag);
if (tag_or_error.is_error())
return with_eof_check(stream, ParseError::ExpectedKindTag);
auto tag = tag_or_error.release_value();
if (tag > 0x07) if (tag > 0x07)
return ParseError::InvalidTag; return ParseError::InvalidTag;
@ -1192,12 +1093,10 @@ ParseResult<ElementSection::Element> ElementSection::Element::parse(Stream& stre
if (has_exprs) { if (has_exprs) {
type = TRY(ValueType::parse(stream)); type = TRY(ValueType::parse(stream));
} else { } else {
auto extern_or_error = stream.read_value<u8>(); auto extern_ = TRY_READ(stream, u8, ParseError::InvalidType);
if (extern_or_error.is_error())
return with_eof_check(stream, ParseError::InvalidType);
// Make sure that this is a function, as it's technically only the // Make sure that this is a function, as it's technically only the
// allowed one. // allowed one.
if (extern_or_error.release_value() != 0x00) { if (extern_ != 0x00) {
return ParseError::InvalidType; return ParseError::InvalidType;
} }
type = ValueType(ValueType::FunctionReference); type = ValueType(ValueType::FunctionReference);
@ -1228,17 +1127,14 @@ ParseResult<ElementSection> ElementSection::parse(Stream& stream)
ParseResult<Locals> Locals::parse(Stream& stream) ParseResult<Locals> Locals::parse(Stream& stream)
{ {
ScopeLogger<WASM_BINPARSER_DEBUG> logger("Locals"sv); ScopeLogger<WASM_BINPARSER_DEBUG> logger("Locals"sv);
auto count_or_error = stream.read_value<LEB128<u32>>(); auto count = TRY_READ(stream, LEB128<u32>, ParseError::InvalidSize);
if (count_or_error.is_error())
return with_eof_check(stream, ParseError::InvalidSize);
size_t count = count_or_error.release_value();
if (count > Constants::max_allowed_function_locals_per_type) if (count > Constants::max_allowed_function_locals_per_type)
return with_eof_check(stream, ParseError::HugeAllocationRequested); return ParseError::HugeAllocationRequested;
auto type = TRY(ValueType::parse(stream)); auto type = TRY(ValueType::parse(stream));
return Locals { static_cast<u32>(count), type }; return Locals { count, type };
} }
ParseResult<CodeSection::Func> CodeSection::Func::parse(Stream& stream, size_t size_hint) ParseResult<CodeSection::Func> CodeSection::Func::parse(Stream& stream, size_t size_hint)
@ -1252,16 +1148,13 @@ ParseResult<CodeSection::Func> CodeSection::Func::parse(Stream& stream, size_t s
ParseResult<CodeSection::Code> CodeSection::Code::parse(Stream& stream) ParseResult<CodeSection::Code> CodeSection::Code::parse(Stream& stream)
{ {
ScopeLogger<WASM_BINPARSER_DEBUG> logger("Code"sv); ScopeLogger<WASM_BINPARSER_DEBUG> logger("Code"sv);
auto size_or_error = stream.read_value<LEB128<u32>>(); auto size = TRY_READ(stream, LEB128<u32>, ParseError::InvalidSize);
if (size_or_error.is_error())
return with_eof_check(stream, ParseError::InvalidSize);
size_t size = size_or_error.release_value();
// Emprically, if there are `size` bytes to be read, then there's around // Emprically, if there are `size` bytes to be read, then there's around
// `size / 2` instructions, so we pass that as our size hint. // `size / 2` instructions, so we pass that as our size hint.
auto func = TRY(Func::parse(stream, size / 2)); auto func = TRY(Func::parse(stream, size / 2));
return Code { static_cast<u32>(size), move(func) }; return Code { size, move(func) };
} }
ParseResult<CodeSection> CodeSection::parse(Stream& stream) ParseResult<CodeSection> CodeSection::parse(Stream& stream)
@ -1274,14 +1167,10 @@ ParseResult<CodeSection> CodeSection::parse(Stream& stream)
ParseResult<DataSection::Data> DataSection::Data::parse(Stream& stream) ParseResult<DataSection::Data> DataSection::Data::parse(Stream& stream)
{ {
ScopeLogger<WASM_BINPARSER_DEBUG> logger("Data"sv); ScopeLogger<WASM_BINPARSER_DEBUG> logger("Data"sv);
auto tag_or_error = stream.read_value<LEB128<u32>>(); auto tag = TRY_READ(stream, LEB128<u32>, ParseError::ExpectedKindTag);
if (tag_or_error.is_error())
return with_eof_check(stream, ParseError::ExpectedKindTag);
auto tag = tag_or_error.release_value();
if (tag > 0x02) if (tag > 0x02)
return with_eof_check(stream, ParseError::InvalidTag); return ParseError::InvalidTag;
if (tag == 0x00) { if (tag == 0x00) {
auto expr = TRY(Expression::parse(stream)); auto expr = TRY(Expression::parse(stream));
@ -1340,17 +1229,8 @@ ParseResult<Module> Module::parse(Stream& stream)
Vector<AnySection> sections; Vector<AnySection> sections;
while (!stream.is_eof()) { while (!stream.is_eof()) {
auto section_id_or_error = stream.read_value<u8>(); auto section_id = TRY_READ(stream, u8, ParseError::ExpectedIndex);
if (section_id_or_error.is_error()) size_t section_size = TRY_READ(stream, LEB128<u32>, ParseError::ExpectedSize);
return with_eof_check(stream, ParseError::ExpectedIndex);
auto section_id = section_id_or_error.release_value();
auto section_size_or_error = stream.read_value<LEB128<u32>>();
if (section_size_or_error.is_error())
return with_eof_check(stream, ParseError::ExpectedSize);
size_t section_size = section_size_or_error.release_value();
auto section_stream = ConstrainedStream { MaybeOwned<Stream>(stream), section_size }; auto section_stream = ConstrainedStream { MaybeOwned<Stream>(stream), section_size };
switch (section_id) { switch (section_id) {
@ -1394,7 +1274,7 @@ ParseResult<Module> Module::parse(Stream& stream)
sections.append(TRY(DataCountSection::parse(section_stream))); sections.append(TRY(DataCountSection::parse(section_stream)));
break; break;
default: default:
return with_eof_check(stream, ParseError::InvalidIndex); return ParseError::InvalidIndex;
} }
if (section_stream.remaining() != 0) if (section_stream.remaining() != 0)
return ParseError::SectionSizeMismatch; return ParseError::SectionSizeMismatch;