-- not built --

exposing call hook functionality
This commit is contained in:
rginda%netscape.com 2001-09-01 18:03:53 +00:00
parent 95a185d386
commit c5226cb4b5
9 changed files with 409 additions and 30 deletions

@ -56,6 +56,7 @@ interface jsdIDebuggerService;
interface jsdIScriptEnumerator;
interface jsdIScriptHook;
interface jsdIExecutionHook;
interface jsdICallHook;
interface jsdIEphemeral;
interface jsdIPC;
interface jsdIStackFrame;
@ -95,6 +96,14 @@ interface jsdIDebuggerService : nsISupports
* Called when an exception is thrown (even if it will be caught.)
*/
attribute jsdIExecutionHook throwHook;
/**
* Called before and after a toplevel script is evaluated.
*/
attribute jsdICallHook topLevelHook;
/**
* Called before and after a function is called.
*/
attribute jsdICallHook functionHook;
/**
* |true| if the debugger service has been turned on. This does not
@ -195,6 +204,21 @@ interface jsdIScriptHook : nsISupports
void onScriptDestroyed (in jsdIScript script);
};
/**
* Hook instances of this interface up to the
* jsdIDebuggerService::functionHook and toplevelHook properties.
*/
[scriptable, uuid(f102caf6-1dd1-11b2-bd43-c1dbacb95a98)]
interface jsdICallHook : nsISupports
{
const unsigned long TYPE_TOPLEVEL_START = 0;
const unsigned long TYPE_TOPLEVEL_END = 1;
const unsigned long TYPE_FUNCTION_CALL = 2;
const unsigned long TYPE_FUNCTION_RETURN = 3;
void onCall (in jsdIStackFrame frame, in unsigned long type);
};
/**
* Hook instances of this interface up to the
* jsdIDebuggerService::breakpointHook, debuggerHook, errorHook, interruptHook,

@ -129,6 +129,10 @@ struct JSDContext
void* debuggerHookData;
JSD_ExecutionHookProc throwHook;
void* throwHookData;
JSD_CallHookProc functionHook;
void* functionHookData;
JSD_CallHookProc toplevelHook;
void* toplevelHookData;
JSContext* dumbContext;
JSObject* glob;
JSD_UserCallbacks userCallbacks;
@ -513,12 +517,19 @@ extern JSBool
jsd_ClearDebuggerHook(JSDContext* jsdc);
extern JSTrapStatus
jsd_CallExecutionHook(JSDContext* jsdc,
JSContext *cx,
uintN type,
jsd_CallExecutionHook(JSDContext* jsdc,
JSContext* cx,
uintN type,
JSD_ExecutionHookProc hook,
void* hookData,
jsval* rval);
void* hookData,
jsval* rval);
extern JSBool
jsd_CallCallHook (JSDContext* jsdc,
JSContext* cx,
uintN type,
JSD_CallHookProc hook,
void* hookData);
extern JSBool
jsd_SetThrowHook(JSDContext* jsdc,
@ -535,6 +546,22 @@ extern JSTrapStatus JS_DLL_CALLBACK
jsd_ThrowHandler(JSContext *cx, JSScript *script, jsbytecode *pc,
jsval *rval, void *closure);
extern JSBool
jsd_SetFunctionHook(JSDContext* jsdc,
JSD_CallHookProc hook,
void* callerdata);
extern JSBool
jsd_ClearFunctionHook(JSDContext* jsdc);
extern JSBool
jsd_SetTopLevelHook(JSDContext* jsdc,
JSD_CallHookProc hook,
void* callerdata);
extern JSBool
jsd_ClearTopLevelHook(JSDContext* jsdc);
/***************************************************************************/
/* Stack Frame functions */
@ -863,8 +890,12 @@ jsd_GetPropertyVarArgSlot(JSDContext* jsdc, JSDProperty* jsdprop);
/* Stepping Functions */
extern void * JS_DLL_CALLBACK
jsd_InterpreterHook(JSContext *cx, JSStackFrame *fp, JSBool before,
JSBool *ok, void *closure);
jsd_FunctionCallHook(JSContext *cx, JSStackFrame *fp, JSBool before,
JSBool *ok, void *closure);
extern void * JS_DLL_CALLBACK
jsd_TopLevelCallHook(JSContext *cx, JSStackFrame *fp, JSBool before,
JSBool *ok, void *closure);
/**************************************************/
/* Object Functions */

