/* * Copyright (c) 2021, Ali Mohammad Pur * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include #include #include #include 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()) return ParseError::UnexpectedEof; return error_if_not_eof; } template static auto parse_vector(Stream& stream) { ScopeLogger logger; if constexpr (requires { T::parse(stream); }) { using ResultT = typename decltype(T::parse(stream))::ResultType; auto count_or_error = stream.read_value>(); if (count_or_error.is_error()) return ParseResult> { with_eof_check(stream, ParseError::ExpectedSize) }; size_t count = count_or_error.release_value(); Vector entries; entries.ensure_capacity(count); for (size_t i = 0; i < count; ++i) { auto result = T::parse(stream); if (result.is_error()) return ParseResult> { result.error() }; entries.append(result.release_value()); } return ParseResult> { move(entries) }; } else { auto count_or_error = stream.read_value>(); if (count_or_error.is_error()) return ParseResult> { with_eof_check(stream, ParseError::ExpectedSize) }; size_t count = count_or_error.release_value(); Vector entries; entries.ensure_capacity(count); for (size_t i = 0; i < count; ++i) { if constexpr (IsSame) { auto value_or_error = stream.read_value>(); if (value_or_error.is_error()) return ParseResult> { with_eof_check(stream, ParseError::ExpectedSize) }; size_t value = value_or_error.release_value(); entries.append(value); } else if constexpr (IsSame) { auto value_or_error = stream.read_value>(); if (value_or_error.is_error()) return ParseResult> { with_eof_check(stream, ParseError::ExpectedSize) }; ssize_t value = value_or_error.release_value(); entries.append(value); } else if constexpr (IsSame) { if (count > Constants::max_allowed_vector_size) return ParseResult> { ParseError::HugeAllocationRequested }; entries.resize(count); if (stream.read_until_filled({ entries.data(), entries.size() }).is_error()) return ParseResult> { with_eof_check(stream, ParseError::InvalidInput) }; break; // Note: We read this all in one go! } } return ParseResult> { move(entries) }; } } static ParseResult parse_name(Stream& stream) { ScopeLogger logger; auto data = TRY(parse_vector(stream)); auto string = ByteString::copy(data); if (!Utf8View(string).validate(Utf8View::AllowSurrogates::No)) return ParseError::InvalidUtf8; return string; } ParseResult ValueType::parse(Stream& stream) { ScopeLogger logger("ValueType"sv); auto tag = TRY_READ(stream, u8, ParseError::ExpectedKindTag); switch (tag) { case Constants::i32_tag: return ValueType(I32); case Constants::i64_tag: return ValueType(I64); case Constants::f32_tag: return ValueType(F32); case Constants::f64_tag: return ValueType(F64); case Constants::v128_tag: return ValueType(V128); case Constants::function_reference_tag: return ValueType(FunctionReference); case Constants::extern_reference_tag: return ValueType(ExternReference); default: return ParseError::InvalidTag; } } ParseResult ResultType::parse(Stream& stream) { ScopeLogger logger("ResultType"sv); auto types = TRY(parse_vector(stream)); return ResultType { types }; } ParseResult FunctionType::parse(Stream& stream) { ScopeLogger logger("FunctionType"sv); auto tag = TRY_READ(stream, u8, ParseError::ExpectedKindTag); if (tag != Constants::function_signature_tag) { dbgln("Expected 0x60, but found {:#x}", tag); return with_eof_check(stream, ParseError::InvalidTag); } auto parameters_result = TRY(parse_vector(stream)); auto results_result = TRY(parse_vector(stream)); return FunctionType { parameters_result, results_result }; } ParseResult Limits::parse(Stream& stream) { ScopeLogger logger("Limits"sv); auto flag = TRY_READ(stream, u8, ParseError::ExpectedKindTag); if (flag > 1) return with_eof_check(stream, ParseError::InvalidTag); auto min_or_error = stream.read_value>(); if (min_or_error.is_error()) return with_eof_check(stream, ParseError::ExpectedSize); size_t min = min_or_error.release_value(); Optional max; if (flag) { auto value_or_error = stream.read_value>(); if (value_or_error.is_error()) return with_eof_check(stream, ParseError::ExpectedSize); max = value_or_error.release_value(); } return Limits { static_cast(min), move(max) }; } ParseResult MemoryType::parse(Stream& stream) { ScopeLogger logger("MemoryType"sv); auto limits_result = TRY(Limits::parse(stream)); return MemoryType { limits_result }; } ParseResult TableType::parse(Stream& stream) { ScopeLogger logger("TableType"sv); auto type_result = TRY(ValueType::parse(stream)); if (!type_result.is_reference()) return ParseError::InvalidType; auto limits_result = TRY(Limits::parse(stream)); return TableType { type_result, limits_result }; } ParseResult GlobalType::parse(Stream& stream) { ScopeLogger logger("GlobalType"sv); auto type_result = TRY(ValueType::parse(stream)); auto mutable_ = TRY_READ(stream, u8, ParseError::ExpectedKindTag); if (mutable_ > 1) return with_eof_check(stream, ParseError::InvalidTag); return GlobalType { type_result, mutable_ == 0x01 }; } ParseResult BlockType::parse(Stream& stream) { ScopeLogger logger("BlockType"sv); auto kind = TRY_READ(stream, u8, ParseError::ExpectedKindTag); if (kind == Constants::empty_block_tag) return BlockType {}; { FixedMemoryStream value_stream { ReadonlyBytes { &kind, 1 } }; if (auto value_type = ValueType::parse(value_stream); !value_type.is_error()) return BlockType { value_type.release_value() }; } ReconsumableStream new_stream { stream }; new_stream.unread({ &kind, 1 }); // 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 i32 index_value = TRY_READ(new_stream, LEB128, ParseError::ExpectedIndex); if (index_value < 0) { dbgln("Invalid type index {}", index_value); return with_eof_check(stream, ParseError::InvalidIndex); } return BlockType { TypeIndex(index_value) }; } ParseResult Instruction::parse(Stream& stream) { ScopeLogger logger("Instruction"sv); auto byte = TRY_READ(stream, u8, ParseError::ExpectedKindTag); OpCode opcode { byte }; switch (opcode.value()) { case Instructions::block.value(): case Instructions::loop.value(): case Instructions::if_.value(): { auto block_type = TRY(BlockType::parse(stream)); return Instruction { opcode, StructuredInstructionArgs { block_type, {}, {} } }; } case Instructions::br.value(): case Instructions::br_if.value(): { // branches with a single label immediate auto index = TRY(GenericIndexParser::parse(stream)); return Instruction { opcode, index }; } case Instructions::br_table.value(): { // br_table label* label auto labels = TRY(parse_vector>(stream)); auto default_label = TRY(GenericIndexParser::parse(stream)); return Instruction { opcode, TableBranchArgs { labels, default_label } }; } case Instructions::call.value(): { // call function auto function_index = TRY(GenericIndexParser::parse(stream)); return Instruction { opcode, function_index }; } case Instructions::call_indirect.value(): { // call_indirect type table auto type_index = TRY(GenericIndexParser::parse(stream)); auto table_index = TRY(GenericIndexParser::parse(stream)); return Instruction { opcode, IndirectCallArgs { type_index, table_index } }; } case Instructions::i32_load.value(): case Instructions::i64_load.value(): case Instructions::f32_load.value(): case Instructions::f64_load.value(): case Instructions::i32_load8_s.value(): case Instructions::i32_load8_u.value(): case Instructions::i32_load16_s.value(): case Instructions::i32_load16_u.value(): case Instructions::i64_load8_s.value(): case Instructions::i64_load8_u.value(): case Instructions::i64_load16_s.value(): case Instructions::i64_load16_u.value(): case Instructions::i64_load32_s.value(): case Instructions::i64_load32_u.value(): case Instructions::i32_store.value(): case Instructions::i64_store.value(): case Instructions::f32_store.value(): case Instructions::f64_store.value(): case Instructions::i32_store8.value(): case Instructions::i32_store16.value(): case Instructions::i64_store8.value(): case Instructions::i64_store16.value(): case Instructions::i64_store32.value(): { // op (align [multi-memory: memindex] offset) 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. auto memory_index = 0; if ((align & 0x40) != 0) { align &= ~0x40; memory_index = TRY_READ(stream, LEB128, ParseError::InvalidInput); } auto offset = TRY_READ(stream, LEB128, ParseError::InvalidInput); return Instruction { opcode, MemoryArgument { align, offset, MemoryIndex(memory_index) } }; } case Instructions::local_get.value(): case Instructions::local_set.value(): case Instructions::local_tee.value(): { auto index = TRY(GenericIndexParser::parse(stream)); return Instruction { opcode, index }; } case Instructions::global_get.value(): case Instructions::global_set.value(): { auto index = TRY(GenericIndexParser::parse(stream)); return Instruction { opcode, index }; } case Instructions::memory_size.value(): case Instructions::memory_grow.value(): { // op [multi-memory: memindex]|0x00 auto memory_index = TRY_READ(stream, u8, ParseError::ExpectedKindTag); return Instruction { opcode, MemoryIndexArgument { MemoryIndex(memory_index) } }; } case Instructions::i32_const.value(): { auto value = TRY_READ(stream, LEB128, ParseError::ExpectedSignedImmediate); return Instruction { opcode, value }; } case Instructions::i64_const.value(): { // op literal auto value = TRY_READ(stream, LEB128, ParseError::ExpectedSignedImmediate); return Instruction { opcode, value }; } case Instructions::f32_const.value(): { // op literal 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 = TRY_READ(stream, LittleEndian, ParseError::ExpectedFloatingImmediate); auto floating = bit_cast(static_cast(value)); return Instruction { opcode, floating }; } case Instructions::table_get.value(): case Instructions::table_set.value(): { auto index = TRY(GenericIndexParser::parse(stream)); return Instruction { opcode, index }; } case Instructions::select_typed.value(): { auto types = TRY(parse_vector(stream)); return Instruction { opcode, types }; } case Instructions::ref_null.value(): { auto type = TRY(ValueType::parse(stream)); if (!type.is_reference()) return ParseError::InvalidType; return Instruction { opcode, type }; } case Instructions::ref_func.value(): { auto index = TRY(GenericIndexParser::parse(stream)); return Instruction { opcode, index }; } case Instructions::structured_end.value(): case Instructions::structured_else.value(): case Instructions::ref_is_null.value(): case Instructions::unreachable.value(): case Instructions::nop.value(): case Instructions::return_.value(): case Instructions::drop.value(): case Instructions::select.value(): case Instructions::i32_eqz.value(): case Instructions::i32_eq.value(): case Instructions::i32_ne.value(): case Instructions::i32_lts.value(): case Instructions::i32_ltu.value(): case Instructions::i32_gts.value(): case Instructions::i32_gtu.value(): case Instructions::i32_les.value(): case Instructions::i32_leu.value(): case Instructions::i32_ges.value(): case Instructions::i32_geu.value(): case Instructions::i64_eqz.value(): case Instructions::i64_eq.value(): case Instructions::i64_ne.value(): case Instructions::i64_lts.value(): case Instructions::i64_ltu.value(): case Instructions::i64_gts.value(): case Instructions::i64_gtu.value(): case Instructions::i64_les.value(): case Instructions::i64_leu.value(): case Instructions::i64_ges.value(): case Instructions::i64_geu.value(): case Instructions::f32_eq.value(): case Instructions::f32_ne.value(): case Instructions::f32_lt.value(): case Instructions::f32_gt.value(): case Instructions::f32_le.value(): case Instructions::f32_ge.value(): case Instructions::f64_eq.value(): case Instructions::f64_ne.value(): case Instructions::f64_lt.value(): case Instructions::f64_gt.value(): case Instructions::f64_le.value(): case Instructions::f64_ge.value(): case Instructions::i32_clz.value(): case Instructions::i32_ctz.value(): case Instructions::i32_popcnt.value(): case Instructions::i32_add.value(): case Instructions::i32_sub.value(): case Instructions::i32_mul.value(): case Instructions::i32_divs.value(): case Instructions::i32_divu.value(): case Instructions::i32_rems.value(): case Instructions::i32_remu.value(): case Instructions::i32_and.value(): case Instructions::i32_or.value(): case Instructions::i32_xor.value(): case Instructions::i32_shl.value(): case Instructions::i32_shrs.value(): case Instructions::i32_shru.value(): case Instructions::i32_rotl.value(): case Instructions::i32_rotr.value(): case Instructions::i64_clz.value(): case Instructions::i64_ctz.value(): case Instructions::i64_popcnt.value(): case Instructions::i64_add.value(): case Instructions::i64_sub.value(): case Instructions::i64_mul.value(): case Instructions::i64_divs.value(): case Instructions::i64_divu.value(): case Instructions::i64_rems.value(): case Instructions::i64_remu.value(): case Instructions::i64_and.value(): case Instructions::i64_or.value(): case Instructions::i64_xor.value(): case Instructions::i64_shl.value(): case Instructions::i64_shrs.value(): case Instructions::i64_shru.value(): case Instructions::i64_rotl.value(): case Instructions::i64_rotr.value(): case Instructions::f32_abs.value(): case Instructions::f32_neg.value(): case Instructions::f32_ceil.value(): case Instructions::f32_floor.value(): case Instructions::f32_trunc.value(): case Instructions::f32_nearest.value(): case Instructions::f32_sqrt.value(): case Instructions::f32_add.value(): case Instructions::f32_sub.value(): case Instructions::f32_mul.value(): case Instructions::f32_div.value(): case Instructions::f32_min.value(): case Instructions::f32_max.value(): case Instructions::f32_copysign.value(): case Instructions::f64_abs.value(): case Instructions::f64_neg.value(): case Instructions::f64_ceil.value(): case Instructions::f64_floor.value(): case Instructions::f64_trunc.value(): case Instructions::f64_nearest.value(): case Instructions::f64_sqrt.value(): case Instructions::f64_add.value(): case Instructions::f64_sub.value(): case Instructions::f64_mul.value(): case Instructions::f64_div.value(): case Instructions::f64_min.value(): case Instructions::f64_max.value(): case Instructions::f64_copysign.value(): case Instructions::i32_wrap_i64.value(): case Instructions::i32_trunc_sf32.value(): case Instructions::i32_trunc_uf32.value(): case Instructions::i32_trunc_sf64.value(): case Instructions::i32_trunc_uf64.value(): case Instructions::i64_extend_si32.value(): case Instructions::i64_extend_ui32.value(): case Instructions::i64_trunc_sf32.value(): case Instructions::i64_trunc_uf32.value(): case Instructions::i64_trunc_sf64.value(): case Instructions::i64_trunc_uf64.value(): case Instructions::f32_convert_si32.value(): case Instructions::f32_convert_ui32.value(): case Instructions::f32_convert_si64.value(): case Instructions::f32_convert_ui64.value(): case Instructions::f32_demote_f64.value(): case Instructions::f64_convert_si32.value(): case Instructions::f64_convert_ui32.value(): case Instructions::f64_convert_si64.value(): case Instructions::f64_convert_ui64.value(): case Instructions::f64_promote_f32.value(): case Instructions::i32_reinterpret_f32.value(): case Instructions::i64_reinterpret_f64.value(): case Instructions::f32_reinterpret_i32.value(): case Instructions::f64_reinterpret_i64.value(): case Instructions::i32_extend8_s.value(): case Instructions::i32_extend16_s.value(): case Instructions::i64_extend8_s.value(): case Instructions::i64_extend16_s.value(): case Instructions::i64_extend32_s.value(): return Instruction { opcode }; case 0xfc: case 0xfd: { // These are multibyte instructions. auto selector = TRY_READ(stream, LEB128, ParseError::InvalidInput); OpCode full_opcode = static_cast(opcode.value()) << 56 | selector; switch (full_opcode.value()) { case Instructions::i32_trunc_sat_f32_s.value(): case Instructions::i32_trunc_sat_f32_u.value(): case Instructions::i32_trunc_sat_f64_s.value(): case Instructions::i32_trunc_sat_f64_u.value(): case Instructions::i64_trunc_sat_f32_s.value(): case Instructions::i64_trunc_sat_f32_u.value(): case Instructions::i64_trunc_sat_f64_s.value(): case Instructions::i64_trunc_sat_f64_u.value(): return Instruction { full_opcode }; case Instructions::memory_init.value(): { auto index = TRY(GenericIndexParser::parse(stream)); // Proposal "multi-memory", literal 0x00 is replaced with a memory index. auto memory_index = TRY_READ(stream, u8, ParseError::InvalidInput); return Instruction { full_opcode, MemoryInitArgs { index, MemoryIndex(memory_index) } }; } case Instructions::data_drop.value(): { auto index = TRY(GenericIndexParser::parse(stream)); return Instruction { full_opcode, index }; } case Instructions::memory_copy.value(): { // Proposal "multi-memory", literal 0x00 is replaced with two memory indices, destination and source, respectively. MemoryIndex indices[] = { 0, 0 }; for (size_t i = 0; i < 2; ++i) { 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 = TRY_READ(stream, u8, ParseError::InvalidInput); return Instruction { full_opcode, MemoryIndexArgument { MemoryIndex { memory_index } } }; } case Instructions::table_init.value(): { auto element_index = TRY(GenericIndexParser::parse(stream)); auto table_index = TRY(GenericIndexParser::parse(stream)); return Instruction { full_opcode, TableElementArgs { element_index, table_index } }; } case Instructions::elem_drop.value(): { auto element_index = TRY(GenericIndexParser::parse(stream)); return Instruction { full_opcode, element_index }; } case Instructions::table_copy.value(): { auto lhs = TRY(GenericIndexParser::parse(stream)); auto rhs = TRY(GenericIndexParser::parse(stream)); return Instruction { full_opcode, TableTableArgs { lhs, rhs } }; } case Instructions::table_grow.value(): case Instructions::table_size.value(): case Instructions::table_fill.value(): { auto index = TRY(GenericIndexParser::parse(stream)); return Instruction { full_opcode, index }; } case Instructions::v128_load.value(): case Instructions::v128_load8x8_s.value(): case Instructions::v128_load8x8_u.value(): case Instructions::v128_load16x4_s.value(): case Instructions::v128_load16x4_u.value(): case Instructions::v128_load32x2_s.value(): case Instructions::v128_load32x2_u.value(): case Instructions::v128_load8_splat.value(): case Instructions::v128_load16_splat.value(): case Instructions::v128_load32_splat.value(): case Instructions::v128_load64_splat.value(): case Instructions::v128_load32_zero.value(): case Instructions::v128_load64_zero.value(): case Instructions::v128_store.value(): { // op (align [multi-memory memindex] offset) 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. auto memory_index = 0; if ((align & 0x20) != 0) { align &= ~0x20; memory_index = TRY_READ(stream, LEB128, ParseError::InvalidInput); } auto offset = TRY_READ(stream, LEB128, ParseError::ExpectedIndex); return Instruction { full_opcode, MemoryArgument { align, offset, MemoryIndex(memory_index) } }; } case Instructions::v128_load8_lane.value(): case Instructions::v128_load16_lane.value(): case Instructions::v128_load32_lane.value(): case Instructions::v128_load64_lane.value(): case Instructions::v128_store8_lane.value(): case Instructions::v128_store16_lane.value(): case Instructions::v128_store32_lane.value(): case Instructions::v128_store64_lane.value(): { // op (align [multi-memory: memindex] offset) (index) 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. auto memory_index = 0; if ((align & 0x20) != 0) { align &= ~0x20; memory_index = TRY_READ(stream, LEB128, ParseError::InvalidInput); } auto offset = TRY_READ(stream, LEB128, ParseError::ExpectedIndex); auto index = TRY_READ(stream, u8, ParseError::InvalidInput); return Instruction { full_opcode, MemoryAndLaneArgument { { align, offset, MemoryIndex(memory_index) }, index } }; } case Instructions::v128_const.value(): { // op (literal:16) 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 = TRY_READ(stream, u8, ParseError::InvalidInput); lanes[i] = value; } return Instruction { full_opcode, ShuffleArgument(lanes) }; } case Instructions::i8x16_extract_lane_s.value(): case Instructions::i8x16_extract_lane_u.value(): case Instructions::i8x16_replace_lane.value(): case Instructions::i16x8_extract_lane_s.value(): case Instructions::i16x8_extract_lane_u.value(): case Instructions::i16x8_replace_lane.value(): case Instructions::i32x4_extract_lane.value(): case Instructions::i32x4_replace_lane.value(): case Instructions::i64x2_extract_lane.value(): case Instructions::i64x2_replace_lane.value(): case Instructions::f32x4_extract_lane.value(): case Instructions::f32x4_replace_lane.value(): case Instructions::f64x2_extract_lane.value(): case Instructions::f64x2_replace_lane.value(): { // op (lane) auto lane = TRY_READ(stream, u8, ParseError::InvalidInput); return Instruction { full_opcode, LaneIndex { lane } }; } case Instructions::i8x16_swizzle.value(): case Instructions::i8x16_splat.value(): case Instructions::i16x8_splat.value(): case Instructions::i32x4_splat.value(): case Instructions::i64x2_splat.value(): case Instructions::f32x4_splat.value(): case Instructions::f64x2_splat.value(): case Instructions::i8x16_eq.value(): case Instructions::i8x16_ne.value(): case Instructions::i8x16_lt_s.value(): case Instructions::i8x16_lt_u.value(): case Instructions::i8x16_gt_s.value(): case Instructions::i8x16_gt_u.value(): case Instructions::i8x16_le_s.value(): case Instructions::i8x16_le_u.value(): case Instructions::i8x16_ge_s.value(): case Instructions::i8x16_ge_u.value(): case Instructions::i16x8_eq.value(): case Instructions::i16x8_ne.value(): case Instructions::i16x8_lt_s.value(): case Instructions::i16x8_lt_u.value(): case Instructions::i16x8_gt_s.value(): case Instructions::i16x8_gt_u.value(): case Instructions::i16x8_le_s.value(): case Instructions::i16x8_le_u.value(): case Instructions::i16x8_ge_s.value(): case Instructions::i16x8_ge_u.value(): case Instructions::i32x4_eq.value(): case Instructions::i32x4_ne.value(): case Instructions::i32x4_lt_s.value(): case Instructions::i32x4_lt_u.value(): case Instructions::i32x4_gt_s.value(): case Instructions::i32x4_gt_u.value(): case Instructions::i32x4_le_s.value(): case Instructions::i32x4_le_u.value(): case Instructions::i32x4_ge_s.value(): case Instructions::i32x4_ge_u.value(): case Instructions::f32x4_eq.value(): case Instructions::f32x4_ne.value(): case Instructions::f32x4_lt.value(): case Instructions::f32x4_gt.value(): case Instructions::f32x4_le.value(): case Instructions::f32x4_ge.value(): case Instructions::f64x2_eq.value(): case Instructions::f64x2_ne.value(): case Instructions::f64x2_lt.value(): case Instructions::f64x2_gt.value(): case Instructions::f64x2_le.value(): case Instructions::f64x2_ge.value(): case Instructions::v128_not.value(): case Instructions::v128_and.value(): case Instructions::v128_andnot.value(): case Instructions::v128_or.value(): case Instructions::v128_xor.value(): case Instructions::v128_bitselect.value(): case Instructions::v128_any_true.value(): case Instructions::f32x4_demote_f64x2_zero.value(): case Instructions::f64x2_promote_low_f32x4.value(): case Instructions::i8x16_abs.value(): case Instructions::i8x16_neg.value(): case Instructions::i8x16_popcnt.value(): case Instructions::i8x16_all_true.value(): case Instructions::i8x16_bitmask.value(): case Instructions::i8x16_narrow_i16x8_s.value(): case Instructions::i8x16_narrow_i16x8_u.value(): case Instructions::f32x4_ceil.value(): case Instructions::f32x4_floor.value(): case Instructions::f32x4_trunc.value(): case Instructions::f32x4_nearest.value(): case Instructions::i8x16_shl.value(): case Instructions::i8x16_shr_s.value(): case Instructions::i8x16_shr_u.value(): case Instructions::i8x16_add.value(): case Instructions::i8x16_add_sat_s.value(): case Instructions::i8x16_add_sat_u.value(): case Instructions::i8x16_sub.value(): case Instructions::i8x16_sub_sat_s.value(): case Instructions::i8x16_sub_sat_u.value(): case Instructions::f64x2_ceil.value(): case Instructions::f64x2_floor.value(): case Instructions::i8x16_min_s.value(): case Instructions::i8x16_min_u.value(): case Instructions::i8x16_max_s.value(): case Instructions::i8x16_max_u.value(): case Instructions::f64x2_trunc.value(): case Instructions::i8x16_avgr_u.value(): case Instructions::i16x8_extadd_pairwise_i8x16_s.value(): case Instructions::i16x8_extadd_pairwise_i8x16_u.value(): case Instructions::i32x4_extadd_pairwise_i16x8_s.value(): case Instructions::i32x4_extadd_pairwise_i16x8_u.value(): case Instructions::i16x8_abs.value(): case Instructions::i16x8_neg.value(): case Instructions::i16x8_q15mulr_sat_s.value(): case Instructions::i16x8_all_true.value(): case Instructions::i16x8_bitmask.value(): case Instructions::i16x8_narrow_i32x4_s.value(): case Instructions::i16x8_narrow_i32x4_u.value(): case Instructions::i16x8_extend_low_i8x16_s.value(): case Instructions::i16x8_extend_high_i8x16_s.value(): case Instructions::i16x8_extend_low_i8x16_u.value(): case Instructions::i16x8_extend_high_i8x16_u.value(): case Instructions::i16x8_shl.value(): case Instructions::i16x8_shr_s.value(): case Instructions::i16x8_shr_u.value(): case Instructions::i16x8_add.value(): case Instructions::i16x8_add_sat_s.value(): case Instructions::i16x8_add_sat_u.value(): case Instructions::i16x8_sub.value(): case Instructions::i16x8_sub_sat_s.value(): case Instructions::i16x8_sub_sat_u.value(): case Instructions::f64x2_nearest.value(): case Instructions::i16x8_mul.value(): case Instructions::i16x8_min_s.value(): case Instructions::i16x8_min_u.value(): case Instructions::i16x8_max_s.value(): case Instructions::i16x8_max_u.value(): case Instructions::i16x8_avgr_u.value(): case Instructions::i16x8_extmul_low_i8x16_s.value(): case Instructions::i16x8_extmul_high_i8x16_s.value(): case Instructions::i16x8_extmul_low_i8x16_u.value(): case Instructions::i16x8_extmul_high_i8x16_u.value(): case Instructions::i32x4_abs.value(): case Instructions::i32x4_neg.value(): case Instructions::i32x4_all_true.value(): case Instructions::i32x4_bitmask.value(): case Instructions::i32x4_extend_low_i16x8_s.value(): case Instructions::i32x4_extend_high_i16x8_s.value(): case Instructions::i32x4_extend_low_i16x8_u.value(): case Instructions::i32x4_extend_high_i16x8_u.value(): case Instructions::i32x4_shl.value(): case Instructions::i32x4_shr_s.value(): case Instructions::i32x4_shr_u.value(): case Instructions::i32x4_add.value(): case Instructions::i32x4_sub.value(): case Instructions::i32x4_mul.value(): case Instructions::i32x4_min_s.value(): case Instructions::i32x4_min_u.value(): case Instructions::i32x4_max_s.value(): case Instructions::i32x4_max_u.value(): case Instructions::i32x4_dot_i16x8_s.value(): case Instructions::i32x4_extmul_low_i16x8_s.value(): case Instructions::i32x4_extmul_high_i16x8_s.value(): case Instructions::i32x4_extmul_low_i16x8_u.value(): case Instructions::i32x4_extmul_high_i16x8_u.value(): case Instructions::i64x2_abs.value(): case Instructions::i64x2_neg.value(): case Instructions::i64x2_all_true.value(): case Instructions::i64x2_bitmask.value(): case Instructions::i64x2_extend_low_i32x4_s.value(): case Instructions::i64x2_extend_high_i32x4_s.value(): case Instructions::i64x2_extend_low_i32x4_u.value(): case Instructions::i64x2_extend_high_i32x4_u.value(): case Instructions::i64x2_shl.value(): case Instructions::i64x2_shr_s.value(): case Instructions::i64x2_shr_u.value(): case Instructions::i64x2_add.value(): case Instructions::i64x2_sub.value(): case Instructions::i64x2_mul.value(): case Instructions::i64x2_eq.value(): case Instructions::i64x2_ne.value(): case Instructions::i64x2_lt_s.value(): case Instructions::i64x2_gt_s.value(): case Instructions::i64x2_le_s.value(): case Instructions::i64x2_ge_s.value(): case Instructions::i64x2_extmul_low_i32x4_s.value(): case Instructions::i64x2_extmul_high_i32x4_s.value(): case Instructions::i64x2_extmul_low_i32x4_u.value(): case Instructions::i64x2_extmul_high_i32x4_u.value(): case Instructions::f32x4_abs.value(): case Instructions::f32x4_neg.value(): case Instructions::f32x4_sqrt.value(): case Instructions::f32x4_add.value(): case Instructions::f32x4_sub.value(): case Instructions::f32x4_mul.value(): case Instructions::f32x4_div.value(): case Instructions::f32x4_min.value(): case Instructions::f32x4_max.value(): case Instructions::f32x4_pmin.value(): case Instructions::f32x4_pmax.value(): case Instructions::f64x2_abs.value(): case Instructions::f64x2_neg.value(): case Instructions::f64x2_sqrt.value(): case Instructions::f64x2_add.value(): case Instructions::f64x2_sub.value(): case Instructions::f64x2_mul.value(): case Instructions::f64x2_div.value(): case Instructions::f64x2_min.value(): case Instructions::f64x2_max.value(): case Instructions::f64x2_pmin.value(): case Instructions::f64x2_pmax.value(): case Instructions::i32x4_trunc_sat_f32x4_s.value(): case Instructions::i32x4_trunc_sat_f32x4_u.value(): case Instructions::f32x4_convert_i32x4_s.value(): case Instructions::f32x4_convert_i32x4_u.value(): case Instructions::i32x4_trunc_sat_f64x2_s_zero.value(): case Instructions::i32x4_trunc_sat_f64x2_u_zero.value(): case Instructions::f64x2_convert_low_i32x4_s.value(): case Instructions::f64x2_convert_low_i32x4_u.value(): // op return Instruction { full_opcode }; default: return ParseError::UnknownInstruction; } } } return ParseError::UnknownInstruction; } ParseResult CustomSection::parse(Stream& stream) { ScopeLogger logger("CustomSection"sv); auto name = TRY(parse_name(stream)); ByteBuffer data_buffer; if (data_buffer.try_resize(64).is_error()) return ParseError::OutOfMemory; while (!stream.is_eof()) { char buf[16]; auto span_or_error = stream.read_some({ buf, 16 }); if (span_or_error.is_error()) break; auto size = span_or_error.release_value().size(); if (size == 0) break; if (data_buffer.try_append(buf, size).is_error()) return ParseError::HugeAllocationRequested; } return CustomSection(name, move(data_buffer)); } ParseResult TypeSection::parse(Stream& stream) { ScopeLogger logger("TypeSection"sv); auto types = TRY(parse_vector(stream)); return TypeSection { types }; } ParseResult ImportSection::Import::parse(Stream& stream) { ScopeLogger logger("Import"sv); auto module = TRY(parse_name(stream)); auto name = TRY(parse_name(stream)); auto tag = TRY_READ(stream, u8, ParseError::ExpectedKindTag); switch (tag) { case Constants::extern_function_tag: { auto index = TRY(GenericIndexParser::parse(stream)); return Import { module, name, index }; } case Constants::extern_table_tag: return parse_with_type(stream, module, name); case Constants::extern_memory_tag: return parse_with_type(stream, module, name); case Constants::extern_global_tag: return parse_with_type(stream, module, name); default: return ParseError::InvalidTag; } } ParseResult ImportSection::parse(Stream& stream) { ScopeLogger logger("ImportSection"sv); auto imports = TRY(parse_vector(stream)); return ImportSection { imports }; } ParseResult FunctionSection::parse(Stream& stream) { ScopeLogger logger("FunctionSection"sv); auto indices = TRY(parse_vector(stream)); Vector typed_indices; typed_indices.ensure_capacity(indices.size()); for (auto entry : indices) typed_indices.append(entry); return FunctionSection { move(typed_indices) }; } ParseResult TableSection::Table::parse(Stream& stream) { ScopeLogger logger("Table"sv); auto type = TRY(TableType::parse(stream)); return Table { type }; } ParseResult TableSection::parse(Stream& stream) { ScopeLogger logger("TableSection"sv); auto tables = TRY(parse_vector(stream)); return TableSection { tables }; } ParseResult MemorySection::Memory::parse(Stream& stream) { ScopeLogger logger("Memory"sv); auto type = TRY(MemoryType::parse(stream)); return Memory { type }; } ParseResult MemorySection::parse(Stream& stream) { ScopeLogger logger("MemorySection"sv); auto memories = TRY(parse_vector(stream)); return MemorySection { memories }; } ParseResult Expression::parse(Stream& stream, Optional size_hint) { ScopeLogger logger("Expression"sv); InstructionPointer ip { 0 }; Vector stack; Vector instructions; if (size_hint.has_value()) instructions.ensure_capacity(size_hint.release_value()); while (true) { auto instruction = TRY(Instruction::parse(stream)); switch (instruction.opcode().value()) { case Instructions::block.value(): case Instructions::loop.value(): case Instructions::if_.value(): stack.append(ip); break; case Instructions::structured_end.value(): { if (stack.is_empty()) return Expression { move(instructions) }; auto entry = stack.take_last(); auto& args = instructions[entry.value()].arguments().get(); // Patch the end_ip of the last structured instruction args.end_ip = ip + (args.else_ip.has_value() ? 1 : 0); break; } case Instructions::structured_else.value(): { if (stack.is_empty()) return ParseError::UnknownInstruction; auto entry = stack.last(); auto& args = instructions[entry.value()].arguments().get(); args.else_ip = ip + 1; break; } } instructions.append(move(instruction)); ++ip; } return Expression { move(instructions) }; } ParseResult GlobalSection::Global::parse(Stream& stream) { ScopeLogger logger("Global"sv); auto type = TRY(GlobalType::parse(stream)); auto exprs = TRY(Expression::parse(stream)); return Global { type, exprs }; } ParseResult GlobalSection::parse(Stream& stream) { ScopeLogger logger("GlobalSection"sv); auto result = TRY(parse_vector(stream)); return GlobalSection { result }; } ParseResult ExportSection::Export::parse(Stream& stream) { ScopeLogger logger("Export"sv); auto name = TRY(parse_name(stream)); auto tag = TRY_READ(stream, u8, ParseError::ExpectedKindTag); auto index = TRY_READ(stream, LEB128, ParseError::ExpectedIndex); switch (tag) { case Constants::extern_function_tag: return Export { name, ExportDesc { FunctionIndex { index } } }; case Constants::extern_table_tag: return Export { name, ExportDesc { TableIndex { index } } }; case Constants::extern_memory_tag: return Export { name, ExportDesc { MemoryIndex { index } } }; case Constants::extern_global_tag: return Export { name, ExportDesc { GlobalIndex { index } } }; default: return ParseError::InvalidTag; } } ParseResult ExportSection::parse(Stream& stream) { ScopeLogger logger("ExportSection"sv); auto result = TRY(parse_vector(stream)); return ExportSection { result }; } ParseResult StartSection::StartFunction::parse(Stream& stream) { ScopeLogger logger("StartFunction"sv); auto index = TRY(GenericIndexParser::parse(stream)); return StartFunction { index }; } ParseResult StartSection::parse(Stream& stream) { ScopeLogger logger("StartSection"sv); auto result = TRY(StartFunction::parse(stream)); return StartSection { result }; } ParseResult ElementSection::Element::parse(Stream& stream) { ScopeLogger logger("Element"sv); auto tag = TRY_READ(stream, LEB128, ParseError::ExpectedKindTag); if (tag > 0x07) return ParseError::InvalidTag; auto has_passive = (tag & 0x01) != 0; auto has_explicit_index = (tag & 0x02) != 0; auto has_exprs = (tag & 0x04) != 0; Variant mode = Passive {}; if (has_passive) { if (has_explicit_index) { mode = Declarative {}; } else { mode = Passive {}; } } else { TableIndex table_index = 0; if (has_explicit_index) table_index = TRY(GenericIndexParser::parse(stream)); auto expression = TRY(Expression::parse(stream)); mode = Active { table_index, expression }; } auto type = ValueType(ValueType::FunctionReference); if (has_passive || has_explicit_index) { if (has_exprs) { type = TRY(ValueType::parse(stream)); } else { 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_ != 0x00) { return ParseError::InvalidType; } type = ValueType(ValueType::FunctionReference); } } Vector items; if (!has_exprs) { auto indices = TRY(parse_vector>(stream)); for (auto& index : indices) { Vector instructions { Instruction(Instructions::ref_func, index) }; items.empend(move(instructions)); } } else { items = TRY(parse_vector(stream)); } return Element { type, move(items), move(mode) }; } ParseResult ElementSection::parse(Stream& stream) { ScopeLogger logger("ElementSection"sv); auto result = TRY(parse_vector(stream)); return ElementSection { result }; } ParseResult Locals::parse(Stream& stream) { ScopeLogger logger("Locals"sv); auto count = TRY_READ(stream, LEB128, ParseError::InvalidSize); if (count > Constants::max_allowed_function_locals_per_type) return ParseError::HugeAllocationRequested; auto type = TRY(ValueType::parse(stream)); return Locals { count, type }; } ParseResult CodeSection::Func::parse(Stream& stream, size_t size_hint) { ScopeLogger logger("Func"sv); auto locals = TRY(parse_vector(stream)); auto body = TRY(Expression::parse(stream, size_hint)); return Func { move(locals), move(body) }; } ParseResult CodeSection::Code::parse(Stream& stream) { ScopeLogger logger("Code"sv); 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 { size, move(func) }; } ParseResult CodeSection::parse(Stream& stream) { ScopeLogger logger("CodeSection"sv); auto result = TRY(parse_vector(stream)); return CodeSection { move(result) }; } ParseResult DataSection::Data::parse(Stream& stream) { ScopeLogger logger("Data"sv); auto tag = TRY_READ(stream, LEB128, ParseError::ExpectedKindTag); if (tag > 0x02) return ParseError::InvalidTag; if (tag == 0x00) { auto expr = TRY(Expression::parse(stream)); auto init = TRY(parse_vector(stream)); return Data { Active { init, { 0 }, expr } }; } if (tag == 0x01) { auto init = TRY(parse_vector(stream)); return Data { Passive { init } }; } if (tag == 0x02) { auto index = TRY(GenericIndexParser::parse(stream)); auto expr = TRY(Expression::parse(stream)); auto init = TRY(parse_vector(stream)); return Data { Active { init, index, expr } }; } VERIFY_NOT_REACHED(); } ParseResult DataSection::parse(Stream& stream) { ScopeLogger logger("DataSection"sv); auto data = TRY(parse_vector(stream)); return DataSection { data }; } ParseResult DataCountSection::parse([[maybe_unused]] Stream& stream) { ScopeLogger logger("DataCountSection"sv); auto value_or_error = stream.read_value>(); if (value_or_error.is_error()) { if (stream.is_eof()) { // The section simply didn't contain anything. return DataCountSection { {} }; } return ParseError::ExpectedSize; } u32 value = value_or_error.release_value(); return DataCountSection { value }; } ParseResult SectionId::parse(Stream& stream) { u8 id = TRY_READ(stream, u8, ParseError::ExpectedIndex); switch (id) { case 0x00: return SectionId(SectionIdKind::Custom); case 0x01: return SectionId(SectionIdKind::Type); case 0x02: return SectionId(SectionIdKind::Import); case 0x03: return SectionId(SectionIdKind::Function); case 0x04: return SectionId(SectionIdKind::Table); case 0x05: return SectionId(SectionIdKind::Memory); case 0x06: return SectionId(SectionIdKind::Global); case 0x07: return SectionId(SectionIdKind::Export); case 0x08: return SectionId(SectionIdKind::Start); case 0x09: return SectionId(SectionIdKind::Element); case 0x0a: return SectionId(SectionIdKind::Code); case 0x0b: return SectionId(SectionIdKind::Data); case 0x0c: return SectionId(SectionIdKind::DataCount); default: return ParseError::InvalidIndex; } } ParseResult> Module::parse(Stream& stream) { ScopeLogger logger("Module"sv); u8 buf[4]; if (stream.read_until_filled({ buf, 4 }).is_error()) return with_eof_check(stream, ParseError::InvalidInput); if (Bytes { buf, 4 } != wasm_magic.span()) return with_eof_check(stream, ParseError::InvalidModuleMagic); if (stream.read_until_filled({ buf, 4 }).is_error()) return with_eof_check(stream, ParseError::InvalidInput); if (Bytes { buf, 4 } != wasm_version.span()) return with_eof_check(stream, ParseError::InvalidModuleVersion); auto last_section_id = SectionId::SectionIdKind::Custom; auto module_ptr = make_ref_counted(); auto& module = *module_ptr; while (!stream.is_eof()) { auto section_id = TRY(SectionId::parse(stream)); size_t section_size = TRY_READ(stream, LEB128, ParseError::ExpectedSize); auto section_stream = ConstrainedStream { MaybeOwned(stream), section_size }; if (section_id.kind() != SectionId::SectionIdKind::Custom && section_id.kind() == last_section_id) return ParseError::DuplicateSection; switch (section_id.kind()) { case SectionId::SectionIdKind::Custom: module.custom_sections().append(TRY(CustomSection::parse(section_stream))); break; case SectionId::SectionIdKind::Type: module.type_section() = TRY(TypeSection::parse(section_stream)); break; case SectionId::SectionIdKind::Import: module.import_section() = TRY(ImportSection::parse(section_stream)); break; case SectionId::SectionIdKind::Function: module.function_section() = TRY(FunctionSection::parse(section_stream)); break; case SectionId::SectionIdKind::Table: module.table_section() = TRY(TableSection::parse(section_stream)); break; case SectionId::SectionIdKind::Memory: module.memory_section() = TRY(MemorySection::parse(section_stream)); break; case SectionId::SectionIdKind::Global: module.global_section() = TRY(GlobalSection::parse(section_stream)); break; case SectionId::SectionIdKind::Export: module.export_section() = TRY(ExportSection::parse(section_stream)); break; case SectionId::SectionIdKind::Start: module.start_section() = TRY(StartSection::parse(section_stream)); break; case SectionId::SectionIdKind::Element: module.element_section() = TRY(ElementSection::parse(section_stream)); break; case SectionId::SectionIdKind::Code: module.code_section() = TRY(CodeSection::parse(section_stream)); break; case SectionId::SectionIdKind::Data: module.data_section() = TRY(DataSection::parse(section_stream)); break; case SectionId::SectionIdKind::DataCount: module.data_count_section() = TRY(DataCountSection::parse(section_stream)); break; default: return ParseError::InvalidIndex; } if (section_id.kind() != SectionId::SectionIdKind::Custom) { if (section_id.kind() < last_section_id) return ParseError::SectionOutOfOrder; last_section_id = section_id.kind(); } if (section_stream.remaining() != 0) return ParseError::SectionSizeMismatch; } return module_ptr; } ByteString parse_error_to_byte_string(ParseError error) { switch (error) { case ParseError::UnexpectedEof: return "Unexpected end-of-file"; case ParseError::ExpectedIndex: return "Expected a valid index value"; case ParseError::ExpectedKindTag: return "Expected a valid kind tag"; case ParseError::ExpectedSize: return "Expected a valid LEB128-encoded size"; case ParseError::ExpectedValueOrTerminator: return "Expected either a terminator or a value"; case ParseError::InvalidIndex: return "An index parsed was semantically invalid"; case ParseError::InvalidInput: return "Input data contained invalid bytes"; case ParseError::InvalidModuleMagic: return "Incorrect module magic (did not match \\0asm)"; case ParseError::InvalidModuleVersion: return "Incorrect module version"; case ParseError::InvalidSize: return "A parsed size did not make sense in context"; case ParseError::InvalidTag: return "A parsed tag did not make sense in context"; case ParseError::InvalidType: return "A parsed type did not make sense in context"; case ParseError::HugeAllocationRequested: return "Parsing caused an attempt to allocate a very big chunk of memory, likely malformed data"; case ParseError::OutOfMemory: return "The parser hit an OOM condition"; case ParseError::ExpectedFloatingImmediate: return "Expected a floating point immediate"; case ParseError::ExpectedSignedImmediate: return "Expected a signed integer immediate"; case ParseError::InvalidImmediate: return "A parsed instruction immediate was invalid for the instruction it was used for"; case ParseError::SectionSizeMismatch: return "A parsed section did not fulfill its expected size"; case ParseError::InvalidUtf8: return "A parsed string was not valid UTF-8"; case ParseError::UnknownInstruction: return "A parsed instruction was not known to this parser"; case ParseError::DuplicateSection: return "Two sections of the same type were encountered"; case ParseError::SectionOutOfOrder: return "A section encountered was not in the correct ordering"; } return "Unknown error"; } }