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