Sat, 03 Jan 2015 20:18:00 +0100
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 | } |