use crate::limits::{MAX_WASM_CATCHES, MAX_WASM_HANDLERS};
use crate::prelude::*;
use crate::{BinaryReader, BinaryReaderError, FromReader, Result, ValType};
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum BlockType {
Empty,
Type(ValType),
FuncType(u32),
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum FrameKind {
Block,
If,
Else,
Loop,
TryTable,
LegacyTry,
LegacyCatch,
LegacyCatchAll,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct MemArg {
pub align: u8,
pub max_align: u8,
pub offset: u64,
pub memory: u32,
}
#[derive(Clone)]
pub struct BrTable<'a> {
pub(crate) reader: crate::BinaryReader<'a>,
pub(crate) cnt: u32,
pub(crate) default: u32,
}
impl PartialEq<Self> for BrTable<'_> {
fn eq(&self, other: &Self) -> bool {
self.cnt == other.cnt
&& self.default == other.default
&& self.reader.remaining_buffer() == other.reader.remaining_buffer()
}
}
impl Eq for BrTable<'_> {}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct Ieee32(pub(crate) u32);
impl Ieee32 {
pub fn bits(self) -> u32 {
self.0
}
}
impl From<f32> for Ieee32 {
fn from(value: f32) -> Self {
Ieee32 {
0: u32::from_le_bytes(value.to_le_bytes()),
}
}
}
impl From<Ieee32> for f32 {
fn from(bits: Ieee32) -> f32 {
f32::from_bits(bits.bits())
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct Ieee64(pub(crate) u64);
impl Ieee64 {
pub fn bits(self) -> u64 {
self.0
}
}
impl From<f64> for Ieee64 {
fn from(value: f64) -> Self {
Ieee64 {
0: u64::from_le_bytes(value.to_le_bytes()),
}
}
}
impl From<Ieee64> for f64 {
fn from(bits: Ieee64) -> f64 {
f64::from_bits(bits.bits())
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct V128(pub(crate) [u8; 16]);
impl V128 {
pub fn bytes(&self) -> &[u8; 16] {
&self.0
}
pub fn i128(&self) -> i128 {
i128::from_le_bytes(self.0)
}
}
impl From<V128> for i128 {
fn from(bits: V128) -> i128 {
bits.i128()
}
}
impl From<V128> for u128 {
fn from(bits: V128) -> u128 {
u128::from_le_bytes(bits.0)
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum Ordering {
AcqRel,
SeqCst,
}
macro_rules! define_operator {
($(@$proposal:ident $op:ident $({ $($payload:tt)* })? => $visit:ident ($($ann:tt)*))*) => {
#[derive(Debug, Clone, Eq, PartialEq)]
#[allow(missing_docs)]
pub enum Operator<'a> {
$(
$op $({ $($payload)* })?,
)*
}
}
}
for_each_operator!(define_operator);
#[derive(Clone)]
pub struct OperatorsReader<'a> {
reader: BinaryReader<'a>,
}
impl<'a> OperatorsReader<'a> {
pub(crate) fn new(reader: BinaryReader<'a>) -> OperatorsReader<'a> {
OperatorsReader { reader }
}
pub fn eof(&self) -> bool {
self.reader.eof()
}
pub fn original_position(&self) -> usize {
self.reader.original_position()
}
pub fn ensure_end(&self) -> Result<()> {
if self.eof() {
return Ok(());
}
Err(BinaryReaderError::new(
"unexpected data at the end of operators",
self.reader.original_position(),
))
}
pub fn read(&mut self) -> Result<Operator<'a>> {
self.reader.read_operator()
}
pub fn into_iter_with_offsets(self) -> OperatorsIteratorWithOffsets<'a> {
OperatorsIteratorWithOffsets {
reader: self,
err: false,
}
}
pub fn read_with_offset(&mut self) -> Result<(Operator<'a>, usize)> {
let pos = self.reader.original_position();
Ok((self.read()?, pos))
}
pub fn visit_operator<T>(&mut self, visitor: &mut T) -> Result<<T as VisitOperator<'a>>::Output>
where
T: VisitOperator<'a>,
{
self.reader.visit_operator(visitor)
}
pub fn get_binary_reader(&self) -> BinaryReader<'a> {
self.reader.clone()
}
pub fn is_end_then_eof(&self) -> bool {
self.reader.is_end_then_eof()
}
}
impl<'a> IntoIterator for OperatorsReader<'a> {
type Item = Result<Operator<'a>>;
type IntoIter = OperatorsIterator<'a>;
fn into_iter(self) -> Self::IntoIter {
OperatorsIterator {
reader: self,
err: false,
}
}
}
pub struct OperatorsIterator<'a> {
reader: OperatorsReader<'a>,
err: bool,
}
impl<'a> Iterator for OperatorsIterator<'a> {
type Item = Result<Operator<'a>>;
fn next(&mut self) -> Option<Self::Item> {
if self.err || self.reader.eof() {
return None;
}
let result = self.reader.read();
self.err = result.is_err();
Some(result)
}
}
pub struct OperatorsIteratorWithOffsets<'a> {
reader: OperatorsReader<'a>,
err: bool,
}
impl<'a> Iterator for OperatorsIteratorWithOffsets<'a> {
type Item = Result<(Operator<'a>, usize)>;
fn next(&mut self) -> Option<Self::Item> {
if self.err || self.reader.eof() {
return None;
}
let result = self.reader.read_with_offset();
self.err = result.is_err();
Some(result)
}
}
macro_rules! define_visit_operator {
($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*))*) => {
$(
fn $visit(&mut self $($(,$arg: $argty)*)?) -> Self::Output;
)*
}
}
#[allow(missing_docs)]
pub trait VisitOperator<'a> {
type Output: 'a;
fn visit_operator(&mut self, op: &Operator<'a>) -> Self::Output {
macro_rules! visit_operator {
($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*))*) => {
match op {
$(
Operator::$op $({ $($arg),* })? => self.$visit($($($arg.clone()),*)?),
)*
}
}
}
for_each_operator!(visit_operator)
}
for_each_operator!(define_visit_operator);
}
macro_rules! define_visit_operator_delegate {
($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*))*) => {
$(
fn $visit(&mut self $($(,$arg: $argty)*)?) -> Self::Output {
V::$visit(&mut *self, $($($arg),*)?)
}
)*
}
}
impl<'a, 'b, V: VisitOperator<'a> + ?Sized> VisitOperator<'a> for &'b mut V {
type Output = V::Output;
fn visit_operator(&mut self, op: &Operator<'a>) -> Self::Output {
V::visit_operator(*self, op)
}
for_each_operator!(define_visit_operator_delegate);
}
impl<'a, V: VisitOperator<'a> + ?Sized> VisitOperator<'a> for Box<V> {
type Output = V::Output;
fn visit_operator(&mut self, op: &Operator<'a>) -> Self::Output {
V::visit_operator(&mut *self, op)
}
for_each_operator!(define_visit_operator_delegate);
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct TryTable {
pub ty: BlockType,
pub catches: Vec<Catch>,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[allow(missing_docs)]
pub enum Catch {
One { tag: u32, label: u32 },
OneRef { tag: u32, label: u32 },
All { label: u32 },
AllRef { label: u32 },
}
impl<'a> FromReader<'a> for TryTable {
fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
let ty = reader.read_block_type()?;
let catches = reader
.read_iter(MAX_WASM_CATCHES, "catches")?
.collect::<Result<_>>()?;
Ok(TryTable { ty, catches })
}
}
impl<'a> FromReader<'a> for Catch {
fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
Ok(match reader.read_u8()? {
0x00 => Catch::One {
tag: reader.read_var_u32()?,
label: reader.read_var_u32()?,
},
0x01 => Catch::OneRef {
tag: reader.read_var_u32()?,
label: reader.read_var_u32()?,
},
0x02 => Catch::All {
label: reader.read_var_u32()?,
},
0x03 => Catch::AllRef {
label: reader.read_var_u32()?,
},
x => return reader.invalid_leading_byte(x, "catch"),
})
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ResumeTable {
pub handlers: Vec<Handle>,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[allow(missing_docs)]
pub enum Handle {
OnLabel { tag: u32, label: u32 },
OnSwitch { tag: u32 },
}
impl ResumeTable {
pub fn len(&self) -> usize {
self.handlers.len()
}
}
impl<'a> FromReader<'a> for ResumeTable {
fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
let handlers = reader
.read_iter(MAX_WASM_HANDLERS, "resume table")?
.collect::<Result<_>>()?;
let table = ResumeTable { handlers };
Ok(table)
}
}
impl<'a> FromReader<'a> for Handle {
fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
Ok(match reader.read_u8()? {
0x00 => Handle::OnLabel {
tag: reader.read_var_u32()?,
label: reader.read_var_u32()?,
},
0x01 => Handle::OnSwitch {
tag: reader.read_var_u32()?,
},
x => return reader.invalid_leading_byte(x, "on clause"),
})
}
}