gecko-dev/servo/components/script/dom/bindings/js.rs
Josh Matthews 3df7a65e2a servo: Merge #4057 - Enable refcounting arbitrary DOM types (from jdm:refcountdom); r=Ms2ger
This replaces the specialized TrustedXHRAddress and TrustedWorkerAddress code that was used for the same purpose. A non-zero refcount pins the given DOM object's reflector and prevents it from being GCed even when there are no other outstanding references visible to SpiderMonkey. This will enable us to implement asynchronous operations that refer to particular DOM objects (such as "queue a task to fire a simple event named load at the iframe element" from the spec) safely and conveniently, and paves the way for things like asynchronous network responses.

Some concerns about the resulting size of XHR progress messages have been expressed, but I believe optimizations to reduce that can be implemented in subsequent PRs.

r? @Ms2ger - note in particular the changes to the worker lifetime code. I couldn't figure out how to achieve an identical lifetime to the previous addref/release pairing, and I also was having trouble figuring out why the existing setup was safe. The new implementation now holds the main script task Worker object alive via the TrustedWorkerAddress field in the dedicated worker global scope, which is a significant difference.

Source-Repo: https://github.com/servo/servo
Source-Revision: 2c259f477c41331e66beab8bda865971982a1ff4
2014-12-29 11:57:45 -07:00

614 lines
19 KiB
Rust

