diff --git a/xpcom/base/CycleCollectedJSRuntime.cpp b/xpcom/base/CycleCollectedJSRuntime.cpp index e7aea86995e0..28957c7a208e 100644 --- a/xpcom/base/CycleCollectedJSRuntime.cpp +++ b/xpcom/base/CycleCollectedJSRuntime.cpp @@ -483,12 +483,30 @@ JSHolderMap::Entry::Entry(void* aHolder, nsScriptObjectTracer* aTracer, { } +void JSHolderMap::EntryVectorIter::Settle() { + if (Done()) { + return; + } + + Entry* entry = &mIter.Get(); + + // If the entry has been cleared, remove it and shrink the vector. + if (!entry->mHolder && !mHolderMap.RemoveEntry(mVector, entry)) { + // We removed the last entry, so reset the iterator to an empty one. + mIter = EntryVector().Iter(); + MOZ_ASSERT(Done()); + } +} + JSHolderMap::JSHolderMap() : mJSHolderMap(256) {} template inline void JSHolderMap::ForEach(F&& f, WhichHolders aWhich) { // Multi-zone JS holders must always be considered. - ForEach(mAnyZoneJSHolders, f, nullptr); + for (EntryVectorIter entry(*this, mAnyZoneJSHolders); !entry.Done(); + entry.Next()) { + f(entry->mHolder, entry->mTracer, nullptr); + } for (auto i = mPerZoneJSHolders.modIter(); !i.done(); i.next()) { if (aWhich == HoldersInGrayMarkingZones && @@ -496,30 +514,19 @@ inline void JSHolderMap::ForEach(F&& f, WhichHolders aWhich) { continue; } + JS::Zone* zone = i.get().key(); EntryVector* holders = i.get().value().get(); - ForEach(*holders, f, i.get().key()); + for (EntryVectorIter entry(*this, *holders); !entry.Done(); entry.Next()) { + MOZ_ASSERT(entry->mZone == zone); + f(entry->mHolder, entry->mTracer, zone); + } + if (holders->IsEmpty()) { i.remove(); } } } -template -inline void JSHolderMap::ForEach(EntryVector& aJSHolders, const F& f, - JS::Zone* aZone) { - for (auto iter = aJSHolders.Iter(); !iter.Done(); iter.Next()) { - Entry* entry = &iter.Get(); - - // If the entry has been cleared, remove it and shrink the vector. - if (!entry->mHolder && !RemoveEntry(aJSHolders, entry)) { - break; // Removed the last entry. - } - - MOZ_ASSERT_IF(aZone, entry->mZone == aZone); - f(entry->mHolder, entry->mTracer, aZone); - } -} - bool JSHolderMap::RemoveEntry(EntryVector& aJSHolders, Entry* aEntry) { MOZ_ASSERT(aEntry); MOZ_ASSERT(!aEntry->mHolder); diff --git a/xpcom/base/CycleCollectedJSRuntime.h b/xpcom/base/CycleCollectedJSRuntime.h index fad9ac0531bb..1e97d4609658 100644 --- a/xpcom/base/CycleCollectedJSRuntime.h +++ b/xpcom/base/CycleCollectedJSRuntime.h @@ -124,8 +124,7 @@ class JSHolderMap { mozilla::HashMap, DefaultHasher, InfallibleAllocPolicy>; - template - void ForEach(EntryVector& aJSHolders, const F& f, JS::Zone* aZone); + class EntryVectorIter; bool RemoveEntry(EntryVector& aJSHolders, Entry* aEntry); @@ -144,6 +143,35 @@ class JSHolderMap { EntryVectorMap mPerZoneJSHolders; }; +// An iterator over an EntryVector that skips over removed entries and removes +// them from the map. +class JSHolderMap::EntryVectorIter { + public: + EntryVectorIter(JSHolderMap& aMap, EntryVector& aVector) + : mHolderMap(aMap), mVector(aVector), mIter(aVector.Iter()) { + Settle(); + } + + const EntryVector& Vector() const { return mVector; } + + bool Done() const { return mIter.Done(); } + const Entry& Get() const { return mIter.Get(); } + void Next() { + mIter.Next(); + Settle(); + } + + operator const Entry*() const { return &Get(); } + const Entry* operator->() const { return &Get(); } + + private: + void Settle(); + + JSHolderMap& mHolderMap; + EntryVector& mVector; + EntryVector::IterImpl mIter; +}; + class CycleCollectedJSRuntime { friend class JSGCThingParticipant; friend class JSZoneParticipant;