Backed out changeset 2a200a9215d7 (bug 1288780) for bustage on a CLOSED TREE
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Fri, 29 Jul 2016 15:41:34 +0200
changeset 349390 4dcce7c669037971a862a39d3a44790523d9c819
parent 349389 18af1ad335a52bc3852d195627757ceb0dad6836
child 349391 e5ae984c685bb798e439fb4ec0bf02b3ab49f8df
push id1230
push userjlund@mozilla.com
push dateMon, 31 Oct 2016 18:13:35 +0000
treeherdermozilla-release@5e06e3766db2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1288780
milestone50.0a1
backs out2a200a9215d731481dd0bfc2de8409203a255ee6
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
Backed out changeset 2a200a9215d7 (bug 1288780) for bustage on a CLOSED TREE
js/src/builtin/WeakMapObject.cpp
js/src/gc/Barrier.cpp
js/src/gc/Barrier.h
js/src/gc/GCRuntime.h
js/src/gc/Nursery.cpp
js/src/gc/Nursery.h
js/src/jsapi-tests/testGCHeapPostBarriers.cpp
js/src/jsutil.h
js/src/jsweakmap.cpp
js/src/jsweakmap.h
js/src/vm/Debugger.cpp
js/src/vm/Debugger.h
js/src/vm/Runtime.cpp
js/src/vm/Runtime.h
js/src/vm/ScopeObject.cpp
js/src/vm/ScopeObject.h
js/src/vm/TaggedProto.h
js/src/vm/TypeInference-inl.h
js/src/vm/TypeInference.cpp
js/src/vm/UnboxedObject.cpp
js/src/vm/UnboxedObject.h
--- a/js/src/builtin/WeakMapObject.cpp
+++ b/js/src/builtin/WeakMapObject.cpp
@@ -126,17 +126,18 @@ TryPreserveReflector(JSContext* cx, Hand
 }
 
 static MOZ_ALWAYS_INLINE bool
 SetWeakMapEntryInternal(JSContext* cx, Handle<WeakMapObject*> mapObj,
                         HandleObject key, HandleValue value)
 {
     ObjectValueMap* map = mapObj->getMap();
     if (!map) {
-        auto newMap = cx->make_unique<ObjectValueMap>(cx, mapObj.get());
+        AutoInitGCManagedObject<ObjectValueMap> newMap(
+            cx->make_unique<ObjectValueMap>(cx, mapObj.get()));
         if (!newMap)
             return false;
         if (!newMap->init()) {
             JS_ReportOutOfMemory(cx);
             return false;
         }
         map = newMap.release();
         mapObj->setPrivate(map);
--- a/js/src/gc/Barrier.cpp
+++ b/js/src/gc/Barrier.cpp
@@ -60,20 +60,20 @@ CurrentThreadIsIonCompilingSafeForMinorG
 
 bool
 CurrentThreadIsGCSweeping()
 {
     return TlsPerThreadData.get()->gcSweeping;
 }
 
 bool
-CurrentThreadCanSkipPostBarrier(bool inNursery)
+CurrentThreadIsHandlingInitFailure()
 {
-    bool onMainThread = TlsPerThreadData.get()->runtimeIfOnOwnerThread() != nullptr;
-    return !onMainThread && !inNursery;
+    JSRuntime* rt = TlsPerThreadData.get()->runtimeIfOnOwnerThread();
+    return rt && rt->handlingInitFailure;
 }
 
 #endif // DEBUG
 
 template <typename S>
 template <typename T>
 void
 ReadBarrierFunctor<S>::operator()(T* t)
--- a/js/src/gc/Barrier.h
+++ b/js/src/gc/Barrier.h
@@ -238,17 +238,17 @@ CurrentThreadIsIonCompiling();
 
 bool
 CurrentThreadIsIonCompilingSafeForMinorGC();
 
 bool
 CurrentThreadIsGCSweeping();
 
 bool
-CurrentThreadCanSkipPostBarrier(bool inNursery);
+CurrentThreadIsHandlingInitFailure();
 #endif
 
 namespace gc {
 
 // Marking.h depends on these barrier definitions, so we need a separate
 // entry point for marking to implement the pre-barrier.
 void MarkValueForBarrier(JSTracer* trc, Value* v, const char* name);
 void MarkIdForBarrier(JSTracer* trc, jsid* idp, const char* name);
@@ -265,18 +265,16 @@ struct InternalBarrierMethods<T*>
 
     static bool isMarkableTaggedPointer(T* v) { return !IsNullTaggedPointer(v); }
 
     static void preBarrier(T* v) { T::writeBarrierPre(v); }
 
     static void postBarrier(T** vp, T* prev, T* next) { T::writeBarrierPost(vp, prev, next); }
 
     static void readBarrier(T* v) { T::readBarrier(v); }
-
-    static bool isInsideNursery(T* v) { return IsInsideNursery(v); }
 };
 
 template <typename S> struct PreBarrierFunctor : public VoidDefaultAdaptor<S> {
     template <typename T> void operator()(T* t);
 };
 
 template <typename S> struct ReadBarrierFunctor : public VoidDefaultAdaptor<S> {
     template <typename T> void operator()(T* t);
@@ -311,32 +309,26 @@ struct InternalBarrierMethods<Value>
         // Remove the prev entry if the new value does not need it.
         if (prev.isObject() && (sb = reinterpret_cast<gc::Cell*>(&prev.toObject())->storeBuffer()))
             sb->unputValue(vp);
     }
 
     static void readBarrier(const Value& v) {
         DispatchTyped(ReadBarrierFunctor<Value>(), v);
     }
-
-    static bool isInsideNursery(const Value& v) {
-        return v.isMarkable() && IsInsideNursery(v.toGCThing());
-    }
 };
 
 template <>
 struct InternalBarrierMethods<jsid>
 {
     static bool isMarkable(jsid id) { return JSID_IS_STRING(id) || JSID_IS_SYMBOL(id); }
     static bool isMarkableTaggedPointer(jsid id) { return isMarkable(id); }
 
     static void preBarrier(jsid id) { DispatchTyped(PreBarrierFunctor<jsid>(), id); }
     static void postBarrier(jsid* idp, jsid prev, jsid next) {}
-
-    static bool isInsideNursery(jsid id) { return false; }
 };
 
 // Barrier classes can use Mixins to add methods to a set of barrier
 // instantiations, to make the barriered thing look and feel more like the
 // thing itself.
 template <typename T>
 class BarrieredBaseMixins {};
 
@@ -446,21 +438,18 @@ class GCPtr : public WriteBarrieredBase<
         this->post(JS::GCPolicy<T>::initial(), v);
     }
     explicit GCPtr(const GCPtr<T>& v) : WriteBarrieredBase<T>(v) {
         this->post(JS::GCPolicy<T>::initial(), v);
     }
 #ifdef DEBUG
     ~GCPtr() {
         // No prebarrier necessary as this only happens when we are sweeping or
-        // after we have just collected the nursery.
-        bool inNursery = InternalBarrierMethods<T>::isInsideNursery(this->value);
-        MOZ_ASSERT(CurrentThreadIsGCSweeping() ||
-                   CurrentThreadCanSkipPostBarrier(inNursery));
-        Poison(this, JS_FREED_HEAP_PTR_PATTERN, sizeof(*this));
+        // before the containing object becomes part of the GC graph.
+        MOZ_ASSERT(CurrentThreadIsGCSweeping() || CurrentThreadIsHandlingInitFailure());
     }
 #endif
 
     void init(T v) {
         this->value = v;
         this->post(JS::GCPolicy<T>::initial(), v);
     }
 
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -872,21 +872,16 @@ class GCRuntime
     bool isVerifyPreBarriersEnabled() const { return false; }
 #endif
 
     // Free certain LifoAlloc blocks when it is safe to do so.
     void freeUnusedLifoBlocksAfterSweeping(LifoAlloc* lifo);
     void freeAllLifoBlocksAfterSweeping(LifoAlloc* lifo);
     void freeAllLifoBlocksAfterMinorGC(LifoAlloc* lifo);
 
