compose_library/gc/
mod.rs

1mod clean;
2mod trigger;
3
4use crate::gc::trigger::{GcTriggerPolicy, SimplePolicy};
5use crate::{Array, Value};
6use compose_library::gc::trigger::GcData;
7use compose_library::{Iter, Map};
8use slotmap::{new_key_type, SlotMap};
9use std::fmt::Debug;
10use std::ops::Deref;
11
12#[derive(Debug)]
13pub struct Heap {
14    map: SlotMap<UntypedRef, HeapItem>,
15    policy: Box<dyn GcTriggerPolicy>,
16}
17
18#[derive(Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
19pub struct HeapRef<T: HeapObject> {
20    key: UntypedRef,
21    _marker: std::marker::PhantomData<T>,
22}
23
24impl<T: HeapObject> Clone for HeapRef<T> {
25    fn clone(&self) -> Self {
26        HeapRef {
27            key: self.key,
28            _marker: Default::default(),
29        }
30    }
31}
32impl<T: HeapObject> Copy for HeapRef<T> {}
33
34impl<T: HeapObject> HeapRef<T> {
35    pub fn key(&self) -> UntypedRef {
36        self.key
37    }
38}
39
40impl<T> Deref for HeapRef<T>
41where
42    T: HeapObject,
43{
44    type Target = UntypedRef;
45
46    fn deref(&self) -> &Self::Target {
47        &self.key
48    }
49}
50
51impl<T> From<UntypedRef> for HeapRef<T>
52where
53    T: HeapObject,
54{
55    fn from(key: UntypedRef) -> Self {
56        Self {
57            key,
58            _marker: Default::default(),
59        }
60    }
61}
62
63new_key_type! {
64    pub struct UntypedRef;
65}
66
67impl<T> HeapRef<T>
68where
69    T: HeapObject,
70{
71    #[track_caller]
72    pub fn get_unwrap(self, heap: &Heap) -> &T {
73        heap.get(self).unwrap_or_else(|| {
74            panic!("Use after free. This is a bug. Key: {:?}", self.key)
75        })
76    }
77    
78    pub fn try_get(self, heap: &Heap) -> StrResult<&T> {
79        heap.get(self).ok_or_else(|| "Use after free. This is a bug.".into())
80    }
81
82    #[track_caller]   
83    pub fn get_mut_unwrap(self, heap: &mut Heap) -> &mut T {
84        heap.get_mut(self).unwrap_or_else(|| {
85            panic!("Use after free. This is a bug. Key: {:?}", self.key)       
86        })
87    }
88    
89    pub fn try_get_mut(self, heap: &mut Heap) -> StrResult<&mut T> {
90        heap.get_mut(self).ok_or_else(|| "Use after free. This is a bug.".into())
91    }
92}
93
94impl Heap {
95    pub fn new() -> Self {
96        Self {
97            map: SlotMap::with_key(),
98            policy: Box::new(SimplePolicy {
99                heap_size_threshold: 500,
100            })
101        }
102    }
103
104    pub fn get_mut<T: HeapObject>(&mut self, key: HeapRef<T>) -> Option<&mut T> {
105        self.map.get_mut(*key)?.as_type_mut()
106    }
107    pub fn alloc<T: HeapObject>(&mut self, value: T) -> HeapRef<T> {
108        self.map.insert(value.to_untyped()).into()
109    }
110
111    pub fn data(&self) -> GcData {
112        GcData {
113            heap_size: self.map.len(),
114        }
115    }
116
117    pub fn get<T: HeapObject>(&self, key: HeapRef<T>) -> Option<&T> {
118        self.map.get(*key)?.as_type()
119    }
120
121    pub fn remove<T: HeapObject>(&mut self, key: HeapRef<T>) -> Option<HeapItem> {
122        self.map.remove(*key)
123    }
124    
125    pub fn get_untyped(&self, key: UntypedRef) -> Option<&HeapItem> {
126        self.map.get(key)
127    }
128}
129
130/// A type that can keep references to heap values
131pub trait Trace: Debug {
132    fn visit_refs(&self, f: &mut dyn FnMut(UntypedRef));
133}
134
135impl Trace for &[UntypedRef] {
136    fn visit_refs(&self, f: &mut dyn FnMut(UntypedRef)) {
137        for key in *self {
138            f(*key)
139        }
140    }
141}
142
143pub trait HeapObject: Trace + Debug {
144    fn from_untyped(untyped: &HeapItem) -> Option<&Self>;
145    fn from_untyped_mut(untyped: &mut HeapItem) -> Option<&mut Self>;
146    fn to_untyped(self) -> HeapItem;
147}
148
149macro_rules! heap_enum {
150    (
151        $(#[$meta:meta])*
152        pub enum $enum_name:ident {
153            $(
154                $variant:ident($ty:ty)
155            ),* $(,)?
156        }
157    ) => {
158        $(#[$meta])*
159        pub enum $enum_name {
160            $(
161                $variant($ty),
162            )*
163        }
164
165        impl Trace for $enum_name {
166            fn visit_refs(&self, f: &mut dyn FnMut(UntypedRef)) {
167                match self {
168                    $(
169                        $enum_name::$variant(inner) => inner.visit_refs(f),
170                    )*
171                }
172            }
173        }
174    };
175}
176
177impl Trace for Value {
178    fn visit_refs(&self, f: &mut dyn FnMut(UntypedRef)) {
179        match self {
180            Value::Int(_) => {}
181            Value::Bool(_) => {}
182            Value::Unit(_) => {}
183            Value::Str(_) => {}
184            Value::Func(func) => func.visit_refs(f),
185            Value::Type(ty) => ty.visit_refs(f),
186            Value::Iterator(i) => i.visit_refs(f),
187            Value::Box(b) => f(b.key()),
188            Value::Array(a) => a.visit_refs(f),
189            Value::Range(r) => r.visit_refs(f),
190            Value::Map(m) => m.visit_refs(f),
191            Value::Module(m) => m.visit_refs(f),
192        }
193    }
194}
195
196macro_rules! impl_heap_obj {
197    ($ident:ident, $ty:ty) => {
198        impl HeapObject for $ty {
199            fn from_untyped(untyped: &HeapItem) -> Option<&Self> {
200                match untyped {
201                    HeapItem::$ident(v) => Some(v),
202                    _ => None,
203                }
204            }
205
206            fn from_untyped_mut(untyped: &mut HeapItem) -> Option<&mut Self> {
207                match untyped {
208                    HeapItem::$ident(v) => Some(v),
209                    _ => None,
210                }
211            }
212
213            fn to_untyped(self) -> HeapItem {
214                HeapItem::$ident(self)
215            }
216        }
217    };
218}
219
220pub(crate) use impl_heap_obj;
221use crate::diag::StrResult;
222
223impl_heap_obj!(Value, Value);
224impl_heap_obj!(Iter, Iter);
225impl_heap_obj!(Array, Array);
226impl_heap_obj!(Map, Map);
227
228heap_enum! {
229    #[derive(Debug, Clone, PartialEq)]
230    pub enum HeapItem {
231        Value(Value),
232        Iter(Iter),
233        Array(Array),
234        Map(Map)
235    }
236}
237
238impl HeapItem {
239    fn as_type<T: HeapObject>(&self) -> Option<&T> {
240        T::from_untyped(self)
241    }
242
243    fn as_type_mut<T: HeapObject>(&mut self) -> Option<&mut T> {
244        T::from_untyped_mut(self)
245    }
246}