#ifndef gc_GCMarker_h
#define gc_GCMarker_h
#include "ds/OrderedHashTable.h"
#include "js/SliceBudget.h"
#include "js/TracingAPI.h"
#include "js/TypeDecls.h"
namespace js {
class AutoAccessAtomsZone;
class WeakMapBase;
static const size_t NON_INCREMENTAL_MARK_STACK_BASE_CAPACITY = 4096;
static const size_t INCREMENTAL_MARK_STACK_BASE_CAPACITY = 32768;
namespace gc {
struct Cell;
struct WeakKeyTableHashPolicy {
typedef JS::GCCellPtr Lookup;
static HashNumber hash(const Lookup& v, const mozilla::HashCodeScrambler&) {
return mozilla::HashGeneric(v.asCell());
}
static bool match(const JS::GCCellPtr& k, const Lookup& l) { return k == l; }
static bool isEmpty(const JS::GCCellPtr& v) { return !v; }
static void makeEmpty(JS::GCCellPtr* vp) { *vp = nullptr; }
};
struct WeakMarkable {
WeakMapBase* weakmap;
JS::GCCellPtr key;
WeakMarkable(WeakMapBase* weakmapArg, JS::GCCellPtr keyArg)
: weakmap(weakmapArg), key(keyArg) {}
};
using WeakEntryVector = Vector<WeakMarkable, 2, js::SystemAllocPolicy>;
using WeakKeyTable =
OrderedHashMap<JS::GCCellPtr, WeakEntryVector, WeakKeyTableHashPolicy,
js::SystemAllocPolicy>;
class MarkStack {
public:
enum Tag {
ValueArrayTag,
ObjectTag,
GroupTag,
SavedValueArrayTag,
JitCodeTag,
ScriptTag,
TempRopeTag,
LastTag = TempRopeTag
};
static const uintptr_t TagMask = 7;
static_assert(TagMask >= uintptr_t(LastTag),
"The tag mask must subsume the tags.");
static_assert(TagMask <= gc::CellAlignMask,
"The tag mask must be embeddable in a Cell*.");
class TaggedPtr {
uintptr_t bits;
Cell* ptr() const;
public:
TaggedPtr() {}
TaggedPtr(Tag tag, Cell* ptr);
Tag tag() const;
template <typename T>
T* as() const;
JSObject* asValueArrayObject() const;
JSObject* asSavedValueArrayObject() const;
JSRope* asTempRope() const;
void assertValid() const;
};
struct ValueArray {
ValueArray(JSObject* obj, HeapSlot* start, HeapSlot* end);
void assertValid() const;
HeapSlot* end;
HeapSlot* start;
TaggedPtr ptr;
};
struct SavedValueArray {
SavedValueArray(JSObject* obj, size_t index, HeapSlot::Kind kind);
void assertValid() const;
uintptr_t kind;
uintptr_t index;
TaggedPtr ptr;
};
explicit MarkStack(size_t maxCapacity = DefaultCapacity);
~MarkStack();
static const size_t DefaultCapacity = SIZE_MAX;
size_t capacity() { return stack().length(); }
size_t position() const { return topIndex_; }
MOZ_MUST_USE bool init(JSGCMode gcMode);
MOZ_MUST_USE bool setCapacityForMode(JSGCMode mode);
size_t maxCapacity() const { return maxCapacity_; }
void setMaxCapacity(size_t maxCapacity);
template <typename T>
MOZ_MUST_USE bool push(T* ptr);
MOZ_MUST_USE bool push(JSObject* obj, HeapSlot* start, HeapSlot* end);
MOZ_MUST_USE bool push(const ValueArray& array);
MOZ_MUST_USE bool push(const SavedValueArray& array);
MOZ_MUST_USE bool pushTempRope(JSRope* ptr);
bool isEmpty() const { return topIndex_ == 0; }
Tag peekTag() const;
TaggedPtr popPtr();
ValueArray popValueArray();
SavedValueArray popSavedValueArray();
void clear() { topIndex_ = 0; }
void setGCMode(JSGCMode gcMode);
void poisonUnused();
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
private:
using StackVector = Vector<TaggedPtr, 0, SystemAllocPolicy>;
const StackVector& stack() const { return stack_.ref(); }
StackVector& stack() { return stack_.ref(); }
MOZ_MUST_USE bool ensureSpace(size_t count);
MOZ_MUST_USE bool enlarge(size_t count);
MOZ_MUST_USE bool resize(size_t newCapacity);
TaggedPtr* topPtr();
const TaggedPtr& peekPtr() const;
MOZ_MUST_USE bool pushTaggedPtr(Tag tag, Cell* ptr);
MainThreadData<size_t> topIndex_;
MainThreadData<size_t> maxCapacity_;
MainThreadData<StackVector> stack_;
#ifdef DEBUG
mutable size_t iteratorCount_;
#endif
friend class MarkStackIter;
};
class MarkStackIter {
MarkStack& stack_;
size_t pos_;
public:
explicit MarkStackIter(MarkStack& stack);
~MarkStackIter();
bool done() const;
MarkStack::Tag peekTag() const;
MarkStack::TaggedPtr peekPtr() const;
MarkStack::ValueArray peekValueArray() const;
void next();
void nextPtr();
void nextArray();
void saveValueArray(const MarkStack::SavedValueArray& savedArray);
private:
size_t position() const;
};
}
class GCMarker : public JSTracer {
public:
explicit GCMarker(JSRuntime* rt);
MOZ_MUST_USE bool init(JSGCMode gcMode);
void setMaxCapacity(size_t maxCap) { stack.setMaxCapacity(maxCap); }
size_t maxCapacity() const { return stack.maxCapacity(); }
void start();
void stop();
void reset();
template <typename T>
void traverse(T thing);
template <typename S, typename T>
void traverseEdge(S source, T* target);
template <typename S, typename T>
void traverseEdge(S source, const T& target);
template <typename S>
void traverseObjectEdge(S source, JSObject* target) {
traverseEdge(source, target);
}
template <typename S>
void traverseStringEdge(S source, JSString* target) {
traverseEdge(source, target);
}
template <typename T>
void noteWeakEdge(T* edge);
void setMarkColorGray();
void setMarkColorBlack();
void setMarkColor(gc::MarkColor newColor);
gc::MarkColor markColor() const { return color; }
template <typename T>
bool isMarked(T* thingp) {
return color == gc::MarkColor::Black ? gc::IsMarkedBlack(runtime(), thingp)
: gc::IsMarked(runtime(), thingp);
}
template <typename T>
bool isMarkedUnbarriered(T* thingp) {
return color == gc::MarkColor::Black
? gc::IsMarkedBlackUnbarriered(runtime(), thingp)
: gc::IsMarkedUnbarriered(runtime(), thingp);
}
void enterWeakMarkingMode();
void leaveWeakMarkingMode();
void abortLinearWeakMarking() {
leaveWeakMarkingMode();
linearWeakMarkingDisabled_ = true;
}
void delayMarkingChildren(gc::Cell* cell);
bool isDrained() { return isMarkStackEmpty() && !delayedMarkingList; }
MOZ_MUST_USE bool markUntilBudgetExhausted(SliceBudget& budget);
void setGCMode(JSGCMode mode) { stack.setGCMode(mode); }
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
#ifdef DEBUG
bool shouldCheckCompartments() { return strictCompartmentChecking; }
#endif
void markEphemeronValues(gc::Cell* markedCell, gc::WeakEntryVector& entry);
size_t getMarkCount() const { return markCount; }
void clearMarkCount() { markCount = 0; }
static GCMarker* fromTracer(JSTracer* trc) {
MOZ_ASSERT(trc->isMarkingTracer());
return static_cast<GCMarker*>(trc);
}
template <typename T>
void markImplicitEdges(T* oldThing);
private:
#ifdef DEBUG
void checkZone(void* p);
#else
void checkZone(void* p) {}
#endif
inline void repush(JSObject* obj);
template <typename T>
void markAndTraceChildren(T* thing);
template <typename T>
void markAndPush(T* thing);
template <typename T>
void markAndScan(T* thing);
template <typename T>
void markImplicitEdgesHelper(T oldThing);
void eagerlyMarkChildren(JSLinearString* str);
void eagerlyMarkChildren(JSRope* rope);
void eagerlyMarkChildren(JSString* str);
void eagerlyMarkChildren(LazyScript* thing);
void eagerlyMarkChildren(Shape* shape);
void eagerlyMarkChildren(Scope* scope);
void lazilyMarkChildren(ObjectGroup* group);
template <typename T>
void dispatchToTraceChildren(T* thing);
template <typename T>
MOZ_MUST_USE bool mark(T* thing);
template <typename T>
inline void pushTaggedPtr(T* ptr);
inline void pushValueArray(JSObject* obj, HeapSlot* start, HeapSlot* end);
bool isMarkStackEmpty() { return stack.isEmpty(); }
bool hasBlackEntries() const { return stack.position() > grayPosition; }
bool hasGrayEntries() const { return grayPosition > 0 && !stack.isEmpty(); }
MOZ_MUST_USE bool restoreValueArray(
const gc::MarkStack::SavedValueArray& array, HeapSlot** vpp,
HeapSlot** endp);
gc::MarkStack::ValueArray restoreValueArray(
const gc::MarkStack::SavedValueArray& savedArray);
void saveValueRanges();
gc::MarkStack::SavedValueArray saveValueRange(
const gc::MarkStack::ValueArray& array);
inline void processMarkStackTop(SliceBudget& budget);
void markDelayedChildren(gc::Arena* arena, gc::MarkColor color);
MOZ_MUST_USE bool markAllDelayedChildren(SliceBudget& budget);
bool processDelayedMarkingList(gc::MarkColor color, SliceBudget& budget);
bool hasDelayedChildren() const { return !!delayedMarkingList; }
void rebuildDelayedMarkingList();
void appendToDelayedMarkingList(gc::Arena** listTail, gc::Arena* arena);
template <typename F>
void forEachDelayedMarkingArena(F&& f);
gc::MarkStack stack;
MainThreadData<size_t> grayPosition;
MainThreadData<gc::MarkColor> color;
MainThreadData<js::gc::Arena*> delayedMarkingList;
MainThreadData<bool> delayedMarkingWorkAdded;
MainThreadData<bool> linearWeakMarkingDisabled_;
size_t markCount;
#ifdef DEBUG
MainThreadData<size_t> markLaterArenas;
MainThreadData<bool> started;
MainThreadData<bool> strictCompartmentChecking;
#endif };
namespace gc {
class MOZ_RAII AutoSetMarkColor {
GCMarker& marker_;
MarkColor initialColor_;
public:
AutoSetMarkColor(GCMarker& marker, MarkColor newColor)
: marker_(marker), initialColor_(marker.markColor()) {
marker_.setMarkColor(newColor);
}
~AutoSetMarkColor() { marker_.setMarkColor(initialColor_); }
};
}
}
inline bool ThingIsPermanentAtomOrWellKnownSymbol(js::gc::Cell* thing) {
return false;
}
bool ThingIsPermanentAtomOrWellKnownSymbol(JSString*);
bool ThingIsPermanentAtomOrWellKnownSymbol(JSFlatString*);
bool ThingIsPermanentAtomOrWellKnownSymbol(JSLinearString*);
bool ThingIsPermanentAtomOrWellKnownSymbol(JSAtom*);
bool ThingIsPermanentAtomOrWellKnownSymbol(js::PropertyName*);
bool ThingIsPermanentAtomOrWellKnownSymbol(JS::Symbol*);
#endif