#ifndef gc_WeakMap_h
#define gc_WeakMap_h
#include "mozilla/LinkedList.h"
#include "gc/Barrier.h"
#include "gc/DeletePolicy.h"
#include "gc/Zone.h"
#include "js/HashTable.h"
namespace JS {
class Zone;
}
namespace js {
class GCMarker;
class WeakMapBase;
struct WeakMapTracer;
namespace gc {
struct WeakMarkable;
#if defined(JS_GC_ZEAL) || defined(DEBUG)
bool CheckWeakMapEntryMarking(const WeakMapBase* map, Cell* key, Cell* value);
#endif
}
typedef HashSet<WeakMapBase*, DefaultHasher<WeakMapBase*>, SystemAllocPolicy>
WeakMapSet;
class WeakMapBase : public mozilla::LinkedListElement<WeakMapBase> {
friend class js::GCMarker;
public:
WeakMapBase(JSObject* memOf, JS::Zone* zone);
virtual ~WeakMapBase();
JS::Zone* zone() const { return zone_; }
static void unmarkZone(JS::Zone* zone);
static void traceZone(JS::Zone* zone, JSTracer* tracer);
static bool markZoneIteratively(JS::Zone* zone, GCMarker* marker);
static MOZ_MUST_USE bool findSweepGroupEdges(JS::Zone* zone);
static void sweepZone(JS::Zone* zone);
static void traceAllMappings(WeakMapTracer* tracer);
static bool saveZoneMarkedWeakMaps(JS::Zone* zone,
WeakMapSet& markedWeakMaps);
static void restoreMarkedWeakMaps(WeakMapSet& markedWeakMaps);
#if defined(JS_GC_ZEAL) || defined(DEBUG)
static bool checkMarkingForZone(JS::Zone* zone);
#endif
static JSObject* getDelegate(JSObject* key);
static JSObject* getDelegate(JSScript* script);
static JSObject* getDelegate(LazyScript* script);
protected:
virtual void trace(JSTracer* tracer) = 0;
virtual bool findZoneEdges() = 0;
virtual void sweep() = 0;
virtual void traceMappings(WeakMapTracer* tracer) = 0;
virtual void clearAndCompact() = 0;
virtual void markEntry(GCMarker* marker, gc::Cell* markedCell,
JS::GCCellPtr l) = 0;
virtual bool markIteratively(GCMarker* marker) = 0;
#ifdef JS_GC_ZEAL
virtual bool checkMarking() const = 0;
virtual bool allowKeysInOtherZones() const { return false; }
friend bool gc::CheckWeakMapEntryMarking(const WeakMapBase*, gc::Cell*,
gc::Cell*);
#endif
GCPtrObject memberOf;
JS::Zone* zone_;
bool marked;
gc::MarkColor markColor;
};
template <class Key, class Value>
class WeakMap
: private HashMap<Key, Value, MovableCellHasher<Key>, ZoneAllocPolicy>,
public WeakMapBase {
public:
using Base = HashMap<Key, Value, MovableCellHasher<Key>, ZoneAllocPolicy>;
using Lookup = typename Base::Lookup;
using Entry = typename Base::Entry;
using Range = typename Base::Range;
using Ptr = typename Base::Ptr;
using AddPtr = typename Base::AddPtr;
struct Enum : public Base::Enum {
explicit Enum(WeakMap& map) : Base::Enum(static_cast<Base&>(map)) {}
};
using Base::all;
using Base::clear;
using Base::has;
using Base::shallowSizeOfExcludingThis;
using Base::remove;
explicit WeakMap(JSContext* cx, JSObject* memOf = nullptr);
Ptr lookup(const Lookup& l) const {
Ptr p = Base::lookup(l);
if (p) {
exposeGCThingToActiveJS(p->value());
}
return p;
}
AddPtr lookupForAdd(const Lookup& l) {
AddPtr p = Base::lookupForAdd(l);
if (p) {
exposeGCThingToActiveJS(p->value());
}
return p;
}
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 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,
JS::GCCellPtr origKey) override;
void trace(JSTracer* trc) override;
protected:
static void addWeakEntry(GCMarker* marker, JS::GCCellPtr key,
const gc::WeakMarkable& markable);
bool markIteratively(GCMarker* marker) override;
private:
void exposeGCThingToActiveJS(const JS::Value& v) const {
JS::ExposeValueToActiveJS(v);
}
void exposeGCThingToActiveJS(JSObject* obj) const {
JS::ExposeObjectToActiveJS(obj);
}
bool keyNeedsMark(GCMarker* marker, JSObject* key) const;
bool keyNeedsMark(GCMarker* marker, JSScript* script) const;
bool keyNeedsMark(GCMarker* marker, LazyScript* script) const;
bool findZoneEdges() override {
return true;
}
void sweep() override;
void clearAndCompact() override {
Base::clear();
Base::compact();
}
void traceMappings(WeakMapTracer* tracer) override;
protected:
#if DEBUG
void assertEntriesNotAboutToBeFinalized();
#endif
#ifdef JS_GC_ZEAL
bool checkMarking() const override;
#endif
};
class ObjectValueMap : public WeakMap<HeapPtr<JSObject*>, HeapPtr<Value>> {
public:
ObjectValueMap(JSContext* cx, JSObject* obj) : WeakMap(cx, obj) {}
bool findZoneEdges() override;
};
class ObjectWeakMap {
ObjectValueMap map;
public:
explicit ObjectWeakMap(JSContext* cx);
JS::Zone* zone() const { return map.zone(); }
JSObject* lookup(const JSObject* obj);
bool add(JSContext* cx, JSObject* obj, JSObject* target);
void clear();
void trace(JSTracer* trc);
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) {
return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf);
}
#ifdef JSGC_HASH_TABLE_CHECKS
void checkAfterMovingGC();
#endif
};
}
namespace JS {
template <>
struct DeletePolicy<js::ObjectValueMap>
: public js::GCManagedDeletePolicy<js::ObjectValueMap> {};
}
#endif