Bug 378793: Patch from Mike Moening to implement per-context debug hooks. r=me,brendan

This commit is contained in:
igor%mir2.org 2007-06-14 20:04:09 +00:00
Родитель 381a5bcaf3
Коммит ce916182a4
10 изменённых файлов: 144 добавлений и 107 удалений

Просмотреть файл

@ -4699,7 +4699,7 @@ JS_ExecuteScriptPart(JSContext *cx, JSObject *obj, JSScript *script,
JSExecPart part, jsval *rval)
{
JSScript tmp;
JSRuntime *rt;
JSDebugHooks *hooks;
JSBool ok;
/* Make a temporary copy of the JSScript structure and farble it a bit. */
@ -4712,16 +4712,16 @@ JS_ExecuteScriptPart(JSContext *cx, JSObject *obj, JSScript *script,
}
/* Tell the debugger about our temporary copy of the script structure. */
rt = cx->runtime;
if (rt->newScriptHook) {
rt->newScriptHook(cx, tmp.filename, tmp.lineno, &tmp, NULL,
rt->newScriptHookData);
hooks = cx->debugHooks;
if (hooks->newScriptHook) {
hooks->newScriptHook(cx, tmp.filename, tmp.lineno, &tmp, NULL,
hooks->newScriptHookData);
}
/* Execute the farbled struct and tell the debugger to forget about it. */
ok = JS_ExecuteScript(cx, obj, &tmp, rval);
if (rt->destroyScriptHook)
rt->destroyScriptHook(cx, &tmp, rt->destroyScriptHookData);
if (hooks->destroyScriptHook)
hooks->destroyScriptHook(cx, &tmp, hooks->destroyScriptHookData);
return ok;
}

Просмотреть файл

@ -225,6 +225,7 @@ js_NewContext(JSRuntime *rt, size_t stackChunkSize)
memset(cx, 0, sizeof *cx);
cx->runtime = rt;
cx->debugHooks = &rt->globalDebugHooks;
#if JS_STACK_GROWTH_DIRECTION > 0
cx->stackLimit = (jsuword)-1;
#endif
@ -851,11 +852,11 @@ ReportError(JSContext *cx, const char *message, JSErrorReport *reportp)
*/
if (!js_ErrorToException(cx, message, reportp)) {
js_ReportErrorAgain(cx, message, reportp);
} else if (cx->runtime->debugErrorHook && cx->errorReporter) {
JSDebugErrorHook hook = cx->runtime->debugErrorHook;
} else if (cx->debugHooks->debugErrorHook && cx->errorReporter) {
JSDebugErrorHook hook = cx->debugHooks->debugErrorHook;
/* test local in case debugErrorHook changed on another thread */
if (hook)
hook(cx, message, reportp, cx->runtime->debugErrorHookData);
hook(cx, message, reportp, cx->debugHooks->debugErrorHookData);
}
}
@ -900,9 +901,9 @@ js_ReportOutOfMemory(JSContext *cx)
* sending the error on to the regular ErrorReporter.
*/
if (onError) {
JSDebugErrorHook hook = cx->runtime->debugErrorHook;
JSDebugErrorHook hook = cx->debugHooks->debugErrorHook;
if (hook &&
!hook(cx, msg, &report, cx->runtime->debugErrorHookData)) {
!hook(cx, msg, &report, cx->debugHooks->debugErrorHookData)) {
onError = NULL;
}
}
@ -1202,10 +1203,10 @@ js_ReportErrorAgain(JSContext *cx, const char *message, JSErrorReport *reportp)
* sending the error on to the regular ErrorReporter.
*/
if (onError) {
JSDebugErrorHook hook = cx->runtime->debugErrorHook;
JSDebugErrorHook hook = cx->debugHooks->debugErrorHook;
if (hook &&
!hook(cx, cx->lastMessage, reportp,
cx->runtime->debugErrorHookData)) {
cx->debugHooks->debugErrorHookData)) {
onError = NULL;
}
}

Просмотреть файл

