bug 99601, add filter support to js/jsd, r=jband sr=brendan

large addition to the jsd_xpc component allows arbitrary filtering of debug hooks by url pattern, line range, and global object.  also adds ability to begin instrumenting jsscripts at app startup.


git-svn-id: svn://10.0.0.236/trunk@106689 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
rginda%netscape.com 2001-10-30 13:41:32 +00:00
parent 487a8a8072
commit 9dffd404bd
11 changed files with 952 additions and 86 deletions

View File

@ -32,12 +32,17 @@ MODULE = jsdebug
LIBRARY_NAME = jsd
# REQUIRES = java js
REQUIRES = xpcom \
REQUIRES = js
ifndef JSD_STANDALONE
REQUIRES += \
string \
js \
xpcom \
xpconnect \
widget \
dom \
$(NULL)
endif
EXTRA_DSO_LDOPTS += \
$(MOZ_COMPONENT_LIBS) \

View File

@ -53,7 +53,9 @@ native jsuword(jsuword);
/* interfaces we declare in this file */
interface jsdIDebuggerService;
interface jsdIFilter;
interface jsdINestCallback;
interface jsdIFilterEnumerator;
interface jsdIScriptEnumerator;
interface jsdIScriptHook;
interface jsdIExecutionHook;
@ -106,6 +108,12 @@ interface jsdIDebuggerService : nsISupports
*/
attribute jsdICallHook functionHook;
/**
* |true| if the debugger should register an app-start observer in order
* to begin collecting debug information when mozilla is launched.
*/
attribute boolean initAtStartup;
/**
* |true| if the debugger service has been turned on. This does not
* necessarily mean another app is actively using the service, as the
@ -134,23 +142,87 @@ interface jsdIDebuggerService : nsISupports
*/
void off ();
/**
* Temporarily disable the debugger. Hooks will not be called while the
* debugger is paused. Multiple calls to pause will increase the "pause
* depth", and equal number of unPause calles must be made to resume
* normal debugging.
*
* @return depth Number of times pause has been called since the debugger
* has been unpaused.
*/
unsigned long pause();
/**
* Undo a pause.
*
* @return depth The number of remaining pending pause calls.
*/
unsigned long unPause();
/**
* Force the engine to perform garbage collection.
*/
void GC();
/**
* Ignore/unignore execution hooks which are triggered by a context with a
* particular global object.
* Adds an execution hook filter. These filters are consulted each time one
* of the jsdIExecutionHooks is about to be called. Filters are matched in
* a first in, first compared fashion. The first filter to match determines
* whether or not the hook is called. Use swapFilter to reorder existing
* filters, and removeFilter to remove them.
*
* @param glob Global object to ignore/unignore. For the typical Mozilla
* context, global objects are DOM windows.
* If |filter| is already present this method throws NS_ERROR_INVALID_ARG.
*
* @param state |true| to ignore this global object, |false| to unignore.
* If state is |false| and the the glob passed in is not
* currently ignored, this method will throw an
* NS_ERROR_INVALID_PARAM exception.
* @param filter Object representing the filter to add.
* @param after Insert |filter| after this one. Pass null to insert at
* the beginning.
*/
//void filterGlobalObject (in nsISupports glob, in boolean state);
void insertFilter (in jsdIFilter filter, in jsdIFilter after);
/**
* Same as insertFilter, except always add to the end of the list.
*/
void appendFilter (in jsdIFilter filter);
/**
* Unignore execution hooks which are triggered by a jsdIScript matching
* particular filter.
*
* If |filter| is not present this method throws NS_ERROR_INVALID_ARG.
*
* @param filter Object representing the filter to remove. Must be the exact
* object passed to addFilter, not just a new object with the same
* properties.
*/
void removeFilter (in jsdIFilter filter);
/**
* Swaps the positions of the filters passed in.
*
* If |filter_a| is not present, this method throws NS_ERROR_INVALID_ARG.
* If |filter_b| is not present, filter_a is replaced by filter_b.
* If |filter_a| == |filter_b|, then the glob and urlPattern properties of
* the filter are reread.
*/
void swapFilters (in jsdIFilter filter_a, in jsdIFilter filter_b);
/**
* Enumerate all active filters. This routine refreshes each filter before
* passing them on to the enumeration function. Calling this with a null
* |enumerator| is equivilant to jsdIService::refreshFilters.
*
* @param enumerator jsdIFilterEnumerator instance to be called back for the
* enumeration.
*/
void enumerateFilters (in jsdIFilterEnumerator enumerator);
/**
* Force the debugger to resync its internal filter cache with the
* actual values in the jsdIFilter objects. To refresh a single filter,
* see jsdIService::swapFilters. This method is equivilant to
* jsdIService::enumerateFilters with a null enumerator.
*/
void refreshFilters ();
/**
* Causes the debugger service to clear its list of filters.
*/
void clearFilters();
/**
* Enumerate all scripts the debugger knows about. Any scripts created
* before you turned the debugger on, or after turning the debugger off
@ -165,16 +237,97 @@ interface jsdIDebuggerService : nsISupports
*/
void clearAllBreakpoints ();
/* XXX these two routines are candidates for refactoring. The only problem
* is that it is not clear where and how they should land.
*/
/**
* XXX temporary hacks
* These routines are used to nest and unnest UI and network event loops.
* Push a new network queue, and enter a new UI event loop.
* @param callback jsdINestCallback instance to be called back after the
* network queue has been pushed, but before the
* UI loop starts.
* @return depth returns the current number of times the event loop has been
* nested. your code can use it for sanity checks.
*/
unsigned long enterNestedEventLoop (in jsdINestCallback callback);
/**
* Exit the current nested event loop after the current iteration completes,
* and pop the network event queue.
*
* @return depth returns the current number of times the event loop has been
* nested. your code can use it for sanity checks.
*/
unsigned long exitNestedEventLoop ();
};
/* callback interfaces */
/**
* Object representing a pattern of global object and/or url the debugger should
* ignore. The debugger service itself will not modify properties of these
* objects.
*/
[scriptable, uuid(05593438-1b83-4517-864f-3cea3d37a266)]
interface jsdIFilter : nsISupports
{
/**
* These two bytes of the flags attribute are reserved for interpretation
* by the jsdService implementation. You can do what you like with the
* remaining flags.
*/
const unsigned long FLAG_RESERVED_MASK = 0xFF;
/**
* Filters without this flag set are ignored.
*/
const unsigned long FLAG_ENABLED = 0x01;
/**
* Filters with this flag set are "pass" filters, they allow matching hooks
* to continue. Filters without this flag block matching hooks.
*/
const unsigned long FLAG_PASS = 0x02;
/**
* FLAG_* values from above, OR'd together.
*/
attribute unsigned long flags;
/**
* An nsISupports version of the global object to be filtered. A null glob
* matches all hooks. This attribute must be QI'able to the
* (non-scriptable) nsIScriptGlobalObject interface.
*
* The jsdIService caches this value internally, to if it changes you must
* swap the filter with itself using jsdIService::swapFilters.
*/
attribute nsISupports glob;
/**
* String representing the url pattern to be filtered. Supports limited
* glob matching, at the beginning and end of the pattern only. For example,
* "chrome://venkman*" filters all urls that start with chrome/venkman,
* "*.cgi" filters all cgi's, and "http://myserver/utils.js" filters only
* the utils.js file on "myserver". A null urlPattern matches all urls.
*
* The jsdIService caches this value internally, to if it changes you must
* swap the filter with itself using jsdIService::swapFilters.
*/
attribute string urlPattern;
/**
* Line number for the start of this filter. Line numbers are one based.
* Assigning a 0 to this attribute will tell the debugger to ignore the
* entire file.
*/
attribute unsigned long startLine;
/**
* Line number for the end of this filter. Line numbers are one based.
* Assigning a 0 to this attribute will tell the debugger to ignore from
* |startLine| to the end of the file.
*/
attribute unsigned long endLine;
};
/**
* Pass an instance of one of these to jsdIDebuggerService::enterNestedEventLoop.
*/
@ -189,6 +342,19 @@ interface jsdINestCallback : nsISupports
void onNest ();
};
/**
* Pass an instance of one of these to jsdIDebuggerService::enumerateFilters.
*/
[scriptable, uuid(54382875-ed12-4f90-9a63-1f0498d0a3f2)]
interface jsdIFilterEnumerator : nsISupports
{
/**
* The enumerateFilter method will be called once for every filter the
* debugger knows about.
*/
void enumerateFilter (in jsdIFilter filter);
};
/**
* Pass an instance of one of these to jsdIDebuggerService::enumerateScripts.
*/
@ -199,7 +365,7 @@ interface jsdIScriptEnumerator : nsISupports
* The enumerateScript method will be called once for every script the
* debugger knows about.
*/
boolean enumerateScript (in jsdIScript script);
void enumerateScript (in jsdIScript script);
};
/**
@ -427,7 +593,12 @@ interface jsdIScript : jsdIEphemeral
/**
* Get the first PC associated with a line.
*/
jsdIPC lineToPc (in unsigned long line);
jsdIPC lineToPc (in unsigned long line);
/**
* Determine is a particular line is executable, like checking that
* lineToPc == pcToLine, except in one call.
*/
boolean isLineExecutable (in unsigned long line);
/**
* Set a breakpoint at a PC in this script.
*/

