Debuggees are globals, not compartments.

This commit is contained in:
Jason Orendorff 2011-05-23 11:11:09 -05:00
parent 2e05c87e09
commit 5323e5a91c
12 changed files with 300 additions and 131 deletions

View File

@ -0,0 +1,12 @@
// |jit-test| debug
// Events in a non-debuggee are ignored, even if a debuggee is in the same compartment.
var g1 = newGlobal('new-compartment');
var g2 = g1.eval("newGlobal('same-compartment')");
var dbg = new Debug(g1);
var hits = 0;
dbg.hooks = {debuggerHandler: function () { hits++; }};
g1.eval("debugger;");
assertEq(hits, 1);
g2.eval("debugger;");
assertEq(hits, 1);

View File

@ -2022,7 +2022,7 @@ struct JSClass {
* with the following flags. Failure to use JSCLASS_GLOBAL_FLAGS was
* prevously allowed, but is now an ES5 violation and thus unsupported.
*/
#define JSCLASS_GLOBAL_SLOT_COUNT (JSProto_LIMIT * 3 + 6)
#define JSCLASS_GLOBAL_SLOT_COUNT (JSProto_LIMIT * 3 + 7)
#define JSCLASS_GLOBAL_FLAGS \
(JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT))

View File

@ -147,10 +147,11 @@ JSCompartment::init()
#ifdef JS_METHODJIT
if (!(jaegerCompartment = rt->new_<mjit::JaegerCompartment>()))
return false;
return jaegerCompartment->Initialize();
#else
return true;
if (!jaegerCompartment->Initialize())
return false;
#endif
return debuggees.init();
}
#ifdef JS_METHODJIT
@ -602,15 +603,3 @@ JSCompartment::isAboutToBeCollected(JSGCInvocationKind gckind)
{
return !hold && (arenaListsAreEmpty() || gckind == GC_LAST_CONTEXT);
}
void
JSCompartment::removeDebug(Debug *dbg)
{
for (Debug **p = debuggers.begin(); p != debuggers.end(); p++) {
if (*p == dbg) {
debuggers.erase(p);
return;
}
}
JS_NOT_REACHED("JSCompartment::removeDebug");
}

View File

