servo: Merge #11872 - Replace return_address usage for rooting with stack guards and convenience macros (from eddyb:back-to-roots); r=Ms2ger
authorEduard Burtescu <edy.burt@gmail.com>
Mon, 04 Jul 2016 11:03:35 -0700
changeset 339200 70db9045abfa70e0440944258f92294eb298000c
parent 339199 d5d8308847f78836c13cd2651e671e6b4ff99ffe
child 339201 c5ea84c9e035a26fcdd4cb0d0d8d3222d0910fc8
push id31307
push usergszorc@mozilla.com
push dateSat, 04 Feb 2017 00:59:06 +0000
treeherdermozilla-central@94079d43835f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersMs2ger
servo: Merge #11872 - Replace return_address usage for rooting with stack guards and convenience macros (from eddyb:back-to-roots); r=Ms2ger The existing `Rooted` and `RootedVec` users were migrated the the following two macros: ```rust let x = Rooted::new(cx, value); // Was changed to: rooted!(in(cx) let x = value); // Which expands to: let mut __root = Rooted::new_unrooted(value); let x = RootedGuard::new(cx, &mut __root); ``` ```rust let mut v = RootedVec::new(); v.extend(iterator); // Was changed to: rooted_vec!(let v <- iterator); // Which expands to: let mut __root = RootableVec::new(); let v = RootedVec::new(&mut __root, iterator); ``` The `rooted!` macro depends on servo/rust-mozjs#272. These APIs based on two types, a container to be rooted and a rooting guard, allow implementing both `Rooted`-style rooting and `Traceable`-based rooting in stable Rust, without abusing `return_address`. Such macros may have been tried before, but in 1.9 their hygiene is broken, they work only since 1.10. Sadly, `Rooted` is a FFI type and completely exposed, so I cannot prevent anyone from creating their own, although all fields but the value get overwritten by `RootedGuard::new` anyway. `RootableVec` OTOH is *guaranteed* to be empty when not rooted, which makes it harmless AFAICT. By fixing rust-lang/rust#34227, this PR enables Servo to build with `-Zorbit`. --- - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors - [x] These changes fix rust-lang/rust#34227 - [x] These changes do not require tests because they are not functional changes Source-Repo: https://github.com/servo/servo Source-Revision: 80cb0cf8214fd52d2884724614c40cb278ee7575
servo/components/script/devtools.rs
servo/components/script/dom/bindings/callback.rs
servo/components/script/dom/bindings/codegen/CodegenRust.py
servo/components/script/dom/bindings/error.rs
servo/components/script/dom/bindings/interface.rs
servo/components/script/dom/bindings/proxyhandler.rs
servo/components/script/dom/bindings/trace.rs
servo/components/script/dom/bindings/utils.rs
servo/components/script/dom/browsingcontext.rs
servo/components/script/dom/dedicatedworkerglobalscope.rs
servo/components/script/dom/document.rs
servo/components/script/dom/errorevent.rs
servo/components/script/dom/eventdispatcher.rs
servo/components/script/dom/eventtarget.rs
servo/components/script/dom/htmliframeelement.rs
servo/components/script/dom/htmlscriptelement.rs
servo/components/script/dom/macros.rs
servo/components/script/dom/messageevent.rs
servo/components/script/dom/node.rs
servo/components/script/dom/range.rs
servo/components/script/dom/serviceworker.rs
servo/components/script/dom/serviceworkerglobalscope.rs
servo/components/script/dom/webglrenderingcontext.rs
servo/components/script/dom/websocket.rs
servo/components/script/dom/worker.rs
servo/components/script/dom/workerglobalscope.rs
servo/components/script/dom/xmlhttprequest.rs
servo/components/script/lib.rs
servo/components/script/script_thread.rs
servo/components/script/timers.rs
servo/components/script/webdriver_handlers.rs
servo/components/servo/Cargo.lock
servo/ports/cef/Cargo.lock
--- a/servo/components/script/devtools.rs
+++ b/servo/components/script/devtools.rs
@@ -17,50 +17,50 @@ use dom::bindings::global::GlobalRef;
 use dom::bindings::inheritance::Castable;
 use dom::bindings::js::Root;
 use dom::bindings::str::DOMString;
 use dom::browsingcontext::BrowsingContext;
 use dom::element::Element;
 use dom::node::Node;
 use dom::window::Window;
 use ipc_channel::ipc::IpcSender;
