pub mod add_function;
pub mod add_type;
pub mod codemotion;
pub mod custom;
pub mod function_body_unreachable;
pub mod modify_const_exprs;
pub mod modify_data;
pub mod peephole;
pub mod remove_export;
pub mod remove_item;
pub mod remove_section;
pub mod rename_export;
pub mod snip_function;
pub mod start;
mod translate;
pub use self::translate::Item;
use std::borrow::Cow;
use super::Result;
use crate::WasmMutate;
use wasm_encoder::Module;
use wasmparser::Operator;
pub trait Mutator {
fn can_mutate(&self, config: &WasmMutate) -> bool;
fn mutate<'a>(
&self,
config: &'a mut WasmMutate,
) -> Result<Box<dyn Iterator<Item = Result<Module>> + 'a>>;
fn name(&self) -> Cow<'static, str> {
return std::any::type_name::<Self>().into();
}
}
pub type OperatorAndByteOffset<'a> = (Operator<'a>, usize);
#[cfg(test)]
fn match_mutation<T>(original: &str, mutator: T, expected: &str)
where
T: Mutator + Clone,
{
WasmMutate::default().match_mutation(original, mutator, expected)
}
impl WasmMutate<'_> {
#[cfg(test)]
fn match_mutation<T>(&mut self, original: &str, mutator: T, expected: &str)
where
T: Mutator + Clone,
{
use crate::ErrorKind;
drop(env_logger::try_init());
let original = &wat::parse_str(original).unwrap();
let expected = &wat::parse_str(expected).unwrap();
let expected_text = wasmprinter::print_bytes(expected).unwrap();
let mut config = self.clone();
config.setup(&original).unwrap();
let can_mutate = mutator.can_mutate(&config);
assert!(can_mutate);
let attempts = 2000;
let mut last_mutation = None;
for _ in 0..attempts {
let mutation = match mutator
.clone()
.mutate(&mut config)
.map(|mut mutation| mutation.next())
{
Ok(Some(mutation)) => mutation.unwrap(),
Ok(None) => continue,
Err(e) if matches!(e.kind(), ErrorKind::NoMutationsApplicable) => continue,
Err(e) => panic!("mutation error: {e}"),
};
let mutation_bytes = mutation.finish();
crate::validate(&mutation_bytes);
let text = wasmprinter::print_bytes(mutation_bytes).unwrap();
if text.trim() == expected_text.trim() {
return;
}
log::debug!("skipping mutation {text}");
last_mutation = Some(text);
}
match last_mutation {
Some(mutation) => {
panic!(
"after {attempts} attempts the last mutation:\n{mutation:?}\n\n\
did not match the expected mutation\n{expected_text:?}"
);
}
None => {
panic!("never found any applicable mutations after {attempts} attempts");
}
}
}
}