LCOV - code coverage report
Current view: directory - js/src/shell - js.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 2171 810 37.3 %
Date: 2012-04-07 Functions: 141 71 50.4 %

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

Generated by: LCOV version 1.7