/* Copyright 2018 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ use crate::prelude::*; use crate::{limits::*, *}; use core::fmt; use core::marker; use core::ops::Range; use core::str; pub(crate) const WASM_MAGIC_NUMBER: &[u8; 4] = b"\0asm"; /// A binary reader for WebAssembly modules. #[derive(Debug, Clone)] pub struct BinaryReaderError { // Wrap the actual error data in a `Box` so that the error is just one // word. This means that we can continue returning small `Result`s in // registers. pub(crate) inner: Box, } #[derive(Debug, Clone)] pub(crate) struct BinaryReaderErrorInner { pub(crate) message: String, pub(crate) kind: BinaryReaderErrorKind, pub(crate) offset: usize, pub(crate) needed_hint: Option, } #[derive(Debug, Clone, Copy)] pub(crate) enum BinaryReaderErrorKind { Custom, Invalid, } /// The result for `BinaryReader` operations. pub type Result = core::result::Result; impl core::error::Error for BinaryReaderError {} impl fmt::Display for BinaryReaderError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, "{} (at offset 0x{:x})", self.inner.message, self.inner.offset ) } } impl BinaryReaderError { #[cold] pub(crate) fn _new(kind: BinaryReaderErrorKind, message: String, offset: usize) -> Self { BinaryReaderError { inner: Box::new(BinaryReaderErrorInner { kind, message, offset, needed_hint: None, }), } } #[cold] pub(crate) fn new(message: impl Into, offset: usize) -> Self { Self::_new(BinaryReaderErrorKind::Custom, message.into(), offset) } #[cold] pub(crate) fn invalid(msg: &'static str, offset: usize) -> Self { Self::_new(BinaryReaderErrorKind::Invalid, msg.into(), offset) } #[cold] pub(crate) fn fmt(args: fmt::Arguments<'_>, offset: usize) -> Self { BinaryReaderError::new(args.to_string(), offset) } #[cold] pub(crate) fn eof(offset: usize, needed_hint: usize) -> Self { let mut err = BinaryReaderError::new("unexpected end-of-file", offset); err.inner.needed_hint = Some(needed_hint); err } pub(crate) fn kind(&mut self) -> BinaryReaderErrorKind { self.inner.kind } /// Get this error's message. pub fn message(&self) -> &str { &self.inner.message } /// Get the offset within the Wasm binary where the error occurred. pub fn offset(&self) -> usize { self.inner.offset } #[cfg(all(feature = "validate", feature = "component-model"))] pub(crate) fn add_context(&mut self, context: String) { self.inner.message = format!("{context}\n{}", self.inner.message); } pub(crate) fn set_message(&mut self, message: &str) { self.inner.message = message.to_string(); } } /// A binary reader of the WebAssembly structures and types. #[derive(Clone, Debug, Hash)] pub struct BinaryReader<'a> { buffer: &'a [u8], position: usize, original_offset: usize, // When the `features` feature is disabled then the `WasmFeatures` type // still exists but this field is still omitted. When `features` is // disabled then the only constructor of this type is `BinaryReader::new` // which documents all known features being active. All known features // being active isn't represented by `WasmFeatures` when the feature is // disabled so the field is omitted here to prevent accidentally using the // wrong listing of features. // // Feature accessors are defined by `foreach_wasm_feature!` below with a // method-per-feature on `BinaryReader` which when the `features` feature // is disabled returns `true` by default. #[cfg(feature = "features")] features: WasmFeatures, } impl<'a> BinaryReader<'a> { /// Creates a new binary reader which will parse the `data` provided. /// /// The `original_offset` provided is used for byte offsets in errors that /// are generated. That offset is added to the current position in `data`. /// This can be helpful when `data` is just a window of a view into a larger /// wasm binary perhaps not even entirely stored locally. /// /// The returned binary reader will have all features known to this crate /// enabled. To reject binaries that aren't valid unless a certain feature /// is enabled use the [`BinaryReader::new_features`] constructor instead. pub fn new(data: &[u8], original_offset: usize) -> BinaryReader<'_> { BinaryReader { buffer: data, position: 0, original_offset, #[cfg(feature = "features")] features: WasmFeatures::all(), } } /// Creates a new binary reader which will parse the `data` provided. /// /// The `original_offset` provided is used for byte offsets in errors that /// are generated. That offset is added to the current position in `data`. /// This can be helpful when `data` is just a window of a view into a larger /// wasm binary perhaps not even entirely stored locally. /// /// The `features` argument provided controls which WebAssembly features are /// active when parsing this data. Wasm features typically don't affect /// parsing too much and are generally more applicable during /// validation, but features and proposals will often reinterpret /// previously-invalid constructs as now-valid things meaning something /// slightly different. This means that invalid bytes before a feature may /// now be interpreted differently after a feature is implemented. This /// means that the set of activated features can affect what errors are /// generated and when they are generated. /// /// In general it's safe to pass `WasmFeatures::all()` here. There's no /// downside to enabling all features while parsing and only enabling a /// subset of features during validation. /// /// 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 /// 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 /// different ways with future proposals, such as the `memarg` moving from a /// 32-bit offset to a 64-bit offset with the `memory64` proposal. #[cfg(feature = "features")] pub fn new_features( data: &[u8], original_offset: usize, features: WasmFeatures, ) -> BinaryReader<'_> { BinaryReader { buffer: data, position: 0, original_offset, features, } } /// "Shrinks" this binary reader to retain only the buffer left-to-parse. /// /// The primary purpose of this method is to change the return value of the /// `range()` method. That method returns the range of the original buffer /// within the wasm binary so calling `range()` on the returned /// `BinaryReader` will return a smaller range than if `range()` is called /// on `self`. /// /// Otherwise parsing values from either `self` or the return value should /// return the same thing. pub(crate) fn shrink(&self) -> BinaryReader<'a> { BinaryReader { buffer: &self.buffer[self.position..], position: 0, original_offset: self.original_offset + self.position, #[cfg(feature = "features")] features: self.features, } } /// Gets the original position of the binary reader. #[inline] pub fn original_position(&self) -> usize { self.original_offset + self.position } /// Returns the currently active set of wasm features that this reader is /// using while parsing. /// /// For more information see [`BinaryReader::new`]. #[cfg(feature = "features")] pub fn features(&self) -> WasmFeatures { self.features } /// Sets the wasm features active while parsing to the `features` specified. /// /// For more information see [`BinaryReader::new`]. #[cfg(feature = "features")] pub fn set_features(&mut self, features: WasmFeatures) { self.features = features; } /// Returns a range from the starting offset to the end of the buffer. pub fn range(&self) -> Range { self.original_offset..self.original_offset + self.buffer.len() } pub(crate) fn remaining_buffer(&self) -> &'a [u8] { &self.buffer[self.position..] } fn ensure_has_byte(&self) -> Result<()> { if self.position < self.buffer.len() { Ok(()) } else { Err(BinaryReaderError::eof(self.original_position(), 1)) } } pub(crate) fn ensure_has_bytes(&self, len: usize) -> Result<()> { if self.position + len <= self.buffer.len() { Ok(()) } else { let hint = self.position + len - self.buffer.len(); Err(BinaryReaderError::eof(self.original_position(), hint)) } } /// Reads a value of type `T` from this binary reader, advancing the /// internal position in this reader forward as data is read. #[inline] pub fn read(&mut self) -> Result where T: FromReader<'a>, { T::from_reader(self) } pub(crate) fn read_u7(&mut self) -> Result { let b = self.read_u8()?; if (b & 0x80) != 0 { return Err(BinaryReaderError::new( "invalid u7", self.original_position() - 1, )); } Ok(b) } pub(crate) fn external_kind_from_byte(byte: u8, offset: usize) -> Result { match byte { 0x00 => Ok(ExternalKind::Func), 0x01 => Ok(ExternalKind::Table), 0x02 => Ok(ExternalKind::Memory), 0x03 => Ok(ExternalKind::Global), 0x04 => Ok(ExternalKind::Tag), 0x20 => Ok(ExternalKind::FuncExact), x => Err(Self::invalid_leading_byte_error(x, "external kind", offset)), } } /// Reads a variable-length 32-bit size from the byte stream while checking /// against a limit. pub fn read_size(&mut self, limit: usize, desc: &str) -> Result { let pos = self.original_position(); let size = self.read_var_u32()? as usize; if size > limit { bail!(pos, "{desc} size is out of bounds"); } Ok(size) } /// Reads a variable-length 32-bit size from the byte stream while checking /// against a limit. /// /// Then reads that many values of type `T` and returns them as an iterator. /// /// Note that regardless of how many items are read from the returned /// iterator the items will still be parsed from this reader. pub fn read_iter<'me, T>( &'me mut self, limit: usize, desc: &str, ) -> Result> where T: FromReader<'a>, { let size = self.read_size(limit, desc)?; Ok(BinaryReaderIter { remaining: size, reader: self, _marker: marker::PhantomData, }) } /// Returns whether the `BinaryReader` has reached the end of the file. #[inline] pub fn eof(&self) -> bool { self.position >= self.buffer.len() } /// Returns the `BinaryReader`'s current position. #[inline] pub fn current_position(&self) -> usize { self.position } /// Returns the number of bytes remaining in the `BinaryReader`. #[inline] pub fn bytes_remaining(&self) -> usize { self.buffer.len() - self.position } /// Advances the `BinaryReader` `size` bytes, and returns a slice from the /// current position of `size` length. /// /// # Errors /// If `size` exceeds the remaining length in `BinaryReader`. pub fn read_bytes(&mut self, size: usize) -> Result<&'a [u8]> { self.ensure_has_bytes(size)?; let start = self.position; self.position += size; Ok(&self.buffer[start..self.position]) } /// Reads a length-prefixed list of bytes from this reader and returns a /// new `BinaryReader` to read that list of bytes. pub fn read_reader(&mut self) -> Result> { let size = self.read_var_u32()? as usize; self.skip(|reader| { reader.read_bytes(size)?; Ok(()) }) } /// Advances the `BinaryReader` four bytes and returns a `u32`. /// # Errors /// If `BinaryReader` has less than four bytes remaining. pub fn read_u32(&mut self) -> Result { self.ensure_has_bytes(4)?; let word = u32::from_le_bytes( self.buffer[self.position..self.position + 4] .try_into() .unwrap(), ); self.position += 4; Ok(word) } /// Advances the `BinaryReader` eight bytes and returns a `u64`. /// # Errors /// If `BinaryReader` has less than eight bytes remaining. pub fn read_u64(&mut self) -> Result { self.ensure_has_bytes(8)?; let word = u64::from_le_bytes( self.buffer[self.position..self.position + 8] .try_into() .unwrap(), ); self.position += 8; Ok(word) } /// Advances the `BinaryReader` a single byte. /// /// # Errors /// /// If `BinaryReader` has no bytes remaining. #[inline] pub fn read_u8(&mut self) -> Result { let b = match self.buffer.get(self.position) { Some(b) => *b, None => return Err(self.eof_err()), }; self.position += 1; Ok(b) } #[cold] fn eof_err(&self) -> BinaryReaderError { BinaryReaderError::eof(self.original_position(), 1) } /// Advances the `BinaryReader` up to four bytes to parse a variable /// length integer as a `u32`. /// /// # Errors /// /// If `BinaryReader` has less than one or up to four bytes remaining, or /// the integer is larger than 32 bits. #[inline] pub fn read_var_u32(&mut self) -> Result { // Optimization for single byte i32. let byte = self.read_u8()?; if (byte & 0x80) == 0 { Ok(u32::from(byte)) } else { self.read_var_u32_big(byte) } } fn read_var_u32_big(&mut self, byte: u8) -> Result { let mut result = (byte & 0x7F) as u32; let mut shift = 7; loop { let byte = self.read_u8()?; result |= ((byte & 0x7F) as u32) << shift; if shift >= 25 && (byte >> (32 - shift)) != 0 { let msg = if byte & 0x80 != 0 { "invalid var_u32: integer representation too long" } else { "invalid var_u32: integer too large" }; // The continuation bit or unused bits are set. return Err(BinaryReaderError::new(msg, self.original_position() - 1)); } shift += 7; if (byte & 0x80) == 0 { break; } } Ok(result) } /// Advances the `BinaryReader` up to four bytes to parse a variable /// length integer as a `u64`. /// /// # Errors /// /// If `BinaryReader` has less than one or up to eight bytes remaining, or /// the integer is larger than 64 bits. #[inline] pub fn read_var_u64(&mut self) -> Result { // Optimization for single byte u64. let byte = u64::from(self.read_u8()?); if (byte & 0x80) == 0 { Ok(byte) } else { self.read_var_u64_big(byte) } } fn read_var_u64_big(&mut self, byte: u64) -> Result { let mut result = byte & 0x7F; let mut shift = 7; loop { let byte = u64::from(self.read_u8()?); result |= (byte & 0x7F) << shift; if shift >= 57 && (byte >> (64 - shift)) != 0 { let msg = if byte & 0x80 != 0 { "invalid var_u64: integer representation too long" } else { "invalid var_u64: integer too large" }; // The continuation bit or unused bits are set. return Err(BinaryReaderError::new(msg, self.original_position() - 1)); } shift += 7; if (byte & 0x80) == 0 { break; } } Ok(result) } /// Executes `f` to skip some data in this binary reader and then returns a /// reader which will read the skipped data. pub fn skip(&mut self, f: impl FnOnce(&mut Self) -> Result<()>) -> Result { let start = self.position; f(self)?; let mut ret = self.clone(); ret.buffer = &self.buffer[start..self.position]; ret.position = 0; ret.original_offset = self.original_offset + start; Ok(ret) } /// Advances the `BinaryReader` past a WebAssembly string. This method does /// not perform any utf-8 validation. /// # Errors /// If `BinaryReader` has less than four bytes, the string's length exceeds /// the remaining bytes, or the string length /// exceeds `limits::MAX_WASM_STRING_SIZE`. pub fn skip_string(&mut self) -> Result<()> { let len = self.read_var_u32()? as usize; if len > MAX_WASM_STRING_SIZE { return Err(BinaryReaderError::new( "string size out of bounds", self.original_position() - 1, )); } self.ensure_has_bytes(len)?; self.position += len; Ok(()) } /// Advances the `BinaryReader` up to four bytes to parse a variable /// length integer as a `i32`. /// # Errors /// If `BinaryReader` has less than one or up to four bytes remaining, or /// the integer is larger than 32 bits. #[inline] pub fn read_var_i32(&mut self) -> Result { // Optimization for single byte i32. let byte = self.read_u8()?; if (byte & 0x80) == 0 { Ok(((byte as i32) << 25) >> 25) } else { self.read_var_i32_big(byte) } } fn read_var_i32_big(&mut self, byte: u8) -> Result { let mut result = (byte & 0x7F) as i32; let mut shift = 7; loop { let byte = self.read_u8()?; result |= ((byte & 0x7F) as i32) << shift; if shift >= 25 { let continuation_bit = (byte & 0x80) != 0; let sign_and_unused_bit = (byte << 1) as i8 >> (32 - shift); if continuation_bit || (sign_and_unused_bit != 0 && sign_and_unused_bit != -1) { let msg = if continuation_bit { "invalid var_i32: integer representation too long" } else { "invalid var_i32: integer too large" }; return Err(BinaryReaderError::new(msg, self.original_position() - 1)); } return Ok(result); } shift += 7; if (byte & 0x80) == 0 { break; } } let ashift = 32 - shift; Ok((result << ashift) >> ashift) } /// Advances the `BinaryReader` up to four bytes to parse a variable /// length integer as a signed 33 bit integer, returned as a `i64`. /// # Errors /// If `BinaryReader` has less than one or up to five bytes remaining, or /// the integer is larger than 33 bits. pub fn read_var_s33(&mut self) -> Result { // Optimization for single byte. let byte = self.read_u8()?; if (byte & 0x80) == 0 { return Ok(((byte as i8) << 1) as i64 >> 1); } let mut result = (byte & 0x7F) as i64; let mut shift = 7; loop { let byte = self.read_u8()?; result |= ((byte & 0x7F) as i64) << shift; if shift >= 25 { let continuation_bit = (byte & 0x80) != 0; let sign_and_unused_bit = (byte << 1) as i8 >> (33 - shift); if continuation_bit || (sign_and_unused_bit != 0 && sign_and_unused_bit != -1) { return Err(BinaryReaderError::new( "invalid var_s33: integer representation too long", self.original_position() - 1, )); } return Ok(result); } shift += 7; if (byte & 0x80) == 0 { break; } } let ashift = 64 - shift; Ok((result << ashift) >> ashift) } /// Advances the `BinaryReader` up to eight bytes to parse a variable /// length integer as a 64 bit integer, returned as a `i64`. /// # Errors /// If `BinaryReader` has less than one or up to eight bytes remaining, or /// the integer is larger than 64 bits. pub fn read_var_i64(&mut self) -> Result { let mut result: i64 = 0; let mut shift = 0; loop { let byte = self.read_u8()?; result |= i64::from(byte & 0x7F) << shift; if shift >= 57 { let continuation_bit = (byte & 0x80) != 0; let sign_and_unused_bit = ((byte << 1) as i8) >> (64 - shift); if continuation_bit || (sign_and_unused_bit != 0 && sign_and_unused_bit != -1) { let msg = if continuation_bit { "invalid var_i64: integer representation too long" } else { "invalid var_i64: integer too large" }; return Err(BinaryReaderError::new(msg, self.original_position() - 1)); } return Ok(result); } shift += 7; if (byte & 0x80) == 0 { break; } } let ashift = 64 - shift; Ok((result << ashift) >> ashift) } /// Advances the `BinaryReader` four bytes to parse a 32 bit floating point /// number, returned as `Ieee32`. /// # Errors /// If `BinaryReader` has less than four bytes remaining. pub fn read_f32(&mut self) -> Result { let value = self.read_u32()?; Ok(Ieee32(value)) } /// Advances the `BinaryReader` eight bytes to parse a 64 bit floating point /// number, returned as `Ieee64`. /// # Errors /// If `BinaryReader` has less than eight bytes remaining. pub fn read_f64(&mut self) -> Result { let value = self.read_u64()?; Ok(Ieee64(value)) } /// (internal) Reads a fixed-size WebAssembly string from the module. fn internal_read_string(&mut self, len: usize) -> Result<&'a str> { let bytes = self.read_bytes(len)?; str::from_utf8(bytes).map_err(|_| { BinaryReaderError::new("malformed UTF-8 encoding", self.original_position() - 1) }) } /// Reads a WebAssembly string from the module. /// /// # Errors /// /// If `BinaryReader` has less than up to four bytes remaining, the string's /// length exceeds the remaining bytes, the string's length exceeds /// `limits::MAX_WASM_STRING_SIZE`, or the string contains invalid utf-8. pub fn read_string(&mut self) -> Result<&'a str> { let len = self.read_var_u32()? as usize; if len > MAX_WASM_STRING_SIZE { return Err(BinaryReaderError::new( "string size out of bounds", self.original_position() - 1, )); } return self.internal_read_string(len); } /// Reads a unlimited WebAssembly string from the module. /// /// Note that this is similar to [`BinaryReader::read_string`] except that /// it will not limit the size of the returned string by /// `limits::MAX_WASM_STRING_SIZE`. pub fn read_unlimited_string(&mut self) -> Result<&'a str> { let len = self.read_var_u32()? as usize; return self.internal_read_string(len); } #[cold] pub(crate) fn invalid_leading_byte(&self, byte: u8, desc: &str) -> Result { Err(Self::invalid_leading_byte_error( byte, desc, self.original_position() - 1, )) } pub(crate) fn invalid_leading_byte_error( byte: u8, desc: &str, offset: usize, ) -> BinaryReaderError { format_err!(offset, "invalid leading byte (0x{byte:x}) for {desc}") } pub(crate) fn peek(&self) -> Result { self.ensure_has_byte()?; Ok(self.buffer[self.position]) } pub(crate) fn peek_bytes(&self, len: usize) -> Result<&[u8]> { self.ensure_has_bytes(len)?; Ok(&self.buffer[self.position..(self.position + len)]) } pub(crate) fn read_block_type(&mut self) -> Result { let b = self.peek()?; // Block types are encoded as either 0x40, a `valtype`, or `s33`. All // current `valtype` encodings are negative numbers when encoded with // sleb128, but it's also required that valtype encodings are in their // canonical form. For example an overlong encoding of -1 as `0xff 0x7f` // is not valid and it is required to be `0x7f`. This means that we // can't simply match on the `s33` that pops out below since reading the // whole `s33` might read an overlong encoding. // // To test for this the first byte `b` is inspected. The highest bit, // the continuation bit in LEB128 encoding, must be clear. The next bit, // the sign bit, must be set to indicate that the number is negative. If // these two conditions hold then we're guaranteed that this is a // negative number. // // After this a value type is read directly instead of looking for an // indexed value type. if b & 0x80 == 0 && b & 0x40 != 0 { if b == 0x40 { self.position += 1; return Ok(BlockType::Empty); } return Ok(BlockType::Type(self.read()?)); } // Not empty or a singular type, so read the function type index let idx = self.read_var_s33()?; match u32::try_from(idx) { Ok(idx) => Ok(BlockType::FuncType(idx)), Err(_) => { return Err(BinaryReaderError::new( "invalid function type", self.original_position(), )); } } } /// 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] } pub(crate) fn read_header_version(&mut self) -> Result { let magic_number = self.read_bytes(4)?; if magic_number != WASM_MAGIC_NUMBER { return Err(BinaryReaderError::new( format!( "magic header not detected: bad magic number - expected={WASM_MAGIC_NUMBER:#x?} actual={magic_number:#x?}" ), self.original_position() - 4, )); } self.read_u32() } } // See documentation on `BinaryReader::features` for more on what's going on // here. macro_rules! define_feature_accessor { ($feature:ident = $default:expr) => { impl BinaryReader<'_> { #[inline] #[allow(dead_code)] pub(crate) fn $feature(&self) -> bool { #[cfg(feature = "features")] { self.features.$feature() } #[cfg(not(feature = "features"))] { true } } } }; } super::features::foreach_wasm_feature!(define_feature_accessor); /// Iterator returned from [`BinaryReader::read_iter`]. pub struct BinaryReaderIter<'a, 'me, T: FromReader<'a>> { remaining: usize, pub(crate) reader: &'me mut BinaryReader<'a>, _marker: marker::PhantomData, } impl<'a, T> Iterator for BinaryReaderIter<'a, '_, T> where T: FromReader<'a>, { type Item = Result; fn next(&mut self) -> Option> { if self.remaining == 0 { None } else { let ret = self.reader.read::(); if ret.is_err() { self.remaining = 0; } else { self.remaining -= 1; } Some(ret) } } fn size_hint(&self) -> (usize, Option) { (self.remaining, Some(self.remaining)) } } impl<'a, T> Drop for BinaryReaderIter<'a, '_, T> where T: FromReader<'a>, { fn drop(&mut self) { while self.next().is_some() { // ... } } } impl<'a> BinaryReader<'a> { /// Function that must be called after the last opcode in an expression (instruction sequence) /// has been processed. Returns an error if there is extra data after the operators. pub fn finish_expression(&self, stack: &impl FrameStack) -> Result<()> { if stack.current_frame().is_some() { bail!( self.original_position(), "control frames remain at end of function body or expression" ); } if !self.eof() { bail!( self.original_position(), "unexpected data at the end of operators" ); } Ok(()) } #[inline] fn expect_frame(&mut self, stack: &impl FrameStack, k: FrameKind, found: &str) -> Result<()> { if stack.current_frame() == Some(k) { return Ok(()); } bail!( self.original_position(), "`{}` found outside `{:?}` block", found, k ); } /// Visit the next available operator with the specified [`VisitOperator`] instance /// that is also a [`FrameStack`]. /// /// See the documentation for [`OperatorsReader::visit_operator`] for a version that /// does not require the visitor to implement [`FrameStack`]. pub fn visit_operator(&mut self, visitor: &mut T) -> Result<>::Output> where T: VisitOperator<'a> + FrameStack, { if visitor.current_frame().is_none() { bail!( self.original_position(), "operators remaining after end of function body or expression" ); } let pos = self.original_position(); let code = self.read_u8()?; Ok(match code { 0x00 => visitor.visit_unreachable(), 0x01 => visitor.visit_nop(), 0x02 => visitor.visit_block(self.read_block_type()?), 0x03 => visitor.visit_loop(self.read_block_type()?), 0x04 => visitor.visit_if(self.read_block_type()?), 0x05 => { self.expect_frame(visitor, FrameKind::If, "else")?; visitor.visit_else() } 0x06 => { if !self.legacy_exceptions() { bail!( pos, "legacy_exceptions feature required for try instruction" ); } visitor.visit_try(self.read_block_type()?) } 0x07 => { if !self.legacy_exceptions() { bail!( pos, "legacy_exceptions feature required for catch instruction" ); } match self.expect_frame(visitor, FrameKind::LegacyCatch, "catch") { Ok(()) => (), Err(_) => self.expect_frame(visitor, FrameKind::LegacyTry, "catch")?, } visitor.visit_catch(self.read_var_u32()?) } 0x08 => visitor.visit_throw(self.read_var_u32()?), 0x09 => visitor.visit_rethrow(self.read_var_u32()?), 0x0a => visitor.visit_throw_ref(), 0x0b => visitor.visit_end(), 0x0c => visitor.visit_br(self.read_var_u32()?), 0x0d => visitor.visit_br_if(self.read_var_u32()?), 0x0e => visitor.visit_br_table(self.read_br_table()?), 0x0f => visitor.visit_return(), 0x10 => visitor.visit_call(self.read_var_u32()?), 0x11 => { let index = self.read_var_u32()?; let table = self.read_call_indirect_table_immediate()?; visitor.visit_call_indirect(index, table) } 0x12 => visitor.visit_return_call(self.read_var_u32()?), 0x13 => visitor.visit_return_call_indirect(self.read_var_u32()?, self.read_var_u32()?), 0x14 => visitor.visit_call_ref(self.read()?), 0x15 => visitor.visit_return_call_ref(self.read()?), 0x18 => { self.expect_frame(visitor, FrameKind::LegacyTry, "delegate")?; visitor.visit_delegate(self.read_var_u32()?) } 0x19 => { if !self.legacy_exceptions() { bail!( pos, "legacy_exceptions feature required for catch_all instruction" ); } match self.expect_frame(visitor, FrameKind::LegacyCatch, "catch_all") { Ok(()) => (), Err(_) => self.expect_frame(visitor, FrameKind::LegacyTry, "catch_all")?, } visitor.visit_catch_all() } 0x1a => visitor.visit_drop(), 0x1b => visitor.visit_select(), 0x1c => { let result_count = self.read_size(MAX_WASM_SELECT_RESULT_SIZE, "select types")?; if result_count == 1 { visitor.visit_typed_select(self.read()?) } else { let mut results = Vec::new(); results.reserve_exact(result_count); for _ in 0..result_count { results.push(self.read()?); } visitor.visit_typed_select_multi(results) } } 0x1f => visitor.visit_try_table(self.read()?), 0x20 => visitor.visit_local_get(self.read_var_u32()?), 0x21 => visitor.visit_local_set(self.read_var_u32()?), 0x22 => visitor.visit_local_tee(self.read_var_u32()?), 0x23 => visitor.visit_global_get(self.read_var_u32()?), 0x24 => visitor.visit_global_set(self.read_var_u32()?), 0x25 => visitor.visit_table_get(self.read_var_u32()?), 0x26 => visitor.visit_table_set(self.read_var_u32()?), 0x28 => visitor.visit_i32_load(self.read_memarg(2)?), 0x29 => visitor.visit_i64_load(self.read_memarg(3)?), 0x2a => visitor.visit_f32_load(self.read_memarg(2)?), 0x2b => visitor.visit_f64_load(self.read_memarg(3)?), 0x2c => visitor.visit_i32_load8_s(self.read_memarg(0)?), 0x2d => visitor.visit_i32_load8_u(self.read_memarg(0)?), 0x2e => visitor.visit_i32_load16_s(self.read_memarg(1)?), 0x2f => visitor.visit_i32_load16_u(self.read_memarg(1)?), 0x30 => visitor.visit_i64_load8_s(self.read_memarg(0)?), 0x31 => visitor.visit_i64_load8_u(self.read_memarg(0)?), 0x32 => visitor.visit_i64_load16_s(self.read_memarg(1)?), 0x33 => visitor.visit_i64_load16_u(self.read_memarg(1)?), 0x34 => visitor.visit_i64_load32_s(self.read_memarg(2)?), 0x35 => visitor.visit_i64_load32_u(self.read_memarg(2)?), 0x36 => visitor.visit_i32_store(self.read_memarg(2)?), 0x37 => visitor.visit_i64_store(self.read_memarg(3)?), 0x38 => visitor.visit_f32_store(self.read_memarg(2)?), 0x39 => visitor.visit_f64_store(self.read_memarg(3)?), 0x3a => visitor.visit_i32_store8(self.read_memarg(0)?), 0x3b => visitor.visit_i32_store16(self.read_memarg(1)?), 0x3c => visitor.visit_i64_store8(self.read_memarg(0)?), 0x3d => visitor.visit_i64_store16(self.read_memarg(1)?), 0x3e => visitor.visit_i64_store32(self.read_memarg(2)?), 0x3f => { let mem = self.read_memory_index_or_zero_if_not_multi_memory()?; visitor.visit_memory_size(mem) } 0x40 => { let mem = self.read_memory_index_or_zero_if_not_multi_memory()?; visitor.visit_memory_grow(mem) } 0x41 => visitor.visit_i32_const(self.read_var_i32()?), 0x42 => visitor.visit_i64_const(self.read_var_i64()?), 0x43 => visitor.visit_f32_const(self.read_f32()?), 0x44 => visitor.visit_f64_const(self.read_f64()?), 0x45 => visitor.visit_i32_eqz(), 0x46 => visitor.visit_i32_eq(), 0x47 => visitor.visit_i32_ne(), 0x48 => visitor.visit_i32_lt_s(), 0x49 => visitor.visit_i32_lt_u(), 0x4a => visitor.visit_i32_gt_s(), 0x4b => visitor.visit_i32_gt_u(), 0x4c => visitor.visit_i32_le_s(), 0x4d => visitor.visit_i32_le_u(), 0x4e => visitor.visit_i32_ge_s(), 0x4f => visitor.visit_i32_ge_u(), 0x50 => visitor.visit_i64_eqz(), 0x51 => visitor.visit_i64_eq(), 0x52 => visitor.visit_i64_ne(), 0x53 => visitor.visit_i64_lt_s(), 0x54 => visitor.visit_i64_lt_u(), 0x55 => visitor.visit_i64_gt_s(), 0x56 => visitor.visit_i64_gt_u(), 0x57 => visitor.visit_i64_le_s(), 0x58 => visitor.visit_i64_le_u(), 0x59 => visitor.visit_i64_ge_s(), 0x5a => visitor.visit_i64_ge_u(), 0x5b => visitor.visit_f32_eq(), 0x5c => visitor.visit_f32_ne(), 0x5d => visitor.visit_f32_lt(), 0x5e => visitor.visit_f32_gt(), 0x5f => visitor.visit_f32_le(), 0x60 => visitor.visit_f32_ge(), 0x61 => visitor.visit_f64_eq(), 0x62 => visitor.visit_f64_ne(), 0x63 => visitor.visit_f64_lt(), 0x64 => visitor.visit_f64_gt(), 0x65 => visitor.visit_f64_le(), 0x66 => visitor.visit_f64_ge(), 0x67 => visitor.visit_i32_clz(), 0x68 => visitor.visit_i32_ctz(), 0x69 => visitor.visit_i32_popcnt(), 0x6a => visitor.visit_i32_add(), 0x6b => visitor.visit_i32_sub(), 0x6c => visitor.visit_i32_mul(), 0x6d => visitor.visit_i32_div_s(), 0x6e => visitor.visit_i32_div_u(), 0x6f => visitor.visit_i32_rem_s(), 0x70 => visitor.visit_i32_rem_u(), 0x71 => visitor.visit_i32_and(), 0x72 => visitor.visit_i32_or(), 0x73 => visitor.visit_i32_xor(), 0x74 => visitor.visit_i32_shl(), 0x75 => visitor.visit_i32_shr_s(), 0x76 => visitor.visit_i32_shr_u(), 0x77 => visitor.visit_i32_rotl(), 0x78 => visitor.visit_i32_rotr(), 0x79 => visitor.visit_i64_clz(), 0x7a => visitor.visit_i64_ctz(), 0x7b => visitor.visit_i64_popcnt(), 0x7c => visitor.visit_i64_add(), 0x7d => visitor.visit_i64_sub(), 0x7e => visitor.visit_i64_mul(), 0x7f => visitor.visit_i64_div_s(), 0x80 => visitor.visit_i64_div_u(), 0x81 => visitor.visit_i64_rem_s(), 0x82 => visitor.visit_i64_rem_u(), 0x83 => visitor.visit_i64_and(), 0x84 => visitor.visit_i64_or(), 0x85 => visitor.visit_i64_xor(), 0x86 => visitor.visit_i64_shl(), 0x87 => visitor.visit_i64_shr_s(), 0x88 => visitor.visit_i64_shr_u(), 0x89 => visitor.visit_i64_rotl(), 0x8a => visitor.visit_i64_rotr(), 0x8b => visitor.visit_f32_abs(), 0x8c => visitor.visit_f32_neg(), 0x8d => visitor.visit_f32_ceil(), 0x8e => visitor.visit_f32_floor(), 0x8f => visitor.visit_f32_trunc(), 0x90 => visitor.visit_f32_nearest(), 0x91 => visitor.visit_f32_sqrt(), 0x92 => visitor.visit_f32_add(), 0x93 => visitor.visit_f32_sub(), 0x94 => visitor.visit_f32_mul(), 0x95 => visitor.visit_f32_div(), 0x96 => visitor.visit_f32_min(), 0x97 => visitor.visit_f32_max(), 0x98 => visitor.visit_f32_copysign(), 0x99 => visitor.visit_f64_abs(), 0x9a => visitor.visit_f64_neg(), 0x9b => visitor.visit_f64_ceil(), 0x9c => visitor.visit_f64_floor(), 0x9d => visitor.visit_f64_trunc(), 0x9e => visitor.visit_f64_nearest(), 0x9f => visitor.visit_f64_sqrt(), 0xa0 => visitor.visit_f64_add(), 0xa1 => visitor.visit_f64_sub(), 0xa2 => visitor.visit_f64_mul(), 0xa3 => visitor.visit_f64_div(), 0xa4 => visitor.visit_f64_min(), 0xa5 => visitor.visit_f64_max(), 0xa6 => visitor.visit_f64_copysign(), 0xa7 => visitor.visit_i32_wrap_i64(), 0xa8 => visitor.visit_i32_trunc_f32_s(), 0xa9 => visitor.visit_i32_trunc_f32_u(), 0xaa => visitor.visit_i32_trunc_f64_s(), 0xab => visitor.visit_i32_trunc_f64_u(), 0xac => visitor.visit_i64_extend_i32_s(), 0xad => visitor.visit_i64_extend_i32_u(), 0xae => visitor.visit_i64_trunc_f32_s(), 0xaf => visitor.visit_i64_trunc_f32_u(), 0xb0 => visitor.visit_i64_trunc_f64_s(), 0xb1 => visitor.visit_i64_trunc_f64_u(), 0xb2 => visitor.visit_f32_convert_i32_s(), 0xb3 => visitor.visit_f32_convert_i32_u(), 0xb4 => visitor.visit_f32_convert_i64_s(), 0xb5 => visitor.visit_f32_convert_i64_u(), 0xb6 => visitor.visit_f32_demote_f64(), 0xb7 => visitor.visit_f64_convert_i32_s(), 0xb8 => visitor.visit_f64_convert_i32_u(), 0xb9 => visitor.visit_f64_convert_i64_s(), 0xba => visitor.visit_f64_convert_i64_u(), 0xbb => visitor.visit_f64_promote_f32(), 0xbc => visitor.visit_i32_reinterpret_f32(), 0xbd => visitor.visit_i64_reinterpret_f64(), 0xbe => visitor.visit_f32_reinterpret_i32(), 0xbf => visitor.visit_f64_reinterpret_i64(), 0xc0 => visitor.visit_i32_extend8_s(), 0xc1 => visitor.visit_i32_extend16_s(), 0xc2 => visitor.visit_i64_extend8_s(), 0xc3 => visitor.visit_i64_extend16_s(), 0xc4 => visitor.visit_i64_extend32_s(), 0xd0 => visitor.visit_ref_null(self.read()?), 0xd1 => visitor.visit_ref_is_null(), 0xd2 => visitor.visit_ref_func(self.read_var_u32()?), 0xd3 => visitor.visit_ref_eq(), 0xd4 => visitor.visit_ref_as_non_null(), 0xd5 => visitor.visit_br_on_null(self.read_var_u32()?), 0xd6 => visitor.visit_br_on_non_null(self.read_var_u32()?), 0xe0 => visitor.visit_cont_new(self.read_var_u32()?), 0xe1 => visitor.visit_cont_bind(self.read_var_u32()?, self.read_var_u32()?), 0xe2 => visitor.visit_suspend(self.read_var_u32()?), 0xe3 => visitor.visit_resume(self.read_var_u32()?, self.read()?), 0xe4 => { visitor.visit_resume_throw(self.read_var_u32()?, self.read_var_u32()?, self.read()?) } 0xe5 => visitor.visit_switch(self.read_var_u32()?, self.read_var_u32()?), 0xfb => self.visit_0xfb_operator(pos, visitor)?, 0xfc => self.visit_0xfc_operator(pos, visitor)?, 0xfd => { #[cfg(feature = "simd")] if let Some(mut visitor) = visitor.simd_visitor() { return self.visit_0xfd_operator(pos, &mut visitor); } bail!(pos, "unexpected SIMD opcode: 0x{code:x}") } 0xfe => self.visit_0xfe_operator(pos, visitor)?, _ => bail!(pos, "illegal opcode: 0x{code:x}"), }) } fn visit_0xfb_operator( &mut self, pos: usize, visitor: &mut T, ) -> Result<>::Output> where T: VisitOperator<'a>, { let code = self.read_var_u32()?; Ok(match code { 0x0 => { let type_index = self.read_var_u32()?; visitor.visit_struct_new(type_index) } 0x01 => { let type_index = self.read_var_u32()?; visitor.visit_struct_new_default(type_index) } 0x02 => { let type_index = self.read_var_u32()?; let field_index = self.read_var_u32()?; visitor.visit_struct_get(type_index, field_index) } 0x03 => { let type_index = self.read_var_u32()?; let field_index = self.read_var_u32()?; visitor.visit_struct_get_s(type_index, field_index) } 0x04 => { let type_index = self.read_var_u32()?; let field_index = self.read_var_u32()?; visitor.visit_struct_get_u(type_index, field_index) } 0x05 => { let type_index = self.read_var_u32()?; let field_index = self.read_var_u32()?; visitor.visit_struct_set(type_index, field_index) } 0x06 => { let type_index = self.read_var_u32()?; visitor.visit_array_new(type_index) } 0x07 => { let type_index = self.read_var_u32()?; visitor.visit_array_new_default(type_index) } 0x08 => { let type_index = self.read_var_u32()?; let n = self.read_var_u32()?; visitor.visit_array_new_fixed(type_index, n) } 0x09 => { let type_index = self.read_var_u32()?; let data_index = self.read_var_u32()?; visitor.visit_array_new_data(type_index, data_index) } 0x0a => { let type_index = self.read_var_u32()?; let elem_index = self.read_var_u32()?; visitor.visit_array_new_elem(type_index, elem_index) } 0x0b => { let type_index = self.read_var_u32()?; visitor.visit_array_get(type_index) } 0x0c => { let type_index = self.read_var_u32()?; visitor.visit_array_get_s(type_index) } 0x0d => { let type_index = self.read_var_u32()?; visitor.visit_array_get_u(type_index) } 0x0e => { let type_index = self.read_var_u32()?; visitor.visit_array_set(type_index) } 0x0f => visitor.visit_array_len(), 0x10 => { let type_index = self.read_var_u32()?; visitor.visit_array_fill(type_index) } 0x11 => { let type_index_dst = self.read_var_u32()?; let type_index_src = self.read_var_u32()?; visitor.visit_array_copy(type_index_dst, type_index_src) } 0x12 => { let type_index = self.read_var_u32()?; let data_index = self.read_var_u32()?; visitor.visit_array_init_data(type_index, data_index) } 0x13 => { let type_index = self.read_var_u32()?; let elem_index = self.read_var_u32()?; visitor.visit_array_init_elem(type_index, elem_index) } 0x14 => visitor.visit_ref_test_non_null(self.read()?), 0x15 => visitor.visit_ref_test_nullable(self.read()?), 0x16 => visitor.visit_ref_cast_non_null(self.read()?), 0x17 => visitor.visit_ref_cast_nullable(self.read()?), 0x18 => { let pos = self.original_position(); let cast_flags = self.read_u8()?; let relative_depth = self.read_var_u32()?; let (from_type_nullable, to_type_nullable) = match cast_flags { 0b00 => (false, false), 0b01 => (true, false), 0b10 => (false, true), 0b11 => (true, true), _ => bail!(pos, "invalid cast flags: {cast_flags:08b}"), }; let from_heap_type = self.read()?; let from_ref_type = RefType::new(from_type_nullable, from_heap_type).ok_or_else(|| { format_err!(pos, "implementation error: type index too large") })?; let to_heap_type = self.read()?; let to_ref_type = RefType::new(to_type_nullable, to_heap_type).ok_or_else(|| { format_err!(pos, "implementation error: type index too large") })?; visitor.visit_br_on_cast(relative_depth, from_ref_type, to_ref_type) } 0x19 => { let pos = self.original_position(); let cast_flags = self.read_u8()?; let relative_depth = self.read_var_u32()?; let (from_type_nullable, to_type_nullable) = match cast_flags { 0 => (false, false), 1 => (true, false), 2 => (false, true), 3 => (true, true), _ => bail!(pos, "invalid cast flags: {cast_flags:08b}"), }; let from_heap_type = self.read()?; let from_ref_type = RefType::new(from_type_nullable, from_heap_type).ok_or_else(|| { format_err!(pos, "implementation error: type index too large") })?; let to_heap_type = self.read()?; let to_ref_type = RefType::new(to_type_nullable, to_heap_type).ok_or_else(|| { format_err!(pos, "implementation error: type index too large") })?; visitor.visit_br_on_cast_fail(relative_depth, from_ref_type, to_ref_type) } 0x1a => visitor.visit_any_convert_extern(), 0x1b => visitor.visit_extern_convert_any(), 0x1c => visitor.visit_ref_i31(), 0x1d => visitor.visit_i31_get_s(), 0x1e => visitor.visit_i31_get_u(), 0x20 => { let type_index = self.read_var_u32()?; visitor.visit_struct_new_desc(type_index) } 0x21 => { let type_index = self.read_var_u32()?; visitor.visit_struct_new_default_desc(type_index) } 0x22 => visitor.visit_ref_get_desc(self.read()?), 0x23 => visitor.visit_ref_cast_desc_non_null(self.read()?), 0x24 => visitor.visit_ref_cast_desc_nullable(self.read()?), 0x25 => { 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_desc(relative_depth, from_ref_type, to_ref_type) } 0x26 => { 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_desc_fail(relative_depth, from_ref_type, to_ref_type) } _ => bail!(pos, "unknown 0xfb subopcode: 0x{code:x}"), }) } fn visit_0xfc_operator( &mut self, pos: usize, visitor: &mut T, ) -> Result<>::Output> where T: VisitOperator<'a>, { let code = self.read_var_u32()?; Ok(match code { 0x00 => visitor.visit_i32_trunc_sat_f32_s(), 0x01 => visitor.visit_i32_trunc_sat_f32_u(), 0x02 => visitor.visit_i32_trunc_sat_f64_s(), 0x03 => visitor.visit_i32_trunc_sat_f64_u(), 0x04 => visitor.visit_i64_trunc_sat_f32_s(), 0x05 => visitor.visit_i64_trunc_sat_f32_u(), 0x06 => visitor.visit_i64_trunc_sat_f64_s(), 0x07 => visitor.visit_i64_trunc_sat_f64_u(), 0x08 => { let segment = self.read_var_u32()?; let mem = self.read_var_u32()?; visitor.visit_memory_init(segment, mem) } 0x09 => { let segment = self.read_var_u32()?; visitor.visit_data_drop(segment) } 0x0a => { let dst = self.read_var_u32()?; let src = self.read_var_u32()?; visitor.visit_memory_copy(dst, src) } 0x0b => { let mem = self.read_var_u32()?; visitor.visit_memory_fill(mem) } 0x0c => { let segment = self.read_var_u32()?; let table = self.read_var_u32()?; visitor.visit_table_init(segment, table) } 0x0d => { let segment = self.read_var_u32()?; visitor.visit_elem_drop(segment) } 0x0e => { let dst_table = self.read_var_u32()?; let src_table = self.read_var_u32()?; visitor.visit_table_copy(dst_table, src_table) } 0x0f => { let table = self.read_var_u32()?; visitor.visit_table_grow(table) } 0x10 => { let table = self.read_var_u32()?; visitor.visit_table_size(table) } 0x11 => { let table = self.read_var_u32()?; visitor.visit_table_fill(table) } 0x12 => { let mem = self.read_var_u32()?; visitor.visit_memory_discard(mem) } 0x13 => visitor.visit_i64_add128(), 0x14 => visitor.visit_i64_sub128(), 0x15 => visitor.visit_i64_mul_wide_s(), 0x16 => visitor.visit_i64_mul_wide_u(), _ => bail!(pos, "unknown 0xfc subopcode: 0x{code:x}"), }) } #[cfg(feature = "simd")] pub(super) fn visit_0xfd_operator( &mut self, pos: usize, visitor: &mut T, ) -> Result<>::Output> where T: VisitSimdOperator<'a>, { let code = self.read_var_u32()?; Ok(match code { 0x00 => visitor.visit_v128_load(self.read_memarg(4)?), 0x01 => visitor.visit_v128_load8x8_s(self.read_memarg(3)?), 0x02 => visitor.visit_v128_load8x8_u(self.read_memarg(3)?), 0x03 => visitor.visit_v128_load16x4_s(self.read_memarg(3)?), 0x04 => visitor.visit_v128_load16x4_u(self.read_memarg(3)?), 0x05 => visitor.visit_v128_load32x2_s(self.read_memarg(3)?), 0x06 => visitor.visit_v128_load32x2_u(self.read_memarg(3)?), 0x07 => visitor.visit_v128_load8_splat(self.read_memarg(0)?), 0x08 => visitor.visit_v128_load16_splat(self.read_memarg(1)?), 0x09 => visitor.visit_v128_load32_splat(self.read_memarg(2)?), 0x0a => visitor.visit_v128_load64_splat(self.read_memarg(3)?), 0x0b => visitor.visit_v128_store(self.read_memarg(4)?), 0x0c => visitor.visit_v128_const(self.read_v128()?), 0x0d => { let mut lanes: [u8; 16] = [0; 16]; for lane in &mut lanes { *lane = self.read_lane_index()? } visitor.visit_i8x16_shuffle(lanes) } 0x0e => visitor.visit_i8x16_swizzle(), 0x0f => visitor.visit_i8x16_splat(), 0x10 => visitor.visit_i16x8_splat(), 0x11 => visitor.visit_i32x4_splat(), 0x12 => visitor.visit_i64x2_splat(), 0x13 => visitor.visit_f32x4_splat(), 0x14 => visitor.visit_f64x2_splat(), 0x15 => visitor.visit_i8x16_extract_lane_s(self.read_lane_index()?), 0x16 => visitor.visit_i8x16_extract_lane_u(self.read_lane_index()?), 0x17 => visitor.visit_i8x16_replace_lane(self.read_lane_index()?), 0x18 => visitor.visit_i16x8_extract_lane_s(self.read_lane_index()?), 0x19 => visitor.visit_i16x8_extract_lane_u(self.read_lane_index()?), 0x1a => visitor.visit_i16x8_replace_lane(self.read_lane_index()?), 0x1b => visitor.visit_i32x4_extract_lane(self.read_lane_index()?), 0x1c => visitor.visit_i32x4_replace_lane(self.read_lane_index()?), 0x1d => visitor.visit_i64x2_extract_lane(self.read_lane_index()?), 0x1e => visitor.visit_i64x2_replace_lane(self.read_lane_index()?), 0x1f => visitor.visit_f32x4_extract_lane(self.read_lane_index()?), 0x20 => visitor.visit_f32x4_replace_lane(self.read_lane_index()?), 0x21 => visitor.visit_f64x2_extract_lane(self.read_lane_index()?), 0x22 => visitor.visit_f64x2_replace_lane(self.read_lane_index()?), 0x23 => visitor.visit_i8x16_eq(), 0x24 => visitor.visit_i8x16_ne(), 0x25 => visitor.visit_i8x16_lt_s(), 0x26 => visitor.visit_i8x16_lt_u(), 0x27 => visitor.visit_i8x16_gt_s(), 0x28 => visitor.visit_i8x16_gt_u(), 0x29 => visitor.visit_i8x16_le_s(), 0x2a => visitor.visit_i8x16_le_u(), 0x2b => visitor.visit_i8x16_ge_s(), 0x2c => visitor.visit_i8x16_ge_u(), 0x2d => visitor.visit_i16x8_eq(), 0x2e => visitor.visit_i16x8_ne(), 0x2f => visitor.visit_i16x8_lt_s(), 0x30 => visitor.visit_i16x8_lt_u(), 0x31 => visitor.visit_i16x8_gt_s(), 0x32 => visitor.visit_i16x8_gt_u(), 0x33 => visitor.visit_i16x8_le_s(), 0x34 => visitor.visit_i16x8_le_u(), 0x35 => visitor.visit_i16x8_ge_s(), 0x36 => visitor.visit_i16x8_ge_u(), 0x37 => visitor.visit_i32x4_eq(), 0x38 => visitor.visit_i32x4_ne(), 0x39 => visitor.visit_i32x4_lt_s(), 0x3a => visitor.visit_i32x4_lt_u(), 0x3b => visitor.visit_i32x4_gt_s(), 0x3c => visitor.visit_i32x4_gt_u(), 0x3d => visitor.visit_i32x4_le_s(), 0x3e => visitor.visit_i32x4_le_u(), 0x3f => visitor.visit_i32x4_ge_s(), 0x40 => visitor.visit_i32x4_ge_u(), 0x41 => visitor.visit_f32x4_eq(), 0x42 => visitor.visit_f32x4_ne(), 0x43 => visitor.visit_f32x4_lt(), 0x44 => visitor.visit_f32x4_gt(), 0x45 => visitor.visit_f32x4_le(), 0x46 => visitor.visit_f32x4_ge(), 0x47 => visitor.visit_f64x2_eq(), 0x48 => visitor.visit_f64x2_ne(), 0x49 => visitor.visit_f64x2_lt(), 0x4a => visitor.visit_f64x2_gt(), 0x4b => visitor.visit_f64x2_le(), 0x4c => visitor.visit_f64x2_ge(), 0x4d => visitor.visit_v128_not(), 0x4e => visitor.visit_v128_and(), 0x4f => visitor.visit_v128_andnot(), 0x50 => visitor.visit_v128_or(), 0x51 => visitor.visit_v128_xor(), 0x52 => visitor.visit_v128_bitselect(), 0x53 => visitor.visit_v128_any_true(), 0x54 => { let memarg = self.read_memarg(0)?; let lane = self.read_lane_index()?; visitor.visit_v128_load8_lane(memarg, lane) } 0x55 => { let memarg = self.read_memarg(1)?; let lane = self.read_lane_index()?; visitor.visit_v128_load16_lane(memarg, lane) } 0x56 => { let memarg = self.read_memarg(2)?; let lane = self.read_lane_index()?; visitor.visit_v128_load32_lane(memarg, lane) } 0x57 => { let memarg = self.read_memarg(3)?; let lane = self.read_lane_index()?; visitor.visit_v128_load64_lane(memarg, lane) } 0x58 => { let memarg = self.read_memarg(0)?; let lane = self.read_lane_index()?; visitor.visit_v128_store8_lane(memarg, lane) } 0x59 => { let memarg = self.read_memarg(1)?; let lane = self.read_lane_index()?; visitor.visit_v128_store16_lane(memarg, lane) } 0x5a => { let memarg = self.read_memarg(2)?; let lane = self.read_lane_index()?; visitor.visit_v128_store32_lane(memarg, lane) } 0x5b => { let memarg = self.read_memarg(3)?; let lane = self.read_lane_index()?; visitor.visit_v128_store64_lane(memarg, lane) } 0x5c => visitor.visit_v128_load32_zero(self.read_memarg(2)?), 0x5d => visitor.visit_v128_load64_zero(self.read_memarg(3)?), 0x5e => visitor.visit_f32x4_demote_f64x2_zero(), 0x5f => visitor.visit_f64x2_promote_low_f32x4(), 0x60 => visitor.visit_i8x16_abs(), 0x61 => visitor.visit_i8x16_neg(), 0x62 => visitor.visit_i8x16_popcnt(), 0x63 => visitor.visit_i8x16_all_true(), 0x64 => visitor.visit_i8x16_bitmask(), 0x65 => visitor.visit_i8x16_narrow_i16x8_s(), 0x66 => visitor.visit_i8x16_narrow_i16x8_u(), 0x67 => visitor.visit_f32x4_ceil(), 0x68 => visitor.visit_f32x4_floor(), 0x69 => visitor.visit_f32x4_trunc(), 0x6a => visitor.visit_f32x4_nearest(), 0x6b => visitor.visit_i8x16_shl(), 0x6c => visitor.visit_i8x16_shr_s(), 0x6d => visitor.visit_i8x16_shr_u(), 0x6e => visitor.visit_i8x16_add(), 0x6f => visitor.visit_i8x16_add_sat_s(), 0x70 => visitor.visit_i8x16_add_sat_u(), 0x71 => visitor.visit_i8x16_sub(), 0x72 => visitor.visit_i8x16_sub_sat_s(), 0x73 => visitor.visit_i8x16_sub_sat_u(), 0x74 => visitor.visit_f64x2_ceil(), 0x75 => visitor.visit_f64x2_floor(), 0x76 => visitor.visit_i8x16_min_s(), 0x77 => visitor.visit_i8x16_min_u(), 0x78 => visitor.visit_i8x16_max_s(), 0x79 => visitor.visit_i8x16_max_u(), 0x7a => visitor.visit_f64x2_trunc(), 0x7b => visitor.visit_i8x16_avgr_u(), 0x7c => visitor.visit_i16x8_extadd_pairwise_i8x16_s(), 0x7d => visitor.visit_i16x8_extadd_pairwise_i8x16_u(), 0x7e => visitor.visit_i32x4_extadd_pairwise_i16x8_s(), 0x7f => visitor.visit_i32x4_extadd_pairwise_i16x8_u(), 0x80 => visitor.visit_i16x8_abs(), 0x81 => visitor.visit_i16x8_neg(), 0x82 => visitor.visit_i16x8_q15mulr_sat_s(), 0x83 => visitor.visit_i16x8_all_true(), 0x84 => visitor.visit_i16x8_bitmask(), 0x85 => visitor.visit_i16x8_narrow_i32x4_s(), 0x86 => visitor.visit_i16x8_narrow_i32x4_u(), 0x87 => visitor.visit_i16x8_extend_low_i8x16_s(), 0x88 => visitor.visit_i16x8_extend_high_i8x16_s(), 0x89 => visitor.visit_i16x8_extend_low_i8x16_u(), 0x8a => visitor.visit_i16x8_extend_high_i8x16_u(), 0x8b => visitor.visit_i16x8_shl(), 0x8c => visitor.visit_i16x8_shr_s(), 0x8d => visitor.visit_i16x8_shr_u(), 0x8e => visitor.visit_i16x8_add(), 0x8f => visitor.visit_i16x8_add_sat_s(), 0x90 => visitor.visit_i16x8_add_sat_u(), 0x91 => visitor.visit_i16x8_sub(), 0x92 => visitor.visit_i16x8_sub_sat_s(), 0x93 => visitor.visit_i16x8_sub_sat_u(), 0x94 => visitor.visit_f64x2_nearest(), 0x95 => visitor.visit_i16x8_mul(), 0x96 => visitor.visit_i16x8_min_s(), 0x97 => visitor.visit_i16x8_min_u(), 0x98 => visitor.visit_i16x8_max_s(), 0x99 => visitor.visit_i16x8_max_u(), 0x9b => visitor.visit_i16x8_avgr_u(), 0x9c => visitor.visit_i16x8_extmul_low_i8x16_s(), 0x9d => visitor.visit_i16x8_extmul_high_i8x16_s(), 0x9e => visitor.visit_i16x8_extmul_low_i8x16_u(), 0x9f => visitor.visit_i16x8_extmul_high_i8x16_u(), 0xa0 => visitor.visit_i32x4_abs(), 0xa1 => visitor.visit_i32x4_neg(), 0xa3 => visitor.visit_i32x4_all_true(), 0xa4 => visitor.visit_i32x4_bitmask(), 0xa7 => visitor.visit_i32x4_extend_low_i16x8_s(), 0xa8 => visitor.visit_i32x4_extend_high_i16x8_s(), 0xa9 => visitor.visit_i32x4_extend_low_i16x8_u(), 0xaa => visitor.visit_i32x4_extend_high_i16x8_u(), 0xab => visitor.visit_i32x4_shl(), 0xac => visitor.visit_i32x4_shr_s(), 0xad => visitor.visit_i32x4_shr_u(), 0xae => visitor.visit_i32x4_add(), 0xb1 => visitor.visit_i32x4_sub(), 0xb5 => visitor.visit_i32x4_mul(), 0xb6 => visitor.visit_i32x4_min_s(), 0xb7 => visitor.visit_i32x4_min_u(), 0xb8 => visitor.visit_i32x4_max_s(), 0xb9 => visitor.visit_i32x4_max_u(), 0xba => visitor.visit_i32x4_dot_i16x8_s(), 0xbc => visitor.visit_i32x4_extmul_low_i16x8_s(), 0xbd => visitor.visit_i32x4_extmul_high_i16x8_s(), 0xbe => visitor.visit_i32x4_extmul_low_i16x8_u(), 0xbf => visitor.visit_i32x4_extmul_high_i16x8_u(), 0xc0 => visitor.visit_i64x2_abs(), 0xc1 => visitor.visit_i64x2_neg(), 0xc3 => visitor.visit_i64x2_all_true(), 0xc4 => visitor.visit_i64x2_bitmask(), 0xc7 => visitor.visit_i64x2_extend_low_i32x4_s(), 0xc8 => visitor.visit_i64x2_extend_high_i32x4_s(), 0xc9 => visitor.visit_i64x2_extend_low_i32x4_u(), 0xca => visitor.visit_i64x2_extend_high_i32x4_u(), 0xcb => visitor.visit_i64x2_shl(), 0xcc => visitor.visit_i64x2_shr_s(), 0xcd => visitor.visit_i64x2_shr_u(), 0xce => visitor.visit_i64x2_add(), 0xd1 => visitor.visit_i64x2_sub(), 0xd5 => visitor.visit_i64x2_mul(), 0xd6 => visitor.visit_i64x2_eq(), 0xd7 => visitor.visit_i64x2_ne(), 0xd8 => visitor.visit_i64x2_lt_s(), 0xd9 => visitor.visit_i64x2_gt_s(), 0xda => visitor.visit_i64x2_le_s(), 0xdb => visitor.visit_i64x2_ge_s(), 0xdc => visitor.visit_i64x2_extmul_low_i32x4_s(), 0xdd => visitor.visit_i64x2_extmul_high_i32x4_s(), 0xde => visitor.visit_i64x2_extmul_low_i32x4_u(), 0xdf => visitor.visit_i64x2_extmul_high_i32x4_u(), 0xe0 => visitor.visit_f32x4_abs(), 0xe1 => visitor.visit_f32x4_neg(), 0xe3 => visitor.visit_f32x4_sqrt(), 0xe4 => visitor.visit_f32x4_add(), 0xe5 => visitor.visit_f32x4_sub(), 0xe6 => visitor.visit_f32x4_mul(), 0xe7 => visitor.visit_f32x4_div(), 0xe8 => visitor.visit_f32x4_min(), 0xe9 => visitor.visit_f32x4_max(), 0xea => visitor.visit_f32x4_pmin(), 0xeb => visitor.visit_f32x4_pmax(), 0xec => visitor.visit_f64x2_abs(), 0xed => visitor.visit_f64x2_neg(), 0xef => visitor.visit_f64x2_sqrt(), 0xf0 => visitor.visit_f64x2_add(), 0xf1 => visitor.visit_f64x2_sub(), 0xf2 => visitor.visit_f64x2_mul(), 0xf3 => visitor.visit_f64x2_div(), 0xf4 => visitor.visit_f64x2_min(), 0xf5 => visitor.visit_f64x2_max(), 0xf6 => visitor.visit_f64x2_pmin(), 0xf7 => visitor.visit_f64x2_pmax(), 0xf8 => visitor.visit_i32x4_trunc_sat_f32x4_s(), 0xf9 => visitor.visit_i32x4_trunc_sat_f32x4_u(), 0xfa => visitor.visit_f32x4_convert_i32x4_s(), 0xfb => visitor.visit_f32x4_convert_i32x4_u(), 0xfc => visitor.visit_i32x4_trunc_sat_f64x2_s_zero(), 0xfd => visitor.visit_i32x4_trunc_sat_f64x2_u_zero(), 0xfe => visitor.visit_f64x2_convert_low_i32x4_s(), 0xff => visitor.visit_f64x2_convert_low_i32x4_u(), 0x100 => visitor.visit_i8x16_relaxed_swizzle(), 0x101 => visitor.visit_i32x4_relaxed_trunc_f32x4_s(), 0x102 => visitor.visit_i32x4_relaxed_trunc_f32x4_u(), 0x103 => visitor.visit_i32x4_relaxed_trunc_f64x2_s_zero(), 0x104 => visitor.visit_i32x4_relaxed_trunc_f64x2_u_zero(), 0x105 => visitor.visit_f32x4_relaxed_madd(), 0x106 => visitor.visit_f32x4_relaxed_nmadd(), 0x107 => visitor.visit_f64x2_relaxed_madd(), 0x108 => visitor.visit_f64x2_relaxed_nmadd(), 0x109 => visitor.visit_i8x16_relaxed_laneselect(), 0x10a => visitor.visit_i16x8_relaxed_laneselect(), 0x10b => visitor.visit_i32x4_relaxed_laneselect(), 0x10c => visitor.visit_i64x2_relaxed_laneselect(), 0x10d => visitor.visit_f32x4_relaxed_min(), 0x10e => visitor.visit_f32x4_relaxed_max(), 0x10f => visitor.visit_f64x2_relaxed_min(), 0x110 => visitor.visit_f64x2_relaxed_max(), 0x111 => visitor.visit_i16x8_relaxed_q15mulr_s(), 0x112 => visitor.visit_i16x8_relaxed_dot_i8x16_i7x16_s(), 0x113 => visitor.visit_i32x4_relaxed_dot_i8x16_i7x16_add_s(), _ => bail!(pos, "unknown 0xfd subopcode: 0x{code:x}"), }) } fn visit_0xfe_operator( &mut self, pos: usize, visitor: &mut T, ) -> Result<>::Output> where T: VisitOperator<'a>, { let code = self.read_var_u32()?; Ok(match code { 0x00 => visitor.visit_memory_atomic_notify(self.read_memarg(2)?), 0x01 => visitor.visit_memory_atomic_wait32(self.read_memarg(2)?), 0x02 => visitor.visit_memory_atomic_wait64(self.read_memarg(3)?), 0x03 => { if self.read_u8()? != 0 { bail!(pos, "nonzero byte after `atomic.fence`"); } visitor.visit_atomic_fence() } 0x10 => visitor.visit_i32_atomic_load(self.read_memarg(2)?), 0x11 => visitor.visit_i64_atomic_load(self.read_memarg(3)?), 0x12 => visitor.visit_i32_atomic_load8_u(self.read_memarg(0)?), 0x13 => visitor.visit_i32_atomic_load16_u(self.read_memarg(1)?), 0x14 => visitor.visit_i64_atomic_load8_u(self.read_memarg(0)?), 0x15 => visitor.visit_i64_atomic_load16_u(self.read_memarg(1)?), 0x16 => visitor.visit_i64_atomic_load32_u(self.read_memarg(2)?), 0x17 => visitor.visit_i32_atomic_store(self.read_memarg(2)?), 0x18 => visitor.visit_i64_atomic_store(self.read_memarg(3)?), 0x19 => visitor.visit_i32_atomic_store8(self.read_memarg(0)?), 0x1a => visitor.visit_i32_atomic_store16(self.read_memarg(1)?), 0x1b => visitor.visit_i64_atomic_store8(self.read_memarg(0)?), 0x1c => visitor.visit_i64_atomic_store16(self.read_memarg(1)?), 0x1d => visitor.visit_i64_atomic_store32(self.read_memarg(2)?), 0x1e => visitor.visit_i32_atomic_rmw_add(self.read_memarg(2)?), 0x1f => visitor.visit_i64_atomic_rmw_add(self.read_memarg(3)?), 0x20 => visitor.visit_i32_atomic_rmw8_add_u(self.read_memarg(0)?), 0x21 => visitor.visit_i32_atomic_rmw16_add_u(self.read_memarg(1)?), 0x22 => visitor.visit_i64_atomic_rmw8_add_u(self.read_memarg(0)?), 0x23 => visitor.visit_i64_atomic_rmw16_add_u(self.read_memarg(1)?), 0x24 => visitor.visit_i64_atomic_rmw32_add_u(self.read_memarg(2)?), 0x25 => visitor.visit_i32_atomic_rmw_sub(self.read_memarg(2)?), 0x26 => visitor.visit_i64_atomic_rmw_sub(self.read_memarg(3)?), 0x27 => visitor.visit_i32_atomic_rmw8_sub_u(self.read_memarg(0)?), 0x28 => visitor.visit_i32_atomic_rmw16_sub_u(self.read_memarg(1)?), 0x29 => visitor.visit_i64_atomic_rmw8_sub_u(self.read_memarg(0)?), 0x2a => visitor.visit_i64_atomic_rmw16_sub_u(self.read_memarg(1)?), 0x2b => visitor.visit_i64_atomic_rmw32_sub_u(self.read_memarg(2)?), 0x2c => visitor.visit_i32_atomic_rmw_and(self.read_memarg(2)?), 0x2d => visitor.visit_i64_atomic_rmw_and(self.read_memarg(3)?), 0x2e => visitor.visit_i32_atomic_rmw8_and_u(self.read_memarg(0)?), 0x2f => visitor.visit_i32_atomic_rmw16_and_u(self.read_memarg(1)?), 0x30 => visitor.visit_i64_atomic_rmw8_and_u(self.read_memarg(0)?), 0x31 => visitor.visit_i64_atomic_rmw16_and_u(self.read_memarg(1)?), 0x32 => visitor.visit_i64_atomic_rmw32_and_u(self.read_memarg(2)?), 0x33 => visitor.visit_i32_atomic_rmw_or(self.read_memarg(2)?), 0x34 => visitor.visit_i64_atomic_rmw_or(self.read_memarg(3)?), 0x35 => visitor.visit_i32_atomic_rmw8_or_u(self.read_memarg(0)?), 0x36 => visitor.visit_i32_atomic_rmw16_or_u(self.read_memarg(1)?), 0x37 => visitor.visit_i64_atomic_rmw8_or_u(self.read_memarg(0)?), 0x38 => visitor.visit_i64_atomic_rmw16_or_u(self.read_memarg(1)?), 0x39 => visitor.visit_i64_atomic_rmw32_or_u(self.read_memarg(2)?), 0x3a => visitor.visit_i32_atomic_rmw_xor(self.read_memarg(2)?), 0x3b => visitor.visit_i64_atomic_rmw_xor(self.read_memarg(3)?), 0x3c => visitor.visit_i32_atomic_rmw8_xor_u(self.read_memarg(0)?), 0x3d => visitor.visit_i32_atomic_rmw16_xor_u(self.read_memarg(1)?), 0x3e => visitor.visit_i64_atomic_rmw8_xor_u(self.read_memarg(0)?), 0x3f => visitor.visit_i64_atomic_rmw16_xor_u(self.read_memarg(1)?), 0x40 => visitor.visit_i64_atomic_rmw32_xor_u(self.read_memarg(2)?), 0x41 => visitor.visit_i32_atomic_rmw_xchg(self.read_memarg(2)?), 0x42 => visitor.visit_i64_atomic_rmw_xchg(self.read_memarg(3)?), 0x43 => visitor.visit_i32_atomic_rmw8_xchg_u(self.read_memarg(0)?), 0x44 => visitor.visit_i32_atomic_rmw16_xchg_u(self.read_memarg(1)?), 0x45 => visitor.visit_i64_atomic_rmw8_xchg_u(self.read_memarg(0)?), 0x46 => visitor.visit_i64_atomic_rmw16_xchg_u(self.read_memarg(1)?), 0x47 => visitor.visit_i64_atomic_rmw32_xchg_u(self.read_memarg(2)?), 0x48 => visitor.visit_i32_atomic_rmw_cmpxchg(self.read_memarg(2)?), 0x49 => visitor.visit_i64_atomic_rmw_cmpxchg(self.read_memarg(3)?), 0x4a => visitor.visit_i32_atomic_rmw8_cmpxchg_u(self.read_memarg(0)?), 0x4b => visitor.visit_i32_atomic_rmw16_cmpxchg_u(self.read_memarg(1)?), 0x4c => visitor.visit_i64_atomic_rmw8_cmpxchg_u(self.read_memarg(0)?), 0x4d => visitor.visit_i64_atomic_rmw16_cmpxchg_u(self.read_memarg(1)?), 0x4e => visitor.visit_i64_atomic_rmw32_cmpxchg_u(self.read_memarg(2)?), // Decode shared-everything-threads proposal. 0x4f => visitor.visit_global_atomic_get(self.read_ordering()?, self.read_var_u32()?), 0x50 => visitor.visit_global_atomic_set(self.read_ordering()?, self.read_var_u32()?), 0x51 => { visitor.visit_global_atomic_rmw_add(self.read_ordering()?, self.read_var_u32()?) } 0x52 => { visitor.visit_global_atomic_rmw_sub(self.read_ordering()?, self.read_var_u32()?) } 0x53 => { visitor.visit_global_atomic_rmw_and(self.read_ordering()?, self.read_var_u32()?) } 0x54 => visitor.visit_global_atomic_rmw_or(self.read_ordering()?, self.read_var_u32()?), 0x55 => { visitor.visit_global_atomic_rmw_xor(self.read_ordering()?, self.read_var_u32()?) } 0x56 => { visitor.visit_global_atomic_rmw_xchg(self.read_ordering()?, self.read_var_u32()?) } 0x57 => { visitor.visit_global_atomic_rmw_cmpxchg(self.read_ordering()?, self.read_var_u32()?) } 0x58 => visitor.visit_table_atomic_get(self.read_ordering()?, self.read_var_u32()?), 0x59 => visitor.visit_table_atomic_set(self.read_ordering()?, self.read_var_u32()?), 0x5a => { visitor.visit_table_atomic_rmw_xchg(self.read_ordering()?, self.read_var_u32()?) } 0x5b => { visitor.visit_table_atomic_rmw_cmpxchg(self.read_ordering()?, self.read_var_u32()?) } 0x5c => visitor.visit_struct_atomic_get( self.read_ordering()?, self.read_var_u32()?, self.read_var_u32()?, ), 0x5d => visitor.visit_struct_atomic_get_s( self.read_ordering()?, self.read_var_u32()?, self.read_var_u32()?, ), 0x5e => visitor.visit_struct_atomic_get_u( self.read_ordering()?, self.read_var_u32()?, self.read_var_u32()?, ), 0x5f => visitor.visit_struct_atomic_set( self.read_ordering()?, self.read_var_u32()?, self.read_var_u32()?, ), 0x60 => visitor.visit_struct_atomic_rmw_add( self.read_ordering()?, self.read_var_u32()?, self.read_var_u32()?, ), 0x61 => visitor.visit_struct_atomic_rmw_sub( self.read_ordering()?, self.read_var_u32()?, self.read_var_u32()?, ), 0x62 => visitor.visit_struct_atomic_rmw_and( self.read_ordering()?, self.read_var_u32()?, self.read_var_u32()?, ), 0x63 => visitor.visit_struct_atomic_rmw_or( self.read_ordering()?, self.read_var_u32()?, self.read_var_u32()?, ), 0x64 => visitor.visit_struct_atomic_rmw_xor( self.read_ordering()?, self.read_var_u32()?, self.read_var_u32()?, ), 0x65 => visitor.visit_struct_atomic_rmw_xchg( self.read_ordering()?, self.read_var_u32()?, self.read_var_u32()?, ), 0x66 => visitor.visit_struct_atomic_rmw_cmpxchg( self.read_ordering()?, self.read_var_u32()?, self.read_var_u32()?, ), 0x67 => visitor.visit_array_atomic_get(self.read_ordering()?, self.read_var_u32()?), 0x68 => visitor.visit_array_atomic_get_s(self.read_ordering()?, self.read_var_u32()?), 0x69 => visitor.visit_array_atomic_get_u(self.read_ordering()?, self.read_var_u32()?), 0x6a => visitor.visit_array_atomic_set(self.read_ordering()?, self.read_var_u32()?), 0x6b => visitor.visit_array_atomic_rmw_add(self.read_ordering()?, self.read_var_u32()?), 0x6c => visitor.visit_array_atomic_rmw_sub(self.read_ordering()?, self.read_var_u32()?), 0x6d => visitor.visit_array_atomic_rmw_and(self.read_ordering()?, self.read_var_u32()?), 0x6e => visitor.visit_array_atomic_rmw_or(self.read_ordering()?, self.read_var_u32()?), 0x6f => visitor.visit_array_atomic_rmw_xor(self.read_ordering()?, self.read_var_u32()?), 0x70 => { visitor.visit_array_atomic_rmw_xchg(self.read_ordering()?, self.read_var_u32()?) } 0x71 => { visitor.visit_array_atomic_rmw_cmpxchg(self.read_ordering()?, self.read_var_u32()?) } 0x72 => visitor.visit_ref_i31_shared(), _ => bail!(pos, "unknown 0xfe subopcode: 0x{code:x}"), }) } fn read_memarg(&mut self, max_align: u8) -> Result { let flags_pos = self.original_position(); let mut flags = self.read_var_u32()?; let memory = if self.multi_memory() && flags & (1 << 6) != 0 { flags ^= 1 << 6; self.read_var_u32()? } else { 0 }; let max_flag_bits = if self.multi_memory() { 6 } else { 5 }; if flags >= (1 << max_flag_bits) { return Err(BinaryReaderError::new( "malformed memop alignment: alignment too large", flags_pos, )); } let align = flags as u8; let offset = if self.memory64() { self.read_var_u64()? } else { u64::from(self.read_var_u32()?) }; Ok(MemArg { align, max_align, offset, memory, }) } fn read_ordering(&mut self) -> Result { let byte = self.read_var_u32()?; match byte { 0 => Ok(Ordering::SeqCst), 1 => Ok(Ordering::AcqRel), x => Err(BinaryReaderError::new( &format!("invalid atomic consistency ordering {x}"), self.original_position() - 1, )), } } fn read_br_table(&mut self) -> Result> { let cnt = self.read_size(MAX_WASM_BR_TABLE_SIZE, "br_table")?; let reader = self.skip(|reader| { for _ in 0..cnt { reader.read_var_u32()?; } Ok(()) })?; let default = self.read_var_u32()?; Ok(BrTable { reader, cnt: cnt as u32, default, }) } #[cfg(feature = "simd")] fn read_lane_index(&mut self) -> Result { self.read_u8() } #[cfg(feature = "simd")] fn read_v128(&mut self) -> Result { let mut bytes = [0; 16]; bytes.clone_from_slice(self.read_bytes(16)?); Ok(V128(bytes)) } fn read_memory_index_or_zero_if_not_multi_memory(&mut self) -> Result { if self.multi_memory() { self.read_var_u32() } else { // Before bulk memory this byte was required to be a single zero // byte, not a LEB-encoded zero, so require a precise zero byte. match self.read_u8()? { 0 => Ok(0), _ => bail!(self.original_position() - 1, "zero byte expected"), } } } fn read_call_indirect_table_immediate(&mut self) -> Result { // If the `call_indirect_overlong` feature is enabled, then read this // immediate as a LEB. This feature is enabled as part of the // `reference_types` feature or the `lime1` feature. if self.call_indirect_overlong() { return self.read_var_u32(); } // Before reference types this byte was required to be a single zero // byte, not a LEB-encoded zero, so require a precise zero byte. match self.read_u8()? { 0 => Ok(0), _ => bail!(self.original_position() - 1, "zero byte expected"), } } }