js/src/shell/js.cpp

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

mercurial