View File

@ -586,6 +586,9 @@ jsd_GetCountOfStackFrames(JSDContext* jsdc, JSDThreadState* jsdthreadstate);
extern JSDStackFrameInfo*
jsd_GetStackFrame(JSDContext* jsdc, JSDThreadState* jsdthreadstate);
extern JSContext*
jsd_GetJSContext(JSDContext* jsdc, JSDThreadState* jsdthreadstate);
extern JSDStackFrameInfo*
jsd_GetCallingStackFrame(JSDContext* jsdc,
JSDThreadState* jsdthreadstate,

View File

@ -56,6 +56,15 @@ jsd_InterruptHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rva
if( JSD_IS_DANGEROUS_THREAD(jsdc) )
return JSTRAP_CONTINUE;
/* local in case jsdc->interruptHook gets cleared on another thread */
JSD_LOCK();
hook = jsdc->interruptHook;
hookData = jsdc->interruptHookData;
JSD_UNLOCK();
if (!hook)
return JSTRAP_CONTINUE;
JSD_LOCK_SCRIPTS(jsdc);
jsdscript = jsd_FindJSDScript(jsdc, script);
JSD_UNLOCK_SCRIPTS(jsdc);
@ -67,12 +76,6 @@ jsd_InterruptHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rva
return JSTRAP_CONTINUE;
#endif
/* local in case jsdc->interruptHook gets cleared on another thread */
JSD_LOCK();
hook = jsdc->interruptHook;
hookData = jsdc->interruptHookData;
JSD_UNLOCK();
return jsd_CallExecutionHook(jsdc, cx, JSD_HOOK_INTERRUPTED,
hook, hookData, rval);
}
@ -92,17 +95,19 @@ jsd_DebuggerHandler(JSContext *cx, JSScript *script, jsbytecode *pc,
if( JSD_IS_DANGEROUS_THREAD(jsdc) )
return JSTRAP_CONTINUE;
JSD_LOCK_SCRIPTS(jsdc);
jsdscript = jsd_FindJSDScript(jsdc, script);
JSD_UNLOCK_SCRIPTS(jsdc);
if( ! jsdscript )
return JSTRAP_CONTINUE;
/* local in case jsdc->debuggerHook gets cleared on another thread */
JSD_LOCK();
hook = jsdc->debuggerHook;
hookData = jsdc->debuggerHookData;
JSD_UNLOCK();
if(!hook)
return JSTRAP_CONTINUE;
JSD_LOCK_SCRIPTS(jsdc);
jsdscript = jsd_FindJSDScript(jsdc, script);
JSD_UNLOCK_SCRIPTS(jsdc);
if( ! jsdscript )
return JSTRAP_CONTINUE;
return jsd_CallExecutionHook(jsdc, cx, JSD_HOOK_DEBUGGER_KEYWORD,
hook, hookData, rval);
@ -118,25 +123,27 @@ jsd_ThrowHandler(JSContext *cx, JSScript *script, jsbytecode *pc,
JSD_ExecutionHookProc hook;
void* hookData;
JS_GetPendingException(cx, rval);
if( ! jsdc || ! jsdc->inited )
return JSD_HOOK_RETURN_CONTINUE_THROW;
if( JSD_IS_DANGEROUS_THREAD(jsdc) )
return JSD_HOOK_RETURN_CONTINUE_THROW;
/* local in case jsdc->throwHook gets cleared on another thread */
JSD_LOCK();
hook = jsdc->throwHook;
hookData = jsdc->throwHookData;
JSD_UNLOCK();
if (!hook)
return JSD_HOOK_RETURN_CONTINUE_THROW;
JSD_LOCK_SCRIPTS(jsdc);
jsdscript = jsd_FindJSDScript(jsdc, script);
JSD_UNLOCK_SCRIPTS(jsdc);
if( ! jsdscript )
return JSD_HOOK_RETURN_CONTINUE_THROW;
/* local in case jsdc->throwHook gets cleared on another thread */
JSD_LOCK();
hook = jsdc->throwHook;
hookData = jsdc->throwHookData;
JSD_UNLOCK();
JS_GetPendingException(cx, rval);
return jsd_CallExecutionHook(jsdc, cx, JSD_HOOK_THROW,
hook, hookData, rval);

View File

@ -95,7 +95,6 @@ _destroyFrame(JSDStackFrameInfo* jsdframe)
free(jsdframe);
}
JSDThreadState*
jsd_NewThreadState(JSDContext* jsdc, JSContext *cx )
{
@ -188,6 +187,19 @@ jsd_GetStackFrame(JSDContext* jsdc, JSDThreadState* jsdthreadstate)
return jsdframe;
}
JSContext *
jsd_GetJSContext (JSDContext* jsdc, JSDThreadState* jsdthreadstate)
{
JSContext* cx = NULL;
JSD_LOCK_THREADSTATES(jsdc);
if( jsd_IsValidThreadState(jsdc, jsdthreadstate) )
cx = jsdthreadstate->context;
JSD_UNLOCK_THREADSTATES(jsdc);
return cx;
}
JSDStackFrameInfo*
jsd_GetCallingStackFrame(JSDContext* jsdc,
JSDThreadState* jsdthreadstate,

View File

@ -136,7 +136,7 @@ _callHook(JSDContext *jsdc, JSContext *cx, JSStackFrame *fp, JSBool before,
}
else
{
hookresult = JS_FALSE;
hookresult = JS_TRUE;
}
#ifdef JSD_TRACE

View File

@ -39,6 +39,11 @@
#include "nsIXPConnect.h"
#include "nsIGenericFactory.h"
#include "nsIServiceManager.h"
#include "nsIScriptGlobalObject.h"
#include "nsIObserver.h"
#include "nsIObserverService.h"
#include "nsICategoryManager.h"
#include "nsIJSRuntimeService.h"
#include "nsIEventQueueService.h"
#include "nsMemory.h"
#include "jsdebug.h"
@ -76,6 +81,21 @@
{0xa3, 0x47, 0xee, 0x6b, 0x76, 0x60, 0xe0, 0x48} \
}
#define JSDASO_CID \
{ /* 2fd6b7f6-eb8c-4f32-ad26-113f2c02d0fe */ \
0x2fd6b7f6, \
0xeb8c, \
0x4f32, \
{0xad, 0x26, 0x11, 0x3f, 0x2c, 0x02, 0xd0, 0xfe} \
}
#define NS_CATMAN_CTRID "@mozilla.org/categorymanager;1"
#define NS_JSRT_CTRID "@mozilla.org/js/xpc/RuntimeService;1"
#define AUTOREG_CATEGORY "xpcom-autoregistration"
#define APPSTART_CATEGORY "app-startup"
#define JSD_STARTUP_ENTRY "JSDebugger Startup Observer"
JS_STATIC_DLL_CALLBACK (JSBool)
jsds_GCCallbackProc (JSContext *cx, JSGCStatus status);
@ -87,7 +107,7 @@ static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID);
const char jsdServiceCtrID[] = "@mozilla.org/js/jsd/debugger-service;1";
const char jsdASObserverCtrID[] = "@mozilla.org/js/jsd/app-start-observer;1";
const char jsdASObserverCtrID[] = "@mozilla.org/js/jsd/app-start-observer;2";
#ifdef DEBUG_verbose
PRUint32 gScriptCount = 0;
@ -105,6 +125,25 @@ static struct DeadScript {
jsdIScript *script;
} *gDeadScripts = nsnull;
enum PatternType {
ptIgnore = 0U,
ptStartsWith = 1U,
ptEndsWith = 2U,
ptContains = 3U,
ptEquals = 4U
};
static struct FilterRecord {
PRCList links;
jsdIFilter *filterObject;
void *glob;
char *urlPattern;
PRUint32 patternLength;
PatternType patternType;
PRUint32 startLine;
PRUint32 endLine;
} *gFilters = nsnull;
static struct LiveEphemeral *gLiveValues = nsnull, *gLiveProperties = nsnull;
/*******************************************************************************
@ -164,6 +203,218 @@ jsds_RemoveEphemeral (LiveEphemeral **listHead, LiveEphemeral *item)
PR_REMOVE_AND_INIT_LINK(&item->links);
}
/*******************************************************************************
* utility functions for filters
*******************************************************************************/
void
jsds_FreeFilter (FilterRecord *filter)
{
NS_IF_RELEASE (filter->filterObject);
if (filter->urlPattern)
nsMemory::Free(filter->urlPattern);
PR_Free (filter);
}
/* copies appropriate |filter| attributes into |rec|.
* False return indicates failure, the contents of |rec| will not be changed.
*/
PRBool
jsds_SyncFilter (FilterRecord *rec, jsdIFilter *filter)
{
NS_ASSERTION (rec, "jsds_SyncFilter without rec");
NS_ASSERTION (filter, "jsds_SyncFilter without filter");
JSObject *glob_proper = nsnull;
nsCOMPtr<nsISupports> glob;
nsresult rv = filter->GetGlob(getter_AddRefs(glob));
if (NS_FAILED(rv))
return PR_FALSE;
if (glob) {
nsCOMPtr<nsIScriptGlobalObject> nsiglob = do_QueryInterface(glob);
if (nsiglob)
glob_proper = nsiglob->GetGlobalJSObject();
}
PRUint32 startLine;
rv = filter->GetStartLine(&startLine);
if (NS_FAILED(rv))
return PR_FALSE;
PRUint32 endLine;
rv = filter->GetStartLine(&endLine);
if (NS_FAILED(rv))
return PR_FALSE;
char *urlPattern;
rv = filter->GetUrlPattern (&urlPattern);
if (NS_FAILED(rv))
return PR_FALSE;
if (urlPattern) {
PRUint32 len = PL_strlen(urlPattern);
if (urlPattern[0] == '*') {
/* pattern starts with a *, shift all chars once to the left,
* including the trailing null. */
memmove (&urlPattern[0], &urlPattern[1], len);
if (urlPattern[len - 2] == '*') {
/* pattern is in the format "*foo*", overwrite the final * with
* a null. */
urlPattern[len - 2] = '\0';
rec->patternType = ptContains;
rec->patternLength = len - 2;
} else {
/* pattern is in the format "*foo", just make a note of the
* new length. */
rec->patternType = ptEndsWith;
rec->patternLength = len - 1;
}
} else if (urlPattern[len - 1] == '*') {
/* pattern is in the format "foo*", overwrite the final * with a
* null. */
urlPattern[len - 1] = '\0';
rec->patternType = ptStartsWith;
rec->patternLength = len - 1;
} else {
/* pattern is in the format "foo". */
rec->patternType = ptEquals;
rec->patternLength = len;
}
} else {
rec->patternType = ptIgnore;
rec->patternLength = 0;
}
/* we got everything we need without failing, now copy it into rec. */
if (rec->filterObject != filter) {
NS_IF_RELEASE(rec->filterObject);
NS_ADDREF(filter);
rec->filterObject = filter;
}
rec->glob = glob_proper;
rec->startLine = startLine;
rec->endLine = endLine;
if (rec->urlPattern)
nsMemory::Free (rec->urlPattern);
rec->urlPattern = urlPattern;
return PR_TRUE;
}
FilterRecord *
jsds_FindFilter (jsdIFilter *filter)
{
if (!gFilters)
return nsnull;
FilterRecord *current = gFilters;
do {
if (current->filterObject == filter)
return current;
current = NS_REINTERPRET_CAST(FilterRecord *,
PR_NEXT_LINK(&current->links));
} while (current != gFilters);
return nsnull;
}
/* returns true if the hook should be executed. */
PRBool
jsds_FilterHook (JSDContext *jsdc, JSDThreadState *state)
{
if (!gFilters)
return PR_TRUE;
JSContext *cx = JSD_GetJSContext (jsdc, state);
void *glob = NS_STATIC_CAST(void *, JS_GetGlobalObject (cx));
if (NS_WARN_IF_FALSE(glob, "No global in threadstate"))
return PR_TRUE;
JSDStackFrameInfo *frame = JSD_GetStackFrame (jsdc, state);
if (NS_WARN_IF_FALSE(frame, "No frame in threadstate"))
return PR_TRUE;
JSDScript *script = JSD_GetScriptForStackFrame (jsdc, state, frame);
if (NS_WARN_IF_FALSE(script, "No script in threadstate"))
return PR_TRUE;
jsuint pc = JSD_GetPCForStackFrame (jsdc, state, frame);
if (NS_WARN_IF_FALSE(pc, "No pc in threadstate"))
return PR_TRUE;
PRUint32 currentLine = JSD_GetClosestLine (jsdc, script, pc);
if (NS_WARN_IF_FALSE(currentLine, "Can't convert pc to line"))
return PR_TRUE;
const char *url = nsnull;
PRUint32 len = 0;
FilterRecord *currentFilter = gFilters;
do {
PRUint32 flags = 0;
nsresult rv = currentFilter->filterObject->GetFlags(&flags);
NS_ASSERTION(NS_SUCCEEDED(rv), "Error getting flags for filter");
if (flags & jsdIFilter::FLAG_ENABLED) {
/* if there is no glob, or the globs match */
if ((!currentFilter->glob || currentFilter->glob == glob) &&
/* and there is no start line, or the start line is before
* or equal to the current */
(!currentFilter->startLine ||
currentFilter->startLine <= currentLine) &&
/* and there is no end line, or the end line is after
* or equal to the current */
(!currentFilter->endLine ||
currentFilter->endLine >= currentLine)) {
/* then we're going to have to compare the url. */
if (currentFilter->patternType == ptIgnore)
return flags & jsdIFilter::FLAG_PASS;
if (!url) {
url = JSD_GetScriptFilename (jsdc, script);
NS_ASSERTION (url, "Script with no filename");
len = PL_strlen(url);
}
if (len >= currentFilter->patternLength) {
switch (currentFilter->patternType) {
case ptEquals:
if (!PL_strcmp(currentFilter->urlPattern, url))
return flags & jsdIFilter::FLAG_PASS;
break;
case ptStartsWith:
if (!PL_strncmp(currentFilter->urlPattern, url,
currentFilter->patternLength))
return flags & jsdIFilter::FLAG_PASS;
break;
case ptEndsWith:
if (!PL_strcmp(currentFilter->urlPattern,
&url[len -
currentFilter->patternLength]))
return flags & jsdIFilter::FLAG_PASS;
break;
case ptContains:
if (PL_strstr(url, currentFilter->urlPattern))
return flags & jsdIFilter::FLAG_PASS;
break;
default:
NS_ASSERTION(0, "Invalid pattern type");
}
}
}
}
currentFilter = NS_REINTERPRET_CAST(FilterRecord *,
PR_NEXT_LINK(&currentFilter->links));
} while (currentFilter != gFilters);
return PR_TRUE;
}
/*******************************************************************************
* c callbacks
*******************************************************************************/
@ -179,6 +430,7 @@ jsds_NotifyPendingDeadScripts (JSContext *cx)
#ifdef CAUTIOUS_SCRIPTHOOK
JSRuntime *rt = JS_GetRuntime(cx);
#endif
gJsds->Pause(nsnull);
do {
ds = gDeadScripts;
@ -201,6 +453,7 @@ jsds_NotifyPendingDeadScripts (JSContext *cx)
PR_Free(ds);
} while (&gDeadScripts->links != &ds->links);
/* keep going until we catch up with our tail */
gJsds->UnPause(nsnull);
}
gDeadScripts = 0;
}
@ -209,7 +462,7 @@ JS_STATIC_DLL_CALLBACK (JSBool)
jsds_GCCallbackProc (JSContext *cx, JSGCStatus status)
{
gGCStatus = status;
#ifdef DEBUG
#ifdef DEBUG_verbose
printf ("new gc status is %i\n", status);
#endif
if (status == JSGC_END && gDeadScripts)
@ -221,16 +474,6 @@ jsds_GCCallbackProc (JSContext *cx, JSGCStatus status)
return JS_TRUE;
}
JS_STATIC_DLL_CALLBACK (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;
}
JS_STATIC_DLL_CALLBACK (JSBool)
jsds_CallHookProc (JSDContext* jsdc, JSDThreadState* jsdthreadstate,
uintN type, void* callerdata)
@ -256,11 +499,16 @@ jsds_CallHookProc (JSDContext* jsdc, JSDThreadState* jsdthreadstate,
if (!hook)
return JS_TRUE;
if (!jsds_FilterHook (jsdc, jsdthreadstate))
return JS_FALSE;
JSDStackFrameInfo *native_frame = JSD_GetStackFrame (jsdc, jsdthreadstate);
nsCOMPtr<jsdIStackFrame> frame =
getter_AddRefs(jsdStackFrame::FromPtr(jsdc, jsdthreadstate,
native_frame));
gJsds->Pause(nsnull);
hook->OnCall(frame, type);
gJsds->UnPause(nsnull);
frame->Invalidate();
return JS_TRUE;
@ -305,11 +553,16 @@ jsds_ExecutionHookProc (JSDContext* jsdc, JSDThreadState* jsdthreadstate,
if (!hook)
return hook_rv;
if (!jsds_FilterHook (jsdc, jsdthreadstate))
return JSD_HOOK_RETURN_CONTINUE;
JSDStackFrameInfo *native_frame = JSD_GetStackFrame (jsdc, jsdthreadstate);
nsCOMPtr<jsdIStackFrame> frame =
getter_AddRefs(jsdStackFrame::FromPtr(jsdc, jsdthreadstate,
native_frame));
gJsds->Pause(nsnull);
hook->OnExecute (frame, type, &js_rv, &hook_rv);
gJsds->UnPause(nsnull);
frame->Invalidate();
if (hook_rv == JSD_HOOK_RETURN_RET_WITH_VAL ||
@ -345,7 +598,9 @@ jsds_ScriptHookProc (JSDContext* jsdc, JSDScript* jsdscript, JSBool creating,
#ifdef CAUTIOUS_SCRIPTHOOK
JS_DISABLE_GC(rt);
#endif
gJsds->Pause(nsnull);
hook->OnScriptCreated (script);
gJsds->UnPause(nsnull);
#ifdef CAUTIOUS_SCRIPTHOOK
JS_ENABLE_GC(rt);
#endif
@ -364,7 +619,10 @@ jsds_ScriptHookProc (JSDContext* jsdc, JSDScript* jsdscript, JSBool creating,
#ifdef CAUTIOUS_SCRIPTHOOK
JS_DISABLE_GC(rt);
#endif
gJsds->Pause(nsnull);
hook->OnScriptDestroyed (jsdis);
gJsds->UnPause(nsnull);
#ifdef CAUTIOUS_SCRIPTHOOK
JS_ENABLE_GC(rt);
#endif
@ -589,8 +847,7 @@ jsdScript::jsdScript (JSDContext *aCx, JSDScript *aScript) : mValid(PR_FALSE),
DEBUG_CREATE ("jsdScript", gScriptCount);
NS_INIT_ISUPPORTS();
if (mScript)
{
if (mScript) {
/* copy the script's information now, so we have it later, when it
* gets destroyed. */
JSD_LockScriptSubsystem(mCx);
@ -705,6 +962,15 @@ jsdScript::LineToPc(PRUint32 aLine, jsdIPC **_rval)
return NS_OK;
}
NS_IMETHODIMP
jsdScript::IsLineExecutable(PRUint32 aLine, PRBool *_rval)
{
ASSERT_VALID_SCRIPT;
jsuword pc = JSD_GetClosestPC (mCx, mScript, aLine);
*_rval = (aLine == JSD_GetClosestLine (mCx, mScript, pc));
return NS_OK;
}
NS_IMETHODIMP
jsdScript::SetBreakpoint(jsdIPC *aPC)
{
@ -1033,7 +1299,8 @@ NS_IMETHODIMP
jsdValue::GetJsFunctionName(char **_rval)
{
ASSERT_VALID_VALUE;
*_rval = ToNewCString(nsDependentCString(JSD_GetValueFunctionName(mCx, mValue)));
*_rval =
ToNewCString(nsDependentCString(JSD_GetValueFunctionName(mCx, mValue)));
return NS_OK;
}
@ -1157,6 +1424,109 @@ jsdValue::Refresh()
******************************************************************************/
NS_IMPL_THREADSAFE_ISUPPORTS1(jsdService, jsdIDebuggerService);
NS_IMETHODIMP
jsdService::GetInitAtStartup (PRBool *_rval)
{
nsresult rv;
nsCOMPtr<nsICategoryManager>
categoryManager(do_GetService(NS_CATMAN_CTRID, &rv));
if (NS_FAILED(rv))
return rv;
if (mInitAtStartup == triUnknown) {
nsXPIDLCString notused;
nsresult autoreg_rv, appstart_rv;
autoreg_rv = categoryManager->GetCategoryEntry(AUTOREG_CATEGORY,
JSD_STARTUP_ENTRY,
getter_Copies(notused));
appstart_rv = categoryManager->GetCategoryEntry(APPSTART_CATEGORY,
JSD_STARTUP_ENTRY,
getter_Copies(notused));
if (autoreg_rv != appstart_rv) {
/* we have an inconsistent state in the registry, attempt to fix.
* we need to make mInitAtStartup disagree with the state passed
* to SetInitAtStartup to make it actually do something.
*/
mInitAtStartup = triYes;
rv = SetInitAtStartup (PR_FALSE);
if (NS_FAILED(rv))
return rv;
} else if (autoreg_rv == NS_ERROR_NOT_AVAILABLE) {
mInitAtStartup = triNo;
} else if (NS_SUCCEEDED(autoreg_rv)) {
mInitAtStartup = triYes;
} else {
return rv;
}
}
if (_rval)
*_rval = (mInitAtStartup == triYes);
return NS_OK;
}
/*
* The initAtStartup property controls whether or not we register the
* app start observer (jsdASObserver.) We register for both
* "xpcom-autoregistration" and "app-startup" notifications if |state| is true.
* the autoreg message is sent just before registration occurs (before
* "app-startup".) We care about autoreg because it may load javascript
* components. autoreg does *not* fire if components haven't changed since the
* last autoreg, so we watch "app-startup" as a fallback.
*/
NS_IMETHODIMP
jsdService::SetInitAtStartup (PRBool state)
{
nsresult rv;
if (mInitAtStartup == triUnknown) {
/* side effect sets mInitAtStartup */
rv = GetInitAtStartup(nsnull);
if (NS_FAILED(rv))
return rv;
}
if (state && mInitAtStartup == triYes ||
!state && mInitAtStartup == triNo) {
/* already in the requested state */
return NS_OK;
}
nsCOMPtr<nsICategoryManager>
categoryManager(do_GetService(NS_CATMAN_CTRID, &rv));
if (state) {
rv = categoryManager->AddCategoryEntry(AUTOREG_CATEGORY,
JSD_STARTUP_ENTRY,
jsdASObserverCtrID,
PR_TRUE, PR_TRUE, nsnull);
if (NS_FAILED(rv))
return rv;
rv = categoryManager->AddCategoryEntry(APPSTART_CATEGORY,
JSD_STARTUP_ENTRY,
jsdASObserverCtrID,
PR_TRUE, PR_TRUE, nsnull);
if (NS_FAILED(rv))
return rv;
mInitAtStartup = triYes;
} else {
rv = categoryManager->DeleteCategoryEntry(AUTOREG_CATEGORY,
JSD_STARTUP_ENTRY, PR_TRUE);
if (NS_FAILED(rv))
return rv;
rv = categoryManager->DeleteCategoryEntry(APPSTART_CATEGORY,
JSD_STARTUP_ENTRY, PR_TRUE);
if (NS_FAILED(rv))
return rv;
mInitAtStartup = triNo;
}
return NS_OK;
}
NS_IMETHODIMP
jsdService::GetIsOn (PRBool *_rval)
{
@ -1228,23 +1598,29 @@ jsdService::OnForRuntime (JSRuntime *rt)
if (mTopLevelHook)
JSD_SetTopLevelHook (mCx, jsds_CallHookProc, NULL);
else
JSD_SetTopLevelHook (mCx, jsds_EmptyCallHookProc, NULL);
JSD_ClearTopLevelHook (mCx);
if (mFunctionHook)
JSD_SetFunctionHook (mCx, jsds_CallHookProc, NULL);
else
JSD_SetFunctionHook (mCx, jsds_EmptyCallHookProc, NULL);
JSD_ClearFunctionHook (mCx);
mOn = PR_TRUE;
#ifdef DEBUG
printf ("+++ JavaScript debuging hooks installed.\n");
#endif
return NS_OK;
}
NS_IMETHODIMP
jsdService::Off (void)
{
if (!mOn)
return NS_OK;
if (!mCx || !mRuntime)
return NS_ERROR_NOT_INITIALIZED;
if (gDeadScripts)
if (gDeadScripts) {
if (gGCStatus == JSGC_END)
{
JSContext *cx = JSD_GetDefaultJSContext(mCx);
@ -1252,6 +1628,7 @@ jsdService::Off (void)
}
else
return NS_ERROR_NOT_AVAILABLE;
}
/*
if (gLastGCProc != jsds_GCCallbackProc)
@ -1262,19 +1639,73 @@ jsdService::Off (void)
jsdProperty::InvalidateAll();
ClearAllBreakpoints();
JSD_SetThrowHook (mCx, NULL, NULL);
JSD_ClearThrowHook (mCx);
JSD_SetScriptHook (mCx, NULL, NULL);
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_ClearInterruptHook (mCx);
JSD_ClearDebuggerHook (mCx);
JSD_ClearDebugBreakHook (mCx);
JSD_ClearTopLevelHook (mCx);
JSD_ClearFunctionHook (mCx);
JSD_DebuggerOff (mCx);
mCx = nsnull;
mRuntime = nsnull;
mOn = PR_FALSE;
#ifdef DEBUG
printf ("+++ JavaScript debuging hooks removed.\n");
#endif
return NS_OK;
}
NS_IMETHODIMP
jsdService::Pause(PRUint32 *_rval)
{
if (++mPauseLevel == 1) {
JSD_ClearThrowHook (mCx);
JSD_ClearInterruptHook (mCx);
JSD_ClearDebuggerHook (mCx);
JSD_ClearDebugBreakHook (mCx);
JSD_ClearTopLevelHook (mCx);
JSD_ClearFunctionHook (mCx);
}
if (_rval)
*_rval = mPauseLevel;
return NS_OK;
}
NS_IMETHODIMP
jsdService::UnPause(PRUint32 *_rval)
{
if (mPauseLevel == 0)
return NS_ERROR_NOT_AVAILABLE;
if (--mPauseLevel == 0) {
if (mThrowHook)
JSD_SetThrowHook (mCx, jsds_ExecutionHookProc, NULL);
if (mInterruptHook)
JSD_SetInterruptHook (mCx, jsds_ExecutionHookProc, NULL);
if (mDebuggerHook)
JSD_SetDebuggerHook (mCx, jsds_ExecutionHookProc, NULL);
if (mErrorHook)
JSD_SetDebugBreakHook (mCx, jsds_ExecutionHookProc, NULL);
if (mTopLevelHook)
JSD_SetTopLevelHook (mCx, jsds_CallHookProc, NULL);
else
JSD_ClearTopLevelHook (mCx);
if (mFunctionHook)
JSD_SetFunctionHook (mCx, jsds_CallHookProc, NULL);
else
JSD_ClearFunctionHook (mCx);
}
if (_rval)
*_rval = mPauseLevel;
return NS_OK;
}
@ -1285,14 +1716,12 @@ jsdService::EnumerateScripts (jsdIScriptEnumerator *enumerator)
JSDScript *script;
JSDScript *iter = NULL;
PRBool cont_flag;
nsresult rv = NS_OK;
JSD_LockScriptSubsystem(mCx);
while((script = JSD_IterateScripts(mCx, &iter)) != NULL) {
rv = enumerator->EnumerateScript (jsdScript::FromPtr(mCx, script),
&cont_flag);
if (NS_FAILED(rv) || !cont_flag)
rv = enumerator->EnumerateScript (jsdScript::FromPtr(mCx, script));
if (NS_FAILED(rv))
break;
}
JSD_UnlockScriptSubsystem(mCx);
@ -1310,6 +1739,171 @@ jsdService::GC (void)
return NS_OK;
}
NS_IMETHODIMP
jsdService::InsertFilter (jsdIFilter *filter, jsdIFilter *after)
{
NS_ENSURE_ARG_POINTER (filter);
if (jsds_FindFilter (filter))
return NS_ERROR_INVALID_ARG;
FilterRecord *rec = PR_NEWZAP (FilterRecord);
if (!jsds_SyncFilter (rec, filter)) {
PR_Free (rec);
return NS_ERROR_FAILURE;
}
if (gFilters) {
if (!after) {
/* insert at head of list */
PR_INSERT_LINK(&rec->links, &gFilters->links);
gFilters = rec;
} else {
/* insert somewhere in the list */
FilterRecord *afterRecord = jsds_FindFilter (after);
if (!afterRecord) {
jsds_FreeFilter(rec);
return NS_ERROR_INVALID_ARG;
}
PR_INSERT_AFTER(&rec->links, &afterRecord->links);
}
} else {
if (after) {
/* user asked to insert into the middle of an empty list, bail. */
jsds_FreeFilter(rec);
return NS_ERROR_NOT_INITIALIZED;
}
PR_INIT_CLIST(&rec->links);
gFilters = rec;
}
return NS_OK;
}
NS_IMETHODIMP
jsdService::AppendFilter (jsdIFilter *filter)
{
NS_ENSURE_ARG_POINTER (filter);
if (jsds_FindFilter (filter))
return NS_ERROR_INVALID_ARG;
FilterRecord *rec = PR_NEWZAP (FilterRecord);
if (!jsds_SyncFilter (rec, filter)) {
PR_Free (rec);
return NS_ERROR_FAILURE;
}
if (gFilters) {
PR_INSERT_BEFORE(&rec->links, &gFilters->links);
} else {
PR_INIT_CLIST(&rec->links);
gFilters = rec;
}
return NS_OK;
}
NS_IMETHODIMP
jsdService::RemoveFilter (jsdIFilter *filter)
{
NS_ENSURE_ARG_POINTER(filter);
FilterRecord *rec = jsds_FindFilter (filter);
if (!rec)
return NS_ERROR_INVALID_ARG;
if (gFilters == rec)
gFilters = NS_REINTERPRET_CAST(FilterRecord *,
PR_NEXT_LINK(&rec->links));
PR_REMOVE_LINK(&rec->links);
jsds_FreeFilter (rec);
return NS_OK;
}
NS_IMETHODIMP
jsdService::SwapFilters (jsdIFilter *filter_a, jsdIFilter *filter_b)
{
NS_ENSURE_ARG_POINTER(filter_a);
NS_ENSURE_ARG_POINTER(filter_b);
FilterRecord *rec_a = jsds_FindFilter (filter_a);
if (!rec_a)
return NS_ERROR_INVALID_ARG;
if (filter_a == filter_b) {
/* just a refresh */
if (!jsds_SyncFilter (rec_a, filter_a))
return NS_ERROR_FAILURE;
return NS_OK;
}
FilterRecord *rec_b = jsds_FindFilter (filter_b);
if (!rec_b) {
/* filter_b is not in the list, replace filter_a with filter_b. */
if (!jsds_SyncFilter (rec_a, filter_b))
return NS_ERROR_FAILURE;
} else {
/* both filters are in the list, swap. */
if (!jsds_SyncFilter (rec_a, filter_b))
return NS_ERROR_FAILURE;
if (!jsds_SyncFilter (rec_b, filter_a))
return NS_ERROR_FAILURE;
}
return NS_OK;
}
NS_IMETHODIMP
jsdService::EnumerateFilters (jsdIFilterEnumerator *enumerator)
{
if (!gFilters)
return NS_OK;
FilterRecord *current = gFilters;
do {
jsds_SyncFilter (current, current->filterObject);
/* SyncFilter failure would be bad, but what would we do about it? */
if (enumerator) {
nsresult rv = enumerator->EnumerateFilter (current->filterObject);
if (NS_FAILED(rv))
return rv;
}
current = NS_REINTERPRET_CAST(FilterRecord *,
PR_NEXT_LINK (&current->links));
} while (current != gFilters);
return NS_OK;
}
NS_IMETHODIMP
jsdService::RefreshFilters ()
{
return EnumerateFilters(nsnull);
}
NS_IMETHODIMP
jsdService::ClearFilters ()
{
if (!gFilters)
return NS_OK;
FilterRecord *current = NS_REINTERPRET_CAST(FilterRecord *,
PR_NEXT_LINK (&gFilters->links));
do {
FilterRecord *next = NS_REINTERPRET_CAST(FilterRecord *,
PR_NEXT_LINK (&current->links));
PR_REMOVE_AND_INIT_LINK(&current->links);
jsds_FreeFilter(current);
current = next;
} while (current != gFilters);
jsds_FreeFilter(current);
gFilters = nsnull;
return NS_OK;
}
NS_IMETHODIMP
jsdService::ClearAllBreakpoints (void)
{
@ -1342,9 +1936,10 @@ jsdService::EnterNestedEventLoop (jsdINestCallback *callback, PRUint32 *_rval)
if (stack && NS_SUCCEEDED(stack->Push(nsnull)) &&
NS_SUCCEEDED(eventService->PushThreadEventQueue(getter_AddRefs(eventQ))))
{
if (NS_SUCCEEDED(rv) && callback)
{
if (NS_SUCCEEDED(rv) && callback) {
Pause(nsnull);
rv = callback->OnNest();
UnPause(nsnull);
}
while(NS_SUCCEEDED(rv) && mNestedLoopLevel >= nestLevel)
@ -1355,10 +1950,8 @@ jsdService::EnterNestedEventLoop (jsdINestCallback *callback, PRUint32 *_rval)
rv = appShell->GetNativeEvent(isRealEvent, data);
if(NS_SUCCEEDED(rv))
{
appShell->DispatchNativeEvent(isRealEvent, data);
}
}
JSContext* cx;
stack->Pop(&cx);
NS_ASSERTION(cx == nsnull, "JSContextStack mismatch");
@ -1416,7 +2009,7 @@ jsdService::SetErrorHook (jsdIExecutionHook *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)
if (!mCx || mPauseLevel)
return NS_OK;
if (aHook)
@ -1444,7 +2037,7 @@ jsdService::SetDebuggerHook (jsdIExecutionHook *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)
if (!mCx || mPauseLevel)
return NS_OK;
if (aHook)
@ -1472,7 +2065,7 @@ jsdService::SetInterruptHook (jsdIExecutionHook *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)
if (!mCx || mPauseLevel)
return NS_OK;
if (aHook)
@ -1500,7 +2093,7 @@ jsdService::SetScriptHook (jsdIScriptHook *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)
if (!mCx || mPauseLevel)
return NS_OK;
if (aHook)
@ -1528,7 +2121,7 @@ jsdService::SetThrowHook (jsdIExecutionHook *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)
if (!mCx || mPauseLevel)
return NS_OK;
if (aHook)
@ -1556,13 +2149,13 @@ jsdService::SetTopLevelHook (jsdICallHook *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)
if (!mCx || mPauseLevel)
return NS_OK;
if (aHook)
JSD_SetTopLevelHook (mCx, jsds_CallHookProc, NULL);
else
JSD_SetTopLevelHook (mCx, jsds_EmptyCallHookProc, NULL);
JSD_ClearTopLevelHook (mCx);
return NS_OK;
}
@ -1584,13 +2177,13 @@ jsdService::SetFunctionHook (jsdICallHook *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)
if (!mCx || mPauseLevel)
return NS_OK;
if (aHook)
JSD_SetFunctionHook (mCx, jsds_CallHookProc, NULL);
else
JSD_SetFunctionHook (mCx, jsds_EmptyCallHookProc, NULL);
JSD_ClearFunctionHook (mCx);
return NS_OK;
}
@ -1616,8 +2209,55 @@ jsdService::GetService ()
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(jsdService, jsdService::GetService);
/* app-start observer. turns on the debugger at app-start. this is inserted
* and/or removed from the app-start category by the jsdService::initAtStartup
* property.
*/
class jsdASObserver : public nsIObserver
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
jsdASObserver ()
{
NS_INIT_ISUPPORTS();
}
};
NS_IMPL_THREADSAFE_ISUPPORTS1(jsdASObserver, nsIObserver);
NS_IMETHODIMP
jsdASObserver::Observe (nsISupports *aSubject, const char *aTopic,
const PRUnichar *aData)
{
nsresult rv;
jsdService *jsds = jsdService::GetService();
PRBool on;
rv = jsds->GetIsOn(&on);
if (NS_FAILED(rv) || on)
return rv;
nsCOMPtr<nsIJSRuntimeService> rts = do_GetService(NS_JSRT_CTRID, &rv);
if (NS_FAILED(rv))
return rv;
JSRuntime *rt;
rts->GetRuntime (&rt);
if (NS_FAILED(rv))
return rv;
rv = jsds->OnForRuntime(rt);
return rv;
}
NS_GENERIC_FACTORY_CONSTRUCTOR(jsdASObserver);
static nsModuleComponentInfo components[] = {
{"JSDService", JSDSERVICE_CID, jsdServiceCtrID, jsdServiceConstructor}
{"JSDService", JSDSERVICE_CID, jsdServiceCtrID, jsdServiceConstructor},
{"JSDASObserver", JSDASO_CID, jsdASObserverCtrID, jsdASObserverConstructor}
};
NS_IMPL_NSGETMODULE(JavaScript_Debugger, components);

View File

@ -285,20 +285,33 @@ class jsdService : public jsdIDebuggerService
NS_DECL_ISUPPORTS
NS_DECL_JSDIDEBUGGERSERVICE
jsdService() : mOn(PR_FALSE), mNestedLoopLevel(0), mCx(0), mRuntime(0),
mBreakpointHook(0), mErrorHook(0), mDebuggerHook(0),
mInterruptHook(0), mScriptHook(0), mThrowHook(0),
mTopLevelHook(0), mFunctionHook(0)
jsdService() : mInitAtStartup(triUnknown), mOn(PR_FALSE), mPauseLevel(0),
mNestedLoopLevel(0), mCx(0), mRuntime(0), mBreakpointHook(0),
mErrorHook(0), mDebuggerHook(0), mInterruptHook(0),
mScriptHook(0), mThrowHook(0), mTopLevelHook(0),
mFunctionHook(0)
{
NS_INIT_ISUPPORTS();
}
virtual ~jsdService() { }
virtual ~jsdService()
{
ClearFilters();
Off();
}
static jsdService *GetService ();
private:
enum Tristate {
triUnknown = 0U,
triYes = 1U,
triNo = 2U
};
Tristate mInitAtStartup;
PRBool mOn;
PRUint32 mPauseLevel;
PRUint32 mNestedLoopLevel;
JSDContext *mCx;
JSRuntime *mRuntime;

View File

@ -550,6 +550,13 @@ JSD_GetStackFrame(JSDContext* jsdc, JSDThreadState* jsdthreadstate)
return jsd_GetStackFrame(jsdc, jsdthreadstate);
}
JSD_PUBLIC_API(JSContext*)
JSD_GetJSContext(JSDContext* jsdc, JSDThreadState* jsdthreadstate)
{
JSD_ASSERT_VALID_CONTEXT(jsdc);
return jsd_GetJSContext(jsdc, jsdthreadstate);
}
JSD_PUBLIC_API(JSDStackFrameInfo*)
JSD_GetCallingStackFrame(JSDContext* jsdc,
JSDThreadState* jsdthreadstate,

View File

@ -723,6 +723,12 @@ JSD_GetCountOfStackFrames(JSDContext* jsdc, JSDThreadState* jsdthreadstate);
extern JSD_PUBLIC_API(JSDStackFrameInfo*)
JSD_GetStackFrame(JSDContext* jsdc, JSDThreadState* jsdthreadstate);
/*
* Get the JSContext for the given JSDThreadState
*/
extern JSD_PUBLIC_API(JSContext*)
JSD_GetJSContext(JSDContext* jsdc, JSDThreadState* jsdthreadstate);
/*
* Get the calling call stack frame for the given frame
*/

View File

@ -25,13 +25,15 @@
#//------------------------------------------------------------------------
DEPTH=..\..
REQUIRES = xpcom \
REQUIRES = js \
!if !defined(JSD_STANDALONE)
xpcom \
string \
js \
xpconnect \
widget \
appshell \
pref \
dom \
!endif
$(NULL)
include <$(DEPTH)\config\config.mak>