rune/core/gc/
root.rs

1use super::{
2    super::{cons::Cons, object::Object},
3    GcMoveable, GcState, TracePtr,
4};
5use super::{Block, Context, RootSet, Trace};
6use crate::core::object::{Gc, GcPtr, IntoObject, ObjectType, OptionalFlag, Untag, WithLifetime};
7use rune_core::hashmap::IndexMap;
8use std::hash::{Hash, Hasher};
9use std::slice::SliceIndex;
10use std::{
11    cell::UnsafeCell,
12    fmt,
13    ops::{Deref, DerefMut, Index, IndexMut, RangeBounds},
14};
15use std::{
16    fmt::{Debug, Display},
17    marker::PhantomPinned,
18};
19
20/// Helper trait to break the dependency between an object and the lifetimes of
21/// it's [traceable](Trace) children. If This trait is implemented, then the
22/// object can be traced by the garbage collector. Once it becomes rooted, it as
23/// well as all of it's tracable children will be live until it is unrooted.
24/// This essentially makes any lifetimes of a tracable objects meaningless. They
25/// can be anything, including 'static. When an object is removed from a root it
26/// will be given the proper lifetime again. Care must be taken to ensure that
27/// any lifetimes that are changed only belong to traceable objects. Object can
28/// contain lifetimes parameters for both traceable and untracable children, and
29/// only the traceable children's lifetimes can be changed.
30///
31/// On top of scrubbing the lifetimes, this trait can also do a transformation
32/// of the underlying type for convenience, similar to calling `Into::into`.
33#[diagnostic::on_unimplemented(
34    message = "`{Self}` does not implement `Trace`",
35    label = "cannot be rooted",
36    note = "Use #[derive(Trace)] to make `{Self}` Rootable",
37    note = "If this is a foreign type, implement `Trace` and `IntoRoot` manually"
38)]
39pub(crate) trait IntoRoot<T> {
40    unsafe fn into_root(self) -> T;
41}
42
43impl<'new, T, U> IntoRoot<Slot<U>> for T
44where
45    T: WithLifetime<'new, Out = U> + GcPtr,
46{
47    unsafe fn into_root(self) -> Slot<U> {
48        Slot::new(self.with_lifetime())
49    }
50}
51
52impl<T, U> IntoRoot<Option<U>> for Option<T>
53where
54    T: IntoRoot<U>,
55{
56    unsafe fn into_root(self) -> Option<U> {
57        self.map(|x| x.into_root())
58    }
59}
60
61impl<T, U> IntoRoot<Vec<U>> for Vec<T>
62where
63    T: IntoRoot<U>,
64{
65    unsafe fn into_root(self) -> Vec<U> {
66        let mut new = Vec::with_capacity(self.len());
67        for x in self {
68            new.push(x.into_root());
69        }
70        new
71    }
72}
73
74impl<T, U, Tx, Ux> IntoRoot<(Tx, Ux)> for (T, U)
75where
76    T: IntoRoot<Tx>,
77    U: IntoRoot<Ux>,
78{
79    unsafe fn into_root(self) -> (Tx, Ux) {
80        (self.0.into_root(), self.1.into_root())
81    }
82}
83
84impl<'a, T, U> IntoRoot<Slot<U>> for &Rt<Slot<T>>
85where
86    T: WithLifetime<'a, Out = U> + Copy,
87{
88    unsafe fn into_root(self) -> Slot<U> {
89        Slot::new(self.inner().get().with_lifetime())
90    }
91}
92
93impl<'a> IntoRoot<Slot<Object<'a>>> for bool {
94    unsafe fn into_root(self) -> Slot<Object<'a>> {
95        Slot::new(self.into())
96    }
97}
98
99impl<'a> IntoRoot<Slot<Object<'a>>> for i64 {
100    unsafe fn into_root(self) -> Slot<Object<'a>> {
101        Slot::new(self.into())
102    }
103}
104
105// Represents an object T rooted on the Stack. This will remove the the object
106// from the root set when dropped.
107#[doc(hidden)]
108pub(crate) struct __StackRoot<'rt, T> {
109    data: &'rt mut Rt<T>,
110    root_set: &'rt RootSet,
111}
112
113impl<T> AsMut<Rt<T>> for __StackRoot<'_, T> {
114    fn as_mut(&mut self) -> &mut Rt<T> {
115        self.data
116    }
117}
118
119impl<T: Debug> Debug for __StackRoot<'_, T> {
120    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
121        Debug::fmt(self.data, f)
122    }
123}
124
125impl<T: Display> Display for __StackRoot<'_, T> {
126    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
127        Display::fmt(self.data, f)
128    }
129}
130
131// Do not use this function directly. Use the `root` macro instead.
132//
133// SAFETY: An owned StackRoot must never be exposed to the rest of the program.
134// That could result in calling `mem::forget` on the root, which would
135// invalidate the stack property of the root set.
136impl<'rt, T: Trace> __StackRoot<'rt, T> {
137    pub(crate) unsafe fn new(data: &'rt mut T, root_set: &'rt RootSet) -> __StackRoot<'rt, T> {
138        let dyn_ptr = data as &mut dyn Trace as *mut dyn Trace;
139        // We are using this transmute to dissociate the `dyn Trace` from the T.
140        // Otherwise rust tries to require us to add a 'static bound. We don't
141        // need this because stackroot can't outlive data (due to the 'rt
142        // lifetime), and therefore it can't outlive T.
143        let dyn_ptr = std::mem::transmute::<*mut dyn Trace, *mut dyn Trace>(dyn_ptr);
144        let data = &mut *(dyn_ptr.cast::<Rt<T>>());
145        let root = Self { data, root_set };
146        root_set.roots.borrow_mut().push(dyn_ptr);
147        root
148    }
149}
150
151impl<T> Drop for __StackRoot<'_, T> {
152    fn drop(&mut self) {
153        self.root_set.roots.borrow_mut().pop();
154    }
155}
156
157/// Trait created to overpass the orphan rule when deriving the
158/// [Trace](`rune_macros::Trace`) derive macro. The derive
159/// macro contains a blanket `Deref` (and `DerefMut`) like this:
160///
161/// ```ignore
162/// unsafe { &*(rt as *const Rt<Self>).cast::<Self::Target>() }
163/// ```
164///
165/// By creating a trait that the functions defined in the main crate
166/// can define, we avoid the orphan rule by implementing `Deref`
167/// on the rooted version of the types: [Rt\<T\>](`self::Rt`).
168pub trait RootedDeref {
169    type Target;
170    fn rooted_deref(rooted: &Rt<Self>) -> &Self::Target;
171    fn rooted_derefmut(rooted: &mut Rt<Self>) -> &mut Self::Target;
172}
173
174impl<T: RootedDeref> Deref for Rt<T> {
175    type Target = <T as RootedDeref>::Target;
176    fn deref(&self) -> &Self::Target {
177        RootedDeref::rooted_deref(self)
178    }
179}
180
181impl<T: RootedDeref> DerefMut for Rt<T> {
182    fn deref_mut(&mut self) -> &mut Self::Target {
183        RootedDeref::rooted_derefmut(self)
184    }
185}
186
187/// A "Root Tracable" type. If a type is wrapped in Rt, it is known to be rooted
188/// and hold items past garbage collection. This type is never used as an owned
189/// type, only a reference. This ensures that underlying data does not move. In
190/// order to access the inner data, use [`Rt::bind_ref`] or [`Rt::bind_mut`]
191/// methods.
192#[repr(transparent)]
193#[derive(PartialEq, Eq)]
194pub struct Rt<T: ?Sized> {
195    _aliasable: PhantomPinned,
196    inner: T,
197}
198
199/// A "Root Tracable Object". This differs from [`Rt`] by wrapping a [`Slot`]
200/// which allows the underlying data to be moved during garbage collection. GC
201/// owned types will always be wrapped in a `Slot` when rooted. To access the
202/// object, use [`Rto::bind`].
203pub type Rto<T> = Rt<Slot<T>>;
204
205/// A moveable pointer to the GC heap. This is used to wrap [Rooted](`Rt`)
206/// [`Object`]'s so that we don't trigger UB when moving types during
207/// collection.
208#[repr(transparent)]
209#[derive(Default)]
210pub struct Slot<T: ?Sized> {
211    inner: UnsafeCell<T>,
212}
213
214impl<T: Clone> Clone for Slot<T> {
215    fn clone(&self) -> Self {
216        Self::new(self.get().clone())
217    }
218}
219
220impl<'new, T: WithLifetime<'new> + Copy> WithLifetime<'new> for Slot<T> {
221    type Out = Slot<<T as WithLifetime<'new>>::Out>;
222
223    unsafe fn with_lifetime(self) -> Self::Out {
224        Slot::new(self.get().with_lifetime())
225    }
226}
227
228impl<T: Hash> Hash for Slot<T> {
229    fn hash<H: Hasher>(&self, state: &mut H) {
230        self.get().hash(state);
231    }
232}
233
234impl<T: PartialEq> PartialEq for Slot<T> {
235    fn eq(&self, other: &Self) -> bool {
236        self.get() == other.get()
237    }
238}
239
240// This type signature is so complex due to lifetime restrictions around
241// invariance. deriving PartialEq will provide T == T. But if the lifetime is
242// invariant than two types with different lifetimes will be different types.
243// Using an UnsafeCell makes the lifetime invariant So we have to use
244// WithLifetime to convert the lifetime to the same invariant lifetime 'a.
245impl<'a, T, U> PartialEq<U> for Slot<T>
246where
247    U: WithLifetime<'a> + Copy,
248    T: PartialEq<<U as WithLifetime<'a>>::Out>,
249{
250    fn eq(&self, other: &U) -> bool {
251        *self.get() == unsafe { other.with_lifetime() }
252    }
253}
254
255impl<T: Eq> Eq for Slot<T> {}
256
257impl<T> Slot<T> {
258    fn get(&self) -> &T {
259        unsafe { &*self.inner.get() }
260    }
261
262    unsafe fn set(&self, new: T) {
263        *self.inner.get() = new
264    }
265
266    pub(crate) fn new(val: T) -> Self {
267        Slot { inner: UnsafeCell::new(val) }
268    }
269}
270
271impl<T> Deref for Slot<T> {
272    type Target = T;
273
274    fn deref(&self) -> &Self::Target {
275        self.get()
276    }
277}
278
279// This is the boundary between the traceable structs, and heap objects. Each
280// heap object that is rooted needs to live in a Slot, so everything below it is
281// also a heap object. We completely trace each object graph before moving to
282// the next slot.
283impl<T> Trace for Slot<T>
284where
285    T: TracePtr + GcMoveable<Value = T>,
286{
287    fn trace(&self, state: &mut GcState) {
288        if let Some((new, moved)) = self.get().move_value(&state.to_space) {
289            unsafe { self.set(new) };
290            if moved {
291                self.get().trace_ptr(state);
292                // finish tracing anything connected to this object. This will
293                // help them be co-located in memory.
294                state.trace_stack();
295            }
296        }
297    }
298}
299
300impl<T: Debug> Debug for Rt<T> {
301    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
302        Debug::fmt(&self.inner(), f)
303    }
304}
305
306impl<T: Display> Display for Rt<T> {
307    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
308        Display::fmt(&self.inner(), f)
309    }
310}
311
312impl<T: Debug> Debug for Slot<T> {
313    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
314        Debug::fmt(self.get(), f)
315    }
316}
317
318impl<T: Display> Display for Slot<T> {
319    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
320        Display::fmt(self.get(), f)
321    }
322}
323
324impl<T, U> PartialEq<U> for Rt<Slot<T>>
325where
326    Slot<T>: PartialEq<U>,
327{
328    fn eq(&self, other: &U) -> bool {
329        self.inner() == other
330    }
331}
332
333impl<T> Hash for Rt<T>
334where
335    T: Hash,
336{
337    fn hash<H: Hasher>(&self, state: &mut H) {
338        self.inner().hash(state);
339    }
340}
341
342impl<T> Rt<T> {
343    fn inner(&self) -> &T {
344        &self.inner
345    }
346
347    fn inner_mut(&mut self) -> &mut T {
348        &mut self.inner
349    }
350
351    fn inner_ptr(&self) -> *const T {
352        &self.inner as *const T
353    }
354
355    fn inner_mut_ptr(&mut self) -> *mut T {
356        &mut self.inner as *mut T
357    }
358
359    pub(crate) fn bind_ref<'a, 'ob>(&'a self, _: &'ob Context) -> &'a <T as WithLifetime<'ob>>::Out
360    where
361        T: WithLifetime<'ob>,
362    {
363        // SAFETY: We are holding a reference to the context
364        unsafe { &*self.inner_ptr().cast::<<T as WithLifetime<'ob>>::Out>() }
365    }
366
367    pub(crate) fn bind_mut<'a, 'ob>(
368        &'a mut self,
369        _: &'ob Context,
370    ) -> &'a mut <T as WithLifetime<'ob>>::Out
371    where
372        T: WithLifetime<'ob>,
373    {
374        // SAFETY: We are holding a reference to the context
375        unsafe { &mut *self.inner_mut_ptr().cast::<<T as WithLifetime<'ob>>::Out>() }
376    }
377
378    pub(crate) fn set<U: IntoRoot<T>>(&mut self, item: U) {
379        // SAFETY: we drop the old type so it never exposed and take the new
380        // rooted type and replace it.
381        unsafe { *self.inner_mut() = item.into_root() }
382    }
383}
384
385impl<T> Rt<Slot<T>> {
386    pub(crate) fn bind<'ob>(&self, _: &'ob Context) -> <T as WithLifetime<'ob>>::Out
387    where
388        T: WithLifetime<'ob> + Copy,
389    {
390        // SAFETY: We are holding a reference to the context
391        unsafe { self.inner().get().with_lifetime() }
392    }
393
394    pub(crate) unsafe fn bind_unchecked<'ob>(&'ob self) -> <T as WithLifetime<'ob>>::Out
395    where
396        T: WithLifetime<'ob> + Copy,
397    {
398        self.inner().get().with_lifetime()
399    }
400
401    pub(crate) fn bind_slice<'brw, 'ob, U>(
402        slice: &'brw [Rt<Slot<Gc<T>>>],
403        _: &'ob Context,
404    ) -> &'brw [Gc<U>]
405    where
406        Gc<T>: WithLifetime<'ob, Out = Gc<U>>,
407        Gc<U>: 'ob,
408    {
409        // SAFETY: Gc<T> does not have any niche optimizations, so it is safe to
410        // cast from a Rt<Slot>
411        unsafe { &*(slice as *const [Rt<Slot<Gc<T>>>] as *const [Gc<U>]) }
412    }
413}
414
415impl<T> Rt<Slot<Gc<T>>> {
416    /// Calls [untag](Untag::untag_erased) on the tagged Gc pointer
417    pub(crate) fn untag<'ob, U>(&self, cx: &'ob Context) -> U
418    where
419        Gc<T>: WithLifetime<'ob, Out = Gc<U>> + Copy,
420        Gc<U>: Untag<U>,
421    {
422        cx.bind(*self.inner().get()).untag_erased()
423    }
424
425    /// Like `try_into`, but needed to due no specialization
426    pub(crate) fn try_as<U, E>(&self) -> Result<&Rt<Slot<Gc<U>>>, E>
427    where
428        Gc<T>: TryInto<Gc<U>, Error = E> + Copy,
429    {
430        let _: Gc<U> = (*self.inner().get()).try_into()?;
431        // SAFETY: This is safe because all Gc types have the same representation
432        unsafe { Ok(&*((self as *const Self).cast::<Rt<Slot<Gc<U>>>>())) }
433    }
434}
435
436impl TryFrom<&Rt<Slot<Object<'_>>>> for usize {
437    type Error = anyhow::Error;
438
439    fn try_from(value: &Rt<Slot<Object>>) -> Result<Self, Self::Error> {
440        (*value.inner().get()).try_into()
441    }
442}
443
444impl<T> Rt<Slot<Gc<T>>> {
445    /// Like `try_into().bind(cx)`, but needed to due no specialization
446    pub(crate) fn bind_as<'ob, U, E>(&self, _cx: &'ob Context) -> Result<U, E>
447    where
448        Gc<T>: WithLifetime<'ob> + Copy,
449        <Gc<T> as WithLifetime<'ob>>::Out: TryInto<U, Error = E> + Copy,
450    {
451        unsafe { self.inner().get().with_lifetime().try_into() }
452    }
453
454    /// Like `Into`, but needed to due no specialization
455    pub(crate) fn cast<U>(&self) -> &Rt<Slot<Gc<U>>>
456    where
457        Gc<T>: Into<Gc<U>> + Copy,
458    {
459        // SAFETY: This is safe because all Gc types have the same representation
460        unsafe { &*((self as *const Self).cast::<Rt<Slot<Gc<U>>>>()) }
461    }
462
463    // TODO: Find a way to remove this method. We should never need to guess
464    // if something is cons
465    pub(crate) fn as_cons(&self) -> &Rt<Slot<Gc<&Cons>>> {
466        match self.inner().as_obj().untag() {
467            crate::core::object::ObjectType::Cons(_) => unsafe {
468                &*(self as *const Self).cast::<Rt<Slot<Gc<&Cons>>>>()
469            },
470            x => panic!("attempt to convert type that was not cons: {x}"),
471        }
472    }
473}
474
475impl From<&Rt<Slot<Object<'_>>>> for OptionalFlag {
476    fn from(value: &Rt<Slot<Object<'_>>>) -> Self {
477        value.inner().is_nil().then_some(())
478    }
479}
480
481impl<'a> Rt<Slot<Object<'a>>> {
482    pub(crate) fn try_as_option<T, E>(&self) -> Result<Option<&Rt<Slot<Gc<T>>>>, E>
483    where
484        Object<'a>: TryInto<Gc<T>, Error = E>,
485    {
486        if self.inner().is_nil() {
487            Ok(None)
488        } else {
489            let _: Gc<T> = (*self.inner().get()).try_into()?;
490            unsafe { Ok(Some(&*((self as *const Self).cast::<Rt<Slot<Gc<T>>>>()))) }
491        }
492    }
493}
494
495impl IntoObject for &Rt<Slot<Object<'_>>> {
496    type Out<'ob> = ObjectType<'ob>;
497
498    fn into_obj<const C: bool>(self, _block: &Block<C>) -> Gc<Self::Out<'_>> {
499        unsafe { self.inner().get().with_lifetime() }
500    }
501}
502
503impl IntoObject for Slot<Object<'_>> {
504    type Out<'ob> = ObjectType<'ob>;
505
506    fn into_obj<const C: bool>(self, _block: &Block<C>) -> Gc<Self::Out<'_>> {
507        unsafe { self.get().with_lifetime() }
508    }
509}
510
511impl IntoObject for &mut Rt<Slot<Object<'_>>> {
512    type Out<'ob> = ObjectType<'ob>;
513
514    fn into_obj<const C: bool>(self, _block: &Block<C>) -> Gc<Self::Out<'_>> {
515        unsafe { self.inner().get().with_lifetime() }
516    }
517}
518
519impl Rt<Slot<&Cons>> {
520    pub(crate) fn car<'ob>(&self, cx: &'ob Context) -> Object<'ob> {
521        self.bind(cx).car()
522    }
523
524    pub(crate) fn cdr<'ob>(&self, cx: &'ob Context) -> Object<'ob> {
525        self.bind(cx).cdr()
526    }
527}
528
529impl<T, U> Deref for Rt<(T, U)> {
530    type Target = (Rt<T>, Rt<U>);
531
532    fn deref(&self) -> &Self::Target {
533        unsafe { &*(self as *const Self).cast::<(Rt<T>, Rt<U>)>() }
534    }
535}
536
537impl<T, U> DerefMut for Rt<(T, U)> {
538    fn deref_mut(&mut self) -> &mut Self::Target {
539        unsafe { &mut *(self as *mut Rt<(T, U)>).cast::<(Rt<T>, Rt<U>)>() }
540    }
541}
542
543// Can't implement [`DerefMut`] because it would allow you to call
544// [`Option::take`] which would return an owned Rt and break the chain of
545// traceability
546impl<T> Deref for Rt<Option<T>> {
547    type Target = Option<Rt<T>>;
548    fn deref(&self) -> &Self::Target {
549        unsafe { &*self.inner_ptr().cast::<Self::Target>() }
550    }
551}
552
553impl<T, I, const N: usize> Index<I> for Rt<[T; N]>
554where
555    [Rt<T>]: Index<I>,
556{
557    type Output = <[Rt<T>] as Index<I>>::Output;
558
559    fn index(&self, index: I) -> &Self::Output {
560        let slice = unsafe { &*self.inner_ptr().cast::<[Rt<T>; N]>() };
561        Index::index(slice, index)
562    }
563}
564
565impl<T, I, const N: usize> IndexMut<I> for Rt<[T; N]>
566where
567    [Rt<T>]: IndexMut<I>,
568{
569    fn index_mut(&mut self, index: I) -> &mut Self::Output {
570        let slice = unsafe { &mut *self.inner_mut_ptr().cast::<[Rt<T>; N]>() };
571        IndexMut::index_mut(slice, index)
572    }
573}
574
575impl<T, const N: usize> AsRef<[Rt<T>]> for Rt<[T; N]> {
576    fn as_ref(&self) -> &[Rt<T>] {
577        unsafe { &*self.inner_ptr().cast::<[Rt<T>; N]>() }
578    }
579}
580
581impl<T> Rt<Vec<T>> {
582    // This is not safe to expose pub(crate) because you could call pop and get
583    // an owned Rt
584    fn as_mut_ref(&mut self) -> &mut Vec<Rt<T>> {
585        // SAFETY: `Rt<T>` has the same memory layout as `T`.
586        unsafe { &mut *(self as *mut Self).cast::<Vec<Rt<T>>>() }
587    }
588
589    pub(crate) fn push<U: IntoRoot<T>>(&mut self, item: U) {
590        self.inner_mut().push(unsafe { item.into_root() });
591    }
592
593    pub(crate) fn truncate(&mut self, len: usize) {
594        self.inner_mut().truncate(len);
595    }
596
597    pub(crate) fn pop(&mut self) {
598        self.inner_mut().pop();
599    }
600
601    pub(crate) fn swap_remove(&mut self, index: usize) {
602        self.inner_mut().swap_remove(index);
603    }
604
605    pub(crate) fn reserve(&mut self, additional: usize) {
606        self.inner_mut().reserve(additional);
607    }
608
609    pub(crate) fn capacity(&self) -> usize {
610        self.inner().capacity()
611    }
612}
613
614impl<T> Rt<Vec<T>> {
615    pub(crate) fn extend_from_slice<U: IntoRoot<T> + Copy>(&mut self, src: &[U]) {
616        // TODO: Slot fix extend_from_slice
617        let inner = self.inner_mut();
618        for x in src {
619            inner.push(unsafe { x.into_root() })
620        }
621    }
622}
623
624impl<T: Clone> Rt<Vec<T>> {
625    pub(crate) fn extend_from_within(&mut self, src: impl RangeBounds<usize>) {
626        self.inner_mut().extend_from_within(src);
627    }
628}
629
630impl<T> Deref for Rt<Vec<T>> {
631    type Target = [Rt<T>];
632    fn deref(&self) -> &Self::Target {
633        // SAFETY: `Rt<T>` has the same memory layout as `T`.
634        unsafe { &*(self as *const Self).cast::<Vec<Rt<T>>>() }
635    }
636}
637
638impl<T> DerefMut for Rt<Vec<T>> {
639    fn deref_mut(&mut self) -> &mut Self::Target {
640        // SAFETY: `Rt<T>` has the same memory layout as `T`.
641        unsafe { &mut *(self as *mut Self).cast::<Vec<Rt<T>>>() }
642    }
643}
644
645impl<T, I: SliceIndex<[Rt<T>]>> Index<I> for Rt<Vec<T>> {
646    type Output = I::Output;
647
648    fn index(&self, index: I) -> &Self::Output {
649        let slice: &[Rt<T>] = self;
650        Index::index(slice, index)
651    }
652}
653
654impl<T, I: SliceIndex<[Rt<T>]>> IndexMut<I> for Rt<Vec<T>> {
655    fn index_mut(&mut self, index: I) -> &mut Self::Output {
656        IndexMut::index_mut(self.as_mut_ref(), index)
657    }
658}
659
660#[derive(Debug)]
661#[repr(transparent)]
662/// A HashMap that can hold values past garbage collection.
663///
664/// This type is needed because Garbage Collection can move keys, which changes
665/// their hash value. `ObjectMap` will rehash the keys after collection.
666// It is not safe to Deref into the inner IndexMap type because we will be
667// constructing a mutable reference during garbage collection. So we have to
668// ensure that there cannot exist a &IndexMap to the same location.
669pub(crate) struct ObjectMap<K, V>(UnsafeCell<IndexMap<K, V>>);
670
671impl<K, V> Default for ObjectMap<K, V> {
672    fn default() -> Self {
673        Self(UnsafeCell::new(Default::default()))
674    }
675}
676
677impl<K, V> Rt<ObjectMap<K, V>>
678where
679    K: Eq + Hash,
680{
681    // inner function that should not be exposed
682    fn as_ref(&self) -> &IndexMap<K, V> {
683        unsafe { &*self.inner().0.get() }
684    }
685
686    // inner function that should not be exposed
687    fn as_mut(&mut self) -> &mut IndexMap<K, V> {
688        unsafe { &mut *self.inner_mut().0.get() }
689    }
690
691    pub(crate) fn insert<Kx: IntoRoot<K>, Vx: IntoRoot<V>>(&mut self, k: Kx, v: Vx) {
692        unsafe {
693            self.as_mut().insert(k.into_root(), v.into_root());
694        }
695    }
696
697    pub(crate) fn get<Q: IntoRoot<K>>(&self, k: Q) -> Option<&Rt<V>> {
698        use std::ptr::from_ref;
699        let inner = unsafe { &*from_ref(self.as_ref()).cast::<IndexMap<K, Rt<V>>>() };
700        let root = unsafe { k.into_root() };
701        inner.get(&root)
702    }
703
704    pub(crate) fn get_mut<Q: IntoRoot<K>>(&mut self, k: Q) -> Option<&mut Rt<V>> {
705        use std::ptr::from_mut;
706        let inner = unsafe { &mut *from_mut(self.as_mut()).cast::<IndexMap<K, Rt<V>>>() };
707        let root = unsafe { k.into_root() };
708        inner.get_mut(&root)
709    }
710
711    pub(crate) fn remove<Q: IntoRoot<K>>(&mut self, k: Q) {
712        let root = unsafe { k.into_root() };
713        self.as_mut().swap_remove(&root);
714    }
715}
716
717impl<K, V> Trace for ObjectMap<K, V>
718where
719    K: Trace + Hash + Eq,
720    V: Trace,
721{
722    fn trace(&self, state: &mut GcState) {
723        let map = unsafe { &mut *self.0.get() };
724        map.rehash_keys(|key, val| {
725            key.trace(state);
726            val.trace(state);
727        });
728    }
729}
730
731#[cfg(test)]
732mod test {
733    use crate::core::object::NIL;
734    use rune_core::macros::root;
735
736    use super::*;
737
738    #[test]
739    fn mem_swap() {
740        let root = &RootSet::default();
741        let cx = &mut Context::new(root);
742        let outer = cx.add("outer");
743        root!(outer, cx);
744        {
745            let inner = cx.add("inner");
746            root!(inner, cx);
747            std::mem::swap(outer, inner);
748        }
749        cx.garbage_collect(true);
750        assert_eq!(outer.bind(cx), "inner");
751    }
752
753    #[test]
754    fn indexing() {
755        let root = &RootSet::default();
756        let cx = &Context::new(root);
757        let mut vec = Rt { inner: vec![], _aliasable: PhantomPinned };
758
759        vec.push(NIL);
760        assert_eq!(vec[0], NIL);
761        let str1 = cx.add("str1");
762        let str2 = cx.add("str2");
763        vec.push(str1);
764        vec.push(str2);
765        assert_eq!(vec.bind_ref(cx)[0..3], vec![NIL, str1, str2]);
766    }
767
768    #[test]
769    fn test_object_map() {
770        type Map<'a> = ObjectMap<Slot<Object<'a>>, Slot<Object<'a>>>;
771        let root = &RootSet::default();
772        let cx = &mut Context::new(root);
773        root!(map, new(Map), cx);
774        let key = cx.add("key");
775
776        map.insert(key, cx.add("val"));
777        root!(key, cx);
778        cx.garbage_collect(true);
779        let val = map.get(key.bind(cx)).unwrap().bind(cx);
780        assert_eq!(val, "val");
781    }
782}