mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-08 04:27:37 +00:00
509 lines
13 KiB
C
509 lines
13 KiB
C
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
*
|
|
* The contents of this file are subject to the Netscape Public License
|
|
* Version 1.0 (the "NPL"); you may not use this file except in
|
|
* compliance with the NPL. You may obtain a copy of the NPL at
|
|
* http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
|
* for the specific language governing rights and limitations under the
|
|
* NPL.
|
|
*
|
|
* The Initial Developer of this code under the NPL is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
|
* Reserved.
|
|
*/
|
|
|
|
/*
|
|
** JavaScript Debugger Navigator API - Script support
|
|
*/
|
|
|
|
#include "jsd.h"
|
|
#ifdef NSPR20
|
|
#ifdef XP_MAC
|
|
#include "prpriv.h"
|
|
#else
|
|
#include "private/prpriv.h"
|
|
#endif
|
|
#endif
|
|
|
|
/* Comment this out to disable (NT specific) dumping as we go */
|
|
/*
|
|
** #ifdef DEBUG
|
|
** #define JSD_DUMP 1
|
|
** #endif
|
|
*/
|
|
|
|
#define NOT_SET_YET -1
|
|
|
|
/***************************************************************************/
|
|
PRCList jsd_script_list = PR_INIT_STATIC_CLIST(&jsd_script_list);
|
|
|
|
#ifdef DEBUG
|
|
void JSD_ASSERT_VALID_SCRIPT( JSDScript* jsdscript )
|
|
{
|
|
PR_ASSERT( jsdscript );
|
|
PR_ASSERT( jsdscript->script );
|
|
}
|
|
void JSD_ASSERT_VALID_EXEC_HOOK( JSDExecHook* jsdhook )
|
|
{
|
|
PR_ASSERT( jsdhook );
|
|
PR_ASSERT( jsdhook->hook );
|
|
}
|
|
#endif
|
|
|
|
static JSDScript*
|
|
NewJSDScript( JSDContext* jsdc,
|
|
JSContext *cx,
|
|
JSScript *script,
|
|
JSFunction* function )
|
|
{
|
|
JSDScript* jsdscript;
|
|
PRUintn lineno;
|
|
|
|
/* these are inlined javascript: urls and we can't handle them now */
|
|
lineno = (PRUintn) JS_GetScriptBaseLineNumber(cx, script);
|
|
if( lineno == 0 )
|
|
return NULL;
|
|
|
|
jsdscript = PR_NEWZAP(JSDScript);
|
|
if( ! jsdscript )
|
|
return NULL;
|
|
|
|
PR_APPEND_LINK(&jsdscript->links, &jsd_script_list);
|
|
jsdscript->jsdc = jsdc;
|
|
jsdscript->script = script;
|
|
jsdscript->function = function;
|
|
jsdscript->lineBase = lineno;
|
|
jsdscript->lineExtent = (PRUintn)NOT_SET_YET;
|
|
jsdscript->url = (char*)jsd_BuildNormalizedURL(JS_GetScriptFilename(cx,script));
|
|
|
|
PR_INIT_CLIST(&jsdscript->hooks);
|
|
|
|
return jsdscript;
|
|
}
|
|
|
|
static void
|
|
DestroyJSDScript( JSDContext* jsdc,
|
|
JSDScript* jsdscript )
|
|
{
|
|
/* destroy all hooks */
|
|
jsd_ClearAllExecutionHooksForScript(jsdc, jsdscript);
|
|
|
|
PR_REMOVE_LINK(&jsdscript->links);
|
|
PR_FREEIF(jsdscript->url);
|
|
|
|
PR_FREEIF(jsdscript);
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
#ifdef JSD_DUMP
|
|
static void
|
|
DumpJSDScript( JSDContext* jsdc, JSDScript* jsdscript, const char* leadingtext)
|
|
{
|
|
const char* name;
|
|
const char* fun;
|
|
PRUintn base;
|
|
PRUintn extent;
|
|
char Buf[256];
|
|
|
|
name = jsd_GetScriptFilename(jsdc, jsdscript);
|
|
fun = jsd_GetScriptFunctionName(jsdc, jsdscript);
|
|
base = jsd_GetScriptBaseLineNumber(jsdc, jsdscript);
|
|
extent = jsd_GetScriptLineExtent(jsdc, jsdscript);
|
|
|
|
sprintf( Buf, "%sscript=%08X, %s, %s, %d-%d\n",
|
|
leadingtext,
|
|
(unsigned) jsdscript->script,
|
|
name ? name : "no URL",
|
|
fun ? fun : "no fun",
|
|
base, base + extent - 1 );
|
|
OutputDebugString( Buf );
|
|
}
|
|
|
|
static void
|
|
DumpJSDScriptList( JSDContext* jsdc )
|
|
{
|
|
JSDScript* iterp = NULL;
|
|
JSDScript* jsdscript = NULL;
|
|
|
|
OutputDebugString( "*** JSDScriptDump\n" );
|
|
while( NULL != (jsdscript = jsd_IterateScripts(jsdc, &iterp)) )
|
|
DumpJSDScript( jsdc, jsdscript, " script: " );
|
|
}
|
|
#endif /* JSD_DUMP */
|
|
|
|
/***************************************************************************/
|
|
|
|
#ifndef JSD_SIMULATION
|
|
static PRMonitor *jsd_script_mon = NULL;
|
|
#endif /* JSD_SIMULATION */
|
|
|
|
void
|
|
jsd_LockScriptSubsystem(JSDContext* jsdc)
|
|
{
|
|
#ifndef JSD_SIMULATION
|
|
if (jsd_script_mon == NULL)
|
|
jsd_script_mon = PR_NewNamedMonitor("jsd-script-monitor");
|
|
|
|
PR_EnterMonitor(jsd_script_mon);
|
|
#endif /* JSD_SIMULATION */
|
|
}
|
|
|
|
void
|
|
jsd_UnlockScriptSubsystem(JSDContext* jsdc)
|
|
{
|
|
#ifndef JSD_SIMULATION
|
|
PR_ExitMonitor(jsd_script_mon);
|
|
#endif /* JSD_SIMULATION */
|
|
}
|
|
|
|
void
|
|
jsd_DestroyAllJSDScripts( JSDContext* jsdc )
|
|
{
|
|
JSDScript *jsdscript;
|
|
JSDScript *next;
|
|
|
|
for (jsdscript = (JSDScript*)jsd_script_list.next;
|
|
jsdscript != (JSDScript*)&jsd_script_list;
|
|
jsdscript = next)
|
|
{
|
|
next = (JSDScript*)jsdscript->links.next;
|
|
DestroyJSDScript( jsdc, jsdscript );
|
|
}
|
|
}
|
|
|
|
JSDScript*
|
|
jsd_FindJSDScript( JSDContext* jsdc,
|
|
JSScript *script )
|
|
{
|
|
JSDScript *jsdscript;
|
|
|
|
for (jsdscript = (JSDScript *)jsd_script_list.next;
|
|
jsdscript != (JSDScript *)&jsd_script_list;
|
|
jsdscript = (JSDScript *)jsdscript->links.next) {
|
|
if (jsdscript->script == script)
|
|
return jsdscript;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
JSDScript*
|
|
jsd_IterateScripts(JSDContext* jsdc, JSDScript **iterp)
|
|
{
|
|
JSDScript *jsdscript = *iterp;
|
|
|
|
if (!jsdscript)
|
|
jsdscript = (JSDScript *)jsd_script_list.next;
|
|
if (jsdscript == (JSDScript *)&jsd_script_list)
|
|
return NULL;
|
|
*iterp = (JSDScript *)jsdscript->links.next;
|
|
return jsdscript;
|
|
}
|
|
|
|
const char*
|
|
jsd_GetScriptFilename(JSDContext* jsdc, JSDScript *jsdscript)
|
|
{
|
|
return jsdscript->url;
|
|
}
|
|
|
|
const char*
|
|
jsd_GetScriptFunctionName(JSDContext* jsdc, JSDScript *jsdscript)
|
|
{
|
|
if( ! jsdscript->function )
|
|
return NULL;
|
|
return JS_GetFunctionName(jsdscript->function);
|
|
}
|
|
|
|
PRUintn
|
|
jsd_GetScriptBaseLineNumber(JSDContext* jsdc, JSDScript *jsdscript)
|
|
{
|
|
return jsdscript->lineBase;
|
|
}
|
|
|
|
PRUintn
|
|
jsd_GetScriptLineExtent(JSDContext* jsdc, JSDScript *jsdscript)
|
|
{
|
|
if( NOT_SET_YET == jsdscript->lineExtent )
|
|
jsdscript->lineExtent = JS_GetScriptLineExtent(jsdc->dumbContext, jsdscript->script);
|
|
return jsdscript->lineExtent;
|
|
}
|
|
|
|
prword_t
|
|
jsd_GetClosestPC(JSDContext* jsdc, JSDScript* jsdscript, PRUintn line)
|
|
{
|
|
return (prword_t) JS_LineNumberToPC(jsdc->dumbContext,
|
|
jsdscript->script, line );
|
|
}
|
|
|
|
PRUintn
|
|
jsd_GetClosestLine(JSDContext* jsdc, JSDScript* jsdscript, prword_t pc)
|
|
{
|
|
PRUintn first = jsdscript->lineBase;
|
|
PRUintn last = first + jsd_GetScriptLineExtent(jsdc, jsdscript) - 1;
|
|
PRUintn line = JS_PCToLineNumber(jsdc->dumbContext,
|
|
jsdscript->script, (jsbytecode*)pc);
|
|
|
|
if( line < first )
|
|
return first;
|
|
if( line > last )
|
|
return last;
|
|
return line;
|
|
}
|
|
|
|
JSD_ScriptHookProc
|
|
jsd_SetScriptHook(JSDContext* jsdc, JSD_ScriptHookProc hook, void* callerdata)
|
|
{
|
|
JSD_ScriptHookProc oldHook = jsdc->scriptHook;
|
|
jsdc->scriptHook = hook;
|
|
jsdc->scriptHookData = callerdata;
|
|
return oldHook;
|
|
}
|
|
|
|
JSD_ScriptHookProc
|
|
jsd_GetScriptHook(JSDContext* jsdc)
|
|
{
|
|
return jsdc->scriptHook;
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
void PR_CALLBACK
|
|
jsd_NewScriptHookProc(
|
|
JSContext *cx,
|
|
const char *filename, /* URL this script loads from */
|
|
PRUintn lineno, /* line where this script starts */
|
|
JSScript *script,
|
|
JSFunction *fun,
|
|
void* callerdata )
|
|
{
|
|
JSDScript* jsdscript = NULL;
|
|
JSDContext* jsdc = (JSDContext*) callerdata;
|
|
|
|
if( jsd_IsCurrentThreadDangerous() )
|
|
return;
|
|
|
|
JSD_ASSERT_VALID_CONTEXT(jsdc);
|
|
|
|
jsd_LockScriptSubsystem(jsdc);
|
|
|
|
jsd_JSContextUsed(jsdc, cx);
|
|
|
|
jsdscript = NewJSDScript( jsdc, cx, script, fun );
|
|
if( ! jsdscript )
|
|
{
|
|
jsd_UnlockScriptSubsystem(jsdc);
|
|
return;
|
|
}
|
|
|
|
#ifdef JSD_DUMP
|
|
if( jsdscript )
|
|
{
|
|
DumpJSDScript( jsdc, jsdscript, "***NEW Script: " );
|
|
DumpJSDScriptList( jsdc );
|
|
}
|
|
#endif /* JSD_DUMP */
|
|
|
|
if( jsdc->scriptHook )
|
|
jsdc->scriptHook( jsdc, jsdscript, TRUE, jsdc->scriptHookData );
|
|
|
|
jsd_UnlockScriptSubsystem(jsdc);
|
|
}
|
|
|
|
void PR_CALLBACK
|
|
jsd_DestroyScriptHookProc(
|
|
JSContext *cx,
|
|
JSScript *script,
|
|
void* callerdata )
|
|
{
|
|
JSDScript* jsdscript = NULL;
|
|
JSDContext* jsdc = (JSDContext*) callerdata;
|
|
|
|
if( jsd_IsCurrentThreadDangerous() )
|
|
return;
|
|
|
|
JSD_ASSERT_VALID_CONTEXT(jsdc);
|
|
|
|
jsd_LockScriptSubsystem(jsdc);
|
|
|
|
jsd_JSContextUsed(jsdc, cx);
|
|
|
|
jsdscript = jsd_FindJSDScript( jsdc, script );
|
|
if( ! jsdscript )
|
|
{
|
|
jsd_UnlockScriptSubsystem(jsdc);
|
|
return;
|
|
}
|
|
|
|
#ifdef JSD_DUMP
|
|
DumpJSDScript( jsdc, jsdscript, "***DESTROY Script: " );
|
|
#endif /* JSD_DUMP */
|
|
|
|
if( jsdc->scriptHook )
|
|
jsdc->scriptHook( jsdc, jsdscript, FALSE, jsdc->scriptHookData );
|
|
|
|
DestroyJSDScript( jsdc, jsdscript );
|
|
|
|
#ifdef JSD_DUMP
|
|
DumpJSDScriptList( jsdc );
|
|
#endif /* JSD_DUMP */
|
|
|
|
jsd_UnlockScriptSubsystem(jsdc);
|
|
}
|
|
|
|
|
|
/***************************************************************************/
|
|
|
|
static JSDExecHook*
|
|
FindHook( JSDContext* jsdc, JSDScript* jsdscript, prword_t pc)
|
|
{
|
|
JSDExecHook* jsdhook;
|
|
PRCList* list = &jsdscript->hooks;
|
|
|
|
for (jsdhook = (JSDExecHook*)list->next;
|
|
jsdhook != (JSDExecHook*)list;
|
|
jsdhook = (JSDExecHook*)jsdhook->links.next)
|
|
{
|
|
if (jsdhook->pc == pc)
|
|
return jsdhook;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
JSTrapStatus PR_CALLBACK
|
|
jsd_TrapHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval,
|
|
void *closure)
|
|
{
|
|
PRUintn hookanswer = JSD_HOOK_RETURN_CONTINUE;
|
|
JSDThreadState* jsdthreadstate;
|
|
JSDExecHook* jsdhook = (JSDExecHook*) closure;
|
|
JSDContext* jsdc;
|
|
|
|
if( jsd_IsCurrentThreadDangerous() )
|
|
return JSTRAP_CONTINUE;
|
|
|
|
JSD_ASSERT_VALID_EXEC_HOOK(jsdhook);
|
|
PR_ASSERT(jsdhook->pc == (prword_t)pc);
|
|
PR_ASSERT(jsdhook->jsdscript->script == script);
|
|
|
|
jsdc = jsdhook->jsdscript->jsdc;
|
|
if( ! jsdc || ! jsdc->inited )
|
|
return JSTRAP_CONTINUE;
|
|
|
|
jsd_JSContextUsed(jsdc, cx);
|
|
|
|
jsdthreadstate = jsd_NewThreadState(jsdc,cx);
|
|
if( jsdthreadstate )
|
|
{
|
|
hookanswer =
|
|
(*jsdhook->hook)(jsdc, jsdthreadstate,
|
|
JSD_HOOK_BREAKPOINT,
|
|
jsdhook->callerdata );
|
|
|
|
jsd_DestroyThreadState(jsdc, jsdthreadstate);
|
|
}
|
|
|
|
*rval = NULL; /* XXX fix this!!! */
|
|
|
|
if( JSD_HOOK_RETURN_ABORT == hookanswer )
|
|
return JSTRAP_ERROR;
|
|
|
|
return JSTRAP_CONTINUE; /* XXX fix this!!! */
|
|
}
|
|
|
|
|
|
|
|
JSBool
|
|
jsd_SetExecutionHook(JSDContext* jsdc,
|
|
JSDScript* jsdscript,
|
|
prword_t pc,
|
|
JSD_ExecutionHookProc hook,
|
|
void* callerdata)
|
|
{
|
|
JSDExecHook* jsdhook;
|
|
|
|
if( ! hook )
|
|
return jsd_ClearExecutionHook(jsdc, jsdscript, pc);
|
|
|
|
jsdhook = FindHook(jsdc, jsdscript, pc);
|
|
if( jsdhook )
|
|
{
|
|
jsdhook->hook = hook;
|
|
jsdhook->callerdata = callerdata;
|
|
return JS_TRUE;
|
|
}
|
|
/* else... */
|
|
|
|
jsdhook = PR_NEWZAP(JSDExecHook);
|
|
if( ! jsdhook )
|
|
return JS_FALSE;
|
|
jsdhook->jsdscript = jsdscript;
|
|
jsdhook->pc = pc;
|
|
jsdhook->hook = hook;
|
|
jsdhook->callerdata = callerdata;
|
|
|
|
if( ! JS_SetTrap(jsdc->dumbContext, jsdscript->script,
|
|
(jsbytecode*)pc, jsd_TrapHandler, (void*) jsdhook) )
|
|
{
|
|
PR_FREEIF(jsdhook);
|
|
return JS_FALSE;
|
|
}
|
|
|
|
PR_APPEND_LINK(&jsdhook->links, &jsdscript->hooks);
|
|
return JS_TRUE;
|
|
}
|
|
|
|
JSBool
|
|
jsd_ClearExecutionHook(JSDContext* jsdc,
|
|
JSDScript* jsdscript,
|
|
prword_t pc)
|
|
{
|
|
JSDExecHook* jsdhook;
|
|
|
|
jsdhook = FindHook(jsdc, jsdscript, pc);
|
|
if( ! jsdhook )
|
|
{
|
|
PR_ASSERT(0);
|
|
return JS_FALSE;
|
|
}
|
|
|
|
JS_ClearTrap(jsdc->dumbContext, jsdscript->script,
|
|
(jsbytecode*)pc, NULL, NULL );
|
|
|
|
PR_REMOVE_LINK(&jsdhook->links);
|
|
PR_FREEIF(jsdhook);
|
|
|
|
return JS_TRUE;
|
|
}
|
|
|
|
JSBool
|
|
jsd_ClearAllExecutionHooksForScript(JSDContext* jsdc, JSDScript* jsdscript)
|
|
{
|
|
JSDExecHook* jsdhook;
|
|
PRCList* list = &jsdscript->hooks;
|
|
|
|
while( (JSDExecHook*)list != (jsdhook = (JSDExecHook*)list->next) )
|
|
{
|
|
PR_REMOVE_LINK(&jsdhook->links);
|
|
PR_FREEIF(jsdhook);
|
|
}
|
|
|
|
JS_ClearScriptTraps(jsdc->dumbContext, jsdscript->script);
|
|
return JS_TRUE;
|
|
}
|
|
|
|
JSBool
|
|
jsd_ClearAllExecutionHooks(JSDContext* jsdc)
|
|
{
|
|
JSDScript* jsdscript;
|
|
JSDScript* iterp = NULL;
|
|
|
|
while( NULL != (jsdscript = jsd_IterateScripts(jsdc, &iterp)) )
|
|
jsd_ClearAllExecutionHooksForScript(jsdc, jsdscript);
|
|
return JS_TRUE;
|
|
}
|
|
|