diff --git a/js/src/ds/Fifo.h b/js/src/ds/Fifo.h index cea46c12ac7a..84851ac947d8 100644 --- a/js/src/ds/Fifo.h +++ b/js/src/ds/Fifo.h @@ -46,11 +46,19 @@ class Fifo private: // Maintain invariants after adding or removing entries. - void fixup() { - if (front_.empty() && !rear_.empty()) { - front_.swap(rear_); - Reverse(front_.begin(), front_.end()); + bool fixup() { + if (!front_.empty()) + return true; + + if (!front_.reserve(rear_.length())) + return false; + + while (!rear_.empty()) { + front_.infallibleAppend(mozilla::Move(rear_.back())); + rear_.popBack(); } + + return true; } public: @@ -90,7 +98,10 @@ class Fifo MOZ_MUST_USE bool pushBack(U&& u) { if (!rear_.append(mozilla::Forward(u))) return false; - fixup(); + if (!fixup()) { + rear_.popBack(); + return false; + } return true; } @@ -99,7 +110,10 @@ class Fifo MOZ_MUST_USE bool emplaceBack(Args&&... args) { if (!rear_.emplaceBack(mozilla::Forward(args)...)) return false; - fixup(); + if (!fixup()) { + rear_.popBack(); + return false; + } return true; } @@ -114,10 +128,20 @@ class Fifo } // Remove the front element from the queue. - void popFront() { + MOZ_MUST_USE bool popFront() { MOZ_ASSERT(!empty()); + T t(mozilla::Move(front())); front_.popBack(); - fixup(); + if (!fixup()) { + // Attempt to remain in a valid state by reinserting the element + // back at the front. If we can't remain in a valid state in the + // face of OOMs, crash. + AutoEnterOOMUnsafeRegion oomUnsafe; + if (!front_.append(mozilla::Move(t))) + oomUnsafe.crash("js::Fifo::popFront"); + return false; + } + return true; } // Clear all elements from the queue. diff --git a/js/src/ds/TraceableFifo.h b/js/src/ds/TraceableFifo.h index 67daad1fa7f5..95871688dc00 100644 --- a/js/src/ds/TraceableFifo.h +++ b/js/src/ds/TraceableFifo.h @@ -79,7 +79,7 @@ class MutableWrappedPtrOperations, Wrapp return fifo().emplaceBack(mozilla::Forward(args...)); } - void popFront() { fifo().popFront(); } + bool popFront() { return fifo().popFront(); } void clear() { fifo().clear(); } }; diff --git a/js/src/jsapi-tests/testGCExactRooting.cpp b/js/src/jsapi-tests/testGCExactRooting.cpp index f4dc23dab474..38d2d8003c1c 100644 --- a/js/src/jsapi-tests/testGCExactRooting.cpp +++ b/js/src/jsapi-tests/testGCExactRooting.cpp @@ -323,7 +323,7 @@ BEGIN_TEST(testTraceableFifo) bool match; CHECK(JS_StringEqualsAscii(cx, JSID_TO_STRING(shapes.front()->propid()), buffer, &match)); CHECK(match); - shapes.popFront(); + CHECK(shapes.popFront()); } CHECK(shapes.empty()); diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index 2e8c4c9b2ce3..1cc59946ab3d 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -2298,7 +2298,10 @@ Debugger::appendAllocationSite(JSContext* cx, HandleObject obj, HandleSavedFrame } if (allocationsLog.length() > maxAllocationsLogLength) { - allocationsLog.popFront(); + if (!allocationsLog.popFront()) { + ReportOutOfMemory(cx); + return false; + } MOZ_ASSERT(allocationsLog.length() == maxAllocationsLogLength); allocationsLogOverflowed = true; } diff --git a/js/src/vm/DebuggerMemory.cpp b/js/src/vm/DebuggerMemory.cpp index d7474c3aa25f..d48df08204b4 100644 --- a/js/src/vm/DebuggerMemory.cpp +++ b/js/src/vm/DebuggerMemory.cpp @@ -238,7 +238,10 @@ DebuggerMemory::drainAllocationsLog(JSContext* cx, unsigned argc, Value* vp) // Pop the front queue entry, and delete it immediately, so that the GC // sees the AllocationsLogEntry's HeapPtr barriers run atomically with // the change to the graph (the queue link). - dbg->allocationsLog.popFront(); + if (!dbg->allocationsLog.popFront()) { + ReportOutOfMemory(cx); + return false; + } } dbg->allocationsLogOverflowed = false; @@ -275,8 +278,12 @@ DebuggerMemory::setMaxAllocationsLogLength(JSContext* cx, unsigned argc, Value* Debugger* dbg = memory->getDebugger(); dbg->maxAllocationsLogLength = max; - while (dbg->allocationsLog.length() > dbg->maxAllocationsLogLength) - dbg->allocationsLog.popFront(); + while (dbg->allocationsLog.length() > dbg->maxAllocationsLogLength) { + if (!dbg->allocationsLog.popFront()) { + ReportOutOfMemory(cx); + return false; + } + } args.rval().setUndefined(); return true;