diff --git a/Cargo.toml b/Cargo.toml index 1b3dd2d0af..252d54c0e4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -127,12 +127,12 @@ env_logger = { workspace = true } log = { workspace = true } tempfile = "3.2.0" termcolor = { workspace = true } +wasmparser = { workspace = true, features = ['std', 'component-model', 'simd'] } wat = { workspace = true, features = ['dwarf', 'component-model'] } # Dependencies of `validate` bitflags = { workspace = true, optional = true } rayon = { workspace = true, optional = true } -wasmparser = { workspace = true, optional = true, features = ['std', 'component-model', 'simd'] } # Dependencies of `print` wasmprinter = { workspace = true, features = ['component-model'] } @@ -191,6 +191,8 @@ pretty_assertions = { workspace = true } serde_json = "1.0" tempfile = "3.1" wast = { workspace = true } +arbitrary = { workspace = true } +wasm-smith = { workspace = true } [[test]] name = "cli" @@ -221,7 +223,6 @@ default = [ # Each subcommand is gated behind a feature and lists the dependencies it needs validate = [ - 'dep:wasmparser', 'rayon', 'dep:addr2line', 'dep:gimli', @@ -234,23 +235,22 @@ parse = [] smith = ['wasm-smith', 'arbitrary', 'dep:serde', 'dep:serde_derive', 'dep:serde_json'] shrink = ['wasm-shrink', 'is_executable'] mutate = ['wasm-mutate'] -dump = ['dep:wasmparser'] -objdump = ['dep:wasmparser'] -strip = ['wasm-encoder', 'dep:wasmparser', 'regex'] -compose = ['wasm-compose', 'dep:wasmparser'] -demangle = ['rustc-demangle', 'cpp_demangle', 'dep:wasmparser', 'wasm-encoder'] +dump = [] +objdump = [] +strip = ['wasm-encoder', 'regex'] +compose = ['wasm-compose'] +demangle = ['rustc-demangle', 'cpp_demangle', 'wasm-encoder'] component = [ 'wit-component', 'wit-encoder', 'wit-parser', 'dep:wast', 'wasm-encoder', - 'dep:wasmparser', 'dep:serde_json', ] -metadata = ['dep:wasmparser', 'wasm-metadata', 'dep:serde_json'] +metadata = ['wasm-metadata', 'dep:serde_json'] wit-smith = ['dep:wit-smith', 'arbitrary'] -addr2line = ['dep:addr2line', 'dep:gimli', 'dep:wasmparser'] +addr2line = ['dep:addr2line', 'dep:gimli'] completion = ['dep:clap_complete'] json-from-wast = ['dep:serde_derive', 'dep:serde_json', 'dep:wast', 'dep:serde'] wast = [ diff --git a/ci/generate-spec-tests.rs b/ci/generate-spec-tests.rs index 35676b6ef2..3496102745 100644 --- a/ci/generate-spec-tests.rs +++ b/ci/generate-spec-tests.rs @@ -42,6 +42,12 @@ fn copy_test(src: &Path, dst: &Path) { let mut contents = format!(";; RUN: wast \\\n"); contents.push_str(";; --assert default \\\n"); + + // Allow certain assert_malformed tests to be interpreted as assert_invalid + if src.iter().any(|p| p == "binary.wast") || src.iter().any(|p| p == "global.wast") { + contents.push_str(";; --assert permissive \\\n"); + } + contents.push_str(";; --snapshot tests/snapshots \\\n"); // This test specifically tests various forms of unicode which are diff --git a/crates/wasmparser/benches/benchmark.rs b/crates/wasmparser/benches/benchmark.rs index 89f5da5326..7e4b48c7e0 100644 --- a/crates/wasmparser/benches/benchmark.rs +++ b/crates/wasmparser/benches/benchmark.rs @@ -157,14 +157,14 @@ fn read_all_wasm(wasm: &[u8]) -> Result<()> { } } CodeSectionEntry(body) => { - let mut reader = body.get_binary_reader(); - for _ in 0..reader.read_var_u32()? { - reader.read_var_u32()?; - reader.read::()?; + for item in body.get_locals_reader()? { + let _ = item?; } - while !reader.eof() { - reader.visit_operator(&mut NopVisit)?; + let mut ops = body.get_operators_reader()?; + while !ops.eof() { + ops.visit_operator(&mut NopVisit)?; } + ops.finish()?; } // Component sections diff --git a/crates/wasmparser/src/arity.rs b/crates/wasmparser/src/arity.rs index 9f60d86ced..9deaf92b60 100644 --- a/crates/wasmparser/src/arity.rs +++ b/crates/wasmparser/src/arity.rs @@ -14,8 +14,8 @@ */ use crate::{ - BinaryReader, BinaryReaderError, BlockType, CompositeInnerType, ContType, FrameKind, FuncType, - Operator, RefType, Result, SubType, + BinaryReaderError, BlockType, CompositeInnerType, ContType, FrameKind, FuncType, Operator, + OperatorsReader, RefType, Result, SubType, }; /// To compute the arity (param and result counts) of "variable-arity" @@ -69,15 +69,12 @@ pub trait ModuleArity { } } -impl BinaryReader<'_> { +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()? - .operator_arity(module) - .ok_or_else(|| { - BinaryReaderError::new("operator arity is unknown", self.original_position()) - }) + self.clone().read()?.operator_arity(module).ok_or_else(|| { + BinaryReaderError::new("operator arity is unknown", self.original_position()) + }) } } diff --git a/crates/wasmparser/src/binary_reader.rs b/crates/wasmparser/src/binary_reader.rs index 050a967f14..2dfb61c46c 100644 --- a/crates/wasmparser/src/binary_reader.rs +++ b/crates/wasmparser/src/binary_reader.rs @@ -13,9 +13,6 @@ * limitations under the License. */ -#[cfg(feature = "simd")] -mod simd; - use crate::prelude::*; use crate::{limits::*, *}; use core::fmt; @@ -193,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 - /// [`BinaryReader::visit_operator`]. Validation must still be performed to + /// [`OperatorsReader::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 @@ -348,65 +345,6 @@ impl<'a> BinaryReader<'a> { }) } - 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 align = if flags >= (1 << 6) { - return Err(BinaryReaderError::new( - "malformed memop alignment: alignment too large", - flags_pos, - )); - } else { - 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, - }) - } - /// Returns whether the `BinaryReader` has reached the end of the file. #[inline] pub fn eof(&self) -> bool { @@ -838,770 +776,12 @@ impl<'a> BinaryReader<'a> { } } - /// Visit the next available operator with the specified [`VisitOperator`] instance. - /// - /// Note that this does not implicitly propagate any additional information such as instruction - /// offsets. In order to do so, consider storing such data within the visitor before visiting. - /// - /// # Errors - /// - /// If `BinaryReader` has less bytes remaining than required to parse the `Operator`. - /// - /// # Examples - /// - /// Store an offset for use in diagnostics or any other purposes: - /// - /// ``` - /// # use wasmparser::{BinaryReader, VisitOperator, Result, for_each_visit_operator}; - /// - /// pub fn dump(mut reader: BinaryReader) -> Result<()> { - /// let mut visitor = Dumper { offset: 0 }; - /// while !reader.eof() { - /// visitor.offset = reader.original_position(); - /// reader.visit_operator(&mut visitor)?; - /// } - /// Ok(()) - /// } - /// - /// struct Dumper { - /// offset: usize - /// } - /// - /// macro_rules! define_visit_operator { - /// ($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*))*) => { - /// $( - /// fn $visit(&mut self $($(,$arg: $argty)*)?) -> Self::Output { - /// println!("{}: {}", self.offset, stringify!($visit)); - /// } - /// )* - /// } - /// } - /// - /// impl<'a> VisitOperator<'a> for Dumper { - /// type Output = (); - /// for_each_visit_operator!(define_visit_operator); - /// } - /// - /// ``` - pub fn visit_operator(&mut self, visitor: &mut T) -> Result<>::Output> - where - T: VisitOperator<'a>, - { - let pos = self.original_position(); - let code = self.read_u8()? as 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 => visitor.visit_else(), - 0x06 => visitor.visit_try(self.read_block_type()?), - 0x07 => 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_table_index_or_zero_if_not_reference_types()?; - 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 => visitor.visit_delegate(self.read_var_u32()?), - 0x19 => visitor.visit_catch_all(), - 0x1a => visitor.visit_drop(), - 0x1b => visitor.visit_select(), - 0x1c => { - let results = self.read_var_u32()?; - if results != 1 { - return Err(BinaryReaderError::new( - "invalid result arity", - self.position, - )); - } - visitor.visit_typed_select(self.read()?) - } - 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}"), - }) - } - - 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}"), - }) - } - - /// Reads the next available `Operator`. - /// - /// # Errors - /// - /// If `BinaryReader` has less bytes remaining than required to parse - /// the `Operator`. - pub fn read_operator(&mut self) -> Result> { - self.visit_operator(&mut OperatorFactory::new()) - } - /// Returns whether there is an `end` opcode followed by eof remaining in /// this reader. pub fn is_end_then_eof(&self) -> bool { self.remaining_buffer() == &[0x0b] } - #[cfg(feature = "simd")] - fn read_lane_index(&mut self, max: u8) -> Result { - let index = self.read_u8()?; - if index >= max { - return Err(BinaryReaderError::new( - "invalid lane index", - self.original_position() - 1, - )); - } - Ok(index) - } - - #[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)) - } - pub(crate) fn read_header_version(&mut self) -> Result { let magic_number = self.read_bytes(4)?; if magic_number != WASM_MAGIC_NUMBER { @@ -1612,41 +792,6 @@ impl<'a> BinaryReader<'a> { } self.read_u32() } - - 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_operator()? { - return Ok(()); - } - } - } - - 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_table_index_or_zero_if_not_reference_types(&mut self) -> Result { - if self.reference_types() { - self.read_var_u32() - } else { - // 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"), - } - } - } } // See documentation on `BinaryReader::features` for more on what's going on @@ -1672,145 +817,6 @@ macro_rules! define_feature_accessor { super::features::foreach_wasm_feature!(define_feature_accessor); -impl<'a> BrTable<'a> { - /// Returns the number of `br_table` entries, not including the default - /// label - pub fn len(&self) -> u32 { - self.cnt - } - - /// Returns whether `BrTable` doesn't have any labels apart from the default one. - pub fn is_empty(&self) -> bool { - self.len() == 0 - } - - /// Returns the default target of this `br_table` instruction. - pub fn default(&self) -> u32 { - self.default - } - - /// Returns the list of targets that this `br_table` instruction will be - /// jumping to. - /// - /// This method will return an iterator which parses each target of this - /// `br_table` except the default target. The returned iterator will - /// yield `self.len()` elements. - /// - /// # Examples - /// - /// ```rust - /// use wasmparser::{BinaryReader, Operator}; - /// - /// let buf = [0x0e, 0x02, 0x01, 0x02, 0x00]; - /// let mut reader = BinaryReader::new(&buf, 0); - /// let op = reader.read_operator().unwrap(); - /// if let Operator::BrTable { targets } = op { - /// let targets = targets.targets().collect::, _>>().unwrap(); - /// assert_eq!(targets, [1, 2]); - /// } - /// ``` - pub fn targets(&self) -> BrTableTargets { - BrTableTargets { - reader: self.reader.clone(), - remaining: self.cnt, - } - } -} - -/// An iterator over the targets of a [`BrTable`]. -/// -/// # Note -/// -/// This iterator parses each target of the underlying `br_table` -/// except for the default target. -/// The iterator will yield exactly as many targets as the `br_table` has. -pub struct BrTableTargets<'a> { - reader: crate::BinaryReader<'a>, - remaining: u32, -} - -impl<'a> Iterator for BrTableTargets<'a> { - type Item = Result; - - fn size_hint(&self) -> (usize, Option) { - let remaining = usize::try_from(self.remaining).unwrap_or_else(|error| { - panic!("could not convert remaining `u32` into `usize`: {}", error) - }); - (remaining, Some(remaining)) - } - - fn next(&mut self) -> Option { - if self.remaining == 0 { - if !self.reader.eof() { - return Some(Err(BinaryReaderError::new( - "trailing data in br_table", - self.reader.original_position(), - ))); - } - return None; - } - self.remaining -= 1; - Some(self.reader.read_var_u32()) - } -} - -impl fmt::Debug for BrTable<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut f = f.debug_struct("BrTable"); - f.field("count", &self.cnt); - f.field("default", &self.default); - match self.targets().collect::>>() { - Ok(targets) => { - f.field("targets", &targets); - } - Err(_) => { - f.field("reader", &self.reader); - } - } - f.finish() - } -} - -/// 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, - } - } -} - -macro_rules! define_visit_operator { - ($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*))*) => { - $( - fn $visit(&mut self $($(,$arg: $argty)*)?) -> Operator<'a> { - Operator::$op $({ $($arg),* })? - } - )* - } -} - -impl<'a> VisitOperator<'a> for OperatorFactory<'a> { - type Output = Operator<'a>; - - #[cfg(feature = "simd")] - fn simd_visitor(&mut self) -> Option<&mut dyn VisitSimdOperator<'a, Output = Self::Output>> { - Some(self) - } - - crate::for_each_visit_operator!(define_visit_operator); -} - -#[cfg(feature = "simd")] -impl<'a> VisitSimdOperator<'a> for OperatorFactory<'a> { - crate::for_each_visit_simd_operator!(define_visit_operator); -} - /// Iterator returned from [`BinaryReader::read_iter`]. pub struct BinaryReaderIter<'a, 'me, T: FromReader<'a>> { remaining: usize, diff --git a/crates/wasmparser/src/binary_reader/simd.rs b/crates/wasmparser/src/binary_reader/simd.rs deleted file mode 100644 index 7d5854c582..0000000000 --- a/crates/wasmparser/src/binary_reader/simd.rs +++ /dev/null @@ -1,320 +0,0 @@ -use super::BinaryReader; -use crate::{Result, VisitOperator, VisitSimdOperator}; - -impl<'a> BinaryReader<'a> { - 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(32)? - } - 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(16)?), - 0x16 => visitor.visit_i8x16_extract_lane_u(self.read_lane_index(16)?), - 0x17 => visitor.visit_i8x16_replace_lane(self.read_lane_index(16)?), - 0x18 => visitor.visit_i16x8_extract_lane_s(self.read_lane_index(8)?), - 0x19 => visitor.visit_i16x8_extract_lane_u(self.read_lane_index(8)?), - 0x1a => visitor.visit_i16x8_replace_lane(self.read_lane_index(8)?), - 0x1b => visitor.visit_i32x4_extract_lane(self.read_lane_index(4)?), - - 0x1c => visitor.visit_i32x4_replace_lane(self.read_lane_index(4)?), - 0x1d => visitor.visit_i64x2_extract_lane(self.read_lane_index(2)?), - 0x1e => visitor.visit_i64x2_replace_lane(self.read_lane_index(2)?), - 0x1f => visitor.visit_f32x4_extract_lane(self.read_lane_index(4)?), - 0x20 => visitor.visit_f32x4_replace_lane(self.read_lane_index(4)?), - 0x21 => visitor.visit_f64x2_extract_lane(self.read_lane_index(2)?), - 0x22 => visitor.visit_f64x2_replace_lane(self.read_lane_index(2)?), - - 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(16)?; - visitor.visit_v128_load8_lane(memarg, lane) - } - 0x55 => { - let memarg = self.read_memarg(1)?; - let lane = self.read_lane_index(8)?; - visitor.visit_v128_load16_lane(memarg, lane) - } - 0x56 => { - let memarg = self.read_memarg(2)?; - let lane = self.read_lane_index(4)?; - visitor.visit_v128_load32_lane(memarg, lane) - } - 0x57 => { - let memarg = self.read_memarg(3)?; - let lane = self.read_lane_index(2)?; - visitor.visit_v128_load64_lane(memarg, lane) - } - 0x58 => { - let memarg = self.read_memarg(0)?; - let lane = self.read_lane_index(16)?; - visitor.visit_v128_store8_lane(memarg, lane) - } - 0x59 => { - let memarg = self.read_memarg(1)?; - let lane = self.read_lane_index(8)?; - visitor.visit_v128_store16_lane(memarg, lane) - } - 0x5a => { - let memarg = self.read_memarg(2)?; - let lane = self.read_lane_index(4)?; - visitor.visit_v128_store32_lane(memarg, lane) - } - 0x5b => { - let memarg = self.read_memarg(3)?; - let lane = self.read_lane_index(2)?; - 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}"), - }) - } -} diff --git a/crates/wasmparser/src/limits.rs b/crates/wasmparser/src/limits.rs index 47320ca2bb..33d5578e34 100644 --- a/crates/wasmparser/src/limits.rs +++ b/crates/wasmparser/src/limits.rs @@ -47,7 +47,7 @@ pub const DEFAULT_WASM_PAGE_SIZE: u64 = 1 << 16; pub fn max_wasm_memory32_pages(page_size: u64) -> u64 { assert!(page_size.is_power_of_two()); assert!(page_size <= DEFAULT_WASM_PAGE_SIZE); - (1 << 32) / page_size + u32::try_from((1_u128 << 32) / u128::from(page_size)).unwrap_or(u32::MAX) as u64 } pub fn max_wasm_memory64_pages(page_size: u64) -> u64 { diff --git a/crates/wasmparser/src/parser.rs b/crates/wasmparser/src/parser.rs index a8aa331bbb..be006bc82a 100644 --- a/crates/wasmparser/src/parser.rs +++ b/crates/wasmparser/src/parser.rs @@ -42,6 +42,39 @@ pub enum Encoding { Component, } +#[derive(Debug, Clone, Default)] +struct ParserCounts { + function_entries: Option, + code_entries: Option, + data_entries: Option, + data_count: Option, + #[cfg(feature = "component-model")] + component_start_sections: bool, +} + +// Section order for WebAssembly modules. +// +// Component sections are unordered and allow for duplicates, +// so this isn't used for components. +#[derive(Copy, Clone, Default, PartialOrd, Ord, PartialEq, Eq, Debug)] +pub(crate) enum Order { + #[default] + Initial, + Type, + Import, + Function, + Table, + Memory, + Tag, + Global, + Export, + Start, + Element, + DataCount, + Code, + Data, +} + /// An incremental parser of a binary WebAssembly module or component. /// /// This type is intended to be used to incrementally parse a WebAssembly module @@ -59,6 +92,8 @@ pub struct Parser { encoding: Encoding, #[cfg(feature = "features")] features: WasmFeatures, + counts: ParserCounts, + order: (Order, u64), } #[derive(Debug, Clone)] @@ -373,6 +408,8 @@ impl Parser { encoding: Encoding::Module, #[cfg(feature = "features")] features: WasmFeatures::all(), + counts: ParserCounts::default(), + order: (Order::default(), offset), } } @@ -627,6 +664,22 @@ impl Parser { } } + fn update_order(&mut self, order: Order, pos: usize) -> Result<()> { + let pos_u64 = usize_to_u64(pos); + if self.encoding == Encoding::Module { + match self.order { + (last_order, last_pos) if last_order >= order && last_pos < pos_u64 => { + bail!(pos, "section out of order") + } + _ => (), + } + } + + self.order = (order, pos_u64); + + Ok(()) + } + fn parse_reader<'a>( &mut self, reader: &mut BinaryReader<'a>, @@ -638,12 +691,12 @@ impl Parser { State::Header => { let start = reader.original_position(); let header_version = reader.read_header_version()?; - self.encoding = match (header_version >> 16) as u16 { - KIND_MODULE => Encoding::Module, - KIND_COMPONENT => Encoding::Component, + let num = header_version as u16; + self.encoding = match (num, (header_version >> 16) as u16) { + (WASM_MODULE_VERSION, KIND_MODULE) => Encoding::Module, + (WASM_COMPONENT_VERSION, KIND_COMPONENT) => Encoding::Component, _ => bail!(start + 4, "unknown binary version: {header_version:#10x}"), }; - let num = header_version as u16; self.state = State::SectionStart; Ok(Version { num, @@ -656,6 +709,8 @@ impl Parser { // that means we reached the end of the data since it's // just a bunch of sections concatenated after the header. if eof && reader.bytes_remaining() == 0 { + self.check_function_code_counts(reader.original_position())?; + self.check_data_count(reader.original_position())?; return Ok(Payload::End(reader.original_position())); } @@ -688,36 +743,53 @@ impl Parser { // Module sections (Encoding::Module, TYPE_SECTION) => { + self.update_order(Order::Type, reader.original_position())?; section(reader, len, TypeSectionReader::new, TypeSection) } (Encoding::Module, IMPORT_SECTION) => { + self.update_order(Order::Import, reader.original_position())?; section(reader, len, ImportSectionReader::new, ImportSection) } (Encoding::Module, FUNCTION_SECTION) => { - section(reader, len, FunctionSectionReader::new, FunctionSection) + self.update_order(Order::Function, reader.original_position())?; + let s = section(reader, len, FunctionSectionReader::new, FunctionSection)?; + match &s { + FunctionSection(f) => self.counts.function_entries = Some(f.count()), + _ => unreachable!(), + } + Ok(s) } (Encoding::Module, TABLE_SECTION) => { + self.update_order(Order::Table, reader.original_position())?; section(reader, len, TableSectionReader::new, TableSection) } (Encoding::Module, MEMORY_SECTION) => { + self.update_order(Order::Memory, reader.original_position())?; section(reader, len, MemorySectionReader::new, MemorySection) } (Encoding::Module, GLOBAL_SECTION) => { + self.update_order(Order::Global, reader.original_position())?; section(reader, len, GlobalSectionReader::new, GlobalSection) } (Encoding::Module, EXPORT_SECTION) => { + self.update_order(Order::Export, reader.original_position())?; section(reader, len, ExportSectionReader::new, ExportSection) } (Encoding::Module, START_SECTION) => { + self.update_order(Order::Start, reader.original_position())?; let (func, range) = single_item(reader, len, "start")?; Ok(StartSection { func, range }) } (Encoding::Module, ELEMENT_SECTION) => { + self.update_order(Order::Element, reader.original_position())?; section(reader, len, ElementSectionReader::new, ElementSection) } (Encoding::Module, CODE_SECTION) => { + self.update_order(Order::Code, reader.original_position())?; let start = reader.original_position(); let count = delimited(reader, &mut len, |r| r.read_var_u32())?; + self.counts.code_entries = Some(count); + self.check_function_code_counts(start)?; let range = start..reader.original_position() + len as usize; self.state = State::FunctionBody { remaining: count, @@ -730,13 +802,23 @@ impl Parser { }) } (Encoding::Module, DATA_SECTION) => { - section(reader, len, DataSectionReader::new, DataSection) + self.update_order(Order::Data, reader.original_position())?; + let s = section(reader, len, DataSectionReader::new, DataSection)?; + match &s { + DataSection(d) => self.counts.data_entries = Some(d.count()), + _ => unreachable!(), + } + self.check_data_count(reader.original_position())?; + Ok(s) } (Encoding::Module, DATA_COUNT_SECTION) => { + self.update_order(Order::DataCount, reader.original_position())?; let (count, range) = single_item(reader, len, "data count")?; + self.counts.data_count = Some(count); Ok(DataCountSection { count, range }) } (Encoding::Module, TAG_SECTION) => { + self.update_order(Order::Tag, reader.original_position())?; section(reader, len, TagSectionReader::new, TagSection) } @@ -810,6 +892,15 @@ impl Parser { ), #[cfg(feature = "component-model")] (Encoding::Component, COMPONENT_START_SECTION) => { + match self.counts.component_start_sections { + false => self.counts.component_start_sections = true, + true => { + bail!( + reader.original_position(), + "component cannot have more than one start function" + ) + } + } let (start, range) = single_item(reader, len, "component start")?; Ok(ComponentStartSection { start, range }) } @@ -1082,6 +1173,35 @@ impl Parser { self.max_size -= u64::from(skip); self.state = State::SectionStart; } + + fn check_function_code_counts(&self, pos: usize) -> Result<()> { + match (self.counts.function_entries, self.counts.code_entries) { + (Some(n), Some(m)) if n != m => { + bail!(pos, "function and code section have inconsistent lengths") + } + (Some(n), None) if n > 0 => bail!( + pos, + "function section has non-zero count but code section is absent" + ), + (None, Some(m)) if m > 0 => bail!( + pos, + "function section is absent but code section has non-zero count" + ), + _ => Ok(()), + } + } + + fn check_data_count(&self, pos: usize) -> Result<()> { + match (self.counts.data_count, self.counts.data_entries) { + (Some(n), Some(m)) if n != m => { + bail!(pos, "data count and data section have inconsistent lengths") + } + (Some(n), None) if n > 0 => { + bail!(pos, "data count is non-zero but data section is absent") + } + _ => Ok(()), + } + } } fn usize_to_u64(a: usize) -> u64 { @@ -1593,6 +1713,13 @@ mod tests { }), ); let mut p = parser_after_header(); + assert_matches!( + p.parse(&[3, 2, 1, 0], false), + Ok(Chunk::Parsed { + consumed: 4, + payload: Payload::FunctionSection { .. }, + }), + ); assert_matches!( p.parse(&[10, 2, 1, 0], false), Ok(Chunk::Parsed { @@ -1611,13 +1738,20 @@ mod tests { p.parse(&[], true), Ok(Chunk::Parsed { consumed: 0, - payload: Payload::End(12), + payload: Payload::End(16), }), ); // 1 byte section with 1 function can't read the function body because // the section is too small let mut p = parser_after_header(); + assert_matches!( + p.parse(&[3, 2, 1, 0], false), + Ok(Chunk::Parsed { + consumed: 4, + payload: Payload::FunctionSection { .. }, + }), + ); assert_matches!( p.parse(&[10, 1, 1], false), Ok(Chunk::Parsed { @@ -1632,6 +1766,13 @@ mod tests { // section with 2 functions but section is cut off let mut p = parser_after_header(); + assert_matches!( + p.parse(&[3, 2, 2, 0], false), + Ok(Chunk::Parsed { + consumed: 4, + payload: Payload::FunctionSection { .. }, + }), + ); assert_matches!( p.parse(&[10, 2, 2], false), Ok(Chunk::Parsed { @@ -1654,6 +1795,13 @@ mod tests { // trailing data is bad let mut p = parser_after_header(); + assert_matches!( + p.parse(&[3, 2, 1, 0], false), + Ok(Chunk::Parsed { + consumed: 4, + payload: Payload::FunctionSection { .. }, + }), + ); assert_matches!( p.parse(&[10, 3, 1], false), Ok(Chunk::Parsed { diff --git a/crates/wasmparser/src/readers/core/code.rs b/crates/wasmparser/src/readers/core/code.rs index 73b1fd5afc..04176fbc63 100644 --- a/crates/wasmparser/src/readers/core/code.rs +++ b/crates/wasmparser/src/readers/core/code.rs @@ -48,8 +48,12 @@ impl<'a> FunctionBody<'a> { /// Gets the locals reader for this function body. pub fn get_locals_reader(&self) -> Result> { let mut reader = self.reader.clone(); - let count = reader.read_var_u32()?; - Ok(LocalsReader { reader, count }) + let declaration_count = reader.read_var_u32()?; + Ok(LocalsReader { + reader, + declaration_count, + total_count: 0, + }) } /// Gets the operators reader for this function body. @@ -75,20 +79,21 @@ impl<'a> FunctionBody<'a> { impl<'a> FromReader<'a> for FunctionBody<'a> { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { let reader = reader.read_reader()?; - Ok(FunctionBody { reader }) + Ok(FunctionBody::new(reader)) } } /// A reader for a function body's locals. pub struct LocalsReader<'a> { reader: BinaryReader<'a>, - count: u32, + declaration_count: u32, + total_count: u32, } impl<'a> LocalsReader<'a> { - /// Gets the count of locals in the function body. + /// Gets the count of locals declarations in the function body. pub fn get_count(&self) -> u32 { - self.count + self.declaration_count } /// Gets the original position of the reader. @@ -99,6 +104,10 @@ impl<'a> LocalsReader<'a> { /// Reads an item from the reader. pub fn read(&mut self) -> Result<(u32, ValType)> { let count = self.reader.read()?; + match self.total_count.checked_add(count) { + Some(total) => self.total_count = total, + None => bail!(self.reader.original_position(), "too many locals"), + } let value_type = self.reader.read()?; Ok((count, value_type)) } @@ -108,7 +117,7 @@ impl<'a> IntoIterator for LocalsReader<'a> { type Item = Result<(u32, ValType)>; type IntoIter = LocalsIterator<'a>; fn into_iter(self) -> Self::IntoIter { - let count = self.count; + let count = self.declaration_count; LocalsIterator { reader: self, left: count, diff --git a/crates/wasmparser/src/readers/core/elements.rs b/crates/wasmparser/src/readers/core/elements.rs index 85e2040ffc..aeb8637327 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, RefType, Result, - SectionLimited, + BinaryReader, BinaryReaderError, ConstExpr, ExternalKind, FromReader, OperatorsReader, RefType, + Result, SectionLimited, }; use core::ops::Range; @@ -122,7 +122,9 @@ impl<'a> FromReader<'a> for Element<'a> { let items_count = reader.read_var_u32()?; if exprs { for _ in 0..items_count { - reader.skip_const_expr()?; + let mut ops = OperatorsReader::new(reader.clone()); + ops.skip_const_expr()?; + *reader = ops.get_binary_reader(); } } else { for _ in 0..items_count { diff --git a/crates/wasmparser/src/readers/core/globals.rs b/crates/wasmparser/src/readers/core/globals.rs index 629f3f4d00..6413db8d21 100644 --- a/crates/wasmparser/src/readers/core/globals.rs +++ b/crates/wasmparser/src/readers/core/globals.rs @@ -39,18 +39,8 @@ impl<'a> FromReader<'a> for GlobalType { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { let content_type = reader.read()?; let flags = reader.read_u8()?; - if reader.shared_everything_threads() { - if flags > 0b11 { - bail!(reader.original_position() - 1, "malformed global flags") - } - } else { - if flags > 0b1 { - bail!( - reader.original_position() - 1, - "malformed mutability -- or shared globals \ - require the shared-everything-threads proposal" - ) - } + if flags > 0b11 { + bail!(reader.original_position() - 1, "malformed global flags") } Ok(GlobalType { content_type, diff --git a/crates/wasmparser/src/readers/core/init.rs b/crates/wasmparser/src/readers/core/init.rs index f14e31051a..d932fa7082 100644 --- a/crates/wasmparser/src/readers/core/init.rs +++ b/crates/wasmparser/src/readers/core/init.rs @@ -50,7 +50,12 @@ impl<'a> ConstExpr<'a> { impl<'a> FromReader<'a> for ConstExpr<'a> { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { // FIXME(#188) ideally shouldn't need to skip here - let reader = reader.skip(|r| r.skip_const_expr())?; + let reader = reader.skip(|reader| { + let mut ops = OperatorsReader::new(reader.clone()); + ops.skip_const_expr()?; + *reader = ops.get_binary_reader(); + Ok(()) + })?; Ok(ConstExpr { reader }) } } diff --git a/crates/wasmparser/src/readers/core/memories.rs b/crates/wasmparser/src/readers/core/memories.rs index e36eaa9770..b4c318a0c1 100644 --- a/crates/wasmparser/src/readers/core/memories.rs +++ b/crates/wasmparser/src/readers/core/memories.rs @@ -35,20 +35,24 @@ impl<'a> FromReader<'a> for MemoryType { Ok(MemoryType { memory64, shared, - initial: if memory64 { + initial: if reader.memory64() { reader.read_var_u64()? } else { reader.read_var_u32()?.into() }, maximum: if !has_max { None - } else if memory64 { + } else if reader.memory64() { Some(reader.read_var_u64()?) } else { Some(reader.read_var_u32()?.into()) }, page_size_log2: if has_page_size { - Some(reader.read_var_u32()?) + let val = reader.read_var_u32()?; + if val >= 64 { + bail!(pos, "invalid custom page size"); + } + Some(val) } else { None }, diff --git a/crates/wasmparser/src/readers/core/operators.rs b/crates/wasmparser/src/readers/core/operators.rs index 84c8ff840c..399fa19005 100644 --- a/crates/wasmparser/src/readers/core/operators.rs +++ b/crates/wasmparser/src/readers/core/operators.rs @@ -13,9 +13,10 @@ * limitations under the License. */ -use crate::limits::{MAX_WASM_CATCHES, MAX_WASM_HANDLERS}; +use crate::limits::{MAX_WASM_BR_TABLE_SIZE, MAX_WASM_CATCHES, MAX_WASM_HANDLERS}; use crate::prelude::*; -use crate::{BinaryReader, BinaryReaderError, FromReader, Result, ValType}; +use crate::{BinaryReader, BinaryReaderError, FromReader, RefType, Result, ValType}; +use core::fmt; /// Represents a block type. #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -111,6 +112,105 @@ impl PartialEq for BrTable<'_> { impl Eq for BrTable<'_> {} +impl<'a> BrTable<'a> { + /// Returns the number of `br_table` entries, not including the default + /// label + pub fn len(&self) -> u32 { + self.cnt + } + + /// Returns whether `BrTable` doesn't have any labels apart from the default one. + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Returns the default target of this `br_table` instruction. + pub fn default(&self) -> u32 { + self.default + } + + /// Returns the list of targets that this `br_table` instruction will be + /// jumping to. + /// + /// This method will return an iterator which parses each target of this + /// `br_table` except the default target. The returned iterator will + /// yield `self.len()` elements. + /// + /// # Examples + /// + /// ```rust + /// use wasmparser::{BinaryReader, OperatorsReader, Operator}; + /// + /// let buf = [0x0e, 0x02, 0x01, 0x02, 0x00]; + /// let mut reader = OperatorsReader::new(BinaryReader::new(&buf, 0)); + /// let op = reader.read().unwrap(); + /// if let Operator::BrTable { targets } = op { + /// let targets = targets.targets().collect::, _>>().unwrap(); + /// assert_eq!(targets, [1, 2]); + /// } + /// ``` + pub fn targets(&self) -> BrTableTargets { + BrTableTargets { + reader: self.reader.clone(), + remaining: self.cnt, + } + } +} + +/// An iterator over the targets of a [`BrTable`]. +/// +/// # Note +/// +/// This iterator parses each target of the underlying `br_table` +/// except for the default target. +/// The iterator will yield exactly as many targets as the `br_table` has. +pub struct BrTableTargets<'a> { + reader: crate::BinaryReader<'a>, + remaining: u32, +} + +impl<'a> Iterator for BrTableTargets<'a> { + type Item = Result; + + fn size_hint(&self) -> (usize, Option) { + let remaining = usize::try_from(self.remaining).unwrap_or_else(|error| { + panic!("could not convert remaining `u32` into `usize`: {}", error) + }); + (remaining, Some(remaining)) + } + + fn next(&mut self) -> Option { + if self.remaining == 0 { + if !self.reader.eof() { + return Some(Err(BinaryReaderError::new( + "trailing data in br_table", + self.reader.original_position(), + ))); + } + return None; + } + self.remaining -= 1; + Some(self.reader.read_var_u32()) + } +} + +impl fmt::Debug for BrTable<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut f = f.debug_struct("BrTable"); + f.field("count", &self.cnt); + f.field("default", &self.default); + match self.targets().collect::>>() { + Ok(targets) => { + f.field("targets", &targets); + } + Err(_) => { + f.field("reader", &self.reader); + } + } + f.finish() + } +} + /// An IEEE binary32 immediate floating point value, represented as a u32 /// containing the bit pattern. /// @@ -234,11 +334,18 @@ crate::for_each_operator!(define_operator); #[derive(Clone)] pub struct OperatorsReader<'a> { reader: BinaryReader<'a>, + blocks: Vec, + data_index_occurred: Option, } impl<'a> OperatorsReader<'a> { - pub(crate) fn new(reader: BinaryReader<'a>) -> OperatorsReader<'a> { - OperatorsReader { reader } + /// Creates a new reader for an expression (instruction sequence) + pub fn new(reader: BinaryReader<'a>) -> OperatorsReader<'a> { + OperatorsReader { + reader, + blocks: vec![FrameKind::Block], + data_index_occurred: None, + } } /// Determines if the reader is at the end of the operators. @@ -251,22 +358,40 @@ impl<'a> OperatorsReader<'a> { self.reader.original_position() } - /// Ensures the reader is at the end. + /// 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. - pub fn ensure_end(&self) -> Result<()> { - if self.eof() { - return Ok(()); + /// 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(()) + } + + 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" + ); } - Err(BinaryReaderError::new( - "unexpected data at the end of operators", - self.reader.original_position(), - )) + Ok(()) } - /// Reads an operator from the reader. + /// Reads the next available `Operator`. + /// + /// # Errors + /// + /// If `OperatorsReader` has less bytes remaining than required to parse + /// the `Operator`. pub fn read(&mut self) -> Result> { - self.reader.read_operator() + self.visit_operator(&mut OperatorFactory::new()) } /// Converts to an iterator of operators paired with offsets. @@ -283,14 +408,1168 @@ impl<'a> OperatorsReader<'a> { Ok((self.read()?, pos)) } - /// Visit a single operator with the specified [`VisitOperator`] instance. + fn enter(&mut self, k: FrameKind) { + self.blocks.push(k) + } + + fn expect_block(&mut self, k: FrameKind, found: &str) -> Result<()> { + match self.blocks.last() { + None => bail!( + self.original_position(), + "empty stack found where {:?} expected", + k + ), + Some(x) if *x == k => Ok(()), + Some(_) => bail!( + self.original_position(), + "`{}` found outside `{:?}` block", + found, + k + ), + } + } + + fn end(&mut self) -> Result<()> { + assert!(!self.blocks.is_empty()); + self.blocks.pop(); + Ok(()) + } + + /// Visit the next available operator with the specified [`VisitOperator`] instance. + /// + /// Note that this does not implicitly propagate any additional information such as instruction + /// offsets. In order to do so, consider storing such data within the visitor before visiting. + /// + /// # Errors + /// + /// If `OperatorsReader` has less bytes remaining than required to parse the `Operator`. + /// + /// # Examples + /// + /// Store an offset for use in diagnostics or any other purposes: /// - /// See [`BinaryReader::visit_operator`] for more information. + /// ``` + /// # use wasmparser::{OperatorsReader, VisitOperator, Result, for_each_visit_operator}; + /// + /// pub fn dump(mut reader: OperatorsReader) -> Result<()> { + /// let mut visitor = Dumper { offset: 0 }; + /// while !reader.eof() { + /// visitor.offset = reader.original_position(); + /// reader.visit_operator(&mut visitor)?; + /// } + /// Ok(()) + /// } + /// + /// struct Dumper { + /// offset: usize + /// } + /// + /// macro_rules! define_visit_operator { + /// ($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*))*) => { + /// $( + /// fn $visit(&mut self $($(,$arg: $argty)*)?) -> Self::Output { + /// println!("{}: {}", self.offset, stringify!($visit)); + /// } + /// )* + /// } + /// } + /// + /// impl<'a> VisitOperator<'a> for Dumper { + /// type Output = (); + /// for_each_visit_operator!(define_visit_operator); + /// } + /// + /// ``` pub fn visit_operator(&mut self, visitor: &mut T) -> Result<>::Output> where T: VisitOperator<'a>, { - self.reader.visit_operator(visitor) + 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_table_index_or_zero_if_not_reference_types()?; + 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 results = self.reader.read_var_u32()?; + if results != 1 { + bail!(pos, "invalid result arity"); + } + visitor.visit_typed_select(self.reader.read()?) + } + 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 => { + self.data_index_occurred + .get_or_insert(self.original_position()); + 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 => { + self.data_index_occurred + .get_or_insert(self.original_position()); + let type_index = self.reader.read_var_u32()?; + let data_index = self.reader.read_var_u32()?; + visitor.visit_array_init_data(type_index, data_index) + } + 0x13 => { + let type_index = self.reader.read_var_u32()?; + let elem_index = self.reader.read_var_u32()?; + visitor.visit_array_init_elem(type_index, elem_index) + } + 0x14 => visitor.visit_ref_test_non_null(self.reader.read()?), + 0x15 => visitor.visit_ref_test_nullable(self.reader.read()?), + 0x16 => visitor.visit_ref_cast_non_null(self.reader.read()?), + 0x17 => visitor.visit_ref_cast_nullable(self.reader.read()?), + 0x18 => { + let pos = self.original_position(); + let cast_flags = self.reader.read_u8()?; + let relative_depth = self.reader.read_var_u32()?; + let (from_type_nullable, to_type_nullable) = match cast_flags { + 0b00 => (false, false), + 0b01 => (true, false), + 0b10 => (false, true), + 0b11 => (true, true), + _ => bail!(pos, "invalid cast flags: {cast_flags:08b}"), + }; + let from_heap_type = self.reader.read()?; + let from_ref_type = + RefType::new(from_type_nullable, from_heap_type).ok_or_else(|| { + format_err!(pos, "implementation error: type index too large") + })?; + let to_heap_type = self.reader.read()?; + let to_ref_type = + RefType::new(to_type_nullable, to_heap_type).ok_or_else(|| { + format_err!(pos, "implementation error: type index too large") + })?; + visitor.visit_br_on_cast(relative_depth, from_ref_type, to_ref_type) + } + 0x19 => { + let pos = self.original_position(); + let cast_flags = self.reader.read_u8()?; + let relative_depth = self.reader.read_var_u32()?; + let (from_type_nullable, to_type_nullable) = match cast_flags { + 0 => (false, false), + 1 => (true, false), + 2 => (false, true), + 3 => (true, true), + _ => bail!(pos, "invalid cast flags: {cast_flags:08b}"), + }; + let from_heap_type = self.reader.read()?; + let from_ref_type = + RefType::new(from_type_nullable, from_heap_type).ok_or_else(|| { + format_err!(pos, "implementation error: type index too large") + })?; + let to_heap_type = self.reader.read()?; + let to_ref_type = + RefType::new(to_type_nullable, to_heap_type).ok_or_else(|| { + format_err!(pos, "implementation error: type index too large") + })?; + visitor.visit_br_on_cast_fail(relative_depth, from_ref_type, to_ref_type) + } + + 0x1a => visitor.visit_any_convert_extern(), + 0x1b => visitor.visit_extern_convert_any(), + + 0x1c => visitor.visit_ref_i31(), + 0x1d => visitor.visit_i31_get_s(), + 0x1e => visitor.visit_i31_get_u(), + + _ => bail!(pos, "unknown 0xfb subopcode: 0x{code:x}"), + }) + } + + fn visit_0xfc_operator( + &mut self, + pos: usize, + visitor: &mut T, + ) -> Result<>::Output> + where + T: VisitOperator<'a>, + { + let code = self.reader.read_var_u32()?; + Ok(match code { + 0x00 => visitor.visit_i32_trunc_sat_f32_s(), + 0x01 => visitor.visit_i32_trunc_sat_f32_u(), + 0x02 => visitor.visit_i32_trunc_sat_f64_s(), + 0x03 => visitor.visit_i32_trunc_sat_f64_u(), + 0x04 => visitor.visit_i64_trunc_sat_f32_s(), + 0x05 => visitor.visit_i64_trunc_sat_f32_u(), + 0x06 => visitor.visit_i64_trunc_sat_f64_s(), + 0x07 => visitor.visit_i64_trunc_sat_f64_u(), + + 0x08 => { + self.data_index_occurred + .get_or_insert(self.original_position()); + let segment = self.reader.read_var_u32()?; + let mem = self.reader.read_var_u32()?; + visitor.visit_memory_init(segment, mem) + } + 0x09 => { + self.data_index_occurred + .get_or_insert(self.original_position()); + 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}"), + }) + } + + 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}"), + }) + } + + 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()?; + return Ok(()); + } + } } /// Gets a binary reader from this operators reader. @@ -303,6 +1582,103 @@ impl<'a> OperatorsReader<'a> { 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"), + } + } + } + + fn read_table_index_or_zero_if_not_reference_types(&mut self) -> Result { + if self.reader.reference_types() { + self.reader.read_var_u32() + } else { + // 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> IntoIterator for OperatorsReader<'a> { @@ -650,3 +2026,43 @@ 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, + } + } +} + +macro_rules! define_visit_operator { + ($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*))*) => { + $( + fn $visit(&mut self $($(,$arg: $argty)*)?) -> Operator<'a> { + Operator::$op $({ $($arg),* })? + } + )* + } +} + +impl<'a> VisitOperator<'a> for OperatorFactory<'a> { + type Output = Operator<'a>; + + #[cfg(feature = "simd")] + fn simd_visitor(&mut self) -> Option<&mut dyn VisitSimdOperator<'a, Output = Self::Output>> { + Some(self) + } + + crate::for_each_visit_operator!(define_visit_operator); +} + +#[cfg(feature = "simd")] +impl<'a> VisitSimdOperator<'a> for OperatorFactory<'a> { + crate::for_each_visit_simd_operator!(define_visit_operator); +} diff --git a/crates/wasmparser/src/readers/core/tables.rs b/crates/wasmparser/src/readers/core/tables.rs index 7df2a76707..8093578982 100644 --- a/crates/wasmparser/src/readers/core/tables.rs +++ b/crates/wasmparser/src/readers/core/tables.rs @@ -74,17 +74,18 @@ impl<'a> FromReader<'a> for TableType { let has_max = (flags & 0b001) != 0; let shared = (flags & 0b010) != 0; let table64 = (flags & 0b100) != 0; + Ok(TableType { element_type, table64, - initial: if table64 { + initial: if reader.memory64() { reader.read_var_u64()? } else { reader.read_var_u32()?.into() }, maximum: if !has_max { None - } else if table64 { + } else if reader.memory64() { Some(reader.read_var_u64()?) } else { Some(reader.read_var_u32()?.into()) diff --git a/crates/wasmparser/src/validator.rs b/crates/wasmparser/src/validator.rs index 46b218a9df..dc41e60aec 100644 --- a/crates/wasmparser/src/validator.rs +++ b/crates/wasmparser/src/validator.rs @@ -580,10 +580,10 @@ impl Validator { ElementSection(s) => self.element_section(s)?, DataCountSection { count, range } => self.data_count_section(*count, range)?, CodeSectionStart { - count, + count: _, range, size: _, - } => self.code_section_start(*count, range)?, + } => self.code_section_start(range)?, CodeSectionEntry(body) => { let func_validator = self.code_section_entry(body)?; return Ok(ValidPayload::Func(func_validator, body.clone())); @@ -705,7 +705,6 @@ impl Validator { /// Validates [`Payload::TypeSection`](crate::Payload). pub fn type_section(&mut self, section: &crate::TypeSectionReader<'_>) -> Result<()> { self.process_module_section( - Order::Type, section, "type", |state, _types, count, offset| { @@ -734,7 +733,6 @@ impl Validator { /// This method should only be called when parsing a module. pub fn import_section(&mut self, section: &crate::ImportSectionReader<'_>) -> Result<()> { self.process_module_section( - Order::Import, section, "import", |state, _, count, offset| { @@ -759,7 +757,6 @@ impl Validator { /// This method should only be called when parsing a module. pub fn function_section(&mut self, section: &crate::FunctionSectionReader<'_>) -> Result<()> { self.process_module_section( - Order::Function, section, "function", |state, _, count, offset| { @@ -771,8 +768,6 @@ impl Validator { offset, )?; state.module.assert_mut().functions.reserve(count as usize); - debug_assert!(state.expected_code_bodies.is_none()); - state.expected_code_bodies = Some(count); Ok(()) }, |state, types, ty, offset| state.module.assert_mut().add_function(ty, types, offset), @@ -784,7 +779,6 @@ impl Validator { /// This method should only be called when parsing a module. pub fn table_section(&mut self, section: &crate::TableSectionReader<'_>) -> Result<()> { self.process_module_section( - Order::Table, section, "table", |state, _, count, offset| { @@ -807,7 +801,6 @@ impl Validator { /// This method should only be called when parsing a module. pub fn memory_section(&mut self, section: &crate::MemorySectionReader<'_>) -> Result<()> { self.process_module_section( - Order::Memory, section, "memory", |state, _, count, offset| { @@ -835,9 +828,7 @@ impl Validator { section.range().start, )); } - self.process_module_section( - Order::Tag, section, "tag", |state, _, count, offset| { @@ -860,7 +851,6 @@ impl Validator { /// This method should only be called when parsing a module. pub fn global_section(&mut self, section: &crate::GlobalSectionReader<'_>) -> Result<()> { self.process_module_section( - Order::Global, section, "global", |state, _, count, offset| { @@ -883,7 +873,6 @@ impl Validator { /// This method should only be called when parsing a module. pub fn export_section(&mut self, section: &crate::ExportSectionReader<'_>) -> Result<()> { self.process_module_section( - Order::Export, section, "export", |state, _, count, offset| { @@ -912,7 +901,6 @@ impl Validator { let offset = range.start; self.state.ensure_module("start", offset)?; let state = self.module.as_mut().unwrap(); - state.update_order(Order::Start, offset)?; let ty = state.module.get_func_type(func, &self.types, offset)?; if !ty.params().is_empty() || !ty.results().is_empty() { @@ -930,7 +918,6 @@ impl Validator { /// This method should only be called when parsing a module. pub fn element_section(&mut self, section: &crate::ElementSectionReader<'_>) -> Result<()> { self.process_module_section( - Order::Element, section, "element", |state, _, count, offset| { @@ -960,7 +947,6 @@ impl Validator { self.state.ensure_module("data count", offset)?; let state = self.module.as_mut().unwrap(); - state.update_order(Order::DataCount, offset)?; if count > MAX_WASM_DATA_SEGMENTS as u32 { return Err(BinaryReaderError::new( @@ -976,31 +962,11 @@ impl Validator { /// Validates [`Payload::CodeSectionStart`](crate::Payload). /// /// This method should only be called when parsing a module. - pub fn code_section_start(&mut self, count: u32, range: &Range) -> Result<()> { + pub fn code_section_start(&mut self, range: &Range) -> Result<()> { let offset = range.start; self.state.ensure_module("code", offset)?; let state = self.module.as_mut().unwrap(); - state.update_order(Order::Code, offset)?; - - match state.expected_code_bodies.take() { - Some(n) if n == count => {} - Some(_) => { - return Err(BinaryReaderError::new( - "function and code section have inconsistent lengths", - offset, - )); - } - // empty code sections are allowed even if the function section is - // missing - None if count == 0 => {} - None => { - return Err(BinaryReaderError::new( - "code section without function section", - offset, - )) - } - } // Take a snapshot of the types when we start the code section. state.module.assert_mut().snapshot = Some(Arc::new(self.types.commit())); @@ -1030,7 +996,7 @@ impl Validator { let state = self.module.as_mut().unwrap(); - let (index, ty) = state.next_code_index_and_type(offset)?; + let (index, ty) = state.next_code_index_and_type(); Ok(FuncToValidate { index, ty, @@ -1044,11 +1010,9 @@ impl Validator { /// This method should only be called when parsing a module. pub fn data_section(&mut self, section: &crate::DataSectionReader<'_>) -> Result<()> { self.process_module_section( - Order::Data, section, "data", - |state, _, count, offset| { - state.data_segment_count = count; + |_, _, count, offset| { check_max(0, count, MAX_WASM_DATA_SEGMENTS, "data segments", offset) }, |state, types, d, offset| state.add_data_segment(d, types, offset), @@ -1359,7 +1323,6 @@ impl Validator { )), State::Module => { let mut state = self.module.take().unwrap(); - state.validate_end(offset)?; // If there's a parent component, we'll add a module to the parent state // and continue to validate the component @@ -1407,7 +1370,6 @@ impl Validator { fn process_module_section<'a, T>( &mut self, - order: Order, section: &SectionLimited<'a, T>, name: &str, validate_section: impl FnOnce(&mut ModuleState, &mut TypeAlloc, u32, usize) -> Result<()>, @@ -1420,7 +1382,6 @@ impl Validator { self.state.ensure_module(name, offset)?; let state = self.module.as_mut().unwrap(); - state.update_order(order, offset)?; validate_section(state, &mut self.types, section.count(), offset)?; @@ -1456,13 +1417,6 @@ impl Validator { { let offset = section.range().start; - if !self.features.component_model() { - return Err(BinaryReaderError::new( - "component model feature is not enabled", - offset, - )); - } - self.state.ensure_component(name, offset)?; validate_section( &mut self.components, diff --git a/crates/wasmparser/src/validator/core.rs b/crates/wasmparser/src/validator/core.rs index 87edbe9191..812f0d6d76 100644 --- a/crates/wasmparser/src/validator/core.rs +++ b/crates/wasmparser/src/validator/core.rs @@ -22,29 +22,6 @@ use crate::{prelude::*, CompositeInnerType}; use alloc::sync::Arc; use core::mem; -// Section order for WebAssembly modules. -// -// Component sections are unordered and allow for duplicates, -// so this isn't used for components. -#[derive(Copy, Clone, Default, PartialOrd, Ord, PartialEq, Eq, Debug)] -pub enum Order { - #[default] - Initial, - Type, - Import, - Function, - Table, - Memory, - Tag, - Global, - Export, - Start, - Element, - DataCount, - Code, - Data, -} - pub(crate) struct ModuleState { /// Internal state that is incrementally built-up for the module being /// validated. This houses type information for all wasm items, like @@ -53,19 +30,6 @@ pub(crate) struct ModuleState { /// mutated to we can clone it cheaply and hand it to sub-validators. pub module: arc::MaybeOwned, - /// Where we are, order-wise, in the wasm binary. - order: Order, - - /// The number of data segments in the data section (if present). - pub data_segment_count: u32, - - /// The number of functions we expect to be defined in the code section, or - /// basically the length of the function section if it was found. The next - /// index is where we are, in the code section index space, for the next - /// entry in the code section (used to figure out what type is next for the - /// function being validated). - pub expected_code_bodies: Option, - const_expr_allocs: OperatorValidatorAllocations, /// When parsing the code section, represents the current index in the section. @@ -76,64 +40,22 @@ impl ModuleState { pub fn new(features: WasmFeatures) -> ModuleState { ModuleState { module: arc::MaybeOwned::new(Module::new(features)), - order: Order::default(), - data_segment_count: 0, - expected_code_bodies: None, const_expr_allocs: OperatorValidatorAllocations::default(), code_section_index: None, } } - pub fn update_order(&mut self, order: Order, offset: usize) -> Result<()> { - if self.order >= order { - return Err(BinaryReaderError::new("section out of order", offset)); - } - - self.order = order; - - Ok(()) - } - - pub fn validate_end(&self, offset: usize) -> Result<()> { - // Ensure that the data count section, if any, was correct. - if let Some(data_count) = self.module.data_count { - if data_count != self.data_segment_count { - return Err(BinaryReaderError::new( - "data count and data section have inconsistent lengths", - offset, - )); - } - } - // Ensure that the function section, if nonzero, was paired with a code - // section with the appropriate length. - if let Some(n) = self.expected_code_bodies { - if n > 0 { - return Err(BinaryReaderError::new( - "function and code section have inconsistent lengths", - offset, - )); - } - } - - Ok(()) - } - - pub fn next_code_index_and_type(&mut self, offset: usize) -> Result<(u32, u32)> { + pub fn next_code_index_and_type(&mut self) -> (u32, u32) { let index = self .code_section_index .get_or_insert(self.module.num_imported_functions as usize); - if *index >= self.module.functions.len() { - return Err(BinaryReaderError::new( - "code section entry exceeds number of functions", - offset, - )); - } + assert!(*index < self.module.functions.len()); let ty = self.module.functions[*index]; *index += 1; - Ok(((*index - 1) as u32, ty)) + ((*index - 1) as u32, ty) } pub fn add_global( @@ -275,7 +197,6 @@ impl ModuleState { ) -> Result<()> { let mut validator = VisitConstOperator { offset: 0, - order: self.order, uninserted_funcref: false, ops: OperatorValidator::new_const_expr( &self.module.features, @@ -293,7 +214,6 @@ impl ModuleState { validator.offset = ops.original_position(); ops.visit_operator(&mut validator)??; } - validator.ops.finish(ops.original_position())?; // See comment in `RefFunc` below for why this is an assert. assert!(!validator.uninserted_funcref); @@ -307,7 +227,6 @@ impl ModuleState { uninserted_funcref: bool, ops: OperatorValidator, resources: OperatorValidatorResources<'a>, - order: Order, } impl VisitConstOperator<'_> { @@ -394,7 +313,7 @@ impl ModuleState { // (aka a fuzz bug) if we somehow forget to emit an error somewhere // else. fn insert_ref_func(&mut self, index: u32) { - if self.order == Order::Data { + if self.resources.module.as_mut().is_none() { self.uninserted_funcref = true; } else { self.resources @@ -769,43 +688,53 @@ impl Module { self.check_ref_type(&mut ty.element_type, offset)? } - if ty.table64 && !self.features.memory64() { - return Err(BinaryReaderError::new( - "memory64 must be enabled for 64-bit tables", + self.check_limits(ty.initial, ty.maximum, offset)?; + if ty.table64 && !self.features().memory64() { + bail!(offset, "memory64 must be enabled for 64-bit tables"); + } + if ty.shared && !self.features().shared_everything_threads() { + bail!( offset, - )); + "shared tables require the shared-everything-threads proposal" + ); } - self.check_limits(ty.initial, ty.maximum, offset)?; - - if ty.shared { - if !self.features.shared_everything_threads() { - return Err(BinaryReaderError::new( - "shared tables require the shared-everything-threads proposal", - offset, - )); - } - - if !types.reftype_is_shared(ty.element_type) { - return Err(BinaryReaderError::new( - "shared tables must have a shared element type", - offset, - )); + let true_maximum = if ty.table64 { + u64::MAX + } else { + u32::MAX as u64 + }; + let err = format!("table size must be at most {true_maximum:#x} entries"); + if ty.initial > true_maximum { + return Err(BinaryReaderError::new(err, offset)); + } + if let Some(maximum) = ty.maximum { + if maximum > true_maximum { + return Err(BinaryReaderError::new(err, offset)); } } - + if ty.shared && !types.reftype_is_shared(ty.element_type) { + return Err(BinaryReaderError::new( + "shared tables must have a shared element type", + offset, + )); + } Ok(()) } fn check_memory_type(&self, ty: &MemoryType, offset: usize) -> Result<()> { self.check_limits(ty.initial, ty.maximum, offset)?; - let (page_size, page_size_log2) = if let Some(page_size_log2) = ty.page_size_log2 { - if !self.features.custom_page_sizes() { - return Err(BinaryReaderError::new( - "the custom page sizes proposal must be enabled to \ - customize a memory's page size", - offset, - )); + + if ty.memory64 && !self.features().memory64() { + bail!(offset, "memory64 must be enabled for 64-bit memories"); + } + if ty.shared && !self.features().threads() { + bail!(offset, "threads must be enabled for shared memories"); + } + + let page_size = if let Some(page_size_log2) = ty.page_size_log2 { + if !self.features().custom_page_sizes() { + return Err(BinaryReaderError::new("the custom page sizes proposal must be enabled to customize a memory's page size", offset)); } // Currently 2**0 and 2**16 are the only valid page sizes, but this // may be relaxed to allow any power of two in the future. @@ -815,33 +744,18 @@ impl Module { let page_size = 1_u64 << page_size_log2; debug_assert!(page_size.is_power_of_two()); debug_assert!(page_size == DEFAULT_WASM_PAGE_SIZE || page_size == 1); - (page_size, page_size_log2) + page_size } else { let page_size_log2 = 16; debug_assert_eq!(DEFAULT_WASM_PAGE_SIZE, 1 << page_size_log2); - (DEFAULT_WASM_PAGE_SIZE, page_size_log2) + DEFAULT_WASM_PAGE_SIZE }; - let (true_maximum, err) = if ty.memory64 { - if !self.features.memory64() { - return Err(BinaryReaderError::new( - "memory64 must be enabled for 64-bit memories", - offset, - )); - } - ( - max_wasm_memory64_pages(page_size), - format!( - "memory size must be at most 2**{} pages", - 64 - page_size_log2 - ), - ) + let true_maximum = if ty.memory64 { + max_wasm_memory64_pages(page_size) } else { - let max = max_wasm_memory32_pages(page_size); - ( - max, - format!("memory size must be at most {max} pages (4GiB)"), - ) + max_wasm_memory32_pages(page_size) }; + let err = format!("memory size must be at most {true_maximum:#x} {page_size}-byte pages"); if ty.initial > true_maximum { return Err(BinaryReaderError::new(err, offset)); } @@ -850,19 +764,11 @@ impl Module { return Err(BinaryReaderError::new(err, offset)); } } - if ty.shared { - if !self.features.threads() { - return Err(BinaryReaderError::new( - "threads must be enabled for shared memories", - offset, - )); - } - if ty.maximum.is_none() { - return Err(BinaryReaderError::new( - "shared memory must have maximum size", - offset, - )); - } + if ty.shared && ty.maximum.is_none() { + return Err(BinaryReaderError::new( + "shared memory must have maximum size", + offset, + )); } Ok(()) } @@ -931,11 +837,8 @@ impl Module { } fn check_tag_type(&self, ty: &TagType, types: &TypeList, offset: usize) -> Result<()> { - if !self.features.exceptions() { - return Err(BinaryReaderError::new( - "exceptions proposal not enabled", - offset, - )); + if !self.features().exceptions() { + bail!(offset, "exceptions proposal not enabled"); } let ty = self.func_type_at(ty.func_type_idx, types, offset)?; if !ty.results().is_empty() && !self.features.stack_switching() { @@ -954,19 +857,17 @@ impl Module { offset: usize, ) -> Result<()> { self.check_value_type(&mut ty.content_type, offset)?; - if ty.shared { - if !self.features.shared_everything_threads() { - return Err(BinaryReaderError::new( - "shared globals require the shared-everything-threads proposal", - offset, - )); - } - if !types.valtype_is_shared(ty.content_type) { - return Err(BinaryReaderError::new( - "shared globals must have a shared value type", - offset, - )); - } + if ty.shared && !self.features.shared_everything_threads() { + bail!( + offset, + "shared globals require the shared-everything-threads proposal" + ); + } + if ty.shared && !types.valtype_is_shared(ty.content_type) { + return Err(BinaryReaderError::new( + "shared globals must have a shared value type", + offset, + )); } Ok(()) } @@ -1297,7 +1198,7 @@ mod arc { } #[inline] - fn as_mut(&mut self) -> Option<&mut T> { + pub(crate) fn as_mut(&mut self) -> Option<&mut T> { match &mut self.inner { Inner::Owned(x) => Some(x), Inner::Shared(_) => None, diff --git a/crates/wasmparser/src/validator/func.rs b/crates/wasmparser/src/validator/func.rs index 90b5a50b07..f102dcb48a 100644 --- a/crates/wasmparser/src/validator/func.rs +++ b/crates/wasmparser/src/validator/func.rs @@ -1,5 +1,5 @@ use super::operators::{Frame, OperatorValidator, OperatorValidatorAllocations}; -use crate::{BinaryReader, Result, ValType, VisitOperator}; +use crate::{BinaryReader, OperatorsReader, Result, ValType, VisitOperator}; use crate::{FunctionBody, ModuleArity, Operator, WasmFeatures, WasmModuleResources}; /// Resources necessary to perform validation of a function. @@ -84,24 +84,24 @@ impl FuncValidator { { reader.set_features(self.validator.features); } - while !reader.eof() { + let mut ops = OperatorsReader::new(reader); + while !ops.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 (pop_push_snapshot, arity) = ( self.validator.pop_push_count, - reader - .clone() - .read_operator()? - .operator_arity(&self.visitor(reader.original_position())), + ops.clone() + .read()? + .operator_arity(&self.visitor(ops.original_position())), ); - reader.visit_operator(&mut self.visitor(reader.original_position()))??; + ops.visit_operator(&mut self.visitor(ops.original_position()))??; #[cfg(debug_assertions)] { let (params, results) = arity.ok_or(format_err!( - reader.original_position(), + ops.original_position(), "could not calculate operator arity" ))?; @@ -114,7 +114,7 @@ impl FuncValidator { } } } - self.finish(reader.original_position()) + ops.finish() } /// Reads the local definitions from the given `BinaryReader`, often sourced @@ -162,12 +162,12 @@ impl FuncValidator { /// pub fn validate(validator: &mut FuncValidator, body: &FunctionBody<'_>) -> Result<()> /// where R: WasmModuleResources /// { - /// let mut operator_reader = body.get_binary_reader(); + /// let mut operator_reader = body.get_operators_reader()?; /// while !operator_reader.eof() { /// let mut visitor = validator.visitor(operator_reader.original_position()); /// operator_reader.visit_operator(&mut visitor)??; /// } - /// validator.finish(operator_reader.original_position()) + /// operator_reader.finish() /// } /// ``` pub fn visitor<'this, 'a: 'this>( @@ -188,18 +188,6 @@ impl FuncValidator { self.validator.with_resources_simd(&self.resources, offset) } - /// Function that must be called after the last opcode has been processed. - /// - /// This will validate that the function was properly terminated with the - /// `end` opcode. If this function is not called then the function will not - /// be properly validated. - /// - /// The `offset` provided to this function will be used as a position for an - /// error if validation fails. - pub fn finish(&mut self, offset: usize) -> Result<()> { - self.validator.finish(offset) - } - /// Returns the Wasm features enabled for this validator. pub fn features(&self) -> &WasmFeatures { &self.validator.features diff --git a/crates/wasmparser/src/validator/operators.rs b/crates/wasmparser/src/validator/operators.rs index 249bc070d9..4dbc1298e1 100644 --- a/crates/wasmparser/src/validator/operators.rs +++ b/crates/wasmparser/src/validator/operators.rs @@ -52,10 +52,6 @@ pub(crate) struct OperatorValidator { /// The `operands` is the current type stack. operands: Vec, - /// Offset of the `end` instruction which emptied the `control` stack, which - /// must be the end of the function. - end_which_emptied_control: Option, - /// Whether validation is happening in a shared context. shared: bool, @@ -347,7 +343,6 @@ impl OperatorValidator { popped_types_tmp, operands, control, - end_which_emptied_control: None, shared: false, #[cfg(debug_assertions)] pop_push_count: (0, 0), @@ -512,28 +507,6 @@ impl OperatorValidator { }) } - pub fn finish(&mut self, offset: usize) -> Result<()> { - if self.control.last().is_some() { - bail!( - offset, - "control frames remain at end of function: END opcode expected" - ); - } - - // The `end` opcode is one byte which means that the `offset` here - // should point just beyond the `end` opcode which emptied the control - // stack. If not that means more instructions were present after the - // control stack was emptied. - if offset != self.end_which_emptied_control.unwrap() + 1 { - return Err(self.err_beyond_end(offset)); - } - Ok(()) - } - - fn err_beyond_end(&self, offset: usize) -> BinaryReaderError { - format_err!(offset, "operators remaining after end of function") - } - pub fn into_allocations(mut self) -> OperatorValidatorAllocations { fn clear(mut tmp: Vec) -> Vec { tmp.clear(); @@ -717,10 +690,7 @@ where popped: Option, ) -> Result { self.operands.extend(popped); - let control = match self.control.last() { - Some(c) => c, - None => return Err(self.err_beyond_end(self.offset)), - }; + let control = self.control.last().unwrap(); let actual = if self.operands.len() == control.height && control.unreachable { MaybeType::Bottom } else { @@ -914,10 +884,7 @@ where /// Flags the current control frame as unreachable, additionally truncating /// the currently active operand stack. fn unreachable(&mut self) -> Result<()> { - let control = match self.control.last_mut() { - Some(frame) => frame, - None => return Err(self.err_beyond_end(self.offset)), - }; + let control = self.control.last_mut().unwrap(); control.unreachable = true; let new_height = control.height; self.operands.truncate(new_height); @@ -957,10 +924,7 @@ where fn pop_ctrl(&mut self) -> Result { // Read the expected type and expected height of the operand stack the // end of the frame. - let frame = match self.control.last() { - Some(f) => f, - None => return Err(self.err_beyond_end(self.offset)), - }; + let frame = self.control.last().unwrap(); let ty = frame.block_type; let height = frame.height; let init_height = frame.init_height; @@ -992,9 +956,7 @@ where /// Returns the type signature of the block that we're jumping to as well /// as the kind of block if the jump is valid. Otherwise returns an error. fn jump(&self, depth: u32) -> Result<(BlockType, FrameKind)> { - if self.control.is_empty() { - return Err(self.err_beyond_end(self.offset)); - } + assert!(!self.control.is_empty()); match (self.control.len() - 1).checked_sub(depth as usize) { Some(i) => { let frame = &self.control[i]; @@ -1168,9 +1130,7 @@ where /// Validates a `return` instruction, popping types from the operand /// stack that the function needs. fn check_return(&mut self) -> Result<()> { - if self.control.is_empty() { - return Err(self.err_beyond_end(self.offset)); - } + assert!(!self.control.is_empty()); for ty in self.results(self.control[0].block_type)?.rev() { self.pop_operand(Some(ty))?; } @@ -1181,9 +1141,7 @@ where /// Check that the given type has the same result types as the current /// function's results. fn check_func_type_same_results(&self, callee_ty: &FuncType) -> Result<()> { - if self.control.is_empty() { - return Err(self.err_beyond_end(self.offset)); - } + assert!(!self.control.is_empty()); let caller_rets = self.results(self.control[0].block_type)?; if callee_ty.results().len() != caller_rets.len() || !caller_rets @@ -1964,10 +1922,8 @@ where for ty in self.results(frame.block_type)? { self.push_operand(ty)?; } - - if self.control.is_empty() && self.end_which_emptied_control.is_none() { + if self.control.is_empty() { assert_ne!(self.offset, 0); - self.end_which_emptied_control = Some(self.offset); } Ok(()) } diff --git a/crates/wasmprinter/src/lib.rs b/crates/wasmprinter/src/lib.rs index cc0aa35de3..1259ba2477 100644 --- a/crates/wasmprinter/src/lib.rs +++ b/crates/wasmprinter/src/lib.rs @@ -1291,9 +1291,7 @@ impl Printer<'_, '_> { state: &mut State, body: &FunctionBody<'_>, ) -> Result<()> { - let mut body = body.get_binary_reader(); - let offset = body.original_position(); - self.newline(offset)?; + self.newline(body.get_binary_reader().original_position())?; self.start_group("func ")?; let func_idx = state.core.funcs; self.print_name(&state.core.func_names, func_idx)?; @@ -1319,7 +1317,7 @@ impl Printer<'_, '_> { if self.config.print_skeleton { self.result.write_str(" ...")?; } else { - self.print_func_body(state, func_idx, params, &mut body, &hints)?; + self.print_func_body(state, func_idx, params, &body, &hints)?; } self.end_group()?; @@ -1332,17 +1330,18 @@ impl Printer<'_, '_> { state: &mut State, func_idx: u32, params: u32, - body: &mut BinaryReader<'_>, + body: &FunctionBody<'_>, branch_hints: &[(usize, BranchHint)], ) -> Result<()> { let mut first = true; let mut local_idx = 0; let mut locals = NamedLocalPrinter::new("local"); - let func_start = body.original_position(); - for _ in 0..body.read_var_u32()? { - let offset = body.original_position(); - let cnt = body.read_var_u32()?; - let ty = body.read()?; + let mut reader = body.get_binary_reader(); + let func_start = reader.original_position(); + for _ in 0..reader.read_var_u32()? { + let offset = reader.original_position(); + let cnt = reader.read_var_u32()?; + let ty = reader.read()?; if MAX_LOCALS .checked_sub(local_idx) .and_then(|s| s.checked_sub(cnt)) @@ -1371,11 +1370,11 @@ impl Printer<'_, '_> { let mut folded_printer = PrintOperatorFolded::new(self, state, &mut operator_state); folded_printer.set_offset(func_start); folded_printer.begin_function(func_idx)?; - Self::print_operators(body, branch_hints, func_start, &mut folded_printer)?; + Self::print_operators(&mut reader, branch_hints, func_start, &mut folded_printer)?; folded_printer.finalize()?; } else { let mut flat_printer = PrintOperator::new(self, state, &mut operator_state); - Self::print_operators(body, branch_hints, func_start, &mut flat_printer)?; + Self::print_operators(&mut reader, branch_hints, func_start, &mut flat_printer)?; } // If this was an invalid function body then the nesting may not @@ -1385,7 +1384,7 @@ impl Printer<'_, '_> { // with the closing paren printed for the func. if self.nesting != nesting_start { self.nesting = nesting_start; - self.newline(body.original_position())?; + self.newline(reader.original_position())?; } Ok(()) @@ -1397,20 +1396,28 @@ impl Printer<'_, '_> { func_start: usize, op_printer: &mut O, ) -> Result<()> { - while !body.is_end_then_eof() { + let mut ops = OperatorsReader::new(body.clone()); + while !ops.eof() { + if ops.is_end_then_eof() { + ops.read()?; // final "end" opcode terminates instruction sequence + ops.finish()?; + return Ok(()); + } + // Branch hints are stored in increasing order of their body offset // so print them whenever their instruction comes up. if let Some(((hint_offset, hint), rest)) = branch_hints.split_first() { - if hint.func_offset == (body.original_position() - func_start) as u32 { + if hint.func_offset == (ops.original_position() - func_start) as u32 { branch_hints = rest; op_printer.branch_hint(*hint_offset, hint.taken)?; } } - op_printer.set_offset(body.original_position()); - op_printer.visit_operator(body)?; + op_printer.set_offset(ops.original_position()); + op_printer.visit_operator(&mut ops)?; } - Ok(()) + ops.finish()?; // for the error message + bail!("unexpected end of operators"); } fn newline(&mut self, offset: usize) -> Result<()> { diff --git a/crates/wasmprinter/src/operator.rs b/crates/wasmprinter/src/operator.rs index a87df5066a..2759c9916d 100644 --- a/crates/wasmprinter/src/operator.rs +++ b/crates/wasmprinter/src/operator.rs @@ -3,8 +3,8 @@ use anyhow::{anyhow, bail, Result}; use termcolor::{Ansi, NoColor}; use wasmparser::VisitSimdOperator; use wasmparser::{ - BinaryReader, BlockType, BrTable, Catch, CompositeInnerType, ContType, FrameKind, FuncType, - Handle, MemArg, ModuleArity, Operator, Ordering, RefType, ResumeTable, SubType, TryTable, + BlockType, BrTable, Catch, CompositeInnerType, ContType, FrameKind, FuncType, Handle, MemArg, + ModuleArity, Operator, OperatorsReader, Ordering, RefType, ResumeTable, SubType, TryTable, VisitOperator, }; @@ -1404,7 +1404,7 @@ impl<'a> VisitSimdOperator<'a> for PrintOperator<'_, '_, '_, '_> { pub trait OpPrinter { fn branch_hint(&mut self, offset: usize, taken: bool) -> Result<()>; fn set_offset(&mut self, offset: usize); - fn visit_operator(&mut self, reader: &mut BinaryReader<'_>) -> Result<()>; + fn visit_operator(&mut self, reader: &mut OperatorsReader<'_>) -> Result<()>; } impl OpPrinter for PrintOperator<'_, '_, '_, '_> { @@ -1421,7 +1421,7 @@ impl OpPrinter for PrintOperator<'_, '_, '_, '_> { self.operator_state.op_offset = offset; } - fn visit_operator(&mut self, reader: &mut BinaryReader<'_>) -> Result<()> { + fn visit_operator(&mut self, reader: &mut OperatorsReader<'_>) -> Result<()> { reader.visit_operator(self)? } } @@ -1444,8 +1444,8 @@ impl OpPrinter for PrintOperatorFolded<'_, '_, '_, '_> { self.operator_state.op_offset = offset; } - fn visit_operator(&mut self, reader: &mut BinaryReader<'_>) -> Result<()> { - let operator = reader.clone().read_operator()?; + fn visit_operator(&mut self, reader: &mut OperatorsReader<'_>) -> Result<()> { + let operator = reader.clone().read()?; let (params, results) = operator .operator_arity(self) .ok_or_else(|| anyhow::anyhow!("could not calculate operator arity"))?; diff --git a/crates/wasmprinter/tests/all.rs b/crates/wasmprinter/tests/all.rs index 3d4d3bb255..03de5b9d32 100644 --- a/crates/wasmprinter/tests/all.rs +++ b/crates/wasmprinter/tests/all.rs @@ -5,7 +5,7 @@ fn no_oom() { let mut s = String::new(); s.push_str("(module\n"); for _ in 0..20_000 { - s.push_str("(func if)\n"); + s.push_str("(func i32.const 0 if i32.const 0 else i32.const 0 end)\n"); } s.push(')'); let bytes = wat::parse_str(&s).unwrap(); diff --git a/crates/wit-component/src/gc.rs b/crates/wit-component/src/gc.rs index 9a1747f93d..6a5a3251e2 100644 --- a/crates/wit-component/src/gc.rs +++ b/crates/wit-component/src/gc.rs @@ -465,10 +465,12 @@ impl<'a> Module<'a> { })); } - fn operators(&mut self, mut reader: BinaryReader<'a>) -> Result<()> { - while !reader.eof() { - reader.visit_operator(self)?; + fn operators(&mut self, reader: BinaryReader<'a>) -> Result<()> { + let mut ops = OperatorsReader::new(reader); + while !ops.eof() { + ops.visit_operator(self)?; } + ops.finish()?; Ok(()) } diff --git a/src/bin/wasm-tools/component.rs b/src/bin/wasm-tools/component.rs index 9baf414deb..15e8edaef1 100644 --- a/src/bin/wasm-tools/component.rs +++ b/src/bin/wasm-tools/component.rs @@ -958,7 +958,7 @@ impl TargetsOpts { fn run(self) -> Result<()> { let (resolve, pkg_id) = self.resolve.load()?; let world = resolve.select_world(pkg_id, self.world.as_deref())?; - let component_to_test = self.input.parse_wasm()?; + let component_to_test = self.input.get_binary_wasm()?; wit_component::targets(&resolve, world, &component_to_test)?; diff --git a/src/bin/wasm-tools/validate.rs b/src/bin/wasm-tools/validate.rs index ff2d7fdebb..712d03085e 100644 --- a/src/bin/wasm-tools/validate.rs +++ b/src/bin/wasm-tools/validate.rs @@ -81,7 +81,7 @@ impl Opts { pub fn run(&self) -> Result<()> { let start = Instant::now(); - let wasm = self.io.parse_input_wasm()?; + let wasm = self.io.get_input_wasm()?; // no need to parse as the validator will do this log::info!("read module in {:?}", start.elapsed()); // If validation fails then try to attach extra information to the diff --git a/src/bin/wasm-tools/wast.rs b/src/bin/wasm-tools/wast.rs index bdcab079cd..696a5b2f70 100644 --- a/src/bin/wasm-tools/wast.rs +++ b/src/bin/wasm-tools/wast.rs @@ -9,6 +9,7 @@ use std::path::{Path, PathBuf}; use std::process::{Command, Stdio}; use std::str; use wasm_encoder::reencode::{Reencode, ReencodeComponent, RoundtripReencoder}; +use wasm_tools::parse_binary_wasm; use wasmprinter::PrintFmtWrite; use wast::component::{Component, ComponentKind}; use wast::core::{Module, ModuleKind}; @@ -193,11 +194,110 @@ impl Opts { } WastDirective::AssertMalformed { - span: _, + span, mut module, message, + } => { + // The WebAssembly specification itself has a strict + // distinction between `assert_invalid` and `assert_malformed` + // where a "malformed" module is one that simply does not parse + // while an "invalid" module is one that parses successfully + // but then fails validation. Effectively "malformed" can't be + // represented in the spec's form of the AST, while and + // "invalid" module has an AST but it's semantically invalid. + // + // Currently in wasmparser we do not match the specification + // 1:1 in this respect. It's seen as unnecessarily onerous to + // match the upstream AST 1:1 to ensure all errors are reported + // exactly at the same time. In a similar to manner to how we + // don't actually check error messages with upstream spec tests + // we handle this situation by asserting that the module still + // produces an error, somehow, but just not the exact same + // stage of the error. + // + // For this reason there's a few error messages here which are + // found in upstream spec tests which are "relaxed" to becoming + // `assert_invalid` instead of `assert_malformed`. That gets + // the tests "passing" and means we don't need to contort the + // implementations in this crate to exactly match upstream. + let permissive_error_messages = [ + // The WebAssembly specification says that the validation + // of the data count section is a syntactic validation rule + // and thus part of the binary format. This means that + // an invalid data count is `assert_malformed`, not + // `assert_invalid`, as it's considered invalid before + // reaching the validator. Currently though `wasmparser` + // does not respect this and instead detects the invalid + // module during validation, not parsing. This is basically + // due to the fact that wasmparser represents the AST + // differently. + "data count section required", + // The upstream specification's tests have not been + // adjusted for `shared-everything-threads` yet so some + // flags which are valid with `shared-everything-threads` + // are asserted as malformed. While we wait for upstream + // tests to be adjusted to use a different flag bit in + // their `assert_malformed` blocks this makes it easier to + // implement validation in wasmparser. Effectively these + // two error messages are swapped to `assert_invalid`. + "malformed mutability", + "integer too large", + ]; + if self.assert(Assert::Permissive) && permissive_error_messages.contains(&message) { + return self.test_wast_directive( + test, + WastDirective::AssertInvalid { + span, + module, + message, + }, + idx, + ); + } + + let result: Result> = module.encode().map_err(|e| e.into()); + match result { + Err(e) => { + if self.error_matches(test, &format!("{e:?}"), message) { + return Ok(()); + } + bail!( + "bad error: {e:?}\n\ + should have failed with: {message:?}\n\ + suppress this failure with `--ignore-error-messages`", + ); + } + Ok(bytes) => { + let mut parser = wasmparser::Parser::new(0); + parser.set_features(self.features.features()); + match parse_binary_wasm(parser, &bytes) { + Err(e) => { + if self.error_matches(test, &format!("{e:?}"), message) { + // make sure validator also rejects module (not necessarily with same error) + + match self.test_wasm_valid(test, &bytes) { + Ok(_) => { + bail!("validator thought malformed example was valid") + } + Err(_) => return Ok(()), + } + } + bail!( + "bad error: {e:?}\n\ + should have failed with: {message:?}\n\ + suppress this failure with `--ignore-error-messages`", + ); + } + Ok(_) => (), + } + bail!( + "encoded and parsed successfully but should have failed with: {message:?}", + ) + } + } } - | WastDirective::AssertInvalid { + + WastDirective::AssertInvalid { mut module, message, span: _, @@ -253,7 +353,9 @@ impl Opts { // if explicitly requested, enable (a, b) if a == b => enabled = true, // default enables almost all assertions - (Assert::Default, b) if b != Assert::SnapshotFolded => enabled = true, + (Assert::Default, b) if b != Assert::SnapshotFolded && b != Assert::Permissive => { + enabled = true + } // NoTestFolded disables TestFolded (Assert::NoTestFolded, Assert::TestFolded) => enabled = false, _ => (), @@ -571,4 +673,5 @@ enum Assert { SnapshotFolded, TestFolded, NoTestFolded, + Permissive, } diff --git a/src/lib.rs b/src/lib.rs index c5e33c0f9f..39d9e9e71f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -109,7 +109,7 @@ impl FromStr for GenerateDwarf { } impl InputArg { - pub fn parse_wasm(&self) -> Result> { + pub fn get_binary_wasm(&self) -> Result> { let mut parser = wat::Parser::new(); match (self.generate_full_dwarf, self.generate_dwarf) { (false, Some(GenerateDwarf::Lines)) => { @@ -160,7 +160,13 @@ pub enum Output<'a> { impl InputOutput { pub fn parse_input_wasm(&self) -> Result> { - self.input.parse_wasm() + let ret = self.get_input_wasm()?; + parse_binary_wasm(wasmparser::Parser::new(0), &ret)?; + Ok(ret) + } + + pub fn get_input_wasm(&self) -> Result> { + self.input.get_binary_wasm() } pub fn output_wasm(&self, wasm: &[u8], wat: bool) -> Result<()> { @@ -289,3 +295,63 @@ 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)?, + wasmparser::Payload::FunctionSection(s) => parse_section(s)?, + wasmparser::Payload::TableSection(s) => parse_section(s)?, + wasmparser::Payload::MemorySection(s) => parse_section(s)?, + wasmparser::Payload::TagSection(s) => parse_section(s)?, + wasmparser::Payload::GlobalSection(s) => parse_section(s)?, + wasmparser::Payload::ExportSection(s) => parse_section(s)?, + wasmparser::Payload::ElementSection(s) => parse_section(s)?, + wasmparser::Payload::DataSection(s) => parse_section(s)?, + wasmparser::Payload::CodeSectionEntry(body) => { + for item in body.get_locals_reader()? { + let _ = item?; + } + let mut ops = body.get_operators_reader()?; + while !ops.eof() { + ops.read()?; + } + ops.finish()?; + } + + wasmparser::Payload::InstanceSection(s) => parse_section(s)?, + wasmparser::Payload::CoreTypeSection(s) => parse_section(s)?, + wasmparser::Payload::ComponentInstanceSection(s) => parse_section(s)?, + wasmparser::Payload::ComponentAliasSection(s) => parse_section(s)?, + wasmparser::Payload::ComponentTypeSection(s) => parse_section(s)?, + wasmparser::Payload::ComponentCanonicalSection(s) => parse_section(s)?, + wasmparser::Payload::ComponentImportSection(s) => parse_section(s)?, + wasmparser::Payload::ComponentExportSection(s) => parse_section(s)?, + + wasmparser::Payload::UnknownSection { id, .. } => { + bail!("malformed section id: {}", id) + } + + _ => (), + } + Ok(()) + } + + fn parse_section<'a, T>(s: wasmparser::SectionLimited<'a, T>) -> Result<()> + where + T: wasmparser::FromReader<'a>, + { + for item in s { + let _ = item?; + } + Ok(()) + } +} diff --git a/tests/cli/dangling_if.wat b/tests/cli/dangling_if.wat index 420cb6da9c..7a656a1608 100644 --- a/tests/cli/dangling_if.wat +++ b/tests/cli/dangling_if.wat @@ -1,4 +1,4 @@ -;; RUN: print % | parse -t +;; FAIL: print % (module (func if) diff --git a/tests/cli/dangling_if.wat.stderr b/tests/cli/dangling_if.wat.stderr new file mode 100644 index 0000000000..49a462bf27 --- /dev/null +++ b/tests/cli/dangling_if.wat.stderr @@ -0,0 +1 @@ +error: control frames remain at end of function body or expression (at offset 0x1a) diff --git a/tests/cli/dangling_if.wat.stdout b/tests/cli/dangling_if.wat.stdout deleted file mode 100644 index f7cec4aaab..0000000000 --- a/tests/cli/dangling_if.wat.stdout +++ /dev/null @@ -1,7 +0,0 @@ -(module - (type (;0;) (func)) - (func (;0;) (type 0) - if ;; label = @1 - - ) -) diff --git a/tests/cli/data-count-big.wast b/tests/cli/data-count-big.wast index dd4580fda5..a73e4483f3 100644 --- a/tests/cli/data-count-big.wast +++ b/tests/cli/data-count-big.wast @@ -6,5 +6,5 @@ "\0c\05\ff\ff\ff\ff\01" ;; data section, 5 bytes, huge count ) - "data count section specifies too many data segments") + "data count is non-zero but data section is absent") diff --git a/tests/cli/dump/select.wat b/tests/cli/dump/select.wat index c72d8625c7..0ee9171091 100644 --- a/tests/cli/dump/select.wat +++ b/tests/cli/dump/select.wat @@ -1,4 +1,6 @@ -;; RUN: dump % +;; FAIL: dump % +;; This fails because wasmprinter can't (yet) print an invalid multi-value select. +;; Can be changed back to a "RUN" test once that code lands. (module (func diff --git a/tests/cli/dump/select.wat.stderr b/tests/cli/dump/select.wat.stderr new file mode 100644 index 0000000000..f86ca350cf --- /dev/null +++ b/tests/cli/dump/select.wat.stderr @@ -0,0 +1 @@ +error: invalid result arity (at offset 0x18) diff --git a/tests/cli/dump/select.wat.stdout b/tests/cli/dump/select.wat.stdout deleted file mode 100644 index be65160bf7..0000000000 --- a/tests/cli/dump/select.wat.stdout +++ /dev/null @@ -1,21 +0,0 @@ - 0x0 | 00 61 73 6d | version 1 (Module) - | 01 00 00 00 - 0x8 | 01 04 | type section - 0xa | 01 | 1 count ---- rec group 0 (implicit) --- - 0xb | 60 00 00 | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [], results: [] }), shared: false } } - 0xe | 03 02 | func section - 0x10 | 01 | 1 count - 0x11 | 00 | [func 0] type 0 - 0x12 | 0a 0e | code section - 0x14 | 01 | 1 count -============== func 0 ==================== - 0x15 | 0c | size of function - 0x16 | 00 | 0 local blocks - 0x17 | 1b | select - 0x18 | 1c 00 | ?? - 0x1a | 1c 01 7f | typed_select ty:I32 - 0x1d | 1c 02 | ?? - 0x1f | 7f | i64_div_s - 0x20 | 7f | i64_div_s - 0x21 | 0b | end diff --git a/tests/cli/elem.wast b/tests/cli/elem.wast index 70cf333a60..47bf5a8852 100644 --- a/tests/cli/elem.wast +++ b/tests/cli/elem.wast @@ -16,12 +16,12 @@ ) "type mismatch" ) -(assert_invalid +(assert_malformed (module (table 1 funcref) (elem (i32.const 0) funcref (if)) ) - "constant expression required" + "control frames remain" ) (assert_malformed (module quote diff --git a/tests/cli/gc/invalid.wast b/tests/cli/gc/invalid.wast index f113e9e149..a69b5d4bad 100644 --- a/tests/cli/gc/invalid.wast +++ b/tests/cli/gc/invalid.wast @@ -135,7 +135,7 @@ ) "array.new_data can only create arrays with numeric and vector elements") -(assert_malformed +(assert_invalid (module binary "\00asm" "\01\00\00\00" ;; module header diff --git a/tests/cli/invalid.wast b/tests/cli/invalid.wast index 031834fa75..6a627af060 100644 --- a/tests/cli/invalid.wast +++ b/tests/cli/invalid.wast @@ -6,7 +6,7 @@ (func table.init 0 100)) "unknown elem segment") -(assert_invalid +(assert_malformed (module (func else)) - "else found outside of an `if` block") + "`else` found outside `If` block") diff --git a/tests/cli/invalid/crash-1eefc5597ae263919265239b0dba6a033eb80384.wast b/tests/cli/invalid/crash-1eefc5597ae263919265239b0dba6a033eb80384.wast index b343108764..703803880a 100644 --- a/tests/cli/invalid/crash-1eefc5597ae263919265239b0dba6a033eb80384.wast +++ b/tests/cli/invalid/crash-1eefc5597ae263919265239b0dba6a033eb80384.wast @@ -11,4 +11,4 @@ ;; trailing bytes "\00" ) - "type index out of bounds") + "section size mismatch: unexpected data at the end of the section") diff --git a/tests/cli/invalid/invalid-ty2.wast b/tests/cli/invalid/invalid-ty2.wast index 536183e80d..1e3290bdc1 100644 --- a/tests/cli/invalid/invalid-ty2.wast +++ b/tests/cli/invalid/invalid-ty2.wast @@ -4,7 +4,7 @@ (module quote "(func block (type 2))" ) - "type index out of bounds") + "control frames remain at end of function body or expression") (assert_invalid (module diff --git a/tests/cli/missing-features/custom-page-sizes.wast b/tests/cli/missing-features/custom-page-sizes.wast index bf4cc7a6f8..93fc08d4b9 100644 --- a/tests/cli/missing-features/custom-page-sizes.wast +++ b/tests/cli/missing-features/custom-page-sizes.wast @@ -1,7 +1,11 @@ ;; RUN: wast --assert default --snapshot tests/snapshots % -f mvp -(assert_malformed +(assert_invalid (module (memory 0 (pagesize 1)) ) "the custom page sizes proposal must be enabled to customize a memory's page size") + +(assert_invalid + (module (memory 0 (pagesize 65536))) + "the custom page sizes proposal must be enabled to customize a memory's page size") diff --git a/tests/cli/missing-features/missing-exceptions.wast b/tests/cli/missing-features/missing-exceptions.wast index a00e10230a..00997f09a6 100644 --- a/tests/cli/missing-features/missing-exceptions.wast +++ b/tests/cli/missing-features/missing-exceptions.wast @@ -1,13 +1,13 @@ ;; RUN: wast --assert default --snapshot tests/snapshots % -f wasm1 -(assert_malformed +(assert_invalid (module binary "\00asm" "\01\00\00\00" "\0d\01\00" ) "exceptions proposal not enabled") -(assert_malformed +(assert_invalid (module (import "" "" (tag)) ) diff --git a/tests/cli/missing-features/passive-and-declare-elems.wast b/tests/cli/missing-features/passive-and-declare-elems.wast new file mode 100644 index 0000000000..324ae86227 --- /dev/null +++ b/tests/cli/missing-features/passive-and-declare-elems.wast @@ -0,0 +1,11 @@ +;; RUN: wast --assert default --snapshot tests/snapshots % -f wasm1 + +(assert_invalid + (module (elem func)) + "bulk memory must be enabled" +) + +(assert_invalid + (module (elem declare func)) + "bulk memory must be enabled" +) diff --git a/tests/cli/missing-features/shared-everything-threads-globals.wast b/tests/cli/missing-features/shared-everything-threads-globals.wast index 953f83995c..f44556721f 100644 --- a/tests/cli/missing-features/shared-everything-threads-globals.wast +++ b/tests/cli/missing-features/shared-everything-threads-globals.wast @@ -1,12 +1,12 @@ ;; RUN: wast --assert default --snapshot tests/snapshots % -f mvp -(assert_malformed +(assert_invalid (module quote "(global (shared i32) (i32.const 0))" ) "shared globals require the shared-everything-threads proposal") -(assert_malformed +(assert_invalid (module quote "(global (import \"spectest\" \"global_i64\") (shared mut i64))" ) diff --git a/tests/cli/missing-features/shared-everything-threads-tables.wast b/tests/cli/missing-features/shared-everything-threads-tables.wast index 20669dc40e..f9d14546c9 100644 --- a/tests/cli/missing-features/shared-everything-threads-tables.wast +++ b/tests/cli/missing-features/shared-everything-threads-tables.wast @@ -1,6 +1,6 @@ ;; RUN: wast --assert default --snapshot tests/snapshots % -f mvp -(assert_malformed +(assert_invalid (module quote "(table shared 1 funcref)" ) diff --git a/tests/cli/print-code-section-overflow.wat.stderr b/tests/cli/print-code-section-overflow.wat.stderr index 165859a27d..f8ed1b5890 100644 --- a/tests/cli/print-code-section-overflow.wat.stderr +++ b/tests/cli/print-code-section-overflow.wat.stderr @@ -1 +1 @@ -error: invalid code section size +error: function section is absent but code section has non-zero count (at offset 0xa) diff --git a/tests/cli/print-code-section-overflow.wat.stdout b/tests/cli/print-code-section-overflow.wat.stdout deleted file mode 100644 index b55cecc0af..0000000000 --- a/tests/cli/print-code-section-overflow.wat.stdout +++ /dev/null @@ -1 +0,0 @@ -(module diff --git a/tests/cli/print-dont-reserve-the-world.wat.stderr b/tests/cli/print-dont-reserve-the-world.wat.stderr index ac95ab81ab..19f2dec544 100644 --- a/tests/cli/print-dont-reserve-the-world.wat.stderr +++ b/tests/cli/print-dont-reserve-the-world.wat.stderr @@ -1 +1 @@ -error: module contains 4294967295 functions which exceeds the limit of 1000000 +error: unexpected end-of-file (at offset 0xf) diff --git a/tests/cli/print-dont-reserve-the-world.wat.stdout b/tests/cli/print-dont-reserve-the-world.wat.stdout deleted file mode 100644 index b55cecc0af..0000000000 --- a/tests/cli/print-dont-reserve-the-world.wat.stdout +++ /dev/null @@ -1 +0,0 @@ -(module diff --git a/tests/cli/print-locals-overflow.wat.stderr b/tests/cli/print-locals-overflow.wat.stderr index 42167d7e9d..990d430321 100644 --- a/tests/cli/print-locals-overflow.wat.stderr +++ b/tests/cli/print-locals-overflow.wat.stderr @@ -1 +1 @@ -error: function exceeds the maximum number of locals that can be printed +error: control frames remain at end of function body or expression (at offset 0x1d) diff --git a/tests/cli/print-locals-overflow.wat.stdout b/tests/cli/print-locals-overflow.wat.stdout deleted file mode 100644 index 5af803de5c..0000000000 --- a/tests/cli/print-locals-overflow.wat.stdout +++ /dev/null @@ -1,3 +0,0 @@ -(module - (type (;0;) (func)) - (func (;0;) (type 0) diff --git a/tests/cli/print-no-panic-dangling-else.wat b/tests/cli/print-no-panic-dangling-else.wat index 7c6307f1af..c38c1b38cc 100644 --- a/tests/cli/print-no-panic-dangling-else.wat +++ b/tests/cli/print-no-panic-dangling-else.wat @@ -1,4 +1,4 @@ -;; RUN: print % +;; FAIL: print % (module (func else) diff --git a/tests/cli/print-no-panic-dangling-else.wat.stderr b/tests/cli/print-no-panic-dangling-else.wat.stderr new file mode 100644 index 0000000000..483dfe7f28 --- /dev/null +++ b/tests/cli/print-no-panic-dangling-else.wat.stderr @@ -0,0 +1 @@ +error: `else` found outside `If` block (at offset 0x18) diff --git a/tests/cli/print-no-panic-dangling-else.wat.stdout b/tests/cli/print-no-panic-dangling-else.wat.stdout deleted file mode 100644 index e43ca79fee..0000000000 --- a/tests/cli/print-no-panic-dangling-else.wat.stdout +++ /dev/null @@ -1,6 +0,0 @@ -(module - (type (;0;) (func)) - (func (;0;) (type 0) - else - ) -) diff --git a/tests/cli/print-no-panic-double-end.wat b/tests/cli/print-no-panic-double-end.wat index 4d7b65d450..7ed11e7031 100644 --- a/tests/cli/print-no-panic-double-end.wat +++ b/tests/cli/print-no-panic-double-end.wat @@ -1,4 +1,4 @@ -;; RUN: print % +;; FAIL: print % (module (func end) diff --git a/tests/cli/print-no-panic-double-end.wat.stderr b/tests/cli/print-no-panic-double-end.wat.stderr new file mode 100644 index 0000000000..2855463a0a --- /dev/null +++ b/tests/cli/print-no-panic-double-end.wat.stderr @@ -0,0 +1 @@ +error: operators remaining after end of function body or expression (at offset 0x18) diff --git a/tests/cli/print-no-panic-double-end.wat.stdout b/tests/cli/print-no-panic-double-end.wat.stdout deleted file mode 100644 index 3d0ed6beb9..0000000000 --- a/tests/cli/print-no-panic-double-end.wat.stdout +++ /dev/null @@ -1,6 +0,0 @@ -(module - (type (;0;) (func)) - (func (;0;) (type 0) - end - ) -) diff --git a/tests/cli/print-with-too-many-ends.wat b/tests/cli/print-with-too-many-ends.wat index f498a40ef9..e43a6c3749 100644 --- a/tests/cli/print-with-too-many-ends.wat +++ b/tests/cli/print-with-too-many-ends.wat @@ -1,4 +1,4 @@ -;; RUN: print % +;; FAIL: print % (module (func end i32.const 0 drop end) diff --git a/tests/cli/print-with-too-many-ends.wat.stderr b/tests/cli/print-with-too-many-ends.wat.stderr new file mode 100644 index 0000000000..2855463a0a --- /dev/null +++ b/tests/cli/print-with-too-many-ends.wat.stderr @@ -0,0 +1 @@ +error: operators remaining after end of function body or expression (at offset 0x18) diff --git a/tests/cli/print-with-too-many-ends.wat.stdout b/tests/cli/print-with-too-many-ends.wat.stdout deleted file mode 100644 index b06719885d..0000000000 --- a/tests/cli/print-with-too-many-ends.wat.stdout +++ /dev/null @@ -1,9 +0,0 @@ -(module - (type (;0;) (func)) - (func (;0;) (type 0) - end - i32.const 0 - drop - end - ) -) diff --git a/tests/cli/spec/binary.wast b/tests/cli/spec/binary.wast index fc81bb4141..46f8c261b7 100644 --- a/tests/cli/spec/binary.wast +++ b/tests/cli/spec/binary.wast @@ -1,5 +1,6 @@ ;; RUN: wast \ ;; --assert default \ +;; --assert permissive \ ;; --snapshot tests/snapshots \ ;; --ignore-error-messages \ ;; --features=wasm2 \ diff --git a/tests/cli/spec/global.wast b/tests/cli/spec/global.wast index e40c46913c..f8024df71e 100644 --- a/tests/cli/spec/global.wast +++ b/tests/cli/spec/global.wast @@ -1,5 +1,6 @@ ;; RUN: wast \ ;; --assert default \ +;; --assert permissive \ ;; --snapshot tests/snapshots \ ;; --ignore-error-messages \ ;; --features=wasm2 \ diff --git a/tests/cli/spec/proposals/exception-handling/binary.wast b/tests/cli/spec/proposals/exception-handling/binary.wast index c4a18cd704..6d2959b7b6 100644 --- a/tests/cli/spec/proposals/exception-handling/binary.wast +++ b/tests/cli/spec/proposals/exception-handling/binary.wast @@ -1,5 +1,6 @@ ;; RUN: wast \ ;; --assert default \ +;; --assert permissive \ ;; --snapshot tests/snapshots \ ;; --ignore-error-messages \ ;; --features=wasm2,exceptions,tail-call \ diff --git a/tests/cli/spec/proposals/extended-const/global.wast b/tests/cli/spec/proposals/extended-const/global.wast index b994847e11..72ea627d3f 100644 --- a/tests/cli/spec/proposals/extended-const/global.wast +++ b/tests/cli/spec/proposals/extended-const/global.wast @@ -1,5 +1,6 @@ ;; RUN: wast \ ;; --assert default \ +;; --assert permissive \ ;; --snapshot tests/snapshots \ ;; --ignore-error-messages \ ;; --features=wasm2,extended-const \ diff --git a/tests/cli/spec/proposals/function-references/binary.wast b/tests/cli/spec/proposals/function-references/binary.wast index 27cceebebb..33a31ee0ef 100644 --- a/tests/cli/spec/proposals/function-references/binary.wast +++ b/tests/cli/spec/proposals/function-references/binary.wast @@ -1,5 +1,6 @@ ;; RUN: wast \ ;; --assert default \ +;; --assert permissive \ ;; --snapshot tests/snapshots \ ;; --ignore-error-messages \ ;; --features=wasm2,function-references,tail-call \ diff --git a/tests/cli/spec/proposals/function-references/global.wast b/tests/cli/spec/proposals/function-references/global.wast index 25ba79b94b..1115dd92c5 100644 --- a/tests/cli/spec/proposals/function-references/global.wast +++ b/tests/cli/spec/proposals/function-references/global.wast @@ -1,5 +1,6 @@ ;; RUN: wast \ ;; --assert default \ +;; --assert permissive \ ;; --snapshot tests/snapshots \ ;; --ignore-error-messages \ ;; --features=wasm2,function-references,tail-call \ diff --git a/tests/cli/spec/proposals/gc/binary.wast b/tests/cli/spec/proposals/gc/binary.wast index 46366f7273..d74d61038a 100644 --- a/tests/cli/spec/proposals/gc/binary.wast +++ b/tests/cli/spec/proposals/gc/binary.wast @@ -1,5 +1,6 @@ ;; RUN: wast \ ;; --assert default \ +;; --assert permissive \ ;; --snapshot tests/snapshots \ ;; --ignore-error-messages \ ;; --features=wasm2,function-references,gc,tail-call \ diff --git a/tests/cli/spec/proposals/gc/global.wast b/tests/cli/spec/proposals/gc/global.wast index 62a5a8ea71..e9fb92f23c 100644 --- a/tests/cli/spec/proposals/gc/global.wast +++ b/tests/cli/spec/proposals/gc/global.wast @@ -1,5 +1,6 @@ ;; RUN: wast \ ;; --assert default \ +;; --assert permissive \ ;; --snapshot tests/snapshots \ ;; --ignore-error-messages \ ;; --features=wasm2,function-references,gc,tail-call \ diff --git a/tests/cli/spec/proposals/multi-memory/binary.wast b/tests/cli/spec/proposals/multi-memory/binary.wast index 16c9290029..e742e3b8ee 100644 --- a/tests/cli/spec/proposals/multi-memory/binary.wast +++ b/tests/cli/spec/proposals/multi-memory/binary.wast @@ -1,5 +1,6 @@ ;; RUN: wast \ ;; --assert default \ +;; --assert permissive \ ;; --snapshot tests/snapshots \ ;; --ignore-error-messages \ ;; --features=wasm2,multi-memory \ diff --git a/tests/cli/spec/proposals/wasm-3.0/binary.wast b/tests/cli/spec/proposals/wasm-3.0/binary.wast index f8fa1e1d65..d3e1b03231 100644 --- a/tests/cli/spec/proposals/wasm-3.0/binary.wast +++ b/tests/cli/spec/proposals/wasm-3.0/binary.wast @@ -1,5 +1,6 @@ ;; RUN: wast \ ;; --assert default \ +;; --assert permissive \ ;; --snapshot tests/snapshots \ ;; --ignore-error-messages \ ;; --features=wasm3 \ diff --git a/tests/cli/spec/proposals/wasm-3.0/global.wast b/tests/cli/spec/proposals/wasm-3.0/global.wast index cd32eca546..38f3e74f98 100644 --- a/tests/cli/spec/proposals/wasm-3.0/global.wast +++ b/tests/cli/spec/proposals/wasm-3.0/global.wast @@ -1,5 +1,6 @@ ;; RUN: wast \ ;; --assert default \ +;; --assert permissive \ ;; --snapshot tests/snapshots \ ;; --ignore-error-messages \ ;; --features=wasm3 \ diff --git a/tests/snapshots/cli/data-count-big.wast.json b/tests/snapshots/cli/data-count-big.wast.json index 9aa558f980..e72d645f65 100644 --- a/tests/snapshots/cli/data-count-big.wast.json +++ b/tests/snapshots/cli/data-count-big.wast.json @@ -6,7 +6,7 @@ "line": 4, "filename": "data-count-big.0.wasm", "module_type": "binary", - "text": "data count section specifies too many data segments" + "text": "data count is non-zero but data section is absent" } ] } \ No newline at end of file diff --git a/tests/snapshots/cli/elem.wast.json b/tests/snapshots/cli/elem.wast.json index 7ff7ba945c..83321afd66 100644 --- a/tests/snapshots/cli/elem.wast.json +++ b/tests/snapshots/cli/elem.wast.json @@ -15,11 +15,11 @@ "text": "type mismatch" }, { - "type": "assert_invalid", + "type": "assert_malformed", "line": 20, "filename": "elem.2.wasm", "module_type": "binary", - "text": "constant expression required" + "text": "control frames remain" }, { "type": "assert_malformed", diff --git a/tests/snapshots/cli/gc/invalid.wast.json b/tests/snapshots/cli/gc/invalid.wast.json index cf5bcfd155..ed52d5718d 100644 --- a/tests/snapshots/cli/gc/invalid.wast.json +++ b/tests/snapshots/cli/gc/invalid.wast.json @@ -107,7 +107,7 @@ "text": "array.new_data can only create arrays with numeric and vector elements" }, { - "type": "assert_malformed", + "type": "assert_invalid", "line": 139, "filename": "invalid.15.wasm", "module_type": "binary", diff --git a/tests/snapshots/cli/invalid.wast.json b/tests/snapshots/cli/invalid.wast.json index 9ca0b8902b..74fa700c59 100644 --- a/tests/snapshots/cli/invalid.wast.json +++ b/tests/snapshots/cli/invalid.wast.json @@ -9,11 +9,11 @@ "text": "unknown elem segment" }, { - "type": "assert_invalid", + "type": "assert_malformed", "line": 10, "filename": "invalid.1.wasm", "module_type": "binary", - "text": "else found outside of an `if` block" + "text": "`else` found outside `If` block" } ] } \ No newline at end of file diff --git a/tests/snapshots/cli/invalid/crash-1eefc5597ae263919265239b0dba6a033eb80384.wast.json b/tests/snapshots/cli/invalid/crash-1eefc5597ae263919265239b0dba6a033eb80384.wast.json index 26429f9aab..a58bcc6df8 100644 --- a/tests/snapshots/cli/invalid/crash-1eefc5597ae263919265239b0dba6a033eb80384.wast.json +++ b/tests/snapshots/cli/invalid/crash-1eefc5597ae263919265239b0dba6a033eb80384.wast.json @@ -6,7 +6,7 @@ "line": 4, "filename": "crash-1eefc5597ae263919265239b0dba6a033eb80384.0.wasm", "module_type": "binary", - "text": "type index out of bounds" + "text": "section size mismatch: unexpected data at the end of the section" } ] } \ No newline at end of file diff --git a/tests/snapshots/cli/invalid/invalid-ty2.wast.json b/tests/snapshots/cli/invalid/invalid-ty2.wast.json index fda302ee17..a8216077b5 100644 --- a/tests/snapshots/cli/invalid/invalid-ty2.wast.json +++ b/tests/snapshots/cli/invalid/invalid-ty2.wast.json @@ -6,7 +6,7 @@ "line": 4, "filename": "invalid-ty2.0.wat", "module_type": "text", - "text": "type index out of bounds" + "text": "control frames remain at end of function body or expression" }, { "type": "assert_invalid", diff --git a/tests/snapshots/cli/missing-features/custom-page-sizes.wast.json b/tests/snapshots/cli/missing-features/custom-page-sizes.wast.json index 3425ed49c5..b41d3f2e42 100644 --- a/tests/snapshots/cli/missing-features/custom-page-sizes.wast.json +++ b/tests/snapshots/cli/missing-features/custom-page-sizes.wast.json @@ -2,11 +2,18 @@ "source_filename": "tests/cli/missing-features/custom-page-sizes.wast", "commands": [ { - "type": "assert_malformed", + "type": "assert_invalid", "line": 4, "filename": "custom-page-sizes.0.wasm", "module_type": "binary", "text": "the custom page sizes proposal must be enabled to customize a memory's page size" + }, + { + "type": "assert_invalid", + "line": 10, + "filename": "custom-page-sizes.1.wasm", + "module_type": "binary", + "text": "the custom page sizes proposal must be enabled to customize a memory's page size" } ] } \ No newline at end of file diff --git a/tests/snapshots/cli/missing-features/missing-exceptions.wast.json b/tests/snapshots/cli/missing-features/missing-exceptions.wast.json index 842791d820..c9b98e5175 100644 --- a/tests/snapshots/cli/missing-features/missing-exceptions.wast.json +++ b/tests/snapshots/cli/missing-features/missing-exceptions.wast.json @@ -2,14 +2,14 @@ "source_filename": "tests/cli/missing-features/missing-exceptions.wast", "commands": [ { - "type": "assert_malformed", + "type": "assert_invalid", "line": 4, "filename": "missing-exceptions.0.wasm", "module_type": "binary", "text": "exceptions proposal not enabled" }, { - "type": "assert_malformed", + "type": "assert_invalid", "line": 11, "filename": "missing-exceptions.1.wasm", "module_type": "binary", diff --git a/tests/snapshots/cli/missing-features/passive-and-declare-elems.wast.json b/tests/snapshots/cli/missing-features/passive-and-declare-elems.wast.json new file mode 100644 index 0000000000..4f0ad92bae --- /dev/null +++ b/tests/snapshots/cli/missing-features/passive-and-declare-elems.wast.json @@ -0,0 +1,19 @@ +{ + "source_filename": "tests/cli/missing-features/passive-and-declare-elems.wast", + "commands": [ + { + "type": "assert_invalid", + "line": 4, + "filename": "passive-and-declare-elems.0.wasm", + "module_type": "binary", + "text": "bulk memory must be enabled" + }, + { + "type": "assert_invalid", + "line": 9, + "filename": "passive-and-declare-elems.1.wasm", + "module_type": "binary", + "text": "bulk memory must be enabled" + } + ] +} \ No newline at end of file diff --git a/tests/snapshots/cli/missing-features/shared-everything-threads-globals.wast.json b/tests/snapshots/cli/missing-features/shared-everything-threads-globals.wast.json index 47b702ed01..0456bd0bc5 100644 --- a/tests/snapshots/cli/missing-features/shared-everything-threads-globals.wast.json +++ b/tests/snapshots/cli/missing-features/shared-everything-threads-globals.wast.json @@ -2,17 +2,19 @@ "source_filename": "tests/cli/missing-features/shared-everything-threads-globals.wast", "commands": [ { - "type": "assert_malformed", + "type": "assert_invalid", "line": 4, "filename": "shared-everything-threads-globals.0.wat", "module_type": "text", + "binary_filename": "shared-everything-threads-globals.0.wasm", "text": "shared globals require the shared-everything-threads proposal" }, { - "type": "assert_malformed", + "type": "assert_invalid", "line": 10, "filename": "shared-everything-threads-globals.1.wat", "module_type": "text", + "binary_filename": "shared-everything-threads-globals.1.wasm", "text": "shared globals require the shared-everything-threads proposal" }, { diff --git a/tests/snapshots/cli/missing-features/shared-everything-threads-tables.wast.json b/tests/snapshots/cli/missing-features/shared-everything-threads-tables.wast.json index 1d108ce76d..7aa3ef191f 100644 --- a/tests/snapshots/cli/missing-features/shared-everything-threads-tables.wast.json +++ b/tests/snapshots/cli/missing-features/shared-everything-threads-tables.wast.json @@ -2,10 +2,11 @@ "source_filename": "tests/cli/missing-features/shared-everything-threads-tables.wast", "commands": [ { - "type": "assert_malformed", + "type": "assert_invalid", "line": 4, "filename": "shared-everything-threads-tables.0.wat", "module_type": "text", + "binary_filename": "shared-everything-threads-tables.0.wasm", "text": "shared tables require the shared-everything-threads proposal" } ]