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