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