1 //! Types for the `gc` operations.
2 
3 use crate::generators::gc_ops::limits::GcOpsLimits;
4 use crate::generators::gc_ops::ops::GcOp;
5 use serde::{Deserialize, Serialize};
6 use std::collections::btree_map::Entry;
7 use std::collections::{BTreeMap, BTreeSet};
8 use wasmtime_environ::graphs::{Dfs, DfsEvent, Graph};
9 
10 /// Identifies a `(rec ...)` group.
11 #[derive(
12     Debug, Copy, Clone, Eq, PartialOrd, PartialEq, Ord, Hash, Default, Serialize, Deserialize,
13 )]
14 pub struct RecGroupId(pub(crate) u32);
15 
16 /// Identifies a type within a rec group.
17 #[derive(
18     Debug, Copy, Clone, Eq, PartialOrd, PartialEq, Ord, Hash, Default, Serialize, Deserialize,
19 )]
20 pub struct TypeId(pub(crate) u32);
21 
22 /// A struct type definition.
23 #[derive(Debug, Default, Serialize, Deserialize)]
24 pub struct StructType {}
25 
26 /// A composite type: currently only structs.
27 #[derive(Debug, Serialize, Deserialize)]
28 pub enum CompositeType {
29     /// A struct composite type.
30     Struct(StructType),
31 }
32 
33 /// A sub-type definition (the per-type payload).
34 #[derive(Debug, Serialize, Deserialize)]
35 pub struct SubType {
36     pub(crate) is_final: bool,
37     pub(crate) supertype: Option<TypeId>,
38     pub(crate) composite_type: CompositeType,
39 }
40 
41 /// Supertype graph: edges go from a type to its supertype.
42 struct SupertypeGraph<'a> {
43     type_defs: &'a BTreeMap<TypeId, SubType>,
44 }
45 
46 impl Graph<TypeId> for SupertypeGraph<'_> {
47     type NodesIter<'a>
48         = std::iter::Copied<std::collections::btree_map::Keys<'a, TypeId, SubType>>
49     where
50         Self: 'a;
51 
nodes(&self) -> Self::NodesIter<'_>52     fn nodes(&self) -> Self::NodesIter<'_> {
53         self.type_defs.keys().copied()
54     }
55 
56     type SuccessorsIter<'a>
57         = std::option::IntoIter<TypeId>
58     where
59         Self: 'a;
60 
successors(&self, node: TypeId) -> Self::SuccessorsIter<'_>61     fn successors(&self, node: TypeId) -> Self::SuccessorsIter<'_> {
62         self.type_defs
63             .get(&node)
64             .and_then(|def| def.supertype)
65             .into_iter()
66     }
67 }
68 
69 /// Rec-group dependency graph: group A depends on group B when a type
70 /// in A has a supertype in B.
71 struct RecGroupGraph<'a> {
72     type_defs: &'a BTreeMap<TypeId, SubType>,
73     rec_groups: &'a BTreeMap<RecGroupId, BTreeSet<TypeId>>,
74     type_to_group: &'a BTreeMap<TypeId, RecGroupId>,
75 }
76 
77 impl Graph<RecGroupId> for RecGroupGraph<'_> {
78     type NodesIter<'a>
79         = std::iter::Copied<std::collections::btree_map::Keys<'a, RecGroupId, BTreeSet<TypeId>>>
80     where
81         Self: 'a;
82 
nodes(&self) -> Self::NodesIter<'_>83     fn nodes(&self) -> Self::NodesIter<'_> {
84         self.rec_groups.keys().copied()
85     }
86 
87     type SuccessorsIter<'a>
88         = std::vec::IntoIter<RecGroupId>
89     where
90         Self: 'a;
91 
successors(&self, group: RecGroupId) -> Self::SuccessorsIter<'_>92     fn successors(&self, group: RecGroupId) -> Self::SuccessorsIter<'_> {
93         let mut deps = BTreeSet::new();
94 
95         if let Some(type_ids) = self.rec_groups.get(&group) {
96             for &ty in type_ids {
97                 if let Some(super_ty) = self.type_defs.get(&ty).and_then(|d| d.supertype) {
98                     if let Some(&super_group) = self.type_to_group.get(&super_ty) {
99                         if super_group != group {
100                             deps.insert(super_group);
101                         }
102                     }
103                 }
104             }
105         }
106 
107         deps.into_iter().collect::<Vec<_>>().into_iter()
108     }
109 }
110 
111 /// All type and rec-group state.
112 ///
113 /// Rec groups own sets of [`TypeId`]s; moving a type between groups is
114 /// just a set remove + set insert with no cascading index fixups.
115 #[derive(Debug, Default, Serialize, Deserialize)]
116 pub struct Types {
117     /// Map from rec-group id to the set of types it contains.
118     pub(crate) rec_groups: BTreeMap<RecGroupId, BTreeSet<TypeId>>,
119     /// Map from type id to its definition.
120     pub(crate) type_defs: BTreeMap<TypeId, SubType>,
121 }
122 
123 impl Types {
124     /// Create empty type state.
new() -> Self125     pub fn new() -> Self {
126         Self::default()
127     }
128 
129     /// Return a fresh [`RecGroupId`] not already in use.
fresh_rec_group_id(&self, rng: &mut mutatis::Rng) -> RecGroupId130     pub fn fresh_rec_group_id(&self, rng: &mut mutatis::Rng) -> RecGroupId {
131         for _ in 0..1000 {
132             let id = RecGroupId(rng.gen_u32());
133             if !self.rec_groups.contains_key(&id) {
134                 return id;
135             }
136         }
137         panic!("failed to generate a new RecGroupId in 1000 iterations");
138     }
139 
140     /// Return a fresh [`TypeId`] not already in use.
fresh_type_id(&self, rng: &mut mutatis::Rng) -> TypeId141     pub fn fresh_type_id(&self, rng: &mut mutatis::Rng) -> TypeId {
142         for _ in 0..1000 {
143             let id = TypeId(rng.gen_u32());
144             if !self.type_defs.contains_key(&id) {
145                 return id;
146             }
147         }
148         panic!("failed to generate a new TypeId in 1000 iterations");
149     }
150 
151     /// Insert an empty rec group. Returns `true` if it was newly inserted.
insert_rec_group(&mut self, id: RecGroupId) -> bool152     pub fn insert_rec_group(&mut self, id: RecGroupId) -> bool {
153         match self.rec_groups.entry(id) {
154             Entry::Vacant(e) => {
155                 e.insert(BTreeSet::new());
156                 true
157             }
158             Entry::Occupied(_) => false,
159         }
160     }
161 
162     /// Insert an empty struct type into the given rec group.
163     ///
164     /// The rec group must already exist.
insert_empty_struct( &mut self, id: TypeId, group: RecGroupId, is_final: bool, supertype: Option<TypeId>, )165     pub fn insert_empty_struct(
166         &mut self,
167         id: TypeId,
168         group: RecGroupId,
169         is_final: bool,
170         supertype: Option<TypeId>,
171     ) {
172         self.rec_groups
173             .get_mut(&group)
174             .expect("rec group must exist")
175             .insert(id);
176         self.type_defs.insert(
177             id,
178             SubType {
179                 is_final,
180                 supertype,
181                 composite_type: CompositeType::Struct(StructType::default()),
182             },
183         );
184     }
185 
186     /// Remove a type from its rec group and from `type_defs`.
remove_type(&mut self, id: TypeId)187     pub fn remove_type(&mut self, id: TypeId) {
188         self.type_defs.remove(&id);
189         for members in self.rec_groups.values_mut() {
190             members.remove(&id);
191         }
192     }
193 
194     /// Find which rec group a type belongs to, if any.
rec_group_of(&self, id: TypeId) -> Option<RecGroupId>195     pub fn rec_group_of(&self, id: TypeId) -> Option<RecGroupId> {
196         self.rec_groups
197             .iter()
198             .find(|(_, members)| members.contains(&id))
199             .map(|(gid, _)| *gid)
200     }
201 
202     /// Topological sort of types by their supertype (supertype before subtype).
sort_types_topo(&self, out: &mut Vec<TypeId>)203     pub fn sort_types_topo(&self, out: &mut Vec<TypeId>) {
204         let graph = SupertypeGraph {
205             type_defs: &self.type_defs,
206         };
207 
208         let mut dfs = Dfs::new(graph.nodes());
209         let mut seen = BTreeSet::new();
210 
211         out.clear();
212         out.reserve(self.type_defs.len());
213 
214         while let Some(event) = dfs.next(&graph, |id| seen.contains(&id)) {
215             match event {
216                 DfsEvent::Pre(id) => {
217                     seen.insert(id);
218                 }
219                 DfsEvent::Post(id) => {
220                     out.push(id);
221                 }
222                 DfsEvent::AfterEdge(_, _) => {}
223             }
224         }
225     }
226 
227     /// Topological sort of rec groups: if a type in group G has a
228     /// supertype in group H, then H appears before G in the output.
sort_rec_groups_topo(&self, out: &mut Vec<RecGroupId>)229     pub fn sort_rec_groups_topo(&self, out: &mut Vec<RecGroupId>) {
230         let type_to_group = self.type_to_group_map();
231         let graph = RecGroupGraph {
232             type_defs: &self.type_defs,
233             rec_groups: &self.rec_groups,
234             type_to_group: &type_to_group,
235         };
236 
237         let mut dfs = Dfs::new(graph.nodes());
238         let mut seen = BTreeSet::new();
239 
240         out.clear();
241         out.reserve(self.rec_groups.len());
242 
243         while let Some(event) = dfs.next(&graph, |id| seen.contains(&id)) {
244             match event {
245                 DfsEvent::Pre(id) => {
246                     seen.insert(id);
247                 }
248                 DfsEvent::Post(id) => {
249                     out.push(id);
250                 }
251                 DfsEvent::AfterEdge(_, _) => {}
252             }
253         }
254     }
255 
256     /// Break cycles in the [type -> supertype] graph by dropping some supertype edges.
break_supertype_cycles(&mut self)257     pub fn break_supertype_cycles(&mut self) {
258         let graph = SupertypeGraph {
259             type_defs: &self.type_defs,
260         };
261 
262         let mut dfs = Dfs::new(graph.nodes());
263         let mut seen = BTreeSet::new();
264         let mut active = BTreeSet::new();
265         let mut to_clear = BTreeSet::new();
266 
267         while let Some(event) = dfs.next(&graph, |id| seen.contains(&id)) {
268             match event {
269                 DfsEvent::Pre(id) => {
270                     seen.insert(id);
271                     active.insert(id);
272                 }
273                 DfsEvent::Post(id) => {
274                     active.remove(&id);
275                 }
276                 DfsEvent::AfterEdge(from, to) => {
277                     if active.contains(&to) {
278                         to_clear.insert(from);
279                     }
280                 }
281             }
282         }
283 
284         for id in to_clear {
285             if let Some(def) = self.type_defs.get_mut(&id) {
286                 def.supertype = None;
287             }
288         }
289     }
290 
291     /// Build a reverse map from type id to its owning rec group.
type_to_group_map(&self) -> BTreeMap<TypeId, RecGroupId>292     fn type_to_group_map(&self) -> BTreeMap<TypeId, RecGroupId> {
293         self.rec_groups
294             .iter()
295             .flat_map(|(&gid, members)| members.iter().map(move |&tid| (tid, gid)))
296             .collect()
297     }
298 
299     /// Break cycles in the rec-group dependency graph by dropping cross-group
300     /// supertype edges that are DFS back edges.
break_rec_group_cycles(&mut self)301     pub fn break_rec_group_cycles(&mut self) {
302         let type_to_group = self.type_to_group_map();
303         let graph = RecGroupGraph {
304             type_defs: &self.type_defs,
305             rec_groups: &self.rec_groups,
306             type_to_group: &type_to_group,
307         };
308 
309         let mut seen = BTreeSet::new();
310         let mut back_edges: BTreeSet<(RecGroupId, RecGroupId)> = BTreeSet::new();
311         let mut dfs = Dfs::default();
312 
313         for &root in self.rec_groups.keys() {
314             if seen.contains(&root) {
315                 continue;
316             }
317             dfs.add_root(root);
318             let mut active = BTreeSet::new();
319 
320             while let Some(event) = dfs.next(&graph, |id| seen.contains(&id)) {
321                 match event {
322                     DfsEvent::Pre(id) => {
323                         seen.insert(id);
324                         active.insert(id);
325                     }
326                     DfsEvent::Post(id) => {
327                         active.remove(&id);
328                     }
329                     DfsEvent::AfterEdge(from, to) => {
330                         if active.contains(&to) {
331                             back_edges.insert((from, to));
332                         }
333                     }
334                 }
335             }
336         }
337 
338         // Drop supertype edges that correspond to back edges.
339         if !back_edges.is_empty() {
340             for (&tid, def) in self.type_defs.iter_mut() {
341                 if let Some(st) = def.supertype {
342                     if let (Some(&sg), Some(&spg)) =
343                         (type_to_group.get(&tid), type_to_group.get(&st))
344                     {
345                         if back_edges.contains(&(sg, spg)) {
346                             def.supertype = None;
347                         }
348                     }
349                 }
350             }
351         }
352     }
353 
354     /// Fix up the types to ensure they are within the limits.
fixup(&mut self, limits: &GcOpsLimits)355     pub fn fixup(&mut self, limits: &GcOpsLimits) {
356         let max_rec_groups =
357             usize::try_from(limits.max_rec_groups).expect("max_rec_groups is too large");
358         let max_types = usize::try_from(limits.max_types).expect("max_types is too large");
359 
360         // 1. Trim excess types.
361         while self.type_defs.len() > max_types {
362             if let Some((tid, _)) = self.type_defs.pop_last() {
363                 for members in self.rec_groups.values_mut() {
364                     members.remove(&tid);
365                 }
366             }
367         }
368 
369         // 2. Drop dangling references and deduplicate across groups.
370         let mut seen = BTreeSet::new();
371         for members in self.rec_groups.values_mut() {
372             members.retain(|tid| self.type_defs.contains_key(tid) && seen.insert(*tid));
373         }
374 
375         // 3. Trim excess rec groups.
376         while self.rec_groups.len() > max_rec_groups {
377             self.rec_groups.pop_last();
378         }
379 
380         // 4. Find all orphans (from trimmed groups or never in any group).
381         let housed: BTreeSet<TypeId> = self
382             .rec_groups
383             .values()
384             .flat_map(|m| m.iter().copied())
385             .collect();
386         let orphans: Vec<TypeId> = self
387             .type_defs
388             .keys()
389             .filter(|tid| !housed.contains(tid))
390             .copied()
391             .collect();
392 
393         // 5. Adopt orphans or drop them.
394         if let Some(first_members) = self.rec_groups.values_mut().next() {
395             first_members.extend(orphans);
396         } else {
397             for tid in &orphans {
398                 self.type_defs.remove(tid);
399             }
400         }
401 
402         // 6. Clear supertypes that reference removed types.
403         let valid_type_ids: BTreeSet<TypeId> = self.type_defs.keys().copied().collect();
404         for def in self.type_defs.values_mut() {
405             if let Some(st) = def.supertype {
406                 if !valid_type_ids.contains(&st) {
407                     def.supertype = None;
408                 }
409             }
410         }
411 
412         // 7. A subtype cannot have a final supertype.
413         let final_type_ids: BTreeSet<TypeId> = self
414             .type_defs
415             .iter()
416             .filter(|(_, d)| d.is_final)
417             .map(|(id, _)| *id)
418             .collect();
419         for def in self.type_defs.values_mut() {
420             if let Some(st) = def.supertype {
421                 if final_type_ids.contains(&st) {
422                     def.supertype = None;
423                 }
424             }
425         }
426 
427         // 8. Break supertype cycles and rec-group dependency cycles.
428         self.break_supertype_cycles();
429         self.break_rec_group_cycles();
430 
431         debug_assert!(self.is_well_formed(limits));
432     }
433 
434     /// Check if the types are well-formed and within configured limits, i.e.
435     /// rec/type counts are within limits,
436     /// every type belongs to exactly one rec group,
437     /// and every rec group member must exist in type_defs.
is_well_formed(&self, limits: &GcOpsLimits) -> bool438     fn is_well_formed(&self, limits: &GcOpsLimits) -> bool {
439         if self.rec_groups.len()
440             > usize::try_from(limits.max_rec_groups).expect("max_rec_groups is too large")
441         {
442             log::debug!("[-] Failed: rec_groups.len() > max_rec_groups");
443             return false;
444         }
445         if self.type_defs.len() > usize::try_from(limits.max_types).expect("max_types is too large")
446         {
447             log::debug!("[-] Failed: type_defs.len() > max_types");
448             return false;
449         }
450         let mut all = BTreeSet::new();
451         for members in self.rec_groups.values() {
452             for tid in members {
453                 if !self.type_defs.contains_key(tid) {
454                     log::debug!("[-] Failed: type_defs.contains_key(tid) is false");
455                     return false;
456                 }
457                 if !all.insert(*tid) {
458                     log::debug!("[-] Failed: all.insert(tid) is false");
459                     return false;
460                 }
461             }
462         }
463         if !self.type_defs.keys().all(|tid| all.contains(tid)) {
464             log::debug!("[-] Failed: type_defs.keys().all(|tid| all.contains(tid)) is false");
465             return false;
466         }
467         // Every supertype must exist and must not be final.
468         for (&tid, def) in &self.type_defs {
469             if let Some(st) = def.supertype {
470                 match self.type_defs.get(&st) {
471                     None => {
472                         log::debug!("[-] Failed: supertype {st:?} missing for subtype {tid:?}");
473                         return false;
474                     }
475                     Some(super_def) if super_def.is_final => {
476                         log::debug!("[-] Failed: subtype {tid:?} has final supertype {st:?}");
477                         return false;
478                     }
479                     _ => {}
480                 }
481             }
482         }
483         true
484     }
485 }
486 
487 /// Tracks the required operand type on the abstract value stack.
488 #[derive(Copy, Clone, Debug)]
489 pub enum StackType {
490     /// `externref`.
491     ExternRef,
492     /// `(ref $*)` — optionally with a concrete type index.
493     Struct(Option<u32>),
494 }
495 
496 impl StackType {
497     /// Ensure the top of `stack` satisfies `req`, emitting fixup ops as needed.
fixup( req: Option<StackType>, stack: &mut Vec<StackType>, out: &mut Vec<GcOp>, num_types: u32, )498     pub fn fixup(
499         req: Option<StackType>,
500         stack: &mut Vec<StackType>,
501         out: &mut Vec<GcOp>,
502         num_types: u32,
503     ) {
504         let mut result_types = Vec::new();
505         match req {
506             None => {
507                 if stack.is_empty() {
508                     Self::emit(GcOp::NullExtern, stack, out, num_types, &mut result_types);
509                 }
510                 stack.pop();
511             }
512             Some(Self::ExternRef) => match stack.last() {
513                 Some(Self::ExternRef) => {
514                     stack.pop();
515                 }
516                 _ => {
517                     Self::emit(GcOp::NullExtern, stack, out, num_types, &mut result_types);
518                     stack.pop();
519                 }
520             },
521             Some(Self::Struct(wanted)) => {
522                 let ok = match (wanted, stack.last()) {
523                     (Some(wanted), Some(Self::Struct(Some(s)))) => *s == wanted,
524                     (None, Some(Self::Struct(_))) => true,
525                     _ => false,
526                 };
527 
528                 if ok {
529                     stack.pop();
530                 } else {
531                     match wanted {
532                         // When num_types == 0, GcOp::fixup() should have dropped the ops
533                         // that require a concrete type.
534                         // But it keeps the ops that work with abstract types.
535                         // Since our mutator can legally remove all the types,
536                         // StackType::fixup() should insert GcOp::NullStruct()
537                         // to satisfy the undropped ops that work with abstract types.
538                         None => {
539                             Self::emit(GcOp::NullStruct, stack, out, num_types, &mut result_types);
540                             stack.pop();
541                         }
542                         Some(t) => {
543                             debug_assert_ne!(
544                                 num_types, 0,
545                                 "typed struct requirement with num_types == 0; op should have been removed"
546                             );
547                             let t = Self::clamp(t, num_types);
548                             Self::emit(
549                                 GcOp::StructNew { type_index: t },
550                                 stack,
551                                 out,
552                                 num_types,
553                                 &mut result_types,
554                             );
555                             stack.pop();
556                         }
557                     }
558                 }
559             }
560         }
561     }
562 
563     /// Emit an opcode and update the stack.
emit( op: GcOp, stack: &mut Vec<Self>, out: &mut Vec<GcOp>, num_types: u32, result_types: &mut Vec<Self>, )564     pub(crate) fn emit(
565         op: GcOp,
566         stack: &mut Vec<Self>,
567         out: &mut Vec<GcOp>,
568         num_types: u32,
569         result_types: &mut Vec<Self>,
570     ) {
571         out.push(op);
572         result_types.clear();
573         op.result_types(result_types);
574         for ty in result_types {
575             let clamped_ty = match ty {
576                 Self::Struct(Some(t)) => Self::Struct(Some(Self::clamp(*t, num_types))),
577                 other => *other,
578             };
579             stack.push(clamped_ty);
580         }
581     }
582 
583     /// Clamp a type index to the number of types.
clamp(t: u32, n: u32) -> u32584     fn clamp(t: u32, n: u32) -> u32 {
585         if n == 0 { 0 } else { t % n }
586     }
587 }
588