Skip to main content

boa_gc/
cell.rs

1//! A garbage collected cell implementation
2
3use crate::{
4    Tracer,
5    trace::{Finalize, Trace},
6};
7use std::marker::PhantomData;
8use std::ptr::NonNull;
9use std::{
10    cell::{Cell, UnsafeCell},
11    cmp::Ordering,
12    fmt::{self, Debug, Display},
13    hash::Hash,
14    ops::{Deref, DerefMut},
15};
16
17/// `BorrowFlag` represent the internal state of a `GcCell` and
18/// keeps track of the number of current borrows.
19#[derive(Copy, Clone)]
20struct BorrowFlag(usize);
21
22/// `BorrowState` represents the various states of a `BorrowFlag`
23///
24///  - Reading: the value is currently being read/borrowed.
25///  - Writing: the value is currently being written/borrowed mutably.
26///  - Unused: the value is currently unrooted.
27#[derive(Copy, Clone, Debug, Eq, PartialEq)]
28enum BorrowState {
29    Reading,
30    Writing,
31    Unused,
32}
33
34const WRITING: usize = !0;
35const UNUSED: usize = 0;
36
37/// The base borrow flag init is rooted, and has no outstanding borrows.
38const BORROWFLAG_INIT: BorrowFlag = BorrowFlag(UNUSED);
39
40impl BorrowFlag {
41    /// Check the current `BorrowState` of `BorrowFlag`.
42    const fn borrowed(self) -> BorrowState {
43        match self.0 {
44            UNUSED => BorrowState::Unused,
45            WRITING => BorrowState::Writing,
46            _ => BorrowState::Reading,
47        }
48    }
49
50    /// Set the `BorrowFlag`'s state to writing.
51    const fn set_writing(self) -> Self {
52        Self(self.0 | WRITING)
53    }
54
55    /// Increments the counter for a new borrow.
56    ///
57    /// # Panic
58    ///  - This method will panic if the current `BorrowState` is writing.
59    ///  - This method will panic after incrementing if the borrow count overflows.
60    #[inline]
61    fn add_reading(self) -> Self {
62        assert_ne!(self.borrowed(), BorrowState::Writing);
63        let flags = Self(self.0 + 1);
64
65        // This will fail if the borrow count overflows, which shouldn't happen,
66        // but let's be safe
67        {
68            assert_eq!(flags.borrowed(), BorrowState::Reading);
69        }
70        flags
71    }
72
73    /// Decrements the counter to remove a borrow.
74    ///
75    /// # Panic
76    ///  - This method will panic if the current `BorrowState` is not reading.
77    fn sub_reading(self) -> Self {
78        assert_eq!(self.borrowed(), BorrowState::Reading);
79        Self(self.0 - 1)
80    }
81}
82
83impl Debug for BorrowFlag {
84    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85        f.debug_struct("BorrowFlag")
86            .field("State", &self.borrowed())
87            .finish()
88    }
89}
90
91/// A mutable memory location with dynamically checked borrow rules
92/// that can be used inside of a garbage-collected pointer.
93///
94/// This object is a `RefCell` that can be used inside of a `Gc<T>`.
95pub struct GcRefCell<T: ?Sized + 'static> {
96    borrow: Cell<BorrowFlag>,
97    cell: UnsafeCell<T>,
98}
99
100impl<T> GcRefCell<T> {
101    /// Creates a new `GcCell` containing `value`.
102    pub const fn new(value: T) -> Self {
103        Self {
104            borrow: Cell::new(BORROWFLAG_INIT),
105            cell: UnsafeCell::new(value),
106        }
107    }
108
109    /// Consumes the `GcCell`, returning the wrapped value.
110    pub fn into_inner(self) -> T {
111        self.cell.into_inner()
112    }
113}
114
115impl<T: ?Sized> GcRefCell<T> {
116    /// Immutably borrows the wrapped value.
117    ///
118    /// The borrow lasts until the returned `GcCellRef` exits scope.
119    /// Multiple immutable borrows can be taken out at the same time.
120    ///
121    /// # Panics
122    ///
123    /// Panics if the value is currently mutably borrowed.
124    pub fn borrow(&self) -> GcRef<'_, T> {
125        match self.try_borrow() {
126            Ok(value) => value,
127            Err(e) => panic!("{}", e),
128        }
129    }
130
131    /// Mutably borrows the wrapped value.
132    ///
133    /// The borrow lasts until the returned `GcCellRefMut` exits scope.
134    /// The value cannot be borrowed while this borrow is active.
135    ///
136    /// # Panics
137    ///
138    /// Panics if the value is currently borrowed.
139    #[track_caller]
140    pub fn borrow_mut(&self) -> GcRefMut<'_, T> {
141        match self.try_borrow_mut() {
142            Ok(value) => value,
143            Err(e) => panic!("{}", e),
144        }
145    }
146
147    /// Immutably borrows the wrapped value, returning an error if the value is currently mutably
148    /// borrowed.
149    ///
150    /// The borrow lasts until the returned `GcCellRef` exits scope. Multiple immutable borrows can be
151    /// taken out at the same time.
152    ///
153    /// This is the non-panicking variant of [`borrow`](#method.borrow).
154    ///
155    /// # Errors
156    ///
157    /// Returns an `Err` if the value is currently mutably borrowed.
158    pub fn try_borrow(&self) -> Result<GcRef<'_, T>, BorrowError> {
159        if self.borrow.get().borrowed() == BorrowState::Writing {
160            return Err(BorrowError);
161        }
162        self.borrow.set(self.borrow.get().add_reading());
163
164        // SAFETY: calling value on a rooted value may cause Undefined Behavior
165        unsafe {
166            Ok(GcRef {
167                borrow: BorrowGcRef {
168                    borrow: &self.borrow,
169                },
170                value: NonNull::new_unchecked(self.cell.get()),
171            })
172        }
173    }
174
175    /// Mutably borrows the wrapped value, returning an error if the value is currently borrowed.
176    ///
177    /// The borrow lasts until the returned `GcCellRefMut` exits scope.
178    /// The value cannot be borrowed while this borrow is active.
179    ///
180    /// This is the non-panicking variant of [`borrow_mut`](#method.borrow_mut).
181    ///
182    /// # Errors
183    ///
184    /// Returns an `Err` if the value is currently borrowed.
185    pub fn try_borrow_mut(&self) -> Result<GcRefMut<'_, T>, BorrowMutError> {
186        if self.borrow.get().borrowed() != BorrowState::Unused {
187            return Err(BorrowMutError);
188        }
189        self.borrow.set(self.borrow.get().set_writing());
190
191        // SAFETY: This is safe as the value is rooted if it was not previously rooted,
192        // so it cannot be dropped.
193        unsafe {
194            Ok(GcRefMut {
195                borrow: BorrowGcRefMut {
196                    borrow: &self.borrow,
197                },
198                value: NonNull::new_unchecked(self.cell.get()),
199                marker: PhantomData,
200            })
201        }
202    }
203}
204
205/// An error returned by [`GcCell::try_borrow`](struct.GcCell.html#method.try_borrow).
206#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Default, Hash)]
207pub struct BorrowError;
208
209impl Display for BorrowError {
210    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
211        Display::fmt("GcCell<T> already mutably borrowed", f)
212    }
213}
214
215/// An error returned by [`GcCell::try_borrow_mut`](struct.GcCell.html#method.try_borrow_mut).
216#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Default, Hash)]
217pub struct BorrowMutError;
218
219impl Display for BorrowMutError {
220    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
221        Display::fmt("GcCell<T> already borrowed", f)
222    }
223}
224
225impl<T: Trace + ?Sized> Finalize for GcRefCell<T> {}
226
227// SAFETY: GcCell maintains its own BorrowState and rootedness. GcCell's implementation
228// focuses on only continuing Trace-based methods while the cell state is not written.
229// Implementing a Trace while the cell is being written to or incorrectly implementing Trace
230// on GcCell's value may cause Undefined Behavior
231unsafe impl<T: Trace + ?Sized> Trace for GcRefCell<T> {
232    unsafe fn trace(&self, tracer: &mut Tracer) {
233        match self.borrow.get().borrowed() {
234            BorrowState::Writing => (),
235            // SAFETY: Please see GcCell's Trace impl Safety note.
236            _ => unsafe { (*self.cell.get()).trace(tracer) },
237        }
238    }
239
240    unsafe fn trace_non_roots(&self) {
241        match self.borrow.get().borrowed() {
242            BorrowState::Writing => (),
243            // SAFETY: Please see GcCell's Trace impl Safety note.
244            _ => unsafe { (*self.cell.get()).trace_non_roots() },
245        }
246    }
247
248    fn run_finalizer(&self) {
249        Finalize::finalize(self);
250        match self.borrow.get().borrowed() {
251            BorrowState::Writing => (),
252            // SAFETY: Please see GcCell's Trace impl Safety note.
253            _ => unsafe { (*self.cell.get()).run_finalizer() },
254        }
255    }
256}
257
258struct BorrowGcRef<'a> {
259    borrow: &'a Cell<BorrowFlag>,
260}
261
262impl Drop for BorrowGcRef<'_> {
263    fn drop(&mut self) {
264        debug_assert_eq!(self.borrow.get().borrowed(), BorrowState::Reading);
265        self.borrow.set(self.borrow.get().sub_reading());
266    }
267}
268
269impl Clone for BorrowGcRef<'_> {
270    #[inline]
271    fn clone(&self) -> Self {
272        self.borrow.set(self.borrow.get().add_reading());
273        BorrowGcRef {
274            borrow: self.borrow,
275        }
276    }
277}
278
279/// A wrapper type for an immutably borrowed value from a `GcCell<T>`.
280pub struct GcRef<'a, T: ?Sized + 'static> {
281    value: NonNull<T>,
282    borrow: BorrowGcRef<'a>,
283}
284
285impl<'a, T: ?Sized> GcRef<'a, T> {
286    /// Casts to a `GcRef` of another type.
287    ///
288    /// # Safety
289    /// * The caller must ensure that `T` can be safely cast to `U`.
290    #[must_use]
291    pub unsafe fn cast<U>(orig: Self) -> GcRef<'a, U> {
292        let value = orig.value.cast::<U>();
293
294        GcRef {
295            borrow: orig.borrow,
296            value,
297        }
298    }
299
300    /// Copies a `GcCellRef`.
301    ///
302    /// The `GcCell` is already immutably borrowed, so this cannot fail.
303    ///
304    /// This is an associated function that needs to be used as
305    /// `GcCellRef::clone(...)`. A `Clone` implementation or a method
306    /// would interfere with the use of `c.borrow().clone()` to clone
307    /// the contents of a `GcCell`.
308    #[allow(clippy::should_implement_trait)]
309    #[must_use]
310    pub fn clone(orig: &GcRef<'a, T>) -> GcRef<'a, T> {
311        GcRef {
312            borrow: orig.borrow.clone(),
313            value: orig.value,
314        }
315    }
316
317    /// Tries to make a new `GcCellRef` from a component of the borrowed data, returning `None`
318    /// if the mapping function returns `None`.
319    ///
320    /// The `GcCell` is already immutably borrowed, so this cannot fail.
321    ///
322    /// This is an associated function that needs to be used as `GcCellRef::try_map(...)`.
323    /// A method would interfere with methods of the same name on the contents
324    /// of a `GcCellRef` used through `Deref`.
325    pub fn try_map<U, F>(orig: Self, f: F) -> Option<GcRef<'a, U>>
326    where
327        U: ?Sized,
328        F: FnOnce(&T) -> Option<&U>,
329    {
330        let value = NonNull::from(f(&*orig)?);
331
332        let ret = GcRef {
333            borrow: orig.borrow,
334            value,
335        };
336
337        Some(ret)
338    }
339
340    /// Makes a new `GcCellRef` from a component of the borrowed data.
341    ///
342    /// The `GcCell` is already immutably borrowed, so this cannot fail.
343    ///
344    /// This is an associated function that needs to be used as `GcCellRef::map(...)`.
345    /// A method would interfere with methods of the same name on the contents
346    /// of a `GcCellRef` used through `Deref`.
347    pub fn map<U, F>(orig: Self, f: F) -> GcRef<'a, U>
348    where
349        U: ?Sized,
350        F: FnOnce(&T) -> &U,
351    {
352        let value = NonNull::from(f(&*orig));
353
354        GcRef {
355            borrow: orig.borrow,
356            value,
357        }
358    }
359
360    /// Splits a `GcCellRef` into multiple `GcCellRef`s for different components of the borrowed data.
361    ///
362    /// The `GcCell` is already immutably borrowed, so this cannot fail.
363    ///
364    /// This is an associated function that needs to be used as `GcCellRef::map_split(...)`.
365    /// A method would interfere with methods of the same name on the contents of a `GcCellRef` used through `Deref`.
366    pub fn map_split<U, V, F>(orig: Self, f: F) -> (GcRef<'a, U>, GcRef<'a, V>)
367    where
368        U: ?Sized,
369        V: ?Sized,
370        F: FnOnce(&T) -> (&U, &V),
371    {
372        let (a, b) = f(&*orig);
373        let borrow = orig.borrow.clone();
374
375        (
376            GcRef {
377                borrow,
378                value: NonNull::from(a),
379            },
380            GcRef {
381                value: NonNull::from(b),
382                borrow: orig.borrow,
383            },
384        )
385    }
386}
387
388impl<T: ?Sized> Deref for GcRef<'_, T> {
389    type Target = T;
390
391    fn deref(&self) -> &T {
392        unsafe { self.value.as_ref() }
393    }
394}
395
396impl<T: ?Sized + Debug> Debug for GcRef<'_, T> {
397    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
398        Debug::fmt(&**self, f)
399    }
400}
401
402impl<T: ?Sized + Display> Display for GcRef<'_, T> {
403    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
404        Display::fmt(&**self, f)
405    }
406}
407
408struct BorrowGcRefMut<'a> {
409    borrow: &'a Cell<BorrowFlag>,
410}
411
412impl Drop for BorrowGcRefMut<'_> {
413    fn drop(&mut self) {
414        debug_assert_eq!(self.borrow.get().borrowed(), BorrowState::Writing);
415        self.borrow.set(BorrowFlag(UNUSED));
416    }
417}
418
419/// A wrapper type for a mutably borrowed value from a `GcCell<T>`.
420pub struct GcRefMut<'a, T: ?Sized> {
421    // NB: we use a pointer instead of `&'a mut U` to avoid `noalias` violations, because
422    // a `GcRefMut` argument doesn't hold exclusivity for its whole scope, only until it
423    // drops.
424    value: NonNull<T>,
425    borrow: BorrowGcRefMut<'a>,
426    // `NonNull` is covariant over `T`, so we need to reintroduce invariance.
427    marker: PhantomData<&'a mut T>,
428}
429
430impl<'a, T: ?Sized> GcRefMut<'a, T> {
431    /// Casts to a `GcRefMut` of another type.
432    ///
433    /// # Safety
434    /// * The caller must ensure that `U` can be safely cast to `V`.
435    #[must_use]
436    pub unsafe fn cast<V>(orig: Self) -> GcRefMut<'a, V> {
437        let value = orig.value.cast::<V>();
438
439        GcRefMut {
440            borrow: orig.borrow,
441            value,
442            marker: PhantomData,
443        }
444    }
445
446    /// Tries to make a new `GcCellRefMut` for a component of the borrowed data, returning `None`
447    /// if the mapping function returns `None`.
448    ///
449    /// The `GcCellRefMut` is already mutably borrowed, so this cannot fail.
450    ///
451    /// This is an associated function that needs to be used as
452    /// `GcCellRefMut::map(...)`. A method would interfere with methods of the same
453    /// name on the contents of a `GcCell` used through `Deref`.
454    pub fn try_map<V, F>(mut orig: GcRefMut<'a, T>, f: F) -> Option<GcRefMut<'a, V>>
455    where
456        V: ?Sized,
457        F: FnOnce(&mut T) -> Option<&mut V>,
458    {
459        let value = NonNull::from(f(&mut *orig)?);
460
461        let ret = GcRefMut {
462            borrow: orig.borrow,
463            value,
464            marker: PhantomData,
465        };
466
467        Some(ret)
468    }
469
470    /// Makes a new `GcCellRefMut` for a component of the borrowed data, e.g., an enum
471    /// variant.
472    ///
473    /// The `GcCellRefMut` is already mutably borrowed, so this cannot fail.
474    ///
475    /// This is an associated function that needs to be used as
476    /// `GcCellRefMut::map(...)`. A method would interfere with methods of the same
477    /// name on the contents of a `GcCell` used through `Deref`.
478    pub fn map<V, F>(mut orig: Self, f: F) -> GcRefMut<'a, V>
479    where
480        V: ?Sized,
481        F: FnOnce(&mut T) -> &mut V,
482    {
483        let value = NonNull::from(f(&mut *orig));
484
485        GcRefMut {
486            borrow: orig.borrow,
487            value,
488            marker: PhantomData,
489        }
490    }
491}
492
493impl<T: ?Sized> Deref for GcRefMut<'_, T> {
494    type Target = T;
495
496    fn deref(&self) -> &T {
497        // SAFETY: the value is accessible as long as we hold our borrow.
498        unsafe { self.value.as_ref() }
499    }
500}
501
502impl<T: ?Sized> DerefMut for GcRefMut<'_, T> {
503    fn deref_mut(&mut self) -> &mut T {
504        // SAFETY: the value is accessible as long as we hold our borrow.
505        unsafe { self.value.as_mut() }
506    }
507}
508
509impl<T: Debug + ?Sized> Debug for GcRefMut<'_, T> {
510    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
511        Debug::fmt(&**self, f)
512    }
513}
514
515impl<T: Display + ?Sized> Display for GcRefMut<'_, T> {
516    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
517        Display::fmt(&**self, f)
518    }
519}
520
521// SAFETY: GcCell<T> tracks it's `BorrowState` is `Writing`
522unsafe impl<T: ?Sized + Send> Send for GcRefCell<T> {}
523
524impl<T: Trace + Clone> Clone for GcRefCell<T> {
525    fn clone(&self) -> Self {
526        Self::new(self.borrow().clone())
527    }
528}
529
530impl<T: Default> Default for GcRefCell<T> {
531    fn default() -> Self {
532        Self::new(Default::default())
533    }
534}
535
536#[allow(clippy::inline_always)]
537impl<T: ?Sized + PartialEq> PartialEq for GcRefCell<T> {
538    #[inline(always)]
539    fn eq(&self, other: &Self) -> bool {
540        *self.borrow() == *other.borrow()
541    }
542}
543
544impl<T: ?Sized + Eq> Eq for GcRefCell<T> {}
545
546#[allow(clippy::inline_always)]
547impl<T: ?Sized + PartialOrd> PartialOrd for GcRefCell<T> {
548    #[inline(always)]
549    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
550        (*self.borrow()).partial_cmp(&*other.borrow())
551    }
552
553    #[inline(always)]
554    fn lt(&self, other: &Self) -> bool {
555        *self.borrow() < *other.borrow()
556    }
557
558    #[inline(always)]
559    fn le(&self, other: &Self) -> bool {
560        *self.borrow() <= *other.borrow()
561    }
562
563    #[inline(always)]
564    fn gt(&self, other: &Self) -> bool {
565        *self.borrow() > *other.borrow()
566    }
567
568    #[inline(always)]
569    fn ge(&self, other: &Self) -> bool {
570        *self.borrow() >= *other.borrow()
571    }
572}
573
574impl<T: ?Sized + Ord> Ord for GcRefCell<T> {
575    fn cmp(&self, other: &Self) -> Ordering {
576        (*self.borrow()).cmp(&*other.borrow())
577    }
578}
579
580impl<T: ?Sized + Debug> Debug for GcRefCell<T> {
581    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
582        match self.borrow.get().borrowed() {
583            BorrowState::Unused | BorrowState::Reading => f
584                .debug_struct("GcCell")
585                .field("flags", &self.borrow.get())
586                .field("value", &self.borrow())
587                .finish_non_exhaustive(),
588            BorrowState::Writing => f
589                .debug_struct("GcCell")
590                .field("flags", &self.borrow.get())
591                .field("value", &"<borrowed>")
592                .finish_non_exhaustive(),
593        }
594    }
595}