@ -264,27 +264,8 @@ struct JSRuntime {
/* List of active contexts sharing this runtime; protected by gcLock. */
JSCList contextList;
/* These are used for debugging -- see jsprvtd.h and jsdbgapi.h. */
JSTrapHandler interruptHandler;
void *interruptHandlerData;
JSNewScriptHook newScriptHook;
void *newScriptHookData;
JSDestroyScriptHook destroyScriptHook;
void *destroyScriptHookData;
JSTrapHandler debuggerHandler;
void *debuggerHandlerData;
JSSourceHandler sourceHandler;
void *sourceHandlerData;
JSInterpreterHook executeHook;
void *executeHookData;
JSInterpreterHook callHook;
void *callHookData;
JSObjectHook objectHook;
void *objectHookData;
JSTrapHandler throwHook;
void *throwHookData;
JSDebugErrorHook debugErrorHook;
void *debugErrorHookData;
/* Per runtime debug hooks -- see jsprvtd.h and jsdbgapi.h. */
JSDebugHooks globalDebugHooks;
/* More debugging state, see jsdbgapi.c. */
JSCList trapList;
@ -786,6 +767,9 @@ struct JSContext {
/* Stack of thread-stack-allocated temporary GC roots. */
JSTempValueRooter *tempValueRooters;
/* Debug hooks associated with the current context. */
JSDebugHooks *debugHooks;
};
#ifdef JS_THREADSAFE

Просмотреть файл

@ -296,8 +296,8 @@ JS_HandleTrap(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval)
JS_PUBLIC_API(JSBool)
JS_SetInterrupt(JSRuntime *rt, JSTrapHandler handler, void *closure)
{
rt->interruptHandler = handler;
rt->interruptHandlerData = closure;
rt->globalDebugHooks.interruptHandler = handler;
rt->globalDebugHooks.interruptHandlerData = closure;
return JS_TRUE;
}
@ -305,11 +305,11 @@ JS_PUBLIC_API(JSBool)
JS_ClearInterrupt(JSRuntime *rt, JSTrapHandler *handlerp, void **closurep)
{
if (handlerp)
*handlerp = (JSTrapHandler)rt->interruptHandler;
*handlerp = (JSTrapHandler)rt->globalDebugHooks.interruptHandler;
if (closurep)
*closurep = rt->interruptHandlerData;
rt->interruptHandler = 0;
rt->interruptHandlerData = 0;
*closurep = rt->globalDebugHooks.interruptHandlerData;
rt->globalDebugHooks.interruptHandler = 0;
rt->globalDebugHooks.interruptHandlerData = 0;
return JS_TRUE;
}
@ -1115,16 +1115,16 @@ JS_GetScriptVersion(JSContext *cx, JSScript *script)
JS_PUBLIC_API(void)
JS_SetNewScriptHook(JSRuntime *rt, JSNewScriptHook hook, void *callerdata)
{
rt->newScriptHook = hook;
rt->newScriptHookData = callerdata;
rt->globalDebugHooks.newScriptHook = hook;
rt->globalDebugHooks.newScriptHookData = callerdata;
}
JS_PUBLIC_API(void)
JS_SetDestroyScriptHook(JSRuntime *rt, JSDestroyScriptHook hook,
void *callerdata)
{
rt->destroyScriptHook = hook;
rt->destroyScriptHookData = callerdata;
rt->globalDebugHooks.destroyScriptHook = hook;
rt->globalDebugHooks.destroyScriptHookData = callerdata;
}
/***************************************************************************/
@ -1375,56 +1375,56 @@ JS_PutPropertyDescArray(JSContext *cx, JSPropertyDescArray *pda)
JS_PUBLIC_API(JSBool)
JS_SetDebuggerHandler(JSRuntime *rt, JSTrapHandler handler, void *closure)
{
rt->debuggerHandler = handler;
rt->debuggerHandlerData = closure;
rt->globalDebugHooks.debuggerHandler = handler;
rt->globalDebugHooks.debuggerHandlerData = closure;
return JS_TRUE;
}
JS_PUBLIC_API(JSBool)
JS_SetSourceHandler(JSRuntime *rt, JSSourceHandler handler, void *closure)
{
rt->sourceHandler = handler;
rt->sourceHandlerData = closure;
rt->globalDebugHooks.sourceHandler = handler;
rt->globalDebugHooks.sourceHandlerData = closure;
return JS_TRUE;
}
JS_PUBLIC_API(JSBool)
JS_SetExecuteHook(JSRuntime *rt, JSInterpreterHook hook, void *closure)
{
rt->executeHook = hook;
rt->executeHookData = closure;
rt->globalDebugHooks.executeHook = hook;
rt->globalDebugHooks.executeHookData = closure;
return JS_TRUE;
}
JS_PUBLIC_API(JSBool)
JS_SetCallHook(JSRuntime *rt, JSInterpreterHook hook, void *closure)
{
rt->callHook = hook;
rt->callHookData = closure;
rt->globalDebugHooks.callHook = hook;
rt->globalDebugHooks.callHookData = closure;
return JS_TRUE;
}
JS_PUBLIC_API(JSBool)
JS_SetObjectHook(JSRuntime *rt, JSObjectHook hook, void *closure)
{
rt->objectHook = hook;
rt->objectHookData = closure;
rt->globalDebugHooks.objectHook = hook;
rt->globalDebugHooks.objectHookData = closure;
return JS_TRUE;
}
JS_PUBLIC_API(JSBool)
JS_SetThrowHook(JSRuntime *rt, JSTrapHandler hook, void *closure)
{
rt->throwHook = hook;
rt->throwHookData = closure;
rt->globalDebugHooks.throwHook = hook;
rt->globalDebugHooks.throwHookData = closure;
return JS_TRUE;
}
JS_PUBLIC_API(JSBool)
JS_SetDebugErrorHook(JSRuntime *rt, JSDebugErrorHook hook, void *closure)
{
rt->debugErrorHook = hook;
rt->debugErrorHookData = closure;
rt->globalDebugHooks.debugErrorHook = hook;
rt->globalDebugHooks.debugErrorHookData = closure;
return JS_TRUE;
}
@ -1574,3 +1574,22 @@ JS_FlagSystemObject(JSContext *cx, JSObject *obj)
flagp = js_GetGCThingFlags(obj);
*flagp |= GCF_SYSTEM;
}
/************************************************************************/
JS_PUBLIC_API(JSDebugHooks *)
JS_GetGlobalDebugHooks(JSRuntime *rt)
{
return &rt->globalDebugHooks;
}
JS_PUBLIC_API(JSDebugHooks *)
JS_SetContextDebugHooks(JSContext *cx, JSDebugHooks *hooks)
{
JSDebugHooks *old;
JS_ASSERT(hooks);
old = cx->debugHooks;
cx->debugHooks = hooks;
return old;
}

