Bug 1486727 - Move some inline method defintions out of gc/Zone.h into new Zone-inl.h r=sfink
authorJon Coppeard <jcoppeard@mozilla.com>
Wed, 29 Aug 2018 13:23:56 +0100
changeset 433875 b7dada718ee0f14154562db052bfab83e9999a17
parent 433874 82dc592c7293290a7b17c4473c01a228e42615cd
child 433876 3a11a4848d4038adb1d631a6ed80151371591c91
push id34523
push usercsabou@mozilla.com
push dateWed, 29 Aug 2018 17:34:43 +0000
treeherdermozilla-central@c2e3be6a1dd3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssfink
bugs1486727
milestone63.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 1486727 - Move some inline method defintions out of gc/Zone.h into new Zone-inl.h r=sfink
js/src/gc/Barrier.cpp
js/src/gc/GC.cpp
js/src/gc/Marking.cpp
js/src/gc/Nursery.cpp
js/src/gc/Zone-inl.h
js/src/gc/Zone.cpp
js/src/gc/Zone.h
js/src/jsapi-tests/testGCUniqueId.cpp
--- a/js/src/gc/Barrier.cpp
+++ b/js/src/gc/Barrier.cpp
@@ -13,16 +13,18 @@
 #include "js/Value.h"
 #include "vm/EnvironmentObject.h"
 #include "vm/JSObject.h"
 #include "vm/Realm.h"
 #include "vm/SharedArrayObject.h"
 #include "vm/SymbolType.h"
 #include "wasm/WasmJS.h"
 
