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-06-02 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         4552243 : CheckObjectAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, jsval *vp)
    4885                 : {
    4886         4552243 :     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