wasmtime_fuzzing/generators/
component_types.rs1use arbitrary::{Arbitrary, Unstructured};
10use std::any::Any;
11use std::fmt::Debug;
12use std::ops::ControlFlow;
13use wasmtime::component::{self, Component, ComponentNamedList, Lift, Linker, Lower, Val};
14use wasmtime::{Config, Engine, Store, StoreContextMut};
15use wasmtime_test_util::component_fuzz::{Declarations, EXPORT_FUNCTION, IMPORT_FUNCTION};
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)
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(_) | Type::Future(_) | Type::Stream(_) | Type::ErrorContext => {
113 unreachable!()
114 }
115 })
116}
117
118pub fn static_api_test<'a, P, R>(
121 input: &mut Unstructured<'a>,
122 declarations: &Declarations,
123) -> arbitrary::Result<()>
124where
125 P: ComponentNamedList
126 + Lift
127 + Lower
128 + Clone
129 + PartialEq
130 + Debug
131 + Arbitrary<'a>
132 + Send
133 + Sync
134 + 'static,
135 R: ComponentNamedList
136 + Lift
137 + Lower
138 + Clone
139 + PartialEq
140 + Debug
141 + Arbitrary<'a>
142 + Send
143 + Sync
144 + 'static,
145{
146 crate::init_fuzzing();
147
148 let mut config = Config::new();
149 config.wasm_component_model(true);
150 config.debug_adapter_modules(input.arbitrary()?);
151 let engine = Engine::new(&config).unwrap();
152 let wat = declarations.make_component();
153 let wat = wat.as_bytes();
154 crate::oracles::log_wasm(wat);
155 let component = Component::new(&engine, wat).unwrap();
156 let mut linker = Linker::new(&engine);
157 linker
158 .root()
159 .func_wrap(
160 IMPORT_FUNCTION,
161 |cx: StoreContextMut<'_, Box<dyn Any + Send>>, params: P| {
162 log::trace!("received parameters {params:?}");
163 let data: &(P, R) = cx.data().downcast_ref().unwrap();
164 let (expected_params, result) = data;
165 assert_eq!(params, *expected_params);
166 log::trace!("returning result {result:?}");
167 Ok(result.clone())
168 },
169 )
170 .unwrap();
171 let mut store: Store<Box<dyn Any + Send>> = Store::new(&engine, Box::new(()));
172 let instance = linker.instantiate(&mut store, &component).unwrap();
173 let func = instance
174 .get_typed_func::<P, R>(&mut store, EXPORT_FUNCTION)
175 .unwrap();
176
177 while input.arbitrary()? {
178 let params = input.arbitrary::<P>()?;
179 let result = input.arbitrary::<R>()?;
180 *store.data_mut() = Box::new((params.clone(), result.clone()));
181 log::trace!("passing in parameters {params:?}");
182 let actual = func.call(&mut store, params).unwrap();
183 log::trace!("got result {actual:?}");
184 assert_eq!(actual, result);
185 func.post_return(&mut store).unwrap();
186 }
187
188 Ok(())
189}