@ -48,6 +48,7 @@
#include "jsgcstats.h"
#include "jsclist.h"
#include "jsxml.h"
#include "vm/GlobalObject.h"
#ifdef _MSC_VER
#pragma warning(push)
@ -381,8 +382,6 @@ class DtoaCache {
} /* namespace js */
struct JS_FRIEND_API(JSCompartment) {
typedef js::Vector<js::Debug *, 0, js::SystemAllocPolicy> DebugVector;
JSRuntime *rt;
JSPrincipals *principals;
js::gc::Chunk *chunk;
@ -518,7 +517,11 @@ struct JS_FRIEND_API(JSCompartment) {
BackEdgeMap backEdgeTable;
DebugVector debuggers;
/*
* Weak reference to each global in this compartment that is a debuggee.
* Each global has its own list of debuggers.
*/
js::GlobalObjectSet debuggees;
JSCompartment *thisForCtor() { return this; }
public:
@ -529,13 +532,14 @@ struct JS_FRIEND_API(JSCompartment) {
size_t backEdgeCount(jsbytecode *pc) const;
size_t incBackEdgeCount(jsbytecode *pc);
const DebugVector &getDebuggers() const { return debuggers; }
bool addDebug(js::Debug *dbg) {
js::GlobalObjectSet &getDebuggees() { return debuggees; }
bool addDebuggee(js::GlobalObject *global) {
JS_ASSERT(debugMode);
return debuggers.append(dbg);
return !!debuggees.put(global);
}
void removeDebuggee(js::GlobalObject *global) {
debuggees.remove(global);
}
void removeDebug(js::Debug *dbg);
};
#define JS_SCRIPTS_TO_GC(cx) ((cx)->compartment->scriptsToGC)

View File

@ -136,27 +136,37 @@ enum {
JSSLOT_DEBUG_COUNT
};
Debug::Debug(JSObject *dbg, JSObject *hooks, JSCompartment *compartment)
: object(dbg), debuggeeCompartment(compartment), hooksObject(hooks),
uncaughtExceptionHook(NULL), enabled(true), hasDebuggerHandler(false),
hasThrowHandler(false)
Debug::Debug(JSObject *dbg, JSObject *hooks)
: object(dbg), debuggeeGlobal(NULL), hooksObject(hooks), uncaughtExceptionHook(NULL),
enabled(true), hasDebuggerHandler(false), hasThrowHandler(false)
{
// This always happens within a request on some cx.
AutoLockGC lock(compartment->rt);
JS_APPEND_LINK(&link, &compartment->rt->debuggerList);
JSRuntime *rt = dbg->compartment()->rt;
AutoLockGC lock(rt);
JS_APPEND_LINK(&link, &rt->debuggerList);
}
Debug::~Debug()
{
// This always happens in the GC thread, so no locking is required.
JS_ASSERT(object->compartment()->rt->gcRunning);
if (debuggeeGlobal) {
// This happens only during per-compartment GC. See comment in
// Debug::sweepAll.
JS_ASSERT(object->compartment()->rt->gcCurrentCompartment == object->compartment());
removeDebuggee(debuggeeGlobal, NULL);
}
// This always happens in the GC thread, so no locking is required.
JS_REMOVE_LINK(&link);
}
bool
Debug::init()
Debug::init(JSContext *cx)
{
return frames.init() && objects.init();
bool ok = frames.init() && objects.init();
if (!ok)
js_ReportOutOfMemory(cx);
return ok;
}
JS_STATIC_ASSERT(uintN(JSSLOT_DEBUGFRAME_OWNER) == uintN(JSSLOT_DEBUGOBJECT_OWNER));
@ -216,9 +226,12 @@ void
Debug::slowPathLeaveStackFrame(JSContext *cx)
{
StackFrame *fp = cx->fp();
JSCompartment *compartment = cx->compartment;
const JSCompartment::DebugVector &debuggers = compartment->getDebuggers();
for (Debug **p = debuggers.begin(); p != debuggers.end(); p++) {
GlobalObject *global = fp->scopeChain().getGlobal();
// FIXME This assumes that only current debuggers of global have Frame
// objects for fp. Adding .removeDebuggee will therefore break this code.
if (GlobalObject::DebugVector *debuggers = global->getDebuggers()) {
for (Debug **p = debuggers->begin(); p != debuggers->end(); p++) {
Debug *dbg = *p;
if (FrameMap::Ptr p = dbg->frames.lookup(fp)) {
JSObject *frameobj = p->value;
@ -227,6 +240,7 @@ Debug::slowPathLeaveStackFrame(JSContext *cx)
}
}
}
}
bool
Debug::wrapDebuggeeValue(JSContext *cx, Value *vp)
@ -469,21 +483,22 @@ Debug::dispatchHook(JSContext *cx, js::Value *vp, DebugObservesMethod observesEv
// Note: In the general case, 'triggered' contains references to objects in
// different compartments--every compartment *except* this one.
AutoValueVector triggered(cx);
JSCompartment *compartment = cx->compartment;
const JSCompartment::DebugVector &debuggers = compartment->getDebuggers();
for (Debug **p = debuggers.begin(); p != debuggers.end(); p++) {
GlobalObject *global = cx->fp()->scopeChain().getGlobal();
if (GlobalObject::DebugVector *debuggers = global->getDebuggers()) {
for (Debug **p = debuggers->begin(); p != debuggers->end(); p++) {
Debug *dbg = *p;
if ((dbg->*observesEvent)()) {
if (!triggered.append(ObjectValue(*dbg->toJSObject())))
return JSTRAP_ERROR;
}
}
}
// Deliver the event to each debugger, checking again to make sure it
// should still be delivered.
for (Value *p = triggered.begin(); p != triggered.end(); p++) {
Debug *dbg = Debug::fromJSObject(&p->toObject());
if (dbg->observesCompartment(compartment) && (dbg->*observesEvent)()) {
if (dbg->debuggeeGlobal == global && (dbg->*observesEvent)()) {
JSTrapStatus st = (dbg->*handleEvent)(cx, vp);
if (st != JSTRAP_CONTINUE)
return st;
@ -497,9 +512,13 @@ Debug::dispatchHook(JSContext *cx, js::Value *vp, DebugObservesMethod observesEv
bool
Debug::mark(GCMarker *trc, JSCompartment *comp, JSGCInvocationKind gckind)
{
// Debuggers are marked during the incremental long tail of the GC mark
// phase. This method returns true if it has to mark anything; GC calls it
// repeatedly until it returns false.
bool markedAny = false;
// Search for Debug objects in the given compartment. We do this by
// searching all the compartments being debugged.
bool markedAny = false;
JSRuntime *rt = trc->context->runtime;
for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); c++) {
JSCompartment *dc = *c;
@ -509,8 +528,14 @@ Debug::mark(GCMarker *trc, JSCompartment *comp, JSGCInvocationKind gckind)
// debug itself). If comp is null, this is a global GC and we
// search every dc that is live.
if (comp ? dc != comp : !dc->isAboutToBeCollected(gckind)) {
const JSCompartment::DebugVector &debuggers = dc->getDebuggers();
for (Debug **p = debuggers.begin(); p != debuggers.end(); p++) {
const GlobalObjectSet &debuggees = dc->getDebuggees();
for (GlobalObjectSet::Range r = debuggees.all(); !r.empty(); r.popFront()) {
GlobalObject *global = r.front();
// Every debuggee has at least one debugger, so in this case
// getDebuggers can't return NULL.
const GlobalObject::DebugVector *debuggers = global->getDebuggers();
for (Debug **p = debuggers->begin(); p != debuggers->end(); p++) {
Debug *dbg = *p;
JSObject *obj = dbg->toJSObject();
@ -528,15 +553,15 @@ Debug::mark(GCMarker *trc, JSCompartment *comp, JSGCInvocationKind gckind)
// Handling Debug.Objects:
//
// If comp is the debuggee's compartment, do nothing. No
// referent objects will be collected, since we have a wrapper
// of each one.
// 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 comp is null, then for each key (referent-wrapper)
// that is marked, mark the corresponding value.
//
if (!comp || obj->compartment() == comp) {
for (ObjectMap::Range r = dbg->objects.all(); !r.empty(); r.popFront()) {
@ -546,7 +571,8 @@ Debug::mark(GCMarker *trc, JSCompartment *comp, JSGCInvocationKind gckind)
if (!r.front().value->isMarked() &&
(comp || r.front().key->unwrap()->isMarked()))
{
MarkObject(trc, *r.front().value, "Debug.Object with live referent");
MarkObject(trc, *r.front().value,
"Debug.Object with live referent");
markedAny = true;
}
}
@ -554,6 +580,7 @@ Debug::mark(GCMarker *trc, JSCompartment *comp, JSGCInvocationKind gckind)
}
}
}
}
return markedAny;
}
@ -578,34 +605,92 @@ Debug::trace(JSTracer *trc, JSObject *obj)
void
Debug::sweepAll(JSRuntime *rt)
{
// Sweep ObjectMap entries for objects being collected.
for (JSCList *p = &rt->debuggerList; (p = JS_NEXT_LINK(p)) != &rt->debuggerList;) {
Debug *dbg = (Debug *) ((unsigned char *) p - offsetof(Debug, link));
// 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 a per-compartment GC, it is impossible
// for both objects to be GC'd (since they are in different
// compartments), so in that case we just wait for Debug::finalize.
if (!dbg->object->isMarked()) {
if (dbg->debuggeeGlobal)
dbg->removeDebuggee(dbg->debuggeeGlobal, NULL);
}
// 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();
}
}
for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); c++)
sweepCompartment(*c);
}
void
Debug::sweepCompartment(JSCompartment *compartment)
{
// For each debuggee being GC'd, detach it from all its debuggers.
GlobalObjectSet &debuggees = compartment->getDebuggees();
for (GlobalObjectSet::Enum e(debuggees); !e.empty(); e.popFront()) {
GlobalObject *global = e.front();
if (!global->isMarked()) {
const GlobalObject::DebugVector *debuggers = global->getDebuggers();
JS_ASSERT(!debuggers->empty());
for (size_t i = debuggers->length(); i--; )
(*debuggers)[i]->removeDebuggee(global, &e);
}
}
}
void
Debug::detachFromCompartment(JSCompartment *comp)
{
for (GlobalObjectSet::Enum e(comp->getDebuggees()); !e.empty(); e.popFront()) {
GlobalObject *global = e.front();
for (;;) {
GlobalObject::DebugVector *debuggers = global->getDebuggers();
if (!debuggers || debuggers->empty())
break;
debuggers->back()->removeDebuggee(global, &e);
}
e.removeFront();
}
}
void
Debug::removeDebuggee(GlobalObject *global, GlobalObjectSet::Enum *e)
{
JS_ASSERT(global == debuggeeGlobal);
GlobalObject::DebugVector *v = global->getDebuggers();
for (Debug **p = v->begin(); p != v->end(); p++) {
if (*p == this) {
v->erase(p);
if (v->empty()) {
if (e)
e->removeFront();
else
global->compartment()->removeDebuggee(global);
}
debuggeeGlobal = NULL;
return;
}
}
JS_NOT_REACHED("Debug::removeDebugee");
}
void
Debug::finalize(JSContext *cx, JSObject *obj)
{
Debug *dbg = (Debug *) obj->getPrivate();
if (dbg && dbg->debuggeeCompartment)
dbg->detachFrom(dbg->debuggeeCompartment);
cx->delete_(dbg);
}
void
Debug::detachFrom(JSCompartment *c)
{
JS_ASSERT(c == debuggeeCompartment);
c->removeDebug(this);
debuggeeCompartment = NULL;
}
Class Debug::jsclass = {
"Debug", JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUG_COUNT),
PropertyStub, PropertyStub, PropertyStub, StrictPropertyStub,
@ -724,8 +809,8 @@ Debug::construct(JSContext *cx, uintN argc, Value *vp)
}
// Check that the target compartment is in debug mode.
JSCompartment *debuggeeCompartment = argobj->getProxyPrivate().toObject().compartment();
if (!debuggeeCompartment->debugMode) {
GlobalObject *debuggee = argobj->getProxyPrivate().toObject().getGlobal();
if (!debuggee->compartment()->debugMode) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NEED_DEBUG_MODE);
return false;
}
@ -750,15 +835,15 @@ Debug::construct(JSContext *cx, uintN argc, Value *vp)
if (!hooks)
return false;
Debug *dbg = cx->new_<Debug>(obj, hooks, debuggeeCompartment);
Debug *dbg = cx->new_<Debug>(obj, hooks);
if (!dbg)
return false;
obj->setPrivate(dbg);
if (!dbg->init() || !debuggeeCompartment->addDebug(dbg)) {
js_ReportOutOfMemory(cx);
if (!dbg->init(cx) || !debuggee->addDebug(cx, dbg)) {
cx->delete_(dbg);
return false;
}
dbg->debuggeeGlobal = debuggee;
vp->setObject(*obj);
return true;
}

View File

@ -48,6 +48,7 @@
#include "jshashtable.h"
#include "jswrapper.h"
#include "jsvalue.h"
#include "vm/GlobalObject.h"
namespace js {
@ -57,7 +58,7 @@ class Debug {
private:
JSCList link; // See JSRuntime::debuggerList.
JSObject *object; // The Debug object. Strong reference.
JSCompartment *debuggeeCompartment; // Weak reference.
GlobalObject *debuggeeGlobal; // The debuggee. Cross-compartment weak reference.
JSObject *hooksObject; // See Debug.prototype.hooks. Strong reference.
JSObject *uncaughtExceptionHook; // Strong reference.
bool enabled;
@ -67,10 +68,15 @@ class Debug {
bool hasDebuggerHandler; // hooks.debuggerHandler
bool hasThrowHandler; // hooks.throw
// Weak references to stack frames that are currently on the stack
// and thus necessarily alive. (Removed in slowPathLeaveStackFrame.)
typedef HashMap<StackFrame *, JSObject *, DefaultHasher<StackFrame *>, SystemAllocPolicy>
FrameMap;
FrameMap frames;
// 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;
@ -111,17 +117,18 @@ class Debug {
JSTrapStatus handleThrow(JSContext *cx, Value *vp);
public:
Debug(JSObject *dbg, JSObject *hooks, JSCompartment *compartment);
Debug(JSObject *dbg, JSObject *hooks);
~Debug();
bool init();
bool init(JSContext *cx);
inline JSObject *toJSObject() const;
static inline Debug *fromJSObject(JSObject *obj);
static Debug *fromChildJSObject(JSObject *obj);
void removeDebuggee(GlobalObject *global, GlobalObjectSet::Enum *e);
static void detachFromCompartment(JSCompartment *comp);
/*********************************** Methods for interaction with the GC. */
//
// A Debug object is live if:
// * the Debug JSObject is live (Debug::trace handles this case); OR
// * it is in the middle of dispatching an event (the event dispatching
@ -138,9 +145,7 @@ class Debug {
//
static bool mark(GCMarker *trc, JSCompartment *compartment, JSGCInvocationKind gckind);
static void sweepAll(JSRuntime *rt);
inline bool observesCompartment(JSCompartment *c) const;
void detachFrom(JSCompartment *c);
static void sweepCompartment(JSCompartment *compartment);
static inline void leaveStackFrame(JSContext *cx);
static inline JSTrapStatus onDebuggerStatement(JSContext *cx, js::Value *vp);
@ -188,6 +193,11 @@ class Debug {
// is false.)
//
bool newCompletionValue(AutoCompartment &ac, bool ok, Value val, Value *vp);
private:
// Prohibit copying.
Debug(const Debug &);
Debug & operator=(const Debug &);
};
bool
@ -199,7 +209,7 @@ Debug::hasAnyLiveHooks() const
bool
Debug::observesScope(JSObject *obj) const
{
return observesCompartment(obj->compartment());
return obj->getGlobal() == debuggeeGlobal;
}
bool
@ -208,13 +218,6 @@ Debug::observesFrame(StackFrame *fp) const
return observesScope(&fp->scopeChain());
}
bool
Debug::observesCompartment(JSCompartment *c) const
{
JS_ASSERT(c);
return debuggeeCompartment == c;
}
JSObject *
Debug::toJSObject() const
{
@ -232,14 +235,14 @@ Debug::fromJSObject(JSObject *obj)
void
Debug::leaveStackFrame(JSContext *cx)
{
if (!cx->compartment->getDebuggers().empty())
if (!cx->compartment->getDebuggees().empty())
slowPathLeaveStackFrame(cx);
}
JSTrapStatus
Debug::onDebuggerStatement(JSContext *cx, js::Value *vp)
{
return cx->compartment->getDebuggers().empty()
return cx->compartment->getDebuggees().empty()
? JSTRAP_CONTINUE
: dispatchHook(cx, vp,
DebugObservesMethod(&Debug::observesDebuggerStatement),
@ -249,7 +252,7 @@ Debug::onDebuggerStatement(JSContext *cx, js::Value *vp)
JSTrapStatus
Debug::onThrow(JSContext *cx, js::Value *vp)
{
return cx->compartment->getDebuggers().empty()
return cx->compartment->getDebuggees().empty()
? JSTRAP_CONTINUE
: dispatchHook(cx, vp,
DebugObservesMethod(&Debug::observesThrow),

View File

@ -199,12 +199,10 @@ JS_SetDebugModeForCompartment(JSContext *cx, JSCompartment *comp, JSBool debug)
comp->debugMode = !!debug;
// Detach any debuggers attached to this compartment.
if (debug) {
JS_ASSERT(comp->getDebuggers().empty());
} else {
while (!comp->getDebuggers().empty())
comp->getDebuggers().back()->detachFrom(comp);
}
if (debug)
JS_ASSERT(comp->getDebuggees().empty());
else
Debug::detachFromCompartment(comp);
// Discard JIT code for any scripts that change debugMode. This function
// assumes that 'comp' is in the same thread as 'cx'.

View File

@ -6643,7 +6643,7 @@ END_CASE(JSOP_ARRAYPUSH)
atoms = script->atomMap.vector;
/* Call debugger throw hook if set. */
if (cx->debugHooks->throwHook || !cx->compartment->getDebuggers().empty()) {
if (cx->debugHooks->throwHook || !cx->compartment->getDebuggees().empty()) {
Value rval;
JSTrapStatus st = Debug::onThrow(cx, &rval);
if (st == JSTRAP_CONTINUE) {

View File

@ -503,7 +503,7 @@ js_InternalThrow(VMFrame &f)
for (;;) {
// Call the throw hook if necessary
JSThrowHook handler = cx->debugHooks->throwHook;
if (handler || !cx->compartment->getDebuggers().empty()) {
if (handler || !cx->compartment->getDebuggees().empty()) {
Value rval;
JSTrapStatus st = Debug::onThrow(cx, &rval);
if (st == JSTRAP_CONTINUE && handler) {

View File

@ -1169,7 +1169,7 @@ void JS_FASTCALL
stubs::Debugger(VMFrame &f, jsbytecode *pc)
{
JSDebuggerHandler handler = f.cx->debugHooks->debuggerHandler;
if (handler || !f.cx->compartment->getDebuggers().empty()) {
if (handler || !f.cx->compartment->getDebuggees().empty()) {
JSTrapStatus st = JSTRAP_CONTINUE;
Value rval;
if (handler) {

View File

@ -201,4 +201,65 @@ GlobalObject::isEvalAllowed(JSContext *cx)
return !v.isFalse();
}
void
GlobalDebuggees_finalize(JSContext *cx, JSObject *obj)
{
cx->delete_((GlobalObject::DebugVector *) obj->getPrivate());
}
static Class
GlobalDebuggees_class = {
"GlobalDebuggee", JSCLASS_HAS_PRIVATE,
PropertyStub, PropertyStub, PropertyStub, StrictPropertyStub,
EnumerateStub, ResolveStub, ConvertStub, GlobalDebuggees_finalize
};
GlobalObject::DebugVector *
GlobalObject::getDebuggers()
{
Value debuggers = getReservedSlot(DEBUGGERS);
if (debuggers.isUndefined())
return NULL;
JS_ASSERT(debuggers.toObject().clasp == &GlobalDebuggees_class);
return (DebugVector *) debuggers.toObject().getPrivate();
}
GlobalObject::DebugVector *
GlobalObject::getOrCreateDebuggers(JSContext *cx)
{
DebugVector *vec = getDebuggers();
if (vec)
return vec;
JSObject *obj = NewNonFunction<WithProto::Given>(cx, &GlobalDebuggees_class, NULL, NULL);
if (!obj)
return NULL;
vec = cx->new_<DebugVector>();
if (!vec)
return NULL;
obj->setPrivate(vec);
if (!js_SetReservedSlot(cx, this, DEBUGGERS, ObjectValue(*obj)))
return NULL;
return vec;
}
bool
GlobalObject::addDebug(JSContext *cx, Debug *dbg)
{
DebugVector *vec = getOrCreateDebuggers(cx);
if (!vec)
return false;
#ifdef DEBUG
for (Debug **p = vec->begin(); p != vec->end(); p++)
JS_ASSERT(*p != dbg);
#endif
if (vec->empty() && !compartment()->addDebuggee(this))
return false;
if (!vec->append(dbg)) {
compartment()->removeDebuggee(this);
return false;
}
return true;
}
} // namespace js

View File

@ -42,6 +42,8 @@
#define GlobalObject_h___
#include "jsfun.h"
#include "jsprvtd.h"
#include "jsvector.h"
extern JSObject *
js_InitFunctionAndObjectClasses(JSContext *cx, JSObject *obj);
@ -89,9 +91,10 @@ class GlobalObject : public ::JSObject {
static const uintN EVAL_ALLOWED = FUNCTION_NS + 1;
static const uintN EVAL = EVAL_ALLOWED + 1;
static const uintN FLAGS = EVAL + 1;
static const uintN DEBUGGERS = FLAGS + 1;
/* Total reserved-slot count for global objects. */
static const uintN RESERVED_SLOTS = FLAGS + 1;
static const uintN RESERVED_SLOTS = DEBUGGERS + 1;
void staticAsserts() {
/*
@ -150,8 +153,22 @@ class GlobalObject : public ::JSObject {
bool getFunctionNamespace(JSContext *cx, Value *vp);
bool initStandardClasses(JSContext *cx);
typedef js::Vector<js::Debug *, 0, js::SystemAllocPolicy> DebugVector;
// The collection of Debug objects debugging this global. If this global is
// not a debuggee, this returns either NULL or an empty vector.
DebugVector *getDebuggers();
// The same, but create the empty vector if one does not already
// exist. Returns NULL only on OOM.
DebugVector *getOrCreateDebuggers(JSContext *cx);
bool addDebug(JSContext *cx, Debug *dbg);
};
typedef HashSet<GlobalObject *, DefaultHasher<GlobalObject *>, SystemAllocPolicy> GlobalObjectSet;
} // namespace js
js::GlobalObject *