diff --git a/components/script/dom/bindings/cell.rs b/components/script/dom/bindings/cell.rs index a4a15b7d5e71..a8b8902f7292 100644 --- a/components/script/dom/bindings/cell.rs +++ b/components/script/dom/bindings/cell.rs @@ -5,64 +5,142 @@ use dom::bindings::trace::JSTraceable; use js::jsapi::{JSTracer}; -use std::cell; -use std::cell::RefCell; -use std::mem; - -/// A mutable field in DOM for large sized value. -/// This has a special method to return the pointer of itself -/// for used in layout task. -/// This simply wraps `RefCell` to add the special method. +use std::cell::{Cell, UnsafeCell}; +use std::kinds::marker; + +/// A mutable field in the DOM. +/// +/// This extends the API of `core::cell::RefCell` to allow unsafe access in +/// certain situations. pub struct DOMRefCell { - base: RefCell, + value: UnsafeCell, + borrow: Cell, + nocopy: marker::NoCopy, + nosync: marker::NoSync, } -pub type Ref<'a, T> = cell::Ref<'a, T>; -pub type RefMut<'a, T> = cell::RefMut<'a, T>; +// Functionality specific to Servo's `DOMRefCell` type +// =================================================== +impl DOMRefCell { + /// Return a reference to the contents. + /// + /// For use in the layout task only. + pub unsafe fn borrow_for_layout<'a>(&'a self) -> &'a T { + &*self.value.get() + } +} + +impl JSTraceable for DOMRefCell { + fn trace(&self, trc: *mut JSTracer) { + (*self).borrow().trace(trc) + } +} + +// Functionality duplicated with `core::cell::RefCell` +// =================================================== +// +// This can shrink once rust-lang/rust#18131 is fixed. + +// Values [1, MAX-1] represent the number of `Ref` active +// (will not outgrow its range since `uint` is the size of the address space) +type BorrowFlag = uint; +static UNUSED: BorrowFlag = 0; +static WRITING: BorrowFlag = -1; impl DOMRefCell { - #[inline(always)] pub fn new(value: T) -> DOMRefCell { DOMRefCell { - base: RefCell::new(value), + value: UnsafeCell::new(value), + borrow: Cell::new(UNUSED), + nocopy: marker::NoCopy, + nosync: marker::NoSync, } } - #[inline(always)] pub fn unwrap(self) -> T { - self.base.unwrap() + debug_assert!(self.borrow.get() == UNUSED); + unsafe{self.value.unwrap()} } - #[inline(always)] pub fn try_borrow<'a>(&'a self) -> Option> { - self.base.try_borrow() + match self.borrow.get() { + WRITING => None, + borrow => { + self.borrow.set(borrow + 1); + Some(Ref { _parent: self }) + } + } } - #[inline(always)] pub fn borrow<'a>(&'a self) -> Ref<'a, T> { - self.base.borrow() + match self.try_borrow() { + Some(ptr) => ptr, + None => fail!("DOMRefCell already mutably borrowed") + } } - #[inline(always)] pub fn try_borrow_mut<'a>(&'a self) -> Option> { - self.base.try_borrow_mut() + match self.borrow.get() { + UNUSED => { + self.borrow.set(WRITING); + Some(RefMut { _parent: self }) + }, + _ => None + } } - #[inline(always)] pub fn borrow_mut<'a>(&'a self) -> RefMut<'a, T> { - self.base.borrow_mut() + match self.try_borrow_mut() { + Some(ptr) => ptr, + None => fail!("DOMRefCell already borrowed") + } } +} - /// This returns the pointer which refers T in `RefCell` directly. - pub unsafe fn borrow_for_layout<'a>(&'a self) -> &'a T { - let val = mem::transmute::<&RefCell, &T>(&self.base); - val +pub struct Ref<'b, T:'b> { + _parent: &'b DOMRefCell +} + +#[unsafe_destructor] +impl<'b, T> Drop for Ref<'b, T> { + fn drop(&mut self) { + let borrow = self._parent.borrow.get(); + debug_assert!(borrow != WRITING && borrow != UNUSED); + self._parent.borrow.set(borrow - 1); } } -impl JSTraceable for DOMRefCell { - fn trace(&self, trc: *mut JSTracer) { - (*self).base.borrow().trace(trc) +impl<'b, T> Deref for Ref<'b, T> { + #[inline] + fn deref<'a>(&'a self) -> &'a T { + unsafe { &*self._parent.value.get() } + } +} + +pub struct RefMut<'b, T:'b> { + _parent: &'b DOMRefCell +} + +#[unsafe_destructor] +impl<'b, T> Drop for RefMut<'b, T> { + fn drop(&mut self) { + let borrow = self._parent.borrow.get(); + debug_assert!(borrow == WRITING); + self._parent.borrow.set(UNUSED); + } +} + +impl<'b, T> Deref for RefMut<'b, T> { + #[inline] + fn deref<'a>(&'a self) -> &'a T { + unsafe { &*self._parent.value.get() } + } +} + +impl<'b, T> DerefMut for RefMut<'b, T> { + #[inline] + fn deref_mut<'a>(&'a mut self) -> &'a mut T { + unsafe { &mut *self._parent.value.get() } } }