mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Very rudimentary support for creating Debug.Frame objects, passing them to hooks, and cleaning them up afterwards.
This commit is contained in:
parent
4fcb55d0aa
commit
27da170dc0
@ -8,7 +8,7 @@ var log;
|
|||||||
function makeDebug(g, name) {
|
function makeDebug(g, name) {
|
||||||
var dbg = new Debug(g);
|
var dbg = new Debug(g);
|
||||||
dbg.hooks = {
|
dbg.hooks = {
|
||||||
debuggerHandler: function () {
|
debuggerHandler: function (frame) {
|
||||||
log += name;
|
log += name;
|
||||||
throw new Error(name);
|
throw new Error(name);
|
||||||
}
|
}
|
||||||
|
51
js/src/jit-test/tests/debug/debug-object-22.js
Normal file
51
js/src/jit-test/tests/debug/debug-object-22.js
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
// |jit-test| debug
|
||||||
|
// Test .type and .generator fields of topmost stack frame passed to debuggerHandler.
|
||||||
|
|
||||||
|
var g = newGlobal('new-compartment');
|
||||||
|
g.debuggeeGlobal = this;
|
||||||
|
g.eval("var hits;");
|
||||||
|
g.eval("(" + function () {
|
||||||
|
var dbg = Debug(debuggeeGlobal);
|
||||||
|
dbg.hooks = {
|
||||||
|
debuggerHandler: function (f) {
|
||||||
|
assertEq(Object.getPrototypeOf(f), Debug.Frame.prototype);
|
||||||
|
assertEq(f.type, ftype);
|
||||||
|
assertEq(f.generator, fgen);
|
||||||
|
hits++;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} + ")()");
|
||||||
|
|
||||||
|
g.ftype = "global";
|
||||||
|
g.fgen = false;
|
||||||
|
g.hits = 0;
|
||||||
|
debugger;
|
||||||
|
assertEq(g.hits, 1);
|
||||||
|
|
||||||
|
g.ftype = "call";
|
||||||
|
g.hits = 0;
|
||||||
|
(function () { debugger; })();
|
||||||
|
assertEq(g.hits, 1);
|
||||||
|
|
||||||
|
g.ftype = "eval";
|
||||||
|
g.hits = 0;
|
||||||
|
eval("debugger;");
|
||||||
|
assertEq(g.hits, 1);
|
||||||
|
|
||||||
|
g.ftype = "eval";
|
||||||
|
g.hits = 0;
|
||||||
|
this.eval("debugger;"); // indirect eval
|
||||||
|
assertEq(g.hits, 1);
|
||||||
|
|
||||||
|
g.ftype = "eval";
|
||||||
|
g.hits = 0;
|
||||||
|
(function () { eval("debugger;"); })();
|
||||||
|
assertEq(g.hits, 1);
|
||||||
|
|
||||||
|
g.ftype = "call";
|
||||||
|
g.fgen = true;
|
||||||
|
g.hits = 0;
|
||||||
|
function gen() { debugger; yield 1; debugger; }
|
||||||
|
for (var x in gen()) {
|
||||||
|
}
|
||||||
|
assertEq(g.hits, 2);
|
35
js/src/jit-test/tests/debug/debug-object-23.js
Normal file
35
js/src/jit-test/tests/debug/debug-object-23.js
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// |jit-test| debug
|
||||||
|
// When the debugger is triggered twice from the same stack frame, the same
|
||||||
|
// Debug.Frame object must be passed to the hook both times.
|
||||||
|
|
||||||
|
var g = newGlobal('new-compartment');
|
||||||
|
g.debuggeeGlobal = this;
|
||||||
|
g.eval("var hits, frame;");
|
||||||
|
g.eval("(" + function () {
|
||||||
|
var dbg = Debug(debuggeeGlobal);
|
||||||
|
dbg.hooks = {
|
||||||
|
debuggerHandler: function (f) {
|
||||||
|
if (hits++ == 0)
|
||||||
|
frame = f;
|
||||||
|
else
|
||||||
|
assertEq(f, frame);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} + ")()");
|
||||||
|
|
||||||
|
g.hits = 0;
|
||||||
|
debugger;
|
||||||
|
debugger;
|
||||||
|
assertEq(g.hits, 2);
|
||||||
|
|
||||||
|
g.hits = 0;
|
||||||
|
function f() {
|
||||||
|
debugger;
|
||||||
|
debugger;
|
||||||
|
}
|
||||||
|
f();
|
||||||
|
assertEq(g.hits, 2);
|
||||||
|
|
||||||
|
g.hits = 0;
|
||||||
|
eval("debugger; debugger;");
|
||||||
|
assertEq(g.hits, 2);
|
26
js/src/jit-test/tests/debug/debug-object-24.js
Normal file
26
js/src/jit-test/tests/debug/debug-object-24.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// |jit-test| debug
|
||||||
|
// When the debugger is triggered from different stack frames that happen to
|
||||||
|
// occupy the same memory, it must deliver different Debug.Frame objects.
|
||||||
|
|
||||||
|
var g = newGlobal('new-compartment');
|
||||||
|
g.debuggeeGlobal = this;
|
||||||
|
g.eval("var hits;");
|
||||||
|
g.eval("(" + function () {
|
||||||
|
var a = [];
|
||||||
|
var dbg = Debug(debuggeeGlobal);
|
||||||
|
dbg.hooks = {
|
||||||
|
debuggerHandler: function (frame) {
|
||||||
|
for (var i = 0; i < a.length; i++)
|
||||||
|
assertEq(a[i] === frame, false);
|
||||||
|
a.push(frame);
|
||||||
|
hits++;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} + ")()");
|
||||||
|
|
||||||
|
function f() { debugger; }
|
||||||
|
function h() { debugger; f(); }
|
||||||
|
g.hits = 0;
|
||||||
|
for (var i = 0; i < 4; i++)
|
||||||
|
h();
|
||||||
|
assertEq(g.hits, 8);
|
179
js/src/jsdbg.cpp
179
js/src/jsdbg.cpp
@ -49,6 +49,17 @@
|
|||||||
|
|
||||||
using namespace js;
|
using namespace js;
|
||||||
|
|
||||||
|
// === Forward declarations
|
||||||
|
|
||||||
|
extern Class Frame_class;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
JSSLOT_FRAME_OWNER,
|
||||||
|
JSSLOT_FRAME_COUNT
|
||||||
|
};
|
||||||
|
|
||||||
|
// === Utils
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
NotImplemented(JSContext *cx)
|
NotImplemented(JSContext *cx)
|
||||||
{
|
{
|
||||||
@ -114,12 +125,59 @@ CheckThisClass(JSContext *cx, Value *vp, Class *clasp, const char *fnname)
|
|||||||
|
|
||||||
// === Debug hook dispatch
|
// === Debug hook dispatch
|
||||||
|
|
||||||
|
enum {
|
||||||
|
JSSLOT_DEBUG_FRAME_PROTO,
|
||||||
|
JSSLOT_DEBUG_COUNT
|
||||||
|
};
|
||||||
|
|
||||||
Debug::Debug(JSObject *dbg, JSObject *hooks, JSCompartment *compartment)
|
Debug::Debug(JSObject *dbg, JSObject *hooks, JSCompartment *compartment)
|
||||||
: object(dbg), debuggeeCompartment(compartment), hooksObject(hooks),
|
: object(dbg), debuggeeCompartment(compartment), hooksObject(hooks),
|
||||||
uncaughtExceptionHook(NULL), enabled(true), hasDebuggerHandler(false)
|
uncaughtExceptionHook(NULL), enabled(true), hasDebuggerHandler(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
Debug::init()
|
||||||
|
{
|
||||||
|
return frames.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
Debug::getScriptFrame(JSContext *cx, JSStackFrame *fp, Value *vp)
|
||||||
|
{
|
||||||
|
FrameMap::AddPtr p = frames.lookupForAdd(fp);
|
||||||
|
if (!p) {
|
||||||
|
JSObject *proto = &object->getReservedSlot(JSSLOT_DEBUG_FRAME_PROTO).toObject();
|
||||||
|
JSObject *frameobj = NewNonFunction<WithProto::Given>(cx, &Frame_class, proto, NULL);
|
||||||
|
if (!frameobj || !frameobj->ensureClassReservedSlots(cx))
|
||||||
|
return false;
|
||||||
|
frameobj->setPrivate(fp);
|
||||||
|
frameobj->setReservedSlot(JSSLOT_FRAME_OWNER, ObjectValue(*object));
|
||||||
|
if (!frames.add(p, fp, frameobj)) {
|
||||||
|
js_ReportOutOfMemory(cx);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vp->setObject(*p->value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Debug::slowPathLeaveStackFrame(JSContext *cx)
|
||||||
|
{
|
||||||
|
JSStackFrame *fp = cx->regs->fp;
|
||||||
|
JSCompartment *compartment = cx->compartment;
|
||||||
|
const JSCompartment::DebugVector &debuggers = compartment->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;
|
||||||
|
frameobj->setPrivate(NULL);
|
||||||
|
dbg->frames.remove(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
JSTrapStatus
|
JSTrapStatus
|
||||||
Debug::handleUncaughtException(AutoCompartment &ac, Value *vp, bool callHook)
|
Debug::handleUncaughtException(AutoCompartment &ac, Value *vp, bool callHook)
|
||||||
{
|
{
|
||||||
@ -133,7 +191,7 @@ Debug::handleUncaughtException(AutoCompartment &ac, Value *vp, bool callHook)
|
|||||||
if (ExternalInvoke(cx, ObjectValue(*object), fval, 1, &exc, &rv))
|
if (ExternalInvoke(cx, ObjectValue(*object), fval, 1, &exc, &rv))
|
||||||
return parseResumptionValue(ac, true, rv, vp, false);
|
return parseResumptionValue(ac, true, rv, vp, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cx->isExceptionPending()) {
|
if (cx->isExceptionPending()) {
|
||||||
JS_ReportPendingException(cx);
|
JS_ReportPendingException(cx);
|
||||||
cx->clearPendingException();
|
cx->clearPendingException();
|
||||||
@ -209,14 +267,20 @@ CallMethodIfPresent(JSContext *cx, JSObject *obj, const char *name, int argc, Va
|
|||||||
JSTrapStatus
|
JSTrapStatus
|
||||||
Debug::handleDebuggerStatement(JSContext *cx, Value *vp)
|
Debug::handleDebuggerStatement(JSContext *cx, Value *vp)
|
||||||
{
|
{
|
||||||
|
// Grab cx->regs->fp before pushing a dummy frame.
|
||||||
|
JSStackFrame *fp = cx->regs->fp;
|
||||||
|
|
||||||
JS_ASSERT(hasDebuggerHandler);
|
JS_ASSERT(hasDebuggerHandler);
|
||||||
AutoCompartment ac(cx, hooksObject);
|
AutoCompartment ac(cx, hooksObject);
|
||||||
if (!ac.enter())
|
if (!ac.enter())
|
||||||
return JSTRAP_ERROR;
|
return JSTRAP_ERROR;
|
||||||
|
|
||||||
// XXX debuggerHandler should receive a Frame.
|
Value argv[1];
|
||||||
|
if (!getScriptFrame(cx, fp, argv))
|
||||||
|
return JSTRAP_ERROR;
|
||||||
|
|
||||||
Value rv;
|
Value rv;
|
||||||
bool ok = CallMethodIfPresent(cx, hooksObject, "debuggerHandler", 0, NULL, &rv);
|
bool ok = CallMethodIfPresent(cx, hooksObject, "debuggerHandler", 1, argv, &rv);
|
||||||
return parseResumptionValue(ac, ok, rv, vp);
|
return parseResumptionValue(ac, ok, rv, vp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -252,7 +316,6 @@ Debug::dispatchDebuggerStatement(JSContext *cx, js::Value *vp)
|
|||||||
return JSTRAP_CONTINUE;
|
return JSTRAP_CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// === Debug JSObjects
|
// === Debug JSObjects
|
||||||
|
|
||||||
bool
|
bool
|
||||||
@ -299,6 +362,34 @@ Debug::trace(JSTracer *trc, JSObject *obj)
|
|||||||
MarkObject(trc, *dbg->hooksObject, "hooks");
|
MarkObject(trc, *dbg->hooksObject, "hooks");
|
||||||
if (dbg->uncaughtExceptionHook)
|
if (dbg->uncaughtExceptionHook)
|
||||||
MarkObject(trc, *dbg->uncaughtExceptionHook, "hooks");
|
MarkObject(trc, *dbg->uncaughtExceptionHook, "hooks");
|
||||||
|
|
||||||
|
// Mark Debug.Frame objects that are reachable from JS if we look them up
|
||||||
|
// again (because the corresponding JSStackFrame is still on the stack).
|
||||||
|
for (FrameMap::Enum e(dbg->frames); !e.empty(); e.popFront()) {
|
||||||
|
if (e.front().value->getPrivate())
|
||||||
|
MarkObject(trc, *obj, "live Debug.Frame");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Debug::sweepAll(JSRuntime *rt)
|
||||||
|
{
|
||||||
|
for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); c++)
|
||||||
|
sweepCompartment(*c);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Debug::sweepCompartment(JSCompartment *compartment)
|
||||||
|
{
|
||||||
|
// Sweep FrameMap entries for objects being collected.
|
||||||
|
const JSCompartment::DebugVector &debuggers = compartment->getDebuggers();
|
||||||
|
for (Debug **p = debuggers.begin(); p != debuggers.end(); p++) {
|
||||||
|
Debug *dbg = *p;
|
||||||
|
for (FrameMap::Enum e(dbg->frames); !e.empty(); e.popFront()) {
|
||||||
|
if (!e.front().value->isMarked())
|
||||||
|
e.removeFront();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -319,7 +410,7 @@ Debug::detachFrom(JSCompartment *c)
|
|||||||
}
|
}
|
||||||
|
|
||||||
Class Debug::jsclass = {
|
Class Debug::jsclass = {
|
||||||
"Debug", JSCLASS_HAS_PRIVATE,
|
"Debug", JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUG_COUNT),
|
||||||
PropertyStub, PropertyStub, PropertyStub, StrictPropertyStub,
|
PropertyStub, PropertyStub, PropertyStub, StrictPropertyStub,
|
||||||
EnumerateStub, ResolveStub, ConvertStub, Debug::finalize,
|
EnumerateStub, ResolveStub, ConvertStub, Debug::finalize,
|
||||||
NULL, /* reserved0 */
|
NULL, /* reserved0 */
|
||||||
@ -432,17 +523,20 @@ Debug::construct(JSContext *cx, uintN argc, Value *vp)
|
|||||||
JSObject *proto = &v.toObject();
|
JSObject *proto = &v.toObject();
|
||||||
JS_ASSERT(proto->getClass() == &Debug::jsclass);
|
JS_ASSERT(proto->getClass() == &Debug::jsclass);
|
||||||
|
|
||||||
// Make the new Debug object.
|
// Make the new Debug object. Each one has a reference to
|
||||||
|
// Debug.Frame.prototype in a reserved slot.
|
||||||
JSObject *obj = NewNonFunction<WithProto::Given>(cx, &Debug::jsclass, proto, NULL);
|
JSObject *obj = NewNonFunction<WithProto::Given>(cx, &Debug::jsclass, proto, NULL);
|
||||||
if (!obj)
|
if (!obj || !obj->ensureClassReservedSlots(cx))
|
||||||
return false;
|
return false;
|
||||||
|
obj->setReservedSlot(JSSLOT_DEBUG_FRAME_PROTO,
|
||||||
|
proto->getReservedSlot(JSSLOT_DEBUG_FRAME_PROTO));
|
||||||
JSObject *hooks = NewBuiltinClassInstance(cx, &js_ObjectClass);
|
JSObject *hooks = NewBuiltinClassInstance(cx, &js_ObjectClass);
|
||||||
if (!hooks)
|
if (!hooks)
|
||||||
return false;
|
return false;
|
||||||
Debug *dbg = cx->new_<Debug>(obj, hooks, debuggeeCompartment);
|
Debug *dbg = cx->new_<Debug>(obj, hooks, debuggeeCompartment);
|
||||||
if (!dbg)
|
if (!dbg)
|
||||||
return false;
|
return false;
|
||||||
if (!debuggeeCompartment->addDebug(dbg)) {
|
if (!dbg->init() || !debuggeeCompartment->addDebug(dbg)) {
|
||||||
js_ReportOutOfMemory(cx);
|
js_ReportOutOfMemory(cx);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -460,14 +554,77 @@ JSPropertySpec Debug::properties[] = {
|
|||||||
JS_PS_END
|
JS_PS_END
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// === Debug.Frame
|
||||||
|
|
||||||
|
Class Frame_class = {
|
||||||
|
"Frame", JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_FRAME_COUNT),
|
||||||
|
PropertyStub, PropertyStub, PropertyStub, StrictPropertyStub,
|
||||||
|
EnumerateStub, ResolveStub, ConvertStub, FinalizeStub,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define THIS_FRAME(cx, vp, fnname, thisobj, fp) \
|
||||||
|
JSObject *thisobj = CheckThisClass(cx, vp, &Frame_class, fnname); \
|
||||||
|
if (!thisobj) \
|
||||||
|
return false; \
|
||||||
|
JSStackFrame *fp = (JSStackFrame *) thisobj->getPrivate()
|
||||||
|
|
||||||
|
JSBool
|
||||||
|
Frame_getType(JSContext *cx, uintN argc, Value *vp)
|
||||||
|
{
|
||||||
|
THIS_FRAME(cx, vp, "get type", thisobj, fp);
|
||||||
|
|
||||||
|
// Indirect eval frames are both isGlobalFrame() and isEvalFrame(), so the
|
||||||
|
// order of checks here is significant.
|
||||||
|
vp->setString(fp->isEvalFrame()
|
||||||
|
? cx->runtime->atomState.evalAtom
|
||||||
|
: fp->isGlobalFrame()
|
||||||
|
? cx->runtime->atomState.globalAtom
|
||||||
|
: cx->runtime->atomState.callAtom);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSBool
|
||||||
|
Frame_getGenerator(JSContext *cx, uintN argc, Value *vp)
|
||||||
|
{
|
||||||
|
THIS_FRAME(cx, vp, "get generator", thisobj, fp);
|
||||||
|
vp->setBoolean(fp->isGeneratorFrame());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSBool
|
||||||
|
Frame_construct(JSContext *cx, uintN argc, Value *vp)
|
||||||
|
{
|
||||||
|
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NO_CONSTRUCTOR, "Debug.Frame");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSPropertySpec Frame_properties[] = {
|
||||||
|
JS_PSG("type", Frame_getType, 0),
|
||||||
|
JS_PSG("generator", Frame_getGenerator, 0),
|
||||||
|
JS_PS_END
|
||||||
|
};
|
||||||
|
|
||||||
|
// === Glue
|
||||||
|
|
||||||
extern JS_PUBLIC_API(JSBool)
|
extern JS_PUBLIC_API(JSBool)
|
||||||
JS_DefineDebugObject(JSContext *cx, JSObject *obj)
|
JS_DefineDebugObject(JSContext *cx, JSObject *obj)
|
||||||
{
|
{
|
||||||
JSObject *objProto;
|
JSObject *objProto;
|
||||||
if (!js_GetClassPrototype(cx, obj, JSProto_Object, &objProto))
|
if (!js_GetClassPrototype(cx, obj, JSProto_Object, &objProto))
|
||||||
return NULL;
|
return false;
|
||||||
|
|
||||||
return !!js_InitClass(cx, obj, objProto, &Debug::jsclass, Debug::construct, 1,
|
JSObject *debugCtor;
|
||||||
Debug::properties, NULL, NULL, NULL);
|
JSObject *debugProto = js_InitClass(cx, obj, objProto, &Debug::jsclass, Debug::construct, 1,
|
||||||
|
Debug::properties, NULL, NULL, NULL, &debugCtor);
|
||||||
|
if (!debugProto || !debugProto->ensureClassReservedSlots(cx))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
JSObject *frameCtor;
|
||||||
|
JSObject *frameProto = js_InitClass(cx, debugCtor, objProto, &Frame_class, Frame_construct, 0,
|
||||||
|
Frame_properties, NULL, NULL, NULL, &frameCtor);
|
||||||
|
if (!frameProto)
|
||||||
|
return false;
|
||||||
|
debugProto->setReservedSlot(JSSLOT_DEBUG_FRAME_PROTO, ObjectValue(*frameProto));
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -45,6 +45,7 @@
|
|||||||
#include "jsapi.h"
|
#include "jsapi.h"
|
||||||
#include "jscompartment.h"
|
#include "jscompartment.h"
|
||||||
#include "jsgc.h"
|
#include "jsgc.h"
|
||||||
|
#include "jshashtable.h"
|
||||||
#include "jswrapper.h"
|
#include "jswrapper.h"
|
||||||
#include "jsvalue.h"
|
#include "jsvalue.h"
|
||||||
|
|
||||||
@ -64,6 +65,10 @@ class Debug {
|
|||||||
// property was set.
|
// property was set.
|
||||||
bool hasDebuggerHandler;
|
bool hasDebuggerHandler;
|
||||||
|
|
||||||
|
typedef HashMap<JSStackFrame *, JSObject *, DefaultHasher<JSStackFrame *>, SystemAllocPolicy>
|
||||||
|
FrameMap;
|
||||||
|
FrameMap frames;
|
||||||
|
|
||||||
JSTrapStatus handleUncaughtException(AutoCompartment &ac, Value *vp, bool callHook);
|
JSTrapStatus handleUncaughtException(AutoCompartment &ac, Value *vp, bool callHook);
|
||||||
JSTrapStatus parseResumptionValue(AutoCompartment &ac, bool ok, const Value &rv, Value *vp,
|
JSTrapStatus parseResumptionValue(AutoCompartment &ac, bool ok, const Value &rv, Value *vp,
|
||||||
bool callHook = true);
|
bool callHook = true);
|
||||||
@ -83,14 +88,22 @@ class Debug {
|
|||||||
|
|
||||||
inline bool hasAnyLiveHooks() const;
|
inline bool hasAnyLiveHooks() const;
|
||||||
|
|
||||||
|
bool getScriptFrame(JSContext *cx, JSStackFrame *fp, Value *vp);
|
||||||
|
static void slowPathLeaveStackFrame(JSContext *cx);
|
||||||
|
|
||||||
inline bool observesDebuggerStatement() const;
|
inline bool observesDebuggerStatement() const;
|
||||||
static JSTrapStatus dispatchDebuggerStatement(JSContext *cx, Value *vp);
|
static JSTrapStatus dispatchDebuggerStatement(JSContext *cx, Value *vp);
|
||||||
JSTrapStatus handleDebuggerStatement(JSContext *cx, Value *vp);
|
JSTrapStatus handleDebuggerStatement(JSContext *cx, Value *vp);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Debug(JSObject *dbg, JSObject *hooks, JSCompartment *compartment);
|
Debug(JSObject *dbg, JSObject *hooks, JSCompartment *compartment);
|
||||||
|
bool init();
|
||||||
|
inline JSObject *toJSObject() const;
|
||||||
|
static inline Debug *fromJSObject(JSObject *obj);
|
||||||
|
|
||||||
// Mark some Debug objects. A Debug object is live if:
|
// Methods for interaction with the GC.
|
||||||
|
//
|
||||||
|
// 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
|
||||||
// code roots it in this case); OR
|
// code roots it in this case); OR
|
||||||
@ -100,18 +113,18 @@ class Debug {
|
|||||||
// - it has a breakpoint set on a live script
|
// - it has a breakpoint set on a live script
|
||||||
// - it has a watchpoint set on a live object.
|
// - it has a watchpoint set on a live object.
|
||||||
//
|
//
|
||||||
// The last case is handled by this method. If it finds any Debug objects
|
// The last case is handled by the mark() method. If it finds any Debug
|
||||||
// that are definitely live but not yet marked, it marks them and returns
|
// objects that are definitely live but not yet marked, it marks them and
|
||||||
// true. If not, it returns false.
|
// returns true. If not, it returns false.
|
||||||
//
|
//
|
||||||
static bool mark(GCMarker *trc, JSCompartment *compartment, JSGCInvocationKind gckind);
|
static bool mark(GCMarker *trc, JSCompartment *compartment, JSGCInvocationKind gckind);
|
||||||
|
static void sweepAll(JSRuntime *rt);
|
||||||
inline JSObject *toJSObject() const;
|
static void sweepCompartment(JSCompartment *compartment);
|
||||||
static inline Debug *fromJSObject(JSObject *obj);
|
|
||||||
|
|
||||||
inline bool observesCompartment(JSCompartment *c) const;
|
inline bool observesCompartment(JSCompartment *c) const;
|
||||||
void detachFrom(JSCompartment *c);
|
void detachFrom(JSCompartment *c);
|
||||||
|
|
||||||
|
static inline void leaveStackFrame(JSContext *cx);
|
||||||
static inline JSTrapStatus onDebuggerStatement(JSContext *cx, js::Value *vp);
|
static inline JSTrapStatus onDebuggerStatement(JSContext *cx, js::Value *vp);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -142,6 +155,13 @@ Debug::fromJSObject(JSObject *obj)
|
|||||||
return (Debug *) obj->getPrivate();
|
return (Debug *) obj->getPrivate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Debug::leaveStackFrame(JSContext *cx)
|
||||||
|
{
|
||||||
|
if (!cx->compartment->getDebuggers().empty())
|
||||||
|
slowPathLeaveStackFrame(cx);
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
Debug::observesDebuggerStatement() const
|
Debug::observesDebuggerStatement() const
|
||||||
{
|
{
|
||||||
|
@ -133,6 +133,7 @@ ScriptDebugPrologue(JSContext *cx, JSStackFrame *fp)
|
|||||||
bool
|
bool
|
||||||
ScriptDebugEpilogue(JSContext *cx, JSStackFrame *fp, bool okArg)
|
ScriptDebugEpilogue(JSContext *cx, JSStackFrame *fp, bool okArg)
|
||||||
{
|
{
|
||||||
|
JS_ASSERT(fp == cx->fp());
|
||||||
JSBool ok = okArg;
|
JSBool ok = okArg;
|
||||||
|
|
||||||
Probes::exitJSFun(cx, fp->maybeFun(), fp->script());
|
Probes::exitJSFun(cx, fp->maybeFun(), fp->script());
|
||||||
@ -146,6 +147,7 @@ ScriptDebugEpilogue(JSContext *cx, JSStackFrame *fp, bool okArg)
|
|||||||
hook(cx, fp, false, &ok, hookData);
|
hook(cx, fp, false, &ok, hookData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Debug::leaveStackFrame(cx);
|
||||||
|
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
@ -2527,6 +2527,11 @@ MarkAndSweep(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIM
|
|||||||
/* Finalize watch points associated with unreachable objects. */
|
/* Finalize watch points associated with unreachable objects. */
|
||||||
js_SweepWatchPoints(cx);
|
js_SweepWatchPoints(cx);
|
||||||
|
|
||||||
|
if (comp)
|
||||||
|
Debug::sweepCompartment(comp);
|
||||||
|
else
|
||||||
|
Debug::sweepAll(rt);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We finalize objects before other GC things to ensure that object's finalizer
|
* We finalize objects before other GC things to ensure that object's finalizer
|
||||||
* can access them even if they will be freed. Sweep the runtime's property trees
|
* can access them even if they will be freed. Sweep the runtime's property trees
|
||||||
|
@ -3827,7 +3827,8 @@ DefineConstructorAndPrototype(JSContext *cx, JSObject *obj, JSProtoKey key, JSAt
|
|||||||
JSObject *protoProto, Class *clasp,
|
JSObject *protoProto, Class *clasp,
|
||||||
Native constructor, uintN nargs,
|
Native constructor, uintN nargs,
|
||||||
JSPropertySpec *ps, JSFunctionSpec *fs,
|
JSPropertySpec *ps, JSFunctionSpec *fs,
|
||||||
JSPropertySpec *static_ps, JSFunctionSpec *static_fs)
|
JSPropertySpec *static_ps, JSFunctionSpec *static_fs,
|
||||||
|
JSObject **ctorp)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Create a prototype object for this class.
|
* Create a prototype object for this class.
|
||||||
@ -3966,6 +3967,8 @@ DefineConstructorAndPrototype(JSContext *cx, JSObject *obj, JSProtoKey key, JSAt
|
|||||||
if (key != JSProto_Null && !js_SetClassObject(cx, obj, key, ctor, proto))
|
if (key != JSProto_Null && !js_SetClassObject(cx, obj, key, ctor, proto))
|
||||||
goto bad;
|
goto bad;
|
||||||
|
|
||||||
|
if (ctorp)
|
||||||
|
*ctorp = ctor;
|
||||||
return proto;
|
return proto;
|
||||||
|
|
||||||
bad:
|
bad:
|
||||||
@ -3982,7 +3985,8 @@ JSObject *
|
|||||||
js_InitClass(JSContext *cx, JSObject *obj, JSObject *protoProto,
|
js_InitClass(JSContext *cx, JSObject *obj, JSObject *protoProto,
|
||||||
Class *clasp, Native constructor, uintN nargs,
|
Class *clasp, Native constructor, uintN nargs,
|
||||||
JSPropertySpec *ps, JSFunctionSpec *fs,
|
JSPropertySpec *ps, JSFunctionSpec *fs,
|
||||||
JSPropertySpec *static_ps, JSFunctionSpec *static_fs)
|
JSPropertySpec *static_ps, JSFunctionSpec *static_fs,
|
||||||
|
JSObject **ctorp)
|
||||||
{
|
{
|
||||||
JSAtom *atom = js_Atomize(cx, clasp->name, strlen(clasp->name), 0);
|
JSAtom *atom = js_Atomize(cx, clasp->name, strlen(clasp->name), 0);
|
||||||
if (!atom)
|
if (!atom)
|
||||||
@ -4008,7 +4012,7 @@ js_InitClass(JSContext *cx, JSObject *obj, JSObject *protoProto,
|
|||||||
}
|
}
|
||||||
|
|
||||||
return DefineConstructorAndPrototype(cx, obj, key, atom, protoProto, clasp, constructor, nargs,
|
return DefineConstructorAndPrototype(cx, obj, key, atom, protoProto, clasp, constructor, nargs,
|
||||||
ps, fs, static_ps, static_fs);
|
ps, fs, static_ps, static_fs, ctorp);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
@ -663,6 +663,9 @@ struct JSObject : js::gc::Cell {
|
|||||||
|
|
||||||
inline js::Value getReservedSlot(uintN index) const;
|
inline js::Value getReservedSlot(uintN index) const;
|
||||||
|
|
||||||
|
/* Call this only after the appropriate ensure{Class,Instance}ReservedSlots call. */
|
||||||
|
inline void setReservedSlot(uintN index, const js::Value &v);
|
||||||
|
|
||||||
/* Defined in jsscopeinlines.h to avoid including implementation dependencies here. */
|
/* Defined in jsscopeinlines.h to avoid including implementation dependencies here. */
|
||||||
inline void updateShape(JSContext *cx);
|
inline void updateShape(JSContext *cx);
|
||||||
inline void updateFlags(const js::Shape *shape, bool isDefinitelyAtom = false);
|
inline void updateFlags(const js::Shape *shape, bool isDefinitelyAtom = false);
|
||||||
@ -1548,14 +1551,16 @@ DefineConstructorAndPrototype(JSContext *cx, JSObject *obj, JSProtoKey key, JSAt
|
|||||||
JSObject *protoProto, Class *clasp,
|
JSObject *protoProto, Class *clasp,
|
||||||
Native constructor, uintN nargs,
|
Native constructor, uintN nargs,
|
||||||
JSPropertySpec *ps, JSFunctionSpec *fs,
|
JSPropertySpec *ps, JSFunctionSpec *fs,
|
||||||
JSPropertySpec *static_ps, JSFunctionSpec *static_fs);
|
JSPropertySpec *static_ps, JSFunctionSpec *static_fs,
|
||||||
|
JSObject **ctorp = NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern JSObject *
|
extern JSObject *
|
||||||
js_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto,
|
js_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto,
|
||||||
js::Class *clasp, js::Native constructor, uintN nargs,
|
js::Class *clasp, js::Native constructor, uintN nargs,
|
||||||
JSPropertySpec *ps, JSFunctionSpec *fs,
|
JSPropertySpec *ps, JSFunctionSpec *fs,
|
||||||
JSPropertySpec *static_ps, JSFunctionSpec *static_fs);
|
JSPropertySpec *static_ps, JSFunctionSpec *static_fs,
|
||||||
|
JSObject **ctorp = NULL);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Select Object.prototype method names shared between jsapi.cpp and jsobj.cpp.
|
* Select Object.prototype method names shared between jsapi.cpp and jsobj.cpp.
|
||||||
|
@ -298,6 +298,12 @@ JSObject::getReservedSlot(uintN index) const
|
|||||||
return (index < numSlots()) ? getSlot(index) : js::UndefinedValue();
|
return (index < numSlots()) ? getSlot(index) : js::UndefinedValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void
|
||||||
|
JSObject::setReservedSlot(uintN index, const js::Value &v)
|
||||||
|
{
|
||||||
|
setSlot(index, v);
|
||||||
|
}
|
||||||
|
|
||||||
inline bool
|
inline bool
|
||||||
JSObject::canHaveMethodBarrier() const
|
JSObject::canHaveMethodBarrier() const
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user