-    // Queue a thunk to run after the next minor GC.
-    void callAfterMinorGC(void (*thunk)(void* data), void* data) {
-        nursery.queueSweepAction(thunk, data);
-    }
-
     // Public here for ReleaseArenaLists and FinalizeTypedArenas.
     void releaseArena(Arena* arena, const AutoLockGC& lock);
 
     void releaseHeldRelocatedArenas();
     void releaseHeldRelocatedArenasWithoutUnlocking(const AutoLockGC& lock);
 
     // Allocator
     template <AllowGC allowGC>
@@ -1451,12 +1446,11 @@ GCRuntime::needZealousGC() {
 #else
 inline bool GCRuntime::hasZealMode(ZealMode mode) { return false; }
 inline void GCRuntime::clearZealMode(ZealMode mode) { }
 inline bool GCRuntime::upcomingZealousGC() { return false; }
 inline bool GCRuntime::needZealousGC() { return false; }
 #endif
 
 } /* namespace gc */
-
 } /* namespace js */
 
 #endif
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -53,49 +53,31 @@ struct js::Nursery::FreeMallocedBuffersT
 
   private:
     FreeOp* fop_;
     MallocedBuffersSet buffers_;
 
     virtual void run() override;
 };
 
-struct js::Nursery::SweepAction
-{
-    SweepAction(SweepThunk thunk, void* data, SweepAction* next)
-      : thunk(thunk), data(data), next(next)
-    {}
-
-    SweepThunk thunk;
-    void* data;
-    SweepAction* next;
-
-  private:
-#if JS_BITS_PER_WORD == 32
-    uint32_t padding;
-#endif
-};
-
-
 js::Nursery::Nursery(JSRuntime* rt)
   : runtime_(rt)
   , position_(0)
   , currentStart_(0)
   , currentEnd_(0)
   , heapStart_(0)
   , heapEnd_(0)
   , currentChunk_(0)
   , numActiveChunks_(0)
   , numNurseryChunks_(0)
   , previousPromotionRate_(0)
   , profileThreshold_(0)
   , enableProfiling_(false)
   , minorGcCount_(0)
   , freeMallocedBuffersTask(nullptr)
-  , sweepActions_(nullptr)
 #ifdef JS_GC_ZEAL
   , lastCanary_(nullptr)
 #endif
 {}
 
 bool
 js::Nursery::init(uint32_t maxNurseryBytes)
 {
@@ -736,18 +718,16 @@ js::Nursery::sweep()
         JSObject* obj = static_cast<JSObject*>(e.front());
         if (!IsForwarded(obj))
             obj->zone()->removeUniqueId(obj);
         else
             MOZ_ASSERT(Forwarded(obj)->zone()->hasUniqueId(Forwarded(obj)));
     }
     cellsWithUid_.clear();
 
