зеркало из https://github.com/mozilla/gecko-dev.git
Bug 660039: Provide a WeakMap usable from C++. r=jorendorff
Remove WeakMap class; implement the JavaScript object using functions static to jsweakmap.cpp. Define a new WeakMap class template, parameterized by Key and Value types, and accepting a MarkPolicy argument saying how to mark them. Add assertions to check that we check and set the right mark bits, and tests that trip them in the presence of mistakes in earlier revisions of this patch.
This commit is contained in:
Родитель
add4733d5f
Коммит
10db00cf8a
|
@ -1605,7 +1605,7 @@ static JSStdName standard_class_atoms[] = {
|
|||
#endif
|
||||
{js_InitJSONClass, EAGER_ATOM_AND_CLASP(JSON)},
|
||||
{js_InitTypedArrayClasses, EAGER_CLASS_ATOM(ArrayBuffer), &js::ArrayBuffer::slowClass},
|
||||
{js_InitWeakMapClass, EAGER_CLASS_ATOM(WeakMap), &WeakMap::jsclass},
|
||||
{js_InitWeakMapClass, EAGER_CLASS_ATOM(WeakMap), &js::WeakMapClass},
|
||||
{NULL, 0, NULL, NULL}
|
||||
};
|
||||
|
||||
|
|
|
@ -117,6 +117,8 @@ namespace mjit {
|
|||
class JaegerCompartment;
|
||||
}
|
||||
|
||||
class WeakMapBase;
|
||||
|
||||
/*
|
||||
* GetSrcNote cache to avoid O(n^2) growth in finding a source note for a
|
||||
* given pc in a script. We use the script->code pointer to tag the cache,
|
||||
|
@ -423,7 +425,7 @@ struct JSRuntime {
|
|||
int64 gcJitReleaseTime;
|
||||
JSGCMode gcMode;
|
||||
volatile bool gcIsNeeded;
|
||||
JSObject *gcWeakMapList;
|
||||
js::WeakMapBase *gcWeakMapList;
|
||||
|
||||
/* Pre-allocated space for the GC mark stacks. Pointer type ensures alignment. */
|
||||
void *gcMarkStackObjs[js::OBJECT_MARK_STACK_SIZE / sizeof(void *)];
|
||||
|
|
|
@ -927,8 +927,6 @@ js_FinishGC(JSRuntime *rt)
|
|||
rt->compartments.clear();
|
||||
rt->atomsCompartment = NULL;
|
||||
|
||||
rt->gcWeakMapList = NULL;
|
||||
|
||||
for (GCChunkSet::Range r(rt->gcChunkSet.all()); !r.empty(); r.popFront())
|
||||
ReleaseGCChunk(rt, r.front());
|
||||
rt->gcChunkSet.clear();
|
||||
|
@ -1513,7 +1511,7 @@ namespace js {
|
|||
*
|
||||
* To implement such delayed marking of the children with minimal overhead for
|
||||
* the normal case of sufficient native stack, the code adds a field per
|
||||
* arena. The field marlingdelay->link links all arenas with delayed things
|
||||
* arena. The field markingDelay->link links all arenas with delayed things
|
||||
* into a stack list with the pointer to stack top in
|
||||
* GCMarker::unmarkedArenaStackTop. delayMarkingChildren adds
|
||||
* arenas to the stack as necessary while markDelayedChildren pops the arenas
|
||||
|
@ -2285,7 +2283,7 @@ MarkAndSweep(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIM
|
|||
* Mark weak roots.
|
||||
*/
|
||||
while (true) {
|
||||
if (!js_TraceWatchPoints(&gcmarker) && !WeakMap::markIteratively(&gcmarker))
|
||||
if (!js_TraceWatchPoints(&gcmarker) && !WeakMapBase::markAllIteratively(&gcmarker))
|
||||
break;
|
||||
gcmarker.drainMarkStack();
|
||||
}
|
||||
|
@ -2320,7 +2318,7 @@ MarkAndSweep(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIM
|
|||
GCTIMESTAMP(startSweep);
|
||||
|
||||
/* Finalize unreachable (key,value) pairs in all weak maps. */
|
||||
WeakMap::sweep(cx);
|
||||
WeakMapBase::sweepAll(&gcmarker);
|
||||
|
||||
js_SweepAtomState(cx);
|
||||
|
||||
|
@ -2657,6 +2655,7 @@ GCCycle(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIMER_P
|
|||
rt->gcRegenShapes = false;
|
||||
rt->setGCLastBytes(rt->gcBytes);
|
||||
rt->gcCurrentCompartment = NULL;
|
||||
rt->gcWeakMapList = NULL;
|
||||
|
||||
for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c)
|
||||
(*c)->setGCLastBytes((*c)->gcBytes);
|
||||
|
|
|
@ -54,25 +54,43 @@
|
|||
|
||||
using namespace js;
|
||||
|
||||
namespace js {
|
||||
|
||||
bool
|
||||
WeakMapBase::markAllIteratively(JSTracer *tracer)
|
||||
{
|
||||
bool markedAny = false;
|
||||
JSRuntime *rt = tracer->context->runtime;
|
||||
for (WeakMapBase *m = rt->gcWeakMapList; m; m = m->next) {
|
||||
if (m->markIteratively(tracer))
|
||||
markedAny = true;
|
||||
}
|
||||
return markedAny;
|
||||
}
|
||||
|
||||
void
|
||||
WeakMapBase::sweepAll(JSTracer *tracer)
|
||||
{
|
||||
JSRuntime *rt = tracer->context->runtime;
|
||||
for (WeakMapBase *m = rt->gcWeakMapList; m; m = m->next)
|
||||
m->sweep(tracer);
|
||||
}
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
bool
|
||||
JSObject::isWeakMap() const
|
||||
{
|
||||
return getClass() == &WeakMap::jsclass;
|
||||
return getClass() == &WeakMapClass;
|
||||
}
|
||||
|
||||
namespace js {
|
||||
typedef WeakMap<JSObject *, Value> ObjectValueMap;
|
||||
|
||||
WeakMap::WeakMap(JSContext *cx) :
|
||||
map(cx),
|
||||
next(NULL)
|
||||
static ObjectValueMap *
|
||||
GetObjectMap(JSObject *obj)
|
||||
{
|
||||
}
|
||||
|
||||
WeakMap *
|
||||
WeakMap::fromJSObject(JSObject *obj)
|
||||
{
|
||||
JS_ASSERT(obj->getClass() == &WeakMap::jsclass);
|
||||
return (WeakMap *)obj->getPrivate();
|
||||
JS_ASSERT(obj->isWeakMap());
|
||||
return (ObjectValueMap *)obj->getPrivate();
|
||||
}
|
||||
|
||||
static JSObject *
|
||||
|
@ -85,14 +103,14 @@ NonNullObject(JSContext *cx, Value *vp)
|
|||
return &vp->toObject();
|
||||
}
|
||||
|
||||
JSBool
|
||||
WeakMap::has(JSContext *cx, uintN argc, Value *vp)
|
||||
static JSBool
|
||||
WeakMap_has(JSContext *cx, uintN argc, Value *vp)
|
||||
{
|
||||
JSObject *obj = ToObject(cx, &vp[1]);
|
||||
if (!obj)
|
||||
return false;
|
||||
if (!obj->isWeakMap()) {
|
||||
ReportIncompatibleMethod(cx, vp, &WeakMap::jsclass);
|
||||
ReportIncompatibleMethod(cx, vp, &WeakMapClass);
|
||||
return false;
|
||||
}
|
||||
if (argc < 1) {
|
||||
|
@ -103,9 +121,9 @@ WeakMap::has(JSContext *cx, uintN argc, Value *vp)
|
|||
JSObject *key = NonNullObject(cx, &vp[2]);
|
||||
if (!key)
|
||||
return false;
|
||||
WeakMap *weakmap = fromJSObject(obj);
|
||||
if (weakmap) {
|
||||
ObjectValueMap::Ptr ptr = weakmap->map.lookup(key);
|
||||
ObjectValueMap *map = GetObjectMap(obj);
|
||||
if (map) {
|
||||
ObjectValueMap::Ptr ptr = map->lookup(key);
|
||||
if (ptr) {
|
||||
*vp = BooleanValue(true);
|
||||
return true;
|
||||
|
@ -116,14 +134,14 @@ WeakMap::has(JSContext *cx, uintN argc, Value *vp)
|
|||
return true;
|
||||
}
|
||||
|
||||
JSBool
|
||||
WeakMap::get(JSContext *cx, uintN argc, Value *vp)
|
||||
static JSBool
|
||||
WeakMap_get(JSContext *cx, uintN argc, Value *vp)
|
||||
{
|
||||
JSObject *obj = ToObject(cx, &vp[1]);
|
||||
if (!obj)
|
||||
return false;
|
||||
if (!obj->isWeakMap()) {
|
||||
ReportIncompatibleMethod(cx, vp, &WeakMap::jsclass);
|
||||
ReportIncompatibleMethod(cx, vp, &WeakMapClass);
|
||||
return false;
|
||||
}
|
||||
if (argc < 1) {
|
||||
|
@ -134,9 +152,9 @@ WeakMap::get(JSContext *cx, uintN argc, Value *vp)
|
|||
JSObject *key = NonNullObject(cx, &vp[2]);
|
||||
if (!key)
|
||||
return false;
|
||||
WeakMap *weakmap = fromJSObject(obj);
|
||||
if (weakmap) {
|
||||
ObjectValueMap::Ptr ptr = weakmap->map.lookup(key);
|
||||
ObjectValueMap *map = GetObjectMap(obj);
|
||||
if (map) {
|
||||
ObjectValueMap::Ptr ptr = map->lookup(key);
|
||||
if (ptr) {
|
||||
*vp = ptr->value;
|
||||
return true;
|
||||
|
@ -147,14 +165,14 @@ WeakMap::get(JSContext *cx, uintN argc, Value *vp)
|
|||
return true;
|
||||
}
|
||||
|
||||
JSBool
|
||||
WeakMap::delete_(JSContext *cx, uintN argc, Value *vp)
|
||||
static JSBool
|
||||
WeakMap_delete(JSContext *cx, uintN argc, Value *vp)
|
||||
{
|
||||
JSObject *obj = ToObject(cx, &vp[1]);
|
||||
if (!obj)
|
||||
return false;
|
||||
if (!obj->isWeakMap()) {
|
||||
ReportIncompatibleMethod(cx, vp, &WeakMap::jsclass);
|
||||
ReportIncompatibleMethod(cx, vp, &WeakMapClass);
|
||||
return false;
|
||||
}
|
||||
if (argc < 1) {
|
||||
|
@ -165,11 +183,11 @@ WeakMap::delete_(JSContext *cx, uintN argc, Value *vp)
|
|||
JSObject *key = NonNullObject(cx, &vp[2]);
|
||||
if (!key)
|
||||
return false;
|
||||
WeakMap *weakmap = fromJSObject(obj);
|
||||
if (weakmap) {
|
||||
ObjectValueMap::Ptr ptr = weakmap->map.lookup(key);
|
||||
ObjectValueMap *map = GetObjectMap(obj);
|
||||
if (map) {
|
||||
ObjectValueMap::Ptr ptr = map->lookup(key);
|
||||
if (ptr) {
|
||||
weakmap->map.remove(ptr);
|
||||
map->remove(ptr);
|
||||
*vp = BooleanValue(true);
|
||||
return true;
|
||||
}
|
||||
|
@ -179,14 +197,14 @@ WeakMap::delete_(JSContext *cx, uintN argc, Value *vp)
|
|||
return true;
|
||||
}
|
||||
|
||||
JSBool
|
||||
WeakMap::set(JSContext *cx, uintN argc, Value *vp)
|
||||
static JSBool
|
||||
WeakMap_set(JSContext *cx, uintN argc, Value *vp)
|
||||
{
|
||||
JSObject *obj = ToObject(cx, &vp[1]);
|
||||
if (!obj)
|
||||
return false;
|
||||
if (!obj->isWeakMap()) {
|
||||
ReportIncompatibleMethod(cx, vp, &WeakMap::jsclass);
|
||||
ReportIncompatibleMethod(cx, vp, &WeakMapClass);
|
||||
return false;
|
||||
}
|
||||
if (argc < 1) {
|
||||
|
@ -199,18 +217,18 @@ WeakMap::set(JSContext *cx, uintN argc, Value *vp)
|
|||
return false;
|
||||
Value value = (argc > 1) ? vp[3] : UndefinedValue();
|
||||
|
||||
WeakMap *table = (WeakMap *)obj->getPrivate();
|
||||
if (!table) {
|
||||
table = cx->new_<WeakMap>(cx);
|
||||
if (!table->map.init()) {
|
||||
cx->delete_(table);
|
||||
ObjectValueMap *map = GetObjectMap(obj);
|
||||
if (!map) {
|
||||
map = cx->new_<ObjectValueMap>(cx);
|
||||
if (!map->init()) {
|
||||
cx->delete_(map);
|
||||
goto out_of_memory;
|
||||
}
|
||||
obj->setPrivate(table);
|
||||
obj->setPrivate(map);
|
||||
}
|
||||
|
||||
*vp = UndefinedValue();
|
||||
if (!table->map.put(key, value))
|
||||
if (!map->put(key, value))
|
||||
goto out_of_memory;
|
||||
return true;
|
||||
|
||||
|
@ -219,93 +237,31 @@ WeakMap::set(JSContext *cx, uintN argc, Value *vp)
|
|||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
WeakMap::mark(JSTracer *trc, JSObject *obj)
|
||||
static void
|
||||
WeakMap_mark(JSTracer *trc, JSObject *obj)
|
||||
{
|
||||
WeakMap *table = fromJSObject(obj);
|
||||
if (table) {
|
||||
if (IS_GC_MARKING_TRACER(trc)) {
|
||||
if (table->map.empty()) {
|
||||
trc->context->delete_(table);
|
||||
obj->setPrivate(NULL);
|
||||
return;
|
||||
}
|
||||
JSRuntime *rt = trc->context->runtime;
|
||||
table->next = rt->gcWeakMapList;
|
||||
rt->gcWeakMapList = obj;
|
||||
ObjectValueMap *map = GetObjectMap(obj);
|
||||
if (map) {
|
||||
if (IS_GC_MARKING_TRACER(trc) && map->empty()) {
|
||||
trc->context->delete_(map);
|
||||
obj->setPrivate(NULL);
|
||||
} else {
|
||||
for (ObjectValueMap::Range r = table->map.all(); !r.empty(); r.popFront()) {
|
||||
JSObject *key = r.front().key;
|
||||
Value &value = r.front().value;
|
||||
js::gc::MarkObject(trc, *key, "key");
|
||||
js::gc::MarkValue(trc, value, "value");
|
||||
}
|
||||
map->trace(trc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Walk through the previously collected list of tables and mark rows
|
||||
* iteratively.
|
||||
*/
|
||||
bool
|
||||
WeakMap::markIteratively(JSTracer *trc)
|
||||
static void
|
||||
WeakMap_finalize(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
JSContext *cx = trc->context;
|
||||
JSRuntime *rt = cx->runtime;
|
||||
|
||||
bool again = false;
|
||||
JSObject *obj = rt->gcWeakMapList;
|
||||
while (obj) {
|
||||
WeakMap *table = fromJSObject(obj);
|
||||
for (ObjectValueMap::Range r = table->map.all(); !r.empty(); r.popFront()) {
|
||||
JSObject *key = r.front().key;
|
||||
Value &value = r.front().value;
|
||||
if (value.isMarkable() && !IsAboutToBeFinalized(cx, key)) {
|
||||
/* If the key is alive, mark the value if needed. */
|
||||
if (IsAboutToBeFinalized(cx, value.toGCThing())) {
|
||||
js::gc::MarkValue(trc, value, "value");
|
||||
/* We revived a value with children, we have to iterate again. */
|
||||
if (value.isGCThing())
|
||||
again = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
obj = table->next;
|
||||
}
|
||||
return again;
|
||||
ObjectValueMap *map = GetObjectMap(obj);
|
||||
cx->delete_(map);
|
||||
}
|
||||
|
||||
void
|
||||
WeakMap::sweep(JSContext *cx)
|
||||
static JSBool
|
||||
WeakMap_construct(JSContext *cx, uintN argc, Value *vp)
|
||||
{
|
||||
JSRuntime *rt = cx->runtime;
|
||||
|
||||
JSObject *obj = rt->gcWeakMapList;
|
||||
while (obj) {
|
||||
WeakMap *table = fromJSObject(obj);
|
||||
for (ObjectValueMap::Enum e(table->map); !e.empty(); e.popFront()) {
|
||||
if (IsAboutToBeFinalized(cx, e.front().key))
|
||||
e.removeFront();
|
||||
}
|
||||
obj = table->next;
|
||||
}
|
||||
|
||||
rt->gcWeakMapList = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
WeakMap::finalize(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
WeakMap *table = fromJSObject(obj);
|
||||
if (table)
|
||||
cx->delete_(table);
|
||||
}
|
||||
|
||||
JSBool
|
||||
WeakMap::construct(JSContext *cx, uintN argc, Value *vp)
|
||||
{
|
||||
JSObject *obj = NewBuiltinClassInstance(cx, &WeakMap::jsclass);
|
||||
JSObject *obj = NewBuiltinClassInstance(cx, &WeakMapClass);
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
|
@ -315,7 +271,9 @@ WeakMap::construct(JSContext *cx, uintN argc, Value *vp)
|
|||
return true;
|
||||
}
|
||||
|
||||
Class WeakMap::jsclass = {
|
||||
namespace js {
|
||||
|
||||
Class WeakMapClass = {
|
||||
"WeakMap",
|
||||
JSCLASS_HAS_PRIVATE |
|
||||
JSCLASS_HAS_CACHED_PROTO(JSProto_WeakMap),
|
||||
|
@ -326,31 +284,31 @@ Class WeakMap::jsclass = {
|
|||
EnumerateStub,
|
||||
ResolveStub,
|
||||
ConvertStub,
|
||||
WeakMap::finalize,
|
||||
WeakMap_finalize,
|
||||
NULL, /* reserved0 */
|
||||
NULL, /* checkAccess */
|
||||
NULL, /* call */
|
||||
NULL, /* construct */
|
||||
NULL, /* xdrObject */
|
||||
NULL, /* hasInstance */
|
||||
WeakMap::mark
|
||||
WeakMap_mark
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
JSFunctionSpec WeakMap::methods[] = {
|
||||
JS_FN("has", WeakMap::has, 1, 0),
|
||||
JS_FN("get", WeakMap::get, 2, 0),
|
||||
JS_FN("delete", WeakMap::delete_, 1, 0),
|
||||
JS_FN("set", WeakMap::set, 2, 0),
|
||||
static JSFunctionSpec weak_map_methods[] = {
|
||||
JS_FN("has", WeakMap_has, 1, 0),
|
||||
JS_FN("get", WeakMap_get, 2, 0),
|
||||
JS_FN("delete", WeakMap_delete, 1, 0),
|
||||
JS_FN("set", WeakMap_set, 2, 0),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
JSObject *
|
||||
js_InitWeakMapClass(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
JSObject *proto = js_InitClass(cx, obj, NULL, &WeakMap::jsclass, WeakMap::construct, 0,
|
||||
NULL, WeakMap::methods, NULL, NULL);
|
||||
JSObject *proto = js_InitClass(cx, obj, NULL, &WeakMapClass, WeakMap_construct, 0,
|
||||
NULL, weak_map_methods, NULL, NULL);
|
||||
if (!proto)
|
||||
return NULL;
|
||||
|
||||
|
|
|
@ -48,37 +48,182 @@
|
|||
|
||||
namespace js {
|
||||
|
||||
typedef js::HashMap<JSObject *, Value, DefaultHasher<JSObject *>, RuntimeAllocPolicy>
|
||||
ObjectValueMap;
|
||||
// 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.
|
||||
//
|
||||
// You must call this table's 'mark' method when the object of which it is a part is
|
||||
// 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.
|
||||
//
|
||||
// You may provide your own MarkPolicy class to specify how keys and values are marked; a
|
||||
// policy template provides default definitions for some common key/value type
|
||||
// combinations.
|
||||
//
|
||||
// Details:
|
||||
//
|
||||
// The interface is as for a js::HashMap, with the following additions:
|
||||
//
|
||||
// - You must call the WeakMap's 'trace' member function when you discover that the map is
|
||||
// part of a live object. (You'll typically call this from the containing type's 'trace'
|
||||
// function.)
|
||||
//
|
||||
// - There is no AllocPolicy parameter; these are used with our garbage collector, so
|
||||
// RuntimeAllocPolicy is hard-wired.
|
||||
//
|
||||
// - Optional fourth template parameter is a class MarkPolicy, with the following constructor:
|
||||
//
|
||||
// MarkPolicy(JSTracer *)
|
||||
//
|
||||
// and the following static member functions:
|
||||
//
|
||||
// bool keyMarked(Key &k)
|
||||
// bool valueMarked(Value &v)
|
||||
// Return true if k/v has been marked as reachable by the collector, false otherwise.
|
||||
// void markKey(Key &k, const char *description)
|
||||
// void markValue(Value &v, const char *description)
|
||||
// Mark k/v as reachable by the collector, using trc. Use description to identify
|
||||
// k/v in debugging. (markKey is used only for non-marking tracers, other code
|
||||
// using the GC heap tracing functions to map the heap for some purpose or other.)
|
||||
//
|
||||
// If omitted, this parameter defaults to js::DefaultMarkPolicy<Key, Value>, a policy
|
||||
// template with the obvious definitions for some typical SpiderMonkey type combinations.
|
||||
|
||||
class WeakMap {
|
||||
ObjectValueMap map;
|
||||
JSObject *next;
|
||||
// A policy template holding default marking algorithms for common type combinations. This
|
||||
// provides default types for WeakMap's MarkPolicy template parameter.
|
||||
template <class Key, class Value> class DefaultMarkPolicy;
|
||||
|
||||
static WeakMap *fromJSObject(JSObject *obj);
|
||||
// Common base class for all WeakMap specializations. The collector uses this to call
|
||||
// their markIteratively and sweep methods.
|
||||
class WeakMapBase {
|
||||
public:
|
||||
WeakMapBase() : next(NULL) { }
|
||||
|
||||
static JSBool has(JSContext *cx, uintN argc, Value *vp);
|
||||
static JSBool get(JSContext *cx, uintN argc, Value *vp);
|
||||
static JSBool delete_(JSContext *cx, uintN argc, Value *vp);
|
||||
static JSBool set(JSContext *cx, uintN argc, Value *vp);
|
||||
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.
|
||||
JSRuntime *rt = tracer->context->runtime;
|
||||
next = rt->gcWeakMapList;
|
||||
rt->gcWeakMapList = this;
|
||||
} 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
|
||||
// must use their own means for cycle detection. So here we do a conservative
|
||||
// approximation: pretend all keys are live.
|
||||
nonMarkingTrace(tracer);
|
||||
}
|
||||
}
|
||||
|
||||
// Garbage collector entry points.
|
||||
|
||||
// Check all weak maps that have been marked as live so far in this garbage
|
||||
// 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.
|
||||
static bool markAllIteratively(JSTracer *tracer);
|
||||
|
||||
// Remove entries whose keys are dead from all weak maps marked as live in this
|
||||
// garbage collection.
|
||||
static void sweepAll(JSTracer *tracer);
|
||||
|
||||
protected:
|
||||
static void mark(JSTracer *trc, JSObject *obj);
|
||||
static void finalize(JSContext *cx, JSObject *obj);
|
||||
// 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;
|
||||
virtual void sweep(JSTracer *tracer) = 0;
|
||||
|
||||
private:
|
||||
// Link in a list of all the WeakMaps we have marked in this garbage collection,
|
||||
// headed by JSRuntime::gcWeakMapList.
|
||||
WeakMapBase *next;
|
||||
};
|
||||
|
||||
template <class Key, class Value,
|
||||
class HashPolicy = DefaultHasher<Key>,
|
||||
class MarkPolicy = DefaultMarkPolicy<Key, Value> >
|
||||
class WeakMap : public HashMap<Key, Value, HashPolicy, RuntimeAllocPolicy>, public WeakMapBase {
|
||||
private:
|
||||
typedef HashMap<Key, Value, HashPolicy, RuntimeAllocPolicy> Base;
|
||||
typedef typename Base::Range Range;
|
||||
typedef typename Base::Enum Enum;
|
||||
|
||||
public:
|
||||
WeakMap(JSContext *cx);
|
||||
WeakMap(JSContext *cx) : Base(cx) { }
|
||||
|
||||
static JSBool construct(JSContext *cx, uintN argc, Value *vp);
|
||||
private:
|
||||
void nonMarkingTrace(JSTracer *tracer) {
|
||||
MarkPolicy t(tracer);
|
||||
for (Range r = Base::all(); !r.empty(); r.popFront()) {
|
||||
t.markKey(r.front().key, "WeakMap entry key");
|
||||
t.markValue(r.front().value, "WeakMap entry value");
|
||||
}
|
||||
}
|
||||
|
||||
static bool markIteratively(JSTracer *trc);
|
||||
static void sweep(JSContext *cx);
|
||||
bool markIteratively(JSTracer *tracer) {
|
||||
MarkPolicy t(tracer);
|
||||
bool markedAny = false;
|
||||
for (Range r = Base::all(); !r.empty(); r.popFront()) {
|
||||
/* If the key is alive, mark the value if needed. */
|
||||
if (!t.valueMarked(r.front().value) && t.keyMarked(r.front().key)) {
|
||||
t.markValue(r.front().value, "WeakMap entry with live key");
|
||||
/* We revived a value with children, we have to iterate again. */
|
||||
markedAny = true;
|
||||
}
|
||||
}
|
||||
return markedAny;
|
||||
}
|
||||
|
||||
static Class jsclass;
|
||||
static JSFunctionSpec methods[];
|
||||
void sweep(JSTracer *tracer) {
|
||||
MarkPolicy t(tracer);
|
||||
for (Enum e(*this); !e.empty(); e.popFront()) {
|
||||
if (!t.keyMarked(e.front().key))
|
||||
e.removeFront();
|
||||
}
|
||||
#if DEBUG
|
||||
// Once we've swept, all edges should stay within the known-live part of the graph.
|
||||
for (Range r = Base::all(); !r.empty(); r.popFront()) {
|
||||
JS_ASSERT(t.keyMarked(r.front().key));
|
||||
JS_ASSERT(t.valueMarked(r.front().value));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
// Marking policy for maps from JSObject pointers to js::Values.
|
||||
template <>
|
||||
class DefaultMarkPolicy<JSObject *, Value> {
|
||||
private:
|
||||
JSTracer *tracer;
|
||||
public:
|
||||
DefaultMarkPolicy(JSTracer *t) : tracer(t) { }
|
||||
bool keyMarked(JSObject *k) { return !IsAboutToBeFinalized(tracer->context, k); }
|
||||
bool valueMarked(const Value &v) {
|
||||
if (v.isMarkable())
|
||||
return !IsAboutToBeFinalized(tracer->context, v.toGCThing());
|
||||
else
|
||||
return true;
|
||||
}
|
||||
void markKey(JSObject *k, const char *description) {
|
||||
js::gc::MarkObject(tracer, *k, description);
|
||||
}
|
||||
void markValue(const Value &v, const char *description) {
|
||||
js::gc::MarkValue(tracer, v, description);
|
||||
}
|
||||
};
|
||||
|
||||
// The class of JavaScript WeakMap objects.
|
||||
extern Class WeakMapClass;
|
||||
|
||||
}
|
||||
|
||||
extern JSObject *
|
||||
|
|
|
@ -95,6 +95,10 @@ function test()
|
|||
check(function() typeof map.get(key) == "undefined");
|
||||
check(function() !map.has(key));
|
||||
|
||||
var value = { };
|
||||
map.set(new Object(), value);
|
||||
gc(); gc(); gc();
|
||||
|
||||
print ("done");
|
||||
|
||||
reportCompare(0, TestFailCount, "weak map tests");
|
||||
|
|
Загрузка…
Ссылка в новой задаче