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