-    runSweepActions();
-
 #ifdef JS_GC_ZEAL
     /* Poison the nursery contents so touching a freed object will crash. */
     JS_POISON((void*)start(), JS_SWEPT_NURSERY_PATTERN, nurserySize());
     for (int i = 0; i < numNurseryChunks_; ++i)
         initChunk(i);
 
     if (runtime()->hasZealMode(ZealMode::GenerationalGC)) {
         MOZ_ASSERT(numActiveChunks_ == numNurseryChunks_);
@@ -808,42 +788,8 @@ js::Nursery::updateNumActiveChunks(int n
         uintptr_t decommitSize = chunk(priorChunks - 1).start() + ChunkSize - decommitStart;
         MOZ_ASSERT(decommitSize != 0);
         MOZ_ASSERT(decommitStart == AlignBytes(decommitStart, Alignment));
         MOZ_ASSERT(decommitSize == AlignBytes(decommitSize, Alignment));
         MarkPagesUnused((void*)decommitStart, decommitSize);
     }
 #endif // !defined(JS_GC_ZEAL)
 }
-
-void
-js::Nursery::queueSweepAction(SweepThunk thunk, void* data)
-{
-    static_assert(sizeof(SweepAction) % CellSize == 0,
-                  "SweepAction size must be a multiple of cell size");
-    MOZ_ASSERT(!runtime()->mainThread.suppressGC);
-
-    SweepAction* action = nullptr;
-    if (isEnabled() && !js::oom::ShouldFailWithOOM())
-        action = reinterpret_cast<SweepAction*>(allocate(sizeof(SweepAction)));
-
-    if (!action) {
-        runtime()->gc.evictNursery();
-        AutoSetThreadIsSweeping threadIsSweeping;
-        thunk(data);
-        return;
-    }
-
-    new (action) SweepAction(thunk, data, sweepActions_);
-    sweepActions_ = action;
-}
-
-void
-js::Nursery::runSweepActions()
-{
-    // The hazard analysis doesn't know whether the thunks can GC.
-    JS::AutoSuppressGCAnalysis nogc;
-
-    AutoSetThreadIsSweeping threadIsSweeping;
-    for (auto action = sweepActions_; action; action = action->next)
-        action->thunk(action->data);
-    sweepActions_ = nullptr;
-}
--- a/js/src/gc/Nursery.h
+++ b/js/src/gc/Nursery.h
@@ -203,19 +203,16 @@ class Nursery
     MOZ_MUST_USE bool addedUniqueIdToCell(gc::Cell* cell) {
         if (!IsInsideNursery(cell) || !isEnabled())
             return true;
         MOZ_ASSERT(cellsWithUid_.initialized());
         MOZ_ASSERT(!cellsWithUid_.has(cell));
         return cellsWithUid_.put(cell);
     }
 
-    using SweepThunk = void (*)(void *data);
-    void queueSweepAction(SweepThunk thunk, void* data);
-
     size_t sizeOfHeapCommitted() const {
         return numActiveChunks_ * gc::ChunkSize;
     }
     size_t sizeOfHeapDecommitted() const {
         return (numNurseryChunks_ - numActiveChunks_) * gc::ChunkSize;
     }
     size_t sizeOfMallocedBuffers(mozilla::MallocSizeOf mallocSizeOf) const {
         size_t total = 0;
@@ -333,20 +330,16 @@ class Nursery
      *
      * Note: we store the pointers as Cell* here, resulting in an ugly cast in
      *       sweep. This is because this structure is used to help implement
      *       stable object hashing and we have to break the cycle somehow.
      */
     using CellsWithUniqueIdSet = HashSet<gc::Cell*, PointerHasher<gc::Cell*, 3>, SystemAllocPolicy>;
     CellsWithUniqueIdSet cellsWithUid_;
 
-    struct SweepAction;
-    SweepAction* sweepActions_;
-    SweepAction* reservedSweepAction_;
-
 #ifdef JS_GC_ZEAL
     struct Canary
     {
         uintptr_t magicValue;
         Canary* next;
     };
 
     Canary* lastCanary_;
@@ -431,18 +424,16 @@ class Nursery
     void freeMallocedBuffers();
 
     /*
      * Frees all non-live nursery-allocated things at the end of a minor
      * collection.
      */
     void sweep();
 
-    void runSweepActions();
-
     /* Change the allocable space provided by the nursery. */
     void growAllocableSpace();
     void shrinkAllocableSpace();
 
     /* Profile recording and printing. */
     void startProfile(ProfileKey key);
     void endProfile(ProfileKey key);
     void maybeStartProfile(ProfileKey key);
--- a/js/src/jsapi-tests/testGCHeapPostBarriers.cpp
+++ b/js/src/jsapi-tests/testGCHeapPostBarriers.cpp
@@ -4,157 +4,88 @@
 /* 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 "mozilla/UniquePtr.h"
 
 #include "js/RootingAPI.h"
 #include "jsapi-tests/tests.h"
-#include "vm/Runtime.h"
-
-template <typename T>
-static T* CreateGCThing(JSContext* cx)
-{
-    MOZ_CRASH();
-    return nullptr;
-}
-
-template <>
-JSObject* CreateGCThing(JSContext* cx)
-{
-    JS::RootedObject obj(cx, JS_NewPlainObject(cx));
-    if (!obj)
-        return nullptr;
-    JS_DefineProperty(cx, obj, "x", 42, 0);
-    return obj;
-}
-
-template <>
-JSFunction* CreateGCThing(JSContext* cx)
-{
-    /*
-     * We don't actually use the function as a function, so here we cheat and
-     * cast a JSObject.
-     */
-    return static_cast<JSFunction*>(CreateGCThing<JSObject>(cx));
-}
 
 BEGIN_TEST(testGCHeapPostBarriers)
 {
 #ifdef JS_GC_ZEAL
     AutoLeaveZeal nozeal(cx);
 #endif /* JS_GC_ZEAL */
 
     /* Sanity check - objects start in the nursery and then become tenured. */
     JS_GC(cx);
-    JS::RootedObject obj(cx, CreateGCThing<JSObject>(cx));
+    JS::RootedObject obj(cx, NurseryObject());
     CHECK(js::gc::IsInsideNursery(obj.get()));
     JS_GC(cx);
     CHECK(!js::gc::IsInsideNursery(obj.get()));
     JS::RootedObject tenuredObject(cx, obj);
 
     /* Currently JSObject and JSFunction objects are nursery allocated. */
-    CHECK(TestHeapPostBarriersForType<JSObject>());
-    CHECK(TestHeapPostBarriersForType<JSFunction>());
+    CHECK(TestHeapPostBarriers(NurseryObject()));
+    CHECK(TestHeapPostBarriers(NurseryFunction()));
 
     return true;
 }
 
 MOZ_NEVER_INLINE bool
 Passthrough(bool value)
 {
     /* Work around a Win64 optimization bug in VS2010. (Bug 1033146) */
     return value;
 }
 