+#include "gc/Zone-inl.h"
+
 namespace js {
 
 bool
 RuntimeFromMainThreadIsHeapMajorCollecting(JS::shadow::Zone* shadowZone)
 {
     MOZ_ASSERT(CurrentThreadCanAccessRuntime(shadowZone->runtimeFromMainThread()));
     return JS::RuntimeHeapIsMajorCollecting();
 }
--- a/js/src/gc/GC.cpp
+++ b/js/src/gc/GC.cpp
@@ -246,16 +246,17 @@
 #include "vm/Time.h"
 #include "vm/TraceLogging.h"
 #include "vm/WrapperObject.h"
 
 #include "gc/Heap-inl.h"
 #include "gc/Marking-inl.h"
 #include "gc/Nursery-inl.h"
 #include "gc/PrivateIterators-inl.h"
+#include "gc/Zone-inl.h"
 #include "vm/GeckoProfiler-inl.h"
 #include "vm/JSObject-inl.h"
 #include "vm/JSScript-inl.h"
 #include "vm/Stack-inl.h"
 #include "vm/StringType-inl.h"
 
 using namespace js;
 using namespace js::gc;
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -32,16 +32,17 @@
 #include "vm/SymbolType.h"
 #include "vm/TypedArrayObject.h"
 #include "vm/UnboxedObject.h"
 #include "wasm/WasmJS.h"
 
 #include "gc/GC-inl.h"
 #include "gc/Nursery-inl.h"
 #include "gc/PrivateIterators-inl.h"
+#include "gc/Zone-inl.h"
 #include "vm/NativeObject-inl.h"
 #include "vm/Realm-inl.h"
 #include "vm/StringType-inl.h"
 #include "vm/UnboxedObject-inl.h"
 
 using namespace js;
 using namespace js::gc;
 
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -27,16 +27,17 @@
 #endif
 #include "vm/JSONPrinter.h"
 #include "vm/Realm.h"
 #include "vm/Time.h"
 #include "vm/TypedArrayObject.h"
 #include "vm/TypeInference.h"
 
 #include "gc/Marking-inl.h"
+#include "gc/Zone-inl.h"
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 using namespace gc;
 
 using mozilla::DebugOnly;
 using mozilla::PodCopy;
 using mozilla::TimeDuration;
new file mode 100644
--- /dev/null
+++ b/js/src/gc/Zone-inl.h
@@ -0,0 +1,161 @@
+/* -*- 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 gc_Zone_inl_h
+#define gc_Zone_inl_h
+
+#include "gc/Zone.h"
+
+#ifdef DEBUG
+inline bool
+JS::Zone::requireGCTracer() const
+{
+    JSRuntime* rt = runtimeFromAnyThread();
+    return RuntimeHeapIsMajorCollecting() && !rt->gc.isHeapCompacting() && gcState_ != NoGC;
+}
+#endif
+
+inline void
+JS::Zone::updateAllGCMallocCountersOnGCStart()
+{
+    gcMallocCounter.updateOnGCStart();
+    jitCodeCounter.updateOnGCStart();
+}
+
+inline void
+JS::Zone::updateAllGCMallocCountersOnGCEnd(const js::AutoLockGC& lock)
+{
+    auto& gc = runtimeFromAnyThread()->gc;
+    gcMallocCounter.updateOnGCEnd(gc.tunables, lock);
+    jitCodeCounter.updateOnGCEnd(gc.tunables, lock);
+}
+
+inline js::gc::TriggerKind
+JS::Zone::shouldTriggerGCForTooMuchMalloc()
+{
+    auto& gc = runtimeFromAnyThread()->gc;
+    return std::max(gcMallocCounter.shouldTriggerGC(gc.tunables),
+                    jitCodeCounter.shouldTriggerGC(gc.tunables));
+}
+
+
+/* static */ inline js::HashNumber
+JS::Zone::UniqueIdToHash(uint64_t uid)
+{
+    return mozilla::HashGeneric(uid);
+}
+
+inline bool
+JS::Zone::getHashCode(js::gc::Cell* cell, js::HashNumber* hashp)
+{
+    uint64_t uid;
+    if (!getOrCreateUniqueId(cell, &uid))
+        return false;
+    *hashp = UniqueIdToHash(uid);
+    return true;
+}
+
+inline bool
+JS::Zone::maybeGetUniqueId(js::gc::Cell* cell, uint64_t* uidp)
+{
+    MOZ_ASSERT(uidp);
+    MOZ_ASSERT(js::CurrentThreadCanAccessZone(this));
+
+    // Get an existing uid, if one has been set.
+    auto p = uniqueIds().lookup(cell);
+    if (p)
+        *uidp = p->value();
+
+    return p.found();
+}
+
+inline bool
+JS::Zone::getOrCreateUniqueId(js::gc::Cell* cell, uint64_t* uidp)
+{
+    MOZ_ASSERT(uidp);
+    MOZ_ASSERT(js::CurrentThreadCanAccessZone(this) || js::CurrentThreadIsPerformingGC());
+
+    // Get an existing uid, if one has been set.
+    auto p = uniqueIds().lookupForAdd(cell);
+    if (p) {
+        *uidp = p->value();
+        return true;
+    }
+
+    MOZ_ASSERT(js::CurrentThreadCanAccessZone(this));
+
+    // Set a new uid on the cell.
+    *uidp = js::gc::NextCellUniqueId(runtimeFromAnyThread());
+    if (!uniqueIds().add(p, cell, *uidp))
+        return false;
+
+    // If the cell was in the nursery, hopefully unlikely, then we need to
+    // tell the nursery about it so that it can sweep the uid if the thing
+    // does not get tenured.
+    if (IsInsideNursery(cell) &&
+        !runtimeFromMainThread()->gc.nursery().addedUniqueIdToCell(cell))
+    {
+        uniqueIds().remove(cell);
+        return false;
+    }
+
+    return true;
+}
+
+inline js::HashNumber
+JS::Zone::getHashCodeInfallible(js::gc::Cell* cell)
+{
+    return UniqueIdToHash(getUniqueIdInfallible(cell));
+}
+
+inline uint64_t
+JS::Zone::getUniqueIdInfallible(js::gc::Cell* cell)
+{
+    uint64_t uid;
+    js::AutoEnterOOMUnsafeRegion oomUnsafe;
+    if (!getOrCreateUniqueId(cell, &uid))
+        oomUnsafe.crash("failed to allocate uid");
+    return uid;
+}
+
+inline bool
+JS::Zone::hasUniqueId(js::gc::Cell* cell)
+{
+    MOZ_ASSERT(js::CurrentThreadCanAccessZone(this) || js::CurrentThreadIsPerformingGC());
+    return uniqueIds().has(cell);
+}
+
+inline void
+JS::Zone::transferUniqueId(js::gc::Cell* tgt, js::gc::Cell* src)
+{
+    MOZ_ASSERT(src != tgt);
+    MOZ_ASSERT(!IsInsideNursery(tgt));
+    MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(runtimeFromMainThread()));
+    MOZ_ASSERT(js::CurrentThreadCanAccessZone(this));
+    MOZ_ASSERT(!uniqueIds().has(tgt));
+    uniqueIds().rekeyIfMoved(src, tgt);
+}
+
+inline void
+JS::Zone::removeUniqueId(js::gc::Cell* cell)
+{
+    MOZ_ASSERT(js::CurrentThreadCanAccessZone(this));
+    uniqueIds().remove(cell);
+}
+
+inline void
+JS::Zone::adoptUniqueIds(JS::Zone* source)
+{
+    js::AutoEnterOOMUnsafeRegion oomUnsafe;
+    for (js::gc::UniqueIdMap::Enum e(source->uniqueIds()); !e.empty(); e.popFront()) {
+        MOZ_ASSERT(!uniqueIds().has(e.front().key()));
+        if (!uniqueIds().put(e.front().key(), e.front().value()))
+            oomUnsafe.crash("failed to transfer unique ids from off-thread");
+    }
+    source->uniqueIds().clear();
+}
+
+#endif // gc_Zone_inl_h
--- a/js/src/gc/Zone.cpp
+++ b/js/src/gc/Zone.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 "gc/Zone.h"
+#include "gc/Zone-inl.h"
 
 #include "gc/FreeOp.h"
 #include "gc/Policy.h"
 #include "gc/PublicIterators.h"
 #include "jit/BaselineJIT.h"
 #include "jit/Ion.h"
 #include "jit/JitRealm.h"
 #include "vm/Debugger.h"
