Index: ossp-pkg/js/ChangeLog RCS File: /v/ossp/cvs/ossp-pkg/js/ChangeLog,v rcsdiff -q -kk '-r1.1' '-r1.2' -u '/v/ossp/cvs/ossp-pkg/js/ChangeLog,v' 2>/dev/null --- ChangeLog 2006/07/22 19:43:48 1.1 +++ ChangeLog 2006/07/23 18:34:15 1.2 @@ -11,6 +11,25 @@ This is a list of all changes to OSSP js. + Changes between 1.6.20060722 and 1.6.20060723 (2006-07-22 to 2006-07-23) + + o Install all src/js*.h headers during "make install" to allow + applications to at least optionally poke around in the internals. + This also allows an application to call js_InitFileClass() from + without us having to really call this function in the + standard API function JS_InitStandardClasses(). + [Ralf S. Engelschall ] + + o Change the default of the --with-file/--without from --without-file + to --with-file (enable the File object by default), but activate + it only in the CLI (where it doesn't hurt and where it is actually + really required to get something useful running there in practice) + but explicitly _NOT_ in JS_InitStandardClasses() (as this is + what would hurt the security in applications which assume that + JS_InitStandardClasses() initialized only really the standard + classes). + [Ralf S. Engelschall ] + Changes between *GENESIS* and 1.6.20060722 (2006-06-XX to 2006-07-22) o Initial OSSP js distribution of Mozilla JavaScript 1.6 as of 2006-07-22. Index: ossp-pkg/js/Makefile.in RCS File: /v/ossp/cvs/ossp-pkg/js/Makefile.in,v rcsdiff -q -kk '-r1.20' '-r1.21' -u '/v/ossp/cvs/ossp-pkg/js/Makefile.in,v' 2>/dev/null --- Makefile.in 2006/07/23 10:51:04 1.20 +++ Makefile.in 2006/07/23 18:34:15 1.21 @@ -60,15 +60,6 @@ PERL = @PERL@ TRUE = true -LIB_HDRS = src/jsautocfg.h \ - src/jsapi.h \ - src/jsdbgapi.h \ - src/jscompat.h \ - src/jslong.h \ - src/jsosdep.h \ - src/jsotypes.h \ - src/jspubtd.h \ - src/jstypes.h LIB_OBJS = src/jsapi.lo \ src/jsarena.lo \ src/jsarray.lo \ @@ -193,7 +184,7 @@ $(SHTOOL) install -c -m 755 js $(DESTDIR)$(bindir)/ $(SHTOOL) install -c -m 644 js.1 $(DESTDIR)$(mandir)/man1/ $(SHTOOL) install -c -m 644 js.3 $(DESTDIR)$(mandir)/man3/ - $(SHTOOL) install -c -m 644 $(LIB_HDRS) $(DESTDIR)$(includedir)/js/ + $(SHTOOL) install -c -m 644 src/js*.h $(DESTDIR)$(includedir)/js/ @$(LIBTOOL) --mode=install $(SHTOOL) install -c -m 644 $(LIB_NAME) $(DESTDIR)$(libdir)/ $(SHTOOL) install -c -m 755 js-config.sh $(DESTDIR)$(bindir)/js-config $(SHTOOL) install -c -m 644 js.pc $(DESTDIR)$(libdir)/pkgconfig/ Index: ossp-pkg/js/configure.ac RCS File: /v/ossp/cvs/ossp-pkg/js/configure.ac,v rcsdiff -q -kk '-r1.12' '-r1.13' -u '/v/ossp/cvs/ossp-pkg/js/configure.ac,v' 2>/dev/null --- configure.ac 2006/07/23 10:52:12 1.12 +++ configure.ac 2006/07/23 18:34:15 1.13 @@ -119,12 +119,12 @@ CLI_LIBS="$CLI_LIBS `$PERL -MExtUtils::Embed -e ldopts`" fi -dnl # configure option --with-file +dnl # configure option --without-file AC_ARG_WITH([file], - AS_HELP_STRING([--with-file], [build with File object (grants access to the filesystem)]), - [ac_cv_with_file=$withval], [ac_cv_with_file=no]) -AC_CACHE_CHECK([whether to build with the File object], [ac_cv_with_file], [ac_cv_with_file=no]) -if test ".$ac_cv_with_file" != ".no"; then + AS_HELP_STRING([--without-file], [build without File object (grants access to the filesystem)]), + [ac_cv_with_file=$withval], [ac_cv_with_file=yes]) +AC_CACHE_CHECK([whether to build with the File object], [ac_cv_with_file], [ac_cv_with_file=yes]) +if test ".$ac_cv_with_file" = ".yes"; then CPPFLAGS="$CPPFLAGS -DJS_HAS_FILE_OBJECT" fi Index: ossp-pkg/js/src/js.c RCS File: /v/ossp/cvs/ossp-pkg/js/src/js.c,v co -q -kk -p'1.2' '/v/ossp/cvs/ossp-pkg/js/src/js.c,v' | diff -u /dev/null - -L'ossp-pkg/js/src/js.c' 2>/dev/null --- ossp-pkg/js/src/js.c +++ - 2024-05-11 10:07:07.257356906 +0200 @@ -0,0 +1,2588 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=80: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla 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/MPL/ + * + * 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 the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * JS shell. + */ +#include "jsstddef.h" +#include +#include +#include +#include +#include "jstypes.h" +#include "jsarena.h" +#include "jsutil.h" +#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 OSSP +#if JS_HAS_FILE_OBJECT +#include "jsfile.h" +#endif +#endif + +#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 +#include +#include +#endif + +#if defined(XP_WIN) || defined(XP_OS2) +#include /* for isatty() */ +#endif + +#define EXITCODE_RUNTIME_ERROR 3 +#define EXITCODE_FILE_NOT_FOUND 4 + +size_t gStackChunkSize = 8192; +static size_t gMaxStackSize = 0; +static jsuword gStackBase; +int gExitCode = 0; +JSBool gQuitting = JS_FALSE; +FILE *gErrFile = NULL; +FILE *gOutFile = NULL; + +#ifdef JSDEBUGGER +static JSDContext *_jsdc; +#ifdef JSDEBUGGER_JAVA_UI +static JSDJContext *_jsdjc; +#endif /* JSDEBUGGER_JAVA_UI */ +#endif /* JSDEBUGGER */ + +static JSBool reportWarnings = JS_TRUE; +static JSBool compileOnly = JS_FALSE; + +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 = readline(prompt); + if (!linep) + 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); + fflush(gOutFile); + if (!fgets(line, sizeof line, file)) + 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; + jsuword stackLimit; + + if (!filename || strcmp(filename, "-") == 0) { + file = stdin; + } else { + file = fopen(filename, "r"); + if (!file) { + JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, + JSSMSG_CANT_OPEN, filename, strerror(errno)); + gExitCode = EXITCODE_FILE_NOT_FOUND; + return; + } + } + + if (gMaxStackSize == 0) { + /* + * Disable checking for stack overflow if limit is zero. + */ + stackLimit = 0; + } else { +#if JS_STACK_GROWTH_DIRECTION > 0 + stackLimit = gStackBase + gMaxStackSize; +#else + stackLimit = gStackBase - gMaxStackSize; +#endif + } + JS_SetThreadStackLimit(cx, stackLimit); + + 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) { + if (!compileOnly) + (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), "typein", + startline); + if (script) { + if (!compileOnly) { + ok = JS_ExecuteScript(cx, obj, script, &result); + if (ok && result != JSVAL_VOID) { + str = JS_ValueToString(cx, result); + if (str) + fprintf(gOutFile, "%s\n", JS_GetStringBytes(str)); + else + ok = JS_FALSE; + } + } + JS_DestroyScript(cx, script); + } + } while (!hitEOF && !gQuitting); + fprintf(gOutFile, "\n"); + return; +} + +static int +usage(void) +{ + fprintf(gErrFile, "%s\n", JS_GetImplementationVersion()); + fprintf(gErrFile, "usage: js [-PswWxC] [-b branchlimit] [-c stackchunksize] [-v version] [-f scriptfile] [-e script] [-S maxstacksize] [scriptfile] [scriptarg...]\n"); + return 2; +} + +static uint32 gBranchCount; +static uint32 gBranchLimit; + +static JSBool +my_BranchCallback(JSContext *cx, JSScript *script) +{ + if (++gBranchCount == gBranchLimit) { + if (script) { + if (script->filename) + fprintf(gErrFile, "%s:", script->filename); + fprintf(gErrFile, "%u: script branch callback (%u callbacks)\n", + script->lineno, gBranchLimit); + } else { + fprintf(gErrFile, "native branch callback (%u callbacks)\n", + gBranchLimit); + } + gBranchCount = 0; + return JS_FALSE; + } + if ((gBranchCount & 0x3fff) == 1) + JS_MaybeGC(cx); + return JS_TRUE; +} + +extern JSClass global_class; + +static int +ProcessArgs(JSContext *cx, JSObject *obj, char **argv, int argc) +{ + int i, j, length; + JSObject *argsObj; + char *filename = NULL; + JSBool isInteractive = JS_TRUE; + + /* + * Scan past all optional arguments so we can create the arguments object + * before processing any -f options, which must interleave properly with + * -v and -w options. This requires two passes, and without getopt, we'll + * have to keep the option logic here and in the second for loop in sync. + */ + for (i = 0; i < argc; i++) { + if (argv[i][0] != '-' || argv[i][1] == '\0') { + ++i; + break; + } + switch (argv[i][1]) { + case 'b': + case 'c': + case 'f': + case 'e': + case 'v': + case 'S': + ++i; + break; + default:; + } + } + + /* + * Create arguments early and define it to root it, so it's safe from any + * GC calls nested below, and so it is available to -f arguments. + */ + argsObj = JS_NewArrayObject(cx, 0, NULL); + if (!argsObj) + return 1; + if (!JS_DefineProperty(cx, obj, "arguments", OBJECT_TO_JSVAL(argsObj), + NULL, NULL, 0)) { + return 1; + } + + length = argc - i; + for (j = 0; j < length; j++) { + JSString *str = JS_NewStringCopyZ(cx, argv[i++]); + if (!str) + return 1; + if (!JS_DefineElement(cx, argsObj, j, STRING_TO_JSVAL(str), + NULL, NULL, JSPROP_ENUMERATE)) { + return 1; + } + } + + for (i = 0; i < argc; i++) { + if (argv[i][0] != '-' || argv[i][1] == '\0') { + filename = argv[i++]; + isInteractive = JS_FALSE; + break; + } + + switch (argv[i][1]) { + case 'v': + if (++i == argc) { + return usage(); + } + JS_SetVersion(cx, (JSVersion) atoi(argv[i])); + break; + + case 'w': + reportWarnings = JS_TRUE; + break; + + case 'W': + reportWarnings = JS_FALSE; + break; + + case 's': + JS_ToggleOptions(cx, JSOPTION_STRICT); + break; + + case 'x': + JS_ToggleOptions(cx, JSOPTION_XML); + break; + + case 'P': + if (JS_GET_CLASS(cx, JS_GetPrototype(cx, obj)) != &global_class) { + JSObject *gobj; + + if (!JS_SealObject(cx, obj, JS_TRUE)) + return JS_FALSE; + gobj = JS_NewObject(cx, &global_class, NULL, NULL); + if (!gobj) + return JS_FALSE; + if (!JS_SetPrototype(cx, gobj, obj)) + return JS_FALSE; + JS_SetParent(cx, gobj, NULL); + JS_SetGlobalObject(cx, gobj); + obj = gobj; + } + break; + + case 'b': + gBranchLimit = atoi(argv[++i]); + JS_SetBranchCallback(cx, my_BranchCallback); + JS_ToggleOptions(cx, JSOPTION_NATIVE_BRANCH_CALLBACK); + break; + + case 'c': + /* set stack chunk size */ + gStackChunkSize = atoi(argv[++i]); + break; + + case 'f': + if (++i == argc) { + return usage(); + } + Process(cx, obj, argv[i]); + /* + * XXX: js -f foo.js should interpret foo.js and then + * drop into interactive mode, but that breaks the test + * harness. Just execute foo.js for now. + */ + isInteractive = JS_FALSE; + break; + + case 'e': + { + jsval rval; + + if (++i == argc) { + return usage(); + } + + /* Pass a filename of -e to imitate PERL */ + JS_EvaluateScript(cx, obj, argv[i], strlen(argv[i]), + "-e", 1, &rval); + + isInteractive = JS_FALSE; + break; + + } + case 'C': + compileOnly = JS_TRUE; + isInteractive = JS_FALSE; + break; + + case 'S': + if (++i == argc) { + return usage(); + } + /* Set maximum stack size. */ + gMaxStackSize = atoi(argv[i]); + break; + + default: + return usage(); + } + } + + 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}, + {"atline", JSOPTION_ATLINE}, + {"xml", JSOPTION_XML}, + {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; + uint32 oldopts; + + 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); + oldopts = JS_GetOptions(cx); + JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO); + script = JS_CompileFile(cx, obj, filename); + if (!script) { + ok = JS_FALSE; + } else { + ok = !compileOnly + ? JS_ExecuteScript(cx, obj, script, &result) + : JS_TRUE; + JS_DestroyScript(cx, script); + } + JS_SetOptions(cx, oldopts); + JS_SetErrorReporter(cx, older); + if (!ok) + return JS_FALSE; + } + + return JS_TRUE; +} + +/* + * function readline() + * Provides a hook for scripts to read a line from stdin. + */ +static JSBool +ReadLine(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ +#define BUFSIZE 256 + FILE *from; + char *buf, *tmp; + size_t bufsize, buflength, gotlength; + JSString *str; + + from = stdin; + buflength = 0; + bufsize = BUFSIZE; + buf = JS_malloc(cx, bufsize); + if (!buf) + return JS_FALSE; + + while ((gotlength = + js_fgets(buf + buflength, bufsize - buflength, from)) > 0) { + buflength += gotlength; + + /* Are we done? */ + if (buf[buflength - 1] == '\n') { + buf[buflength - 1] = '\0'; + break; + } + + /* Else, grow our buffer for another pass. */ + tmp = JS_realloc(cx, buf, bufsize * 2); + if (!tmp) { + JS_free(cx, buf); + return JS_FALSE; + } + + bufsize *= 2; + buf = tmp; + } + + /* Treat the empty string specially. */ + if (buflength == 0) { + *rval = JS_GetEmptyStringValue(cx); + JS_free(cx, buf); + return JS_TRUE; + } + + /* Shrink the buffer to the real size. */ + tmp = JS_realloc(cx, buf, buflength); + if (!tmp) { + JS_free(cx, buf); + return JS_FALSE; + } + + buf = tmp; + + /* + * Turn buf into a JSString. Note that buflength includes the trailing null + * character. + */ + str = JS_NewString(cx, buf, buflength - 1); + if (!str) { + JS_free(cx, buf); + return JS_FALSE; + } + + *rval = STRING_TO_JSVAL(str); + 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) +{ +#ifdef LIVECONNECT + JSJ_SimpleShutdown(); +#endif + + JS_ConvertArguments(cx, argc, argv,"/ i", &gExitCode); + + gQuitting = JS_TRUE; + 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_PRIMITIVE(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(fun); + } + return script; +} + +static JSBool +GetTrapArgs(JSContext *cx, uintN argc, jsval *argv, JSScript **scriptp, + int32 *ip) +{ + jsval v; + uintN intarg; + JSScript *script; + + *scriptp = cx->fp->down->script; + *ip = 0; + if (argc != 0) { + v = argv[0]; + intarg = 0; + if (!JSVAL_IS_PRIMITIVE(v) && + (JS_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_FunctionClass || + JS_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_ScriptClass)) { + script = ValueToScript(cx, v); + 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 = JS_GetScriptedCaller(cx, NULL); + 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; + + fprintf(gOutFile, "\nSource notes:\n"); + offset = 0; + notes = SCRIPT_NOTES(script); + 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 = js_AtomToPrintableString(cx, 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'); + } + } + + if (!js_Disassemble(cx, script, lines, stdout)) + return JS_FALSE; + 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; +} + +typedef struct DumpAtomArgs { + JSContext *cx; + FILE *fp; +} DumpAtomArgs; + +static int +DumpAtom(JSHashEntry *he, int i, void *arg) +{ + DumpAtomArgs *args = (DumpAtomArgs *)arg; + FILE *fp = args->fp; + 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", js_AtomToPrintableString(args->cx, 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 (JSID_IS_INT(sprop->id)) { + fprintf(fp, " [%ld]", (long)JSVAL_TO_INT(sprop->id)); + } else if (JSID_IS_ATOM(sprop->id)) { + JSAtom *atom = JSID_TO_ATOM(sprop->id); + fprintf(fp, " \"%s\"", js_AtomToPrintableString(cx, atom)); + } else { + jsval v = OBJECT_TO_JSVAL(JSID_TO_OBJECT(sprop->id)); + fprintf(fp, " \"%s\"", js_ValueToPrintableString(cx, v)); + } + +#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) { + DumpAtomArgs args; + + fprintf(gOutFile, "\natom table contents:\n"); + args.cx = cx; + args.fp = stdout; + JS_HashTableEnumerateEntries(cx->runtime->atomState.table, + DumpAtom, + &args); +#ifdef HASHMETER + JS_HashTableDumpMeter(cx->runtime->atomState.table, + DumpAtom, + stdout); +#endif + } 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, ATOM_TO_JSID(atom), &obj, &obj2, &prop)) + return JS_FALSE; + if (prop) { + OBJ_DROP_PROPERTY(cx, obj2, prop); + if (!OBJ_GET_PROPERTY(cx, obj, ATOM_TO_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, ATOM_TO_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, ATOM_TO_JSID(atom), prop, &attrs); + if (ok) { + attrs |= JSPROP_EXPORTED; + ok = OBJ_SET_ATTRIBUTES(cx, obj, ATOM_TO_JSID(atom), prop, &attrs); + } + OBJ_DROP_PROPERTY(cx, obj2, prop); + } + return ok; +} +#endif + +#ifdef TEST_CVTARGS +#include + +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 + +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 JSBool +Seal(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSObject *target; + JSBool deep = JS_FALSE; + + if (!JS_ConvertArguments(cx, argc, argv, "o/b", &target, &deep)) + return JS_FALSE; + if (!target) + return JS_TRUE; + return JS_SealObject(cx, target, deep); +} + +static JSBool +GetPDA(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSObject *vobj, *aobj, *pdobj; + JSBool ok; + JSPropertyDescArray pda; + JSPropertyDesc *pd; + uint32 i; + jsval v; + + if (!JS_ValueToObject(cx, argv[0], &vobj)) + return JS_FALSE; + if (!vobj) + return JS_TRUE; + + aobj = JS_NewArrayObject(cx, 0, NULL); + if (!aobj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(aobj); + + ok = JS_GetPropertyDescArray(cx, vobj, &pda); + if (!ok) + return JS_FALSE; + pd = pda.array; + for (i = 0; i < pda.length; i++) { + pdobj = JS_NewObject(cx, NULL, NULL, NULL); + if (!pdobj) { + ok = JS_FALSE; + break; + } + + ok = JS_SetProperty(cx, pdobj, "id", &pd->id) && + JS_SetProperty(cx, pdobj, "value", &pd->value) && + (v = INT_TO_JSVAL(pd->flags), + JS_SetProperty(cx, pdobj, "flags", &v)) && + (v = INT_TO_JSVAL(pd->slot), + JS_SetProperty(cx, pdobj, "slot", &v)) && + JS_SetProperty(cx, pdobj, "alias", &pd->alias); + if (!ok) + break; + + v = OBJECT_TO_JSVAL(pdobj); + ok = JS_SetElement(cx, aobj, i, &v); + if (!ok) + break; + } + JS_PutPropertyDescArray(cx, &pda); + return ok; +} + +static JSBool +GetSLX(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSScript *script; + + script = ValueToScript(cx, argv[0]); + if (!script) + return JS_FALSE; + *rval = INT_TO_JSVAL(js_GetScriptLineExtent(script)); + return JS_TRUE; +} + +static JSBool +ToInt32(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + int32 i; + + if (!JS_ValueToInt32(cx, argv[0], &i)) + return JS_FALSE; + return JS_NewNumberValue(cx, i, rval); +} + +static JSFunctionSpec shell_functions[] = { + {"version", Version, 0}, + {"options", Options, 0}, + {"load", Load, 1}, + {"readline", ReadLine, 0}, + {"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}, + {"seal", Seal, 1, 0, 1}, + {"getpda", GetPDA, 1}, + {"getslx", GetSLX, 1}, + {"toint32", ToInt32, 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", + "readline() Read a single line from stdin", + "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", + "seal(obj[, deep]) Seal object, or object graph if deep", + "getpda(obj) Get the property descriptors for obj", + "getslx(obj) Get script line extent", + "toint32(n) Testing hook for JS_ValueToInt32", + 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 JSBool +its_bindMethod(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + char *name; + JSObject *method; + + if (!JS_ConvertArguments(cx, argc, argv, "so", &name, &method)) + return JS_FALSE; + + *rval = OBJECT_TO_JSVAL(method); + + if (JS_TypeOfValue(cx, *rval) != JSTYPE_FUNCTION) { + JSString *valstr = JS_ValueToString(cx, *rval); + if (valstr) { + JS_ReportError(cx, "can't bind method %s to non-callable object %s", + name, JS_GetStringBytes(valstr)); + } + return JS_FALSE; + } + + if (!JS_DefineProperty(cx, obj, name, *rval, NULL, NULL, JSPROP_ENUMERATE)) + return JS_FALSE; + + return JS_SetParent(cx, method, obj); +} + +static JSFunctionSpec its_methods[] = { + {"item", its_item, 0}, + {"bindMethod", its_bindMethod, 2}, + {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, uintN flags, + JSObject **objp) +{ + if (its_noisy) { + fprintf(gOutFile, "resolving its property %s, flags {%s,%s,%s}\n", + JS_GetStringBytes(JS_ValueToString(cx, id)), + (flags & JSRESOLVE_QUALIFIED) ? "qualified" : "", + (flags & JSRESOLVE_ASSIGNING) ? "assigning" : "", + (flags & JSRESOLVE_DETECTING) ? "detecting" : ""); + } + 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", JSCLASS_NEW_RESOLVE, + its_addProperty, its_delProperty, its_getProperty, its_setProperty, + its_enumerate, (JSResolveOp)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 = JS_GetStringBytes(ATOM_TO_STRING(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 +} + +JSClass global_class = { + "global", JSCLASS_NEW_RESOLVE, + JS_PropertyStub, JS_PropertyStub, + JS_PropertyStub, JS_PropertyStub, + global_enumerate, (JSResolveOp) global_resolve, + JS_ConvertStub, JS_FinalizeStub +}; + +static JSBool +env_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ +/* XXX porting may be easy, but these don't seem to supply setenv by default */ +#if !defined XP_BEOS && !defined XP_OS2 && !defined SOLARIS + JSString *idstr, *valstr; + const char *name, *value; + int rv; + + idstr = JS_ValueToString(cx, id); + valstr = JS_ValueToString(cx, *vp); + if (!idstr || !valstr) + return JS_FALSE; + name = JS_GetStringBytes(idstr); + value = JS_GetStringBytes(valstr); +#if defined XP_WIN || defined HPUX || defined OSF1 || defined IRIX + { + char *waste = JS_smprintf("%s=%s", name, value); + if (!waste) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + rv = putenv(waste); +#ifdef XP_WIN + /* + * HPUX9 at least still has the bad old non-copying putenv. + * + * Per mail from , OSF1 also has a putenv + * that will crash if you pass it an auto char array (so it must place + * its argument directly in the char *environ[] array). + */ + free(waste); +#endif + } +#else + rv = setenv(name, value, 1); +#endif + if (rv < 0) { + JS_ReportError(cx, "can't set envariable %s to %s", name, value); + return JS_FALSE; + } + *vp = STRING_TO_JSVAL(valstr); +#endif /* !defined XP_BEOS && !defined XP_OS2 && !defined SOLARIS */ + return JS_TRUE; +} + +static JSBool +env_enumerate(JSContext *cx, JSObject *obj) +{ + static JSBool reflected; + char **evp, *name, *value; + JSString *valstr; + JSBool ok; + + if (reflected) + return JS_TRUE; + + for (evp = (char **)JS_GetPrivate(cx, obj); (name = *evp) != NULL; evp++) { + value = strchr(name, '='); + if (!value) + continue; + *value++ = '\0'; + valstr = JS_NewStringCopyZ(cx, value); + if (!valstr) { + ok = JS_FALSE; + } else { + ok = JS_DefineProperty(cx, obj, name, STRING_TO_JSVAL(valstr), + NULL, NULL, JSPROP_ENUMERATE); + } + value[-1] = '='; + if (!ok) + return JS_FALSE; + } + + reflected = JS_TRUE; + return JS_TRUE; +} + +static JSBool +env_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, + JSObject **objp) +{ + JSString *idstr, *valstr; + const char *name, *value; + + if (flags & JSRESOLVE_ASSIGNING) + return JS_TRUE; + + idstr = JS_ValueToString(cx, id); + if (!idstr) + return JS_FALSE; + name = JS_GetStringBytes(idstr); + value = getenv(name); + if (value) { + valstr = JS_NewStringCopyZ(cx, value); + if (!valstr) + return JS_FALSE; + if (!JS_DefineProperty(cx, obj, name, STRING_TO_JSVAL(valstr), + NULL, NULL, JSPROP_ENUMERATE)) { + return JS_FALSE; + } + *objp = obj; + } + return JS_TRUE; +} + +static JSClass env_class = { + "environment", JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE, + JS_PropertyStub, JS_PropertyStub, + JS_PropertyStub, env_setProperty, + env_enumerate, (JSResolveOp) env_resolve, + JS_ConvertStub, JS_FinalizeStub +}; + +#ifdef NARCISSUS + +static JSBool +defineProperty(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSString *str; + jsval value; + JSBool dontDelete, readOnly, dontEnum; + const jschar *chars; + size_t length; + uintN attrs; + + dontDelete = readOnly = dontEnum = JS_FALSE; + if (!JS_ConvertArguments(cx, argc, argv, "Sv/bbb", + &str, &value, &dontDelete, &readOnly, &dontEnum)) { + return JS_FALSE; + } + chars = JS_GetStringChars(str); + length = JS_GetStringLength(str); + attrs = dontEnum ? 0 : JSPROP_ENUMERATE; + if (dontDelete) + attrs |= JSPROP_PERMANENT; + if (readOnly) + attrs |= JSPROP_READONLY; + return JS_DefineUCProperty(cx, obj, chars, length, value, NULL, NULL, + attrs); +} + +static JSBool +Evaluate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + /* function evaluate(source, filename, lineno) { ... } */ + JSString *source; + const char *filename = ""; + jsuint lineno = 0; + uint32 oldopts; + JSBool ok; + + if (argc == 0) { + *rval = JSVAL_VOID; + return JS_TRUE; + } + + if (!JS_ConvertArguments(cx, argc, argv, "S/su", + &source, &filename, &lineno)) { + return JS_FALSE; + } + + oldopts = JS_GetOptions(cx); + JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO); + ok = JS_EvaluateUCScript(cx, obj, JS_GetStringChars(source), + JS_GetStringLength(source), filename, + lineno, rval); + JS_SetOptions(cx, oldopts); + + return ok; +} + +#include +#include + +/* + * Returns a JS_malloc'd string (that the caller needs to JS_free) + * containing the directory (non-leaf) part of |from| prepended to |leaf|. + * If |from| is empty or a leaf, MakeAbsolutePathname returns a copy of leaf. + * Returns NULL to indicate an error. + */ +static char * +MakeAbsolutePathname(JSContext *cx, const char *from, const char *leaf) +{ + size_t dirlen; + char *dir; + const char *slash = NULL, *cp; + + cp = from; + while (*cp) { + if (*cp == '/' +#ifdef XP_WIN + || *cp == '\\' +#endif + ) { + slash = cp; + } + + ++cp; + } + + if (!slash) { + /* We were given a leaf or |from| was empty. */ + return JS_strdup(cx, leaf); + } + + /* Else, we were given a real pathname, return that + the leaf. */ + dirlen = slash - from + 1; + dir = JS_malloc(cx, dirlen + strlen(leaf) + 1); + if (!dir) + return NULL; + + strncpy(dir, from, dirlen); + strcpy(dir + dirlen, leaf); /* Note: we can't use strcat here. */ + + return dir; +} + +static JSBool +snarf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSString *str; + const char *filename; + char *pathname; + JSStackFrame *fp; + int fd, cc; + JSBool ok; + size_t len; + char *buf; + struct stat sb; + + str = JS_ValueToString(cx, argv[0]); + if (!str) + return JS_FALSE; + filename = JS_GetStringBytes(str); + + /* Get the currently executing script's name. */ + fp = JS_GetScriptedCaller(cx, NULL); + JS_ASSERT(fp && fp->script->filename); + pathname = MakeAbsolutePathname(cx, fp->script->filename, filename); + if (!pathname) + return JS_FALSE; + + fd = open(pathname, O_RDONLY); + ok = JS_TRUE; + len = 0; + buf = NULL; + if (fd < 0) { + JS_ReportError(cx, "can't open %s: %s", pathname, strerror(errno)); + ok = JS_FALSE; + } else if (fstat(fd, &sb) < 0) { + JS_ReportError(cx, "can't stat %s", pathname); + ok = JS_FALSE; + } else { + len = sb.st_size; + buf = JS_malloc(cx, len + 1); + if (!buf) { + ok = JS_FALSE; + } else if ((cc = read(fd, buf, len)) != len) { + JS_free(cx, buf); + JS_ReportError(cx, "can't read %s: %s", pathname, + (cc < 0) ? strerror(errno) : "short read"); + ok = JS_FALSE; + } + } + close(fd); + JS_free(cx, pathname); + if (!ok) + return ok; + buf[len] = '\0'; + str = JS_NewString(cx, buf, len); + if (!str) { + JS_free(cx, buf); + return JS_FALSE; + } + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +#endif /* NARCISSUS */ + +int +main(int argc, char **argv, char **envp) +{ + int stackDummy; + JSRuntime *rt; + JSContext *cx; + JSObject *glob, *it, *envobj; + int result; +#ifdef LIVECONNECT + JavaVM *java_vm = NULL; +#endif +#ifdef JSDEBUGGER_JAVA_UI + JNIEnv *java_env; +#endif + + gStackBase = (jsuword)&stackDummy; + +#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; + + argc--; + argv++; + + rt = JS_NewRuntime(64L * 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; +#ifdef OSSP +#if JS_HAS_FILE_OBJECT + if (!js_InitFileClass(cx, glob)) + return 1; +#endif +#endif +#endif + if (!JS_DefineFunctions(cx, glob, shell_functions)) + return 1; + + 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 + + envobj = JS_DefineObject(cx, glob, "environment", &env_class, NULL, 0); + if (!envobj || !JS_SetPrivate(cx, envobj, envp)) + return 1; + +#ifdef NARCISSUS + { + jsval v; + static const char Object_prototype[] = "Object.prototype"; + + if (!JS_DefineFunction(cx, glob, "snarf", snarf, 1, 0)) + return 1; + if (!JS_DefineFunction(cx, glob, "evaluate", Evaluate, 3, 0)) + return 1; + + if (!JS_EvaluateScript(cx, glob, + Object_prototype, sizeof Object_prototype - 1, + NULL, 0, &v)) { + return 1; + } + if (!JS_DefineFunction(cx, JSVAL_TO_OBJECT(v), "__defineProperty__", + defineProperty, 5, 0)) { + return 1; + } + } +#endif + + result = ProcessArgs(cx, glob, argv, argc); + +#ifdef JSDEBUGGER + if (_jsdc) + JSD_DebuggerOff(_jsdc); +#endif /* JSDEBUGGER */ + + JS_DestroyContext(cx); + JS_DestroyRuntime(rt); + JS_ShutDown(); + return result; +} Index: ossp-pkg/js/src/jsapi.c RCS File: /v/ossp/cvs/ossp-pkg/js/src/jsapi.c,v rcsdiff -q -kk '-r1.3' '-r1.4' -u '/v/ossp/cvs/ossp-pkg/js/src/jsapi.c,v' 2>/dev/null --- jsapi.c 2006/07/23 12:01:17 1.3 +++ jsapi.c 2006/07/23 18:34:15 1.4 @@ -1203,9 +1203,11 @@ #if JS_HAS_XML_SUPPORT js_InitXMLClasses(cx, obj) && #endif +#ifndef OSSP #if JS_HAS_FILE_OBJECT js_InitFileClass(cx, obj) && #endif +#endif js_InitDateClass(cx, obj); }