From e8a1e9eabb121aff2aab3eb82395880b733d0090 Mon Sep 17 00:00:00 2001 From: Mukilan Thiyagarajan Date: Sat, 13 Dec 2014 13:25:51 +0530 Subject: [PATCH] Implement RootedVec --- components/script/dom/bindings/trace.rs | 136 ++++++++++++++++++++++- components/script/dom/domrectlist.rs | 8 +- components/script/dom/element.rs | 24 ++-- components/script/dom/eventdispatcher.rs | 21 ++-- components/script/script_task.rs | 6 +- 5 files changed, 168 insertions(+), 27 deletions(-) diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index fe3129dce07..0dbe3bd4c78 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -57,11 +57,13 @@ use msg::constellation_msg::ConstellationChan; use util::smallvec::{SmallVec1, SmallVec}; use util::str::{LengthOrPercentageOrAuto}; use std::cell::{Cell, RefCell}; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::collections::hash_state::HashState; use std::ffi::CString; use std::hash::{Hash, Hasher}; +use std::intrinsics::return_address; use std::old_io::timer::Timer; +use std::ops::{Deref, DerefMut}; use std::rc::Rc; use std::sync::mpsc::{Receiver, Sender}; use string_cache::{Atom, Namespace}; @@ -291,3 +293,135 @@ impl JSTraceable for Box { // Do nothing } } + +/// Holds a set of vectors that need to be rooted +pub struct RootedCollectionSet { + set: Vec>> +} + +/// TLV Holds a set of vectors that need to be rooted +thread_local!(pub static ROOTED_COLLECTIONS: Rc> = + Rc::new(RefCell::new(RootedCollectionSet::new()))); + +enum CollectionType { + DOMObjects, + JSVals, + JSObjects, +} + + +impl RootedCollectionSet { + fn new() -> RootedCollectionSet { + RootedCollectionSet { + set: vec!(HashSet::new(), HashSet::new(), HashSet::new()) + } + } + + fn remove(collection: &RootedVec) { + ROOTED_COLLECTIONS.with(|ref collections| { + let type_ = VecRootableType::tag(None::); + let mut collections = collections.borrow_mut(); + assert!(collections.set[type_ as uint].remove(&(collection as *const _ as *const _))); + }); + } + + fn add(collection: &RootedVec) { + ROOTED_COLLECTIONS.with(|ref collections| { + let type_ = VecRootableType::tag(None::); + let mut collections = collections.borrow_mut(); + collections.set[type_ as uint].insert(collection as *const _ as *const _); + }) + } + + unsafe fn trace(&self, tracer: *mut JSTracer) { + fn trace_collection_type(tracer: *mut JSTracer, + collections: &HashSet<*const RootedVec<()>>) { + for collection in collections { + let collection: *const RootedVec<()> = *collection; + let collection = collection as *const RootedVec; + unsafe { + let _ = (*collection).trace(tracer); + } + } + } + + let dom_collections = &self.set[CollectionType::DOMObjects as uint] as *const _ as *const HashSet<*const RootedVec<*const Reflector>>; + for dom_collection in (*dom_collections).iter() { + for reflector in (**dom_collection).iter() { + trace_reflector(tracer, "", &**reflector); + } + } + + trace_collection_type::(tracer, &self.set[CollectionType::JSVals as uint]); + trace_collection_type::<*mut JSObject>(tracer, &self.set[CollectionType::JSObjects as uint]); + } +} + + +/// Trait implemented by all types that can be used with RootedVec +trait VecRootableType { + /// Return the type tag used to determine how to trace RootedVec + fn tag(_a: Option) -> CollectionType; +} + +impl VecRootableType for JS { + fn tag(_a: Option>) -> CollectionType { CollectionType::DOMObjects } +} + +impl VecRootableType for JSVal { + fn tag(_a: Option) -> CollectionType { CollectionType::JSVals } +} + +impl VecRootableType for *mut JSObject { + fn tag(_a: Option<*mut JSObject>) -> CollectionType { CollectionType::JSObjects } +} + +/// A vector of items that are rooted for the lifetime +/// of this struct +#[allow(unrooted_must_root)] +#[jstraceable] +pub struct RootedVec { + v: Vec +} + + +impl RootedVec { + /// Create a vector of items of type T that is rooted for + /// the lifetime of this struct + pub fn new() -> RootedVec { + unsafe { + RootedCollectionSet::add::(&*(return_address() as *const _)); + } + RootedVec:: { v: vec!() } + } + +} + +#[unsafe_destructor] +impl Drop for RootedVec { + fn drop(&mut self) { + RootedCollectionSet::remove(self); + } +} + +impl Deref for RootedVec { + type Target = Vec; + fn deref(&self) -> &Vec { + &self.v + } +} + +impl DerefMut for RootedVec { + fn deref_mut(&mut self) -> &mut Vec { + &mut self.v + } +} + + +/// SM Callback that traces the rooted collections +pub unsafe extern fn trace_collections(tracer: *mut JSTracer, _data: *mut libc::c_void) { + ROOTED_COLLECTIONS.with(|ref collections| { + let collections = collections.borrow(); + collections.trace(tracer); + }); +} diff --git a/components/script/dom/domrectlist.rs b/components/script/dom/domrectlist.rs index 8ef7b8fce52..ec70329e092 100644 --- a/components/script/dom/domrectlist.rs +++ b/components/script/dom/domrectlist.rs @@ -6,6 +6,7 @@ use dom::bindings::codegen::Bindings::DOMRectListBinding; use dom::bindings::codegen::Bindings::DOMRectListBinding::DOMRectListMethods; use dom::bindings::global::GlobalRef; use dom::bindings::js::{JS, JSRef, Temporary}; +use dom::bindings::trace::RootedVec; use dom::bindings::utils::{Reflector, reflect_dom_object}; use dom::domrect::DOMRect; use dom::window::Window; @@ -19,17 +20,16 @@ pub struct DOMRectList { impl DOMRectList { fn new_inherited(window: JSRef, - rects: Vec>) -> DOMRectList { - let rects = rects.iter().map(|rect| JS::from_rooted(*rect)).collect(); + rects: &RootedVec>) -> DOMRectList { DOMRectList { reflector_: Reflector::new(), - rects: rects, + rects: (**rects).clone(), window: JS::from_rooted(window), } } pub fn new(window: JSRef, - rects: Vec>) -> Temporary { + rects: &RootedVec>) -> Temporary { reflect_dom_object(box DOMRectList::new_inherited(window, rects), GlobalRef::Window(window), DOMRectListBinding::Wrap) } diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index 016ba9a87c1..1501e01292d 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -28,7 +28,8 @@ use dom::bindings::codegen::InheritTypes::HTMLFormElementDerived; use dom::bindings::error::{ErrorResult, Fallible}; use dom::bindings::error::Error::{NamespaceError, InvalidCharacter, Syntax}; use dom::bindings::js::{MutNullableJS, JS, JSRef, LayoutJS, Temporary, TemporaryPushable}; -use dom::bindings::js::{OptionalRootable, Root}; +use dom::bindings::js::OptionalRootable; +use dom::bindings::trace::RootedVec; use dom::bindings::utils::xml_name_type; use dom::bindings::utils::XMLName::{QName, Name, InvalidXMLName}; use dom::create::create_element; @@ -1114,17 +1115,18 @@ impl<'a> ElementMethods for JSRef<'a, Element> { fn GetClientRects(self) -> Temporary { let win = window_from_node(self).root(); let node: JSRef = NodeCast::from_ref(self); - let rects = node.get_content_boxes(); - let rects: Vec> = rects.iter().map(|r| { - DOMRect::new( - win.r(), - r.origin.y, - r.origin.y + r.size.height, - r.origin.x, - r.origin.x + r.size.width).root() - }).collect(); + let raw_rects = node.get_content_boxes(); + let mut rects = RootedVec::new(); + for rect in raw_rects.iter() { + let rect = DOMRect::new(win.r(), + rect.origin.y, + rect.origin.y + rect.size.height, + rect.origin.x, + rect.origin.x + rect.size.width); + rects.push(JS::from_rooted(rect)); + } - DOMRectList::new(win.r(), rects.iter().map(|rect| rect.r()).collect()) + DOMRectList::new(win.r(), &rects) } // http://dev.w3.org/csswg/cssom-view/#dom-element-getboundingclientrect diff --git a/components/script/dom/eventdispatcher.rs b/components/script/dom/eventdispatcher.rs index 3518ff20eb6..029691f702c 100644 --- a/components/script/dom/eventdispatcher.rs +++ b/components/script/dom/eventdispatcher.rs @@ -4,8 +4,9 @@ use dom::bindings::callback::ExceptionHandling::Report; use dom::bindings::codegen::Bindings::EventBinding::EventMethods; -use dom::bindings::codegen::InheritTypes::{EventTargetCast, NodeCast, NodeDerived}; -use dom::bindings::js::{JS, JSRef, OptionalRootable, Root}; +use dom::bindings::codegen::InheritTypes::{EventTargetCast, NodeCast}; +use dom::bindings::js::{JS, JSRef, OptionalRootable}; +use dom::bindings::trace::RootedVec; use dom::eventtarget::{EventTarget, ListenerPhase}; use dom::event::{Event, EventPhase}; use dom::node::{Node, NodeHelpers}; @@ -27,15 +28,13 @@ pub fn dispatch_event<'a, 'b>(target: JSRef<'a, EventTarget>, let type_ = event.Type(); //TODO: no chain if not participating in a tree - let mut chain: Vec> = if target.is_node() { - let target_node: JSRef = NodeCast::to_ref(target).unwrap(); - target_node.ancestors().map(|ancestor| { + let mut chain: RootedVec> = RootedVec::new(); + if let Some(target_node) = NodeCast::to_ref(target) { + for ancestor in target_node.ancestors() { let ancestor_target: JSRef = EventTargetCast::from_ref(ancestor); - JS::from_rooted(ancestor_target).root() - }).collect() - } else { - vec!() - }; + chain.push(JS::from_rooted(ancestor_target)) + } + } event.set_phase(EventPhase::Capturing); @@ -43,6 +42,7 @@ pub fn dispatch_event<'a, 'b>(target: JSRef<'a, EventTarget>, /* capturing */ for cur_target in chain.as_slice().iter().rev() { + let cur_target = cur_target.root(); let stopped = match cur_target.r().get_listeners_for(type_.as_slice(), ListenerPhase::Capturing) { Some(listeners) => { event.set_current_target(cur_target.r()); @@ -88,6 +88,7 @@ pub fn dispatch_event<'a, 'b>(target: JSRef<'a, EventTarget>, event.set_phase(EventPhase::Bubbling); for cur_target in chain.iter() { + let cur_target = cur_target.root(); let stopped = match cur_target.r().get_listeners_for(type_.as_slice(), ListenerPhase::Bubbling) { Some(listeners) => { event.set_current_target(cur_target.r()); diff --git a/components/script/script_task.rs b/components/script/script_task.rs index b267c036435..20c87170e10 100644 --- a/components/script/script_task.rs +++ b/components/script/script_task.rs @@ -28,7 +28,7 @@ use dom::bindings::js::{JS, JSRef, Temporary, OptionalRootable, RootedReference} use dom::bindings::js::{RootCollection, RootCollectionPtr, Unrooted}; use dom::bindings::refcounted::{LiveDOMReferences, Trusted, TrustedReference}; use dom::bindings::structuredclone::StructuredCloneData; -use dom::bindings::trace::JSTraceable; +use dom::bindings::trace::{JSTraceable, trace_collections}; use dom::bindings::utils::{wrap_for_same_compartment, pre_wrap}; use dom::document::{Document, IsHTMLDocument, DocumentHelpers, DocumentProgressHandler, DocumentProgressTask, DocumentSource}; use dom::element::{Element, AttributeHandlers}; @@ -461,6 +461,10 @@ impl ScriptTask { !ptr.is_null() }); + + unsafe { + JS_SetExtraGCRootsTracer((*js_runtime).ptr, Some(trace_collections), ptr::null_mut()); + } // Unconstrain the runtime's threshold on nominal heap size, to avoid // triggering GC too often if operating continuously near an arbitrary // finite threshold. This leaves the maximum-JS_malloc-bytes threshold