@@ -460,16 +460,51 @@ Zone::traceAtomCache(JSTracer* trc)
     MOZ_ASSERT(hasKeptAtoms());
     for (auto r = atomCache().all(); !r.empty(); r.popFront()) {
         JSAtom* atom = r.front().asPtrUnbarriered();
         TraceRoot(trc, &atom, "kept atom");
         MOZ_ASSERT(r.front().asPtrUnbarriered() == atom);
     }
 }
 
+void*
+Zone::onOutOfMemory(js::AllocFunction allocFunc, size_t nbytes, void* reallocPtr)
+{
+    if (!js::CurrentThreadCanAccessRuntime(runtime_))
+        return nullptr;
+    return runtimeFromMainThread()->onOutOfMemory(allocFunc, nbytes, reallocPtr);
+}
+
+void
+Zone::reportAllocationOverflow()
+{
+    js::ReportAllocationOverflow(nullptr);
+}
+
+void
+JS::Zone::maybeTriggerGCForTooMuchMalloc(js::gc::MemoryCounter& counter, TriggerKind trigger)
+{
+    JSRuntime* rt = runtimeFromAnyThread();
+
+    if (!js::CurrentThreadCanAccessRuntime(rt))
+        return;
+
+    bool wouldInterruptGC = rt->gc.isIncrementalGCInProgress() && !isCollecting();
+    if (wouldInterruptGC && !counter.shouldResetIncrementalGC(rt->gc.tunables))
+        return;
+
+    if (!rt->gc.triggerZoneGC(this, JS::gcreason::TOO_MUCH_MALLOC,
+                              counter.bytes(), counter.maxBytes()))
+    {
+        return;
+    }
+
+    counter.recordTrigger(trigger);
+}
+
 ZoneList::ZoneList()
   : head(nullptr), tail(nullptr)
 {}
 
 ZoneList::ZoneList(Zone* zone)
   : head(zone), tail(zone)
 {
     MOZ_RELEASE_ASSERT(!zone->isOnList());
--- a/js/src/gc/Zone.h
+++ b/js/src/gc/Zone.h
@@ -6,17 +6,16 @@
 
 #ifndef gc_Zone_h
 #define gc_Zone_h
 
 #include "mozilla/Atomics.h"
 #include "mozilla/HashFunctions.h"
 
 #include "gc/FindSCCs.h"
-#include "gc/GCRuntime.h"
 #include "js/GCHashTable.h"
 #include "vm/MallocProvider.h"
 #include "vm/Runtime.h"
 
 namespace js {
 
 class Debugger;
 class RegExpZone;
@@ -213,22 +212,18 @@ class Zone : public JS::shadow::Zone,
     // Iterate over all cells in the zone. See the definition of ZoneCellIter
     // in gc/GC-inl.h for the possible arguments and documentation.
     template <typename T, typename... Args>
     js::gc::ZoneCellIter<T> cellIter(Args&&... args) {
         return js::gc::ZoneCellIter<T>(const_cast<Zone*>(this), std::forward<Args>(args)...);
     }
 
     MOZ_MUST_USE void* onOutOfMemory(js::AllocFunction allocFunc, size_t nbytes,
-                                     void* reallocPtr = nullptr) {
-        if (!js::CurrentThreadCanAccessRuntime(runtime_))
-            return nullptr;
-        return runtimeFromMainThread()->onOutOfMemory(allocFunc, nbytes, reallocPtr);
-    }
-    void reportAllocationOverflow() { js::ReportAllocationOverflow(nullptr); }
+                                     void* reallocPtr = nullptr);
+    void reportAllocationOverflow();
 
     void beginSweepTypes(bool releaseTypes);
 
     bool hasMarkedRealms();
 
     void scheduleGC() { MOZ_ASSERT(!RuntimeHeapIsBusy()); gcScheduled_ = true; }
     void unscheduleGC() { gcScheduled_ = false; }
     bool isGCScheduled() { return gcScheduled_; }
@@ -254,49 +249,40 @@ class Zone : public JS::shadow::Zone,
 
     bool isCollectingFromAnyThread() const {
         if (RuntimeHeapIsCollecting())
             return gcState_ != NoGC;
         else
             return needsIncrementalBarrier();
     }
 
-    // If this returns true, all object tracing must be done with a GC marking
-    // tracer.
-    bool requireGCTracer() const {
-        JSRuntime* rt = runtimeFromAnyThread();
-        return RuntimeHeapIsMajorCollecting() && !rt->gc.isHeapCompacting() && gcState_ != NoGC;
-    }
-
     bool shouldMarkInZone() const {
         return needsIncrementalBarrier() || isGCMarking();
     }
 
     // Get a number that is incremented whenever this zone is collected, and
     // possibly at other times too.
     uint64_t gcNumber();
 
-    bool compileBarriers() const { return compileBarriers(needsIncrementalBarrier()); }
-    bool compileBarriers(bool needsIncrementalBarrier) const {
-        return needsIncrementalBarrier ||
-               runtimeFromMainThread()->hasZealMode(js::gc::ZealMode::VerifierPre);
-    }
-
     void setNeedsIncrementalBarrier(bool needs);
     const uint32_t* addressOfNeedsIncrementalBarrier() const { return &needsIncrementalBarrier_; }
 
     js::jit::JitZone* getJitZone(JSContext* cx) { return jitZone_ ? jitZone_ : createJitZone(cx); }
     js::jit::JitZone* jitZone() { return jitZone_; }
 
     bool isAtomsZone() const { return runtimeFromAnyThread()->isAtomsZone(this); }
     bool isSelfHostingZone() const { return runtimeFromAnyThread()->isSelfHostingZone(this); }
 
     void prepareForCompacting();
 
 #ifdef DEBUG
+    // If this returns true, all object tracing must be done with a GC marking
+    // tracer.
+    bool requireGCTracer() const;
+
     // For testing purposes, return the index of the sweep group which this zone
     // was swept in in the last GC.
     unsigned lastSweepGroupIndex() { return gcLastSweepGroupIndex; }
 #endif
 
     void sweepBreakpoints(js::FreeOp* fop);
     void sweepUniqueIds();
     void sweepWeakMaps();
@@ -438,31 +424,21 @@ class Zone : public JS::shadow::Zone,
     void updateMemoryCounter(js::gc::MemoryCounter& counter, size_t nbytes) {
         JSRuntime* rt = runtimeFromAnyThread();
 
         counter.update(nbytes);
         auto trigger = counter.shouldTriggerGC(rt->gc.tunables);
         if (MOZ_LIKELY(trigger == js::gc::NoTrigger) || trigger <= counter.triggered())
             return;
 
-        if (!js::CurrentThreadCanAccessRuntime(rt))
-            return;
-
-        bool wouldInterruptGC = rt->gc.isIncrementalGCInProgress() && !isCollecting();
-        if (wouldInterruptGC && !counter.shouldResetIncrementalGC(rt->gc.tunables))
-            return;
+        maybeTriggerGCForTooMuchMalloc(counter, trigger);
+    }
 
-        if (!rt->gc.triggerZoneGC(this, JS::gcreason::TOO_MUCH_MALLOC,
-                                  counter.bytes(), counter.maxBytes()))
-        {
-            return;
-        }
-
-        counter.recordTrigger(trigger);
-    }
+    void maybeTriggerGCForTooMuchMalloc(js::gc::MemoryCounter& counter,
+                                        js::gc::TriggerKind trigger);
 
     js::MainThreadData<js::UniquePtr<js::RegExpZone>> regExps_;
 
   public:
     js::RegExpZone& regExps() { return *regExps_.ref(); }
 
     JS::WeakCache<TypeDescrObjectSet>& typeDescrObjects() { return typeDescrObjects_.ref(); }
 
