pjs/js/jsd/jsd_scpt.c

509 строки
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;
}