pjs/js/src/js.c

2145 строки
59 KiB
C
Исходник Обычный вид История

2001-09-20 04:02:59 +04:00
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
1998-03-28 05:44:41 +03:00
*
2001-09-20 04:02:59 +04:00
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
2001-09-20 04:02:59 +04:00
* the License at http://www.mozilla.org/NPL/
1998-03-28 05:44:41 +03:00
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
1998-03-28 05:44:41 +03:00
*
2001-09-20 04:02:59 +04:00
* The Original Code is Mozilla Communicator client code, released
* March 31, 1998.
*
2001-09-20 04:02:59 +04:00
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*
2001-09-20 04:02:59 +04:00
* Alternatively, the contents of this file may be used under the
* terms of the GNU Public License (the "GPL"), in which case the
* provisions of the GPL are applicable instead of those above.
* If you wish to allow use of your version of this file only
* under the terms of the GPL and not to allow others to use your
* version of this file under the NPL, indicate your decision by
* deleting the provisions above and replace them with the notice
* and other provisions required by the GPL. If you do not delete
* the provisions above, a recipient may use your version of this
* file under either the NPL or the GPL.
*/
1998-03-28 05:44:41 +03:00
/*
* JS shell.
*/
#include "jsstddef.h"
1998-03-28 05:44:41 +03:00
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "jstypes.h"
#include "jsarena.h" /* Added by JSIFY */
/* Removed by JSIFY: #include "prlog.h" */
#include "jsutil.h" /* Added by JSIFY */
#include "jsprf.h"
1998-03-28 05:44:41 +03:00
#include "jsapi.h"
#include "jsatom.h"
#include "jscntxt.h"
#include "jsdbgapi.h"
#include "jsemit.h"
#include "jsfun.h"
#include "jsgc.h"
#include "jslock.h"
#include "jsobj.h"
#include "jsparse.h"
#include "jsscope.h"
#include "jsscript.h"
#ifdef PERLCONNECT
#include "perlconnect/jsperl.h"
#endif
#ifdef LIVECONNECT
#include "jsjava.h"
#endif
#ifdef JSDEBUGGER
#include "jsdebug.h"
#ifdef JSDEBUGGER_JAVA_UI
#include "jsdjava.h"
#endif /* JSDEBUGGER_JAVA_UI */
#ifdef JSDEBUGGER_C_UI
#include "jsdb.h"
#endif /* JSDEBUGGER_C_UI */
#endif /* JSDEBUGGER */
1998-03-28 05:44:41 +03:00
#ifdef XP_UNIX
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#endif
#ifdef XP_PC
#include <io.h> /* for isatty() */
#endif
#define EXITCODE_RUNTIME_ERROR 3
#define EXITCODE_FILE_NOT_FOUND 4
size_t gStackChunkSize = 8192;
int gExitCode = 0;
FILE *gErrFile = NULL;
FILE *gOutFile = NULL;
1998-03-28 05:44:41 +03:00
#ifdef XP_MAC
#ifdef MAC_TEST_HACK
/* this is the data file that all Print strings will be echoed into */
FILE *gTestResultFile = NULL;
#define isatty(f) 0
#else
1998-03-28 05:44:41 +03:00
#define isatty(f) 1
#endif
char *strdup(const char *str)
{
char *copy = (char *) malloc(strlen(str)+1);
if (copy)
strcpy(copy, str);
return copy;
}
#ifdef XP_MAC_MPW
/* Macintosh MPW replacements for the ANSI routines. These translate LF's to CR's because
the MPW libraries supplied by Metrowerks don't do that for some reason. */
static void translateLFtoCR(char *str, int length)
{
char *limit = str + length;
while (str != limit) {
if (*str == '\n')
*str = '\r';
str++;
}
}
int fputc(int c, FILE *file)
{
char buffer = c;
if (buffer == '\n')
buffer = '\r';
return fwrite(&buffer, 1, 1, file);
}
int fputs(const char *s, FILE *file)
{
char buffer[4096];
int n = strlen(s);
int extra = 0;
while (n > sizeof buffer) {
memcpy(buffer, s, sizeof buffer);
translateLFtoCR(buffer, sizeof buffer);
extra += fwrite(buffer, 1, sizeof buffer, file);
n -= sizeof buffer;
s += sizeof buffer;
}
memcpy(buffer, s, n);
translateLFtoCR(buffer, n);
return extra + fwrite(buffer, 1, n, file);
}
int fprintf(FILE* file, const char *format, ...)
{
va_list args;
char smallBuffer[4096];
int n;
int bufferSize = sizeof smallBuffer;
char *buffer = smallBuffer;
int result;
va_start(args, format);
n = vsnprintf(buffer, bufferSize, format, args);
va_end(args);
while (n < 0) {
if (buffer != smallBuffer)
free(buffer);
bufferSize <<= 1;
buffer = malloc(bufferSize);
if (!buffer) {
JS_ASSERT(JS_FALSE);
return 0;
}
va_start(args, format);
n = vsnprintf(buffer, bufferSize, format, args);
va_end(args);
}
translateLFtoCR(buffer, n);
result = fwrite(buffer, 1, n, file);
if (buffer != smallBuffer)
free(buffer);
return result;
}
#else
#include <SIOUX.h>
#include <MacTypes.h>
static char* mac_argv[] = { "js", NULL };
static void initConsole(StringPtr consoleName, const char* startupMessage, int *argc, char** *argv)
{
SIOUXSettings.autocloseonquit = true;
SIOUXSettings.asktosaveonclose = false;
/* SIOUXSettings.initializeTB = false;
SIOUXSettings.showstatusline = true;*/
puts(startupMessage);
SIOUXSetTitle(consoleName);
/* set up a buffer for stderr (otherwise it's a pig). */
setvbuf(stderr, (char *) malloc(BUFSIZ), _IOLBF, BUFSIZ);
*argc = 1;
*argv = mac_argv;
}
#ifdef LIVECONNECT
/* Little hack to provide a default CLASSPATH on the Mac. */
#define getenv(var) mac_getenv(var)
static char* mac_getenv(const char* var)
{
if (strcmp(var, "CLASSPATH") == 0) {
static char class_path[] = "liveconnect.jar";
return class_path;
}
return NULL;
}
#endif /* LIVECONNECT */
#endif
1998-03-28 05:44:41 +03:00
#endif
#ifdef JSDEBUGGER
static JSDContext *_jsdc;
#ifdef JSDEBUGGER_JAVA_UI
static JSDJContext *_jsdjc;
#endif /* JSDEBUGGER_JAVA_UI */
#endif /* JSDEBUGGER */
static JSBool reportWarnings = JS_TRUE;
typedef enum JSShellErrNum {
#define MSG_DEF(name, number, count, exception, format) \
name = number,
#include "jsshell.msg"
#undef MSG_DEF
JSShellErr_Limit
#undef MSGDEF
} JSShellErrNum;
static const JSErrorFormatString *
my_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber);
#ifdef EDITLINE
extern char *readline(const char *prompt);
extern void add_history(char *line);
#endif
static JSBool
GetLine(JSContext *cx, char *bufp, FILE *file, const char *prompt) {
#ifdef EDITLINE
/*
* Use readline only if file is stdin, because there's no way to specify
* another handle. Are other filehandles interactive?
*/
if (file == stdin) {
char *linep;
if ((linep = readline(prompt)) == NULL)
return JS_FALSE;
if (linep[0] != '\0')
add_history(linep);
strcpy(bufp, linep);
JS_free(cx, linep);
bufp += strlen(bufp);
*bufp++ = '\n';
*bufp = '\0';
} else
#endif
{
char line[256];
fprintf(gOutFile, prompt);
#ifdef XP_MAC_MPW
/* Print a CR after the prompt because MPW grabs the entire line when entering an interactive command */
fputc('\n', gOutFile);
#endif
if (fgets(line, sizeof line, file) == NULL)
return JS_FALSE;
strcpy(bufp, line);
}
return JS_TRUE;
}
1998-03-28 05:44:41 +03:00
static void
Process(JSContext *cx, JSObject *obj, char *filename)
{
JSBool ok, hitEOF;
JSScript *script;
1998-03-28 05:44:41 +03:00
jsval result;
JSString *str;
char buffer[4096];
char *bufp;
int lineno;
int startline;
FILE *file;
if (filename != NULL && strcmp(filename, "-") != 0) {
file = fopen(filename, "r");
if (!file) {
JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
JSSMSG_CANT_OPEN, filename, strerror(errno));
gExitCode = EXITCODE_FILE_NOT_FOUND;
return;
}
} else {
file = stdin;
}
if (!isatty(fileno(file))) {
/*
* It's not interactive - just execute it.
*
* Support the UNIX #! shell hack; gobble the first line if it starts
* with '#'. TODO - this isn't quite compatible with sharp variables,
* as a legal js program (using sharp variables) might start with '#'.
* But that would require multi-character lookahead.
*/
int ch = fgetc(file);
if (ch == '#') {
while((ch = fgetc(file)) != EOF) {
if (ch == '\n' || ch == '\r')
break;
}
}
ungetc(ch, file);
script = JS_CompileFileHandle(cx, obj, filename, file);
if (script) {
(void)JS_ExecuteScript(cx, obj, script, &result);
JS_DestroyScript(cx, script);
}
return;
}
/* It's an interactive filehandle; drop into read-eval-print loop. */
lineno = 1;
hitEOF = JS_FALSE;
do {
bufp = buffer;
*bufp = '\0';
/*
* Accumulate lines until we get a 'compilable unit' - one that either
* generates an error (before running out of source) or that compiles
* cleanly. This should be whenever we get a complete statement that
* coincides with the end of a line.
*/
startline = lineno;
do {
if (!GetLine(cx, bufp, file, startline == lineno ? "js> " : "")) {
hitEOF = JS_TRUE;
break;
}
bufp += strlen(bufp);
lineno++;
} while (!JS_BufferIsCompilableUnit(cx, obj, buffer, strlen(buffer)));
/* Clear any pending exception from previous failed compiles. */
JS_ClearPendingException(cx);
script = JS_CompileScript(cx, obj, buffer, strlen(buffer),
#ifdef JSDEBUGGER
"typein",
#else
NULL,
#endif
startline);
if (script) {
JSErrorReporter older;
ok = JS_ExecuteScript(cx, obj, script, &result);
if (ok && result != JSVAL_VOID) {
/* Suppress error reports from JS_ValueToString(). */
older = JS_SetErrorReporter(cx, NULL);
str = JS_ValueToString(cx, result);
JS_SetErrorReporter(cx, older);
if (str)
fprintf(gOutFile, "%s\n", JS_GetStringBytes(str));
else
ok = JS_FALSE;
}
#if 0
#if JS_HAS_ERROR_EXCEPTIONS
/*
* Require that any time we return failure, an exception has
* been set.
*/
JS_ASSERT(ok || JS_IsExceptionPending(cx));
/*
* Also that any time an exception has been set, we've
* returned failure.
*/
JS_ASSERT(!JS_IsExceptionPending(cx) || !ok);
#endif /* JS_HAS_ERROR_EXCEPTIONS */
#endif
JS_DestroyScript(cx, script);
}
} while (!hitEOF);
fprintf(gOutFile, "\n");
return;
1998-03-28 05:44:41 +03:00
}
static int
usage(void)
{
fprintf(gErrFile, "%s\n", JS_GetImplementationVersion());
fprintf(gErrFile, "usage: js [-s] [-w] [-W] [-b branchlimit] [-c stackchunksize] [-v version] [-f scriptfile] [scriptfile] [scriptarg...]\n");
return 2;
}
static uint32 gBranchCount;
static uint32 gBranchLimit;
static JSBool
my_BranchCallback(JSContext *cx, JSScript *script)
{
if (++gBranchCount == gBranchLimit) {
if (script->filename)
fprintf(gErrFile, "%s:", script->filename);
fprintf(gErrFile, "%u: script branches too much (%u callbacks)\n",
script->lineno, gBranchLimit);
gBranchCount = 0;
return JS_FALSE;
}
return JS_TRUE;
}
static int
ProcessArgs(JSContext *cx, JSObject *obj, char **argv, int argc)
{
int i, j;
char *filename = NULL;
jsint length;
jsval *vector;
JSObject *argsObj;
JSBool isInteractive = JS_TRUE;
for (i=0; i < argc; i++) {
if (argv[i][0] == '-') {
switch (argv[i][1]) {
case 'v':
if (i+1 == argc) {
return usage();
}
JS_SetVersion(cx, (JSVersion) atoi(argv[i+1]));
i++;
break;
case 'w':
reportWarnings = JS_TRUE;
break;
case 'W':
reportWarnings = JS_FALSE;
break;
case 's':
JS_ToggleOptions(cx, JSOPTION_STRICT);
break;
case 'b':
gBranchLimit = atoi(argv[++i]);
JS_SetBranchCallback(cx, my_BranchCallback);
break;
case 'c':
/* set stack chunk size */
gStackChunkSize = atoi(argv[++i]);
break;
case 'f':
if (i+1 == argc) {
return usage();
}
filename = argv[i+1];
/* "-f -" means read from stdin */
if (filename[0] == '-' && filename[1] == '\0')
filename = NULL;
Process(cx, obj, filename);
filename = NULL;
/* XXX: js -f foo.js should interpret foo.js and then
* drop into interactive mode, but that breaks test
* harness. Just execute foo.js for now.
*/
isInteractive = JS_FALSE;
i++;
break;
default:
return usage();
}
} else {
filename = argv[i++];
isInteractive = JS_FALSE;
break;
}
}
length = argc - i;
if (length == 0) {
vector = NULL;
} else {
vector = (jsval *) JS_malloc(cx, length * sizeof(jsval));
if (vector == NULL)
return 1;
for (j = 0; j < length; j++) {
JSString *str = JS_NewStringCopyZ(cx, argv[i++]);
if (str == NULL)
return 1;
vector[j] = STRING_TO_JSVAL(str);
}
}
argsObj = JS_NewArrayObject(cx, length, vector);
if (vector)
JS_free(cx, vector);
if (argsObj == NULL)
return 1;
if (!JS_DefineProperty(cx, obj, "arguments", OBJECT_TO_JSVAL(argsObj),
NULL, NULL, 0)) {
return 1;
}
if (filename || isInteractive)
Process(cx, obj, filename);
return gExitCode;
}
1998-03-28 05:44:41 +03:00
static 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, (JSVersion) JSVAL_TO_INT(argv[0])));
1998-03-28 05:44:41 +03:00
else
*rval = INT_TO_JSVAL(JS_GetVersion(cx));
1998-03-28 05:44:41 +03:00
return JS_TRUE;
}
static struct {
const char *name;
uint32 flag;
} js_options[] = {
{"strict", JSOPTION_STRICT},
{"werror", JSOPTION_WERROR},
{0, 0}
};
static JSBool
Options(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
uint32 optset, flag;
uintN i, j, found;
JSString *str;
const char *opt;
char *names;
optset = 0;
for (i = 0; i < argc; i++) {
str = JS_ValueToString(cx, argv[i]);
if (!str)
return JS_FALSE;
opt = JS_GetStringBytes(str);
for (j = 0; js_options[j].name; j++) {
if (strcmp(js_options[j].name, opt) == 0) {
optset |= js_options[j].flag;
break;
}
}
}
optset = JS_ToggleOptions(cx, optset);
names = NULL;
found = 0;
while (optset != 0) {
flag = optset;
optset &= optset - 1;
flag &= ~optset;
for (j = 0; js_options[j].name; j++) {
if (js_options[j].flag == flag) {
names = JS_sprintf_append(names, "%s%s",
names ? "," : "", js_options[j].name);
found++;
break;
}
}
}
if (!found)
names = strdup("");
if (!names) {
JS_ReportOutOfMemory(cx);
return JS_FALSE;
}
str = JS_NewString(cx, names, strlen(names));
if (!str) {
free(names);
return JS_FALSE;
}
*rval = STRING_TO_JSVAL(str);
return JS_TRUE;
}
static void
my_LoadErrorReporter(JSContext *cx, const char *message, JSErrorReport *report);
static void
my_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report);
1998-03-28 05:44:41 +03:00
static 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;
JSErrorReporter older;
1998-03-28 05:44:41 +03:00
for (i = 0; i < argc; i++) {
str = JS_ValueToString(cx, argv[i]);
if (!str)
return JS_FALSE;
argv[i] = STRING_TO_JSVAL(str);
filename = JS_GetStringBytes(str);
errno = 0;
older = JS_SetErrorReporter(cx, my_LoadErrorReporter);
script = JS_CompileFile(cx, obj, filename);
if (!script)
ok = JS_FALSE;
else {
ok = JS_ExecuteScript(cx, obj, script, &result);
JS_DestroyScript(cx, script);
}
JS_SetErrorReporter(cx, older);
if (!ok)
return JS_FALSE;
1998-03-28 05:44:41 +03:00
}
1998-03-28 05:44:41 +03:00
return JS_TRUE;
}
static JSBool
Print(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
uintN i, n;
JSString *str;
for (i = n = 0; i < argc; i++) {
str = JS_ValueToString(cx, argv[i]);
if (!str)
return JS_FALSE;
fprintf(gOutFile, "%s%s", i ? " " : "", JS_GetStringBytes(str));
1998-03-28 05:44:41 +03:00
}
n++;
1998-03-28 05:44:41 +03:00
if (n)
fputc('\n', gOutFile);
1998-03-28 05:44:41 +03:00
return JS_TRUE;
}
static JSBool
Help(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
static JSBool
Quit(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
int r = 0;
#ifdef LIVECONNECT
JSJ_SimpleShutdown();
#endif
JS_ConvertArguments(cx, argc, argv,"/ i", &r);
exit(r);
1998-03-28 05:44:41 +03:00
return JS_FALSE;
}
#ifdef GC_MARK_DEBUG
extern JS_FRIEND_DATA(FILE *) js_DumpGCHeap;
1998-03-28 05:44:41 +03:00
#endif
static JSBool
GC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
JSRuntime *rt;
uint32 preBytes;
1998-03-28 05:44:41 +03:00
rt = cx->runtime;
preBytes = rt->gcBytes;
#ifdef GC_MARK_DEBUG
if (argc && JSVAL_IS_STRING(argv[0])) {
char *name = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
FILE *file = fopen(name, "w");
if (!file) {
fprintf(gErrFile, "gc: can't open %s: %s\n", strerror(errno));
return JS_FALSE;
}
js_DumpGCHeap = file;
} else {
js_DumpGCHeap = stdout;
}
1998-03-28 05:44:41 +03:00
#endif
JS_GC(cx);
1998-03-28 05:44:41 +03:00
#ifdef GC_MARK_DEBUG
if (js_DumpGCHeap != stdout)
fclose(js_DumpGCHeap);
1998-03-28 05:44:41 +03:00
js_DumpGCHeap = NULL;
#endif
fprintf(gOutFile, "before %lu, after %lu, break %08lx\n",
(unsigned long)preBytes, (unsigned long)rt->gcBytes,
1998-03-28 05:44:41 +03:00
#ifdef XP_UNIX
(unsigned long)sbrk(0)
1998-03-28 05:44:41 +03:00
#else
0
1998-03-28 05:44:41 +03:00
#endif
);
1998-03-28 05:44:41 +03:00
#ifdef JS_GCMETER
js_DumpGCStats(rt, stdout);
#endif
return JS_TRUE;
}
static JSScript *
ValueToScript(JSContext *cx, jsval v)
{
JSScript *script;
JSFunction *fun;
if (JSVAL_IS_OBJECT(v) &&
JS_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_ScriptClass) {
script = (JSScript *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v));
} else {
fun = JS_ValueToFunction(cx, v);
if (!fun)
return NULL;
script = fun->script;
}
return script;
}
1998-03-28 05:44:41 +03:00
static JSBool
GetTrapArgs(JSContext *cx, uintN argc, jsval *argv, JSScript **scriptp,
int32 *ip)
1998-03-28 05:44:41 +03:00
{
uintN intarg;
JSScript *script;
1998-03-28 05:44:41 +03:00
*scriptp = cx->fp->down->script;
*ip = 0;
if (argc != 0) {
intarg = 0;
if (JS_TypeOfValue(cx, argv[0]) == JSTYPE_FUNCTION) {
script = ValueToScript(cx, argv[0]);
if (!script)
return JS_FALSE;
*scriptp = script;
intarg++;
}
if (argc > intarg) {
if (!JS_ValueToInt32(cx, argv[intarg], ip))
return JS_FALSE;
}
1998-03-28 05:44:41 +03:00
}
return JS_TRUE;
}
static JSTrapStatus
TrapHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval,
void *closure)
1998-03-28 05:44:41 +03:00
{
JSString *str;
JSStackFrame *caller;
str = (JSString *) closure;
1998-03-28 05:44:41 +03:00
caller = cx->fp->down;
if (!JS_EvaluateScript(cx, caller->scopeChain,
JS_GetStringBytes(str), JS_GetStringLength(str),
caller->script->filename, caller->script->lineno,
rval)) {
return JSTRAP_ERROR;
1998-03-28 05:44:41 +03:00
}
if (*rval != JSVAL_VOID)
return JSTRAP_RETURN;
1998-03-28 05:44:41 +03:00
return JSTRAP_CONTINUE;
}
static JSBool
Trap(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
JSString *str;
JSScript *script;
int32 i;
if (argc == 0) {
JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_TRAP_USAGE);
return JS_FALSE;
1998-03-28 05:44:41 +03:00
}
argc--;
str = JS_ValueToString(cx, argv[argc]);
if (!str)
return JS_FALSE;
1998-03-28 05:44:41 +03:00
argv[argc] = STRING_TO_JSVAL(str);
if (!GetTrapArgs(cx, argc, argv, &script, &i))
return JS_FALSE;
1998-03-28 05:44:41 +03:00
return JS_SetTrap(cx, script, script->code + i, TrapHandler, str);
}
static JSBool
Untrap(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
JSScript *script;
int32 i;
if (!GetTrapArgs(cx, argc, argv, &script, &i))
return JS_FALSE;
1998-03-28 05:44:41 +03:00
JS_ClearTrap(cx, script, script->code + i, NULL, NULL);
return JS_TRUE;
}
static JSBool
LineToPC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
JSScript *script;
int32 i;
uintN lineno;
jsbytecode *pc;
if (argc == 0) {
JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_LINE2PC_USAGE);
return JS_FALSE;
1998-03-28 05:44:41 +03:00
}
script = cx->fp->down->script;
if (!GetTrapArgs(cx, argc, argv, &script, &i))
return JS_FALSE;
1998-03-28 05:44:41 +03:00
lineno = (i == 0) ? script->lineno : (uintN)i;
pc = JS_LineNumberToPC(cx, script, lineno);
if (!pc)
return JS_FALSE;
*rval = INT_TO_JSVAL(PTRDIFF(pc, script->code, jsbytecode));
1998-03-28 05:44:41 +03:00
return JS_TRUE;
}
static JSBool
PCToLine(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
JSScript *script;
int32 i;
uintN lineno;
if (!GetTrapArgs(cx, argc, argv, &script, &i))
return JS_FALSE;
1998-03-28 05:44:41 +03:00
lineno = JS_PCToLineNumber(cx, script, script->code + i);
if (!lineno)
return JS_FALSE;
1998-03-28 05:44:41 +03:00
*rval = INT_TO_JSVAL(lineno);
return JS_TRUE;
}
#ifdef DEBUG
static void
SrcNotes(JSContext *cx, JSScript *script)
1998-03-28 05:44:41 +03:00
{
uintN offset, delta, caseOff;
1998-03-28 05:44:41 +03:00
jssrcnote *notes, *sn;
JSSrcNoteType type;
jsatomid atomIndex;
JSAtom *atom;
notes = script->notes;
1998-03-28 05:44:41 +03:00
if (notes) {
fprintf(gOutFile, "\nSource notes:\n");
offset = 0;
for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
delta = SN_DELTA(sn);
offset += delta;
fprintf(gOutFile, "%3u: %5u [%4u] %-8s",
PTRDIFF(sn, notes, jssrcnote), offset, delta,
Fixes for bug 80981 (``Need extended jump bytecode to avoid "script too large" errors, etc.''): We now ReportStatementTooLarge only if - a jump offset overflows 32 bits, signed; - there are 2**32 or more span dependencies in a script; - a backpatch chain link is more than (2**30 - 1) bytecodes long; - a source note's distance from the last note, or from script main entry point, is > 0x7fffff bytes. Narrative of the patch, by file: - js.c The js_SrcNoteName array of const char * is now a js_SrcNoteSpec array of "specifiers", structs that include a const char *name member. Also, due to span-dependent jumps at the ends of basic blocks where the decompiler knows the basic block length, but not the jump format, we need an offset operand for SRC_COND, SRC_IF_ELSE, and SRC_WHILE (to tell the distance from the branch bytecode after the condition expression to the span-dependent jump). - jsarena.[ch] JS arenas are used mainly for last-in-first-out allocation with _en masse_ release to the malloc pool (or, optionally, to a private freelist). But the code generator needs to allocate and grow (by doubling, to avoid O(n^2) growth) allocations that hold bytecode, source notes, and span-dependency records. This exception to LIFO allocation works by claiming an entire arena from the pool and realloc'ing it, as soon as the allocation size reaches the pool's default arena size. Call such an allocation a "large single allocation". This patch adds a new arena API, JS_ArenaFreeAllocation, which can be used to free a large single allocation. If called with an allocation that's not a large single allocation, it will nevertheless attempt to retract the arena containing that allocation, if the allocation is last within its arena. Thus JS_ArenaFreeAllocation adds a non-LIFO "free" special case to match the non-LIFO "grow" special case already implemented under JS_ARENA_GROW for large single allocations. The code generator still benefits via this extension to arenas, over purely manual malloc/realloc/free, by virtue of _en masse_ free (JS_ARENA_RELEASE after code generation has completed, successfully or not). To avoid searching for the previous arena, in order to update its next member upon reallocation of the arena containing a large single allocation, the oversized arena has a back-pointer to that next member stored (but not as allocable space within the arena) in a (JSArena **) footer at its end. - jscntxt.c I've observed for many scripts that the bytes of source notes and bytecode are of comparable lengths, but only now am I fixing the default arena size for cx->notePool to match the size for cx->codePool (1024 instead of 256). - jsemit.c Span-dependent instructions in JS bytecode consist of the jump (JOF_JUMP) and switch (JOF_LOOKUPSWITCH, JOF_TABLESWITCH) format opcodes, subdivided into unconditional (gotos and gosubs), and conditional jumps or branches (which pop a value, test it, and jump depending on its value). Most jumps have just one immediate operand, a signed offset from the jump opcode's pc to the target bytecode. The lookup and table switch opcodes may contain many jump offsets. This patch adds "X" counterparts to the opcodes/formats (X is suffixed, btw, to prefer JSOP_ORX and thereby to avoid colliding on the JSOP_XOR name for the extended form of the JSOP_OR branch opcode). The unextended or short formats have 16-bit signed immediate offset operands, the extended or long formats have 32-bit signed immediates. The span-dependency problem consists of selecting as few long instructions as possible, or about as few -- since jumps can span other jumps, extending one jump may cause another to need to be extended. Most JS scripts are short, so need no extended jumps. We optimize for this case by generating short jumps until we know a long jump is needed. After that point, we keep generating short jumps, but each jump's 16-bit immediate offset operand is actually an unsigned index into cg->spanDeps, an array of JSSpanDep structs. Each struct tells the top offset in the script of the opcode, the "before" offset of the jump (which will be the same as top for simplex jumps, but which will index further into the bytecode array for a non-initial jump offset in a lookup or table switch), the after "offset" adjusted during span-dependent instruction selection (initially the same value as the "before" offset), and the jump target (more below). Since we generate cg->spanDeps lazily, from within js_SetJumpOffset, we must ensure that all bytecode generated so far can be inspected to discover where the jump offset immediate operands lie within CG_CODE(cg). But the bonus is that we generate span-dependency records sorted by their offsets, so we can binary-search when trying to find a JSSpanDep for a given bytecode offset, or the nearest JSSpanDep at or above a given pc. To avoid limiting scripts to 64K jumps, if the cg->spanDeps index overflows 65534, we store SPANDEP_INDEX_HUGE in the jump's immediate operand. This tells us that we need to binary-search for the cg->spanDeps entry by the jump opcode's bytecode offset (sd->before). Jump targets need to be maintained in a data structure that lets us look up an already-known target by its address (jumps may have a common target), and that also lets us update the addresses (script-relative, a.k.a. absolute offsets) of targets that come after a jump target (for when a jump below that target needs to be extended). We use an AVL tree, implemented using recursion, but with some tricky optimizations to its height-balancing code (see http://www.enteract.com/~bradapp/ftp/src/libs/C++/AvlTrees.html). A final wrinkle: backpatch chains are linked by jump-to-jump offsets with positive sign, even though they link "backward" (i.e., toward lower bytecode address). We don't want to waste space and search time in the AVL tree for such temporary backpatch deltas, so we use a single-bit wildcard scheme to tag true JSJumpTarget pointers and encode untagged, signed (positive) deltas in JSSpanDep.target pointers, depending on whether the JSSpanDep has a known target, or is still awaiting backpatching. Note that backpatch chains would present a problem for BuildSpanDepTable, which inspects bytecode to build cg->spanDeps on demand, when the first short jump offset overflows. To solve this temporary problem, we emit a proxy bytecode (JSOP_BACKPATCH; JSOP_BACKPATCH_PUSH for jumps that push a result on the interpreter's stack, namely JSOP_GOSUB; or JSOP_BACKPATCH_POP for branch ops) whose nuses/ndefs counts help keep the stack balanced, but whose opcode format distinguishes its backpatch delta immediate operand from a normal jump offset. The cg->spanDeps array and JSJumpTarget structs are allocated from the cx->tempPool arena-pool. This created a LIFO vs. non-LIFO conflict: there were two places under the TOK_SWITCH case in js_EmitTree that used tempPool to allocate and release a chunk of memory, during whose lifetime JSSpanDep and/or JSJumpTarget structs might also be allocated from tempPool -- the ensuing release would prove disastrous. These bitmap and table temporaries are now allocated from the malloc heap. - jsinterp.c Straightforward cloning and JUMP => JUMPX mutating of the jump and switch format bytecode cases. - jsobj.c Silence warnings about %p used without (void *) casts. - jsopcode.c Massive and scary decompiler whackage to cope with extended jumps, using source note offsets to help find jumps whose format (short or long) can't be discovered from properties of prior instructions in the script. One cute hack here: long || and && expressions are broken up to wrap before the 80th column, with the operator at the end of each non-terminal line. - jsopcode.h, jsopcode.tbl The new extended jump opcodes, formats, and fundamental parameterization macros. Also, more comments. - jsparse.c Random and probably only aesthetic fix to avoid decorating a foo[i]++ or --foo[i] parse tree node with JSOP_SETCALL, wrongly (only foo(i)++ or --foo(i), or the other post- or prefix form operator, should have such an opcode decoration on its parse tree). - jsscript.h Random macro naming sanity: use trailing _ rather than leading _ for macro local variables in order to avoid invading the standard C global namespace.
2001-10-17 07:16:48 +04:00
js_SrcNoteSpec[SN_TYPE(sn)].name);
type = (JSSrcNoteType) SN_TYPE(sn);
switch (type) {
case SRC_SETLINE:
fprintf(gOutFile, " lineno %u", (uintN) js_GetSrcNoteOffset(sn, 0));
break;
case SRC_FOR:
fprintf(gOutFile, " cond %u update %u tail %u",
(uintN) js_GetSrcNoteOffset(sn, 0),
(uintN) js_GetSrcNoteOffset(sn, 1),
(uintN) js_GetSrcNoteOffset(sn, 2));
break;
Fixes for bug 80981 (``Need extended jump bytecode to avoid "script too large" errors, etc.''): We now ReportStatementTooLarge only if - a jump offset overflows 32 bits, signed; - there are 2**32 or more span dependencies in a script; - a backpatch chain link is more than (2**30 - 1) bytecodes long; - a source note's distance from the last note, or from script main entry point, is > 0x7fffff bytes. Narrative of the patch, by file: - js.c The js_SrcNoteName array of const char * is now a js_SrcNoteSpec array of "specifiers", structs that include a const char *name member. Also, due to span-dependent jumps at the ends of basic blocks where the decompiler knows the basic block length, but not the jump format, we need an offset operand for SRC_COND, SRC_IF_ELSE, and SRC_WHILE (to tell the distance from the branch bytecode after the condition expression to the span-dependent jump). - jsarena.[ch] JS arenas are used mainly for last-in-first-out allocation with _en masse_ release to the malloc pool (or, optionally, to a private freelist). But the code generator needs to allocate and grow (by doubling, to avoid O(n^2) growth) allocations that hold bytecode, source notes, and span-dependency records. This exception to LIFO allocation works by claiming an entire arena from the pool and realloc'ing it, as soon as the allocation size reaches the pool's default arena size. Call such an allocation a "large single allocation". This patch adds a new arena API, JS_ArenaFreeAllocation, which can be used to free a large single allocation. If called with an allocation that's not a large single allocation, it will nevertheless attempt to retract the arena containing that allocation, if the allocation is last within its arena. Thus JS_ArenaFreeAllocation adds a non-LIFO "free" special case to match the non-LIFO "grow" special case already implemented under JS_ARENA_GROW for large single allocations. The code generator still benefits via this extension to arenas, over purely manual malloc/realloc/free, by virtue of _en masse_ free (JS_ARENA_RELEASE after code generation has completed, successfully or not). To avoid searching for the previous arena, in order to update its next member upon reallocation of the arena containing a large single allocation, the oversized arena has a back-pointer to that next member stored (but not as allocable space within the arena) in a (JSArena **) footer at its end. - jscntxt.c I've observed for many scripts that the bytes of source notes and bytecode are of comparable lengths, but only now am I fixing the default arena size for cx->notePool to match the size for cx->codePool (1024 instead of 256). - jsemit.c Span-dependent instructions in JS bytecode consist of the jump (JOF_JUMP) and switch (JOF_LOOKUPSWITCH, JOF_TABLESWITCH) format opcodes, subdivided into unconditional (gotos and gosubs), and conditional jumps or branches (which pop a value, test it, and jump depending on its value). Most jumps have just one immediate operand, a signed offset from the jump opcode's pc to the target bytecode. The lookup and table switch opcodes may contain many jump offsets. This patch adds "X" counterparts to the opcodes/formats (X is suffixed, btw, to prefer JSOP_ORX and thereby to avoid colliding on the JSOP_XOR name for the extended form of the JSOP_OR branch opcode). The unextended or short formats have 16-bit signed immediate offset operands, the extended or long formats have 32-bit signed immediates. The span-dependency problem consists of selecting as few long instructions as possible, or about as few -- since jumps can span other jumps, extending one jump may cause another to need to be extended. Most JS scripts are short, so need no extended jumps. We optimize for this case by generating short jumps until we know a long jump is needed. After that point, we keep generating short jumps, but each jump's 16-bit immediate offset operand is actually an unsigned index into cg->spanDeps, an array of JSSpanDep structs. Each struct tells the top offset in the script of the opcode, the "before" offset of the jump (which will be the same as top for simplex jumps, but which will index further into the bytecode array for a non-initial jump offset in a lookup or table switch), the after "offset" adjusted during span-dependent instruction selection (initially the same value as the "before" offset), and the jump target (more below). Since we generate cg->spanDeps lazily, from within js_SetJumpOffset, we must ensure that all bytecode generated so far can be inspected to discover where the jump offset immediate operands lie within CG_CODE(cg). But the bonus is that we generate span-dependency records sorted by their offsets, so we can binary-search when trying to find a JSSpanDep for a given bytecode offset, or the nearest JSSpanDep at or above a given pc. To avoid limiting scripts to 64K jumps, if the cg->spanDeps index overflows 65534, we store SPANDEP_INDEX_HUGE in the jump's immediate operand. This tells us that we need to binary-search for the cg->spanDeps entry by the jump opcode's bytecode offset (sd->before). Jump targets need to be maintained in a data structure that lets us look up an already-known target by its address (jumps may have a common target), and that also lets us update the addresses (script-relative, a.k.a. absolute offsets) of targets that come after a jump target (for when a jump below that target needs to be extended). We use an AVL tree, implemented using recursion, but with some tricky optimizations to its height-balancing code (see http://www.enteract.com/~bradapp/ftp/src/libs/C++/AvlTrees.html). A final wrinkle: backpatch chains are linked by jump-to-jump offsets with positive sign, even though they link "backward" (i.e., toward lower bytecode address). We don't want to waste space and search time in the AVL tree for such temporary backpatch deltas, so we use a single-bit wildcard scheme to tag true JSJumpTarget pointers and encode untagged, signed (positive) deltas in JSSpanDep.target pointers, depending on whether the JSSpanDep has a known target, or is still awaiting backpatching. Note that backpatch chains would present a problem for BuildSpanDepTable, which inspects bytecode to build cg->spanDeps on demand, when the first short jump offset overflows. To solve this temporary problem, we emit a proxy bytecode (JSOP_BACKPATCH; JSOP_BACKPATCH_PUSH for jumps that push a result on the interpreter's stack, namely JSOP_GOSUB; or JSOP_BACKPATCH_POP for branch ops) whose nuses/ndefs counts help keep the stack balanced, but whose opcode format distinguishes its backpatch delta immediate operand from a normal jump offset. The cg->spanDeps array and JSJumpTarget structs are allocated from the cx->tempPool arena-pool. This created a LIFO vs. non-LIFO conflict: there were two places under the TOK_SWITCH case in js_EmitTree that used tempPool to allocate and release a chunk of memory, during whose lifetime JSSpanDep and/or JSJumpTarget structs might also be allocated from tempPool -- the ensuing release would prove disastrous. These bitmap and table temporaries are now allocated from the malloc heap. - jsinterp.c Straightforward cloning and JUMP => JUMPX mutating of the jump and switch format bytecode cases. - jsobj.c Silence warnings about %p used without (void *) casts. - jsopcode.c Massive and scary decompiler whackage to cope with extended jumps, using source note offsets to help find jumps whose format (short or long) can't be discovered from properties of prior instructions in the script. One cute hack here: long || and && expressions are broken up to wrap before the 80th column, with the operator at the end of each non-terminal line. - jsopcode.h, jsopcode.tbl The new extended jump opcodes, formats, and fundamental parameterization macros. Also, more comments. - jsparse.c Random and probably only aesthetic fix to avoid decorating a foo[i]++ or --foo[i] parse tree node with JSOP_SETCALL, wrongly (only foo(i)++ or --foo(i), or the other post- or prefix form operator, should have such an opcode decoration on its parse tree). - jsscript.h Random macro naming sanity: use trailing _ rather than leading _ for macro local variables in order to avoid invading the standard C global namespace.
2001-10-17 07:16:48 +04:00
case SRC_COND:
case SRC_IF_ELSE:
case SRC_WHILE:
case SRC_PCBASE:
case SRC_PCDELTA:
fprintf(gOutFile, " offset %u", (uintN) js_GetSrcNoteOffset(sn, 0));
break;
case SRC_LABEL:
case SRC_LABELBRACE:
case SRC_BREAK2LABEL:
case SRC_CONT2LABEL:
case SRC_FUNCDEF: {
const char *bytes;
JSFunction *fun;
JSString *str;
atomIndex = (jsatomid) js_GetSrcNoteOffset(sn, 0);
atom = js_GetAtom(cx, &script->atomMap, atomIndex);
if (type != SRC_FUNCDEF) {
bytes = ATOM_BYTES(atom);
} else {
fun = (JSFunction *)
JS_GetPrivate(cx, ATOM_TO_OBJECT(atom));
str = JS_DecompileFunction(cx, fun, JS_DONT_PRETTY_PRINT);
bytes = str ? JS_GetStringBytes(str) : "N/A";
}
fprintf(gOutFile, " atom %u (%s)", (uintN)atomIndex, bytes);
break;
}
case SRC_SWITCH:
fprintf(gOutFile, " length %u", (uintN) js_GetSrcNoteOffset(sn, 0));
caseOff = (uintN) js_GetSrcNoteOffset(sn, 1);
if (caseOff)
fprintf(gOutFile, " first case offset %u", caseOff);
break;
case SRC_CATCH:
delta = (uintN) js_GetSrcNoteOffset(sn, 0);
if (delta)
fprintf(gOutFile, " guard size %u", delta);
break;
default:;
}
fputc('\n', gOutFile);
}
1998-03-28 05:44:41 +03:00
}
}
static JSBool
Notes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
uintN i;
JSScript *script;
1998-03-28 05:44:41 +03:00
for (i = 0; i < argc; i++) {
script = ValueToScript(cx, argv[i]);
if (!script)
continue;
1998-03-28 05:44:41 +03:00
SrcNotes(cx, script);
1998-03-28 05:44:41 +03:00
}
return JS_TRUE;
}
static JSBool
TryNotes(JSContext *cx, JSScript *script)
{
JSTryNote *tn = script->trynotes;
if (!tn)
return JS_TRUE;
fprintf(gOutFile, "\nException table:\nstart\tend\tcatch\n");
while (tn->start && tn->catchStart) {
fprintf(gOutFile, " %d\t%d\t%d\n",
tn->start, tn->start + tn->length, tn->catchStart);
tn++;
}
return JS_TRUE;
}
1998-03-28 05:44:41 +03:00
static JSBool
Disassemble(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
JSBool lines;
uintN i;
JSScript *script;
1998-03-28 05:44:41 +03:00
if (argc > 0 &&
JSVAL_IS_STRING(argv[0]) &&
!strcmp(JS_GetStringBytes(JSVAL_TO_STRING(argv[0])), "-l")) {
lines = JS_TRUE;
argv++, argc--;
1998-03-28 05:44:41 +03:00
} else {
lines = JS_FALSE;
1998-03-28 05:44:41 +03:00
}
for (i = 0; i < argc; i++) {
script = ValueToScript(cx, argv[i]);
if (!script)
continue;
1998-03-28 05:44:41 +03:00
if (JSVAL_IS_FUNCTION(cx, argv[i])) {
JSFunction *fun = JS_ValueToFunction(cx, argv[i]);
if (fun && (fun->flags & JSFUN_FLAGS_MASK)) {
uint8 flags = fun->flags;
fputs("flags:", stdout);
#define SHOW_FLAG(flag) if (flags & JSFUN_##flag) fputs(" " #flag, stdout);
SHOW_FLAG(LAMBDA);
SHOW_FLAG(SETTER);
SHOW_FLAG(GETTER);
SHOW_FLAG(BOUND_METHOD);
SHOW_FLAG(HEAVYWEIGHT);
#undef SHOW_FLAG
putchar('\n');
}
}
js_Disassemble(cx, script, lines, stdout);
SrcNotes(cx, script);
TryNotes(cx, script);
1998-03-28 05:44:41 +03:00
}
return JS_TRUE;
}
static JSBool
DisassWithSrc(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
jsval *rval)
1998-03-28 05:44:41 +03:00
{
#define LINE_BUF_LEN 512
uintN i, len, line1, line2, bupline;
JSScript *script;
1998-03-28 05:44:41 +03:00
FILE *file;
char linebuf[LINE_BUF_LEN];
1998-03-28 05:44:41 +03:00
jsbytecode *pc, *end;
static char sep[] = ";-------------------------";
1998-03-28 05:44:41 +03:00
for (i = 0; i < argc; i++) {
script = ValueToScript(cx, argv[i]);
if (!script)
continue;
if (!script || !script->filename) {
JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
JSSMSG_FILE_SCRIPTS_ONLY);
return JS_FALSE;
}
file = fopen(script->filename, "r");
if (!file) {
JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
JSSMSG_CANT_OPEN,
script->filename, strerror(errno));
return JS_FALSE;
}
pc = script->code;
end = pc + script->length;
/* burn the leading lines */
line2 = JS_PCToLineNumber(cx, script, pc);
for (line1 = 0; line1 < line2 - 1; line1++)
fgets(linebuf, LINE_BUF_LEN, file);
bupline = 0;
while (pc < end) {
line2 = JS_PCToLineNumber(cx, script, pc);
if (line2 < line1) {
if (bupline != line2) {
bupline = line2;
fprintf(gOutFile, "%s %3u: BACKUP\n", sep, line2);
}
} else {
if (bupline && line1 == line2)
fprintf(gOutFile, "%s %3u: RESTORE\n", sep, line2);
bupline = 0;
while (line1 < line2) {
if (!fgets(linebuf, LINE_BUF_LEN, file)) {
JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
JSSMSG_UNEXPECTED_EOF,
script->filename);
goto bail;
}
line1++;
fprintf(gOutFile, "%s %3u: %s", sep, line1, linebuf);
}
}
len = js_Disassemble1(cx, script, pc,
PTRDIFF(pc, script->code, jsbytecode),
JS_TRUE, stdout);
if (!len)
return JS_FALSE;
pc += len;
}
bail:
fclose(file);
1998-03-28 05:44:41 +03:00
}
return JS_TRUE;
#undef LINE_BUF_LEN
1998-03-28 05:44:41 +03:00
}
static JSBool
Tracing(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
JSBool bval;
JSString *str;
if (argc == 0) {
*rval = BOOLEAN_TO_JSVAL(cx->tracefp != 0);
return JS_TRUE;
1998-03-28 05:44:41 +03:00
}
switch (JS_TypeOfValue(cx, argv[0])) {
case JSTYPE_NUMBER:
bval = JSVAL_IS_INT(argv[0])
? JSVAL_TO_INT(argv[0])
: (jsint) *JSVAL_TO_DOUBLE(argv[0]);
break;
1998-03-28 05:44:41 +03:00
case JSTYPE_BOOLEAN:
bval = JSVAL_TO_BOOLEAN(argv[0]);
break;
1998-03-28 05:44:41 +03:00
default:
str = JS_ValueToString(cx, argv[0]);
if (!str)
return JS_FALSE;
fprintf(gErrFile, "tracing: illegal argument %s\n",
JS_GetStringBytes(str));
return JS_TRUE;
1998-03-28 05:44:41 +03:00
}
cx->tracefp = bval ? stderr : NULL;
1998-03-28 05:44:41 +03:00
return JS_TRUE;
}
1999-09-16 11:08:49 +04:00
static int
DumpAtom(JSHashEntry *he, int i, void *arg)
1998-03-28 05:44:41 +03:00
{
FILE *fp = (FILE *) arg;
1998-03-28 05:44:41 +03:00
JSAtom *atom = (JSAtom *)he;
fprintf(fp, "%3d %08x %5lu ",
i, (uintN)he->keyHash, (unsigned long)atom->number);
1998-03-28 05:44:41 +03:00
if (ATOM_IS_STRING(atom))
fprintf(fp, "\"%s\"\n", ATOM_BYTES(atom));
1998-03-28 05:44:41 +03:00
else if (ATOM_IS_INT(atom))
fprintf(fp, "%ld\n", (long)ATOM_TO_INT(atom));
1998-03-28 05:44:41 +03:00
else
fprintf(fp, "%.16g\n", *ATOM_TO_DOUBLE(atom));
1998-03-28 05:44:41 +03:00
return HT_ENUMERATE_NEXT;
}
1999-09-16 11:08:49 +04:00
static void
DumpScope(JSContext *cx, JSObject *obj, FILE *fp)
1998-03-28 05:44:41 +03:00
{
uintN i;
1998-03-28 05:44:41 +03:00
JSScope *scope;
JSScopeProperty *sprop;
1998-03-28 05:44:41 +03:00
i = 0;
scope = OBJ_SCOPE(obj);
for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) {
if (SCOPE_HAD_MIDDLE_DELETE(scope) && !SCOPE_HAS_PROPERTY(scope, sprop))
continue;
fprintf(fp, "%3u %p", i, sprop);
if (sprop->id & JSVAL_INT)
fprintf(fp, " [%ld]", (long)JSVAL_TO_INT(sprop->id));
else
fprintf(fp, " \"%s\"", ATOM_BYTES((JSAtom *)sprop->id));
#define DUMP_ATTR(name) if (sprop->attrs & JSPROP_##name) fputs(" " #name, fp)
DUMP_ATTR(ENUMERATE);
DUMP_ATTR(READONLY);
DUMP_ATTR(PERMANENT);
DUMP_ATTR(EXPORTED);
DUMP_ATTR(GETTER);
DUMP_ATTR(SETTER);
#undef DUMP_ATTR
fprintf(fp, " slot %lu flags %x shortid %d\n",
sprop->slot, sprop->flags, sprop->shortid);
1998-03-28 05:44:41 +03:00
}
}
static JSBool
DumpStats(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
uintN i;
JSString *str;
const char *bytes;
JSAtom *atom;
JSObject *obj2;
1998-03-28 05:44:41 +03:00
JSProperty *prop;
jsval value;
1998-03-28 05:44:41 +03:00
for (i = 0; i < argc; i++) {
str = JS_ValueToString(cx, argv[i]);
if (!str)
return JS_FALSE;
bytes = JS_GetStringBytes(str);
if (strcmp(bytes, "arena") == 0) {
#ifdef JS_ARENAMETER
JS_DumpArenaStats(stdout);
1998-03-28 05:44:41 +03:00
#endif
} else if (strcmp(bytes, "atom") == 0) {
fprintf(gOutFile, "\natom table contents:\n");
JS_HashTableDump(cx->runtime->atomState.table, DumpAtom, stdout);
} else if (strcmp(bytes, "global") == 0) {
DumpScope(cx, cx->globalObject, stdout);
} else {
atom = js_Atomize(cx, bytes, JS_GetStringLength(str), 0);
if (!atom)
return JS_FALSE;
if (!js_FindProperty(cx, (jsid)atom, &obj, &obj2, &prop))
return JS_FALSE;
if (prop) {
OBJ_DROP_PROPERTY(cx, obj2, prop);
if (!OBJ_GET_PROPERTY(cx, obj, (jsid)atom, &value))
return JS_FALSE;
}
if (!prop || !JSVAL_IS_OBJECT(value)) {
fprintf(gErrFile, "js: invalid stats argument %s\n",
bytes);
continue;
}
obj = JSVAL_TO_OBJECT(value);
if (obj)
DumpScope(cx, obj, stdout);
}
1998-03-28 05:44:41 +03:00
}
return JS_TRUE;
}
#endif /* DEBUG */
#ifdef TEST_EXPORT
static JSBool
DoExport(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
JSAtom *atom;
JSObject *obj2;
1998-03-28 05:44:41 +03:00
JSProperty *prop;
JSBool ok;
uintN attrs;
1998-03-28 05:44:41 +03:00
if (argc != 2) {
JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_DOEXP_USAGE);
return JS_FALSE;
1998-03-28 05:44:41 +03:00
}
if (!JS_ValueToObject(cx, argv[0], &obj))
return JS_FALSE;
1998-03-28 05:44:41 +03:00
argv[0] = OBJECT_TO_JSVAL(obj);
atom = js_ValueToStringAtom(cx, argv[1]);
if (!atom)
return JS_FALSE;
if (!OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &obj2, &prop))
return JS_FALSE;
if (!prop) {
ok = OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, NULL, NULL,
JSPROP_EXPORTED, NULL);
} else {
ok = OBJ_GET_ATTRIBUTES(cx, obj, (jsid)atom, prop, &attrs);
if (ok) {
attrs |= JSPROP_EXPORTED;
ok = OBJ_SET_ATTRIBUTES(cx, obj, (jsid)atom, prop, &attrs);
}
OBJ_DROP_PROPERTY(cx, obj2, prop);
}
return ok;
}
#endif
#ifdef TEST_CVTARGS
#include <ctype.h>
static const char *
EscapeWideString(jschar *w)
{
static char enuf[80];
static char hex[] = "0123456789abcdef";
jschar u;
unsigned char b, c;
int i, j;
if (!w)
return "";
for (i = j = 0; i < sizeof enuf - 1; i++, j++) {
u = w[j];
if (u == 0)
break;
b = (unsigned char)(u >> 8);
c = (unsigned char)(u);
if (b) {
if (i >= sizeof enuf - 6)
break;
enuf[i++] = '\\';
enuf[i++] = 'u';
enuf[i++] = hex[b >> 4];
enuf[i++] = hex[b & 15];
enuf[i++] = hex[c >> 4];
enuf[i] = hex[c & 15];
} else if (!isprint(c)) {
if (i >= sizeof enuf - 4)
break;
enuf[i++] = '\\';
enuf[i++] = 'x';
enuf[i++] = hex[c >> 4];
enuf[i] = hex[c & 15];
} else {
enuf[i] = (char)c;
}
}
enuf[i] = 0;
return enuf;
}
#include <stdarg.h>
static JSBool
ZZ_formatter(JSContext *cx, const char *format, JSBool fromJS, jsval **vpp,
va_list *app)
{
jsval *vp;
va_list ap;
jsdouble re, im;
printf("entering ZZ_formatter");
vp = *vpp;
ap = *app;
if (fromJS) {
if (!JS_ValueToNumber(cx, vp[0], &re))
return JS_FALSE;
if (!JS_ValueToNumber(cx, vp[1], &im))
return JS_FALSE;
*va_arg(ap, jsdouble *) = re;
*va_arg(ap, jsdouble *) = im;
} else {
re = va_arg(ap, jsdouble);
im = va_arg(ap, jsdouble);
if (!JS_NewNumberValue(cx, re, &vp[0]))
return JS_FALSE;
if (!JS_NewNumberValue(cx, im, &vp[1]))
return JS_FALSE;
}
*vpp = vp + 2;
*app = ap;
printf("leaving ZZ_formatter");
return JS_TRUE;
}
static JSBool
ConvertArgs(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
JSBool b = JS_FALSE;
jschar c = 0;
int32 i = 0, j = 0;
uint32 u = 0;
jsdouble d = 0, I = 0, re = 0, im = 0;
char *s = NULL;
JSString *str = NULL;
jschar *w = NULL;
JSObject *obj2 = NULL;
JSFunction *fun = NULL;
jsval v = JSVAL_VOID;
JSBool ok;
if (!JS_AddArgumentFormatter(cx, "ZZ", ZZ_formatter))
return JS_FALSE;;
ok = JS_ConvertArguments(cx, argc, argv, "b/ciujdIsSWofvZZ*",
&b, &c, &i, &u, &j, &d, &I, &s, &str, &w, &obj2,
&fun, &v, &re, &im);
JS_RemoveArgumentFormatter(cx, "ZZ");
if (!ok)
return JS_FALSE;
fprintf(gOutFile,
"b %u, c %x (%c), i %ld, u %lu, j %ld\n",
b, c, (char)c, i, u, j);
fprintf(gOutFile,
"d %g, I %g, s %s, S %s, W %s, obj %s, fun %s\n"
"v %s, re %g, im %g\n",
d, I, s, str ? JS_GetStringBytes(str) : "", EscapeWideString(w),
JS_GetStringBytes(JS_ValueToString(cx, OBJECT_TO_JSVAL(obj2))),
fun ? JS_GetStringBytes(JS_DecompileFunction(cx, fun, 4)) : "",
JS_GetStringBytes(JS_ValueToString(cx, v)), re, im);
1998-03-28 05:44:41 +03:00
return JS_TRUE;
}
#endif
static JSBool
BuildDate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
fprintf(gOutFile, "built on %s at %s\n", __DATE__, __TIME__);
return JS_TRUE;
}
static JSBool
Clear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
if (argc != 0 && !JS_ValueToObject(cx, argv[0], &obj))
return JS_FALSE;
JS_ClearScope(cx, obj);
return JS_TRUE;
}
static JSBool
Intern(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
JSString *str;
str = JS_ValueToString(cx, argv[0]);
if (!str)
return JS_FALSE;
if (!JS_InternUCStringN(cx, JS_GetStringChars(str),
JS_GetStringLength(str))) {
return JS_FALSE;
}
return JS_TRUE;
}
static JSBool
Clone(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
JSFunction *fun;
JSObject *funobj, *parent, *clone;
fun = JS_ValueToFunction(cx, argv[0]);
if (!fun)
return JS_FALSE;
funobj = JS_GetFunctionObject(fun);
if (argc > 1) {
if (!JS_ValueToObject(cx, argv[1], &parent))
return JS_FALSE;
} else {
parent = JS_GetParent(cx, funobj);
}
clone = JS_CloneFunctionObject(cx, funobj, parent);
if (!clone)
return JS_FALSE;
*rval = OBJECT_TO_JSVAL(clone);
return JS_TRUE;
}
1998-03-28 05:44:41 +03:00
static JSFunctionSpec shell_functions[] = {
{"version", Version, 0},
{"options", Options, 0},
1998-03-28 05:44:41 +03:00
{"load", Load, 1},
{"print", Print, 0},
{"help", Help, 0},
{"quit", Quit, 0},
{"gc", GC, 0},
{"trap", Trap, 3},
{"untrap", Untrap, 2},
{"line2pc", LineToPC, 0},
{"pc2line", PCToLine, 0},
#ifdef DEBUG
{"dis", Disassemble, 1},
{"dissrc", DisassWithSrc, 1},
{"notes", Notes, 1},
{"tracing", Tracing, 0},
{"stats", DumpStats, 1},
#endif
#ifdef TEST_EXPORT
{"xport", DoExport, 2},
#endif
#ifdef TEST_CVTARGS
{"cvtargs", ConvertArgs, 0, 0, 12},
1998-03-28 05:44:41 +03:00
#endif
{"build", BuildDate, 0},
{"clear", Clear, 0},
{"intern", Intern, 1},
{"clone", Clone, 1},
1998-03-28 05:44:41 +03:00
{0}
};
/* NOTE: These must be kept in sync with the above. */
static char *shell_help_messages[] = {
"version([number]) Get or set JavaScript version number",
"options([option ...]) Get or toggle JavaScript options",
"load(['foo.js' ...]) Load files named by string arguments",
"print([exp ...]) Evaluate and print expressions",
"help([name ...]) Display usage and help messages",
"quit() Quit the shell",
"gc() Run the garbage collector",
"trap([fun, [pc,]] exp) Trap bytecode execution",
"untrap(fun[, pc]) Remove a trap",
"line2pc([fun,] line) Map line number to PC",
"pc2line(fun[, pc]) Map PC to line number",
1998-03-28 05:44:41 +03:00
#ifdef DEBUG
"dis([fun]) Disassemble functions into bytecodes",
"dissrc([fun]) Disassemble functions with source lines",
"notes([fun]) Show source notes for functions",
"tracing([toggle]) Turn tracing on or off",
"stats([string ...]) Dump 'arena', 'atom', 'global' stats",
1998-03-28 05:44:41 +03:00
#endif
#ifdef TEST_EXPORT
"xport(obj, id) Export identified property from object",
#endif
#ifdef TEST_CVTARGS
"cvtargs(b, c, ...) Test JS_ConvertArguments",
1998-03-28 05:44:41 +03:00
#endif
"build() Show build date and time",
"clear([obj]) Clear properties of object",
"intern(str) Internalize str in the atom table",
"clone(fun[, scope]) Clone function object",
1998-03-28 05:44:41 +03:00
0
};
static void
ShowHelpHeader(void)
{
fprintf(gOutFile, "%-9s %-22s %s\n", "Command", "Usage", "Description");
fprintf(gOutFile, "%-9s %-22s %s\n", "=======", "=====", "===========");
1998-03-28 05:44:41 +03:00
}
static void
ShowHelpForCommand(uintN n)
{
fprintf(gOutFile, "%-9.9s %s\n", shell_functions[n].name, shell_help_messages[n]);
1998-03-28 05:44:41 +03:00
}
static JSBool
Help(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
uintN i, j;
int did_header, did_something;
JSType type;
JSFunction *fun;
JSString *str;
const char *bytes;
fprintf(gOutFile, "%s\n", JS_GetImplementationVersion());
1998-03-28 05:44:41 +03:00
if (argc == 0) {
ShowHelpHeader();
for (i = 0; shell_functions[i].name; i++)
ShowHelpForCommand(i);
1998-03-28 05:44:41 +03:00
} else {
did_header = 0;
for (i = 0; i < argc; i++) {
did_something = 0;
type = JS_TypeOfValue(cx, argv[i]);
if (type == JSTYPE_FUNCTION) {
fun = JS_ValueToFunction(cx, argv[i]);
str = fun->atom ? ATOM_TO_STRING(fun->atom) : NULL;
} else if (type == JSTYPE_STRING) {
str = JSVAL_TO_STRING(argv[i]);
} else {
str = NULL;
}
if (str) {
bytes = JS_GetStringBytes(str);
for (j = 0; shell_functions[j].name; j++) {
if (!strcmp(bytes, shell_functions[j].name)) {
if (!did_header) {
did_header = 1;
ShowHelpHeader();
}
did_something = 1;
ShowHelpForCommand(j);
break;
}
}
}
if (!did_something) {
str = JS_ValueToString(cx, argv[i]);
if (!str)
return JS_FALSE;
fprintf(gErrFile, "Sorry, no help for %s\n",
JS_GetStringBytes(str));
}
}
1998-03-28 05:44:41 +03:00
}
return JS_TRUE;
}
/*
* Define a JS object called "it". Give it class operations that printf why
* they're being called for tutorial purposes.
*/
enum its_tinyid {
ITS_COLOR, ITS_HEIGHT, ITS_WIDTH, ITS_FUNNY, ITS_ARRAY, ITS_RDONLY
};
static JSPropertySpec its_props[] = {
{"color", ITS_COLOR, JSPROP_ENUMERATE},
{"height", ITS_HEIGHT, JSPROP_ENUMERATE},
{"width", ITS_WIDTH, JSPROP_ENUMERATE},
{"funny", ITS_FUNNY, JSPROP_ENUMERATE},
{"array", ITS_ARRAY, JSPROP_ENUMERATE},
{"rdonly", ITS_RDONLY, JSPROP_READONLY},
1998-03-28 05:44:41 +03:00
{0}
};
static JSBool
its_item(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
*rval = OBJECT_TO_JSVAL(obj);
if (argc != 0)
JS_SetCallReturnValue2(cx, argv[0]);
return JS_TRUE;
}
static JSFunctionSpec its_methods[] = {
{"item", its_item, 0},
{0}
};
#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 */
1998-03-28 05:44:41 +03:00
static JSBool its_noisy; /* whether to be noisy when finalizing it */
static JSBool
its_addProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
if (its_noisy) {
fprintf(gOutFile, "adding its property %s,",
JS_GetStringBytes(JS_ValueToString(cx, id)));
fprintf(gOutFile, " initial value %s\n",
JS_GetStringBytes(JS_ValueToString(cx, *vp)));
1998-03-28 05:44:41 +03:00
}
return JS_TRUE;
}
static JSBool
its_delProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
if (its_noisy) {
fprintf(gOutFile, "deleting its property %s,",
JS_GetStringBytes(JS_ValueToString(cx, id)));
fprintf(gOutFile, " current value %s\n",
JS_GetStringBytes(JS_ValueToString(cx, *vp)));
1998-03-28 05:44:41 +03:00
}
return JS_TRUE;
}
static JSBool
its_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
if (its_noisy) {
fprintf(gOutFile, "getting its property %s,",
JS_GetStringBytes(JS_ValueToString(cx, id)));
fprintf(gOutFile, " current value %s\n",
JS_GetStringBytes(JS_ValueToString(cx, *vp)));
1998-03-28 05:44:41 +03:00
}
return JS_TRUE;
}
static JSBool
its_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
if (its_noisy) {
fprintf(gOutFile, "setting its property %s,",
JS_GetStringBytes(JS_ValueToString(cx, id)));
fprintf(gOutFile, " new value %s\n",
JS_GetStringBytes(JS_ValueToString(cx, *vp)));
1998-03-28 05:44:41 +03:00
}
if (JSVAL_IS_STRING(id) &&
!strcmp(JS_GetStringBytes(JSVAL_TO_STRING(id)), "noisy")) {
return JS_ValueToBoolean(cx, *vp, &its_noisy);
1998-03-28 05:44:41 +03:00
}
return JS_TRUE;
}
static JSBool
its_enumerate(JSContext *cx, JSObject *obj)
{
if (its_noisy)
fprintf(gOutFile, "enumerate its properties\n");
1998-03-28 05:44:41 +03:00
return JS_TRUE;
}
static JSBool
its_resolve(JSContext *cx, JSObject *obj, jsval id)
{
if (its_noisy) {
fprintf(gOutFile, "resolving its property %s\n",
JS_GetStringBytes(JS_ValueToString(cx, id)));
1998-03-28 05:44:41 +03:00
}
return JS_TRUE;
}
static JSBool
its_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
{
if (its_noisy)
fprintf(gOutFile, "converting it to %s type\n", JS_GetTypeName(cx, type));
1998-03-28 05:44:41 +03:00
return JS_TRUE;
}
static void
its_finalize(JSContext *cx, JSObject *obj)
{
if (its_noisy)
fprintf(gOutFile, "finalizing it\n");
1998-03-28 05:44:41 +03:00
}
static JSClass its_class = {
"It", 0,
its_addProperty, its_delProperty, its_getProperty, its_setProperty,
its_enumerate, its_resolve, its_convert, its_finalize
};
JSErrorFormatString jsShell_ErrorFormatString[JSErr_Limit] = {
#if JS_HAS_DFLT_MSG_STRINGS
#define MSG_DEF(name, number, count, exception, format) \
{ format, count } ,
#else
#define MSG_DEF(name, number, count, exception, format) \
{ NULL, count } ,
#endif
#include "jsshell.msg"
#undef MSG_DEF
};
static const JSErrorFormatString *
my_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber)
{
if ((errorNumber > 0) && (errorNumber < JSShellErr_Limit))
return &jsShell_ErrorFormatString[errorNumber];
return NULL;
}
static void
my_LoadErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
{
if (!report) {
fprintf(gErrFile, "%s\n", message);
return;
}
/* Ignore any exceptions */
if (JSREPORT_IS_EXCEPTION(report->flags))
return;
/* Otherwise, fall back to the ordinary error reporter. */
my_ErrorReporter(cx, message, report);
}
1998-03-28 05:44:41 +03:00
static void
my_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
{
int i, j, k, n;
char *prefix, *tmp;
const char *ctmp;
1998-03-28 05:44:41 +03:00
if (!report) {
fprintf(gErrFile, "%s\n", message);
return;
1998-03-28 05:44:41 +03:00
}
/* Conditionally ignore reported warnings. */
if (JSREPORT_IS_WARNING(report->flags) && !reportWarnings)
return;
prefix = NULL;
1998-03-28 05:44:41 +03:00
if (report->filename)
prefix = JS_smprintf("%s:", report->filename);
if (report->lineno) {
tmp = prefix;
prefix = JS_smprintf("%s%u: ", tmp ? tmp : "", report->lineno);
JS_free(cx, tmp);
}
if (JSREPORT_IS_WARNING(report->flags)) {
tmp = prefix;
prefix = JS_smprintf("%s%swarning: ",
tmp ? tmp : "",
JSREPORT_IS_STRICT(report->flags) ? "strict " : "");
JS_free(cx, tmp);
}
/* embedded newlines -- argh! */
while ((ctmp = strchr(message, '\n')) != 0) {
ctmp++;
if (prefix)
fputs(prefix, gErrFile);
fwrite(message, 1, ctmp - message, gErrFile);
message = ctmp;
}
/* If there were no filename or lineno, the prefix might be empty */
if (prefix)
fputs(prefix, gErrFile);
fputs(message, gErrFile);
1998-03-28 05:44:41 +03:00
if (!report->linebuf) {
fputc('\n', gErrFile);
goto out;
1998-03-28 05:44:41 +03:00
}
/* report->linebuf usually ends with a newline. */
n = strlen(report->linebuf);
fprintf(gErrFile, ":\n%s%s%s%s",
prefix,
report->linebuf,
(n > 0 && report->linebuf[n-1] == '\n') ? "" : "\n",
prefix);
n = PTRDIFF(report->tokenptr, report->linebuf, char);
1998-03-28 05:44:41 +03:00
for (i = j = 0; i < n; i++) {
if (report->linebuf[i] == '\t') {
for (k = (j + 8) & ~7; j < k; j++) {
fputc('.', gErrFile);
}
continue;
}
fputc('.', gErrFile);
j++;
1998-03-28 05:44:41 +03:00
}
fputs("^\n", gErrFile);
out:
if (!JSREPORT_IS_WARNING(report->flags))
gExitCode = EXITCODE_RUNTIME_ERROR;
JS_free(cx, prefix);
1998-03-28 05:44:41 +03:00
}
#if defined(SHELL_HACK) && defined(DEBUG) && defined(XP_UNIX)
1998-03-28 05:44:41 +03:00
static JSBool
Exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
JSFunction *fun;
const char *name, **nargv;
uintN i, nargc;
JSString *str;
pid_t pid;
int status;
fun = JS_ValueToFunction(cx, argv[-2]);
if (!fun)
return JS_FALSE;
1998-03-28 05:44:41 +03:00
if (!fun->atom)
return JS_TRUE;
1998-03-28 05:44:41 +03:00
name = ATOM_BYTES(fun->atom);
nargc = 1 + argc;
nargv = JS_malloc(cx, (nargc + 1) * sizeof(char *));
if (!nargv)
return JS_FALSE;
1998-03-28 05:44:41 +03:00
nargv[0] = name;
for (i = 1; i < nargc; i++) {
str = JS_ValueToString(cx, argv[i-1]);
if (!str) {
JS_free(cx, nargv);
return JS_FALSE;
}
nargv[i] = JS_GetStringBytes(str);
1998-03-28 05:44:41 +03:00
}
nargv[nargc] = 0;
pid = fork();
switch (pid) {
case -1:
perror("js");
break;
1998-03-28 05:44:41 +03:00
case 0:
(void) execvp(name, (char **)nargv);
perror("js");
exit(127);
1998-03-28 05:44:41 +03:00
default:
while (waitpid(pid, &status, 0) < 0 && errno == EINTR)
continue;
break;
1998-03-28 05:44:41 +03:00
}
JS_free(cx, nargv);
return JS_TRUE;
}
#endif
#define LAZY_STANDARD_CLASSES
static JSBool
global_enumerate(JSContext *cx, JSObject *obj)
{
#ifdef LAZY_STANDARD_CLASSES
return JS_EnumerateStandardClasses(cx, obj);
#else
return JS_TRUE;
#endif
}
1998-03-28 05:44:41 +03:00
static JSBool
global_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
JSObject **objp)
1998-03-28 05:44:41 +03:00
{
#ifdef LAZY_STANDARD_CLASSES
if ((flags & JSRESOLVE_ASSIGNING) == 0) {
JSBool resolved;
if (!JS_ResolveStandardClass(cx, obj, id, &resolved))
return JS_FALSE;
if (resolved) {
*objp = obj;
return JS_TRUE;
}
}
#endif
#if defined(SHELL_HACK) && defined(DEBUG) && defined(XP_UNIX)
if ((flags & (JSRESOLVE_QUALIFIED | JSRESOLVE_ASSIGNING)) == 0) {
/*
* Do this expensive hack only for unoptimized Unix builds, which are
* not used for benchmarking.
*/
char *path, *comp, *full;
const char *name;
JSBool ok, found;
JSFunction *fun;
if (!JSVAL_IS_STRING(id))
return JS_TRUE;
path = getenv("PATH");
if (!path)
return JS_TRUE;
path = JS_strdup(cx, path);
if (!path)
return JS_FALSE;
name = JS_GetStringBytes(JSVAL_TO_STRING(id));
ok = JS_TRUE;
for (comp = strtok(path, ":"); comp; comp = strtok(NULL, ":")) {
if (*comp != '\0') {
full = JS_smprintf("%s/%s", comp, name);
if (!full) {
JS_ReportOutOfMemory(cx);
ok = JS_FALSE;
break;
}
} else {
full = (char *)name;
}
found = (access(full, X_OK) == 0);
if (*comp != '\0')
free(full);
if (found) {
fun = JS_DefineFunction(cx, obj, name, Exec, 0,
JSPROP_ENUMERATE);
ok = (fun != NULL);
if (ok)
*objp = obj;
break;
}
}
JS_free(cx, path);
return ok;
1998-03-28 05:44:41 +03:00
}
#else
return JS_TRUE;
#endif
}
static JSClass global_class = {
"global", JSCLASS_NEW_RESOLVE,
JS_PropertyStub, JS_PropertyStub,
JS_PropertyStub, JS_PropertyStub,
global_enumerate, (JSResolveOp) global_resolve,
JS_ConvertStub, JS_FinalizeStub
1998-03-28 05:44:41 +03:00
};
int
main(int argc, char **argv)
{
JSVersion version;
JSRuntime *rt;
JSContext *cx;
JSObject *glob, *it;
int result;
#ifdef LIVECONNECT
JavaVM *java_vm = NULL;
#endif
#ifdef JSDEBUGGER_JAVA_UI
JNIEnv *java_env;
#endif
1998-03-28 05:44:41 +03:00
#ifdef XP_OS2
/* these streams are normally line buffered on OS/2 and need a \n, *
* so we need to unbuffer then to get a reasonable prompt */
setbuf(stdout,0);
setbuf(stderr,0);
#endif
gErrFile = stderr;
gOutFile = stdout;
#ifdef XP_MAC
#ifndef XP_MAC_MPW
initConsole("\pJavaScript Shell", "Welcome to js shell.", &argc, &argv);
#endif
#endif
#ifdef MAC_TEST_HACK
/*
Open a file "testArgs.txt" and read each line into argc/argv.
Re-direct all output to "results.txt"
*/
{
char argText[256];
FILE *f = fopen("testargs.txt", "r");
if (f != NULL) {
int maxArgs = 32; /* arbitrary max !!! */
int argText_strlen;
argc = 1;
argv = malloc(sizeof(char *) * maxArgs);
argv[0] = NULL;
while (fgets(argText, 255, f) != NULL) {
/* argText includes '\n' */
argText_strlen = strlen(argText);
argv[argc] = malloc(argText_strlen);
strncpy(argv[argc], argText,
argText_strlen - 1);
argv[argc][argText_strlen - 1] = '\0';
argc++;
if (argc >= maxArgs) break;
}
fclose(f);
}
gTestResultFile = fopen("results.txt", "w");
}
gErrFile = gTestResultFile;
gOutFile = gTestResultFile;
#endif
1998-03-28 05:44:41 +03:00
version = JSVERSION_DEFAULT;
1998-03-28 05:44:41 +03:00
argc--;
argv++;
rt = JS_NewRuntime(8L * 1024L * 1024L);
1998-03-28 05:44:41 +03:00
if (!rt)
return 1;
cx = JS_NewContext(rt, gStackChunkSize);
1998-03-28 05:44:41 +03:00
if (!cx)
return 1;
JS_SetErrorReporter(cx, my_ErrorReporter);
1998-03-28 05:44:41 +03:00
glob = JS_NewObject(cx, &global_class, NULL, NULL);
if (!glob)
return 1;
#ifdef LAZY_STANDARD_CLASSES
JS_SetGlobalObject(cx, glob);
#else
1998-03-28 05:44:41 +03:00
if (!JS_InitStandardClasses(cx, glob))
return 1;
#endif
1998-03-28 05:44:41 +03:00
if (!JS_DefineFunctions(cx, glob, shell_functions))
return 1;
1998-03-28 05:44:41 +03:00
/* Set version only after there is a global object. */
if (version != JSVERSION_DEFAULT)
JS_SetVersion(cx, version);
1998-03-28 05:44:41 +03:00
it = JS_DefineObject(cx, glob, "it", &its_class, NULL, 0);
if (!it)
return 1;
1998-03-28 05:44:41 +03:00
if (!JS_DefineProperties(cx, it, its_props))
return 1;
if (!JS_DefineFunctions(cx, it, its_methods))
return 1;
1998-03-28 05:44:41 +03:00
#ifdef PERLCONNECT
if (!JS_InitPerlClass(cx, glob))
return 1;
#endif
#ifdef JSDEBUGGER
/*
* XXX A command line option to enable debugging (or not) would be good
*/
_jsdc = JSD_DebuggerOnForUser(rt, NULL, NULL);
if (!_jsdc)
return 1;
JSD_JSContextInUse(_jsdc, cx);
#ifdef JSD_LOWLEVEL_SOURCE
JS_SetSourceHandler(rt, SendSourceToJSDebugger, _jsdc);
#endif /* JSD_LOWLEVEL_SOURCE */
#ifdef JSDEBUGGER_JAVA_UI
_jsdjc = JSDJ_CreateContext();
if (! _jsdjc)
return 1;
JSDJ_SetJSDContext(_jsdjc, _jsdc);
java_env = JSDJ_CreateJavaVMAndStartDebugger(_jsdjc);
#ifdef LIVECONNECT
if (java_env)
(*java_env)->GetJavaVM(java_env, &java_vm);
#endif
/*
* XXX This would be the place to wait for the debugger to start.
* Waiting would be nice in general, but especially when a js file
* is passed on the cmd line.
*/
#endif /* JSDEBUGGER_JAVA_UI */
#ifdef JSDEBUGGER_C_UI
JSDB_InitDebugger(rt, _jsdc, 0);
#endif /* JSDEBUGGER_C_UI */
#endif /* JSDEBUGGER */
#ifdef LIVECONNECT
if (!JSJ_SimpleInit(cx, glob, java_vm, getenv("CLASSPATH")))
return 1;
#endif
result = ProcessArgs(cx, glob, argv, argc);
1998-03-28 05:44:41 +03:00
#ifdef JSDEBUGGER
if (_jsdc)
JSD_DebuggerOff(_jsdc);
#endif /* JSDEBUGGER */
#ifdef MAC_TEST_HACK
fclose(gTestResultFile);
#endif
1998-03-28 05:44:41 +03:00
JS_DestroyContext(cx);
JS_DestroyRuntime(rt);
JS_ShutDown();
return result;
1998-03-28 05:44:41 +03:00
}