@@ -479,30 +455,19 @@ class Zone : public JS::shadow::Zone,
     }
     size_t GCMaxMallocBytes() const { return gcMallocCounter.maxBytes(); }
     size_t GCMallocBytes() const { return gcMallocCounter.bytes(); }
 
     void updateJitCodeMallocBytes(size_t nbytes) {
         updateMemoryCounter(jitCodeCounter, nbytes);
     }
 
-    void updateAllGCMallocCountersOnGCStart() {
-        gcMallocCounter.updateOnGCStart();
-        jitCodeCounter.updateOnGCStart();
-    }
-    void updateAllGCMallocCountersOnGCEnd(const js::AutoLockGC& lock) {
-        auto& gc = runtimeFromAnyThread()->gc;
-        gcMallocCounter.updateOnGCEnd(gc.tunables, lock);
-        jitCodeCounter.updateOnGCEnd(gc.tunables, lock);
-    }
-    js::gc::TriggerKind shouldTriggerGCForTooMuchMalloc() {
-        auto& gc = runtimeFromAnyThread()->gc;
-        return std::max(gcMallocCounter.shouldTriggerGC(gc.tunables),
-                        jitCodeCounter.shouldTriggerGC(gc.tunables));
-    }
+    void updateAllGCMallocCountersOnGCStart();
+    void updateAllGCMallocCountersOnGCEnd(const js::AutoLockGC& lock);
+    js::gc::TriggerKind shouldTriggerGCForTooMuchMalloc();
 
     void keepAtoms() {
         keepAtomsCount++;
     }
     void releaseAtoms();
     bool hasKeptAtoms() const {
         return keepAtomsCount;
     }
