diff --git a/components/script/dom/bindings/root.rs b/components/script/dom/bindings/root.rs index 251b17c3054..cbab8951184 100644 --- a/components/script/dom/bindings/root.rs +++ b/components/script/dom/bindings/root.rs @@ -35,18 +35,231 @@ use js::jsapi::{JSObject, JSTracer, Heap}; use js::rust::GCMethods; use mitochondria::OnceCell; use script_layout_interface::TrustedNodeAddress; -use script_thread::STACK_ROOTS; -use std::cell::UnsafeCell; +use std::cell::{Cell, UnsafeCell}; use std::default::Default; use std::hash::{Hash, Hasher}; #[cfg(debug_assertions)] use std::intrinsics::type_name; +use std::marker::PhantomData; use std::mem; use std::ops::Deref; use std::ptr; use std::rc::Rc; use style::thread_state; +/// A rooted reference to a DOM object. +/// +/// The JS value is pinned for the duration of this object's lifetime; roots +/// are additive, so this object's destruction will not invalidate other roots +/// for the same JS value. `Root`s cannot outlive the associated +/// `RootCollection` object. +#[allow(unrooted_must_root)] +#[allow_unrooted_interior] +pub struct DomRoot { + /// Reference to rooted value that must not outlive this container + ptr: Dom, + /// List that ensures correct dynamic root ordering + root_list: *const RootCollection, +} + +impl DomRoot { + /// Cast a DOM object root upwards to one of the interfaces it derives from. + pub fn upcast(root: DomRoot) -> DomRoot + where U: Castable, + T: DerivedFrom + { + unsafe { mem::transmute(root) } + } + + /// Cast a DOM object root downwards to one of the interfaces it might implement. + pub fn downcast(root: DomRoot) -> Option> + where U: DerivedFrom + { + if root.is::() { + Some(unsafe { mem::transmute(root) }) + } else { + None + } + } +} + +impl DomRoot { + /// Create a new stack-bounded root for the provided JS-owned value. + /// It cannot outlive its associated `RootCollection`, and it gives + /// out references which cannot outlive this new `Root`. + #[allow(unrooted_must_root)] + unsafe fn new(unrooted: Dom) -> DomRoot { + debug_assert!(thread_state::get().is_script()); + STACK_ROOTS.with(|ref collection| { + let collection = collection.get().unwrap(); + (*collection).root(unrooted.reflector()); + DomRoot { + ptr: unrooted, + root_list: collection, + } + }) + } + + /// Generate a new root from a reference + pub fn from_ref(unrooted: &T) -> DomRoot { + unsafe { DomRoot::new(Dom::from_ref(unrooted)) } + } +} + +impl Deref for DomRoot { + type Target = T; + fn deref(&self) -> &T { + debug_assert!(thread_state::get().is_script()); + &self.ptr + } +} + +impl HeapSizeOf for DomRoot { + fn heap_size_of_children(&self) -> usize { + (**self).heap_size_of_children() + } +} + +impl PartialEq for DomRoot { + fn eq(&self, other: &Self) -> bool { + self.ptr == other.ptr + } +} + +impl Clone for DomRoot { + fn clone(&self) -> DomRoot { + DomRoot::from_ref(&*self) + } +} + +unsafe impl JSTraceable for DomRoot { + unsafe fn trace(&self, _: *mut JSTracer) { + // Already traced. + } +} + +impl Drop for DomRoot { + fn drop(&mut self) { + unsafe { + (*self.root_list).unroot(self.reflector()); + } + } +} + +/// A rooting mechanism for reflectors on the stack. +/// LIFO is not required. +/// +/// See also [*Exact Stack Rooting - Storing a GCPointer on the CStack*] +/// (https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/Internals/GC/Exact_Stack_Rooting). +pub struct RootCollection { + roots: UnsafeCell>, +} + +thread_local!(static STACK_ROOTS: Cell> = Cell::new(None)); + +pub struct ThreadLocalStackRoots<'a>(PhantomData<&'a u32>); + +impl<'a> ThreadLocalStackRoots<'a> { + pub fn new(roots: &'a RootCollection) -> Self { + STACK_ROOTS.with(|ref r| { + r.set(Some(roots)) + }); + ThreadLocalStackRoots(PhantomData) + } +} + +impl<'a> Drop for ThreadLocalStackRoots<'a> { + fn drop(&mut self) { + STACK_ROOTS.with(|ref r| r.set(None)); + } +} + +impl RootCollection { + /// Create an empty collection of roots + pub fn new() -> RootCollection { + debug_assert!(thread_state::get().is_script()); + RootCollection { + roots: UnsafeCell::new(vec![]), + } + } + + /// Start tracking a stack-based root + unsafe fn root(&self, untracked_reflector: *const Reflector) { + debug_assert!(thread_state::get().is_script()); + let roots = &mut *self.roots.get(); + roots.push(untracked_reflector); + assert!(!(*untracked_reflector).get_jsobject().is_null()) + } + + /// Stop tracking a stack-based reflector, asserting if it isn't found. + unsafe fn unroot(&self, tracked_reflector: *const Reflector) { + assert!(!tracked_reflector.is_null()); + assert!(!(*tracked_reflector).get_jsobject().is_null()); + debug_assert!(thread_state::get().is_script()); + let roots = &mut *self.roots.get(); + match roots.iter().rposition(|r| *r == tracked_reflector) { + Some(idx) => { + roots.remove(idx); + }, + None => panic!("Can't remove a root that was never rooted!"), + } + } +} + +/// SM Callback that traces the rooted reflectors +pub unsafe fn trace_roots(tracer: *mut JSTracer) { + debug!("tracing stack roots"); + STACK_ROOTS.with(|ref collection| { + let collection = &*(*collection.get().unwrap()).roots.get(); + for root in collection { + trace_reflector(tracer, "on stack", &**root); + } + }); +} + +/// Get a reference out of a rooted value. +pub trait RootedReference<'root> { + /// The type of the reference. + type Ref: 'root; + /// Obtain a reference out of the rooted value. + fn r(&'root self) -> Self::Ref; +} + +impl<'root, T: DomObject + 'root> RootedReference<'root> for DomRoot { + type Ref = &'root T; + fn r(&'root self) -> &'root T { + self + } +} + +impl<'root, T: DomObject + 'root> RootedReference<'root> for Dom { + type Ref = &'root T; + fn r(&'root self) -> &'root T { + &self + } +} + +impl<'root, T: JSTraceable + DomObject + 'root> RootedReference<'root> for [Dom] { + type Ref = &'root [&'root T]; + fn r(&'root self) -> &'root [&'root T] { + unsafe { mem::transmute(self) } + } +} + +impl<'root, T: DomObject + 'root> RootedReference<'root> for Rc { + type Ref = &'root T; + fn r(&'root self) -> &'root T { + self + } +} + +impl<'root, T: RootedReference<'root> + 'root> RootedReference<'root> for Option { + type Ref = Option; + fn r(&'root self) -> Option { + self.as_ref().map(RootedReference::r) + } +} + /// A traced reference to a DOM object /// /// This type is critical to making garbage collection work with the DOM, @@ -88,13 +301,6 @@ impl Dom { } } -impl<'root, T: DomObject + 'root> RootedReference<'root> for Dom { - type Ref = &'root T; - fn r(&'root self) -> &'root T { - &self - } -} - impl Deref for Dom { type Target = T; @@ -460,204 +666,6 @@ impl LayoutDom { } } -/// Get a reference out of a rooted value. -pub trait RootedReference<'root> { - /// The type of the reference. - type Ref: 'root; - /// Obtain a reference out of the rooted value. - fn r(&'root self) -> Self::Ref; -} - -impl<'root, T: JSTraceable + DomObject + 'root> RootedReference<'root> for [Dom] { - type Ref = &'root [&'root T]; - fn r(&'root self) -> &'root [&'root T] { - unsafe { mem::transmute(self) } - } -} - -impl<'root, T: DomObject + 'root> RootedReference<'root> for Rc { - type Ref = &'root T; - fn r(&'root self) -> &'root T { - self - } -} - -impl<'root, T: RootedReference<'root> + 'root> RootedReference<'root> for Option { - type Ref = Option; - fn r(&'root self) -> Option { - self.as_ref().map(RootedReference::r) - } -} - -/// A rooting mechanism for reflectors on the stack. -/// LIFO is not required. -/// -/// See also [*Exact Stack Rooting - Storing a GCPointer on the CStack*] -/// (https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/Internals/GC/Exact_Stack_Rooting). -pub struct RootCollection { - roots: UnsafeCell>, -} - -/// A pointer to a RootCollection, for use in global variables. -pub struct RootCollectionPtr(pub *const RootCollection); - -impl Copy for RootCollectionPtr {} -impl Clone for RootCollectionPtr { - fn clone(&self) -> RootCollectionPtr { - *self - } -} - -impl RootCollection { - /// Create an empty collection of roots - pub fn new() -> RootCollection { - debug_assert!(thread_state::get().is_script()); - RootCollection { - roots: UnsafeCell::new(vec![]), - } - } - - /// Start tracking a stack-based root - unsafe fn root(&self, untracked_reflector: *const Reflector) { - debug_assert!(thread_state::get().is_script()); - let roots = &mut *self.roots.get(); - roots.push(untracked_reflector); - assert!(!(*untracked_reflector).get_jsobject().is_null()) - } - - /// Stop tracking a stack-based reflector, asserting if it isn't found. - unsafe fn unroot(&self, tracked_reflector: *const Reflector) { - assert!(!tracked_reflector.is_null()); - assert!(!(*tracked_reflector).get_jsobject().is_null()); - debug_assert!(thread_state::get().is_script()); - let roots = &mut *self.roots.get(); - match roots.iter().rposition(|r| *r == tracked_reflector) { - Some(idx) => { - roots.remove(idx); - }, - None => panic!("Can't remove a root that was never rooted!"), - } - } -} - -/// SM Callback that traces the rooted reflectors -pub unsafe fn trace_roots(tracer: *mut JSTracer) { - debug!("tracing stack roots"); - STACK_ROOTS.with(|ref collection| { - let RootCollectionPtr(collection) = collection.get().unwrap(); - let collection = &*(*collection).roots.get(); - for root in collection { - trace_reflector(tracer, "on stack", &**root); - } - }); -} - -/// A rooted reference to a DOM object. -/// -/// The JS value is pinned for the duration of this object's lifetime; roots -/// are additive, so this object's destruction will not invalidate other roots -/// for the same JS value. `Root`s cannot outlive the associated -/// `RootCollection` object. -#[allow(unrooted_must_root)] -#[allow_unrooted_interior] -pub struct DomRoot { - /// Reference to rooted value that must not outlive this container - ptr: Dom, - /// List that ensures correct dynamic root ordering - root_list: *const RootCollection, -} - -impl DomRoot { - /// Cast a DOM object root upwards to one of the interfaces it derives from. - pub fn upcast(root: DomRoot) -> DomRoot - where U: Castable, - T: DerivedFrom - { - unsafe { mem::transmute(root) } - } - - /// Cast a DOM object root downwards to one of the interfaces it might implement. - pub fn downcast(root: DomRoot) -> Option> - where U: DerivedFrom - { - if root.is::() { - Some(unsafe { mem::transmute(root) }) - } else { - None - } - } -} - -impl DomRoot { - /// Create a new stack-bounded root for the provided JS-owned value. - /// It cannot outlive its associated `RootCollection`, and it gives - /// out references which cannot outlive this new `Root`. - #[allow(unrooted_must_root)] - unsafe fn new(unrooted: Dom) -> DomRoot { - debug_assert!(thread_state::get().is_script()); - STACK_ROOTS.with(|ref collection| { - let RootCollectionPtr(collection) = collection.get().unwrap(); - (*collection).root(unrooted.reflector()); - DomRoot { - ptr: unrooted, - root_list: collection, - } - }) - } - - /// Generate a new root from a reference - pub fn from_ref(unrooted: &T) -> DomRoot { - unsafe { DomRoot::new(Dom::from_ref(unrooted)) } - } -} - -impl<'root, T: DomObject + 'root> RootedReference<'root> for DomRoot { - type Ref = &'root T; - fn r(&'root self) -> &'root T { - self - } -} - -impl Deref for DomRoot { - type Target = T; - fn deref(&self) -> &T { - debug_assert!(thread_state::get().is_script()); - &self.ptr - } -} - -impl HeapSizeOf for DomRoot { - fn heap_size_of_children(&self) -> usize { - (**self).heap_size_of_children() - } -} - -impl PartialEq for DomRoot { - fn eq(&self, other: &Self) -> bool { - self.ptr == other.ptr - } -} - -impl Clone for DomRoot { - fn clone(&self) -> DomRoot { - DomRoot::from_ref(&*self) - } -} - -impl Drop for DomRoot { - fn drop(&mut self) { - unsafe { - (*self.root_list).unroot(self.reflector()); - } - } -} - -unsafe impl JSTraceable for DomRoot { - unsafe fn trace(&self, _: *mut JSTracer) { - // Already traced. - } -} - /// Helper trait for safer manipulations of Option> values. pub trait OptionalHeapSetter { type Value; diff --git a/components/script/dom/dedicatedworkerglobalscope.rs b/components/script/dom/dedicatedworkerglobalscope.rs index 118f2d32887..dc816aca637 100644 --- a/components/script/dom/dedicatedworkerglobalscope.rs +++ b/components/script/dom/dedicatedworkerglobalscope.rs @@ -12,7 +12,7 @@ use dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding::Dedicat use dom::bindings::error::{ErrorInfo, ErrorResult}; use dom::bindings::inheritance::Castable; use dom::bindings::reflector::DomObject; -use dom::bindings::root::{DomRoot, RootCollection}; +use dom::bindings::root::{DomRoot, RootCollection, ThreadLocalStackRoots}; use dom::bindings::str::DOMString; use dom::bindings::structuredclone::StructuredCloneData; use dom::errorevent::ErrorEvent; @@ -32,7 +32,7 @@ use js::rust::Runtime; use msg::constellation_msg::TopLevelBrowsingContextId; use net_traits::{IpcSend, load_whole_resource}; use net_traits::request::{CredentialsMode, Destination, RequestInit, Type as RequestType}; -use script_runtime::{CommonScriptMsg, ScriptChan, ScriptPort, StackRootTLS, new_rt_and_cx}; +use script_runtime::{CommonScriptMsg, ScriptChan, ScriptPort, new_rt_and_cx}; use script_runtime::ScriptThreadEventCategory::WorkerEvent; use script_traits::{TimerEvent, TimerSource, WorkerGlobalScopeInit, WorkerScriptLoadOrigin}; use servo_rand::random; @@ -172,7 +172,7 @@ impl DedicatedWorkerGlobalScope { } let roots = RootCollection::new(); - let _stack_roots_tls = StackRootTLS::new(&roots); + let _stack_roots = ThreadLocalStackRoots::new(&roots); let WorkerScriptLoadOrigin { referrer_url, referrer_policy, pipeline_id } = worker_load_origin; diff --git a/components/script/dom/serviceworkerglobalscope.rs b/components/script/dom/serviceworkerglobalscope.rs index 5e4ad439c90..3183598e229 100644 --- a/components/script/dom/serviceworkerglobalscope.rs +++ b/components/script/dom/serviceworkerglobalscope.rs @@ -9,7 +9,7 @@ use dom::bindings::codegen::Bindings::ServiceWorkerGlobalScopeBinding; use dom::bindings::codegen::Bindings::ServiceWorkerGlobalScopeBinding::ServiceWorkerGlobalScopeMethods; use dom::bindings::inheritance::Castable; use dom::bindings::reflector::DomObject; -use dom::bindings::root::{DomRoot, RootCollection}; +use dom::bindings::root::{DomRoot, RootCollection, ThreadLocalStackRoots}; use dom::bindings::str::DOMString; use dom::event::Event; use dom::eventtarget::EventTarget; @@ -25,7 +25,7 @@ use js::jsval::UndefinedValue; use js::rust::Runtime; use net_traits::{load_whole_resource, IpcSend, CustomResponseMediator}; use net_traits::request::{CredentialsMode, Destination, RequestInit, Type as RequestType}; -use script_runtime::{CommonScriptMsg, ScriptChan, StackRootTLS, new_rt_and_cx}; +use script_runtime::{CommonScriptMsg, ScriptChan, new_rt_and_cx}; use script_traits::{TimerEvent, WorkerGlobalScopeInit, ScopeThings, ServiceWorkerMsg, WorkerScriptLoadOrigin}; use servo_config::prefs::PREFS; use servo_rand::random; @@ -154,7 +154,7 @@ impl ServiceWorkerGlobalScope { thread::Builder::new().name(format!("ServiceWorker for {}", serialized_worker_url)).spawn(move || { thread_state::initialize(SCRIPT | IN_WORKER); let roots = RootCollection::new(); - let _stack_roots_tls = StackRootTLS::new(&roots); + let _stack_roots = ThreadLocalStackRoots::new(&roots); let WorkerScriptLoadOrigin { referrer_url, referrer_policy, pipeline_id } = worker_load_origin; diff --git a/components/script/dom/worklet.rs b/components/script/dom/worklet.rs index 4f13fac1b1a..5b822ab9d4f 100644 --- a/components/script/dom/worklet.rs +++ b/components/script/dom/worklet.rs @@ -20,7 +20,7 @@ use dom::bindings::inheritance::Castable; use dom::bindings::refcounted::TrustedPromise; use dom::bindings::reflector::Reflector; use dom::bindings::reflector::reflect_dom_object; -use dom::bindings::root::{Dom, DomRoot, RootCollection}; +use dom::bindings::root::{Dom, DomRoot, RootCollection, ThreadLocalStackRoots}; use dom::bindings::str::USVString; use dom::bindings::trace::JSTraceable; use dom::bindings::trace::RootedTraceableBox; @@ -47,7 +47,6 @@ use net_traits::request::RequestMode; use net_traits::request::Type as RequestType; use script_runtime::CommonScriptMsg; use script_runtime::ScriptThreadEventCategory; -use script_runtime::StackRootTLS; use script_runtime::new_rt_and_cx; use script_thread::{MainThreadScriptMsg, ScriptThread}; use servo_rand; @@ -429,7 +428,7 @@ impl WorkletThread { debug!("Initializing worklet thread."); thread_state::initialize(thread_state::SCRIPT | thread_state::IN_WORKER); let roots = RootCollection::new(); - let _stack_roots_tls = StackRootTLS::new(&roots); + let _stack_roots = ThreadLocalStackRoots::new(&roots); let mut thread = RootedTraceableBox::new(WorkletThread { role: role, control_receiver: control_receiver, diff --git a/components/script/script_runtime.rs b/components/script/script_runtime.rs index e437e1a6001..7985f562cbe 100644 --- a/components/script/script_runtime.rs +++ b/components/script/script_runtime.rs @@ -7,7 +7,7 @@ use dom::bindings::codegen::Bindings::PromiseBinding::PromiseJobCallback; use dom::bindings::refcounted::{LiveDOMReferences, trace_refcounted_objects}; -use dom::bindings::root::{RootCollection, RootCollectionPtr, trace_roots}; +use dom::bindings::root::trace_roots; use dom::bindings::settings_stack; use dom::bindings::trace::{JSTraceable, trace_traceables}; use dom::bindings::utils::DOM_CALLBACKS; @@ -23,13 +23,12 @@ use js::panic::wrap_panic; use js::rust::Runtime; use microtask::{EnqueuedPromiseCallback, Microtask}; use profile_traits::mem::{Report, ReportKind, ReportsChan}; -use script_thread::{STACK_ROOTS, trace_thread}; +use script_thread::trace_thread; use servo_config::opts; use servo_config::prefs::PREFS; use std::cell::Cell; use std::fmt; use std::io::{Write, stdout}; -use std::marker::PhantomData; use std::os; use std::os::raw::c_void; use std::panic::AssertUnwindSafe; @@ -102,23 +101,6 @@ pub trait ScriptPort { fn recv(&self) -> Result; } -pub struct StackRootTLS<'a>(PhantomData<&'a u32>); - -impl<'a> StackRootTLS<'a> { - pub fn new(roots: &'a RootCollection) -> StackRootTLS<'a> { - STACK_ROOTS.with(|ref r| { - r.set(Some(RootCollectionPtr(roots as *const _))) - }); - StackRootTLS(PhantomData) - } -} - -impl<'a> Drop for StackRootTLS<'a> { - fn drop(&mut self) { - STACK_ROOTS.with(|ref r| r.set(None)); - } -} - /// SM callback for promise job resolution. Adds a promise callback to the current /// global's microtask queue. #[allow(unsafe_code)] diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 4b9984845e8..1891e84784d 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -35,7 +35,7 @@ use dom::bindings::inheritance::Castable; use dom::bindings::num::Finite; use dom::bindings::reflector::DomObject; use dom::bindings::root::{Dom, DomRoot, MutNullableDom, RootCollection}; -use dom::bindings::root::{RootCollectionPtr, RootedReference}; +use dom::bindings::root::{RootedReference, ThreadLocalStackRoots}; use dom::bindings::str::DOMString; use dom::bindings::structuredclone::StructuredCloneData; use dom::bindings::trace::JSTraceable; @@ -86,7 +86,7 @@ use profile_traits::mem::{self, OpaqueSender, Report, ReportKind, ReportsChan}; use profile_traits::time::{self, ProfilerCategory, profile}; use script_layout_interface::message::{self, Msg, NewLayoutThreadInfo, ReflowQueryType}; use script_runtime::{CommonScriptMsg, ScriptChan, ScriptThreadEventCategory}; -use script_runtime::{ScriptPort, StackRootTLS, get_reports, new_rt_and_cx}; +use script_runtime::{ScriptPort, get_reports, new_rt_and_cx}; use script_traits::{CompositorEvent, ConstellationControlMsg}; use script_traits::{DiscardBrowsingContext, DocumentActivity, EventResult}; use script_traits::{InitialScriptState, JsEvalResult, LayoutMsg, LoadData}; @@ -129,7 +129,6 @@ use webvr_traits::{WebVREvent, WebVRMsg}; pub type ImageCacheMsg = (PipelineId, PendingImageResponse); -thread_local!(pub static STACK_ROOTS: Cell> = Cell::new(None)); thread_local!(static SCRIPT_THREAD_ROOT: Cell> = Cell::new(None)); pub unsafe fn trace_thread(tr: *mut JSTracer) { @@ -545,7 +544,7 @@ impl ScriptThreadFactory for ScriptThread { PipelineNamespace::install(state.pipeline_namespace_id); TopLevelBrowsingContextId::install(state.top_level_browsing_context_id); let roots = RootCollection::new(); - let _stack_roots_tls = StackRootTLS::new(&roots); + let _stack_roots = ThreadLocalStackRoots::new(&roots); let id = state.id; let browsing_context_id = state.browsing_context_id; let top_level_browsing_context_id = state.top_level_browsing_context_id;