js/src/shell/js.cpp

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

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

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

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

mercurial