diff options
author | Anthony Ramine <n.oxyde@gmail.com> | 2017-09-28 14:51:23 +0200 |
---|---|---|
committer | Anthony Ramine <n.oxyde@gmail.com> | 2017-09-28 15:42:43 +0200 |
commit | dee38475b8da91ad2cea9f40a425fcab5e3ab70b (patch) | |
tree | e46416f270cc952b0e7196d3b1a599697779ccfa /components/script/dom/bindings/root.rs | |
parent | 7574d2647699e211638379f8f89760c57a1d6cc8 (diff) | |
download | servo-dee38475b8da91ad2cea9f40a425fcab5e3ab70b.tar.gz servo-dee38475b8da91ad2cea9f40a425fcab5e3ab70b.zip |
Make DomRoot<T> a type alias of a more general Root<T> type
This Root<T> relies on the concept of a stable trace object, which doesn't
move for the whole lifetime of the T value. For safety reasons, T must be 'static.
The only implementor of StableTraceObject for now is Dom<T>.
Diffstat (limited to 'components/script/dom/bindings/root.rs')
-rw-r--r-- | components/script/dom/bindings/root.rs | 166 |
1 files changed, 107 insertions, 59 deletions
diff --git a/components/script/dom/bindings/root.rs b/components/script/dom/bindings/root.rs index cbab8951184..5aaa806ceaf 100644 --- a/components/script/dom/bindings/root.rs +++ b/components/script/dom/bindings/root.rs @@ -11,6 +11,7 @@ //! //! Here is a brief overview of the important types: //! +//! - `Root<T>`: a stack-based rooted value. //! - `DomRoot<T>`: a stack-based reference to a rooted DOM object. //! - `Dom<T>`: a reference to a DOM object that can automatically be traced by //! the GC when encountered as a field of a Rust structure. @@ -47,21 +48,89 @@ 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. +/// A rooted value. #[allow(unrooted_must_root)] #[allow_unrooted_interior] -pub struct DomRoot<T: DomObject> { - /// Reference to rooted value that must not outlive this container - ptr: Dom<T>, +pub struct Root<T: StableTraceObject> { + /// The value to root. + value: T, /// List that ensures correct dynamic root ordering root_list: *const RootCollection, } +impl<T> Root<T> +where + T: StableTraceObject + 'static, +{ + /// Create a new stack-bounded root for the provided 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(value: T) -> Self { + debug_assert!(thread_state::get().is_script()); + STACK_ROOTS.with(|ref root_list| { + let root_list = &*root_list.get().unwrap(); + root_list.root(value.stable_trace_object()); + Root { value, root_list } + }) + } +} + +/// Represents values that can be rooted through a stable address that will +/// not change for their whole lifetime. +pub unsafe trait StableTraceObject { + /// Returns a stable trace object which address won't change for the whole + /// lifetime of the value. + fn stable_trace_object(&self) -> *const JSTraceable; +} + +unsafe impl<T> StableTraceObject for Dom<T> +where + T: DomObject, +{ + fn stable_trace_object<'a>(&'a self) -> *const JSTraceable { + // The JSTraceable impl for Reflector doesn't actually do anything, + // so we need this shenanigan to actually trace the reflector of the + // T pointer in Dom<T>. + #[allow(unrooted_must_root)] + struct ReflectorStackRoot(Reflector); + unsafe impl JSTraceable for ReflectorStackRoot { + unsafe fn trace(&self, tracer: *mut JSTracer) { + trace_reflector(tracer, "on stack", &self.0); + } + } + unsafe { + &*(self.reflector() as *const Reflector as *const ReflectorStackRoot) + } + } +} + +impl<T> Deref for Root<T> +where + T: Deref + StableTraceObject, +{ + type Target = <T as Deref>::Target; + + fn deref(&self) -> &Self::Target { + debug_assert!(thread_state::get().is_script()); + &self.value + } +} + +impl<T> Drop for Root<T> +where + T: StableTraceObject, +{ + fn drop(&mut self) { + unsafe { + (*self.root_list).unroot(self.value.stable_trace_object()); + } + } +} + +/// A rooted reference to a DOM object. +pub type DomRoot<T> = Root<Dom<T>>; + impl<T: Castable> DomRoot<T> { /// Cast a DOM object root upwards to one of the interfaces it derives from. pub fn upcast<U>(root: DomRoot<T>) -> DomRoot<U> @@ -84,75 +153,55 @@ impl<T: Castable> DomRoot<T> { } impl<T: DomObject> DomRoot<T> { - /// 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<T>) -> DomRoot<T> { - 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<T> { unsafe { DomRoot::new(Dom::from_ref(unrooted)) } } } -impl<T: DomObject> Deref for DomRoot<T> { - type Target = T; - fn deref(&self) -> &T { - debug_assert!(thread_state::get().is_script()); - &self.ptr - } -} - -impl<T: DomObject + HeapSizeOf> HeapSizeOf for DomRoot<T> { +impl<T> HeapSizeOf for DomRoot<T> +where + T: DomObject + HeapSizeOf, +{ fn heap_size_of_children(&self) -> usize { (**self).heap_size_of_children() } } -impl<T: DomObject> PartialEq for DomRoot<T> { +impl<T> PartialEq for DomRoot<T> +where + T: DomObject, +{ fn eq(&self, other: &Self) -> bool { - self.ptr == other.ptr + self.value == other.value } } -impl<T: DomObject> Clone for DomRoot<T> { +impl<T> Clone for DomRoot<T> +where + T: DomObject, +{ fn clone(&self) -> DomRoot<T> { DomRoot::from_ref(&*self) } } -unsafe impl<T: DomObject> JSTraceable for DomRoot<T> { +unsafe impl<T> JSTraceable for DomRoot<T> +where + T: DomObject, +{ unsafe fn trace(&self, _: *mut JSTracer) { // Already traced. } } -impl<T: DomObject> Drop for DomRoot<T> { - 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<Vec<*const Reflector>>, + roots: UnsafeCell<Vec<*const JSTraceable>>, } thread_local!(static STACK_ROOTS: Cell<Option<*const RootCollection>> = Cell::new(None)); @@ -183,21 +232,17 @@ impl RootCollection { } } - /// Start tracking a stack-based root - unsafe fn root(&self, untracked_reflector: *const Reflector) { + /// Starts tracking a trace object. + unsafe fn root(&self, object: *const JSTraceable) { debug_assert!(thread_state::get().is_script()); - let roots = &mut *self.roots.get(); - roots.push(untracked_reflector); - assert!(!(*untracked_reflector).get_jsobject().is_null()) + (*self.roots.get()).push(object); } - /// 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()); + /// Stops tracking a trace object, asserting if it isn't found. + unsafe fn unroot(&self, object: *const JSTraceable) { debug_assert!(thread_state::get().is_script()); let roots = &mut *self.roots.get(); - match roots.iter().rposition(|r| *r == tracked_reflector) { + match roots.iter().rposition(|r| *r == object) { Some(idx) => { roots.remove(idx); }, @@ -212,7 +257,7 @@ pub unsafe fn trace_roots(tracer: *mut JSTracer) { STACK_ROOTS.with(|ref collection| { let collection = &*(*collection.get().unwrap()).roots.get(); for root in collection { - trace_reflector(tracer, "on stack", &**root); + (**root).trace(tracer); } }); } @@ -610,7 +655,10 @@ pub struct DomOnceCell<T: DomObject> { ptr: OnceCell<Dom<T>>, } -impl<T: DomObject> DomOnceCell<T> { +impl<T> DomOnceCell<T> +where + T: DomObject +{ /// Retrieve a copy of the current inner value. If it is `None`, it is /// initialized with the result of `cb` first. #[allow(unrooted_must_root)] |