From 4b449e0b3d6d68abf3bb465de1de7890d08a16cb Mon Sep 17 00:00:00 2001 From: Keith Winstein Date: Fri, 23 May 2025 21:16:27 -0700 Subject: [PATCH 1/8] wasmparser: improve visit_operator performance - The BinaryReader can visit_operator by itself again, with no need for OperatorsReader or extra state, when the visitor implements the FrameStack trait. Validation uses this to avoid duplicating the frame stack. - OperatorsReader keeps an internal FrameStack and can be created from its allocations (and turned back into them), avoiding the need to allocate a stack of frames. --- crates/wasm-encoder/src/reencode.rs | 4 +- crates/wasm-mutate/src/mutators/codemotion.rs | 2 +- .../src/mutators/modify_const_exprs.rs | 2 +- crates/wasm-mutate/src/mutators/peephole.rs | 4 +- .../wasm-mutate/src/mutators/peephole/dfg.rs | 8 +- crates/wasmparser/benches/benchmark.rs | 23 +- crates/wasmparser/src/arity.rs | 13 +- crates/wasmparser/src/binary_reader.rs | 1188 ++++++++++++- crates/wasmparser/src/readers/core/code.rs | 31 +- .../wasmparser/src/readers/core/elements.rs | 9 +- crates/wasmparser/src/readers/core/init.rs | 10 +- .../wasmparser/src/readers/core/operators.rs | 1508 +++-------------- crates/wasmparser/src/validator/core.rs | 16 +- crates/wasmparser/src/validator/func.rs | 31 +- crates/wasmparser/src/validator/operators.rs | 18 +- crates/wasmprinter/src/lib.rs | 4 +- crates/wit-component/src/gc.rs | 4 +- src/bin/wasm-tools/dump.rs | 11 +- src/lib.rs | 13 +- 19 files changed, 1561 insertions(+), 1338 deletions(-) diff --git a/crates/wasm-encoder/src/reencode.rs b/crates/wasm-encoder/src/reencode.rs index 46f87d98bd..512098edb8 100644 --- a/crates/wasm-encoder/src/reencode.rs +++ b/crates/wasm-encoder/src/reencode.rs @@ -1594,7 +1594,7 @@ pub mod utils { reencoder: &mut T, const_expr: wasmparser::ConstExpr, ) -> Result> { - let mut ops = const_expr.get_operators_reader(); + let mut ops = const_expr.get_operators_reader(Default::default()); let mut bytes = Vec::new(); while !ops.is_end_then_eof() { @@ -1740,7 +1740,7 @@ pub mod utils { func: wasmparser::FunctionBody<'_>, ) -> Result<(), Error> { let mut f = reencoder.new_function_with_parsed_locals(&func)?; - let mut reader = func.get_operators_reader()?; + let mut reader = func.get_operators_reader(Default::default())?; while !reader.eof() { f.instruction(&reencoder.parse_instruction(&mut reader)?); } diff --git a/crates/wasm-mutate/src/mutators/codemotion.rs b/crates/wasm-mutate/src/mutators/codemotion.rs index b4b0854a08..1d8d02d750 100644 --- a/crates/wasm-mutate/src/mutators/codemotion.rs +++ b/crates/wasm-mutate/src/mutators/codemotion.rs @@ -75,7 +75,7 @@ impl CodemotionMutator { for fidx in (function_to_mutate..function_count).chain(0..function_to_mutate) { config.consume_fuel(1)?; let reader = all_readers[fidx as usize].clone(); - let operatorreader = reader.get_operators_reader()?; + let operatorreader = reader.get_operators_reader(Default::default())?; let operators = operatorreader .into_iter_with_offsets() diff --git a/crates/wasm-mutate/src/mutators/modify_const_exprs.rs b/crates/wasm-mutate/src/mutators/modify_const_exprs.rs index 28f794efba..e5fd15462b 100644 --- a/crates/wasm-mutate/src/mutators/modify_const_exprs.rs +++ b/crates/wasm-mutate/src/mutators/modify_const_exprs.rs @@ -62,7 +62,7 @@ impl<'cfg, 'wasm> Reencode for InitTranslator<'cfg, 'wasm> { return reencode::utils::const_expr(self, e); } - let mut reader = e.get_operators_reader(); + let mut reader = e.get_operators_reader(Default::default()); if !self.config.reduce { // FIXME: implement non-reducing mutations for constant expressions. diff --git a/crates/wasm-mutate/src/mutators/peephole.rs b/crates/wasm-mutate/src/mutators/peephole.rs index 58e2224f55..45d7ee1760 100644 --- a/crates/wasm-mutate/src/mutators/peephole.rs +++ b/crates/wasm-mutate/src/mutators/peephole.rs @@ -122,7 +122,7 @@ impl PeepholeMutator { } let reader = readers[function_to_mutate as usize].clone(); - let operatorreader = reader.get_operators_reader()?; + let operatorreader = reader.get_operators_reader(Default::default())?; let mut localsreader = reader.get_locals_reader()?; let operators = operatorreader .into_iter_with_offsets() @@ -490,7 +490,7 @@ macro_rules! match_code_mutation { }); } Payload::CodeSectionEntry(reader) => { - let operatorsreader = reader.get_operators_reader().unwrap(); + let operatorsreader = reader.get_operators_reader(Default::default()).unwrap(); let range = operatorsreader.get_binary_reader().range(); let operators = operatorsreader .into_iter_with_offsets() diff --git a/crates/wasm-mutate/src/mutators/peephole/dfg.rs b/crates/wasm-mutate/src/mutators/peephole/dfg.rs index c02c96c132..db6af5c127 100644 --- a/crates/wasm-mutate/src/mutators/peephole/dfg.rs +++ b/crates/wasm-mutate/src/mutators/peephole/dfg.rs @@ -1314,7 +1314,7 @@ mod tests { match payload { wasmparser::Payload::CodeSectionEntry(reader) => { let operators = reader - .get_operators_reader() + .get_operators_reader(Default::default()) .unwrap() .into_iter_with_offsets() .collect::>>() @@ -1370,7 +1370,7 @@ mod tests { match payload { wasmparser::Payload::CodeSectionEntry(reader) => { let operators = reader - .get_operators_reader() + .get_operators_reader(Default::default()) .unwrap() .into_iter_with_offsets() .collect::>>() @@ -1452,7 +1452,7 @@ mod tests { match payload { wasmparser::Payload::CodeSectionEntry(reader) => { let operators = reader - .get_operators_reader() + .get_operators_reader(Default::default()) .unwrap() .into_iter_with_offsets() .collect::>>() @@ -1509,7 +1509,7 @@ mod tests { match payload { wasmparser::Payload::CodeSectionEntry(reader) => { let operators = reader - .get_operators_reader() + .get_operators_reader(Default::default()) .unwrap() .into_iter_with_offsets() .collect::>>() diff --git a/crates/wasmparser/benches/benchmark.rs b/crates/wasmparser/benches/benchmark.rs index 3ed10a8f9e..a3de9f7ad6 100644 --- a/crates/wasmparser/benches/benchmark.rs +++ b/crates/wasmparser/benches/benchmark.rs @@ -80,6 +80,7 @@ fn collect_test_files(path: &Path, list: &mut Vec) -> Result<()> /// so that we can report better errors in case of failures. fn read_all_wasm(wasm: &[u8]) -> Result<()> { use Payload::*; + let mut allocs = wasmparser::OperatorsReaderAllocations::default(); for item in Parser::new(0).parse_all(wasm) { match item? { TypeSection(s) => { @@ -114,9 +115,14 @@ fn read_all_wasm(wasm: &[u8]) -> Result<()> { } GlobalSection(s) => { for item in s { - for op in item?.init_expr.get_operators_reader() { + let mut ops = item? + .init_expr + .get_operators_reader(std::mem::take(&mut allocs)) + .into_iter(); + for op in ops.by_ref() { op?; } + allocs = ops.into_allocations(); } } ExportSection(s) => { @@ -128,9 +134,13 @@ fn read_all_wasm(wasm: &[u8]) -> Result<()> { for item in s { let item = item?; if let ElementKind::Active { offset_expr, .. } = item.kind { - for op in offset_expr.get_operators_reader() { + let mut ops = offset_expr + .get_operators_reader(std::mem::take(&mut allocs)) + .into_iter(); + for op in ops.by_ref() { op?; } + allocs = ops.into_allocations(); } match item.items { wasmparser::ElementItems::Functions(r) => { @@ -150,9 +160,13 @@ fn read_all_wasm(wasm: &[u8]) -> Result<()> { for item in s { let item = item?; if let DataKind::Active { offset_expr, .. } = item.kind { - for op in offset_expr.get_operators_reader() { + let mut ops = offset_expr + .get_operators_reader(std::mem::take(&mut allocs)) + .into_iter(); + for op in ops.by_ref() { op?; } + allocs = ops.into_allocations(); } } } @@ -161,11 +175,12 @@ fn read_all_wasm(wasm: &[u8]) -> Result<()> { for item in locals.by_ref() { let _ = item?; } - let mut ops = locals.into_operators_reader(); + let mut ops = locals.into_operators_reader(std::mem::take(&mut allocs)); while !ops.eof() { ops.visit_operator(&mut NopVisit)?; } ops.finish()?; + allocs = ops.into_allocations(); } // Component sections diff --git a/crates/wasmparser/src/arity.rs b/crates/wasmparser/src/arity.rs index aa1cc53b7e..9eef6fa828 100644 --- a/crates/wasmparser/src/arity.rs +++ b/crates/wasmparser/src/arity.rs @@ -15,8 +15,8 @@ use crate::prelude::*; use crate::{ - BinaryReaderError, BlockType, BrTable, CompositeInnerType, ContType, FrameKind, FuncType, - Operator, OperatorsReader, RefType, Result, ResumeTable, SubType, TryTable, ValType, + BlockType, BrTable, CompositeInnerType, ContType, FrameKind, FuncType, Operator, RefType, + ResumeTable, SubType, TryTable, ValType, }; /// To compute the arity (param and result counts) of "variable-arity" @@ -70,15 +70,6 @@ pub trait ModuleArity { } } -impl OperatorsReader<'_> { - /// Read the next operator and compute its arity (param and result counts) - pub fn operator_arity(&self, module: &impl ModuleArity) -> Result<(u32, u32)> { - self.clone().read()?.operator_arity(module).ok_or_else(|| { - BinaryReaderError::new("operator arity is unknown", self.original_position()) - }) - } -} - impl Operator<'_> { /// Compute the arity (param and result counts) of the operator, given /// an impl ModuleArity, which stores the necessary module state. diff --git a/crates/wasmparser/src/binary_reader.rs b/crates/wasmparser/src/binary_reader.rs index bcaaade54c..8754a604d0 100644 --- a/crates/wasmparser/src/binary_reader.rs +++ b/crates/wasmparser/src/binary_reader.rs @@ -190,7 +190,7 @@ impl<'a> BinaryReader<'a> { /// Note that the activated set of features does not guarantee that /// `BinaryReader` will return an error for disabled features. For example /// if SIMD is disabled then SIMD instructions will still be parsed via - /// [`OperatorsReader::visit_operator`]. Validation must still be performed to + /// [`BinaryReader::visit_operator`]. Validation must still be performed to /// provide a strict guarantee that if a feature is disabled that a binary /// doesn't leverage the feature. The activated set of features here instead /// only affects locations where preexisting bytes are reinterpreted in @@ -861,3 +861,1189 @@ where } } } + +impl<'a> BinaryReader<'a> { + /// Function that must be called after the last opcode in an expression (instruction sequence) + /// has been processed. Returns an error if there is extra data after the operators. + pub fn finish_expression(&self, stack: &impl FrameStack) -> Result<()> { + if stack.current_frame().is_some() { + bail!( + self.original_position(), + "control frames remain at end of function body or expression" + ); + } + if !self.eof() { + bail!( + self.original_position(), + "unexpected data at the end of operators" + ); + } + Ok(()) + } + + #[inline] + fn expect_frame(&mut self, stack: &impl FrameStack, k: &FrameKind, found: &str) -> Result<()> { + if stack.current_frame() == Some(k) { + return Ok(()); + } + bail!( + self.original_position(), + "`{}` found outside `{:?}` block", + found, + k + ); + } + + /// Visit the next available operator with the specified [`VisitOperator`] instance + /// that is also a [`FrameStack`]. + /// + /// See the documentation for [`OperatorsReader::visit_operator`] for a version that + /// does not require the visitor to implement [`FrameStack`]. + pub fn visit_operator(&mut self, visitor: &mut T) -> Result<>::Output> + where + T: VisitOperator<'a> + FrameStack, + { + if visitor.current_frame().is_none() { + bail!( + self.original_position(), + "operators remaining after end of function body or expression" + ); + } + let pos = self.original_position(); + let code = self.read_u8()?; + Ok(match code { + 0x00 => visitor.visit_unreachable(), + 0x01 => visitor.visit_nop(), + 0x02 => visitor.visit_block(self.read_block_type()?), + 0x03 => visitor.visit_loop(self.read_block_type()?), + 0x04 => visitor.visit_if(self.read_block_type()?), + 0x05 => { + self.expect_frame(visitor, &FrameKind::If, "else")?; + visitor.visit_else() + } + 0x06 => { + if !self.legacy_exceptions() { + bail!( + pos, + "legacy_exceptions feature required for try instruction" + ); + } + visitor.visit_try(self.read_block_type()?) + } + 0x07 => { + if !self.legacy_exceptions() { + bail!( + pos, + "legacy_exceptions feature required for catch instruction" + ); + } + match self.expect_frame(visitor, &FrameKind::LegacyCatch, "catch") { + Ok(()) => (), + Err(_) => self.expect_frame(visitor, &FrameKind::LegacyTry, "catch")?, + } + visitor.visit_catch(self.read_var_u32()?) + } + 0x08 => visitor.visit_throw(self.read_var_u32()?), + 0x09 => visitor.visit_rethrow(self.read_var_u32()?), + 0x0a => visitor.visit_throw_ref(), + 0x0b => visitor.visit_end(), + 0x0c => visitor.visit_br(self.read_var_u32()?), + 0x0d => visitor.visit_br_if(self.read_var_u32()?), + 0x0e => visitor.visit_br_table(self.read_br_table()?), + 0x0f => visitor.visit_return(), + 0x10 => visitor.visit_call(self.read_var_u32()?), + 0x11 => { + let index = self.read_var_u32()?; + let table = self.read_call_indirect_table_immediate()?; + visitor.visit_call_indirect(index, table) + } + 0x12 => visitor.visit_return_call(self.read_var_u32()?), + 0x13 => visitor.visit_return_call_indirect(self.read_var_u32()?, self.read_var_u32()?), + 0x14 => visitor.visit_call_ref(self.read()?), + 0x15 => visitor.visit_return_call_ref(self.read()?), + 0x18 => { + self.expect_frame(visitor, &FrameKind::LegacyTry, "delegate")?; + visitor.visit_delegate(self.read_var_u32()?) + } + 0x19 => { + if !self.legacy_exceptions() { + bail!( + pos, + "legacy_exceptions feature required for catch_all instruction" + ); + } + match self.expect_frame(visitor, &FrameKind::LegacyCatch, "catch_all") { + Ok(()) => (), + Err(_) => self.expect_frame(visitor, &FrameKind::LegacyTry, "catch_all")?, + } + visitor.visit_catch_all() + } + 0x1a => visitor.visit_drop(), + 0x1b => visitor.visit_select(), + 0x1c => { + let result_count = self.read_size(MAX_WASM_SELECT_RESULT_SIZE, "select types")?; + if result_count == 1 { + visitor.visit_typed_select(self.read()?) + } else { + let mut results = Vec::new(); + results.reserve_exact(result_count); + for _ in 0..result_count { + results.push(self.read()?); + } + visitor.visit_typed_select_multi(results) + } + } + 0x1f => visitor.visit_try_table(self.read()?), + + 0x20 => visitor.visit_local_get(self.read_var_u32()?), + 0x21 => visitor.visit_local_set(self.read_var_u32()?), + 0x22 => visitor.visit_local_tee(self.read_var_u32()?), + 0x23 => visitor.visit_global_get(self.read_var_u32()?), + 0x24 => visitor.visit_global_set(self.read_var_u32()?), + 0x25 => visitor.visit_table_get(self.read_var_u32()?), + 0x26 => visitor.visit_table_set(self.read_var_u32()?), + + 0x28 => visitor.visit_i32_load(self.read_memarg(2)?), + 0x29 => visitor.visit_i64_load(self.read_memarg(3)?), + 0x2a => visitor.visit_f32_load(self.read_memarg(2)?), + 0x2b => visitor.visit_f64_load(self.read_memarg(3)?), + 0x2c => visitor.visit_i32_load8_s(self.read_memarg(0)?), + 0x2d => visitor.visit_i32_load8_u(self.read_memarg(0)?), + 0x2e => visitor.visit_i32_load16_s(self.read_memarg(1)?), + 0x2f => visitor.visit_i32_load16_u(self.read_memarg(1)?), + 0x30 => visitor.visit_i64_load8_s(self.read_memarg(0)?), + 0x31 => visitor.visit_i64_load8_u(self.read_memarg(0)?), + 0x32 => visitor.visit_i64_load16_s(self.read_memarg(1)?), + 0x33 => visitor.visit_i64_load16_u(self.read_memarg(1)?), + 0x34 => visitor.visit_i64_load32_s(self.read_memarg(2)?), + 0x35 => visitor.visit_i64_load32_u(self.read_memarg(2)?), + 0x36 => visitor.visit_i32_store(self.read_memarg(2)?), + 0x37 => visitor.visit_i64_store(self.read_memarg(3)?), + 0x38 => visitor.visit_f32_store(self.read_memarg(2)?), + 0x39 => visitor.visit_f64_store(self.read_memarg(3)?), + 0x3a => visitor.visit_i32_store8(self.read_memarg(0)?), + 0x3b => visitor.visit_i32_store16(self.read_memarg(1)?), + 0x3c => visitor.visit_i64_store8(self.read_memarg(0)?), + 0x3d => visitor.visit_i64_store16(self.read_memarg(1)?), + 0x3e => visitor.visit_i64_store32(self.read_memarg(2)?), + 0x3f => { + let mem = self.read_memory_index_or_zero_if_not_multi_memory()?; + visitor.visit_memory_size(mem) + } + 0x40 => { + let mem = self.read_memory_index_or_zero_if_not_multi_memory()?; + visitor.visit_memory_grow(mem) + } + + 0x41 => visitor.visit_i32_const(self.read_var_i32()?), + 0x42 => visitor.visit_i64_const(self.read_var_i64()?), + 0x43 => visitor.visit_f32_const(self.read_f32()?), + 0x44 => visitor.visit_f64_const(self.read_f64()?), + + 0x45 => visitor.visit_i32_eqz(), + 0x46 => visitor.visit_i32_eq(), + 0x47 => visitor.visit_i32_ne(), + 0x48 => visitor.visit_i32_lt_s(), + 0x49 => visitor.visit_i32_lt_u(), + 0x4a => visitor.visit_i32_gt_s(), + 0x4b => visitor.visit_i32_gt_u(), + 0x4c => visitor.visit_i32_le_s(), + 0x4d => visitor.visit_i32_le_u(), + 0x4e => visitor.visit_i32_ge_s(), + 0x4f => visitor.visit_i32_ge_u(), + 0x50 => visitor.visit_i64_eqz(), + 0x51 => visitor.visit_i64_eq(), + 0x52 => visitor.visit_i64_ne(), + 0x53 => visitor.visit_i64_lt_s(), + 0x54 => visitor.visit_i64_lt_u(), + 0x55 => visitor.visit_i64_gt_s(), + 0x56 => visitor.visit_i64_gt_u(), + 0x57 => visitor.visit_i64_le_s(), + 0x58 => visitor.visit_i64_le_u(), + 0x59 => visitor.visit_i64_ge_s(), + 0x5a => visitor.visit_i64_ge_u(), + 0x5b => visitor.visit_f32_eq(), + 0x5c => visitor.visit_f32_ne(), + 0x5d => visitor.visit_f32_lt(), + 0x5e => visitor.visit_f32_gt(), + 0x5f => visitor.visit_f32_le(), + 0x60 => visitor.visit_f32_ge(), + 0x61 => visitor.visit_f64_eq(), + 0x62 => visitor.visit_f64_ne(), + 0x63 => visitor.visit_f64_lt(), + 0x64 => visitor.visit_f64_gt(), + 0x65 => visitor.visit_f64_le(), + 0x66 => visitor.visit_f64_ge(), + 0x67 => visitor.visit_i32_clz(), + 0x68 => visitor.visit_i32_ctz(), + 0x69 => visitor.visit_i32_popcnt(), + 0x6a => visitor.visit_i32_add(), + 0x6b => visitor.visit_i32_sub(), + 0x6c => visitor.visit_i32_mul(), + 0x6d => visitor.visit_i32_div_s(), + 0x6e => visitor.visit_i32_div_u(), + 0x6f => visitor.visit_i32_rem_s(), + 0x70 => visitor.visit_i32_rem_u(), + 0x71 => visitor.visit_i32_and(), + 0x72 => visitor.visit_i32_or(), + 0x73 => visitor.visit_i32_xor(), + 0x74 => visitor.visit_i32_shl(), + 0x75 => visitor.visit_i32_shr_s(), + 0x76 => visitor.visit_i32_shr_u(), + 0x77 => visitor.visit_i32_rotl(), + 0x78 => visitor.visit_i32_rotr(), + 0x79 => visitor.visit_i64_clz(), + 0x7a => visitor.visit_i64_ctz(), + 0x7b => visitor.visit_i64_popcnt(), + 0x7c => visitor.visit_i64_add(), + 0x7d => visitor.visit_i64_sub(), + 0x7e => visitor.visit_i64_mul(), + 0x7f => visitor.visit_i64_div_s(), + 0x80 => visitor.visit_i64_div_u(), + 0x81 => visitor.visit_i64_rem_s(), + 0x82 => visitor.visit_i64_rem_u(), + 0x83 => visitor.visit_i64_and(), + 0x84 => visitor.visit_i64_or(), + 0x85 => visitor.visit_i64_xor(), + 0x86 => visitor.visit_i64_shl(), + 0x87 => visitor.visit_i64_shr_s(), + 0x88 => visitor.visit_i64_shr_u(), + 0x89 => visitor.visit_i64_rotl(), + 0x8a => visitor.visit_i64_rotr(), + 0x8b => visitor.visit_f32_abs(), + 0x8c => visitor.visit_f32_neg(), + 0x8d => visitor.visit_f32_ceil(), + 0x8e => visitor.visit_f32_floor(), + 0x8f => visitor.visit_f32_trunc(), + 0x90 => visitor.visit_f32_nearest(), + 0x91 => visitor.visit_f32_sqrt(), + 0x92 => visitor.visit_f32_add(), + 0x93 => visitor.visit_f32_sub(), + 0x94 => visitor.visit_f32_mul(), + 0x95 => visitor.visit_f32_div(), + 0x96 => visitor.visit_f32_min(), + 0x97 => visitor.visit_f32_max(), + 0x98 => visitor.visit_f32_copysign(), + 0x99 => visitor.visit_f64_abs(), + 0x9a => visitor.visit_f64_neg(), + 0x9b => visitor.visit_f64_ceil(), + 0x9c => visitor.visit_f64_floor(), + 0x9d => visitor.visit_f64_trunc(), + 0x9e => visitor.visit_f64_nearest(), + 0x9f => visitor.visit_f64_sqrt(), + 0xa0 => visitor.visit_f64_add(), + 0xa1 => visitor.visit_f64_sub(), + 0xa2 => visitor.visit_f64_mul(), + 0xa3 => visitor.visit_f64_div(), + 0xa4 => visitor.visit_f64_min(), + 0xa5 => visitor.visit_f64_max(), + 0xa6 => visitor.visit_f64_copysign(), + 0xa7 => visitor.visit_i32_wrap_i64(), + 0xa8 => visitor.visit_i32_trunc_f32_s(), + 0xa9 => visitor.visit_i32_trunc_f32_u(), + 0xaa => visitor.visit_i32_trunc_f64_s(), + 0xab => visitor.visit_i32_trunc_f64_u(), + 0xac => visitor.visit_i64_extend_i32_s(), + 0xad => visitor.visit_i64_extend_i32_u(), + 0xae => visitor.visit_i64_trunc_f32_s(), + 0xaf => visitor.visit_i64_trunc_f32_u(), + 0xb0 => visitor.visit_i64_trunc_f64_s(), + 0xb1 => visitor.visit_i64_trunc_f64_u(), + 0xb2 => visitor.visit_f32_convert_i32_s(), + 0xb3 => visitor.visit_f32_convert_i32_u(), + 0xb4 => visitor.visit_f32_convert_i64_s(), + 0xb5 => visitor.visit_f32_convert_i64_u(), + 0xb6 => visitor.visit_f32_demote_f64(), + 0xb7 => visitor.visit_f64_convert_i32_s(), + 0xb8 => visitor.visit_f64_convert_i32_u(), + 0xb9 => visitor.visit_f64_convert_i64_s(), + 0xba => visitor.visit_f64_convert_i64_u(), + 0xbb => visitor.visit_f64_promote_f32(), + 0xbc => visitor.visit_i32_reinterpret_f32(), + 0xbd => visitor.visit_i64_reinterpret_f64(), + 0xbe => visitor.visit_f32_reinterpret_i32(), + 0xbf => visitor.visit_f64_reinterpret_i64(), + + 0xc0 => visitor.visit_i32_extend8_s(), + 0xc1 => visitor.visit_i32_extend16_s(), + 0xc2 => visitor.visit_i64_extend8_s(), + 0xc3 => visitor.visit_i64_extend16_s(), + 0xc4 => visitor.visit_i64_extend32_s(), + + 0xd0 => visitor.visit_ref_null(self.read()?), + 0xd1 => visitor.visit_ref_is_null(), + 0xd2 => visitor.visit_ref_func(self.read_var_u32()?), + 0xd3 => visitor.visit_ref_eq(), + 0xd4 => visitor.visit_ref_as_non_null(), + 0xd5 => visitor.visit_br_on_null(self.read_var_u32()?), + 0xd6 => visitor.visit_br_on_non_null(self.read_var_u32()?), + + 0xe0 => visitor.visit_cont_new(self.read_var_u32()?), + 0xe1 => visitor.visit_cont_bind(self.read_var_u32()?, self.read_var_u32()?), + 0xe2 => visitor.visit_suspend(self.read_var_u32()?), + 0xe3 => visitor.visit_resume(self.read_var_u32()?, self.read()?), + 0xe4 => { + visitor.visit_resume_throw(self.read_var_u32()?, self.read_var_u32()?, self.read()?) + } + 0xe5 => visitor.visit_switch(self.read_var_u32()?, self.read_var_u32()?), + + 0xfb => self.visit_0xfb_operator(pos, visitor)?, + 0xfc => self.visit_0xfc_operator(pos, visitor)?, + 0xfd => { + #[cfg(feature = "simd")] + if let Some(mut visitor) = visitor.simd_visitor() { + return self.visit_0xfd_operator(pos, &mut visitor); + } + bail!(pos, "unexpected SIMD opcode: 0x{code:x}") + } + 0xfe => self.visit_0xfe_operator(pos, visitor)?, + + _ => bail!(pos, "illegal opcode: 0x{code:x}"), + }) + } + + fn visit_0xfb_operator( + &mut self, + pos: usize, + visitor: &mut T, + ) -> Result<>::Output> + where + T: VisitOperator<'a>, + { + let code = self.read_var_u32()?; + Ok(match code { + 0x0 => { + let type_index = self.read_var_u32()?; + visitor.visit_struct_new(type_index) + } + 0x01 => { + let type_index = self.read_var_u32()?; + visitor.visit_struct_new_default(type_index) + } + 0x02 => { + let type_index = self.read_var_u32()?; + let field_index = self.read_var_u32()?; + visitor.visit_struct_get(type_index, field_index) + } + 0x03 => { + let type_index = self.read_var_u32()?; + let field_index = self.read_var_u32()?; + visitor.visit_struct_get_s(type_index, field_index) + } + 0x04 => { + let type_index = self.read_var_u32()?; + let field_index = self.read_var_u32()?; + visitor.visit_struct_get_u(type_index, field_index) + } + 0x05 => { + let type_index = self.read_var_u32()?; + let field_index = self.read_var_u32()?; + visitor.visit_struct_set(type_index, field_index) + } + 0x06 => { + let type_index = self.read_var_u32()?; + visitor.visit_array_new(type_index) + } + 0x07 => { + let type_index = self.read_var_u32()?; + visitor.visit_array_new_default(type_index) + } + 0x08 => { + let type_index = self.read_var_u32()?; + let n = self.read_var_u32()?; + visitor.visit_array_new_fixed(type_index, n) + } + 0x09 => { + let type_index = self.read_var_u32()?; + let data_index = self.read_var_u32()?; + visitor.visit_array_new_data(type_index, data_index) + } + 0x0a => { + let type_index = self.read_var_u32()?; + let elem_index = self.read_var_u32()?; + visitor.visit_array_new_elem(type_index, elem_index) + } + 0x0b => { + let type_index = self.read_var_u32()?; + visitor.visit_array_get(type_index) + } + 0x0c => { + let type_index = self.read_var_u32()?; + visitor.visit_array_get_s(type_index) + } + 0x0d => { + let type_index = self.read_var_u32()?; + visitor.visit_array_get_u(type_index) + } + 0x0e => { + let type_index = self.read_var_u32()?; + visitor.visit_array_set(type_index) + } + 0x0f => visitor.visit_array_len(), + 0x10 => { + let type_index = self.read_var_u32()?; + visitor.visit_array_fill(type_index) + } + 0x11 => { + let type_index_dst = self.read_var_u32()?; + let type_index_src = self.read_var_u32()?; + visitor.visit_array_copy(type_index_dst, type_index_src) + } + 0x12 => { + let type_index = self.read_var_u32()?; + let data_index = self.read_var_u32()?; + visitor.visit_array_init_data(type_index, data_index) + } + 0x13 => { + let type_index = self.read_var_u32()?; + let elem_index = self.read_var_u32()?; + visitor.visit_array_init_elem(type_index, elem_index) + } + 0x14 => visitor.visit_ref_test_non_null(self.read()?), + 0x15 => visitor.visit_ref_test_nullable(self.read()?), + 0x16 => visitor.visit_ref_cast_non_null(self.read()?), + 0x17 => visitor.visit_ref_cast_nullable(self.read()?), + 0x18 => { + let pos = self.original_position(); + let cast_flags = self.read_u8()?; + let relative_depth = self.read_var_u32()?; + let (from_type_nullable, to_type_nullable) = match cast_flags { + 0b00 => (false, false), + 0b01 => (true, false), + 0b10 => (false, true), + 0b11 => (true, true), + _ => bail!(pos, "invalid cast flags: {cast_flags:08b}"), + }; + let from_heap_type = self.read()?; + let from_ref_type = + RefType::new(from_type_nullable, from_heap_type).ok_or_else(|| { + format_err!(pos, "implementation error: type index too large") + })?; + let to_heap_type = self.read()?; + let to_ref_type = + RefType::new(to_type_nullable, to_heap_type).ok_or_else(|| { + format_err!(pos, "implementation error: type index too large") + })?; + visitor.visit_br_on_cast(relative_depth, from_ref_type, to_ref_type) + } + 0x19 => { + let pos = self.original_position(); + let cast_flags = self.read_u8()?; + let relative_depth = self.read_var_u32()?; + let (from_type_nullable, to_type_nullable) = match cast_flags { + 0 => (false, false), + 1 => (true, false), + 2 => (false, true), + 3 => (true, true), + _ => bail!(pos, "invalid cast flags: {cast_flags:08b}"), + }; + let from_heap_type = self.read()?; + let from_ref_type = + RefType::new(from_type_nullable, from_heap_type).ok_or_else(|| { + format_err!(pos, "implementation error: type index too large") + })?; + let to_heap_type = self.read()?; + let to_ref_type = + RefType::new(to_type_nullable, to_heap_type).ok_or_else(|| { + format_err!(pos, "implementation error: type index too large") + })?; + visitor.visit_br_on_cast_fail(relative_depth, from_ref_type, to_ref_type) + } + + 0x1a => visitor.visit_any_convert_extern(), + 0x1b => visitor.visit_extern_convert_any(), + + 0x1c => visitor.visit_ref_i31(), + 0x1d => visitor.visit_i31_get_s(), + 0x1e => visitor.visit_i31_get_u(), + + _ => bail!(pos, "unknown 0xfb subopcode: 0x{code:x}"), + }) + } + + fn visit_0xfc_operator( + &mut self, + pos: usize, + visitor: &mut T, + ) -> Result<>::Output> + where + T: VisitOperator<'a>, + { + let code = self.read_var_u32()?; + Ok(match code { + 0x00 => visitor.visit_i32_trunc_sat_f32_s(), + 0x01 => visitor.visit_i32_trunc_sat_f32_u(), + 0x02 => visitor.visit_i32_trunc_sat_f64_s(), + 0x03 => visitor.visit_i32_trunc_sat_f64_u(), + 0x04 => visitor.visit_i64_trunc_sat_f32_s(), + 0x05 => visitor.visit_i64_trunc_sat_f32_u(), + 0x06 => visitor.visit_i64_trunc_sat_f64_s(), + 0x07 => visitor.visit_i64_trunc_sat_f64_u(), + + 0x08 => { + let segment = self.read_var_u32()?; + let mem = self.read_var_u32()?; + visitor.visit_memory_init(segment, mem) + } + 0x09 => { + let segment = self.read_var_u32()?; + visitor.visit_data_drop(segment) + } + 0x0a => { + let dst = self.read_var_u32()?; + let src = self.read_var_u32()?; + visitor.visit_memory_copy(dst, src) + } + 0x0b => { + let mem = self.read_var_u32()?; + visitor.visit_memory_fill(mem) + } + 0x0c => { + let segment = self.read_var_u32()?; + let table = self.read_var_u32()?; + visitor.visit_table_init(segment, table) + } + 0x0d => { + let segment = self.read_var_u32()?; + visitor.visit_elem_drop(segment) + } + 0x0e => { + let dst_table = self.read_var_u32()?; + let src_table = self.read_var_u32()?; + visitor.visit_table_copy(dst_table, src_table) + } + + 0x0f => { + let table = self.read_var_u32()?; + visitor.visit_table_grow(table) + } + 0x10 => { + let table = self.read_var_u32()?; + visitor.visit_table_size(table) + } + + 0x11 => { + let table = self.read_var_u32()?; + visitor.visit_table_fill(table) + } + + 0x12 => { + let mem = self.read_var_u32()?; + visitor.visit_memory_discard(mem) + } + + 0x13 => visitor.visit_i64_add128(), + 0x14 => visitor.visit_i64_sub128(), + 0x15 => visitor.visit_i64_mul_wide_s(), + 0x16 => visitor.visit_i64_mul_wide_u(), + + _ => bail!(pos, "unknown 0xfc subopcode: 0x{code:x}"), + }) + } + + #[cfg(feature = "simd")] + pub(super) fn visit_0xfd_operator( + &mut self, + pos: usize, + visitor: &mut T, + ) -> Result<>::Output> + where + T: VisitSimdOperator<'a>, + { + let code = self.read_var_u32()?; + Ok(match code { + 0x00 => visitor.visit_v128_load(self.read_memarg(4)?), + 0x01 => visitor.visit_v128_load8x8_s(self.read_memarg(3)?), + 0x02 => visitor.visit_v128_load8x8_u(self.read_memarg(3)?), + 0x03 => visitor.visit_v128_load16x4_s(self.read_memarg(3)?), + 0x04 => visitor.visit_v128_load16x4_u(self.read_memarg(3)?), + 0x05 => visitor.visit_v128_load32x2_s(self.read_memarg(3)?), + 0x06 => visitor.visit_v128_load32x2_u(self.read_memarg(3)?), + 0x07 => visitor.visit_v128_load8_splat(self.read_memarg(0)?), + 0x08 => visitor.visit_v128_load16_splat(self.read_memarg(1)?), + 0x09 => visitor.visit_v128_load32_splat(self.read_memarg(2)?), + 0x0a => visitor.visit_v128_load64_splat(self.read_memarg(3)?), + + 0x0b => visitor.visit_v128_store(self.read_memarg(4)?), + 0x0c => visitor.visit_v128_const(self.read_v128()?), + 0x0d => { + let mut lanes: [u8; 16] = [0; 16]; + for lane in &mut lanes { + *lane = self.read_lane_index()? + } + visitor.visit_i8x16_shuffle(lanes) + } + + 0x0e => visitor.visit_i8x16_swizzle(), + 0x0f => visitor.visit_i8x16_splat(), + 0x10 => visitor.visit_i16x8_splat(), + 0x11 => visitor.visit_i32x4_splat(), + 0x12 => visitor.visit_i64x2_splat(), + 0x13 => visitor.visit_f32x4_splat(), + 0x14 => visitor.visit_f64x2_splat(), + + 0x15 => visitor.visit_i8x16_extract_lane_s(self.read_lane_index()?), + 0x16 => visitor.visit_i8x16_extract_lane_u(self.read_lane_index()?), + 0x17 => visitor.visit_i8x16_replace_lane(self.read_lane_index()?), + 0x18 => visitor.visit_i16x8_extract_lane_s(self.read_lane_index()?), + 0x19 => visitor.visit_i16x8_extract_lane_u(self.read_lane_index()?), + 0x1a => visitor.visit_i16x8_replace_lane(self.read_lane_index()?), + 0x1b => visitor.visit_i32x4_extract_lane(self.read_lane_index()?), + + 0x1c => visitor.visit_i32x4_replace_lane(self.read_lane_index()?), + 0x1d => visitor.visit_i64x2_extract_lane(self.read_lane_index()?), + 0x1e => visitor.visit_i64x2_replace_lane(self.read_lane_index()?), + 0x1f => visitor.visit_f32x4_extract_lane(self.read_lane_index()?), + 0x20 => visitor.visit_f32x4_replace_lane(self.read_lane_index()?), + 0x21 => visitor.visit_f64x2_extract_lane(self.read_lane_index()?), + 0x22 => visitor.visit_f64x2_replace_lane(self.read_lane_index()?), + + 0x23 => visitor.visit_i8x16_eq(), + 0x24 => visitor.visit_i8x16_ne(), + 0x25 => visitor.visit_i8x16_lt_s(), + 0x26 => visitor.visit_i8x16_lt_u(), + 0x27 => visitor.visit_i8x16_gt_s(), + 0x28 => visitor.visit_i8x16_gt_u(), + 0x29 => visitor.visit_i8x16_le_s(), + 0x2a => visitor.visit_i8x16_le_u(), + 0x2b => visitor.visit_i8x16_ge_s(), + 0x2c => visitor.visit_i8x16_ge_u(), + 0x2d => visitor.visit_i16x8_eq(), + 0x2e => visitor.visit_i16x8_ne(), + 0x2f => visitor.visit_i16x8_lt_s(), + 0x30 => visitor.visit_i16x8_lt_u(), + 0x31 => visitor.visit_i16x8_gt_s(), + 0x32 => visitor.visit_i16x8_gt_u(), + 0x33 => visitor.visit_i16x8_le_s(), + 0x34 => visitor.visit_i16x8_le_u(), + 0x35 => visitor.visit_i16x8_ge_s(), + 0x36 => visitor.visit_i16x8_ge_u(), + 0x37 => visitor.visit_i32x4_eq(), + 0x38 => visitor.visit_i32x4_ne(), + 0x39 => visitor.visit_i32x4_lt_s(), + 0x3a => visitor.visit_i32x4_lt_u(), + 0x3b => visitor.visit_i32x4_gt_s(), + 0x3c => visitor.visit_i32x4_gt_u(), + 0x3d => visitor.visit_i32x4_le_s(), + 0x3e => visitor.visit_i32x4_le_u(), + 0x3f => visitor.visit_i32x4_ge_s(), + 0x40 => visitor.visit_i32x4_ge_u(), + 0x41 => visitor.visit_f32x4_eq(), + 0x42 => visitor.visit_f32x4_ne(), + 0x43 => visitor.visit_f32x4_lt(), + 0x44 => visitor.visit_f32x4_gt(), + 0x45 => visitor.visit_f32x4_le(), + 0x46 => visitor.visit_f32x4_ge(), + 0x47 => visitor.visit_f64x2_eq(), + 0x48 => visitor.visit_f64x2_ne(), + 0x49 => visitor.visit_f64x2_lt(), + 0x4a => visitor.visit_f64x2_gt(), + 0x4b => visitor.visit_f64x2_le(), + 0x4c => visitor.visit_f64x2_ge(), + 0x4d => visitor.visit_v128_not(), + 0x4e => visitor.visit_v128_and(), + 0x4f => visitor.visit_v128_andnot(), + 0x50 => visitor.visit_v128_or(), + 0x51 => visitor.visit_v128_xor(), + 0x52 => visitor.visit_v128_bitselect(), + 0x53 => visitor.visit_v128_any_true(), + + 0x54 => { + let memarg = self.read_memarg(0)?; + let lane = self.read_lane_index()?; + visitor.visit_v128_load8_lane(memarg, lane) + } + 0x55 => { + let memarg = self.read_memarg(1)?; + let lane = self.read_lane_index()?; + visitor.visit_v128_load16_lane(memarg, lane) + } + 0x56 => { + let memarg = self.read_memarg(2)?; + let lane = self.read_lane_index()?; + visitor.visit_v128_load32_lane(memarg, lane) + } + 0x57 => { + let memarg = self.read_memarg(3)?; + let lane = self.read_lane_index()?; + visitor.visit_v128_load64_lane(memarg, lane) + } + 0x58 => { + let memarg = self.read_memarg(0)?; + let lane = self.read_lane_index()?; + visitor.visit_v128_store8_lane(memarg, lane) + } + 0x59 => { + let memarg = self.read_memarg(1)?; + let lane = self.read_lane_index()?; + visitor.visit_v128_store16_lane(memarg, lane) + } + 0x5a => { + let memarg = self.read_memarg(2)?; + let lane = self.read_lane_index()?; + visitor.visit_v128_store32_lane(memarg, lane) + } + 0x5b => { + let memarg = self.read_memarg(3)?; + let lane = self.read_lane_index()?; + visitor.visit_v128_store64_lane(memarg, lane) + } + + 0x5c => visitor.visit_v128_load32_zero(self.read_memarg(2)?), + 0x5d => visitor.visit_v128_load64_zero(self.read_memarg(3)?), + 0x5e => visitor.visit_f32x4_demote_f64x2_zero(), + 0x5f => visitor.visit_f64x2_promote_low_f32x4(), + 0x60 => visitor.visit_i8x16_abs(), + 0x61 => visitor.visit_i8x16_neg(), + 0x62 => visitor.visit_i8x16_popcnt(), + 0x63 => visitor.visit_i8x16_all_true(), + 0x64 => visitor.visit_i8x16_bitmask(), + 0x65 => visitor.visit_i8x16_narrow_i16x8_s(), + 0x66 => visitor.visit_i8x16_narrow_i16x8_u(), + 0x67 => visitor.visit_f32x4_ceil(), + 0x68 => visitor.visit_f32x4_floor(), + 0x69 => visitor.visit_f32x4_trunc(), + 0x6a => visitor.visit_f32x4_nearest(), + 0x6b => visitor.visit_i8x16_shl(), + 0x6c => visitor.visit_i8x16_shr_s(), + 0x6d => visitor.visit_i8x16_shr_u(), + 0x6e => visitor.visit_i8x16_add(), + 0x6f => visitor.visit_i8x16_add_sat_s(), + 0x70 => visitor.visit_i8x16_add_sat_u(), + 0x71 => visitor.visit_i8x16_sub(), + 0x72 => visitor.visit_i8x16_sub_sat_s(), + 0x73 => visitor.visit_i8x16_sub_sat_u(), + 0x74 => visitor.visit_f64x2_ceil(), + 0x75 => visitor.visit_f64x2_floor(), + 0x76 => visitor.visit_i8x16_min_s(), + 0x77 => visitor.visit_i8x16_min_u(), + 0x78 => visitor.visit_i8x16_max_s(), + 0x79 => visitor.visit_i8x16_max_u(), + 0x7a => visitor.visit_f64x2_trunc(), + 0x7b => visitor.visit_i8x16_avgr_u(), + 0x7c => visitor.visit_i16x8_extadd_pairwise_i8x16_s(), + 0x7d => visitor.visit_i16x8_extadd_pairwise_i8x16_u(), + 0x7e => visitor.visit_i32x4_extadd_pairwise_i16x8_s(), + 0x7f => visitor.visit_i32x4_extadd_pairwise_i16x8_u(), + 0x80 => visitor.visit_i16x8_abs(), + 0x81 => visitor.visit_i16x8_neg(), + 0x82 => visitor.visit_i16x8_q15mulr_sat_s(), + 0x83 => visitor.visit_i16x8_all_true(), + 0x84 => visitor.visit_i16x8_bitmask(), + 0x85 => visitor.visit_i16x8_narrow_i32x4_s(), + 0x86 => visitor.visit_i16x8_narrow_i32x4_u(), + 0x87 => visitor.visit_i16x8_extend_low_i8x16_s(), + 0x88 => visitor.visit_i16x8_extend_high_i8x16_s(), + 0x89 => visitor.visit_i16x8_extend_low_i8x16_u(), + 0x8a => visitor.visit_i16x8_extend_high_i8x16_u(), + 0x8b => visitor.visit_i16x8_shl(), + 0x8c => visitor.visit_i16x8_shr_s(), + 0x8d => visitor.visit_i16x8_shr_u(), + 0x8e => visitor.visit_i16x8_add(), + 0x8f => visitor.visit_i16x8_add_sat_s(), + 0x90 => visitor.visit_i16x8_add_sat_u(), + 0x91 => visitor.visit_i16x8_sub(), + 0x92 => visitor.visit_i16x8_sub_sat_s(), + 0x93 => visitor.visit_i16x8_sub_sat_u(), + 0x94 => visitor.visit_f64x2_nearest(), + 0x95 => visitor.visit_i16x8_mul(), + 0x96 => visitor.visit_i16x8_min_s(), + 0x97 => visitor.visit_i16x8_min_u(), + 0x98 => visitor.visit_i16x8_max_s(), + 0x99 => visitor.visit_i16x8_max_u(), + 0x9b => visitor.visit_i16x8_avgr_u(), + 0x9c => visitor.visit_i16x8_extmul_low_i8x16_s(), + 0x9d => visitor.visit_i16x8_extmul_high_i8x16_s(), + 0x9e => visitor.visit_i16x8_extmul_low_i8x16_u(), + 0x9f => visitor.visit_i16x8_extmul_high_i8x16_u(), + 0xa0 => visitor.visit_i32x4_abs(), + 0xa1 => visitor.visit_i32x4_neg(), + 0xa3 => visitor.visit_i32x4_all_true(), + 0xa4 => visitor.visit_i32x4_bitmask(), + 0xa7 => visitor.visit_i32x4_extend_low_i16x8_s(), + 0xa8 => visitor.visit_i32x4_extend_high_i16x8_s(), + 0xa9 => visitor.visit_i32x4_extend_low_i16x8_u(), + 0xaa => visitor.visit_i32x4_extend_high_i16x8_u(), + 0xab => visitor.visit_i32x4_shl(), + 0xac => visitor.visit_i32x4_shr_s(), + 0xad => visitor.visit_i32x4_shr_u(), + 0xae => visitor.visit_i32x4_add(), + 0xb1 => visitor.visit_i32x4_sub(), + 0xb5 => visitor.visit_i32x4_mul(), + 0xb6 => visitor.visit_i32x4_min_s(), + 0xb7 => visitor.visit_i32x4_min_u(), + 0xb8 => visitor.visit_i32x4_max_s(), + 0xb9 => visitor.visit_i32x4_max_u(), + 0xba => visitor.visit_i32x4_dot_i16x8_s(), + 0xbc => visitor.visit_i32x4_extmul_low_i16x8_s(), + 0xbd => visitor.visit_i32x4_extmul_high_i16x8_s(), + 0xbe => visitor.visit_i32x4_extmul_low_i16x8_u(), + 0xbf => visitor.visit_i32x4_extmul_high_i16x8_u(), + 0xc0 => visitor.visit_i64x2_abs(), + 0xc1 => visitor.visit_i64x2_neg(), + 0xc3 => visitor.visit_i64x2_all_true(), + 0xc4 => visitor.visit_i64x2_bitmask(), + 0xc7 => visitor.visit_i64x2_extend_low_i32x4_s(), + 0xc8 => visitor.visit_i64x2_extend_high_i32x4_s(), + 0xc9 => visitor.visit_i64x2_extend_low_i32x4_u(), + 0xca => visitor.visit_i64x2_extend_high_i32x4_u(), + 0xcb => visitor.visit_i64x2_shl(), + 0xcc => visitor.visit_i64x2_shr_s(), + 0xcd => visitor.visit_i64x2_shr_u(), + 0xce => visitor.visit_i64x2_add(), + 0xd1 => visitor.visit_i64x2_sub(), + 0xd5 => visitor.visit_i64x2_mul(), + 0xd6 => visitor.visit_i64x2_eq(), + 0xd7 => visitor.visit_i64x2_ne(), + 0xd8 => visitor.visit_i64x2_lt_s(), + 0xd9 => visitor.visit_i64x2_gt_s(), + 0xda => visitor.visit_i64x2_le_s(), + 0xdb => visitor.visit_i64x2_ge_s(), + 0xdc => visitor.visit_i64x2_extmul_low_i32x4_s(), + 0xdd => visitor.visit_i64x2_extmul_high_i32x4_s(), + 0xde => visitor.visit_i64x2_extmul_low_i32x4_u(), + 0xdf => visitor.visit_i64x2_extmul_high_i32x4_u(), + 0xe0 => visitor.visit_f32x4_abs(), + 0xe1 => visitor.visit_f32x4_neg(), + 0xe3 => visitor.visit_f32x4_sqrt(), + 0xe4 => visitor.visit_f32x4_add(), + 0xe5 => visitor.visit_f32x4_sub(), + 0xe6 => visitor.visit_f32x4_mul(), + 0xe7 => visitor.visit_f32x4_div(), + 0xe8 => visitor.visit_f32x4_min(), + 0xe9 => visitor.visit_f32x4_max(), + 0xea => visitor.visit_f32x4_pmin(), + 0xeb => visitor.visit_f32x4_pmax(), + 0xec => visitor.visit_f64x2_abs(), + 0xed => visitor.visit_f64x2_neg(), + 0xef => visitor.visit_f64x2_sqrt(), + 0xf0 => visitor.visit_f64x2_add(), + 0xf1 => visitor.visit_f64x2_sub(), + 0xf2 => visitor.visit_f64x2_mul(), + 0xf3 => visitor.visit_f64x2_div(), + 0xf4 => visitor.visit_f64x2_min(), + 0xf5 => visitor.visit_f64x2_max(), + 0xf6 => visitor.visit_f64x2_pmin(), + 0xf7 => visitor.visit_f64x2_pmax(), + 0xf8 => visitor.visit_i32x4_trunc_sat_f32x4_s(), + 0xf9 => visitor.visit_i32x4_trunc_sat_f32x4_u(), + 0xfa => visitor.visit_f32x4_convert_i32x4_s(), + 0xfb => visitor.visit_f32x4_convert_i32x4_u(), + 0xfc => visitor.visit_i32x4_trunc_sat_f64x2_s_zero(), + 0xfd => visitor.visit_i32x4_trunc_sat_f64x2_u_zero(), + 0xfe => visitor.visit_f64x2_convert_low_i32x4_s(), + 0xff => visitor.visit_f64x2_convert_low_i32x4_u(), + 0x100 => visitor.visit_i8x16_relaxed_swizzle(), + 0x101 => visitor.visit_i32x4_relaxed_trunc_f32x4_s(), + 0x102 => visitor.visit_i32x4_relaxed_trunc_f32x4_u(), + 0x103 => visitor.visit_i32x4_relaxed_trunc_f64x2_s_zero(), + 0x104 => visitor.visit_i32x4_relaxed_trunc_f64x2_u_zero(), + 0x105 => visitor.visit_f32x4_relaxed_madd(), + 0x106 => visitor.visit_f32x4_relaxed_nmadd(), + 0x107 => visitor.visit_f64x2_relaxed_madd(), + 0x108 => visitor.visit_f64x2_relaxed_nmadd(), + 0x109 => visitor.visit_i8x16_relaxed_laneselect(), + 0x10a => visitor.visit_i16x8_relaxed_laneselect(), + 0x10b => visitor.visit_i32x4_relaxed_laneselect(), + 0x10c => visitor.visit_i64x2_relaxed_laneselect(), + 0x10d => visitor.visit_f32x4_relaxed_min(), + 0x10e => visitor.visit_f32x4_relaxed_max(), + 0x10f => visitor.visit_f64x2_relaxed_min(), + 0x110 => visitor.visit_f64x2_relaxed_max(), + 0x111 => visitor.visit_i16x8_relaxed_q15mulr_s(), + 0x112 => visitor.visit_i16x8_relaxed_dot_i8x16_i7x16_s(), + 0x113 => visitor.visit_i32x4_relaxed_dot_i8x16_i7x16_add_s(), + + _ => bail!(pos, "unknown 0xfd subopcode: 0x{code:x}"), + }) + } + + fn visit_0xfe_operator( + &mut self, + pos: usize, + visitor: &mut T, + ) -> Result<>::Output> + where + T: VisitOperator<'a>, + { + let code = self.read_var_u32()?; + Ok(match code { + 0x00 => visitor.visit_memory_atomic_notify(self.read_memarg(2)?), + 0x01 => visitor.visit_memory_atomic_wait32(self.read_memarg(2)?), + 0x02 => visitor.visit_memory_atomic_wait64(self.read_memarg(3)?), + 0x03 => { + if self.read_u8()? != 0 { + bail!(pos, "nonzero byte after `atomic.fence`"); + } + visitor.visit_atomic_fence() + } + 0x10 => visitor.visit_i32_atomic_load(self.read_memarg(2)?), + 0x11 => visitor.visit_i64_atomic_load(self.read_memarg(3)?), + 0x12 => visitor.visit_i32_atomic_load8_u(self.read_memarg(0)?), + 0x13 => visitor.visit_i32_atomic_load16_u(self.read_memarg(1)?), + 0x14 => visitor.visit_i64_atomic_load8_u(self.read_memarg(0)?), + 0x15 => visitor.visit_i64_atomic_load16_u(self.read_memarg(1)?), + 0x16 => visitor.visit_i64_atomic_load32_u(self.read_memarg(2)?), + 0x17 => visitor.visit_i32_atomic_store(self.read_memarg(2)?), + 0x18 => visitor.visit_i64_atomic_store(self.read_memarg(3)?), + 0x19 => visitor.visit_i32_atomic_store8(self.read_memarg(0)?), + 0x1a => visitor.visit_i32_atomic_store16(self.read_memarg(1)?), + 0x1b => visitor.visit_i64_atomic_store8(self.read_memarg(0)?), + 0x1c => visitor.visit_i64_atomic_store16(self.read_memarg(1)?), + 0x1d => visitor.visit_i64_atomic_store32(self.read_memarg(2)?), + 0x1e => visitor.visit_i32_atomic_rmw_add(self.read_memarg(2)?), + 0x1f => visitor.visit_i64_atomic_rmw_add(self.read_memarg(3)?), + 0x20 => visitor.visit_i32_atomic_rmw8_add_u(self.read_memarg(0)?), + 0x21 => visitor.visit_i32_atomic_rmw16_add_u(self.read_memarg(1)?), + 0x22 => visitor.visit_i64_atomic_rmw8_add_u(self.read_memarg(0)?), + 0x23 => visitor.visit_i64_atomic_rmw16_add_u(self.read_memarg(1)?), + 0x24 => visitor.visit_i64_atomic_rmw32_add_u(self.read_memarg(2)?), + 0x25 => visitor.visit_i32_atomic_rmw_sub(self.read_memarg(2)?), + 0x26 => visitor.visit_i64_atomic_rmw_sub(self.read_memarg(3)?), + 0x27 => visitor.visit_i32_atomic_rmw8_sub_u(self.read_memarg(0)?), + 0x28 => visitor.visit_i32_atomic_rmw16_sub_u(self.read_memarg(1)?), + 0x29 => visitor.visit_i64_atomic_rmw8_sub_u(self.read_memarg(0)?), + 0x2a => visitor.visit_i64_atomic_rmw16_sub_u(self.read_memarg(1)?), + 0x2b => visitor.visit_i64_atomic_rmw32_sub_u(self.read_memarg(2)?), + 0x2c => visitor.visit_i32_atomic_rmw_and(self.read_memarg(2)?), + 0x2d => visitor.visit_i64_atomic_rmw_and(self.read_memarg(3)?), + 0x2e => visitor.visit_i32_atomic_rmw8_and_u(self.read_memarg(0)?), + 0x2f => visitor.visit_i32_atomic_rmw16_and_u(self.read_memarg(1)?), + 0x30 => visitor.visit_i64_atomic_rmw8_and_u(self.read_memarg(0)?), + 0x31 => visitor.visit_i64_atomic_rmw16_and_u(self.read_memarg(1)?), + 0x32 => visitor.visit_i64_atomic_rmw32_and_u(self.read_memarg(2)?), + 0x33 => visitor.visit_i32_atomic_rmw_or(self.read_memarg(2)?), + 0x34 => visitor.visit_i64_atomic_rmw_or(self.read_memarg(3)?), + 0x35 => visitor.visit_i32_atomic_rmw8_or_u(self.read_memarg(0)?), + 0x36 => visitor.visit_i32_atomic_rmw16_or_u(self.read_memarg(1)?), + 0x37 => visitor.visit_i64_atomic_rmw8_or_u(self.read_memarg(0)?), + 0x38 => visitor.visit_i64_atomic_rmw16_or_u(self.read_memarg(1)?), + 0x39 => visitor.visit_i64_atomic_rmw32_or_u(self.read_memarg(2)?), + 0x3a => visitor.visit_i32_atomic_rmw_xor(self.read_memarg(2)?), + 0x3b => visitor.visit_i64_atomic_rmw_xor(self.read_memarg(3)?), + 0x3c => visitor.visit_i32_atomic_rmw8_xor_u(self.read_memarg(0)?), + 0x3d => visitor.visit_i32_atomic_rmw16_xor_u(self.read_memarg(1)?), + 0x3e => visitor.visit_i64_atomic_rmw8_xor_u(self.read_memarg(0)?), + 0x3f => visitor.visit_i64_atomic_rmw16_xor_u(self.read_memarg(1)?), + 0x40 => visitor.visit_i64_atomic_rmw32_xor_u(self.read_memarg(2)?), + 0x41 => visitor.visit_i32_atomic_rmw_xchg(self.read_memarg(2)?), + 0x42 => visitor.visit_i64_atomic_rmw_xchg(self.read_memarg(3)?), + 0x43 => visitor.visit_i32_atomic_rmw8_xchg_u(self.read_memarg(0)?), + 0x44 => visitor.visit_i32_atomic_rmw16_xchg_u(self.read_memarg(1)?), + 0x45 => visitor.visit_i64_atomic_rmw8_xchg_u(self.read_memarg(0)?), + 0x46 => visitor.visit_i64_atomic_rmw16_xchg_u(self.read_memarg(1)?), + 0x47 => visitor.visit_i64_atomic_rmw32_xchg_u(self.read_memarg(2)?), + 0x48 => visitor.visit_i32_atomic_rmw_cmpxchg(self.read_memarg(2)?), + 0x49 => visitor.visit_i64_atomic_rmw_cmpxchg(self.read_memarg(3)?), + 0x4a => visitor.visit_i32_atomic_rmw8_cmpxchg_u(self.read_memarg(0)?), + 0x4b => visitor.visit_i32_atomic_rmw16_cmpxchg_u(self.read_memarg(1)?), + 0x4c => visitor.visit_i64_atomic_rmw8_cmpxchg_u(self.read_memarg(0)?), + 0x4d => visitor.visit_i64_atomic_rmw16_cmpxchg_u(self.read_memarg(1)?), + 0x4e => visitor.visit_i64_atomic_rmw32_cmpxchg_u(self.read_memarg(2)?), + + // Decode shared-everything-threads proposal. + 0x4f => visitor.visit_global_atomic_get(self.read_ordering()?, self.read_var_u32()?), + 0x50 => visitor.visit_global_atomic_set(self.read_ordering()?, self.read_var_u32()?), + 0x51 => { + visitor.visit_global_atomic_rmw_add(self.read_ordering()?, self.read_var_u32()?) + } + 0x52 => { + visitor.visit_global_atomic_rmw_sub(self.read_ordering()?, self.read_var_u32()?) + } + 0x53 => { + visitor.visit_global_atomic_rmw_and(self.read_ordering()?, self.read_var_u32()?) + } + 0x54 => visitor.visit_global_atomic_rmw_or(self.read_ordering()?, self.read_var_u32()?), + 0x55 => { + visitor.visit_global_atomic_rmw_xor(self.read_ordering()?, self.read_var_u32()?) + } + 0x56 => { + visitor.visit_global_atomic_rmw_xchg(self.read_ordering()?, self.read_var_u32()?) + } + 0x57 => { + visitor.visit_global_atomic_rmw_cmpxchg(self.read_ordering()?, self.read_var_u32()?) + } + 0x58 => visitor.visit_table_atomic_get(self.read_ordering()?, self.read_var_u32()?), + 0x59 => visitor.visit_table_atomic_set(self.read_ordering()?, self.read_var_u32()?), + 0x5a => { + visitor.visit_table_atomic_rmw_xchg(self.read_ordering()?, self.read_var_u32()?) + } + 0x5b => { + visitor.visit_table_atomic_rmw_cmpxchg(self.read_ordering()?, self.read_var_u32()?) + } + 0x5c => visitor.visit_struct_atomic_get( + self.read_ordering()?, + self.read_var_u32()?, + self.read_var_u32()?, + ), + 0x5d => visitor.visit_struct_atomic_get_s( + self.read_ordering()?, + self.read_var_u32()?, + self.read_var_u32()?, + ), + 0x5e => visitor.visit_struct_atomic_get_u( + self.read_ordering()?, + self.read_var_u32()?, + self.read_var_u32()?, + ), + 0x5f => visitor.visit_struct_atomic_set( + self.read_ordering()?, + self.read_var_u32()?, + self.read_var_u32()?, + ), + 0x60 => visitor.visit_struct_atomic_rmw_add( + self.read_ordering()?, + self.read_var_u32()?, + self.read_var_u32()?, + ), + 0x61 => visitor.visit_struct_atomic_rmw_sub( + self.read_ordering()?, + self.read_var_u32()?, + self.read_var_u32()?, + ), + 0x62 => visitor.visit_struct_atomic_rmw_and( + self.read_ordering()?, + self.read_var_u32()?, + self.read_var_u32()?, + ), + 0x63 => visitor.visit_struct_atomic_rmw_or( + self.read_ordering()?, + self.read_var_u32()?, + self.read_var_u32()?, + ), + 0x64 => visitor.visit_struct_atomic_rmw_xor( + self.read_ordering()?, + self.read_var_u32()?, + self.read_var_u32()?, + ), + 0x65 => visitor.visit_struct_atomic_rmw_xchg( + self.read_ordering()?, + self.read_var_u32()?, + self.read_var_u32()?, + ), + 0x66 => visitor.visit_struct_atomic_rmw_cmpxchg( + self.read_ordering()?, + self.read_var_u32()?, + self.read_var_u32()?, + ), + 0x67 => visitor.visit_array_atomic_get(self.read_ordering()?, self.read_var_u32()?), + 0x68 => visitor.visit_array_atomic_get_s(self.read_ordering()?, self.read_var_u32()?), + 0x69 => visitor.visit_array_atomic_get_u(self.read_ordering()?, self.read_var_u32()?), + 0x6a => visitor.visit_array_atomic_set(self.read_ordering()?, self.read_var_u32()?), + 0x6b => visitor.visit_array_atomic_rmw_add(self.read_ordering()?, self.read_var_u32()?), + 0x6c => visitor.visit_array_atomic_rmw_sub(self.read_ordering()?, self.read_var_u32()?), + 0x6d => visitor.visit_array_atomic_rmw_and(self.read_ordering()?, self.read_var_u32()?), + 0x6e => visitor.visit_array_atomic_rmw_or(self.read_ordering()?, self.read_var_u32()?), + 0x6f => visitor.visit_array_atomic_rmw_xor(self.read_ordering()?, self.read_var_u32()?), + 0x70 => { + visitor.visit_array_atomic_rmw_xchg(self.read_ordering()?, self.read_var_u32()?) + } + 0x71 => { + visitor.visit_array_atomic_rmw_cmpxchg(self.read_ordering()?, self.read_var_u32()?) + } + 0x72 => visitor.visit_ref_i31_shared(), + + _ => bail!(pos, "unknown 0xfe subopcode: 0x{code:x}"), + }) + } + + fn read_memarg(&mut self, max_align: u8) -> Result { + let flags_pos = self.original_position(); + let mut flags = self.read_var_u32()?; + + let memory = if self.multi_memory() && flags & (1 << 6) != 0 { + flags ^= 1 << 6; + self.read_var_u32()? + } else { + 0 + }; + let max_flag_bits = if self.multi_memory() { 6 } else { 5 }; + if flags >= (1 << max_flag_bits) { + return Err(BinaryReaderError::new( + "malformed memop alignment: alignment too large", + flags_pos, + )); + } + let align = flags as u8; + let offset = if self.memory64() { + self.read_var_u64()? + } else { + u64::from(self.read_var_u32()?) + }; + Ok(MemArg { + align, + max_align, + offset, + memory, + }) + } + + fn read_ordering(&mut self) -> Result { + let byte = self.read_var_u32()?; + match byte { + 0 => Ok(Ordering::SeqCst), + 1 => Ok(Ordering::AcqRel), + x => Err(BinaryReaderError::new( + &format!("invalid atomic consistency ordering {x}"), + self.original_position() - 1, + )), + } + } + + fn read_br_table(&mut self) -> Result> { + let cnt = self.read_size(MAX_WASM_BR_TABLE_SIZE, "br_table")?; + let reader = self.skip(|reader| { + for _ in 0..cnt { + reader.read_var_u32()?; + } + Ok(()) + })?; + let default = self.read_var_u32()?; + Ok(BrTable { + reader, + cnt: cnt as u32, + default, + }) + } + + #[cfg(feature = "simd")] + fn read_lane_index(&mut self) -> Result { + self.read_u8() + } + + #[cfg(feature = "simd")] + fn read_v128(&mut self) -> Result { + let mut bytes = [0; 16]; + bytes.clone_from_slice(self.read_bytes(16)?); + Ok(V128(bytes)) + } + + fn read_memory_index_or_zero_if_not_multi_memory(&mut self) -> Result { + if self.multi_memory() { + self.read_var_u32() + } else { + // Before bulk memory this byte was required to be a single zero + // byte, not a LEB-encoded zero, so require a precise zero byte. + match self.read_u8()? { + 0 => Ok(0), + _ => bail!(self.original_position() - 1, "zero byte expected"), + } + } + } + + fn read_call_indirect_table_immediate(&mut self) -> Result { + // If the `call_indirect_overlong` feature is enabled, then read this + // immediate as a LEB. This feature is enabled as part of the + // `reference_types` feature or the `lime1` feature. + if self.call_indirect_overlong() { + return self.read_var_u32(); + } + + // Before reference types this byte was required to be a single zero + // byte, not a LEB-encoded zero, so require a precise zero byte. + match self.read_u8()? { + 0 => Ok(0), + _ => bail!(self.original_position() - 1, "zero byte expected"), + } + } +} diff --git a/crates/wasmparser/src/readers/core/code.rs b/crates/wasmparser/src/readers/core/code.rs index 08bc68fbc9..6b9e206839 100644 --- a/crates/wasmparser/src/readers/core/code.rs +++ b/crates/wasmparser/src/readers/core/code.rs @@ -13,7 +13,10 @@ * limitations under the License. */ -use crate::{BinaryReader, FromReader, OperatorsReader, Result, SectionLimited, ValType}; +use crate::{ + BinaryReader, FromReader, OperatorsReader, OperatorsReaderAllocations, Result, SectionLimited, + ValType, +}; use core::ops::Range; /// A reader for the code section of a WebAssembly module. @@ -56,11 +59,23 @@ impl<'a> FunctionBody<'a> { }) } + /// Gets a binary reader for this function body, after skipping locals. + pub fn get_binary_reader_for_operators(&self) -> Result> { + let mut reader = self.reader.clone(); + Self::skip_locals(&mut reader)?; + Ok(reader) + } + /// Gets the operators reader for this function body, after skipping locals. - pub fn get_operators_reader(&self) -> Result> { + /// A pre-existing [`OperatorsReaderAllocations`] instance can be used to reuse allocations + /// from a previous operators reader. It is also sufficient to pass `Default::default()`. + pub fn get_operators_reader( + &self, + allocs: OperatorsReaderAllocations, + ) -> Result> { let mut reader = self.reader.clone(); Self::skip_locals(&mut reader)?; - Ok(OperatorsReader::new(reader)) + Ok(OperatorsReader::new(reader, allocs)) } /// Gets the range of the function body. @@ -157,8 +172,14 @@ impl<'a> Iterator for LocalsIterator<'a> { impl<'a> LocalsIterator<'a> { /// After reading the locals, the BinaryReader is ready to read the operators. - pub fn into_operators_reader(self) -> OperatorsReader<'a> { + pub fn into_binary_reader_for_operators(self) -> BinaryReader<'a> { + debug_assert!(self.err || self.left == 0); + self.reader.get_binary_reader() + } + + /// After reading the locals, the BinaryReader is ready to read the operators. + pub fn into_operators_reader(self, allocs: OperatorsReaderAllocations) -> OperatorsReader<'a> { debug_assert!(self.err || self.left == 0); - OperatorsReader::new(self.reader.get_binary_reader()) + OperatorsReader::new(self.reader.get_binary_reader(), allocs) } } diff --git a/crates/wasmparser/src/readers/core/elements.rs b/crates/wasmparser/src/readers/core/elements.rs index aeb8637327..32b3463d91 100644 --- a/crates/wasmparser/src/readers/core/elements.rs +++ b/crates/wasmparser/src/readers/core/elements.rs @@ -14,8 +14,8 @@ */ use crate::{ - BinaryReader, BinaryReaderError, ConstExpr, ExternalKind, FromReader, OperatorsReader, RefType, - Result, SectionLimited, + BinaryReader, BinaryReaderError, ConstExpr, ExternalKind, FromReader, OperatorsReader, + OperatorsReaderAllocations, RefType, Result, SectionLimited, }; use core::ops::Range; @@ -120,11 +120,14 @@ impl<'a> FromReader<'a> for Element<'a> { // FIXME(#188) ideally wouldn't have to do skips here let data = reader.skip(|reader| { let items_count = reader.read_var_u32()?; + let mut allocs = OperatorsReaderAllocations::default(); if exprs { for _ in 0..items_count { - let mut ops = OperatorsReader::new(reader.clone()); + let mut ops = + OperatorsReader::new(reader.clone(), core::mem::take(&mut allocs)); ops.skip_const_expr()?; *reader = ops.get_binary_reader(); + allocs = ops.into_allocations(); } } else { for _ in 0..items_count { diff --git a/crates/wasmparser/src/readers/core/init.rs b/crates/wasmparser/src/readers/core/init.rs index d932fa7082..ddfccb23a7 100644 --- a/crates/wasmparser/src/readers/core/init.rs +++ b/crates/wasmparser/src/readers/core/init.rs @@ -13,7 +13,7 @@ * limitations under the License. */ -use crate::{BinaryReader, FromReader, OperatorsReader, Result}; +use crate::{BinaryReader, FromReader, OperatorsReader, OperatorsReaderAllocations, Result}; use core::fmt; /// Represents an initialization expression. @@ -41,9 +41,9 @@ impl<'a> ConstExpr<'a> { self.reader.clone() } - /// Gets an operators reader for the initialization expression. - pub fn get_operators_reader(&self) -> OperatorsReader<'a> { - OperatorsReader::new(self.get_binary_reader()) + /// Gets an operators parser for the initialization expression. + pub fn get_operators_reader(&self, allocs: OperatorsReaderAllocations) -> OperatorsReader<'a> { + OperatorsReader::new(self.get_binary_reader(), allocs) } } @@ -51,7 +51,7 @@ impl<'a> FromReader<'a> for ConstExpr<'a> { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { // FIXME(#188) ideally shouldn't need to skip here let reader = reader.skip(|reader| { - let mut ops = OperatorsReader::new(reader.clone()); + let mut ops = OperatorsReader::new(reader.clone(), Default::default()); ops.skip_const_expr()?; *reader = ops.get_binary_reader(); Ok(()) diff --git a/crates/wasmparser/src/readers/core/operators.rs b/crates/wasmparser/src/readers/core/operators.rs index c964c1ba6a..e2e0e313fc 100644 --- a/crates/wasmparser/src/readers/core/operators.rs +++ b/crates/wasmparser/src/readers/core/operators.rs @@ -13,11 +13,9 @@ * limitations under the License. */ -use crate::limits::{ - MAX_WASM_BR_TABLE_SIZE, MAX_WASM_CATCHES, MAX_WASM_HANDLERS, MAX_WASM_SELECT_RESULT_SIZE, -}; +use crate::limits::{MAX_WASM_CATCHES, MAX_WASM_HANDLERS}; use crate::prelude::*; -use crate::{BinaryReader, BinaryReaderError, FromReader, RefType, Result, ValType}; +use crate::{BinaryReader, BinaryReaderError, FromReader, Result, ValType}; use core::fmt; /// Represents a block type. @@ -144,7 +142,7 @@ impl<'a> BrTable<'a> { /// use wasmparser::{BinaryReader, OperatorsReader, Operator}; /// /// let buf = [0x0e, 0x02, 0x01, 0x02, 0x00]; - /// let mut reader = OperatorsReader::new(BinaryReader::new(&buf, 0)); + /// let mut reader = OperatorsReader::new(BinaryReader::new(&buf, 0), Default::default()); /// let op = reader.read().unwrap(); /// if let Operator::BrTable { targets } = op { /// let targets = targets.targets().collect::, _>>().unwrap(); @@ -332,22 +330,80 @@ macro_rules! define_operator { } crate::for_each_operator!(define_operator); -/// A reader for a core WebAssembly function's operators. +/// A trait representing the stack of frames within a function. +/// The BinaryReader and OperatorsReaders use information about the current frame kind +/// to enforce the syntactic requirements of the binary format. +pub trait FrameStack { + /// The current frame kind. + fn current_frame(&self) -> Option<&FrameKind>; +} + +/// Adapters from VisitOperators to FrameStacks +struct FrameStackAdapter<'x, 'a, T: VisitOperator<'a>> { + stack: &'x mut Vec, + visitor: &'x mut T, + marker: core::marker::PhantomData &'a ()>, +} + +impl<'x, 'a, T: VisitOperator<'a>> FrameStack for FrameStackAdapter<'x, 'a, T> { + fn current_frame(&self) -> Option<&FrameKind> { + self.stack.last() + } +} + +struct SingleFrameAdapter<'x, 'a, T: VisitOperator<'a>> { + current_frame: FrameKind, + visitor: &'x mut T, + marker: core::marker::PhantomData &'a ()>, +} + +impl<'x, 'a, T: VisitOperator<'a>> FrameStack for SingleFrameAdapter<'x, 'a, T> { + fn current_frame(&self) -> Option<&FrameKind> { + Some(&self.current_frame) + } +} + +/// A reader for a core WebAssembly function's operators. The [`OperatorsReader`] internally +/// maintains a stack of the kinds of frames within an expression or function body. +/// This is necessary to enforce the syntactic requirements of the binary format. +/// The BinaryReader can also be used to read the operators by providing an external [`FrameStack`] instance. #[derive(Clone)] pub struct OperatorsReader<'a> { reader: BinaryReader<'a>, - blocks: Vec, + stack: Vec, } +/// External handle to the internal allocations used by the OperatorsReader +/// +/// This is created with either the `Default` implementation or with +/// [`OperatorsReader::into_allocations`]. It is then passed as an argument to +/// [`OperatorsReader::new`] to provide a means of reusing allocations +/// between each expression or function body. +#[derive(Default)] +pub struct OperatorsReaderAllocations(Vec); + impl<'a> OperatorsReader<'a> { - /// Creates a new reader for an expression (instruction sequence) - pub fn new(reader: BinaryReader<'a>) -> OperatorsReader<'a> { - OperatorsReader { + /// Creates a new reader for an expression (instruction sequence). + /// + /// This method, in conjunction with [`OperatorsReader::into_allocations`], + /// provides a means to reuse allocations across reading each + /// individual expression or function body. Note that it is also sufficient + /// to call this method with `Default::default()` if no prior allocations are + /// available. + pub fn new(reader: BinaryReader<'a>, mut allocs: OperatorsReaderAllocations) -> Self { + allocs.0.clear(); + allocs.0.push(FrameKind::Block); + Self { reader, - blocks: vec![FrameKind::Block], + stack: allocs.0, } } + /// Get binary reader + pub fn get_binary_reader(&self) -> BinaryReader<'a> { + self.reader.clone() + } + /// Determines if the reader is at the end of the operators. pub fn eof(&self) -> bool { self.reader.eof() @@ -358,30 +414,20 @@ impl<'a> OperatorsReader<'a> { self.reader.original_position() } - /// Function that must be called after the last opcode has been processed. - /// - /// This function returns an error if there is extra data after the operators. - /// It does *not* check the binary format requirement that if the data count - /// section is absent, a data index may not occur in the code section. - pub fn finish(&self) -> Result<()> { - self.ensure_stack_empty()?; - if !self.eof() { - bail!( - self.original_position(), - "unexpected data at the end of operators" - ); - } - Ok(()) + /// Returns whether there is an `end` opcode followed by eof remaining in + /// this reader. + pub fn is_end_then_eof(&self) -> bool { + self.reader.is_end_then_eof() } - fn ensure_stack_empty(&self) -> Result<()> { - if !self.blocks.is_empty() { - bail!( - self.original_position(), - "control frames remain at end of function body or expression" - ); - } - Ok(()) + /// Consumes this reader and returns the underlying allocations that + /// were used to store the frame stack. + /// + /// The returned value here can be paired with + /// [`OperatorsReader::new`] to reuse the allocations already + /// created by this reader. + pub fn into_allocations(self) -> OperatorsReaderAllocations { + OperatorsReaderAllocations(self.stack) } /// Reads the next available `Operator`. @@ -389,52 +435,11 @@ impl<'a> OperatorsReader<'a> { /// # Errors /// /// If `OperatorsReader` has less bytes remaining than required to parse - /// the `Operator`. + /// the `Operator`, or if the input is malformed. pub fn read(&mut self) -> Result> { self.visit_operator(&mut OperatorFactory::new()) } - /// Converts to an iterator of operators paired with offsets. - pub fn into_iter_with_offsets(self) -> OperatorsIteratorWithOffsets<'a> { - OperatorsIteratorWithOffsets { - reader: self, - err: false, - } - } - - /// Reads an operator with its offset. - pub fn read_with_offset(&mut self) -> Result<(Operator<'a>, usize)> { - let pos = self.reader.original_position(); - Ok((self.read()?, pos)) - } - - fn enter(&mut self, k: FrameKind) { - self.blocks.push(k) - } - - fn expect_block(&mut self, k: FrameKind, found: &str) -> Result<()> { - match self.blocks.last() { - None => bail!( - self.original_position(), - "empty stack found where {:?} expected", - k - ), - Some(x) if *x == k => Ok(()), - Some(_) => bail!( - self.original_position(), - "`{}` found outside `{:?}` block", - found, - k - ), - } - } - - fn end(&mut self) -> Result<()> { - assert!(!self.blocks.is_empty()); - self.blocks.pop(); - Ok(()) - } - /// Visit the next available operator with the specified [`VisitOperator`] instance. /// /// Note that this does not implicitly propagate any additional information such as instruction @@ -442,7 +447,8 @@ impl<'a> OperatorsReader<'a> { /// /// # Errors /// - /// If `OperatorsReader` has less bytes remaining than required to parse the `Operator`. + /// If `OperatorsReader` has less bytes remaining than required to parse the `Operator`, + /// or if the input is malformed. /// /// # Examples /// @@ -484,1203 +490,55 @@ impl<'a> OperatorsReader<'a> { where T: VisitOperator<'a>, { - if self.blocks.is_empty() { - bail!( - self.original_position(), - "operators remaining after end of function body or expression" - ); - } - let pos = self.reader.original_position(); - let code = self.reader.read_u8()?; - Ok(match code { - 0x00 => visitor.visit_unreachable(), - 0x01 => visitor.visit_nop(), - 0x02 => { - self.enter(FrameKind::Block); - visitor.visit_block(self.reader.read_block_type()?) - } - 0x03 => { - self.enter(FrameKind::Loop); - visitor.visit_loop(self.reader.read_block_type()?) - } - 0x04 => { - self.enter(FrameKind::If); - visitor.visit_if(self.reader.read_block_type()?) - } - 0x05 => { - self.expect_block(FrameKind::If, "else")?; - visitor.visit_else() - } - 0x06 => { - if !self.reader.legacy_exceptions() { - bail!( - pos, - "legacy_exceptions feature required for try instruction" - ); - } - self.enter(FrameKind::LegacyTry); - visitor.visit_try(self.reader.read_block_type()?) - } - 0x07 => { - if !self.reader.legacy_exceptions() { - bail!( - pos, - "legacy_exceptions feature required for catch instruction" - ); - } - self.expect_block(FrameKind::LegacyTry, "catch")?; - visitor.visit_catch(self.reader.read_var_u32()?) - } - 0x08 => visitor.visit_throw(self.reader.read_var_u32()?), - 0x09 => visitor.visit_rethrow(self.reader.read_var_u32()?), - 0x0a => visitor.visit_throw_ref(), - 0x0b => { - self.end()?; - visitor.visit_end() - } - 0x0c => visitor.visit_br(self.reader.read_var_u32()?), - 0x0d => visitor.visit_br_if(self.reader.read_var_u32()?), - 0x0e => visitor.visit_br_table(self.read_br_table()?), - 0x0f => visitor.visit_return(), - 0x10 => visitor.visit_call(self.reader.read_var_u32()?), - 0x11 => { - let index = self.reader.read_var_u32()?; - let table = self.read_call_indirect_table_immediate()?; - visitor.visit_call_indirect(index, table) - } - 0x12 => visitor.visit_return_call(self.reader.read_var_u32()?), - 0x13 => visitor.visit_return_call_indirect( - self.reader.read_var_u32()?, - self.reader.read_var_u32()?, - ), - 0x14 => visitor.visit_call_ref(self.reader.read()?), - 0x15 => visitor.visit_return_call_ref(self.reader.read()?), - 0x18 => { - self.expect_block(FrameKind::LegacyTry, "delegate")?; - self.blocks.pop(); - visitor.visit_delegate(self.reader.read_var_u32()?) - } - 0x19 => { - if !self.reader.legacy_exceptions() { - bail!( - pos, - "legacy_exceptions feature required for catch_all instruction" - ); - } - self.expect_block(FrameKind::LegacyTry, "catch_all")?; - visitor.visit_catch_all() - } - 0x1a => visitor.visit_drop(), - 0x1b => visitor.visit_select(), - 0x1c => { - let result_count = self - .reader - .read_size(MAX_WASM_SELECT_RESULT_SIZE, "select types")?; - if result_count == 1 { - visitor.visit_typed_select(self.reader.read()?) - } else { - let mut results = Vec::new(); - results.reserve_exact(result_count); - for _ in 0..result_count { - results.push(self.reader.read()?); - } - visitor.visit_typed_select_multi(results) - } - } - 0x1f => { - self.enter(FrameKind::TryTable); - visitor.visit_try_table(self.reader.read()?) - } - - 0x20 => visitor.visit_local_get(self.reader.read_var_u32()?), - 0x21 => visitor.visit_local_set(self.reader.read_var_u32()?), - 0x22 => visitor.visit_local_tee(self.reader.read_var_u32()?), - 0x23 => visitor.visit_global_get(self.reader.read_var_u32()?), - 0x24 => visitor.visit_global_set(self.reader.read_var_u32()?), - 0x25 => visitor.visit_table_get(self.reader.read_var_u32()?), - 0x26 => visitor.visit_table_set(self.reader.read_var_u32()?), - - 0x28 => visitor.visit_i32_load(self.read_memarg(2)?), - 0x29 => visitor.visit_i64_load(self.read_memarg(3)?), - 0x2a => visitor.visit_f32_load(self.read_memarg(2)?), - 0x2b => visitor.visit_f64_load(self.read_memarg(3)?), - 0x2c => visitor.visit_i32_load8_s(self.read_memarg(0)?), - 0x2d => visitor.visit_i32_load8_u(self.read_memarg(0)?), - 0x2e => visitor.visit_i32_load16_s(self.read_memarg(1)?), - 0x2f => visitor.visit_i32_load16_u(self.read_memarg(1)?), - 0x30 => visitor.visit_i64_load8_s(self.read_memarg(0)?), - 0x31 => visitor.visit_i64_load8_u(self.read_memarg(0)?), - 0x32 => visitor.visit_i64_load16_s(self.read_memarg(1)?), - 0x33 => visitor.visit_i64_load16_u(self.read_memarg(1)?), - 0x34 => visitor.visit_i64_load32_s(self.read_memarg(2)?), - 0x35 => visitor.visit_i64_load32_u(self.read_memarg(2)?), - 0x36 => visitor.visit_i32_store(self.read_memarg(2)?), - 0x37 => visitor.visit_i64_store(self.read_memarg(3)?), - 0x38 => visitor.visit_f32_store(self.read_memarg(2)?), - 0x39 => visitor.visit_f64_store(self.read_memarg(3)?), - 0x3a => visitor.visit_i32_store8(self.read_memarg(0)?), - 0x3b => visitor.visit_i32_store16(self.read_memarg(1)?), - 0x3c => visitor.visit_i64_store8(self.read_memarg(0)?), - 0x3d => visitor.visit_i64_store16(self.read_memarg(1)?), - 0x3e => visitor.visit_i64_store32(self.read_memarg(2)?), - 0x3f => { - let mem = self.read_memory_index_or_zero_if_not_multi_memory()?; - visitor.visit_memory_size(mem) - } - 0x40 => { - let mem = self.read_memory_index_or_zero_if_not_multi_memory()?; - visitor.visit_memory_grow(mem) - } - - 0x41 => visitor.visit_i32_const(self.reader.read_var_i32()?), - 0x42 => visitor.visit_i64_const(self.reader.read_var_i64()?), - 0x43 => visitor.visit_f32_const(self.reader.read_f32()?), - 0x44 => visitor.visit_f64_const(self.reader.read_f64()?), - - 0x45 => visitor.visit_i32_eqz(), - 0x46 => visitor.visit_i32_eq(), - 0x47 => visitor.visit_i32_ne(), - 0x48 => visitor.visit_i32_lt_s(), - 0x49 => visitor.visit_i32_lt_u(), - 0x4a => visitor.visit_i32_gt_s(), - 0x4b => visitor.visit_i32_gt_u(), - 0x4c => visitor.visit_i32_le_s(), - 0x4d => visitor.visit_i32_le_u(), - 0x4e => visitor.visit_i32_ge_s(), - 0x4f => visitor.visit_i32_ge_u(), - 0x50 => visitor.visit_i64_eqz(), - 0x51 => visitor.visit_i64_eq(), - 0x52 => visitor.visit_i64_ne(), - 0x53 => visitor.visit_i64_lt_s(), - 0x54 => visitor.visit_i64_lt_u(), - 0x55 => visitor.visit_i64_gt_s(), - 0x56 => visitor.visit_i64_gt_u(), - 0x57 => visitor.visit_i64_le_s(), - 0x58 => visitor.visit_i64_le_u(), - 0x59 => visitor.visit_i64_ge_s(), - 0x5a => visitor.visit_i64_ge_u(), - 0x5b => visitor.visit_f32_eq(), - 0x5c => visitor.visit_f32_ne(), - 0x5d => visitor.visit_f32_lt(), - 0x5e => visitor.visit_f32_gt(), - 0x5f => visitor.visit_f32_le(), - 0x60 => visitor.visit_f32_ge(), - 0x61 => visitor.visit_f64_eq(), - 0x62 => visitor.visit_f64_ne(), - 0x63 => visitor.visit_f64_lt(), - 0x64 => visitor.visit_f64_gt(), - 0x65 => visitor.visit_f64_le(), - 0x66 => visitor.visit_f64_ge(), - 0x67 => visitor.visit_i32_clz(), - 0x68 => visitor.visit_i32_ctz(), - 0x69 => visitor.visit_i32_popcnt(), - 0x6a => visitor.visit_i32_add(), - 0x6b => visitor.visit_i32_sub(), - 0x6c => visitor.visit_i32_mul(), - 0x6d => visitor.visit_i32_div_s(), - 0x6e => visitor.visit_i32_div_u(), - 0x6f => visitor.visit_i32_rem_s(), - 0x70 => visitor.visit_i32_rem_u(), - 0x71 => visitor.visit_i32_and(), - 0x72 => visitor.visit_i32_or(), - 0x73 => visitor.visit_i32_xor(), - 0x74 => visitor.visit_i32_shl(), - 0x75 => visitor.visit_i32_shr_s(), - 0x76 => visitor.visit_i32_shr_u(), - 0x77 => visitor.visit_i32_rotl(), - 0x78 => visitor.visit_i32_rotr(), - 0x79 => visitor.visit_i64_clz(), - 0x7a => visitor.visit_i64_ctz(), - 0x7b => visitor.visit_i64_popcnt(), - 0x7c => visitor.visit_i64_add(), - 0x7d => visitor.visit_i64_sub(), - 0x7e => visitor.visit_i64_mul(), - 0x7f => visitor.visit_i64_div_s(), - 0x80 => visitor.visit_i64_div_u(), - 0x81 => visitor.visit_i64_rem_s(), - 0x82 => visitor.visit_i64_rem_u(), - 0x83 => visitor.visit_i64_and(), - 0x84 => visitor.visit_i64_or(), - 0x85 => visitor.visit_i64_xor(), - 0x86 => visitor.visit_i64_shl(), - 0x87 => visitor.visit_i64_shr_s(), - 0x88 => visitor.visit_i64_shr_u(), - 0x89 => visitor.visit_i64_rotl(), - 0x8a => visitor.visit_i64_rotr(), - 0x8b => visitor.visit_f32_abs(), - 0x8c => visitor.visit_f32_neg(), - 0x8d => visitor.visit_f32_ceil(), - 0x8e => visitor.visit_f32_floor(), - 0x8f => visitor.visit_f32_trunc(), - 0x90 => visitor.visit_f32_nearest(), - 0x91 => visitor.visit_f32_sqrt(), - 0x92 => visitor.visit_f32_add(), - 0x93 => visitor.visit_f32_sub(), - 0x94 => visitor.visit_f32_mul(), - 0x95 => visitor.visit_f32_div(), - 0x96 => visitor.visit_f32_min(), - 0x97 => visitor.visit_f32_max(), - 0x98 => visitor.visit_f32_copysign(), - 0x99 => visitor.visit_f64_abs(), - 0x9a => visitor.visit_f64_neg(), - 0x9b => visitor.visit_f64_ceil(), - 0x9c => visitor.visit_f64_floor(), - 0x9d => visitor.visit_f64_trunc(), - 0x9e => visitor.visit_f64_nearest(), - 0x9f => visitor.visit_f64_sqrt(), - 0xa0 => visitor.visit_f64_add(), - 0xa1 => visitor.visit_f64_sub(), - 0xa2 => visitor.visit_f64_mul(), - 0xa3 => visitor.visit_f64_div(), - 0xa4 => visitor.visit_f64_min(), - 0xa5 => visitor.visit_f64_max(), - 0xa6 => visitor.visit_f64_copysign(), - 0xa7 => visitor.visit_i32_wrap_i64(), - 0xa8 => visitor.visit_i32_trunc_f32_s(), - 0xa9 => visitor.visit_i32_trunc_f32_u(), - 0xaa => visitor.visit_i32_trunc_f64_s(), - 0xab => visitor.visit_i32_trunc_f64_u(), - 0xac => visitor.visit_i64_extend_i32_s(), - 0xad => visitor.visit_i64_extend_i32_u(), - 0xae => visitor.visit_i64_trunc_f32_s(), - 0xaf => visitor.visit_i64_trunc_f32_u(), - 0xb0 => visitor.visit_i64_trunc_f64_s(), - 0xb1 => visitor.visit_i64_trunc_f64_u(), - 0xb2 => visitor.visit_f32_convert_i32_s(), - 0xb3 => visitor.visit_f32_convert_i32_u(), - 0xb4 => visitor.visit_f32_convert_i64_s(), - 0xb5 => visitor.visit_f32_convert_i64_u(), - 0xb6 => visitor.visit_f32_demote_f64(), - 0xb7 => visitor.visit_f64_convert_i32_s(), - 0xb8 => visitor.visit_f64_convert_i32_u(), - 0xb9 => visitor.visit_f64_convert_i64_s(), - 0xba => visitor.visit_f64_convert_i64_u(), - 0xbb => visitor.visit_f64_promote_f32(), - 0xbc => visitor.visit_i32_reinterpret_f32(), - 0xbd => visitor.visit_i64_reinterpret_f64(), - 0xbe => visitor.visit_f32_reinterpret_i32(), - 0xbf => visitor.visit_f64_reinterpret_i64(), - - 0xc0 => visitor.visit_i32_extend8_s(), - 0xc1 => visitor.visit_i32_extend16_s(), - 0xc2 => visitor.visit_i64_extend8_s(), - 0xc3 => visitor.visit_i64_extend16_s(), - 0xc4 => visitor.visit_i64_extend32_s(), - - 0xd0 => visitor.visit_ref_null(self.reader.read()?), - 0xd1 => visitor.visit_ref_is_null(), - 0xd2 => visitor.visit_ref_func(self.reader.read_var_u32()?), - 0xd3 => visitor.visit_ref_eq(), - 0xd4 => visitor.visit_ref_as_non_null(), - 0xd5 => visitor.visit_br_on_null(self.reader.read_var_u32()?), - 0xd6 => visitor.visit_br_on_non_null(self.reader.read_var_u32()?), - - 0xe0 => visitor.visit_cont_new(self.reader.read_var_u32()?), - 0xe1 => { - visitor.visit_cont_bind(self.reader.read_var_u32()?, self.reader.read_var_u32()?) - } - 0xe2 => visitor.visit_suspend(self.reader.read_var_u32()?), - 0xe3 => visitor.visit_resume(self.reader.read_var_u32()?, self.reader.read()?), - 0xe4 => visitor.visit_resume_throw( - self.reader.read_var_u32()?, - self.reader.read_var_u32()?, - self.reader.read()?, - ), - 0xe5 => visitor.visit_switch(self.reader.read_var_u32()?, self.reader.read_var_u32()?), - - 0xfb => self.visit_0xfb_operator(pos, visitor)?, - 0xfc => self.visit_0xfc_operator(pos, visitor)?, - 0xfd => { - #[cfg(feature = "simd")] - if let Some(mut visitor) = visitor.simd_visitor() { - return self.visit_0xfd_operator(pos, &mut visitor); - } - bail!(pos, "unexpected SIMD opcode: 0x{code:x}") - } - 0xfe => self.visit_0xfe_operator(pos, visitor)?, - - _ => bail!(pos, "illegal opcode: 0x{code:x}"), - }) - } - - fn visit_0xfb_operator( - &mut self, - pos: usize, - visitor: &mut T, - ) -> Result<>::Output> - where - T: VisitOperator<'a>, - { - let code = self.reader.read_var_u32()?; - Ok(match code { - 0x0 => { - let type_index = self.reader.read_var_u32()?; - visitor.visit_struct_new(type_index) - } - 0x01 => { - let type_index = self.reader.read_var_u32()?; - visitor.visit_struct_new_default(type_index) - } - 0x02 => { - let type_index = self.reader.read_var_u32()?; - let field_index = self.reader.read_var_u32()?; - visitor.visit_struct_get(type_index, field_index) - } - 0x03 => { - let type_index = self.reader.read_var_u32()?; - let field_index = self.reader.read_var_u32()?; - visitor.visit_struct_get_s(type_index, field_index) - } - 0x04 => { - let type_index = self.reader.read_var_u32()?; - let field_index = self.reader.read_var_u32()?; - visitor.visit_struct_get_u(type_index, field_index) - } - 0x05 => { - let type_index = self.reader.read_var_u32()?; - let field_index = self.reader.read_var_u32()?; - visitor.visit_struct_set(type_index, field_index) - } - 0x06 => { - let type_index = self.reader.read_var_u32()?; - visitor.visit_array_new(type_index) - } - 0x07 => { - let type_index = self.reader.read_var_u32()?; - visitor.visit_array_new_default(type_index) - } - 0x08 => { - let type_index = self.reader.read_var_u32()?; - let n = self.reader.read_var_u32()?; - visitor.visit_array_new_fixed(type_index, n) - } - 0x09 => { - let type_index = self.reader.read_var_u32()?; - let data_index = self.reader.read_var_u32()?; - visitor.visit_array_new_data(type_index, data_index) - } - 0x0a => { - let type_index = self.reader.read_var_u32()?; - let elem_index = self.reader.read_var_u32()?; - visitor.visit_array_new_elem(type_index, elem_index) - } - 0x0b => { - let type_index = self.reader.read_var_u32()?; - visitor.visit_array_get(type_index) - } - 0x0c => { - let type_index = self.reader.read_var_u32()?; - visitor.visit_array_get_s(type_index) - } - 0x0d => { - let type_index = self.reader.read_var_u32()?; - visitor.visit_array_get_u(type_index) - } - 0x0e => { - let type_index = self.reader.read_var_u32()?; - visitor.visit_array_set(type_index) - } - 0x0f => visitor.visit_array_len(), - 0x10 => { - let type_index = self.reader.read_var_u32()?; - visitor.visit_array_fill(type_index) - } - 0x11 => { - let type_index_dst = self.reader.read_var_u32()?; - let type_index_src = self.reader.read_var_u32()?; - visitor.visit_array_copy(type_index_dst, type_index_src) - } - 0x12 => { - let type_index = self.reader.read_var_u32()?; - let data_index = self.reader.read_var_u32()?; - visitor.visit_array_init_data(type_index, data_index) - } - 0x13 => { - let type_index = self.reader.read_var_u32()?; - let elem_index = self.reader.read_var_u32()?; - visitor.visit_array_init_elem(type_index, elem_index) - } - 0x14 => visitor.visit_ref_test_non_null(self.reader.read()?), - 0x15 => visitor.visit_ref_test_nullable(self.reader.read()?), - 0x16 => visitor.visit_ref_cast_non_null(self.reader.read()?), - 0x17 => visitor.visit_ref_cast_nullable(self.reader.read()?), - 0x18 => { - let pos = self.original_position(); - let cast_flags = self.reader.read_u8()?; - let relative_depth = self.reader.read_var_u32()?; - let (from_type_nullable, to_type_nullable) = match cast_flags { - 0b00 => (false, false), - 0b01 => (true, false), - 0b10 => (false, true), - 0b11 => (true, true), - _ => bail!(pos, "invalid cast flags: {cast_flags:08b}"), - }; - let from_heap_type = self.reader.read()?; - let from_ref_type = - RefType::new(from_type_nullable, from_heap_type).ok_or_else(|| { - format_err!(pos, "implementation error: type index too large") - })?; - let to_heap_type = self.reader.read()?; - let to_ref_type = - RefType::new(to_type_nullable, to_heap_type).ok_or_else(|| { - format_err!(pos, "implementation error: type index too large") - })?; - visitor.visit_br_on_cast(relative_depth, from_ref_type, to_ref_type) - } - 0x19 => { - let pos = self.original_position(); - let cast_flags = self.reader.read_u8()?; - let relative_depth = self.reader.read_var_u32()?; - let (from_type_nullable, to_type_nullable) = match cast_flags { - 0 => (false, false), - 1 => (true, false), - 2 => (false, true), - 3 => (true, true), - _ => bail!(pos, "invalid cast flags: {cast_flags:08b}"), - }; - let from_heap_type = self.reader.read()?; - let from_ref_type = - RefType::new(from_type_nullable, from_heap_type).ok_or_else(|| { - format_err!(pos, "implementation error: type index too large") - })?; - let to_heap_type = self.reader.read()?; - let to_ref_type = - RefType::new(to_type_nullable, to_heap_type).ok_or_else(|| { - format_err!(pos, "implementation error: type index too large") - })?; - visitor.visit_br_on_cast_fail(relative_depth, from_ref_type, to_ref_type) - } - - 0x1a => visitor.visit_any_convert_extern(), - 0x1b => visitor.visit_extern_convert_any(), - - 0x1c => visitor.visit_ref_i31(), - 0x1d => visitor.visit_i31_get_s(), - 0x1e => visitor.visit_i31_get_u(), - - _ => bail!(pos, "unknown 0xfb subopcode: 0x{code:x}"), - }) - } - - fn visit_0xfc_operator( - &mut self, - pos: usize, - visitor: &mut T, - ) -> Result<>::Output> - where - T: VisitOperator<'a>, - { - let code = self.reader.read_var_u32()?; - Ok(match code { - 0x00 => visitor.visit_i32_trunc_sat_f32_s(), - 0x01 => visitor.visit_i32_trunc_sat_f32_u(), - 0x02 => visitor.visit_i32_trunc_sat_f64_s(), - 0x03 => visitor.visit_i32_trunc_sat_f64_u(), - 0x04 => visitor.visit_i64_trunc_sat_f32_s(), - 0x05 => visitor.visit_i64_trunc_sat_f32_u(), - 0x06 => visitor.visit_i64_trunc_sat_f64_s(), - 0x07 => visitor.visit_i64_trunc_sat_f64_u(), - - 0x08 => { - let segment = self.reader.read_var_u32()?; - let mem = self.reader.read_var_u32()?; - visitor.visit_memory_init(segment, mem) - } - 0x09 => { - let segment = self.reader.read_var_u32()?; - visitor.visit_data_drop(segment) - } - 0x0a => { - let dst = self.reader.read_var_u32()?; - let src = self.reader.read_var_u32()?; - visitor.visit_memory_copy(dst, src) - } - 0x0b => { - let mem = self.reader.read_var_u32()?; - visitor.visit_memory_fill(mem) - } - 0x0c => { - let segment = self.reader.read_var_u32()?; - let table = self.reader.read_var_u32()?; - visitor.visit_table_init(segment, table) - } - 0x0d => { - let segment = self.reader.read_var_u32()?; - visitor.visit_elem_drop(segment) - } - 0x0e => { - let dst_table = self.reader.read_var_u32()?; - let src_table = self.reader.read_var_u32()?; - visitor.visit_table_copy(dst_table, src_table) - } - - 0x0f => { - let table = self.reader.read_var_u32()?; - visitor.visit_table_grow(table) - } - 0x10 => { - let table = self.reader.read_var_u32()?; - visitor.visit_table_size(table) - } - - 0x11 => { - let table = self.reader.read_var_u32()?; - visitor.visit_table_fill(table) - } - - 0x12 => { - let mem = self.reader.read_var_u32()?; - visitor.visit_memory_discard(mem) - } - - 0x13 => visitor.visit_i64_add128(), - 0x14 => visitor.visit_i64_sub128(), - 0x15 => visitor.visit_i64_mul_wide_s(), - 0x16 => visitor.visit_i64_mul_wide_u(), - - _ => bail!(pos, "unknown 0xfc subopcode: 0x{code:x}"), + self.reader.visit_operator(&mut FrameStackAdapter { + stack: &mut self.stack, + visitor, + marker: core::marker::PhantomData, }) } - #[cfg(feature = "simd")] - pub(super) fn visit_0xfd_operator( - &mut self, - pos: usize, - visitor: &mut T, - ) -> Result<>::Output> - where - T: VisitSimdOperator<'a>, - { - let code = self.reader.read_var_u32()?; - Ok(match code { - 0x00 => visitor.visit_v128_load(self.read_memarg(4)?), - 0x01 => visitor.visit_v128_load8x8_s(self.read_memarg(3)?), - 0x02 => visitor.visit_v128_load8x8_u(self.read_memarg(3)?), - 0x03 => visitor.visit_v128_load16x4_s(self.read_memarg(3)?), - 0x04 => visitor.visit_v128_load16x4_u(self.read_memarg(3)?), - 0x05 => visitor.visit_v128_load32x2_s(self.read_memarg(3)?), - 0x06 => visitor.visit_v128_load32x2_u(self.read_memarg(3)?), - 0x07 => visitor.visit_v128_load8_splat(self.read_memarg(0)?), - 0x08 => visitor.visit_v128_load16_splat(self.read_memarg(1)?), - 0x09 => visitor.visit_v128_load32_splat(self.read_memarg(2)?), - 0x0a => visitor.visit_v128_load64_splat(self.read_memarg(3)?), - - 0x0b => visitor.visit_v128_store(self.read_memarg(4)?), - 0x0c => visitor.visit_v128_const(self.read_v128()?), - 0x0d => { - let mut lanes: [u8; 16] = [0; 16]; - for lane in &mut lanes { - *lane = self.read_lane_index()? - } - visitor.visit_i8x16_shuffle(lanes) - } - - 0x0e => visitor.visit_i8x16_swizzle(), - 0x0f => visitor.visit_i8x16_splat(), - 0x10 => visitor.visit_i16x8_splat(), - 0x11 => visitor.visit_i32x4_splat(), - 0x12 => visitor.visit_i64x2_splat(), - 0x13 => visitor.visit_f32x4_splat(), - 0x14 => visitor.visit_f64x2_splat(), - - 0x15 => visitor.visit_i8x16_extract_lane_s(self.read_lane_index()?), - 0x16 => visitor.visit_i8x16_extract_lane_u(self.read_lane_index()?), - 0x17 => visitor.visit_i8x16_replace_lane(self.read_lane_index()?), - 0x18 => visitor.visit_i16x8_extract_lane_s(self.read_lane_index()?), - 0x19 => visitor.visit_i16x8_extract_lane_u(self.read_lane_index()?), - 0x1a => visitor.visit_i16x8_replace_lane(self.read_lane_index()?), - 0x1b => visitor.visit_i32x4_extract_lane(self.read_lane_index()?), - - 0x1c => visitor.visit_i32x4_replace_lane(self.read_lane_index()?), - 0x1d => visitor.visit_i64x2_extract_lane(self.read_lane_index()?), - 0x1e => visitor.visit_i64x2_replace_lane(self.read_lane_index()?), - 0x1f => visitor.visit_f32x4_extract_lane(self.read_lane_index()?), - 0x20 => visitor.visit_f32x4_replace_lane(self.read_lane_index()?), - 0x21 => visitor.visit_f64x2_extract_lane(self.read_lane_index()?), - 0x22 => visitor.visit_f64x2_replace_lane(self.read_lane_index()?), - - 0x23 => visitor.visit_i8x16_eq(), - 0x24 => visitor.visit_i8x16_ne(), - 0x25 => visitor.visit_i8x16_lt_s(), - 0x26 => visitor.visit_i8x16_lt_u(), - 0x27 => visitor.visit_i8x16_gt_s(), - 0x28 => visitor.visit_i8x16_gt_u(), - 0x29 => visitor.visit_i8x16_le_s(), - 0x2a => visitor.visit_i8x16_le_u(), - 0x2b => visitor.visit_i8x16_ge_s(), - 0x2c => visitor.visit_i8x16_ge_u(), - 0x2d => visitor.visit_i16x8_eq(), - 0x2e => visitor.visit_i16x8_ne(), - 0x2f => visitor.visit_i16x8_lt_s(), - 0x30 => visitor.visit_i16x8_lt_u(), - 0x31 => visitor.visit_i16x8_gt_s(), - 0x32 => visitor.visit_i16x8_gt_u(), - 0x33 => visitor.visit_i16x8_le_s(), - 0x34 => visitor.visit_i16x8_le_u(), - 0x35 => visitor.visit_i16x8_ge_s(), - 0x36 => visitor.visit_i16x8_ge_u(), - 0x37 => visitor.visit_i32x4_eq(), - 0x38 => visitor.visit_i32x4_ne(), - 0x39 => visitor.visit_i32x4_lt_s(), - 0x3a => visitor.visit_i32x4_lt_u(), - 0x3b => visitor.visit_i32x4_gt_s(), - 0x3c => visitor.visit_i32x4_gt_u(), - 0x3d => visitor.visit_i32x4_le_s(), - 0x3e => visitor.visit_i32x4_le_u(), - 0x3f => visitor.visit_i32x4_ge_s(), - 0x40 => visitor.visit_i32x4_ge_u(), - 0x41 => visitor.visit_f32x4_eq(), - 0x42 => visitor.visit_f32x4_ne(), - 0x43 => visitor.visit_f32x4_lt(), - 0x44 => visitor.visit_f32x4_gt(), - 0x45 => visitor.visit_f32x4_le(), - 0x46 => visitor.visit_f32x4_ge(), - 0x47 => visitor.visit_f64x2_eq(), - 0x48 => visitor.visit_f64x2_ne(), - 0x49 => visitor.visit_f64x2_lt(), - 0x4a => visitor.visit_f64x2_gt(), - 0x4b => visitor.visit_f64x2_le(), - 0x4c => visitor.visit_f64x2_ge(), - 0x4d => visitor.visit_v128_not(), - 0x4e => visitor.visit_v128_and(), - 0x4f => visitor.visit_v128_andnot(), - 0x50 => visitor.visit_v128_or(), - 0x51 => visitor.visit_v128_xor(), - 0x52 => visitor.visit_v128_bitselect(), - 0x53 => visitor.visit_v128_any_true(), - - 0x54 => { - let memarg = self.read_memarg(0)?; - let lane = self.read_lane_index()?; - visitor.visit_v128_load8_lane(memarg, lane) - } - 0x55 => { - let memarg = self.read_memarg(1)?; - let lane = self.read_lane_index()?; - visitor.visit_v128_load16_lane(memarg, lane) - } - 0x56 => { - let memarg = self.read_memarg(2)?; - let lane = self.read_lane_index()?; - visitor.visit_v128_load32_lane(memarg, lane) - } - 0x57 => { - let memarg = self.read_memarg(3)?; - let lane = self.read_lane_index()?; - visitor.visit_v128_load64_lane(memarg, lane) - } - 0x58 => { - let memarg = self.read_memarg(0)?; - let lane = self.read_lane_index()?; - visitor.visit_v128_store8_lane(memarg, lane) - } - 0x59 => { - let memarg = self.read_memarg(1)?; - let lane = self.read_lane_index()?; - visitor.visit_v128_store16_lane(memarg, lane) - } - 0x5a => { - let memarg = self.read_memarg(2)?; - let lane = self.read_lane_index()?; - visitor.visit_v128_store32_lane(memarg, lane) - } - 0x5b => { - let memarg = self.read_memarg(3)?; - let lane = self.read_lane_index()?; - visitor.visit_v128_store64_lane(memarg, lane) - } - - 0x5c => visitor.visit_v128_load32_zero(self.read_memarg(2)?), - 0x5d => visitor.visit_v128_load64_zero(self.read_memarg(3)?), - 0x5e => visitor.visit_f32x4_demote_f64x2_zero(), - 0x5f => visitor.visit_f64x2_promote_low_f32x4(), - 0x60 => visitor.visit_i8x16_abs(), - 0x61 => visitor.visit_i8x16_neg(), - 0x62 => visitor.visit_i8x16_popcnt(), - 0x63 => visitor.visit_i8x16_all_true(), - 0x64 => visitor.visit_i8x16_bitmask(), - 0x65 => visitor.visit_i8x16_narrow_i16x8_s(), - 0x66 => visitor.visit_i8x16_narrow_i16x8_u(), - 0x67 => visitor.visit_f32x4_ceil(), - 0x68 => visitor.visit_f32x4_floor(), - 0x69 => visitor.visit_f32x4_trunc(), - 0x6a => visitor.visit_f32x4_nearest(), - 0x6b => visitor.visit_i8x16_shl(), - 0x6c => visitor.visit_i8x16_shr_s(), - 0x6d => visitor.visit_i8x16_shr_u(), - 0x6e => visitor.visit_i8x16_add(), - 0x6f => visitor.visit_i8x16_add_sat_s(), - 0x70 => visitor.visit_i8x16_add_sat_u(), - 0x71 => visitor.visit_i8x16_sub(), - 0x72 => visitor.visit_i8x16_sub_sat_s(), - 0x73 => visitor.visit_i8x16_sub_sat_u(), - 0x74 => visitor.visit_f64x2_ceil(), - 0x75 => visitor.visit_f64x2_floor(), - 0x76 => visitor.visit_i8x16_min_s(), - 0x77 => visitor.visit_i8x16_min_u(), - 0x78 => visitor.visit_i8x16_max_s(), - 0x79 => visitor.visit_i8x16_max_u(), - 0x7a => visitor.visit_f64x2_trunc(), - 0x7b => visitor.visit_i8x16_avgr_u(), - 0x7c => visitor.visit_i16x8_extadd_pairwise_i8x16_s(), - 0x7d => visitor.visit_i16x8_extadd_pairwise_i8x16_u(), - 0x7e => visitor.visit_i32x4_extadd_pairwise_i16x8_s(), - 0x7f => visitor.visit_i32x4_extadd_pairwise_i16x8_u(), - 0x80 => visitor.visit_i16x8_abs(), - 0x81 => visitor.visit_i16x8_neg(), - 0x82 => visitor.visit_i16x8_q15mulr_sat_s(), - 0x83 => visitor.visit_i16x8_all_true(), - 0x84 => visitor.visit_i16x8_bitmask(), - 0x85 => visitor.visit_i16x8_narrow_i32x4_s(), - 0x86 => visitor.visit_i16x8_narrow_i32x4_u(), - 0x87 => visitor.visit_i16x8_extend_low_i8x16_s(), - 0x88 => visitor.visit_i16x8_extend_high_i8x16_s(), - 0x89 => visitor.visit_i16x8_extend_low_i8x16_u(), - 0x8a => visitor.visit_i16x8_extend_high_i8x16_u(), - 0x8b => visitor.visit_i16x8_shl(), - 0x8c => visitor.visit_i16x8_shr_s(), - 0x8d => visitor.visit_i16x8_shr_u(), - 0x8e => visitor.visit_i16x8_add(), - 0x8f => visitor.visit_i16x8_add_sat_s(), - 0x90 => visitor.visit_i16x8_add_sat_u(), - 0x91 => visitor.visit_i16x8_sub(), - 0x92 => visitor.visit_i16x8_sub_sat_s(), - 0x93 => visitor.visit_i16x8_sub_sat_u(), - 0x94 => visitor.visit_f64x2_nearest(), - 0x95 => visitor.visit_i16x8_mul(), - 0x96 => visitor.visit_i16x8_min_s(), - 0x97 => visitor.visit_i16x8_min_u(), - 0x98 => visitor.visit_i16x8_max_s(), - 0x99 => visitor.visit_i16x8_max_u(), - 0x9b => visitor.visit_i16x8_avgr_u(), - 0x9c => visitor.visit_i16x8_extmul_low_i8x16_s(), - 0x9d => visitor.visit_i16x8_extmul_high_i8x16_s(), - 0x9e => visitor.visit_i16x8_extmul_low_i8x16_u(), - 0x9f => visitor.visit_i16x8_extmul_high_i8x16_u(), - 0xa0 => visitor.visit_i32x4_abs(), - 0xa1 => visitor.visit_i32x4_neg(), - 0xa3 => visitor.visit_i32x4_all_true(), - 0xa4 => visitor.visit_i32x4_bitmask(), - 0xa7 => visitor.visit_i32x4_extend_low_i16x8_s(), - 0xa8 => visitor.visit_i32x4_extend_high_i16x8_s(), - 0xa9 => visitor.visit_i32x4_extend_low_i16x8_u(), - 0xaa => visitor.visit_i32x4_extend_high_i16x8_u(), - 0xab => visitor.visit_i32x4_shl(), - 0xac => visitor.visit_i32x4_shr_s(), - 0xad => visitor.visit_i32x4_shr_u(), - 0xae => visitor.visit_i32x4_add(), - 0xb1 => visitor.visit_i32x4_sub(), - 0xb5 => visitor.visit_i32x4_mul(), - 0xb6 => visitor.visit_i32x4_min_s(), - 0xb7 => visitor.visit_i32x4_min_u(), - 0xb8 => visitor.visit_i32x4_max_s(), - 0xb9 => visitor.visit_i32x4_max_u(), - 0xba => visitor.visit_i32x4_dot_i16x8_s(), - 0xbc => visitor.visit_i32x4_extmul_low_i16x8_s(), - 0xbd => visitor.visit_i32x4_extmul_high_i16x8_s(), - 0xbe => visitor.visit_i32x4_extmul_low_i16x8_u(), - 0xbf => visitor.visit_i32x4_extmul_high_i16x8_u(), - 0xc0 => visitor.visit_i64x2_abs(), - 0xc1 => visitor.visit_i64x2_neg(), - 0xc3 => visitor.visit_i64x2_all_true(), - 0xc4 => visitor.visit_i64x2_bitmask(), - 0xc7 => visitor.visit_i64x2_extend_low_i32x4_s(), - 0xc8 => visitor.visit_i64x2_extend_high_i32x4_s(), - 0xc9 => visitor.visit_i64x2_extend_low_i32x4_u(), - 0xca => visitor.visit_i64x2_extend_high_i32x4_u(), - 0xcb => visitor.visit_i64x2_shl(), - 0xcc => visitor.visit_i64x2_shr_s(), - 0xcd => visitor.visit_i64x2_shr_u(), - 0xce => visitor.visit_i64x2_add(), - 0xd1 => visitor.visit_i64x2_sub(), - 0xd5 => visitor.visit_i64x2_mul(), - 0xd6 => visitor.visit_i64x2_eq(), - 0xd7 => visitor.visit_i64x2_ne(), - 0xd8 => visitor.visit_i64x2_lt_s(), - 0xd9 => visitor.visit_i64x2_gt_s(), - 0xda => visitor.visit_i64x2_le_s(), - 0xdb => visitor.visit_i64x2_ge_s(), - 0xdc => visitor.visit_i64x2_extmul_low_i32x4_s(), - 0xdd => visitor.visit_i64x2_extmul_high_i32x4_s(), - 0xde => visitor.visit_i64x2_extmul_low_i32x4_u(), - 0xdf => visitor.visit_i64x2_extmul_high_i32x4_u(), - 0xe0 => visitor.visit_f32x4_abs(), - 0xe1 => visitor.visit_f32x4_neg(), - 0xe3 => visitor.visit_f32x4_sqrt(), - 0xe4 => visitor.visit_f32x4_add(), - 0xe5 => visitor.visit_f32x4_sub(), - 0xe6 => visitor.visit_f32x4_mul(), - 0xe7 => visitor.visit_f32x4_div(), - 0xe8 => visitor.visit_f32x4_min(), - 0xe9 => visitor.visit_f32x4_max(), - 0xea => visitor.visit_f32x4_pmin(), - 0xeb => visitor.visit_f32x4_pmax(), - 0xec => visitor.visit_f64x2_abs(), - 0xed => visitor.visit_f64x2_neg(), - 0xef => visitor.visit_f64x2_sqrt(), - 0xf0 => visitor.visit_f64x2_add(), - 0xf1 => visitor.visit_f64x2_sub(), - 0xf2 => visitor.visit_f64x2_mul(), - 0xf3 => visitor.visit_f64x2_div(), - 0xf4 => visitor.visit_f64x2_min(), - 0xf5 => visitor.visit_f64x2_max(), - 0xf6 => visitor.visit_f64x2_pmin(), - 0xf7 => visitor.visit_f64x2_pmax(), - 0xf8 => visitor.visit_i32x4_trunc_sat_f32x4_s(), - 0xf9 => visitor.visit_i32x4_trunc_sat_f32x4_u(), - 0xfa => visitor.visit_f32x4_convert_i32x4_s(), - 0xfb => visitor.visit_f32x4_convert_i32x4_u(), - 0xfc => visitor.visit_i32x4_trunc_sat_f64x2_s_zero(), - 0xfd => visitor.visit_i32x4_trunc_sat_f64x2_u_zero(), - 0xfe => visitor.visit_f64x2_convert_low_i32x4_s(), - 0xff => visitor.visit_f64x2_convert_low_i32x4_u(), - 0x100 => visitor.visit_i8x16_relaxed_swizzle(), - 0x101 => visitor.visit_i32x4_relaxed_trunc_f32x4_s(), - 0x102 => visitor.visit_i32x4_relaxed_trunc_f32x4_u(), - 0x103 => visitor.visit_i32x4_relaxed_trunc_f64x2_s_zero(), - 0x104 => visitor.visit_i32x4_relaxed_trunc_f64x2_u_zero(), - 0x105 => visitor.visit_f32x4_relaxed_madd(), - 0x106 => visitor.visit_f32x4_relaxed_nmadd(), - 0x107 => visitor.visit_f64x2_relaxed_madd(), - 0x108 => visitor.visit_f64x2_relaxed_nmadd(), - 0x109 => visitor.visit_i8x16_relaxed_laneselect(), - 0x10a => visitor.visit_i16x8_relaxed_laneselect(), - 0x10b => visitor.visit_i32x4_relaxed_laneselect(), - 0x10c => visitor.visit_i64x2_relaxed_laneselect(), - 0x10d => visitor.visit_f32x4_relaxed_min(), - 0x10e => visitor.visit_f32x4_relaxed_max(), - 0x10f => visitor.visit_f64x2_relaxed_min(), - 0x110 => visitor.visit_f64x2_relaxed_max(), - 0x111 => visitor.visit_i16x8_relaxed_q15mulr_s(), - 0x112 => visitor.visit_i16x8_relaxed_dot_i8x16_i7x16_s(), - 0x113 => visitor.visit_i32x4_relaxed_dot_i8x16_i7x16_add_s(), - - _ => bail!(pos, "unknown 0xfd subopcode: 0x{code:x}"), - }) + /// Reads an operator with its offset. + pub fn read_with_offset(&mut self) -> Result<(Operator<'a>, usize)> { + let pos = self.reader.original_position(); + Ok((self.read()?, pos)) } - fn visit_0xfe_operator( - &mut self, - pos: usize, - visitor: &mut T, - ) -> Result<>::Output> - where - T: VisitOperator<'a>, - { - let code = self.reader.read_var_u32()?; - Ok(match code { - 0x00 => visitor.visit_memory_atomic_notify(self.read_memarg(2)?), - 0x01 => visitor.visit_memory_atomic_wait32(self.read_memarg(2)?), - 0x02 => visitor.visit_memory_atomic_wait64(self.read_memarg(3)?), - 0x03 => { - if self.reader.read_u8()? != 0 { - bail!(pos, "nonzero byte after `atomic.fence`"); - } - visitor.visit_atomic_fence() - } - 0x10 => visitor.visit_i32_atomic_load(self.read_memarg(2)?), - 0x11 => visitor.visit_i64_atomic_load(self.read_memarg(3)?), - 0x12 => visitor.visit_i32_atomic_load8_u(self.read_memarg(0)?), - 0x13 => visitor.visit_i32_atomic_load16_u(self.read_memarg(1)?), - 0x14 => visitor.visit_i64_atomic_load8_u(self.read_memarg(0)?), - 0x15 => visitor.visit_i64_atomic_load16_u(self.read_memarg(1)?), - 0x16 => visitor.visit_i64_atomic_load32_u(self.read_memarg(2)?), - 0x17 => visitor.visit_i32_atomic_store(self.read_memarg(2)?), - 0x18 => visitor.visit_i64_atomic_store(self.read_memarg(3)?), - 0x19 => visitor.visit_i32_atomic_store8(self.read_memarg(0)?), - 0x1a => visitor.visit_i32_atomic_store16(self.read_memarg(1)?), - 0x1b => visitor.visit_i64_atomic_store8(self.read_memarg(0)?), - 0x1c => visitor.visit_i64_atomic_store16(self.read_memarg(1)?), - 0x1d => visitor.visit_i64_atomic_store32(self.read_memarg(2)?), - 0x1e => visitor.visit_i32_atomic_rmw_add(self.read_memarg(2)?), - 0x1f => visitor.visit_i64_atomic_rmw_add(self.read_memarg(3)?), - 0x20 => visitor.visit_i32_atomic_rmw8_add_u(self.read_memarg(0)?), - 0x21 => visitor.visit_i32_atomic_rmw16_add_u(self.read_memarg(1)?), - 0x22 => visitor.visit_i64_atomic_rmw8_add_u(self.read_memarg(0)?), - 0x23 => visitor.visit_i64_atomic_rmw16_add_u(self.read_memarg(1)?), - 0x24 => visitor.visit_i64_atomic_rmw32_add_u(self.read_memarg(2)?), - 0x25 => visitor.visit_i32_atomic_rmw_sub(self.read_memarg(2)?), - 0x26 => visitor.visit_i64_atomic_rmw_sub(self.read_memarg(3)?), - 0x27 => visitor.visit_i32_atomic_rmw8_sub_u(self.read_memarg(0)?), - 0x28 => visitor.visit_i32_atomic_rmw16_sub_u(self.read_memarg(1)?), - 0x29 => visitor.visit_i64_atomic_rmw8_sub_u(self.read_memarg(0)?), - 0x2a => visitor.visit_i64_atomic_rmw16_sub_u(self.read_memarg(1)?), - 0x2b => visitor.visit_i64_atomic_rmw32_sub_u(self.read_memarg(2)?), - 0x2c => visitor.visit_i32_atomic_rmw_and(self.read_memarg(2)?), - 0x2d => visitor.visit_i64_atomic_rmw_and(self.read_memarg(3)?), - 0x2e => visitor.visit_i32_atomic_rmw8_and_u(self.read_memarg(0)?), - 0x2f => visitor.visit_i32_atomic_rmw16_and_u(self.read_memarg(1)?), - 0x30 => visitor.visit_i64_atomic_rmw8_and_u(self.read_memarg(0)?), - 0x31 => visitor.visit_i64_atomic_rmw16_and_u(self.read_memarg(1)?), - 0x32 => visitor.visit_i64_atomic_rmw32_and_u(self.read_memarg(2)?), - 0x33 => visitor.visit_i32_atomic_rmw_or(self.read_memarg(2)?), - 0x34 => visitor.visit_i64_atomic_rmw_or(self.read_memarg(3)?), - 0x35 => visitor.visit_i32_atomic_rmw8_or_u(self.read_memarg(0)?), - 0x36 => visitor.visit_i32_atomic_rmw16_or_u(self.read_memarg(1)?), - 0x37 => visitor.visit_i64_atomic_rmw8_or_u(self.read_memarg(0)?), - 0x38 => visitor.visit_i64_atomic_rmw16_or_u(self.read_memarg(1)?), - 0x39 => visitor.visit_i64_atomic_rmw32_or_u(self.read_memarg(2)?), - 0x3a => visitor.visit_i32_atomic_rmw_xor(self.read_memarg(2)?), - 0x3b => visitor.visit_i64_atomic_rmw_xor(self.read_memarg(3)?), - 0x3c => visitor.visit_i32_atomic_rmw8_xor_u(self.read_memarg(0)?), - 0x3d => visitor.visit_i32_atomic_rmw16_xor_u(self.read_memarg(1)?), - 0x3e => visitor.visit_i64_atomic_rmw8_xor_u(self.read_memarg(0)?), - 0x3f => visitor.visit_i64_atomic_rmw16_xor_u(self.read_memarg(1)?), - 0x40 => visitor.visit_i64_atomic_rmw32_xor_u(self.read_memarg(2)?), - 0x41 => visitor.visit_i32_atomic_rmw_xchg(self.read_memarg(2)?), - 0x42 => visitor.visit_i64_atomic_rmw_xchg(self.read_memarg(3)?), - 0x43 => visitor.visit_i32_atomic_rmw8_xchg_u(self.read_memarg(0)?), - 0x44 => visitor.visit_i32_atomic_rmw16_xchg_u(self.read_memarg(1)?), - 0x45 => visitor.visit_i64_atomic_rmw8_xchg_u(self.read_memarg(0)?), - 0x46 => visitor.visit_i64_atomic_rmw16_xchg_u(self.read_memarg(1)?), - 0x47 => visitor.visit_i64_atomic_rmw32_xchg_u(self.read_memarg(2)?), - 0x48 => visitor.visit_i32_atomic_rmw_cmpxchg(self.read_memarg(2)?), - 0x49 => visitor.visit_i64_atomic_rmw_cmpxchg(self.read_memarg(3)?), - 0x4a => visitor.visit_i32_atomic_rmw8_cmpxchg_u(self.read_memarg(0)?), - 0x4b => visitor.visit_i32_atomic_rmw16_cmpxchg_u(self.read_memarg(1)?), - 0x4c => visitor.visit_i64_atomic_rmw8_cmpxchg_u(self.read_memarg(0)?), - 0x4d => visitor.visit_i64_atomic_rmw16_cmpxchg_u(self.read_memarg(1)?), - 0x4e => visitor.visit_i64_atomic_rmw32_cmpxchg_u(self.read_memarg(2)?), - - // Decode shared-everything-threads proposal. - 0x4f => { - visitor.visit_global_atomic_get(self.read_ordering()?, self.reader.read_var_u32()?) - } - 0x50 => { - visitor.visit_global_atomic_set(self.read_ordering()?, self.reader.read_var_u32()?) - } - 0x51 => visitor - .visit_global_atomic_rmw_add(self.read_ordering()?, self.reader.read_var_u32()?), - 0x52 => visitor - .visit_global_atomic_rmw_sub(self.read_ordering()?, self.reader.read_var_u32()?), - 0x53 => visitor - .visit_global_atomic_rmw_and(self.read_ordering()?, self.reader.read_var_u32()?), - 0x54 => visitor - .visit_global_atomic_rmw_or(self.read_ordering()?, self.reader.read_var_u32()?), - 0x55 => visitor - .visit_global_atomic_rmw_xor(self.read_ordering()?, self.reader.read_var_u32()?), - 0x56 => visitor - .visit_global_atomic_rmw_xchg(self.read_ordering()?, self.reader.read_var_u32()?), - 0x57 => visitor.visit_global_atomic_rmw_cmpxchg( - self.read_ordering()?, - self.reader.read_var_u32()?, - ), - 0x58 => { - visitor.visit_table_atomic_get(self.read_ordering()?, self.reader.read_var_u32()?) - } - 0x59 => { - visitor.visit_table_atomic_set(self.read_ordering()?, self.reader.read_var_u32()?) - } - 0x5a => visitor - .visit_table_atomic_rmw_xchg(self.read_ordering()?, self.reader.read_var_u32()?), - 0x5b => visitor - .visit_table_atomic_rmw_cmpxchg(self.read_ordering()?, self.reader.read_var_u32()?), - 0x5c => visitor.visit_struct_atomic_get( - self.read_ordering()?, - self.reader.read_var_u32()?, - self.reader.read_var_u32()?, - ), - 0x5d => visitor.visit_struct_atomic_get_s( - self.read_ordering()?, - self.reader.read_var_u32()?, - self.reader.read_var_u32()?, - ), - 0x5e => visitor.visit_struct_atomic_get_u( - self.read_ordering()?, - self.reader.read_var_u32()?, - self.reader.read_var_u32()?, - ), - 0x5f => visitor.visit_struct_atomic_set( - self.read_ordering()?, - self.reader.read_var_u32()?, - self.reader.read_var_u32()?, - ), - 0x60 => visitor.visit_struct_atomic_rmw_add( - self.read_ordering()?, - self.reader.read_var_u32()?, - self.reader.read_var_u32()?, - ), - 0x61 => visitor.visit_struct_atomic_rmw_sub( - self.read_ordering()?, - self.reader.read_var_u32()?, - self.reader.read_var_u32()?, - ), - 0x62 => visitor.visit_struct_atomic_rmw_and( - self.read_ordering()?, - self.reader.read_var_u32()?, - self.reader.read_var_u32()?, - ), - 0x63 => visitor.visit_struct_atomic_rmw_or( - self.read_ordering()?, - self.reader.read_var_u32()?, - self.reader.read_var_u32()?, - ), - 0x64 => visitor.visit_struct_atomic_rmw_xor( - self.read_ordering()?, - self.reader.read_var_u32()?, - self.reader.read_var_u32()?, - ), - 0x65 => visitor.visit_struct_atomic_rmw_xchg( - self.read_ordering()?, - self.reader.read_var_u32()?, - self.reader.read_var_u32()?, - ), - 0x66 => visitor.visit_struct_atomic_rmw_cmpxchg( - self.read_ordering()?, - self.reader.read_var_u32()?, - self.reader.read_var_u32()?, - ), - 0x67 => { - visitor.visit_array_atomic_get(self.read_ordering()?, self.reader.read_var_u32()?) - } - 0x68 => { - visitor.visit_array_atomic_get_s(self.read_ordering()?, self.reader.read_var_u32()?) - } - 0x69 => { - visitor.visit_array_atomic_get_u(self.read_ordering()?, self.reader.read_var_u32()?) - } - 0x6a => { - visitor.visit_array_atomic_set(self.read_ordering()?, self.reader.read_var_u32()?) - } - 0x6b => visitor - .visit_array_atomic_rmw_add(self.read_ordering()?, self.reader.read_var_u32()?), - 0x6c => visitor - .visit_array_atomic_rmw_sub(self.read_ordering()?, self.reader.read_var_u32()?), - 0x6d => visitor - .visit_array_atomic_rmw_and(self.read_ordering()?, self.reader.read_var_u32()?), - 0x6e => visitor - .visit_array_atomic_rmw_or(self.read_ordering()?, self.reader.read_var_u32()?), - 0x6f => visitor - .visit_array_atomic_rmw_xor(self.read_ordering()?, self.reader.read_var_u32()?), - 0x70 => visitor - .visit_array_atomic_rmw_xchg(self.read_ordering()?, self.reader.read_var_u32()?), - 0x71 => visitor - .visit_array_atomic_rmw_cmpxchg(self.read_ordering()?, self.reader.read_var_u32()?), - 0x72 => visitor.visit_ref_i31_shared(), - - _ => bail!(pos, "unknown 0xfe subopcode: 0x{code:x}"), - }) + /// Converts to an iterator of operators paired with offsets. + pub fn into_iter_with_offsets(self) -> OperatorsIteratorWithOffsets<'a> { + OperatorsIteratorWithOffsets { + reader: self, + err: false, + } } pub(crate) fn skip_const_expr(&mut self) -> Result<()> { // TODO add skip_operator() method and/or validate ConstExpr operators. loop { if let Operator::End = self.read()? { - self.ensure_stack_empty()?; + if self.current_frame().is_some() { + bail!( + self.original_position(), + "control frames remain at end of expression" + ); + } return Ok(()); } } } - /// Gets a binary reader from this operators reader. - pub fn get_binary_reader(&self) -> BinaryReader<'a> { - self.reader.clone() - } - - /// Returns whether there is an `end` opcode followed by eof remaining in - /// this reader. - pub fn is_end_then_eof(&self) -> bool { - self.reader.is_end_then_eof() - } - - fn read_memarg(&mut self, max_align: u8) -> Result { - let flags_pos = self.original_position(); - let mut flags = self.reader.read_var_u32()?; - - let memory = if self.reader.multi_memory() && flags & (1 << 6) != 0 { - flags ^= 1 << 6; - self.reader.read_var_u32()? - } else { - 0 - }; - let max_flag_bits = if self.reader.multi_memory() { 6 } else { 5 }; - if flags >= (1 << max_flag_bits) { - return Err(BinaryReaderError::new( - "malformed memop alignment: alignment too large", - flags_pos, - )); - } - let align = flags as u8; - let offset = if self.reader.memory64() { - self.reader.read_var_u64()? - } else { - u64::from(self.reader.read_var_u32()?) - }; - Ok(MemArg { - align, - max_align, - offset, - memory, - }) - } - - fn read_ordering(&mut self) -> Result { - let byte = self.reader.read_var_u32()?; - match byte { - 0 => Ok(Ordering::SeqCst), - 1 => Ok(Ordering::AcqRel), - x => Err(BinaryReaderError::new( - &format!("invalid atomic consistency ordering {x}"), - self.original_position() - 1, - )), - } - } - - fn read_br_table(&mut self) -> Result> { - let cnt = self.reader.read_size(MAX_WASM_BR_TABLE_SIZE, "br_table")?; - let reader = self.reader.skip(|reader| { - for _ in 0..cnt { - reader.read_var_u32()?; - } - Ok(()) - })?; - let default = self.reader.read_var_u32()?; - Ok(BrTable { - reader, - cnt: cnt as u32, - default, - }) - } - - #[cfg(feature = "simd")] - fn read_lane_index(&mut self) -> Result { - self.reader.read_u8() - } - - #[cfg(feature = "simd")] - fn read_v128(&mut self) -> Result { - let mut bytes = [0; 16]; - bytes.clone_from_slice(self.reader.read_bytes(16)?); - Ok(V128(bytes)) - } - - fn read_memory_index_or_zero_if_not_multi_memory(&mut self) -> Result { - if self.reader.multi_memory() { - self.reader.read_var_u32() - } else { - // Before bulk memory this byte was required to be a single zero - // byte, not a LEB-encoded zero, so require a precise zero byte. - match self.reader.read_u8()? { - 0 => Ok(0), - _ => bail!(self.original_position() - 1, "zero byte expected"), - } - } + /// Function that must be called after the last opcode has been processed. + /// + /// This function returns an error if there is extra data after the operators. + /// It does *not* check the binary format requirement that if the data count + /// section is absent, a data index may not occur in the code section. + pub fn finish(&self) -> Result<()> { + self.reader.finish_expression(self) } +} - fn read_call_indirect_table_immediate(&mut self) -> Result { - // If the `call_indirect_overlong` feature is enabled, then read this - // immediate as a LEB. This feature is enabled as part of the - // `reference_types` feature or the `lime1` feature. - if self.reader.call_indirect_overlong() { - return self.reader.read_var_u32(); - } - - // Before reference types this byte was required to be a single zero - // byte, not a LEB-encoded zero, so require a precise zero byte. - match self.reader.read_u8()? { - 0 => Ok(0), - _ => bail!(self.original_position() - 1, "zero byte expected"), - } +impl<'a> FrameStack for OperatorsReader<'a> { + fn current_frame(&self) -> Option<&FrameKind> { + self.stack.last() } } @@ -1699,7 +557,7 @@ impl<'a> IntoIterator for OperatorsReader<'a> { /// let code_reader = CodeSectionReader::new(reader).unwrap(); /// for body in code_reader { /// let body = body.expect("function body"); - /// let mut op_reader = body.get_operators_reader().expect("op reader"); + /// let mut op_reader = body.get_operators_reader(Default::default()).expect("op reader"); /// let ops = op_reader.into_iter().collect::>>().expect("ops"); /// assert!( /// if let [Operator::Nop, Operator::End] = ops.as_slice() { true } else { false }, @@ -1722,6 +580,14 @@ pub struct OperatorsIterator<'a> { err: bool, } +impl<'a> OperatorsIterator<'a> { + /// Consumes this iterator and returns the underlying allocations. + /// See [`OperatorsReader::into_allocations`]. + pub fn into_allocations(self) -> OperatorsReaderAllocations { + self.reader.into_allocations() + } +} + impl<'a> Iterator for OperatorsIterator<'a> { type Item = Result>; @@ -1741,6 +607,14 @@ pub struct OperatorsIteratorWithOffsets<'a> { err: bool, } +impl<'a> OperatorsIteratorWithOffsets<'a> { + /// Consumes this iterator and returns the underlying allocations. + /// See [`OperatorsReader::into_allocations`]. + pub fn into_allocations(self) -> OperatorsReaderAllocations { + self.reader.into_allocations() + } +} + impl<'a> Iterator for OperatorsIteratorWithOffsets<'a> { type Item = Result<(Operator<'a>, usize)>; @@ -1755,7 +629,7 @@ impl<'a> Iterator for OperatorsIteratorWithOffsets<'a> { /// let code_reader = CodeSectionReader::new(reader).unwrap(); /// for body in code_reader { /// let body = body.expect("function body"); - /// let mut op_reader = body.get_operators_reader().expect("op reader"); + /// let mut op_reader = body.get_operators_reader(Default::default()).expect("op reader"); /// let ops = op_reader.into_iter_with_offsets().collect::>>().expect("ops"); /// assert!( /// if let [(Operator::Nop, 23), (Operator::End, 24)] = ops.as_slice() { true } else { false }, @@ -2069,3 +943,123 @@ impl<'a> VisitOperator<'a> for OperatorFactory<'a> { impl<'a> VisitSimdOperator<'a> for OperatorFactory<'a> { crate::for_each_visit_simd_operator!(define_visit_operator); } + +macro_rules! define_visit_operator_stack_adapter { + ($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*))*) => { + $( + fn $visit(&mut self $($(,$arg: $argty)*)?) -> T::Output { + define_visit_operator_stack_adapter!(@visit self $visit $($($arg,)*)?) + } + )* + }; + + (@visit $self:ident visit_block $($rest:tt)*) => {{ + $self.stack.push(FrameKind::Block); + $self.visitor.visit_block( $($rest)* ) + }}; + + (@visit $self:ident visit_loop $($rest:tt)*) => {{ + $self.stack.push(FrameKind::Loop); + $self.visitor.visit_loop( $($rest)* ) + }}; + + (@visit $self:ident visit_if $($rest:tt)*) => {{ + $self.stack.push(FrameKind::If); + $self.visitor.visit_if( $($rest)* ) + }}; + + (@visit $self:ident visit_else $($rest:tt)*) => {{ + $self.stack.pop(); + $self.stack.push(FrameKind::Else); + $self.visitor.visit_else( $($rest)* ) + }}; + + (@visit $self:ident visit_try $($rest:tt)*) => {{ + $self.stack.push(FrameKind::LegacyTry); + $self.visitor.visit_try( $($rest)* ) + }}; + + (@visit $self:ident visit_catch $($rest:tt)*) => {{ + $self.stack.pop(); + $self.stack.push(FrameKind::LegacyCatch); + $self.visitor.visit_catch( $($rest)* ) + }}; + + (@visit $self:ident visit_catch_all $($rest:tt)*) => {{ + $self.stack.pop(); + $self.stack.push(FrameKind::LegacyCatchAll); + $self.visitor.visit_catch_all( $($rest)* ) + }}; + + (@visit $self:ident visit_try_table $($rest:tt)*) => {{ + $self.stack.push(FrameKind::TryTable); + $self.visitor.visit_try_table( $($rest)* ) + }}; + + (@visit $self:ident visit_delegate $($rest:tt)*) => {{ + $self.stack.pop(); + $self.visitor.visit_delegate( $($rest)* ) + }}; + + (@visit $self:ident visit_end $($rest:tt)*) => {{ + $self.stack.pop(); + $self.visitor.visit_end( $($rest)* ) + }}; + + (@visit $self:ident $visit:ident $($rest:tt)*) => { + $self.visitor.$visit( $($rest)* ) + }; +} + +impl<'x, 'a, T: VisitOperator<'a>> VisitOperator<'a> for FrameStackAdapter<'x, 'a, T> { + type Output = T::Output; + + #[cfg(feature = "simd")] + fn simd_visitor(&mut self) -> Option<&mut dyn VisitSimdOperator<'a, Output = Self::Output>> { + self.visitor.simd_visitor() + } + + crate::for_each_visit_operator!(define_visit_operator_stack_adapter); +} + +macro_rules! define_passthrough_visit_operator { + ($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*))*) => { + $( + fn $visit(&mut self $($(,$arg: $argty)*)?) -> T::Output { + self.visitor.$visit( $($($arg,)*)? ) + } + )* + }; +} + +impl<'x, 'a, T: VisitOperator<'a>> VisitOperator<'a> for SingleFrameAdapter<'x, 'a, T> { + type Output = T::Output; + + #[cfg(feature = "simd")] + fn simd_visitor(&mut self) -> Option<&mut dyn VisitSimdOperator<'a, Output = Self::Output>> { + self.visitor.simd_visitor() + } + + crate::for_each_visit_operator!(define_passthrough_visit_operator); +} + +impl<'a> BinaryReader<'a> { + /// Peeks at the next available `Operator`, given a borrowed `FrameStack`. + /// + /// # Errors + /// + /// If `BinaryReader` has less bytes remaining than required to parse + /// the `Operator`, or if the input is malformed. + pub fn peek_operator(&self, stack: &T) -> Result> { + self.clone().visit_operator(&mut SingleFrameAdapter { + current_frame: *stack.current_frame().ok_or_else(|| { + format_err!( + self.original_position(), + "operators remaining after end of function body or expression" + ) + })?, + visitor: &mut OperatorFactory::new(), + marker: core::marker::PhantomData, + }) + } +} diff --git a/crates/wasmparser/src/validator/core.rs b/crates/wasmparser/src/validator/core.rs index 4c8373402b..34e3ab78ed 100644 --- a/crates/wasmparser/src/validator/core.rs +++ b/crates/wasmparser/src/validator/core.rs @@ -13,10 +13,10 @@ use super::{ #[cfg(feature = "simd")] use crate::VisitSimdOperator; use crate::{ - BinaryReaderError, ConstExpr, Data, DataKind, Element, ElementKind, ExternalKind, FuncType, - Global, GlobalType, HeapType, MemoryType, RecGroup, RefType, Result, SubType, Table, TableInit, - TableType, TagType, TypeRef, UnpackedIndex, ValType, VisitOperator, WasmFeatures, - WasmModuleResources, limits::*, + BinaryReaderError, ConstExpr, Data, DataKind, Element, ElementKind, ExternalKind, FrameKind, + FrameStack, FuncType, Global, GlobalType, HeapType, MemoryType, RecGroup, RefType, Result, + SubType, Table, TableInit, TableType, TagType, TypeRef, UnpackedIndex, ValType, VisitOperator, + WasmFeatures, WasmModuleResources, limits::*, }; use crate::{CompositeInnerType, prelude::*}; use alloc::sync::Arc; @@ -217,7 +217,7 @@ impl ModuleState { }, }; - let mut ops = expr.get_operators_reader(); + let mut ops = expr.get_binary_reader(); while !ops.eof() { validator.offset = ops.original_position(); ops.visit_operator(&mut validator)??; @@ -466,6 +466,12 @@ impl ModuleState { impl<'a> VisitSimdOperator<'a> for VisitConstOperator<'a> { crate::for_each_visit_simd_operator!(define_visit_operator); } + + impl<'a> FrameStack for VisitConstOperator<'a> { + fn current_frame(&self) -> Option<&FrameKind> { + Some(&self.ops.get_frame(0)?.kind) + } + } } } diff --git a/crates/wasmparser/src/validator/func.rs b/crates/wasmparser/src/validator/func.rs index 8456a63c9a..17f2122d05 100644 --- a/crates/wasmparser/src/validator/func.rs +++ b/crates/wasmparser/src/validator/func.rs @@ -1,6 +1,6 @@ use super::operators::{Frame, OperatorValidator, OperatorValidatorAllocations}; -use crate::{BinaryReader, OperatorsReader, Result, ValType, VisitOperator}; -use crate::{FunctionBody, ModuleArity, Operator, WasmFeatures, WasmModuleResources}; +use crate::{BinaryReader, Result, ValType, VisitOperator}; +use crate::{FrameStack, FunctionBody, ModuleArity, Operator, WasmFeatures, WasmModuleResources}; /// Resources necessary to perform validation of a function. /// @@ -120,23 +120,22 @@ impl FuncValidator { { reader.set_features(self.validator.features); } - let mut ops = OperatorsReader::new(reader); - while !ops.eof() { + while !reader.eof() { // In a debug build, verify that the validator's pops and pushes to and from // the operand stack match the operator's arity. #[cfg(debug_assertions)] - let (mut ops_before, arity) = { - let op = ops.clone().read()?; - let arity = op.operator_arity(&self.visitor(ops.original_position())); - (ops.clone(), arity) + let (ops_before, arity) = { + let op = reader.peek_operator(&self.visitor(reader.original_position()))?; + let arity = op.operator_arity(&self.visitor(reader.original_position())); + (reader.clone(), arity) }; - ops.visit_operator(&mut self.visitor(ops.original_position()))??; + reader.visit_operator(&mut self.visitor(reader.original_position()))??; #[cfg(debug_assertions)] { let (params, results) = arity.ok_or(format_err!( - ops.original_position(), + reader.original_position(), "could not calculate operator arity" ))?; @@ -161,12 +160,12 @@ arity mismatch in validation operator: {:?} expected: {params} -> {results} got {pop_count} -> {push_count}", - ops_before.read()?, + ops_before.peek_operator(&self.visitor(ops_before.original_position()))?, ); } } } - ops.finish() + reader.finish_expression(&self.visitor(reader.original_position())) } /// Reads the local definitions from the given `BinaryReader`, often sourced @@ -214,18 +213,18 @@ arity mismatch in validation /// pub fn validate(validator: &mut FuncValidator, body: &FunctionBody<'_>) -> Result<()> /// where R: WasmModuleResources /// { - /// let mut operator_reader = body.get_operators_reader()?; + /// let mut operator_reader = body.get_binary_reader_for_operators()?; /// while !operator_reader.eof() { /// let mut visitor = validator.visitor(operator_reader.original_position()); /// operator_reader.visit_operator(&mut visitor)??; /// } - /// operator_reader.finish() + /// operator_reader.finish_expression(&validator.visitor(operator_reader.original_position())) /// } /// ``` pub fn visitor<'this, 'a: 'this>( &'this mut self, offset: usize, - ) -> impl VisitOperator<'a, Output = Result<()>> + ModuleArity + 'this { + ) -> impl VisitOperator<'a, Output = Result<()>> + ModuleArity + FrameStack + 'this { self.validator.with_resources(&self.resources, offset) } @@ -444,7 +443,7 @@ mod tests { .code_section_entry(&body) .unwrap() .into_validator(FuncValidatorAllocations::default()); - let ops = body.get_operators_reader().unwrap(); + let ops = body.get_operators_reader(Default::default()).unwrap(); for op in ops.into_iter() { let op = op.unwrap(); arity.push( diff --git a/crates/wasmparser/src/validator/operators.rs b/crates/wasmparser/src/validator/operators.rs index 6e1a1f45f4..5f23d6ede3 100644 --- a/crates/wasmparser/src/validator/operators.rs +++ b/crates/wasmparser/src/validator/operators.rs @@ -26,9 +26,10 @@ use crate::VisitSimdOperator; use crate::{ AbstractHeapType, BinaryReaderError, BlockType, BrTable, Catch, ContType, FieldType, FrameKind, - FuncType, GlobalType, Handle, HeapType, Ieee32, Ieee64, MemArg, ModuleArity, RefType, Result, - ResumeTable, StorageType, StructType, SubType, TableType, TryTable, UnpackedIndex, ValType, - VisitOperator, WasmFeatures, WasmModuleResources, limits::MAX_WASM_FUNCTION_LOCALS, + FrameStack, FuncType, GlobalType, Handle, HeapType, Ieee32, Ieee64, MemArg, ModuleArity, + RefType, Result, ResumeTable, StorageType, StructType, SubType, TableType, TryTable, + UnpackedIndex, ValType, VisitOperator, WasmFeatures, WasmModuleResources, + limits::MAX_WASM_FUNCTION_LOCALS, }; use crate::{CompositeInnerType, Ordering, prelude::*}; use core::ops::{Deref, DerefMut}; @@ -492,7 +493,7 @@ impl OperatorValidator { &'validator mut self, resources: &'resources T, offset: usize, - ) -> impl VisitOperator<'a, Output = Result<()>> + ModuleArity + 'validator + ) -> impl VisitOperator<'a, Output = Result<()>> + ModuleArity + FrameStack + 'validator where T: WasmModuleResources, 'resources: 'validator, @@ -4374,3 +4375,12 @@ where self.0.jump(depth).ok() } } + +impl FrameStack for WasmProposalValidator<'_, '_, R> +where + R: WasmModuleResources, +{ + fn current_frame(&self) -> Option<&FrameKind> { + Some(&self.0.control.last()?.kind) + } +} diff --git a/crates/wasmprinter/src/lib.rs b/crates/wasmprinter/src/lib.rs index dd0d21f87c..1a599e95f7 100644 --- a/crates/wasmprinter/src/lib.rs +++ b/crates/wasmprinter/src/lib.rs @@ -1434,7 +1434,7 @@ impl Printer<'_, '_> { func_start: usize, op_printer: &mut O, ) -> Result<()> { - let mut ops = OperatorsReader::new(body.clone()); + let mut ops = OperatorsReader::new(body.clone(), Default::default()); while !ops.eof() { if ops.is_end_then_eof() { ops.read()?; // final "end" opcode terminates instruction sequence @@ -1705,7 +1705,7 @@ impl Printer<'_, '_> { explicit: &str, ) -> Result<()> { self.start_group("")?; - let mut reader = expr.get_operators_reader(); + let mut reader = expr.get_operators_reader(Default::default()); if reader.read().is_ok() && !reader.is_end_then_eof() { write!(self.result, "{explicit} ")?; diff --git a/crates/wit-component/src/gc.rs b/crates/wit-component/src/gc.rs index 71a58e4d26..e8e9dd630e 100644 --- a/crates/wit-component/src/gc.rs +++ b/crates/wit-component/src/gc.rs @@ -472,7 +472,7 @@ impl<'a> Module<'a> { } fn operators(&mut self, reader: BinaryReader<'a>) -> Result<()> { - let mut ops = OperatorsReader::new(reader); + let mut ops = OperatorsReader::new(reader, Default::default()); while !ops.eof() { ops.visit_operator(self)?; } @@ -711,7 +711,7 @@ impl<'a> Module<'a> { (Some(lazy_stack_init_index), true) => { let mut func = map.new_function_with_parsed_locals(&body)?; func.instructions().call(lazy_stack_init_index); - let mut reader = body.get_operators_reader()?; + let mut reader = body.get_operators_reader(Default::default())?; while !reader.eof() { func.instruction(&map.parse_instruction(&mut reader)?); } diff --git a/src/bin/wasm-tools/dump.rs b/src/bin/wasm-tools/dump.rs index 42f81c2e88..8fe942010d 100644 --- a/src/bin/wasm-tools/dump.rs +++ b/src/bin/wasm-tools/dump.rs @@ -159,7 +159,7 @@ impl<'a> Dump<'a> { Payload::GlobalSection(s) => self.section(s, "global", |me, _end, g| { write!(me.state, "[global {}] {:?}", inc(&mut i.core_globals), g.ty)?; me.print(g.init_expr.get_binary_reader().original_position())?; - me.print_ops(g.init_expr.get_operators_reader()) + me.print_ops(g.init_expr.get_operators_reader(Default::default())) })?, Payload::StartSection { func, range } => { write!(self.state, "start section")?; @@ -189,7 +189,7 @@ impl<'a> Dump<'a> { } => { write!(me.state, " table[{table_index:?}]")?; me.print(offset_expr.get_binary_reader().original_position())?; - me.print_ops(offset_expr.get_operators_reader())?; + me.print_ops(offset_expr.get_operators_reader(Default::default()))?; write!(me.state, "{item_count} items")?; } ElementKind::Declared => { @@ -231,7 +231,7 @@ impl<'a> Dump<'a> { } => { write!(me.state, "data memory[{memory_index}]")?; me.print(offset_expr.get_binary_reader().original_position())?; - me.print_ops(offset_expr.get_operators_reader())?; + me.print_ops(offset_expr.get_operators_reader(Default::default()))?; } } me.print_byte_header()?; @@ -266,7 +266,10 @@ impl<'a> Dump<'a> { write!(self.state, "{amt} locals of type {ty:?}")?; self.print(locals.original_position())?; } - self.print_ops(OperatorsReader::new(locals.get_binary_reader()))?; + self.print_ops(OperatorsReader::new( + locals.get_binary_reader(), + Default::default(), + ))?; } // Component sections diff --git a/src/lib.rs b/src/lib.rs index 97022a3734..6b53231db4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -299,14 +299,8 @@ impl OutputArg { } pub fn parse_binary_wasm(parser: wasmparser::Parser, bytes: &[u8]) -> Result<()> { + let mut allocs = wasmparser::OperatorsReaderAllocations::default(); for payload in parser.parse_all(&bytes) { - parse_payload(payload)?; - } - return Ok(()); - - fn parse_payload( - payload: Result, - ) -> Result<()> { match payload? { wasmparser::Payload::TypeSection(s) => parse_section(s)?, wasmparser::Payload::ImportSection(s) => parse_section(s)?, @@ -323,11 +317,12 @@ pub fn parse_binary_wasm(parser: wasmparser::Parser, bytes: &[u8]) -> Result<()> for item in locals.by_ref() { let _ = item?; } - let mut ops = locals.into_operators_reader(); + let mut ops = locals.into_operators_reader(std::mem::take(&mut allocs)); while !ops.eof() { ops.read()?; } ops.finish()?; + allocs = ops.into_allocations(); } wasmparser::Payload::InstanceSection(s) => parse_section(s)?, @@ -345,8 +340,8 @@ pub fn parse_binary_wasm(parser: wasmparser::Parser, bytes: &[u8]) -> Result<()> _ => (), } - Ok(()) } + return Ok(()); fn parse_section<'a, T>(s: wasmparser::SectionLimited<'a, T>) -> Result<()> where From 2fe7a49a27d5dfb97ba72158383b3cb5e6d79ea8 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 23 Jun 2025 11:41:17 -0700 Subject: [PATCH 2/8] Don't require handling OperatorsReaderAllocations Make it a non-default API that other various locations can optionally use but aren't required to do so. --- crates/wasm-encoder/src/reencode.rs | 4 +-- crates/wasm-mutate/src/mutators/codemotion.rs | 2 +- .../src/mutators/modify_const_exprs.rs | 2 +- crates/wasm-mutate/src/mutators/peephole.rs | 2 +- crates/wasmparser/src/readers/core/code.rs | 26 +++++++------------ .../wasmparser/src/readers/core/elements.rs | 6 +++-- crates/wasmparser/src/readers/core/init.rs | 8 +++--- .../wasmparser/src/readers/core/operators.rs | 12 ++++++++- crates/wasmprinter/src/lib.rs | 4 +-- crates/wit-component/src/gc.rs | 4 +-- src/bin/wasm-tools/dump.rs | 11 +++----- src/lib.rs | 4 +-- 12 files changed, 42 insertions(+), 43 deletions(-) diff --git a/crates/wasm-encoder/src/reencode.rs b/crates/wasm-encoder/src/reencode.rs index 512098edb8..46f87d98bd 100644 --- a/crates/wasm-encoder/src/reencode.rs +++ b/crates/wasm-encoder/src/reencode.rs @@ -1594,7 +1594,7 @@ pub mod utils { reencoder: &mut T, const_expr: wasmparser::ConstExpr, ) -> Result> { - let mut ops = const_expr.get_operators_reader(Default::default()); + let mut ops = const_expr.get_operators_reader(); let mut bytes = Vec::new(); while !ops.is_end_then_eof() { @@ -1740,7 +1740,7 @@ pub mod utils { func: wasmparser::FunctionBody<'_>, ) -> Result<(), Error> { let mut f = reencoder.new_function_with_parsed_locals(&func)?; - let mut reader = func.get_operators_reader(Default::default())?; + let mut reader = func.get_operators_reader()?; while !reader.eof() { f.instruction(&reencoder.parse_instruction(&mut reader)?); } diff --git a/crates/wasm-mutate/src/mutators/codemotion.rs b/crates/wasm-mutate/src/mutators/codemotion.rs index 1d8d02d750..b4b0854a08 100644 --- a/crates/wasm-mutate/src/mutators/codemotion.rs +++ b/crates/wasm-mutate/src/mutators/codemotion.rs @@ -75,7 +75,7 @@ impl CodemotionMutator { for fidx in (function_to_mutate..function_count).chain(0..function_to_mutate) { config.consume_fuel(1)?; let reader = all_readers[fidx as usize].clone(); - let operatorreader = reader.get_operators_reader(Default::default())?; + let operatorreader = reader.get_operators_reader()?; let operators = operatorreader .into_iter_with_offsets() diff --git a/crates/wasm-mutate/src/mutators/modify_const_exprs.rs b/crates/wasm-mutate/src/mutators/modify_const_exprs.rs index e5fd15462b..28f794efba 100644 --- a/crates/wasm-mutate/src/mutators/modify_const_exprs.rs +++ b/crates/wasm-mutate/src/mutators/modify_const_exprs.rs @@ -62,7 +62,7 @@ impl<'cfg, 'wasm> Reencode for InitTranslator<'cfg, 'wasm> { return reencode::utils::const_expr(self, e); } - let mut reader = e.get_operators_reader(Default::default()); + let mut reader = e.get_operators_reader(); if !self.config.reduce { // FIXME: implement non-reducing mutations for constant expressions. diff --git a/crates/wasm-mutate/src/mutators/peephole.rs b/crates/wasm-mutate/src/mutators/peephole.rs index 45d7ee1760..3b7d7ddd70 100644 --- a/crates/wasm-mutate/src/mutators/peephole.rs +++ b/crates/wasm-mutate/src/mutators/peephole.rs @@ -122,7 +122,7 @@ impl PeepholeMutator { } let reader = readers[function_to_mutate as usize].clone(); - let operatorreader = reader.get_operators_reader(Default::default())?; + let operatorreader = reader.get_operators_reader()?; let mut localsreader = reader.get_locals_reader()?; let operators = operatorreader .into_iter_with_offsets() diff --git a/crates/wasmparser/src/readers/core/code.rs b/crates/wasmparser/src/readers/core/code.rs index 6b9e206839..95ce70b8d6 100644 --- a/crates/wasmparser/src/readers/core/code.rs +++ b/crates/wasmparser/src/readers/core/code.rs @@ -13,10 +13,7 @@ * limitations under the License. */ -use crate::{ - BinaryReader, FromReader, OperatorsReader, OperatorsReaderAllocations, Result, SectionLimited, - ValType, -}; +use crate::{BinaryReader, FromReader, OperatorsReader, Result, SectionLimited, ValType}; use core::ops::Range; /// A reader for the code section of a WebAssembly module. @@ -66,16 +63,12 @@ impl<'a> FunctionBody<'a> { Ok(reader) } - /// Gets the operators reader for this function body, after skipping locals. - /// A pre-existing [`OperatorsReaderAllocations`] instance can be used to reuse allocations - /// from a previous operators reader. It is also sufficient to pass `Default::default()`. - pub fn get_operators_reader( - &self, - allocs: OperatorsReaderAllocations, - ) -> Result> { - let mut reader = self.reader.clone(); - Self::skip_locals(&mut reader)?; - Ok(OperatorsReader::new(reader, allocs)) + /// Uses [`FunctionBody::get_binary_reader_for_operators`] and then converts + /// that to an [`OperatorsReader`]. + pub fn get_operators_reader(&self) -> Result> { + Ok(OperatorsReader::new( + self.get_binary_reader_for_operators()?, + )) } /// Gets the range of the function body. @@ -178,8 +171,7 @@ impl<'a> LocalsIterator<'a> { } /// After reading the locals, the BinaryReader is ready to read the operators. - pub fn into_operators_reader(self, allocs: OperatorsReaderAllocations) -> OperatorsReader<'a> { - debug_assert!(self.err || self.left == 0); - OperatorsReader::new(self.reader.get_binary_reader(), allocs) + pub fn into_operators_reader(self) -> OperatorsReader<'a> { + OperatorsReader::new(self.reader.get_binary_reader()) } } diff --git a/crates/wasmparser/src/readers/core/elements.rs b/crates/wasmparser/src/readers/core/elements.rs index 32b3463d91..176b7f894c 100644 --- a/crates/wasmparser/src/readers/core/elements.rs +++ b/crates/wasmparser/src/readers/core/elements.rs @@ -123,8 +123,10 @@ impl<'a> FromReader<'a> for Element<'a> { let mut allocs = OperatorsReaderAllocations::default(); if exprs { for _ in 0..items_count { - let mut ops = - OperatorsReader::new(reader.clone(), core::mem::take(&mut allocs)); + let mut ops = OperatorsReader::new_with_allocs( + reader.clone(), + core::mem::take(&mut allocs), + ); ops.skip_const_expr()?; *reader = ops.get_binary_reader(); allocs = ops.into_allocations(); diff --git a/crates/wasmparser/src/readers/core/init.rs b/crates/wasmparser/src/readers/core/init.rs index ddfccb23a7..46407d111f 100644 --- a/crates/wasmparser/src/readers/core/init.rs +++ b/crates/wasmparser/src/readers/core/init.rs @@ -13,7 +13,7 @@ * limitations under the License. */ -use crate::{BinaryReader, FromReader, OperatorsReader, OperatorsReaderAllocations, Result}; +use crate::{BinaryReader, FromReader, OperatorsReader, Result}; use core::fmt; /// Represents an initialization expression. @@ -42,8 +42,8 @@ impl<'a> ConstExpr<'a> { } /// Gets an operators parser for the initialization expression. - pub fn get_operators_reader(&self, allocs: OperatorsReaderAllocations) -> OperatorsReader<'a> { - OperatorsReader::new(self.get_binary_reader(), allocs) + pub fn get_operators_reader(&self) -> OperatorsReader<'a> { + OperatorsReader::new(self.get_binary_reader()) } } @@ -51,7 +51,7 @@ impl<'a> FromReader<'a> for ConstExpr<'a> { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { // FIXME(#188) ideally shouldn't need to skip here let reader = reader.skip(|reader| { - let mut ops = OperatorsReader::new(reader.clone(), Default::default()); + let mut ops = OperatorsReader::new(reader.clone()); ops.skip_const_expr()?; *reader = ops.get_binary_reader(); Ok(()) diff --git a/crates/wasmparser/src/readers/core/operators.rs b/crates/wasmparser/src/readers/core/operators.rs index e2e0e313fc..eff4d15406 100644 --- a/crates/wasmparser/src/readers/core/operators.rs +++ b/crates/wasmparser/src/readers/core/operators.rs @@ -390,7 +390,17 @@ impl<'a> OperatorsReader<'a> { /// individual expression or function body. Note that it is also sufficient /// to call this method with `Default::default()` if no prior allocations are /// available. - pub fn new(reader: BinaryReader<'a>, mut allocs: OperatorsReaderAllocations) -> Self { + pub fn new(reader: BinaryReader<'a>) -> Self { + Self::new_with_allocs(reader, Default::default()) + } + + /// Same as [`OperatorsReader::new`] except that the + /// [`OperatorsReaderAllocations`] can be specified here to amortize the + /// cost of them over multiple readers. + pub fn new_with_allocs( + reader: BinaryReader<'a>, + mut allocs: OperatorsReaderAllocations, + ) -> Self { allocs.0.clear(); allocs.0.push(FrameKind::Block); Self { diff --git a/crates/wasmprinter/src/lib.rs b/crates/wasmprinter/src/lib.rs index 1a599e95f7..dd0d21f87c 100644 --- a/crates/wasmprinter/src/lib.rs +++ b/crates/wasmprinter/src/lib.rs @@ -1434,7 +1434,7 @@ impl Printer<'_, '_> { func_start: usize, op_printer: &mut O, ) -> Result<()> { - let mut ops = OperatorsReader::new(body.clone(), Default::default()); + let mut ops = OperatorsReader::new(body.clone()); while !ops.eof() { if ops.is_end_then_eof() { ops.read()?; // final "end" opcode terminates instruction sequence @@ -1705,7 +1705,7 @@ impl Printer<'_, '_> { explicit: &str, ) -> Result<()> { self.start_group("")?; - let mut reader = expr.get_operators_reader(Default::default()); + let mut reader = expr.get_operators_reader(); if reader.read().is_ok() && !reader.is_end_then_eof() { write!(self.result, "{explicit} ")?; diff --git a/crates/wit-component/src/gc.rs b/crates/wit-component/src/gc.rs index e8e9dd630e..71a58e4d26 100644 --- a/crates/wit-component/src/gc.rs +++ b/crates/wit-component/src/gc.rs @@ -472,7 +472,7 @@ impl<'a> Module<'a> { } fn operators(&mut self, reader: BinaryReader<'a>) -> Result<()> { - let mut ops = OperatorsReader::new(reader, Default::default()); + let mut ops = OperatorsReader::new(reader); while !ops.eof() { ops.visit_operator(self)?; } @@ -711,7 +711,7 @@ impl<'a> Module<'a> { (Some(lazy_stack_init_index), true) => { let mut func = map.new_function_with_parsed_locals(&body)?; func.instructions().call(lazy_stack_init_index); - let mut reader = body.get_operators_reader(Default::default())?; + let mut reader = body.get_operators_reader()?; while !reader.eof() { func.instruction(&map.parse_instruction(&mut reader)?); } diff --git a/src/bin/wasm-tools/dump.rs b/src/bin/wasm-tools/dump.rs index 8fe942010d..42f81c2e88 100644 --- a/src/bin/wasm-tools/dump.rs +++ b/src/bin/wasm-tools/dump.rs @@ -159,7 +159,7 @@ impl<'a> Dump<'a> { Payload::GlobalSection(s) => self.section(s, "global", |me, _end, g| { write!(me.state, "[global {}] {:?}", inc(&mut i.core_globals), g.ty)?; me.print(g.init_expr.get_binary_reader().original_position())?; - me.print_ops(g.init_expr.get_operators_reader(Default::default())) + me.print_ops(g.init_expr.get_operators_reader()) })?, Payload::StartSection { func, range } => { write!(self.state, "start section")?; @@ -189,7 +189,7 @@ impl<'a> Dump<'a> { } => { write!(me.state, " table[{table_index:?}]")?; me.print(offset_expr.get_binary_reader().original_position())?; - me.print_ops(offset_expr.get_operators_reader(Default::default()))?; + me.print_ops(offset_expr.get_operators_reader())?; write!(me.state, "{item_count} items")?; } ElementKind::Declared => { @@ -231,7 +231,7 @@ impl<'a> Dump<'a> { } => { write!(me.state, "data memory[{memory_index}]")?; me.print(offset_expr.get_binary_reader().original_position())?; - me.print_ops(offset_expr.get_operators_reader(Default::default()))?; + me.print_ops(offset_expr.get_operators_reader())?; } } me.print_byte_header()?; @@ -266,10 +266,7 @@ impl<'a> Dump<'a> { write!(self.state, "{amt} locals of type {ty:?}")?; self.print(locals.original_position())?; } - self.print_ops(OperatorsReader::new( - locals.get_binary_reader(), - Default::default(), - ))?; + self.print_ops(OperatorsReader::new(locals.get_binary_reader()))?; } // Component sections diff --git a/src/lib.rs b/src/lib.rs index 6b53231db4..f95130e262 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -299,7 +299,6 @@ impl OutputArg { } pub fn parse_binary_wasm(parser: wasmparser::Parser, bytes: &[u8]) -> Result<()> { - let mut allocs = wasmparser::OperatorsReaderAllocations::default(); for payload in parser.parse_all(&bytes) { match payload? { wasmparser::Payload::TypeSection(s) => parse_section(s)?, @@ -317,12 +316,11 @@ pub fn parse_binary_wasm(parser: wasmparser::Parser, bytes: &[u8]) -> Result<()> for item in locals.by_ref() { let _ = item?; } - let mut ops = locals.into_operators_reader(std::mem::take(&mut allocs)); + let mut ops = locals.into_operators_reader(); while !ops.eof() { ops.read()?; } ops.finish()?; - allocs = ops.into_allocations(); } wasmparser::Payload::InstanceSection(s) => parse_section(s)?, From 7922819b330ae8c7ed31d7ac98109f5619ad1ccd Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 23 Jun 2025 11:48:19 -0700 Subject: [PATCH 3/8] More test fixes --- .../wasm-mutate/src/mutators/peephole/dfg.rs | 8 ++-- crates/wasmparser/benches/benchmark.rs | 48 ++++++++----------- crates/wasmparser/src/validator/func.rs | 2 +- 3 files changed, 24 insertions(+), 34 deletions(-) diff --git a/crates/wasm-mutate/src/mutators/peephole/dfg.rs b/crates/wasm-mutate/src/mutators/peephole/dfg.rs index db6af5c127..c02c96c132 100644 --- a/crates/wasm-mutate/src/mutators/peephole/dfg.rs +++ b/crates/wasm-mutate/src/mutators/peephole/dfg.rs @@ -1314,7 +1314,7 @@ mod tests { match payload { wasmparser::Payload::CodeSectionEntry(reader) => { let operators = reader - .get_operators_reader(Default::default()) + .get_operators_reader() .unwrap() .into_iter_with_offsets() .collect::>>() @@ -1370,7 +1370,7 @@ mod tests { match payload { wasmparser::Payload::CodeSectionEntry(reader) => { let operators = reader - .get_operators_reader(Default::default()) + .get_operators_reader() .unwrap() .into_iter_with_offsets() .collect::>>() @@ -1452,7 +1452,7 @@ mod tests { match payload { wasmparser::Payload::CodeSectionEntry(reader) => { let operators = reader - .get_operators_reader(Default::default()) + .get_operators_reader() .unwrap() .into_iter_with_offsets() .collect::>>() @@ -1509,7 +1509,7 @@ mod tests { match payload { wasmparser::Payload::CodeSectionEntry(reader) => { let operators = reader - .get_operators_reader(Default::default()) + .get_operators_reader() .unwrap() .into_iter_with_offsets() .collect::>>() diff --git a/crates/wasmparser/benches/benchmark.rs b/crates/wasmparser/benches/benchmark.rs index a3de9f7ad6..573c1fe558 100644 --- a/crates/wasmparser/benches/benchmark.rs +++ b/crates/wasmparser/benches/benchmark.rs @@ -2,10 +2,14 @@ use anyhow::Result; use criterion::{Criterion, criterion_group, criterion_main}; use once_cell::unsync::Lazy; use std::fs; +use std::mem; use std::path::Path; use std::path::PathBuf; use wasmparser::VisitSimdOperator; -use wasmparser::{DataKind, ElementKind, Parser, Payload, Validator, VisitOperator, WasmFeatures}; +use wasmparser::{ + BinaryReader, DataKind, ElementKind, OperatorsReader, Parser, Payload, Validator, + VisitOperator, WasmFeatures, +}; /// A benchmark input. pub struct BenchmarkInput { @@ -81,6 +85,16 @@ fn collect_test_files(path: &Path, list: &mut Vec) -> Result<()> fn read_all_wasm(wasm: &[u8]) -> Result<()> { use Payload::*; let mut allocs = wasmparser::OperatorsReaderAllocations::default(); + let mut read_expr = |reader: BinaryReader<'_>| -> Result<_> { + let mut ops = OperatorsReader::new_with_allocs(reader, mem::take(&mut allocs)); + + while !ops.eof() { + ops.visit_operator(&mut NopVisit)?; + } + ops.finish()?; + allocs = ops.into_allocations(); + Ok(()) + }; for item in Parser::new(0).parse_all(wasm) { match item? { TypeSection(s) => { @@ -115,14 +129,7 @@ fn read_all_wasm(wasm: &[u8]) -> Result<()> { } GlobalSection(s) => { for item in s { - let mut ops = item? - .init_expr - .get_operators_reader(std::mem::take(&mut allocs)) - .into_iter(); - for op in ops.by_ref() { - op?; - } - allocs = ops.into_allocations(); + read_expr(item?.init_expr.get_binary_reader())?; } } ExportSection(s) => { @@ -134,13 +141,7 @@ fn read_all_wasm(wasm: &[u8]) -> Result<()> { for item in s { let item = item?; if let ElementKind::Active { offset_expr, .. } = item.kind { - let mut ops = offset_expr - .get_operators_reader(std::mem::take(&mut allocs)) - .into_iter(); - for op in ops.by_ref() { - op?; - } - allocs = ops.into_allocations(); + read_expr(offset_expr.get_binary_reader())?; } match item.items { wasmparser::ElementItems::Functions(r) => { @@ -160,13 +161,7 @@ fn read_all_wasm(wasm: &[u8]) -> Result<()> { for item in s { let item = item?; if let DataKind::Active { offset_expr, .. } = item.kind { - let mut ops = offset_expr - .get_operators_reader(std::mem::take(&mut allocs)) - .into_iter(); - for op in ops.by_ref() { - op?; - } - allocs = ops.into_allocations(); + read_expr(offset_expr.get_binary_reader())?; } } } @@ -175,12 +170,7 @@ fn read_all_wasm(wasm: &[u8]) -> Result<()> { for item in locals.by_ref() { let _ = item?; } - let mut ops = locals.into_operators_reader(std::mem::take(&mut allocs)); - while !ops.eof() { - ops.visit_operator(&mut NopVisit)?; - } - ops.finish()?; - allocs = ops.into_allocations(); + read_expr(locals.into_binary_reader_for_operators())?; } // Component sections diff --git a/crates/wasmparser/src/validator/func.rs b/crates/wasmparser/src/validator/func.rs index 17f2122d05..9f21fca6a3 100644 --- a/crates/wasmparser/src/validator/func.rs +++ b/crates/wasmparser/src/validator/func.rs @@ -443,7 +443,7 @@ mod tests { .code_section_entry(&body) .unwrap() .into_validator(FuncValidatorAllocations::default()); - let ops = body.get_operators_reader(Default::default()).unwrap(); + let ops = body.get_operators_reader().unwrap(); for op in ops.into_iter() { let op = op.unwrap(); arity.push( From 22ab987868ec56c3c163f7cc81cb58836f184f1c Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 23 Jun 2025 11:49:49 -0700 Subject: [PATCH 4/8] Even more test fixes --- crates/wasm-mutate/src/mutators/peephole.rs | 2 +- crates/wasmparser/src/readers/core/operators.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/wasm-mutate/src/mutators/peephole.rs b/crates/wasm-mutate/src/mutators/peephole.rs index 3b7d7ddd70..58e2224f55 100644 --- a/crates/wasm-mutate/src/mutators/peephole.rs +++ b/crates/wasm-mutate/src/mutators/peephole.rs @@ -490,7 +490,7 @@ macro_rules! match_code_mutation { }); } Payload::CodeSectionEntry(reader) => { - let operatorsreader = reader.get_operators_reader(Default::default()).unwrap(); + let operatorsreader = reader.get_operators_reader().unwrap(); let range = operatorsreader.get_binary_reader().range(); let operators = operatorsreader .into_iter_with_offsets() diff --git a/crates/wasmparser/src/readers/core/operators.rs b/crates/wasmparser/src/readers/core/operators.rs index eff4d15406..045dead37d 100644 --- a/crates/wasmparser/src/readers/core/operators.rs +++ b/crates/wasmparser/src/readers/core/operators.rs @@ -142,7 +142,7 @@ impl<'a> BrTable<'a> { /// use wasmparser::{BinaryReader, OperatorsReader, Operator}; /// /// let buf = [0x0e, 0x02, 0x01, 0x02, 0x00]; - /// let mut reader = OperatorsReader::new(BinaryReader::new(&buf, 0), Default::default()); + /// let mut reader = OperatorsReader::new(BinaryReader::new(&buf, 0)); /// let op = reader.read().unwrap(); /// if let Operator::BrTable { targets } = op { /// let targets = targets.targets().collect::, _>>().unwrap(); @@ -567,7 +567,7 @@ impl<'a> IntoIterator for OperatorsReader<'a> { /// let code_reader = CodeSectionReader::new(reader).unwrap(); /// for body in code_reader { /// let body = body.expect("function body"); - /// let mut op_reader = body.get_operators_reader(Default::default()).expect("op reader"); + /// let mut op_reader = body.get_operators_reader().expect("op reader"); /// let ops = op_reader.into_iter().collect::>>().expect("ops"); /// assert!( /// if let [Operator::Nop, Operator::End] = ops.as_slice() { true } else { false }, @@ -639,7 +639,7 @@ impl<'a> Iterator for OperatorsIteratorWithOffsets<'a> { /// let code_reader = CodeSectionReader::new(reader).unwrap(); /// for body in code_reader { /// let body = body.expect("function body"); - /// let mut op_reader = body.get_operators_reader(Default::default()).expect("op reader"); + /// let mut op_reader = body.get_operators_reader().expect("op reader"); /// let ops = op_reader.into_iter_with_offsets().collect::>>().expect("ops"); /// assert!( /// if let [(Operator::Nop, 23), (Operator::End, 24)] = ops.as_slice() { true } else { false }, From 1e7bbbfd97b3e13bea050dca87480d341908a5af Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 23 Jun 2025 11:55:08 -0700 Subject: [PATCH 5/8] Simplify some adapter definitions --- .../wasmparser/src/readers/core/operators.rs | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/crates/wasmparser/src/readers/core/operators.rs b/crates/wasmparser/src/readers/core/operators.rs index 045dead37d..5d787caf1f 100644 --- a/crates/wasmparser/src/readers/core/operators.rs +++ b/crates/wasmparser/src/readers/core/operators.rs @@ -339,25 +339,23 @@ pub trait FrameStack { } /// Adapters from VisitOperators to FrameStacks -struct FrameStackAdapter<'x, 'a, T: VisitOperator<'a>> { - stack: &'x mut Vec, - visitor: &'x mut T, - marker: core::marker::PhantomData &'a ()>, +struct FrameStackAdapter<'a, T> { + stack: &'a mut Vec, + visitor: &'a mut T, } -impl<'x, 'a, T: VisitOperator<'a>> FrameStack for FrameStackAdapter<'x, 'a, T> { +impl FrameStack for FrameStackAdapter<'_, T> { fn current_frame(&self) -> Option<&FrameKind> { self.stack.last() } } -struct SingleFrameAdapter<'x, 'a, T: VisitOperator<'a>> { +struct SingleFrameAdapter<'a, T> { current_frame: FrameKind, - visitor: &'x mut T, - marker: core::marker::PhantomData &'a ()>, + visitor: &'a mut T, } -impl<'x, 'a, T: VisitOperator<'a>> FrameStack for SingleFrameAdapter<'x, 'a, T> { +impl FrameStack for SingleFrameAdapter<'_, T> { fn current_frame(&self) -> Option<&FrameKind> { Some(&self.current_frame) } @@ -503,7 +501,6 @@ impl<'a> OperatorsReader<'a> { self.reader.visit_operator(&mut FrameStackAdapter { stack: &mut self.stack, visitor, - marker: core::marker::PhantomData, }) } @@ -1021,7 +1018,7 @@ macro_rules! define_visit_operator_stack_adapter { }; } -impl<'x, 'a, T: VisitOperator<'a>> VisitOperator<'a> for FrameStackAdapter<'x, 'a, T> { +impl<'a, T: VisitOperator<'a>> VisitOperator<'a> for FrameStackAdapter<'_, T> { type Output = T::Output; #[cfg(feature = "simd")] @@ -1042,7 +1039,7 @@ macro_rules! define_passthrough_visit_operator { }; } -impl<'x, 'a, T: VisitOperator<'a>> VisitOperator<'a> for SingleFrameAdapter<'x, 'a, T> { +impl<'a, T: VisitOperator<'a>> VisitOperator<'a> for SingleFrameAdapter<'_, T> { type Output = T::Output; #[cfg(feature = "simd")] @@ -1069,7 +1066,6 @@ impl<'a> BinaryReader<'a> { ) })?, visitor: &mut OperatorFactory::new(), - marker: core::marker::PhantomData, }) } } From 1150709205b910c17b47a2be3e293dd90cffa8a9 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 23 Jun 2025 12:01:33 -0700 Subject: [PATCH 6/8] Simplify definition of `OperatorFactory` --- .../wasmparser/src/readers/core/operators.rs | 21 +++++-------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/crates/wasmparser/src/readers/core/operators.rs b/crates/wasmparser/src/readers/core/operators.rs index 5d787caf1f..78bb027f98 100644 --- a/crates/wasmparser/src/readers/core/operators.rs +++ b/crates/wasmparser/src/readers/core/operators.rs @@ -445,7 +445,7 @@ impl<'a> OperatorsReader<'a> { /// If `OperatorsReader` has less bytes remaining than required to parse /// the `Operator`, or if the input is malformed. pub fn read(&mut self) -> Result> { - self.visit_operator(&mut OperatorFactory::new()) + self.visit_operator(&mut OperatorFactory) } /// Visit the next available operator with the specified [`VisitOperator`] instance. @@ -912,18 +912,7 @@ impl<'a> FromReader<'a> for Handle { } /// A factory to construct [`Operator`] instances via the [`VisitOperator`] trait. -struct OperatorFactory<'a> { - marker: core::marker::PhantomData &'a ()>, -} - -impl<'a> OperatorFactory<'a> { - /// Creates a new [`OperatorFactory`]. - fn new() -> Self { - Self { - marker: core::marker::PhantomData, - } - } -} +struct OperatorFactory; macro_rules! define_visit_operator { ($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*))*) => { @@ -935,7 +924,7 @@ macro_rules! define_visit_operator { } } -impl<'a> VisitOperator<'a> for OperatorFactory<'a> { +impl<'a> VisitOperator<'a> for OperatorFactory { type Output = Operator<'a>; #[cfg(feature = "simd")] @@ -947,7 +936,7 @@ impl<'a> VisitOperator<'a> for OperatorFactory<'a> { } #[cfg(feature = "simd")] -impl<'a> VisitSimdOperator<'a> for OperatorFactory<'a> { +impl<'a> VisitSimdOperator<'a> for OperatorFactory { crate::for_each_visit_simd_operator!(define_visit_operator); } @@ -1065,7 +1054,7 @@ impl<'a> BinaryReader<'a> { "operators remaining after end of function body or expression" ) })?, - visitor: &mut OperatorFactory::new(), + visitor: &mut OperatorFactory, }) } } From 194e9bd86ba955dc7f39468d0eadd8419a4c650f Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 23 Jun 2025 12:03:49 -0700 Subject: [PATCH 7/8] Minor doc updates --- crates/wasmparser/src/readers/core/operators.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/wasmparser/src/readers/core/operators.rs b/crates/wasmparser/src/readers/core/operators.rs index 78bb027f98..242693301c 100644 --- a/crates/wasmparser/src/readers/core/operators.rs +++ b/crates/wasmparser/src/readers/core/operators.rs @@ -331,8 +331,10 @@ macro_rules! define_operator { crate::for_each_operator!(define_operator); /// A trait representing the stack of frames within a function. -/// The BinaryReader and OperatorsReaders use information about the current frame kind -/// to enforce the syntactic requirements of the binary format. +/// +/// The [`BinaryReader::visit_operator`] and [`OperatorsReaders`] type use +/// information about the current frame kind to enforce the syntactic +/// requirements of the binary format. pub trait FrameStack { /// The current frame kind. fn current_frame(&self) -> Option<&FrameKind>; From c9dc8cff7e3abbfd0fd5451ed305a45f33b77f31 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 23 Jun 2025 12:35:28 -0700 Subject: [PATCH 8/8] Fix doc link --- crates/wasmparser/src/readers/core/operators.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/wasmparser/src/readers/core/operators.rs b/crates/wasmparser/src/readers/core/operators.rs index 242693301c..176db94f6b 100644 --- a/crates/wasmparser/src/readers/core/operators.rs +++ b/crates/wasmparser/src/readers/core/operators.rs @@ -332,7 +332,8 @@ crate::for_each_operator!(define_operator); /// A trait representing the stack of frames within a function. /// -/// The [`BinaryReader::visit_operator`] and [`OperatorsReaders`] type use +/// The [`BinaryReader::visit_operator`] and +/// [`OperatorsReaders`](crate::OperatorsReader) type use /// information about the current frame kind to enforce the syntactic /// requirements of the binary format. pub trait FrameStack {