michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: * JavaScript Debugging support - Script support michael@0: */ michael@0: michael@0: #include "jsd.h" michael@0: #include "jsfriendapi.h" michael@0: #include "nsCxPusher.h" michael@0: michael@0: using mozilla::AutoSafeJSContext; michael@0: michael@0: /* Comment this out to disable (NT specific) dumping as we go */ michael@0: /* michael@0: ** #ifdef DEBUG michael@0: ** #define JSD_DUMP 1 michael@0: ** #endif michael@0: */ michael@0: michael@0: #define NOT_SET_YET -1 michael@0: michael@0: /***************************************************************************/ michael@0: michael@0: #ifdef DEBUG michael@0: void JSD_ASSERT_VALID_SCRIPT(JSDScript* jsdscript) michael@0: { michael@0: MOZ_ASSERT(jsdscript); michael@0: MOZ_ASSERT(jsdscript->script); michael@0: } michael@0: void JSD_ASSERT_VALID_EXEC_HOOK(JSDExecHook* jsdhook) michael@0: { michael@0: MOZ_ASSERT(jsdhook); michael@0: MOZ_ASSERT(jsdhook->hook); michael@0: } michael@0: #endif michael@0: michael@0: static JSDScript* michael@0: _newJSDScript(JSDContext* jsdc, michael@0: JSContext *cx, michael@0: JSScript *script_) michael@0: { michael@0: JS::RootedScript script(cx, script_); michael@0: if ( JS_GetScriptIsSelfHosted(script) ) michael@0: return nullptr; michael@0: michael@0: JSDScript* jsdscript; michael@0: unsigned lineno; michael@0: const char* raw_filename; michael@0: michael@0: MOZ_ASSERT(JSD_SCRIPTS_LOCKED(jsdc)); michael@0: michael@0: /* these are inlined javascript: urls and we can't handle them now */ michael@0: lineno = (unsigned) JS_GetScriptBaseLineNumber(cx, script); michael@0: if( lineno == 0 ) michael@0: return nullptr; michael@0: michael@0: jsdscript = (JSDScript*) calloc(1, sizeof(JSDScript)); michael@0: if( ! jsdscript ) michael@0: return nullptr; michael@0: michael@0: raw_filename = JS_GetScriptFilename(script); michael@0: michael@0: JS_HashTableAdd(jsdc->scriptsTable, (void *)script, (void *)jsdscript); michael@0: JS_APPEND_LINK(&jsdscript->links, &jsdc->scripts); michael@0: jsdscript->jsdc = jsdc; michael@0: jsdscript->script = script; michael@0: jsdscript->lineBase = lineno; michael@0: jsdscript->lineExtent = (unsigned)NOT_SET_YET; michael@0: jsdscript->data = nullptr; michael@0: jsdscript->url = (char*) jsd_BuildNormalizedURL(raw_filename); michael@0: michael@0: JS_INIT_CLIST(&jsdscript->hooks); michael@0: michael@0: return jsdscript; michael@0: } michael@0: michael@0: static void michael@0: _destroyJSDScript(JSDContext* jsdc, michael@0: JSDScript* jsdscript) michael@0: { michael@0: MOZ_ASSERT(JSD_SCRIPTS_LOCKED(jsdc)); michael@0: michael@0: /* destroy all hooks */ michael@0: jsd_ClearAllExecutionHooksForScript(jsdc, jsdscript); michael@0: michael@0: JS_REMOVE_LINK(&jsdscript->links); michael@0: if(jsdscript->url) michael@0: free(jsdscript->url); michael@0: michael@0: if (jsdscript->profileData) michael@0: free(jsdscript->profileData); michael@0: michael@0: free(jsdscript); michael@0: } michael@0: michael@0: /***************************************************************************/ michael@0: michael@0: #ifdef JSD_DUMP michael@0: #ifndef XP_WIN michael@0: void michael@0: OutputDebugString (char *buf) michael@0: { michael@0: fprintf (stderr, "%s", buf); michael@0: } michael@0: #endif michael@0: michael@0: static void michael@0: _dumpJSDScript(JSDContext* jsdc, JSDScript* jsdscript, const char* leadingtext) michael@0: { michael@0: const char* name; michael@0: JSString* fun; michael@0: unsigned base; michael@0: unsigned extent; michael@0: char Buf[256]; michael@0: size_t n; michael@0: michael@0: name = jsd_GetScriptFilename(jsdc, jsdscript); michael@0: fun = jsd_GetScriptFunctionId(jsdc, jsdscript); michael@0: base = jsd_GetScriptBaseLineNumber(jsdc, jsdscript); michael@0: extent = jsd_GetScriptLineExtent(jsdc, jsdscript); michael@0: n = size_t(snprintf(Buf, sizeof(Buf), "%sscript=%08X, %s, ", michael@0: leadingtext, (unsigned) jsdscript->script, michael@0: name ? name : "no URL")); michael@0: if (n + 1 < sizeof(Buf)) { michael@0: if (fun) { michael@0: n += size_t(snprintf(Buf + n, sizeof(Buf) - n, "%s", "no fun")); michael@0: } else { michael@0: n += JS_PutEscapedFlatString(Buf + n, sizeof(Buf) - n, michael@0: MOZ_ASSERT_STRING_IS_FLAT(fun), 0); michael@0: Buf[sizeof(Buf) - 1] = '\0'; michael@0: } michael@0: if (n + 1 < sizeof(Buf)) michael@0: snprintf(Buf + n, sizeof(Buf) - n, ", %d-%d\n", base, base + extent - 1); michael@0: } michael@0: OutputDebugString( Buf ); michael@0: } michael@0: michael@0: static void michael@0: _dumpJSDScriptList( JSDContext* jsdc ) michael@0: { michael@0: JSDScript* iterp = nullptr; michael@0: JSDScript* jsdscript = nullptr; michael@0: michael@0: OutputDebugString( "*** JSDScriptDump\n" ); michael@0: while( nullptr != (jsdscript = jsd_IterateScripts(jsdc, &iterp)) ) michael@0: _dumpJSDScript( jsdc, jsdscript, " script: " ); michael@0: } michael@0: #endif /* JSD_DUMP */ michael@0: michael@0: /***************************************************************************/ michael@0: static JSHashNumber michael@0: jsd_hash_script(const void *key) michael@0: { michael@0: return ((JSHashNumber)(ptrdiff_t) key) >> 2; /* help lame MSVC1.5 on Win16 */ michael@0: } michael@0: michael@0: static void * michael@0: jsd_alloc_script_table(void *priv, size_t size) michael@0: { michael@0: return malloc(size); michael@0: } michael@0: michael@0: static void michael@0: jsd_free_script_table(void *priv, void *item, size_t size) michael@0: { michael@0: free(item); michael@0: } michael@0: michael@0: static JSHashEntry * michael@0: jsd_alloc_script_entry(void *priv, const void *item) michael@0: { michael@0: return (JSHashEntry*) malloc(sizeof(JSHashEntry)); michael@0: } michael@0: michael@0: static void michael@0: jsd_free_script_entry(void *priv, JSHashEntry *he, unsigned flag) michael@0: { michael@0: if (flag == HT_FREE_ENTRY) michael@0: { michael@0: _destroyJSDScript((JSDContext*) priv, (JSDScript*) he->value); michael@0: free(he); michael@0: } michael@0: } michael@0: michael@0: static const JSHashAllocOps script_alloc_ops = { michael@0: jsd_alloc_script_table, jsd_free_script_table, michael@0: jsd_alloc_script_entry, jsd_free_script_entry michael@0: }; michael@0: michael@0: #ifndef JSD_SCRIPT_HASH_SIZE michael@0: #define JSD_SCRIPT_HASH_SIZE 1024 michael@0: #endif michael@0: michael@0: bool michael@0: jsd_InitScriptManager(JSDContext* jsdc) michael@0: { michael@0: JS_INIT_CLIST(&jsdc->scripts); michael@0: jsdc->scriptsTable = JS_NewHashTable(JSD_SCRIPT_HASH_SIZE, jsd_hash_script, michael@0: JS_CompareValues, JS_CompareValues, michael@0: &script_alloc_ops, (void*) jsdc); michael@0: return !!jsdc->scriptsTable; michael@0: } michael@0: michael@0: void michael@0: jsd_DestroyScriptManager(JSDContext* jsdc) michael@0: { michael@0: JSD_LOCK_SCRIPTS(jsdc); michael@0: if (jsdc->scriptsTable) michael@0: JS_HashTableDestroy(jsdc->scriptsTable); michael@0: JSD_UNLOCK_SCRIPTS(jsdc); michael@0: } michael@0: michael@0: JSDScript* michael@0: jsd_FindJSDScript( JSDContext* jsdc, michael@0: JSScript *script ) michael@0: { michael@0: MOZ_ASSERT(JSD_SCRIPTS_LOCKED(jsdc)); michael@0: return (JSDScript*) JS_HashTableLookup(jsdc->scriptsTable, (void *)script); michael@0: } michael@0: michael@0: JSDScript * michael@0: jsd_FindOrCreateJSDScript(JSDContext *jsdc, michael@0: JSContext *cx, michael@0: JSScript *script_, michael@0: JSAbstractFramePtr frame) michael@0: { michael@0: JS::RootedScript script(cx, script_); michael@0: JSDScript *jsdscript; michael@0: MOZ_ASSERT(JSD_SCRIPTS_LOCKED(jsdc)); michael@0: michael@0: jsdscript = jsd_FindJSDScript(jsdc, script); michael@0: if (jsdscript) michael@0: return jsdscript; michael@0: michael@0: /* Fallback for unknown scripts: create a new script. */ michael@0: if (!frame) { michael@0: JSBrokenFrameIterator iter(cx); michael@0: if (!iter.done()) michael@0: frame = iter.abstractFramePtr(); michael@0: } michael@0: if (frame) michael@0: jsdscript = _newJSDScript(jsdc, cx, script); michael@0: michael@0: return jsdscript; michael@0: } michael@0: michael@0: JSDProfileData* michael@0: jsd_GetScriptProfileData(JSDContext* jsdc, JSDScript *script) michael@0: { michael@0: if (!script->profileData) michael@0: script->profileData = (JSDProfileData*)calloc(1, sizeof(JSDProfileData)); michael@0: michael@0: return script->profileData; michael@0: } michael@0: michael@0: uint32_t michael@0: jsd_GetScriptFlags(JSDContext *jsdc, JSDScript *script) michael@0: { michael@0: return script->flags; michael@0: } michael@0: michael@0: void michael@0: jsd_SetScriptFlags(JSDContext *jsdc, JSDScript *script, uint32_t flags) michael@0: { michael@0: script->flags = flags; michael@0: } michael@0: michael@0: unsigned michael@0: jsd_GetScriptCallCount(JSDContext* jsdc, JSDScript *script) michael@0: { michael@0: if (script->profileData) michael@0: return script->profileData->callCount; michael@0: michael@0: return 0; michael@0: } michael@0: michael@0: unsigned michael@0: jsd_GetScriptMaxRecurseDepth(JSDContext* jsdc, JSDScript *script) michael@0: { michael@0: if (script->profileData) michael@0: return script->profileData->maxRecurseDepth; michael@0: michael@0: return 0; michael@0: } michael@0: michael@0: double michael@0: jsd_GetScriptMinExecutionTime(JSDContext* jsdc, JSDScript *script) michael@0: { michael@0: if (script->profileData) michael@0: return script->profileData->minExecutionTime; michael@0: michael@0: return 0.0; michael@0: } michael@0: michael@0: double michael@0: jsd_GetScriptMaxExecutionTime(JSDContext* jsdc, JSDScript *script) michael@0: { michael@0: if (script->profileData) michael@0: return script->profileData->maxExecutionTime; michael@0: michael@0: return 0.0; michael@0: } michael@0: michael@0: double michael@0: jsd_GetScriptTotalExecutionTime(JSDContext* jsdc, JSDScript *script) michael@0: { michael@0: if (script->profileData) michael@0: return script->profileData->totalExecutionTime; michael@0: michael@0: return 0.0; michael@0: } michael@0: michael@0: double michael@0: jsd_GetScriptMinOwnExecutionTime(JSDContext* jsdc, JSDScript *script) michael@0: { michael@0: if (script->profileData) michael@0: return script->profileData->minOwnExecutionTime; michael@0: michael@0: return 0.0; michael@0: } michael@0: michael@0: double michael@0: jsd_GetScriptMaxOwnExecutionTime(JSDContext* jsdc, JSDScript *script) michael@0: { michael@0: if (script->profileData) michael@0: return script->profileData->maxOwnExecutionTime; michael@0: michael@0: return 0.0; michael@0: } michael@0: michael@0: double michael@0: jsd_GetScriptTotalOwnExecutionTime(JSDContext* jsdc, JSDScript *script) michael@0: { michael@0: if (script->profileData) michael@0: return script->profileData->totalOwnExecutionTime; michael@0: michael@0: return 0.0; michael@0: } michael@0: michael@0: void michael@0: jsd_ClearScriptProfileData(JSDContext* jsdc, JSDScript *script) michael@0: { michael@0: if (script->profileData) michael@0: { michael@0: free(script->profileData); michael@0: script->profileData = nullptr; michael@0: } michael@0: } michael@0: michael@0: JSScript * michael@0: jsd_GetJSScript (JSDContext *jsdc, JSDScript *script) michael@0: { michael@0: return script->script; michael@0: } michael@0: michael@0: JSFunction * michael@0: jsd_GetJSFunction (JSDContext *jsdc, JSDScript *script) michael@0: { michael@0: AutoSafeJSContext cx; michael@0: return JS_GetScriptFunction(cx, script->script); michael@0: } michael@0: michael@0: JSDScript* michael@0: jsd_IterateScripts(JSDContext* jsdc, JSDScript **iterp) michael@0: { michael@0: JSDScript *jsdscript = *iterp; michael@0: michael@0: MOZ_ASSERT(JSD_SCRIPTS_LOCKED(jsdc)); michael@0: michael@0: if( !jsdscript ) michael@0: jsdscript = (JSDScript *)jsdc->scripts.next; michael@0: if( jsdscript == (JSDScript *)&jsdc->scripts ) michael@0: return nullptr; michael@0: *iterp = (JSDScript*) jsdscript->links.next; michael@0: return jsdscript; michael@0: } michael@0: michael@0: void * michael@0: jsd_SetScriptPrivate(JSDScript *jsdscript, void *data) michael@0: { michael@0: void *rval = jsdscript->data; michael@0: jsdscript->data = data; michael@0: return rval; michael@0: } michael@0: michael@0: void * michael@0: jsd_GetScriptPrivate(JSDScript *jsdscript) michael@0: { michael@0: return jsdscript->data; michael@0: } michael@0: michael@0: bool michael@0: jsd_IsActiveScript(JSDContext* jsdc, JSDScript *jsdscript) michael@0: { michael@0: JSDScript *current; michael@0: michael@0: MOZ_ASSERT(JSD_SCRIPTS_LOCKED(jsdc)); michael@0: michael@0: for( current = (JSDScript *)jsdc->scripts.next; michael@0: current != (JSDScript *)&jsdc->scripts; michael@0: current = (JSDScript *)current->links.next ) michael@0: { michael@0: if(jsdscript == current) michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: const char* michael@0: jsd_GetScriptFilename(JSDContext* jsdc, JSDScript *jsdscript) michael@0: { michael@0: return jsdscript->url; michael@0: } michael@0: michael@0: JSString* michael@0: jsd_GetScriptFunctionId(JSDContext* jsdc, JSDScript *jsdscript) michael@0: { michael@0: JSString* str; michael@0: JSFunction *fun = jsd_GetJSFunction(jsdc, jsdscript); michael@0: michael@0: if( ! fun ) michael@0: return nullptr; michael@0: str = JS_GetFunctionId(fun); michael@0: michael@0: /* For compatibility we return "anonymous", not an empty string here. */ michael@0: return str ? str : JS_GetAnonymousString(jsdc->jsrt); michael@0: } michael@0: michael@0: unsigned michael@0: jsd_GetScriptBaseLineNumber(JSDContext* jsdc, JSDScript *jsdscript) michael@0: { michael@0: return jsdscript->lineBase; michael@0: } michael@0: michael@0: unsigned michael@0: jsd_GetScriptLineExtent(JSDContext* jsdc, JSDScript *jsdscript) michael@0: { michael@0: AutoSafeJSContext cx; michael@0: JSAutoCompartment ac(cx, jsdc->glob); // Just in case. michael@0: if( NOT_SET_YET == (int)jsdscript->lineExtent ) michael@0: jsdscript->lineExtent = JS_GetScriptLineExtent(cx, jsdscript->script); michael@0: return jsdscript->lineExtent; michael@0: } michael@0: michael@0: uintptr_t michael@0: jsd_GetClosestPC(JSDContext* jsdc, JSDScript* jsdscript, unsigned line) michael@0: { michael@0: uintptr_t pc; michael@0: michael@0: if( !jsdscript ) michael@0: return 0; michael@0: michael@0: AutoSafeJSContext cx; michael@0: JSAutoCompartment ac(cx, jsdscript->script); michael@0: pc = (uintptr_t) JS_LineNumberToPC(cx, jsdscript->script, line ); michael@0: return pc; michael@0: } michael@0: michael@0: unsigned michael@0: jsd_GetClosestLine(JSDContext* jsdc, JSDScript* jsdscript, uintptr_t pc) michael@0: { michael@0: unsigned first = jsdscript->lineBase; michael@0: unsigned last = first + jsd_GetScriptLineExtent(jsdc, jsdscript) - 1; michael@0: unsigned line = 0; michael@0: michael@0: if (pc) { michael@0: AutoSafeJSContext cx; michael@0: JSAutoCompartment ac(cx, jsdscript->script); michael@0: line = JS_PCToLineNumber(cx, jsdscript->script, (jsbytecode*)pc); michael@0: } michael@0: michael@0: if( line < first ) michael@0: return first; michael@0: if( line > last ) michael@0: return last; michael@0: michael@0: return line; michael@0: } michael@0: michael@0: bool michael@0: jsd_GetLinePCs(JSDContext* jsdc, JSDScript* jsdscript, michael@0: unsigned startLine, unsigned maxLines, michael@0: unsigned* count, unsigned** retLines, uintptr_t** retPCs) michael@0: { michael@0: unsigned first = jsdscript->lineBase; michael@0: unsigned last = first + jsd_GetScriptLineExtent(jsdc, jsdscript) - 1; michael@0: bool ok; michael@0: jsbytecode **pcs; michael@0: unsigned i; michael@0: michael@0: if (last < startLine) michael@0: return true; michael@0: michael@0: AutoSafeJSContext cx; michael@0: JSAutoCompartment ac(cx, jsdscript->script); michael@0: michael@0: ok = JS_GetLinePCs(cx, jsdscript->script, michael@0: startLine, maxLines, michael@0: count, retLines, &pcs); michael@0: michael@0: if (ok) { michael@0: if (retPCs) { michael@0: for (i = 0; i < *count; ++i) { michael@0: (*retPCs)[i] = (*pcs)[i]; michael@0: } michael@0: } michael@0: michael@0: JS_free(cx, pcs); michael@0: } michael@0: michael@0: return ok; michael@0: } michael@0: michael@0: bool michael@0: jsd_SetScriptHook(JSDContext* jsdc, JSD_ScriptHookProc hook, void* callerdata) michael@0: { michael@0: JSD_LOCK(); michael@0: jsdc->scriptHook = hook; michael@0: jsdc->scriptHookData = callerdata; michael@0: JSD_UNLOCK(); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: jsd_GetScriptHook(JSDContext* jsdc, JSD_ScriptHookProc* hook, void** callerdata) michael@0: { michael@0: JSD_LOCK(); michael@0: if( hook ) michael@0: *hook = jsdc->scriptHook; michael@0: if( callerdata ) michael@0: *callerdata = jsdc->scriptHookData; michael@0: JSD_UNLOCK(); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: jsd_EnableSingleStepInterrupts(JSDContext* jsdc, JSDScript* jsdscript, bool enable) michael@0: { michael@0: bool rv; michael@0: AutoSafeJSContext cx; michael@0: JS::RootedScript script(cx, jsdscript->script); michael@0: JSAutoCompartment ac(cx, script); michael@0: JSD_LOCK(); michael@0: rv = JS_SetSingleStepMode(cx, script, enable); michael@0: JSD_UNLOCK(); michael@0: return rv; michael@0: } michael@0: michael@0: michael@0: /***************************************************************************/ michael@0: michael@0: void michael@0: jsd_NewScriptHookProc( michael@0: JSContext *cx, michael@0: const char *filename, /* URL this script loads from */ michael@0: unsigned lineno, /* line where this script starts */ michael@0: JSScript *script, michael@0: JSFunction *fun, michael@0: void* callerdata ) michael@0: { michael@0: JSDScript* jsdscript = nullptr; michael@0: JSDContext* jsdc = (JSDContext*) callerdata; michael@0: JSD_ScriptHookProc hook; michael@0: void* hookData; michael@0: michael@0: JSD_ASSERT_VALID_CONTEXT(jsdc); michael@0: michael@0: if( JSD_IS_DANGEROUS_THREAD(jsdc) ) michael@0: return; michael@0: michael@0: JSD_LOCK_SCRIPTS(jsdc); michael@0: jsdscript = _newJSDScript(jsdc, cx, script); michael@0: JSD_UNLOCK_SCRIPTS(jsdc); michael@0: if( ! jsdscript ) michael@0: return; michael@0: michael@0: #ifdef JSD_DUMP michael@0: JSD_LOCK_SCRIPTS(jsdc); michael@0: _dumpJSDScript(jsdc, jsdscript, "***NEW Script: "); michael@0: _dumpJSDScriptList( jsdc ); michael@0: JSD_UNLOCK_SCRIPTS(jsdc); michael@0: #endif /* JSD_DUMP */ michael@0: michael@0: /* local in case jsdc->scriptHook gets cleared on another thread */ michael@0: JSD_LOCK(); michael@0: hook = jsdc->scriptHook; michael@0: if( hook ) michael@0: jsdscript->flags = jsdscript->flags | JSD_SCRIPT_CALL_DESTROY_HOOK_BIT; michael@0: hookData = jsdc->scriptHookData; michael@0: JSD_UNLOCK(); michael@0: michael@0: if( hook ) michael@0: hook(jsdc, jsdscript, true, hookData); michael@0: } michael@0: michael@0: void michael@0: jsd_DestroyScriptHookProc( michael@0: JSFreeOp *fop, michael@0: JSScript *script_, michael@0: void* callerdata ) michael@0: { michael@0: JSDScript* jsdscript = nullptr; michael@0: JSDContext* jsdc = (JSDContext*) callerdata; michael@0: // NB: We're called during GC, so we can't push a cx. Root directly with michael@0: // the runtime. michael@0: JS::RootedScript script(jsdc->jsrt, script_); michael@0: JSD_ScriptHookProc hook; michael@0: void* hookData; michael@0: michael@0: JSD_ASSERT_VALID_CONTEXT(jsdc); michael@0: michael@0: if( JSD_IS_DANGEROUS_THREAD(jsdc) ) michael@0: return; michael@0: michael@0: JSD_LOCK_SCRIPTS(jsdc); michael@0: jsdscript = jsd_FindJSDScript(jsdc, script); michael@0: JSD_UNLOCK_SCRIPTS(jsdc); michael@0: michael@0: if( ! jsdscript ) michael@0: return; michael@0: michael@0: #ifdef JSD_DUMP michael@0: JSD_LOCK_SCRIPTS(jsdc); michael@0: _dumpJSDScript(jsdc, jsdscript, "***DESTROY Script: "); michael@0: JSD_UNLOCK_SCRIPTS(jsdc); michael@0: #endif /* JSD_DUMP */ michael@0: michael@0: /* local in case hook gets cleared on another thread */ michael@0: JSD_LOCK(); michael@0: hook = (jsdscript->flags & JSD_SCRIPT_CALL_DESTROY_HOOK_BIT) ? jsdc->scriptHook michael@0: : nullptr; michael@0: hookData = jsdc->scriptHookData; michael@0: JSD_UNLOCK(); michael@0: michael@0: if( hook ) michael@0: hook(jsdc, jsdscript, false, hookData); michael@0: michael@0: JSD_LOCK_SCRIPTS(jsdc); michael@0: JS_HashTableRemove(jsdc->scriptsTable, (void *)script); michael@0: JSD_UNLOCK_SCRIPTS(jsdc); michael@0: michael@0: #ifdef JSD_DUMP michael@0: JSD_LOCK_SCRIPTS(jsdc); michael@0: _dumpJSDScriptList(jsdc); michael@0: JSD_UNLOCK_SCRIPTS(jsdc); michael@0: #endif /* JSD_DUMP */ michael@0: } michael@0: michael@0: michael@0: /***************************************************************************/ michael@0: michael@0: static JSDExecHook* michael@0: _findHook(JSDContext* jsdc, JSDScript* jsdscript, uintptr_t pc) michael@0: { michael@0: JSDExecHook* jsdhook; michael@0: JSCList* list = &jsdscript->hooks; michael@0: michael@0: for( jsdhook = (JSDExecHook*)list->next; michael@0: jsdhook != (JSDExecHook*)list; michael@0: jsdhook = (JSDExecHook*)jsdhook->links.next ) michael@0: { michael@0: if (jsdhook->pc == pc) michael@0: return jsdhook; michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: static bool michael@0: _isActiveHook(JSDContext* jsdc, JSScript *script, JSDExecHook* jsdhook) michael@0: { michael@0: JSDExecHook* current; michael@0: JSCList* list; michael@0: JSDScript* jsdscript; michael@0: michael@0: JSD_LOCK_SCRIPTS(jsdc); michael@0: jsdscript = jsd_FindJSDScript(jsdc, script); michael@0: if( ! jsdscript) michael@0: { michael@0: JSD_UNLOCK_SCRIPTS(jsdc); michael@0: return false; michael@0: } michael@0: michael@0: list = &jsdscript->hooks; michael@0: michael@0: for( current = (JSDExecHook*)list->next; michael@0: current != (JSDExecHook*)list; michael@0: current = (JSDExecHook*)current->links.next ) michael@0: { michael@0: if(current == jsdhook) michael@0: { michael@0: JSD_UNLOCK_SCRIPTS(jsdc); michael@0: return true; michael@0: } michael@0: } michael@0: JSD_UNLOCK_SCRIPTS(jsdc); michael@0: return false; michael@0: } michael@0: michael@0: michael@0: JSTrapStatus michael@0: jsd_TrapHandler(JSContext *cx, JSScript *script_, jsbytecode *pc, jsval *rval, michael@0: jsval closure) michael@0: { michael@0: JS::RootedScript script(cx, script_); michael@0: JSDExecHook* jsdhook = (JSDExecHook*) JSVAL_TO_PRIVATE(closure); michael@0: JSD_ExecutionHookProc hook; michael@0: void* hookData; michael@0: JSDContext* jsdc; michael@0: michael@0: JSD_LOCK(); michael@0: michael@0: if( nullptr == (jsdc = jsd_JSDContextForJSContext(cx)) || michael@0: ! _isActiveHook(jsdc, script, jsdhook) ) michael@0: { michael@0: JSD_UNLOCK(); michael@0: return JSTRAP_CONTINUE; michael@0: } michael@0: michael@0: JSD_ASSERT_VALID_EXEC_HOOK(jsdhook); michael@0: MOZ_ASSERT(!jsdhook->pc || jsdhook->pc == (uintptr_t)pc); michael@0: MOZ_ASSERT(jsdhook->jsdscript->script == script); michael@0: MOZ_ASSERT(jsdhook->jsdscript->jsdc == jsdc); michael@0: michael@0: hook = jsdhook->hook; michael@0: hookData = jsdhook->callerdata; michael@0: michael@0: /* do not use jsdhook-> after this point */ michael@0: JSD_UNLOCK(); michael@0: michael@0: if( ! jsdc || ! jsdc->inited ) michael@0: return JSTRAP_CONTINUE; michael@0: michael@0: if( JSD_IS_DANGEROUS_THREAD(jsdc) ) michael@0: return JSTRAP_CONTINUE; michael@0: michael@0: return jsd_CallExecutionHook(jsdc, cx, JSD_HOOK_BREAKPOINT, michael@0: hook, hookData, rval); michael@0: } michael@0: michael@0: michael@0: michael@0: bool michael@0: jsd_SetExecutionHook(JSDContext* jsdc, michael@0: JSDScript* jsdscript, michael@0: uintptr_t pc, michael@0: JSD_ExecutionHookProc hook, michael@0: void* callerdata) michael@0: { michael@0: JSDExecHook* jsdhook; michael@0: bool rv; michael@0: michael@0: JSD_LOCK(); michael@0: if( ! hook ) michael@0: { michael@0: jsd_ClearExecutionHook(jsdc, jsdscript, pc); michael@0: JSD_UNLOCK(); michael@0: return true; michael@0: } michael@0: michael@0: jsdhook = _findHook(jsdc, jsdscript, pc); michael@0: if( jsdhook ) michael@0: { michael@0: jsdhook->hook = hook; michael@0: jsdhook->callerdata = callerdata; michael@0: JSD_UNLOCK(); michael@0: return true; michael@0: } michael@0: /* else... */ michael@0: michael@0: jsdhook = (JSDExecHook*)calloc(1, sizeof(JSDExecHook)); michael@0: if( ! jsdhook ) { michael@0: JSD_UNLOCK(); michael@0: return false; michael@0: } michael@0: jsdhook->jsdscript = jsdscript; michael@0: jsdhook->pc = pc; michael@0: jsdhook->hook = hook; michael@0: jsdhook->callerdata = callerdata; michael@0: michael@0: { michael@0: AutoSafeJSContext cx; michael@0: JSAutoCompartment ac(cx, jsdscript->script); michael@0: JS::RootedScript script(cx, jsdscript->script); michael@0: JS::RootedValue hookValue(cx, PRIVATE_TO_JSVAL(jsdhook)); michael@0: rv = JS_SetTrap(cx, script, (jsbytecode*)pc, jsd_TrapHandler, hookValue); michael@0: } michael@0: michael@0: if ( ! rv ) { michael@0: free(jsdhook); michael@0: JSD_UNLOCK(); michael@0: return false; michael@0: } michael@0: michael@0: JS_APPEND_LINK(&jsdhook->links, &jsdscript->hooks); michael@0: JSD_UNLOCK(); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: jsd_ClearExecutionHook(JSDContext* jsdc, michael@0: JSDScript* jsdscript, michael@0: uintptr_t pc) michael@0: { michael@0: JSDExecHook* jsdhook; michael@0: michael@0: JSD_LOCK(); michael@0: michael@0: jsdhook = _findHook(jsdc, jsdscript, pc); michael@0: if( ! jsdhook ) michael@0: { michael@0: JSD_UNLOCK(); michael@0: return false; michael@0: } michael@0: michael@0: { michael@0: AutoSafeJSContext cx; michael@0: JSAutoCompartment ac(cx, jsdscript->script); michael@0: JS_ClearTrap(cx, jsdscript->script, michael@0: (jsbytecode*)pc, nullptr, nullptr); michael@0: } michael@0: michael@0: JS_REMOVE_LINK(&jsdhook->links); michael@0: free(jsdhook); michael@0: michael@0: JSD_UNLOCK(); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: jsd_ClearAllExecutionHooksForScript(JSDContext* jsdc, JSDScript* jsdscript) michael@0: { michael@0: JSDExecHook* jsdhook; michael@0: JSCList* list = &jsdscript->hooks; michael@0: JSD_LOCK(); michael@0: michael@0: while( (JSDExecHook*)list != (jsdhook = (JSDExecHook*)list->next) ) michael@0: { michael@0: JS_REMOVE_LINK(&jsdhook->links); michael@0: free(jsdhook); michael@0: } michael@0: michael@0: JS_ClearScriptTraps(jsdc->jsrt, jsdscript->script); michael@0: JSD_UNLOCK(); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: jsd_ClearAllExecutionHooks(JSDContext* jsdc) michael@0: { michael@0: JSDScript* jsdscript; michael@0: JSDScript* iterp = nullptr; michael@0: michael@0: JSD_LOCK(); michael@0: while( nullptr != (jsdscript = jsd_IterateScripts(jsdc, &iterp)) ) michael@0: jsd_ClearAllExecutionHooksForScript(jsdc, jsdscript); michael@0: JSD_UNLOCK(); michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: jsd_ScriptCreated(JSDContext* jsdc, michael@0: JSContext *cx, michael@0: const char *filename, /* URL this script loads from */ michael@0: unsigned lineno, /* line where this script starts */ michael@0: JSScript *script, michael@0: JSFunction *fun) michael@0: { michael@0: jsd_NewScriptHookProc(cx, filename, lineno, script, fun, jsdc); michael@0: } michael@0: michael@0: void michael@0: jsd_ScriptDestroyed(JSDContext* jsdc, michael@0: JSFreeOp *fop, michael@0: JSScript *script) michael@0: { michael@0: jsd_DestroyScriptHookProc(fop, script, jsdc); michael@0: }