use super::Mutator;
use crate::module::{PrimitiveTypeInfo, TypeInfo};
use crate::{Result, WasmMutate};
use rand::RngExt;
use wasm_encoder::{AbstractHeapType, HeapType, Module};
#[derive(Clone, Copy)]
pub struct AddFunctionMutator;
impl Mutator for AddFunctionMutator {
fn mutate<'a>(
&self,
config: &'a mut WasmMutate,
) -> Result<Box<dyn Iterator<Item = Result<Module>> + 'a>> {
let max_ty_idx = config.info().num_types() - 1;
let ty_idx = config.rng().random_range(0..=max_ty_idx);
let mut func_sec_enc = wasm_encoder::FunctionSection::new();
if let Some(func_sec_idx) = config.info().functions {
let reader = config.info().get_binary_reader(func_sec_idx);
let reader = wasmparser::FunctionSectionReader::new(reader)?;
for x in reader {
func_sec_enc.function(x?);
}
}
func_sec_enc.function(ty_idx);
let mut code_sec_enc = wasm_encoder::CodeSection::new();
if let Some(code_sec_idx) = config.info().code {
let reader = config.info().get_binary_reader(code_sec_idx);
let reader = wasmparser::CodeSectionReader::new(reader)?;
for body in reader {
let body = body?;
code_sec_enc.raw(body.as_bytes());
}
}
let func_ty = match &config.info().types_map[usize::try_from(ty_idx).unwrap()] {
TypeInfo::Func(func_ty) => func_ty,
};
let mut func = wasm_encoder::Function::new(vec![]);
for ty in &func_ty.returns {
match ty {
PrimitiveTypeInfo::I32 => {
func.instructions().i32_const(0);
}
PrimitiveTypeInfo::I64 => {
func.instructions().i64_const(0);
}
PrimitiveTypeInfo::F32 => {
func.instructions().f32_const(0.0.into());
}
PrimitiveTypeInfo::F64 => {
func.instructions().f64_const(0.0.into());
}
PrimitiveTypeInfo::V128 => {
func.instructions().v128_const(0);
}
PrimitiveTypeInfo::FuncRef => {
func.instructions().ref_null(HeapType::Abstract {
shared: false,
ty: AbstractHeapType::Func,
});
}
PrimitiveTypeInfo::ExternRef => {
func.instructions().ref_null(HeapType::Abstract {
shared: false,
ty: AbstractHeapType::Extern,
});
}
PrimitiveTypeInfo::Empty => unreachable!(),
}
}
func.instructions().end();
code_sec_enc.function(&func);
let module = if config.info().functions.is_some() {
config
.info()
.replace_multiple_sections(|_, sec_id, module| match sec_id {
x if x == wasm_encoder::SectionId::Function as u8 => {
module.section(&func_sec_enc);
true
}
x if x == wasm_encoder::SectionId::Code as u8 => {
module.section(&code_sec_enc);
true
}
_ => false,
})
} else {
let mut added_func = false;
let mut added_code = false;
let mut module = config
.info()
.replace_multiple_sections(|_, sec_id, module| {
if !added_func && sec_id >= wasm_encoder::SectionId::Function as u8 {
module.section(&func_sec_enc);
added_func = true;
}
if !added_code
&& sec_id >= wasm_encoder::SectionId::Code as u8
&& sec_id != wasm_encoder::SectionId::DataCount as u8
{
module.section(&code_sec_enc);
added_code = true;
}
sec_id == wasm_encoder::SectionId::Function as u8
|| sec_id == wasm_encoder::SectionId::Code as u8
});
if !added_func {
module.section(&func_sec_enc);
}
if !added_code {
module.section(&code_sec_enc);
}
module
};
Ok(Box::new(std::iter::once(Ok(module))))
}
fn can_mutate<'a>(&self, config: &'a WasmMutate) -> bool {
!config.reduce && config.info().num_types() > 0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_add_first_function() {
crate::mutators::match_mutation(
r#"
(module
(type (;0;) (func (param i32 f32) (result i64)))
)
"#,
AddFunctionMutator,
r#"
(module
(type (;0;) (func (param i32 f32) (result i64)))
(func (;0;) (type 0) (param i32 f32) (result i64)
i64.const 0)
)
"#,
);
}
#[test]
fn test_add_another_function() {
crate::mutators::match_mutation(
r#"
(module
(type (;0;) (func (param i32 f32) (result i64 f64)))
(func (;0;) (type 0) (param i32 f32) (result i64 f64)
i64.const 0
f64.const 0.0
)
)
"#,
AddFunctionMutator,
r#"
(module
(type (;0;) (func (param i32 f32) (result i64 f64)))
(func (;0;) (type 0) (param i32 f32) (result i64 f64)
i64.const 0
f64.const 0.0
)
(func (;0;) (type 0) (param i32 f32) (result i64 f64)
i64.const 0
f64.const 0.0
)
)
"#,
);
}
}