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