Back to index

lightning-sunbird  0.9+nobinonly
js.c
Go to the documentation of this file.
00001 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
00002  * vim: set ts=8 sw=4 et tw=78:
00003  *
00004  * ***** BEGIN LICENSE BLOCK *****
00005  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00006  *
00007  * The contents of this file are subject to the Mozilla Public License Version
00008  * 1.1 (the "License"); you may not use this file except in compliance with
00009  * the License. You may obtain a copy of the License at
00010  * http://www.mozilla.org/MPL/
00011  *
00012  * Software distributed under the License is distributed on an "AS IS" basis,
00013  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00014  * for the specific language governing rights and limitations under the
00015  * License.
00016  *
00017  * The Original Code is Mozilla Communicator client code, released
00018  * March 31, 1998.
00019  *
00020  * The Initial Developer of the Original Code is
00021  * Netscape Communications Corporation.
00022  * Portions created by the Initial Developer are Copyright (C) 1998
00023  * the Initial Developer. All Rights Reserved.
00024  *
00025  * Contributor(s):
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either of the GNU General Public License Version 2 or later (the "GPL"),
00029  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 /*
00042  * JS shell.
00043  */
00044 #include "jsstddef.h"
00045 #include <errno.h>
00046 #include <stdio.h>
00047 #include <stdlib.h>
00048 #include <string.h>
00049 #include <locale.h>
00050 #include "jstypes.h"
00051 #include "jsarena.h"
00052 #include "jsutil.h"
00053 #include "jsprf.h"
00054 #include "jsapi.h"
00055 #include "jsatom.h"
00056 #include "jscntxt.h"
00057 #include "jsdbgapi.h"
00058 #include "jsemit.h"
00059 #include "jsfun.h"
00060 #include "jsgc.h"
00061 #include "jslock.h"
00062 #include "jsobj.h"
00063 #include "jsparse.h"
00064 #include "jsscope.h"
00065 #include "jsscript.h"
00066 
00067 #ifdef PERLCONNECT
00068 #include "perlconnect/jsperl.h"
00069 #endif
00070 
00071 #ifdef LIVECONNECT
00072 #include "jsjava.h"
00073 #endif
00074 
00075 #ifdef JSDEBUGGER
00076 #include "jsdebug.h"
00077 #ifdef JSDEBUGGER_JAVA_UI
00078 #include "jsdjava.h"
00079 #endif /* JSDEBUGGER_JAVA_UI */
00080 #ifdef JSDEBUGGER_C_UI
00081 #include "jsdb.h"
00082 #endif /* JSDEBUGGER_C_UI */
00083 #endif /* JSDEBUGGER */
00084 
00085 #ifdef XP_UNIX
00086 #include <unistd.h>
00087 #include <sys/types.h>
00088 #include <sys/wait.h>
00089 #endif
00090 
00091 #if defined(XP_WIN) || defined(XP_OS2)
00092 #include <io.h>     /* for isatty() */
00093 #endif
00094 
00095 typedef enum JSShellExitCode {
00096     EXITCODE_RUNTIME_ERROR      = 3,
00097     EXITCODE_FILE_NOT_FOUND     = 4,
00098     EXITCODE_OUT_OF_MEMORY      = 5
00099 } JSShellExitCode;
00100 
00101 size_t gStackChunkSize = 8192;
00102 
00103 /* Assume that we can not use more than 5e5 bytes of C stack by default. */
00104 static size_t gMaxStackSize = 500000;
00105 
00106 static jsuword gStackBase;
00107 int gExitCode = 0;
00108 JSBool gQuitting = JS_FALSE;
00109 FILE *gErrFile = NULL;
00110 FILE *gOutFile = NULL;
00111 
00112 #ifdef JSDEBUGGER
00113 static JSDContext *_jsdc;
00114 #ifdef JSDEBUGGER_JAVA_UI
00115 static JSDJContext *_jsdjc;
00116 #endif /* JSDEBUGGER_JAVA_UI */
00117 #endif /* JSDEBUGGER */
00118 
00119 static JSBool reportWarnings = JS_TRUE;
00120 static JSBool compileOnly = JS_FALSE;
00121 
00122 typedef enum JSShellErrNum {
00123 #define MSG_DEF(name, number, count, exception, format) \
00124     name = number,
00125 #include "jsshell.msg"
00126 #undef MSG_DEF
00127     JSShellErr_Limit
00128 #undef MSGDEF
00129 } JSShellErrNum;
00130 
00131 static const JSErrorFormatString *
00132 my_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber);
00133 static JSObject *
00134 split_setup(JSContext *cx);
00135 
00136 #ifdef EDITLINE
00137 extern char     *readline(const char *prompt);
00138 extern void     add_history(char *line);
00139 #endif
00140 
00141 static JSBool
00142 GetLine(JSContext *cx, char *bufp, FILE *file, const char *prompt) {
00143 #ifdef EDITLINE
00144     /*
00145      * Use readline only if file is stdin, because there's no way to specify
00146      * another handle.  Are other filehandles interactive?
00147      */
00148     if (file == stdin) {
00149         char *linep = readline(prompt);
00150         if (!linep)
00151             return JS_FALSE;
00152         if (linep[0] != '\0')
00153             add_history(linep);
00154         strcpy(bufp, linep);
00155         JS_free(cx, linep);
00156         bufp += strlen(bufp);
00157         *bufp++ = '\n';
00158         *bufp = '\0';
00159     } else
00160 #endif
00161     {
00162         char line[256];
00163         fprintf(gOutFile, prompt);
00164         fflush(gOutFile);
00165         if (!fgets(line, sizeof line, file))
00166             return JS_FALSE;
00167         strcpy(bufp, line);
00168     }
00169     return JS_TRUE;
00170 }
00171 
00172 static void
00173 Process(JSContext *cx, JSObject *obj, char *filename, JSBool forceTTY)
00174 {
00175     JSBool ok, hitEOF;
00176     JSScript *script;
00177     jsval result;
00178     JSString *str;
00179     char buffer[4096];
00180     char *bufp;
00181     int lineno;
00182     int startline;
00183     FILE *file;
00184     jsuword stackLimit;
00185 
00186     if (forceTTY || !filename || strcmp(filename, "-") == 0) {
00187         file = stdin;
00188     } else {
00189         file = fopen(filename, "r");
00190         if (!file) {
00191             JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
00192                                  JSSMSG_CANT_OPEN, filename, strerror(errno));
00193             gExitCode = EXITCODE_FILE_NOT_FOUND;
00194             return;
00195         }
00196     }
00197 
00198     if (gMaxStackSize == 0) {
00199         /*
00200          * Disable checking for stack overflow if limit is zero.
00201          */
00202         stackLimit = 0;
00203     } else {
00204 #if JS_STACK_GROWTH_DIRECTION > 0
00205         stackLimit = gStackBase + gMaxStackSize;
00206 #else
00207         stackLimit = gStackBase - gMaxStackSize;
00208 #endif
00209     }
00210     JS_SetThreadStackLimit(cx, stackLimit);
00211 
00212     if (!forceTTY && !isatty(fileno(file))) {
00213         /*
00214          * It's not interactive - just execute it.
00215          *
00216          * Support the UNIX #! shell hack; gobble the first line if it starts
00217          * with '#'.  TODO - this isn't quite compatible with sharp variables,
00218          * as a legal js program (using sharp variables) might start with '#'.
00219          * But that would require multi-character lookahead.
00220          */
00221         int ch = fgetc(file);
00222         if (ch == '#') {
00223             while((ch = fgetc(file)) != EOF) {
00224                 if (ch == '\n' || ch == '\r')
00225                     break;
00226             }
00227         }
00228         ungetc(ch, file);
00229         script = JS_CompileFileHandle(cx, obj, filename, file);
00230         if (script) {
00231             if (!compileOnly)
00232                 (void)JS_ExecuteScript(cx, obj, script, &result);
00233             JS_DestroyScript(cx, script);
00234         }
00235 
00236         return;
00237     }
00238 
00239     /* It's an interactive filehandle; drop into read-eval-print loop. */
00240     lineno = 1;
00241     hitEOF = JS_FALSE;
00242     do {
00243         bufp = buffer;
00244         *bufp = '\0';
00245 
00246         /*
00247          * Accumulate lines until we get a 'compilable unit' - one that either
00248          * generates an error (before running out of source) or that compiles
00249          * cleanly.  This should be whenever we get a complete statement that
00250          * coincides with the end of a line.
00251          */
00252         startline = lineno;
00253         do {
00254             if (!GetLine(cx, bufp, file, startline == lineno ? "js> " : "")) {
00255                 hitEOF = JS_TRUE;
00256                 break;
00257             }
00258             bufp += strlen(bufp);
00259             lineno++;
00260         } while (!JS_BufferIsCompilableUnit(cx, obj, buffer, strlen(buffer)));
00261 
00262         /* Clear any pending exception from previous failed compiles.  */
00263         JS_ClearPendingException(cx);
00264         script = JS_CompileScript(cx, obj, buffer, strlen(buffer), "typein",
00265                                   startline);
00266         if (script) {
00267             if (!compileOnly) {
00268                 ok = JS_ExecuteScript(cx, obj, script, &result);
00269                 if (ok && result != JSVAL_VOID) {
00270                     str = JS_ValueToString(cx, result);
00271                     if (str)
00272                         fprintf(gOutFile, "%s\n", JS_GetStringBytes(str));
00273                     else
00274                         ok = JS_FALSE;
00275                 }
00276             }
00277             JS_DestroyScript(cx, script);
00278         }
00279     } while (!hitEOF && !gQuitting);
00280     fprintf(gOutFile, "\n");
00281     return;
00282 }
00283 
00284 static int
00285 usage(void)
00286 {
00287     fprintf(gErrFile, "%s\n", JS_GetImplementationVersion());
00288     fprintf(gErrFile, "usage: js [-PswWxCi] [-b branchlimit] [-c stackchunksize] [-v version] [-f scriptfile] [-e script] [-S maxstacksize] [scriptfile] [scriptarg...]\n");
00289     return 2;
00290 }
00291 
00292 static uint32 gBranchCount;
00293 static uint32 gBranchLimit;
00294 
00295 static JSBool
00296 my_BranchCallback(JSContext *cx, JSScript *script)
00297 {
00298     if (++gBranchCount == gBranchLimit) {
00299         if (script) {
00300             if (script->filename)
00301                 fprintf(gErrFile, "%s:", script->filename);
00302             fprintf(gErrFile, "%u: script branch callback (%u callbacks)\n",
00303                     script->lineno, gBranchLimit);
00304         } else {
00305             fprintf(gErrFile, "native branch callback (%u callbacks)\n",
00306                     gBranchLimit);
00307         }
00308         gBranchCount = 0;
00309         return JS_FALSE;
00310     }
00311     if ((gBranchCount & 0x3fff) == 1)
00312         JS_MaybeGC(cx);
00313     return JS_TRUE;
00314 }
00315 
00316 extern JSClass global_class;
00317 
00318 static int
00319 ProcessArgs(JSContext *cx, JSObject *obj, char **argv, int argc)
00320 {
00321     int i, j, length;
00322     JSObject *argsObj;
00323     char *filename = NULL;
00324     JSBool isInteractive = JS_TRUE;
00325     JSBool forceTTY = JS_FALSE;
00326 
00327     /*
00328      * Scan past all optional arguments so we can create the arguments object
00329      * before processing any -f options, which must interleave properly with
00330      * -v and -w options.  This requires two passes, and without getopt, we'll
00331      * have to keep the option logic here and in the second for loop in sync.
00332      */
00333     for (i = 0; i < argc; i++) {
00334         if (argv[i][0] != '-' || argv[i][1] == '\0') {
00335             ++i;
00336             break;
00337         }
00338         switch (argv[i][1]) {
00339           case 'b':
00340           case 'c':
00341           case 'f':
00342           case 'e':
00343           case 'v':
00344           case 'S':
00345             ++i;
00346             break;
00347           default:;
00348         }
00349     }
00350 
00351     /*
00352      * Create arguments early and define it to root it, so it's safe from any
00353      * GC calls nested below, and so it is available to -f <file> arguments.
00354      */
00355     argsObj = JS_NewArrayObject(cx, 0, NULL);
00356     if (!argsObj)
00357         return 1;
00358     if (!JS_DefineProperty(cx, obj, "arguments", OBJECT_TO_JSVAL(argsObj),
00359                            NULL, NULL, 0)) {
00360         return 1;
00361     }
00362 
00363     length = argc - i;
00364     for (j = 0; j < length; j++) {
00365         JSString *str = JS_NewStringCopyZ(cx, argv[i++]);
00366         if (!str)
00367             return 1;
00368         if (!JS_DefineElement(cx, argsObj, j, STRING_TO_JSVAL(str),
00369                               NULL, NULL, JSPROP_ENUMERATE)) {
00370             return 1;
00371         }
00372     }
00373 
00374     for (i = 0; i < argc; i++) {
00375         if (argv[i][0] != '-' || argv[i][1] == '\0') {
00376             filename = argv[i++];
00377             isInteractive = JS_FALSE;
00378             break;
00379         }
00380 
00381         switch (argv[i][1]) {
00382         case 'v':
00383             if (++i == argc)
00384                 return usage();
00385 
00386             JS_SetVersion(cx, (JSVersion) atoi(argv[i]));
00387             break;
00388 
00389         case 'w':
00390             reportWarnings = JS_TRUE;
00391             break;
00392 
00393         case 'W':
00394             reportWarnings = JS_FALSE;
00395             break;
00396 
00397         case 's':
00398             JS_ToggleOptions(cx, JSOPTION_STRICT);
00399             break;
00400 
00401         case 'x':
00402             JS_ToggleOptions(cx, JSOPTION_XML);
00403             break;
00404 
00405         case 'P':
00406             if (JS_GET_CLASS(cx, JS_GetPrototype(cx, obj)) != &global_class) {
00407                 JSObject *gobj;
00408 
00409                 if (!JS_SealObject(cx, obj, JS_TRUE))
00410                     return JS_FALSE;
00411                 gobj = JS_NewObject(cx, &global_class, NULL, NULL);
00412                 if (!gobj)
00413                     return JS_FALSE;
00414                 if (!JS_SetPrototype(cx, gobj, obj))
00415                     return JS_FALSE;
00416                 JS_SetParent(cx, gobj, NULL);
00417                 JS_SetGlobalObject(cx, gobj);
00418                 obj = gobj;
00419             }
00420             break;
00421 
00422         case 'b':
00423             gBranchLimit = atoi(argv[++i]);
00424             JS_SetBranchCallback(cx, my_BranchCallback);
00425             JS_ToggleOptions(cx, JSOPTION_NATIVE_BRANCH_CALLBACK);
00426             break;
00427 
00428         case 'c':
00429             /* set stack chunk size */
00430             gStackChunkSize = atoi(argv[++i]);
00431             break;
00432 
00433         case 'f':
00434             if (++i == argc)
00435                 return usage();
00436 
00437             Process(cx, obj, argv[i], JS_FALSE);
00438 
00439             /*
00440              * XXX: js -f foo.js should interpret foo.js and then
00441              * drop into interactive mode, but that breaks the test
00442              * harness. Just execute foo.js for now.
00443              */
00444             isInteractive = JS_FALSE;
00445             break;
00446 
00447         case 'e':
00448         {
00449             jsval rval;
00450 
00451             if (++i == argc)
00452                 return usage();
00453 
00454             /* Pass a filename of -e to imitate PERL */
00455             JS_EvaluateScript(cx, obj, argv[i], strlen(argv[i]),
00456                               "-e", 1, &rval);
00457 
00458             isInteractive = JS_FALSE;
00459             break;
00460 
00461         }
00462         case 'C':
00463             compileOnly = JS_TRUE;
00464             isInteractive = JS_FALSE;
00465             break;
00466 
00467         case 'i':
00468             isInteractive = forceTTY = JS_TRUE;
00469             break;
00470 
00471         case 'S':
00472             if (++i == argc)
00473                 return usage();
00474 
00475             /* Set maximum stack size. */
00476             gMaxStackSize = atoi(argv[i]);
00477             break;
00478 
00479         case 'z':
00480             obj = split_setup(cx);
00481             break;
00482 
00483         default:
00484             return usage();
00485         }
00486     }
00487 
00488     if (filename || isInteractive)
00489         Process(cx, obj, filename, forceTTY);
00490     return gExitCode;
00491 }
00492 
00493 
00494 static JSBool
00495 Version(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00496 {
00497     if (argc > 0 && JSVAL_IS_INT(argv[0]))
00498         *rval = INT_TO_JSVAL(JS_SetVersion(cx, (JSVersion) JSVAL_TO_INT(argv[0])));
00499     else
00500         *rval = INT_TO_JSVAL(JS_GetVersion(cx));
00501     return JS_TRUE;
00502 }
00503 
00504 static struct {
00505     const char  *name;
00506     uint32      flag;
00507 } js_options[] = {
00508     {"strict",          JSOPTION_STRICT},
00509     {"werror",          JSOPTION_WERROR},
00510     {"atline",          JSOPTION_ATLINE},
00511     {"xml",             JSOPTION_XML},
00512     {0,                 0}
00513 };
00514 
00515 static JSBool
00516 Options(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00517 {
00518     uint32 optset, flag;
00519     uintN i, j, found;
00520     JSString *str;
00521     const char *opt;
00522     char *names;
00523 
00524     optset = 0;
00525     for (i = 0; i < argc; i++) {
00526         str = JS_ValueToString(cx, argv[i]);
00527         if (!str)
00528             return JS_FALSE;
00529         opt = JS_GetStringBytes(str);
00530         for (j = 0; js_options[j].name; j++) {
00531             if (strcmp(js_options[j].name, opt) == 0) {
00532                 optset |= js_options[j].flag;
00533                 break;
00534             }
00535         }
00536     }
00537     optset = JS_ToggleOptions(cx, optset);
00538 
00539     names = NULL;
00540     found = 0;
00541     while (optset != 0) {
00542         flag = optset;
00543         optset &= optset - 1;
00544         flag &= ~optset;
00545         for (j = 0; js_options[j].name; j++) {
00546             if (js_options[j].flag == flag) {
00547                 names = JS_sprintf_append(names, "%s%s",
00548                                           names ? "," : "", js_options[j].name);
00549                 found++;
00550                 break;
00551             }
00552         }
00553     }
00554     if (!found)
00555         names = strdup("");
00556     if (!names) {
00557         JS_ReportOutOfMemory(cx);
00558         return JS_FALSE;
00559     }
00560 
00561     str = JS_NewString(cx, names, strlen(names));
00562     if (!str) {
00563         free(names);
00564         return JS_FALSE;
00565     }
00566     *rval = STRING_TO_JSVAL(str);
00567     return JS_TRUE;
00568 }
00569 
00570 static JSBool
00571 Load(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00572 {
00573     uintN i;
00574     JSString *str;
00575     const char *filename;
00576     JSScript *script;
00577     JSBool ok;
00578     jsval result;
00579     uint32 oldopts;
00580 
00581     for (i = 0; i < argc; i++) {
00582         str = JS_ValueToString(cx, argv[i]);
00583         if (!str)
00584             return JS_FALSE;
00585         argv[i] = STRING_TO_JSVAL(str);
00586         filename = JS_GetStringBytes(str);
00587         errno = 0;
00588         oldopts = JS_GetOptions(cx);
00589         JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO);
00590         script = JS_CompileFile(cx, obj, filename);
00591         if (!script) {
00592             ok = JS_FALSE;
00593         } else {
00594             ok = !compileOnly
00595                  ? JS_ExecuteScript(cx, obj, script, &result)
00596                  : JS_TRUE;
00597             JS_DestroyScript(cx, script);
00598         }
00599         JS_SetOptions(cx, oldopts);
00600         if (!ok)
00601             return JS_FALSE;
00602     }
00603 
00604     return JS_TRUE;
00605 }
00606 
00607 /*
00608  * function readline()
00609  * Provides a hook for scripts to read a line from stdin.
00610  */
00611 static JSBool
00612 ReadLine(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00613 {
00614 #define BUFSIZE 256
00615     FILE *from;
00616     char *buf, *tmp;
00617     size_t bufsize, buflength, gotlength;
00618     JSString *str;
00619 
00620     from = stdin;
00621     buflength = 0;
00622     bufsize = BUFSIZE;
00623     buf = JS_malloc(cx, bufsize);
00624     if (!buf)
00625         return JS_FALSE;
00626 
00627     while ((gotlength =
00628             js_fgets(buf + buflength, bufsize - buflength, from)) > 0) {
00629         buflength += gotlength;
00630 
00631         /* Are we done? */
00632         if (buf[buflength - 1] == '\n') {
00633             buf[buflength - 1] = '\0';
00634             break;
00635         }
00636 
00637         /* Else, grow our buffer for another pass. */
00638         tmp = JS_realloc(cx, buf, bufsize * 2);
00639         if (!tmp) {
00640             JS_free(cx, buf);
00641             return JS_FALSE;
00642         }
00643 
00644         bufsize *= 2;
00645         buf = tmp;
00646     }
00647 
00648     /* Treat the empty string specially. */
00649     if (buflength == 0) {
00650         *rval = JS_GetEmptyStringValue(cx);
00651         JS_free(cx, buf);
00652         return JS_TRUE;
00653     }
00654 
00655     /* Shrink the buffer to the real size. */
00656     tmp = JS_realloc(cx, buf, buflength);
00657     if (!tmp) {
00658         JS_free(cx, buf);
00659         return JS_FALSE;
00660     }
00661 
00662     buf = tmp;
00663 
00664     /*
00665      * Turn buf into a JSString. Note that buflength includes the trailing null
00666      * character.
00667      */
00668     str = JS_NewString(cx, buf, buflength - 1);
00669     if (!str) {
00670         JS_free(cx, buf);
00671         return JS_FALSE;
00672     }
00673 
00674     *rval = STRING_TO_JSVAL(str);
00675     return JS_TRUE;
00676 }
00677 
00678 static JSBool
00679 Print(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00680 {
00681     uintN i, n;
00682     JSString *str;
00683 
00684     for (i = n = 0; i < argc; i++) {
00685         str = JS_ValueToString(cx, argv[i]);
00686         if (!str)
00687             return JS_FALSE;
00688         fprintf(gOutFile, "%s%s", i ? " " : "", JS_GetStringBytes(str));
00689     }
00690     n++;
00691     if (n)
00692         fputc('\n', gOutFile);
00693     
00694     fflush(gOutFile);
00695     
00696     return JS_TRUE;
00697 }
00698 
00699 static JSBool
00700 Help(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
00701 
00702 static JSBool
00703 Quit(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00704 {
00705 #ifdef LIVECONNECT
00706     JSJ_SimpleShutdown();
00707 #endif
00708 
00709     JS_ConvertArguments(cx, argc, argv,"/ i", &gExitCode);
00710 
00711     gQuitting = JS_TRUE;
00712     return JS_FALSE;
00713 }
00714 
00715 static JSBool
00716 GC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00717 {
00718     JSRuntime *rt;
00719     uint32 preBytes;
00720 
00721     rt = cx->runtime;
00722     preBytes = rt->gcBytes;
00723 #ifdef GC_MARK_DEBUG
00724     if (argc && JSVAL_IS_STRING(argv[0])) {
00725         char *name = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
00726         FILE *file = fopen(name, "w");
00727         if (!file) {
00728             fprintf(gErrFile, "gc: can't open %s: %s\n", strerror(errno));
00729             return JS_FALSE;
00730         }
00731         js_DumpGCHeap = file;
00732     } else {
00733         js_DumpGCHeap = stdout;
00734     }
00735 #endif
00736     JS_GC(cx);
00737 #ifdef GC_MARK_DEBUG
00738     if (js_DumpGCHeap != stdout)
00739         fclose(js_DumpGCHeap);
00740     js_DumpGCHeap = NULL;
00741 #endif
00742     fprintf(gOutFile, "before %lu, after %lu, break %08lx\n",
00743             (unsigned long)preBytes, (unsigned long)rt->gcBytes,
00744 #ifdef XP_UNIX
00745             (unsigned long)sbrk(0)
00746 #else
00747             0
00748 #endif
00749             );
00750 #ifdef JS_GCMETER
00751     js_DumpGCStats(rt, stdout);
00752 #endif
00753     return JS_TRUE;
00754 }
00755 
00756 #ifdef JS_GC_ZEAL
00757 static JSBool
00758 GCZeal(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00759 {
00760     uintN zeal;
00761 
00762     if (!JS_ValueToECMAUint32(cx, argv[0], &zeal))
00763         return JS_FALSE;
00764     JS_SetGCZeal(cx, zeal);
00765     return JS_TRUE;
00766 }
00767 #endif /* JS_GC_ZEAL */
00768 
00769 static JSScript *
00770 ValueToScript(JSContext *cx, jsval v)
00771 {
00772     JSScript *script;
00773     JSFunction *fun;
00774 
00775     if (!JSVAL_IS_PRIMITIVE(v) &&
00776         JS_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_ScriptClass) {
00777         script = (JSScript *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v));
00778     } else {
00779         fun = JS_ValueToFunction(cx, v);
00780         if (!fun)
00781             return NULL;
00782         script = FUN_SCRIPT(fun);
00783     }
00784     return script;
00785 }
00786 
00787 static JSBool
00788 GetTrapArgs(JSContext *cx, uintN argc, jsval *argv, JSScript **scriptp,
00789             int32 *ip)
00790 {
00791     jsval v;
00792     uintN intarg;
00793     JSScript *script;
00794 
00795     *scriptp = cx->fp->down->script;
00796     *ip = 0;
00797     if (argc != 0) {
00798         v = argv[0];
00799         intarg = 0;
00800         if (!JSVAL_IS_PRIMITIVE(v) &&
00801             (JS_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_FunctionClass ||
00802              JS_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_ScriptClass)) {
00803             script = ValueToScript(cx, v);
00804             if (!script)
00805                 return JS_FALSE;
00806             *scriptp = script;
00807             intarg++;
00808         }
00809         if (argc > intarg) {
00810             if (!JS_ValueToInt32(cx, argv[intarg], ip))
00811                 return JS_FALSE;
00812         }
00813     }
00814     return JS_TRUE;
00815 }
00816 
00817 static JSTrapStatus
00818 TrapHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval,
00819             void *closure)
00820 {
00821     JSString *str;
00822     JSStackFrame *caller;
00823 
00824     str = (JSString *) closure;
00825     caller = JS_GetScriptedCaller(cx, NULL);
00826     if (!JS_EvaluateScript(cx, caller->scopeChain,
00827                            JS_GetStringBytes(str), JS_GetStringLength(str),
00828                            caller->script->filename, caller->script->lineno,
00829                            rval)) {
00830         return JSTRAP_ERROR;
00831     }
00832     if (*rval != JSVAL_VOID)
00833         return JSTRAP_RETURN;
00834     return JSTRAP_CONTINUE;
00835 }
00836 
00837 static JSBool
00838 Trap(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00839 {
00840     JSString *str;
00841     JSScript *script;
00842     int32 i;
00843 
00844     if (argc == 0) {
00845         JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_TRAP_USAGE);
00846         return JS_FALSE;
00847     }
00848     argc--;
00849     str = JS_ValueToString(cx, argv[argc]);
00850     if (!str)
00851         return JS_FALSE;
00852     argv[argc] = STRING_TO_JSVAL(str);
00853     if (!GetTrapArgs(cx, argc, argv, &script, &i))
00854         return JS_FALSE;
00855     return JS_SetTrap(cx, script, script->code + i, TrapHandler, str);
00856 }
00857 
00858 static JSBool
00859 Untrap(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00860 {
00861     JSScript *script;
00862     int32 i;
00863 
00864     if (!GetTrapArgs(cx, argc, argv, &script, &i))
00865         return JS_FALSE;
00866     JS_ClearTrap(cx, script, script->code + i, NULL, NULL);
00867     return JS_TRUE;
00868 }
00869 
00870 static JSBool
00871 LineToPC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00872 {
00873     JSScript *script;
00874     int32 i;
00875     uintN lineno;
00876     jsbytecode *pc;
00877 
00878     if (argc == 0) {
00879         JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_LINE2PC_USAGE);
00880         return JS_FALSE;
00881     }
00882     script = cx->fp->down->script;
00883     if (!GetTrapArgs(cx, argc, argv, &script, &i))
00884         return JS_FALSE;
00885     lineno = (i == 0) ? script->lineno : (uintN)i;
00886     pc = JS_LineNumberToPC(cx, script, lineno);
00887     if (!pc)
00888         return JS_FALSE;
00889     *rval = INT_TO_JSVAL(PTRDIFF(pc, script->code, jsbytecode));
00890     return JS_TRUE;
00891 }
00892 
00893 static JSBool
00894 PCToLine(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00895 {
00896     JSScript *script;
00897     int32 i;
00898     uintN lineno;
00899 
00900     if (!GetTrapArgs(cx, argc, argv, &script, &i))
00901         return JS_FALSE;
00902     lineno = JS_PCToLineNumber(cx, script, script->code + i);
00903     if (!lineno)
00904         return JS_FALSE;
00905     *rval = INT_TO_JSVAL(lineno);
00906     return JS_TRUE;
00907 }
00908 
00909 #ifdef DEBUG
00910 
00911 static void
00912 GetSwitchTableBounds(JSScript *script, uintN offset,
00913                      uintN *start, uintN *end)
00914 {
00915     jsbytecode *pc;
00916     JSOp op;
00917     ptrdiff_t jmplen;
00918     jsint low, high, n;
00919 
00920     pc = script->code + offset;
00921     op = *pc;
00922     switch (op) {
00923       case JSOP_TABLESWITCHX:
00924         jmplen = JUMPX_OFFSET_LEN;
00925         goto jump_table;
00926       case JSOP_TABLESWITCH:
00927         jmplen = JUMP_OFFSET_LEN;
00928       jump_table:
00929         pc += jmplen;
00930         low = GET_JUMP_OFFSET(pc);
00931         pc += JUMP_OFFSET_LEN;
00932         high = GET_JUMP_OFFSET(pc);
00933         pc += JUMP_OFFSET_LEN;
00934         n = high - low + 1;
00935         break;
00936 
00937       case JSOP_LOOKUPSWITCHX:
00938         jmplen = JUMPX_OFFSET_LEN;
00939         goto lookup_table;
00940       default:
00941         JS_ASSERT(op == JSOP_LOOKUPSWITCH);
00942         jmplen = JUMP_OFFSET_LEN;
00943       lookup_table:
00944         pc += jmplen;
00945         n = GET_ATOM_INDEX(pc);
00946         pc += ATOM_INDEX_LEN;
00947         jmplen += ATOM_INDEX_LEN;
00948         break;
00949     }
00950 
00951     *start = (uintN)(pc - script->code);
00952     *end = *start + (uintN)(n * jmplen);
00953 }
00954 
00955 
00956 /*
00957  * SrcNotes assumes that SRC_METHODBASE should be distinguished from SRC_LABEL
00958  * using the bytecode the source note points to.
00959  */
00960 JS_STATIC_ASSERT(SRC_LABEL == SRC_METHODBASE);
00961 
00962 static void
00963 SrcNotes(JSContext *cx, JSScript *script)
00964 {
00965     uintN offset, delta, caseOff, switchTableStart, switchTableEnd;
00966     jssrcnote *notes, *sn;
00967     JSSrcNoteType type;
00968     const char *name;
00969     JSOp op;
00970     jsatomid atomIndex;
00971     JSAtom *atom;
00972 
00973     fprintf(gOutFile, "\nSource notes:\n");
00974     offset = 0;
00975     notes = SCRIPT_NOTES(script);
00976     switchTableEnd = switchTableStart = 0;
00977     for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
00978         delta = SN_DELTA(sn);
00979         offset += delta;
00980         type = (JSSrcNoteType) SN_TYPE(sn);
00981         name = js_SrcNoteSpec[type].name;
00982         if (type == SRC_LABEL) {
00983             /* Heavily overloaded case. */
00984             if (switchTableStart <= offset && offset < switchTableEnd) {
00985                 name = "case";
00986             } else {
00987                 op = script->code[offset];
00988                 if (op == JSOP_GETMETHOD || op == JSOP_SETMETHOD) {
00989                     /* This is SRC_METHODBASE which we print as SRC_PCBASE. */
00990                     type = SRC_PCBASE;
00991                     name = "methodbase";
00992                 } else {
00993                     JS_ASSERT(op == JSOP_NOP);
00994                 }
00995             }
00996         }
00997         fprintf(gOutFile, "%3u: %5u [%4u] %-8s",
00998                 PTRDIFF(sn, notes, jssrcnote), offset, delta, name);
00999         switch (type) {
01000           case SRC_SETLINE:
01001             fprintf(gOutFile, " lineno %u", (uintN) js_GetSrcNoteOffset(sn, 0));
01002             break;
01003           case SRC_FOR:
01004             fprintf(gOutFile, " cond %u update %u tail %u",
01005                    (uintN) js_GetSrcNoteOffset(sn, 0),
01006                    (uintN) js_GetSrcNoteOffset(sn, 1),
01007                    (uintN) js_GetSrcNoteOffset(sn, 2));
01008             break;
01009           case SRC_IF_ELSE:
01010             fprintf(gOutFile, " else %u elseif %u",
01011                    (uintN) js_GetSrcNoteOffset(sn, 0),
01012                    (uintN) js_GetSrcNoteOffset(sn, 1));
01013             break;
01014           case SRC_COND:
01015           case SRC_WHILE:
01016           case SRC_PCBASE:
01017           case SRC_PCDELTA:
01018           case SRC_DECL:
01019           case SRC_BRACE:
01020             fprintf(gOutFile, " offset %u", (uintN) js_GetSrcNoteOffset(sn, 0));
01021             break;
01022           case SRC_LABEL:
01023           case SRC_LABELBRACE:
01024           case SRC_BREAK2LABEL:
01025           case SRC_CONT2LABEL:
01026           case SRC_FUNCDEF: {
01027             const char *bytes;
01028             JSFunction *fun;
01029             JSString *str;
01030 
01031             atomIndex = (jsatomid) js_GetSrcNoteOffset(sn, 0);
01032             atom = js_GetAtom(cx, &script->atomMap, atomIndex);
01033             if (type != SRC_FUNCDEF) {
01034                 bytes = js_AtomToPrintableString(cx, atom);
01035             } else {
01036                 fun = (JSFunction *)
01037                     JS_GetPrivate(cx, ATOM_TO_OBJECT(atom));
01038                 str = JS_DecompileFunction(cx, fun, JS_DONT_PRETTY_PRINT);
01039                 bytes = str ? JS_GetStringBytes(str) : "N/A";
01040             }
01041             fprintf(gOutFile, " atom %u (%s)", (uintN)atomIndex, bytes);
01042             break;
01043           }
01044           case SRC_SWITCH:
01045             fprintf(gOutFile, " length %u", (uintN) js_GetSrcNoteOffset(sn, 0));
01046             caseOff = (uintN) js_GetSrcNoteOffset(sn, 1);
01047             if (caseOff)
01048                 fprintf(gOutFile, " first case offset %u", caseOff);
01049             GetSwitchTableBounds(script, offset,
01050                                  &switchTableStart, &switchTableEnd);
01051             break;
01052           case SRC_CATCH:
01053             delta = (uintN) js_GetSrcNoteOffset(sn, 0);
01054             if (delta) {
01055                 if (script->main[offset] == JSOP_LEAVEBLOCK)
01056                     fprintf(gOutFile, " stack depth %u", delta);
01057                 else
01058                     fprintf(gOutFile, " guard delta %u", delta);
01059             }
01060             break;
01061           default:;
01062         }
01063         fputc('\n', gOutFile);
01064     }
01065 }
01066 
01067 static JSBool
01068 Notes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
01069 {
01070     uintN i;
01071     JSScript *script;
01072 
01073     for (i = 0; i < argc; i++) {
01074         script = ValueToScript(cx, argv[i]);
01075         if (!script)
01076             continue;
01077 
01078         SrcNotes(cx, script);
01079     }
01080     return JS_TRUE;
01081 }
01082 
01083 static JSBool
01084 TryNotes(JSContext *cx, JSScript *script)
01085 {
01086     JSTryNote *tn = script->trynotes;
01087 
01088     if (!tn)
01089         return JS_TRUE;
01090     fprintf(gOutFile, "\nException table:\nstart\tend\tcatch\n");
01091     while (tn->start && tn->catchStart) {
01092         fprintf(gOutFile, "  %d\t%d\t%d\n",
01093                tn->start, tn->start + tn->length, tn->catchStart);
01094         tn++;
01095     }
01096     return JS_TRUE;
01097 }
01098 
01099 static JSBool
01100 Disassemble(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
01101 {
01102     JSBool lines;
01103     uintN i;
01104     JSScript *script;
01105 
01106     if (argc > 0 &&
01107         JSVAL_IS_STRING(argv[0]) &&
01108         !strcmp(JS_GetStringBytes(JSVAL_TO_STRING(argv[0])), "-l")) {
01109         lines = JS_TRUE;
01110         argv++, argc--;
01111     } else {
01112         lines = JS_FALSE;
01113     }
01114     for (i = 0; i < argc; i++) {
01115         script = ValueToScript(cx, argv[i]);
01116         if (!script)
01117             return JS_FALSE;
01118 
01119         if (VALUE_IS_FUNCTION(cx, argv[i])) {
01120             JSFunction *fun = JS_ValueToFunction(cx, argv[i]);
01121             if (fun && (fun->flags & JSFUN_FLAGS_MASK)) {
01122                 uint16 flags = fun->flags;
01123                 fputs("flags:", stdout);
01124 
01125 #define SHOW_FLAG(flag) if (flags & JSFUN_##flag) fputs(" " #flag, stdout);
01126 
01127                 SHOW_FLAG(LAMBDA);
01128                 SHOW_FLAG(SETTER);
01129                 SHOW_FLAG(GETTER);
01130                 SHOW_FLAG(BOUND_METHOD);
01131                 SHOW_FLAG(HEAVYWEIGHT);
01132                 SHOW_FLAG(THISP_STRING);
01133                 SHOW_FLAG(THISP_NUMBER);
01134                 SHOW_FLAG(THISP_BOOLEAN);
01135                 SHOW_FLAG(INTERPRETED);
01136 
01137 #undef SHOW_FLAG
01138                 putchar('\n');
01139             }
01140         }
01141 
01142         if (!js_Disassemble(cx, script, lines, stdout))
01143             return JS_FALSE;
01144         SrcNotes(cx, script);
01145         TryNotes(cx, script);
01146     }
01147     return JS_TRUE;
01148 }
01149 
01150 static JSBool
01151 DisassWithSrc(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
01152               jsval *rval)
01153 {
01154 #define LINE_BUF_LEN 512
01155     uintN i, len, line1, line2, bupline;
01156     JSScript *script;
01157     FILE *file;
01158     char linebuf[LINE_BUF_LEN];
01159     jsbytecode *pc, *end;
01160     static char sep[] = ";-------------------------";
01161 
01162     for (i = 0; i < argc; i++) {
01163         script = ValueToScript(cx, argv[i]);
01164         if (!script)
01165             return JS_FALSE;
01166 
01167         if (!script || !script->filename) {
01168             JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
01169                                             JSSMSG_FILE_SCRIPTS_ONLY);
01170             return JS_FALSE;
01171         }
01172 
01173         file = fopen(script->filename, "r");
01174         if (!file) {
01175             JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
01176                             JSSMSG_CANT_OPEN,
01177                             script->filename, strerror(errno));
01178             return JS_FALSE;
01179         }
01180 
01181         pc = script->code;
01182         end = pc + script->length;
01183 
01184         /* burn the leading lines */
01185         line2 = JS_PCToLineNumber(cx, script, pc);
01186         for (line1 = 0; line1 < line2 - 1; line1++)
01187             fgets(linebuf, LINE_BUF_LEN, file);
01188 
01189         bupline = 0;
01190         while (pc < end) {
01191             line2 = JS_PCToLineNumber(cx, script, pc);
01192 
01193             if (line2 < line1) {
01194                 if (bupline != line2) {
01195                     bupline = line2;
01196                     fprintf(gOutFile, "%s %3u: BACKUP\n", sep, line2);
01197                 }
01198             } else {
01199                 if (bupline && line1 == line2)
01200                     fprintf(gOutFile, "%s %3u: RESTORE\n", sep, line2);
01201                 bupline = 0;
01202                 while (line1 < line2) {
01203                     if (!fgets(linebuf, LINE_BUF_LEN, file)) {
01204                         JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
01205                                              JSSMSG_UNEXPECTED_EOF,
01206                                              script->filename);
01207                         goto bail;
01208                     }
01209                     line1++;
01210                     fprintf(gOutFile, "%s %3u: %s", sep, line1, linebuf);
01211                 }
01212             }
01213 
01214             len = js_Disassemble1(cx, script, pc,
01215                                   PTRDIFF(pc, script->code, jsbytecode),
01216                                   JS_TRUE, stdout);
01217             if (!len)
01218                 return JS_FALSE;
01219             pc += len;
01220         }
01221 
01222       bail:
01223         fclose(file);
01224     }
01225     return JS_TRUE;
01226 #undef LINE_BUF_LEN
01227 }
01228 
01229 static JSBool
01230 Tracing(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
01231 {
01232     JSBool bval;
01233     JSString *str;
01234 
01235     if (argc == 0) {
01236         *rval = BOOLEAN_TO_JSVAL(cx->tracefp != 0);
01237         return JS_TRUE;
01238     }
01239 
01240     switch (JS_TypeOfValue(cx, argv[0])) {
01241       case JSTYPE_NUMBER:
01242         bval = JSVAL_IS_INT(argv[0])
01243                ? JSVAL_TO_INT(argv[0])
01244                : (jsint) *JSVAL_TO_DOUBLE(argv[0]);
01245         break;
01246       case JSTYPE_BOOLEAN:
01247         bval = JSVAL_TO_BOOLEAN(argv[0]);
01248         break;
01249       default:
01250         str = JS_ValueToString(cx, argv[0]);
01251         if (!str)
01252             return JS_FALSE;
01253         fprintf(gErrFile, "tracing: illegal argument %s\n",
01254                 JS_GetStringBytes(str));
01255         return JS_TRUE;
01256     }
01257     cx->tracefp = bval ? stderr : NULL;
01258     return JS_TRUE;
01259 }
01260 
01261 typedef struct DumpAtomArgs {
01262     JSContext   *cx;
01263     FILE        *fp;
01264 } DumpAtomArgs;
01265 
01266 static int
01267 DumpAtom(JSHashEntry *he, int i, void *arg)
01268 {
01269     DumpAtomArgs *args = (DumpAtomArgs *)arg;
01270     FILE *fp = args->fp;
01271     JSAtom *atom = (JSAtom *)he;
01272 
01273     fprintf(fp, "%3d %08x %5lu ",
01274             i, (uintN)he->keyHash, (unsigned long)atom->number);
01275     if (ATOM_IS_STRING(atom))
01276         fprintf(fp, "\"%s\"\n", js_AtomToPrintableString(args->cx, atom));
01277     else if (ATOM_IS_INT(atom))
01278         fprintf(fp, "%ld\n", (long)ATOM_TO_INT(atom));
01279     else
01280         fprintf(fp, "%.16g\n", *ATOM_TO_DOUBLE(atom));
01281     return HT_ENUMERATE_NEXT;
01282 }
01283 
01284 static void
01285 DumpScope(JSContext *cx, JSObject *obj, FILE *fp)
01286 {
01287     uintN i;
01288     JSScope *scope;
01289     JSScopeProperty *sprop;
01290 
01291     i = 0;
01292     scope = OBJ_SCOPE(obj);
01293     for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) {
01294         if (SCOPE_HAD_MIDDLE_DELETE(scope) && !SCOPE_HAS_PROPERTY(scope, sprop))
01295             continue;
01296         fprintf(fp, "%3u %p", i, (void *)sprop);
01297         if (JSID_IS_INT(sprop->id)) {
01298             fprintf(fp, " [%ld]", (long)JSVAL_TO_INT(sprop->id));
01299         } else if (JSID_IS_ATOM(sprop->id)) {
01300             JSAtom *atom = JSID_TO_ATOM(sprop->id);
01301             fprintf(fp, " \"%s\"", js_AtomToPrintableString(cx, atom));
01302         } else {
01303             jsval v = OBJECT_TO_JSVAL(JSID_TO_OBJECT(sprop->id));
01304             fprintf(fp, " \"%s\"", js_ValueToPrintableString(cx, v));
01305         }
01306 
01307 #define DUMP_ATTR(name) if (sprop->attrs & JSPROP_##name) fputs(" " #name, fp)
01308         DUMP_ATTR(ENUMERATE);
01309         DUMP_ATTR(READONLY);
01310         DUMP_ATTR(PERMANENT);
01311         DUMP_ATTR(EXPORTED);
01312         DUMP_ATTR(GETTER);
01313         DUMP_ATTR(SETTER);
01314 #undef  DUMP_ATTR
01315 
01316         fprintf(fp, " slot %lu flags %x shortid %d\n",
01317                 (unsigned long)sprop->slot, sprop->flags, sprop->shortid);
01318     }
01319 }
01320 
01321 static JSBool
01322 DumpStats(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
01323 {
01324     uintN i;
01325     JSString *str;
01326     const char *bytes;
01327     JSAtom *atom;
01328     JSObject *obj2;
01329     JSProperty *prop;
01330     jsval value;
01331 
01332     for (i = 0; i < argc; i++) {
01333         str = JS_ValueToString(cx, argv[i]);
01334         if (!str)
01335             return JS_FALSE;
01336         bytes = JS_GetStringBytes(str);
01337         if (strcmp(bytes, "arena") == 0) {
01338 #ifdef JS_ARENAMETER
01339             JS_DumpArenaStats(stdout);
01340 #endif
01341         } else if (strcmp(bytes, "atom") == 0) {
01342             DumpAtomArgs args;
01343 
01344             fprintf(gOutFile, "\natom table contents:\n");
01345             args.cx = cx;
01346             args.fp = stdout;
01347             JS_HashTableEnumerateEntries(cx->runtime->atomState.table,
01348                                          DumpAtom,
01349                                          &args);
01350 #ifdef HASHMETER
01351             JS_HashTableDumpMeter(cx->runtime->atomState.table,
01352                                   DumpAtom,
01353                                   stdout);
01354 #endif
01355         } else if (strcmp(bytes, "global") == 0) {
01356             DumpScope(cx, cx->globalObject, stdout);
01357         } else {
01358             atom = js_Atomize(cx, bytes, JS_GetStringLength(str), 0);
01359             if (!atom)
01360                 return JS_FALSE;
01361             if (!js_FindProperty(cx, ATOM_TO_JSID(atom), &obj, &obj2, &prop))
01362                 return JS_FALSE;
01363             if (prop) {
01364                 OBJ_DROP_PROPERTY(cx, obj2, prop);
01365                 if (!OBJ_GET_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &value))
01366                     return JS_FALSE;
01367             }
01368             if (!prop || !JSVAL_IS_OBJECT(value)) {
01369                 fprintf(gErrFile, "js: invalid stats argument %s\n",
01370                         bytes);
01371                 continue;
01372             }
01373             obj = JSVAL_TO_OBJECT(value);
01374             if (obj)
01375                 DumpScope(cx, obj, stdout);
01376         }
01377     }
01378     return JS_TRUE;
01379 }
01380 
01381 #endif /* DEBUG */
01382 
01383 #ifdef TEST_EXPORT
01384 static JSBool
01385 DoExport(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
01386 {
01387     JSAtom *atom;
01388     JSObject *obj2;
01389     JSProperty *prop;
01390     JSBool ok;
01391     uintN attrs;
01392 
01393     if (argc != 2) {
01394         JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_DOEXP_USAGE);
01395         return JS_FALSE;
01396     }
01397     if (!JS_ValueToObject(cx, argv[0], &obj))
01398         return JS_FALSE;
01399     argv[0] = OBJECT_TO_JSVAL(obj);
01400     atom = js_ValueToStringAtom(cx, argv[1]);
01401     if (!atom)
01402         return JS_FALSE;
01403     if (!OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &obj2, &prop))
01404         return JS_FALSE;
01405     if (!prop) {
01406         ok = OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, NULL, NULL,
01407                                  JSPROP_EXPORTED, NULL);
01408     } else {
01409         ok = OBJ_GET_ATTRIBUTES(cx, obj, ATOM_TO_JSID(atom), prop, &attrs);
01410         if (ok) {
01411             attrs |= JSPROP_EXPORTED;
01412             ok = OBJ_SET_ATTRIBUTES(cx, obj, ATOM_TO_JSID(atom), prop, &attrs);
01413         }
01414         OBJ_DROP_PROPERTY(cx, obj2, prop);
01415     }
01416     return ok;
01417 }
01418 #endif
01419 
01420 #ifdef TEST_CVTARGS
01421 #include <ctype.h>
01422 
01423 static const char *
01424 EscapeWideString(jschar *w)
01425 {
01426     static char enuf[80];
01427     static char hex[] = "0123456789abcdef";
01428     jschar u;
01429     unsigned char b, c;
01430     int i, j;
01431 
01432     if (!w)
01433         return "";
01434     for (i = j = 0; i < sizeof enuf - 1; i++, j++) {
01435         u = w[j];
01436         if (u == 0)
01437             break;
01438         b = (unsigned char)(u >> 8);
01439         c = (unsigned char)(u);
01440         if (b) {
01441             if (i >= sizeof enuf - 6)
01442                 break;
01443             enuf[i++] = '\\';
01444             enuf[i++] = 'u';
01445             enuf[i++] = hex[b >> 4];
01446             enuf[i++] = hex[b & 15];
01447             enuf[i++] = hex[c >> 4];
01448             enuf[i] = hex[c & 15];
01449         } else if (!isprint(c)) {
01450             if (i >= sizeof enuf - 4)
01451                 break;
01452             enuf[i++] = '\\';
01453             enuf[i++] = 'x';
01454             enuf[i++] = hex[c >> 4];
01455             enuf[i] = hex[c & 15];
01456         } else {
01457             enuf[i] = (char)c;
01458         }
01459     }
01460     enuf[i] = 0;
01461     return enuf;
01462 }
01463 
01464 #include <stdarg.h>
01465 
01466 static JSBool
01467 ZZ_formatter(JSContext *cx, const char *format, JSBool fromJS, jsval **vpp,
01468              va_list *app)
01469 {
01470     jsval *vp;
01471     va_list ap;
01472     jsdouble re, im;
01473 
01474     printf("entering ZZ_formatter");
01475     vp = *vpp;
01476     ap = *app;
01477     if (fromJS) {
01478         if (!JS_ValueToNumber(cx, vp[0], &re))
01479             return JS_FALSE;
01480         if (!JS_ValueToNumber(cx, vp[1], &im))
01481             return JS_FALSE;
01482         *va_arg(ap, jsdouble *) = re;
01483         *va_arg(ap, jsdouble *) = im;
01484     } else {
01485         re = va_arg(ap, jsdouble);
01486         im = va_arg(ap, jsdouble);
01487         if (!JS_NewNumberValue(cx, re, &vp[0]))
01488             return JS_FALSE;
01489         if (!JS_NewNumberValue(cx, im, &vp[1]))
01490             return JS_FALSE;
01491     }
01492     *vpp = vp + 2;
01493     *app = ap;
01494     printf("leaving ZZ_formatter");
01495     return JS_TRUE;
01496 }
01497 
01498 static JSBool
01499 ConvertArgs(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
01500 {
01501     JSBool b = JS_FALSE;
01502     jschar c = 0;
01503     int32 i = 0, j = 0;
01504     uint32 u = 0;
01505     jsdouble d = 0, I = 0, re = 0, im = 0;
01506     char *s = NULL;
01507     JSString *str = NULL;
01508     jschar *w = NULL;
01509     JSObject *obj2 = NULL;
01510     JSFunction *fun = NULL;
01511     jsval v = JSVAL_VOID;
01512     JSBool ok;
01513 
01514     if (!JS_AddArgumentFormatter(cx, "ZZ", ZZ_formatter))
01515         return JS_FALSE;;
01516     ok = JS_ConvertArguments(cx, argc, argv, "b/ciujdIsSWofvZZ*",
01517                              &b, &c, &i, &u, &j, &d, &I, &s, &str, &w, &obj2,
01518                              &fun, &v, &re, &im);
01519     JS_RemoveArgumentFormatter(cx, "ZZ");
01520     if (!ok)
01521         return JS_FALSE;
01522     fprintf(gOutFile,
01523             "b %u, c %x (%c), i %ld, u %lu, j %ld\n",
01524             b, c, (char)c, i, u, j);
01525     fprintf(gOutFile,
01526             "d %g, I %g, s %s, S %s, W %s, obj %s, fun %s\n"
01527             "v %s, re %g, im %g\n",
01528             d, I, s, str ? JS_GetStringBytes(str) : "", EscapeWideString(w),
01529             JS_GetStringBytes(JS_ValueToString(cx, OBJECT_TO_JSVAL(obj2))),
01530             fun ? JS_GetStringBytes(JS_DecompileFunction(cx, fun, 4)) : "",
01531             JS_GetStringBytes(JS_ValueToString(cx, v)), re, im);
01532     return JS_TRUE;
01533 }
01534 #endif
01535 
01536 static JSBool
01537 BuildDate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
01538 {
01539     char version[20] = "\n";
01540 #if JS_VERSION < 150
01541     sprintf(version, " for version %d\n", JS_VERSION);
01542 #endif
01543     fprintf(gOutFile, "built on %s at %s%s", __DATE__, __TIME__, version);
01544     return JS_TRUE;
01545 }
01546 
01547 static JSBool
01548 Clear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
01549 {
01550     if (argc != 0 && !JS_ValueToObject(cx, argv[0], &obj))
01551         return JS_FALSE;
01552     JS_ClearScope(cx, obj);
01553     return JS_TRUE;
01554 }
01555 
01556 static JSBool
01557 Intern(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
01558 {
01559     JSString *str;
01560 
01561     str = JS_ValueToString(cx, argv[0]);
01562     if (!str)
01563         return JS_FALSE;
01564     if (!JS_InternUCStringN(cx, JS_GetStringChars(str),
01565                                 JS_GetStringLength(str))) {
01566         return JS_FALSE;
01567     }
01568     return JS_TRUE;
01569 }
01570 
01571 static JSBool
01572 Clone(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
01573 {
01574     JSFunction *fun;
01575     JSObject *funobj, *parent, *clone;
01576 
01577     fun = JS_ValueToFunction(cx, argv[0]);
01578     if (!fun)
01579         return JS_FALSE;
01580     funobj = JS_GetFunctionObject(fun);
01581     if (argc > 1) {
01582         if (!JS_ValueToObject(cx, argv[1], &parent))
01583             return JS_FALSE;
01584     } else {
01585         parent = JS_GetParent(cx, funobj);
01586     }
01587     clone = JS_CloneFunctionObject(cx, funobj, parent);
01588     if (!clone)
01589         return JS_FALSE;
01590     *rval = OBJECT_TO_JSVAL(clone);
01591     return JS_TRUE;
01592 }
01593 
01594 static JSBool
01595 Seal(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
01596 {
01597     JSObject *target;
01598     JSBool deep = JS_FALSE;
01599 
01600     if (!JS_ConvertArguments(cx, argc, argv, "o/b", &target, &deep))
01601         return JS_FALSE;
01602     if (!target)
01603         return JS_TRUE;
01604     return JS_SealObject(cx, target, deep);
01605 }
01606 
01607 static JSBool
01608 GetPDA(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
01609 {
01610     JSObject *vobj, *aobj, *pdobj;
01611     JSBool ok;
01612     JSPropertyDescArray pda;
01613     JSPropertyDesc *pd;
01614     uint32 i;
01615     jsval v;
01616 
01617     if (!JS_ValueToObject(cx, argv[0], &vobj))
01618         return JS_FALSE;
01619     if (!vobj)
01620         return JS_TRUE;
01621 
01622     aobj = JS_NewArrayObject(cx, 0, NULL);
01623     if (!aobj)
01624         return JS_FALSE;
01625     *rval = OBJECT_TO_JSVAL(aobj);
01626 
01627     ok = JS_GetPropertyDescArray(cx, vobj, &pda);
01628     if (!ok)
01629         return JS_FALSE;
01630     pd = pda.array;
01631     for (i = 0; i < pda.length; i++) {
01632         pdobj = JS_NewObject(cx, NULL, NULL, NULL);
01633         if (!pdobj) {
01634             ok = JS_FALSE;
01635             break;
01636         }
01637 
01638         ok = JS_SetProperty(cx, pdobj, "id", &pd->id) &&
01639              JS_SetProperty(cx, pdobj, "value", &pd->value) &&
01640              (v = INT_TO_JSVAL(pd->flags),
01641               JS_SetProperty(cx, pdobj, "flags", &v)) &&
01642              (v = INT_TO_JSVAL(pd->slot),
01643               JS_SetProperty(cx, pdobj, "slot", &v)) &&
01644              JS_SetProperty(cx, pdobj, "alias", &pd->alias);
01645         if (!ok)
01646             break;
01647 
01648         v = OBJECT_TO_JSVAL(pdobj);
01649         ok = JS_SetElement(cx, aobj, i, &v);
01650         if (!ok)
01651             break;
01652     }
01653     JS_PutPropertyDescArray(cx, &pda);
01654     return ok;
01655 }
01656 
01657 static JSBool
01658 GetSLX(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
01659 {
01660     JSScript *script;
01661 
01662     script = ValueToScript(cx, argv[0]);
01663     if (!script)
01664         return JS_FALSE;
01665     *rval = INT_TO_JSVAL(js_GetScriptLineExtent(script));
01666     return JS_TRUE;
01667 }
01668 
01669 static JSBool
01670 ToInt32(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
01671 {
01672     int32 i;
01673 
01674     if (!JS_ValueToInt32(cx, argv[0], &i))
01675         return JS_FALSE;
01676     return JS_NewNumberValue(cx, i, rval);
01677 }
01678 
01679 static JSBool
01680 StringsAreUtf8(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
01681                jsval *rval)
01682 {
01683     *rval = JS_CStringsAreUTF8() ? JSVAL_TRUE : JSVAL_FALSE;
01684     return JS_TRUE;
01685 }
01686 
01687 static const char* badUtf8 = "...\xC0...";
01688 static const char* bigUtf8 = "...\xFB\xBF\xBF\xBF\xBF...";
01689 static const jschar badSurrogate[] = { 'A', 'B', 'C', 0xDEEE, 'D', 'E', 0 };
01690 
01691 static JSBool
01692 TestUtf8(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
01693 {
01694     intN mode = 1;
01695     jschar chars[20];
01696     size_t charsLength = 5;
01697     char bytes[20];
01698     size_t bytesLength = 20;
01699     if (argc && !JS_ValueToInt32(cx, *argv, &mode))
01700         return JS_FALSE;
01701 
01702     /* The following throw errors if compiled with UTF-8. */
01703     switch (mode) {
01704       /* mode 1: malformed UTF-8 string. */
01705       case 1:
01706         JS_NewStringCopyZ(cx, badUtf8);
01707         break;
01708       /* mode 2: big UTF-8 character. */
01709       case 2:
01710         JS_NewStringCopyZ(cx, bigUtf8);
01711         break;
01712       /* mode 3: bad surrogate character. */
01713       case 3:
01714         JS_EncodeCharacters(cx, badSurrogate, 6, bytes, &bytesLength);
01715         break;
01716       /* mode 4: use a too small buffer. */
01717       case 4:
01718         JS_DecodeBytes(cx, "1234567890", 10, chars, &charsLength);
01719         break;
01720       default:
01721         JS_ReportError(cx, "invalid mode parameter");
01722         return JS_FALSE;
01723     }
01724     return !JS_IsExceptionPending (cx);
01725 }
01726 
01727 static JSBool
01728 ThrowError(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
01729 {
01730     JS_ReportError(cx, "This is an error");
01731     return JS_FALSE;
01732 }
01733 
01734 #define LAZY_STANDARD_CLASSES
01735 
01736 /* A class for easily testing the inner/outer object callbacks. */
01737 typedef struct ComplexObject {
01738     JSBool isInner;
01739     JSObject *inner;
01740     JSObject *outer;
01741 } ComplexObject;
01742 
01743 static JSObject *
01744 split_create_outer(JSContext *cx);
01745 
01746 static JSObject *
01747 split_create_inner(JSContext *cx, JSObject *outer);
01748 
01749 static ComplexObject *
01750 split_get_private(JSContext *cx, JSObject *obj);
01751 
01752 JS_STATIC_DLL_CALLBACK(JSBool)
01753 split_addProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
01754 {
01755     ComplexObject *cpx;
01756     jsid asId;
01757 
01758     cpx = split_get_private(cx, obj);
01759     if (!cpx)
01760         return JS_TRUE;
01761     if (!cpx->isInner && cpx->inner) {
01762         /* Make sure to define this property on the inner object. */
01763         if (!JS_ValueToId(cx, *vp, &asId))
01764             return JS_FALSE;
01765         return OBJ_DEFINE_PROPERTY(cx, cpx->inner, asId, *vp, NULL, NULL,
01766                                    JSPROP_ENUMERATE, NULL);
01767     }
01768     return JS_TRUE;
01769 }
01770 
01771 JS_STATIC_DLL_CALLBACK(JSBool)
01772 split_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
01773 {
01774     ComplexObject *cpx;
01775 
01776     cpx = split_get_private(cx, obj);
01777     if (!cpx)
01778         return JS_TRUE;
01779     if (!cpx->isInner && cpx->inner) {
01780         if (JSVAL_IS_STRING(id)) {
01781             JSString *str;
01782 
01783             str = JSVAL_TO_STRING(id);
01784             return JS_GetUCProperty(cx, cpx->inner, JS_GetStringChars(str),
01785                                     JS_GetStringLength(str), vp);
01786         }
01787         if (JSVAL_IS_INT(id))
01788             return JS_GetElement(cx, cpx->inner, JSVAL_TO_INT(id), vp);
01789         return JS_TRUE;
01790     }
01791 
01792     return JS_TRUE;
01793 }
01794 
01795 JS_STATIC_DLL_CALLBACK(JSBool)
01796 split_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
01797 {
01798     ComplexObject *cpx;
01799 
01800     cpx = split_get_private(cx, obj);
01801     if (!cpx)
01802         return JS_TRUE;
01803     if (!cpx->isInner && cpx->inner) {
01804         if (JSVAL_IS_STRING(id)) {
01805             JSString *str;
01806 
01807             str = JSVAL_TO_STRING(id);
01808             return JS_SetUCProperty(cx, cpx->inner, JS_GetStringChars(str),
01809                                     JS_GetStringLength(str), vp);
01810         }
01811         if (JSVAL_IS_INT(id))
01812             return JS_SetElement(cx, cpx->inner, JSVAL_TO_INT(id), vp);
01813         return JS_TRUE;
01814     }
01815 
01816     return JS_TRUE;
01817 }
01818 
01819 JS_STATIC_DLL_CALLBACK(JSBool)
01820 split_delProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
01821 {
01822     ComplexObject *cpx;
01823     jsid asId;
01824 
01825     cpx = split_get_private(cx, obj);
01826     if (!cpx)
01827         return JS_TRUE;
01828     if (!cpx->isInner && cpx->inner) {
01829         /* Make sure to define this property on the inner object. */
01830         if (!JS_ValueToId(cx, *vp, &asId))
01831             return JS_FALSE;
01832         return OBJ_DELETE_PROPERTY(cx, cpx->inner, asId, vp);
01833     }
01834     return JS_TRUE;
01835 }
01836 
01837 JS_STATIC_DLL_CALLBACK(JSBool)
01838 split_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
01839                   jsval *statep, jsid *idp)
01840 {
01841     ComplexObject *cpx;
01842     JSObject *iterator;
01843 
01844     switch (enum_op) {
01845       case JSENUMERATE_INIT:
01846         cpx = JS_GetPrivate(cx, obj);
01847 
01848         if (!cpx->isInner && cpx->inner)
01849             obj = cpx->inner;
01850 
01851         iterator = JS_NewPropertyIterator(cx, obj);
01852         if (!iterator)
01853             return JS_FALSE;
01854 
01855         *statep = OBJECT_TO_JSVAL(iterator);
01856         if (idp)
01857             *idp = JSVAL_ZERO;
01858         break;
01859 
01860       case JSENUMERATE_NEXT:
01861         iterator = (JSObject*)JSVAL_TO_OBJECT(*statep);
01862         if (!JS_NextProperty(cx, iterator, idp))
01863             return JS_FALSE;
01864 
01865         if (*idp != JSVAL_VOID)
01866             break;
01867         /* Fall through. */
01868 
01869       case JSENUMERATE_DESTROY:
01870         /* Let GC at our iterator object. */
01871         *statep = JSVAL_NULL;
01872         break;
01873     }
01874 
01875     return JS_TRUE;
01876 }
01877 
01878 JS_STATIC_DLL_CALLBACK(JSBool)
01879 split_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
01880                 JSObject **objp)
01881 {
01882     ComplexObject *cpx;
01883 
01884     cpx = split_get_private(cx, obj);
01885     if (!cpx)
01886         return JS_TRUE;
01887     if (!cpx->isInner && cpx->inner) {
01888         jsid asId;
01889         JSProperty *prop;
01890 
01891         if (!JS_ValueToId(cx, id, &asId))
01892             return JS_FALSE;
01893 
01894         if (!OBJ_LOOKUP_PROPERTY(cx, cpx->inner, asId, objp, &prop))
01895             return JS_FALSE;
01896         if (prop)
01897             OBJ_DROP_PROPERTY(cx, cpx->inner, prop);
01898 
01899         return JS_TRUE;
01900     }
01901 
01902 #ifdef LAZY_STANDARD_CLASSES
01903     if (!(flags & JSRESOLVE_ASSIGNING)) {
01904         JSBool resolved;
01905 
01906         if (!JS_ResolveStandardClass(cx, obj, id, &resolved))
01907             return JS_FALSE;
01908 
01909         if (resolved) {
01910             *objp = obj;
01911             return JS_TRUE;
01912         }
01913     }
01914 #endif
01915 
01916     /* XXX For additional realism, let's resolve some random property here. */
01917     return JS_TRUE;
01918 }
01919 
01920 JS_STATIC_DLL_CALLBACK(void)
01921 split_finalize(JSContext *cx, JSObject *obj)
01922 {
01923     JS_free(cx, JS_GetPrivate(cx, obj));
01924 }
01925 
01926 JS_STATIC_DLL_CALLBACK(uint32)
01927 split_mark(JSContext *cx, JSObject *obj, void *arg)
01928 {
01929     ComplexObject *cpx;
01930 
01931     cpx = JS_GetPrivate(cx, obj);
01932 
01933     if (!cpx->isInner && cpx->inner) {
01934         /* Mark the inner object. */
01935         JS_MarkGCThing(cx, cpx->inner, "ComplexObject.inner", arg);
01936     }
01937 
01938     return 0;
01939 }
01940 
01941 JS_STATIC_DLL_CALLBACK(JSObject *)
01942 split_outerObject(JSContext *cx, JSObject *obj)
01943 {
01944     ComplexObject *cpx;
01945 
01946     cpx = JS_GetPrivate(cx, obj);
01947     return cpx->isInner ? cpx->outer : obj;
01948 }
01949 
01950 JS_STATIC_DLL_CALLBACK(JSObject *)
01951 split_innerObject(JSContext *cx, JSObject *obj)
01952 {
01953     ComplexObject *cpx;
01954 
01955     cpx = JS_GetPrivate(cx, obj);
01956     return !cpx->isInner ? cpx->inner : obj;
01957 }
01958 
01959 static JSExtendedClass split_global_class = {
01960     {"split_global",
01961     JSCLASS_NEW_RESOLVE | JSCLASS_HAS_PRIVATE | JSCLASS_IS_EXTENDED,
01962     split_addProperty, split_delProperty,
01963     split_getProperty, split_setProperty,
01964     (JSEnumerateOp)split_enumerate,
01965     (JSResolveOp)split_resolve,
01966     JS_ConvertStub, split_finalize,
01967     NULL, NULL, NULL, NULL, NULL, NULL,
01968     split_mark, NULL},
01969     NULL, split_outerObject, split_innerObject,
01970     NULL, NULL, NULL, NULL, NULL
01971 };
01972 
01973 JSObject *
01974 split_create_outer(JSContext *cx)
01975 {
01976     ComplexObject *cpx;
01977     JSObject *obj;
01978 
01979     cpx = JS_malloc(cx, sizeof *obj);
01980     if (!cpx)
01981         return NULL;
01982     cpx->outer = NULL;
01983     cpx->inner = NULL;
01984     cpx->isInner = JS_FALSE;
01985 
01986     obj = JS_NewObject(cx, &split_global_class.base, NULL, NULL);
01987     if (!obj) {
01988         JS_free(cx, cpx);
01989         return NULL;
01990     }
01991 
01992     JS_ASSERT(!JS_GetParent(cx, obj));
01993     if (!JS_SetPrivate(cx, obj, cpx)) {
01994         JS_free(cx, cpx);
01995         return NULL;
01996     }
01997 
01998     return obj;
01999 }
02000 
02001 static JSObject *
02002 split_create_inner(JSContext *cx, JSObject *outer)
02003 {
02004     ComplexObject *cpx, *outercpx;
02005     JSObject *obj;
02006 
02007     JS_ASSERT(JS_GET_CLASS(cx, outer) == &split_global_class.base);
02008 
02009     cpx = JS_malloc(cx, sizeof *cpx);
02010     if (!cpx)
02011         return NULL;
02012     cpx->outer = outer;
02013     cpx->inner = NULL;
02014     cpx->isInner = JS_TRUE;
02015 
02016     obj = JS_NewObject(cx, &split_global_class.base, NULL, NULL);
02017     if (!obj || !JS_SetParent(cx, obj, NULL) || !JS_SetPrivate(cx, obj, cpx)) {
02018         JS_free(cx, cpx);
02019         return NULL;
02020     }
02021 
02022     outercpx = JS_GetPrivate(cx, outer);
02023     outercpx->inner = obj;
02024 
02025     return obj;
02026 }
02027 
02028 static ComplexObject *
02029 split_get_private(JSContext *cx, JSObject *obj)
02030 {
02031     do {
02032         if (JS_GET_CLASS(cx, obj) == &split_global_class.base)
02033             return JS_GetPrivate(cx, obj);
02034         obj = JS_GetParent(cx, obj);
02035     } while (obj);
02036 
02037     return NULL;
02038 }
02039 
02040 static JSBool
02041 sandbox_enumerate(JSContext *cx, JSObject *obj)
02042 {
02043     jsval v;
02044     JSBool b;
02045 
02046     if (!JS_GetProperty(cx, obj, "lazy", &v) || !JS_ValueToBoolean(cx, v, &b))
02047         return JS_FALSE;
02048     return !b || JS_EnumerateStandardClasses(cx, obj);
02049 }
02050 
02051 static JSBool
02052 sandbox_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
02053                 JSObject **objp)
02054 {
02055     jsval v;
02056     JSBool b, resolved;
02057 
02058     if (!JS_GetProperty(cx, obj, "lazy", &v) || !JS_ValueToBoolean(cx, v, &b))
02059         return JS_FALSE;
02060     if (b && (flags & JSRESOLVE_ASSIGNING) == 0) {
02061         if (!JS_ResolveStandardClass(cx, obj, id, &resolved))
02062             return JS_FALSE;
02063         if (resolved) {
02064             *objp = obj;
02065             return JS_TRUE;
02066         }
02067     }
02068     *objp = NULL;
02069     return JS_TRUE;
02070 }
02071 
02072 static JSClass sandbox_class = {
02073     "sandbox",
02074     JSCLASS_NEW_RESOLVE,
02075     JS_PropertyStub,   JS_PropertyStub,
02076     JS_PropertyStub,   JS_PropertyStub,
02077     sandbox_enumerate, (JSResolveOp)sandbox_resolve,
02078     JS_ConvertStub,    JS_FinalizeStub,
02079     JSCLASS_NO_OPTIONAL_MEMBERS
02080 };
02081 
02082 static JSBool
02083 EvalInContext(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
02084               jsval *rval)
02085 {
02086     JSString *str;
02087     JSObject *sobj;
02088     JSContext *scx;
02089     const jschar *src;
02090     size_t srclen;
02091     JSBool lazy, ok;
02092     jsval v;
02093     JSStackFrame *fp;
02094 
02095     sobj = NULL;
02096     if (!JS_ConvertArguments(cx, argc, argv, "S / o", &str, &sobj))
02097         return JS_FALSE;
02098 
02099     scx = JS_NewContext(JS_GetRuntime(cx), gStackChunkSize);
02100     if (!scx) {
02101         JS_ReportOutOfMemory(cx);
02102         return JS_FALSE;
02103     }
02104 
02105     src = JS_GetStringChars(str);
02106     srclen = JS_GetStringLength(str);
02107     lazy = JS_FALSE;
02108     if (srclen == 4 &&
02109         src[0] == 'l' && src[1] == 'a' && src[2] == 'z' && src[3] == 'y') {
02110         lazy = JS_TRUE;
02111         srclen = 0;
02112     }
02113 
02114     if (!sobj) {
02115         sobj = JS_NewObject(scx, &sandbox_class, NULL, NULL);
02116         if (!sobj || (!lazy && !JS_InitStandardClasses(scx, sobj))) {
02117             ok = JS_FALSE;
02118             goto out;
02119         }
02120         v = BOOLEAN_TO_JSVAL(v);
02121         ok = JS_SetProperty(cx, sobj, "lazy", &v);
02122         if (!ok)
02123             goto out;
02124     }
02125 
02126     if (srclen == 0) {
02127         *rval = OBJECT_TO_JSVAL(sobj);
02128         ok = JS_TRUE;
02129     } else {
02130         fp = JS_GetScriptedCaller(cx, NULL);
02131         ok = JS_EvaluateUCScript(scx, sobj, src, srclen,
02132                                  fp->script->filename,
02133                                  JS_PCToLineNumber(cx, fp->script, fp->pc),
02134                                  rval);
02135     }
02136 
02137 out:
02138     JS_DestroyContext(scx);
02139     return ok;
02140 }
02141 
02142 static JSFunctionSpec shell_functions[] = {
02143     {"version",         Version,        0,0,0},
02144     {"options",         Options,        0,0,0},
02145     {"load",            Load,           1,0,0},
02146     {"readline",        ReadLine,       0,0,0},
02147     {"print",           Print,          0,0,0},
02148     {"help",            Help,           0,0,0},
02149     {"quit",            Quit,           0,0,0},
02150     {"gc",              GC,             0,0,0},
02151 #ifdef JS_GC_ZEAL
02152     {"gczeal",          GCZeal,         1,0,0},
02153 #endif
02154     {"trap",            Trap,           3,0,0},
02155     {"untrap",          Untrap,         2,0,0},
02156     {"line2pc",         LineToPC,       0,0,0},
02157     {"pc2line",         PCToLine,       0,0,0},
02158     {"stringsAreUtf8",  StringsAreUtf8, 0,0,0},
02159     {"testUtf8",        TestUtf8,       1,0,0},
02160     {"throwError",      ThrowError,     0,0,0},
02161 #ifdef DEBUG
02162     {"dis",             Disassemble,    1,0,0},
02163     {"dissrc",          DisassWithSrc,  1,0,0},
02164     {"notes",           Notes,          1,0,0},
02165     {"tracing",         Tracing,        0,0,0},
02166     {"stats",           DumpStats,      1,0,0},
02167 #endif
02168 #ifdef TEST_EXPORT
02169     {"xport",           DoExport,       2,0,0},
02170 #endif
02171 #ifdef TEST_CVTARGS
02172     {"cvtargs",         ConvertArgs,    0,0,12},
02173 #endif
02174     {"build",           BuildDate,      0,0,0},
02175     {"clear",           Clear,          0,0,0},
02176     {"intern",          Intern,         1,0,0},
02177     {"clone",           Clone,          1,0,0},
02178     {"seal",            Seal,           1,0,1},
02179     {"getpda",          GetPDA,         1,0,0},
02180     {"getslx",          GetSLX,         1,0,0},
02181     {"toint32",         ToInt32,        1,0,0},
02182     {"evalcx",          EvalInContext,  1,0,0},
02183     {NULL,NULL,0,0,0}
02184 };
02185 
02186 /* NOTE: These must be kept in sync with the above. */
02187 
02188 static char *shell_help_messages[] = {
02189     "version([number])      Get or set JavaScript version number",
02190     "options([option ...])  Get or toggle JavaScript options",
02191     "load(['foo.js' ...])   Load files named by string arguments",
02192     "readline()             Read a single line from stdin",
02193     "print([exp ...])       Evaluate and print expressions",
02194     "help([name ...])       Display usage and help messages",
02195     "quit()                 Quit the shell",
02196     "gc()                   Run the garbage collector",
02197 #ifdef JS_GC_ZEAL
02198     "gczeal(level)          How zealous the garbage collector should be",
02199 #endif
02200     "trap([fun, [pc,]] exp) Trap bytecode execution",
02201     "untrap(fun[, pc])      Remove a trap",
02202     "line2pc([fun,] line)   Map line number to PC",
02203     "pc2line(fun[, pc])     Map PC to line number",
02204     "stringsAreUTF8()       Check if strings are UTF-8 encoded",
02205     "testUTF8(mode)         Perform UTF-8 tests (modes are 1 to 4)",
02206     "throwError()           Throw an error from JS_ReportError",
02207 #ifdef DEBUG
02208     "dis([fun])             Disassemble functions into bytecodes",
02209     "dissrc([fun])          Disassemble functions with source lines",
02210     "notes([fun])           Show source notes for functions",
02211     "tracing([toggle])      Turn tracing on or off",
02212     "stats([string ...])    Dump 'arena', 'atom', 'global' stats",
02213 #endif
02214 #ifdef TEST_EXPORT
02215     "xport(obj, id)         Export identified property from object",
02216 #endif
02217 #ifdef TEST_CVTARGS
02218     "cvtargs(b, c, ...)     Test JS_ConvertArguments",
02219 #endif
02220     "build()                Show build date and time",
02221     "clear([obj])           Clear properties of object",
02222     "intern(str)            Internalize str in the atom table",
02223     "clone(fun[, scope])    Clone function object",
02224     "seal(obj[, deep])      Seal object, or object graph if deep",
02225     "getpda(obj)            Get the property descriptors for obj",
02226     "getslx(obj)            Get script line extent",
02227     "toint32(n)             Testing hook for JS_ValueToInt32",
02228     "evalcx(s[, o])         Evaluate s in optional sandbox object o\n"
02229     "    if (s == '' && !o) return new o with eager standard classes\n"
02230     "    if (s == 'lazy' && !o) return new o with lazy standard classes",
02231     0
02232 };
02233 
02234 static void
02235 ShowHelpHeader(void)
02236 {
02237     fprintf(gOutFile, "%-14s %-22s %s\n", "Command", "Usage", "Description");
02238     fprintf(gOutFile, "%-14s %-22s %s\n", "=======", "=====", "===========");
02239 }
02240 
02241 static void
02242 ShowHelpForCommand(uintN n)
02243 {
02244     fprintf(gOutFile, "%-14.14s %s\n", shell_functions[n].name, shell_help_messages[n]);
02245 }
02246 
02247 static JSObject *
02248 split_setup(JSContext *cx)
02249 {
02250     JSObject *outer, *inner, *arguments;
02251 
02252     outer = split_create_outer(cx);
02253     if (!outer)
02254         return NULL;
02255     JS_SetGlobalObject(cx, outer);
02256 
02257     inner = split_create_inner(cx, outer);
02258     if (!inner)
02259         return NULL;
02260 
02261     if (!JS_DefineFunctions(cx, inner, shell_functions))
02262         return NULL;
02263     JS_ClearScope(cx, outer);
02264 
02265     /* Create a dummy arguments object. */
02266     arguments = JS_NewArrayObject(cx, 0, NULL);
02267     if (!arguments ||
02268         !JS_DefineProperty(cx, inner, "arguments", OBJECT_TO_JSVAL(arguments),
02269                            NULL, NULL, 0)) {
02270         return NULL;
02271     }
02272 
02273 #ifndef LAZY_STANDARD_CLASSES
02274     if (!JS_InitStandardClasses(cx, inner))
02275         return NULL;
02276 #endif
02277 
02278     return inner;
02279 }
02280 
02281 static JSBool
02282 Help(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
02283 {
02284     uintN i, j;
02285     int did_header, did_something;
02286     JSType type;
02287     JSFunction *fun;
02288     JSString *str;
02289     const char *bytes;
02290 
02291     fprintf(gOutFile, "%s\n", JS_GetImplementationVersion());
02292     if (argc == 0) {
02293         ShowHelpHeader();
02294         for (i = 0; shell_functions[i].name; i++)
02295             ShowHelpForCommand(i);
02296     } else {
02297         did_header = 0;
02298         for (i = 0; i < argc; i++) {
02299             did_something = 0;
02300             type = JS_TypeOfValue(cx, argv[i]);
02301             if (type == JSTYPE_FUNCTION) {
02302                 fun = JS_ValueToFunction(cx, argv[i]);
02303                 str = fun->atom ? ATOM_TO_STRING(fun->atom) : NULL;
02304             } else if (type == JSTYPE_STRING) {
02305                 str = JSVAL_TO_STRING(argv[i]);
02306             } else {
02307                 str = NULL;
02308             }
02309             if (str) {
02310                 bytes = JS_GetStringBytes(str);
02311                 for (j = 0; shell_functions[j].name; j++) {
02312                     if (!strcmp(bytes, shell_functions[j].name)) {
02313                         if (!did_header) {
02314                             did_header = 1;
02315                             ShowHelpHeader();
02316                         }
02317                         did_something = 1;
02318                         ShowHelpForCommand(j);
02319                         break;
02320                     }
02321                 }
02322             }
02323             if (!did_something) {
02324                 str = JS_ValueToString(cx, argv[i]);
02325                 if (!str)
02326                     return JS_FALSE;
02327                 fprintf(gErrFile, "Sorry, no help for %s\n",
02328                         JS_GetStringBytes(str));
02329             }
02330         }
02331     }
02332     return JS_TRUE;
02333 }
02334 
02335 /*
02336  * Define a JS object called "it".  Give it class operations that printf why
02337  * they're being called for tutorial purposes.
02338  */
02339 enum its_tinyid {
02340     ITS_COLOR, ITS_HEIGHT, ITS_WIDTH, ITS_FUNNY, ITS_ARRAY, ITS_RDONLY
02341 };
02342 
02343 static JSPropertySpec its_props[] = {
02344     {"color",           ITS_COLOR,      JSPROP_ENUMERATE,       NULL, NULL},
02345     {"height",          ITS_HEIGHT,     JSPROP_ENUMERATE,       NULL, NULL},
02346     {"width",           ITS_WIDTH,      JSPROP_ENUMERATE,       NULL, NULL},
02347     {"funny",           ITS_FUNNY,      JSPROP_ENUMERATE,       NULL, NULL},
02348     {"array",           ITS_ARRAY,      JSPROP_ENUMERATE,       NULL, NULL},
02349     {"rdonly",          ITS_RDONLY,     JSPROP_READONLY,        NULL, NULL},
02350     {NULL,0,0,NULL,NULL}
02351 };
02352 
02353 static JSBool
02354 its_item(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
02355 {
02356     *rval = OBJECT_TO_JSVAL(obj);
02357     if (argc != 0)
02358         JS_SetCallReturnValue2(cx, argv[0]);
02359     return JS_TRUE;
02360 }
02361 
02362 static JSBool
02363 its_bindMethod(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
02364                jsval *rval)
02365 {
02366     char *name;
02367     JSObject *method;
02368 
02369     if (!JS_ConvertArguments(cx, argc, argv, "so", &name, &method))
02370         return JS_FALSE;
02371 
02372     *rval = OBJECT_TO_JSVAL(method);
02373 
02374     if (JS_TypeOfValue(cx, *rval) != JSTYPE_FUNCTION) {
02375         JSString *valstr = JS_ValueToString(cx, *rval);
02376         if (valstr) {
02377             JS_ReportError(cx, "can't bind method %s to non-callable object %s",
02378                            name, JS_GetStringBytes(valstr));
02379         }
02380         return JS_FALSE;
02381     }
02382 
02383     if (!JS_DefineProperty(cx, obj, name, *rval, NULL, NULL, JSPROP_ENUMERATE))
02384         return JS_FALSE;
02385 
02386     return JS_SetParent(cx, method, obj);
02387 }
02388 
02389 static JSFunctionSpec its_methods[] = {
02390     {"item",            its_item,       0,0,0},
02391     {"bindMethod",      its_bindMethod, 2,0,0},
02392     {NULL,NULL,0,0,0}
02393 };
02394 
02395 #ifdef JSD_LOWLEVEL_SOURCE
02396 /*
02397  * This facilitates sending source to JSD (the debugger system) in the shell
02398  * where the source is loaded using the JSFILE hack in jsscan. The function
02399  * below is used as a callback for the jsdbgapi JS_SetSourceHandler hook.
02400  * A more normal embedding (e.g. mozilla) loads source itself and can send
02401  * source directly to JSD without using this hook scheme.
02402  */
02403 static void
02404 SendSourceToJSDebugger(const char *filename, uintN lineno,
02405                        jschar *str, size_t length,
02406                        void **listenerTSData, JSDContext* jsdc)
02407 {
02408     JSDSourceText *jsdsrc = (JSDSourceText *) *listenerTSData;
02409 
02410     if (!jsdsrc) {
02411         if (!filename)
02412             filename = "typein";
02413         if (1 == lineno) {
02414             jsdsrc = JSD_NewSourceText(jsdc, filename);
02415         } else {
02416             jsdsrc = JSD_FindSourceForURL(jsdc, filename);
02417             if (jsdsrc && JSD_SOURCE_PARTIAL !=
02418                 JSD_GetSourceStatus(jsdc, jsdsrc)) {
02419                 jsdsrc = NULL;
02420             }
02421         }
02422     }
02423     if (jsdsrc) {
02424         jsdsrc = JSD_AppendUCSourceText(jsdc,jsdsrc, str, length,
02425                                         JSD_SOURCE_PARTIAL);
02426     }
02427     *listenerTSData = jsdsrc;
02428 }
02429 #endif /* JSD_LOWLEVEL_SOURCE */
02430 
02431 static JSBool its_noisy;    /* whether to be noisy when finalizing it */
02432 
02433 static JSBool
02434 its_addProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
02435 {
02436     if (its_noisy) {
02437         fprintf(gOutFile, "adding its property %s,",
02438                JS_GetStringBytes(JS_ValueToString(cx, id)));
02439         fprintf(gOutFile, " initial value %s\n",
02440                JS_GetStringBytes(JS_ValueToString(cx, *vp)));
02441     }
02442     return JS_TRUE;
02443 }
02444 
02445 static JSBool
02446 its_delProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
02447 {
02448     if (its_noisy) {
02449         fprintf(gOutFile, "deleting its property %s,",
02450                JS_GetStringBytes(JS_ValueToString(cx, id)));
02451         fprintf(gOutFile, " current value %s\n",
02452                JS_GetStringBytes(JS_ValueToString(cx, *vp)));
02453     }
02454     return JS_TRUE;
02455 }
02456 
02457 static JSBool
02458 its_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
02459 {
02460     if (its_noisy) {
02461         fprintf(gOutFile, "getting its property %s,",
02462                JS_GetStringBytes(JS_ValueToString(cx, id)));
02463         fprintf(gOutFile, " current value %s\n",
02464                JS_GetStringBytes(JS_ValueToString(cx, *vp)));
02465     }
02466     return JS_TRUE;
02467 }
02468 
02469 static JSBool
02470 its_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
02471 {
02472     if (its_noisy) {
02473         fprintf(gOutFile, "setting its property %s,",
02474                JS_GetStringBytes(JS_ValueToString(cx, id)));
02475         fprintf(gOutFile, " new value %s\n",
02476                JS_GetStringBytes(JS_ValueToString(cx, *vp)));
02477     }
02478     if (JSVAL_IS_STRING(id) &&
02479         !strcmp(JS_GetStringBytes(JSVAL_TO_STRING(id)), "noisy")) {
02480         return JS_ValueToBoolean(cx, *vp, &its_noisy);
02481     }
02482     return JS_TRUE;
02483 }
02484 
02485 static JSBool
02486 its_enumerate(JSContext *cx, JSObject *obj)
02487 {
02488     if (its_noisy)
02489         fprintf(gOutFile, "enumerate its properties\n");
02490     return JS_TRUE;
02491 }
02492 
02493 static JSBool
02494 its_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
02495             JSObject **objp)
02496 {
02497     if (its_noisy) {
02498         fprintf(gOutFile, "resolving its property %s, flags {%s,%s,%s}\n",
02499                JS_GetStringBytes(JS_ValueToString(cx, id)),
02500                (flags & JSRESOLVE_QUALIFIED) ? "qualified" : "",
02501                (flags & JSRESOLVE_ASSIGNING) ? "assigning" : "",
02502                (flags & JSRESOLVE_DETECTING) ? "detecting" : "");
02503     }
02504     return JS_TRUE;
02505 }
02506 
02507 static JSBool
02508 its_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
02509 {
02510     if (its_noisy)
02511         fprintf(gOutFile, "converting it to %s type\n", JS_GetTypeName(cx, type));
02512     return JS_TRUE;
02513 }
02514 
02515 static void
02516 its_finalize(JSContext *cx, JSObject *obj)
02517 {
02518     if (its_noisy)
02519         fprintf(gOutFile, "finalizing it\n");
02520 }
02521 
02522 static JSClass its_class = {
02523     "It", JSCLASS_NEW_RESOLVE,
02524     its_addProperty,  its_delProperty,  its_getProperty,  its_setProperty,
02525     its_enumerate,    (JSResolveOp)its_resolve,
02526     its_convert,      its_finalize,
02527     JSCLASS_NO_OPTIONAL_MEMBERS
02528 };
02529 
02530 JSErrorFormatString jsShell_ErrorFormatString[JSErr_Limit] = {
02531 #define MSG_DEF(name, number, count, exception, format) \
02532     { format, count, JSEXN_ERR } ,
02533 #include "jsshell.msg"
02534 #undef MSG_DEF
02535 };
02536 
02537 static const JSErrorFormatString *
02538 my_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber)
02539 {
02540     if ((errorNumber > 0) && (errorNumber < JSShellErr_Limit))
02541         return &jsShell_ErrorFormatString[errorNumber];
02542     return NULL;
02543 }
02544 
02545 static void
02546 my_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
02547 {
02548     int i, j, k, n;
02549     char *prefix, *tmp;
02550     const char *ctmp;
02551 
02552     if (!report) {
02553         fprintf(gErrFile, "%s\n", message);
02554         return;
02555     }
02556 
02557     /* Conditionally ignore reported warnings. */
02558     if (JSREPORT_IS_WARNING(report->flags) && !reportWarnings)
02559         return;
02560 
02561     prefix = NULL;
02562     if (report->filename)
02563         prefix = JS_smprintf("%s:", report->filename);
02564     if (report->lineno) {
02565         tmp = prefix;
02566         prefix = JS_smprintf("%s%u: ", tmp ? tmp : "", report->lineno);
02567         JS_free(cx, tmp);
02568     }
02569     if (JSREPORT_IS_WARNING(report->flags)) {
02570         tmp = prefix;
02571         prefix = JS_smprintf("%s%swarning: ",
02572                              tmp ? tmp : "",
02573                              JSREPORT_IS_STRICT(report->flags) ? "strict " : "");
02574         JS_free(cx, tmp);
02575     }
02576 
02577     /* embedded newlines -- argh! */
02578     while ((ctmp = strchr(message, '\n')) != 0) {
02579         ctmp++;
02580         if (prefix)
02581             fputs(prefix, gErrFile);
02582         fwrite(message, 1, ctmp - message, gErrFile);
02583         message = ctmp;
02584     }
02585 
02586     /* If there were no filename or lineno, the prefix might be empty */
02587     if (prefix)
02588         fputs(prefix, gErrFile);
02589     fputs(message, gErrFile);
02590 
02591     if (!report->linebuf) {
02592         fputc('\n', gErrFile);
02593         goto out;
02594     }
02595 
02596     /* report->linebuf usually ends with a newline. */
02597     n = strlen(report->linebuf);
02598     fprintf(gErrFile, ":\n%s%s%s%s",
02599             prefix,
02600             report->linebuf,
02601             (n > 0 && report->linebuf[n-1] == '\n') ? "" : "\n",
02602             prefix);
02603     n = PTRDIFF(report->tokenptr, report->linebuf, char);
02604     for (i = j = 0; i < n; i++) {
02605         if (report->linebuf[i] == '\t') {
02606             for (k = (j + 8) & ~7; j < k; j++) {
02607                 fputc('.', gErrFile);
02608             }
02609             continue;
02610         }
02611         fputc('.', gErrFile);
02612         j++;
02613     }
02614     fputs("^\n", gErrFile);
02615  out:
02616     if (!JSREPORT_IS_WARNING(report->flags)) {
02617         if (report->errorNumber == JSMSG_OUT_OF_MEMORY) {
02618             gExitCode = EXITCODE_OUT_OF_MEMORY;
02619         } else {
02620             gExitCode = EXITCODE_RUNTIME_ERROR;
02621         }
02622     }
02623     JS_free(cx, prefix);
02624 }
02625 
02626 #if defined(SHELL_HACK) && defined(DEBUG) && defined(XP_UNIX)
02627 static JSBool
02628 Exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
02629 {
02630     JSFunction *fun;
02631     const char *name, **nargv;
02632     uintN i, nargc;
02633     JSString *str;
02634     pid_t pid;
02635     int status;
02636 
02637     fun = JS_ValueToFunction(cx, argv[-2]);
02638     if (!fun)
02639         return JS_FALSE;
02640     if (!fun->atom)
02641         return JS_TRUE;
02642     name = JS_GetStringBytes(ATOM_TO_STRING(fun->atom));
02643     nargc = 1 + argc;
02644     nargv = JS_malloc(cx, (nargc + 1) * sizeof(char *));
02645     if (!nargv)
02646         return JS_FALSE;
02647     nargv[0] = name;
02648     for (i = 1; i < nargc; i++) {
02649         str = JS_ValueToString(cx, argv[i-1]);
02650         if (!str) {
02651             JS_free(cx, nargv);
02652             return JS_FALSE;
02653         }
02654         nargv[i] = JS_GetStringBytes(str);
02655     }
02656     nargv[nargc] = 0;
02657     pid = fork();
02658     switch (pid) {
02659       case -1:
02660         perror("js");
02661         break;
02662       case 0:
02663         (void) execvp(name, (char **)nargv);
02664         perror("js");
02665         exit(127);
02666       default:
02667         while (waitpid(pid, &status, 0) < 0 && errno == EINTR)
02668             continue;
02669         break;
02670     }
02671     JS_free(cx, nargv);
02672     return JS_TRUE;
02673 }
02674 #endif
02675 
02676 static JSBool
02677 global_enumerate(JSContext *cx, JSObject *obj)
02678 {
02679 #ifdef LAZY_STANDARD_CLASSES
02680     return JS_EnumerateStandardClasses(cx, obj);
02681 #else
02682     return JS_TRUE;
02683 #endif
02684 }
02685 
02686 static JSBool
02687 global_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
02688                JSObject **objp)
02689 {
02690 #ifdef LAZY_STANDARD_CLASSES
02691     JSBool resolved;
02692 
02693     if (!JS_ResolveStandardClass(cx, obj, id, &resolved))
02694         return JS_FALSE;
02695     if (resolved) {
02696         *objp = obj;
02697         return JS_TRUE;
02698     }
02699 #endif
02700 
02701 #if defined(SHELL_HACK) && defined(DEBUG) && defined(XP_UNIX)
02702     if ((flags & (JSRESOLVE_QUALIFIED | JSRESOLVE_ASSIGNING)) == 0) {
02703         /*
02704          * Do this expensive hack only for unoptimized Unix builds, which are
02705          * not used for benchmarking.
02706          */
02707         char *path, *comp, *full;
02708         const char *name;
02709         JSBool ok, found;
02710         JSFunction *fun;
02711 
02712         if (!JSVAL_IS_STRING(id))
02713             return JS_TRUE;
02714         path = getenv("PATH");
02715         if (!path)
02716             return JS_TRUE;
02717         path = JS_strdup(cx, path);
02718         if (!path)
02719             return JS_FALSE;
02720         name = JS_GetStringBytes(JSVAL_TO_STRING(id));
02721         ok = JS_TRUE;
02722         for (comp = strtok(path, ":"); comp; comp = strtok(NULL, ":")) {
02723             if (*comp != '\0') {
02724                 full = JS_smprintf("%s/%s", comp, name);
02725                 if (!full) {
02726                     JS_ReportOutOfMemory(cx);
02727                     ok = JS_FALSE;
02728                     break;
02729                 }
02730             } else {
02731                 full = (char *)name;
02732             }
02733             found = (access(full, X_OK) == 0);
02734             if (*comp != '\0')
02735                 free(full);
02736             if (found) {
02737                 fun = JS_DefineFunction(cx, obj, name, Exec, 0,
02738                                         JSPROP_ENUMERATE);
02739                 ok = (fun != NULL);
02740                 if (ok)
02741                     *objp = obj;
02742                 break;
02743             }
02744         }
02745         JS_free(cx, path);
02746         return ok;
02747     }
02748 #else
02749     return JS_TRUE;
02750 #endif
02751 }
02752 
02753 JSClass global_class = {
02754     "global", JSCLASS_NEW_RESOLVE | JSCLASS_GLOBAL_FLAGS,
02755     JS_PropertyStub,  JS_PropertyStub,
02756     JS_PropertyStub,  JS_PropertyStub,
02757     global_enumerate, (JSResolveOp) global_resolve,
02758     JS_ConvertStub,   JS_FinalizeStub,
02759     JSCLASS_NO_OPTIONAL_MEMBERS
02760 };
02761 
02762 static JSBool
02763 env_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
02764 {
02765 /* XXX porting may be easy, but these don't seem to supply setenv by default */
02766 #if !defined XP_BEOS && !defined XP_OS2 && !defined SOLARIS
02767     JSString *idstr, *valstr;
02768     const char *name, *value;
02769     int rv;
02770 
02771     idstr = JS_ValueToString(cx, id);
02772     valstr = JS_ValueToString(cx, *vp);
02773     if (!idstr || !valstr)
02774         return JS_FALSE;
02775     name = JS_GetStringBytes(idstr);
02776     value = JS_GetStringBytes(valstr);
02777 #if defined XP_WIN || defined HPUX || defined OSF1 || defined IRIX
02778     {
02779         char *waste = JS_smprintf("%s=%s", name, value);
02780         if (!waste) {
02781             JS_ReportOutOfMemory(cx);
02782             return JS_FALSE;
02783         }
02784         rv = putenv(waste);
02785 #ifdef XP_WIN
02786         /*
02787          * HPUX9 at least still has the bad old non-copying putenv.
02788          *
02789          * Per mail from <s.shanmuganathan@digital.com>, OSF1 also has a putenv
02790          * that will crash if you pass it an auto char array (so it must place
02791          * its argument directly in the char *environ[] array).
02792          */
02793         free(waste);
02794 #endif
02795     }
02796 #else
02797     rv = setenv(name, value, 1);
02798 #endif
02799     if (rv < 0) {
02800         JS_ReportError(cx, "can't set envariable %s to %s", name, value);
02801         return JS_FALSE;
02802     }
02803     *vp = STRING_TO_JSVAL(valstr);
02804 #endif /* !defined XP_BEOS && !defined XP_OS2 && !defined SOLARIS */
02805     return JS_TRUE;
02806 }
02807 
02808 static JSBool
02809 env_enumerate(JSContext *cx, JSObject *obj)
02810 {
02811     static JSBool reflected;
02812     char **evp, *name, *value;
02813     JSString *valstr;
02814     JSBool ok;
02815 
02816     if (reflected)
02817         return JS_TRUE;
02818 
02819     for (evp = (char **)JS_GetPrivate(cx, obj); (name = *evp) != NULL; evp++) {
02820         value = strchr(name, '=');
02821         if (!value)
02822             continue;
02823         *value++ = '\0';
02824         valstr = JS_NewStringCopyZ(cx, value);
02825         if (!valstr) {
02826             ok = JS_FALSE;
02827         } else {
02828             ok = JS_DefineProperty(cx, obj, name, STRING_TO_JSVAL(valstr),
02829                                    NULL, NULL, JSPROP_ENUMERATE);
02830         }
02831         value[-1] = '=';
02832         if (!ok)
02833             return JS_FALSE;
02834     }
02835 
02836     reflected = JS_TRUE;
02837     return JS_TRUE;
02838 }
02839 
02840 static JSBool
02841 env_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
02842             JSObject **objp)
02843 {
02844     JSString *idstr, *valstr;
02845     const char *name, *value;
02846 
02847     if (flags & JSRESOLVE_ASSIGNING)
02848         return JS_TRUE;
02849 
02850     idstr = JS_ValueToString(cx, id);
02851     if (!idstr)
02852         return JS_FALSE;
02853     name = JS_GetStringBytes(idstr);
02854     value = getenv(name);
02855     if (value) {
02856         valstr = JS_NewStringCopyZ(cx, value);
02857         if (!valstr)
02858             return JS_FALSE;
02859         if (!JS_DefineProperty(cx, obj, name, STRING_TO_JSVAL(valstr),
02860                                NULL, NULL, JSPROP_ENUMERATE)) {
02861             return JS_FALSE;
02862         }
02863         *objp = obj;
02864     }
02865     return JS_TRUE;
02866 }
02867 
02868 static JSClass env_class = {
02869     "environment", JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE,
02870     JS_PropertyStub,  JS_PropertyStub,
02871     JS_PropertyStub,  env_setProperty,
02872     env_enumerate, (JSResolveOp) env_resolve,
02873     JS_ConvertStub,   JS_FinalizeStub,
02874     JSCLASS_NO_OPTIONAL_MEMBERS
02875 };
02876 
02877 #ifdef NARCISSUS
02878 
02879 static JSBool
02880 defineProperty(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
02881                jsval *rval)
02882 {
02883     JSString *str;
02884     jsval value;
02885     JSBool dontDelete, readOnly, dontEnum;
02886     const jschar *chars;
02887     size_t length;
02888     uintN attrs;
02889 
02890     dontDelete = readOnly = dontEnum = JS_FALSE;
02891     if (!JS_ConvertArguments(cx, argc, argv, "Sv/bbb",
02892                              &str, &value, &dontDelete, &readOnly, &dontEnum)) {
02893         return JS_FALSE;
02894     }
02895     chars = JS_GetStringChars(str);
02896     length = JS_GetStringLength(str);
02897     attrs = dontEnum ? 0 : JSPROP_ENUMERATE;
02898     if (dontDelete)
02899         attrs |= JSPROP_PERMANENT;
02900     if (readOnly)
02901         attrs |= JSPROP_READONLY;
02902     return JS_DefineUCProperty(cx, obj, chars, length, value, NULL, NULL,
02903                                attrs);
02904 }
02905 
02906 static JSBool
02907 Evaluate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
02908 {
02909     /* function evaluate(source, filename, lineno) { ... } */
02910     JSString *source;
02911     const char *filename = "";
02912     jsuint lineno = 0;
02913     uint32 oldopts;
02914     JSBool ok;
02915 
02916     if (argc == 0) {
02917         *rval = JSVAL_VOID;
02918         return JS_TRUE;
02919     }
02920 
02921     if (!JS_ConvertArguments(cx, argc, argv, "S/su",
02922                              &source, &filename, &lineno)) {
02923         return JS_FALSE;
02924     }
02925 
02926     oldopts = JS_GetOptions(cx);
02927     JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO);
02928     ok = JS_EvaluateUCScript(cx, obj, JS_GetStringChars(source),
02929                              JS_GetStringLength(source), filename,
02930                              lineno, rval);
02931     JS_SetOptions(cx, oldopts);
02932 
02933     return ok;
02934 }
02935 
02936 #include <fcntl.h>
02937 #include <sys/stat.h>
02938 
02939 /*
02940  * Returns a JS_malloc'd string (that the caller needs to JS_free)
02941  * containing the directory (non-leaf) part of |from| prepended to |leaf|.
02942  * If |from| is empty or a leaf, MakeAbsolutePathname returns a copy of leaf.
02943  * Returns NULL to indicate an error.
02944  */
02945 static char *
02946 MakeAbsolutePathname(JSContext *cx, const char *from, const char *leaf)
02947 {
02948     size_t dirlen;
02949     char *dir;
02950     const char *slash = NULL, *cp;
02951 
02952     cp = from;
02953     while (*cp) {
02954         if (*cp == '/'
02955 #ifdef XP_WIN
02956             || *cp == '\\'
02957 #endif
02958            ) {
02959             slash = cp;
02960         }
02961 
02962         ++cp;
02963     }
02964 
02965     if (!slash) {
02966         /* We were given a leaf or |from| was empty. */
02967         return JS_strdup(cx, leaf);
02968     }
02969 
02970     /* Else, we were given a real pathname, return that + the leaf. */
02971     dirlen = slash - from + 1;
02972     dir = JS_malloc(cx, dirlen + strlen(leaf) + 1);
02973     if (!dir)
02974         return NULL;
02975 
02976     strncpy(dir, from, dirlen);
02977     strcpy(dir + dirlen, leaf); /* Note: we can't use strcat here. */
02978 
02979     return dir;
02980 }
02981 
02982 static JSBool
02983 snarf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
02984 {
02985     JSString *str;
02986     const char *filename;
02987     char *pathname;
02988     JSStackFrame *fp;
02989     JSBool ok;
02990     off_t cc, len;
02991     char *buf;
02992     FILE *file;
02993 
02994     str = JS_ValueToString(cx, argv[0]);
02995     if (!str)
02996         return JS_FALSE;
02997     filename = JS_GetStringBytes(str);
02998 
02999     /* Get the currently executing script's name. */
03000     fp = JS_GetScriptedCaller(cx, NULL);
03001     JS_ASSERT(fp && fp->script->filename);
03002     pathname = MakeAbsolutePathname(cx, fp->script->filename, filename);
03003     if (!pathname)
03004         return JS_FALSE;
03005 
03006     ok = JS_FALSE;
03007     len = 0;
03008     buf = NULL;
03009     file = fopen(pathname, "rb");
03010     if (!file) {
03011         JS_ReportError(cx, "can't open %s: %s", pathname, strerror(errno));
03012     } else {
03013         if (fseek(file, 0, SEEK_END) == EOF) {
03014             JS_ReportError(cx, "can't seek end of %s", pathname);
03015         } else {
03016             len = ftell(file);
03017             if (fseek(file, 0, SEEK_SET) == EOF) {
03018                 JS_ReportError(cx, "can't seek start of %s", pathname);
03019             } else {
03020                 buf = JS_malloc(cx, len + 1);
03021                 if (buf) {
03022                     cc = fread(buf, 1, len, file);
03023                     if (cc != len) {
03024                         JS_free(cx, buf);
03025                         JS_ReportError(cx, "can't read %s: %s", pathname,
03026                                        (cc < 0) ? strerror(errno)
03027                                                 : "short read");
03028                     } else {
03029                         len = (size_t)cc;
03030                         ok = JS_TRUE;
03031                     }
03032                 }
03033             }
03034         }
03035         fclose(file);
03036     }
03037     JS_free(cx, pathname);
03038     if (!ok) {
03039         JS_free(cx, buf);
03040         return ok;
03041     }
03042 
03043     buf[len] = '\0';
03044     str = JS_NewString(cx, buf, len);
03045     if (!str) {
03046         JS_free(cx, buf);
03047         return JS_FALSE;
03048     }
03049     *rval = STRING_TO_JSVAL(str);
03050     return JS_TRUE;
03051 }
03052 
03053 #endif /* NARCISSUS */
03054 
03055 int
03056 main(int argc, char **argv, char **envp)
03057 {
03058     int stackDummy;
03059     JSRuntime *rt;
03060     JSContext *cx;
03061     JSObject *glob, *it, *envobj;
03062     int result;
03063 #ifdef LIVECONNECT
03064     JavaVM *java_vm = NULL;
03065 #endif
03066 #ifdef JSDEBUGGER_JAVA_UI
03067     JNIEnv *java_env;
03068 #endif
03069 
03070     gStackBase = (jsuword)&stackDummy;
03071 
03072     setlocale(LC_ALL, "");
03073 
03074 #ifdef XP_OS2
03075    /* these streams are normally line buffered on OS/2 and need a \n, *
03076     * so we need to unbuffer then to get a reasonable prompt          */
03077     setbuf(stdout,0);
03078     setbuf(stderr,0);
03079 #endif
03080 
03081     gErrFile = stderr;
03082     gOutFile = stdout;
03083 
03084     argc--;
03085     argv++;
03086 
03087     rt = JS_NewRuntime(64L * 1024L * 1024L);
03088     if (!rt)
03089         return 1;
03090 
03091     cx = JS_NewContext(rt, gStackChunkSize);
03092     if (!cx)
03093         return 1;
03094     JS_SetErrorReporter(cx, my_ErrorReporter);
03095 
03096 #ifdef JS_THREADSAFE
03097     JS_BeginRequest(cx);
03098 #endif
03099 
03100     glob = JS_NewObject(cx, &global_class, NULL, NULL);
03101     if (!glob)
03102         return 1;
03103 #ifdef LAZY_STANDARD_CLASSES
03104     JS_SetGlobalObject(cx, glob);
03105 #else
03106     if (!JS_InitStandardClasses(cx, glob))
03107         return 1;
03108 #endif
03109     if (!JS_DefineFunctions(cx, glob, shell_functions))
03110         return 1;
03111 
03112     it = JS_DefineObject(cx, glob, "it", &its_class, NULL, 0);
03113     if (!it)
03114         return 1;
03115     if (!JS_DefineProperties(cx, it, its_props))
03116         return 1;
03117     if (!JS_DefineFunctions(cx, it, its_methods))
03118         return 1;
03119 
03120 #ifdef PERLCONNECT
03121     if (!JS_InitPerlClass(cx, glob))
03122         return 1;
03123 #endif
03124 
03125 #ifdef JSDEBUGGER
03126     /*
03127     * XXX A command line option to enable debugging (or not) would be good
03128     */
03129     _jsdc = JSD_DebuggerOnForUser(rt, NULL, NULL);
03130     if (!_jsdc)
03131         return 1;
03132     JSD_JSContextInUse(_jsdc, cx);
03133 #ifdef JSD_LOWLEVEL_SOURCE
03134     JS_SetSourceHandler(rt, SendSourceToJSDebugger, _jsdc);
03135 #endif /* JSD_LOWLEVEL_SOURCE */
03136 #ifdef JSDEBUGGER_JAVA_UI
03137     _jsdjc = JSDJ_CreateContext();
03138     if (! _jsdjc)
03139         return 1;
03140     JSDJ_SetJSDContext(_jsdjc, _jsdc);
03141     java_env = JSDJ_CreateJavaVMAndStartDebugger(_jsdjc);
03142 #ifdef LIVECONNECT
03143     if (java_env)
03144         (*java_env)->GetJavaVM(java_env, &java_vm);
03145 #endif
03146     /*
03147     * XXX This would be the place to wait for the debugger to start.
03148     * Waiting would be nice in general, but especially when a js file
03149     * is passed on the cmd line.
03150     */
03151 #endif /* JSDEBUGGER_JAVA_UI */
03152 #ifdef JSDEBUGGER_C_UI
03153     JSDB_InitDebugger(rt, _jsdc, 0);
03154 #endif /* JSDEBUGGER_C_UI */
03155 #endif /* JSDEBUGGER */
03156 
03157 #ifdef LIVECONNECT
03158     if (!JSJ_SimpleInit(cx, glob, java_vm, getenv("CLASSPATH")))
03159         return 1;
03160 #endif
03161 
03162     envobj = JS_DefineObject(cx, glob, "environment", &env_class, NULL, 0);
03163     if (!envobj || !JS_SetPrivate(cx, envobj, envp))
03164         return 1;
03165 
03166 #ifdef NARCISSUS
03167     {
03168         jsval v;
03169         static const char Object_prototype[] = "Object.prototype";
03170 
03171         if (!JS_DefineFunction(cx, glob, "snarf", snarf, 1, 0))
03172             return 1;
03173         if (!JS_DefineFunction(cx, glob, "evaluate", Evaluate, 3, 0))
03174             return 1;
03175 
03176         if (!JS_EvaluateScript(cx, glob,
03177                                Object_prototype, sizeof Object_prototype - 1,
03178                                NULL, 0, &v)) {
03179             return 1;
03180         }
03181         if (!JS_DefineFunction(cx, JSVAL_TO_OBJECT(v), "__defineProperty__",
03182                                defineProperty, 5, 0)) {
03183             return 1;
03184         }
03185     }
03186 #endif
03187 
03188     result = ProcessArgs(cx, glob, argv, argc);
03189 
03190 #ifdef JSDEBUGGER
03191     if (_jsdc)
03192         JSD_DebuggerOff(_jsdc);
03193 #endif  /* JSDEBUGGER */
03194 
03195 #ifdef JS_THREADSAFE
03196     JS_EndRequest(cx);
03197 #endif
03198 
03199     JS_DestroyContext(cx);
03200     JS_DestroyRuntime(rt);
03201     JS_ShutDown();
03202     return result;
03203 }