compose_library/gc/
mod.rs1mod 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
130pub 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}