Index: jsd.h =================================================================== RCS file: /cvsroot/mozilla/js/jsd/jsd.h,v retrieving revision 3.19 diff -up -r3.19 jsd.h --- jsd.h +++ jsd.h @@ -804,6 +804,20 @@ extern void* _jsd_global_lock; (NULL != (jsdc->objectsLock = jsd_CreateLock())) && \ (NULL != (jsdc->threadStatesLock = jsd_CreateLock())) ) +#define JSD_RELEASE_LOCKS(jsdc) \ + JS_BEGIN_MACRO \ + if ( jsdc->scriptsLock ) \ + jsd_DestroyLock(jsdc->scriptsLock); \ + if ( jsdc->sourceTextLock ) \ + jsd_DestroyLock(jsdc->sourceTextLock); \ + if ( jsdc->atomsLock ) \ + jsd_DestroyLock(jsdc->atomsLock); \ + if ( jsdc->objectsLock ) \ + jsd_DestroyLock(jsdc->objectsLock); \ + if ( jsdc->threadStatesLock ) \ + jsd_DestroyLock(jsdc->threadStatesLock); \ + JS_END_MACRO + #define JSD_LOCK_SCRIPTS(jsdc) jsd_Lock(jsdc->scriptsLock) #define JSD_UNLOCK_SCRIPTS(jsdc) jsd_Unlock(jsdc->scriptsLock) @@ -903,10 +917,10 @@ extern void* _jsd_global_lock; /* Value and Property Functions */ extern JSDValue* -jsd_NewValue(JSDContext* jsdc, jsval val); +jsd_NewValue(JSDContext* jsdc, JSContext* cx, jsval val); extern void -jsd_DropValue(JSDContext* jsdc, JSDValue* jsdval); +jsd_DropValue(JSDContext* jsdc, JSContext* cx, JSDValue* jsdval); extern jsval jsd_GetValueWrappedJSVal(JSDContext* jsdc, JSDValue* jsdval); Index: jsd_high.c =================================================================== RCS file: /cvsroot/mozilla/js/jsd/jsd_high.c,v retrieving revision 3.12 diff -up -r3.12 jsd_high.c --- jsd_high.c +++ jsd_high.c @@ -83,6 +83,26 @@ _validateUserCallbacks(JSD_UserCallbacks (callbacks->size && callbacks->size <= sizeof(JSD_UserCallbacks)); } +static jsuword +GetStackLimit() +{ + // XXX improve me + + jsuword stackLimit; + + int stackDummy; + jsuword currentStackAddr = (jsuword)&stackDummy; + + // We assume here that the stack grows down, and that a stack + // limit of 512k below the current stack addr is ok. If this is + // not the case on some platforms, #ifdef's are needed for those + // platforms. + stackLimit = currentStackAddr + + (0x80000 * JS_STACK_GROWTH_DIRECTION); + + return stackLimit; +} + static JSDContext* _newJSDContext(JSRuntime* jsrt, JSD_UserCallbacks* callbacks, @@ -98,7 +118,7 @@ _newJSDContext(JSRuntime* jsrt, jsdc = (JSDContext*) calloc(1, sizeof(JSDContext)); if( ! jsdc ) - goto label_newJSDContext_failure; + return NULL; if( ! JSD_INIT_LOCKS(jsdc) ) goto label_newJSDContext_failure; @@ -135,12 +155,19 @@ _newJSDContext(JSRuntime* jsrt, if( ! jsdc->dumbContext ) goto label_newJSDContext_failure; + JS_SetThreadStackLimit(jsdc->dumbContext, GetStackLimit()); + + JS_BeginRequest(jsdc->dumbContext); + /* We do not have to JS_EndRequest if we discard the context + * (label_newJSDContext_failure) + */ jsdc->glob = JS_NewObject(jsdc->dumbContext, &global_class, NULL, NULL); if( ! jsdc->glob ) goto label_newJSDContext_failure; if( ! JS_InitStandardClasses(jsdc->dumbContext, jsdc->glob) ) goto label_newJSDContext_failure; + JS_EndRequest(jsdc->dumbContext); jsdc->data = NULL; jsdc->inited = JS_TRUE; @@ -154,8 +181,10 @@ _newJSDContext(JSRuntime* jsrt, label_newJSDContext_failure: jsd_DestroyObjectManager(jsdc); jsd_DestroyAtomTable(jsdc); - if( jsdc ) - free(jsdc); + if ( jsdc->dumbContext ) + JS_DestroyContext(jsdc->dumbContext); + JSD_RELEASE_LOCKS(jsdc); + free(jsdc); return NULL; } @@ -178,6 +207,7 @@ _destroyJSDContext(JSDContext* jsdc) * asynchronous hooks calling into the system using it as a handle * * XXX we also leak the locks + JSD_RELEASE_LOCKS(jsdc); */ JS_DestroyContext(jsdc->dumbContext); jsdc->dumbContext = NULL; Index: jsd_lock.c =================================================================== RCS file: /cvsroot/mozilla/js/jsd/jsd_lock.c,v retrieving revision 3.8 diff -up -r3.8 jsd_lock.c --- jsd_lock.c +++ jsd_lock.c @@ -103,7 +103,7 @@ void ASSERT_VALID_LOCK(JSDStaticLock* lo JS_ASSERT(lock); JS_ASSERT(lock->lock); JS_ASSERT(lock->count >= 0); - JS_ASSERT((! lock->count && ! lock->owner) || (lock->count && lock->owner)); + JS_ASSERT(lock->count ^ !lock->owner); JS_ASSERT(lock->sig == (uint16) JSD_LOCK_SIG); } #else @@ -115,20 +115,27 @@ jsd_CreateLock() { JSDStaticLock* lock; - if( ! (lock = calloc(1, sizeof(JSDStaticLock))) || - ! (lock->lock = PR_NewLock()) ) + if( ! (lock = calloc(1, sizeof(JSDStaticLock)))) + return NULL; + if( ! (lock->lock = PR_NewLock()) ) { - if(lock) - { - free(lock); - lock = NULL; - } + free(lock); + return NULL; } #ifdef DEBUG - if(lock) lock->sig = (uint16) JSD_LOCK_SIG; + lock->sig = (uint16) JSD_LOCK_SIG; #endif return lock; -} +} + +void +jsd_DestroyLock(void* aLock) +{ + JSDStaticLock* lock; + lock = (JSDStaticLock*) aLock; + PR_DestroyLock(lock->lock); + free(lock); +} void jsd_Lock(JSDStaticLock* lock) @@ -210,6 +217,11 @@ jsd_CreateLock() { return (void*)1; } + +void +jsd_DestroyLock(void*) +{ +} void jsd_Lock(void* lock) Index: jsd_lock.h =================================================================== RCS file: /cvsroot/mozilla/js/jsd/jsd_lock.h,v retrieving revision 3.7 diff -up -r3.7 jsd_lock.h --- jsd_lock.h +++ jsd_lock.h @@ -58,6 +58,9 @@ extern void* jsd_CreateLock(); extern void +jsd_DestroyLock(void* lock); + +extern void jsd_Lock(JSDStaticLock* lock); extern void Index: jsd_obj.c =================================================================== RCS file: /cvsroot/mozilla/js/jsd/jsd_obj.c,v retrieving revision 3.8 diff -up -r3.8 jsd_obj.c --- jsd_obj.c +++ jsd_obj.c @@ -242,7 +242,8 @@ jsd_DestroyObjectManager(JSDContext* jsd JSD_LOCK_OBJECTS(jsdc); while( !JS_CLIST_IS_EMPTY(&jsdc->objectsList) ) _destroyJSDObject(jsdc, (JSDObject*)JS_NEXT_LINK(&jsdc->objectsList)); - JS_HashTableDestroy(jsdc->objectsTable); + if ( jsdc->objectsTable ) + JS_HashTableDestroy(jsdc->objectsTable); JSD_UNLOCK_OBJECTS(jsdc); } @@ -323,7 +324,7 @@ jsd_GetObjectForValue(JSDContext* jsdc, JSDValue* jsd_GetValueForObject(JSDContext* jsdc, JSDObject* jsdobj) { - return jsd_NewValue(jsdc, OBJECT_TO_JSVAL(jsdobj->obj)); + return jsd_NewValue(jsdc, jsdc->dumbContext, OBJECT_TO_JSVAL(jsdobj->obj)); } Index: jsd_scpt.c =================================================================== RCS file: /cvsroot/mozilla/js/jsd/jsd_scpt.c,v retrieving revision 3.14 diff -up -r3.14 jsd_scpt.c --- jsd_scpt.c +++ jsd_scpt.c @@ -101,8 +101,10 @@ _newJSDScript(JSDContext* jsdc, /* these are inlined javascript: urls and we can't handle them now */ lineno = (uintN) JS_GetScriptBaseLineNumber(cx, script); +#if 0 if( lineno == 0 ) return NULL; +#endif jsdscript = (JSDScript*) calloc(1, sizeof(JSDScript)); if( ! jsdscript ) @@ -691,6 +693,7 @@ _isActiveHook(JSDContext* jsdc, JSScript return JS_FALSE; } +#define DANGEROUS_TRAPPING 1 JSTrapStatus JS_DLL_CALLBACK jsd_TrapHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval, @@ -705,7 +708,11 @@ jsd_TrapHandler(JSContext *cx, JSScript JSD_LOCK(); if( NULL == (jsdc = jsd_JSDContextForJSContext(cx)) || +#if !defined(DANGEROUS_TRAPPING) || !DANGEROUS_TRAPPING ! _isActiveHook(jsdc, script, jsdhook) ) +#else + 0) +#endif { JSD_UNLOCK(); return JSTRAP_CONTINUE; Index: jsd_stak.c =================================================================== RCS file: /cvsroot/mozilla/js/jsd/jsd_stak.c,v retrieving revision 3.21 diff -up -r3.21 jsd_stak.c --- jsd_stak.c +++ jsd_stak.c @@ -138,8 +138,10 @@ jsd_NewThreadState(JSDContext* jsdc, JSC frame = _addNewFrame( jsdc, jsdthreadstate, script, pc, fp ); - if ((jsdthreadstate->stackDepth == 0 && !frame) || - (jsdthreadstate->stackDepth == 1 && frame && + if ((jsdthreadstate->stackDepth == 0 && !frame)) + break; + + if ((jsdthreadstate->stackDepth == 1 && frame && frame->jsdscript && !JSD_IS_DEBUG_ENABLED(jsdc, frame->jsdscript))) { /* @@ -316,7 +318,9 @@ jsd_GetScopeChainForStackFrame(JSDContex if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) ) { + JS_BeginRequest(jsdthreadstate->context); obj = JS_GetFrameScopeChain(jsdthreadstate->context, jsdframe->fp); + JS_EndRequest(jsdthreadstate->context); if(obj) jsdval = JSD_NewValue(jsdc, OBJECT_TO_JSVAL(obj)); } @@ -585,7 +589,7 @@ jsd_GetException(JSDContext* jsdc, JSDTh return NULL; if(JS_GetPendingException(cx, &val)) - return jsd_NewValue(jsdc, val); + return jsd_NewValue(jsdc, cx, val); return NULL; } Index: jsd_val.c =================================================================== RCS file: /cvsroot/mozilla/js/jsd/jsd_val.c,v retrieving revision 3.9 diff -up -r3.9 jsd_val.c --- jsd_val.c +++ jsd_val.c @@ -41,6 +41,8 @@ #include "jsd.h" +#define USE_SCARY_NOT_ROOTING + #ifdef DEBUG void JSD_ASSERT_VALID_VALUE(JSDValue* jsdval) { @@ -155,15 +157,16 @@ jsd_IsValueNative(JSDContext* jsdc, JSDV if(JSVAL_IS_FUNCTION(cx, val)) { + JSBool ok = JS_FALSE; + JS_BeginRequest(cx); exceptionState = JS_SaveExceptionState(cx); fun = JS_ValueToFunction(cx, val); JS_RestoreExceptionState(cx, exceptionState); - if(!fun) - { - JS_ASSERT(0); - return JS_FALSE; - } - return JS_GetFunctionScript(cx, fun) ? JS_FALSE : JS_TRUE; + if (fun) + ok = !JS_GetFunctionScript(cx, fun); + JS_EndRequest(cx); + JS_ASSERT(fun); + return ok; } return JSVAL_TO_OBJECT(val) && OBJ_IS_NATIVE(JSVAL_TO_OBJECT(val)); } @@ -205,12 +208,20 @@ jsd_GetValueString(JSDContext* jsdc, JSD if(!jsdval->string) { +#ifdef USE_SCARY_NOT_ROOTING /* if the jsval is a string, then we don't need to double root it */ if(JSVAL_IS_STRING(jsdval->val)) jsdval->string = JSVAL_TO_STRING(jsdval->val); else +#endif { + JS_BeginRequest(cx); exceptionState = JS_SaveExceptionState(cx); +#ifndef USE_SCARY_NOT_ROOTING + if(JSVAL_IS_STRING(jsdval->val)) + jsdval->string = JSVAL_TO_STRING(jsdval->val); + else +#endif jsdval->string = JS_ValueToString(cx, jsdval->val); JS_RestoreExceptionState(cx, exceptionState); if(jsdval->string) @@ -218,6 +229,7 @@ jsd_GetValueString(JSDContext* jsdc, JSD if(!JS_AddNamedRoot(cx, &jsdval->string, "ValueString")) jsdval->string = NULL; } + JS_EndRequest(cx); } } return jsdval->string; @@ -233,9 +245,11 @@ jsd_GetValueFunctionName(JSDContext* jsd if(!jsdval->funName && JSVAL_IS_FUNCTION(cx, val)) { + JS_BeginRequest(cx); exceptionState = JS_SaveExceptionState(cx); fun = JS_ValueToFunction(cx, val); JS_RestoreExceptionState(cx, exceptionState); + JS_EndRequest(cx); if(!fun) return NULL; jsdval->funName = JS_GetFunctionName(fun); @@ -246,7 +260,7 @@ jsd_GetValueFunctionName(JSDContext* jsd /***************************************************************************/ JSDValue* -jsd_NewValue(JSDContext* jsdc, jsval val) +jsd_NewValue(JSDContext* jsdc, JSContext* cx, jsval val) { JSDValue* jsdval; @@ -255,7 +269,11 @@ jsd_NewValue(JSDContext* jsdc, jsval val if(JSVAL_IS_GCTHING(val)) { - if(!JS_AddNamedRoot(jsdc->dumbContext, &jsdval->val, "JSDValue")) + JSBool ok; + JS_BeginRequest(cx); + ok = JS_AddNamedRoot(cx, &jsdval->val, "JSDValue"); + JS_EndRequest(cx); + if(!ok) { free(jsdval); return NULL; @@ -269,14 +287,17 @@ jsd_NewValue(JSDContext* jsdc, jsval val } void -jsd_DropValue(JSDContext* jsdc, JSDValue* jsdval) +jsd_DropValue(JSDContext* jsdc, JSContext* cx, JSDValue* jsdval) { JS_ASSERT(jsdval->nref > 0); if(0 == --jsdval->nref) { jsd_RefreshValue(jsdc, jsdval); - if(JSVAL_IS_GCTHING(jsdval->val)) - JS_RemoveRoot(jsdc->dumbContext, &jsdval->val); + if(JSVAL_IS_GCTHING(jsdval->val)) { + JS_BeginRequest(cx); + JS_RemoveRoot(cx, &jsdval->val); + JS_EndRequest(cx); + } free(jsdval); } } @@ -300,14 +321,14 @@ static JSDProperty* _newProperty(JSDCont jsdprop->flags = pd->flags | additionalFlags; jsdprop->slot = pd->slot; - if(!(jsdprop->name = jsd_NewValue(jsdc, pd->id))) + if(!(jsdprop->name = jsd_NewValue(jsdc, jsdc->dumbContext, pd->id))) goto new_prop_fail; - if(!(jsdprop->val = jsd_NewValue(jsdc, pd->value))) + if(!(jsdprop->val = jsd_NewValue(jsdc, jsdc->dumbContext, pd->value))) goto new_prop_fail; if((jsdprop->flags & JSDPD_ALIAS) && - !(jsdprop->alias = jsd_NewValue(jsdc, pd->alias))) + !(jsdprop->alias = jsd_NewValue(jsdc, jsdc->dumbContext, pd->alias))) goto new_prop_fail; return jsdprop; @@ -343,8 +364,11 @@ static JSBool _buildProps(JSDContext* js if(!JSVAL_IS_OBJECT(jsdval->val) || JSVAL_IS_NULL(jsdval->val)) return JS_FALSE; - if(!JS_GetPropertyDescArray(cx, JSVAL_TO_OBJECT(jsdval->val), &pda)) + JS_BeginRequest(cx); + if(!JS_GetPropertyDescArray(cx, JSVAL_TO_OBJECT(jsdval->val), &pda)) { + JS_EndRequest(cx); return JS_FALSE; + } for(i = 0; i < pda.length; i++) { @@ -357,12 +381,13 @@ static JSBool _buildProps(JSDContext* js JS_APPEND_LINK(&prop->links, &jsdval->props); } JS_PutPropertyDescArray(cx, &pda); + JS_EndRequest(cx); SET_BIT_FLAG(jsdval->flags, GOT_PROPS); return !JS_CLIST_IS_EMPTY(&jsdval->props); } #undef DROP_CLEAR_VALUE -#define DROP_CLEAR_VALUE(jsdc, x) if(x){jsd_DropValue(jsdc,x); x = NULL;} +#define DROP_CLEAR_VALUE(jsdc, x) if(x){jsd_DropValue(jsdc,jsdc->dumbContext,x); x = NULL;} void jsd_RefreshValue(JSDContext* jsdc, JSDValue* jsdval) @@ -371,9 +396,15 @@ jsd_RefreshValue(JSDContext* jsdc, JSDVa if(jsdval->string) { +#ifndef USE_SCARY_NOT_ROOTING /* if the jsval is a string, then we didn't need to root the string */ if(!JSVAL_IS_STRING(jsdval->val)) +#endif + { + JS_BeginRequest(cx); JS_RemoveRoot(cx, &jsdval->string); + JS_EndRequest(cx); + } jsdval->string = NULL; } @@ -462,9 +493,12 @@ jsd_GetValueProperty(JSDContext* jsdc, J nameChars = JS_GetStringChars(name); nameLen = JS_GetStringLength(name); + JS_BeginRequest(cx); JS_GetUCPropertyAttributes(cx, obj, nameChars, nameLen, &attrs, &found); - if (!found) + if (!found) { + JS_EndRequest(cx); return NULL; + } JS_ClearPendingException(cx); @@ -472,8 +506,10 @@ jsd_GetValueProperty(JSDContext* jsdc, J { if (JS_IsExceptionPending(cx)) { - if (!JS_GetPendingException(cx, &pd.value)) + if (!JS_GetPendingException(cx, &pd.value)) { + JS_EndRequest(cx); return NULL; + } pd.flags = JSPD_EXCEPTION; } else @@ -486,6 +522,7 @@ jsd_GetValueProperty(JSDContext* jsdc, J { pd.value = val; } + JS_EndRequest(cx); pd.id = STRING_TO_JSVAL(name); pd.alias = pd.slot = pd.spare = 0; @@ -510,9 +547,12 @@ jsd_GetValuePrototype(JSDContext* jsdc, return NULL; if(!(obj = JSVAL_TO_OBJECT(jsdval->val))) return NULL; - if(!(proto = OBJ_GET_PROTO(jsdc->dumbContext,obj))) + JS_BeginRequest(jsdc->dumbContext); + proto = OBJ_GET_PROTO(jsdc->dumbContext,obj); + JS_EndRequest(jsdc->dumbContext); + if(!proto) return NULL; - jsdval->proto = jsd_NewValue(jsdc, OBJECT_TO_JSVAL(proto)); + jsdval->proto = jsd_NewValue(jsdc, jsdc->dumbContext, OBJECT_TO_JSVAL(proto)); } if(jsdval->proto) jsdval->proto->nref++; @@ -532,9 +572,12 @@ jsd_GetValueParent(JSDContext* jsdc, JSD return NULL; if(!(obj = JSVAL_TO_OBJECT(jsdval->val))) return NULL; - if(!(parent = OBJ_GET_PARENT(jsdc->dumbContext,obj))) + JS_BeginRequest(jsdc->dumbContext); + parent = OBJ_GET_PARENT(jsdc->dumbContext,obj); + JS_EndRequest(jsdc->dumbContext); + if(!parent) return NULL; - jsdval->parent = jsd_NewValue(jsdc, OBJECT_TO_JSVAL(parent)); + jsdval->parent = jsd_NewValue(jsdc, jsdc->dumbContext, OBJECT_TO_JSVAL(parent)); } if(jsdval->parent) jsdval->parent->nref++; @@ -548,18 +591,21 @@ jsd_GetValueConstructor(JSDContext* jsdc { JSObject* obj; JSObject* proto; - JSObject* ctor; + JSObject* ctor = NULL; JS_ASSERT(!jsdval->ctor); SET_BIT_FLAG(jsdval->flags, GOT_CTOR); if(!JSVAL_IS_OBJECT(jsdval->val)) return NULL; if(!(obj = JSVAL_TO_OBJECT(jsdval->val))) return NULL; - if(!(proto = OBJ_GET_PROTO(jsdc->dumbContext,obj))) - return NULL; - if(!(ctor = JS_GetConstructor(jsdc->dumbContext,proto))) + JS_BeginRequest(jsdc->dumbContext); + proto = OBJ_GET_PROTO(jsdc->dumbContext,obj); + if(proto) + ctor = JS_GetConstructor(jsdc->dumbContext,proto); + JS_EndRequest(jsdc->dumbContext); + if(!ctor) return NULL; - jsdval->ctor = jsd_NewValue(jsdc, OBJECT_TO_JSVAL(ctor)); + jsdval->ctor = jsd_NewValue(jsdc, jsdc->dumbContext, OBJECT_TO_JSVAL(ctor)); } if(jsdval->ctor) jsdval->ctor->nref++; @@ -575,8 +621,10 @@ jsd_GetValueClassName(JSDContext* jsdc, JSObject* obj; if(!(obj = JSVAL_TO_OBJECT(val))) return NULL; + JS_BeginRequest(jsdc->dumbContext); if(OBJ_GET_CLASS(jsdc->dumbContext, obj)) jsdval->className = OBJ_GET_CLASS(jsdc->dumbContext, obj)->name; + JS_EndRequest(jsdc->dumbContext); } return jsdval->className; } Index: jsd_xpc.cpp =================================================================== RCS file: /cvsroot/mozilla/js/jsd/jsd_xpc.cpp,v retrieving revision 1.72 diff -up -r1.72 jsd_xpc.cpp --- jsd_xpc.cpp +++ jsd_xpc.cpp @@ -50,11 +50,15 @@ #include "nsIObserverService.h" #include "nsICategoryManager.h" #include "nsIJSRuntimeService.h" -#include "nsIEventQueueService.h" +#include "nsIThread.h" #include "nsMemory.h" #include "jsdebug.h" #include "nsReadableUtils.h" #include "nsCRT.h" +#include "nsPrintfCString.h" + +#include "nsIOutputStream.h" +#include "nsISupportsPrimitives.h" /* XXX this stuff is used by NestEventLoop, a temporary hack to be refactored * later */ @@ -69,6 +73,7 @@ * be fixed now (see Mozilla bug 77636). */ #undef CAUTIOUS_SCRIPTHOOK +#define CAUTIOUS_SCRIPTHOOK 1 #ifdef DEBUG_verbose # define DEBUG_COUNT(name, count) \ @@ -76,7 +81,7 @@ # define DEBUG_CREATE(name, count) {count++; DEBUG_COUNT ("+++++ "name,count)} # define DEBUG_DESTROY(name, count) {count--; DEBUG_COUNT ("----- "name,count)} #else -# define DEBUG_CREATE(name, count) +# define DEBUG_CREATE(name, count) # define DEBUG_DESTROY(name, count) #endif @@ -154,7 +159,7 @@ static struct FilterRecord { PRCList links; jsdIFilter *filterObject; void *glob; - char *urlPattern; + char *urlPattern; PRUint32 patternLength; PatternType patternType; PRUint32 startLine; @@ -174,8 +179,8 @@ jsds_FindEphemeral (LiveEphemeral **list { if (!*listHead) return nsnull; - - LiveEphemeral *lv_record = + + LiveEphemeral *lv_record = NS_REINTERPRET_CAST (LiveEphemeral *, PR_NEXT_LINK(&(*listHead)->links)); do @@ -196,7 +201,7 @@ jsds_FindEphemeral (LiveEphemeral **list void jsds_InvalidateAllEphemerals (LiveEphemeral **listHead) { - LiveEphemeral *lv_record = + LiveEphemeral *lv_record = NS_REINTERPRET_CAST (LiveEphemeral *, PR_NEXT_LINK(&(*listHead)->links)); while (*listHead) @@ -241,7 +246,7 @@ jsds_RemoveEphemeral (LiveEphemeral **li /* otherwise, if we're currently the list head, change it */ *listHead = next; } - + PR_REMOVE_AND_INIT_LINK(&item->links); } @@ -265,7 +270,7 @@ jsds_SyncFilter (FilterRecord *rec, jsdI { NS_ASSERTION (rec, "jsds_SyncFilter without rec"); NS_ASSERTION (filter, "jsds_SyncFilter without filter"); - + JSObject *glob_proper = nsnull; nsCOMPtr glob; nsresult rv = filter->GetGlobalObject(getter_AddRefs(glob)); @@ -276,7 +281,7 @@ jsds_SyncFilter (FilterRecord *rec, jsdI if (nsiglob) glob_proper = nsiglob->GetGlobalJSObject(); } - + PRUint32 startLine; rv = filter->GetStartLine(&startLine); if (NS_FAILED(rv)) @@ -285,13 +290,13 @@ jsds_SyncFilter (FilterRecord *rec, jsdI PRUint32 endLine; rv = filter->GetStartLine(&endLine); if (NS_FAILED(rv)) - return PR_FALSE; + 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] == '*') { @@ -312,7 +317,7 @@ jsds_SyncFilter (FilterRecord *rec, jsdI rec->patternLength = len - 1; } } else if (urlPattern[len - 1] == '*') { - /* pattern is in the format "foo*", overwrite the final * with a + /* pattern is in the format "foo*", overwrite the final * with a * null. */ urlPattern[len - 1] = '\0'; rec->patternType = ptStartsWith; @@ -334,18 +339,18 @@ jsds_SyncFilter (FilterRecord *rec, jsdI 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 * @@ -353,16 +358,16 @@ 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(¤t->links)); } while (current != gFilters); - + return nsnull; } @@ -377,7 +382,7 @@ jsds_FilterHook (JSDContext *jsdc, JSDTh NS_WARNING("No global in threadstate"); return PR_FALSE; } - + JSDStackFrameInfo *frame = JSD_GetStackFrame (jsdc, state); if (!frame) { @@ -398,7 +403,7 @@ jsds_FilterHook (JSDContext *jsdc, JSDTh } if (!gFilters) - return PR_TRUE; + return PR_TRUE; PRUint32 currentLine = JSD_GetClosestLine (jsdc, script, pc); PRUint32 len = 0; @@ -410,9 +415,9 @@ jsds_FilterHook (JSDContext *jsdc, JSDTh 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 + /* and there is no start line, or the start line is before * or equal to the current */ - (!currentFilter->startLine || + (!currentFilter->startLine || currentFilter->startLine <= currentLine) && /* and there is no end line, or the end line is after * or equal to the current */ @@ -424,7 +429,7 @@ jsds_FilterHook (JSDContext *jsdc, JSDTh if (!len) len = PL_strlen(url); - + if (len >= currentFilter->patternLength) { switch (currentFilter->patternType) { case ptEquals: @@ -432,13 +437,13 @@ jsds_FilterHook (JSDContext *jsdc, JSDTh return flags & jsdIFilter::FLAG_PASS; break; case ptStartsWith: - if (!PL_strncmp(currentFilter->urlPattern, url, + if (!PL_strncmp(currentFilter->urlPattern, url, currentFilter->patternLength)) return flags & jsdIFilter::FLAG_PASS; break; case ptEndsWith: if (!PL_strcmp(currentFilter->urlPattern, - &url[len - + &url[len - currentFilter->patternLength])) return flags & jsdIFilter::FLAG_PASS; break; @@ -449,7 +454,7 @@ jsds_FilterHook (JSDContext *jsdc, JSDTh default: NS_ASSERTION(0, "Invalid pattern type"); } - } + } } } currentFilter = NS_REINTERPRET_CAST(FilterRecord *, @@ -457,7 +462,7 @@ jsds_FilterHook (JSDContext *jsdc, JSDTh } while (currentFilter != gFilters); return PR_TRUE; - + } /******************************************************************************* @@ -467,7 +472,7 @@ jsds_FilterHook (JSDContext *jsdc, JSDTh JS_STATIC_DLL_CALLBACK (void) jsds_NotifyPendingDeadScripts (JSContext *cx) { - nsCOMPtr hook = 0; + nsCOMPtr hook = 0; gJsds->GetScriptHook (getter_AddRefs(hook)); DeadScript *ds; @@ -477,14 +482,16 @@ jsds_NotifyPendingDeadScripts (JSContext gJsds->Pause(nsnull); while (gDeadScripts) { ds = gDeadScripts; - + if (hook) { /* tell the user this script has been destroyed */ #ifdef CAUTIOUS_SCRIPTHOOK JS_UNKEEP_ATOMS(rt); #endif + jsrefcount rc = JS_SuspendRequest(cx); hook->OnScriptDestroyed (ds->script); + JS_ResumeRequest(cx, rc); #ifdef CAUTIOUS_SCRIPTHOOK JS_KEEP_ATOMS(rt); #endif @@ -496,7 +503,7 @@ jsds_NotifyPendingDeadScripts (JSContext /* last script in the list */ gDeadScripts = nsnull; } - + /* take ourselves out of the circular list */ PR_REMOVE_LINK(&ds->links); /* addref came from the FromPtr call in jsds_ScriptHookProc */ @@ -517,10 +524,10 @@ jsds_GCCallbackProc (JSContext *cx, JSGC #endif if (status == JSGC_END && gDeadScripts) jsds_NotifyPendingDeadScripts (cx); - + if (gLastGCProc) return gLastGCProc (cx, status); - + return JS_TRUE; } @@ -537,17 +544,17 @@ jsds_ErrorHookProc (JSDContext *jsdc, JS if (running) return JSD_ERROR_REPORTER_PASS_ALONG; - + running = PR_TRUE; - + nsCOMPtr val; if (JS_IsExceptionPending(cx)) { jsval jv; JS_GetPendingException(cx, &jv); - JSDValue *jsdv = JSD_NewValue (jsdc, jv); + JSDValue *jsdv = JSD_NewValue2 (jsdc, cx, jv); val = getter_AddRefs(jsdValue::FromPtr(jsdc, jsdv)); } - + const char *fileName; PRUint32 line; PRUint32 pos; @@ -569,15 +576,17 @@ jsds_ErrorHookProc (JSDContext *jsdc, JS flags = 0; errnum = 0; } - + gJsds->Pause(nsnull); + jsrefcount rc = JS_SuspendRequest(cx); hook->OnError (message, fileName, line, pos, flags, errnum, val, &rval); + JS_ResumeRequest(cx, rc); gJsds->UnPause(nsnull); - + running = PR_FALSE; if (!rval) return JSD_ERROR_REPORTER_DEBUG; - + return JSD_ERROR_REPORTER_PASS_ALONG; } @@ -593,7 +602,7 @@ jsds_CallHookProc (JSDContext* jsdc, JSD 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)); @@ -602,7 +611,7 @@ jsds_CallHookProc (JSDContext* jsdc, JSD default: NS_ASSERTION (0, "Unknown hook type."); } - + if (!hook) return JS_TRUE; @@ -614,7 +623,10 @@ jsds_CallHookProc (JSDContext* jsdc, JSD getter_AddRefs(jsdStackFrame::FromPtr(jsdc, jsdthreadstate, native_frame)); gJsds->Pause(nsnull); - hook->OnCall(frame, type); + JSContext *cx = JSD_GetJSContext(jsdc, jsdthreadstate); + jsrefcount rc = JS_SuspendRequest(cx); + hook->OnCall(frame, type); + JS_ResumeRequest(cx, rc); gJsds->UnPause(nsnull); jsdStackFrame::InvalidateAll(); @@ -667,10 +679,10 @@ jsds_ExecutionHookProc (JSDContext* jsdc if (!hook) return hook_rv; - + if (!jsds_FilterHook (jsdc, jsdthreadstate)) return JSD_HOOK_RETURN_CONTINUE; - + JSDStackFrameInfo *native_frame = JSD_GetStackFrame (jsdc, jsdthreadstate); nsCOMPtr frame = getter_AddRefs(jsdStackFrame::FromPtr(jsdc, jsdthreadstate, @@ -678,12 +690,16 @@ jsds_ExecutionHookProc (JSDContext* jsdc gJsds->Pause(nsnull); jsdIValue *inout_rv = js_rv; NS_IF_ADDREF(inout_rv); + JSContext *cx = JSD_GetJSContext(jsdc, jsdthreadstate); + jsrefcount rc = JS_SuspendRequest(cx); hook->OnExecute (frame, type, &inout_rv, &hook_rv); + JS_SetContextThread(cx); + JS_ResumeRequest(cx, rc); js_rv = inout_rv; NS_IF_RELEASE(inout_rv); gJsds->UnPause(nsnull); jsdStackFrame::InvalidateAll(); - + if (hook_rv == JSD_HOOK_RETURN_RET_WITH_VAL || hook_rv == JSD_HOOK_RETURN_THROW_WITH_VAL) { if (js_rv) { @@ -694,7 +710,7 @@ jsds_ExecutionHookProc (JSDContext* jsdc *rval = JSVAL_VOID; } } - + return hook_rv; } @@ -709,21 +725,23 @@ jsds_ScriptHookProc (JSDContext* jsdc, J nsCOMPtr hook; gJsds->GetScriptHook (getter_AddRefs(hook)); - + if (creating) { /* a script is being created */ if (!hook) { /* nobody cares, just exit */ return; } - - nsCOMPtr script = + + nsCOMPtr script = getter_AddRefs(jsdScript::FromPtr(jsdc, jsdscript)); #ifdef CAUTIOUS_SCRIPTHOOK JS_UNKEEP_ATOMS(rt); #endif gJsds->Pause(nsnull); + jsrefcount rc = JS_SuspendRequest(cx); hook->OnScriptCreated (script); + JS_ResumeRequest(cx, rc); gJsds->UnPause(nsnull); #ifdef CAUTIOUS_SCRIPTHOOK JS_KEEP_ATOMS(rt); @@ -732,24 +750,26 @@ jsds_ScriptHookProc (JSDContext* jsdc, J /* a script is being destroyed. even if there is no registered hook * we'll still need to invalidate the jsdIScript record, in order * to remove the reference held in the JSDScript private data. */ - nsCOMPtr jsdis = + nsCOMPtr jsdis = NS_STATIC_CAST(jsdIScript *, JSD_GetScriptPrivate(jsdscript)); if (!jsdis) return; - + jsdis->Invalidate(); if (!hook) return; - + if (gGCStatus == JSGC_END) { /* if GC *isn't* running, we can tell the user about the script * delete now. */ #ifdef CAUTIOUS_SCRIPTHOOK JS_UNKEEP_ATOMS(rt); #endif - + gJsds->Pause(nsnull); + jsrefcount rc = JS_SuspendRequest(cx); hook->OnScriptDestroyed (jsdis); + JS_ResumeRequest(cx, rc); gJsds->UnPause(nsnull); #ifdef CAUTIOUS_SCRIPTHOOK JS_KEEP_ATOMS(rt); @@ -761,7 +781,7 @@ jsds_ScriptHookProc (JSDContext* jsdc, J DeadScript *ds = PR_NEW(DeadScript); if (!ds) return; /* NS_ERROR_OUT_OF_MEMORY */ - + ds->jsdc = jsdc; ds->script = jsdis; NS_ADDREF(ds->script); @@ -774,7 +794,7 @@ jsds_ScriptHookProc (JSDContext* jsdc, J gDeadScripts = ds; } } - } + } } /******************************************************************************* @@ -783,7 +803,7 @@ jsds_ScriptHookProc (JSDContext* jsdc, J /* Contexts */ /* -NS_IMPL_THREADSAFE_ISUPPORTS1(jsdContext, jsdIContext); +NS_IMPL_THREADSAFE_ISUPPORTS1(jsdContext, jsdIContext) NS_IMETHODIMP jsdContext::GetJSDContext(JSDContext **_rval) @@ -856,7 +876,7 @@ NS_IMETHODIMP jsdObject::GetValue(jsdIValue **_rval) { JSDValue *jsdv = JSD_GetValueForObject (mCx, mObject); - + *_rval = jsdValue::FromPtr (mCx, jsdv); return NS_OK; } @@ -873,7 +893,7 @@ jsdProperty::jsdProperty (JSDContext *aC jsds_InsertEphemeral (&gLiveProperties, &mLiveListEntry); } -jsdProperty::~jsdProperty () +jsdProperty::~jsdProperty () { DEBUG_DESTROY ("jsdProperty", gPropertyCount); if (mValid) @@ -922,7 +942,7 @@ NS_IMETHODIMP jsdProperty::GetAlias(jsdIValue **_rval) { JSDValue *jsdv = JSD_GetPropertyValue (mCx, mProperty); - + *_rval = jsdValue::FromPtr (mCx, jsdv); return NS_OK; } @@ -938,7 +958,7 @@ NS_IMETHODIMP jsdProperty::GetName(jsdIValue **_rval) { JSDValue *jsdv = JSD_GetPropertyName (mCx, mProperty); - + *_rval = jsdValue::FromPtr (mCx, jsdv); return NS_OK; } @@ -947,7 +967,7 @@ NS_IMETHODIMP jsdProperty::GetValue(jsdIValue **_rval) { JSDValue *jsdv = JSD_GetPropertyValue (mCx, mProperty); - + *_rval = jsdValue::FromPtr (mCx, jsdv); return NS_OK; } @@ -966,7 +986,7 @@ jsdScript::jsdScript (JSDContext *aCx, J mTag(0), mCx(aCx), mScript(aScript), - mFileName(0), + mFileName(0), mFunctionName(0), mBaseLineNumber(0), mLineExtent(0), @@ -979,19 +999,24 @@ jsdScript::jsdScript (JSDContext *aCx, J /* copy the script's information now, so we have it later, when it * gets destroyed. */ JSD_LockScriptSubsystem(mCx); - mFileName = new nsCString(JSD_GetScriptFilename(mCx, mScript)); - mFunctionName = - new nsCString(JSD_GetScriptFunctionName(mCx, mScript)); - mBaseLineNumber = JSD_GetScriptBaseLineNumber(mCx, mScript); - mLineExtent = JSD_GetScriptLineExtent(mCx, mScript); - mFirstPC = JSD_GetClosestPC(mCx, mScript, 0); + do { + mFileName = new nsCString(JSD_GetScriptFilename(mCx, mScript)); + if (!mFileName) + break; + mFunctionName = + new nsCString(JSD_GetScriptFunctionName(mCx, mScript)); + if (!mFunctionName) + break; + mBaseLineNumber = JSD_GetScriptBaseLineNumber(mCx, mScript); + mLineExtent = JSD_GetScriptLineExtent(mCx, mScript); + mFirstPC = JSD_GetClosestPC(mCx, mScript, 0); + mValid = PR_TRUE; + } while (0); JSD_UnlockScriptSubsystem(mCx); - - mValid = PR_TRUE; } } -jsdScript::~jsdScript () +jsdScript::~jsdScript () { DEBUG_DESTROY ("jsdScript", gScriptCount); if (mFileName) @@ -1015,55 +1040,65 @@ jsdScript::~jsdScript () */ PCMapEntry * jsdScript::CreatePPLineMap() -{ +{ JSContext *cx = JSD_GetDefaultJSContext (mCx); + JS_BeginRequest(cx); JSObject *obj = JS_NewObject(cx, NULL, NULL, NULL); JSFunction *fun = JSD_GetJSFunction (mCx, mScript); JSScript *script; PRUint32 baseLine; PRBool scriptOwner = PR_FALSE; - + if (fun) { if (fun->nargs > 12) return nsnull; JSString *jsstr = JS_DecompileFunctionBody (cx, fun, 4); - if (!jsstr) + if (!jsstr) { + JS_EndRequest(cx); return nsnull; - - const char *argnames[] = {"arg1", "arg2", "arg3", "arg4", + } + + const char *argnames[] = {"arg1", "arg2", "arg3", "arg4", "arg5", "arg6", "arg7", "arg8", "arg9", "arg10", "arg11", "arg12" }; fun = JS_CompileUCFunction (cx, obj, "ppfun", fun->nargs, argnames, JS_GetStringChars(jsstr), JS_GetStringLength(jsstr), "x-jsd:ppbuffer?type=function", 3); - if (!fun || !(script = JS_GetFunctionScript(cx, fun))) + if (!fun || !(script = JS_GetFunctionScript(cx, fun))) { + JS_EndRequest(cx); return nsnull; + } + baseLine = 3; } else { JSString *jsstr = JS_DecompileScript (cx, JSD_GetJSScript(mCx, mScript), "ppscript", 4); - if (!jsstr) + if (!jsstr) { + JS_EndRequest(cx); return nsnull; + } script = JS_CompileUCScript (cx, obj, JS_GetStringChars(jsstr), JS_GetStringLength(jsstr), "x-jsd:ppbuffer?type=script", 1); - if (!script) + if (!script) { + JS_EndRequest(cx); return nsnull; + } scriptOwner = PR_TRUE; baseLine = 1; } - + PRUint32 scriptExtent = JS_GetScriptLineExtent (cx, script); jsbytecode* firstPC = JS_LineNumberToPC (cx, script, 0); /* allocate worst case size of map (number of lines in script + 1 * for our 0 record), we'll shrink it with a realloc later. */ - mPPLineMap = + mPPLineMap = NS_STATIC_CAST(PCMapEntry *, PR_Malloc((scriptExtent + 1) * sizeof (PCMapEntry))); - if (mPPLineMap) { + if (mPPLineMap) { mPCMapSize = 0; for (PRUint32 line = baseLine; line < scriptExtent + baseLine; ++line) { jsbytecode* pc = JS_LineNumberToPC (cx, script, line); @@ -1083,6 +1118,7 @@ jsdScript::CreatePPLineMap() if (scriptOwner) JS_DestroyScript (cx, script); + JS_EndRequest(cx); return mPPLineMap; } @@ -1095,7 +1131,7 @@ jsdScript::PPPcToLine (PRUint32 aPC) PRUint32 i; for (i = 1; i < mPCMapSize; ++i) { if (mPPLineMap[i].pc > aPC) - return mPPLineMap[i - 1].line; + return mPPLineMap[i - 1].line; } return mPPLineMap[mPCMapSize - 1].line; @@ -1146,7 +1182,7 @@ jsdScript::GetTag(PRUint32 *_rval) { if (!mTag) mTag = ++jsdScript::LastTag; - + *_rval = mTag; return NS_OK; } @@ -1156,7 +1192,7 @@ jsdScript::Invalidate() { ASSERT_VALID_EPHEMERAL; mValid = PR_FALSE; - + /* release the addref we do in FromPtr */ jsdIScript *script = NS_STATIC_CAST(jsdIScript *, JSD_GetScriptPrivate(mScript)); @@ -1173,10 +1209,10 @@ jsdScript::InvalidateAll () gJsds->GetJSDContext (&cx); JSDScript *script; JSDScript *iter = NULL; - + JSD_LockScriptSubsystem(cx); while((script = JSD_IterateScripts(cx, &iter)) != NULL) { - nsCOMPtr jsdis = + nsCOMPtr jsdis = NS_STATIC_CAST(jsdIScript *, JSD_GetScriptPrivate(script)); if (jsdis) jsdis->Invalidate(); @@ -1210,6 +1246,7 @@ jsdScript::GetFlags(PRUint32 *_rval) NS_IMETHODIMP jsdScript::GetFileName(char **_rval) { + NS_ENSURE_STATE(mFileName); *_rval = ToNewCString(*mFileName); if (!*_rval) return NS_ERROR_OUT_OF_MEMORY; @@ -1219,6 +1256,7 @@ jsdScript::GetFileName(char **_rval) NS_IMETHODIMP jsdScript::GetFunctionName(char **_rval) { + NS_ENSURE_STATE(mFunctionName); *_rval = ToNewCString(*mFunctionName); if (!*_rval) return NS_ERROR_OUT_OF_MEMORY; @@ -1231,21 +1269,18 @@ jsdScript::GetFunctionObject(jsdIValue * JSFunction *fun = JSD_GetJSFunction(mCx, mScript); if (!fun) return NS_ERROR_NOT_AVAILABLE; - + JSObject *obj = JS_GetFunctionObject(fun); if (!obj) return NS_ERROR_FAILURE; - JSDContext *cx; - gJsds->GetJSDContext (&cx); - - JSDValue *jsdv = JSD_NewValue(cx, OBJECT_TO_JSVAL(obj)); + JSDValue *jsdv = JSD_NewValue(mCx, OBJECT_TO_JSVAL(obj)); if (!jsdv) return NS_ERROR_FAILURE; - *_rval = jsdValue::FromPtr(cx, jsdv); + *_rval = jsdValue::FromPtr(mCx, jsdv); if (!*_rval) { - JSD_DropValue(cx, jsdv); + JSD_DropValue(mCx, jsdv); return NS_ERROR_FAILURE; } @@ -1263,6 +1298,7 @@ jsdScript::GetFunctionSource(nsAString & } JSFunction *fun = JSD_GetJSFunction (mCx, mScript); JSString *jsstr; + JS_BeginRequest(cx); if (fun) { jsstr = JS_DecompileFunction (cx, fun, 4); @@ -1272,6 +1308,7 @@ jsdScript::GetFunctionSource(nsAString & JSScript *script = JSD_GetJSScript (mCx, mScript); jsstr = JS_DecompileScript (cx, script, "ppscript", 4); } + JS_EndRequest(cx); if (!jsstr) return NS_ERROR_FAILURE; aFunctionSource = NS_REINTERPRET_CAST(PRUnichar*, JS_GetStringChars(jsstr)); @@ -1375,7 +1412,7 @@ jsdScript::PcToLine(PRUint32 aPC, PRUint } else { return NS_ERROR_INVALID_ARG; } - + return NS_OK; } @@ -1399,7 +1436,7 @@ NS_IMETHODIMP jsdScript::IsLineExecutable(PRUint32 aLine, PRUint32 aPcmap, PRBool *_rval) { ASSERT_VALID_EPHEMERAL; - if (aPcmap == PCMAP_SOURCETEXT) { + if (aPcmap == PCMAP_SOURCETEXT) { jsuword pc = JSD_GetClosestPC (mCx, mScript, aLine); *_rval = (aLine == JSD_GetClosestLine (mCx, mScript, pc)); } else if (aPcmap == PCMAP_PRETTYPRINT) { @@ -1415,7 +1452,7 @@ jsdScript::IsLineExecutable(PRUint32 aLi } else { return NS_ERROR_INVALID_ARG; } - + return NS_OK; } @@ -1432,7 +1469,7 @@ jsdScript::SetBreakpoint(PRUint32 aPC) NS_IMETHODIMP jsdScript::ClearBreakpoint(PRUint32 aPC) { - ASSERT_VALID_EPHEMERAL; + ASSERT_VALID_EPHEMERAL; jsuword pc = mFirstPC + aPC; JSD_ClearExecutionHook (mCx, mScript, pc); return NS_OK; @@ -1459,9 +1496,9 @@ jsdContext::FromPtr (JSDContext *aJSDCx, { return nsnull; } - + nsCOMPtr jsdicx; - nsCOMPtr eph = + nsCOMPtr eph = jsds_FindEphemeral (&gLiveContexts, NS_STATIC_CAST(void *, aJSCx)); if (eph) { @@ -1469,11 +1506,11 @@ jsdContext::FromPtr (JSDContext *aJSDCx, } else { - nsCOMPtr iscx = + nsCOMPtr iscx = NS_STATIC_CAST(nsISupports *, JS_GetContextPrivate(aJSCx)); if (!iscx) return nsnull; - + jsdicx = new jsdContext (aJSDCx, aJSCx, iscx); } @@ -1490,10 +1527,11 @@ jsdContext::jsdContext (JSDContext *aJSD DEBUG_CREATE ("jsdContext", gContextCount); mLiveListEntry.value = this; mLiveListEntry.key = NS_STATIC_CAST (void *, aJSCx); + mAllowTimeouts = PR_TRUE; jsds_InsertEphemeral (&gLiveContexts, &mLiveListEntry); } -jsdContext::~jsdContext() +jsdContext::~jsdContext() { DEBUG_DESTROY ("jsdContext", gContextCount); if (mValid) @@ -1571,10 +1609,10 @@ jsdContext::GetPrivateData(nsISupports * { *_rval = nsnull; } - + return NS_OK; } - + NS_IMETHODIMP jsdContext::GetWrappedContext(nsISupports **_rval) { @@ -1590,7 +1628,7 @@ jsdContext::GetTag(PRUint32 *_rval) ASSERT_VALID_EPHEMERAL; if (!mTag) mTag = ++jsdContext::LastTag; - + *_rval = mTag; return NS_OK; } @@ -1647,7 +1685,36 @@ jsdContext::SetScriptsEnabled (PRBool _r if (!context) return NS_ERROR_NO_INTERFACE; - context->SetScriptsEnabled(_rval, PR_TRUE); + context->SetScriptsEnabled(_rval, mAllowTimeouts); + + return NS_OK; +} + +NS_IMETHODIMP +jsdContext::GetTimeoutsEnabled (PRBool *_rval) +{ + ASSERT_VALID_EPHEMERAL; + + *_rval = mAllowTimeouts; + + return NS_OK; +} + +NS_IMETHODIMP +jsdContext::SetTimeoutsEnabled (PRBool _rval) +{ + ASSERT_VALID_EPHEMERAL; + nsCOMPtr context = do_QueryInterface(mISCx); + if (!context) + return NS_ERROR_NO_INTERFACE; + + mAllowTimeouts = _rval; + if (mAllowTimeouts && context->GetScriptsEnabled()) { + // Scripts are enabled... so lets disable them quickly to then allow + // firing of the timers. + context->SetScriptsEnabled(PR_FALSE, PR_FALSE); + context->SetScriptsEnabled(PR_TRUE, mAllowTimeouts); + } return NS_OK; } @@ -1668,7 +1735,7 @@ jsdStackFrame::jsdStackFrame (JSDContext } } -jsdStackFrame::~jsdStackFrame() +jsdStackFrame::~jsdStackFrame() { DEBUG_DESTROY ("jsdStackFrame", gFrameCount); if (mValid) @@ -1779,7 +1846,7 @@ jsdStackFrame::GetFunctionName(char **_r const char *name = JSD_GetNameForStackFrame(mCx, mThreadState, mStackFrameInfo); if (name) { - *_rval = PL_strdup(name); + *_rval = (char*)nsMemory::Clone(name, strlen(name)+1); if (!*_rval) return NS_ERROR_OUT_OF_MEMORY; } else { @@ -1833,7 +1900,7 @@ jsdStackFrame::GetPc(PRUint32 *_rval) if (!script) return NS_ERROR_FAILURE; jsuword pcbase = JSD_GetClosestPC(mCx, script, 0); - + jsuword pc = JSD_GetPCForStackFrame (mCx, mThreadState, mStackFrameInfo); if (pc) *_rval = pc - pcbase; @@ -1865,7 +1932,7 @@ jsdStackFrame::GetCallee(jsdIValue **_rv ASSERT_VALID_EPHEMERAL; JSDValue *jsdv = JSD_GetCallObjectForStackFrame (mCx, mThreadState, mStackFrameInfo); - + *_rval = jsdValue::FromPtr (mCx, jsdv); return NS_OK; } @@ -1876,7 +1943,7 @@ jsdStackFrame::GetScope(jsdIValue **_rva ASSERT_VALID_EPHEMERAL; JSDValue *jsdv = JSD_GetScopeChainForStackFrame (mCx, mThreadState, mStackFrameInfo); - + *_rval = jsdValue::FromPtr (mCx, jsdv); return NS_OK; } @@ -1887,7 +1954,7 @@ jsdStackFrame::GetThisValue(jsdIValue ** ASSERT_VALID_EPHEMERAL; JSDValue *jsdv = JSD_GetThisForStackFrame (mCx, mThreadState, mStackFrameInfo); - + *_rval = jsdValue::FromPtr (mCx, jsdv); return NS_OK; } @@ -1911,6 +1978,7 @@ jsdStackFrame::Eval (const nsAString &by jsval jv; JSContext *cx = JSD_GetJSContext (mCx, mThreadState); + JS_BeginRequest(cx); estate = JS_SaveExceptionState (cx); JS_ClearPendingException (cx); @@ -1926,47 +1994,58 @@ jsdStackFrame::Eval (const nsAString &by } JS_RestoreExceptionState (cx, estate); + JS_EndRequest(cx); JSDValue *jsdv = JSD_NewValue (mCx, jv); if (!jsdv) return NS_ERROR_FAILURE; *result = jsdValue::FromPtr (mCx, jsdv); if (!*result) return NS_ERROR_FAILURE; - + return NS_OK; -} +} /* Values */ NS_IMPL_THREADSAFE_ISUPPORTS2(jsdValue, jsdIValue, jsdIEphemeral) jsdIValue * jsdValue::FromPtr (JSDContext *aCx, JSDValue *aValue) { - /* value will be dropped by te jsdValue destructor. */ + /* value will be dropped by the jsdValue destructor. */ if (!aValue) return nsnull; - + jsdIValue *rv = new jsdValue (aCx, aValue); NS_IF_ADDREF(rv); return rv; } jsdValue::jsdValue (JSDContext *aCx, JSDValue *aValue) : mValid(PR_TRUE), - mCx(aCx), + mCx(aCx), mValue(aValue) { DEBUG_CREATE ("jsdValue", gValueCount); + JSContext *cx; + PRThread *thr = PR_GetCurrentThread(); + if (!gJsds->mDumbContextMap.Get(thr, &cx)) + { + cx = JS_NewContext(gJsds->mRuntime, 256); + if (cx) { + if (!gJsds->mDumbContextMap.Put(thr, cx)) + JS_DestroyContext(cx); + } + } mLiveListEntry.value = this; jsds_InsertEphemeral (&gLiveValues, &mLiveListEntry); } -jsdValue::~jsdValue() +jsdValue::~jsdValue() { DEBUG_DESTROY ("jsdValue", gValueCount); if (mValid) /* call Invalidate() to take ourselves out of the live list */ Invalidate(); -} +} NS_IMETHODIMP jsdValue::GetIsValid(PRBool *_rval) @@ -1981,7 +2060,9 @@ jsdValue::Invalidate() ASSERT_VALID_EPHEMERAL; mValid = PR_FALSE; jsds_RemoveEphemeral (&gLiveValues, &mLiveListEntry); - JSD_DropValue (mCx, mValue); + JSContext *cx = JSD_GetDefaultJSContext (mCx); + gJsds->mDumbContextMap.Get(PR_GetCurrentThread(), &cx); + JSD_DropValue2 (mCx, cx, mValue); return NS_OK; } @@ -2039,7 +2120,7 @@ jsdValue::GetJsType (PRUint32 *_rval) jsval val; val = JSD_GetValueWrappedJSVal (mCx, mValue); - + if (JSVAL_IS_NULL(val)) *_rval = TYPE_NULL; else if (JSVAL_IS_BOOLEAN(val)) @@ -2056,8 +2137,10 @@ jsdValue::GetJsType (PRUint32 *_rval) *_rval = TYPE_FUNCTION; else if (JSVAL_IS_OBJECT(val)) *_rval = TYPE_OBJECT; - else - NS_ASSERTION (0, "Value has no discernible type."); + else { + NS_ERROR ("Value has no discernible type."); + return NS_ERROR_UNEXPECTED; + } return NS_OK; } @@ -2092,7 +2175,7 @@ jsdValue::GetJsClassName(char **_rval) } else { *_rval = nsnull; } - + return NS_OK; } @@ -2161,19 +2244,17 @@ jsdValue::GetObjectValue(jsdIObject **_r return NS_ERROR_FAILURE; return NS_OK; } - + NS_IMETHODIMP -jsdValue::GetStringValue(char **_rval) +jsdValue::GetStringValue(nsAString &_rval) { ASSERT_VALID_EPHEMERAL; JSString *jstr_val = JSD_GetValueString(mCx, mValue); if (jstr_val) { - char *bytes = JS_GetStringBytes(jstr_val); - *_rval = PL_strdup(bytes); - if (!*_rval) - return NS_ERROR_OUT_OF_MEMORY; + _rval.Assign(JS_GetStringChars(jstr_val), + JS_GetStringLength(jstr_val)); } else { - *_rval = nsnull; + _rval.Truncate(); } return NS_OK; } @@ -2204,7 +2285,7 @@ jsdValue::GetProperties (jsdIProperty ** jsdIProperty **pa_temp = NS_STATIC_CAST(jsdIProperty **, - nsMemory::Alloc(sizeof (jsdIProperty *) * + nsMemory::Alloc(sizeof (jsdIProperty *) * prop_count)); NS_ENSURE_TRUE(pa_temp, NS_ERROR_OUT_OF_MEMORY); @@ -2215,27 +2296,37 @@ jsdValue::GetProperties (jsdIProperty ** pa_temp[i] = jsdProperty::FromPtr (mCx, prop); ++i; } - - NS_ASSERTION (prop_count == i, "property count mismatch"); + + NS_ASSERTION (prop_count == i, "property count mismatch"); /* if caller doesn't care about length, don't bother telling them */ *propArray = pa_temp; if (length) *length = prop_count; - + return NS_OK; } NS_IMETHODIMP -jsdValue::GetProperty (const char *name, jsdIProperty **_rval) +jsdValue::GetProperty (const nsAString &aName, jsdIProperty **_rval) { ASSERT_VALID_EPHEMERAL; JSContext *cx = JSD_GetDefaultJSContext (mCx); + const nsString& name = PromiseFlatString(aName); /* not rooting this */ - JSString *jstr_name = JS_NewStringCopyZ (cx, name); + /* XXX timeless: ok, so it isn't being rooted, what prevents TOO_MUCH_GC from killing it? + * it looks like this string only needs to last until JSD_GetValueProperty + * finishes. isn't there some way to do this using shareable strings? + * XXX JS_NewExternalString ? + */ + JS_BeginRequest(cx); + JSString *jstr_name = JS_NewUCStringCopyN (cx, name.get(), name.Length()); + JS_EndRequest(cx); + if (!jstr_name) + return NS_ERROR_OUT_OF_MEMORY; JSDProperty *prop = JSD_GetValueProperty (mCx, mValue, jstr_name); - + *_rval = jsdProperty::FromPtr (mCx, prop); return NS_OK; } @@ -2276,6 +2367,125 @@ jsdValue::GetWrappedValue() return NS_OK; } +NS_IMETHODIMP +jsdValue::GetJsSupportsPrivate (nsISupports **_rval) +{ + ASSERT_VALID_EPHEMERAL; + if(!JSD_IsValueObject(mCx, mValue)) + return NS_OK; + + JSObject *obj = JSVAL_TO_OBJECT(JSD_GetValueWrappedJSVal (mCx, mValue)); + JSContext *cx = JSD_GetDefaultJSContext (mCx); + JSClass *clasp = JS_GetClass(cx, obj); + if (!(clasp->flags & (JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS))) + return NS_OK; + + nsISupports* priv = (nsISupports*)JS_GetPrivate(cx, obj); + NS_IF_ADDREF(*_rval = priv); + + return NS_OK; +} + +static PRUint32 calculateJSValSize(jsval val, JSContext *cx) +{ + if (JSVAL_IS_NULL(val)) + return sizeof(jsval); + if (JSVAL_IS_BOOLEAN(val)) + return sizeof(jsval); + if (JSVAL_IS_DOUBLE(val)) + return sizeof(jsdouble) + sizeof(jsval); + if (JSVAL_IS_INT(val)) + return sizeof(jsval); + if (JSVAL_IS_STRING(val)) + return sizeof(jschar) * JS_GetStringLength(JSVAL_TO_STRING(val)); + if (JSVAL_IS_VOID(val)) + return sizeof(jsval); + + JS_BeginRequest(cx); + PRUint32 size = 0; + if (JSVAL_IS_FUNCTION(cx, val)) + size = JS_GetFunctionTotalSize(cx, JS_ValueToFunction(cx, val)); + else if (JSVAL_IS_OBJECT(val)) + size = JS_GetObjectTotalSize(cx, JSVAL_TO_OBJECT(val)); + JS_EndRequest(cx); + + return size; +} + +NS_IMETHODIMP +jsdValue::GetSize(PRUint32 *aSize) +{ + ASSERT_VALID_EPHEMERAL; + jsval val = JSD_GetValueWrappedJSVal (mCx, mValue); + PRUint32 size = calculateJSValSize(val, JSD_GetDefaultJSContext (mCx)); + *aSize = size; + if (!size) + return NS_ERROR_UNEXPECTED; + + return NS_OK; +} + +/****************************************************************************** + * wrapper objects + ******************************************************************************/ + +class ExecutionHookWrapper : public jsdIExecutionHook +{ + NS_DECL_ISUPPORTS + NS_DECL_JSDIEXECUTIONHOOK + nsCOMPtr mHook; +}; + +NS_IMPL_THREADSAFE_ISUPPORTS1(ExecutionHookWrapper, jsdIExecutionHook) + +class CallHookWrapper : public jsdICallHook +{ + NS_DECL_ISUPPORTS + NS_DECL_JSDICALLHOOK + nsCOMPtr mHook; +}; + +NS_IMPL_THREADSAFE_ISUPPORTS1(CallHookWrapper, jsdICallHook) + +NS_IMETHODIMP CallHookWrapper::OnCall(jsdIStackFrame *frame, PRUint32 type) +{ + NS_ENSURE_STATE(mHook); + NS_ENSURE_ARG_POINTER(frame); + nsresult rv; + JSDContext *jsdc = nsnull; + JSDThreadState* jsdthreadstate = nsnull; + JSContext *cx = nsnull; + rv = frame->GetJSDContext(&jsdc); + NS_ENSURE_SUCCESS(rv, rv); + rv = frame->GetJSDThreadState(&jsdthreadstate); + NS_ENSURE_SUCCESS(rv, rv); + cx = JSD_GetJSContext(jsdc, jsdthreadstate); + JS_SetContextThread(cx); + rv = mHook->OnCall (frame, type); + JS_SetContextThread(cx); + return rv; +} + +NS_IMETHODIMP ExecutionHookWrapper::OnExecute(jsdIStackFrame *frame, PRUint32 type, jsdIValue **val, PRUint32 *_retval) +{ + NS_ENSURE_STATE(mHook); + NS_ENSURE_ARG_POINTER(frame); + nsresult rv; + JSDContext *jsdc = nsnull; + JSDThreadState* jsdthreadstate = nsnull; + JSContext *cx = nsnull; + rv = frame->GetJSDContext(&jsdc); + NS_ENSURE_SUCCESS(rv, rv); + rv = frame->GetJSDThreadState(&jsdthreadstate); + NS_ENSURE_SUCCESS(rv, rv); + cx = JSD_GetJSContext(jsdc, jsdthreadstate); + JS_SetContextThread(cx); + rv = mHook->OnExecute (frame, type, val, _retval); + JS_SetContextThread(cx); + return rv; +} + + /****************************************************************************** * debugger service implementation ******************************************************************************/ @@ -2294,7 +2504,7 @@ jsdService::GetInitAtStartup (PRBool *_r nsresult rv; nsCOMPtr categoryManager(do_GetService(NS_CATMAN_CTRID, &rv)); - + if (NS_FAILED(rv)) { NS_WARNING("couldn't get category manager"); @@ -2304,8 +2514,8 @@ jsdService::GetInitAtStartup (PRBool *_r if (mInitAtStartup == triUnknown) { nsXPIDLCString notused; nsresult autoreg_rv, appstart_rv; - - autoreg_rv = categoryManager->GetCategoryEntry(AUTOREG_CATEGORY, + + autoreg_rv = categoryManager->GetCategoryEntry(AUTOREG_CATEGORY, JSD_AUTOREG_ENTRY, getter_Copies(notused)); appstart_rv = categoryManager->GetCategoryEntry(APPSTART_CATEGORY, @@ -2335,7 +2545,7 @@ jsdService::GetInitAtStartup (PRBool *_r return rv; } } - + if (_rval) *_rval = (mInitAtStartup == triYes); @@ -2344,7 +2554,7 @@ jsdService::GetInitAtStartup (PRBool *_r /* * The initAtStartup property controls whether or not we register the - * app start observer (jsdASObserver.) We register for both + * 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 @@ -2353,7 +2563,7 @@ jsdService::GetInitAtStartup (PRBool *_r */ NS_IMETHODIMP jsdService::SetInitAtStartup (PRBool state) -{ +{ nsresult rv; if (mInitAtStartup == triUnknown) { @@ -2368,7 +2578,7 @@ jsdService::SetInitAtStartup (PRBool sta /* already in the requested state */ return NS_OK; } - + nsCOMPtr categoryManager(do_GetService(NS_CATMAN_CTRID, &rv)); @@ -2404,6 +2614,7 @@ jsdService::SetInitAtStartup (PRBool sta NS_IMETHODIMP jsdService::GetFlags (PRUint32 *_rval) { + ASSERT_VALID_CONTEXT; *_rval = JSD_GetContextFlags (mCx); return NS_OK; } @@ -2411,6 +2622,7 @@ jsdService::GetFlags (PRUint32 *_rval) NS_IMETHODIMP jsdService::SetFlags (PRUint32 flags) { + ASSERT_VALID_CONTEXT; JSD_SetContextFlags (mCx, flags); return NS_OK; } @@ -2450,6 +2662,31 @@ jsdService::On (void) { nsresult rv; + mEventService = do_GetService(kEventQueueServiceCID, &rv); + if (NS_FAILED(rv)) + return rv; + rv = mEventService->GetSpecialEventQueue( + nsIEventQueueService::UI_THREAD_EVENT_QUEUE, + getter_AddRefs(mUIEq) + ); + if (NS_FAILED(rv)) + return rv; + + if (!mProxyService) { + mProxyService = do_GetService(NS_XPCOMPROXY_CONTRACTID); + if (mProxyService) { + SetErrorHook(mErrorHook); + SetBreakpointHook(mBreakpointHook); + SetDebugHook(mDebugHook); + SetDebuggerHook(mDebuggerHook); + SetInterruptHook(mInterruptHook); + SetThrowHook(mThrowHook); + SetScriptHook(mScriptHook); + SetTopLevelHook(mTopLevelHook); + SetFunctionHook(mFunctionHook); + } + } + /* get JS things from the CallContext */ nsCOMPtr xpc = do_GetService(nsIXPConnect::GetCID()); if (!xpc) return NS_ERROR_FAILURE; @@ -2461,9 +2698,9 @@ jsdService::On (void) JSContext *cx; rv = cc->GetJSContext (&cx); if (NS_FAILED(rv)) return NS_ERROR_FAILURE; - + return OnForRuntime(JS_GetRuntime (cx)); - + } NS_IMETHODIMP @@ -2490,9 +2727,9 @@ jsdService::OnForRuntime (JSRuntime *rt) nsCOMPtr xpc = do_GetService(nsIXPConnect::GetCID()); if (!xpc) return NS_ERROR_FAILURE; - + xpc->InitClasses (cx, glob); - + /* If any of these mFooHook objects are installed, do the required JSD * hookup now. See also, jsdService::SetFooHook(). */ @@ -2500,7 +2737,7 @@ jsdService::OnForRuntime (JSRuntime *rt) JSD_SetErrorReporter (mCx, jsds_ErrorHookProc, NULL); if (mThrowHook) JSD_SetThrowHook (mCx, jsds_ExecutionHookProc, NULL); - /* can't ignore script callbacks, as we need to |Release| the wrapper + /* can't ignore script callbacks, as we need to |Release| the wrapper * stored in private data when a script is deleted. */ if (mInterruptHook) JSD_SetInterruptHook (mCx, jsds_ExecutionHookProc, NULL); @@ -2529,10 +2766,10 @@ jsdService::Off (void) { if (!mOn) return NS_OK; - + if (!mCx || !mRuntime) return NS_ERROR_NOT_INITIALIZED; - + if (gDeadScripts) { if (gGCStatus == JSGC_END) { @@ -2561,7 +2798,7 @@ jsdService::Off (void) JSD_ClearDebugBreakHook (mCx); JSD_ClearTopLevelHook (mCx); JSD_ClearFunctionHook (mCx); - + JSD_DebuggerOff (mCx); mCx = nsnull; @@ -2582,7 +2819,7 @@ jsdService::GetPauseDepth(PRUint32 *_rva *_rval = mPauseLevel; return NS_OK; } - + NS_IMETHODIMP jsdService::Pause(PRUint32 *_rval) { @@ -2631,7 +2868,7 @@ jsdService::UnPause(PRUint32 *_rval) else JSD_ClearFunctionHook (mCx); } - + if (_rval) *_rval = mPauseLevel; @@ -2642,16 +2879,16 @@ NS_IMETHODIMP jsdService::EnumerateContexts (jsdIContextEnumerator *enumerator) { ASSERT_VALID_CONTEXT; - + if (!enumerator) return NS_OK; - + JSContext *iter = NULL; JSContext *cx; while ((cx = JS_ContextIterator (mRuntime, &iter))) { - nsCOMPtr jsdicx = + nsCOMPtr jsdicx = getter_AddRefs(jsdContext::FromPtr(mCx, cx)); if (jsdicx) { @@ -2667,11 +2904,11 @@ NS_IMETHODIMP jsdService::EnumerateScripts (jsdIScriptEnumerator *enumerator) { ASSERT_VALID_CONTEXT; - + JSDScript *script; JSDScript *iter = NULL; nsresult rv = NS_OK; - + JSD_LockScriptSubsystem(mCx); while((script = JSD_IterateScripts(mCx, &iter))) { nsCOMPtr jsdis = @@ -2708,7 +2945,7 @@ jsdService::GC (void) #endif return NS_OK; } - + NS_IMETHODIMP jsdService::ClearProfileData () { @@ -2732,7 +2969,7 @@ jsdService::InsertFilter (jsdIFilter *fi PR_Free (rec); return NS_ERROR_FAILURE; } - + if (gFilters) { if (!after) { /* insert at head of list */ @@ -2756,7 +2993,7 @@ jsdService::InsertFilter (jsdIFilter *fi PR_INIT_CLIST(&rec->links); gFilters = rec; } - + return NS_OK; } @@ -2772,14 +3009,14 @@ jsdService::AppendFilter (jsdIFilter *fi 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; } @@ -2790,7 +3027,7 @@ jsdService::RemoveFilter (jsdIFilter *fi 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)); @@ -2799,10 +3036,10 @@ jsdService::RemoveFilter (jsdIFilter *fi gFilters = nsnull; } - + PR_REMOVE_LINK(&rec->links); jsds_FreeFilter (rec); - + return NS_OK; } @@ -2811,18 +3048,18 @@ jsdService::SwapFilters (jsdIFilter *fil { 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. */ @@ -2835,16 +3072,16 @@ jsdService::SwapFilters (jsdIFilter *fil if (!jsds_SyncFilter (rec_b, filter_a)) return NS_ERROR_FAILURE; } - + return NS_OK; } NS_IMETHODIMP -jsdService::EnumerateFilters (jsdIFilterEnumerator *enumerator) +jsdService::EnumerateFilters (jsdIFilterEnumerator *enumerator) { if (!gFilters) return NS_OK; - + FilterRecord *current = gFilters; do { jsds_SyncFilter (current, current->filterObject); @@ -2857,7 +3094,7 @@ jsdService::EnumerateFilters (jsdIFilter current = NS_REINTERPRET_CAST(FilterRecord *, PR_NEXT_LINK (¤t->links)); } while (current != gFilters); - + return NS_OK; } @@ -2882,13 +3119,13 @@ jsdService::ClearFilters () jsds_FreeFilter(current); current = next; } while (current != gFilters); - + jsds_FreeFilter(current); gFilters = nsnull; - + return NS_OK; } - + NS_IMETHODIMP jsdService::ClearAllBreakpoints (void) { @@ -2921,7 +3158,7 @@ jsdService::WrapValue(jsdIValue **_rval) return rv; if (argc < 1) return NS_ERROR_INVALID_ARG; - + jsval *argv; rv = cc->GetArgvPtr (&argv); if (NS_FAILED(rv)) @@ -2930,7 +3167,7 @@ jsdService::WrapValue(jsdIValue **_rval) JSDValue *jsdv = JSD_NewValue (mCx, argv[0]); if (!jsdv) return NS_ERROR_FAILURE; - + *_rval = jsdValue::FromPtr (mCx, jsdv); return NS_OK; } @@ -2941,34 +3178,32 @@ jsdService::EnterNestedEventLoop (jsdINe { nsCOMPtr appShell(do_CreateInstance(kAppShellCID)); NS_ENSURE_TRUE(appShell, NS_ERROR_FAILURE); - nsCOMPtr - eventService(do_GetService(kEventQueueServiceCID)); - NS_ENSURE_TRUE(eventService, NS_ERROR_FAILURE); - + NS_ENSURE_TRUE(mEventService, NS_ERROR_FAILURE); + appShell->Create(0, nsnull); appShell->Spinup(); - nsCOMPtr + nsCOMPtr stack(do_GetService("@mozilla.org/js/xpc/ContextStack;1")); nsresult rv = NS_OK; PRUint32 nestLevel = ++mNestedLoopLevel; - + nsCOMPtr eventQ; if (stack && NS_SUCCEEDED(stack->Push(nsnull)) && - NS_SUCCEEDED(eventService->PushThreadEventQueue(getter_AddRefs(eventQ)))) + NS_SUCCEEDED(mEventService->PushThreadEventQueue(getter_AddRefs(eventQ)))) { if (NS_SUCCEEDED(rv) && callback) { Pause(nsnull); rv = callback->OnNest(); UnPause(nsnull); } - + while(NS_SUCCEEDED(rv) && mNestedLoopLevel >= nestLevel) { void* data; PRBool isRealEvent; //PRBool processEvent; - + rv = appShell->GetNativeEvent(isRealEvent, data); if(NS_SUCCEEDED(rv)) appShell->DispatchNativeEvent(isRealEvent, data); @@ -2979,8 +3214,8 @@ jsdService::EnterNestedEventLoop (jsdINe } else rv = NS_ERROR_FAILURE; - - eventService->PopThreadEventQueue(eventQ); + + mEventService->PopThreadEventQueue(eventQ); appShell->Spindown(); NS_ASSERTION (mNestedLoopLevel <= nestLevel, @@ -3000,16 +3235,203 @@ jsdService::ExitNestedEventLoop (PRUint3 else return NS_ERROR_FAILURE; - *_rval = mNestedLoopLevel; + *_rval = mNestedLoopLevel; return NS_OK; -} +} /* hook attribute get/set functions */ +nsresult +jsdService::WrapCallHook(jsdICallHook *aHook, jsdICallHook **aWrappedHook) +{ + *aWrappedHook = nsnull; + if (!aHook) + return NS_OK; + + PRUint32 flags = 0; + if (mProxyService) { + nsCOMPtr hookCI(do_QueryInterface(aHook)); + if (hookCI) { + nsresult rv = hookCI->GetFlags(&flags); + if (NS_FAILED(rv) || + !(flags & nsIClassInfo::THREADSAFE)) { + flags = 0; + } + } + } else { + /* if there's no proxyservice, then we let the act of getting a proxy service do this work. + * when we do callbacks, we'll only operate + */ + flags = 1; + } + if (flags) { + *aWrappedHook = aHook; + return NS_OK; + } + + CallHookWrapper* wrapper = new CallHookWrapper; + if (!wrapper) + return NS_ERROR_OUT_OF_MEMORY; + wrapper->mHook = aHook; + nsresult rv = mProxyService->GetProxyForObject( + mUIEq, + NS_GET_IID(jsdICallHook), + wrapper, + nsIProxyObjectManager::INVOKE_SYNC | nsIProxyObjectManager::FORCE_PROXY_CREATION, + (void**)aWrappedHook + ); + if (NS_FAILED(rv)) { + delete wrapper; + return rv; + } + wrapper = new CallHookWrapper; + if (NS_FAILED(rv)) { + NS_RELEASE(*aWrappedHook); + return NS_ERROR_OUT_OF_MEMORY; + } + wrapper->mHook.swap(*aWrappedHook); + NS_ADDREF(*aWrappedHook = wrapper); + return NS_OK; +} + +nsresult +jsdService::WrapErrorHook(jsdIErrorHook *aHook, jsdIErrorHook **aWrappedHook) +{ + *aWrappedHook = nsnull; + if (!aHook) + return NS_OK; + + PRUint32 flags = 0; + if (mProxyService) { + nsCOMPtr hookCI(do_QueryInterface(aHook)); + if (hookCI) { + nsresult rv = hookCI->GetFlags(&flags); + if (NS_FAILED(rv) || + !(flags & nsIClassInfo::THREADSAFE)) { + flags = 0; + } + } + } else { + /* if there's no proxyservice, then we let the act of getting a proxy service do this work. + * when we do callbacks, we'll only operate + */ + flags = 1; + } + if (flags) { + *aWrappedHook = aHook; + return NS_OK; + } + + return mProxyService->GetProxyForObject( + mUIEq, + NS_GET_IID(jsdIErrorHook), + aHook, + nsIProxyObjectManager::INVOKE_SYNC | nsIProxyObjectManager::FORCE_PROXY_CREATION, + (void**)aWrappedHook + ); + +} + +nsresult +jsdService::WrapExecutionHook(jsdIExecutionHook *aHook, jsdIExecutionHook **aWrappedHook) +{ + *aWrappedHook = nsnull; + if (!aHook) + return NS_OK; + + PRUint32 flags = 0; + if (mProxyService) { + nsCOMPtr hookCI(do_QueryInterface(aHook)); + if (hookCI) { + nsresult rv = hookCI->GetFlags(&flags); + if (NS_FAILED(rv) || + !(flags & nsIClassInfo::THREADSAFE)) { + flags = 0; + } + } + } else { + /* if there's no proxyservice, then we let the act of getting a proxy service do this work. + * when we do callbacks, we'll only operate + */ + flags = 1; + } + if (flags) { + *aWrappedHook = aHook; + return NS_OK; + } + + ExecutionHookWrapper* wrapper = new ExecutionHookWrapper; + if (!wrapper) + return NS_ERROR_OUT_OF_MEMORY; + wrapper->mHook = aHook; + nsresult rv = mProxyService->GetProxyForObject( + mUIEq, + NS_GET_IID(jsdIExecutionHook), + wrapper, + nsIProxyObjectManager::INVOKE_SYNC | nsIProxyObjectManager::FORCE_PROXY_CREATION, + (void**)aWrappedHook + ); + if (NS_FAILED(rv)) { + delete wrapper; + return rv; + } + wrapper = new ExecutionHookWrapper; + if (NS_FAILED(rv)) { + NS_RELEASE(*aWrappedHook); + return NS_ERROR_OUT_OF_MEMORY; + } + wrapper->mHook.swap(*aWrappedHook); + NS_ADDREF(*aWrappedHook = wrapper); + return NS_OK; +} + +nsresult +jsdService::WrapScriptHook(jsdIScriptHook *aHook, jsdIScriptHook **aWrappedHook) +{ + *aWrappedHook = nsnull; + if (!aHook) + return NS_OK; + + PRUint32 flags = 0; + if (mProxyService) { + nsCOMPtr hookCI(do_QueryInterface(aHook)); + if (hookCI) { + nsresult rv = hookCI->GetFlags(&flags); + if (NS_FAILED(rv) || + !(flags & nsIClassInfo::THREADSAFE)) { + flags = 0; + } + } + } else { + /* if there's no proxyservice, then we let the act of getting a proxy service do this work. + * when we do callbacks, we'll only operate + */ + flags = 1; + } + if (flags) { + *aWrappedHook = aHook; + return NS_OK; + } + + return mProxyService->GetProxyForObject( + mUIEq, + NS_GET_IID(jsdIScriptHook), + aHook, + nsIProxyObjectManager::INVOKE_SYNC | nsIProxyObjectManager::FORCE_PROXY_CREATION, + (void**)aWrappedHook + ); + +} + NS_IMETHODIMP jsdService::SetErrorHook (jsdIErrorHook *aHook) { - mErrorHook = aHook; + nsCOMPtr hook; + nsresult rv = WrapErrorHook(aHook, getter_AddRefs(hook)); + if (NS_FAILED(rv)) + return rv; + + mErrorHook = hook; /* 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. @@ -3028,32 +3450,52 @@ jsdService::SetErrorHook (jsdIErrorHook NS_IMETHODIMP jsdService::GetErrorHook (jsdIErrorHook **aHook) { - *aHook = mErrorHook; - NS_IF_ADDREF(*aHook); - + *aHook = nsnull; + if (!mErrorHook) + return NS_OK; + + if (!mProxyService && nsIThread::IsMainThread()) + return NS_OK; + NS_ADDREF(*aHook = mErrorHook); + return NS_OK; } NS_IMETHODIMP jsdService::SetBreakpointHook (jsdIExecutionHook *aHook) -{ - mBreakpointHook = aHook; +{ + nsCOMPtr hook; + nsresult rv = WrapExecutionHook(aHook, getter_AddRefs(hook)); + if (NS_FAILED(rv)) + return rv; + + mBreakpointHook = hook; return NS_OK; } NS_IMETHODIMP jsdService::GetBreakpointHook (jsdIExecutionHook **aHook) -{ - *aHook = mBreakpointHook; - NS_IF_ADDREF(*aHook); - +{ + *aHook = nsnull; + if (!mBreakpointHook) + return NS_OK; + + if (!mProxyService && nsIThread::IsMainThread()) + return NS_OK; + NS_ADDREF(*aHook = mBreakpointHook); + return NS_OK; } NS_IMETHODIMP jsdService::SetDebugHook (jsdIExecutionHook *aHook) -{ - mDebugHook = aHook; +{ + nsCOMPtr hook; + nsresult rv = WrapExecutionHook(aHook, getter_AddRefs(hook)); + if (NS_FAILED(rv)) + return rv; + + mDebugHook = hook; /* 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. @@ -3065,23 +3507,33 @@ jsdService::SetDebugHook (jsdIExecutionH JSD_SetDebugBreakHook (mCx, jsds_ExecutionHookProc, NULL); else JSD_ClearDebugBreakHook (mCx); - + return NS_OK; } NS_IMETHODIMP jsdService::GetDebugHook (jsdIExecutionHook **aHook) -{ - *aHook = mDebugHook; - NS_IF_ADDREF(*aHook); - +{ + *aHook = nsnull; + if (!mDebugHook) + return NS_OK; + + if (!mProxyService && nsIThread::IsMainThread()) + return NS_OK; + NS_ADDREF(*aHook = mDebugHook); + return NS_OK; } NS_IMETHODIMP jsdService::SetDebuggerHook (jsdIExecutionHook *aHook) -{ - mDebuggerHook = aHook; +{ + nsCOMPtr hook; + nsresult rv = WrapExecutionHook(aHook, getter_AddRefs(hook)); + if (NS_FAILED(rv)) + return rv; + + mDebuggerHook = hook; /* 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. @@ -3093,23 +3545,33 @@ jsdService::SetDebuggerHook (jsdIExecuti JSD_SetDebuggerHook (mCx, jsds_ExecutionHookProc, NULL); else JSD_ClearDebuggerHook (mCx); - + return NS_OK; } NS_IMETHODIMP jsdService::GetDebuggerHook (jsdIExecutionHook **aHook) -{ - *aHook = mDebuggerHook; - NS_IF_ADDREF(*aHook); - +{ + *aHook = nsnull; + if (!mDebuggerHook) + return NS_OK; + + if (!mProxyService && nsIThread::IsMainThread()) + return NS_OK; + NS_ADDREF(*aHook = mDebuggerHook); + return NS_OK; } NS_IMETHODIMP jsdService::SetInterruptHook (jsdIExecutionHook *aHook) -{ - mInterruptHook = aHook; +{ + nsCOMPtr hook; + nsresult rv = WrapExecutionHook(aHook, getter_AddRefs(hook)); + if (NS_FAILED(rv)) + return rv; + + mInterruptHook = hook; /* 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. @@ -3121,30 +3583,40 @@ jsdService::SetInterruptHook (jsdIExecut JSD_SetInterruptHook (mCx, jsds_ExecutionHookProc, NULL); else JSD_ClearInterruptHook (mCx); - + return NS_OK; } NS_IMETHODIMP jsdService::GetInterruptHook (jsdIExecutionHook **aHook) -{ - *aHook = mInterruptHook; - NS_IF_ADDREF(*aHook); - +{ + *aHook = nsnull; + if (!mInterruptHook) + return NS_OK; + + if (!mProxyService && nsIThread::IsMainThread()) + return NS_OK; + NS_ADDREF(*aHook = mInterruptHook); + return NS_OK; } NS_IMETHODIMP jsdService::SetScriptHook (jsdIScriptHook *aHook) -{ - mScriptHook = aHook; +{ + nsCOMPtr hook; + nsresult rv = WrapScriptHook(aHook, getter_AddRefs(hook)); + if (NS_FAILED(rv)) + return rv; + + mScriptHook = hook; /* 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 || mPauseLevel) return NS_OK; - + if (aHook) JSD_SetScriptHook (mCx, jsds_ScriptHookProc, NULL); /* we can't unset it if !aHook, because we still need to see script @@ -3155,17 +3627,27 @@ jsdService::SetScriptHook (jsdIScriptHoo NS_IMETHODIMP jsdService::GetScriptHook (jsdIScriptHook **aHook) -{ - *aHook = mScriptHook; - NS_IF_ADDREF(*aHook); - +{ + *aHook = nsnull; + if (!mScriptHook) + return NS_OK; + + if (!mProxyService && nsIThread::IsMainThread()) + return NS_OK; + NS_ADDREF(*aHook = mScriptHook); + return NS_OK; } NS_IMETHODIMP jsdService::SetThrowHook (jsdIExecutionHook *aHook) -{ - mThrowHook = aHook; +{ + nsCOMPtr hook; + nsresult rv = WrapExecutionHook(aHook, getter_AddRefs(hook)); + if (NS_FAILED(rv)) + return rv; + + mThrowHook = hook; /* 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. @@ -3177,23 +3659,33 @@ jsdService::SetThrowHook (jsdIExecutionH JSD_SetThrowHook (mCx, jsds_ExecutionHookProc, NULL); else JSD_ClearThrowHook (mCx); - + return NS_OK; } NS_IMETHODIMP jsdService::GetThrowHook (jsdIExecutionHook **aHook) -{ - *aHook = mThrowHook; - NS_IF_ADDREF(*aHook); - +{ + *aHook = nsnull; + if (!mThrowHook) + return NS_OK; + + if (!mProxyService && nsIThread::IsMainThread()) + return NS_OK; + NS_ADDREF(*aHook = mThrowHook); + return NS_OK; } NS_IMETHODIMP jsdService::SetTopLevelHook (jsdICallHook *aHook) -{ - mTopLevelHook = aHook; +{ + nsCOMPtr hook; + nsresult rv = WrapCallHook(aHook, getter_AddRefs(hook)); + if (NS_FAILED(rv)) + return rv; + + mTopLevelHook = hook; /* 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. @@ -3205,23 +3697,33 @@ jsdService::SetTopLevelHook (jsdICallHoo JSD_SetTopLevelHook (mCx, jsds_CallHookProc, NULL); else JSD_ClearTopLevelHook (mCx); - + return NS_OK; } NS_IMETHODIMP jsdService::GetTopLevelHook (jsdICallHook **aHook) -{ - *aHook = mTopLevelHook; - NS_IF_ADDREF(*aHook); - +{ + *aHook = nsnull; + if (!mTopLevelHook) + return NS_OK; + + if (!mProxyService && nsIThread::IsMainThread()) + return NS_OK; + NS_ADDREF(*aHook = mTopLevelHook); + return NS_OK; } NS_IMETHODIMP jsdService::SetFunctionHook (jsdICallHook *aHook) -{ - mFunctionHook = aHook; +{ + nsCOMPtr hook; + nsresult rv = WrapCallHook(aHook, getter_AddRefs(hook)); + if (NS_FAILED(rv)) + return rv; + + mFunctionHook = hook; /* 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. @@ -3233,16 +3735,315 @@ jsdService::SetFunctionHook (jsdICallHoo JSD_SetFunctionHook (mCx, jsds_CallHookProc, NULL); else JSD_ClearFunctionHook (mCx); - + return NS_OK; } NS_IMETHODIMP jsdService::GetFunctionHook (jsdICallHook **aHook) -{ - *aHook = mFunctionHook; - NS_IF_ADDREF(*aHook); - +{ + *aHook = nsnull; + if (!mFunctionHook) + return NS_OK; + + if (!mProxyService && nsIThread::IsMainThread()) + return NS_OK; + NS_ADDREF(*aHook = mFunctionHook); + + return NS_OK; +} + +struct ObjectCreationInfo { + uintN minSize; + uintN maxSize; + uintN count; + PRInt64 totalSize; +}; + +const char objectTypeNames[][16] = { + "unknown,", + "null,", + "boolean,", + "double,", + "integer,", + "string,", + "void,", + "function,", + "object,", + 0 +}; + +static void annotateJSValType(nsAFlatCString &type, jsval val, JSContext *cx) +{ + PRUint32 kind = 0; + if (JSVAL_IS_NULL(val)) + kind = 1; + else if (JSVAL_IS_BOOLEAN(val)) + kind = 2; + else if (JSVAL_IS_DOUBLE(val)) + kind = 3; + else if (JSVAL_IS_INT(val)) + kind = 4; + else if (JSVAL_IS_STRING(val)) + kind = 5; + else if (JSVAL_IS_VOID(val)) + kind = 6; + else { + JS_BeginRequest(cx); + if (JSVAL_IS_FUNCTION(cx, val)) + kind = 7; + JS_EndRequest(cx); + if (!kind) { + if (JSVAL_IS_OBJECT(val)) + kind = 8; + } + } + type.Append(objectTypeNames[kind]); +} + +static void annotateJSValName(nsAFlatCString &name, jsval val, JSContext *cx, JSDContext *dx, JSDValue *dval) +{ + PRUint32 kind = 0; + if (JSVAL_IS_NULL(val)) + kind = 1; + else if (JSVAL_IS_BOOLEAN(val)) + kind = 2; + else if (JSVAL_IS_DOUBLE(val)) + kind = 3; + else if (JSVAL_IS_INT(val)) + kind = 4; + else if (JSVAL_IS_STRING(val)) + kind = 5; + else if (JSVAL_IS_VOID(val)) + kind = 6; + else { + JS_BeginRequest(cx); + if (JSVAL_IS_FUNCTION(cx, val)) + kind = 7; + JS_EndRequest(cx); + if (!kind) { + if (JSVAL_IS_OBJECT(val)) + kind = 8; + } + } + switch (kind) { + case 7: + name.Append(JSD_GetValueFunctionName(dx, dval)); + break; + case 8: + name.Append(JSD_GetValueClassName(dx, dval)); + break; + default: + name.Append(objectTypeNames[kind]); + } +} + +static void FormatSpaceString(nsCAutoString &lineItem, ObjectCreationInfo *oci) +{ + lineItem.AppendInt(oci->count); + lineItem.AppendLiteral(","); + if (oci->minSize) { + lineItem.AppendInt(oci->minSize); + lineItem.AppendLiteral(","); + lineItem.AppendInt(oci->maxSize); + lineItem.AppendLiteral(","); + } + lineItem.AppendInt(oci->totalSize); + lineItem.AppendLiteral("\r\n"); +} + +static PLDHashOperator PR_CALLBACK +ObjectsSpaceStreamWriter(const nsACString &aKey, ObjectCreationInfo *&oci, void *aClosure) +{ + /* Count,Min,Max,Total */ + nsIOutputStream* stream = (nsIOutputStream*) aClosure; + PRUint32 written; + nsCAutoString lineItem(aKey); + FormatSpaceString(lineItem, oci); + stream->Write(lineItem.get(), lineItem.Length(), &written); + return PL_DHASH_NEXT; +} + +static PLDHashOperator +ObjectsSpaceStringWriter(const nsACString &aKey, ObjectCreationInfo *&oci, void *aClosure) +{ + nsISupportsCString* string = (nsISupportsCString*) aClosure; + nsCAutoString report; + string->GetData(report); + nsCAutoString lineItem(aKey); + FormatSpaceString(lineItem, oci); + report.Append(lineItem); + string->SetData(report); + return PL_DHASH_NEXT; +} + +static PLDHashOperator +ObjectsSpaceDestructor(const nsACString &aKey, ObjectCreationInfo *&oci, void *aClosure) +{ + free(oci); + return PL_DHASH_NEXT; +} + +void +Write(nsIOutputStream* stream, nsISupportsCString *string, const nsAFlatCString &lineItem, nsAFlatCString &report) +{ + PRUint32 written; + if (stream) + stream->Write(lineItem.get(), lineItem.Length(), &written); + /* yes, this can work really poorly on oom */ + report.Append(lineItem); +} + +NS_IMETHODIMP +jsdService::ReportLiveObjects(nsISupports *reportSink, PRUint32 flags) +{ + /** + * flags + * 0x1 Distinguish lines + * 0x2 Distinguish types + * 0x4 Distinguish sizes + * 0x8 Distinguish names + * 0x10 Distinguish objects + */ + NS_ENSURE_ARG_POINTER(reportSink); + nsCAutoString report; + nsCAutoString lineItem; + nsDataHashtable objectSpaceTable; + jsval val; + JSDObject *dobject, *iter = 0; + JSContext *cx = JSD_GetDefaultJSContext (mCx); + ObjectCreationInfo *oci = nsnull; + nsCOMPtr stream(do_QueryInterface(reportSink)); + nsCOMPtr string(do_QueryInterface(reportSink)); + int stateful = 2; + + if (!stream && !string) + return NS_ERROR_NOT_IMPLEMENTED; + + NS_NAMED_LITERAL_CSTRING(unknownFilename, "\"_unknown_\","); + nsCAutoString header("File,Line,"); + if (flags & jsdIDebuggerService::DISTINGUISH_TYPES) + header.AppendLiteral("Type,"); + NS_NAMED_LITERAL_CSTRING(sizeHeader, "Object,Size,\r\n"); + if (flags & jsdIDebuggerService::DISTINGUISH_NAMES) { + header.AppendLiteral("Name,"); + } + if ((flags & jsdIDebuggerService::DISTINGUISH_OBJECTS) || + !objectSpaceTable.Init(2048)) { + stateful = 1; + flags |= jsdIDebuggerService::DISTINGUISH_SIZES; + header.Append(sizeHeader); + Write(stream, string, header, report); + } + + PRUint32 size; + /* stateful: + * 2 - standard operation + * 1 - low memory + * 0 - done + */ + JSD_LockObjectSubsystem(mCx); + dobject = JSD_IterateObjects(mCx, &iter); + while (dobject) { + if (const char* url = JSD_GetObjectNewURL(mCx, dobject)) { + lineItem.AssignLiteral("\""); + lineItem.Append(url); + lineItem.AppendLiteral("\","); + } else { + lineItem.Assign(unknownFilename); + } + if (flags & jsdIDebuggerService::DISTINGUISH_LINES) { + lineItem.AppendInt(JSD_GetObjectNewLineNumber(mCx, dobject)); + lineItem.AppendLiteral(","); + } + val = OBJECT_TO_JSVAL(JSD_GetWrappedObject(mCx, dobject)); + if (flags & jsdIDebuggerService::DISTINGUISH_TYPES) + annotateJSValType(lineItem, val, cx); + size = calculateJSValSize(val, cx); + if (flags & jsdIDebuggerService::DISTINGUISH_NAMES) { + JSDValue* dval = JSD_GetValueForObject(mCx, dobject); + annotateJSValName(lineItem, val, cx, mCx, dval); + JSD_DropValue2(mCx, cx, dval); + lineItem.AppendLiteral(","); + } + if (flags & jsdIDebuggerService::DISTINGUISH_OBJECTS) { + nsPrintfCString pointer("%p,",(void*)val); + lineItem.Append(pointer); + } + if (flags & jsdIDebuggerService::DISTINGUISH_SIZES) { + lineItem.AppendInt(size); + lineItem.AppendLiteral(","); + } + if (stateful == 2) { + if (!objectSpaceTable.Get(lineItem, &oci)) + oci = nsnull; + if (!oci) { + oci = (ObjectCreationInfo*)malloc(sizeof ObjectCreationInfo); + if (!oci) { + stateful = 1; + flags |= jsdIDebuggerService::DISTINGUISH_SIZES; + iter = nsnull; + dobject = JSD_IterateObjects(mCx, &iter); + objectSpaceTable.Enumerate(ObjectsSpaceDestructor, nsnull); + JSD_UnlockObjectSubsystem(mCx); + if (string) + string->SetData(EmptyCString()); + if (flags & jsdIDebuggerService::DISTINGUISH_NAMES) { + header.AppendLiteral("Name,"); + } + header.Append(sizeHeader); + Write(stream, string, header, report); + JSD_LockObjectSubsystem(mCx); + continue; + } + if (~flags & jsdIDebuggerService::DISTINGUISH_SIZES) { + oci->minSize = size; + oci->maxSize = size; + } else { + oci->minSize = oci->maxSize = 0; + } + oci->count = 0; + oci->totalSize = LL_ZERO; + objectSpaceTable.Put(lineItem, oci); + } + if (~flags & jsdIDebuggerService::DISTINGUISH_SIZES) { + oci->minSize = PR_MIN(oci->minSize, size); + oci->maxSize = PR_MAX(oci->maxSize, size); + } + PRUint64 delta; + LL_UI2L(delta, size); + LL_ADD(oci->totalSize, oci->totalSize, delta); + ++oci->count; + } else if (stateful == 1) { + JSD_UnlockObjectSubsystem(mCx); + lineItem.AppendLiteral("\r\n"); + Write(stream, string, lineItem, report); + JSD_LockObjectSubsystem(mCx); + } + dobject = JSD_IterateObjects(mCx, &iter); + } + JSD_UnlockObjectSubsystem(mCx); + if (stateful == 2) { + if (flags & jsdIDebuggerService::DISTINGUISH_SIZES) + header.AppendLiteral("Size,"); + header.AppendLiteral("Count,"); + if (~flags & jsdIDebuggerService::DISTINGUISH_SIZES) + header.AppendLiteral("Min,Max,"); + header.AppendLiteral("Total,"); + header.AppendLiteral("\r\n"); + Write(stream, string, header, report); + if (stream) + objectSpaceTable.Enumerate(ObjectsSpaceStreamWriter, stream); + if (string) { + string->SetData(report); + objectSpaceTable.Enumerate(ObjectsSpaceStringWriter, string); + } + objectSpaceTable.Enumerate(ObjectsSpaceDestructor, nsnull); + } else if (stateful == 1) { + if (string) { + string->SetData(report); + } + } return NS_OK; } @@ -3257,9 +4058,16 @@ jsdService::~jsdService() jsdService * jsdService::GetService () { - if (!gJsds) - gJsds = new jsdService(); - + if (!gJsds) { + jsdService *jsds = new jsdService(); + if (jsds) { + if (!jsds->mDumbContextMap.IsInitialized()) { + delete jsds; + } else { + gJsds = jsds; + } + } + } NS_IF_ADDREF(gJsds); return gJsds; } @@ -3270,13 +4078,13 @@ NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR * and/or removed from the app-start category by the jsdService::initAtStartup * property. */ -class jsdASObserver : public nsIObserver +class jsdASObserver : public nsIObserver { public: NS_DECL_ISUPPORTS NS_DECL_NSIOBSERVER - jsdASObserver () {} + jsdASObserver () {} }; NS_IMPL_THREADSAFE_ISUPPORTS1(jsdASObserver, nsIObserver) @@ -3295,10 +4103,10 @@ jsdASObserver::Observe (nsISupports *aSu rv = jsds->GetIsOn(&on); if (NS_FAILED(rv) || on) return rv; - + nsCOMPtr rts = do_GetService(NS_JSRT_CTRID, &rv); if (NS_FAILED(rv)) - return rv; + return rv; JSRuntime *rt; rts->GetRuntime (&rt); @@ -3308,7 +4116,7 @@ jsdASObserver::Observe (nsISupports *aSu rv = jsds->OnForRuntime(rt); if (NS_FAILED(rv)) return rv; - + return jsds->SetFlags(JSD_DISABLE_OBJECT_TRACE); } @@ -3328,7 +4136,7 @@ NS_IMPL_NSGETMODULE(JavaScript_Debugger, #if 0 /* Thread States */ -NS_IMPL_THREADSAFE_ISUPPORTS1(jsdThreadState, jsdIThreadState); +NS_IMPL_THREADSAFE_ISUPPORTS1(jsdThreadState, jsdIThreadState) NS_IMETHODIMP jsdThreadState::GetJSDContext(JSDContext **_rval) @@ -3355,7 +4163,7 @@ NS_IMETHODIMP jsdThreadState::GetTopFrame (jsdIStackFrame **_rval) { JSDStackFrameInfo *sfi = JSD_GetStackFrame (mCx, mThreadState); - + *_rval = jsdStackFrame::FromPtr (mCx, mThreadState, sfi); return NS_OK; } @@ -3364,7 +4172,7 @@ NS_IMETHODIMP jsdThreadState::GetPendingException(jsdIValue **_rval) { JSDValue *jsdv = JSD_GetException (mCx, mThreadState); - + *_rval = jsdValue::FromPtr (mCx, jsdv); return NS_OK; } @@ -3373,11 +4181,11 @@ NS_IMETHODIMP jsdThreadState::SetPendingException(jsdIValue *aException) { JSDValue *jsdv; - + nsresult rv = aException->GetJSDValue (&jsdv); if (NS_FAILED(rv)) return NS_ERROR_FAILURE; - + if (!JSD_SetException (mCx, mThreadState, jsdv)) return NS_ERROR_FAILURE; Index: jsd_xpc.h =================================================================== RCS file: /cvsroot/mozilla/js/jsd/jsd_xpc.h,v retrieving revision 1.21 diff -up -r1.21 jsd_xpc.h --- jsd_xpc.h +++ jsd_xpc.h @@ -41,10 +41,13 @@ #define JSDSERVICE_H___ #include "jsdIDebuggerService.h" +#include "nsIProxyObjectManager.h" +#include "nsIEventQueueService.h" #include "jsdebug.h" #include "nsString.h" #include "nsCOMPtr.h" #include "nspr.h" +#include "nsDataHashtable.h" // #if defined(DEBUG_rginda_l) // # define DEBUG_verbose @@ -208,6 +211,7 @@ class jsdContext : public jsdIContext PRBool mValid; LiveEphemeral mLiveListEntry; PRUint32 mTag; + PRBool mAllowTimeouts; JSDContext *mJSDCx; JSContext *mJSCx; nsCOMPtr mISCx; @@ -283,13 +287,23 @@ class jsdService : public jsdIDebuggerSe mInterruptHook(0), mScriptHook(0), mThrowHook(0), mTopLevelHook(0), mFunctionHook(0) { + mDumbContextMap.Init(32); } virtual ~jsdService(); static jsdService *GetService (); - private: +private: + nsresult + WrapCallHook(jsdICallHook *aHook, jsdICallHook **aWrappedHook); + nsresult + WrapErrorHook(jsdIErrorHook *aHook, jsdIErrorHook **aWrappedHook); + nsresult + WrapExecutionHook(jsdIExecutionHook *aHook, jsdIExecutionHook **aWrappedHook); + nsresult + WrapScriptHook(jsdIScriptHook *aHook, jsdIScriptHook **aWrappedHook); + enum Tristate { triUnknown = 0U, triYes = 1U, @@ -312,7 +326,11 @@ class jsdService : public jsdIDebuggerSe nsCOMPtr mThrowHook; nsCOMPtr mTopLevelHook; nsCOMPtr mFunctionHook; - + nsCOMPtr mProxyService; + nsCOMPtr mEventService; + nsCOMPtr mUIEq; + friend class jsdValue; + nsDataHashtableMT mDumbContextMap; }; #endif /* JSDSERVICE_H___ */ Index: jsdebug.c =================================================================== RCS file: /cvsroot/mozilla/js/jsd/jsdebug.c,v retrieving revision 3.15 diff -up -r3.15 jsdebug.c --- jsdebug.c +++ jsdebug.c @@ -946,16 +946,28 @@ JSD_CurrentThread() JSD_PUBLIC_API(JSDValue*) JSD_NewValue(JSDContext* jsdc, jsval val) { - JSD_ASSERT_VALID_CONTEXT(jsdc); - return jsd_NewValue(jsdc, val); + return JSD_NewValue2(jsdc, jsdc->dumbContext, val); } JSD_PUBLIC_API(void) JSD_DropValue(JSDContext* jsdc, JSDValue* jsdval) { + JSD_DropValue2(jsdc, jsdc->dumbContext, jsdval); +} + +JSD_PUBLIC_API(JSDValue*) +JSD_NewValue2(JSDContext* jsdc, JSContext* cx, jsval val) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + return jsd_NewValue(jsdc, cx, val); +} + +JSD_PUBLIC_API(void) +JSD_DropValue2(JSDContext* jsdc, JSContext* cx, JSDValue* jsdval) +{ JSD_ASSERT_VALID_CONTEXT(jsdc); JSD_ASSERT_VALID_VALUE(jsdval); - jsd_DropValue(jsdc, jsdval); + jsd_DropValue(jsdc, cx, jsdval); } JSD_PUBLIC_API(jsval) Index: jsdebug.h =================================================================== RCS file: /cvsroot/mozilla/js/jsd/jsdebug.h,v retrieving revision 3.20 diff -up -r3.20 jsdebug.h --- jsdebug.h +++ jsdebug.h @@ -1510,6 +1510,20 @@ JSD_GetObjectForValue(JSDContext* jsdc, extern JSD_PUBLIC_API(JSDValue*) JSD_GetValueForObject(JSDContext* jsdc, JSDObject* jsdobj); +/* +* Create a new JSDValue to wrap the given jsval +* NOTE: must eventually release by calling JSD_DropValue (if not NULL) +* *** new for version 1.2 **** +*/ +extern JSD_PUBLIC_API(JSDValue*) +JSD_NewValue2(JSDContext* jsdc, JSContext* cx, jsval val); +/* +* Release the JSDValue. After this call the object MUST not be referenced again! +* *** new for version 1.2 **** +*/ +extern JSD_PUBLIC_API(void) +JSD_DropValue2(JSDContext* jsdc, JSContext* cx, JSDValue* jsdval); + /***************************************************************************/ /* Livewire specific API */ #ifdef LIVEWIRE Index: idl/jsdIDebuggerService.idl =================================================================== RCS file: /cvsroot/mozilla/js/jsd/idl/jsdIDebuggerService.idl,v retrieving revision 1.32 diff -up -r1.32 idl/jsdIDebuggerService.idl --- idl/jsdIDebuggerService.idl +++ idl/jsdIDebuggerService.idl @@ -136,6 +136,7 @@ interface jsdIDebuggerService : nsISuppo const long VERSION_1_3 = 130; const long VERSION_1_4 = 140; const long VERSION_1_5 = 150; + const long VERSION_1_6 = 160; const long VERSION_DEFAULT = 0; const long VERSION_UNKNOWN = -1; @@ -382,6 +383,37 @@ interface jsdIDebuggerService : nsISuppo * nested. your code can use it for sanity checks. */ unsigned long exitNestedEventLoop (); + + /** + * List allocations by line + */ + const unsigned long DISTINGUISH_LINES = 0x1; + /** + * List allocations by type + */ + const unsigned long DISTINGUISH_TYPES = 0x2; + /** + * List allocations by size + */ + const unsigned long DISTINGUISH_SIZES = 0x4; + /** + * List allocations by name + */ + const unsigned long DISTINGUISH_NAMES = 0x8; + /** + * List individual objects + */ + const unsigned long DISTINGUISH_OBJECTS = 0x10; + /** + * Report the size and allocation location of objects + * @param reportSink The sink into which the resulting output will be sent. + * This function will try to send it as a very long single + * lump of data. + * @param flags Bit field of flag modifiers. + * @see DISTINGUISH_ bits above. + * @throws NS_ERROR_NOT_IMPLEMENTED if the sink object type isn't supported + */ + void reportLiveObjects(in nsISupports reportSink, in unsigned long flags); }; /* callback interfaces */ @@ -768,6 +800,12 @@ interface jsdIContext : jsdIEphemeral * context will throw a NS_ERROR_NO_INTERFACE exception. */ attribute boolean scriptsEnabled; + + /** + * |true| to indicate that timeouts may be fired. |false| will prevent + * timeouts firing. + */ + attribute boolean timeoutsEnabled; }; /** @@ -1103,7 +1141,7 @@ interface jsdIValue : jsdIEphemeral /** * Value if interpreted as a string. Converts if necessary. */ - readonly attribute string stringValue; + readonly attribute AString stringValue; /** * Number of properties. 0 if the value is not an object, or the value is @@ -1126,7 +1164,7 @@ interface jsdIValue : jsdIEphemeral * @retval jsdIProperty for the requested property name or null if no * property exists for the requested name. */ - jsdIProperty getProperty (in string name); + jsdIProperty getProperty (in AString name); /** * jsdIValues are wrappers around JavaScript engine structures. Much of the @@ -1143,6 +1181,19 @@ interface jsdIValue : jsdIEphemeral * defined error code. */ void getWrappedValue(); + + /** + * Private untagged nsISupports pointer if the class is tagged with + * JSCLASS_PRIVATE_IS_NSISUPPORTS. Null if the class does not have the + * tag or if private is null. + */ + readonly attribute nsISupports jsSupportsPrivate; + + /** + * Retrieves the size of the object (including space required for slots, + * but excluding space required by objects referenced by the object). + */ + readonly attribute unsigned long size; }; /** Index: jsdb/Makefile.in =================================================================== RCS file: /cvsroot/mozilla/js/jsd/jsdb/Makefile.in,v retrieving revision 1.4 diff -up -r1.4 jsdb/Makefile.in --- jsdb/Makefile.in +++ jsdb/Makefile.in @@ -44,17 +44,40 @@ srcdir = @srcdir@ include $(DEPTH)/config/autoconf.mk -CSRCS = jsdrefl.c jsdb.c +CSRCS = jsdrefl.c \ + jsdb.c \ + ../../src/js.c \ + $(NULL) + EXPORTS = jsdb.h MODULE = jsd_refl LIBRARY_NAME = jsdr +PROGRAM = jsdb$(BIN_SUFFIX) + +REQUIRES = jsdebug js + +MOZ_JSD_LIBS = ../jsd$(MOZ_BITS)$(VERSION_NUMBER).$(LIB_SUFFIX) -REQUIRES = jsd mozjs +EXTRA_DSO_LDOPTS += \ + $(MOZ_JS_LIBS) \ + $(MOZ_JSD_LIBS) \ + $(NULL) + +LIBS = \ + $(EXTRA_DSO_LIBS) \ + $(EXTRA_DSO_LDOPTS) \ + $(NULL) ifdef JS_THREADSAFE DEFINES += -DJS_THREADSAFE endif +DEFINES += -DJSDEBUGGER -DJSDEBUGGER_C_UI -DJSD_LOWLEVEL_SOURCE -DJSFILE + include $(topsrcdir)/config/rules.mk +libs:: + $(INSTALL) $(IFLAGS2) ../jsd$(MOZ_BITS)$(VERSION_NUMBER)$(DLL_SUFFIX) $(DIST)/bin + $(INSTALL) $(IFLAGS1) $(srcdir)/debugger.js $(DIST)/bin + $(INSTALL) $(IFLAGS1) $(srcdir)/f.js $(DIST)/bin Index: jsdb/debugger.js =================================================================== RCS file: /cvsroot/mozilla/js/jsd/jsdb/debugger.js,v retrieving revision 1.5 diff -up -r1.5 jsdb/debugger.js --- jsdb/debugger.js +++ jsdb/debugger.js @@ -43,7 +43,7 @@ /* Top level code is run when this file is first loaded... (duh) */ var noisy = false -//var noisy = true +var noisy = true jsd.SetScriptHook(scriptHook); jsd.SetExecutionHook(execHook); Index: jsdb/jsdb.c =================================================================== RCS file: /cvsroot/mozilla/js/jsd/jsdb/jsdb.c,v retrieving revision 1.6 diff -up -r1.6 jsdb/jsdb.c --- jsdb/jsdb.c +++ jsdb/jsdb.c @@ -43,6 +43,11 @@ #include "jsdbpriv.h" #include +#ifndef JS_THREADSAFE +#define JS_BeginRequest(cx) (void)0 +#define JS_EndRequest(cx) (void)0 +#endif + /***************************************************************************/ JS_STATIC_DLL_CALLBACK(void) @@ -89,6 +94,7 @@ jsdb_ScriptHookProc(JSDContext* jsdc, JSDB_Data* data = (JSDB_Data*) callerdata; JSFunction* fun; + JS_BeginRequest(data->cxDebugger); if(data->jsScriptHook && NULL != (fun = JS_ValueToFunction(data->cxDebugger, data->jsScriptHook))) { @@ -100,6 +106,7 @@ jsdb_ScriptHookProc(JSDContext* jsdc, JS_CallFunction(data->cxDebugger, NULL, fun, 2, args, &result); } + JS_EndRequest(data->cxDebugger); } uintN JS_DLL_CALLBACK @@ -120,6 +127,7 @@ jsdb_ExecHookHandler(JSDContext* jsd if(data->jsdthreadstate) return JSD_HOOK_RETURN_CONTINUE; + JS_BeginRequest(data->cxDebugger); if(!jsdb_SetThreadState(data, jsdthreadstate)) goto label_bail; @@ -150,6 +158,7 @@ jsdb_ExecHookHandler(JSDContext* jsd label_bail: jsdb_SetThreadState(data, NULL); + JS_EndRequest(data->cxDebugger); return ourRetVal; } @@ -177,6 +186,7 @@ jsdb_ErrorReporter(JSDContext* jsdc, JSDB_Data* data = (JSDB_Data*) callerdata; JS_ASSERT(data); + JS_BeginRequest(data->cxDebugger); if(data->jsErrorReporterHook && NULL != (fun = JS_ValueToFunction(data->cxDebugger, data->jsErrorReporterHook))) @@ -205,6 +215,7 @@ jsdb_ErrorReporter(JSDContext* jsdc, if(JS_ValueToInt32(data->cxDebugger, result, &answer)) ourRetVal = (uintN) answer; } + JS_EndRequest(data->cxDebugger); return ourRetVal; } @@ -405,11 +416,20 @@ _initReturn(const char* str, JSBool retv return retval; } +static JSBool +_initReturn_cx(const char* str, JSBool retval, JSContext* cx) +{ + JS_EndRequest(cx); + return _initReturn(str, retval); +} + #define MAX_DEBUGGER_DEPTH 3 JS_EXPORT_API(JSBool) JSDB_InitDebugger(JSRuntime* rt, JSDContext* jsdc, int depth) { + JSContext* cx; + JSObject* dbgObj; jsval rvalIgnore; static char load_deb[] = "load('debugger.js')"; @@ -424,35 +444,36 @@ JSDB_InitDebugger(JSRuntime* rt, JSDCont if(!(data->rtDebugger = JS_NewRuntime(8L * 1024L * 1024L))) return _initReturn("debugger runtime creation error", JS_FALSE); - if(!(data->cxDebugger = JS_NewContext(data->rtDebugger, 8192))) + if(!(data->cxDebugger = cx = JS_NewContext(data->rtDebugger, 8192))) return _initReturn("debugger creation error", JS_FALSE); - JS_SetContextPrivate(data->cxDebugger, data); + JS_SetContextPrivate(cx, data); - JS_SetErrorReporter(data->cxDebugger, _ErrorReporter); + JS_SetErrorReporter(cx, _ErrorReporter); - if(!(data->globDebugger = - JS_NewObject(data->cxDebugger, &debugger_global_class, NULL, NULL))) - return _initReturn("debugger global object creation error", JS_FALSE); + JS_BeginRequest(cx); + if(!(data->globDebugger = dbgObj = + JS_NewObject(cx, &debugger_global_class, NULL, NULL))) + return _initReturn_cx("debugger global object creation error", JS_FALSE, cx); - if(!JS_InitStandardClasses(data->cxDebugger, data->globDebugger)) - return _initReturn("debugger InitStandardClasses error", JS_FALSE); + if(!JS_InitStandardClasses(cx, dbgObj)) + return _initReturn_cx("debugger InitStandardClasses error", JS_FALSE, cx); - if(!JS_DefineFunctions(data->cxDebugger, data->globDebugger, debugger_functions)) - return _initReturn("debugger DefineFunctions error", JS_FALSE); + if(!JS_DefineFunctions(cx, dbgObj, debugger_functions)) + return _initReturn_cx("debugger DefineFunctions error", JS_FALSE, cx); if(!jsdb_ReflectJSD(data)) - return _initReturn("debugger reflection of JSD API error", JS_FALSE); + return _initReturn_cx("debugger reflection of JSD API error", JS_FALSE, cx); if(data->debuggerDepth < MAX_DEBUGGER_DEPTH) { JSDContext* local_jsdc; if(!(local_jsdc = JSD_DebuggerOnForUser(data->rtDebugger, NULL, NULL))) - return _initReturn("failed to create jsdc for nested debugger", - JS_FALSE); - JSD_JSContextInUse(local_jsdc, data->cxDebugger); + return _initReturn_cx("failed to create jsdc for nested debugger", + JS_FALSE, cx); + JSD_JSContextInUse(local_jsdc, cx); if(!JSDB_InitDebugger(data->rtDebugger, local_jsdc, data->debuggerDepth)) - return _initReturn("failed to init nested debugger", JS_FALSE); + return _initReturn_cx("failed to init nested debugger", JS_FALSE, cx); } JSD_SetScriptHook(jsdc, jsdb_ScriptHookProc, data); @@ -465,10 +486,10 @@ JSDB_InitDebugger(JSRuntime* rt, JSDCont JS_SetSourceHandler(data->rtDebugger, SendSourceToJSDebugger, jsdc); #endif /* JSD_LOWLEVEL_SOURCE */ - JS_EvaluateScript(data->cxDebugger, data->globDebugger, + JS_EvaluateScript(cx, dbgObj, load_deb, sizeof(load_deb)-1, "jsdb_autoload", 1, &rvalIgnore); - return _initReturn(NULL, JS_TRUE); + return _initReturn_cx(NULL, JS_TRUE, cx); }