/* 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, } #[derive(Debug, Clone)] pub(crate) struct BinaryReaderErrorInner { pub(crate) message: String, pub(crate) offset: usize, pub(crate) needed_hint: Option, } /// The result for `BinaryReader` operations. pub type Result = std::result::Result; 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, 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 { 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), 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, }) } 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 { 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> { 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> { 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 { 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)?; 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 { // 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` 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 { 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 { 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(&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]) } fn read_block_type(&mut self) -> Result { 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(&mut self, visitor: &mut T) -> Result<>::Output> where T: VisitOperator<'a>, { let pos = self.original_position(); let code = self.read_u8()? as u8; Ok(match code { 0x00 => visitor.visit_unreachable(), 0x01 => visitor.visit_nop(), 0x02 => visitor.visit_block(self.read_block_type()?), 0x03 => visitor.visit_loop(self.read_block_type()?), 0x04 => visitor.visit_if(self.read_block_type()?), 0x05 => visitor.visit_else(), 0x06 => visitor.visit_try(self.read_block_type()?), 0x07 => visitor.visit_catch(self.read_var_u32()?), 0x08 => visitor.visit_throw(self.read_var_u32()?), 0x09 => visitor.visit_rethrow(self.read_var_u32()?), 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( &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) } _ => bail!(pos, "unknown 0xfc subopcode: 0x{code:x}"), }) } fn visit_0xfd_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_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( &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)?), _ => bail!(pos, "unknown 0xfe subopcode: 0x{code:x}"), }) } /// Reads the next available `Operator`. /// /// # Errors /// /// If `BinaryReader` has less bytes remaining than required to parse /// the `Operator`. pub fn read_operator(&mut self) -> Result> { self.visit_operator(&mut OperatorFactory::new()) } fn read_lane_index(&mut self, max: u8) -> Result { let index = self.read_u8()?; if index >= max { return Err(BinaryReaderError::new( "invalid lane index", self.original_position() - 1, )); } Ok(index) } fn read_v128(&mut self) -> Result { let mut bytes = [0; 16]; bytes.clone_from_slice(self.read_bytes(16)?); Ok(V128(bytes)) } pub(crate) fn read_header_version(&mut self) -> Result { let magic_number = self.read_bytes(4)?; if magic_number != WASM_MAGIC_NUMBER { 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::, _>>().unwrap(); /// assert_eq!(targets, [1, 2]); /// } /// ``` pub fn targets(&self) -> BrTableTargets { BrTableTargets { reader: self.reader.clone(), remaining: self.cnt, } } } /// An iterator over the targets of a [`BrTable`]. /// /// # Note /// /// This iterator parses each target of the underlying `br_table` /// except for the default target. /// The iterator will yield exactly as many targets as the `br_table` has. pub struct BrTableTargets<'a> { reader: crate::BinaryReader<'a>, remaining: u32, } impl<'a> Iterator for BrTableTargets<'a> { type Item = Result; fn size_hint(&self) -> (usize, Option) { let remaining = usize::try_from(self.remaining).unwrap_or_else(|error| { panic!("could not convert remaining `u32` into `usize`: {}", error) }); (remaining, Some(remaining)) } fn next(&mut self) -> Option { if self.remaining == 0 { if !self.reader.eof() { return Some(Err(BinaryReaderError::new( "trailing data in br_table", self.reader.original_position(), ))); } return None; } self.remaining -= 1; Some(self.reader.read_var_u32()) } } impl fmt::Debug for BrTable<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut f = f.debug_struct("BrTable"); f.field("count", &self.cnt); f.field("default", &self.default); match self.targets().collect::>>() { Ok(targets) => { f.field("targets", &targets); } Err(_) => { f.field("reader", &self.reader); } } f.finish() } } /// A factory to construct [`Operator`] instances via the [`VisitOperator`] trait. struct OperatorFactory<'a> { marker: core::marker::PhantomData &'a ()>, } impl<'a> OperatorFactory<'a> { /// Creates a new [`OperatorFactory`]. fn new() -> Self { Self { marker: core::marker::PhantomData, } } } macro_rules! define_visit_operator { ($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident)*) => { $( 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, } 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() { // ... } } }