Просмотреть файл

@ -404,6 +404,14 @@ JS_IsSystemObject(JSContext *cx, JSObject *obj);
extern JS_PUBLIC_API(void)
JS_FlagSystemObject(JSContext *cx, JSObject *obj);
/************************************************************************/
extern JS_PUBLIC_API(JSDebugHooks *)
JS_GetGlobalDebugHooks(JSRuntime *rt);
extern JS_PUBLIC_API(JSDebugHooks *)
JS_SetContextDebugHooks(JSContext *cx, JSDebugHooks *hooks);
JS_END_EXTERN_C
#endif /* jsdbgapi_h___ */

Просмотреть файл

@ -1208,7 +1208,7 @@ have_fun:
cx->fp = &frame;
/* Init these now in case we goto out before first hook call. */
hook = cx->runtime->callHook;
hook = cx->debugHooks->callHook;
hookData = NULL;
/* Check for argument slots required by the function. */
@ -1287,7 +1287,7 @@ have_fun:
/* call the hook if present */
if (hook && (native || script))
hookData = hook(cx, &frame, JS_TRUE, 0, cx->runtime->callHookData);
hookData = hook(cx, &frame, JS_TRUE, 0, cx->debugHooks->callHookData);
/* Call the function, either a native method or an interpreted script. */
if (native) {
@ -1331,7 +1331,7 @@ have_fun:
out:
if (hookData) {
hook = cx->runtime->callHook;
hook = cx->debugHooks->callHook;
if (hook)
hook(cx, &frame, JS_FALSE, &ok, hookData);
}
@ -1482,7 +1482,7 @@ js_Execute(JSContext *cx, JSObject *chain, JSScript *script,
JSObject *obj, *tmp;
JSBool ok;
hook = cx->runtime->executeHook;
hook = cx->debugHooks->executeHook;
hookData = mark = NULL;
oldfp = cx->fp;
frame.script = script;
@ -1555,8 +1555,10 @@ js_Execute(JSContext *cx, JSObject *chain, JSScript *script,
}
cx->fp = &frame;
if (hook)
hookData = hook(cx, &frame, JS_TRUE, 0, cx->runtime->executeHookData);
if (hook) {
hookData = hook(cx, &frame, JS_TRUE, 0,
cx->debugHooks->executeHookData);
}
/*
* Use frame.rval, not result, so the last result stays rooted across any
@ -1566,7 +1568,7 @@ js_Execute(JSContext *cx, JSObject *chain, JSScript *script,
*result = frame.rval;
if (hookData) {
hook = cx->runtime->executeHook;
hook = cx->debugHooks->executeHook;
if (hook)
hook(cx, &frame, JS_FALSE, &ok, hookData);
}
@ -2255,13 +2257,13 @@ js_Interpret(JSContext *cx, jsbytecode *pc, jsval *result)
# define LOAD_JUMP_TABLE() /* nothing */
#endif
#define LOAD_INTERRUPT_HANDLER(rt) \
#define LOAD_INTERRUPT_HANDLER(cx) \
JS_BEGIN_MACRO \
interruptHandler = (rt)->interruptHandler; \
interruptHandler = (cx)->debugHooks->interruptHandler; \
LOAD_JUMP_TABLE(); \
JS_END_MACRO
LOAD_INTERRUPT_HANDLER(rt);
LOAD_INTERRUPT_HANDLER(cx);
/* Check for too much js_Interpret nesting, or too deep a C stack. */
++cx->interpLevel;
@ -2327,7 +2329,7 @@ js_Interpret(JSContext *cx, jsbytecode *pc, jsval *result)
interrupt:
SAVE_SP_AND_PC(fp);
switch (interruptHandler(cx, script, pc, &rval,
rt->interruptHandlerData)) {
cx->debugHooks->interruptHandlerData)) {
case JSTRAP_ERROR:
ok = JS_FALSE;
goto out;
@ -2343,7 +2345,7 @@ interrupt:
goto out;
default:;
}
LOAD_INTERRUPT_HANDLER(rt);
LOAD_INTERRUPT_HANDLER(cx);
}
JS_ASSERT((uintN)op < (uintN)JSOP_LIMIT);
@ -2386,7 +2388,7 @@ interrupt:
if (interruptHandler) {
SAVE_SP_AND_PC(fp);
switch (interruptHandler(cx, script, pc, &rval,
rt->interruptHandlerData)) {
cx->debugHooks->interruptHandlerData)) {
case JSTRAP_ERROR:
ok = JS_FALSE;
goto out;
@ -2402,7 +2404,7 @@ interrupt:
goto out;
default:;
}
LOAD_INTERRUPT_HANDLER(rt);
LOAD_INTERRUPT_HANDLER(cx);
}
switch (op) {
@ -2519,11 +2521,11 @@ interrupt:
}
if (hookData) {
JSInterpreterHook hook = rt->callHook;
JSInterpreterHook hook = cx->debugHooks->callHook;
if (hook) {
SAVE_SP_AND_PC(fp);
hook(cx, fp, JS_FALSE, &ok, hookData);
LOAD_INTERRUPT_HANDLER(rt);
LOAD_INTERRUPT_HANDLER(cx);
}
}
@ -3423,7 +3425,7 @@ interrupt:
if (!ok)
goto out;
RESTORE_SP(fp);
LOAD_INTERRUPT_HANDLER(rt);
LOAD_INTERRUPT_HANDLER(cx);
obj = JSVAL_TO_OBJECT(*vp);
len = js_CodeSpec[op].length;
DO_NEXT_OP(len);
@ -3939,12 +3941,12 @@ interrupt:
SAVE_SP(&newifp->frame);
/* Call the debugger hook if present. */
hook = rt->callHook;
hook = cx->debugHooks->callHook;
if (hook) {
newifp->frame.pc = NULL;
newifp->hookData = hook(cx, &newifp->frame, JS_TRUE, 0,
rt->callHookData);
LOAD_INTERRUPT_HANDLER(rt);
cx->debugHooks->callHookData);
LOAD_INTERRUPT_HANDLER(cx);
} else {
newifp->hookData = NULL;
}
@ -3989,7 +3991,7 @@ interrupt:
ok = js_Invoke(cx, argc, 0);
RESTORE_SP(fp);
LOAD_INTERRUPT_HANDLER(rt);
LOAD_INTERRUPT_HANDLER(cx);
if (!ok)
goto out;
JS_RUNTIME_METER(rt, nonInlineCalls);
@ -4025,7 +4027,7 @@ interrupt:
SAVE_SP_AND_PC(fp);
ok = js_Invoke(cx, argc, 0);
RESTORE_SP(fp);
LOAD_INTERRUPT_HANDLER(rt);
LOAD_INTERRUPT_HANDLER(cx);
if (!ok)
goto out;
if (!cx->rval2set) {
@ -4479,7 +4481,7 @@ interrupt:
JS_ASSERT(JSVAL_IS_INT(rval));
op = (JSOp) JSVAL_TO_INT(rval);
JS_ASSERT((uintN)op < (uintN)JSOP_LIMIT);
LOAD_INTERRUPT_HANDLER(rt);
LOAD_INTERRUPT_HANDLER(cx);
DO_OP();
case JSTRAP_RETURN:
fp->rval = rval;
@ -4491,7 +4493,7 @@ interrupt:
goto out;
default:;
}
LOAD_INTERRUPT_HANDLER(rt);
LOAD_INTERRUPT_HANDLER(cx);
END_CASE(JSOP_TRAP)
BEGIN_CASE(JSOP_ARGUMENTS)
@ -5344,11 +5346,11 @@ interrupt:
#if JS_HAS_DEBUGGER_KEYWORD
BEGIN_CASE(JSOP_DEBUGGER)
{
JSTrapHandler handler = rt->debuggerHandler;
JSTrapHandler handler = cx->debugHooks->debuggerHandler;
if (handler) {
SAVE_SP_AND_PC(fp);
switch (handler(cx, script, pc, &rval,
rt->debuggerHandlerData)) {
cx->debugHooks->debuggerHandlerData)) {
case JSTRAP_ERROR:
ok = JS_FALSE;
goto out;
@ -5364,7 +5366,7 @@ interrupt:
goto out;
default:;
}
LOAD_INTERRUPT_HANDLER(rt);
LOAD_INTERRUPT_HANDLER(cx);
}
}
END_CASE(JSOP_DEBUGGER)
@ -6001,10 +6003,11 @@ out:
/*
* Call debugger throw hook if set (XXX thread safety?).
*/
handler = rt->throwHook;
handler = cx->debugHooks->throwHook;
if (handler) {
SAVE_SP_AND_PC(fp);
switch (handler(cx, script, pc, &rval, rt->throwHookData)) {
switch (handler(cx, script, pc, &rval,
cx->debugHooks->throwHookData)) {
case JSTRAP_ERROR:
cx->throwing = JS_FALSE;
goto no_catch;
@ -6018,7 +6021,7 @@ out:
case JSTRAP_CONTINUE:
default:;
}
LOAD_INTERRUPT_HANDLER(rt);
LOAD_INTERRUPT_HANDLER(cx);
}
/*

Просмотреть файл

@ -2506,9 +2506,10 @@ js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent)
}
}
if (cx->runtime->objectHook) {
if (cx->debugHooks->objectHook) {
JS_KEEP_ATOMS(cx->runtime);
cx->runtime->objectHook(cx, obj, JS_TRUE, cx->runtime->objectHookData);
cx->debugHooks->objectHook(cx, obj, JS_TRUE,
cx->debugHooks->objectHookData);
JS_UNKEEP_ATOMS(cx->runtime);
}
@ -2756,8 +2757,10 @@ js_FinalizeObject(JSContext *cx, JSObject *obj)
if (!map)
return;
if (cx->runtime->objectHook)
cx->runtime->objectHook(cx, obj, JS_FALSE, cx->runtime->objectHookData);
if (cx->debugHooks->objectHook) {
cx->debugHooks->objectHook(cx, obj, JS_FALSE,
cx->debugHooks->objectHookData);
}
/* Remove all watchpoints with weak links to obj. */
JS_ClearWatchPointsForObject(cx, obj);

Просмотреть файл

@ -199,4 +199,27 @@ typedef JSBool
(* JS_DLL_CALLBACK JSDebugErrorHook)(JSContext *cx, const char *message,
JSErrorReport *report, void *closure);
typedef struct JSDebugHooks {
JSTrapHandler interruptHandler;
void *interruptHandlerData;
JSNewScriptHook newScriptHook;
void *newScriptHookData;
JSDestroyScriptHook destroyScriptHook;
void *destroyScriptHookData;
JSTrapHandler debuggerHandler;
void *debuggerHandlerData;
JSSourceHandler sourceHandler;
void *sourceHandlerData;
JSInterpreterHook executeHook;
void *executeHookData;
JSInterpreterHook callHook;
void *callHookData;
JSObjectHook objectHook;
void *objectHookData;
JSTrapHandler throwHook;
void *throwHookData;
JSDebugErrorHook debugErrorHook;
void *debugErrorHookData;
} JSDebugHooks;
#endif /* jsprvtd_h___ */

Просмотреть файл

@ -251,8 +251,8 @@ js_NewBufferTokenStream(JSContext *cx, const jschar *base, size_t length)
ts->userbuf.ptr = (jschar *)base;
ts->tokenbuf.grow = GrowTokenBuf;
ts->tokenbuf.data = cx;
ts->listener = cx->runtime->sourceHandler;
ts->listenerData = cx->runtime->sourceHandlerData;
ts->listener = cx->debugHooks->sourceHandler;
ts->listenerData = cx->debugHooks->sourceHandlerData;
return ts;
}
@ -696,14 +696,14 @@ ReportCompileErrorNumber(JSContext *cx, void *handle, uintN flags,
onError = NULL;
if (onError) {
JSDebugErrorHook hook = cx->runtime->debugErrorHook;
JSDebugErrorHook hook = cx->debugHooks->debugErrorHook;
/*
* If debugErrorHook is present then we give it a chance to veto
* sending the error on to the regular error reporter.
*/
if (hook && !hook(cx, message, report,
cx->runtime->debugErrorHookData)) {
cx->debugHooks->debugErrorHookData)) {
onError = NULL;
}
}

Просмотреть файл

@ -1459,29 +1459,25 @@ bad:
JS_FRIEND_API(void)
js_CallNewScriptHook(JSContext *cx, JSScript *script, JSFunction *fun)
{
JSRuntime *rt;
JSNewScriptHook hook;
rt = cx->runtime;
hook = rt->newScriptHook;
hook = cx->debugHooks->newScriptHook;
if (hook) {
JS_KEEP_ATOMS(rt);
JS_KEEP_ATOMS(cx->runtime);
hook(cx, script->filename, script->lineno, script, fun,
rt->newScriptHookData);
JS_UNKEEP_ATOMS(rt);
cx->debugHooks->newScriptHookData);
JS_UNKEEP_ATOMS(cx->runtime);
}
}
JS_FRIEND_API(void)
js_CallDestroyScriptHook(JSContext *cx, JSScript *script)
{
JSRuntime *rt;
JSDestroyScriptHook hook;
rt = cx->runtime;
hook = rt->destroyScriptHook;
hook = cx->debugHooks->destroyScriptHook;
if (hook)
hook(cx, script, rt->destroyScriptHookData);
hook(cx, script, cx->debugHooks->destroyScriptHookData);
}
void