@ -183,8 +183,8 @@ jsd_DebuggerOnForUser(JSRuntime* jsrt,
JS_SetNewScriptHookProc(jsdc->jsrt, jsd_NewScriptHookProc, jsdc);
JS_SetDestroyScriptHookProc(jsdc->jsrt, jsd_DestroyScriptHookProc, jsdc);
JS_SetDebuggerHandler(jsdc->jsrt, jsd_DebuggerHandler, jsdc);
JS_SetExecuteHook(jsdc->jsrt, jsd_InterpreterHook, jsdc);
JS_SetCallHook(jsdc->jsrt, jsd_InterpreterHook, jsdc);
JS_SetExecuteHook(jsdc->jsrt, jsd_TopLevelCallHook, jsdc);
JS_SetCallHook(jsdc->jsrt, jsd_FunctionCallHook, jsdc);
JS_SetObjectHook(jsdc->jsrt, jsd_ObjectHook, jsdc);
JS_SetThrowHook(jsdc->jsrt, jsd_ThrowHandler, jsdc);
JS_SetDebugErrorHook(jsdc->jsrt, jsd_DebugErrorHook, jsdc);

@ -168,6 +168,26 @@ jsd_CallExecutionHook(JSDContext* jsdc,
return JSTRAP_CONTINUE;
}
JSBool
jsd_CallCallHook (JSDContext* jsdc,
JSContext *cx,
uintN type,
JSD_CallHookProc hook,
void* hookData)
{
JSBool hookanswer;
JSDThreadState* jsdthreadstate;
hookanswer = JS_FALSE;
if(hook && NULL != (jsdthreadstate = jsd_NewThreadState(jsdc, cx)))
{
hookanswer = hook(jsdc, jsdthreadstate, type, hookData);
jsd_DestroyThreadState(jsdc, jsdthreadstate);
}
return hookanswer;
}
JSBool
jsd_SetInterruptHook(JSDContext* jsdc,
JSD_ExecutionHookProc hook,
@ -262,3 +282,49 @@ jsd_ClearThrowHook(JSDContext* jsdc)
return JS_TRUE;
}
JSBool
jsd_SetFunctionHook(JSDContext* jsdc,
JSD_CallHookProc hook,
void* callerdata)
{
JSD_LOCK();
jsdc->functionHookData = callerdata;
jsdc->functionHook = hook;
JSD_UNLOCK();
return JS_TRUE;
}
JSBool
jsd_ClearFunctionHook(JSDContext* jsdc)
{
JSD_LOCK();
jsdc->functionHook = NULL;
JSD_UNLOCK();
return JS_TRUE;
}
JSBool
jsd_SetTopLevelHook(JSDContext* jsdc,
JSD_CallHookProc hook,
void* callerdata)
{
JSD_LOCK();
jsdc->toplevelHookData = callerdata;
jsdc->toplevelHook = hook;
JSD_UNLOCK();
return JS_TRUE;
}
JSBool
jsd_ClearTopLevelHook(JSDContext* jsdc)
{
JSD_LOCK();
jsdc->toplevelHook = NULL;
JSD_UNLOCK();
return JS_TRUE;
}

@ -49,7 +49,7 @@ _indentSpaces(int i)
static void
_interpreterTrace(JSDContext* jsdc, JSContext *cx, JSStackFrame *fp,
JSBool before, JSBool *ok)
JSBool before)
{
JSDScript* jsdscript = NULL;
JSScript * script;
@ -93,28 +93,95 @@ _interpreterTrace(JSDContext* jsdc, JSContext *cx, JSStackFrame *fp,
}
#endif
void * JS_DLL_CALLBACK
jsd_InterpreterHook(JSContext *cx, JSStackFrame *fp, JSBool before,
JSBool *ok, void *closure)
JSBool
_callHook(JSDContext *jsdc, JSContext *cx, JSStackFrame *fp, JSBool before,
uintN type, JSD_CallHookProc hook, void *hookData)
{
JSDContext* jsdc = (JSDContext*) closure;
if( ! jsdc || ! jsdc->inited )
return NULL;
if(before && JS_IsContructorFrame(cx, fp))
JSDScript* jsdscript;
JSScript* jsscript;
JSBool hookresult;
if (!jsdc || !jsdc->inited)
return JS_FALSE;
if (before && JS_IsContructorFrame(cx, fp))
jsd_Constructing(jsdc, cx, JS_GetFrameThis(cx, fp), fp);
if (hook)
{
jsscript = JS_GetFrameScript(cx, fp);
if (jsscript) {
JSD_LOCK_SCRIPTS(jsdc);
jsdscript = jsd_FindJSDScript(jsdc, jsscript);
JSD_UNLOCK_SCRIPTS(jsdc);
if (jsdscript) {
hookresult = jsd_CallCallHook (jsdc, cx, type, hook, hookData);
}
}
}
else
{
hookresult = JS_FALSE;
}
#ifdef JSD_TRACE
_interpreterTrace(jsdc, cx, fp, before, ok);
return closure;
_interpreterTrace(jsdc, cx, fp, before);
return JS_TRUE;
#else
return NULL;
return hookresult;
#endif
/*
* use this before calling any hook...
* if( JSD_IS_DANGEROUS_THREAD(jsdc) )
* return NULL;
*/
}
void * JS_DLL_CALLBACK
jsd_FunctionCallHook(JSContext *cx, JSStackFrame *fp, JSBool before,
JSBool *ok, void *closure)
{
JSDContext* jsdc;
JSD_CallHookProc hook;
void* hookData;
jsdc = (JSDContext*) closure;
/* local in case jsdc->functionHook gets cleared on another thread */
JSD_LOCK();
hook = jsdc->functionHook;
hookData = jsdc->functionHookData;
JSD_UNLOCK();
if (_callHook (jsdc, cx, fp, before,
(before) ? JSD_HOOK_FUNCTION_CALL : JSD_HOOK_FUNCTION_RETURN,
hook, hookData))
{
return closure;
}
return NULL;
}
void * JS_DLL_CALLBACK
jsd_TopLevelCallHook(JSContext *cx, JSStackFrame *fp, JSBool before,
JSBool *ok, void *closure)
{
JSDContext* jsdc;
JSD_CallHookProc hook;
void* hookData;
jsdc = (JSDContext*) closure;
/* local in case jsdc->toplevelHook gets cleared on another thread */
JSD_LOCK();
hook = jsdc->toplevelHook;
hookData = jsdc->toplevelHookData;
JSD_UNLOCK();
if (_callHook (jsdc, cx, fp, before,
(before) ? JSD_HOOK_TOPLEVEL_START : JSD_HOOK_TOPLEVEL_END,
hook, hookData))
{
return closure;
}
return NULL;
}

@ -238,6 +238,51 @@ jsds_GCCallbackProc (JSContext *cx, JSGCStatus status)
return JS_TRUE;
}
static JSBool
jsds_EmptyCallHookProc (JSDContext* jsdc, JSDThreadState* jsdthreadstate,
uintN type, void* callerdata)
{
/* empty hook returns true so that we get called when the function
* returns (we may have a hook function by then.) */
return JS_TRUE;
}
static JSBool
jsds_CallHookProc (JSDContext* jsdc, JSDThreadState* jsdthreadstate,
uintN type, void* callerdata)
{
nsCOMPtr<jsdICallHook> hook(0);
switch (type)
{
case JSD_HOOK_TOPLEVEL_START:
case JSD_HOOK_TOPLEVEL_END:
gJsds->GetTopLevelHook(getter_AddRefs(hook));
break;
case JSD_HOOK_FUNCTION_CALL:
case JSD_HOOK_FUNCTION_RETURN:
gJsds->GetFunctionHook(getter_AddRefs(hook));
break;
default:
NS_ASSERTION (0, "Unknown hook type.");
}
if (!hook)
return JS_TRUE;
JSDStackFrameInfo *native_frame = JSD_GetStackFrame (jsdc, jsdthreadstate);
nsCOMPtr<jsdIStackFrame> frame =
getter_AddRefs(jsdStackFrame::FromPtr(jsdc, jsdthreadstate,
native_frame));
hook->OnCall(frame, type);
frame->Invalidate();
return JS_TRUE;
}
static PRUint32
jsds_ExecutionHookProc (JSDContext* jsdc, JSDThreadState* jsdthreadstate,
uintN type, void* callerdata, jsval* rval)
@ -262,6 +307,7 @@ jsds_ExecutionHookProc (JSDContext* jsdc, JSDThreadState* jsdthreadstate,
break;
case JSD_HOOK_THROW:
{
hook_rv = JSD_HOOK_RETURN_CONTINUE_THROW;
gJsds->GetThrowHook(getter_AddRefs(hook));
if (hook) {
JSDValue *jsdv = JSD_GetException (jsdc, jsdthreadstate);
@ -274,7 +320,7 @@ jsds_ExecutionHookProc (JSDContext* jsdc, JSDThreadState* jsdthreadstate,
}
if (!hook)
return NS_OK;
return hook_rv;
JSDStackFrameInfo *native_frame = JSD_GetStackFrame (jsdc, jsdthreadstate);
nsCOMPtr<jsdIStackFrame> frame =
@ -1185,7 +1231,14 @@ jsdService::OnForRuntime (JSRuntime *rt)
JSD_SetDebuggerHook (mCx, jsds_ExecutionHookProc, NULL);
if (mErrorHook)
JSD_SetDebugBreakHook (mCx, jsds_ExecutionHookProc, NULL);
if (mTopLevelHook)
JSD_SetTopLevelHook (mCx, jsds_CallHookProc, NULL);
else
JSD_SetTopLevelHook (mCx, jsds_EmptyCallHookProc, NULL);
if (mFunctionHook)
JSD_SetFunctionHook (mCx, jsds_CallHookProc, NULL);
else
JSD_SetFunctionHook (mCx, jsds_EmptyCallHookProc, NULL);
mOn = PR_TRUE;
return NS_OK;
@ -1220,6 +1273,8 @@ jsdService::Off (void)
JSD_SetInterruptHook (mCx, NULL, NULL);
JSD_SetDebuggerHook (mCx, NULL, NULL);
JSD_SetDebugBreakHook (mCx, NULL, NULL);
JSD_SetTopLevelHook (mCx, NULL, NULL);
JSD_SetFunctionHook (mCx, NULL, NULL);
JSD_DebuggerOff (mCx);
@ -1489,6 +1544,62 @@ jsdService::GetThrowHook (jsdIExecutionHook **aHook)
return NS_OK;
}
NS_IMETHODIMP
jsdService::SetTopLevelHook (jsdICallHook *aHook)
{
mTopLevelHook = aHook;
/* if the debugger isn't initialized, that's all we can do for now. The
* OnForRuntime() method will do the rest when the coast is clear.
*/
if (!mCx)
return NS_OK;
if (aHook)
JSD_SetTopLevelHook (mCx, jsds_CallHookProc, NULL);
else
JSD_SetTopLevelHook (mCx, jsds_EmptyCallHookProc, NULL);
return NS_OK;
}
NS_IMETHODIMP
jsdService::GetTopLevelHook (jsdICallHook **aHook)
{
*aHook = mTopLevelHook;
NS_IF_ADDREF(*aHook);
return NS_OK;
}
NS_IMETHODIMP
jsdService::SetFunctionHook (jsdICallHook *aHook)
{
mFunctionHook = aHook;
/* if the debugger isn't initialized, that's all we can do for now. The
* OnForRuntime() method will do the rest when the coast is clear.
*/
if (!mCx)
return NS_OK;
if (aHook)
JSD_SetFunctionHook (mCx, jsds_CallHookProc, NULL);
else
JSD_SetFunctionHook (mCx, jsds_EmptyCallHookProc, NULL);
return NS_OK;
}
NS_IMETHODIMP
jsdService::GetFunctionHook (jsdICallHook **aHook)
{
*aHook = mFunctionHook;
NS_IF_ADDREF(*aHook);
return NS_OK;
}
jsdService *
jsdService::GetService ()
{

@ -287,7 +287,8 @@ class jsdService : public jsdIDebuggerService
jsdService() : mOn(PR_FALSE), mNestedLoopLevel(0), mCx(0), mRuntime(0),
mBreakpointHook(0), mErrorHook(0), mDebuggerHook(0),
mInterruptHook(0), mScriptHook(0), mThrowHook(0)
mInterruptHook(0), mScriptHook(0), mThrowHook(0),
mTopLevelHook(0), mFunctionHook(0)
{
NS_INIT_ISUPPORTS();
}
@ -308,6 +309,9 @@ class jsdService : public jsdIDebuggerService
nsCOMPtr<jsdIExecutionHook> mInterruptHook;
nsCOMPtr<jsdIScriptHook> mScriptHook;
nsCOMPtr<jsdIExecutionHook> mThrowHook;
nsCOMPtr<jsdICallHook> mTopLevelHook;
nsCOMPtr<jsdICallHook> mFunctionHook;
};
#endif /* JSDSERVICE_H___ */

@ -486,6 +486,38 @@ JSD_ClearThrowHook(JSDContext* jsdc)
return jsd_ClearThrowHook(jsdc);
}
JSD_PUBLIC_API(JSBool)
JSD_SetTopLevelHook(JSDContext* jsdc,
JSD_CallHookProc hook,
void* callerdata)
{
JSD_ASSERT_VALID_CONTEXT(jsdc);
return jsd_SetTopLevelHook(jsdc, hook, callerdata);
}
JSD_PUBLIC_API(JSBool)
JSD_ClearTopLevelHook(JSDContext* jsdc)
{
JSD_ASSERT_VALID_CONTEXT(jsdc);
return jsd_ClearTopLevelHook(jsdc);
}
JSD_PUBLIC_API(JSBool)
JSD_SetFunctionHook(JSDContext* jsdc,
JSD_CallHookProc hook,
void* callerdata)
{
JSD_ASSERT_VALID_CONTEXT(jsdc);
return jsd_SetFunctionHook(jsdc, hook, callerdata);
}
JSD_PUBLIC_API(JSBool)
JSD_ClearFunctionHook(JSDContext* jsdc)
{
JSD_ASSERT_VALID_CONTEXT(jsdc);
return jsd_ClearFunctionHook(jsdc);
}
/***************************************************************************/
/* Stack Frame functions */

@ -555,6 +555,24 @@ typedef uintN
void* callerdata,
jsval* rval);
/* possible 'type' params for JSD_CallHookProc */
#define JSD_HOOK_TOPLEVEL_START 0 /* about to evaluate top level script */
#define JSD_HOOK_TOPLEVEL_END 1 /* done evaluting top level script */
#define JSD_HOOK_FUNCTION_CALL 2 /* about to call a function */
#define JSD_HOOK_FUNCTION_RETURN 3 /* done calling function */
/*
* Implement a callback of this form in order to hook function call/returns.
* Return JS_TRUE from a TOPLEVEL_START or FUNCTION_CALL type call hook if you
* want to hear about the TOPLEVEL_END or FUNCTION_RETURN too. Return value is
* ignored to TOPLEVEL_END and FUNCTION_RETURN type hooks.
*/
typedef JSBool
(*JSD_CallHookProc)(JSDContext* jsdc,
JSDThreadState* jsdthreadstate,
uintN type,
void* callerdata);
/*
* Set Hook to be called whenever the given pc is about to be executed --
* i.e. for 'trap' or 'breakpoint'
@ -649,6 +667,32 @@ JSD_SetThrowHook(JSDContext* jsdc,
extern JSD_PUBLIC_API(JSBool)
JSD_ClearThrowHook(JSDContext* jsdc);
/*
* Set the hook that should be called when a toplevel script begins or completes.
*/
extern JSD_PUBLIC_API(JSBool)
JSD_SetTopLevelHook(JSDContext* jsdc,
JSD_CallHookProc hook,
void* callerdata);
/*
* Clear the toplevel call hook
*/
extern JSD_PUBLIC_API(JSBool)
JSD_ClearTopLevelHook(JSDContext* jsdc);
/*
* Set the hook that should be called when a function call or return happens.
*/
extern JSD_PUBLIC_API(JSBool)
JSD_SetFunctionHook(JSDContext* jsdc,
JSD_CallHookProc hook,
void* callerdata);
/*
* Clear the function call hook
*/
extern JSD_PUBLIC_API(JSBool)
JSD_ClearFunctionHook(JSDContext* jsdc);
/***************************************************************************/
/* Stack Frame functions */