зеркало из https://github.com/mozilla/pjs.git
2145 строки
59 KiB
C
2145 строки
59 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.1 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.mozilla.org/NPL/
|
|
*
|
|
* 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.
|
|
*
|
|
* The Original Code is Mozilla Communicator client code, released
|
|
* March 31, 1998.
|
|
*
|
|
* 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):
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
/*
|
|
* JS shell.
|
|
*/
|
|
#include "jsstddef.h"
|
|
#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"
|
|
#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 */
|
|
|
|
#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;
|
|
|
|
#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
|
|
#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
|
|
#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;
|
|
}
|
|
|
|
static void
|
|
Process(JSContext *cx, JSObject *obj, char *filename)
|
|
{
|
|
JSBool ok, hitEOF;
|
|
JSScript *script;
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
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])));
|
|
else
|
|
*rval = INT_TO_JSVAL(JS_GetVersion(cx));
|
|
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);
|
|
|
|
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;
|
|
|
|
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;
|
|
}
|
|
|
|
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));
|
|
}
|
|
n++;
|
|
if (n)
|
|
fputc('\n', gOutFile);
|
|
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);
|
|
return JS_FALSE;
|
|
}
|
|
|
|
#ifdef GC_MARK_DEBUG
|
|
extern JS_FRIEND_DATA(FILE *) js_DumpGCHeap;
|
|
#endif
|
|
|
|
static JSBool
|
|
GC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
|
{
|
|
JSRuntime *rt;
|
|
uint32 preBytes;
|
|
|
|
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;
|
|
}
|
|
#endif
|
|
JS_GC(cx);
|
|
#ifdef GC_MARK_DEBUG
|
|
if (js_DumpGCHeap != stdout)
|
|
fclose(js_DumpGCHeap);
|
|
js_DumpGCHeap = NULL;
|
|
#endif
|
|
fprintf(gOutFile, "before %lu, after %lu, break %08lx\n",
|
|
(unsigned long)preBytes, (unsigned long)rt->gcBytes,
|
|
#ifdef XP_UNIX
|
|
(unsigned long)sbrk(0)
|
|
#else
|
|
0
|
|
#endif
|
|
);
|
|
#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;
|
|
}
|
|
|
|
static JSBool
|
|
GetTrapArgs(JSContext *cx, uintN argc, jsval *argv, JSScript **scriptp,
|
|
int32 *ip)
|
|
{
|
|
uintN intarg;
|
|
JSScript *script;
|
|
|
|
*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;
|
|
}
|
|
}
|
|
return JS_TRUE;
|
|
}
|
|
|
|
static JSTrapStatus
|
|
TrapHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval,
|
|
void *closure)
|
|
{
|
|
JSString *str;
|
|
JSStackFrame *caller;
|
|
|
|
str = (JSString *) closure;
|
|
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;
|
|
}
|
|
if (*rval != JSVAL_VOID)
|
|
return JSTRAP_RETURN;
|
|
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;
|
|
}
|
|
argc--;
|
|
str = JS_ValueToString(cx, argv[argc]);
|
|
if (!str)
|
|
return JS_FALSE;
|
|
argv[argc] = STRING_TO_JSVAL(str);
|
|
if (!GetTrapArgs(cx, argc, argv, &script, &i))
|
|
return JS_FALSE;
|
|
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;
|
|
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;
|
|
}
|
|
script = cx->fp->down->script;
|
|
if (!GetTrapArgs(cx, argc, argv, &script, &i))
|
|
return JS_FALSE;
|
|
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));
|
|
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;
|
|
lineno = JS_PCToLineNumber(cx, script, script->code + i);
|
|
if (!lineno)
|
|
return JS_FALSE;
|
|
*rval = INT_TO_JSVAL(lineno);
|
|
return JS_TRUE;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
|
|
static void
|
|
SrcNotes(JSContext *cx, JSScript *script)
|
|
{
|
|
uintN offset, delta, caseOff;
|
|
jssrcnote *notes, *sn;
|
|
JSSrcNoteType type;
|
|
jsatomid atomIndex;
|
|
JSAtom *atom;
|
|
|
|
notes = script->notes;
|
|
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,
|
|
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;
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
static JSBool
|
|
Notes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
|
{
|
|
uintN i;
|
|
JSScript *script;
|
|
|
|
for (i = 0; i < argc; i++) {
|
|
script = ValueToScript(cx, argv[i]);
|
|
if (!script)
|
|
continue;
|
|
|
|
SrcNotes(cx, script);
|
|
}
|
|
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;
|
|
}
|
|
|
|
static JSBool
|
|
Disassemble(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
|
{
|
|
JSBool lines;
|
|
uintN i;
|
|
JSScript *script;
|
|
|
|
if (argc > 0 &&
|
|
JSVAL_IS_STRING(argv[0]) &&
|
|
!strcmp(JS_GetStringBytes(JSVAL_TO_STRING(argv[0])), "-l")) {
|
|
lines = JS_TRUE;
|
|
argv++, argc--;
|
|
} else {
|
|
lines = JS_FALSE;
|
|
}
|
|
for (i = 0; i < argc; i++) {
|
|
script = ValueToScript(cx, argv[i]);
|
|
if (!script)
|
|
continue;
|
|
|
|
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);
|
|
}
|
|
return JS_TRUE;
|
|
}
|
|
|
|
static JSBool
|
|
DisassWithSrc(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
|
jsval *rval)
|
|
{
|
|
#define LINE_BUF_LEN 512
|
|
uintN i, len, line1, line2, bupline;
|
|
JSScript *script;
|
|
FILE *file;
|
|
char linebuf[LINE_BUF_LEN];
|
|
jsbytecode *pc, *end;
|
|
static char sep[] = ";-------------------------";
|
|
|
|
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);
|
|
}
|
|
return JS_TRUE;
|
|
#undef LINE_BUF_LEN
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
case JSTYPE_BOOLEAN:
|
|
bval = JSVAL_TO_BOOLEAN(argv[0]);
|
|
break;
|
|
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;
|
|
}
|
|
cx->tracefp = bval ? stderr : NULL;
|
|
return JS_TRUE;
|
|
}
|
|
|
|
static int
|
|
DumpAtom(JSHashEntry *he, int i, void *arg)
|
|
{
|
|
FILE *fp = (FILE *) arg;
|
|
JSAtom *atom = (JSAtom *)he;
|
|
|
|
fprintf(fp, "%3d %08x %5lu ",
|
|
i, (uintN)he->keyHash, (unsigned long)atom->number);
|
|
if (ATOM_IS_STRING(atom))
|
|
fprintf(fp, "\"%s\"\n", ATOM_BYTES(atom));
|
|
else if (ATOM_IS_INT(atom))
|
|
fprintf(fp, "%ld\n", (long)ATOM_TO_INT(atom));
|
|
else
|
|
fprintf(fp, "%.16g\n", *ATOM_TO_DOUBLE(atom));
|
|
return HT_ENUMERATE_NEXT;
|
|
}
|
|
|
|
static void
|
|
DumpScope(JSContext *cx, JSObject *obj, FILE *fp)
|
|
{
|
|
uintN i;
|
|
JSScope *scope;
|
|
JSScopeProperty *sprop;
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
static JSBool
|
|
DumpStats(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
|
{
|
|
uintN i;
|
|
JSString *str;
|
|
const char *bytes;
|
|
JSAtom *atom;
|
|
JSObject *obj2;
|
|
JSProperty *prop;
|
|
jsval value;
|
|
|
|
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);
|
|
#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);
|
|
}
|
|
}
|
|
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;
|
|
JSProperty *prop;
|
|
JSBool ok;
|
|
uintN attrs;
|
|
|
|
if (argc != 2) {
|
|
JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_DOEXP_USAGE);
|
|
return JS_FALSE;
|
|
}
|
|
if (!JS_ValueToObject(cx, argv[0], &obj))
|
|
return JS_FALSE;
|
|
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);
|
|
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;
|
|
}
|
|
|
|
static JSFunctionSpec shell_functions[] = {
|
|
{"version", Version, 0},
|
|
{"options", Options, 0},
|
|
{"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},
|
|
#endif
|
|
{"build", BuildDate, 0},
|
|
{"clear", Clear, 0},
|
|
{"intern", Intern, 1},
|
|
{"clone", Clone, 1},
|
|
{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",
|
|
#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",
|
|
#endif
|
|
#ifdef TEST_EXPORT
|
|
"xport(obj, id) Export identified property from object",
|
|
#endif
|
|
#ifdef TEST_CVTARGS
|
|
"cvtargs(b, c, ...) Test JS_ConvertArguments",
|
|
#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",
|
|
0
|
|
};
|
|
|
|
static void
|
|
ShowHelpHeader(void)
|
|
{
|
|
fprintf(gOutFile, "%-9s %-22s %s\n", "Command", "Usage", "Description");
|
|
fprintf(gOutFile, "%-9s %-22s %s\n", "=======", "=====", "===========");
|
|
}
|
|
|
|
static void
|
|
ShowHelpForCommand(uintN n)
|
|
{
|
|
fprintf(gOutFile, "%-9.9s %s\n", shell_functions[n].name, shell_help_messages[n]);
|
|
}
|
|
|
|
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());
|
|
if (argc == 0) {
|
|
ShowHelpHeader();
|
|
for (i = 0; shell_functions[i].name; i++)
|
|
ShowHelpForCommand(i);
|
|
} 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));
|
|
}
|
|
}
|
|
}
|
|
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},
|
|
{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 */
|
|
|
|
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)));
|
|
}
|
|
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)));
|
|
}
|
|
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)));
|
|
}
|
|
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)));
|
|
}
|
|
if (JSVAL_IS_STRING(id) &&
|
|
!strcmp(JS_GetStringBytes(JSVAL_TO_STRING(id)), "noisy")) {
|
|
return JS_ValueToBoolean(cx, *vp, &its_noisy);
|
|
}
|
|
return JS_TRUE;
|
|
}
|
|
|
|
static JSBool
|
|
its_enumerate(JSContext *cx, JSObject *obj)
|
|
{
|
|
if (its_noisy)
|
|
fprintf(gOutFile, "enumerate its properties\n");
|
|
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)));
|
|
}
|
|
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));
|
|
return JS_TRUE;
|
|
}
|
|
|
|
static void
|
|
its_finalize(JSContext *cx, JSObject *obj)
|
|
{
|
|
if (its_noisy)
|
|
fprintf(gOutFile, "finalizing it\n");
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
static void
|
|
my_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
|
|
{
|
|
int i, j, k, n;
|
|
char *prefix, *tmp;
|
|
const char *ctmp;
|
|
|
|
if (!report) {
|
|
fprintf(gErrFile, "%s\n", message);
|
|
return;
|
|
}
|
|
|
|
/* Conditionally ignore reported warnings. */
|
|
if (JSREPORT_IS_WARNING(report->flags) && !reportWarnings)
|
|
return;
|
|
|
|
prefix = NULL;
|
|
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);
|
|
|
|
if (!report->linebuf) {
|
|
fputc('\n', gErrFile);
|
|
goto out;
|
|
}
|
|
|
|
/* 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);
|
|
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++;
|
|
}
|
|
fputs("^\n", gErrFile);
|
|
out:
|
|
if (!JSREPORT_IS_WARNING(report->flags))
|
|
gExitCode = EXITCODE_RUNTIME_ERROR;
|
|
JS_free(cx, prefix);
|
|
}
|
|
|
|
#if defined(SHELL_HACK) && defined(DEBUG) && defined(XP_UNIX)
|
|
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;
|
|
if (!fun->atom)
|
|
return JS_TRUE;
|
|
name = ATOM_BYTES(fun->atom);
|
|
nargc = 1 + argc;
|
|
nargv = JS_malloc(cx, (nargc + 1) * sizeof(char *));
|
|
if (!nargv)
|
|
return JS_FALSE;
|
|
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);
|
|
}
|
|
nargv[nargc] = 0;
|
|
pid = fork();
|
|
switch (pid) {
|
|
case -1:
|
|
perror("js");
|
|
break;
|
|
case 0:
|
|
(void) execvp(name, (char **)nargv);
|
|
perror("js");
|
|
exit(127);
|
|
default:
|
|
while (waitpid(pid, &status, 0) < 0 && errno == EINTR)
|
|
continue;
|
|
break;
|
|
}
|
|
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
|
|
}
|
|
|
|
static JSBool
|
|
global_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
|
|
JSObject **objp)
|
|
{
|
|
#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;
|
|
}
|
|
#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
|
|
};
|
|
|
|
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
|
|
|
|
#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
|
|
|
|
version = JSVERSION_DEFAULT;
|
|
|
|
argc--;
|
|
argv++;
|
|
|
|
rt = JS_NewRuntime(8L * 1024L * 1024L);
|
|
if (!rt)
|
|
return 1;
|
|
|
|
cx = JS_NewContext(rt, gStackChunkSize);
|
|
if (!cx)
|
|
return 1;
|
|
JS_SetErrorReporter(cx, my_ErrorReporter);
|
|
|
|
glob = JS_NewObject(cx, &global_class, NULL, NULL);
|
|
if (!glob)
|
|
return 1;
|
|
#ifdef LAZY_STANDARD_CLASSES
|
|
JS_SetGlobalObject(cx, glob);
|
|
#else
|
|
if (!JS_InitStandardClasses(cx, glob))
|
|
return 1;
|
|
#endif
|
|
if (!JS_DefineFunctions(cx, glob, shell_functions))
|
|
return 1;
|
|
|
|
/* Set version only after there is a global object. */
|
|
if (version != JSVERSION_DEFAULT)
|
|
JS_SetVersion(cx, version);
|
|
|
|
it = JS_DefineObject(cx, glob, "it", &its_class, NULL, 0);
|
|
if (!it)
|
|
return 1;
|
|
if (!JS_DefineProperties(cx, it, its_props))
|
|
return 1;
|
|
if (!JS_DefineFunctions(cx, it, its_methods))
|
|
return 1;
|
|
|
|
#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);
|
|
|
|
#ifdef JSDEBUGGER
|
|
if (_jsdc)
|
|
JSD_DebuggerOff(_jsdc);
|
|
#endif /* JSDEBUGGER */
|
|
|
|
#ifdef MAC_TEST_HACK
|
|
fclose(gTestResultFile);
|
|
#endif
|
|
|
|
JS_DestroyContext(cx);
|
|
JS_DestroyRuntime(rt);
|
|
JS_ShutDown();
|
|
return result;
|
|
}
|