1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
24 * Alternatively, the contents of this file may be used under the terms of
25 * either the GNU General Public License Version 2 or later (the "GPL"), or
26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
39 * JavaScript Debugging support - Script support
43 #include "jsfriendapi.h"
45 /* Comment this out to disable (NT specific) dumping as we go */
52 #define NOT_SET_YET -1
54 /***************************************************************************/
57 void JSD_ASSERT_VALID_SCRIPT(JSDScript* jsdscript)
60 JS_ASSERT(jsdscript->script);
62 void JSD_ASSERT_VALID_EXEC_HOOK(JSDExecHook* jsdhook)
65 JS_ASSERT(jsdhook->hook);
71 HasFileExtention(const char* name, const char* ext)
74 int len = strlen(ext);
75 const char* p = strrchr(name,'.');
79 for(i = 0; i < len; i++ )
81 JS_ASSERT(islower(ext[i]));
82 if( 0 == p[i] || tolower(p[i]) != ext[i] )
92 _newJSDScript(JSDContext* jsdc,
99 const char* raw_filename;
101 JS_ASSERT(JSD_SCRIPTS_LOCKED(jsdc));
103 /* these are inlined javascript: urls and we can't handle them now */
104 lineno = (uintN) JS_GetScriptBaseLineNumber(cx, script);
108 jsdscript = (JSDScript*) calloc(1, sizeof(JSDScript));
112 raw_filename = JS_GetScriptFilename(cx,script);
114 JS_HashTableAdd(jsdc->scriptsTable, (void *)script, (void *)jsdscript);
115 JS_APPEND_LINK(&jsdscript->links, &jsdc->scripts);
116 jsdscript->jsdc = jsdc;
117 jsdscript->script = script;
118 jsdscript->function = function;
119 jsdscript->lineBase = lineno;
120 jsdscript->lineExtent = (uintN)NOT_SET_YET;
121 jsdscript->data = NULL;
123 jsdscript->url = (char*) jsd_BuildNormalizedURL(raw_filename);
125 jsdscript->app = LWDBG_GetCurrentApp();
126 if( jsdscript->app && raw_filename )
128 jsdscript->url = jsdlw_BuildAppRelativeFilename(jsdscript->app, raw_filename);
131 JSString* funid = JS_GetFunctionId(function);
133 const char* funnanme;
136 funbytes = JS_EncodeString(cx, funid);
137 funname = funbytes ? funbytes : "";
142 funname = "anonymous";
144 jsdscript->lwscript =
145 LWDBG_GetScriptOfFunction(jsdscript->app,funname);
146 JS_Free(cx, funbytes);
148 /* also, make sure this file is added to filelist if is .js file */
149 if( HasFileExtention(raw_filename,"js") ||
150 HasFileExtention(raw_filename,"sjs") )
152 jsdlw_PreLoadSource(jsdc, jsdscript->app, raw_filename, JS_FALSE);
157 jsdscript->lwscript = LWDBG_GetCurrentTopLevelScript();
162 JS_INIT_CLIST(&jsdscript->hooks);
168 _destroyJSDScript(JSDContext* jsdc,
169 JSDScript* jsdscript)
171 JS_ASSERT(JSD_SCRIPTS_LOCKED(jsdc));
173 /* destroy all hooks */
174 jsd_ClearAllExecutionHooksForScript(jsdc, jsdscript);
176 JS_REMOVE_LINK(&jsdscript->links);
178 free(jsdscript->url);
180 if (jsdscript->profileData)
181 free(jsdscript->profileData);
186 /***************************************************************************/
191 OutputDebugString (char *buf)
193 fprintf (stderr, "%s", buf);
198 _dumpJSDScript(JSDContext* jsdc, JSDScript* jsdscript, const char* leadingtext)
207 name = jsd_GetScriptFilename(jsdc, jsdscript);
208 fun = jsd_GetScriptFunctionId(jsdc, jsdscript);
209 base = jsd_GetScriptBaseLineNumber(jsdc, jsdscript);
210 extent = jsd_GetScriptLineExtent(jsdc, jsdscript);
211 n = size_t(snprintf(Buf, sizeof(Buf), "%sscript=%08X, %s, ",
212 leadingtext, (unsigned) jsdscript->script,
213 name ? name : "no URL"));
214 if (n + 1 < sizeof(Buf)) {
216 n += size_t(snprintf(Buf + n, sizeof(Buf) - n, "%s", "no fun"));
218 n += JS_PutEscapedFlatString(Buf + n, sizeof(Buf) - n,
219 JS_ASSERT_STRING_IS_FLAT(fun), 0);
220 Buf[sizeof(Buf) - 1] = '\0';
222 if (n + 1 < sizeof(Buf))
223 snprintf(Buf + n, sizeof(Buf) - n, ", %d-%d\n", base, base + extent - 1);
225 OutputDebugString( Buf );
229 _dumpJSDScriptList( JSDContext* jsdc )
231 JSDScript* iterp = NULL;
232 JSDScript* jsdscript = NULL;
234 OutputDebugString( "*** JSDScriptDump\n" );
235 while( NULL != (jsdscript = jsd_IterateScripts(jsdc, &iterp)) )
236 _dumpJSDScript( jsdc, jsdscript, " script: " );
238 #endif /* JSD_DUMP */
240 /***************************************************************************/
242 jsd_hash_script(const void *key)
244 return ((JSHashNumber)(ptrdiff_t) key) >> 2; /* help lame MSVC1.5 on Win16 */
248 jsd_alloc_script_table(void *priv, size_t size)
254 jsd_free_script_table(void *priv, void *item, size_t size)
260 jsd_alloc_script_entry(void *priv, const void *item)
262 return (JSHashEntry*) malloc(sizeof(JSHashEntry));
266 jsd_free_script_entry(void *priv, JSHashEntry *he, uintN flag)
268 if (flag == HT_FREE_ENTRY)
270 _destroyJSDScript((JSDContext*) priv, (JSDScript*) he->value);
275 static JSHashAllocOps script_alloc_ops = {
276 jsd_alloc_script_table, jsd_free_script_table,
277 jsd_alloc_script_entry, jsd_free_script_entry
280 #ifndef JSD_SCRIPT_HASH_SIZE
281 #define JSD_SCRIPT_HASH_SIZE 1024
285 jsd_InitScriptManager(JSDContext* jsdc)
287 JS_INIT_CLIST(&jsdc->scripts);
288 jsdc->scriptsTable = JS_NewHashTable(JSD_SCRIPT_HASH_SIZE, jsd_hash_script,
289 JS_CompareValues, JS_CompareValues,
290 &script_alloc_ops, (void*) jsdc);
291 return !!jsdc->scriptsTable;
295 jsd_DestroyScriptManager(JSDContext* jsdc)
297 JSD_LOCK_SCRIPTS(jsdc);
298 if (jsdc->scriptsTable)
299 JS_HashTableDestroy(jsdc->scriptsTable);
300 JSD_UNLOCK_SCRIPTS(jsdc);
304 jsd_FindJSDScript( JSDContext* jsdc,
307 JS_ASSERT(JSD_SCRIPTS_LOCKED(jsdc));
308 return (JSDScript*) JS_HashTableLookup(jsdc->scriptsTable, (void *)script);
312 jsd_FindOrCreateJSDScript(JSDContext *jsdc,
317 JSDScript *jsdscript;
318 JS_ASSERT(JSD_SCRIPTS_LOCKED(jsdc));
320 jsdscript = jsd_FindJSDScript(jsdc, script);
324 /* Fallback for unknown scripts: create a new script. */
326 JS_FrameIterator(cx, &fp);
328 jsdscript = _newJSDScript(jsdc, cx, script, JS_GetFrameFunction(cx, fp));
334 jsd_GetScriptProfileData(JSDContext* jsdc, JSDScript *script)
336 if (!script->profileData)
337 script->profileData = (JSDProfileData*)calloc(1, sizeof(JSDProfileData));
339 return script->profileData;
343 jsd_GetScriptFlags(JSDContext *jsdc, JSDScript *script)
345 return script->flags;
349 jsd_SetScriptFlags(JSDContext *jsdc, JSDScript *script, uint32 flags)
351 script->flags = flags;
355 jsd_GetScriptCallCount(JSDContext* jsdc, JSDScript *script)
357 if (script->profileData)
358 return script->profileData->callCount;
364 jsd_GetScriptMaxRecurseDepth(JSDContext* jsdc, JSDScript *script)
366 if (script->profileData)
367 return script->profileData->maxRecurseDepth;
373 jsd_GetScriptMinExecutionTime(JSDContext* jsdc, JSDScript *script)
375 if (script->profileData)
376 return script->profileData->minExecutionTime;
382 jsd_GetScriptMaxExecutionTime(JSDContext* jsdc, JSDScript *script)
384 if (script->profileData)
385 return script->profileData->maxExecutionTime;
391 jsd_GetScriptTotalExecutionTime(JSDContext* jsdc, JSDScript *script)
393 if (script->profileData)
394 return script->profileData->totalExecutionTime;
400 jsd_GetScriptMinOwnExecutionTime(JSDContext* jsdc, JSDScript *script)
402 if (script->profileData)
403 return script->profileData->minOwnExecutionTime;
409 jsd_GetScriptMaxOwnExecutionTime(JSDContext* jsdc, JSDScript *script)
411 if (script->profileData)
412 return script->profileData->maxOwnExecutionTime;
418 jsd_GetScriptTotalOwnExecutionTime(JSDContext* jsdc, JSDScript *script)
420 if (script->profileData)
421 return script->profileData->totalOwnExecutionTime;
427 jsd_ClearScriptProfileData(JSDContext* jsdc, JSDScript *script)
429 if (script->profileData)
431 free(script->profileData);
432 script->profileData = NULL;
437 jsd_GetJSScript (JSDContext *jsdc, JSDScript *script)
439 return script->script;
443 jsd_GetJSFunction (JSDContext *jsdc, JSDScript *script)
445 return script->function;
449 jsd_IterateScripts(JSDContext* jsdc, JSDScript **iterp)
451 JSDScript *jsdscript = *iterp;
453 JS_ASSERT(JSD_SCRIPTS_LOCKED(jsdc));
456 jsdscript = (JSDScript *)jsdc->scripts.next;
457 if( jsdscript == (JSDScript *)&jsdc->scripts )
459 *iterp = (JSDScript*) jsdscript->links.next;
464 jsd_SetScriptPrivate(JSDScript *jsdscript, void *data)
466 void *rval = jsdscript->data;
467 jsdscript->data = data;
472 jsd_GetScriptPrivate(JSDScript *jsdscript)
474 return jsdscript->data;
478 jsd_IsActiveScript(JSDContext* jsdc, JSDScript *jsdscript)
482 JS_ASSERT(JSD_SCRIPTS_LOCKED(jsdc));
484 for( current = (JSDScript *)jsdc->scripts.next;
485 current != (JSDScript *)&jsdc->scripts;
486 current = (JSDScript *)current->links.next )
488 if(jsdscript == current)
495 jsd_GetScriptFilename(JSDContext* jsdc, JSDScript *jsdscript)
497 return jsdscript->url;
501 jsd_GetScriptFunctionId(JSDContext* jsdc, JSDScript *jsdscript)
505 if( ! jsdscript->function )
507 str = JS_GetFunctionId(jsdscript->function);
509 /* For compatibility we return "anonymous", not an empty string here. */
510 return str ? str : JS_GetAnonymousString(jsdc->jsrt);
514 jsd_GetScriptBaseLineNumber(JSDContext* jsdc, JSDScript *jsdscript)
516 return jsdscript->lineBase;
520 jsd_GetScriptLineExtent(JSDContext* jsdc, JSDScript *jsdscript)
522 if( NOT_SET_YET == (int)jsdscript->lineExtent )
523 jsdscript->lineExtent = JS_GetScriptLineExtent(jsdc->dumbContext, jsdscript->script);
524 return jsdscript->lineExtent;
528 jsd_GetClosestPC(JSDContext* jsdc, JSDScript* jsdscript, uintN line)
531 JSCrossCompartmentCall *call;
534 if( jsdscript && jsdscript->lwscript )
537 jsdlw_RawToProcessedLineNumber(jsdc, jsdscript, line, &newline);
538 if( line != newline )
543 call = JS_EnterCrossCompartmentCallScript(jsdc->dumbContext, jsdscript->script);
546 pc = (jsuword) JS_LineNumberToPC(jsdc->dumbContext, jsdscript->script, line );
547 JS_LeaveCrossCompartmentCall(call);
552 jsd_GetClosestLine(JSDContext* jsdc, JSDScript* jsdscript, jsuword pc)
554 JSCrossCompartmentCall *call;
555 uintN first = jsdscript->lineBase;
556 uintN last = first + jsd_GetScriptLineExtent(jsdc, jsdscript) - 1;
559 call = JS_EnterCrossCompartmentCallScript(jsdc->dumbContext, jsdscript->script);
563 line = JS_PCToLineNumber(jsdc->dumbContext, jsdscript->script, (jsbytecode*)pc);
564 JS_LeaveCrossCompartmentCall(call);
572 if( jsdscript && jsdscript->lwscript )
575 jsdlw_ProcessedToRawLineNumber(jsdc, jsdscript, line, &newline);
584 jsd_SetScriptHook(JSDContext* jsdc, JSD_ScriptHookProc hook, void* callerdata)
587 jsdc->scriptHook = hook;
588 jsdc->scriptHookData = callerdata;
594 jsd_GetScriptHook(JSDContext* jsdc, JSD_ScriptHookProc* hook, void** callerdata)
598 *hook = jsdc->scriptHook;
600 *callerdata = jsdc->scriptHookData;
606 jsd_EnableSingleStepInterrupts(JSDContext* jsdc, JSDScript* jsdscript, JSBool enable)
608 JSCrossCompartmentCall *call;
610 call = JS_EnterCrossCompartmentCallScript(jsdc->dumbContext, jsdscript->script);
614 rv = JS_SetSingleStepMode(jsdc->dumbContext, jsdscript->script, enable);
616 JS_LeaveCrossCompartmentCall(call);
621 /***************************************************************************/
624 jsd_NewScriptHookProc(
626 const char *filename, /* URL this script loads from */
627 uintN lineno, /* line where this script starts */
632 JSDScript* jsdscript = NULL;
633 JSDContext* jsdc = (JSDContext*) callerdata;
634 JSD_ScriptHookProc hook;
637 JSD_ASSERT_VALID_CONTEXT(jsdc);
639 if( JSD_IS_DANGEROUS_THREAD(jsdc) )
642 JSD_LOCK_SCRIPTS(jsdc);
643 jsdscript = _newJSDScript(jsdc, cx, script, fun);
644 JSD_UNLOCK_SCRIPTS(jsdc);
649 JSD_LOCK_SCRIPTS(jsdc);
650 _dumpJSDScript(jsdc, jsdscript, "***NEW Script: ");
651 _dumpJSDScriptList( jsdc );
652 JSD_UNLOCK_SCRIPTS(jsdc);
653 #endif /* JSD_DUMP */
655 /* local in case jsdc->scriptHook gets cleared on another thread */
657 hook = jsdc->scriptHook;
658 hookData = jsdc->scriptHookData;
662 hook(jsdc, jsdscript, JS_TRUE, hookData);
666 jsd_DestroyScriptHookProc(
671 JSDScript* jsdscript = NULL;
672 JSDContext* jsdc = (JSDContext*) callerdata;
673 JSD_ScriptHookProc hook;
676 JSD_ASSERT_VALID_CONTEXT(jsdc);
678 if( JSD_IS_DANGEROUS_THREAD(jsdc) )
681 JSD_LOCK_SCRIPTS(jsdc);
682 jsdscript = jsd_FindJSDScript(jsdc, script);
683 JSD_UNLOCK_SCRIPTS(jsdc);
689 JSD_LOCK_SCRIPTS(jsdc);
690 _dumpJSDScript(jsdc, jsdscript, "***DESTROY Script: ");
691 JSD_UNLOCK_SCRIPTS(jsdc);
692 #endif /* JSD_DUMP */
694 /* local in case hook gets cleared on another thread */
696 hook = jsdc->scriptHook;
697 hookData = jsdc->scriptHookData;
701 hook(jsdc, jsdscript, JS_FALSE, hookData);
703 JSD_LOCK_SCRIPTS(jsdc);
704 JS_HashTableRemove(jsdc->scriptsTable, (void *)script);
705 JSD_UNLOCK_SCRIPTS(jsdc);
708 JSD_LOCK_SCRIPTS(jsdc);
709 _dumpJSDScriptList(jsdc);
710 JSD_UNLOCK_SCRIPTS(jsdc);
711 #endif /* JSD_DUMP */
715 /***************************************************************************/
718 _findHook(JSDContext* jsdc, JSDScript* jsdscript, jsuword pc)
720 JSDExecHook* jsdhook;
721 JSCList* list = &jsdscript->hooks;
723 for( jsdhook = (JSDExecHook*)list->next;
724 jsdhook != (JSDExecHook*)list;
725 jsdhook = (JSDExecHook*)jsdhook->links.next )
727 if (jsdhook->pc == pc)
734 _isActiveHook(JSDContext* jsdc, JSScript *script, JSDExecHook* jsdhook)
736 JSDExecHook* current;
738 JSDScript* jsdscript;
740 JSD_LOCK_SCRIPTS(jsdc);
741 jsdscript = jsd_FindJSDScript(jsdc, script);
744 JSD_UNLOCK_SCRIPTS(jsdc);
748 list = &jsdscript->hooks;
750 for( current = (JSDExecHook*)list->next;
751 current != (JSDExecHook*)list;
752 current = (JSDExecHook*)current->links.next )
754 if(current == jsdhook)
756 JSD_UNLOCK_SCRIPTS(jsdc);
760 JSD_UNLOCK_SCRIPTS(jsdc);
766 jsd_TrapHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval,
769 JSDExecHook* jsdhook = (JSDExecHook*) JSVAL_TO_PRIVATE(closure);
770 JSD_ExecutionHookProc hook;
773 JSDScript* jsdscript;
777 if( NULL == (jsdc = jsd_JSDContextForJSContext(cx)) ||
778 ! _isActiveHook(jsdc, script, jsdhook) )
781 return JSTRAP_CONTINUE;
784 JSD_ASSERT_VALID_EXEC_HOOK(jsdhook);
785 JS_ASSERT(!jsdhook->pc || jsdhook->pc == (jsuword)pc);
786 JS_ASSERT(jsdhook->jsdscript->script == script);
787 JS_ASSERT(jsdhook->jsdscript->jsdc == jsdc);
789 hook = jsdhook->hook;
790 hookData = jsdhook->callerdata;
791 jsdscript = jsdhook->jsdscript;
793 /* do not use jsdhook-> after this point */
796 if( ! jsdc || ! jsdc->inited )
797 return JSTRAP_CONTINUE;
799 if( JSD_IS_DANGEROUS_THREAD(jsdc) )
800 return JSTRAP_CONTINUE;
803 if( ! jsdlw_UserCodeAtPC(jsdc, jsdscript, (jsuword)pc) )
804 return JSTRAP_CONTINUE;
807 return jsd_CallExecutionHook(jsdc, cx, JSD_HOOK_BREAKPOINT,
808 hook, hookData, rval);
814 jsd_SetExecutionHook(JSDContext* jsdc,
815 JSDScript* jsdscript,
817 JSD_ExecutionHookProc hook,
820 JSDExecHook* jsdhook;
822 JSCrossCompartmentCall *call;
827 jsd_ClearExecutionHook(jsdc, jsdscript, pc);
832 jsdhook = _findHook(jsdc, jsdscript, pc);
835 jsdhook->hook = hook;
836 jsdhook->callerdata = callerdata;
842 jsdhook = (JSDExecHook*)calloc(1, sizeof(JSDExecHook));
847 jsdhook->jsdscript = jsdscript;
849 jsdhook->hook = hook;
850 jsdhook->callerdata = callerdata;
852 call = JS_EnterCrossCompartmentCallScript(jsdc->dumbContext, jsdscript->script);
859 rv = JS_SetTrap(jsdc->dumbContext, jsdscript->script,
860 (jsbytecode*)pc, jsd_TrapHandler,
861 PRIVATE_TO_JSVAL(jsdhook));
863 JS_LeaveCrossCompartmentCall(call);
871 JS_APPEND_LINK(&jsdhook->links, &jsdscript->hooks);
878 jsd_ClearExecutionHook(JSDContext* jsdc,
879 JSDScript* jsdscript,
882 JSCrossCompartmentCall *call;
883 JSDExecHook* jsdhook;
887 jsdhook = _findHook(jsdc, jsdscript, pc);
894 call = JS_EnterCrossCompartmentCallScript(jsdc->dumbContext, jsdscript->script);
900 JS_ClearTrap(jsdc->dumbContext, jsdscript->script,
901 (jsbytecode*)pc, NULL, NULL );
903 JS_LeaveCrossCompartmentCall(call);
905 JS_REMOVE_LINK(&jsdhook->links);
913 jsd_ClearAllExecutionHooksForScript(JSDContext* jsdc, JSDScript* jsdscript)
915 JSDExecHook* jsdhook;
916 JSCList* list = &jsdscript->hooks;
920 while( (JSDExecHook*)list != (jsdhook = (JSDExecHook*)list->next) )
922 JS_REMOVE_LINK(&jsdhook->links);
926 /* No cross-compartment call here because we may be in the middle of GC */
927 JS_ClearScriptTraps(jsdc->dumbContext, jsdscript->script);
934 jsd_ClearAllExecutionHooks(JSDContext* jsdc)
936 JSDScript* jsdscript;
937 JSDScript* iterp = NULL;
940 while( NULL != (jsdscript = jsd_IterateScripts(jsdc, &iterp)) )
941 jsd_ClearAllExecutionHooksForScript(jsdc, jsdscript);
947 jsd_ScriptCreated(JSDContext* jsdc,
949 const char *filename, /* URL this script loads from */
950 uintN lineno, /* line where this script starts */
954 jsd_NewScriptHookProc(cx, filename, lineno, script, fun, jsdc);
958 jsd_ScriptDestroyed(JSDContext* jsdc,
962 jsd_DestroyScriptHookProc(cx, script, jsdc);