Skip to main content

boa_gc/pointers/
gc.rs

1use crate::{
2    Allocator, Ephemeron, GcErasedPointer, Tracer, WeakGc, custom_trace, finalizer_safe,
3    internals::{EphemeronBox, GcBox, VTable},
4    trace::{Finalize, Trace},
5};
6use std::{
7    any::TypeId,
8    cmp::Ordering,
9    fmt::{self, Debug, Display},
10    hash::{Hash, Hasher},
11    marker::PhantomData,
12    ops::Deref,
13    ptr::NonNull,
14    rc::Rc,
15};
16
17/// Zero sized struct that is used to ensure that we do not call trace methods,
18/// call its finalization method or drop it.
19///
20/// This can only happen if we are accessing a [`GcErasedPointer`] directly which is a bug.
21/// Panics if any of it's methods are called.
22///
23/// Note: Accessing the [`crate::internals::GcHeader`] of [`GcErasedPointer`] is fine.
24pub(crate) struct NonTraceable(());
25
26impl Finalize for NonTraceable {
27    fn finalize(&self) {
28        unreachable!()
29    }
30}
31
32unsafe impl Trace for NonTraceable {
33    unsafe fn trace(&self, _tracer: &mut Tracer) {
34        unreachable!()
35    }
36    unsafe fn trace_non_roots(&self) {
37        unreachable!()
38    }
39    fn run_finalizer(&self) {
40        unreachable!()
41    }
42}
43
44impl Drop for NonTraceable {
45    fn drop(&mut self) {
46        unreachable!()
47    }
48}
49
50/// A type erased [`Gc<T>`] pointer type.
51#[repr(transparent)]
52pub struct GcErased {
53    inner: Gc<NonTraceable>,
54}
55
56impl GcErased {
57    /// Convert a [`Gc<T>`] into a type erased [`GcErased`].
58    #[inline]
59    #[must_use]
60    pub fn new<T: Trace>(gc: Gc<T>) -> Self {
61        let inner_ptr = gc.inner_ptr;
62        std::mem::forget(gc);
63
64        Self {
65            inner: Gc {
66                inner_ptr: inner_ptr.cast(),
67                marker: PhantomData,
68            },
69        }
70    }
71
72    /// Returns `true` if the two [`GcErased`]s point to the same allocation.
73    #[must_use]
74    pub fn ptr_eq(this: &Self, other: &Self) -> bool {
75        Gc::ptr_eq(&this.inner, &other.inner)
76    }
77
78    /// Returns the [`TypeId`] of the inner type.
79    #[inline]
80    #[must_use]
81    pub fn type_id(&self) -> TypeId {
82        Gc::type_id(&self.inner)
83    }
84
85    /// Returns true if the inner type is the same as `T`.
86    #[inline]
87    #[must_use]
88    pub fn is<T: Trace + 'static>(&self) -> bool {
89        Gc::is::<T>(&self.inner)
90    }
91
92    /// Returns [`Some`] `Gc<T>` if it is of type `T`, or [`None`] if it isn’t.
93    #[inline]
94    #[must_use]
95    pub fn downcast<T: Trace + 'static>(self) -> Option<Gc<T>> {
96        Gc::downcast::<T>(self.inner)
97    }
98
99    /// Downcast the inner value of type `T`.
100    ///
101    /// # Safety
102    ///
103    /// The caller must ensure that the cast is valid.
104    #[inline]
105    #[must_use]
106    pub unsafe fn downcast_unchecked<T: Trace + 'static>(self) -> Gc<T> {
107        // SAFETY: It's the callers responsibility to make sure this is valid.
108        unsafe { Gc::cast_unchecked::<T>(self.inner) }
109    }
110
111    /// Returns reference to the inner value of type `T`.
112    ///
113    /// # Safety
114    ///
115    /// The caller must ensure that the cast is valid.
116    #[inline]
117    #[must_use]
118    pub unsafe fn downcast_ref_unchecked<T: Trace + 'static>(&self) -> &Gc<T> {
119        // SAFETY: It's the callers responsibility to make sure this is valid.
120        unsafe { Gc::cast_ref_unchecked::<T>(&self.inner) }
121    }
122}
123
124impl Debug for GcErased {
125    #[inline]
126    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
127        f.debug_struct("GcErased")
128            .field("inner", &self.inner.inner_ptr)
129            .finish()
130    }
131}
132
133impl Finalize for GcErased {
134    fn finalize(&self) {}
135}
136
137// SAFETY: We only have one transparent field in GcErased that needs trace,
138//         so this is safe.
139unsafe impl Trace for GcErased {
140    custom_trace!(this, mark, {
141        mark(&this.inner);
142    });
143}
144
145impl Clone for GcErased {
146    #[inline]
147    fn clone(&self) -> Self {
148        Self {
149            inner: self.inner.clone(),
150        }
151    }
152}
153
154/// A garbage-collected pointer type over an immutable value.
155pub struct Gc<T: Trace + ?Sized + 'static> {
156    pub(crate) inner_ptr: NonNull<GcBox<T>>,
157    pub(crate) marker: PhantomData<Rc<T>>,
158}
159
160impl<T: Trace + ?Sized> Gc<T> {
161    /// Constructs a new `Gc<T>` with the given value.
162    #[must_use]
163    pub fn new(value: T) -> Self
164    where
165        T: Sized,
166    {
167        // Create GcBox and allocate it to heap.
168        //
169        // Note: Allocator can cause Collector to run
170        let inner_ptr = Allocator::alloc_gc(GcBox::new(value));
171
172        Self {
173            inner_ptr,
174            marker: PhantomData,
175        }
176    }
177
178    /// Constructs a new `Gc<T>` while giving you a `WeakGc<T>` to the allocation, to allow
179    /// constructing a T which holds a weak pointer to itself.
180    ///
181    /// Since the new `Gc<T>` is not fully-constructed until `Gc<T>::new_cyclic` returns, calling
182    /// [`upgrade`][WeakGc::upgrade]  on the weak reference inside the closure will fail and result
183    /// in a `None` value.
184    #[must_use]
185    pub fn new_cyclic<F>(data_fn: F) -> Self
186    where
187        F: FnOnce(&WeakGc<T>) -> T,
188        T: Sized,
189    {
190        // SAFETY: The newly allocated ephemeron is only live here, meaning `Ephemeron` is the
191        // sole owner of the allocation after passing it to `from_raw`, making this operation safe.
192        let weak = unsafe {
193            Ephemeron::from_raw(Allocator::alloc_ephemeron(EphemeronBox::new_empty())).into()
194        };
195
196        let gc = Self::new(data_fn(&weak));
197
198        // SAFETY:
199        // - `as_mut`: `weak` is properly initialized by `alloc_ephemeron` and cannot escape the
200        //   `unsafe` block.
201        // - `set_kv`: `weak` is a newly created `EphemeronBox`, meaning it isn't possible to
202        //   collect it since `weak` is still live.
203        unsafe { weak.inner().inner_ptr().as_mut().set(&gc, ()) }
204
205        gc
206    }
207
208    /// Consumes the `Gc`, returning a wrapped raw pointer.
209    ///
210    /// To avoid a memory leak, the pointer must be converted back to a `Gc` using [`Gc::from_raw`].
211    #[must_use]
212    pub fn into_raw(this: Self) -> NonNull<GcBox<T>> {
213        let ptr = this.inner_ptr();
214        std::mem::forget(this);
215        ptr
216    }
217
218    /// Returns `true` if the two `Gc`s point to the same allocation.
219    #[must_use]
220    pub fn ptr_eq<U: Trace + ?Sized>(this: &Self, other: &Gc<U>) -> bool {
221        std::ptr::addr_eq(this.inner(), other.inner())
222    }
223
224    /// Constructs a `Gc<T>` from a raw pointer.
225    ///
226    /// The raw pointer must have been returned by a previous call to [`Gc<U>::into_raw`][Gc::into_raw]
227    /// where `U` must have the same size and alignment as `T`.
228    ///
229    /// # Safety
230    ///
231    /// This function is unsafe because improper use may lead to memory corruption, double-free,
232    /// or misbehaviour of the garbage collector.
233    #[must_use]
234    pub const unsafe fn from_raw(inner_ptr: NonNull<GcBox<T>>) -> Self {
235        Self {
236            inner_ptr,
237            marker: PhantomData,
238        }
239    }
240
241    pub(crate) fn as_erased_pointer(&self) -> GcErasedPointer {
242        self.inner_ptr.cast()
243    }
244
245    /// Return the [`TypeId`] of the `T`.
246    #[inline]
247    #[must_use]
248    pub fn type_id(this: &Self) -> TypeId {
249        this.vtable().type_id()
250    }
251
252    /// Returns true if the inner type is the same as `T`.
253    #[inline]
254    #[must_use]
255    pub fn is<U: Trace + 'static>(this: &Self) -> bool {
256        Gc::type_id(this) == TypeId::of::<U>()
257    }
258
259    /// Returns [`Some`] reference to the inner value if it is of type `T`, or [`None`] if it isn’t.
260    #[inline]
261    #[must_use]
262    pub fn downcast<U: Trace + 'static>(this: Self) -> Option<Gc<U>> {
263        if !Gc::is::<U>(&this) {
264            return None;
265        }
266
267        // SAFETY: We check that the type is correct above, so this is safe.
268        Some(unsafe { Gc::cast_unchecked::<U>(this) })
269    }
270
271    /// Returns reference to the inner value of type `T`.
272    ///
273    /// # Safety
274    ///
275    /// The caller must ensure that the cast is valid.
276    #[inline]
277    #[must_use]
278    pub unsafe fn cast_unchecked<U: Trace + 'static>(this: Self) -> Gc<U> {
279        let inner_ptr = this.inner_ptr.cast::<U>();
280        core::mem::forget(this); // Prevents double free.
281        Gc {
282            inner_ptr: inner_ptr.cast(),
283            marker: PhantomData,
284        }
285    }
286
287    /// Returns reference to the inner value of type `T`.
288    ///
289    /// # Safety
290    ///
291    /// The caller must ensure that the cast is valid.
292    #[inline]
293    #[must_use]
294    pub unsafe fn cast_ref_unchecked<U: Trace + 'static>(this: &Self) -> &Gc<U> {
295        // SAFETY: Casting a Gc<T> to a Gc<U> of any type is safe, as long as you don’t actually access it as a U.
296        //         The correct functions for T will still be called during tracing, finalization, and dropping.
297        unsafe { &(*(&raw const *this).cast::<Gc<U>>()) }
298    }
299}
300
301impl<T: Trace + ?Sized> Gc<T> {
302    pub(crate) fn vtable(&self) -> &'static VTable {
303        // SAFETY: The inner pointer is valid at all times.
304        unsafe { self.inner_ptr.as_ref() }.vtable
305    }
306
307    #[inline(always)]
308    #[allow(clippy::inline_always)]
309    pub(crate) fn inner_ptr(&self) -> NonNull<GcBox<T>> {
310        debug_assert!(finalizer_safe());
311        self.inner_ptr
312    }
313
314    fn inner(&self) -> &GcBox<T> {
315        // SAFETY: Please see Gc::inner_ptr()
316        unsafe { self.inner_ptr().as_ref() }
317    }
318}
319
320impl<T: Trace + ?Sized> Gc<T> {
321    /// Drops `this` without asking whether a collection is sweeping.
322    ///
323    /// [`Drop`] reads a thread-local flag on every drop because a value freed by the sweep
324    /// may hold pointers into boxes the sweep already freed. Code that cannot run inside a
325    /// sweep, such as the interpreter releasing a frame, can skip that read.
326    ///
327    /// # Safety
328    ///
329    /// The caller is not running inside a garbage collection sweep on this thread.
330    #[inline]
331    pub unsafe fn release_outside_collection(this: Self) {
332        let this = std::mem::ManuallyDrop::new(this);
333        // SAFETY: outside a sweep the box is alive while this pointer holds its reference.
334        unsafe { this.inner_ptr.as_ref().dec_ref_count() };
335    }
336}
337
338impl<T: Trace + ?Sized> Finalize for Gc<T> {
339    fn finalize(&self) {
340        // SAFETY: inner_ptr should be alive when calling finalize.
341        // We don't call inner_ptr() to avoid overhead of calling finalizer_safe().
342        unsafe {
343            self.inner_ptr.as_ref().dec_ref_count();
344        }
345    }
346}
347
348// SAFETY: `Gc` maintains it's own rootedness and implements all methods of
349// Trace. It is not possible to root an already rooted `Gc` and vice versa.
350unsafe impl<T: Trace + ?Sized> Trace for Gc<T> {
351    unsafe fn trace(&self, tracer: &mut Tracer) {
352        tracer.enqueue(self.as_erased_pointer());
353    }
354
355    unsafe fn trace_non_roots(&self) {
356        self.inner().inc_non_root_count();
357    }
358
359    fn run_finalizer(&self) {
360        Finalize::finalize(self);
361    }
362}
363
364impl<T: Trace + ?Sized> Clone for Gc<T> {
365    fn clone(&self) -> Self {
366        let ptr = self.inner_ptr();
367        // SAFETY: though `ptr` doesn't come from a `into_raw` call, it essentially does the same,
368        // but it skips the call to `std::mem::forget` since we have a reference instead of an owned
369        // value.
370        unsafe {
371            ptr.as_ref().inc_ref_count();
372            Self::from_raw(ptr)
373        }
374    }
375}
376
377impl<T: Trace + ?Sized> Deref for Gc<T> {
378    type Target = T;
379
380    fn deref(&self) -> &T {
381        self.inner().value()
382    }
383}
384
385impl<T: Trace + ?Sized> Drop for Gc<T> {
386    fn drop(&mut self) {
387        if finalizer_safe() {
388            Finalize::finalize(self);
389        }
390    }
391}
392
393impl<T: Trace + Default> Default for Gc<T> {
394    fn default() -> Self {
395        Self::new(Default::default())
396    }
397}
398
399#[allow(clippy::inline_always)]
400impl<T: Trace + ?Sized + PartialEq> PartialEq for Gc<T> {
401    #[inline(always)]
402    fn eq(&self, other: &Self) -> bool {
403        **self == **other
404    }
405}
406
407impl<T: Trace + ?Sized + Eq> Eq for Gc<T> {}
408
409#[allow(clippy::inline_always)]
410impl<T: Trace + ?Sized + PartialOrd> PartialOrd for Gc<T> {
411    #[inline(always)]
412    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
413        (**self).partial_cmp(&**other)
414    }
415
416    #[inline(always)]
417    fn lt(&self, other: &Self) -> bool {
418        **self < **other
419    }
420
421    #[inline(always)]
422    fn le(&self, other: &Self) -> bool {
423        **self <= **other
424    }
425
426    #[inline(always)]
427    fn gt(&self, other: &Self) -> bool {
428        **self > **other
429    }
430
431    #[inline(always)]
432    fn ge(&self, other: &Self) -> bool {
433        **self >= **other
434    }
435}
436
437impl<T: Trace + ?Sized + Ord> Ord for Gc<T> {
438    fn cmp(&self, other: &Self) -> Ordering {
439        (**self).cmp(&**other)
440    }
441}
442
443impl<T: Trace + ?Sized + Hash> Hash for Gc<T> {
444    fn hash<H: Hasher>(&self, state: &mut H) {
445        (**self).hash(state);
446    }
447}
448
449impl<T: Trace + ?Sized + Display> Display for Gc<T> {
450    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
451        Display::fmt(&**self, f)
452    }
453}
454
455impl<T: Trace + ?Sized + Debug> Debug for Gc<T> {
456    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
457        Debug::fmt(&**self, f)
458    }
459}
460
461impl<T: Trace + ?Sized> fmt::Pointer for Gc<T> {
462    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
463        fmt::Pointer::fmt(&self.inner(), f)
464    }
465}
466
467impl<T: Trace + ?Sized> std::borrow::Borrow<T> for Gc<T> {
468    fn borrow(&self) -> &T {
469        self
470    }
471}
472
473impl<T: Trace + ?Sized> AsRef<T> for Gc<T> {
474    fn as_ref(&self) -> &T {
475        self
476    }
477}