LCOV - app.info - js/src/shell/js.cpp

LCOV - code coverage report
| Current view: | directory - js/src/shell - js.cpp (source / functions) |  | Found | Hit | Coverage |
| Test: | app.info | Lines: | 2206 | 807 | 36.6 %
| Date: | 2012-07-07 | Functions: | 142 | 71 | 50.0 %

           1                 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
           2                 :  * vim: set ts=8 sw=4 et tw=99:
           3                 :  *
           4                 :  * ***** BEGIN LICENSE BLOCK *****
           5                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
           6                 :  *
           7                 :  * The contents of this file are subject to the Mozilla Public License Version
           8                 :  * 1.1 (the "License"); you may not use this file except in compliance with
           9                 :  * the License. You may obtain a copy of the License at
          10                 :  * http://www.mozilla.org/MPL/
          11                 :  *
          12                 :  * Software distributed under the License is distributed on an "AS IS" basis,
          13                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
          14                 :  * for the specific language governing rights and limitations under the
          15                 :  * License.
          16                 :  *
          17                 :  * The Original Code is Mozilla Communicator client code, released
          18                 :  * March 31, 1998.
          19                 :  *
          20                 :  * The Initial Developer of the Original Code is
          21                 :  * Netscape Communications Corporation.
          22                 :  * Portions created by the Initial Developer are Copyright (C) 1998
          23                 :  * the Initial Developer. All Rights Reserved.
          24                 :  *
          25                 :  * Contributor(s):
          26                 :  *
          27                 :  * Alternatively, the contents of this file may be used under the terms of
          28                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
          29                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
          30                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
          31                 :  * of those above. If you wish to allow use of your version of this file only
          32                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
          33                 :  * use your version of this file under the terms of the MPL, indicate your
          34                 :  * decision by deleting the provisions above and replace them with the notice
          35                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
          36                 :  * the provisions above, a recipient may use your version of this file under
          37                 :  * the terms of any one of the MPL, the GPL or the LGPL.
          38                 :  *
          39                 :  * ***** END LICENSE BLOCK ***** */
          40                 :
          41                 : /*
          42                 :  * JS shell.
          43                 :  */
          44                 : #include <errno.h>
          45                 : #include <math.h>
          46                 : #include <stdio.h>
          47                 : #include <stdlib.h>
          48                 : #include <string.h>
          49                 : #include <signal.h>
          50                 : #include <locale.h>
          51                 :
          52                 : #include "mozilla/Util.h"
          53                 :
          54                 : #include "jstypes.h"
          55                 : #include "jsutil.h"
          56                 : #include "jsprf.h"
          57                 : #include "jswrapper.h"
          58                 : #include "jsapi.h"
          59                 : #include "jsarray.h"
          60                 : #include "jsatom.h"
          61                 : #include "jscntxt.h"
          62                 : #include "jsdate.h"
          63                 : #include "jsdbgapi.h"
          64                 : #include "jsfun.h"
          65                 : #include "jsgc.h"
          66                 : #include "jsiter.h"
          67                 : #include "jslock.h"
          68                 : #include "jsnum.h"
          69                 : #include "jsobj.h"
          70                 : #include "json.h"
          71                 : #include "jsreflect.h"
          72                 : #include "jsscope.h"
          73                 : #include "jsscript.h"
          74                 : #include "jstypedarray.h"
          75                 : #include "jstypedarrayinlines.h"
          76                 : #include "jsxml.h"
          77                 : #include "jsperf.h"
          78                 :
          79                 : #include "builtin/TestingFunctions.h"
          80                 : #include "frontend/BytecodeEmitter.h"
          81                 : #include "frontend/Parser.h"
          82                 : #include "methodjit/MethodJIT.h"
          83                 :
          84                 : #include "prmjtime.h"
          85                 :
          86                 : #ifdef JSDEBUGGER
          87                 : #include "jsdebug.h"
          88                 : #ifdef JSDEBUGGER_JAVA_UI
          89                 : #include "jsdjava.h"
          90                 : #endif /* JSDEBUGGER_JAVA_UI */
          91                 : #ifdef JSDEBUGGER_C_UI
          92                 : #include "jsdb.h"
          93                 : #endif /* JSDEBUGGER_C_UI */
          94                 : #endif /* JSDEBUGGER */
          95                 :
          96                 : #include "jsoptparse.h"
          97                 : #include "jsworkers.h"
          98                 : #include "jsheaptools.h"
          99                 :
         100                 : #include "jsinferinlines.h"
         101                 : #include "jsinterpinlines.h"
         102                 : #include "jsobjinlines.h"
         103                 : #include "jsscriptinlines.h"
         104                 :
         105                 : #ifdef XP_UNIX
         106                 : #include <unistd.h>
         107                 : #include <sys/types.h>
         108                 : #include <sys/wait.h>
         109                 : #endif
         110                 :
         111                 : #if defined(XP_WIN) || defined(XP_OS2)
         112                 : #include <io.h>     /* for isatty() */
         113                 : #endif
         114                 :
         115                 : #ifdef XP_WIN
         116                 : #include "jswin.h"
         117                 : #endif
         118                 :
         119                 : using namespace mozilla;
         120                 : using namespace js;
         121                 : using namespace js::cli;
         122                 :
         123                 : typedef enum JSShellExitCode {
         124                 :     EXITCODE_RUNTIME_ERROR      = 3,
         125                 :     EXITCODE_FILE_NOT_FOUND     = 4,
         126                 :     EXITCODE_OUT_OF_MEMORY      = 5,
         127                 :     EXITCODE_TIMEOUT            = 6
         128                 : } JSShellExitCode;
         129                 :
         130                 : size_t gStackChunkSize = 8192;
         131                 :
         132                 : /* Assume that we can not use more than 5e5 bytes of C stack by default. */
         133                 : #if (defined(DEBUG) && defined(__SUNPRO_CC))  || defined(JS_CPU_SPARC)
         134                 : /* Sun compiler uses larger stack space for js_Interpret() with debug
         135                 :    Use a bigger gMaxStackSize to make "make check" happy. */
         136                 : #define DEFAULT_MAX_STACK_SIZE 5000000
         137                 : #else
         138                 : #define DEFAULT_MAX_STACK_SIZE 500000
         139                 : #endif
         140                 :
         141                 : size_t gMaxStackSize = DEFAULT_MAX_STACK_SIZE;
         142                 :
         143                 :
         144                 : #ifdef JS_THREADSAFE
         145                 : static PRUintn gStackBaseThreadIndex;
         146                 : #else
         147                 : static uintptr_t gStackBase;
         148                 : #endif
         149                 :
         150                 : /*
         151                 :  * Limit the timeout to 30 minutes to prevent an overflow on platfoms
         152                 :  * that represent the time internally in microseconds using 32-bit int.
         153                 :  */
         154                 : static double MAX_TIMEOUT_INTERVAL = 1800.0;
         155                 : static double gTimeoutInterval = -1.0;
         156                 : static volatile bool gCanceled = false;
         157                 :
         158                 : static bool enableMethodJit = false;
         159                 : static bool enableTypeInference = false;
         160                 : static bool enableDisassemblyDumps = false;
         161                 :
         162                 : static bool printTiming = false;
         163                 :
         164                 : static JSBool
         165                 : SetTimeoutValue(JSContext *cx, double t);
         166                 :
         167                 : static bool
         168                 : InitWatchdog(JSRuntime *rt);
         169                 :
         170                 : static void
         171                 : KillWatchdog();
         172                 :
         173                 : static bool
         174                 : ScheduleWatchdog(JSRuntime *rt, double t);
         175                 :
         176                 : static void
         177                 : CancelExecution(JSRuntime *rt);
         178                 :
         179                 : /*
         180                 :  * Watchdog thread state.
         181                 :  */
         182                 : #ifdef JS_THREADSAFE
         183                 :
         184                 : static PRLock *gWatchdogLock = NULL;
         185                 : static PRCondVar *gWatchdogWakeup = NULL;
         186                 : static PRThread *gWatchdogThread = NULL;
         187                 : static bool gWatchdogHasTimeout = false;
         188                 : static PRIntervalTime gWatchdogTimeout = 0;
         189                 :
         190                 : static PRCondVar *gSleepWakeup = NULL;
         191                 :
         192                 : #else
         193                 :
         194                 : static JSRuntime *gRuntime = NULL;
         195                 :
         196                 : #endif
         197                 :
         198                 : int gExitCode = 0;
         199                 : JSBool gQuitting = JS_FALSE;
         200                 : FILE *gErrFile = NULL;
         201                 : FILE *gOutFile = NULL;
         202                 : #ifdef JS_THREADSAFE
         203                 : JSObject *gWorkers = NULL;
         204                 : js::workers::ThreadPool *gWorkerThreadPool = NULL;
         205                 : #endif
         206                 :
         207                 : static JSBool reportWarnings = JS_TRUE;
         208                 : static JSBool compileOnly = JS_FALSE;
         209                 :
         210                 : #ifdef DEBUG
         211                 : static JSBool OOM_printAllocationCount = JS_FALSE;
         212                 : #endif
         213                 :
         214                 : typedef enum JSShellErrNum {
         215                 : #define MSG_DEF(name, number, count, exception, format) \
         216                 :     name = number,
         217                 : #include "jsshell.msg"
         218                 : #undef MSG_DEF
         219                 :     JSShellErr_Limit
         220                 : } JSShellErrNum;
         221                 :
         222                 : static JSContext *
         223                 : NewContext(JSRuntime *rt);
         224                 :
         225                 : static void
         226                 : DestroyContext(JSContext *cx, bool withGC);
         227                 :
         228                 : static const JSErrorFormatString *
         229                 : my_GetErrorMessage(void *userRef, const char *locale, const unsigned errorNumber);
         230                 :
         231                 : #ifdef EDITLINE
         232                 : JS_BEGIN_EXTERN_C
         233                 : extern JS_EXPORT_API(char *) readline(const char *prompt);
         234                 : extern JS_EXPORT_API(void)   add_history(char *line);
         235                 : JS_END_EXTERN_C
         236                 : #endif
         237                 :
         238                 : static void
         239               0 : ReportException(JSContext *cx)
         240                 : {
         241               0 :     if (JS_IsExceptionPending(cx)) {
         242               0 :         if (!JS_ReportPendingException(cx))
         243               0 :             JS_ClearPendingException(cx);
         244                 :     }
         245               0 : }
         246                 :
         247                 : class ToStringHelper {
         248                 :   public:
         249               0 :     ToStringHelper(JSContext *aCx, jsval v, JSBool aThrow = JS_FALSE)
         250               0 :       : cx(aCx), mThrow(aThrow)
         251                 :     {
         252               0 :         mStr = JS_ValueToString(cx, v);
         253               0 :         if (!aThrow && !mStr)
         254               0 :             ReportException(cx);
         255               0 :         JS_AddNamedStringRoot(cx, &mStr, "Value ToString helper");
         256               0 :     }
         257               0 :     ~ToStringHelper() {
         258               0 :         JS_RemoveStringRoot(cx, &mStr);
         259               0 :     }
         260               0 :     JSBool threw() { return !mStr; }
         261               0 :     jsval getJSVal() { return STRING_TO_JSVAL(mStr); }
         262               0 :     const char *getBytes() {
         263               0 :         if (mStr && (mBytes.ptr() || mBytes.encode(cx, mStr)))
         264               0 :             return mBytes.ptr();
         265               0 :         return "(error converting value)";
         266                 :     }
         267                 :   private:
         268                 :     JSContext *cx;
         269                 :     JSString *mStr;
         270                 :     JSBool mThrow;
         271                 :     JSAutoByteString mBytes;
         272                 : };
         273                 :
         274               0 : class IdStringifier : public ToStringHelper {
         275                 : public:
         276               0 :     IdStringifier(JSContext *cx, jsid id, JSBool aThrow = JS_FALSE)
         277               0 :     : ToStringHelper(cx, IdToJsval(id), aThrow)
         278               0 :     { }
         279                 : };
         280                 :
         281                 : static char *
         282               0 : GetLine(FILE *file, const char * prompt)
         283                 : {
         284                 :     size_t size;
         285                 :     char *buffer;
         286                 : #ifdef EDITLINE
         287                 :     /*
         288                 :      * Use readline only if file is stdin, because there's no way to specify
         289                 :      * another handle.  Are other filehandles interactive?
         290                 :      */
         291               0 :     if (file == stdin) {
         292               0 :         char *linep = readline(prompt);
         293                 :         /*
         294                 :          * We set it to zero to avoid complaining about inappropriate ioctl
         295                 :          * for device in the case of EOF. Looks like errno == 251 if line is
         296                 :          * finished with EOF and errno == 25 (EINVAL on Mac) if there is
         297                 :          * nothing left to read.
         298                 :          */
         299               0 :         if (errno == 251 || errno == 25 || errno == EINVAL)
         300               0 :             errno = 0;
         301               0 :         if (!linep)
         302               0 :             return NULL;
         303               0 :         if (linep[0] != '\0')
         304               0 :             add_history(linep);
         305               0 :         return linep;
         306                 :     }
         307                 : #endif
         308               0 :     size_t len = 0;
         309               0 :     if (*prompt != '\0') {
         310               0 :         fprintf(gOutFile, "%s", prompt);
         311               0 :         fflush(gOutFile);
         312                 :     }
         313               0 :     size = 80;
         314               0 :     buffer = (char *) malloc(size);
         315               0 :     if (!buffer)
         316               0 :         return NULL;
         317               0 :     char *current = buffer;
         318               0 :     while (fgets(current, size - len, file)) {
         319               0 :         len += strlen(current);
         320               0 :         char *t = buffer + len - 1;
         321               0 :         if (*t == '\n') {
         322                 :             /* Line was read. We remove '\n' and exit. */
         323               0 :             *t = '\0';
         324               0 :             return buffer;
         325                 :         }
         326               0 :         if (len + 1 == size) {
         327               0 :             size = size * 2;
         328               0 :             char *tmp = (char *) realloc(buffer, size);
         329               0 :             if (!tmp) {
         330               0 :                 free(buffer);
         331               0 :                 return NULL;
         332                 :             }
         333               0 :             buffer = tmp;
         334                 :         }
         335               0 :         current = buffer + len;
         336                 :     }
         337               0 :     if (len && !ferror(file))
         338               0 :         return buffer;
         339               0 :     free(buffer);
         340               0 :     return NULL;
         341                 : }
         342                 :
         343                 : /*
         344                 :  * State to store as JSContext private.
         345                 :  *
         346                 :  * We declare such timestamp as volatile as they are updated in the operation
         347                 :  * callback without taking any locks. Any possible race can only lead to more
         348                 :  * frequent callback calls. This is safe as the callback does everything based
         349                 :  * on timing.
         350                 :  */
         351                 : struct JSShellContextData {
         352                 :     volatile JSIntervalTime startTime;
         353                 : };
         354                 :
         355                 : static JSShellContextData *
         356           18405 : NewContextData()
         357                 : {
         358                 :     /* Prevent creation of new contexts after we have been canceled. */
         359           18405 :     if (gCanceled)
         360               0 :         return NULL;
         361                 :
         362                 :     JSShellContextData *data = (JSShellContextData *)
         363           18405 :                                calloc(sizeof(JSShellContextData), 1);
         364           18405 :     if (!data)
         365               0 :         return NULL;
         366           18405 :     data->startTime = js_IntervalNow();
         367           18405 :     return data;
         368                 : }
         369                 :
         370                 : static inline JSShellContextData *
         371           18405 : GetContextData(JSContext *cx)
         372                 : {
         373           18405 :     JSShellContextData *data = (JSShellContextData *) JS_GetContextPrivate(cx);
         374                 :
         375           18405 :     JS_ASSERT(data);
         376           18405 :     return data;
         377                 : }
         378                 :
         379                 : static JSBool
         380             160 : ShellOperationCallback(JSContext *cx)
         381                 : {
         382             160 :     if (!gCanceled)
         383             160 :         return JS_TRUE;
         384                 :
         385               0 :     JS_ClearPendingException(cx);
         386               0 :     return JS_FALSE;
         387                 : }
         388                 :
         389                 : static void
         390           55215 : SetContextOptions(JSContext *cx)
         391                 : {
         392           55215 :     JS_SetOperationCallback(cx, ShellOperationCallback);
         393           55215 : }
         394                 :
         395                 : /*
         396                 :  * Some UTF-8 files, notably those written using Notepad, have a Unicode
         397                 :  * Byte-Order-Mark (BOM) as their first character. This is useless (byte-order
         398                 :  * is meaningless for UTF-8) but causes a syntax error unless we skip it.
         399                 :  */
         400                 : static void
         401           36810 : SkipUTF8BOM(FILE* file)
         402                 : {
         403           36810 :     if (!js_CStringsAreUTF8)
         404           36810 :         return;
         405                 :
         406               0 :     int ch1 = fgetc(file);
         407               0 :     int ch2 = fgetc(file);
         408               0 :     int ch3 = fgetc(file);
         409                 :
         410                 :     // Skip the BOM
         411               0 :     if (ch1 == 0xEF && ch2 == 0xBB && ch3 == 0xBF)
         412               0 :         return;
         413                 :
         414                 :     // No BOM - revert
         415               0 :     if (ch3 != EOF)
         416               0 :         ungetc(ch3, file);
         417               0 :     if (ch2 != EOF)
         418               0 :         ungetc(ch2, file);
         419               0 :     if (ch1 != EOF)
         420               0 :         ungetc(ch1, file);
         421                 : }
         422                 :
         423                 : static void
         424           36810 : Process(JSContext *cx, JSObject *obj, const char *filename, bool forceTTY)
         425                 : {
         426                 :     JSBool ok, hitEOF;
         427                 :     JSScript *script;
         428                 :     jsval result;
         429                 :     JSString *str;
         430                 :     char *buffer;
         431                 :     size_t size;
         432                 :     jschar *uc_buffer;
         433                 :     size_t uc_len;
         434                 :     int lineno;
         435                 :     int startline;
         436                 :     FILE *file;
         437                 :     uint32_t oldopts;
         438                 :
         439           73620 :     RootObject root(cx, &obj);
         440                 :
         441           36810 :     if (forceTTY || !filename || strcmp(filename, "-") == 0) {
         442               0 :         file = stdin;
         443                 :     } else {
         444           36810 :         file = fopen(filename, "r");
         445           36810 :         if (!file) {
         446                 :             JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
         447               0 :                                  JSSMSG_CANT_OPEN, filename, strerror(errno));
         448               0 :             gExitCode = EXITCODE_FILE_NOT_FOUND;
         449                 :             return;
         450                 :         }
         451                 :     }
         452                 :
         453           36810 :     SetContextOptions(cx);
         454                 :
         455           36810 :     if (!forceTTY && !isatty(fileno(file)))
         456                 :     {
         457           36810 :         SkipUTF8BOM(file);
         458                 :
         459                 :         /*
         460                 :          * It's not interactive - just execute it.  Support the UNIX #! shell
         461                 :          * hack, and gobble the first line if it starts with '#'.
         462                 :          */
         463           36810 :         int ch = fgetc(file);
         464           36810 :         if (ch == '#') {
         465               0 :             while((ch = fgetc(file)) != EOF) {
         466               0 :                 if (ch == '\n' || ch == '\r')
         467               0 :                     break;
         468                 :             }
         469                 :         }
         470           36810 :         ungetc(ch, file);
         471                 :
         472           36810 :         int64_t t1 = PRMJ_Now();
         473           36810 :         oldopts = JS_GetOptions(cx);
         474           36810 :         JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO | JSOPTION_NO_SCRIPT_RVAL);
         475           36810 :         script = JS_CompileUTF8FileHandle(cx, obj, filename, file);
         476           36810 :         JS_SetOptions(cx, oldopts);
         477           36810 :         if (script && !compileOnly) {
         478           36765 :             if (!JS_ExecuteScript(cx, obj, script, NULL)) {
         479             810 :                 if (!gQuitting && !gCanceled)
         480             783 :                     gExitCode = EXITCODE_RUNTIME_ERROR;
         481                 :             }
         482           36765 :             int64_t t2 = PRMJ_Now() - t1;
         483           36765 :             if (printTiming)
         484               0 :                 printf("runtime = %.3f ms\n", double(t2) / PRMJ_USEC_PER_MSEC);
         485                 :         }
         486                 :
         487           36810 :         goto cleanup;
         488                 :     }
         489                 :
         490                 :     /* It's an interactive filehandle; drop into read-eval-print loop. */
         491               0 :     lineno = 1;
         492               0 :     hitEOF = JS_FALSE;
         493               0 :     buffer = NULL;
         494               0 :     size = 0;           /* assign here to avoid warnings */
         495               0 :     do {
         496                 :         /*
         497                 :          * Accumulate lines until we get a 'compilable unit' - one that either
         498                 :          * generates an error (before running out of source) or that compiles
         499                 :          * cleanly.  This should be whenever we get a complete statement that
         500                 :          * coincides with the end of a line.
         501                 :          */
         502               0 :         startline = lineno;
         503               0 :         size_t len = 0; /* initialize to avoid warnings */
         504               0 :         do {
         505               0 :             ScheduleWatchdog(cx->runtime, -1);
         506               0 :             gCanceled = false;
         507               0 :             errno = 0;
         508                 :
         509                 :             char *line;
         510                 :             {
         511               0 :                 JSAutoSuspendRequest suspended(cx);
         512               0 :                 line = GetLine(file, startline == lineno ? "js> " : "");
         513                 :             }
         514               0 :             if (!line) {
         515               0 :                 if (errno) {
         516               0 :                     JS_ReportError(cx, strerror(errno));
         517               0 :                     free(buffer);
         518               0 :                     goto cleanup;
         519                 :                 }
         520               0 :                 hitEOF = JS_TRUE;
         521               0 :                 break;
         522                 :             }
         523               0 :             if (!buffer) {
         524               0 :                 buffer = line;
         525               0 :                 len = strlen(buffer);
         526               0 :                 size = len + 1;
         527                 :             } else {
         528                 :                 /*
         529                 :                  * len + 1 is required to store '\n' in the end of line.
         530                 :                  */
         531               0 :                 size_t newlen = strlen(line) + (len ? len + 1 : 0);
         532               0 :                 if (newlen + 1 > size) {
         533               0 :                     size = newlen + 1 > size * 2 ? newlen + 1 : size * 2;
         534               0 :                     char *newBuf = (char *) realloc(buffer, size);
         535               0 :                     if (!newBuf) {
         536               0 :                         free(buffer);
         537               0 :                         free(line);
         538               0 :                         JS_ReportOutOfMemory(cx);
         539               0 :                         goto cleanup;
         540                 :                     }
         541               0 :                     buffer = newBuf;
         542                 :                 }
         543               0 :                 char *current = buffer + len;
         544               0 :                 if (startline != lineno)
         545               0 :                     *current++ = '\n';
         546               0 :                 strcpy(current, line);
         547               0 :                 len = newlen;
         548               0 :                 free(line);
         549                 :             }
         550               0 :             lineno++;
         551               0 :             if (!ScheduleWatchdog(cx->runtime, gTimeoutInterval)) {
         552               0 :                 hitEOF = JS_TRUE;
         553               0 :                 break;
         554                 :             }
         555               0 :         } while (!JS_BufferIsCompilableUnit(cx, JS_TRUE, obj, buffer, len));
         556                 :
         557               0 :         if (hitEOF && !buffer)
         558               0 :             break;
         559                 :
         560               0 :         if (!JS_DecodeUTF8(cx, buffer, len, NULL, &uc_len)) {
         561               0 :             JS_ReportError(cx, "Invalid UTF-8 in input");
         562               0 :             gExitCode = EXITCODE_RUNTIME_ERROR;
         563                 :             return;
         564                 :         }
         565                 :
         566               0 :         uc_buffer = (jschar*)malloc(uc_len * sizeof(jschar));
         567               0 :         JS_DecodeUTF8(cx, buffer, len, uc_buffer, &uc_len);
         568                 :
         569                 :         /* Clear any pending exception from previous failed compiles. */
         570               0 :         JS_ClearPendingException(cx);
         571                 :
         572                 :         /* Even though we're interactive, we have a compile-n-go opportunity. */
         573               0 :         oldopts = JS_GetOptions(cx);
         574               0 :         if (!compileOnly)
         575               0 :             JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO);
         576               0 :         script = JS_CompileUCScript(cx, obj, uc_buffer, uc_len, "typein", startline);
         577               0 :         if (!compileOnly)
         578               0 :             JS_SetOptions(cx, oldopts);
         579                 :
         580               0 :         if (script && !compileOnly) {
         581               0 :             ok = JS_ExecuteScript(cx, obj, script, &result);
         582               0 :             if (ok && !JSVAL_IS_VOID(result)) {
         583               0 :                 str = JS_ValueToSource(cx, result);
         584               0 :                 ok = !!str;
         585               0 :                 if (ok) {
         586               0 :                     JSAutoByteString bytes(cx, str);
         587               0 :                     ok = !!bytes;
         588               0 :                     if (ok)
         589               0 :                         fprintf(gOutFile, "%s\n", bytes.ptr());
         590                 :                 }
         591                 :             }
         592                 :         }
         593               0 :         *buffer = '\0';
         594               0 :         free(uc_buffer);
         595               0 :     } while (!hitEOF && !gQuitting);
         596                 :
         597               0 :     free(buffer);
         598               0 :     fprintf(gOutFile, "\n");
         599                 : cleanup:
         600           36810 :     if (file != stdin)
         601           36810 :         fclose(file);
         602                 :     return;
         603                 : }
         604                 :
         605                 : /*
         606                 :  * JSContext option name to flag map. The option names are in alphabetical
         607                 :  * order for better reporting.
         608                 :  */
         609                 : static const struct JSOption {
         610                 :     const char  *name;
         611                 :     uint32_t    flag;
         612                 : } js_options[] = {
         613                 :     {"atline",          JSOPTION_ATLINE},
         614                 :     {"methodjit",       JSOPTION_METHODJIT},
         615                 :     {"methodjit_always",JSOPTION_METHODJIT_ALWAYS},
         616                 :     {"relimit",         JSOPTION_RELIMIT},
         617                 :     {"strict",          JSOPTION_STRICT},
         618                 :     {"typeinfer",       JSOPTION_TYPE_INFERENCE},
         619                 :     {"werror",          JSOPTION_WERROR},
         620                 :     {"xml",             JSOPTION_XML},
         621                 : };
         622                 :
         623                 : static uint32_t
         624              45 : MapContextOptionNameToFlag(JSContext* cx, const char* name)
         625                 : {
         626             240 :     for (size_t i = 0; i < ArrayLength(js_options); ++i) {
         627             240 :         if (strcmp(name, js_options[i].name) == 0)
         628              45 :             return js_options[i].flag;
         629                 :     }
         630                 :
         631                 :     char* msg = JS_sprintf_append(NULL,
         632                 :                                   "unknown option name '%s'."
         633               0 :                                   " The valid names are ", name);
         634               0 :     for (size_t i = 0; i < ArrayLength(js_options); ++i) {
         635               0 :         if (!msg)
         636               0 :             break;
         637                 :         msg = JS_sprintf_append(msg, "%s%s", js_options[i].name,
         638               0 :                                 (i + 2 < ArrayLength(js_options)
         639                 :                                  ? ", "
         640               0 :                                  : i + 2 == ArrayLength(js_options)
         641                 :                                  ? " and "
         642               0 :                                  : "."));
         643                 :     }
         644               0 :     if (!msg) {
         645               0 :         JS_ReportOutOfMemory(cx);
         646                 :     } else {
         647               0 :         JS_ReportError(cx, msg);
         648               0 :         free(msg);
         649                 :     }
         650               0 :     return 0;
         651                 : }
         652                 :
         653                 : extern JSClass global_class;
         654                 :
         655                 : #ifdef JS_GC_ZEAL
         656                 : static void
         657               0 : ParseZealArg(JSContext *cx, const char *arg)
         658                 : {
         659               0 :     int zeal, freq = 1, compartment = 0;
         660               0 :     const char *p = strchr(arg, ',');
         661                 :
         662               0 :     zeal = atoi(arg);
         663               0 :     if (p) {
         664               0 :         freq = atoi(p + 1);
         665               0 :         p = strchr(p + 1, ',');
         666               0 :         if (p)
         667               0 :             compartment = atoi(p + 1);
         668                 :     }
         669                 :
         670               0 :     JS_SetGCZeal(cx, (uint8_t)zeal, freq, !!compartment);
         671               0 : }
         672                 : #endif
         673                 :
         674                 : static JSBool
         675              54 : Version(JSContext *cx, unsigned argc, jsval *vp)
         676                 : {
         677              54 :     jsval *argv = JS_ARGV(cx, vp);
         678              54 :     if (argc == 0 || JSVAL_IS_VOID(argv[0])) {
         679                 :         /* Get version. */
         680               0 :         *vp = INT_TO_JSVAL(JS_GetVersion(cx));
         681                 :     } else {
         682                 :         /* Set version. */
         683              54 :         int32_t v = -1;
         684              54 :         if (JSVAL_IS_INT(argv[0])) {
         685              54 :             v = JSVAL_TO_INT(argv[0]);
         686               0 :         } else if (JSVAL_IS_DOUBLE(argv[0])) {
         687               0 :             double fv = JSVAL_TO_DOUBLE(argv[0]);
         688               0 :             if (int32_t(fv) == fv)
         689               0 :                 v = int32_t(fv);
         690                 :         }
         691              54 :         if (v < 0 || v > JSVERSION_LATEST) {
         692               0 :             JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_INVALID_ARGS, "version");
         693               0 :             return false;
         694                 :         }
         695              54 :         *vp = INT_TO_JSVAL(JS_SetVersion(cx, JSVersion(v)));
         696                 :     }
         697              54 :     return true;
         698                 : }
         699                 :
         700                 : static JSBool
         701              36 : RevertVersion(JSContext *cx, unsigned argc, jsval *vp)
         702                 : {
         703              36 :     js_RevertVersion(cx);
         704              36 :     JS_SET_RVAL(cx, vp, JSVAL_VOID);
         705              36 :     return JS_TRUE;
         706                 : }
         707                 :
         708                 : static JSBool
         709              45 : Options(JSContext *cx, unsigned argc, jsval *vp)
         710                 : {
         711                 :     uint32_t optset, flag;
         712                 :     JSString *str;
         713                 :     char *names;
         714                 :     JSBool found;
         715                 :
         716              45 :     optset = 0;
         717              45 :     jsval *argv = JS_ARGV(cx, vp);
         718              90 :     for (unsigned i = 0; i < argc; i++) {
         719              45 :         str = JS_ValueToString(cx, argv[i]);
         720              45 :         if (!str)
         721               0 :             return JS_FALSE;
         722              45 :         argv[i] = STRING_TO_JSVAL(str);
         723              90 :         JSAutoByteString opt(cx, str);
         724              45 :         if (!opt)
         725               0 :             return JS_FALSE;
         726              45 :         flag = MapContextOptionNameToFlag(cx, opt.ptr());
         727              45 :         if (!flag)
         728               0 :             return JS_FALSE;
         729              90 :         optset |= flag;
         730                 :     }
         731              45 :     optset = JS_ToggleOptions(cx, optset);
         732                 :
         733              45 :     names = NULL;
         734              45 :     found = JS_FALSE;
         735             405 :     for (size_t i = 0; i < ArrayLength(js_options); i++) {
         736             360 :         if (js_options[i].flag & optset) {
         737              90 :             found = JS_TRUE;
         738                 :             names = JS_sprintf_append(names, "%s%s",
         739              90 :                                       names ? "," : "", js_options[i].name);
         740              90 :             if (!names)
         741               0 :                 break;
         742                 :         }
         743                 :     }
         744              45 :     if (!found)
         745               3 :         names = strdup("");
         746              45 :     if (!names) {
         747               0 :         JS_ReportOutOfMemory(cx);
         748               0 :         return JS_FALSE;
         749                 :     }
         750              45 :     str = JS_NewStringCopyZ(cx, names);
         751              45 :     free(names);
         752              45 :     if (!str)
         753               0 :         return JS_FALSE;
         754              45 :     *vp = STRING_TO_JSVAL(str);
         755              45 :     return JS_TRUE;
         756                 : }
         757                 :
         758                 : static JSBool
         759             873 : Load(JSContext *cx, unsigned argc, jsval *vp)
         760                 : {
         761             873 :     JSObject *thisobj = JS_THIS_OBJECT(cx, vp);
         762             873 :     if (!thisobj)
         763               0 :         return JS_FALSE;
         764                 :
         765             873 :     jsval *argv = JS_ARGV(cx, vp);
         766            1746 :     for (unsigned i = 0; i < argc; i++) {
         767             873 :         JSString *str = JS_ValueToString(cx, argv[i]);
         768             873 :         if (!str)
         769               0 :             return false;
         770             873 :         argv[i] = STRING_TO_JSVAL(str);
         771            1746 :         JSAutoByteString filename(cx, str);
         772             873 :         if (!filename)
         773               0 :             return JS_FALSE;
         774             873 :         errno = 0;
         775             873 :         uint32_t oldopts = JS_GetOptions(cx);
         776             873 :         JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO | JSOPTION_NO_SCRIPT_RVAL);
         777             873 :         JSScript *script = JS_CompileUTF8File(cx, thisobj, filename.ptr());
         778             873 :         JS_SetOptions(cx, oldopts);
         779             873 :         if (!script)
         780               0 :             return false;
         781                 :
         782             873 :         if (!compileOnly && !JS_ExecuteScript(cx, thisobj, script, NULL))
         783               0 :             return false;
         784                 :     }
         785                 :
         786             873 :     JS_SET_RVAL(cx, vp, JSVAL_VOID);
         787             873 :     return true;
         788                 : }
         789                 :
         790                 : static JSBool
         791               0 : EvaluateWithLocation(JSContext *cx, unsigned argc, jsval *vp)
         792                 : {
         793               0 :     if (argc != 3) {
         794                 :         JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
         795                 :                              argc > 3 ? JSSMSG_TOO_MANY_ARGS : JSSMSG_NOT_ENOUGH_ARGS,
         796               0 :                              "evalWithLocation");
         797               0 :         return false;
         798                 :     }
         799                 :
         800               0 :     JSString *code = JS_ValueToString(cx, JS_ARGV(cx, vp)[0]);
         801               0 :     if (!code)
         802               0 :         return false;
         803               0 :     JS::Anchor<JSString *> a_code(code);
         804                 :
         805                 :     size_t codeLength;
         806               0 :     const jschar *codeChars = JS_GetStringCharsAndLength(cx, code, &codeLength);
         807               0 :     if (!codeChars)
         808               0 :         return false;
         809                 :
         810               0 :     JSString *filename = JS_ValueToString(cx, JS_ARGV(cx, vp)[1]);
         811               0 :     if (!filename)
         812               0 :         return false;
         813                 :
         814                 :     uint32_t lineno;
         815               0 :     if (!JS_ValueToECMAUint32(cx, JS_ARGV(cx, vp)[2], &lineno))
         816               0 :         return false;
         817                 :
         818               0 :     JSObject *thisobj = JS_THIS_OBJECT(cx, vp);
         819               0 :     if (!thisobj)
         820               0 :         return false;
         821                 :
         822               0 :     if ((JS_GetClass(thisobj)->flags & JSCLASS_IS_GLOBAL) != JSCLASS_IS_GLOBAL) {
         823                 :         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_UNEXPECTED_TYPE,
         824               0 :                              "this-value passed to evalWithLocation()", "not a global object");
         825               0 :         return false;
         826                 :     }
         827                 :
         828               0 :     char *filenameBytes = JS_EncodeString(cx, filename);
         829               0 :     if (!filenameBytes)
         830               0 :         return false;
         831                 :
         832                 :     jsval rval;
         833               0 :     bool ok = JS_EvaluateUCScript(cx, thisobj, codeChars, codeLength, filenameBytes, lineno, &rval);
         834               0 :     JS_free(cx, filenameBytes);
         835                 :
         836               0 :     if (!ok)
         837               0 :         return false;
         838                 :
         839               0 :     JS_SET_RVAL(cx, vp, rval);
         840               0 :     return true;
         841                 : }
         842                 :
         843                 : static JSBool
         844             333 : Evaluate(JSContext *cx, unsigned argc, jsval *vp)
         845                 : {
         846             333 :     if (argc != 1 || !JSVAL_IS_STRING(JS_ARGV(cx, vp)[0])) {
         847                 :         JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
         848                 :                              (argc != 1) ? JSSMSG_NOT_ENOUGH_ARGS : JSSMSG_INVALID_ARGS,
         849               0 :                              "evaluate");
         850               0 :         return false;
         851                 :     }
         852                 :
         853             333 :     JSString *code = JSVAL_TO_STRING(JS_ARGV(cx, vp)[0]);
         854                 :
         855                 :     size_t codeLength;
         856             333 :     const jschar *codeChars = JS_GetStringCharsAndLength(cx, code, &codeLength);
         857             333 :     if (!codeChars)
         858               0 :         return false;
         859                 :
         860             333 :     JSObject *thisobj = JS_THIS_OBJECT(cx, vp);
         861             333 :     if (!thisobj)
         862               0 :         return false;
         863                 :
         864             333 :     if ((JS_GetClass(thisobj)->flags & JSCLASS_IS_GLOBAL) != JSCLASS_IS_GLOBAL) {
         865                 :         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_UNEXPECTED_TYPE,
         866               0 :                              "this-value passed to evaluate()", "not a global object");
         867               0 :         return false;
         868                 :     }
         869                 :
         870             333 :     return JS_EvaluateUCScript(cx, thisobj, codeChars, codeLength, "@evaluate", 0, vp);
         871                 : }
         872                 :
         873                 : static JSString *
         874               0 : FileAsString(JSContext *cx, const char *pathname)
         875                 : {
         876                 :     FILE *file;
         877               0 :     JSString *str = NULL;
         878                 :     size_t len, cc;
         879                 :     char *buf;
         880                 :
         881               0 :     file = fopen(pathname, "rb");
         882               0 :     if (!file) {
         883               0 :         JS_ReportError(cx, "can't open %s: %s", pathname, strerror(errno));
         884               0 :         return NULL;
         885                 :     }
         886                 :
         887               0 :     if (fseek(file, 0, SEEK_END) != 0) {
         888               0 :         JS_ReportError(cx, "can't seek end of %s", pathname);
         889                 :     } else {
         890               0 :         len = ftell(file);
         891               0 :         if (fseek(file, 0, SEEK_SET) != 0) {
         892               0 :             JS_ReportError(cx, "can't seek start of %s", pathname);
         893                 :         } else {
         894               0 :             buf = (char*) JS_malloc(cx, len + 1);
         895               0 :             if (buf) {
         896               0 :                 cc = fread(buf, 1, len, file);
         897               0 :                 if (cc != len) {
         898                 :                     JS_ReportError(cx, "can't read %s: %s", pathname,
         899               0 :                                    (ptrdiff_t(cc) < 0) ? strerror(errno) : "short read");
         900                 :                 } else {
         901                 :                     jschar *ucbuf;
         902                 :                     size_t uclen;
         903                 :
         904               0 :                     len = (size_t)cc;
         905                 :
         906               0 :                     if (!JS_DecodeUTF8(cx, buf, len, NULL, &uclen)) {
         907               0 :                         JS_ReportError(cx, "Invalid UTF-8 in file '%s'", pathname);
         908               0 :                         gExitCode = EXITCODE_RUNTIME_ERROR;
         909               0 :                         return NULL;
         910                 :                     }
         911                 :
         912               0 :                     ucbuf = (jschar*)malloc(uclen * sizeof(jschar));
         913               0 :                     JS_DecodeUTF8(cx, buf, len, ucbuf, &uclen);
         914               0 :                     str = JS_NewUCStringCopyN(cx, ucbuf, uclen);
         915               0 :                     free(ucbuf);
         916                 :                 }
         917               0 :                 JS_free(cx, buf);
         918                 :             }
         919                 :         }
         920                 :     }
         921               0 :     fclose(file);
         922                 :
         923               0 :     return str;
         924                 : }
         925                 :
         926                 : static JSObject *
         927               0 : FileAsTypedArray(JSContext *cx, const char *pathname)
         928                 : {
         929               0 :     FILE *file = fopen(pathname, "rb");
         930               0 :     if (!file) {
         931               0 :         JS_ReportError(cx, "can't open %s: %s", pathname, strerror(errno));
         932               0 :         return NULL;
         933                 :     }
         934                 :
         935               0 :     JSObject *obj = NULL;
         936               0 :     if (fseek(file, 0, SEEK_END) != 0) {
         937               0 :         JS_ReportError(cx, "can't seek end of %s", pathname);
         938                 :     } else {
         939               0 :         size_t len = ftell(file);
         940               0 :         if (fseek(file, 0, SEEK_SET) != 0) {
         941               0 :             JS_ReportError(cx, "can't seek start of %s", pathname);
         942                 :         } else {
         943               0 :             obj = js_CreateTypedArray(cx, TypedArray::TYPE_UINT8, len);
         944               0 :             if (!obj)
         945               0 :                 return NULL;
         946               0 :             char *buf = (char *) TypedArray::getDataOffset(TypedArray::getTypedArray(obj));
         947               0 :             size_t cc = fread(buf, 1, len, file);
         948               0 :             if (cc != len) {
         949                 :                 JS_ReportError(cx, "can't read %s: %s", pathname,
         950               0 :                                (ptrdiff_t(cc) < 0) ? strerror(errno) : "short read");
         951               0 :                 obj = NULL;
         952                 :             }
         953                 :         }
         954                 :     }
         955               0 :     fclose(file);
         956               0 :     return obj;
         957                 : }
         958                 :
         959                 : /*
         960                 :  * Function to run scripts and return compilation + execution time. Semantics
         961                 :  * are closely modelled after the equivalent function in WebKit, as this is used
         962                 :  * to produce benchmark timings by SunSpider.
         963                 :  */
         964                 : static JSBool
         965               0 : Run(JSContext *cx, unsigned argc, jsval *vp)
         966                 : {
         967               0 :     if (argc != 1) {
         968               0 :         JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_INVALID_ARGS, "run");
         969               0 :         return false;
         970                 :     }
         971                 :
         972               0 :     JSObject *thisobj = JS_THIS_OBJECT(cx, vp);
         973               0 :     if (!thisobj)
         974               0 :         return false;
         975                 :
         976               0 :     jsval *argv = JS_ARGV(cx, vp);
         977               0 :     JSString *str = JS_ValueToString(cx, argv[0]);
         978               0 :     if (!str)
         979               0 :         return false;
         980               0 :     argv[0] = STRING_TO_JSVAL(str);
         981               0 :     JSAutoByteString filename(cx, str);
         982               0 :     if (!filename)
         983               0 :         return false;
         984                 :
         985               0 :     const jschar *ucbuf = NULL;
         986                 :     size_t buflen;
         987               0 :     str = FileAsString(cx, filename.ptr());
         988               0 :     if (str)
         989               0 :         ucbuf = JS_GetStringCharsAndLength(cx, str, &buflen);
         990               0 :     if (!ucbuf)
         991               0 :         return false;
         992                 :
         993               0 :     JS::Anchor<JSString *> a_str(str);
         994               0 :     uint32_t oldopts = JS_GetOptions(cx);
         995               0 :     JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO | JSOPTION_NO_SCRIPT_RVAL);
         996                 :
         997               0 :     int64_t startClock = PRMJ_Now();
         998               0 :     JSScript *script = JS_CompileUCScript(cx, thisobj, ucbuf, buflen, filename.ptr(), 1);
         999               0 :     JS_SetOptions(cx, oldopts);
        1000               0 :     if (!script || !JS_ExecuteScript(cx, thisobj, script, NULL))
        1001               0 :         return false;
        1002                 :
        1003               0 :     int64_t endClock = PRMJ_Now();
        1004               0 :     JS_SET_RVAL(cx, vp, DOUBLE_TO_JSVAL((endClock - startClock) / double(PRMJ_USEC_PER_MSEC)));
        1005               0 :     return true;
        1006                 : }
        1007                 :
        1008                 : /*
        1009                 :  * function readline()
        1010                 :  * Provides a hook for scripts to read a line from stdin.
        1011                 :  */
        1012                 : static JSBool
        1013               0 : ReadLine(JSContext *cx, unsigned argc, jsval *vp)
        1014                 : {
        1015                 : #define BUFSIZE 256
        1016                 :     FILE *from;
        1017                 :     char *buf, *tmp;
        1018                 :     size_t bufsize, buflength, gotlength;
        1019                 :     JSBool sawNewline;
        1020                 :     JSString *str;
        1021                 :
        1022               0 :     from = stdin;
        1023               0 :     buflength = 0;
        1024               0 :     bufsize = BUFSIZE;
        1025               0 :     buf = (char *) JS_malloc(cx, bufsize);
        1026               0 :     if (!buf)
        1027               0 :         return JS_FALSE;
        1028                 :
        1029               0 :     sawNewline = JS_FALSE;
        1030               0 :     while ((gotlength =
        1031               0 :             js_fgets(buf + buflength, bufsize - buflength, from)) > 0) {
        1032               0 :         buflength += gotlength;
        1033                 :
        1034                 :         /* Are we done? */
        1035               0 :         if (buf[buflength - 1] == '\n') {
        1036               0 :             buf[buflength - 1] = '\0';
        1037               0 :             sawNewline = JS_TRUE;
        1038               0 :             break;
        1039               0 :         } else if (buflength < bufsize - 1) {
        1040               0 :             break;
        1041                 :         }
        1042                 :
        1043                 :         /* Else, grow our buffer for another pass. */
        1044               0 :         bufsize *= 2;
        1045               0 :         if (bufsize > buflength) {
        1046               0 :             tmp = (char *) JS_realloc(cx, buf, bufsize);
        1047                 :         } else {
        1048               0 :             JS_ReportOutOfMemory(cx);
        1049               0 :             tmp = NULL;
        1050                 :         }
        1051                 :
        1052               0 :         if (!tmp) {
        1053               0 :             JS_free(cx, buf);
        1054               0 :             return JS_FALSE;
        1055                 :         }
        1056                 :
        1057               0 :         buf = tmp;
        1058                 :     }
        1059                 :
        1060                 :     /* Treat the empty string specially. */
        1061               0 :     if (buflength == 0) {
        1062               0 :         *vp = feof(from) ? JSVAL_NULL : JS_GetEmptyStringValue(cx);
        1063               0 :         JS_free(cx, buf);
        1064               0 :         return JS_TRUE;
        1065                 :     }
        1066                 :
        1067                 :     /* Shrink the buffer to the real size. */
        1068               0 :     tmp = (char *) JS_realloc(cx, buf, buflength);
        1069               0 :     if (!tmp) {
        1070               0 :         JS_free(cx, buf);
        1071               0 :         return JS_FALSE;
        1072                 :     }
        1073                 :
        1074               0 :     buf = tmp;
        1075                 :
        1076                 :     /*
        1077                 :      * Turn buf into a JSString. Note that buflength includes the trailing null
        1078                 :      * character.
        1079                 :      */
        1080               0 :     str = JS_NewStringCopyN(cx, buf, sawNewline ? buflength - 1 : buflength);
        1081               0 :     JS_free(cx, buf);
        1082               0 :     if (!str)
        1083               0 :         return JS_FALSE;
        1084                 :
        1085               0 :     *vp = STRING_TO_JSVAL(str);
        1086               0 :     return JS_TRUE;
        1087                 : }
        1088                 :
        1089                 : static JSBool
        1090               0 : PutStr(JSContext *cx, unsigned argc, jsval *vp)
        1091                 : {
        1092                 :     jsval *argv;
        1093                 :     JSString *str;
        1094                 :     char *bytes;
        1095                 :
        1096               0 :     if (argc != 0) {
        1097               0 :         argv = JS_ARGV(cx, vp);
        1098               0 :         str = JS_ValueToString(cx, argv[0]);
        1099               0 :         if (!str)
        1100               0 :             return JS_FALSE;
        1101               0 :         bytes = JS_EncodeString(cx, str);
        1102               0 :         if (!bytes)
        1103               0 :             return JS_FALSE;
        1104               0 :         fputs(bytes, gOutFile);
        1105               0 :         JS_free(cx, bytes);
        1106               0 :         fflush(gOutFile);
        1107                 :     }
        1108                 :
        1109               0 :     JS_SET_RVAL(cx, vp, JSVAL_VOID);
        1110               0 :     return JS_TRUE;
        1111                 : }
        1112                 :
        1113                 : static JSBool
        1114               0 : Now(JSContext *cx, unsigned argc, jsval *vp)
        1115                 : {
        1116               0 :     double now = PRMJ_Now() / double(PRMJ_USEC_PER_MSEC);
        1117               0 :     JS_SET_RVAL(cx, vp, DOUBLE_TO_JSVAL(now));
        1118               0 :     return true;
        1119                 : }
        1120                 :
        1121                 : static JSBool
        1122            9036 : PrintInternal(JSContext *cx, unsigned argc, jsval *vp, FILE *file)
        1123                 : {
        1124                 :     jsval *argv;
        1125                 :     unsigned i;
        1126                 :     JSString *str;
        1127                 :     char *bytes;
        1128                 :
        1129            9036 :     argv = JS_ARGV(cx, vp);
        1130           31428 :     for (i = 0; i < argc; i++) {
        1131           22392 :         str = JS_ValueToString(cx, argv[i]);
        1132           22392 :         if (!str)
        1133               0 :             return JS_FALSE;
        1134           22392 :         bytes = JS_EncodeString(cx, str);
        1135           22392 :         if (!bytes)
        1136               0 :             return JS_FALSE;
        1137           22392 :         fprintf(file, "%s%s", i ? " " : "", bytes);
        1138           22392 :         JS_free(cx, bytes);
        1139                 :     }
        1140                 :
        1141            9036 :     fputc('\n', file);
        1142            9036 :     fflush(file);
        1143                 :
        1144            9036 :     JS_SET_RVAL(cx, vp, JSVAL_VOID);
        1145            9036 :     return JS_TRUE;
        1146                 : }
        1147                 :
        1148                 : static JSBool
        1149            9036 : Print(JSContext *cx, unsigned argc, jsval *vp)
        1150                 : {
        1151            9036 :     return PrintInternal(cx, argc, vp, gOutFile);
        1152                 : }
        1153                 :
        1154                 : static JSBool
        1155               0 : PrintErr(JSContext *cx, unsigned argc, jsval *vp)
        1156                 : {
        1157               0 :     return PrintInternal(cx, argc, vp, gErrFile);
        1158                 : }
        1159                 :
        1160                 : static JSBool
        1161                 : Help(JSContext *cx, unsigned argc, jsval *vp);
        1162                 :
        1163                 : static JSBool
        1164              27 : Quit(JSContext *cx, unsigned argc, jsval *vp)
        1165                 : {
        1166              27 :     JS_ConvertArguments(cx, argc, JS_ARGV(cx, vp), "/ i", &gExitCode);
        1167                 :
        1168              27 :     gQuitting = JS_TRUE;
        1169                 : #ifdef JS_THREADSAFE
        1170              27 :     if (gWorkerThreadPool)
        1171              27 :         js::workers::terminateAll(gWorkerThreadPool);
        1172                 : #endif
        1173              27 :     return JS_FALSE;
        1174                 : }
        1175                 :
        1176                 : static const char *
        1177             486 : ToSource(JSContext *cx, jsval *vp, JSAutoByteString *bytes)
        1178                 : {
        1179             486 :     JSString *str = JS_ValueToSource(cx, *vp);
        1180             486 :     if (str) {
        1181             486 :         *vp = STRING_TO_JSVAL(str);
        1182             486 :         if (bytes->encode(cx, str))
        1183             486 :             return bytes->ptr();
        1184                 :     }
        1185               0 :     JS_ClearPendingException(cx);
        1186               0 :     return "<<error converting value to string>>";
        1187                 : }
        1188                 :
        1189                 : static JSBool
        1190         4104914 : AssertEq(JSContext *cx, unsigned argc, jsval *vp)
        1191                 : {
        1192         4104914 :     if (!(argc == 2 || (argc == 3 && JSVAL_IS_STRING(JS_ARGV(cx, vp)[2])))) {
        1193                 :         JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
        1194                 :                              (argc < 2)
        1195                 :                              ? JSSMSG_NOT_ENOUGH_ARGS
        1196                 :                              : (argc == 3)
        1197                 :                              ? JSSMSG_INVALID_ARGS
        1198                 :                              : JSSMSG_TOO_MANY_ARGS,
        1199              27 :                              "assertEq");
        1200              27 :         return JS_FALSE;
        1201                 :     }
        1202                 :
        1203         4104887 :     jsval *argv = JS_ARGV(cx, vp);
        1204                 :     JSBool same;
        1205         4104887 :     if (!JS_SameValue(cx, argv[0], argv[1], &same))
        1206               0 :         return JS_FALSE;
        1207         4104887 :     if (!same) {
        1208             486 :         JSAutoByteString bytes0, bytes1;
        1209             243 :         const char *actual = ToSource(cx, &argv[0], &bytes0);
        1210             243 :         const char *expected = ToSource(cx, &argv[1], &bytes1);
        1211             243 :         if (argc == 2) {
        1212                 :             JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_ASSERT_EQ_FAILED,
        1213             243 :                                  actual, expected);
        1214                 :         } else {
        1215               0 :             JSAutoByteString bytes2(cx, JSVAL_TO_STRING(argv[2]));
        1216               0 :             if (!bytes2)
        1217               0 :                 return JS_FALSE;
        1218                 :             JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_ASSERT_EQ_FAILED_MSG,
        1219               0 :                                  actual, expected, bytes2.ptr());
        1220                 :         }
        1221             243 :         return JS_FALSE;
        1222                 :     }
        1223         4104644 :     JS_SET_RVAL(cx, vp, JSVAL_VOID);
        1224         4104644 :     return JS_TRUE;
        1225                 : }
        1226                 :
        1227                 : static JSBool
        1228              81 : AssertJit(JSContext *cx, unsigned argc, jsval *vp)
        1229                 : {
        1230                 : #ifdef JS_METHODJIT
        1231              81 :     if (JS_GetOptions(cx) & JSOPTION_METHODJIT) {
        1232                 :         /*
        1233                 :          * :XXX: Ignore calls to this native when inference is enabled,
        1234                 :          * with METHODJIT_ALWAYS recompilation can happen and discard the
        1235                 :          * script's jitcode.
        1236                 :          */
        1237              90 :         if (!cx->typeInferenceEnabled() &&
        1238              27 :             !cx->fp()->script()->getJIT(cx->fp()->isConstructing())) {
        1239               0 :             JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_ASSERT_JIT_FAILED);
        1240               0 :             return JS_FALSE;
        1241                 :         }
        1242                 :     }
        1243                 : #endif
        1244                 :
        1245              81 :     JS_SET_RVAL(cx, vp, JSVAL_VOID);
        1246              81 :     return JS_TRUE;
        1247                 : }
        1248                 :
        1249                 : static JSScript *
        1250             432 : ValueToScript(JSContext *cx, jsval v, JSFunction **funp = NULL)
        1251                 : {
        1252             432 :     JSScript *script = NULL;
        1253             432 :     JSFunction *fun = NULL;
        1254                 :
        1255             432 :     if (!JSVAL_IS_PRIMITIVE(v)) {
        1256             432 :         JSObject *obj = JSVAL_TO_OBJECT(v);
        1257             432 :         JSClass *clasp = JS_GetClass(obj);
        1258                 :
        1259             432 :         if (clasp == Jsvalify(&GeneratorClass)) {
        1260               9 :             if (JSGenerator *gen = (JSGenerator *) JS_GetPrivate(obj)) {
        1261               0 :                 fun = gen->floatingFrame()->fun();
        1262               0 :                 script = fun->script();
        1263                 :             }
        1264                 :         }
        1265                 :     }
        1266                 :
        1267             432 :     if (!script) {
        1268             432 :         fun = JS_ValueToFunction(cx, v);
        1269             432 :         if (!fun)
        1270               9 :             return NULL;
        1271             423 :         script = fun->maybeScript();
        1272             423 :         if (!script) {
        1273                 :             JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
        1274               0 :                                  JSSMSG_SCRIPTS_ONLY);
        1275                 :         }
        1276                 :     }
        1277             423 :     if (fun && funp)
        1278               9 :         *funp = fun;
        1279                 :
        1280             423 :     return script;
        1281                 : }
        1282                 :
        1283                 : static JSBool
        1284             279 : SetDebug(JSContext *cx, unsigned argc, jsval *vp)
        1285                 : {
        1286             279 :     jsval *argv = JS_ARGV(cx, vp);
        1287             279 :     if (argc == 0 || !JSVAL_IS_BOOLEAN(argv[0])) {
        1288                 :         JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
        1289               0 :                              JSSMSG_NOT_ENOUGH_ARGS, "setDebug");
        1290               0 :         return JS_FALSE;
        1291                 :     }
        1292                 :
        1293                 :     /*
        1294                 :      * Debug mode can only be set when there is no JS code executing on the
        1295                 :      * stack. Unfortunately, that currently means that this call will fail
        1296                 :      * unless debug mode is already set to what you're trying to set it to.
        1297                 :      * In the future, this restriction may be lifted.
        1298                 :      */
        1299                 :
        1300             279 :     JSBool ok = JS_SetDebugMode(cx, JSVAL_TO_BOOLEAN(argv[0]));
        1301             279 :     if (ok)
        1302             279 :         JS_SET_RVAL(cx, vp, JSVAL_TRUE);
        1303             279 :     return ok;
        1304                 : }
        1305                 :
        1306                 : static JSScript *
        1307             423 : GetTopScript(JSContext *cx)
        1308                 : {
        1309                 :     JSScript *script;
        1310             423 :     JS_DescribeScriptedCaller(cx, &script, NULL);
        1311             423 :     return script;
        1312                 : }
        1313                 :
        1314                 : static JSBool
        1315             414 : GetScriptAndPCArgs(JSContext *cx, unsigned argc, jsval *argv, JSScript **scriptp,
        1316                 :                    int32_t *ip)
        1317                 : {
        1318             414 :     JSScript *script = GetTopScript(cx);
        1319             414 :     *ip = 0;
        1320             414 :     if (argc != 0) {
        1321             405 :         jsval v = argv[0];
        1322             405 :         unsigned intarg = 0;
        1323             810 :         if (!JSVAL_IS_PRIMITIVE(v) &&
        1324             405 :             JS_GetClass(JSVAL_TO_OBJECT(v)) == Jsvalify(&FunctionClass)) {
        1325             405 :             script = ValueToScript(cx, v);
        1326             405 :             if (!script)
        1327               0 :                 return JS_FALSE;
        1328             405 :             intarg++;
        1329                 :         }
        1330             405 :         if (argc > intarg) {
        1331             405 :             if (!JS_ValueToInt32(cx, argv[intarg], ip))
        1332               0 :                 return JS_FALSE;
        1333             405 :             if ((uint32_t)*ip >= script->length) {
        1334               0 :                 JS_ReportError(cx, "Invalid PC");
        1335               0 :                 return JS_FALSE;
        1336                 :             }
        1337                 :         }
        1338                 :     }
        1339                 :
        1340             414 :     *scriptp = script;
        1341                 :
        1342             414 :     return JS_TRUE;
        1343                 : }
        1344                 :
        1345                 : static JSTrapStatus
        1346             378 : TrapHandler(JSContext *cx, JSScript *, jsbytecode *pc, jsval *rval,
        1347                 :             jsval closure)
        1348                 : {
        1349             378 :     JSString *str = JSVAL_TO_STRING(closure);
        1350                 :
        1351             378 :     FrameRegsIter iter(cx);
        1352             378 :     JS_ASSERT(!iter.done());
        1353                 :
        1354             378 :     JSStackFrame *caller = Jsvalify(iter.fp());
        1355             378 :     JSScript *script = iter.script();
        1356                 :
        1357                 :     size_t length;
        1358             378 :     const jschar *chars = JS_GetStringCharsAndLength(cx, str, &length);
        1359             378 :     if (!chars)
        1360               0 :         return JSTRAP_ERROR;
        1361                 :
        1362             378 :     if (!JS_EvaluateUCInStackFrame(cx, caller, chars, length,
        1363                 :                                    script->filename,
        1364                 :                                    script->lineno,
        1365             378 :                                    rval)) {
        1366               0 :         return JSTRAP_ERROR;
        1367                 :     }
        1368             378 :     if (!JSVAL_IS_VOID(*rval))
        1369              63 :         return JSTRAP_RETURN;
        1370             315 :     return JSTRAP_CONTINUE;
        1371                 : }
        1372                 :
        1373                 : static JSBool
        1374             369 : Trap(JSContext *cx, unsigned argc, jsval *vp)
        1375                 : {
        1376                 :     JSString *str;
        1377                 :     JSScript *script;
        1378                 :     int32_t i;
        1379                 :
        1380             369 :     jsval *argv = JS_ARGV(cx, vp);
        1381             369 :     if (argc == 0) {
        1382               0 :         JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_TRAP_USAGE);
        1383               0 :         return JS_FALSE;
        1384                 :     }
        1385             369 :     argc--;
        1386             369 :     str = JS_ValueToString(cx, argv[argc]);
        1387             369 :     if (!str)
        1388               0 :         return JS_FALSE;
        1389             369 :     argv[argc] = STRING_TO_JSVAL(str);
        1390             369 :     if (!GetScriptAndPCArgs(cx, argc, argv, &script, &i))
        1391               0 :         return JS_FALSE;
        1392             369 :     if (uint32_t(i) >= script->length) {
        1393               0 :         JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_TRAP_USAGE);
        1394               0 :         return JS_FALSE;
        1395                 :     }
        1396             369 :     JS_SET_RVAL(cx, vp, JSVAL_VOID);
        1397             369 :     return JS_SetTrap(cx, script, script->code + i, TrapHandler, STRING_TO_JSVAL(str));
        1398                 : }
        1399                 :
        1400                 : static JSBool
        1401              36 : Untrap(JSContext *cx, unsigned argc, jsval *vp)
        1402                 : {
        1403                 :     JSScript *script;
        1404                 :     int32_t i;
        1405                 :
        1406              36 :     if (!GetScriptAndPCArgs(cx, argc, JS_ARGV(cx, vp), &script, &i))
        1407               0 :         return JS_FALSE;
        1408              36 :     JS_ClearTrap(cx, script, script->code + i, NULL, NULL);
        1409              36 :     JS_SET_RVAL(cx, vp, JSVAL_VOID);
        1410              36 :     return JS_TRUE;
        1411                 : }
        1412                 :
        1413                 : static JSTrapStatus
        1414              36 : DebuggerAndThrowHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval,
        1415                 :                         void *closure)
        1416                 : {
        1417              36 :     return TrapHandler(cx, script, pc, rval, STRING_TO_JSVAL((JSString *)closure));
        1418                 : }
        1419                 :
        1420                 : static JSBool
        1421              18 : SetDebuggerHandler(JSContext *cx, unsigned argc, jsval *vp)
        1422                 : {
        1423                 :     JSString *str;
        1424              18 :     if (argc == 0) {
        1425                 :         JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
        1426               0 :                              JSSMSG_NOT_ENOUGH_ARGS, "setDebuggerHandler");
        1427               0 :         return JS_FALSE;
        1428                 :     }
        1429                 :
        1430              18 :     str = JS_ValueToString(cx, JS_ARGV(cx, vp)[0]);
        1431              18 :     if (!str)
        1432               0 :         return JS_FALSE;
        1433                 :
        1434              18 :     JS_SetDebuggerHandler(cx->runtime, DebuggerAndThrowHandler, str);
        1435              18 :     JS_SET_RVAL(cx, vp, JSVAL_VOID);
        1436              18 :     return JS_TRUE;
        1437                 : }
        1438                 :
        1439                 : static JSBool
        1440              18 : SetThrowHook(JSContext *cx, unsigned argc, jsval *vp)
        1441                 : {
        1442                 :     JSString *str;
        1443              18 :     if (argc == 0) {
        1444                 :         JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
        1445               0 :                              JSSMSG_NOT_ENOUGH_ARGS, "setThrowHook");
        1446               0 :         return JS_FALSE;
        1447                 :     }
        1448                 :
        1449              18 :     str = JS_ValueToString(cx, JS_ARGV(cx, vp)[0]);
        1450              18 :     if (!str)
        1451               0 :         return JS_FALSE;
        1452                 :
        1453              18 :     JS_SetThrowHook(cx->runtime, DebuggerAndThrowHandler, str);
        1454              18 :     JS_SET_RVAL(cx, vp, JSVAL_VOID);
        1455              18 :     return JS_TRUE;
        1456                 : }
        1457                 :
        1458                 : static JSBool
        1459               9 : LineToPC(JSContext *cx, unsigned argc, jsval *vp)
        1460                 : {
        1461                 :     JSScript *script;
        1462               9 :     int32_t lineArg = 0;
        1463                 :     uint32_t lineno;
        1464                 :     jsbytecode *pc;
        1465                 :
        1466               9 :     if (argc == 0) {
        1467               0 :         JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_LINE2PC_USAGE);
        1468               0 :         return JS_FALSE;
        1469                 :     }
        1470               9 :     script = GetTopScript(cx);
        1471               9 :     jsval v = JS_ARGV(cx, vp)[0];
        1472              18 :     if (!JSVAL_IS_PRIMITIVE(v) &&
        1473               9 :         JS_GetClass(JSVAL_TO_OBJECT(v)) == Jsvalify(&FunctionClass))
        1474                 :     {
        1475               9 :         script = ValueToScript(cx, v);
        1476               9 :         if (!script)
        1477               0 :             return JS_FALSE;
        1478               9 :         lineArg++;
        1479                 :     }
        1480               9 :     if (!JS_ValueToECMAUint32(cx, JS_ARGV(cx, vp)[lineArg], &lineno))
        1481               0 :         return JS_FALSE;
        1482               9 :     pc = JS_LineNumberToPC(cx, script, lineno);
        1483               9 :     if (!pc)
        1484               0 :         return JS_FALSE;
        1485               9 :     *vp = INT_TO_JSVAL(pc - script->code);
        1486               9 :     return JS_TRUE;
        1487                 : }
        1488                 :
        1489                 : static JSBool
        1490               9 : PCToLine(JSContext *cx, unsigned argc, jsval *vp)
        1491                 : {
        1492                 :     JSScript *script;
        1493                 :     int32_t i;
        1494                 :     unsigned lineno;
        1495                 :
        1496               9 :     if (!GetScriptAndPCArgs(cx, argc, JS_ARGV(cx, vp), &script, &i))
        1497               0 :         return JS_FALSE;
        1498               9 :     lineno = JS_PCToLineNumber(cx, script, script->code + i);
        1499               9 :     if (!lineno)
        1500               0 :         return JS_FALSE;
        1501               9 :     *vp = INT_TO_JSVAL(lineno);
        1502               9 :     return JS_TRUE;
        1503                 : }
        1504                 :
        1505                 : #ifdef DEBUG
        1506                 :
        1507                 : static void
        1508               9 : UpdateSwitchTableBounds(JSContext *cx, JSScript *script, unsigned offset,
        1509                 :                         unsigned *start, unsigned *end)
        1510                 : {
        1511                 :     jsbytecode *pc;
        1512                 :     JSOp op;
        1513                 :     ptrdiff_t jmplen;
        1514                 :     int32_t low, high, n;
        1515                 :
        1516               9 :     pc = script->code + offset;
        1517               9 :     op = JSOp(*pc);
        1518               9 :     switch (op) {
        1519                 :       case JSOP_TABLESWITCH:
        1520               9 :         jmplen = JUMP_OFFSET_LEN;
        1521               9 :         pc += jmplen;
        1522               9 :         low = GET_JUMP_OFFSET(pc);
        1523               9 :         pc += JUMP_OFFSET_LEN;
        1524               9 :         high = GET_JUMP_OFFSET(pc);
        1525               9 :         pc += JUMP_OFFSET_LEN;
        1526               9 :         n = high - low + 1;
        1527               9 :         break;
        1528                 :
        1529                 :       case JSOP_LOOKUPSWITCH:
        1530               0 :         jmplen = JUMP_OFFSET_LEN;
        1531               0 :         pc += jmplen;
        1532               0 :         n = GET_UINT16(pc);
        1533               0 :         pc += UINT16_LEN;
        1534               0 :         jmplen += JUMP_OFFSET_LEN;
        1535               0 :         break;
        1536                 :
        1537                 :       default:
        1538                 :         /* [condswitch] switch does not have any jump or lookup tables. */
        1539               0 :         JS_ASSERT(op == JSOP_CONDSWITCH);
        1540               0 :         return;
        1541                 :     }
        1542                 :
        1543               9 :     *start = (unsigned)(pc - script->code);
        1544               9 :     *end = *start + (unsigned)(n * jmplen);
        1545                 : }
        1546                 :
        1547                 : static void
        1548               9 : SrcNotes(JSContext *cx, JSScript *script, Sprinter *sp)
        1549                 : {
        1550               9 :     Sprint(sp, "\nSource notes:\n");
        1551                 :     Sprint(sp, "%4s  %4s %5s %6s %-8s %s\n",
        1552               9 :            "ofs", "line", "pc", "delta", "desc", "args");
        1553               9 :     Sprint(sp, "---- ---- ----- ------ -------- ------\n");
        1554               9 :     unsigned offset = 0;
        1555               9 :     unsigned lineno = script->lineno;
        1556               9 :     jssrcnote *notes = script->notes();
        1557               9 :     unsigned switchTableEnd = 0, switchTableStart = 0;
        1558          110574 :     for (jssrcnote *sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
        1559          110565 :         unsigned delta = SN_DELTA(sn);
        1560          110565 :         offset += delta;
        1561          110565 :         SrcNoteType type = (SrcNoteType) SN_TYPE(sn);
        1562          110565 :         const char *name = js_SrcNoteSpec[type].name;
        1563          110565 :         if (type == SRC_LABEL) {
        1564                 :             /* Check if the source note is for a switch case. */
        1565               0 :             if (switchTableStart <= offset && offset < switchTableEnd) {
        1566               0 :                 name = "case";
        1567                 :             } else {
        1568               0 :                 JSOp op = JSOp(script->code[offset]);
        1569               0 :                 JS_ASSERT(op == JSOP_LABEL);
        1570                 :             }
        1571                 :         }
        1572          110565 :         Sprint(sp, "%3u: %4u %5u [%4u] %-8s", unsigned(sn - notes), lineno, offset, delta, name);
        1573          110565 :         switch (type) {
        1574                 :           case SRC_SETLINE:
        1575               0 :             lineno = js_GetSrcNoteOffset(sn, 0);
        1576               0 :             Sprint(sp, " lineno %u", lineno);
        1577               0 :             break;
        1578                 :           case SRC_NEWLINE:
        1579               0 :             ++lineno;
        1580               0 :             break;
        1581                 :           case SRC_FOR:
        1582                 :             Sprint(sp, " cond %u update %u tail %u",
        1583               0 :                    unsigned(js_GetSrcNoteOffset(sn, 0)),
        1584               0 :                    unsigned(js_GetSrcNoteOffset(sn, 1)),
        1585               0 :                    unsigned(js_GetSrcNoteOffset(sn, 2)));
        1586               0 :             break;
        1587                 :           case SRC_IF_ELSE:
        1588                 :             Sprint(sp, " else %u elseif %u",
        1589               0 :                    unsigned(js_GetSrcNoteOffset(sn, 0)),
        1590               0 :                    unsigned(js_GetSrcNoteOffset(sn, 1)));
        1591               0 :             break;
        1592                 :           case SRC_COND:
        1593                 :           case SRC_WHILE:
        1594                 :           case SRC_PCBASE:
        1595                 :           case SRC_PCDELTA:
        1596                 :           case SRC_DECL:
        1597                 :           case SRC_BRACE:
        1598               0 :             Sprint(sp, " offset %u", unsigned(js_GetSrcNoteOffset(sn, 0)));
        1599               0 :             break;
        1600                 :           case SRC_LABEL:
        1601                 :           case SRC_LABELBRACE:
        1602                 :           case SRC_BREAK2LABEL:
        1603                 :           case SRC_CONT2LABEL: {
        1604               0 :             uint32_t index = js_GetSrcNoteOffset(sn, 0);
        1605               0 :             JSAtom *atom = script->getAtom(index);
        1606               0 :             Sprint(sp, " atom %u (", index);
        1607               0 :             size_t len = PutEscapedString(NULL, 0, atom, '\0');
        1608               0 :             if (char *buf = sp->reserve(len)) {
        1609               0 :                 PutEscapedString(buf, len, atom, 0);
        1610               0 :                 buf[len] = 0;
        1611                 :             }
        1612               0 :             Sprint(sp, ")");
        1613               0 :             break;
        1614                 :           }
        1615                 :           case SRC_FUNCDEF: {
        1616               0 :             uint32_t index = js_GetSrcNoteOffset(sn, 0);
        1617               0 :             JSObject *obj = script->getObject(index);
        1618               0 :             JSFunction *fun = obj->toFunction();
        1619               0 :             JSString *str = JS_DecompileFunction(cx, fun, JS_DONT_PRETTY_PRINT);
        1620               0 :             JSAutoByteString bytes;
        1621               0 :             if (!str || !bytes.encode(cx, str))
        1622               0 :                 ReportException(cx);
        1623               0 :             Sprint(sp, " function %u (%s)", index, !!bytes ? bytes.ptr() : "N/A");
        1624                 :             break;
        1625                 :           }
        1626                 :           case SRC_SWITCH: {
        1627              18 :             JSOp op = JSOp(script->code[offset]);
        1628              18 :             if (op == JSOP_GOTO)
        1629               9 :                 break;
        1630               9 :             Sprint(sp, " length %u", unsigned(js_GetSrcNoteOffset(sn, 0)));
        1631               9 :             unsigned caseOff = (unsigned) js_GetSrcNoteOffset(sn, 1);
        1632               9 :             if (caseOff)
        1633               0 :                 Sprint(sp, " first case offset %u", caseOff);
        1634                 :             UpdateSwitchTableBounds(cx, script, offset,
        1635               9 :                                     &switchTableStart, &switchTableEnd);
        1636               9 :             break;
        1637                 :           }
        1638                 :           case SRC_CATCH:
        1639           36846 :             delta = (unsigned) js_GetSrcNoteOffset(sn, 0);
        1640           36846 :             if (delta) {
        1641           18423 :                 if (script->main()[offset] == JSOP_LEAVEBLOCK)
        1642           18423 :                     Sprint(sp, " stack depth %u", delta);
        1643                 :                 else
        1644               0 :                     Sprint(sp, " guard delta %u", delta);
        1645                 :             }
        1646           36846 :             break;
        1647                 :           default:;
        1648                 :         }
        1649          110565 :         Sprint(sp, "\n");
        1650                 :     }
        1651               9 : }
        1652                 :
        1653                 : static JSBool
        1654               0 : Notes(JSContext *cx, unsigned argc, jsval *vp)
        1655                 : {
        1656               0 :     Sprinter sprinter(cx);
        1657               0 :     if (!sprinter.init())
        1658               0 :         return JS_FALSE;
        1659                 :
        1660               0 :     jsval *argv = JS_ARGV(cx, vp);
        1661               0 :     for (unsigned i = 0; i < argc; i++) {
        1662               0 :         JSScript *script = ValueToScript(cx, argv[i]);
        1663               0 :         if (!script)
        1664               0 :             continue;
        1665                 :
        1666               0 :         SrcNotes(cx, script, &sprinter);
        1667                 :     }
        1668                 :
        1669               0 :     JSString *str = JS_NewStringCopyZ(cx, sprinter.string());
        1670               0 :     if (!str)
        1671               0 :         return JS_FALSE;
        1672               0 :     JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(str));
        1673               0 :     return JS_TRUE;
        1674                 : }
        1675                 :
        1676                 : JS_STATIC_ASSERT(JSTRY_CATCH == 0);
        1677                 : JS_STATIC_ASSERT(JSTRY_FINALLY == 1);
        1678                 : JS_STATIC_ASSERT(JSTRY_ITER == 2);
        1679                 :
        1680                 : static const char* const TryNoteNames[] = { "catch", "finally", "iter" };
        1681                 :
        1682                 : static JSBool
        1683               9 : TryNotes(JSContext *cx, JSScript *script, Sprinter *sp)
        1684                 : {
        1685                 :     JSTryNote *tn, *tnlimit;
        1686                 :
        1687               9 :     if (!JSScript::isValidOffset(script->trynotesOffset))
        1688               0 :         return JS_TRUE;
        1689                 :
        1690               9 :     tn = script->trynotes()->vector;
        1691               9 :     tnlimit = tn + script->trynotes()->length;
        1692               9 :     Sprint(sp, "\nException table:\nkind      stack    start      end\n");
        1693           18423 :     do {
        1694           18423 :         JS_ASSERT(tn->kind < ArrayLength(TryNoteNames));
        1695                 :         Sprint(sp, " %-7s %6u %8u %8u\n",
        1696           18423 :                TryNoteNames[tn->kind], tn->stackDepth,
        1697           36846 :                tn->start, tn->start + tn->length);
        1698                 :     } while (++tn != tnlimit);
        1699               9 :     return JS_TRUE;
        1700                 : }
        1701                 :
        1702                 : static bool
        1703               9 : DisassembleScript(JSContext *cx, JSScript *script, JSFunction *fun, bool lines, bool recursive,
        1704                 :                   Sprinter *sp)
        1705                 : {
        1706               9 :     if (fun && (fun->flags & ~7U)) {
        1707               9 :         uint16_t flags = fun->flags;
        1708               9 :         Sprint(sp, "flags:");
        1709                 :
        1710                 : #define SHOW_FLAG(flag) if (flags & JSFUN_##flag) Sprint(sp, " " #flag);
        1711                 :
        1712               9 :         SHOW_FLAG(LAMBDA);
        1713               9 :         SHOW_FLAG(HEAVYWEIGHT);
        1714               9 :         SHOW_FLAG(EXPR_CLOSURE);
        1715                 :
        1716                 : #undef SHOW_FLAG
        1717                 :
        1718               9 :         if (fun->isNullClosure())
        1719               0 :             Sprint(sp, " NULL_CLOSURE");
        1720               9 :         else if (fun->isFlatClosure())
        1721               0 :             Sprint(sp, " FLAT_CLOSURE");
        1722                 :
        1723               9 :         JSScript *script = fun->script();
        1724               9 :         if (script->bindings.hasUpvars()) {
        1725               0 :             Sprint(sp, "\nupvars: {\n");
        1726                 :
        1727               0 :             Vector<JSAtom *> localNames(cx);
        1728               0 :             if (!script->bindings.getLocalNameArray(cx, &localNames))
        1729               0 :                 return false;
        1730                 :
        1731               0 :             JSUpvarArray *uva = script->upvars();
        1732               0 :             unsigned upvar_base = script->bindings.countArgsAndVars();
        1733                 :
        1734               0 :             for (uint32_t i = 0, n = uva->length; i < n; i++) {
        1735               0 :                 JSAtom *atom = localNames[upvar_base + i];
        1736               0 :                 UpvarCookie cookie = uva->vector[i];
        1737               0 :                 JSAutoByteString printable;
        1738               0 :                 if (js_AtomToPrintableString(cx, atom, &printable)) {
        1739                 :                     Sprint(sp, "  %s: {skip:%u, slot:%u},\n",
        1740               0 :                            printable.ptr(), cookie.level(), cookie.slot());
        1741                 :                 }
        1742                 :             }
        1743                 :
        1744               0 :             Sprint(sp, "}");
        1745                 :         }
        1746               9 :         Sprint(sp, "\n");
        1747                 :     }
        1748                 :
        1749               9 :     if (!js_Disassemble(cx, script, lines, sp))
        1750               0 :         return false;
        1751               9 :     SrcNotes(cx, script, sp);
        1752               9 :     TryNotes(cx, script, sp);
        1753                 :
        1754               9 :     if (recursive && JSScript::isValidOffset(script->objectsOffset)) {
        1755               9 :         JSObjectArray *objects = script->objects();
        1756           18432 :         for (unsigned i = 0; i != objects->length; ++i) {
        1757           18423 :             JSObject *obj = objects->vector[i];
        1758           18423 :             if (obj->isFunction()) {
        1759               0 :                 Sprint(sp, "\n");
        1760               0 :                 JSFunction *fun = obj->toFunction();
        1761               0 :                 JSScript *nested = fun->maybeScript();
        1762               0 :                 if (!DisassembleScript(cx, nested, fun, lines, recursive, sp))
        1763               0 :                     return false;
        1764                 :             }
        1765                 :         }
        1766                 :     }
        1767               9 :     return true;
        1768                 : }
        1769                 :
        1770                 : namespace {
        1771                 :
        1772                 : struct DisassembleOptionParser {
        1773                 :     unsigned   argc;
        1774                 :     jsval   *argv;
        1775                 :     bool    lines;
        1776                 :     bool    recursive;
        1777                 :
        1778              18 :     DisassembleOptionParser(unsigned argc, jsval *argv)
        1779              18 :       : argc(argc), argv(argv), lines(false), recursive(false) {}
        1780                 :
        1781              18 :     bool parse(JSContext *cx) {
        1782                 :         /* Read options off early arguments */
        1783              45 :         while (argc > 0 && JSVAL_IS_STRING(argv[0])) {
        1784               9 :             JSString *str = JSVAL_TO_STRING(argv[0]);
        1785               9 :             JSFlatString *flatStr = JS_FlattenString(cx, str);
        1786               9 :             if (!flatStr)
        1787               0 :                 return false;
        1788               9 :             if (JS_FlatStringEqualsAscii(flatStr, "-l"))
        1789               0 :                 lines = true;
        1790               9 :             else if (JS_FlatStringEqualsAscii(flatStr, "-r"))
        1791               9 :                 recursive = true;
        1792                 :             else
        1793               0 :                 break;
        1794               9 :             argv++, argc--;
        1795                 :         }
        1796              18 :         return true;
        1797                 :     }
        1798                 : };
        1799                 :
        1800                 : } /* anonymous namespace */
        1801                 :
        1802                 : static JSBool
        1803               9 : DisassembleToString(JSContext *cx, unsigned argc, jsval *vp)
        1804                 : {
        1805               9 :     DisassembleOptionParser p(argc, JS_ARGV(cx, vp));
        1806               9 :     if (!p.parse(cx))
        1807               0 :         return false;
        1808                 :
        1809              18 :     Sprinter sprinter(cx);
        1810               9 :     if (!sprinter.init())
        1811               0 :         return false;
        1812                 :
        1813               9 :     bool ok = true;
        1814               9 :     if (p.argc == 0) {
        1815                 :         /* Without arguments, disassemble the current script. */
        1816               0 :         if (JSScript *script = GetTopScript(cx)) {
        1817               0 :             if (js_Disassemble(cx, script, p.lines, &sprinter)) {
        1818               0 :                 SrcNotes(cx, script, &sprinter);
        1819               0 :                 TryNotes(cx, script, &sprinter);
        1820                 :             } else {
        1821               0 :                 ok = false;
        1822                 :             }
        1823                 :         }
        1824                 :     } else {
        1825              18 :         for (unsigned i = 0; i < p.argc; i++) {
        1826                 :             JSFunction *fun;
        1827               9 :             JSScript *script = ValueToScript(cx, p.argv[i], &fun);
        1828               9 :             ok = ok && script && DisassembleScript(cx, script, fun, p.lines, p.recursive, &sprinter);
        1829                 :         }
        1830                 :     }
        1831                 :
        1832               9 :     JSString *str = ok ? JS_NewStringCopyZ(cx, sprinter.string()) : NULL;
        1833               9 :     if (!str)
        1834               0 :         return false;
        1835               9 :     JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(str));
        1836               9 :     return true;
        1837                 : }
        1838                 :
        1839                 : static JSBool
        1840               9 : Disassemble(JSContext *cx, unsigned argc, jsval *vp)
        1841                 : {
        1842               9 :     DisassembleOptionParser p(argc, JS_ARGV(cx, vp));
        1843               9 :     if (!p.parse(cx))
        1844               0 :         return false;
        1845                 :
        1846              18 :     Sprinter sprinter(cx);
        1847               9 :     if (!sprinter.init())
        1848               0 :         return false;
        1849                 :
        1850               9 :     bool ok = true;
        1851               9 :     if (p.argc == 0) {
        1852                 :         /* Without arguments, disassemble the current script. */
        1853               0 :         if (JSScript *script = GetTopScript(cx)) {
        1854               0 :             if (js_Disassemble(cx, script, p.lines, &sprinter)) {
        1855               0 :                 SrcNotes(cx, script, &sprinter);
        1856               0 :                 TryNotes(cx, script, &sprinter);
        1857                 :             } else {
        1858               0 :                 ok = false;
        1859                 :             }
        1860                 :         }
        1861                 :     } else {
        1862              18 :         for (unsigned i = 0; i < p.argc; i++) {
        1863                 :             JSFunction *fun;
        1864               9 :             JSScript *script = ValueToScript(cx, p.argv[i], &fun);
        1865               9 :             ok = ok && script && DisassembleScript(cx, script, fun, p.lines, p.recursive, &sprinter);
        1866                 :         }
        1867                 :     }
        1868                 :
        1869               9 :     if (ok)
        1870               0 :         fprintf(stdout, "%s\n", sprinter.string());
        1871               9 :     JS_SET_RVAL(cx, vp, JSVAL_VOID);
        1872               9 :     return ok;
        1873                 : }
        1874                 :
        1875                 : static JSBool
        1876               0 : DisassFile(JSContext *cx, unsigned argc, jsval *vp)
        1877                 : {
        1878                 :     /* Support extra options at the start, just like Dissassemble. */
        1879               0 :     DisassembleOptionParser p(argc, JS_ARGV(cx, vp));
        1880               0 :     if (!p.parse(cx))
        1881               0 :         return false;
        1882                 :
        1883               0 :     if (!p.argc) {
        1884               0 :         JS_SET_RVAL(cx, vp, JSVAL_VOID);
        1885               0 :         return JS_TRUE;
        1886                 :     }
        1887                 :
        1888               0 :     JSObject *thisobj = JS_THIS_OBJECT(cx, vp);
        1889               0 :     if (!thisobj)
        1890               0 :         return JS_FALSE;
        1891                 :
        1892               0 :     JSString *str = JS_ValueToString(cx, p.argv[0]);
        1893               0 :     if (!str)
        1894               0 :         return JS_FALSE;
        1895               0 :     JSAutoByteString filename(cx, str);
        1896               0 :     if (!filename)
        1897               0 :         return JS_FALSE;
        1898                 :
        1899               0 :     uint32_t oldopts = JS_GetOptions(cx);
        1900               0 :     JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO | JSOPTION_NO_SCRIPT_RVAL);
        1901               0 :     JSScript *script = JS_CompileUTF8File(cx, thisobj, filename.ptr());
        1902               0 :     JS_SetOptions(cx, oldopts);
        1903               0 :     if (!script)
        1904               0 :         return false;
        1905                 :
        1906               0 :     Sprinter sprinter(cx);
        1907               0 :     if (!sprinter.init())
        1908               0 :         return false;
        1909               0 :     bool ok = DisassembleScript(cx, script, NULL, p.lines, p.recursive, &sprinter);
        1910               0 :     if (ok)
        1911               0 :         fprintf(stdout, "%s\n", sprinter.string());
        1912               0 :     if (!ok)
        1913               0 :         return false;
        1914                 :
        1915               0 :     JS_SET_RVAL(cx, vp, JSVAL_VOID);
        1916               0 :     return true;
        1917                 : }
        1918                 :
        1919                 : static JSBool
        1920               0 : DisassWithSrc(JSContext *cx, unsigned argc, jsval *vp)
        1921                 : {
        1922                 : #define LINE_BUF_LEN 512
        1923                 :     unsigned i, len, line1, line2, bupline;
        1924                 :     JSScript *script;
        1925                 :     FILE *file;
        1926                 :     char linebuf[LINE_BUF_LEN];
        1927                 :     jsbytecode *pc, *end;
        1928                 :     JSBool ok;
        1929                 :     static char sep[] = ";-------------------------";
        1930                 :
        1931               0 :     ok = JS_TRUE;
        1932               0 :     jsval *argv = JS_ARGV(cx, vp);
        1933               0 :     for (i = 0; ok && i < argc; i++) {
        1934               0 :         script = ValueToScript(cx, argv[i]);
        1935               0 :         if (!script)
        1936               0 :            return JS_FALSE;
        1937                 :
        1938               0 :         if (!script->filename) {
        1939                 :             JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
        1940               0 :                                  JSSMSG_FILE_SCRIPTS_ONLY);
        1941               0 :             return JS_FALSE;
        1942                 :         }
        1943                 :
        1944               0 :         file = fopen(script->filename, "r");
        1945               0 :         if (!file) {
        1946                 :             JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
        1947                 :                                  JSSMSG_CANT_OPEN, script->filename,
        1948               0 :                                  strerror(errno));
        1949               0 :             return JS_FALSE;
        1950                 :         }
        1951                 :
        1952               0 :         pc = script->code;
        1953               0 :         end = pc + script->length;
        1954                 :
        1955               0 :         Sprinter sprinter(cx);
        1956               0 :         if (!sprinter.init()) {
        1957               0 :             ok = JS_FALSE;
        1958               0 :             goto bail;
        1959                 :         }
        1960                 :
        1961                 :         /* burn the leading lines */
        1962               0 :         line2 = JS_PCToLineNumber(cx, script, pc);
        1963               0 :         for (line1 = 0; line1 < line2 - 1; line1++) {
        1964               0 :             char *tmp = fgets(linebuf, LINE_BUF_LEN, file);
        1965               0 :             if (!tmp) {
        1966               0 :                 JS_ReportError(cx, "failed to read %s fully", script->filename);
        1967               0 :                 ok = JS_FALSE;
        1968               0 :                 goto bail;
        1969                 :             }
        1970                 :         }
        1971                 :
        1972               0 :         bupline = 0;
        1973               0 :         while (pc < end) {
        1974               0 :             line2 = JS_PCToLineNumber(cx, script, pc);
        1975                 :
        1976               0 :             if (line2 < line1) {
        1977               0 :                 if (bupline != line2) {
        1978               0 :                     bupline = line2;
        1979               0 :                     Sprint(&sprinter, "%s %3u: BACKUP\n", sep, line2);
        1980                 :                 }
        1981                 :             } else {
        1982               0 :                 if (bupline && line1 == line2)
        1983               0 :                     Sprint(&sprinter, "%s %3u: RESTORE\n", sep, line2);
        1984               0 :                 bupline = 0;
        1985               0 :                 while (line1 < line2) {
        1986               0 :                     if (!fgets(linebuf, LINE_BUF_LEN, file)) {
        1987                 :                         JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
        1988                 :                                              JSSMSG_UNEXPECTED_EOF,
        1989               0 :                                              script->filename);
        1990               0 :                         ok = JS_FALSE;
        1991               0 :                         goto bail;
        1992                 :                     }
        1993               0 :                     line1++;
        1994               0 :                     Sprint(&sprinter, "%s %3u: %s", sep, line1, linebuf);
        1995                 :                 }
        1996                 :             }
        1997                 :
        1998               0 :             len = js_Disassemble1(cx, script, pc, pc - script->code, JS_TRUE, &sprinter);
        1999               0 :             if (!len) {
        2000               0 :                 ok = JS_FALSE;
        2001               0 :                 goto bail;
        2002                 :             }
        2003               0 :             pc += len;
        2004                 :         }
        2005                 :
        2006                 :       bail:
        2007               0 :         fclose(file);
        2008                 :     }
        2009               0 :     JS_SET_RVAL(cx, vp, JSVAL_VOID);
        2010               0 :     return ok;
        2011                 : #undef LINE_BUF_LEN
        2012                 : }
        2013                 :
        2014                 : static void
        2015               0 : DumpScope(JSContext *cx, JSObject *obj, FILE *fp)
        2016                 : {
        2017               0 :     unsigned i = 0;
        2018               0 :     for (JSScopeProperty *sprop = NULL; JS_PropertyIterator(obj, &sprop);) {
        2019               0 :         fprintf(fp, "%3u %p ", i++, (void *) sprop);
        2020               0 :         ((Shape *) sprop)->dump(cx, fp);
        2021                 :     }
        2022               0 : }
        2023                 :
        2024                 : static JSBool
        2025               0 : DumpStats(JSContext *cx, unsigned argc, jsval *vp)
        2026                 : {
        2027               0 :     jsval *argv = JS_ARGV(cx, vp);
        2028               0 :     for (unsigned i = 0; i < argc; i++) {
        2029               0 :         JSString *str = JS_ValueToString(cx, argv[i]);
        2030               0 :         if (!str)
        2031               0 :             return JS_FALSE;
        2032               0 :         argv[i] = STRING_TO_JSVAL(str);
        2033               0 :         JSFlatString *flatStr = JS_FlattenString(cx, str);
        2034               0 :         if (!flatStr)
        2035               0 :             return JS_FALSE;
        2036               0 :         if (JS_FlatStringEqualsAscii(flatStr, "atom")) {
        2037               0 :             js_DumpAtoms(cx, gOutFile);
        2038               0 :         } else if (JS_FlatStringEqualsAscii(flatStr, "global")) {
        2039               0 :             DumpScope(cx, cx->globalObject, stdout);
        2040                 :         } else {
        2041               0 :             fputs("js: invalid stats argument ", gErrFile);
        2042               0 :             JS_FileEscapedString(gErrFile, str, 0);
        2043               0 :             putc('\n', gErrFile);
        2044               0 :             continue;
        2045                 :         }
        2046                 :     }
        2047               0 :     JS_SET_RVAL(cx, vp, JSVAL_VOID);
        2048               0 :     return JS_TRUE;
        2049                 : }
        2050                 :
        2051                 : static JSBool
        2052               0 : DumpHeap(JSContext *cx, unsigned argc, jsval *vp)
        2053                 : {
        2054                 :     jsval v;
        2055                 :     void* startThing;
        2056                 :     JSGCTraceKind startTraceKind;
        2057                 :     const char *badTraceArg;
        2058                 :     void *thingToFind;
        2059                 :     size_t maxDepth;
        2060                 :     void *thingToIgnore;
        2061                 :     FILE *dumpFile;
        2062                 :     JSBool ok;
        2063                 :
        2064               0 :     const char *fileName = NULL;
        2065               0 :     JSAutoByteString fileNameBytes;
        2066               0 :     if (argc > 0) {
        2067               0 :         v = JS_ARGV(cx, vp)[0];
        2068               0 :         if (!JSVAL_IS_NULL(v)) {
        2069                 :             JSString *str;
        2070                 :
        2071               0 :             str = JS_ValueToString(cx, v);
        2072               0 :             if (!str)
        2073               0 :                 return JS_FALSE;
        2074               0 :             JS_ARGV(cx, vp)[0] = STRING_TO_JSVAL(str);
        2075               0 :             if (!fileNameBytes.encode(cx, str))
        2076               0 :                 return JS_FALSE;
        2077               0 :             fileName = fileNameBytes.ptr();
        2078                 :         }
        2079                 :     }
        2080                 :
        2081               0 :     startThing = NULL;
        2082               0 :     startTraceKind = JSTRACE_OBJECT;
        2083               0 :     if (argc > 1) {
        2084               0 :         v = JS_ARGV(cx, vp)[1];
        2085               0 :         if (JSVAL_IS_TRACEABLE(v)) {
        2086               0 :             startThing = JSVAL_TO_TRACEABLE(v);
        2087               0 :             startTraceKind = JSVAL_TRACE_KIND(v);
        2088               0 :         } else if (!JSVAL_IS_NULL(v)) {
        2089               0 :             badTraceArg = "start";
        2090               0 :             goto not_traceable_arg;
        2091                 :         }
        2092                 :     }
        2093                 :
        2094               0 :     thingToFind = NULL;
        2095               0 :     if (argc > 2) {
        2096               0 :         v = JS_ARGV(cx, vp)[2];
        2097               0 :         if (JSVAL_IS_TRACEABLE(v)) {
        2098               0 :             thingToFind = JSVAL_TO_TRACEABLE(v);
        2099               0 :         } else if (!JSVAL_IS_NULL(v)) {
        2100               0 :             badTraceArg = "toFind";
        2101               0 :             goto not_traceable_arg;
        2102                 :         }
        2103                 :     }
        2104                 :
        2105               0 :     maxDepth = (size_t)-1;
        2106               0 :     if (argc > 3) {
        2107               0 :         v = JS_ARGV(cx, vp)[3];
        2108               0 :         if (!JSVAL_IS_NULL(v)) {
        2109                 :             uint32_t depth;
        2110                 :
        2111               0 :             if (!JS_ValueToECMAUint32(cx, v, &depth))
        2112               0 :                 return JS_FALSE;
        2113               0 :             maxDepth = depth;
        2114                 :         }
        2115                 :     }
        2116                 :
        2117               0 :     thingToIgnore = NULL;
        2118               0 :     if (argc > 4) {
        2119               0 :         v = JS_ARGV(cx, vp)[4];
        2120               0 :         if (JSVAL_IS_TRACEABLE(v)) {
        2121               0 :             thingToIgnore = JSVAL_TO_TRACEABLE(v);
        2122               0 :         } else if (!JSVAL_IS_NULL(v)) {
        2123               0 :             badTraceArg = "toIgnore";
        2124               0 :             goto not_traceable_arg;
        2125                 :         }
        2126                 :     }
        2127                 :
        2128               0 :     if (!fileName) {
        2129               0 :         dumpFile = stdout;
        2130                 :     } else {
        2131               0 :         dumpFile = fopen(fileName, "w");
        2132               0 :         if (!dumpFile) {
        2133               0 :             JS_ReportError(cx, "can't open %s: %s", fileName, strerror(errno));
        2134               0 :             return JS_FALSE;
        2135                 :         }
        2136                 :     }
        2137                 :
        2138                 :     ok = JS_DumpHeap(JS_GetRuntime(cx), dumpFile, startThing, startTraceKind, thingToFind,
        2139               0 :                      maxDepth, thingToIgnore);
        2140               0 :     if (dumpFile != stdout)
        2141               0 :         fclose(dumpFile);
        2142               0 :     if (!ok) {
        2143               0 :         JS_ReportOutOfMemory(cx);
        2144               0 :         return false;
        2145                 :     }
        2146               0 :     JS_SET_RVAL(cx, vp, JSVAL_VOID);
        2147               0 :     return true;
        2148                 :
        2149                 :   not_traceable_arg:
        2150                 :     JS_ReportError(cx, "argument '%s' is not null or a heap-allocated thing",
        2151               0 :                    badTraceArg);
        2152               0 :     return JS_FALSE;
        2153                 : }
        2154                 :
        2155                 : JSBool
        2156               0 : DumpObject(JSContext *cx, unsigned argc, jsval *vp)
        2157                 : {
        2158               0 :     JSObject *arg0 = NULL;
        2159               0 :     if (!JS_ConvertArguments(cx, argc, JS_ARGV(cx, vp), "o", &arg0))
        2160               0 :         return JS_FALSE;
        2161                 :
        2162               0 :     js_DumpObject(arg0);
        2163                 :
        2164               0 :     JS_SET_RVAL(cx, vp, JSVAL_VOID);
        2165               0 :     return JS_TRUE;
        2166                 : }
        2167                 :
        2168                 : #endif /* DEBUG */
        2169                 :
        2170                 : /*
        2171                 :  * This shell function is temporary (used by testStackIter.js) and should be
        2172                 :  * removed once JSD2 lands wholly subsumes the functionality here.
        2173                 :  */
        2174                 : JSBool
        2175             594 : DumpStack(JSContext *cx, unsigned argc, Value *vp)
        2176                 : {
        2177             594 :     JSObject *arr = JS_NewArrayObject(cx, 0, NULL);
        2178             594 :     if (!arr)
        2179               0 :         return false;
        2180                 :
        2181             594 :     JSString *evalStr = JS_NewStringCopyZ(cx, "eval-code");
        2182             594 :     if (!evalStr)
        2183               0 :         return false;
        2184                 :
        2185             594 :     JSString *globalStr = JS_NewStringCopyZ(cx, "global-code");
        2186             594 :     if (!globalStr)
        2187               0 :         return false;
        2188                 :
        2189             594 :     StackIter iter(cx);
        2190             594 :     JS_ASSERT(iter.nativeArgs().callee().toFunction()->native() == DumpStack);
        2191             594 :     ++iter;
        2192                 :
        2193             594 :     uint32_t index = 0;
        2194            4229 :     for (; !iter.done(); ++index, ++iter) {
        2195                 :         Value v;
        2196            3635 :         if (iter.isScript()) {
        2197            2925 :             if (iter.fp()->isNonEvalFunctionFrame()) {
        2198            2088 :                 if (!iter.fp()->getValidCalleeObject(cx, &v))
        2199               0 :                     return false;
        2200             837 :             } else if (iter.fp()->isEvalFrame()) {
        2201             243 :                 v = StringValue(evalStr);
        2202                 :             } else {
        2203             594 :                 v = StringValue(globalStr);
        2204                 :             }
        2205                 :         } else {
        2206             710 :             v = iter.nativeArgs().calleev();
        2207                 :         }
        2208            3635 :         if (!JS_SetElement(cx, arr, index, &v))
        2209               0 :             return false;
        2210                 :     }
        2211                 :
        2212             594 :     JS_SET_RVAL(cx, vp, ObjectValue(*arr));
        2213             594 :     return true;
        2214                 : }
        2215                 :
        2216                 : #ifdef TEST_CVTARGS
        2217                 : #include <ctype.h>
        2218                 :
        2219                 : static const char *
        2220                 : EscapeWideString(jschar *w)
        2221                 : {
        2222                 :     static char enuf[80];
        2223                 :     static char hex[] = "0123456789abcdef";
        2224                 :     jschar u;
        2225                 :     unsigned char b, c;
        2226                 :     int i, j;
        2227                 :
        2228                 :     if (!w)
        2229                 :         return "";
        2230                 :     for (i = j = 0; i < sizeof enuf - 1; i++, j++) {
        2231                 :         u = w[j];
        2232                 :         if (u == 0)
        2233                 :             break;
        2234                 :         b = (unsigned char)(u >> 8);
        2235                 :         c = (unsigned char)(u);
        2236                 :         if (b) {
        2237                 :             if (i >= sizeof enuf - 6)
        2238                 :                 break;
        2239                 :             enuf[i++] = '\\';
        2240                 :             enuf[i++] = 'u';
        2241                 :             enuf[i++] = hex[b >> 4];
        2242                 :             enuf[i++] = hex[b & 15];
        2243                 :             enuf[i++] = hex[c >> 4];
        2244                 :             enuf[i] = hex[c & 15];
        2245                 :         } else if (!isprint(c)) {
        2246                 :             if (i >= sizeof enuf - 4)
        2247                 :                 break;
        2248                 :             enuf[i++] = '\\';
        2249                 :             enuf[i++] = 'x';
        2250                 :             enuf[i++] = hex[c >> 4];
        2251                 :             enuf[i] = hex[c & 15];
        2252                 :         } else {
        2253                 :             enuf[i] = (char)c;
        2254                 :         }
        2255                 :     }
        2256                 :     enuf[i] = 0;
        2257                 :     return enuf;
        2258                 : }
        2259                 :
        2260                 : #include <stdarg.h>
        2261                 :
        2262                 : static JSBool
        2263                 : ZZ_formatter(JSContext *cx, const char *format, JSBool fromJS, jsval **vpp,
        2264                 :              va_list *app)
        2265                 : {
        2266                 :     jsval *vp;
        2267                 :     va_list ap;
        2268                 :     double re, im;
        2269                 :
        2270                 :     printf("entering ZZ_formatter");
        2271                 :     vp = *vpp;
        2272                 :     ap = *app;
        2273                 :     if (fromJS) {
        2274                 :         if (!JS_ValueToNumber(cx, vp[0], &re))
        2275                 :             return JS_FALSE;
        2276                 :         if (!JS_ValueToNumber(cx, vp[1], &im))
        2277                 :             return JS_FALSE;
        2278                 :         *va_arg(ap, double *) = re;
        2279                 :         *va_arg(ap, double *) = im;
        2280                 :     } else {
        2281                 :         re = va_arg(ap, double);
        2282                 :         im = va_arg(ap, double);
        2283                 :         if (!JS_NewNumberValue(cx, re, &vp[0]))
        2284                 :             return JS_FALSE;
        2285                 :         if (!JS_NewNumberValue(cx, im, &vp[1]))
        2286                 :             return JS_FALSE;
        2287                 :     }
        2288                 :     *vpp = vp + 2;
        2289                 :     *app = ap;
        2290                 :     printf("leaving ZZ_formatter");
        2291                 :     return JS_TRUE;
        2292                 : }
        2293                 :
        2294                 : static JSBool
        2295                 : ConvertArgs(JSContext *cx, unsigned argc, jsval *vp)
        2296                 : {
        2297                 :     JSBool b = JS_FALSE;
        2298                 :     jschar c = 0;
        2299                 :     int32_t i = 0, j = 0;
        2300                 :     uint32_t u = 0;
        2301                 :     double d = 0, I = 0, re = 0, im = 0;
        2302                 :     JSString *str = NULL;
        2303                 :     jschar *w = NULL;
        2304                 :     JSObject *obj2 = NULL;
        2305                 :     JSFunction *fun = NULL;
        2306                 :     jsval v = JSVAL_VOID;
        2307                 :     JSBool ok;
        2308                 :
        2309                 :     if (!JS_AddArgumentFormatter(cx, "ZZ", ZZ_formatter))
        2310                 :         return JS_FALSE;
        2311                 :     ok = JS_ConvertArguments(cx, argc, JS_ARGV(cx, vp), "b/ciujdISWofvZZ*",
        2312                 :                              &b, &c, &i, &u, &j, &d, &I, &str, &w, &obj2,
        2313                 :                              &fun, &v, &re, &im);
        2314                 :     JS_RemoveArgumentFormatter(cx, "ZZ");
        2315                 :     if (!ok)
        2316                 :         return JS_FALSE;
        2317                 :     fprintf(gOutFile,
        2318                 :             "b %u, c %x (%c), i %ld, u %lu, j %ld\n",
        2319                 :             b, c, (char)c, i, u, j);
        2320                 :     ToStringHelper obj2string(cx, obj2);
        2321                 :     ToStringHelper valueString(cx, v);
        2322                 :     JSAutoByteString strBytes;
        2323                 :     if (str)
        2324                 :         strBytes.encode(cx, str);
        2325                 :     JSString *tmpstr = JS_DecompileFunction(cx, fun, 4);
        2326                 :     JSAutoByteString func;
        2327                 :     if (!tmpstr || !func.encode(cx, tmpstr))
        2328                 :         ReportException(cx);
        2329                 :     fprintf(gOutFile,
        2330                 :             "d %g, I %g, S %s, W %s, obj %s, fun %s\n"
        2331                 :             "v %s, re %g, im %g\n",
        2332                 :             d, I, !!strBytes ? strBytes.ptr() : "", EscapeWideString(w),
        2333                 :             obj2string.getBytes(),
        2334                 :             fun ? (!!func ? func.ptr() : "error decompiling fun") : "",
        2335                 :             valueString.getBytes(), re, im);
        2336                 :     JS_SET_RVAL(cx, vp, JSVAL_VOID);
        2337                 :     return JS_TRUE;
        2338                 : }
        2339                 : #endif
        2340                 :
        2341                 : static JSBool
        2342               0 : BuildDate(JSContext *cx, unsigned argc, jsval *vp)
        2343                 : {
        2344               0 :     char version[20] = "\n";
        2345                 : #if JS_VERSION < 150
        2346                 :     sprintf(version, " for version %d\n", JS_VERSION);
        2347                 : #endif
        2348               0 :     fprintf(gOutFile, "built on %s at %s%s", __DATE__, __TIME__, version);
        2349               0 :     *vp = JSVAL_VOID;
        2350               0 :     return JS_TRUE;
        2351                 : }
        2352                 :
        2353                 : static JSBool
        2354               0 : Clear(JSContext *cx, unsigned argc, jsval *vp)
        2355                 : {
        2356                 :     JSObject *obj;
        2357               0 :     if (argc == 0) {
        2358               0 :         obj = JS_GetGlobalForScopeChain(cx);
        2359               0 :         if (!obj)
        2360               0 :             return false;
        2361               0 :     } else if (!JS_ValueToObject(cx, JS_ARGV(cx, vp)[0], &obj)) {
        2362               0 :         return false;
        2363                 :     }
        2364               0 :     JS_ClearScope(cx, obj);
        2365               0 :     JS_SET_RVAL(cx, vp, JSVAL_VOID);
        2366               0 :     return true;
        2367                 : }
        2368                 :
        2369                 : static JSBool
        2370               0 : Intern(JSContext *cx, unsigned argc, jsval *vp)
        2371                 : {
        2372               0 :     JSString *str = JS_ValueToString(cx, argc == 0 ? JSVAL_VOID : vp[2]);
        2373               0 :     if (!str)
        2374               0 :         return false;
        2375                 :
        2376                 :     size_t length;
        2377               0 :     const jschar *chars = JS_GetStringCharsAndLength(cx, str, &length);
        2378               0 :     if (!chars)
        2379               0 :         return false;
        2380                 :
        2381               0 :     if (!JS_InternUCStringN(cx, chars, length))
        2382               0 :         return false;
        2383                 :
        2384               0 :     JS_SET_RVAL(cx, vp, JSVAL_VOID);
        2385               0 :     return true;
        2386                 : }
        2387                 :
        2388                 : static JSBool
        2389            2277 : Clone(JSContext *cx, unsigned argc, jsval *vp)
        2390                 : {
        2391                 :     JSObject *funobj, *parent, *clone;
        2392                 :
        2393            2277 :     if (!argc) {
        2394               0 :         JS_ReportError(cx, "Invalid arguments to clone");
        2395               0 :         return JS_FALSE;
        2396                 :     }
        2397                 :
        2398            2277 :     jsval *argv = JS_ARGV(cx, vp);
        2399                 :     {
        2400            4554 :         JSAutoEnterCompartment ac;
        2401            4554 :         if (!JSVAL_IS_PRIMITIVE(argv[0]) &&
        2402            2277 :             IsCrossCompartmentWrapper(JSVAL_TO_OBJECT(argv[0])))
        2403                 :         {
        2404            2277 :             JSObject *obj = UnwrapObject(JSVAL_TO_OBJECT(argv[0]));
        2405            2277 :             if (!ac.enter(cx, obj))
        2406               0 :                 return JS_FALSE;
        2407            2277 :             argv[0] = OBJECT_TO_JSVAL(obj);
        2408                 :         }
        2409            2277 :         if (!JSVAL_IS_PRIMITIVE(argv[0]) && JSVAL_TO_OBJECT(argv[0])->isFunction()) {
        2410            2277 :             funobj = JSVAL_TO_OBJECT(argv[0]);
        2411                 :         } else {
        2412               0 :             JSFunction *fun = JS_ValueToFunction(cx, argv[0]);
        2413               0 :             if (!fun)
        2414               0 :                 return JS_FALSE;
        2415               0 :             funobj = JS_GetFunctionObject(fun);
        2416                 :         }
        2417                 :     }
        2418            2277 :     if (funobj->compartment() != cx->compartment) {
        2419            2277 :         JSFunction *fun = funobj->toFunction();
        2420            2277 :         if (fun->isInterpreted() && fun->script()->compileAndGo) {
        2421                 :             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_UNEXPECTED_TYPE,
        2422               0 :                                  "function", "compile-and-go");
        2423               0 :             return JS_FALSE;
        2424                 :         }
        2425                 :     }
        2426                 :
        2427            2277 :     if (argc > 1) {
        2428               0 :         if (!JS_ValueToObject(cx, argv[1], &parent))
        2429               0 :             return JS_FALSE;
        2430                 :     } else {
        2431            2277 :         parent = JS_GetParent(JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)));
        2432                 :     }
        2433                 :
        2434            2277 :     clone = JS_CloneFunctionObject(cx, funobj, parent);
        2435            2277 :     if (!clone)
        2436               0 :         return JS_FALSE;
        2437            2277 :     *vp = OBJECT_TO_JSVAL(clone);
        2438            2277 :     return JS_TRUE;
        2439                 : }
        2440                 :
        2441                 : static JSBool
        2442               0 : GetPDA(JSContext *cx, unsigned argc, jsval *vp)
        2443                 : {
        2444                 :     JSObject *vobj, *aobj, *pdobj;
        2445                 :     JSBool ok;
        2446                 :     JSPropertyDescArray pda;
        2447                 :     JSPropertyDesc *pd;
        2448                 :     jsval v;
        2449                 :
        2450               0 :     if (!JS_ValueToObject(cx, argc == 0 ? JSVAL_VOID : vp[2], &vobj))
        2451               0 :         return JS_FALSE;
        2452               0 :     if (!vobj) {
        2453               0 :         *vp = JSVAL_VOID;
        2454               0 :         return JS_TRUE;
        2455                 :     }
        2456                 :
        2457               0 :     aobj = JS_NewArrayObject(cx, 0, NULL);
        2458               0 :     if (!aobj)
        2459               0 :         return JS_FALSE;
        2460               0 :     *vp = OBJECT_TO_JSVAL(aobj);
        2461                 :
        2462               0 :     ok = JS_GetPropertyDescArray(cx, vobj, &pda);
        2463               0 :     if (!ok)
        2464               0 :         return JS_FALSE;
        2465               0 :     pd = pda.array;
        2466               0 :     for (uint32_t i = 0; i < pda.length; i++, pd++) {
        2467               0 :         pdobj = JS_NewObject(cx, NULL, NULL, NULL);
        2468               0 :         if (!pdobj) {
        2469               0 :             ok = JS_FALSE;
        2470               0 :             break;
        2471                 :         }
        2472                 :
        2473                 :         /* Protect pdobj from GC by setting it as an element of aobj now */
        2474               0 :         v = OBJECT_TO_JSVAL(pdobj);
        2475               0 :         ok = JS_SetElement(cx, aobj, i, &v);
        2476               0 :         if (!ok)
        2477               0 :             break;
        2478                 :
        2479               0 :         ok = JS_SetProperty(cx, pdobj, "id", &pd->id) &&
        2480               0 :              JS_SetProperty(cx, pdobj, "value", &pd->value) &&
        2481               0 :              (v = INT_TO_JSVAL(pd->flags),
        2482               0 :               JS_SetProperty(cx, pdobj, "flags", &v)) &&
        2483               0 :              (v = INT_TO_JSVAL(pd->slot),
        2484               0 :               JS_SetProperty(cx, pdobj, "slot", &v)) &&
        2485               0 :              JS_SetProperty(cx, pdobj, "alias", &pd->alias);
        2486               0 :         if (!ok)
        2487               0 :             break;
        2488                 :     }
        2489               0 :     JS_PutPropertyDescArray(cx, &pda);
        2490               0 :     return ok;
        2491                 : }
        2492                 :
        2493                 : static JSBool
        2494               0 : GetSLX(JSContext *cx, unsigned argc, jsval *vp)
        2495                 : {
        2496                 :     JSScript *script;
        2497                 :
        2498               0 :     script = ValueToScript(cx, argc == 0 ? JSVAL_VOID : vp[2]);
        2499               0 :     if (!script)
        2500               0 :         return JS_FALSE;
        2501               0 :     *vp = INT_TO_JSVAL(js_GetScriptLineExtent(script));
        2502               0 :     return JS_TRUE;
        2503                 : }
        2504                 :
        2505                 : static JSBool
        2506               0 : ToInt32(JSContext *cx, unsigned argc, jsval *vp)
        2507                 : {
        2508                 :     int32_t i;
        2509                 :
        2510               0 :     if (!JS_ValueToInt32(cx, argc == 0 ? JSVAL_VOID : vp[2], &i))
        2511               0 :         return JS_FALSE;
        2512               0 :     return JS_NewNumberValue(cx, i, vp);
        2513                 : }
        2514                 :
        2515                 : static JSBool
        2516               0 : StringsAreUTF8(JSContext *cx, unsigned argc, jsval *vp)
        2517                 : {
        2518               0 :     *vp = JS_CStringsAreUTF8() ? JSVAL_TRUE : JSVAL_FALSE;
        2519               0 :     return JS_TRUE;
        2520                 : }
        2521                 :
        2522                 : static const char* badUTF8 = "...\xC0...";
        2523                 : static const char* bigUTF8 = "...\xFB\xBF\xBF\xBF\xBF...";
        2524                 : static const jschar badSurrogate[] = { 'A', 'B', 'C', 0xDEEE, 'D', 'E', 0 };
        2525                 :
        2526                 : static JSBool
        2527               0 : TestUTF8(JSContext *cx, unsigned argc, jsval *vp)
        2528                 : {
        2529               0 :     int32_t mode = 1;
        2530                 :     jschar chars[20];
        2531               0 :     size_t charsLength = 5;
        2532                 :     char bytes[20];
        2533               0 :     size_t bytesLength = 20;
        2534               0 :     if (argc && !JS_ValueToInt32(cx, *JS_ARGV(cx, vp), &mode))
        2535               0 :         return JS_FALSE;
        2536                 :
        2537                 :     /* The following throw errors if compiled with UTF-8. */
        2538               0 :     switch (mode) {
        2539                 :       /* mode 1: malformed UTF-8 string. */
        2540                 :       case 1:
        2541               0 :         JS_NewStringCopyZ(cx, badUTF8);
        2542               0 :         break;
        2543                 :       /* mode 2: big UTF-8 character. */
        2544                 :       case 2:
        2545               0 :         JS_NewStringCopyZ(cx, bigUTF8);
        2546               0 :         break;
        2547                 :       /* mode 3: bad surrogate character. */
        2548                 :       case 3:
        2549               0 :         JS_EncodeCharacters(cx, badSurrogate, 6, bytes, &bytesLength);
        2550               0 :         break;
        2551                 :       /* mode 4: use a too small buffer. */
        2552                 :       case 4:
        2553               0 :         JS_DecodeBytes(cx, "1234567890", 10, chars, &charsLength);
        2554               0 :         break;
        2555                 :       default:
        2556               0 :         JS_ReportError(cx, "invalid mode parameter");
        2557               0 :         return JS_FALSE;
        2558                 :     }
        2559               0 :     JS_SET_RVAL(cx, vp, JSVAL_VOID);
        2560               0 :     return !JS_IsExceptionPending (cx);
        2561                 : }
        2562                 :
        2563                 : static JSBool
        2564               0 : ThrowError(JSContext *cx, unsigned argc, jsval *vp)
        2565                 : {
        2566               0 :     JS_ReportError(cx, "This is an error");
        2567               0 :     return JS_FALSE;
        2568                 : }
        2569                 :
        2570                 : #define LAZY_STANDARD_CLASSES
        2571                 :
        2572                 : /* A class for easily testing the inner/outer object callbacks. */
        2573                 : typedef struct ComplexObject {
        2574                 :     JSBool isInner;
        2575                 :     JSBool frozen;
        2576                 :     JSObject *inner;
        2577                 :     JSObject *outer;
        2578                 : } ComplexObject;
        2579                 :
        2580                 : static JSBool
        2581              36 : sandbox_enumerate(JSContext *cx, JSObject *obj)
        2582                 : {
        2583                 :     jsval v;
        2584                 :     JSBool b;
        2585                 :
        2586              36 :     if (!JS_GetProperty(cx, obj, "lazy", &v))
        2587               0 :         return JS_FALSE;
        2588                 :
        2589              36 :     JS_ValueToBoolean(cx, v, &b);
        2590              36 :     return !b || JS_EnumerateStandardClasses(cx, obj);
        2591                 : }
        2592                 :
        2593                 : static JSBool
        2594             630 : sandbox_resolve(JSContext *cx, JSObject *obj, jsid id, unsigned flags,
        2595                 :                 JSObject **objp)
        2596                 : {
        2597                 :     jsval v;
        2598                 :     JSBool b, resolved;
        2599                 :
        2600             630 :     if (!JS_GetProperty(cx, obj, "lazy", &v))
        2601               0 :         return JS_FALSE;
        2602                 :
        2603             630 :     JS_ValueToBoolean(cx, v, &b);
        2604             630 :     if (b && (flags & JSRESOLVE_ASSIGNING) == 0) {
        2605              72 :         if (!JS_ResolveStandardClass(cx, obj, id, &resolved))
        2606               0 :             return JS_FALSE;
        2607              72 :         if (resolved) {
        2608              27 :             *objp = obj;
        2609              27 :             return JS_TRUE;
        2610                 :         }
        2611                 :     }
        2612             603 :     *objp = NULL;
        2613             603 :     return JS_TRUE;
        2614                 : }
        2615                 :
        2616                 : static JSClass sandbox_class = {
        2617                 :     "sandbox",
        2618                 :     JSCLASS_NEW_RESOLVE | JSCLASS_GLOBAL_FLAGS,
        2619                 :     JS_PropertyStub,   JS_PropertyStub,
        2620                 :     JS_PropertyStub,   JS_StrictPropertyStub,
        2621                 :     sandbox_enumerate, (JSResolveOp)sandbox_resolve,
        2622                 :     JS_ConvertStub,    NULL,
        2623                 :     JSCLASS_NO_OPTIONAL_MEMBERS
        2624                 : };
        2625                 :
        2626                 : static JSObject *
        2627             216 : NewSandbox(JSContext *cx, bool lazy)
        2628                 : {
        2629             216 :     JSObject *obj = JS_NewCompartmentAndGlobalObject(cx, &sandbox_class, NULL);
        2630             216 :     if (!obj)
        2631               0 :         return NULL;
        2632                 :
        2633                 :     {
        2634             432 :         JSAutoEnterCompartment ac;
        2635             216 :         if (!ac.enter(cx, obj))
        2636               0 :             return NULL;
        2637                 :
        2638             216 :         if (!lazy && !JS_InitStandardClasses(cx, obj))
        2639               0 :             return NULL;
        2640                 :
        2641             432 :         AutoValueRooter root(cx, BooleanValue(lazy));
        2642             216 :         if (!JS_SetProperty(cx, obj, "lazy", root.jsval_addr()))
        2643               0 :             return NULL;
        2644                 :     }
        2645                 :
        2646             216 :     if (!cx->compartment->wrap(cx, &obj))
        2647               0 :         return NULL;
        2648             216 :     return obj;
        2649                 : }
        2650                 :
        2651                 : static JSBool
        2652             306 : EvalInContext(JSContext *cx, unsigned argc, jsval *vp)
        2653                 : {
        2654                 :     JSString *str;
        2655             306 :     JSObject *sobj = NULL;
        2656             306 :     if (!JS_ConvertArguments(cx, argc, JS_ARGV(cx, vp), "S / o", &str, &sobj))
        2657               0 :         return false;
        2658                 :
        2659                 :     size_t srclen;
        2660             306 :     const jschar *src = JS_GetStringCharsAndLength(cx, str, &srclen);
        2661             306 :     if (!src)
        2662               0 :         return false;
        2663                 :
        2664             306 :     bool lazy = false;
        2665             306 :     if (srclen == 4) {
        2666              63 :         if (src[0] == 'l' && src[1] == 'a' && src[2] == 'z' && src[3] == 'y') {
        2667              63 :             lazy = true;
        2668              63 :             srclen = 0;
        2669                 :         }
        2670                 :     }
        2671                 :
        2672             306 :     if (!sobj) {
        2673             216 :         sobj = NewSandbox(cx, lazy);
        2674             216 :         if (!sobj)
        2675               0 :             return false;
        2676                 :     }
        2677                 :
        2678             306 :     if (srclen == 0) {
        2679             180 :         JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(sobj));
        2680             180 :         return true;
        2681                 :     }
        2682                 :
        2683                 :     JSScript *script;
        2684                 :     unsigned lineno;
        2685                 :
        2686             126 :     JS_DescribeScriptedCaller(cx, &script, &lineno);
        2687                 :     jsval rval;
        2688                 :     {
        2689             252 :         JSAutoEnterCompartment ac;
        2690                 :         unsigned flags;
        2691             126 :         JSObject *unwrapped = UnwrapObject(sobj, true, &flags);
        2692             126 :         if (flags & Wrapper::CROSS_COMPARTMENT) {
        2693             108 :             sobj = unwrapped;
        2694             108 :             if (!ac.enter(cx, sobj))
        2695               0 :                 return false;
        2696                 :         }
        2697                 :
        2698             126 :         OBJ_TO_INNER_OBJECT(cx, sobj);
        2699             126 :         if (!sobj)
        2700               0 :             return false;
        2701             126 :         if (!(sobj->getClass()->flags & JSCLASS_IS_GLOBAL)) {
        2702               0 :             JS_ReportError(cx, "Invalid scope argument to evalcx");
        2703               0 :             return false;
        2704                 :         }
        2705             126 :         if (!JS_EvaluateUCScript(cx, sobj, src, srclen,
        2706                 :                                  script->filename,
        2707                 :                                  lineno,
        2708             126 :                                  &rval)) {
        2709              27 :             return false;
        2710                 :         }
        2711                 :     }
        2712                 :
        2713              99 :     if (!cx->compartment->wrap(cx, &rval))
        2714               0 :         return false;
        2715                 :
        2716              99 :     JS_SET_RVAL(cx, vp, rval);
        2717              99 :     return true;
        2718                 : }
        2719                 :
        2720                 : static JSBool
        2721             594 : EvalInFrame(JSContext *cx, unsigned argc, jsval *vp)
        2722                 : {
        2723             594 :     jsval *argv = JS_ARGV(cx, vp);
        2724            1782 :     if (argc < 2 ||
        2725             594 :         !JSVAL_IS_INT(argv[0]) ||
        2726             594 :         !JSVAL_IS_STRING(argv[1])) {
        2727               0 :         JS_ReportError(cx, "Invalid arguments to evalInFrame");
        2728               0 :         return JS_FALSE;
        2729                 :     }
        2730                 :
        2731             594 :     uint32_t upCount = JSVAL_TO_INT(argv[0]);
        2732             594 :     JSString *str = JSVAL_TO_STRING(argv[1]);
        2733                 :
        2734             189 :     bool saveCurrent = (argc >= 3 && JSVAL_IS_BOOLEAN(argv[2]))
        2735             189 :                         ? !!(JSVAL_TO_BOOLEAN(argv[2]))
        2736             972 :                         : false;
        2737                 :
        2738             594 :     JS_ASSERT(cx->hasfp());
        2739                 :
        2740             594 :     FrameRegsIter fi(cx);
        2741            1152 :     for (uint32_t i = 0; i < upCount; ++i, ++fi) {
        2742             558 :         if (!fi.fp()->prev())
        2743               0 :             break;
        2744                 :     }
        2745                 :
        2746             594 :     StackFrame *const fp = fi.fp();
        2747             594 :     if (!fp->isScriptFrame()) {
        2748               0 :         JS_ReportError(cx, "cannot eval in non-script frame");
        2749               0 :         return JS_FALSE;
        2750                 :     }
        2751                 :
        2752             594 :     JSBool saved = JS_FALSE;;
        2753             594 :     if (saveCurrent)
        2754             144 :         saved = JS_SaveFrameChain(cx);
        2755                 :
        2756                 :     size_t length;
        2757             594 :     const jschar *chars = JS_GetStringCharsAndLength(cx, str, &length);
        2758             594 :     if (!chars)
        2759               0 :         return JS_FALSE;
        2760                 :
        2761                 :     JSBool ok = JS_EvaluateUCInStackFrame(cx, Jsvalify(fp), chars, length,
        2762             594 :                                           fp->script()->filename,
        2763                 :                                           JS_PCToLineNumber(cx, fp->script(),
        2764                 :                                                             fi.pc()),
        2765            1188 :                                           vp);
        2766                 :
        2767             594 :     if (saved)
        2768             144 :         JS_RestoreFrameChain(cx);
        2769                 :
        2770             594 :     return ok;
        2771                 : }
        2772                 :
        2773                 : static JSBool
        2774             342 : ShapeOf(JSContext *cx, unsigned argc, jsval *vp)
        2775                 : {
        2776                 :     jsval v;
        2777             342 :     if (argc < 1 || !JSVAL_IS_OBJECT(v = JS_ARGV(cx, vp)[0])) {
        2778               0 :         JS_ReportError(cx, "shapeOf: object expected");
        2779               0 :         return JS_FALSE;
        2780                 :     }
        2781             342 :     JSObject *obj = JSVAL_TO_OBJECT(v);
        2782             342 :     if (!obj) {
        2783               0 :         *vp = JSVAL_ZERO;
        2784               0 :         return JS_TRUE;
        2785                 :     }
        2786             342 :     return JS_NewNumberValue(cx, (double) ((uintptr_t)obj->lastProperty() >> 3), vp);
        2787                 : }
        2788                 :
        2789                 : /*
        2790                 :  * If referent has an own property named id, copy that property to obj[id].
        2791                 :  * Since obj is native, this isn't totally transparent; properties of a
        2792                 :  * non-native referent may be simplified to data properties.
        2793                 :  */
        2794                 : static JSBool
        2795               0 : CopyProperty(JSContext *cx, JSObject *obj, JSObject *referent, jsid id,
        2796                 :              unsigned lookupFlags, JSObject **objp)
        2797                 : {
        2798                 :     JSProperty *prop;
        2799                 :     PropertyDescriptor desc;
        2800               0 :     unsigned propFlags = 0;
        2801                 :     JSObject *obj2;
        2802                 :
        2803               0 :     *objp = NULL;
        2804               0 :     if (referent->isNative()) {
        2805               0 :         if (!LookupPropertyWithFlags(cx, referent, id, lookupFlags, &obj2, &prop))
        2806               0 :             return false;
        2807               0 :         if (obj2 != referent)
        2808               0 :             return true;
        2809                 :
        2810               0 :         const Shape *shape = (Shape *) prop;
        2811               0 :         if (shape->isMethod()) {
        2812               0 :             shape = referent->methodReadBarrier(cx, *shape, &desc.value);
        2813               0 :             if (!shape)
        2814               0 :                 return false;
        2815               0 :         } else if (shape->hasSlot()) {
        2816               0 :             desc.value = referent->nativeGetSlot(shape->slot());
        2817                 :         } else {
        2818               0 :             desc.value.setUndefined();
        2819                 :         }
        2820                 :
        2821               0 :         desc.attrs = shape->attributes();
        2822               0 :         desc.getter = shape->getter();
        2823               0 :         if (!desc.getter && !(desc.attrs & JSPROP_GETTER))
        2824               0 :             desc.getter = JS_PropertyStub;
        2825               0 :         desc.setter = shape->setter();
        2826               0 :         if (!desc.setter && !(desc.attrs & JSPROP_SETTER))
        2827               0 :             desc.setter = JS_StrictPropertyStub;
        2828               0 :         desc.shortid = shape->shortid();
        2829               0 :         propFlags = shape->getFlags();
        2830               0 :    } else if (IsProxy(referent)) {
        2831                 :         PropertyDescriptor desc;
        2832               0 :         if (!Proxy::getOwnPropertyDescriptor(cx, referent, id, false, &desc))
        2833               0 :             return false;
        2834               0 :         if (!desc.obj)
        2835               0 :             return true;
        2836                 :     } else {
        2837               0 :         if (!referent->lookupGeneric(cx, id, objp, &prop))
        2838               0 :             return false;
        2839               0 :         if (*objp != referent)
        2840               0 :             return true;
        2841               0 :         if (!referent->getGeneric(cx, id, &desc.value) ||
        2842               0 :             !referent->getGenericAttributes(cx, id, &desc.attrs)) {
        2843               0 :             return false;
        2844                 :         }
        2845               0 :         desc.attrs &= JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT;
        2846               0 :         desc.getter = JS_PropertyStub;
        2847               0 :         desc.setter = JS_StrictPropertyStub;
        2848               0 :         desc.shortid = 0;
        2849                 :     }
        2850                 :
        2851               0 :     *objp = obj;
        2852                 :     return !!DefineNativeProperty(cx, obj, id, desc.value, desc.getter, desc.setter,
        2853               0 :                                   desc.attrs, propFlags, desc.shortid);
        2854                 : }
        2855                 :
        2856                 : static JSBool
        2857               0 : resolver_resolve(JSContext *cx, JSObject *obj, jsid id, unsigned flags, JSObject **objp)
        2858                 : {
        2859               0 :     jsval v = JS_GetReservedSlot(obj, 0);
        2860               0 :     return CopyProperty(cx, obj, JSVAL_TO_OBJECT(v), id, flags, objp);
        2861                 : }
        2862                 :
        2863                 : static JSBool
        2864               0 : resolver_enumerate(JSContext *cx, JSObject *obj)
        2865                 : {
        2866               0 :     jsval v = JS_GetReservedSlot(obj, 0);
        2867               0 :     JSObject *referent = JSVAL_TO_OBJECT(v);
        2868                 :
        2869               0 :     AutoIdArray ida(cx, JS_Enumerate(cx, referent));
        2870               0 :     bool ok = !!ida;
        2871                 :     JSObject *ignore;
        2872               0 :     for (size_t i = 0; ok && i < ida.length(); i++)
        2873               0 :         ok = CopyProperty(cx, obj, referent, ida[i], JSRESOLVE_QUALIFIED, &ignore);
        2874               0 :     return ok;
        2875                 : }
        2876                 :
        2877                 : static JSClass resolver_class = {
        2878                 :     "resolver",
        2879                 :     JSCLASS_NEW_RESOLVE | JSCLASS_HAS_RESERVED_SLOTS(1),
        2880                 :     JS_PropertyStub,   JS_PropertyStub,
        2881                 :     JS_PropertyStub,   JS_StrictPropertyStub,
        2882                 :     resolver_enumerate, (JSResolveOp)resolver_resolve,
        2883                 :     JS_ConvertStub,    NULL,
        2884                 :     JSCLASS_NO_OPTIONAL_MEMBERS
        2885                 : };
        2886                 :
        2887                 :
        2888                 : static JSBool
        2889               0 : Resolver(JSContext *cx, unsigned argc, jsval *vp)
        2890                 : {
        2891               0 :     JSObject *referent, *proto = NULL;
        2892               0 :     if (!JS_ConvertArguments(cx, argc, JS_ARGV(cx, vp), "o/o", &referent, &proto))
        2893               0 :         return false;
        2894                 :
        2895                 :     JSObject *result = (argc > 1
        2896                 :                         ? JS_NewObjectWithGivenProto
        2897               0 :                         : JS_NewObject)(cx, &resolver_class, proto, JS_GetParent(referent));
        2898               0 :     if (!result)
        2899               0 :         return false;
        2900                 :
        2901               0 :     JS_SetReservedSlot(result, 0, OBJECT_TO_JSVAL(referent));
        2902               0 :     JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
        2903               0 :     return true;
        2904                 : }
        2905                 :
        2906                 : #ifdef JS_THREADSAFE
        2907                 :
        2908                 : /*
        2909                 :  * Check that t1 comes strictly before t2. The function correctly deals with
        2910                 :  * PRIntervalTime wrap-around between t2 and t1 assuming that t2 and t1 stays
        2911                 :  * within INT32_MAX from each other. We use MAX_TIMEOUT_INTERVAL to enforce
        2912                 :  * this restriction.
        2913                 :  */
        2914                 : static bool
        2915               0 : IsBefore(PRIntervalTime t1, PRIntervalTime t2)
        2916                 : {
        2917               0 :     return int32_t(t1 - t2) < 0;
        2918                 : }
        2919                 :
        2920                 : static JSBool
        2921               0 : Sleep_fn(JSContext *cx, unsigned argc, jsval *vp)
        2922                 : {
        2923                 :     PRIntervalTime t_ticks;
        2924                 :
        2925               0 :     if (argc == 0) {
        2926               0 :         t_ticks = 0;
        2927                 :     } else {
        2928                 :         double t_secs;
        2929                 :
        2930               0 :         if (!JS_ValueToNumber(cx, argc == 0 ? JSVAL_VOID : vp[2], &t_secs))
        2931               0 :             return JS_FALSE;
        2932                 :
        2933                 :         /* NB: The next condition also filter out NaNs. */
        2934               0 :         if (!(t_secs <= MAX_TIMEOUT_INTERVAL)) {
        2935               0 :             JS_ReportError(cx, "Excessive sleep interval");
        2936               0 :             return JS_FALSE;
        2937                 :         }
        2938                 :         t_ticks = (t_secs <= 0.0)
        2939                 :                   ? 0
        2940               0 :                   : PRIntervalTime(PR_TicksPerSecond() * t_secs);
        2941                 :     }
        2942               0 :     if (t_ticks == 0) {
        2943               0 :         JS_YieldRequest(cx);
        2944                 :     } else {
        2945               0 :         JSAutoSuspendRequest suspended(cx);
        2946               0 :         PR_Lock(gWatchdogLock);
        2947               0 :         PRIntervalTime to_wakeup = PR_IntervalNow() + t_ticks;
        2948               0 :         for (;;) {
        2949               0 :             PR_WaitCondVar(gSleepWakeup, t_ticks);
        2950               0 :             if (gCanceled)
        2951               0 :                 break;
        2952               0 :             PRIntervalTime now = PR_IntervalNow();
        2953               0 :             if (!IsBefore(now, to_wakeup))
        2954               0 :                 break;
        2955               0 :             t_ticks = to_wakeup - now;
        2956                 :         }
        2957               0 :         PR_Unlock(gWatchdogLock);
        2958                 :     }
        2959               0 :     return !gCanceled;
        2960                 : }
        2961                 :
        2962                 : static bool
        2963           18405 : InitWatchdog(JSRuntime *rt)
        2964                 : {
        2965           18405 :     JS_ASSERT(!gWatchdogThread);
        2966           18405 :     gWatchdogLock = PR_NewLock();
        2967           18405 :     if (gWatchdogLock) {
        2968           18405 :         gWatchdogWakeup = PR_NewCondVar(gWatchdogLock);
        2969           18405 :         if (gWatchdogWakeup) {
        2970           18405 :             gSleepWakeup = PR_NewCondVar(gWatchdogLock);
        2971           18405 :             if (gSleepWakeup)
        2972           18405 :                 return true;
        2973               0 :             PR_DestroyCondVar(gWatchdogWakeup);
        2974                 :         }
        2975               0 :         PR_DestroyLock(gWatchdogLock);
        2976                 :     }
        2977               0 :     return false;
        2978                 : }
        2979                 :
        2980                 : static void
        2981           18405 : KillWatchdog()
        2982                 : {
        2983                 :     PRThread *thread;
        2984                 :
        2985           18405 :     PR_Lock(gWatchdogLock);
        2986           18405 :     thread = gWatchdogThread;
        2987           18405 :     if (thread) {
        2988                 :         /*
        2989                 :          * The watchdog thread is running, tell it to terminate waking it up
        2990                 :          * if necessary.
        2991                 :          */
        2992               0 :         gWatchdogThread = NULL;
        2993               0 :         PR_NotifyCondVar(gWatchdogWakeup);
        2994                 :     }
        2995           18405 :     PR_Unlock(gWatchdogLock);
        2996           18405 :     if (thread)
        2997               0 :         PR_JoinThread(thread);
        2998           18405 :     PR_DestroyCondVar(gSleepWakeup);
        2999           18405 :     PR_DestroyCondVar(gWatchdogWakeup);
        3000           18405 :     PR_DestroyLock(gWatchdogLock);
        3001           18405 : }
        3002                 :
        3003                 : static void
        3004               0 : WatchdogMain(void *arg)
        3005                 : {
        3006               0 :     JSRuntime *rt = (JSRuntime *) arg;
        3007                 :
        3008               0 :     PR_Lock(gWatchdogLock);
        3009               0 :     while (gWatchdogThread) {
        3010               0 :         PRIntervalTime now = PR_IntervalNow();
        3011               0 :          if (gWatchdogHasTimeout && !IsBefore(now, gWatchdogTimeout)) {
        3012                 :             /*
        3013                 :              * The timeout has just expired. Trigger the operation callback
        3014                 :              * outside the lock.
        3015                 :              */
        3016               0 :             gWatchdogHasTimeout = false;
        3017               0 :             PR_Unlock(gWatchdogLock);
        3018               0 :             CancelExecution(rt);
        3019               0 :             PR_Lock(gWatchdogLock);
        3020                 :
        3021                 :             /* Wake up any threads doing sleep. */
        3022               0 :             PR_NotifyAllCondVar(gSleepWakeup);
        3023                 :         } else {
        3024                 :             PRIntervalTime sleepDuration = gWatchdogHasTimeout
        3025                 :                                            ? gWatchdogTimeout - now
        3026               0 :                                            : PR_INTERVAL_NO_TIMEOUT;
        3027                 :             DebugOnly<PRStatus> status =
        3028               0 :                 PR_WaitCondVar(gWatchdogWakeup, sleepDuration);
        3029               0 :             JS_ASSERT(status == PR_SUCCESS);
        3030                 :         }
        3031                 :     }
        3032               0 :     PR_Unlock(gWatchdogLock);
        3033               0 : }
        3034                 :
        3035                 : static bool
        3036               0 : ScheduleWatchdog(JSRuntime *rt, double t)
        3037                 : {
        3038               0 :     if (t <= 0) {
        3039               0 :         PR_Lock(gWatchdogLock);
        3040               0 :         gWatchdogHasTimeout = false;
        3041               0 :         PR_Unlock(gWatchdogLock);
        3042               0 :         return true;
        3043                 :     }
        3044                 :
        3045               0 :     PRIntervalTime interval = PRIntervalTime(ceil(t * PR_TicksPerSecond()));
        3046               0 :     PRIntervalTime timeout = PR_IntervalNow() + interval;
        3047               0 :     PR_Lock(gWatchdogLock);
        3048               0 :     if (!gWatchdogThread) {
        3049               0 :         JS_ASSERT(!gWatchdogHasTimeout);
        3050                 :         gWatchdogThread = PR_CreateThread(PR_USER_THREAD,
        3051                 :                                           WatchdogMain,
        3052                 :                                           rt,
        3053                 :                                           PR_PRIORITY_NORMAL,
        3054                 :                                           PR_LOCAL_THREAD,
        3055                 :                                           PR_JOINABLE_THREAD,
        3056               0 :                                           0);
        3057               0 :         if (!gWatchdogThread) {
        3058               0 :             PR_Unlock(gWatchdogLock);
        3059               0 :             return false;
        3060                 :         }
        3061               0 :     } else if (!gWatchdogHasTimeout || IsBefore(timeout, gWatchdogTimeout)) {
        3062               0 :          PR_NotifyCondVar(gWatchdogWakeup);
        3063                 :     }
        3064               0 :     gWatchdogHasTimeout = true;
        3065               0 :     gWatchdogTimeout = timeout;
        3066               0 :     PR_Unlock(gWatchdogLock);
        3067               0 :     return true;
        3068                 : }
        3069                 :
        3070                 : #else /* !JS_THREADSAFE */
        3071                 :
        3072                 : #ifdef XP_WIN
        3073                 : static HANDLE gTimerHandle = 0;
        3074                 :
        3075                 : VOID CALLBACK
        3076                 : TimerCallback(PVOID lpParameter, BOOLEAN TimerOrWaitFired)
        3077                 : {
        3078                 :     CancelExecution((JSRuntime *) lpParameter);
        3079                 : }
        3080                 :
        3081                 : #else
        3082                 :
        3083                 : static void
        3084                 : AlarmHandler(int sig)
        3085                 : {
        3086                 :     CancelExecution(gRuntime);
        3087                 : }
        3088                 :
        3089                 : #endif
        3090                 :
        3091                 : static bool
        3092                 : InitWatchdog(JSRuntime *rt)
        3093                 : {
        3094                 :     gRuntime = rt;
        3095                 :     return true;
        3096                 : }
        3097                 :
        3098                 : static void
        3099                 : KillWatchdog()
        3100                 : {
        3101                 :     ScheduleWatchdog(gRuntime, -1);
        3102                 : }
        3103                 :
        3104                 : static bool
        3105                 : ScheduleWatchdog(JSRuntime *rt, double t)
        3106                 : {
        3107                 : #ifdef XP_WIN
        3108                 :     if (gTimerHandle) {
        3109                 :         DeleteTimerQueueTimer(NULL, gTimerHandle, NULL);
        3110                 :         gTimerHandle = 0;
        3111                 :     }
        3112                 :     if (t > 0 &&
        3113                 :         !CreateTimerQueueTimer(&gTimerHandle,
        3114                 :                                NULL,
        3115                 :                                (WAITORTIMERCALLBACK)TimerCallback,
        3116                 :                                rt,
        3117                 :                                DWORD(ceil(t * 1000.0)),
        3118                 :                                0,
        3119                 :                                WT_EXECUTEINTIMERTHREAD | WT_EXECUTEONLYONCE)) {
        3120                 :         gTimerHandle = 0;
        3121                 :         return false;
        3122                 :     }
        3123                 : #else
        3124                 :     /* FIXME: use setitimer when available for sub-second resolution. */
        3125                 :     if (t <= 0) {
        3126                 :         alarm(0);
        3127                 :         signal(SIGALRM, NULL);
        3128                 :     } else {
        3129                 :         signal(SIGALRM, AlarmHandler); /* set the Alarm signal capture */
        3130                 :         alarm(ceil(t));
        3131                 :     }
        3132                 : #endif
        3133                 :     return true;
        3134                 : }
        3135                 :
        3136                 : #endif /* !JS_THREADSAFE */
        3137                 :
        3138                 : static void
        3139               0 : CancelExecution(JSRuntime *rt)
        3140                 : {
        3141               0 :     gCanceled = true;
        3142               0 :     if (gExitCode == 0)
        3143               0 :         gExitCode = EXITCODE_TIMEOUT;
        3144                 : #ifdef JS_THREADSAFE
        3145               0 :     if (gWorkerThreadPool)
        3146               0 :         js::workers::terminateAll(gWorkerThreadPool);
        3147                 : #endif
        3148               0 :     JS_TriggerOperationCallback(rt);
        3149                 :
        3150                 :     static const char msg[] = "Script runs for too long, terminating.\n";
        3151                 : #if defined(XP_UNIX) && !defined(JS_THREADSAFE)
        3152                 :     /* It is not safe to call fputs from signals. */
        3153                 :     /* Dummy assignment avoids GCC warning on "attribute warn_unused_result" */
        3154                 :     ssize_t dummy = write(2, msg, sizeof(msg) - 1);
        3155                 :     (void)dummy;
        3156                 : #else
        3157               0 :     fputs(msg, stderr);
        3158                 : #endif
        3159               0 : }
        3160                 :
        3161                 : static JSBool
        3162               0 : SetTimeoutValue(JSContext *cx, double t)
        3163                 : {
        3164                 :     /* NB: The next condition also filter out NaNs. */
        3165               0 :     if (!(t <= MAX_TIMEOUT_INTERVAL)) {
        3166               0 :         JS_ReportError(cx, "Excessive timeout value");
        3167               0 :         return JS_FALSE;
        3168                 :     }
        3169               0 :     gTimeoutInterval = t;
        3170               0 :     if (!ScheduleWatchdog(cx->runtime, t)) {
        3171               0 :         JS_ReportError(cx, "Failed to create the watchdog");
        3172               0 :         return JS_FALSE;
        3173                 :     }
        3174               0 :     return JS_TRUE;
        3175                 : }
        3176                 :
        3177                 : static JSBool
        3178               0 : Timeout(JSContext *cx, unsigned argc, jsval *vp)
        3179                 : {
        3180               0 :     if (argc == 0)
        3181               0 :         return JS_NewNumberValue(cx, gTimeoutInterval, vp);
        3182                 :
        3183               0 :     if (argc > 1) {
        3184               0 :         JS_ReportError(cx, "Wrong number of arguments");
        3185               0 :         return JS_FALSE;
        3186                 :     }
        3187                 :
        3188                 :     double t;
        3189               0 :     if (!JS_ValueToNumber(cx, JS_ARGV(cx, vp)[0], &t))
        3190               0 :         return JS_FALSE;
        3191                 :
        3192               0 :     *vp = JSVAL_VOID;
        3193               0 :     return SetTimeoutValue(cx, t);
        3194                 : }
        3195                 :
        3196                 : static JSBool
        3197               0 : Elapsed(JSContext *cx, unsigned argc, jsval *vp)
        3198                 : {
        3199               0 :     if (argc == 0) {
        3200               0 :         double d = 0.0;
        3201               0 :         JSShellContextData *data = GetContextData(cx);
        3202               0 :         if (data)
        3203               0 :             d = js_IntervalNow() - data->startTime;
        3204               0 :         return JS_NewNumberValue(cx, d, vp);
        3205                 :     }
        3206               0 :     JS_ReportError(cx, "Wrong number of arguments");
        3207               0 :     return JS_FALSE;
        3208                 : }
        3209                 :
        3210                 : static JSBool
        3211               0 : Parent(JSContext *cx, unsigned argc, jsval *vp)
        3212                 : {
        3213               0 :     if (argc != 1) {
        3214               0 :         JS_ReportError(cx, "Wrong number of arguments");
        3215               0 :         return JS_FALSE;
        3216                 :     }
        3217                 :
        3218               0 :     jsval v = JS_ARGV(cx, vp)[0];
        3219               0 :     if (JSVAL_IS_PRIMITIVE(v)) {
        3220               0 :         JS_ReportError(cx, "Only objects have parents!");
        3221               0 :         return JS_FALSE;
        3222                 :     }
        3223                 :
        3224               0 :     JSObject *parent = JS_GetParent(JSVAL_TO_OBJECT(v));
        3225               0 :     *vp = OBJECT_TO_JSVAL(parent);
        3226                 :
        3227                 :     /* Outerize if necessary.  Embrace the ugliness! */
        3228               0 :     if (parent) {
        3229               0 :         if (JSObjectOp op = parent->getClass()->ext.outerObject)
        3230               0 :             *vp = OBJECT_TO_JSVAL(op(cx, parent));
        3231                 :     }
        3232                 :
        3233               0 :     return JS_TRUE;
        3234                 : }
        3235                 :
        3236                 : #ifdef XP_UNIX
        3237                 :
        3238                 : #include <fcntl.h>
        3239                 : #include <sys/stat.h>
        3240                 :
        3241                 : /*
        3242                 :  * Returns a JS_malloc'd string (that the caller needs to JS_free)
        3243                 :  * containing the directory (non-leaf) part of |from| prepended to |leaf|.
        3244                 :  * If |from| is empty or a leaf, MakeAbsolutePathname returns a copy of leaf.
        3245                 :  * Returns NULL to indicate an error.
        3246                 :  */
        3247                 : static char *
        3248               0 : MakeAbsolutePathname(JSContext *cx, const char *from, const char *leaf)
        3249                 : {
        3250                 :     size_t dirlen;
        3251                 :     char *dir;
        3252               0 :     const char *slash = NULL, *cp;
        3253                 :
        3254               0 :     if (*leaf == '/') {
        3255                 :         /* We were given an absolute pathname. */
        3256               0 :         return JS_strdup(cx, leaf);
        3257                 :     }
        3258                 :
        3259               0 :     cp = from;
        3260               0 :     while (*cp) {
        3261               0 :         if (*cp == '/') {
        3262               0 :             slash = cp;
        3263                 :         }
        3264                 :
        3265               0 :         ++cp;
        3266                 :     }
        3267                 :
        3268               0 :     if (!slash) {
        3269                 :         /* We were given a leaf or |from| was empty. */
        3270               0 :         return JS_strdup(cx, leaf);
        3271                 :     }
        3272                 :
        3273                 :     /* Else, we were given a real pathname, return that + the leaf. */
        3274               0 :     dirlen = slash - from + 1;
        3275               0 :     dir = (char*) JS_malloc(cx, dirlen + strlen(leaf) + 1);
        3276               0 :     if (!dir)
        3277               0 :         return NULL;
        3278                 :
        3279               0 :     strncpy(dir, from, dirlen);
        3280               0 :     strcpy(dir + dirlen, leaf); /* Note: we can't use strcat here. */
        3281                 :
        3282               0 :     return dir;
        3283                 : }
        3284                 :
        3285                 : #endif // XP_UNIX
        3286                 :
        3287                 : static JSBool
        3288               0 : Compile(JSContext *cx, unsigned argc, jsval *vp)
        3289                 : {
        3290               0 :     if (argc < 1) {
        3291                 :         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
        3292               0 :                              "compile", "0", "s");
        3293               0 :         return JS_FALSE;
        3294                 :     }
        3295               0 :     jsval arg0 = JS_ARGV(cx, vp)[0];
        3296               0 :     if (!JSVAL_IS_STRING(arg0)) {
        3297               0 :         const char *typeName = JS_GetTypeName(cx, JS_TypeOfValue(cx, arg0));
        3298               0 :         JS_ReportError(cx, "expected string to compile, got %s", typeName);
        3299               0 :         return JS_FALSE;
        3300                 :     }
        3301                 :
        3302                 :     static JSClass dummy_class = {
        3303                 :         "jdummy",
        3304                 :         JSCLASS_GLOBAL_FLAGS,
        3305                 :         JS_PropertyStub,  JS_PropertyStub,
        3306                 :         JS_PropertyStub,  JS_StrictPropertyStub,
        3307                 :         JS_EnumerateStub, JS_ResolveStub,
        3308                 :         JS_ConvertStub,   NULL,
        3309                 :         JSCLASS_NO_OPTIONAL_MEMBERS
        3310                 :     };
        3311                 :
        3312               0 :     JSObject *fakeGlobal = JS_NewGlobalObject(cx, &dummy_class);
        3313               0 :     if (!fakeGlobal)
        3314               0 :         return JS_FALSE;
        3315                 :
        3316               0 :     JSString *scriptContents = JSVAL_TO_STRING(arg0);
        3317                 :
        3318               0 :     unsigned oldopts = JS_GetOptions(cx);
        3319               0 :     JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO | JSOPTION_NO_SCRIPT_RVAL);
        3320                 :     bool ok = JS_CompileUCScript(cx, fakeGlobal, JS_GetStringCharsZ(cx, scriptContents),
        3321               0 :                                  JS_GetStringLength(scriptContents), "<string>", 0);
        3322               0 :     JS_SetOptions(cx, oldopts);
        3323                 :
        3324               0 :     JS_SET_RVAL(cx, vp, JSVAL_VOID);
        3325               0 :     return ok;
        3326                 : }
        3327                 :
        3328                 : static JSBool
        3329               0 : Parse(JSContext *cx, unsigned argc, jsval *vp)
        3330                 : {
        3331               0 :     if (argc < 1) {
        3332                 :         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
        3333               0 :                              "compile", "0", "s");
        3334               0 :         return JS_FALSE;
        3335                 :     }
        3336               0 :     jsval arg0 = JS_ARGV(cx, vp)[0];
        3337               0 :     if (!JSVAL_IS_STRING(arg0)) {
        3338               0 :         const char *typeName = JS_GetTypeName(cx, JS_TypeOfValue(cx, arg0));
        3339               0 :         JS_ReportError(cx, "expected string to parse, got %s", typeName);
        3340               0 :         return JS_FALSE;
        3341                 :     }
        3342                 :
        3343               0 :     JSString *scriptContents = JSVAL_TO_STRING(arg0);
        3344               0 :     js::Parser parser(cx);
        3345                 :     parser.init(JS_GetStringCharsZ(cx, scriptContents), JS_GetStringLength(scriptContents),
        3346               0 :                 "<string>", 0, cx->findVersion());
        3347               0 :     ParseNode *pn = parser.parse(NULL);
        3348               0 :     if (!pn)
        3349               0 :         return JS_FALSE;
        3350                 : #ifdef DEBUG
        3351               0 :     DumpParseTree(pn);
        3352                 : #endif
        3353               0 :     JS_SET_RVAL(cx, vp, JSVAL_VOID);
        3354               0 :     return JS_TRUE;
        3355                 : }
        3356                 :
        3357                 : struct FreeOnReturn {
        3358                 :     JSContext *cx;
        3359                 :     const char *ptr;
        3360                 :     JS_DECL_USE_GUARD_OBJECT_NOTIFIER
        3361                 :
        3362               0 :     FreeOnReturn(JSContext *cx, const char *ptr = NULL JS_GUARD_OBJECT_NOTIFIER_PARAM)
        3363               0 :       : cx(cx), ptr(ptr) {
        3364               0 :         JS_GUARD_OBJECT_NOTIFIER_INIT;
        3365               0 :     }
        3366                 :
        3367               0 :     void init(const char *ptr) {
        3368               0 :         JS_ASSERT(!this->ptr);
        3369               0 :         this->ptr = ptr;
        3370               0 :     }
        3371                 :
        3372               0 :     ~FreeOnReturn() {
        3373               0 :         JS_free(cx, (void*)ptr);
        3374               0 :     }
        3375                 : };
        3376                 :
        3377                 : static JSBool
        3378               0 : Snarf(JSContext *cx, unsigned argc, jsval *vp)
        3379                 : {
        3380                 :     JSString *str;
        3381                 :
        3382               0 :     if (!argc)
        3383               0 :         return JS_FALSE;
        3384                 :
        3385               0 :     str = JS_ValueToString(cx, JS_ARGV(cx, vp)[0]);
        3386               0 :     if (!str)
        3387               0 :         return JS_FALSE;
        3388               0 :     JSAutoByteString filename(cx, str);
        3389               0 :     if (!filename)
        3390               0 :         return JS_FALSE;
        3391                 :
        3392                 :     /* Get the currently executing script's name. */
        3393               0 :     JSScript *script = GetTopScript(cx);
        3394               0 :     JS_ASSERT(script->filename);
        3395               0 :     const char *pathname = filename.ptr();
        3396                 : #ifdef XP_UNIX
        3397               0 :     FreeOnReturn pnGuard(cx);
        3398               0 :     if (pathname[0] != '/') {
        3399               0 :         pathname = MakeAbsolutePathname(cx, script->filename, pathname);
        3400               0 :         if (!pathname)
        3401               0 :             return JS_FALSE;
        3402               0 :         pnGuard.init(pathname);
        3403                 :     }
        3404                 : #endif
        3405                 :
        3406               0 :     if (argc > 1) {
        3407               0 :         JSString *opt = JS_ValueToString(cx, JS_ARGV(cx, vp)[1]);
        3408               0 :         if (!opt)
        3409               0 :             return JS_FALSE;
        3410                 :         JSBool match;
        3411               0 :         if (!JS_StringEqualsAscii(cx, opt, "binary", &match))
        3412               0 :             return JS_FALSE;
        3413               0 :         if (match) {
        3414                 :             JSObject *obj;
        3415               0 :             if (!(obj = FileAsTypedArray(cx, pathname)))
        3416               0 :                 return JS_FALSE;
        3417               0 :             *vp = OBJECT_TO_JSVAL(obj);
        3418               0 :             return JS_TRUE;
        3419                 :         }
        3420                 :     }
        3421                 :
        3422               0 :     if (!(str = FileAsString(cx, pathname)))
        3423               0 :         return JS_FALSE;
        3424               0 :     *vp = STRING_TO_JSVAL(str);
        3425               0 :     return JS_TRUE;
        3426                 : }
        3427                 :
        3428                 : JSBool
        3429              27 : Wrap(JSContext *cx, unsigned argc, jsval *vp)
        3430                 : {
        3431              27 :     jsval v = argc > 0 ? JS_ARGV(cx, vp)[0] : JSVAL_VOID;
        3432              27 :     if (JSVAL_IS_PRIMITIVE(v)) {
        3433               9 :         JS_SET_RVAL(cx, vp, v);
        3434               9 :         return true;
        3435                 :     }
        3436                 :
        3437              18 :     JSObject *obj = JSVAL_TO_OBJECT(v);
        3438              18 :     JSObject *wrapped = Wrapper::New(cx, obj, obj->getProto(), &obj->global(),
        3439              18 :                                      &Wrapper::singleton);
        3440              18 :     if (!wrapped)
        3441               0 :         return false;
        3442                 :
        3443              18 :     JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(wrapped));
        3444              18 :     return true;
        3445                 : }
        3446                 :
        3447                 : JSBool
        3448              54 : Serialize(JSContext *cx, unsigned argc, jsval *vp)
        3449                 : {
        3450              54 :     jsval v = argc > 0 ? JS_ARGV(cx, vp)[0] : JSVAL_VOID;
        3451                 :     uint64_t *datap;
        3452                 :     size_t nbytes;
        3453              54 :     if (!JS_WriteStructuredClone(cx, v, &datap, &nbytes, NULL, NULL))
        3454               0 :         return false;
        3455                 :
        3456              54 :     JSObject *arrayobj = js_CreateTypedArray(cx, TypedArray::TYPE_UINT8, nbytes);
        3457              54 :     if (!arrayobj) {
        3458               0 :         JS_free(cx, datap);
        3459               0 :         return false;
        3460                 :     }
        3461              54 :     JSObject *array = TypedArray::getTypedArray(arrayobj);
        3462              54 :     JS_ASSERT((uintptr_t(TypedArray::getDataOffset(array)) & 7) == 0);
        3463              54 :     js_memcpy(TypedArray::getDataOffset(array), datap, nbytes);
        3464              54 :     JS_free(cx, datap);
        3465              54 :     JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(arrayobj));
        3466              54 :     return true;
        3467                 : }
        3468                 :
        3469                 : JSBool
        3470               9 : Deserialize(JSContext *cx, unsigned argc, jsval *vp)
        3471                 : {
        3472               9 :     jsval v = argc > 0 ? JS_ARGV(cx, vp)[0] : JSVAL_VOID;
        3473                 :     JSObject *obj;
        3474               9 :     if (JSVAL_IS_PRIMITIVE(v) || !js_IsTypedArray((obj = JSVAL_TO_OBJECT(v)))) {
        3475               0 :         JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_INVALID_ARGS, "deserialize");
        3476               0 :         return false;
        3477                 :     }
        3478               9 :     JSObject *array = TypedArray::getTypedArray(obj);
        3479               9 :     if ((TypedArray::getByteLength(array) & 7) != 0) {
        3480               0 :         JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_INVALID_ARGS, "deserialize");
        3481               0 :         return false;
        3482                 :     }
        3483               9 :     if ((uintptr_t(TypedArray::getDataOffset(array)) & 7) != 0) {
        3484               0 :         JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_BAD_ALIGNMENT);
        3485               0 :         return false;
        3486                 :     }
        3487                 :
        3488              18 :     if (!JS_ReadStructuredClone(cx, (uint64_t *) TypedArray::getDataOffset(array), TypedArray::getByteLength(array),
        3489              18 :                                 JS_STRUCTURED_CLONE_VERSION, &v, NULL, NULL)) {
        3490               0 :         return false;
        3491                 :     }
        3492               9 :     JS_SET_RVAL(cx, vp, v);
        3493               9 :     return true;
        3494                 : }
        3495                 :
        3496                 : enum CompartmentKind { SAME_COMPARTMENT, NEW_COMPARTMENT };
        3497                 :
        3498                 : static JSObject *
        3499                 : NewGlobalObject(JSContext *cx, CompartmentKind compartment);
        3500                 :
        3501                 : JSBool
        3502            4419 : NewGlobal(JSContext *cx, unsigned argc, jsval *vp)
        3503                 : {
        3504            4419 :     if (argc != 1 || !JSVAL_IS_STRING(JS_ARGV(cx, vp)[0])) {
        3505               0 :         JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_INVALID_ARGS, "newGlobal");
        3506               0 :         return false;
        3507                 :     }
        3508                 :
        3509            4419 :     JSString *str = JSVAL_TO_STRING(JS_ARGV(cx, vp)[0]);
        3510                 :
        3511            4419 :     JSBool equalSame = JS_FALSE, equalNew = JS_FALSE;
        3512            8838 :     if (!JS_StringEqualsAscii(cx, str, "same-compartment", &equalSame) ||
        3513            4419 :         !JS_StringEqualsAscii(cx, str, "new-compartment", &equalNew)) {
        3514               0 :         return false;
        3515                 :     }
        3516                 :
        3517            4419 :     if (!equalSame && !equalNew) {
        3518               0 :         JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_INVALID_ARGS, "newGlobal");
        3519               0 :         return false;
        3520                 :     }
        3521                 :
        3522            4419 :     JSObject *global = NewGlobalObject(cx, equalSame ? SAME_COMPARTMENT : NEW_COMPARTMENT);
        3523            4419 :     if (!global)
        3524               0 :         return false;
        3525                 :
        3526            4419 :     JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(global));
        3527            4419 :     return true;
        3528                 : }
        3529                 :
        3530                 : static JSBool
        3531               0 : ParseLegacyJSON(JSContext *cx, unsigned argc, jsval *vp)
        3532                 : {
        3533               0 :     if (argc != 1 || !JSVAL_IS_STRING(JS_ARGV(cx, vp)[0])) {
        3534               0 :         JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_INVALID_ARGS, "parseLegacyJSON");
        3535               0 :         return false;
        3536                 :     }
        3537                 :
        3538               0 :     JSString *str = JSVAL_TO_STRING(JS_ARGV(cx, vp)[0]);
        3539                 :
        3540                 :     size_t length;
        3541               0 :     const jschar *chars = JS_GetStringCharsAndLength(cx, str, &length);
        3542               0 :     if (!chars)
        3543               0 :         return false;
        3544               0 :     return js::ParseJSONWithReviver(cx, chars, length, js::NullValue(), vp, LEGACY);
        3545                 : }
        3546                 :
        3547                 : static JSBool
        3548               0 : EnableStackWalkingAssertion(JSContext *cx, unsigned argc, jsval *vp)
        3549                 : {
        3550               0 :     if (argc == 0 || !JSVAL_IS_BOOLEAN(JS_ARGV(cx, vp)[0])) {
        3551                 :         JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_INVALID_ARGS,
        3552               0 :                              "enableStackWalkingAssertion");
        3553               0 :         return false;
        3554                 :     }
        3555                 :
        3556                 : #ifdef DEBUG
        3557               0 :     cx->stackIterAssertionEnabled = JSVAL_TO_BOOLEAN(JS_ARGV(cx, vp)[0]);
        3558                 : #endif
        3559                 :
        3560               0 :     JS_SET_RVAL(cx, vp, JSVAL_VOID);
        3561               0 :     return true;
        3562                 : }
        3563                 :
        3564                 : static JSBool
        3565             234 : GetMaxArgs(JSContext *cx, unsigned arg, jsval *vp)
        3566                 : {
        3567             234 :     JS_SET_RVAL(cx, vp, INT_TO_JSVAL(StackSpace::ARGS_LENGTH_MAX));
        3568             234 :     return JS_TRUE;
        3569                 : }
        3570                 :
        3571                 : static JSFunctionSpecWithHelp shell_functions[] = {
        3572                 :     JS_FN_HELP("version", Version, 0, 0,
        3573                 : "version([number])",
        3574                 : "  Get or force a script compilation version number."),
        3575                 :
        3576                 :     JS_FN_HELP("revertVersion", RevertVersion, 0, 0,
        3577                 : "revertVersion()",
        3578                 : "  Revert previously set version number."),
        3579                 :
        3580                 :     JS_FN_HELP("options", Options, 0, 0,
        3581                 : "options([option ...])",
        3582                 : "  Get or toggle JavaScript options."),
        3583                 :
        3584                 :     JS_FN_HELP("load", Load, 1, 0,
        3585                 : "load(['foo.js' ...])",
        3586                 : "  Load files named by string arguments."),
        3587                 :
        3588                 :     JS_FN_HELP("evaluate", Evaluate, 1, 0,
        3589                 : "evaluate(code)",
        3590                 : "  Evaluate code as though it were the contents of a file."),
        3591                 :
        3592                 :     JS_FN_HELP("evalWithLocation", EvaluateWithLocation, 3, 0,
        3593                 : "evalWithLocation(code, filename, lineno)",
        3594                 : "  Eval code as if loaded from the given filename and line number."),
        3595                 :
        3596                 :     JS_FN_HELP("run", Run, 1, 0,
        3597                 : "run('foo.js')",
        3598                 : "  Run the file named by the first argument, returning the number of\n"
        3599                 : "  of milliseconds spent compiling and executing it."),
        3600                 :
        3601                 :     JS_FN_HELP("readline", ReadLine, 0, 0,
        3602                 : "readline()",
        3603                 : "  Read a single line from stdin."),
        3604                 :
        3605                 :     JS_FN_HELP("print", Print, 0, 0,
        3606                 : "print([exp ...])",
        3607                 : "  Evaluate and print expressions to stdout."),
        3608                 :
        3609                 :     JS_FN_HELP("printErr", PrintErr, 0, 0,
        3610                 : "printErr([exp ...])",
        3611                 : "  Evaluate and print expressions to stderr."),
        3612                 :
        3613                 :     JS_FN_HELP("putstr", PutStr, 0, 0,
        3614                 : "putstr([exp])",
        3615                 : "  Evaluate and print expression without newline."),
        3616                 :
        3617                 :     JS_FN_HELP("dateNow", Now, 0, 0,
        3618                 : "dateNow()",
        3619                 : "  Return the current time with sub-ms precision."),
        3620                 :
        3621                 :     JS_FN_HELP("help", Help, 0, 0,
        3622                 : "help([name ...])",
        3623                 : "  Display usage and help messages."),
        3624                 :
        3625                 :     JS_FN_HELP("quit", Quit, 0, 0,
        3626                 : "quit()",
        3627                 : "  Quit the shell."),
        3628                 :
        3629                 :     JS_FN_HELP("assertEq", AssertEq, 2, 0,
        3630                 : "assertEq(actual, expected[, msg])",
        3631                 : "  Throw if the first two arguments are not the same (both +0 or both -0,\n"
        3632                 : "  both NaN, or non-zero and ===)."),
        3633                 :
        3634                 :     JS_FN_HELP("assertJit", AssertJit, 0, 0,
        3635                 : "assertJit()",
        3636                 : "  Throw if the calling function failed to JIT."),
        3637                 :
        3638                 :     JS_FN_HELP("setDebug", SetDebug, 1, 0,
        3639                 : "setDebug(debug)",
        3640                 : "  Set debug mode."),
        3641                 :
        3642                 :     JS_FN_HELP("setDebuggerHandler", SetDebuggerHandler, 1, 0,
        3643                 : "setDebuggerHandler(f)",
        3644                 : "  Set handler for debugger keyword to f."),
        3645                 :
        3646                 :     JS_FN_HELP("setThrowHook", SetThrowHook, 1, 0,
        3647                 : "setThrowHook(f)",
        3648                 : "  Set throw hook to f."),
        3649                 :
        3650                 :     JS_FN_HELP("trap", Trap, 3, 0,
        3651                 : "trap([fun, [pc,]] exp)",
        3652                 : "  Trap bytecode execution."),
        3653                 :
        3654                 :     JS_FN_HELP("untrap", Untrap, 2, 0,
        3655                 : "untrap(fun[, pc])",
        3656                 : "  Remove a trap."),
        3657                 :
        3658                 :     JS_FN_HELP("line2pc", LineToPC, 0, 0,
        3659                 : "line2pc([fun,] line)",
        3660                 : "  Map line number to PC."),
        3661                 :
        3662                 :     JS_FN_HELP("pc2line", PCToLine, 0, 0,
        3663                 : "pc2line(fun[, pc])",
        3664                 : "  Map PC to line number."),
        3665                 :
        3666                 :     JS_FN_HELP("stringsAreUTF8", StringsAreUTF8, 0, 0,
        3667                 : "stringsAreUTF8()",
        3668                 : "  Check if strings are UTF-8 encoded."),
        3669                 :
        3670                 :     JS_FN_HELP("testUTF8", TestUTF8, 1, 0,
        3671                 : "testUTF8(mode)",
        3672                 : "  Perform UTF-8 tests (modes are 1 to 4)."),
        3673                 :
        3674                 :     JS_FN_HELP("throwError", ThrowError, 0, 0,
        3675                 : "throwError()",
        3676                 : "  Throw an error from JS_ReportError."),
        3677                 :
        3678                 : #ifdef DEBUG
        3679                 :     JS_FN_HELP("disassemble", DisassembleToString, 1, 0,
        3680                 : "disassemble([fun])",
        3681                 : "  Return the disassembly for the given function."),
        3682                 :
        3683                 :     JS_FN_HELP("dis", Disassemble, 1, 0,
        3684                 : "dis([fun])",
        3685                 : "  Disassemble functions into bytecodes."),
        3686                 :
        3687                 :     JS_FN_HELP("disfile", DisassFile, 1, 0,
        3688                 : "disfile('foo.js')",
        3689                 : "  Disassemble script file into bytecodes.\n"
        3690                 : "  dis and disfile take these options as preceeding string arguments:\n"
        3691                 : "    \"-r\" (disassemble recursively)\n"
        3692                 : "    \"-l\" (show line numbers)"),
        3693                 :
        3694                 :     JS_FN_HELP("dissrc", DisassWithSrc, 1, 0,
        3695                 : "dissrc([fun])",
        3696                 : "  Disassemble functions with source lines."),
        3697                 :
        3698                 :     JS_FN_HELP("dumpHeap", DumpHeap, 0, 0,
        3699                 : "dumpHeap([fileName[, start[, toFind[, maxDepth[, toIgnore]]]]])",
        3700                 : "  Interface to JS_DumpHeap with output sent to file."),
        3701                 :
        3702                 :     JS_FN_HELP("dumpObject", DumpObject, 1, 0,
        3703                 : "dumpObject()",
        3704                 : "  Dump an internal representation of an object."),
        3705                 :
        3706                 :     JS_FN_HELP("notes", Notes, 1, 0,
        3707                 : "notes([fun])",
        3708                 : "  Show source notes for functions."),
        3709                 :
        3710                 :     JS_FN_HELP("stats", DumpStats, 1, 0,
        3711                 : "stats([string ...])",
        3712                 : "  Dump 'atom' or 'global' stats."),
        3713                 :
        3714                 :     JS_FN_HELP("findReferences", FindReferences, 1, 0,
        3715                 : "findReferences(target)",
        3716                 : "  Walk the entire heap, looking for references to |target|, and return a\n"
        3717                 : "  \"references object\" describing what we found.\n"
        3718                 : "\n"
        3719                 : "  Each property of the references object describes one kind of reference. The\n"
        3720                 : "  property's name is the label supplied to MarkObject, JS_CALL_TRACER, or what\n"
        3721                 : "  have you, prefixed with \"edge: \" to avoid collisions with system properties\n"
        3722                 : "  (like \"toString\" and \"__proto__\"). The property's value is an array of things\n"
        3723                 : "  that refer to |thing| via that kind of reference. Ordinary references from\n"
        3724                 : "  one object to another are named after the property name (with the \"edge: \"\n"
        3725                 : "  prefix).\n"
        3726                 : "\n"
        3727                 : "  Garbage collection roots appear as references from 'null'. We use the name\n"
        3728                 : "  given to the root (with the \"edge: \" prefix) as the name of the reference.\n"
        3729                 : "\n"
        3730                 : "  Note that the references object does record references from objects that are\n"
        3731                 : "  only reachable via |thing| itself, not just the references reachable\n"
        3732                 : "  themselves from roots that keep |thing| from being collected. (We could make\n"
        3733                 : "  this distinction if it is useful.)\n"
        3734                 : "\n"
        3735                 : "  If any references are found by the conservative scanner, the references\n"
        3736                 : "  object will have a property named \"edge: machine stack\"; the referrers will\n"
        3737                 : "  be 'null', because they are roots."),
        3738                 :
        3739                 : #endif
        3740                 :     JS_FN_HELP("dumpStack", DumpStack, 1, 0,
        3741                 : "dumpStack()",
        3742                 : "  Dump the stack as an array of callees (youngest first)."),
        3743                 :
        3744                 : #ifdef TEST_CVTARGS
        3745                 :     JS_FN_HELP("cvtargs", ConvertArgs, 0, 0,
        3746                 : "cvtargs(arg1..., arg12)",
        3747                 : "  Test argument formatter."),
        3748                 :
        3749                 : #endif
        3750                 :     JS_FN_HELP("build", BuildDate, 0, 0,
        3751                 : "build()",
        3752                 : "  Show build date and time."),
        3753                 :
        3754                 :     JS_FN_HELP("clear", Clear, 0, 0,
        3755                 : "clear([obj])",
        3756                 : "  Clear properties of object."),
        3757                 :
        3758                 :     JS_FN_HELP("intern", Intern, 1, 0,
        3759                 : "intern(str)",
        3760                 : "  Internalize str in the atom table."),
        3761                 :
        3762                 :     JS_FN_HELP("clone", Clone, 1, 0,
        3763                 : "clone(fun[, scope])",
        3764                 : "  Clone function object."),
        3765                 :
        3766                 :     JS_FN_HELP("getpda", GetPDA, 1, 0,
        3767                 : "getpda(obj)",
        3768                 : "  Get the property descriptors for obj."),
        3769                 :
        3770                 :     JS_FN_HELP("getslx", GetSLX, 1, 0,
        3771                 : "getslx(obj)",
        3772                 : "  Get script line extent."),
        3773                 :
        3774                 :     JS_FN_HELP("toint32", ToInt32, 1, 0,
        3775                 : "toint32(n)",
        3776                 : "  Testing hook for JS_ValueToInt32."),
        3777                 :
        3778                 :     JS_FN_HELP("evalcx", EvalInContext, 1, 0,
        3779                 : "evalcx(s[, o])",
        3780                 : "  Evaluate s in optional sandbox object o.\n"
        3781                 : "  if (s == '' && !o) return new o with eager standard classes\n"
        3782                 : "  if (s == 'lazy' && !o) return new o with lazy standard classes"),
        3783                 :
        3784                 :     JS_FN_HELP("evalInFrame", EvalInFrame, 2, 0,
        3785                 : "evalInFrame(n,str,save)",
        3786                 : "  Evaluate 'str' in the nth up frame.\n"
        3787                 : "  If 'save' (default false), save the frame chain."),
        3788                 :
        3789                 :     JS_FN_HELP("shapeOf", ShapeOf, 1, 0,
        3790                 : "shapeOf(obj)",
        3791                 : "  Get the shape of obj (an implementation detail)."),
        3792                 :
        3793                 :     JS_FN_HELP("resolver", Resolver, 1, 0,
        3794                 : "resolver(src[, proto])",
        3795                 : "  Create object with resolve hook that copies properties\n"
        3796                 : "  from src. If proto is omitted, use Object.prototype."),
        3797                 :
        3798                 : #ifdef DEBUG
        3799                 :     JS_FN_HELP("arrayInfo", js_ArrayInfo, 1, 0,
        3800                 : "arrayInfo(a1, a2, ...)",
        3801                 : "  Report statistics about arrays."),
        3802                 :
        3803                 : #endif
        3804                 : #ifdef JS_THREADSAFE
        3805                 :     JS_FN_HELP("sleep", Sleep_fn, 1, 0,
        3806                 : "sleep(dt)",
        3807                 : "  Sleep for dt seconds."),
        3808                 :
        3809                 : #endif
        3810                 :     JS_FN_HELP("snarf", Snarf, 0, 0,
        3811                 : "snarf(filename)",
        3812                 : "  Read filename into returned string."),
        3813                 :
        3814                 :     JS_FN_HELP("read", Snarf, 0, 0,
        3815                 : "read(filename)",
        3816                 : "  Synonym for snarf."),
        3817                 :
        3818                 :     JS_FN_HELP("compile", Compile, 1, 0,
        3819                 : "compile(code)",
        3820                 : "  Compiles a string to bytecode, potentially throwing."),
        3821                 :
        3822                 :     JS_FN_HELP("parse", Parse, 1, 0,
        3823                 : "parse(code)",
        3824                 : "  Parses a string, potentially throwing."),
        3825                 :
        3826                 :     JS_FN_HELP("timeout", Timeout, 1, 0,
        3827                 : "timeout([seconds])",
        3828                 : "  Get/Set the limit in seconds for the execution time for the current context.\n"
        3829                 : "  A negative value (default) means that the execution time is unlimited."),
        3830                 :
        3831                 :     JS_FN_HELP("elapsed", Elapsed, 0, 0,
        3832                 : "elapsed()",
        3833                 : "  Execution time elapsed for the current context.."),
        3834                 :
        3835                 :     JS_FN_HELP("parent", Parent, 1, 0,
        3836                 : "parent(obj)",
        3837                 : "  Returns the parent of obj.."),
        3838                 :
        3839                 :     JS_FN_HELP("wrap", Wrap, 1, 0,
        3840                 : "wrap(obj)",
        3841                 : "  Wrap an object into a noop wrapper.."),
        3842                 :
        3843                 :     JS_FN_HELP("serialize", Serialize, 1, 0,
        3844                 : "serialize(sd)",
        3845                 : "  Serialize sd using JS_WriteStructuredClone. Returns a TypedArray.."),
        3846                 :
        3847                 :     JS_FN_HELP("deserialize", Deserialize, 1, 0,
        3848                 : "deserialize(a)",
        3849                 : "  Deserialize data generated by serialize.."),
        3850                 :
        3851                 :     JS_FN_HELP("newGlobal", NewGlobal, 1, 0,
        3852                 : "newGlobal(kind)",
        3853                 : "  Return a new global object, in the current\n"
        3854                 : "  compartment if kind === 'same-compartment' or in a\n"
        3855                 : "  new compartment if kind === 'new-compartment'."),
        3856                 :
        3857                 :     JS_FN_HELP("parseLegacyJSON", ParseLegacyJSON, 1, 0,
        3858                 : "parseLegacyJSON(str)",
        3859                 : "  Parse str as legacy JSON, returning the result if the\n"
        3860                 : "  parse succeeded and throwing a SyntaxError if not."),
        3861                 :
        3862                 :     JS_FN_HELP("enableStackWalkingAssertion", EnableStackWalkingAssertion, 1, 0,
        3863                 : "enableStackWalkingAssertion(enabled)",
        3864                 : "  Enables or disables a particularly expensive assertion in stack-walking\n"
        3865                 : "  code.  If your test isn't ridiculously thorough, such that performing this\n"
        3866                 : "  assertion increases test duration by an order of magnitude, you shouldn't\n"
        3867                 : "  use this."),
        3868                 :
        3869                 :     JS_FN_HELP("getMaxArgs", GetMaxArgs, 0, 0,
        3870                 : "getMaxArgs()",
        3871                 : "  Return the maximum number of supported args for a call."),
        3872                 :
        3873                 :     JS_FS_END
        3874                 : };
        3875                 : #ifdef MOZ_PROFILING
        3876                 : # define PROFILING_FUNCTION_COUNT 5
        3877                 : # ifdef MOZ_CALLGRIND
        3878                 : #  define CALLGRIND_FUNCTION_COUNT 3
        3879                 : # else
        3880                 : #  define CALLGRIND_FUNCTION_COUNT 0
        3881                 : # endif
        3882                 : # ifdef MOZ_VTUNE
        3883                 : #  define VTUNE_FUNCTION_COUNT 4
        3884                 : # else
        3885                 : #  define VTUNE_FUNCTION_COUNT 0
        3886                 : # endif
        3887                 : # define EXTERNAL_FUNCTION_COUNT (PROFILING_FUNCTION_COUNT + CALLGRIND_FUNCTION_COUNT + VTUNE_FUNCTION_COUNT)
        3888                 : #else
        3889                 : # define EXTERNAL_FUNCTION_COUNT 0
        3890                 : #endif
        3891                 :
        3892                 : #undef PROFILING_FUNCTION_COUNT
        3893                 : #undef CALLGRIND_FUNCTION_COUNT
        3894                 : #undef VTUNE_FUNCTION_COUNT
        3895                 : #undef EXTERNAL_FUNCTION_COUNT
        3896                 :
        3897                 : static bool
        3898               0 : PrintHelpString(JSContext *cx, jsval v)
        3899                 : {
        3900               0 :     JSString *str = JSVAL_TO_STRING(v);
        3901               0 :     JS::Anchor<JSString *> a_str(str);
        3902               0 :     const jschar *chars = JS_GetStringCharsZ(cx, str);
        3903               0 :     if (!chars)
        3904               0 :         return false;
        3905                 :
        3906               0 :     for (const jschar *p = chars; *p; p++)
        3907               0 :         fprintf(gOutFile, "%c", char(*p));
        3908                 :
        3909               0 :     fprintf(gOutFile, "\n");
        3910                 :
        3911               0 :     return true;
        3912                 : }
        3913                 :
        3914                 : static bool
        3915               0 : PrintHelp(JSContext *cx, JSObject *obj)
        3916                 : {
        3917                 :     jsval usage, help;
        3918               0 :     if (!JS_LookupProperty(cx, obj, "usage", &usage))
        3919               0 :         return false;
        3920               0 :     if (!JS_LookupProperty(cx, obj, "help", &help))
        3921               0 :         return false;
        3922                 :
        3923               0 :     if (JSVAL_IS_VOID(usage) || JSVAL_IS_VOID(help))
        3924               0 :         return true;
        3925                 :
        3926               0 :     return PrintHelpString(cx, usage) && PrintHelpString(cx, help);
        3927                 : }
        3928                 :
        3929                 : static JSBool
        3930               0 : Help(JSContext *cx, unsigned argc, jsval *vp)
        3931                 : {
        3932               0 :     fprintf(gOutFile, "%s\n", JS_GetImplementationVersion());
        3933                 :
        3934               0 :     if (argc == 0) {
        3935               0 :         JSObject *global = JS_GetGlobalObject(cx);
        3936               0 :         AutoIdArray ida(cx, JS_Enumerate(cx, global));
        3937               0 :         if (!ida)
        3938               0 :             return false;
        3939                 :
        3940               0 :         for (size_t i = 0; i < ida.length(); i++) {
        3941                 :             jsval v;
        3942               0 :             if (!JS_LookupPropertyById(cx, global, ida[i], &v))
        3943               0 :                 return false;
        3944               0 :             if (JSVAL_IS_OBJECT(v) && !PrintHelp(cx, JSVAL_TO_OBJECT(v)))
        3945               0 :                 return false;
        3946                 :         }
        3947                 :     } else {
        3948               0 :         jsval *argv = JS_ARGV(cx, vp);
        3949               0 :         for (unsigned i = 0; i < argc; i++) {
        3950               0 :             if (JSVAL_IS_OBJECT(argv[i]) && !PrintHelp(cx, JSVAL_TO_OBJECT(argv[i])))
        3951               0 :                 return false;
        3952                 :         }
        3953                 :     }
        3954                 :
        3955               0 :     JS_SET_RVAL(cx, vp, JSVAL_VOID);
        3956               0 :     return true;
        3957                 : }
        3958                 :
        3959                 : /*
        3960                 :  * Define a JS object called "it".  Give it class operations that printf why
        3961                 :  * they're being called for tutorial purposes.
        3962                 :  */
        3963                 : enum its_tinyid {
        3964                 :     ITS_COLOR, ITS_HEIGHT, ITS_WIDTH, ITS_FUNNY, ITS_ARRAY, ITS_RDONLY,
        3965                 :     ITS_CUSTOM, ITS_CUSTOMRDONLY
        3966                 : };
        3967                 :
        3968                 : static JSBool
        3969                 : its_getter(JSContext *cx, JSObject *obj, jsid id, jsval *vp);
        3970                 :
        3971                 : static JSBool
        3972                 : its_setter(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp);
        3973                 :
        3974                 : static JSPropertySpec its_props[] = {
        3975                 :     {"color",           ITS_COLOR,      JSPROP_ENUMERATE,       NULL, NULL},
        3976                 :     {"height",          ITS_HEIGHT,     JSPROP_ENUMERATE,       NULL, NULL},
        3977                 :     {"width",           ITS_WIDTH,      JSPROP_ENUMERATE,       NULL, NULL},
        3978                 :     {"funny",           ITS_FUNNY,      JSPROP_ENUMERATE,       NULL, NULL},
        3979                 :     {"array",           ITS_ARRAY,      JSPROP_ENUMERATE,       NULL, NULL},
        3980                 :     {"rdonly",          ITS_RDONLY,     JSPROP_READONLY,        NULL, NULL},
        3981                 :     {"custom",          ITS_CUSTOM,     JSPROP_ENUMERATE,
        3982                 :                         its_getter,     its_setter},
        3983                 :     {"customRdOnly",    ITS_CUSTOMRDONLY, JSPROP_ENUMERATE | JSPROP_READONLY,
        3984                 :                         its_getter,     its_setter},
        3985                 :     {NULL,0,0,NULL,NULL}
        3986                 : };
        3987                 :
        3988                 : static JSBool its_noisy;    /* whether to be noisy when finalizing it */
        3989                 : static JSBool its_enum_fail;/* whether to fail when enumerating it */
        3990                 :
        3991                 : static JSBool
        3992          182592 : its_addProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
        3993                 : {
        3994          182592 :     if (!its_noisy)
        3995          182592 :         return JS_TRUE;
        3996                 :
        3997               0 :     IdStringifier idString(cx, id);
        3998               0 :     fprintf(gOutFile, "adding its property %s,", idString.getBytes());
        3999               0 :     ToStringHelper valueString(cx, *vp);
        4000               0 :     fprintf(gOutFile, " initial value %s\n", valueString.getBytes());
        4001               0 :     return JS_TRUE;
        4002                 : }
        4003                 :
        4004                 : static JSBool
        4005               0 : its_delProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
        4006                 : {
        4007               0 :     if (!its_noisy)
        4008               0 :         return JS_TRUE;
        4009                 :
        4010               0 :     IdStringifier idString(cx, id);
        4011               0 :     fprintf(gOutFile, "deleting its property %s,", idString.getBytes());
        4012               0 :     ToStringHelper valueString(cx, *vp);
        4013               0 :     fprintf(gOutFile, " initial value %s\n", valueString.getBytes());
        4014               0 :     return JS_TRUE;
        4015                 : }
        4016                 :
        4017                 : static JSBool
        4018               0 : its_getProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
        4019                 : {
        4020               0 :     if (!its_noisy)
        4021               0 :         return JS_TRUE;
        4022                 :
        4023               0 :     IdStringifier idString(cx, id);
        4024               0 :     fprintf(gOutFile, "getting its property %s,", idString.getBytes());
        4025               0 :     ToStringHelper valueString(cx, *vp);
        4026               0 :     fprintf(gOutFile, " initial value %s\n", valueString.getBytes());
        4027               0 :     return JS_TRUE;
        4028                 : }
        4029                 :
        4030                 : static JSBool
        4031               0 : its_setProperty(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp)
        4032                 : {
        4033               0 :     IdStringifier idString(cx, id);
        4034               0 :     if (its_noisy) {
        4035               0 :         fprintf(gOutFile, "setting its property %s,", idString.getBytes());
        4036               0 :         ToStringHelper valueString(cx, *vp);
        4037               0 :         fprintf(gOutFile, " new value %s\n", valueString.getBytes());
        4038                 :     }
        4039                 :
        4040               0 :     if (!JSID_IS_ATOM(id))
        4041               0 :         return JS_TRUE;
        4042                 :
        4043               0 :     if (!strcmp(idString.getBytes(), "noisy"))
        4044               0 :         JS_ValueToBoolean(cx, *vp, &its_noisy);
        4045               0 :     else if (!strcmp(idString.getBytes(), "enum_fail"))
        4046               0 :         JS_ValueToBoolean(cx, *vp, &its_enum_fail);
        4047                 :
        4048               0 :     return JS_TRUE;
        4049                 : }
        4050                 :
        4051                 : /*
        4052                 :  * Its enumerator, implemented using the "new" enumerate API,
        4053                 :  * see class flags.
        4054                 :  */
        4055                 : static JSBool
        4056               0 : its_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
        4057                 :               jsval *statep, jsid *idp)
        4058                 : {
        4059                 :     JSObject *iterator;
        4060                 :
        4061               0 :     switch (enum_op) {
        4062                 :       case JSENUMERATE_INIT:
        4063                 :       case JSENUMERATE_INIT_ALL:
        4064               0 :         if (its_noisy)
        4065               0 :             fprintf(gOutFile, "enumerate its properties\n");
        4066                 :
        4067               0 :         iterator = JS_NewPropertyIterator(cx, obj);
        4068               0 :         if (!iterator)
        4069               0 :             return JS_FALSE;
        4070                 :
        4071               0 :         *statep = OBJECT_TO_JSVAL(iterator);
        4072               0 :         if (idp)
        4073               0 :             *idp = INT_TO_JSID(0);
        4074               0 :         break;
        4075                 :
        4076                 :       case JSENUMERATE_NEXT:
        4077               0 :         if (its_enum_fail) {
        4078               0 :             JS_ReportError(cx, "its enumeration failed");
        4079               0 :             return JS_FALSE;
        4080                 :         }
        4081                 :
        4082               0 :         iterator = (JSObject *) JSVAL_TO_OBJECT(*statep);
        4083               0 :         if (!JS_NextProperty(cx, iterator, idp))
        4084               0 :             return JS_FALSE;
        4085                 :
        4086               0 :         if (!JSID_IS_VOID(*idp))
        4087               0 :             break;
        4088                 :         /* Fall through. */
        4089                 :
        4090                 :       case JSENUMERATE_DESTROY:
        4091                 :         /* Allow our iterator object to be GC'd. */
        4092               0 :         *statep = JSVAL_NULL;
        4093               0 :         break;
        4094                 :     }
        4095                 :
        4096               0 :     return JS_TRUE;
        4097                 : }
        4098                 :
        4099                 : static JSBool
        4100               0 : its_resolve(JSContext *cx, JSObject *obj, jsid id, unsigned flags,
        4101                 :             JSObject **objp)
        4102                 : {
        4103               0 :     if (its_noisy) {
        4104               0 :         IdStringifier idString(cx, id);
        4105                 :         fprintf(gOutFile, "resolving its property %s, flags {%s,%s,%s}\n",
        4106                 :                idString.getBytes(),
        4107                 :                (flags & JSRESOLVE_QUALIFIED) ? "qualified" : "",
        4108                 :                (flags & JSRESOLVE_ASSIGNING) ? "assigning" : "",
        4109               0 :                (flags & JSRESOLVE_DETECTING) ? "detecting" : "");
        4110                 :     }
        4111               0 :     return JS_TRUE;
        4112                 : }
        4113                 :
        4114                 : static JSBool
        4115               0 : its_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
        4116                 : {
        4117               0 :     if (its_noisy)
        4118               0 :         fprintf(gOutFile, "converting it to %s type\n", JS_GetTypeName(cx, type));
        4119               0 :     return JS_ConvertStub(cx, obj, type, vp);
        4120                 : }
        4121                 :
        4122                 : static void
        4123           45648 : its_finalize(JSContext *cx, JSObject *obj)
        4124                 : {
        4125                 :     jsval *rootedVal;
        4126           45648 :     if (its_noisy)
        4127               0 :         fprintf(gOutFile, "finalizing it\n");
        4128           45648 :     rootedVal = (jsval *) JS_GetPrivate(obj);
        4129           45648 :     if (rootedVal) {
        4130               0 :       JS_RemoveValueRoot(cx, rootedVal);
        4131               0 :       JS_SetPrivate(obj, NULL);
        4132               0 :       delete rootedVal;
        4133                 :     }
        4134           45648 : }
        4135                 :
        4136                 : static JSClass its_class = {
        4137                 :     "It", JSCLASS_NEW_RESOLVE | JSCLASS_NEW_ENUMERATE | JSCLASS_HAS_PRIVATE,
        4138                 :     its_addProperty,  its_delProperty,  its_getProperty,  its_setProperty,
        4139                 :     (JSEnumerateOp)its_enumerate, (JSResolveOp)its_resolve,
        4140                 :     its_convert,      its_finalize,
        4141                 :     JSCLASS_NO_OPTIONAL_MEMBERS
        4142                 : };
        4143                 :
        4144                 : static JSBool
        4145             144 : its_getter(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
        4146                 : {
        4147             144 :     if (JS_GetClass(obj) == &its_class) {
        4148               0 :         jsval *val = (jsval *) JS_GetPrivate(obj);
        4149               0 :         *vp = val ? *val : JSVAL_VOID;
        4150                 :     } else {
        4151             144 :         *vp = JSVAL_VOID;
        4152                 :     }
        4153                 :
        4154             144 :     return JS_TRUE;
        4155                 : }
        4156                 :
        4157                 : static JSBool
        4158               0 : its_setter(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp)
        4159                 : {
        4160               0 :     if (JS_GetClass(obj) != &its_class)
        4161               0 :         return JS_TRUE;
        4162                 :
        4163               0 :     jsval *val = (jsval *) JS_GetPrivate(obj);
        4164               0 :     if (val) {
        4165               0 :         *val = *vp;
        4166               0 :         return JS_TRUE;
        4167                 :     }
        4168                 :
        4169               0 :     val = new jsval;
        4170               0 :     if (!val) {
        4171               0 :         JS_ReportOutOfMemory(cx);
        4172               0 :         return JS_FALSE;
        4173                 :     }
        4174                 :
        4175               0 :     if (!JS_AddValueRoot(cx, val)) {
        4176               0 :         delete val;
        4177               0 :         return JS_FALSE;
        4178                 :     }
        4179                 :
        4180               0 :     JS_SetPrivate(obj, (void*)val);
        4181                 :
        4182               0 :     *val = *vp;
        4183               0 :     return JS_TRUE;
        4184                 : }
        4185                 :
        4186                 : JSErrorFormatString jsShell_ErrorFormatString[JSShellErr_Limit] = {
        4187                 : #define MSG_DEF(name, number, count, exception, format) \
        4188                 :     { format, count, JSEXN_ERR } ,
        4189                 : #include "jsshell.msg"
        4190                 : #undef MSG_DEF
        4191                 : };
        4192                 :
        4193                 : static const JSErrorFormatString *
        4194             540 : my_GetErrorMessage(void *userRef, const char *locale, const unsigned errorNumber)
        4195                 : {
        4196             540 :     if (errorNumber == 0 || errorNumber >= JSShellErr_Limit)
        4197               0 :         return NULL;
        4198                 :
        4199             540 :     return &jsShell_ErrorFormatString[errorNumber];
        4200                 : }
        4201                 :
        4202                 : static void
        4203             927 : my_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
        4204                 : {
        4205                 :     int i, j, k, n;
        4206                 :     char *prefix, *tmp;
        4207                 :     const char *ctmp;
        4208                 :
        4209             927 :     if (!report) {
        4210               0 :         fprintf(gErrFile, "%s\n", message);
        4211               0 :         fflush(gErrFile);
        4212               0 :         return;
        4213                 :     }
        4214                 :
        4215                 :     /* Conditionally ignore reported warnings. */
        4216             927 :     if (JSREPORT_IS_WARNING(report->flags) && !reportWarnings)
        4217               0 :         return;
        4218                 :
        4219             927 :     prefix = NULL;
        4220             927 :     if (report->filename)
        4221             774 :         prefix = JS_smprintf("%s:", report->filename);
        4222             927 :     if (report->lineno) {
        4223             774 :         tmp = prefix;
        4224             774 :         prefix = JS_smprintf("%s%u: ", tmp ? tmp : "", report->lineno);
        4225             774 :         JS_free(cx, tmp);
        4226                 :     }
        4227             927 :     if (JSREPORT_IS_WARNING(report->flags)) {
        4228              72 :         tmp = prefix;
        4229                 :         prefix = JS_smprintf("%s%swarning: ",
        4230                 :                              tmp ? tmp : "",
        4231              72 :                              JSREPORT_IS_STRICT(report->flags) ? "strict " : "");
        4232              72 :         JS_free(cx, tmp);
        4233                 :     }
        4234                 :
        4235                 :     /* embedded newlines -- argh! */
        4236            1854 :     while ((ctmp = strchr(message, '\n')) != 0) {
        4237               0 :         ctmp++;
        4238               0 :         if (prefix)
        4239               0 :             fputs(prefix, gErrFile);
        4240               0 :         fwrite(message, 1, ctmp - message, gErrFile);
        4241               0 :         message = ctmp;
        4242                 :     }
        4243                 :
        4244                 :     /* If there were no filename or lineno, the prefix might be empty */
        4245             927 :     if (prefix)
        4246             774 :         fputs(prefix, gErrFile);
        4247             927 :     fputs(message, gErrFile);
        4248                 :
        4249             927 :     if (!report->linebuf) {
        4250             882 :         fputc('\n', gErrFile);
        4251             882 :         goto out;
        4252                 :     }
        4253                 :
        4254                 :     /* report->linebuf usually ends with a newline. */
        4255              45 :     n = strlen(report->linebuf);
        4256                 :     fprintf(gErrFile, ":\n%s%s%s%s",
        4257                 :             prefix,
        4258                 :             report->linebuf,
        4259              18 :             (n > 0 && report->linebuf[n-1] == '\n') ? "" : "\n",
        4260              63 :             prefix);
        4261              45 :     n = report->tokenptr - report->linebuf;
        4262             414 :     for (i = j = 0; i < n; i++) {
        4263             369 :         if (report->linebuf[i] == '\t') {
        4264               0 :             for (k = (j + 8) & ~7; j < k; j++) {
        4265               0 :                 fputc('.', gErrFile);
        4266                 :             }
        4267               0 :             continue;
        4268                 :         }
        4269             369 :         fputc('.', gErrFile);
        4270             369 :         j++;
        4271                 :     }
        4272              45 :     fputs("^\n", gErrFile);
        4273                 :  out:
        4274             927 :     fflush(gErrFile);
        4275             927 :     if (!JSREPORT_IS_WARNING(report->flags)) {
        4276             855 :         if (report->errorNumber == JSMSG_OUT_OF_MEMORY) {
        4277              18 :             gExitCode = EXITCODE_OUT_OF_MEMORY;
        4278                 :         } else {
        4279             837 :             gExitCode = EXITCODE_RUNTIME_ERROR;
        4280                 :         }
        4281                 :     }
        4282             927 :     JS_free(cx, prefix);
        4283                 : }
        4284                 :
        4285                 : #if defined(SHELL_HACK) && defined(DEBUG) && defined(XP_UNIX)
        4286                 : static JSBool
        4287                 : Exec(JSContext *cx, unsigned argc, jsval *vp)
        4288                 : {
        4289                 :     JSFunction *fun;
        4290                 :     const char *name, **nargv;
        4291                 :     unsigned i, nargc;
        4292                 :     JSString *str;
        4293                 :     bool ok;
        4294                 :     pid_t pid;
        4295                 :     int status;
        4296                 :
        4297                 :     JS_SET_RVAL(cx, vp, JSVAL_VOID);
        4298                 :
        4299                 :     fun = JS_ValueToFunction(cx, vp[0]);
        4300                 :     if (!fun)
        4301                 :         return JS_FALSE;
        4302                 :     if (!fun->atom)
        4303                 :         return JS_TRUE;
        4304                 :
        4305                 :     nargc = 1 + argc;
        4306                 :
        4307                 :     /* nargc + 1 accounts for the terminating NULL. */
        4308                 :     nargv = new (char *)[nargc + 1];
        4309                 :     if (!nargv)
        4310                 :         return JS_FALSE;
        4311                 :     memset(nargv, 0, sizeof(nargv[0]) * (nargc + 1));
        4312                 :     nargv[0] = name;
        4313                 :     jsval *argv = JS_ARGV(cx, vp);
        4314                 :     for (i = 0; i < nargc; i++) {
        4315                 :         str = (i == 0) ? fun->atom : JS_ValueToString(cx, argv[i-1]);
        4316                 :         if (!str) {
        4317                 :             ok = false;
        4318                 :             goto done;
        4319                 :         }
        4320                 :         nargv[i] = JS_EncodeString(cx, str);
        4321                 :         if (!nargv[i]) {
        4322                 :             ok = false;
        4323                 :             goto done;
        4324                 :         }
        4325                 :     }
        4326                 :     pid = fork();
        4327                 :     switch (pid) {
        4328                 :       case -1:
        4329                 :         perror("js");
        4330                 :         break;
        4331                 :       case 0:
        4332                 :         (void) execvp(name, (char **)nargv);
        4333                 :         perror("js");
        4334                 :         exit(127);
        4335                 :       default:
        4336                 :         while (waitpid(pid, &status, 0) < 0 && errno == EINTR)
        4337                 :             continue;
        4338                 :         break;
        4339                 :     }
        4340                 :     ok = true;
        4341                 :
        4342                 :   done:
        4343                 :     for (i = 0; i < nargc; i++)
        4344                 :         JS_free(cx, nargv[i]);
        4345                 :     delete[] nargv;
        4346                 :     return ok;
        4347                 : }
        4348                 : #endif
        4349                 :
        4350                 : static JSBool
        4351          144441 : global_enumerate(JSContext *cx, JSObject *obj)
        4352                 : {
        4353                 : #ifdef LAZY_STANDARD_CLASSES
        4354          144441 :     return JS_EnumerateStandardClasses(cx, obj);
        4355                 : #else
        4356                 :     return JS_TRUE;
        4357                 : #endif
        4358                 : }
        4359                 :
        4360                 : static JSBool
        4361          622728 : global_resolve(JSContext *cx, JSObject *obj, jsid id, unsigned flags,
        4362                 :                JSObject **objp)
        4363                 : {
        4364                 : #ifdef LAZY_STANDARD_CLASSES
        4365                 :     JSBool resolved;
        4366                 :
        4367          622728 :     if (!JS_ResolveStandardClass(cx, obj, id, &resolved))
        4368               0 :         return JS_FALSE;
        4369          622728 :     if (resolved) {
        4370            7372 :         *objp = obj;
        4371            7372 :         return JS_TRUE;
        4372                 :     }
        4373                 : #endif
        4374                 :
        4375                 : #if defined(SHELL_HACK) && defined(DEBUG) && defined(XP_UNIX)
        4376                 :     if (!(flags & JSRESOLVE_QUALIFIED)) {
        4377                 :         /*
        4378                 :          * Do this expensive hack only for unoptimized Unix builds, which are
        4379                 :          * not used for benchmarking.
        4380                 :          */
        4381                 :         char *path, *comp, *full;
        4382                 :         const char *name;
        4383                 :         JSBool ok, found;
        4384                 :         JSFunction *fun;
        4385                 :
        4386                 :         if (!JSVAL_IS_STRING(id))
        4387                 :             return JS_TRUE;
        4388                 :         path = getenv("PATH");
        4389                 :         if (!path)
        4390                 :             return JS_TRUE;
        4391                 :         path = JS_strdup(cx, path);
        4392                 :         if (!path)
        4393                 :             return JS_FALSE;
        4394                 :         JSAutoByteString name(cx, JSVAL_TO_STRING(id));
        4395                 :         if (!name)
        4396                 :             return JS_FALSE;
        4397                 :         ok = JS_TRUE;
        4398                 :         for (comp = strtok(path, ":"); comp; comp = strtok(NULL, ":")) {
        4399                 :             if (*comp != '\0') {
        4400                 :                 full = JS_smprintf("%s/%s", comp, name.ptr());
        4401                 :                 if (!full) {
        4402                 :                     JS_ReportOutOfMemory(cx);
        4403                 :                     ok = JS_FALSE;
        4404                 :                     break;
        4405                 :                 }
        4406                 :             } else {
        4407                 :                 full = (char *)name;
        4408                 :             }
        4409                 :             found = (access(full, X_OK) == 0);
        4410                 :             if (*comp != '\0')
        4411                 :                 free(full);
        4412                 :             if (found) {
        4413                 :                 fun = JS_DefineFunction(cx, obj, name, Exec, 0,
        4414                 :                                         JSPROP_ENUMERATE);
        4415                 :                 ok = (fun != NULL);
        4416                 :                 if (ok)
        4417                 :                     *objp = obj;
        4418                 :                 break;
        4419                 :             }
        4420                 :         }
        4421                 :         JS_free(cx, path);
        4422                 :         return ok;
        4423                 :     }
        4424                 : #else
        4425          615356 :     return JS_TRUE;
        4426                 : #endif
        4427                 : }
        4428                 :
        4429                 : JSClass global_class = {
        4430                 :     "global", JSCLASS_NEW_RESOLVE | JSCLASS_GLOBAL_FLAGS | JSCLASS_HAS_PRIVATE,
        4431                 :     JS_PropertyStub,  JS_PropertyStub,
        4432                 :     JS_PropertyStub,  JS_StrictPropertyStub,
        4433                 :     global_enumerate, (JSResolveOp) global_resolve,
        4434                 :     JS_ConvertStub,   its_finalize,
        4435                 :     JSCLASS_NO_OPTIONAL_MEMBERS
        4436                 : };
        4437                 :
        4438                 : static JSBool
        4439               0 : env_setProperty(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp)
        4440                 : {
        4441                 : /* XXX porting may be easy, but these don't seem to supply setenv by default */
        4442                 : #if !defined XP_OS2 && !defined SOLARIS
        4443                 :     int rv;
        4444                 :
        4445               0 :     IdStringifier idstr(cx, id, JS_TRUE);
        4446               0 :     if (idstr.threw())
        4447               0 :         return JS_FALSE;
        4448               0 :     ToStringHelper valstr(cx, *vp, JS_TRUE);
        4449               0 :     if (valstr.threw())
        4450               0 :         return JS_FALSE;
        4451                 : #if defined XP_WIN || defined HPUX || defined OSF1
        4452                 :     {
        4453                 :         char *waste = JS_smprintf("%s=%s", idstr.getBytes(), valstr.getBytes());
        4454                 :         if (!waste) {
        4455                 :             JS_ReportOutOfMemory(cx);
        4456                 :             return JS_FALSE;
        4457                 :         }
        4458                 :         rv = putenv(waste);
        4459                 : #ifdef XP_WIN
        4460                 :         /*
        4461                 :          * HPUX9 at least still has the bad old non-copying putenv.
        4462                 :          *
        4463                 :          * Per mail from <s.shanmuganathan@digital.com>, OSF1 also has a putenv
        4464                 :          * that will crash if you pass it an auto char array (so it must place
        4465                 :          * its argument directly in the char *environ[] array).
        4466                 :          */
        4467                 :         JS_smprintf_free(waste);
        4468                 : #endif
        4469                 :     }
        4470                 : #else
        4471               0 :     rv = setenv(idstr.getBytes(), valstr.getBytes(), 1);
        4472                 : #endif
        4473               0 :     if (rv < 0) {
        4474               0 :         JS_ReportError(cx, "can't set env variable %s to %s", idstr.getBytes(), valstr.getBytes());
        4475               0 :         return JS_FALSE;
        4476                 :     }
        4477               0 :     *vp = valstr.getJSVal();
        4478                 : #endif /* !defined XP_OS2 && !defined SOLARIS */
        4479               0 :     return JS_TRUE;
        4480                 : }
        4481                 :
        4482                 : static JSBool
        4483               0 : env_enumerate(JSContext *cx, JSObject *obj)
        4484                 : {
        4485                 :     static JSBool reflected;
        4486                 :     char **evp, *name, *value;
        4487                 :     JSString *valstr;
        4488                 :     JSBool ok;
        4489                 :
        4490               0 :     if (reflected)
        4491               0 :         return JS_TRUE;
        4492                 :
        4493               0 :     for (evp = (char **)JS_GetPrivate(obj); (name = *evp) != NULL; evp++) {
        4494               0 :         value = strchr(name, '=');
        4495               0 :         if (!value)
        4496               0 :             continue;
        4497               0 :         *value++ = '\0';
        4498               0 :         valstr = JS_NewStringCopyZ(cx, value);
        4499               0 :         if (!valstr) {
        4500               0 :             ok = JS_FALSE;
        4501                 :         } else {
        4502                 :             ok = JS_DefineProperty(cx, obj, name, STRING_TO_JSVAL(valstr),
        4503               0 :                                    NULL, NULL, JSPROP_ENUMERATE);
        4504                 :         }
        4505               0 :         value[-1] = '=';
        4506               0 :         if (!ok)
        4507               0 :             return JS_FALSE;
        4508                 :     }
        4509                 :
        4510               0 :     reflected = JS_TRUE;
        4511               0 :     return JS_TRUE;
        4512                 : }
        4513                 :
        4514                 : static JSBool
        4515               0 : env_resolve(JSContext *cx, JSObject *obj, jsid id, unsigned flags,
        4516                 :             JSObject **objp)
        4517                 : {
        4518                 :     JSString *valstr;
        4519                 :     const char *name, *value;
        4520                 :
        4521               0 :     if (flags & JSRESOLVE_ASSIGNING)
        4522               0 :         return JS_TRUE;
        4523                 :
        4524               0 :     IdStringifier idstr(cx, id, JS_TRUE);
        4525               0 :     if (idstr.threw())
        4526               0 :         return JS_FALSE;
        4527                 :
        4528               0 :     name = idstr.getBytes();
        4529               0 :     value = getenv(name);
        4530               0 :     if (value) {
        4531               0 :         valstr = JS_NewStringCopyZ(cx, value);
        4532               0 :         if (!valstr)
        4533               0 :             return JS_FALSE;
        4534               0 :         if (!JS_DefineProperty(cx, obj, name, STRING_TO_JSVAL(valstr),
        4535               0 :                                NULL, NULL, JSPROP_ENUMERATE)) {
        4536               0 :             return JS_FALSE;
        4537                 :         }
        4538               0 :         *objp = obj;
        4539                 :     }
        4540               0 :     return JS_TRUE;
        4541                 : }
        4542                 :
        4543                 : static JSClass env_class = {
        4544                 :     "environment", JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE,
        4545                 :     JS_PropertyStub,  JS_PropertyStub,
        4546                 :     JS_PropertyStub,  env_setProperty,
        4547                 :     env_enumerate, (JSResolveOp) env_resolve,
        4548                 :     JS_ConvertStub,   NULL,
        4549                 :     JSCLASS_NO_OPTIONAL_MEMBERS
        4550                 : };
        4551                 :
        4552                 : /*
        4553                 :  * Avoid a reentrancy hazard.
        4554                 :  *
        4555                 :  * The non-JS_THREADSAFE shell uses a signal handler to implement timeout().
        4556                 :  * The JS engine is not really reentrant, but JS_TriggerAllOperationCallbacks
        4557                 :  * is mostly safe--the only danger is that we might interrupt JS_NewContext or
        4558                 :  * JS_DestroyContext while the context list is being modified. Therefore we
        4559                 :  * disable the signal handler around calls to those functions.
        4560                 :  */
        4561                 : #ifdef JS_THREADSAFE
        4562                 : # define WITH_SIGNALS_DISABLED(x)  x
        4563                 : #else
        4564                 : # define WITH_SIGNALS_DISABLED(x)                                               \
        4565                 :     JS_BEGIN_MACRO                                                              \
        4566                 :         ScheduleWatchdog(gRuntime, -1);                                         \
        4567                 :         x;                                                                      \
        4568                 :         ScheduleWatchdog(gRuntime, gTimeoutInterval);                           \
        4569                 :     JS_END_MACRO
        4570                 : #endif
        4571                 :
        4572                 : static JSContext *
        4573           18405 : NewContext(JSRuntime *rt)
        4574                 : {
        4575                 :     JSContext *cx;
        4576           18405 :     WITH_SIGNALS_DISABLED(cx = JS_NewContext(rt, gStackChunkSize));
        4577           18405 :     if (!cx)
        4578               0 :         return NULL;
        4579                 :
        4580           18405 :     JSShellContextData *data = NewContextData();
        4581           18405 :     if (!data) {
        4582               0 :         DestroyContext(cx, false);
        4583               0 :         return NULL;
        4584                 :     }
        4585                 :
        4586           18405 :     JS_SetContextPrivate(cx, data);
        4587           18405 :     JS_SetErrorReporter(cx, my_ErrorReporter);
        4588           18405 :     JS_SetVersion(cx, JSVERSION_LATEST);
        4589           18405 :     SetContextOptions(cx);
        4590           18405 :     if (enableMethodJit)
        4591               0 :         JS_ToggleOptions(cx, JSOPTION_METHODJIT);
        4592           18405 :     if (enableTypeInference)
        4593               0 :         JS_ToggleOptions(cx, JSOPTION_TYPE_INFERENCE);
        4594           18405 :     return cx;
        4595                 : }
        4596                 :
        4597                 : static void
        4598           18405 : DestroyContext(JSContext *cx, bool withGC)
        4599                 : {
        4600           18405 :     JSShellContextData *data = GetContextData(cx);
        4601           18405 :     JS_SetContextPrivate(cx, NULL);
        4602           18405 :     free(data);
        4603           18405 :     WITH_SIGNALS_DISABLED(withGC ? JS_DestroyContext(cx) : JS_DestroyContextNoGC(cx));
        4604           18405 : }
        4605                 :
        4606                 : static JSObject *
        4607           22824 : NewGlobalObject(JSContext *cx, CompartmentKind compartment)
        4608                 : {
        4609           45648 :     RootedVarObject glob(cx);
        4610                 :
        4611                 :     glob = (compartment == NEW_COMPARTMENT)
        4612                 :            ? JS_NewCompartmentAndGlobalObject(cx, &global_class, NULL)
        4613           22824 :            : JS_NewGlobalObject(cx, &global_class);
        4614           22824 :     if (!glob)
        4615               0 :         return NULL;
        4616                 :
        4617                 :     {
        4618           45648 :         JSAutoEnterCompartment ac;
        4619           22824 :         if (!ac.enter(cx, glob))
        4620               0 :             return NULL;
        4621                 :
        4622                 : #ifndef LAZY_STANDARD_CLASSES
        4623                 :         if (!JS_InitStandardClasses(cx, glob))
        4624                 :             return NULL;
        4625                 : #endif
        4626                 :
        4627                 : #ifdef JS_HAS_CTYPES
        4628           22824 :         if (!JS_InitCTypesClass(cx, glob))
        4629               0 :             return NULL;
        4630                 : #endif
        4631           22824 :         if (!JS_InitReflect(cx, glob))
        4632               0 :             return NULL;
        4633           22824 :         if (!JS_DefineDebuggerObject(cx, glob))
        4634               0 :             return NULL;
        4635           22824 :         if (!JS::RegisterPerfMeasurement(cx, glob))
        4636               0 :             return NULL;
        4637           45648 :         if (!JS_DefineFunctionsWithHelp(cx, glob, shell_functions) ||
        4638           22824 :             !JS_DefineProfilingFunctions(cx, glob)) {
        4639               0 :             return NULL;
        4640                 :         }
        4641           22824 :         if (!js::DefineTestingFunctions(cx, glob))
        4642               0 :             return NULL;
        4643                 :
        4644           22824 :         JSObject *it = JS_DefineObject(cx, glob, "it", &its_class, NULL, 0);
        4645           22824 :         if (!it)
        4646               0 :             return NULL;
        4647           22824 :         if (!JS_DefineProperties(cx, it, its_props))
        4648               0 :             return NULL;
        4649                 :
        4650           22824 :         if (!JS_DefineProperty(cx, glob, "custom", JSVAL_VOID, its_getter,
        4651           22824 :                                its_setter, 0))
        4652               0 :             return NULL;
        4653           22824 :         if (!JS_DefineProperty(cx, glob, "customRdOnly", JSVAL_VOID, its_getter,
        4654           22824 :                                its_setter, JSPROP_READONLY))
        4655               0 :             return NULL;
        4656                 :     }
        4657                 :
        4658           22824 :     if (compartment == NEW_COMPARTMENT && !JS_WrapObject(cx, glob.address()))
        4659               0 :         return NULL;
        4660                 :
        4661           22824 :     return glob;
        4662                 : }
        4663                 :
        4664                 : static bool
        4665           18405 : BindScriptArgs(JSContext *cx, JSObject *obj, OptionParser *op)
        4666                 : {
        4667           36810 :     RootObject root(cx, &obj);
        4668                 :
        4669           18405 :     MultiStringRange msr = op->getMultiStringArg("scriptArgs");
        4670           36810 :     RootedVarObject scriptArgs(cx);
        4671           18405 :     scriptArgs = JS_NewArrayObject(cx, 0, NULL);
        4672           18405 :     if (!scriptArgs)
        4673               0 :         return false;
        4674                 :
        4675                 :     /*
        4676                 :      * Script arguments are bound as a normal |arguments| property on the
        4677                 :      * global object. It has no special significance, like |arguments| in
        4678                 :      * function scope does -- this identifier is used de-facto across shell
        4679                 :      * implementations, see bug 675269.
        4680                 :      */
        4681           18405 :     if (!JS_DefineProperty(cx, obj, "arguments", OBJECT_TO_JSVAL(scriptArgs), NULL, NULL, 0))
        4682               0 :         return false;
        4683                 :
        4684           18405 :     for (size_t i = 0; !msr.empty(); msr.popFront(), ++i) {
        4685               0 :         const char *scriptArg = msr.front();
        4686               0 :         JSString *str = JS_NewStringCopyZ(cx, scriptArg);
        4687               0 :         if (!str ||
        4688                 :             !JS_DefineElement(cx, scriptArgs, i, STRING_TO_JSVAL(str), NULL, NULL,
        4689               0 :                               JSPROP_ENUMERATE)) {
        4690               0 :             return false;
        4691                 :         }
        4692                 :     }
        4693                 :
        4694           18405 :     return true;
        4695                 : }
        4696                 :
        4697                 : static int
        4698           18405 : ProcessArgs(JSContext *cx, JSObject *obj, OptionParser *op)
        4699                 : {
        4700           36810 :     RootObject root(cx, &obj);
        4701                 :
        4702           18405 :     if (op->getBoolOption('a'))
        4703            8260 :         JS_ToggleOptions(cx, JSOPTION_METHODJIT_ALWAYS);
        4704                 :
        4705           18405 :     if (op->getBoolOption('c'))
        4706               0 :         compileOnly = true;
        4707                 :
        4708           18405 :     if (op->getBoolOption('m')) {
        4709           14317 :         enableMethodJit = true;
        4710           14317 :         JS_ToggleOptions(cx, JSOPTION_METHODJIT);
        4711                 :     }
        4712                 :
        4713                 : #ifdef JS_GC_ZEAL
        4714           18405 :     if (const char *zeal = op->getStringOption('Z'))
        4715               0 :         ParseZealArg(cx, zeal);
        4716                 : #endif
        4717                 :
        4718           18405 :     if (op->getBoolOption('d')) {
        4719            6567 :         JS_SetRuntimeDebugMode(JS_GetRuntime(cx), true);
        4720            6567 :         JS_SetDebugMode(cx, true);
        4721                 :     }
        4722                 :
        4723           18405 :     if (op->getBoolOption('b'))
        4724               0 :         printTiming = true;
        4725                 :
        4726           18405 :     if (op->getBoolOption('D'))
        4727               0 :         enableDisassemblyDumps = true;
        4728                 :
        4729                 :     /* |scriptArgs| gets bound on the global before any code is run. */
        4730           18405 :     if (!BindScriptArgs(cx, obj, op))
        4731               0 :         return EXIT_FAILURE;
        4732                 :
        4733           18405 :     MultiStringRange filePaths = op->getMultiStringOption('f');
        4734           18405 :     MultiStringRange codeChunks = op->getMultiStringOption('e');
        4735                 :
        4736           18405 :     if (filePaths.empty() && codeChunks.empty() && !op->getStringArg("script")) {
        4737               0 :         Process(cx, obj, NULL, true); /* Interactive. */
        4738               0 :         return gExitCode;
        4739                 :     }
        4740                 :
        4741           91188 :     while (!filePaths.empty() || !codeChunks.empty()) {
        4742           55215 :         size_t fpArgno = filePaths.empty() ? -1 : filePaths.argno();
        4743           55215 :         size_t ccArgno = codeChunks.empty() ? -1 : codeChunks.argno();
        4744           55215 :         if (fpArgno < ccArgno) {
        4745           36810 :             char *path = filePaths.front();
        4746           36810 :             Process(cx, obj, path, false);
        4747           36810 :             if (gExitCode)
        4748             837 :                 return gExitCode;
        4749           35973 :             filePaths.popFront();
        4750                 :         } else {
        4751           18405 :             const char *code = codeChunks.front();
        4752                 :             jsval rval;
        4753           18405 :             if (!JS_EvaluateScript(cx, obj, code, strlen(code), "-e", 1, &rval))
        4754               0 :                 return EXIT_FAILURE;
        4755           18405 :             codeChunks.popFront();
        4756                 :         }
        4757                 :     }
        4758                 :
        4759                 :     /* The |script| argument is processed after all options. */
        4760           17568 :     if (const char *path = op->getStringArg("script")) {
        4761               0 :         Process(cx, obj, path, false);
        4762               0 :         if (gExitCode)
        4763               0 :             return gExitCode;
        4764                 :     }
        4765                 :
        4766           17568 :     if (op->getBoolOption('i'))
        4767               0 :         Process(cx, obj, NULL, true);
        4768                 :
        4769           17568 :     return gExitCode ? gExitCode : EXIT_SUCCESS;
        4770                 : }
        4771                 :
        4772                 : int
        4773           18405 : Shell(JSContext *cx, OptionParser *op, char **envp)
        4774                 : {
        4775           36810 :     JSAutoRequest ar(cx);
        4776                 :
        4777                 :     /*
        4778                 :      * First check to see if type inference is enabled. This flag must be set
        4779                 :      * on the compartment when it is constructed.
        4780                 :      */
        4781           18405 :     if (op->getBoolOption('n')) {
        4782           10225 :         enableTypeInference = !enableTypeInference;
        4783           10225 :         JS_ToggleOptions(cx, JSOPTION_TYPE_INFERENCE);
        4784                 :     }
        4785                 :
        4786           36810 :     RootedVarObject glob(cx);
        4787           18405 :     glob = NewGlobalObject(cx, NEW_COMPARTMENT);
        4788           18405 :     if (!glob)
        4789               0 :         return 1;
        4790                 :
        4791           36810 :     JSAutoEnterCompartment ac;
        4792           18405 :     if (!ac.enter(cx, glob))
        4793               0 :         return 1;
        4794                 :
        4795           18405 :     JS_SetGlobalObject(cx, glob);
        4796                 :
        4797           18405 :     JSObject *envobj = JS_DefineObject(cx, glob, "environment", &env_class, NULL, 0);
        4798           18405 :     if (!envobj)
        4799               0 :         return 1;
        4800           18405 :     JS_SetPrivate(envobj, envp);
        4801                 :
        4802                 : #ifdef JSDEBUGGER
        4803                 :     /*
        4804                 :     * XXX A command line option to enable debugging (or not) would be good
        4805                 :     */
        4806                 :     jsdc = JSD_DebuggerOnForUser(rt, NULL, NULL);
        4807                 :     if (!jsdc)
        4808                 :         return 1;
        4809                 :     JSD_JSContextInUse(jsdc, cx);
        4810                 : #ifdef JSD_LOWLEVEL_SOURCE
        4811                 :     JS_SetSourceHandler(rt, SendSourceToJSDebugger, jsdc);
        4812                 : #endif /* JSD_LOWLEVEL_SOURCE */
        4813                 : #ifdef JSDEBUGGER_JAVA_UI
        4814                 :     jsdjc = JSDJ_CreateContext();
        4815                 :     if (! jsdjc)
        4816                 :         return 1;
        4817                 :     JSDJ_SetJSDContext(jsdjc, jsdc);
        4818                 :     java_env = JSDJ_CreateJavaVMAndStartDebugger(jsdjc);
        4819                 :     /*
        4820                 :     * XXX This would be the place to wait for the debugger to start.
        4821                 :     * Waiting would be nice in general, but especially when a js file
        4822                 :     * is passed on the cmd line.
        4823                 :     */
        4824                 : #endif /* JSDEBUGGER_JAVA_UI */
        4825                 : #ifdef JSDEBUGGER_C_UI
        4826                 :     jsdbc = JSDB_InitDebugger(rt, jsdc, 0);
        4827                 : #endif /* JSDEBUGGER_C_UI */
        4828                 : #endif /* JSDEBUGGER */
        4829                 :
        4830                 : #ifdef JS_THREADSAFE
        4831           36810 :     class ShellWorkerHooks : public js::workers::WorkerHooks {
        4832                 :     public:
        4833               0 :         JSObject *newGlobalObject(JSContext *cx) {
        4834               0 :             return NewGlobalObject(cx, NEW_COMPARTMENT);
        4835                 :         }
        4836                 :     };
        4837           36810 :     ShellWorkerHooks hooks;
        4838           36810 :     if (!JS_AddNamedObjectRoot(cx, &gWorkers, "Workers") ||
        4839           18405 :         (gWorkerThreadPool = js::workers::init(cx, &hooks, glob, &gWorkers)) == NULL) {
        4840               0 :         return 1;
        4841                 :     }
        4842                 : #endif
        4843                 :
        4844           18405 :     int result = ProcessArgs(cx, glob, op);
        4845                 :
        4846                 : #ifdef JS_THREADSAFE
        4847           18405 :     js::workers::finish(cx, gWorkerThreadPool);
        4848           18405 :     JS_RemoveObjectRoot(cx, &gWorkers);
        4849           18405 :     if (result == 0)
        4850           17568 :         result = gExitCode;
        4851                 : #endif
        4852                 :
        4853                 : #ifdef JSDEBUGGER
        4854                 :     if (jsdc) {
        4855                 : #ifdef JSDEBUGGER_C_UI
        4856                 :         if (jsdbc)
        4857                 :             JSDB_TermDebugger(jsdc);
        4858                 : #endif /* JSDEBUGGER_C_UI */
        4859                 :         JSD_DebuggerOff(jsdc);
        4860                 :     }
        4861                 : #endif  /* JSDEBUGGER */
        4862                 :
        4863           18405 :     if (enableDisassemblyDumps)
        4864               0 :         JS_DumpCompartmentPCCounts(cx);
        4865                 :
        4866           18405 :     return result;
        4867                 : }
        4868                 :
        4869                 : static void
        4870           36810 : MaybeOverrideOutFileFromEnv(const char* const envVar,
        4871                 :                             FILE* defaultOut,
        4872                 :                             FILE** outFile)
        4873                 : {
        4874           36810 :     const char* outPath = getenv(envVar);
        4875           36810 :     if (!outPath || !*outPath || !(*outFile = fopen(outPath, "w"))) {
        4876           36810 :         *outFile = defaultOut;
        4877                 :     }
        4878           36810 : }
        4879                 :
        4880                 : /* Set the initial counter to 1 so the principal will never be destroyed. */
        4881                 : JSPrincipals shellTrustedPrincipals = { 1 };
        4882                 :
        4883                 : JSBool
        4884         4554184 : CheckObjectAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, jsval *vp)
        4885                 : {
        4886         4554184 :     return true;
        4887                 : }
        4888                 :
        4889                 : JSSecurityCallbacks securityCallbacks = {
        4890                 :     CheckObjectAccess,
        4891                 :     NULL,
        4892                 :     NULL,
        4893                 :     NULL,
        4894                 :     NULL
        4895                 : };
        4896                 :
        4897                 : int
        4898           18405 : main(int argc, char **argv, char **envp)
        4899                 : {
        4900                 :     int stackDummy;
        4901                 :     JSRuntime *rt;
        4902                 :     JSContext *cx;
        4903                 :     int result;
        4904                 : #ifdef JSDEBUGGER
        4905                 :     JSDContext *jsdc;
        4906                 : #ifdef JSDEBUGGER_JAVA_UI
        4907                 :     JNIEnv *java_env;
        4908                 :     JSDJContext *jsdjc;
        4909                 : #endif
        4910                 : #ifdef JSDEBUGGER_C_UI
        4911                 :     JSBool jsdbc;
        4912                 : #endif /* JSDEBUGGER_C_UI */
        4913                 : #endif /* JSDEBUGGER */
        4914                 : #ifdef XP_WIN
        4915                 :     {
        4916                 :         const char *crash_option = getenv("XRE_NO_WINDOWS_CRASH_DIALOG");
        4917                 :         if (crash_option && strncmp(crash_option, "1", 1)) {
        4918                 :             DWORD oldmode = SetErrorMode(SEM_NOGPFAULTERRORBOX);
        4919                 :             SetErrorMode(oldmode | SEM_NOGPFAULTERRORBOX);
        4920                 :         }
        4921                 :     }
        4922                 : #endif
        4923                 :
        4924                 : #ifdef HAVE_SETLOCALE
        4925           18405 :     setlocale(LC_ALL, "");
        4926                 : #endif
        4927                 :
        4928                 : #ifdef JS_THREADSAFE
        4929           36810 :     if (PR_FAILURE == PR_NewThreadPrivateIndex(&gStackBaseThreadIndex, NULL) ||
        4930           18405 :         PR_FAILURE == PR_SetThreadPrivate(gStackBaseThreadIndex, &stackDummy)) {
        4931               0 :         return 1;
        4932                 :     }
        4933                 : #else
        4934                 :     gStackBase = (uintptr_t) &stackDummy;
        4935                 : #endif
        4936                 :
        4937                 : #ifdef XP_OS2
        4938                 :    /* these streams are normally line buffered on OS/2 and need a \n, *
        4939                 :     * so we need to unbuffer then to get a reasonable prompt          */
        4940                 :     setbuf(stdout,0);
        4941                 :     setbuf(stderr,0);
        4942                 : #endif
        4943                 :
        4944           18405 :     MaybeOverrideOutFileFromEnv("JS_STDERR", stderr, &gErrFile);
        4945           18405 :     MaybeOverrideOutFileFromEnv("JS_STDOUT", stdout, &gOutFile);
        4946                 :
        4947           36810 :     OptionParser op("Usage: {progname} [options] [[script] scriptArgs*]");
        4948                 :
        4949                 :     op.setDescription("The SpiderMonkey shell provides a command line interface to the "
        4950                 :         "JavaScript engine. Code and file options provided via the command line are "
        4951                 :         "run left to right. If provided, the optional script argument is run after "
        4952                 :         "all options have been processed. Just-In-Time compilation modes may be enabled via "
        4953           18405 :         "command line options.");
        4954           18405 :     op.setDescriptionWidth(72);
        4955           18405 :     op.setHelpWidth(80);
        4956           18405 :     op.setVersion(JS_GetImplementationVersion());
        4957                 :
        4958          294480 :     if (!op.addMultiStringOption('f', "file", "PATH", "File path to run")
        4959           18405 :         || !op.addMultiStringOption('e', "execute", "CODE", "Inline code to run")
        4960           18405 :         || !op.addBoolOption('i', "shell", "Enter prompt after running code")
        4961           18405 :         || !op.addBoolOption('m', "methodjit", "Enable the JaegerMonkey method JIT")
        4962           18405 :         || !op.addBoolOption('n', "typeinfer", "Enable type inference")
        4963           18405 :         || !op.addBoolOption('c', "compileonly", "Only compile, don't run (syntax checking mode)")
        4964           18405 :         || !op.addBoolOption('d', "debugjit", "Enable runtime debug mode for method JIT code")
        4965                 :         || !op.addBoolOption('a', "always-mjit",
        4966           18405 :                              "Do not try to run in the interpreter before method jitting.")
        4967           18405 :         || !op.addBoolOption('D', "dump-bytecode", "Dump bytecode with exec count for all scripts")
        4968           18405 :         || !op.addBoolOption('b', "print-timing", "Print sub-ms runtime for each file that's run")
        4969                 : #ifdef DEBUG
        4970           18405 :         || !op.addIntOption('A', "oom-after", "COUNT", "Trigger OOM after COUNT allocations", -1)
        4971           18405 :         || !op.addBoolOption('O', "print-alloc", "Print the number of allocations at exit")
        4972                 : #endif
        4973           18405 :         || !op.addBoolOption('U', "utf8", "C strings passed to the JSAPI are UTF-8 encoded")
        4974                 : #ifdef JS_GC_ZEAL
        4975                 :         || !op.addStringOption('Z', "gc-zeal", "N[,F[,C]]",
        4976                 :                                "N indicates \"zealousness\":\n"
        4977                 :                                "  0: no additional GCs\n"
        4978                 :                                "  1: additional GCs at common danger points\n"
        4979                 :                                "  2: GC every F allocations (default: 100)\n"
        4980           18405 :                                "If C is 1, compartmental GCs are performed; otherwise, full")
        4981                 : #endif
        4982           18405 :         || !op.addOptionalStringArg("script", "A script to execute (after all options)")
        4983                 :         || !op.addOptionalMultiStringArg("scriptArgs",
        4984                 :                                          "String arguments to bind as |arguments| in the "
        4985           18405 :                                          "shell's global")) {
        4986               0 :         return EXIT_FAILURE;
        4987                 :     }
        4988                 :
        4989           18405 :     op.setArgTerminatesOptions("script", true);
        4990                 :
        4991           18405 :     switch (op.parseArgs(argc, argv)) {
        4992                 :       case OptionParser::ParseHelp:
        4993               0 :         return EXIT_SUCCESS;
        4994                 :       case OptionParser::ParseError:
        4995               0 :         op.printHelp(argv[0]);
        4996               0 :         return EXIT_FAILURE;
        4997                 :       case OptionParser::Fail:
        4998               0 :         return EXIT_FAILURE;
        4999                 :       case OptionParser::Okay:
        5000                 :         break;
        5001                 :     }
        5002                 :
        5003           18405 :     if (op.getHelpOption())
        5004               0 :         return EXIT_SUCCESS;
        5005                 :
        5006                 : #ifdef DEBUG
        5007                 :     /*
        5008                 :      * Process OOM options as early as possible so that we can observe as many
        5009                 :      * allocations as possible.
        5010                 :      */
        5011           18405 :     if (op.getIntOption('A') >= 0)
        5012               0 :         OOM_maxAllocations = op.getIntOption('A');
        5013           18405 :     if (op.getBoolOption('O'))
        5014               0 :         OOM_printAllocationCount = true;
        5015                 : #endif
        5016                 :
        5017                 :     /* Must be done before we create the JSRuntime. */
        5018           18405 :     if (op.getBoolOption('U'))
        5019               0 :         JS_SetCStringsAreUTF8();
        5020                 :
        5021                 : #ifdef XP_WIN
        5022                 :     // Set the timer calibration delay count to 0 so we get high
        5023                 :     // resolution right away, which we need for precise benchmarking.
        5024                 :     extern int CALIBRATION_DELAY_COUNT;
        5025                 :     CALIBRATION_DELAY_COUNT = 0;
        5026                 : #endif
        5027                 :
        5028                 :     /* Use the same parameters as the browser in xpcjsruntime.cpp. */
        5029           18405 :     rt = JS_NewRuntime(32L * 1024L * 1024L);
        5030           18405 :     if (!rt)
        5031               0 :         return 1;
        5032                 :
        5033           18405 :     JS_SetGCParameter(rt, JSGC_MAX_BYTES, 0xffffffff);
        5034                 :
        5035           18405 :     JS_SetTrustedPrincipals(rt, &shellTrustedPrincipals);
        5036           18405 :     JS_SetSecurityCallbacks(rt, &securityCallbacks);
        5037                 :
        5038           18405 :     JS_SetNativeStackQuota(rt, gMaxStackSize);
        5039                 :
        5040           18405 :     if (!InitWatchdog(rt))
        5041               0 :         return 1;
        5042                 :
        5043           18405 :     cx = NewContext(rt);
        5044           18405 :     if (!cx)
        5045               0 :         return 1;
        5046                 :
        5047           18405 :     JS_SetGCParameter(rt, JSGC_MODE, JSGC_MODE_INCREMENTAL);
        5048           18405 :     JS_SetGCParameterForThread(cx, JSGC_MAX_CODE_CACHE_BYTES, 16 * 1024 * 1024);
        5049                 :
        5050                 :     /* Must be done before creating the global object */
        5051           18405 :     if (op.getBoolOption('D'))
        5052               0 :         JS_ToggleOptions(cx, JSOPTION_PCCOUNT);
        5053                 :
        5054           18405 :     result = Shell(cx, &op, envp);
        5055                 :
        5056                 : #ifdef DEBUG
        5057           18405 :     if (OOM_printAllocationCount)
        5058               0 :         printf("OOM max count: %u\n", OOM_counter);
        5059                 : #endif
        5060                 :
        5061           18405 :     DestroyContext(cx, true);
        5062                 :
        5063           18405 :     KillWatchdog();
        5064                 :
        5065           18405 :     JS_DestroyRuntime(rt);
        5066           18405 :     JS_ShutDown();
        5067           18405 :     return result;
        5068                 : }

Generated by: LCOV version 1.7