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};
14use style::thread_state;
15
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    #[cfg_attr(crown, expect(crown::unrooted_must_root))]
195    fn clone(&self) -> Self {
196        assert_in_script();
197        Dom { ptr: self.ptr }
198    }
199}
200
201impl<T: DomObject> Dom<T> {
202    /// Create a `Dom<T>` from a `&T`
203    #[cfg_attr(crown, expect(crown::unrooted_must_root))]
204    pub fn from_ref(obj: &T) -> Dom<T> {
205        assert_in_script();
206        Dom {
207            ptr: ptr::NonNull::from(obj),
208        }
209    }
210
211    /// Return a rooted version of this DOM object ([`DomRoot<T>`]) suitable for use on the stack.
212    pub fn as_rooted(&self) -> DomRoot<T> {
213        DomRoot::from_ref(self)
214    }
215
216    pub fn as_ptr(&self) -> *const T {
217        self.ptr.as_ptr()
218    }
219}
220
221impl<T: DomObject> Deref for Dom<T> {
222    type Target = T;
223
224    fn deref(&self) -> &T {
225        assert_in_script();
226        // We can only have &Dom<T> from a rooted thing, so it's safe to deref
227        // it to &T.
228        unsafe { &*self.ptr.as_ptr() }
229    }
230}
231
232unsafe impl<T: DomObject> JSTraceable for Dom<T> {
233    unsafe fn trace(&self, tracer: *mut JSTracer) {
234        let trace_info = if cfg!(debug_assertions) {
235            std::any::type_name::<T>()
236        } else {
237            "DOM object on heap"
238        };
239        unsafe {
240            trace_reflector(tracer, trace_info, (*self.ptr.as_ptr()).reflector());
241        }
242    }
243}
244
245/// A traced reference to a DOM object that may not be reflected yet.
246#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
247pub struct MaybeUnreflectedDom<T> {
248    ptr: ptr::NonNull<T>,
249}
250
251impl<T> MaybeUnreflectedDom<T>
252where
253    T: DomObject,
254{
255    /// Create a new MaybeUnreflectedDom value from the given boxed DOM object.
256    ///
257    /// # Safety
258    /// TODO: unclear why this is marked unsafe.
259    #[cfg_attr(crown, expect(crown::unrooted_must_root))]
260    pub unsafe fn from_box(value: Box<T>) -> Self {
261        Self {
262            ptr: Box::leak(value).into(),
263        }
264    }
265}
266
267impl<T> Root<MaybeUnreflectedDom<T>>
268where
269    T: DomObject,
270{
271    pub fn as_ptr(&self) -> *const T {
272        self.value.ptr.as_ptr()
273    }
274}
275
276impl<T> Root<MaybeUnreflectedDom<T>>
277where
278    T: MutDomObject,
279{
280    /// Treat the given JS object as the reflector of this unreflected object.
281    ///
282    /// # Safety
283    /// obj must point to a valid, non-null JS object.
284    pub unsafe fn reflect_with(self, obj: *mut JSObject) -> DomRoot<T> {
285        let ptr = self.as_ptr();
286        drop(self);
287        let root = DomRoot::from_ref(unsafe { &*ptr });
288        unsafe { root.init_reflector::<T>(obj) };
289        root
290    }
291}
292
293/// A rooted reference to a DOM object.
294pub type DomRoot<T> = Root<Dom<T>>;
295
296impl<T: Castable> DomRoot<T> {
297    /// Cast a DOM object root upwards to one of the interfaces it derives from.
298    pub fn upcast<U>(root: DomRoot<T>) -> DomRoot<U>
299    where
300        U: Castable,
301        T: DerivedFrom<U>,
302    {
303        unsafe { mem::transmute::<DomRoot<T>, DomRoot<U>>(root) }
304    }
305
306    /// Cast a DOM object root downwards to one of the interfaces it might implement.
307    pub fn downcast<U>(root: DomRoot<T>) -> Option<DomRoot<U>>
308    where
309        U: DerivedFrom<T>,
310    {
311        if root.is::<U>() {
312            Some(unsafe { mem::transmute::<DomRoot<T>, DomRoot<U>>(root) })
313        } else {
314            None
315        }
316    }
317}
318
319impl<T: DomObject> DomRoot<T> {
320    /// Generate a new root from a reference
321    pub fn from_ref(unrooted: &T) -> DomRoot<T> {
322        unsafe { DomRoot::new(Dom::from_ref(unrooted)) }
323    }
324
325    /// Create a traced version of this rooted object.
326    ///
327    /// # Safety
328    ///
329    /// This should never be used to create on-stack values. Instead these values should always
330    /// end up as members of other DOM objects.
331    #[cfg_attr(crown, expect(crown::unrooted_must_root))]
332    pub fn as_traced(&self) -> Dom<T> {
333        Dom::from_ref(self)
334    }
335}
336
337impl<T> MallocSizeOf for DomRoot<T>
338where
339    T: DomObject + MallocSizeOf,
340{
341    fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
342        0
343    }
344}
345
346impl<T> PartialEq for DomRoot<T>
347where
348    T: DomObject,
349{
350    fn eq(&self, other: &Self) -> bool {
351        self.value == other.value
352    }
353}
354
355impl<T: DomObject> Eq for DomRoot<T> {}
356
357impl<T: DomObject> Hash for DomRoot<T> {
358    fn hash<H: Hasher>(&self, state: &mut H) {
359        self.value.hash(state);
360    }
361}
362
363impl<T> Clone for DomRoot<T>
364where
365    T: DomObject,
366{
367    fn clone(&self) -> DomRoot<T> {
368        DomRoot::from_ref(self)
369    }
370}
371
372unsafe impl<T> JSTraceable for DomRoot<T>
373where
374    T: DomObject,
375{
376    unsafe fn trace(&self, _: *mut JSTracer) {
377        // Already traced.
378    }
379}
380
381/// A rooting mechanism for reflectors on the stack.
382/// LIFO is not required.
383///
384/// See also [*Exact Stack Rooting - Storing a GCPointer on the CStack*][cstack].
385///
386/// [cstack]: https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/Internals/GC/Exact_Stack_Rooting
387pub struct RootCollection {
388    roots: UnsafeCell<Vec<*const dyn JSTraceable>>,
389}
390
391impl RootCollection {
392    /// Create an empty collection of roots
393    #[expect(clippy::new_without_default)]
394    pub const fn new() -> RootCollection {
395        RootCollection {
396            roots: UnsafeCell::new(vec![]),
397        }
398    }
399
400    /// Starts tracking a trace object.
401    unsafe fn root(&self, object: *const dyn JSTraceable) {
402        assert_in_script();
403        unsafe { (*self.roots.get()).push(object) };
404    }
405
406    /// Stops tracking a trace object, asserting if it isn't found.
407    unsafe fn unroot(&self, object: *const dyn JSTraceable) {
408        assert_in_script();
409        let roots = unsafe { &mut *self.roots.get() };
410        match roots
411            .iter()
412            .rposition(|r| std::ptr::addr_eq(*r as *const (), object as *const ()))
413        {
414            Some(idx) => {
415                // Partial inlining of `Vec::swap_remove` to avoid having to read and return the value since
416                // we don't care about it, doing less work in our case.
417                // SAFETY: the copy source and destination are derived from valid positions in the vector.
418                unsafe {
419                    let len = roots.len() - 1;
420                    if len != idx {
421                        let base_ptr = roots.as_mut_ptr();
422                        ptr::copy_nonoverlapping(base_ptr.add(len), base_ptr.add(idx), 1);
423                    }
424                    roots.set_len(len);
425                }
426            },
427            None => panic!("Can't remove a root that was never rooted!"),
428        }
429    }
430}
431
432thread_local!(pub static STACK_ROOTS: RootCollection = const { RootCollection::new() });
433
434/// SM Callback that traces the rooted reflectors
435///
436/// # Safety
437/// tracer must point to a valid, non-null JS tracer object.
438pub unsafe fn trace_roots(tracer: *mut JSTracer) {
439    trace!("tracing stack roots");
440    STACK_ROOTS.with(|collection| {
441        let collection = unsafe { &*collection.roots.get() };
442        for root in collection {
443            unsafe {
444                (**root).trace(tracer);
445            }
446        }
447    });
448}
449
450pub fn assert_in_script() {
451    debug_assert!(thread_state::get().is_script());
452}
453
454/// Get a slice of references to DOM objects.
455pub trait DomSlice<T>
456where
457    T: JSTraceable + DomObject,
458{
459    /// Returns the slice of `T` references.
460    fn r(&self) -> &[&T];
461}
462
463impl<T> DomSlice<T> for [Dom<T>]
464where
465    T: JSTraceable + DomObject,
466{
467    #[inline]
468    fn r(&self) -> &[&T] {
469        let _ = mem::transmute::<Dom<T>, &T>;
470        unsafe { &*(self as *const [Dom<T>] as *const [&T]) }
471    }
472}
473
474/// Returns a handle to a Heap member of a reflected DOM object.
475/// The provided callback acts as a projection of the rooted-ness of
476/// the provided DOM object; it must return a reference to a Heap
477/// member of the DOM object.
478pub fn rooted_heap_handle<'a, T: DomObject, U: GCMethods + Copy>(
479    object: &'a T,
480    f: impl Fn(&'a T) -> &'a Heap<U>,
481) -> Handle<'a, U> {
482    // SAFETY: Heap::handle is safe to call when the Heap is a member
483    //   of a rooted object. Our safety invariants for DOM objects
484    //   ensure that a &T is obtained via a root of T.
485    unsafe { Handle::from_raw(f(object).handle()) }
486}