1use 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
17pub 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 pub fn defer(&self) -> DeferPoint {
44 DeferPoint::new(&self.heap)
45 }
46
47 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 pub fn gc(&mut self) {
61 self.heap.collect_garbage();
62 }
63
64 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 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 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 pub fn add_constraint(&mut self, constraint: impl MarkingConstraint + 'static) {
122 self.heap.add_constraint(constraint);
123 }
124
125 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
139pub trait GcCell: mopa::Any {
141 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#[repr(transparent)]
155pub struct GcPointer<T: GcCell + ?Sized> {
156 base: NonNull<GcPointerBase>,
157 marker: PhantomData<T>,
158}
159
160#[repr(C)]
162pub struct GcPointerBase {
163 hdr: HeapObjectHeader,
164}
165
166impl<T: GcCell + ?Sized> GcPointer<T> {
167 pub fn untyped(self) -> UntypedGcRef {
169 unsafe { std::mem::transmute(self) }
170 }
171 pub fn ptr_eq<U: GcCell + ?Sized>(this: &Self, other: &GcPointer<U>) -> bool {
173 this.base == other.base
174 }
175
176 #[inline]
178 pub fn as_dyn(self) -> GcPointer<dyn GcCell> {
179 GcPointer {
180 base: self.base,
181 marker: PhantomData,
182 }
183 }
184 #[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 #[inline]
191 pub fn get_dyn(&self) -> &dyn GcCell {
192 unsafe { (*self.base.as_ptr()).get_dyn() }
193 }
194 #[inline]
196 pub fn get_dyn_mut(&mut self) -> &mut dyn GcCell {
197 unsafe { (*self.base.as_ptr()).get_dyn() }
198 }
199 #[inline]
201 pub unsafe fn downcast_unchecked<U: GcCell>(self) -> GcPointer<U> {
202 GcPointer {
203 base: self.base,
204 marker: PhantomData,
205 }
206 }
207 #[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 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
287pub 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::*};