js/src/shell/js.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sts=4 et sw=4 tw=99:
     2  * This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 /* JS shell. */
     8 #include "mozilla/ArrayUtils.h"
     9 #include "mozilla/Atomics.h"
    10 #include "mozilla/DebugOnly.h"
    11 #include "mozilla/GuardObjects.h"
    12 #include "mozilla/PodOperations.h"
    14 #ifdef XP_WIN
    15 # include <direct.h>
    16 # include <process.h>
    17 #endif
    18 #include <errno.h>
    19 #include <fcntl.h>
    20 #if defined(XP_WIN)
    21 # include <io.h>     /* for isatty() */
    22 #endif
    23 #include <locale.h>
    24 #include <math.h>
    25 #include <signal.h>
    26 #include <stdio.h>
    27 #include <stdlib.h>
    28 #include <string.h>
    29 #include <sys/stat.h>
    30 #include <sys/types.h>
    31 #ifdef XP_UNIX
    32 # include <sys/mman.h>
    33 # include <sys/stat.h>
    34 # include <sys/wait.h>
    35 # include <unistd.h>
    36 #endif
    38 #include "jsapi.h"
    39 #include "jsarray.h"
    40 #include "jsatom.h"
    41 #include "jscntxt.h"
    42 #include "jsfun.h"
    43 #ifdef JS_THREADSAFE
    44 #include "jslock.h"
    45 #endif
    46 #include "jsobj.h"
    47 #include "jsprf.h"
    48 #include "jsscript.h"
    49 #include "jstypes.h"
    50 #include "jsutil.h"
    51 #ifdef XP_WIN
    52 # include "jswin.h"
    53 #endif
    54 #include "jsworkers.h"
    55 #include "jswrapper.h"
    56 #include "prmjtime.h"
    58 #include "builtin/TestingFunctions.h"
    59 #include "frontend/Parser.h"
    60 #include "jit/arm/Simulator-arm.h"
    61 #include "jit/Ion.h"
    62 #include "js/OldDebugAPI.h"
    63 #include "js/StructuredClone.h"
    64 #include "perf/jsperf.h"
    65 #include "shell/jsheaptools.h"
    66 #include "shell/jsoptparse.h"
    67 #include "vm/ArgumentsObject.h"
    68 #include "vm/Monitor.h"
    69 #include "vm/Shape.h"
    70 #include "vm/TypedArrayObject.h"
    71 #include "vm/WrapperObject.h"
    73 #include "jscompartmentinlines.h"
    74 #include "jsobjinlines.h"
    76 #ifdef XP_WIN
    77 # define PATH_MAX (MAX_PATH > _MAX_DIR ? MAX_PATH : _MAX_DIR)
    78 #else
    79 # include <libgen.h>
    80 #endif
    82 using namespace js;
    83 using namespace js::cli;
    85 using mozilla::ArrayLength;
    86 using mozilla::NumberEqualsInt32;
    87 using mozilla::Maybe;
    88 using mozilla::PodCopy;
    90 enum JSShellExitCode {
    91     EXITCODE_RUNTIME_ERROR      = 3,
    92     EXITCODE_FILE_NOT_FOUND     = 4,
    93     EXITCODE_OUT_OF_MEMORY      = 5,
    94     EXITCODE_TIMEOUT            = 6
    95 };
    97 enum PathResolutionMode {
    98     RootRelative,
    99     ScriptRelative
   100 };
   102 static size_t gStackChunkSize = 8192;
   104 /*
   105  * Note: This limit should match the stack limit set by the browser in
   106  *       js/xpconnect/src/XPCJSRuntime.cpp
   107  */
   108 #if defined(MOZ_ASAN) || (defined(DEBUG) && !defined(XP_WIN))
   109 static size_t gMaxStackSize = 2 * 128 * sizeof(size_t) * 1024;
   110 #else
   111 static size_t gMaxStackSize = 128 * sizeof(size_t) * 1024;
   112 #endif
   114 /*
   115  * Limit the timeout to 30 minutes to prevent an overflow on platfoms
   116  * that represent the time internally in microseconds using 32-bit int.
   117  */
   118 static double MAX_TIMEOUT_INTERVAL = 1800.0;
   119 static double gTimeoutInterval = -1.0;
   120 static volatile bool gServiceInterrupt = false;
   121 static Maybe<JS::PersistentRootedValue> gInterruptFunc;
   123 static bool enableDisassemblyDumps = false;
   125 static bool printTiming = false;
   126 static const char *jsCacheDir = nullptr;
   127 static const char *jsCacheAsmJSPath = nullptr;
   128 static bool jsCachingEnabled = false;
   129 mozilla::Atomic<bool> jsCacheOpened(false);
   131 static bool
   132 SetTimeoutValue(JSContext *cx, double t);
   134 static bool
   135 InitWatchdog(JSRuntime *rt);
   137 static void
   138 KillWatchdog();
   140 static bool
   141 ScheduleWatchdog(JSRuntime *rt, double t);
   143 static void
   144 CancelExecution(JSRuntime *rt);
   146 /*
   147  * Watchdog thread state.
   148  */
   149 #ifdef JS_THREADSAFE
   151 static PRLock *gWatchdogLock = nullptr;
   152 static PRCondVar *gWatchdogWakeup = nullptr;
   153 static PRThread *gWatchdogThread = nullptr;
   154 static bool gWatchdogHasTimeout = false;
   155 static int64_t gWatchdogTimeout = 0;
   157 static PRCondVar *gSleepWakeup = nullptr;
   159 #else
   161 static JSRuntime *gRuntime = nullptr;
   163 #endif
   165 static int gExitCode = 0;
   166 static bool gQuitting = false;
   167 static bool gGotError = false;
   168 static FILE *gErrFile = nullptr;
   169 static FILE *gOutFile = nullptr;
   171 static bool reportWarnings = true;
   172 static bool compileOnly = false;
   173 static bool fuzzingSafe = false;
   175 #ifdef DEBUG
   176 static bool dumpEntrainedVariables = false;
   177 static bool OOM_printAllocationCount = false;
   178 #endif
   180 enum JSShellErrNum {
   181 #define MSG_DEF(name, number, count, exception, format) \
   182     name = number,
   183 #include "jsshell.msg"
   184 #undef MSG_DEF
   185     JSShellErr_Limit
   186 };
   188 static JSContext *
   189 NewContext(JSRuntime *rt);
   191 static void
   192 DestroyContext(JSContext *cx, bool withGC);
   194 static JSObject *
   195 NewGlobalObject(JSContext *cx, JS::CompartmentOptions &options,
   196                 JSPrincipals *principals);
   198 static const JSErrorFormatString *
   199 my_GetErrorMessage(void *userRef, const char *locale, const unsigned errorNumber);
   202 /*
   203  * A toy principals type for the shell.
   204  *
   205  * In the shell, a principal is simply a 32-bit mask: P subsumes Q if the
   206  * set bits in P are a superset of those in Q. Thus, the principal 0 is
   207  * subsumed by everything, and the principal ~0 subsumes everything.
   208  *
   209  * As a special case, a null pointer as a principal is treated like 0xffff.
   210  *
   211  * The 'newGlobal' function takes an option indicating which principal the
   212  * new global should have; 'evaluate' does for the new code.
   213  */
   214 class ShellPrincipals: public JSPrincipals {
   215     uint32_t bits;
   217     static uint32_t getBits(JSPrincipals *p) {
   218         if (!p)
   219             return 0xffff;
   220         return static_cast<ShellPrincipals *>(p)->bits;
   221     }
   223   public:
   224     ShellPrincipals(uint32_t bits, int32_t refcount = 0) : bits(bits) {
   225         this->refcount = refcount;
   226     }
   228     static void destroy(JSPrincipals *principals) {
   229         MOZ_ASSERT(principals != &fullyTrusted);
   230         MOZ_ASSERT(principals->refcount == 0);
   231         js_free(static_cast<ShellPrincipals *>(principals));
   232     }
   234     static bool subsumes(JSPrincipals *first, JSPrincipals *second) {
   235         uint32_t firstBits  = getBits(first);
   236         uint32_t secondBits = getBits(second);
   237         return (firstBits | secondBits) == firstBits;
   238     }
   240     static JSSecurityCallbacks securityCallbacks;
   242     // Fully-trusted principals singleton.
   243     static ShellPrincipals fullyTrusted;
   244 };
   246 JSSecurityCallbacks ShellPrincipals::securityCallbacks = {
   247     nullptr, // contentSecurityPolicyAllows
   248     subsumes
   249 };
   251 // The fully-trusted principal subsumes all other principals.
   252 ShellPrincipals ShellPrincipals::fullyTrusted(-1, 1);
   254 #ifdef EDITLINE
   255 extern "C" {
   256 extern JS_EXPORT_API(char *) readline(const char *prompt);
   257 extern JS_EXPORT_API(void)   add_history(char *line);
   258 } // extern "C"
   259 #endif
   261 static char *
   262 GetLine(FILE *file, const char * prompt)
   263 {
   264     size_t size;
   265     char *buffer;
   266 #ifdef EDITLINE
   267     /*
   268      * Use readline only if file is stdin, because there's no way to specify
   269      * another handle.  Are other filehandles interactive?
   270      */
   271     if (file == stdin) {
   272         char *linep = readline(prompt);
   273         /*
   274          * We set it to zero to avoid complaining about inappropriate ioctl
   275          * for device in the case of EOF. Looks like errno == 251 if line is
   276          * finished with EOF and errno == 25 (EINVAL on Mac) if there is
   277          * nothing left to read.
   278          */
   279         if (errno == 251 || errno == 25 || errno == EINVAL)
   280             errno = 0;
   281         if (!linep)
   282             return nullptr;
   283         if (linep[0] != '\0')
   284             add_history(linep);
   285         return linep;
   286     }
   287 #endif
   288     size_t len = 0;
   289     if (*prompt != '\0') {
   290         fprintf(gOutFile, "%s", prompt);
   291         fflush(gOutFile);
   292     }
   293     size = 80;
   294     buffer = (char *) malloc(size);
   295     if (!buffer)
   296         return nullptr;
   297     char *current = buffer;
   298     while (fgets(current, size - len, file)) {
   299         len += strlen(current);
   300         char *t = buffer + len - 1;
   301         if (*t == '\n') {
   302             /* Line was read. We remove '\n' and exit. */
   303             *t = '\0';
   304             return buffer;
   305         }
   306         if (len + 1 == size) {
   307             size = size * 2;
   308             char *tmp = (char *) js_realloc(buffer, size);
   309             if (!tmp) {
   310                 free(buffer);
   311                 return nullptr;
   312             }
   313             buffer = tmp;
   314         }
   315         current = buffer + len;
   316     }
   317     if (len && !ferror(file))
   318         return buffer;
   319     free(buffer);
   320     return nullptr;
   321 }
   323 static char *
   324 JSStringToUTF8(JSContext *cx, JSString *str)
   325 {
   326     JSLinearString *linear = str->ensureLinear(cx);
   327     if (!linear)
   328         return nullptr;
   330     return TwoByteCharsToNewUTF8CharsZ(cx, linear->range()).c_str();
   331 }
   333 /* State to store as JSContext private. */
   334 struct JSShellContextData {
   335     /* Creation timestamp, used by the elapsed() shell builtin. */
   336     int64_t startTime;
   337 };
   339 static JSShellContextData *
   340 NewContextData()
   341 {
   342     /* Prevent creation of new contexts after we have been canceled. */
   343     if (gServiceInterrupt)
   344         return nullptr;
   346     JSShellContextData *data = (JSShellContextData *)
   347                                js_calloc(sizeof(JSShellContextData), 1);
   348     if (!data)
   349         return nullptr;
   350     data->startTime = PRMJ_Now();
   351     return data;
   352 }
   354 static inline JSShellContextData *
   355 GetContextData(JSContext *cx)
   356 {
   357     JSShellContextData *data = (JSShellContextData *) JS_GetContextPrivate(cx);
   359     JS_ASSERT(data);
   360     return data;
   361 }
   363 static bool
   364 ShellInterruptCallback(JSContext *cx)
   365 {
   366     if (!gServiceInterrupt)
   367         return true;
   369     bool result;
   370     RootedValue interruptFunc(cx, gInterruptFunc.ref());
   371     if (!interruptFunc.isNull()) {
   372         JS::AutoSaveExceptionState savedExc(cx);
   373         JSAutoCompartment ac(cx, &interruptFunc.toObject());
   374         RootedValue rval(cx);
   375         if (!JS_CallFunctionValue(cx, JS::NullPtr(), interruptFunc,
   376                                   JS::HandleValueArray::empty(), &rval))
   377         {
   378             return false;
   379         }
   380         if (rval.isBoolean())
   381             result = rval.toBoolean();
   382         else
   383             result = false;
   384     } else {
   385         result = false;
   386     }
   388     if (!result && gExitCode == 0)
   389         gExitCode = EXITCODE_TIMEOUT;
   391     return result;
   392 }
   394 /*
   395  * Some UTF-8 files, notably those written using Notepad, have a Unicode
   396  * Byte-Order-Mark (BOM) as their first character. This is useless (byte-order
   397  * is meaningless for UTF-8) but causes a syntax error unless we skip it.
   398  */
   399 static void
   400 SkipUTF8BOM(FILE* file)
   401 {
   402     int ch1 = fgetc(file);
   403     int ch2 = fgetc(file);
   404     int ch3 = fgetc(file);
   406     // Skip the BOM
   407     if (ch1 == 0xEF && ch2 == 0xBB && ch3 == 0xBF)
   408         return;
   410     // No BOM - revert
   411     if (ch3 != EOF)
   412         ungetc(ch3, file);
   413     if (ch2 != EOF)
   414         ungetc(ch2, file);
   415     if (ch1 != EOF)
   416         ungetc(ch1, file);
   417 }
   419 static void
   420 RunFile(JSContext *cx, Handle<JSObject*> obj, const char *filename, FILE *file, bool compileOnly)
   421 {
   422     SkipUTF8BOM(file);
   424     // To support the UNIX #! shell hack, gobble the first line if it starts
   425     // with '#'.
   426     int ch = fgetc(file);
   427     if (ch == '#') {
   428         while ((ch = fgetc(file)) != EOF) {
   429             if (ch == '\n' || ch == '\r')
   430                 break;
   431         }
   432     }
   433     ungetc(ch, file);
   435     int64_t t1 = PRMJ_Now();
   436     RootedScript script(cx);
   438     {
   439         JS::AutoSaveContextOptions asco(cx);
   440         JS::ContextOptionsRef(cx).setNoScriptRval(true);
   442         CompileOptions options(cx);
   443         options.setIntroductionType("js shell file")
   444                .setUTF8(true)
   445                .setFileAndLine(filename, 1)
   446                .setCompileAndGo(true);
   448         gGotError = false;
   449         script = JS::Compile(cx, obj, options, file);
   450         JS_ASSERT_IF(!script, gGotError);
   451     }
   453     #ifdef DEBUG
   454         if (dumpEntrainedVariables)
   455             AnalyzeEntrainedVariables(cx, script);
   456     #endif
   457     if (script && !compileOnly) {
   458         if (!JS_ExecuteScript(cx, obj, script)) {
   459             if (!gQuitting && !gServiceInterrupt)
   460                 gExitCode = EXITCODE_RUNTIME_ERROR;
   461         }
   462         int64_t t2 = PRMJ_Now() - t1;
   463         if (printTiming)
   464             printf("runtime = %.3f ms\n", double(t2) / PRMJ_USEC_PER_MSEC);
   465     }
   466 }
   468 static bool
   469 EvalAndPrint(JSContext *cx, Handle<JSObject*> global, const char *bytes, size_t length,
   470              int lineno, bool compileOnly, FILE *out)
   471 {
   472     // Eval.
   473     JS::CompileOptions options(cx);
   474     options.setIntroductionType("js shell interactive")
   475            .setUTF8(true)
   476            .setCompileAndGo(true)
   477            .setFileAndLine("typein", lineno);
   478     RootedScript script(cx);
   479     script = JS::Compile(cx, global, options, bytes, length);
   480     if (!script)
   481         return false;
   482     if (compileOnly)
   483         return true;
   484     RootedValue result(cx);
   485     if (!JS_ExecuteScript(cx, global, script, &result))
   486         return false;
   488     if (!result.isUndefined()) {
   489         // Print.
   490         RootedString str(cx);
   491         str = JS_ValueToSource(cx, result);
   492         if (!str)
   493             return false;
   495         char *utf8chars = JSStringToUTF8(cx, str);
   496         if (!utf8chars)
   497             return false;
   498         fprintf(out, "%s\n", utf8chars);
   499         JS_free(cx, utf8chars);
   500     }
   501     return true;
   502 }
   504 static void
   505 ReadEvalPrintLoop(JSContext *cx, Handle<JSObject*> global, FILE *in, FILE *out, bool compileOnly)
   506 {
   507     int lineno = 1;
   508     bool hitEOF = false;
   510     do {
   511         /*
   512          * Accumulate lines until we get a 'compilable unit' - one that either
   513          * generates an error (before running out of source) or that compiles
   514          * cleanly.  This should be whenever we get a complete statement that
   515          * coincides with the end of a line.
   516          */
   517         int startline = lineno;
   518         typedef Vector<char, 32, ContextAllocPolicy> CharBuffer;
   519         CharBuffer buffer(cx);
   520         do {
   521             ScheduleWatchdog(cx->runtime(), -1);
   522             gServiceInterrupt = false;
   523             errno = 0;
   525             char *line = GetLine(in, startline == lineno ? "js> " : "");
   526             if (!line) {
   527                 if (errno) {
   528                     JS_ReportError(cx, strerror(errno));
   529                     return;
   530                 }
   531                 hitEOF = true;
   532                 break;
   533             }
   535             if (!buffer.append(line, strlen(line)) || !buffer.append('\n'))
   536                 return;
   538             lineno++;
   539             if (!ScheduleWatchdog(cx->runtime(), gTimeoutInterval)) {
   540                 hitEOF = true;
   541                 break;
   542             }
   543         } while (!JS_BufferIsCompilableUnit(cx, global, buffer.begin(), buffer.length()));
   545         if (hitEOF && buffer.empty())
   546             break;
   548         if (!EvalAndPrint(cx, global, buffer.begin(), buffer.length(), startline, compileOnly,
   549                           out))
   550         {
   551             // Catch the error, report it, and keep going.
   552             JS_ReportPendingException(cx);
   553         }
   554     } while (!hitEOF && !gQuitting);
   556     fprintf(out, "\n");
   557 }
   559 class AutoCloseInputFile
   560 {
   561   private:
   562     FILE *f_;
   563   public:
   564     explicit AutoCloseInputFile(FILE *f) : f_(f) {}
   565     ~AutoCloseInputFile() {
   566         if (f_ && f_ != stdin)
   567             fclose(f_);
   568     }
   569 };
   571 static void
   572 Process(JSContext *cx, JSObject *obj_, const char *filename, bool forceTTY)
   573 {
   574     RootedObject obj(cx, obj_);
   576     FILE *file;
   577     if (forceTTY || !filename || strcmp(filename, "-") == 0) {
   578         file = stdin;
   579     } else {
   580         file = fopen(filename, "r");
   581         if (!file) {
   582             JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr,
   583                                  JSSMSG_CANT_OPEN, filename, strerror(errno));
   584             gExitCode = EXITCODE_FILE_NOT_FOUND;
   585             return;
   586         }
   587     }
   588     AutoCloseInputFile autoClose(file);
   590     if (!forceTTY && !isatty(fileno(file))) {
   591         // It's not interactive - just execute it.
   592         RunFile(cx, obj, filename, file, compileOnly);
   593     } else {
   594         // It's an interactive filehandle; drop into read-eval-print loop.
   595         ReadEvalPrintLoop(cx, obj, file, gOutFile, compileOnly);
   596     }
   597 }
   599 static bool
   600 Version(JSContext *cx, unsigned argc, jsval *vp)
   601 {
   602     CallArgs args = CallArgsFromVp(argc, vp);
   603     JSVersion origVersion = JS_GetVersion(cx);
   604     if (args.length() == 0 || JSVAL_IS_VOID(args[0])) {
   605         /* Get version. */
   606         args.rval().setInt32(origVersion);
   607     } else {
   608         /* Set version. */
   609         int32_t v = -1;
   610         if (args[0].isInt32()) {
   611             v = args[0].toInt32();
   612         } else if (args[0].isDouble()) {
   613             double fv = args[0].toDouble();
   614             int32_t fvi;
   615             if (NumberEqualsInt32(fv, &fvi))
   616                 v = fvi;
   617         }
   618         if (v < 0 || v > JSVERSION_LATEST) {
   619             JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS, "version");
   620             return false;
   621         }
   622         JS_SetVersionForCompartment(js::GetContextCompartment(cx), JSVersion(v));
   623         args.rval().setInt32(origVersion);
   624     }
   625     return true;
   626 }
   628 /*
   629  * Resolve a (possibly) relative filename to an absolute path. If
   630  * |scriptRelative| is true, then the result will be relative to the directory
   631  * containing the currently-running script, or the current working directory if
   632  * the currently-running script is "-e" (namely, you're using it from the
   633  * command line.) Otherwise, it will be relative to the current working
   634  * directory.
   635  */
   636 static JSString *
   637 ResolvePath(JSContext *cx, HandleString filenameStr, PathResolutionMode resolveMode)
   638 {
   639     JSAutoByteString filename(cx, filenameStr);
   640     if (!filename)
   641         return nullptr;
   643     const char *pathname = filename.ptr();
   644     if (pathname[0] == '/')
   645         return filenameStr;
   646 #ifdef XP_WIN
   647     // Various forms of absolute paths per http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx
   648     // "\..."
   649     if (pathname[0] == '\\')
   650         return filenameStr;
   651     // "C:\..."
   652     if (strlen(pathname) > 3 && isalpha(pathname[0]) && pathname[1] == ':' && pathname[2] == '\\')
   653         return filenameStr;
   654     // "\\..."
   655     if (strlen(pathname) > 2 && pathname[1] == '\\' && pathname[2] == '\\')
   656         return filenameStr;
   657 #endif
   659     /* Get the currently executing script's name. */
   660     JS::AutoFilename scriptFilename;
   661     if (!DescribeScriptedCaller(cx, &scriptFilename))
   662         return nullptr;
   664     if (!scriptFilename.get())
   665         return nullptr;
   667     if (strcmp(scriptFilename.get(), "-e") == 0 || strcmp(scriptFilename.get(), "typein") == 0)
   668         resolveMode = RootRelative;
   670     static char buffer[PATH_MAX+1];
   671     if (resolveMode == ScriptRelative) {
   672 #ifdef XP_WIN
   673         // The docs say it can return EINVAL, but the compiler says it's void
   674         _splitpath(scriptFilename.get(), nullptr, buffer, nullptr, nullptr);
   675 #else
   676         strncpy(buffer, scriptFilename.get(), PATH_MAX+1);
   677         if (buffer[PATH_MAX] != '\0')
   678             return nullptr;
   680         // dirname(buffer) might return buffer, or it might return a
   681         // statically-allocated string
   682         memmove(buffer, dirname(buffer), strlen(buffer) + 1);
   683 #endif
   684     } else {
   685         const char *cwd = getcwd(buffer, PATH_MAX);
   686         if (!cwd)
   687             return nullptr;
   688     }
   690     size_t len = strlen(buffer);
   691     buffer[len] = '/';
   692     strncpy(buffer + len + 1, pathname, sizeof(buffer) - (len+1));
   693     if (buffer[PATH_MAX] != '\0')
   694         return nullptr;
   696     return JS_NewStringCopyZ(cx, buffer);
   697 }
   699 static bool
   700 CreateMappedArrayBuffer(JSContext *cx, unsigned argc, Value *vp)
   701 {
   702     CallArgs args = CallArgsFromVp(argc, vp);
   704     if (args.length() < 1 || args.length() > 3) {
   705         JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr,
   706                              args.length() < 1 ? JSSMSG_NOT_ENOUGH_ARGS : JSSMSG_TOO_MANY_ARGS,
   707                              "createMappedArrayBuffer");
   708         return false;
   709     }
   711     RootedString rawFilenameStr(cx, JS::ToString(cx, args[0]));
   712     if (!rawFilenameStr)
   713         return false;
   714     // It's a little bizarre to resolve relative to the script, but for testing
   715     // I need a file at a known location, and the only good way I know of to do
   716     // that right now is to include it in the repo alongside the test script.
   717     // Bug 944164 would introduce an alternative.
   718     JSString *filenameStr = ResolvePath(cx, rawFilenameStr, ScriptRelative);
   719     if (!filenameStr)
   720         return false;
   721     JSAutoByteString filename(cx, filenameStr);
   722     if (!filename)
   723         return false;
   725     uint32_t offset = 0;
   726     if (args.length() >= 2) {
   727         if (!JS::ToUint32(cx, args[1], &offset))
   728             return false;
   729     }
   731     bool sizeGiven = false;
   732     uint32_t size;
   733     if (args.length() >= 3) {
   734         if (!JS::ToUint32(cx, args[2], &size))
   735             return false;
   736         sizeGiven = true;
   737         if (offset > size) {
   738             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
   739                                  JSMSG_ARG_INDEX_OUT_OF_RANGE, "2");
   740             return false;
   741         }
   742     }
   744     FILE *file = fopen(filename.ptr(), "r");
   745     if (!file) {
   746         JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr,
   747                              JSSMSG_CANT_OPEN, filename.ptr(), strerror(errno));
   748         return false;
   749     }
   750     AutoCloseInputFile autoClose(file);
   752     if (!sizeGiven) {
   753         struct stat st;
   754         if (fstat(fileno(file), &st) < 0) {
   755             JS_ReportError(cx, "Unable to stat file");
   756             return false;
   757         }
   758         if (st.st_size < offset) {
   759             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
   760                                  JSMSG_ARG_INDEX_OUT_OF_RANGE, "2");
   761             return false;
   762         }
   763         size = st.st_size - offset;
   764     }
   766     void *contents = JS_CreateMappedArrayBufferContents(fileno(file), offset, size);
   767     if (!contents) {
   768         JS_ReportError(cx, "failed to allocate mapped array buffer contents (possibly due to bad alignment)");
   769         return false;
   770     }
   772     RootedObject obj(cx, JS_NewMappedArrayBufferWithContents(cx, size, contents));
   773     if (!obj)
   774         return false;
   776     args.rval().setObject(*obj);
   777     return true;
   778 }
   780 static bool
   781 Options(JSContext *cx, unsigned argc, jsval *vp)
   782 {
   783     CallArgs args = CallArgsFromVp(argc, vp);
   785     JS::ContextOptions oldContextOptions = JS::ContextOptionsRef(cx);
   786     for (unsigned i = 0; i < args.length(); i++) {
   787         JSString *str = JS::ToString(cx, args[i]);
   788         if (!str)
   789             return false;
   790         args[i].setString(str);
   792         JSAutoByteString opt(cx, str);
   793         if (!opt)
   794             return false;
   796         if (strcmp(opt.ptr(), "strict") == 0)
   797             JS::ContextOptionsRef(cx).toggleExtraWarnings();
   798         else if (strcmp(opt.ptr(), "werror") == 0)
   799             JS::ContextOptionsRef(cx).toggleWerror();
   800         else if (strcmp(opt.ptr(), "strict_mode") == 0)
   801             JS::ContextOptionsRef(cx).toggleStrictMode();
   802         else {
   803             char* msg = JS_sprintf_append(nullptr,
   804                                           "unknown option name '%s'."
   805                                           " The valid names are strict,"
   806                                           " werror, and strict_mode.",
   807                                           opt.ptr());
   808             if (!msg) {
   809                 JS_ReportOutOfMemory(cx);
   810                 return false;
   811             }
   813             JS_ReportError(cx, msg);
   814             free(msg);
   815             return false;
   816         }
   817     }
   819     char *names = strdup("");
   820     bool found = false;
   821     if (!names && oldContextOptions.extraWarnings()) {
   822         names = JS_sprintf_append(names, "%s%s", found ? "," : "", "strict");
   823         found = true;
   824     }
   825     if (!names && oldContextOptions.werror()) {
   826         names = JS_sprintf_append(names, "%s%s", found ? "," : "", "werror");
   827         found = true;
   828     }
   829     if (!names && oldContextOptions.strictMode()) {
   830         names = JS_sprintf_append(names, "%s%s", found ? "," : "", "strict_mode");
   831         found = true;
   832     }
   833     if (!names) {
   834         JS_ReportOutOfMemory(cx);
   835         return false;
   836     }
   838     JSString *str = JS_NewStringCopyZ(cx, names);
   839     free(names);
   840     if (!str)
   841         return false;
   842     args.rval().setString(str);
   843     return true;
   844 }
   846 static bool
   847 LoadScript(JSContext *cx, unsigned argc, jsval *vp, bool scriptRelative)
   848 {
   849     CallArgs args = CallArgsFromVp(argc, vp);
   850     RootedObject thisobj(cx, JS_THIS_OBJECT(cx, vp));
   851     if (!thisobj)
   852         return false;
   854     RootedString str(cx);
   855     for (unsigned i = 0; i < args.length(); i++) {
   856         str = JS::ToString(cx, args[i]);
   857         if (!str) {
   858             JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS, "load");
   859             return false;
   860         }
   861         str = ResolvePath(cx, str, scriptRelative ? ScriptRelative : RootRelative);
   862         if (!str) {
   863             JS_ReportError(cx, "unable to resolve path");
   864             return false;
   865         }
   866         JSAutoByteString filename(cx, str);
   867         if (!filename)
   868             return false;
   869         errno = 0;
   870         CompileOptions opts(cx);
   871         opts.setIntroductionType("js shell load")
   872             .setUTF8(true)
   873             .setCompileAndGo(true)
   874             .setNoScriptRval(true);
   875         if ((compileOnly && !Compile(cx, thisobj, opts, filename.ptr())) ||
   876             !Evaluate(cx, thisobj, opts, filename.ptr()))
   877         {
   878             return false;
   879         }
   880     }
   882     args.rval().setUndefined();
   883     return true;
   884 }
   886 static bool
   887 Load(JSContext *cx, unsigned argc, jsval *vp)
   888 {
   889     return LoadScript(cx, argc, vp, false);
   890 }
   892 static bool
   893 LoadScriptRelativeToScript(JSContext *cx, unsigned argc, jsval *vp)
   894 {
   895     return LoadScript(cx, argc, vp, true);
   896 }
   898 // Populate |options| with the options given by |opts|'s properties. If we
   899 // need to convert a filename to a C string, let fileNameBytes own the
   900 // bytes.
   901 static bool
   902 ParseCompileOptions(JSContext *cx, CompileOptions &options, HandleObject opts,
   903                     JSAutoByteString &fileNameBytes)
   904 {
   905     RootedValue v(cx);
   906     RootedString s(cx);
   908     if (!JS_GetProperty(cx, opts, "compileAndGo", &v))
   909         return false;
   910     if (!v.isUndefined())
   911         options.setCompileAndGo(ToBoolean(v));
   913     if (!JS_GetProperty(cx, opts, "noScriptRval", &v))
   914         return false;
   915     if (!v.isUndefined())
   916         options.setNoScriptRval(ToBoolean(v));
   918     if (!JS_GetProperty(cx, opts, "fileName", &v))
   919         return false;
   920     if (v.isNull()) {
   921         options.setFile(nullptr);
   922     } else if (!v.isUndefined()) {
   923         s = ToString(cx, v);
   924         if (!s)
   925             return false;
   926         char *fileName = fileNameBytes.encodeLatin1(cx, s);
   927         if (!fileName)
   928             return false;
   929         options.setFile(fileName);
   930     }
   932     if (!JS_GetProperty(cx, opts, "element", &v))
   933         return false;
   934     if (v.isObject())
   935         options.setElement(&v.toObject());
   937     if (!JS_GetProperty(cx, opts, "elementAttributeName", &v))
   938         return false;
   939     if (!v.isUndefined()) {
   940         s = ToString(cx, v);
   941         if (!s)
   942             return false;
   943         options.setElementAttributeName(s);
   944     }
   946     if (!JS_GetProperty(cx, opts, "lineNumber", &v))
   947         return false;
   948     if (!v.isUndefined()) {
   949         uint32_t u;
   950         if (!ToUint32(cx, v, &u))
   951             return false;
   952         options.setLine(u);
   953     }
   955     if (!JS_GetProperty(cx, opts, "sourceIsLazy", &v))
   956         return false;
   957     if (v.isBoolean())
   958         options.setSourceIsLazy(v.toBoolean());
   960     return true;
   961 }
   963 class AutoNewContext
   964 {
   965   private:
   966     JSContext *oldcx;
   967     JSContext *newcx;
   968     Maybe<JSAutoRequest> newRequest;
   969     Maybe<AutoCompartment> newCompartment;
   971     AutoNewContext(const AutoNewContext &) MOZ_DELETE;
   973   public:
   974     AutoNewContext() : oldcx(nullptr), newcx(nullptr) {}
   976     bool enter(JSContext *cx) {
   977         JS_ASSERT(!JS_IsExceptionPending(cx));
   978         oldcx = cx;
   979         newcx = NewContext(JS_GetRuntime(cx));
   980         if (!newcx)
   981             return false;
   982         JS::ContextOptionsRef(newcx).setDontReportUncaught(true);
   983         js::SetDefaultObjectForContext(newcx, JS::CurrentGlobalOrNull(cx));
   985         newRequest.construct(newcx);
   986         newCompartment.construct(newcx, JS::CurrentGlobalOrNull(cx));
   987         return true;
   988     }
   990     JSContext *get() { return newcx; }
   992     ~AutoNewContext() {
   993         if (newcx) {
   994             RootedValue exc(oldcx);
   995             bool throwing = JS_IsExceptionPending(newcx);
   996             if (throwing)
   997                 JS_GetPendingException(newcx, &exc);
   998             newCompartment.destroy();
   999             newRequest.destroy();
  1000             if (throwing)
  1001                 JS_SetPendingException(oldcx, exc);
  1002             DestroyContext(newcx, false);
  1005 };
  1007 static const uint32_t CacheEntry_SOURCE = 0;
  1008 static const uint32_t CacheEntry_BYTECODE = 1;
  1010 static const JSClass CacheEntry_class = {
  1011     "CacheEntryObject", JSCLASS_HAS_RESERVED_SLOTS(2),
  1012     JS_PropertyStub,       /* addProperty */
  1013     JS_DeletePropertyStub, /* delProperty */
  1014     JS_PropertyStub,       /* getProperty */
  1015     JS_StrictPropertyStub, /* setProperty */
  1016     JS_EnumerateStub,
  1017     JS_ResolveStub,
  1018     JS_ConvertStub,
  1019     nullptr,               /* finalize */
  1020     nullptr,               /* call */
  1021     nullptr,               /* hasInstance */
  1022     nullptr,               /* construct */
  1023     nullptr,               /* trace */
  1024     JSCLASS_NO_INTERNAL_MEMBERS
  1025 };
  1027 static bool
  1028 CacheEntry(JSContext* cx, unsigned argc, JS::Value *vp)
  1030     CallArgs args = CallArgsFromVp(argc, vp);
  1032     if (args.length() != 1 || !args[0].isString()) {
  1033         JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS, "CacheEntry");
  1034         return false;
  1037     RootedObject obj(cx, JS_NewObject(cx, &CacheEntry_class, JS::NullPtr(), JS::NullPtr()));
  1038     if (!obj)
  1039         return false;
  1041     SetReservedSlot(obj, CacheEntry_SOURCE, args[0]);
  1042     SetReservedSlot(obj, CacheEntry_BYTECODE, UndefinedValue());
  1043     args.rval().setObject(*obj);
  1044     return true;
  1047 static bool
  1048 CacheEntry_isCacheEntry(JSObject *cache)
  1050     return JS_GetClass(cache) == &CacheEntry_class;
  1053 static JSString *
  1054 CacheEntry_getSource(HandleObject cache)
  1056     JS_ASSERT(CacheEntry_isCacheEntry(cache));
  1057     Value v = JS_GetReservedSlot(cache, CacheEntry_SOURCE);
  1058     if (!v.isString())
  1059         return nullptr;
  1061     return v.toString();
  1064 static uint8_t *
  1065 CacheEntry_getBytecode(HandleObject cache, uint32_t *length)
  1067     JS_ASSERT(CacheEntry_isCacheEntry(cache));
  1068     Value v = JS_GetReservedSlot(cache, CacheEntry_BYTECODE);
  1069     if (!v.isObject() || !v.toObject().is<ArrayBufferObject>())
  1070         return nullptr;
  1072     ArrayBufferObject *arrayBuffer = &v.toObject().as<ArrayBufferObject>();
  1073     *length = arrayBuffer->byteLength();
  1074     return arrayBuffer->dataPointer();
  1077 static bool
  1078 CacheEntry_setBytecode(JSContext *cx, HandleObject cache, uint8_t *buffer, uint32_t length)
  1080     JS_ASSERT(CacheEntry_isCacheEntry(cache));
  1081     Rooted<ArrayBufferObject*> arrayBuffer(cx, ArrayBufferObject::create(cx, length, buffer));
  1083     if (!arrayBuffer || !ArrayBufferObject::ensureNonInline(cx, arrayBuffer))
  1084         return false;
  1086     SetReservedSlot(cache, CacheEntry_BYTECODE, OBJECT_TO_JSVAL(arrayBuffer));
  1087     return true;
  1090 class AutoSaveFrameChain
  1092     JSContext *cx_;
  1093     bool saved_;
  1095   public:
  1096     AutoSaveFrameChain(JSContext *cx)
  1097       : cx_(cx),
  1098         saved_(false)
  1099     {}
  1101     bool save() {
  1102         if (!JS_SaveFrameChain(cx_))
  1103             return false;
  1104         saved_ = true;
  1105         return true;
  1108     ~AutoSaveFrameChain() {
  1109         if (saved_)
  1110             JS_RestoreFrameChain(cx_);
  1112 };
  1114 static bool
  1115 Evaluate(JSContext *cx, unsigned argc, jsval *vp)
  1117     CallArgs args = CallArgsFromVp(argc, vp);
  1119     if (args.length() < 1 || args.length() > 2) {
  1120         JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr,
  1121                              args.length() < 1 ? JSSMSG_NOT_ENOUGH_ARGS : JSSMSG_TOO_MANY_ARGS,
  1122                              "evaluate");
  1123         return false;
  1126     RootedString code(cx, nullptr);
  1127     RootedObject cacheEntry(cx, nullptr);
  1128     if (args[0].isString()) {
  1129         code = args[0].toString();
  1130     } else if (args[0].isObject() && CacheEntry_isCacheEntry(&args[0].toObject())) {
  1131         cacheEntry = &args[0].toObject();
  1132         code = CacheEntry_getSource(cacheEntry);
  1135     if (!code || (args.length() == 2 && args[1].isPrimitive())) {
  1136         JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS, "evaluate");
  1137         return false;
  1140     CompileOptions options(cx);
  1141     JSAutoByteString fileNameBytes;
  1142     bool newContext = false;
  1143     RootedString displayURL(cx);
  1144     RootedString sourceMapURL(cx);
  1145     RootedObject global(cx, nullptr);
  1146     bool catchTermination = false;
  1147     bool saveFrameChain = false;
  1148     bool loadBytecode = false;
  1149     bool saveBytecode = false;
  1150     bool assertEqBytecode = false;
  1151     RootedObject callerGlobal(cx, cx->global());
  1153     options.setIntroductionType("js shell evaluate")
  1154            .setFileAndLine("@evaluate", 1);
  1156     global = JS_GetGlobalForObject(cx, &args.callee());
  1157     if (!global)
  1158         return false;
  1160     if (args.length() == 2) {
  1161         RootedObject opts(cx, &args[1].toObject());
  1162         RootedValue v(cx);
  1164         if (!ParseCompileOptions(cx, options, opts, fileNameBytes))
  1165             return false;
  1167         if (!JS_GetProperty(cx, opts, "newContext", &v))
  1168             return false;
  1169         if (!v.isUndefined())
  1170             newContext = ToBoolean(v);
  1172         if (!JS_GetProperty(cx, opts, "displayURL", &v))
  1173             return false;
  1174         if (!v.isUndefined()) {
  1175             displayURL = ToString(cx, v);
  1176             if (!displayURL)
  1177                 return false;
  1180         if (!JS_GetProperty(cx, opts, "sourceMapURL", &v))
  1181             return false;
  1182         if (!v.isUndefined()) {
  1183             sourceMapURL = ToString(cx, v);
  1184             if (!sourceMapURL)
  1185                 return false;
  1188         if (!JS_GetProperty(cx, opts, "global", &v))
  1189             return false;
  1190         if (!v.isUndefined()) {
  1191             if (v.isObject()) {
  1192                 global = js::UncheckedUnwrap(&v.toObject());
  1193                 if (!global)
  1194                     return false;
  1196             if (!global || !(JS_GetClass(global)->flags & JSCLASS_IS_GLOBAL)) {
  1197                 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
  1198                                      "\"global\" passed to evaluate()", "not a global object");
  1199                 return false;
  1203         if (!JS_GetProperty(cx, opts, "catchTermination", &v))
  1204             return false;
  1205         if (!v.isUndefined())
  1206             catchTermination = ToBoolean(v);
  1208         if (!JS_GetProperty(cx, opts, "saveFrameChain", &v))
  1209             return false;
  1210         if (!v.isUndefined())
  1211             saveFrameChain = ToBoolean(v);
  1213         if (!JS_GetProperty(cx, opts, "loadBytecode", &v))
  1214             return false;
  1215         if (!v.isUndefined())
  1216             loadBytecode = ToBoolean(v);
  1218         if (!JS_GetProperty(cx, opts, "saveBytecode", &v))
  1219             return false;
  1220         if (!v.isUndefined())
  1221             saveBytecode = ToBoolean(v);
  1223         if (!JS_GetProperty(cx, opts, "assertEqBytecode", &v))
  1224             return false;
  1225         if (!v.isUndefined())
  1226             assertEqBytecode = ToBoolean(v);
  1228         // We cannot load or save the bytecode if we have no object where the
  1229         // bytecode cache is stored.
  1230         if (loadBytecode || saveBytecode) {
  1231             if (!cacheEntry) {
  1232                 JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS,
  1233                                      "evaluate");
  1234                 return false;
  1239     size_t codeLength;
  1240     const jschar *codeChars = JS_GetStringCharsAndLength(cx, code, &codeLength);
  1241     if (!codeChars)
  1242         return false;
  1244     AutoNewContext ancx;
  1245     if (newContext) {
  1246         if (!ancx.enter(cx))
  1247             return false;
  1248         cx = ancx.get();
  1251     uint32_t loadLength = 0;
  1252     uint8_t *loadBuffer = nullptr;
  1253     uint32_t saveLength = 0;
  1254     ScopedJSFreePtr<uint8_t> saveBuffer;
  1256     if (loadBytecode) {
  1257         loadBuffer = CacheEntry_getBytecode(cacheEntry, &loadLength);
  1258         if (!loadBuffer)
  1259             return false;
  1263         AutoSaveFrameChain asfc(cx);
  1264         if (saveFrameChain && !asfc.save())
  1265             return false;
  1267         JSAutoCompartment ac(cx, global);
  1268         RootedScript script(cx);
  1270         if (!options.wrap(cx, cx->compartment()))
  1271             return false;
  1274             JS::AutoSaveContextOptions asco(cx);
  1275             JS::ContextOptionsRef(cx).setNoScriptRval(options.noScriptRval);
  1276             if (saveBytecode) {
  1277                 if (!JS::CompartmentOptionsRef(cx).getSingletonsAsTemplates()) {
  1278                     JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr,
  1279                                          JSSMSG_CACHE_SINGLETON_FAILED);
  1280                     return false;
  1282                 JS::CompartmentOptionsRef(cx).cloneSingletonsOverride().set(true);
  1285             if (loadBytecode) {
  1286                 script = JS_DecodeScript(cx, loadBuffer, loadLength, options.originPrincipals(cx));
  1287             } else {
  1288                 script = JS::Compile(cx, global, options, codeChars, codeLength);
  1291             if (!script)
  1292                 return false;
  1295         if (displayURL && !script->scriptSource()->hasDisplayURL()) {
  1296             const jschar *durl = JS_GetStringCharsZ(cx, displayURL);
  1297             if (!durl)
  1298                 return false;
  1299             if (!script->scriptSource()->setDisplayURL(cx, durl))
  1300                 return false;
  1302         if (sourceMapURL && !script->scriptSource()->hasSourceMapURL()) {
  1303             const jschar *smurl = JS_GetStringCharsZ(cx, sourceMapURL);
  1304             if (!smurl)
  1305                 return false;
  1306             if (!script->scriptSource()->setSourceMapURL(cx, smurl))
  1307                 return false;
  1309         if (!JS_ExecuteScript(cx, global, script, args.rval())) {
  1310             if (catchTermination && !JS_IsExceptionPending(cx)) {
  1311                 JSAutoCompartment ac1(cx, callerGlobal);
  1312                 JSString *str = JS_NewStringCopyZ(cx, "terminated");
  1313                 if (!str)
  1314                     return false;
  1315                 args.rval().setString(str);
  1316                 return true;
  1318             return false;
  1321         if (saveBytecode) {
  1322             saveBuffer = reinterpret_cast<uint8_t *>(JS_EncodeScript(cx, script, &saveLength));
  1323             if (!saveBuffer)
  1324                 return false;
  1328     if (saveBytecode) {
  1329         // If we are both loading and saving, we assert that we are going to
  1330         // replace the current bytecode by the same stream of bytes.
  1331         if (loadBytecode && assertEqBytecode) {
  1332             if (saveLength != loadLength) {
  1333                 JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_CACHE_EQ_SIZE_FAILED,
  1334                                      loadLength, saveLength);
  1335             } else if (!mozilla::PodEqual(loadBuffer, saveBuffer.get(), loadLength)) {
  1336                 JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr,
  1337                                      JSSMSG_CACHE_EQ_CONTENT_FAILED);
  1341         if (!CacheEntry_setBytecode(cx, cacheEntry, saveBuffer, saveLength))
  1342             return false;
  1344         saveBuffer.forget();
  1347     return JS_WrapValue(cx, args.rval());
  1350 static JSString *
  1351 FileAsString(JSContext *cx, const char *pathname)
  1353     FILE *file;
  1354     RootedString str(cx);
  1355     size_t len, cc;
  1356     char *buf;
  1358     file = fopen(pathname, "rb");
  1359     if (!file) {
  1360         JS_ReportError(cx, "can't open %s: %s", pathname, strerror(errno));
  1361         return nullptr;
  1363     AutoCloseInputFile autoClose(file);
  1365     if (fseek(file, 0, SEEK_END) != 0) {
  1366         JS_ReportError(cx, "can't seek end of %s", pathname);
  1367     } else {
  1368         len = ftell(file);
  1369         if (fseek(file, 0, SEEK_SET) != 0) {
  1370             JS_ReportError(cx, "can't seek start of %s", pathname);
  1371         } else {
  1372             buf = (char*) JS_malloc(cx, len + 1);
  1373             if (buf) {
  1374                 cc = fread(buf, 1, len, file);
  1375                 if (cc != len) {
  1376                     JS_ReportError(cx, "can't read %s: %s", pathname,
  1377                                    (ptrdiff_t(cc) < 0) ? strerror(errno) : "short read");
  1378                 } else {
  1379                     jschar *ucbuf =
  1380                         JS::UTF8CharsToNewTwoByteCharsZ(cx, JS::UTF8Chars(buf, len), &len).get();
  1381                     if (!ucbuf) {
  1382                         JS_ReportError(cx, "Invalid UTF-8 in file '%s'", pathname);
  1383                         gExitCode = EXITCODE_RUNTIME_ERROR;
  1384                         return nullptr;
  1386                     str = JS_NewUCStringCopyN(cx, ucbuf, len);
  1387                     free(ucbuf);
  1389                 JS_free(cx, buf);
  1394     return str;
  1397 static JSObject *
  1398 FileAsTypedArray(JSContext *cx, const char *pathname)
  1400     FILE *file = fopen(pathname, "rb");
  1401     if (!file) {
  1402         JS_ReportError(cx, "can't open %s: %s", pathname, strerror(errno));
  1403         return nullptr;
  1405     AutoCloseInputFile autoClose(file);
  1407     RootedObject obj(cx);
  1408     if (fseek(file, 0, SEEK_END) != 0) {
  1409         JS_ReportError(cx, "can't seek end of %s", pathname);
  1410     } else {
  1411         size_t len = ftell(file);
  1412         if (fseek(file, 0, SEEK_SET) != 0) {
  1413             JS_ReportError(cx, "can't seek start of %s", pathname);
  1414         } else {
  1415             obj = JS_NewUint8Array(cx, len);
  1416             if (!obj)
  1417                 return nullptr;
  1418             char *buf = (char *) obj->as<TypedArrayObject>().viewData();
  1419             size_t cc = fread(buf, 1, len, file);
  1420             if (cc != len) {
  1421                 JS_ReportError(cx, "can't read %s: %s", pathname,
  1422                                (ptrdiff_t(cc) < 0) ? strerror(errno) : "short read");
  1423                 obj = nullptr;
  1427     return obj;
  1430 /*
  1431  * Function to run scripts and return compilation + execution time. Semantics
  1432  * are closely modelled after the equivalent function in WebKit, as this is used
  1433  * to produce benchmark timings by SunSpider.
  1434  */
  1435 static bool
  1436 Run(JSContext *cx, unsigned argc, jsval *vp)
  1438     CallArgs args = CallArgsFromVp(argc, vp);
  1439     if (args.length() != 1) {
  1440         JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS, "run");
  1441         return false;
  1444     RootedObject thisobj(cx, JS_THIS_OBJECT(cx, vp));
  1445     if (!thisobj)
  1446         return false;
  1448     JSString *str = JS::ToString(cx, args[0]);
  1449     if (!str)
  1450         return false;
  1451     args[0].setString(str);
  1452     JSAutoByteString filename(cx, str);
  1453     if (!filename)
  1454         return false;
  1456     const jschar *ucbuf = nullptr;
  1457     size_t buflen;
  1458     str = FileAsString(cx, filename.ptr());
  1459     if (str)
  1460         ucbuf = JS_GetStringCharsAndLength(cx, str, &buflen);
  1461     if (!ucbuf)
  1462         return false;
  1464     JS::Anchor<JSString *> a_str(str);
  1466     RootedScript script(cx);
  1467     int64_t startClock = PRMJ_Now();
  1469         JS::AutoSaveContextOptions asco(cx);
  1470         JS::ContextOptionsRef(cx).setNoScriptRval(true);
  1472         JS::CompileOptions options(cx);
  1473         options.setIntroductionType("js shell run")
  1474                .setFileAndLine(filename.ptr(), 1)
  1475                .setCompileAndGo(true);
  1476         script = JS_CompileUCScript(cx, thisobj, ucbuf, buflen, options);
  1477         if (!script)
  1478             return false;
  1481     if (!JS_ExecuteScript(cx, thisobj, script))
  1482         return false;
  1484     int64_t endClock = PRMJ_Now();
  1486     args.rval().setDouble((endClock - startClock) / double(PRMJ_USEC_PER_MSEC));
  1487     return true;
  1490 /*
  1491  * function readline()
  1492  * Provides a hook for scripts to read a line from stdin.
  1493  */
  1494 static bool
  1495 ReadLine(JSContext *cx, unsigned argc, jsval *vp)
  1497     CallArgs args = CallArgsFromVp(argc, vp);
  1499 #define BUFSIZE 256
  1500     FILE *from = stdin;
  1501     size_t buflength = 0;
  1502     size_t bufsize = BUFSIZE;
  1503     char *buf = (char *) JS_malloc(cx, bufsize);
  1504     if (!buf)
  1505         return false;
  1507     bool sawNewline = false;
  1508     size_t gotlength;
  1509     while ((gotlength = js_fgets(buf + buflength, bufsize - buflength, from)) > 0) {
  1510         buflength += gotlength;
  1512         /* Are we done? */
  1513         if (buf[buflength - 1] == '\n') {
  1514             buf[buflength - 1] = '\0';
  1515             sawNewline = true;
  1516             break;
  1517         } else if (buflength < bufsize - 1) {
  1518             break;
  1521         /* Else, grow our buffer for another pass. */
  1522         char *tmp;
  1523         bufsize *= 2;
  1524         if (bufsize > buflength) {
  1525             tmp = (char *) JS_realloc(cx, buf, bufsize);
  1526         } else {
  1527             JS_ReportOutOfMemory(cx);
  1528             tmp = nullptr;
  1531         if (!tmp) {
  1532             JS_free(cx, buf);
  1533             return false;
  1536         buf = tmp;
  1539     /* Treat the empty string specially. */
  1540     if (buflength == 0) {
  1541         args.rval().set(feof(from) ? NullValue() : JS_GetEmptyStringValue(cx));
  1542         JS_free(cx, buf);
  1543         return true;
  1546     /* Shrink the buffer to the real size. */
  1547     char *tmp = static_cast<char*>(JS_realloc(cx, buf, buflength));
  1548     if (!tmp) {
  1549         JS_free(cx, buf);
  1550         return false;
  1553     buf = tmp;
  1555     /*
  1556      * Turn buf into a JSString. Note that buflength includes the trailing null
  1557      * character.
  1558      */
  1559     JSString *str = JS_NewStringCopyN(cx, buf, sawNewline ? buflength - 1 : buflength);
  1560     JS_free(cx, buf);
  1561     if (!str)
  1562         return false;
  1564     args.rval().setString(str);
  1565     return true;
  1568 static bool
  1569 PutStr(JSContext *cx, unsigned argc, jsval *vp)
  1571     CallArgs args = CallArgsFromVp(argc, vp);
  1573     if (args.length() != 0) {
  1574         JSString *str = JS::ToString(cx, args[0]);
  1575         if (!str)
  1576             return false;
  1577         char *bytes = JSStringToUTF8(cx, str);
  1578         if (!bytes)
  1579             return false;
  1580         fputs(bytes, gOutFile);
  1581         JS_free(cx, bytes);
  1582         fflush(gOutFile);
  1585     args.rval().setUndefined();
  1586     return true;
  1589 static bool
  1590 Now(JSContext *cx, unsigned argc, jsval *vp)
  1592     CallArgs args = CallArgsFromVp(argc, vp);
  1593     double now = PRMJ_Now() / double(PRMJ_USEC_PER_MSEC);
  1594     args.rval().setDouble(now);
  1595     return true;
  1598 static bool
  1599 PrintInternal(JSContext *cx, const CallArgs &args, FILE *file)
  1601     for (unsigned i = 0; i < args.length(); i++) {
  1602         JSString *str = JS::ToString(cx, args[i]);
  1603         if (!str)
  1604             return false;
  1605         char *bytes = JSStringToUTF8(cx, str);
  1606         if (!bytes)
  1607             return false;
  1608         fprintf(file, "%s%s", i ? " " : "", bytes);
  1609         JS_free(cx, bytes);
  1612     fputc('\n', file);
  1613     fflush(file);
  1615     args.rval().setUndefined();
  1616     return true;
  1619 static bool
  1620 Print(JSContext *cx, unsigned argc, jsval *vp)
  1622     CallArgs args = CallArgsFromVp(argc, vp);
  1623     return PrintInternal(cx, args, gOutFile);
  1626 static bool
  1627 PrintErr(JSContext *cx, unsigned argc, jsval *vp)
  1629     CallArgs args = CallArgsFromVp(argc, vp);
  1630     return PrintInternal(cx, args, gErrFile);
  1633 static bool
  1634 Help(JSContext *cx, unsigned argc, jsval *vp);
  1636 static bool
  1637 Quit(JSContext *cx, unsigned argc, jsval *vp)
  1639 #ifdef JS_MORE_DETERMINISTIC
  1640     // Print a message to stderr in more-deterministic builds to help jsfunfuzz
  1641     // find uncatchable-exception bugs.
  1642     fprintf(stderr, "quit called\n");
  1643 #endif
  1645     CallArgs args = CallArgsFromVp(argc, vp);
  1646     JS_ConvertArguments(cx, args, "/ i", &gExitCode);
  1648     gQuitting = true;
  1649     return false;
  1652 static const char *
  1653 ToSource(JSContext *cx, MutableHandleValue vp, JSAutoByteString *bytes)
  1655     JSString *str = JS_ValueToSource(cx, vp);
  1656     if (str) {
  1657         vp.setString(str);
  1658         if (bytes->encodeLatin1(cx, str))
  1659             return bytes->ptr();
  1661     JS_ClearPendingException(cx);
  1662     return "<<error converting value to string>>";
  1665 static bool
  1666 AssertEq(JSContext *cx, unsigned argc, jsval *vp)
  1668     CallArgs args = CallArgsFromVp(argc, vp);
  1669     if (!(args.length() == 2 || (args.length() == 3 && args[2].isString()))) {
  1670         JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr,
  1671                              (args.length() < 2)
  1672                              ? JSSMSG_NOT_ENOUGH_ARGS
  1673                              : (args.length() == 3)
  1674                              ? JSSMSG_INVALID_ARGS
  1675                              : JSSMSG_TOO_MANY_ARGS,
  1676                              "assertEq");
  1677         return false;
  1680     bool same;
  1681     if (!JS_SameValue(cx, args[0], args[1], &same))
  1682         return false;
  1683     if (!same) {
  1684         JSAutoByteString bytes0, bytes1;
  1685         const char *actual = ToSource(cx, args[0], &bytes0);
  1686         const char *expected = ToSource(cx, args[1], &bytes1);
  1687         if (args.length() == 2) {
  1688             JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_ASSERT_EQ_FAILED,
  1689                                  actual, expected);
  1690         } else {
  1691             JSAutoByteString bytes2(cx, args[2].toString());
  1692             if (!bytes2)
  1693                 return false;
  1694             JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_ASSERT_EQ_FAILED_MSG,
  1695                                  actual, expected, bytes2.ptr());
  1697         return false;
  1699     args.rval().setUndefined();
  1700     return true;
  1703 static JSScript *
  1704 ValueToScript(JSContext *cx, jsval vArg, JSFunction **funp = nullptr)
  1706     RootedValue v(cx, vArg);
  1707     RootedFunction fun(cx, JS_ValueToFunction(cx, v));
  1708     if (!fun)
  1709         return nullptr;
  1711     // Unwrap bound functions.
  1712     while (fun->isBoundFunction()) {
  1713         JSObject *target = fun->getBoundFunctionTarget();
  1714         if (target && target->is<JSFunction>())
  1715             fun = &target->as<JSFunction>();
  1716         else
  1717             break;
  1720     if (!fun->isInterpreted()) {
  1721         JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_SCRIPTS_ONLY);
  1722         return nullptr;
  1725     JSScript *script = fun->getOrCreateScript(cx);
  1726     if (!script)
  1727         return nullptr;
  1729     if (fun && funp)
  1730         *funp = fun;
  1732     return script;
  1735 static bool
  1736 SetDebug(JSContext *cx, unsigned argc, jsval *vp)
  1738     CallArgs args = CallArgsFromVp(argc, vp);
  1739     if (args.length() == 0 || !args[0].isBoolean()) {
  1740         JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr,
  1741                              JSSMSG_NOT_ENOUGH_ARGS, "setDebug");
  1742         return false;
  1745     /*
  1746      * Debug mode can only be set when there is no JS code executing on the
  1747      * stack. Unfortunately, that currently means that this call will fail
  1748      * unless debug mode is already set to what you're trying to set it to.
  1749      * In the future, this restriction may be lifted.
  1750      */
  1752     bool ok = !!JS_SetDebugMode(cx, args[0].toBoolean());
  1753     if (ok)
  1754         args.rval().setBoolean(true);
  1755     return ok;
  1758 static JSScript *
  1759 GetTopScript(JSContext *cx)
  1761     NonBuiltinScriptFrameIter iter(cx);
  1762     return iter.done() ? nullptr : iter.script();
  1765 static bool
  1766 GetScriptAndPCArgs(JSContext *cx, unsigned argc, jsval *argv, MutableHandleScript scriptp,
  1767                    int32_t *ip)
  1769     RootedScript script(cx, GetTopScript(cx));
  1770     *ip = 0;
  1771     if (argc != 0) {
  1772         jsval v = argv[0];
  1773         unsigned intarg = 0;
  1774         if (!JSVAL_IS_PRIMITIVE(v) &&
  1775             JS_GetClass(&v.toObject()) == Jsvalify(&JSFunction::class_)) {
  1776             script = ValueToScript(cx, v);
  1777             if (!script)
  1778                 return false;
  1779             intarg++;
  1781         if (argc > intarg) {
  1782             if (!JS::ToInt32(cx, HandleValue::fromMarkedLocation(&argv[intarg]), ip))
  1783                 return false;
  1784             if ((uint32_t)*ip >= script->length()) {
  1785                 JS_ReportError(cx, "Invalid PC");
  1786                 return false;
  1791     scriptp.set(script);
  1793     return true;
  1796 static JSTrapStatus
  1797 TrapHandler(JSContext *cx, JSScript *, jsbytecode *pc, jsval *rvalArg,
  1798             jsval closure)
  1800     RootedString str(cx, JSVAL_TO_STRING(closure));
  1801     RootedValue rval(cx, *rvalArg);
  1803     ScriptFrameIter iter(cx);
  1804     JS_ASSERT(!iter.done());
  1806     /* Debug-mode currently disables Ion compilation. */
  1807     JSAbstractFramePtr frame(iter.abstractFramePtr().raw(), iter.pc());
  1808     RootedScript script(cx, iter.script());
  1810     size_t length;
  1811     const jschar *chars = JS_GetStringCharsAndLength(cx, str, &length);
  1812     if (!chars)
  1813         return JSTRAP_ERROR;
  1815     if (!frame.evaluateUCInStackFrame(cx, chars, length,
  1816                                       script->filename(),
  1817                                       script->lineno(),
  1818                                       &rval))
  1820         *rvalArg = rval;
  1821         return JSTRAP_ERROR;
  1823     *rvalArg = rval;
  1824     if (!rval.isUndefined())
  1825         return JSTRAP_RETURN;
  1826     return JSTRAP_CONTINUE;
  1829 static bool
  1830 Trap(JSContext *cx, unsigned argc, jsval *vp)
  1832     CallArgs args = CallArgsFromVp(argc, vp);
  1833     RootedScript script(cx);
  1834     int32_t i;
  1836     if (args.length() == 0) {
  1837         JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_TRAP_USAGE);
  1838         return false;
  1840     argc = args.length() - 1;
  1841     RootedString str(cx, JS::ToString(cx, args[argc]));
  1842     if (!str)
  1843         return false;
  1844     args[argc].setString(str);
  1845     if (!GetScriptAndPCArgs(cx, argc, args.array(), &script, &i))
  1846         return false;
  1847     args.rval().setUndefined();
  1848     RootedValue strValue(cx, StringValue(str));
  1849     return JS_SetTrap(cx, script, script->offsetToPC(i), TrapHandler, strValue);
  1852 static bool
  1853 Untrap(JSContext *cx, unsigned argc, jsval *vp)
  1855     CallArgs args = CallArgsFromVp(argc, vp);
  1856     RootedScript script(cx);
  1857     int32_t i;
  1859     if (!GetScriptAndPCArgs(cx, args.length(), args.array(), &script, &i))
  1860         return false;
  1861     JS_ClearTrap(cx, script, script->offsetToPC(i), nullptr, nullptr);
  1862     args.rval().setUndefined();
  1863     return true;
  1866 static JSTrapStatus
  1867 DebuggerAndThrowHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval,
  1868                         void *closure)
  1870     return TrapHandler(cx, script, pc, rval, STRING_TO_JSVAL((JSString *)closure));
  1873 static bool
  1874 SetDebuggerHandler(JSContext *cx, unsigned argc, jsval *vp)
  1876     CallArgs args = CallArgsFromVp(argc, vp);
  1877     if (args.length() == 0) {
  1878         JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr,
  1879                              JSSMSG_NOT_ENOUGH_ARGS, "setDebuggerHandler");
  1880         return false;
  1883     JSString *str = JS::ToString(cx, args[0]);
  1884     if (!str)
  1885         return false;
  1887     JS_SetDebuggerHandler(cx->runtime(), DebuggerAndThrowHandler, str);
  1888     args.rval().setUndefined();
  1889     return true;
  1892 static bool
  1893 SetThrowHook(JSContext *cx, unsigned argc, jsval *vp)
  1895     CallArgs args = CallArgsFromVp(argc, vp);
  1896     JSString *str;
  1897     if (args.length() == 0) {
  1898         JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr,
  1899                              JSSMSG_NOT_ENOUGH_ARGS, "setThrowHook");
  1900         return false;
  1903     str = JS::ToString(cx, args[0]);
  1904     if (!str)
  1905         return false;
  1907     JS_SetThrowHook(cx->runtime(), DebuggerAndThrowHandler, str);
  1908     args.rval().setUndefined();
  1909     return true;
  1912 static bool
  1913 LineToPC(JSContext *cx, unsigned argc, jsval *vp)
  1915     CallArgs args = CallArgsFromVp(argc, vp);
  1917     if (args.length() == 0) {
  1918         JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_LINE2PC_USAGE);
  1919         return false;
  1922     RootedScript script(cx, GetTopScript(cx));
  1923     int32_t lineArg = 0;
  1924     if (args[0].isObject() && args[0].toObject().is<JSFunction>()) {
  1925         script = ValueToScript(cx, args[0]);
  1926         if (!script)
  1927             return false;
  1928         lineArg++;
  1931     uint32_t lineno;
  1932     if (!ToUint32(cx, args.get(lineArg), &lineno))
  1933          return false;
  1935     jsbytecode *pc = JS_LineNumberToPC(cx, script, lineno);
  1936     if (!pc)
  1937         return false;
  1938     args.rval().setInt32(script->pcToOffset(pc));
  1939     return true;
  1942 static bool
  1943 PCToLine(JSContext *cx, unsigned argc, jsval *vp)
  1945     CallArgs args = CallArgsFromVp(argc, vp);
  1946     RootedScript script(cx);
  1947     int32_t i;
  1948     unsigned lineno;
  1950     if (!GetScriptAndPCArgs(cx, args.length(), args.array(), &script, &i))
  1951         return false;
  1952     lineno = JS_PCToLineNumber(cx, script, script->offsetToPC(i));
  1953     if (!lineno)
  1954         return false;
  1955     args.rval().setInt32(lineno);
  1956     return true;
  1959 #ifdef DEBUG
  1961 static void
  1962 UpdateSwitchTableBounds(JSContext *cx, HandleScript script, unsigned offset,
  1963                         unsigned *start, unsigned *end)
  1965     jsbytecode *pc;
  1966     JSOp op;
  1967     ptrdiff_t jmplen;
  1968     int32_t low, high, n;
  1970     pc = script->offsetToPC(offset);
  1971     op = JSOp(*pc);
  1972     switch (op) {
  1973       case JSOP_TABLESWITCH:
  1974         jmplen = JUMP_OFFSET_LEN;
  1975         pc += jmplen;
  1976         low = GET_JUMP_OFFSET(pc);
  1977         pc += JUMP_OFFSET_LEN;
  1978         high = GET_JUMP_OFFSET(pc);
  1979         pc += JUMP_OFFSET_LEN;
  1980         n = high - low + 1;
  1981         break;
  1983       default:
  1984         /* [condswitch] switch does not have any jump or lookup tables. */
  1985         JS_ASSERT(op == JSOP_CONDSWITCH);
  1986         return;
  1989     *start = script->pcToOffset(pc);
  1990     *end = *start + (unsigned)(n * jmplen);
  1993 static void
  1994 SrcNotes(JSContext *cx, HandleScript script, Sprinter *sp)
  1996     Sprint(sp, "\nSource notes:\n");
  1997     Sprint(sp, "%4s %4s %5s %6s %-8s %s\n",
  1998            "ofs", "line", "pc", "delta", "desc", "args");
  1999     Sprint(sp, "---- ---- ----- ------ -------- ------\n");
  2000     unsigned offset = 0;
  2001     unsigned colspan = 0;
  2002     unsigned lineno = script->lineno();
  2003     jssrcnote *notes = script->notes();
  2004     unsigned switchTableEnd = 0, switchTableStart = 0;
  2005     for (jssrcnote *sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
  2006         unsigned delta = SN_DELTA(sn);
  2007         offset += delta;
  2008         SrcNoteType type = (SrcNoteType) SN_TYPE(sn);
  2009         const char *name = js_SrcNoteSpec[type].name;
  2010         Sprint(sp, "%3u: %4u %5u [%4u] %-8s", unsigned(sn - notes), lineno, offset, delta, name);
  2011         switch (type) {
  2012           case SRC_NULL:
  2013           case SRC_IF:
  2014           case SRC_CONTINUE:
  2015           case SRC_BREAK:
  2016           case SRC_BREAK2LABEL:
  2017           case SRC_SWITCHBREAK:
  2018           case SRC_ASSIGNOP:
  2019           case SRC_XDELTA:
  2020             break;
  2022           case SRC_COLSPAN:
  2023             colspan = js_GetSrcNoteOffset(sn, 0);
  2024             if (colspan >= SN_COLSPAN_DOMAIN / 2)
  2025                 colspan -= SN_COLSPAN_DOMAIN;
  2026             Sprint(sp, "%d", colspan);
  2027             break;
  2029           case SRC_SETLINE:
  2030             lineno = js_GetSrcNoteOffset(sn, 0);
  2031             Sprint(sp, " lineno %u", lineno);
  2032             break;
  2034           case SRC_NEWLINE:
  2035             ++lineno;
  2036             break;
  2038           case SRC_FOR:
  2039             Sprint(sp, " cond %u update %u tail %u",
  2040                    unsigned(js_GetSrcNoteOffset(sn, 0)),
  2041                    unsigned(js_GetSrcNoteOffset(sn, 1)),
  2042                    unsigned(js_GetSrcNoteOffset(sn, 2)));
  2043             break;
  2045           case SRC_IF_ELSE:
  2046             Sprint(sp, " else %u", unsigned(js_GetSrcNoteOffset(sn, 0)));
  2047             break;
  2049           case SRC_FOR_IN:
  2050           case SRC_FOR_OF:
  2051             Sprint(sp, " closingjump %u", unsigned(js_GetSrcNoteOffset(sn, 0)));
  2052             break;
  2054           case SRC_COND:
  2055           case SRC_WHILE:
  2056           case SRC_NEXTCASE:
  2057             Sprint(sp, " offset %u", unsigned(js_GetSrcNoteOffset(sn, 0)));
  2058             break;
  2060           case SRC_TABLESWITCH: {
  2061             JSOp op = JSOp(script->code()[offset]);
  2062             JS_ASSERT(op == JSOP_TABLESWITCH);
  2063             Sprint(sp, " length %u", unsigned(js_GetSrcNoteOffset(sn, 0)));
  2064             UpdateSwitchTableBounds(cx, script, offset,
  2065                                     &switchTableStart, &switchTableEnd);
  2066             break;
  2068           case SRC_CONDSWITCH: {
  2069             JSOp op = JSOp(script->code()[offset]);
  2070             JS_ASSERT(op == JSOP_CONDSWITCH);
  2071             Sprint(sp, " length %u", unsigned(js_GetSrcNoteOffset(sn, 0)));
  2072             unsigned caseOff = (unsigned) js_GetSrcNoteOffset(sn, 1);
  2073             if (caseOff)
  2074                 Sprint(sp, " first case offset %u", caseOff);
  2075             UpdateSwitchTableBounds(cx, script, offset,
  2076                                     &switchTableStart, &switchTableEnd);
  2077             break;
  2080           case SRC_TRY:
  2081             JS_ASSERT(JSOp(script->code()[offset]) == JSOP_TRY);
  2082             Sprint(sp, " offset to jump %u", unsigned(js_GetSrcNoteOffset(sn, 0)));
  2083             break;
  2085           default:
  2086             JS_ASSERT(0);
  2087             break;
  2089         Sprint(sp, "\n");
  2093 static bool
  2094 Notes(JSContext *cx, unsigned argc, jsval *vp)
  2096     CallArgs args = CallArgsFromVp(argc, vp);
  2097     Sprinter sprinter(cx);
  2098     if (!sprinter.init())
  2099         return false;
  2101     for (unsigned i = 0; i < args.length(); i++) {
  2102         RootedScript script (cx, ValueToScript(cx, args[i]));
  2103         if (!script)
  2104             return false;
  2106         SrcNotes(cx, script, &sprinter);
  2109     JSString *str = JS_NewStringCopyZ(cx, sprinter.string());
  2110     if (!str)
  2111         return false;
  2112     args.rval().setString(str);
  2113     return true;
  2116 JS_STATIC_ASSERT(JSTRY_CATCH == 0);
  2117 JS_STATIC_ASSERT(JSTRY_FINALLY == 1);
  2118 JS_STATIC_ASSERT(JSTRY_ITER == 2);
  2120 static const char* const TryNoteNames[] = { "catch", "finally", "iter", "loop" };
  2122 static bool
  2123 TryNotes(JSContext *cx, HandleScript script, Sprinter *sp)
  2125     JSTryNote *tn, *tnlimit;
  2127     if (!script->hasTrynotes())
  2128         return true;
  2130     tn = script->trynotes()->vector;
  2131     tnlimit = tn + script->trynotes()->length;
  2132     Sprint(sp, "\nException table:\nkind      stack    start      end\n");
  2133     do {
  2134         JS_ASSERT(tn->kind < ArrayLength(TryNoteNames));
  2135         Sprint(sp, " %-7s %6u %8u %8u\n",
  2136                TryNoteNames[tn->kind], tn->stackDepth,
  2137                tn->start, tn->start + tn->length);
  2138     } while (++tn != tnlimit);
  2139     return true;
  2142 static bool
  2143 DisassembleScript(JSContext *cx, HandleScript script, HandleFunction fun, bool lines,
  2144                   bool recursive, Sprinter *sp)
  2146     if (fun) {
  2147         Sprint(sp, "flags:");
  2148         if (fun->isLambda())
  2149             Sprint(sp, " LAMBDA");
  2150         if (fun->isHeavyweight())
  2151             Sprint(sp, " HEAVYWEIGHT");
  2152         if (fun->isExprClosure())
  2153             Sprint(sp, " EXPRESSION_CLOSURE");
  2154         if (fun->isFunctionPrototype())
  2155             Sprint(sp, " Function.prototype");
  2156         if (fun->isSelfHostedBuiltin())
  2157             Sprint(sp, " SELF_HOSTED");
  2158         if (fun->isSelfHostedConstructor())
  2159             Sprint(sp, " SELF_HOSTED_CTOR");
  2160         if (fun->isArrow())
  2161             Sprint(sp, " ARROW");
  2162         Sprint(sp, "\n");
  2165     if (!js_Disassemble(cx, script, lines, sp))
  2166         return false;
  2167     SrcNotes(cx, script, sp);
  2168     TryNotes(cx, script, sp);
  2170     if (recursive && script->hasObjects()) {
  2171         ObjectArray *objects = script->objects();
  2172         for (unsigned i = 0; i != objects->length; ++i) {
  2173             JSObject *obj = objects->vector[i];
  2174             if (obj->is<JSFunction>()) {
  2175                 Sprint(sp, "\n");
  2176                 RootedFunction fun(cx, &obj->as<JSFunction>());
  2177                 if (fun->isInterpreted()) {
  2178                     RootedScript script(cx, fun->getOrCreateScript(cx));
  2179                     if (!script || !DisassembleScript(cx, script, fun, lines, recursive, sp))
  2180                         return false;
  2181                 } else {
  2182                     Sprint(sp, "[native code]\n");
  2187     return true;
  2190 namespace {
  2192 struct DisassembleOptionParser {
  2193     unsigned   argc;
  2194     jsval   *argv;
  2195     bool    lines;
  2196     bool    recursive;
  2198     DisassembleOptionParser(unsigned argc, jsval *argv)
  2199       : argc(argc), argv(argv), lines(false), recursive(false) {}
  2201     bool parse(JSContext *cx) {
  2202         /* Read options off early arguments */
  2203         while (argc > 0 && argv[0].isString()) {
  2204             JSString *str = argv[0].toString();
  2205             JSFlatString *flatStr = JS_FlattenString(cx, str);
  2206             if (!flatStr)
  2207                 return false;
  2208             if (JS_FlatStringEqualsAscii(flatStr, "-l"))
  2209                 lines = true;
  2210             else if (JS_FlatStringEqualsAscii(flatStr, "-r"))
  2211                 recursive = true;
  2212             else
  2213                 break;
  2214             argv++, argc--;
  2216         return true;
  2218 };
  2220 } /* anonymous namespace */
  2222 static bool
  2223 DisassembleToSprinter(JSContext *cx, unsigned argc, jsval *vp, Sprinter *sprinter)
  2225     CallArgs args = CallArgsFromVp(argc, vp);
  2226     DisassembleOptionParser p(args.length(), args.array());
  2227     if (!p.parse(cx))
  2228         return false;
  2230     if (p.argc == 0) {
  2231         /* Without arguments, disassemble the current script. */
  2232         RootedScript script(cx, GetTopScript(cx));
  2233         if (script) {
  2234             JSAutoCompartment ac(cx, script);
  2235             if (!js_Disassemble(cx, script, p.lines, sprinter))
  2236                 return false;
  2237             SrcNotes(cx, script, sprinter);
  2238             TryNotes(cx, script, sprinter);
  2240     } else {
  2241         for (unsigned i = 0; i < p.argc; i++) {
  2242             RootedFunction fun(cx);
  2243             RootedScript script (cx, ValueToScript(cx, p.argv[i], fun.address()));
  2244             if (!script)
  2245                 return false;
  2246             if (!DisassembleScript(cx, script, fun, p.lines, p.recursive, sprinter))
  2247                 return false;
  2250     return true;
  2253 static bool
  2254 DisassembleToString(JSContext *cx, unsigned argc, jsval *vp)
  2256     CallArgs args = CallArgsFromVp(argc, vp);
  2257     Sprinter sprinter(cx);
  2258     if (!sprinter.init())
  2259         return false;
  2260     if (!DisassembleToSprinter(cx, args.length(), vp, &sprinter))
  2261         return false;
  2263     JSString *str = JS_NewStringCopyZ(cx, sprinter.string());
  2264     if (!str)
  2265         return false;
  2266     args.rval().setString(str);
  2267     return true;
  2270 static bool
  2271 Disassemble(JSContext *cx, unsigned argc, jsval *vp)
  2273     CallArgs args = CallArgsFromVp(argc, vp);
  2274     Sprinter sprinter(cx);
  2275     if (!sprinter.init())
  2276         return false;
  2277     if (!DisassembleToSprinter(cx, args.length(), vp, &sprinter))
  2278         return false;
  2280     fprintf(stdout, "%s\n", sprinter.string());
  2281     args.rval().setUndefined();
  2282     return true;
  2285 static bool
  2286 DisassFile(JSContext *cx, unsigned argc, jsval *vp)
  2288     CallArgs args = CallArgsFromVp(argc, vp);
  2290     /* Support extra options at the start, just like Disassemble. */
  2291     DisassembleOptionParser p(args.length(), args.array());
  2292     if (!p.parse(cx))
  2293         return false;
  2295     if (!p.argc) {
  2296         args.rval().setUndefined();
  2297         return true;
  2300     RootedObject thisobj(cx, JS_THIS_OBJECT(cx, vp));
  2301     if (!thisobj)
  2302         return false;
  2304     // We should change DisassembleOptionParser to store CallArgs.
  2305     JSString *str = JS::ToString(cx, HandleValue::fromMarkedLocation(&p.argv[0]));
  2306     if (!str)
  2307         return false;
  2308     JSAutoByteString filename(cx, str);
  2309     if (!filename)
  2310         return false;
  2311     RootedScript script(cx);
  2314         JS::AutoSaveContextOptions asco(cx);
  2315         JS::ContextOptionsRef(cx).setNoScriptRval(true);
  2317         CompileOptions options(cx);
  2318         options.setIntroductionType("js shell disFile")
  2319                .setUTF8(true)
  2320                .setFileAndLine(filename.ptr(), 1)
  2321                .setCompileAndGo(true);
  2323         script = JS::Compile(cx, thisobj, options, filename.ptr());
  2324         if (!script)
  2325             return false;
  2328     Sprinter sprinter(cx);
  2329     if (!sprinter.init())
  2330         return false;
  2331     bool ok = DisassembleScript(cx, script, NullPtr(), p.lines, p.recursive, &sprinter);
  2332     if (ok)
  2333         fprintf(stdout, "%s\n", sprinter.string());
  2334     if (!ok)
  2335         return false;
  2337     args.rval().setUndefined();
  2338     return true;
  2341 static bool
  2342 DisassWithSrc(JSContext *cx, unsigned argc, jsval *vp)
  2344     CallArgs args = CallArgsFromVp(argc, vp);
  2346 #define LINE_BUF_LEN 512
  2347     unsigned len, line1, line2, bupline;
  2348     FILE *file;
  2349     char linebuf[LINE_BUF_LEN];
  2350     jsbytecode *pc, *end;
  2351     static const char sep[] = ";-------------------------";
  2353     bool ok = true;
  2354     RootedScript script(cx);
  2355     for (unsigned i = 0; ok && i < args.length(); i++) {
  2356         script = ValueToScript(cx, args[i]);
  2357         if (!script)
  2358            return false;
  2360         if (!script->filename()) {
  2361             JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr,
  2362                                  JSSMSG_FILE_SCRIPTS_ONLY);
  2363             return false;
  2366         file = fopen(script->filename(), "r");
  2367         if (!file) {
  2368             JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr,
  2369                                  JSSMSG_CANT_OPEN, script->filename(),
  2370                                  strerror(errno));
  2371             return false;
  2374         pc = script->code();
  2375         end = script->codeEnd();
  2377         Sprinter sprinter(cx);
  2378         if (!sprinter.init()) {
  2379             ok = false;
  2380             goto bail;
  2383         /* burn the leading lines */
  2384         line2 = JS_PCToLineNumber(cx, script, pc);
  2385         for (line1 = 0; line1 < line2 - 1; line1++) {
  2386             char *tmp = fgets(linebuf, LINE_BUF_LEN, file);
  2387             if (!tmp) {
  2388                 JS_ReportError(cx, "failed to read %s fully", script->filename());
  2389                 ok = false;
  2390                 goto bail;
  2394         bupline = 0;
  2395         while (pc < end) {
  2396             line2 = JS_PCToLineNumber(cx, script, pc);
  2398             if (line2 < line1) {
  2399                 if (bupline != line2) {
  2400                     bupline = line2;
  2401                     Sprint(&sprinter, "%s %3u: BACKUP\n", sep, line2);
  2403             } else {
  2404                 if (bupline && line1 == line2)
  2405                     Sprint(&sprinter, "%s %3u: RESTORE\n", sep, line2);
  2406                 bupline = 0;
  2407                 while (line1 < line2) {
  2408                     if (!fgets(linebuf, LINE_BUF_LEN, file)) {
  2409                         JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr,
  2410                                              JSSMSG_UNEXPECTED_EOF,
  2411                                              script->filename());
  2412                         ok = false;
  2413                         goto bail;
  2415                     line1++;
  2416                     Sprint(&sprinter, "%s %3u: %s", sep, line1, linebuf);
  2420             len = js_Disassemble1(cx, script, pc, script->pcToOffset(pc), true, &sprinter);
  2421             if (!len) {
  2422                 ok = false;
  2423                 goto bail;
  2425             pc += len;
  2428       bail:
  2429         fclose(file);
  2431     args.rval().setUndefined();
  2432     return ok;
  2433 #undef LINE_BUF_LEN
  2436 static bool
  2437 DumpHeap(JSContext *cx, unsigned argc, jsval *vp)
  2439     CallArgs args = CallArgsFromVp(argc, vp);
  2441     JSAutoByteString fileName;
  2442     if (args.hasDefined(0)) {
  2443         RootedString str(cx, JS::ToString(cx, args[0]));
  2444         if (!str)
  2445             return false;
  2447         if (!fileName.encodeLatin1(cx, str))
  2448             return false;
  2451     RootedValue startThing(cx);
  2452     if (args.hasDefined(1)) {
  2453         if (!args[1].isGCThing()) {
  2454             JS_ReportError(cx, "dumpHeap: Second argument not a GC thing!");
  2455             return false;
  2457         startThing = args[1];
  2460     RootedValue thingToFind(cx);
  2461     if (args.hasDefined(2)) {
  2462         if (!args[2].isGCThing()) {
  2463             JS_ReportError(cx, "dumpHeap: Third argument not a GC thing!");
  2464             return false;
  2466         thingToFind = args[2];
  2469     size_t maxDepth = size_t(-1);
  2470     if (args.hasDefined(3)) {
  2471         uint32_t depth;
  2472         if (!ToUint32(cx, args[3], &depth))
  2473             return false;
  2474         maxDepth = depth;
  2477     RootedValue thingToIgnore(cx);
  2478     if (args.hasDefined(4)) {
  2479         if (!args[2].isGCThing()) {
  2480             JS_ReportError(cx, "dumpHeap: Fifth argument not a GC thing!");
  2481             return false;
  2483         thingToIgnore = args[4];
  2487     FILE *dumpFile = stdout;
  2488     if (fileName.length()) {
  2489         dumpFile = fopen(fileName.ptr(), "w");
  2490         if (!dumpFile) {
  2491             JS_ReportError(cx, "dumpHeap: can't open %s: %s\n",
  2492                           fileName.ptr(), strerror(errno));
  2493             return false;
  2497     bool ok = JS_DumpHeap(JS_GetRuntime(cx), dumpFile,
  2498                           startThing.isUndefined() ? nullptr : startThing.toGCThing(),
  2499                           startThing.isUndefined() ? JSTRACE_OBJECT : startThing.get().gcKind(),
  2500                           thingToFind.isUndefined() ? nullptr : thingToFind.toGCThing(),
  2501                           maxDepth,
  2502                           thingToIgnore.isUndefined() ? nullptr : thingToIgnore.toGCThing());
  2504     if (dumpFile != stdout)
  2505         fclose(dumpFile);
  2507     if (!ok)
  2508         JS_ReportOutOfMemory(cx);
  2510     return ok;
  2513 static bool
  2514 DumpObject(JSContext *cx, unsigned argc, jsval *vp)
  2516     CallArgs args = CallArgsFromVp(argc, vp);
  2517     RootedObject arg0(cx);
  2518     if (!JS_ConvertArguments(cx, args, "o", arg0.address()))
  2519         return false;
  2521     js_DumpObject(arg0);
  2523     args.rval().setUndefined();
  2524     return true;
  2527 #endif /* DEBUG */
  2529 static bool
  2530 BuildDate(JSContext *cx, unsigned argc, jsval *vp)
  2532     CallArgs args = CallArgsFromVp(argc, vp);
  2533     fprintf(gOutFile, "built on %s at %s\n", __DATE__, __TIME__);
  2534     args.rval().setUndefined();
  2535     return true;
  2538 static bool
  2539 Intern(JSContext *cx, unsigned argc, jsval *vp)
  2541     CallArgs args = CallArgsFromVp(argc, vp);
  2542     JSString *str = JS::ToString(cx, args.get(0));
  2543     if (!str)
  2544         return false;
  2546     size_t length;
  2547     const jschar *chars = JS_GetStringCharsAndLength(cx, str, &length);
  2548     if (!chars)
  2549         return false;
  2551     if (!JS_InternUCStringN(cx, chars, length))
  2552         return false;
  2554     args.rval().setUndefined();
  2555     return true;
  2558 static bool
  2559 Clone(JSContext *cx, unsigned argc, jsval *vp)
  2561     CallArgs args = CallArgsFromVp(argc, vp);
  2562     RootedObject parent(cx);
  2563     RootedObject funobj(cx);
  2565     if (!args.length()) {
  2566         JS_ReportError(cx, "Invalid arguments to clone");
  2567         return false;
  2571         Maybe<JSAutoCompartment> ac;
  2572         RootedObject obj(cx, JSVAL_IS_PRIMITIVE(args[0]) ? nullptr : &args[0].toObject());
  2574         if (obj && obj->is<CrossCompartmentWrapperObject>()) {
  2575             obj = UncheckedUnwrap(obj);
  2576             ac.construct(cx, obj);
  2577             args[0].setObject(*obj);
  2579         if (obj && obj->is<JSFunction>()) {
  2580             funobj = obj;
  2581         } else {
  2582             JSFunction *fun = JS_ValueToFunction(cx, args[0]);
  2583             if (!fun)
  2584                 return false;
  2585             funobj = JS_GetFunctionObject(fun);
  2588     if (funobj->compartment() != cx->compartment()) {
  2589         JSFunction *fun = &funobj->as<JSFunction>();
  2590         if (fun->hasScript() && fun->nonLazyScript()->compileAndGo()) {
  2591             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
  2592                                  "function", "compile-and-go");
  2593             return false;
  2597     if (args.length() > 1) {
  2598         if (!JS_ValueToObject(cx, args[1], &parent))
  2599             return false;
  2600     } else {
  2601         parent = JS_GetParent(&args.callee());
  2604     JSObject *clone = JS_CloneFunctionObject(cx, funobj, parent);
  2605     if (!clone)
  2606         return false;
  2607     args.rval().setObject(*clone);
  2608     return true;
  2611 static bool
  2612 GetPDA(JSContext *cx, unsigned argc, jsval *vp)
  2614     CallArgs args = CallArgsFromVp(argc, vp);
  2615     RootedObject vobj(cx);
  2616     bool ok;
  2617     JSPropertyDescArray pda;
  2618     JSPropertyDesc *pd;
  2620     if (!JS_ValueToObject(cx, args.get(0), &vobj))
  2621         return false;
  2622     if (!vobj) {
  2623         args.rval().setUndefined();
  2624         return true;
  2627     RootedObject aobj(cx, JS_NewArrayObject(cx, 0));
  2628     if (!aobj)
  2629         return false;
  2630     args.rval().setObject(*aobj);
  2632     ok = !!JS_GetPropertyDescArray(cx, vobj, &pda);
  2633     if (!ok)
  2634         return false;
  2635     pd = pda.array;
  2637     RootedObject pdobj(cx);
  2638     RootedValue id(cx);
  2639     RootedValue value(cx);
  2640     RootedValue flags(cx);
  2641     RootedValue alias(cx);
  2643     for (uint32_t i = 0; i < pda.length; i++, pd++) {
  2644         pdobj = JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr());
  2645         if (!pdobj) {
  2646             ok = false;
  2647             break;
  2650         /* Protect pdobj from GC by setting it as an element of aobj now */
  2651         ok = !!JS_SetElement(cx, aobj, i, pdobj);
  2652         if (!ok)
  2653             break;
  2655         id = pd->id;
  2656         value = pd->value;
  2657         flags.setInt32(pd->flags);
  2658         alias = pd->alias;
  2659         ok = JS_SetProperty(cx, pdobj, "id", id) &&
  2660              JS_SetProperty(cx, pdobj, "value", value) &&
  2661              JS_SetProperty(cx, pdobj, "flags", flags) &&
  2662              JS_SetProperty(cx, pdobj, "alias", alias);
  2663         if (!ok)
  2664             break;
  2666     JS_PutPropertyDescArray(cx, &pda);
  2667     return ok;
  2670 static bool
  2671 GetSLX(JSContext *cx, unsigned argc, jsval *vp)
  2673     CallArgs args = CallArgsFromVp(argc, vp);
  2674     RootedScript script(cx);
  2676     script = ValueToScript(cx, args.get(0));
  2677     if (!script)
  2678         return false;
  2679     args.rval().setInt32(js_GetScriptLineExtent(script));
  2680     return true;
  2683 static bool
  2684 ThrowError(JSContext *cx, unsigned argc, jsval *vp)
  2686     JS_ReportError(cx, "This is an error");
  2687     return false;
  2690 #define LAZY_STANDARD_CLASSES
  2692 /* A class for easily testing the inner/outer object callbacks. */
  2693 typedef struct ComplexObject {
  2694     bool isInner;
  2695     bool frozen;
  2696     JSObject *inner;
  2697     JSObject *outer;
  2698 } ComplexObject;
  2700 static bool
  2701 sandbox_enumerate(JSContext *cx, HandleObject obj)
  2703     RootedValue v(cx);
  2705     if (!JS_GetProperty(cx, obj, "lazy", &v))
  2706         return false;
  2708     if (!ToBoolean(v))
  2709         return true;
  2711     return JS_EnumerateStandardClasses(cx, obj);
  2714 static bool
  2715 sandbox_resolve(JSContext *cx, HandleObject obj, HandleId id, MutableHandleObject objp)
  2717     RootedValue v(cx);
  2718     if (!JS_GetProperty(cx, obj, "lazy", &v))
  2719         return false;
  2721     if (ToBoolean(v)) {
  2722         bool resolved;
  2723         if (!JS_ResolveStandardClass(cx, obj, id, &resolved))
  2724             return false;
  2725         if (resolved) {
  2726             objp.set(obj);
  2727             return true;
  2730     objp.set(nullptr);
  2731     return true;
  2734 static const JSClass sandbox_class = {
  2735     "sandbox",
  2736     JSCLASS_NEW_RESOLVE | JSCLASS_GLOBAL_FLAGS,
  2737     JS_PropertyStub,   JS_DeletePropertyStub,
  2738     JS_PropertyStub,   JS_StrictPropertyStub,
  2739     sandbox_enumerate, (JSResolveOp)sandbox_resolve,
  2740     JS_ConvertStub, nullptr,
  2741     nullptr, nullptr, nullptr,
  2742     JS_GlobalObjectTraceHook
  2743 };
  2745 static JSObject *
  2746 NewSandbox(JSContext *cx, bool lazy)
  2748     RootedObject obj(cx, JS_NewGlobalObject(cx, &sandbox_class, nullptr,
  2749                                             JS::DontFireOnNewGlobalHook));
  2750     if (!obj)
  2751         return nullptr;
  2754         JSAutoCompartment ac(cx, obj);
  2755         if (!lazy && !JS_InitStandardClasses(cx, obj))
  2756             return nullptr;
  2758         RootedValue value(cx, BooleanValue(lazy));
  2759         if (!JS_SetProperty(cx, obj, "lazy", value))
  2760             return nullptr;
  2763     JS_FireOnNewGlobalObject(cx, obj);
  2765     if (!cx->compartment()->wrap(cx, &obj))
  2766         return nullptr;
  2767     return obj;
  2770 static bool
  2771 EvalInContext(JSContext *cx, unsigned argc, jsval *vp)
  2773     CallArgs args = CallArgsFromVp(argc, vp);
  2775     RootedString str(cx);
  2776     RootedObject sobj(cx);
  2777     if (!JS_ConvertArguments(cx, args, "S / o", str.address(), sobj.address()))
  2778         return false;
  2780     size_t srclen;
  2781     const jschar *src = JS_GetStringCharsAndLength(cx, str, &srclen);
  2782     if (!src)
  2783         return false;
  2785     bool lazy = false;
  2786     if (srclen == 4) {
  2787         if (src[0] == 'l' && src[1] == 'a' && src[2] == 'z' && src[3] == 'y') {
  2788             lazy = true;
  2789             srclen = 0;
  2793     if (!sobj) {
  2794         sobj = NewSandbox(cx, lazy);
  2795         if (!sobj)
  2796             return false;
  2799     if (srclen == 0) {
  2800         args.rval().setObject(*sobj);
  2801         return true;
  2804     JS::AutoFilename filename;
  2805     unsigned lineno;
  2807     DescribeScriptedCaller(cx, &filename, &lineno);
  2809         Maybe<JSAutoCompartment> ac;
  2810         unsigned flags;
  2811         JSObject *unwrapped = UncheckedUnwrap(sobj, true, &flags);
  2812         if (flags & Wrapper::CROSS_COMPARTMENT) {
  2813             sobj = unwrapped;
  2814             ac.construct(cx, sobj);
  2817         sobj = GetInnerObject(cx, sobj);
  2818         if (!sobj)
  2819             return false;
  2820         if (!(sobj->getClass()->flags & JSCLASS_IS_GLOBAL)) {
  2821             JS_ReportError(cx, "Invalid scope argument to evalcx");
  2822             return false;
  2824         if (!JS_EvaluateUCScript(cx, sobj, src, srclen,
  2825                                  filename.get(),
  2826                                  lineno,
  2827                                  args.rval())) {
  2828             return false;
  2832     if (!cx->compartment()->wrap(cx, args.rval()))
  2833         return false;
  2835     return true;
  2838 static bool
  2839 EvalInFrame(JSContext *cx, unsigned argc, jsval *vp)
  2841     CallArgs args = CallArgsFromVp(argc, vp);
  2842     if (!args.get(0).isInt32() || !args.get(1).isString()) {
  2843         JS_ReportError(cx, "Invalid arguments to evalInFrame");
  2844         return false;
  2847     uint32_t upCount = args[0].toInt32();
  2848     RootedString str(cx, args[1].toString());
  2849     bool saveCurrent = args.get(2).isBoolean() ? args[2].toBoolean() : false;
  2851     /* This is a copy of CheckDebugMode. */
  2852     if (!JS_GetDebugMode(cx)) {
  2853         JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage,
  2854                                      nullptr, JSMSG_NEED_DEBUG_MODE);
  2855         return false;
  2858     /* Debug-mode currently disables Ion compilation. */
  2859     ScriptFrameIter fi(cx);
  2860     for (uint32_t i = 0; i < upCount; ++i, ++fi) {
  2861         ScriptFrameIter next(fi);
  2862         ++next;
  2863         if (next.done())
  2864             break;
  2867     AutoSaveFrameChain sfc(cx);
  2868     mozilla::Maybe<AutoCompartment> ac;
  2869     if (saveCurrent) {
  2870         if (!sfc.save())
  2871             return false;
  2872         ac.construct(cx, DefaultObjectForContextOrNull(cx));
  2875     size_t length;
  2876     const jschar *chars = JS_GetStringCharsAndLength(cx, str, &length);
  2877     if (!chars)
  2878         return false;
  2880     JSAbstractFramePtr frame(fi.abstractFramePtr().raw(), fi.pc());
  2881     RootedScript fpscript(cx, frame.script());
  2882     bool ok = !!frame.evaluateUCInStackFrame(cx, chars, length,
  2883                                              fpscript->filename(),
  2884                                              JS_PCToLineNumber(cx, fpscript,
  2885                                                                fi.pc()),
  2886                                              MutableHandleValue::fromMarkedLocation(vp));
  2887     return ok;
  2890 #ifdef JS_THREADSAFE
  2891 struct WorkerInput
  2893     JSRuntime *runtime;
  2894     jschar *chars;
  2895     size_t length;
  2897     WorkerInput(JSRuntime *runtime, jschar *chars, size_t length)
  2898       : runtime(runtime), chars(chars), length(length)
  2899     {}
  2901     ~WorkerInput() {
  2902         js_free(chars);
  2904 };
  2906 static void
  2907 WorkerMain(void *arg)
  2909     WorkerInput *input = (WorkerInput *) arg;
  2911     JSRuntime *rt = JS_NewRuntime(8L * 1024L * 1024L,
  2912                                   JS_USE_HELPER_THREADS,
  2913                                   input->runtime);
  2914     if (!rt) {
  2915         js_delete(input);
  2916         return;
  2919     JSContext *cx = NewContext(rt);
  2920     if (!cx) {
  2921         JS_DestroyRuntime(rt);
  2922         js_delete(input);
  2923         return;
  2926     do {
  2927         JSAutoRequest ar(cx);
  2929         JS::CompartmentOptions compartmentOptions;
  2930         compartmentOptions.setVersion(JSVERSION_LATEST);
  2931         RootedObject global(cx, NewGlobalObject(cx, compartmentOptions, nullptr));
  2932         if (!global)
  2933             break;
  2935         JSAutoCompartment ac(cx, global);
  2937         JS::CompileOptions options(cx);
  2938         options.setFileAndLine("<string>", 1)
  2939                .setCompileAndGo(true);
  2941         RootedScript script(cx, JS::Compile(cx, global, options,
  2942                                             input->chars, input->length));
  2943         if (!script)
  2944             break;
  2945         RootedValue result(cx);
  2946         JS_ExecuteScript(cx, global, script, &result);
  2947     } while (0);
  2949     DestroyContext(cx, false);
  2950     JS_DestroyRuntime(rt);
  2952     js_delete(input);
  2955 Vector<PRThread *, 0, SystemAllocPolicy> workerThreads;
  2957 static bool
  2958 EvalInWorker(JSContext *cx, unsigned argc, jsval *vp)
  2960     CallArgs args = CallArgsFromVp(argc, vp);
  2961     if (!args.get(0).isString()) {
  2962         JS_ReportError(cx, "Invalid arguments to evalInWorker");
  2963         return false;
  2966     if (!args[0].toString()->ensureLinear(cx))
  2967         return false;
  2969     JSLinearString *str = &args[0].toString()->asLinear();
  2971     jschar *chars = (jschar *) js_malloc(str->length() * sizeof(jschar));
  2972     if (!chars)
  2973         return false;
  2974     PodCopy(chars, str->chars(), str->length());
  2976     WorkerInput *input = js_new<WorkerInput>(cx->runtime(), chars, str->length());
  2977     if (!input)
  2978         return false;
  2980     PRThread *thread = PR_CreateThread(PR_USER_THREAD, WorkerMain, input,
  2981                                        PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
  2982     if (!thread || !workerThreads.append(thread))
  2983         return false;
  2985     return true;
  2987 #endif
  2989 static bool
  2990 ShapeOf(JSContext *cx, unsigned argc, JS::Value *vp)
  2992     CallArgs args = CallArgsFromVp(argc, vp);
  2993     if (!args.get(0).isObject()) {
  2994         JS_ReportError(cx, "shapeOf: object expected");
  2995         return false;
  2997     JSObject *obj = &args[0].toObject();
  2998     args.rval().set(JS_NumberValue(double(uintptr_t(obj->lastProperty()) >> 3)));
  2999     return true;
  3002 /*
  3003  * If referent has an own property named id, copy that property to obj[id].
  3004  * Since obj is native, this isn't totally transparent; properties of a
  3005  * non-native referent may be simplified to data properties.
  3006  */
  3007 static bool
  3008 CopyProperty(JSContext *cx, HandleObject obj, HandleObject referent, HandleId id,
  3009              MutableHandleObject objp)
  3011     RootedShape shape(cx);
  3012     Rooted<PropertyDescriptor> desc(cx);
  3013     RootedObject obj2(cx);
  3015     objp.set(nullptr);
  3016     if (referent->isNative()) {
  3017         if (!LookupNativeProperty(cx, referent, id, &obj2, &shape))
  3018             return false;
  3019         if (obj2 != referent)
  3020             return true;
  3022         if (shape->hasSlot()) {
  3023             desc.value().set(referent->nativeGetSlot(shape->slot()));
  3024         } else {
  3025             desc.value().setUndefined();
  3028         desc.setAttributes(shape->attributes());
  3029         desc.setGetter(shape->getter());
  3030         if (!desc.getter() && !desc.hasGetterObject())
  3031             desc.setGetter(JS_PropertyStub);
  3032         desc.setSetter(shape->setter());
  3033         if (!desc.setter() && !desc.hasSetterObject())
  3034             desc.setSetter(JS_StrictPropertyStub);
  3035     } else if (referent->is<ProxyObject>()) {
  3036         if (!Proxy::getOwnPropertyDescriptor(cx, referent, id, &desc))
  3037             return false;
  3038         if (!desc.object())
  3039             return true;
  3040     } else {
  3041         if (!JSObject::lookupGeneric(cx, referent, id, objp, &shape))
  3042             return false;
  3043         if (objp != referent)
  3044             return true;
  3045         RootedValue value(cx);
  3046         if (!JSObject::getGeneric(cx, referent, referent, id, &value) ||
  3047             !JSObject::getGenericAttributes(cx, referent, id, &desc.attributesRef()))
  3049             return false;
  3051         desc.value().set(value);
  3052         desc.attributesRef() &= JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT;
  3053         desc.setGetter(JS_PropertyStub);
  3054         desc.setSetter(JS_StrictPropertyStub);
  3057     objp.set(obj);
  3058     return DefineNativeProperty(cx, obj, id, desc.value(), desc.getter(), desc.setter(),
  3059                                 desc.attributes());
  3062 static bool
  3063 resolver_resolve(JSContext *cx, HandleObject obj, HandleId id, MutableHandleObject objp)
  3065     jsval v = JS_GetReservedSlot(obj, 0);
  3066     Rooted<JSObject*> vobj(cx, &v.toObject());
  3067     return CopyProperty(cx, obj, vobj, id, objp);
  3070 static bool
  3071 resolver_enumerate(JSContext *cx, HandleObject obj)
  3073     jsval v = JS_GetReservedSlot(obj, 0);
  3074     RootedObject referent(cx, JSVAL_TO_OBJECT(v));
  3076     AutoIdArray ida(cx, JS_Enumerate(cx, referent));
  3077     bool ok = !!ida;
  3078     RootedObject ignore(cx);
  3079     for (size_t i = 0; ok && i < ida.length(); i++) {
  3080         Rooted<jsid> id(cx, ida[i]);
  3081         ok = CopyProperty(cx, obj, referent, id, &ignore);
  3083     return ok;
  3086 static const JSClass resolver_class = {
  3087     "resolver",
  3088     JSCLASS_NEW_RESOLVE | JSCLASS_HAS_RESERVED_SLOTS(1),
  3089     JS_PropertyStub,   JS_DeletePropertyStub,
  3090     JS_PropertyStub,   JS_StrictPropertyStub,
  3091     resolver_enumerate, (JSResolveOp)resolver_resolve,
  3092     JS_ConvertStub
  3093 };
  3096 static bool
  3097 Resolver(JSContext *cx, unsigned argc, jsval *vp)
  3099     CallArgs args = CallArgsFromVp(argc, vp);
  3101     RootedObject referent(cx);
  3102     if (!JS_ValueToObject(cx, args.get(0), &referent))
  3103         return false;
  3104     if (!referent) {
  3105         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO,
  3106                              args.get(0).isNull() ? "null" : "undefined", "object");
  3107         return false;
  3110     RootedObject proto(cx, nullptr);
  3111     if (!args.get(1).isNullOrUndefined()) {
  3112         if (!JS_ValueToObject(cx, args.get(1), &proto))
  3113             return false;
  3116     RootedObject parent(cx, JS_GetParent(referent));
  3117     JSObject *result = (args.length() > 1
  3118                         ? JS_NewObjectWithGivenProto
  3119                         : JS_NewObject)(cx, &resolver_class, proto, parent);
  3120     if (!result)
  3121         return false;
  3123     JS_SetReservedSlot(result, 0, ObjectValue(*referent));
  3124     args.rval().setObject(*result);
  3125     return true;
  3128 #ifdef JS_THREADSAFE
  3130 /*
  3131  * Check that t1 comes strictly before t2. The function correctly deals with
  3132  * wrap-around between t2 and t1 assuming that t2 and t1 stays within INT32_MAX
  3133  * from each other. We use MAX_TIMEOUT_INTERVAL to enforce this restriction.
  3134  */
  3135 static bool
  3136 IsBefore(int64_t t1, int64_t t2)
  3138     return int32_t(t1 - t2) < 0;
  3141 static bool
  3142 Sleep_fn(JSContext *cx, unsigned argc, Value *vp)
  3144     CallArgs args = CallArgsFromVp(argc, vp);
  3145     int64_t t_ticks;
  3147     if (args.length() == 0) {
  3148         t_ticks = 0;
  3149     } else {
  3150         double t_secs;
  3152         if (!ToNumber(cx, args[0], &t_secs))
  3153             return false;
  3155         /* NB: The next condition also filter out NaNs. */
  3156         if (!(t_secs <= MAX_TIMEOUT_INTERVAL)) {
  3157             JS_ReportError(cx, "Excessive sleep interval");
  3158             return false;
  3160         t_ticks = (t_secs <= 0.0)
  3161                   ? 0
  3162                   : int64_t(PRMJ_USEC_PER_SEC * t_secs);
  3164     PR_Lock(gWatchdogLock);
  3165     int64_t to_wakeup = PRMJ_Now() + t_ticks;
  3166     for (;;) {
  3167         PR_WaitCondVar(gSleepWakeup, t_ticks);
  3168         if (gServiceInterrupt)
  3169             break;
  3170         int64_t now = PRMJ_Now();
  3171         if (!IsBefore(now, to_wakeup))
  3172             break;
  3173         t_ticks = to_wakeup - now;
  3175     PR_Unlock(gWatchdogLock);
  3176     return !gServiceInterrupt;
  3179 static bool
  3180 InitWatchdog(JSRuntime *rt)
  3182     JS_ASSERT(!gWatchdogThread);
  3183     gWatchdogLock = PR_NewLock();
  3184     if (gWatchdogLock) {
  3185         gWatchdogWakeup = PR_NewCondVar(gWatchdogLock);
  3186         if (gWatchdogWakeup) {
  3187             gSleepWakeup = PR_NewCondVar(gWatchdogLock);
  3188             if (gSleepWakeup)
  3189                 return true;
  3190             PR_DestroyCondVar(gWatchdogWakeup);
  3192         PR_DestroyLock(gWatchdogLock);
  3194     return false;
  3197 static void
  3198 KillWatchdog()
  3200     PRThread *thread;
  3202     PR_Lock(gWatchdogLock);
  3203     thread = gWatchdogThread;
  3204     if (thread) {
  3205         /*
  3206          * The watchdog thread is running, tell it to terminate waking it up
  3207          * if necessary.
  3208          */
  3209         gWatchdogThread = nullptr;
  3210         PR_NotifyCondVar(gWatchdogWakeup);
  3212     PR_Unlock(gWatchdogLock);
  3213     if (thread)
  3214         PR_JoinThread(thread);
  3215     PR_DestroyCondVar(gSleepWakeup);
  3216     PR_DestroyCondVar(gWatchdogWakeup);
  3217     PR_DestroyLock(gWatchdogLock);
  3220 static void
  3221 WatchdogMain(void *arg)
  3223     PR_SetCurrentThreadName("JS Watchdog");
  3225     JSRuntime *rt = (JSRuntime *) arg;
  3227     PR_Lock(gWatchdogLock);
  3228     while (gWatchdogThread) {
  3229         int64_t now = PRMJ_Now();
  3230         if (gWatchdogHasTimeout && !IsBefore(now, gWatchdogTimeout)) {
  3231             /*
  3232              * The timeout has just expired. Request an interrupt callback
  3233              * outside the lock.
  3234              */
  3235             gWatchdogHasTimeout = false;
  3236             PR_Unlock(gWatchdogLock);
  3237             CancelExecution(rt);
  3238             PR_Lock(gWatchdogLock);
  3240             /* Wake up any threads doing sleep. */
  3241             PR_NotifyAllCondVar(gSleepWakeup);
  3242         } else {
  3243             if (gWatchdogHasTimeout) {
  3244                 /*
  3245                  * Time hasn't expired yet. Simulate an interrupt callback
  3246                  * which doesn't abort execution.
  3247                  */
  3248                 JS_RequestInterruptCallback(rt);
  3251             uint64_t sleepDuration = PR_INTERVAL_NO_TIMEOUT;
  3252             if (gWatchdogHasTimeout)
  3253                 sleepDuration = PR_TicksPerSecond() / 10;
  3254             mozilla::DebugOnly<PRStatus> status =
  3255               PR_WaitCondVar(gWatchdogWakeup, sleepDuration);
  3256             JS_ASSERT(status == PR_SUCCESS);
  3259     PR_Unlock(gWatchdogLock);
  3262 static bool
  3263 ScheduleWatchdog(JSRuntime *rt, double t)
  3265     if (t <= 0) {
  3266         PR_Lock(gWatchdogLock);
  3267         gWatchdogHasTimeout = false;
  3268         PR_Unlock(gWatchdogLock);
  3269         return true;
  3272     int64_t interval = int64_t(ceil(t * PRMJ_USEC_PER_SEC));
  3273     int64_t timeout = PRMJ_Now() + interval;
  3274     PR_Lock(gWatchdogLock);
  3275     if (!gWatchdogThread) {
  3276         JS_ASSERT(!gWatchdogHasTimeout);
  3277         gWatchdogThread = PR_CreateThread(PR_USER_THREAD,
  3278                                           WatchdogMain,
  3279                                           rt,
  3280                                           PR_PRIORITY_NORMAL,
  3281                                           PR_GLOBAL_THREAD,
  3282                                           PR_JOINABLE_THREAD,
  3283                                           0);
  3284         if (!gWatchdogThread) {
  3285             PR_Unlock(gWatchdogLock);
  3286             return false;
  3288     } else if (!gWatchdogHasTimeout || IsBefore(timeout, gWatchdogTimeout)) {
  3289          PR_NotifyCondVar(gWatchdogWakeup);
  3291     gWatchdogHasTimeout = true;
  3292     gWatchdogTimeout = timeout;
  3293     PR_Unlock(gWatchdogLock);
  3294     return true;
  3297 #else /* !JS_THREADSAFE */
  3299 #ifdef XP_WIN
  3300 static HANDLE gTimerHandle = 0;
  3302 VOID CALLBACK
  3303 TimerCallback(PVOID lpParameter, BOOLEAN TimerOrWaitFired)
  3305     CancelExecution((JSRuntime *) lpParameter);
  3308 #else
  3310 static void
  3311 AlarmHandler(int sig)
  3313     CancelExecution(gRuntime);
  3316 #endif
  3318 static bool
  3319 InitWatchdog(JSRuntime *rt)
  3321     gRuntime = rt;
  3322     return true;
  3325 static void
  3326 KillWatchdog()
  3328     ScheduleWatchdog(gRuntime, -1);
  3331 static bool
  3332 ScheduleWatchdog(JSRuntime *rt, double t)
  3334 #ifdef XP_WIN
  3335     if (gTimerHandle) {
  3336         DeleteTimerQueueTimer(nullptr, gTimerHandle, nullptr);
  3337         gTimerHandle = 0;
  3339     if (t > 0 &&
  3340         !CreateTimerQueueTimer(&gTimerHandle,
  3341                                nullptr,
  3342                                (WAITORTIMERCALLBACK)TimerCallback,
  3343                                rt,
  3344                                DWORD(ceil(t * 1000.0)),
  3345                                0,
  3346                                WT_EXECUTEINTIMERTHREAD | WT_EXECUTEONLYONCE)) {
  3347         gTimerHandle = 0;
  3348         return false;
  3350 #else
  3351     /* FIXME: use setitimer when available for sub-second resolution. */
  3352     if (t <= 0) {
  3353         alarm(0);
  3354         signal(SIGALRM, nullptr);
  3355     } else {
  3356         signal(SIGALRM, AlarmHandler); /* set the Alarm signal capture */
  3357         alarm(ceil(t));
  3359 #endif
  3360     return true;
  3363 #endif /* !JS_THREADSAFE */
  3365 static void
  3366 CancelExecution(JSRuntime *rt)
  3368     gServiceInterrupt = true;
  3369     JS_RequestInterruptCallback(rt);
  3371     if (!gInterruptFunc.ref().get().isNull()) {
  3372         static const char msg[] = "Script runs for too long, terminating.\n";
  3373 #if defined(XP_UNIX) && !defined(JS_THREADSAFE)
  3374         /* It is not safe to call fputs from signals. */
  3375         /* Dummy assignment avoids GCC warning on "attribute warn_unused_result" */
  3376         ssize_t dummy = write(2, msg, sizeof(msg) - 1);
  3377         (void)dummy;
  3378 #else
  3379         fputs(msg, stderr);
  3380 #endif
  3384 static bool
  3385 SetTimeoutValue(JSContext *cx, double t)
  3387     /* NB: The next condition also filter out NaNs. */
  3388     if (!(t <= MAX_TIMEOUT_INTERVAL)) {
  3389         JS_ReportError(cx, "Excessive timeout value");
  3390         return false;
  3392     gTimeoutInterval = t;
  3393     if (!ScheduleWatchdog(cx->runtime(), t)) {
  3394         JS_ReportError(cx, "Failed to create the watchdog");
  3395         return false;
  3397     return true;
  3400 static bool
  3401 Timeout(JSContext *cx, unsigned argc, Value *vp)
  3403     CallArgs args = CallArgsFromVp(argc, vp);
  3405     if (args.length() == 0) {
  3406         args.rval().setNumber(gTimeoutInterval);
  3407         return true;
  3410     if (args.length() > 2) {
  3411         JS_ReportError(cx, "Wrong number of arguments");
  3412         return false;
  3415     double t;
  3416     if (!ToNumber(cx, args[0], &t))
  3417         return false;
  3419     if (args.length() > 1) {
  3420         RootedValue value(cx, args[1]);
  3421         if (!value.isObject() || !value.toObject().is<JSFunction>()) {
  3422             JS_ReportError(cx, "Second argument must be a timeout function");
  3423             return false;
  3425         gInterruptFunc.ref() = value;
  3428     args.rval().setUndefined();
  3429     return SetTimeoutValue(cx, t);
  3432 static bool
  3433 InterruptIf(JSContext *cx, unsigned argc, Value *vp)
  3435     CallArgs args = CallArgsFromVp(argc, vp);
  3437     if (args.length() != 1) {
  3438         JS_ReportError(cx, "Wrong number of arguments");
  3439         return false;
  3442     if (ToBoolean(args[0])) {
  3443         gServiceInterrupt = true;
  3444         JS_RequestInterruptCallback(cx->runtime());
  3447     args.rval().setUndefined();
  3448     return true;
  3451 static bool
  3452 SetInterruptCallback(JSContext *cx, unsigned argc, Value *vp)
  3454     CallArgs args = CallArgsFromVp(argc, vp);
  3456     if (args.length() != 1) {
  3457         JS_ReportError(cx, "Wrong number of arguments");
  3458         return false;
  3461     RootedValue value(cx, args[0]);
  3462     if (!value.isObject() || !value.toObject().is<JSFunction>()) {
  3463         JS_ReportError(cx, "Argument must be a function");
  3464         return false;
  3466     gInterruptFunc.ref() = value;
  3468     args.rval().setUndefined();
  3469     return true;
  3472 static bool
  3473 Elapsed(JSContext *cx, unsigned argc, jsval *vp)
  3475     CallArgs args = CallArgsFromVp(argc, vp);
  3476     if (args.length() == 0) {
  3477         double d = 0.0;
  3478         JSShellContextData *data = GetContextData(cx);
  3479         if (data)
  3480             d = PRMJ_Now() - data->startTime;
  3481         args.rval().setDouble(d);
  3482         return true;
  3484     JS_ReportError(cx, "Wrong number of arguments");
  3485     return false;
  3488 static bool
  3489 Parent(JSContext *cx, unsigned argc, jsval *vp)
  3491     CallArgs args = CallArgsFromVp(argc, vp);
  3492     if (args.length() != 1) {
  3493         JS_ReportError(cx, "Wrong number of arguments");
  3494         return false;
  3497     Value v = args[0];
  3498     if (JSVAL_IS_PRIMITIVE(v)) {
  3499         JS_ReportError(cx, "Only objects have parents!");
  3500         return false;
  3503     Rooted<JSObject*> parent(cx, JS_GetParent(&v.toObject()));
  3504     args.rval().setObjectOrNull(parent);
  3506     /* Outerize if necessary.  Embrace the ugliness! */
  3507     if (parent) {
  3508         if (JSObjectOp op = parent->getClass()->ext.outerObject)
  3509             args.rval().setObjectOrNull(op(cx, parent));
  3512     return true;
  3515 static bool
  3516 Compile(JSContext *cx, unsigned argc, jsval *vp)
  3518     CallArgs args = CallArgsFromVp(argc, vp);
  3519     if (args.length() < 1) {
  3520         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
  3521                              "compile", "0", "s");
  3522         return false;
  3524     if (!args[0].isString()) {
  3525         const char *typeName = JS_GetTypeName(cx, JS_TypeOfValue(cx, args[0]));
  3526         JS_ReportError(cx, "expected string to compile, got %s", typeName);
  3527         return false;
  3530     RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
  3531     JSString *scriptContents = args[0].toString();
  3532     JS::AutoSaveContextOptions asco(cx);
  3533     JS::ContextOptionsRef(cx).setNoScriptRval(true);
  3534     JS::CompileOptions options(cx);
  3535     options.setIntroductionType("js shell compile")
  3536            .setFileAndLine("<string>", 1)
  3537            .setCompileAndGo(true);
  3538     bool ok = JS_CompileUCScript(cx, global, JS_GetStringCharsZ(cx, scriptContents),
  3539                                  JS_GetStringLength(scriptContents), options);
  3540     args.rval().setUndefined();
  3541     return ok;
  3544 static bool
  3545 Parse(JSContext *cx, unsigned argc, jsval *vp)
  3547     using namespace js::frontend;
  3549     CallArgs args = CallArgsFromVp(argc, vp);
  3551     if (args.length() < 1) {
  3552         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
  3553                              "parse", "0", "s");
  3554         return false;
  3556     if (!args[0].isString()) {
  3557         const char *typeName = JS_GetTypeName(cx, JS_TypeOfValue(cx, args[0]));
  3558         JS_ReportError(cx, "expected string to parse, got %s", typeName);
  3559         return false;
  3562     JSString *scriptContents = args[0].toString();
  3563     CompileOptions options(cx);
  3564     options.setIntroductionType("js shell parse")
  3565            .setFileAndLine("<string>", 1)
  3566            .setCompileAndGo(false);
  3567     Parser<FullParseHandler> parser(cx, &cx->tempLifoAlloc(), options,
  3568                                     JS_GetStringCharsZ(cx, scriptContents),
  3569                                     JS_GetStringLength(scriptContents),
  3570                                     /* foldConstants = */ true, nullptr, nullptr);
  3572     ParseNode *pn = parser.parse(nullptr);
  3573     if (!pn)
  3574         return false;
  3575 #ifdef DEBUG
  3576     DumpParseTree(pn);
  3577     fputc('\n', stderr);
  3578 #endif
  3579     args.rval().setUndefined();
  3580     return true;
  3583 static bool
  3584 SyntaxParse(JSContext *cx, unsigned argc, jsval *vp)
  3586     using namespace js::frontend;
  3588     CallArgs args = CallArgsFromVp(argc, vp);
  3590     if (args.length() < 1) {
  3591         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
  3592                              "parse", "0", "s");
  3593         return false;
  3595     if (!args[0].isString()) {
  3596         const char *typeName = JS_GetTypeName(cx, JS_TypeOfValue(cx, args[0]));
  3597         JS_ReportError(cx, "expected string to parse, got %s", typeName);
  3598         return false;
  3601     JSString *scriptContents = args[0].toString();
  3602     CompileOptions options(cx);
  3603     options.setIntroductionType("js shell syntaxParse")
  3604            .setFileAndLine("<string>", 1)
  3605            .setCompileAndGo(false);
  3607     const jschar *chars = JS_GetStringCharsZ(cx, scriptContents);
  3608     size_t length = JS_GetStringLength(scriptContents);
  3609     Parser<frontend::SyntaxParseHandler> parser(cx, &cx->tempLifoAlloc(),
  3610                                                 options, chars, length, false, nullptr, nullptr);
  3612     bool succeeded = parser.parse(nullptr);
  3613     if (cx->isExceptionPending())
  3614         return false;
  3616     if (!succeeded && !parser.hadAbortedSyntaxParse()) {
  3617         // If no exception is posted, either there was an OOM or a language
  3618         // feature unhandled by the syntax parser was encountered.
  3619         JS_ASSERT(cx->runtime()->hadOutOfMemory);
  3620         return false;
  3623     args.rval().setBoolean(succeeded);
  3624     return true;
  3627 #ifdef JS_THREADSAFE
  3629 class OffThreadState {
  3630   public:
  3631     enum State {
  3632         IDLE,           /* ready to work; no token, no source */
  3633         COMPILING,      /* working; no token, have source */
  3634         DONE            /* compilation done: have token and source */
  3635     };
  3637     OffThreadState() : monitor(), state(IDLE), token() { }
  3638     bool init() { return monitor.init(); }
  3640     bool startIfIdle(JSContext *cx, JSString *newSource) {
  3641         AutoLockMonitor alm(monitor);
  3642         if (state != IDLE)
  3643             return false;
  3645         JS_ASSERT(!token);
  3647         source.construct(cx, newSource);
  3649         state = COMPILING;
  3650         return true;
  3653     void abandon(JSContext *cx) {
  3654         AutoLockMonitor alm(monitor);
  3655         JS_ASSERT(state == COMPILING);
  3656         JS_ASSERT(!token);
  3657         JS_ASSERT(source.ref());
  3659         source.destroy();
  3661         state = IDLE;
  3664     void markDone(void *newToken) {
  3665         AutoLockMonitor alm(monitor);
  3666         JS_ASSERT(state == COMPILING);
  3667         JS_ASSERT(!token);
  3668         JS_ASSERT(source.ref());
  3669         JS_ASSERT(newToken);
  3671         token = newToken;
  3672         state = DONE;
  3673         alm.notify();
  3676     void *waitUntilDone(JSContext *cx) {
  3677         AutoLockMonitor alm(monitor);
  3678         if (state == IDLE)
  3679             return nullptr;
  3681         if (state == COMPILING) {
  3682             while (state != DONE)
  3683                 alm.wait();
  3686         JS_ASSERT(source.ref());
  3687         source.destroy();
  3689         JS_ASSERT(token);
  3690         void *holdToken = token;
  3691         token = nullptr;
  3692         state = IDLE;
  3693         return holdToken;
  3696   private:
  3697     Monitor monitor;
  3698     State state;
  3699     void *token;
  3700     Maybe<PersistentRootedString> source;
  3701 };
  3703 static OffThreadState offThreadState;
  3705 static void
  3706 OffThreadCompileScriptCallback(void *token, void *callbackData)
  3708     offThreadState.markDone(token);
  3711 static bool
  3712 OffThreadCompileScript(JSContext *cx, unsigned argc, jsval *vp)
  3714     CallArgs args = CallArgsFromVp(argc, vp);
  3716     if (args.length() < 1) {
  3717         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
  3718                              "offThreadCompileScript", "0", "s");
  3719         return false;
  3721     if (!args[0].isString()) {
  3722         const char *typeName = JS_GetTypeName(cx, JS_TypeOfValue(cx, args[0]));
  3723         JS_ReportError(cx, "expected string to parse, got %s", typeName);
  3724         return false;
  3727     JSAutoByteString fileNameBytes;
  3728     CompileOptions options(cx);
  3729     options.setIntroductionType("js shell offThreadCompileScript")
  3730            .setFileAndLine("<string>", 1);
  3732     if (args.length() >= 2) {
  3733         if (args[1].isPrimitive()) {
  3734             JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS, "evaluate");
  3735             return false;
  3738         RootedObject opts(cx, &args[1].toObject());
  3739         if (!ParseCompileOptions(cx, options, opts, fileNameBytes))
  3740             return false;
  3743     // These option settings must override whatever the caller requested.
  3744     options.setCompileAndGo(true)
  3745            .setSourceIsLazy(false);
  3747     // We assume the caller wants caching if at all possible, ignoring
  3748     // heuristics that make sense for a real browser.
  3749     options.forceAsync = true;
  3751     JSString *scriptContents = args[0].toString();
  3752     const jschar *chars = JS_GetStringCharsZ(cx, scriptContents);
  3753     if (!chars)
  3754         return false;
  3755     size_t length = JS_GetStringLength(scriptContents);
  3757     if (!JS::CanCompileOffThread(cx, options, length)) {
  3758         JS_ReportError(cx, "cannot compile code on worker thread");
  3759         return false;
  3762     if (!offThreadState.startIfIdle(cx, scriptContents)) {
  3763         JS_ReportError(cx, "called offThreadCompileScript without calling runOffThreadScript"
  3764                        " to receive prior off-thread compilation");
  3765         return false;
  3768     if (!JS::CompileOffThread(cx, options, chars, length,
  3769                               OffThreadCompileScriptCallback, nullptr))
  3771         offThreadState.abandon(cx);
  3772         return false;
  3775     args.rval().setUndefined();
  3776     return true;
  3779 static bool
  3780 runOffThreadScript(JSContext *cx, unsigned argc, jsval *vp)
  3782     CallArgs args = CallArgsFromVp(argc, vp);
  3784     void *token = offThreadState.waitUntilDone(cx);
  3785     if (!token) {
  3786         JS_ReportError(cx, "called runOffThreadScript when no compilation is pending");
  3787         return false;
  3790     RootedScript script(cx, JS::FinishOffThreadScript(cx, cx->runtime(), token));
  3791     if (!script)
  3792         return false;
  3794     return JS_ExecuteScript(cx, cx->global(), script, args.rval());
  3797 #endif // JS_THREADSAFE
  3799 struct FreeOnReturn
  3801     JSContext *cx;
  3802     const char *ptr;
  3803     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
  3805     FreeOnReturn(JSContext *cx, const char *ptr = nullptr
  3806                  MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
  3807       : cx(cx), ptr(ptr)
  3809         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
  3812     void init(const char *ptr) {
  3813         JS_ASSERT(!this->ptr);
  3814         this->ptr = ptr;
  3817     ~FreeOnReturn() {
  3818         JS_free(cx, (void*)ptr);
  3820 };
  3822 static bool
  3823 ReadFile(JSContext *cx, unsigned argc, jsval *vp, bool scriptRelative)
  3825     CallArgs args = CallArgsFromVp(argc, vp);
  3827     if (args.length() < 1 || args.length() > 2) {
  3828         JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr,
  3829                              args.length() < 1 ? JSSMSG_NOT_ENOUGH_ARGS : JSSMSG_TOO_MANY_ARGS,
  3830                              "snarf");
  3831         return false;
  3834     if (!args[0].isString() || (args.length() == 2 && !args[1].isString())) {
  3835         JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS, "snarf");
  3836         return false;
  3839     RootedString givenPath(cx, args[0].toString());
  3840     RootedString str(cx, ResolvePath(cx, givenPath, scriptRelative ? ScriptRelative : RootRelative));
  3841     if (!str)
  3842         return false;
  3844     JSAutoByteString filename(cx, str);
  3845     if (!filename)
  3846         return false;
  3848     if (args.length() > 1) {
  3849         JSString *opt = JS::ToString(cx, args[1]);
  3850         if (!opt)
  3851             return false;
  3852         bool match;
  3853         if (!JS_StringEqualsAscii(cx, opt, "binary", &match))
  3854             return false;
  3855         if (match) {
  3856             JSObject *obj;
  3857             if (!(obj = FileAsTypedArray(cx, filename.ptr())))
  3858                 return false;
  3859             args.rval().setObject(*obj);
  3860             return true;
  3864     if (!(str = FileAsString(cx, filename.ptr())))
  3865         return false;
  3866     args.rval().setString(str);
  3867     return true;
  3870 static bool
  3871 Snarf(JSContext *cx, unsigned argc, jsval *vp)
  3873     return ReadFile(cx, argc, vp, false);
  3876 static bool
  3877 ReadRelativeToScript(JSContext *cx, unsigned argc, jsval *vp)
  3879     return ReadFile(cx, argc, vp, true);
  3882 static bool
  3883 redirect(JSContext *cx, FILE* fp, HandleString relFilename)
  3885     RootedString filename(cx, ResolvePath(cx, relFilename, RootRelative));
  3886     if (!filename)
  3887         return false;
  3888     JSAutoByteString filenameABS(cx, filename);
  3889     if (!filenameABS)
  3890         return false;
  3891     if (freopen(filenameABS.ptr(), "wb", fp) == nullptr) {
  3892         JS_ReportError(cx, "cannot redirect to %s: %s", filenameABS.ptr(), strerror(errno));
  3893         return false;
  3895     return true;
  3898 static bool
  3899 RedirectOutput(JSContext *cx, unsigned argc, jsval *vp)
  3901     CallArgs args = CallArgsFromVp(argc, vp);
  3903     if (args.length() < 1 || args.length() > 2) {
  3904         JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS, "redirect");
  3905         return false;
  3908     if (args[0].isString()) {
  3909         RootedString stdoutPath(cx, args[0].toString());
  3910         if (!stdoutPath)
  3911             return false;
  3912         if (!redirect(cx, stdout, stdoutPath))
  3913             return false;
  3916     if (args.length() > 1 && args[1].isString()) {
  3917         RootedString stderrPath(cx, args[1].toString());
  3918         if (!stderrPath)
  3919             return false;
  3920         if (!redirect(cx, stderr, stderrPath))
  3921             return false;
  3924     args.rval().setUndefined();
  3925     return true;
  3928 static bool
  3929 System(JSContext *cx, unsigned argc, jsval *vp)
  3931     CallArgs args = CallArgsFromVp(argc, vp);
  3933     if (args.length() == 0) {
  3934         JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS,
  3935                              "system");
  3936         return false;
  3939     JSString *str = JS::ToString(cx, args[0]);
  3940     if (!str)
  3941         return false;
  3943     JSAutoByteString command(cx, str);
  3944     if (!command)
  3945         return false;
  3947     int result = system(command.ptr());
  3948     args.rval().setInt32(result);
  3949     return true;
  3952 static int sArgc;
  3953 static char **sArgv;
  3955 class AutoCStringVector
  3957     Vector<char *> argv_;
  3958   public:
  3959     AutoCStringVector(JSContext *cx) : argv_(cx) {}
  3960     ~AutoCStringVector() {
  3961         for (size_t i = 0; i < argv_.length(); i++)
  3962             js_free(argv_[i]);
  3964     bool append(char *arg) {
  3965         if (!argv_.append(arg)) {
  3966             js_free(arg);
  3967             return false;
  3969         return true;
  3971     char* const* get() const {
  3972         return argv_.begin();
  3974     size_t length() const {
  3975         return argv_.length();
  3977     char *operator[](size_t i) const {
  3978         return argv_[i];
  3980     void replace(size_t i, char *arg) {
  3981         js_free(argv_[i]);
  3982         argv_[i] = arg;
  3984     char *back() const {
  3985         return argv_.back();
  3987     void replaceBack(char *arg) {
  3988         js_free(argv_.back());
  3989         argv_.back() = arg;
  3991 };
  3993 #if defined(XP_WIN)
  3994 static bool
  3995 EscapeForShell(AutoCStringVector &argv)
  3997     // Windows will break arguments in argv by various spaces, so we wrap each
  3998     // argument in quotes and escape quotes within. Even with quotes, \ will be
  3999     // treated like an escape character, so inflate each \ to \\.
  4001     for (size_t i = 0; i < argv.length(); i++) {
  4002         if (!argv[i])
  4003             continue;
  4005         size_t newLen = 3;  // quotes before and after and null-terminator
  4006         for (char *p = argv[i]; *p; p++) {
  4007             newLen++;
  4008             if (*p == '\"' || *p == '\\')
  4009                 newLen++;
  4012         char *escaped = (char *)js_malloc(newLen);
  4013         if (!escaped)
  4014             return false;
  4016         char *src = argv[i];
  4017         char *dst = escaped;
  4018         *dst++ = '\"';
  4019         while (*src) {
  4020             if (*src == '\"' || *src == '\\')
  4021                 *dst++ = '\\';
  4022             *dst++ = *src++;
  4024         *dst++ = '\"';
  4025         *dst++ = '\0';
  4026         JS_ASSERT(escaped + newLen == dst);
  4028         argv.replace(i, escaped);
  4030     return true;
  4032 #endif
  4034 static Vector<const char*, 4, js::SystemAllocPolicy> sPropagatedFlags;
  4036 #ifdef DEBUG
  4037 #if (defined(JS_CPU_X86) || defined(JS_CPU_X64)) && defined(JS_ION)
  4038 static bool
  4039 PropagateFlagToNestedShells(const char *flag)
  4041     return sPropagatedFlags.append(flag);
  4043 #endif
  4044 #endif
  4046 static bool
  4047 NestedShell(JSContext *cx, unsigned argc, jsval *vp)
  4049     CallArgs args = CallArgsFromVp(argc, vp);
  4051     AutoCStringVector argv(cx);
  4053     // The first argument to the shell is its path, which we assume is our own
  4054     // argv[0].
  4055     if (sArgc < 1) {
  4056         JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_NESTED_FAIL);
  4057         return false;
  4059     if (!argv.append(strdup(sArgv[0])))
  4060         return false;
  4062     // Propagate selected flags from the current shell
  4063     for (unsigned i = 0; i < sPropagatedFlags.length(); i++) {
  4064         char *cstr = strdup(sPropagatedFlags[i]);
  4065         if (!cstr || !argv.append(cstr))
  4066             return false;
  4069     // The arguments to nestedShell are stringified and append to argv.
  4070     RootedString str(cx);
  4071     for (unsigned i = 0; i < args.length(); i++) {
  4072         str = ToString(cx, args[i]);
  4073         if (!str || !argv.append(JS_EncodeString(cx, str)))
  4074             return false;
  4076         // As a special case, if the caller passes "--js-cache", replace that
  4077         // with "--js-cache=$(jsCacheDir)"
  4078         if (!strcmp(argv.back(), "--js-cache") && jsCacheDir) {
  4079             char *newArg = JS_smprintf("--js-cache=%s", jsCacheDir);
  4080             if (!newArg)
  4081                 return false;
  4082             argv.replaceBack(newArg);
  4086     // execv assumes argv is null-terminated
  4087     if (!argv.append(nullptr))
  4088         return false;
  4090     int status = 0;
  4091 #if defined(XP_WIN)
  4092     if (!EscapeForShell(argv))
  4093         return false;
  4094     status = _spawnv(_P_WAIT, sArgv[0], argv.get());
  4095 #else
  4096     pid_t pid = fork();
  4097     switch (pid) {
  4098       case -1:
  4099         JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_NESTED_FAIL);
  4100         return false;
  4101       case 0:
  4102         (void)execv(sArgv[0], argv.get());
  4103         exit(-1);
  4104       default: {
  4105         while (waitpid(pid, &status, 0) < 0 && errno == EINTR)
  4106             continue;
  4107         break;
  4110 #endif
  4112     if (status != 0) {
  4113         JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_NESTED_FAIL);
  4114         return false;
  4117     args.rval().setUndefined();
  4118     return true;
  4121 static bool
  4122 DecompileFunctionSomehow(JSContext *cx, unsigned argc, Value *vp,
  4123                          JSString *(*decompiler)(JSContext *, HandleFunction, unsigned))
  4125     CallArgs args = CallArgsFromVp(argc, vp);
  4126     if (args.length() < 1 || !args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
  4127         args.rval().setUndefined();
  4128         return true;
  4130     RootedFunction fun(cx, &args[0].toObject().as<JSFunction>());
  4131     JSString *result = decompiler(cx, fun, 0);
  4132     if (!result)
  4133         return false;
  4134     args.rval().setString(result);
  4135     return true;
  4138 static bool
  4139 DecompileBody(JSContext *cx, unsigned argc, Value *vp)
  4141     return DecompileFunctionSomehow(cx, argc, vp, JS_DecompileFunctionBody);
  4144 static bool
  4145 DecompileFunction(JSContext *cx, unsigned argc, Value *vp)
  4147     return DecompileFunctionSomehow(cx, argc, vp, JS_DecompileFunction);
  4150 static bool
  4151 DecompileThisScript(JSContext *cx, unsigned argc, Value *vp)
  4153     CallArgs args = CallArgsFromVp(argc, vp);
  4155     NonBuiltinScriptFrameIter iter(cx);
  4156     if (iter.done()) {
  4157         args.rval().setString(cx->runtime()->emptyString);
  4158         return true;
  4162         JSAutoCompartment ac(cx, iter.script());
  4164         RootedScript script(cx, iter.script());
  4165         JSString *result = JS_DecompileScript(cx, script, "test", 0);
  4166         if (!result)
  4167             return false;
  4169         args.rval().setString(result);
  4172     return JS_WrapValue(cx, args.rval());
  4175 static bool
  4176 ThisFilename(JSContext *cx, unsigned argc, Value *vp)
  4178     CallArgs args = CallArgsFromVp(argc, vp);
  4180     JS::AutoFilename filename;
  4181     if (!DescribeScriptedCaller(cx, &filename) || !filename.get()) {
  4182         args.rval().setString(cx->runtime()->emptyString);
  4183         return true;
  4186     JSString *str = JS_NewStringCopyZ(cx, filename.get());
  4187     if (!str)
  4188         return false;
  4190     args.rval().setString(str);
  4191     return true;
  4194 static bool
  4195 Wrap(JSContext *cx, unsigned argc, jsval *vp)
  4197     CallArgs args = CallArgsFromVp(argc, vp);
  4198     Value v = args.get(0);
  4199     if (JSVAL_IS_PRIMITIVE(v)) {
  4200         args.rval().set(v);
  4201         return true;
  4204     RootedObject obj(cx, JSVAL_TO_OBJECT(v));
  4205     JSObject *wrapped = Wrapper::New(cx, obj, &obj->global(),
  4206                                      &Wrapper::singleton);
  4207     if (!wrapped)
  4208         return false;
  4210     args.rval().setObject(*wrapped);
  4211     return true;
  4214 static bool
  4215 WrapWithProto(JSContext *cx, unsigned argc, jsval *vp)
  4217     CallArgs args = CallArgsFromVp(argc, vp);
  4218     Value obj = UndefinedValue(), proto = UndefinedValue();
  4219     if (args.length() == 2) {
  4220         obj = args[0];
  4221         proto = args[1];
  4223     if (!obj.isObject() || !proto.isObjectOrNull()) {
  4224         JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS,
  4225                              "wrapWithProto");
  4226         return false;
  4229     WrapperOptions options(cx);
  4230     options.setProto(proto.toObjectOrNull());
  4231     options.selectDefaultClass(obj.toObject().isCallable());
  4232     JSObject *wrapped = Wrapper::New(cx, &obj.toObject(), &obj.toObject().global(),
  4233                                      &Wrapper::singletonWithPrototype, &options);
  4234     if (!wrapped)
  4235         return false;
  4237     args.rval().setObject(*wrapped);
  4238     return true;
  4241 static bool
  4242 NewGlobal(JSContext *cx, unsigned argc, jsval *vp)
  4244     JSPrincipals *principals = nullptr;
  4245     JS::CompartmentOptions options;
  4246     options.setVersion(JSVERSION_LATEST);
  4248     CallArgs args = CallArgsFromVp(argc, vp);
  4249     if (args.length() == 1 && args[0].isObject()) {
  4250         RootedObject opts(cx, &args[0].toObject());
  4251         RootedValue v(cx);
  4253         if (!JS_GetProperty(cx, opts, "sameZoneAs", &v))
  4254             return false;
  4255         if (v.isObject())
  4256             options.setSameZoneAs(UncheckedUnwrap(&v.toObject()));
  4258         if (!JS_GetProperty(cx, opts, "invisibleToDebugger", &v))
  4259             return false;
  4260         if (v.isBoolean())
  4261             options.setInvisibleToDebugger(v.toBoolean());
  4263         if (!JS_GetProperty(cx, opts, "principal", &v))
  4264             return false;
  4265         if (!v.isUndefined()) {
  4266             uint32_t bits;
  4267             if (!ToUint32(cx, v, &bits))
  4268                 return false;
  4269             principals = cx->new_<ShellPrincipals>(bits);
  4270             if (!principals)
  4271                 return false;
  4272             JS_HoldPrincipals(principals);
  4276     RootedObject global(cx, NewGlobalObject(cx, options, principals));
  4277     if (principals)
  4278         JS_DropPrincipals(cx->runtime(), principals);
  4279     if (!global)
  4280         return false;
  4282     if (!JS_WrapObject(cx, &global))
  4283         return false;
  4285     args.rval().setObject(*global);
  4286     return true;
  4289 static bool
  4290 EnableStackWalkingAssertion(JSContext *cx, unsigned argc, jsval *vp)
  4292     CallArgs args = CallArgsFromVp(argc, vp);
  4293     if (!args.get(0).isBoolean()) {
  4294         JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS,
  4295                              "enableStackWalkingAssertion");
  4296         return false;
  4299 #ifdef DEBUG
  4300     cx->stackIterAssertionEnabled = args[0].toBoolean();
  4301 #endif
  4303     args.rval().setUndefined();
  4304     return true;
  4307 static bool
  4308 GetMaxArgs(JSContext *cx, unsigned argc, jsval *vp)
  4310     CallArgs args = CallArgsFromVp(argc, vp);
  4311     args.rval().setInt32(ARGS_LENGTH_MAX);
  4312     return true;
  4315 static bool
  4316 ObjectEmulatingUndefined(JSContext *cx, unsigned argc, jsval *vp)
  4318     CallArgs args = CallArgsFromVp(argc, vp);
  4320     static const JSClass cls = {
  4321         "ObjectEmulatingUndefined",
  4322         JSCLASS_EMULATES_UNDEFINED,
  4323         JS_PropertyStub,
  4324         JS_DeletePropertyStub,
  4325         JS_PropertyStub,
  4326         JS_StrictPropertyStub,
  4327         JS_EnumerateStub,
  4328         JS_ResolveStub,
  4329         JS_ConvertStub
  4330     };
  4332     RootedObject obj(cx, JS_NewObject(cx, &cls, JS::NullPtr(), JS::NullPtr()));
  4333     if (!obj)
  4334         return false;
  4335     args.rval().setObject(*obj);
  4336     return true;
  4339 static bool
  4340 GetSelfHostedValue(JSContext *cx, unsigned argc, jsval *vp)
  4342     CallArgs args = CallArgsFromVp(argc, vp);
  4344     if (args.length() != 1 || !args[0].isString()) {
  4345         JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS,
  4346                              "getSelfHostedValue");
  4347         return false;
  4349     RootedAtom srcAtom(cx, ToAtom<CanGC>(cx, args[0]));
  4350     if (!srcAtom)
  4351         return false;
  4352     RootedPropertyName srcName(cx, srcAtom->asPropertyName());
  4353     return cx->runtime()->cloneSelfHostedValue(cx, srcName, args.rval());
  4356 class ShellSourceHook: public SourceHook {
  4357     // The function we should call to lazily retrieve source code.
  4358     PersistentRootedFunction fun;
  4360   public:
  4361     ShellSourceHook(JSContext *cx, JSFunction &fun) : fun(cx, &fun) {}
  4363     bool load(JSContext *cx, const char *filename, jschar **src, size_t *length) {
  4364         RootedString str(cx, JS_NewStringCopyZ(cx, filename));
  4365         if (!str)
  4366             return false;
  4367         RootedValue filenameValue(cx, StringValue(str));
  4369         RootedValue result(cx);
  4370         if (!Call(cx, UndefinedHandleValue, fun, filenameValue, &result))
  4371             return false;
  4373         str = JS::ToString(cx, result);
  4374         if (!str)
  4375             return false;
  4377         *length = JS_GetStringLength(str);
  4378         *src = cx->pod_malloc<jschar>(*length);
  4379         if (!*src)
  4380             return false;
  4382         const jschar *chars = JS_GetStringCharsZ(cx, str);
  4383         if (!chars)
  4384             return false;
  4386         PodCopy(*src, chars, *length);
  4387         return true;
  4389 };
  4391 static bool
  4392 WithSourceHook(JSContext *cx, unsigned argc, jsval *vp)
  4394     CallArgs args = CallArgsFromVp(argc, vp);
  4395     RootedObject callee(cx, &args.callee());
  4397     if (args.length() != 2) {
  4398         ReportUsageError(cx, callee, "Wrong number of arguments.");
  4399         return false;
  4402     if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()
  4403         || !args[1].isObject() || !args[1].toObject().is<JSFunction>()) {
  4404         ReportUsageError(cx, callee, "First and second arguments must be functions.");
  4405         return false;
  4408     ShellSourceHook *hook = new ShellSourceHook(cx, args[0].toObject().as<JSFunction>());
  4409     if (!hook)
  4410         return false;
  4412     SourceHook *savedHook = js::ForgetSourceHook(cx->runtime());
  4413     js::SetSourceHook(cx->runtime(), hook);
  4414     RootedObject fun(cx, &args[1].toObject());
  4415     bool result = Call(cx, UndefinedHandleValue, fun, JS::HandleValueArray::empty(), args.rval());
  4416     js::SetSourceHook(cx->runtime(), savedHook);
  4417     return result;
  4420 static bool
  4421 IsCachingEnabled(JSContext *cx, unsigned argc, Value *vp)
  4423     CallArgs args = CallArgsFromVp(argc, vp);
  4424     args.rval().setBoolean(jsCachingEnabled && jsCacheAsmJSPath != nullptr);
  4425     return true;
  4428 static bool
  4429 SetCachingEnabled(JSContext *cx, unsigned argc, Value *vp)
  4431     CallArgs args = CallArgsFromVp(argc, vp);
  4432     jsCachingEnabled = ToBoolean(args.get(0));
  4433     args.rval().setUndefined();
  4434     return true;
  4437 static void
  4438 PrintProfilerEvents_Callback(const char *msg)
  4440     fprintf(stderr, "PROFILER EVENT: %s\n", msg);
  4443 static bool
  4444 PrintProfilerEvents(JSContext *cx, unsigned argc, Value *vp)
  4446     CallArgs args = CallArgsFromVp(argc, vp);
  4447     if (cx->runtime()->spsProfiler.enabled())
  4448         js::RegisterRuntimeProfilingEventMarker(cx->runtime(), &PrintProfilerEvents_Callback);
  4449     args.rval().setUndefined();
  4450     return true;
  4453 static const JSFunctionSpecWithHelp shell_functions[] = {
  4454     JS_FN_HELP("version", Version, 0, 0,
  4455 "version([number])",
  4456 "  Get or force a script compilation version number."),
  4458     JS_FN_HELP("options", Options, 0, 0,
  4459 "options([option ...])",
  4460 "  Get or toggle JavaScript options."),
  4462     JS_FN_HELP("load", Load, 1, 0,
  4463 "load(['foo.js' ...])",
  4464 "  Load files named by string arguments. Filename is relative to the\n"
  4465 "      current working directory."),
  4467     JS_FN_HELP("loadRelativeToScript", LoadScriptRelativeToScript, 1, 0,
  4468 "loadRelativeToScript(['foo.js' ...])",
  4469 "  Load files named by string arguments. Filename is relative to the\n"
  4470 "      calling script."),
  4472     JS_FN_HELP("evaluate", Evaluate, 2, 0,
  4473 "evaluate(code[, options])",
  4474 "  Evaluate code as though it were the contents of a file.\n"
  4475 "  options is an optional object that may have these properties:\n"
  4476 "      compileAndGo: use the compile-and-go compiler option (default: true)\n"
  4477 "      noScriptRval: use the no-script-rval compiler option (default: false)\n"
  4478 "      fileName: filename for error messages and debug info\n"
  4479 "      lineNumber: starting line number for error messages and debug info\n"
  4480 "      global: global in which to execute the code\n"
  4481 "      newContext: if true, create and use a new cx (default: false)\n"
  4482 "      saveFrameChain: if true, save the frame chain before evaluating code\n"
  4483 "         and restore it afterwards\n"
  4484 "      catchTermination: if true, catch termination (failure without\n"
  4485 "         an exception value, as for slow scripts or out-of-memory)\n"
  4486 "         and return 'terminated'\n"
  4487 "      element: if present with value |v|, convert |v| to an object |o| and\n"
  4488 "         mark the source as being attached to the DOM element |o|. If the\n"
  4489 "         property is omitted or |v| is null, don't attribute the source to\n"
  4490 "         any DOM element.\n"
  4491 "      elementAttributeName: if present and not undefined, the name of\n"
  4492 "         property of 'element' that holds this code. This is what\n"
  4493 "         Debugger.Source.prototype.elementAttributeName returns.\n"
  4494 "      sourceMapURL: if present with value |v|, convert |v| to a string, and\n"
  4495 "         provide that as the code's source map URL. If omitted, attach no\n"
  4496 "         source map URL to the code (although the code may provide one itself,\n"
  4497 "         via a //#sourceMappingURL comment).\n"
  4498 "      sourceIsLazy: if present and true, indicates that, after compilation, \n"
  4499           "script source should not be cached by the JS engine and should be \n"
  4500           "lazily loaded from the embedding as-needed.\n"
  4501 "      loadBytecode: if true, and if the source is a CacheEntryObject,\n"
  4502 "         the bytecode would be loaded and decoded from the cache entry instead\n"
  4503 "         of being parsed, then it would be executed as usual.\n"
  4504 "      saveBytecode: if true, and if the source is a CacheEntryObject,\n"
  4505 "         the bytecode would be encoded and saved into the cache entry after\n"
  4506 "         the script execution.\n"
  4507 "      assertEqBytecode: if true, and if both loadBytecode and saveBytecode are \n"
  4508 "         true, then the loaded bytecode and the encoded bytecode are compared.\n"
  4509 "         and an assertion is raised if they differ.\n"
  4510 ),
  4512     JS_FN_HELP("run", Run, 1, 0,
  4513 "run('foo.js')",
  4514 "  Run the file named by the first argument, returning the number of\n"
  4515 "  of milliseconds spent compiling and executing it."),
  4517     JS_FN_HELP("readline", ReadLine, 0, 0,
  4518 "readline()",
  4519 "  Read a single line from stdin."),
  4521     JS_FN_HELP("print", Print, 0, 0,
  4522 "print([exp ...])",
  4523 "  Evaluate and print expressions to stdout."),
  4525     JS_FN_HELP("printErr", PrintErr, 0, 0,
  4526 "printErr([exp ...])",
  4527 "  Evaluate and print expressions to stderr."),
  4529     JS_FN_HELP("putstr", PutStr, 0, 0,
  4530 "putstr([exp])",
  4531 "  Evaluate and print expression without newline."),
  4533     JS_FN_HELP("dateNow", Now, 0, 0,
  4534 "dateNow()",
  4535 "  Return the current time with sub-ms precision."),
  4537     JS_FN_HELP("help", Help, 0, 0,
  4538 "help([name ...])",
  4539 "  Display usage and help messages."),
  4541     JS_FN_HELP("quit", Quit, 0, 0,
  4542 "quit()",
  4543 "  Quit the shell."),
  4545     JS_FN_HELP("assertEq", AssertEq, 2, 0,
  4546 "assertEq(actual, expected[, msg])",
  4547 "  Throw if the first two arguments are not the same (both +0 or both -0,\n"
  4548 "  both NaN, or non-zero and ===)."),
  4550     JS_FN_HELP("setDebug", SetDebug, 1, 0,
  4551 "setDebug(debug)",
  4552 "  Set debug mode."),
  4554     JS_FN_HELP("setDebuggerHandler", SetDebuggerHandler, 1, 0,
  4555 "setDebuggerHandler(f)",
  4556 "  Set handler for debugger keyword to f."),
  4558     JS_FN_HELP("throwError", ThrowError, 0, 0,
  4559 "throwError()",
  4560 "  Throw an error from JS_ReportError."),
  4562 #ifdef DEBUG
  4563     JS_FN_HELP("disassemble", DisassembleToString, 1, 0,
  4564 "disassemble([fun])",
  4565 "  Return the disassembly for the given function."),
  4567     JS_FN_HELP("dis", Disassemble, 1, 0,
  4568 "dis([fun])",
  4569 "  Disassemble functions into bytecodes."),
  4571     JS_FN_HELP("disfile", DisassFile, 1, 0,
  4572 "disfile('foo.js')",
  4573 "  Disassemble script file into bytecodes.\n"
  4574 "  dis and disfile take these options as preceeding string arguments:\n"
  4575 "    \"-r\" (disassemble recursively)\n"
  4576 "    \"-l\" (show line numbers)"),
  4578     JS_FN_HELP("dissrc", DisassWithSrc, 1, 0,
  4579 "dissrc([fun])",
  4580 "  Disassemble functions with source lines."),
  4582     JS_FN_HELP("dumpObject", DumpObject, 1, 0,
  4583 "dumpObject()",
  4584 "  Dump an internal representation of an object."),
  4586     JS_FN_HELP("notes", Notes, 1, 0,
  4587 "notes([fun])",
  4588 "  Show source notes for functions."),
  4590     JS_FN_HELP("findReferences", FindReferences, 1, 0,
  4591 "findReferences(target)",
  4592 "  Walk the entire heap, looking for references to |target|, and return a\n"
  4593 "  \"references object\" describing what we found.\n"
  4594 "\n"
  4595 "  Each property of the references object describes one kind of reference. The\n"
  4596 "  property's name is the label supplied to MarkObject, JS_CALL_TRACER, or what\n"
  4597 "  have you, prefixed with \"edge: \" to avoid collisions with system properties\n"
  4598 "  (like \"toString\" and \"__proto__\"). The property's value is an array of things\n"
  4599 "  that refer to |thing| via that kind of reference. Ordinary references from\n"
  4600 "  one object to another are named after the property name (with the \"edge: \"\n"
  4601 "  prefix).\n"
  4602 "\n"
  4603 "  Garbage collection roots appear as references from 'null'. We use the name\n"
  4604 "  given to the root (with the \"edge: \" prefix) as the name of the reference.\n"
  4605 "\n"
  4606 "  Note that the references object does record references from objects that are\n"
  4607 "  only reachable via |thing| itself, not just the references reachable\n"
  4608 "  themselves from roots that keep |thing| from being collected. (We could make\n"
  4609 "  this distinction if it is useful.)\n"
  4610 "\n"
  4611 "  If any references are found by the conservative scanner, the references\n"
  4612 "  object will have a property named \"edge: machine stack\"; the referrers will\n"
  4613 "  be 'null', because they are roots."),
  4615 #endif
  4616     JS_FN_HELP("build", BuildDate, 0, 0,
  4617 "build()",
  4618 "  Show build date and time."),
  4620     JS_FN_HELP("intern", Intern, 1, 0,
  4621 "intern(str)",
  4622 "  Internalize str in the atom table."),
  4624     JS_FN_HELP("getpda", GetPDA, 1, 0,
  4625 "getpda(obj)",
  4626 "  Get the property descriptors for obj."),
  4628     JS_FN_HELP("getslx", GetSLX, 1, 0,
  4629 "getslx(obj)",
  4630 "  Get script line extent."),
  4632     JS_FN_HELP("evalcx", EvalInContext, 1, 0,
  4633 "evalcx(s[, o])",
  4634 "  Evaluate s in optional sandbox object o.\n"
  4635 "  if (s == '' && !o) return new o with eager standard classes\n"
  4636 "  if (s == 'lazy' && !o) return new o with lazy standard classes"),
  4638     JS_FN_HELP("evalInFrame", EvalInFrame, 2, 0,
  4639 "evalInFrame(n,str,save)",
  4640 "  Evaluate 'str' in the nth up frame.\n"
  4641 "  If 'save' (default false), save the frame chain."),
  4643 #ifdef JS_THREADSAFE
  4644     JS_FN_HELP("evalInWorker", EvalInWorker, 1, 0,
  4645 "evalInWorker(str)",
  4646 "  Evaluate 'str' in a separate thread with its own runtime.\n"),
  4647 #endif
  4649     JS_FN_HELP("shapeOf", ShapeOf, 1, 0,
  4650 "shapeOf(obj)",
  4651 "  Get the shape of obj (an implementation detail)."),
  4653     JS_FN_HELP("resolver", Resolver, 1, 0,
  4654 "resolver(src[, proto])",
  4655 "  Create object with resolve hook that copies properties\n"
  4656 "  from src. If proto is omitted, use Object.prototype."),
  4658 #ifdef DEBUG
  4659     JS_FN_HELP("arrayInfo", js_ArrayInfo, 1, 0,
  4660 "arrayInfo(a1, a2, ...)",
  4661 "  Report statistics about arrays."),
  4662 #endif
  4664 #ifdef JS_THREADSAFE
  4665     JS_FN_HELP("sleep", Sleep_fn, 1, 0,
  4666 "sleep(dt)",
  4667 "  Sleep for dt seconds."),
  4668 #endif
  4670     JS_FN_HELP("snarf", Snarf, 1, 0,
  4671 "snarf(filename, [\"binary\"])",
  4672 "  Read filename into returned string. Filename is relative to the current\n"
  4673                "  working directory."),
  4675     JS_FN_HELP("read", Snarf, 1, 0,
  4676 "read(filename, [\"binary\"])",
  4677 "  Synonym for snarf."),
  4679     JS_FN_HELP("readRelativeToScript", ReadRelativeToScript, 1, 0,
  4680 "readRelativeToScript(filename, [\"binary\"])",
  4681 "  Read filename into returned string. Filename is relative to the directory\n"
  4682 "  containing the current script."),
  4684     JS_FN_HELP("compile", Compile, 1, 0,
  4685 "compile(code)",
  4686 "  Compiles a string to bytecode, potentially throwing."),
  4688     JS_FN_HELP("parse", Parse, 1, 0,
  4689 "parse(code)",
  4690 "  Parses a string, potentially throwing."),
  4692     JS_FN_HELP("syntaxParse", SyntaxParse, 1, 0,
  4693 "syntaxParse(code)",
  4694 "  Check the syntax of a string, returning success value"),
  4696 #ifdef JS_THREADSAFE
  4697     JS_FN_HELP("offThreadCompileScript", OffThreadCompileScript, 1, 0,
  4698 "offThreadCompileScript(code[, options])",
  4699 "  Compile |code| on a helper thread. To wait for the compilation to finish\n"
  4700 "  and run the code, call |runOffThreadScript|. If present, |options| may\n"
  4701 "  have properties saying how the code should be compiled:\n"
  4702 "      noScriptRval: use the no-script-rval compiler option (default: false)\n"
  4703 "      fileName: filename for error messages and debug info\n"
  4704 "      lineNumber: starting line number for error messages and debug info\n"
  4705 "      element: if present with value |v|, convert |v| to an object |o| and\n"
  4706 "         mark the source as being attached to the DOM element |o|. If the\n"
  4707 "         property is omitted or |v| is null, don't attribute the source to\n"
  4708 "         any DOM element.\n"
  4709 "      elementAttributeName: if present and not undefined, the name of\n"
  4710 "         property of 'element' that holds this code. This is what\n"
  4711 "         Debugger.Source.prototype.elementAttributeName returns.\n"),
  4713     JS_FN_HELP("runOffThreadScript", runOffThreadScript, 0, 0,
  4714 "runOffThreadScript()",
  4715 "  Wait for off-thread compilation to complete. If an error occurred,\n"
  4716 "  throw the appropriate exception; otherwise, run the script and return\n"
  4717 "  its value."),
  4719 #endif
  4721     JS_FN_HELP("timeout", Timeout, 1, 0,
  4722 "timeout([seconds], [func])",
  4723 "  Get/Set the limit in seconds for the execution time for the current context.\n"
  4724 "  A negative value (default) means that the execution time is unlimited.\n"
  4725 "  If a second argument is provided, it will be invoked when the timer elapses.\n"
  4726 "  Calling this function will replace any callback set by |setInterruptCallback|.\n"),
  4728     JS_FN_HELP("interruptIf", InterruptIf, 1, 0,
  4729 "interruptIf(cond)",
  4730 "  Requests interrupt callback if cond is true. If a callback function is set via\n"
  4731 "  |timeout| or |setInterruptCallback|, it will be called. No-op otherwise."),
  4733     JS_FN_HELP("setInterruptCallback", SetInterruptCallback, 1, 0,
  4734 "setInterruptCallback(func)",
  4735 "  Sets func as the interrupt callback function.\n"
  4736 "  Calling this function will replace any callback set by |timeout|.\n"),
  4738     JS_FN_HELP("elapsed", Elapsed, 0, 0,
  4739 "elapsed()",
  4740 "  Execution time elapsed for the current context."),
  4742     JS_FN_HELP("decompileFunction", DecompileFunction, 1, 0,
  4743 "decompileFunction(func)",
  4744 "  Decompile a function."),
  4746     JS_FN_HELP("decompileBody", DecompileBody, 1, 0,
  4747 "decompileBody(func)",
  4748 "  Decompile a function's body."),
  4750     JS_FN_HELP("decompileThis", DecompileThisScript, 0, 0,
  4751 "decompileThis()",
  4752 "  Decompile the currently executing script."),
  4754     JS_FN_HELP("thisFilename", ThisFilename, 0, 0,
  4755 "thisFilename()",
  4756 "  Return the filename of the current script"),
  4758     JS_FN_HELP("wrap", Wrap, 1, 0,
  4759 "wrap(obj)",
  4760 "  Wrap an object into a noop wrapper."),
  4762     JS_FN_HELP("wrapWithProto", WrapWithProto, 2, 0,
  4763 "wrapWithProto(obj)",
  4764 "  Wrap an object into a noop wrapper with prototype semantics."),
  4766     JS_FN_HELP("newGlobal", NewGlobal, 1, 0,
  4767 "newGlobal([options])",
  4768 "  Return a new global object in a new compartment. If options\n"
  4769 "  is given, it may have any of the following properties:\n"
  4770 "      sameZoneAs: the compartment will be in the same zone as the given object (defaults to a new zone)\n"
  4771 "      invisibleToDebugger: the global will be invisible to the debugger (default false)\n"
  4772 "      principal: if present, its value converted to a number must be an\n"
  4773 "         integer that fits in 32 bits; use that as the new compartment's\n"
  4774 "         principal. Shell principals are toys, meant only for testing; one\n"
  4775 "         shell principal subsumes another if its set bits are a superset of\n"
  4776 "         the other's. Thus, a principal of 0 subsumes nothing, while a\n"
  4777 "         principals of ~0 subsumes all other principals. The absence of a\n"
  4778 "         principal is treated as if its bits were 0xffff, for subsumption\n"
  4779 "         purposes. If this property is omitted, supply no principal."),
  4781     JS_FN_HELP("createMappedArrayBuffer", CreateMappedArrayBuffer, 1, 0,
  4782 "createMappedArrayBuffer(filename, [offset, [size]])",
  4783 "  Create an array buffer that mmaps the given file."),
  4785     JS_FN_HELP("enableStackWalkingAssertion", EnableStackWalkingAssertion, 1, 0,
  4786 "enableStackWalkingAssertion(enabled)",
  4787 "  Enables or disables a particularly expensive assertion in stack-walking\n"
  4788 "  code.  If your test isn't ridiculously thorough, such that performing this\n"
  4789 "  assertion increases test duration by an order of magnitude, you shouldn't\n"
  4790 "  use this."),
  4792     JS_FN_HELP("getMaxArgs", GetMaxArgs, 0, 0,
  4793 "getMaxArgs()",
  4794 "  Return the maximum number of supported args for a call."),
  4796     JS_FN_HELP("objectEmulatingUndefined", ObjectEmulatingUndefined, 0, 0,
  4797 "objectEmulatingUndefined()",
  4798 "  Return a new object obj for which typeof obj === \"undefined\", obj == null\n"
  4799 "  and obj == undefined (and vice versa for !=), and ToBoolean(obj) === false.\n"),
  4801     JS_FN_HELP("isCachingEnabled", IsCachingEnabled, 0, 0,
  4802 "isCachingEnabled()",
  4803 "  Return whether JS caching is enabled."),
  4805     JS_FN_HELP("setCachingEnabled", SetCachingEnabled, 1, 0,
  4806 "setCachingEnabled(b)",
  4807 "  Enable or disable JS caching."),
  4809     JS_FN_HELP("cacheEntry", CacheEntry, 1, 0,
  4810 "cacheEntry(code)",
  4811 "  Return a new opaque object which emulates a cache entry of a script.  This\n"
  4812 "  object encapsulates the code and its cached content. The cache entry is filled\n"
  4813 "  and read by the \"evaluate\" function by using it in place of the source, and\n"
  4814 "  by setting \"saveBytecode\" and \"loadBytecode\" options."),
  4816     JS_FN_HELP("printProfilerEvents", PrintProfilerEvents, 0, 0,
  4817 "printProfilerEvents()",
  4818 "  Register a callback with the profiler that prints javascript profiler events\n"
  4819 "  to stderr.  Callback is only registered if profiling is enabled."),
  4821     JS_FS_HELP_END
  4822 };
  4824 static const JSFunctionSpecWithHelp fuzzing_unsafe_functions[] = {
  4825     JS_FN_HELP("clone", Clone, 1, 0,
  4826 "clone(fun[, scope])",
  4827 "  Clone function object."),
  4829     JS_FN_HELP("getSelfHostedValue", GetSelfHostedValue, 1, 0,
  4830 "getSelfHostedValue()",
  4831 "  Get a self-hosted value by its name. Note that these values don't get \n"
  4832 "  cached, so repeatedly getting the same value creates multiple distinct clones."),
  4834 #ifdef DEBUG
  4835     JS_FN_HELP("dumpHeap", DumpHeap, 0, 0,
  4836 "dumpHeap([fileName[, start[, toFind[, maxDepth[, toIgnore]]]]])",
  4837 "  Interface to JS_DumpHeap with output sent to file."),
  4838 #endif
  4840     JS_FN_HELP("parent", Parent, 1, 0,
  4841 "parent(obj)",
  4842 "  Returns the parent of obj."),
  4844     JS_FN_HELP("line2pc", LineToPC, 0, 0,
  4845 "line2pc([fun,] line)",
  4846 "  Map line number to PC."),
  4848     JS_FN_HELP("pc2line", PCToLine, 0, 0,
  4849 "pc2line(fun[, pc])",
  4850 "  Map PC to line number."),
  4852     JS_FN_HELP("redirect", RedirectOutput, 2, 0,
  4853 "redirect(stdoutFilename[, stderrFilename])",
  4854 "  Redirect stdout and/or stderr to the named file. Pass undefined to avoid\n"
  4855 "   redirecting. Filenames are relative to the current working directory."),
  4857     JS_FN_HELP("setThrowHook", SetThrowHook, 1, 0,
  4858 "setThrowHook(f)",
  4859 "  Set throw hook to f."),
  4861     JS_FN_HELP("system", System, 1, 0,
  4862 "system(command)",
  4863 "  Execute command on the current host, returning result code."),
  4865     JS_FN_HELP("nestedShell", NestedShell, 0, 0,
  4866 "nestedShell(shellArgs...)",
  4867 "  Execute the given code in a new JS shell process, passing this nested shell\n"
  4868 "  the arguments passed to nestedShell. argv[0] of the nested shell will be argv[0]\n"
  4869 "  of the current shell (which is assumed to be the actual path to the shell.\n"
  4870 "  arguments[0] (of the call to nestedShell) will be argv[1], arguments[1] will\n"
  4871 "  be argv[2], etc."),
  4873     JS_FN_HELP("trap", Trap, 3, 0,
  4874 "trap([fun, [pc,]] exp)",
  4875 "  Trap bytecode execution."),
  4877     JS_FN_HELP("assertFloat32", testingFunc_assertFloat32, 2, 0,
  4878 "assertFloat32(value, isFloat32)",
  4879 "  In IonMonkey only, asserts that value has (resp. hasn't) the MIRType_Float32 if isFloat32 is true (resp. false)."),
  4881     JS_FN_HELP("untrap", Untrap, 2, 0,
  4882 "untrap(fun[, pc])",
  4883 "  Remove a trap."),
  4885     JS_FN_HELP("withSourceHook", WithSourceHook, 1, 0,
  4886 "withSourceHook(hook, fun)",
  4887 "  Set this JS runtime's lazy source retrieval hook (that is, the hook\n"
  4888 "  used to find sources compiled with |CompileOptions::LAZY_SOURCE|) to\n"
  4889 "  |hook|; call |fun| with no arguments; and then restore the runtime's\n"
  4890 "  original hook. Return or throw whatever |fun| did. |hook| gets\n"
  4891 "  passed the requested code's URL, and should return a string.\n"
  4892 "\n"
  4893 "  Notes:\n"
  4894 "\n"
  4895 "  1) SpiderMonkey may assert if the returned code isn't close enough\n"
  4896 "  to the script's real code, so this function is not fuzzer-safe.\n"
  4897 "\n"
  4898 "  2) The runtime can have only one source retrieval hook active at a\n"
  4899 "  time. If |fun| is not careful, |hook| could be asked to retrieve the\n"
  4900 "  source code for compilations that occurred long before it was set,\n"
  4901 "  and that it knows nothing about. The reverse applies as well: the\n"
  4902 "  original hook, that we reinstate after the call to |fun| completes,\n"
  4903 "  might be asked for the source code of compilations that |fun|\n"
  4904 "  performed, and which, presumably, only |hook| knows how to find.\n"),
  4906     JS_FS_HELP_END
  4907 };
  4909 #ifdef MOZ_PROFILING
  4910 # define PROFILING_FUNCTION_COUNT 5
  4911 # ifdef MOZ_CALLGRIND
  4912 #  define CALLGRIND_FUNCTION_COUNT 3
  4913 # else
  4914 #  define CALLGRIND_FUNCTION_COUNT 0
  4915 # endif
  4916 # ifdef MOZ_VTUNE
  4917 #  define VTUNE_FUNCTION_COUNT 4
  4918 # else
  4919 #  define VTUNE_FUNCTION_COUNT 0
  4920 # endif
  4921 # define EXTERNAL_FUNCTION_COUNT (PROFILING_FUNCTION_COUNT + CALLGRIND_FUNCTION_COUNT + VTUNE_FUNCTION_COUNT)
  4922 #else
  4923 # define EXTERNAL_FUNCTION_COUNT 0
  4924 #endif
  4926 #undef PROFILING_FUNCTION_COUNT
  4927 #undef CALLGRIND_FUNCTION_COUNT
  4928 #undef VTUNE_FUNCTION_COUNT
  4929 #undef EXTERNAL_FUNCTION_COUNT
  4931 static bool
  4932 PrintHelpString(JSContext *cx, jsval v)
  4934     JSString *str = JSVAL_TO_STRING(v);
  4935     JS::Anchor<JSString *> a_str(str);
  4936     const jschar *chars = JS_GetStringCharsZ(cx, str);
  4937     if (!chars)
  4938         return false;
  4940     for (const jschar *p = chars; *p; p++)
  4941         fprintf(gOutFile, "%c", char(*p));
  4943     fprintf(gOutFile, "\n");
  4945     return true;
  4948 static bool
  4949 PrintHelp(JSContext *cx, HandleObject obj)
  4951     RootedValue usage(cx);
  4952     if (!JS_LookupProperty(cx, obj, "usage", &usage))
  4953         return false;
  4954     RootedValue help(cx);
  4955     if (!JS_LookupProperty(cx, obj, "help", &help))
  4956         return false;
  4958     if (JSVAL_IS_VOID(usage) || JSVAL_IS_VOID(help))
  4959         return true;
  4961     return PrintHelpString(cx, usage) && PrintHelpString(cx, help);
  4964 static bool
  4965 Help(JSContext *cx, unsigned argc, jsval *vp)
  4967     CallArgs args = CallArgsFromVp(argc, vp);
  4968     fprintf(gOutFile, "%s\n", JS_GetImplementationVersion());
  4970     RootedObject obj(cx);
  4971     if (args.length() == 0) {
  4972         RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
  4973         AutoIdArray ida(cx, JS_Enumerate(cx, global));
  4974         if (!ida)
  4975             return false;
  4977         for (size_t i = 0; i < ida.length(); i++) {
  4978             RootedValue v(cx);
  4979             RootedId id(cx, ida[i]);
  4980             if (!JS_LookupPropertyById(cx, global, id, &v))
  4981                 return false;
  4982             if (JSVAL_IS_PRIMITIVE(v)) {
  4983                 JS_ReportError(cx, "primitive arg");
  4984                 return false;
  4986             obj = JSVAL_TO_OBJECT(v);
  4987             if (!PrintHelp(cx, obj))
  4988                 return false;
  4990     } else {
  4991         for (unsigned i = 0; i < args.length(); i++) {
  4992             if (args[i].isPrimitive()) {
  4993                 JS_ReportError(cx, "primitive arg");
  4994                 return false;
  4996             obj = args[i].toObjectOrNull();
  4997             if (!PrintHelp(cx, obj))
  4998                 return false;
  5002     args.rval().setUndefined();
  5003     return true;
  5006 static const JSErrorFormatString jsShell_ErrorFormatString[JSShellErr_Limit] = {
  5007 #define MSG_DEF(name, number, count, exception, format) \
  5008     { format, count, JSEXN_ERR } ,
  5009 #include "jsshell.msg"
  5010 #undef MSG_DEF
  5011 };
  5013 static const JSErrorFormatString *
  5014 my_GetErrorMessage(void *userRef, const char *locale, const unsigned errorNumber)
  5016     if (errorNumber == 0 || errorNumber >= JSShellErr_Limit)
  5017         return nullptr;
  5019     return &jsShell_ErrorFormatString[errorNumber];
  5022 static void
  5023 my_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
  5025     gGotError = PrintError(cx, gErrFile, message, report, reportWarnings);
  5026     if (!JSREPORT_IS_WARNING(report->flags)) {
  5027         if (report->errorNumber == JSMSG_OUT_OF_MEMORY) {
  5028             gExitCode = EXITCODE_OUT_OF_MEMORY;
  5029         } else {
  5030             gExitCode = EXITCODE_RUNTIME_ERROR;
  5035 static void
  5036 my_OOMCallback(JSContext *cx)
  5038     // If a script is running, the engine is about to throw the string "out of
  5039     // memory", which may or may not be caught. Otherwise the engine will just
  5040     // unwind and return null/false, with no exception set.
  5041     if (!JS_IsRunning(cx))
  5042         gGotError = true;
  5045 static bool
  5046 global_enumerate(JSContext *cx, HandleObject obj)
  5048 #ifdef LAZY_STANDARD_CLASSES
  5049     return JS_EnumerateStandardClasses(cx, obj);
  5050 #else
  5051     return true;
  5052 #endif
  5055 static bool
  5056 global_resolve(JSContext *cx, HandleObject obj, HandleId id, MutableHandleObject objp)
  5058 #ifdef LAZY_STANDARD_CLASSES
  5059     bool resolved;
  5061     if (!JS_ResolveStandardClass(cx, obj, id, &resolved))
  5062         return false;
  5063     if (resolved) {
  5064         objp.set(obj);
  5065         return true;
  5067 #endif
  5069     return true;
  5072 static const JSClass global_class = {
  5073     "global", JSCLASS_NEW_RESOLVE | JSCLASS_GLOBAL_FLAGS,
  5074     JS_PropertyStub,  JS_DeletePropertyStub,
  5075     JS_PropertyStub,  JS_StrictPropertyStub,
  5076     global_enumerate, (JSResolveOp) global_resolve,
  5077     JS_ConvertStub,   nullptr,
  5078     nullptr, nullptr, nullptr,
  5079     JS_GlobalObjectTraceHook
  5080 };
  5082 static bool
  5083 env_setProperty(JSContext *cx, HandleObject obj, HandleId id, bool strict, MutableHandleValue vp)
  5085 /* XXX porting may be easy, but these don't seem to supply setenv by default */
  5086 #if !defined SOLARIS
  5087     int rv;
  5089     RootedValue idvalue(cx, IdToValue(id));
  5090     RootedString idstring(cx, ToString(cx, idvalue));
  5091     JSAutoByteString idstr;
  5092     if (!idstr.encodeLatin1(cx, idstring))
  5093         return false;
  5095     RootedString value(cx, ToString(cx, vp));
  5096     if (!value)
  5097         return false;
  5098     JSAutoByteString valstr;
  5099     if (!valstr.encodeLatin1(cx, value))
  5100         return false;
  5102 #if defined XP_WIN || defined HPUX || defined OSF1
  5104         char *waste = JS_smprintf("%s=%s", idstr.ptr(), valstr.ptr());
  5105         if (!waste) {
  5106             JS_ReportOutOfMemory(cx);
  5107             return false;
  5109         rv = putenv(waste);
  5110 #ifdef XP_WIN
  5111         /*
  5112          * HPUX9 at least still has the bad old non-copying putenv.
  5114          * Per mail from <s.shanmuganathan@digital.com>, OSF1 also has a putenv
  5115          * that will crash if you pass it an auto char array (so it must place
  5116          * its argument directly in the char *environ[] array).
  5117          */
  5118         JS_smprintf_free(waste);
  5119 #endif
  5121 #else
  5122     rv = setenv(idstr.ptr(), valstr.ptr(), 1);
  5123 #endif
  5124     if (rv < 0) {
  5125         JS_ReportError(cx, "can't set env variable %s to %s", idstr.ptr(), valstr.ptr());
  5126         return false;
  5128     vp.set(StringValue(value));
  5129 #endif /* !defined SOLARIS */
  5130     return true;
  5133 static bool
  5134 env_enumerate(JSContext *cx, HandleObject obj)
  5136     static bool reflected;
  5137     char **evp, *name, *value;
  5138     RootedString valstr(cx);
  5139     bool ok;
  5141     if (reflected)
  5142         return true;
  5144     for (evp = (char **)JS_GetPrivate(obj); (name = *evp) != nullptr; evp++) {
  5145         value = strchr(name, '=');
  5146         if (!value)
  5147             continue;
  5148         *value++ = '\0';
  5149         valstr = JS_NewStringCopyZ(cx, value);
  5150         ok = valstr && JS_DefineProperty(cx, obj, name, valstr, JSPROP_ENUMERATE);
  5151         value[-1] = '=';
  5152         if (!ok)
  5153             return false;
  5156     reflected = true;
  5157     return true;
  5160 static bool
  5161 env_resolve(JSContext *cx, HandleObject obj, HandleId id, MutableHandleObject objp)
  5163     RootedValue idvalue(cx, IdToValue(id));
  5164     RootedString idstring(cx, ToString(cx, idvalue));
  5165     JSAutoByteString idstr;
  5166     if (!idstr.encodeLatin1(cx, idstring))
  5167         return false;
  5169     const char *name = idstr.ptr();
  5170     const char *value = getenv(name);
  5171     if (value) {
  5172         RootedString valstr(cx, JS_NewStringCopyZ(cx, value));
  5173         if (!valstr)
  5174             return false;
  5175         if (!JS_DefineProperty(cx, obj, name, valstr, JSPROP_ENUMERATE))
  5176             return false;
  5177         objp.set(obj);
  5179     return true;
  5182 static const JSClass env_class = {
  5183     "environment", JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE,
  5184     JS_PropertyStub,  JS_DeletePropertyStub,
  5185     JS_PropertyStub,  env_setProperty,
  5186     env_enumerate, (JSResolveOp) env_resolve,
  5187     JS_ConvertStub
  5188 };
  5190 /*
  5191  * Define a FakeDOMObject constructor. It returns an object with a getter,
  5192  * setter and method with attached JitInfo. This object can be used to test
  5193  * IonMonkey DOM optimizations in the shell.
  5194  */
  5195 static uint32_t DOM_OBJECT_SLOT = 0;
  5197 static bool
  5198 dom_genericGetter(JSContext* cx, unsigned argc, JS::Value *vp);
  5200 static bool
  5201 dom_genericSetter(JSContext* cx, unsigned argc, JS::Value *vp);
  5203 static bool
  5204 dom_genericMethod(JSContext *cx, unsigned argc, JS::Value *vp);
  5206 #ifdef DEBUG
  5207 static const JSClass *GetDomClass();
  5208 #endif
  5210 static bool
  5211 dom_get_x(JSContext* cx, HandleObject obj, void *self, JSJitGetterCallArgs args)
  5213     JS_ASSERT(JS_GetClass(obj) == GetDomClass());
  5214     JS_ASSERT(self == (void *)0x1234);
  5215     args.rval().set(JS_NumberValue(double(3.14)));
  5216     return true;
  5219 static bool
  5220 dom_set_x(JSContext* cx, HandleObject obj, void *self, JSJitSetterCallArgs args)
  5222     JS_ASSERT(JS_GetClass(obj) == GetDomClass());
  5223     JS_ASSERT(self == (void *)0x1234);
  5224     return true;
  5227 static bool
  5228 dom_doFoo(JSContext* cx, HandleObject obj, void *self, const JSJitMethodCallArgs& args)
  5230     JS_ASSERT(JS_GetClass(obj) == GetDomClass());
  5231     JS_ASSERT(self == (void *)0x1234);
  5233     /* Just return args.length(). */
  5234     args.rval().setInt32(args.length());
  5235     return true;
  5238 static const JSJitInfo dom_x_getterinfo = {
  5239     { (JSJitGetterOp)dom_get_x },
  5240     0,        /* protoID */
  5241     0,        /* depth */
  5242     JSJitInfo::AliasNone, /* aliasSet */
  5243     JSJitInfo::Getter,
  5244     JSVAL_TYPE_UNKNOWN, /* returnType */
  5245     true,     /* isInfallible. False in setters. */
  5246     true,     /* isMovable */
  5247     false,    /* isInSlot */
  5248     false,    /* isTypedMethod */
  5249     0         /* slotIndex */
  5250 };
  5252 static const JSJitInfo dom_x_setterinfo = {
  5253     { (JSJitGetterOp)dom_set_x },
  5254     0,        /* protoID */
  5255     0,        /* depth */
  5256     JSJitInfo::Setter,
  5257     JSJitInfo::AliasEverything, /* aliasSet */
  5258     JSVAL_TYPE_UNKNOWN, /* returnType */
  5259     false,    /* isInfallible. False in setters. */
  5260     false,    /* isMovable. */
  5261     false,    /* isInSlot */
  5262     false,    /* isTypedMethod */
  5263     0         /* slotIndex */
  5264 };
  5266 static const JSJitInfo doFoo_methodinfo = {
  5267     { (JSJitGetterOp)dom_doFoo },
  5268     0,        /* protoID */
  5269     0,        /* depth */
  5270     JSJitInfo::Method,
  5271     JSJitInfo::AliasEverything, /* aliasSet */
  5272     JSVAL_TYPE_UNKNOWN, /* returnType */
  5273     false,    /* isInfallible. False in setters. */
  5274     false,    /* isMovable */
  5275     false,    /* isInSlot */
  5276     false,    /* isTypedMethod */
  5277     0         /* slotIndex */
  5278 };
  5280 static const JSPropertySpec dom_props[] = {
  5281     {"x",
  5282      JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_NATIVE_ACCESSORS,
  5283      { { (JSPropertyOp)dom_genericGetter, &dom_x_getterinfo } },
  5284      { { (JSStrictPropertyOp)dom_genericSetter, &dom_x_setterinfo } }
  5285     },
  5286     JS_PS_END
  5287 };
  5289 static const JSFunctionSpec dom_methods[] = {
  5290     JS_FNINFO("doFoo", dom_genericMethod, &doFoo_methodinfo, 3, JSPROP_ENUMERATE),
  5291     JS_FS_END
  5292 };
  5294 static const JSClass dom_class = {
  5295     "FakeDOMObject", JSCLASS_IS_DOMJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(2),
  5296     JS_PropertyStub,       /* addProperty */
  5297     JS_DeletePropertyStub, /* delProperty */
  5298     JS_PropertyStub,       /* getProperty */
  5299     JS_StrictPropertyStub, /* setProperty */
  5300     JS_EnumerateStub,
  5301     JS_ResolveStub,
  5302     JS_ConvertStub,
  5303     nullptr,               /* finalize */
  5304     nullptr,               /* call */
  5305     nullptr,               /* hasInstance */
  5306     nullptr,               /* construct */
  5307     nullptr,               /* trace */
  5308     JSCLASS_NO_INTERNAL_MEMBERS
  5309 };
  5311 #ifdef DEBUG
  5312 static const JSClass *GetDomClass() {
  5313     return &dom_class;
  5315 #endif
  5317 static bool
  5318 dom_genericGetter(JSContext *cx, unsigned argc, JS::Value *vp)
  5320     CallArgs args = CallArgsFromVp(argc, vp);
  5321     RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
  5322     if (!obj)
  5323         return false;
  5325     if (JS_GetClass(obj) != &dom_class) {
  5326         args.rval().set(UndefinedValue());
  5327         return true;
  5330     JS::Value val = js::GetReservedSlot(obj, DOM_OBJECT_SLOT);
  5332     const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
  5333     MOZ_ASSERT(info->type() == JSJitInfo::Getter);
  5334     JSJitGetterOp getter = info->getter;
  5335     return getter(cx, obj, val.toPrivate(), JSJitGetterCallArgs(args));
  5338 static bool
  5339 dom_genericSetter(JSContext* cx, unsigned argc, JS::Value* vp)
  5341     CallArgs args = CallArgsFromVp(argc, vp);
  5342     RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
  5343     if (!obj)
  5344         return false;
  5346     JS_ASSERT(args.length() == 1);
  5348     if (JS_GetClass(obj) != &dom_class) {
  5349         args.rval().set(UndefinedValue());
  5350         return true;
  5353     JS::Value val = js::GetReservedSlot(obj, DOM_OBJECT_SLOT);
  5355     const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
  5356     MOZ_ASSERT(info->type() == JSJitInfo::Setter);
  5357     JSJitSetterOp setter = info->setter;
  5358     if (!setter(cx, obj, val.toPrivate(), JSJitSetterCallArgs(args)))
  5359         return false;
  5360     args.rval().set(UndefinedValue());
  5361     return true;
  5364 static bool
  5365 dom_genericMethod(JSContext* cx, unsigned argc, JS::Value *vp)
  5367     CallArgs args = CallArgsFromVp(argc, vp);
  5368     RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
  5369     if (!obj)
  5370         return false;
  5372     if (JS_GetClass(obj) != &dom_class) {
  5373         args.rval().set(UndefinedValue());
  5374         return true;
  5377     JS::Value val = js::GetReservedSlot(obj, DOM_OBJECT_SLOT);
  5379     const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
  5380     MOZ_ASSERT(info->type() == JSJitInfo::Method);
  5381     JSJitMethodOp method = info->method;
  5382     return method(cx, obj, val.toPrivate(), JSJitMethodCallArgs(args));
  5385 static void
  5386 InitDOMObject(HandleObject obj)
  5388     /* Fow now just initialize to a constant we can check. */
  5389     SetReservedSlot(obj, DOM_OBJECT_SLOT, PRIVATE_TO_JSVAL((void *)0x1234));
  5392 static bool
  5393 dom_constructor(JSContext* cx, unsigned argc, JS::Value *vp)
  5395     CallArgs args = CallArgsFromVp(argc, vp);
  5397     RootedObject callee(cx, &args.callee());
  5398     RootedValue protov(cx);
  5399     if (!JSObject::getProperty(cx, callee, callee, cx->names().prototype, &protov))
  5400         return false;
  5402     if (!protov.isObject()) {
  5403         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_PROTOTYPE, "FakeDOMObject");
  5404         return false;
  5407     RootedObject proto(cx, &protov.toObject());
  5408     RootedObject domObj(cx, JS_NewObject(cx, &dom_class, proto, JS::NullPtr()));
  5409     if (!domObj)
  5410         return false;
  5412     InitDOMObject(domObj);
  5414     args.rval().setObject(*domObj);
  5415     return true;
  5418 static bool
  5419 InstanceClassHasProtoAtDepth(JSObject *protoObject, uint32_t protoID, uint32_t depth)
  5421     /* There's only a single (fake) DOM object in the shell, so just return true. */
  5422     return true;
  5425 class ScopedFileDesc
  5427     intptr_t fd_;
  5428   public:
  5429     enum LockType { READ_LOCK, WRITE_LOCK };
  5430     ScopedFileDesc(int fd, LockType lockType)
  5431       : fd_(fd)
  5433         if (fd == -1)
  5434             return;
  5435         if (!jsCacheOpened.compareExchange(false, true)) {
  5436             close(fd_);
  5437             fd_ = -1;
  5438             return;
  5441     ~ScopedFileDesc() {
  5442         if (fd_ == -1)
  5443             return;
  5444         JS_ASSERT(jsCacheOpened == true);
  5445         jsCacheOpened = false;
  5446         close(fd_);
  5448     operator intptr_t() const {
  5449         return fd_;
  5451     intptr_t forget() {
  5452         intptr_t ret = fd_;
  5453         fd_ = -1;
  5454         return ret;
  5456 };
  5458 // To guard against corrupted cache files generated by previous crashes, write
  5459 // asmJSCacheCookie to the first uint32_t of the file only after the file is
  5460 // fully serialized and flushed to disk.
  5461 static const uint32_t asmJSCacheCookie = 0xabbadaba;
  5463 static bool
  5464 ShellOpenAsmJSCacheEntryForRead(HandleObject global, const jschar *begin, const jschar *limit,
  5465                                 size_t *serializedSizeOut, const uint8_t **memoryOut,
  5466                                 intptr_t *handleOut)
  5468     if (!jsCachingEnabled || !jsCacheAsmJSPath)
  5469         return false;
  5471     ScopedFileDesc fd(open(jsCacheAsmJSPath, O_RDWR), ScopedFileDesc::READ_LOCK);
  5472     if (fd == -1)
  5473         return false;
  5475     // Get the size and make sure we can dereference at least one uint32_t.
  5476     off_t off = lseek(fd, 0, SEEK_END);
  5477     if (off == -1 || off < (off_t)sizeof(uint32_t))
  5478         return false;
  5480     // Map the file into memory.
  5481     void *memory;
  5482 #ifdef XP_WIN
  5483     HANDLE fdOsHandle = (HANDLE)_get_osfhandle(fd);
  5484     HANDLE fileMapping = CreateFileMapping(fdOsHandle, nullptr, PAGE_READWRITE, 0, 0, nullptr);
  5485     if (!fileMapping)
  5486         return false;
  5488     memory = MapViewOfFile(fileMapping, FILE_MAP_READ, 0, 0, 0);
  5489     CloseHandle(fileMapping);
  5490     if (!memory)
  5491         return false;
  5492 #else
  5493     memory = mmap(nullptr, off, PROT_READ, MAP_SHARED, fd, 0);
  5494     if (memory == MAP_FAILED)
  5495         return false;
  5496 #endif
  5498     // Perform check described by asmJSCacheCookie comment.
  5499     if (*(uint32_t *)memory != asmJSCacheCookie) {
  5500 #ifdef XP_WIN
  5501         UnmapViewOfFile(memory);
  5502 #else
  5503         munmap(memory, off);
  5504 #endif
  5505         return false;
  5508     // The embedding added the cookie so strip it off of the buffer returned to
  5509     // the JS engine.
  5510     *serializedSizeOut = off - sizeof(uint32_t);
  5511     *memoryOut = (uint8_t *)memory + sizeof(uint32_t);
  5512     *handleOut = fd.forget();
  5513     return true;
  5516 static void
  5517 ShellCloseAsmJSCacheEntryForRead(HandleObject global, size_t serializedSize, const uint8_t *memory,
  5518                                  intptr_t handle)
  5520     // Undo the cookie adjustment done when opening the file.
  5521     memory -= sizeof(uint32_t);
  5522     serializedSize += sizeof(uint32_t);
  5524     // Release the memory mapping and file.
  5525 #ifdef XP_WIN
  5526     UnmapViewOfFile(const_cast<uint8_t*>(memory));
  5527 #else
  5528     munmap(const_cast<uint8_t*>(memory), serializedSize);
  5529 #endif
  5531     JS_ASSERT(jsCacheOpened == true);
  5532     jsCacheOpened = false;
  5533     close(handle);
  5536 static bool
  5537 ShellOpenAsmJSCacheEntryForWrite(HandleObject global, bool installed,
  5538                                  const jschar *begin, const jschar *end,
  5539                                  size_t serializedSize, uint8_t **memoryOut, intptr_t *handleOut)
  5541     if (!jsCachingEnabled || !jsCacheAsmJSPath)
  5542         return false;
  5544     // Create the cache directory if it doesn't already exist.
  5545     struct stat dirStat;
  5546     if (stat(jsCacheDir, &dirStat) == 0) {
  5547         if (!(dirStat.st_mode & S_IFDIR))
  5548             return false;
  5549     } else {
  5550 #ifdef XP_WIN
  5551         if (mkdir(jsCacheDir) != 0)
  5552             return false;
  5553 #else
  5554         if (mkdir(jsCacheDir, 0777) != 0)
  5555             return false;
  5556 #endif
  5559     ScopedFileDesc fd(open(jsCacheAsmJSPath, O_CREAT|O_RDWR, 0660), ScopedFileDesc::WRITE_LOCK);
  5560     if (fd == -1)
  5561         return false;
  5563     // Include extra space for the asmJSCacheCookie.
  5564     serializedSize += sizeof(uint32_t);
  5566     // Resize the file to the appropriate size after zeroing their contents.
  5567 #ifdef XP_WIN
  5568     if (chsize(fd, 0))
  5569         return false;
  5570     if (chsize(fd, serializedSize))
  5571         return false;
  5572 #else
  5573     if (ftruncate(fd, 0))
  5574         return false;
  5575     if (ftruncate(fd, serializedSize))
  5576         return false;
  5577 #endif
  5579     // Map the file into memory.
  5580     void *memory;
  5581 #ifdef XP_WIN
  5582     HANDLE fdOsHandle = (HANDLE)_get_osfhandle(fd);
  5583     HANDLE fileMapping = CreateFileMapping(fdOsHandle, nullptr, PAGE_READWRITE, 0, 0, nullptr);
  5584     if (!fileMapping)
  5585         return false;
  5587     memory = MapViewOfFile(fileMapping, FILE_MAP_WRITE, 0, 0, 0);
  5588     CloseHandle(fileMapping);
  5589     if (!memory)
  5590         return false;
  5591 #else
  5592     memory = mmap(nullptr, serializedSize, PROT_WRITE, MAP_SHARED, fd, 0);
  5593     if (memory == MAP_FAILED)
  5594         return false;
  5595 #endif
  5597     // The embedding added the cookie so strip it off of the buffer returned to
  5598     // the JS engine. The asmJSCacheCookie will be written on close, below.
  5599     JS_ASSERT(*(uint32_t *)memory == 0);
  5600     *memoryOut = (uint8_t *)memory + sizeof(uint32_t);
  5601     *handleOut = fd.forget();
  5602     return true;
  5605 static void
  5606 ShellCloseAsmJSCacheEntryForWrite(HandleObject global, size_t serializedSize, uint8_t *memory,
  5607                                   intptr_t handle)
  5609     // Undo the cookie adjustment done when opening the file.
  5610     memory -= sizeof(uint32_t);
  5611     serializedSize += sizeof(uint32_t);
  5613     // Write the magic cookie value after flushing the entire cache entry.
  5614 #ifdef XP_WIN
  5615     FlushViewOfFile(memory, serializedSize);
  5616     FlushFileBuffers(HANDLE(_get_osfhandle(handle)));
  5617 #else
  5618     msync(memory, serializedSize, MS_SYNC);
  5619 #endif
  5621     JS_ASSERT(*(uint32_t *)memory == 0);
  5622     *(uint32_t *)memory = asmJSCacheCookie;
  5624     // Free the memory mapping and file.
  5625 #ifdef XP_WIN
  5626     UnmapViewOfFile(const_cast<uint8_t*>(memory));
  5627 #else
  5628     munmap(memory, serializedSize);
  5629 #endif
  5631     JS_ASSERT(jsCacheOpened == true);
  5632     jsCacheOpened = false;
  5633     close(handle);
  5636 static bool
  5637 ShellBuildId(JS::BuildIdCharVector *buildId)
  5639     // The browser embeds the date into the buildid and the buildid is embedded
  5640     // in the binary, so every 'make' necessarily builds a new firefox binary.
  5641     // Fortunately, the actual firefox executable is tiny -- all the code is in
  5642     // libxul.so and other shared modules -- so this isn't a big deal. Not so
  5643     // for the statically-linked JS shell. To avoid recompmiling js.cpp and
  5644     // re-linking 'js' on every 'make', we use a constant buildid and rely on
  5645     // the shell user to manually clear the cache (deleting the dir passed to
  5646     // --js-cache) between cache-breaking updates. Note: jit_tests.py does this
  5647     // on every run).
  5648     const char buildid[] = "JS-shell";
  5649     return buildId->append(buildid, sizeof(buildid));
  5652 static JS::AsmJSCacheOps asmJSCacheOps = {
  5653     ShellOpenAsmJSCacheEntryForRead,
  5654     ShellCloseAsmJSCacheEntryForRead,
  5655     ShellOpenAsmJSCacheEntryForWrite,
  5656     ShellCloseAsmJSCacheEntryForWrite,
  5657     ShellBuildId
  5658 };
  5660 /*
  5661  * Avoid a reentrancy hazard.
  5663  * The non-JS_THREADSAFE shell uses a signal handler to implement timeout().
  5664  * The JS engine is not really reentrant, but JS_RequestInterruptCallback
  5665  * is mostly safe--the only danger is that we might interrupt JS_NewContext or
  5666  * JS_DestroyContext while the context list is being modified. Therefore we
  5667  * disable the signal handler around calls to those functions.
  5668  */
  5669 #ifdef JS_THREADSAFE
  5670 # define WITH_SIGNALS_DISABLED(x)  x
  5671 #else
  5672 # define WITH_SIGNALS_DISABLED(x)                                               \
  5673     JS_BEGIN_MACRO                                                              \
  5674         ScheduleWatchdog(gRuntime, -1);                                         \
  5675         x;                                                                      \
  5676         ScheduleWatchdog(gRuntime, gTimeoutInterval);                           \
  5677     JS_END_MACRO
  5678 #endif
  5680 static JSContext *
  5681 NewContext(JSRuntime *rt)
  5683     JSContext *cx;
  5684     WITH_SIGNALS_DISABLED(cx = JS_NewContext(rt, gStackChunkSize));
  5685     if (!cx)
  5686         return nullptr;
  5688     JSShellContextData *data = NewContextData();
  5689     if (!data) {
  5690         DestroyContext(cx, false);
  5691         return nullptr;
  5694     JS_SetContextPrivate(cx, data);
  5695     JS_SetErrorReporter(cx, my_ErrorReporter);
  5696     return cx;
  5699 static void
  5700 DestroyContext(JSContext *cx, bool withGC)
  5702     JSShellContextData *data = GetContextData(cx);
  5703     JS_SetContextPrivate(cx, nullptr);
  5704     free(data);
  5705     WITH_SIGNALS_DISABLED(withGC ? JS_DestroyContext(cx) : JS_DestroyContextNoGC(cx));
  5708 static JSObject *
  5709 NewGlobalObject(JSContext *cx, JS::CompartmentOptions &options,
  5710                 JSPrincipals *principals)
  5712     RootedObject glob(cx, JS_NewGlobalObject(cx, &global_class, principals,
  5713                                              JS::DontFireOnNewGlobalHook, options));
  5714     if (!glob)
  5715         return nullptr;
  5718         JSAutoCompartment ac(cx, glob);
  5720 #ifndef LAZY_STANDARD_CLASSES
  5721         if (!JS_InitStandardClasses(cx, glob))
  5722             return nullptr;
  5723 #endif
  5725 #ifdef JS_HAS_CTYPES
  5726         if (!JS_InitCTypesClass(cx, glob))
  5727             return nullptr;
  5728 #endif
  5729         if (!JS_InitReflect(cx, glob))
  5730             return nullptr;
  5731         if (!JS_DefineDebuggerObject(cx, glob))
  5732             return nullptr;
  5733         if (!JS::RegisterPerfMeasurement(cx, glob))
  5734             return nullptr;
  5735         if (!JS_DefineFunctionsWithHelp(cx, glob, shell_functions) ||
  5736             !JS_DefineProfilingFunctions(cx, glob))
  5738             return nullptr;
  5740         if (!js::DefineTestingFunctions(cx, glob, fuzzingSafe))
  5741             return nullptr;
  5743         if (!fuzzingSafe && !JS_DefineFunctionsWithHelp(cx, glob, fuzzing_unsafe_functions))
  5744             return nullptr;
  5746         /* Initialize FakeDOMObject. */
  5747         static const js::DOMCallbacks DOMcallbacks = {
  5748             InstanceClassHasProtoAtDepth
  5749         };
  5750         SetDOMCallbacks(cx->runtime(), &DOMcallbacks);
  5752         RootedObject domProto(cx, JS_InitClass(cx, glob, js::NullPtr(), &dom_class, dom_constructor,
  5753                                                0, dom_props, dom_methods, nullptr, nullptr));
  5754         if (!domProto)
  5755             return nullptr;
  5757         /* Initialize FakeDOMObject.prototype */
  5758         InitDOMObject(domProto);
  5761     JS_FireOnNewGlobalObject(cx, glob);
  5763     return glob;
  5766 static bool
  5767 BindScriptArgs(JSContext *cx, JSObject *obj_, OptionParser *op)
  5769     RootedObject obj(cx, obj_);
  5771     MultiStringRange msr = op->getMultiStringArg("scriptArgs");
  5772     RootedObject scriptArgs(cx);
  5773     scriptArgs = JS_NewArrayObject(cx, 0);
  5774     if (!scriptArgs)
  5775         return false;
  5777     if (!JS_DefineProperty(cx, obj, "scriptArgs", scriptArgs, 0))
  5778         return false;
  5780     for (size_t i = 0; !msr.empty(); msr.popFront(), ++i) {
  5781         const char *scriptArg = msr.front();
  5782         JSString *str = JS_NewStringCopyZ(cx, scriptArg);
  5783         if (!str ||
  5784             !JS_DefineElement(cx, scriptArgs, i, STRING_TO_JSVAL(str), nullptr, nullptr,
  5785                               JSPROP_ENUMERATE)) {
  5786             return false;
  5790     return true;
  5793 // This function is currently only called from "#if defined(JS_ION)" chunks,
  5794 // so we're guarding the function definition with an #ifdef, too, to avoid
  5795 // build warning for unused function in non-ion-enabled builds:
  5796 #if defined(JS_ION)
  5797 static bool
  5798 OptionFailure(const char *option, const char *str)
  5800     fprintf(stderr, "Unrecognized option for %s: %s\n", option, str);
  5801     return false;
  5803 #endif /* JS_ION */
  5805 static int
  5806 ProcessArgs(JSContext *cx, JSObject *obj_, OptionParser *op)
  5808     RootedObject obj(cx, obj_);
  5810     if (op->getBoolOption('s'))
  5811         JS::ContextOptionsRef(cx).toggleExtraWarnings();
  5813     if (op->getBoolOption('d')) {
  5814         JS_SetRuntimeDebugMode(JS_GetRuntime(cx), true);
  5815         JS_SetDebugMode(cx, true);
  5818     /* |scriptArgs| gets bound on the global before any code is run. */
  5819     if (!BindScriptArgs(cx, obj, op))
  5820         return EXIT_FAILURE;
  5822     MultiStringRange filePaths = op->getMultiStringOption('f');
  5823     MultiStringRange codeChunks = op->getMultiStringOption('e');
  5825     if (filePaths.empty() && codeChunks.empty() && !op->getStringArg("script")) {
  5826         Process(cx, obj, nullptr, true); /* Interactive. */
  5827         return gExitCode;
  5830     while (!filePaths.empty() || !codeChunks.empty()) {
  5831         size_t fpArgno = filePaths.empty() ? -1 : filePaths.argno();
  5832         size_t ccArgno = codeChunks.empty() ? -1 : codeChunks.argno();
  5833         if (fpArgno < ccArgno) {
  5834             char *path = filePaths.front();
  5835             Process(cx, obj, path, false);
  5836             if (gExitCode)
  5837                 return gExitCode;
  5838             filePaths.popFront();
  5839         } else {
  5840             const char *code = codeChunks.front();
  5841             RootedValue rval(cx);
  5842             if (!JS_EvaluateScript(cx, obj, code, strlen(code), "-e", 1, &rval))
  5843                 return gExitCode ? gExitCode : EXITCODE_RUNTIME_ERROR;
  5844             codeChunks.popFront();
  5848     /* The |script| argument is processed after all options. */
  5849     if (const char *path = op->getStringArg("script")) {
  5850         Process(cx, obj, path, false);
  5851         if (gExitCode)
  5852             return gExitCode;
  5855     if (op->getBoolOption('i'))
  5856         Process(cx, obj, nullptr, true);
  5858     return gExitCode ? gExitCode : EXIT_SUCCESS;
  5861 static bool
  5862 SetRuntimeOptions(JSRuntime *rt, const OptionParser &op)
  5864 #if defined(JS_ION)
  5865     bool enableBaseline = !op.getBoolOption("no-baseline");
  5866     bool enableIon = !op.getBoolOption("no-ion");
  5867     bool enableAsmJS = !op.getBoolOption("no-asmjs");
  5869     JS::RuntimeOptionsRef(rt).setBaseline(enableBaseline)
  5870                              .setIon(enableIon)
  5871                              .setAsmJS(enableAsmJS);
  5873     if (const char *str = op.getStringOption("ion-gvn")) {
  5874         if (strcmp(str, "off") == 0) {
  5875             jit::js_JitOptions.disableGvn = true;
  5876         } else if (strcmp(str, "pessimistic") == 0) {
  5877             jit::js_JitOptions.forceGvnKind = true;
  5878             jit::js_JitOptions.forcedGvnKind = jit::GVN_Pessimistic;
  5879         } else if (strcmp(str, "optimistic") == 0) {
  5880             jit::js_JitOptions.forceGvnKind = true;
  5881             jit::js_JitOptions.forcedGvnKind = jit::GVN_Optimistic;
  5882         } else {
  5883             return OptionFailure("ion-gvn", str);
  5887     if (const char *str = op.getStringOption("ion-licm")) {
  5888         if (strcmp(str, "on") == 0)
  5889             jit::js_JitOptions.disableLicm = false;
  5890         else if (strcmp(str, "off") == 0)
  5891             jit::js_JitOptions.disableLicm = true;
  5892         else
  5893             return OptionFailure("ion-licm", str);
  5896     if (const char *str = op.getStringOption("ion-edgecase-analysis")) {
  5897         if (strcmp(str, "on") == 0)
  5898             jit::js_JitOptions.disableEdgeCaseAnalysis = false;
  5899         else if (strcmp(str, "off") == 0)
  5900             jit::js_JitOptions.disableEdgeCaseAnalysis = true;
  5901         else
  5902             return OptionFailure("ion-edgecase-analysis", str);
  5905      if (const char *str = op.getStringOption("ion-range-analysis")) {
  5906          if (strcmp(str, "on") == 0)
  5907              jit::js_JitOptions.disableRangeAnalysis = false;
  5908          else if (strcmp(str, "off") == 0)
  5909              jit::js_JitOptions.disableRangeAnalysis = true;
  5910          else
  5911              return OptionFailure("ion-range-analysis", str);
  5914     if (op.getBoolOption("ion-check-range-analysis"))
  5915         jit::js_JitOptions.checkRangeAnalysis = true;
  5917     if (const char *str = op.getStringOption("ion-inlining")) {
  5918         if (strcmp(str, "on") == 0)
  5919             jit::js_JitOptions.disableInlining = false;
  5920         else if (strcmp(str, "off") == 0)
  5921             jit::js_JitOptions.disableInlining = true;
  5922         else
  5923             return OptionFailure("ion-inlining", str);
  5926     if (const char *str = op.getStringOption("ion-osr")) {
  5927         if (strcmp(str, "on") == 0)
  5928             jit::js_JitOptions.osr = true;
  5929         else if (strcmp(str, "off") == 0)
  5930             jit::js_JitOptions.osr = false;
  5931         else
  5932             return OptionFailure("ion-osr", str);
  5935     if (const char *str = op.getStringOption("ion-limit-script-size")) {
  5936         if (strcmp(str, "on") == 0)
  5937             jit::js_JitOptions.limitScriptSize = true;
  5938         else if (strcmp(str, "off") == 0)
  5939             jit::js_JitOptions.limitScriptSize = false;
  5940         else
  5941             return OptionFailure("ion-limit-script-size", str);
  5944     int32_t useCount = op.getIntOption("ion-uses-before-compile");
  5945     if (useCount >= 0)
  5946         jit::js_JitOptions.setUsesBeforeCompile(useCount);
  5948     useCount = op.getIntOption("baseline-uses-before-compile");
  5949     if (useCount >= 0)
  5950         jit::js_JitOptions.baselineUsesBeforeCompile = useCount;
  5952     if (op.getBoolOption("baseline-eager"))
  5953         jit::js_JitOptions.baselineUsesBeforeCompile = 0;
  5955     if (const char *str = op.getStringOption("ion-regalloc")) {
  5956         if (strcmp(str, "lsra") == 0) {
  5957             jit::js_JitOptions.forceRegisterAllocator = true;
  5958             jit::js_JitOptions.forcedRegisterAllocator = jit::RegisterAllocator_LSRA;
  5959         } else if (strcmp(str, "backtracking") == 0) {
  5960             jit::js_JitOptions.forceRegisterAllocator = true;
  5961             jit::js_JitOptions.forcedRegisterAllocator = jit::RegisterAllocator_Backtracking;
  5962         } else if (strcmp(str, "stupid") == 0) {
  5963             jit::js_JitOptions.forceRegisterAllocator = true;
  5964             jit::js_JitOptions.forcedRegisterAllocator = jit::RegisterAllocator_Stupid;
  5965         } else {
  5966             return OptionFailure("ion-regalloc", str);
  5970     if (op.getBoolOption("ion-eager"))
  5971         jit::js_JitOptions.setEagerCompilation();
  5973     if (op.getBoolOption("ion-compile-try-catch"))
  5974         jit::js_JitOptions.compileTryCatch = true;
  5976     bool parallelCompilation = true;
  5977     if (const char *str = op.getStringOption("ion-parallel-compile")) {
  5978         if (strcmp(str, "off") == 0)
  5979             parallelCompilation = false;
  5980         else if (strcmp(str, "on") != 0)
  5981             return OptionFailure("ion-parallel-compile", str);
  5983 #ifdef JS_THREADSAFE
  5984     rt->setParallelIonCompilationEnabled(parallelCompilation);
  5985 #endif
  5987 #endif // JS_ION
  5989 #ifdef JS_ARM_SIMULATOR
  5990     if (op.getBoolOption("arm-sim-icache-checks"))
  5991         jit::Simulator::ICacheCheckingEnabled = true;
  5993     int32_t stopAt = op.getIntOption("arm-sim-stop-at");
  5994     if (stopAt >= 0)
  5995         jit::Simulator::StopSimAt = stopAt;
  5996 #endif
  5998     reportWarnings = op.getBoolOption('w');
  5999     compileOnly = op.getBoolOption('c');
  6000     printTiming = op.getBoolOption('b');
  6001     rt->profilingScripts = enableDisassemblyDumps = op.getBoolOption('D');
  6003     jsCacheDir = op.getStringOption("js-cache");
  6004     if (jsCacheDir) {
  6005         if (op.getBoolOption("js-cache-per-process"))
  6006             jsCacheDir = JS_smprintf("%s/%u", jsCacheDir, (unsigned)getpid());
  6007         jsCacheAsmJSPath = JS_smprintf("%s/asmjs.cache", jsCacheDir);
  6010 #ifdef JS_THREADSAFE
  6011     int32_t threadCount = op.getIntOption("thread-count");
  6012     if (threadCount >= 0)
  6013         SetFakeCPUCount(threadCount);
  6014 #endif /* JS_THREADSAFE */
  6016 #ifdef DEBUG
  6017     dumpEntrainedVariables = op.getBoolOption("dump-entrained-variables");
  6018 #endif
  6020     return true;
  6023 static int
  6024 Shell(JSContext *cx, OptionParser *op, char **envp)
  6026     JSAutoRequest ar(cx);
  6028     if (op->getBoolOption("fuzzing-safe"))
  6029         fuzzingSafe = true;
  6030     else
  6031         fuzzingSafe = (getenv("MOZ_FUZZING_SAFE") && getenv("MOZ_FUZZING_SAFE")[0] != '0');
  6033     RootedObject glob(cx);
  6034     JS::CompartmentOptions options;
  6035     options.setVersion(JSVERSION_LATEST);
  6036     glob = NewGlobalObject(cx, options, nullptr);
  6037     if (!glob)
  6038         return 1;
  6040     JSAutoCompartment ac(cx, glob);
  6041     js::SetDefaultObjectForContext(cx, glob);
  6043     JSObject *envobj = JS_DefineObject(cx, glob, "environment", &env_class, nullptr, 0);
  6044     if (!envobj)
  6045         return 1;
  6046     JS_SetPrivate(envobj, envp);
  6048     int result = ProcessArgs(cx, glob, op);
  6050     if (enableDisassemblyDumps)
  6051         JS_DumpCompartmentPCCounts(cx);
  6053     if (op->getBoolOption("js-cache-per-process")) {
  6054         if (jsCacheAsmJSPath)
  6055             unlink(jsCacheAsmJSPath);
  6056         if (jsCacheDir)
  6057             rmdir(jsCacheDir);
  6060     return result;
  6063 static void
  6064 MaybeOverrideOutFileFromEnv(const char* const envVar,
  6065                             FILE* defaultOut,
  6066                             FILE** outFile)
  6068     const char* outPath = getenv(envVar);
  6069     if (!outPath || !*outPath || !(*outFile = fopen(outPath, "w"))) {
  6070         *outFile = defaultOut;
  6074 /* Pretend we can always preserve wrappers for dummy DOM objects. */
  6075 static bool
  6076 DummyPreserveWrapperCallback(JSContext *cx, JSObject *obj)
  6078     return true;
  6081 int
  6082 main(int argc, char **argv, char **envp)
  6084     sArgc = argc;
  6085     sArgv = argv;
  6087     JSRuntime *rt;
  6088     JSContext *cx;
  6089     int result;
  6090 #ifdef XP_WIN
  6092         const char *crash_option = getenv("XRE_NO_WINDOWS_CRASH_DIALOG");
  6093         if (crash_option && strncmp(crash_option, "1", 1)) {
  6094             DWORD oldmode = SetErrorMode(SEM_NOGPFAULTERRORBOX);
  6095             SetErrorMode(oldmode | SEM_NOGPFAULTERRORBOX);
  6098 #endif
  6100 #ifdef HAVE_SETLOCALE
  6101     setlocale(LC_ALL, "");
  6102 #endif
  6104     MaybeOverrideOutFileFromEnv("JS_STDERR", stderr, &gErrFile);
  6105     MaybeOverrideOutFileFromEnv("JS_STDOUT", stdout, &gOutFile);
  6107     OptionParser op("Usage: {progname} [options] [[script] scriptArgs*]");
  6109     op.setDescription("The SpiderMonkey shell provides a command line interface to the "
  6110         "JavaScript engine. Code and file options provided via the command line are "
  6111         "run left to right. If provided, the optional script argument is run after "
  6112         "all options have been processed. Just-In-Time compilation modes may be enabled via "
  6113         "command line options.");
  6114     op.setDescriptionWidth(72);
  6115     op.setHelpWidth(80);
  6116     op.setVersion(JS_GetImplementationVersion());
  6118     if (!op.addMultiStringOption('f', "file", "PATH", "File path to run")
  6119         || !op.addMultiStringOption('e', "execute", "CODE", "Inline code to run")
  6120         || !op.addBoolOption('i', "shell", "Enter prompt after running code")
  6121         || !op.addBoolOption('m', "jm", "No-op (still used by fuzzers)")
  6122         || !op.addBoolOption('\0', "no-jm", "No-op (still used by fuzzers)")
  6123         || !op.addBoolOption('c', "compileonly", "Only compile, don't run (syntax checking mode)")
  6124         || !op.addBoolOption('w', "warnings", "Emit warnings")
  6125         || !op.addBoolOption('W', "nowarnings", "Don't emit warnings")
  6126         || !op.addBoolOption('s', "strict", "Check strictness")
  6127         || !op.addBoolOption('d', "debugjit", "Enable runtime debug mode for method JIT code")
  6128         || !op.addBoolOption('a', "always-mjit", "No-op (still used by fuzzers)")
  6129         || !op.addBoolOption('D', "dump-bytecode", "Dump bytecode with exec count for all scripts")
  6130         || !op.addBoolOption('b', "print-timing", "Print sub-ms runtime for each file that's run")
  6131         || !op.addStringOption('\0', "js-cache", "[path]",
  6132                                "Enable the JS cache by specifying the path of the directory to use "
  6133                                "to hold cache files")
  6134         || !op.addBoolOption('\0', "js-cache-per-process",
  6135                                "Generate a separate cache sub-directory for this process inside "
  6136                                "the cache directory specified by --js-cache. This cache directory "
  6137                                "will be removed when the js shell exits. This is useful for running "
  6138                                "tests in parallel.")
  6139 #ifdef DEBUG
  6140         || !op.addBoolOption('O', "print-alloc", "Print the number of allocations at exit")
  6141 #endif
  6142         || !op.addOptionalStringArg("script", "A script to execute (after all options)")
  6143         || !op.addOptionalMultiStringArg("scriptArgs",
  6144                                          "String arguments to bind as |scriptArgs| in the "
  6145                                          "shell's global")
  6146 #ifdef JS_THREADSAFE
  6147         || !op.addIntOption('\0', "thread-count", "COUNT", "Use COUNT auxiliary threads "
  6148                             "(default: # of cores - 1)", -1)
  6149 #endif
  6150         || !op.addBoolOption('\0', "ion", "Enable IonMonkey (default)")
  6151         || !op.addBoolOption('\0', "no-ion", "Disable IonMonkey")
  6152         || !op.addBoolOption('\0', "no-asmjs", "Disable asm.js compilation")
  6153         || !op.addStringOption('\0', "ion-gvn", "[mode]",
  6154                                "Specify Ion global value numbering:\n"
  6155                                "  off: disable GVN\n"
  6156                                "  pessimistic: use pessimistic GVN\n"
  6157                                "  optimistic: (default) use optimistic GVN")
  6158         || !op.addStringOption('\0', "ion-licm", "on/off",
  6159                                "Loop invariant code motion (default: on, off to disable)")
  6160         || !op.addStringOption('\0', "ion-edgecase-analysis", "on/off",
  6161                                "Find edge cases where Ion can avoid bailouts (default: on, off to disable)")
  6162         || !op.addStringOption('\0', "ion-range-analysis", "on/off",
  6163                                "Range analysis (default: on, off to disable)")
  6164         || !op.addBoolOption('\0', "ion-check-range-analysis",
  6165                                "Range analysis checking")
  6166         || !op.addStringOption('\0', "ion-inlining", "on/off",
  6167                                "Inline methods where possible (default: on, off to disable)")
  6168         || !op.addStringOption('\0', "ion-osr", "on/off",
  6169                                "On-Stack Replacement (default: on, off to disable)")
  6170         || !op.addStringOption('\0', "ion-limit-script-size", "on/off",
  6171                                "Don't compile very large scripts (default: on, off to disable)")
  6172         || !op.addIntOption('\0', "ion-uses-before-compile", "COUNT",
  6173                             "Wait for COUNT calls or iterations before compiling "
  6174                             "(default: 1000)", -1)
  6175         || !op.addStringOption('\0', "ion-regalloc", "[mode]",
  6176                                "Specify Ion register allocation:\n"
  6177                                "  lsra: Linear Scan register allocation (default)\n"
  6178                                "  backtracking: Priority based backtracking register allocation\n"
  6179                                "  stupid: Simple block local register allocation")
  6180         || !op.addBoolOption('\0', "ion-eager", "Always ion-compile methods (implies --baseline-eager)")
  6181         || !op.addBoolOption('\0', "ion-compile-try-catch", "Ion-compile try-catch statements")
  6182         || !op.addStringOption('\0', "ion-parallel-compile", "on/off",
  6183                                "Compile scripts off thread (default: off)")
  6184         || !op.addBoolOption('\0', "baseline", "Enable baseline compiler (default)")
  6185         || !op.addBoolOption('\0', "no-baseline", "Disable baseline compiler")
  6186         || !op.addBoolOption('\0', "baseline-eager", "Always baseline-compile methods")
  6187         || !op.addIntOption('\0', "baseline-uses-before-compile", "COUNT",
  6188                             "Wait for COUNT calls or iterations before baseline-compiling "
  6189                             "(default: 10)", -1)
  6190         || !op.addBoolOption('\0', "no-fpu", "Pretend CPU does not support floating-point operations "
  6191                              "to test JIT codegen (no-op on platforms other than x86).")
  6192         || !op.addBoolOption('\0', "no-sse3", "Pretend CPU does not support SSE3 instructions and above "
  6193                              "to test JIT codegen (no-op on platforms other than x86 and x64).")
  6194         || !op.addBoolOption('\0', "no-sse4", "Pretend CPU does not support SSE4 instructions"
  6195                              "to test JIT codegen (no-op on platforms other than x86 and x64).")
  6196         || !op.addBoolOption('\0', "fuzzing-safe", "Don't expose functions that aren't safe for "
  6197                              "fuzzers to call")
  6198 #ifdef DEBUG
  6199         || !op.addBoolOption('\0', "dump-entrained-variables", "Print variables which are "
  6200                              "unnecessarily entrained by inner functions")
  6201 #endif
  6202 #ifdef JSGC_GENERATIONAL
  6203         || !op.addBoolOption('\0', "no-ggc", "Disable Generational GC")
  6204 #endif
  6205         || !op.addIntOption('\0', "available-memory", "SIZE",
  6206                             "Select GC settings based on available memory (MB)", 0)
  6207 #ifdef JS_ARM_SIMULATOR
  6208         || !op.addBoolOption('\0', "arm-sim-icache-checks", "Enable icache flush checks in the ARM "
  6209                              "simulator.")
  6210         || !op.addIntOption('\0', "arm-sim-stop-at", "NUMBER", "Stop the ARM simulator after the given "
  6211                             "NUMBER of instructions.", -1)
  6212 #endif
  6215         return EXIT_FAILURE;
  6218     op.setArgTerminatesOptions("script", true);
  6219     op.setArgCapturesRest("scriptArgs");
  6221     switch (op.parseArgs(argc, argv)) {
  6222       case OptionParser::ParseHelp:
  6223         return EXIT_SUCCESS;
  6224       case OptionParser::ParseError:
  6225         op.printHelp(argv[0]);
  6226         return EXIT_FAILURE;
  6227       case OptionParser::Fail:
  6228         return EXIT_FAILURE;
  6229       case OptionParser::Okay:
  6230         break;
  6233     if (op.getHelpOption())
  6234         return EXIT_SUCCESS;
  6236 #ifdef DEBUG
  6237     /*
  6238      * Process OOM options as early as possible so that we can observe as many
  6239      * allocations as possible.
  6240      */
  6241     OOM_printAllocationCount = op.getBoolOption('O');
  6243 #if defined(JS_CODEGEN_X86) && defined(JS_ION)
  6244     if (op.getBoolOption("no-fpu"))
  6245         JSC::MacroAssembler::SetFloatingPointDisabled();
  6246 #endif
  6248 #if (defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)) && defined(JS_ION)
  6249     if (op.getBoolOption("no-sse3")) {
  6250         JSC::MacroAssembler::SetSSE3Disabled();
  6251         PropagateFlagToNestedShells("--no-sse3");
  6253     if (op.getBoolOption("no-sse4")) {
  6254         JSC::MacroAssembler::SetSSE4Disabled();
  6255         PropagateFlagToNestedShells("--no-sse4");
  6257 #endif
  6259 #endif // DEBUG
  6261     // Start the engine.
  6262     if (!JS_Init())
  6263         return 1;
  6265     /* Use the same parameters as the browser in xpcjsruntime.cpp. */
  6266     rt = JS_NewRuntime(32L * 1024L * 1024L, JS_USE_HELPER_THREADS);
  6267     if (!rt)
  6268         return 1;
  6270     JS::SetOutOfMemoryCallback(rt, my_OOMCallback);
  6271     if (!SetRuntimeOptions(rt, op))
  6272         return 1;
  6274     gInterruptFunc.construct(rt, NullValue());
  6276     JS_SetGCParameter(rt, JSGC_MAX_BYTES, 0xffffffff);
  6277 #ifdef JSGC_GENERATIONAL
  6278     Maybe<JS::AutoDisableGenerationalGC> noggc;
  6279     if (op.getBoolOption("no-ggc"))
  6280         noggc.construct(rt);
  6281 #endif
  6283     size_t availMem = op.getIntOption("available-memory");
  6284     if (availMem > 0)
  6285         JS_SetGCParametersBasedOnAvailableMemory(rt, availMem);
  6287     JS_SetTrustedPrincipals(rt, &ShellPrincipals::fullyTrusted);
  6288     JS_SetSecurityCallbacks(rt, &ShellPrincipals::securityCallbacks);
  6289     JS_InitDestroyPrincipalsCallback(rt, ShellPrincipals::destroy);
  6291     JS_SetInterruptCallback(rt, ShellInterruptCallback);
  6292     JS::SetAsmJSCacheOps(rt, &asmJSCacheOps);
  6294     JS_SetNativeStackQuota(rt, gMaxStackSize);
  6296 #ifdef JS_THREADSAFE
  6297     if (!offThreadState.init())
  6298         return 1;
  6299 #endif
  6301     if (!InitWatchdog(rt))
  6302         return 1;
  6304     cx = NewContext(rt);
  6305     if (!cx)
  6306         return 1;
  6308     JS_SetGCParameter(rt, JSGC_MODE, JSGC_MODE_INCREMENTAL);
  6309     JS_SetGCParameterForThread(cx, JSGC_MAX_CODE_CACHE_BYTES, 16 * 1024 * 1024);
  6311     js::SetPreserveWrapperCallback(rt, DummyPreserveWrapperCallback);
  6313     result = Shell(cx, &op, envp);
  6315 #ifdef DEBUG
  6316     if (OOM_printAllocationCount)
  6317         printf("OOM max count: %u\n", OOM_counter);
  6318 #endif
  6320     DestroyContext(cx, true);
  6322     KillWatchdog();
  6324     gInterruptFunc.destroy();
  6326 #ifdef JS_THREADSAFE
  6327     for (size_t i = 0; i < workerThreads.length(); i++)
  6328         PR_JoinThread(workerThreads[i]);
  6329 #endif
  6331 #ifdef JSGC_GENERATIONAL
  6332     if (!noggc.empty())
  6333         noggc.destroy();
  6334 #endif
  6336     JS_DestroyRuntime(rt);
  6337     JS_ShutDown();
  6338     return result;

mercurial