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    #[cfg_attr(crown, allow(crown::unrooted_must_root))]
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, allow(crown::unrooted_must_root))]
78        struct ReflectorStackRoot(Reflector);
79        unsafe impl JSTraceable for ReflectorStackRoot {
80            unsafe fn trace(&self, tracer: *mut JSTracer) {
81                unsafe { trace_reflector(tracer, "on stack", &self.0) };
82            }
83        }
84        unsafe { &*(self.reflector() as *const Reflector as *const ReflectorStackRoot) }
85    }
86}
87
88unsafe impl<T> StableTraceObject for MaybeUnreflectedDom<T>
89where
90    T: DomObject,
91{
92    fn stable_trace_object(&self) -> *const dyn JSTraceable {
93        // The JSTraceable impl for Reflector doesn't actually do anything,
94        // so we need this shenanigan to actually trace the reflector of the
95        // T pointer in Dom<T>.
96        #[cfg_attr(crown, allow(crown::unrooted_must_root))]
97        struct MaybeUnreflectedStackRoot<T>(T);
98        unsafe impl<T> JSTraceable for MaybeUnreflectedStackRoot<T>
99        where
100            T: DomObject,
101        {
102            unsafe fn trace(&self, tracer: *mut JSTracer) {
103                if self.0.reflector().get_jsobject().is_null() {
104                    unsafe { self.0.trace(tracer) };
105                } else {
106                    unsafe { trace_reflector(tracer, "on stack", self.0.reflector()) };
107                }
108            }
109        }
110        unsafe { &*(self.ptr.as_ptr() as *const T as *const MaybeUnreflectedStackRoot<T>) }
111    }
112}
113
114impl<T> Deref for Root<T>
115where
116    T: Deref + StableTraceObject,
117{
118    type Target = <T as Deref>::Target;
119
120    fn deref(&self) -> &Self::Target {
121        assert_in_script();
122        &self.value
123    }
124}
125
126impl<T> Drop for Root<T>
127where
128    T: StableTraceObject,
129{
130    fn drop(&mut self) {
131        unsafe {
132            (*self.root_list).unroot(self.value.stable_trace_object());
133        }
134    }
135}
136
137impl<T: fmt::Debug + StableTraceObject> fmt::Debug for Root<T> {
138    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
139        self.value.fmt(f)
140    }
141}
142
143impl<T: fmt::Debug + DomObject> fmt::Debug for Dom<T> {
144    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
145        (**self).fmt(f)
146    }
147}
148
149/// A traced reference to a DOM object
150///
151/// This type is critical to making garbage collection work with the DOM,
152/// but it is very dangerous; if garbage collection happens with a `Dom<T>`
153/// on the stack, the `Dom<T>` can point to freed memory.
154///
155/// This should only be used as a field in other DOM objects.
156#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
157#[repr(transparent)]
158pub struct Dom<T> {
159    ptr: ptr::NonNull<T>,
160}
161
162// Dom<T> is similar to Rc<T>, in that it's not always clear how to avoid double-counting.
163// For now, we choose not to follow any such pointers.
164impl<T> MallocSizeOf for Dom<T> {
165    fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
166        0
167    }
168}
169
170impl<T> PartialEq for Dom<T> {
171    fn eq(&self, other: &Dom<T>) -> bool {
172        self.ptr.as_ptr() == other.ptr.as_ptr()
173    }
174}
175
176impl<'a, T: DomObject> PartialEq<&'a T> for Dom<T> {
177    fn eq(&self, other: &&'a T) -> bool {
178        *self == Dom::from_ref(*other)
179    }
180}
181
182impl<T> Eq for Dom<T> {}
183
184impl<T> Hash for Dom<T> {
185    fn hash<H: Hasher>(&self, state: &mut H) {
186        self.ptr.as_ptr().hash(state)
187    }
188}
189
190impl<T> Clone for Dom<T> {
191    #[inline]
192    #[cfg_attr(crown, allow(crown::unrooted_must_root))]
193    fn clone(&self) -> Self {
194        assert_in_script();
195        Dom { ptr: self.ptr }
196    }
197}
198
199impl<T: DomObject> Dom<T> {
200    /// Create a `Dom<T>` from a `&T`
201    #[cfg_attr(crown, allow(crown::unrooted_must_root))]
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    #[cfg_attr(crown, allow(crown::unrooted_must_root))]
258    pub unsafe fn from_box(value: Box<T>) -> Self {
259        Self {
260            ptr: Box::leak(value).into(),
261        }
262    }
263}
264
265impl<T> Root<MaybeUnreflectedDom<T>>
266where
267    T: DomObject,
268{
269    pub fn as_ptr(&self) -> *const T {
270        self.value.ptr.as_ptr()
271    }
272}
273
274impl<T> Root<MaybeUnreflectedDom<T>>
275where
276    T: MutDomObject,
277{
278    /// Treat the given JS object as the reflector of this unreflected object.
279    ///
280    /// # Safety
281    /// obj must point to a valid, non-null JS object.
282    pub unsafe fn reflect_with(self, obj: *mut JSObject) -> DomRoot<T> {
283        let ptr = self.as_ptr();
284        drop(self);
285        let root = DomRoot::from_ref(unsafe { &*ptr });
286        unsafe { root.init_reflector(obj) };
287        root
288    }
289}
290
291/// A rooted reference to a DOM object.
292pub type DomRoot<T> = Root<Dom<T>>;
293
294impl<T: Castable> DomRoot<T> {
295    /// Cast a DOM object root upwards to one of the interfaces it derives from.
296    pub fn upcast<U>(root: DomRoot<T>) -> DomRoot<U>
297    where
298        U: Castable,
299        T: DerivedFrom<U>,
300    {
301        unsafe { mem::transmute::<DomRoot<T>, DomRoot<U>>(root) }
302    }
303
304    /// Cast a DOM object root downwards to one of the interfaces it might implement.
305    pub fn downcast<U>(root: DomRoot<T>) -> Option<DomRoot<U>>
306    where
307        U: DerivedFrom<T>,
308    {
309        if root.is::<U>() {
310            Some(unsafe { mem::transmute::<DomRoot<T>, DomRoot<U>>(root) })
311        } else {
312            None
313        }
314    }
315}
316
317impl<T: DomObject> DomRoot<T> {
318    /// Generate a new root from a reference
319    pub fn from_ref(unrooted: &T) -> DomRoot<T> {
320        unsafe { DomRoot::new(Dom::from_ref(unrooted)) }
321    }
322
323    /// Create a traced version of this rooted object.
324    ///
325    /// # Safety
326    ///
327    /// This should never be used to create on-stack values. Instead these values should always
328    /// end up as members of other DOM objects.
329    #[cfg_attr(crown, allow(crown::unrooted_must_root))]
330    pub fn as_traced(&self) -> Dom<T> {
331        Dom::from_ref(self)
332    }
333}
334
335impl<T> MallocSizeOf for DomRoot<T>
336where
337    T: DomObject + MallocSizeOf,
338{
339    fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
340        0
341    }
342}
343
344impl<T> PartialEq for DomRoot<T>
345where
346    T: DomObject,
347{
348    fn eq(&self, other: &Self) -> bool {
349        self.value == other.value
350    }
351}
352
353impl<T: DomObject> Eq for DomRoot<T> {}
354
355impl<T: DomObject> Hash for DomRoot<T> {
356    fn hash<H: Hasher>(&self, state: &mut H) {
357        self.value.hash(state);
358    }
359}
360
361impl<T> Clone for DomRoot<T>
362where
363    T: DomObject,
364{
365    fn clone(&self) -> DomRoot<T> {
366        DomRoot::from_ref(self)
367    }
368}
369
370unsafe impl<T> JSTraceable for DomRoot<T>
371where
372    T: DomObject,
373{
374    unsafe fn trace(&self, _: *mut JSTracer) {
375        // Already traced.
376    }
377}
378
379/// A rooting mechanism for reflectors on the stack.
380/// LIFO is not required.
381///
382/// See also [*Exact Stack Rooting - Storing a GCPointer on the CStack*][cstack].
383///
384/// [cstack]: https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/Internals/GC/Exact_Stack_Rooting
385pub struct RootCollection {
386    roots: UnsafeCell<Vec<*const dyn JSTraceable>>,
387}
388
389impl RootCollection {
390    /// Create an empty collection of roots
391    #[allow(clippy::new_without_default)]
392    pub const fn new() -> RootCollection {
393        RootCollection {
394            roots: UnsafeCell::new(vec![]),
395        }
396    }
397
398    /// Starts tracking a trace object.
399    unsafe fn root(&self, object: *const dyn JSTraceable) {
400        assert_in_script();
401        unsafe { (*self.roots.get()).push(object) };
402    }
403
404    /// Stops tracking a trace object, asserting if it isn't found.
405    unsafe fn unroot(&self, object: *const dyn JSTraceable) {
406        assert_in_script();
407        let roots = unsafe { &mut *self.roots.get() };
408        match roots
409            .iter()
410            .rposition(|r| std::ptr::addr_eq(*r as *const (), object as *const ()))
411        {
412            Some(idx) => {
413                roots.swap_remove(idx);
414            },
415            None => panic!("Can't remove a root that was never rooted!"),
416        }
417    }
418}
419
420thread_local!(pub static STACK_ROOTS: RootCollection = const { RootCollection::new() });
421
422/// SM Callback that traces the rooted reflectors
423///
424/// # Safety
425/// tracer must point to a valid, non-null JS tracer object.
426pub unsafe fn trace_roots(tracer: *mut JSTracer) {
427    trace!("tracing stack roots");
428    STACK_ROOTS.with(|collection| {
429        let collection = unsafe { &*collection.roots.get() };
430        for root in collection {
431            unsafe {
432                (**root).trace(tracer);
433            }
434        }
435    });
436}
437
438pub fn assert_in_script() {
439    debug_assert!(thread_state::get().is_script());
440}
441
442/// Get a slice of references to DOM objects.
443pub trait DomSlice<T>
444where
445    T: JSTraceable + DomObject,
446{
447    /// Returns the slice of `T` references.
448    fn r(&self) -> &[&T];
449}
450
451impl<T> DomSlice<T> for [Dom<T>]
452where
453    T: JSTraceable + DomObject,
454{
455    #[inline]
456    fn r(&self) -> &[&T] {
457        let _ = mem::transmute::<Dom<T>, &T>;
458        unsafe { &*(self as *const [Dom<T>] as *const [&T]) }
459    }
460}