-bool
-CanAccessObject(JSObject* obj)
-{
-    JS::RootedObject rootedObj(cx, obj);
-    JS::RootedValue value(cx);
-    CHECK(JS_GetProperty(cx, rootedObj, "x", &value));
-    CHECK(value.isInt32());
-    CHECK(value.toInt32() == 42);
-    return true;
-}
-
 template <typename T>
 bool
-TestHeapPostBarriersForType()
-{
-    CHECK((TestHeapPostBarriersForWrapper<T, JS::Heap<T*>>()));
-    CHECK((TestHeapPostBarriersForWrapper<T, js::GCPtr<T*>>()));
-    CHECK((TestHeapPostBarriersForWrapper<T, js::HeapPtr<T*>>()));
-    return true;
-}
-
-template <typename T, typename W>
-bool
-TestHeapPostBarriersForWrapper()
+TestHeapPostBarriers(T initialObj)
 {
-    CHECK((TestHeapPostBarrierUpdate<T, W>()));
-    CHECK((TestHeapPostBarrierInitFailure<T, W>()));
-    return true;
-}
-
-template <typename T, typename W>
-bool
-TestHeapPostBarrierUpdate()
-{
-    // Normal case - allocate a heap object, write a nursery pointer into it and
-    // check that it gets updated on minor GC.
-
-    T* initialObj = CreateGCThing<T>(cx);
     CHECK(initialObj != nullptr);
     CHECK(js::gc::IsInsideNursery(initialObj));
+
+    /* Construct Heap<> wrapper. */
+    auto heapDataStorage = mozilla::MakeUnique<char[]>(sizeof(JS::Heap<T>));
+    auto* heapData = new (heapDataStorage.get()) JS::Heap<T>();
+    CHECK(heapData);
+    CHECK(Passthrough(*heapData == nullptr));
+    *heapData = initialObj;
+
+    /* Store the pointer as an integer so that the hazard analysis will miss it. */
     uintptr_t initialObjAsInt = uintptr_t(initialObj);
 
-    W* ptr = nullptr;
-
-    {
-        auto heapPtr = cx->make_unique<W>();
-        CHECK(heapPtr);
+    /* Perform minor GC and check heap wrapper is udated with new pointer. */
+    cx->minorGC(JS::gcreason::API);
+    CHECK(uintptr_t(heapData) != initialObjAsInt);
+    CHECK(!js::gc::IsInsideNursery(*heapData));
 
-        W& wrapper = *heapPtr;
-        CHECK(Passthrough(wrapper.get() == nullptr));
-        wrapper = initialObj;
-        CHECK(Passthrough(wrapper == initialObj));
-
-        ptr = heapPtr.release();
-    }
-
-    cx->minorGC(JS::gcreason::API);
-
-    CHECK(uintptr_t(ptr->get()) != initialObjAsInt);
-    CHECK(!js::gc::IsInsideNursery(ptr->get()));
-    CHECK(CanAccessObject(ptr->get()));
+    /* Check object is definitely still alive. */
+    JS::Rooted<T> obj(cx, *heapData);
+    JS::RootedValue value(cx);
+    CHECK(JS_GetProperty(cx, obj, "x", &value));
+    CHECK(value.isInt32());
+    CHECK(value.toInt32() == 42);
 
     return true;
 }
 
-template <typename T, typename W>
-bool
-TestHeapPostBarrierInitFailure()
+JSObject* NurseryObject()
 {
-    // Failure case - allocate a heap object, write a nursery pointer into it
-    // and fail to complete initialization.
-
-    T* initialObj = CreateGCThing<T>(cx);
-    CHECK(initialObj != nullptr);
-    CHECK(js::gc::IsInsideNursery(initialObj));
+    JS::RootedObject obj(cx, JS_NewPlainObject(cx));
+    if (!obj)
+        return nullptr;
+    JS_DefineProperty(cx, obj, "x", 42, 0);
+    return obj;
+}
 
