Skip to main content

script_bindings/
trace.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::OnceCell;
6use std::fmt::Display;
7use std::hash::{BuildHasher, Hash};
8use std::marker::PhantomData;
9use std::mem;
10use std::ops::{Deref, DerefMut};
11
12use atomic_refcell::AtomicRefCell;
13use crossbeam_channel::Sender;
14use html5ever::interface::{Tracer as HtmlTracer, TreeSink};
15use html5ever::tokenizer::{TokenSink, Tokenizer};
16use html5ever::tree_builder::TreeBuilder;
17use indexmap::IndexMap;
18use js::gc::{GCMethods, Handle};
19use js::glue::CallObjectTracer;
20use js::jsapi::{GCTraceKindToAscii, Heap, JSObject, JSTracer, TraceKind};
21use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
22use parking_lot::RwLock;
23use servo_arc::Arc as ServoArc;
24use servo_base::text::{Utf8CodeUnits, Utf16CodeUnits};
25use smallvec::{Array, SmallVec};
26use style::author_styles::AuthorStyles;
27use style::stylesheet_set::{AuthorStylesheetSet, DocumentStylesheetSet};
28use tendril::TendrilSink;
29use tendril::fmt::UTF8;
30use tendril::stream::LossyDecoder;
31#[cfg(feature = "webxr")]
32use webxr_api::{Finger, Hand};
33use xml5ever::interface::TreeSink as XmlTreeSink;
34use xml5ever::tokenizer::XmlTokenizer;
35use xml5ever::tree_builder::{Tracer as XmlTracer, XmlTreeBuilder};
36
37use crate::JSTraceable;
38use crate::error::Error;
39use crate::reflector::Reflector;
40use crate::str::USVString;
41use crate::tasks::TaskBox;
42
43/// Trace the `JSObject` held by `reflector`.
44///
45/// # Safety
46/// tracer must point to a valid, non-null JS tracer.
47pub unsafe fn trace_reflector<T>(
48    tracer: *mut JSTracer,
49    description: &str,
50    reflector: &Reflector<T>,
51) {
52    trace!("tracing reflector {}", description);
53    unsafe { trace_object(tracer, description, reflector.rootable()) }
54}
55
56/// Trace a `JSObject`.
57///
58/// # Safety
59/// tracer must point to a valid, non-null JS tracer.
60pub(crate) unsafe fn trace_object(
61    tracer: *mut JSTracer,
62    description: &str,
63    obj: &Heap<*mut JSObject>,
64) {
65    unsafe {
66        trace!("tracing {}", description);
67        CallObjectTracer(
68            tracer,
69            obj.ptr.get() as *mut _,
70            GCTraceKindToAscii(TraceKind::Object),
71        );
72    }
73}
74
75/// For use on non-jsmanaged types
76/// Use #[derive(JSTraceable)] on JS managed types
77macro_rules! unsafe_no_jsmanaged_fields(
78    ($($ty:ty),+) => (
79        $(
80            #[expect(unsafe_code)]
81            unsafe impl crate::JSTraceable for $ty {
82                #[inline]
83                unsafe fn trace(&self, _: *mut ::js::jsapi::JSTracer) {
84                    // Do nothing
85                }
86            }
87        )+
88    );
89);
90
91unsafe_no_jsmanaged_fields!(USVString);
92unsafe_no_jsmanaged_fields!(Error);
93unsafe_no_jsmanaged_fields!(Box<dyn TaskBox>);
94
95/// A trait to allow tracing only DOM sub-objects.
96///
97/// # Safety
98///
99/// This trait is unsafe; if it is implemented incorrectly, the GC may end up collecting objects
100/// that are still reachable.
101pub unsafe trait CustomTraceable {
102    /// Trace `self`.
103    ///
104    /// # Safety
105    ///
106    /// The `JSTracer` argument must point to a valid `JSTracer` in memory. In addition,
107    /// implementors of this method must ensure that all active objects are properly traced
108    /// or else the garbage collector may end up collecting objects that are still reachable.
109    unsafe fn trace(&self, trc: *mut JSTracer);
110}
111
112unsafe impl<T: CustomTraceable> CustomTraceable for Box<T> {
113    #[inline]
114    unsafe fn trace(&self, trc: *mut JSTracer) {
115        unsafe { (**self).trace(trc) };
116    }
117}
118
119unsafe impl<T: JSTraceable> CustomTraceable for OnceCell<T> {
120    unsafe fn trace(&self, tracer: *mut JSTracer) {
121        if let Some(value) = self.get() {
122            unsafe { value.trace(tracer) }
123        }
124    }
125}
126
127unsafe impl<T> CustomTraceable for Sender<T> {
128    unsafe fn trace(&self, _: *mut JSTracer) {}
129}
130
131unsafe impl<T: JSTraceable> CustomTraceable for ServoArc<T> {
132    unsafe fn trace(&self, trc: *mut JSTracer) {
133        unsafe { (**self).trace(trc) }
134    }
135}
136
137unsafe impl<T: JSTraceable> CustomTraceable for RwLock<T> {
138    unsafe fn trace(&self, trc: *mut JSTracer) {
139        unsafe { self.read().trace(trc) }
140    }
141}
142
143unsafe impl<T: JSTraceable> CustomTraceable for AtomicRefCell<T> {
144    unsafe fn trace(&self, trc: *mut JSTracer) {
145        unsafe { self.borrow().trace(trc) }
146    }
147}
148
149unsafe impl<T: JSTraceable + Eq + Hash> CustomTraceable for indexmap::IndexSet<T> {
150    #[inline]
151    unsafe fn trace(&self, trc: *mut JSTracer) {
152        for e in self.iter() {
153            unsafe { e.trace(trc) };
154        }
155    }
156}
157
158// XXXManishearth Check if the following three are optimized to no-ops
159// if e.trace() is a no-op (e.g it is an unsafe_no_jsmanaged_fields type)
160unsafe impl<T: JSTraceable + 'static, const N: usize> CustomTraceable for SmallVec<[T; N]> {
161    #[inline]
162    unsafe fn trace(&self, trc: *mut JSTracer) {
163        for e in self.iter() {
164            unsafe { e.trace(trc) };
165        }
166    }
167}
168
169unsafe impl<K, V, S> CustomTraceable for IndexMap<K, V, S>
170where
171    K: Hash + Eq + JSTraceable,
172    V: JSTraceable,
173    S: BuildHasher,
174{
175    #[inline]
176    unsafe fn trace(&self, trc: *mut JSTracer) {
177        for (k, v) in self {
178            unsafe { k.trace(trc) };
179            unsafe { v.trace(trc) };
180        }
181    }
182}
183
184unsafe impl<S> CustomTraceable for DocumentStylesheetSet<S>
185where
186    S: JSTraceable + ::style::stylesheets::StylesheetInDocument + PartialEq + 'static,
187{
188    unsafe fn trace(&self, tracer: *mut JSTracer) {
189        for (s, _origin) in self.iter() {
190            unsafe { s.trace(tracer) };
191        }
192    }
193}
194
195unsafe impl<S> CustomTraceable for AuthorStylesheetSet<S>
196where
197    S: JSTraceable + ::style::stylesheets::StylesheetInDocument + PartialEq + 'static,
198{
199    unsafe fn trace(&self, tracer: *mut JSTracer) {
200        for s in self.iter() {
201            unsafe { s.trace(tracer) };
202        }
203    }
204}
205
206unsafe impl<S> CustomTraceable for AuthorStyles<S>
207where
208    S: JSTraceable + ::style::stylesheets::StylesheetInDocument + PartialEq + 'static,
209{
210    unsafe fn trace(&self, tracer: *mut JSTracer) {
211        unsafe { self.stylesheets.trace(tracer) };
212    }
213}
214
215unsafe impl<Sink> CustomTraceable for LossyDecoder<Sink>
216where
217    Sink: JSTraceable + TendrilSink<UTF8>,
218{
219    unsafe fn trace(&self, tracer: *mut JSTracer) {
220        unsafe { self.inner_sink().trace(tracer) };
221    }
222}
223
224#[cfg(feature = "webxr")]
225unsafe impl<J> CustomTraceable for Hand<J>
226where
227    J: JSTraceable,
228{
229    #[inline]
230    unsafe fn trace(&self, trc: *mut JSTracer) {
231        // exhaustive match so we don't miss new fields
232        let Hand {
233            ref wrist,
234            ref thumb_metacarpal,
235            ref thumb_phalanx_proximal,
236            ref thumb_phalanx_distal,
237            ref thumb_phalanx_tip,
238            ref index,
239            ref middle,
240            ref ring,
241            ref little,
242        } = *self;
243        unsafe {
244            wrist.trace(trc);
245            thumb_metacarpal.trace(trc);
246            thumb_phalanx_proximal.trace(trc);
247            thumb_phalanx_distal.trace(trc);
248            thumb_phalanx_tip.trace(trc);
249            index.trace(trc);
250            middle.trace(trc);
251            ring.trace(trc);
252            little.trace(trc);
253        }
254    }
255}
256
257#[cfg(feature = "webxr")]
258unsafe impl<J> CustomTraceable for Finger<J>
259where
260    J: JSTraceable,
261{
262    #[inline]
263    unsafe fn trace(&self, trc: *mut JSTracer) {
264        // exhaustive match so we don't miss new fields
265        let Finger {
266            ref metacarpal,
267            ref phalanx_proximal,
268            ref phalanx_intermediate,
269            ref phalanx_distal,
270            ref phalanx_tip,
271        } = *self;
272        unsafe {
273            metacarpal.trace(trc);
274            phalanx_proximal.trace(trc);
275            phalanx_intermediate.trace(trc);
276            phalanx_distal.trace(trc);
277            phalanx_tip.trace(trc);
278        }
279    }
280}
281
282unsafe impl<Handle: JSTraceable + Clone, Sink: TreeSink<Handle = Handle> + JSTraceable>
283    CustomTraceable for TreeBuilder<Handle, Sink>
284{
285    unsafe fn trace(&self, trc: *mut JSTracer) {
286        struct Tracer<Handle>(*mut JSTracer, PhantomData<Handle>);
287        let tracer = Tracer::<Handle>(trc, PhantomData);
288
289        impl<Handle: JSTraceable> HtmlTracer for Tracer<Handle> {
290            type Handle = Handle;
291            fn trace_handle(&self, node: &Handle) {
292                unsafe {
293                    node.trace(self.0);
294                }
295            }
296        }
297
298        self.trace_handles(&tracer);
299        unsafe { self.sink.trace(trc) };
300    }
301}
302
303#[expect(unsafe_code)]
304unsafe impl<Handle: JSTraceable + Clone, Sink: TokenSink<Handle = Handle> + CustomTraceable>
305    CustomTraceable for Tokenizer<Sink>
306{
307    unsafe fn trace(&self, trc: *mut JSTracer) {
308        unsafe { self.sink.trace(trc) };
309    }
310}
311
312#[expect(unsafe_code)]
313unsafe impl<Handle: JSTraceable + Clone, Sink: JSTraceable + XmlTreeSink<Handle = Handle>>
314    CustomTraceable for XmlTokenizer<XmlTreeBuilder<Handle, Sink>>
315{
316    unsafe fn trace(&self, trc: *mut JSTracer) {
317        struct Tracer<Handle>(*mut JSTracer, PhantomData<Handle>);
318        let tracer = Tracer(trc, PhantomData);
319
320        impl<Handle: JSTraceable> XmlTracer for Tracer<Handle> {
321            type Handle = Handle;
322            fn trace_handle(&self, node: &Handle) {
323                unsafe {
324                    node.trace(self.0);
325                }
326            }
327        }
328
329        let tree_builder = &self.sink;
330        tree_builder.trace_handles(&tracer);
331        unsafe { tree_builder.sink.trace(trc) };
332    }
333}
334
335/// Roots any JSTraceable thing
336///
337/// If you have a valid DomObject, use DomRoot.
338/// If you have GC things like *mut JSObject or JSVal, use rooted!.
339/// If you have an arbitrary number of DomObjects to root, use rooted_vec!.
340/// If you know what you're doing, use this.
341#[cfg_attr(crown, crown::unrooted_must_root_lint::allow_unrooted_interior)]
342pub struct RootedTraceableBox<T: JSTraceable + 'static>(js::gc::RootedTraceableBox<T>);
343
344unsafe impl<T: JSTraceable + 'static> JSTraceable for RootedTraceableBox<T> {
345    unsafe fn trace(&self, tracer: *mut JSTracer) {
346        unsafe { self.0.trace(tracer) };
347    }
348}
349
350impl<T: JSTraceable + 'static> RootedTraceableBox<T> {
351    /// DomRoot a JSTraceable thing for the life of this RootedTraceableBox
352    pub fn new(traceable: T) -> RootedTraceableBox<T> {
353        Self(js::gc::RootedTraceableBox::new(traceable))
354    }
355
356    /// Consumes a boxed JSTraceable and roots it for the life of this RootedTraceableBox.
357    pub fn from_box(boxed_traceable: Box<T>) -> RootedTraceableBox<T> {
358        Self(js::gc::RootedTraceableBox::from_box(boxed_traceable))
359    }
360
361    pub fn into_box(self) -> Box<T> {
362        self.0.into_box()
363    }
364}
365
366impl<T> RootedTraceableBox<Heap<T>>
367where
368    Heap<T>: JSTraceable + 'static,
369    T: GCMethods + Copy,
370{
371    pub fn handle(&self) -> Handle<'_, T> {
372        self.0.handle()
373    }
374}
375
376impl<T: JSTraceable + MallocSizeOf> MallocSizeOf for RootedTraceableBox<T> {
377    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
378        // Briefly resurrect the real Box value so we can rely on the existing calculations.
379        // Then immediately forget about it again to avoid dropping the box.
380        let inner = unsafe { Box::from_raw(self.0.ptr()) };
381        let size = inner.size_of(ops);
382        mem::forget(inner);
383        size
384    }
385}
386
387impl<T: JSTraceable + Default> Default for RootedTraceableBox<T> {
388    fn default() -> RootedTraceableBox<T> {
389        RootedTraceableBox::new(T::default())
390    }
391}
392
393impl<T: JSTraceable> Deref for RootedTraceableBox<T> {
394    type Target = T;
395    fn deref(&self) -> &T {
396        self.0.deref()
397    }
398}
399
400impl<T: JSTraceable> DerefMut for RootedTraceableBox<T> {
401    fn deref_mut(&mut self) -> &mut T {
402        self.0.deref_mut()
403    }
404}
405
406/// Wrapper type for nop traceble
407///
408/// SAFETY: Inner type must not impl JSTraceable
409#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
410#[cfg_attr(crown, crown::trace_in_no_trace_lint::must_not_have_traceable)]
411pub(crate) struct NoTrace<T>(pub(crate) T);
412
413impl<T: Display> Display for NoTrace<T> {
414    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
415        self.0.fmt(f)
416    }
417}
418
419impl<T> From<T> for NoTrace<T> {
420    fn from(item: T) -> Self {
421        Self(item)
422    }
423}
424
425#[expect(unsafe_code)]
426unsafe impl<T> JSTraceable for NoTrace<T> {
427    #[inline]
428    unsafe fn trace(&self, _: *mut ::js::jsapi::JSTracer) {}
429}
430
431impl<T: MallocSizeOf> MallocSizeOf for NoTrace<T> {
432    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
433        self.0.size_of(ops)
434    }
435}
436
437unsafe impl CustomTraceable for Utf8CodeUnits {
438    #[inline]
439    unsafe fn trace(&self, _: *mut JSTracer) {}
440}
441
442unsafe impl CustomTraceable for Utf16CodeUnits {
443    #[inline]
444    unsafe fn trace(&self, _: *mut JSTracer) {}
445}
446
447unsafe impl<T: CustomTraceable> CustomTraceable for Option<T> {
448    #[inline]
449    unsafe fn trace(&self, tracer: *mut JSTracer) {
450        unsafe {
451            self.as_ref().inspect(|value| value.trace(tracer));
452        }
453    }
454}
455
456/// A newtype wrapper for [`SmallVec`] that allows tracing its contents. This allows creating
457/// [`SmallVec`]s of unrooted nodes on the stack that are contained within a containing
458/// [`RootedTraceableBox`].
459pub struct TraceableSmallVec<A: Array>(SmallVec<A>);
460
461impl<A: Array> Default for TraceableSmallVec<A> {
462    fn default() -> Self {
463        Self(Default::default())
464    }
465}
466
467impl<A: Array> Deref for TraceableSmallVec<A> {
468    type Target = SmallVec<A>;
469    fn deref(&self) -> &SmallVec<A> {
470        &self.0
471    }
472}
473
474impl<A: Array> DerefMut for TraceableSmallVec<A> {
475    fn deref_mut(&mut self) -> &mut SmallVec<A> {
476        &mut self.0
477    }
478}
479
480unsafe impl<A: Array> js::rust::Trace for TraceableSmallVec<A>
481where
482    A::Item: JSTraceable,
483{
484    #[inline]
485    unsafe fn trace(&self, tracer: *mut JSTracer) {
486        for value in self.0.iter() {
487            unsafe { value.trace(tracer) };
488        }
489    }
490}