/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ //! Weak-referenceable JS-managed DOM objects. //! //! IDL interfaces marked as `weakReferenceable` in `Bindings.conf` //! automatically implement the `WeakReferenceable` trait in codegen. //! The instance object is responsible for setting `None` in its own //! own `WeakBox` when it is collected, through the `DOM_WEAK_SLOT` //! slot. When all associated `WeakRef` values are dropped, the //! `WeakBox` itself is dropped too. use core::nonzero::NonZero; use dom::bindings::js::Root; use dom::bindings::reflector::Reflectable; use dom::bindings::trace::JSTraceable; use heapsize::HeapSizeOf; use js::jsapi::{JSTracer, JS_GetReservedSlot, JS_SetReservedSlot}; use js::jsval::PrivateValue; use libc::c_void; use std::cell::{Cell, UnsafeCell}; use std::iter::Iterator; use std::mem; use std::ops::{Deref, DerefMut, Drop}; /// The index of the slot wherein a pointer to the weak holder cell is /// stored for weak-referenceable bindings. We use slot 1 for holding it, /// this is unsafe for globals, we disallow weak-referenceable globals /// directly in codegen. pub const DOM_WEAK_SLOT: u32 = 1; /// A weak reference to a JS-managed DOM object. #[allow_unrooted_interior] pub struct WeakRef { ptr: NonZero<*mut WeakBox>, } /// The inner box of weak references, public for the finalization in codegen. #[must_root] pub struct WeakBox { /// The reference count. When it reaches zero, the `value` field should /// have already been set to `None`. The pointee contributes one to the count. pub count: Cell, /// The pointer to the JS-managed object, set to None when it is collected. pub value: Cell>>, } /// Trait implemented by weak-referenceable interfaces. pub trait WeakReferenceable: Reflectable + Sized { /// Downgrade a DOM object reference to a weak one. fn downgrade(&self) -> WeakRef { unsafe { let object = self.reflector().get_jsobject().get(); let mut ptr = JS_GetReservedSlot(object, DOM_WEAK_SLOT) .to_private() as *mut WeakBox; if ptr.is_null() { debug!("Creating new WeakBox holder for {:p}.", self); ptr = Box::into_raw(box WeakBox { count: Cell::new(1), value: Cell::new(Some(NonZero::new(self))), }); JS_SetReservedSlot(object, DOM_WEAK_SLOT, PrivateValue(ptr as *const c_void)); } let box_ = &*ptr; assert!(box_.value.get().is_some()); let new_count = box_.count.get() + 1; debug!("Incrementing WeakBox refcount for {:p} to {}.", self, new_count); box_.count.set(new_count); WeakRef { ptr: NonZero::new(ptr), } } } } impl WeakRef { /// Create a new weak reference from a `WeakReferenceable` interface instance. /// This is just a convenience wrapper around `::downgrade` /// to not have to import `WeakReferenceable`. pub fn new(value: &T) -> Self { value.downgrade() } /// Root a weak reference. Returns `None` if the object was already collected. pub fn root(&self) -> Option> { unsafe { &**self.ptr }.value.get().map(Root::new) } /// Return whether the weakly-referenced object is still alive. pub fn is_alive(&self) -> bool { unsafe { &**self.ptr }.value.get().is_some() } } impl Clone for WeakRef { fn clone(&self) -> WeakRef { unsafe { let box_ = &**self.ptr; let new_count = box_.count.get() + 1; box_.count.set(new_count); WeakRef { ptr: self.ptr, } } } } impl HeapSizeOf for WeakRef { fn heap_size_of_children(&self) -> usize { 0 } } impl PartialEq for WeakRef { fn eq(&self, other: &Self) -> bool { unsafe { (**self.ptr).value.get() == (**other.ptr).value.get() } } } impl PartialEq for WeakRef { fn eq(&self, other: &T) -> bool { unsafe { match (**self.ptr).value.get() { Some(ptr) => *ptr == other, None => false, } } } } no_jsmanaged_fields!(WeakRef); impl Drop for WeakRef { fn drop(&mut self) { unsafe { let (count, value) = { let weak_box = &**self.ptr; assert!(weak_box.count.get() > 0); let count = weak_box.count.get() - 1; weak_box.count.set(count); (count, weak_box.value.get()) }; if count == 0 { assert!(value.is_none()); mem::drop(Box::from_raw(*self.ptr)); } } } } /// A mutable weak reference to a JS-managed DOM object. On tracing, /// the contained weak reference is dropped if the pointee was already /// collected. pub struct MutableWeakRef { cell: UnsafeCell>>, } impl MutableWeakRef { /// Create a new mutable weak reference. pub fn new(value: Option<&T>) -> MutableWeakRef { MutableWeakRef { cell: UnsafeCell::new(value.map(WeakRef::new)), } } /// Set the pointee of a mutable weak reference. pub fn set(&self, value: Option<&T>) { unsafe { *self.cell.get() = value.map(WeakRef::new); } } /// Root a mutable weak reference. Returns `None` if the object /// was already collected. pub fn root(&self) -> Option> { unsafe { &*self.cell.get() }.as_ref().and_then(WeakRef::root) } } impl HeapSizeOf for MutableWeakRef { fn heap_size_of_children(&self) -> usize { 0 } } impl JSTraceable for MutableWeakRef { fn trace(&self, _: *mut JSTracer) { let ptr = self.cell.get(); unsafe { let should_drop = match *ptr { Some(ref value) => !value.is_alive(), None => false, }; if should_drop { mem::drop((*ptr).take().unwrap()); } } } } /// A vector of weak references. On tracing, the vector retains /// only references which still point to live objects. #[allow_unrooted_interior] #[derive(HeapSizeOf)] pub struct WeakRefVec { vec: Vec>, } impl WeakRefVec { /// Create a new vector of weak references. pub fn new() -> Self { WeakRefVec { vec: vec![] } } /// Calls a function on each reference which still points to a /// live object. The order of the references isn't preserved. pub fn update)>(&mut self, mut f: F) { let mut i = 0; while i < self.vec.len() { if self.vec[i].is_alive() { f(WeakRefEntry { vec: self, index: &mut i }); } else { self.vec.swap_remove(i); } } } /// Clears the vector of its dead references. pub fn retain_alive(&mut self) { self.update(|_| ()); } } impl Deref for WeakRefVec { type Target = Vec>; fn deref(&self) -> &Vec> { &self.vec } } impl DerefMut for WeakRefVec { fn deref_mut(&mut self) -> &mut Vec> { &mut self.vec } } /// An entry of a vector of weak references. Passed to the closure /// given to `WeakRefVec::update`. #[allow_unrooted_interior] pub struct WeakRefEntry<'a, T: WeakReferenceable + 'a> { vec: &'a mut WeakRefVec, index: &'a mut usize, } impl<'a, T: WeakReferenceable + 'a> WeakRefEntry<'a, T> { /// Remove the entry from the underlying vector of weak references. pub fn remove(self) -> WeakRef { let ref_ = self.vec.swap_remove(*self.index); mem::forget(self); ref_ } } impl<'a, T: WeakReferenceable + 'a> Deref for WeakRefEntry<'a, T> { type Target = WeakRef; fn deref(&self) -> &WeakRef { &self.vec[*self.index] } } impl<'a, T: WeakReferenceable + 'a> Drop for WeakRefEntry<'a, T> { fn drop(&mut self) { *self.index += 1; } }