/* 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/. */
//! Smart pointers for the JS-managed DOM objects.
//!
//! The DOM is made up of Rust types whose lifetime is entirely controlled by the whims of
//! the SpiderMonkey garbage collector. The types in this module are designed to ensure
//! that any interactions with said Rust types only occur on values that will remain alive
//! the entire time.
//!
//! Here is a brief overview of the important types:
//!
//! - `JSRef<T>`: a freely-copyable reference to a rooted value.
//! - `Root<T>`: a stack-based reference to a rooted value.
//! - `JS<T>`: a pointer to JS-owned memory that can automatically be traced by the GC when
//! encountered as a field of a Rust structure.
//! - `Temporary<T>`: a value that will remain rooted for the duration of its lifetime.
//!
//! The rule of thumb is as follows:
//!
//! - All methods return `Temporary<T>`, to ensure the value remains alive until it is stored
//! somewhere that is reachable by the GC.
//! - All functions take `JSRef<T>` arguments, to ensure that they will remain uncollected for
//! the duration of their usage.
//! - All types contain `JS<T>` fields and derive the `Encodable` trait, to ensure that they are
//! transitively marked as reachable by the GC if the enclosing value is reachable.
//! - All methods for type `T` are implemented for `JSRef<T>`, to ensure that the self value
//! will not be collected for the duration of the method call.
//!
//! Both `Temporary<T>` and `JS<T>` do not allow access to their inner value without explicitly
//! creating a stack-based root via the `root` method. This returns a `Root<T>`, which causes
//! the JS-owned value to be uncollectable for the duration of the `Root` object's lifetime.
//! A `JSRef<T>` can be obtained from a `Root<T>` either by dereferencing the `Root<T>` (`*rooted`)
//! or explicitly calling the `root_ref` method. These `JSRef<T>` values are not allowed to
//! outlive their originating `Root<T>`, to ensure that all interactions with the enclosed value
//! only occur when said value is uncollectable, and will cause static lifetime errors if
//! misused.
//!
//! Other miscellaneous helper traits:
//!
//! - `OptionalRootable` and `OptionalRootedRootable`: make rooting `Option` values easy via a `root` method
//! - `ResultRootable`: make rooting successful `Result` values easy
//! - `TemporaryPushable`: allows mutating vectors of `JS<T>` with new elements of `JSRef`/`Temporary`
//! - `OptionalSettable`: allows assigning `Option` values of `JSRef`/`Temporary` to fields of `Option<JS<T>>`
//! - `RootedReference`: makes obtaining an `Option<JSRef<T>>` from an `Option<Root<T>>` easy
use dom::bindings::trace::JSTraceable;
use dom::bindings::utils::{Reflector, Reflectable};
use dom::node::Node;
use js::jsapi::JSObject;
use js::jsval::JSVal;
use layout_interface::TrustedNodeAddress;
use script_task::StackRoots;
use servo_util::smallvec::{SmallVec, SmallVec16};
use std::cell::{Cell, UnsafeCell};
use std::default::Default;
use std::kinds::marker::ContravariantLifetime;
use std::mem;
/// A type that represents a JS-owned value that is rooted for the lifetime of this value.
/// Importantly, it requires explicit rooting in order to interact with the inner value.
/// Can be assigned into JS-owned member fields (i.e. `JS<T>` types) safely via the
/// `JS<T>::assign` method or `OptionalSettable::assign` (for `Option<JS<T>>` fields).
#[allow(unrooted_must_root)]
pub struct Temporary<T> {
inner: JS<T>,
/// On-stack JS pointer to assuage conservative stack scanner
_js_ptr: *mut JSObject,
}
impl<T> PartialEq for Temporary<T> {
fn eq(&self, other: &Temporary<T>) -> bool {
self.inner == other.inner
}
}
impl<T: Reflectable> Temporary<T> {
/// Create a new `Temporary` value from a JS-owned value.
pub fn new(inner: JS<T>) -> Temporary<T> {
Temporary {
inner: inner,
_js_ptr: inner.reflector().get_jsobject(),
}
}
/// Create a new `Temporary` value from a rooted value.
pub fn from_rooted<'a>(root: JSRef<'a, T>) -> Temporary<T> {
Temporary::new(JS::from_rooted(root))
}
/// Create a stack-bounded root for this value.
pub fn root<'a, 'b>(self) -> Root<'a, 'b, T> {
let collection = StackRoots.get().unwrap();
unsafe {
Root::new(&**collection, &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) -> Temporary<To> {
mem::transmute(self)
}
}
/// A rooted, JS-owned value. Must only be used as a field in other JS-owned types.
#[must_root]
pub struct JS<T> {
ptr: *const T
}
impl<T> PartialEq for JS<T> {
#[allow(unrooted_must_root)]
fn eq(&self, other: &JS<T>) -> bool {
self.ptr == other.ptr
}
}
impl <T> Clone for JS<T> {
#[inline]
fn clone(&self) -> JS<T> {
JS {
ptr: self.ptr.clone()
}
}
}
impl JS<Node> {
/// 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<Node> {
let TrustedNodeAddress(addr) = inner;
JS {
ptr: addr as *const Node
}
}
}
impl<T: Reflectable> JS<T> {
/// Create a new JS-owned value wrapped from a raw Rust pointer.
pub unsafe fn from_raw(raw: *const T) -> JS<T> {
JS {
ptr: raw
}
}
/// Root this JS-owned value to prevent its collection as garbage.
pub fn root<'a, 'b>(&self) -> Root<'a, 'b, T> {
let collection = StackRoots.get().unwrap();
unsafe {
Root::new(&**collection, self)
}
}
}
impl<T: Assignable<U>, U: Reflectable> JS<U> {
pub fn from_rooted(root: T) -> JS<U> {
unsafe {
root.get_js()
}
}
}
//XXXjdm This is disappointing. This only gets called from trace hooks, in theory,
// so it's safe to assume that self is rooted and thereby safe to access.
impl<T: Reflectable> Reflectable for JS<T> {
fn reflector<'a>(&'a self) -> &'a Reflector {
unsafe {
(*self.unsafe_get()).reflector()
}
}
}
pub trait HeapGCValue: JSTraceable {
}
impl HeapGCValue for JSVal {
}
impl<T: Reflectable> HeapGCValue for JS<T> {
}
/// A mutable holder for a GC-owned SpiderMonkey value stored on the heap.
/// Must be used in place of traditional interior mutability to ensure proper
/// GC barriers are enforced.
#[must_root]
#[jstraceable]
pub struct MutHeap<T: HeapGCValue+Copy> {
val: Cell<T>,
}
impl<T: HeapGCValue+Copy> MutHeap<T> {
pub fn new(initial: T) -> MutHeap<T> {
MutHeap {
val: Cell::new(initial),
}
}
pub fn set(&self, val: T) {
self.val.set(val)
}
pub fn get(&self) -> T {
self.val.get()
}
}
/// A mutable `JS<T>` value, with nullability represented by an enclosing
/// Option wrapper. Must be used in place of traditional internal mutability
/// to ensure that the proper GC barriers are enforced.
#[must_root]
#[jstraceable]
pub struct MutNullableJS<T: Reflectable> {
ptr: Cell<Option<JS<T>>>
}
impl<T: Assignable<U>, U: Reflectable> MutNullableJS<U> {
pub fn new(initial: Option<T>) -> MutNullableJS<U> {
MutNullableJS {
ptr: Cell::new(initial.map(|initial| {
unsafe { initial.get_js() }
}))
}
}
}
impl<T: Reflectable> Default for MutNullableJS<T> {
fn default() -> MutNullableJS<T> {
MutNullableJS {
ptr: Cell::new(None)
}
}
}
impl<T: Reflectable> MutNullableJS<T> {
/// Store an unrooted value in this field. This is safe under the
/// assumption that `MutNullableJS<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<U: Assignable<T>>(&self, val: Option<U>) {
self.ptr.set(val.map(|val| {
unsafe { val.get_js() }
}));
}
/// Set the inner value to null, without making API users jump through
/// useless type-ascription hoops.
pub fn clear(&self) {
self.assign(None::<JS<T>>);
}
/// Retrieve a copy of the current optional inner value.
pub fn get(&self) -> Option<Temporary<T>> {
self.ptr.get().map(Temporary::new)
}
/// Retrieve a copy of the inner optional `JS<T>`. For use by layout, which
/// can't use safe types like Temporary.
pub unsafe fn get_inner(&self) -> Option<JS<T>> {
self.ptr.get()
}
pub fn or_init(&self, cb: || -> Temporary<T>) -> Temporary<T> {
match self.get() {
Some(inner) => inner,
None => {
let inner = cb();
self.assign(Some(inner));
inner
},
}
}
}
impl<T: Reflectable> JS<T> {
/// Returns an unsafe pointer to the interior of this JS object without touching the borrow
/// flags. This is the only method that be safely accessed from layout. (The fact that this
/// is unsafe is what necessitates the layout wrappers.)
pub unsafe fn unsafe_get(&self) -> *mut T {
mem::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: Temporary<T>) {
*self = unsafe { val.inner() };
}
}
impl<From, To> JS<From> {
//XXXjdm It would be lovely if this could be private.
pub unsafe fn transmute(self) -> JS<To> {
mem::transmute(self)
}
pub unsafe fn transmute_copy(&self) -> JS<To> {
mem::transmute_copy(self)
}
}
/// Get an `Option<JSRef<T>>` out of an `Option<Root<T>>`
pub trait RootedReference<T> {
fn root_ref<'a>(&'a self) -> Option<JSRef<'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())
}
}
/// Get an `Option<Option<JSRef<T>>>` out of an `Option<Option<Root<T>>>`
pub trait OptionalRootedReference<T> {
fn root_ref<'a>(&'a self) -> Option<Option<JSRef<'a, T>>>;
}
impl<'a, 'b, T: Reflectable> OptionalRootedReference<T> for Option<Option<Root<'a, 'b, T>>> {
fn root_ref<'a>(&'a self) -> Option<Option<JSRef<'a, T>>> {
self.as_ref().map(|inner| inner.root_ref())
}
}
/// Trait that allows extracting a `JS<T>` value from a variety of rooting-related containers,
/// which in general is an unsafe operation since they can outlive the rooted lifetime of the
/// original value.
pub trait Assignable<T> {
unsafe fn get_js(&self) -> JS<T>;
}
impl<T> Assignable<T> for JS<T> {
unsafe fn get_js(&self) -> JS<T> {
self.clone()
}
}
impl<'a, T: Reflectable> Assignable<T> for JSRef<'a, T> {
unsafe fn get_js(&self) -> JS<T> {
self.unrooted()
}
}
impl<T: Reflectable> Assignable<T> for Temporary<T> {
unsafe fn get_js(&self) -> JS<T> {
self.inner()
}
}
/// Assign an optional rootable value (either of `JS<T>` or `Temporary<T>`) to an optional
/// field of a DOM type (ie. `Option<JS<T>>`)
pub trait OptionalSettable<T> {
fn assign(&self, val: Option<T>);
}
impl<T: Assignable<U>, U: Reflectable> OptionalSettable<T> for Cell<Option<JS<U>>> {
fn assign(&self, val: Option<T>) {
self.set(val.map(|val| unsafe { val.get_js() }));
}
}
/// Root a rootable `Option` type (used for `Option<Temporary<T>>`)
pub trait OptionalRootable<T> {
fn root<'a, 'b>(self) -> Option<Root<'a, 'b, T>>;
}
impl<T: Reflectable> OptionalRootable<T> for Option<Temporary<T>> {
fn root<'a, 'b>(self) -> Option<Root<'a, 'b, T>> {
self.map(|inner| inner.root())
}
}
/// Return an unrooted type for storing in optional DOM fields
pub trait OptionalUnrootable<T> {
fn unrooted(&self) -> Option<JS<T>>;
}
impl<'a, T: Reflectable> OptionalUnrootable<T> for Option<JSRef<'a, T>> {
fn unrooted(&self) -> Option<JS<T>> {
self.as_ref().map(|inner| JS::from_rooted(*inner))
}
}
/// Root a rootable `Option` type (used for `Option<JS<T>>`)
pub trait OptionalRootedRootable<T> {
fn root<'a, 'b>(&self) -> Option<Root<'a, 'b, T>>;
}
impl<T: Reflectable> OptionalRootedRootable<T> for Option<JS<T>> {
fn root<'a, 'b>(&self) -> Option<Root<'a, 'b, T>> {
self.as_ref().map(|inner| inner.root())
}
}
/// Root a rootable `Option<Option>` type (used for `Option<Option<JS<T>>>`)
pub trait OptionalOptionalRootedRootable<T> {
fn root<'a, 'b>(&self) -> Option<Option<Root<'a, 'b, T>>>;
}
impl<T: Reflectable> OptionalOptionalRootedRootable<T> for Option<Option<JS<T>>> {
fn root<'a, 'b>(&self) -> Option<Option<Root<'a, 'b, T>>> {
self.as_ref().map(|inner| inner.root())
}
}
/// Root a rootable `Result` type (any of `Temporary<T>` or `JS<T>`)
pub trait ResultRootable<T,U> {
fn root<'a, 'b>(self) -> Result<Root<'a, 'b, T>, U>;
}
impl<T: Reflectable, U> ResultRootable<T, U> for Result<Temporary<T>, U> {
fn root<'a, 'b>(self) -> Result<Root<'a, 'b, T>, U> {
self.map(|inner| inner.root())
}
}
impl<T: Reflectable, U> ResultRootable<T, U> for Result<JS<T>, U> {
fn root<'a, 'b>(self) -> Result<Root<'a, 'b, T>, U> {
self.map(|inner| inner.root())
}
}
/// 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 TemporaryPushable<T> {
fn push_unrooted(&mut self, val: &T);
fn insert_unrooted(&mut self, index: uint, val: &T);
}
impl<T: Assignable<U>, U: Reflectable> TemporaryPushable<T> for Vec<JS<U>> {
fn push_unrooted(&mut self, val: &T) {
self.push(unsafe { val.get_js() });
}
fn insert_unrooted(&mut self, index: uint, val: &T) {
self.insert(index, unsafe { val.get_js() });
}
}
/// An opaque, LIFO rooting mechanism.
pub struct RootCollection {
roots: UnsafeCell<SmallVec16<*mut JSObject>>,
}
impl RootCollection {
/// Create an empty collection of roots
pub fn new() -> RootCollection {
RootCollection {
roots: UnsafeCell::new(SmallVec16::new()),
}
}
/// Track a stack-based root to ensure LIFO root ordering
fn root<'a, 'b, T: Reflectable>(&self, untracked: &Root<'a, 'b, T>) {
unsafe {
let roots = self.roots.get();
(*roots).push(untracked.js_ptr);
debug!(" rooting {}", untracked.js_ptr);
}
}
/// Stop tracking a stack-based root, asserting if LIFO root ordering has been violated
fn unroot<'a, 'b, T: Reflectable>(&self, rooted: &Root<'a, 'b, T>) {
unsafe {
let roots = self.roots.get();
debug!("unrooting {} (expecting {}",
(*roots).as_slice().last().unwrap(),
rooted.js_ptr);
assert!(*(*roots).as_slice().last().unwrap() == rooted.js_ptr);
(*roots).pop().unwrap();
}
}
}
/// 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. `Root`s 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> {
/// List that ensures correct dynamic root ordering
root_list: &'a RootCollection,
/// Reference to rooted value that must not outlive this container
jsref: JSRef<'b, T>,
/// On-stack JS pointer to assuage conservative stack scanner
js_ptr: *mut JSObject,
}
impl<'b, 'a: 'b, T: Reflectable> Root<'a, 'b, T> {
/// Create a new stack-bounded root for the provided JS-owned value.
/// It cannot not outlive its associated `RootCollection`, and it contains a `JSRef`
/// which cannot outlive this new `Root`.
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: ContravariantLifetime,
},
js_ptr: unrooted.reflector().get_jsobject(),
};
roots.root(&root);
root
}
/// Obtain a safe reference to the wrapped JS owned-value that cannot outlive
/// the lifetime of this root.
pub fn root_ref<'b>(&'b self) -> JSRef<'b,T> {
self.jsref.clone()
}
}
#[unsafe_destructor]
impl<'b, 'a: 'b, T: Reflectable> Drop for Root<'a, 'b, T> {
fn drop(&mut self) {
self.root_list.unroot(self);
}
}
impl<'b, 'a: 'b, T: Reflectable> Deref<JSRef<'b, T>> for Root<'a, 'b, T> {
fn deref<'c>(&'c self) -> &'c JSRef<'b, T> {
&self.jsref
}
}
impl<'a, T: Reflectable> Deref<T> for JSRef<'a, T> {
fn deref<'b>(&'b self) -> &'b T {
unsafe {
&*self.ptr
}
}
}
/// Encapsulates a reference to something that is guaranteed to be alive. This is freely copyable.
pub struct JSRef<'a, T> {
ptr: *const T,
chain: ContravariantLifetime<'a>,
}
impl<'a, T> Clone for JSRef<'a, T> {
fn clone(&self) -> JSRef<'a, T> {
JSRef {
ptr: self.ptr.clone(),
chain: self.chain,
}
}
}
impl<'a, T> PartialEq for JSRef<'a, T> {
fn eq(&self, other: &JSRef<T>) -> bool {
self.ptr == other.ptr
}
}
impl<'a,T> JSRef<'a,T> {
//XXXjdm It would be lovely if this could be private.
pub unsafe fn transmute<To>(self) -> JSRef<'a, To> {
mem::transmute(self)
}
// FIXME(zwarich): It would be nice to get rid of this entirely.
pub unsafe fn transmute_borrowed<'b, To>(&'b self) -> &'b JSRef<'a, To> {
mem::transmute(self)
}
pub fn unrooted(&self) -> JS<T> {
JS {
ptr: self.ptr
}
}
}
impl<'a, T: Reflectable> JSRef<'a, T> {
pub fn extended_deref(self) -> &'a T {
unsafe {
&*self.ptr
}
}
}
impl<'a, T: Reflectable> Reflectable for JSRef<'a, T> {
fn reflector<'a>(&'a self) -> &'a Reflector {
self.deref().reflector()
}
}
/// A trait for comparing smart pointers ignoring the lifetimes
pub trait Comparable<T> {
fn equals(&self, other: T) -> bool;
}
impl<'a, 'b, T> Comparable<JSRef<'a, T>> for JSRef<'b, T> {
fn equals(&self, other: JSRef<'a, T>) -> bool {
self.ptr == other.ptr
}
}
impl<'a, 'b, T> Comparable<Option<JSRef<'a, T>>> for Option<JSRef<'b, T>> {
fn equals(&self, other: Option<JSRef<'a, T>>) -> bool {
match (*self, other) {
(Some(x), Some(y)) => x.ptr == y.ptr,
(None, None) => true,
_ => false
}
}
}