diff --git a/crates/wasmparser/benches/benchmark.rs b/crates/wasmparser/benches/benchmark.rs index 3ed10a8f9e..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 { @@ -80,6 +84,17 @@ 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(); + 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) => { @@ -114,9 +129,7 @@ fn read_all_wasm(wasm: &[u8]) -> Result<()> { } GlobalSection(s) => { for item in s { - for op in item?.init_expr.get_operators_reader() { - op?; - } + read_expr(item?.init_expr.get_binary_reader())?; } } ExportSection(s) => { @@ -128,9 +141,7 @@ 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() { - op?; - } + read_expr(offset_expr.get_binary_reader())?; } match item.items { wasmparser::ElementItems::Functions(r) => { @@ -150,9 +161,7 @@ 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() { - op?; - } + read_expr(offset_expr.get_binary_reader())?; } } } @@ -161,11 +170,7 @@ fn read_all_wasm(wasm: &[u8]) -> Result<()> { for item in locals.by_ref() { let _ = item?; } - let mut ops = locals.into_operators_reader(); - while !ops.eof() { - ops.visit_operator(&mut NopVisit)?; - } - ops.finish()?; + read_expr(locals.into_binary_reader_for_operators())?; } // 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..95ce70b8d6 100644 --- a/crates/wasmparser/src/readers/core/code.rs +++ b/crates/wasmparser/src/readers/core/code.rs @@ -56,11 +56,19 @@ impl<'a> FunctionBody<'a> { }) } - /// Gets the operators reader for this function body, after skipping locals. - pub fn get_operators_reader(&self) -> Result> { + /// 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(OperatorsReader::new(reader)) + Ok(reader) + } + + /// 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. @@ -157,8 +165,13 @@ 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) -> 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 aeb8637327..176b7f894c 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,16 @@ 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_with_allocs( + 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..46407d111f 100644 --- a/crates/wasmparser/src/readers/core/init.rs +++ b/crates/wasmparser/src/readers/core/init.rs @@ -41,7 +41,7 @@ impl<'a> ConstExpr<'a> { self.reader.clone() } - /// Gets an operators reader for the initialization expression. + /// Gets an operators parser for the initialization expression. pub fn get_operators_reader(&self) -> OperatorsReader<'a> { OperatorsReader::new(self.get_binary_reader()) } diff --git a/crates/wasmparser/src/readers/core/operators.rs b/crates/wasmparser/src/readers/core/operators.rs index c964c1ba6a..176db94f6b 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. @@ -332,22 +330,91 @@ 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::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 { + /// The current frame kind. + fn current_frame(&self) -> Option<&FrameKind>; +} + +/// Adapters from VisitOperators to FrameStacks +struct FrameStackAdapter<'a, T> { + stack: &'a mut Vec, + visitor: &'a mut T, +} + +impl FrameStack for FrameStackAdapter<'_, T> { + fn current_frame(&self) -> Option<&FrameKind> { + self.stack.last() + } +} + +struct SingleFrameAdapter<'a, T> { + current_frame: FrameKind, + visitor: &'a mut T, +} + +impl FrameStack for SingleFrameAdapter<'_, 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>) -> 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 { 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 +425,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,50 +446,9 @@ 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(()) + self.visit_operator(&mut OperatorFactory) } /// Visit the next available operator with the specified [`VisitOperator`] instance. @@ -442,7 +458,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 +501,54 @@ 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}"), + self.reader.visit_operator(&mut FrameStackAdapter { + stack: &mut self.stack, + visitor, }) } - 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}"), - }) - } - - #[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() } } @@ -1722,6 +590,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 +617,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)>; @@ -2031,18 +915,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)*))*) => { @@ -2054,7 +927,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")] @@ -2066,6 +939,125 @@ 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); } + +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<'a, T: VisitOperator<'a>> VisitOperator<'a> for FrameStackAdapter<'_, 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<'a, T: VisitOperator<'a>> VisitOperator<'a> for SingleFrameAdapter<'_, 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, + }) + } +} 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..9f21fca6a3 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) } 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/src/lib.rs b/src/lib.rs index 97022a3734..f95130e262 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -300,13 +300,6 @@ impl OutputArg { pub fn parse_binary_wasm(parser: wasmparser::Parser, bytes: &[u8]) -> Result<()> { 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)?, @@ -345,8 +338,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