Backed out 6 changesets (bug 1556321, bug 1556430, bug 1167452) for causing multiple regressions.

Backed out changeset 667da63fb2b1 (bug 1556430)
Backed out changeset 5ec3133f6457 (bug 1556321)
Backed out changeset 585157ab153a (bug 1556321)
Backed out changeset 72fc109fe0f2 (bug 1167452)
Backed out changeset 11d3f2265b35 (bug 1167452)
Backed out changeset 37f9bd277c34 (bug 1167452)
This commit is contained in:
Cosmin Sabou 2019-06-11 07:04:16 +03:00
parent 3d72a8ffe7
commit 7658be0902
22 changed files with 232 additions and 701 deletions

View File

@ -66,6 +66,12 @@ class JS_PUBLIC_API JSTracer {
// everything reachable by regular edges has been marked.
Marking,
// Same as Marking, except we have now moved on to the "weak marking
// phase", in which every marked obj/script is immediately looked up to
// see if it is a weak map key (and therefore might require marking its
// weak map value).
WeakMarking,
// A tracer that traverses the graph for the purposes of moving objects
// from the nursery to the tenured area.
Tenuring,
@ -74,7 +80,12 @@ class JS_PUBLIC_API JSTracer {
// Traversing children is the responsibility of the callback.
Callback
};
bool isMarkingTracer() const { return tag_ == TracerKindTag::Marking; }
bool isMarkingTracer() const {
return tag_ == TracerKindTag::Marking || tag_ == TracerKindTag::WeakMarking;
}
bool isWeakMarkingTracer() const {
return tag_ == TracerKindTag::WeakMarking;
}
bool isTenuringTracer() const { return tag_ == TracerKindTag::Tenuring; }
bool isCallbackTracer() const { return tag_ == TracerKindTag::Callback; }
inline JS::CallbackTracer* asCallbackTracer();

View File

@ -92,10 +92,7 @@ bool WeakMapObject::get(JSContext* cx, unsigned argc, Value* vp) {
if (ObjectValueMap* map =
args.thisv().toObject().as<WeakMapObject>().getMap()) {
JSObject* key = &args[0].toObject();
// The lookup here is only used for the removal, so we can skip the read
// barrier. This is not very important for performance, but makes it easier
// to test nonbarriered removal from internal weakmaps (eg Debugger maps.)
if (ObjectValueMap::Ptr ptr = map->unbarrieredLookup(key)) {
if (ObjectValueMap::Ptr ptr = map->lookup(key)) {
map->remove(ptr);
args.rval().setBoolean(true);
return true;

View File

@ -439,34 +439,6 @@ static MOZ_ALWAYS_INLINE void AssertValidToSkipBarrier(TenuredCell* thing) {
AssertValidToSkipBarrier(next);
}
// Like gc::MarkColor but allows the possibility of the cell being
// unmarked. Order is important here, with white being 'least marked'
// and black being 'most marked'.
enum class CellColor : uint8_t { White = 0, Gray = 1, Black = 2 };
inline CellColor GetCellColor(Cell* cell) {
if (cell->isMarkedBlack()) {
return CellColor::Black;
}
if (cell->isMarkedGray()) {
return CellColor::Gray;
}
return CellColor::White;
}
static inline CellColor GetCellColor(MarkColor color) {
return color == MarkColor::Black ? CellColor::Black : CellColor::Gray;
}
static inline MarkColor GetMarkColor(CellColor color) {
MOZ_ASSERT(color != CellColor::White);
return color == CellColor::Black ? MarkColor::Black : MarkColor::Gray;
}
static inline bool IsMarked(CellColor c) { return c != CellColor::White; }
#ifdef DEBUG
/* static */ void Cell::assertThingIsNotGray(Cell* cell) {

View File

@ -4596,46 +4596,39 @@ void GCRuntime::updateMallocCountersOnGCStart() {
}
template <class ZoneIterT>
IncrementalProgress GCRuntime::markWeakReferences(gcstats::PhaseKind phase,
SliceBudget& budget) {
void GCRuntime::markWeakReferences(gcstats::PhaseKind phase) {
MOZ_ASSERT(marker.isDrained());
gcstats::AutoPhase ap1(stats(), phase);
// We may have already entered weak marking mode, in which case this will do
// nothing.
marker.enterWeakMarkingMode();
// This is not strictly necessary; if we yield here, we could run the mutator
// in weak marking mode and unmark gray would end up doing the key lookups.
// But it seems better to not slow down barriers. Re-entering weak marking
// mode will be fast since already-processed markables have been removed.
auto leaveOnExit =
mozilla::MakeScopeExit([&] { marker.leaveWeakMarkingMode(); });
// TODO bug 1167452: Make weak marking incremental
drainMarkStack();
bool markedAny = true;
while (markedAny) {
if (!marker.markUntilBudgetExhausted(budget)) {
return NotFinished;
}
markedAny = false;
if (!marker.isWeakMarking()) {
for (;;) {
bool markedAny = false;
if (!marker.isWeakMarkingTracer()) {
for (ZoneIterT zone(rt); !zone.done(); zone.next()) {
markedAny |= WeakMapBase::markZoneIteratively(zone, &marker);
}
}
markedAny |= Debugger::markIteratively(&marker);
markedAny |= jit::JitRuntime::MarkJitcodeGlobalTableIteratively(&marker);
if (!markedAny) {
break;
}
drainMarkStack();
}
MOZ_ASSERT(marker.isDrained());
return Finished;
marker.leaveWeakMarkingMode();
}
IncrementalProgress GCRuntime::markWeakReferencesInCurrentGroup(
gcstats::PhaseKind phase, SliceBudget& budget) {
return markWeakReferences<SweepGroupZonesIter>(phase, budget);
void GCRuntime::markWeakReferencesInCurrentGroup(gcstats::PhaseKind phase) {
markWeakReferences<SweepGroupZonesIter>(phase);
}
template <class ZoneIterT>
@ -4655,9 +4648,8 @@ void GCRuntime::markGrayRoots(gcstats::PhaseKind phase) {
}
}
IncrementalProgress GCRuntime::markAllWeakReferences(gcstats::PhaseKind phase,
SliceBudget& budget) {
return markWeakReferences<GCZonesIter>(phase, budget);
void GCRuntime::markAllWeakReferences(gcstats::PhaseKind phase) {
markWeakReferences<GCZonesIter>(phase);
}
void GCRuntime::markAllGrayReferences(gcstats::PhaseKind phase) {
@ -4745,7 +4737,7 @@ void js::gc::MarkingValidator::nonIncrementalMark(AutoGCSession& session) {
* collecting.
*/
WeakMapColors markedWeakMaps;
WeakMapSet markedWeakMaps;
/*
* For saving, smush all of the keys into one big table and split them back
@ -4821,8 +4813,7 @@ void js::gc::MarkingValidator::nonIncrementalMark(AutoGCSession& session) {
gcstats::AutoPhase ap1(gc->stats(), gcstats::PhaseKind::SWEEP);
gcstats::AutoPhase ap2(gc->stats(), gcstats::PhaseKind::SWEEP_MARK);
auto unlimited = SliceBudget::unlimited();
gc->markAllWeakReferences(gcstats::PhaseKind::SWEEP_MARK_WEAK, unlimited);
gc->markAllWeakReferences(gcstats::PhaseKind::SWEEP_MARK_WEAK);
/* Update zone state for gray marking. */
for (GCZonesIter zone(runtime); !zone.done(); zone.next()) {
@ -4832,8 +4823,7 @@ void js::gc::MarkingValidator::nonIncrementalMark(AutoGCSession& session) {
AutoSetMarkColor setColorGray(gc->marker, MarkColor::Gray);
gc->markAllGrayReferences(gcstats::PhaseKind::SWEEP_MARK_GRAY);
gc->markAllWeakReferences(gcstats::PhaseKind::SWEEP_MARK_GRAY_WEAK,
unlimited);
gc->markAllWeakReferences(gcstats::PhaseKind::SWEEP_MARK_GRAY_WEAK);
/* Restore zone state. */
for (GCZonesIter zone(runtime); !zone.done(); zone.next()) {
@ -5504,18 +5494,12 @@ IncrementalProgress GCRuntime::endMarkingSweepGroup(FreeOp* fop,
gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::SWEEP_MARK);
if (markWeakReferencesInCurrentGroup(gcstats::PhaseKind::SWEEP_MARK_WEAK,
budget) == NotFinished) {
return NotFinished;
}
markWeakReferencesInCurrentGroup(gcstats::PhaseKind::SWEEP_MARK_WEAK);
AutoSetMarkColor setColorGray(marker, MarkColor::Gray);
// Mark transitively inside the current compartment group.
if (markWeakReferencesInCurrentGroup(gcstats::PhaseKind::SWEEP_MARK_GRAY_WEAK,
budget) == NotFinished) {
return NotFinished;
}
markWeakReferencesInCurrentGroup(gcstats::PhaseKind::SWEEP_MARK_GRAY_WEAK);
MOZ_ASSERT(marker.isDrained());

View File

@ -225,26 +225,6 @@ class MarkStackIter {
} /* namespace gc */
enum MarkingState : uint8_t {
// Have not yet started marking.
NotActive,
// Main marking mode. Weakmap marking will be populating the weakKeys tables
// but not consulting them. The state will transition to WeakMarking until it
// is done, then back to RegularMarking.
RegularMarking,
// Same as RegularMarking except now every marked obj/script is immediately
// looked up in the weakKeys table to see if it is a weakmap key, and
// therefore might require marking its value. Transitions back to
// RegularMarking when done.
WeakMarking,
// Same as RegularMarking, but we OOMed (or obeyed a directive in the test
// marking queue) and fell back to iterating until the next GC.
IterativeMarking
};
class GCMarker : public JSTracer {
public:
explicit GCMarker(JSRuntime* rt);
@ -311,24 +291,12 @@ class GCMarker : public JSTracer {
void enterWeakMarkingMode();
void leaveWeakMarkingMode();
void abortLinearWeakMarking() {
if (state == MarkingState::WeakMarking) {
leaveWeakMarkingMode();
}
state = MarkingState::IterativeMarking;
leaveWeakMarkingMode();
linearWeakMarkingDisabled_ = true;
}
void delayMarkingChildren(gc::Cell* cell);
// Remove <map,toRemove> from the weak keys table indexed by 'key'.
void forgetWeakKey(js::gc::WeakKeyTable& weakKeys, WeakMapBase* map,
gc::Cell* keyOrDelegate, gc::Cell* keyToRemove);
// Purge all mention of 'map' from the weak keys table.
void forgetWeakMap(WeakMapBase* map, Zone* zone);
// 'delegate' is no longer the delegate of 'key'.
void severWeakDelegate(JSObject* key, JSObject* delegate);
bool isDrained() { return isMarkStackEmpty() && !delayedMarkingList; }
// The mark queue is a testing-only feature for controlling mark ordering and
@ -366,8 +334,6 @@ class GCMarker : public JSTracer {
template <typename T>
void markImplicitEdges(T* oldThing);
bool isWeakMarking() const { return state == MarkingState::WeakMarking; }
private:
#ifdef DEBUG
void checkZone(void* p);
@ -457,16 +423,22 @@ class GCMarker : public JSTracer {
/* Whether more work has been added to the delayed marking list. */
MainThreadData<bool> delayedMarkingWorkAdded;
/*
* If the weakKeys table OOMs, disable the linear algorithm and fall back
* to iterating until the next GC.
*/
MainThreadData<bool> linearWeakMarkingDisabled_;
/* The count of marked objects during GC. */
size_t markCount;
/* Track the state of marking. */
MainThreadData<MarkingState> state;
#ifdef DEBUG
/* Count of arenas that are currently in the stack. */
MainThreadData<size_t> markLaterArenas;
/* Assert that start and stop are called with correct ordering. */
MainThreadData<bool> started;
/* The test marking queue might want to be marking a particular color. */
mozilla::Maybe<js::gc::MarkColor> queueMarkColor;
@ -513,11 +485,6 @@ class MOZ_RAII AutoSetMarkColor {
marker_.setMarkColor(newColor);
}
AutoSetMarkColor(GCMarker& marker, CellColor newColor)
: AutoSetMarkColor(marker, GetMarkColor(newColor)) {
MOZ_ASSERT(newColor != CellColor::White);
}
~AutoSetMarkColor() { marker_.setMarkColor(initialColor_); }
};

View File

@ -624,15 +624,12 @@ class GCRuntime {
gcstats::PhaseKind phase);
void drainMarkStack();
template <class ZoneIterT>
IncrementalProgress markWeakReferences(gcstats::PhaseKind phase,
SliceBudget& budget);
IncrementalProgress markWeakReferencesInCurrentGroup(gcstats::PhaseKind phase,
SliceBudget& budget);
void markWeakReferences(gcstats::PhaseKind phase);
void markWeakReferencesInCurrentGroup(gcstats::PhaseKind phase);
template <class ZoneIterT>
void markGrayRoots(gcstats::PhaseKind phase);
void markBufferedGrayRoots(JS::Zone* zone);
IncrementalProgress markAllWeakReferences(gcstats::PhaseKind phase,
SliceBudget& budget);
void markAllWeakReferences(gcstats::PhaseKind phase);
void markAllGrayReferences(gcstats::PhaseKind phase);
void beginSweepPhase(JS::GCReason reason, AutoGCSession& session);

View File

@ -110,12 +110,8 @@ PhaseKindGraphRoots = [
UnmarkGrayPhaseKind,
]),
PhaseKind("SWEEP_MARK_INCOMING_GRAY", "Mark Incoming Gray Pointers", 14),
PhaseKind("SWEEP_MARK_GRAY", "Mark Gray", 15, [
UnmarkGrayPhaseKind,
]),
PhaseKind("SWEEP_MARK_GRAY_WEAK", "Mark Gray and Weak", 16, [
UnmarkGrayPhaseKind,
]),
PhaseKind("SWEEP_MARK_GRAY", "Mark Gray", 15),
PhaseKind("SWEEP_MARK_GRAY_WEAK", "Mark Gray and Weak", 16)
]),
PhaseKind("FINALIZE_START", "Finalize Start Callbacks", 17, [
PhaseKind("WEAK_ZONES_CALLBACK", "Per-Slice Weak Callback", 57),

View File

@ -35,7 +35,6 @@
#include "gc/GC-inl.h"
#include "gc/Nursery-inl.h"
#include "gc/PrivateIterators-inl.h"
#include "gc/WeakMap-inl.h"
#include "gc/Zone-inl.h"
#include "vm/GeckoProfiler-inl.h"
#include "vm/NativeObject-inl.h"
@ -617,6 +616,11 @@ void GCMarker::markEphemeronValues(gc::Cell* markedCell,
DebugOnly<size_t> initialLen = values.length();
for (const auto& markable : values) {
if (color == gc::MarkColor::Black &&
markable.weakmap->markColor == gc::MarkColor::Gray) {
continue;
}
markable.weakmap->markEntry(this, markedCell, markable.key);
}
@ -626,76 +630,9 @@ void GCMarker::markEphemeronValues(gc::Cell* markedCell,
MOZ_ASSERT(values.length() == initialLen);
}
void GCMarker::forgetWeakKey(js::gc::WeakKeyTable& weakKeys, WeakMapBase* map,
gc::Cell* keyOrDelegate, gc::Cell* keyToRemove) {
// Find and remove the exact pair <map,keyToRemove> from the values of the
// weak keys table.
//
// This function is called when 'keyToRemove' is removed from a weakmap
// 'map'. If 'keyToRemove' has a delegate, then the delegate will be used as
// the lookup key in gcWeakKeys; otherwise, 'keyToRemove' itself will be. In
// either case, 'keyToRemove' is what we will be filtering out of the
// Markable values in the weakKey table.
auto p = weakKeys.get(keyOrDelegate);
// Note that this is not guaranteed to find anything. The key will have
// only been inserted into the weakKeys table if it was unmarked when the
// map was traced.
if (p) {
EraseIf(p->value, [map, keyToRemove](const WeakMarkable& markable) -> bool {
// Note that we should only have had the key in weakKeys if the map
// was marked.
MOZ_ASSERT(IsMarked(markable.weakmap->markColor));
return (markable.weakmap == map) && (markable.key == keyToRemove);
});
}
} // namespace js
void GCMarker::forgetWeakMap(WeakMapBase* map, Zone* zone) {
for (auto p = zone->gcNurseryWeakKeys().all(); !p.empty(); p.popFront()) {
EraseIf(p.front().value, [map](const WeakMarkable& markable) -> bool {
return markable.weakmap == map;
});
}
for (auto p = zone->gcWeakKeys().all(); !p.empty(); p.popFront()) {
EraseIf(p.front().value, [map](const WeakMarkable& markable) -> bool {
return markable.weakmap == map;
});
}
}
// 'delegate' is no longer the delegate of 'key'.
void GCMarker::severWeakDelegate(JSObject* key, JSObject* delegate) {
JS::Zone* zone = delegate->zone();
bool found = true;
while (found) {
found = false;
auto p = zone->gcWeakKeys(delegate).get(delegate);
if (!p) {
// The key may not even be in the weak key table, eg if it was already
// marked when marking the map.
break;
}
for (auto i = p->value.begin(); i != p->value.end(); i++) {
if (i->key == key) {
WeakMapBase* weakmap = i->weakmap;
p->value.erase(i);
// Warning: the key and delegate can be same-zone (even though they're
// different-compartment). This line will insert into the weak keys
// table and so can invalidate the pointer |p| on a rehash. So redo the
// lookup after each one of these.
weakmap->postSeverDelegate(this, key);
found = true;
break;
}
}
}
}
template <typename T>
void GCMarker::markImplicitEdgesHelper(T markedThing) {
if (state != MarkingState::WeakMarking) {
if (!isWeakMarkingTracer()) {
return;
}
@ -1597,7 +1534,7 @@ GCMarker::MarkQueueProgress GCMarker::processMarkQueue() {
return QueueYielded;
} else if (js::StringEqualsAscii(str, "enter-weak-marking-mode") ||
js::StringEqualsAscii(str, "abort-weak-marking-mode")) {
if (state == MarkingState::RegularMarking) {
if (!isWeakMarkingTracer() && !linearWeakMarkingDisabled_) {
// We can't enter weak marking mode at just any time, so instead
// we'll stop processing the queue and continue on with the GC. Once
// we enter weak marking mode, we can continue to the rest of the
@ -2429,11 +2366,11 @@ GCMarker::GCMarker(JSRuntime* rt)
grayStack(),
color(MarkColor::Black),
delayedMarkingList(nullptr),
delayedMarkingWorkAdded(false),
state(MarkingState::NotActive)
delayedMarkingWorkAdded(false)
#ifdef DEBUG
,
markLaterArenas(0),
started(false),
strictCompartmentChecking(false),
markQueue(rt),
queuePos(0)
@ -2446,9 +2383,12 @@ bool GCMarker::init(JSGCMode gcMode) {
}
void GCMarker::start() {
MOZ_ASSERT(state == MarkingState::NotActive);
state = MarkingState::RegularMarking;
#ifdef DEBUG
MOZ_ASSERT(!started);
started = true;
#endif
color = MarkColor::Black;
linearWeakMarkingDisabled_ = false;
#ifdef DEBUG
queuePos = 0;
@ -2460,11 +2400,15 @@ void GCMarker::start() {
}
void GCMarker::stop() {
#ifdef DEBUG
MOZ_ASSERT(isDrained());
MOZ_ASSERT(started);
started = false;
MOZ_ASSERT(!delayedMarkingList);
MOZ_ASSERT(markLaterArenas == 0);
MOZ_ASSERT(state != MarkingState::NotActive);
state = MarkingState::NotActive;
#endif
/* Free non-ballast stack memory. */
blackStack.clear();
@ -2565,89 +2509,50 @@ void GCMarker::repush(JSObject* obj) {
void GCMarker::enterWeakMarkingMode() {
MOZ_ASSERT(runtime()->gc.nursery().isEmpty());
MOZ_ASSERT(isMarkingTracer());
if (state != MarkingState::RegularMarking) {
MOZ_ASSERT(tag_ == TracerKindTag::Marking);
if (linearWeakMarkingDisabled_) {
return;
}
if (weakMapAction() != ExpandWeakMaps) {
return; // This marker does not do linear-time weak marking.
}
// During weak marking mode, we maintain a table mapping weak keys to
// entries in known-live weakmaps. Initialize it with the keys of marked
// weakmaps -- or more precisely, the keys of marked weakmaps that are
// mapped to not yet live values. (Once bug 1167452 implements incremental
// weakmap marking, this initialization step will become unnecessary, as
// the table will already hold all such keys.)
if (weakMapAction() == ExpandWeakMaps) {
tag_ = TracerKindTag::WeakMarking;
// Set state before doing anything else, so any new key that is marked
// during the following gcWeakKeys scan will itself be looked up in
// gcWeakKeys and marked according to ephemeron rules.
state = MarkingState::WeakMarking;
// If there was an 'enter-weak-marking-mode' token in the queue, then it
// and everything after it will still be in the queue so we can process
// them now.
while (processMarkQueue() == QueueYielded) {
};
// If there was an 'enter-weak-marking-mode' token in the queue, then it
// and everything after it will still be in the queue so we can process
// them now.
while (processMarkQueue() == QueueYielded) {
};
// gcWeakKeys contains the keys from all weakmaps marked so far, or at least
// the keys that might still need to be marked through. Scan through
// gcWeakKeys and mark all values whose keys are marked. This marking may
// recursively mark through other weakmap entries (by looking them up in
// gcWeakKeys, since we are now in WeakMarking mode). Once the mark stack is
// drained, the end result is a consistent state where all values are marked
// if both their map and key are marked -- though note that we may later leave
// weak marking mode, do some more marking, and then enter back in.
for (SweepGroupZonesIter zone(runtime(), js::SkipAtoms); !zone.done();
zone.next()) {
if (!zone->isGCMarking()) {
continue;
}
MOZ_ASSERT(zone->gcNurseryWeakKeys().count() == 0);
// An OrderedHashMap::Range stays valid even when the underlying table
// (zone->gcWeakKeys) is mutated, which is useful here since we may add
// additional entries while iterating over the Range.
gc::WeakKeyTable::Range r = zone->gcWeakKeys().all();
while (!r.empty()) {
gc::Cell* key = r.front().key;
gc::CellColor keyColor = GetCellColor(key);
if (IsMarked(keyColor)) {
MOZ_ASSERT(key == r.front().key);
auto& markables = r.front().value;
r.popFront(); // Pop before any mutations happen.
size_t end = markables.length();
for (size_t i = 0; i < end; i++) {
WeakMarkable& v = markables[i];
// Note: if the key is marked gray but not black, then the markables
// vector may be appended to within this loop body. So iterate just
// over the ones from before weak marking mode was switched on.
v.weakmap->markEntry(this, key, v.key);
for (SweepGroupZonesIter zone(runtime()); !zone.done(); zone.next()) {
for (WeakMapBase* m : zone->gcWeakMapList()) {
if (m->marked) {
(void)m->markEntries(this);
}
if (keyColor == gc::CellColor::Black) {
// We can't mark the key any more than already is, so it no longer
// needs to be in the weak keys table.
if (end == markables.length()) {
bool found;
zone->gcWeakKeys().remove(key, &found);
} else {
markables.erase(markables.begin(), &markables[end]);
}
}
} else {
r.popFront();
}
}
}
}
void GCMarker::leaveWeakMarkingMode() {
MOZ_ASSERT_IF(weakMapAction() == ExpandWeakMaps,
state == MarkingState::WeakMarking ||
state == MarkingState::IterativeMarking);
if (state != MarkingState::IterativeMarking) {
state = MarkingState::RegularMarking;
}
MOZ_ASSERT_IF(
weakMapAction() == ExpandWeakMaps && !linearWeakMarkingDisabled_,
tag_ == TracerKindTag::WeakMarking);
tag_ = TracerKindTag::Marking;
// The gcWeakKeys table is still populated and may be used during a future
// weak marking mode within this GC.
// Table is expensive to maintain when not in weak marking mode, so we'll
// rebuild it upon entry rather than allow it to contain stale data.
AutoEnterOOMUnsafeRegion oomUnsafe;
for (GCZonesIter zone(runtime()); !zone.done(); zone.next()) {
if (!zone->gcWeakKeys().clear()) {
oomUnsafe.crash("clearing weak keys in GCMarker::leaveWeakMarkingMode()");
}
}
}
void GCMarker::delayMarkingChildren(Cell* cell) {
@ -2796,7 +2701,7 @@ void gc::PushArena(GCMarker* gcmarker, Arena* arena) {
#ifdef DEBUG
void GCMarker::checkZone(void* p) {
MOZ_ASSERT(state != MarkingState::NotActive);
MOZ_ASSERT(started);
DebugOnly<Cell*> cell = static_cast<Cell*>(p);
MOZ_ASSERT_IF(cell->isTenured(), cell->asTenured().zone()->isCollecting());
}

View File

@ -763,7 +763,8 @@ bool js::gc::CheckWeakMapEntryMarking(const WeakMapBase* map, Cell* key,
Zone* valueZone = GetCellZoneFromAnyThread(value);
MOZ_ASSERT(valueZone == zone || valueZone->isAtomsZone());
CellColor mapColor = map->markColor;
CellColor mapColor =
map->markColor == MarkColor::Black ? CellColor::Black : CellColor::Gray;
if (object && GetCellColor(object) != mapColor) {
fprintf(stderr, "WeakMap object is marked differently to the map\n");
fprintf(stderr, "(map %p is %s, object %p is %s)\n", map,

View File

@ -16,12 +16,32 @@
namespace js {
namespace gc {
// Like gc::MarkColor but allows the possibility of the cell being
// unmarked.
enum class CellColor : uint8_t {
White = 0,
Gray = uint8_t(MarkColor::Gray),
Black = uint8_t(MarkColor::Black)
};
static constexpr CellColor AllCellColors[] = {CellColor::White, CellColor::Gray,
CellColor::Black};
static constexpr CellColor MarkedCellColors[] = {CellColor::Gray,
CellColor::Black};
inline CellColor GetCellColor(Cell* cell) {
if (cell->isMarkedBlack()) {
return CellColor::Black;
}
if (cell->isMarkedGray()) {
return CellColor::Gray;
}
return CellColor::White;
}
inline CellColor ExpectedWeakMapValueColor(CellColor keyColor,
CellColor mapColor) {
return Min(keyColor, mapColor);

View File

@ -8,15 +8,12 @@
#define gc_WeakMap_inl_h
#include "gc/WeakMap.h"
#include "gc/PublicIterators.h"
#include "gc/Zone.h"
#include "js/TraceKind.h"
#include "vm/JSContext.h"
namespace js {
namespace gc {
namespace detail {
template <typename T>
static T extractUnbarriered(const WriteBarriered<T>& v) {
@ -28,45 +25,18 @@ static T* extractUnbarriered(T* v) {
return v;
}
template <typename T>
static JS::Zone* GetZone(T t) {
return t->zone();
inline /* static */ JSObject* WeakMapBase::getDelegate(JSObject* key) {
return UncheckedUnwrapWithoutExpose(key);
}
static JS::Zone* GetZone(js::HeapPtr<JS::Value>& t) {
if (!t.isGCThing()) {
return nullptr;
}
return t.toGCThing()->asTenured().zone();
}
// Only objects have delegates, so default to returning nullptr. Note that some
// compilation units will only ever use the object version.
static MOZ_MAYBE_UNUSED JSObject* GetDelegateHelper(gc::Cell* key) {
inline /* static */ JSObject* WeakMapBase::getDelegate(JSScript* script) {
return nullptr;
}
static JSObject* GetDelegateHelper(JSObject* key) {
JSObject* delegate = UncheckedUnwrapWithoutExpose(key);
return (key == delegate) ? nullptr : delegate;
inline /* static */ JSObject* WeakMapBase::getDelegate(LazyScript* script) {
return nullptr;
}
} /* namespace detail */
} /* namespace gc */
// Use a helper function to do overload resolution to handle cases like
// Heap<ObjectSubclass*>: find everything that is convertible to JSObject* (and
// avoid calling barriers).
template <typename T>
inline /* static */ JSObject* WeakMapBase::getDelegate(const T& key) {
using namespace gc::detail;
return GetDelegateHelper(extractUnbarriered(key));
}
template <>
inline /* static */ JSObject* WeakMapBase::getDelegate(gc::Cell* const&) =
delete;
template <class K, class V>
WeakMap<K, V>::WeakMap(JSContext* cx, JSObject* memOf)
: Base(cx->zone()), WeakMapBase(memOf, cx->zone()) {
@ -82,22 +52,11 @@ WeakMap<K, V>::WeakMap(JSContext* cx, JSObject* memOf)
zone()->gcWeakMapList().insertFront(this);
if (zone()->wasGCStarted()) {
markColor = CellColor::Black;
marked = true;
markColor = gc::MarkColor::Black;
}
}
namespace gc {
// Compute the correct color to mark a weakmap entry value based on the map and
// key colors.
struct AutoSetValueColor : gc::AutoSetMarkColor {
AutoSetValueColor(GCMarker& marker, CellColor mapColor, CellColor keyColor)
: gc::AutoSetMarkColor(
marker, mapColor == keyColor ? mapColor : CellColor::Gray) {}
};
} // namespace gc
// Trace a WeakMap entry based on 'markedCell' getting marked, where 'origKey'
// is the key in the weakmap. These will probably be the same, but can be
// different eg when markedCell is a delegate for origKey.
@ -105,40 +64,25 @@ struct AutoSetValueColor : gc::AutoSetMarkColor {
// This implementation does not use 'markedCell'; it looks up origKey and checks
// the mark bits on everything it cares about, one of which will be
// markedCell. But a subclass might use it to optimize the liveness check.
//
// markEntry is called when encountering a weakmap key during marking, or when
// entering weak marking mode.
template <class K, class V>
void WeakMap<K, V>::markEntry(GCMarker* marker, gc::Cell* markedCell,
gc::Cell* origKey) {
using namespace gc::detail;
MOZ_ASSERT(marked);
Ptr p = Base::lookup(static_cast<Lookup>(origKey));
// We should only be processing <weakmap,key> pairs where the key exists in
// the weakmap. Such pairs are inserted when a weakmap is marked, and are
// removed by barriers if the key is removed from the weakmap. Failure here
// probably means gcWeakKeys is not being properly traced during a minor GC,
// or the weakmap keys are not being updated when tenured.
MOZ_ASSERT(p.found());
K key(p->key());
MOZ_ASSERT((markedCell == extractUnbarriered(key)) ||
(markedCell == getDelegate(key)));
CellColor delegateColor = getDelegateColor(key);
if (IsMarked(delegateColor) && key->zone()->isGCMarking()) {
gc::AutoSetMarkColor autoColor(*marker, delegateColor);
if (marker->isMarked(&key)) {
TraceEdge(marker, &p->value(), "ephemeron value");
} else if (keyNeedsMark(marker, key)) {
TraceEdge(marker, &p->value(), "WeakMap ephemeron value");
TraceEdge(marker, &key, "proxy-preserved WeakMap ephemeron key");
MOZ_ASSERT(key == p->key(), "no moving GC");
}
CellColor keyColor = getCellColor(key);
if (IsMarked(keyColor)) {
JS::Zone* valueZone = GetZone(p->value());
if (valueZone && valueZone->isGCMarking()) {
gc::AutoSetValueColor autoColor(*marker, markColor, keyColor);
TraceEdge(marker, &p->value(), "WeakMap ephemeron value");
}
MOZ_ASSERT(key == p->key()); // No moving
}
key.unsafeSet(nullptr); // Prevent destructor from running barriers.
}
template <class K, class V>
@ -154,12 +98,13 @@ void WeakMap<K, V>::trace(JSTracer* trc) {
// Don't change the map color from black to gray. This can happen when a
// barrier pushes the map object onto the black mark stack when it's already
// present on the gray mark stack, which is marked later.
if (markColor == CellColor::Black &&
if (marked && markColor == gc::MarkColor::Black &&
marker->markColor() == gc::MarkColor::Gray) {
return;
}
markColor = GetCellColor(marker->markColor());
marked = true;
markColor = marker->markColor();
(void)markEntries(marker);
return;
}
@ -186,7 +131,8 @@ template <class K, class V>
/* static */ void WeakMap<K, V>::addWeakEntry(
GCMarker* marker, gc::Cell* key, const gc::WeakMarkable& markable) {
Zone* zone = key->asTenured().zone();
auto& weakKeys = zone->gcWeakKeys(key);
auto& weakKeys =
gc::IsInsideNursery(key) ? zone->gcNurseryWeakKeys() : zone->gcWeakKeys();
auto p = weakKeys.get(key);
if (p) {
gc::WeakEntryVector& weakEntries = p->value;
@ -204,45 +150,39 @@ template <class K, class V>
template <class K, class V>
bool WeakMap<K, V>::markEntries(GCMarker* marker) {
MOZ_ASSERT(IsMarked(markColor));
MOZ_ASSERT(marked);
if (marker->markColor() == gc::MarkColor::Black &&
markColor == gc::MarkColor::Gray) {
return false;
}
bool markedAny = false;
for (Enum e(*this); !e.empty(); e.popFront()) {
// If the entry is live, ensure its key and value are marked.
CellColor keyColor = getCellColor(e.front().key().get());
CellColor delegateColor = getDelegateColor(e.front().key());
if (IsMarked(delegateColor) && keyColor < delegateColor) {
gc::AutoSetMarkColor autoColor(*marker, delegateColor);
bool keyIsMarked = marker->isMarked(&e.front().mutableKey());
if (!keyIsMarked && keyNeedsMark(marker, e.front().key())) {
TraceEdge(marker, &e.front().mutableKey(),
"proxy-preserved WeakMap entry key");
keyIsMarked = true;
markedAny = true;
keyColor = delegateColor;
}
if (IsMarked(keyColor)) {
gc::AutoSetValueColor autoColor(*marker, markColor, keyColor);
if (keyIsMarked) {
if (!marker->isMarked(&e.front().value())) {
TraceEdge(marker, &e.front().value(), "WeakMap entry value");
markedAny = true;
}
}
// Changes in the map's mark color will be handled in this code, but
// changes in the key's mark color are handled through the weak keys table.
// So we only need to populate the table if the key is less marked than the
// map, to catch later updates in the key's mark color.
if (keyColor < markColor) {
MOZ_ASSERT(marker->weakMapAction() == ExpandWeakMaps);
// Entry is not yet known to be live. Record this weakmap and the lookup
// key in the list of weak keys. If the key has a delegate, then the
// lookup key is the delegate (because marking the key will end up
// marking the delegate and thereby mark the entry.)
gc::Cell* weakKey = gc::detail::extractUnbarriered(e.front().key());
} else if (marker->isWeakMarkingTracer()) {
// Entry is not yet known to be live. Record this weakmap and
// the lookup key in the list of weak keys. Also record the
// delegate, if any, because marking the delegate also marks
// the entry.
gc::Cell* weakKey = extractUnbarriered(e.front().key());
gc::WeakMarkable markable(this, weakKey);
addWeakEntry(marker, weakKey, markable);
if (JSObject* delegate = getDelegate(e.front().key())) {
addWeakEntry(marker, delegate, markable);
} else {
addWeakEntry(marker, weakKey, markable);
}
}
}
@ -251,32 +191,25 @@ bool WeakMap<K, V>::markEntries(GCMarker* marker) {
}
template <class K, class V>
void WeakMap<K, V>::postSeverDelegate(GCMarker* marker, gc::Cell* key) {
if (IsMarked(markColor)) {
// We only stored the delegate, not the key, and we're severing the
// delegate from the key. So store the key.
gc::WeakMarkable markable(this, key);
addWeakEntry(marker, key, markable);
}
}
template <class K, class V>
inline WeakMapBase::CellColor WeakMap<K, V>::getDelegateColor(
JSObject* key) const {
inline bool WeakMap<K, V>::keyNeedsMark(GCMarker* marker, JSObject* key) const {
JSObject* delegate = getDelegate(key);
return delegate ? getCellColor(delegate) : CellColor::White;
/*
* Check if the delegate is marked with any color to properly handle
* gray marking when the key's delegate is black and the map is gray.
*/
return delegate && marker->isMarkedUnbarriered(&delegate);
}
template <class K, class V>
inline WeakMapBase::CellColor WeakMap<K, V>::getDelegateColor(
JSScript* script) const {
return CellColor::White;
inline bool WeakMap<K, V>::keyNeedsMark(GCMarker* marker,
JSScript* script) const {
return false;
}
template <class K, class V>
inline WeakMapBase::CellColor WeakMap<K, V>::getDelegateColor(
LazyScript* script) const {
return CellColor::White;
inline bool WeakMap<K, V>::keyNeedsMark(GCMarker* marker,
LazyScript* script) const {
return false;
}
template <class K, class V>

View File

@ -23,7 +23,7 @@ using namespace js;
using namespace js::gc;
WeakMapBase::WeakMapBase(JSObject* memOf, Zone* zone)
: memberOf(memOf), zone_(zone), markColor(CellColor::White) {
: memberOf(memOf), zone_(zone), marked(false), markColor(MarkColor::Black) {
MOZ_ASSERT_IF(memberOf, memberOf->compartment()->zone() == zone);
}
@ -32,14 +32,8 @@ WeakMapBase::~WeakMapBase() {
}
void WeakMapBase::unmarkZone(JS::Zone* zone) {
AutoEnterOOMUnsafeRegion oomUnsafe;
if (!zone->gcWeakKeys().clear()) {
oomUnsafe.crash("clearing weak keys table");
}
MOZ_ASSERT(zone->gcNurseryWeakKeys().count() == 0);
for (WeakMapBase* m : zone->gcWeakMapList()) {
m->markColor = CellColor::White;
m->marked = false;
}
}
@ -58,7 +52,7 @@ bool WeakMapBase::checkMarkingForZone(JS::Zone* zone) {
bool ok = true;
for (WeakMapBase* m : zone->gcWeakMapList()) {
if (IsMarked(m->markColor) && !m->checkMarking()) {
if (m->marked && !m->checkMarking()) {
ok = false;
}
}
@ -70,7 +64,7 @@ bool WeakMapBase::checkMarkingForZone(JS::Zone* zone) {
bool WeakMapBase::markZoneIteratively(JS::Zone* zone, GCMarker* marker) {
bool markedAny = false;
for (WeakMapBase* m : zone->gcWeakMapList()) {
if (IsMarked(m->markColor) && m->markEntries(marker)) {
if (m->marked && m->markEntries(marker)) {
markedAny = true;
}
}
@ -89,7 +83,7 @@ bool WeakMapBase::findSweepGroupEdges(JS::Zone* zone) {
void WeakMapBase::sweepZone(JS::Zone* zone) {
for (WeakMapBase* m = zone->gcWeakMapList().getFirst(); m;) {
WeakMapBase* next = m->getNext();
if (IsMarked(m->markColor)) {
if (m->marked) {
m->sweep();
} else {
m->clearAndCompact();
@ -100,7 +94,7 @@ void WeakMapBase::sweepZone(JS::Zone* zone) {
#ifdef DEBUG
for (WeakMapBase* m : zone->gcWeakMapList()) {
MOZ_ASSERT(m->isInList() && IsMarked(m->markColor));
MOZ_ASSERT(m->isInList() && m->marked);
}
#endif
}
@ -117,24 +111,21 @@ void WeakMapBase::traceAllMappings(WeakMapTracer* tracer) {
}
bool WeakMapBase::saveZoneMarkedWeakMaps(JS::Zone* zone,
WeakMapColors& markedWeakMaps) {
WeakMapSet& markedWeakMaps) {
for (WeakMapBase* m : zone->gcWeakMapList()) {
if (IsMarked(m->markColor)) {
if (!markedWeakMaps.put(m, m->markColor)) {
return false;
}
if (m->marked && !markedWeakMaps.put(m)) {
return false;
}
}
return true;
}
void WeakMapBase::restoreMarkedWeakMaps(WeakMapColors& markedWeakMaps) {
for (WeakMapColors::Range r = markedWeakMaps.all(); !r.empty();
r.popFront()) {
WeakMapBase* map = r.front().key();
void WeakMapBase::restoreMarkedWeakMaps(WeakMapSet& markedWeakMaps) {
for (WeakMapSet::Range r = markedWeakMaps.all(); !r.empty(); r.popFront()) {
WeakMapBase* map = r.front();
MOZ_ASSERT(map->zone()->isGCMarking());
MOZ_ASSERT(map->markColor == CellColor::White);
map->markColor = r.front().value();
MOZ_ASSERT(!map->marked);
map->marked = true;
}
}
@ -143,15 +134,17 @@ size_t ObjectValueMap::sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) {
}
bool ObjectValueMap::findZoneEdges() {
// For weakmap keys with delegates in a different zone, add a zone edge to
// ensure that the delegate zone finishes marking before the key zone.
//
// Possibly add an edge the other direction too, because scanning the
// gcWeakKeys for a zone containing a delegate might end up marking a value
// in the map/key zone.
/*
* For unmarked weakmap keys with delegates in a different zone, add a zone
* edge to ensure that the delegate zone finishes marking before the key
* zone.
*/
JS::AutoSuppressGCAnalysis nogc;
for (Range r = all(); !r.empty(); r.popFront()) {
JSObject* key = r.front().key();
if (key->asTenured().isMarkedBlack()) {
continue;
}
JSObject* delegate = getDelegate(key);
if (!delegate) {
continue;
@ -163,30 +156,6 @@ bool ObjectValueMap::findZoneEdges() {
if (!delegateZone->addSweepGroupEdgeTo(key->zone())) {
return false;
}
// The various cases depend on the order that the map and key are marked:
//
// If the key is marked:
// key marked, then map marked:
// - value was marked with map
// map marked, key already in map, key marked before weak marking mode:
// - key added to weakKeys when map marked
// - value marked during enterWeakMarkingMode: this requires the
// delegate's zone to be marked before the key zone, since only the
// delegate will be in weakKeys.
// map marked, key already in map, key marked after weak marking mode:
// - during key marking, weakKeys[key] triggers marking of value
// map marked, key inserted into map, key marked:
// - value marked by insert barrier
//
// In all cases, a marked key will have already marked the value, so there
// is no need for a key->delegate zone. We still need the delegate->key
// edge for the enterWeakMarkingMode case described above.
if (!key->asTenured().isMarkedBlack()) {
if (!key->zone()->addSweepGroupEdgeTo(delegateZone)) {
return false;
}
}
}
return true;
}

View File

@ -12,7 +12,6 @@
#include "gc/Barrier.h"
#include "gc/DeletePolicy.h"
#include "gc/Tracer.h"
#include "gc/Zone.h"
#include "gc/ZoneAllocator.h"
#include "js/HashTable.h"
@ -48,24 +47,13 @@ bool CheckWeakMapEntryMarking(const WeakMapBase* map, Cell* key, Cell* value);
// the implicit edges stored in the map) and of removing (sweeping) table
// entries when collection is complete.
using WeakMapColors = HashMap<WeakMapBase*, js::gc::CellColor,
DefaultHasher<WeakMapBase*>, SystemAllocPolicy>;
typedef HashSet<WeakMapBase*, DefaultHasher<WeakMapBase*>, SystemAllocPolicy>
WeakMapSet;
// Common base class for all WeakMap specializations, used for calling
// subclasses' GC-related methods.
class WeakMapBase : public mozilla::LinkedListElement<WeakMapBase> {
public:
friend class js::GCMarker;
using CellColor = js::gc::CellColor;
protected:
template <typename T>
CellColor getCellColor(const T& k) const {
if (!k->zone()->shouldMarkInZone() || !k->isTenured()) {
return CellColor::Black;
}
return GetCellColor(k);
}
public:
WeakMapBase(JSObject* memOf, JS::Zone* zone);
@ -95,25 +83,23 @@ class WeakMapBase : public mozilla::LinkedListElement<WeakMapBase> {
// entries of live weak maps whose keys are dead.
static void sweepZone(JS::Zone* zone);
// Sweep the marked weak maps in a zone, updating moved keys.
static void sweepZoneAfterMinorGC(JS::Zone* zone);
// Trace all weak map bindings. Used by the cycle collector.
static void traceAllMappings(WeakMapTracer* tracer);
// Save information about which weak maps are marked for a zone.
static bool saveZoneMarkedWeakMaps(JS::Zone* zone,
WeakMapColors& markedWeakMaps);
WeakMapSet& markedWeakMaps);
// Restore information about which weak maps are marked for many zones.
static void restoreMarkedWeakMaps(WeakMapColors& markedWeakMaps);
static void restoreMarkedWeakMaps(WeakMapSet& markedWeakMaps);
#if defined(JS_GC_ZEAL) || defined(DEBUG)
static bool checkMarkingForZone(JS::Zone* zone);
#endif
template <typename T>
static JSObject* getDelegate(const T& key);
static JSObject* getDelegate(JSObject* key);
static JSObject* getDelegate(JSScript* script);
static JSObject* getDelegate(LazyScript* script);
protected:
// Instance member functions called by the above. Instantiations of WeakMap
@ -129,11 +115,6 @@ class WeakMapBase : public mozilla::LinkedListElement<WeakMapBase> {
virtual void markEntry(GCMarker* marker, gc::Cell* markedCell,
gc::Cell* l) = 0;
// An unmarked CCW with a delegate will add a weakKeys entry for the
// delegate. If the delegate is removed with NukeCrossCompartmentWrapper,
// then the (former) CCW needs to be added to weakKeys instead.
virtual void postSeverDelegate(GCMarker* marker, gc::Cell* key) = 0;
virtual bool markEntries(GCMarker* marker) = 0;
#ifdef JS_GC_ZEAL
@ -151,21 +132,10 @@ class WeakMapBase : public mozilla::LinkedListElement<WeakMapBase> {
// Whether this object has been marked during garbage collection and which
// color it was marked.
gc::CellColor markColor;
bool marked;
gc::MarkColor markColor;
};
namespace detail {
template <typename T>
struct RemoveBarrier {};
template <typename T>
struct RemoveBarrier<js::HeapPtr<T>> {
using Type = T;
};
} // namespace detail
template <class Key, class Value>
class WeakMap
: private HashMap<Key, Value, MovableCellHasher<Key>, ZoneAllocPolicy>,
@ -191,8 +161,6 @@ class WeakMap
// Resolve ambiguity with LinkedListElement<>::remove.
using Base::remove;
using UnbarrieredKey = typename detail::RemoveBarrier<Key>::Type;
explicit WeakMap(JSContext* cx, JSObject* memOf = nullptr);
// Add a read barrier to prevent an incorrectly gray value from escaping the
@ -205,8 +173,6 @@ class WeakMap
return p;
}
Ptr unbarrieredLookup(const Lookup& l) const { return Base::lookup(l); }
AddPtr lookupForAdd(const Lookup& l) {
AddPtr p = Base::lookupForAdd(l);
if (p) {
@ -215,111 +181,34 @@ class WeakMap
return p;
}
void remove(Ptr p) {
MOZ_ASSERT(p.found());
if (markColor != CellColor::White) {
forgetKey(p->key());
}
Base::remove(p);
}
void remove(const Lookup& l) {
if (Ptr p = lookup(l)) {
remove(p);
}
}
void clear() {
Base::clear();
JSRuntime* rt = zone()->runtimeFromMainThread();
if (zone()->needsIncrementalBarrier()) {
rt->gc.marker.forgetWeakMap(this, zone());
}
template <typename KeyInput, typename ValueInput>
MOZ_MUST_USE bool put(KeyInput&& key, ValueInput&& value) {
MOZ_ASSERT(key);
return Base::put(std::forward<KeyInput>(key),
std::forward<ValueInput>(value));
}
template <typename KeyInput, typename ValueInput>
MOZ_MUST_USE bool add(AddPtr& p, KeyInput&& k, ValueInput&& v) {
MOZ_ASSERT(k);
if (!Base::add(p, std::forward<KeyInput>(k), std::forward<ValueInput>(v))) {
return false;
}
barrierForInsert(p->key(), p->value());
return true;
MOZ_MUST_USE bool putNew(KeyInput&& key, ValueInput&& value) {
MOZ_ASSERT(key);
return Base::putNew(std::forward<KeyInput>(key),
std::forward<ValueInput>(value));
}
template <typename KeyInput, typename ValueInput>
MOZ_MUST_USE bool relookupOrAdd(AddPtr& p, KeyInput&& k, ValueInput&& v) {
MOZ_ASSERT(k);
if (!Base::relookupOrAdd(p, std::forward<KeyInput>(k),
std::forward<ValueInput>(v))) {
return false;
}
barrierForInsert(p->key(), p->value());
return true;
}
template <typename KeyInput, typename ValueInput>
MOZ_MUST_USE bool put(KeyInput&& k, ValueInput&& v) {
MOZ_ASSERT(k);
AddPtr p = lookupForAdd(k);
if (p) {
p->value() = std::forward<ValueInput>(v);
return true;
}
return add(p, std::forward<KeyInput>(k), std::forward<ValueInput>(v));
}
template <typename KeyInput, typename ValueInput>
MOZ_MUST_USE bool putNew(KeyInput&& k, ValueInput&& v) {
MOZ_ASSERT(k);
barrierForInsert(k, v);
return Base::putNew(std::forward<KeyInput>(k), std::forward<ValueInput>(v));
}
template <typename KeyInput, typename ValueInput>
void putNewInfallible(KeyInput&& k, ValueInput&& v) {
MOZ_ASSERT(k);
barrierForInsert(k, v);
Base::putNewInfallible(std::forward(k), std::forward<KeyInput>(k));
MOZ_MUST_USE bool relookupOrAdd(AddPtr& ptr, KeyInput&& key,
ValueInput&& value) {
MOZ_ASSERT(key);
return Base::relookupOrAdd(ptr, std::forward<KeyInput>(key),
std::forward<ValueInput>(value));
}
void markEntry(GCMarker* marker, gc::Cell* markedCell,
gc::Cell* origKey) override;
// 'key' has lost its delegate, update our weak key state.
void postSeverDelegate(GCMarker* marker, gc::Cell* key) override;
void trace(JSTracer* trc) override;
protected:
void forgetKey(UnbarrieredKey key) {
// Remove the key or its delegate from weakKeys.
JSRuntime* rt = zone()->runtimeFromMainThread();
if (rt->gc.isIncrementalGCInProgress()) {
if (JSObject* delegate = getDelegate(key)) {
js::gc::WeakKeyTable& weakKeys = delegate->zone()->gcWeakKeys(delegate);
rt->gc.marker.forgetWeakKey(weakKeys, this, delegate, key);
} else {
js::gc::WeakKeyTable& weakKeys = key->zone()->gcWeakKeys(key);
rt->gc.marker.forgetWeakKey(weakKeys, this, key, key);
}
}
}
void barrierForInsert(Key k, const Value& v) {
if (markColor == CellColor::White) {
return;
}
if (!zone()->needsIncrementalBarrier()) {
return;
}
JSTracer* trc = zone()->barrierTracer();
Value tmp = v;
TraceEdge(trc, &tmp, "weakmap inserted value");
MOZ_ASSERT(tmp == v);
}
// We have a key that, if it or its delegate is marked, may lead to a WeakMap
// value getting marked. Insert it or its delegate (if any) into the
// appropriate zone's gcWeakKeys or gcNurseryWeakKeys.
@ -345,9 +234,9 @@ class WeakMap
JS::ExposeObjectToActiveJS(obj);
}
CellColor getDelegateColor(JSObject* key) const;
CellColor getDelegateColor(JSScript* script) const;
CellColor getDelegateColor(LazyScript* script) const;
bool keyNeedsMark(GCMarker* marker, JSObject* key) const;
bool keyNeedsMark(GCMarker* marker, JSScript* script) const;
bool keyNeedsMark(GCMarker* marker, LazyScript* script) const;
bool findZoneEdges() override {
// This is overridden by ObjectValueMap.

View File

@ -399,13 +399,6 @@ void Zone::discardJitCode(FreeOp* fop,
jitZone()->cfgSpace()->lifoAlloc().freeAll();
}
void JS::Zone::delegatePreWriteBarrierInternal(JSObject* obj,
JSObject* delegate) {
MOZ_ASSERT(js::WeakMapBase::getDelegate(obj) == delegate);
MOZ_ASSERT(needsIncrementalBarrier());
GCMarker::fromTracer(barrierTracer())->severWeakDelegate(obj, delegate);
}
#ifdef JSGC_HASH_TABLE_CHECKS
void JS::Zone::checkUniqueIdTableAfterMovingGC() {
for (auto r = uniqueIds().all(); !r.empty(); r.popFront()) {

View File

@ -404,12 +404,6 @@ class Zone : public js::ZoneAllocator, public js::gc::GraphNodeBase<JS::Zone> {
weakCaches().insertBack(cachep);
}
void delegatePreWriteBarrier(JSObject* obj, JSObject* delegate) {
if (needsIncrementalBarrier()) {
delegatePreWriteBarrierInternal(obj, delegate);
}
}
private:
/*
* Mapping from not yet marked keys to a vector of all values that the key
@ -420,14 +414,9 @@ class Zone : public js::ZoneAllocator, public js::gc::GraphNodeBase<JS::Zone> {
js::ZoneOrGCTaskData<js::gc::WeakKeyTable> gcNurseryWeakKeys_;
public:
void delegatePreWriteBarrierInternal(JSObject* obj, JSObject* delegate);
js::gc::WeakKeyTable& gcWeakKeys() { return gcWeakKeys_.ref(); }
js::gc::WeakKeyTable& gcNurseryWeakKeys() { return gcNurseryWeakKeys_.ref(); }
js::gc::WeakKeyTable& gcWeakKeys(const js::gc::Cell* cell) {
return cell->isTenured() ? gcWeakKeys() : gcNurseryWeakKeys();
}
// A set of edges from this zone to other zones used during GC to calculate
// sweep groups.
NodeSet& gcSweepGroupEdges() {

View File

@ -1,19 +0,0 @@
// |jit-test| skip-if: !('oomTest' in this)
// Adapted from randomly chosen test: js/src/jit-test/tests/debug/Memory-drainAllocationsLog-13.js
//
// This triggers OOMs that will cause weak marking mode to abort.
const root = newGlobal({
newCompartment: true
});
root.eval("dbg = new Debugger()");
root.dbg.addDebuggee(this);
root.dbg.memory.trackingAllocationSites = true;
// jsfunfuzz-generated
relazifyFunctions('compartment');
print(/x/);
oomTest((function() {
String.prototype.localeCompare()
}), {
keepFailing: true
});

View File

@ -17,7 +17,7 @@ function basicSweeping() {
finishgc();
startgc(100000, 'shrinking');
finishgc();
gcslice();
assertEq(wm1.get(hold).name, 'val2');
assertEq(nondeterministicGetWeakMapKeys(wm1).length, 1);
@ -41,7 +41,7 @@ function weakGraph() {
finishgc();
startgc(100000, 'shrinking');
finishgc();
gcslice();
assertEq(obj2.name, "obj2");
assertEq(wm1.get(obj2).name, "obj3");
@ -70,7 +70,7 @@ function deadWeakMap() {
finishgc();
startgc(100000, 'shrinking');
finishgc();
gcslice();
assertEq(obj2.name, "obj2");
assertEq(finalizeCount(), initialCount + 1);
@ -98,7 +98,7 @@ function deadKeys() {
finishgc();
startgc(100000, 'shrinking');
finishgc();
gcslice();
assertEq(finalizeCount(), initialCount + 2);
assertEq(nondeterministicGetWeakMapKeys(wm1).length, 0);
@ -132,7 +132,7 @@ function weakKeysRealloc() {
var initialCount = finalizeCount();
finishgc();
startgc(100000, 'shrinking');
finishgc();
gcslice();
assertEq(finalizeCount(), initialCount + 1);
}

View File

@ -171,56 +171,6 @@ function nukeMarking() {
if (this.enqueueMark)
runtest(nukeMarking);
// Similar to the above, but try to trigger a rehash of the weak keys table for
// a zone, which previously invalidated an iterator (bug 1556430).
function nukeMarking2() {
// Create a global in a different compartment, same zone, so the delegate and
// key can live in the same zone.
const g1 = newGlobal({sameZoneAs: this});
let vals = { maps: [], keys: [] };
for (const i of [,,,,,]) {
vals.maps.push(new WeakMap());
vals.keys.push(g1.eval("Object.create(null)"));
}
vals.val = Object.create(null);
for (const i in vals.maps) {
vals.maps[i].set(vals.keys[i], vals.val);
}
vals.val = null;
gc();
// Set up the sequence of marking events.
for (const map of vals.maps) {
enqueueMark(map);
}
enqueueMark("yield");
// We will nuke the key's delegate here.
for (const key of vals.keys) {
enqueueMark(key);
}
addMarkObservers([vals.keys[0]]);
enqueueMark("enter-weak-marking-mode");
vals = null;
// Okay, run through the GC now.
startgc(1000000);
assertEq(gcstate(), "Mark", "expected to yield after marking map");
print(getMarks()[0]);
const q = getMarkQueue();
// We should have marked the map and then yielded back here.
nukeCCW(q[q.length - 2]);
// Finish up the GC.
gcslice();
clearMarkQueue();
clearMarkObservers();
}
if (this.enqueueMark)
nukeMarking2();
function transplantMarking() {
const g1 = newGlobal({newCompartment: true});

View File

@ -27,7 +27,6 @@
#include "gc/GC-inl.h"
#include "gc/Marking-inl.h"
#include "gc/WeakMap-inl.h"
#include "vm/JSAtom-inl.h"
#include "vm/JSFunction-inl.h"
#include "vm/JSObject-inl.h"
@ -77,18 +76,6 @@ bool Compartment::putWrapper(JSContext* cx, const CrossCompartmentKey& wrapped,
return true;
}
void Compartment::removeWrapper(js::WrapperMap::Ptr p) {
if (p->key().is<JSObject*>()) {
JSObject* key = p->key().as<JSObject*>();
JS::Value value = p->value().unbarrieredGet();
if (js::WeakMapBase::getDelegate(&value.toObject()) == key) {
key->zone()->delegatePreWriteBarrier(&value.toObject(), key);
}
}
crossCompartmentWrappers.remove(p);
}
static JSString* CopyStringPure(JSContext* cx, JSString* str) {
/*
* Directly allocate the copy in the destination compartment, rather than

View File

@ -602,8 +602,6 @@ class JS::Compartment {
const js::CrossCompartmentKey& wrapped,
const js::Value& wrapper);
void removeWrapper(js::WrapperMap::Ptr p);
js::WrapperMap::Ptr lookupWrapper(const js::Value& wrapped) const {
return crossCompartmentWrappers.lookup(js::CrossCompartmentKey(wrapped));
}
@ -612,6 +610,10 @@ class JS::Compartment {
return crossCompartmentWrappers.lookup(js::CrossCompartmentKey(obj));
}
void removeWrapper(js::WrapperMap::Ptr p) {
crossCompartmentWrappers.remove(p);
}
bool hasNurseryAllocatedWrapperEntries(const js::CompartmentFilter& f) {
return crossCompartmentWrappers.hasNurseryAllocatedWrapperEntries(f);
}

View File

@ -12,7 +12,6 @@
#include "vm/Realm.h"
#include "gc/ObjectKind-inl.h"
#include "gc/WeakMap-inl.h"
#include "vm/JSObject-inl.h"
#include "vm/TypeInference-inl.h"
@ -152,15 +151,6 @@ inline void ProxyObject::setPrivate(const Value& priv) {
}
void ProxyObject::nuke() {
// Notify the zone that a delegate is no longer a delegate. Be careful not to
// expose this pointer, because it has already been removed from the wrapper
// map yet we have assertions during tracing that will verify that it is
// still present.
JSObject* delegate = UncheckedUnwrapWithoutExpose(this);
if (delegate != this) {
delegate->zone()->delegatePreWriteBarrier(this, delegate);
}
// Clear the target reference and replaced it with a value that encodes
// various information about the original target.
setSameCompartmentPrivate(DeadProxyTargetValue(this));

View File

@ -488,8 +488,6 @@ class JS::Realm : public JS::shadow::Realm {
// can easily lead to races. Use this method very carefully.
JSRuntime* runtimeFromAnyThread() const { return runtime_; }
void removeWrapper(js::WrapperMap::Ptr p);
const JS::RealmCreationOptions& creationOptions() const {
return creationOptions_;
}