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(Reflector);
78        unsafe impl JSTraceable for ReflectorStackRoot {
79            unsafe fn trace(&self, tracer: *mut JSTracer) {
80                unsafe { trace_reflector(tracer, "on stack", &self.0) };
81            }
82        }
83        unsafe { &*(self.reflector() as *const Reflector as *const ReflectorStackRoot) }
84    }
85}
86
87unsafe impl<T> StableTraceObject for MaybeUnreflectedDom<T>
88where
89    T: DomObject,
90{
91    fn stable_trace_object(&self) -> *const dyn JSTraceable {
92        // The JSTraceable impl for Reflector doesn't actually do anything,
93        // so we need this shenanigan to actually trace the reflector of the
94        // T pointer in Dom<T>.
95        struct MaybeUnreflectedStackRoot<T>(T);
96        unsafe impl<T> JSTraceable for MaybeUnreflectedStackRoot<T>
97        where
98            T: DomObject,
99        {
100            unsafe fn trace(&self, tracer: *mut JSTracer) {
101                if self.0.reflector().get_jsobject().is_null() {
102                    unsafe { self.0.trace(tracer) };
103                } else {
104                    unsafe { trace_reflector(tracer, "on stack", self.0.reflector()) };
105                }
106            }
107        }
108        unsafe { &*(self.ptr.as_ptr() as *const T as *const MaybeUnreflectedStackRoot<T>) }
109    }
110}
111
112impl<T> Deref for Root<T>
113where
114    T: Deref + StableTraceObject,
115{
116    type Target = <T as Deref>::Target;
117
118    fn deref(&self) -> &Self::Target {
119        assert_in_script();
120        &self.value
121    }
122}
123
124impl<T> Drop for Root<T>
125where
126    T: StableTraceObject,
127{
128    fn drop(&mut self) {
129        unsafe {
130            (*self.root_list).unroot(self.value.stable_trace_object());
131        }
132    }
133}
134
135impl<T: fmt::Debug + StableTraceObject> fmt::Debug for Root<T> {
136    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
137        self.value.fmt(f)
138    }
139}
140
141impl<T: fmt::Debug + DomObject> fmt::Debug for Dom<T> {
142    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
143        (**self).fmt(f)
144    }
145}
146
147/// A traced reference to a DOM object
148///
149/// This type is critical to making garbage collection work with the DOM,
150/// but it is very dangerous; if garbage collection happens with a `Dom<T>`
151/// on the stack, the `Dom<T>` can point to freed memory.
152///
153/// This should only be used as a field in other DOM objects.
154#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
155#[repr(transparent)]
156pub struct Dom<T> {
157    ptr: ptr::NonNull<T>,
158}
159
160// Dom<T> is similar to Rc<T>, in that it's not always clear how to avoid double-counting.
161// For now, we choose not to follow any such pointers.
162impl<T> MallocSizeOf for Dom<T> {
163    fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
164        0
165    }
166}
167
168impl<T> PartialEq for Dom<T> {
169    fn eq(&self, other: &Dom<T>) -> bool {
170        self.ptr.as_ptr() == other.ptr.as_ptr()
171    }
172}
173
174impl<'a, T: DomObject> PartialEq<&'a T> for Dom<T> {
175    fn eq(&self, other: &&'a T) -> bool {
176        *self == Dom::from_ref(*other)
177    }
178}
179
180impl<T> Eq for Dom<T> {}
181
182impl<T> Hash for Dom<T> {
183    fn hash<H: Hasher>(&self, state: &mut H) {
184        self.ptr.as_ptr().hash(state)
185    }
186}
187
188impl<T> Clone for Dom<T> {
189    #[inline]
190    #[cfg_attr(crown, expect(crown::unrooted_must_root))]
191    fn clone(&self) -> Self {
192        assert_in_script();
193        Dom { ptr: self.ptr }
194    }
195}
196
197impl<T: DomObject> Dom<T> {
198    /// Create a `Dom<T>` from a `&T`
199    #[cfg_attr(crown, expect(crown::unrooted_must_root))]
200    pub fn from_ref(obj: &T) -> Dom<T> {
201        assert_in_script();
202        Dom {
203            ptr: ptr::NonNull::from(obj),
204        }
205    }
206
207    /// Return a rooted version of this DOM object ([`DomRoot<T>`]) suitable for use on the stack.
208    pub fn as_rooted(&self) -> DomRoot<T> {
209        DomRoot::from_ref(self)
210    }
211
212    pub fn as_ptr(&self) -> *const T {
213        self.ptr.as_ptr()
214    }
215}
216
217impl<T: DomObject> Deref for Dom<T> {
218    type Target = T;
219
220    fn deref(&self) -> &T {
221        assert_in_script();
222        // We can only have &Dom<T> from a rooted thing, so it's safe to deref
223        // it to &T.
224        unsafe { &*self.ptr.as_ptr() }
225    }
226}
227
228unsafe impl<T: DomObject> JSTraceable for Dom<T> {
229    unsafe fn trace(&self, tracer: *mut JSTracer) {
230        let trace_info = if cfg!(debug_assertions) {
231            std::any::type_name::<T>()
232        } else {
233            "DOM object on heap"
234        };
235        unsafe {
236            trace_reflector(tracer, trace_info, (*self.ptr.as_ptr()).reflector());
237        }
238    }
239}
240
241/// A traced reference to a DOM object that may not be reflected yet.
242#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
243pub struct MaybeUnreflectedDom<T> {
244    ptr: ptr::NonNull<T>,
245}
246
247impl<T> MaybeUnreflectedDom<T>
248where
249    T: DomObject,
250{
251    /// Create a new MaybeUnreflectedDom value from the given boxed DOM object.
252    ///
253    /// # Safety
254    /// TODO: unclear why this is marked unsafe.
255    #[cfg_attr(crown, expect(crown::unrooted_must_root))]
256    pub unsafe fn from_box(value: Box<T>) -> Self {
257        Self {
258            ptr: Box::leak(value).into(),
259        }
260    }
261}
262
263impl<T> Root<MaybeUnreflectedDom<T>>
264where
265    T: DomObject,
266{
267    pub fn as_ptr(&self) -> *const T {
268        self.value.ptr.as_ptr()
269    }
270}
271
272impl<T> Root<MaybeUnreflectedDom<T>>
273where
274    T: MutDomObject,
275{
276    /// Treat the given JS object as the reflector of this unreflected object.
277    ///
278    /// # Safety
279    /// obj must point to a valid, non-null JS object.
280    pub unsafe fn reflect_with(self, obj: *mut JSObject) -> DomRoot<T> {
281        let ptr = self.as_ptr();
282        drop(self);
283        let root = DomRoot::from_ref(unsafe { &*ptr });
284        unsafe { root.init_reflector::<T>(obj) };
285        root
286    }
287}
288
289/// A rooted reference to a DOM object.
290pub type DomRoot<T> = Root<Dom<T>>;
291
292impl<T: Castable> DomRoot<T> {
293    /// Cast a DOM object root upwards to one of the interfaces it derives from.
294    pub fn upcast<U>(root: DomRoot<T>) -> DomRoot<U>
295    where
296        U: Castable,
297        T: DerivedFrom<U>,
298    {
299        unsafe { mem::transmute::<DomRoot<T>, DomRoot<U>>(root) }
300    }
301
302    /// Cast a DOM object root downwards to one of the interfaces it might implement.
303    pub fn downcast<U>(root: DomRoot<T>) -> Option<DomRoot<U>>
304    where
305        U: DerivedFrom<T>,
306    {
307        if root.is::<U>() {
308            Some(unsafe { mem::transmute::<DomRoot<T>, DomRoot<U>>(root) })
309        } else {
310            None
311        }
312    }
313}
314
315impl<T: DomObject> DomRoot<T> {
316    /// Generate a new root from a reference
317    pub fn from_ref(unrooted: &T) -> DomRoot<T> {
318        unsafe { DomRoot::new(Dom::from_ref(unrooted)) }
319    }
320
321    /// Create a traced version of this rooted object.
322    ///
323    /// # Safety
324    ///
325    /// This should never be used to create on-stack values. Instead these values should always
326    /// end up as members of other DOM objects.
327    #[cfg_attr(crown, expect(crown::unrooted_must_root))]
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
446pub fn assert_in_script() {
447    debug_assert!(thread_state::get().is_script());
448}
449
450/// Get a slice of references to DOM objects.
451pub trait DomSlice<T>
452where
453    T: JSTraceable + DomObject,
454{
455    /// Returns the slice of `T` references.
456    fn r(&self) -> &[&T];
457}
458
459impl<T> DomSlice<T> for [Dom<T>]
460where
461    T: JSTraceable + DomObject,
462{
463    #[inline]
464    fn r(&self) -> &[&T] {
465        let _ = mem::transmute::<Dom<T>, &T>;
466        unsafe { &*(self as *const [Dom<T>] as *const [&T]) }
467    }
468}