Use WeakMap for the Debug::objects, the mapping from referents to Debug.Objects. r=jorendorff, push=jorendorff.

This commit is contained in:
Jim Blandy 2011-06-15 17:53:26 -05:00
parent fcfd80cfc5
commit f79cb0d6c7
3 changed files with 65 additions and 59 deletions

View File

@ -150,7 +150,7 @@ enum {
Debug::Debug(JSObject *dbg, JSObject *hooks)
: object(dbg), hooksObject(hooks), uncaughtExceptionHook(NULL), enabled(true),
hasDebuggerHandler(false), hasThrowHandler(false)
hasDebuggerHandler(false), hasThrowHandler(false), objects(dbg->compartment()->rt)
{
// This always happens within a request on some cx.
JSRuntime *rt = dbg->compartment()->rt;
@ -259,7 +259,7 @@ Debug::wrapDebuggeeValue(JSContext *cx, Value *vp)
return false;
}
ObjectMap::AddPtr p = objects.lookupForAdd(ccwobj);
ObjectWeakMap::AddPtr p = objects.lookupForAdd(ccwobj);
if (p) {
vp->setObject(*p->value);
} else {
@ -560,36 +560,6 @@ Debug::mark(GCMarker *trc, JSCompartment *comp, JSGCInvocationKind gckind)
markedAny = true;
}
}
// Handling Debug.Objects:
//
// If obj (the Debug object) hasn't been marked
// yet, it may not be live, so don't mark anything.
//
// If comp is the debuggee's compartment, do nothing. No
// referent objects will be collected, since we have a
// wrapper of each one.
//
// If comp is the debugger's compartment, mark all
// Debug.Objects, since the referents might be alive and
// therefore the table entries must remain.
//
// If comp is null, then for each key (referent-wrapper)
// that is marked, mark the corresponding value.
//
if (obj->isMarked() && (!comp || obj->compartment() == comp)) {
for (ObjectMap::Range r = dbg->objects.all(); !r.empty(); r.popFront()) {
// The unwrap() call below has the following effect: we
// mark the Debug.Object if the *referent* is alive,
// even if the CCW of the referent seems unreachable.
if (!r.front().value->isMarked() &&
(comp || r.front().key->unwrap()->isMarked())) {
MarkObject(trc, *r.front().value,
"Debug.Object with live referent");
markedAny = true;
}
}
}
}
}
}
@ -598,21 +568,29 @@ Debug::mark(GCMarker *trc, JSCompartment *comp, JSGCInvocationKind gckind)
}
void
Debug::trace(JSTracer *trc, JSObject *obj)
Debug::traceObject(JSTracer *trc, JSObject *obj)
{
if (Debug *dbg = (Debug *) obj->getPrivate()) {
MarkObject(trc, *dbg->hooksObject, "hooks");
if (dbg->uncaughtExceptionHook)
MarkObject(trc, *dbg->uncaughtExceptionHook, "hooks");
if (Debug *dbg = Debug::fromJSObject(obj))
dbg->trace(trc);
}
// Mark Debug.Frame objects that are reachable from JS if we look them up
// again (because the corresponding StackFrame is still on the stack).
for (FrameMap::Enum e(dbg->frames); !e.empty(); e.popFront()) {
JSObject *frameobj = e.front().value;
JS_ASSERT(frameobj->getPrivate());
MarkObject(trc, *frameobj, "live Debug.Frame");
}
void
Debug::trace(JSTracer *trc)
{
MarkObject(trc, *hooksObject, "hooks");
if (uncaughtExceptionHook)
MarkObject(trc, *uncaughtExceptionHook, "hooks");
// Mark Debug.Frame objects that are reachable from JS if we look them up
// again (because the corresponding StackFrame is still on the stack).
for (FrameMap::Enum e(frames); !e.empty(); e.popFront()) {
JSObject *frameobj = e.front().value;
JS_ASSERT(frameobj->getPrivate());
MarkObject(trc, *frameobj, "live Debug.Frame");
}
// Trace the referent -> Debug.Object weak map.
objects.trace(trc);
}
void
@ -621,14 +599,7 @@ Debug::sweepAll(JSRuntime *rt)
for (JSCList *p = &rt->debuggerList; (p = JS_NEXT_LINK(p)) != &rt->debuggerList;) {
Debug *dbg = (Debug *) ((unsigned char *) p - offsetof(Debug, link));
if (dbg->object->isMarked()) {
// Sweep ObjectMap entries for referents being collected.
for (ObjectMap::Enum e(dbg->objects); !e.empty(); e.popFront()) {
JS_ASSERT(e.front().key->isMarked() == e.front().value->isMarked());
if (!e.front().value->isMarked())
e.removeFront();
}
} else {
if (!dbg->object->isMarked()) {
// If this Debug is being GC'd, detach it from its debuggees. In the case of
// runtime-wide GC, the debuggee might be GC'd too. Since detaching requires
// access to both objects, this must be done before finalize time. However, in
@ -690,7 +661,7 @@ Class Debug::jsclass = {
NULL, /* construct */
NULL, /* xdrObject */
NULL, /* hasInstance */
Debug::trace
Debug::traceObject
};
JSBool

View File

@ -46,6 +46,7 @@
#include "jscompartment.h"
#include "jsgc.h"
#include "jshashtable.h"
#include "jsweakmap.h"
#include "jswrapper.h"
#include "jsvalue.h"
#include "vm/GlobalObject.h"
@ -75,12 +76,28 @@ class Debug {
FrameMap;
FrameMap frames;
// Mark policy for ObjectMap.
class ObjectMapMarkPolicy: public DefaultMarkPolicy<JSObject *, JSObject *> {
typedef DefaultMarkPolicy<JSObject *, JSObject *> Base;
public:
explicit ObjectMapMarkPolicy(JSTracer *tracer) : Base(tracer) { }
// The unwrap() call has the following effect: we mark the Debug.Object if the
// *referent* is alive, even if the CCW of the referent seems unreachable. Since
// the value always refers to the CCW, marking the value marks the CCW, so we
// needn't worry that the CCW will go dead.
bool keyMarked(JSObject *k) { return k->unwrap()->isMarked(); }
void markKey(JSObject *k, const char *description) {
js::gc::MarkObject(tracer, *k->unwrap(), description);
}
};
// Keys are referents, values are Debug.Object objects. The combination of
// the a key being live and this Debug being live keeps the corresponding
// Debug.Object alive.
typedef HashMap<JSObject *, JSObject *, DefaultHasher<JSObject *>, SystemAllocPolicy>
ObjectMap;
ObjectMap objects;
typedef WeakMap<JSObject *, JSObject *, DefaultHasher<JSObject *>, ObjectMapMarkPolicy>
ObjectWeakMap;
ObjectWeakMap objects;
bool addDebuggeeGlobal(JSContext *cx, GlobalObject *obj);
void removeDebuggeeGlobal(GlobalObject *global, GlobalObjectSet::Enum *compartmentEnum,
@ -91,7 +108,8 @@ class Debug {
bool callHook = true);
JSObject *unwrapDebuggeeArgument(JSContext *cx, Value *vp);
static void trace(JSTracer *trc, JSObject *obj);
static void traceObject(JSTracer *trc, JSObject *obj);
void trace(JSTracer *trc);
static void finalize(JSContext *cx, JSObject *obj);
static Class jsclass;

View File

@ -45,6 +45,7 @@
#include "jsapi.h"
#include "jscntxt.h"
#include "jsobj.h"
#include "jsgcmark.h"
namespace js {
@ -158,7 +159,8 @@ class WeakMap : public HashMap<Key, Value, HashPolicy, RuntimeAllocPolicy>, publ
typedef typename Base::Enum Enum;
public:
WeakMap(JSContext *cx) : Base(cx) { }
explicit WeakMap(JSRuntime *rt) : Base(rt) { }
explicit WeakMap(JSContext *cx) : Base(cx) { }
private:
void nonMarkingTrace(JSTracer *tracer) {
@ -199,7 +201,6 @@ class WeakMap : public HashMap<Key, Value, HashPolicy, RuntimeAllocPolicy>, publ
}
};
// Marking policy for maps from JSObject pointers to js::Values.
template <>
class DefaultMarkPolicy<JSObject *, Value> {
private:
@ -220,6 +221,22 @@ class DefaultMarkPolicy<JSObject *, Value> {
}
};
template <>
class DefaultMarkPolicy<JSObject *, JSObject *> {
public:
DefaultMarkPolicy(JSTracer *t) : tracer(t) { }
bool keyMarked(JSObject *k) { return !IsAboutToBeFinalized(tracer->context, k); }
bool valueMarked(JSObject *v) { return !IsAboutToBeFinalized(tracer->context, v); }
void markKey(JSObject *k, const char *description) {
js::gc::MarkObject(tracer, *k, description);
}
void markValue(JSObject *v, const char *description) {
js::gc::MarkObject(tracer, *v, description);
}
protected:
JSTracer *tracer;
};
// The class of JavaScript WeakMap objects.
extern Class WeakMapClass;