mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Debuggees are globals, not compartments.
This commit is contained in:
parent
2e05c87e09
commit
5323e5a91c
12
js/src/jit-test/tests/debug/debuggees-01.js
Normal file
12
js/src/jit-test/tests/debug/debuggees-01.js
Normal 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);
|
@ -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))
|
||||
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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)
|
||||
|
167
js/src/jsdbg.cpp
167
js/src/jsdbg.cpp
@ -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;
|
||||
}
|
||||
|
@ -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),
|
||||
|
@ -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'.
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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 *
|
||||
|
Loading…
Reference in New Issue
Block a user