@@ -601,121 +566,44 @@ class Zone : public JS::shadow::Zone,
     js::ZoneData<void*> data;
 
     js::ZoneData<bool> isSystem;
 
 #ifdef DEBUG
     js::MainThreadData<unsigned> gcLastSweepGroupIndex;
 #endif
 
-    static js::HashNumber UniqueIdToHash(uint64_t uid) {
-        return mozilla::HashGeneric(uid);
-    }
+    static js::HashNumber UniqueIdToHash(uint64_t uid);
 
     // Creates a HashNumber based on getUniqueId. Returns false on OOM.
-    MOZ_MUST_USE bool getHashCode(js::gc::Cell* cell, js::HashNumber* hashp) {
-        uint64_t uid;
-        if (!getOrCreateUniqueId(cell, &uid))
-            return false;
-        *hashp = UniqueIdToHash(uid);
-        return true;
-    }
+    MOZ_MUST_USE bool getHashCode(js::gc::Cell* cell, js::HashNumber* hashp);
 
     // Gets an existing UID in |uidp| if one exists.
-    MOZ_MUST_USE bool maybeGetUniqueId(js::gc::Cell* cell, uint64_t* uidp) {
-        MOZ_ASSERT(uidp);
-        MOZ_ASSERT(js::CurrentThreadCanAccessZone(this));
-
-        // Get an existing uid, if one has been set.
-        auto p = uniqueIds().lookup(cell);
-        if (p)
-            *uidp = p->value();
-
-        return p.found();
-    }
+    MOZ_MUST_USE bool maybeGetUniqueId(js::gc::Cell* cell, uint64_t* uidp);
 
     // Puts an existing UID in |uidp|, or creates a new UID for this Cell and
     // puts that into |uidp|. Returns false on OOM.
