use crate::{Function, Result};
use paste::paste;
use tracing::trace;
use wasmparser::{for_each_operator, BlockType, BrTable, Ieee32, Ieee64, MemArg, VisitOperator};
mod call;
mod control;
mod local;
mod log;
macro_rules! impl_visit_operator {
( @mvp $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident $($rest:tt)* ) => {
impl_visit_operator!($($rest)*);
};
( @$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident $($rest:tt)* ) => {
fn $visit(&mut self $($(, $arg: $argty)*)?) -> Self::Output {
trace!("{}", stringify!($op));
Ok(())
}
impl_visit_operator!($($rest)*);
};
() => {};
}
macro_rules! map_wasm_operators {
(@basic $ty:tt, $wasm:tt, $evm:tt $($arg:ident: $argty:ty),*) => {
paste! {
fn [< visit_ $ty _ $wasm >](&mut self $(,$arg: $argty),*) -> Self::Output {
trace!("{}.{}", stringify!($ty), stringify!($evm));
let before = self.masm.buffer().len();
self.masm.[< _ $evm >]()?;
let instr = self.masm.buffer()[before..].to_vec();
self.backtrace.push(instr);
Ok(())
}
}
};
(@integer32 $wasm:tt, $evm:tt $($arg:ident: $argty:ty),*) => {
map_wasm_operators!(@basic i32, $wasm, $evm $($arg: $argty),*);
};
(@integer64 $wasm:tt, $evm:tt $($arg:ident: $argty:ty),*) => {
map_wasm_operators!(@basic i64, $wasm, $evm $($arg: $argty),*);
};
(@integer $wasm:tt, $evm:tt $($arg:ident: $argty:ty),*) => {
map_wasm_operators!(@integer32 $wasm, $evm $($arg: $argty),*);
map_wasm_operators!(@integer64 $wasm, $evm $($arg: $argty),*);
};
(@xdr $wasm:tt, $evm:tt $($arg:ident: $argty:ty),*) => {
paste!{
map_wasm_operators!(@integer [< $wasm _s >], $evm $($arg: $argty),*);
map_wasm_operators!(@integer [< $wasm _u >], $evm $($arg: $argty),*);
}
};
(@float32 $wasm:tt, $evm:tt $($arg:ident: $argty:ty),*) => {
map_wasm_operators!(@basic f32, $wasm, $evm $($arg: $argty),*);
};
(@float64 $wasm:tt, $evm:tt $($arg:ident: $argty:ty),*) => {
map_wasm_operators!(@basic f64, $wasm, $evm $($arg: $argty),*);
};
(@float $wasm:tt, $evm:tt $($arg:ident: $argty:ty),*) => {
map_wasm_operators!(@float32 $wasm, $evm $($arg: $argty),*);
map_wasm_operators!(@float64 $wasm, $evm $($arg: $argty),*);
};
(@integer_and_float $op:tt $($arg:ident: $argty:ty),*) => {
map_wasm_operators!(@integer $op, $op);
map_wasm_operators!(@float $op, $op);
};
(@field ($($field:ident).*) ($op:tt -> $evm:tt) $($arg:tt: $argty:ty),* ) => {
paste! {
fn [< visit_ $op >](&mut self, $($arg: $argty),*) -> Self::Output {
let mut log = stringify!($op).to_string();
log = log.replace('_', ".");
$(
let fmt = &format!(" {:?}", $arg);
if fmt != " Empty" {
log.push_str(&fmt);
}
)*
trace!("{}", log);
let before = self.masm.buffer().len();
self.$($field.)*[< _ $evm >]($($arg),*)?;
let instr = self.masm.buffer()[before..].to_vec();
self.backtrace.push(instr);
Ok(())
}
}
};
(
all: [$($all:tt),+],
xdr: [$($xdr:tt),+],
integer: [$($integer:tt),+],
integer_and_float: [$($op:tt),+],
float: [$($float:tt),+],
map: {
integer: [$($map_int_wasm:tt => $map_int_evm:tt),+],
},
mem: {
all: [$($mem:tt),+],
integer: [$($mem_integer:tt),+],
integer64: [$($mem_integer64:tt),+],
signed: [$($mem_signed:tt),+],
signed64: [$($mem_signed64:tt),+],
signed_and_float: [$($mem_signed_and_float:tt),+],
},
masm: {
$( $masm:tt $(: { $($marg:ident: $margty:ty),+ })? ),+
},
global: {
$( $global:tt $(: { $($garg:ident: $gargty:ty),+ })? ),+
}
) => {
paste! {
$(map_wasm_operators!(@integer_and_float $op);)+
$(
map_wasm_operators!(@integer [< $all _s >], [< s $all >]);
map_wasm_operators!(@integer [< $all _u >], $all);
map_wasm_operators!(@float $all, $all);
)+
$(map_wasm_operators!(@integer $integer, $integer);)+
$(map_wasm_operators!(@xdr $xdr, $xdr);)+
$(map_wasm_operators!(@float $float, $float);)+
$(
map_wasm_operators!(@integer [< $map_int_wasm _s >], [< s $map_int_evm >]);
map_wasm_operators!(@integer [< $map_int_wasm _u >], $map_int_evm);
)+
$(
map_wasm_operators!(@integer $mem, $mem _arg: MemArg);
map_wasm_operators!(@float $mem, $mem _arg: MemArg);
)+
$(
map_wasm_operators!(@xdr $mem_integer, $mem_integer _arg: MemArg);
)+
$(
map_wasm_operators!(@integer64 [< $mem_integer64 _s >], $mem_integer64 _arg: MemArg );
map_wasm_operators!(@integer64 [< $mem_integer64 _u >], $mem_integer64 _arg: MemArg );
)+
$(
map_wasm_operators!(@integer $mem_signed, $mem_signed _arg: MemArg);
)+
$(
map_wasm_operators!(@integer $mem_signed_and_float, $mem_signed_and_float _arg: MemArg);
map_wasm_operators!(@float $mem_signed_and_float, $mem_signed_and_float _arg: MemArg);
)+
$(
map_wasm_operators!(@integer64 $mem_signed64, $mem_signed64 _arg: MemArg);
)+
$(
map_wasm_operators!(@field (masm) ($masm -> $masm) $( $($marg: $margty),+ )?);
)+
$(
map_wasm_operators!(@field () ($global -> $global) $( $($garg: $gargty),+ )?);
)+
}
};
}
impl<'a> VisitOperator<'a> for Function {
type Output = Result<()>;
for_each_operator!(impl_visit_operator);
map_wasm_operators! {
all: [div, lt, gt, ge, le],
xdr: [shr, trunc_f32, trunc_f64],
integer: [and, clz, ctz, eqz, or, popcnt, rotl, rotr, shl, xor],
integer_and_float: [add, sub, mul, eq, ne],
float: [
abs, ceil, copysign, floor, max, min, nearest, neg, sqrt,
convert_i32_s, convert_i32_u, convert_i64_s, convert_i64_u,
trunc
],
map: {
integer: [rem => mod],
},
mem: {
all: [load],
integer: [load8, load16],
integer64: [load32],
signed: [store8, store16],
signed64: [store32],
signed_and_float: [store],
},
masm: {
drop,
memory_grow: {
mem: u32,
mem_byte: u8
},
memory_size: {
mem: u32,
mem_byte: u8
},
i32_const: {
value: i32
},
i64_const: {
value: i64
},
f32_const: {
value: Ieee32
},
f64_const: {
value: Ieee64
},
i32_wrap_i64,
i64_extend_i32_s,
i64_extend_i32_u,
f32_demote_f64,
f64_promote_f32,
i32_reinterpret_f32,
i64_reinterpret_f64,
f32_reinterpret_i32,
f64_reinterpret_i64,
return
},
global: {
else, select, end, nop, unreachable,
if: {
blockty: BlockType
},
block: {
blockty: BlockType
},
loop: {
blockty: BlockType
},
br: {
relative_depth: u32
},
br_if: {
relative_depth: u32
},
br_table: {
table: BrTable<'_>
},
local_get: {
local_index: u32
},
local_set: {
local_index: u32
},
local_tee: {
local_index: u32
},
global_get: {
global_index: u32
},
global_set: {
global_index: u32
},
call: {
func_index: u32
},
call_indirect: {
type_index: u32,
table_index: u32,
table_byte: u8
}
}
}
}