diff --git a/components/script/dom/bindings/root.rs b/components/script/dom/bindings/root.rs index 251b17c3054..6f1dbcc2735 100644 --- a/components/script/dom/bindings/root.rs +++ b/components/script/dom/bindings/root.rs @@ -47,6 +47,211 @@ 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 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 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>, +} + +/// 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); + } + }); +} + +/// 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 +293,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 +658,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;