-    {
-        auto heapPtr = cx->make_unique<W>();
-        CHECK(heapPtr);
-
-        W& wrapper = *heapPtr;
-        CHECK(Passthrough(wrapper.get() == nullptr));
-        wrapper = initialObj;
-        CHECK(Passthrough(wrapper == initialObj));
-    }
-
-    cx->minorGC(JS::gcreason::API);
-
-    return true;
+JSFunction* NurseryFunction()
+{
+    /*
+     * We don't actually use the function as a function, so here we cheat and
+     * cast a JSObject.
+     */
+    return static_cast<JSFunction*>(NurseryObject());
 }
 
 END_TEST(testGCHeapPostBarriers)
--- a/js/src/jsutil.h
+++ b/js/src/jsutil.h
@@ -323,17 +323,16 @@ PodSet(T* aDst, T aSrc, size_t aNElem)
  */
 #define JS_FRESH_NURSERY_PATTERN 0x2F
 #define JS_SWEPT_NURSERY_PATTERN 0x2B
 #define JS_ALLOCATED_NURSERY_PATTERN 0x2D
 #define JS_FRESH_TENURED_PATTERN 0x4F
 #define JS_MOVED_TENURED_PATTERN 0x49
 #define JS_SWEPT_TENURED_PATTERN 0x4B
 #define JS_ALLOCATED_TENURED_PATTERN 0x4D
-#define JS_FREED_HEAP_PTR_PATTERN 0x6B
 
 /*
  * Ensure JS_SWEPT_CODE_PATTERN is a byte pattern that will crash immediately
  * when executed, so either an undefined instruction or an instruction that's
  * illegal in user mode.
  */
 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_NONE)
 # define JS_SWEPT_CODE_PATTERN 0xED // IN instruction, crashes in user mode.
--- a/js/src/jsweakmap.cpp
+++ b/js/src/jsweakmap.cpp
@@ -27,17 +27,17 @@ WeakMapBase::WeakMapBase(JSObject* memOf
     zone(zone),
     marked(false)
 {
     MOZ_ASSERT_IF(memberOf, memberOf->compartment()->zone() == zone);
 }
 
 WeakMapBase::~WeakMapBase()
 {
-    MOZ_ASSERT(CurrentThreadIsGCSweeping());
+    MOZ_ASSERT(CurrentThreadIsGCSweeping() || CurrentThreadIsHandlingInitFailure());
 }
 
 void
 WeakMapBase::unmarkZone(JS::Zone* zone)
 {
     for (WeakMapBase* m : zone->gcWeakMapList)
         m->marked = false;
 }
--- a/js/src/jsweakmap.h
+++ b/js/src/jsweakmap.h
@@ -407,17 +407,9 @@ class ObjectWeakMap
 
 #ifdef JSGC_HASH_TABLE_CHECKS
     void checkAfterMovingGC();
 #endif
 };
 
 } /* namespace js */
 
