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
|
* with the following flags. Failure to use JSCLASS_GLOBAL_FLAGS was
|
||||||
* prevously allowed, but is now an ES5 violation and thus unsupported.
|
* 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 \
|
#define JSCLASS_GLOBAL_FLAGS \
|
||||||
(JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT))
|
(JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT))
|
||||||
|
|
||||||
|
@ -147,10 +147,11 @@ JSCompartment::init()
|
|||||||
#ifdef JS_METHODJIT
|
#ifdef JS_METHODJIT
|
||||||
if (!(jaegerCompartment = rt->new_<mjit::JaegerCompartment>()))
|
if (!(jaegerCompartment = rt->new_<mjit::JaegerCompartment>()))
|
||||||
return false;
|
return false;
|
||||||
return jaegerCompartment->Initialize();
|
if (!jaegerCompartment->Initialize())
|
||||||
#else
|
return false;
|
||||||
return true;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
return debuggees.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef JS_METHODJIT
|
#ifdef JS_METHODJIT
|
||||||
@ -602,15 +603,3 @@ JSCompartment::isAboutToBeCollected(JSGCInvocationKind gckind)
|
|||||||
{
|
{
|
||||||
return !hold && (arenaListsAreEmpty() || gckind == GC_LAST_CONTEXT);
|
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 "jsgcstats.h"
|
||||||
#include "jsclist.h"
|
#include "jsclist.h"
|
||||||
#include "jsxml.h"
|
#include "jsxml.h"
|
||||||
|
#include "vm/GlobalObject.h"
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
#pragma warning(push)
|
#pragma warning(push)
|
||||||
@ -381,8 +382,6 @@ class DtoaCache {
|
|||||||
} /* namespace js */
|
} /* namespace js */
|
||||||
|
|
||||||
struct JS_FRIEND_API(JSCompartment) {
|
struct JS_FRIEND_API(JSCompartment) {
|
||||||
typedef js::Vector<js::Debug *, 0, js::SystemAllocPolicy> DebugVector;
|
|
||||||
|
|
||||||
JSRuntime *rt;
|
JSRuntime *rt;
|
||||||
JSPrincipals *principals;
|
JSPrincipals *principals;
|
||||||
js::gc::Chunk *chunk;
|
js::gc::Chunk *chunk;
|
||||||
@ -518,7 +517,11 @@ struct JS_FRIEND_API(JSCompartment) {
|
|||||||
|
|
||||||
BackEdgeMap backEdgeTable;
|
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; }
|
JSCompartment *thisForCtor() { return this; }
|
||||||
public:
|
public:
|
||||||
@ -529,13 +532,14 @@ struct JS_FRIEND_API(JSCompartment) {
|
|||||||
size_t backEdgeCount(jsbytecode *pc) const;
|
size_t backEdgeCount(jsbytecode *pc) const;
|
||||||
size_t incBackEdgeCount(jsbytecode *pc);
|
size_t incBackEdgeCount(jsbytecode *pc);
|
||||||
|
|
||||||
const DebugVector &getDebuggers() const { return debuggers; }
|
js::GlobalObjectSet &getDebuggees() { return debuggees; }
|
||||||
|
bool addDebuggee(js::GlobalObject *global) {
|
||||||
bool addDebug(js::Debug *dbg) {
|
|
||||||
JS_ASSERT(debugMode);
|
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)
|
#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
|
JSSLOT_DEBUG_COUNT
|
||||||
};
|
};
|
||||||
|
|
||||||
Debug::Debug(JSObject *dbg, JSObject *hooks, JSCompartment *compartment)
|
Debug::Debug(JSObject *dbg, JSObject *hooks)
|
||||||
: object(dbg), debuggeeCompartment(compartment), hooksObject(hooks),
|
: object(dbg), debuggeeGlobal(NULL), hooksObject(hooks), uncaughtExceptionHook(NULL),
|
||||||
uncaughtExceptionHook(NULL), enabled(true), hasDebuggerHandler(false),
|
enabled(true), hasDebuggerHandler(false), hasThrowHandler(false)
|
||||||
hasThrowHandler(false)
|
|
||||||
{
|
{
|
||||||
// This always happens within a request on some cx.
|
// This always happens within a request on some cx.
|
||||||
AutoLockGC lock(compartment->rt);
|
JSRuntime *rt = dbg->compartment()->rt;
|
||||||
JS_APPEND_LINK(&link, &compartment->rt->debuggerList);
|
AutoLockGC lock(rt);
|
||||||
|
JS_APPEND_LINK(&link, &rt->debuggerList);
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug::~Debug()
|
Debug::~Debug()
|
||||||
{
|
{
|
||||||
// This always happens in the GC thread, so no locking is required.
|
|
||||||
JS_ASSERT(object->compartment()->rt->gcRunning);
|
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);
|
JS_REMOVE_LINK(&link);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
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));
|
JS_STATIC_ASSERT(uintN(JSSLOT_DEBUGFRAME_OWNER) == uintN(JSSLOT_DEBUGOBJECT_OWNER));
|
||||||
@ -216,9 +226,12 @@ void
|
|||||||
Debug::slowPathLeaveStackFrame(JSContext *cx)
|
Debug::slowPathLeaveStackFrame(JSContext *cx)
|
||||||
{
|
{
|
||||||
StackFrame *fp = cx->fp();
|
StackFrame *fp = cx->fp();
|
||||||
JSCompartment *compartment = cx->compartment;
|
GlobalObject *global = fp->scopeChain().getGlobal();
|
||||||
const JSCompartment::DebugVector &debuggers = compartment->getDebuggers();
|
|
||||||
for (Debug **p = debuggers.begin(); p != debuggers.end(); p++) {
|
// 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;
|
Debug *dbg = *p;
|
||||||
if (FrameMap::Ptr p = dbg->frames.lookup(fp)) {
|
if (FrameMap::Ptr p = dbg->frames.lookup(fp)) {
|
||||||
JSObject *frameobj = p->value;
|
JSObject *frameobj = p->value;
|
||||||
@ -226,6 +239,7 @@ Debug::slowPathLeaveStackFrame(JSContext *cx)
|
|||||||
dbg->frames.remove(p);
|
dbg->frames.remove(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
@ -469,21 +483,22 @@ Debug::dispatchHook(JSContext *cx, js::Value *vp, DebugObservesMethod observesEv
|
|||||||
// Note: In the general case, 'triggered' contains references to objects in
|
// Note: In the general case, 'triggered' contains references to objects in
|
||||||
// different compartments--every compartment *except* this one.
|
// different compartments--every compartment *except* this one.
|
||||||
AutoValueVector triggered(cx);
|
AutoValueVector triggered(cx);
|
||||||
JSCompartment *compartment = cx->compartment;
|
GlobalObject *global = cx->fp()->scopeChain().getGlobal();
|
||||||
const JSCompartment::DebugVector &debuggers = compartment->getDebuggers();
|
if (GlobalObject::DebugVector *debuggers = global->getDebuggers()) {
|
||||||
for (Debug **p = debuggers.begin(); p != debuggers.end(); p++) {
|
for (Debug **p = debuggers->begin(); p != debuggers->end(); p++) {
|
||||||
Debug *dbg = *p;
|
Debug *dbg = *p;
|
||||||
if ((dbg->*observesEvent)()) {
|
if ((dbg->*observesEvent)()) {
|
||||||
if (!triggered.append(ObjectValue(*dbg->toJSObject())))
|
if (!triggered.append(ObjectValue(*dbg->toJSObject())))
|
||||||
return JSTRAP_ERROR;
|
return JSTRAP_ERROR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Deliver the event to each debugger, checking again to make sure it
|
// Deliver the event to each debugger, checking again to make sure it
|
||||||
// should still be delivered.
|
// should still be delivered.
|
||||||
for (Value *p = triggered.begin(); p != triggered.end(); p++) {
|
for (Value *p = triggered.begin(); p != triggered.end(); p++) {
|
||||||
Debug *dbg = Debug::fromJSObject(&p->toObject());
|
Debug *dbg = Debug::fromJSObject(&p->toObject());
|
||||||
if (dbg->observesCompartment(compartment) && (dbg->*observesEvent)()) {
|
if (dbg->debuggeeGlobal == global && (dbg->*observesEvent)()) {
|
||||||
JSTrapStatus st = (dbg->*handleEvent)(cx, vp);
|
JSTrapStatus st = (dbg->*handleEvent)(cx, vp);
|
||||||
if (st != JSTRAP_CONTINUE)
|
if (st != JSTRAP_CONTINUE)
|
||||||
return st;
|
return st;
|
||||||
@ -497,9 +512,13 @@ Debug::dispatchHook(JSContext *cx, js::Value *vp, DebugObservesMethod observesEv
|
|||||||
bool
|
bool
|
||||||
Debug::mark(GCMarker *trc, JSCompartment *comp, JSGCInvocationKind gckind)
|
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
|
// Search for Debug objects in the given compartment. We do this by
|
||||||
// searching all the compartments being debugged.
|
// searching all the compartments being debugged.
|
||||||
bool markedAny = false;
|
|
||||||
JSRuntime *rt = trc->context->runtime;
|
JSRuntime *rt = trc->context->runtime;
|
||||||
for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); c++) {
|
for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); c++) {
|
||||||
JSCompartment *dc = *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
|
// debug itself). If comp is null, this is a global GC and we
|
||||||
// search every dc that is live.
|
// search every dc that is live.
|
||||||
if (comp ? dc != comp : !dc->isAboutToBeCollected(gckind)) {
|
if (comp ? dc != comp : !dc->isAboutToBeCollected(gckind)) {
|
||||||
const JSCompartment::DebugVector &debuggers = dc->getDebuggers();
|
const GlobalObjectSet &debuggees = dc->getDebuggees();
|
||||||
for (Debug **p = debuggers.begin(); p != debuggers.end(); p++) {
|
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;
|
Debug *dbg = *p;
|
||||||
JSObject *obj = dbg->toJSObject();
|
JSObject *obj = dbg->toJSObject();
|
||||||
|
|
||||||
@ -528,15 +553,15 @@ Debug::mark(GCMarker *trc, JSCompartment *comp, JSGCInvocationKind gckind)
|
|||||||
// Handling Debug.Objects:
|
// Handling Debug.Objects:
|
||||||
//
|
//
|
||||||
// If comp is the debuggee's compartment, do nothing. No
|
// If comp is the debuggee's compartment, do nothing. No
|
||||||
// referent objects will be collected, since we have a wrapper
|
// referent objects will be collected, since we have a
|
||||||
// of each one.
|
// wrapper of each one.
|
||||||
//
|
//
|
||||||
// If comp is the debugger's compartment, mark all
|
// If comp is the debugger's compartment, mark all
|
||||||
// Debug.Objects, since the referents might be alive and
|
// Debug.Objects, since the referents might be alive and
|
||||||
// therefore the table entries must remain.
|
// therefore the table entries must remain.
|
||||||
//
|
//
|
||||||
// If comp is null, then for each key (referent-wrapper) that
|
// If comp is null, then for each key (referent-wrapper)
|
||||||
// is marked, mark the corresponding value.
|
// that is marked, mark the corresponding value.
|
||||||
//
|
//
|
||||||
if (!comp || obj->compartment() == comp) {
|
if (!comp || obj->compartment() == comp) {
|
||||||
for (ObjectMap::Range r = dbg->objects.all(); !r.empty(); r.popFront()) {
|
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() &&
|
if (!r.front().value->isMarked() &&
|
||||||
(comp || r.front().key->unwrap()->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;
|
markedAny = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -554,6 +580,7 @@ Debug::mark(GCMarker *trc, JSCompartment *comp, JSGCInvocationKind gckind)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return markedAny;
|
return markedAny;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -578,34 +605,92 @@ Debug::trace(JSTracer *trc, JSObject *obj)
|
|||||||
void
|
void
|
||||||
Debug::sweepAll(JSRuntime *rt)
|
Debug::sweepAll(JSRuntime *rt)
|
||||||
{
|
{
|
||||||
// Sweep ObjectMap entries for objects being collected.
|
|
||||||
for (JSCList *p = &rt->debuggerList; (p = JS_NEXT_LINK(p)) != &rt->debuggerList;) {
|
for (JSCList *p = &rt->debuggerList; (p = JS_NEXT_LINK(p)) != &rt->debuggerList;) {
|
||||||
Debug *dbg = (Debug *) ((unsigned char *) p - offsetof(Debug, link));
|
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()) {
|
for (ObjectMap::Enum e(dbg->objects); !e.empty(); e.popFront()) {
|
||||||
JS_ASSERT(e.front().key->isMarked() == e.front().value->isMarked());
|
JS_ASSERT(e.front().key->isMarked() == e.front().value->isMarked());
|
||||||
if (!e.front().value->isMarked())
|
if (!e.front().value->isMarked())
|
||||||
e.removeFront();
|
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
|
void
|
||||||
Debug::finalize(JSContext *cx, JSObject *obj)
|
Debug::finalize(JSContext *cx, JSObject *obj)
|
||||||
{
|
{
|
||||||
Debug *dbg = (Debug *) obj->getPrivate();
|
Debug *dbg = (Debug *) obj->getPrivate();
|
||||||
if (dbg && dbg->debuggeeCompartment)
|
|
||||||
dbg->detachFrom(dbg->debuggeeCompartment);
|
|
||||||
cx->delete_(dbg);
|
cx->delete_(dbg);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
Debug::detachFrom(JSCompartment *c)
|
|
||||||
{
|
|
||||||
JS_ASSERT(c == debuggeeCompartment);
|
|
||||||
c->removeDebug(this);
|
|
||||||
debuggeeCompartment = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
Class Debug::jsclass = {
|
Class Debug::jsclass = {
|
||||||
"Debug", JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUG_COUNT),
|
"Debug", JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUG_COUNT),
|
||||||
PropertyStub, PropertyStub, PropertyStub, StrictPropertyStub,
|
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.
|
// Check that the target compartment is in debug mode.
|
||||||
JSCompartment *debuggeeCompartment = argobj->getProxyPrivate().toObject().compartment();
|
GlobalObject *debuggee = argobj->getProxyPrivate().toObject().getGlobal();
|
||||||
if (!debuggeeCompartment->debugMode) {
|
if (!debuggee->compartment()->debugMode) {
|
||||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NEED_DEBUG_MODE);
|
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NEED_DEBUG_MODE);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -750,15 +835,15 @@ Debug::construct(JSContext *cx, uintN argc, Value *vp)
|
|||||||
if (!hooks)
|
if (!hooks)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Debug *dbg = cx->new_<Debug>(obj, hooks, debuggeeCompartment);
|
Debug *dbg = cx->new_<Debug>(obj, hooks);
|
||||||
if (!dbg)
|
if (!dbg)
|
||||||
return false;
|
return false;
|
||||||
obj->setPrivate(dbg);
|
obj->setPrivate(dbg);
|
||||||
if (!dbg->init() || !debuggeeCompartment->addDebug(dbg)) {
|
if (!dbg->init(cx) || !debuggee->addDebug(cx, dbg)) {
|
||||||
js_ReportOutOfMemory(cx);
|
cx->delete_(dbg);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
dbg->debuggeeGlobal = debuggee;
|
||||||
vp->setObject(*obj);
|
vp->setObject(*obj);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -48,6 +48,7 @@
|
|||||||
#include "jshashtable.h"
|
#include "jshashtable.h"
|
||||||
#include "jswrapper.h"
|
#include "jswrapper.h"
|
||||||
#include "jsvalue.h"
|
#include "jsvalue.h"
|
||||||
|
#include "vm/GlobalObject.h"
|
||||||
|
|
||||||
namespace js {
|
namespace js {
|
||||||
|
|
||||||
@ -57,7 +58,7 @@ class Debug {
|
|||||||
private:
|
private:
|
||||||
JSCList link; // See JSRuntime::debuggerList.
|
JSCList link; // See JSRuntime::debuggerList.
|
||||||
JSObject *object; // The Debug object. Strong reference.
|
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 *hooksObject; // See Debug.prototype.hooks. Strong reference.
|
||||||
JSObject *uncaughtExceptionHook; // Strong reference.
|
JSObject *uncaughtExceptionHook; // Strong reference.
|
||||||
bool enabled;
|
bool enabled;
|
||||||
@ -67,10 +68,15 @@ class Debug {
|
|||||||
bool hasDebuggerHandler; // hooks.debuggerHandler
|
bool hasDebuggerHandler; // hooks.debuggerHandler
|
||||||
bool hasThrowHandler; // hooks.throw
|
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>
|
typedef HashMap<StackFrame *, JSObject *, DefaultHasher<StackFrame *>, SystemAllocPolicy>
|
||||||
FrameMap;
|
FrameMap;
|
||||||
FrameMap frames;
|
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>
|
typedef HashMap<JSObject *, JSObject *, DefaultHasher<JSObject *>, SystemAllocPolicy>
|
||||||
ObjectMap;
|
ObjectMap;
|
||||||
ObjectMap objects;
|
ObjectMap objects;
|
||||||
@ -111,17 +117,18 @@ class Debug {
|
|||||||
JSTrapStatus handleThrow(JSContext *cx, Value *vp);
|
JSTrapStatus handleThrow(JSContext *cx, Value *vp);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Debug(JSObject *dbg, JSObject *hooks, JSCompartment *compartment);
|
Debug(JSObject *dbg, JSObject *hooks);
|
||||||
~Debug();
|
~Debug();
|
||||||
|
|
||||||
bool init();
|
bool init(JSContext *cx);
|
||||||
inline JSObject *toJSObject() const;
|
inline JSObject *toJSObject() const;
|
||||||
static inline Debug *fromJSObject(JSObject *obj);
|
static inline Debug *fromJSObject(JSObject *obj);
|
||||||
static Debug *fromChildJSObject(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. */
|
/*********************************** Methods for interaction with the GC. */
|
||||||
|
|
||||||
//
|
|
||||||
// A Debug object is live if:
|
// A Debug object is live if:
|
||||||
// * the Debug JSObject is live (Debug::trace handles this case); OR
|
// * the Debug JSObject is live (Debug::trace handles this case); OR
|
||||||
// * it is in the middle of dispatching an event (the event dispatching
|
// * 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 bool mark(GCMarker *trc, JSCompartment *compartment, JSGCInvocationKind gckind);
|
||||||
static void sweepAll(JSRuntime *rt);
|
static void sweepAll(JSRuntime *rt);
|
||||||
|
static void sweepCompartment(JSCompartment *compartment);
|
||||||
inline bool observesCompartment(JSCompartment *c) const;
|
|
||||||
void detachFrom(JSCompartment *c);
|
|
||||||
|
|
||||||
static inline void leaveStackFrame(JSContext *cx);
|
static inline void leaveStackFrame(JSContext *cx);
|
||||||
static inline JSTrapStatus onDebuggerStatement(JSContext *cx, js::Value *vp);
|
static inline JSTrapStatus onDebuggerStatement(JSContext *cx, js::Value *vp);
|
||||||
@ -188,6 +193,11 @@ class Debug {
|
|||||||
// is false.)
|
// is false.)
|
||||||
//
|
//
|
||||||
bool newCompletionValue(AutoCompartment &ac, bool ok, Value val, Value *vp);
|
bool newCompletionValue(AutoCompartment &ac, bool ok, Value val, Value *vp);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Prohibit copying.
|
||||||
|
Debug(const Debug &);
|
||||||
|
Debug & operator=(const Debug &);
|
||||||
};
|
};
|
||||||
|
|
||||||
bool
|
bool
|
||||||
@ -199,7 +209,7 @@ Debug::hasAnyLiveHooks() const
|
|||||||
bool
|
bool
|
||||||
Debug::observesScope(JSObject *obj) const
|
Debug::observesScope(JSObject *obj) const
|
||||||
{
|
{
|
||||||
return observesCompartment(obj->compartment());
|
return obj->getGlobal() == debuggeeGlobal;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
@ -208,13 +218,6 @@ Debug::observesFrame(StackFrame *fp) const
|
|||||||
return observesScope(&fp->scopeChain());
|
return observesScope(&fp->scopeChain());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
|
||||||
Debug::observesCompartment(JSCompartment *c) const
|
|
||||||
{
|
|
||||||
JS_ASSERT(c);
|
|
||||||
return debuggeeCompartment == c;
|
|
||||||
}
|
|
||||||
|
|
||||||
JSObject *
|
JSObject *
|
||||||
Debug::toJSObject() const
|
Debug::toJSObject() const
|
||||||
{
|
{
|
||||||
@ -232,14 +235,14 @@ Debug::fromJSObject(JSObject *obj)
|
|||||||
void
|
void
|
||||||
Debug::leaveStackFrame(JSContext *cx)
|
Debug::leaveStackFrame(JSContext *cx)
|
||||||
{
|
{
|
||||||
if (!cx->compartment->getDebuggers().empty())
|
if (!cx->compartment->getDebuggees().empty())
|
||||||
slowPathLeaveStackFrame(cx);
|
slowPathLeaveStackFrame(cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
JSTrapStatus
|
JSTrapStatus
|
||||||
Debug::onDebuggerStatement(JSContext *cx, js::Value *vp)
|
Debug::onDebuggerStatement(JSContext *cx, js::Value *vp)
|
||||||
{
|
{
|
||||||
return cx->compartment->getDebuggers().empty()
|
return cx->compartment->getDebuggees().empty()
|
||||||
? JSTRAP_CONTINUE
|
? JSTRAP_CONTINUE
|
||||||
: dispatchHook(cx, vp,
|
: dispatchHook(cx, vp,
|
||||||
DebugObservesMethod(&Debug::observesDebuggerStatement),
|
DebugObservesMethod(&Debug::observesDebuggerStatement),
|
||||||
@ -249,7 +252,7 @@ Debug::onDebuggerStatement(JSContext *cx, js::Value *vp)
|
|||||||
JSTrapStatus
|
JSTrapStatus
|
||||||
Debug::onThrow(JSContext *cx, js::Value *vp)
|
Debug::onThrow(JSContext *cx, js::Value *vp)
|
||||||
{
|
{
|
||||||
return cx->compartment->getDebuggers().empty()
|
return cx->compartment->getDebuggees().empty()
|
||||||
? JSTRAP_CONTINUE
|
? JSTRAP_CONTINUE
|
||||||
: dispatchHook(cx, vp,
|
: dispatchHook(cx, vp,
|
||||||
DebugObservesMethod(&Debug::observesThrow),
|
DebugObservesMethod(&Debug::observesThrow),
|
||||||
|
@ -199,12 +199,10 @@ JS_SetDebugModeForCompartment(JSContext *cx, JSCompartment *comp, JSBool debug)
|
|||||||
comp->debugMode = !!debug;
|
comp->debugMode = !!debug;
|
||||||
|
|
||||||
// Detach any debuggers attached to this compartment.
|
// Detach any debuggers attached to this compartment.
|
||||||
if (debug) {
|
if (debug)
|
||||||
JS_ASSERT(comp->getDebuggers().empty());
|
JS_ASSERT(comp->getDebuggees().empty());
|
||||||
} else {
|
else
|
||||||
while (!comp->getDebuggers().empty())
|
Debug::detachFromCompartment(comp);
|
||||||
comp->getDebuggers().back()->detachFrom(comp);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Discard JIT code for any scripts that change debugMode. This function
|
// Discard JIT code for any scripts that change debugMode. This function
|
||||||
// assumes that 'comp' is in the same thread as 'cx'.
|
// assumes that 'comp' is in the same thread as 'cx'.
|
||||||
|
@ -6643,7 +6643,7 @@ END_CASE(JSOP_ARRAYPUSH)
|
|||||||
atoms = script->atomMap.vector;
|
atoms = script->atomMap.vector;
|
||||||
|
|
||||||
/* Call debugger throw hook if set. */
|
/* Call debugger throw hook if set. */
|
||||||
if (cx->debugHooks->throwHook || !cx->compartment->getDebuggers().empty()) {
|
if (cx->debugHooks->throwHook || !cx->compartment->getDebuggees().empty()) {
|
||||||
Value rval;
|
Value rval;
|
||||||
JSTrapStatus st = Debug::onThrow(cx, &rval);
|
JSTrapStatus st = Debug::onThrow(cx, &rval);
|
||||||
if (st == JSTRAP_CONTINUE) {
|
if (st == JSTRAP_CONTINUE) {
|
||||||
|
@ -503,7 +503,7 @@ js_InternalThrow(VMFrame &f)
|
|||||||
for (;;) {
|
for (;;) {
|
||||||
// Call the throw hook if necessary
|
// Call the throw hook if necessary
|
||||||
JSThrowHook handler = cx->debugHooks->throwHook;
|
JSThrowHook handler = cx->debugHooks->throwHook;
|
||||||
if (handler || !cx->compartment->getDebuggers().empty()) {
|
if (handler || !cx->compartment->getDebuggees().empty()) {
|
||||||
Value rval;
|
Value rval;
|
||||||
JSTrapStatus st = Debug::onThrow(cx, &rval);
|
JSTrapStatus st = Debug::onThrow(cx, &rval);
|
||||||
if (st == JSTRAP_CONTINUE && handler) {
|
if (st == JSTRAP_CONTINUE && handler) {
|
||||||
|
@ -1169,7 +1169,7 @@ void JS_FASTCALL
|
|||||||
stubs::Debugger(VMFrame &f, jsbytecode *pc)
|
stubs::Debugger(VMFrame &f, jsbytecode *pc)
|
||||||
{
|
{
|
||||||
JSDebuggerHandler handler = f.cx->debugHooks->debuggerHandler;
|
JSDebuggerHandler handler = f.cx->debugHooks->debuggerHandler;
|
||||||
if (handler || !f.cx->compartment->getDebuggers().empty()) {
|
if (handler || !f.cx->compartment->getDebuggees().empty()) {
|
||||||
JSTrapStatus st = JSTRAP_CONTINUE;
|
JSTrapStatus st = JSTRAP_CONTINUE;
|
||||||
Value rval;
|
Value rval;
|
||||||
if (handler) {
|
if (handler) {
|
||||||
|
@ -201,4 +201,65 @@ GlobalObject::isEvalAllowed(JSContext *cx)
|
|||||||
return !v.isFalse();
|
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
|
} // namespace js
|
||||||
|
@ -42,6 +42,8 @@
|
|||||||
#define GlobalObject_h___
|
#define GlobalObject_h___
|
||||||
|
|
||||||
#include "jsfun.h"
|
#include "jsfun.h"
|
||||||
|
#include "jsprvtd.h"
|
||||||
|
#include "jsvector.h"
|
||||||
|
|
||||||
extern JSObject *
|
extern JSObject *
|
||||||
js_InitFunctionAndObjectClasses(JSContext *cx, JSObject *obj);
|
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_ALLOWED = FUNCTION_NS + 1;
|
||||||
static const uintN EVAL = EVAL_ALLOWED + 1;
|
static const uintN EVAL = EVAL_ALLOWED + 1;
|
||||||
static const uintN FLAGS = EVAL + 1;
|
static const uintN FLAGS = EVAL + 1;
|
||||||
|
static const uintN DEBUGGERS = FLAGS + 1;
|
||||||
|
|
||||||
/* Total reserved-slot count for global objects. */
|
/* Total reserved-slot count for global objects. */
|
||||||
static const uintN RESERVED_SLOTS = FLAGS + 1;
|
static const uintN RESERVED_SLOTS = DEBUGGERS + 1;
|
||||||
|
|
||||||
void staticAsserts() {
|
void staticAsserts() {
|
||||||
/*
|
/*
|
||||||
@ -150,8 +153,22 @@ class GlobalObject : public ::JSObject {
|
|||||||
bool getFunctionNamespace(JSContext *cx, Value *vp);
|
bool getFunctionNamespace(JSContext *cx, Value *vp);
|
||||||
|
|
||||||
bool initStandardClasses(JSContext *cx);
|
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
|
} // namespace js
|
||||||
|
|
||||||
js::GlobalObject *
|
js::GlobalObject *
|
||||||
|
Loading…
Reference in New Issue
Block a user