diff options
Diffstat (limited to 'src/components/script/dom/bindings/js.rs')
-rw-r--r-- | src/components/script/dom/bindings/js.rs | 207 |
1 files changed, 181 insertions, 26 deletions
diff --git a/src/components/script/dom/bindings/js.rs b/src/components/script/dom/bindings/js.rs index dac459f9c03..2386777cac9 100644 --- a/src/components/script/dom/bindings/js.rs +++ b/src/components/script/dom/bindings/js.rs @@ -10,8 +10,52 @@ use layout_interface::TrustedNodeAddress; use std::cast; use std::cell::{Cell, RefCell}; use std::ptr; -//use std::ops::{Deref, DerefMut}; +/// A type that represents a JS-owned value that may or may not be rooted. +/// Importantly, it requires rooting in order to interact with the value in any way. +/// Can be assigned into JS-owned member fields (ie. JS<T> types) safely via the +/// `JS<T>::assign` method or `OptionalAssignable::assign` (for Option<JS<T>> fields). +pub struct Unrooted<T> { + inner: JS<T> +} + +impl<T> Eq for Unrooted<T> { + fn eq(&self, other: &Unrooted<T>) -> bool { + self.inner == other.inner + } +} + +impl<T: Reflectable> Unrooted<T> { + /// Create a new Unrooted value from a JS-owned value. + pub fn new(inner: JS<T>) -> Unrooted<T> { + Unrooted { + inner: inner + } + } + + /// Create a new Unrooted value from a rooted value. + pub fn new_rooted<'a>(root: &JSRef<'a, T>) -> Unrooted<T> { + Unrooted { + inner: root.unrooted() + } + } + + /// Root this unrooted value. + pub fn root<'a, 'b>(self, collection: &'a RootCollection) -> Root<'a, 'b, T> { + collection.new_root(&self.inner) + } + + unsafe fn inner(&self) -> JS<T> { + self.inner.clone() + } + + //XXXjdm It would be lovely if this could be private. + pub unsafe fn transmute<To>(self) -> Unrooted<To> { + cast::transmute(self) + } +} + +/// A rooted, JS-owned value. Must only be used as a field in other JS-owned types. pub struct JS<T> { ptr: RefCell<*mut T> } @@ -32,12 +76,15 @@ impl <T> Clone for JS<T> { } impl<T: Reflectable> JS<T> { + /// Create a new JS-reflected DOM object; returns an Unrooted type because the new value + /// is not safe to use until it is rooted. pub fn new(obj: ~T, window: &JSRef<Window>, - wrap_fn: extern "Rust" fn(*JSContext, &JSRef<Window>, ~T) -> JS<T>) -> JS<T> { - wrap_fn(window.get().get_cx(), window, obj) + wrap_fn: extern "Rust" fn(*JSContext, &JSRef<Window>, ~T) -> JS<T>) -> Unrooted<T> { + Unrooted::new(wrap_fn(window.get().get_cx(), window, obj)) } + /// Create a new JS-owned value wrapped from a raw Rust pointer. pub unsafe fn from_raw(raw: *mut T) -> JS<T> { JS { ptr: RefCell::new(raw) @@ -45,6 +92,7 @@ impl<T: Reflectable> JS<T> { } + /// Create a new JS-owned value wrapped from an address known to be a Node pointer. pub unsafe fn from_trusted_node_address(inner: TrustedNodeAddress) -> JS<T> { let TrustedNodeAddress(addr) = inner; JS { @@ -52,7 +100,8 @@ impl<T: Reflectable> JS<T> { } } - pub fn root<'a>(&self, collection: &'a RootCollection) -> Root<'a, T> { + /// Root this JS-owned value to prevent its collection as garbage. + pub fn root<'a, 'b>(&self, collection: &'a RootCollection) -> Root<'a, 'b, T> { collection.new_root(self) } } @@ -67,7 +116,7 @@ impl<T: Reflectable> Reflectable for JS<T> { } } -impl<T> JS<T> { +impl<T: Reflectable> JS<T> { pub fn get<'a>(&'a self) -> &'a T { let borrowed = self.ptr.borrow(); unsafe { @@ -88,6 +137,13 @@ impl<T> JS<T> { pub unsafe fn unsafe_get(&self) -> *mut T { cast::transmute_copy(&self.ptr) } + + /// Store an unrooted value in this field. This is safe under the assumption that JS<T> + /// values are only used as fields in DOM types that are reachable in the GC graph, + /// so this unrooted value becomes transitively rooted for the lifetime of its new owner. + pub fn assign(&mut self, val: Unrooted<T>) { + *self = unsafe { val.inner() }; + } } impl<From, To> JS<From> { @@ -105,17 +161,86 @@ pub trait RootedReference<T> { fn root_ref<'a>(&'a self) -> Option<JSRef<'a, T>>; } -impl<'a, T: Reflectable> RootedReference<T> for Option<Root<'a, T>> { +impl<'a, 'b, T: Reflectable> RootedReference<T> for Option<Root<'a, 'b, T>> { fn root_ref<'a>(&'a self) -> Option<JSRef<'a, T>> { self.as_ref().map(|root| root.root_ref()) } } +// This trait should never be public; it allows access to unrooted values, and is thus +// easy to misuse. +/*definitely not public*/ trait Assignable<T> { + fn get_js(&self) -> JS<T>; +} + +impl<T> Assignable<T> for JS<T> { + fn get_js(&self) -> JS<T> { + self.clone() + } +} + +impl<'a, T> Assignable<T> for JSRef<'a, T> { + fn get_js(&self) -> JS<T> { + self.unrooted() + } +} + +// Assignable should not be exposed publically, since it's used to +// extract unrooted values in a safe way WHEN USED CORRECTLY. +impl<T: Reflectable> Assignable<T> for Unrooted<T> { + fn get_js(&self) -> JS<T> { + unsafe { self.inner() } + } +} + +pub trait OptionalAssignable<T> { + fn assign(&mut self, val: Option<T>); +} + +impl<T: Assignable<U>, U: Reflectable> OptionalAssignable<T> for Option<JS<U>> { + fn assign(&mut self, val: Option<T>) { + *self = val.map(|val| val.get_js()); + } +} + +pub trait OptionalRootable<T> { + fn root<'a, 'b>(self, roots: &'a RootCollection) -> Option<Root<'a, 'b, T>>; +} + +impl<T: Reflectable> OptionalRootable<T> for Option<Unrooted<T>> { + fn root<'a, 'b>(self, roots: &'a RootCollection) -> Option<Root<'a, 'b, T>> { + self.map(|inner| inner.root(roots)) + } +} + +pub trait ResultRootable<T,U> { + fn root<'a, 'b>(self, roots: &'a RootCollection) -> Result<Root<'a, 'b, T>, U>; +} + +impl<T: Reflectable, U> ResultRootable<T, U> for Result<Unrooted<T>, U> { + fn root<'a, 'b>(self, roots: &'a RootCollection) -> Result<Root<'a, 'b, T>, U> { + self.map(|inner| inner.root(roots)) + } +} + +/// Provides a facility to push unrooted values onto lists of rooted values. This is safe +/// under the assumption that said lists are reachable via the GC graph, and therefore the +/// new values are transitively rooted for the lifetime of their new owner. +pub trait UnrootedPushable<T> { + fn push_unrooted(&mut self, val: Unrooted<T>); +} + +impl<T: Reflectable> UnrootedPushable<T> for Vec<JS<T>> { + fn push_unrooted(&mut self, val: Unrooted<T>) { + unsafe { self.push(val.inner()) }; + } +} + #[deriving(Eq, Clone)] struct RootReference(*JSObject); impl RootReference { - fn new<'a, T: Reflectable>(unrooted: &Root<'a, T>) -> RootReference { + fn new<'a, 'b, T: Reflectable>(unrooted: &Root<'a, 'b, T>) -> RootReference { RootReference(unrooted.rooted()) } @@ -126,6 +251,7 @@ impl RootReference { static MAX_STACK_ROOTS: uint = 10; +/// An opaque, LIFO rooting mechanism. pub struct RootCollection { roots: [Cell<RootReference>, ..MAX_STACK_ROOTS], current: Cell<uint>, @@ -139,7 +265,7 @@ impl RootCollection { } } - fn new_root<'a, T: Reflectable>(&'a self, unrooted: &JS<T>) -> Root<'a, T> { + fn new_root<'a, 'b, T: Reflectable>(&'a self, unrooted: &JS<T>) -> Root<'a, 'b, T> { Root::new(self, unrooted) } @@ -147,13 +273,15 @@ impl RootCollection { let current = self.current.get(); assert!(current < MAX_STACK_ROOTS); self.roots[current].set(unrooted); + debug!(" rooting {:?}", unrooted); self.current.set(current + 1); } - fn root<'a, T: Reflectable>(&self, unrooted: &Root<'a, T>) { + fn root<'a, 'b, T: Reflectable>(&self, unrooted: &Root<'a, 'b, T>) { self.root_impl(RootReference::new(unrooted)); } + /// Root a raw JS pointer. pub fn root_raw(&self, unrooted: *JSObject) { self.root_impl(RootReference(unrooted)); } @@ -162,29 +290,41 @@ impl RootCollection { let mut current = self.current.get(); assert!(current != 0); current -= 1; + debug!("unrooting {:?} (expecting {:?}", self.roots[current].get(), rooted); assert!(self.roots[current].get() == rooted); self.roots[current].set(RootReference::null()); self.current.set(current); } - fn unroot<'a, T: Reflectable>(&self, rooted: &Root<'a, T>) { + fn unroot<'a, 'b, T: Reflectable>(&self, rooted: &Root<'a, 'b, T>) { self.unroot_impl(RootReference::new(rooted)); } + /// Unroot a raw JS pointer. Must occur in reverse order to its rooting. pub fn unroot_raw(&self, rooted: *JSObject) { self.unroot_impl(RootReference(rooted)); } } -pub struct Root<'a, T> { +/// A rooted JS 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. Roots cannot outlive the associated RootCollection object. +/// Attempts to transfer ownership of a Root via moving will trigger dynamic unrooting +/// failures due to incorrect ordering. +pub struct Root<'a, 'b, T> { root_list: &'a RootCollection, + jsref: JSRef<'b, T>, ptr: RefCell<*mut T>, } -impl<'a, T: Reflectable> Root<'a, T> { - fn new(roots: &'a RootCollection, unrooted: &JS<T>) -> Root<'a, T> { +impl<'a, 'b, T: Reflectable> Root<'a, 'b, T> { + fn new(roots: &'a RootCollection, unrooted: &JS<T>) -> Root<'a, 'b, T> { let root = Root { root_list: roots, + jsref: JSRef { + ptr: unrooted.ptr.clone(), + chain: unsafe { ::std::cast::transmute_region(&()) }, + }, ptr: unrooted.ptr.clone() }; roots.root(&root); @@ -209,24 +349,27 @@ impl<'a, T: Reflectable> Root<'a, T> { self.reflector().get_jsobject() } + fn internal_root_ref<'a>(&'a self) -> &'a JSRef<'b, T> { + &'a self.jsref + } + + fn mut_internal_root_ref<'a>(&'a mut self) -> &'a mut JSRef<'b, T> { + &'a mut self.jsref + } + pub fn root_ref<'b>(&'b self) -> JSRef<'b,T> { - unsafe { - JSRef { - ptr: self.ptr.clone(), - chain: ::std::cast::transmute_region(&()), - } - } + self.internal_root_ref().clone() } } #[unsafe_destructor] -impl<'a, T: Reflectable> Drop for Root<'a, T> { +impl<'a, 'b, T: Reflectable> Drop for Root<'a, 'b, T> { fn drop(&mut self) { self.root_list.unroot(self); } } -impl<'a, T: Reflectable> Reflectable for Root<'a, T> { +impl<'a, 'b, T: Reflectable> Reflectable for Root<'a, 'b, T> { fn reflector<'a>(&'a self) -> &'a Reflector { self.get().reflector() } @@ -236,17 +379,29 @@ impl<'a, T: Reflectable> Reflectable for Root<'a, T> { } } -/*impl<'a, T> Deref for Root<'a, T> { - fn deref<'a>(&'a self) -> &'a T { +impl<'a, 'b, T: Reflectable> Deref<JSRef<'b, T>> for Root<'a, 'b, T> { + fn deref<'c>(&'c self) -> &'c JSRef<'b, T> { + self.internal_root_ref() + } +} + +impl<'a, 'b, T: Reflectable> DerefMut<JSRef<'b, T>> for Root<'a, 'b, T> { + fn deref_mut<'c>(&'c mut self) -> &'c mut JSRef<'b, T> { + self.mut_internal_root_ref() + } +} + +impl<'a, T: Reflectable> Deref<T> for JSRef<'a, T> { + fn deref<'b>(&'b self) -> &'b T { self.get() } } -impl<'a, T> DerefMut for Root<'a, T> { - fn deref_mut<'a>(&'a mut self) -> &'a mut T { +impl<'a, T: Reflectable> DerefMut<T> for JSRef<'a, T> { + fn deref_mut<'b>(&'b mut self) -> &'b mut T { self.get_mut() } -}*/ +} /// Encapsulates a reference to something that is guaranteed to be alive. This is freely copyable. pub struct JSRef<'a, T> { |