wasmtime_fuzzing/generators/gc_ops/
mutator.rs1use crate::generators::gc_ops::limits::GcOpsLimits;
3use crate::generators::gc_ops::ops::{GcOp, GcOpMutator, GcOps};
4use crate::generators::gc_ops::types::{SubType, TypeId, Types};
5use mutatis::{
6 Candidates, Context, DefaultMutate, Generate, Mutate, Result as MutResult, mutators as m,
7};
8use smallvec::SmallVec;
9use std::collections::BTreeMap;
10
11#[derive(Debug, Default)]
14pub struct TypesMutator;
15
16impl TypesMutator {
17 fn add_type(
20 &mut self,
21 c: &mut Candidates<'_>,
22 types: &mut Types,
23 limits: &GcOpsLimits,
24 ) -> mutatis::Result<()> {
25 if c.shrink() || types.type_defs.len() >= usize::try_from(limits.max_types).unwrap() {
26 return Ok(());
27 }
28
29 if types.rec_groups.is_empty() && limits.max_rec_groups == 0 {
30 return Ok(());
31 }
32
33 c.mutation(|ctx| {
34 let gid = match ctx.rng().choose(types.rec_groups.keys()).copied() {
35 Some(gid) => gid,
36 None => {
37 let new_gid = types.fresh_rec_group_id(ctx.rng());
38 types.insert_rec_group(new_gid);
39 new_gid
40 }
41 };
42
43 let tid = types.fresh_type_id(ctx.rng());
44 let def = m::default::<SubType>().generate(ctx)?;
45
46 types.insert_type(tid, gid, def.is_final, def.supertype, def.composite_type);
47 log::debug!("Added type {tid:?} to rec group {gid:?}");
48 Ok(())
49 })?;
50 Ok(())
51 }
52
53 fn remove_type(&mut self, c: &mut Candidates<'_>, types: &mut Types) -> mutatis::Result<()> {
55 if types.type_defs.is_empty() {
56 return Ok(());
57 }
58 c.mutation(|ctx| {
59 let Some(tid) = ctx.rng().choose(types.type_defs.keys()).copied() else {
60 return Ok(());
61 };
62 types.remove_type(tid);
63 log::debug!("Removed type {tid:?}");
64 Ok(())
65 })?;
66 Ok(())
67 }
68
69 fn swap_within_group(
71 &mut self,
72 c: &mut Candidates<'_>,
73 types: &mut Types,
74 ) -> mutatis::Result<()> {
75 if types.rec_groups.is_empty() || types.type_defs.len() <= 2 {
76 return Ok(());
77 }
78 c.mutation(|ctx| {
79 for _ in 0..16 {
80 let Some(gid) = ctx.rng().choose(types.rec_groups.keys()).copied() else {
81 return Ok(());
82 };
83
84 let Some(member_set) = types.rec_groups.get(&gid) else {
85 continue;
86 };
87 let members: SmallVec<[TypeId; 32]> = member_set.iter().copied().collect();
88 if members.len() < 2 {
89 continue;
90 }
91
92 let Some(tid_a) = ctx.rng().choose(&members).copied() else {
93 return Ok(());
94 };
95 let Some(mut tid_b) = ctx.rng().choose(&members).copied() else {
96 return Ok(());
97 };
98 for _ in 0..members.len() {
99 if tid_a != tid_b {
100 break;
101 }
102 let Some(next_tid) = ctx.rng().choose(&members).copied() else {
103 return Ok(());
104 };
105 tid_b = next_tid;
106 }
107 if tid_a == tid_b {
108 continue;
109 }
110
111 let Some(a_def) = types.type_defs.remove(&tid_a) else {
112 return Ok(());
113 };
114 let Some(b_def) = types.type_defs.remove(&tid_b) else {
115 types.type_defs.insert(tid_a, a_def);
116 return Ok(());
117 };
118 types.type_defs.insert(tid_a, b_def);
119 types.type_defs.insert(tid_b, a_def);
120 log::debug!("Swapped types {tid_a:?} and {tid_b:?} in rec group {gid:?}");
121 return Ok(());
122 }
123 Ok(())
124 })?;
125 Ok(())
126 }
127
128 fn move_between_groups(
130 &mut self,
131 c: &mut Candidates<'_>,
132 types: &mut Types,
133 ) -> mutatis::Result<()> {
134 if types.rec_groups.len() < 2 {
135 return Ok(());
136 }
137 c.mutation(|ctx| {
138 let mut old_gid = None;
139 for _ in 0..16 {
140 let Some(gid) = ctx.rng().choose(types.rec_groups.keys()).copied() else {
141 return Ok(());
142 };
143 if types
144 .rec_groups
145 .get(&gid)
146 .map(|s| !s.is_empty())
147 .unwrap_or(false)
148 {
149 old_gid = Some(gid);
150 break;
151 }
152 }
153 let Some(old_gid) = old_gid else {
154 return Ok(());
155 };
156
157 let Some(tid) = types
158 .rec_groups
159 .get(&old_gid)
160 .and_then(|members| ctx.rng().choose(members.iter()).copied())
161 else {
162 return Ok(());
163 };
164
165 let Some(new_gid) = ctx.rng().choose(types.rec_groups.keys()).copied() else {
166 return Ok(());
167 };
168
169 let Some(old_members) = types.rec_groups.get_mut(&old_gid) else {
170 return Ok(());
171 };
172 old_members.remove(&tid);
173 let Some(new_members) = types.rec_groups.get_mut(&new_gid) else {
174 return Ok(());
175 };
176 new_members.insert(tid);
177 log::debug!("Moved type {tid:?} from {old_gid:?} to {new_gid:?}");
178 Ok(())
179 })?;
180 Ok(())
181 }
182
183 fn duplicate_group(
185 &mut self,
186 c: &mut Candidates<'_>,
187 types: &mut Types,
188 limits: &GcOpsLimits,
189 ) -> mutatis::Result<()> {
190 if c.shrink()
191 || types.rec_groups.is_empty()
192 || types.rec_groups.len() >= usize::try_from(limits.max_rec_groups).unwrap()
193 || types.type_defs.len() >= usize::try_from(limits.max_types).unwrap()
194 {
195 return Ok(());
196 }
197 c.mutation(|ctx| {
198 let Some(src_gid) = ctx.rng().choose(types.rec_groups.keys()).copied() else {
199 return Ok(());
200 };
201 let Some(src_members) = types.rec_groups.get(&src_gid) else {
202 return Ok(());
203 };
204 if src_members.is_empty() {
205 return Ok(());
206 }
207
208 let members: SmallVec<[(TypeId, SubType); 32]> = src_members
210 .iter()
211 .filter_map(|tid| types.type_defs.get(tid).map(|def| (*tid, def.clone())))
212 .collect();
213
214 if members.is_empty() {
215 return Ok(());
216 }
217
218 let new_gid = types.fresh_rec_group_id(ctx.rng());
220 types.insert_rec_group(new_gid);
221
222 let mut old_to_new: BTreeMap<TypeId, TypeId> = BTreeMap::new();
224 for (old_tid, _) in &members {
225 old_to_new.insert(*old_tid, types.fresh_type_id(ctx.rng()));
226 }
227
228 for (old_tid, def) in &members {
230 let new_tid = old_to_new[old_tid];
231 let mapped_super = def.supertype.map(|st| *old_to_new.get(&st).unwrap_or(&st));
232 types.insert_type(
233 new_tid,
234 new_gid,
235 def.is_final,
236 mapped_super,
237 def.composite_type.clone(),
238 );
239 }
240
241 log::debug!(
242 "Duplicated rec group {src_gid:?} as {new_gid:?} ({count} types)",
243 count = members.len()
244 );
245 Ok(())
246 })?;
247 Ok(())
248 }
249
250 fn remove_group(&mut self, c: &mut Candidates<'_>, types: &mut Types) -> mutatis::Result<()> {
252 if types.rec_groups.len() <= 2 {
253 return Ok(());
254 }
255 c.mutation(|ctx| {
256 let Some(gid) = ctx.rng().choose(types.rec_groups.keys()).copied() else {
257 return Ok(());
258 };
259 let Some(members) = types.rec_groups.remove(&gid) else {
260 return Ok(());
261 };
262 for tid in &members {
263 types.type_defs.remove(tid);
264 }
265 log::debug!(
266 "Removed rec group {gid:?} and its {len} member types",
267 len = members.len()
268 );
269 Ok(())
270 })?;
271 Ok(())
272 }
273
274 fn merge_groups(&mut self, c: &mut Candidates<'_>, types: &mut Types) -> mutatis::Result<()> {
276 if types.rec_groups.len() <= 2 {
277 return Ok(());
278 }
279 c.mutation(|ctx| {
280 let Some(dst_gid) = ctx.rng().choose(types.rec_groups.keys()).copied() else {
281 return Ok(());
282 };
283
284 let mut src_gid = None;
286 for _ in 0..16 {
287 let Some(g) = ctx.rng().choose(types.rec_groups.keys()).copied() else {
288 return Ok(());
289 };
290 if g != dst_gid {
291 src_gid = Some(g);
292 break;
293 }
294 }
295 let Some(src_gid) = src_gid else {
296 return Ok(());
297 };
298
299 let Some(src_members) = types.rec_groups.remove(&src_gid) else {
301 return Ok(());
302 };
303 let Some(dst_members) = types.rec_groups.get_mut(&dst_gid) else {
304 return Ok(());
305 };
306 dst_members.extend(src_members.iter());
307 log::debug!("Merged rec group {src_gid:?} into {dst_gid:?}");
308 Ok(())
309 })?;
310 Ok(())
311 }
312
313 fn split_group(
315 &mut self,
316 c: &mut Candidates<'_>,
317 types: &mut Types,
318 limits: &GcOpsLimits,
319 ) -> mutatis::Result<()> {
320 if c.shrink()
321 || types.rec_groups.is_empty()
322 || types.type_defs.len() < 2
323 || types.rec_groups.len() >= usize::try_from(limits.max_rec_groups).unwrap()
324 {
325 return Ok(());
326 }
327 c.mutation(|ctx| {
328 let mut old_gid = None;
330 for _ in 0..16 {
331 let Some(gid) = ctx.rng().choose(types.rec_groups.keys()).copied() else {
332 return Ok(());
333 };
334 if types.rec_groups.get(&gid).map(|s| s.len()).unwrap_or(0) >= 2 {
335 old_gid = Some(gid);
336 break;
337 }
338 }
339 let Some(old_gid) = old_gid else {
340 return Ok(());
341 };
342
343 let new_gid = types.fresh_rec_group_id(ctx.rng());
344 types.insert_rec_group(new_gid);
345
346 let Some(old_members) = types.rec_groups.get(&old_gid) else {
348 return Ok(());
349 };
350 let mut members: SmallVec<[TypeId; 32]> = old_members.iter().copied().collect();
351 let len = members.len();
352
353 let Some(k_minus_1) = ctx.rng().gen_index(len - 1) else {
355 return Ok(());
356 };
357 let k = k_minus_1 + 1;
358
359 for _ in 0..k {
360 let Some(i) = ctx.rng().gen_index(members.len()) else {
361 break;
362 };
363 let tid = members.remove(i);
364 let Some(old_members) = types.rec_groups.get_mut(&old_gid) else {
365 return Ok(());
366 };
367 old_members.remove(&tid);
368 let Some(new_members) = types.rec_groups.get_mut(&new_gid) else {
369 return Ok(());
370 };
371 new_members.insert(tid);
372 }
373
374 log::debug!("Split rec group {old_gid:?}: moved {k} of {len} members into {new_gid:?}");
375 Ok(())
376 })?;
377 Ok(())
378 }
379
380 fn mutate_with_limits(
382 &mut self,
383 c: &mut Candidates<'_>,
384 types: &mut Types,
385 limits: &GcOpsLimits,
386 ) -> mutatis::Result<()> {
387 self.add_type(c, types, limits)?;
388 self.remove_type(c, types)?;
389 self.swap_within_group(c, types)?;
390 self.move_between_groups(c, types)?;
391 self.duplicate_group(c, types, limits)?;
392 self.remove_group(c, types)?;
393 self.merge_groups(c, types)?;
394 self.split_group(c, types, limits)?;
395
396 m::btree_map(m::default::<TypeId>(), m::default::<SubType>())
399 .mutate(c, &mut types.type_defs)?;
400
401 Ok(())
402 }
403}
404
405#[derive(Debug, Default)]
407pub struct GcOpsMutator {
408 types_mutator: TypesMutator,
410}
411
412impl Mutate<GcOp> for GcOpsMutator {
413 fn mutate(&mut self, c: &mut Candidates<'_>, value: &mut GcOp) -> MutResult<()> {
414 c.mutation(|ctx| {
421 *value = GcOpMutator::default().generate(ctx)?;
422 Ok(())
423 })
424 }
425}
426
427impl Generate<GcOp> for GcOpsMutator {
428 fn generate(&mut self, ctx: &mut Context) -> MutResult<GcOp> {
429 GcOpMutator::default().generate(ctx)
430 }
431}
432
433impl DefaultMutate for GcOp {
434 type DefaultMutate = GcOpsMutator;
435}
436
437impl Mutate<GcOps> for GcOpsMutator {
438 fn mutate(&mut self, c: &mut Candidates<'_>, ops: &mut GcOps) -> MutResult<()> {
439 m::default::<GcOpsLimits>()
440 .map(|_ctx, limits: &mut GcOpsLimits| {
441 limits.fixup_limits();
442 Ok(())
443 })
444 .mutate(c, &mut ops.limits)?;
445 m::vec(m::default::<GcOp>()).mutate(c, &mut ops.ops)?;
446 self.types_mutator
447 .mutate_with_limits(c, &mut ops.types, &ops.limits)?;
448 Ok(())
449 }
450}
451
452impl DefaultMutate for GcOps {
453 type DefaultMutate = GcOpsMutator;
454}
455
456impl<'a> arbitrary::Arbitrary<'a> for GcOps {
457 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
458 let mut session = mutatis::Session::new().seed(u.arbitrary()?);
459 session
460 .generate()
461 .map_err(|_| arbitrary::Error::IncorrectFormat)
462 }
463}
464
465impl Generate<GcOps> for GcOpsMutator {
466 fn generate(&mut self, ctx: &mut Context) -> MutResult<GcOps> {
467 let mut ops = GcOps::default();
468 let mut session = mutatis::Session::new().seed(ctx.rng().gen_u64());
469 for _ in 0..2048 {
470 session.mutate(&mut ops)?;
471 }
472 Ok(ops)
473 }
474}