use crate::ir::module::module_types::HeapType;
use crate::ir::types::{BlockType, FuncInstrMode, InstrumentationMode};
use crate::Location;
use wasmparser::Operator;
macro_rules! wirm_ty {
(function_index, $orig:ty) => {
crate::ir::id::FunctionID
};
(local_index, $orig:ty) => {
crate::ir::id::LocalID
};
(global_index, $orig:ty) => {
crate::ir::id::GlobalID
};
(type_index, $orig:ty) => {
crate::ir::id::TypeID
};
(table_index, $orig:ty) => {
crate::ir::id::TableID
};
(table, $orig:ty) => {
crate::ir::id::TableID
};
(dst_table, $orig:ty) => {
crate::ir::id::TableID
};
(src_table, $orig:ty) => {
crate::ir::id::TableID
};
(mem, $orig:ty) => {
crate::ir::id::MemoryID
};
(dst_mem, $orig:ty) => {
crate::ir::id::MemoryID
};
(src_mem, $orig:ty) => {
crate::ir::id::MemoryID
};
(data_index, $orig:ty) => {
crate::ir::id::DataSegmentID
};
(array_data_index, $orig:ty) => {
crate::ir::id::DataSegmentID
};
(elem_index, $orig:ty) => {
crate::ir::id::ElementID
};
(array_elem_index, $orig:ty) => {
crate::ir::id::ElementID
};
(struct_type_index, $orig:ty) => {
crate::ir::id::TypeID
};
(array_type_index, $orig:ty) => {
crate::ir::id::TypeID
};
(array_type_index_dst, $orig:ty) => {
crate::ir::id::TypeID
};
(array_type_index_src, $orig:ty) => {
crate::ir::id::TypeID
};
(field_index, $orig:ty) => {
crate::ir::id::FieldID
};
($name:ident, $orig:ty) => {
$orig
};
}
macro_rules! wirm_val {
(function_index, $arg:ident) => {
*$arg
};
(local_index, $arg:ident) => {
*$arg
};
(global_index, $arg:ident) => {
*$arg
};
(type_index, $arg:ident) => {
*$arg
};
(table_index, $arg:ident) => {
*$arg
};
(table, $arg:ident) => {
*$arg
};
(dst_table, $arg:ident) => {
*$arg
};
(src_table, $arg:ident) => {
*$arg
};
(mem, $arg:ident) => {
*$arg
};
(dst_mem, $arg:ident) => {
*$arg
};
(src_mem, $arg:ident) => {
*$arg
};
(data_index, $arg:ident) => {
*$arg
};
(array_data_index, $arg:ident) => {
*$arg
};
(elem_index, $arg:ident) => {
*$arg
};
(array_elem_index, $arg:ident) => {
*$arg
};
(struct_type_index, $arg:ident) => {
*$arg
};
(array_type_index, $arg:ident) => {
*$arg
};
(array_type_index_dst, $arg:ident) => {
*$arg
};
(array_type_index_src, $arg:ident) => {
*$arg
};
(field_index, $arg:ident) => {
*$arg
};
($name:ident, $arg:ident) => {
$arg
};
}
macro_rules! define_opcode_methods {
($( @$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*))*) => {
$( define_opcode_methods!(one @$proposal $op $({ $($arg: $argty),* })? => $visit); )*
};
(one @stack_switching $($rest:tt)*) => {};
(one @shared_everything_threads $($rest:tt)*) => {};
(one @mvp Block $($rest:tt)*) => {};
(one @mvp Loop $($rest:tt)*) => {};
(one @mvp If $($rest:tt)*) => {};
(one @mvp Else $($rest:tt)*) => {};
(one @mvp Return $($rest:tt)*) => {};
(one @mvp BrTable $($rest:tt)*) => {};
(one @mvp F32Const $($rest:tt)*) => {};
(one @mvp F64Const $($rest:tt)*) => {};
(one @reference_types RefNull $($rest:tt)*) => {};
(one @reference_types TypedSelect $($rest:tt)*) => {};
(one @reference_types TypedSelectMulti $($rest:tt)*) => {};
(one @exceptions TryTable $($rest:tt)*) => {};
(one @legacy_exceptions Try $($rest:tt)*) => {};
(one @gc RefTestNonNull $($rest:tt)*) => {};
(one @gc RefTestNullable $($rest:tt)*) => {};
(one @gc RefCastNonNull $($rest:tt)*) => {};
(one @gc RefCastNullable $($rest:tt)*) => {};
(one @gc BrOnCast $($rest:tt)*) => {};
(one @gc BrOnCastFail $($rest:tt)*) => {};
(one @custom_descriptors RefCastDescEqNonNull $($rest:tt)*) => {};
(one @custom_descriptors RefCastDescEqNullable $($rest:tt)*) => {};
(one @custom_descriptors BrOnCastDescEq $($rest:tt)*) => {};
(one @custom_descriptors BrOnCastDescEqFail $($rest:tt)*) => {};
(one @$_proposal:ident $op:ident => $visit:ident) => {
paste::paste! {
fn [<$op:snake>](&mut self) -> &mut Self {
self.inject(Operator::$op);
self
}
}
};
(one @$_proposal:ident $op:ident { $($arg:ident: $argty:ty),* } => $visit:ident) => {
paste::paste! {
fn [<$op:snake>](&mut self, $($arg: wirm_ty!($arg, $argty)),*) -> &mut Self {
self.inject(Operator::$op { $($arg: wirm_val!($arg, $arg)),* });
self
}
}
};
}
pub trait Instrumenter<'a> {
fn finish_instr(&mut self);
fn curr_instrument_mode(&self) -> Option<InstrumentationMode>;
fn set_instrument_mode_at(&mut self, mode: InstrumentationMode, loc: Location);
fn curr_func_instrument_mode(&self) -> &Option<FuncInstrMode>;
fn set_func_instrument_mode(&mut self, mode: FuncInstrMode);
fn curr_instr_len(&self) -> usize;
fn func_entry(&mut self) -> &mut Self {
self.set_func_instrument_mode(FuncInstrMode::Entry);
self
}
fn func_exit(&mut self) -> &mut Self {
self.set_func_instrument_mode(FuncInstrMode::Exit);
self
}
fn clear_instr_at(&mut self, loc: Location, mode: InstrumentationMode);
fn add_instr_at(&mut self, loc: Location, instr: Operator<'a>);
fn before_at(&mut self, loc: Location) -> &mut Self {
self.set_instrument_mode_at(InstrumentationMode::Before, loc);
self
}
fn after_at(&mut self, loc: Location) -> &mut Self {
self.set_instrument_mode_at(InstrumentationMode::After, loc);
self
}
fn alternate_at(&mut self, loc: Location) -> &mut Self {
self.set_instrument_mode_at(InstrumentationMode::Alternate, loc);
self
}
fn empty_alternate_at(&mut self, loc: Location) -> &mut Self;
fn semantic_after_at(&mut self, loc: Location) -> &mut Self {
self.set_instrument_mode_at(InstrumentationMode::SemanticAfter, loc);
self
}
fn block_entry_at(&mut self, loc: Location) -> &mut Self {
self.set_instrument_mode_at(InstrumentationMode::BlockEntry, loc);
self
}
fn block_exit_at(&mut self, loc: Location) -> &mut Self {
self.set_instrument_mode_at(InstrumentationMode::BlockExit, loc);
self
}
fn block_alt_at(&mut self, loc: Location) -> &mut Self {
self.set_instrument_mode_at(InstrumentationMode::BlockAlt, loc);
self
}
fn empty_block_alt_at(&mut self, loc: Location) -> &mut Self;
fn append_tag_at(&mut self, data: Vec<u8>, loc: Location) -> &mut Self;
}
pub trait Inject<'a> {
fn inject(&mut self, instr: Operator<'a>);
fn inject_all(&mut self, instrs: &[Operator<'a>]) -> &mut Self {
instrs.iter().for_each(|instr| {
self.inject(instr.to_owned());
});
self
}
}
pub trait InjectAt<'a> {
fn inject_at(&mut self, idx: usize, mode: InstrumentationMode, instr: Operator<'a>);
}
#[allow(dead_code)]
pub trait Opcode<'a>: Inject<'a> {
wasmparser::for_each_operator!(define_opcode_methods);
fn block(&mut self, block_type: BlockType) -> &mut Self {
self.inject(Operator::Block {
blockty: wasmparser::BlockType::from(block_type),
});
self
}
fn loop_stmt(&mut self, block_type: BlockType) -> &mut Self {
self.inject(Operator::Loop {
blockty: wasmparser::BlockType::from(block_type),
});
self
}
fn if_stmt(&mut self, block_type: BlockType) -> &mut Self {
self.inject(Operator::If {
blockty: wasmparser::BlockType::from(block_type),
});
self
}
fn else_stmt(&mut self) -> &mut Self {
self.inject(Operator::Else);
self
}
fn return_stmt(&mut self) -> &mut Self {
self.inject(Operator::Return);
self
}
fn f32_const(&mut self, val: f32) -> &mut Self {
self.inject(Operator::F32Const {
value: wasmparser::Ieee32::from(val),
});
self
}
fn f64_const(&mut self, val: f64) -> &mut Self {
self.inject(Operator::F64Const {
value: wasmparser::Ieee64::from(val),
});
self
}
fn br_table(&mut self, default: u32, labels: impl IntoIterator<Item = u32>) -> &mut Self {
use std::borrow::Cow;
use wasm_encoder::{Encode, Instruction};
use wasmparser::{BinaryReader, OperatorsReader};
let labels: Vec<u32> = labels.into_iter().collect();
let mut bytes = Vec::new();
Instruction::BrTable(Cow::Borrowed(&labels), default).encode(&mut bytes);
let bytes: &'static [u8] = Box::leak(bytes.into_boxed_slice());
let mut reader = OperatorsReader::new(BinaryReader::new(bytes, 0));
let op = reader
.read()
.expect("round-tripping our own br_table encoding must succeed");
self.inject(op);
self
}
fn ref_null(&mut self, heap_type: HeapType) -> &mut Self {
self.inject(Operator::RefNull {
hty: wasmparser::HeapType::from(heap_type),
});
self
}
fn ref_test(&mut self, heap_type: HeapType) -> &mut Self {
self.inject(Operator::RefTestNonNull {
hty: wasmparser::HeapType::from(heap_type),
});
self
}
fn ref_test_null(&mut self, heap_type: HeapType) -> &mut Self {
self.inject(Operator::RefTestNullable {
hty: wasmparser::HeapType::from(heap_type),
});
self
}
fn ref_cast(&mut self, heap_type: HeapType) -> &mut Self {
self.inject(Operator::RefCastNonNull {
hty: wasmparser::HeapType::from(heap_type),
});
self
}
fn ref_cast_null(&mut self, heap_type: HeapType) -> &mut Self {
self.inject(Operator::RefCastNullable {
hty: wasmparser::HeapType::from(heap_type),
});
self
}
fn ref_cast_desc(&mut self, heap_type: HeapType) -> &mut Self {
self.inject(Operator::RefCastDescEqNonNull {
hty: wasmparser::HeapType::from(heap_type),
});
self
}
fn ref_cast_desc_null(&mut self, heap_type: HeapType) -> &mut Self {
self.inject(Operator::RefCastDescEqNullable {
hty: wasmparser::HeapType::from(heap_type),
});
self
}
fn typed_select(&mut self, ty: wasmparser::ValType) -> &mut Self {
self.inject(Operator::TypedSelect { ty });
self
}
fn typed_select_multi(&mut self, tys: Vec<wasmparser::ValType>) -> &mut Self {
self.inject(Operator::TypedSelectMulti { tys });
self
}
fn br_on_cast(
&mut self,
relative_depth: u32,
from_ref_type: wasmparser::RefType,
to_ref_type: wasmparser::RefType,
) -> &mut Self {
self.inject(Operator::BrOnCast {
relative_depth,
from_ref_type,
to_ref_type,
});
self
}
fn br_on_cast_fail(
&mut self,
relative_depth: u32,
from_ref_type: wasmparser::RefType,
to_ref_type: wasmparser::RefType,
) -> &mut Self {
self.inject(Operator::BrOnCastFail {
relative_depth,
from_ref_type,
to_ref_type,
});
self
}
fn br_on_cast_desc(
&mut self,
relative_depth: u32,
from_ref_type: wasmparser::RefType,
to_ref_type: wasmparser::RefType,
) -> &mut Self {
self.inject(Operator::BrOnCastDescEq {
relative_depth,
from_ref_type,
to_ref_type,
});
self
}
fn br_on_cast_desc_fail(
&mut self,
relative_depth: u32,
from_ref_type: wasmparser::RefType,
to_ref_type: wasmparser::RefType,
) -> &mut Self {
self.inject(Operator::BrOnCastDescEqFail {
relative_depth,
from_ref_type,
to_ref_type,
});
self
}
fn try_table(&mut self, ty: BlockType, catches: Vec<wasmparser::Catch>) -> &mut Self {
self.inject(Operator::TryTable {
try_table: wasmparser::TryTable {
ty: wasmparser::BlockType::from(ty),
catches,
},
});
self
}
fn try_stmt(&mut self, block_type: BlockType) -> &mut Self {
self.inject(Operator::Try {
blockty: wasmparser::BlockType::from(block_type),
});
self
}
}
#[allow(dead_code)]
pub trait MacroOpcode<'a>: Inject<'a> {
fn u32_const(&mut self, value: u32) -> &mut Self {
let i32_val = value as i32;
self.inject(Operator::I32Const { value: i32_val });
self
}
fn u64_const(&mut self, value: u64) -> &mut Self {
let i64_val = value as i64;
self.inject(Operator::I64Const { value: i64_val });
self
}
}