mozjs/gc/
custom.rs

1use std::ffi::c_void;
2use std::ops::{Deref, DerefMut};
3
4use crate::glue::{CallObjectRootTracer, CallValueRootTracer};
5use crate::jsapi;
6use crate::jsapi::{AutoGCRooter, AutoGCRooterKind, JSContext, JSObject, JSTracer, Value};
7use crate::rust::{Handle, MutableHandle};
8use mozjs_sys::jsgc::{CustomAutoRooterVFTable, RootKind};
9
10/// Similarly to `Traceable` trait, it's used to specify tracing of various types
11/// that are used in conjunction with `CustomAutoRooter`.
12pub unsafe trait CustomTrace {
13    fn trace(&self, trc: *mut JSTracer);
14}
15
16unsafe impl CustomTrace for *mut JSObject {
17    fn trace(&self, trc: *mut JSTracer) {
18        let this = self as *const *mut _ as *mut *mut _;
19        unsafe {
20            CallObjectRootTracer(trc, this, c"object".as_ptr());
21        }
22    }
23}
24
25unsafe impl CustomTrace for Value {
26    fn trace(&self, trc: *mut JSTracer) {
27        let this = self as *const _ as *mut _;
28        unsafe {
29            CallValueRootTracer(trc, this, c"any".as_ptr());
30        }
31    }
32}
33
34unsafe impl<T: CustomTrace> CustomTrace for Option<T> {
35    fn trace(&self, trc: *mut JSTracer) {
36        if let Some(ref some) = *self {
37            some.trace(trc);
38        }
39    }
40}
41
42unsafe impl<T: CustomTrace> CustomTrace for Vec<T> {
43    fn trace(&self, trc: *mut JSTracer) {
44        for elem in self {
45            elem.trace(trc);
46        }
47    }
48}
49
50// This structure reimplements a C++ class that uses virtual dispatch, so
51// use C layout to guarantee that vftable in CustomAutoRooter is in right place.
52#[repr(C)]
53#[cfg_attr(
54    feature = "crown",
55    crown::unrooted_must_root_lint::allow_unrooted_interior
56)]
57pub struct CustomAutoRooter<T> {
58    _base: jsapi::CustomAutoRooter,
59    data: T,
60}
61
62impl<T> CustomAutoRooter<T> {
63    unsafe fn add_to_root_stack(&mut self, cx: *mut JSContext) {
64        self._base._base.add_to_root_stack(cx);
65    }
66
67    unsafe fn remove_from_root_stack(&mut self) {
68        self._base._base.remove_from_root_stack();
69    }
70}
71
72/// `CustomAutoRooter` uses dynamic dispatch on the C++ side for custom tracing,
73/// so provide trace logic via vftable when creating an object on Rust side.
74unsafe trait CustomAutoTraceable: Sized {
75    const vftable: CustomAutoRooterVFTable = CustomAutoRooterVFTable {
76        padding: CustomAutoRooterVFTable::PADDING,
77        trace: Self::trace,
78    };
79
80    unsafe extern "C" fn trace(this: *mut c_void, trc: *mut JSTracer) {
81        let this = this as *const Self;
82        let this = this.as_ref().unwrap();
83        Self::do_trace(this, trc);
84    }
85
86    /// Used by `CustomAutoTraceable` implementer to trace its contents.
87    /// Corresponds to virtual `trace` call in a `CustomAutoRooter` subclass (C++).
88    fn do_trace(&self, trc: *mut JSTracer);
89}
90
91unsafe impl<T: CustomTrace> CustomAutoTraceable for CustomAutoRooter<T> {
92    fn do_trace(&self, trc: *mut JSTracer) {
93        self.data.trace(trc);
94    }
95}
96
97impl<T: CustomTrace> CustomAutoRooter<T> {
98    pub fn new(data: T) -> Self {
99        let vftable = &Self::vftable;
100        CustomAutoRooter {
101            _base: jsapi::CustomAutoRooter {
102                vtable_: vftable as *const _ as *const _,
103                _base: AutoGCRooter::new_unrooted(AutoGCRooterKind::Custom),
104            },
105            data,
106        }
107    }
108
109    pub fn root(&mut self, cx: *mut JSContext) -> CustomAutoRooterGuard<T> {
110        CustomAutoRooterGuard::new(cx, self)
111    }
112}
113
114/// An RAII guard used to root underlying data in `CustomAutoRooter` until the
115/// guard is dropped (falls out of scope).
116/// The underlying data can be accessed through this guard via its Deref and
117/// DerefMut implementations.
118/// This structure is created by `root` method on `CustomAutoRooter` or
119/// by the `auto_root!` macro.
120#[cfg_attr(
121    feature = "crown",
122    crown::unrooted_must_root_lint::allow_unrooted_interior
123)]
124pub struct CustomAutoRooterGuard<'a, T: 'a + CustomTrace> {
125    rooter: &'a mut CustomAutoRooter<T>,
126}
127
128impl<'a, T: 'a + CustomTrace> CustomAutoRooterGuard<'a, T> {
129    pub fn new(cx: *mut JSContext, rooter: &'a mut CustomAutoRooter<T>) -> Self {
130        unsafe {
131            rooter.add_to_root_stack(cx);
132        }
133        CustomAutoRooterGuard { rooter }
134    }
135
136    pub fn handle(&'a self) -> Handle<'a, T>
137    where
138        T: RootKind,
139    {
140        Handle::new(&self.rooter.data)
141    }
142
143    pub fn handle_mut(&mut self) -> MutableHandle<T>
144    where
145        T: RootKind,
146    {
147        unsafe { MutableHandle::from_marked_location(&mut self.rooter.data) }
148    }
149}
150
151impl<'a, T: 'a + CustomTrace> Deref for CustomAutoRooterGuard<'a, T> {
152    type Target = T;
153    fn deref(&self) -> &T {
154        &self.rooter.data
155    }
156}
157
158impl<'a, T: 'a + CustomTrace> DerefMut for CustomAutoRooterGuard<'a, T> {
159    fn deref_mut(&mut self) -> &mut T {
160        &mut self.rooter.data
161    }
162}
163
164impl<'a, T: 'a + CustomTrace> Drop for CustomAutoRooterGuard<'a, T> {
165    fn drop(&mut self) {
166        unsafe {
167            self.rooter.remove_from_root_stack();
168        }
169    }
170}
171
172pub type SequenceRooter<T> = CustomAutoRooter<Vec<T>>;
173pub type SequenceRooterGuard<'a, T> = CustomAutoRooterGuard<'a, Vec<T>>;