wasmtime_fuzzing/generators/
component_types.rs1use arbitrary::{Arbitrary, Unstructured};
10use component_fuzz_util::{Declarations, EXPORT_FUNCTION, IMPORT_FUNCTION};
11use std::any::Any;
12use std::fmt::Debug;
13use std::ops::ControlFlow;
14use wasmtime::component::{self, Component, ComponentNamedList, Lift, Linker, Lower, Val};
15use wasmtime::{Config, Engine, Store, StoreContextMut};
16
17const MIN_LIST_LENGTH: u32 = 0;
19
20const MAX_LIST_LENGTH: u32 = 10;
22
23pub fn arbitrary_val(ty: &component::Type, input: &mut Unstructured) -> arbitrary::Result<Val> {
25 use component::Type;
26
27 Ok(match ty {
28 Type::Bool => Val::Bool(input.arbitrary()?),
29 Type::S8 => Val::S8(input.arbitrary()?),
30 Type::U8 => Val::U8(input.arbitrary()?),
31 Type::S16 => Val::S16(input.arbitrary()?),
32 Type::U16 => Val::U16(input.arbitrary()?),
33 Type::S32 => Val::S32(input.arbitrary()?),
34 Type::U32 => Val::U32(input.arbitrary()?),
35 Type::S64 => Val::S64(input.arbitrary()?),
36 Type::U64 => Val::U64(input.arbitrary()?),
37 Type::Float32 => Val::Float32(input.arbitrary()?),
38 Type::Float64 => Val::Float64(input.arbitrary()?),
39 Type::Char => Val::Char(input.arbitrary()?),
40 Type::String => Val::String(input.arbitrary()?),
41 Type::List(list) => {
42 let mut values = Vec::new();
43 input.arbitrary_loop(Some(MIN_LIST_LENGTH), Some(MAX_LIST_LENGTH), |input| {
44 values.push(arbitrary_val(&list.ty(), input)?);
45
46 Ok(ControlFlow::Continue(()))
47 })?;
48
49 Val::List(values.into())
50 }
51 Type::Record(record) => Val::Record(
52 record
53 .fields()
54 .map(|field| Ok((field.name.to_string(), arbitrary_val(&field.ty, input)?)))
55 .collect::<arbitrary::Result<_>>()?,
56 ),
57 Type::Tuple(tuple) => Val::Tuple(
58 tuple
59 .types()
60 .map(|ty| arbitrary_val(&ty, input))
61 .collect::<arbitrary::Result<_>>()?,
62 ),
63 Type::Variant(variant) => {
64 let cases = variant.cases().collect::<Vec<_>>();
65 let case = input.choose(&cases)?;
66 let payload = match &case.ty {
67 Some(ty) => Some(Box::new(arbitrary_val(ty, input)?)),
68 None => None,
69 };
70 Val::Variant(case.name.to_string(), payload)
71 }
72 Type::Enum(en) => {
73 let names = en.names().collect::<Vec<_>>();
74 let name = input.choose(&names)?;
75 Val::Enum(name.to_string())
76 }
77 Type::Option(option) => {
78 let discriminant = input.int_in_range(0..=1)?;
79 Val::Option(match discriminant {
80 0 => None,
81 1 => Some(Box::new(arbitrary_val(&option.ty(), input)?)),
82 _ => unreachable!(),
83 })
84 }
85 Type::Result(result) => {
86 let discriminant = input.int_in_range(0..=1)?;
87 Val::Result(match discriminant {
88 0 => Ok(match result.ok() {
89 Some(ty) => Some(Box::new(arbitrary_val(&ty, input)?)),
90 None => None,
91 }),
92 1 => Err(match result.err() {
93 Some(ty) => Some(Box::new(arbitrary_val(&ty, input)?)),
94 None => None,
95 }),
96 _ => unreachable!(),
97 })
98 }
99 Type::Flags(flags) => Val::Flags(
100 flags
101 .names()
102 .filter_map(|name| {
103 input
104 .arbitrary()
105 .map(|p| if p { Some(name.to_string()) } else { None })
106 .transpose()
107 })
108 .collect::<arbitrary::Result<_>>()?,
109 ),
110
111 Type::Own(_) | Type::Borrow(_) => unreachable!(),
113 })
114}
115
116pub fn static_api_test<'a, P, R>(
119 input: &mut Unstructured<'a>,
120 declarations: &Declarations,
121) -> arbitrary::Result<()>
122where
123 P: ComponentNamedList + Lift + Lower + Clone + PartialEq + Debug + Arbitrary<'a> + 'static,
124 R: ComponentNamedList + Lift + Lower + Clone + PartialEq + Debug + Arbitrary<'a> + 'static,
125{
126 crate::init_fuzzing();
127
128 let mut config = Config::new();
129 config.wasm_component_model(true);
130 config.debug_adapter_modules(input.arbitrary()?);
131 let engine = Engine::new(&config).unwrap();
132 let wat = declarations.make_component();
133 let wat = wat.as_bytes();
134 crate::oracles::log_wasm(wat);
135 let component = Component::new(&engine, wat).unwrap();
136 let mut linker = Linker::new(&engine);
137 linker
138 .root()
139 .func_wrap(
140 IMPORT_FUNCTION,
141 |cx: StoreContextMut<'_, Box<dyn Any>>, params: P| {
142 log::trace!("received parameters {params:?}");
143 let data: &(P, R) = cx.data().downcast_ref().unwrap();
144 let (expected_params, result) = data;
145 assert_eq!(params, *expected_params);
146 log::trace!("returning result {:?}", result);
147 Ok(result.clone())
148 },
149 )
150 .unwrap();
151 let mut store: Store<Box<dyn Any>> = Store::new(&engine, Box::new(()));
152 let instance = linker.instantiate(&mut store, &component).unwrap();
153 let func = instance
154 .get_typed_func::<P, R>(&mut store, EXPORT_FUNCTION)
155 .unwrap();
156
157 while input.arbitrary()? {
158 let params = input.arbitrary::<P>()?;
159 let result = input.arbitrary::<R>()?;
160 *store.data_mut() = Box::new((params.clone(), result.clone()));
161 log::trace!("passing in parameters {params:?}");
162 let actual = func.call(&mut store, params).unwrap();
163 log::trace!("got result {:?}", actual);
164 assert_eq!(actual, result);
165 func.post_return(&mut store).unwrap();
166 }
167
168 Ok(())
169}