Merge mozilla-central and inbound
authorEd Morley <emorley@mozilla.com>
Mon, 09 Sep 2013 13:29:36 +0100
changeset 146174 11abb5d7d409884e5df82889f7f24d9636919493
parent 146137 218d4334d29e5d55bcb70498bb66228d8b4463c5 (current diff)
parent 146173 2b26501fd20357d4dc10dd39fa36c47f5baf2ef3 (diff)
child 146175 5c9f3fb14995931208c04d29ad4f6cc6616616ac
push id25244
push userryanvm@gmail.com
push dateMon, 09 Sep 2013 20:03:14 +0000
treeherdermozilla-central@f320b8c034bd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone26.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central and inbound
js/src/vm/GlobalObject-inl.h
--- a/content/canvas/src/WebGLContext.h
+++ b/content/canvas/src/WebGLContext.h
@@ -71,17 +71,17 @@ class WebGLShaderPrecisionFormat;
 class WebGLTexture;
 class WebGLVertexArray;
 
 namespace dom {
 class ImageData;
 
 struct WebGLContextAttributes;
 struct WebGLContextAttributesInitializer;
-template<typename> class Nullable;
+template<typename> struct Nullable;
 }
 
 using WebGLTexelConversions::WebGLTexelFormat;
 
 WebGLTexelFormat GetWebGLTexelFormat(GLenum format, GLenum type);
 
 struct WebGLContextOptions {
     // these are defaults
--- a/content/events/public/EventTarget.h
+++ b/content/events/public/EventTarget.h
@@ -17,17 +17,17 @@ class nsIDOMEventListener;
 namespace mozilla {
 
 class ErrorResult;
 
 namespace dom {
 
 class EventListener;
 class EventHandlerNonNull;
-template <class T> class Nullable;
+template <class T> struct Nullable;
 
 // IID for the dom::EventTarget interface
 #define NS_EVENTTARGET_IID \
 { 0x0a5aed21, 0x0bab, 0x48b3, \
  { 0xbe, 0x4b, 0xd4, 0xf9, 0xd4, 0xea, 0xc7, 0xdb } }
 
 class EventTarget : public nsIDOMEventTarget,
                     public nsWrapperCache
--- a/content/html/content/src/HTMLPropertiesCollection.cpp
+++ b/content/html/content/src/HTMLPropertiesCollection.cpp
@@ -8,16 +8,17 @@
 #include "nsIDocument.h"
 #include "nsContentUtils.h"
 #include "nsGenericHTMLElement.h"
 #include "nsVariant.h"
 #include "nsDOMSettableTokenList.h"
 #include "nsAttrValue.h"
 #include "nsWrapperCacheInlines.h"
 #include "mozilla/dom/HTMLPropertiesCollectionBinding.h"
+#include "jsapi.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLPropertiesCollection)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(HTMLPropertiesCollection)
   // SetDocument(nullptr) ensures that we remove ourselves as a mutation observer
--- a/content/xul/document/src/nsXULPrototypeCache.cpp
+++ b/content/xul/document/src/nsXULPrototypeCache.cpp
@@ -18,16 +18,17 @@
 #include "nsIObserverService.h"
 #include "nsIStringStream.h"
 #include "nsIStorageStream.h"
 
 #include "nsNetUtil.h"
 #include "nsAppDirectoryServiceDefs.h"
 
 #include "jsapi.h"
+#include "js/Tracer.h"
 
 #include "mozilla/Preferences.h"
 #include "mozilla/scache/StartupCache.h"
 #include "mozilla/scache/StartupCacheUtils.h"
 #include "mozilla/Telemetry.h"
 
 using namespace mozilla;
 using namespace mozilla::scache;
--- a/dom/base/nsWrapperCacheInlines.h
+++ b/dom/base/nsWrapperCacheInlines.h
@@ -3,17 +3,17 @@
  * 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/. */
 
 #ifndef nsWrapperCacheInline_h___
 #define nsWrapperCacheInline_h___
 
 #include "nsWrapperCache.h"
 #include "js/GCAPI.h"
-#include "jsapi.h"
+#include "js/Tracer.h"
 
 inline JSObject*
 nsWrapperCache::GetWrapper() const
 {
     JSObject* obj = GetWrapperPreserveColor();
     if (obj) {
       JS::ExposeObjectToActiveJS(obj);
     }
--- a/dom/bindings/TypedArray.h
+++ b/dom/bindings/TypedArray.h
@@ -5,16 +5,17 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_TypedArray_h
 #define mozilla_dom_TypedArray_h
 
 #include "jsapi.h"
 #include "jsfriendapi.h"
 #include "js/RootingAPI.h"
+#include "js/Tracer.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/Util.h" // for Maybe
 #include "nsWrapperCache.h"
 
 namespace mozilla {
 namespace dom {
 
--- a/dom/workers/EventListenerManager.cpp
+++ b/dom/workers/EventListenerManager.cpp
@@ -2,18 +2,19 @@
 /* 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/. */
 
 #include "EventListenerManager.h"
 
 #include "jsapi.h"
 #include "jsfriendapi.h"
+#include "js/GCAPI.h"
+#include "js/Tracer.h"
 #include "js/Vector.h"
-#include "js/GCAPI.h"
 #include "mozilla/Util.h"
 #include "nsAutoJSValHolder.h"
 
 #include "Events.h"
 #include "EventTarget.h"
 
 using namespace mozilla::dom;
 using namespace mozilla;
--- a/js/public/Class.h
+++ b/js/public/Class.h
@@ -287,21 +287,16 @@ typedef bool
 // structures. The only exception for this rule is the case when the embedding
 // needs a tight integration with GC. In that case the embedding can check if
 // the traversal is a part of the marking phase through calling
 // JS_IsGCMarkingTracer and apply a special code like emptying caches or
 // marking its native structures.
 typedef void
 (* JSTraceOp)(JSTracer *trc, JSObject *obj);
 
-// Callback that JSTraceOp implementation can provide to return a string
-// describing the reference traced with JS_CallTracer.
-typedef void
-(* JSTraceNamePrinter)(JSTracer *trc, char *buf, size_t bufsize);
-
 // A generic type for functions mapping an object to another object, or null
 // if an error or exception was thrown on cx.
 typedef JSObject *
 (* JSObjectOp)(JSContext *cx, JS::Handle<JSObject*> obj);
 
 // Hook that creates an iterator object for a given object. Returns the
 // iterator object or null if an error or exception was thrown on cx.
 typedef JSObject *
--- a/js/public/HeapAPI.h
+++ b/js/public/HeapAPI.h
@@ -8,16 +8,22 @@
 #define js_HeapAPI_h
 
 #include "jspubtd.h"
 
 #include "js/Utility.h"
 
 /* These values are private to the JS engine. */
 namespace js {
+
+// Whether the current thread is permitted access to any part of the specified
+// runtime or zone.
+extern bool CurrentThreadCanAccessRuntime(JSRuntime *rt);
+extern bool CurrentThreadCanAccessZone(JS::Zone *zone);
+
 namespace gc {
 
 const size_t ArenaShift = 12;
 const size_t ArenaSize = size_t(1) << ArenaShift;
 const size_t ArenaMask = ArenaSize - 1;
 
 const size_t ChunkShift = 20;
 const size_t ChunkSize = size_t(1) << ChunkShift;
@@ -52,19 +58,53 @@ namespace shadow {
 
 struct ArenaHeader
 {
     JS::Zone *zone;
 };
 
 struct Zone
 {
+  protected:
+    JSRuntime *const runtime_;
+    JSTracer *const barrierTracer_;     // A pointer to the JSRuntime's |gcMarker|.
+
+  public:
     bool needsBarrier_;
 
-    Zone() : needsBarrier_(false) {}
+    Zone(JSRuntime *runtime, JSTracer *barrierTracerArg)
+      : runtime_(runtime),
+        barrierTracer_(barrierTracerArg),
+        needsBarrier_(false)
+    {}
+
+    bool needsBarrier() const {
+        return needsBarrier_;
+    }
+
+    JSTracer *barrierTracer() {
+        JS_ASSERT(needsBarrier_);
+        JS_ASSERT(js::CurrentThreadCanAccessRuntime(runtime_));
+        return barrierTracer_;
+    }
+
+    JSRuntime *runtimeFromMainThread() const {
+        JS_ASSERT(js::CurrentThreadCanAccessRuntime(runtime_));
+        return runtime_;
+    }
+
+    // Note: Unrestricted access to the zone's runtime from an arbitrary
+    // thread can easily lead to races. Use this method very carefully.
+    JSRuntime *runtimeFromAnyThread() const {
+        return runtime_;
+    }
+
+    static JS::shadow::Zone *asShadowZone(JS::Zone *zone) {
+        return reinterpret_cast<JS::shadow::Zone*>(zone);
+    }
 };
 
 } /* namespace shadow */
 } /* namespace JS */
 
 namespace js {
 namespace gc {
 
new file mode 100644
--- /dev/null
+++ b/js/public/Tracer.h
@@ -0,0 +1,191 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * 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/. */
+
+#ifndef js_Tracer_h
+#define js_Tracer_h
+
+#include "jspubtd.h"
+
+struct JSTracer;
+
+namespace JS {
+template <typename T> class Heap;
+template <typename T> class TenuredHeap;
+}
+
+// Tracer callback, called for each traceable thing directly referenced by a
+// particular object or runtime structure. It is the callback responsibility
+// to ensure the traversal of the full object graph via calling eventually
+// JS_TraceChildren on the passed thing. In this case the callback must be
+// prepared to deal with cycles in the traversal graph.
+//
+// kind argument is one of JSTRACE_OBJECT, JSTRACE_STRING or a tag denoting
+// internal implementation-specific traversal kind. In the latter case the only
+// operations on thing that the callback can do is to call JS_TraceChildren or
+// JS_GetTraceThingInfo.
+//
+// If eagerlyTraceWeakMaps is true, when we trace a WeakMap visit all
+// of its mappings. This should be used in cases where the tracer
+// wants to use the existing liveness of entries.
+typedef void
+(* JSTraceCallback)(JSTracer *trc, void **thingp, JSGCTraceKind kind);
+
+// Callback that JSTraceOp implementation can provide to return a string
+// describing the reference traced with JS_CallTracer.
+typedef void
+(* JSTraceNamePrinter)(JSTracer *trc, char *buf, size_t bufsize);
+
+enum WeakMapTraceKind {
+    DoNotTraceWeakMaps = 0,
+    TraceWeakMapValues = 1,
+    TraceWeakMapKeysValues = 2
+};
+
+struct JSTracer {
+    JSRuntime           *runtime;
+    JSTraceCallback     callback;
+    JSTraceNamePrinter  debugPrinter;
+    const void          *debugPrintArg;
+    size_t              debugPrintIndex;
+    WeakMapTraceKind    eagerlyTraceWeakMaps;
+#ifdef JS_GC_ZEAL
+    void                *realLocation;
+#endif
+};
+
+// Set debugging information about a reference to a traceable thing to prepare
+// for the following call to JS_CallTracer.
+//
+// When printer is null, arg must be const char * or char * C string naming
+// the reference and index must be either (size_t)-1 indicating that the name
+// alone describes the reference or it must be an index into some array vector
+// that stores the reference.
+//
+// When printer callback is not null, the arg and index arguments are
+// available to the callback as debugPrintArg and debugPrintIndex fields
+// of JSTracer.
+//
+// The storage for name or callback's arguments needs to live only until
+// the following call to JS_CallTracer returns.
+//
+# define JS_SET_TRACING_DETAILS(trc, printer, arg, index)                     \
+    JS_BEGIN_MACRO                                                            \
+        (trc)->debugPrinter = (printer);                                      \
+        (trc)->debugPrintArg = (arg);                                         \
+        (trc)->debugPrintIndex = (index);                                     \
+    JS_END_MACRO
+
+// Sets the real location for a marked reference, when passing the address
+// directly is not feasable.
+//
+// FIXME: This is currently overcomplicated by our need to nest calls for Values
+// stored as keys in hash tables, but will get simplified once we can rekey
+// in-place.
+//
+#ifdef JS_GC_ZEAL
+# define JS_SET_TRACING_LOCATION(trc, location)                               \
+    JS_BEGIN_MACRO                                                            \
+        if (!(trc)->realLocation || !(location))                              \
+            (trc)->realLocation = (location);                                 \
+    JS_END_MACRO
+# define JS_UNSET_TRACING_LOCATION(trc)                                       \
+    JS_BEGIN_MACRO                                                            \
+        (trc)->realLocation = NULL;                                           \
+    JS_END_MACRO
+#else
+# define JS_SET_TRACING_LOCATION(trc, location)                               \
+    JS_BEGIN_MACRO                                                            \
+    JS_END_MACRO
+# define JS_UNSET_TRACING_LOCATION(trc)                                       \
+    JS_BEGIN_MACRO                                                            \
+    JS_END_MACRO
+#endif
+
+// Convenience macro to describe the argument of JS_CallTracer using C string
+// and index.
+# define JS_SET_TRACING_INDEX(trc, name, index)                               \
+    JS_SET_TRACING_DETAILS(trc, NULL, name, index)
+
+// Convenience macro to describe the argument of JS_CallTracer using C string.
+# define JS_SET_TRACING_NAME(trc, name)                                       \
+    JS_SET_TRACING_DETAILS(trc, NULL, name, (size_t)-1)
+
+// The JS_Call*Tracer family of functions traces the given GC thing reference.
+// This performs the tracing action configured on the given JSTracer:
+// typically calling the JSTracer::callback or marking the thing as live.
+//
+// The argument to JS_Call*Tracer is an in-out param: when the function
+// returns, the garbage collector might have moved the GC thing. In this case,
+// the reference passed to JS_Call*Tracer will be updated to the object's new
+// location. Callers of this method are responsible for updating any state
+// that is dependent on the object's address. For example, if the object's
+// address is used as a key in a hashtable, then the object must be removed
+// and re-inserted with the correct hash.
+//
+extern JS_PUBLIC_API(void)
+JS_CallValueTracer(JSTracer *trc, JS::Value *valuep, const char *name);
+
+extern JS_PUBLIC_API(void)
+JS_CallIdTracer(JSTracer *trc, jsid *idp, const char *name);
+
+extern JS_PUBLIC_API(void)
+JS_CallObjectTracer(JSTracer *trc, JSObject **objp, const char *name);
+
+extern JS_PUBLIC_API(void)
+JS_CallStringTracer(JSTracer *trc, JSString **strp, const char *name);
+
+extern JS_PUBLIC_API(void)
+JS_CallScriptTracer(JSTracer *trc, JSScript **scriptp, const char *name);
+
+extern JS_PUBLIC_API(void)
+JS_CallHeapValueTracer(JSTracer *trc, JS::Heap<JS::Value> *valuep, const char *name);
+
+extern JS_PUBLIC_API(void)
+JS_CallHeapIdTracer(JSTracer *trc, JS::Heap<jsid> *idp, const char *name);
+
+extern JS_PUBLIC_API(void)
+JS_CallHeapObjectTracer(JSTracer *trc, JS::Heap<JSObject *> *objp, const char *name);
+
+extern JS_PUBLIC_API(void)
+JS_CallHeapStringTracer(JSTracer *trc, JS::Heap<JSString *> *strp, const char *name);
+
+extern JS_PUBLIC_API(void)
+JS_CallHeapScriptTracer(JSTracer *trc, JS::Heap<JSScript *> *scriptp, const char *name);
+
+template <typename HashSetEnum>
+inline void
+JS_CallHashSetObjectTracer(JSTracer *trc, HashSetEnum &e, JSObject *const &key, const char *name)
+{
+    JSObject *updated = key;
+    JS_SET_TRACING_LOCATION(trc, reinterpret_cast<void *>(&const_cast<JSObject *&>(key)));
+    JS_CallObjectTracer(trc, &updated, name);
+    if (updated != key)
+        e.rekeyFront(key, updated);
+}
+
+// Trace an object that is known to always be tenured.  No post barriers are
+// required in this case.
+extern JS_PUBLIC_API(void)
+JS_CallTenuredObjectTracer(JSTracer *trc, JS::TenuredHeap<JSObject *> *objp, const char *name);
+
+// API for JSTraceCallback implementations.
+extern JS_PUBLIC_API(void)
+JS_TracerInit(JSTracer *trc, JSRuntime *rt, JSTraceCallback callback);
+
+extern JS_PUBLIC_API(void)
+JS_TraceChildren(JSTracer *trc, void *thing, JSGCTraceKind kind);
+
+extern JS_PUBLIC_API(void)
+JS_TraceRuntime(JSTracer *trc);
+
+extern JS_PUBLIC_API(void)
+JS_GetTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc,
+                     void *thing, JSGCTraceKind kind, bool includeDetails);
+
+extern JS_PUBLIC_API(const char *)
+JS_GetTraceEdgeName(JSTracer *trc, char *buffer, int bufferSize);
+
+#endif /* js_Tracer_h */
--- a/js/src/gc/Barrier-inl.h
+++ b/js/src/gc/Barrier-inl.h
@@ -13,25 +13,16 @@
 
 #include "gc/Marking.h"
 #include "gc/StoreBuffer.h"
 
 #include "vm/String-inl.h"
 
 namespace js {
 
-JS_ALWAYS_INLINE JS::Zone *
-ZoneOfValue(const JS::Value &value)
-{
-    JS_ASSERT(value.isMarkable());
-    if (value.isObject())
-        return value.toObject().zone();
-    return static_cast<js::gc::Cell *>(value.toGCThing())->tenuredZone();
-}
-
 template <typename T, typename Unioned>
 void
 EncapsulatedPtr<T, Unioned>::pre()
 {
     T::writeBarrierPre(value);
 }
 
 template <typename T>
@@ -48,363 +39,16 @@ template <typename T>
 inline void
 RelocatablePtr<T>::relocate(JSRuntime *rt)
 {
 #ifdef JSGC_GENERATIONAL
     T::writeBarrierPostRemove(this->value, &this->value);
 #endif
 }
 
-inline
-EncapsulatedValue::~EncapsulatedValue()
-{
-    pre();
-}
-
-inline EncapsulatedValue &
-EncapsulatedValue::operator=(const Value &v)
-{
-    pre();
-    JS_ASSERT(!IsPoisonedValue(v));
-    value = v;
-    return *this;
-}
-
-inline EncapsulatedValue &
-EncapsulatedValue::operator=(const EncapsulatedValue &v)
-{
-    pre();
-    JS_ASSERT(!IsPoisonedValue(v));
-    value = v.get();
-    return *this;
-}
-
-inline void
-EncapsulatedValue::writeBarrierPre(const Value &value)
-{
-#ifdef JSGC_INCREMENTAL
-    if (value.isMarkable() && runtimeFromAnyThread(value)->needsBarrier())
-        writeBarrierPre(ZoneOfValue(value), value);
-#endif
-}
-
-inline void
-EncapsulatedValue::writeBarrierPre(Zone *zone, const Value &value)
-{
-#ifdef JSGC_INCREMENTAL
-    if (zone->needsBarrier()) {
-        JS_ASSERT_IF(value.isMarkable(), runtimeFromMainThread(value)->needsBarrier());
-        Value tmp(value);
-        js::gc::MarkValueUnbarriered(zone->barrierTracer(), &tmp, "write barrier");
-        JS_ASSERT(tmp == value);
-    }
-#endif
-}
-
-inline void
-EncapsulatedValue::pre()
-{
-    writeBarrierPre(value);
-}
-
-inline void
-EncapsulatedValue::pre(Zone *zone)
-{
-    writeBarrierPre(zone, value);
-}
-
-inline
-HeapValue::HeapValue()
-    : EncapsulatedValue(UndefinedValue())
-{
-    post();
-}
-
-inline
-HeapValue::HeapValue(const Value &v)
-    : EncapsulatedValue(v)
-{
-    JS_ASSERT(!IsPoisonedValue(v));
-    post();
-}
-
-inline
-HeapValue::HeapValue(const HeapValue &v)
-    : EncapsulatedValue(v.value)
-{
-    JS_ASSERT(!IsPoisonedValue(v.value));
-    post();
-}
-
-inline
-HeapValue::~HeapValue()
-{
-    pre();
-}
-
-inline void
-HeapValue::init(const Value &v)
-{
-    JS_ASSERT(!IsPoisonedValue(v));
-    value = v;
-    post();
-}
-
-inline void
-HeapValue::init(JSRuntime *rt, const Value &v)
-{
-    JS_ASSERT(!IsPoisonedValue(v));
-    value = v;
-    post(rt);
-}
-
-inline HeapValue &
-HeapValue::operator=(const Value &v)
-{
-    pre();
-    JS_ASSERT(!IsPoisonedValue(v));
-    value = v;
-    post();
-    return *this;
-}
-
-inline HeapValue &
-HeapValue::operator=(const HeapValue &v)
-{
-    pre();
-    JS_ASSERT(!IsPoisonedValue(v.value));
-    value = v.value;
-    post();
-    return *this;
-}
-
-inline void
-HeapValue::set(Zone *zone, const Value &v)
-{
-#ifdef DEBUG
-    if (value.isMarkable()) {
-        JS_ASSERT(ZoneOfValue(value) == zone ||
-                  zone->runtimeFromAnyThread()->isAtomsZone(ZoneOfValue(value)));
-    }
-#endif
-
-    pre(zone);
-    JS_ASSERT(!IsPoisonedValue(v));
-    value = v;
-    post(zone->runtimeFromAnyThread());
-}
-
-inline void
-HeapValue::writeBarrierPost(const Value &value, Value *addr)
-{
-#ifdef JSGC_GENERATIONAL
-    if (value.isMarkable())
-        runtimeFromMainThread(value)->gcStoreBuffer.putValue(addr);
-#endif
-}
-
-inline void
-HeapValue::writeBarrierPost(JSRuntime *rt, const Value &value, Value *addr)
-{
-#ifdef JSGC_GENERATIONAL
-    if (value.isMarkable())
-        rt->gcStoreBuffer.putValue(addr);
-#endif
-}
-
-inline void
-HeapValue::post()
-{
-    writeBarrierPost(value, &value);
-}
-
-inline void
-HeapValue::post(JSRuntime *rt)
-{
-    writeBarrierPost(rt, value, &value);
-}
-
-inline
-RelocatableValue::RelocatableValue()
-    : EncapsulatedValue(UndefinedValue())
-{
-}
-
-inline
-RelocatableValue::RelocatableValue(const Value &v)
-    : EncapsulatedValue(v)
-{
-    JS_ASSERT(!IsPoisonedValue(v));
-    if (v.isMarkable())
-        post();
-}
-
-inline
-RelocatableValue::RelocatableValue(const RelocatableValue &v)
-    : EncapsulatedValue(v.value)
-{
-    JS_ASSERT(!IsPoisonedValue(v.value));
-    if (v.value.isMarkable())
-        post();
-}
-
-inline
-RelocatableValue::~RelocatableValue()
-{
-    if (value.isMarkable())
-        relocate(runtimeFromMainThread(value));
-}
-
-inline RelocatableValue &
-RelocatableValue::operator=(const Value &v)
-{
-    pre();
-    JS_ASSERT(!IsPoisonedValue(v));
-    if (v.isMarkable()) {
-        value = v;
-        post();
-    } else if (value.isMarkable()) {
-        JSRuntime *rt = runtimeFromMainThread(value);
-        relocate(rt);
-        value = v;
-    } else {
-        value = v;
-    }
-    return *this;
-}
-
-inline RelocatableValue &
-RelocatableValue::operator=(const RelocatableValue &v)
-{
-    pre();
-    JS_ASSERT(!IsPoisonedValue(v.value));
-    if (v.value.isMarkable()) {
-        value = v.value;
-        post();
-    } else if (value.isMarkable()) {
-        JSRuntime *rt = runtimeFromMainThread(value);
-        relocate(rt);
-        value = v.value;
-    } else {
-        value = v.value;
-    }
-    return *this;
-}
-
-inline void
-RelocatableValue::post()
-{
-#ifdef JSGC_GENERATIONAL
-    JS_ASSERT(value.isMarkable());
-    runtimeFromMainThread(value)->gcStoreBuffer.putRelocatableValue(&value);
-#endif
-}
-
-inline void
-RelocatableValue::relocate(JSRuntime *rt)
-{
-#ifdef JSGC_GENERATIONAL
-    rt->gcStoreBuffer.removeRelocatableValue(&value);
-#endif
-}
-
-inline
-HeapSlot::HeapSlot(JSObject *obj, Kind kind, uint32_t slot, const Value &v)
-    : EncapsulatedValue(v)
-{
-    JS_ASSERT(!IsPoisonedValue(v));
-    post(obj, kind, slot, v);
-}
-
-inline
-HeapSlot::HeapSlot(JSObject *obj, Kind kind, uint32_t slot, const HeapSlot &s)
-    : EncapsulatedValue(s.value)
-{
-    JS_ASSERT(!IsPoisonedValue(s.value));
-    post(obj, kind, slot, s);
-}
-
-inline
-HeapSlot::~HeapSlot()
-{
-    pre();
-}
-
-inline void
-HeapSlot::init(JSObject *obj, Kind kind, uint32_t slot, const Value &v)
-{
-    value = v;
-    post(obj, kind, slot, v);
-}
-
-inline void
-HeapSlot::init(JSRuntime *rt, JSObject *obj, Kind kind, uint32_t slot, const Value &v)
-{
-    value = v;
-    post(rt, obj, kind, slot, v);
-}
-
-inline void
-HeapSlot::set(JSObject *obj, Kind kind, uint32_t slot, const Value &v)
-{
-    JS_ASSERT_IF(kind == Slot, &obj->getSlotRef(slot) == this);
-    JS_ASSERT_IF(kind == Element, &obj->getDenseElement(slot) == (const Value *)this);
-
-    pre();
-    JS_ASSERT(!IsPoisonedValue(v));
-    value = v;
-    post(obj, kind, slot, v);
-}
-
-inline void
-HeapSlot::set(Zone *zone, JSObject *obj, Kind kind, uint32_t slot, const Value &v)
-{
-    JS_ASSERT_IF(kind == Slot, &obj->getSlotRef(slot) == this);
-    JS_ASSERT_IF(kind == Element, &obj->getDenseElement(slot) == (const Value *)this);
-    JS_ASSERT(obj->zone() == zone);
-
-    pre(zone);
-    JS_ASSERT(!IsPoisonedValue(v));
-    value = v;
-    post(zone->runtimeFromAnyThread(), obj, kind, slot, v);
-}
-
-inline void
-HeapSlot::writeBarrierPost(JSObject *obj, Kind kind, uint32_t slot, Value target)
-{
-#ifdef JSGC_GENERATIONAL
-    writeBarrierPost(obj->runtimeFromAnyThread(), obj, kind, slot, target);
-#endif
-}
-
-inline void
-HeapSlot::writeBarrierPost(JSRuntime *rt, JSObject *obj, Kind kind, uint32_t slot, Value target)
-{
-#ifdef JSGC_GENERATIONAL
-    JS_ASSERT_IF(kind == Slot, obj->getSlotAddressUnchecked(slot)->get() == target);
-    JS_ASSERT_IF(kind == Element,
-                 static_cast<HeapSlot *>(obj->getDenseElements() + slot)->get() == target);
-
-    if (target.isObject())
-        rt->gcStoreBuffer.putSlot(obj, kind, slot, &target.toObject());
-#endif
-}
-
-inline void
-HeapSlot::post(JSObject *owner, Kind kind, uint32_t slot, Value target)
-{
-    HeapSlot::writeBarrierPost(owner, kind, slot, target);
-}
-
-inline void
-HeapSlot::post(JSRuntime *rt, JSObject *owner, Kind kind, uint32_t slot, Value target)
-{
-    HeapSlot::writeBarrierPost(rt, owner, kind, slot, target);
-}
-
 #ifdef JSGC_GENERATIONAL
 class DenseRangeRef : public gc::BufferableRef
 {
     JSObject *owner;
     uint32_t start;
     uint32_t end;
 
   public:
@@ -444,17 +88,16 @@ inline void
 HashTableWriteBarrierPost(JSRuntime *rt, Map *map, const Key &key)
 {
 #ifdef JSGC_GENERATIONAL
     if (key && IsInsideNursery(rt, key))
         rt->gcStoreBuffer.putGeneric(gc::HashKeyRef<Map, Key>(map, key));
 #endif
 }
 
-
 inline
 EncapsulatedId::~EncapsulatedId()
 {
     pre();
 }
 
 inline EncapsulatedId &
 EncapsulatedId::operator=(const EncapsulatedId &v)
new file mode 100644
--- /dev/null
+++ b/js/src/gc/Barrier.cpp
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * 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/. */
+
+#include "gc/Barrier.h"
+
+#include "jsobj.h"
+
+#include "vm/ObjectImpl-inl.h"
+
+namespace js {
+
+#ifdef DEBUG
+
+bool
+HeapValue::preconditionForSet(Zone *zone)
+{
+    if (!value.isMarkable())
+        return true;
+
+    return ZoneOfValue(value) == zone ||
+           zone->runtimeFromAnyThread()->isAtomsZone(ZoneOfValue(value));
+}
+
+bool
+HeapSlot::preconditionForSet(JSObject *owner, Kind kind, uint32_t slot)
+{
+    return kind == Slot
+         ? &owner->getSlotRef(slot) == this
+         : &owner->getDenseElement(slot) == (const Value *)this;
+}
+
+bool
+HeapSlot::preconditionForSet(Zone *zone, JSObject *owner, Kind kind, uint32_t slot)
+{
+    bool ok = kind == Slot
+            ? &owner->getSlotRef(slot) == this
+            : &owner->getDenseElement(slot) == (const Value *)this;
+    return ok && owner->zone() == zone;
+}
+
+void
+HeapSlot::preconditionForWriteBarrierPost(JSObject *obj, Kind kind, uint32_t slot, Value target)
+{
+    JS_ASSERT_IF(kind == Slot, obj->getSlotAddressUnchecked(slot)->get() == target);
+    JS_ASSERT_IF(kind == Element,
+                 static_cast<HeapSlot *>(obj->getDenseElements() + slot)->get() == target);
+}
+
+#endif // DEBUG
+
+} // namespace js
--- a/js/src/gc/Barrier.h
+++ b/js/src/gc/Barrier.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef gc_Barrier_h
 #define gc_Barrier_h
 
 #include "NamespaceImports.h"
 
 #include "gc/Heap.h"
+#include "gc/StoreBuffer.h"
 #include "js/HashTable.h"
 #include "js/Id.h"
 #include "js/RootingAPI.h"
 
 /*
  * A write barrier is a mechanism used by incremental or generation GCs to
  * ensure that every value that needs to be marked is marked. In general, the
  * write barrier should be invoked whenever a write can cause the set of things
@@ -114,16 +115,34 @@
  * the init naming idiom in many places to signify that a field is being
  * assigned for the first time.
  */
 
 namespace js {
 
 class PropertyName;
 
+namespace gc {
+// Direct value access used by the write barriers and the jits.
+void
+MarkValueUnbarriered(JSTracer *trc, Value *v, const char *name);
+}
+
+JS::Zone *
+ZoneOfObject(const JSObject &obj);
+
+JS_ALWAYS_INLINE JS::Zone *
+ZoneOfValue(const JS::Value &value)
+{
+    JS_ASSERT(value.isMarkable());
+    if (value.isObject())
+        return ZoneOfObject(value.toObject());
+    return static_cast<js::gc::Cell *>(value.toGCThing())->tenuredZone();
+}
+
 template<class T, typename Unioned = uintptr_t>
 class EncapsulatedPtr
 {
   protected:
     union {
         T *value;
         Unioned other;
     };
@@ -387,106 +406,273 @@ class EncapsulatedValue : public ValueOp
 
   public:
     EncapsulatedValue(const Value &v) : value(v) {
         JS_ASSERT(!IsPoisonedValue(v));
     }
     EncapsulatedValue(const EncapsulatedValue &v) : value(v) {
         JS_ASSERT(!IsPoisonedValue(v));
     }
-    inline ~EncapsulatedValue();
+
+    ~EncapsulatedValue() {
+        pre();
+    }
 
     void init(const Value &v) {
         JS_ASSERT(!IsPoisonedValue(v));
         value = v;
     }
     void init(JSRuntime *rt, const Value &v) {
         JS_ASSERT(!IsPoisonedValue(v));
         value = v;
     }
 
-    inline EncapsulatedValue &operator=(const Value &v);
-    inline EncapsulatedValue &operator=(const EncapsulatedValue &v);
+    EncapsulatedValue &operator=(const Value &v) {
+        pre();
+        JS_ASSERT(!IsPoisonedValue(v));
+        value = v;
+        return *this;
+    }
+
+    EncapsulatedValue &operator=(const EncapsulatedValue &v) {
+        pre();
+        JS_ASSERT(!IsPoisonedValue(v));
+        value = v.get();
+        return *this;
+    }
 
     bool operator==(const EncapsulatedValue &v) const { return value == v.value; }
     bool operator!=(const EncapsulatedValue &v) const { return value != v.value; }
 
     const Value &get() const { return value; }
     Value *unsafeGet() { return &value; }
     operator const Value &() const { return value; }
 
     JSGCTraceKind gcKind() const { return value.gcKind(); }
 
     uint64_t asRawBits() const { return value.asRawBits(); }
 
-    static inline void writeBarrierPre(const Value &v);
-    static inline void writeBarrierPre(Zone *zone, const Value &v);
+    static void writeBarrierPre(const Value &v) {
+#ifdef JSGC_INCREMENTAL
+        if (v.isMarkable() && shadowRuntimeFromAnyThread(v)->needsBarrier())
+            writeBarrierPre(ZoneOfValue(v), v);
+#endif
+    }
+
+    static void writeBarrierPre(Zone *zone, const Value &v) {
+#ifdef JSGC_INCREMENTAL
+        JS::shadow::Zone *shadowZone = JS::shadow::Zone::asShadowZone(zone);
+        if (shadowZone->needsBarrier()) {
+            JS_ASSERT_IF(v.isMarkable(), shadowRuntimeFromMainThread(v)->needsBarrier());
+            Value tmp(v);
+            js::gc::MarkValueUnbarriered(shadowZone->barrierTracer(), &tmp, "write barrier");
+            JS_ASSERT(tmp == v);
+        }
+#endif
+    }
 
   protected:
-    inline void pre();
-    inline void pre(Zone *zone);
+    void pre() { writeBarrierPre(value); }
+    void pre(Zone *zone) { writeBarrierPre(zone, value); }
 
-    static inline JSRuntime *runtimeFromMainThread(const Value &v) {
+    static JSRuntime *runtimeFromMainThread(const Value &v) {
         JS_ASSERT(v.isMarkable());
         return static_cast<js::gc::Cell *>(v.toGCThing())->runtimeFromMainThread();
     }
-    static inline JSRuntime *runtimeFromAnyThread(const Value &v) {
+    static JSRuntime *runtimeFromAnyThread(const Value &v) {
         JS_ASSERT(v.isMarkable());
         return static_cast<js::gc::Cell *>(v.toGCThing())->runtimeFromAnyThread();
     }
+    static JS::shadow::Runtime *shadowRuntimeFromMainThread(const Value &v) {
+        return reinterpret_cast<JS::shadow::Runtime*>(runtimeFromMainThread(v));
+    }
+    static JS::shadow::Runtime *shadowRuntimeFromAnyThread(const Value &v) {
+        return reinterpret_cast<JS::shadow::Runtime*>(runtimeFromAnyThread(v));
+    }
 
   private:
     friend class ValueOperations<EncapsulatedValue>;
     const Value * extract() const { return &value; }
 };
 
 class HeapValue : public EncapsulatedValue
 {
   public:
-    explicit inline HeapValue();
-    explicit inline HeapValue(const Value &v);
-    explicit inline HeapValue(const HeapValue &v);
-    inline ~HeapValue();
+    explicit HeapValue()
+      : EncapsulatedValue(UndefinedValue())
+    {
+        post();
+    }
+
+    explicit HeapValue(const Value &v)
+      : EncapsulatedValue(v)
+    {
+        JS_ASSERT(!IsPoisonedValue(v));
+        post();
+    }
+
+    explicit HeapValue(const HeapValue &v)
+      : EncapsulatedValue(v.value)
+    {
+        JS_ASSERT(!IsPoisonedValue(v.value));
+        post();
+    }
+
+    ~HeapValue() {
+        pre();
+    }
 
-    inline void init(const Value &v);
-    inline void init(JSRuntime *rt, const Value &v);
+    void init(const Value &v) {
+        JS_ASSERT(!IsPoisonedValue(v));
+        value = v;
+        post();
+    }
+
+    void init(JSRuntime *rt, const Value &v) {
+        JS_ASSERT(!IsPoisonedValue(v));
+        value = v;
+        post(rt);
+    }
 
-    inline HeapValue &operator=(const Value &v);
-    inline HeapValue &operator=(const HeapValue &v);
+    HeapValue &operator=(const Value &v) {
+        pre();
+        JS_ASSERT(!IsPoisonedValue(v));
+        value = v;
+        post();
+        return *this;
+    }
+
+    HeapValue &operator=(const HeapValue &v) {
+        pre();
+        JS_ASSERT(!IsPoisonedValue(v.value));
+        value = v.value;
+        post();
+        return *this;
+    }
+
+#ifdef DEBUG
+    bool preconditionForSet(Zone *zone);
+#endif
 
     /*
      * This is a faster version of operator=. Normally, operator= has to
      * determine the compartment of the value before it can decide whether to do
      * the barrier. If you already know the compartment, it's faster to pass it
      * in.
      */
-    inline void set(Zone *zone, const Value &v);
+    void set(Zone *zone, const Value &v) {
+        JS::shadow::Zone *shadowZone = JS::shadow::Zone::asShadowZone(zone);
+        JS_ASSERT(preconditionForSet(zone));
+        pre(zone);
+        JS_ASSERT(!IsPoisonedValue(v));
+        value = v;
+        post(shadowZone->runtimeFromAnyThread());
+    }
 
-    static inline void writeBarrierPost(const Value &v, Value *addr);
-    static inline void writeBarrierPost(JSRuntime *rt, const Value &v, Value *addr);
+    static void writeBarrierPost(const Value &value, Value *addr) {
+#ifdef JSGC_GENERATIONAL
+        if (value.isMarkable())
+            shadowRuntimeFromMainThread(value)->gcStoreBufferPtr()->putValue(addr);
+#endif
+    }
+
+    static void writeBarrierPost(JSRuntime *rt, const Value &value, Value *addr) {
+#ifdef JSGC_GENERATIONAL
+        if (value.isMarkable()) {
+            JS::shadow::Runtime *shadowRuntime = JS::shadow::Runtime::asShadowRuntime(rt);
+            shadowRuntime->gcStoreBufferPtr()->putValue(addr);
+        }
+#endif
+    }
 
   private:
-    inline void post();
-    inline void post(JSRuntime *rt);
+    void post() {
+        writeBarrierPost(value, &value);
+    }
+
+    void post(JSRuntime *rt) {
+        writeBarrierPost(rt, value, &value);
+    }
 };
 
 class RelocatableValue : public EncapsulatedValue
 {
   public:
-    explicit inline RelocatableValue();
-    explicit inline RelocatableValue(const Value &v);
-    inline RelocatableValue(const RelocatableValue &v);
-    inline ~RelocatableValue();
+    explicit RelocatableValue()
+      : EncapsulatedValue(UndefinedValue())
+    {}
+
+    explicit RelocatableValue(const Value &v)
+      : EncapsulatedValue(v)
+    {
+        JS_ASSERT(!IsPoisonedValue(v));
+        if (v.isMarkable())
+            post();
+    }
+
+    RelocatableValue(const RelocatableValue &v)
+      : EncapsulatedValue(v.value)
+    {
+        JS_ASSERT(!IsPoisonedValue(v.value));
+        if (v.value.isMarkable())
+            post();
+    }
+
+    ~RelocatableValue()
+    {
+        if (value.isMarkable())
+            relocate(runtimeFromMainThread(value));
+    }
 
-    inline RelocatableValue &operator=(const Value &v);
-    inline RelocatableValue &operator=(const RelocatableValue &v);
+    RelocatableValue &operator=(const Value &v) {
+        pre();
+        JS_ASSERT(!IsPoisonedValue(v));
+        if (v.isMarkable()) {
+            value = v;
+            post();
+        } else if (value.isMarkable()) {
+            JSRuntime *rt = runtimeFromMainThread(value);
+            relocate(rt);
+            value = v;
+        } else {
+            value = v;
+        }
+        return *this;
+    }
+
+    RelocatableValue &operator=(const RelocatableValue &v) {
+        pre();
+        JS_ASSERT(!IsPoisonedValue(v.value));
+        if (v.value.isMarkable()) {
+            value = v.value;
+            post();
+        } else if (value.isMarkable()) {
+            JSRuntime *rt = runtimeFromMainThread(value);
+            relocate(rt);
+            value = v.value;
+        } else {
+            value = v.value;
+        }
+        return *this;
+    }
 
   private:
-    inline void post();
-    inline void relocate(JSRuntime *rt);
+    void post() {
+#ifdef JSGC_GENERATIONAL
+        JS_ASSERT(value.isMarkable());
+        shadowRuntimeFromMainThread(value)->gcStoreBufferPtr()->putRelocatableValue(&value);
+#endif
+    }
+
+    void relocate(JSRuntime *rt) {
+#ifdef JSGC_GENERATIONAL
+        JS::shadow::Runtime *shadowRuntime = JS::shadow::Runtime::asShadowRuntime(rt);
+        shadowRuntime->gcStoreBufferPtr()->removeRelocatableValue(&value);
+#endif
+    }
 };
 
 class HeapSlot : public EncapsulatedValue
 {
     /*
      * Operator= is not valid for HeapSlot because is must take the object and
      * slot offset to provide to the post/generational barrier.
      */
@@ -495,34 +681,100 @@ class HeapSlot : public EncapsulatedValu
     inline HeapSlot &operator=(const HeapSlot &v) MOZ_DELETE;
 
   public:
     enum Kind {
         Slot,
         Element
     };
 
-    explicit inline HeapSlot() MOZ_DELETE;
-    explicit inline HeapSlot(JSObject *obj, Kind kind, uint32_t slot, const Value &v);
-    explicit inline HeapSlot(JSObject *obj, Kind kind, uint32_t slot, const HeapSlot &v);
-    inline ~HeapSlot();
+    explicit HeapSlot() MOZ_DELETE;
+
+    explicit HeapSlot(JSObject *obj, Kind kind, uint32_t slot, const Value &v)
+      : EncapsulatedValue(v)
+    {
+        JS_ASSERT(!IsPoisonedValue(v));
+        post(obj, kind, slot, v);
+    }
+
+    explicit HeapSlot(JSObject *obj, Kind kind, uint32_t slot, const HeapSlot &s)
+      : EncapsulatedValue(s.value)
+    {
+        JS_ASSERT(!IsPoisonedValue(s.value));
+        post(obj, kind, slot, s);
+    }
+
+    ~HeapSlot() {
+        pre();
+    }
+
+    void init(JSObject *owner, Kind kind, uint32_t slot, const Value &v) {
+        value = v;
+        post(owner, kind, slot, v);
+    }
+
+    void init(JSRuntime *rt, JSObject *owner, Kind kind, uint32_t slot, const Value &v) {
+        value = v;
+        post(rt, owner, kind, slot, v);
+    }
+
+#ifdef DEBUG
+    bool preconditionForSet(JSObject *owner, Kind kind, uint32_t slot);
+    bool preconditionForSet(Zone *zone, JSObject *owner, Kind kind, uint32_t slot);
+    static void preconditionForWriteBarrierPost(JSObject *obj, Kind kind, uint32_t slot,
+                                                Value target);
+#endif
 
-    inline void init(JSObject *owner, Kind kind, uint32_t slot, const Value &v);
-    inline void init(JSRuntime *rt, JSObject *owner, Kind kind, uint32_t slot, const Value &v);
+    void set(JSObject *owner, Kind kind, uint32_t slot, const Value &v) {
+        JS_ASSERT(preconditionForSet(owner, kind, slot));
+        pre();
+        JS_ASSERT(!IsPoisonedValue(v));
+        value = v;
+        post(owner, kind, slot, v);
+    }
+
+    void set(Zone *zone, JSObject *owner, Kind kind, uint32_t slot, const Value &v) {
+        JS_ASSERT(preconditionForSet(zone, owner, kind, slot));
+        JS::shadow::Zone *shadowZone = JS::shadow::Zone::asShadowZone(zone);
+        pre(zone);
+        JS_ASSERT(!IsPoisonedValue(v));
+        value = v;
+        post(shadowZone->runtimeFromAnyThread(), owner, kind, slot, v);
+    }
 
-    inline void set(JSObject *owner, Kind kind, uint32_t slot, const Value &v);
-    inline void set(Zone *zone, JSObject *owner, Kind kind, uint32_t slot, const Value &v);
+    static void writeBarrierPost(JSObject *obj, Kind kind, uint32_t slot, Value target)
+    {
+#ifdef JSGC_GENERATIONAL
+        js::gc::Cell *cell = reinterpret_cast<js::gc::Cell*>(obj);
+        writeBarrierPost(cell->runtimeFromAnyThread(), obj, kind, slot, target);
+#endif
+    }
 
-    static inline void writeBarrierPost(JSObject *obj, Kind kind, uint32_t slot, Value target);
-    static inline void writeBarrierPost(JSRuntime *rt, JSObject *obj, Kind kind, uint32_t slot,
-                                        Value target);
+    static void writeBarrierPost(JSRuntime *rt, JSObject *obj, Kind kind, uint32_t slot,
+                                 Value target)
+    {
+#ifdef DEBUG
+        preconditionForWriteBarrierPost(obj, kind, slot, target);
+#endif
+#ifdef JSGC_GENERATIONAL
+        if (target.isObject()) {
+            JS::shadow::Runtime *shadowRuntime = JS::shadow::Runtime::asShadowRuntime(rt);
+            shadowRuntime->gcStoreBufferPtr()->putSlot(obj, kind, slot, &target.toObject());
+        }
+#endif
+    }
 
   private:
-    inline void post(JSObject *owner, Kind kind, uint32_t slot, Value target);
-    inline void post(JSRuntime *rt, JSObject *owner, Kind kind, uint32_t slot, Value target);
+    void post(JSObject *owner, Kind kind, uint32_t slot, Value target) {
+        HeapSlot::writeBarrierPost(owner, kind, slot, target);
+    }
+
+    void post(JSRuntime *rt, JSObject *owner, Kind kind, uint32_t slot, Value target) {
+        HeapSlot::writeBarrierPost(rt, owner, kind, slot, target);
+    }
 };
 
 /*
  * NOTE: This is a placeholder for bug 619558.
  *
  * Run a post write barrier that encompasses multiple contiguous slots in a
  * single step.
  */
--- a/js/src/gc/Heap.h
+++ b/js/src/gc/Heap.h
@@ -17,27 +17,26 @@
 #include "jstypes.h"
 #include "jsutil.h"
 
 #include "ds/BitArray.h"
 #include "js/HeapAPI.h"
 
 struct JSCompartment;
 
-extern "C" {
 struct JSRuntime;
+
+namespace JS {
+namespace shadow {
+class Runtime;
+}
 }
 
 namespace js {
 
-// Whether the current thread is permitted access to any part of the specified
-// runtime or zone.
-extern bool CurrentThreadCanAccessRuntime(JSRuntime *rt);
-extern bool CurrentThreadCanAccessZone(JS::Zone *zone);
-
 class FreeOp;
 
 namespace gc {
 
 struct Arena;
 struct ArenaHeader;
 struct Chunk;
 
@@ -101,16 +100,17 @@ struct Cell
 
     inline JSRuntime *runtimeFromMainThread() const;
     inline JS::Zone *tenuredZone() const;
     inline bool tenuredIsInsideZone(JS::Zone *zone) const;
 
     // Note: Unrestricted access to the runtime of a GC thing from an arbitrary
     // thread can easily lead to races. Use this method very carefully.
     inline JSRuntime *runtimeFromAnyThread() const;
+    inline JS::shadow::Runtime *shadowRuntimeFromAnyThread() const;
 
 #ifdef DEBUG
     inline bool isAligned() const;
     inline bool isTenured() const;
 #endif
 
   protected:
     inline uintptr_t address() const;
@@ -964,16 +964,22 @@ Cell::runtimeFromMainThread() const
 }
 
 inline JSRuntime *
 Cell::runtimeFromAnyThread() const
 {
     return chunk()->info.runtime;
 }
 
+inline JS::shadow::Runtime *
+Cell::shadowRuntimeFromAnyThread() const
+{
+    return reinterpret_cast<JS::shadow::Runtime*>(runtimeFromAnyThread());
+}
+
 AllocKind
 Cell::tenuredGetAllocKind() const
 {
     return arenaHeader()->getAllocKind();
 }
 
 bool
 Cell::isMarked(uint32_t color /* = BLACK */) const
--- a/js/src/gc/Marking.h
+++ b/js/src/gc/Marking.h
@@ -218,20 +218,16 @@ MarkCrossCompartmentSlot(JSTracer *trc, 
 
 /*
  * The unioned HeapPtr stored in script->globalObj needs special treatment to
  * typecheck correctly.
  */
 void
 MarkObject(JSTracer *trc, HeapPtr<GlobalObject, JSScript *> *thingp, const char *name);
 
-/* Direct value access used by the write barriers and the methodjit. */
-void
-MarkValueUnbarriered(JSTracer *trc, Value *v, const char *name);
-
 /*
  * MarkChildren<JSObject> is exposed solely for preWriteBarrier on
  * JSObject::TradeGuts. It should not be considered external interface.
  */
 void
 MarkChildren(JSTracer *trc, JSObject *obj);
 
 /*
--- a/js/src/gc/Nursery.h
+++ b/js/src/gc/Nursery.h
@@ -5,27 +5,37 @@
  * 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/. */
 
 #ifndef gc_Nursery_h
 #define gc_Nursery_h
 
 #ifdef JSGC_GENERATIONAL
 
-#include "jsgc.h"
+#include "jsalloc.h"
 #include "jspubtd.h"
 
 #include "ds/BitArray.h"
+#include "gc/Heap.h"
+#include "js/GCAPI.h"
 #include "js/HashTable.h"
+#include "js/HeapAPI.h"
+#include "js/Value.h"
+
+namespace JS {
+struct Zone;
+}
 
 namespace js {
 
 class ObjectElements;
+class HeapSlot;
 
 namespace gc {
+class Cell;
 class MinorCollectionTracer;
 } /* namespace gc */
 
 namespace jit {
 class CodeGenerator;
 class MacroAssembler;
 class ICStubCompiler;
 class BaselineCompiler;
@@ -123,17 +133,17 @@ class Nursery
      * The set of externally malloced slots potentially kept live by objects
      * stored in the nursery. Any external slots that do not belong to a
      * tenured thing at the end of a minor GC must be freed.
      */
     typedef HashSet<HeapSlot *, PointerHasher<HeapSlot *, 3>, SystemAllocPolicy> HugeSlotsSet;
     HugeSlotsSet hugeSlots;
 
     /* The marking bitmap for the fallback marker. */
-    const static size_t ThingAlignment = sizeof(Value);
+    const static size_t ThingAlignment = sizeof(JS::Value);
     const static size_t FallbackBitmapBits = NurserySize / ThingAlignment;
     BitArray<FallbackBitmapBits> fallbackBitmap;
 
 #ifdef DEBUG
     /*
      * In DEBUG builds, these bytes indicate the state of an unused segment of
      * nursery-allocated memory.
      */
@@ -198,17 +208,17 @@ class Nursery
     void *addressOfPosition() const { return (void*)&position_; }
 
     JSRuntime *runtime() const { return runtime_; }
 
     /* Allocates and registers external slots with the nursery. */
     HeapSlot *allocateHugeSlots(JSContext *cx, size_t nslots);
 
     /* Allocates a new GC thing from the tenured generation during minor GC. */
-    void *allocateFromTenured(Zone *zone, gc::AllocKind thingKind);
+    void *allocateFromTenured(JS::Zone *zone, gc::AllocKind thingKind);
 
     /*
      * Move the object at |src| in the Nursery to an already-allocated cell
      * |dst| in Tenured.
      */
     void collectToFixedPoint(gc::MinorCollectionTracer *trc);
     JS_ALWAYS_INLINE void traceObject(gc::MinorCollectionTracer *trc, JSObject *src);
     JS_ALWAYS_INLINE void markSlots(gc::MinorCollectionTracer *trc, HeapSlot *vp, uint32_t nslots);
--- a/js/src/gc/StoreBuffer.h
+++ b/js/src/gc/StoreBuffer.h
@@ -15,16 +15,17 @@
 
 #include "mozilla/DebugOnly.h"
 #include "mozilla/ReentrancyGuard.h"
 
 #include "jsalloc.h"
 
 #include "ds/LifoAlloc.h"
 #include "gc/Nursery.h"
+#include "js/Tracer.h"
 
 namespace js {
 namespace gc {
 
 class AccumulateEdgesTracer;
 
 /*
  * BufferableRef represents an abstract reference for use in the generational
@@ -246,50 +247,50 @@ class StoreBuffer
     };
 
     class ValueEdge
     {
         friend class StoreBuffer;
         friend class StoreBuffer::MonoTypeBuffer<ValueEdge>;
         friend class StoreBuffer::RelocatableMonoTypeBuffer<ValueEdge>;
 
-        Value *edge;
+        JS::Value *edge;
 
-        explicit ValueEdge(Value *v) : edge(v) {}
+        explicit ValueEdge(JS::Value *v) : edge(v) {}
         bool operator==(const ValueEdge &other) const { return edge == other.edge; }
         bool operator!=(const ValueEdge &other) const { return edge != other.edge; }
 
         void *deref() const { return edge->isGCThing() ? edge->toGCThing() : NULL; }
         void *location() const { return (void *)untagged().edge; }
 
         bool inRememberedSet(const Nursery &nursery) const {
             return !nursery.isInside(edge) && nursery.isInside(deref());
         }
 
         bool isNullEdge() const {
             return !deref();
         }
 
         void mark(JSTracer *trc);
 
-        ValueEdge tagged() const { return ValueEdge((Value *)(uintptr_t(edge) | 1)); }
-        ValueEdge untagged() const { return ValueEdge((Value *)(uintptr_t(edge) & ~1)); }
+        ValueEdge tagged() const { return ValueEdge((JS::Value *)(uintptr_t(edge) | 1)); }
+        ValueEdge untagged() const { return ValueEdge((JS::Value *)(uintptr_t(edge) & ~1)); }
         bool isTagged() const { return bool(uintptr_t(edge) & 1); }
     };
 
     struct SlotEdge
     {
         friend class StoreBuffer;
         friend class StoreBuffer::MonoTypeBuffer<SlotEdge>;
 
         JSObject *object;
         uint32_t offset;
-        HeapSlot::Kind kind;
+        int kind; // this is really just HeapSlot::Kind, but we can't see that type easily here
 
-        SlotEdge(JSObject *object, HeapSlot::Kind kind, uint32_t offset)
+        SlotEdge(JSObject *object, int kind, uint32_t offset)
           : object(object), offset(offset), kind(kind)
         {}
 
         bool operator==(const SlotEdge &other) const {
             return object == other.object && offset == other.offset && kind == other.kind;
         }
 
         bool operator!=(const SlotEdge &other) const {
@@ -378,53 +379,53 @@ class StoreBuffer
     bool isEnabled() { return enabled; }
 
     bool clear();
 
     /* Get the overflowed status. */
     bool isAboutToOverflow() const { return aboutToOverflow; }
 
     /* Insert a single edge into the buffer/remembered set. */
-    void putValue(Value *valuep) {
+    void putValue(JS::Value *valuep) {
         ValueEdge edge(valuep);
         if (!edge.inRememberedSet(nursery_))
             return;
         bufferVal.put(edge);
     }
     void putCell(Cell **cellp) {
         CellPtrEdge edge(cellp);
         if (!edge.inRememberedSet(nursery_))
             return;
         bufferCell.put(edge);
     }
-    void putSlot(JSObject *obj, HeapSlot::Kind kind, uint32_t slot, void *target) {
+    void putSlot(JSObject *obj, int kind, uint32_t slot, void *target) {
         SlotEdge edge(obj, kind, slot);
         /* This is manually inlined because slotLocation cannot be defined here. */
         if (nursery_.isInside(obj) || !nursery_.isInside(target))
             return;
         bufferSlot.put(edge);
     }
     void putWholeCell(Cell *cell) {
         bufferWholeCell.put(WholeCellEdges(cell));
     }
 
     /* Insert or update a single edge in the Relocatable buffer. */
-    void putRelocatableValue(Value *valuep) {
+    void putRelocatableValue(JS::Value *valuep) {
         ValueEdge edge(valuep);
         if (!edge.inRememberedSet(nursery_))
             return;
         bufferRelocVal.put(edge);
     }
     void putRelocatableCell(Cell **cellp) {
         CellPtrEdge edge(cellp);
         if (!edge.inRememberedSet(nursery_))
             return;
         bufferRelocCell.put(edge);
     }
-    void removeRelocatableValue(Value *valuep) {
+    void removeRelocatableValue(JS::Value *valuep) {
         ValueEdge edge(valuep);
         if (!edge.inRememberedSet(nursery_))
             return;
         bufferRelocVal.unput(edge);
     }
     void removeRelocatableCell(Cell **cellp) {
         CellPtrEdge edge(cellp);
         if (!edge.inRememberedSet(nursery_))
new file mode 100644
--- /dev/null
+++ b/js/src/gc/Tracer.cpp
@@ -0,0 +1,258 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * 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/. */
+
+#include "js/Tracer.h"
+
+#include "jsapi.h"
+#include "jsfun.h"
+#include "jsgc.h"
+#include "jsprf.h"
+#include "jsscript.h"
+#include "NamespaceImports.h"
+
+#include "gc/Marking.h"
+
+using namespace js;
+using namespace js::gc;
+
+JS_PUBLIC_API(void)
+JS_CallValueTracer(JSTracer *trc, Value *valuep, const char *name)
+{
+    MarkValueUnbarriered(trc, valuep, name);
+}
+
+JS_PUBLIC_API(void)
+JS_CallIdTracer(JSTracer *trc, jsid *idp, const char *name)
+{
+    MarkIdUnbarriered(trc, idp, name);
+}
+
+JS_PUBLIC_API(void)
+JS_CallObjectTracer(JSTracer *trc, JSObject **objp, const char *name)
+{
+    MarkObjectUnbarriered(trc, objp, name);
+}
+
+JS_PUBLIC_API(void)
+JS_CallStringTracer(JSTracer *trc, JSString **strp, const char *name)
+{
+    MarkStringUnbarriered(trc, strp, name);
+}
+
+JS_PUBLIC_API(void)
+JS_CallScriptTracer(JSTracer *trc, JSScript **scriptp, const char *name)
+{
+    MarkScriptUnbarriered(trc, scriptp, name);
+}
+
+JS_PUBLIC_API(void)
+JS_CallHeapValueTracer(JSTracer *trc, JS::Heap<JS::Value> *valuep, const char *name)
+{
+    MarkValueUnbarriered(trc, valuep->unsafeGet(), name);
+}
+
+JS_PUBLIC_API(void)
+JS_CallHeapIdTracer(JSTracer *trc, JS::Heap<jsid> *idp, const char *name)
+{
+    MarkIdUnbarriered(trc, idp->unsafeGet(), name);
+}
+
+JS_PUBLIC_API(void)
+JS_CallHeapObjectTracer(JSTracer *trc, JS::Heap<JSObject *> *objp, const char *name)
+{
+    MarkObjectUnbarriered(trc, objp->unsafeGet(), name);
+}
+
+JS_PUBLIC_API(void)
+JS_CallHeapStringTracer(JSTracer *trc, JS::Heap<JSString *> *strp, const char *name)
+{
+    MarkStringUnbarriered(trc, strp->unsafeGet(), name);
+}
+
+JS_PUBLIC_API(void)
+JS_CallHeapScriptTracer(JSTracer *trc, JS::Heap<JSScript *> *scriptp, const char *name)
+{
+    MarkScriptUnbarriered(trc, scriptp->unsafeGet(), name);
+}
+
+JS_PUBLIC_API(void)
+JS_CallTenuredObjectTracer(JSTracer *trc, JS::TenuredHeap<JSObject *> *objp, const char *name)
+{
+    JSObject *obj = objp->getPtr();
+    if (!obj)
+        return;
+
+    JS_SET_TRACING_LOCATION(trc, (void*)objp);
+    MarkObjectUnbarriered(trc, &obj, name);
+
+    objp->setPtr(obj);
+}
+
+JS_PUBLIC_API(void)
+JS_TracerInit(JSTracer *trc, JSRuntime *rt, JSTraceCallback callback)
+{
+    InitTracer(trc, rt, callback);
+}
+
+JS_PUBLIC_API(void)
+JS_TraceChildren(JSTracer *trc, void *thing, JSGCTraceKind kind)
+{
+    js::TraceChildren(trc, thing, kind);
+}
+
+JS_PUBLIC_API(void)
+JS_TraceRuntime(JSTracer *trc)
+{
+    AssertHeapIsIdle(trc->runtime);
+    TraceRuntime(trc);
+}
+
+static size_t
+CountDecimalDigits(size_t num)
+{
+    size_t numDigits = 0;
+    do {
+        num /= 10;
+        numDigits++;
+    } while (num > 0);
+
+    return numDigits;
+}
+
+JS_PUBLIC_API(void)
+JS_GetTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc, void *thing,
+                     JSGCTraceKind kind, bool details)
+{
+    const char *name = NULL; /* silence uninitialized warning */
+    size_t n;
+
+    if (bufsize == 0)
+        return;
+
+    switch (kind) {
+      case JSTRACE_OBJECT:
+      {
+        name = static_cast<JSObject *>(thing)->getClass()->name;
+        break;
+      }
+
+      case JSTRACE_STRING:
+        name = ((JSString *)thing)->isDependent()
+               ? "substring"
+               : "string";
+        break;
+
+      case JSTRACE_SCRIPT:
+        name = "script";
+        break;
+
+      case JSTRACE_LAZY_SCRIPT:
+        name = "lazyscript";
+        break;
+
+      case JSTRACE_IONCODE:
+        name = "ioncode";
+        break;
+
+      case JSTRACE_SHAPE:
+        name = "shape";
+        break;
+
+      case JSTRACE_BASE_SHAPE:
+        name = "base_shape";
+        break;
+
+      case JSTRACE_TYPE_OBJECT:
+        name = "type_object";
+        break;
+    }
+
+    n = strlen(name);
+    if (n > bufsize - 1)
+        n = bufsize - 1;
+    js_memcpy(buf, name, n + 1);
+    buf += n;
+    bufsize -= n;
+    *buf = '\0';
+
+    if (details && bufsize > 2) {
+        switch (kind) {
+          case JSTRACE_OBJECT:
+          {
+            JSObject *obj = (JSObject *)thing;
+            if (obj->is<JSFunction>()) {
+                JSFunction *fun = &obj->as<JSFunction>();
+                if (fun->displayAtom()) {
+                    *buf++ = ' ';
+                    bufsize--;
+                    PutEscapedString(buf, bufsize, fun->displayAtom(), 0);
+                }
+            } else if (obj->getClass()->flags & JSCLASS_HAS_PRIVATE) {
+                JS_snprintf(buf, bufsize, " %p", obj->getPrivate());
+            } else {
+                JS_snprintf(buf, bufsize, " <no private>");
+            }
+            break;
+          }
+
+          case JSTRACE_STRING:
+          {
+            *buf++ = ' ';
+            bufsize--;
+            JSString *str = (JSString *)thing;
+
+            if (str->isLinear()) {
+                bool willFit = str->length() + strlen("<length > ") +
+                               CountDecimalDigits(str->length()) < bufsize;
+
+                n = JS_snprintf(buf, bufsize, "<length %d%s> ",
+                                (int)str->length(),
+                                willFit ? "" : " (truncated)");
+                buf += n;
+                bufsize -= n;
+
+                PutEscapedString(buf, bufsize, &str->asLinear(), 0);
+            }
+            else
+                JS_snprintf(buf, bufsize, "<rope: length %d>", (int)str->length());
+            break;
+          }
+
+          case JSTRACE_SCRIPT:
+          {
+            JSScript *script = static_cast<JSScript *>(thing);
+            JS_snprintf(buf, bufsize, " %s:%u", script->filename(), unsigned(script->lineno));
+            break;
+          }
+
+          case JSTRACE_LAZY_SCRIPT:
+          case JSTRACE_IONCODE:
+          case JSTRACE_SHAPE:
+          case JSTRACE_BASE_SHAPE:
+          case JSTRACE_TYPE_OBJECT:
+            break;
+        }
+    }
+    buf[bufsize - 1] = '\0';
+}
+
+extern JS_PUBLIC_API(const char *)
+JS_GetTraceEdgeName(JSTracer *trc, char *buffer, int bufferSize)
+{
+    if (trc->debugPrinter) {
+        trc->debugPrinter(trc, buffer, bufferSize);
+        return buffer;
+    }
+    if (trc->debugPrintIndex != (size_t) - 1) {
+        JS_snprintf(buffer, bufferSize, "%s[%lu]",
+                    (const char *)trc->debugPrintArg,
+                    trc->debugPrintIndex);
+        return buffer;
+    }
+    return (const char*)trc->debugPrintArg;
+}
+
+
--- a/js/src/gc/Zone.cpp
+++ b/js/src/gc/Zone.cpp
@@ -19,17 +19,17 @@
 #include "jsgcinlines.h"
 
 #include "vm/ObjectImpl-inl.h"
 
 using namespace js;
 using namespace js::gc;
 
 JS::Zone::Zone(JSRuntime *rt)
-  : runtime_(rt),
+  : JS::shadow::Zone(rt, &rt->gcMarker),
     allocator(this),
     hold(false),
     ionUsingBarriers_(false),
     active(false),
     gcScheduled(false),
     gcState(NoGC),
     gcPreserveCode(false),
     gcBytes(0),
@@ -229,8 +229,16 @@ Zone::discardJitCode(FreeOp *fop, bool d
             if (comp->ionCompartment())
                 comp->ionCompartment()->optimizedStubSpace()->free();
 
             comp->types.sweepCompilerOutputs(fop, discardConstraints);
         }
     }
 #endif
 }
+
+JS::Zone *
+js::ZoneOfObject(const JSObject &obj)
+{
+    return obj.zone();
+}
+
+
--- a/js/src/gc/Zone.h
+++ b/js/src/gc/Zone.h
@@ -87,53 +87,36 @@ namespace JS {
  * shapes within it are alive.
  *
  * We always guarantee that a zone has at least one live compartment by refusing
  * to delete the last compartment in a live zone. (This could happen, for
  * example, if the conservative scanner marks a string in an otherwise dead
  * zone.)
  */
 
-struct Zone : private JS::shadow::Zone,
+struct Zone : public JS::shadow::Zone,
               public js::gc::GraphNodeBase<JS::Zone>,
               public js::MallocProvider<JS::Zone>
 {
   private:
-    JSRuntime                    *runtime_;
-
     friend bool js::CurrentThreadCanAccessZone(Zone *zone);
 
   public:
     js::Allocator                allocator;
 
     js::CompartmentVector        compartments;
 
     bool                         hold;
 
   private:
     bool                         ionUsingBarriers_;
 
   public:
     bool                         active;  // GC flag, whether there are active frames
 
-    JSRuntime *runtimeFromMainThread() const {
-        JS_ASSERT(CurrentThreadCanAccessRuntime(runtime_));
-        return runtime_;
-    }
-
-    // Note: Unrestricted access to the zone's runtime from an arbitrary
-    // thread can easily lead to races. Use this method very carefully.
-    JSRuntime *runtimeFromAnyThread() const {
-        return runtime_;
-    }
-
-    bool needsBarrier() const {
-        return needsBarrier_;
-    }
-
     bool compileBarriers(bool needsBarrier) const {
         return needsBarrier || runtimeFromMainThread()->gcZeal() == js::gc::ZealVerifierPreValue;
     }
 
     bool compileBarriers() const {
         return compileBarriers(needsBarrier());
     }
 
@@ -143,21 +126,16 @@ struct Zone : private JS::shadow::Zone,
     };
 
     void setNeedsBarrier(bool needs, ShouldUpdateIon updateIon);
 
     static size_t OffsetOfNeedsBarrier() {
         return offsetof(Zone, needsBarrier_);
     }
 
-    js::GCMarker *barrierTracer() {
-        JS_ASSERT(needsBarrier_);
-        return &runtimeFromMainThread()->gcMarker;
-    }
-
   public:
     enum CompartmentGCState {
         NoGC,
         Mark,
         MarkGray,
         Sweep,
         Finished
     };
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1896,253 +1896,16 @@ JS_RemoveExtraGCRootsTracer(JSRuntime *r
         JSRuntime::ExtraTracer *e = &rt->gcBlackRootTracers[i];
         if (e->op == traceOp && e->data == data) {
             rt->gcBlackRootTracers.erase(e);
             break;
         }
     }
 }
 
-JS_PUBLIC_API(void)
-JS_CallValueTracer(JSTracer *trc, Value *valuep, const char *name)
-{
-    MarkValueUnbarriered(trc, valuep, name);
-}
-
-JS_PUBLIC_API(void)
-JS_CallIdTracer(JSTracer *trc, jsid *idp, const char *name)
-{
-    MarkIdUnbarriered(trc, idp, name);
-}
-
-JS_PUBLIC_API(void)
-JS_CallObjectTracer(JSTracer *trc, JSObject **objp, const char *name)
-{
-    MarkObjectUnbarriered(trc, objp, name);
-}
-
-JS_PUBLIC_API(void)
-JS_CallTenuredObjectTracer(JSTracer *trc, JS::TenuredHeap<JSObject *> *objp, const char *name)
-{
-    JSObject *obj = objp->getPtr();
-    if (!obj)
-        return;
-
-    JS_SET_TRACING_LOCATION(trc, (void*)objp);
-    MarkObjectUnbarriered(trc, &obj, name);
-
-    objp->setPtr(obj);
-}
-
-JS_PUBLIC_API(void)
-JS_CallStringTracer(JSTracer *trc, JSString **strp, const char *name)
-{
-    MarkStringUnbarriered(trc, strp, name);
-}
-
-JS_PUBLIC_API(void)
-JS_CallScriptTracer(JSTracer *trc, JSScript **scriptp, const char *name)
-{
-    MarkScriptUnbarriered(trc, scriptp, name);
-}
-
-JS_PUBLIC_API(void)
-JS_CallHeapValueTracer(JSTracer *trc, JS::Heap<JS::Value> *valuep, const char *name)
-{
-    MarkValueUnbarriered(trc, valuep->unsafeGet(), name);
-}
-
-JS_PUBLIC_API(void)
-JS_CallHeapIdTracer(JSTracer *trc, JS::Heap<jsid> *idp, const char *name)
-{
-    MarkIdUnbarriered(trc, idp->unsafeGet(), name);
-}
-
-JS_PUBLIC_API(void)
-JS_CallHeapObjectTracer(JSTracer *trc, JS::Heap<JSObject *> *objp, const char *name)
-{
-    MarkObjectUnbarriered(trc, objp->unsafeGet(), name);
-}
-
-JS_PUBLIC_API(void)
-JS_CallHeapStringTracer(JSTracer *trc, JS::Heap<JSString *> *strp, const char *name)
-{
-    MarkStringUnbarriered(trc, strp->unsafeGet(), name);
-}
-
-JS_PUBLIC_API(void)
-JS_CallHeapScriptTracer(JSTracer *trc, JS::Heap<JSScript *> *scriptp, const char *name)
-{
-    MarkScriptUnbarriered(trc, scriptp->unsafeGet(), name);
-}
-
-JS_PUBLIC_API(void)
-JS_TracerInit(JSTracer *trc, JSRuntime *rt, JSTraceCallback callback)
-{
-    InitTracer(trc, rt, callback);
-}
-
-JS_PUBLIC_API(void)
-JS_TraceRuntime(JSTracer *trc)
-{
-    AssertHeapIsIdle(trc->runtime);
-    TraceRuntime(trc);
-}
-
-JS_PUBLIC_API(void)
-JS_TraceChildren(JSTracer *trc, void *thing, JSGCTraceKind kind)
-{
-    js::TraceChildren(trc, thing, kind);
-}
-
-static size_t
-CountDecimalDigits(size_t num)
-{
-    size_t numDigits = 0;
-    do {
-        num /= 10;
-        numDigits++;
-    } while (num > 0);
-
-    return numDigits;
-}
-
-JS_PUBLIC_API(void)
-JS_GetTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc, void *thing,
-                     JSGCTraceKind kind, bool details)
-{
-    const char *name = NULL; /* silence uninitialized warning */
-    size_t n;
-
-    if (bufsize == 0)
-        return;
-
-    switch (kind) {
-      case JSTRACE_OBJECT:
-      {
-        name = static_cast<JSObject *>(thing)->getClass()->name;
-        break;
-      }
-
-      case JSTRACE_STRING:
-        name = ((JSString *)thing)->isDependent()
-               ? "substring"
-               : "string";
-        break;
-
-      case JSTRACE_SCRIPT:
-        name = "script";
-        break;
-
-      case JSTRACE_LAZY_SCRIPT:
-        name = "lazyscript";
-        break;
-
-      case JSTRACE_IONCODE:
-        name = "ioncode";
-        break;
-
-      case JSTRACE_SHAPE:
-        name = "shape";
-        break;
-
-      case JSTRACE_BASE_SHAPE:
-        name = "base_shape";
-        break;
-
-      case JSTRACE_TYPE_OBJECT:
-        name = "type_object";
-        break;
-    }
-
-    n = strlen(name);
-    if (n > bufsize - 1)
-        n = bufsize - 1;
-    js_memcpy(buf, name, n + 1);
-    buf += n;
-    bufsize -= n;
-    *buf = '\0';
-
-    if (details && bufsize > 2) {
-        switch (kind) {
-          case JSTRACE_OBJECT:
-          {
-            JSObject *obj = (JSObject *)thing;
-            if (obj->is<JSFunction>()) {
-                JSFunction *fun = &obj->as<JSFunction>();
-                if (fun->displayAtom()) {
-                    *buf++ = ' ';
-                    bufsize--;
-                    PutEscapedString(buf, bufsize, fun->displayAtom(), 0);
-                }
-            } else if (obj->getClass()->flags & JSCLASS_HAS_PRIVATE) {
-                JS_snprintf(buf, bufsize, " %p", obj->getPrivate());
-            } else {
-                JS_snprintf(buf, bufsize, " <no private>");
-            }
-            break;
-          }
-
-          case JSTRACE_STRING:
-          {
-            *buf++ = ' ';
-            bufsize--;
-            JSString *str = (JSString *)thing;
-
-            if (str->isLinear()) {
-                bool willFit = str->length() + strlen("<length > ") +
-                               CountDecimalDigits(str->length()) < bufsize;
-
-                n = JS_snprintf(buf, bufsize, "<length %d%s> ",
-                                (int)str->length(),
-                                willFit ? "" : " (truncated)");
-                buf += n;
-                bufsize -= n;
-
-                PutEscapedString(buf, bufsize, &str->asLinear(), 0);
-            }
-            else
-                JS_snprintf(buf, bufsize, "<rope: length %d>", (int)str->length());
-            break;
-          }
-
-          case JSTRACE_SCRIPT:
-          {
-            JSScript *script = static_cast<JSScript *>(thing);
-            JS_snprintf(buf, bufsize, " %s:%u", script->filename(), unsigned(script->lineno));
-            break;
-          }
-
-          case JSTRACE_LAZY_SCRIPT:
-          case JSTRACE_IONCODE:
-          case JSTRACE_SHAPE:
-          case JSTRACE_BASE_SHAPE:
-          case JSTRACE_TYPE_OBJECT:
-            break;
-        }
-    }
-    buf[bufsize - 1] = '\0';
-}
-
-extern JS_PUBLIC_API(const char *)
-JS_GetTraceEdgeName(JSTracer *trc, char *buffer, int bufferSize)
-{
-    if (trc->debugPrinter) {
-        trc->debugPrinter(trc, buffer, bufferSize);
-        return buffer;
-    }
-    if (trc->debugPrintIndex != (size_t) - 1) {
-        JS_snprintf(buffer, bufferSize, "%s[%lu]",
-                    (const char *)trc->debugPrintArg,
-                    trc->debugPrintIndex);
-        return buffer;
-    }
-    return (const char*)trc->debugPrintArg;
-}
-
 #ifdef DEBUG
 
 typedef struct JSHeapDumpNode JSHeapDumpNode;
 
 struct JSHeapDumpNode {
     void            *thing;
     JSGCTraceKind   kind;
     JSHeapDumpNode  *next;          /* next sibling */
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -27,16 +27,18 @@
 #include "js/Id.h"
 #include "js/RootingAPI.h"
 #include "js/Utility.h"
 #include "js/Value.h"
 #include "js/Vector.h"
 
 /************************************************************************/
 
+struct JSTracer;
+
 namespace JS {
 
 class Latin1CharsZ;
 class TwoByteChars;
 
 typedef mozilla::RangedPtr<const jschar> CharPtr;
 
 class StableCharPtr : public CharPtr {
@@ -1985,197 +1987,16 @@ JSVAL_TO_TRACEABLE(jsval v)
 
 static JS_ALWAYS_INLINE JSGCTraceKind
 JSVAL_TRACE_KIND(jsval v)
 {
     JS_ASSERT(JSVAL_IS_GCTHING(v));
     return (JSGCTraceKind) JSVAL_TRACE_KIND_IMPL(JSVAL_TO_IMPL(v));
 }
 
-/*
- * Tracer callback, called for each traceable thing directly referenced by a
- * particular object or runtime structure. It is the callback responsibility
- * to ensure the traversal of the full object graph via calling eventually
- * JS_TraceChildren on the passed thing. In this case the callback must be
- * prepared to deal with cycles in the traversal graph.
- *
- * kind argument is one of JSTRACE_OBJECT, JSTRACE_STRING or a tag denoting
- * internal implementation-specific traversal kind. In the latter case the only
- * operations on thing that the callback can do is to call JS_TraceChildren or
- * JS_GetTraceThingInfo.
- *
- * If eagerlyTraceWeakMaps is true, when we trace a WeakMap visit all
- * of its mappings.  This should be used in cases where the tracer
- * wants to use the existing liveness of entries.
- */
-typedef void
-(* JSTraceCallback)(JSTracer *trc, void **thingp, JSGCTraceKind kind);
-
-enum WeakMapTraceKind {
-    DoNotTraceWeakMaps = 0,
-    TraceWeakMapValues = 1,
-    TraceWeakMapKeysValues = 2
-};
-
-struct JSTracer {
-    JSRuntime           *runtime;
-    JSTraceCallback     callback;
-    JSTraceNamePrinter  debugPrinter;
-    const void          *debugPrintArg;
-    size_t              debugPrintIndex;
-    WeakMapTraceKind    eagerlyTraceWeakMaps;
-#ifdef JS_GC_ZEAL
-    void                *realLocation;
-#endif
-};
-
-/*
- * Set debugging information about a reference to a traceable thing to prepare
- * for the following call to JS_CallTracer.
- *
- * When printer is null, arg must be const char * or char * C string naming
- * the reference and index must be either (size_t)-1 indicating that the name
- * alone describes the reference or it must be an index into some array vector
- * that stores the reference.
- *
- * When printer callback is not null, the arg and index arguments are
- * available to the callback as debugPrintArg and debugPrintIndex fields
- * of JSTracer.
- *
- * The storage for name or callback's arguments needs to live only until
- * the following call to JS_CallTracer returns.
- */
-# define JS_SET_TRACING_DETAILS(trc, printer, arg, index)                     \
-    JS_BEGIN_MACRO                                                            \
-        (trc)->debugPrinter = (printer);                                      \
-        (trc)->debugPrintArg = (arg);                                         \
-        (trc)->debugPrintIndex = (index);                                     \
-    JS_END_MACRO
-
-/*
- * Sets the real location for a marked reference, when passing the address
- * directly is not feasable.
- *
- * FIXME: This is currently overcomplicated by our need to nest calls for Values
- * stored as keys in hash tables, but will get simplified once we can rekey
- * in-place.
- */
-#ifdef JS_GC_ZEAL
-# define JS_SET_TRACING_LOCATION(trc, location)                               \
-    JS_BEGIN_MACRO                                                            \
-        if (!(trc)->realLocation || !(location))                              \
-            (trc)->realLocation = (location);                                 \
-    JS_END_MACRO
-# define JS_UNSET_TRACING_LOCATION(trc)                                       \
-    JS_BEGIN_MACRO                                                            \
-        (trc)->realLocation = NULL;                                           \
-    JS_END_MACRO
-#else
-# define JS_SET_TRACING_LOCATION(trc, location)                               \
-    JS_BEGIN_MACRO                                                            \
-    JS_END_MACRO
-# define JS_UNSET_TRACING_LOCATION(trc)                                       \
-    JS_BEGIN_MACRO                                                            \
-    JS_END_MACRO
-#endif
-
-
-/*
- * Convenience macro to describe the argument of JS_CallTracer using C string
- * and index.
- */
-# define JS_SET_TRACING_INDEX(trc, name, index)                               \
-    JS_SET_TRACING_DETAILS(trc, NULL, name, index)
-
-/*
- * Convenience macro to describe the argument of JS_CallTracer using C string.
- */
-# define JS_SET_TRACING_NAME(trc, name)                                       \
-    JS_SET_TRACING_DETAILS(trc, NULL, name, (size_t)-1)
-
-/*
- * The JS_Call*Tracer family of functions traces the given GC thing reference.
- * This performs the tracing action configured on the given JSTracer:
- * typically calling the JSTracer::callback or marking the thing as live.
- *
- * The argument to JS_Call*Tracer is an in-out param: when the function
- * returns, the garbage collector might have moved the GC thing. In this case,
- * the reference passed to JS_Call*Tracer will be updated to the object's new
- * location. Callers of this method are responsible for updating any state
- * that is dependent on the object's address. For example, if the object's
- * address is used as a key in a hashtable, then the object must be removed
- * and re-inserted with the correct hash.
- */
-extern JS_PUBLIC_API(void)
-JS_CallValueTracer(JSTracer *trc, JS::Value *valuep, const char *name);
-
-extern JS_PUBLIC_API(void)
-JS_CallIdTracer(JSTracer *trc, jsid *idp, const char *name);
-
-extern JS_PUBLIC_API(void)
-JS_CallObjectTracer(JSTracer *trc, JSObject **objp, const char *name);
-
-extern JS_PUBLIC_API(void)
-JS_CallStringTracer(JSTracer *trc, JSString **strp, const char *name);
-
-extern JS_PUBLIC_API(void)
-JS_CallScriptTracer(JSTracer *trc, JSScript **scriptp, const char *name);
-
-extern JS_PUBLIC_API(void)
-JS_CallHeapValueTracer(JSTracer *trc, JS::Heap<JS::Value> *valuep, const char *name);
-
-extern JS_PUBLIC_API(void)
-JS_CallHeapIdTracer(JSTracer *trc, JS::Heap<jsid> *idp, const char *name);
-
-extern JS_PUBLIC_API(void)
-JS_CallHeapObjectTracer(JSTracer *trc, JS::Heap<JSObject *> *objp, const char *name);
-
-extern JS_PUBLIC_API(void)
-JS_CallHeapStringTracer(JSTracer *trc, JS::Heap<JSString *> *strp, const char *name);
-
-extern JS_PUBLIC_API(void)
-JS_CallHeapScriptTracer(JSTracer *trc, JS::Heap<JSScript *> *scriptp, const char *name);
-
-template <typename HashSetEnum>
-inline void
-JS_CallHashSetObjectTracer(JSTracer *trc, HashSetEnum &e, JSObject *const &key, const char *name)
-{
-    JSObject *updated = key;
-    JS_SET_TRACING_LOCATION(trc, reinterpret_cast<void *>(&const_cast<JSObject *&>(key)));
-    JS_CallObjectTracer(trc, &updated, name);
-    if (updated != key)
-        e.rekeyFront(key, updated);
-}
-
-/*
- * Trace an object that is known to always be tenured.  No post barriers are
- * required in this case.
- */
-extern JS_PUBLIC_API(void)
-JS_CallTenuredObjectTracer(JSTracer *trc, JS::TenuredHeap<JSObject *> *objp, const char *name);
-
-/*
- * API for JSTraceCallback implementations.
- */
-extern JS_PUBLIC_API(void)
-JS_TracerInit(JSTracer *trc, JSRuntime *rt, JSTraceCallback callback);
-
-extern JS_PUBLIC_API(void)
-JS_TraceChildren(JSTracer *trc, void *thing, JSGCTraceKind kind);
-
-extern JS_PUBLIC_API(void)
-JS_TraceRuntime(JSTracer *trc);
-
-extern JS_PUBLIC_API(void)
-JS_GetTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc,
-                     void *thing, JSGCTraceKind kind, bool includeDetails);
-
-extern JS_PUBLIC_API(const char *)
-JS_GetTraceEdgeName(JSTracer *trc, char *buffer, int bufferSize);
-
 #ifdef DEBUG
 
 /*
  * DEBUG-only method to dump the object graph of heap-allocated things.
  *
  * fp:              file for the dump output.
  * start:           when non-null, dump only things reachable from start
  *                  thing. Otherwise dump all things reachable from the
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -199,34 +199,48 @@ class JSFunction : public JSObject
 
     // Can be called multiple times by the parser.
     void setIsExprClosure() {
         flags |= EXPR_CLOSURE;
     }
 
     JSAtom *atom() const { return hasGuessedAtom() ? NULL : atom_.get(); }
     js::PropertyName *name() const { return hasGuessedAtom() || !atom_ ? NULL : atom_->asPropertyName(); }
-    inline void initAtom(JSAtom *atom);
+    void initAtom(JSAtom *atom) { atom_.init(atom); }
     JSAtom *displayAtom() const { return atom_; }
 
-    inline void setGuessedAtom(JSAtom *atom);
+    void setGuessedAtom(JSAtom *atom) {
+        JS_ASSERT(atom_ == NULL);
+        JS_ASSERT(atom != NULL);
+        JS_ASSERT(!hasGuessedAtom());
+        atom_ = atom;
+        flags |= HAS_GUESSED_ATOM;
+    }
 
     /* uint16_t representation bounds number of call object dynamic slots. */
     enum { MAX_ARGS_AND_VARS = 2 * ((1U << 16) - 1) };
 
     /*
      * For an interpreted function, accessors for the initial scope object of
      * activations (stack frames) of the function.
      */
     JSObject *environment() const {
         JS_ASSERT(isInterpreted());
         return u.i.env_;
     }
-    inline void setEnvironment(JSObject *obj);
-    inline void initEnvironment(JSObject *obj);
+
+    void setEnvironment(JSObject *obj) {
+        JS_ASSERT(isInterpreted());
+        *(js::HeapPtrObject *)&u.i.env_ = obj;
+    }
+
+    void initEnvironment(JSObject *obj) {
+        JS_ASSERT(isInterpreted());
+        ((js::HeapPtrObject *)&u.i.env_)->init(obj);
+    }
 
     static inline size_t offsetOfEnvironment() { return offsetof(JSFunction, u.i.env_); }
     static inline size_t offsetOfAtom() { return offsetof(JSFunction, atom_); }
 
     static bool createScriptForLazilyInterpretedFunction(JSContext *cx, js::HandleFunction fun);
 
     // Function Scripts
     //
@@ -299,16 +313,17 @@ class JSFunction : public JSObject
     bool isGenerator() const { return generatorKind() != js::NotGenerator; }
 
     bool isLegacyGenerator() const { return generatorKind() == js::LegacyGenerator; }
 
     bool isStarGenerator() const { return generatorKind() == js::StarGenerator; }
 
     inline void setScript(JSScript *script_);
     inline void initScript(JSScript *script_);
+
     void initLazyScript(js::LazyScript *lazy) {
         JS_ASSERT(isInterpreted());
         flags &= ~INTERPRETED;
         flags |= INTERPRETED_LAZY;
         u.i.s.lazy_ = lazy;
     }
 
     JSNative native() const {
@@ -361,17 +376,23 @@ class JSFunction : public JSObject
 
     inline void trace(JSTracer *trc);
 
     /* Bound function accessors. */
 
     inline bool initBoundFunction(JSContext *cx, js::HandleValue thisArg,
                                   const js::Value *args, unsigned argslen);
 
-    inline JSObject *getBoundFunctionTarget() const;
+    JSObject *getBoundFunctionTarget() const {
+        JS_ASSERT(isBoundFunction());
+
+        /* Bound functions abuse |parent| to store their target function. */
+        return getParent();
+    }
+
     inline const js::Value &getBoundFunctionThis() const;
     inline const js::Value &getBoundFunctionArgument(unsigned which) const;
     inline size_t getBoundFunctionArgumentCount() const;
 
   private:
     inline js::FunctionExtended *toExtended();
     inline const js::FunctionExtended *toExtended() const;
 
@@ -484,16 +505,40 @@ JSFunction::toExtended()
 
 inline const js::FunctionExtended *
 JSFunction::toExtended() const
 {
     JS_ASSERT(isExtended());
     return static_cast<const js::FunctionExtended *>(this);
 }
 
+inline void
+JSFunction::initializeExtended()
+{
+    JS_ASSERT(isExtended());
+
+    JS_ASSERT(mozilla::ArrayLength(toExtended()->extendedSlots) == 2);
+    toExtended()->extendedSlots[0].init(js::UndefinedValue());
+    toExtended()->extendedSlots[1].init(js::UndefinedValue());
+}
+
+inline void
+JSFunction::initExtendedSlot(size_t which, const js::Value &val)
+{
+    JS_ASSERT(which < mozilla::ArrayLength(toExtended()->extendedSlots));
+    toExtended()->extendedSlots[which].init(val);
+}
+
+inline void
+JSFunction::setExtendedSlot(size_t which, const js::Value &val)
+{
+    JS_ASSERT(which < mozilla::ArrayLength(toExtended()->extendedSlots));
+    toExtended()->extendedSlots[which] = val;
+}
+
 inline const js::Value &
 JSFunction::getExtendedSlot(size_t which) const
 {
     JS_ASSERT(which < mozilla::ArrayLength(toExtended()->extendedSlots));
     return toExtended()->extendedSlots[which];
 }
 
 namespace js {
--- a/js/src/jsfuninlines.h
+++ b/js/src/jsfuninlines.h
@@ -6,70 +6,16 @@
 
 #ifndef jsfuninlines_h
 #define jsfuninlines_h
 
 #include "jsfun.h"
 
 #include "vm/ScopeObject.h"
 
-inline void
-JSFunction::initAtom(JSAtom *atom)
-{
-    atom_.init(atom);
-}
-
-inline void
-JSFunction::setGuessedAtom(JSAtom *atom)
-{
-    JS_ASSERT(atom_ == NULL);
-    JS_ASSERT(atom != NULL);
-    JS_ASSERT(!hasGuessedAtom());
-    atom_ = atom;
-    flags |= HAS_GUESSED_ATOM;
-}
-
-inline void
-JSFunction::setEnvironment(JSObject *obj)
-{
-    JS_ASSERT(isInterpreted());
-    *(js::HeapPtrObject *)&u.i.env_ = obj;
-}
-
-inline void
-JSFunction::initEnvironment(JSObject *obj)
-{
-    JS_ASSERT(isInterpreted());
-    ((js::HeapPtrObject *)&u.i.env_)->init(obj);
-}
-
-inline void
-JSFunction::initializeExtended()
-{
-    JS_ASSERT(isExtended());
-
-    JS_ASSERT(mozilla::ArrayLength(toExtended()->extendedSlots) == 2);
-    toExtended()->extendedSlots[0].init(js::UndefinedValue());
-    toExtended()->extendedSlots[1].init(js::UndefinedValue());
-}
-
-inline void
-JSFunction::initExtendedSlot(size_t which, const js::Value &val)
-{
-    JS_ASSERT(which < mozilla::ArrayLength(toExtended()->extendedSlots));
-    toExtended()->extendedSlots[which].init(val);
-}
-
-inline void
-JSFunction::setExtendedSlot(size_t which, const js::Value &val)
-{
-    JS_ASSERT(which < mozilla::ArrayLength(toExtended()->extendedSlots));
-    toExtended()->extendedSlots[which] = val;
-}
-
 namespace js {
 
 inline const char *
 GetFunctionNameBytes(JSContext *cx, JSFunction *fun, JSAutoByteString *bytes)
 {
     JSAtom *atom = fun->atom();
     if (atom)
         return bytes->encodeLatin1(cx, atom);
@@ -170,18 +116,9 @@ JSFunction::setScript(JSScript *script_)
 
 inline void
 JSFunction::initScript(JSScript *script_)
 {
     JS_ASSERT(isInterpreted());
     mutableScript().init(script_);
 }
 
-inline JSObject *
-JSFunction::getBoundFunctionTarget() const
-{
-    JS_ASSERT(isBoundFunction());
-
-    /* Bound functions abuse |parent| to store their target function. */
-    return getParent();
-}
-
 #endif /* jsfuninlines_h */
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -11,16 +11,17 @@
 
 #include "mozilla/DebugOnly.h"
 #include "mozilla/MemoryReporting.h"
 
 #include "jslock.h"
 #include "jsobj.h"
 
 #include "js/GCAPI.h"
+#include "js/Tracer.h"
 #include "js/Vector.h"
 
 class JSAtom;
 struct JSCompartment;
 class JSFlatString;
 class JSLinearString;
 
 namespace js {
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -346,38 +346,58 @@ class JSObject : public js::ObjectImpl
     static inline bool updateSlotsForSpan(js::ExclusiveContext *cx,
                                           js::HandleObject obj, size_t oldSpan, size_t newSpan);
 
   public:
     /*
      * Trigger the write barrier on a range of slots that will no longer be
      * reachable.
      */
-    inline void prepareSlotRangeForOverwrite(size_t start, size_t end);
-    inline void prepareElementRangeForOverwrite(size_t start, size_t end);
+    void prepareSlotRangeForOverwrite(size_t start, size_t end) {
+        for (size_t i = start; i < end; i++)
+            getSlotAddressUnchecked(i)->js::HeapSlot::~HeapSlot();
+    }
+
+    void prepareElementRangeForOverwrite(size_t start, size_t end) {
+        JS_ASSERT(end <= getDenseInitializedLength());
+        for (size_t i = start; i < end; i++)
+            elements[i].js::HeapSlot::~HeapSlot();
+    }
 
     void rollbackProperties(js::ExclusiveContext *cx, uint32_t slotSpan);
 
-    inline void nativeSetSlot(uint32_t slot, const js::Value &value);
+    void nativeSetSlot(uint32_t slot, const js::Value &value) {
+        JS_ASSERT(uninlinedIsNative());
+        JS_ASSERT(slot < uninlinedSlotSpan());
+        return setSlot(slot, value);
+    }
+
     static inline void nativeSetSlotWithType(js::ExclusiveContext *cx,
                                              js::HandleObject, js::Shape *shape,
                                              const js::Value &value);
 
     inline const js::Value &getReservedSlot(uint32_t index) const {
         JS_ASSERT(index < JSSLOT_FREE(getClass()));
         return getSlot(index);
     }
 
     inline js::HeapSlot &getReservedSlotRef(uint32_t index) {
         JS_ASSERT(index < JSSLOT_FREE(getClass()));
         return getSlotRef(index);
     }
 
-    inline void initReservedSlot(uint32_t index, const js::Value &v);
-    inline void setReservedSlot(uint32_t index, const js::Value &v);
+    void initReservedSlot(uint32_t index, const js::Value &v) {
+        JS_ASSERT(index < JSSLOT_FREE(getClass()));
+        initSlot(index, v);
+    }
+
+    void setReservedSlot(uint32_t index, const js::Value &v) {
+        JS_ASSERT(index < JSSLOT_FREE(getClass()));
+        setSlot(index, v);
+    }
 
     /*
      * Marks this object as having a singleton type, and leave the type lazy.
      * Constructs a new, unique shape for the object.
      */
     static inline bool setSingletonType(js::ExclusiveContext *cx, js::HandleObject obj);
 
     // uninlinedGetType() is the same as getType(), but not inlined.
@@ -523,47 +543,79 @@ class JSObject : public js::ObjectImpl
     static inline bool isFrozen(JSContext *cx, js::HandleObject obj, bool *resultp) {
         return isSealedOrFrozen(cx, obj, FREEZE, resultp);
     }
 
     /* toString support. */
     static const char *className(JSContext *cx, js::HandleObject obj);
 
     /* Accessors for elements. */
-    inline bool ensureElements(js::ThreadSafeContext *cx, uint32_t capacity);
+    bool ensureElements(js::ThreadSafeContext *cx, uint32_t capacity) {
+        if (capacity > getDenseCapacity())
+            return growElements(cx, capacity);
+        return true;
+    }
+
     bool growElements(js::ThreadSafeContext *cx, uint32_t newcap);
     void shrinkElements(js::ThreadSafeContext *cx, uint32_t cap);
     void setDynamicElements(js::ObjectElements *header) {
         JS_ASSERT(!hasDynamicElements());
         elements = header->elements();
         JS_ASSERT(hasDynamicElements());
     }
 
     uint32_t getDenseCapacity() {
         JS_ASSERT(uninlinedIsNative());
         JS_ASSERT(getElementsHeader()->capacity >= getElementsHeader()->initializedLength);
         return getElementsHeader()->capacity;
     }
 
-    inline void setDenseInitializedLength(uint32_t length);
+    void setDenseInitializedLength(uint32_t length) {
+        JS_ASSERT(uninlinedIsNative());
+        JS_ASSERT(length <= getDenseCapacity());
+        prepareElementRangeForOverwrite(length, getElementsHeader()->initializedLength);
+        getElementsHeader()->initializedLength = length;
+    }
+
     inline void ensureDenseInitializedLength(js::ExclusiveContext *cx,
                                              uint32_t index, uint32_t extra);
-    inline void setDenseElement(uint32_t index, const js::Value &val);
-    inline void initDenseElement(uint32_t index, const js::Value &val);
-    inline void setDenseElementMaybeConvertDouble(uint32_t index, const js::Value &val);
+    void setDenseElement(uint32_t index, const js::Value &val) {
+        JS_ASSERT(uninlinedIsNative() && index < getDenseInitializedLength());
+        elements[index].set(this, js::HeapSlot::Element, index, val);
+    }
+
+    void initDenseElement(uint32_t index, const js::Value &val) {
+        JS_ASSERT(uninlinedIsNative() && index < getDenseInitializedLength());
+        elements[index].init(this, js::HeapSlot::Element, index, val);
+    }
+
+    void setDenseElementMaybeConvertDouble(uint32_t index, const js::Value &val) {
+        if (val.isInt32() && shouldConvertDoubleElements())
+            setDenseElement(index, js::DoubleValue(val.toInt32()));
+        else
+            setDenseElement(index, val);
+    }
+
     static inline void setDenseElementWithType(js::ExclusiveContext *cx, js::HandleObject obj,
                                                uint32_t index, const js::Value &val);
     static inline void initDenseElementWithType(js::ExclusiveContext *cx, js::HandleObject obj,
                                                 uint32_t index, const js::Value &val);
     static inline void setDenseElementHole(js::ExclusiveContext *cx,
                                            js::HandleObject obj, uint32_t index);
     static inline void removeDenseElementForSparseIndex(js::ExclusiveContext *cx,
                                                         js::HandleObject obj, uint32_t index);
     inline void copyDenseElements(uint32_t dstStart, const js::Value *src, uint32_t count);
-    inline void initDenseElements(uint32_t dstStart, const js::Value *src, uint32_t count);
+
+    void initDenseElements(uint32_t dstStart, const js::Value *src, uint32_t count) {
+        JS_ASSERT(dstStart + count <= getDenseCapacity());
+        JSRuntime *rt = runtimeFromMainThread();
+        for (uint32_t i = 0; i < count; ++i)
+            elements[dstStart + i].init(rt, this, js::HeapSlot::Element, dstStart + i, src[i]);
+    }
+
     inline void moveDenseElements(uint32_t dstStart, uint32_t srcStart, uint32_t count);
     inline void moveDenseElementsUnbarriered(uint32_t dstStart, uint32_t srcStart, uint32_t count);
 
     bool shouldConvertDoubleElements() {
         JS_ASSERT(uninlinedIsNative());
         return getElementsHeader()->shouldConvertDoubleElements();
     }
 
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -125,91 +125,22 @@ JSObject::canRemoveLastProperty()
     JS_ASSERT(!inDictionaryMode());
     js::Shape *previous = lastProperty()->previous().get();
     return previous->getObjectParent() == lastProperty()->getObjectParent()
         && previous->getObjectMetadata() == lastProperty()->getObjectMetadata()
         && previous->getObjectFlags() == lastProperty()->getObjectFlags();
 }
 
 inline void
-JSObject::setReservedSlot(uint32_t index, const js::Value &v)
-{
-    JS_ASSERT(index < JSSLOT_FREE(getClass()));
-    setSlot(index, v);
-}
-
-inline void
-JSObject::initReservedSlot(uint32_t index, const js::Value &v)
-{
-    JS_ASSERT(index < JSSLOT_FREE(getClass()));
-    initSlot(index, v);
-}
-
-inline void
-JSObject::prepareSlotRangeForOverwrite(size_t start, size_t end)
-{
-    for (size_t i = start; i < end; i++)
-        getSlotAddressUnchecked(i)->js::HeapSlot::~HeapSlot();
-}
-
-inline void
-JSObject::prepareElementRangeForOverwrite(size_t start, size_t end)
-{
-    JS_ASSERT(end <= getDenseInitializedLength());
-    for (size_t i = start; i < end; i++)
-        elements[i].js::HeapSlot::~HeapSlot();
-}
-
-inline void
-JSObject::setDenseInitializedLength(uint32_t length)
-{
-    JS_ASSERT(isNative());
-    JS_ASSERT(length <= getDenseCapacity());
-    prepareElementRangeForOverwrite(length, getElementsHeader()->initializedLength);
-    getElementsHeader()->initializedLength = length;
-}
-
-inline void
 JSObject::setShouldConvertDoubleElements()
 {
     JS_ASSERT(is<js::ArrayObject>() && !hasEmptyElements());
     getElementsHeader()->setShouldConvertDoubleElements();
 }
 
-inline bool
-JSObject::ensureElements(js::ThreadSafeContext *cx, uint32_t capacity)
-{
-    if (capacity > getDenseCapacity())
-        return growElements(cx, capacity);
-    return true;
-}
-
-inline void
-JSObject::setDenseElement(uint32_t index, const js::Value &val)
-{
-    JS_ASSERT(isNative() && index < getDenseInitializedLength());
-    elements[index].set(this, js::HeapSlot::Element, index, val);
-}
-
-inline void
-JSObject::setDenseElementMaybeConvertDouble(uint32_t index, const js::Value &val)
-{
-    if (val.isInt32() && shouldConvertDoubleElements())
-        setDenseElement(index, js::DoubleValue(val.toInt32()));
-    else
-        setDenseElement(index, val);
-}
-
-inline void
-JSObject::initDenseElement(uint32_t index, const js::Value &val)
-{
-    JS_ASSERT(isNative() && index < getDenseInitializedLength());
-    elements[index].init(this, js::HeapSlot::Element, index, val);
-}
-
 /* static */ inline void
 JSObject::setDenseElementWithType(js::ExclusiveContext *cx, js::HandleObject obj, uint32_t index,
                                   const js::Value &val)
 {
     js::types::AddTypePropertyId(cx, obj, JSID_VOID, val);
     obj->setDenseElementMaybeConvertDouble(index, val);
 }
 
@@ -245,25 +176,16 @@ JSObject::copyDenseElements(uint32_t dst
 {
     JS_ASSERT(dstStart + count <= getDenseCapacity());
     JS::Zone *zone = this->zone();
     for (uint32_t i = 0; i < count; ++i)
         elements[dstStart + i].set(zone, this, js::HeapSlot::Element, dstStart + i, src[i]);
 }
 
 inline void
-JSObject::initDenseElements(uint32_t dstStart, const js::Value *src, uint32_t count)
-{
-    JS_ASSERT(dstStart + count <= getDenseCapacity());
-    JSRuntime *rt = runtimeFromMainThread();
-    for (uint32_t i = 0; i < count; ++i)
-        elements[dstStart + i].init(rt, this, js::HeapSlot::Element, dstStart + i, src[i]);
-}
-
-inline void
 JSObject::moveDenseElements(uint32_t dstStart, uint32_t srcStart, uint32_t count)
 {
     JS_ASSERT(dstStart + count <= getDenseCapacity());
     JS_ASSERT(srcStart + count <= getDenseInitializedLength());
 
     /*
      * Using memmove here would skip write barriers. Also, we need to consider
      * an array containing [A, B, C], in the following situation:
@@ -642,24 +564,16 @@ JSObject::hasProperty(JSContext *cx, js:
     if (!lookupGeneric(cx, obj, id, &pobj, &prop)) {
         *foundp = false;  /* initialize to shut GCC up */
         return false;
     }
     *foundp = !!prop;
     return true;
 }
 
-inline void
-JSObject::nativeSetSlot(uint32_t slot, const js::Value &value)
-{
-    JS_ASSERT(isNative());
-    JS_ASSERT(slot < slotSpan());
-    return setSlot(slot, value);
-}
-
 /* static */ inline void
 JSObject::nativeSetSlotWithType(js::ExclusiveContext *cx, js::HandleObject obj, js::Shape *shape,
                                 const js::Value &value)
 {
     obj->nativeSetSlot(shape->slot(), value);
     js::types::AddTypePropertyId(cx, obj, shape->propid(), value);
 }
 
--- a/js/src/jspubtd.h
+++ b/js/src/jspubtd.h
@@ -167,40 +167,67 @@ typedef bool                    (*JSInit
 
 /*
  * Generic trace operation that calls JS_CallTracer on each traceable thing
  * stored in data.
  */
 typedef void
 (* JSTraceDataOp)(JSTracer *trc, void *data);
 
+namespace js {
+namespace gc {
+class StoreBuffer;
+}
+}
+
 namespace JS {
 
 typedef void (*OffThreadCompileCallback)(void *token, void *callbackData);
 
 namespace shadow {
 
 struct Runtime
 {
     /* Restrict zone access during Minor GC. */
     bool needsBarrier_;
 
 #ifdef JSGC_GENERATIONAL
     /* Allow inlining of Nursery::isInside. */
     uintptr_t gcNurseryStart_;
     uintptr_t gcNurseryEnd_;
+
+  private:
+    js::gc::StoreBuffer *gcStoreBufferPtr_;
 #endif
 
-    Runtime()
+  public:
+    Runtime(
+#ifdef JSGC_GENERATIONAL
+        js::gc::StoreBuffer *gcStoreBufferPtr
+#endif
+    )
       : needsBarrier_(false)
 #ifdef JSGC_GENERATIONAL
       , gcNurseryStart_(0)
       , gcNurseryEnd_(0)
+      , gcStoreBufferPtr_(gcStoreBufferPtr)
 #endif
     {}
+
+    bool needsBarrier() const {
+        return needsBarrier_;
+    }
+
+#ifdef JSGC_GENERATIONAL
+    js::gc::StoreBuffer *gcStoreBufferPtr() { return gcStoreBufferPtr_; }
+#endif
+
+    static JS::shadow::Runtime *asShadowRuntime(JSRuntime *rt) {
+        return reinterpret_cast<JS::shadow::Runtime*>(rt);
+    }
 };
 
 } /* namespace shadow */
 } /* namespace JS */
 
 namespace js {
 
 /*
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -34,16 +34,17 @@
 #include "vm/ArgumentsObject.h"
 #include "vm/Debugger.h"
 #include "vm/Interpreter.h"
 #include "vm/Shape.h"
 #include "vm/Xdr.h"
 
 #include "jsfuninlines.h"
 #include "jsinferinlines.h"
+#include "jsobjinlines.h"
 
 #include "vm/Runtime-inl.h"
 #include "vm/ScopeObject-inl.h"
 #include "vm/Stack-inl.h"
 
 using namespace js;
 using namespace js::gc;
 using namespace js::frontend;
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -65,25 +65,26 @@ EXPORTS.js += [
     '../public/LegacyIntTypes.h',
     '../public/MemoryMetrics.h',
     '../public/OldDebugAPI.h',
     '../public/ProfilingStack.h',
     '../public/PropertyKey.h',
     '../public/RequiredDefines.h',
     '../public/RootingAPI.h',
     '../public/StructuredClone.h',
+    '../public/Tracer.h',
     '../public/TypeDecls.h',
     '../public/Utility.h',
     '../public/Value.h',
     '../public/Vector.h',
 ]
 
 CPP_SOURCES += [
     'ArgumentsObject.cpp',
-    'TypedObject.cpp',
+    'Barrier.cpp',
     'BytecodeCompiler.cpp',
     'BytecodeEmitter.cpp',
     'CallNonGenericMethod.cpp',
     'CharacterEncoding.cpp',
     'DateTime.cpp',
     'Debugger.cpp',
     'Eval.cpp',
     'ExecutableAllocator.cpp',
@@ -127,18 +128,20 @@ CPP_SOURCES += [
     'Statistics.cpp',
     'StoreBuffer.cpp',
     'String.cpp',
     'StringBuffer.cpp',
     'StructuredClone.cpp',
     'TestingFunctions.cpp',
     'ThreadPool.cpp',
     'TokenStream.cpp',
+    'Tracer.cpp',
     'TypeRepresentation.cpp',
     'TypedArrayObject.cpp',
+    'TypedObject.cpp',
     'Unicode.cpp',
     'Value.cpp',
     'Verifier.cpp',
     'Xdr.cpp',
     'YarrCanonicalizeUCS2.cpp',
     'YarrInterpreter.cpp',
     'YarrPattern.cpp',
     'YarrSyntaxChecker.cpp',
deleted file mode 100644
--- a/js/src/vm/GlobalObject-inl.h
+++ /dev/null
@@ -1,158 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * 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/. */
-
-#ifndef vm_GlobalObject_inl_h
-#define vm_GlobalObject_inl_h
-
-#include "vm/GlobalObject.h"
-
-#include "vm/ObjectImpl-inl.h"
-
-namespace js {
-
-inline void
-GlobalObject::setDetailsForKey(JSProtoKey key, JSObject *ctor, JSObject *proto)
-{
-    JS_ASSERT(getSlotRef(key).isUndefined());
-    JS_ASSERT(getSlotRef(JSProto_LIMIT + key).isUndefined());
-    JS_ASSERT(getSlotRef(2 * JSProto_LIMIT + key).isUndefined());
-    setSlot(key, ObjectValue(*ctor));
-    setSlot(JSProto_LIMIT + key, ObjectValue(*proto));
-    setSlot(2 * JSProto_LIMIT + key, ObjectValue(*ctor));
-}
-
-inline void
-GlobalObject::setObjectClassDetails(JSFunction *ctor, JSObject *proto)
-{
-    setDetailsForKey(JSProto_Object, ctor, proto);
-}
-
-inline void
-GlobalObject::setFunctionClassDetails(JSFunction *ctor, JSObject *proto)
-{
-    setDetailsForKey(JSProto_Function, ctor, proto);
-}
-
-void
-GlobalObject::setThrowTypeError(JSFunction *fun)
-{
-    JS_ASSERT(getSlotRef(THROWTYPEERROR).isUndefined());
-    setSlot(THROWTYPEERROR, ObjectValue(*fun));
-}
-
-void
-GlobalObject::setOriginalEval(JSObject *evalobj)
-{
-    JS_ASSERT(getSlotRef(EVAL).isUndefined());
-    setSlot(EVAL, ObjectValue(*evalobj));
-}
-
-void
-GlobalObject::setCreateArrayFromBufferHelper(uint32_t slot, Handle<JSFunction*> fun)
-{
-    JS_ASSERT(getSlotRef(slot).isUndefined());
-    setSlot(slot, ObjectValue(*fun));
-}
-
-void
-GlobalObject::setCreateDataViewForThis(Handle<JSFunction*> fun)
-{
-    JS_ASSERT(getSlotRef(CREATE_DATAVIEW_FOR_THIS).isUndefined());
-    setSlot(CREATE_DATAVIEW_FOR_THIS, ObjectValue(*fun));
-}
-
-template<>
-inline void
-GlobalObject::setCreateArrayFromBuffer<uint8_t>(Handle<JSFunction*> fun)
-{
-    setCreateArrayFromBufferHelper(FROM_BUFFER_UINT8, fun);
-}
-
-template<>
-inline void
-GlobalObject::setCreateArrayFromBuffer<int8_t>(Handle<JSFunction*> fun)
-{
-    setCreateArrayFromBufferHelper(FROM_BUFFER_INT8, fun);
-}
-
-template<>
-inline void
-GlobalObject::setCreateArrayFromBuffer<uint16_t>(Handle<JSFunction*> fun)
-{
-    setCreateArrayFromBufferHelper(FROM_BUFFER_UINT16, fun);
-}
-
-template<>
-inline void
-GlobalObject::setCreateArrayFromBuffer<int16_t>(Handle<JSFunction*> fun)
-{
-    setCreateArrayFromBufferHelper(FROM_BUFFER_INT16, fun);
-}
-
-template<>
-inline void
-GlobalObject::setCreateArrayFromBuffer<uint32_t>(Handle<JSFunction*> fun)
-{
-    setCreateArrayFromBufferHelper(FROM_BUFFER_UINT32, fun);
-}
-
-template<>
-inline void
-GlobalObject::setCreateArrayFromBuffer<int32_t>(Handle<JSFunction*> fun)
-{
-    setCreateArrayFromBufferHelper(FROM_BUFFER_INT32, fun);
-}
-
-template<>
-inline void
-GlobalObject::setCreateArrayFromBuffer<float>(Handle<JSFunction*> fun)
-{
-    setCreateArrayFromBufferHelper(FROM_BUFFER_FLOAT32, fun);
-}
-
-template<>
-inline void
-GlobalObject::setCreateArrayFromBuffer<double>(Handle<JSFunction*> fun)
-{
-    setCreateArrayFromBufferHelper(FROM_BUFFER_FLOAT64, fun);
-}
-
-template<>
-inline void
-GlobalObject::setCreateArrayFromBuffer<uint8_clamped>(Handle<JSFunction*> fun)
-{
-    setCreateArrayFromBufferHelper(FROM_BUFFER_UINT8CLAMPED, fun);
-}
-
-void
-GlobalObject::setProtoGetter(JSFunction *protoGetter)
-{
-    JS_ASSERT(getSlotRef(PROTO_GETTER).isUndefined());
-    setSlot(PROTO_GETTER, ObjectValue(*protoGetter));
-}
-
-bool
-GlobalObject::setIntrinsicValue(JSContext *cx, PropertyName *name, HandleValue value)
-{
-#ifdef DEBUG
-    RootedObject self(cx, this);
-    JS_ASSERT(cx->runtime()->isSelfHostingGlobal(self));
-#endif
-    RootedObject holder(cx, intrinsicsHolder());
-    RootedValue valCopy(cx, value);
-    return JSObject::setProperty(cx, holder, holder, name, &valCopy, false);
-}
-
-void
-GlobalObject::setIntrinsicsHolder(JSObject *obj)
-{
-    JS_ASSERT(getSlotRef(INTRINSICS).isUndefined());
-    setSlot(INTRINSICS, ObjectValue(*obj));
-}
-
-} // namespace js
-
-#endif /* vm_GlobalObject_inl_h */
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -1,15 +1,15 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * 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/. */
 
-#include "vm/GlobalObject-inl.h"
+#include "vm/GlobalObject.h"
 
 #include "jscntxt.h"
 #include "jsdate.h"
 #include "jsexn.h"
 #include "jsfriendapi.h"
 #include "jsmath.h"
 #include "json.h"
 #include "jsweakmap.h"
--- a/js/src/vm/GlobalObject.h
+++ b/js/src/vm/GlobalObject.h
@@ -119,25 +119,52 @@ class GlobalObject : public JSObject
     ::js_InitObjectClass(JSContext *cx, js::HandleObject);
     friend JSObject *
     ::js_InitFunctionClass(JSContext *cx, js::HandleObject);
 
     /* Initialize the Function and Object classes.  Must only be called once! */
     JSObject *
     initFunctionAndObjectClasses(JSContext *cx);
 
-    inline void setDetailsForKey(JSProtoKey key, JSObject *ctor, JSObject *proto);
-    inline void setObjectClassDetails(JSFunction *ctor, JSObject *proto);
-    inline void setFunctionClassDetails(JSFunction *ctor, JSObject *proto);
+    void setDetailsForKey(JSProtoKey key, JSObject *ctor, JSObject *proto) {
+        JS_ASSERT(getSlotRef(key).isUndefined());
+        JS_ASSERT(getSlotRef(JSProto_LIMIT + key).isUndefined());
+        JS_ASSERT(getSlotRef(2 * JSProto_LIMIT + key).isUndefined());
+        setSlot(key, ObjectValue(*ctor));
+        setSlot(JSProto_LIMIT + key, ObjectValue(*proto));
+        setSlot(2 * JSProto_LIMIT + key, ObjectValue(*ctor));
+    }
+
+    void setObjectClassDetails(JSFunction *ctor, JSObject *proto) {
+        setDetailsForKey(JSProto_Object, ctor, proto);
+    }
+
+    void setFunctionClassDetails(JSFunction *ctor, JSObject *proto) {
+        setDetailsForKey(JSProto_Function, ctor, proto);
+    }
 
-    inline void setThrowTypeError(JSFunction *fun);
-    inline void setOriginalEval(JSObject *evalobj);
-    inline void setProtoGetter(JSFunction *protoGetter);
+    void setThrowTypeError(JSFunction *fun) {
+        JS_ASSERT(getSlotRef(THROWTYPEERROR).isUndefined());
+        setSlot(THROWTYPEERROR, ObjectValue(*fun));
+    }
+
+    void setOriginalEval(JSObject *evalobj) {
+        JS_ASSERT(getSlotRef(EVAL).isUndefined());
+        setSlot(EVAL, ObjectValue(*evalobj));
+    }
 
-    inline void setIntrinsicsHolder(JSObject *obj);
+    void setProtoGetter(JSFunction *protoGetter) {
+        JS_ASSERT(getSlotRef(PROTO_GETTER).isUndefined());
+        setSlot(PROTO_GETTER, ObjectValue(*protoGetter));
+    }
+
+    void setIntrinsicsHolder(JSObject *obj) {
+        JS_ASSERT(getSlotRef(INTRINSICS).isUndefined());
+        setSlot(INTRINSICS, ObjectValue(*obj));
+    }
 
     Value getConstructor(JSProtoKey key) const {
         JS_ASSERT(key <= JSProto_LIMIT);
         return getSlot(key);
     }
 
     Value getPrototype(JSProtoKey key) const {
         JS_ASSERT(key <= JSProto_LIMIT);
@@ -188,21 +215,27 @@ class GlobalObject : public JSObject
     }
 
     Value createArrayFromBufferHelper(uint32_t slot) const {
         JS_ASSERT(typedArrayClassesInitialized());
         JS_ASSERT(FROM_BUFFER_UINT8 <= slot && slot <= FROM_BUFFER_UINT8CLAMPED);
         return getSlot(slot);
     }
 
-    inline void setCreateArrayFromBufferHelper(uint32_t slot, Handle<JSFunction*> fun);
+    void setCreateArrayFromBufferHelper(uint32_t slot, Handle<JSFunction*> fun) {
+        JS_ASSERT(getSlotRef(slot).isUndefined());
+        setSlot(slot, ObjectValue(*fun));
+    }
 
   public:
     /* XXX Privatize me! */
-    inline void setCreateDataViewForThis(Handle<JSFunction*> fun);
+    void setCreateDataViewForThis(Handle<JSFunction*> fun) {
+        JS_ASSERT(getSlotRef(CREATE_DATAVIEW_FOR_THIS).isUndefined());
+        setSlot(CREATE_DATAVIEW_FOR_THIS, ObjectValue(*fun));
+    }
 
     template<typename T>
     inline void setCreateArrayFromBuffer(Handle<JSFunction*> fun);
 
   public:
     static GlobalObject *create(JSContext *cx, Class *clasp);
 
     /*
@@ -411,19 +444,30 @@ class GlobalObject : public JSObject
             return true;
         if (!cx->runtime()->cloneSelfHostedValue(cx, name, value))
             return false;
         mozilla::DebugOnly<bool> ok = JS_DefinePropertyById(cx, holder, id, value, NULL, NULL, 0);
         JS_ASSERT(ok);
         return true;
     }
 
-    inline bool setIntrinsicValue(JSContext *cx, PropertyName *name, HandleValue value);
+    bool setIntrinsicValue(JSContext *cx, PropertyName *name, HandleValue value) {
+#ifdef DEBUG
+        RootedObject self(cx, this);
+        JS_ASSERT(cx->runtime()->isSelfHostingGlobal(self));
+#endif
+        RootedObject holder(cx, intrinsicsHolder());
+        RootedValue valCopy(cx, value);
+        return JSObject::setProperty(cx, holder, holder, name, &valCopy, false);
+    }
 
-    inline RegExpStatics *getRegExpStatics() const;
+    RegExpStatics *getRegExpStatics() const {
+        JSObject &resObj = getSlot(REGEXP_STATICS).toObject();
+        return static_cast<RegExpStatics *>(resObj.getPrivate());
+    }
 
     JSObject *getThrowTypeError() const {
         JS_ASSERT(functionObjectClassesInitialized());
         return &getSlot(THROWTYPEERROR).toObject();
     }
 
     Value createDataViewForThis() const {
         JS_ASSERT(dataViewClassInitialized());
@@ -478,16 +522,79 @@ class GlobalObject : public JSObject
      * exist. Returns NULL only on OOM.
      */
     static DebuggerVector *getOrCreateDebuggers(JSContext *cx, Handle<GlobalObject*> global);
 
     static bool addDebugger(JSContext *cx, Handle<GlobalObject*> global, Debugger *dbg);
 };
 
 template<>
+inline void
+GlobalObject::setCreateArrayFromBuffer<uint8_t>(Handle<JSFunction*> fun)
+{
+    setCreateArrayFromBufferHelper(FROM_BUFFER_UINT8, fun);
+}
+
+template<>
+inline void
+GlobalObject::setCreateArrayFromBuffer<int8_t>(Handle<JSFunction*> fun)
+{
+    setCreateArrayFromBufferHelper(FROM_BUFFER_INT8, fun);
+}
+
+template<>
+inline void
+GlobalObject::setCreateArrayFromBuffer<uint16_t>(Handle<JSFunction*> fun)
+{
+    setCreateArrayFromBufferHelper(FROM_BUFFER_UINT16, fun);
+}
+
+template<>
+inline void
+GlobalObject::setCreateArrayFromBuffer<int16_t>(Handle<JSFunction*> fun)
+{
+    setCreateArrayFromBufferHelper(FROM_BUFFER_INT16, fun);
+}
+
+template<>
+inline void
+GlobalObject::setCreateArrayFromBuffer<uint32_t>(Handle<JSFunction*> fun)
+{
+    setCreateArrayFromBufferHelper(FROM_BUFFER_UINT32, fun);
+}
+
+template<>
+inline void
+GlobalObject::setCreateArrayFromBuffer<int32_t>(Handle<JSFunction*> fun)
+{
+    setCreateArrayFromBufferHelper(FROM_BUFFER_INT32, fun);
+}
+
+template<>
+inline void
+GlobalObject::setCreateArrayFromBuffer<float>(Handle<JSFunction*> fun)
+{
+    setCreateArrayFromBufferHelper(FROM_BUFFER_FLOAT32, fun);
+}
+
+template<>
+inline void
+GlobalObject::setCreateArrayFromBuffer<double>(Handle<JSFunction*> fun)
+{
+    setCreateArrayFromBufferHelper(FROM_BUFFER_FLOAT64, fun);
+}
+
+template<>
+inline void
+GlobalObject::setCreateArrayFromBuffer<uint8_clamped>(Handle<JSFunction*> fun)
+{
+    setCreateArrayFromBufferHelper(FROM_BUFFER_UINT8CLAMPED, fun);
+}
+
+template<>
 inline Value
 GlobalObject::createArrayFromBuffer<uint8_t>() const
 {
     return createArrayFromBufferHelper(FROM_BUFFER_UINT8);
 }
 
 template<>
 inline Value
--- a/js/src/vm/Interpreter-inl.h
+++ b/js/src/vm/Interpreter-inl.h
@@ -18,17 +18,16 @@
 #include "jit/IonCompartment.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/ForkJoin.h"
 
 #include "jsatominlines.h"
 #include "jsinferinlines.h"
 #include "jsobjinlines.h"
 
-#include "vm/GlobalObject-inl.h"
 #include "vm/Stack-inl.h"
 
 namespace js {
 
 inline bool
 ComputeThis(JSContext *cx, AbstractFramePtr frame)
 {
     JS_ASSERT_IF(frame.isStackFrame(), !frame.asStackFrame()->runningInJit());
--- a/js/src/vm/ObjectImpl-inl.h
+++ b/js/src/vm/ObjectImpl-inl.h
@@ -62,78 +62,16 @@ js::ObjectImpl::isExtensible(ExclusiveCo
 }
 
 inline bool
 js::ObjectImpl::isNative() const
 {
     return lastProperty()->isNative();
 }
 
-#ifdef DEBUG
-inline bool
-IsObjectValueInCompartment(js::Value v, JSCompartment *comp)
-{
-    if (!v.isObject())
-        return true;
-    return v.toObject().compartment() == comp;
-}
-#endif
-
-inline void
-js::ObjectImpl::setSlot(uint32_t slot, const js::Value &value)
-{
-    MOZ_ASSERT(slotInRange(slot));
-    MOZ_ASSERT(IsObjectValueInCompartment(value, asObjectPtr()->compartment()));
-    getSlotRef(slot).set(this->asObjectPtr(), HeapSlot::Slot, slot, value);
-}
-
-inline void
-js::ObjectImpl::setCrossCompartmentSlot(uint32_t slot, const js::Value &value)
-{
-    MOZ_ASSERT(slotInRange(slot));
-    getSlotRef(slot).set(this->asObjectPtr(), HeapSlot::Slot, slot, value);
-}
-
-inline void
-js::ObjectImpl::initSlot(uint32_t slot, const js::Value &value)
-{
-    MOZ_ASSERT(getSlot(slot).isUndefined());
-    MOZ_ASSERT(slotInRange(slot));
-    MOZ_ASSERT(IsObjectValueInCompartment(value, asObjectPtr()->compartment()));
-    initSlotUnchecked(slot, value);
-}
-
-inline void
-js::ObjectImpl::initCrossCompartmentSlot(uint32_t slot, const js::Value &value)
-{
-    MOZ_ASSERT(getSlot(slot).isUndefined());
-    MOZ_ASSERT(slotInRange(slot));
-    initSlotUnchecked(slot, value);
-}
-
-inline void
-js::ObjectImpl::initSlotUnchecked(uint32_t slot, const js::Value &value)
-{
-    getSlotAddressUnchecked(slot)->init(this->asObjectPtr(), HeapSlot::Slot, slot, value);
-}
-
-inline void
-js::ObjectImpl::setFixedSlot(uint32_t slot, const js::Value &value)
-{
-    MOZ_ASSERT(slot < numFixedSlots());
-    fixedSlots()[slot].set(this->asObjectPtr(), HeapSlot::Slot, slot, value);
-}
-
-inline void
-js::ObjectImpl::initFixedSlot(uint32_t slot, const js::Value &value)
-{
-    MOZ_ASSERT(slot < numFixedSlots());
-    fixedSlots()[slot].init(this->asObjectPtr(), HeapSlot::Slot, slot, value);
-}
-
 inline uint32_t
 js::ObjectImpl::slotSpan() const
 {
     if (inDictionaryMode())
         return lastProperty()->base()->slotSpan();
     return lastProperty()->slotSpan();
 }
 
@@ -183,24 +121,16 @@ js::ObjectImpl::privateWriteBarrierPre(v
     Zone *zone = this->zone();
     if (zone->needsBarrier()) {
         if (*old && getClass()->trace)
             getClass()->trace(zone->barrierTracer(), this->asObjectPtr());
     }
 #endif
 }
 
-inline void
-js::ObjectImpl::privateWriteBarrierPost(void **pprivate)
-{
-#ifdef JSGC_GENERATIONAL
-    runtimeFromAnyThread()->gcStoreBuffer.putCell(reinterpret_cast<js::gc::Cell **>(pprivate));
-#endif
-}
-
 /* static */ inline void
 js::ObjectImpl::writeBarrierPre(ObjectImpl *obj)
 {
 #ifdef JSGC_INCREMENTAL
     /*
      * This would normally be a null test, but TypeScript::global uses 0x1 as a
      * special value.
      */
@@ -212,42 +142,16 @@ js::ObjectImpl::writeBarrierPre(ObjectIm
         MOZ_ASSERT(!zone->runtimeFromMainThread()->isHeapMajorCollecting());
         JSObject *tmp = obj->asObjectPtr();
         MarkObjectUnbarriered(zone->barrierTracer(), &tmp, "write barrier");
         MOZ_ASSERT(tmp == obj->asObjectPtr());
     }
 #endif
 }
 
-/* static */ inline void
-js::ObjectImpl::writeBarrierPost(ObjectImpl *obj, void *addr)
-{
-#ifdef JSGC_GENERATIONAL
-    if (IsNullTaggedPointer(obj))
-        return;
-    obj->runtimeFromAnyThread()->gcStoreBuffer.putCell((Cell **)addr);
-#endif
-}
-
-/* static */ inline void
-js::ObjectImpl::writeBarrierPostRelocate(ObjectImpl *obj, void *addr)
-{
-#ifdef JSGC_GENERATIONAL
-    obj->runtimeFromAnyThread()->gcStoreBuffer.putRelocatableCell((Cell **)addr);
-#endif
-}
-
-/* static */ inline void
-js::ObjectImpl::writeBarrierPostRemove(ObjectImpl *obj, void *addr)
-{
-#ifdef JSGC_GENERATIONAL
-    obj->runtimeFromAnyThread()->gcStoreBuffer.removeRelocatableCell((Cell **)addr);
-#endif
-}
-
 inline void
 js::ObjectImpl::setPrivate(void *data)
 {
     void **pprivate = &privateRef(numFixedSlots());
     privateWriteBarrierPre(pprivate);
     *pprivate = data;
 }
 
--- a/js/src/vm/ObjectImpl.cpp
+++ b/js/src/vm/ObjectImpl.cpp
@@ -11,16 +11,22 @@
 #include "vm/Debugger.h"
 
 #include "jsobjinlines.h"
 
 #include "gc/Barrier-inl.h"
 
 using namespace js;
 
+JSCompartment *
+js::ObjectImpl::uninlinedCompartment() const
+{
+    return compartment();
+}
+
 bool
 js::ObjectImpl::uninlinedIsNative() const
 {
     return isNative();
 }
 
 uint32_t
 js::ObjectImpl::uninlinedSlotSpan() const
--- a/js/src/vm/ObjectImpl.h
+++ b/js/src/vm/ObjectImpl.h
@@ -13,16 +13,17 @@
 #include <stdint.h>
 
 #include "jsfriendapi.h"
 #include "jsinfer.h"
 #include "NamespaceImports.h"
 
 #include "gc/Barrier.h"
 #include "gc/Heap.h"
+#include "gc/Marking.h"
 #include "js/Value.h"
 #include "vm/NumericConversions.h"
 #include "vm/String.h"
 
 #define JSSLOT_FREE(clasp)  JSCLASS_RESERVED_SLOTS(clasp)
 
 namespace js {
 
@@ -1140,16 +1141,21 @@ struct ObjectOps;
 class Shape;
 
 class NewObjectCache;
 class TaggedProto;
 
 inline Value
 ObjectValue(ObjectImpl &obj);
 
+#ifdef DEBUG
+static inline bool
+IsObjectValueInCompartment(js::Value v, JSCompartment *comp);
+#endif
+
 /*
  * ObjectImpl specifies the internal implementation of an object.  (In contrast
  * JSObject specifies an "external" interface, at the conceptual level of that
  * exposed in ECMAScript.)
  *
  * The |shape_| member stores the shape of the object, which includes the
  * object's class and the layout of all its properties.
  *
@@ -1192,17 +1198,17 @@ ObjectValue(ObjectImpl &obj);
  * will change so that some members are private, and only certain methods that
  * act upon them will be protected.
  */
 class ObjectImpl : public gc::Cell
 {
   protected:
     /*
      * Shape of the object, encodes the layout of the object's properties and
-     * all other information about its structure. See jsscope.h.
+     * all other information about its structure. See vm/Shape.h.
      */
     HeapPtrShape shape_;
 
     /*
      * The object's type and prototype. For objects with the LAZY_TYPE flag
      * set, this is the prototype's default 'new' type and can only be used
      * to get that prototype.
      */
@@ -1431,17 +1437,19 @@ class ObjectImpl : public gc::Cell
         MOZ_ASSERT(shape_);
         return shape_;
     }
 
     bool generateOwnShape(ExclusiveContext *cx, js::Shape *newShape = NULL) {
         return replaceWithNewEquivalentShape(cx, lastProperty(), newShape);
     }
 
+    // uninlinedCompartment() is equivalent to compartment(), but isn't inlined.
     inline JSCompartment *compartment() const;
+    JSCompartment *uninlinedCompartment() const;
 
     // uninlinedIsNative() is equivalent to isNative(), but isn't inlined.
     inline bool isNative() const;
     bool uninlinedIsNative() const;
 
     types::TypeObject *type() const {
         MOZ_ASSERT(!hasLazyType());
         return type_;
@@ -1568,36 +1576,65 @@ class ObjectImpl : public gc::Cell
         JS_ASSERT(uninlinedIsNative() && slot < uninlinedSlotSpan());
         return getSlotRef(slot);
     }
     const Value &nativeGetSlot(uint32_t slot) const {
         JS_ASSERT(uninlinedIsNative() && slot < uninlinedSlotSpan());
         return getSlot(slot);
     }
 
-    inline void setSlot(uint32_t slot, const Value &value);
-    inline void setCrossCompartmentSlot(uint32_t slot, const Value &value);
-    inline void initSlot(uint32_t slot, const Value &value);
-    inline void initCrossCompartmentSlot(uint32_t slot, const Value &value);
-    inline void initSlotUnchecked(uint32_t slot, const Value &value);
+    void setSlot(uint32_t slot, const Value &value) {
+        MOZ_ASSERT(slotInRange(slot));
+        MOZ_ASSERT(IsObjectValueInCompartment(value, uninlinedCompartment()));
+        getSlotRef(slot).set(this->asObjectPtr(), HeapSlot::Slot, slot, value);
+    }
+
+    inline void setCrossCompartmentSlot(uint32_t slot, const Value &value) {
+        MOZ_ASSERT(slotInRange(slot));
+        getSlotRef(slot).set(this->asObjectPtr(), HeapSlot::Slot, slot, value);
+    }
+
+    void initSlot(uint32_t slot, const Value &value) {
+        MOZ_ASSERT(getSlot(slot).isUndefined());
+        MOZ_ASSERT(slotInRange(slot));
+        MOZ_ASSERT(IsObjectValueInCompartment(value, uninlinedCompartment()));
+        initSlotUnchecked(slot, value);
+    }
+
+    void initCrossCompartmentSlot(uint32_t slot, const Value &value) {
+        MOZ_ASSERT(getSlot(slot).isUndefined());
+        MOZ_ASSERT(slotInRange(slot));
+        initSlotUnchecked(slot, value);
+    }
+
+    void initSlotUnchecked(uint32_t slot, const Value &value) {
+        getSlotAddressUnchecked(slot)->init(this->asObjectPtr(), HeapSlot::Slot, slot, value);
+    }
 
     /* For slots which are known to always be fixed, due to the way they are allocated. */
 
     HeapSlot &getFixedSlotRef(uint32_t slot) {
         MOZ_ASSERT(slot < numFixedSlots());
         return fixedSlots()[slot];
     }
 
     const Value &getFixedSlot(uint32_t slot) const {
         MOZ_ASSERT(slot < numFixedSlots());
         return fixedSlots()[slot];
     }
 
-    inline void setFixedSlot(uint32_t slot, const Value &value);
-    inline void initFixedSlot(uint32_t slot, const Value &value);
+    void setFixedSlot(uint32_t slot, const Value &value) {
+        MOZ_ASSERT(slot < numFixedSlots());
+        fixedSlots()[slot].set(this->asObjectPtr(), HeapSlot::Slot, slot, value);
+    }
+
+    void initFixedSlot(uint32_t slot, const Value &value) {
+        MOZ_ASSERT(slot < numFixedSlots());
+        fixedSlots()[slot].init(this->asObjectPtr(), HeapSlot::Slot, slot, value);
+    }
 
     /*
      * Get the number of dynamic slots to allocate to cover the properties in
      * an object with the given number of fixed slots and slot span. The slot
      * capacity is not stored explicitly, and the allocated size of the slot
      * array is kept in sync with this count.
      */
     static uint32_t dynamicSlotsCount(uint32_t nfixed, uint32_t span) {
@@ -1653,24 +1690,49 @@ class ObjectImpl : public gc::Cell
     }
 
     inline bool hasEmptyElements() const {
         return elements == emptyObjectElements;
     }
 
     /* GC support. */
     JS_ALWAYS_INLINE Zone *zone() const;
-    static inline ThingRootKind rootKind() { return THING_ROOT_OBJECT; }
+    static ThingRootKind rootKind() { return THING_ROOT_OBJECT; }
+
     static inline void readBarrier(ObjectImpl *obj);
     static inline void writeBarrierPre(ObjectImpl *obj);
-    static inline void writeBarrierPost(ObjectImpl *obj, void *addr);
-    static inline void writeBarrierPostRelocate(ObjectImpl *obj, void *addr);
-    static inline void writeBarrierPostRemove(ObjectImpl *obj, void *addr);
+
+    static void writeBarrierPost(ObjectImpl *obj, void *addr) {
+#ifdef JSGC_GENERATIONAL
+        if (IsNullTaggedPointer(obj))
+            return;
+        obj->shadowRuntimeFromAnyThread()->gcStoreBufferPtr()->putCell((Cell **)addr);
+#endif
+    }
+
+    static void writeBarrierPostRelocate(ObjectImpl *obj, void *addr) {
+#ifdef JSGC_GENERATIONAL
+        obj->shadowRuntimeFromAnyThread()->gcStoreBufferPtr()->putRelocatableCell((Cell **)addr);
+#endif
+    }
+
+    static void writeBarrierPostRemove(ObjectImpl *obj, void *addr) {
+#ifdef JSGC_GENERATIONAL
+        obj->shadowRuntimeFromAnyThread()->gcStoreBufferPtr()->removeRelocatableCell((Cell **)addr);
+#endif
+    }
+
     inline void privateWriteBarrierPre(void **oldval);
-    inline void privateWriteBarrierPost(void **pprivate);
+
+    void privateWriteBarrierPost(void **pprivate) {
+#ifdef JSGC_GENERATIONAL
+        shadowRuntimeFromAnyThread()->gcStoreBufferPtr()->putCell(reinterpret_cast<js::gc::Cell **>(pprivate));
+#endif
+    }
+
     void markChildren(JSTracer *trc);
 
     /* Private data accessors. */
 
     inline void *&privateRef(uint32_t nfixed) const { /* XXX should be private, not protected! */
         /*
          * The private pointer of an object can hold any word sized value.
          * Private pointers are stored immediately after the last fixed slot of
@@ -1731,16 +1793,26 @@ ObjectValue(ObjectImpl &obj)
 }
 
 inline Handle<JSObject*>
 Downcast(Handle<ObjectImpl*> obj)
 {
     return Handle<JSObject*>::fromMarkedLocation(reinterpret_cast<JSObject* const*>(obj.address()));
 }
 
+#ifdef DEBUG
+static inline bool
+IsObjectValueInCompartment(js::Value v, JSCompartment *comp)
+{
+    if (!v.isObject())
+        return true;
+    return reinterpret_cast<ObjectImpl*>(&v.toObject())->uninlinedCompartment() == comp;
+}
+#endif
+
 extern JSObject *
 ArrayBufferDelegate(JSContext *cx, Handle<ObjectImpl*> obj);
 
 /* Generic [[GetOwnProperty]] method. */
 bool
 GetOwnElement(JSContext *cx, Handle<ObjectImpl*> obj, uint32_t index, unsigned resolveFlags,
               PropDesc *desc);
 extern bool
--- a/js/src/vm/RegExpObject-inl.h
+++ b/js/src/vm/RegExpObject-inl.h
@@ -13,53 +13,11 @@ namespace js {
 
 inline void
 RegExpObject::setShared(ExclusiveContext *cx, RegExpShared &shared)
 {
     shared.prepareForUse(cx);
     JSObject::setPrivate(&shared);
 }
 
-inline void
-RegExpObject::setLastIndex(double d)
-{
-    setSlot(LAST_INDEX_SLOT, NumberValue(d));
-}
-
-inline void
-RegExpObject::zeroLastIndex()
-{
-    setSlot(LAST_INDEX_SLOT, Int32Value(0));
-}
-
-inline void
-RegExpObject::setSource(JSAtom *source)
-{
-    setSlot(SOURCE_SLOT, StringValue(source));
-}
-
-inline void
-RegExpObject::setIgnoreCase(bool enabled)
-{
-    setSlot(IGNORE_CASE_FLAG_SLOT, BooleanValue(enabled));
-}
-
-inline void
-RegExpObject::setGlobal(bool enabled)
-{
-    setSlot(GLOBAL_FLAG_SLOT, BooleanValue(enabled));
-}
-
-inline void
-RegExpObject::setMultiline(bool enabled)
-{
-    setSlot(MULTILINE_FLAG_SLOT, BooleanValue(enabled));
-}
-
-inline void
-RegExpObject::setSticky(bool enabled)
-{
-    setSlot(STICKY_FLAG_SLOT, BooleanValue(enabled));
-}
-
 } /* namespace js */
 
 #endif /* vm_RegExpObject_inl_h */
--- a/js/src/vm/RegExpObject.h
+++ b/js/src/vm/RegExpObject.h
@@ -361,39 +361,60 @@ class RegExpObject : public JSObject
     static RegExpObject *
     createNoStatics(ExclusiveContext *cx, HandleAtom atom, RegExpFlag flags, frontend::TokenStream *ts);
 
     /* Accessors. */
 
     static unsigned lastIndexSlot() { return LAST_INDEX_SLOT; }
 
     const Value &getLastIndex() const { return getSlot(LAST_INDEX_SLOT); }
-    inline void setLastIndex(double d);
-    inline void zeroLastIndex();
+
+    void setLastIndex(double d) {
+        setSlot(LAST_INDEX_SLOT, NumberValue(d));
+    }
+
+    void zeroLastIndex() {
+        setSlot(LAST_INDEX_SLOT, Int32Value(0));
+    }
 
     JSFlatString *toString(JSContext *cx) const;
 
     JSAtom *getSource() const { return &getSlot(SOURCE_SLOT).toString()->asAtom(); }
-    inline void setSource(JSAtom *source);
+
+    void setSource(JSAtom *source) {
+        setSlot(SOURCE_SLOT, StringValue(source));
+    }
 
     RegExpFlag getFlags() const {
         unsigned flags = 0;
         flags |= global() ? GlobalFlag : 0;
         flags |= ignoreCase() ? IgnoreCaseFlag : 0;
         flags |= multiline() ? MultilineFlag : 0;
         flags |= sticky() ? StickyFlag : 0;
         return RegExpFlag(flags);
     }
 
     /* Flags. */
 
-    inline void setIgnoreCase(bool enabled);
-    inline void setGlobal(bool enabled);
-    inline void setMultiline(bool enabled);
-    inline void setSticky(bool enabled);
+    void setIgnoreCase(bool enabled) {
+        setSlot(IGNORE_CASE_FLAG_SLOT, BooleanValue(enabled));
+    }
+
+    void setGlobal(bool enabled) {
+        setSlot(GLOBAL_FLAG_SLOT, BooleanValue(enabled));
+    }
+
+    void setMultiline(bool enabled) {
+        setSlot(MULTILINE_FLAG_SLOT, BooleanValue(enabled));
+    }
+
+    void setSticky(bool enabled) {
+        setSlot(STICKY_FLAG_SLOT, BooleanValue(enabled));
+    }
+
     bool ignoreCase() const { return getSlot(IGNORE_CASE_FLAG_SLOT).toBoolean(); }
     bool global() const     { return getSlot(GLOBAL_FLAG_SLOT).toBoolean(); }
     bool multiline() const  { return getSlot(MULTILINE_FLAG_SLOT).toBoolean(); }
     bool sticky() const     { return getSlot(STICKY_FLAG_SLOT).toBoolean(); }
 
     void shared(RegExpGuard *g) const {
         JS_ASSERT(maybeShared() != NULL);
         g->init(*maybeShared());
@@ -401,16 +422,17 @@ class RegExpObject : public JSObject
 
     bool getShared(ExclusiveContext *cx, RegExpGuard *g) {
         if (RegExpShared *shared = maybeShared()) {
             g->init(*shared);
             return true;
         }
         return createShared(cx, g);
     }
+
     inline void setShared(ExclusiveContext *cx, RegExpShared &shared);
 
   private:
     friend class RegExpObjectBuilder;
 
     /*
      * Compute the initial shape to associate with fresh RegExp objects,
      * encoding their initial properties. Return the shape after
--- a/js/src/vm/RegExpStatics.h
+++ b/js/src/vm/RegExpStatics.h
@@ -195,23 +195,16 @@ class PreserveRegExpStatics
 
     bool init(JSContext *cx) {
         return original->save(cx, &buffer.getStatics());
     }
 
     ~PreserveRegExpStatics() { original->restore(); }
 };
 
-inline js::RegExpStatics *
-js::GlobalObject::getRegExpStatics() const
-{
-    JSObject &resObj = getSlot(REGEXP_STATICS).toObject();
-    return static_cast<RegExpStatics *>(resObj.getPrivate());
-}
-
 inline bool
 RegExpStatics::createDependent(JSContext *cx, size_t start, size_t end, MutableHandleValue out)
 {
     /* Private function: caller must perform lazy evaluation. */
     JS_ASSERT(!pendingLazyEvaluation);
 
     JS_ASSERT(start <= end);
     JS_ASSERT(end <= matchesInput->length());
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -93,17 +93,22 @@ PerThreadData::addToThreadList()
 void
 PerThreadData::removeFromThreadList()
 {
     JS_ASSERT(CurrentThreadCanAccessRuntime(runtime_));
     removeFrom(runtime_->threadList);
 }
 
 JSRuntime::JSRuntime(JSUseHelperThreads useHelperThreads)
-  : mainThread(this),
+  : JS::shadow::Runtime(
+#ifdef JSGC_GENERATIONAL
+        &gcStoreBuffer
+#endif
+    ),
+    mainThread(this),
     interrupt(0),
     handlingSignal(false),
     operationCallback(NULL),
 #ifdef JS_THREADSAFE
     operationCallbackLock(NULL),
 #ifdef DEBUG
     operationCallbackOwner(NULL),
 #endif
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -1221,20 +1221,16 @@ struct JSRuntime : public JS::shadow::Ru
      */
     volatile ptrdiff_t  gcMallocBytes;
 
   public:
     void setNeedsBarrier(bool needs) {
         needsBarrier_ = needs;
     }
 
-    bool needsBarrier() const {
-        return needsBarrier_;
-    }
-
     struct ExtraTracer {
         JSTraceDataOp op;
         void *data;
 
         ExtraTracer()
           : op(NULL), data(NULL)
         {}
         ExtraTracer(JSTraceDataOp op, void *data)
--- a/js/src/vm/ScopeObject-inl.h
+++ b/js/src/vm/ScopeObject-inl.h
@@ -5,29 +5,20 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef vm_ScopeObject_inl_h
 #define vm_ScopeObject_inl_h
 
 #include "vm/ScopeObject.h"
 
 #include "jsinferinlines.h"
-#include "jsobjinlines.h"
 
 namespace js {
 
 inline void
-ScopeObject::setEnclosingScope(HandleObject obj)
-{
-    JS_ASSERT_IF(obj->is<CallObject>() || obj->is<DeclEnvObject>() || obj->is<BlockObject>(),
-                 obj->isDelegate());
-    setFixedSlot(SCOPE_CHAIN_SLOT, ObjectValue(*obj));
-}
-
-inline void
 ScopeObject::setAliasedVar(JSContext *cx, ScopeCoordinate sc, PropertyName *name, const Value &v)
 {
     JS_ASSERT(is<CallObject>() || is<ClonedBlockObject>());
     JS_STATIC_ASSERT(CallObject::RESERVED_SLOTS == BlockObject::RESERVED_SLOTS);
 
     // name may be null for non-singletons, whose types do not need to be tracked.
     JS_ASSERT_IF(hasSingletonType(), name);
 
@@ -40,68 +31,11 @@ inline void
 CallObject::setAliasedVar(JSContext *cx, AliasedFormalIter fi, PropertyName *name, const Value &v)
 {
     JS_ASSERT(name == fi->name());
     setSlot(fi.scopeSlot(), v);
     if (hasSingletonType())
         types::AddTypePropertyId(cx, this, NameToId(name), v);
 }
 
-inline void
-BlockObject::setSlotValue(unsigned i, const Value &v)
-{
-    setSlot(RESERVED_SLOTS + i, v);
-}
-
-inline void
-StaticBlockObject::initPrevBlockChainFromParser(StaticBlockObject *prev)
-{
-    setReservedSlot(SCOPE_CHAIN_SLOT, ObjectOrNullValue(prev));
-}
-
-inline void
-StaticBlockObject::resetPrevBlockChainFromParser()
-{
-    setReservedSlot(SCOPE_CHAIN_SLOT, UndefinedValue());
-}
-
-inline void
-StaticBlockObject::initEnclosingStaticScope(JSObject *obj)
-{
-    JS_ASSERT(getReservedSlot(SCOPE_CHAIN_SLOT).isUndefined());
-    setReservedSlot(SCOPE_CHAIN_SLOT, ObjectOrNullValue(obj));
-}
-
-inline void
-StaticBlockObject::setStackDepth(uint32_t depth)
-{
-    JS_ASSERT(getReservedSlot(DEPTH_SLOT).isUndefined());
-    initReservedSlot(DEPTH_SLOT, PrivateUint32Value(depth));
-}
-
-inline void
-StaticBlockObject::setDefinitionParseNode(unsigned i, frontend::Definition *def)
-{
-    JS_ASSERT(slotValue(i).isUndefined());
-    setSlotValue(i, PrivateValue(def));
-}
-
-inline void
-StaticBlockObject::setAliased(unsigned i, bool aliased)
-{
-    JS_ASSERT_IF(i > 0, slotValue(i-1).isBoolean());
-    setSlotValue(i, BooleanValue(aliased));
-    if (aliased && !needsClone()) {
-        setSlotValue(0, MagicValue(JS_BLOCK_NEEDS_CLONE));
-        JS_ASSERT(needsClone());
-    }
-}
-
-inline void
-ClonedBlockObject::setVar(unsigned i, const Value &v, MaybeCheckAliasing checkAliasing)
-{
-    JS_ASSERT_IF(checkAliasing, staticBlock().isAliased(i));
-    setSlotValue(i, v);
-}
-
 }  /* namespace js */
 
 #endif /* vm_ScopeObject_inl_h */
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -161,16 +161,24 @@ js::ScopeCoordinateFunctionScript(JSCont
     }
     if (ssi.type() != StaticScopeIter::FUNCTION)
         return NULL;
     return ssi.funScript();
 }
 
 /*****************************************************************************/
 
+inline void
+ScopeObject::setEnclosingScope(HandleObject obj)
+{
+    JS_ASSERT_IF(obj->is<CallObject>() || obj->is<DeclEnvObject>() || obj->is<BlockObject>(),
+                 obj->isDelegate());
+    setFixedSlot(SCOPE_CHAIN_SLOT, ObjectValue(*obj));
+}
+
 /*
  * Construct a bare-bones call object given a shape, type, and slots pointer.
  * The call object must be further initialized to be usable.
  */
 CallObject *
 CallObject::create(JSContext *cx, HandleScript script, HandleShape shape, HandleTypeObject type, HeapSlot *slots)
 {
     gc::AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots());
--- a/js/src/vm/ScopeObject.h
+++ b/js/src/vm/ScopeObject.h
@@ -165,17 +165,18 @@ class ScopeObject : public JSObject
     /*
      * Since every scope chain terminates with a global object and GlobalObject
      * does not derive ScopeObject (it has a completely different layout), the
      * enclosing scope of a ScopeObject is necessarily non-null.
      */
     inline JSObject &enclosingScope() const {
         return getReservedSlot(SCOPE_CHAIN_SLOT).toObject();
     }
-    void setEnclosingScope(HandleObject obj);
+
+    inline void setEnclosingScope(HandleObject obj);
 
     /*
      * Get or set an aliased variable contained in this scope. Unaliased
      * variables should instead access the StackFrame. Aliased variable access
      * is primarily made through JOF_SCOPECOORD ops which is why these members
      * take a ScopeCoordinate instead of just the slot index.
      */
     inline const Value &aliasedVar(ScopeCoordinate sc);
@@ -337,17 +338,19 @@ class BlockObject : public NestedScopeOb
     }
 
   protected:
     /* Blocks contain an object slot for each slot i: 0 <= i < slotCount. */
     const Value &slotValue(unsigned i) {
         return getSlotRef(RESERVED_SLOTS + i);
     }
 
-    inline void setSlotValue(unsigned i, const Value &v);
+    void setSlotValue(unsigned i, const Value &v) {
+        setSlot(RESERVED_SLOTS + i, v);
+    }
 };
 
 class StaticBlockObject : public BlockObject
 {
   public:
     static StaticBlockObject *create(ExclusiveContext *cx);
 
     /* See StaticScopeIter comment. */
@@ -383,38 +386,62 @@ class StaticBlockObject : public BlockOb
      */
     bool needsClone() {
         return !slotValue(0).isFalse();
     }
 
     /* Frontend-only functions ***********************************************/
 
     /* Initialization functions for above fields. */
-    void setAliased(unsigned i, bool aliased);
-    void setStackDepth(uint32_t depth);
-    void initEnclosingStaticScope(JSObject *obj);
+    void setAliased(unsigned i, bool aliased) {
+        JS_ASSERT_IF(i > 0, slotValue(i-1).isBoolean());
+        setSlotValue(i, BooleanValue(aliased));
+        if (aliased && !needsClone()) {
+            setSlotValue(0, MagicValue(JS_BLOCK_NEEDS_CLONE));
+            JS_ASSERT(needsClone());
+        }
+    }
+
+    void setStackDepth(uint32_t depth) {
+        JS_ASSERT(getReservedSlot(DEPTH_SLOT).isUndefined());
+        initReservedSlot(DEPTH_SLOT, PrivateUint32Value(depth));
+    }
+
+    void initEnclosingStaticScope(JSObject *obj) {
+        JS_ASSERT(getReservedSlot(SCOPE_CHAIN_SLOT).isUndefined());
+        setReservedSlot(SCOPE_CHAIN_SLOT, ObjectOrNullValue(obj));
+    }
 
     /*
      * Frontend compilation temporarily uses the object's slots to link
      * a let var to its associated Definition parse node.
      */
-    void setDefinitionParseNode(unsigned i, frontend::Definition *def);
+    void setDefinitionParseNode(unsigned i, frontend::Definition *def) {
+        JS_ASSERT(slotValue(i).isUndefined());
+        setSlotValue(i, PrivateValue(def));
+    }
+
     frontend::Definition *maybeDefinitionParseNode(unsigned i) {
         Value v = slotValue(i);
         return v.isUndefined() ? NULL : reinterpret_cast<frontend::Definition *>(v.toPrivate());
     }
 
     /*
      * The parser uses 'enclosingBlock' as the prev-link in the pc->blockChain
      * stack. Note: in the case of hoisting, this prev-link will not ultimately
      * be the same as enclosingBlock, initEnclosingStaticScope must be called
      * separately in the emitter. 'reset' is just for asserting stackiness.
      */
-    void initPrevBlockChainFromParser(StaticBlockObject *prev);
-    void resetPrevBlockChainFromParser();
+    void initPrevBlockChainFromParser(StaticBlockObject *prev) {
+        setReservedSlot(SCOPE_CHAIN_SLOT, ObjectOrNullValue(prev));
+    }
+
+    void resetPrevBlockChainFromParser() {
+        setReservedSlot(SCOPE_CHAIN_SLOT, UndefinedValue());
+    }
 
     static Shape *addVar(ExclusiveContext *cx, Handle<StaticBlockObject*> block, HandleId id,
                          int index, bool *redeclared);
 };
 
 class ClonedBlockObject : public BlockObject
 {
   public:
@@ -427,17 +454,20 @@ class ClonedBlockObject : public BlockOb
     }
 
     /* Assuming 'put' has been called, return the value of the ith let var. */
     const Value &var(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) {
         JS_ASSERT_IF(checkAliasing, staticBlock().isAliased(i));
         return slotValue(i);
     }
 
-    void setVar(unsigned i, const Value &v, MaybeCheckAliasing checkAliasing = CHECK_ALIASING);
+    void setVar(unsigned i, const Value &v, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) {
+        JS_ASSERT_IF(checkAliasing, staticBlock().isAliased(i));
+        setSlotValue(i, v);
+    }
 
     /* Copy in all the unaliased formals and locals. */
     void copyUnaliasedValues(AbstractFramePtr frame);
 };
 
 template<XDRMode mode>
 bool
 XDRStaticBlockObject(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript script,
--- a/js/src/vm/Stack-inl.h
+++ b/js/src/vm/Stack-inl.h
@@ -11,16 +11,18 @@
 
 #include "mozilla/PodOperations.h"
 
 #include "jscntxt.h"
 
 #include "jit/BaselineFrame.h"
 #include "vm/ScopeObject.h"
 
+#include "jsobjinlines.h"
+
 #include "jit/BaselineFrame-inl.h"
 #include "jit/IonFrameIterator-inl.h"
 
 namespace js {
 
 /*
  * We cache name lookup results only for the global object or for native
  * non-global objects without prototype or with prototype that never mutates,
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -35,18 +35,16 @@
 #include "vm/Interpreter.h"
 #include "vm/NumericConversions.h"
 #include "vm/WrapperObject.h"
 
 #include "jsatominlines.h"
 #include "jsinferinlines.h"
 #include "jsobjinlines.h"
 
-#include "vm/GlobalObject-inl.h"
-
 #if JS_USE_NEW_OBJECT_REPRESENTATION
 // See the comment above OldObjectRepresentationHack.
 #  error "TypedArray support for new object representation unimplemented."
 #endif
 
 using namespace js;
 using namespace js::gc;
 using namespace js::types;
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -87,16 +87,17 @@
 
 #include <math.h>
 #include <stdarg.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include "xpcpublic.h"
+#include "js/Tracer.h"
 #include "pldhash.h"
 #include "nscore.h"
 #include "nsXPCOM.h"
 #include "nsAutoPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsDebug.h"
 #include "nsISupports.h"
 #include "nsIServiceManager.h"
--- a/layout/style/nsTransitionManager.cpp
+++ b/layout/style/nsTransitionManager.cpp
@@ -129,17 +129,18 @@ ElementPropertyTransition::IsRunningAt(T
          mStartTime <= aTime &&
          aTime < mStartTime + mDuration;
 }
 
 bool
 ElementTransitions::HasAnimationOfProperty(nsCSSProperty aProperty) const
 {
   for (uint32_t tranIdx = mPropertyTransitions.Length(); tranIdx-- != 0; ) {
-    if (aProperty == mPropertyTransitions[tranIdx].mProperty) {
+    const ElementPropertyTransition& pt = mPropertyTransitions[tranIdx];
+    if (aProperty == pt.mProperty && !pt.IsRemovedSentinel()) {
       return true;
     }
   }
   return false;
 }
 
 bool
 ElementTransitions::CanPerformOnCompositorThread(CanAnimateFlags aFlags) const
--- a/layout/style/test/test_transitions_per_property.html
+++ b/layout/style/test/test_transitions_per_property.html
@@ -581,16 +581,33 @@ for (prop in supported_properties) {
   // Test that prop is in the property database.
   ok(prop in gCSSProperties, "property " + prop + " in gCSSProperties");
 
   // Test that the entry has at least one test function.
   ok(supported_properties[prop].length > 0,
      "property " + prop + " must have at least one test function");
 }
 
+// Return a consistent sampling of |count| values out of |array|.
+function sample_array(array, count) {
+  if (count <= 0) {
+    ok(false, "unexpected count");
+    return [];
+  }
+  var ratio = array.length / count;
+  if (ratio <= 1) {
+    return array;
+  }
+  var result = new Array(count);
+  for (var i = 0; i < count; ++i) {
+    result[i] = array[Math.floor(i * ratio)];
+  }
+  return result;
+}
+
 // Test that transitions don't do anything (i.e., aren't supported) on
 // the properties not in our test list above (and not transition
 // properties themselves).
 for (prop in gCSSProperties) {
   var info = gCSSProperties[prop];
   if (!(prop in supported_properties) &&
       info.type != CSS_TYPE_TRUE_SHORTHAND &&
       !("alias_for" in info) &&
@@ -601,16 +618,36 @@ for (prop in gCSSProperties) {
     if ("prerequisites" in info) {
       var prereqs = info.prerequisites;
       for (var prereq in prereqs) {
         div.style.setProperty(prereq, prereqs[prereq], "");
       }
     }
 
     var all_values = info.initial_values.concat(info.other_values);
+
+    if (all_values.length > 50) {
+      // Since we're using an O(N^2) algorithm here, reduce the list of
+      // values that we want to test.  (This test is really only testing
+      // that somebody didn't make a property animatable without
+      // modifying this test.  The odds of somebody doing that without
+      // making at least one of the many pairs of values we have left
+      // animatable seems pretty low, at least relative to the chance
+      // that any pair of the values listed in property_database.js is
+      // animatable.)
+      //
+      // That said, we still try to use all of the start of the list on
+      // the assumption that the more basic values are likely to be at
+      // the beginning of the list.
+      all_values = [].concat(info.initial_values.slice(0,2),
+                             sample_array(info.initial_values.slice(2), 6),
+                             info.other_values.slice(0, 10),
+                             sample_array(info.other_values.slice(10), 40));
+    }
+
     var all_computed = [];
     for (var idx in all_values) {
       var val = all_values[idx];
       div.style.setProperty(prop, val, "");
       all_computed.push(cs.getPropertyValue(prop));
     }
     div.style.removeProperty(prop);
 
--- a/testing/mochitest/browser-test.js
+++ b/testing/mochitest/browser-test.js
@@ -83,16 +83,17 @@ Tester.prototype = {
   Promise: null,
 
   repeat: 0,
   runUntilFailure: false,
   checker: null,
   currentTestIndex: -1,
   lastStartTime: null,
   openedWindows: null,
+  lastAssertionCount: 0,
 
   get currentTest() {
     return this.tests[this.currentTestIndex];
   },
   get done() {
     return this.currentTestIndex == this.tests.length - 1;
   },
 
@@ -310,16 +311,49 @@ Tester.prototype = {
       // Notify a long running test problem if it didn't end up in a timeout.
       if (this.currentTest.unexpectedTimeouts && !this.currentTest.timedOut) {
         let msg = "This test exceeded the timeout threshold. It should be " +
                   "rewritten or split up. If that's not possible, use " +
                   "requestLongerTimeout(N), but only as a last resort.";
         this.currentTest.addResult(new testResult(false, msg, "", false));
       }
 
+      // If we're in a debug build, check assertion counts.  This code
+      // is similar to the code in TestRunner.testUnloaded in
+      // TestRunner.js used for all other types of mochitests.
+      let debugsvc = Cc["@mozilla.org/xpcom/debug;1"].getService(Ci.nsIDebug2);
+      if (debugsvc.isDebugBuild) {
+        let newAssertionCount = debugsvc.assertionCount;
+        let numAsserts = newAssertionCount - this.lastAssertionCount;
+        this.lastAssertionCount = newAssertionCount;
+
+        let max = testScope.__expectedMaxAsserts;
+        let min = testScope.__expectedMinAsserts;
+        if (numAsserts > max) {
+          let msg = "Assertion count " + numAsserts +
+                    " is greater than expected range " +
+                    min + "-" + max + " assertions.";
+          // TEST-UNEXPECTED-FAIL (TEMPORARILY TEST-KNOWN-FAIL)
+          //this.currentTest.addResult(new testResult(false, msg, "", false));
+          this.currentTest.addResult(new testResult(true, msg, "", true));
+        } else if (numAsserts < min) {
+          let msg = "Assertion count " + numAsserts +
+                    " is less than expected range " +
+                    min + "-" + max + " assertions.";
+          // TEST-UNEXPECTED-PASS
+          this.currentTest.addResult(new testResult(false, msg, "", true));
+        } else if (numAsserts > 0) {
+          let msg = "Assertion count " + numAsserts +
+                    " is within expected range " +
+                    min + "-" + max + " assertions.";
+          // TEST-KNOWN-FAIL
+          this.currentTest.addResult(new testResult(true, msg, "", true));
+        }
+      }
+
       // Note the test run time
       let time = Date.now() - this.lastStartTime;
       this.dumper.dump("INFO TEST-END | " + this.currentTest.path + " | finished in " + time + "ms\n");
       this.currentTest.setDuration(time);
 
       testScope.destroy();
       this.currentTest.scope = null;
     }
@@ -401,17 +435,17 @@ Tester.prototype = {
     // Import utils in the test scope.
     this.currentTest.scope.EventUtils = this.EventUtils;
     this.currentTest.scope.SimpleTest = this.SimpleTest;
     this.currentTest.scope.gTestPath = this.currentTest.path;
     this.currentTest.scope.Task = this.Task;
     this.currentTest.scope.Promise = this.Promise;
 
     // Override SimpleTest methods with ours.
-    ["ok", "is", "isnot", "ise", "todo", "todo_is", "todo_isnot", "info"].forEach(function(m) {
+    ["ok", "is", "isnot", "ise", "todo", "todo_is", "todo_isnot", "info", "expectAssertions"].forEach(function(m) {
       this.SimpleTest[m] = this[m];
     }, this.currentTest.scope);
 
     //load the tools to work with chrome .jar and remote
     try {
       this._scriptLoader.loadSubScript("chrome://mochikit/content/chrome-harness.js", this.currentTest.scope);
     } catch (ex) { /* no chrome-harness tools */ }
 
@@ -678,16 +712,30 @@ function testScope(aTester, aTest) {
   this.expectUncaughtException = function test_expectUncaughtException(aExpecting) {
     self.SimpleTest.expectUncaughtException(aExpecting);
   };
 
   this.ignoreAllUncaughtExceptions = function test_ignoreAllUncaughtExceptions(aIgnoring) {
     self.SimpleTest.ignoreAllUncaughtExceptions(aIgnoring);
   };
 
+  this.expectAssertions = function test_expectAssertions(aMin, aMax) {
+    let min = aMin;
+    let max = aMax;
+    if (typeof(max) == "undefined") {
+      max = min;
+    }
+    if (typeof(min) != "number" || typeof(max) != "number" ||
+        min < 0 || max < min) {
+      throw "bad parameter to expectAssertions";
+    }
+    self.__expectedMinAsserts = min;
+    self.__expectedMaxAsserts = max;
+  };
+
   this.finish = function test_finish() {
     self.__done = true;
     if (self.__waitTimer) {
       self.executeSoon(function() {
         if (self.__done && self.__waitTimer) {
           clearTimeout(self.__waitTimer);
           self.__waitTimer = null;
           self.__tester.nextTest();
@@ -698,16 +746,18 @@ function testScope(aTester, aTest) {
 }
 testScope.prototype = {
   __done: true,
   __generator: null,
   __tasks: null,
   __waitTimer: null,
   __cleanupFunctions: [],
   __timeoutFactor: 1,
+  __expectedMinAsserts: 0,
+  __expectedMaxAsserts: 0,
 
   EventUtils: {},
   SimpleTest: {},
   Task: null,
   Promise: null,
 
   /**
    * Add a test function which is a Task function.
--- a/testing/mochitest/tests/SimpleTest/TestRunner.js
+++ b/testing/mochitest/tests/SimpleTest/TestRunner.js
@@ -457,16 +457,19 @@ TestRunner.testFinished = function(tests
 
     SpecialPowers.executeAfterFlushingMessageQueue(function() {
         cleanUpCrashDumpFiles();
         SpecialPowers.flushPermissions(function () { SpecialPowers.flushPrefEnv(runNextTest); });
     });
 };
 
 TestRunner.testUnloaded = function() {
+    // If we're in a debug build, check assertion counts.  This code is
+    // similar to the code in Tester_nextTest in browser-test.js used
+    // for browser-chrome mochitests.
     if (SpecialPowers.isDebugBuild) {
         var newAssertionCount = SpecialPowers.assertionCount();
         var numAsserts = newAssertionCount - TestRunner._lastAssertionCount;
         TestRunner._lastAssertionCount = newAssertionCount;
 
         var url = TestRunner._urls[TestRunner._currentTest];
         var max = TestRunner._expectedMaxAsserts;
         var min = TestRunner._expectedMinAsserts;