-    MOZ_MUST_USE bool getOrCreateUniqueId(js::gc::Cell* cell, uint64_t* uidp) {
-        MOZ_ASSERT(uidp);
-        MOZ_ASSERT(js::CurrentThreadCanAccessZone(this) || js::CurrentThreadIsPerformingGC());
-
-        // Get an existing uid, if one has been set.
-        auto p = uniqueIds().lookupForAdd(cell);
-        if (p) {
-            *uidp = p->value();
-            return true;
-        }
-
-        MOZ_ASSERT(js::CurrentThreadCanAccessZone(this));
-
-        // Set a new uid on the cell.
-        *uidp = js::gc::NextCellUniqueId(runtimeFromAnyThread());
-        if (!uniqueIds().add(p, cell, *uidp))
-            return false;
+    MOZ_MUST_USE bool getOrCreateUniqueId(js::gc::Cell* cell, uint64_t* uidp);
 
-        // If the cell was in the nursery, hopefully unlikely, then we need to
-        // tell the nursery about it so that it can sweep the uid if the thing
-        // does not get tenured.
-        if (IsInsideNursery(cell) &&
-            !runtimeFromMainThread()->gc.nursery().addedUniqueIdToCell(cell))
-        {
-            uniqueIds().remove(cell);
-            return false;
-        }
-
-        return true;
-    }
-
-    js::HashNumber getHashCodeInfallible(js::gc::Cell* cell) {
-        return UniqueIdToHash(getUniqueIdInfallible(cell));
-    }
-
-    uint64_t getUniqueIdInfallible(js::gc::Cell* cell) {
-        uint64_t uid;
-        js::AutoEnterOOMUnsafeRegion oomUnsafe;
-        if (!getOrCreateUniqueId(cell, &uid))
-            oomUnsafe.crash("failed to allocate uid");
-        return uid;
-    }
+    js::HashNumber getHashCodeInfallible(js::gc::Cell* cell);
+    uint64_t getUniqueIdInfallible(js::gc::Cell* cell);
 
     // Return true if this cell has a UID associated with it.
-    MOZ_MUST_USE bool hasUniqueId(js::gc::Cell* cell) {
-        MOZ_ASSERT(js::CurrentThreadCanAccessZone(this) || js::CurrentThreadIsPerformingGC());
-        return uniqueIds().has(cell);
-    }
+    MOZ_MUST_USE bool hasUniqueId(js::gc::Cell* cell);
 
     // Transfer an id from another cell. This must only be called on behalf of a
     // moving GC. This method is infallible.
-    void transferUniqueId(js::gc::Cell* tgt, js::gc::Cell* src) {
-        MOZ_ASSERT(src != tgt);
-        MOZ_ASSERT(!IsInsideNursery(tgt));
-        MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(runtimeFromMainThread()));
-        MOZ_ASSERT(js::CurrentThreadCanAccessZone(this));
-        MOZ_ASSERT(!uniqueIds().has(tgt));
-        uniqueIds().rekeyIfMoved(src, tgt);
-    }
+    void transferUniqueId(js::gc::Cell* tgt, js::gc::Cell* src);
 
     // Remove any unique id associated with this Cell.
-    void removeUniqueId(js::gc::Cell* cell) {
-        MOZ_ASSERT(js::CurrentThreadCanAccessZone(this));
-        uniqueIds().remove(cell);
-    }
+    void removeUniqueId(js::gc::Cell* cell);
 
     // When finished parsing off-thread, transfer any UIDs we created in the
     // off-thread zone into the target zone.
-    void adoptUniqueIds(JS::Zone* source) {
-        js::AutoEnterOOMUnsafeRegion oomUnsafe;
-        for (js::gc::UniqueIdMap::Enum e(source->uniqueIds()); !e.empty(); e.popFront()) {
-            MOZ_ASSERT(!uniqueIds().has(e.front().key()));
-            if (!uniqueIds().put(e.front().key(), e.front().value()))
-                oomUnsafe.crash("failed to transfer unique ids from off-thread");
-        }
-        source->uniqueIds().clear();
-    }
+    void adoptUniqueIds(JS::Zone* source);
 
 #ifdef JSGC_HASH_TABLE_CHECKS
     // Assert that the UniqueId table has been redirected successfully.
     void checkUniqueIdTableAfterMovingGC();
 #endif
 
     bool keepShapeTables() const {
         return keepShapeTables_;
--- a/js/src/jsapi-tests/testGCUniqueId.cpp
+++ b/js/src/jsapi-tests/testGCUniqueId.cpp
@@ -6,16 +6,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "gc/GCInternals.h"
 #include "gc/Zone.h"
 #include "js/GCVector.h"
 
 #include "jsapi-tests/tests.h"
 
+#include "gc/Zone-inl.h"
+
 static void
 MinimizeHeap(JSContext* cx)
 {
     // The second collection is to force us to wait for the background
     // sweeping that the first GC started to finish.
     JS_GC(cx);
     JS_GC(cx);
     js::gc::FinishGC(cx);