-namespace JS {
-
-template <>
-struct DeletePolicy<js::ObjectValueMap> : public js::GCManagedDeletePolicy<js::ObjectValueMap>
-{};
-
-} /* namespace JS */
-
 #endif /* jsweakmap_h */
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -642,16 +642,17 @@ Debugger::Debugger(JSContext* cx, Native
     traceLoggerLastDrainedSize(0),
     traceLoggerLastDrainedIteration(0),
 #endif
     traceLoggerScriptedCallsLastDrainedSize(0),
     traceLoggerScriptedCallsLastDrainedIteration(0)
 {
     assertSameCompartment(cx, dbg);
 
+    cx->runtime()->debuggerList.insertBack(this);
     JS_INIT_CLIST(&breakpoints);
     JS_INIT_CLIST(&onNewGlobalObjectWatchersLink);
 }
 
 Debugger::~Debugger()
 {
     MOZ_ASSERT_IF(debuggees.initialized(), debuggees.empty());
     allocationsLog.clear();
@@ -664,33 +665,29 @@ Debugger::~Debugger()
      * background finalized.
      */
     JS_REMOVE_LINK(&onNewGlobalObjectWatchersLink);
 }
 
 bool
 Debugger::init(JSContext* cx)
 {
-    if (!debuggees.init() ||
-        !debuggeeZones.init() ||
-        !frames.init() ||
-        !scripts.init() ||
-        !sources.init() ||
-        !objects.init() ||
-        !observedGCs.init() ||
-        !environments.init() ||
-        !wasmInstanceScripts.init() ||
-        !wasmInstanceSources.init())
-    {
+    bool ok = debuggees.init() &&
+              debuggeeZones.init() &&
+              frames.init() &&
+              scripts.init() &&
+              sources.init() &&
+              objects.init() &&
+              observedGCs.init() &&
+              environments.init() &&
+              wasmInstanceScripts.init() &&
+              wasmInstanceSources.init();
+    if (!ok)
         ReportOutOfMemory(cx);
-        return false;
-    }
-
-    cx->runtime()->debuggerList.insertBack(this);
-    return true;
+    return ok;
 }
 
 JS_STATIC_ASSERT(unsigned(JSSLOT_DEBUGFRAME_OWNER) == unsigned(JSSLOT_DEBUGSCRIPT_OWNER));
 JS_STATIC_ASSERT(unsigned(JSSLOT_DEBUGFRAME_OWNER) == unsigned(JSSLOT_DEBUGSOURCE_OWNER));
 JS_STATIC_ASSERT(unsigned(JSSLOT_DEBUGFRAME_OWNER) == unsigned(JSSLOT_DEBUGOBJECT_OWNER));
 JS_STATIC_ASSERT(unsigned(JSSLOT_DEBUGFRAME_OWNER) == unsigned(DebuggerEnvironment::OWNER_SLOT));
 
 /* static */ Debugger*
@@ -3686,17 +3683,17 @@ Debugger::construct(JSContext* cx, unsig
         return false;
     for (unsigned slot = JSSLOT_DEBUG_PROTO_START; slot < JSSLOT_DEBUG_PROTO_STOP; slot++)
         obj->setReservedSlot(slot, proto->getReservedSlot(slot));
     obj->setReservedSlot(JSSLOT_DEBUG_MEMORY_INSTANCE, NullValue());
 
     Debugger* debugger;
     {
         /* Construct the underlying C++ object. */
-        auto dbg = cx->make_unique<Debugger>(cx, obj.get());
+        AutoInitGCManagedObject<Debugger> dbg(cx->make_unique<Debugger>(cx, obj.get()));
         if (!dbg || !dbg->init(cx))
             return false;
 
         debugger = dbg.release();
         obj->setPrivate(debugger); // owns the released pointer
     }
 
     /* Add the initial debuggees, if any. */
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -1532,17 +1532,10 @@ Debugger::onLogAllocationSite(JSContext*
     RootedObject hobj(cx, obj);
     return Debugger::slowPathOnLogAllocationSite(cx, hobj, frame, when, *dbgs);
 }
 
 MOZ_MUST_USE bool ReportObjectRequired(JSContext* cx);
 
 } /* namespace js */
 
-namespace JS {
-
-template <>
-struct DeletePolicy<js::Debugger> : public js::GCManagedDeletePolicy<js::Debugger>
-{};
-
-} /* namespace JS */
 
 #endif /* vm_Debugger_h */
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -196,16 +196,19 @@ JSRuntime::JSRuntime(JSRuntime* parentRu
     NaNValue(DoubleNaNValue()),
     negativeInfinityValue(DoubleValue(NegativeInfinity<double>())),
     positiveInfinityValue(DoubleValue(PositiveInfinity<double>())),
     emptyString(nullptr),
     spsProfiler(thisFromCtor()),
     profilingScripts(false),
     suppressProfilerSampling(false),
     hadOutOfMemory(false),
+#ifdef DEBUG
+    handlingInitFailure(false),
+#endif
 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
     runningOOMTest(false),
 #endif
     allowRelazificationForTesting(false),
     defaultFreeOp_(thisFromCtor()),
     debuggerMutations(0),
     securityCallbacks(&NullSecurityCallbacks),
     DOMcallbacks(nullptr),
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -936,16 +936,21 @@ struct JSRuntime : public JS::shadow::Ru
     }
     void enableProfilerSampling() {
         suppressProfilerSampling = false;
     }
 
     /* Had an out-of-memory error which did not populate an exception. */
     bool                hadOutOfMemory;
 
+#ifdef DEBUG
+    /* We are currently deleting an object due to an initialization failure. */
+    bool handlingInitFailure;
+#endif
+
 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
     /* We are currently running a simulated OOM test. */
     bool runningOOMTest;
 #endif
 
     /*
      * Allow relazifying functions in compartments that are active. This is
      * only used by the relazifyFunctions() testing function.
@@ -1710,51 +1715,83 @@ class MOZ_RAII AutoEnterIonCompilation
         pt->ionCompilingSafeForMinorGC = false;
 #endif
     }
 
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 /*
- * Provides a delete policy that can be used for objects which have their
- * lifetime managed by the GC and can only safely be destroyed while the nursery
- * is empty.
+ * AutoInitGCManagedObject is a wrapper for use when initializing a object whose
+ * lifetime is managed by the GC.  It ensures that the object is destroyed if
+ * initialization fails but also allows us to assert the invariant that such
+ * objects are only destroyed in this way or by the GC.
+ *
+ * It has a limited interface but is a drop-in replacement for UniquePtr<T> is
+ * this situation.  For example:
  *
- * This is necessary when initializing such an object may fail after the initial
- * allocation.  The partially-initialized object must be destroyed, but it may
- * not be safe to do so at the current time.  This policy puts the object on a
- * queue to be destroyed at a safe time.
+ *   AutoInitGCManagedObject<MyClass> ptr(cx->make_unique<MyClass>());
+ *   if (!ptr) {
+ *     ReportOutOfMemory(cx);
+ *     return nullptr;
+ *   }
+ *
+ *   if (!ptr->init(cx))
+ *     return nullptr;    // Object destroyed here if init() failed.
+ *
+ *   object->setPrivate(ptr.release());
+ *   // Initialization successful, ptr is now owned through another object.
  */
 template <typename T>
-struct GCManagedDeletePolicy
+class MOZ_STACK_CLASS AutoInitGCManagedObject
 {
-    void operator()(const T* ptr) {
-        if (ptr) {
-            JSRuntime* rt = TlsPerThreadData.get()->runtimeIfOnOwnerThread();
-            if (rt)
-                rt->gc.callAfterMinorGC(deletePtr, const_cast<T*>(ptr));
-            else
-                js_delete(const_cast<T*>(ptr));
+    typedef UniquePtr<T> UniquePtrT;
+
+    UniquePtrT ptr_;
+
+  public:
+    explicit AutoInitGCManagedObject(UniquePtrT&& ptr)
+      : ptr_(mozilla::Move(ptr))
+    {}
+
+    ~AutoInitGCManagedObject() {
+#ifdef DEBUG
+        if (ptr_) {
+            JSRuntime* rt = TlsPerThreadData.get()->runtimeFromMainThread();
+            MOZ_ASSERT(!rt->handlingInitFailure);
+            rt->handlingInitFailure = true;
+            ptr_.reset(nullptr);
+            rt->handlingInitFailure = false;
         }
+#endif
     }
 
-  private:
-    static void deletePtr(void* data) {
-        js_delete(reinterpret_cast<T*>(data));
+    T& operator*() const {
+        return *get();
+    }
+
+    T* operator->() const {
+        return get();
+    }
+
+    explicit operator bool() const {
+        return get() != nullptr;
     }
+
+    T* get() const {
+        return ptr_.get();
+    }
+
+    T* release() {
+        return ptr_.release();
+    }
+
+    AutoInitGCManagedObject(const AutoInitGCManagedObject<T>& other) = delete;
+    AutoInitGCManagedObject& operator=(const AutoInitGCManagedObject<T>& other) = delete;
 };
 
 } /* namespace js */
 
-namespace JS {
-
-template <typename T>
-struct DeletePolicy<js::GCPtr<T>> : public js::GCManagedDeletePolicy<js::GCPtr<T>>
-{};
-
-} /* namespace JS */
-
 #ifdef _MSC_VER
 #pragma warning(pop)
 #endif
 
 #endif /* vm_Runtime_h */
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -2588,17 +2588,17 @@ CanUseDebugScopeMaps(JSContext* cx)
 
 DebugScopes*
 DebugScopes::ensureCompartmentData(JSContext* cx)
 {
     JSCompartment* c = cx->compartment();
     if (c->debugScopes)
         return c->debugScopes;
 
-    auto debugScopes = cx->make_unique<DebugScopes>(cx);
+    AutoInitGCManagedObject<DebugScopes> debugScopes(cx->make_unique<DebugScopes>(cx));
     if (!debugScopes || !debugScopes->init()) {
         ReportOutOfMemory(cx);
         return nullptr;
     }
 
     c->debugScopes = debugScopes.release();
     return c->debugScopes;
 }
--- a/js/src/vm/ScopeObject.h
+++ b/js/src/vm/ScopeObject.h
@@ -1606,23 +1606,9 @@ bool CheckEvalDeclarationConflicts(JSCon
 void DumpStaticScopeChain(JSScript* script);
 void DumpStaticScopeChain(JSObject* staticScope);
 bool
 AnalyzeEntrainedVariables(JSContext* cx, HandleScript script);
 #endif
 
 } // namespace js
 
-namespace JS {
-
-template <>
-struct DeletePolicy<js::DebugScopeObject>
-{
-    explicit DeletePolicy(JSRuntime* rt) : rt_(rt) {}
-    void operator()(const js::DebugScopeObject* ptr);
-
-  private:
-    JSRuntime* rt_;
-};
-
-} // namespace JS
-
 #endif /* vm_ScopeObject_h */
--- a/js/src/vm/TaggedProto.h
+++ b/js/src/vm/TaggedProto.h
@@ -67,21 +67,16 @@ struct InternalBarrierMethods<TaggedProt
 
     static bool isMarkableTaggedPointer(TaggedProto proto) {
         return proto.isObject();
     }
 
     static bool isMarkable(TaggedProto proto) {
         return proto.isObject();
     }
-
-    static bool isInsideNursery(TaggedProto proto) {
-        return proto.isObject() &&
-            gc::IsInsideNursery(reinterpret_cast<gc::Cell*>(proto.toObject()));
-    }
 };
 
 template<class Outer>
 class TaggedProtoOperations
 {
     const TaggedProto& value() const {
         return static_cast<const Outer*>(this)->get();
     }
--- a/js/src/vm/TypeInference-inl.h
+++ b/js/src/vm/TypeInference-inl.h
@@ -271,21 +271,17 @@ TypeIdString(jsid id)
  * intermediate types (i.e. JITs) can use this to ensure that intermediate
  * information is not collected and does not change.
  *
  * Ensures that GC cannot occur. Does additional sanity checking that inference
  * is not reentrant and that recompilations occur properly.
  */
 struct AutoEnterAnalysis
 {
-    // For use when initializing an UnboxedLayout.  The UniquePtr's destructor
-    // must run when GC is not suppressed.
-    UniquePtr<UnboxedLayout> unboxedLayoutToCleanUp;
-
-    // Prevent GC activity in the middle of analysis.
+    /* Prevent GC activity in the middle of analysis. */
     gc::AutoSuppressGC suppressGC;
 
     // Allow clearing inference info on OOM during incremental sweeping.
     AutoClearTypeInferenceStateOnOOM oom;
 
     // Pending recompilations to perform before execution of JIT code can resume.
     RecompileInfoVector pendingRecompiles;
 
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -3508,17 +3508,17 @@ PreliminaryObjectArrayWithTemplate::mayb
             if (obj->inDictionaryMode() || !OnlyHasDataProperties(obj->lastProperty()))
                 return;
 
             if (CommonPrefix(obj->lastProperty(), shape()) != shape())
                 return;
         }
     }
 
-    TryConvertToUnboxedLayout(cx, enter, shape(), group, preliminaryObjects);
+    TryConvertToUnboxedLayout(cx, shape(), group, preliminaryObjects);
     if (group->maybeUnboxedLayout())
         return;
 
     if (shape()) {
         // We weren't able to use an unboxed layout, but since the preliminary
         // objects still reflect the template object's properties, and all
         // objects in the future will be created with those properties, the
         // properties can be marked as definite for objects in the group.
@@ -3789,17 +3789,17 @@ TypeNewScript::maybeAnalyze(JSContext* c
         if (!initializerList) {
             ReportOutOfMemory(cx);
             return false;
         }
         PodCopy(initializerList, initializerVector.begin(), initializerVector.length());
     }
 
     // Try to use an unboxed representation for the group.
-    if (!TryConvertToUnboxedLayout(cx, enter, templateObject()->lastProperty(), group, preliminaryObjects))
+    if (!TryConvertToUnboxedLayout(cx, templateObject()->lastProperty(), group, preliminaryObjects))
         return false;
 
     js_delete(preliminaryObjects);
     preliminaryObjects = nullptr;
 
     if (group->maybeUnboxedLayout()) {
         // An unboxed layout was constructed for the group, and this has already
         // been hooked into it.
--- a/js/src/vm/UnboxedObject.cpp
+++ b/js/src/vm/UnboxedObject.cpp
@@ -1915,17 +1915,17 @@ UnboxedPlainObject::fillAfterConvert(Exc
 {
     initExpando();
     memset(data(), 0, layout().size());
     for (size_t i = 0; i < layout().properties().length(); i++)
         JS_ALWAYS_TRUE(setValue(cx, layout().properties()[i], NextValue(values, valueCursor)));
 }
 
 bool
-js::TryConvertToUnboxedLayout(ExclusiveContext* cx, AutoEnterAnalysis& enter, Shape* templateShape,
+js::TryConvertToUnboxedLayout(ExclusiveContext* cx, Shape* templateShape,
                               ObjectGroup* group, PreliminaryObjectArray* objects)
 {
     bool isArray = !templateShape;
 
     // Unboxed arrays are nightly only for now. The getenv() call will be
     // removed when they are on by default. See bug 1153266.
     if (isArray) {
 #ifdef NIGHTLY_BUILD
@@ -2010,19 +2010,17 @@ js::TryConvertToUnboxedLayout(ExclusiveC
 
         layoutSize = ComputePlainObjectLayout(cx, templateShape, properties);
 
         // The entire object must be allocatable inline.
         if (UnboxedPlainObject::offsetOfData() + layoutSize > JSObject::MAX_BYTE_SIZE)
             return true;
     }
 
-    UniquePtr<UnboxedLayout>& layout = enter.unboxedLayoutToCleanUp;
-    MOZ_ASSERT(!layout);
-    layout = group->zone()->make_unique<UnboxedLayout>();
+    AutoInitGCManagedObject<UnboxedLayout> layout(group->zone()->make_unique<UnboxedLayout>());
     if (!layout)
         return false;
 
     if (isArray) {
         layout->initArray(elementType);
     } else {
         if (!layout->initProperties(properties, layoutSize))
             return false;
@@ -2086,16 +2084,17 @@ js::TryConvertToUnboxedLayout(ExclusiveC
 
         if (isArray)
             obj->as<UnboxedArrayObject>().fillAfterConvert(cx, values, &valueCursor);
         else
             obj->as<UnboxedPlainObject>().fillAfterConvert(cx, values, &valueCursor);
     }
 
     MOZ_ASSERT(valueCursor == values.length());
+    layout.release();
     return true;
 }
 
 DefineBoxedOrUnboxedFunctor6(SetOrExtendBoxedOrUnboxedDenseElements,
                              ExclusiveContext*, JSObject*, uint32_t, const Value*, uint32_t,
                              ShouldUpdateTypes);
 
 DenseElementResult
--- a/js/src/vm/UnboxedObject.h
+++ b/js/src/vm/UnboxedObject.h
@@ -5,17 +5,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef vm_UnboxedObject_h
 #define vm_UnboxedObject_h
 
 #include "jsgc.h"
 #include "jsobj.h"
 
-#include "vm/Runtime.h"
 #include "vm/TypeInference.h"
 
 namespace js {
 
 // Memory required for an unboxed value of a given type. Returns zero for types
 // which can't be used for unboxed objects.
 static inline size_t
 UnboxedTypeSize(JSValueType type)
@@ -316,17 +315,17 @@ class UnboxedPlainObject : public JSObje
         return offsetof(UnboxedPlainObject, data_[0]);
     }
 };
 
 // Try to construct an UnboxedLayout for each of the preliminary objects,
 // provided they all match the template shape. If successful, converts the
 // preliminary objects and their group to the new unboxed representation.
 bool
-TryConvertToUnboxedLayout(ExclusiveContext* cx, AutoEnterAnalysis& enter, Shape* templateShape,
+TryConvertToUnboxedLayout(ExclusiveContext* cx, Shape* templateShape,
                           ObjectGroup* group, PreliminaryObjectArray* objects);
 
 inline gc::AllocKind
 UnboxedLayout::getAllocKind() const
 {
     MOZ_ASSERT(size());
     return gc::GetGCObjectKindForBytes(UnboxedPlainObject::offsetOfData() + size());
 }
@@ -515,17 +514,9 @@ class UnboxedArrayObject : public JSObje
         MOZ_ASSERT(index <= (CapacityMask >> CapacityShift));
         capacityIndexAndInitializedLength_ =
             (index << CapacityShift) | initializedLength();
     }
 };
 
 } // namespace js
 
-namespace JS {
-
-template <>
-struct DeletePolicy<js::UnboxedLayout> : public js::GCManagedDeletePolicy<js::UnboxedLayout>
-{};
-
-} /* namespace JS */
-
 #endif /* vm_UnboxedObject_h */