diff options
Diffstat (limited to 'third_party/rust/wasmparser/src/binary_reader.rs')
-rw-r--r-- | third_party/rust/wasmparser/src/binary_reader.rs | 1682 |
1 files changed, 1682 insertions, 0 deletions
diff --git a/third_party/rust/wasmparser/src/binary_reader.rs b/third_party/rust/wasmparser/src/binary_reader.rs new file mode 100644 index 0000000000..43fef14cdd --- /dev/null +++ b/third_party/rust/wasmparser/src/binary_reader.rs @@ -0,0 +1,1682 @@ +/* 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::{limits::*, *}; +use std::convert::TryInto; +use std::error::Error; +use std::fmt; +use std::marker; +use std::ops::Range; +use std::str; + +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<BinaryReaderErrorInner>, +} + +#[derive(Debug, Clone)] +pub(crate) struct BinaryReaderErrorInner { + pub(crate) message: String, + pub(crate) offset: usize, + pub(crate) needed_hint: Option<usize>, +} + +/// The result for `BinaryReader` operations. +pub type Result<T, E = BinaryReaderError> = std::result::Result<T, E>; + +impl 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(message: impl Into<String>, offset: usize) -> Self { + let message = message.into(); + BinaryReaderError { + inner: Box::new(BinaryReaderErrorInner { + message, + offset, + needed_hint: None, + }), + } + } + + #[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 { + BinaryReaderError { + inner: Box::new(BinaryReaderErrorInner { + message: "unexpected end-of-file".to_string(), + offset, + needed_hint: Some(needed_hint), + }), + } + } + + /// 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 + } +} + +/// A binary reader of the WebAssembly structures and types. +#[derive(Clone, Debug, Hash)] +pub struct BinaryReader<'a> { + pub(crate) buffer: &'a [u8], + pub(crate) position: usize, + original_offset: usize, + allow_memarg64: bool, +} + +impl<'a> BinaryReader<'a> { + /// Constructs `BinaryReader` type. + /// + /// # Examples + /// ``` + /// let fn_body = &vec![0x41, 0x00, 0x10, 0x00, 0x0B]; + /// let mut reader = wasmparser::BinaryReader::new(fn_body); + /// while !reader.eof() { + /// let op = reader.read_operator(); + /// println!("{:?}", op) + /// } + /// ``` + pub fn new(data: &[u8]) -> BinaryReader { + BinaryReader { + buffer: data, + position: 0, + original_offset: 0, + allow_memarg64: false, + } + } + + /// Constructs a `BinaryReader` with an explicit starting offset. + pub fn new_with_offset(data: &[u8], original_offset: usize) -> BinaryReader { + BinaryReader { + buffer: data, + position: 0, + original_offset, + allow_memarg64: false, + } + } + + /// Gets the original position of the binary reader. + #[inline] + pub fn original_position(&self) -> usize { + self.original_offset + self.position + } + + /// Whether or not to allow 64-bit memory arguments in functions. + /// + /// This is intended to be `true` when support for the memory64 + /// WebAssembly proposal is also enabled. + pub fn allow_memarg64(&mut self, allow: bool) { + self.allow_memarg64 = allow; + } + + /// Returns a range from the starting offset to the end of the buffer. + pub fn range(&self) -> Range<usize> { + 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<T>(&mut self) -> Result<T> + where + T: FromReader<'a>, + { + T::from_reader(self) + } + + pub(crate) fn read_u7(&mut self) -> Result<u8> { + 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<ExternalKind> { + match byte { + 0x00 => Ok(ExternalKind::Func), + 0x01 => Ok(ExternalKind::Table), + 0x02 => Ok(ExternalKind::Memory), + 0x03 => Ok(ExternalKind::Global), + 0x04 => Ok(ExternalKind::Tag), + 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<usize> { + 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<BinaryReaderIter<'a, 'me, T>> + where + T: FromReader<'a>, + { + let size = self.read_size(limit, desc)?; + Ok(BinaryReaderIter { + remaining: size, + reader: self, + _marker: marker::PhantomData, + }) + } + + fn read_first_byte_and_var_u32(&mut self) -> Result<(u8, u32)> { + let pos = self.position; + let val = self.read_var_u32()?; + Ok((self.buffer[pos], val)) + } + + fn read_memarg(&mut self, max_align: u8) -> Result<MemArg> { + let flags_pos = self.original_position(); + let mut flags = self.read_var_u32()?; + let memory = if flags & (1 << 6) != 0 { + flags ^= 1 << 6; + self.read_var_u32()? + } else { + 0 + }; + let align = if flags >= (1 << 6) { + return Err(BinaryReaderError::new("alignment too large", flags_pos)); + } else { + flags as u8 + }; + let offset = if self.allow_memarg64 { + self.read_var_u64()? + } else { + u64::from(self.read_var_u32()?) + }; + Ok(MemArg { + align, + max_align, + offset, + memory, + }) + } + + fn read_br_table(&mut self) -> Result<BrTable<'a>> { + let cnt = self.read_size(MAX_WASM_BR_TABLE_SIZE, "br_table")?; + let start = self.position; + for _ in 0..cnt { + self.read_var_u32()?; + } + let end = self.position; + let default = self.read_var_u32()?; + Ok(BrTable { + reader: BinaryReader::new_with_offset(&self.buffer[start..end], start), + cnt: cnt as u32, + default, + }) + } + + /// 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. + /// + /// Advances the position of this reader by the number of bytes read. + pub fn read_reader(&mut self, err: &str) -> Result<BinaryReader<'a>> { + let size = self.read_var_u32()? as usize; + let body_start = self.position; + let buffer = match self.buffer.get(self.position..).and_then(|s| s.get(..size)) { + Some(buf) => buf, + None => { + return Err(BinaryReaderError::new( + err, + self.original_offset + self.buffer.len(), + )) + } + }; + self.position += size; + Ok(BinaryReader::new_with_offset( + buffer, + self.original_offset + body_start, + )) + } + + /// 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<u32> { + 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<u64> { + 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<u8> { + 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<u32> { + // 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<u32> { + 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<u64> { + // 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<u64> { + 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<Self> { + let start = self.position; + f(self)?; + Ok(BinaryReader::new_with_offset( + &self.buffer[start..self.position], + self.original_offset + start, + )) + } + + /// 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<i32> { + // 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<i32> { + 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<i64> { + // 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<i64> { + 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` up to four bytes to parse a variable + /// length integer as a 32 bit floating point integer, returned as `Ieee32`. + /// # Errors + /// If `BinaryReader` has less than one or up to four bytes remaining, or + /// the integer is larger than 32 bits. + pub fn read_f32(&mut self) -> Result<Ieee32> { + let value = self.read_u32()?; + Ok(Ieee32(value)) + } + + /// Advances the `BinaryReader` up to four bytes to parse a variable + /// length integer as a 32 bit floating point integer, returned as `Ieee32`. + /// # Errors + /// If `BinaryReader` has less than one or up to four bytes remaining, or + /// the integer is larger than 32 bits. + pub fn read_f64(&mut self) -> Result<Ieee64> { + let value = self.read_u64()?; + Ok(Ieee64(value)) + } + + /// 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, + )); + } + let bytes = self.read_bytes(len)?; + str::from_utf8(bytes).map_err(|_| { + BinaryReaderError::new("invalid UTF-8 encoding", self.original_position() - 1) + }) + } + + #[cold] + pub(crate) fn invalid_leading_byte<T>(&self, byte: u8, desc: &str) -> Result<T> { + 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<u8> { + self.ensure_has_byte()?; + Ok(self.buffer[self.position]) + } + + fn read_block_type(&mut self) -> Result<BlockType> { + let b = self.peek()?; + + // Check for empty block + if b == 0x40 { + self.position += 1; + return Ok(BlockType::Empty); + } + + // Check for a block type of form [] -> [t]. + if ValType::is_valtype_byte(b) { + 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(), + )); + } + } + } + + /// 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_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)*) => { + /// $( + /// fn $visit(&mut self $($(,$arg: $argty)*)?) -> Self::Output { + /// println!("{}: {}", self.offset, stringify!($visit)); + /// } + /// )* + /// } + /// } + /// + /// impl<'a> VisitOperator<'a> for Dumper { + /// type Output = (); + /// for_each_operator!(define_visit_operator); + /// } + /// + /// ``` + pub fn visit_operator<T>(&mut self, visitor: &mut T) -> Result<<T as VisitOperator<'a>>::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()?), + 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_byte, table_index) = self.read_first_byte_and_var_u32()?; + visitor.visit_call_indirect(index, table_index, table_byte) + } + 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()?) + } + + 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_byte, mem) = self.read_first_byte_and_var_u32()?; + visitor.visit_memory_size(mem, mem_byte) + } + 0x40 => { + let (mem_byte, mem) = self.read_first_byte_and_var_u32()?; + visitor.visit_memory_grow(mem, mem_byte) + } + + 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_as_non_null(), + 0xd4 => visitor.visit_br_on_null(self.read_var_u32()?), + 0xd6 => visitor.visit_br_on_non_null(self.read_var_u32()?), + + 0xfc => self.visit_0xfc_operator(pos, visitor)?, + 0xfd => self.visit_0xfd_operator(pos, visitor)?, + 0xfe => self.visit_0xfe_operator(pos, visitor)?, + + _ => bail!(pos, "illegal opcode: 0x{code:x}"), + }) + } + + fn visit_0xfc_operator<T>( + &mut self, + pos: usize, + visitor: &mut T, + ) -> Result<<T as VisitOperator<'a>>::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) + } + + _ => bail!(pos, "unknown 0xfc subopcode: 0x{code:x}"), + }) + } + + fn visit_0xfd_operator<T>( + &mut self, + pos: usize, + visitor: &mut T, + ) -> Result<<T as VisitOperator<'a>>::Output> + where + T: VisitOperator<'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}"), + }) + } + + fn visit_0xfe_operator<T>( + &mut self, + pos: usize, + visitor: &mut T, + ) -> Result<<T as VisitOperator<'a>>::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)?), + + _ => 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<Operator<'a>> { + self.visit_operator(&mut OperatorFactory::new()) + } + + fn read_lane_index(&mut self, max: u8) -> Result<u8> { + let index = self.read_u8()?; + if index >= max { + return Err(BinaryReaderError::new( + "invalid lane index", + self.original_position() - 1, + )); + } + Ok(index) + } + + fn read_v128(&mut self) -> Result<V128> { + 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<u32> { + let magic_number = self.read_bytes(4)?; + if magic_number != WASM_MAGIC_NUMBER { + return Err(BinaryReaderError::new( + "magic header not detected: bad magic number", + self.original_position() - 4, + )); + } + 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(()); + } + } + } +} + +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 + /// let buf = [0x0e, 0x02, 0x01, 0x02, 0x00]; + /// let mut reader = wasmparser::BinaryReader::new(&buf); + /// let op = reader.read_operator().unwrap(); + /// if let wasmparser::Operator::BrTable { targets } = op { + /// let targets = targets.targets().collect::<Result<Vec<_>, _>>().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<u32>; + + fn size_hint(&self) -> (usize, Option<usize>) { + 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<Self::Item> { + 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::<Result<Vec<_>>>() { + 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<fn() -> &'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)*) => { + $( + fn $visit(&mut self $($(,$arg: $argty)*)?) -> Operator<'a> { + Operator::$op $({ $($arg),* })? + } + )* + } +} + +impl<'a> VisitOperator<'a> for OperatorFactory<'a> { + type Output = Operator<'a>; + + for_each_operator!(define_visit_operator); +} + +/// Iterator returned from [`BinaryReader::read_iter`]. +pub struct BinaryReaderIter<'a, 'me, T: FromReader<'a>> { + remaining: usize, + reader: &'me mut BinaryReader<'a>, + _marker: marker::PhantomData<T>, +} + +impl<'a, T> Iterator for BinaryReaderIter<'a, '_, T> +where + T: FromReader<'a>, +{ + type Item = Result<T>; + + fn next(&mut self) -> Option<Result<T>> { + if self.remaining == 0 { + None + } else { + let ret = self.reader.read::<T>(); + if ret.is_err() { + self.remaining = 0; + } else { + self.remaining -= 1; + } + Some(ret) + } + } + + fn size_hint(&self) -> (usize, Option<usize>) { + (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() { + // ... + } + } +} |