Skip to main content

script_bindings/
root.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::cell::UnsafeCell;
6use std::hash::{Hash, Hasher};
7use std::ops::Deref;
8use std::{fmt, mem, ptr};
9
10use js::gc::{Handle, Traceable as JSTraceable};
11use js::jsapi::{Heap, JSObject, JSTracer};
12use js::rust::GCMethods;
13use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
14
15use crate::assert::assert_in_script;
16use crate::conversions::DerivedFrom;
17use crate::inheritance::Castable;
18use crate::reflector::{DomObject, MutDomObject, Reflector};
19use crate::trace::trace_reflector;
20
21/// A rooted value.
22#[cfg_attr(crown, crown::unrooted_must_root_lint::allow_unrooted_interior)]
23pub struct Root<T: StableTraceObject> {
24    /// The value to root.
25    value: T,
26    /// List that ensures correct dynamic root ordering
27    root_list: *const RootCollection,
28}
29
30impl<T> Root<T>
31where
32    T: StableTraceObject + 'static,
33{
34    /// Create a new stack-bounded root for the provided value.
35    /// It gives out references which cannot outlive this new `Root`.
36    ///
37    /// # Safety
38    /// It must not outlive its associated `RootCollection`.
39    pub unsafe fn new(value: T) -> Self {
40        unsafe fn add_to_root_list(object: *const dyn JSTraceable) -> *const RootCollection {
41            assert_in_script();
42            STACK_ROOTS.with(|root_list| {
43                unsafe { root_list.root(object) };
44                root_list as *const _
45            })
46        }
47
48        let root_list = unsafe { add_to_root_list(value.stable_trace_object()) };
49        Root { value, root_list }
50    }
51}
52
53/// `StableTraceObject` represents values that can be rooted through a stable address that will
54/// not change for their whole lifetime.
55/// It is an unsafe trait that requires implementors to ensure certain safety guarantees.
56///
57/// # Safety
58///
59/// Implementors of this trait must ensure that the `trace` method correctly accounts for all
60/// owned and referenced objects, so that the garbage collector can accurately determine which
61/// objects are still in use. Failing to adhere to this contract may result in undefined behavior,
62/// such as use-after-free errors.
63pub unsafe trait StableTraceObject {
64    /// Returns a stable trace object which address won't change for the whole
65    /// lifetime of the value.
66    fn stable_trace_object(&self) -> *const dyn JSTraceable;
67}
68
69unsafe impl<T> StableTraceObject for Dom<T>
70where
71    T: DomObject,
72{
73    fn stable_trace_object(&self) -> *const dyn JSTraceable {
74        // The JSTraceable impl for Reflector doesn't actually do anything,
75        // so we need this shenanigan to actually trace the reflector of the
76        // T pointer in Dom<T>.
77        #[cfg_attr(crown, expect(crown::unrooted_must_root))]
78        struct ReflectorStackRoot<T>(Reflector<T>);
79        unsafe impl<T> JSTraceable for ReflectorStackRoot<T> {
80            unsafe fn trace(&self, tracer: *mut JSTracer) {
81                unsafe { trace_reflector(tracer, "on stack", &self.0) };
82            }
83        }
84        unsafe {
85            &*(self.reflector() as *const Reflector<T::ReflectorType>
86                as *const ReflectorStackRoot<T::ReflectorType>)
87        }
88    }
89}
90
91unsafe impl<T> StableTraceObject for MaybeUnreflectedDom<T>
92where
93    T: DomObject,
94{
95    fn stable_trace_object(&self) -> *const dyn JSTraceable {
96        // The JSTraceable impl for Reflector doesn't actually do anything,
97        // so we need this shenanigan to actually trace the reflector of the
98        // T pointer in Dom<T>.
99        struct MaybeUnreflectedStackRoot<T>(T);
100        unsafe impl<T> JSTraceable for MaybeUnreflectedStackRoot<T>
101        where
102            T: DomObject,
103        {
104            unsafe fn trace(&self, tracer: *mut JSTracer) {
105                if self.0.reflector().get_jsobject().is_null() {
106                    unsafe { self.0.trace(tracer) };
107                } else {
108                    unsafe { trace_reflector(tracer, "on stack", self.0.reflector()) };
109                }
110            }
111        }
112        unsafe { &*(self.ptr.as_ptr() as *const T as *const MaybeUnreflectedStackRoot<T>) }
113    }
114}
115
116impl<T> Deref for Root<T>
117where
118    T: Deref + StableTraceObject,
119{
120    type Target = <T as Deref>::Target;
121
122    fn deref(&self) -> &Self::Target {
123        assert_in_script();
124        &self.value
125    }
126}
127
128impl<T> Drop for Root<T>
129where
130    T: StableTraceObject,
131{
132    fn drop(&mut self) {
133        unsafe {
134            (*self.root_list).unroot(self.value.stable_trace_object());
135        }
136    }
137}
138
139impl<T: fmt::Debug + StableTraceObject> fmt::Debug for Root<T> {
140    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
141        self.value.fmt(f)
142    }
143}
144
145impl<T: fmt::Debug + DomObject> fmt::Debug for Dom<T> {
146    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
147        (**self).fmt(f)
148    }
149}
150
151/// A traced reference to a DOM object
152///
153/// This type is critical to making garbage collection work with the DOM,
154/// but it is very dangerous; if garbage collection happens with a `Dom<T>`
155/// on the stack, the `Dom<T>` can point to freed memory.
156///
157/// This should only be used as a field in other DOM objects.
158#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
159#[repr(transparent)]
160pub struct Dom<T> {
161    ptr: ptr::NonNull<T>,
162}
163
164// Dom<T> is similar to Rc<T>, in that it's not always clear how to avoid double-counting.
165// For now, we choose not to follow any such pointers.
166impl<T> MallocSizeOf for Dom<T> {
167    fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
168        0
169    }
170}
171
172impl<T> PartialEq for Dom<T> {
173    fn eq(&self, other: &Dom<T>) -> bool {
174        self.ptr.as_ptr() == other.ptr.as_ptr()
175    }
176}
177
178impl<'a, T: DomObject> PartialEq<&'a T> for Dom<T> {
179    fn eq(&self, other: &&'a T) -> bool {
180        *self == Dom::from_ref(*other)
181    }
182}
183
184impl<T> Eq for Dom<T> {}
185
186impl<T> Hash for Dom<T> {
187    fn hash<H: Hasher>(&self, state: &mut H) {
188        self.ptr.as_ptr().hash(state)
189    }
190}
191
192impl<T> Clone for Dom<T> {
193    #[inline]
194    fn clone(&self) -> Self {
195        assert_in_script();
196        Dom { ptr: self.ptr }
197    }
198}
199
200impl<T: DomObject> Dom<T> {
201    /// Create a `Dom<T>` from a `&T`
202    pub fn from_ref(obj: &T) -> Dom<T> {
203        assert_in_script();
204        Dom {
205            ptr: ptr::NonNull::from(obj),
206        }
207    }
208
209    /// Return a rooted version of this DOM object ([`DomRoot<T>`]) suitable for use on the stack.
210    pub fn as_rooted(&self) -> DomRoot<T> {
211        DomRoot::from_ref(self)
212    }
213
214    pub fn as_ptr(&self) -> *const T {
215        self.ptr.as_ptr()
216    }
217}
218
219impl<T: DomObject> Deref for Dom<T> {
220    type Target = T;
221
222    fn deref(&self) -> &T {
223        assert_in_script();
224        // We can only have &Dom<T> from a rooted thing, so it's safe to deref
225        // it to &T.
226        unsafe { &*self.ptr.as_ptr() }
227    }
228}
229
230unsafe impl<T: DomObject> JSTraceable for Dom<T> {
231    unsafe fn trace(&self, tracer: *mut JSTracer) {
232        let trace_info = if cfg!(debug_assertions) {
233            std::any::type_name::<T>()
234        } else {
235            "DOM object on heap"
236        };
237        unsafe {
238            trace_reflector(tracer, trace_info, (*self.ptr.as_ptr()).reflector());
239        }
240    }
241}
242
243/// A traced reference to a DOM object that may not be reflected yet.
244#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
245pub struct MaybeUnreflectedDom<T> {
246    ptr: ptr::NonNull<T>,
247}
248
249impl<T> MaybeUnreflectedDom<T>
250where
251    T: DomObject,
252{
253    /// Create a new MaybeUnreflectedDom value from the given boxed DOM object.
254    ///
255    /// # Safety
256    /// TODO: unclear why this is marked unsafe.
257    pub unsafe fn from_box(value: Box<T>) -> Self {
258        Self {
259            ptr: Box::leak(value).into(),
260        }
261    }
262}
263
264impl<T> Root<MaybeUnreflectedDom<T>>
265where
266    T: DomObject,
267{
268    pub fn as_ptr(&self) -> *const T {
269        self.value.ptr.as_ptr()
270    }
271}
272
273impl<T> Root<MaybeUnreflectedDom<T>>
274where
275    T: MutDomObject,
276{
277    /// Treat the given JS object as the reflector of this unreflected object.
278    ///
279    /// # Safety
280    /// obj must point to a valid, non-null JS object.
281    pub unsafe fn reflect_with(self, obj: *mut JSObject) -> DomRoot<T> {
282        let ptr = self.as_ptr();
283        drop(self);
284        let root = DomRoot::from_ref(unsafe { &*ptr });
285        unsafe { root.init_reflector::<T>(obj) };
286        root
287    }
288}
289
290/// A rooted reference to a DOM object.
291pub type DomRoot<T> = Root<Dom<T>>;
292
293impl<T: Castable> DomRoot<T> {
294    /// Cast a DOM object root upwards to one of the interfaces it derives from.
295    pub fn upcast<U>(root: DomRoot<T>) -> DomRoot<U>
296    where
297        U: Castable,
298        T: DerivedFrom<U>,
299    {
300        unsafe { mem::transmute::<DomRoot<T>, DomRoot<U>>(root) }
301    }
302
303    /// Cast a DOM object root downwards to one of the interfaces it might implement.
304    pub fn downcast<U>(root: DomRoot<T>) -> Option<DomRoot<U>>
305    where
306        U: DerivedFrom<T>,
307    {
308        if root.is::<U>() {
309            Some(unsafe { mem::transmute::<DomRoot<T>, DomRoot<U>>(root) })
310        } else {
311            None
312        }
313    }
314}
315
316impl<T: DomObject> DomRoot<T> {
317    /// Generate a new root from a reference
318    pub fn from_ref(unrooted: &T) -> DomRoot<T> {
319        unsafe { DomRoot::new(Dom::from_ref(unrooted)) }
320    }
321
322    /// Create a traced version of this rooted object.
323    ///
324    /// # Safety
325    ///
326    /// This should never be used to create on-stack values. Instead these values should always
327    /// end up as members of other DOM objects.
328    pub fn as_traced(&self) -> Dom<T> {
329        Dom::from_ref(self)
330    }
331}
332
333impl<T> MallocSizeOf for DomRoot<T>
334where
335    T: DomObject + MallocSizeOf,
336{
337    fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
338        0
339    }
340}
341
342impl<T> PartialEq for DomRoot<T>
343where
344    T: DomObject,
345{
346    fn eq(&self, other: &Self) -> bool {
347        self.value == other.value
348    }
349}
350
351impl<T: DomObject> Eq for DomRoot<T> {}
352
353impl<T: DomObject> Hash for DomRoot<T> {
354    fn hash<H: Hasher>(&self, state: &mut H) {
355        self.value.hash(state);
356    }
357}
358
359impl<T> Clone for DomRoot<T>
360where
361    T: DomObject,
362{
363    fn clone(&self) -> DomRoot<T> {
364        DomRoot::from_ref(self)
365    }
366}
367
368unsafe impl<T> JSTraceable for DomRoot<T>
369where
370    T: DomObject,
371{
372    unsafe fn trace(&self, _: *mut JSTracer) {
373        // Already traced.
374    }
375}
376
377/// A rooting mechanism for reflectors on the stack.
378/// LIFO is not required.
379///
380/// See also [*Exact Stack Rooting - Storing a GCPointer on the CStack*][cstack].
381///
382/// [cstack]: https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/Internals/GC/Exact_Stack_Rooting
383pub struct RootCollection {
384    roots: UnsafeCell<Vec<*const dyn JSTraceable>>,
385}
386
387impl RootCollection {
388    /// Create an empty collection of roots
389    #[expect(clippy::new_without_default)]
390    pub const fn new() -> RootCollection {
391        RootCollection {
392            roots: UnsafeCell::new(vec![]),
393        }
394    }
395
396    /// Starts tracking a trace object.
397    unsafe fn root(&self, object: *const dyn JSTraceable) {
398        assert_in_script();
399        unsafe { (*self.roots.get()).push(object) };
400    }
401
402    /// Stops tracking a trace object, asserting if it isn't found.
403    unsafe fn unroot(&self, object: *const dyn JSTraceable) {
404        assert_in_script();
405        let roots = unsafe { &mut *self.roots.get() };
406        match roots
407            .iter()
408            .rposition(|r| std::ptr::addr_eq(*r as *const (), object as *const ()))
409        {
410            Some(idx) => {
411                // Partial inlining of `Vec::swap_remove` to avoid having to read and return the value since
412                // we don't care about it, doing less work in our case.
413                // SAFETY: the copy source and destination are derived from valid positions in the vector.
414                unsafe {
415                    let len = roots.len() - 1;
416                    if len != idx {
417                        let base_ptr = roots.as_mut_ptr();
418                        ptr::copy_nonoverlapping(base_ptr.add(len), base_ptr.add(idx), 1);
419                    }
420                    roots.set_len(len);
421                }
422            },
423            None => panic!("Can't remove a root that was never rooted!"),
424        }
425    }
426}
427
428thread_local!(pub static STACK_ROOTS: RootCollection = const { RootCollection::new() });
429
430/// SM Callback that traces the rooted reflectors
431///
432/// # Safety
433/// tracer must point to a valid, non-null JS tracer object.
434pub unsafe fn trace_roots(tracer: *mut JSTracer) {
435    trace!("tracing stack roots");
436    STACK_ROOTS.with(|collection| {
437        let collection = unsafe { &*collection.roots.get() };
438        for root in collection {
439            unsafe {
440                (**root).trace(tracer);
441            }
442        }
443    });
444}
445
446/// Get a slice of references to DOM objects.
447pub trait DomSlice<T>
448where
449    T: JSTraceable + DomObject,
450{
451    /// Returns the slice of `T` references.
452    fn r(&self) -> &[&T];
453}
454
455impl<T> DomSlice<T> for [Dom<T>]
456where
457    T: JSTraceable + DomObject,
458{
459    #[inline]
460    fn r(&self) -> &[&T] {
461        let _ = mem::transmute::<Dom<T>, &T>;
462        unsafe { &*(self as *const [Dom<T>] as *const [&T]) }
463    }
464}
465
466/// Returns a handle to a Heap member of a reflected DOM object.
467/// The provided callback acts as a projection of the rooted-ness of
468/// the provided DOM object; it must return a reference to a Heap
469/// member of the DOM object.
470pub fn rooted_heap_handle<'a, T: DomObject, U: GCMethods + Copy>(
471    object: &'a T,
472    f: impl Fn(&'a T) -> &'a Heap<U>,
473) -> Handle<'a, U> {
474    // SAFETY: Heap::handle is safe to call when the Heap is a member
475    //   of a rooted object. Our safety invariants for DOM objects
476    //   ensure that a &T is obtained via a root of T.
477    unsafe { Handle::from_raw(f(object).handle()) }
478}