comet_api/
lib.rs

1/// Small wrapper over Comet API for ease of use. 
2use comet::gcref::UntypedGcRef;
3use comet::header::HeapObjectHeader;
4use comet::heap::{DeferPoint, Heap as CometHeap, MarkingConstraint};
5pub use comet::internal::finalize_trait::FinalizeTrait as Finalize;
6use comet::internal::gc_info::{GCInfoIndex, GCInfoTrait};
7pub use comet::internal::trace_trait::TraceTrait as Trace;
8pub use comet::visitor::Visitor;
9use comet::gcref::GcRef;
10use mopa::mopafy;
11use std::collections::HashMap;
12use std::marker::PhantomData;
13use std::mem::{size_of, transmute};
14use std::ops::{Deref, DerefMut};
15use std::ptr::NonNull;
16
17/// Wrapper over Comet heap. 
18pub struct Heap {
19    pub heap: Box<CometHeap>,
20}
21#[allow(dead_code)]
22pub struct SimpleMarkingConstraint {
23    name: String,
24    exec: Box<dyn FnMut(&mut Visitor)>,
25}
26impl SimpleMarkingConstraint {
27    pub fn new(name: &str, exec: impl FnMut(&mut Visitor) + 'static) -> Self {
28        Self {
29            name: name.to_owned(),
30            exec: Box::new(exec),
31        }
32    }
33}
34
35impl MarkingConstraint for SimpleMarkingConstraint {
36    fn execute(&mut self, vis: &mut Visitor) {
37        (self.exec)(vis);
38    }
39}
40
41impl Heap {
42    /// Creates GC defer point. 
43    pub fn defer(&self) -> DeferPoint {
44        DeferPoint::new(&self.heap)
45    }
46
47    /// Creates new Comet heap. if `size` is < 1MB then it will be set to 1GB by default.
48    pub fn new(size: Option<usize>) -> Self {
49        let mut configs = comet::Config::default();
50        if let Some(size) = size {
51            if size >= 1024 * 1024 {
52                configs.heap_size = size;
53            }
54        } 
55        let mut heap = CometHeap::new(configs);
56        heap.add_core_constraints();
57        Self { heap }
58    }
59    /// Triggers GC cycle. 
60    pub fn gc(&mut self) {
61        self.heap.collect_garbage();
62    }
63
64    /// Allocates GcPointerBase in Comet heap with all internal fields initialized correctly
65    pub fn allocate_(
66        &mut self,
67        size: usize,
68        vtable: usize,
69        idx: GCInfoIndex,
70    ) -> Option<NonNull<GcPointerBase>> {
71        unsafe {
72            let ptr = self
73                .heap
74                .allocate_raw(size + size_of::<GcPointerBase>(), idx);
75            match ptr {
76                Some(ptr) => {
77                    let raw = HeapObjectHeader::from_object(ptr.get()).cast::<GcPointerBase>();
78                    idx.get_mut().vtable = vtable;
79
80                    Some(NonNull::new_unchecked(raw))
81                }
82                _ => None,
83            }
84        }
85    }
86    /// Allocates raw memory in Comet heap.
87    pub fn allocate_raw(
88        &mut self,
89        size: usize,
90        vtable: usize,
91        idx: GCInfoIndex,
92    ) -> *mut GcPointerBase {
93        self.allocate_(size, vtable, idx)
94            .unwrap_or_else(|| memory_oom())
95            .as_ptr()
96    }
97
98    /// Allocates `T` on the GC heap with initializing all data correctly. 
99    pub fn allocate<T: GcCell + GCInfoTrait<T> + Trace + Finalize<T>>(
100        &mut self,
101        value: T,
102    ) -> GcPointer<T> {
103        let size = value.compute_size();
104        let memory = self.allocate_raw(size, vtable_of(&value), T::index());
105        unsafe {
106            (*memory).data::<T>().write(value);
107            GcPointer {
108                base: NonNull::new_unchecked(memory),
109                marker: PhantomData,
110            }
111        }
112    }
113    /*
114    pub fn walk(&mut self,callback: &mut dyn FnMut(*mut GcPointerBase)) -> SafepointScope
115    {
116        let mut point = SafepointScope::new(&mut self.main);
117        self.heap.for_each_cell(&point, callback, weak_refs)
118    }*/
119
120    /// Adds constraint to be executed before each gc cycle. 
121    pub fn add_constraint(&mut self, constraint: impl MarkingConstraint + 'static) {
122        self.heap.add_constraint(constraint);
123    }
124
125    /// Allocates weak ref for `T`. 
126    pub fn make_weak<T: GcCell>(&mut self, target: GcPointer<T>) -> WeakRef<T> {
127        let weak = unsafe { self.heap.allocate_weak(std::mem::transmute(target)) };
128        WeakRef {
129            ref_: weak,
130            marker: PhantomData,
131        }
132    }
133
134    pub fn collect_if_necessary(&mut self) {
135        self.heap.collect_if_necessary_or_defer();
136    }
137}
138
139/// `GcCell` is a type that can be allocated in GC heap.
140pub trait GcCell: mopa::Any {
141    /// Used when object has dynamic size i.e arrays
142    fn compute_size(&self) -> usize {
143        std::mem::size_of_val(self)
144    }
145
146    fn type_name(&self) -> &'static str {
147        std::any::type_name::<Self>()
148    }
149}
150
151mopafy!(GcCell);
152
153/// GC collected smart-pointer. It allows `T` to be `?Sized` for dynamic casts. 
154#[repr(transparent)]
155pub struct GcPointer<T: GcCell + ?Sized> {
156    base: NonNull<GcPointerBase>,
157    marker: PhantomData<T>,
158}
159
160/// GC object header. 
161#[repr(C)]
162pub struct GcPointerBase {
163    hdr: HeapObjectHeader,
164}
165
166impl<T: GcCell + ?Sized> GcPointer<T> {
167    /// Returns untyped GC ref from this pointer. 
168    pub fn untyped(self) -> UntypedGcRef {
169        unsafe { std::mem::transmute(self) }
170    }
171    /// Pointer equality. 
172    pub fn ptr_eq<U: GcCell + ?Sized>(this: &Self, other: &GcPointer<U>) -> bool {
173        this.base == other.base
174    }
175
176    /// Casts `T` back to `dyn GcCell.
177    #[inline]
178    pub fn as_dyn(self) -> GcPointer<dyn GcCell> {
179        GcPointer {
180            base: self.base,
181            marker: PhantomData,
182        }
183    }
184    /// Check if this GC pointer stores `U`. 
185    #[inline]
186    pub fn is<U: Trace + Finalize<U> + GcCell + GCInfoTrait<U>>(self) -> bool {
187        unsafe { (*self.base.as_ptr()).hdr.get_gc_info_index() == U::index() }
188    }
189    /// Get reference to `dyn GcCell`
190    #[inline]
191    pub fn get_dyn(&self) -> &dyn GcCell {
192        unsafe { (*self.base.as_ptr()).get_dyn() }
193    }
194    /// Get mutable reference to `dyn GcCell`
195    #[inline]
196    pub fn get_dyn_mut(&mut self) -> &mut dyn GcCell {
197        unsafe { (*self.base.as_ptr()).get_dyn() }
198    }
199    /// Unchecked downcast to `U`. 
200    #[inline]
201    pub unsafe fn downcast_unchecked<U: GcCell>(self) -> GcPointer<U> {
202        GcPointer {
203            base: self.base,
204            marker: PhantomData,
205        }
206    }
207    /// Checked downcast to type `U`. 
208    #[inline]
209    pub fn downcast<U: Trace + Finalize<U> + GcCell + GCInfoTrait<U>>(
210        self,
211    ) -> Option<GcPointer<U>> {
212        if !self.is::<U>() {
213            None
214        } else {
215            Some(unsafe { self.downcast_unchecked() })
216        }
217    }
218}
219
220impl GcPointerBase {
221    /// Returns alloction size. 
222    pub fn allocation_size(&self) -> usize {
223        comet::gc_size(&self.hdr)
224    }
225
226    pub fn get_dyn(&self) -> &mut dyn GcCell {
227        unsafe {
228            std::mem::transmute(mopa::TraitObject {
229                vtable: self.hdr.get_gc_info_index().get().vtable as _,
230                data: self.data::<u8>() as _,
231            })
232        }
233    }
234
235    pub fn data<T>(&self) -> *mut T {
236        unsafe {
237            (self as *const Self as *mut u8)
238                .add(size_of::<Self>())
239                .cast()
240        }
241    }
242}
243pub fn vtable_of<T: GcCell>(x: *const T) -> usize {
244    unsafe { core::mem::transmute::<_, mopa::TraitObject>(x as *const dyn GcCell).vtable as _ }
245}
246
247pub fn vtable_of_type<T: GcCell + Sized>() -> usize {
248    vtable_of(core::ptr::null::<T>())
249}
250
251impl<T: GcCell + ?Sized> Copy for GcPointer<T> {}
252impl<T: GcCell + ?Sized> Clone for GcPointer<T> {
253    fn clone(&self) -> Self {
254        *self
255    }
256}
257
258impl<T: GcCell> Deref for GcPointer<T> {
259    type Target = T;
260    fn deref(&self) -> &Self::Target {
261        unsafe { &*(&*self.base.as_ptr()).data::<T>() }
262    }
263}
264impl<T: GcCell> DerefMut for GcPointer<T> {
265    fn deref_mut(&mut self) -> &mut Self::Target {
266        unsafe { &mut *(&*self.base.as_ptr()).data::<T>() }
267    }
268}
269
270impl<T: GcCell + ?Sized> std::fmt::Pointer for GcPointer<T> {
271    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
272        write!(f, "{:p}", self.base)
273    }
274}
275
276impl<T: GcCell + std::fmt::Debug> std::fmt::Debug for GcPointer<T> {
277    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
278        write!(f, "{:?}", **self)
279    }
280}
281impl<T: GcCell + std::fmt::Display> std::fmt::Display for GcPointer<T> {
282    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
283        write!(f, "{}", **self)
284    }
285}
286
287/// Weak reference wrapper. 
288pub struct WeakRef<T: GcCell> {
289    ref_: comet::gcref::WeakGcRef,
290    marker: PhantomData<T>,
291}
292
293impl<T: GcCell> WeakRef<T> {
294    pub fn upgrade(&self) -> Option<GcPointer<T>> {
295        match self.ref_.upgrade() {
296            Some(ptr) => Some(GcPointer {
297                base: unsafe { transmute(ptr) },
298                marker: PhantomData,
299            }),
300            _ => None,
301        }
302    }
303}
304#[cold]
305fn memory_oom() -> ! {
306    eprintln!("Starlight: No memory left");
307    std::process::abort();
308}
309
310pub mod cell {
311    pub use super::*;
312}
313
314macro_rules! impl_prim {
315    ($($t: ty)*) => {
316        $(
317            impl GcCell for $t {}
318        )*
319    };
320}
321
322impl_prim!(String bool f32 f64 u8 i8 u16 i16 u32 i32 u64 i64 std::fs::File u128 i128);
323
324impl<K: GcCell, V: GcCell> GcCell for HashMap<K, V> {}
325impl<T: GcCell> GcCell for WeakRef<T> {}
326impl<T: GcCell> GcCell for Option<T> {}
327impl<T: GcCell> GcCell for Vec<T> {}
328
329impl<T: GcCell + Trace> Trace for GcPointer<T> {
330    fn trace(&self, vis: &mut Visitor) {
331        unsafe {
332            vis.trace_gcref(transmute::<_, GcRef<T>>(*self));
333        }
334    }
335}
336impl<T: GcCell> PartialEq for GcPointer<T> {
337    fn eq(&self, other: &Self) -> bool {
338        self.base == other.base
339    }
340}
341
342impl<T: GcCell> Eq for GcPointer<T> {}
343impl<T: GcCell> Copy for WeakRef<T> {}
344impl<T: GcCell> Clone for WeakRef<T> {
345    fn clone(&self) -> Self {
346        *self
347    }
348}
349impl<T: GcCell> Trace for WeakRef<T> {
350    fn trace(&self, vis: &mut Visitor) {
351        vis.trace_gcref(self.ref_.slot())
352    }
353}
354
355impl<T: GcCell> GcCell for GcPointer<T> {}
356
357pub use comet::internal::{trace_trait::*,finalize_trait::*,gc_info::*};
358pub use comet::{header::*,*,gc_info_table::*};