-use js::jsapi::{JSAutoCompartment, ObjectClassName, RootedObject, RootedValue};
+use js::jsapi::{JSAutoCompartment, ObjectClassName};
 use js::jsval::UndefinedValue;
 use msg::constellation_msg::PipelineId;
 use script_thread::get_browsing_context;
 use std::ffi::CStr;
 use std::str;
 use style::properties::longhands::{margin_top, margin_right, margin_bottom, margin_left};
 use uuid::Uuid;
 
 #[allow(unsafe_code)]
 pub fn handle_evaluate_js(global: &GlobalRef, eval: String, reply: IpcSender<EvaluateJSReply>) {
     // global.get_cx() returns a valid `JSContext` pointer, so this is safe.
     let result = unsafe {
         let cx = global.get_cx();
         let globalhandle = global.reflector().get_jsobject();
         let _ac = JSAutoCompartment::new(cx, globalhandle.get());
-        let mut rval = RootedValue::new(cx, UndefinedValue());
+        rooted!(in(cx) let mut rval = UndefinedValue());
         global.evaluate_js_on_global_with_result(&eval, rval.handle_mut());
 
-        if rval.ptr.is_undefined() {
+        if rval.is_undefined() {
             EvaluateJSReply::VoidValue
-        } else if rval.ptr.is_boolean() {
-            EvaluateJSReply::BooleanValue(rval.ptr.to_boolean())
-        } else if rval.ptr.is_double() || rval.ptr.is_int32() {
+        } else if rval.is_boolean() {
+            EvaluateJSReply::BooleanValue(rval.to_boolean())
+        } else if rval.is_double() || rval.is_int32() {
             EvaluateJSReply::NumberValue(FromJSValConvertible::from_jsval(cx, rval.handle(), ())
                                              .unwrap())
-        } else if rval.ptr.is_string() {
-            EvaluateJSReply::StringValue(String::from(jsstring_to_str(cx, rval.ptr.to_string())))
-        } else if rval.ptr.is_null() {
+        } else if rval.is_string() {
+            EvaluateJSReply::StringValue(String::from(jsstring_to_str(cx, rval.to_string())))
+        } else if rval.is_null() {
             EvaluateJSReply::NullValue
         } else {
-            assert!(rval.ptr.is_object());
+            assert!(rval.is_object());
 
-            let obj = RootedObject::new(cx, rval.ptr.to_object());
+            rooted!(in(cx) let obj = rval.to_object());
             let class_name = CStr::from_ptr(ObjectClassName(cx, obj.handle()));
             let class_name = str::from_utf8(class_name.to_bytes()).unwrap();
 
             EvaluateJSReply::ActorValue {
                 class: class_name.to_owned(),
                 uuid: Uuid::new_v4().to_string(),
             }
         }
--- a/servo/components/script/dom/bindings/callback.rs
+++ b/servo/components/script/dom/bindings/callback.rs
@@ -4,24 +4,24 @@
 
 //! Base classes to work with IDL callbacks.
 
 use dom::bindings::error::{Error, Fallible};
 use dom::bindings::global::global_root_from_object;
 use dom::bindings::reflector::Reflectable;
 use js::jsapi::GetGlobalForObjectCrossCompartment;
 use js::jsapi::JSAutoCompartment;
-use js::jsapi::{Heap, MutableHandleObject, RootedObject, RootedValue};
+use js::jsapi::{Heap, MutableHandleObject, RootedObject};
 use js::jsapi::{IsCallable, JSContext, JSObject, JS_WrapObject};
 use js::jsapi::{JSCompartment, JS_EnterCompartment, JS_LeaveCompartment};
 use js::jsapi::{JS_GetProperty, JS_IsExceptionPending, JS_ReportPendingException};
 use js::jsval::{JSVal, UndefinedValue};
+use js::rust::RootedGuard;
 use std::default::Default;
 use std::ffi::CString;
-use std::intrinsics::return_address;
 use std::ptr;
 use std::rc::Rc;
 
 /// The exception handling used for a call.
 #[derive(Copy, Clone, PartialEq)]
 pub enum ExceptionHandling {
     /// Report any exception and don't throw it to the caller code.
     Report,
@@ -109,30 +109,30 @@ impl CallbackInterface {
     /// Should be called once this object is done moving.
     pub fn init(&mut self, callback: *mut JSObject) {
         self.object.callback.set(callback);
     }
 
     /// Returns the property with the given `name`, if it is a callable object,
     /// or an error otherwise.
     pub fn get_callable_property(&self, cx: *mut JSContext, name: &str) -> Fallible<JSVal> {
-        let mut callable = RootedValue::new(cx, UndefinedValue());
-        let obj = RootedObject::new(cx, self.callback());
+        rooted!(in(cx) let mut callable = UndefinedValue());
+        rooted!(in(cx) let obj = self.callback());
         unsafe {
             let c_name = CString::new(name).unwrap();
             if !JS_GetProperty(cx, obj.handle(), c_name.as_ptr(), callable.handle_mut()) {
                 return Err(Error::JSFailed);
             }
 
-            if !callable.ptr.is_object() || !IsCallable(callable.ptr.to_object()) {
+            if !callable.is_object() || !IsCallable(callable.to_object()) {
                 return Err(Error::Type(format!("The value of the {} property is not callable",
                                                name)));
             }
         }
-        Ok(callable.ptr)
+        Ok(callable.get())
     }
 }
 
 /// Wraps the reflector for `p` into the compartment of `cx`.
 pub fn wrap_call_this_object<T: Reflectable>(cx: *mut JSContext,
                                              p: &T,
                                              rval: MutableHandleObject) {
     rval.set(p.reflector().get_jsobject().get());
@@ -142,62 +142,61 @@ pub fn wrap_call_this_object<T: Reflecta
         if !JS_WrapObject(cx, rval) {
             rval.set(ptr::null_mut());
         }
     }
 }
 
 /// A class that performs whatever setup we need to safely make a call while
 /// this class is on the stack. After `new` returns, the call is safe to make.
-pub struct CallSetup {
+pub struct CallSetup<'a> {
     /// The compartment for reporting exceptions.
-    /// As a RootedObject, this must be the first field in order to
-    /// determine the final address on the stack correctly.
-    exception_compartment: RootedObject,
+    exception_compartment: RootedGuard<'a, *mut JSObject>,
     /// The `JSContext` used for the call.
     cx: *mut JSContext,
     /// The compartment we were in before the call.
     old_compartment: *mut JSCompartment,
     /// The exception handling used for the call.
     handling: ExceptionHandling,
 }
 
-impl CallSetup {
+impl<'a> CallSetup<'a> {
     /// Performs the setup needed to make a call.
     #[allow(unrooted_must_root)]
-    pub fn new<T: CallbackContainer>(callback: &T, handling: ExceptionHandling) -> CallSetup {
+    pub fn new<T: CallbackContainer>(exception_compartment: &'a mut RootedObject,
+                                     callback: &T,
+                                     handling: ExceptionHandling)
+                                     -> CallSetup<'a> {
         let global = unsafe { global_root_from_object(callback.callback()) };
         let cx = global.r().get_cx();
 
-        let exception_compartment = unsafe {
+        exception_compartment.ptr = unsafe {
             GetGlobalForObjectCrossCompartment(callback.callback())
         };
         CallSetup {
-            exception_compartment: RootedObject::new_with_addr(cx,
-                                                               exception_compartment,
-                                                               unsafe { return_address() }),
+            exception_compartment: RootedGuard::new(cx, exception_compartment),
             cx: cx,
             old_compartment: unsafe { JS_EnterCompartment(cx, callback.callback()) },
             handling: handling,
         }
     }
 
     /// Returns the `JSContext` used for the call.
     pub fn get_context(&self) -> *mut JSContext {
         self.cx
     }
 }
 
-impl Drop for CallSetup {
+impl<'a> Drop for CallSetup<'a> {
     fn drop(&mut self) {
         unsafe {
             JS_LeaveCompartment(self.cx, self.old_compartment);
         }
         let need_to_deal_with_exception = self.handling == ExceptionHandling::Report &&
                                           unsafe { JS_IsExceptionPending(self.cx) };
         if need_to_deal_with_exception {
             unsafe {
-                let _ac = JSAutoCompartment::new(self.cx, self.exception_compartment.ptr);
+                let _ac = JSAutoCompartment::new(self.cx, *self.exception_compartment);
                 JS_ReportPendingException(self.cx);
             }
         }
     }
 }
--- a/servo/components/script/dom/bindings/codegen/CodegenRust.py
+++ b/servo/components/script/dom/bindings/codegen/CodegenRust.py
@@ -1161,33 +1161,32 @@ class CGArgumentConverter(CGThing):
             assert argument.optional
             variadicConversion = {
                 "val": string.Template("${args}.get(variadicArg)").substitute(replacer),
             }
             innerConverter = [instantiateJSToNativeConversionTemplate(
                 template, variadicConversion, declType, "slot")]
 
             arg = "arg%d" % index
-
             if argument.type.isGeckoInterface():
-                vec = "RootedVec::new()"
+                init = "rooted_vec!(let mut %s)" % arg
                 innerConverter.append(CGGeneric("%s.push(JS::from_ref(&*slot));" % arg))
             else:
-                vec = "vec![]"
+                init = "let mut %s = vec![]" % arg
                 innerConverter.append(CGGeneric("%s.push(slot);" % arg))
             inner = CGIndenter(CGList(innerConverter, "\n"), 8).define()
 
             self.converter = CGGeneric("""\
-let mut %(arg)s = %(vec)s;
+%(init)s;
 if %(argc)s > %(index)s {
     %(arg)s.reserve(%(argc)s as usize - %(index)s);
     for variadicArg in %(index)s..%(argc)s {
 %(inner)s
     }
-}""" % {'arg': arg, 'argc': argc, 'index': index, 'inner': inner, 'vec': vec})
+}""" % {'arg': arg, 'argc': argc, 'index': index, 'inner': inner, 'init': init})
 
     def define(self):
         return self.converter.define()
 
 
 def wrapForType(jsvalRef, result='result', successCode='return true;', pre=''):
     """
     Reflect a Rust value into JS.
@@ -2273,44 +2272,43 @@ class CGConstructorEnabled(CGAbstractMet
 
 
 def CreateBindingJSObject(descriptor, parent=None):
     create = "let raw = Box::into_raw(object);\nlet _rt = RootedTraceable::new(&*raw);\n"
     if descriptor.proxy:
         assert not descriptor.isGlobal()
         create += """
 let handler = RegisterBindings::proxy_handlers[PrototypeList::Proxies::%s as usize];
-let private = RootedValue::new(cx, PrivateValue(raw as *const libc::c_void));
+rooted!(in(cx) let private = PrivateValue(raw as *const libc::c_void));
 let obj = NewProxyObject(cx, handler,
                          private.handle(),
-                         proto.ptr, %s.get(),
+                         proto.get(), %s.get(),
                          ptr::null_mut(), ptr::null_mut());
 assert!(!obj.is_null());
-let obj = RootedObject::new(cx, obj);\
+rooted!(in(cx) let obj = obj);\
 """ % (descriptor.name, parent)
     elif descriptor.isGlobal():
-        create += ("let obj = RootedObject::new(\n"
-                   "    cx,\n"
+        create += ("rooted!(in(cx) let obj =\n"
                    "    create_dom_global(\n"
                    "        cx,\n"
                    "        &Class.base as *const js::jsapi::Class as *const JSClass,\n"
                    "        raw as *const libc::c_void,\n"
                    "        Some(%s))\n"
                    ");\n"
-                   "assert!(!obj.ptr.is_null());" % TRACE_HOOK_NAME)
+                   "assert!(!obj.is_null());" % TRACE_HOOK_NAME)
     else:
-        create += ("let obj = RootedObject::new(cx, JS_NewObjectWithGivenProto(\n"
+        create += ("rooted!(in(cx) let obj = JS_NewObjectWithGivenProto(\n"
                    "    cx, &Class.base as *const js::jsapi::Class as *const JSClass, proto.handle()));\n"
-                   "assert!(!obj.ptr.is_null());\n"
+                   "assert!(!obj.is_null());\n"
                    "\n"
-                   "JS_SetReservedSlot(obj.ptr, DOM_OBJECT_SLOT,\n"
+                   "JS_SetReservedSlot(obj.get(), DOM_OBJECT_SLOT,\n"
                    "                   PrivateValue(raw as *const libc::c_void));")
     if descriptor.weakReferenceable:
         create += """
-JS_SetReservedSlot(obj.ptr, DOM_WEAK_SLOT, PrivateValue(ptr::null()));"""
+JS_SetReservedSlot(obj.get(), DOM_WEAK_SLOT, PrivateValue(ptr::null()));"""
     return create
 
 
 def InitUnforgeablePropertiesOnHolder(descriptor, properties):
     """
     Define the unforgeable properties on the unforgeable holder for
     the interface represented by descriptor.
 
@@ -2339,33 +2337,33 @@ def CopyUnforgeablePropertiesToInstance(
     if not descriptor.hasUnforgeableMembers:
         return ""
     copyCode = ""
 
     # For proxies, we want to define on the expando object, not directly on the
     # reflector, so we can make sure we don't get confused by named getters.
     if descriptor.proxy:
         copyCode += """\
-let expando = RootedObject::new(cx, ensure_expando_object(cx, obj.handle()));
+rooted!(in(cx) let expando = ensure_expando_object(cx, obj.handle()));
 """
         obj = "expando"
     else:
         obj = "obj"
 
     # We can't do the fast copy for globals, because we can't allocate the
     # unforgeable holder for those with the right JSClass. Luckily, there
     # aren't too many globals being created.
     if descriptor.isGlobal():
         copyFunc = "JS_CopyPropertiesFrom"
     else:
         copyFunc = "JS_InitializePropertiesFromCompatibleNativeObject"
     copyCode += """\
-let mut unforgeable_holder = RootedObject::new(cx, ptr::null_mut());
+rooted!(in(cx) let mut unforgeable_holder = ptr::null_mut());
 unforgeable_holder.handle_mut().set(
-    JS_GetReservedSlot(proto.ptr, DOM_PROTO_UNFORGEABLE_HOLDER_SLOT).to_object());
+    JS_GetReservedSlot(proto.get(), DOM_PROTO_UNFORGEABLE_HOLDER_SLOT).to_object());
 assert!(%(copyFunc)s(cx, %(obj)s.handle(), unforgeable_holder.handle()));
 """ % {'copyFunc': copyFunc, 'obj': obj}
 
     return copyCode
 
 
 class CGWrapMethod(CGAbstractMethod):
     """
@@ -2388,35 +2386,35 @@ class CGWrapMethod(CGAbstractMethod):
         unforgeable = CopyUnforgeablePropertiesToInstance(self.descriptor)
         if not self.descriptor.isGlobal():
             create = CreateBindingJSObject(self.descriptor, "scope")
             return CGGeneric("""\
 let scope = scope.reflector().get_jsobject();
 assert!(!scope.get().is_null());
 assert!(((*JS_GetClass(scope.get())).flags & JSCLASS_IS_GLOBAL) != 0);
 
-let mut proto = RootedObject::new(cx, ptr::null_mut());
+rooted!(in(cx) let mut proto = ptr::null_mut());
 let _ac = JSAutoCompartment::new(cx, scope.get());
 GetProtoObject(cx, scope, proto.handle_mut());
-assert!(!proto.ptr.is_null());
+assert!(!proto.is_null());
 
 %(createObject)s
 
 %(copyUnforgeable)s
-(*raw).init_reflector(obj.ptr);
+(*raw).init_reflector(obj.get());
 
 Root::from_ref(&*raw)""" % {'copyUnforgeable': unforgeable, 'createObject': create})
         else:
             create = CreateBindingJSObject(self.descriptor)
             return CGGeneric("""\
 %(createObject)s
-(*raw).init_reflector(obj.ptr);
-
-let _ac = JSAutoCompartment::new(cx, obj.ptr);
-let mut proto = RootedObject::new(cx, ptr::null_mut());
+(*raw).init_reflector(obj.get());
+
+let _ac = JSAutoCompartment::new(cx, obj.get());
+rooted!(in(cx) let mut proto = ptr::null_mut());
 GetProtoObject(cx, obj.handle(), proto.handle_mut());
 JS_SetPrototype(cx, obj.handle(), proto.handle());
 
 %(copyUnforgeable)s
 
 Root::from_ref(&*raw)\
 """ % {'copyUnforgeable': unforgeable, 'createObject': create})
 
@@ -2520,106 +2518,106 @@ class CGCreateInterfaceObjectsMethod(CGA
         self.properties = properties
         self.haveUnscopables = haveUnscopables
 
     def definition_body(self):
         name = self.descriptor.interface.identifier.name
         if self.descriptor.interface.isCallback():
             assert not self.descriptor.interface.ctor() and self.descriptor.interface.hasConstants()
             return CGGeneric("""\
-let mut interface = RootedObject::new(cx, ptr::null_mut());
+rooted!(in(cx) let mut interface = ptr::null_mut());
 create_callback_interface_object(cx, global, sConstants, %(name)s, interface.handle_mut());
-assert!(!interface.ptr.is_null());
+assert!(!interface.is_null());
 assert!((*cache)[PrototypeList::Constructor::%(id)s as usize].is_null());
-(*cache)[PrototypeList::Constructor::%(id)s as usize] = interface.ptr;
+(*cache)[PrototypeList::Constructor::%(id)s as usize] = interface.get();
 <*mut JSObject>::post_barrier((*cache).as_mut_ptr().offset(PrototypeList::Constructor::%(id)s as isize),
                               ptr::null_mut(),
-                              interface.ptr);
+                              interface.get());
 """ % {"id": name, "name": str_to_const_array(name)})
 
         if len(self.descriptor.prototypeChain) == 1:
             if self.descriptor.interface.getExtendedAttribute("ExceptionClass"):
-                getPrototypeProto = "prototype_proto.ptr = JS_GetErrorPrototype(cx)"
+                getPrototypeProto = "prototype_proto.set(JS_GetErrorPrototype(cx))"
             else:
-                getPrototypeProto = "prototype_proto.ptr = JS_GetObjectPrototype(cx, global)"
+                getPrototypeProto = "prototype_proto.set(JS_GetObjectPrototype(cx, global))"
         else:
             getPrototypeProto = ("%s::GetProtoObject(cx, global, prototype_proto.handle_mut())" %
                                  toBindingNamespace(self.descriptor.prototypeChain[-2]))
 
         code = [CGGeneric("""\
-let mut prototype_proto = RootedObject::new(cx, ptr::null_mut());
+rooted!(in(cx) let mut prototype_proto = ptr::null_mut());
 %s;
-assert!(!prototype_proto.ptr.is_null());""" % getPrototypeProto)]
+assert!(!prototype_proto.is_null());""" % getPrototypeProto)]
 
         properties = {
             "id": name,
             "unscopables": "unscopable_names" if self.haveUnscopables else "&[]"
         }
         for arrayName in self.properties.arrayNames():
             array = getattr(self.properties, arrayName)
             if array.length():
                 properties[arrayName] = array.variableName()
             else:
                 properties[arrayName] = "&[]"
 
         code.append(CGGeneric("""
-let mut prototype = RootedObject::new(cx, ptr::null_mut());
+rooted!(in(cx) let mut prototype = ptr::null_mut());
 create_interface_prototype_object(cx,
                                   prototype_proto.handle(),
                                   &PrototypeClass,
                                   %(methods)s,
                                   %(attrs)s,
                                   %(consts)s,
                                   %(unscopables)s,
                                   prototype.handle_mut());
-assert!(!prototype.ptr.is_null());
+assert!(!prototype.is_null());
 assert!((*cache)[PrototypeList::ID::%(id)s as usize].is_null());
-(*cache)[PrototypeList::ID::%(id)s as usize] = prototype.ptr;
+(*cache)[PrototypeList::ID::%(id)s as usize] = prototype.get();
 <*mut JSObject>::post_barrier((*cache).as_mut_ptr().offset(PrototypeList::ID::%(id)s as isize),
                               ptr::null_mut(),
-                              prototype.ptr);
+                              prototype.get());
 """ % properties))
 
         if self.descriptor.interface.hasInterfaceObject():
             properties["name"] = str_to_const_array(name)
             if self.descriptor.interface.ctor():
                 properties["length"] = methodLength(self.descriptor.interface.ctor())
             else:
                 properties["length"] = 0
             if self.descriptor.interface.parent:
                 parentName = toBindingNamespace(self.descriptor.getParentName())
                 code.append(CGGeneric("""
-let mut interface_proto = RootedObject::new(cx, ptr::null_mut());
+rooted!(in(cx) let mut interface_proto = ptr::null_mut());
 %s::GetConstructorObject(cx, global, interface_proto.handle_mut());""" % parentName))
             else:
                 code.append(CGGeneric("""
-let interface_proto = RootedObject::new(cx, JS_GetFunctionPrototype(cx, global));"""))
+rooted!(in(cx) let interface_proto = JS_GetFunctionPrototype(cx, global));"""))
             code.append(CGGeneric("""\
-assert!(!interface_proto.ptr.is_null());
-
-let mut interface = RootedObject::new(cx, ptr::null_mut());
+assert!(!interface_proto.is_null());
+
+rooted!(in(cx) let mut interface = ptr::null_mut());
 create_noncallback_interface_object(cx,
                                     global,
                                     interface_proto.handle(),
                                     &InterfaceObjectClass,
                                     %(static_methods)s,
                                     %(static_attrs)s,
                                     %(consts)s,
                                     prototype.handle(),
                                     %(name)s,
                                     %(length)s,
                                     interface.handle_mut());
-assert!(!interface.ptr.is_null());""" % properties))
+assert!(!interface.is_null());""" % properties))
             if self.descriptor.hasDescendants():
                 code.append(CGGeneric("""\
 assert!((*cache)[PrototypeList::Constructor::%(id)s as usize].is_null());
-(*cache)[PrototypeList::Constructor::%(id)s as usize] = interface.ptr;
+(*cache)[PrototypeList::Constructor::%(id)s as usize] = interface.get();
 <*mut JSObject>::post_barrier((*cache).as_mut_ptr().offset(PrototypeList::Constructor::%(id)s as isize),
                               ptr::null_mut(),
-                              interface.ptr);
+                              interface.get());
 """ % properties))
 
         constructors = self.descriptor.interface.namedConstructors
         if constructors:
             decl = "let named_constructors: [(NonNullJSNative, &'static [u8], u32); %d]" % len(constructors)
             specs = []
             for constructor in constructors:
                 hook = CONSTRUCT_HOOK_NAME + "_" + constructor.identifier.name
@@ -2644,25 +2642,25 @@ assert!((*cache)[PrototypeList::Construc
             # the prototype.
             if self.descriptor.proxy or self.descriptor.isGlobal():
                 holderClass = "ptr::null()"
                 holderProto = "HandleObject::null()"
             else:
                 holderClass = "&Class.base as *const js::jsapi::Class as *const JSClass"
                 holderProto = "prototype.handle()"
             code.append(CGGeneric("""
-let mut unforgeable_holder = RootedObject::new(cx, ptr::null_mut());
+rooted!(in(cx) let mut unforgeable_holder = ptr::null_mut());
 unforgeable_holder.handle_mut().set(
     JS_NewObjectWithoutMetadata(cx, %(holderClass)s, %(holderProto)s));
-assert!(!unforgeable_holder.ptr.is_null());
+assert!(!unforgeable_holder.is_null());
 """ % {'holderClass': holderClass, 'holderProto': holderProto}))
             code.append(InitUnforgeablePropertiesOnHolder(self.descriptor, self.properties))
             code.append(CGGeneric("""\
-JS_SetReservedSlot(prototype.ptr, DOM_PROTO_UNFORGEABLE_HOLDER_SLOT,
-                   ObjectValue(&*unforgeable_holder.ptr))"""))
+JS_SetReservedSlot(prototype.get(), DOM_PROTO_UNFORGEABLE_HOLDER_SLOT,
+                   ObjectValue(&*unforgeable_holder.get()))"""))
 
         return CGList(code, "\n")
 
 
 class CGGetPerInterfaceObject(CGAbstractMethod):
     """
     A method for getting a per-interface object (a prototype object or interface
     constructor object).
@@ -2821,19 +2819,19 @@ class CGDefineDOMInterfaceMethod(CGAbstr
                 return "if !ConstructorEnabled(cx, global) { return; }"
         if self.descriptor.interface.isCallback():
             function = "GetConstructorObject"
         else:
             function = "GetProtoObject"
         return CGGeneric("""\
 assert!(!global.get().is_null());
 %s
-let mut proto = RootedObject::new(cx, ptr::null_mut());
+rooted!(in(cx) let mut proto = ptr::null_mut());
 %s(cx, global, proto.handle_mut());
-assert!(!proto.ptr.is_null());""" % (getCheck(self.descriptor), function))
+assert!(!proto.is_null());""" % (getCheck(self.descriptor), function))
 
 
 def needCx(returnType, arguments, considerTypes):
     return (considerTypes and
             (typeNeedsCx(returnType, True) or
              any(typeNeedsCx(a.type) for a in arguments)))
 
 
@@ -3272,25 +3270,25 @@ class CGSpecializedForwardingSetter(CGSp
 
     def definition_body(self):
         attrName = self.attr.identifier.name
         forwardToAttrName = self.attr.getExtendedAttribute("PutForwards")[0]
         # JS_GetProperty and JS_SetProperty can only deal with ASCII
         assert all(ord(c) < 128 for c in attrName)
         assert all(ord(c) < 128 for c in forwardToAttrName)
         return CGGeneric("""\
-let mut v = RootedValue::new(cx, UndefinedValue());
+rooted!(in(cx) let mut v = UndefinedValue());
 if !JS_GetProperty(cx, obj, %s as *const u8 as *const libc::c_char, v.handle_mut()) {
     return false;
 }
-if !v.ptr.is_object() {
+if !v.is_object() {
     throw_type_error(cx, "Value.%s is not an object.");
     return false;
 }
-let target_obj = RootedObject::new(cx, v.ptr.to_object());
+rooted!(in(cx) let target_obj = v.to_object());
 JS_SetProperty(cx, target_obj.handle(), %s as *const u8 as *const libc::c_char, args.get(0))
 """ % (str_to_const_array(attrName), attrName, str_to_const_array(forwardToAttrName)))
 
 
 class CGMemberJITInfo(CGThing):
     """
     A class for generating the JITInfo for a property that points to
     our specialized getter and setter.
@@ -4337,17 +4335,17 @@ class CGProxySpecialOperation(CGPerSigna
             template = info.template
             declType = info.declType
 
             templateValues = {
                 "val": "value.handle()",
             }
             self.cgRoot.prepend(instantiateJSToNativeConversionTemplate(
                 template, templateValues, declType, argument.identifier.name))
-            self.cgRoot.prepend(CGGeneric("let value = RootedValue::new(cx, desc.get().value);"))
+            self.cgRoot.prepend(CGGeneric("rooted!(in(cx) let value = desc.value);"))
         elif operation.isGetter():
             self.cgRoot.prepend(CGGeneric("let mut found = false;"))
 
     def getArguments(self):
         def process(arg):
             argVal = arg.identifier.name
             if arg.type.isGeckoInterface() and not arg.type.unroll().inner.isCallback():
                 argVal += ".r()"
@@ -4445,48 +4443,49 @@ class CGProxyUnwrap(CGAbstractMethod):
                                   alwaysInline=True, unsafe=True)
 
     def definition_body(self):
         return CGGeneric("""\
 /*if (xpc::WrapperFactory::IsXrayWrapper(obj)) {
     obj = js::UnwrapObject(obj);
 }*/
 //MOZ_ASSERT(IsProxy(obj));
-let box_ = GetProxyPrivate(*obj.ptr).to_private() as *const %s;
+let box_ = GetProxyPrivate(obj.get()).to_private() as *const %s;
 return box_;""" % self.descriptor.concreteType)
 
 
 class CGDOMJSProxyHandler_getOwnPropertyDescriptor(CGAbstractExternMethod):
     def __init__(self, descriptor):
         args = [Argument('*mut JSContext', 'cx'), Argument('HandleObject', 'proxy'),
                 Argument('HandleId', 'id'),
-                Argument('MutableHandle<PropertyDescriptor>', 'desc')]
+                Argument('MutableHandle<PropertyDescriptor>', 'desc', mutable=True)]
         CGAbstractExternMethod.__init__(self, descriptor, "getOwnPropertyDescriptor",
                                         "bool", args)
         self.descriptor = descriptor
 
     def getBody(self):
         indexedGetter = self.descriptor.operations['IndexedGetter']
         indexedSetter = self.descriptor.operations['IndexedSetter']
 
         get = ""
         if indexedGetter or indexedSetter:
             get = "let index = get_array_index_from_id(cx, id);\n"
 
         if indexedGetter:
             attrs = "JSPROP_ENUMERATE"
             if self.descriptor.operations['IndexedSetter'] is None:
                 attrs += " | JSPROP_READONLY"
-            fillDescriptor = ("desc.get().value = result_root.ptr;\n"
-                              "fill_property_descriptor(&mut *desc.ptr, *proxy.ptr, %s);\n"
+            # FIXME(#11868) Should assign to desc.value, desc.get() is a copy.
+            fillDescriptor = ("desc.get().value = result_root.get();\n"
+                              "fill_property_descriptor(&mut desc, proxy.get(), %s);\n"
                               "return true;" % attrs)
             templateValues = {
                 'jsvalRef': 'result_root.handle_mut()',
                 'successCode': fillDescriptor,
-                'pre': 'let mut result_root = RootedValue::new(cx, UndefinedValue());'
+                'pre': 'rooted!(in(cx) let mut result_root = UndefinedValue());'
             }
             get += ("if let Some(index) = index {\n" +
                     "    let this = UnwrapProxy(proxy);\n" +
                     "    let this = &*this;\n" +
                     CGIndenter(CGProxyIndexedGetter(self.descriptor, templateValues)).define() + "\n" +
                     "}\n")
 
         namedGetter = self.descriptor.operations['NamedGetter']
@@ -4495,44 +4494,46 @@ class CGDOMJSProxyHandler_getOwnProperty
             if not self.descriptor.interface.getExtendedAttribute("LegacyUnenumerableNamedProperties"):
                 attrs.append("JSPROP_ENUMERATE")
             if self.descriptor.operations['NamedSetter'] is None:
                 attrs.append("JSPROP_READONLY")
             if attrs:
                 attrs = " | ".join(attrs)
             else:
                 attrs = "0"
-            fillDescriptor = ("desc.get().value = result_root.ptr;\n"
-                              "fill_property_descriptor(&mut *desc.ptr, *proxy.ptr, %s);\n"
+            # FIXME(#11868) Should assign to desc.value, desc.get() is a copy.
+            fillDescriptor = ("desc.get().value = result_root.get();\n"
+                              "fill_property_descriptor(&mut desc, proxy.get(), %s);\n"
                               "return true;" % attrs)
             templateValues = {
                 'jsvalRef': 'result_root.handle_mut()',
                 'successCode': fillDescriptor,
-                'pre': 'let mut result_root = RootedValue::new(cx, UndefinedValue());'
+                'pre': 'rooted!(in(cx) let mut result_root = UndefinedValue());'
             }
             # Once we start supporting OverrideBuiltins we need to make
             # ResolveOwnProperty or EnumerateOwnProperties filter out named
             # properties that shadow prototype properties.
             namedGet = ("\n" +
                         "if RUST_JSID_IS_STRING(id) && !has_property_on_prototype(cx, proxy, id) {\n" +
                         CGIndenter(CGProxyNamedGetter(self.descriptor, templateValues)).define() + "\n" +
                         "}\n")
         else:
             namedGet = ""
 
+        # FIXME(#11868) Should assign to desc.obj, desc.get() is a copy.
         return get + """\
-let expando = RootedObject::new(cx, get_expando_object(proxy));
+rooted!(in(cx) let expando = get_expando_object(proxy));
 //if (!xpc::WrapperFactory::IsXrayWrapper(proxy) && (expando = GetExpandoObject(proxy))) {
-if !expando.ptr.is_null() {
+if !expando.is_null() {
     if !JS_GetPropertyDescriptorById(cx, expando.handle(), id, desc) {
         return false;
     }
-    if !desc.get().obj.is_null() {
+    if !desc.obj.is_null() {
         // Pretend the property lives on the wrapper.
-        desc.get().obj = *proxy.ptr;
+        desc.get().obj = proxy.get();
         return true;
     }
 }
 """ + namedGet + """\
 desc.get().obj = ptr::null_mut();
 return true;"""
 
     def definition_body(self):
@@ -4632,39 +4633,39 @@ class CGDOMJSProxyHandler_ownPropertyKey
             """
             let unwrapped_proxy = UnwrapProxy(proxy);
             """)
 
         if self.descriptor.operations['IndexedGetter']:
             body += dedent(
                 """
                 for i in 0..(*unwrapped_proxy).Length() {
-                    let rooted_jsid = RootedId::new(cx, int_to_jsid(i as i32));
+                    rooted!(in(cx) let rooted_jsid = int_to_jsid(i as i32));
                     AppendToAutoIdVector(props, rooted_jsid.handle().get());
                 }
                 """)
 
         if self.descriptor.operations['NamedGetter']:
             body += dedent(
                 """
                 for name in (*unwrapped_proxy).SupportedPropertyNames() {
                     let cstring = CString::new(name).unwrap();
                     let jsstring = JS_AtomizeAndPinString(cx, cstring.as_ptr());
-                    let rooted = RootedString::new(cx, jsstring);
+                    rooted!(in(cx) let rooted = jsstring);
                     let jsid = INTERNED_STRING_TO_JSID(cx, rooted.handle().get());
-                    let rooted_jsid = RootedId::new(cx, jsid);
+                    rooted!(in(cx) let rooted_jsid = jsid);
                     AppendToAutoIdVector(props, rooted_jsid.handle().get());
                 }
                 """)
 
         body += dedent(
             """
             let expando = get_expando_object(proxy);
             if !expando.is_null() {
-                let rooted_expando = RootedObject::new(cx, expando);
+                rooted!(in(cx) let rooted_expando = expando);
                 GetPropertyKeys(cx, rooted_expando.handle(), JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, props);
             }
 
             return true;
             """)
 
         return body
 
@@ -4688,26 +4689,26 @@ class CGDOMJSProxyHandler_getOwnEnumerab
             """
             let unwrapped_proxy = UnwrapProxy(proxy);
             """)
 
         if self.descriptor.operations['IndexedGetter']:
             body += dedent(
                 """
                 for i in 0..(*unwrapped_proxy).Length() {
-                    let rooted_jsid = RootedId::new(cx, int_to_jsid(i as i32));
+                    rooted!(in(cx) let rooted_jsid = int_to_jsid(i as i32));
                     AppendToAutoIdVector(props, rooted_jsid.handle().get());
                 }
                 """)
 
         body += dedent(
             """
             let expando = get_expando_object(proxy);
             if !expando.is_null() {
-                let rooted_expando = RootedObject::new(cx, expando);
+                rooted!(in(cx) let rooted_expando = expando);
                 GetPropertyKeys(cx, rooted_expando.handle(), JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, props);
             }
 
             return true;
             """)
 
         return body
 
@@ -4743,18 +4744,18 @@ class CGDOMJSProxyHandler_hasOwn(CGAbstr
                      "    *bp = found;\n"
                      "    return true;\n"
                      "}\n" +
                      "\n")
         else:
             named = ""
 
         return indexed + """\
-let expando = RootedObject::new(cx, get_expando_object(proxy));
-if !expando.ptr.is_null() {
+rooted!(in(cx) let expando = get_expando_object(proxy));
+if !expando.is_null() {
     let ok = JS_HasPropertyById(cx, expando.handle(), id, bp);
     if !ok || *bp {
         return ok;
     }
 }
 """ + named + """\
 *bp = false;
 return true;"""
@@ -4768,18 +4769,18 @@ class CGDOMJSProxyHandler_get(CGAbstract
         args = [Argument('*mut JSContext', 'cx'), Argument('HandleObject', 'proxy'),
                 Argument('HandleValue', 'receiver'), Argument('HandleId', 'id'),
                 Argument('MutableHandleValue', 'vp')]
         CGAbstractExternMethod.__init__(self, descriptor, "get", "bool", args)
         self.descriptor = descriptor
 
     def getBody(self):
         getFromExpando = """\
-let expando = RootedObject::new(cx, get_expando_object(proxy));
-if !expando.ptr.is_null() {
+rooted!(in(cx) let expando = get_expando_object(proxy));
+if !expando.is_null() {
     let mut hasProp = false;
     if !JS_HasPropertyById(cx, expando.handle(), id, &mut hasProp) {
         return false;
     }
 
     if hasProp {
         return JS_ForwardGetPropertyTo(cx, expando.handle(), id, receiver, vp);
     }
@@ -4824,17 +4825,17 @@ let mut found = false;
 if !get_property_on_prototype(cx, proxy, id, &mut found, vp) {
     return false;
 }
 
 if found {
     return true;
 }
 %s
-*vp.ptr = UndefinedValue();
+vp.set(UndefinedValue());
 return true;""" % (getIndexedOrExpando, getNamed)
 
     def definition_body(self):
         return CGGeneric(self.getBody())
 
 
 class CGDOMJSProxyHandler_className(CGAbstractExternMethod):
     def __init__(self, descriptor):
@@ -5292,17 +5293,17 @@ class CGDictionary(CGThing):
 
         def memberInit(memberInfo):
             member, _ = memberInfo
             name = self.makeMemberName(member.identifier.name)
             conversion = self.getMemberConversion(memberInfo, member.type)
             return CGGeneric("%s: %s,\n" % (name, conversion.define()))
 
         def varInsert(varName, dictionaryName):
-            insertion = ("let mut %s_js = RootedValue::new(cx, UndefinedValue());\n"
+            insertion = ("rooted!(in(cx) let mut %s_js = UndefinedValue());\n"
                          "%s.to_jsval(cx, %s_js.handle_mut());\n"
                          "set_dictionary_property(cx, obj.handle(), \"%s\", %s_js.handle()).unwrap();"
                          % (varName, varName, varName, dictionaryName, varName))
             return CGGeneric(insertion)
 
         def memberInsert(memberInfo):
             member, _ = memberInfo
             name = self.makeMemberName(member.identifier.name)
@@ -5319,23 +5320,24 @@ class CGDictionary(CGThing):
 
         return string.Template(
             "impl ${selfName} {\n"
             "    pub unsafe fn empty(cx: *mut JSContext) -> ${selfName} {\n"
             "        ${selfName}::new(cx, HandleValue::null()).unwrap()\n"
             "    }\n"
             "    pub unsafe fn new(cx: *mut JSContext, val: HandleValue) -> Result<${selfName}, ()> {\n"
             "        let object = if val.get().is_null_or_undefined() {\n"
-            "            RootedObject::new(cx, ptr::null_mut())\n"
+            "            ptr::null_mut()\n"
             "        } else if val.get().is_object() {\n"
-            "            RootedObject::new(cx, val.get().to_object())\n"
+            "            val.get().to_object()\n"
             "        } else {\n"
             "            throw_type_error(cx, \"Value not an object.\");\n"
             "            return Err(());\n"
             "        };\n"
+            "        rooted!(in(cx) let object = object);\n"
             "        Ok(${selfName} {\n"
             "${initParent}"
             "${initMembers}"
             "        })\n"
             "    }\n"
             "}\n"
             "\n"
             "impl FromJSValConvertible for ${selfName} {\n"
@@ -5343,19 +5345,19 @@ class CGDictionary(CGThing):
             "    unsafe fn from_jsval(cx: *mut JSContext, value: HandleValue, _option: ())\n"
             "                         -> Result<${selfName}, ()> {\n"
             "        ${selfName}::new(cx, value)\n"
             "    }\n"
             "}\n"
             "\n"
             "impl ToJSValConvertible for ${selfName} {\n"
             "    unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) {\n"
-            "        let obj = RootedObject::new(cx, JS_NewObject(cx, ptr::null()));\n"
+            "        rooted!(in(cx) let obj = JS_NewObject(cx, ptr::null()));\n"
             "${insertMembers}"
-            "        rval.set(ObjectOrNullValue(obj.ptr))\n"
+            "        rval.set(ObjectOrNullValue(obj.get()))\n"
             "    }\n"
             "}\n").substitute({
                 "selfName": self.makeClassName(d),
                 "initParent": CGIndenter(CGGeneric(initParent), indentLevel=12).define(),
                 "initMembers": CGIndenter(memberInits, indentLevel=12).define(),
                 "insertMembers": CGIndenter(memberInserts, indentLevel=8).define(),
             })
 
@@ -5395,17 +5397,17 @@ class CGDictionary(CGThing):
             default = ("throw_type_error(cx, \"Missing required member \\\"%s\\\".\");\n"
                        "return Err(());") % member.identifier.name
         elif not default:
             default = "None"
             conversion = "Some(%s)" % conversion
 
         conversion = (
             "{\n"
-            "let mut rval = RootedValue::new(cx, UndefinedValue());\n"
+            "rooted!(in(cx) let mut rval = UndefinedValue());\n"
             "match try!(get_dictionary_property(cx, object.handle(), \"%s\", rval.handle_mut())) {\n"
             "    true => {\n"
             "%s\n"
             "    },\n"
             "    false => {\n"
             "%s\n"
             "    },\n"
             "}\n}") % (member.identifier.name, indent(conversion), indent(default))
@@ -5543,18 +5545,18 @@ class CGBindingRoot(CGThing):
             'js::jsapi::{JS_AtomizeAndPinString, JS_IsExceptionPending, JS_NewObject, JS_NewObjectWithGivenProto}',
             'js::jsapi::{JS_NewObjectWithoutMetadata, JS_NewStringCopyZ, JS_SetProperty}',
             'js::jsapi::{JS_SetPrototype, JS_SetReservedSlot, JS_WrapValue, JSAutoCompartment}',
             'js::jsapi::{JSContext, JSClass, JSFreeOp, JSFunctionSpec}',
             'js::jsapi::{JSJitGetterCallArgs, JSJitInfo, JSJitMethodCallArgs, JSJitSetterCallArgs}',
             'js::jsapi::{JSNative, JSObject, JSNativeWrapper, JSPropertySpec}',
             'js::jsapi::{JSString, JSTracer, JSType, JSTypedMethodJitInfo, JSValueType}',
             'js::jsapi::{ObjectOpResult, JSJitInfo_OpType, MutableHandle, MutableHandleObject}',
-            'js::jsapi::{MutableHandleValue, PropertyDescriptor, RootedId, RootedObject}',
-            'js::jsapi::{RootedString, RootedValue, SymbolCode, jsid}',
+            'js::jsapi::{MutableHandleValue, PropertyDescriptor, RootedObject}',
+            'js::jsapi::{SymbolCode, jsid}',
             'js::jsval::JSVal',
             'js::jsval::{ObjectValue, ObjectOrNullValue, PrivateValue}',
             'js::jsval::{NullValue, UndefinedValue}',
             'js::glue::{CallJitMethodOp, CallJitGetterOp, CallJitSetterOp, CreateProxyHandler}',
             'js::glue::{GetProxyPrivate, NewProxyObject, ProxyTraps}',
             'js::glue::{RUST_FUNCTION_VALUE_TO_JITINFO}',
             'js::glue::{RUST_JS_NumberValue, RUST_JSID_IS_STRING, int_to_jsid}',
             'js::glue::AppendToAutoIdVector',
@@ -5595,17 +5597,16 @@ class CGBindingRoot(CGThing):
             'dom::bindings::error::Error::JSFailed',
             'dom::bindings::error::throw_dom_exception',
             'dom::bindings::guard::{Condition, Guard}',
             'dom::bindings::proxyhandler',
             'dom::bindings::proxyhandler::{ensure_expando_object, fill_property_descriptor}',
             'dom::bindings::proxyhandler::{get_expando_object, get_property_descriptor}',
             'dom::bindings::num::Finite',
             'dom::bindings::str::{ByteString, DOMString, USVString}',
-            'dom::bindings::trace::RootedVec',
             'dom::bindings::weakref::{DOM_WEAK_SLOT, WeakBox, WeakReferenceable}',
             'dom::browsingcontext::BrowsingContext',
             'mem::heap_size_of_raw_self_and_children',
             'libc',
             'util::prefs::PREFS',
             'script_runtime::{store_panic_result, maybe_take_panic_result}',
             'std::borrow::ToOwned',
             'std::cmp',
@@ -5764,35 +5765,36 @@ class CGCallback(CGClass):
         argsWithoutThis = list(args)
         args.insert(0, Argument("&T", "thisObj"))
 
         # And the self argument
         method.args.insert(0, Argument(None, "&self"))
         args.insert(0, Argument(None, "&self"))
         argsWithoutThis.insert(0, Argument(None, "&self"))
 
-        setupCall = ("let s = CallSetup::new(self, aExceptionHandling);\n"
+        setupCall = ("let mut s_ec = RootedObject::new_unrooted(ptr::null_mut());\n"
+                     "let s = CallSetup::new(&mut s_ec, self, aExceptionHandling);\n"
                      "if s.get_context().is_null() {\n"
                      "    return Err(JSFailed);\n"
                      "}\n")
 
         bodyWithThis = string.Template(
             setupCall +
-            "let mut thisObjJS = RootedObject::new(s.get_context(), ptr::null_mut());\n"
+            "rooted!(in(s.get_context()) let mut thisObjJS = ptr::null_mut());\n"
             "wrap_call_this_object(s.get_context(), thisObj, thisObjJS.handle_mut());\n"
-            "if thisObjJS.ptr.is_null() {\n"
+            "if thisObjJS.is_null() {\n"
             "    return Err(JSFailed);\n"
             "}\n"
             "return ${methodName}(${callArgs});").substitute({
                 "callArgs": ", ".join(argnamesWithThis),
                 "methodName": 'self.' + method.name,
             })
         bodyWithoutThis = string.Template(
             setupCall +
-            "let thisObjJS = RootedObject::new(s.get_context(), ptr::null_mut());"
+            "rooted!(in(s.get_context()) let thisObjJS = ptr::null_mut());"
             "return ${methodName}(${callArgs});").substitute({
                 "callArgs": ", ".join(argnamesWithoutThis),
                 "methodName": 'self.' + method.name,
             })
         return [ClassMethod(method.name + '_', method.returnType, args,
                             bodyInHeader=True,
                             templateArgs=["T: Reflectable"],
                             body=bodyWithThis,
@@ -6001,18 +6003,18 @@ class CallbackMember(CGNativeMember):
             jsvalIndex = "%d + idx" % i
         else:
             jsvalIndex = "%d" % i
             if arg.optional and not arg.defaultValue:
                 argval += ".clone().unwrap()"
 
         conversion = wrapForType(
             "argv_root.handle_mut()", result=argval,
-            successCode="argv[%s] = argv_root.ptr;" % jsvalIndex,
-            pre="let mut argv_root = RootedValue::new(cx, UndefinedValue());")
+            successCode="argv[%s] = argv_root.get();" % jsvalIndex,
+            pre="rooted!(in(cx) let mut argv_root = UndefinedValue());")
         if arg.variadic:
             conversion = string.Template(
                 "for idx in 0..${arg}.len() {\n" +
                 CGIndenter(CGGeneric(conversion)).define() + "\n"
                 "}"
             ).substitute({"arg": arg.identifier.name})
         elif arg.optional and not arg.defaultValue:
             conversion = (
@@ -6072,32 +6074,32 @@ class CallbackMember(CGNativeMember):
 
 
 class CallbackMethod(CallbackMember):
     def __init__(self, sig, name, descriptorProvider, needThisHandling):
         CallbackMember.__init__(self, sig, name, descriptorProvider,
                                 needThisHandling)
 
     def getRvalDecl(self):
-        return "let mut rval = RootedValue::new(cx, UndefinedValue());\n"
+        return "rooted!(in(cx) let mut rval = UndefinedValue());\n"
 
     def getCall(self):
         replacements = {
             "thisObj": self.getThisObj(),
             "getCallable": self.getCallableDecl()
         }
         if self.argCount > 0:
             replacements["argv"] = "argv.as_ptr()"
             replacements["argc"] = "argc"
         else:
             replacements["argv"] = "ptr::null_mut()"
             replacements["argc"] = "0"
         return string.Template(
             "${getCallable}"
-            "let rootedThis = RootedObject::new(cx, ${thisObj});\n"
+            "rooted!(in(cx) let rootedThis = ${thisObj});\n"
             "let ok = JS_CallFunctionValue(\n"
             "    cx, rootedThis.handle(), callable.handle(),\n"
             "    &HandleValueArray {\n"
             "        length_: ${argc} as ::libc::size_t,\n"
             "        elements_: ${argv}\n"
             "    }, rval.handle_mut());\n"
             "if let Some(error) = maybe_take_panic_result() {\n"
             "    panic::resume_unwind(error);\n"
@@ -6111,17 +6113,17 @@ class CallCallback(CallbackMethod):
     def __init__(self, callback, descriptorProvider):
         CallbackMethod.__init__(self, callback.signatures()[0], "Call",
                                 descriptorProvider, needThisHandling=True)
 
     def getThisObj(self):
         return "aThisObj.get()"
 
     def getCallableDecl(self):
-        return "let callable = RootedValue::new(cx, ObjectValue(&*self.parent.callback()));\n"
+        return "rooted!(in(cx) let callable = ObjectValue(&*self.parent.callback()));\n"
 
 
 class CallbackOperationBase(CallbackMethod):
     """
     Common class for implementing various callback operations.
     """
     def __init__(self, signature, jsName, nativeName, descriptor, singleOperation):
         self.singleOperation = singleOperation
@@ -6136,27 +6138,27 @@ class CallbackOperationBase(CallbackMeth
         # interface.
         return "if isCallable { aThisObj.get() } else { self.parent.callback() }"
 
     def getCallableDecl(self):
         replacements = {
             "methodName": self.methodName
         }
         getCallableFromProp = string.Template(
-            'RootedValue::new(cx, try!(self.parent.get_callable_property(cx, "${methodName}")))'
+            'try!(self.parent.get_callable_property(cx, "${methodName}"))'
         ).substitute(replacements)
         if not self.singleOperation:
-            return 'JS::Rooted<JS::Value> callable(cx);\n' + getCallableFromProp
+            return 'rooted!(in(cx) let callable =\n' + getCallableFromProp + ');\n'
         return (
             'let isCallable = IsCallable(self.parent.callback());\n'
-            'let callable =\n' +
+            'rooted!(in(cx) let callable =\n' +
             CGIndenter(
                 CGIfElseWrapper('isCallable',
-                                CGGeneric('RootedValue::new(cx, ObjectValue(&*self.parent.callback()))'),
-                                CGGeneric(getCallableFromProp))).define() + ';\n')
+                                CGGeneric('ObjectValue(&*self.parent.callback())'),
+                                CGGeneric(getCallableFromProp))).define() + ');\n')
 
 
 class CallbackOperation(CallbackOperationBase):
     """
     Codegen actual WebIDL operations on callback interfaces.
     """
     def __init__(self, method, signature, descriptor):
         self.ensureASCIIName(method)
--- a/servo/components/script/dom/bindings/error.rs
+++ b/servo/components/script/dom/bindings/error.rs
@@ -5,17 +5,17 @@
 //! Utilities to throw exceptions from Rust bindings.
 
 use dom::bindings::codegen::PrototypeList::proto_id_to_name;
 use dom::bindings::conversions::ToJSValConvertible;
 use dom::bindings::global::GlobalRef;
 use dom::domexception::{DOMErrorName, DOMException};
 use js::error::{throw_range_error, throw_type_error};
 use js::jsapi::JSAutoCompartment;
-use js::jsapi::{JSContext, JSObject, RootedValue};
+use js::jsapi::{JSContext, JSObject};
 use js::jsapi::{JS_IsExceptionPending, JS_ReportPendingException, JS_SetPendingException};
 use js::jsval::UndefinedValue;
 
 /// DOM exceptions that can be thrown by a native DOM method.
 #[derive(Debug, Clone, HeapSizeOf)]
 pub enum Error {
     /// IndexSizeError DOMException
     IndexSize,
@@ -110,17 +110,17 @@ pub unsafe fn throw_dom_exception(cx: *m
         Error::JSFailed => {
             assert!(JS_IsExceptionPending(cx));
             return;
         }
     };
 
     assert!(!JS_IsExceptionPending(cx));
     let exception = DOMException::new(global, code);
-    let mut thrown = RootedValue::new(cx, UndefinedValue());
+    rooted!(in(cx) let mut thrown = UndefinedValue());
     exception.to_jsval(cx, thrown.handle_mut());
     JS_SetPendingException(cx, thrown.handle());
 }
 
 /// Report a pending exception, thereby clearing it.
 pub unsafe fn report_pending_exception(cx: *mut JSContext, obj: *mut JSObject) {
     if JS_IsExceptionPending(cx) {
         let _ac = JSAutoCompartment::new(cx, obj);
--- a/servo/components/script/dom/bindings/interface.rs
+++ b/servo/components/script/dom/bindings/interface.rs
@@ -14,18 +14,18 @@ use js::jsapi::{Class, ClassExtension, C
 use js::jsapi::{GetWellKnownSymbol, HandleObject, HandleValue, JSClass, JSContext};
 use js::jsapi::{JSFunctionSpec, JSNative, JSFUN_CONSTRUCTOR, JSPROP_ENUMERATE};
 use js::jsapi::{JSPROP_PERMANENT, JSPROP_READONLY, JSPROP_RESOLVING, JSPropertySpec};
 use js::jsapi::{JSString, JS_AtomizeAndPinString, JS_DefineProperty, JS_DefineProperty1};
 use js::jsapi::{JS_DefineProperty2, JS_DefineProperty4, JS_DefinePropertyById3};
 use js::jsapi::{JS_GetClass, JS_GetFunctionObject, JS_GetPrototype, JS_LinkConstructorAndPrototype};
 use js::jsapi::{JS_NewFunction, JS_NewObject, JS_NewObjectWithUniqueType};
 use js::jsapi::{JS_NewPlainObject, JS_NewStringCopyN, MutableHandleObject};
-use js::jsapi::{MutableHandleValue, ObjectOps, RootedId, RootedObject};
-use js::jsapi::{RootedString, RootedValue, SymbolCode, TrueHandleValue, Value};
+use js::jsapi::{MutableHandleValue, ObjectOps};
+use js::jsapi::{SymbolCode, TrueHandleValue, Value};
 use js::jsval::{BooleanValue, DoubleValue, Int32Value, JSVal, NullValue, UInt32Value};
 use js::rust::{define_methods, define_properties};
 use libc;
 use std::ptr;
 
 /// Representation of an IDL constant value.
 #[derive(Clone)]
 pub enum ConstantVal {
@@ -69,17 +69,17 @@ pub type NonNullJSNative =
 
 /// Defines constants on `obj`.
 /// Fails on JSAPI failure.
 fn define_constants(
         cx: *mut JSContext,
         obj: HandleObject,
         constants: &[ConstantSpec]) {
     for spec in constants {
-        let value = RootedValue::new(cx, spec.get_value());
+        rooted!(in(cx) let value = spec.get_value());
         unsafe {
             assert!(JS_DefineProperty(cx,
                                       obj,
                                       spec.name.as_ptr() as *const libc::c_char,
                                       value.handle(),
                                       JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT,
                                       None,
                                       None));
@@ -238,23 +238,23 @@ pub unsafe fn create_interface_prototype
         regular_methods: &[Guard<&'static [JSFunctionSpec]>],
         regular_properties: &[Guard<&'static [JSPropertySpec]>],
         constants: &[Guard<&[ConstantSpec]>],
         unscopable_names: &[&[u8]],
         rval: MutableHandleObject) {
     create_object(cx, proto, class, regular_methods, regular_properties, constants, rval);
 
     if !unscopable_names.is_empty() {
-        let mut unscopable_obj = RootedObject::new(cx, ptr::null_mut());
+        rooted!(in(cx) let mut unscopable_obj = ptr::null_mut());
         create_unscopable_object(cx, unscopable_names, unscopable_obj.handle_mut());
 
         let unscopable_symbol = GetWellKnownSymbol(cx, SymbolCode::unscopables);
         assert!(!unscopable_symbol.is_null());
 
-        let unscopable_id = RootedId::new(cx, RUST_SYMBOL_TO_JSID(unscopable_symbol));
+        rooted!(in(cx) let unscopable_id = RUST_SYMBOL_TO_JSID(unscopable_symbol));
         assert!(JS_DefinePropertyById3(
             cx, rval.handle(), unscopable_id.handle(), unscopable_obj.handle(),
             JSPROP_READONLY, None, None))
     }
 }
 
 /// Create and define the interface object of a non-callback interface.
 pub unsafe fn create_noncallback_interface_object(
@@ -283,29 +283,29 @@ pub unsafe fn create_noncallback_interfa
 }
 
 /// Create and define the named constructors of a non-callback interface.
 pub unsafe fn create_named_constructors(
         cx: *mut JSContext,
         global: HandleObject,
         named_constructors: &[(NonNullJSNative, &[u8], u32)],
         interface_prototype_object: HandleObject) {
-    let mut constructor = RootedObject::new(cx, ptr::null_mut());
+    rooted!(in(cx) let mut constructor = ptr::null_mut());
 
     for &(native, name, arity) in named_constructors {
         assert!(*name.last().unwrap() == b'\0');
 
         let fun = JS_NewFunction(cx,
                                  Some(native),
                                  arity,
                                  JSFUN_CONSTRUCTOR,
                                  name.as_ptr() as *const libc::c_char);
         assert!(!fun.is_null());
-        constructor.ptr = JS_GetFunctionObject(fun);
-        assert!(!constructor.ptr.is_null());
+        constructor.set(JS_GetFunctionObject(fun));
+        assert!(!constructor.is_null());
 
         assert!(JS_DefineProperty1(cx,
                                    constructor.handle(),
                                    b"prototype\0".as_ptr() as *const libc::c_char,
                                    interface_prototype_object,
                                    JSPROP_PERMANENT | JSPROP_READONLY,
                                    None,
                                    None));
@@ -334,41 +334,42 @@ unsafe fn has_instance(
         cx: *mut JSContext,
         interface_object: HandleObject,
         value: HandleValue)
         -> Result<bool, ()> {
     if !value.is_object() {
         // Step 1.
         return Ok(false);
     }
-    let mut value = RootedObject::new(cx, value.to_object());
+    rooted!(in(cx) let mut value = value.to_object());
 
     let js_class = JS_GetClass(interface_object.get());
     let object_class = &*(js_class as *const NonCallbackInterfaceObjectClass);
 
-    if let Ok(dom_class) = get_dom_class(UncheckedUnwrapObject(value.ptr, /* stopAtWindowProxy = */ 0)) {
+    if let Ok(dom_class) = get_dom_class(UncheckedUnwrapObject(value.get(),
+                                                               /* stopAtWindowProxy = */ 0)) {
         if dom_class.interface_chain[object_class.proto_depth as usize] == object_class.proto_id {
             // Step 4.
             return Ok(true);
         }
     }
 
     // Step 2.
     let global = GetGlobalForObjectCrossCompartment(interface_object.get());
     assert!(!global.is_null());
     let proto_or_iface_array = get_proto_or_iface_array(global);
-    let prototype = RootedObject::new(cx, (*proto_or_iface_array)[object_class.proto_id as usize]);
-    assert!(!prototype.ptr.is_null());
+    rooted!(in(cx) let prototype = (*proto_or_iface_array)[object_class.proto_id as usize]);
+    assert!(!prototype.is_null());
     // Step 3 only concern legacy callback interface objects (i.e. NodeFilter).
 
     while JS_GetPrototype(cx, value.handle(), value.handle_mut()) {
-        if value.ptr.is_null() {
+        if value.is_null() {
             // Step 5.2.
             return Ok(false);
-        } else if value.ptr as *const _ == prototype.ptr {
+        } else if value.get() as *const _ == prototype.get() {
             // Step 5.3.
             return Ok(true);
         }
     }
     // JS_GetPrototype threw an exception.
     Err(())
 }
 
@@ -428,19 +429,18 @@ pub unsafe fn define_guarded_properties(
         if let Some(specs) = guard.expose(cx, obj) {
             define_properties(cx, obj, specs).unwrap();
         }
     }
 }
 
 unsafe fn define_name(cx: *mut JSContext, obj: HandleObject, name: &[u8]) {
     assert!(*name.last().unwrap() == b'\0');
-    let name = RootedString::new(
-        cx, JS_AtomizeAndPinString(cx, name.as_ptr() as *const libc::c_char));
-    assert!(!name.ptr.is_null());
+    rooted!(in(cx) let name = JS_AtomizeAndPinString(cx, name.as_ptr() as *const libc::c_char));
+    assert!(!name.is_null());
     assert!(JS_DefineProperty2(cx,
                                obj,
                                b"name\0".as_ptr() as *const libc::c_char,
                                name.handle(),
                                JSPROP_READONLY,
                                None, None));
 }
 
--- a/servo/components/script/dom/bindings/proxyhandler.rs
+++ b/servo/components/script/dom/bindings/proxyhandler.rs
@@ -8,17 +8,17 @@
 
 use dom::bindings::conversions::is_dom_proxy;
 use dom::bindings::utils::delete_property_by_id;
 use js::glue::GetProxyExtra;
 use js::glue::InvokeGetOwnPropertyDescriptor;
 use js::glue::{GetProxyHandler, SetProxyExtra};
 use js::jsapi::GetObjectProto;
 use js::jsapi::JS_GetPropertyDescriptorById;
-use js::jsapi::{Handle, HandleId, HandleObject, MutableHandle, ObjectOpResult, RootedObject};
+use js::jsapi::{Handle, HandleId, HandleObject, MutableHandle, ObjectOpResult};
 use js::jsapi::{JSContext, JSObject, JSPROP_GETTER, PropertyDescriptor};
 use js::jsapi::{JSErrNum, JS_StrictPropertyStub};
 use js::jsapi::{JS_DefinePropertyById, JS_NewObjectWithGivenProto};
 use js::jsval::ObjectValue;
 use libc;
 use std::{mem, ptr};
 
 static JSPROXYSLOT_EXPANDO: u32 = 0;
@@ -31,22 +31,23 @@ pub unsafe extern "C" fn get_property_de
                                                  proxy: HandleObject,
                                                  id: HandleId,
                                                  desc: MutableHandle<PropertyDescriptor>)
                                                  -> bool {
     let handler = GetProxyHandler(proxy.get());
     if !InvokeGetOwnPropertyDescriptor(handler, cx, proxy, id, desc) {
         return false;
     }
-    if !desc.get().obj.is_null() {
+    if !desc.obj.is_null() {
         return true;
     }
 
-    let mut proto = RootedObject::new(cx, ptr::null_mut());
+    rooted!(in(cx) let mut proto = ptr::null_mut());
     if !GetObjectProto(cx, proxy, proto.handle_mut()) {
+        // FIXME(#11868) Should assign to desc.obj, desc.get() is a copy.
         desc.get().obj = ptr::null_mut();
         return true;
     }
 
     JS_GetPropertyDescriptorById(cx, proto.handle(), id, desc)
 }
 
 /// Defines an expando on the given `proxy`.
@@ -60,28 +61,28 @@ pub unsafe extern "C" fn define_property
     let setter: *const libc::c_void = mem::transmute(desc.get().setter);
     let setter_stub: unsafe extern fn(_, _, _, _, _) -> _ = JS_StrictPropertyStub;
     let setter_stub: *const libc::c_void = mem::transmute(setter_stub);
     if (desc.get().attrs & JSPROP_GETTER) != 0 && setter == setter_stub {
         (*result).code_ = JSErrNum::JSMSG_GETTER_ONLY as ::libc::uintptr_t;
         return true;
     }
 
-    let expando = RootedObject::new(cx, ensure_expando_object(cx, proxy));
+    rooted!(in(cx) let expando = ensure_expando_object(cx, proxy));
     JS_DefinePropertyById(cx, expando.handle(), id, desc, result)
 }
 
 /// Deletes an expando off the given `proxy`.
 pub unsafe extern "C" fn delete(cx: *mut JSContext,
                                 proxy: HandleObject,
                                 id: HandleId,
                                 bp: *mut ObjectOpResult)
                                 -> bool {
-    let expando = RootedObject::new(cx, get_expando_object(proxy));
-    if expando.ptr.is_null() {
+    rooted!(in(cx) let expando = get_expando_object(proxy));
+    if expando.is_null() {
         (*bp).code_ = 0 /* OkCode */;
         return true;
     }
 
     delete_property_by_id(cx, expando.handle(), id, bp)
 }
 
 /// Controls whether the Extensible bit can be changed
--- a/servo/components/script/dom/bindings/trace.rs
+++ b/servo/components/script/dom/bindings/trace.rs
@@ -72,18 +72,16 @@ use script_layout_interface::rpc::Layout
 use script_runtime::ScriptChan;
 use script_traits::{TimerEventId, TimerSource, TouchpadPressurePhase, UntrustedNodeAddress};
 use serde::{Deserialize, Serialize};
 use smallvec::SmallVec;
 use std::boxed::FnBox;
 use std::cell::{Cell, UnsafeCell};
 use std::collections::{BTreeMap, HashMap, HashSet};
 use std::hash::{BuildHasher, Hash};
-use std::intrinsics::return_address;
-use std::iter::{FromIterator, IntoIterator};
 use std::mem;
 use std::ops::{Deref, DerefMut};
 use std::rc::Rc;
 use std::sync::Arc;
 use std::sync::atomic::{AtomicBool, AtomicUsize};
 use std::sync::mpsc::{Receiver, Sender};
 use std::time::SystemTime;
 use string_cache::{Atom, Namespace, QualName};
@@ -461,18 +459,18 @@ impl RootedTraceableSet {
             (info.trace)(info.ptr, tracer);
         }
     }
 }
 
 /// Roots any JSTraceable thing
 ///
 /// If you have a valid Reflectable, use Root.
-/// If you have GC things like *mut JSObject or JSVal, use jsapi::Rooted.
-/// If you have an arbitrary number of Reflectables to root, use RootedVec<JS<T>>
+/// If you have GC things like *mut JSObject or JSVal, use rooted!.
+/// If you have an arbitrary number of Reflectables to root, use rooted_vec!.
 /// If you know what you're doing, use this.
 #[derive(JSTraceable)]
 pub struct RootedTraceable<'a, T: 'a + JSTraceable> {
     ptr: &'a T,
 }
 
 impl<'a, T: JSTraceable> RootedTraceable<'a, T> {
     /// Root a JSTraceable thing for the life of this RootedTraceable
@@ -489,83 +487,83 @@ impl<'a, T: JSTraceable> RootedTraceable
 impl<'a, T: JSTraceable> Drop for RootedTraceable<'a, T> {
     fn drop(&mut self) {
         unsafe {
             RootedTraceableSet::remove(self.ptr);
         }
     }
 }
 
-/// A vector of items that are rooted for the lifetime of this struct.
+/// A vector of items to be rooted with `RootedVec`.
+/// Guaranteed to be empty when not rooted.
+/// Usage: `rooted_vec!(let mut v);` or if you have an
+/// iterator of `Root`s, `rooted_vec!(let v <- iterator);`.
 #[allow(unrooted_must_root)]
-#[no_move]
 #[derive(JSTraceable)]
 #[allow_unrooted_interior]
-pub struct RootedVec<T: JSTraceable> {
+pub struct RootableVec<T: JSTraceable> {
     v: Vec<T>,
 }
 
+impl<T: JSTraceable> RootableVec<T> {
+    /// Create a vector of items of type T that can be rooted later.
+    pub fn new_unrooted() -> RootableVec<T> {
+        RootableVec {
+            v: vec![],
+        }
+   }
+}
 
-impl<T: JSTraceable> RootedVec<T> {
+/// A vector of items that are rooted for the lifetime 'a.
+#[allow_unrooted_interior]
+pub struct RootedVec<'a, T: 'a + JSTraceable> {
+    root: &'a mut RootableVec<T>,
+}
+
+impl<'a, T: JSTraceable + Reflectable> RootedVec<'a, JS<T>> {
     /// Create a vector of items of type T that is rooted for
     /// the lifetime of this struct
-    pub fn new() -> RootedVec<T> {
-        let addr = unsafe { return_address() as *const libc::c_void };
-
-        unsafe { RootedVec::new_with_destination_address(addr) }
-    }
-
-    /// Create a vector of items of type T. This constructor is specific
-    /// for RootTraceableSet.
-    pub unsafe fn new_with_destination_address(addr: *const libc::c_void) -> RootedVec<T> {
-        RootedTraceableSet::add::<RootedVec<T>>(&*(addr as *const _));
-        RootedVec::<T> {
-            v: vec![],
+    pub fn new<I: Iterator<Item = Root<T>>>(root: &'a mut RootableVec<JS<T>>, iter: I)
+                                            -> RootedVec<'a, JS<T>> {
+        unsafe {
+            RootedTraceableSet::add(root);
+        }
+        root.v.extend(iter.map(|item| JS::from_ref(&*item)));
+        RootedVec {
+            root: root,
         }
     }
 }
 
-impl<T: JSTraceable + Reflectable> RootedVec<JS<T>> {
+impl<'a, T: JSTraceable + Reflectable> RootedVec<'a, JS<T>> {
     /// Obtain a safe slice of references that can't outlive that RootedVec.
     pub fn r(&self) -> &[&T] {
-        unsafe { mem::transmute(&self.v[..]) }
+        unsafe { mem::transmute(&self[..]) }
     }
 }
 
-impl<T: JSTraceable> Drop for RootedVec<T> {
+impl<'a, T: JSTraceable> Drop for RootedVec<'a, T> {
     fn drop(&mut self) {
+        self.clear();
         unsafe {
-            RootedTraceableSet::remove(self);
+            RootedTraceableSet::remove(self.root);
         }
     }
 }
 
-impl<T: JSTraceable> Deref for RootedVec<T> {
+impl<'a, T: JSTraceable> Deref for RootedVec<'a, T> {
     type Target = Vec<T>;
     fn deref(&self) -> &Vec<T> {
-        &self.v
+        &self.root.v
     }
 }
 
-impl<T: JSTraceable> DerefMut for RootedVec<T> {
+impl<'a, T: JSTraceable> DerefMut for RootedVec<'a, T> {
     fn deref_mut(&mut self) -> &mut Vec<T> {
-        &mut self.v
-    }
-}
-
-impl<A: JSTraceable + Reflectable> FromIterator<Root<A>> for RootedVec<JS<A>> {
-    #[allow(moved_no_move)]
-    fn from_iter<T>(iterable: T) -> RootedVec<JS<A>>
-        where T: IntoIterator<Item = Root<A>>
-    {
-        let mut vec = unsafe {
-            RootedVec::new_with_destination_address(return_address() as *const libc::c_void)
-        };
-        vec.extend(iterable.into_iter().map(|item| JS::from_ref(&*item)));
-        vec
+        &mut self.root.v
     }
 }
 
 /// SM Callback that traces the rooted traceables
 pub unsafe fn trace_traceables(tracer: *mut JSTracer) {
     debug!("tracing stack-rooted traceables");
     ROOTED_TRACEABLES.with(|ref traceables| {
         let traceables = traceables.borrow();
--- a/servo/components/script/dom/bindings/utils.rs
+++ b/servo/components/script/dom/bindings/utils.rs
@@ -24,18 +24,18 @@ use js::glue::{RUST_JSID_TO_INT, RUST_JS
 use js::jsapi::{CallArgs, CompartmentOptions, DOMCallbacks, GetGlobalForObjectCrossCompartment};
 use js::jsapi::{HandleId, HandleObject, HandleValue, Heap, JSAutoCompartment, JSClass, JSContext};
 use js::jsapi::{JSJitInfo, JSObject, JSTraceOp, JSTracer, JSVersion, JSWrapObjectCallbacks};
 use js::jsapi::{JS_DeletePropertyById, JS_EnumerateStandardClasses, JS_FireOnNewGlobalObject};
 use js::jsapi::{JS_ForwardGetPropertyTo, JS_GetClass, JS_GetLatin1StringCharsAndLength};
 use js::jsapi::{JS_GetProperty, JS_GetPrototype, JS_GetReservedSlot, JS_HasProperty};
 use js::jsapi::{JS_HasPropertyById, JS_IsExceptionPending, JS_IsGlobalObject, JS_NewGlobalObject};
 use js::jsapi::{JS_ResolveStandardClass, JS_SetProperty, ToWindowProxyIfWindow};
-use js::jsapi::{JS_SetReservedSlot, JS_StringHasLatin1Chars, MutableHandleValue, ObjectOpResult};
-use js::jsapi::{OnNewGlobalHookOption, RootedObject, RootedValue};
+use js::jsapi::{JS_SetReservedSlot, JS_StringHasLatin1Chars, MutableHandleValue};
+use js::jsapi::{ObjectOpResult, OnNewGlobalHookOption};
 use js::jsval::{JSVal, ObjectValue, PrivateValue, UndefinedValue};
 use js::rust::{GCMethods, ToString};
 use libc;
 use std::default::Default;
 use std::ffi::CString;
 use std::os::raw::c_void;
 use std::ptr;
 use std::slice;
@@ -130,32 +130,32 @@ pub type ProtoOrIfaceArray = [*mut JSObj
 pub fn get_property_on_prototype(cx: *mut JSContext,
                                  proxy: HandleObject,
                                  id: HandleId,
                                  found: *mut bool,
                                  vp: MutableHandleValue)
                                  -> bool {
     unsafe {
         // let proto = GetObjectProto(proxy);
-        let mut proto = RootedObject::new(cx, ptr::null_mut());
-        if !JS_GetPrototype(cx, proxy, proto.handle_mut()) || proto.ptr.is_null() {
+        rooted!(in(cx) let mut proto = ptr::null_mut());
+        if !JS_GetPrototype(cx, proxy, proto.handle_mut()) || proto.is_null() {
             *found = false;
             return true;
         }
         let mut has_property = false;
         if !JS_HasPropertyById(cx, proto.handle(), id, &mut has_property) {
             return false;
         }
         *found = has_property;
         let no_output = vp.ptr.is_null();
         if !has_property || no_output {
             return true;
         }
 
-        let receiver = RootedValue::new(cx, ObjectValue(&**proxy.ptr));
+        rooted!(in(cx) let receiver = ObjectValue(&*proxy.get()));
         JS_ForwardGetPropertyTo(cx, proto.handle(), id, receiver.handle(), vp)
     }
 }
 
 /// Get an array index from the given `jsid`. Returns `None` if the given
 /// `jsid` is not an integer.
 pub fn get_array_index_from_id(_cx: *mut JSContext, id: HandleId) -> Option<u32> {
     unsafe {
@@ -300,39 +300,38 @@ pub fn create_dom_global(cx: *mut JSCont
                          trace: JSTraceOp)
                          -> *mut JSObject {
     unsafe {
         let mut options = CompartmentOptions::default();
         options.behaviors_.version_ = JSVersion::JSVERSION_ECMA_5;
         options.creationOptions_.traceGlobal_ = trace;
         options.creationOptions_.sharedMemoryAndAtomics_ = true;
 
-        let obj =
-            RootedObject::new(cx,
-                              JS_NewGlobalObject(cx,
-                                                 class,
-                                                 ptr::null_mut(),
-                                                 OnNewGlobalHookOption::DontFireOnNewGlobalHook,
-                                                 &options));
-        if obj.ptr.is_null() {
+        rooted!(in(cx) let obj =
+            JS_NewGlobalObject(cx,
+                               class,
+                               ptr::null_mut(),
+                               OnNewGlobalHookOption::DontFireOnNewGlobalHook,
+                               &options));
+        if obj.is_null() {
             return ptr::null_mut();
         }
 
         // Initialize the reserved slots before doing amything that can GC, to
         // avoid getting trace hooks called on a partially initialized object.
-        JS_SetReservedSlot(obj.ptr, DOM_OBJECT_SLOT, PrivateValue(private));
+        JS_SetReservedSlot(obj.get(), DOM_OBJECT_SLOT, PrivateValue(private));
         let proto_array: Box<ProtoOrIfaceArray> =
             box [0 as *mut JSObject; PROTO_OR_IFACE_LENGTH];
-        JS_SetReservedSlot(obj.ptr,
+        JS_SetReservedSlot(obj.get(),
                            DOM_PROTOTYPE_SLOT,
                            PrivateValue(Box::into_raw(proto_array) as *const libc::c_void));
 
-        let _ac = JSAutoCompartment::new(cx, obj.ptr);
+        let _ac = JSAutoCompartment::new(cx, obj.get());
         JS_FireOnNewGlobalObject(cx, obj.handle());
-        obj.ptr
+        obj.get()
     }
 }
 
 /// Drop the resources held by reserved slots of a global object
 pub unsafe fn finalize_global(obj: *mut JSObject) {
     let protolist = get_proto_or_iface_array(obj);
     let list = (*protolist).as_mut_ptr();
     for idx in 0..PROTO_OR_IFACE_LENGTH as isize {
@@ -454,24 +453,24 @@ unsafe fn generic_call(cx: *mut JSContex
     if !thisobj.get().is_null_or_undefined() && !thisobj.get().is_object() {
         return false;
     }
     let obj = if thisobj.get().is_object() {
         thisobj.get().to_object()
     } else {
         GetGlobalForObjectCrossCompartment(JS_CALLEE(cx, vp).to_object_or_null())
     };
-    let obj = RootedObject::new(cx, obj);
+    rooted!(in(cx) let obj = obj);
     let info = RUST_FUNCTION_VALUE_TO_JITINFO(JS_CALLEE(cx, vp));
     let proto_id = (*info).protoID;
     let depth = (*info).depth;
     let proto_check = |class: &'static DOMClass| {
         class.interface_chain[depth as usize] as u16 == proto_id
     };
-    let this = match private_from_proto_check(obj.ptr, proto_check) {
+    let this = match private_from_proto_check(obj.get(), proto_check) {
         Ok(val) => val,
         Err(()) => {
             if is_lenient {
                 debug_assert!(!JS_IsExceptionPending(cx));
                 *vp = UndefinedValue();
                 return true;
             } else {
                 throw_invalid_this(cx, proto_id);
--- a/servo/components/script/dom/browsingcontext.rs
+++ b/servo/components/script/dom/browsingcontext.rs
@@ -17,17 +17,17 @@ use dom::element::Element;
 use dom::window::Window;
 use js::JSCLASS_IS_GLOBAL;
 use js::glue::{CreateWrapperProxyHandler, ProxyTraps, NewWindowProxy};
 use js::glue::{GetProxyPrivate, SetProxyExtra, GetProxyExtra};
 use js::jsapi::{Handle, HandleId, HandleObject, HandleValue, JSAutoCompartment};
 use js::jsapi::{JSContext, JSPROP_READONLY, JSErrNum, JSObject, PropertyDescriptor, JS_DefinePropertyById};
 use js::jsapi::{JS_ForwardGetPropertyTo, JS_ForwardSetPropertyTo, JS_GetClass, JSTracer, FreeOp};
 use js::jsapi::{JS_GetOwnPropertyDescriptorById, JS_HasPropertyById, MutableHandle};
-use js::jsapi::{MutableHandleValue, ObjectOpResult, RootedObject, RootedValue};
+use js::jsapi::{MutableHandleValue, ObjectOpResult};
 use js::jsval::{UndefinedValue, PrivateValue};
 use msg::constellation_msg::{PipelineId, SubpageId};
 use std::cell::Cell;
 use url::Url;
 
 #[dom_struct]
 pub struct BrowsingContext {
     reflector: Reflector,
@@ -69,26 +69,25 @@ impl BrowsingContext {
             let WindowProxyHandler(handler) = window.windowproxy_handler();
             assert!(!handler.is_null());
 
             let cx = window.get_cx();
             let parent = window.reflector().get_jsobject();
             assert!(!parent.get().is_null());
             assert!(((*JS_GetClass(parent.get())).flags & JSCLASS_IS_GLOBAL) != 0);
             let _ac = JSAutoCompartment::new(cx, parent.get());
-            let window_proxy = RootedObject::new(cx,
-                NewWindowProxy(cx, parent, handler));
-            assert!(!window_proxy.ptr.is_null());
+            rooted!(in(cx) let window_proxy = NewWindowProxy(cx, parent, handler));
+            assert!(!window_proxy.is_null());
 
             let object = box BrowsingContext::new_inherited(frame_element, id);
 
             let raw = Box::into_raw(object);
-            SetProxyExtra(window_proxy.ptr, 0, &PrivateValue(raw as *const _));
+            SetProxyExtra(window_proxy.get(), 0, &PrivateValue(raw as *const _));
 
-            (*raw).init_reflector(window_proxy.ptr);
+            (*raw).init_reflector(window_proxy.get());
 
             Root::from_ref(&*raw)
         }
     }
 
     pub fn init(&self, document: &Document) {
         assert!(self.history.borrow().is_empty());
         assert_eq!(self.active_index.get(), 0);
@@ -230,48 +229,49 @@ impl SessionHistoryEntry {
 
 #[allow(unsafe_code)]
 unsafe fn GetSubframeWindow(cx: *mut JSContext,
                             proxy: HandleObject,
                             id: HandleId)
                             -> Option<Root<Window>> {
     let index = get_array_index_from_id(cx, id);
     if let Some(index) = index {
-        let target = RootedObject::new(cx, GetProxyPrivate(*proxy.ptr).to_object());
+        rooted!(in(cx) let target = GetProxyPrivate(*proxy.ptr).to_object());
         let win = root_from_handleobject::<Window>(target.handle()).unwrap();
         let mut found = false;
         return win.IndexedGetter(index, &mut found);
     }
 
     None
 }
 
 #[allow(unsafe_code)]
 unsafe extern "C" fn getOwnPropertyDescriptor(cx: *mut JSContext,
                                               proxy: HandleObject,
                                               id: HandleId,
-                                              desc: MutableHandle<PropertyDescriptor>)
+                                              mut desc: MutableHandle<PropertyDescriptor>)
                                               -> bool {
     let window = GetSubframeWindow(cx, proxy, id);
     if let Some(window) = window {
-        let mut val = RootedValue::new(cx, UndefinedValue());
+        rooted!(in(cx) let mut val = UndefinedValue());
         window.to_jsval(cx, val.handle_mut());
-        (*desc.ptr).value = val.ptr;
-        fill_property_descriptor(&mut *desc.ptr, *proxy.ptr, JSPROP_READONLY);
+        desc.value = val.get();
+        fill_property_descriptor(&mut desc, proxy.get(), JSPROP_READONLY);
         return true;
     }
 
-    let target = RootedObject::new(cx, GetProxyPrivate(*proxy.ptr).to_object());
+    rooted!(in(cx) let target = GetProxyPrivate(proxy.get()).to_object());
     if !JS_GetOwnPropertyDescriptorById(cx, target.handle(), id, desc) {
         return false;
     }
 
-    assert!(desc.get().obj.is_null() || desc.get().obj == target.ptr);
-    if desc.get().obj == target.ptr {
-        desc.get().obj = *proxy.ptr;
+    assert!(desc.obj.is_null() || desc.obj == target.get());
+    if desc.obj == target.get() {
+        // FIXME(#11868) Should assign to desc.obj, desc.get() is a copy.
+        desc.get().obj = proxy.get();
     }
 
     true
 }
 
 #[allow(unsafe_code)]
 unsafe extern "C" fn defineProperty(cx: *mut JSContext,
                                     proxy: HandleObject,
@@ -283,33 +283,33 @@ unsafe extern "C" fn defineProperty(cx: 
         // Spec says to Reject whether this is a supported index or not,
         // since we have no indexed setter or indexed creator.  That means
         // throwing in strict mode (FIXME: Bug 828137), doing nothing in
         // non-strict mode.
         (*res).code_ = JSErrNum::JSMSG_CANT_DEFINE_WINDOW_ELEMENT as ::libc::uintptr_t;
         return true;
     }
 
-    let target = RootedObject::new(cx, GetProxyPrivate(*proxy.ptr).to_object());
+    rooted!(in(cx) let target = GetProxyPrivate(*proxy.ptr).to_object());
     JS_DefinePropertyById(cx, target.handle(), id, desc, res)
 }
 
 #[allow(unsafe_code)]
 unsafe extern "C" fn has(cx: *mut JSContext,
                          proxy: HandleObject,
                          id: HandleId,
                          bp: *mut bool)
                          -> bool {
     let window = GetSubframeWindow(cx, proxy, id);
     if window.is_some() {
         *bp = true;
         return true;
     }
 
-    let target = RootedObject::new(cx, GetProxyPrivate(*proxy.ptr).to_object());
+    rooted!(in(cx) let target = GetProxyPrivate(*proxy.ptr).to_object());
     let mut found = false;
     if !JS_HasPropertyById(cx, target.handle(), id, &mut found) {
         return false;
     }
 
     *bp = found;
     true
 }
@@ -322,17 +322,17 @@ unsafe extern "C" fn get(cx: *mut JSCont
                          vp: MutableHandleValue)
                          -> bool {
     let window = GetSubframeWindow(cx, proxy, id);
     if let Some(window) = window {
         window.to_jsval(cx, vp);
         return true;
     }
 
-    let target = RootedObject::new(cx, GetProxyPrivate(*proxy.ptr).to_object());
+    rooted!(in(cx) let target = GetProxyPrivate(*proxy.ptr).to_object());
     JS_ForwardGetPropertyTo(cx, target.handle(), id, receiver, vp)
 }
 
 #[allow(unsafe_code)]
 unsafe extern "C" fn set(cx: *mut JSContext,
                          proxy: HandleObject,
                          id: HandleId,
                          v: HandleValue,
@@ -340,17 +340,17 @@ unsafe extern "C" fn set(cx: *mut JSCont
                          res: *mut ObjectOpResult)
                          -> bool {
     if get_array_index_from_id(cx, id).is_some() {
         // Reject (which means throw if and only if strict) the set.
         (*res).code_ = JSErrNum::JSMSG_READ_ONLY as ::libc::uintptr_t;
         return true;
     }
 
-    let target = RootedObject::new(cx, GetProxyPrivate(*proxy.ptr).to_object());
+    rooted!(in(cx) let target = GetProxyPrivate(*proxy.ptr).to_object());
     JS_ForwardSetPropertyTo(cx,
                             target.handle(),
                             id,
                             v,
                             receiver,
                             res)
 }
 
--- a/servo/components/script/dom/dedicatedworkerglobalscope.rs
+++ b/servo/components/script/dom/dedicatedworkerglobalscope.rs
@@ -20,17 +20,17 @@ use dom::bindings::str::DOMString;
 use dom::bindings::structuredclone::StructuredCloneData;
 use dom::messageevent::MessageEvent;
 use dom::worker::{TrustedWorkerAddress, WorkerMessageHandler};
 use dom::workerglobalscope::WorkerGlobalScope;
 use dom::workerglobalscope::WorkerGlobalScopeInit;
 use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
 use ipc_channel::router::ROUTER;
 use js::jsapi::{HandleValue, JS_SetInterruptCallback};
-use js::jsapi::{JSAutoCompartment, JSContext, RootedValue};
+use js::jsapi::{JSAutoCompartment, JSContext};
 use js::jsval::UndefinedValue;
 use js::rust::Runtime;
 use msg::constellation_msg::PipelineId;
 use net_traits::{LoadContext, load_whole_resource, CustomResponse, IpcSend};
 use rand::random;
 use script_runtime::ScriptThreadEventCategory::WorkerEvent;
 use script_runtime::{CommonScriptMsg, ScriptChan, ScriptPort, StackRootTLS, get_reports, new_rt_and_cx};
 use script_traits::{TimerEvent, TimerSource};
@@ -285,17 +285,17 @@ impl DedicatedWorkerGlobalScope {
 
     fn handle_script_event(&self, msg: WorkerScriptMsg) {
         match msg {
             WorkerScriptMsg::DOMMessage(data) => {
                 let scope = self.upcast::<WorkerGlobalScope>();
                 let target = self.upcast();
                 let _ac = JSAutoCompartment::new(scope.get_cx(),
                                                  scope.reflector().get_jsobject().get());
-                let mut message = RootedValue::new(scope.get_cx(), UndefinedValue());
+                rooted!(in(scope.get_cx()) let mut message = UndefinedValue());
                 data.read(GlobalRef::Worker(scope), message.handle_mut());
                 MessageEvent::dispatch_jsval(target, GlobalRef::Worker(scope), message.handle());
             },
             WorkerScriptMsg::Common(CommonScriptMsg::RunnableMsg(_, runnable)) => {
                 runnable.handler()
             },
             WorkerScriptMsg::Common(CommonScriptMsg::RefcountCleanup(addr)) => {
                 LiveDOMReferences::cleanup(addr);
--- a/servo/components/script/dom/document.rs
+++ b/servo/components/script/dom/document.rs
@@ -23,17 +23,16 @@ use dom::bindings::error::{Error, ErrorR
 use dom::bindings::global::GlobalRef;
 use dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId};
 use dom::bindings::js::RootedReference;
 use dom::bindings::js::{JS, LayoutJS, MutNullableHeap, Root};
 use dom::bindings::num::Finite;
 use dom::bindings::refcounted::Trusted;
 use dom::bindings::reflector::{Reflectable, reflect_dom_object};
 use dom::bindings::str::{DOMString, USVString};
-use dom::bindings::trace::RootedVec;
 use dom::bindings::xmlname::XMLName::InvalidXMLName;
 use dom::bindings::xmlname::{validate_and_extract, namespace_from_domstring, xml_name_type};
 use dom::browsingcontext::BrowsingContext;
 use dom::closeevent::CloseEvent;
 use dom::comment::Comment;
 use dom::customevent::CustomEvent;
 use dom::documentfragment::DocumentFragment;
 use dom::documenttype::DocumentType;
@@ -110,16 +109,17 @@ use script_traits::{ScriptMsg as Constel
 use script_traits::{TouchEventType, TouchId};
 use std::ascii::AsciiExt;
 use std::borrow::ToOwned;
 use std::boxed::FnBox;
 use std::cell::{Cell, Ref, RefMut};
 use std::collections::HashMap;
 use std::collections::hash_map::Entry::{Occupied, Vacant};
 use std::default::Default;
+use std::iter::once;
 use std::mem;
 use std::ptr;
 use std::rc::Rc;
 use std::sync::Arc;
 use string_cache::{Atom, QualName};
 use style::attr::AttrValue;
 use style::context::ReflowGoal;
 use style::restyle_hints::ElementSnapshot;
@@ -1004,28 +1004,25 @@ impl Document {
                     Some(i) => {
                         active_touch_points.swap_remove(i);
                     }
                     None => warn!("Got a touchend event for a non-active touch point"),
                 }
             }
         }
 
-        let mut touches = RootedVec::new();
+        rooted_vec!(let mut touches);
         touches.extend(self.active_touch_points.borrow().iter().cloned());
-
-        let mut changed_touches = RootedVec::new();
-        changed_touches.push(JS::from_ref(&*touch));
-
-        let mut target_touches = RootedVec::new();
+        rooted_vec!(let mut target_touches);
         target_touches.extend(self.active_touch_points
                                   .borrow()
                                   .iter()
                                   .filter(|t| t.Target() == target)
                                   .cloned());
+        rooted_vec!(let changed_touches <- once(touch));
 
         let event = TouchEvent::new(window,
                                     DOMString::from(event_name),
                                     EventBubbles::Bubbles,
                                     EventCancelable::Cancelable,
                                     Some(window),
                                     0i32,
                                     &TouchList::new(window, touches.r()),
--- a/servo/components/script/dom/errorevent.rs
+++ b/servo/components/script/dom/errorevent.rs
@@ -8,17 +8,17 @@ use dom::bindings::codegen::Bindings::Er
 use dom::bindings::codegen::Bindings::EventBinding::EventMethods;
 use dom::bindings::error::Fallible;
 use dom::bindings::global::GlobalRef;
 use dom::bindings::inheritance::Castable;
 use dom::bindings::js::{MutHeapJSVal, Root};
 use dom::bindings::reflector::reflect_dom_object;
 use dom::bindings::str::DOMString;
 use dom::event::{Event, EventBubbles, EventCancelable};
-use js::jsapi::{RootedValue, HandleValue, JSContext};
+use js::jsapi::{HandleValue, JSContext};
 use js::jsval::JSVal;
 use std::cell::Cell;
 use string_cache::Atom;
 
 #[dom_struct]
 pub struct ErrorEvent {
     event: Event,
     message: DOMRefCell<DOMString>,
@@ -88,17 +88,17 @@ impl ErrorEvent {
         let col_num = init.colno.unwrap_or(0);
 
         let bubbles = EventBubbles::from(init.parent.bubbles);
 
         let cancelable = EventCancelable::from(init.parent.cancelable);
 
         // Dictionaries need to be rooted
         // https://github.com/servo/servo/issues/6381
-        let error = RootedValue::new(global.get_cx(), init.error);
+        rooted!(in(global.get_cx()) let error = init.error);
         let event = ErrorEvent::new(global, Atom::from(type_),
                                 bubbles, cancelable,
                                 msg, file_name,
                                 line_num, col_num,
                                 error.handle());
         Ok(event)
     }
 
--- a/servo/components/script/dom/eventdispatcher.rs
+++ b/servo/components/script/dom/eventdispatcher.rs
@@ -4,17 +4,16 @@
 
 use devtools_traits::{StartedTimelineMarker, TimelineMarker, TimelineMarkerType};
 use dom::bindings::callback::ExceptionHandling::Report;
 use dom::bindings::codegen::Bindings::EventBinding::EventMethods;
 use dom::bindings::global::GlobalRoot;
 use dom::bindings::inheritance::Castable;
 use dom::bindings::js::{JS, Root, RootedReference};
 use dom::bindings::reflector::Reflectable;
-use dom::bindings::trace::RootedVec;
 use dom::document::Document;
 use dom::event::{Event, EventPhase};
 use dom::eventtarget::{CompiledEventListener, EventTarget, ListenerPhase};
 use dom::node::Node;
 use dom::virtualmethods::vtable_for;
 use dom::window::Window;
 
 struct AutoDOMEventMarker {
@@ -123,22 +122,22 @@ pub fn dispatch_event(target: &EventTarg
         return !event.DefaultPrevented();
     }
 
     // Step 1. Postponed here for the reason stated above.
     event.set_dispatching(true);
 
     // Step 3. The "invoke" algorithm is only used on `target` separately,
     // so we don't put it in the path.
-    let mut event_path: RootedVec<JS<EventTarget>> = RootedVec::new();
+    rooted_vec!(let mut event_path);
 
     // Step 4.
     if let Some(target_node) = target.downcast::<Node>() {
         for ancestor in target_node.ancestors() {
-            event_path.push(JS::from_ref(ancestor.upcast()));
+            event_path.push(JS::from_ref(ancestor.upcast::<EventTarget>()));
         }
         let top_most_ancestor_or_target =
             Root::from_ref(event_path.r().last().cloned().unwrap_or(target));
         if let Some(document) = Root::downcast::<Document>(top_most_ancestor_or_target) {
             if event.type_() != atom!("load") && document.browsing_context().is_some() {
                 event_path.push(JS::from_ref(document.window().upcast()));
             }
         }
--- a/servo/components/script/dom/eventtarget.rs
+++ b/servo/components/script/dom/eventtarget.rs
@@ -24,17 +24,17 @@ use dom::element::Element;
 use dom::errorevent::ErrorEvent;
 use dom::event::{Event, EventBubbles, EventCancelable};
 use dom::eventdispatcher::dispatch_event;
 use dom::node::document_from_node;
 use dom::virtualmethods::VirtualMethods;
 use dom::window::Window;
 use fnv::FnvHasher;
 use heapsize::HeapSizeOf;
-use js::jsapi::{CompileFunction, JS_GetFunctionObject, RootedValue, RootedFunction, JSAutoCompartment};
+use js::jsapi::{CompileFunction, JS_GetFunctionObject, JSAutoCompartment};
 use js::rust::{AutoObjectVectorWrapper, CompileOptionsWrapper};
 use libc::{c_char, size_t};
 use std::collections::HashMap;
 use std::collections::hash_map::Entry::{Occupied, Vacant};
 use std::default::Default;
 use std::ffi::CString;
 use std::hash::BuildHasherDefault;
 use std::mem;
@@ -151,27 +151,27 @@ impl CompiledEventListener {
                 let _ = listener.HandleEvent_(object, event, exception_handle);
             },
             CompiledEventListener::Handler(ref handler) => {
                 match *handler {
                     CommonEventHandler::ErrorEventHandler(ref handler) => {
                         if let Some(event) = event.downcast::<ErrorEvent>() {
                             let global = object.global();
                             let cx = global.r().get_cx();
-                            let error = RootedValue::new(cx, event.Error(cx));
+                            rooted!(in(cx) let error = event.Error(cx));
                             let return_value = handler.Call_(object,
                                                              EventOrString::String(event.Message()),
                                                              Some(event.Filename()),
                                                              Some(event.Lineno()),
                                                              Some(event.Colno()),
                                                              Some(error.handle()),
                                                              exception_handle);
                             // Step 4
                             if let Ok(return_value) = return_value {
-                                let return_value = RootedValue::new(cx, return_value);
+                                rooted!(in(cx) let return_value = return_value);
                                 if return_value.handle().is_boolean() && return_value.handle().to_boolean() == true {
                                     event.upcast::<Event>().PreventDefault();
                                 }
                             }
                             return;
                         }
 
                         let _ = handler.Call_(object, EventOrString::Event(Root::from_ref(event)),
@@ -198,17 +198,17 @@ impl CompiledEventListener {
                             }
                         }
                     }
 
                     CommonEventHandler::EventHandler(ref handler) => {
                         if let Ok(value) = handler.Call_(object, event, exception_handle) {
                             let global = object.global();
                             let cx = global.r().get_cx();
-                            let value = RootedValue::new(cx, value);
+                            rooted!(in(cx) let value = value);
                             let value = value.handle();
 
                             //Step 4
                             let should_cancel = match event.type_() {
                                 atom!("mouseover") => value.is_boolean() && value.to_boolean() == true,
                                 _ => value.is_boolean() && value.to_boolean() == false
                             };
                             if should_cancel {
@@ -406,39 +406,39 @@ impl EventTarget {
 
         let cx = window.get_cx();
         let options = CompileOptionsWrapper::new(cx, url_serialized.as_ptr(), handler.line as u32);
         // TODO step 1.10.1-3 (document, form owner, element in scope chain)
 
         let scopechain = AutoObjectVectorWrapper::new(cx);
 
         let _ac = JSAutoCompartment::new(cx, window.reflector().get_jsobject().get());
-        let mut handler = RootedFunction::new(cx, ptr::null_mut());
+        rooted!(in(cx) let mut handler = ptr::null_mut());
         let rv = unsafe {
             CompileFunction(cx,
                             scopechain.ptr,
                             options.ptr,
                             name.as_ptr(),
                             args.len() as u32,
                             args.as_ptr(),
                             body.as_ptr(),
                             body.len() as size_t,
                             handler.handle_mut())
         };
-        if !rv || handler.ptr.is_null() {
+        if !rv || handler.get().is_null() {
             // Step 1.8.2
             unsafe {
                 report_pending_exception(cx, self.reflector().get_jsobject().get());
             }
             // Step 1.8.1 / 1.8.3
             return None;
         }
 
         // TODO step 1.11-13
-        let funobj = unsafe { JS_GetFunctionObject(handler.ptr) };
+        let funobj = unsafe { JS_GetFunctionObject(handler.get()) };
         assert!(!funobj.is_null());
         // Step 1.14
         if is_error {
             Some(CommonEventHandler::ErrorEventHandler(OnErrorEventHandlerNonNull::new(funobj)))
         } else {
             if ty == &atom!("beforeunload") {
                 Some(CommonEventHandler::BeforeUnloadEventHandler(
                         OnBeforeUnloadEventHandlerNonNull::new(funobj)))
--- a/servo/components/script/dom/htmliframeelement.rs
+++ b/servo/components/script/dom/htmliframeelement.rs
@@ -31,17 +31,17 @@ use dom::element::{AttributeMutation, El
 use dom::event::Event;
 use dom::eventtarget::EventTarget;
 use dom::htmlelement::HTMLElement;
 use dom::node::{Node, NodeDamage, UnbindContext, window_from_node, document_from_node};
 use dom::urlhelper::UrlHelper;
 use dom::virtualmethods::VirtualMethods;
 use dom::window::{ReflowReason, Window};
 use ipc_channel::ipc;
-use js::jsapi::{JSAutoCompartment, RootedValue, JSContext, MutableHandleValue};
+use js::jsapi::{JSAutoCompartment, JSContext, MutableHandleValue};
 use js::jsval::{UndefinedValue, NullValue};
 use msg::constellation_msg::{FrameType, LoadData, NavigationDirection, PipelineId, SubpageId};
 use net_traits::response::HttpsState;
 use script_layout_interface::message::ReflowQueryType;
 use script_traits::IFrameSandboxState::{IFrameSandboxed, IFrameUnsandboxed};
 use script_traits::{IFrameLoadInfo, MozBrowserEvent, ScriptMsg as ConstellationMsg};
 use std::cell::Cell;
 use string_cache::Atom;
@@ -166,17 +166,17 @@ impl HTMLIFrameElement {
         // for a list of mozbrowser events.
         assert!(PREFS.is_mozbrowser_enabled());
 
         if self.Mozbrowser() {
             let window = window_from_node(self);
             let custom_event = unsafe {
                 let cx = window.get_cx();
                 let _ac = JSAutoCompartment::new(cx, window.reflector().get_jsobject().get());
-                let mut detail = RootedValue::new(cx, UndefinedValue());
+                rooted!(in(cx) let mut detail = UndefinedValue());
                 let event_name = Atom::from(event.name());
                 self.build_mozbrowser_event_detail(event, cx, detail.handle_mut());
                 CustomEvent::new(GlobalRef::Window(window.r()),
                                  event_name,
                                  true,
                                  true,
                                  detail.handle())
             };
--- a/servo/components/script/dom/htmlscriptelement.rs
+++ b/servo/components/script/dom/htmlscriptelement.rs
@@ -25,17 +25,16 @@ use dom::node::{document_from_node, wind
 use dom::virtualmethods::VirtualMethods;
 use dom::window::ScriptHelpers;
 use encoding::label::encoding_from_whatwg_label;
 use encoding::types::{DecoderTrap, EncodingRef};
 use html5ever::tree_builder::NextParserState;
 use hyper::http::RawStatus;
 use ipc_channel::ipc;
 use ipc_channel::router::ROUTER;
-use js::jsapi::RootedValue;
 use js::jsval::UndefinedValue;
 use net_traits::{AsyncResponseListener, AsyncResponseTarget, Metadata, NetworkError};
 use network_listener::{NetworkListener, PreInvoke};
 use std::ascii::AsciiExt;
 use std::cell::Cell;
 use std::mem;
 use std::sync::{Arc, Mutex};
 use string_cache::Atom;
@@ -436,17 +435,17 @@ impl HTMLScriptElement {
         let old_script = document.GetCurrentScript();
 
         // Step 2.b.5.
         document.set_current_script(Some(self));
 
         // Step 2.b.6.
         // TODO: Create a script...
         let window = window_from_node(self);
-        let mut rval = RootedValue::new(window.get_cx(), UndefinedValue());
+        rooted!(in(window.get_cx()) let mut rval = UndefinedValue());
         window.evaluate_script_on_global_with_result(&*source,
                                                          url.as_str(),
                                                          rval.handle_mut());
 
         // Step 2.b.7.
         document.set_current_script(old_script.r());
 
         // Step 2.b.8.
--- a/servo/components/script/dom/macros.rs
+++ b/servo/components/script/dom/macros.rs
@@ -498,8 +498,23 @@ macro_rules! window_event_handlers(
 // As more methods get added, just update them here.
 macro_rules! document_and_element_event_handlers(
     () => (
         event_handler!(cut, GetOncut, SetOncut);
         event_handler!(copy, GetOncopy, SetOncopy);
         event_handler!(paste, GetOnpaste, SetOnpaste);
     )
 );
+
+#[macro_export]
+macro_rules! rooted_vec {
+    (let mut $name:ident) => {
+        rooted_vec!(let mut $name <- ::std::iter::empty())
+    };
+    (let $name:ident <- $iter:expr) => {
+        let mut __root = $crate::dom::bindings::trace::RootableVec::new_unrooted();
+        let $name = $crate::dom::bindings::trace::RootedVec::new(&mut __root, $iter);
+    };
+    (let mut $name:ident <- $iter:expr) => {
+        let mut __root = $crate::dom::bindings::trace::RootableVec::new_unrooted();
+        let mut $name = $crate::dom::bindings::trace::RootedVec::new(&mut __root, $iter);
+    }
+}
--- a/servo/components/script/dom/messageevent.rs
+++ b/servo/components/script/dom/messageevent.rs
@@ -8,17 +8,17 @@ use dom::bindings::codegen::Bindings::Me
 use dom::bindings::error::Fallible;
 use dom::bindings::global::GlobalRef;
 use dom::bindings::inheritance::Castable;
 use dom::bindings::js::Root;
 use dom::bindings::reflector::reflect_dom_object;
 use dom::bindings::str::DOMString;
 use dom::event::Event;
 use dom::eventtarget::EventTarget;
-use js::jsapi::{RootedValue, HandleValue, Heap, JSContext};
+use js::jsapi::{HandleValue, Heap, JSContext};
 use js::jsval::JSVal;
 use std::default::Default;
 use string_cache::Atom;
 
 #[dom_struct]
 pub struct MessageEvent {
     event: Event,
     data: Heap<JSVal>,
@@ -61,17 +61,17 @@ impl MessageEvent {
     }
 
     pub fn Constructor(global: GlobalRef,
                        type_: DOMString,
                        init: &MessageEventBinding::MessageEventInit)
                        -> Fallible<Root<MessageEvent>> {
         // Dictionaries need to be rooted
         // https://github.com/servo/servo/issues/6381
-        let data = RootedValue::new(global.get_cx(), init.data);
+        rooted!(in(global.get_cx()) let data = init.data);
         let ev = MessageEvent::new(global, Atom::from(type_), init.parent.bubbles, init.parent.cancelable,
                                    data.handle(),
                                    init.origin.clone(), init.lastEventId.clone());
         Ok(ev)
     }
 }
 
 impl MessageEvent {
--- a/servo/components/script/dom/node.rs
+++ b/servo/components/script/dom/node.rs
@@ -23,17 +23,16 @@ use dom::bindings::error::{Error, ErrorR
 use dom::bindings::global::GlobalRef;
 use dom::bindings::inheritance::{Castable, CharacterDataTypeId, ElementTypeId};
 use dom::bindings::inheritance::{EventTargetTypeId, HTMLElementTypeId, NodeTypeId};
 use dom::bindings::js::Root;
 use dom::bindings::js::RootedReference;
 use dom::bindings::js::{JS, LayoutJS, MutNullableHeap};
 use dom::bindings::reflector::{Reflectable, reflect_dom_object};
 use dom::bindings::str::{DOMString, USVString};
-use dom::bindings::trace::RootedVec;
 use dom::bindings::xmlname::namespace_from_domstring;
 use dom::characterdata::{CharacterData, LayoutCharacterDataHelpers};
 use dom::document::{Document, DocumentSource, IsHTMLDocument};
 use dom::documentfragment::DocumentFragment;
 use dom::documenttype::DocumentType;
 use dom::element::{Element, ElementCreator};
 use dom::eventtarget::EventTarget;
 use dom::htmlbodyelement::HTMLBodyElement;
@@ -1557,17 +1556,17 @@ impl Node {
         // Step 2.
         if let Some(child) = child {
             if !parent.ranges.is_empty() {
                 let index = child.index();
                 // Steps 2.1-2.
                 parent.ranges.increase_above(parent, index, count);
             }
         }
-        let mut new_nodes = RootedVec::new();
+        rooted_vec!(let mut new_nodes);
         let new_nodes = if let NodeTypeId::DocumentFragment = node.type_id() {
             // Step 3.
             new_nodes.extend(node.children().map(|kid| JS::from_ref(&*kid)));
             // Step 4: mutation observers.
             // Step 5.
             for kid in new_nodes.r() {
                 Node::remove(*kid, node, SuppressObserver::Suppressed);
             }
@@ -1601,19 +1600,19 @@ impl Node {
 
     // https://dom.spec.whatwg.org/#concept-node-replace-all
     pub fn replace_all(node: Option<&Node>, parent: &Node) {
         // Step 1.
         if let Some(node) = node {
             Node::adopt(node, &*parent.owner_doc());
         }
         // Step 2.
-        let removed_nodes = parent.children().collect::<RootedVec<_>>();
+        rooted_vec!(let removed_nodes <- parent.children());
         // Step 3.
-        let mut added_nodes = RootedVec::new();
+        rooted_vec!(let mut added_nodes);
         let added_nodes = if let Some(node) = node.as_ref() {
             if let NodeTypeId::DocumentFragment = node.type_id() {
                 added_nodes.extend(node.children().map(|child| JS::from_ref(&*child)));
                 added_nodes.r()
             } else {
                 ref_slice(node)
             }
         } else {
@@ -2147,17 +2146,17 @@ impl NodeMethods for Node {
             // Step 11.
             Node::remove(child, self, SuppressObserver::Suppressed);
             Some(child)
         } else {
             None
         };
 
         // Step 12.
-        let mut nodes = RootedVec::new();
+        rooted_vec!(let mut nodes);
         let nodes = if node.type_id() == NodeTypeId::DocumentFragment {
             nodes.extend(node.children().map(|node| JS::from_ref(&*node)));
             nodes.r()
         } else {
             ref_slice(&node)
         };
 
         // Step 13.
--- a/servo/components/script/dom/range.rs
+++ b/servo/components/script/dom/range.rs
@@ -12,17 +12,17 @@ use dom::bindings::codegen::Bindings::Te
 use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
 use dom::bindings::error::{Error, ErrorResult, Fallible};
 use dom::bindings::global::GlobalRef;
 use dom::bindings::inheritance::Castable;
 use dom::bindings::inheritance::{CharacterDataTypeId, NodeTypeId};
 use dom::bindings::js::{JS, MutHeap, Root, RootedReference};
 use dom::bindings::reflector::{Reflector, reflect_dom_object};
 use dom::bindings::str::DOMString;
-use dom::bindings::trace::{JSTraceable, RootedVec};
+use dom::bindings::trace::JSTraceable;
 use dom::bindings::weakref::{WeakRef, WeakRefVec};
 use dom::characterdata::CharacterData;
 use dom::document::Document;
 use dom::documentfragment::DocumentFragment;
 use dom::element::Element;
 use dom::htmlbodyelement::HTMLBodyElement;
 use dom::htmlscriptelement::HTMLScriptElement;
 use dom::node::{Node, UnbindContext};
@@ -757,17 +757,17 @@ impl RangeMethods for Range {
             if let Some(text) = start_node.downcast::<CharacterData>() {
                 return text.ReplaceData(start_offset,
                                         end_offset - start_offset,
                                         DOMString::new());
             }
         }
 
         // Step 4.
-        let mut contained_children: RootedVec<JS<Node>> = RootedVec::new();
+        rooted_vec!(let mut contained_children);
         let ancestor = self.CommonAncestorContainer();
 
         let mut iter = start_node.following_nodes(ancestor.r());
 
         let mut next = iter.next();
         while let Some(child) = next {
             if self.contains(child.r()) {
                 contained_children.push(JS::from_ref(child.r()));
--- a/servo/components/script/dom/serviceworker.rs
+++ b/servo/components/script/dom/serviceworker.rs
@@ -15,17 +15,16 @@ use dom::bindings::reflector::{Reflectab
 use dom::bindings::str::{DOMString, USVString};
 use dom::client::Client;
 use dom::errorevent::ErrorEvent;
 use dom::event::{Event, EventBubbles, EventCancelable};
 use dom::eventtarget::EventTarget;
 use dom::serviceworkerglobalscope::ServiceWorkerGlobalScope;
 use dom::workerglobalscope::prepare_workerscope_init;
 use ipc_channel::ipc;
-use js::jsapi::RootedValue;
 use js::jsval::UndefinedValue;
 use script_thread::Runnable;
 use std::cell::Cell;
 use std::sync::atomic::{AtomicBool, Ordering};
 use std::sync::mpsc::{Sender, channel};
 use std::sync::{Arc, Mutex};
 use url::Url;
 
@@ -86,17 +85,17 @@ impl ServiceWorker {
                                 filename: DOMString, lineno: u32, colno: u32) {
         let worker = address.root();
 
         if worker.is_closing() {
             return;
         }
 
         let global = worker.r().global();
-        let error = RootedValue::new(global.r().get_cx(), UndefinedValue());
+        rooted!(in(global.r().get_cx()) let error = UndefinedValue());
         let errorevent = ErrorEvent::new(global.r(), atom!("error"),
                                          EventBubbles::Bubbles, EventCancelable::Cancelable,
                                          message, filename, lineno, colno, error.handle());
         errorevent.upcast::<Event>().fire(worker.upcast());
     }
 
     #[allow(unsafe_code)]
     pub fn init_service_worker(global: GlobalRef,
--- a/servo/components/script/dom/serviceworkerglobalscope.rs
+++ b/servo/components/script/dom/serviceworkerglobalscope.rs
@@ -18,17 +18,17 @@ use dom::bindings::reflector::Reflectabl
 use dom::bindings::str::DOMString;
 use dom::client::Client;
 use dom::messageevent::MessageEvent;
 use dom::serviceworker::TrustedServiceWorkerAddress;
 use dom::workerglobalscope::WorkerGlobalScope;
 use dom::workerglobalscope::WorkerGlobalScopeInit;
 use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
 use ipc_channel::router::ROUTER;
-use js::jsapi::{JS_SetInterruptCallback, JSAutoCompartment, JSContext, RootedValue};
+use js::jsapi::{JS_SetInterruptCallback, JSAutoCompartment, JSContext};
 use js::jsval::UndefinedValue;
 use js::rust::Runtime;
 use msg::constellation_msg::PipelineId;
 use net_traits::{LoadContext, load_whole_resource, CustomResponse, IpcSend};
 use rand::random;
 use script_runtime::ScriptThreadEventCategory::ServiceWorkerEvent;
 use script_runtime::{CommonScriptMsg, ScriptChan, ScriptPort, StackRootTLS, get_reports, new_rt_and_cx};
 use script_traits::{TimerEvent, TimerSource};
@@ -278,17 +278,17 @@ impl ServiceWorkerGlobalScope {
 
     fn handle_script_event(&self, msg: WorkerScriptMsg) {
         match msg {
             WorkerScriptMsg::DOMMessage(data) => {
                 let scope = self.upcast::<WorkerGlobalScope>();
                 let target = self.upcast();
                 let _ac = JSAutoCompartment::new(scope.get_cx(),
                                                  scope.reflector().get_jsobject().get());
-                let mut message = RootedValue::new(scope.get_cx(), UndefinedValue());
+                rooted!(in(scope.get_cx()) let mut message = UndefinedValue());
                 data.read(GlobalRef::Worker(scope), message.handle_mut());
                 MessageEvent::dispatch_jsval(target, GlobalRef::Worker(scope), message.handle());
             },
             WorkerScriptMsg::Common(CommonScriptMsg::RunnableMsg(_, runnable)) => {
                 runnable.handler()
             },
             WorkerScriptMsg::Common(CommonScriptMsg::RefcountCleanup(addr)) => {
                 LiveDOMReferences::cleanup(addr);
--- a/servo/components/script/dom/webglrenderingcontext.rs
+++ b/servo/components/script/dom/webglrenderingcontext.rs
@@ -28,17 +28,17 @@ use dom::webglcontextevent::WebGLContext
 use dom::webglframebuffer::WebGLFramebuffer;
 use dom::webglprogram::WebGLProgram;
 use dom::webglrenderbuffer::WebGLRenderbuffer;
 use dom::webglshader::WebGLShader;
 use dom::webgltexture::{TexParameterValue, WebGLTexture};
 use dom::webgluniformlocation::WebGLUniformLocation;
 use euclid::size::Size2D;
 use ipc_channel::ipc::{self, IpcSender};
-use js::jsapi::{JSContext, JS_GetArrayBufferViewType, JSObject, RootedValue, Type};
+use js::jsapi::{JSContext, JS_GetArrayBufferViewType, JSObject, Type};
 use js::jsval::{BooleanValue, DoubleValue, Int32Value, JSVal, NullValue, UndefinedValue};
 use net_traits::image::base::PixelFormat;
 use net_traits::image_cache_thread::ImageResponse;
 use offscreen_gl_context::{GLContextAttributes, GLLimits};
 use script_traits::ScriptMsg as ConstellationMsg;
 use std::cell::Cell;
 use util::vec::byte_swap;
 use webrender_traits::WebGLError::*;
@@ -517,21 +517,21 @@ impl WebGLRenderingContextMethods for We
             .send(CanvasMsg::WebGL(WebGLCommand::GetParameter(parameter, sender)))
             .unwrap();
         match handle_potential_webgl_error!(self, receiver.recv().unwrap(), WebGLParameter::Invalid) {
             WebGLParameter::Int(val) => Int32Value(val),
             WebGLParameter::Bool(val) => BooleanValue(val),
             WebGLParameter::Float(val) => DoubleValue(val as f64),
             WebGLParameter::FloatArray(_) => panic!("Parameter should not be float array"),
             WebGLParameter::String(val) => {
-                let mut rval = RootedValue::new(cx, UndefinedValue());
+                rooted!(in(cx) let mut rval = UndefinedValue());
                 unsafe {
                     val.to_jsval(cx, rval.handle_mut());
                 }
-                rval.ptr
+                rval.get()
             }
             WebGLParameter::Invalid => NullValue(),
         }
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
     fn GetError(&self) -> u32 {
         let error_code = if let Some(error) = self.last_error.get() {
@@ -1262,39 +1262,39 @@ impl WebGLRenderingContextMethods for We
                 .map(|location| WebGLUniformLocation::new(self.global().r(), location, p.id()))
         })
     }
 
     #[allow(unsafe_code)]
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9
     fn GetVertexAttrib(&self, cx: *mut JSContext, index: u32, pname: u32) -> JSVal {
         if index == 0 && pname == constants::CURRENT_VERTEX_ATTRIB {
-            let mut result = RootedValue::new(cx, UndefinedValue());
+            rooted!(in(cx) let mut result = UndefinedValue());
             let (x, y, z, w) = self.current_vertex_attrib_0.get();
             let attrib = vec![x, y, z, w];
             unsafe {
                 attrib.to_jsval(cx, result.handle_mut());
             }
-            return result.ptr
+            return result.get()
         }
 
         let (sender, receiver) = ipc::channel().unwrap();
         self.ipc_renderer.send(CanvasMsg::WebGL(WebGLCommand::GetVertexAttrib(index, pname, sender))).unwrap();
 
         match handle_potential_webgl_error!(self, receiver.recv().unwrap(), WebGLParameter::Invalid) {
             WebGLParameter::Int(val) => Int32Value(val),
             WebGLParameter::Bool(val) => BooleanValue(val),
             WebGLParameter::String(_) => panic!("Vertex attrib should not be string"),
             WebGLParameter::Float(_) => panic!("Vertex attrib should not be float"),
             WebGLParameter::FloatArray(val) => {
-                let mut result = RootedValue::new(cx, UndefinedValue());
+                rooted!(in(cx) let mut result = UndefinedValue());
                 unsafe {
                     val.to_jsval(cx, result.handle_mut());
                 }
-                result.ptr
+                result.get()
             }
             WebGLParameter::Invalid => NullValue(),
         }
     }
 
     // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
     fn Hint(&self, target: u32, mode: u32) {
         if target != constants::GENERATE_MIPMAP_HINT {
--- a/servo/components/script/dom/websocket.rs
+++ b/servo/components/script/dom/websocket.rs
@@ -18,17 +18,17 @@ use dom::bindings::reflector::{Reflectab
 use dom::bindings::str::{DOMString, USVString, is_token};
 use dom::blob::{Blob, BlobImpl, DataSlice};
 use dom::closeevent::CloseEvent;
 use dom::event::{Event, EventBubbles, EventCancelable};
 use dom::eventtarget::EventTarget;
 use dom::messageevent::MessageEvent;
 use dom::urlhelper::UrlHelper;
 use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
-use js::jsapi::{JSAutoCompartment, RootedValue};
+use js::jsapi::JSAutoCompartment;
 use js::jsapi::{JS_GetArrayBufferData, JS_NewArrayBuffer};
 use js::jsval::UndefinedValue;
 use libc::{uint32_t, uint8_t};
 use net_traits::CookieSource::HTTP;
 use net_traits::CoreResourceMsg::{WebsocketConnect, SetCookiesForUrl};
 use net_traits::MessageData;
 use net_traits::hosts::replace_hosts;
 use net_traits::unwrap_websocket_protocol;
@@ -580,17 +580,17 @@ impl Runnable for MessageReceivedTask {
         }
 
         // Step 2-5.
         let global = ws.r().global();
         // global.get_cx() returns a valid `JSContext` pointer, so this is safe.
         unsafe {
             let cx = global.r().get_cx();
             let _ac = JSAutoCompartment::new(cx, ws.reflector().get_jsobject().get());
-            let mut message = RootedValue::new(cx, UndefinedValue());
+            rooted!(in(cx) let mut message = UndefinedValue());
             match self.message {
                 MessageData::Text(text) => text.to_jsval(cx, message.handle_mut()),
                 MessageData::Binary(data) => {
                     match ws.binary_type.get() {
                         BinaryType::Blob => {
                             let slice = DataSlice::from_bytes(data);
                             let blob = Blob::new(global.r(), BlobImpl::new_from_slice(slice), "".to_owned());
                             blob.to_jsval(cx, message.handle_mut());
--- a/servo/components/script/dom/worker.rs
+++ b/servo/components/script/dom/worker.rs
@@ -18,17 +18,17 @@ use dom::bindings::str::DOMString;
 use dom::bindings::structuredclone::StructuredCloneData;
 use dom::dedicatedworkerglobalscope::DedicatedWorkerGlobalScope;
 use dom::errorevent::ErrorEvent;
 use dom::event::{Event, EventBubbles, EventCancelable};
 use dom::eventtarget::EventTarget;
 use dom::messageevent::MessageEvent;
 use dom::workerglobalscope::prepare_workerscope_init;
 use ipc_channel::ipc;
-use js::jsapi::{HandleValue, JSContext, RootedValue, JSAutoCompartment};
+use js::jsapi::{HandleValue, JSContext, JSAutoCompartment};
 use js::jsval::UndefinedValue;
 use script_thread::Runnable;
 use std::sync::atomic::{AtomicBool, Ordering};
 use std::sync::mpsc::{Sender, channel};
 use std::sync::{Arc, Mutex};
 
 pub type TrustedWorkerAddress = Trusted<Worker>;
 
@@ -110,17 +110,17 @@ impl Worker {
 
         if worker.is_closing() {
             return;
         }
 
         let global = worker.r().global();
         let target = worker.upcast();
         let _ac = JSAutoCompartment::new(global.r().get_cx(), target.reflector().get_jsobject().get());
-        let mut message = RootedValue::new(global.r().get_cx(), UndefinedValue());
+        rooted!(in(global.r().get_cx()) let mut message = UndefinedValue());
         data.read(global.r(), message.handle_mut());
         MessageEvent::dispatch_jsval(target, global.r(), message.handle());
     }
 
     pub fn dispatch_simple_error(address: TrustedWorkerAddress) {
         let worker = address.root();
         worker.upcast().fire_simple_event("error");
     }
@@ -129,17 +129,17 @@ impl Worker {
                                 filename: DOMString, lineno: u32, colno: u32) {
         let worker = address.root();
 
         if worker.is_closing() {
             return;
         }
 
         let global = worker.r().global();
-        let error = RootedValue::new(global.r().get_cx(), UndefinedValue());
+        rooted!(in(global.r().get_cx()) let error = UndefinedValue());
         let errorevent = ErrorEvent::new(global.r(), atom!("error"),
                                          EventBubbles::Bubbles, EventCancelable::Cancelable,
                                          message, filename, lineno, colno, error.handle());
         errorevent.upcast::<Event>().fire(worker.upcast());
     }
 }
 
 impl WorkerMethods for Worker {
--- a/servo/components/script/dom/workerglobalscope.rs
+++ b/servo/components/script/dom/workerglobalscope.rs
@@ -16,17 +16,17 @@ use dom::crypto::Crypto;
 use dom::dedicatedworkerglobalscope::DedicatedWorkerGlobalScope;
 use dom::eventtarget::EventTarget;
 use dom::serviceworkerglobalscope::ServiceWorkerGlobalScope;
 use dom::window::{base64_atob, base64_btoa};
 use dom::workerlocation::WorkerLocation;
 use dom::workernavigator::WorkerNavigator;
 use ipc_channel::ipc::{self, IpcSender};
 use ipc_channel::router::ROUTER;
-use js::jsapi::{HandleValue, JSContext, JSRuntime, RootedValue};
+use js::jsapi::{HandleValue, JSContext, JSRuntime};
 use js::jsval::UndefinedValue;
 use js::rust::Runtime;
 use msg::constellation_msg::{PipelineId, ReferrerPolicy, PanicMsg};
 use net_traits::{LoadContext, ResourceThreads, load_whole_resource};
 use net_traits::{RequestSource, LoadOrigin, CustomResponseSender, IpcSend};
 use profile_traits::{mem, time};
 use script_runtime::{CommonScriptMsg, ScriptChan, ScriptPort, maybe_take_panic_result};
 use script_traits::ScriptMsg as ConstellationMsg;
@@ -304,17 +304,17 @@ impl WorkerGlobalScopeMethods for Worker
         for url in url_strings {
             let url = self.worker_url.join(&url);
             match url {
                 Ok(url) => urls.push(url),
                 Err(_) => return Err(Error::Syntax),
             };
         }
 
-        let mut rval = RootedValue::new(self.runtime.cx(), UndefinedValue());
+        rooted!(in(self.runtime.cx()) let mut rval = UndefinedValue());
         for url in urls {
             let (url, source) = match load_whole_resource(LoadContext::Script,
                                                           &self.resource_threads.sender(),
                                                           url,
                                                           self) {
                 Err(_) => return Err(Error::Network),
                 Ok((metadata, bytes)) => {
                     (metadata.final_url, String::from_utf8(bytes).unwrap())
@@ -415,17 +415,17 @@ impl WorkerGlobalScopeMethods for Worker
         self.ClearTimeout(handle);
     }
 }
 
 
 impl WorkerGlobalScope {
     #[allow(unsafe_code)]
     pub fn execute_script(&self, source: DOMString) {
-        let mut rval = RootedValue::new(self.runtime.cx(), UndefinedValue());
+        rooted!(in(self.runtime.cx()) let mut rval = UndefinedValue());
         match self.runtime.evaluate_script(
             self.reflector().get_jsobject(), &source, self.worker_url.as_str(), 1, rval.handle_mut()) {
             Ok(_) => (),
             Err(_) => {
                 if self.is_closing() {
                     println!("evaluate_script failed (terminated)");
                 } else {
                     // TODO: An error needs to be dispatched to the parent.
--- a/servo/components/script/dom/xmlhttprequest.rs
+++ b/servo/components/script/dom/xmlhttprequest.rs
@@ -35,17 +35,17 @@ use euclid::length::Length;
 use hyper::header::Headers;
 use hyper::header::{ContentLength, ContentType};
 use hyper::http::RawStatus;
 use hyper::method::Method;
 use hyper::mime::{self, Mime, Attr as MimeAttr, Value as MimeValue};
 use ipc_channel::ipc;
 use ipc_channel::router::ROUTER;
 use js::jsapi::JS_ClearPendingException;
-use js::jsapi::{JSContext, JS_ParseJSON, RootedValue};
+use js::jsapi::{JSContext, JS_ParseJSON};
 use js::jsval::{JSVal, NullValue, UndefinedValue};
 use msg::constellation_msg::{PipelineId, ReferrerPolicy};
 use net_traits::CoreResourceMsg::Fetch;
 use net_traits::request::{CredentialsMode, Destination, RequestInit, RequestMode};
 use net_traits::trim_http_whitespace;
 use net_traits::{CoreResourceThread, LoadOrigin};
 use net_traits::{FetchResponseListener, Metadata, NetworkError, RequestSource};
 use network_listener::{NetworkListener, PreInvoke};
@@ -766,17 +766,17 @@ impl XMLHttpRequestMethods for XMLHttpRe
             }
         }
     }
 
     #[allow(unsafe_code)]
     // https://xhr.spec.whatwg.org/#the-response-attribute
     fn Response(&self, cx: *mut JSContext) -> JSVal {
         unsafe {
-            let mut rval = RootedValue::new(cx, UndefinedValue());
+            rooted!(in(cx) let mut rval = UndefinedValue());
             match self.response_type.get() {
                 XMLHttpRequestResponseType::_empty | XMLHttpRequestResponseType::Text => {
                     let ready_state = self.ready_state.get();
                     // Step 2
                     if ready_state == XMLHttpRequestState::Done || ready_state == XMLHttpRequestState::Loading {
                         self.text_response().to_jsval(cx, rval.handle_mut());
                     } else {
                     // Step 1
@@ -803,17 +803,17 @@ impl XMLHttpRequestMethods for XMLHttpRe
                 XMLHttpRequestResponseType::Blob => {
                     self.blob_response().to_jsval(cx, rval.handle_mut());
                 },
                 _ => {
                     // XXXManishearth handle other response types
                     self.response.borrow().to_jsval(cx, rval.handle_mut());
                 }
             }
-            rval.ptr
+            rval.get()
         }
     }
 
     // https://xhr.spec.whatwg.org/#the-responsetext-attribute
     fn GetResponseText(&self) -> Fallible<USVString> {
         match self.response_type.get() {
             XMLHttpRequestResponseType::_empty | XMLHttpRequestResponseType::Text => {
                 Ok(USVString(String::from(match self.ready_state.get() {
@@ -1171,28 +1171,28 @@ impl XMLHttpRequest {
         // Step 3
         if bytes.len() == 0 {
             return NullValue();
         }
         // Step 4
         let json_text = UTF_8.decode(&bytes, DecoderTrap::Replace).unwrap();
         let json_text: Vec<u16> = json_text.encode_utf16().collect();
         // Step 5
-        let mut rval = RootedValue::new(cx, UndefinedValue());
+        rooted!(in(cx) let mut rval = UndefinedValue());
         unsafe {
             if !JS_ParseJSON(cx,
                              json_text.as_ptr(),
                              json_text.len() as u32,
                              rval.handle_mut()) {
                 JS_ClearPendingException(cx);
                 return NullValue();
             }
         }
         // Step 6
-        self.response_json.set(rval.ptr);
+        self.response_json.set(rval.get());
         self.response_json.get()
     }
 
     fn document_text_html(&self) -> Root<Document>{
         let charset = self.final_charset().unwrap_or(UTF_8);
         let wr = self.global();
         let wr = wr.r();
         let decoded = charset.decode(&self.response.borrow(), DecoderTrap::Replace).unwrap();
--- a/servo/components/script/lib.rs
+++ b/servo/components/script/lib.rs
@@ -1,17 +1,17 @@
 /* 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/. */
 
 #![feature(as_unsafe_cell)]
 #![feature(borrow_state)]
 #![feature(box_syntax)]
 #![feature(const_fn)]
-#![feature(core_intrinsics)]
+#![cfg_attr(debug_assertions, feature(core_intrinsics))]
 #![feature(custom_attribute)]
 #![feature(custom_derive)]
 #![feature(fnbox)]
 #![feature(iter_arith)]
 #![feature(mpsc_select)]
 #![feature(nonzero)]
 #![feature(on_unimplemented)]
 #![feature(optin_builtin_traits)]
@@ -45,16 +45,17 @@ extern crate encoding;
 extern crate euclid;
 extern crate fnv;
 extern crate gfx_traits;
 extern crate heapsize;
 extern crate html5ever;
 extern crate hyper;
 extern crate image;
 extern crate ipc_channel;
+#[macro_use]
 extern crate js;
 extern crate libc;
 #[macro_use]
 extern crate log;
 #[macro_use]
 extern crate mime;
 extern crate mime_guess;
 extern crate msg;
--- a/servo/components/script/script_thread.rs
+++ b/servo/components/script/script_thread.rs
@@ -53,17 +53,17 @@ use euclid::point::Point2D;
 use gfx_traits::LayerId;
 use hyper::header::{ContentType, HttpDate};
 use hyper::header::{Headers, LastModified};
 use hyper::method::Method;
 use hyper::mime::{Mime, SubLevel, TopLevel};
 use ipc_channel::ipc::{self, IpcSender};
 use ipc_channel::router::ROUTER;
 use js::glue::GetWindowProxyClass;
-use js::jsapi::{DOMProxyShadowsResult, HandleId, HandleObject, RootedValue};
+use js::jsapi::{DOMProxyShadowsResult, HandleId, HandleObject};
 use js::jsapi::{JSAutoCompartment, JSContext, JS_SetWrapObjectCallbacks};
 use js::jsapi::{JSTracer, SetWindowProxyClass};
 use js::jsval::UndefinedValue;
 use js::rust::Runtime;
 use mem::heap_size_of_self_and_children;
 use msg::constellation_msg::{FrameType, LoadData, PanicMsg, PipelineId, PipelineNamespace};
 use msg::constellation_msg::{SubpageId, WindowSizeData, WindowSizeType};
 use msg::webdriver_msg::WebDriverScriptCommand;
@@ -1742,17 +1742,17 @@ impl ScriptThread {
             let encoded = &incomplete.url[Position::BeforePath..];
 
             // Percent-decode (8.) and UTF-8 decode (9.)
             let script_source = percent_decode(encoded.as_bytes()).decode_utf8_lossy();
 
             // Script source is ready to be evaluated (11.)
             unsafe {
                 let _ac = JSAutoCompartment::new(self.get_cx(), window.reflector().get_jsobject().get());
-                let mut jsval = RootedValue::new(self.get_cx(), UndefinedValue());
+                rooted!(in(self.get_cx()) let mut jsval = UndefinedValue());
                 window.evaluate_js_on_global_with_result(&script_source, jsval.handle_mut());
                 let strval = DOMString::from_jsval(self.get_cx(),
                                                    jsval.handle(),
                                                    StringificationBehavior::Empty);
                 strval.unwrap_or(DOMString::new())
             }
         } else {
             DOMString::new()
--- a/servo/components/script/timers.rs
+++ b/servo/components/script/timers.rs
@@ -8,17 +8,17 @@ use dom::bindings::codegen::Bindings::Fu
 use dom::bindings::global::GlobalRef;
 use dom::bindings::reflector::Reflectable;
 use dom::bindings::str::DOMString;
 use dom::window::ScriptHelpers;
 use dom::xmlhttprequest::XHRTimeoutCallback;
 use euclid::length::Length;
 use heapsize::HeapSizeOf;
 use ipc_channel::ipc::IpcSender;
-use js::jsapi::{HandleValue, Heap, RootedValue};
+use js::jsapi::{HandleValue, Heap};
 use js::jsval::{JSVal, UndefinedValue};
 use script_traits::{MsDuration, precise_time_ms};
 use script_traits::{TimerEvent, TimerEventId, TimerEventRequest, TimerSource};
 use std::cell::Cell;
 use std::cmp::{self, Ord, Ordering};
 use std::collections::HashMap;
 use std::default::Default;
 use std::rc::Rc;
@@ -483,17 +483,17 @@ impl JsTimerTask {
 
         // prep for step 6 in nested set_timeout_or_interval calls
         timers.nesting_level.set(self.nesting_level);
 
         // step 4.2
         match *&self.callback {
             InternalTimerCallback::StringTimerCallback(ref code_str) => {
                 let cx = this.global().r().get_cx();
-                let mut rval = RootedValue::new(cx, UndefinedValue());
+                rooted!(in(cx) let mut rval = UndefinedValue());
 
                 this.evaluate_js_on_global_with_result(code_str, rval.handle_mut());
             },
             InternalTimerCallback::FunctionTimerCallback(ref function, ref arguments) => {
                 let arguments: Vec<JSVal> = arguments.iter().map(|arg| arg.get()).collect();
                 let arguments = arguments.iter().by_ref().map(|arg| unsafe {
                     HandleValue::from_marked_location(arg)
                 }).collect();
--- a/servo/components/script/webdriver_handlers.rs
+++ b/servo/components/script/webdriver_handlers.rs
@@ -23,18 +23,17 @@ use dom::htmliframeelement::HTMLIFrameEl
 use dom::htmlinputelement::HTMLInputElement;
 use dom::htmloptionelement::HTMLOptionElement;
 use dom::node::Node;
 use dom::window::ScriptHelpers;
 use euclid::point::Point2D;
 use euclid::rect::Rect;
 use euclid::size::Size2D;
 use ipc_channel::ipc::{self, IpcSender};
-use js::jsapi::JSContext;
-use js::jsapi::{HandleValue, RootedValue};
+use js::jsapi::{JSContext, HandleValue};
 use js::jsval::UndefinedValue;
 use msg::constellation_msg::PipelineId;
 use msg::webdriver_msg::WebDriverCookieError;
 use msg::webdriver_msg::{WebDriverFrameId, WebDriverJSError, WebDriverJSResult, WebDriverJSValue};
 use net_traits::CookieSource::{HTTP, NonHTTP};
 use net_traits::CoreResourceMsg::{GetCookiesDataForUrl, SetCookiesForUrlWithData};
 use net_traits::IpcSend;
 use script_thread::get_browsing_context;
@@ -72,32 +71,32 @@ pub unsafe fn jsval_to_webdriver(cx: *mu
 pub fn handle_execute_script(context: &BrowsingContext,
                              pipeline: PipelineId,
                              eval: String,
                              reply: IpcSender<WebDriverJSResult>) {
     let context = get_browsing_context(&context, pipeline);
     let window = context.active_window();
     let result = unsafe {
         let cx = window.get_cx();
-        let mut rval = RootedValue::new(cx, UndefinedValue());
+        rooted!(in(cx) let mut rval = UndefinedValue());
         window.evaluate_js_on_global_with_result(&eval, rval.handle_mut());
         jsval_to_webdriver(cx, rval.handle())
     };
     reply.send(result).unwrap();
 }
 
 pub fn handle_execute_async_script(context: &BrowsingContext,
                                    pipeline: PipelineId,
                                    eval: String,
                                    reply: IpcSender<WebDriverJSResult>) {
     let context = get_browsing_context(&context, pipeline);
     let window = context.active_window();
     let cx = window.get_cx();
     window.set_webdriver_script_chan(Some(reply));
-    let mut rval = RootedValue::new(cx, UndefinedValue());
+    rooted!(in(cx) let mut rval = UndefinedValue());
     window.evaluate_js_on_global_with_result(&eval, rval.handle_mut());
 }
 
 pub fn handle_get_frame_id(context: &BrowsingContext,
                            pipeline: PipelineId,
                            webdriver_frame_id: WebDriverFrameId,
                            reply: IpcSender<Result<Option<PipelineId>, ()>>) {
     let window = match webdriver_frame_id {
--- a/servo/components/servo/Cargo.lock
+++ b/servo/components/servo/Cargo.lock
@@ -1077,17 +1077,17 @@ source = "registry+https://github.com/ru
 dependencies = [
  "byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "rayon 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "js"
 version = "0.1.3"
-source = "git+https://github.com/servo/rust-mozjs#707bfb4ff4fe2c0abde1cc2bb87ac35ff8f40aaa"
+source = "git+https://github.com/servo/rust-mozjs#7ccfee50f407841b8cd03b6520a2b9db866ac90a"
 dependencies = [
  "heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "mozjs_sys 0.0.0 (git+https://github.com/servo/mozjs)",
  "num-traits 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
--- a/servo/ports/cef/Cargo.lock
+++ b/servo/ports/cef/Cargo.lock
@@ -986,17 +986,17 @@ source = "registry+https://github.com/ru
 dependencies = [
  "byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "rayon 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "js"
 version = "0.1.3"
-source = "git+https://github.com/servo/rust-mozjs#707bfb4ff4fe2c0abde1cc2bb87ac35ff8f40aaa"
+source = "git+https://github.com/servo/rust-mozjs#7ccfee50f407841b8cd03b6520a2b9db866ac90a"
 dependencies = [
  "heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "mozjs_sys 0.0.0 (git+https://github.com/servo/mozjs)",
  "num-traits 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
 ]