Bug 1024250; r=billm
authorTerrence Cole <terrence@mozilla.com>
Mon, 28 Jul 2014 10:16:56 -0700
changeset 220603 2986d8d21bc5997f063e1c7a2f7cf1e17de8e602
parent 220602 c6c02127a3cea5d11aefbb054038b2d3951e3f99
child 220604 e7682eb7cd19b43964a3fabeda6d54f33f56be35
push id3979
push userraliiev@mozilla.com
push dateMon, 13 Oct 2014 16:35:44 +0000
treeherdermozilla-beta@30f2cc610691 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbillm
bugs1024250
milestone34.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
Bug 1024250; r=billm
js/src/gc/Barrier.h
js/src/gc/Marking.cpp
js/src/jscompartment.h
js/src/jscompartmentinlines.h
js/src/jsgc.cpp
js/src/jsgc.h
js/src/jsinfer.cpp
js/src/jsobjinlines.h
js/src/jspropertytree.cpp
js/src/jsweakmap.cpp
js/src/jsweakmap.h
js/src/vm/Debugger.cpp
js/src/vm/SavedStacks.cpp
js/src/vm/SavedStacks.h
js/src/vm/Shape.cpp
js/src/vm/Stack.cpp
--- a/js/src/gc/Barrier.h
+++ b/js/src/gc/Barrier.h
@@ -146,38 +146,92 @@
  * RelocatablePtr         provides pre- and post-barriers and is relocatable
  *
  * These classes are designed to be used by the internals of the JS engine.
  * Barriers designed to be used externally are provided in
  * js/public/RootingAPI.h.
  */
 
 class JSAtom;
+struct JSCompartment;
 class JSFlatString;
 class JSLinearString;
 
