jlrs/memory/target/frame/
mod.rs

1//! Dynamically and statically-sized frames.
2//!
3//! Every scope has its own frame which can hold some number of roots. When the scope ends these
4//! roots are removed from the set of roots, so all data rooted in a frame can safely be used
5//! until its scope ends. This hold true even if the frame is dropped before its scope ends.
6//!
7//! For more information see the documentation in the [`memory`] and [`target`] modules.
8//!
9//! [`memory`]: crate::memory
10//! [`target`]: crate::memory::target
11
12#[cfg(feature = "async")]
13pub mod async_frame;
14
15use std::{marker::PhantomData, pin::Pin, ptr::NonNull};
16
17use jlrs_sys::{RawGcFrame, UnsizedGcFrame, pop_frame};
18
19#[cfg(feature = "async")]
20pub use self::async_frame::*;
21use super::{
22    ExtendedTarget, Target,
23    output::Output,
24    reusable_slot::ReusableSlot,
25    slot_ref::{LocalSlotRef, StackSlotRef},
26    unrooted::Unrooted,
27};
28use crate::{
29    data::managed::Managed,
30    memory::{context::stack::Stack, scope::private::LocalScopePriv},
31    prelude::{LocalScope, Scope},
32    private::Private,
33};
34
35/// A dynamically-sized frame that can hold an arbitrary number of roots.
36pub struct GcFrame<'scope> {
37    stack: &'scope Stack,
38    offset: usize,
39    _marker: PhantomData<&'scope mut &'scope ()>,
40}
41
42impl<'scope> GcFrame<'scope> {
43    /// Returns a mutable reference to this frame.
44    #[inline]
45    pub fn as_mut(&mut self) -> &mut Self {
46        self
47    }
48
49    /// Reserve capacity for at least `additional` roots.
50    #[inline]
51    pub fn reserve(&mut self, additional: usize) {
52        self.stack.reserve(additional)
53    }
54
55    /// Borrow the current frame.
56    #[inline]
57    pub fn borrow<'borrow>(&'borrow mut self) -> BorrowedFrame<'borrow, 'scope, Self> {
58        BorrowedFrame(self, PhantomData)
59    }
60
61    /// Borrow this frame as an `ExtendedTarget` with the provided `target`.
62    #[inline]
63    pub fn extended_target<'target, 'borrow, Tgt>(
64        &'borrow mut self,
65        target: Tgt,
66    ) -> ExtendedTarget<'target, 'scope, 'borrow, Tgt>
67    where
68        Tgt: Target<'target>,
69    {
70        ExtendedTarget {
71            target,
72            frame: self,
73            _target_marker: PhantomData,
74        }
75    }
76
77    /// Borrow this frame as an `ExtendedTarget` with an `Output` that targets this frame.
78    #[inline]
79    pub fn as_extended_target<'borrow>(
80        &'borrow mut self,
81    ) -> ExtendedTarget<'scope, 'scope, 'borrow, Output<'scope, StackSlotRef<'scope>>> {
82        let target = self.output();
83        ExtendedTarget {
84            target,
85            frame: self,
86            _target_marker: PhantomData,
87        }
88    }
89
90    /// Returns the number of values rooted in this frame.
91    #[inline]
92    pub fn n_roots(&self) -> usize {
93        self.stack_size() - self.offset
94    }
95
96    /// Returns the number of values rooted in this frame.
97    #[inline]
98    pub fn stack_size(&self) -> usize {
99        self.stack.size()
100    }
101
102    /// Returns an `Output` that targets the current frame.
103    #[inline]
104    pub fn output(&mut self) -> Output<'scope, StackSlotRef<'scope>> {
105        unsafe {
106            let offset = self.stack.reserve_slot();
107            Output::new(StackSlotRef::new(self.stack, offset))
108        }
109    }
110
111    /// Returns a `ReusableSlot` that targets the current frame.
112    #[inline]
113    pub fn reusable_slot(&mut self) -> ReusableSlot<'scope, StackSlotRef<'scope>> {
114        unsafe {
115            let offset = self.stack.reserve_slot();
116            let slot = StackSlotRef::new(self.stack, offset);
117            ReusableSlot::new(slot)
118        }
119    }
120
121    /// Returns an `Unrooted` that targets the current frame.
122    #[inline]
123    pub const fn unrooted(&self) -> Unrooted<'scope> {
124        unsafe { Unrooted::new() }
125    }
126
127    // Safety: ptr must be a valid pointer to T
128    #[inline]
129    #[track_caller]
130    pub(crate) unsafe fn root<'data, T: Managed<'scope, 'data>>(
131        &self,
132        ptr: NonNull<T::Wraps>,
133    ) -> T {
134        unsafe {
135            self.stack.push_root(ptr.cast());
136            T::wrap_non_null(ptr, Private)
137        }
138    }
139
140    #[inline]
141    pub(crate) fn stack(&self) -> &Stack {
142        self.stack
143    }
144
145    #[inline]
146    pub(crate) unsafe fn nest<'nested>(&'nested mut self) -> (usize, GcFrame<'nested>) {
147        let frame = GcFrame {
148            stack: self.stack(),
149            offset: self.stack.size(),
150            _marker: PhantomData,
151        };
152        (self.stack.size(), frame)
153    }
154
155    // Safety: only one base frame can exist per `Stack`
156    #[inline]
157    pub(crate) unsafe fn base(stack: &'scope Stack) -> GcFrame<'scope> {
158        debug_assert_eq!(stack.size(), 0);
159        GcFrame {
160            stack,
161            offset: 0,
162            _marker: PhantomData,
163        }
164    }
165
166    // pub fn stack_addr(&self) -> *const c_void {
167    //     self.stack as *const _ as *const _
168    // }
169}
170
171unsafe impl<'f> Scope for GcFrame<'f> {
172    #[inline]
173    fn scope<T>(&mut self, func: impl for<'scope> FnOnce(GcFrame<'scope>) -> T) -> T {
174        unsafe {
175            let (offset, nested) = self.nest();
176            let res = func(nested);
177            self.stack.pop_roots(offset);
178            res
179        }
180    }
181}
182
183/// A statically-sized frame that can hold `N` roots.
184pub struct LocalGcFrame<'scope, const N: usize> {
185    frame: &'scope PinnedLocalFrame<'scope, N>,
186    offset: usize,
187}
188
189impl<'scope, const N: usize> LocalGcFrame<'scope, N> {
190    /// Returns a mutable reference to this frame.
191    #[inline]
192    pub fn as_mut(&mut self) -> &mut Self {
193        self
194    }
195
196    /// Returns the number of values rooted in this frame.
197    #[inline]
198    pub fn n_roots(&self) -> usize {
199        self.offset
200    }
201
202    /// Returns the number of values that can be rooted in this frame.
203    #[inline]
204    pub const fn frame_size(&self) -> usize {
205        N
206    }
207
208    /// Returns an `Output` that targets the current frame.
209    #[inline]
210    pub fn output(&mut self) -> Output<'scope, LocalSlotRef<'scope>> {
211        unsafe {
212            let slot = self.frame.frame.raw.get_root(self.offset);
213            self.offset += 1;
214            Output::new(LocalSlotRef::new(slot))
215        }
216    }
217
218    /// Returns a `ReusableSlot` that targets the current frame.
219    #[inline]
220    pub fn reusable_slot(&mut self) -> ReusableSlot<'scope, LocalSlotRef<'scope>> {
221        unsafe {
222            let slot = self.frame.frame.raw.get_root(self.offset);
223            let slot = LocalSlotRef::new(slot);
224            self.offset += 1;
225            ReusableSlot::new(slot)
226        }
227    }
228
229    /// Returns a `Unrooted` that targets the current frame.
230    #[inline]
231    pub const fn unrooted(&self) -> Unrooted<'scope> {
232        unsafe { Unrooted::new() }
233    }
234
235    #[inline]
236    pub(crate) unsafe fn new(frame: &'scope PinnedLocalFrame<'scope, N>) -> Self {
237        LocalGcFrame { frame, offset: 0 }
238    }
239
240    #[inline]
241    #[track_caller]
242    pub(crate) unsafe fn root<'data, T: Managed<'scope, 'data>>(
243        &mut self,
244        ptr: NonNull<T::Wraps>,
245    ) -> T {
246        unsafe {
247            self.frame
248                .frame
249                .raw
250                .set_root(self.offset, ptr.as_ptr().cast());
251            self.offset += 1;
252            T::wrap_non_null(ptr, Private)
253        }
254    }
255}
256
257pub struct UnsizedLocalGcFrame<'scope> {
258    frame: UnsizedGcFrame<'scope>,
259    offset: usize,
260}
261
262impl<'scope> UnsizedLocalGcFrame<'scope> {
263    pub(crate) fn new(frame: UnsizedGcFrame<'scope>) -> Self {
264        UnsizedLocalGcFrame { frame, offset: 0 }
265    }
266
267    /// Returns a mutable reference to this frame.
268    #[inline]
269    pub fn as_mut(&mut self) -> &mut Self {
270        self
271    }
272
273    /// Returns the number of values rooted in this frame.
274    #[inline]
275    pub fn n_roots(&self) -> usize {
276        self.offset
277    }
278
279    /// Returns the number of values that can be rooted in this frame.
280    #[inline]
281    pub fn frame_size(&self) -> usize {
282        self.frame.size()
283    }
284
285    /// Returns an `Output` that targets the current frame.
286    #[inline]
287    pub fn output(&mut self) -> Output<'scope, LocalSlotRef<'scope>> {
288        unsafe {
289            let slot = self.frame.get_root(self.offset);
290            self.offset += 1;
291            Output::new(LocalSlotRef::new(slot))
292        }
293    }
294
295    /// Returns a `ReusableSlot` that targets the current frame.
296    #[inline]
297    pub fn reusable_slot(&mut self) -> ReusableSlot<'scope, LocalSlotRef<'scope>> {
298        unsafe {
299            let slot = self.frame.get_root(self.offset);
300            let slot = LocalSlotRef::new(slot);
301            self.offset += 1;
302            ReusableSlot::new(slot)
303        }
304    }
305
306    /// Returns a `Unrooted` that targets the current frame.
307    #[inline]
308    pub const fn unrooted(&self) -> Unrooted<'scope> {
309        unsafe { Unrooted::new() }
310    }
311
312    #[inline]
313    #[track_caller]
314    pub(crate) unsafe fn root<'data, T: Managed<'scope, 'data>>(
315        &mut self,
316        ptr: NonNull<T::Wraps>,
317    ) -> T {
318        unsafe {
319            self.frame.get_root(self.offset).set(ptr.as_ptr().cast());
320            self.offset += 1;
321            T::wrap_non_null(ptr, Private)
322        }
323    }
324}
325
326/// A frame that has been borrowed. A new scope must be created before it can be used as a target
327/// again.
328// TODO privacy
329pub struct BorrowedFrame<'borrow, 'current, F>(
330    pub(crate) &'borrow mut F,
331    pub(crate) PhantomData<&'current ()>,
332);
333
334impl<'borrow, 'current> LocalScopePriv for BorrowedFrame<'borrow, 'current, GcFrame<'current>> {}
335
336unsafe impl<'borrow, 'current> LocalScope for BorrowedFrame<'borrow, 'current, GcFrame<'current>> {}
337
338unsafe impl<'borrow, 'current> Scope for BorrowedFrame<'borrow, 'current, GcFrame<'current>> {
339    #[inline]
340    fn scope<T>(&mut self, func: impl for<'scope> FnOnce(GcFrame<'scope>) -> T) -> T {
341        self.0.scope(func)
342    }
343}
344
345#[cfg(feature = "async")]
346impl<'borrow, 'current> LocalScopePriv
347    for BorrowedFrame<'borrow, 'current, AsyncGcFrame<'current>>
348{
349}
350
351#[cfg(feature = "async")]
352unsafe impl<'borrow, 'current> LocalScope
353    for BorrowedFrame<'borrow, 'current, AsyncGcFrame<'current>>
354{
355}
356
357#[cfg(feature = "async")]
358unsafe impl<'borrow, 'current> Scope for BorrowedFrame<'borrow, 'current, AsyncGcFrame<'current>> {
359    #[inline]
360    fn scope<T>(&mut self, func: impl for<'scope> FnOnce(GcFrame<'scope>) -> T) -> T {
361        self.0.scope(func)
362    }
363}
364
365#[cfg(feature = "async")]
366unsafe impl<'borrow, 'current> crate::prelude::AsyncScope
367    for BorrowedFrame<'borrow, 'current, AsyncGcFrame<'current>>
368{
369    #[inline]
370    async fn async_scope<T>(
371        &mut self,
372        func: impl for<'scope> AsyncFnOnce(AsyncGcFrame<'scope>) -> T,
373    ) -> T {
374        self.0.async_scope(func).await
375    }
376}
377
378#[repr(C)]
379pub(crate) struct LocalFrame<const N: usize> {
380    raw: RawGcFrame<N>,
381}
382
383impl<const N: usize> LocalFrame<N> {
384    #[inline]
385    pub(crate) const fn new() -> Self {
386        unsafe {
387            LocalFrame {
388                raw: RawGcFrame::new(),
389            }
390        }
391    }
392
393    #[inline]
394    pub(crate) unsafe fn pin<'scope>(&'scope mut self) -> PinnedLocalFrame<'scope, N> {
395        unsafe { PinnedLocalFrame::new(self) }
396    }
397}
398
399pub(crate) struct PinnedLocalFrame<'scope, const N: usize> {
400    frame: Pin<&'scope mut LocalFrame<N>>,
401    _marker: PhantomData<&'scope mut &'scope ()>,
402}
403
404impl<'scope, const N: usize> PinnedLocalFrame<'scope, N> {
405    #[inline]
406    unsafe fn new(frame: &'scope mut LocalFrame<N>) -> Self {
407        unsafe {
408            if N > 0 {
409                frame.raw.push_frame();
410            }
411
412            PinnedLocalFrame {
413                frame: Pin::new_unchecked(frame),
414                _marker: PhantomData,
415            }
416        }
417    }
418
419    #[inline]
420    pub(crate) unsafe fn pop(&self) {
421        unsafe {
422            if N > 0 {
423                pop_frame()
424            }
425        }
426    }
427}