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