1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/shell/js.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,6339 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sts=4 et sw=4 tw=99: 1.5 + * This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +/* JS shell. */ 1.10 + 1.11 +#include "mozilla/ArrayUtils.h" 1.12 +#include "mozilla/Atomics.h" 1.13 +#include "mozilla/DebugOnly.h" 1.14 +#include "mozilla/GuardObjects.h" 1.15 +#include "mozilla/PodOperations.h" 1.16 + 1.17 +#ifdef XP_WIN 1.18 +# include <direct.h> 1.19 +# include <process.h> 1.20 +#endif 1.21 +#include <errno.h> 1.22 +#include <fcntl.h> 1.23 +#if defined(XP_WIN) 1.24 +# include <io.h> /* for isatty() */ 1.25 +#endif 1.26 +#include <locale.h> 1.27 +#include <math.h> 1.28 +#include <signal.h> 1.29 +#include <stdio.h> 1.30 +#include <stdlib.h> 1.31 +#include <string.h> 1.32 +#include <sys/stat.h> 1.33 +#include <sys/types.h> 1.34 +#ifdef XP_UNIX 1.35 +# include <sys/mman.h> 1.36 +# include <sys/stat.h> 1.37 +# include <sys/wait.h> 1.38 +# include <unistd.h> 1.39 +#endif 1.40 + 1.41 +#include "jsapi.h" 1.42 +#include "jsarray.h" 1.43 +#include "jsatom.h" 1.44 +#include "jscntxt.h" 1.45 +#include "jsfun.h" 1.46 +#ifdef JS_THREADSAFE 1.47 +#include "jslock.h" 1.48 +#endif 1.49 +#include "jsobj.h" 1.50 +#include "jsprf.h" 1.51 +#include "jsscript.h" 1.52 +#include "jstypes.h" 1.53 +#include "jsutil.h" 1.54 +#ifdef XP_WIN 1.55 +# include "jswin.h" 1.56 +#endif 1.57 +#include "jsworkers.h" 1.58 +#include "jswrapper.h" 1.59 +#include "prmjtime.h" 1.60 + 1.61 +#include "builtin/TestingFunctions.h" 1.62 +#include "frontend/Parser.h" 1.63 +#include "jit/arm/Simulator-arm.h" 1.64 +#include "jit/Ion.h" 1.65 +#include "js/OldDebugAPI.h" 1.66 +#include "js/StructuredClone.h" 1.67 +#include "perf/jsperf.h" 1.68 +#include "shell/jsheaptools.h" 1.69 +#include "shell/jsoptparse.h" 1.70 +#include "vm/ArgumentsObject.h" 1.71 +#include "vm/Monitor.h" 1.72 +#include "vm/Shape.h" 1.73 +#include "vm/TypedArrayObject.h" 1.74 +#include "vm/WrapperObject.h" 1.75 + 1.76 +#include "jscompartmentinlines.h" 1.77 +#include "jsobjinlines.h" 1.78 + 1.79 +#ifdef XP_WIN 1.80 +# define PATH_MAX (MAX_PATH > _MAX_DIR ? MAX_PATH : _MAX_DIR) 1.81 +#else 1.82 +# include <libgen.h> 1.83 +#endif 1.84 + 1.85 +using namespace js; 1.86 +using namespace js::cli; 1.87 + 1.88 +using mozilla::ArrayLength; 1.89 +using mozilla::NumberEqualsInt32; 1.90 +using mozilla::Maybe; 1.91 +using mozilla::PodCopy; 1.92 + 1.93 +enum JSShellExitCode { 1.94 + EXITCODE_RUNTIME_ERROR = 3, 1.95 + EXITCODE_FILE_NOT_FOUND = 4, 1.96 + EXITCODE_OUT_OF_MEMORY = 5, 1.97 + EXITCODE_TIMEOUT = 6 1.98 +}; 1.99 + 1.100 +enum PathResolutionMode { 1.101 + RootRelative, 1.102 + ScriptRelative 1.103 +}; 1.104 + 1.105 +static size_t gStackChunkSize = 8192; 1.106 + 1.107 +/* 1.108 + * Note: This limit should match the stack limit set by the browser in 1.109 + * js/xpconnect/src/XPCJSRuntime.cpp 1.110 + */ 1.111 +#if defined(MOZ_ASAN) || (defined(DEBUG) && !defined(XP_WIN)) 1.112 +static size_t gMaxStackSize = 2 * 128 * sizeof(size_t) * 1024; 1.113 +#else 1.114 +static size_t gMaxStackSize = 128 * sizeof(size_t) * 1024; 1.115 +#endif 1.116 + 1.117 +/* 1.118 + * Limit the timeout to 30 minutes to prevent an overflow on platfoms 1.119 + * that represent the time internally in microseconds using 32-bit int. 1.120 + */ 1.121 +static double MAX_TIMEOUT_INTERVAL = 1800.0; 1.122 +static double gTimeoutInterval = -1.0; 1.123 +static volatile bool gServiceInterrupt = false; 1.124 +static Maybe<JS::PersistentRootedValue> gInterruptFunc; 1.125 + 1.126 +static bool enableDisassemblyDumps = false; 1.127 + 1.128 +static bool printTiming = false; 1.129 +static const char *jsCacheDir = nullptr; 1.130 +static const char *jsCacheAsmJSPath = nullptr; 1.131 +static bool jsCachingEnabled = false; 1.132 +mozilla::Atomic<bool> jsCacheOpened(false); 1.133 + 1.134 +static bool 1.135 +SetTimeoutValue(JSContext *cx, double t); 1.136 + 1.137 +static bool 1.138 +InitWatchdog(JSRuntime *rt); 1.139 + 1.140 +static void 1.141 +KillWatchdog(); 1.142 + 1.143 +static bool 1.144 +ScheduleWatchdog(JSRuntime *rt, double t); 1.145 + 1.146 +static void 1.147 +CancelExecution(JSRuntime *rt); 1.148 + 1.149 +/* 1.150 + * Watchdog thread state. 1.151 + */ 1.152 +#ifdef JS_THREADSAFE 1.153 + 1.154 +static PRLock *gWatchdogLock = nullptr; 1.155 +static PRCondVar *gWatchdogWakeup = nullptr; 1.156 +static PRThread *gWatchdogThread = nullptr; 1.157 +static bool gWatchdogHasTimeout = false; 1.158 +static int64_t gWatchdogTimeout = 0; 1.159 + 1.160 +static PRCondVar *gSleepWakeup = nullptr; 1.161 + 1.162 +#else 1.163 + 1.164 +static JSRuntime *gRuntime = nullptr; 1.165 + 1.166 +#endif 1.167 + 1.168 +static int gExitCode = 0; 1.169 +static bool gQuitting = false; 1.170 +static bool gGotError = false; 1.171 +static FILE *gErrFile = nullptr; 1.172 +static FILE *gOutFile = nullptr; 1.173 + 1.174 +static bool reportWarnings = true; 1.175 +static bool compileOnly = false; 1.176 +static bool fuzzingSafe = false; 1.177 + 1.178 +#ifdef DEBUG 1.179 +static bool dumpEntrainedVariables = false; 1.180 +static bool OOM_printAllocationCount = false; 1.181 +#endif 1.182 + 1.183 +enum JSShellErrNum { 1.184 +#define MSG_DEF(name, number, count, exception, format) \ 1.185 + name = number, 1.186 +#include "jsshell.msg" 1.187 +#undef MSG_DEF 1.188 + JSShellErr_Limit 1.189 +}; 1.190 + 1.191 +static JSContext * 1.192 +NewContext(JSRuntime *rt); 1.193 + 1.194 +static void 1.195 +DestroyContext(JSContext *cx, bool withGC); 1.196 + 1.197 +static JSObject * 1.198 +NewGlobalObject(JSContext *cx, JS::CompartmentOptions &options, 1.199 + JSPrincipals *principals); 1.200 + 1.201 +static const JSErrorFormatString * 1.202 +my_GetErrorMessage(void *userRef, const char *locale, const unsigned errorNumber); 1.203 + 1.204 + 1.205 +/* 1.206 + * A toy principals type for the shell. 1.207 + * 1.208 + * In the shell, a principal is simply a 32-bit mask: P subsumes Q if the 1.209 + * set bits in P are a superset of those in Q. Thus, the principal 0 is 1.210 + * subsumed by everything, and the principal ~0 subsumes everything. 1.211 + * 1.212 + * As a special case, a null pointer as a principal is treated like 0xffff. 1.213 + * 1.214 + * The 'newGlobal' function takes an option indicating which principal the 1.215 + * new global should have; 'evaluate' does for the new code. 1.216 + */ 1.217 +class ShellPrincipals: public JSPrincipals { 1.218 + uint32_t bits; 1.219 + 1.220 + static uint32_t getBits(JSPrincipals *p) { 1.221 + if (!p) 1.222 + return 0xffff; 1.223 + return static_cast<ShellPrincipals *>(p)->bits; 1.224 + } 1.225 + 1.226 + public: 1.227 + ShellPrincipals(uint32_t bits, int32_t refcount = 0) : bits(bits) { 1.228 + this->refcount = refcount; 1.229 + } 1.230 + 1.231 + static void destroy(JSPrincipals *principals) { 1.232 + MOZ_ASSERT(principals != &fullyTrusted); 1.233 + MOZ_ASSERT(principals->refcount == 0); 1.234 + js_free(static_cast<ShellPrincipals *>(principals)); 1.235 + } 1.236 + 1.237 + static bool subsumes(JSPrincipals *first, JSPrincipals *second) { 1.238 + uint32_t firstBits = getBits(first); 1.239 + uint32_t secondBits = getBits(second); 1.240 + return (firstBits | secondBits) == firstBits; 1.241 + } 1.242 + 1.243 + static JSSecurityCallbacks securityCallbacks; 1.244 + 1.245 + // Fully-trusted principals singleton. 1.246 + static ShellPrincipals fullyTrusted; 1.247 +}; 1.248 + 1.249 +JSSecurityCallbacks ShellPrincipals::securityCallbacks = { 1.250 + nullptr, // contentSecurityPolicyAllows 1.251 + subsumes 1.252 +}; 1.253 + 1.254 +// The fully-trusted principal subsumes all other principals. 1.255 +ShellPrincipals ShellPrincipals::fullyTrusted(-1, 1); 1.256 + 1.257 +#ifdef EDITLINE 1.258 +extern "C" { 1.259 +extern JS_EXPORT_API(char *) readline(const char *prompt); 1.260 +extern JS_EXPORT_API(void) add_history(char *line); 1.261 +} // extern "C" 1.262 +#endif 1.263 + 1.264 +static char * 1.265 +GetLine(FILE *file, const char * prompt) 1.266 +{ 1.267 + size_t size; 1.268 + char *buffer; 1.269 +#ifdef EDITLINE 1.270 + /* 1.271 + * Use readline only if file is stdin, because there's no way to specify 1.272 + * another handle. Are other filehandles interactive? 1.273 + */ 1.274 + if (file == stdin) { 1.275 + char *linep = readline(prompt); 1.276 + /* 1.277 + * We set it to zero to avoid complaining about inappropriate ioctl 1.278 + * for device in the case of EOF. Looks like errno == 251 if line is 1.279 + * finished with EOF and errno == 25 (EINVAL on Mac) if there is 1.280 + * nothing left to read. 1.281 + */ 1.282 + if (errno == 251 || errno == 25 || errno == EINVAL) 1.283 + errno = 0; 1.284 + if (!linep) 1.285 + return nullptr; 1.286 + if (linep[0] != '\0') 1.287 + add_history(linep); 1.288 + return linep; 1.289 + } 1.290 +#endif 1.291 + size_t len = 0; 1.292 + if (*prompt != '\0') { 1.293 + fprintf(gOutFile, "%s", prompt); 1.294 + fflush(gOutFile); 1.295 + } 1.296 + size = 80; 1.297 + buffer = (char *) malloc(size); 1.298 + if (!buffer) 1.299 + return nullptr; 1.300 + char *current = buffer; 1.301 + while (fgets(current, size - len, file)) { 1.302 + len += strlen(current); 1.303 + char *t = buffer + len - 1; 1.304 + if (*t == '\n') { 1.305 + /* Line was read. We remove '\n' and exit. */ 1.306 + *t = '\0'; 1.307 + return buffer; 1.308 + } 1.309 + if (len + 1 == size) { 1.310 + size = size * 2; 1.311 + char *tmp = (char *) js_realloc(buffer, size); 1.312 + if (!tmp) { 1.313 + free(buffer); 1.314 + return nullptr; 1.315 + } 1.316 + buffer = tmp; 1.317 + } 1.318 + current = buffer + len; 1.319 + } 1.320 + if (len && !ferror(file)) 1.321 + return buffer; 1.322 + free(buffer); 1.323 + return nullptr; 1.324 +} 1.325 + 1.326 +static char * 1.327 +JSStringToUTF8(JSContext *cx, JSString *str) 1.328 +{ 1.329 + JSLinearString *linear = str->ensureLinear(cx); 1.330 + if (!linear) 1.331 + return nullptr; 1.332 + 1.333 + return TwoByteCharsToNewUTF8CharsZ(cx, linear->range()).c_str(); 1.334 +} 1.335 + 1.336 +/* State to store as JSContext private. */ 1.337 +struct JSShellContextData { 1.338 + /* Creation timestamp, used by the elapsed() shell builtin. */ 1.339 + int64_t startTime; 1.340 +}; 1.341 + 1.342 +static JSShellContextData * 1.343 +NewContextData() 1.344 +{ 1.345 + /* Prevent creation of new contexts after we have been canceled. */ 1.346 + if (gServiceInterrupt) 1.347 + return nullptr; 1.348 + 1.349 + JSShellContextData *data = (JSShellContextData *) 1.350 + js_calloc(sizeof(JSShellContextData), 1); 1.351 + if (!data) 1.352 + return nullptr; 1.353 + data->startTime = PRMJ_Now(); 1.354 + return data; 1.355 +} 1.356 + 1.357 +static inline JSShellContextData * 1.358 +GetContextData(JSContext *cx) 1.359 +{ 1.360 + JSShellContextData *data = (JSShellContextData *) JS_GetContextPrivate(cx); 1.361 + 1.362 + JS_ASSERT(data); 1.363 + return data; 1.364 +} 1.365 + 1.366 +static bool 1.367 +ShellInterruptCallback(JSContext *cx) 1.368 +{ 1.369 + if (!gServiceInterrupt) 1.370 + return true; 1.371 + 1.372 + bool result; 1.373 + RootedValue interruptFunc(cx, gInterruptFunc.ref()); 1.374 + if (!interruptFunc.isNull()) { 1.375 + JS::AutoSaveExceptionState savedExc(cx); 1.376 + JSAutoCompartment ac(cx, &interruptFunc.toObject()); 1.377 + RootedValue rval(cx); 1.378 + if (!JS_CallFunctionValue(cx, JS::NullPtr(), interruptFunc, 1.379 + JS::HandleValueArray::empty(), &rval)) 1.380 + { 1.381 + return false; 1.382 + } 1.383 + if (rval.isBoolean()) 1.384 + result = rval.toBoolean(); 1.385 + else 1.386 + result = false; 1.387 + } else { 1.388 + result = false; 1.389 + } 1.390 + 1.391 + if (!result && gExitCode == 0) 1.392 + gExitCode = EXITCODE_TIMEOUT; 1.393 + 1.394 + return result; 1.395 +} 1.396 + 1.397 +/* 1.398 + * Some UTF-8 files, notably those written using Notepad, have a Unicode 1.399 + * Byte-Order-Mark (BOM) as their first character. This is useless (byte-order 1.400 + * is meaningless for UTF-8) but causes a syntax error unless we skip it. 1.401 + */ 1.402 +static void 1.403 +SkipUTF8BOM(FILE* file) 1.404 +{ 1.405 + int ch1 = fgetc(file); 1.406 + int ch2 = fgetc(file); 1.407 + int ch3 = fgetc(file); 1.408 + 1.409 + // Skip the BOM 1.410 + if (ch1 == 0xEF && ch2 == 0xBB && ch3 == 0xBF) 1.411 + return; 1.412 + 1.413 + // No BOM - revert 1.414 + if (ch3 != EOF) 1.415 + ungetc(ch3, file); 1.416 + if (ch2 != EOF) 1.417 + ungetc(ch2, file); 1.418 + if (ch1 != EOF) 1.419 + ungetc(ch1, file); 1.420 +} 1.421 + 1.422 +static void 1.423 +RunFile(JSContext *cx, Handle<JSObject*> obj, const char *filename, FILE *file, bool compileOnly) 1.424 +{ 1.425 + SkipUTF8BOM(file); 1.426 + 1.427 + // To support the UNIX #! shell hack, gobble the first line if it starts 1.428 + // with '#'. 1.429 + int ch = fgetc(file); 1.430 + if (ch == '#') { 1.431 + while ((ch = fgetc(file)) != EOF) { 1.432 + if (ch == '\n' || ch == '\r') 1.433 + break; 1.434 + } 1.435 + } 1.436 + ungetc(ch, file); 1.437 + 1.438 + int64_t t1 = PRMJ_Now(); 1.439 + RootedScript script(cx); 1.440 + 1.441 + { 1.442 + JS::AutoSaveContextOptions asco(cx); 1.443 + JS::ContextOptionsRef(cx).setNoScriptRval(true); 1.444 + 1.445 + CompileOptions options(cx); 1.446 + options.setIntroductionType("js shell file") 1.447 + .setUTF8(true) 1.448 + .setFileAndLine(filename, 1) 1.449 + .setCompileAndGo(true); 1.450 + 1.451 + gGotError = false; 1.452 + script = JS::Compile(cx, obj, options, file); 1.453 + JS_ASSERT_IF(!script, gGotError); 1.454 + } 1.455 + 1.456 + #ifdef DEBUG 1.457 + if (dumpEntrainedVariables) 1.458 + AnalyzeEntrainedVariables(cx, script); 1.459 + #endif 1.460 + if (script && !compileOnly) { 1.461 + if (!JS_ExecuteScript(cx, obj, script)) { 1.462 + if (!gQuitting && !gServiceInterrupt) 1.463 + gExitCode = EXITCODE_RUNTIME_ERROR; 1.464 + } 1.465 + int64_t t2 = PRMJ_Now() - t1; 1.466 + if (printTiming) 1.467 + printf("runtime = %.3f ms\n", double(t2) / PRMJ_USEC_PER_MSEC); 1.468 + } 1.469 +} 1.470 + 1.471 +static bool 1.472 +EvalAndPrint(JSContext *cx, Handle<JSObject*> global, const char *bytes, size_t length, 1.473 + int lineno, bool compileOnly, FILE *out) 1.474 +{ 1.475 + // Eval. 1.476 + JS::CompileOptions options(cx); 1.477 + options.setIntroductionType("js shell interactive") 1.478 + .setUTF8(true) 1.479 + .setCompileAndGo(true) 1.480 + .setFileAndLine("typein", lineno); 1.481 + RootedScript script(cx); 1.482 + script = JS::Compile(cx, global, options, bytes, length); 1.483 + if (!script) 1.484 + return false; 1.485 + if (compileOnly) 1.486 + return true; 1.487 + RootedValue result(cx); 1.488 + if (!JS_ExecuteScript(cx, global, script, &result)) 1.489 + return false; 1.490 + 1.491 + if (!result.isUndefined()) { 1.492 + // Print. 1.493 + RootedString str(cx); 1.494 + str = JS_ValueToSource(cx, result); 1.495 + if (!str) 1.496 + return false; 1.497 + 1.498 + char *utf8chars = JSStringToUTF8(cx, str); 1.499 + if (!utf8chars) 1.500 + return false; 1.501 + fprintf(out, "%s\n", utf8chars); 1.502 + JS_free(cx, utf8chars); 1.503 + } 1.504 + return true; 1.505 +} 1.506 + 1.507 +static void 1.508 +ReadEvalPrintLoop(JSContext *cx, Handle<JSObject*> global, FILE *in, FILE *out, bool compileOnly) 1.509 +{ 1.510 + int lineno = 1; 1.511 + bool hitEOF = false; 1.512 + 1.513 + do { 1.514 + /* 1.515 + * Accumulate lines until we get a 'compilable unit' - one that either 1.516 + * generates an error (before running out of source) or that compiles 1.517 + * cleanly. This should be whenever we get a complete statement that 1.518 + * coincides with the end of a line. 1.519 + */ 1.520 + int startline = lineno; 1.521 + typedef Vector<char, 32, ContextAllocPolicy> CharBuffer; 1.522 + CharBuffer buffer(cx); 1.523 + do { 1.524 + ScheduleWatchdog(cx->runtime(), -1); 1.525 + gServiceInterrupt = false; 1.526 + errno = 0; 1.527 + 1.528 + char *line = GetLine(in, startline == lineno ? "js> " : ""); 1.529 + if (!line) { 1.530 + if (errno) { 1.531 + JS_ReportError(cx, strerror(errno)); 1.532 + return; 1.533 + } 1.534 + hitEOF = true; 1.535 + break; 1.536 + } 1.537 + 1.538 + if (!buffer.append(line, strlen(line)) || !buffer.append('\n')) 1.539 + return; 1.540 + 1.541 + lineno++; 1.542 + if (!ScheduleWatchdog(cx->runtime(), gTimeoutInterval)) { 1.543 + hitEOF = true; 1.544 + break; 1.545 + } 1.546 + } while (!JS_BufferIsCompilableUnit(cx, global, buffer.begin(), buffer.length())); 1.547 + 1.548 + if (hitEOF && buffer.empty()) 1.549 + break; 1.550 + 1.551 + if (!EvalAndPrint(cx, global, buffer.begin(), buffer.length(), startline, compileOnly, 1.552 + out)) 1.553 + { 1.554 + // Catch the error, report it, and keep going. 1.555 + JS_ReportPendingException(cx); 1.556 + } 1.557 + } while (!hitEOF && !gQuitting); 1.558 + 1.559 + fprintf(out, "\n"); 1.560 +} 1.561 + 1.562 +class AutoCloseInputFile 1.563 +{ 1.564 + private: 1.565 + FILE *f_; 1.566 + public: 1.567 + explicit AutoCloseInputFile(FILE *f) : f_(f) {} 1.568 + ~AutoCloseInputFile() { 1.569 + if (f_ && f_ != stdin) 1.570 + fclose(f_); 1.571 + } 1.572 +}; 1.573 + 1.574 +static void 1.575 +Process(JSContext *cx, JSObject *obj_, const char *filename, bool forceTTY) 1.576 +{ 1.577 + RootedObject obj(cx, obj_); 1.578 + 1.579 + FILE *file; 1.580 + if (forceTTY || !filename || strcmp(filename, "-") == 0) { 1.581 + file = stdin; 1.582 + } else { 1.583 + file = fopen(filename, "r"); 1.584 + if (!file) { 1.585 + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, 1.586 + JSSMSG_CANT_OPEN, filename, strerror(errno)); 1.587 + gExitCode = EXITCODE_FILE_NOT_FOUND; 1.588 + return; 1.589 + } 1.590 + } 1.591 + AutoCloseInputFile autoClose(file); 1.592 + 1.593 + if (!forceTTY && !isatty(fileno(file))) { 1.594 + // It's not interactive - just execute it. 1.595 + RunFile(cx, obj, filename, file, compileOnly); 1.596 + } else { 1.597 + // It's an interactive filehandle; drop into read-eval-print loop. 1.598 + ReadEvalPrintLoop(cx, obj, file, gOutFile, compileOnly); 1.599 + } 1.600 +} 1.601 + 1.602 +static bool 1.603 +Version(JSContext *cx, unsigned argc, jsval *vp) 1.604 +{ 1.605 + CallArgs args = CallArgsFromVp(argc, vp); 1.606 + JSVersion origVersion = JS_GetVersion(cx); 1.607 + if (args.length() == 0 || JSVAL_IS_VOID(args[0])) { 1.608 + /* Get version. */ 1.609 + args.rval().setInt32(origVersion); 1.610 + } else { 1.611 + /* Set version. */ 1.612 + int32_t v = -1; 1.613 + if (args[0].isInt32()) { 1.614 + v = args[0].toInt32(); 1.615 + } else if (args[0].isDouble()) { 1.616 + double fv = args[0].toDouble(); 1.617 + int32_t fvi; 1.618 + if (NumberEqualsInt32(fv, &fvi)) 1.619 + v = fvi; 1.620 + } 1.621 + if (v < 0 || v > JSVERSION_LATEST) { 1.622 + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS, "version"); 1.623 + return false; 1.624 + } 1.625 + JS_SetVersionForCompartment(js::GetContextCompartment(cx), JSVersion(v)); 1.626 + args.rval().setInt32(origVersion); 1.627 + } 1.628 + return true; 1.629 +} 1.630 + 1.631 +/* 1.632 + * Resolve a (possibly) relative filename to an absolute path. If 1.633 + * |scriptRelative| is true, then the result will be relative to the directory 1.634 + * containing the currently-running script, or the current working directory if 1.635 + * the currently-running script is "-e" (namely, you're using it from the 1.636 + * command line.) Otherwise, it will be relative to the current working 1.637 + * directory. 1.638 + */ 1.639 +static JSString * 1.640 +ResolvePath(JSContext *cx, HandleString filenameStr, PathResolutionMode resolveMode) 1.641 +{ 1.642 + JSAutoByteString filename(cx, filenameStr); 1.643 + if (!filename) 1.644 + return nullptr; 1.645 + 1.646 + const char *pathname = filename.ptr(); 1.647 + if (pathname[0] == '/') 1.648 + return filenameStr; 1.649 +#ifdef XP_WIN 1.650 + // Various forms of absolute paths per http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx 1.651 + // "\..." 1.652 + if (pathname[0] == '\\') 1.653 + return filenameStr; 1.654 + // "C:\..." 1.655 + if (strlen(pathname) > 3 && isalpha(pathname[0]) && pathname[1] == ':' && pathname[2] == '\\') 1.656 + return filenameStr; 1.657 + // "\\..." 1.658 + if (strlen(pathname) > 2 && pathname[1] == '\\' && pathname[2] == '\\') 1.659 + return filenameStr; 1.660 +#endif 1.661 + 1.662 + /* Get the currently executing script's name. */ 1.663 + JS::AutoFilename scriptFilename; 1.664 + if (!DescribeScriptedCaller(cx, &scriptFilename)) 1.665 + return nullptr; 1.666 + 1.667 + if (!scriptFilename.get()) 1.668 + return nullptr; 1.669 + 1.670 + if (strcmp(scriptFilename.get(), "-e") == 0 || strcmp(scriptFilename.get(), "typein") == 0) 1.671 + resolveMode = RootRelative; 1.672 + 1.673 + static char buffer[PATH_MAX+1]; 1.674 + if (resolveMode == ScriptRelative) { 1.675 +#ifdef XP_WIN 1.676 + // The docs say it can return EINVAL, but the compiler says it's void 1.677 + _splitpath(scriptFilename.get(), nullptr, buffer, nullptr, nullptr); 1.678 +#else 1.679 + strncpy(buffer, scriptFilename.get(), PATH_MAX+1); 1.680 + if (buffer[PATH_MAX] != '\0') 1.681 + return nullptr; 1.682 + 1.683 + // dirname(buffer) might return buffer, or it might return a 1.684 + // statically-allocated string 1.685 + memmove(buffer, dirname(buffer), strlen(buffer) + 1); 1.686 +#endif 1.687 + } else { 1.688 + const char *cwd = getcwd(buffer, PATH_MAX); 1.689 + if (!cwd) 1.690 + return nullptr; 1.691 + } 1.692 + 1.693 + size_t len = strlen(buffer); 1.694 + buffer[len] = '/'; 1.695 + strncpy(buffer + len + 1, pathname, sizeof(buffer) - (len+1)); 1.696 + if (buffer[PATH_MAX] != '\0') 1.697 + return nullptr; 1.698 + 1.699 + return JS_NewStringCopyZ(cx, buffer); 1.700 +} 1.701 + 1.702 +static bool 1.703 +CreateMappedArrayBuffer(JSContext *cx, unsigned argc, Value *vp) 1.704 +{ 1.705 + CallArgs args = CallArgsFromVp(argc, vp); 1.706 + 1.707 + if (args.length() < 1 || args.length() > 3) { 1.708 + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, 1.709 + args.length() < 1 ? JSSMSG_NOT_ENOUGH_ARGS : JSSMSG_TOO_MANY_ARGS, 1.710 + "createMappedArrayBuffer"); 1.711 + return false; 1.712 + } 1.713 + 1.714 + RootedString rawFilenameStr(cx, JS::ToString(cx, args[0])); 1.715 + if (!rawFilenameStr) 1.716 + return false; 1.717 + // It's a little bizarre to resolve relative to the script, but for testing 1.718 + // I need a file at a known location, and the only good way I know of to do 1.719 + // that right now is to include it in the repo alongside the test script. 1.720 + // Bug 944164 would introduce an alternative. 1.721 + JSString *filenameStr = ResolvePath(cx, rawFilenameStr, ScriptRelative); 1.722 + if (!filenameStr) 1.723 + return false; 1.724 + JSAutoByteString filename(cx, filenameStr); 1.725 + if (!filename) 1.726 + return false; 1.727 + 1.728 + uint32_t offset = 0; 1.729 + if (args.length() >= 2) { 1.730 + if (!JS::ToUint32(cx, args[1], &offset)) 1.731 + return false; 1.732 + } 1.733 + 1.734 + bool sizeGiven = false; 1.735 + uint32_t size; 1.736 + if (args.length() >= 3) { 1.737 + if (!JS::ToUint32(cx, args[2], &size)) 1.738 + return false; 1.739 + sizeGiven = true; 1.740 + if (offset > size) { 1.741 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, 1.742 + JSMSG_ARG_INDEX_OUT_OF_RANGE, "2"); 1.743 + return false; 1.744 + } 1.745 + } 1.746 + 1.747 + FILE *file = fopen(filename.ptr(), "r"); 1.748 + if (!file) { 1.749 + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, 1.750 + JSSMSG_CANT_OPEN, filename.ptr(), strerror(errno)); 1.751 + return false; 1.752 + } 1.753 + AutoCloseInputFile autoClose(file); 1.754 + 1.755 + if (!sizeGiven) { 1.756 + struct stat st; 1.757 + if (fstat(fileno(file), &st) < 0) { 1.758 + JS_ReportError(cx, "Unable to stat file"); 1.759 + return false; 1.760 + } 1.761 + if (st.st_size < offset) { 1.762 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, 1.763 + JSMSG_ARG_INDEX_OUT_OF_RANGE, "2"); 1.764 + return false; 1.765 + } 1.766 + size = st.st_size - offset; 1.767 + } 1.768 + 1.769 + void *contents = JS_CreateMappedArrayBufferContents(fileno(file), offset, size); 1.770 + if (!contents) { 1.771 + JS_ReportError(cx, "failed to allocate mapped array buffer contents (possibly due to bad alignment)"); 1.772 + return false; 1.773 + } 1.774 + 1.775 + RootedObject obj(cx, JS_NewMappedArrayBufferWithContents(cx, size, contents)); 1.776 + if (!obj) 1.777 + return false; 1.778 + 1.779 + args.rval().setObject(*obj); 1.780 + return true; 1.781 +} 1.782 + 1.783 +static bool 1.784 +Options(JSContext *cx, unsigned argc, jsval *vp) 1.785 +{ 1.786 + CallArgs args = CallArgsFromVp(argc, vp); 1.787 + 1.788 + JS::ContextOptions oldContextOptions = JS::ContextOptionsRef(cx); 1.789 + for (unsigned i = 0; i < args.length(); i++) { 1.790 + JSString *str = JS::ToString(cx, args[i]); 1.791 + if (!str) 1.792 + return false; 1.793 + args[i].setString(str); 1.794 + 1.795 + JSAutoByteString opt(cx, str); 1.796 + if (!opt) 1.797 + return false; 1.798 + 1.799 + if (strcmp(opt.ptr(), "strict") == 0) 1.800 + JS::ContextOptionsRef(cx).toggleExtraWarnings(); 1.801 + else if (strcmp(opt.ptr(), "werror") == 0) 1.802 + JS::ContextOptionsRef(cx).toggleWerror(); 1.803 + else if (strcmp(opt.ptr(), "strict_mode") == 0) 1.804 + JS::ContextOptionsRef(cx).toggleStrictMode(); 1.805 + else { 1.806 + char* msg = JS_sprintf_append(nullptr, 1.807 + "unknown option name '%s'." 1.808 + " The valid names are strict," 1.809 + " werror, and strict_mode.", 1.810 + opt.ptr()); 1.811 + if (!msg) { 1.812 + JS_ReportOutOfMemory(cx); 1.813 + return false; 1.814 + } 1.815 + 1.816 + JS_ReportError(cx, msg); 1.817 + free(msg); 1.818 + return false; 1.819 + } 1.820 + } 1.821 + 1.822 + char *names = strdup(""); 1.823 + bool found = false; 1.824 + if (!names && oldContextOptions.extraWarnings()) { 1.825 + names = JS_sprintf_append(names, "%s%s", found ? "," : "", "strict"); 1.826 + found = true; 1.827 + } 1.828 + if (!names && oldContextOptions.werror()) { 1.829 + names = JS_sprintf_append(names, "%s%s", found ? "," : "", "werror"); 1.830 + found = true; 1.831 + } 1.832 + if (!names && oldContextOptions.strictMode()) { 1.833 + names = JS_sprintf_append(names, "%s%s", found ? "," : "", "strict_mode"); 1.834 + found = true; 1.835 + } 1.836 + if (!names) { 1.837 + JS_ReportOutOfMemory(cx); 1.838 + return false; 1.839 + } 1.840 + 1.841 + JSString *str = JS_NewStringCopyZ(cx, names); 1.842 + free(names); 1.843 + if (!str) 1.844 + return false; 1.845 + args.rval().setString(str); 1.846 + return true; 1.847 +} 1.848 + 1.849 +static bool 1.850 +LoadScript(JSContext *cx, unsigned argc, jsval *vp, bool scriptRelative) 1.851 +{ 1.852 + CallArgs args = CallArgsFromVp(argc, vp); 1.853 + RootedObject thisobj(cx, JS_THIS_OBJECT(cx, vp)); 1.854 + if (!thisobj) 1.855 + return false; 1.856 + 1.857 + RootedString str(cx); 1.858 + for (unsigned i = 0; i < args.length(); i++) { 1.859 + str = JS::ToString(cx, args[i]); 1.860 + if (!str) { 1.861 + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS, "load"); 1.862 + return false; 1.863 + } 1.864 + str = ResolvePath(cx, str, scriptRelative ? ScriptRelative : RootRelative); 1.865 + if (!str) { 1.866 + JS_ReportError(cx, "unable to resolve path"); 1.867 + return false; 1.868 + } 1.869 + JSAutoByteString filename(cx, str); 1.870 + if (!filename) 1.871 + return false; 1.872 + errno = 0; 1.873 + CompileOptions opts(cx); 1.874 + opts.setIntroductionType("js shell load") 1.875 + .setUTF8(true) 1.876 + .setCompileAndGo(true) 1.877 + .setNoScriptRval(true); 1.878 + if ((compileOnly && !Compile(cx, thisobj, opts, filename.ptr())) || 1.879 + !Evaluate(cx, thisobj, opts, filename.ptr())) 1.880 + { 1.881 + return false; 1.882 + } 1.883 + } 1.884 + 1.885 + args.rval().setUndefined(); 1.886 + return true; 1.887 +} 1.888 + 1.889 +static bool 1.890 +Load(JSContext *cx, unsigned argc, jsval *vp) 1.891 +{ 1.892 + return LoadScript(cx, argc, vp, false); 1.893 +} 1.894 + 1.895 +static bool 1.896 +LoadScriptRelativeToScript(JSContext *cx, unsigned argc, jsval *vp) 1.897 +{ 1.898 + return LoadScript(cx, argc, vp, true); 1.899 +} 1.900 + 1.901 +// Populate |options| with the options given by |opts|'s properties. If we 1.902 +// need to convert a filename to a C string, let fileNameBytes own the 1.903 +// bytes. 1.904 +static bool 1.905 +ParseCompileOptions(JSContext *cx, CompileOptions &options, HandleObject opts, 1.906 + JSAutoByteString &fileNameBytes) 1.907 +{ 1.908 + RootedValue v(cx); 1.909 + RootedString s(cx); 1.910 + 1.911 + if (!JS_GetProperty(cx, opts, "compileAndGo", &v)) 1.912 + return false; 1.913 + if (!v.isUndefined()) 1.914 + options.setCompileAndGo(ToBoolean(v)); 1.915 + 1.916 + if (!JS_GetProperty(cx, opts, "noScriptRval", &v)) 1.917 + return false; 1.918 + if (!v.isUndefined()) 1.919 + options.setNoScriptRval(ToBoolean(v)); 1.920 + 1.921 + if (!JS_GetProperty(cx, opts, "fileName", &v)) 1.922 + return false; 1.923 + if (v.isNull()) { 1.924 + options.setFile(nullptr); 1.925 + } else if (!v.isUndefined()) { 1.926 + s = ToString(cx, v); 1.927 + if (!s) 1.928 + return false; 1.929 + char *fileName = fileNameBytes.encodeLatin1(cx, s); 1.930 + if (!fileName) 1.931 + return false; 1.932 + options.setFile(fileName); 1.933 + } 1.934 + 1.935 + if (!JS_GetProperty(cx, opts, "element", &v)) 1.936 + return false; 1.937 + if (v.isObject()) 1.938 + options.setElement(&v.toObject()); 1.939 + 1.940 + if (!JS_GetProperty(cx, opts, "elementAttributeName", &v)) 1.941 + return false; 1.942 + if (!v.isUndefined()) { 1.943 + s = ToString(cx, v); 1.944 + if (!s) 1.945 + return false; 1.946 + options.setElementAttributeName(s); 1.947 + } 1.948 + 1.949 + if (!JS_GetProperty(cx, opts, "lineNumber", &v)) 1.950 + return false; 1.951 + if (!v.isUndefined()) { 1.952 + uint32_t u; 1.953 + if (!ToUint32(cx, v, &u)) 1.954 + return false; 1.955 + options.setLine(u); 1.956 + } 1.957 + 1.958 + if (!JS_GetProperty(cx, opts, "sourceIsLazy", &v)) 1.959 + return false; 1.960 + if (v.isBoolean()) 1.961 + options.setSourceIsLazy(v.toBoolean()); 1.962 + 1.963 + return true; 1.964 +} 1.965 + 1.966 +class AutoNewContext 1.967 +{ 1.968 + private: 1.969 + JSContext *oldcx; 1.970 + JSContext *newcx; 1.971 + Maybe<JSAutoRequest> newRequest; 1.972 + Maybe<AutoCompartment> newCompartment; 1.973 + 1.974 + AutoNewContext(const AutoNewContext &) MOZ_DELETE; 1.975 + 1.976 + public: 1.977 + AutoNewContext() : oldcx(nullptr), newcx(nullptr) {} 1.978 + 1.979 + bool enter(JSContext *cx) { 1.980 + JS_ASSERT(!JS_IsExceptionPending(cx)); 1.981 + oldcx = cx; 1.982 + newcx = NewContext(JS_GetRuntime(cx)); 1.983 + if (!newcx) 1.984 + return false; 1.985 + JS::ContextOptionsRef(newcx).setDontReportUncaught(true); 1.986 + js::SetDefaultObjectForContext(newcx, JS::CurrentGlobalOrNull(cx)); 1.987 + 1.988 + newRequest.construct(newcx); 1.989 + newCompartment.construct(newcx, JS::CurrentGlobalOrNull(cx)); 1.990 + return true; 1.991 + } 1.992 + 1.993 + JSContext *get() { return newcx; } 1.994 + 1.995 + ~AutoNewContext() { 1.996 + if (newcx) { 1.997 + RootedValue exc(oldcx); 1.998 + bool throwing = JS_IsExceptionPending(newcx); 1.999 + if (throwing) 1.1000 + JS_GetPendingException(newcx, &exc); 1.1001 + newCompartment.destroy(); 1.1002 + newRequest.destroy(); 1.1003 + if (throwing) 1.1004 + JS_SetPendingException(oldcx, exc); 1.1005 + DestroyContext(newcx, false); 1.1006 + } 1.1007 + } 1.1008 +}; 1.1009 + 1.1010 +static const uint32_t CacheEntry_SOURCE = 0; 1.1011 +static const uint32_t CacheEntry_BYTECODE = 1; 1.1012 + 1.1013 +static const JSClass CacheEntry_class = { 1.1014 + "CacheEntryObject", JSCLASS_HAS_RESERVED_SLOTS(2), 1.1015 + JS_PropertyStub, /* addProperty */ 1.1016 + JS_DeletePropertyStub, /* delProperty */ 1.1017 + JS_PropertyStub, /* getProperty */ 1.1018 + JS_StrictPropertyStub, /* setProperty */ 1.1019 + JS_EnumerateStub, 1.1020 + JS_ResolveStub, 1.1021 + JS_ConvertStub, 1.1022 + nullptr, /* finalize */ 1.1023 + nullptr, /* call */ 1.1024 + nullptr, /* hasInstance */ 1.1025 + nullptr, /* construct */ 1.1026 + nullptr, /* trace */ 1.1027 + JSCLASS_NO_INTERNAL_MEMBERS 1.1028 +}; 1.1029 + 1.1030 +static bool 1.1031 +CacheEntry(JSContext* cx, unsigned argc, JS::Value *vp) 1.1032 +{ 1.1033 + CallArgs args = CallArgsFromVp(argc, vp); 1.1034 + 1.1035 + if (args.length() != 1 || !args[0].isString()) { 1.1036 + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS, "CacheEntry"); 1.1037 + return false; 1.1038 + } 1.1039 + 1.1040 + RootedObject obj(cx, JS_NewObject(cx, &CacheEntry_class, JS::NullPtr(), JS::NullPtr())); 1.1041 + if (!obj) 1.1042 + return false; 1.1043 + 1.1044 + SetReservedSlot(obj, CacheEntry_SOURCE, args[0]); 1.1045 + SetReservedSlot(obj, CacheEntry_BYTECODE, UndefinedValue()); 1.1046 + args.rval().setObject(*obj); 1.1047 + return true; 1.1048 +} 1.1049 + 1.1050 +static bool 1.1051 +CacheEntry_isCacheEntry(JSObject *cache) 1.1052 +{ 1.1053 + return JS_GetClass(cache) == &CacheEntry_class; 1.1054 +} 1.1055 + 1.1056 +static JSString * 1.1057 +CacheEntry_getSource(HandleObject cache) 1.1058 +{ 1.1059 + JS_ASSERT(CacheEntry_isCacheEntry(cache)); 1.1060 + Value v = JS_GetReservedSlot(cache, CacheEntry_SOURCE); 1.1061 + if (!v.isString()) 1.1062 + return nullptr; 1.1063 + 1.1064 + return v.toString(); 1.1065 +} 1.1066 + 1.1067 +static uint8_t * 1.1068 +CacheEntry_getBytecode(HandleObject cache, uint32_t *length) 1.1069 +{ 1.1070 + JS_ASSERT(CacheEntry_isCacheEntry(cache)); 1.1071 + Value v = JS_GetReservedSlot(cache, CacheEntry_BYTECODE); 1.1072 + if (!v.isObject() || !v.toObject().is<ArrayBufferObject>()) 1.1073 + return nullptr; 1.1074 + 1.1075 + ArrayBufferObject *arrayBuffer = &v.toObject().as<ArrayBufferObject>(); 1.1076 + *length = arrayBuffer->byteLength(); 1.1077 + return arrayBuffer->dataPointer(); 1.1078 +} 1.1079 + 1.1080 +static bool 1.1081 +CacheEntry_setBytecode(JSContext *cx, HandleObject cache, uint8_t *buffer, uint32_t length) 1.1082 +{ 1.1083 + JS_ASSERT(CacheEntry_isCacheEntry(cache)); 1.1084 + Rooted<ArrayBufferObject*> arrayBuffer(cx, ArrayBufferObject::create(cx, length, buffer)); 1.1085 + 1.1086 + if (!arrayBuffer || !ArrayBufferObject::ensureNonInline(cx, arrayBuffer)) 1.1087 + return false; 1.1088 + 1.1089 + SetReservedSlot(cache, CacheEntry_BYTECODE, OBJECT_TO_JSVAL(arrayBuffer)); 1.1090 + return true; 1.1091 +} 1.1092 + 1.1093 +class AutoSaveFrameChain 1.1094 +{ 1.1095 + JSContext *cx_; 1.1096 + bool saved_; 1.1097 + 1.1098 + public: 1.1099 + AutoSaveFrameChain(JSContext *cx) 1.1100 + : cx_(cx), 1.1101 + saved_(false) 1.1102 + {} 1.1103 + 1.1104 + bool save() { 1.1105 + if (!JS_SaveFrameChain(cx_)) 1.1106 + return false; 1.1107 + saved_ = true; 1.1108 + return true; 1.1109 + } 1.1110 + 1.1111 + ~AutoSaveFrameChain() { 1.1112 + if (saved_) 1.1113 + JS_RestoreFrameChain(cx_); 1.1114 + } 1.1115 +}; 1.1116 + 1.1117 +static bool 1.1118 +Evaluate(JSContext *cx, unsigned argc, jsval *vp) 1.1119 +{ 1.1120 + CallArgs args = CallArgsFromVp(argc, vp); 1.1121 + 1.1122 + if (args.length() < 1 || args.length() > 2) { 1.1123 + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, 1.1124 + args.length() < 1 ? JSSMSG_NOT_ENOUGH_ARGS : JSSMSG_TOO_MANY_ARGS, 1.1125 + "evaluate"); 1.1126 + return false; 1.1127 + } 1.1128 + 1.1129 + RootedString code(cx, nullptr); 1.1130 + RootedObject cacheEntry(cx, nullptr); 1.1131 + if (args[0].isString()) { 1.1132 + code = args[0].toString(); 1.1133 + } else if (args[0].isObject() && CacheEntry_isCacheEntry(&args[0].toObject())) { 1.1134 + cacheEntry = &args[0].toObject(); 1.1135 + code = CacheEntry_getSource(cacheEntry); 1.1136 + } 1.1137 + 1.1138 + if (!code || (args.length() == 2 && args[1].isPrimitive())) { 1.1139 + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS, "evaluate"); 1.1140 + return false; 1.1141 + } 1.1142 + 1.1143 + CompileOptions options(cx); 1.1144 + JSAutoByteString fileNameBytes; 1.1145 + bool newContext = false; 1.1146 + RootedString displayURL(cx); 1.1147 + RootedString sourceMapURL(cx); 1.1148 + RootedObject global(cx, nullptr); 1.1149 + bool catchTermination = false; 1.1150 + bool saveFrameChain = false; 1.1151 + bool loadBytecode = false; 1.1152 + bool saveBytecode = false; 1.1153 + bool assertEqBytecode = false; 1.1154 + RootedObject callerGlobal(cx, cx->global()); 1.1155 + 1.1156 + options.setIntroductionType("js shell evaluate") 1.1157 + .setFileAndLine("@evaluate", 1); 1.1158 + 1.1159 + global = JS_GetGlobalForObject(cx, &args.callee()); 1.1160 + if (!global) 1.1161 + return false; 1.1162 + 1.1163 + if (args.length() == 2) { 1.1164 + RootedObject opts(cx, &args[1].toObject()); 1.1165 + RootedValue v(cx); 1.1166 + 1.1167 + if (!ParseCompileOptions(cx, options, opts, fileNameBytes)) 1.1168 + return false; 1.1169 + 1.1170 + if (!JS_GetProperty(cx, opts, "newContext", &v)) 1.1171 + return false; 1.1172 + if (!v.isUndefined()) 1.1173 + newContext = ToBoolean(v); 1.1174 + 1.1175 + if (!JS_GetProperty(cx, opts, "displayURL", &v)) 1.1176 + return false; 1.1177 + if (!v.isUndefined()) { 1.1178 + displayURL = ToString(cx, v); 1.1179 + if (!displayURL) 1.1180 + return false; 1.1181 + } 1.1182 + 1.1183 + if (!JS_GetProperty(cx, opts, "sourceMapURL", &v)) 1.1184 + return false; 1.1185 + if (!v.isUndefined()) { 1.1186 + sourceMapURL = ToString(cx, v); 1.1187 + if (!sourceMapURL) 1.1188 + return false; 1.1189 + } 1.1190 + 1.1191 + if (!JS_GetProperty(cx, opts, "global", &v)) 1.1192 + return false; 1.1193 + if (!v.isUndefined()) { 1.1194 + if (v.isObject()) { 1.1195 + global = js::UncheckedUnwrap(&v.toObject()); 1.1196 + if (!global) 1.1197 + return false; 1.1198 + } 1.1199 + if (!global || !(JS_GetClass(global)->flags & JSCLASS_IS_GLOBAL)) { 1.1200 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE, 1.1201 + "\"global\" passed to evaluate()", "not a global object"); 1.1202 + return false; 1.1203 + } 1.1204 + } 1.1205 + 1.1206 + if (!JS_GetProperty(cx, opts, "catchTermination", &v)) 1.1207 + return false; 1.1208 + if (!v.isUndefined()) 1.1209 + catchTermination = ToBoolean(v); 1.1210 + 1.1211 + if (!JS_GetProperty(cx, opts, "saveFrameChain", &v)) 1.1212 + return false; 1.1213 + if (!v.isUndefined()) 1.1214 + saveFrameChain = ToBoolean(v); 1.1215 + 1.1216 + if (!JS_GetProperty(cx, opts, "loadBytecode", &v)) 1.1217 + return false; 1.1218 + if (!v.isUndefined()) 1.1219 + loadBytecode = ToBoolean(v); 1.1220 + 1.1221 + if (!JS_GetProperty(cx, opts, "saveBytecode", &v)) 1.1222 + return false; 1.1223 + if (!v.isUndefined()) 1.1224 + saveBytecode = ToBoolean(v); 1.1225 + 1.1226 + if (!JS_GetProperty(cx, opts, "assertEqBytecode", &v)) 1.1227 + return false; 1.1228 + if (!v.isUndefined()) 1.1229 + assertEqBytecode = ToBoolean(v); 1.1230 + 1.1231 + // We cannot load or save the bytecode if we have no object where the 1.1232 + // bytecode cache is stored. 1.1233 + if (loadBytecode || saveBytecode) { 1.1234 + if (!cacheEntry) { 1.1235 + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS, 1.1236 + "evaluate"); 1.1237 + return false; 1.1238 + } 1.1239 + } 1.1240 + } 1.1241 + 1.1242 + size_t codeLength; 1.1243 + const jschar *codeChars = JS_GetStringCharsAndLength(cx, code, &codeLength); 1.1244 + if (!codeChars) 1.1245 + return false; 1.1246 + 1.1247 + AutoNewContext ancx; 1.1248 + if (newContext) { 1.1249 + if (!ancx.enter(cx)) 1.1250 + return false; 1.1251 + cx = ancx.get(); 1.1252 + } 1.1253 + 1.1254 + uint32_t loadLength = 0; 1.1255 + uint8_t *loadBuffer = nullptr; 1.1256 + uint32_t saveLength = 0; 1.1257 + ScopedJSFreePtr<uint8_t> saveBuffer; 1.1258 + 1.1259 + if (loadBytecode) { 1.1260 + loadBuffer = CacheEntry_getBytecode(cacheEntry, &loadLength); 1.1261 + if (!loadBuffer) 1.1262 + return false; 1.1263 + } 1.1264 + 1.1265 + { 1.1266 + AutoSaveFrameChain asfc(cx); 1.1267 + if (saveFrameChain && !asfc.save()) 1.1268 + return false; 1.1269 + 1.1270 + JSAutoCompartment ac(cx, global); 1.1271 + RootedScript script(cx); 1.1272 + 1.1273 + if (!options.wrap(cx, cx->compartment())) 1.1274 + return false; 1.1275 + 1.1276 + { 1.1277 + JS::AutoSaveContextOptions asco(cx); 1.1278 + JS::ContextOptionsRef(cx).setNoScriptRval(options.noScriptRval); 1.1279 + if (saveBytecode) { 1.1280 + if (!JS::CompartmentOptionsRef(cx).getSingletonsAsTemplates()) { 1.1281 + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, 1.1282 + JSSMSG_CACHE_SINGLETON_FAILED); 1.1283 + return false; 1.1284 + } 1.1285 + JS::CompartmentOptionsRef(cx).cloneSingletonsOverride().set(true); 1.1286 + } 1.1287 + 1.1288 + if (loadBytecode) { 1.1289 + script = JS_DecodeScript(cx, loadBuffer, loadLength, options.originPrincipals(cx)); 1.1290 + } else { 1.1291 + script = JS::Compile(cx, global, options, codeChars, codeLength); 1.1292 + } 1.1293 + 1.1294 + if (!script) 1.1295 + return false; 1.1296 + } 1.1297 + 1.1298 + if (displayURL && !script->scriptSource()->hasDisplayURL()) { 1.1299 + const jschar *durl = JS_GetStringCharsZ(cx, displayURL); 1.1300 + if (!durl) 1.1301 + return false; 1.1302 + if (!script->scriptSource()->setDisplayURL(cx, durl)) 1.1303 + return false; 1.1304 + } 1.1305 + if (sourceMapURL && !script->scriptSource()->hasSourceMapURL()) { 1.1306 + const jschar *smurl = JS_GetStringCharsZ(cx, sourceMapURL); 1.1307 + if (!smurl) 1.1308 + return false; 1.1309 + if (!script->scriptSource()->setSourceMapURL(cx, smurl)) 1.1310 + return false; 1.1311 + } 1.1312 + if (!JS_ExecuteScript(cx, global, script, args.rval())) { 1.1313 + if (catchTermination && !JS_IsExceptionPending(cx)) { 1.1314 + JSAutoCompartment ac1(cx, callerGlobal); 1.1315 + JSString *str = JS_NewStringCopyZ(cx, "terminated"); 1.1316 + if (!str) 1.1317 + return false; 1.1318 + args.rval().setString(str); 1.1319 + return true; 1.1320 + } 1.1321 + return false; 1.1322 + } 1.1323 + 1.1324 + if (saveBytecode) { 1.1325 + saveBuffer = reinterpret_cast<uint8_t *>(JS_EncodeScript(cx, script, &saveLength)); 1.1326 + if (!saveBuffer) 1.1327 + return false; 1.1328 + } 1.1329 + } 1.1330 + 1.1331 + if (saveBytecode) { 1.1332 + // If we are both loading and saving, we assert that we are going to 1.1333 + // replace the current bytecode by the same stream of bytes. 1.1334 + if (loadBytecode && assertEqBytecode) { 1.1335 + if (saveLength != loadLength) { 1.1336 + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_CACHE_EQ_SIZE_FAILED, 1.1337 + loadLength, saveLength); 1.1338 + } else if (!mozilla::PodEqual(loadBuffer, saveBuffer.get(), loadLength)) { 1.1339 + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, 1.1340 + JSSMSG_CACHE_EQ_CONTENT_FAILED); 1.1341 + } 1.1342 + } 1.1343 + 1.1344 + if (!CacheEntry_setBytecode(cx, cacheEntry, saveBuffer, saveLength)) 1.1345 + return false; 1.1346 + 1.1347 + saveBuffer.forget(); 1.1348 + } 1.1349 + 1.1350 + return JS_WrapValue(cx, args.rval()); 1.1351 +} 1.1352 + 1.1353 +static JSString * 1.1354 +FileAsString(JSContext *cx, const char *pathname) 1.1355 +{ 1.1356 + FILE *file; 1.1357 + RootedString str(cx); 1.1358 + size_t len, cc; 1.1359 + char *buf; 1.1360 + 1.1361 + file = fopen(pathname, "rb"); 1.1362 + if (!file) { 1.1363 + JS_ReportError(cx, "can't open %s: %s", pathname, strerror(errno)); 1.1364 + return nullptr; 1.1365 + } 1.1366 + AutoCloseInputFile autoClose(file); 1.1367 + 1.1368 + if (fseek(file, 0, SEEK_END) != 0) { 1.1369 + JS_ReportError(cx, "can't seek end of %s", pathname); 1.1370 + } else { 1.1371 + len = ftell(file); 1.1372 + if (fseek(file, 0, SEEK_SET) != 0) { 1.1373 + JS_ReportError(cx, "can't seek start of %s", pathname); 1.1374 + } else { 1.1375 + buf = (char*) JS_malloc(cx, len + 1); 1.1376 + if (buf) { 1.1377 + cc = fread(buf, 1, len, file); 1.1378 + if (cc != len) { 1.1379 + JS_ReportError(cx, "can't read %s: %s", pathname, 1.1380 + (ptrdiff_t(cc) < 0) ? strerror(errno) : "short read"); 1.1381 + } else { 1.1382 + jschar *ucbuf = 1.1383 + JS::UTF8CharsToNewTwoByteCharsZ(cx, JS::UTF8Chars(buf, len), &len).get(); 1.1384 + if (!ucbuf) { 1.1385 + JS_ReportError(cx, "Invalid UTF-8 in file '%s'", pathname); 1.1386 + gExitCode = EXITCODE_RUNTIME_ERROR; 1.1387 + return nullptr; 1.1388 + } 1.1389 + str = JS_NewUCStringCopyN(cx, ucbuf, len); 1.1390 + free(ucbuf); 1.1391 + } 1.1392 + JS_free(cx, buf); 1.1393 + } 1.1394 + } 1.1395 + } 1.1396 + 1.1397 + return str; 1.1398 +} 1.1399 + 1.1400 +static JSObject * 1.1401 +FileAsTypedArray(JSContext *cx, const char *pathname) 1.1402 +{ 1.1403 + FILE *file = fopen(pathname, "rb"); 1.1404 + if (!file) { 1.1405 + JS_ReportError(cx, "can't open %s: %s", pathname, strerror(errno)); 1.1406 + return nullptr; 1.1407 + } 1.1408 + AutoCloseInputFile autoClose(file); 1.1409 + 1.1410 + RootedObject obj(cx); 1.1411 + if (fseek(file, 0, SEEK_END) != 0) { 1.1412 + JS_ReportError(cx, "can't seek end of %s", pathname); 1.1413 + } else { 1.1414 + size_t len = ftell(file); 1.1415 + if (fseek(file, 0, SEEK_SET) != 0) { 1.1416 + JS_ReportError(cx, "can't seek start of %s", pathname); 1.1417 + } else { 1.1418 + obj = JS_NewUint8Array(cx, len); 1.1419 + if (!obj) 1.1420 + return nullptr; 1.1421 + char *buf = (char *) obj->as<TypedArrayObject>().viewData(); 1.1422 + size_t cc = fread(buf, 1, len, file); 1.1423 + if (cc != len) { 1.1424 + JS_ReportError(cx, "can't read %s: %s", pathname, 1.1425 + (ptrdiff_t(cc) < 0) ? strerror(errno) : "short read"); 1.1426 + obj = nullptr; 1.1427 + } 1.1428 + } 1.1429 + } 1.1430 + return obj; 1.1431 +} 1.1432 + 1.1433 +/* 1.1434 + * Function to run scripts and return compilation + execution time. Semantics 1.1435 + * are closely modelled after the equivalent function in WebKit, as this is used 1.1436 + * to produce benchmark timings by SunSpider. 1.1437 + */ 1.1438 +static bool 1.1439 +Run(JSContext *cx, unsigned argc, jsval *vp) 1.1440 +{ 1.1441 + CallArgs args = CallArgsFromVp(argc, vp); 1.1442 + if (args.length() != 1) { 1.1443 + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS, "run"); 1.1444 + return false; 1.1445 + } 1.1446 + 1.1447 + RootedObject thisobj(cx, JS_THIS_OBJECT(cx, vp)); 1.1448 + if (!thisobj) 1.1449 + return false; 1.1450 + 1.1451 + JSString *str = JS::ToString(cx, args[0]); 1.1452 + if (!str) 1.1453 + return false; 1.1454 + args[0].setString(str); 1.1455 + JSAutoByteString filename(cx, str); 1.1456 + if (!filename) 1.1457 + return false; 1.1458 + 1.1459 + const jschar *ucbuf = nullptr; 1.1460 + size_t buflen; 1.1461 + str = FileAsString(cx, filename.ptr()); 1.1462 + if (str) 1.1463 + ucbuf = JS_GetStringCharsAndLength(cx, str, &buflen); 1.1464 + if (!ucbuf) 1.1465 + return false; 1.1466 + 1.1467 + JS::Anchor<JSString *> a_str(str); 1.1468 + 1.1469 + RootedScript script(cx); 1.1470 + int64_t startClock = PRMJ_Now(); 1.1471 + { 1.1472 + JS::AutoSaveContextOptions asco(cx); 1.1473 + JS::ContextOptionsRef(cx).setNoScriptRval(true); 1.1474 + 1.1475 + JS::CompileOptions options(cx); 1.1476 + options.setIntroductionType("js shell run") 1.1477 + .setFileAndLine(filename.ptr(), 1) 1.1478 + .setCompileAndGo(true); 1.1479 + script = JS_CompileUCScript(cx, thisobj, ucbuf, buflen, options); 1.1480 + if (!script) 1.1481 + return false; 1.1482 + } 1.1483 + 1.1484 + if (!JS_ExecuteScript(cx, thisobj, script)) 1.1485 + return false; 1.1486 + 1.1487 + int64_t endClock = PRMJ_Now(); 1.1488 + 1.1489 + args.rval().setDouble((endClock - startClock) / double(PRMJ_USEC_PER_MSEC)); 1.1490 + return true; 1.1491 +} 1.1492 + 1.1493 +/* 1.1494 + * function readline() 1.1495 + * Provides a hook for scripts to read a line from stdin. 1.1496 + */ 1.1497 +static bool 1.1498 +ReadLine(JSContext *cx, unsigned argc, jsval *vp) 1.1499 +{ 1.1500 + CallArgs args = CallArgsFromVp(argc, vp); 1.1501 + 1.1502 +#define BUFSIZE 256 1.1503 + FILE *from = stdin; 1.1504 + size_t buflength = 0; 1.1505 + size_t bufsize = BUFSIZE; 1.1506 + char *buf = (char *) JS_malloc(cx, bufsize); 1.1507 + if (!buf) 1.1508 + return false; 1.1509 + 1.1510 + bool sawNewline = false; 1.1511 + size_t gotlength; 1.1512 + while ((gotlength = js_fgets(buf + buflength, bufsize - buflength, from)) > 0) { 1.1513 + buflength += gotlength; 1.1514 + 1.1515 + /* Are we done? */ 1.1516 + if (buf[buflength - 1] == '\n') { 1.1517 + buf[buflength - 1] = '\0'; 1.1518 + sawNewline = true; 1.1519 + break; 1.1520 + } else if (buflength < bufsize - 1) { 1.1521 + break; 1.1522 + } 1.1523 + 1.1524 + /* Else, grow our buffer for another pass. */ 1.1525 + char *tmp; 1.1526 + bufsize *= 2; 1.1527 + if (bufsize > buflength) { 1.1528 + tmp = (char *) JS_realloc(cx, buf, bufsize); 1.1529 + } else { 1.1530 + JS_ReportOutOfMemory(cx); 1.1531 + tmp = nullptr; 1.1532 + } 1.1533 + 1.1534 + if (!tmp) { 1.1535 + JS_free(cx, buf); 1.1536 + return false; 1.1537 + } 1.1538 + 1.1539 + buf = tmp; 1.1540 + } 1.1541 + 1.1542 + /* Treat the empty string specially. */ 1.1543 + if (buflength == 0) { 1.1544 + args.rval().set(feof(from) ? NullValue() : JS_GetEmptyStringValue(cx)); 1.1545 + JS_free(cx, buf); 1.1546 + return true; 1.1547 + } 1.1548 + 1.1549 + /* Shrink the buffer to the real size. */ 1.1550 + char *tmp = static_cast<char*>(JS_realloc(cx, buf, buflength)); 1.1551 + if (!tmp) { 1.1552 + JS_free(cx, buf); 1.1553 + return false; 1.1554 + } 1.1555 + 1.1556 + buf = tmp; 1.1557 + 1.1558 + /* 1.1559 + * Turn buf into a JSString. Note that buflength includes the trailing null 1.1560 + * character. 1.1561 + */ 1.1562 + JSString *str = JS_NewStringCopyN(cx, buf, sawNewline ? buflength - 1 : buflength); 1.1563 + JS_free(cx, buf); 1.1564 + if (!str) 1.1565 + return false; 1.1566 + 1.1567 + args.rval().setString(str); 1.1568 + return true; 1.1569 +} 1.1570 + 1.1571 +static bool 1.1572 +PutStr(JSContext *cx, unsigned argc, jsval *vp) 1.1573 +{ 1.1574 + CallArgs args = CallArgsFromVp(argc, vp); 1.1575 + 1.1576 + if (args.length() != 0) { 1.1577 + JSString *str = JS::ToString(cx, args[0]); 1.1578 + if (!str) 1.1579 + return false; 1.1580 + char *bytes = JSStringToUTF8(cx, str); 1.1581 + if (!bytes) 1.1582 + return false; 1.1583 + fputs(bytes, gOutFile); 1.1584 + JS_free(cx, bytes); 1.1585 + fflush(gOutFile); 1.1586 + } 1.1587 + 1.1588 + args.rval().setUndefined(); 1.1589 + return true; 1.1590 +} 1.1591 + 1.1592 +static bool 1.1593 +Now(JSContext *cx, unsigned argc, jsval *vp) 1.1594 +{ 1.1595 + CallArgs args = CallArgsFromVp(argc, vp); 1.1596 + double now = PRMJ_Now() / double(PRMJ_USEC_PER_MSEC); 1.1597 + args.rval().setDouble(now); 1.1598 + return true; 1.1599 +} 1.1600 + 1.1601 +static bool 1.1602 +PrintInternal(JSContext *cx, const CallArgs &args, FILE *file) 1.1603 +{ 1.1604 + for (unsigned i = 0; i < args.length(); i++) { 1.1605 + JSString *str = JS::ToString(cx, args[i]); 1.1606 + if (!str) 1.1607 + return false; 1.1608 + char *bytes = JSStringToUTF8(cx, str); 1.1609 + if (!bytes) 1.1610 + return false; 1.1611 + fprintf(file, "%s%s", i ? " " : "", bytes); 1.1612 + JS_free(cx, bytes); 1.1613 + } 1.1614 + 1.1615 + fputc('\n', file); 1.1616 + fflush(file); 1.1617 + 1.1618 + args.rval().setUndefined(); 1.1619 + return true; 1.1620 +} 1.1621 + 1.1622 +static bool 1.1623 +Print(JSContext *cx, unsigned argc, jsval *vp) 1.1624 +{ 1.1625 + CallArgs args = CallArgsFromVp(argc, vp); 1.1626 + return PrintInternal(cx, args, gOutFile); 1.1627 +} 1.1628 + 1.1629 +static bool 1.1630 +PrintErr(JSContext *cx, unsigned argc, jsval *vp) 1.1631 +{ 1.1632 + CallArgs args = CallArgsFromVp(argc, vp); 1.1633 + return PrintInternal(cx, args, gErrFile); 1.1634 +} 1.1635 + 1.1636 +static bool 1.1637 +Help(JSContext *cx, unsigned argc, jsval *vp); 1.1638 + 1.1639 +static bool 1.1640 +Quit(JSContext *cx, unsigned argc, jsval *vp) 1.1641 +{ 1.1642 +#ifdef JS_MORE_DETERMINISTIC 1.1643 + // Print a message to stderr in more-deterministic builds to help jsfunfuzz 1.1644 + // find uncatchable-exception bugs. 1.1645 + fprintf(stderr, "quit called\n"); 1.1646 +#endif 1.1647 + 1.1648 + CallArgs args = CallArgsFromVp(argc, vp); 1.1649 + JS_ConvertArguments(cx, args, "/ i", &gExitCode); 1.1650 + 1.1651 + gQuitting = true; 1.1652 + return false; 1.1653 +} 1.1654 + 1.1655 +static const char * 1.1656 +ToSource(JSContext *cx, MutableHandleValue vp, JSAutoByteString *bytes) 1.1657 +{ 1.1658 + JSString *str = JS_ValueToSource(cx, vp); 1.1659 + if (str) { 1.1660 + vp.setString(str); 1.1661 + if (bytes->encodeLatin1(cx, str)) 1.1662 + return bytes->ptr(); 1.1663 + } 1.1664 + JS_ClearPendingException(cx); 1.1665 + return "<<error converting value to string>>"; 1.1666 +} 1.1667 + 1.1668 +static bool 1.1669 +AssertEq(JSContext *cx, unsigned argc, jsval *vp) 1.1670 +{ 1.1671 + CallArgs args = CallArgsFromVp(argc, vp); 1.1672 + if (!(args.length() == 2 || (args.length() == 3 && args[2].isString()))) { 1.1673 + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, 1.1674 + (args.length() < 2) 1.1675 + ? JSSMSG_NOT_ENOUGH_ARGS 1.1676 + : (args.length() == 3) 1.1677 + ? JSSMSG_INVALID_ARGS 1.1678 + : JSSMSG_TOO_MANY_ARGS, 1.1679 + "assertEq"); 1.1680 + return false; 1.1681 + } 1.1682 + 1.1683 + bool same; 1.1684 + if (!JS_SameValue(cx, args[0], args[1], &same)) 1.1685 + return false; 1.1686 + if (!same) { 1.1687 + JSAutoByteString bytes0, bytes1; 1.1688 + const char *actual = ToSource(cx, args[0], &bytes0); 1.1689 + const char *expected = ToSource(cx, args[1], &bytes1); 1.1690 + if (args.length() == 2) { 1.1691 + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_ASSERT_EQ_FAILED, 1.1692 + actual, expected); 1.1693 + } else { 1.1694 + JSAutoByteString bytes2(cx, args[2].toString()); 1.1695 + if (!bytes2) 1.1696 + return false; 1.1697 + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_ASSERT_EQ_FAILED_MSG, 1.1698 + actual, expected, bytes2.ptr()); 1.1699 + } 1.1700 + return false; 1.1701 + } 1.1702 + args.rval().setUndefined(); 1.1703 + return true; 1.1704 +} 1.1705 + 1.1706 +static JSScript * 1.1707 +ValueToScript(JSContext *cx, jsval vArg, JSFunction **funp = nullptr) 1.1708 +{ 1.1709 + RootedValue v(cx, vArg); 1.1710 + RootedFunction fun(cx, JS_ValueToFunction(cx, v)); 1.1711 + if (!fun) 1.1712 + return nullptr; 1.1713 + 1.1714 + // Unwrap bound functions. 1.1715 + while (fun->isBoundFunction()) { 1.1716 + JSObject *target = fun->getBoundFunctionTarget(); 1.1717 + if (target && target->is<JSFunction>()) 1.1718 + fun = &target->as<JSFunction>(); 1.1719 + else 1.1720 + break; 1.1721 + } 1.1722 + 1.1723 + if (!fun->isInterpreted()) { 1.1724 + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_SCRIPTS_ONLY); 1.1725 + return nullptr; 1.1726 + } 1.1727 + 1.1728 + JSScript *script = fun->getOrCreateScript(cx); 1.1729 + if (!script) 1.1730 + return nullptr; 1.1731 + 1.1732 + if (fun && funp) 1.1733 + *funp = fun; 1.1734 + 1.1735 + return script; 1.1736 +} 1.1737 + 1.1738 +static bool 1.1739 +SetDebug(JSContext *cx, unsigned argc, jsval *vp) 1.1740 +{ 1.1741 + CallArgs args = CallArgsFromVp(argc, vp); 1.1742 + if (args.length() == 0 || !args[0].isBoolean()) { 1.1743 + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, 1.1744 + JSSMSG_NOT_ENOUGH_ARGS, "setDebug"); 1.1745 + return false; 1.1746 + } 1.1747 + 1.1748 + /* 1.1749 + * Debug mode can only be set when there is no JS code executing on the 1.1750 + * stack. Unfortunately, that currently means that this call will fail 1.1751 + * unless debug mode is already set to what you're trying to set it to. 1.1752 + * In the future, this restriction may be lifted. 1.1753 + */ 1.1754 + 1.1755 + bool ok = !!JS_SetDebugMode(cx, args[0].toBoolean()); 1.1756 + if (ok) 1.1757 + args.rval().setBoolean(true); 1.1758 + return ok; 1.1759 +} 1.1760 + 1.1761 +static JSScript * 1.1762 +GetTopScript(JSContext *cx) 1.1763 +{ 1.1764 + NonBuiltinScriptFrameIter iter(cx); 1.1765 + return iter.done() ? nullptr : iter.script(); 1.1766 +} 1.1767 + 1.1768 +static bool 1.1769 +GetScriptAndPCArgs(JSContext *cx, unsigned argc, jsval *argv, MutableHandleScript scriptp, 1.1770 + int32_t *ip) 1.1771 +{ 1.1772 + RootedScript script(cx, GetTopScript(cx)); 1.1773 + *ip = 0; 1.1774 + if (argc != 0) { 1.1775 + jsval v = argv[0]; 1.1776 + unsigned intarg = 0; 1.1777 + if (!JSVAL_IS_PRIMITIVE(v) && 1.1778 + JS_GetClass(&v.toObject()) == Jsvalify(&JSFunction::class_)) { 1.1779 + script = ValueToScript(cx, v); 1.1780 + if (!script) 1.1781 + return false; 1.1782 + intarg++; 1.1783 + } 1.1784 + if (argc > intarg) { 1.1785 + if (!JS::ToInt32(cx, HandleValue::fromMarkedLocation(&argv[intarg]), ip)) 1.1786 + return false; 1.1787 + if ((uint32_t)*ip >= script->length()) { 1.1788 + JS_ReportError(cx, "Invalid PC"); 1.1789 + return false; 1.1790 + } 1.1791 + } 1.1792 + } 1.1793 + 1.1794 + scriptp.set(script); 1.1795 + 1.1796 + return true; 1.1797 +} 1.1798 + 1.1799 +static JSTrapStatus 1.1800 +TrapHandler(JSContext *cx, JSScript *, jsbytecode *pc, jsval *rvalArg, 1.1801 + jsval closure) 1.1802 +{ 1.1803 + RootedString str(cx, JSVAL_TO_STRING(closure)); 1.1804 + RootedValue rval(cx, *rvalArg); 1.1805 + 1.1806 + ScriptFrameIter iter(cx); 1.1807 + JS_ASSERT(!iter.done()); 1.1808 + 1.1809 + /* Debug-mode currently disables Ion compilation. */ 1.1810 + JSAbstractFramePtr frame(iter.abstractFramePtr().raw(), iter.pc()); 1.1811 + RootedScript script(cx, iter.script()); 1.1812 + 1.1813 + size_t length; 1.1814 + const jschar *chars = JS_GetStringCharsAndLength(cx, str, &length); 1.1815 + if (!chars) 1.1816 + return JSTRAP_ERROR; 1.1817 + 1.1818 + if (!frame.evaluateUCInStackFrame(cx, chars, length, 1.1819 + script->filename(), 1.1820 + script->lineno(), 1.1821 + &rval)) 1.1822 + { 1.1823 + *rvalArg = rval; 1.1824 + return JSTRAP_ERROR; 1.1825 + } 1.1826 + *rvalArg = rval; 1.1827 + if (!rval.isUndefined()) 1.1828 + return JSTRAP_RETURN; 1.1829 + return JSTRAP_CONTINUE; 1.1830 +} 1.1831 + 1.1832 +static bool 1.1833 +Trap(JSContext *cx, unsigned argc, jsval *vp) 1.1834 +{ 1.1835 + CallArgs args = CallArgsFromVp(argc, vp); 1.1836 + RootedScript script(cx); 1.1837 + int32_t i; 1.1838 + 1.1839 + if (args.length() == 0) { 1.1840 + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_TRAP_USAGE); 1.1841 + return false; 1.1842 + } 1.1843 + argc = args.length() - 1; 1.1844 + RootedString str(cx, JS::ToString(cx, args[argc])); 1.1845 + if (!str) 1.1846 + return false; 1.1847 + args[argc].setString(str); 1.1848 + if (!GetScriptAndPCArgs(cx, argc, args.array(), &script, &i)) 1.1849 + return false; 1.1850 + args.rval().setUndefined(); 1.1851 + RootedValue strValue(cx, StringValue(str)); 1.1852 + return JS_SetTrap(cx, script, script->offsetToPC(i), TrapHandler, strValue); 1.1853 +} 1.1854 + 1.1855 +static bool 1.1856 +Untrap(JSContext *cx, unsigned argc, jsval *vp) 1.1857 +{ 1.1858 + CallArgs args = CallArgsFromVp(argc, vp); 1.1859 + RootedScript script(cx); 1.1860 + int32_t i; 1.1861 + 1.1862 + if (!GetScriptAndPCArgs(cx, args.length(), args.array(), &script, &i)) 1.1863 + return false; 1.1864 + JS_ClearTrap(cx, script, script->offsetToPC(i), nullptr, nullptr); 1.1865 + args.rval().setUndefined(); 1.1866 + return true; 1.1867 +} 1.1868 + 1.1869 +static JSTrapStatus 1.1870 +DebuggerAndThrowHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval, 1.1871 + void *closure) 1.1872 +{ 1.1873 + return TrapHandler(cx, script, pc, rval, STRING_TO_JSVAL((JSString *)closure)); 1.1874 +} 1.1875 + 1.1876 +static bool 1.1877 +SetDebuggerHandler(JSContext *cx, unsigned argc, jsval *vp) 1.1878 +{ 1.1879 + CallArgs args = CallArgsFromVp(argc, vp); 1.1880 + if (args.length() == 0) { 1.1881 + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, 1.1882 + JSSMSG_NOT_ENOUGH_ARGS, "setDebuggerHandler"); 1.1883 + return false; 1.1884 + } 1.1885 + 1.1886 + JSString *str = JS::ToString(cx, args[0]); 1.1887 + if (!str) 1.1888 + return false; 1.1889 + 1.1890 + JS_SetDebuggerHandler(cx->runtime(), DebuggerAndThrowHandler, str); 1.1891 + args.rval().setUndefined(); 1.1892 + return true; 1.1893 +} 1.1894 + 1.1895 +static bool 1.1896 +SetThrowHook(JSContext *cx, unsigned argc, jsval *vp) 1.1897 +{ 1.1898 + CallArgs args = CallArgsFromVp(argc, vp); 1.1899 + JSString *str; 1.1900 + if (args.length() == 0) { 1.1901 + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, 1.1902 + JSSMSG_NOT_ENOUGH_ARGS, "setThrowHook"); 1.1903 + return false; 1.1904 + } 1.1905 + 1.1906 + str = JS::ToString(cx, args[0]); 1.1907 + if (!str) 1.1908 + return false; 1.1909 + 1.1910 + JS_SetThrowHook(cx->runtime(), DebuggerAndThrowHandler, str); 1.1911 + args.rval().setUndefined(); 1.1912 + return true; 1.1913 +} 1.1914 + 1.1915 +static bool 1.1916 +LineToPC(JSContext *cx, unsigned argc, jsval *vp) 1.1917 +{ 1.1918 + CallArgs args = CallArgsFromVp(argc, vp); 1.1919 + 1.1920 + if (args.length() == 0) { 1.1921 + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_LINE2PC_USAGE); 1.1922 + return false; 1.1923 + } 1.1924 + 1.1925 + RootedScript script(cx, GetTopScript(cx)); 1.1926 + int32_t lineArg = 0; 1.1927 + if (args[0].isObject() && args[0].toObject().is<JSFunction>()) { 1.1928 + script = ValueToScript(cx, args[0]); 1.1929 + if (!script) 1.1930 + return false; 1.1931 + lineArg++; 1.1932 + } 1.1933 + 1.1934 + uint32_t lineno; 1.1935 + if (!ToUint32(cx, args.get(lineArg), &lineno)) 1.1936 + return false; 1.1937 + 1.1938 + jsbytecode *pc = JS_LineNumberToPC(cx, script, lineno); 1.1939 + if (!pc) 1.1940 + return false; 1.1941 + args.rval().setInt32(script->pcToOffset(pc)); 1.1942 + return true; 1.1943 +} 1.1944 + 1.1945 +static bool 1.1946 +PCToLine(JSContext *cx, unsigned argc, jsval *vp) 1.1947 +{ 1.1948 + CallArgs args = CallArgsFromVp(argc, vp); 1.1949 + RootedScript script(cx); 1.1950 + int32_t i; 1.1951 + unsigned lineno; 1.1952 + 1.1953 + if (!GetScriptAndPCArgs(cx, args.length(), args.array(), &script, &i)) 1.1954 + return false; 1.1955 + lineno = JS_PCToLineNumber(cx, script, script->offsetToPC(i)); 1.1956 + if (!lineno) 1.1957 + return false; 1.1958 + args.rval().setInt32(lineno); 1.1959 + return true; 1.1960 +} 1.1961 + 1.1962 +#ifdef DEBUG 1.1963 + 1.1964 +static void 1.1965 +UpdateSwitchTableBounds(JSContext *cx, HandleScript script, unsigned offset, 1.1966 + unsigned *start, unsigned *end) 1.1967 +{ 1.1968 + jsbytecode *pc; 1.1969 + JSOp op; 1.1970 + ptrdiff_t jmplen; 1.1971 + int32_t low, high, n; 1.1972 + 1.1973 + pc = script->offsetToPC(offset); 1.1974 + op = JSOp(*pc); 1.1975 + switch (op) { 1.1976 + case JSOP_TABLESWITCH: 1.1977 + jmplen = JUMP_OFFSET_LEN; 1.1978 + pc += jmplen; 1.1979 + low = GET_JUMP_OFFSET(pc); 1.1980 + pc += JUMP_OFFSET_LEN; 1.1981 + high = GET_JUMP_OFFSET(pc); 1.1982 + pc += JUMP_OFFSET_LEN; 1.1983 + n = high - low + 1; 1.1984 + break; 1.1985 + 1.1986 + default: 1.1987 + /* [condswitch] switch does not have any jump or lookup tables. */ 1.1988 + JS_ASSERT(op == JSOP_CONDSWITCH); 1.1989 + return; 1.1990 + } 1.1991 + 1.1992 + *start = script->pcToOffset(pc); 1.1993 + *end = *start + (unsigned)(n * jmplen); 1.1994 +} 1.1995 + 1.1996 +static void 1.1997 +SrcNotes(JSContext *cx, HandleScript script, Sprinter *sp) 1.1998 +{ 1.1999 + Sprint(sp, "\nSource notes:\n"); 1.2000 + Sprint(sp, "%4s %4s %5s %6s %-8s %s\n", 1.2001 + "ofs", "line", "pc", "delta", "desc", "args"); 1.2002 + Sprint(sp, "---- ---- ----- ------ -------- ------\n"); 1.2003 + unsigned offset = 0; 1.2004 + unsigned colspan = 0; 1.2005 + unsigned lineno = script->lineno(); 1.2006 + jssrcnote *notes = script->notes(); 1.2007 + unsigned switchTableEnd = 0, switchTableStart = 0; 1.2008 + for (jssrcnote *sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) { 1.2009 + unsigned delta = SN_DELTA(sn); 1.2010 + offset += delta; 1.2011 + SrcNoteType type = (SrcNoteType) SN_TYPE(sn); 1.2012 + const char *name = js_SrcNoteSpec[type].name; 1.2013 + Sprint(sp, "%3u: %4u %5u [%4u] %-8s", unsigned(sn - notes), lineno, offset, delta, name); 1.2014 + switch (type) { 1.2015 + case SRC_NULL: 1.2016 + case SRC_IF: 1.2017 + case SRC_CONTINUE: 1.2018 + case SRC_BREAK: 1.2019 + case SRC_BREAK2LABEL: 1.2020 + case SRC_SWITCHBREAK: 1.2021 + case SRC_ASSIGNOP: 1.2022 + case SRC_XDELTA: 1.2023 + break; 1.2024 + 1.2025 + case SRC_COLSPAN: 1.2026 + colspan = js_GetSrcNoteOffset(sn, 0); 1.2027 + if (colspan >= SN_COLSPAN_DOMAIN / 2) 1.2028 + colspan -= SN_COLSPAN_DOMAIN; 1.2029 + Sprint(sp, "%d", colspan); 1.2030 + break; 1.2031 + 1.2032 + case SRC_SETLINE: 1.2033 + lineno = js_GetSrcNoteOffset(sn, 0); 1.2034 + Sprint(sp, " lineno %u", lineno); 1.2035 + break; 1.2036 + 1.2037 + case SRC_NEWLINE: 1.2038 + ++lineno; 1.2039 + break; 1.2040 + 1.2041 + case SRC_FOR: 1.2042 + Sprint(sp, " cond %u update %u tail %u", 1.2043 + unsigned(js_GetSrcNoteOffset(sn, 0)), 1.2044 + unsigned(js_GetSrcNoteOffset(sn, 1)), 1.2045 + unsigned(js_GetSrcNoteOffset(sn, 2))); 1.2046 + break; 1.2047 + 1.2048 + case SRC_IF_ELSE: 1.2049 + Sprint(sp, " else %u", unsigned(js_GetSrcNoteOffset(sn, 0))); 1.2050 + break; 1.2051 + 1.2052 + case SRC_FOR_IN: 1.2053 + case SRC_FOR_OF: 1.2054 + Sprint(sp, " closingjump %u", unsigned(js_GetSrcNoteOffset(sn, 0))); 1.2055 + break; 1.2056 + 1.2057 + case SRC_COND: 1.2058 + case SRC_WHILE: 1.2059 + case SRC_NEXTCASE: 1.2060 + Sprint(sp, " offset %u", unsigned(js_GetSrcNoteOffset(sn, 0))); 1.2061 + break; 1.2062 + 1.2063 + case SRC_TABLESWITCH: { 1.2064 + JSOp op = JSOp(script->code()[offset]); 1.2065 + JS_ASSERT(op == JSOP_TABLESWITCH); 1.2066 + Sprint(sp, " length %u", unsigned(js_GetSrcNoteOffset(sn, 0))); 1.2067 + UpdateSwitchTableBounds(cx, script, offset, 1.2068 + &switchTableStart, &switchTableEnd); 1.2069 + break; 1.2070 + } 1.2071 + case SRC_CONDSWITCH: { 1.2072 + JSOp op = JSOp(script->code()[offset]); 1.2073 + JS_ASSERT(op == JSOP_CONDSWITCH); 1.2074 + Sprint(sp, " length %u", unsigned(js_GetSrcNoteOffset(sn, 0))); 1.2075 + unsigned caseOff = (unsigned) js_GetSrcNoteOffset(sn, 1); 1.2076 + if (caseOff) 1.2077 + Sprint(sp, " first case offset %u", caseOff); 1.2078 + UpdateSwitchTableBounds(cx, script, offset, 1.2079 + &switchTableStart, &switchTableEnd); 1.2080 + break; 1.2081 + } 1.2082 + 1.2083 + case SRC_TRY: 1.2084 + JS_ASSERT(JSOp(script->code()[offset]) == JSOP_TRY); 1.2085 + Sprint(sp, " offset to jump %u", unsigned(js_GetSrcNoteOffset(sn, 0))); 1.2086 + break; 1.2087 + 1.2088 + default: 1.2089 + JS_ASSERT(0); 1.2090 + break; 1.2091 + } 1.2092 + Sprint(sp, "\n"); 1.2093 + } 1.2094 +} 1.2095 + 1.2096 +static bool 1.2097 +Notes(JSContext *cx, unsigned argc, jsval *vp) 1.2098 +{ 1.2099 + CallArgs args = CallArgsFromVp(argc, vp); 1.2100 + Sprinter sprinter(cx); 1.2101 + if (!sprinter.init()) 1.2102 + return false; 1.2103 + 1.2104 + for (unsigned i = 0; i < args.length(); i++) { 1.2105 + RootedScript script (cx, ValueToScript(cx, args[i])); 1.2106 + if (!script) 1.2107 + return false; 1.2108 + 1.2109 + SrcNotes(cx, script, &sprinter); 1.2110 + } 1.2111 + 1.2112 + JSString *str = JS_NewStringCopyZ(cx, sprinter.string()); 1.2113 + if (!str) 1.2114 + return false; 1.2115 + args.rval().setString(str); 1.2116 + return true; 1.2117 +} 1.2118 + 1.2119 +JS_STATIC_ASSERT(JSTRY_CATCH == 0); 1.2120 +JS_STATIC_ASSERT(JSTRY_FINALLY == 1); 1.2121 +JS_STATIC_ASSERT(JSTRY_ITER == 2); 1.2122 + 1.2123 +static const char* const TryNoteNames[] = { "catch", "finally", "iter", "loop" }; 1.2124 + 1.2125 +static bool 1.2126 +TryNotes(JSContext *cx, HandleScript script, Sprinter *sp) 1.2127 +{ 1.2128 + JSTryNote *tn, *tnlimit; 1.2129 + 1.2130 + if (!script->hasTrynotes()) 1.2131 + return true; 1.2132 + 1.2133 + tn = script->trynotes()->vector; 1.2134 + tnlimit = tn + script->trynotes()->length; 1.2135 + Sprint(sp, "\nException table:\nkind stack start end\n"); 1.2136 + do { 1.2137 + JS_ASSERT(tn->kind < ArrayLength(TryNoteNames)); 1.2138 + Sprint(sp, " %-7s %6u %8u %8u\n", 1.2139 + TryNoteNames[tn->kind], tn->stackDepth, 1.2140 + tn->start, tn->start + tn->length); 1.2141 + } while (++tn != tnlimit); 1.2142 + return true; 1.2143 +} 1.2144 + 1.2145 +static bool 1.2146 +DisassembleScript(JSContext *cx, HandleScript script, HandleFunction fun, bool lines, 1.2147 + bool recursive, Sprinter *sp) 1.2148 +{ 1.2149 + if (fun) { 1.2150 + Sprint(sp, "flags:"); 1.2151 + if (fun->isLambda()) 1.2152 + Sprint(sp, " LAMBDA"); 1.2153 + if (fun->isHeavyweight()) 1.2154 + Sprint(sp, " HEAVYWEIGHT"); 1.2155 + if (fun->isExprClosure()) 1.2156 + Sprint(sp, " EXPRESSION_CLOSURE"); 1.2157 + if (fun->isFunctionPrototype()) 1.2158 + Sprint(sp, " Function.prototype"); 1.2159 + if (fun->isSelfHostedBuiltin()) 1.2160 + Sprint(sp, " SELF_HOSTED"); 1.2161 + if (fun->isSelfHostedConstructor()) 1.2162 + Sprint(sp, " SELF_HOSTED_CTOR"); 1.2163 + if (fun->isArrow()) 1.2164 + Sprint(sp, " ARROW"); 1.2165 + Sprint(sp, "\n"); 1.2166 + } 1.2167 + 1.2168 + if (!js_Disassemble(cx, script, lines, sp)) 1.2169 + return false; 1.2170 + SrcNotes(cx, script, sp); 1.2171 + TryNotes(cx, script, sp); 1.2172 + 1.2173 + if (recursive && script->hasObjects()) { 1.2174 + ObjectArray *objects = script->objects(); 1.2175 + for (unsigned i = 0; i != objects->length; ++i) { 1.2176 + JSObject *obj = objects->vector[i]; 1.2177 + if (obj->is<JSFunction>()) { 1.2178 + Sprint(sp, "\n"); 1.2179 + RootedFunction fun(cx, &obj->as<JSFunction>()); 1.2180 + if (fun->isInterpreted()) { 1.2181 + RootedScript script(cx, fun->getOrCreateScript(cx)); 1.2182 + if (!script || !DisassembleScript(cx, script, fun, lines, recursive, sp)) 1.2183 + return false; 1.2184 + } else { 1.2185 + Sprint(sp, "[native code]\n"); 1.2186 + } 1.2187 + } 1.2188 + } 1.2189 + } 1.2190 + return true; 1.2191 +} 1.2192 + 1.2193 +namespace { 1.2194 + 1.2195 +struct DisassembleOptionParser { 1.2196 + unsigned argc; 1.2197 + jsval *argv; 1.2198 + bool lines; 1.2199 + bool recursive; 1.2200 + 1.2201 + DisassembleOptionParser(unsigned argc, jsval *argv) 1.2202 + : argc(argc), argv(argv), lines(false), recursive(false) {} 1.2203 + 1.2204 + bool parse(JSContext *cx) { 1.2205 + /* Read options off early arguments */ 1.2206 + while (argc > 0 && argv[0].isString()) { 1.2207 + JSString *str = argv[0].toString(); 1.2208 + JSFlatString *flatStr = JS_FlattenString(cx, str); 1.2209 + if (!flatStr) 1.2210 + return false; 1.2211 + if (JS_FlatStringEqualsAscii(flatStr, "-l")) 1.2212 + lines = true; 1.2213 + else if (JS_FlatStringEqualsAscii(flatStr, "-r")) 1.2214 + recursive = true; 1.2215 + else 1.2216 + break; 1.2217 + argv++, argc--; 1.2218 + } 1.2219 + return true; 1.2220 + } 1.2221 +}; 1.2222 + 1.2223 +} /* anonymous namespace */ 1.2224 + 1.2225 +static bool 1.2226 +DisassembleToSprinter(JSContext *cx, unsigned argc, jsval *vp, Sprinter *sprinter) 1.2227 +{ 1.2228 + CallArgs args = CallArgsFromVp(argc, vp); 1.2229 + DisassembleOptionParser p(args.length(), args.array()); 1.2230 + if (!p.parse(cx)) 1.2231 + return false; 1.2232 + 1.2233 + if (p.argc == 0) { 1.2234 + /* Without arguments, disassemble the current script. */ 1.2235 + RootedScript script(cx, GetTopScript(cx)); 1.2236 + if (script) { 1.2237 + JSAutoCompartment ac(cx, script); 1.2238 + if (!js_Disassemble(cx, script, p.lines, sprinter)) 1.2239 + return false; 1.2240 + SrcNotes(cx, script, sprinter); 1.2241 + TryNotes(cx, script, sprinter); 1.2242 + } 1.2243 + } else { 1.2244 + for (unsigned i = 0; i < p.argc; i++) { 1.2245 + RootedFunction fun(cx); 1.2246 + RootedScript script (cx, ValueToScript(cx, p.argv[i], fun.address())); 1.2247 + if (!script) 1.2248 + return false; 1.2249 + if (!DisassembleScript(cx, script, fun, p.lines, p.recursive, sprinter)) 1.2250 + return false; 1.2251 + } 1.2252 + } 1.2253 + return true; 1.2254 +} 1.2255 + 1.2256 +static bool 1.2257 +DisassembleToString(JSContext *cx, unsigned argc, jsval *vp) 1.2258 +{ 1.2259 + CallArgs args = CallArgsFromVp(argc, vp); 1.2260 + Sprinter sprinter(cx); 1.2261 + if (!sprinter.init()) 1.2262 + return false; 1.2263 + if (!DisassembleToSprinter(cx, args.length(), vp, &sprinter)) 1.2264 + return false; 1.2265 + 1.2266 + JSString *str = JS_NewStringCopyZ(cx, sprinter.string()); 1.2267 + if (!str) 1.2268 + return false; 1.2269 + args.rval().setString(str); 1.2270 + return true; 1.2271 +} 1.2272 + 1.2273 +static bool 1.2274 +Disassemble(JSContext *cx, unsigned argc, jsval *vp) 1.2275 +{ 1.2276 + CallArgs args = CallArgsFromVp(argc, vp); 1.2277 + Sprinter sprinter(cx); 1.2278 + if (!sprinter.init()) 1.2279 + return false; 1.2280 + if (!DisassembleToSprinter(cx, args.length(), vp, &sprinter)) 1.2281 + return false; 1.2282 + 1.2283 + fprintf(stdout, "%s\n", sprinter.string()); 1.2284 + args.rval().setUndefined(); 1.2285 + return true; 1.2286 +} 1.2287 + 1.2288 +static bool 1.2289 +DisassFile(JSContext *cx, unsigned argc, jsval *vp) 1.2290 +{ 1.2291 + CallArgs args = CallArgsFromVp(argc, vp); 1.2292 + 1.2293 + /* Support extra options at the start, just like Disassemble. */ 1.2294 + DisassembleOptionParser p(args.length(), args.array()); 1.2295 + if (!p.parse(cx)) 1.2296 + return false; 1.2297 + 1.2298 + if (!p.argc) { 1.2299 + args.rval().setUndefined(); 1.2300 + return true; 1.2301 + } 1.2302 + 1.2303 + RootedObject thisobj(cx, JS_THIS_OBJECT(cx, vp)); 1.2304 + if (!thisobj) 1.2305 + return false; 1.2306 + 1.2307 + // We should change DisassembleOptionParser to store CallArgs. 1.2308 + JSString *str = JS::ToString(cx, HandleValue::fromMarkedLocation(&p.argv[0])); 1.2309 + if (!str) 1.2310 + return false; 1.2311 + JSAutoByteString filename(cx, str); 1.2312 + if (!filename) 1.2313 + return false; 1.2314 + RootedScript script(cx); 1.2315 + 1.2316 + { 1.2317 + JS::AutoSaveContextOptions asco(cx); 1.2318 + JS::ContextOptionsRef(cx).setNoScriptRval(true); 1.2319 + 1.2320 + CompileOptions options(cx); 1.2321 + options.setIntroductionType("js shell disFile") 1.2322 + .setUTF8(true) 1.2323 + .setFileAndLine(filename.ptr(), 1) 1.2324 + .setCompileAndGo(true); 1.2325 + 1.2326 + script = JS::Compile(cx, thisobj, options, filename.ptr()); 1.2327 + if (!script) 1.2328 + return false; 1.2329 + } 1.2330 + 1.2331 + Sprinter sprinter(cx); 1.2332 + if (!sprinter.init()) 1.2333 + return false; 1.2334 + bool ok = DisassembleScript(cx, script, NullPtr(), p.lines, p.recursive, &sprinter); 1.2335 + if (ok) 1.2336 + fprintf(stdout, "%s\n", sprinter.string()); 1.2337 + if (!ok) 1.2338 + return false; 1.2339 + 1.2340 + args.rval().setUndefined(); 1.2341 + return true; 1.2342 +} 1.2343 + 1.2344 +static bool 1.2345 +DisassWithSrc(JSContext *cx, unsigned argc, jsval *vp) 1.2346 +{ 1.2347 + CallArgs args = CallArgsFromVp(argc, vp); 1.2348 + 1.2349 +#define LINE_BUF_LEN 512 1.2350 + unsigned len, line1, line2, bupline; 1.2351 + FILE *file; 1.2352 + char linebuf[LINE_BUF_LEN]; 1.2353 + jsbytecode *pc, *end; 1.2354 + static const char sep[] = ";-------------------------"; 1.2355 + 1.2356 + bool ok = true; 1.2357 + RootedScript script(cx); 1.2358 + for (unsigned i = 0; ok && i < args.length(); i++) { 1.2359 + script = ValueToScript(cx, args[i]); 1.2360 + if (!script) 1.2361 + return false; 1.2362 + 1.2363 + if (!script->filename()) { 1.2364 + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, 1.2365 + JSSMSG_FILE_SCRIPTS_ONLY); 1.2366 + return false; 1.2367 + } 1.2368 + 1.2369 + file = fopen(script->filename(), "r"); 1.2370 + if (!file) { 1.2371 + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, 1.2372 + JSSMSG_CANT_OPEN, script->filename(), 1.2373 + strerror(errno)); 1.2374 + return false; 1.2375 + } 1.2376 + 1.2377 + pc = script->code(); 1.2378 + end = script->codeEnd(); 1.2379 + 1.2380 + Sprinter sprinter(cx); 1.2381 + if (!sprinter.init()) { 1.2382 + ok = false; 1.2383 + goto bail; 1.2384 + } 1.2385 + 1.2386 + /* burn the leading lines */ 1.2387 + line2 = JS_PCToLineNumber(cx, script, pc); 1.2388 + for (line1 = 0; line1 < line2 - 1; line1++) { 1.2389 + char *tmp = fgets(linebuf, LINE_BUF_LEN, file); 1.2390 + if (!tmp) { 1.2391 + JS_ReportError(cx, "failed to read %s fully", script->filename()); 1.2392 + ok = false; 1.2393 + goto bail; 1.2394 + } 1.2395 + } 1.2396 + 1.2397 + bupline = 0; 1.2398 + while (pc < end) { 1.2399 + line2 = JS_PCToLineNumber(cx, script, pc); 1.2400 + 1.2401 + if (line2 < line1) { 1.2402 + if (bupline != line2) { 1.2403 + bupline = line2; 1.2404 + Sprint(&sprinter, "%s %3u: BACKUP\n", sep, line2); 1.2405 + } 1.2406 + } else { 1.2407 + if (bupline && line1 == line2) 1.2408 + Sprint(&sprinter, "%s %3u: RESTORE\n", sep, line2); 1.2409 + bupline = 0; 1.2410 + while (line1 < line2) { 1.2411 + if (!fgets(linebuf, LINE_BUF_LEN, file)) { 1.2412 + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, 1.2413 + JSSMSG_UNEXPECTED_EOF, 1.2414 + script->filename()); 1.2415 + ok = false; 1.2416 + goto bail; 1.2417 + } 1.2418 + line1++; 1.2419 + Sprint(&sprinter, "%s %3u: %s", sep, line1, linebuf); 1.2420 + } 1.2421 + } 1.2422 + 1.2423 + len = js_Disassemble1(cx, script, pc, script->pcToOffset(pc), true, &sprinter); 1.2424 + if (!len) { 1.2425 + ok = false; 1.2426 + goto bail; 1.2427 + } 1.2428 + pc += len; 1.2429 + } 1.2430 + 1.2431 + bail: 1.2432 + fclose(file); 1.2433 + } 1.2434 + args.rval().setUndefined(); 1.2435 + return ok; 1.2436 +#undef LINE_BUF_LEN 1.2437 +} 1.2438 + 1.2439 +static bool 1.2440 +DumpHeap(JSContext *cx, unsigned argc, jsval *vp) 1.2441 +{ 1.2442 + CallArgs args = CallArgsFromVp(argc, vp); 1.2443 + 1.2444 + JSAutoByteString fileName; 1.2445 + if (args.hasDefined(0)) { 1.2446 + RootedString str(cx, JS::ToString(cx, args[0])); 1.2447 + if (!str) 1.2448 + return false; 1.2449 + 1.2450 + if (!fileName.encodeLatin1(cx, str)) 1.2451 + return false; 1.2452 + } 1.2453 + 1.2454 + RootedValue startThing(cx); 1.2455 + if (args.hasDefined(1)) { 1.2456 + if (!args[1].isGCThing()) { 1.2457 + JS_ReportError(cx, "dumpHeap: Second argument not a GC thing!"); 1.2458 + return false; 1.2459 + } 1.2460 + startThing = args[1]; 1.2461 + } 1.2462 + 1.2463 + RootedValue thingToFind(cx); 1.2464 + if (args.hasDefined(2)) { 1.2465 + if (!args[2].isGCThing()) { 1.2466 + JS_ReportError(cx, "dumpHeap: Third argument not a GC thing!"); 1.2467 + return false; 1.2468 + } 1.2469 + thingToFind = args[2]; 1.2470 + } 1.2471 + 1.2472 + size_t maxDepth = size_t(-1); 1.2473 + if (args.hasDefined(3)) { 1.2474 + uint32_t depth; 1.2475 + if (!ToUint32(cx, args[3], &depth)) 1.2476 + return false; 1.2477 + maxDepth = depth; 1.2478 + } 1.2479 + 1.2480 + RootedValue thingToIgnore(cx); 1.2481 + if (args.hasDefined(4)) { 1.2482 + if (!args[2].isGCThing()) { 1.2483 + JS_ReportError(cx, "dumpHeap: Fifth argument not a GC thing!"); 1.2484 + return false; 1.2485 + } 1.2486 + thingToIgnore = args[4]; 1.2487 + } 1.2488 + 1.2489 + 1.2490 + FILE *dumpFile = stdout; 1.2491 + if (fileName.length()) { 1.2492 + dumpFile = fopen(fileName.ptr(), "w"); 1.2493 + if (!dumpFile) { 1.2494 + JS_ReportError(cx, "dumpHeap: can't open %s: %s\n", 1.2495 + fileName.ptr(), strerror(errno)); 1.2496 + return false; 1.2497 + } 1.2498 + } 1.2499 + 1.2500 + bool ok = JS_DumpHeap(JS_GetRuntime(cx), dumpFile, 1.2501 + startThing.isUndefined() ? nullptr : startThing.toGCThing(), 1.2502 + startThing.isUndefined() ? JSTRACE_OBJECT : startThing.get().gcKind(), 1.2503 + thingToFind.isUndefined() ? nullptr : thingToFind.toGCThing(), 1.2504 + maxDepth, 1.2505 + thingToIgnore.isUndefined() ? nullptr : thingToIgnore.toGCThing()); 1.2506 + 1.2507 + if (dumpFile != stdout) 1.2508 + fclose(dumpFile); 1.2509 + 1.2510 + if (!ok) 1.2511 + JS_ReportOutOfMemory(cx); 1.2512 + 1.2513 + return ok; 1.2514 +} 1.2515 + 1.2516 +static bool 1.2517 +DumpObject(JSContext *cx, unsigned argc, jsval *vp) 1.2518 +{ 1.2519 + CallArgs args = CallArgsFromVp(argc, vp); 1.2520 + RootedObject arg0(cx); 1.2521 + if (!JS_ConvertArguments(cx, args, "o", arg0.address())) 1.2522 + return false; 1.2523 + 1.2524 + js_DumpObject(arg0); 1.2525 + 1.2526 + args.rval().setUndefined(); 1.2527 + return true; 1.2528 +} 1.2529 + 1.2530 +#endif /* DEBUG */ 1.2531 + 1.2532 +static bool 1.2533 +BuildDate(JSContext *cx, unsigned argc, jsval *vp) 1.2534 +{ 1.2535 + CallArgs args = CallArgsFromVp(argc, vp); 1.2536 + fprintf(gOutFile, "built on %s at %s\n", __DATE__, __TIME__); 1.2537 + args.rval().setUndefined(); 1.2538 + return true; 1.2539 +} 1.2540 + 1.2541 +static bool 1.2542 +Intern(JSContext *cx, unsigned argc, jsval *vp) 1.2543 +{ 1.2544 + CallArgs args = CallArgsFromVp(argc, vp); 1.2545 + JSString *str = JS::ToString(cx, args.get(0)); 1.2546 + if (!str) 1.2547 + return false; 1.2548 + 1.2549 + size_t length; 1.2550 + const jschar *chars = JS_GetStringCharsAndLength(cx, str, &length); 1.2551 + if (!chars) 1.2552 + return false; 1.2553 + 1.2554 + if (!JS_InternUCStringN(cx, chars, length)) 1.2555 + return false; 1.2556 + 1.2557 + args.rval().setUndefined(); 1.2558 + return true; 1.2559 +} 1.2560 + 1.2561 +static bool 1.2562 +Clone(JSContext *cx, unsigned argc, jsval *vp) 1.2563 +{ 1.2564 + CallArgs args = CallArgsFromVp(argc, vp); 1.2565 + RootedObject parent(cx); 1.2566 + RootedObject funobj(cx); 1.2567 + 1.2568 + if (!args.length()) { 1.2569 + JS_ReportError(cx, "Invalid arguments to clone"); 1.2570 + return false; 1.2571 + } 1.2572 + 1.2573 + { 1.2574 + Maybe<JSAutoCompartment> ac; 1.2575 + RootedObject obj(cx, JSVAL_IS_PRIMITIVE(args[0]) ? nullptr : &args[0].toObject()); 1.2576 + 1.2577 + if (obj && obj->is<CrossCompartmentWrapperObject>()) { 1.2578 + obj = UncheckedUnwrap(obj); 1.2579 + ac.construct(cx, obj); 1.2580 + args[0].setObject(*obj); 1.2581 + } 1.2582 + if (obj && obj->is<JSFunction>()) { 1.2583 + funobj = obj; 1.2584 + } else { 1.2585 + JSFunction *fun = JS_ValueToFunction(cx, args[0]); 1.2586 + if (!fun) 1.2587 + return false; 1.2588 + funobj = JS_GetFunctionObject(fun); 1.2589 + } 1.2590 + } 1.2591 + if (funobj->compartment() != cx->compartment()) { 1.2592 + JSFunction *fun = &funobj->as<JSFunction>(); 1.2593 + if (fun->hasScript() && fun->nonLazyScript()->compileAndGo()) { 1.2594 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE, 1.2595 + "function", "compile-and-go"); 1.2596 + return false; 1.2597 + } 1.2598 + } 1.2599 + 1.2600 + if (args.length() > 1) { 1.2601 + if (!JS_ValueToObject(cx, args[1], &parent)) 1.2602 + return false; 1.2603 + } else { 1.2604 + parent = JS_GetParent(&args.callee()); 1.2605 + } 1.2606 + 1.2607 + JSObject *clone = JS_CloneFunctionObject(cx, funobj, parent); 1.2608 + if (!clone) 1.2609 + return false; 1.2610 + args.rval().setObject(*clone); 1.2611 + return true; 1.2612 +} 1.2613 + 1.2614 +static bool 1.2615 +GetPDA(JSContext *cx, unsigned argc, jsval *vp) 1.2616 +{ 1.2617 + CallArgs args = CallArgsFromVp(argc, vp); 1.2618 + RootedObject vobj(cx); 1.2619 + bool ok; 1.2620 + JSPropertyDescArray pda; 1.2621 + JSPropertyDesc *pd; 1.2622 + 1.2623 + if (!JS_ValueToObject(cx, args.get(0), &vobj)) 1.2624 + return false; 1.2625 + if (!vobj) { 1.2626 + args.rval().setUndefined(); 1.2627 + return true; 1.2628 + } 1.2629 + 1.2630 + RootedObject aobj(cx, JS_NewArrayObject(cx, 0)); 1.2631 + if (!aobj) 1.2632 + return false; 1.2633 + args.rval().setObject(*aobj); 1.2634 + 1.2635 + ok = !!JS_GetPropertyDescArray(cx, vobj, &pda); 1.2636 + if (!ok) 1.2637 + return false; 1.2638 + pd = pda.array; 1.2639 + 1.2640 + RootedObject pdobj(cx); 1.2641 + RootedValue id(cx); 1.2642 + RootedValue value(cx); 1.2643 + RootedValue flags(cx); 1.2644 + RootedValue alias(cx); 1.2645 + 1.2646 + for (uint32_t i = 0; i < pda.length; i++, pd++) { 1.2647 + pdobj = JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr()); 1.2648 + if (!pdobj) { 1.2649 + ok = false; 1.2650 + break; 1.2651 + } 1.2652 + 1.2653 + /* Protect pdobj from GC by setting it as an element of aobj now */ 1.2654 + ok = !!JS_SetElement(cx, aobj, i, pdobj); 1.2655 + if (!ok) 1.2656 + break; 1.2657 + 1.2658 + id = pd->id; 1.2659 + value = pd->value; 1.2660 + flags.setInt32(pd->flags); 1.2661 + alias = pd->alias; 1.2662 + ok = JS_SetProperty(cx, pdobj, "id", id) && 1.2663 + JS_SetProperty(cx, pdobj, "value", value) && 1.2664 + JS_SetProperty(cx, pdobj, "flags", flags) && 1.2665 + JS_SetProperty(cx, pdobj, "alias", alias); 1.2666 + if (!ok) 1.2667 + break; 1.2668 + } 1.2669 + JS_PutPropertyDescArray(cx, &pda); 1.2670 + return ok; 1.2671 +} 1.2672 + 1.2673 +static bool 1.2674 +GetSLX(JSContext *cx, unsigned argc, jsval *vp) 1.2675 +{ 1.2676 + CallArgs args = CallArgsFromVp(argc, vp); 1.2677 + RootedScript script(cx); 1.2678 + 1.2679 + script = ValueToScript(cx, args.get(0)); 1.2680 + if (!script) 1.2681 + return false; 1.2682 + args.rval().setInt32(js_GetScriptLineExtent(script)); 1.2683 + return true; 1.2684 +} 1.2685 + 1.2686 +static bool 1.2687 +ThrowError(JSContext *cx, unsigned argc, jsval *vp) 1.2688 +{ 1.2689 + JS_ReportError(cx, "This is an error"); 1.2690 + return false; 1.2691 +} 1.2692 + 1.2693 +#define LAZY_STANDARD_CLASSES 1.2694 + 1.2695 +/* A class for easily testing the inner/outer object callbacks. */ 1.2696 +typedef struct ComplexObject { 1.2697 + bool isInner; 1.2698 + bool frozen; 1.2699 + JSObject *inner; 1.2700 + JSObject *outer; 1.2701 +} ComplexObject; 1.2702 + 1.2703 +static bool 1.2704 +sandbox_enumerate(JSContext *cx, HandleObject obj) 1.2705 +{ 1.2706 + RootedValue v(cx); 1.2707 + 1.2708 + if (!JS_GetProperty(cx, obj, "lazy", &v)) 1.2709 + return false; 1.2710 + 1.2711 + if (!ToBoolean(v)) 1.2712 + return true; 1.2713 + 1.2714 + return JS_EnumerateStandardClasses(cx, obj); 1.2715 +} 1.2716 + 1.2717 +static bool 1.2718 +sandbox_resolve(JSContext *cx, HandleObject obj, HandleId id, MutableHandleObject objp) 1.2719 +{ 1.2720 + RootedValue v(cx); 1.2721 + if (!JS_GetProperty(cx, obj, "lazy", &v)) 1.2722 + return false; 1.2723 + 1.2724 + if (ToBoolean(v)) { 1.2725 + bool resolved; 1.2726 + if (!JS_ResolveStandardClass(cx, obj, id, &resolved)) 1.2727 + return false; 1.2728 + if (resolved) { 1.2729 + objp.set(obj); 1.2730 + return true; 1.2731 + } 1.2732 + } 1.2733 + objp.set(nullptr); 1.2734 + return true; 1.2735 +} 1.2736 + 1.2737 +static const JSClass sandbox_class = { 1.2738 + "sandbox", 1.2739 + JSCLASS_NEW_RESOLVE | JSCLASS_GLOBAL_FLAGS, 1.2740 + JS_PropertyStub, JS_DeletePropertyStub, 1.2741 + JS_PropertyStub, JS_StrictPropertyStub, 1.2742 + sandbox_enumerate, (JSResolveOp)sandbox_resolve, 1.2743 + JS_ConvertStub, nullptr, 1.2744 + nullptr, nullptr, nullptr, 1.2745 + JS_GlobalObjectTraceHook 1.2746 +}; 1.2747 + 1.2748 +static JSObject * 1.2749 +NewSandbox(JSContext *cx, bool lazy) 1.2750 +{ 1.2751 + RootedObject obj(cx, JS_NewGlobalObject(cx, &sandbox_class, nullptr, 1.2752 + JS::DontFireOnNewGlobalHook)); 1.2753 + if (!obj) 1.2754 + return nullptr; 1.2755 + 1.2756 + { 1.2757 + JSAutoCompartment ac(cx, obj); 1.2758 + if (!lazy && !JS_InitStandardClasses(cx, obj)) 1.2759 + return nullptr; 1.2760 + 1.2761 + RootedValue value(cx, BooleanValue(lazy)); 1.2762 + if (!JS_SetProperty(cx, obj, "lazy", value)) 1.2763 + return nullptr; 1.2764 + } 1.2765 + 1.2766 + JS_FireOnNewGlobalObject(cx, obj); 1.2767 + 1.2768 + if (!cx->compartment()->wrap(cx, &obj)) 1.2769 + return nullptr; 1.2770 + return obj; 1.2771 +} 1.2772 + 1.2773 +static bool 1.2774 +EvalInContext(JSContext *cx, unsigned argc, jsval *vp) 1.2775 +{ 1.2776 + CallArgs args = CallArgsFromVp(argc, vp); 1.2777 + 1.2778 + RootedString str(cx); 1.2779 + RootedObject sobj(cx); 1.2780 + if (!JS_ConvertArguments(cx, args, "S / o", str.address(), sobj.address())) 1.2781 + return false; 1.2782 + 1.2783 + size_t srclen; 1.2784 + const jschar *src = JS_GetStringCharsAndLength(cx, str, &srclen); 1.2785 + if (!src) 1.2786 + return false; 1.2787 + 1.2788 + bool lazy = false; 1.2789 + if (srclen == 4) { 1.2790 + if (src[0] == 'l' && src[1] == 'a' && src[2] == 'z' && src[3] == 'y') { 1.2791 + lazy = true; 1.2792 + srclen = 0; 1.2793 + } 1.2794 + } 1.2795 + 1.2796 + if (!sobj) { 1.2797 + sobj = NewSandbox(cx, lazy); 1.2798 + if (!sobj) 1.2799 + return false; 1.2800 + } 1.2801 + 1.2802 + if (srclen == 0) { 1.2803 + args.rval().setObject(*sobj); 1.2804 + return true; 1.2805 + } 1.2806 + 1.2807 + JS::AutoFilename filename; 1.2808 + unsigned lineno; 1.2809 + 1.2810 + DescribeScriptedCaller(cx, &filename, &lineno); 1.2811 + { 1.2812 + Maybe<JSAutoCompartment> ac; 1.2813 + unsigned flags; 1.2814 + JSObject *unwrapped = UncheckedUnwrap(sobj, true, &flags); 1.2815 + if (flags & Wrapper::CROSS_COMPARTMENT) { 1.2816 + sobj = unwrapped; 1.2817 + ac.construct(cx, sobj); 1.2818 + } 1.2819 + 1.2820 + sobj = GetInnerObject(cx, sobj); 1.2821 + if (!sobj) 1.2822 + return false; 1.2823 + if (!(sobj->getClass()->flags & JSCLASS_IS_GLOBAL)) { 1.2824 + JS_ReportError(cx, "Invalid scope argument to evalcx"); 1.2825 + return false; 1.2826 + } 1.2827 + if (!JS_EvaluateUCScript(cx, sobj, src, srclen, 1.2828 + filename.get(), 1.2829 + lineno, 1.2830 + args.rval())) { 1.2831 + return false; 1.2832 + } 1.2833 + } 1.2834 + 1.2835 + if (!cx->compartment()->wrap(cx, args.rval())) 1.2836 + return false; 1.2837 + 1.2838 + return true; 1.2839 +} 1.2840 + 1.2841 +static bool 1.2842 +EvalInFrame(JSContext *cx, unsigned argc, jsval *vp) 1.2843 +{ 1.2844 + CallArgs args = CallArgsFromVp(argc, vp); 1.2845 + if (!args.get(0).isInt32() || !args.get(1).isString()) { 1.2846 + JS_ReportError(cx, "Invalid arguments to evalInFrame"); 1.2847 + return false; 1.2848 + } 1.2849 + 1.2850 + uint32_t upCount = args[0].toInt32(); 1.2851 + RootedString str(cx, args[1].toString()); 1.2852 + bool saveCurrent = args.get(2).isBoolean() ? args[2].toBoolean() : false; 1.2853 + 1.2854 + /* This is a copy of CheckDebugMode. */ 1.2855 + if (!JS_GetDebugMode(cx)) { 1.2856 + JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, 1.2857 + nullptr, JSMSG_NEED_DEBUG_MODE); 1.2858 + return false; 1.2859 + } 1.2860 + 1.2861 + /* Debug-mode currently disables Ion compilation. */ 1.2862 + ScriptFrameIter fi(cx); 1.2863 + for (uint32_t i = 0; i < upCount; ++i, ++fi) { 1.2864 + ScriptFrameIter next(fi); 1.2865 + ++next; 1.2866 + if (next.done()) 1.2867 + break; 1.2868 + } 1.2869 + 1.2870 + AutoSaveFrameChain sfc(cx); 1.2871 + mozilla::Maybe<AutoCompartment> ac; 1.2872 + if (saveCurrent) { 1.2873 + if (!sfc.save()) 1.2874 + return false; 1.2875 + ac.construct(cx, DefaultObjectForContextOrNull(cx)); 1.2876 + } 1.2877 + 1.2878 + size_t length; 1.2879 + const jschar *chars = JS_GetStringCharsAndLength(cx, str, &length); 1.2880 + if (!chars) 1.2881 + return false; 1.2882 + 1.2883 + JSAbstractFramePtr frame(fi.abstractFramePtr().raw(), fi.pc()); 1.2884 + RootedScript fpscript(cx, frame.script()); 1.2885 + bool ok = !!frame.evaluateUCInStackFrame(cx, chars, length, 1.2886 + fpscript->filename(), 1.2887 + JS_PCToLineNumber(cx, fpscript, 1.2888 + fi.pc()), 1.2889 + MutableHandleValue::fromMarkedLocation(vp)); 1.2890 + return ok; 1.2891 +} 1.2892 + 1.2893 +#ifdef JS_THREADSAFE 1.2894 +struct WorkerInput 1.2895 +{ 1.2896 + JSRuntime *runtime; 1.2897 + jschar *chars; 1.2898 + size_t length; 1.2899 + 1.2900 + WorkerInput(JSRuntime *runtime, jschar *chars, size_t length) 1.2901 + : runtime(runtime), chars(chars), length(length) 1.2902 + {} 1.2903 + 1.2904 + ~WorkerInput() { 1.2905 + js_free(chars); 1.2906 + } 1.2907 +}; 1.2908 + 1.2909 +static void 1.2910 +WorkerMain(void *arg) 1.2911 +{ 1.2912 + WorkerInput *input = (WorkerInput *) arg; 1.2913 + 1.2914 + JSRuntime *rt = JS_NewRuntime(8L * 1024L * 1024L, 1.2915 + JS_USE_HELPER_THREADS, 1.2916 + input->runtime); 1.2917 + if (!rt) { 1.2918 + js_delete(input); 1.2919 + return; 1.2920 + } 1.2921 + 1.2922 + JSContext *cx = NewContext(rt); 1.2923 + if (!cx) { 1.2924 + JS_DestroyRuntime(rt); 1.2925 + js_delete(input); 1.2926 + return; 1.2927 + } 1.2928 + 1.2929 + do { 1.2930 + JSAutoRequest ar(cx); 1.2931 + 1.2932 + JS::CompartmentOptions compartmentOptions; 1.2933 + compartmentOptions.setVersion(JSVERSION_LATEST); 1.2934 + RootedObject global(cx, NewGlobalObject(cx, compartmentOptions, nullptr)); 1.2935 + if (!global) 1.2936 + break; 1.2937 + 1.2938 + JSAutoCompartment ac(cx, global); 1.2939 + 1.2940 + JS::CompileOptions options(cx); 1.2941 + options.setFileAndLine("<string>", 1) 1.2942 + .setCompileAndGo(true); 1.2943 + 1.2944 + RootedScript script(cx, JS::Compile(cx, global, options, 1.2945 + input->chars, input->length)); 1.2946 + if (!script) 1.2947 + break; 1.2948 + RootedValue result(cx); 1.2949 + JS_ExecuteScript(cx, global, script, &result); 1.2950 + } while (0); 1.2951 + 1.2952 + DestroyContext(cx, false); 1.2953 + JS_DestroyRuntime(rt); 1.2954 + 1.2955 + js_delete(input); 1.2956 +} 1.2957 + 1.2958 +Vector<PRThread *, 0, SystemAllocPolicy> workerThreads; 1.2959 + 1.2960 +static bool 1.2961 +EvalInWorker(JSContext *cx, unsigned argc, jsval *vp) 1.2962 +{ 1.2963 + CallArgs args = CallArgsFromVp(argc, vp); 1.2964 + if (!args.get(0).isString()) { 1.2965 + JS_ReportError(cx, "Invalid arguments to evalInWorker"); 1.2966 + return false; 1.2967 + } 1.2968 + 1.2969 + if (!args[0].toString()->ensureLinear(cx)) 1.2970 + return false; 1.2971 + 1.2972 + JSLinearString *str = &args[0].toString()->asLinear(); 1.2973 + 1.2974 + jschar *chars = (jschar *) js_malloc(str->length() * sizeof(jschar)); 1.2975 + if (!chars) 1.2976 + return false; 1.2977 + PodCopy(chars, str->chars(), str->length()); 1.2978 + 1.2979 + WorkerInput *input = js_new<WorkerInput>(cx->runtime(), chars, str->length()); 1.2980 + if (!input) 1.2981 + return false; 1.2982 + 1.2983 + PRThread *thread = PR_CreateThread(PR_USER_THREAD, WorkerMain, input, 1.2984 + PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0); 1.2985 + if (!thread || !workerThreads.append(thread)) 1.2986 + return false; 1.2987 + 1.2988 + return true; 1.2989 +} 1.2990 +#endif 1.2991 + 1.2992 +static bool 1.2993 +ShapeOf(JSContext *cx, unsigned argc, JS::Value *vp) 1.2994 +{ 1.2995 + CallArgs args = CallArgsFromVp(argc, vp); 1.2996 + if (!args.get(0).isObject()) { 1.2997 + JS_ReportError(cx, "shapeOf: object expected"); 1.2998 + return false; 1.2999 + } 1.3000 + JSObject *obj = &args[0].toObject(); 1.3001 + args.rval().set(JS_NumberValue(double(uintptr_t(obj->lastProperty()) >> 3))); 1.3002 + return true; 1.3003 +} 1.3004 + 1.3005 +/* 1.3006 + * If referent has an own property named id, copy that property to obj[id]. 1.3007 + * Since obj is native, this isn't totally transparent; properties of a 1.3008 + * non-native referent may be simplified to data properties. 1.3009 + */ 1.3010 +static bool 1.3011 +CopyProperty(JSContext *cx, HandleObject obj, HandleObject referent, HandleId id, 1.3012 + MutableHandleObject objp) 1.3013 +{ 1.3014 + RootedShape shape(cx); 1.3015 + Rooted<PropertyDescriptor> desc(cx); 1.3016 + RootedObject obj2(cx); 1.3017 + 1.3018 + objp.set(nullptr); 1.3019 + if (referent->isNative()) { 1.3020 + if (!LookupNativeProperty(cx, referent, id, &obj2, &shape)) 1.3021 + return false; 1.3022 + if (obj2 != referent) 1.3023 + return true; 1.3024 + 1.3025 + if (shape->hasSlot()) { 1.3026 + desc.value().set(referent->nativeGetSlot(shape->slot())); 1.3027 + } else { 1.3028 + desc.value().setUndefined(); 1.3029 + } 1.3030 + 1.3031 + desc.setAttributes(shape->attributes()); 1.3032 + desc.setGetter(shape->getter()); 1.3033 + if (!desc.getter() && !desc.hasGetterObject()) 1.3034 + desc.setGetter(JS_PropertyStub); 1.3035 + desc.setSetter(shape->setter()); 1.3036 + if (!desc.setter() && !desc.hasSetterObject()) 1.3037 + desc.setSetter(JS_StrictPropertyStub); 1.3038 + } else if (referent->is<ProxyObject>()) { 1.3039 + if (!Proxy::getOwnPropertyDescriptor(cx, referent, id, &desc)) 1.3040 + return false; 1.3041 + if (!desc.object()) 1.3042 + return true; 1.3043 + } else { 1.3044 + if (!JSObject::lookupGeneric(cx, referent, id, objp, &shape)) 1.3045 + return false; 1.3046 + if (objp != referent) 1.3047 + return true; 1.3048 + RootedValue value(cx); 1.3049 + if (!JSObject::getGeneric(cx, referent, referent, id, &value) || 1.3050 + !JSObject::getGenericAttributes(cx, referent, id, &desc.attributesRef())) 1.3051 + { 1.3052 + return false; 1.3053 + } 1.3054 + desc.value().set(value); 1.3055 + desc.attributesRef() &= JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT; 1.3056 + desc.setGetter(JS_PropertyStub); 1.3057 + desc.setSetter(JS_StrictPropertyStub); 1.3058 + } 1.3059 + 1.3060 + objp.set(obj); 1.3061 + return DefineNativeProperty(cx, obj, id, desc.value(), desc.getter(), desc.setter(), 1.3062 + desc.attributes()); 1.3063 +} 1.3064 + 1.3065 +static bool 1.3066 +resolver_resolve(JSContext *cx, HandleObject obj, HandleId id, MutableHandleObject objp) 1.3067 +{ 1.3068 + jsval v = JS_GetReservedSlot(obj, 0); 1.3069 + Rooted<JSObject*> vobj(cx, &v.toObject()); 1.3070 + return CopyProperty(cx, obj, vobj, id, objp); 1.3071 +} 1.3072 + 1.3073 +static bool 1.3074 +resolver_enumerate(JSContext *cx, HandleObject obj) 1.3075 +{ 1.3076 + jsval v = JS_GetReservedSlot(obj, 0); 1.3077 + RootedObject referent(cx, JSVAL_TO_OBJECT(v)); 1.3078 + 1.3079 + AutoIdArray ida(cx, JS_Enumerate(cx, referent)); 1.3080 + bool ok = !!ida; 1.3081 + RootedObject ignore(cx); 1.3082 + for (size_t i = 0; ok && i < ida.length(); i++) { 1.3083 + Rooted<jsid> id(cx, ida[i]); 1.3084 + ok = CopyProperty(cx, obj, referent, id, &ignore); 1.3085 + } 1.3086 + return ok; 1.3087 +} 1.3088 + 1.3089 +static const JSClass resolver_class = { 1.3090 + "resolver", 1.3091 + JSCLASS_NEW_RESOLVE | JSCLASS_HAS_RESERVED_SLOTS(1), 1.3092 + JS_PropertyStub, JS_DeletePropertyStub, 1.3093 + JS_PropertyStub, JS_StrictPropertyStub, 1.3094 + resolver_enumerate, (JSResolveOp)resolver_resolve, 1.3095 + JS_ConvertStub 1.3096 +}; 1.3097 + 1.3098 + 1.3099 +static bool 1.3100 +Resolver(JSContext *cx, unsigned argc, jsval *vp) 1.3101 +{ 1.3102 + CallArgs args = CallArgsFromVp(argc, vp); 1.3103 + 1.3104 + RootedObject referent(cx); 1.3105 + if (!JS_ValueToObject(cx, args.get(0), &referent)) 1.3106 + return false; 1.3107 + if (!referent) { 1.3108 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO, 1.3109 + args.get(0).isNull() ? "null" : "undefined", "object"); 1.3110 + return false; 1.3111 + } 1.3112 + 1.3113 + RootedObject proto(cx, nullptr); 1.3114 + if (!args.get(1).isNullOrUndefined()) { 1.3115 + if (!JS_ValueToObject(cx, args.get(1), &proto)) 1.3116 + return false; 1.3117 + } 1.3118 + 1.3119 + RootedObject parent(cx, JS_GetParent(referent)); 1.3120 + JSObject *result = (args.length() > 1 1.3121 + ? JS_NewObjectWithGivenProto 1.3122 + : JS_NewObject)(cx, &resolver_class, proto, parent); 1.3123 + if (!result) 1.3124 + return false; 1.3125 + 1.3126 + JS_SetReservedSlot(result, 0, ObjectValue(*referent)); 1.3127 + args.rval().setObject(*result); 1.3128 + return true; 1.3129 +} 1.3130 + 1.3131 +#ifdef JS_THREADSAFE 1.3132 + 1.3133 +/* 1.3134 + * Check that t1 comes strictly before t2. The function correctly deals with 1.3135 + * wrap-around between t2 and t1 assuming that t2 and t1 stays within INT32_MAX 1.3136 + * from each other. We use MAX_TIMEOUT_INTERVAL to enforce this restriction. 1.3137 + */ 1.3138 +static bool 1.3139 +IsBefore(int64_t t1, int64_t t2) 1.3140 +{ 1.3141 + return int32_t(t1 - t2) < 0; 1.3142 +} 1.3143 + 1.3144 +static bool 1.3145 +Sleep_fn(JSContext *cx, unsigned argc, Value *vp) 1.3146 +{ 1.3147 + CallArgs args = CallArgsFromVp(argc, vp); 1.3148 + int64_t t_ticks; 1.3149 + 1.3150 + if (args.length() == 0) { 1.3151 + t_ticks = 0; 1.3152 + } else { 1.3153 + double t_secs; 1.3154 + 1.3155 + if (!ToNumber(cx, args[0], &t_secs)) 1.3156 + return false; 1.3157 + 1.3158 + /* NB: The next condition also filter out NaNs. */ 1.3159 + if (!(t_secs <= MAX_TIMEOUT_INTERVAL)) { 1.3160 + JS_ReportError(cx, "Excessive sleep interval"); 1.3161 + return false; 1.3162 + } 1.3163 + t_ticks = (t_secs <= 0.0) 1.3164 + ? 0 1.3165 + : int64_t(PRMJ_USEC_PER_SEC * t_secs); 1.3166 + } 1.3167 + PR_Lock(gWatchdogLock); 1.3168 + int64_t to_wakeup = PRMJ_Now() + t_ticks; 1.3169 + for (;;) { 1.3170 + PR_WaitCondVar(gSleepWakeup, t_ticks); 1.3171 + if (gServiceInterrupt) 1.3172 + break; 1.3173 + int64_t now = PRMJ_Now(); 1.3174 + if (!IsBefore(now, to_wakeup)) 1.3175 + break; 1.3176 + t_ticks = to_wakeup - now; 1.3177 + } 1.3178 + PR_Unlock(gWatchdogLock); 1.3179 + return !gServiceInterrupt; 1.3180 +} 1.3181 + 1.3182 +static bool 1.3183 +InitWatchdog(JSRuntime *rt) 1.3184 +{ 1.3185 + JS_ASSERT(!gWatchdogThread); 1.3186 + gWatchdogLock = PR_NewLock(); 1.3187 + if (gWatchdogLock) { 1.3188 + gWatchdogWakeup = PR_NewCondVar(gWatchdogLock); 1.3189 + if (gWatchdogWakeup) { 1.3190 + gSleepWakeup = PR_NewCondVar(gWatchdogLock); 1.3191 + if (gSleepWakeup) 1.3192 + return true; 1.3193 + PR_DestroyCondVar(gWatchdogWakeup); 1.3194 + } 1.3195 + PR_DestroyLock(gWatchdogLock); 1.3196 + } 1.3197 + return false; 1.3198 +} 1.3199 + 1.3200 +static void 1.3201 +KillWatchdog() 1.3202 +{ 1.3203 + PRThread *thread; 1.3204 + 1.3205 + PR_Lock(gWatchdogLock); 1.3206 + thread = gWatchdogThread; 1.3207 + if (thread) { 1.3208 + /* 1.3209 + * The watchdog thread is running, tell it to terminate waking it up 1.3210 + * if necessary. 1.3211 + */ 1.3212 + gWatchdogThread = nullptr; 1.3213 + PR_NotifyCondVar(gWatchdogWakeup); 1.3214 + } 1.3215 + PR_Unlock(gWatchdogLock); 1.3216 + if (thread) 1.3217 + PR_JoinThread(thread); 1.3218 + PR_DestroyCondVar(gSleepWakeup); 1.3219 + PR_DestroyCondVar(gWatchdogWakeup); 1.3220 + PR_DestroyLock(gWatchdogLock); 1.3221 +} 1.3222 + 1.3223 +static void 1.3224 +WatchdogMain(void *arg) 1.3225 +{ 1.3226 + PR_SetCurrentThreadName("JS Watchdog"); 1.3227 + 1.3228 + JSRuntime *rt = (JSRuntime *) arg; 1.3229 + 1.3230 + PR_Lock(gWatchdogLock); 1.3231 + while (gWatchdogThread) { 1.3232 + int64_t now = PRMJ_Now(); 1.3233 + if (gWatchdogHasTimeout && !IsBefore(now, gWatchdogTimeout)) { 1.3234 + /* 1.3235 + * The timeout has just expired. Request an interrupt callback 1.3236 + * outside the lock. 1.3237 + */ 1.3238 + gWatchdogHasTimeout = false; 1.3239 + PR_Unlock(gWatchdogLock); 1.3240 + CancelExecution(rt); 1.3241 + PR_Lock(gWatchdogLock); 1.3242 + 1.3243 + /* Wake up any threads doing sleep. */ 1.3244 + PR_NotifyAllCondVar(gSleepWakeup); 1.3245 + } else { 1.3246 + if (gWatchdogHasTimeout) { 1.3247 + /* 1.3248 + * Time hasn't expired yet. Simulate an interrupt callback 1.3249 + * which doesn't abort execution. 1.3250 + */ 1.3251 + JS_RequestInterruptCallback(rt); 1.3252 + } 1.3253 + 1.3254 + uint64_t sleepDuration = PR_INTERVAL_NO_TIMEOUT; 1.3255 + if (gWatchdogHasTimeout) 1.3256 + sleepDuration = PR_TicksPerSecond() / 10; 1.3257 + mozilla::DebugOnly<PRStatus> status = 1.3258 + PR_WaitCondVar(gWatchdogWakeup, sleepDuration); 1.3259 + JS_ASSERT(status == PR_SUCCESS); 1.3260 + } 1.3261 + } 1.3262 + PR_Unlock(gWatchdogLock); 1.3263 +} 1.3264 + 1.3265 +static bool 1.3266 +ScheduleWatchdog(JSRuntime *rt, double t) 1.3267 +{ 1.3268 + if (t <= 0) { 1.3269 + PR_Lock(gWatchdogLock); 1.3270 + gWatchdogHasTimeout = false; 1.3271 + PR_Unlock(gWatchdogLock); 1.3272 + return true; 1.3273 + } 1.3274 + 1.3275 + int64_t interval = int64_t(ceil(t * PRMJ_USEC_PER_SEC)); 1.3276 + int64_t timeout = PRMJ_Now() + interval; 1.3277 + PR_Lock(gWatchdogLock); 1.3278 + if (!gWatchdogThread) { 1.3279 + JS_ASSERT(!gWatchdogHasTimeout); 1.3280 + gWatchdogThread = PR_CreateThread(PR_USER_THREAD, 1.3281 + WatchdogMain, 1.3282 + rt, 1.3283 + PR_PRIORITY_NORMAL, 1.3284 + PR_GLOBAL_THREAD, 1.3285 + PR_JOINABLE_THREAD, 1.3286 + 0); 1.3287 + if (!gWatchdogThread) { 1.3288 + PR_Unlock(gWatchdogLock); 1.3289 + return false; 1.3290 + } 1.3291 + } else if (!gWatchdogHasTimeout || IsBefore(timeout, gWatchdogTimeout)) { 1.3292 + PR_NotifyCondVar(gWatchdogWakeup); 1.3293 + } 1.3294 + gWatchdogHasTimeout = true; 1.3295 + gWatchdogTimeout = timeout; 1.3296 + PR_Unlock(gWatchdogLock); 1.3297 + return true; 1.3298 +} 1.3299 + 1.3300 +#else /* !JS_THREADSAFE */ 1.3301 + 1.3302 +#ifdef XP_WIN 1.3303 +static HANDLE gTimerHandle = 0; 1.3304 + 1.3305 +VOID CALLBACK 1.3306 +TimerCallback(PVOID lpParameter, BOOLEAN TimerOrWaitFired) 1.3307 +{ 1.3308 + CancelExecution((JSRuntime *) lpParameter); 1.3309 +} 1.3310 + 1.3311 +#else 1.3312 + 1.3313 +static void 1.3314 +AlarmHandler(int sig) 1.3315 +{ 1.3316 + CancelExecution(gRuntime); 1.3317 +} 1.3318 + 1.3319 +#endif 1.3320 + 1.3321 +static bool 1.3322 +InitWatchdog(JSRuntime *rt) 1.3323 +{ 1.3324 + gRuntime = rt; 1.3325 + return true; 1.3326 +} 1.3327 + 1.3328 +static void 1.3329 +KillWatchdog() 1.3330 +{ 1.3331 + ScheduleWatchdog(gRuntime, -1); 1.3332 +} 1.3333 + 1.3334 +static bool 1.3335 +ScheduleWatchdog(JSRuntime *rt, double t) 1.3336 +{ 1.3337 +#ifdef XP_WIN 1.3338 + if (gTimerHandle) { 1.3339 + DeleteTimerQueueTimer(nullptr, gTimerHandle, nullptr); 1.3340 + gTimerHandle = 0; 1.3341 + } 1.3342 + if (t > 0 && 1.3343 + !CreateTimerQueueTimer(&gTimerHandle, 1.3344 + nullptr, 1.3345 + (WAITORTIMERCALLBACK)TimerCallback, 1.3346 + rt, 1.3347 + DWORD(ceil(t * 1000.0)), 1.3348 + 0, 1.3349 + WT_EXECUTEINTIMERTHREAD | WT_EXECUTEONLYONCE)) { 1.3350 + gTimerHandle = 0; 1.3351 + return false; 1.3352 + } 1.3353 +#else 1.3354 + /* FIXME: use setitimer when available for sub-second resolution. */ 1.3355 + if (t <= 0) { 1.3356 + alarm(0); 1.3357 + signal(SIGALRM, nullptr); 1.3358 + } else { 1.3359 + signal(SIGALRM, AlarmHandler); /* set the Alarm signal capture */ 1.3360 + alarm(ceil(t)); 1.3361 + } 1.3362 +#endif 1.3363 + return true; 1.3364 +} 1.3365 + 1.3366 +#endif /* !JS_THREADSAFE */ 1.3367 + 1.3368 +static void 1.3369 +CancelExecution(JSRuntime *rt) 1.3370 +{ 1.3371 + gServiceInterrupt = true; 1.3372 + JS_RequestInterruptCallback(rt); 1.3373 + 1.3374 + if (!gInterruptFunc.ref().get().isNull()) { 1.3375 + static const char msg[] = "Script runs for too long, terminating.\n"; 1.3376 +#if defined(XP_UNIX) && !defined(JS_THREADSAFE) 1.3377 + /* It is not safe to call fputs from signals. */ 1.3378 + /* Dummy assignment avoids GCC warning on "attribute warn_unused_result" */ 1.3379 + ssize_t dummy = write(2, msg, sizeof(msg) - 1); 1.3380 + (void)dummy; 1.3381 +#else 1.3382 + fputs(msg, stderr); 1.3383 +#endif 1.3384 + } 1.3385 +} 1.3386 + 1.3387 +static bool 1.3388 +SetTimeoutValue(JSContext *cx, double t) 1.3389 +{ 1.3390 + /* NB: The next condition also filter out NaNs. */ 1.3391 + if (!(t <= MAX_TIMEOUT_INTERVAL)) { 1.3392 + JS_ReportError(cx, "Excessive timeout value"); 1.3393 + return false; 1.3394 + } 1.3395 + gTimeoutInterval = t; 1.3396 + if (!ScheduleWatchdog(cx->runtime(), t)) { 1.3397 + JS_ReportError(cx, "Failed to create the watchdog"); 1.3398 + return false; 1.3399 + } 1.3400 + return true; 1.3401 +} 1.3402 + 1.3403 +static bool 1.3404 +Timeout(JSContext *cx, unsigned argc, Value *vp) 1.3405 +{ 1.3406 + CallArgs args = CallArgsFromVp(argc, vp); 1.3407 + 1.3408 + if (args.length() == 0) { 1.3409 + args.rval().setNumber(gTimeoutInterval); 1.3410 + return true; 1.3411 + } 1.3412 + 1.3413 + if (args.length() > 2) { 1.3414 + JS_ReportError(cx, "Wrong number of arguments"); 1.3415 + return false; 1.3416 + } 1.3417 + 1.3418 + double t; 1.3419 + if (!ToNumber(cx, args[0], &t)) 1.3420 + return false; 1.3421 + 1.3422 + if (args.length() > 1) { 1.3423 + RootedValue value(cx, args[1]); 1.3424 + if (!value.isObject() || !value.toObject().is<JSFunction>()) { 1.3425 + JS_ReportError(cx, "Second argument must be a timeout function"); 1.3426 + return false; 1.3427 + } 1.3428 + gInterruptFunc.ref() = value; 1.3429 + } 1.3430 + 1.3431 + args.rval().setUndefined(); 1.3432 + return SetTimeoutValue(cx, t); 1.3433 +} 1.3434 + 1.3435 +static bool 1.3436 +InterruptIf(JSContext *cx, unsigned argc, Value *vp) 1.3437 +{ 1.3438 + CallArgs args = CallArgsFromVp(argc, vp); 1.3439 + 1.3440 + if (args.length() != 1) { 1.3441 + JS_ReportError(cx, "Wrong number of arguments"); 1.3442 + return false; 1.3443 + } 1.3444 + 1.3445 + if (ToBoolean(args[0])) { 1.3446 + gServiceInterrupt = true; 1.3447 + JS_RequestInterruptCallback(cx->runtime()); 1.3448 + } 1.3449 + 1.3450 + args.rval().setUndefined(); 1.3451 + return true; 1.3452 +} 1.3453 + 1.3454 +static bool 1.3455 +SetInterruptCallback(JSContext *cx, unsigned argc, Value *vp) 1.3456 +{ 1.3457 + CallArgs args = CallArgsFromVp(argc, vp); 1.3458 + 1.3459 + if (args.length() != 1) { 1.3460 + JS_ReportError(cx, "Wrong number of arguments"); 1.3461 + return false; 1.3462 + } 1.3463 + 1.3464 + RootedValue value(cx, args[0]); 1.3465 + if (!value.isObject() || !value.toObject().is<JSFunction>()) { 1.3466 + JS_ReportError(cx, "Argument must be a function"); 1.3467 + return false; 1.3468 + } 1.3469 + gInterruptFunc.ref() = value; 1.3470 + 1.3471 + args.rval().setUndefined(); 1.3472 + return true; 1.3473 +} 1.3474 + 1.3475 +static bool 1.3476 +Elapsed(JSContext *cx, unsigned argc, jsval *vp) 1.3477 +{ 1.3478 + CallArgs args = CallArgsFromVp(argc, vp); 1.3479 + if (args.length() == 0) { 1.3480 + double d = 0.0; 1.3481 + JSShellContextData *data = GetContextData(cx); 1.3482 + if (data) 1.3483 + d = PRMJ_Now() - data->startTime; 1.3484 + args.rval().setDouble(d); 1.3485 + return true; 1.3486 + } 1.3487 + JS_ReportError(cx, "Wrong number of arguments"); 1.3488 + return false; 1.3489 +} 1.3490 + 1.3491 +static bool 1.3492 +Parent(JSContext *cx, unsigned argc, jsval *vp) 1.3493 +{ 1.3494 + CallArgs args = CallArgsFromVp(argc, vp); 1.3495 + if (args.length() != 1) { 1.3496 + JS_ReportError(cx, "Wrong number of arguments"); 1.3497 + return false; 1.3498 + } 1.3499 + 1.3500 + Value v = args[0]; 1.3501 + if (JSVAL_IS_PRIMITIVE(v)) { 1.3502 + JS_ReportError(cx, "Only objects have parents!"); 1.3503 + return false; 1.3504 + } 1.3505 + 1.3506 + Rooted<JSObject*> parent(cx, JS_GetParent(&v.toObject())); 1.3507 + args.rval().setObjectOrNull(parent); 1.3508 + 1.3509 + /* Outerize if necessary. Embrace the ugliness! */ 1.3510 + if (parent) { 1.3511 + if (JSObjectOp op = parent->getClass()->ext.outerObject) 1.3512 + args.rval().setObjectOrNull(op(cx, parent)); 1.3513 + } 1.3514 + 1.3515 + return true; 1.3516 +} 1.3517 + 1.3518 +static bool 1.3519 +Compile(JSContext *cx, unsigned argc, jsval *vp) 1.3520 +{ 1.3521 + CallArgs args = CallArgsFromVp(argc, vp); 1.3522 + if (args.length() < 1) { 1.3523 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED, 1.3524 + "compile", "0", "s"); 1.3525 + return false; 1.3526 + } 1.3527 + if (!args[0].isString()) { 1.3528 + const char *typeName = JS_GetTypeName(cx, JS_TypeOfValue(cx, args[0])); 1.3529 + JS_ReportError(cx, "expected string to compile, got %s", typeName); 1.3530 + return false; 1.3531 + } 1.3532 + 1.3533 + RootedObject global(cx, JS::CurrentGlobalOrNull(cx)); 1.3534 + JSString *scriptContents = args[0].toString(); 1.3535 + JS::AutoSaveContextOptions asco(cx); 1.3536 + JS::ContextOptionsRef(cx).setNoScriptRval(true); 1.3537 + JS::CompileOptions options(cx); 1.3538 + options.setIntroductionType("js shell compile") 1.3539 + .setFileAndLine("<string>", 1) 1.3540 + .setCompileAndGo(true); 1.3541 + bool ok = JS_CompileUCScript(cx, global, JS_GetStringCharsZ(cx, scriptContents), 1.3542 + JS_GetStringLength(scriptContents), options); 1.3543 + args.rval().setUndefined(); 1.3544 + return ok; 1.3545 +} 1.3546 + 1.3547 +static bool 1.3548 +Parse(JSContext *cx, unsigned argc, jsval *vp) 1.3549 +{ 1.3550 + using namespace js::frontend; 1.3551 + 1.3552 + CallArgs args = CallArgsFromVp(argc, vp); 1.3553 + 1.3554 + if (args.length() < 1) { 1.3555 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED, 1.3556 + "parse", "0", "s"); 1.3557 + return false; 1.3558 + } 1.3559 + if (!args[0].isString()) { 1.3560 + const char *typeName = JS_GetTypeName(cx, JS_TypeOfValue(cx, args[0])); 1.3561 + JS_ReportError(cx, "expected string to parse, got %s", typeName); 1.3562 + return false; 1.3563 + } 1.3564 + 1.3565 + JSString *scriptContents = args[0].toString(); 1.3566 + CompileOptions options(cx); 1.3567 + options.setIntroductionType("js shell parse") 1.3568 + .setFileAndLine("<string>", 1) 1.3569 + .setCompileAndGo(false); 1.3570 + Parser<FullParseHandler> parser(cx, &cx->tempLifoAlloc(), options, 1.3571 + JS_GetStringCharsZ(cx, scriptContents), 1.3572 + JS_GetStringLength(scriptContents), 1.3573 + /* foldConstants = */ true, nullptr, nullptr); 1.3574 + 1.3575 + ParseNode *pn = parser.parse(nullptr); 1.3576 + if (!pn) 1.3577 + return false; 1.3578 +#ifdef DEBUG 1.3579 + DumpParseTree(pn); 1.3580 + fputc('\n', stderr); 1.3581 +#endif 1.3582 + args.rval().setUndefined(); 1.3583 + return true; 1.3584 +} 1.3585 + 1.3586 +static bool 1.3587 +SyntaxParse(JSContext *cx, unsigned argc, jsval *vp) 1.3588 +{ 1.3589 + using namespace js::frontend; 1.3590 + 1.3591 + CallArgs args = CallArgsFromVp(argc, vp); 1.3592 + 1.3593 + if (args.length() < 1) { 1.3594 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED, 1.3595 + "parse", "0", "s"); 1.3596 + return false; 1.3597 + } 1.3598 + if (!args[0].isString()) { 1.3599 + const char *typeName = JS_GetTypeName(cx, JS_TypeOfValue(cx, args[0])); 1.3600 + JS_ReportError(cx, "expected string to parse, got %s", typeName); 1.3601 + return false; 1.3602 + } 1.3603 + 1.3604 + JSString *scriptContents = args[0].toString(); 1.3605 + CompileOptions options(cx); 1.3606 + options.setIntroductionType("js shell syntaxParse") 1.3607 + .setFileAndLine("<string>", 1) 1.3608 + .setCompileAndGo(false); 1.3609 + 1.3610 + const jschar *chars = JS_GetStringCharsZ(cx, scriptContents); 1.3611 + size_t length = JS_GetStringLength(scriptContents); 1.3612 + Parser<frontend::SyntaxParseHandler> parser(cx, &cx->tempLifoAlloc(), 1.3613 + options, chars, length, false, nullptr, nullptr); 1.3614 + 1.3615 + bool succeeded = parser.parse(nullptr); 1.3616 + if (cx->isExceptionPending()) 1.3617 + return false; 1.3618 + 1.3619 + if (!succeeded && !parser.hadAbortedSyntaxParse()) { 1.3620 + // If no exception is posted, either there was an OOM or a language 1.3621 + // feature unhandled by the syntax parser was encountered. 1.3622 + JS_ASSERT(cx->runtime()->hadOutOfMemory); 1.3623 + return false; 1.3624 + } 1.3625 + 1.3626 + args.rval().setBoolean(succeeded); 1.3627 + return true; 1.3628 +} 1.3629 + 1.3630 +#ifdef JS_THREADSAFE 1.3631 + 1.3632 +class OffThreadState { 1.3633 + public: 1.3634 + enum State { 1.3635 + IDLE, /* ready to work; no token, no source */ 1.3636 + COMPILING, /* working; no token, have source */ 1.3637 + DONE /* compilation done: have token and source */ 1.3638 + }; 1.3639 + 1.3640 + OffThreadState() : monitor(), state(IDLE), token() { } 1.3641 + bool init() { return monitor.init(); } 1.3642 + 1.3643 + bool startIfIdle(JSContext *cx, JSString *newSource) { 1.3644 + AutoLockMonitor alm(monitor); 1.3645 + if (state != IDLE) 1.3646 + return false; 1.3647 + 1.3648 + JS_ASSERT(!token); 1.3649 + 1.3650 + source.construct(cx, newSource); 1.3651 + 1.3652 + state = COMPILING; 1.3653 + return true; 1.3654 + } 1.3655 + 1.3656 + void abandon(JSContext *cx) { 1.3657 + AutoLockMonitor alm(monitor); 1.3658 + JS_ASSERT(state == COMPILING); 1.3659 + JS_ASSERT(!token); 1.3660 + JS_ASSERT(source.ref()); 1.3661 + 1.3662 + source.destroy(); 1.3663 + 1.3664 + state = IDLE; 1.3665 + } 1.3666 + 1.3667 + void markDone(void *newToken) { 1.3668 + AutoLockMonitor alm(monitor); 1.3669 + JS_ASSERT(state == COMPILING); 1.3670 + JS_ASSERT(!token); 1.3671 + JS_ASSERT(source.ref()); 1.3672 + JS_ASSERT(newToken); 1.3673 + 1.3674 + token = newToken; 1.3675 + state = DONE; 1.3676 + alm.notify(); 1.3677 + } 1.3678 + 1.3679 + void *waitUntilDone(JSContext *cx) { 1.3680 + AutoLockMonitor alm(monitor); 1.3681 + if (state == IDLE) 1.3682 + return nullptr; 1.3683 + 1.3684 + if (state == COMPILING) { 1.3685 + while (state != DONE) 1.3686 + alm.wait(); 1.3687 + } 1.3688 + 1.3689 + JS_ASSERT(source.ref()); 1.3690 + source.destroy(); 1.3691 + 1.3692 + JS_ASSERT(token); 1.3693 + void *holdToken = token; 1.3694 + token = nullptr; 1.3695 + state = IDLE; 1.3696 + return holdToken; 1.3697 + } 1.3698 + 1.3699 + private: 1.3700 + Monitor monitor; 1.3701 + State state; 1.3702 + void *token; 1.3703 + Maybe<PersistentRootedString> source; 1.3704 +}; 1.3705 + 1.3706 +static OffThreadState offThreadState; 1.3707 + 1.3708 +static void 1.3709 +OffThreadCompileScriptCallback(void *token, void *callbackData) 1.3710 +{ 1.3711 + offThreadState.markDone(token); 1.3712 +} 1.3713 + 1.3714 +static bool 1.3715 +OffThreadCompileScript(JSContext *cx, unsigned argc, jsval *vp) 1.3716 +{ 1.3717 + CallArgs args = CallArgsFromVp(argc, vp); 1.3718 + 1.3719 + if (args.length() < 1) { 1.3720 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED, 1.3721 + "offThreadCompileScript", "0", "s"); 1.3722 + return false; 1.3723 + } 1.3724 + if (!args[0].isString()) { 1.3725 + const char *typeName = JS_GetTypeName(cx, JS_TypeOfValue(cx, args[0])); 1.3726 + JS_ReportError(cx, "expected string to parse, got %s", typeName); 1.3727 + return false; 1.3728 + } 1.3729 + 1.3730 + JSAutoByteString fileNameBytes; 1.3731 + CompileOptions options(cx); 1.3732 + options.setIntroductionType("js shell offThreadCompileScript") 1.3733 + .setFileAndLine("<string>", 1); 1.3734 + 1.3735 + if (args.length() >= 2) { 1.3736 + if (args[1].isPrimitive()) { 1.3737 + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS, "evaluate"); 1.3738 + return false; 1.3739 + } 1.3740 + 1.3741 + RootedObject opts(cx, &args[1].toObject()); 1.3742 + if (!ParseCompileOptions(cx, options, opts, fileNameBytes)) 1.3743 + return false; 1.3744 + } 1.3745 + 1.3746 + // These option settings must override whatever the caller requested. 1.3747 + options.setCompileAndGo(true) 1.3748 + .setSourceIsLazy(false); 1.3749 + 1.3750 + // We assume the caller wants caching if at all possible, ignoring 1.3751 + // heuristics that make sense for a real browser. 1.3752 + options.forceAsync = true; 1.3753 + 1.3754 + JSString *scriptContents = args[0].toString(); 1.3755 + const jschar *chars = JS_GetStringCharsZ(cx, scriptContents); 1.3756 + if (!chars) 1.3757 + return false; 1.3758 + size_t length = JS_GetStringLength(scriptContents); 1.3759 + 1.3760 + if (!JS::CanCompileOffThread(cx, options, length)) { 1.3761 + JS_ReportError(cx, "cannot compile code on worker thread"); 1.3762 + return false; 1.3763 + } 1.3764 + 1.3765 + if (!offThreadState.startIfIdle(cx, scriptContents)) { 1.3766 + JS_ReportError(cx, "called offThreadCompileScript without calling runOffThreadScript" 1.3767 + " to receive prior off-thread compilation"); 1.3768 + return false; 1.3769 + } 1.3770 + 1.3771 + if (!JS::CompileOffThread(cx, options, chars, length, 1.3772 + OffThreadCompileScriptCallback, nullptr)) 1.3773 + { 1.3774 + offThreadState.abandon(cx); 1.3775 + return false; 1.3776 + } 1.3777 + 1.3778 + args.rval().setUndefined(); 1.3779 + return true; 1.3780 +} 1.3781 + 1.3782 +static bool 1.3783 +runOffThreadScript(JSContext *cx, unsigned argc, jsval *vp) 1.3784 +{ 1.3785 + CallArgs args = CallArgsFromVp(argc, vp); 1.3786 + 1.3787 + void *token = offThreadState.waitUntilDone(cx); 1.3788 + if (!token) { 1.3789 + JS_ReportError(cx, "called runOffThreadScript when no compilation is pending"); 1.3790 + return false; 1.3791 + } 1.3792 + 1.3793 + RootedScript script(cx, JS::FinishOffThreadScript(cx, cx->runtime(), token)); 1.3794 + if (!script) 1.3795 + return false; 1.3796 + 1.3797 + return JS_ExecuteScript(cx, cx->global(), script, args.rval()); 1.3798 +} 1.3799 + 1.3800 +#endif // JS_THREADSAFE 1.3801 + 1.3802 +struct FreeOnReturn 1.3803 +{ 1.3804 + JSContext *cx; 1.3805 + const char *ptr; 1.3806 + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER 1.3807 + 1.3808 + FreeOnReturn(JSContext *cx, const char *ptr = nullptr 1.3809 + MOZ_GUARD_OBJECT_NOTIFIER_PARAM) 1.3810 + : cx(cx), ptr(ptr) 1.3811 + { 1.3812 + MOZ_GUARD_OBJECT_NOTIFIER_INIT; 1.3813 + } 1.3814 + 1.3815 + void init(const char *ptr) { 1.3816 + JS_ASSERT(!this->ptr); 1.3817 + this->ptr = ptr; 1.3818 + } 1.3819 + 1.3820 + ~FreeOnReturn() { 1.3821 + JS_free(cx, (void*)ptr); 1.3822 + } 1.3823 +}; 1.3824 + 1.3825 +static bool 1.3826 +ReadFile(JSContext *cx, unsigned argc, jsval *vp, bool scriptRelative) 1.3827 +{ 1.3828 + CallArgs args = CallArgsFromVp(argc, vp); 1.3829 + 1.3830 + if (args.length() < 1 || args.length() > 2) { 1.3831 + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, 1.3832 + args.length() < 1 ? JSSMSG_NOT_ENOUGH_ARGS : JSSMSG_TOO_MANY_ARGS, 1.3833 + "snarf"); 1.3834 + return false; 1.3835 + } 1.3836 + 1.3837 + if (!args[0].isString() || (args.length() == 2 && !args[1].isString())) { 1.3838 + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS, "snarf"); 1.3839 + return false; 1.3840 + } 1.3841 + 1.3842 + RootedString givenPath(cx, args[0].toString()); 1.3843 + RootedString str(cx, ResolvePath(cx, givenPath, scriptRelative ? ScriptRelative : RootRelative)); 1.3844 + if (!str) 1.3845 + return false; 1.3846 + 1.3847 + JSAutoByteString filename(cx, str); 1.3848 + if (!filename) 1.3849 + return false; 1.3850 + 1.3851 + if (args.length() > 1) { 1.3852 + JSString *opt = JS::ToString(cx, args[1]); 1.3853 + if (!opt) 1.3854 + return false; 1.3855 + bool match; 1.3856 + if (!JS_StringEqualsAscii(cx, opt, "binary", &match)) 1.3857 + return false; 1.3858 + if (match) { 1.3859 + JSObject *obj; 1.3860 + if (!(obj = FileAsTypedArray(cx, filename.ptr()))) 1.3861 + return false; 1.3862 + args.rval().setObject(*obj); 1.3863 + return true; 1.3864 + } 1.3865 + } 1.3866 + 1.3867 + if (!(str = FileAsString(cx, filename.ptr()))) 1.3868 + return false; 1.3869 + args.rval().setString(str); 1.3870 + return true; 1.3871 +} 1.3872 + 1.3873 +static bool 1.3874 +Snarf(JSContext *cx, unsigned argc, jsval *vp) 1.3875 +{ 1.3876 + return ReadFile(cx, argc, vp, false); 1.3877 +} 1.3878 + 1.3879 +static bool 1.3880 +ReadRelativeToScript(JSContext *cx, unsigned argc, jsval *vp) 1.3881 +{ 1.3882 + return ReadFile(cx, argc, vp, true); 1.3883 +} 1.3884 + 1.3885 +static bool 1.3886 +redirect(JSContext *cx, FILE* fp, HandleString relFilename) 1.3887 +{ 1.3888 + RootedString filename(cx, ResolvePath(cx, relFilename, RootRelative)); 1.3889 + if (!filename) 1.3890 + return false; 1.3891 + JSAutoByteString filenameABS(cx, filename); 1.3892 + if (!filenameABS) 1.3893 + return false; 1.3894 + if (freopen(filenameABS.ptr(), "wb", fp) == nullptr) { 1.3895 + JS_ReportError(cx, "cannot redirect to %s: %s", filenameABS.ptr(), strerror(errno)); 1.3896 + return false; 1.3897 + } 1.3898 + return true; 1.3899 +} 1.3900 + 1.3901 +static bool 1.3902 +RedirectOutput(JSContext *cx, unsigned argc, jsval *vp) 1.3903 +{ 1.3904 + CallArgs args = CallArgsFromVp(argc, vp); 1.3905 + 1.3906 + if (args.length() < 1 || args.length() > 2) { 1.3907 + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS, "redirect"); 1.3908 + return false; 1.3909 + } 1.3910 + 1.3911 + if (args[0].isString()) { 1.3912 + RootedString stdoutPath(cx, args[0].toString()); 1.3913 + if (!stdoutPath) 1.3914 + return false; 1.3915 + if (!redirect(cx, stdout, stdoutPath)) 1.3916 + return false; 1.3917 + } 1.3918 + 1.3919 + if (args.length() > 1 && args[1].isString()) { 1.3920 + RootedString stderrPath(cx, args[1].toString()); 1.3921 + if (!stderrPath) 1.3922 + return false; 1.3923 + if (!redirect(cx, stderr, stderrPath)) 1.3924 + return false; 1.3925 + } 1.3926 + 1.3927 + args.rval().setUndefined(); 1.3928 + return true; 1.3929 +} 1.3930 + 1.3931 +static bool 1.3932 +System(JSContext *cx, unsigned argc, jsval *vp) 1.3933 +{ 1.3934 + CallArgs args = CallArgsFromVp(argc, vp); 1.3935 + 1.3936 + if (args.length() == 0) { 1.3937 + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS, 1.3938 + "system"); 1.3939 + return false; 1.3940 + } 1.3941 + 1.3942 + JSString *str = JS::ToString(cx, args[0]); 1.3943 + if (!str) 1.3944 + return false; 1.3945 + 1.3946 + JSAutoByteString command(cx, str); 1.3947 + if (!command) 1.3948 + return false; 1.3949 + 1.3950 + int result = system(command.ptr()); 1.3951 + args.rval().setInt32(result); 1.3952 + return true; 1.3953 +} 1.3954 + 1.3955 +static int sArgc; 1.3956 +static char **sArgv; 1.3957 + 1.3958 +class AutoCStringVector 1.3959 +{ 1.3960 + Vector<char *> argv_; 1.3961 + public: 1.3962 + AutoCStringVector(JSContext *cx) : argv_(cx) {} 1.3963 + ~AutoCStringVector() { 1.3964 + for (size_t i = 0; i < argv_.length(); i++) 1.3965 + js_free(argv_[i]); 1.3966 + } 1.3967 + bool append(char *arg) { 1.3968 + if (!argv_.append(arg)) { 1.3969 + js_free(arg); 1.3970 + return false; 1.3971 + } 1.3972 + return true; 1.3973 + } 1.3974 + char* const* get() const { 1.3975 + return argv_.begin(); 1.3976 + } 1.3977 + size_t length() const { 1.3978 + return argv_.length(); 1.3979 + } 1.3980 + char *operator[](size_t i) const { 1.3981 + return argv_[i]; 1.3982 + } 1.3983 + void replace(size_t i, char *arg) { 1.3984 + js_free(argv_[i]); 1.3985 + argv_[i] = arg; 1.3986 + } 1.3987 + char *back() const { 1.3988 + return argv_.back(); 1.3989 + } 1.3990 + void replaceBack(char *arg) { 1.3991 + js_free(argv_.back()); 1.3992 + argv_.back() = arg; 1.3993 + } 1.3994 +}; 1.3995 + 1.3996 +#if defined(XP_WIN) 1.3997 +static bool 1.3998 +EscapeForShell(AutoCStringVector &argv) 1.3999 +{ 1.4000 + // Windows will break arguments in argv by various spaces, so we wrap each 1.4001 + // argument in quotes and escape quotes within. Even with quotes, \ will be 1.4002 + // treated like an escape character, so inflate each \ to \\. 1.4003 + 1.4004 + for (size_t i = 0; i < argv.length(); i++) { 1.4005 + if (!argv[i]) 1.4006 + continue; 1.4007 + 1.4008 + size_t newLen = 3; // quotes before and after and null-terminator 1.4009 + for (char *p = argv[i]; *p; p++) { 1.4010 + newLen++; 1.4011 + if (*p == '\"' || *p == '\\') 1.4012 + newLen++; 1.4013 + } 1.4014 + 1.4015 + char *escaped = (char *)js_malloc(newLen); 1.4016 + if (!escaped) 1.4017 + return false; 1.4018 + 1.4019 + char *src = argv[i]; 1.4020 + char *dst = escaped; 1.4021 + *dst++ = '\"'; 1.4022 + while (*src) { 1.4023 + if (*src == '\"' || *src == '\\') 1.4024 + *dst++ = '\\'; 1.4025 + *dst++ = *src++; 1.4026 + } 1.4027 + *dst++ = '\"'; 1.4028 + *dst++ = '\0'; 1.4029 + JS_ASSERT(escaped + newLen == dst); 1.4030 + 1.4031 + argv.replace(i, escaped); 1.4032 + } 1.4033 + return true; 1.4034 +} 1.4035 +#endif 1.4036 + 1.4037 +static Vector<const char*, 4, js::SystemAllocPolicy> sPropagatedFlags; 1.4038 + 1.4039 +#ifdef DEBUG 1.4040 +#if (defined(JS_CPU_X86) || defined(JS_CPU_X64)) && defined(JS_ION) 1.4041 +static bool 1.4042 +PropagateFlagToNestedShells(const char *flag) 1.4043 +{ 1.4044 + return sPropagatedFlags.append(flag); 1.4045 +} 1.4046 +#endif 1.4047 +#endif 1.4048 + 1.4049 +static bool 1.4050 +NestedShell(JSContext *cx, unsigned argc, jsval *vp) 1.4051 +{ 1.4052 + CallArgs args = CallArgsFromVp(argc, vp); 1.4053 + 1.4054 + AutoCStringVector argv(cx); 1.4055 + 1.4056 + // The first argument to the shell is its path, which we assume is our own 1.4057 + // argv[0]. 1.4058 + if (sArgc < 1) { 1.4059 + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_NESTED_FAIL); 1.4060 + return false; 1.4061 + } 1.4062 + if (!argv.append(strdup(sArgv[0]))) 1.4063 + return false; 1.4064 + 1.4065 + // Propagate selected flags from the current shell 1.4066 + for (unsigned i = 0; i < sPropagatedFlags.length(); i++) { 1.4067 + char *cstr = strdup(sPropagatedFlags[i]); 1.4068 + if (!cstr || !argv.append(cstr)) 1.4069 + return false; 1.4070 + } 1.4071 + 1.4072 + // The arguments to nestedShell are stringified and append to argv. 1.4073 + RootedString str(cx); 1.4074 + for (unsigned i = 0; i < args.length(); i++) { 1.4075 + str = ToString(cx, args[i]); 1.4076 + if (!str || !argv.append(JS_EncodeString(cx, str))) 1.4077 + return false; 1.4078 + 1.4079 + // As a special case, if the caller passes "--js-cache", replace that 1.4080 + // with "--js-cache=$(jsCacheDir)" 1.4081 + if (!strcmp(argv.back(), "--js-cache") && jsCacheDir) { 1.4082 + char *newArg = JS_smprintf("--js-cache=%s", jsCacheDir); 1.4083 + if (!newArg) 1.4084 + return false; 1.4085 + argv.replaceBack(newArg); 1.4086 + } 1.4087 + } 1.4088 + 1.4089 + // execv assumes argv is null-terminated 1.4090 + if (!argv.append(nullptr)) 1.4091 + return false; 1.4092 + 1.4093 + int status = 0; 1.4094 +#if defined(XP_WIN) 1.4095 + if (!EscapeForShell(argv)) 1.4096 + return false; 1.4097 + status = _spawnv(_P_WAIT, sArgv[0], argv.get()); 1.4098 +#else 1.4099 + pid_t pid = fork(); 1.4100 + switch (pid) { 1.4101 + case -1: 1.4102 + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_NESTED_FAIL); 1.4103 + return false; 1.4104 + case 0: 1.4105 + (void)execv(sArgv[0], argv.get()); 1.4106 + exit(-1); 1.4107 + default: { 1.4108 + while (waitpid(pid, &status, 0) < 0 && errno == EINTR) 1.4109 + continue; 1.4110 + break; 1.4111 + } 1.4112 + } 1.4113 +#endif 1.4114 + 1.4115 + if (status != 0) { 1.4116 + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_NESTED_FAIL); 1.4117 + return false; 1.4118 + } 1.4119 + 1.4120 + args.rval().setUndefined(); 1.4121 + return true; 1.4122 +} 1.4123 + 1.4124 +static bool 1.4125 +DecompileFunctionSomehow(JSContext *cx, unsigned argc, Value *vp, 1.4126 + JSString *(*decompiler)(JSContext *, HandleFunction, unsigned)) 1.4127 +{ 1.4128 + CallArgs args = CallArgsFromVp(argc, vp); 1.4129 + if (args.length() < 1 || !args[0].isObject() || !args[0].toObject().is<JSFunction>()) { 1.4130 + args.rval().setUndefined(); 1.4131 + return true; 1.4132 + } 1.4133 + RootedFunction fun(cx, &args[0].toObject().as<JSFunction>()); 1.4134 + JSString *result = decompiler(cx, fun, 0); 1.4135 + if (!result) 1.4136 + return false; 1.4137 + args.rval().setString(result); 1.4138 + return true; 1.4139 +} 1.4140 + 1.4141 +static bool 1.4142 +DecompileBody(JSContext *cx, unsigned argc, Value *vp) 1.4143 +{ 1.4144 + return DecompileFunctionSomehow(cx, argc, vp, JS_DecompileFunctionBody); 1.4145 +} 1.4146 + 1.4147 +static bool 1.4148 +DecompileFunction(JSContext *cx, unsigned argc, Value *vp) 1.4149 +{ 1.4150 + return DecompileFunctionSomehow(cx, argc, vp, JS_DecompileFunction); 1.4151 +} 1.4152 + 1.4153 +static bool 1.4154 +DecompileThisScript(JSContext *cx, unsigned argc, Value *vp) 1.4155 +{ 1.4156 + CallArgs args = CallArgsFromVp(argc, vp); 1.4157 + 1.4158 + NonBuiltinScriptFrameIter iter(cx); 1.4159 + if (iter.done()) { 1.4160 + args.rval().setString(cx->runtime()->emptyString); 1.4161 + return true; 1.4162 + } 1.4163 + 1.4164 + { 1.4165 + JSAutoCompartment ac(cx, iter.script()); 1.4166 + 1.4167 + RootedScript script(cx, iter.script()); 1.4168 + JSString *result = JS_DecompileScript(cx, script, "test", 0); 1.4169 + if (!result) 1.4170 + return false; 1.4171 + 1.4172 + args.rval().setString(result); 1.4173 + } 1.4174 + 1.4175 + return JS_WrapValue(cx, args.rval()); 1.4176 +} 1.4177 + 1.4178 +static bool 1.4179 +ThisFilename(JSContext *cx, unsigned argc, Value *vp) 1.4180 +{ 1.4181 + CallArgs args = CallArgsFromVp(argc, vp); 1.4182 + 1.4183 + JS::AutoFilename filename; 1.4184 + if (!DescribeScriptedCaller(cx, &filename) || !filename.get()) { 1.4185 + args.rval().setString(cx->runtime()->emptyString); 1.4186 + return true; 1.4187 + } 1.4188 + 1.4189 + JSString *str = JS_NewStringCopyZ(cx, filename.get()); 1.4190 + if (!str) 1.4191 + return false; 1.4192 + 1.4193 + args.rval().setString(str); 1.4194 + return true; 1.4195 +} 1.4196 + 1.4197 +static bool 1.4198 +Wrap(JSContext *cx, unsigned argc, jsval *vp) 1.4199 +{ 1.4200 + CallArgs args = CallArgsFromVp(argc, vp); 1.4201 + Value v = args.get(0); 1.4202 + if (JSVAL_IS_PRIMITIVE(v)) { 1.4203 + args.rval().set(v); 1.4204 + return true; 1.4205 + } 1.4206 + 1.4207 + RootedObject obj(cx, JSVAL_TO_OBJECT(v)); 1.4208 + JSObject *wrapped = Wrapper::New(cx, obj, &obj->global(), 1.4209 + &Wrapper::singleton); 1.4210 + if (!wrapped) 1.4211 + return false; 1.4212 + 1.4213 + args.rval().setObject(*wrapped); 1.4214 + return true; 1.4215 +} 1.4216 + 1.4217 +static bool 1.4218 +WrapWithProto(JSContext *cx, unsigned argc, jsval *vp) 1.4219 +{ 1.4220 + CallArgs args = CallArgsFromVp(argc, vp); 1.4221 + Value obj = UndefinedValue(), proto = UndefinedValue(); 1.4222 + if (args.length() == 2) { 1.4223 + obj = args[0]; 1.4224 + proto = args[1]; 1.4225 + } 1.4226 + if (!obj.isObject() || !proto.isObjectOrNull()) { 1.4227 + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS, 1.4228 + "wrapWithProto"); 1.4229 + return false; 1.4230 + } 1.4231 + 1.4232 + WrapperOptions options(cx); 1.4233 + options.setProto(proto.toObjectOrNull()); 1.4234 + options.selectDefaultClass(obj.toObject().isCallable()); 1.4235 + JSObject *wrapped = Wrapper::New(cx, &obj.toObject(), &obj.toObject().global(), 1.4236 + &Wrapper::singletonWithPrototype, &options); 1.4237 + if (!wrapped) 1.4238 + return false; 1.4239 + 1.4240 + args.rval().setObject(*wrapped); 1.4241 + return true; 1.4242 +} 1.4243 + 1.4244 +static bool 1.4245 +NewGlobal(JSContext *cx, unsigned argc, jsval *vp) 1.4246 +{ 1.4247 + JSPrincipals *principals = nullptr; 1.4248 + JS::CompartmentOptions options; 1.4249 + options.setVersion(JSVERSION_LATEST); 1.4250 + 1.4251 + CallArgs args = CallArgsFromVp(argc, vp); 1.4252 + if (args.length() == 1 && args[0].isObject()) { 1.4253 + RootedObject opts(cx, &args[0].toObject()); 1.4254 + RootedValue v(cx); 1.4255 + 1.4256 + if (!JS_GetProperty(cx, opts, "sameZoneAs", &v)) 1.4257 + return false; 1.4258 + if (v.isObject()) 1.4259 + options.setSameZoneAs(UncheckedUnwrap(&v.toObject())); 1.4260 + 1.4261 + if (!JS_GetProperty(cx, opts, "invisibleToDebugger", &v)) 1.4262 + return false; 1.4263 + if (v.isBoolean()) 1.4264 + options.setInvisibleToDebugger(v.toBoolean()); 1.4265 + 1.4266 + if (!JS_GetProperty(cx, opts, "principal", &v)) 1.4267 + return false; 1.4268 + if (!v.isUndefined()) { 1.4269 + uint32_t bits; 1.4270 + if (!ToUint32(cx, v, &bits)) 1.4271 + return false; 1.4272 + principals = cx->new_<ShellPrincipals>(bits); 1.4273 + if (!principals) 1.4274 + return false; 1.4275 + JS_HoldPrincipals(principals); 1.4276 + } 1.4277 + } 1.4278 + 1.4279 + RootedObject global(cx, NewGlobalObject(cx, options, principals)); 1.4280 + if (principals) 1.4281 + JS_DropPrincipals(cx->runtime(), principals); 1.4282 + if (!global) 1.4283 + return false; 1.4284 + 1.4285 + if (!JS_WrapObject(cx, &global)) 1.4286 + return false; 1.4287 + 1.4288 + args.rval().setObject(*global); 1.4289 + return true; 1.4290 +} 1.4291 + 1.4292 +static bool 1.4293 +EnableStackWalkingAssertion(JSContext *cx, unsigned argc, jsval *vp) 1.4294 +{ 1.4295 + CallArgs args = CallArgsFromVp(argc, vp); 1.4296 + if (!args.get(0).isBoolean()) { 1.4297 + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS, 1.4298 + "enableStackWalkingAssertion"); 1.4299 + return false; 1.4300 + } 1.4301 + 1.4302 +#ifdef DEBUG 1.4303 + cx->stackIterAssertionEnabled = args[0].toBoolean(); 1.4304 +#endif 1.4305 + 1.4306 + args.rval().setUndefined(); 1.4307 + return true; 1.4308 +} 1.4309 + 1.4310 +static bool 1.4311 +GetMaxArgs(JSContext *cx, unsigned argc, jsval *vp) 1.4312 +{ 1.4313 + CallArgs args = CallArgsFromVp(argc, vp); 1.4314 + args.rval().setInt32(ARGS_LENGTH_MAX); 1.4315 + return true; 1.4316 +} 1.4317 + 1.4318 +static bool 1.4319 +ObjectEmulatingUndefined(JSContext *cx, unsigned argc, jsval *vp) 1.4320 +{ 1.4321 + CallArgs args = CallArgsFromVp(argc, vp); 1.4322 + 1.4323 + static const JSClass cls = { 1.4324 + "ObjectEmulatingUndefined", 1.4325 + JSCLASS_EMULATES_UNDEFINED, 1.4326 + JS_PropertyStub, 1.4327 + JS_DeletePropertyStub, 1.4328 + JS_PropertyStub, 1.4329 + JS_StrictPropertyStub, 1.4330 + JS_EnumerateStub, 1.4331 + JS_ResolveStub, 1.4332 + JS_ConvertStub 1.4333 + }; 1.4334 + 1.4335 + RootedObject obj(cx, JS_NewObject(cx, &cls, JS::NullPtr(), JS::NullPtr())); 1.4336 + if (!obj) 1.4337 + return false; 1.4338 + args.rval().setObject(*obj); 1.4339 + return true; 1.4340 +} 1.4341 + 1.4342 +static bool 1.4343 +GetSelfHostedValue(JSContext *cx, unsigned argc, jsval *vp) 1.4344 +{ 1.4345 + CallArgs args = CallArgsFromVp(argc, vp); 1.4346 + 1.4347 + if (args.length() != 1 || !args[0].isString()) { 1.4348 + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS, 1.4349 + "getSelfHostedValue"); 1.4350 + return false; 1.4351 + } 1.4352 + RootedAtom srcAtom(cx, ToAtom<CanGC>(cx, args[0])); 1.4353 + if (!srcAtom) 1.4354 + return false; 1.4355 + RootedPropertyName srcName(cx, srcAtom->asPropertyName()); 1.4356 + return cx->runtime()->cloneSelfHostedValue(cx, srcName, args.rval()); 1.4357 +} 1.4358 + 1.4359 +class ShellSourceHook: public SourceHook { 1.4360 + // The function we should call to lazily retrieve source code. 1.4361 + PersistentRootedFunction fun; 1.4362 + 1.4363 + public: 1.4364 + ShellSourceHook(JSContext *cx, JSFunction &fun) : fun(cx, &fun) {} 1.4365 + 1.4366 + bool load(JSContext *cx, const char *filename, jschar **src, size_t *length) { 1.4367 + RootedString str(cx, JS_NewStringCopyZ(cx, filename)); 1.4368 + if (!str) 1.4369 + return false; 1.4370 + RootedValue filenameValue(cx, StringValue(str)); 1.4371 + 1.4372 + RootedValue result(cx); 1.4373 + if (!Call(cx, UndefinedHandleValue, fun, filenameValue, &result)) 1.4374 + return false; 1.4375 + 1.4376 + str = JS::ToString(cx, result); 1.4377 + if (!str) 1.4378 + return false; 1.4379 + 1.4380 + *length = JS_GetStringLength(str); 1.4381 + *src = cx->pod_malloc<jschar>(*length); 1.4382 + if (!*src) 1.4383 + return false; 1.4384 + 1.4385 + const jschar *chars = JS_GetStringCharsZ(cx, str); 1.4386 + if (!chars) 1.4387 + return false; 1.4388 + 1.4389 + PodCopy(*src, chars, *length); 1.4390 + return true; 1.4391 + } 1.4392 +}; 1.4393 + 1.4394 +static bool 1.4395 +WithSourceHook(JSContext *cx, unsigned argc, jsval *vp) 1.4396 +{ 1.4397 + CallArgs args = CallArgsFromVp(argc, vp); 1.4398 + RootedObject callee(cx, &args.callee()); 1.4399 + 1.4400 + if (args.length() != 2) { 1.4401 + ReportUsageError(cx, callee, "Wrong number of arguments."); 1.4402 + return false; 1.4403 + } 1.4404 + 1.4405 + if (!args[0].isObject() || !args[0].toObject().is<JSFunction>() 1.4406 + || !args[1].isObject() || !args[1].toObject().is<JSFunction>()) { 1.4407 + ReportUsageError(cx, callee, "First and second arguments must be functions."); 1.4408 + return false; 1.4409 + } 1.4410 + 1.4411 + ShellSourceHook *hook = new ShellSourceHook(cx, args[0].toObject().as<JSFunction>()); 1.4412 + if (!hook) 1.4413 + return false; 1.4414 + 1.4415 + SourceHook *savedHook = js::ForgetSourceHook(cx->runtime()); 1.4416 + js::SetSourceHook(cx->runtime(), hook); 1.4417 + RootedObject fun(cx, &args[1].toObject()); 1.4418 + bool result = Call(cx, UndefinedHandleValue, fun, JS::HandleValueArray::empty(), args.rval()); 1.4419 + js::SetSourceHook(cx->runtime(), savedHook); 1.4420 + return result; 1.4421 +} 1.4422 + 1.4423 +static bool 1.4424 +IsCachingEnabled(JSContext *cx, unsigned argc, Value *vp) 1.4425 +{ 1.4426 + CallArgs args = CallArgsFromVp(argc, vp); 1.4427 + args.rval().setBoolean(jsCachingEnabled && jsCacheAsmJSPath != nullptr); 1.4428 + return true; 1.4429 +} 1.4430 + 1.4431 +static bool 1.4432 +SetCachingEnabled(JSContext *cx, unsigned argc, Value *vp) 1.4433 +{ 1.4434 + CallArgs args = CallArgsFromVp(argc, vp); 1.4435 + jsCachingEnabled = ToBoolean(args.get(0)); 1.4436 + args.rval().setUndefined(); 1.4437 + return true; 1.4438 +} 1.4439 + 1.4440 +static void 1.4441 +PrintProfilerEvents_Callback(const char *msg) 1.4442 +{ 1.4443 + fprintf(stderr, "PROFILER EVENT: %s\n", msg); 1.4444 +} 1.4445 + 1.4446 +static bool 1.4447 +PrintProfilerEvents(JSContext *cx, unsigned argc, Value *vp) 1.4448 +{ 1.4449 + CallArgs args = CallArgsFromVp(argc, vp); 1.4450 + if (cx->runtime()->spsProfiler.enabled()) 1.4451 + js::RegisterRuntimeProfilingEventMarker(cx->runtime(), &PrintProfilerEvents_Callback); 1.4452 + args.rval().setUndefined(); 1.4453 + return true; 1.4454 +} 1.4455 + 1.4456 +static const JSFunctionSpecWithHelp shell_functions[] = { 1.4457 + JS_FN_HELP("version", Version, 0, 0, 1.4458 +"version([number])", 1.4459 +" Get or force a script compilation version number."), 1.4460 + 1.4461 + JS_FN_HELP("options", Options, 0, 0, 1.4462 +"options([option ...])", 1.4463 +" Get or toggle JavaScript options."), 1.4464 + 1.4465 + JS_FN_HELP("load", Load, 1, 0, 1.4466 +"load(['foo.js' ...])", 1.4467 +" Load files named by string arguments. Filename is relative to the\n" 1.4468 +" current working directory."), 1.4469 + 1.4470 + JS_FN_HELP("loadRelativeToScript", LoadScriptRelativeToScript, 1, 0, 1.4471 +"loadRelativeToScript(['foo.js' ...])", 1.4472 +" Load files named by string arguments. Filename is relative to the\n" 1.4473 +" calling script."), 1.4474 + 1.4475 + JS_FN_HELP("evaluate", Evaluate, 2, 0, 1.4476 +"evaluate(code[, options])", 1.4477 +" Evaluate code as though it were the contents of a file.\n" 1.4478 +" options is an optional object that may have these properties:\n" 1.4479 +" compileAndGo: use the compile-and-go compiler option (default: true)\n" 1.4480 +" noScriptRval: use the no-script-rval compiler option (default: false)\n" 1.4481 +" fileName: filename for error messages and debug info\n" 1.4482 +" lineNumber: starting line number for error messages and debug info\n" 1.4483 +" global: global in which to execute the code\n" 1.4484 +" newContext: if true, create and use a new cx (default: false)\n" 1.4485 +" saveFrameChain: if true, save the frame chain before evaluating code\n" 1.4486 +" and restore it afterwards\n" 1.4487 +" catchTermination: if true, catch termination (failure without\n" 1.4488 +" an exception value, as for slow scripts or out-of-memory)\n" 1.4489 +" and return 'terminated'\n" 1.4490 +" element: if present with value |v|, convert |v| to an object |o| and\n" 1.4491 +" mark the source as being attached to the DOM element |o|. If the\n" 1.4492 +" property is omitted or |v| is null, don't attribute the source to\n" 1.4493 +" any DOM element.\n" 1.4494 +" elementAttributeName: if present and not undefined, the name of\n" 1.4495 +" property of 'element' that holds this code. This is what\n" 1.4496 +" Debugger.Source.prototype.elementAttributeName returns.\n" 1.4497 +" sourceMapURL: if present with value |v|, convert |v| to a string, and\n" 1.4498 +" provide that as the code's source map URL. If omitted, attach no\n" 1.4499 +" source map URL to the code (although the code may provide one itself,\n" 1.4500 +" via a //#sourceMappingURL comment).\n" 1.4501 +" sourceIsLazy: if present and true, indicates that, after compilation, \n" 1.4502 + "script source should not be cached by the JS engine and should be \n" 1.4503 + "lazily loaded from the embedding as-needed.\n" 1.4504 +" loadBytecode: if true, and if the source is a CacheEntryObject,\n" 1.4505 +" the bytecode would be loaded and decoded from the cache entry instead\n" 1.4506 +" of being parsed, then it would be executed as usual.\n" 1.4507 +" saveBytecode: if true, and if the source is a CacheEntryObject,\n" 1.4508 +" the bytecode would be encoded and saved into the cache entry after\n" 1.4509 +" the script execution.\n" 1.4510 +" assertEqBytecode: if true, and if both loadBytecode and saveBytecode are \n" 1.4511 +" true, then the loaded bytecode and the encoded bytecode are compared.\n" 1.4512 +" and an assertion is raised if they differ.\n" 1.4513 +), 1.4514 + 1.4515 + JS_FN_HELP("run", Run, 1, 0, 1.4516 +"run('foo.js')", 1.4517 +" Run the file named by the first argument, returning the number of\n" 1.4518 +" of milliseconds spent compiling and executing it."), 1.4519 + 1.4520 + JS_FN_HELP("readline", ReadLine, 0, 0, 1.4521 +"readline()", 1.4522 +" Read a single line from stdin."), 1.4523 + 1.4524 + JS_FN_HELP("print", Print, 0, 0, 1.4525 +"print([exp ...])", 1.4526 +" Evaluate and print expressions to stdout."), 1.4527 + 1.4528 + JS_FN_HELP("printErr", PrintErr, 0, 0, 1.4529 +"printErr([exp ...])", 1.4530 +" Evaluate and print expressions to stderr."), 1.4531 + 1.4532 + JS_FN_HELP("putstr", PutStr, 0, 0, 1.4533 +"putstr([exp])", 1.4534 +" Evaluate and print expression without newline."), 1.4535 + 1.4536 + JS_FN_HELP("dateNow", Now, 0, 0, 1.4537 +"dateNow()", 1.4538 +" Return the current time with sub-ms precision."), 1.4539 + 1.4540 + JS_FN_HELP("help", Help, 0, 0, 1.4541 +"help([name ...])", 1.4542 +" Display usage and help messages."), 1.4543 + 1.4544 + JS_FN_HELP("quit", Quit, 0, 0, 1.4545 +"quit()", 1.4546 +" Quit the shell."), 1.4547 + 1.4548 + JS_FN_HELP("assertEq", AssertEq, 2, 0, 1.4549 +"assertEq(actual, expected[, msg])", 1.4550 +" Throw if the first two arguments are not the same (both +0 or both -0,\n" 1.4551 +" both NaN, or non-zero and ===)."), 1.4552 + 1.4553 + JS_FN_HELP("setDebug", SetDebug, 1, 0, 1.4554 +"setDebug(debug)", 1.4555 +" Set debug mode."), 1.4556 + 1.4557 + JS_FN_HELP("setDebuggerHandler", SetDebuggerHandler, 1, 0, 1.4558 +"setDebuggerHandler(f)", 1.4559 +" Set handler for debugger keyword to f."), 1.4560 + 1.4561 + JS_FN_HELP("throwError", ThrowError, 0, 0, 1.4562 +"throwError()", 1.4563 +" Throw an error from JS_ReportError."), 1.4564 + 1.4565 +#ifdef DEBUG 1.4566 + JS_FN_HELP("disassemble", DisassembleToString, 1, 0, 1.4567 +"disassemble([fun])", 1.4568 +" Return the disassembly for the given function."), 1.4569 + 1.4570 + JS_FN_HELP("dis", Disassemble, 1, 0, 1.4571 +"dis([fun])", 1.4572 +" Disassemble functions into bytecodes."), 1.4573 + 1.4574 + JS_FN_HELP("disfile", DisassFile, 1, 0, 1.4575 +"disfile('foo.js')", 1.4576 +" Disassemble script file into bytecodes.\n" 1.4577 +" dis and disfile take these options as preceeding string arguments:\n" 1.4578 +" \"-r\" (disassemble recursively)\n" 1.4579 +" \"-l\" (show line numbers)"), 1.4580 + 1.4581 + JS_FN_HELP("dissrc", DisassWithSrc, 1, 0, 1.4582 +"dissrc([fun])", 1.4583 +" Disassemble functions with source lines."), 1.4584 + 1.4585 + JS_FN_HELP("dumpObject", DumpObject, 1, 0, 1.4586 +"dumpObject()", 1.4587 +" Dump an internal representation of an object."), 1.4588 + 1.4589 + JS_FN_HELP("notes", Notes, 1, 0, 1.4590 +"notes([fun])", 1.4591 +" Show source notes for functions."), 1.4592 + 1.4593 + JS_FN_HELP("findReferences", FindReferences, 1, 0, 1.4594 +"findReferences(target)", 1.4595 +" Walk the entire heap, looking for references to |target|, and return a\n" 1.4596 +" \"references object\" describing what we found.\n" 1.4597 +"\n" 1.4598 +" Each property of the references object describes one kind of reference. The\n" 1.4599 +" property's name is the label supplied to MarkObject, JS_CALL_TRACER, or what\n" 1.4600 +" have you, prefixed with \"edge: \" to avoid collisions with system properties\n" 1.4601 +" (like \"toString\" and \"__proto__\"). The property's value is an array of things\n" 1.4602 +" that refer to |thing| via that kind of reference. Ordinary references from\n" 1.4603 +" one object to another are named after the property name (with the \"edge: \"\n" 1.4604 +" prefix).\n" 1.4605 +"\n" 1.4606 +" Garbage collection roots appear as references from 'null'. We use the name\n" 1.4607 +" given to the root (with the \"edge: \" prefix) as the name of the reference.\n" 1.4608 +"\n" 1.4609 +" Note that the references object does record references from objects that are\n" 1.4610 +" only reachable via |thing| itself, not just the references reachable\n" 1.4611 +" themselves from roots that keep |thing| from being collected. (We could make\n" 1.4612 +" this distinction if it is useful.)\n" 1.4613 +"\n" 1.4614 +" If any references are found by the conservative scanner, the references\n" 1.4615 +" object will have a property named \"edge: machine stack\"; the referrers will\n" 1.4616 +" be 'null', because they are roots."), 1.4617 + 1.4618 +#endif 1.4619 + JS_FN_HELP("build", BuildDate, 0, 0, 1.4620 +"build()", 1.4621 +" Show build date and time."), 1.4622 + 1.4623 + JS_FN_HELP("intern", Intern, 1, 0, 1.4624 +"intern(str)", 1.4625 +" Internalize str in the atom table."), 1.4626 + 1.4627 + JS_FN_HELP("getpda", GetPDA, 1, 0, 1.4628 +"getpda(obj)", 1.4629 +" Get the property descriptors for obj."), 1.4630 + 1.4631 + JS_FN_HELP("getslx", GetSLX, 1, 0, 1.4632 +"getslx(obj)", 1.4633 +" Get script line extent."), 1.4634 + 1.4635 + JS_FN_HELP("evalcx", EvalInContext, 1, 0, 1.4636 +"evalcx(s[, o])", 1.4637 +" Evaluate s in optional sandbox object o.\n" 1.4638 +" if (s == '' && !o) return new o with eager standard classes\n" 1.4639 +" if (s == 'lazy' && !o) return new o with lazy standard classes"), 1.4640 + 1.4641 + JS_FN_HELP("evalInFrame", EvalInFrame, 2, 0, 1.4642 +"evalInFrame(n,str,save)", 1.4643 +" Evaluate 'str' in the nth up frame.\n" 1.4644 +" If 'save' (default false), save the frame chain."), 1.4645 + 1.4646 +#ifdef JS_THREADSAFE 1.4647 + JS_FN_HELP("evalInWorker", EvalInWorker, 1, 0, 1.4648 +"evalInWorker(str)", 1.4649 +" Evaluate 'str' in a separate thread with its own runtime.\n"), 1.4650 +#endif 1.4651 + 1.4652 + JS_FN_HELP("shapeOf", ShapeOf, 1, 0, 1.4653 +"shapeOf(obj)", 1.4654 +" Get the shape of obj (an implementation detail)."), 1.4655 + 1.4656 + JS_FN_HELP("resolver", Resolver, 1, 0, 1.4657 +"resolver(src[, proto])", 1.4658 +" Create object with resolve hook that copies properties\n" 1.4659 +" from src. If proto is omitted, use Object.prototype."), 1.4660 + 1.4661 +#ifdef DEBUG 1.4662 + JS_FN_HELP("arrayInfo", js_ArrayInfo, 1, 0, 1.4663 +"arrayInfo(a1, a2, ...)", 1.4664 +" Report statistics about arrays."), 1.4665 +#endif 1.4666 + 1.4667 +#ifdef JS_THREADSAFE 1.4668 + JS_FN_HELP("sleep", Sleep_fn, 1, 0, 1.4669 +"sleep(dt)", 1.4670 +" Sleep for dt seconds."), 1.4671 +#endif 1.4672 + 1.4673 + JS_FN_HELP("snarf", Snarf, 1, 0, 1.4674 +"snarf(filename, [\"binary\"])", 1.4675 +" Read filename into returned string. Filename is relative to the current\n" 1.4676 + " working directory."), 1.4677 + 1.4678 + JS_FN_HELP("read", Snarf, 1, 0, 1.4679 +"read(filename, [\"binary\"])", 1.4680 +" Synonym for snarf."), 1.4681 + 1.4682 + JS_FN_HELP("readRelativeToScript", ReadRelativeToScript, 1, 0, 1.4683 +"readRelativeToScript(filename, [\"binary\"])", 1.4684 +" Read filename into returned string. Filename is relative to the directory\n" 1.4685 +" containing the current script."), 1.4686 + 1.4687 + JS_FN_HELP("compile", Compile, 1, 0, 1.4688 +"compile(code)", 1.4689 +" Compiles a string to bytecode, potentially throwing."), 1.4690 + 1.4691 + JS_FN_HELP("parse", Parse, 1, 0, 1.4692 +"parse(code)", 1.4693 +" Parses a string, potentially throwing."), 1.4694 + 1.4695 + JS_FN_HELP("syntaxParse", SyntaxParse, 1, 0, 1.4696 +"syntaxParse(code)", 1.4697 +" Check the syntax of a string, returning success value"), 1.4698 + 1.4699 +#ifdef JS_THREADSAFE 1.4700 + JS_FN_HELP("offThreadCompileScript", OffThreadCompileScript, 1, 0, 1.4701 +"offThreadCompileScript(code[, options])", 1.4702 +" Compile |code| on a helper thread. To wait for the compilation to finish\n" 1.4703 +" and run the code, call |runOffThreadScript|. If present, |options| may\n" 1.4704 +" have properties saying how the code should be compiled:\n" 1.4705 +" noScriptRval: use the no-script-rval compiler option (default: false)\n" 1.4706 +" fileName: filename for error messages and debug info\n" 1.4707 +" lineNumber: starting line number for error messages and debug info\n" 1.4708 +" element: if present with value |v|, convert |v| to an object |o| and\n" 1.4709 +" mark the source as being attached to the DOM element |o|. If the\n" 1.4710 +" property is omitted or |v| is null, don't attribute the source to\n" 1.4711 +" any DOM element.\n" 1.4712 +" elementAttributeName: if present and not undefined, the name of\n" 1.4713 +" property of 'element' that holds this code. This is what\n" 1.4714 +" Debugger.Source.prototype.elementAttributeName returns.\n"), 1.4715 + 1.4716 + JS_FN_HELP("runOffThreadScript", runOffThreadScript, 0, 0, 1.4717 +"runOffThreadScript()", 1.4718 +" Wait for off-thread compilation to complete. If an error occurred,\n" 1.4719 +" throw the appropriate exception; otherwise, run the script and return\n" 1.4720 +" its value."), 1.4721 + 1.4722 +#endif 1.4723 + 1.4724 + JS_FN_HELP("timeout", Timeout, 1, 0, 1.4725 +"timeout([seconds], [func])", 1.4726 +" Get/Set the limit in seconds for the execution time for the current context.\n" 1.4727 +" A negative value (default) means that the execution time is unlimited.\n" 1.4728 +" If a second argument is provided, it will be invoked when the timer elapses.\n" 1.4729 +" Calling this function will replace any callback set by |setInterruptCallback|.\n"), 1.4730 + 1.4731 + JS_FN_HELP("interruptIf", InterruptIf, 1, 0, 1.4732 +"interruptIf(cond)", 1.4733 +" Requests interrupt callback if cond is true. If a callback function is set via\n" 1.4734 +" |timeout| or |setInterruptCallback|, it will be called. No-op otherwise."), 1.4735 + 1.4736 + JS_FN_HELP("setInterruptCallback", SetInterruptCallback, 1, 0, 1.4737 +"setInterruptCallback(func)", 1.4738 +" Sets func as the interrupt callback function.\n" 1.4739 +" Calling this function will replace any callback set by |timeout|.\n"), 1.4740 + 1.4741 + JS_FN_HELP("elapsed", Elapsed, 0, 0, 1.4742 +"elapsed()", 1.4743 +" Execution time elapsed for the current context."), 1.4744 + 1.4745 + JS_FN_HELP("decompileFunction", DecompileFunction, 1, 0, 1.4746 +"decompileFunction(func)", 1.4747 +" Decompile a function."), 1.4748 + 1.4749 + JS_FN_HELP("decompileBody", DecompileBody, 1, 0, 1.4750 +"decompileBody(func)", 1.4751 +" Decompile a function's body."), 1.4752 + 1.4753 + JS_FN_HELP("decompileThis", DecompileThisScript, 0, 0, 1.4754 +"decompileThis()", 1.4755 +" Decompile the currently executing script."), 1.4756 + 1.4757 + JS_FN_HELP("thisFilename", ThisFilename, 0, 0, 1.4758 +"thisFilename()", 1.4759 +" Return the filename of the current script"), 1.4760 + 1.4761 + JS_FN_HELP("wrap", Wrap, 1, 0, 1.4762 +"wrap(obj)", 1.4763 +" Wrap an object into a noop wrapper."), 1.4764 + 1.4765 + JS_FN_HELP("wrapWithProto", WrapWithProto, 2, 0, 1.4766 +"wrapWithProto(obj)", 1.4767 +" Wrap an object into a noop wrapper with prototype semantics."), 1.4768 + 1.4769 + JS_FN_HELP("newGlobal", NewGlobal, 1, 0, 1.4770 +"newGlobal([options])", 1.4771 +" Return a new global object in a new compartment. If options\n" 1.4772 +" is given, it may have any of the following properties:\n" 1.4773 +" sameZoneAs: the compartment will be in the same zone as the given object (defaults to a new zone)\n" 1.4774 +" invisibleToDebugger: the global will be invisible to the debugger (default false)\n" 1.4775 +" principal: if present, its value converted to a number must be an\n" 1.4776 +" integer that fits in 32 bits; use that as the new compartment's\n" 1.4777 +" principal. Shell principals are toys, meant only for testing; one\n" 1.4778 +" shell principal subsumes another if its set bits are a superset of\n" 1.4779 +" the other's. Thus, a principal of 0 subsumes nothing, while a\n" 1.4780 +" principals of ~0 subsumes all other principals. The absence of a\n" 1.4781 +" principal is treated as if its bits were 0xffff, for subsumption\n" 1.4782 +" purposes. If this property is omitted, supply no principal."), 1.4783 + 1.4784 + JS_FN_HELP("createMappedArrayBuffer", CreateMappedArrayBuffer, 1, 0, 1.4785 +"createMappedArrayBuffer(filename, [offset, [size]])", 1.4786 +" Create an array buffer that mmaps the given file."), 1.4787 + 1.4788 + JS_FN_HELP("enableStackWalkingAssertion", EnableStackWalkingAssertion, 1, 0, 1.4789 +"enableStackWalkingAssertion(enabled)", 1.4790 +" Enables or disables a particularly expensive assertion in stack-walking\n" 1.4791 +" code. If your test isn't ridiculously thorough, such that performing this\n" 1.4792 +" assertion increases test duration by an order of magnitude, you shouldn't\n" 1.4793 +" use this."), 1.4794 + 1.4795 + JS_FN_HELP("getMaxArgs", GetMaxArgs, 0, 0, 1.4796 +"getMaxArgs()", 1.4797 +" Return the maximum number of supported args for a call."), 1.4798 + 1.4799 + JS_FN_HELP("objectEmulatingUndefined", ObjectEmulatingUndefined, 0, 0, 1.4800 +"objectEmulatingUndefined()", 1.4801 +" Return a new object obj for which typeof obj === \"undefined\", obj == null\n" 1.4802 +" and obj == undefined (and vice versa for !=), and ToBoolean(obj) === false.\n"), 1.4803 + 1.4804 + JS_FN_HELP("isCachingEnabled", IsCachingEnabled, 0, 0, 1.4805 +"isCachingEnabled()", 1.4806 +" Return whether JS caching is enabled."), 1.4807 + 1.4808 + JS_FN_HELP("setCachingEnabled", SetCachingEnabled, 1, 0, 1.4809 +"setCachingEnabled(b)", 1.4810 +" Enable or disable JS caching."), 1.4811 + 1.4812 + JS_FN_HELP("cacheEntry", CacheEntry, 1, 0, 1.4813 +"cacheEntry(code)", 1.4814 +" Return a new opaque object which emulates a cache entry of a script. This\n" 1.4815 +" object encapsulates the code and its cached content. The cache entry is filled\n" 1.4816 +" and read by the \"evaluate\" function by using it in place of the source, and\n" 1.4817 +" by setting \"saveBytecode\" and \"loadBytecode\" options."), 1.4818 + 1.4819 + JS_FN_HELP("printProfilerEvents", PrintProfilerEvents, 0, 0, 1.4820 +"printProfilerEvents()", 1.4821 +" Register a callback with the profiler that prints javascript profiler events\n" 1.4822 +" to stderr. Callback is only registered if profiling is enabled."), 1.4823 + 1.4824 + JS_FS_HELP_END 1.4825 +}; 1.4826 + 1.4827 +static const JSFunctionSpecWithHelp fuzzing_unsafe_functions[] = { 1.4828 + JS_FN_HELP("clone", Clone, 1, 0, 1.4829 +"clone(fun[, scope])", 1.4830 +" Clone function object."), 1.4831 + 1.4832 + JS_FN_HELP("getSelfHostedValue", GetSelfHostedValue, 1, 0, 1.4833 +"getSelfHostedValue()", 1.4834 +" Get a self-hosted value by its name. Note that these values don't get \n" 1.4835 +" cached, so repeatedly getting the same value creates multiple distinct clones."), 1.4836 + 1.4837 +#ifdef DEBUG 1.4838 + JS_FN_HELP("dumpHeap", DumpHeap, 0, 0, 1.4839 +"dumpHeap([fileName[, start[, toFind[, maxDepth[, toIgnore]]]]])", 1.4840 +" Interface to JS_DumpHeap with output sent to file."), 1.4841 +#endif 1.4842 + 1.4843 + JS_FN_HELP("parent", Parent, 1, 0, 1.4844 +"parent(obj)", 1.4845 +" Returns the parent of obj."), 1.4846 + 1.4847 + JS_FN_HELP("line2pc", LineToPC, 0, 0, 1.4848 +"line2pc([fun,] line)", 1.4849 +" Map line number to PC."), 1.4850 + 1.4851 + JS_FN_HELP("pc2line", PCToLine, 0, 0, 1.4852 +"pc2line(fun[, pc])", 1.4853 +" Map PC to line number."), 1.4854 + 1.4855 + JS_FN_HELP("redirect", RedirectOutput, 2, 0, 1.4856 +"redirect(stdoutFilename[, stderrFilename])", 1.4857 +" Redirect stdout and/or stderr to the named file. Pass undefined to avoid\n" 1.4858 +" redirecting. Filenames are relative to the current working directory."), 1.4859 + 1.4860 + JS_FN_HELP("setThrowHook", SetThrowHook, 1, 0, 1.4861 +"setThrowHook(f)", 1.4862 +" Set throw hook to f."), 1.4863 + 1.4864 + JS_FN_HELP("system", System, 1, 0, 1.4865 +"system(command)", 1.4866 +" Execute command on the current host, returning result code."), 1.4867 + 1.4868 + JS_FN_HELP("nestedShell", NestedShell, 0, 0, 1.4869 +"nestedShell(shellArgs...)", 1.4870 +" Execute the given code in a new JS shell process, passing this nested shell\n" 1.4871 +" the arguments passed to nestedShell. argv[0] of the nested shell will be argv[0]\n" 1.4872 +" of the current shell (which is assumed to be the actual path to the shell.\n" 1.4873 +" arguments[0] (of the call to nestedShell) will be argv[1], arguments[1] will\n" 1.4874 +" be argv[2], etc."), 1.4875 + 1.4876 + JS_FN_HELP("trap", Trap, 3, 0, 1.4877 +"trap([fun, [pc,]] exp)", 1.4878 +" Trap bytecode execution."), 1.4879 + 1.4880 + JS_FN_HELP("assertFloat32", testingFunc_assertFloat32, 2, 0, 1.4881 +"assertFloat32(value, isFloat32)", 1.4882 +" In IonMonkey only, asserts that value has (resp. hasn't) the MIRType_Float32 if isFloat32 is true (resp. false)."), 1.4883 + 1.4884 + JS_FN_HELP("untrap", Untrap, 2, 0, 1.4885 +"untrap(fun[, pc])", 1.4886 +" Remove a trap."), 1.4887 + 1.4888 + JS_FN_HELP("withSourceHook", WithSourceHook, 1, 0, 1.4889 +"withSourceHook(hook, fun)", 1.4890 +" Set this JS runtime's lazy source retrieval hook (that is, the hook\n" 1.4891 +" used to find sources compiled with |CompileOptions::LAZY_SOURCE|) to\n" 1.4892 +" |hook|; call |fun| with no arguments; and then restore the runtime's\n" 1.4893 +" original hook. Return or throw whatever |fun| did. |hook| gets\n" 1.4894 +" passed the requested code's URL, and should return a string.\n" 1.4895 +"\n" 1.4896 +" Notes:\n" 1.4897 +"\n" 1.4898 +" 1) SpiderMonkey may assert if the returned code isn't close enough\n" 1.4899 +" to the script's real code, so this function is not fuzzer-safe.\n" 1.4900 +"\n" 1.4901 +" 2) The runtime can have only one source retrieval hook active at a\n" 1.4902 +" time. If |fun| is not careful, |hook| could be asked to retrieve the\n" 1.4903 +" source code for compilations that occurred long before it was set,\n" 1.4904 +" and that it knows nothing about. The reverse applies as well: the\n" 1.4905 +" original hook, that we reinstate after the call to |fun| completes,\n" 1.4906 +" might be asked for the source code of compilations that |fun|\n" 1.4907 +" performed, and which, presumably, only |hook| knows how to find.\n"), 1.4908 + 1.4909 + JS_FS_HELP_END 1.4910 +}; 1.4911 + 1.4912 +#ifdef MOZ_PROFILING 1.4913 +# define PROFILING_FUNCTION_COUNT 5 1.4914 +# ifdef MOZ_CALLGRIND 1.4915 +# define CALLGRIND_FUNCTION_COUNT 3 1.4916 +# else 1.4917 +# define CALLGRIND_FUNCTION_COUNT 0 1.4918 +# endif 1.4919 +# ifdef MOZ_VTUNE 1.4920 +# define VTUNE_FUNCTION_COUNT 4 1.4921 +# else 1.4922 +# define VTUNE_FUNCTION_COUNT 0 1.4923 +# endif 1.4924 +# define EXTERNAL_FUNCTION_COUNT (PROFILING_FUNCTION_COUNT + CALLGRIND_FUNCTION_COUNT + VTUNE_FUNCTION_COUNT) 1.4925 +#else 1.4926 +# define EXTERNAL_FUNCTION_COUNT 0 1.4927 +#endif 1.4928 + 1.4929 +#undef PROFILING_FUNCTION_COUNT 1.4930 +#undef CALLGRIND_FUNCTION_COUNT 1.4931 +#undef VTUNE_FUNCTION_COUNT 1.4932 +#undef EXTERNAL_FUNCTION_COUNT 1.4933 + 1.4934 +static bool 1.4935 +PrintHelpString(JSContext *cx, jsval v) 1.4936 +{ 1.4937 + JSString *str = JSVAL_TO_STRING(v); 1.4938 + JS::Anchor<JSString *> a_str(str); 1.4939 + const jschar *chars = JS_GetStringCharsZ(cx, str); 1.4940 + if (!chars) 1.4941 + return false; 1.4942 + 1.4943 + for (const jschar *p = chars; *p; p++) 1.4944 + fprintf(gOutFile, "%c", char(*p)); 1.4945 + 1.4946 + fprintf(gOutFile, "\n"); 1.4947 + 1.4948 + return true; 1.4949 +} 1.4950 + 1.4951 +static bool 1.4952 +PrintHelp(JSContext *cx, HandleObject obj) 1.4953 +{ 1.4954 + RootedValue usage(cx); 1.4955 + if (!JS_LookupProperty(cx, obj, "usage", &usage)) 1.4956 + return false; 1.4957 + RootedValue help(cx); 1.4958 + if (!JS_LookupProperty(cx, obj, "help", &help)) 1.4959 + return false; 1.4960 + 1.4961 + if (JSVAL_IS_VOID(usage) || JSVAL_IS_VOID(help)) 1.4962 + return true; 1.4963 + 1.4964 + return PrintHelpString(cx, usage) && PrintHelpString(cx, help); 1.4965 +} 1.4966 + 1.4967 +static bool 1.4968 +Help(JSContext *cx, unsigned argc, jsval *vp) 1.4969 +{ 1.4970 + CallArgs args = CallArgsFromVp(argc, vp); 1.4971 + fprintf(gOutFile, "%s\n", JS_GetImplementationVersion()); 1.4972 + 1.4973 + RootedObject obj(cx); 1.4974 + if (args.length() == 0) { 1.4975 + RootedObject global(cx, JS::CurrentGlobalOrNull(cx)); 1.4976 + AutoIdArray ida(cx, JS_Enumerate(cx, global)); 1.4977 + if (!ida) 1.4978 + return false; 1.4979 + 1.4980 + for (size_t i = 0; i < ida.length(); i++) { 1.4981 + RootedValue v(cx); 1.4982 + RootedId id(cx, ida[i]); 1.4983 + if (!JS_LookupPropertyById(cx, global, id, &v)) 1.4984 + return false; 1.4985 + if (JSVAL_IS_PRIMITIVE(v)) { 1.4986 + JS_ReportError(cx, "primitive arg"); 1.4987 + return false; 1.4988 + } 1.4989 + obj = JSVAL_TO_OBJECT(v); 1.4990 + if (!PrintHelp(cx, obj)) 1.4991 + return false; 1.4992 + } 1.4993 + } else { 1.4994 + for (unsigned i = 0; i < args.length(); i++) { 1.4995 + if (args[i].isPrimitive()) { 1.4996 + JS_ReportError(cx, "primitive arg"); 1.4997 + return false; 1.4998 + } 1.4999 + obj = args[i].toObjectOrNull(); 1.5000 + if (!PrintHelp(cx, obj)) 1.5001 + return false; 1.5002 + } 1.5003 + } 1.5004 + 1.5005 + args.rval().setUndefined(); 1.5006 + return true; 1.5007 +} 1.5008 + 1.5009 +static const JSErrorFormatString jsShell_ErrorFormatString[JSShellErr_Limit] = { 1.5010 +#define MSG_DEF(name, number, count, exception, format) \ 1.5011 + { format, count, JSEXN_ERR } , 1.5012 +#include "jsshell.msg" 1.5013 +#undef MSG_DEF 1.5014 +}; 1.5015 + 1.5016 +static const JSErrorFormatString * 1.5017 +my_GetErrorMessage(void *userRef, const char *locale, const unsigned errorNumber) 1.5018 +{ 1.5019 + if (errorNumber == 0 || errorNumber >= JSShellErr_Limit) 1.5020 + return nullptr; 1.5021 + 1.5022 + return &jsShell_ErrorFormatString[errorNumber]; 1.5023 +} 1.5024 + 1.5025 +static void 1.5026 +my_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report) 1.5027 +{ 1.5028 + gGotError = PrintError(cx, gErrFile, message, report, reportWarnings); 1.5029 + if (!JSREPORT_IS_WARNING(report->flags)) { 1.5030 + if (report->errorNumber == JSMSG_OUT_OF_MEMORY) { 1.5031 + gExitCode = EXITCODE_OUT_OF_MEMORY; 1.5032 + } else { 1.5033 + gExitCode = EXITCODE_RUNTIME_ERROR; 1.5034 + } 1.5035 + } 1.5036 +} 1.5037 + 1.5038 +static void 1.5039 +my_OOMCallback(JSContext *cx) 1.5040 +{ 1.5041 + // If a script is running, the engine is about to throw the string "out of 1.5042 + // memory", which may or may not be caught. Otherwise the engine will just 1.5043 + // unwind and return null/false, with no exception set. 1.5044 + if (!JS_IsRunning(cx)) 1.5045 + gGotError = true; 1.5046 +} 1.5047 + 1.5048 +static bool 1.5049 +global_enumerate(JSContext *cx, HandleObject obj) 1.5050 +{ 1.5051 +#ifdef LAZY_STANDARD_CLASSES 1.5052 + return JS_EnumerateStandardClasses(cx, obj); 1.5053 +#else 1.5054 + return true; 1.5055 +#endif 1.5056 +} 1.5057 + 1.5058 +static bool 1.5059 +global_resolve(JSContext *cx, HandleObject obj, HandleId id, MutableHandleObject objp) 1.5060 +{ 1.5061 +#ifdef LAZY_STANDARD_CLASSES 1.5062 + bool resolved; 1.5063 + 1.5064 + if (!JS_ResolveStandardClass(cx, obj, id, &resolved)) 1.5065 + return false; 1.5066 + if (resolved) { 1.5067 + objp.set(obj); 1.5068 + return true; 1.5069 + } 1.5070 +#endif 1.5071 + 1.5072 + return true; 1.5073 +} 1.5074 + 1.5075 +static const JSClass global_class = { 1.5076 + "global", JSCLASS_NEW_RESOLVE | JSCLASS_GLOBAL_FLAGS, 1.5077 + JS_PropertyStub, JS_DeletePropertyStub, 1.5078 + JS_PropertyStub, JS_StrictPropertyStub, 1.5079 + global_enumerate, (JSResolveOp) global_resolve, 1.5080 + JS_ConvertStub, nullptr, 1.5081 + nullptr, nullptr, nullptr, 1.5082 + JS_GlobalObjectTraceHook 1.5083 +}; 1.5084 + 1.5085 +static bool 1.5086 +env_setProperty(JSContext *cx, HandleObject obj, HandleId id, bool strict, MutableHandleValue vp) 1.5087 +{ 1.5088 +/* XXX porting may be easy, but these don't seem to supply setenv by default */ 1.5089 +#if !defined SOLARIS 1.5090 + int rv; 1.5091 + 1.5092 + RootedValue idvalue(cx, IdToValue(id)); 1.5093 + RootedString idstring(cx, ToString(cx, idvalue)); 1.5094 + JSAutoByteString idstr; 1.5095 + if (!idstr.encodeLatin1(cx, idstring)) 1.5096 + return false; 1.5097 + 1.5098 + RootedString value(cx, ToString(cx, vp)); 1.5099 + if (!value) 1.5100 + return false; 1.5101 + JSAutoByteString valstr; 1.5102 + if (!valstr.encodeLatin1(cx, value)) 1.5103 + return false; 1.5104 + 1.5105 +#if defined XP_WIN || defined HPUX || defined OSF1 1.5106 + { 1.5107 + char *waste = JS_smprintf("%s=%s", idstr.ptr(), valstr.ptr()); 1.5108 + if (!waste) { 1.5109 + JS_ReportOutOfMemory(cx); 1.5110 + return false; 1.5111 + } 1.5112 + rv = putenv(waste); 1.5113 +#ifdef XP_WIN 1.5114 + /* 1.5115 + * HPUX9 at least still has the bad old non-copying putenv. 1.5116 + * 1.5117 + * Per mail from <s.shanmuganathan@digital.com>, OSF1 also has a putenv 1.5118 + * that will crash if you pass it an auto char array (so it must place 1.5119 + * its argument directly in the char *environ[] array). 1.5120 + */ 1.5121 + JS_smprintf_free(waste); 1.5122 +#endif 1.5123 + } 1.5124 +#else 1.5125 + rv = setenv(idstr.ptr(), valstr.ptr(), 1); 1.5126 +#endif 1.5127 + if (rv < 0) { 1.5128 + JS_ReportError(cx, "can't set env variable %s to %s", idstr.ptr(), valstr.ptr()); 1.5129 + return false; 1.5130 + } 1.5131 + vp.set(StringValue(value)); 1.5132 +#endif /* !defined SOLARIS */ 1.5133 + return true; 1.5134 +} 1.5135 + 1.5136 +static bool 1.5137 +env_enumerate(JSContext *cx, HandleObject obj) 1.5138 +{ 1.5139 + static bool reflected; 1.5140 + char **evp, *name, *value; 1.5141 + RootedString valstr(cx); 1.5142 + bool ok; 1.5143 + 1.5144 + if (reflected) 1.5145 + return true; 1.5146 + 1.5147 + for (evp = (char **)JS_GetPrivate(obj); (name = *evp) != nullptr; evp++) { 1.5148 + value = strchr(name, '='); 1.5149 + if (!value) 1.5150 + continue; 1.5151 + *value++ = '\0'; 1.5152 + valstr = JS_NewStringCopyZ(cx, value); 1.5153 + ok = valstr && JS_DefineProperty(cx, obj, name, valstr, JSPROP_ENUMERATE); 1.5154 + value[-1] = '='; 1.5155 + if (!ok) 1.5156 + return false; 1.5157 + } 1.5158 + 1.5159 + reflected = true; 1.5160 + return true; 1.5161 +} 1.5162 + 1.5163 +static bool 1.5164 +env_resolve(JSContext *cx, HandleObject obj, HandleId id, MutableHandleObject objp) 1.5165 +{ 1.5166 + RootedValue idvalue(cx, IdToValue(id)); 1.5167 + RootedString idstring(cx, ToString(cx, idvalue)); 1.5168 + JSAutoByteString idstr; 1.5169 + if (!idstr.encodeLatin1(cx, idstring)) 1.5170 + return false; 1.5171 + 1.5172 + const char *name = idstr.ptr(); 1.5173 + const char *value = getenv(name); 1.5174 + if (value) { 1.5175 + RootedString valstr(cx, JS_NewStringCopyZ(cx, value)); 1.5176 + if (!valstr) 1.5177 + return false; 1.5178 + if (!JS_DefineProperty(cx, obj, name, valstr, JSPROP_ENUMERATE)) 1.5179 + return false; 1.5180 + objp.set(obj); 1.5181 + } 1.5182 + return true; 1.5183 +} 1.5184 + 1.5185 +static const JSClass env_class = { 1.5186 + "environment", JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE, 1.5187 + JS_PropertyStub, JS_DeletePropertyStub, 1.5188 + JS_PropertyStub, env_setProperty, 1.5189 + env_enumerate, (JSResolveOp) env_resolve, 1.5190 + JS_ConvertStub 1.5191 +}; 1.5192 + 1.5193 +/* 1.5194 + * Define a FakeDOMObject constructor. It returns an object with a getter, 1.5195 + * setter and method with attached JitInfo. This object can be used to test 1.5196 + * IonMonkey DOM optimizations in the shell. 1.5197 + */ 1.5198 +static uint32_t DOM_OBJECT_SLOT = 0; 1.5199 + 1.5200 +static bool 1.5201 +dom_genericGetter(JSContext* cx, unsigned argc, JS::Value *vp); 1.5202 + 1.5203 +static bool 1.5204 +dom_genericSetter(JSContext* cx, unsigned argc, JS::Value *vp); 1.5205 + 1.5206 +static bool 1.5207 +dom_genericMethod(JSContext *cx, unsigned argc, JS::Value *vp); 1.5208 + 1.5209 +#ifdef DEBUG 1.5210 +static const JSClass *GetDomClass(); 1.5211 +#endif 1.5212 + 1.5213 +static bool 1.5214 +dom_get_x(JSContext* cx, HandleObject obj, void *self, JSJitGetterCallArgs args) 1.5215 +{ 1.5216 + JS_ASSERT(JS_GetClass(obj) == GetDomClass()); 1.5217 + JS_ASSERT(self == (void *)0x1234); 1.5218 + args.rval().set(JS_NumberValue(double(3.14))); 1.5219 + return true; 1.5220 +} 1.5221 + 1.5222 +static bool 1.5223 +dom_set_x(JSContext* cx, HandleObject obj, void *self, JSJitSetterCallArgs args) 1.5224 +{ 1.5225 + JS_ASSERT(JS_GetClass(obj) == GetDomClass()); 1.5226 + JS_ASSERT(self == (void *)0x1234); 1.5227 + return true; 1.5228 +} 1.5229 + 1.5230 +static bool 1.5231 +dom_doFoo(JSContext* cx, HandleObject obj, void *self, const JSJitMethodCallArgs& args) 1.5232 +{ 1.5233 + JS_ASSERT(JS_GetClass(obj) == GetDomClass()); 1.5234 + JS_ASSERT(self == (void *)0x1234); 1.5235 + 1.5236 + /* Just return args.length(). */ 1.5237 + args.rval().setInt32(args.length()); 1.5238 + return true; 1.5239 +} 1.5240 + 1.5241 +static const JSJitInfo dom_x_getterinfo = { 1.5242 + { (JSJitGetterOp)dom_get_x }, 1.5243 + 0, /* protoID */ 1.5244 + 0, /* depth */ 1.5245 + JSJitInfo::AliasNone, /* aliasSet */ 1.5246 + JSJitInfo::Getter, 1.5247 + JSVAL_TYPE_UNKNOWN, /* returnType */ 1.5248 + true, /* isInfallible. False in setters. */ 1.5249 + true, /* isMovable */ 1.5250 + false, /* isInSlot */ 1.5251 + false, /* isTypedMethod */ 1.5252 + 0 /* slotIndex */ 1.5253 +}; 1.5254 + 1.5255 +static const JSJitInfo dom_x_setterinfo = { 1.5256 + { (JSJitGetterOp)dom_set_x }, 1.5257 + 0, /* protoID */ 1.5258 + 0, /* depth */ 1.5259 + JSJitInfo::Setter, 1.5260 + JSJitInfo::AliasEverything, /* aliasSet */ 1.5261 + JSVAL_TYPE_UNKNOWN, /* returnType */ 1.5262 + false, /* isInfallible. False in setters. */ 1.5263 + false, /* isMovable. */ 1.5264 + false, /* isInSlot */ 1.5265 + false, /* isTypedMethod */ 1.5266 + 0 /* slotIndex */ 1.5267 +}; 1.5268 + 1.5269 +static const JSJitInfo doFoo_methodinfo = { 1.5270 + { (JSJitGetterOp)dom_doFoo }, 1.5271 + 0, /* protoID */ 1.5272 + 0, /* depth */ 1.5273 + JSJitInfo::Method, 1.5274 + JSJitInfo::AliasEverything, /* aliasSet */ 1.5275 + JSVAL_TYPE_UNKNOWN, /* returnType */ 1.5276 + false, /* isInfallible. False in setters. */ 1.5277 + false, /* isMovable */ 1.5278 + false, /* isInSlot */ 1.5279 + false, /* isTypedMethod */ 1.5280 + 0 /* slotIndex */ 1.5281 +}; 1.5282 + 1.5283 +static const JSPropertySpec dom_props[] = { 1.5284 + {"x", 1.5285 + JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_NATIVE_ACCESSORS, 1.5286 + { { (JSPropertyOp)dom_genericGetter, &dom_x_getterinfo } }, 1.5287 + { { (JSStrictPropertyOp)dom_genericSetter, &dom_x_setterinfo } } 1.5288 + }, 1.5289 + JS_PS_END 1.5290 +}; 1.5291 + 1.5292 +static const JSFunctionSpec dom_methods[] = { 1.5293 + JS_FNINFO("doFoo", dom_genericMethod, &doFoo_methodinfo, 3, JSPROP_ENUMERATE), 1.5294 + JS_FS_END 1.5295 +}; 1.5296 + 1.5297 +static const JSClass dom_class = { 1.5298 + "FakeDOMObject", JSCLASS_IS_DOMJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(2), 1.5299 + JS_PropertyStub, /* addProperty */ 1.5300 + JS_DeletePropertyStub, /* delProperty */ 1.5301 + JS_PropertyStub, /* getProperty */ 1.5302 + JS_StrictPropertyStub, /* setProperty */ 1.5303 + JS_EnumerateStub, 1.5304 + JS_ResolveStub, 1.5305 + JS_ConvertStub, 1.5306 + nullptr, /* finalize */ 1.5307 + nullptr, /* call */ 1.5308 + nullptr, /* hasInstance */ 1.5309 + nullptr, /* construct */ 1.5310 + nullptr, /* trace */ 1.5311 + JSCLASS_NO_INTERNAL_MEMBERS 1.5312 +}; 1.5313 + 1.5314 +#ifdef DEBUG 1.5315 +static const JSClass *GetDomClass() { 1.5316 + return &dom_class; 1.5317 +} 1.5318 +#endif 1.5319 + 1.5320 +static bool 1.5321 +dom_genericGetter(JSContext *cx, unsigned argc, JS::Value *vp) 1.5322 +{ 1.5323 + CallArgs args = CallArgsFromVp(argc, vp); 1.5324 + RootedObject obj(cx, JS_THIS_OBJECT(cx, vp)); 1.5325 + if (!obj) 1.5326 + return false; 1.5327 + 1.5328 + if (JS_GetClass(obj) != &dom_class) { 1.5329 + args.rval().set(UndefinedValue()); 1.5330 + return true; 1.5331 + } 1.5332 + 1.5333 + JS::Value val = js::GetReservedSlot(obj, DOM_OBJECT_SLOT); 1.5334 + 1.5335 + const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev()); 1.5336 + MOZ_ASSERT(info->type() == JSJitInfo::Getter); 1.5337 + JSJitGetterOp getter = info->getter; 1.5338 + return getter(cx, obj, val.toPrivate(), JSJitGetterCallArgs(args)); 1.5339 +} 1.5340 + 1.5341 +static bool 1.5342 +dom_genericSetter(JSContext* cx, unsigned argc, JS::Value* vp) 1.5343 +{ 1.5344 + CallArgs args = CallArgsFromVp(argc, vp); 1.5345 + RootedObject obj(cx, JS_THIS_OBJECT(cx, vp)); 1.5346 + if (!obj) 1.5347 + return false; 1.5348 + 1.5349 + JS_ASSERT(args.length() == 1); 1.5350 + 1.5351 + if (JS_GetClass(obj) != &dom_class) { 1.5352 + args.rval().set(UndefinedValue()); 1.5353 + return true; 1.5354 + } 1.5355 + 1.5356 + JS::Value val = js::GetReservedSlot(obj, DOM_OBJECT_SLOT); 1.5357 + 1.5358 + const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev()); 1.5359 + MOZ_ASSERT(info->type() == JSJitInfo::Setter); 1.5360 + JSJitSetterOp setter = info->setter; 1.5361 + if (!setter(cx, obj, val.toPrivate(), JSJitSetterCallArgs(args))) 1.5362 + return false; 1.5363 + args.rval().set(UndefinedValue()); 1.5364 + return true; 1.5365 +} 1.5366 + 1.5367 +static bool 1.5368 +dom_genericMethod(JSContext* cx, unsigned argc, JS::Value *vp) 1.5369 +{ 1.5370 + CallArgs args = CallArgsFromVp(argc, vp); 1.5371 + RootedObject obj(cx, JS_THIS_OBJECT(cx, vp)); 1.5372 + if (!obj) 1.5373 + return false; 1.5374 + 1.5375 + if (JS_GetClass(obj) != &dom_class) { 1.5376 + args.rval().set(UndefinedValue()); 1.5377 + return true; 1.5378 + } 1.5379 + 1.5380 + JS::Value val = js::GetReservedSlot(obj, DOM_OBJECT_SLOT); 1.5381 + 1.5382 + const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev()); 1.5383 + MOZ_ASSERT(info->type() == JSJitInfo::Method); 1.5384 + JSJitMethodOp method = info->method; 1.5385 + return method(cx, obj, val.toPrivate(), JSJitMethodCallArgs(args)); 1.5386 +} 1.5387 + 1.5388 +static void 1.5389 +InitDOMObject(HandleObject obj) 1.5390 +{ 1.5391 + /* Fow now just initialize to a constant we can check. */ 1.5392 + SetReservedSlot(obj, DOM_OBJECT_SLOT, PRIVATE_TO_JSVAL((void *)0x1234)); 1.5393 +} 1.5394 + 1.5395 +static bool 1.5396 +dom_constructor(JSContext* cx, unsigned argc, JS::Value *vp) 1.5397 +{ 1.5398 + CallArgs args = CallArgsFromVp(argc, vp); 1.5399 + 1.5400 + RootedObject callee(cx, &args.callee()); 1.5401 + RootedValue protov(cx); 1.5402 + if (!JSObject::getProperty(cx, callee, callee, cx->names().prototype, &protov)) 1.5403 + return false; 1.5404 + 1.5405 + if (!protov.isObject()) { 1.5406 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_PROTOTYPE, "FakeDOMObject"); 1.5407 + return false; 1.5408 + } 1.5409 + 1.5410 + RootedObject proto(cx, &protov.toObject()); 1.5411 + RootedObject domObj(cx, JS_NewObject(cx, &dom_class, proto, JS::NullPtr())); 1.5412 + if (!domObj) 1.5413 + return false; 1.5414 + 1.5415 + InitDOMObject(domObj); 1.5416 + 1.5417 + args.rval().setObject(*domObj); 1.5418 + return true; 1.5419 +} 1.5420 + 1.5421 +static bool 1.5422 +InstanceClassHasProtoAtDepth(JSObject *protoObject, uint32_t protoID, uint32_t depth) 1.5423 +{ 1.5424 + /* There's only a single (fake) DOM object in the shell, so just return true. */ 1.5425 + return true; 1.5426 +} 1.5427 + 1.5428 +class ScopedFileDesc 1.5429 +{ 1.5430 + intptr_t fd_; 1.5431 + public: 1.5432 + enum LockType { READ_LOCK, WRITE_LOCK }; 1.5433 + ScopedFileDesc(int fd, LockType lockType) 1.5434 + : fd_(fd) 1.5435 + { 1.5436 + if (fd == -1) 1.5437 + return; 1.5438 + if (!jsCacheOpened.compareExchange(false, true)) { 1.5439 + close(fd_); 1.5440 + fd_ = -1; 1.5441 + return; 1.5442 + } 1.5443 + } 1.5444 + ~ScopedFileDesc() { 1.5445 + if (fd_ == -1) 1.5446 + return; 1.5447 + JS_ASSERT(jsCacheOpened == true); 1.5448 + jsCacheOpened = false; 1.5449 + close(fd_); 1.5450 + } 1.5451 + operator intptr_t() const { 1.5452 + return fd_; 1.5453 + } 1.5454 + intptr_t forget() { 1.5455 + intptr_t ret = fd_; 1.5456 + fd_ = -1; 1.5457 + return ret; 1.5458 + } 1.5459 +}; 1.5460 + 1.5461 +// To guard against corrupted cache files generated by previous crashes, write 1.5462 +// asmJSCacheCookie to the first uint32_t of the file only after the file is 1.5463 +// fully serialized and flushed to disk. 1.5464 +static const uint32_t asmJSCacheCookie = 0xabbadaba; 1.5465 + 1.5466 +static bool 1.5467 +ShellOpenAsmJSCacheEntryForRead(HandleObject global, const jschar *begin, const jschar *limit, 1.5468 + size_t *serializedSizeOut, const uint8_t **memoryOut, 1.5469 + intptr_t *handleOut) 1.5470 +{ 1.5471 + if (!jsCachingEnabled || !jsCacheAsmJSPath) 1.5472 + return false; 1.5473 + 1.5474 + ScopedFileDesc fd(open(jsCacheAsmJSPath, O_RDWR), ScopedFileDesc::READ_LOCK); 1.5475 + if (fd == -1) 1.5476 + return false; 1.5477 + 1.5478 + // Get the size and make sure we can dereference at least one uint32_t. 1.5479 + off_t off = lseek(fd, 0, SEEK_END); 1.5480 + if (off == -1 || off < (off_t)sizeof(uint32_t)) 1.5481 + return false; 1.5482 + 1.5483 + // Map the file into memory. 1.5484 + void *memory; 1.5485 +#ifdef XP_WIN 1.5486 + HANDLE fdOsHandle = (HANDLE)_get_osfhandle(fd); 1.5487 + HANDLE fileMapping = CreateFileMapping(fdOsHandle, nullptr, PAGE_READWRITE, 0, 0, nullptr); 1.5488 + if (!fileMapping) 1.5489 + return false; 1.5490 + 1.5491 + memory = MapViewOfFile(fileMapping, FILE_MAP_READ, 0, 0, 0); 1.5492 + CloseHandle(fileMapping); 1.5493 + if (!memory) 1.5494 + return false; 1.5495 +#else 1.5496 + memory = mmap(nullptr, off, PROT_READ, MAP_SHARED, fd, 0); 1.5497 + if (memory == MAP_FAILED) 1.5498 + return false; 1.5499 +#endif 1.5500 + 1.5501 + // Perform check described by asmJSCacheCookie comment. 1.5502 + if (*(uint32_t *)memory != asmJSCacheCookie) { 1.5503 +#ifdef XP_WIN 1.5504 + UnmapViewOfFile(memory); 1.5505 +#else 1.5506 + munmap(memory, off); 1.5507 +#endif 1.5508 + return false; 1.5509 + } 1.5510 + 1.5511 + // The embedding added the cookie so strip it off of the buffer returned to 1.5512 + // the JS engine. 1.5513 + *serializedSizeOut = off - sizeof(uint32_t); 1.5514 + *memoryOut = (uint8_t *)memory + sizeof(uint32_t); 1.5515 + *handleOut = fd.forget(); 1.5516 + return true; 1.5517 +} 1.5518 + 1.5519 +static void 1.5520 +ShellCloseAsmJSCacheEntryForRead(HandleObject global, size_t serializedSize, const uint8_t *memory, 1.5521 + intptr_t handle) 1.5522 +{ 1.5523 + // Undo the cookie adjustment done when opening the file. 1.5524 + memory -= sizeof(uint32_t); 1.5525 + serializedSize += sizeof(uint32_t); 1.5526 + 1.5527 + // Release the memory mapping and file. 1.5528 +#ifdef XP_WIN 1.5529 + UnmapViewOfFile(const_cast<uint8_t*>(memory)); 1.5530 +#else 1.5531 + munmap(const_cast<uint8_t*>(memory), serializedSize); 1.5532 +#endif 1.5533 + 1.5534 + JS_ASSERT(jsCacheOpened == true); 1.5535 + jsCacheOpened = false; 1.5536 + close(handle); 1.5537 +} 1.5538 + 1.5539 +static bool 1.5540 +ShellOpenAsmJSCacheEntryForWrite(HandleObject global, bool installed, 1.5541 + const jschar *begin, const jschar *end, 1.5542 + size_t serializedSize, uint8_t **memoryOut, intptr_t *handleOut) 1.5543 +{ 1.5544 + if (!jsCachingEnabled || !jsCacheAsmJSPath) 1.5545 + return false; 1.5546 + 1.5547 + // Create the cache directory if it doesn't already exist. 1.5548 + struct stat dirStat; 1.5549 + if (stat(jsCacheDir, &dirStat) == 0) { 1.5550 + if (!(dirStat.st_mode & S_IFDIR)) 1.5551 + return false; 1.5552 + } else { 1.5553 +#ifdef XP_WIN 1.5554 + if (mkdir(jsCacheDir) != 0) 1.5555 + return false; 1.5556 +#else 1.5557 + if (mkdir(jsCacheDir, 0777) != 0) 1.5558 + return false; 1.5559 +#endif 1.5560 + } 1.5561 + 1.5562 + ScopedFileDesc fd(open(jsCacheAsmJSPath, O_CREAT|O_RDWR, 0660), ScopedFileDesc::WRITE_LOCK); 1.5563 + if (fd == -1) 1.5564 + return false; 1.5565 + 1.5566 + // Include extra space for the asmJSCacheCookie. 1.5567 + serializedSize += sizeof(uint32_t); 1.5568 + 1.5569 + // Resize the file to the appropriate size after zeroing their contents. 1.5570 +#ifdef XP_WIN 1.5571 + if (chsize(fd, 0)) 1.5572 + return false; 1.5573 + if (chsize(fd, serializedSize)) 1.5574 + return false; 1.5575 +#else 1.5576 + if (ftruncate(fd, 0)) 1.5577 + return false; 1.5578 + if (ftruncate(fd, serializedSize)) 1.5579 + return false; 1.5580 +#endif 1.5581 + 1.5582 + // Map the file into memory. 1.5583 + void *memory; 1.5584 +#ifdef XP_WIN 1.5585 + HANDLE fdOsHandle = (HANDLE)_get_osfhandle(fd); 1.5586 + HANDLE fileMapping = CreateFileMapping(fdOsHandle, nullptr, PAGE_READWRITE, 0, 0, nullptr); 1.5587 + if (!fileMapping) 1.5588 + return false; 1.5589 + 1.5590 + memory = MapViewOfFile(fileMapping, FILE_MAP_WRITE, 0, 0, 0); 1.5591 + CloseHandle(fileMapping); 1.5592 + if (!memory) 1.5593 + return false; 1.5594 +#else 1.5595 + memory = mmap(nullptr, serializedSize, PROT_WRITE, MAP_SHARED, fd, 0); 1.5596 + if (memory == MAP_FAILED) 1.5597 + return false; 1.5598 +#endif 1.5599 + 1.5600 + // The embedding added the cookie so strip it off of the buffer returned to 1.5601 + // the JS engine. The asmJSCacheCookie will be written on close, below. 1.5602 + JS_ASSERT(*(uint32_t *)memory == 0); 1.5603 + *memoryOut = (uint8_t *)memory + sizeof(uint32_t); 1.5604 + *handleOut = fd.forget(); 1.5605 + return true; 1.5606 +} 1.5607 + 1.5608 +static void 1.5609 +ShellCloseAsmJSCacheEntryForWrite(HandleObject global, size_t serializedSize, uint8_t *memory, 1.5610 + intptr_t handle) 1.5611 +{ 1.5612 + // Undo the cookie adjustment done when opening the file. 1.5613 + memory -= sizeof(uint32_t); 1.5614 + serializedSize += sizeof(uint32_t); 1.5615 + 1.5616 + // Write the magic cookie value after flushing the entire cache entry. 1.5617 +#ifdef XP_WIN 1.5618 + FlushViewOfFile(memory, serializedSize); 1.5619 + FlushFileBuffers(HANDLE(_get_osfhandle(handle))); 1.5620 +#else 1.5621 + msync(memory, serializedSize, MS_SYNC); 1.5622 +#endif 1.5623 + 1.5624 + JS_ASSERT(*(uint32_t *)memory == 0); 1.5625 + *(uint32_t *)memory = asmJSCacheCookie; 1.5626 + 1.5627 + // Free the memory mapping and file. 1.5628 +#ifdef XP_WIN 1.5629 + UnmapViewOfFile(const_cast<uint8_t*>(memory)); 1.5630 +#else 1.5631 + munmap(memory, serializedSize); 1.5632 +#endif 1.5633 + 1.5634 + JS_ASSERT(jsCacheOpened == true); 1.5635 + jsCacheOpened = false; 1.5636 + close(handle); 1.5637 +} 1.5638 + 1.5639 +static bool 1.5640 +ShellBuildId(JS::BuildIdCharVector *buildId) 1.5641 +{ 1.5642 + // The browser embeds the date into the buildid and the buildid is embedded 1.5643 + // in the binary, so every 'make' necessarily builds a new firefox binary. 1.5644 + // Fortunately, the actual firefox executable is tiny -- all the code is in 1.5645 + // libxul.so and other shared modules -- so this isn't a big deal. Not so 1.5646 + // for the statically-linked JS shell. To avoid recompmiling js.cpp and 1.5647 + // re-linking 'js' on every 'make', we use a constant buildid and rely on 1.5648 + // the shell user to manually clear the cache (deleting the dir passed to 1.5649 + // --js-cache) between cache-breaking updates. Note: jit_tests.py does this 1.5650 + // on every run). 1.5651 + const char buildid[] = "JS-shell"; 1.5652 + return buildId->append(buildid, sizeof(buildid)); 1.5653 +} 1.5654 + 1.5655 +static JS::AsmJSCacheOps asmJSCacheOps = { 1.5656 + ShellOpenAsmJSCacheEntryForRead, 1.5657 + ShellCloseAsmJSCacheEntryForRead, 1.5658 + ShellOpenAsmJSCacheEntryForWrite, 1.5659 + ShellCloseAsmJSCacheEntryForWrite, 1.5660 + ShellBuildId 1.5661 +}; 1.5662 + 1.5663 +/* 1.5664 + * Avoid a reentrancy hazard. 1.5665 + * 1.5666 + * The non-JS_THREADSAFE shell uses a signal handler to implement timeout(). 1.5667 + * The JS engine is not really reentrant, but JS_RequestInterruptCallback 1.5668 + * is mostly safe--the only danger is that we might interrupt JS_NewContext or 1.5669 + * JS_DestroyContext while the context list is being modified. Therefore we 1.5670 + * disable the signal handler around calls to those functions. 1.5671 + */ 1.5672 +#ifdef JS_THREADSAFE 1.5673 +# define WITH_SIGNALS_DISABLED(x) x 1.5674 +#else 1.5675 +# define WITH_SIGNALS_DISABLED(x) \ 1.5676 + JS_BEGIN_MACRO \ 1.5677 + ScheduleWatchdog(gRuntime, -1); \ 1.5678 + x; \ 1.5679 + ScheduleWatchdog(gRuntime, gTimeoutInterval); \ 1.5680 + JS_END_MACRO 1.5681 +#endif 1.5682 + 1.5683 +static JSContext * 1.5684 +NewContext(JSRuntime *rt) 1.5685 +{ 1.5686 + JSContext *cx; 1.5687 + WITH_SIGNALS_DISABLED(cx = JS_NewContext(rt, gStackChunkSize)); 1.5688 + if (!cx) 1.5689 + return nullptr; 1.5690 + 1.5691 + JSShellContextData *data = NewContextData(); 1.5692 + if (!data) { 1.5693 + DestroyContext(cx, false); 1.5694 + return nullptr; 1.5695 + } 1.5696 + 1.5697 + JS_SetContextPrivate(cx, data); 1.5698 + JS_SetErrorReporter(cx, my_ErrorReporter); 1.5699 + return cx; 1.5700 +} 1.5701 + 1.5702 +static void 1.5703 +DestroyContext(JSContext *cx, bool withGC) 1.5704 +{ 1.5705 + JSShellContextData *data = GetContextData(cx); 1.5706 + JS_SetContextPrivate(cx, nullptr); 1.5707 + free(data); 1.5708 + WITH_SIGNALS_DISABLED(withGC ? JS_DestroyContext(cx) : JS_DestroyContextNoGC(cx)); 1.5709 +} 1.5710 + 1.5711 +static JSObject * 1.5712 +NewGlobalObject(JSContext *cx, JS::CompartmentOptions &options, 1.5713 + JSPrincipals *principals) 1.5714 +{ 1.5715 + RootedObject glob(cx, JS_NewGlobalObject(cx, &global_class, principals, 1.5716 + JS::DontFireOnNewGlobalHook, options)); 1.5717 + if (!glob) 1.5718 + return nullptr; 1.5719 + 1.5720 + { 1.5721 + JSAutoCompartment ac(cx, glob); 1.5722 + 1.5723 +#ifndef LAZY_STANDARD_CLASSES 1.5724 + if (!JS_InitStandardClasses(cx, glob)) 1.5725 + return nullptr; 1.5726 +#endif 1.5727 + 1.5728 +#ifdef JS_HAS_CTYPES 1.5729 + if (!JS_InitCTypesClass(cx, glob)) 1.5730 + return nullptr; 1.5731 +#endif 1.5732 + if (!JS_InitReflect(cx, glob)) 1.5733 + return nullptr; 1.5734 + if (!JS_DefineDebuggerObject(cx, glob)) 1.5735 + return nullptr; 1.5736 + if (!JS::RegisterPerfMeasurement(cx, glob)) 1.5737 + return nullptr; 1.5738 + if (!JS_DefineFunctionsWithHelp(cx, glob, shell_functions) || 1.5739 + !JS_DefineProfilingFunctions(cx, glob)) 1.5740 + { 1.5741 + return nullptr; 1.5742 + } 1.5743 + if (!js::DefineTestingFunctions(cx, glob, fuzzingSafe)) 1.5744 + return nullptr; 1.5745 + 1.5746 + if (!fuzzingSafe && !JS_DefineFunctionsWithHelp(cx, glob, fuzzing_unsafe_functions)) 1.5747 + return nullptr; 1.5748 + 1.5749 + /* Initialize FakeDOMObject. */ 1.5750 + static const js::DOMCallbacks DOMcallbacks = { 1.5751 + InstanceClassHasProtoAtDepth 1.5752 + }; 1.5753 + SetDOMCallbacks(cx->runtime(), &DOMcallbacks); 1.5754 + 1.5755 + RootedObject domProto(cx, JS_InitClass(cx, glob, js::NullPtr(), &dom_class, dom_constructor, 1.5756 + 0, dom_props, dom_methods, nullptr, nullptr)); 1.5757 + if (!domProto) 1.5758 + return nullptr; 1.5759 + 1.5760 + /* Initialize FakeDOMObject.prototype */ 1.5761 + InitDOMObject(domProto); 1.5762 + } 1.5763 + 1.5764 + JS_FireOnNewGlobalObject(cx, glob); 1.5765 + 1.5766 + return glob; 1.5767 +} 1.5768 + 1.5769 +static bool 1.5770 +BindScriptArgs(JSContext *cx, JSObject *obj_, OptionParser *op) 1.5771 +{ 1.5772 + RootedObject obj(cx, obj_); 1.5773 + 1.5774 + MultiStringRange msr = op->getMultiStringArg("scriptArgs"); 1.5775 + RootedObject scriptArgs(cx); 1.5776 + scriptArgs = JS_NewArrayObject(cx, 0); 1.5777 + if (!scriptArgs) 1.5778 + return false; 1.5779 + 1.5780 + if (!JS_DefineProperty(cx, obj, "scriptArgs", scriptArgs, 0)) 1.5781 + return false; 1.5782 + 1.5783 + for (size_t i = 0; !msr.empty(); msr.popFront(), ++i) { 1.5784 + const char *scriptArg = msr.front(); 1.5785 + JSString *str = JS_NewStringCopyZ(cx, scriptArg); 1.5786 + if (!str || 1.5787 + !JS_DefineElement(cx, scriptArgs, i, STRING_TO_JSVAL(str), nullptr, nullptr, 1.5788 + JSPROP_ENUMERATE)) { 1.5789 + return false; 1.5790 + } 1.5791 + } 1.5792 + 1.5793 + return true; 1.5794 +} 1.5795 + 1.5796 +// This function is currently only called from "#if defined(JS_ION)" chunks, 1.5797 +// so we're guarding the function definition with an #ifdef, too, to avoid 1.5798 +// build warning for unused function in non-ion-enabled builds: 1.5799 +#if defined(JS_ION) 1.5800 +static bool 1.5801 +OptionFailure(const char *option, const char *str) 1.5802 +{ 1.5803 + fprintf(stderr, "Unrecognized option for %s: %s\n", option, str); 1.5804 + return false; 1.5805 +} 1.5806 +#endif /* JS_ION */ 1.5807 + 1.5808 +static int 1.5809 +ProcessArgs(JSContext *cx, JSObject *obj_, OptionParser *op) 1.5810 +{ 1.5811 + RootedObject obj(cx, obj_); 1.5812 + 1.5813 + if (op->getBoolOption('s')) 1.5814 + JS::ContextOptionsRef(cx).toggleExtraWarnings(); 1.5815 + 1.5816 + if (op->getBoolOption('d')) { 1.5817 + JS_SetRuntimeDebugMode(JS_GetRuntime(cx), true); 1.5818 + JS_SetDebugMode(cx, true); 1.5819 + } 1.5820 + 1.5821 + /* |scriptArgs| gets bound on the global before any code is run. */ 1.5822 + if (!BindScriptArgs(cx, obj, op)) 1.5823 + return EXIT_FAILURE; 1.5824 + 1.5825 + MultiStringRange filePaths = op->getMultiStringOption('f'); 1.5826 + MultiStringRange codeChunks = op->getMultiStringOption('e'); 1.5827 + 1.5828 + if (filePaths.empty() && codeChunks.empty() && !op->getStringArg("script")) { 1.5829 + Process(cx, obj, nullptr, true); /* Interactive. */ 1.5830 + return gExitCode; 1.5831 + } 1.5832 + 1.5833 + while (!filePaths.empty() || !codeChunks.empty()) { 1.5834 + size_t fpArgno = filePaths.empty() ? -1 : filePaths.argno(); 1.5835 + size_t ccArgno = codeChunks.empty() ? -1 : codeChunks.argno(); 1.5836 + if (fpArgno < ccArgno) { 1.5837 + char *path = filePaths.front(); 1.5838 + Process(cx, obj, path, false); 1.5839 + if (gExitCode) 1.5840 + return gExitCode; 1.5841 + filePaths.popFront(); 1.5842 + } else { 1.5843 + const char *code = codeChunks.front(); 1.5844 + RootedValue rval(cx); 1.5845 + if (!JS_EvaluateScript(cx, obj, code, strlen(code), "-e", 1, &rval)) 1.5846 + return gExitCode ? gExitCode : EXITCODE_RUNTIME_ERROR; 1.5847 + codeChunks.popFront(); 1.5848 + } 1.5849 + } 1.5850 + 1.5851 + /* The |script| argument is processed after all options. */ 1.5852 + if (const char *path = op->getStringArg("script")) { 1.5853 + Process(cx, obj, path, false); 1.5854 + if (gExitCode) 1.5855 + return gExitCode; 1.5856 + } 1.5857 + 1.5858 + if (op->getBoolOption('i')) 1.5859 + Process(cx, obj, nullptr, true); 1.5860 + 1.5861 + return gExitCode ? gExitCode : EXIT_SUCCESS; 1.5862 +} 1.5863 + 1.5864 +static bool 1.5865 +SetRuntimeOptions(JSRuntime *rt, const OptionParser &op) 1.5866 +{ 1.5867 +#if defined(JS_ION) 1.5868 + bool enableBaseline = !op.getBoolOption("no-baseline"); 1.5869 + bool enableIon = !op.getBoolOption("no-ion"); 1.5870 + bool enableAsmJS = !op.getBoolOption("no-asmjs"); 1.5871 + 1.5872 + JS::RuntimeOptionsRef(rt).setBaseline(enableBaseline) 1.5873 + .setIon(enableIon) 1.5874 + .setAsmJS(enableAsmJS); 1.5875 + 1.5876 + if (const char *str = op.getStringOption("ion-gvn")) { 1.5877 + if (strcmp(str, "off") == 0) { 1.5878 + jit::js_JitOptions.disableGvn = true; 1.5879 + } else if (strcmp(str, "pessimistic") == 0) { 1.5880 + jit::js_JitOptions.forceGvnKind = true; 1.5881 + jit::js_JitOptions.forcedGvnKind = jit::GVN_Pessimistic; 1.5882 + } else if (strcmp(str, "optimistic") == 0) { 1.5883 + jit::js_JitOptions.forceGvnKind = true; 1.5884 + jit::js_JitOptions.forcedGvnKind = jit::GVN_Optimistic; 1.5885 + } else { 1.5886 + return OptionFailure("ion-gvn", str); 1.5887 + } 1.5888 + } 1.5889 + 1.5890 + if (const char *str = op.getStringOption("ion-licm")) { 1.5891 + if (strcmp(str, "on") == 0) 1.5892 + jit::js_JitOptions.disableLicm = false; 1.5893 + else if (strcmp(str, "off") == 0) 1.5894 + jit::js_JitOptions.disableLicm = true; 1.5895 + else 1.5896 + return OptionFailure("ion-licm", str); 1.5897 + } 1.5898 + 1.5899 + if (const char *str = op.getStringOption("ion-edgecase-analysis")) { 1.5900 + if (strcmp(str, "on") == 0) 1.5901 + jit::js_JitOptions.disableEdgeCaseAnalysis = false; 1.5902 + else if (strcmp(str, "off") == 0) 1.5903 + jit::js_JitOptions.disableEdgeCaseAnalysis = true; 1.5904 + else 1.5905 + return OptionFailure("ion-edgecase-analysis", str); 1.5906 + } 1.5907 + 1.5908 + if (const char *str = op.getStringOption("ion-range-analysis")) { 1.5909 + if (strcmp(str, "on") == 0) 1.5910 + jit::js_JitOptions.disableRangeAnalysis = false; 1.5911 + else if (strcmp(str, "off") == 0) 1.5912 + jit::js_JitOptions.disableRangeAnalysis = true; 1.5913 + else 1.5914 + return OptionFailure("ion-range-analysis", str); 1.5915 + } 1.5916 + 1.5917 + if (op.getBoolOption("ion-check-range-analysis")) 1.5918 + jit::js_JitOptions.checkRangeAnalysis = true; 1.5919 + 1.5920 + if (const char *str = op.getStringOption("ion-inlining")) { 1.5921 + if (strcmp(str, "on") == 0) 1.5922 + jit::js_JitOptions.disableInlining = false; 1.5923 + else if (strcmp(str, "off") == 0) 1.5924 + jit::js_JitOptions.disableInlining = true; 1.5925 + else 1.5926 + return OptionFailure("ion-inlining", str); 1.5927 + } 1.5928 + 1.5929 + if (const char *str = op.getStringOption("ion-osr")) { 1.5930 + if (strcmp(str, "on") == 0) 1.5931 + jit::js_JitOptions.osr = true; 1.5932 + else if (strcmp(str, "off") == 0) 1.5933 + jit::js_JitOptions.osr = false; 1.5934 + else 1.5935 + return OptionFailure("ion-osr", str); 1.5936 + } 1.5937 + 1.5938 + if (const char *str = op.getStringOption("ion-limit-script-size")) { 1.5939 + if (strcmp(str, "on") == 0) 1.5940 + jit::js_JitOptions.limitScriptSize = true; 1.5941 + else if (strcmp(str, "off") == 0) 1.5942 + jit::js_JitOptions.limitScriptSize = false; 1.5943 + else 1.5944 + return OptionFailure("ion-limit-script-size", str); 1.5945 + } 1.5946 + 1.5947 + int32_t useCount = op.getIntOption("ion-uses-before-compile"); 1.5948 + if (useCount >= 0) 1.5949 + jit::js_JitOptions.setUsesBeforeCompile(useCount); 1.5950 + 1.5951 + useCount = op.getIntOption("baseline-uses-before-compile"); 1.5952 + if (useCount >= 0) 1.5953 + jit::js_JitOptions.baselineUsesBeforeCompile = useCount; 1.5954 + 1.5955 + if (op.getBoolOption("baseline-eager")) 1.5956 + jit::js_JitOptions.baselineUsesBeforeCompile = 0; 1.5957 + 1.5958 + if (const char *str = op.getStringOption("ion-regalloc")) { 1.5959 + if (strcmp(str, "lsra") == 0) { 1.5960 + jit::js_JitOptions.forceRegisterAllocator = true; 1.5961 + jit::js_JitOptions.forcedRegisterAllocator = jit::RegisterAllocator_LSRA; 1.5962 + } else if (strcmp(str, "backtracking") == 0) { 1.5963 + jit::js_JitOptions.forceRegisterAllocator = true; 1.5964 + jit::js_JitOptions.forcedRegisterAllocator = jit::RegisterAllocator_Backtracking; 1.5965 + } else if (strcmp(str, "stupid") == 0) { 1.5966 + jit::js_JitOptions.forceRegisterAllocator = true; 1.5967 + jit::js_JitOptions.forcedRegisterAllocator = jit::RegisterAllocator_Stupid; 1.5968 + } else { 1.5969 + return OptionFailure("ion-regalloc", str); 1.5970 + } 1.5971 + } 1.5972 + 1.5973 + if (op.getBoolOption("ion-eager")) 1.5974 + jit::js_JitOptions.setEagerCompilation(); 1.5975 + 1.5976 + if (op.getBoolOption("ion-compile-try-catch")) 1.5977 + jit::js_JitOptions.compileTryCatch = true; 1.5978 + 1.5979 + bool parallelCompilation = true; 1.5980 + if (const char *str = op.getStringOption("ion-parallel-compile")) { 1.5981 + if (strcmp(str, "off") == 0) 1.5982 + parallelCompilation = false; 1.5983 + else if (strcmp(str, "on") != 0) 1.5984 + return OptionFailure("ion-parallel-compile", str); 1.5985 + } 1.5986 +#ifdef JS_THREADSAFE 1.5987 + rt->setParallelIonCompilationEnabled(parallelCompilation); 1.5988 +#endif 1.5989 + 1.5990 +#endif // JS_ION 1.5991 + 1.5992 +#ifdef JS_ARM_SIMULATOR 1.5993 + if (op.getBoolOption("arm-sim-icache-checks")) 1.5994 + jit::Simulator::ICacheCheckingEnabled = true; 1.5995 + 1.5996 + int32_t stopAt = op.getIntOption("arm-sim-stop-at"); 1.5997 + if (stopAt >= 0) 1.5998 + jit::Simulator::StopSimAt = stopAt; 1.5999 +#endif 1.6000 + 1.6001 + reportWarnings = op.getBoolOption('w'); 1.6002 + compileOnly = op.getBoolOption('c'); 1.6003 + printTiming = op.getBoolOption('b'); 1.6004 + rt->profilingScripts = enableDisassemblyDumps = op.getBoolOption('D'); 1.6005 + 1.6006 + jsCacheDir = op.getStringOption("js-cache"); 1.6007 + if (jsCacheDir) { 1.6008 + if (op.getBoolOption("js-cache-per-process")) 1.6009 + jsCacheDir = JS_smprintf("%s/%u", jsCacheDir, (unsigned)getpid()); 1.6010 + jsCacheAsmJSPath = JS_smprintf("%s/asmjs.cache", jsCacheDir); 1.6011 + } 1.6012 + 1.6013 +#ifdef JS_THREADSAFE 1.6014 + int32_t threadCount = op.getIntOption("thread-count"); 1.6015 + if (threadCount >= 0) 1.6016 + SetFakeCPUCount(threadCount); 1.6017 +#endif /* JS_THREADSAFE */ 1.6018 + 1.6019 +#ifdef DEBUG 1.6020 + dumpEntrainedVariables = op.getBoolOption("dump-entrained-variables"); 1.6021 +#endif 1.6022 + 1.6023 + return true; 1.6024 +} 1.6025 + 1.6026 +static int 1.6027 +Shell(JSContext *cx, OptionParser *op, char **envp) 1.6028 +{ 1.6029 + JSAutoRequest ar(cx); 1.6030 + 1.6031 + if (op->getBoolOption("fuzzing-safe")) 1.6032 + fuzzingSafe = true; 1.6033 + else 1.6034 + fuzzingSafe = (getenv("MOZ_FUZZING_SAFE") && getenv("MOZ_FUZZING_SAFE")[0] != '0'); 1.6035 + 1.6036 + RootedObject glob(cx); 1.6037 + JS::CompartmentOptions options; 1.6038 + options.setVersion(JSVERSION_LATEST); 1.6039 + glob = NewGlobalObject(cx, options, nullptr); 1.6040 + if (!glob) 1.6041 + return 1; 1.6042 + 1.6043 + JSAutoCompartment ac(cx, glob); 1.6044 + js::SetDefaultObjectForContext(cx, glob); 1.6045 + 1.6046 + JSObject *envobj = JS_DefineObject(cx, glob, "environment", &env_class, nullptr, 0); 1.6047 + if (!envobj) 1.6048 + return 1; 1.6049 + JS_SetPrivate(envobj, envp); 1.6050 + 1.6051 + int result = ProcessArgs(cx, glob, op); 1.6052 + 1.6053 + if (enableDisassemblyDumps) 1.6054 + JS_DumpCompartmentPCCounts(cx); 1.6055 + 1.6056 + if (op->getBoolOption("js-cache-per-process")) { 1.6057 + if (jsCacheAsmJSPath) 1.6058 + unlink(jsCacheAsmJSPath); 1.6059 + if (jsCacheDir) 1.6060 + rmdir(jsCacheDir); 1.6061 + } 1.6062 + 1.6063 + return result; 1.6064 +} 1.6065 + 1.6066 +static void 1.6067 +MaybeOverrideOutFileFromEnv(const char* const envVar, 1.6068 + FILE* defaultOut, 1.6069 + FILE** outFile) 1.6070 +{ 1.6071 + const char* outPath = getenv(envVar); 1.6072 + if (!outPath || !*outPath || !(*outFile = fopen(outPath, "w"))) { 1.6073 + *outFile = defaultOut; 1.6074 + } 1.6075 +} 1.6076 + 1.6077 +/* Pretend we can always preserve wrappers for dummy DOM objects. */ 1.6078 +static bool 1.6079 +DummyPreserveWrapperCallback(JSContext *cx, JSObject *obj) 1.6080 +{ 1.6081 + return true; 1.6082 +} 1.6083 + 1.6084 +int 1.6085 +main(int argc, char **argv, char **envp) 1.6086 +{ 1.6087 + sArgc = argc; 1.6088 + sArgv = argv; 1.6089 + 1.6090 + JSRuntime *rt; 1.6091 + JSContext *cx; 1.6092 + int result; 1.6093 +#ifdef XP_WIN 1.6094 + { 1.6095 + const char *crash_option = getenv("XRE_NO_WINDOWS_CRASH_DIALOG"); 1.6096 + if (crash_option && strncmp(crash_option, "1", 1)) { 1.6097 + DWORD oldmode = SetErrorMode(SEM_NOGPFAULTERRORBOX); 1.6098 + SetErrorMode(oldmode | SEM_NOGPFAULTERRORBOX); 1.6099 + } 1.6100 + } 1.6101 +#endif 1.6102 + 1.6103 +#ifdef HAVE_SETLOCALE 1.6104 + setlocale(LC_ALL, ""); 1.6105 +#endif 1.6106 + 1.6107 + MaybeOverrideOutFileFromEnv("JS_STDERR", stderr, &gErrFile); 1.6108 + MaybeOverrideOutFileFromEnv("JS_STDOUT", stdout, &gOutFile); 1.6109 + 1.6110 + OptionParser op("Usage: {progname} [options] [[script] scriptArgs*]"); 1.6111 + 1.6112 + op.setDescription("The SpiderMonkey shell provides a command line interface to the " 1.6113 + "JavaScript engine. Code and file options provided via the command line are " 1.6114 + "run left to right. If provided, the optional script argument is run after " 1.6115 + "all options have been processed. Just-In-Time compilation modes may be enabled via " 1.6116 + "command line options."); 1.6117 + op.setDescriptionWidth(72); 1.6118 + op.setHelpWidth(80); 1.6119 + op.setVersion(JS_GetImplementationVersion()); 1.6120 + 1.6121 + if (!op.addMultiStringOption('f', "file", "PATH", "File path to run") 1.6122 + || !op.addMultiStringOption('e', "execute", "CODE", "Inline code to run") 1.6123 + || !op.addBoolOption('i', "shell", "Enter prompt after running code") 1.6124 + || !op.addBoolOption('m', "jm", "No-op (still used by fuzzers)") 1.6125 + || !op.addBoolOption('\0', "no-jm", "No-op (still used by fuzzers)") 1.6126 + || !op.addBoolOption('c', "compileonly", "Only compile, don't run (syntax checking mode)") 1.6127 + || !op.addBoolOption('w', "warnings", "Emit warnings") 1.6128 + || !op.addBoolOption('W', "nowarnings", "Don't emit warnings") 1.6129 + || !op.addBoolOption('s', "strict", "Check strictness") 1.6130 + || !op.addBoolOption('d', "debugjit", "Enable runtime debug mode for method JIT code") 1.6131 + || !op.addBoolOption('a', "always-mjit", "No-op (still used by fuzzers)") 1.6132 + || !op.addBoolOption('D', "dump-bytecode", "Dump bytecode with exec count for all scripts") 1.6133 + || !op.addBoolOption('b', "print-timing", "Print sub-ms runtime for each file that's run") 1.6134 + || !op.addStringOption('\0', "js-cache", "[path]", 1.6135 + "Enable the JS cache by specifying the path of the directory to use " 1.6136 + "to hold cache files") 1.6137 + || !op.addBoolOption('\0', "js-cache-per-process", 1.6138 + "Generate a separate cache sub-directory for this process inside " 1.6139 + "the cache directory specified by --js-cache. This cache directory " 1.6140 + "will be removed when the js shell exits. This is useful for running " 1.6141 + "tests in parallel.") 1.6142 +#ifdef DEBUG 1.6143 + || !op.addBoolOption('O', "print-alloc", "Print the number of allocations at exit") 1.6144 +#endif 1.6145 + || !op.addOptionalStringArg("script", "A script to execute (after all options)") 1.6146 + || !op.addOptionalMultiStringArg("scriptArgs", 1.6147 + "String arguments to bind as |scriptArgs| in the " 1.6148 + "shell's global") 1.6149 +#ifdef JS_THREADSAFE 1.6150 + || !op.addIntOption('\0', "thread-count", "COUNT", "Use COUNT auxiliary threads " 1.6151 + "(default: # of cores - 1)", -1) 1.6152 +#endif 1.6153 + || !op.addBoolOption('\0', "ion", "Enable IonMonkey (default)") 1.6154 + || !op.addBoolOption('\0', "no-ion", "Disable IonMonkey") 1.6155 + || !op.addBoolOption('\0', "no-asmjs", "Disable asm.js compilation") 1.6156 + || !op.addStringOption('\0', "ion-gvn", "[mode]", 1.6157 + "Specify Ion global value numbering:\n" 1.6158 + " off: disable GVN\n" 1.6159 + " pessimistic: use pessimistic GVN\n" 1.6160 + " optimistic: (default) use optimistic GVN") 1.6161 + || !op.addStringOption('\0', "ion-licm", "on/off", 1.6162 + "Loop invariant code motion (default: on, off to disable)") 1.6163 + || !op.addStringOption('\0', "ion-edgecase-analysis", "on/off", 1.6164 + "Find edge cases where Ion can avoid bailouts (default: on, off to disable)") 1.6165 + || !op.addStringOption('\0', "ion-range-analysis", "on/off", 1.6166 + "Range analysis (default: on, off to disable)") 1.6167 + || !op.addBoolOption('\0', "ion-check-range-analysis", 1.6168 + "Range analysis checking") 1.6169 + || !op.addStringOption('\0', "ion-inlining", "on/off", 1.6170 + "Inline methods where possible (default: on, off to disable)") 1.6171 + || !op.addStringOption('\0', "ion-osr", "on/off", 1.6172 + "On-Stack Replacement (default: on, off to disable)") 1.6173 + || !op.addStringOption('\0', "ion-limit-script-size", "on/off", 1.6174 + "Don't compile very large scripts (default: on, off to disable)") 1.6175 + || !op.addIntOption('\0', "ion-uses-before-compile", "COUNT", 1.6176 + "Wait for COUNT calls or iterations before compiling " 1.6177 + "(default: 1000)", -1) 1.6178 + || !op.addStringOption('\0', "ion-regalloc", "[mode]", 1.6179 + "Specify Ion register allocation:\n" 1.6180 + " lsra: Linear Scan register allocation (default)\n" 1.6181 + " backtracking: Priority based backtracking register allocation\n" 1.6182 + " stupid: Simple block local register allocation") 1.6183 + || !op.addBoolOption('\0', "ion-eager", "Always ion-compile methods (implies --baseline-eager)") 1.6184 + || !op.addBoolOption('\0', "ion-compile-try-catch", "Ion-compile try-catch statements") 1.6185 + || !op.addStringOption('\0', "ion-parallel-compile", "on/off", 1.6186 + "Compile scripts off thread (default: off)") 1.6187 + || !op.addBoolOption('\0', "baseline", "Enable baseline compiler (default)") 1.6188 + || !op.addBoolOption('\0', "no-baseline", "Disable baseline compiler") 1.6189 + || !op.addBoolOption('\0', "baseline-eager", "Always baseline-compile methods") 1.6190 + || !op.addIntOption('\0', "baseline-uses-before-compile", "COUNT", 1.6191 + "Wait for COUNT calls or iterations before baseline-compiling " 1.6192 + "(default: 10)", -1) 1.6193 + || !op.addBoolOption('\0', "no-fpu", "Pretend CPU does not support floating-point operations " 1.6194 + "to test JIT codegen (no-op on platforms other than x86).") 1.6195 + || !op.addBoolOption('\0', "no-sse3", "Pretend CPU does not support SSE3 instructions and above " 1.6196 + "to test JIT codegen (no-op on platforms other than x86 and x64).") 1.6197 + || !op.addBoolOption('\0', "no-sse4", "Pretend CPU does not support SSE4 instructions" 1.6198 + "to test JIT codegen (no-op on platforms other than x86 and x64).") 1.6199 + || !op.addBoolOption('\0', "fuzzing-safe", "Don't expose functions that aren't safe for " 1.6200 + "fuzzers to call") 1.6201 +#ifdef DEBUG 1.6202 + || !op.addBoolOption('\0', "dump-entrained-variables", "Print variables which are " 1.6203 + "unnecessarily entrained by inner functions") 1.6204 +#endif 1.6205 +#ifdef JSGC_GENERATIONAL 1.6206 + || !op.addBoolOption('\0', "no-ggc", "Disable Generational GC") 1.6207 +#endif 1.6208 + || !op.addIntOption('\0', "available-memory", "SIZE", 1.6209 + "Select GC settings based on available memory (MB)", 0) 1.6210 +#ifdef JS_ARM_SIMULATOR 1.6211 + || !op.addBoolOption('\0', "arm-sim-icache-checks", "Enable icache flush checks in the ARM " 1.6212 + "simulator.") 1.6213 + || !op.addIntOption('\0', "arm-sim-stop-at", "NUMBER", "Stop the ARM simulator after the given " 1.6214 + "NUMBER of instructions.", -1) 1.6215 +#endif 1.6216 + ) 1.6217 + { 1.6218 + return EXIT_FAILURE; 1.6219 + } 1.6220 + 1.6221 + op.setArgTerminatesOptions("script", true); 1.6222 + op.setArgCapturesRest("scriptArgs"); 1.6223 + 1.6224 + switch (op.parseArgs(argc, argv)) { 1.6225 + case OptionParser::ParseHelp: 1.6226 + return EXIT_SUCCESS; 1.6227 + case OptionParser::ParseError: 1.6228 + op.printHelp(argv[0]); 1.6229 + return EXIT_FAILURE; 1.6230 + case OptionParser::Fail: 1.6231 + return EXIT_FAILURE; 1.6232 + case OptionParser::Okay: 1.6233 + break; 1.6234 + } 1.6235 + 1.6236 + if (op.getHelpOption()) 1.6237 + return EXIT_SUCCESS; 1.6238 + 1.6239 +#ifdef DEBUG 1.6240 + /* 1.6241 + * Process OOM options as early as possible so that we can observe as many 1.6242 + * allocations as possible. 1.6243 + */ 1.6244 + OOM_printAllocationCount = op.getBoolOption('O'); 1.6245 + 1.6246 +#if defined(JS_CODEGEN_X86) && defined(JS_ION) 1.6247 + if (op.getBoolOption("no-fpu")) 1.6248 + JSC::MacroAssembler::SetFloatingPointDisabled(); 1.6249 +#endif 1.6250 + 1.6251 +#if (defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)) && defined(JS_ION) 1.6252 + if (op.getBoolOption("no-sse3")) { 1.6253 + JSC::MacroAssembler::SetSSE3Disabled(); 1.6254 + PropagateFlagToNestedShells("--no-sse3"); 1.6255 + } 1.6256 + if (op.getBoolOption("no-sse4")) { 1.6257 + JSC::MacroAssembler::SetSSE4Disabled(); 1.6258 + PropagateFlagToNestedShells("--no-sse4"); 1.6259 + } 1.6260 +#endif 1.6261 + 1.6262 +#endif // DEBUG 1.6263 + 1.6264 + // Start the engine. 1.6265 + if (!JS_Init()) 1.6266 + return 1; 1.6267 + 1.6268 + /* Use the same parameters as the browser in xpcjsruntime.cpp. */ 1.6269 + rt = JS_NewRuntime(32L * 1024L * 1024L, JS_USE_HELPER_THREADS); 1.6270 + if (!rt) 1.6271 + return 1; 1.6272 + 1.6273 + JS::SetOutOfMemoryCallback(rt, my_OOMCallback); 1.6274 + if (!SetRuntimeOptions(rt, op)) 1.6275 + return 1; 1.6276 + 1.6277 + gInterruptFunc.construct(rt, NullValue()); 1.6278 + 1.6279 + JS_SetGCParameter(rt, JSGC_MAX_BYTES, 0xffffffff); 1.6280 +#ifdef JSGC_GENERATIONAL 1.6281 + Maybe<JS::AutoDisableGenerationalGC> noggc; 1.6282 + if (op.getBoolOption("no-ggc")) 1.6283 + noggc.construct(rt); 1.6284 +#endif 1.6285 + 1.6286 + size_t availMem = op.getIntOption("available-memory"); 1.6287 + if (availMem > 0) 1.6288 + JS_SetGCParametersBasedOnAvailableMemory(rt, availMem); 1.6289 + 1.6290 + JS_SetTrustedPrincipals(rt, &ShellPrincipals::fullyTrusted); 1.6291 + JS_SetSecurityCallbacks(rt, &ShellPrincipals::securityCallbacks); 1.6292 + JS_InitDestroyPrincipalsCallback(rt, ShellPrincipals::destroy); 1.6293 + 1.6294 + JS_SetInterruptCallback(rt, ShellInterruptCallback); 1.6295 + JS::SetAsmJSCacheOps(rt, &asmJSCacheOps); 1.6296 + 1.6297 + JS_SetNativeStackQuota(rt, gMaxStackSize); 1.6298 + 1.6299 +#ifdef JS_THREADSAFE 1.6300 + if (!offThreadState.init()) 1.6301 + return 1; 1.6302 +#endif 1.6303 + 1.6304 + if (!InitWatchdog(rt)) 1.6305 + return 1; 1.6306 + 1.6307 + cx = NewContext(rt); 1.6308 + if (!cx) 1.6309 + return 1; 1.6310 + 1.6311 + JS_SetGCParameter(rt, JSGC_MODE, JSGC_MODE_INCREMENTAL); 1.6312 + JS_SetGCParameterForThread(cx, JSGC_MAX_CODE_CACHE_BYTES, 16 * 1024 * 1024); 1.6313 + 1.6314 + js::SetPreserveWrapperCallback(rt, DummyPreserveWrapperCallback); 1.6315 + 1.6316 + result = Shell(cx, &op, envp); 1.6317 + 1.6318 +#ifdef DEBUG 1.6319 + if (OOM_printAllocationCount) 1.6320 + printf("OOM max count: %u\n", OOM_counter); 1.6321 +#endif 1.6322 + 1.6323 + DestroyContext(cx, true); 1.6324 + 1.6325 + KillWatchdog(); 1.6326 + 1.6327 + gInterruptFunc.destroy(); 1.6328 + 1.6329 +#ifdef JS_THREADSAFE 1.6330 + for (size_t i = 0; i < workerThreads.length(); i++) 1.6331 + PR_JoinThread(workerThreads[i]); 1.6332 +#endif 1.6333 + 1.6334 +#ifdef JSGC_GENERATIONAL 1.6335 + if (!noggc.empty()) 1.6336 + noggc.destroy(); 1.6337 +#endif 1.6338 + 1.6339 + JS_DestroyRuntime(rt); 1.6340 + JS_ShutDown(); 1.6341 + return result; 1.6342 +}