2011-04-17 08:23:44 +04:00
|
|
|
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
|
|
* vim: set ts=4 sw=4 et tw=99:
|
|
|
|
*
|
2012-05-21 15:12:37 +04:00
|
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
2011-04-17 08:23:44 +04:00
|
|
|
|
|
|
|
#ifndef jsweakmap_h___
|
|
|
|
#define jsweakmap_h___
|
|
|
|
|
|
|
|
#include "jsapi.h"
|
2011-11-24 16:35:56 +04:00
|
|
|
#include "jsfriendapi.h"
|
2012-11-02 22:03:59 +04:00
|
|
|
#include "jscompartment.h"
|
2011-04-17 08:23:44 +04:00
|
|
|
#include "jsobj.h"
|
|
|
|
|
2012-05-02 01:30:18 +04:00
|
|
|
#include "gc/Marking.h"
|
2011-10-05 02:33:00 +04:00
|
|
|
#include "js/HashTable.h"
|
|
|
|
|
2011-04-17 08:23:44 +04:00
|
|
|
namespace js {
|
|
|
|
|
2011-06-15 06:21:47 +04:00
|
|
|
// A subclass template of js::HashMap whose keys and values may be garbage-collected. When
|
|
|
|
// a key is collected, the table entry disappears, dropping its reference to the value.
|
|
|
|
//
|
|
|
|
// More precisely:
|
|
|
|
//
|
|
|
|
// A WeakMap entry is collected if and only if either the WeakMap or the entry's key
|
|
|
|
// is collected. If an entry is not collected, it remains in the WeakMap and it has a
|
|
|
|
// strong reference to the value.
|
|
|
|
//
|
2012-10-09 05:22:47 +04:00
|
|
|
// You must call this table's 'trace' method when the object of which it is a part is
|
2011-06-15 06:21:47 +04:00
|
|
|
// reached by the garbage collection tracer. Once a table is known to be live, the
|
|
|
|
// implementation takes care of the iterative marking needed for weak tables and removing
|
|
|
|
// table entries when collection is complete.
|
|
|
|
|
2012-01-06 23:30:09 +04:00
|
|
|
// The value for the next pointer for maps not in the map list.
|
|
|
|
static WeakMapBase * const WeakMapNotInList = reinterpret_cast<WeakMapBase *>(1);
|
|
|
|
|
2012-02-27 21:48:35 +04:00
|
|
|
typedef Vector<WeakMapBase *, 0, SystemAllocPolicy> WeakMapVector;
|
|
|
|
|
2011-06-15 06:21:47 +04:00
|
|
|
// Common base class for all WeakMap specializations. The collector uses this to call
|
|
|
|
// their markIteratively and sweep methods.
|
|
|
|
class WeakMapBase {
|
|
|
|
public:
|
2012-11-02 22:03:59 +04:00
|
|
|
WeakMapBase(JSObject *memOf, JSCompartment *c);
|
|
|
|
virtual ~WeakMapBase();
|
2011-06-15 06:21:47 +04:00
|
|
|
|
|
|
|
void trace(JSTracer *tracer) {
|
|
|
|
if (IS_GC_MARKING_TRACER(tracer)) {
|
|
|
|
// We don't do anything with a WeakMap at trace time. Rather, we wait until as
|
|
|
|
// many keys as possible have been marked, and add ourselves to the list of
|
|
|
|
// known-live WeakMaps to be scanned in the iterative marking phase, by
|
|
|
|
// markAllIteratively.
|
2011-09-04 22:25:49 +04:00
|
|
|
JS_ASSERT(!tracer->eagerlyTraceWeakMaps);
|
2012-01-06 23:30:09 +04:00
|
|
|
|
|
|
|
// Add ourselves to the list if we are not already in the list. We can already
|
|
|
|
// be in the list if the weak map is marked more than once due delayed marking.
|
|
|
|
if (next == WeakMapNotInList) {
|
2012-11-02 22:03:59 +04:00
|
|
|
next = compartment->gcWeakMapList;
|
|
|
|
compartment->gcWeakMapList = this;
|
2012-01-06 23:30:09 +04:00
|
|
|
}
|
2011-06-15 06:21:47 +04:00
|
|
|
} else {
|
|
|
|
// If we're not actually doing garbage collection, the keys won't be marked
|
|
|
|
// nicely as needed by the true ephemeral marking algorithm --- custom tracers
|
2011-11-24 16:35:56 +04:00
|
|
|
// such as the cycle collector must use their own means for cycle detection.
|
|
|
|
// So here we do a conservative approximation: pretend all keys are live.
|
2011-09-04 22:25:49 +04:00
|
|
|
if (tracer->eagerlyTraceWeakMaps)
|
|
|
|
nonMarkingTrace(tracer);
|
2011-06-15 06:21:47 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Garbage collector entry points.
|
|
|
|
|
2012-11-02 22:03:59 +04:00
|
|
|
// Check all weak maps in a compartment that have been marked as live in this garbage
|
2011-06-15 06:21:47 +04:00
|
|
|
// collection, and mark the values of all entries that have become strong references
|
|
|
|
// to them. Return true if we marked any new values, indicating that we need to make
|
|
|
|
// another pass. In other words, mark my marked maps' marked members' mid-collection.
|
2012-11-02 22:03:59 +04:00
|
|
|
static bool markCompartmentIteratively(JSCompartment *c, JSTracer *tracer);
|
2011-06-15 06:21:47 +04:00
|
|
|
|
2012-11-02 22:03:59 +04:00
|
|
|
// Remove entries whose keys are dead from all weak maps in a compartment marked as
|
|
|
|
// live in this garbage collection.
|
|
|
|
static void sweepCompartment(JSCompartment *c);
|
2011-04-17 08:23:44 +04:00
|
|
|
|
2011-11-24 16:35:56 +04:00
|
|
|
// Trace all delayed weak map bindings. Used by the cycle collector.
|
|
|
|
static void traceAllMappings(WeakMapTracer *tracer);
|
|
|
|
|
2012-02-18 02:35:20 +04:00
|
|
|
void check() { JS_ASSERT(next == WeakMapNotInList); }
|
|
|
|
|
2012-11-02 22:03:59 +04:00
|
|
|
// Remove everything from the weak map list for a compartment.
|
|
|
|
static void resetCompartmentWeakMapList(JSCompartment *c);
|
2012-01-06 23:30:09 +04:00
|
|
|
|
2012-11-02 22:03:59 +04:00
|
|
|
// Save the live weak map list for a compartment, appending the data to a vector.
|
|
|
|
static bool saveCompartmentWeakMapList(JSCompartment *c, WeakMapVector &vector);
|
2012-02-27 21:48:35 +04:00
|
|
|
|
2012-11-02 22:03:59 +04:00
|
|
|
// Restore live weak map lists for multiple compartments from a vector.
|
|
|
|
static void restoreCompartmentWeakMapLists(WeakMapVector &vector);
|
|
|
|
|
|
|
|
// Remove a weakmap from the live weakmaps list
|
|
|
|
static void removeWeakMapFromList(WeakMapBase *weakmap);
|
2012-11-02 22:02:25 +04:00
|
|
|
|
2011-04-17 08:23:44 +04:00
|
|
|
protected:
|
2011-06-15 06:21:47 +04:00
|
|
|
// Instance member functions called by the above. Instantiations of WeakMap override
|
|
|
|
// these with definitions appropriate for their Key and Value types.
|
|
|
|
virtual void nonMarkingTrace(JSTracer *tracer) = 0;
|
|
|
|
virtual bool markIteratively(JSTracer *tracer) = 0;
|
2012-11-02 22:03:59 +04:00
|
|
|
virtual void sweep() = 0;
|
2011-11-24 16:35:56 +04:00
|
|
|
virtual void traceMappings(WeakMapTracer *tracer) = 0;
|
|
|
|
|
|
|
|
// Object that this weak map is part of, if any.
|
|
|
|
JSObject *memberOf;
|
2011-06-15 06:21:47 +04:00
|
|
|
|
2012-11-02 22:03:59 +04:00
|
|
|
// Compartment that this weak map is part of.
|
|
|
|
JSCompartment *compartment;
|
|
|
|
|
2011-06-15 06:21:47 +04:00
|
|
|
private:
|
2011-06-21 03:26:05 +04:00
|
|
|
// Link in a list of WeakMaps to mark iteratively and sweep in this garbage
|
2012-11-02 22:03:59 +04:00
|
|
|
// collection, headed by JSCompartment::gcWeakMapList. The last element of
|
|
|
|
// the list has NULL as its next. Maps not in the list have WeakMapNotInList
|
|
|
|
// as their next. We must distinguish these cases to avoid creating
|
|
|
|
// infinite lists when a weak map gets traced twice due to delayed marking.
|
2011-06-15 06:21:47 +04:00
|
|
|
WeakMapBase *next;
|
|
|
|
};
|
2011-04-17 08:23:44 +04:00
|
|
|
|
2011-06-15 06:21:47 +04:00
|
|
|
template <class Key, class Value,
|
2012-03-30 04:35:46 +04:00
|
|
|
class HashPolicy = DefaultHasher<Key> >
|
2012-07-16 21:54:56 +04:00
|
|
|
class WeakMap : public HashMap<Key, Value, HashPolicy, RuntimeAllocPolicy>, public WeakMapBase
|
|
|
|
{
|
2011-06-15 06:21:47 +04:00
|
|
|
public:
|
2012-07-16 21:54:56 +04:00
|
|
|
typedef HashMap<Key, Value, HashPolicy, RuntimeAllocPolicy> Base;
|
2012-08-23 21:58:24 +04:00
|
|
|
typedef typename Base::Enum Enum;
|
2011-10-13 20:33:39 +04:00
|
|
|
typedef typename Base::Range Range;
|
|
|
|
|
2012-11-02 22:03:59 +04:00
|
|
|
explicit WeakMap(JSContext *cx, JSObject *memOf=NULL)
|
|
|
|
: Base(cx), WeakMapBase(memOf, cx->compartment) { }
|
2011-06-15 06:21:47 +04:00
|
|
|
|
|
|
|
private:
|
2012-04-09 22:42:04 +04:00
|
|
|
bool markValue(JSTracer *trc, Value *x) {
|
2012-05-23 21:34:29 +04:00
|
|
|
if (gc::IsMarked(x))
|
2012-03-30 04:35:46 +04:00
|
|
|
return false;
|
2012-04-09 22:42:04 +04:00
|
|
|
gc::Mark(trc, x, "WeakMap entry");
|
2012-10-12 18:26:07 +04:00
|
|
|
JS_ASSERT(gc::IsMarked(x));
|
2012-03-30 04:35:46 +04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2011-10-26 03:07:42 +04:00
|
|
|
void nonMarkingTrace(JSTracer *trc) {
|
2011-06-29 13:27:47 +04:00
|
|
|
for (Range r = Base::all(); !r.empty(); r.popFront())
|
2012-12-18 23:52:37 +04:00
|
|
|
gc::Mark(trc, &r.front().value, "WeakMap entry");
|
2011-06-15 06:21:47 +04:00
|
|
|
}
|
|
|
|
|
2012-10-09 05:22:47 +04:00
|
|
|
bool keyNeedsMark(JSObject *key) {
|
|
|
|
if (JSWeakmapKeyDelegateOp op = key->getClass()->ext.weakmapKeyDelegateOp) {
|
|
|
|
JSObject *delegate = op(key);
|
|
|
|
/*
|
|
|
|
* 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 && gc::IsObjectMarked(&delegate);
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool keyNeedsMark(gc::Cell *cell) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-10-26 03:07:42 +04:00
|
|
|
bool markIteratively(JSTracer *trc) {
|
2011-06-15 06:21:47 +04:00
|
|
|
bool markedAny = false;
|
2012-05-23 21:34:29 +04:00
|
|
|
for (Enum e(*this); !e.empty(); e.popFront()) {
|
2011-06-29 13:27:47 +04:00
|
|
|
/* If the entry is live, ensure its key and value are marked. */
|
2012-07-16 21:54:56 +04:00
|
|
|
Key prior(e.front().key);
|
|
|
|
if (gc::IsMarked(const_cast<Key *>(&e.front().key))) {
|
2012-05-23 21:34:29 +04:00
|
|
|
if (markValue(trc, &e.front().value))
|
|
|
|
markedAny = true;
|
2012-07-16 21:54:56 +04:00
|
|
|
if (prior != e.front().key)
|
|
|
|
e.rekeyFront(e.front().key);
|
2012-10-09 05:22:47 +04:00
|
|
|
} else if (keyNeedsMark(e.front().key)) {
|
|
|
|
gc::Mark(trc, const_cast<Key *>(&e.front().key), "proxy-preserved WeakMap key");
|
|
|
|
if (prior != e.front().key)
|
|
|
|
e.rekeyFront(e.front().key);
|
|
|
|
gc::Mark(trc, &e.front().value, "WeakMap entry");
|
|
|
|
markedAny = true;
|
2012-05-23 21:34:29 +04:00
|
|
|
}
|
2011-06-15 06:21:47 +04:00
|
|
|
}
|
|
|
|
return markedAny;
|
|
|
|
}
|
|
|
|
|
2012-11-02 22:03:59 +04:00
|
|
|
void sweep() {
|
2011-06-29 13:27:47 +04:00
|
|
|
/* Remove all entries whose keys remain unmarked. */
|
2011-06-15 06:21:47 +04:00
|
|
|
for (Enum e(*this); !e.empty(); e.popFront()) {
|
2012-05-23 21:34:29 +04:00
|
|
|
Key k(e.front().key);
|
2012-10-12 13:45:30 +04:00
|
|
|
if (gc::IsAboutToBeFinalized(&k))
|
2011-06-15 06:21:47 +04:00
|
|
|
e.removeFront();
|
|
|
|
}
|
2011-10-26 03:07:42 +04:00
|
|
|
/*
|
2011-06-29 13:27:47 +04:00
|
|
|
* Once we've swept, all remaining edges should stay within the
|
|
|
|
* known-live part of the graph.
|
|
|
|
*/
|
2012-10-16 15:28:32 +04:00
|
|
|
assertEntriesNotAboutToBeFinalized();
|
2011-06-15 06:21:47 +04:00
|
|
|
}
|
2011-11-24 16:35:56 +04:00
|
|
|
|
2012-04-09 22:42:04 +04:00
|
|
|
/* memberOf can be NULL, which means that the map is not part of a JSObject. */
|
2012-03-30 04:35:46 +04:00
|
|
|
void traceMappings(WeakMapTracer *tracer) {
|
2012-04-09 22:42:04 +04:00
|
|
|
for (Range r = Base::all(); !r.empty(); r.popFront()) {
|
|
|
|
gc::Cell *key = gc::ToMarkable(r.front().key);
|
|
|
|
gc::Cell *value = gc::ToMarkable(r.front().value);
|
|
|
|
if (key && value) {
|
|
|
|
tracer->callback(tracer, memberOf,
|
|
|
|
key, gc::TraceKind(r.front().key),
|
|
|
|
value, gc::TraceKind(r.front().value));
|
|
|
|
}
|
|
|
|
}
|
2011-11-24 16:35:56 +04:00
|
|
|
}
|
2012-10-16 15:28:32 +04:00
|
|
|
|
|
|
|
protected:
|
|
|
|
void assertEntriesNotAboutToBeFinalized() {
|
|
|
|
#if DEBUG
|
|
|
|
for (Range r = Base::all(); !r.empty(); r.popFront()) {
|
|
|
|
Key k(r.front().key);
|
|
|
|
JS_ASSERT(!gc::IsAboutToBeFinalized(&k));
|
2013-02-13 05:48:10 +04:00
|
|
|
JS_ASSERT(!gc::IsAboutToBeFinalized(&r.front().value));
|
2012-10-16 15:28:32 +04:00
|
|
|
JS_ASSERT(k == r.front().key);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
2011-11-24 16:35:56 +04:00
|
|
|
};
|
|
|
|
|
2012-03-30 04:35:46 +04:00
|
|
|
} /* namespace js */
|
2011-04-17 08:23:44 +04:00
|
|
|
|
|
|
|
extern JSObject *
|
2012-09-21 09:17:49 +04:00
|
|
|
js_InitWeakMapClass(JSContext *cx, js::HandleObject obj);
|
2011-04-17 08:23:44 +04:00
|
|
|
|
|
|
|
#endif
|