diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs index b2a122adcbe..49c11e5c924 100644 --- a/components/style/properties/properties.mako.rs +++ b/components/style/properties/properties.mako.rs @@ -14,7 +14,7 @@ use std::borrow::Cow; use std::collections::HashSet; use std::fmt; use std::ops::Deref; -use stylearc::Arc; +use stylearc::{Arc, UniqueArc}; use app_units::Au; #[cfg(feature = "servo")] use cssparser::{Color as CSSParserColor, RGBA}; @@ -2057,7 +2057,9 @@ pub enum StyleStructRef<'a, T: 'a> { /// A borrowed struct from the parent, for example, for inheriting style. Borrowed(&'a Arc), /// An owned struct, that we've already mutated. - Owned(Arc), + Owned(UniqueArc), + /// Temporarily vacated, will panic if accessed + Vacated, } impl<'a, T: 'a> StyleStructRef<'a, T> @@ -2067,21 +2069,45 @@ impl<'a, T: 'a> StyleStructRef<'a, T> /// borrowed value, or returning the owned one. pub fn mutate(&mut self) -> &mut T { if let StyleStructRef::Borrowed(v) = *self { - *self = StyleStructRef::Owned(Arc::new((**v).clone())); + *self = StyleStructRef::Owned(UniqueArc::new((**v).clone())); } match *self { - StyleStructRef::Owned(ref mut v) => Arc::get_mut(v).unwrap(), + StyleStructRef::Owned(ref mut v) => v, StyleStructRef::Borrowed(..) => unreachable!(), + StyleStructRef::Vacated => panic!("Accessed vacated style struct") } } + /// Extract a unique Arc from this struct, vacating it. + /// + /// The vacated state is a transient one, please put the Arc back + /// when done via `put()`. This function is to be used to separate + /// the struct being mutated from the computed context + pub fn take(&mut self) -> UniqueArc { + use std::mem::replace; + let inner = replace(self, StyleStructRef::Vacated); + + match inner { + StyleStructRef::Owned(arc) => arc, + StyleStructRef::Borrowed(arc) => UniqueArc::new((**arc).clone()), + StyleStructRef::Vacated => panic!("Accessed vacated style struct"), + } + } + + /// Replace vacated ref with an arc + pub fn put(&mut self, arc: UniqueArc) { + debug_assert!(matches!(*self, StyleStructRef::Vacated)); + *self = StyleStructRef::Owned(arc); + } + /// Get a mutable reference to the owned struct, or `None` if the struct /// hasn't been mutated. pub fn get_if_mutated(&mut self) -> Option<<&mut T> { match *self { - StyleStructRef::Owned(ref mut v) => Some(Arc::get_mut(v).unwrap()), + StyleStructRef::Owned(ref mut v) => Some(v), StyleStructRef::Borrowed(..) => None, + StyleStructRef::Vacated => panic!("Accessed vacated style struct") } } @@ -2089,8 +2115,9 @@ impl<'a, T: 'a> StyleStructRef<'a, T> /// appropriate. pub fn build(self) -> Arc { match self { - StyleStructRef::Owned(v) => v, + StyleStructRef::Owned(v) => v.shareable(), StyleStructRef::Borrowed(v) => v.clone(), + StyleStructRef::Vacated => panic!("Accessed vacated style struct") } } } @@ -2102,6 +2129,7 @@ impl<'a, T: 'a> Deref for StyleStructRef<'a, T> { match *self { StyleStructRef::Owned(ref v) => &**v, StyleStructRef::Borrowed(v) => &**v, + StyleStructRef::Vacated => panic!("Accessed vacated style struct") } } } @@ -2183,6 +2211,16 @@ impl<'a> StyleBuilder<'a> { self.${style_struct.ident}.mutate() } + /// Gets a mutable view of the current `${style_struct.name}` style. + pub fn take_${style_struct.name_lower}(&mut self) -> UniqueArc { + self.${style_struct.ident}.take() + } + + /// Gets a mutable view of the current `${style_struct.name}` style. + pub fn put_${style_struct.name_lower}(&mut self, s: UniqueArc) { + self.${style_struct.ident}.put(s) + } + /// Gets a mutable view of the current `${style_struct.name}` style, /// only if it's been mutated before. pub fn get_${style_struct.name_lower}_if_mutated(&mut self) diff --git a/components/style/stylearc.rs b/components/style/stylearc.rs index 01885dc481b..9b355e7d211 100644 --- a/components/style/stylearc.rs +++ b/components/style/stylearc.rs @@ -31,7 +31,7 @@ use std::convert::From; use std::fmt; use std::hash::{Hash, Hasher}; use std::mem; -use std::ops::Deref; +use std::ops::{Deref, DerefMut}; use std::sync::atomic; use std::sync::atomic::Ordering::{Acquire, Relaxed, Release}; @@ -71,6 +71,40 @@ pub struct Arc { ptr: *mut ArcInner, } +/// An Arc that is known to be uniquely owned +/// +/// This lets us build arcs that we can mutate before +/// freezing, without needing to change the allocation +pub struct UniqueArc(Arc); + +impl UniqueArc { + #[inline] + /// Construct a new UniqueArc + pub fn new(data: T) -> Self { + UniqueArc(Arc::new(data)) + } + + #[inline] + /// Convert to a shareable Arc once we're done using it + pub fn shareable(self) -> Arc { + self.0 + } +} + +impl Deref for UniqueArc { + type Target = T; + fn deref(&self) -> &T { + &*self.0 + } +} + +impl DerefMut for UniqueArc { + fn deref_mut(&mut self) -> &mut T { + // We know this to be uniquely owned + unsafe { &mut (*self.0.ptr).data } + } +} + unsafe impl Send for Arc {} unsafe impl Sync for Arc {}