+namespace JS {
+class Symbol;
+}
+
 namespace js {
 
+class ArgumentsObject;
+class ArrayBufferObject;
+class ArrayBufferViewObject;
+class SharedArrayBufferObject;
+class BaseShape;
+class DebugScopeObject;
+class GlobalObject;
+class LazyScript;
+class Nursery;
+class ObjectImpl;
 class PropertyName;
+class SavedFrame;
+class ScopeObject;
+class ScriptSourceObject;
+class Shape;
+class UnownedBaseShape;
+
+namespace types {
+struct TypeObject;
+}
+
+namespace jit {
+class JitCode;
+}
 
 #ifdef DEBUG
 bool
 RuntimeFromMainThreadIsHeapMajorCollecting(JS::shadow::Zone *shadowZone);
 
 // Barriers can't be triggered during backend Ion compilation, which may run on
 // a helper thread.
 bool
 CurrentThreadIsIonCompiling();
 #endif
 
 bool
 StringIsPermanentAtom(JSString *str);
 
 namespace gc {
 
+template <typename T> struct MapTypeToTraceKind {};
+template <> struct MapTypeToTraceKind<ArgumentsObject>  { static const JSGCTraceKind kind = JSTRACE_OBJECT; };
+template <> struct MapTypeToTraceKind<ArrayBufferObject>{ static const JSGCTraceKind kind = JSTRACE_OBJECT; };
+template <> struct MapTypeToTraceKind<ArrayBufferViewObject>{ static const JSGCTraceKind kind = JSTRACE_OBJECT; };
+template <> struct MapTypeToTraceKind<BaseShape>        { static const JSGCTraceKind kind = JSTRACE_BASE_SHAPE; };
+template <> struct MapTypeToTraceKind<DebugScopeObject> { static const JSGCTraceKind kind = JSTRACE_OBJECT; };
+template <> struct MapTypeToTraceKind<GlobalObject>     { static const JSGCTraceKind kind = JSTRACE_OBJECT; };
+template <> struct MapTypeToTraceKind<JS::Symbol>       { static const JSGCTraceKind kind = JSTRACE_SYMBOL; };
+template <> struct MapTypeToTraceKind<JSAtom>           { static const JSGCTraceKind kind = JSTRACE_STRING; };
+template <> struct MapTypeToTraceKind<JSFlatString>     { static const JSGCTraceKind kind = JSTRACE_STRING; };
+template <> struct MapTypeToTraceKind<JSFunction>       { static const JSGCTraceKind kind = JSTRACE_OBJECT; };
+template <> struct MapTypeToTraceKind<JSLinearString>   { static const JSGCTraceKind kind = JSTRACE_STRING; };
+template <> struct MapTypeToTraceKind<JSObject>         { static const JSGCTraceKind kind = JSTRACE_OBJECT; };
+template <> struct MapTypeToTraceKind<JSScript>         { static const JSGCTraceKind kind = JSTRACE_SCRIPT; };
+template <> struct MapTypeToTraceKind<JSString>         { static const JSGCTraceKind kind = JSTRACE_STRING; };
+template <> struct MapTypeToTraceKind<LazyScript>       { static const JSGCTraceKind kind = JSTRACE_LAZY_SCRIPT; };
+template <> struct MapTypeToTraceKind<ObjectImpl>       { static const JSGCTraceKind kind = JSTRACE_OBJECT; };
+template <> struct MapTypeToTraceKind<PropertyName>     { static const JSGCTraceKind kind = JSTRACE_STRING; };
+template <> struct MapTypeToTraceKind<SavedFrame>       { static const JSGCTraceKind kind = JSTRACE_OBJECT; };
+template <> struct MapTypeToTraceKind<ScopeObject>      { static const JSGCTraceKind kind = JSTRACE_OBJECT; };
+template <> struct MapTypeToTraceKind<Shape>            { static const JSGCTraceKind kind = JSTRACE_SHAPE; };
+template <> struct MapTypeToTraceKind<SharedArrayBufferObject>{ static const JSGCTraceKind kind = JSTRACE_OBJECT; };
+template <> struct MapTypeToTraceKind<UnownedBaseShape> { static const JSGCTraceKind kind = JSTRACE_BASE_SHAPE; };
+template <> struct MapTypeToTraceKind<jit::JitCode>     { static const JSGCTraceKind kind = JSTRACE_JITCODE; };
+template <> struct MapTypeToTraceKind<types::TypeObject>{ static const JSGCTraceKind kind = JSTRACE_TYPE_OBJECT; };
+
 template <typename T>
 void
 MarkUnbarriered(JSTracer *trc, T **thingp, const char *name);
 
 // Direct value access used by the write barriers and the jits.
 void
 MarkValueUnbarriered(JSTracer *trc, Value *v, const char *name);
 
@@ -201,23 +255,26 @@ class BarrieredCell : public gc::Cell
     MOZ_ALWAYS_INLINE JS::Zone *zoneFromAnyThread() const { return tenuredZoneFromAnyThread(); }
     MOZ_ALWAYS_INLINE JS::shadow::Zone *shadowZoneFromAnyThread() const {
         return JS::shadow::Zone::asShadowZone(zoneFromAnyThread());
     }
 
     static MOZ_ALWAYS_INLINE void readBarrier(T *thing) {
 #ifdef JSGC_INCREMENTAL
         JS_ASSERT(!CurrentThreadIsIonCompiling());
+        JS_ASSERT(!T::isNullLike(thing));
         JS::shadow::Zone *shadowZone = thing->shadowZoneFromAnyThread();
         if (shadowZone->needsIncrementalBarrier()) {
             MOZ_ASSERT(!RuntimeFromMainThreadIsHeapMajorCollecting(shadowZone));
             T *tmp = thing;
             js::gc::MarkUnbarriered<T>(shadowZone->barrierTracer(), &tmp, "read barrier");
             JS_ASSERT(tmp == thing);
         }
+        if (JS::GCThingIsMarkedGray(thing))
+            JS::UnmarkGrayGCThingRecursively(thing, MapTypeToTraceKind<T>::kind);
 #endif
     }
 
     static MOZ_ALWAYS_INLINE bool needWriteBarrierPre(JS::Zone *zone) {
 #ifdef JSGC_INCREMENTAL
         return JS::shadow::Zone::asShadowZone(zone)->needsIncrementalBarrier();
 #else
         return false;
@@ -766,16 +823,20 @@ class ReadBarriered
 
     T get() const {
         if (!InternalGCMethods<T>::isMarkable(value))
             return GCMethods<T>::initial();
         InternalGCMethods<T>::readBarrier(value);
         return value;
     }
 
+    T unbarrieredGet() const {
+        return value;
+    }
+
     operator T() const { return get(); }
 
     T &operator*() const { return *get(); }
     T operator->() const { return get(); }
 
     T *unsafeGet() { return &value; }
     T const * unsafeGet() const { return &value; }
 
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -1137,17 +1137,17 @@ ScanBaseShape(GCMarker *gcmarker, BaseSh
     if (base->hasGetterObject())
         MaybePushMarkStackBetweenSlices(gcmarker, base->getterObject());
 
     if (base->hasSetterObject())
         MaybePushMarkStackBetweenSlices(gcmarker, base->setterObject());
 
     if (JSObject *parent = base->getObjectParent()) {
         MaybePushMarkStackBetweenSlices(gcmarker, parent);
-    } else if (GlobalObject *global = base->compartment()->maybeGlobal()) {
+    } else if (GlobalObject *global = base->compartment()->unsafeUnbarrieredMaybeGlobal()) {
         PushMarkStack(gcmarker, global);
     }
 
     if (JSObject *metadata = base->getObjectMetadata())
         MaybePushMarkStackBetweenSlices(gcmarker, metadata);
 
     /*
      * All children of the owned base shape are consistent with its
@@ -1956,19 +1956,22 @@ UnmarkGrayChildren(JSTracer *trc, void *
         childTracer.previousShape = nullptr;
     } while (thing);
     tracer->unmarkedAny |= childTracer.unmarkedAny;
 }
 
 JS_FRIEND_API(bool)
 JS::UnmarkGrayGCThingRecursively(void *thing, JSGCTraceKind kind)
 {
-    JS_ASSERT(kind != JSTRACE_SHAPE);
+    JSRuntime *rt = static_cast<Cell *>(thing)->runtimeFromMainThread();
 
-    JSRuntime *rt = static_cast<Cell *>(thing)->runtimeFromMainThread();
+    // When the ReadBarriered type is used in a HashTable, it is difficult or
+    // impossible to suppress the implicit cast operator while iterating for GC.
+    if (rt->isHeapBusy())
+        return false;
 
     bool unmarkedArg = false;
     if (!IsInsideNursery(static_cast<Cell *>(thing))) {
         if (!JS::GCThingIsMarkedGray(thing))
             return false;
 
         UnmarkGrayGCThing(thing);
         unmarkedArg = true;
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -178,16 +178,19 @@ struct JSCompartment
      * compartment lives on.
      *
      * In contrast, JSObject::global() is infallible because marking a JSObject
      * always marks its global as well.
      * TODO: add infallible JSScript::global()
      */
     inline js::GlobalObject *maybeGlobal() const;
 
+    /* An unbarriered getter for use while tracing. */
+    inline js::GlobalObject *unsafeUnbarrieredMaybeGlobal() const;
+
     inline void initGlobal(js::GlobalObject &global);
 
   public:
     /*
      * Moves all data from the allocator |workerAllocator|, which was
      * in use by a parallel worker, into the compartment's main
      * allocator.  This is used at the end of a parallel section.
      */
--- a/js/src/jscompartmentinlines.h
+++ b/js/src/jscompartmentinlines.h
@@ -23,16 +23,22 @@ JSCompartment::initGlobal(js::GlobalObje
 
 js::GlobalObject *
 JSCompartment::maybeGlobal() const
 {
     JS_ASSERT_IF(global_, global_->compartment() == this);
     return global_;
 }
 
+js::GlobalObject *
+JSCompartment::unsafeUnbarrieredMaybeGlobal() const
+{
+    return *global_.unsafeGet();
+}
+
 js::AutoCompartment::AutoCompartment(ExclusiveContext *cx, JSObject *target)
   : cx_(cx),
     origin_(cx->compartment_)
 {
     cx_->enterCompartment(target->compartment());
 }
 
 js::AutoCompartment::AutoCompartment(ExclusiveContext *cx, JSCompartment *target)
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -4515,17 +4515,17 @@ GCRuntime::endSweepPhase(JSGCInvocationK
 
 #ifdef DEBUG
     for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
         JS_ASSERT(!c->gcIncomingGrayPointers);
         JS_ASSERT(c->gcLiveArrayBuffers.empty());
 
         for (JSCompartment::WrapperEnum e(c); !e.empty(); e.popFront()) {
             if (e.front().key().kind != CrossCompartmentKey::StringWrapper)
-                AssertNotOnGrayList(&e.front().value().get().toObject());
+                AssertNotOnGrayList(&e.front().value().unbarrieredGet().toObject());
         }
     }
 #endif
 
     finishMarkingValidation();
 
     lastGCTime = currentTime;
 }
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -15,42 +15,18 @@
 
 #include "jslock.h"
 #include "jsobj.h"
 
 #include "js/GCAPI.h"
 #include "js/SliceBudget.h"
 #include "js/Vector.h"
 
-class JSAtom;
-struct JSCompartment;
-class JSFlatString;
-class JSLinearString;
-
-namespace JS {
-class Symbol;
-} /* namespace JS */
-
 namespace js {
 
-class ArgumentsObject;
-class ArrayBufferObject;
-class ArrayBufferViewObject;
-class SharedArrayBufferObject;
-class BaseShape;
-class DebugScopeObject;
-class GlobalObject;
-class LazyScript;
-class Nursery;
-class PropertyName;
-class SavedFrame;
-class ScopeObject;
-class Shape;
-class UnownedBaseShape;
-
 namespace gc {
 class ForkJoinNursery;
 }
 
 unsigned GetCPUCount();
 
 enum HeapState {
     Idle,             // doing nothing with the GC heap
@@ -99,42 +75,16 @@ MapAllocToTraceKind(AllocKind kind)
         JSTRACE_STRING,     /* FINALIZE_EXTERNAL_STRING */
         JSTRACE_SYMBOL,     /* FINALIZE_SYMBOL */
         JSTRACE_JITCODE,    /* FINALIZE_JITCODE */
     };
     JS_STATIC_ASSERT(JS_ARRAY_LENGTH(map) == FINALIZE_LIMIT);
     return map[kind];
 }
 
-template <typename T> struct MapTypeToTraceKind {};
-template <> struct MapTypeToTraceKind<ObjectImpl>       { static const JSGCTraceKind kind = JSTRACE_OBJECT; };
-template <> struct MapTypeToTraceKind<JSObject>         { static const JSGCTraceKind kind = JSTRACE_OBJECT; };
-template <> struct MapTypeToTraceKind<JSFunction>       { static const JSGCTraceKind kind = JSTRACE_OBJECT; };
-template <> struct MapTypeToTraceKind<ArgumentsObject>  { static const JSGCTraceKind kind = JSTRACE_OBJECT; };
-template <> struct MapTypeToTraceKind<ArrayBufferObject>{ static const JSGCTraceKind kind = JSTRACE_OBJECT; };
-template <> struct MapTypeToTraceKind<ArrayBufferViewObject>{ static const JSGCTraceKind kind = JSTRACE_OBJECT; };
-template <> struct MapTypeToTraceKind<SharedArrayBufferObject>{ static const JSGCTraceKind kind = JSTRACE_OBJECT; };
-template <> struct MapTypeToTraceKind<DebugScopeObject> { static const JSGCTraceKind kind = JSTRACE_OBJECT; };
-template <> struct MapTypeToTraceKind<GlobalObject>     { static const JSGCTraceKind kind = JSTRACE_OBJECT; };
-template <> struct MapTypeToTraceKind<ScopeObject>      { static const JSGCTraceKind kind = JSTRACE_OBJECT; };
-template <> struct MapTypeToTraceKind<SavedFrame>       { static const JSGCTraceKind kind = JSTRACE_OBJECT; };
-template <> struct MapTypeToTraceKind<JSScript>         { static const JSGCTraceKind kind = JSTRACE_SCRIPT; };
-template <> struct MapTypeToTraceKind<LazyScript>       { static const JSGCTraceKind kind = JSTRACE_LAZY_SCRIPT; };
-template <> struct MapTypeToTraceKind<Shape>            { static const JSGCTraceKind kind = JSTRACE_SHAPE; };
-template <> struct MapTypeToTraceKind<BaseShape>        { static const JSGCTraceKind kind = JSTRACE_BASE_SHAPE; };
-template <> struct MapTypeToTraceKind<UnownedBaseShape> { static const JSGCTraceKind kind = JSTRACE_BASE_SHAPE; };
-template <> struct MapTypeToTraceKind<types::TypeObject>{ static const JSGCTraceKind kind = JSTRACE_TYPE_OBJECT; };
-template <> struct MapTypeToTraceKind<JSAtom>           { static const JSGCTraceKind kind = JSTRACE_STRING; };
-template <> struct MapTypeToTraceKind<JSString>         { static const JSGCTraceKind kind = JSTRACE_STRING; };
-template <> struct MapTypeToTraceKind<JSFlatString>     { static const JSGCTraceKind kind = JSTRACE_STRING; };
-template <> struct MapTypeToTraceKind<JSLinearString>   { static const JSGCTraceKind kind = JSTRACE_STRING; };
-template <> struct MapTypeToTraceKind<JS::Symbol>       { static const JSGCTraceKind kind = JSTRACE_SYMBOL; }; 
-template <> struct MapTypeToTraceKind<PropertyName>     { static const JSGCTraceKind kind = JSTRACE_STRING; };
-template <> struct MapTypeToTraceKind<jit::JitCode>     { static const JSGCTraceKind kind = JSTRACE_JITCODE; };
-
 /* Return a printable string for the given kind, for diagnostic purposes. */
 const char *
 TraceKindAsAscii(JSGCTraceKind kind);
 
 /* Map from C++ type to finalize kind. JSObject does not have a 1:1 mapping, so must use Arena::thingSize. */
 template <typename T> struct MapTypeToFinalizeKind {};
 template <> struct MapTypeToFinalizeKind<JSScript>          { static const AllocKind kind = FINALIZE_SCRIPT; };
 template <> struct MapTypeToFinalizeKind<LazyScript>        { static const AllocKind kind = FINALIZE_LAZY_SCRIPT; };
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -2317,17 +2317,17 @@ TypeCompartment::markSetsUnknown(JSConte
         for (unsigned i = 0; i < count; i++) {
             Property *prop = object->getProperty(i);
             if (prop && prop->types.hasType(Type::ObjectType(target)))
                 prop->types.addType(cx, Type::AnyObjectType());
         }
     }
 
     for (gc::ZoneCellIter i(cx->zone(), gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
-        RootedScript script(cx, i.get<JSScript>());
+        JSScript *script = i.get<JSScript>();
         if (script->types) {
             unsigned count = TypeScript::NumTypeSets(script);
             StackTypeSet *typeArray = script->types->typeArray();
             for (unsigned i = 0; i < count; i++) {
                 if (typeArray[i].hasType(Type::ObjectType(target)))
                     typeArray[i].addType(cx, Type::AnyObjectType());
             }
         }
@@ -4117,26 +4117,26 @@ TypeCompartment::sweep(FreeOp *fop)
     if (arrayTypeTable) {
         for (ArrayTypeTable::Enum e(*arrayTypeTable); !e.empty(); e.popFront()) {
             const ArrayTableKey &key = e.front().key();
             JS_ASSERT(key.type.isUnknown() || !key.type.isSingleObject());
 
             bool remove = false;
             TypeObject *typeObject = nullptr;
             if (!key.type.isUnknown() && key.type.isTypeObject()) {
-                typeObject = key.type.typeObject();
+                typeObject = key.type.typeObjectNoBarrier();
                 if (IsTypeObjectAboutToBeFinalized(&typeObject))
                     remove = true;
             }
             if (IsTypeObjectAboutToBeFinalized(e.front().value().unsafeGet()))
                 remove = true;
 
             if (remove) {
                 e.removeFront();
-            } else if (typeObject && typeObject != key.type.typeObject()) {
+            } else if (typeObject && typeObject != key.type.typeObjectNoBarrier()) {
                 ArrayTableKey newKey;
                 newKey.type = Type::ObjectType(typeObject);
                 newKey.proto = key.proto;
                 e.rekeyFront(newKey);
             }
         }
     }
 
@@ -4155,20 +4155,20 @@ TypeCompartment::sweep(FreeOp *fop)
                     JSString *str = JSID_TO_STRING(key.properties[i]);
                     if (IsStringAboutToBeFinalized(&str))
                         remove = true;
                     JS_ASSERT(AtomToId((JSAtom *)str) == key.properties[i]);
                 }
                 JS_ASSERT(!entry.types[i].isSingleObject());
                 TypeObject *typeObject = nullptr;
                 if (entry.types[i].isTypeObject()) {
-                    typeObject = entry.types[i].typeObject();
+                    typeObject = entry.types[i].typeObjectNoBarrier();
                     if (IsTypeObjectAboutToBeFinalized(&typeObject))
                         remove = true;
-                    else if (typeObject != entry.types[i].typeObject())
+                    else if (typeObject != entry.types[i].typeObjectNoBarrier())
                         entry.types[i] = Type::ObjectType(typeObject);
                 }
             }
 
             if (remove) {
                 js_free(key.properties);
                 js_free(entry.types);
                 e.removeFront();
@@ -4198,17 +4198,17 @@ JSCompartment::sweepNewTypeObjectTable(T
     JS_ASSERT(zone()->isGCSweeping());
     if (table.initialized()) {
         for (TypeObjectWithNewScriptSet::Enum e(table); !e.empty(); e.popFront()) {
             TypeObjectWithNewScriptEntry entry = e.front();
             if (IsTypeObjectAboutToBeFinalized(entry.object.unsafeGet())) {
                 e.removeFront();
             } else if (entry.newFunction && IsObjectAboutToBeFinalized(&entry.newFunction)) {
                 e.removeFront();
-            } else if (entry.object != e.front().object) {
+            } else if (entry.object.unbarrieredGet() != e.front().object.unbarrieredGet()) {
                 TypeObjectWithNewScriptSet::Lookup lookup(entry.object->clasp(),
                                                           entry.object->proto(),
                                                           entry.newFunction);
                 e.rekeyFront(lookup, entry);
             }
         }
     }
 }
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -659,17 +659,23 @@ JSObject::getElementNoGC(JSContext *cx, 
 inline js::GlobalObject &
 JSObject::global() const
 {
 #ifdef DEBUG
     JSObject *obj = const_cast<JSObject *>(this);
     while (JSObject *parent = obj->getParent())
         obj = parent;
 #endif
-    return *compartment()->maybeGlobal();
+    /*
+     * The global is read-barriered so that it is kept live by access through
+     * the JSCompartment. When accessed through a JSObject, however, the global
+     * will be already be kept live by the black JSObject's parent pointer, so
+     * does not need to be read-barriered.
+     */
+    return *compartment()->unsafeUnbarrieredMaybeGlobal();
 }
 
 inline bool
 JSObject::isOwnGlobal() const
 {
     return &global() == this;
 }
 
--- a/js/src/jspropertytree.cpp
+++ b/js/src/jspropertytree.cpp
@@ -140,20 +140,20 @@ PropertyTree::getChild(ExclusiveContext 
      * getters or setters on the new object that is passed in as
      * |this| can significantly increase fan-out below the property
      * tree root -- see bug 335700 for details.
      */
     KidsPointer *kidp = &parent->kids;
     if (kidp->isShape()) {
         Shape *kid = kidp->toShape();
         if (kid->matches(unrootedChild))
-        existingShape = kid;
+            existingShape = kid;
     } else if (kidp->isHash()) {
         if (KidsHash::Ptr p = kidp->toHash()->lookup(unrootedChild))
-        existingShape = *p;
+            existingShape = *p;
     } else {
         /* If kidp->isNull(), we always insert. */
     }
 
 #ifdef JSGC_INCREMENTAL
     if (existingShape) {
         JS::Zone *zone = existingShape->zone();
         if (zone->needsIncrementalBarrier()) {
@@ -169,16 +169,18 @@ PropertyTree::getChild(ExclusiveContext 
         {
             /*
              * The shape we've found is unreachable and due to be finalized, so
              * remove our weak reference to it and don't use it.
              */
             JS_ASSERT(parent->isMarked());
             parent->removeChild(existingShape);
             existingShape = nullptr;
+        } else if (existingShape->isMarked(gc::GRAY)) {
+            JS::UnmarkGrayGCThingRecursively(existingShape, JSTRACE_SHAPE);
         }
     }
 #endif
 
     if (existingShape)
         return existingShape;
 
     RootedGeneric<StackShape*> child(cx, &unrootedChild);
--- a/js/src/jsweakmap.cpp
+++ b/js/src/jsweakmap.cpp
@@ -266,20 +266,16 @@ WeakMap_get_impl(JSContext *cx, CallArgs
         return false;
     }
     JSObject *key = GetKeyArg(cx, args);
     if (!key)
         return false;
 
     if (ObjectValueMap *map = args.thisv().toObject().as<WeakMapObject>().getMap()) {
         if (ObjectValueMap::Ptr ptr = map->lookup(key)) {
-            // Read barrier to prevent an incorrectly gray value from escaping the
-            // weak map. See the comment before UnmarkGrayChildren in gc/Marking.cpp
-            ExposeValueToActiveJS(ptr->value().get());
-
             args.rval().set(ptr->value());
             return true;
         }
     }
 
     args.rval().set((args.length() > 1) ? args[1] : UndefinedValue());
     return true;
 }
@@ -445,16 +441,17 @@ JS_NondeterministicGetWeakMapKeys(JSCont
     RootedObject arr(cx, NewDenseEmptyArray(cx));
     if (!arr)
         return false;
     ObjectValueMap *map = obj->as<WeakMapObject>().getMap();
     if (map) {
         // Prevent GC from mutating the weakmap while iterating.
         AutoSuppressGC suppress(cx);
         for (ObjectValueMap::Base::Range r = map->all(); !r.empty(); r.popFront()) {
+            JS::ExposeObjectToActiveJS(r.front().key());
             RootedObject key(cx, r.front().key());
             if (!cx->compartment()->wrap(cx, &key))
                 return false;
             if (!NewbornArrayPush(cx, arr, ObjectValue(*key)))
                 return false;
         }
     }
     ret.set(arr);
--- a/js/src/jsweakmap.h
+++ b/js/src/jsweakmap.h
@@ -106,30 +106,59 @@ template <class Key, class Value,
           class HashPolicy = DefaultHasher<Key> >
 class WeakMap : public HashMap<Key, Value, HashPolicy, RuntimeAllocPolicy>, public WeakMapBase
 {
   public:
     typedef HashMap<Key, Value, HashPolicy, RuntimeAllocPolicy> Base;
     typedef typename Base::Enum Enum;
     typedef typename Base::Lookup Lookup;
     typedef typename Base::Range Range;
+    typedef typename Base::Ptr Ptr;
+    typedef typename Base::AddPtr AddPtr;
 
     explicit WeakMap(JSContext *cx, JSObject *memOf = nullptr)
         : Base(cx->runtime()), WeakMapBase(memOf, cx->compartment()) { }
 
     bool init(uint32_t len = 16) {
         if (!Base::init(len))
             return false;
         next = compartment->gcWeakMapList;
         compartment->gcWeakMapList = this;
         marked = JS::IsIncrementalGCInProgress(compartment->runtimeFromMainThread());
         return true;
     }
 
+    // Overwritten to add a read barrier to prevent an incorrectly gray value
+    // from escaping the weak map. See the comment before UnmarkGrayChildren in
+    // gc/Marking.cpp
+    Ptr lookup(const Lookup &l) const {
+        Ptr p = Base::lookup(l);
+        if (p)
+            exposeGCThingToActiveJS(p->value());
+        return p;
+    }
+
+    AddPtr lookupForAdd(const Lookup &l) const {
+        AddPtr p = Base::lookupForAdd(l);
+        if (p)
+            exposeGCThingToActiveJS(p->value());
+        return p;
+    }
+
+    Ptr lookupWithDefault(const Key &k, const Value &defaultValue) {
+        Ptr p = Base::lookupWithDefault(k, defaultValue);
+        if (p)
+            exposeGCThingToActiveJS(p->value());
+        return p;
+    }
+
   private:
+    void exposeGCThingToActiveJS(const JS::Value &v) const { JS::ExposeValueToActiveJS(v); }
+    void exposeGCThingToActiveJS(JSObject *obj) const { JS::ExposeObjectToActiveJS(obj); }
+
     bool markValue(JSTracer *trc, Value *x) {
         if (gc::IsMarked(x))
             return false;
         gc::Mark(trc, x, "WeakMap entry value");
         JS_ASSERT(gc::IsMarked(x));
         return true;
     }
 
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -13,16 +13,17 @@
 #include "jshashutil.h"
 #include "jsnum.h"
 #include "jsobj.h"
 #include "jswrapper.h"
 
 #include "frontend/BytecodeCompiler.h"
 #include "gc/Marking.h"
 #include "jit/BaselineJIT.h"
+#include "js/GCAPI.h"
 #include "js/Vector.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/DebuggerMemory.h"
 #include "vm/WrapperObject.h"
 
 #include "jsgcinlines.h"
 #include "jsobjinlines.h"
 #include "jsopcodeinlines.h"
@@ -1421,20 +1422,23 @@ Debugger::slowPathOnNewGlobalObject(JSCo
     /*
      * Make a copy of the runtime's onNewGlobalObjectWatchers before running the
      * handlers. Since one Debugger's handler can disable another's, the list
      * can be mutated while we're walking it.
      */
     AutoObjectVector watchers(cx);
     for (JSCList *link = JS_LIST_HEAD(&cx->runtime()->onNewGlobalObjectWatchers);
          link != &cx->runtime()->onNewGlobalObjectWatchers;
-         link = JS_NEXT_LINK(link)) {
+         link = JS_NEXT_LINK(link))
+    {
         Debugger *dbg = fromOnNewGlobalObjectWatchersLink(link);
         JS_ASSERT(dbg->observesNewGlobalObject());
-        if (!watchers.append(dbg->object))
+        JSObject *obj = dbg->object;
+        JS::ExposeObjectToActiveJS(obj);
+        if (!watchers.append(obj))
             return;
     }
 
     JSTrapStatus status = JSTRAP_CONTINUE;
     RootedValue value(cx);
 
     for (size_t i = 0; i < watchers.length(); i++) {
         Debugger *dbg = fromJSObject(watchers[i]);
@@ -2669,26 +2673,32 @@ class Debugger::ScriptQuery {
         vector = v;
         oom = false;
         IterateScripts(cx->runtime(), singletonComp, this, considerScript);
         if (oom) {
             js_ReportOutOfMemory(cx);
             return false;
         }
 
+        /* We cannot touch the gray bits while isHeapBusy, so do this now. */
+        for (JSScript **i = vector->begin(); i != vector->end(); ++i)
+            JS::ExposeScriptToActiveJS(*i);
+
         /*
          * For most queries, we just accumulate results in 'vector' as we find
          * them. But if this is an 'innermost' query, then we've accumulated the
          * results in the 'innermostForCompartment' map. In that case, we now need to
          * walk that map and populate 'vector'.
          */
         if (innermost) {
             for (CompartmentToScriptMap::Range r = innermostForCompartment.all();
                  !r.empty();
-                 r.popFront()) {
+                 r.popFront())
+            {
+                JS::ExposeScriptToActiveJS(r.front().value());
                 if (!v->append(r.front().value())) {
                     js_ReportOutOfMemory(cx);
                     return false;
                 }
             }
         }
 
         return true;
--- a/js/src/vm/SavedStacks.cpp
+++ b/js/src/vm/SavedStacks.cpp
@@ -447,18 +447,18 @@ SavedStacks::sweep(JSRuntime *rt)
                                  ReadBarriered<SavedFrame *>(frame));
                 }
             }
         }
     }
 
     sweepPCLocationMap();
 
-    if (savedFrameProto && IsObjectAboutToBeFinalized(&savedFrameProto)) {
-        savedFrameProto = nullptr;
+    if (savedFrameProto && IsObjectAboutToBeFinalized(savedFrameProto.unsafeGet())) {
+        savedFrameProto.set(nullptr);
     }
 }
 
 void
 SavedStacks::trace(JSTracer *trc)
 {
     if (!pcLocationMap.initialized())
         return;
@@ -588,19 +588,21 @@ SavedStacks::getOrCreateSavedFrameProtot
 
     RootedObject proto(cx, NewObjectWithGivenProto(cx, &SavedFrame::class_,
                                                    global->getOrCreateObjectPrototype(cx),
                                                    global));
     if (!proto
         || !JS_DefineProperties(cx, proto, SavedFrame::properties)
         || !JS_DefineFunctions(cx, proto, SavedFrame::methods)
         || !JSObject::freeze(cx, proto))
+    {
         return nullptr;
+    }
 
-    savedFrameProto = proto;
+    savedFrameProto.set(proto);
     // The only object with the SavedFrame::class_ that doesn't have a source
     // should be the prototype.
     savedFrameProto->setReservedSlot(SavedFrame::JSSLOT_SOURCE, NullValue());
     return savedFrameProto;
 }
 
 SavedFrame *
 SavedStacks::createFrameFromLookup(JSContext *cx, SavedFrame::HandleLookup lookup)
--- a/js/src/vm/SavedStacks.h
+++ b/js/src/vm/SavedStacks.h
@@ -110,17 +110,17 @@ class SavedStacks {
     void     trace(JSTracer *trc);
     uint32_t count();
     void     clear();
 
     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
 
   private:
     SavedFrame::Set frames;
-    JSObject        *savedFrameProto;
+    ReadBarrieredObject savedFrameProto;
 
     bool       insertFrames(JSContext *cx, FrameIter &iter, MutableHandleSavedFrame frame,
                             unsigned maxFrameCount = 0);
     SavedFrame *getOrCreateSavedFrame(JSContext *cx, SavedFrame::HandleLookup lookup);
     // |SavedFrame.prototype| is created lazily and held weakly. It should only
     // be accessed through this method.
     JSObject   *getOrCreateSavedFramePrototype(JSContext *cx);
     SavedFrame *createFrameFromLookup(JSContext *cx, SavedFrame::HandleLookup lookup);
--- a/js/src/vm/Shape.cpp
+++ b/js/src/vm/Shape.cpp
@@ -1523,17 +1523,17 @@ BaseShape::assertConsistency()
 void
 JSCompartment::sweepBaseShapeTable()
 {
     gcstats::AutoPhase ap(runtimeFromMainThread()->gc.stats,
                           gcstats::PHASE_SWEEP_TABLES_BASE_SHAPE);
 
     if (baseShapes.initialized()) {
         for (BaseShapeSet::Enum e(baseShapes); !e.empty(); e.popFront()) {
-            UnownedBaseShape *base = e.front();
+            UnownedBaseShape *base = e.front().unbarrieredGet();
             if (IsBaseShapeAboutToBeFinalized(&base))
                 e.removeFront();
         }
     }
 }
 
 void
 BaseShape::finalize(FreeOp *fop)
@@ -1811,27 +1811,27 @@ void
 JSCompartment::sweepInitialShapeTable()
 {
     gcstats::AutoPhase ap(runtimeFromMainThread()->gc.stats,
                           gcstats::PHASE_SWEEP_TABLES_INITIAL_SHAPE);
 
     if (initialShapes.initialized()) {
         for (InitialShapeSet::Enum e(initialShapes); !e.empty(); e.popFront()) {
             const InitialShapeEntry &entry = e.front();
-            Shape *shape = entry.shape;
+            Shape *shape = entry.shape.unbarrieredGet();
             JSObject *proto = entry.proto.raw();
             if (IsShapeAboutToBeFinalized(&shape) || (entry.proto.isObject() && IsObjectAboutToBeFinalized(&proto))) {
                 e.removeFront();
             } else {
 #ifdef DEBUG
                 DebugOnly<JSObject *> parent = shape->getObjectParent();
                 JS_ASSERT(!parent || !IsObjectAboutToBeFinalized(&parent));
                 JS_ASSERT(parent == shape->getObjectParent());
 #endif
-                if (shape != entry.shape || proto != entry.proto.raw()) {
+                if (shape != entry.shape.unbarrieredGet() || proto != entry.proto.raw()) {
                     ReadBarrieredShape readBarrieredShape(shape);
                     InitialShapeEntry newKey(readBarrieredShape, TaggedProto(proto));
                     e.rekeyFront(newKey.getLookup(), newKey);
                 }
             }
         }
     }
 }
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -364,17 +364,18 @@ InterpreterFrame::mark(JSTracer *trc)
         gc::MarkObjectUnbarriered(trc, &exec.fun, "fun");
         if (isEvalFrame())
             gc::MarkScriptUnbarriered(trc, &u.evalScript, "eval script");
     } else {
         gc::MarkScriptUnbarriered(trc, &exec.script, "script");
     }
     if (IS_GC_MARKING_TRACER(trc))
         script()->compartment()->zone()->active = true;
-    gc::MarkValueUnbarriered(trc, returnValue().address(), "rval");
+    if (hasReturnValue())
+        gc::MarkValueUnbarriered(trc, &rval_, "rval");
 }
 
 void
 InterpreterFrame::markValues(JSTracer *trc, unsigned start, unsigned end)
 {
     if (start < end)
         gc::MarkValueRootRange(trc, end - start, slots() + start, "vm_stack");
 }