mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-08 20:47:44 +00:00
450 lines
13 KiB
C
450 lines
13 KiB
C
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
/*
|
|
* JSDB public and callback functions
|
|
*/
|
|
|
|
#include "jsdbpriv.h"
|
|
|
|
|
|
/***************************************************************************/
|
|
|
|
JS_STATIC_DLL_CALLBACK(void)
|
|
_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
|
|
{
|
|
int i, j, k, n;
|
|
|
|
fputs("jsdb: ", stderr);
|
|
if (!report) {
|
|
fprintf(stderr, "%s\n", message);
|
|
return;
|
|
}
|
|
|
|
if (report->filename)
|
|
fprintf(stderr, "%s, ", report->filename);
|
|
if (report->lineno)
|
|
fprintf(stderr, "line %u: ", report->lineno);
|
|
fputs(message, stderr);
|
|
if (!report->linebuf) {
|
|
putc('\n', stderr);
|
|
return;
|
|
}
|
|
|
|
fprintf(stderr, ":\n%s\n", report->linebuf);
|
|
n = report->tokenptr - report->linebuf;
|
|
for (i = j = 0; i < n; i++) {
|
|
if (report->linebuf[i] == '\t') {
|
|
for (k = (j + 8) & ~7; j < k; j++)
|
|
putc('.', stderr);
|
|
continue;
|
|
}
|
|
putc('.', stderr);
|
|
j++;
|
|
}
|
|
fputs("^\n", stderr);
|
|
}
|
|
|
|
JS_STATIC_DLL_CALLBACK(void)
|
|
jsdb_ScriptHookProc(JSDContext* jsdc,
|
|
JSDScript* jsdscript,
|
|
JSBool creating,
|
|
void* callerdata)
|
|
{
|
|
JSDB_Data* data = (JSDB_Data*) callerdata;
|
|
JSFunction* fun;
|
|
|
|
if(data->jsScriptHook &&
|
|
NULL != (fun = JS_ValueToFunction(data->cxDebugger, data->jsScriptHook)))
|
|
{
|
|
jsval result;
|
|
jsval args[2] = {P2H_SCRIPT(data->cxDebugger, jsdscript),
|
|
creating ? JSVAL_TRUE : JSVAL_FALSE };
|
|
JS_CallFunction(data->cxDebugger, NULL, fun, 2, args, &result);
|
|
}
|
|
}
|
|
|
|
uintN JS_DLL_CALLBACK
|
|
jsdb_ExecHookHandler(JSDContext* jsdc,
|
|
JSDThreadState* jsdthreadstate,
|
|
uintN type,
|
|
void* callerdata,
|
|
jsval* rval)
|
|
{
|
|
uintN ourRetVal = JSD_HOOK_RETURN_CONTINUE;
|
|
jsval result;
|
|
JSFunction* fun;
|
|
int answer;
|
|
JSDB_Data* data = (JSDB_Data*) callerdata;
|
|
JS_ASSERT(data);
|
|
|
|
/* if we're already stopped, then don't stop */
|
|
if(data->jsdthreadstate)
|
|
return JSD_HOOK_RETURN_CONTINUE;
|
|
|
|
if(!jsdb_SetThreadState(data, jsdthreadstate))
|
|
goto label_bail;
|
|
|
|
if(data->jsExecutionHook &&
|
|
NULL != (fun = JS_ValueToFunction(data->cxDebugger, data->jsExecutionHook)))
|
|
{
|
|
jsval arg = INT_TO_JSVAL((int)type);
|
|
if(!JS_CallFunction(data->cxDebugger, NULL, fun, 1, &arg, &result))
|
|
goto label_bail;
|
|
if(!JSVAL_IS_INT(result))
|
|
goto label_bail;
|
|
answer = JSVAL_TO_INT(result);
|
|
|
|
|
|
if((answer == JSD_HOOK_RETURN_RET_WITH_VAL ||
|
|
answer == JSD_HOOK_RETURN_THROW_WITH_VAL) &&
|
|
!jsdb_EvalReturnExpression(data, rval))
|
|
{
|
|
goto label_bail;
|
|
}
|
|
|
|
if(answer >= JSD_HOOK_RETURN_HOOK_ERROR &&
|
|
answer <= JSD_HOOK_RETURN_CONTINUE_THROW)
|
|
ourRetVal = answer;
|
|
else
|
|
ourRetVal = JSD_HOOK_RETURN_CONTINUE;
|
|
}
|
|
|
|
label_bail:
|
|
jsdb_SetThreadState(data, NULL);
|
|
return ourRetVal;
|
|
}
|
|
|
|
typedef enum
|
|
{
|
|
ARG_MSG = 0,
|
|
ARG_FILENAME,
|
|
ARG_LINENO,
|
|
ARG_LINEBUF,
|
|
ARG_TOKEN_OFFSET,
|
|
ARG_LIMIT
|
|
} ER_ARGS;
|
|
|
|
uintN JS_DLL_CALLBACK
|
|
jsdb_ErrorReporter(JSDContext* jsdc,
|
|
JSContext* cx,
|
|
const char* message,
|
|
JSErrorReport* report,
|
|
void* callerdata)
|
|
{
|
|
uintN ourRetVal = JSD_ERROR_REPORTER_PASS_ALONG;
|
|
jsval result;
|
|
JSFunction* fun;
|
|
int32 answer;
|
|
JSDB_Data* data = (JSDB_Data*) callerdata;
|
|
JS_ASSERT(data);
|
|
|
|
if(data->jsErrorReporterHook &&
|
|
NULL != (fun = JS_ValueToFunction(data->cxDebugger,
|
|
data->jsErrorReporterHook)))
|
|
{
|
|
jsval args[ARG_LIMIT] = {JSVAL_NULL};
|
|
|
|
if(message)
|
|
args[ARG_MSG] =
|
|
STRING_TO_JSVAL(JS_NewStringCopyZ(cx, message));
|
|
if(report && report->filename)
|
|
args[ARG_FILENAME] =
|
|
STRING_TO_JSVAL(JS_NewStringCopyZ(cx, report->filename));
|
|
if(report && report->linebuf)
|
|
args[ARG_LINEBUF] =
|
|
STRING_TO_JSVAL(JS_NewStringCopyZ(cx, report->linebuf));
|
|
if(report)
|
|
args[ARG_LINENO] =
|
|
INT_TO_JSVAL(report->lineno);
|
|
if(report && report->linebuf && report->tokenptr)
|
|
args[ARG_TOKEN_OFFSET] =
|
|
INT_TO_JSVAL((int)(report->tokenptr - report->linebuf));
|
|
|
|
if(!JS_CallFunction(data->cxDebugger, NULL, fun, ARG_LIMIT, args, &result))
|
|
return ourRetVal;
|
|
|
|
if(JS_ValueToInt32(data->cxDebugger, result, &answer))
|
|
ourRetVal = (uintN) answer;
|
|
}
|
|
return ourRetVal;
|
|
}
|
|
|
|
JS_STATIC_DLL_CALLBACK(JSBool)
|
|
Load(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
|
{
|
|
uintN i;
|
|
JSString *str;
|
|
const char *filename;
|
|
JSScript *script;
|
|
JSBool ok;
|
|
jsval result;
|
|
|
|
ok = JS_TRUE;
|
|
for (i = 0; i < argc; i++) {
|
|
str = JS_ValueToString(cx, argv[i]);
|
|
if (!str) {
|
|
ok = JS_FALSE;
|
|
break;
|
|
}
|
|
argv[i] = STRING_TO_JSVAL(str);
|
|
filename = JS_GetStringBytes(str);
|
|
errno = 0;
|
|
script = JS_CompileFile(cx, obj, filename);
|
|
if (!script)
|
|
continue;
|
|
ok = JS_ExecuteScript(cx, obj, script, &result);
|
|
JS_DestroyScript(cx, script);
|
|
if (!ok)
|
|
break;
|
|
}
|
|
JS_GC(cx);
|
|
*rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx,""));
|
|
return ok;
|
|
}
|
|
|
|
JS_STATIC_DLL_CALLBACK(JSBool)
|
|
Write(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
|
{
|
|
JSString *str = JS_ValueToString(cx, argv[0]);
|
|
if (!str)
|
|
return JS_FALSE;
|
|
printf(JS_GetStringBytes(str));
|
|
*rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx,""));
|
|
return JS_TRUE;
|
|
}
|
|
|
|
JS_STATIC_DLL_CALLBACK(JSBool)
|
|
Gets(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
|
{
|
|
char buf[1024];
|
|
if(! gets(buf))
|
|
return JS_FALSE;
|
|
*rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, buf));
|
|
return JS_TRUE;
|
|
}
|
|
|
|
JS_STATIC_DLL_CALLBACK(JSBool)
|
|
Version(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
|
{
|
|
if (argc > 0 && JSVAL_IS_INT(argv[0]))
|
|
*rval = INT_TO_JSVAL(JS_SetVersion(cx, JSVAL_TO_INT(argv[0])));
|
|
else
|
|
*rval = INT_TO_JSVAL(JS_GetVersion(cx));
|
|
return JS_TRUE;
|
|
}
|
|
|
|
|
|
JS_STATIC_DLL_CALLBACK(JSBool)
|
|
SafeEval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
|
{
|
|
static char default_filename[] = "jsdb_eval";
|
|
/* JSContext *cx2; */
|
|
JSString* textJSString;
|
|
char* filename;
|
|
int32 lineno;
|
|
JSDB_Data* data = (JSDB_Data*) JS_GetContextPrivate(cx);
|
|
JS_ASSERT(data);
|
|
|
|
if(argc < 1 || !(textJSString = JS_ValueToString(cx, argv[0])))
|
|
{
|
|
JS_ReportError(cx, "safeEval requires source text as a first argument");
|
|
return JS_FALSE;
|
|
}
|
|
|
|
if(argc < 2)
|
|
filename = default_filename;
|
|
else
|
|
{
|
|
JSString* filenameJSString;
|
|
if(!(filenameJSString = JS_ValueToString(cx, argv[1])))
|
|
{
|
|
JS_ReportError(cx, "safeEval passed non-string filename as 2nd param");
|
|
return JS_FALSE;
|
|
}
|
|
filename = JS_GetStringBytes(filenameJSString);
|
|
}
|
|
|
|
if(argc < 3)
|
|
lineno = 1;
|
|
else
|
|
{
|
|
if(!JS_ValueToInt32(cx, argv[2], &lineno))
|
|
{
|
|
JS_ReportError(cx, "safeEval passed non-int lineno as 3rd param");
|
|
return JS_FALSE;
|
|
}
|
|
}
|
|
|
|
if(! JS_EvaluateScript(cx, obj,
|
|
JS_GetStringBytes(textJSString),
|
|
JS_GetStringLength(textJSString),
|
|
filename, lineno, rval))
|
|
*rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx,""));
|
|
|
|
return JS_TRUE;
|
|
}
|
|
|
|
JS_STATIC_DLL_CALLBACK(JSBool)
|
|
NativeBreak(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
|
{
|
|
#ifdef _WINDOWS
|
|
_asm int 3;
|
|
*rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx,"did _asm int 3;"));
|
|
#else
|
|
*rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx,"only supported for Windows"));
|
|
#endif
|
|
return JS_TRUE;
|
|
}
|
|
|
|
static JSFunctionSpec debugger_functions[] = {
|
|
{"version", Version, 0},
|
|
{"load", Load, 1},
|
|
{"write", Write, 0},
|
|
{"gets", Gets, 0},
|
|
{"safeEval", SafeEval, 3},
|
|
{"nativeBreak", NativeBreak, 0},
|
|
{0}
|
|
};
|
|
|
|
static JSClass debugger_global_class = {
|
|
"debugger_global", 0,
|
|
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
|
|
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
|
|
};
|
|
|
|
/***************************************************************************/
|
|
|
|
#ifdef JSD_LOWLEVEL_SOURCE
|
|
/*
|
|
* This facilitates sending source to JSD (the debugger system) in the shell
|
|
* where the source is loaded using the JSFILE hack in jsscan. The function
|
|
* below is used as a callback for the jsdbgapi JS_SetSourceHandler hook.
|
|
* A more normal embedding (e.g. mozilla) loads source itself and can send
|
|
* source directly to JSD without using this hook scheme.
|
|
*/
|
|
static void
|
|
SendSourceToJSDebugger(const char *filename, uintN lineno,
|
|
jschar *str, size_t length,
|
|
void **listenerTSData, JSDContext* jsdc)
|
|
{
|
|
JSDSourceText *jsdsrc = (JSDSourceText *) *listenerTSData;
|
|
|
|
if (!jsdsrc) {
|
|
if (!filename)
|
|
filename = "typein";
|
|
if (1 == lineno) {
|
|
jsdsrc = JSD_NewSourceText(jsdc, filename);
|
|
} else {
|
|
jsdsrc = JSD_FindSourceForURL(jsdc, filename);
|
|
if (jsdsrc && JSD_SOURCE_PARTIAL !=
|
|
JSD_GetSourceStatus(jsdc, jsdsrc)) {
|
|
jsdsrc = NULL;
|
|
}
|
|
}
|
|
}
|
|
if (jsdsrc) {
|
|
jsdsrc = JSD_AppendUCSourceText(jsdc,jsdsrc, str, length,
|
|
JSD_SOURCE_PARTIAL);
|
|
}
|
|
*listenerTSData = jsdsrc;
|
|
}
|
|
#endif /* JSD_LOWLEVEL_SOURCE */
|
|
|
|
static JSBool
|
|
_initReturn(const char* str, JSBool retval)
|
|
{
|
|
if(str)
|
|
printf("%s\n", str);
|
|
if(retval)
|
|
; /* printf("debugger initialized\n"); */
|
|
else
|
|
{
|
|
JS_ASSERT(0);
|
|
printf("debugger FAILED to initialize\n");
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
#define MAX_DEBUGGER_DEPTH 3
|
|
|
|
JS_EXPORT_API(JSBool)
|
|
JSDB_InitDebugger(JSRuntime* rt, JSDContext* jsdc, int depth)
|
|
{
|
|
jsval rvalIgnore;
|
|
static char load_deb[] = "load('debugger.js')";
|
|
|
|
JSDB_Data* data = (JSDB_Data*) calloc(1, sizeof(JSDB_Data));
|
|
if(!data)
|
|
return _initReturn("memory alloc error", JS_FALSE);
|
|
|
|
data->rtTarget = rt;
|
|
data->jsdcTarget = jsdc;
|
|
data->debuggerDepth = depth+1;
|
|
|
|
if(!(data->rtDebugger = JS_NewRuntime(8L * 1024L * 1024L)))
|
|
return _initReturn("debugger runtime creation error", JS_FALSE);
|
|
|
|
if(!(data->cxDebugger = JS_NewContext(data->rtDebugger, 8192)))
|
|
return _initReturn("debugger creation error", JS_FALSE);
|
|
|
|
JS_SetContextPrivate(data->cxDebugger, data);
|
|
|
|
JS_SetErrorReporter(data->cxDebugger, _ErrorReporter);
|
|
|
|
if(!(data->globDebugger =
|
|
JS_NewObject(data->cxDebugger, &debugger_global_class, NULL, NULL)))
|
|
return _initReturn("debugger global object creation error", JS_FALSE);
|
|
|
|
if(!JS_InitStandardClasses(data->cxDebugger, data->globDebugger))
|
|
return _initReturn("debugger InitStandardClasses error", JS_FALSE);
|
|
|
|
if(!JS_DefineFunctions(data->cxDebugger, data->globDebugger, debugger_functions))
|
|
return _initReturn("debugger DefineFunctions error", JS_FALSE);
|
|
|
|
if(!jsdb_ReflectJSD(data))
|
|
return _initReturn("debugger reflection of JSD API error", JS_FALSE);
|
|
|
|
if(data->debuggerDepth < MAX_DEBUGGER_DEPTH)
|
|
{
|
|
JSDContext* jsdc;
|
|
if(!(jsdc = JSD_DebuggerOnForUser(data->rtDebugger, NULL, NULL)))
|
|
return _initReturn("failed to create jsdc for nested debugger", JS_FALSE);
|
|
JSD_JSContextInUse(jsdc, data->cxDebugger);
|
|
if(!JSDB_InitDebugger(data->rtDebugger, jsdc, data->debuggerDepth))
|
|
return _initReturn("failed to init nested debugger", JS_FALSE);
|
|
}
|
|
|
|
JSD_SetScriptHook(jsdc, jsdb_ScriptHookProc, data);
|
|
JSD_SetDebuggerHook(jsdc, jsdb_ExecHookHandler, data);
|
|
JSD_SetThrowHook(jsdc, jsdb_ExecHookHandler, data);
|
|
JSD_SetDebugBreakHook(jsdc, jsdb_ExecHookHandler, data);
|
|
JSD_SetErrorReporter(jsdc, jsdb_ErrorReporter, data);
|
|
|
|
#ifdef JSD_LOWLEVEL_SOURCE
|
|
JS_SetSourceHandler(data->rtDebugger, SendSourceToJSDebugger, jsdc);
|
|
#endif /* JSD_LOWLEVEL_SOURCE */
|
|
|
|
JS_EvaluateScript(data->cxDebugger, data->globDebugger,
|
|
load_deb, sizeof(load_deb)-1, "jsdb_autoload", 1,
|
|
&rvalIgnore);
|
|
|
|
return _initReturn(NULL, JS_TRUE);
|
|
}
|
|
|