| /* This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this |
| * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ |
| |
| use std::cell::OnceCell; |
| use std::fmt::Display; |
| use std::hash::{BuildHasher, Hash}; |
| use std::marker::PhantomData; |
| use std::mem; |
| use std::ops::{Deref, DerefMut}; |
| |
| use crossbeam_channel::Sender; |
| use html5ever::interface::{Tracer as HtmlTracer, TreeSink}; |
| use html5ever::tokenizer::{TokenSink, Tokenizer}; |
| use html5ever::tree_builder::TreeBuilder; |
| use indexmap::IndexMap; |
| use js::gc::{GCMethods, Handle}; |
| use js::glue::CallObjectTracer; |
| use js::jsapi::{GCTraceKindToAscii, Heap, JSObject, JSTracer, TraceKind}; |
| use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; |
| use parking_lot::RwLock; |
| use servo_arc::Arc as ServoArc; |
| use smallvec::SmallVec; |
| use style::author_styles::AuthorStyles; |
| use style::stylesheet_set::{AuthorStylesheetSet, DocumentStylesheetSet}; |
| use tendril::TendrilSink; |
| use tendril::fmt::UTF8; |
| use tendril::stream::LossyDecoder; |
| #[cfg(feature = "webxr")] |
| use webxr_api::{Finger, Hand}; |
| use xml5ever::interface::TreeSink as XmlTreeSink; |
| use xml5ever::tokenizer::XmlTokenizer; |
| use xml5ever::tree_builder::{Tracer as XmlTracer, XmlTreeBuilder}; |
| |
| use crate::JSTraceable; |
| use crate::error::Error; |
| use crate::reflector::Reflector; |
| use crate::str::{DOMString, USVString}; |
| |
| /// Trace the `JSObject` held by `reflector`. |
| /// |
| /// # Safety |
| /// tracer must point to a valid, non-null JS tracer. |
| #[cfg_attr(crown, allow(crown::unrooted_must_root))] |
| pub unsafe fn trace_reflector(tracer: *mut JSTracer, description: &str, reflector: &Reflector) { |
| trace!("tracing reflector {}", description); |
| unsafe { trace_object(tracer, description, reflector.rootable()) } |
| } |
| |
| /// Trace a `JSObject`. |
| /// |
| /// # Safety |
| /// tracer must point to a valid, non-null JS tracer. |
| pub(crate) unsafe fn trace_object( |
| tracer: *mut JSTracer, |
| description: &str, |
| obj: &Heap<*mut JSObject>, |
| ) { |
| unsafe { |
| trace!("tracing {}", description); |
| CallObjectTracer( |
| tracer, |
| obj.ptr.get() as *mut _, |
| GCTraceKindToAscii(TraceKind::Object), |
| ); |
| } |
| } |
| |
| /// For use on non-jsmanaged types |
| /// Use #[derive(JSTraceable)] on JS managed types |
| macro_rules! unsafe_no_jsmanaged_fields( |
| ($($ty:ty),+) => ( |
| $( |
| #[allow(unsafe_code)] |
| unsafe impl crate::JSTraceable for $ty { |
| #[inline] |
| unsafe fn trace(&self, _: *mut ::js::jsapi::JSTracer) { |
| // Do nothing |
| } |
| } |
| )+ |
| ); |
| ); |
| |
| unsafe_no_jsmanaged_fields!(DOMString); |
| unsafe_no_jsmanaged_fields!(USVString); |
| unsafe_no_jsmanaged_fields!(Error); |
| |
| /// A trait to allow tracing only DOM sub-objects. |
| /// |
| /// # Safety |
| /// |
| /// This trait is unsafe; if it is implemented incorrectly, the GC may end up collecting objects |
| /// that are still reachable. |
| pub unsafe trait CustomTraceable { |
| /// Trace `self`. |
| /// |
| /// # Safety |
| /// |
| /// The `JSTracer` argument must point to a valid `JSTracer` in memory. In addition, |
| /// implementors of this method must ensure that all active objects are properly traced |
| /// or else the garbage collector may end up collecting objects that are still reachable. |
| unsafe fn trace(&self, trc: *mut JSTracer); |
| } |
| |
| unsafe impl<T: CustomTraceable> CustomTraceable for Box<T> { |
| #[inline] |
| unsafe fn trace(&self, trc: *mut JSTracer) { |
| unsafe { (**self).trace(trc) }; |
| } |
| } |
| |
| unsafe impl<T: JSTraceable> CustomTraceable for OnceCell<T> { |
| unsafe fn trace(&self, tracer: *mut JSTracer) { |
| if let Some(value) = self.get() { |
| unsafe { value.trace(tracer) } |
| } |
| } |
| } |
| |
| unsafe impl<T> CustomTraceable for Sender<T> { |
| unsafe fn trace(&self, _: *mut JSTracer) {} |
| } |
| |
| unsafe impl<T: JSTraceable> CustomTraceable for ServoArc<T> { |
| unsafe fn trace(&self, trc: *mut JSTracer) { |
| unsafe { (**self).trace(trc) } |
| } |
| } |
| |
| unsafe impl<T: JSTraceable> CustomTraceable for RwLock<T> { |
| unsafe fn trace(&self, trc: *mut JSTracer) { |
| unsafe { self.read().trace(trc) } |
| } |
| } |
| |
| unsafe impl<T: JSTraceable + Eq + Hash> CustomTraceable for indexmap::IndexSet<T> { |
| #[inline] |
| unsafe fn trace(&self, trc: *mut JSTracer) { |
| for e in self.iter() { |
| unsafe { e.trace(trc) }; |
| } |
| } |
| } |
| |
| // XXXManishearth Check if the following three are optimized to no-ops |
| // if e.trace() is a no-op (e.g it is an unsafe_no_jsmanaged_fields type) |
| unsafe impl<T: JSTraceable + 'static> CustomTraceable for SmallVec<[T; 1]> { |
| #[inline] |
| unsafe fn trace(&self, trc: *mut JSTracer) { |
| for e in self.iter() { |
| unsafe { e.trace(trc) }; |
| } |
| } |
| } |
| |
| unsafe impl<K, V, S> CustomTraceable for IndexMap<K, V, S> |
| where |
| K: Hash + Eq + JSTraceable, |
| V: JSTraceable, |
| S: BuildHasher, |
| { |
| #[inline] |
| unsafe fn trace(&self, trc: *mut JSTracer) { |
| for (k, v) in self { |
| unsafe { k.trace(trc) }; |
| unsafe { v.trace(trc) }; |
| } |
| } |
| } |
| |
| unsafe impl<S> CustomTraceable for DocumentStylesheetSet<S> |
| where |
| S: JSTraceable + ::style::stylesheets::StylesheetInDocument + PartialEq + 'static, |
| { |
| unsafe fn trace(&self, tracer: *mut JSTracer) { |
| for (s, _origin) in self.iter() { |
| unsafe { s.trace(tracer) }; |
| } |
| } |
| } |
| |
| unsafe impl<S> CustomTraceable for AuthorStylesheetSet<S> |
| where |
| S: JSTraceable + ::style::stylesheets::StylesheetInDocument + PartialEq + 'static, |
| { |
| unsafe fn trace(&self, tracer: *mut JSTracer) { |
| for s in self.iter() { |
| unsafe { s.trace(tracer) }; |
| } |
| } |
| } |
| |
| unsafe impl<S> CustomTraceable for AuthorStyles<S> |
| where |
| S: JSTraceable + ::style::stylesheets::StylesheetInDocument + PartialEq + 'static, |
| { |
| unsafe fn trace(&self, tracer: *mut JSTracer) { |
| unsafe { self.stylesheets.trace(tracer) }; |
| } |
| } |
| |
| unsafe impl<Sink> CustomTraceable for LossyDecoder<Sink> |
| where |
| Sink: JSTraceable + TendrilSink<UTF8>, |
| { |
| unsafe fn trace(&self, tracer: *mut JSTracer) { |
| unsafe { self.inner_sink().trace(tracer) }; |
| } |
| } |
| |
| #[cfg(feature = "webxr")] |
| unsafe impl<J> CustomTraceable for Hand<J> |
| where |
| J: JSTraceable, |
| { |
| #[inline] |
| unsafe fn trace(&self, trc: *mut JSTracer) { |
| // exhaustive match so we don't miss new fields |
| let Hand { |
| ref wrist, |
| ref thumb_metacarpal, |
| ref thumb_phalanx_proximal, |
| ref thumb_phalanx_distal, |
| ref thumb_phalanx_tip, |
| ref index, |
| ref middle, |
| ref ring, |
| ref little, |
| } = *self; |
| unsafe { |
| wrist.trace(trc); |
| thumb_metacarpal.trace(trc); |
| thumb_phalanx_proximal.trace(trc); |
| thumb_phalanx_distal.trace(trc); |
| thumb_phalanx_tip.trace(trc); |
| index.trace(trc); |
| middle.trace(trc); |
| ring.trace(trc); |
| little.trace(trc); |
| } |
| } |
| } |
| |
| #[cfg(feature = "webxr")] |
| unsafe impl<J> CustomTraceable for Finger<J> |
| where |
| J: JSTraceable, |
| { |
| #[inline] |
| unsafe fn trace(&self, trc: *mut JSTracer) { |
| // exhaustive match so we don't miss new fields |
| let Finger { |
| ref metacarpal, |
| ref phalanx_proximal, |
| ref phalanx_intermediate, |
| ref phalanx_distal, |
| ref phalanx_tip, |
| } = *self; |
| unsafe { |
| metacarpal.trace(trc); |
| phalanx_proximal.trace(trc); |
| phalanx_intermediate.trace(trc); |
| phalanx_distal.trace(trc); |
| phalanx_tip.trace(trc); |
| } |
| } |
| } |
| |
| unsafe impl<Handle: JSTraceable + Clone, Sink: TreeSink<Handle = Handle> + JSTraceable> |
| CustomTraceable for TreeBuilder<Handle, Sink> |
| { |
| unsafe fn trace(&self, trc: *mut JSTracer) { |
| struct Tracer<Handle>(*mut JSTracer, PhantomData<Handle>); |
| let tracer = Tracer::<Handle>(trc, PhantomData); |
| |
| impl<Handle: JSTraceable> HtmlTracer for Tracer<Handle> { |
| type Handle = Handle; |
| #[cfg_attr(crown, allow(crown::unrooted_must_root))] |
| fn trace_handle(&self, node: &Handle) { |
| unsafe { |
| node.trace(self.0); |
| } |
| } |
| } |
| |
| self.trace_handles(&tracer); |
| unsafe { self.sink.trace(trc) }; |
| } |
| } |
| |
| #[allow(unsafe_code)] |
| unsafe impl<Handle: JSTraceable + Clone, Sink: TokenSink<Handle = Handle> + CustomTraceable> |
| CustomTraceable for Tokenizer<Sink> |
| { |
| unsafe fn trace(&self, trc: *mut JSTracer) { |
| unsafe { self.sink.trace(trc) }; |
| } |
| } |
| |
| #[allow(unsafe_code)] |
| unsafe impl<Handle: JSTraceable + Clone, Sink: JSTraceable + XmlTreeSink<Handle = Handle>> |
| CustomTraceable for XmlTokenizer<XmlTreeBuilder<Handle, Sink>> |
| { |
| unsafe fn trace(&self, trc: *mut JSTracer) { |
| struct Tracer<Handle>(*mut JSTracer, PhantomData<Handle>); |
| let tracer = Tracer(trc, PhantomData); |
| |
| impl<Handle: JSTraceable> XmlTracer for Tracer<Handle> { |
| type Handle = Handle; |
| #[cfg_attr(crown, allow(crown::unrooted_must_root))] |
| fn trace_handle(&self, node: &Handle) { |
| unsafe { |
| node.trace(self.0); |
| } |
| } |
| } |
| |
| let tree_builder = &self.sink; |
| tree_builder.trace_handles(&tracer); |
| unsafe { tree_builder.sink.trace(trc) }; |
| } |
| } |
| |
| /// Roots any JSTraceable thing |
| /// |
| /// If you have a valid DomObject, use DomRoot. |
| /// If you have GC things like *mut JSObject or JSVal, use rooted!. |
| /// If you have an arbitrary number of DomObjects to root, use rooted_vec!. |
| /// If you know what you're doing, use this. |
| #[cfg_attr(crown, crown::unrooted_must_root_lint::allow_unrooted_interior)] |
| pub struct RootedTraceableBox<T: JSTraceable + 'static>(js::gc::RootedTraceableBox<T>); |
| |
| unsafe impl<T: JSTraceable + 'static> JSTraceable for RootedTraceableBox<T> { |
| unsafe fn trace(&self, tracer: *mut JSTracer) { |
| unsafe { self.0.trace(tracer) }; |
| } |
| } |
| |
| impl<T: JSTraceable + 'static> RootedTraceableBox<T> { |
| /// DomRoot a JSTraceable thing for the life of this RootedTraceableBox |
| pub fn new(traceable: T) -> RootedTraceableBox<T> { |
| Self(js::gc::RootedTraceableBox::new(traceable)) |
| } |
| |
| /// Consumes a boxed JSTraceable and roots it for the life of this RootedTraceableBox. |
| pub fn from_box(boxed_traceable: Box<T>) -> RootedTraceableBox<T> { |
| Self(js::gc::RootedTraceableBox::from_box(boxed_traceable)) |
| } |
| } |
| |
| impl<T> RootedTraceableBox<Heap<T>> |
| where |
| Heap<T>: JSTraceable + 'static, |
| T: GCMethods + Copy, |
| { |
| pub fn handle(&self) -> Handle<'_, T> { |
| self.0.handle() |
| } |
| } |
| |
| impl<T: JSTraceable + MallocSizeOf> MallocSizeOf for RootedTraceableBox<T> { |
| fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { |
| // Briefly resurrect the real Box value so we can rely on the existing calculations. |
| // Then immediately forget about it again to avoid dropping the box. |
| let inner = unsafe { Box::from_raw(self.0.ptr()) }; |
| let size = inner.size_of(ops); |
| mem::forget(inner); |
| size |
| } |
| } |
| |
| impl<T: JSTraceable + Default> Default for RootedTraceableBox<T> { |
| fn default() -> RootedTraceableBox<T> { |
| RootedTraceableBox::new(T::default()) |
| } |
| } |
| |
| impl<T: JSTraceable> Deref for RootedTraceableBox<T> { |
| type Target = T; |
| fn deref(&self) -> &T { |
| self.0.deref() |
| } |
| } |
| |
| impl<T: JSTraceable> DerefMut for RootedTraceableBox<T> { |
| fn deref_mut(&mut self) -> &mut T { |
| self.0.deref_mut() |
| } |
| } |
| |
| /// Wrapper type for nop traceble |
| /// |
| /// SAFETY: Inner type must not impl JSTraceable |
| #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] |
| #[cfg_attr(crown, crown::trace_in_no_trace_lint::must_not_have_traceable)] |
| pub(crate) struct NoTrace<T>(pub(crate) T); |
| |
| impl<T: Display> Display for NoTrace<T> { |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| self.0.fmt(f) |
| } |
| } |
| |
| impl<T> From<T> for NoTrace<T> { |
| fn from(item: T) -> Self { |
| Self(item) |
| } |
| } |
| |
| #[allow(unsafe_code)] |
| unsafe impl<T> JSTraceable for NoTrace<T> { |
| #[inline] |
| unsafe fn trace(&self, _: *mut ::js::jsapi::JSTracer) {} |
| } |
| |
| impl<T: MallocSizeOf> MallocSizeOf for NoTrace<T> { |
| fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { |
| self.0.size_of(ops) |
| } |
| } |