mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
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>.
This commit is contained in:
parent
7574d26476
commit
dee38475b8
2 changed files with 108 additions and 60 deletions
|
@ -72,7 +72,7 @@ impl Reflector {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A trait to provide access to the `Reflector` for a DOM object.
|
/// A trait to provide access to the `Reflector` for a DOM object.
|
||||||
pub trait DomObject {
|
pub trait DomObject: 'static {
|
||||||
/// Returns the receiver's reflector.
|
/// Returns the receiver's reflector.
|
||||||
fn reflector(&self) -> &Reflector;
|
fn reflector(&self) -> &Reflector;
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
//!
|
//!
|
||||||
//! Here is a brief overview of the important types:
|
//! 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.
|
//! - `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
|
//! - `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.
|
//! the GC when encountered as a field of a Rust structure.
|
||||||
|
@ -47,21 +48,89 @@ use std::ptr;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use style::thread_state;
|
use style::thread_state;
|
||||||
|
|
||||||
/// A rooted reference to a DOM object.
|
/// A rooted value.
|
||||||
///
|
|
||||||
/// 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_must_root)]
|
||||||
#[allow_unrooted_interior]
|
#[allow_unrooted_interior]
|
||||||
pub struct DomRoot<T: DomObject> {
|
pub struct Root<T: StableTraceObject> {
|
||||||
/// Reference to rooted value that must not outlive this container
|
/// The value to root.
|
||||||
ptr: Dom<T>,
|
value: T,
|
||||||
/// List that ensures correct dynamic root ordering
|
/// List that ensures correct dynamic root ordering
|
||||||
root_list: *const RootCollection,
|
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> {
|
impl<T: Castable> DomRoot<T> {
|
||||||
/// Cast a DOM object root upwards to one of the interfaces it derives from.
|
/// Cast a DOM object root upwards to one of the interfaces it derives from.
|
||||||
pub fn upcast<U>(root: DomRoot<T>) -> DomRoot<U>
|
pub fn upcast<U>(root: DomRoot<T>) -> DomRoot<U>
|
||||||
|
@ -84,75 +153,55 @@ impl<T: Castable> DomRoot<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: DomObject> 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
|
/// Generate a new root from a reference
|
||||||
pub fn from_ref(unrooted: &T) -> DomRoot<T> {
|
pub fn from_ref(unrooted: &T) -> DomRoot<T> {
|
||||||
unsafe { DomRoot::new(Dom::from_ref(unrooted)) }
|
unsafe { DomRoot::new(Dom::from_ref(unrooted)) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: DomObject> Deref for DomRoot<T> {
|
impl<T> HeapSizeOf for DomRoot<T>
|
||||||
type Target = T;
|
where
|
||||||
fn deref(&self) -> &T {
|
T: DomObject + HeapSizeOf,
|
||||||
debug_assert!(thread_state::get().is_script());
|
{
|
||||||
&self.ptr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: DomObject + HeapSizeOf> HeapSizeOf for DomRoot<T> {
|
|
||||||
fn heap_size_of_children(&self) -> usize {
|
fn heap_size_of_children(&self) -> usize {
|
||||||
(**self).heap_size_of_children()
|
(**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 {
|
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> {
|
fn clone(&self) -> DomRoot<T> {
|
||||||
DomRoot::from_ref(&*self)
|
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) {
|
unsafe fn trace(&self, _: *mut JSTracer) {
|
||||||
// Already traced.
|
// 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.
|
/// A rooting mechanism for reflectors on the stack.
|
||||||
/// LIFO is not required.
|
/// LIFO is not required.
|
||||||
///
|
///
|
||||||
/// See also [*Exact Stack Rooting - Storing a GCPointer on the CStack*]
|
/// 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).
|
/// (https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/Internals/GC/Exact_Stack_Rooting).
|
||||||
pub struct RootCollection {
|
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));
|
thread_local!(static STACK_ROOTS: Cell<Option<*const RootCollection>> = Cell::new(None));
|
||||||
|
@ -183,21 +232,17 @@ impl RootCollection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Start tracking a stack-based root
|
/// Starts tracking a trace object.
|
||||||
unsafe fn root(&self, untracked_reflector: *const Reflector) {
|
unsafe fn root(&self, object: *const JSTraceable) {
|
||||||
debug_assert!(thread_state::get().is_script());
|
debug_assert!(thread_state::get().is_script());
|
||||||
let roots = &mut *self.roots.get();
|
(*self.roots.get()).push(object);
|
||||||
roots.push(untracked_reflector);
|
|
||||||
assert!(!(*untracked_reflector).get_jsobject().is_null())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stop tracking a stack-based reflector, asserting if it isn't found.
|
/// Stops tracking a trace object, asserting if it isn't found.
|
||||||
unsafe fn unroot(&self, tracked_reflector: *const Reflector) {
|
unsafe fn unroot(&self, object: *const JSTraceable) {
|
||||||
assert!(!tracked_reflector.is_null());
|
|
||||||
assert!(!(*tracked_reflector).get_jsobject().is_null());
|
|
||||||
debug_assert!(thread_state::get().is_script());
|
debug_assert!(thread_state::get().is_script());
|
||||||
let roots = &mut *self.roots.get();
|
let roots = &mut *self.roots.get();
|
||||||
match roots.iter().rposition(|r| *r == tracked_reflector) {
|
match roots.iter().rposition(|r| *r == object) {
|
||||||
Some(idx) => {
|
Some(idx) => {
|
||||||
roots.remove(idx);
|
roots.remove(idx);
|
||||||
},
|
},
|
||||||
|
@ -212,7 +257,7 @@ pub unsafe fn trace_roots(tracer: *mut JSTracer) {
|
||||||
STACK_ROOTS.with(|ref collection| {
|
STACK_ROOTS.with(|ref collection| {
|
||||||
let collection = &*(*collection.get().unwrap()).roots.get();
|
let collection = &*(*collection.get().unwrap()).roots.get();
|
||||||
for root in collection {
|
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>>,
|
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
|
/// Retrieve a copy of the current inner value. If it is `None`, it is
|
||||||
/// initialized with the result of `cb` first.
|
/// initialized with the result of `cb` first.
|
||||||
#[allow(unrooted_must_root)]
|
#[allow(unrooted_must_root)]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue