CLOSED TREE: TraceMonkey merge head. (a=blockers)
[mozilla-central.git] / js / src / shell / js.cpp
blobeeb0f892fff1be1853daac2159d8a2199397ac67
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sw=4 et tw=99:
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
15 * License.
17 * The Original Code is Mozilla Communicator client code, released
18 * March 31, 1998.
20 * The Initial Developer of the Original Code is
21 * Netscape Communications Corporation.
22 * Portions created by the Initial Developer are Copyright (C) 1998
23 * the Initial Developer. All Rights Reserved.
25 * Contributor(s):
27 * Alternatively, the contents of this file may be used under the terms of
28 * either of the GNU General Public License Version 2 or later (the "GPL"),
29 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
41 #define __STDC_LIMIT_MACROS
44 * JS shell.
46 #include <errno.h>
47 #include <math.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <signal.h>
52 #include <locale.h>
53 #include "jstypes.h"
54 #include "jsstdint.h"
55 #include "jsarena.h"
56 #include "jsutil.h"
57 #include "jsprf.h"
58 #include "jswrapper.h"
59 #include "jsapi.h"
60 #include "jsarray.h"
61 #include "jsatom.h"
62 #include "jsbuiltins.h"
63 #include "jscntxt.h"
64 #include "jsdate.h"
65 #include "jsdbgapi.h"
66 #include "jsemit.h"
67 #include "jsfun.h"
68 #include "jsgc.h"
69 #include "jsiter.h"
70 #include "jslock.h"
71 #include "jsnum.h"
72 #include "jsobj.h"
73 #include "jsparse.h"
74 #include "jsreflect.h"
75 #include "jsscope.h"
76 #include "jsscript.h"
77 #include "jstracer.h"
78 #include "jstypedarray.h"
79 #include "jsxml.h"
80 #include "jsperf.h"
82 #include "prmjtime.h"
84 #ifdef JSDEBUGGER
85 #include "jsdebug.h"
86 #ifdef JSDEBUGGER_JAVA_UI
87 #include "jsdjava.h"
88 #endif /* JSDEBUGGER_JAVA_UI */
89 #ifdef JSDEBUGGER_C_UI
90 #include "jsdb.h"
91 #endif /* JSDEBUGGER_C_UI */
92 #endif /* JSDEBUGGER */
94 #include "jsworkers.h"
96 #include "jsinterpinlines.h"
97 #include "jsobjinlines.h"
98 #include "jsscriptinlines.h"
100 #ifdef XP_UNIX
101 #include <unistd.h>
102 #include <sys/types.h>
103 #include <sys/wait.h>
104 #endif
106 #if defined(XP_WIN) || defined(XP_OS2)
107 #include <io.h> /* for isatty() */
108 #endif
110 #ifdef XP_WIN
111 #include "jswin.h"
112 #endif
114 using namespace js;
116 typedef enum JSShellExitCode {
117 EXITCODE_RUNTIME_ERROR = 3,
118 EXITCODE_FILE_NOT_FOUND = 4,
119 EXITCODE_OUT_OF_MEMORY = 5,
120 EXITCODE_TIMEOUT = 6
121 } JSShellExitCode;
123 size_t gStackChunkSize = 8192;
125 /* Assume that we can not use more than 5e5 bytes of C stack by default. */
126 #if (defined(DEBUG) && defined(__SUNPRO_CC)) || defined(JS_CPU_SPARC)
127 /* Sun compiler uses larger stack space for js_Interpret() with debug
128 Use a bigger gMaxStackSize to make "make check" happy. */
129 #define DEFAULT_MAX_STACK_SIZE 5000000
130 #else
131 #define DEFAULT_MAX_STACK_SIZE 500000
132 #endif
134 size_t gMaxStackSize = DEFAULT_MAX_STACK_SIZE;
137 #ifdef JS_THREADSAFE
138 static PRUintn gStackBaseThreadIndex;
139 #else
140 static jsuword gStackBase;
141 #endif
143 static size_t gScriptStackQuota = JS_DEFAULT_SCRIPT_STACK_QUOTA;
146 * Limit the timeout to 30 minutes to prevent an overflow on platfoms
147 * that represent the time internally in microseconds using 32-bit int.
149 static jsdouble MAX_TIMEOUT_INTERVAL = 1800.0;
150 static jsdouble gTimeoutInterval = -1.0;
151 static volatile bool gCanceled = false;
153 static bool enableTraceJit = false;
154 static bool enableMethodJit = false;
155 static bool enableProfiling = false;
157 static bool printTiming = false;
159 static JSBool
160 SetTimeoutValue(JSContext *cx, jsdouble t);
162 static bool
163 InitWatchdog(JSRuntime *rt);
165 static void
166 KillWatchdog();
168 static bool
169 ScheduleWatchdog(JSRuntime *rt, jsdouble t);
171 static void
172 CancelExecution(JSRuntime *rt);
175 * Watchdog thread state.
177 #ifdef JS_THREADSAFE
179 static PRLock *gWatchdogLock = NULL;
180 static PRCondVar *gWatchdogWakeup = NULL;
181 static PRThread *gWatchdogThread = NULL;
182 static bool gWatchdogHasTimeout = false;
183 static PRIntervalTime gWatchdogTimeout = 0;
185 static PRCondVar *gSleepWakeup = NULL;
187 #else
189 static JSRuntime *gRuntime = NULL;
191 #endif
193 int gExitCode = 0;
194 JSBool gQuitting = JS_FALSE;
195 FILE *gErrFile = NULL;
196 FILE *gOutFile = NULL;
197 #ifdef JS_THREADSAFE
198 JSObject *gWorkers = NULL;
199 js::workers::ThreadPool *gWorkerThreadPool = NULL;
200 #endif
202 static JSBool reportWarnings = JS_TRUE;
203 static JSBool compileOnly = JS_FALSE;
205 typedef enum JSShellErrNum {
206 #define MSG_DEF(name, number, count, exception, format) \
207 name = number,
208 #include "jsshell.msg"
209 #undef MSG_DEF
210 JSShellErr_Limit
211 #undef MSGDEF
212 } JSShellErrNum;
214 static JSContext *
215 NewContext(JSRuntime *rt);
217 static void
218 DestroyContext(JSContext *cx, bool withGC);
220 static const JSErrorFormatString *
221 my_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber);
223 static JSObject *
224 split_setup(JSContext *cx, JSBool evalcx);
226 #ifdef EDITLINE
227 JS_BEGIN_EXTERN_C
228 JS_EXTERN_API(char) *readline(const char *prompt);
229 JS_EXTERN_API(void) add_history(char *line);
230 JS_END_EXTERN_C
231 #endif
233 static void
234 ReportException(JSContext *cx)
236 if (JS_IsExceptionPending(cx)) {
237 if (!JS_ReportPendingException(cx))
238 JS_ClearPendingException(cx);
242 class ToString {
243 public:
244 ToString(JSContext *aCx, jsval v, JSBool aThrow = JS_FALSE)
245 : cx(aCx), mThrow(aThrow)
247 mStr = JS_ValueToString(cx, v);
248 if (!aThrow && !mStr)
249 ReportException(cx);
250 JS_AddNamedStringRoot(cx, &mStr, "Value ToString helper");
252 ~ToString() {
253 JS_RemoveStringRoot(cx, &mStr);
255 JSBool threw() { return !mStr; }
256 jsval getJSVal() { return STRING_TO_JSVAL(mStr); }
257 const char *getBytes() {
258 if (mStr && (mBytes.ptr() || mBytes.encode(cx, mStr)))
259 return mBytes.ptr();
260 return "(error converting value)";
262 private:
263 JSContext *cx;
264 JSString *mStr;
265 JSBool mThrow;
266 JSAutoByteString mBytes;
269 class IdToString : public ToString {
270 public:
271 IdToString(JSContext *cx, jsid id, JSBool aThrow = JS_FALSE)
272 : ToString(cx, IdToJsval(id), aThrow)
276 static char *
277 GetLine(FILE *file, const char * prompt)
279 size_t size;
280 char *buffer;
281 #ifdef EDITLINE
283 * Use readline only if file is stdin, because there's no way to specify
284 * another handle. Are other filehandles interactive?
286 if (file == stdin) {
287 char *linep = readline(prompt);
289 * We set it to zero to avoid complaining about inappropriate ioctl
290 * for device in the case of EOF. Looks like errno == 251 if line is
291 * finished with EOF and errno == 25 (EINVAL on Mac) if there is
292 * nothing left to read.
294 if (errno == 251 || errno == 25 || errno == EINVAL)
295 errno = 0;
296 if (!linep)
297 return NULL;
298 if (linep[0] != '\0')
299 add_history(linep);
300 return linep;
302 #endif
303 size_t len = 0;
304 if (*prompt != '\0') {
305 fprintf(gOutFile, "%s", prompt);
306 fflush(gOutFile);
308 size = 80;
309 buffer = (char *) malloc(size);
310 if (!buffer)
311 return NULL;
312 char *current = buffer;
313 while (fgets(current, size - len, file)) {
314 len += strlen(current);
315 char *t = buffer + len - 1;
316 if (*t == '\n') {
317 /* Line was read. We remove '\n' and exit. */
318 *t = '\0';
319 return buffer;
321 if (len + 1 == size) {
322 size = size * 2;
323 char *tmp = (char *) realloc(buffer, size);
324 if (!tmp) {
325 free(buffer);
326 return NULL;
328 buffer = tmp;
330 current = buffer + len;
332 if (len && !ferror(file))
333 return buffer;
334 free(buffer);
335 return NULL;
339 * State to store as JSContext private.
341 * We declare such timestamp as volatile as they are updated in the operation
342 * callback without taking any locks. Any possible race can only lead to more
343 * frequent callback calls. This is safe as the callback does everything based
344 * on timing.
346 struct JSShellContextData {
347 volatile JSIntervalTime startTime;
350 static JSShellContextData *
351 NewContextData()
353 /* Prevent creation of new contexts after we have been canceled. */
354 if (gCanceled)
355 return NULL;
357 JSShellContextData *data = (JSShellContextData *)
358 calloc(sizeof(JSShellContextData), 1);
359 if (!data)
360 return NULL;
361 data->startTime = js_IntervalNow();
362 return data;
365 static inline JSShellContextData *
366 GetContextData(JSContext *cx)
368 JSShellContextData *data = (JSShellContextData *) JS_GetContextPrivate(cx);
370 JS_ASSERT(data);
371 return data;
374 static JSBool
375 ShellOperationCallback(JSContext *cx)
377 if (!gCanceled)
378 return JS_TRUE;
380 JS_ClearPendingException(cx);
381 return JS_FALSE;
384 static void
385 SetContextOptions(JSContext *cx)
387 JS_SetNativeStackQuota(cx, gMaxStackSize);
388 JS_SetScriptStackQuota(cx, gScriptStackQuota);
389 JS_SetOperationCallback(cx, ShellOperationCallback);
392 #ifdef WINCE
393 int errno;
394 #endif
396 static void
397 Process(JSContext *cx, JSObject *obj, char *filename, JSBool forceTTY)
399 JSBool ok, hitEOF;
400 JSScript *script;
401 jsval result;
402 JSString *str;
403 char *buffer;
404 size_t size;
405 int lineno;
406 int startline;
407 FILE *file;
408 uint32 oldopts;
410 if (forceTTY || !filename || strcmp(filename, "-") == 0) {
411 file = stdin;
412 } else {
413 file = fopen(filename, "r");
414 if (!file) {
415 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
416 JSSMSG_CANT_OPEN, filename, strerror(errno));
417 gExitCode = EXITCODE_FILE_NOT_FOUND;
418 return;
422 SetContextOptions(cx);
424 #ifndef WINCE
425 /* windows mobile (and possibly other os's) does not have a TTY */
426 if (!forceTTY && !isatty(fileno(file)))
427 #endif
430 * It's not interactive - just execute it.
432 * Support the UNIX #! shell hack; gobble the first line if it starts
433 * with '#'. TODO - this isn't quite compatible with sharp variables,
434 * as a legal js program (using sharp variables) might start with '#'.
435 * But that would require multi-character lookahead.
437 int ch = fgetc(file);
438 if (ch == '#') {
439 while((ch = fgetc(file)) != EOF) {
440 if (ch == '\n' || ch == '\r')
441 break;
444 ungetc(ch, file);
446 int64 t1 = PRMJ_Now();
447 oldopts = JS_GetOptions(cx);
448 JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO | JSOPTION_NO_SCRIPT_RVAL);
449 script = JS_CompileFileHandle(cx, obj, filename, file);
450 JS_SetOptions(cx, oldopts);
451 if (script && !compileOnly) {
452 (void)JS_ExecuteScript(cx, obj, script, NULL);
453 int64 t2 = PRMJ_Now() - t1;
454 if (printTiming)
455 printf("runtime = %.3f ms\n", double(t2) / PRMJ_USEC_PER_MSEC);
458 goto cleanup;
461 /* It's an interactive filehandle; drop into read-eval-print loop. */
462 lineno = 1;
463 hitEOF = JS_FALSE;
464 buffer = NULL;
465 size = 0; /* assign here to avoid warnings */
466 do {
468 * Accumulate lines until we get a 'compilable unit' - one that either
469 * generates an error (before running out of source) or that compiles
470 * cleanly. This should be whenever we get a complete statement that
471 * coincides with the end of a line.
473 startline = lineno;
474 size_t len = 0; /* initialize to avoid warnings */
475 do {
476 ScheduleWatchdog(cx->runtime, -1);
477 gCanceled = false;
478 errno = 0;
480 char *line;
482 JSAutoSuspendRequest suspended(cx);
483 line = GetLine(file, startline == lineno ? "js> " : "");
485 if (!line) {
486 if (errno) {
487 JS_ReportError(cx, strerror(errno));
488 free(buffer);
489 goto cleanup;
491 hitEOF = JS_TRUE;
492 break;
494 if (!buffer) {
495 buffer = line;
496 len = strlen(buffer);
497 size = len + 1;
498 } else {
500 * len + 1 is required to store '\n' in the end of line.
502 size_t newlen = strlen(line) + (len ? len + 1 : 0);
503 if (newlen + 1 > size) {
504 size = newlen + 1 > size * 2 ? newlen + 1 : size * 2;
505 char *newBuf = (char *) realloc(buffer, size);
506 if (!newBuf) {
507 free(buffer);
508 free(line);
509 JS_ReportOutOfMemory(cx);
510 goto cleanup;
512 buffer = newBuf;
514 char *current = buffer + len;
515 if (startline != lineno)
516 *current++ = '\n';
517 strcpy(current, line);
518 len = newlen;
519 free(line);
521 lineno++;
522 if (!ScheduleWatchdog(cx->runtime, gTimeoutInterval)) {
523 hitEOF = JS_TRUE;
524 break;
526 } while (!JS_BufferIsCompilableUnit(cx, obj, buffer, len));
528 if (hitEOF && !buffer)
529 break;
531 /* Clear any pending exception from previous failed compiles. */
532 JS_ClearPendingException(cx);
534 /* Even though we're interactive, we have a compile-n-go opportunity. */
535 oldopts = JS_GetOptions(cx);
536 if (!compileOnly)
537 JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO);
538 script = JS_CompileScript(cx, obj, buffer, len, "typein",
539 startline);
540 if (!compileOnly)
541 JS_SetOptions(cx, oldopts);
543 if (script && !compileOnly) {
544 ok = JS_ExecuteScript(cx, obj, script, &result);
545 if (ok && !JSVAL_IS_VOID(result)) {
546 str = JS_ValueToSource(cx, result);
547 ok = !!str;
548 if (ok) {
549 JSAutoByteString bytes(cx, str);
550 ok = !!bytes;
551 if (ok)
552 fprintf(gOutFile, "%s\n", bytes.ptr());
556 *buffer = '\0';
557 } while (!hitEOF && !gQuitting);
559 free(buffer);
560 fprintf(gOutFile, "\n");
561 cleanup:
562 if (file != stdin)
563 fclose(file);
564 return;
567 static int
568 usage(void)
570 fprintf(gErrFile, "%s\n", JS_GetImplementationVersion());
571 fprintf(gErrFile, "usage: js [options] [scriptfile] [scriptarg...]\n"
572 "Options:\n"
573 " -h Display this information\n"
574 " -z Create a split global object\n"
575 " Warning: this option is probably not useful\n"
576 " -P Deeply freeze the global object prototype\n"
577 " -s Toggle JSOPTION_STRICT flag\n"
578 " -w Report strict warnings\n"
579 " -W Do not report strict warnings\n"
580 " -x Toggle JSOPTION_XML flag\n"
581 " -C Compile-only; do not execute\n"
582 " -i Enable interactive read-eval-print loop\n"
583 " -j Enable the TraceMonkey tracing JIT\n"
584 " -m Enable the JaegerMonkey method JIT\n"
585 " -a Always method JIT, ignore internal tuning\n"
586 " This only has effect with -m\n"
587 " -p Enable loop profiling for TraceMonkey\n"
588 " -d Enable debug mode\n"
589 " -b Print timing statistics\n"
590 " -t <timeout> Interrupt long-running execution after <timeout> seconds, where\n"
591 " <timeout> <= 1800.0. Negative values indicate no timeout (default).\n"
592 " -c <size> Suggest stack chunk size of <size> bytes. Default is 8192.\n"
593 " Warning: this option is currently ignored.\n"
594 " -o <option> Enable a context option flag by name\n"
595 " Possible values:\n"
596 " anonfunfix: JSOPTION_ANONFUNFIX\n"
597 " atline: JSOPTION_ATLINE\n"
598 " tracejit: JSOPTION_JIT\n"
599 " methodjit: JSOPTION_METHODJIT\n"
600 " relimit: JSOPTION_RELIMIT\n"
601 " strict: JSOPTION_STRICT\n"
602 " werror: JSOPTION_WERROR\n"
603 " xml: JSOPTION_XML\n"
604 " -v <version> Set the JavaScript language version\n"
605 " Possible values:\n"
606 " 150: JavaScript 1.5\n"
607 " 160: JavaScript 1.6\n"
608 " 170: JavaScript 1.7\n"
609 " 180: JavaScript 1.8\n"
610 " 185: JavaScript 1.8.5 (default)\n"
611 " -f <file> Load and execute JavaScript source <file>\n"
612 " Note: this option switches to non-interactive mode.\n"
613 " -e <source> Execute JavaScript <source>\n"
614 " Note: this option switches to non-interactive mode.\n"
615 " -S <size> Set the maximum size of the stack to <size> bytes\n"
616 " Default is %u.\n", DEFAULT_MAX_STACK_SIZE);
617 #ifdef JS_THREADSAFE
618 fprintf(gErrFile, " -g <n> Sleep for <n> seconds before starting (default: 0)\n");
619 #endif
620 #ifdef JS_GC_ZEAL
621 fprintf(gErrFile, " -Z <n> Toggle GC zeal: low if <n> is 0 (default), high if non-zero\n");
622 #endif
623 #ifdef MOZ_TRACEVIS
624 fprintf(gErrFile, " -T Start TraceVis\n");
625 #endif
626 return 2;
630 * JSContext option name to flag map. The option names are in alphabetical
631 * order for better reporting.
633 static const struct {
634 const char *name;
635 uint32 flag;
636 } js_options[] = {
637 {"anonfunfix", JSOPTION_ANONFUNFIX},
638 {"atline", JSOPTION_ATLINE},
639 {"jitprofiling", JSOPTION_PROFILING},
640 {"tracejit", JSOPTION_JIT},
641 {"methodjit", JSOPTION_METHODJIT},
642 {"methodjit_always",JSOPTION_METHODJIT_ALWAYS},
643 {"relimit", JSOPTION_RELIMIT},
644 {"strict", JSOPTION_STRICT},
645 {"werror", JSOPTION_WERROR},
646 {"xml", JSOPTION_XML},
649 static uint32
650 MapContextOptionNameToFlag(JSContext* cx, const char* name)
652 for (size_t i = 0; i != JS_ARRAY_LENGTH(js_options); ++i) {
653 if (strcmp(name, js_options[i].name) == 0)
654 return js_options[i].flag;
657 char* msg = JS_sprintf_append(NULL,
658 "unknown option name '%s'."
659 " The valid names are ", name);
660 for (size_t i = 0; i != JS_ARRAY_LENGTH(js_options); ++i) {
661 if (!msg)
662 break;
663 msg = JS_sprintf_append(msg, "%s%s", js_options[i].name,
664 (i + 2 < JS_ARRAY_LENGTH(js_options)
665 ? ", "
666 : i + 2 == JS_ARRAY_LENGTH(js_options)
667 ? " and "
668 : "."));
670 if (!msg) {
671 JS_ReportOutOfMemory(cx);
672 } else {
673 JS_ReportError(cx, msg);
674 free(msg);
676 return 0;
679 extern JSClass global_class;
681 #if defined(JS_TRACER) && defined(DEBUG)
682 namespace js {
683 extern struct JSClass jitstats_class;
684 void InitJITStatsClass(JSContext *cx, JSObject *glob);
686 #endif
688 static int
689 ProcessArgs(JSContext *cx, JSObject *obj, char **argv, int argc)
691 int i, j, length;
692 JSObject *argsObj;
693 char *filename = NULL;
694 JSBool isInteractive = JS_TRUE;
695 JSBool forceTTY = JS_FALSE;
698 * Scan past all optional arguments so we can create the arguments object
699 * before processing any -f options, which must interleave properly with
700 * -v and -w options. This requires two passes, and without getopt, we'll
701 * have to keep the option logic here and in the second for loop in sync.
703 for (i = 0; i < argc; i++) {
704 if (argv[i][0] != '-' || argv[i][1] == '\0') {
705 ++i;
706 break;
708 switch (argv[i][1]) {
709 case 'c':
710 case 'f':
711 case 'e':
712 case 'v':
713 case 'S':
714 case 't':
715 #ifdef JS_GC_ZEAL
716 case 'Z':
717 #endif
718 #ifdef MOZ_TRACEVIS
719 case 'T':
720 #endif
721 case 'g':
722 ++i;
723 break;
724 default:;
729 * Create arguments early and define it to root it, so it's safe from any
730 * GC calls nested below, and so it is available to -f <file> arguments.
732 argsObj = JS_NewArrayObject(cx, 0, NULL);
733 if (!argsObj)
734 return 1;
735 if (!JS_DefineProperty(cx, obj, "arguments", OBJECT_TO_JSVAL(argsObj),
736 NULL, NULL, 0)) {
737 return 1;
740 length = argc - i;
741 for (j = 0; j < length; j++) {
742 JSString *str = JS_NewStringCopyZ(cx, argv[i++]);
743 if (!str)
744 return 1;
745 if (!JS_DefineElement(cx, argsObj, j, STRING_TO_JSVAL(str),
746 NULL, NULL, JSPROP_ENUMERATE)) {
747 return 1;
751 for (i = 0; i < argc; i++) {
752 if (argv[i][0] != '-' || argv[i][1] == '\0') {
753 filename = argv[i++];
754 isInteractive = JS_FALSE;
755 break;
758 switch (argv[i][1]) {
759 case 'v':
760 if (++i == argc)
761 return usage();
763 JS_SetVersion(cx, (JSVersion) atoi(argv[i]));
764 break;
766 #ifdef JS_GC_ZEAL
767 case 'Z':
768 if (++i == argc)
769 return usage();
770 JS_SetGCZeal(cx, !!(atoi(argv[i])));
771 break;
772 #endif
774 case 'w':
775 reportWarnings = JS_TRUE;
776 break;
778 case 'W':
779 reportWarnings = JS_FALSE;
780 break;
782 case 's':
783 JS_ToggleOptions(cx, JSOPTION_STRICT);
784 break;
786 case 'E':
787 JS_ToggleOptions(cx, JSOPTION_RELIMIT);
788 break;
790 case 'x':
791 JS_ToggleOptions(cx, JSOPTION_XML);
792 break;
794 case 'b':
795 printTiming = true;
796 break;
798 case 'j':
799 enableTraceJit = !enableTraceJit;
800 JS_ToggleOptions(cx, JSOPTION_JIT);
801 #if defined(JS_TRACER) && defined(DEBUG)
802 js::InitJITStatsClass(cx, JS_GetGlobalObject(cx));
803 JS_DefineObject(cx, JS_GetGlobalObject(cx), "tracemonkey",
804 &js::jitstats_class, NULL, 0);
805 #endif
806 break;
808 case 'm':
809 enableMethodJit = !enableMethodJit;
810 JS_ToggleOptions(cx, JSOPTION_METHODJIT);
811 break;
813 case 'a':
814 JS_ToggleOptions(cx, JSOPTION_METHODJIT_ALWAYS);
815 break;
817 case 'p':
818 enableProfiling = !enableProfiling;
819 JS_ToggleOptions(cx, JSOPTION_PROFILING);
820 break;
822 case 'o':
824 if (++i == argc)
825 return usage();
827 uint32 flag = MapContextOptionNameToFlag(cx, argv[i]);
828 if (flag == 0)
829 return gExitCode;
830 JS_ToggleOptions(cx, flag);
831 break;
833 case 'P':
834 if (JS_GET_CLASS(cx, JS_GetPrototype(cx, obj)) != &global_class) {
835 JSObject *gobj;
837 if (!JS_DeepFreezeObject(cx, obj))
838 return JS_FALSE;
839 gobj = JS_NewGlobalObject(cx, &global_class);
840 if (!gobj)
841 return JS_FALSE;
842 if (!JS_SetPrototype(cx, gobj, obj))
843 return JS_FALSE;
844 JS_SetParent(cx, gobj, NULL);
845 JS_SetGlobalObject(cx, gobj);
846 obj = gobj;
848 break;
850 case 't':
851 if (++i == argc)
852 return usage();
854 if (!SetTimeoutValue(cx, atof(argv[i])))
855 return JS_FALSE;
857 break;
859 case 'c':
860 /* set stack chunk size */
861 gStackChunkSize = atoi(argv[++i]);
862 break;
864 case 'f':
865 if (++i == argc)
866 return usage();
868 Process(cx, obj, argv[i], JS_FALSE);
869 if (gExitCode != 0)
870 return gExitCode;
873 * XXX: js -f foo.js should interpret foo.js and then
874 * drop into interactive mode, but that breaks the test
875 * harness. Just execute foo.js for now.
877 isInteractive = JS_FALSE;
878 break;
880 case 'e':
882 jsval rval;
884 if (++i == argc)
885 return usage();
887 /* Pass a filename of -e to imitate PERL */
888 JS_EvaluateScript(cx, obj, argv[i], strlen(argv[i]),
889 "-e", 1, &rval);
891 isInteractive = JS_FALSE;
892 break;
895 case 'C':
896 compileOnly = JS_TRUE;
897 isInteractive = JS_FALSE;
898 break;
900 case 'i':
901 isInteractive = forceTTY = JS_TRUE;
902 break;
904 case 'S':
905 if (++i == argc)
906 return usage();
908 /* Set maximum stack size. */
909 gMaxStackSize = atoi(argv[i]);
910 break;
912 case 'd':
913 JS_SetDebugMode(cx, JS_TRUE);
914 break;
916 case 'z':
917 obj = split_setup(cx, JS_FALSE);
918 if (!obj)
919 return gExitCode;
920 break;
921 #ifdef MOZ_TRACEVIS
922 case 'T':
923 if (++i == argc)
924 return usage();
926 StartTraceVis(argv[i]);
927 break;
928 #endif
929 #ifdef JS_THREADSAFE
930 case 'g':
931 if (++i == argc)
932 return usage();
934 PR_Sleep(PR_SecondsToInterval(atoi(argv[i])));
935 break;
936 #endif
938 default:
939 return usage();
943 if (filename || isInteractive)
944 Process(cx, obj, filename, forceTTY);
945 return gExitCode;
948 static JSBool
949 Version(JSContext *cx, uintN argc, jsval *vp)
951 jsval *argv = JS_ARGV(cx, vp);
952 if (argc > 0 && JSVAL_IS_INT(argv[0]))
953 *vp = INT_TO_JSVAL(JS_SetVersion(cx, (JSVersion) JSVAL_TO_INT(argv[0])));
954 else
955 *vp = INT_TO_JSVAL(JS_GetVersion(cx));
956 return JS_TRUE;
959 static JSBool
960 RevertVersion(JSContext *cx, uintN argc, jsval *vp)
962 js_RevertVersion(cx);
963 JS_SET_RVAL(cx, vp, JSVAL_VOID);
964 return JS_TRUE;
967 static JSBool
968 Options(JSContext *cx, uintN argc, jsval *vp)
970 uint32 optset, flag;
971 JSString *str;
972 char *names;
973 JSBool found;
975 optset = 0;
976 jsval *argv = JS_ARGV(cx, vp);
977 for (uintN i = 0; i < argc; i++) {
978 str = JS_ValueToString(cx, argv[i]);
979 if (!str)
980 return JS_FALSE;
981 argv[i] = STRING_TO_JSVAL(str);
982 JSAutoByteString opt(cx, str);
983 if (!opt)
984 return JS_FALSE;
985 flag = MapContextOptionNameToFlag(cx, opt.ptr());
986 if (!flag)
987 return JS_FALSE;
988 optset |= flag;
990 optset = JS_ToggleOptions(cx, optset);
992 names = NULL;
993 found = JS_FALSE;
994 for (size_t i = 0; i != JS_ARRAY_LENGTH(js_options); i++) {
995 if (js_options[i].flag & optset) {
996 found = JS_TRUE;
997 names = JS_sprintf_append(names, "%s%s",
998 names ? "," : "", js_options[i].name);
999 if (!names)
1000 break;
1003 if (!found)
1004 names = strdup("");
1005 if (!names) {
1006 JS_ReportOutOfMemory(cx);
1007 return JS_FALSE;
1009 str = JS_NewStringCopyZ(cx, names);
1010 free(names);
1011 if (!str)
1012 return JS_FALSE;
1013 *vp = STRING_TO_JSVAL(str);
1014 return JS_TRUE;
1017 static JSBool
1018 Load(JSContext *cx, uintN argc, jsval *vp)
1020 uintN i;
1021 JSString *str;
1022 JSScript *script;
1023 uint32 oldopts;
1025 JSObject *thisobj = JS_THIS_OBJECT(cx, vp);
1026 if (!thisobj)
1027 return JS_FALSE;
1029 jsval *argv = JS_ARGV(cx, vp);
1030 for (i = 0; i < argc; i++) {
1031 str = JS_ValueToString(cx, argv[i]);
1032 if (!str)
1033 return JS_FALSE;
1034 argv[i] = STRING_TO_JSVAL(str);
1035 JSAutoByteString filename(cx, str);
1036 if (!filename)
1037 return JS_FALSE;
1038 errno = 0;
1039 oldopts = JS_GetOptions(cx);
1040 JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO | JSOPTION_NO_SCRIPT_RVAL);
1041 script = JS_CompileFile(cx, thisobj, filename.ptr());
1042 if (!script)
1043 return JS_FALSE;
1044 JS_SetOptions(cx, oldopts);
1045 if (!compileOnly && !JS_ExecuteScript(cx, thisobj, script, NULL))
1046 return JS_FALSE;
1049 return JS_TRUE;
1052 static JSBool
1053 Evaluate(JSContext *cx, uintN argc, jsval *vp)
1055 if (argc != 1 || !JSVAL_IS_STRING(JS_ARGV(cx, vp)[0])) {
1056 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
1057 (argc != 1) ? JSSMSG_NOT_ENOUGH_ARGS : JSSMSG_INVALID_ARGS,
1058 "evaluate");
1059 return false;
1062 JSString *code = JSVAL_TO_STRING(JS_ARGV(cx, vp)[0]);
1064 size_t codeLength;
1065 const jschar *codeChars = JS_GetStringCharsAndLength(cx, code, &codeLength);
1066 if (!codeChars)
1067 return false;
1069 JSObject *thisobj = JS_THIS_OBJECT(cx, vp);
1070 if (!thisobj)
1071 return false;
1073 if ((JS_GET_CLASS(cx, thisobj)->flags & JSCLASS_IS_GLOBAL) != JSCLASS_IS_GLOBAL) {
1074 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_UNEXPECTED_TYPE,
1075 "this-value passed to evaluate()", "not a global object");
1076 return false;
1079 JS_SET_RVAL(cx, vp, JSVAL_VOID);
1080 return JS_EvaluateUCScript(cx, thisobj, codeChars, codeLength, "@evaluate", 0, NULL);
1083 static JSString *
1084 FileAsString(JSContext *cx, const char *pathname)
1086 FILE *file;
1087 JSString *str = NULL;
1088 size_t len, cc;
1089 char *buf;
1091 file = fopen(pathname, "rb");
1092 if (!file) {
1093 JS_ReportError(cx, "can't open %s: %s", pathname, strerror(errno));
1094 return NULL;
1097 if (fseek(file, 0, SEEK_END) == EOF) {
1098 JS_ReportError(cx, "can't seek end of %s", pathname);
1099 } else {
1100 len = ftell(file);
1101 if (fseek(file, 0, SEEK_SET) == EOF) {
1102 JS_ReportError(cx, "can't seek start of %s", pathname);
1103 } else {
1104 buf = (char*) JS_malloc(cx, len + 1);
1105 if (buf) {
1106 cc = fread(buf, 1, len, file);
1107 if (cc != len) {
1108 JS_ReportError(cx, "can't read %s: %s", pathname,
1109 (ptrdiff_t(cc) < 0) ? strerror(errno) : "short read");
1110 } else {
1111 len = (size_t)cc;
1112 str = JS_NewStringCopyN(cx, buf, len);
1114 JS_free(cx, buf);
1118 fclose(file);
1120 return str;
1124 * Function to run scripts and return compilation + execution time. Semantics
1125 * are closely modelled after the equivalent function in WebKit, as this is used
1126 * to produce benchmark timings by SunSpider.
1128 static JSBool
1129 Run(JSContext *cx, uintN argc, jsval *vp)
1131 if (argc != 1) {
1132 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_INVALID_ARGS, "run");
1133 return false;
1136 JSObject *thisobj = JS_THIS_OBJECT(cx, vp);
1137 if (!thisobj)
1138 return false;
1140 jsval *argv = JS_ARGV(cx, vp);
1141 JSString *str = JS_ValueToString(cx, argv[0]);
1142 if (!str)
1143 return false;
1144 argv[0] = STRING_TO_JSVAL(str);
1145 JSAutoByteString filename(cx, str);
1146 if (!filename)
1147 return false;
1149 const jschar *ucbuf = NULL;
1150 size_t buflen;
1151 str = FileAsString(cx, filename.ptr());
1152 if (str)
1153 ucbuf = JS_GetStringCharsAndLength(cx, str, &buflen);
1154 if (!ucbuf)
1155 return false;
1157 JS::Anchor<JSString *> a_str(str);
1158 uint32 oldopts = JS_GetOptions(cx);
1159 JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO | JSOPTION_NO_SCRIPT_RVAL);
1161 int64 startClock = PRMJ_Now();
1162 JSScript *script = JS_CompileUCScript(cx, thisobj, ucbuf, buflen, filename.ptr(), 1);
1163 JS_SetOptions(cx, oldopts);
1164 if (!script)
1165 return false;
1167 JSBool ok = JS_ExecuteScript(cx, thisobj, script, NULL);
1168 int64 endClock = PRMJ_Now();
1169 JS_DestroyScript(cx, script);
1170 if (!ok)
1171 return false;
1173 JS_SET_RVAL(cx, vp, DOUBLE_TO_JSVAL((endClock - startClock) / double(PRMJ_USEC_PER_MSEC)));
1174 return true;
1178 * function readline()
1179 * Provides a hook for scripts to read a line from stdin.
1181 static JSBool
1182 ReadLine(JSContext *cx, uintN argc, jsval *vp)
1184 #define BUFSIZE 256
1185 FILE *from;
1186 char *buf, *tmp;
1187 size_t bufsize, buflength, gotlength;
1188 JSBool sawNewline;
1189 JSString *str;
1191 from = stdin;
1192 buflength = 0;
1193 bufsize = BUFSIZE;
1194 buf = (char *) JS_malloc(cx, bufsize);
1195 if (!buf)
1196 return JS_FALSE;
1198 sawNewline = JS_FALSE;
1199 while ((gotlength =
1200 js_fgets(buf + buflength, bufsize - buflength, from)) > 0) {
1201 buflength += gotlength;
1203 /* Are we done? */
1204 if (buf[buflength - 1] == '\n') {
1205 buf[buflength - 1] = '\0';
1206 sawNewline = JS_TRUE;
1207 break;
1208 } else if (buflength < bufsize - 1) {
1209 break;
1212 /* Else, grow our buffer for another pass. */
1213 bufsize *= 2;
1214 if (bufsize > buflength) {
1215 tmp = (char *) JS_realloc(cx, buf, bufsize);
1216 } else {
1217 JS_ReportOutOfMemory(cx);
1218 tmp = NULL;
1221 if (!tmp) {
1222 JS_free(cx, buf);
1223 return JS_FALSE;
1226 buf = tmp;
1229 /* Treat the empty string specially. */
1230 if (buflength == 0) {
1231 *vp = feof(from) ? JSVAL_NULL : JS_GetEmptyStringValue(cx);
1232 JS_free(cx, buf);
1233 return JS_TRUE;
1236 /* Shrink the buffer to the real size. */
1237 tmp = (char *) JS_realloc(cx, buf, buflength);
1238 if (!tmp) {
1239 JS_free(cx, buf);
1240 return JS_FALSE;
1243 buf = tmp;
1246 * Turn buf into a JSString. Note that buflength includes the trailing null
1247 * character.
1249 str = JS_NewStringCopyN(cx, buf, sawNewline ? buflength - 1 : buflength);
1250 JS_free(cx, buf);
1251 if (!str)
1252 return JS_FALSE;
1254 *vp = STRING_TO_JSVAL(str);
1255 return JS_TRUE;
1258 static JSBool
1259 PutStr(JSContext *cx, uintN argc, jsval *vp)
1261 jsval *argv;
1262 JSString *str;
1263 char *bytes;
1265 if (argc != 0) {
1266 argv = JS_ARGV(cx, vp);
1267 str = JS_ValueToString(cx, argv[0]);
1268 if (!str)
1269 return JS_FALSE;
1270 bytes = JS_EncodeString(cx, str);
1271 if (!bytes)
1272 return JS_FALSE;
1273 fputs(bytes, gOutFile);
1274 JS_free(cx, bytes);
1275 fflush(gOutFile);
1278 JS_SET_RVAL(cx, vp, JSVAL_VOID);
1279 return JS_TRUE;
1282 static JSBool
1283 Now(JSContext *cx, uintN argc, jsval *vp)
1285 jsdouble now = PRMJ_Now() / double(PRMJ_USEC_PER_MSEC);
1286 JS_SET_RVAL(cx, vp, DOUBLE_TO_JSVAL(now));
1287 return true;
1290 static JSBool
1291 Print(JSContext *cx, uintN argc, jsval *vp)
1293 jsval *argv;
1294 uintN i;
1295 JSString *str;
1296 char *bytes;
1298 argv = JS_ARGV(cx, vp);
1299 for (i = 0; i < argc; i++) {
1300 str = JS_ValueToString(cx, argv[i]);
1301 if (!str)
1302 return JS_FALSE;
1303 bytes = JS_EncodeString(cx, str);
1304 if (!bytes)
1305 return JS_FALSE;
1306 fprintf(gOutFile, "%s%s", i ? " " : "", bytes);
1307 JS_free(cx, bytes);
1310 fputc('\n', gOutFile);
1311 fflush(gOutFile);
1313 JS_SET_RVAL(cx, vp, JSVAL_VOID);
1314 return JS_TRUE;
1317 static JSBool
1318 Help(JSContext *cx, uintN argc, jsval *vp);
1320 static JSBool
1321 Quit(JSContext *cx, uintN argc, jsval *vp)
1323 JS_ConvertArguments(cx, argc, JS_ARGV(cx, vp), "/ i", &gExitCode);
1325 gQuitting = JS_TRUE;
1326 #ifdef JS_THREADSAFE
1327 if (gWorkerThreadPool)
1328 js::workers::terminateAll(JS_GetRuntime(cx), gWorkerThreadPool);
1329 #endif
1330 return JS_FALSE;
1333 static const char *
1334 ToSource(JSContext *cx, jsval *vp, JSAutoByteString *bytes)
1336 JSString *str = JS_ValueToSource(cx, *vp);
1337 if (str) {
1338 *vp = STRING_TO_JSVAL(str);
1339 if (bytes->encode(cx, str))
1340 return bytes->ptr();
1342 JS_ClearPendingException(cx);
1343 return "<<error converting value to string>>";
1346 static JSBool
1347 AssertEq(JSContext *cx, uintN argc, jsval *vp)
1349 if (!(argc == 2 || (argc == 3 && JSVAL_IS_STRING(JS_ARGV(cx, vp)[2])))) {
1350 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
1351 (argc < 2)
1352 ? JSSMSG_NOT_ENOUGH_ARGS
1353 : (argc == 3)
1354 ? JSSMSG_INVALID_ARGS
1355 : JSSMSG_TOO_MANY_ARGS,
1356 "assertEq");
1357 return JS_FALSE;
1360 jsval *argv = JS_ARGV(cx, vp);
1361 JSBool same;
1362 if (!JS_SameValue(cx, argv[0], argv[1], &same))
1363 return JS_FALSE;
1364 if (!same) {
1365 JSAutoByteString bytes0, bytes1;
1366 const char *actual = ToSource(cx, &argv[0], &bytes0);
1367 const char *expected = ToSource(cx, &argv[1], &bytes1);
1368 if (argc == 2) {
1369 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_ASSERT_EQ_FAILED,
1370 actual, expected);
1371 } else {
1372 JSAutoByteString bytes2(cx, JSVAL_TO_STRING(argv[2]));
1373 if (!bytes2)
1374 return JS_FALSE;
1375 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_ASSERT_EQ_FAILED_MSG,
1376 actual, expected, bytes2.ptr());
1378 return JS_FALSE;
1380 JS_SET_RVAL(cx, vp, JSVAL_VOID);
1381 return JS_TRUE;
1384 static JSBool
1385 AssertJit(JSContext *cx, uintN argc, jsval *vp)
1387 #ifdef JS_METHODJIT
1388 if (JS_GetOptions(cx) & JSOPTION_METHODJIT) {
1389 if (!cx->fp()->script()->getJIT(cx->fp()->isConstructing())) {
1390 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_ASSERT_JIT_FAILED);
1391 return JS_FALSE;
1394 #endif
1396 JS_SET_RVAL(cx, vp, JSVAL_VOID);
1397 return JS_TRUE;
1400 static JSBool
1401 GC(JSContext *cx, uintN argc, jsval *vp)
1403 size_t preBytes = cx->runtime->gcBytes;
1404 JS_GC(cx);
1406 char buf[256];
1407 JS_snprintf(buf, sizeof(buf), "before %lu, after %lu, break %08lx\n",
1408 (unsigned long)preBytes, (unsigned long)cx->runtime->gcBytes,
1409 #ifdef HAVE_SBRK
1410 (unsigned long)sbrk(0)
1411 #else
1413 #endif
1415 *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, buf));
1416 return true;
1419 #ifdef JS_GCMETER
1420 static JSBool
1421 GCStats(JSContext *cx, uintN argc, jsval *vp)
1423 js_DumpGCStats(cx->runtime, stdout);
1424 *vp = JSVAL_VOID;
1425 return true;
1427 #endif
1429 static JSBool
1430 GCParameter(JSContext *cx, uintN argc, jsval *vp)
1432 static const struct {
1433 const char *name;
1434 JSGCParamKey param;
1435 } paramMap[] = {
1436 {"maxBytes", JSGC_MAX_BYTES },
1437 {"maxMallocBytes", JSGC_MAX_MALLOC_BYTES},
1438 {"gcStackpoolLifespan", JSGC_STACKPOOL_LIFESPAN},
1439 {"gcBytes", JSGC_BYTES},
1440 {"gcNumber", JSGC_NUMBER},
1441 {"gcTriggerFactor", JSGC_TRIGGER_FACTOR},
1444 JSString *str;
1445 if (argc == 0) {
1446 str = JS_ValueToString(cx, JSVAL_VOID);
1447 JS_ASSERT(str);
1448 } else {
1449 str = JS_ValueToString(cx, vp[2]);
1450 if (!str)
1451 return JS_FALSE;
1452 vp[2] = STRING_TO_JSVAL(str);
1455 JSFlatString *flatStr = JS_FlattenString(cx, str);
1456 if (!flatStr)
1457 return JS_FALSE;
1459 size_t paramIndex = 0;
1460 for (;; paramIndex++) {
1461 if (paramIndex == JS_ARRAY_LENGTH(paramMap)) {
1462 JS_ReportError(cx,
1463 "the first argument argument must be maxBytes, "
1464 "maxMallocBytes, gcStackpoolLifespan, gcBytes, "
1465 "gcNumber or gcTriggerFactor");
1466 return JS_FALSE;
1468 if (JS_FlatStringEqualsAscii(flatStr, paramMap[paramIndex].name))
1469 break;
1471 JSGCParamKey param = paramMap[paramIndex].param;
1473 if (argc == 1) {
1474 uint32 value = JS_GetGCParameter(cx->runtime, param);
1475 return JS_NewNumberValue(cx, value, &vp[0]);
1478 if (param == JSGC_NUMBER ||
1479 param == JSGC_BYTES) {
1480 JS_ReportError(cx, "Attempt to change read-only parameter %s",
1481 paramMap[paramIndex].name);
1482 return JS_FALSE;
1485 uint32 value;
1486 if (!JS_ValueToECMAUint32(cx, vp[3], &value)) {
1487 JS_ReportError(cx,
1488 "the second argument must be convertable to uint32 "
1489 "with non-zero value");
1490 return JS_FALSE;
1492 if (param == JSGC_TRIGGER_FACTOR && value < 100) {
1493 JS_ReportError(cx,
1494 "the gcTriggerFactor value must be >= 100");
1495 return JS_FALSE;
1497 JS_SetGCParameter(cx->runtime, param, value);
1498 *vp = JSVAL_VOID;
1499 return JS_TRUE;
1502 #ifdef JS_GC_ZEAL
1503 static JSBool
1504 GCZeal(JSContext *cx, uintN argc, jsval *vp)
1506 uint32 zeal;
1508 if (!JS_ValueToECMAUint32(cx, argc == 0 ? JSVAL_VOID : vp[2], &zeal))
1509 return JS_FALSE;
1510 JS_SetGCZeal(cx, (uint8)zeal);
1511 *vp = JSVAL_VOID;
1512 return JS_TRUE;
1514 #endif /* JS_GC_ZEAL */
1516 typedef struct JSCountHeapNode JSCountHeapNode;
1518 struct JSCountHeapNode {
1519 void *thing;
1520 int32 kind;
1521 JSCountHeapNode *next;
1524 typedef struct JSCountHeapTracer {
1525 JSTracer base;
1526 JSDHashTable visited;
1527 JSBool ok;
1528 JSCountHeapNode *traceList;
1529 JSCountHeapNode *recycleList;
1530 } JSCountHeapTracer;
1532 static void
1533 CountHeapNotify(JSTracer *trc, void *thing, uint32 kind)
1535 JSCountHeapTracer *countTracer;
1536 JSDHashEntryStub *entry;
1537 JSCountHeapNode *node;
1539 JS_ASSERT(trc->callback == CountHeapNotify);
1540 countTracer = (JSCountHeapTracer *)trc;
1541 if (!countTracer->ok)
1542 return;
1544 entry = (JSDHashEntryStub *)
1545 JS_DHashTableOperate(&countTracer->visited, thing, JS_DHASH_ADD);
1546 if (!entry) {
1547 JS_ReportOutOfMemory(trc->context);
1548 countTracer->ok = JS_FALSE;
1549 return;
1551 if (entry->key)
1552 return;
1553 entry->key = thing;
1555 node = countTracer->recycleList;
1556 if (node) {
1557 countTracer->recycleList = node->next;
1558 } else {
1559 node = (JSCountHeapNode *) JS_malloc(trc->context, sizeof *node);
1560 if (!node) {
1561 countTracer->ok = JS_FALSE;
1562 return;
1565 node->thing = thing;
1566 node->kind = kind;
1567 node->next = countTracer->traceList;
1568 countTracer->traceList = node;
1571 static JSBool
1572 CountHeap(JSContext *cx, uintN argc, jsval *vp)
1574 void* startThing;
1575 int32 startTraceKind;
1576 jsval v;
1577 int32 traceKind, i;
1578 JSString *str;
1579 JSCountHeapTracer countTracer;
1580 JSCountHeapNode *node;
1581 size_t counter;
1583 static const struct {
1584 const char *name;
1585 int32 kind;
1586 } traceKindNames[] = {
1587 { "all", -1 },
1588 { "object", JSTRACE_OBJECT },
1589 { "string", JSTRACE_STRING },
1590 #if JS_HAS_XML_SUPPORT
1591 { "xml", JSTRACE_XML },
1592 #endif
1595 startThing = NULL;
1596 startTraceKind = 0;
1597 if (argc > 0) {
1598 v = JS_ARGV(cx, vp)[0];
1599 if (JSVAL_IS_TRACEABLE(v)) {
1600 startThing = JSVAL_TO_TRACEABLE(v);
1601 startTraceKind = JSVAL_TRACE_KIND(v);
1602 } else if (!JSVAL_IS_NULL(v)) {
1603 JS_ReportError(cx,
1604 "the first argument is not null or a heap-allocated "
1605 "thing");
1606 return JS_FALSE;
1610 traceKind = -1;
1611 if (argc > 1) {
1612 str = JS_ValueToString(cx, JS_ARGV(cx, vp)[1]);
1613 if (!str)
1614 return JS_FALSE;
1615 JSFlatString *flatStr = JS_FlattenString(cx, str);
1616 if (!flatStr)
1617 return JS_FALSE;
1618 for (i = 0; ;) {
1619 if (JS_FlatStringEqualsAscii(flatStr, traceKindNames[i].name)) {
1620 traceKind = traceKindNames[i].kind;
1621 break;
1623 if (++i == JS_ARRAY_LENGTH(traceKindNames)) {
1624 JSAutoByteString bytes(cx, str);
1625 if (!!bytes)
1626 JS_ReportError(cx, "trace kind name '%s' is unknown", bytes.ptr());
1627 return JS_FALSE;
1632 JS_TRACER_INIT(&countTracer.base, cx, CountHeapNotify);
1633 if (!JS_DHashTableInit(&countTracer.visited, JS_DHashGetStubOps(),
1634 NULL, sizeof(JSDHashEntryStub),
1635 JS_DHASH_DEFAULT_CAPACITY(100))) {
1636 JS_ReportOutOfMemory(cx);
1637 return JS_FALSE;
1639 countTracer.ok = JS_TRUE;
1640 countTracer.traceList = NULL;
1641 countTracer.recycleList = NULL;
1643 if (!startThing) {
1644 JS_TraceRuntime(&countTracer.base);
1645 } else {
1646 JS_SET_TRACING_NAME(&countTracer.base, "root");
1647 JS_CallTracer(&countTracer.base, startThing, startTraceKind);
1650 counter = 0;
1651 while ((node = countTracer.traceList) != NULL) {
1652 if (traceKind == -1 || node->kind == traceKind)
1653 counter++;
1654 countTracer.traceList = node->next;
1655 node->next = countTracer.recycleList;
1656 countTracer.recycleList = node;
1657 JS_TraceChildren(&countTracer.base, node->thing, node->kind);
1659 while ((node = countTracer.recycleList) != NULL) {
1660 countTracer.recycleList = node->next;
1661 JS_free(cx, node);
1663 JS_DHashTableFinish(&countTracer.visited);
1665 return countTracer.ok && JS_NewNumberValue(cx, (jsdouble) counter, vp);
1668 static jsrefcount finalizeCount = 0;
1670 static void
1671 finalize_counter_finalize(JSContext *cx, JSObject *obj)
1673 JS_ATOMIC_INCREMENT(&finalizeCount);
1676 static JSClass FinalizeCounterClass = {
1677 "FinalizeCounter", JSCLASS_IS_ANONYMOUS,
1678 JS_PropertyStub, /* addProperty */
1679 JS_PropertyStub, /* delProperty */
1680 JS_PropertyStub, /* getProperty */
1681 JS_StrictPropertyStub, /* setProperty */
1682 JS_EnumerateStub,
1683 JS_ResolveStub,
1684 JS_ConvertStub,
1685 finalize_counter_finalize
1688 static JSBool
1689 MakeFinalizeObserver(JSContext *cx, uintN argc, jsval *vp)
1691 JSObject *obj = JS_NewObjectWithGivenProto(cx, &FinalizeCounterClass, NULL,
1692 JS_GetGlobalObject(cx));
1693 if (!obj)
1694 return false;
1695 *vp = OBJECT_TO_JSVAL(obj);
1696 return true;
1699 static JSBool
1700 FinalizeCount(JSContext *cx, uintN argc, jsval *vp)
1702 *vp = INT_TO_JSVAL(finalizeCount);
1703 return true;
1706 static JSScript *
1707 ValueToScript(JSContext *cx, jsval v)
1709 JSScript *script = NULL;
1710 JSFunction *fun;
1712 if (!JSVAL_IS_PRIMITIVE(v)) {
1713 JSObject *obj = JSVAL_TO_OBJECT(v);
1714 JSClass *clasp = JS_GET_CLASS(cx, obj);
1716 if (clasp == Jsvalify(&js_ScriptClass)) {
1717 script = (JSScript *) JS_GetPrivate(cx, obj);
1718 } else if (clasp == Jsvalify(&js_GeneratorClass)) {
1719 JSGenerator *gen = (JSGenerator *) JS_GetPrivate(cx, obj);
1720 fun = gen->floatingFrame()->fun();
1721 script = FUN_SCRIPT(fun);
1725 if (!script) {
1726 fun = JS_ValueToFunction(cx, v);
1727 if (!fun)
1728 return NULL;
1729 script = FUN_SCRIPT(fun);
1730 if (!script) {
1731 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
1732 JSSMSG_SCRIPTS_ONLY);
1736 return script;
1739 static JSBool
1740 SetDebug(JSContext *cx, uintN argc, jsval *vp)
1742 jsval *argv = JS_ARGV(cx, vp);
1743 if (argc == 0 || !JSVAL_IS_BOOLEAN(argv[0])) {
1744 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
1745 JSSMSG_NOT_ENOUGH_ARGS, "setDebug");
1746 return JS_FALSE;
1750 * Debug mode can only be set when there is no JS code executing on the
1751 * stack. Unfortunately, that currently means that this call will fail
1752 * unless debug mode is already set to what you're trying to set it to.
1753 * In the future, this restriction may be lifted.
1756 JSBool rv = JS_SetDebugMode(cx, JSVAL_TO_BOOLEAN(argv[0]));
1757 JS_SET_RVAL(cx, vp, rv ? JSVAL_TRUE : JSVAL_FALSE);
1758 return JS_TRUE;
1761 static JSBool
1762 GetTrapArgs(JSContext *cx, uintN argc, jsval *argv, JSScript **scriptp,
1763 int32 *ip)
1765 jsval v;
1766 uintN intarg;
1767 JSScript *script;
1769 *scriptp = JS_GetScriptedCaller(cx, NULL)->script();
1770 *ip = 0;
1771 if (argc != 0) {
1772 v = argv[0];
1773 intarg = 0;
1774 if (!JSVAL_IS_PRIMITIVE(v) &&
1775 (JS_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == Jsvalify(&js_FunctionClass) ||
1776 JS_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == Jsvalify(&js_ScriptClass))) {
1777 script = ValueToScript(cx, v);
1778 if (!script)
1779 return JS_FALSE;
1780 *scriptp = script;
1781 intarg++;
1783 if (argc > intarg) {
1784 if (!JS_ValueToInt32(cx, argv[intarg], ip))
1785 return JS_FALSE;
1788 return JS_TRUE;
1791 static JSTrapStatus
1792 TrapHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval,
1793 jsval closure)
1795 JSString *str = JSVAL_TO_STRING(closure);
1796 JSStackFrame *caller = JS_GetScriptedCaller(cx, NULL);
1798 size_t length;
1799 const jschar *chars = JS_GetStringCharsAndLength(cx, str, &length);
1800 if (!chars)
1801 return JSTRAP_ERROR;
1803 if (!JS_EvaluateUCInStackFrame(cx, caller, chars, length,
1804 caller->script()->filename,
1805 caller->script()->lineno,
1806 rval)) {
1807 return JSTRAP_ERROR;
1809 if (!JSVAL_IS_VOID(*rval))
1810 return JSTRAP_RETURN;
1811 return JSTRAP_CONTINUE;
1814 static JSBool
1815 Trap(JSContext *cx, uintN argc, jsval *vp)
1817 JSString *str;
1818 JSScript *script;
1819 int32 i;
1821 jsval *argv = JS_ARGV(cx, vp);
1822 if (argc == 0) {
1823 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_TRAP_USAGE);
1824 return JS_FALSE;
1826 argc--;
1827 str = JS_ValueToString(cx, argv[argc]);
1828 if (!str)
1829 return JS_FALSE;
1830 argv[argc] = STRING_TO_JSVAL(str);
1831 if (!GetTrapArgs(cx, argc, argv, &script, &i))
1832 return JS_FALSE;
1833 JS_SET_RVAL(cx, vp, JSVAL_VOID);
1834 return JS_SetTrap(cx, script, script->code + i, TrapHandler, STRING_TO_JSVAL(str));
1837 static JSBool
1838 Untrap(JSContext *cx, uintN argc, jsval *vp)
1840 JSScript *script;
1841 int32 i;
1843 if (!GetTrapArgs(cx, argc, JS_ARGV(cx, vp), &script, &i))
1844 return JS_FALSE;
1845 JS_ClearTrap(cx, script, script->code + i, NULL, NULL);
1846 JS_SET_RVAL(cx, vp, JSVAL_VOID);
1847 return JS_TRUE;
1850 static JSTrapStatus
1851 DebuggerAndThrowHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval,
1852 void *closure)
1854 return TrapHandler(cx, script, pc, rval, STRING_TO_JSVAL((JSString *)closure));
1857 static JSBool
1858 SetDebuggerHandler(JSContext *cx, uintN argc, jsval *vp)
1860 JSString *str;
1861 if (argc == 0) {
1862 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
1863 JSSMSG_NOT_ENOUGH_ARGS, "setDebuggerHandler");
1864 return JS_FALSE;
1867 str = JS_ValueToString(cx, JS_ARGV(cx, vp)[0]);
1868 if (!str)
1869 return JS_FALSE;
1871 JS_SetDebuggerHandler(cx->runtime, DebuggerAndThrowHandler, str);
1872 JS_SET_RVAL(cx, vp, JSVAL_VOID);
1873 return JS_TRUE;
1876 static JSBool
1877 SetThrowHook(JSContext *cx, uintN argc, jsval *vp)
1879 JSString *str;
1880 if (argc == 0) {
1881 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
1882 JSSMSG_NOT_ENOUGH_ARGS, "setThrowHook");
1883 return JS_FALSE;
1886 str = JS_ValueToString(cx, JS_ARGV(cx, vp)[0]);
1887 if (!str)
1888 return JS_FALSE;
1890 JS_SetThrowHook(cx->runtime, DebuggerAndThrowHandler, str);
1891 JS_SET_RVAL(cx, vp, JSVAL_VOID);
1892 return JS_TRUE;
1895 static JSBool
1896 LineToPC(JSContext *cx, uintN argc, jsval *vp)
1898 JSScript *script;
1899 int32 i;
1900 uintN lineno;
1901 jsbytecode *pc;
1903 if (argc == 0) {
1904 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_LINE2PC_USAGE);
1905 return JS_FALSE;
1907 script = JS_GetScriptedCaller(cx, NULL)->script();
1908 if (!GetTrapArgs(cx, argc, JS_ARGV(cx, vp), &script, &i))
1909 return JS_FALSE;
1910 lineno = (i == 0) ? script->lineno : (uintN)i;
1911 pc = JS_LineNumberToPC(cx, script, lineno);
1912 if (!pc)
1913 return JS_FALSE;
1914 *vp = INT_TO_JSVAL(pc - script->code);
1915 return JS_TRUE;
1918 static JSBool
1919 PCToLine(JSContext *cx, uintN argc, jsval *vp)
1921 JSScript *script;
1922 int32 i;
1923 uintN lineno;
1925 if (!GetTrapArgs(cx, argc, JS_ARGV(cx, vp), &script, &i))
1926 return JS_FALSE;
1927 lineno = JS_PCToLineNumber(cx, script, script->code + i);
1928 if (!lineno)
1929 return JS_FALSE;
1930 *vp = INT_TO_JSVAL(lineno);
1931 return JS_TRUE;
1934 #ifdef DEBUG
1936 static void
1937 UpdateSwitchTableBounds(JSContext *cx, JSScript *script, uintN offset,
1938 uintN *start, uintN *end)
1940 jsbytecode *pc;
1941 JSOp op;
1942 ptrdiff_t jmplen;
1943 jsint low, high, n;
1945 pc = script->code + offset;
1946 op = js_GetOpcode(cx, script, pc);
1947 switch (op) {
1948 case JSOP_TABLESWITCHX:
1949 jmplen = JUMPX_OFFSET_LEN;
1950 goto jump_table;
1951 case JSOP_TABLESWITCH:
1952 jmplen = JUMP_OFFSET_LEN;
1953 jump_table:
1954 pc += jmplen;
1955 low = GET_JUMP_OFFSET(pc);
1956 pc += JUMP_OFFSET_LEN;
1957 high = GET_JUMP_OFFSET(pc);
1958 pc += JUMP_OFFSET_LEN;
1959 n = high - low + 1;
1960 break;
1962 case JSOP_LOOKUPSWITCHX:
1963 jmplen = JUMPX_OFFSET_LEN;
1964 goto lookup_table;
1965 case JSOP_LOOKUPSWITCH:
1966 jmplen = JUMP_OFFSET_LEN;
1967 lookup_table:
1968 pc += jmplen;
1969 n = GET_INDEX(pc);
1970 pc += INDEX_LEN;
1971 jmplen += JUMP_OFFSET_LEN;
1972 break;
1974 default:
1975 /* [condswitch] switch does not have any jump or lookup tables. */
1976 JS_ASSERT(op == JSOP_CONDSWITCH);
1977 return;
1980 *start = (uintN)(pc - script->code);
1981 *end = *start + (uintN)(n * jmplen);
1984 static void
1985 SrcNotes(JSContext *cx, JSScript *script)
1987 uintN offset, delta, caseOff, switchTableStart, switchTableEnd;
1988 jssrcnote *notes, *sn;
1989 JSSrcNoteType type;
1990 const char *name;
1991 uint32 index;
1992 JSAtom *atom;
1993 JSString *str;
1995 fprintf(gOutFile, "\nSource notes:\n");
1996 offset = 0;
1997 notes = script->notes();
1998 switchTableEnd = switchTableStart = 0;
1999 for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
2000 delta = SN_DELTA(sn);
2001 offset += delta;
2002 type = (JSSrcNoteType) SN_TYPE(sn);
2003 name = js_SrcNoteSpec[type].name;
2004 if (type == SRC_LABEL) {
2005 /* Check if the source note is for a switch case. */
2006 if (switchTableStart <= offset && offset < switchTableEnd) {
2007 name = "case";
2008 } else {
2009 JS_ASSERT(js_GetOpcode(cx, script, script->code + offset) == JSOP_NOP);
2012 fprintf(gOutFile, "%3u: %5u [%4u] %-8s",
2013 (uintN) (sn - notes), offset, delta, name);
2014 switch (type) {
2015 case SRC_SETLINE:
2016 fprintf(gOutFile, " lineno %u", (uintN) js_GetSrcNoteOffset(sn, 0));
2017 break;
2018 case SRC_FOR:
2019 fprintf(gOutFile, " cond %u update %u tail %u",
2020 (uintN) js_GetSrcNoteOffset(sn, 0),
2021 (uintN) js_GetSrcNoteOffset(sn, 1),
2022 (uintN) js_GetSrcNoteOffset(sn, 2));
2023 break;
2024 case SRC_IF_ELSE:
2025 fprintf(gOutFile, " else %u elseif %u",
2026 (uintN) js_GetSrcNoteOffset(sn, 0),
2027 (uintN) js_GetSrcNoteOffset(sn, 1));
2028 break;
2029 case SRC_COND:
2030 case SRC_WHILE:
2031 case SRC_PCBASE:
2032 case SRC_PCDELTA:
2033 case SRC_DECL:
2034 case SRC_BRACE:
2035 fprintf(gOutFile, " offset %u", (uintN) js_GetSrcNoteOffset(sn, 0));
2036 break;
2037 case SRC_LABEL:
2038 case SRC_LABELBRACE:
2039 case SRC_BREAK2LABEL:
2040 case SRC_CONT2LABEL:
2041 index = js_GetSrcNoteOffset(sn, 0);
2042 JS_GET_SCRIPT_ATOM(script, NULL, index, atom);
2043 str = ATOM_TO_STRING(atom);
2044 fprintf(gOutFile, " atom %u (", index);
2045 JS_FileEscapedString(gOutFile, str, 0);
2046 putc(')', gOutFile);
2047 break;
2048 case SRC_FUNCDEF: {
2049 index = js_GetSrcNoteOffset(sn, 0);
2050 JSObject *obj = script->getObject(index);
2051 JSFunction *fun = (JSFunction *) JS_GetPrivate(cx, obj);
2052 str = JS_DecompileFunction(cx, fun, JS_DONT_PRETTY_PRINT);
2053 JSAutoByteString bytes;
2054 if (!str || !bytes.encode(cx, str))
2055 ReportException(cx);
2056 fprintf(gOutFile, " function %u (%s)", index, !!bytes ? bytes.ptr() : "N/A");
2057 break;
2059 case SRC_SWITCH:
2060 fprintf(gOutFile, " length %u", (uintN) js_GetSrcNoteOffset(sn, 0));
2061 caseOff = (uintN) js_GetSrcNoteOffset(sn, 1);
2062 if (caseOff)
2063 fprintf(gOutFile, " first case offset %u", caseOff);
2064 UpdateSwitchTableBounds(cx, script, offset,
2065 &switchTableStart, &switchTableEnd);
2066 break;
2067 case SRC_CATCH:
2068 delta = (uintN) js_GetSrcNoteOffset(sn, 0);
2069 if (delta) {
2070 if (script->main[offset] == JSOP_LEAVEBLOCK)
2071 fprintf(gOutFile, " stack depth %u", delta);
2072 else
2073 fprintf(gOutFile, " guard delta %u", delta);
2075 break;
2076 default:;
2078 fputc('\n', gOutFile);
2082 static JSBool
2083 Notes(JSContext *cx, uintN argc, jsval *vp)
2085 uintN i;
2086 JSScript *script;
2088 jsval *argv = JS_ARGV(cx, vp);
2089 for (i = 0; i < argc; i++) {
2090 script = ValueToScript(cx, argv[i]);
2091 if (!script)
2092 continue;
2094 SrcNotes(cx, script);
2096 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2097 return JS_TRUE;
2100 JS_STATIC_ASSERT(JSTRY_CATCH == 0);
2101 JS_STATIC_ASSERT(JSTRY_FINALLY == 1);
2102 JS_STATIC_ASSERT(JSTRY_ITER == 2);
2104 static const char* const TryNoteNames[] = { "catch", "finally", "iter" };
2106 static JSBool
2107 TryNotes(JSContext *cx, JSScript *script)
2109 JSTryNote *tn, *tnlimit;
2111 if (!JSScript::isValidOffset(script->trynotesOffset))
2112 return JS_TRUE;
2114 tn = script->trynotes()->vector;
2115 tnlimit = tn + script->trynotes()->length;
2116 fprintf(gOutFile, "\nException table:\n"
2117 "kind stack start end\n");
2118 do {
2119 JS_ASSERT(tn->kind < JS_ARRAY_LENGTH(TryNoteNames));
2120 fprintf(gOutFile, " %-7s %6u %8u %8u\n",
2121 TryNoteNames[tn->kind], tn->stackDepth,
2122 tn->start, tn->start + tn->length);
2123 } while (++tn != tnlimit);
2124 return JS_TRUE;
2127 static bool
2128 DisassembleValue(JSContext *cx, jsval v, bool lines, bool recursive)
2130 JSScript *script = ValueToScript(cx, v);
2131 if (!script)
2132 return false;
2133 if (VALUE_IS_FUNCTION(cx, v)) {
2134 JSFunction *fun = JS_ValueToFunction(cx, v);
2135 if (fun && (fun->flags & ~7U)) {
2136 uint16 flags = fun->flags;
2137 fputs("flags:", stdout);
2139 #define SHOW_FLAG(flag) if (flags & JSFUN_##flag) fputs(" " #flag, stdout);
2141 SHOW_FLAG(LAMBDA);
2142 SHOW_FLAG(HEAVYWEIGHT);
2143 SHOW_FLAG(EXPR_CLOSURE);
2144 SHOW_FLAG(TRCINFO);
2146 #undef SHOW_FLAG
2148 if (FUN_INTERPRETED(fun)) {
2149 if (FUN_NULL_CLOSURE(fun))
2150 fputs(" NULL_CLOSURE", stdout);
2151 else if (FUN_FLAT_CLOSURE(fun))
2152 fputs(" FLAT_CLOSURE", stdout);
2154 JSScript *script = fun->script();
2155 if (script->bindings.hasUpvars()) {
2156 fputs("\nupvars: {\n", stdout);
2158 void *mark = JS_ARENA_MARK(&cx->tempPool);
2159 jsuword *localNames = script->bindings.getLocalNameArray(cx, &cx->tempPool);
2160 if (!localNames)
2161 return false;
2163 JSUpvarArray *uva = script->upvars();
2164 uintN upvar_base = script->bindings.countArgsAndVars();
2166 for (uint32 i = 0, n = uva->length; i < n; i++) {
2167 JSAtom *atom = JS_LOCAL_NAME_TO_ATOM(localNames[upvar_base + i]);
2168 UpvarCookie cookie = uva->vector[i];
2169 JSAutoByteString printable;
2170 if (js_AtomToPrintableString(cx, atom, &printable)) {
2171 printf(" %s: {skip:%u, slot:%u},\n",
2172 printable.ptr(), cookie.level(), cookie.slot());
2176 JS_ARENA_RELEASE(&cx->tempPool, mark);
2177 putchar('}');
2180 putchar('\n');
2184 if (!js_Disassemble(cx, script, lines, stdout))
2185 return false;
2186 SrcNotes(cx, script);
2187 TryNotes(cx, script);
2189 if (recursive && JSScript::isValidOffset(script->objectsOffset)) {
2190 JSObjectArray *objects = script->objects();
2191 for (uintN i = 0; i != objects->length; ++i) {
2192 JSObject *obj = objects->vector[i];
2193 if (obj->isFunction()) {
2194 putchar('\n');
2195 if (!DisassembleValue(cx, OBJECT_TO_JSVAL(obj),
2196 lines, recursive)) {
2197 return false;
2202 return true;
2205 static JSBool
2206 Disassemble(JSContext *cx, uintN argc, jsval *vp)
2208 jsval *argv = JS_ARGV(cx, vp);
2210 /* Read options off early arguments */
2211 bool lines = false, recursive = false;
2212 while (argc > 0 && JSVAL_IS_STRING(argv[0])) {
2213 JSString *str = JSVAL_TO_STRING(argv[0]);
2214 JSFlatString *flatStr = JS_FlattenString(cx, str);
2215 if (!flatStr)
2216 return JS_FALSE;
2217 lines |= !!JS_FlatStringEqualsAscii(flatStr, "-l");
2218 recursive |= !!JS_FlatStringEqualsAscii(flatStr, "-r");
2219 if (!lines && !recursive)
2220 break;
2221 argv++, argc--;
2224 if (argc == 0) {
2225 /* Without arguments, disassemble the current script. */
2226 if (JSStackFrame *frame = JS_GetScriptedCaller(cx, NULL)) {
2227 JSScript *script = JS_GetFrameScript(cx, frame);
2228 if (!js_Disassemble(cx, script, lines, stdout))
2229 return false;
2230 SrcNotes(cx, script);
2231 TryNotes(cx, script);
2233 } else {
2234 for (uintN i = 0; i < argc; i++) {
2235 if (!DisassembleValue(cx, argv[i], lines, recursive))
2236 return false;
2240 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2241 return true;
2244 static JSBool
2245 DisassFile(JSContext *cx, uintN argc, jsval *vp)
2247 jsval *argv = JS_ARGV(cx, vp);
2249 if (!argc) {
2250 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2251 return JS_TRUE;
2254 /* Support extra options at the start, just like Dissassemble. */
2255 uintN _argc = argc;
2256 argv += argc-1;
2257 argc = 1;
2260 JSObject *thisobj = JS_THIS_OBJECT(cx, vp);
2261 if (!thisobj)
2262 return JS_FALSE;
2264 JSString *str = JS_ValueToString(cx, argv[0]);
2265 if (!str)
2266 return JS_FALSE;
2267 JSAutoByteString filename(cx, str);
2268 if (!filename)
2269 return JS_FALSE;
2271 uint32 oldopts = JS_GetOptions(cx);
2272 JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO | JSOPTION_NO_SCRIPT_RVAL);
2273 JSScript *script = JS_CompileFile(cx, thisobj, filename.ptr());
2274 JS_SetOptions(cx, oldopts);
2275 if (!script)
2276 return JS_FALSE;
2278 JSObject *obj = JS_NewScriptObject(cx, script);
2279 if (!obj)
2280 return JS_FALSE;
2282 argv[0] = OBJECT_TO_JSVAL(obj); /* I like to root it, root it. */
2283 JSBool ok = Disassemble(cx, _argc, vp); /* gross, but works! */
2284 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2285 return ok;
2288 static JSBool
2289 DisassWithSrc(JSContext *cx, uintN argc, jsval *vp)
2291 #define LINE_BUF_LEN 512
2292 uintN i, len, line1, line2, bupline;
2293 JSScript *script;
2294 FILE *file;
2295 char linebuf[LINE_BUF_LEN];
2296 jsbytecode *pc, *end;
2297 JSBool ok;
2298 static char sep[] = ";-------------------------";
2300 ok = JS_TRUE;
2301 jsval *argv = JS_ARGV(cx, vp);
2302 for (i = 0; ok && i < argc; i++) {
2303 script = ValueToScript(cx, argv[i]);
2304 if (!script)
2305 return JS_FALSE;
2307 if (!script->filename) {
2308 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
2309 JSSMSG_FILE_SCRIPTS_ONLY);
2310 return JS_FALSE;
2313 file = fopen(script->filename, "r");
2314 if (!file) {
2315 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
2316 JSSMSG_CANT_OPEN, script->filename,
2317 strerror(errno));
2318 return JS_FALSE;
2321 pc = script->code;
2322 end = pc + script->length;
2324 /* burn the leading lines */
2325 line2 = JS_PCToLineNumber(cx, script, pc);
2326 for (line1 = 0; line1 < line2 - 1; line1++) {
2327 char *tmp = fgets(linebuf, LINE_BUF_LEN, file);
2328 if (!tmp) {
2329 JS_ReportError(cx, "failed to read %s fully",
2330 script->filename);
2331 ok = JS_FALSE;
2332 goto bail;
2336 bupline = 0;
2337 while (pc < end) {
2338 line2 = JS_PCToLineNumber(cx, script, pc);
2340 if (line2 < line1) {
2341 if (bupline != line2) {
2342 bupline = line2;
2343 fprintf(gOutFile, "%s %3u: BACKUP\n", sep, line2);
2345 } else {
2346 if (bupline && line1 == line2)
2347 fprintf(gOutFile, "%s %3u: RESTORE\n", sep, line2);
2348 bupline = 0;
2349 while (line1 < line2) {
2350 if (!fgets(linebuf, LINE_BUF_LEN, file)) {
2351 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
2352 JSSMSG_UNEXPECTED_EOF,
2353 script->filename);
2354 ok = JS_FALSE;
2355 goto bail;
2357 line1++;
2358 fprintf(gOutFile, "%s %3u: %s", sep, line1, linebuf);
2362 len = js_Disassemble1(cx, script, pc,
2363 pc - script->code,
2364 JS_TRUE, stdout);
2365 if (!len) {
2366 ok = JS_FALSE;
2367 goto bail;
2369 pc += len;
2372 bail:
2373 fclose(file);
2375 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2376 return ok;
2377 #undef LINE_BUF_LEN
2380 static JSBool
2381 Tracing(JSContext *cx, uintN argc, jsval *vp)
2383 FILE *file;
2385 if (argc == 0) {
2386 *vp = BOOLEAN_TO_JSVAL(cx->logfp != 0);
2387 return JS_TRUE;
2390 jsval *argv = JS_ARGV(cx, vp);
2391 switch (JS_TypeOfValue(cx, argv[0])) {
2392 case JSTYPE_NUMBER:
2393 case JSTYPE_BOOLEAN: {
2394 JSBool bval;
2395 JS_ValueToBoolean(cx, argv[0], &bval);
2396 file = bval ? stderr : NULL;
2397 break;
2399 case JSTYPE_STRING: {
2400 JSAutoByteString name(cx, JSVAL_TO_STRING(argv[0]));
2401 if (!name)
2402 return JS_FALSE;
2403 file = fopen(name.ptr(), "w");
2404 if (!file) {
2405 JS_ReportError(cx, "tracing: couldn't open output file %s: %s",
2406 name.ptr(), strerror(errno));
2407 return JS_FALSE;
2409 break;
2411 default:
2412 goto bad_argument;
2414 if (cx->logfp && cx->logfp != stderr)
2415 fclose((FILE *)cx->logfp);
2416 cx->logfp = file;
2417 cx->logPrevPc = NULL;
2418 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2419 return JS_TRUE;
2421 bad_argument:
2422 JSString *str = JS_ValueToString(cx, argv[0]);
2423 if (!str)
2424 return JS_FALSE;
2425 JSAutoByteString bytes(cx, str);
2426 if (!bytes)
2427 return JS_FALSE;
2428 JS_ReportError(cx, "tracing: illegal argument %s", bytes.ptr());
2429 return JS_FALSE;
2432 static void
2433 DumpScope(JSContext *cx, JSObject *obj, FILE *fp)
2435 uintN i = 0;
2436 for (JSScopeProperty *sprop = NULL; JS_PropertyIterator(obj, &sprop);) {
2437 fprintf(fp, "%3u %p ", i++, (void *) sprop);
2438 ((Shape *) sprop)->dump(cx, fp);
2442 static JSBool
2443 DumpStats(JSContext *cx, uintN argc, jsval *vp)
2445 uintN i;
2446 JSString *str;
2447 jsid id;
2448 JSObject *obj2;
2449 JSProperty *prop;
2450 Value value;
2452 jsval *argv = JS_ARGV(cx, vp);
2453 for (i = 0; i < argc; i++) {
2454 str = JS_ValueToString(cx, argv[i]);
2455 if (!str)
2456 return JS_FALSE;
2457 argv[i] = STRING_TO_JSVAL(str);
2458 JSFlatString *flatStr = JS_FlattenString(cx, str);
2459 if (!flatStr)
2460 return JS_FALSE;
2461 if (JS_FlatStringEqualsAscii(flatStr, "arena")) {
2462 #ifdef JS_ARENAMETER
2463 JS_DumpArenaStats(stdout);
2464 #endif
2465 } else if (JS_FlatStringEqualsAscii(flatStr, "atom")) {
2466 js_DumpAtoms(cx, gOutFile);
2467 } else if (JS_FlatStringEqualsAscii(flatStr, "global")) {
2468 DumpScope(cx, cx->globalObject, stdout);
2469 } else {
2470 if (!JS_ValueToId(cx, STRING_TO_JSVAL(str), &id))
2471 return JS_FALSE;
2472 JSObject *obj;
2473 if (!js_FindProperty(cx, id, &obj, &obj2, &prop))
2474 return JS_FALSE;
2475 if (prop) {
2476 if (!obj->getProperty(cx, id, &value))
2477 return JS_FALSE;
2479 if (!prop || !value.isObjectOrNull()) {
2480 fputs("js: invalid stats argument ", gErrFile);
2481 JS_FileEscapedString(gErrFile, str, 0);
2482 putc('\n', gErrFile);
2483 continue;
2485 obj = value.toObjectOrNull();
2486 if (obj)
2487 DumpScope(cx, obj, stdout);
2490 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2491 return JS_TRUE;
2494 static JSBool
2495 DumpHeap(JSContext *cx, uintN argc, jsval *vp)
2497 jsval v;
2498 void* startThing;
2499 uint32 startTraceKind;
2500 const char *badTraceArg;
2501 void *thingToFind;
2502 size_t maxDepth;
2503 void *thingToIgnore;
2504 FILE *dumpFile;
2505 JSBool ok;
2507 const char *fileName = NULL;
2508 JSAutoByteString fileNameBytes;
2509 if (argc > 0) {
2510 v = JS_ARGV(cx, vp)[0];
2511 if (!JSVAL_IS_NULL(v)) {
2512 JSString *str;
2514 str = JS_ValueToString(cx, v);
2515 if (!str)
2516 return JS_FALSE;
2517 JS_ARGV(cx, vp)[0] = STRING_TO_JSVAL(str);
2518 if (!fileNameBytes.encode(cx, str))
2519 return JS_FALSE;
2520 fileName = fileNameBytes.ptr();
2524 startThing = NULL;
2525 startTraceKind = 0;
2526 if (argc > 1) {
2527 v = JS_ARGV(cx, vp)[1];
2528 if (JSVAL_IS_TRACEABLE(v)) {
2529 startThing = JSVAL_TO_TRACEABLE(v);
2530 startTraceKind = JSVAL_TRACE_KIND(v);
2531 } else if (!JSVAL_IS_NULL(v)) {
2532 badTraceArg = "start";
2533 goto not_traceable_arg;
2537 thingToFind = NULL;
2538 if (argc > 2) {
2539 v = JS_ARGV(cx, vp)[2];
2540 if (JSVAL_IS_TRACEABLE(v)) {
2541 thingToFind = JSVAL_TO_TRACEABLE(v);
2542 } else if (!JSVAL_IS_NULL(v)) {
2543 badTraceArg = "toFind";
2544 goto not_traceable_arg;
2548 maxDepth = (size_t)-1;
2549 if (argc > 3) {
2550 v = JS_ARGV(cx, vp)[3];
2551 if (!JSVAL_IS_NULL(v)) {
2552 uint32 depth;
2554 if (!JS_ValueToECMAUint32(cx, v, &depth))
2555 return JS_FALSE;
2556 maxDepth = depth;
2560 thingToIgnore = NULL;
2561 if (argc > 4) {
2562 v = JS_ARGV(cx, vp)[4];
2563 if (JSVAL_IS_TRACEABLE(v)) {
2564 thingToIgnore = JSVAL_TO_TRACEABLE(v);
2565 } else if (!JSVAL_IS_NULL(v)) {
2566 badTraceArg = "toIgnore";
2567 goto not_traceable_arg;
2571 if (!fileName) {
2572 dumpFile = stdout;
2573 } else {
2574 dumpFile = fopen(fileName, "w");
2575 if (!dumpFile) {
2576 JS_ReportError(cx, "can't open %s: %s", fileName, strerror(errno));
2577 return JS_FALSE;
2581 ok = JS_DumpHeap(cx, dumpFile, startThing, startTraceKind, thingToFind,
2582 maxDepth, thingToIgnore);
2583 if (dumpFile != stdout)
2584 fclose(dumpFile);
2585 return ok;
2587 not_traceable_arg:
2588 JS_ReportError(cx, "argument '%s' is not null or a heap-allocated thing",
2589 badTraceArg);
2590 return JS_FALSE;
2593 JSBool
2594 DumpObject(JSContext *cx, uintN argc, jsval *vp)
2596 JSObject *arg0 = NULL;
2597 if (!JS_ConvertArguments(cx, argc, JS_ARGV(cx, vp), "o", &arg0))
2598 return JS_FALSE;
2600 js_DumpObject(arg0);
2602 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2603 return JS_TRUE;
2606 #endif /* DEBUG */
2608 #ifdef TEST_CVTARGS
2609 #include <ctype.h>
2611 static const char *
2612 EscapeWideString(jschar *w)
2614 static char enuf[80];
2615 static char hex[] = "0123456789abcdef";
2616 jschar u;
2617 unsigned char b, c;
2618 int i, j;
2620 if (!w)
2621 return "";
2622 for (i = j = 0; i < sizeof enuf - 1; i++, j++) {
2623 u = w[j];
2624 if (u == 0)
2625 break;
2626 b = (unsigned char)(u >> 8);
2627 c = (unsigned char)(u);
2628 if (b) {
2629 if (i >= sizeof enuf - 6)
2630 break;
2631 enuf[i++] = '\\';
2632 enuf[i++] = 'u';
2633 enuf[i++] = hex[b >> 4];
2634 enuf[i++] = hex[b & 15];
2635 enuf[i++] = hex[c >> 4];
2636 enuf[i] = hex[c & 15];
2637 } else if (!isprint(c)) {
2638 if (i >= sizeof enuf - 4)
2639 break;
2640 enuf[i++] = '\\';
2641 enuf[i++] = 'x';
2642 enuf[i++] = hex[c >> 4];
2643 enuf[i] = hex[c & 15];
2644 } else {
2645 enuf[i] = (char)c;
2648 enuf[i] = 0;
2649 return enuf;
2652 #include <stdarg.h>
2654 static JSBool
2655 ZZ_formatter(JSContext *cx, const char *format, JSBool fromJS, jsval **vpp,
2656 va_list *app)
2658 jsval *vp;
2659 va_list ap;
2660 jsdouble re, im;
2662 printf("entering ZZ_formatter");
2663 vp = *vpp;
2664 ap = *app;
2665 if (fromJS) {
2666 if (!JS_ValueToNumber(cx, vp[0], &re))
2667 return JS_FALSE;
2668 if (!JS_ValueToNumber(cx, vp[1], &im))
2669 return JS_FALSE;
2670 *va_arg(ap, jsdouble *) = re;
2671 *va_arg(ap, jsdouble *) = im;
2672 } else {
2673 re = va_arg(ap, jsdouble);
2674 im = va_arg(ap, jsdouble);
2675 if (!JS_NewNumberValue(cx, re, &vp[0]))
2676 return JS_FALSE;
2677 if (!JS_NewNumberValue(cx, im, &vp[1]))
2678 return JS_FALSE;
2680 *vpp = vp + 2;
2681 *app = ap;
2682 printf("leaving ZZ_formatter");
2683 return JS_TRUE;
2686 static JSBool
2687 ConvertArgs(JSContext *cx, uintN argc, jsval *vp)
2689 JSBool b = JS_FALSE;
2690 jschar c = 0;
2691 int32 i = 0, j = 0;
2692 uint32 u = 0;
2693 jsdouble d = 0, I = 0, re = 0, im = 0;
2694 JSString *str = NULL;
2695 jschar *w = NULL;
2696 JSObject *obj2 = NULL;
2697 JSFunction *fun = NULL;
2698 jsval v = JSVAL_VOID;
2699 JSBool ok;
2701 if (!JS_AddArgumentFormatter(cx, "ZZ", ZZ_formatter))
2702 return JS_FALSE;
2703 ok = JS_ConvertArguments(cx, argc, JS_ARGV(cx, vp), "b/ciujdISWofvZZ*",
2704 &b, &c, &i, &u, &j, &d, &I, &str, &w, &obj2,
2705 &fun, &v, &re, &im);
2706 JS_RemoveArgumentFormatter(cx, "ZZ");
2707 if (!ok)
2708 return JS_FALSE;
2709 fprintf(gOutFile,
2710 "b %u, c %x (%c), i %ld, u %lu, j %ld\n",
2711 b, c, (char)c, i, u, j);
2712 ToString obj2string(cx, obj2);
2713 ToString valueString(cx, v);
2714 JSAutoByteString strBytes;
2715 if (str)
2716 strBytes.encode(cx, str);
2717 JSString *tmpstr = JS_DecompileFunction(cx, fun, 4);
2718 JSAutoByteString func;
2719 if (!tmpstr || !func.encode(cx, tmpstr));
2720 ReportException(cx);
2721 fprintf(gOutFile,
2722 "d %g, I %g, S %s, W %s, obj %s, fun %s\n"
2723 "v %s, re %g, im %g\n",
2724 d, I, !!strBytes ? strBytes.ptr() : "", EscapeWideString(w),
2725 obj2string.getBytes(),
2726 fun ? (!!func ? func.ptr() : "error decompiling fun") : "",
2727 valueString.getBytes(), re, im);
2728 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2729 return JS_TRUE;
2731 #endif
2733 static JSBool
2734 BuildDate(JSContext *cx, uintN argc, jsval *vp)
2736 char version[20] = "\n";
2737 #if JS_VERSION < 150
2738 sprintf(version, " for version %d\n", JS_VERSION);
2739 #endif
2740 fprintf(gOutFile, "built on %s at %s%s", __DATE__, __TIME__, version);
2741 *vp = JSVAL_VOID;
2742 return JS_TRUE;
2745 static JSBool
2746 Clear(JSContext *cx, uintN argc, jsval *vp)
2748 JSObject *obj;
2749 if (argc != 0 && !JS_ValueToObject(cx, JS_ARGV(cx, vp)[0], &obj))
2750 return JS_FALSE;
2751 JS_ClearScope(cx, obj);
2752 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2753 return JS_TRUE;
2756 static JSBool
2757 Intern(JSContext *cx, uintN argc, jsval *vp)
2759 JSString *str = JS_ValueToString(cx, argc == 0 ? JSVAL_VOID : vp[2]);
2760 if (!str)
2761 return false;
2763 size_t length;
2764 const jschar *chars = JS_GetStringCharsAndLength(cx, str, &length);
2765 if (!chars)
2766 return false;
2768 if (!JS_InternUCStringN(cx, chars, length))
2769 return false;
2771 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2772 return true;
2775 static JSBool
2776 Clone(JSContext *cx, uintN argc, jsval *vp)
2778 JSObject *funobj, *parent, *clone;
2780 if (!argc)
2781 return JS_FALSE;
2783 jsval *argv = JS_ARGV(cx, vp);
2784 if (VALUE_IS_FUNCTION(cx, argv[0])) {
2785 funobj = JSVAL_TO_OBJECT(argv[0]);
2786 } else {
2787 JSFunction *fun = JS_ValueToFunction(cx, argv[0]);
2788 if (!fun)
2789 return JS_FALSE;
2790 funobj = JS_GetFunctionObject(fun);
2792 if (argc > 1) {
2793 if (!JS_ValueToObject(cx, argv[1], &parent))
2794 return JS_FALSE;
2795 } else {
2796 parent = JS_GetParent(cx, funobj);
2798 clone = JS_CloneFunctionObject(cx, funobj, parent);
2799 if (!clone)
2800 return JS_FALSE;
2801 *vp = OBJECT_TO_JSVAL(clone);
2802 return JS_TRUE;
2805 static JSBool
2806 GetPDA(JSContext *cx, uintN argc, jsval *vp)
2808 JSObject *vobj, *aobj, *pdobj;
2809 JSBool ok;
2810 JSPropertyDescArray pda;
2811 JSPropertyDesc *pd;
2812 uint32 i;
2813 jsval v;
2815 if (!JS_ValueToObject(cx, argc == 0 ? JSVAL_VOID : vp[2], &vobj))
2816 return JS_FALSE;
2817 if (!vobj) {
2818 *vp = JSVAL_VOID;
2819 return JS_TRUE;
2822 aobj = JS_NewArrayObject(cx, 0, NULL);
2823 if (!aobj)
2824 return JS_FALSE;
2825 *vp = OBJECT_TO_JSVAL(aobj);
2827 ok = JS_GetPropertyDescArray(cx, vobj, &pda);
2828 if (!ok)
2829 return JS_FALSE;
2830 pd = pda.array;
2831 for (i = 0; i < pda.length; i++, pd++) {
2832 pdobj = JS_NewObject(cx, NULL, NULL, NULL);
2833 if (!pdobj) {
2834 ok = JS_FALSE;
2835 break;
2838 /* Protect pdobj from GC by setting it as an element of aobj now */
2839 v = OBJECT_TO_JSVAL(pdobj);
2840 ok = JS_SetElement(cx, aobj, i, &v);
2841 if (!ok)
2842 break;
2844 ok = JS_SetProperty(cx, pdobj, "id", &pd->id) &&
2845 JS_SetProperty(cx, pdobj, "value", &pd->value) &&
2846 (v = INT_TO_JSVAL(pd->flags),
2847 JS_SetProperty(cx, pdobj, "flags", &v)) &&
2848 (v = INT_TO_JSVAL(pd->slot),
2849 JS_SetProperty(cx, pdobj, "slot", &v)) &&
2850 JS_SetProperty(cx, pdobj, "alias", &pd->alias);
2851 if (!ok)
2852 break;
2854 JS_PutPropertyDescArray(cx, &pda);
2855 return ok;
2858 static JSBool
2859 GetSLX(JSContext *cx, uintN argc, jsval *vp)
2861 JSScript *script;
2863 script = ValueToScript(cx, argc == 0 ? JSVAL_VOID : vp[2]);
2864 if (!script)
2865 return JS_FALSE;
2866 *vp = INT_TO_JSVAL(js_GetScriptLineExtent(script));
2867 return JS_TRUE;
2870 static JSBool
2871 ToInt32(JSContext *cx, uintN argc, jsval *vp)
2873 int32 i;
2875 if (!JS_ValueToInt32(cx, argc == 0 ? JSVAL_VOID : vp[2], &i))
2876 return JS_FALSE;
2877 return JS_NewNumberValue(cx, i, vp);
2880 static JSBool
2881 StringsAreUTF8(JSContext *cx, uintN argc, jsval *vp)
2883 *vp = JS_CStringsAreUTF8() ? JSVAL_TRUE : JSVAL_FALSE;
2884 return JS_TRUE;
2887 static JSBool
2888 StackQuota(JSContext *cx, uintN argc, jsval *vp)
2890 uint32 n;
2892 if (argc == 0)
2893 return JS_NewNumberValue(cx, (double) gScriptStackQuota, vp);
2894 if (!JS_ValueToECMAUint32(cx, JS_ARGV(cx, vp)[0], &n))
2895 return JS_FALSE;
2896 gScriptStackQuota = n;
2897 JS_SetScriptStackQuota(cx, gScriptStackQuota);
2898 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2899 return JS_TRUE;
2902 static const char* badUTF8 = "...\xC0...";
2903 static const char* bigUTF8 = "...\xFB\xBF\xBF\xBF\xBF...";
2904 static const jschar badSurrogate[] = { 'A', 'B', 'C', 0xDEEE, 'D', 'E', 0 };
2906 static JSBool
2907 TestUTF8(JSContext *cx, uintN argc, jsval *vp)
2909 int32 mode = 1;
2910 jschar chars[20];
2911 size_t charsLength = 5;
2912 char bytes[20];
2913 size_t bytesLength = 20;
2914 if (argc && !JS_ValueToInt32(cx, *JS_ARGV(cx, vp), &mode))
2915 return JS_FALSE;
2917 /* The following throw errors if compiled with UTF-8. */
2918 switch (mode) {
2919 /* mode 1: malformed UTF-8 string. */
2920 case 1:
2921 JS_NewStringCopyZ(cx, badUTF8);
2922 break;
2923 /* mode 2: big UTF-8 character. */
2924 case 2:
2925 JS_NewStringCopyZ(cx, bigUTF8);
2926 break;
2927 /* mode 3: bad surrogate character. */
2928 case 3:
2929 JS_EncodeCharacters(cx, badSurrogate, 6, bytes, &bytesLength);
2930 break;
2931 /* mode 4: use a too small buffer. */
2932 case 4:
2933 JS_DecodeBytes(cx, "1234567890", 10, chars, &charsLength);
2934 break;
2935 default:
2936 JS_ReportError(cx, "invalid mode parameter");
2937 return JS_FALSE;
2939 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2940 return !JS_IsExceptionPending (cx);
2943 static JSBool
2944 ThrowError(JSContext *cx, uintN argc, jsval *vp)
2946 JS_ReportError(cx, "This is an error");
2947 return JS_FALSE;
2950 #define LAZY_STANDARD_CLASSES
2952 /* A class for easily testing the inner/outer object callbacks. */
2953 typedef struct ComplexObject {
2954 JSBool isInner;
2955 JSBool frozen;
2956 JSObject *inner;
2957 JSObject *outer;
2958 } ComplexObject;
2960 static JSObject *
2961 split_create_outer(JSContext *cx);
2963 static JSObject *
2964 split_create_inner(JSContext *cx, JSObject *outer);
2966 static ComplexObject *
2967 split_get_private(JSContext *cx, JSObject *obj);
2969 static JSBool
2970 split_addProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
2972 ComplexObject *cpx;
2974 cpx = split_get_private(cx, obj);
2975 if (!cpx)
2976 return JS_TRUE;
2977 if (!cpx->isInner && cpx->inner) {
2978 /* Make sure to define this property on the inner object. */
2979 return JS_DefinePropertyById(cx, cpx->inner, id, *vp, NULL, NULL, JSPROP_ENUMERATE);
2981 return JS_TRUE;
2984 static JSBool
2985 split_getProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
2987 ComplexObject *cpx;
2989 cpx = split_get_private(cx, obj);
2990 if (!cpx)
2991 return JS_TRUE;
2993 if (JSID_IS_ATOM(id) && JS_FlatStringEqualsAscii(JSID_TO_FLAT_STRING(id), "isInner")) {
2994 *vp = BOOLEAN_TO_JSVAL(cpx->isInner);
2995 return JS_TRUE;
2998 if (!cpx->isInner && cpx->inner) {
2999 if (JSID_IS_ATOM(id)) {
3000 JSString *str = JSID_TO_STRING(id);
3002 size_t length;
3003 const jschar *chars = JS_GetStringCharsAndLength(cx, str, &length);
3004 if (!chars)
3005 return false;
3007 return JS_GetUCProperty(cx, cpx->inner, chars, length, vp);
3009 if (JSID_IS_INT(id))
3010 return JS_GetElement(cx, cpx->inner, JSID_TO_INT(id), vp);
3011 return JS_TRUE;
3014 return JS_TRUE;
3017 static JSBool
3018 split_setProperty(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp)
3020 ComplexObject *cpx;
3022 cpx = split_get_private(cx, obj);
3023 if (!cpx)
3024 return true;
3025 if (!cpx->isInner && cpx->inner) {
3026 if (JSID_IS_ATOM(id)) {
3027 JSString *str = JSID_TO_STRING(id);
3029 size_t length;
3030 const jschar *chars = JS_GetStringCharsAndLength(cx, str, &length);
3031 if (!chars)
3032 return false;
3034 return JS_SetUCProperty(cx, cpx->inner, chars, length, vp);
3036 if (JSID_IS_INT(id))
3037 return JS_SetElement(cx, cpx->inner, JSID_TO_INT(id), vp);
3038 return true;
3041 return true;
3044 static JSBool
3045 split_delProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
3047 ComplexObject *cpx;
3048 jsid asId;
3050 cpx = split_get_private(cx, obj);
3051 if (!cpx)
3052 return JS_TRUE;
3053 if (!cpx->isInner && cpx->inner) {
3054 /* Make sure to define this property on the inner object. */
3055 if (!JS_ValueToId(cx, *vp, &asId))
3056 return JS_FALSE;
3057 return cpx->inner->deleteProperty(cx, asId, Valueify(vp), true);
3059 return JS_TRUE;
3062 static JSBool
3063 split_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
3064 jsval *statep, jsid *idp)
3066 ComplexObject *cpx;
3067 JSObject *iterator;
3069 switch (enum_op) {
3070 case JSENUMERATE_INIT:
3071 case JSENUMERATE_INIT_ALL:
3072 cpx = (ComplexObject *) JS_GetPrivate(cx, obj);
3074 if (!cpx->isInner && cpx->inner)
3075 obj = cpx->inner;
3077 iterator = JS_NewPropertyIterator(cx, obj);
3078 if (!iterator)
3079 return JS_FALSE;
3081 *statep = OBJECT_TO_JSVAL(iterator);
3082 if (idp)
3083 *idp = INT_TO_JSID(0);
3084 break;
3086 case JSENUMERATE_NEXT:
3087 iterator = (JSObject*)JSVAL_TO_OBJECT(*statep);
3088 if (!JS_NextProperty(cx, iterator, idp))
3089 return JS_FALSE;
3091 if (!JSID_IS_VOID(*idp))
3092 break;
3093 /* Fall through. */
3095 case JSENUMERATE_DESTROY:
3096 /* Let GC at our iterator object. */
3097 *statep = JSVAL_NULL;
3098 break;
3101 return JS_TRUE;
3104 static JSBool
3105 ResolveClass(JSContext *cx, JSObject *obj, jsid id, JSBool *resolved)
3107 if (!JS_ResolveStandardClass(cx, obj, id, resolved))
3108 return JS_FALSE;
3110 if (!*resolved) {
3111 if (JSID_IS_ATOM(id, CLASS_ATOM(cx, Reflect))) {
3112 if (!js_InitReflectClass(cx, obj))
3113 return JS_FALSE;
3114 *resolved = JS_TRUE;
3118 return JS_TRUE;
3121 static JSBool
3122 split_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject **objp)
3124 ComplexObject *cpx;
3126 if (JSID_IS_ATOM(id) && JS_FlatStringEqualsAscii(JSID_TO_FLAT_STRING(id), "isInner")) {
3127 *objp = obj;
3128 return JS_DefinePropertyById(cx, obj, id, JSVAL_VOID, NULL, NULL, JSPROP_SHARED);
3131 cpx = split_get_private(cx, obj);
3132 if (!cpx)
3133 return JS_TRUE;
3134 if (!cpx->isInner && cpx->inner) {
3135 JSProperty *prop;
3136 return cpx->inner->lookupProperty(cx, id, objp, &prop);
3139 #ifdef LAZY_STANDARD_CLASSES
3140 if (!(flags & JSRESOLVE_ASSIGNING)) {
3141 JSBool resolved;
3143 if (!ResolveClass(cx, obj, id, &resolved))
3144 return JS_FALSE;
3146 if (resolved) {
3147 *objp = obj;
3148 return JS_TRUE;
3151 #endif
3153 /* XXX For additional realism, let's resolve some random property here. */
3154 return JS_TRUE;
3157 static void
3158 split_finalize(JSContext *cx, JSObject *obj)
3160 JS_free(cx, JS_GetPrivate(cx, obj));
3163 static uint32
3164 split_mark(JSContext *cx, JSObject *obj, void *arg)
3166 ComplexObject *cpx;
3168 cpx = (ComplexObject *) JS_GetPrivate(cx, obj);
3170 if (!cpx->isInner && cpx->inner) {
3171 /* Mark the inner object. */
3172 JS_MarkGCThing(cx, OBJECT_TO_JSVAL(cpx->inner), "ComplexObject.inner", arg);
3175 if (cpx->isInner && cpx->outer) {
3176 /* Mark the inner object. */
3177 JS_MarkGCThing(cx, OBJECT_TO_JSVAL(cpx->outer), "ComplexObject.outer", arg);
3180 return 0;
3183 static JSObject *
3184 split_outerObject(JSContext *cx, JSObject *obj)
3186 ComplexObject *cpx;
3188 cpx = (ComplexObject *) JS_GetPrivate(cx, obj);
3189 return cpx->isInner ? cpx->outer : obj;
3192 static JSObject *
3193 split_thisObject(JSContext *cx, JSObject *obj)
3195 OBJ_TO_OUTER_OBJECT(cx, obj);
3196 if (!obj)
3197 return NULL;
3198 return obj;
3202 static JSBool
3203 split_equality(JSContext *cx, JSObject *obj, const jsval *v, JSBool *bp);
3205 static JSObject *
3206 split_innerObject(JSContext *cx, JSObject *obj)
3208 ComplexObject *cpx;
3210 cpx = (ComplexObject *) JS_GetPrivate(cx, obj);
3211 if (cpx->frozen) {
3212 JS_ASSERT(!cpx->isInner);
3213 return obj;
3215 return !cpx->isInner ? cpx->inner : obj;
3218 static Class split_global_class = {
3219 "split_global",
3220 JSCLASS_NEW_RESOLVE | JSCLASS_NEW_ENUMERATE | JSCLASS_HAS_PRIVATE | JSCLASS_GLOBAL_FLAGS,
3221 Valueify(split_addProperty),
3222 Valueify(split_delProperty),
3223 Valueify(split_getProperty),
3224 Valueify(split_setProperty),
3225 (JSEnumerateOp)split_enumerate,
3226 (JSResolveOp)split_resolve,
3227 ConvertStub,
3228 split_finalize,
3229 NULL, /* reserved0 */
3230 NULL, /* checkAccess */
3231 NULL, /* call */
3232 NULL, /* construct */
3233 NULL, /* xdrObject */
3234 NULL, /* hasInstance */
3235 split_mark,
3237 Valueify(split_equality),
3238 split_outerObject,
3239 split_innerObject,
3240 NULL, /* iteratorObject */
3241 NULL, /* wrappedObject */
3244 NULL, /* lookupProperty */
3245 NULL, /* defineProperty */
3246 NULL, /* getProperty */
3247 NULL, /* setProperty */
3248 NULL, /* getAttributes */
3249 NULL, /* setAttributes */
3250 NULL, /* deleteProperty */
3251 NULL, /* enumerate */
3252 NULL, /* typeOf */
3253 NULL, /* trace */
3254 NULL, /* fix */
3255 split_thisObject,
3256 NULL, /* clear */
3260 static JSBool
3261 split_equality(JSContext *cx, JSObject *obj, const jsval *v, JSBool *bp)
3263 *bp = JS_FALSE;
3264 if (JSVAL_IS_PRIMITIVE(*v))
3265 return JS_TRUE;
3267 JSObject *obj2 = JSVAL_TO_OBJECT(*v);
3268 if (obj2->getClass() != &split_global_class)
3269 return JS_TRUE;
3271 ComplexObject *cpx = (ComplexObject *) JS_GetPrivate(cx, obj2);
3272 JS_ASSERT(!cpx->isInner);
3274 ComplexObject *ourCpx = (ComplexObject *) JS_GetPrivate(cx, obj);
3275 JS_ASSERT(!ourCpx->isInner);
3277 *bp = (cpx == ourCpx);
3278 return JS_TRUE;
3281 JSObject *
3282 split_create_outer(JSContext *cx)
3284 ComplexObject *cpx;
3285 JSObject *obj;
3287 cpx = (ComplexObject *) JS_malloc(cx, sizeof *obj);
3288 if (!cpx)
3289 return NULL;
3290 cpx->isInner = JS_FALSE;
3291 cpx->frozen = JS_TRUE;
3292 cpx->inner = NULL;
3293 cpx->outer = NULL;
3295 obj = JS_NewGlobalObject(cx, Jsvalify(&split_global_class));
3296 if (!obj) {
3297 JS_free(cx, cpx);
3298 return NULL;
3301 if (!JS_SetPrivate(cx, obj, cpx)) {
3302 JS_free(cx, cpx);
3303 return NULL;
3306 return obj;
3309 static JSObject *
3310 split_create_inner(JSContext *cx, JSObject *outer)
3312 ComplexObject *cpx, *outercpx;
3313 JSObject *obj;
3315 JS_ASSERT(outer->getClass() == &split_global_class);
3317 cpx = (ComplexObject *) JS_malloc(cx, sizeof *cpx);
3318 if (!cpx)
3319 return NULL;
3320 cpx->isInner = JS_TRUE;
3321 cpx->frozen = JS_FALSE;
3322 cpx->inner = NULL;
3323 cpx->outer = outer;
3325 obj = JS_NewGlobalObject(cx, Jsvalify(&split_global_class));
3326 if (!obj || !JS_SetPrivate(cx, obj, cpx)) {
3327 JS_free(cx, cpx);
3328 return NULL;
3331 outercpx = (ComplexObject *) JS_GetPrivate(cx, outer);
3332 outercpx->inner = obj;
3333 outercpx->frozen = JS_FALSE;
3335 return obj;
3338 static ComplexObject *
3339 split_get_private(JSContext *cx, JSObject *obj)
3341 do {
3342 if (obj->getClass() == &split_global_class)
3343 return (ComplexObject *) JS_GetPrivate(cx, obj);
3344 obj = JS_GetParent(cx, obj);
3345 } while (obj);
3347 return NULL;
3350 static JSBool
3351 sandbox_enumerate(JSContext *cx, JSObject *obj)
3353 jsval v;
3354 JSBool b;
3356 if (!JS_GetProperty(cx, obj, "lazy", &v))
3357 return JS_FALSE;
3359 JS_ValueToBoolean(cx, v, &b);
3360 return !b || JS_EnumerateStandardClasses(cx, obj);
3363 static JSBool
3364 sandbox_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
3365 JSObject **objp)
3367 jsval v;
3368 JSBool b, resolved;
3370 if (!JS_GetProperty(cx, obj, "lazy", &v))
3371 return JS_FALSE;
3373 JS_ValueToBoolean(cx, v, &b);
3374 if (b && (flags & JSRESOLVE_ASSIGNING) == 0) {
3375 if (!ResolveClass(cx, obj, id, &resolved))
3376 return JS_FALSE;
3377 if (resolved) {
3378 *objp = obj;
3379 return JS_TRUE;
3382 *objp = NULL;
3383 return JS_TRUE;
3386 static JSClass sandbox_class = {
3387 "sandbox",
3388 JSCLASS_NEW_RESOLVE | JSCLASS_GLOBAL_FLAGS,
3389 JS_PropertyStub, JS_PropertyStub,
3390 JS_PropertyStub, JS_StrictPropertyStub,
3391 sandbox_enumerate, (JSResolveOp)sandbox_resolve,
3392 JS_ConvertStub, NULL,
3393 JSCLASS_NO_OPTIONAL_MEMBERS
3396 static JSObject *
3397 NewSandbox(JSContext *cx, bool lazy, bool split)
3399 JSObject *obj = JS_NewCompartmentAndGlobalObject(cx, &sandbox_class, NULL);
3400 if (!obj)
3401 return NULL;
3404 JSAutoEnterCompartment ac;
3405 if (!ac.enter(cx, obj))
3406 return NULL;
3408 if (split) {
3409 obj = split_setup(cx, JS_TRUE);
3410 if (!obj)
3411 return NULL;
3413 if (!lazy && !JS_InitStandardClasses(cx, obj))
3414 return NULL;
3416 AutoValueRooter root(cx, BooleanValue(lazy));
3417 if (!JS_SetProperty(cx, obj, "lazy", root.jsval_addr()))
3418 return NULL;
3420 if (split)
3421 obj = split_outerObject(cx, obj);
3424 AutoObjectRooter objroot(cx, obj);
3425 if (!cx->compartment->wrap(cx, objroot.addr()))
3426 return NULL;
3427 return objroot.object();
3430 static JSBool
3431 EvalInContext(JSContext *cx, uintN argc, jsval *vp)
3433 JSString *str;
3434 JSObject *sobj = NULL;
3435 if (!JS_ConvertArguments(cx, argc, JS_ARGV(cx, vp), "S / o", &str, &sobj))
3436 return false;
3438 size_t srclen;
3439 const jschar *src = JS_GetStringCharsAndLength(cx, str, &srclen);
3440 if (!src)
3441 return false;
3443 bool split = false, lazy = false;
3444 if (srclen == 4) {
3445 if (src[0] == 'l' && src[1] == 'a' && src[2] == 'z' && src[3] == 'y') {
3446 lazy = true;
3447 srclen = 0;
3449 } else if (srclen == 5) {
3450 if (src[0] == 's' && src[1] == 'p' && src[2] == 'l' && src[3] == 'i' && src[4] == 't') {
3451 split = lazy = true;
3452 srclen = 0;
3456 if (!sobj) {
3457 sobj = NewSandbox(cx, lazy, split);
3458 if (!sobj)
3459 return false;
3462 *vp = OBJECT_TO_JSVAL(sobj);
3463 if (srclen == 0)
3464 return true;
3466 JSStackFrame *fp = JS_GetScriptedCaller(cx, NULL);
3468 JSAutoEnterCompartment ac;
3469 uintN flags;
3470 JSObject *unwrapped = sobj->unwrap(&flags);
3471 if (flags & JSWrapper::CROSS_COMPARTMENT) {
3472 sobj = unwrapped;
3473 if (!ac.enter(cx, sobj))
3474 return false;
3477 OBJ_TO_INNER_OBJECT(cx, sobj);
3478 if (!sobj)
3479 return false;
3480 if (!(sobj->getClass()->flags & JSCLASS_IS_GLOBAL)) {
3481 JS_ReportError(cx, "Invalid scope argument to evalcx");
3482 return false;
3484 if (!JS_EvaluateUCScript(cx, sobj, src, srclen,
3485 fp->script()->filename,
3486 JS_PCToLineNumber(cx, fp->script(), fp->pc(cx)),
3487 vp)) {
3488 return false;
3491 return cx->compartment->wrap(cx, Valueify(vp));
3494 static JSBool
3495 EvalInFrame(JSContext *cx, uintN argc, jsval *vp)
3497 jsval *argv = JS_ARGV(cx, vp);
3498 if (argc < 2 ||
3499 !JSVAL_IS_INT(argv[0]) ||
3500 !JSVAL_IS_STRING(argv[1])) {
3501 JS_ReportError(cx, "Invalid arguments to evalInFrame");
3502 return JS_FALSE;
3505 uint32 upCount = JSVAL_TO_INT(argv[0]);
3506 JSString *str = JSVAL_TO_STRING(argv[1]);
3508 bool saveCurrent = (argc >= 3 && JSVAL_IS_BOOLEAN(argv[2]))
3509 ? !!(JSVAL_TO_BOOLEAN(argv[2]))
3510 : false;
3512 JS_ASSERT(cx->hasfp());
3514 FrameRegsIter fi(cx);
3515 for (uint32 i = 0; i < upCount; ++i, ++fi) {
3516 if (!fi.fp()->prev())
3517 break;
3520 JSStackFrame *const fp = fi.fp();
3521 if (!JS_IsScriptFrame(cx, fp)) {
3522 JS_ReportError(cx, "cannot eval in non-script frame");
3523 return JS_FALSE;
3526 JSStackFrame *oldfp = NULL;
3527 if (saveCurrent)
3528 oldfp = JS_SaveFrameChain(cx);
3530 size_t length;
3531 const jschar *chars = JS_GetStringCharsAndLength(cx, str, &length);
3532 if (!chars)
3533 return JS_FALSE;
3535 JSBool ok = JS_EvaluateUCInStackFrame(cx, fp, chars, length,
3536 fp->script()->filename,
3537 JS_PCToLineNumber(cx, fp->script(),
3538 fi.pc()),
3539 vp);
3541 if (saveCurrent)
3542 JS_RestoreFrameChain(cx, oldfp);
3544 return ok;
3547 static JSBool
3548 ShapeOf(JSContext *cx, uintN argc, jsval *vp)
3550 jsval v;
3551 if (argc < 1 || !JSVAL_IS_OBJECT(v = JS_ARGV(cx, vp)[0])) {
3552 JS_ReportError(cx, "shapeOf: object expected");
3553 return JS_FALSE;
3555 JSObject *obj = JSVAL_TO_OBJECT(v);
3556 if (!obj) {
3557 *vp = JSVAL_ZERO;
3558 return JS_TRUE;
3560 if (!obj->isNative()) {
3561 *vp = INT_TO_JSVAL(-1);
3562 return JS_TRUE;
3564 return JS_NewNumberValue(cx, obj->shape(), vp);
3567 #ifdef JS_THREADSAFE
3570 * Check that t1 comes strictly before t2. The function correctly deals with
3571 * PRIntervalTime wrap-around between t2 and t1 assuming that t2 and t1 stays
3572 * within INT32_MAX from each other. We use MAX_TIMEOUT_INTERVAL to enforce
3573 * this restriction.
3575 static bool
3576 IsBefore(PRIntervalTime t1, PRIntervalTime t2)
3578 return int32(t1 - t2) < 0;
3581 static JSBool
3582 Sleep_fn(JSContext *cx, uintN argc, jsval *vp)
3584 PRIntervalTime t_ticks;
3586 if (argc == 0) {
3587 t_ticks = 0;
3588 } else {
3589 jsdouble t_secs;
3591 if (!JS_ValueToNumber(cx, argc == 0 ? JSVAL_VOID : vp[2], &t_secs))
3592 return JS_FALSE;
3594 /* NB: The next condition also filter out NaNs. */
3595 if (!(t_secs <= MAX_TIMEOUT_INTERVAL)) {
3596 JS_ReportError(cx, "Excessive sleep interval");
3597 return JS_FALSE;
3599 t_ticks = (t_secs <= 0.0)
3601 : PRIntervalTime(PR_TicksPerSecond() * t_secs);
3603 if (t_ticks == 0) {
3604 JS_YieldRequest(cx);
3605 } else {
3606 JSAutoSuspendRequest suspended(cx);
3607 PR_Lock(gWatchdogLock);
3608 PRIntervalTime to_wakeup = PR_IntervalNow() + t_ticks;
3609 for (;;) {
3610 PR_WaitCondVar(gSleepWakeup, t_ticks);
3611 if (gCanceled)
3612 break;
3613 PRIntervalTime now = PR_IntervalNow();
3614 if (!IsBefore(now, to_wakeup))
3615 break;
3616 t_ticks = to_wakeup - now;
3618 PR_Unlock(gWatchdogLock);
3620 return !gCanceled;
3623 typedef struct ScatterThreadData ScatterThreadData;
3624 typedef struct ScatterData ScatterData;
3626 typedef enum ScatterStatus {
3627 SCATTER_WAIT,
3628 SCATTER_GO,
3629 SCATTER_CANCEL
3630 } ScatterStatus;
3632 struct ScatterData {
3633 ScatterThreadData *threads;
3634 jsval *results;
3635 PRLock *lock;
3636 PRCondVar *cvar;
3637 ScatterStatus status;
3640 struct ScatterThreadData {
3641 jsint index;
3642 ScatterData *shared;
3643 PRThread *thr;
3644 JSContext *cx;
3645 jsval fn;
3648 static void
3649 DoScatteredWork(JSContext *cx, ScatterThreadData *td)
3651 jsval *rval = &td->shared->results[td->index];
3653 if (!JS_CallFunctionValue(cx, NULL, td->fn, 0, NULL, rval)) {
3654 *rval = JSVAL_VOID;
3655 JS_GetPendingException(cx, rval);
3656 JS_ClearPendingException(cx);
3660 static void
3661 RunScatterThread(void *arg)
3663 int stackDummy;
3664 ScatterThreadData *td;
3665 ScatterStatus st;
3666 JSContext *cx;
3668 if (PR_FAILURE == PR_SetThreadPrivate(gStackBaseThreadIndex, &stackDummy))
3669 return;
3671 td = (ScatterThreadData *)arg;
3672 cx = td->cx;
3674 /* Wait for our signal. */
3675 PR_Lock(td->shared->lock);
3676 while ((st = td->shared->status) == SCATTER_WAIT)
3677 PR_WaitCondVar(td->shared->cvar, PR_INTERVAL_NO_TIMEOUT);
3678 PR_Unlock(td->shared->lock);
3680 if (st == SCATTER_CANCEL)
3681 return;
3683 /* We are good to go. */
3684 JS_SetContextThread(cx);
3685 JS_SetNativeStackQuota(cx, gMaxStackSize);
3686 JS_BeginRequest(cx);
3687 DoScatteredWork(cx, td);
3688 JS_EndRequest(cx);
3689 JS_ClearContextThread(cx);
3693 * scatter(fnArray) - Call each function in `fnArray` without arguments, each
3694 * in a different thread. When all threads have finished, return an array: the
3695 * return values. Errors are not propagated; if any of the function calls
3696 * fails, the corresponding element in the results array gets the exception
3697 * object, if any, else (undefined).
3699 static JSBool
3700 Scatter(JSContext *cx, uintN argc, jsval *vp)
3702 jsuint i;
3703 jsuint n; /* number of threads */
3704 JSObject *inArr;
3705 JSObject *arr;
3706 JSObject *global;
3707 ScatterData sd;
3708 JSBool ok;
3710 sd.lock = NULL;
3711 sd.cvar = NULL;
3712 sd.results = NULL;
3713 sd.threads = NULL;
3714 sd.status = SCATTER_WAIT;
3716 if (argc == 0 || JSVAL_IS_PRIMITIVE(JS_ARGV(cx, vp)[0])) {
3717 JS_ReportError(cx, "the first argument must be an object");
3718 goto fail;
3721 inArr = JSVAL_TO_OBJECT(JS_ARGV(cx, vp)[0]);
3722 ok = JS_GetArrayLength(cx, inArr, &n);
3723 if (!ok)
3724 goto out;
3725 if (n == 0)
3726 goto success;
3728 sd.lock = PR_NewLock();
3729 if (!sd.lock)
3730 goto fail;
3732 sd.cvar = PR_NewCondVar(sd.lock);
3733 if (!sd.cvar)
3734 goto fail;
3736 sd.results = (jsval *) malloc(n * sizeof(jsval));
3737 if (!sd.results)
3738 goto fail;
3739 for (i = 0; i < n; i++) {
3740 sd.results[i] = JSVAL_VOID;
3741 ok = JS_AddValueRoot(cx, &sd.results[i]);
3742 if (!ok) {
3743 while (i-- > 0)
3744 JS_RemoveValueRoot(cx, &sd.results[i]);
3745 free(sd.results);
3746 sd.results = NULL;
3747 goto fail;
3751 sd.threads = (ScatterThreadData *) malloc(n * sizeof(ScatterThreadData));
3752 if (!sd.threads)
3753 goto fail;
3754 for (i = 0; i < n; i++) {
3755 sd.threads[i].index = i;
3756 sd.threads[i].shared = &sd;
3757 sd.threads[i].thr = NULL;
3758 sd.threads[i].cx = NULL;
3759 sd.threads[i].fn = JSVAL_NULL;
3761 ok = JS_AddValueRoot(cx, &sd.threads[i].fn);
3762 if (ok && !JS_GetElement(cx, inArr, (jsint) i, &sd.threads[i].fn)) {
3763 JS_RemoveValueRoot(cx, &sd.threads[i].fn);
3764 ok = JS_FALSE;
3766 if (!ok) {
3767 while (i-- > 0)
3768 JS_RemoveValueRoot(cx, &sd.threads[i].fn);
3769 free(sd.threads);
3770 sd.threads = NULL;
3771 goto fail;
3775 global = JS_GetGlobalObject(cx);
3776 for (i = 1; i < n; i++) {
3777 JSContext *newcx = NewContext(JS_GetRuntime(cx));
3778 if (!newcx)
3779 goto fail;
3782 JSAutoRequest req(newcx);
3783 JS_SetGlobalObject(newcx, global);
3785 JS_ClearContextThread(newcx);
3786 sd.threads[i].cx = newcx;
3789 for (i = 1; i < n; i++) {
3790 PRThread *t = PR_CreateThread(PR_USER_THREAD,
3791 RunScatterThread,
3792 &sd.threads[i],
3793 PR_PRIORITY_NORMAL,
3794 PR_GLOBAL_THREAD,
3795 PR_JOINABLE_THREAD,
3797 if (!t) {
3798 /* Failed to start thread. */
3799 PR_Lock(sd.lock);
3800 sd.status = SCATTER_CANCEL;
3801 PR_NotifyAllCondVar(sd.cvar);
3802 PR_Unlock(sd.lock);
3803 while (i-- > 1)
3804 PR_JoinThread(sd.threads[i].thr);
3805 goto fail;
3808 sd.threads[i].thr = t;
3810 PR_Lock(sd.lock);
3811 sd.status = SCATTER_GO;
3812 PR_NotifyAllCondVar(sd.cvar);
3813 PR_Unlock(sd.lock);
3815 DoScatteredWork(cx, &sd.threads[0]);
3818 JSAutoSuspendRequest suspended(cx);
3819 for (i = 1; i < n; i++) {
3820 PR_JoinThread(sd.threads[i].thr);
3824 success:
3825 arr = JS_NewArrayObject(cx, n, sd.results);
3826 if (!arr)
3827 goto fail;
3828 *vp = OBJECT_TO_JSVAL(arr);
3829 ok = JS_TRUE;
3831 out:
3832 if (sd.threads) {
3833 JSContext *acx;
3835 for (i = 0; i < n; i++) {
3836 JS_RemoveValueRoot(cx, &sd.threads[i].fn);
3837 acx = sd.threads[i].cx;
3838 if (acx) {
3839 JS_SetContextThread(acx);
3840 DestroyContext(acx, true);
3843 free(sd.threads);
3845 if (sd.results) {
3846 for (i = 0; i < n; i++)
3847 JS_RemoveValueRoot(cx, &sd.results[i]);
3848 free(sd.results);
3850 if (sd.cvar)
3851 PR_DestroyCondVar(sd.cvar);
3852 if (sd.lock)
3853 PR_DestroyLock(sd.lock);
3855 return ok;
3857 fail:
3858 ok = JS_FALSE;
3859 goto out;
3862 static bool
3863 InitWatchdog(JSRuntime *rt)
3865 JS_ASSERT(!gWatchdogThread);
3866 gWatchdogLock = PR_NewLock();
3867 if (gWatchdogLock) {
3868 gWatchdogWakeup = PR_NewCondVar(gWatchdogLock);
3869 if (gWatchdogWakeup) {
3870 gSleepWakeup = PR_NewCondVar(gWatchdogLock);
3871 if (gSleepWakeup)
3872 return true;
3873 PR_DestroyCondVar(gWatchdogWakeup);
3875 PR_DestroyLock(gWatchdogLock);
3877 return false;
3880 static void
3881 KillWatchdog()
3883 PRThread *thread;
3885 PR_Lock(gWatchdogLock);
3886 thread = gWatchdogThread;
3887 if (thread) {
3889 * The watchdog thread is running, tell it to terminate waking it up
3890 * if necessary.
3892 gWatchdogThread = NULL;
3893 PR_NotifyCondVar(gWatchdogWakeup);
3895 PR_Unlock(gWatchdogLock);
3896 if (thread)
3897 PR_JoinThread(thread);
3898 PR_DestroyCondVar(gSleepWakeup);
3899 PR_DestroyCondVar(gWatchdogWakeup);
3900 PR_DestroyLock(gWatchdogLock);
3903 static void
3904 WatchdogMain(void *arg)
3906 JSRuntime *rt = (JSRuntime *) arg;
3908 PR_Lock(gWatchdogLock);
3909 while (gWatchdogThread) {
3910 PRIntervalTime now = PR_IntervalNow();
3911 if (gWatchdogHasTimeout && !IsBefore(now, gWatchdogTimeout)) {
3913 * The timeout has just expired. Trigger the operation callback
3914 * outside the lock.
3916 gWatchdogHasTimeout = false;
3917 PR_Unlock(gWatchdogLock);
3918 CancelExecution(rt);
3919 PR_Lock(gWatchdogLock);
3921 /* Wake up any threads doing sleep. */
3922 PR_NotifyAllCondVar(gSleepWakeup);
3923 } else {
3924 PRIntervalTime sleepDuration = gWatchdogHasTimeout
3925 ? gWatchdogTimeout - now
3926 : PR_INTERVAL_NO_TIMEOUT;
3927 #ifdef DEBUG
3928 PRStatus status =
3929 #endif
3930 PR_WaitCondVar(gWatchdogWakeup, sleepDuration);
3931 JS_ASSERT(status == PR_SUCCESS);
3934 PR_Unlock(gWatchdogLock);
3937 static bool
3938 ScheduleWatchdog(JSRuntime *rt, jsdouble t)
3940 if (t <= 0) {
3941 PR_Lock(gWatchdogLock);
3942 gWatchdogHasTimeout = false;
3943 PR_Unlock(gWatchdogLock);
3944 return true;
3947 PRIntervalTime interval = PRIntervalTime(ceil(t * PR_TicksPerSecond()));
3948 PRIntervalTime timeout = PR_IntervalNow() + interval;
3949 PR_Lock(gWatchdogLock);
3950 if (!gWatchdogThread) {
3951 JS_ASSERT(!gWatchdogHasTimeout);
3952 gWatchdogThread = PR_CreateThread(PR_USER_THREAD,
3953 WatchdogMain,
3955 PR_PRIORITY_NORMAL,
3956 PR_LOCAL_THREAD,
3957 PR_JOINABLE_THREAD,
3959 if (!gWatchdogThread) {
3960 PR_Unlock(gWatchdogLock);
3961 return false;
3963 } else if (!gWatchdogHasTimeout || IsBefore(timeout, gWatchdogTimeout)) {
3964 PR_NotifyCondVar(gWatchdogWakeup);
3966 gWatchdogHasTimeout = true;
3967 gWatchdogTimeout = timeout;
3968 PR_Unlock(gWatchdogLock);
3969 return true;
3972 #else /* !JS_THREADSAFE */
3974 #ifdef XP_WIN
3975 static HANDLE gTimerHandle = 0;
3977 VOID CALLBACK
3978 TimerCallback(PVOID lpParameter, BOOLEAN TimerOrWaitFired)
3980 CancelExecution((JSRuntime *) lpParameter);
3983 #else
3985 static void
3986 AlarmHandler(int sig)
3988 CancelExecution(gRuntime);
3991 #endif
3993 static bool
3994 InitWatchdog(JSRuntime *rt)
3996 gRuntime = rt;
3997 return true;
4000 static void
4001 KillWatchdog()
4003 ScheduleWatchdog(gRuntime, -1);
4006 static bool
4007 ScheduleWatchdog(JSRuntime *rt, jsdouble t)
4009 #ifdef XP_WIN
4010 if (gTimerHandle) {
4011 DeleteTimerQueueTimer(NULL, gTimerHandle, NULL);
4012 gTimerHandle = 0;
4014 if (t > 0 &&
4015 !CreateTimerQueueTimer(&gTimerHandle,
4016 NULL,
4017 (WAITORTIMERCALLBACK)TimerCallback,
4019 DWORD(ceil(t * 1000.0)),
4021 WT_EXECUTEINTIMERTHREAD | WT_EXECUTEONLYONCE)) {
4022 gTimerHandle = 0;
4023 return false;
4025 #else
4026 /* FIXME: use setitimer when available for sub-second resolution. */
4027 if (t <= 0) {
4028 alarm(0);
4029 signal(SIGALRM, NULL);
4030 } else {
4031 signal(SIGALRM, AlarmHandler); /* set the Alarm signal capture */
4032 alarm(ceil(t));
4034 #endif
4035 return true;
4038 #endif /* !JS_THREADSAFE */
4040 static void
4041 CancelExecution(JSRuntime *rt)
4043 gCanceled = true;
4044 if (gExitCode == 0)
4045 gExitCode = EXITCODE_TIMEOUT;
4046 #ifdef JS_THREADSAFE
4047 if (gWorkerThreadPool)
4048 js::workers::terminateAll(rt, gWorkerThreadPool);
4049 #endif
4050 JS_TriggerAllOperationCallbacks(rt);
4052 static const char msg[] = "Script runs for too long, terminating.\n";
4053 #if defined(XP_UNIX) && !defined(JS_THREADSAFE)
4054 /* It is not safe to call fputs from signals. */
4055 /* Dummy assignment avoids GCC warning on "attribute warn_unused_result" */
4056 ssize_t dummy = write(2, msg, sizeof(msg) - 1);
4057 (void)dummy;
4058 #else
4059 fputs(msg, stderr);
4060 #endif
4063 static JSBool
4064 SetTimeoutValue(JSContext *cx, jsdouble t)
4066 /* NB: The next condition also filter out NaNs. */
4067 if (!(t <= MAX_TIMEOUT_INTERVAL)) {
4068 JS_ReportError(cx, "Excessive timeout value");
4069 return JS_FALSE;
4071 gTimeoutInterval = t;
4072 if (!ScheduleWatchdog(cx->runtime, t)) {
4073 JS_ReportError(cx, "Failed to create the watchdog");
4074 return JS_FALSE;
4076 return JS_TRUE;
4079 static JSBool
4080 Timeout(JSContext *cx, uintN argc, jsval *vp)
4082 if (argc == 0)
4083 return JS_NewNumberValue(cx, gTimeoutInterval, vp);
4085 if (argc > 1) {
4086 JS_ReportError(cx, "Wrong number of arguments");
4087 return JS_FALSE;
4090 jsdouble t;
4091 if (!JS_ValueToNumber(cx, JS_ARGV(cx, vp)[0], &t))
4092 return JS_FALSE;
4094 *vp = JSVAL_VOID;
4095 return SetTimeoutValue(cx, t);
4098 static JSBool
4099 Elapsed(JSContext *cx, uintN argc, jsval *vp)
4101 if (argc == 0) {
4102 double d = 0.0;
4103 JSShellContextData *data = GetContextData(cx);
4104 if (data)
4105 d = js_IntervalNow() - data->startTime;
4106 return JS_NewNumberValue(cx, d, vp);
4108 JS_ReportError(cx, "Wrong number of arguments");
4109 return JS_FALSE;
4112 static JSBool
4113 Parent(JSContext *cx, uintN argc, jsval *vp)
4115 if (argc != 1) {
4116 JS_ReportError(cx, "Wrong number of arguments");
4117 return JS_FALSE;
4120 jsval v = JS_ARGV(cx, vp)[0];
4121 if (JSVAL_IS_PRIMITIVE(v)) {
4122 JS_ReportError(cx, "Only objects have parents!");
4123 return JS_FALSE;
4126 JSObject *parent = JS_GetParent(cx, JSVAL_TO_OBJECT(v));
4127 *vp = OBJECT_TO_JSVAL(parent);
4129 /* Outerize if necessary. Embrace the ugliness! */
4130 if (parent) {
4131 if (JSObjectOp op = parent->getClass()->ext.outerObject)
4132 *vp = OBJECT_TO_JSVAL(op(cx, parent));
4135 return JS_TRUE;
4138 #ifdef XP_UNIX
4140 #include <fcntl.h>
4141 #include <sys/stat.h>
4144 * Returns a JS_malloc'd string (that the caller needs to JS_free)
4145 * containing the directory (non-leaf) part of |from| prepended to |leaf|.
4146 * If |from| is empty or a leaf, MakeAbsolutePathname returns a copy of leaf.
4147 * Returns NULL to indicate an error.
4149 static char *
4150 MakeAbsolutePathname(JSContext *cx, const char *from, const char *leaf)
4152 size_t dirlen;
4153 char *dir;
4154 const char *slash = NULL, *cp;
4156 cp = from;
4157 while (*cp) {
4158 if (*cp == '/') {
4159 slash = cp;
4162 ++cp;
4165 if (!slash) {
4166 /* We were given a leaf or |from| was empty. */
4167 return JS_strdup(cx, leaf);
4170 /* Else, we were given a real pathname, return that + the leaf. */
4171 dirlen = slash - from + 1;
4172 dir = (char*) JS_malloc(cx, dirlen + strlen(leaf) + 1);
4173 if (!dir)
4174 return NULL;
4176 strncpy(dir, from, dirlen);
4177 strcpy(dir + dirlen, leaf); /* Note: we can't use strcat here. */
4179 return dir;
4182 #endif // XP_UNIX
4184 static JSBool
4185 Compile(JSContext *cx, uintN argc, jsval *vp)
4187 if (argc < 1) {
4188 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
4189 "compile", "0", "s");
4190 return JS_FALSE;
4192 jsval arg0 = JS_ARGV(cx, vp)[0];
4193 if (!JSVAL_IS_STRING(arg0)) {
4194 const char *typeName = JS_GetTypeName(cx, JS_TypeOfValue(cx, arg0));
4195 JS_ReportError(cx, "expected string to compile, got %s", typeName);
4196 return JS_FALSE;
4199 JSString *scriptContents = JSVAL_TO_STRING(arg0);
4200 JSScript *result = JS_CompileUCScript(cx, NULL, JS_GetStringCharsZ(cx, scriptContents),
4201 JS_GetStringLength(scriptContents), "<string>", 0);
4202 if (!result)
4203 return JS_FALSE;
4205 JS_SET_RVAL(cx, vp, JSVAL_VOID);
4206 return JS_TRUE;
4209 static JSBool
4210 Parse(JSContext *cx, uintN argc, jsval *vp)
4212 if (argc < 1) {
4213 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
4214 "compile", "0", "s");
4215 return JS_FALSE;
4217 jsval arg0 = JS_ARGV(cx, vp)[0];
4218 if (!JSVAL_IS_STRING(arg0)) {
4219 const char *typeName = JS_GetTypeName(cx, JS_TypeOfValue(cx, arg0));
4220 JS_ReportError(cx, "expected string to parse, got %s", typeName);
4221 return JS_FALSE;
4224 JSString *scriptContents = JSVAL_TO_STRING(arg0);
4225 js::Parser parser(cx);
4226 parser.init(JS_GetStringCharsZ(cx, scriptContents), JS_GetStringLength(scriptContents),
4227 "<string>", 0, cx->findVersion());
4228 if (!parser.parse(NULL))
4229 return JS_FALSE;
4230 JS_SET_RVAL(cx, vp, JSVAL_VOID);
4231 return JS_TRUE;
4234 static JSBool
4235 Snarf(JSContext *cx, uintN argc, jsval *vp)
4237 JSString *str;
4238 const char *pathname;
4239 JSStackFrame *fp;
4241 if (!argc)
4242 return JS_FALSE;
4244 str = JS_ValueToString(cx, JS_ARGV(cx, vp)[0]);
4245 if (!str)
4246 return JS_FALSE;
4247 JSAutoByteString filename(cx, str);
4248 if (!filename)
4249 return JS_FALSE;
4251 /* Get the currently executing script's name. */
4252 fp = JS_GetScriptedCaller(cx, NULL);
4253 JS_ASSERT(fp && fp->script()->filename);
4254 #ifdef XP_UNIX
4255 pathname = MakeAbsolutePathname(cx, fp->script()->filename, filename.ptr());
4256 if (!pathname)
4257 return JS_FALSE;
4258 #else
4259 pathname = filename.ptr();
4260 #endif
4262 str = FileAsString(cx, pathname);
4263 #ifdef XP_UNIX
4264 JS_free(cx, (void*)pathname);
4265 #endif
4266 if (!str)
4267 return JS_FALSE;
4269 *vp = STRING_TO_JSVAL(str);
4270 return JS_TRUE;
4273 JSBool
4274 Wrap(JSContext *cx, uintN argc, jsval *vp)
4276 jsval v = argc > 0 ? JS_ARGV(cx, vp)[0] : JSVAL_VOID;
4277 if (JSVAL_IS_PRIMITIVE(v)) {
4278 JS_SET_RVAL(cx, vp, v);
4279 return true;
4282 JSObject *obj = JSVAL_TO_OBJECT(v);
4283 JSObject *wrapped = JSWrapper::New(cx, obj, obj->getProto(), obj->getGlobal(),
4284 &JSWrapper::singleton);
4285 if (!wrapped)
4286 return false;
4288 JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(wrapped));
4289 return true;
4292 JSBool
4293 Serialize(JSContext *cx, uintN argc, jsval *vp)
4295 jsval v = argc > 0 ? JS_ARGV(cx, vp)[0] : JSVAL_VOID;
4296 uint64 *datap;
4297 size_t nbytes;
4298 if (!JS_WriteStructuredClone(cx, v, &datap, &nbytes, NULL, NULL))
4299 return false;
4301 JSObject *arrayobj = js_CreateTypedArray(cx, TypedArray::TYPE_UINT8, nbytes);
4302 if (!arrayobj) {
4303 JS_free(cx, datap);
4304 return false;
4306 TypedArray *array = TypedArray::fromJSObject(arrayobj);
4307 JS_ASSERT((uintptr_t(array->data) & 7) == 0);
4308 memcpy(array->data, datap, nbytes);
4309 JS_free(cx, datap);
4310 JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(arrayobj));
4311 return true;
4314 JSBool
4315 Deserialize(JSContext *cx, uintN argc, jsval *vp)
4317 jsval v = argc > 0 ? JS_ARGV(cx, vp)[0] : JSVAL_VOID;
4318 JSObject *obj;
4319 if (JSVAL_IS_PRIMITIVE(v) || !js_IsTypedArray((obj = JSVAL_TO_OBJECT(v)))) {
4320 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_INVALID_ARGS, "deserialize");
4321 return false;
4323 TypedArray *array = TypedArray::fromJSObject(obj);
4324 if ((array->byteLength & 7) != 0) {
4325 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_INVALID_ARGS, "deserialize");
4326 return false;
4328 if ((uintptr_t(array->data) & 7) != 0) {
4329 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_BAD_ALIGNMENT);
4330 return false;
4333 if (!JS_ReadStructuredClone(cx, (uint64 *) array->data, array->byteLength,
4334 JS_STRUCTURED_CLONE_VERSION, &v, NULL, NULL)) {
4335 return false;
4337 JS_SET_RVAL(cx, vp, v);
4338 return true;
4341 JSBool
4342 MJitStats(JSContext *cx, uintN argc, jsval *vp)
4344 #ifdef JS_METHODJIT
4345 JS_SET_RVAL(cx, vp, INT_TO_JSVAL(cx->runtime->mjitMemoryUsed));
4346 #else
4347 JS_SET_RVAL(cx, vp, JSVAL_VOID);
4348 #endif
4349 return true;
4352 JSBool
4353 StringStats(JSContext *cx, uintN argc, jsval *vp)
4355 JS_SET_RVAL(cx, vp, INT_TO_JSVAL(cx->runtime->stringMemoryUsed));
4356 return true;
4359 static JSFunctionSpec shell_functions[] = {
4360 JS_FN("version", Version, 0,0),
4361 JS_FN("revertVersion", RevertVersion, 0,0),
4362 JS_FN("options", Options, 0,0),
4363 JS_FN("load", Load, 1,0),
4364 JS_FN("evaluate", Evaluate, 1,0),
4365 JS_FN("run", Run, 1,0),
4366 JS_FN("readline", ReadLine, 0,0),
4367 JS_FN("print", Print, 0,0),
4368 JS_FN("putstr", PutStr, 0,0),
4369 JS_FN("dateNow", Now, 0,0),
4370 JS_FN("help", Help, 0,0),
4371 JS_FN("quit", Quit, 0,0),
4372 JS_FN("assertEq", AssertEq, 2,0),
4373 JS_FN("assertJit", AssertJit, 0,0),
4374 JS_FN("gc", ::GC, 0,0),
4375 #ifdef JS_GCMETER
4376 JS_FN("gcstats", GCStats, 0,0),
4377 #endif
4378 JS_FN("gcparam", GCParameter, 2,0),
4379 JS_FN("countHeap", CountHeap, 0,0),
4380 JS_FN("makeFinalizeObserver", MakeFinalizeObserver, 0,0),
4381 JS_FN("finalizeCount", FinalizeCount, 0,0),
4382 #ifdef JS_GC_ZEAL
4383 JS_FN("gczeal", GCZeal, 1,0),
4384 #endif
4385 JS_FN("setDebug", SetDebug, 1,0),
4386 JS_FN("setDebuggerHandler", SetDebuggerHandler, 1,0),
4387 JS_FN("setThrowHook", SetThrowHook, 1,0),
4388 JS_FN("trap", Trap, 3,0),
4389 JS_FN("untrap", Untrap, 2,0),
4390 JS_FN("line2pc", LineToPC, 0,0),
4391 JS_FN("pc2line", PCToLine, 0,0),
4392 JS_FN("stackQuota", StackQuota, 0,0),
4393 JS_FN("stringsAreUTF8", StringsAreUTF8, 0,0),
4394 JS_FN("testUTF8", TestUTF8, 1,0),
4395 JS_FN("throwError", ThrowError, 0,0),
4396 #ifdef DEBUG
4397 JS_FN("dis", Disassemble, 1,0),
4398 JS_FN("disfile", DisassFile, 1,0),
4399 JS_FN("dissrc", DisassWithSrc, 1,0),
4400 JS_FN("dumpHeap", DumpHeap, 0,0),
4401 JS_FN("dumpObject", DumpObject, 1,0),
4402 JS_FN("notes", Notes, 1,0),
4403 JS_FN("tracing", Tracing, 0,0),
4404 JS_FN("stats", DumpStats, 1,0),
4405 #endif
4406 #ifdef TEST_CVTARGS
4407 JS_FN("cvtargs", ConvertArgs, 0,0),
4408 #endif
4409 JS_FN("build", BuildDate, 0,0),
4410 JS_FN("clear", Clear, 0,0),
4411 JS_FN("intern", Intern, 1,0),
4412 JS_FN("clone", Clone, 1,0),
4413 JS_FN("getpda", GetPDA, 1,0),
4414 JS_FN("getslx", GetSLX, 1,0),
4415 JS_FN("toint32", ToInt32, 1,0),
4416 JS_FN("evalcx", EvalInContext, 1,0),
4417 JS_FN("evalInFrame", EvalInFrame, 2,0),
4418 JS_FN("shapeOf", ShapeOf, 1,0),
4419 #ifdef MOZ_CALLGRIND
4420 JS_FN("startCallgrind", js_StartCallgrind, 0,0),
4421 JS_FN("stopCallgrind", js_StopCallgrind, 0,0),
4422 JS_FN("dumpCallgrind", js_DumpCallgrind, 1,0),
4423 #endif
4424 #ifdef MOZ_VTUNE
4425 JS_FN("startVtune", js_StartVtune, 1,0),
4426 JS_FN("stopVtune", js_StopVtune, 0,0),
4427 JS_FN("pauseVtune", js_PauseVtune, 0,0),
4428 JS_FN("resumeVtune", js_ResumeVtune, 0,0),
4429 #endif
4430 #ifdef MOZ_TRACEVIS
4431 JS_FN("startTraceVis", StartTraceVisNative, 1,0),
4432 JS_FN("stopTraceVis", StopTraceVisNative, 0,0),
4433 #endif
4434 #ifdef DEBUG
4435 JS_FN("arrayInfo", js_ArrayInfo, 1,0),
4436 #endif
4437 #ifdef JS_THREADSAFE
4438 JS_FN("sleep", Sleep_fn, 1,0),
4439 JS_FN("scatter", Scatter, 1,0),
4440 #endif
4441 JS_FN("snarf", Snarf, 0,0),
4442 JS_FN("read", Snarf, 0,0),
4443 JS_FN("compile", Compile, 1,0),
4444 JS_FN("parse", Parse, 1,0),
4445 JS_FN("timeout", Timeout, 1,0),
4446 JS_FN("elapsed", Elapsed, 0,0),
4447 JS_FN("parent", Parent, 1,0),
4448 JS_FN("wrap", Wrap, 1,0),
4449 JS_FN("serialize", Serialize, 1,0),
4450 JS_FN("deserialize", Deserialize, 1,0),
4451 #ifdef JS_METHODJIT
4452 JS_FN("mjitstats", MJitStats, 0,0),
4453 #endif
4454 JS_FN("stringstats", StringStats, 0,0),
4455 JS_FS_END
4458 static const char shell_help_header[] =
4459 "Command Description\n"
4460 "======= ===========\n";
4462 static const char *const shell_help_messages[] = {
4463 "version([number]) Get or force a script compilation version number",
4464 "revertVersion() Revert previously set version number",
4465 "options([option ...]) Get or toggle JavaScript options",
4466 "load(['foo.js' ...]) Load files named by string arguments",
4467 "evaluate(code) Evaluate code as though it were the contents of a file",
4468 "run('foo.js')\n"
4469 " Run the file named by the first argument, returning the number of\n"
4470 " of milliseconds spent compiling and executing it",
4471 "readline() Read a single line from stdin",
4472 "print([exp ...]) Evaluate and print expressions",
4473 "putstr([exp]) Evaluate and print expression without newline",
4474 "dateNow() Return the current time with sub-ms precision",
4475 "help([name ...]) Display usage and help messages",
4476 "quit() Quit the shell",
4477 "assertEq(actual, expected[, msg])\n"
4478 " Throw if the first two arguments are not the same (both +0 or both -0,\n"
4479 " both NaN, or non-zero and ===)",
4480 "assertJit() Throw if the calling function failed to JIT\n",
4481 "gc() Run the garbage collector",
4482 #ifdef JS_GCMETER
4483 "gcstats() Print garbage collector statistics",
4484 #endif
4485 "gcparam(name, value)\n"
4486 " Wrapper for JS_SetGCParameter. The name must be either 'maxBytes' or\n"
4487 " 'maxMallocBytes' and the value must be convertable to a positive uint32",
4488 "countHeap([start[, kind]])\n"
4489 " Count the number of live GC things in the heap or things reachable from\n"
4490 " start when it is given and is not null. kind is either 'all' (default) to\n"
4491 " count all things or one of 'object', 'double', 'string', 'function',\n"
4492 " 'qname', 'namespace', 'xml' to count only things of that kind",
4493 "makeFinalizeObserver()\n"
4494 " get a special object whose finalization increases the counter returned\n"
4495 " by the finalizeCount function",
4496 "finalizeCount()\n"
4497 " return the current value of the finalization counter that is incremented\n"
4498 " each time an object returned by the makeFinalizeObserver is finalized",
4499 #ifdef JS_GC_ZEAL
4500 "gczeal(level) How zealous the garbage collector should be",
4501 #endif
4502 "setDebug(debug) Set debug mode",
4503 "setDebuggerHandler(f) Set handler for debugger keyword to f",
4504 "setThrowHook(f) Set throw hook to f",
4505 "trap([fun, [pc,]] exp) Trap bytecode execution",
4506 "untrap(fun[, pc]) Remove a trap",
4507 "line2pc([fun,] line) Map line number to PC",
4508 "pc2line(fun[, pc]) Map PC to line number",
4509 "stackQuota([number]) Query/set script stack quota",
4510 "stringsAreUTF8() Check if strings are UTF-8 encoded",
4511 "testUTF8(mode) Perform UTF-8 tests (modes are 1 to 4)",
4512 "throwError() Throw an error from JS_ReportError",
4513 #ifdef DEBUG
4514 "dis([fun]) Disassemble functions into bytecodes",
4515 "disfile('foo.js') Disassemble script file into bytecodes\n"
4516 " dis and disfile take these options as preceeding string arguments\n"
4517 " \"-r\" (disassemble recursively)\n"
4518 " \"-l\" (show line numbers)",
4519 "dissrc([fun]) Disassemble functions with source lines",
4520 "dumpHeap([fileName[, start[, toFind[, maxDepth[, toIgnore]]]]])\n"
4521 " Interface to JS_DumpHeap with output sent to file",
4522 "dumpObject() Dump an internal representation of an object",
4523 "notes([fun]) Show source notes for functions",
4524 "tracing([true|false|filename]) Turn bytecode execution tracing on/off.\n"
4525 " With filename, send to file.\n",
4526 "stats([string ...]) Dump 'arena', 'atom', 'global' stats",
4527 #endif
4528 #ifdef TEST_CVTARGS
4529 "cvtargs(arg1..., arg12) Test argument formatter",
4530 #endif
4531 "build() Show build date and time",
4532 "clear([obj]) Clear properties of object",
4533 "intern(str) Internalize str in the atom table",
4534 "clone(fun[, scope]) Clone function object",
4535 "getpda(obj) Get the property descriptors for obj",
4536 "getslx(obj) Get script line extent",
4537 "toint32(n) Testing hook for JS_ValueToInt32",
4538 "evalcx(s[, o])\n"
4539 " Evaluate s in optional sandbox object o\n"
4540 " if (s == '' && !o) return new o with eager standard classes\n"
4541 " if (s == 'lazy' && !o) return new o with lazy standard classes\n"
4542 " if (s == 'split' && !o) return new split-object o with lazy standard classes",
4543 "evalInFrame(n,str,save) Evaluate 'str' in the nth up frame.\n"
4544 " If 'save' (default false), save the frame chain",
4545 "shapeOf(obj) Get the shape of obj (an implementation detail)",
4546 #ifdef MOZ_CALLGRIND
4547 "startCallgrind() Start callgrind instrumentation",
4548 "stopCallgrind() Stop callgrind instrumentation",
4549 "dumpCallgrind([name]) Dump callgrind counters",
4550 #endif
4551 #ifdef MOZ_VTUNE
4552 "startVtune([filename]) Start vtune instrumentation",
4553 "stopVtune() Stop vtune instrumentation",
4554 "pauseVtune() Pause vtune collection",
4555 "resumeVtune() Resume vtune collection",
4556 #endif
4557 #ifdef MOZ_TRACEVIS
4558 "startTraceVis(filename) Start TraceVis recording (stops any current recording)",
4559 "stopTraceVis() Stop TraceVis recording",
4560 #endif
4561 #ifdef DEBUG
4562 "arrayInfo(a1, a2, ...) Report statistics about arrays",
4563 #endif
4564 #ifdef JS_THREADSAFE
4565 "sleep(dt) Sleep for dt seconds",
4566 "scatter(fns) Call functions concurrently (ignoring errors)",
4567 #endif
4568 "snarf(filename) Read filename into returned string",
4569 "read(filename) Synonym for snarf",
4570 "compile(code) Compiles a string to bytecode, potentially throwing",
4571 "parse(code) Parses a string, potentially throwing",
4572 "timeout([seconds])\n"
4573 " Get/Set the limit in seconds for the execution time for the current context.\n"
4574 " A negative value (default) means that the execution time is unlimited.",
4575 "elapsed() Execution time elapsed for the current context.",
4576 "parent(obj) Returns the parent of obj.\n",
4577 "wrap(obj) Wrap an object into a noop wrapper.\n",
4578 "serialize(sd) Serialize sd using JS_WriteStructuredClone. Returns a TypedArray.\n",
4579 "deserialize(a) Deserialize data generated by serialize.\n",
4580 #ifdef JS_METHODJIT
4581 "mjitstats() Return stats on mjit memory usage.\n",
4582 #endif
4583 "stringstats() Return stats on string memory usage.\n"
4584 #ifdef MOZ_PROFILING
4585 "startProfiling() Start a profiling session.\n"
4586 " Profiler must be running with programatic sampling\n"
4587 "stopProfiling() Stop a running profiling session"
4588 #endif
4591 /* Help messages must match shell functions. */
4592 JS_STATIC_ASSERT(JS_ARRAY_LENGTH(shell_help_messages) + 1 ==
4593 JS_ARRAY_LENGTH(shell_functions));
4595 #ifdef DEBUG
4596 static void
4597 CheckHelpMessages()
4599 const char *const *m;
4600 const char *lp;
4602 /* Each message must begin with "function_name(" prefix. */
4603 for (m = shell_help_messages; m != JS_ARRAY_END(shell_help_messages); ++m) {
4604 lp = strchr(*m, '(');
4605 JS_ASSERT(lp);
4606 JS_ASSERT(memcmp(shell_functions[m - shell_help_messages].name,
4607 *m, lp - *m) == 0);
4610 #else
4611 # define CheckHelpMessages() ((void) 0)
4612 #endif
4614 static JSBool
4615 Help(JSContext *cx, uintN argc, jsval *vp)
4617 uintN i, j;
4618 int did_header, did_something;
4619 JSType type;
4620 JSFunction *fun;
4621 JSString *str;
4623 fprintf(gOutFile, "%s\n", JS_GetImplementationVersion());
4624 if (argc == 0) {
4625 fputs(shell_help_header, gOutFile);
4626 for (i = 0; shell_functions[i].name; i++)
4627 fprintf(gOutFile, "%s\n", shell_help_messages[i]);
4628 } else {
4629 did_header = 0;
4630 jsval *argv = JS_ARGV(cx, vp);
4631 for (i = 0; i < argc; i++) {
4632 did_something = 0;
4633 type = JS_TypeOfValue(cx, argv[i]);
4634 if (type == JSTYPE_FUNCTION) {
4635 fun = JS_ValueToFunction(cx, argv[i]);
4636 str = fun->atom ? ATOM_TO_STRING(fun->atom) : NULL;
4637 } else if (type == JSTYPE_STRING) {
4638 str = JSVAL_TO_STRING(argv[i]);
4639 } else {
4640 str = NULL;
4642 if (str) {
4643 JSFlatString *flatStr = JS_FlattenString(cx, str);
4644 if (!flatStr)
4645 return JS_FALSE;
4646 for (j = 0; shell_functions[j].name; j++) {
4647 if (JS_FlatStringEqualsAscii(flatStr, shell_functions[j].name)) {
4648 if (!did_header) {
4649 did_header = 1;
4650 fputs(shell_help_header, gOutFile);
4652 did_something = 1;
4653 fprintf(gOutFile, "%s\n", shell_help_messages[j]);
4654 break;
4658 if (!did_something) {
4659 str = JS_ValueToString(cx, argv[i]);
4660 if (!str)
4661 return JS_FALSE;
4662 fputs("Sorry, no help for ", gErrFile);
4663 JS_FileEscapedString(gErrFile, str, 0);
4664 putc('\n', gErrFile);
4668 JS_SET_RVAL(cx, vp, JSVAL_VOID);
4669 return JS_TRUE;
4672 static JSObject *
4673 split_setup(JSContext *cx, JSBool evalcx)
4675 JSObject *outer, *inner, *arguments;
4677 outer = split_create_outer(cx);
4678 if (!outer)
4679 return NULL;
4680 AutoObjectRooter root(cx, outer);
4681 if (!evalcx)
4682 JS_SetGlobalObject(cx, outer);
4684 inner = split_create_inner(cx, outer);
4685 if (!inner)
4686 return NULL;
4688 if (!evalcx) {
4689 if (!JS_DefineFunctions(cx, inner, shell_functions) ||
4690 !JS_DefineProfilingFunctions(cx, inner)) {
4691 return NULL;
4694 /* Create a dummy arguments object. */
4695 arguments = JS_NewArrayObject(cx, 0, NULL);
4696 if (!arguments ||
4697 !JS_DefineProperty(cx, inner, "arguments", OBJECT_TO_JSVAL(arguments),
4698 NULL, NULL, 0)) {
4699 return NULL;
4703 JS_ClearScope(cx, outer);
4705 #ifndef LAZY_STANDARD_CLASSES
4706 if (!JS_InitStandardClasses(cx, inner))
4707 return NULL;
4708 #endif
4710 return inner;
4714 * Define a JS object called "it". Give it class operations that printf why
4715 * they're being called for tutorial purposes.
4717 enum its_tinyid {
4718 ITS_COLOR, ITS_HEIGHT, ITS_WIDTH, ITS_FUNNY, ITS_ARRAY, ITS_RDONLY,
4719 ITS_CUSTOM, ITS_CUSTOMRDONLY
4722 static JSBool
4723 its_getter(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
4725 jsval *val = (jsval *) JS_GetPrivate(cx, obj);
4726 *vp = val ? *val : JSVAL_VOID;
4727 return JS_TRUE;
4730 static JSBool
4731 its_setter(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp)
4733 jsval *val = (jsval *) JS_GetPrivate(cx, obj);
4734 if (val) {
4735 *val = *vp;
4736 return JS_TRUE;
4739 val = new jsval;
4740 if (!val) {
4741 JS_ReportOutOfMemory(cx);
4742 return JS_FALSE;
4745 if (!JS_AddValueRoot(cx, val)) {
4746 delete val;
4747 return JS_FALSE;
4750 if (!JS_SetPrivate(cx, obj, (void*)val)) {
4751 JS_RemoveValueRoot(cx, val);
4752 delete val;
4753 return JS_FALSE;
4756 *val = *vp;
4757 return JS_TRUE;
4760 static JSPropertySpec its_props[] = {
4761 {"color", ITS_COLOR, JSPROP_ENUMERATE, NULL, NULL},
4762 {"height", ITS_HEIGHT, JSPROP_ENUMERATE, NULL, NULL},
4763 {"width", ITS_WIDTH, JSPROP_ENUMERATE, NULL, NULL},
4764 {"funny", ITS_FUNNY, JSPROP_ENUMERATE, NULL, NULL},
4765 {"array", ITS_ARRAY, JSPROP_ENUMERATE, NULL, NULL},
4766 {"rdonly", ITS_RDONLY, JSPROP_READONLY, NULL, NULL},
4767 {"custom", ITS_CUSTOM, JSPROP_ENUMERATE,
4768 its_getter, its_setter},
4769 {"customRdOnly", ITS_CUSTOMRDONLY, JSPROP_ENUMERATE | JSPROP_READONLY,
4770 its_getter, its_setter},
4771 {NULL,0,0,NULL,NULL}
4774 static JSBool
4775 its_bindMethod(JSContext *cx, uintN argc, jsval *vp)
4777 JSString *name;
4778 JSObject *method;
4780 JSObject *thisobj = JS_THIS_OBJECT(cx, vp);
4782 if (!JS_ConvertArguments(cx, argc, JS_ARGV(cx, vp), "So", &name, &method))
4783 return JS_FALSE;
4785 *vp = OBJECT_TO_JSVAL(method);
4787 if (JS_TypeOfValue(cx, *vp) != JSTYPE_FUNCTION) {
4788 JSAutoByteString nameBytes(cx, name);
4789 if (!!nameBytes) {
4790 JSString *valstr = JS_ValueToString(cx, *vp);
4791 if (valstr) {
4792 JSAutoByteString valBytes(cx, valstr);
4793 if (!!valBytes) {
4794 JS_ReportError(cx, "can't bind method %s to non-callable object %s",
4795 nameBytes.ptr(), valBytes.ptr());
4799 return JS_FALSE;
4802 jsid id;
4803 if (!JS_ValueToId(cx, STRING_TO_JSVAL(name), &id))
4804 return JS_FALSE;
4806 if (!JS_DefinePropertyById(cx, thisobj, id, *vp, NULL, NULL, JSPROP_ENUMERATE))
4807 return JS_FALSE;
4809 return JS_SetParent(cx, method, thisobj);
4812 static JSFunctionSpec its_methods[] = {
4813 {"bindMethod", its_bindMethod, 2,0},
4814 {NULL,NULL,0,0}
4817 #ifdef JSD_LOWLEVEL_SOURCE
4819 * This facilitates sending source to JSD (the debugger system) in the shell
4820 * where the source is loaded using the JSFILE hack in jsscan. The function
4821 * below is used as a callback for the jsdbgapi JS_SetSourceHandler hook.
4822 * A more normal embedding (e.g. mozilla) loads source itself and can send
4823 * source directly to JSD without using this hook scheme.
4825 static void
4826 SendSourceToJSDebugger(const char *filename, uintN lineno,
4827 jschar *str, size_t length,
4828 void **listenerTSData, JSDContext* jsdc)
4830 JSDSourceText *jsdsrc = (JSDSourceText *) *listenerTSData;
4832 if (!jsdsrc) {
4833 if (!filename)
4834 filename = "typein";
4835 if (1 == lineno) {
4836 jsdsrc = JSD_NewSourceText(jsdc, filename);
4837 } else {
4838 jsdsrc = JSD_FindSourceForURL(jsdc, filename);
4839 if (jsdsrc && JSD_SOURCE_PARTIAL !=
4840 JSD_GetSourceStatus(jsdc, jsdsrc)) {
4841 jsdsrc = NULL;
4845 if (jsdsrc) {
4846 jsdsrc = JSD_AppendUCSourceText(jsdc,jsdsrc, str, length,
4847 JSD_SOURCE_PARTIAL);
4849 *listenerTSData = jsdsrc;
4851 #endif /* JSD_LOWLEVEL_SOURCE */
4853 static JSBool its_noisy; /* whether to be noisy when finalizing it */
4854 static JSBool its_enum_fail;/* whether to fail when enumerating it */
4856 static JSBool
4857 its_addProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
4859 if (!its_noisy)
4860 return JS_TRUE;
4862 IdToString idString(cx, id);
4863 fprintf(gOutFile, "adding its property %s,", idString.getBytes());
4864 ToString valueString(cx, *vp);
4865 fprintf(gOutFile, " initial value %s\n", valueString.getBytes());
4866 return JS_TRUE;
4869 static JSBool
4870 its_delProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
4872 if (!its_noisy)
4873 return JS_TRUE;
4875 IdToString idString(cx, id);
4876 fprintf(gOutFile, "deleting its property %s,", idString.getBytes());
4877 ToString valueString(cx, *vp);
4878 fprintf(gOutFile, " initial value %s\n", valueString.getBytes());
4879 return JS_TRUE;
4882 static JSBool
4883 its_getProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
4885 if (!its_noisy)
4886 return JS_TRUE;
4888 IdToString idString(cx, id);
4889 fprintf(gOutFile, "getting its property %s,", idString.getBytes());
4890 ToString valueString(cx, *vp);
4891 fprintf(gOutFile, " initial value %s\n", valueString.getBytes());
4892 return JS_TRUE;
4895 static JSBool
4896 its_setProperty(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp)
4898 IdToString idString(cx, id);
4899 if (its_noisy) {
4900 fprintf(gOutFile, "setting its property %s,", idString.getBytes());
4901 ToString valueString(cx, *vp);
4902 fprintf(gOutFile, " new value %s\n", valueString.getBytes());
4905 if (!JSID_IS_ATOM(id))
4906 return JS_TRUE;
4908 if (!strcmp(idString.getBytes(), "noisy"))
4909 JS_ValueToBoolean(cx, *vp, &its_noisy);
4910 else if (!strcmp(idString.getBytes(), "enum_fail"))
4911 JS_ValueToBoolean(cx, *vp, &its_enum_fail);
4913 return JS_TRUE;
4917 * Its enumerator, implemented using the "new" enumerate API,
4918 * see class flags.
4920 static JSBool
4921 its_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
4922 jsval *statep, jsid *idp)
4924 JSObject *iterator;
4926 switch (enum_op) {
4927 case JSENUMERATE_INIT:
4928 case JSENUMERATE_INIT_ALL:
4929 if (its_noisy)
4930 fprintf(gOutFile, "enumerate its properties\n");
4932 iterator = JS_NewPropertyIterator(cx, obj);
4933 if (!iterator)
4934 return JS_FALSE;
4936 *statep = OBJECT_TO_JSVAL(iterator);
4937 if (idp)
4938 *idp = INT_TO_JSID(0);
4939 break;
4941 case JSENUMERATE_NEXT:
4942 if (its_enum_fail) {
4943 JS_ReportError(cx, "its enumeration failed");
4944 return JS_FALSE;
4947 iterator = (JSObject *) JSVAL_TO_OBJECT(*statep);
4948 if (!JS_NextProperty(cx, iterator, idp))
4949 return JS_FALSE;
4951 if (!JSID_IS_VOID(*idp))
4952 break;
4953 /* Fall through. */
4955 case JSENUMERATE_DESTROY:
4956 /* Allow our iterator object to be GC'd. */
4957 *statep = JSVAL_NULL;
4958 break;
4961 return JS_TRUE;
4964 static JSBool
4965 its_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
4966 JSObject **objp)
4968 if (its_noisy) {
4969 IdToString idString(cx, id);
4970 fprintf(gOutFile, "resolving its property %s, flags {%s,%s,%s}\n",
4971 idString.getBytes(),
4972 (flags & JSRESOLVE_QUALIFIED) ? "qualified" : "",
4973 (flags & JSRESOLVE_ASSIGNING) ? "assigning" : "",
4974 (flags & JSRESOLVE_DETECTING) ? "detecting" : "");
4976 return JS_TRUE;
4979 static JSBool
4980 its_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
4982 if (its_noisy)
4983 fprintf(gOutFile, "converting it to %s type\n", JS_GetTypeName(cx, type));
4984 return JS_TRUE;
4987 static void
4988 its_finalize(JSContext *cx, JSObject *obj)
4990 jsval *rootedVal;
4991 if (its_noisy)
4992 fprintf(gOutFile, "finalizing it\n");
4993 rootedVal = (jsval *) JS_GetPrivate(cx, obj);
4994 if (rootedVal) {
4995 JS_RemoveValueRoot(cx, rootedVal);
4996 JS_SetPrivate(cx, obj, NULL);
4997 delete rootedVal;
5001 static JSClass its_class = {
5002 "It", JSCLASS_NEW_RESOLVE | JSCLASS_NEW_ENUMERATE | JSCLASS_HAS_PRIVATE,
5003 its_addProperty, its_delProperty, its_getProperty, its_setProperty,
5004 (JSEnumerateOp)its_enumerate, (JSResolveOp)its_resolve,
5005 its_convert, its_finalize,
5006 JSCLASS_NO_OPTIONAL_MEMBERS
5009 JSErrorFormatString jsShell_ErrorFormatString[JSErr_Limit] = {
5010 #define MSG_DEF(name, number, count, exception, format) \
5011 { format, count, JSEXN_ERR } ,
5012 #include "jsshell.msg"
5013 #undef MSG_DEF
5016 static const JSErrorFormatString *
5017 my_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber)
5019 if ((errorNumber > 0) && (errorNumber < JSShellErr_Limit))
5020 return &jsShell_ErrorFormatString[errorNumber];
5021 return NULL;
5024 static void
5025 my_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
5027 int i, j, k, n;
5028 char *prefix, *tmp;
5029 const char *ctmp;
5031 if (!report) {
5032 fprintf(gErrFile, "%s\n", message);
5033 return;
5036 /* Conditionally ignore reported warnings. */
5037 if (JSREPORT_IS_WARNING(report->flags) && !reportWarnings)
5038 return;
5040 prefix = NULL;
5041 if (report->filename)
5042 prefix = JS_smprintf("%s:", report->filename);
5043 if (report->lineno) {
5044 tmp = prefix;
5045 prefix = JS_smprintf("%s%u: ", tmp ? tmp : "", report->lineno);
5046 JS_free(cx, tmp);
5048 if (JSREPORT_IS_WARNING(report->flags)) {
5049 tmp = prefix;
5050 prefix = JS_smprintf("%s%swarning: ",
5051 tmp ? tmp : "",
5052 JSREPORT_IS_STRICT(report->flags) ? "strict " : "");
5053 JS_free(cx, tmp);
5056 /* embedded newlines -- argh! */
5057 while ((ctmp = strchr(message, '\n')) != 0) {
5058 ctmp++;
5059 if (prefix)
5060 fputs(prefix, gErrFile);
5061 fwrite(message, 1, ctmp - message, gErrFile);
5062 message = ctmp;
5065 /* If there were no filename or lineno, the prefix might be empty */
5066 if (prefix)
5067 fputs(prefix, gErrFile);
5068 fputs(message, gErrFile);
5070 if (!report->linebuf) {
5071 fputc('\n', gErrFile);
5072 goto out;
5075 /* report->linebuf usually ends with a newline. */
5076 n = strlen(report->linebuf);
5077 fprintf(gErrFile, ":\n%s%s%s%s",
5078 prefix,
5079 report->linebuf,
5080 (n > 0 && report->linebuf[n-1] == '\n') ? "" : "\n",
5081 prefix);
5082 n = report->tokenptr - report->linebuf;
5083 for (i = j = 0; i < n; i++) {
5084 if (report->linebuf[i] == '\t') {
5085 for (k = (j + 8) & ~7; j < k; j++) {
5086 fputc('.', gErrFile);
5088 continue;
5090 fputc('.', gErrFile);
5091 j++;
5093 fputs("^\n", gErrFile);
5094 out:
5095 if (!JSREPORT_IS_WARNING(report->flags)) {
5096 if (report->errorNumber == JSMSG_OUT_OF_MEMORY) {
5097 gExitCode = EXITCODE_OUT_OF_MEMORY;
5098 } else {
5099 gExitCode = EXITCODE_RUNTIME_ERROR;
5102 JS_free(cx, prefix);
5105 #if defined(SHELL_HACK) && defined(DEBUG) && defined(XP_UNIX)
5106 static JSBool
5107 Exec(JSContext *cx, uintN argc, jsval *vp)
5109 JSFunction *fun;
5110 const char *name, **nargv;
5111 uintN i, nargc;
5112 JSString *str;
5113 bool ok;
5114 pid_t pid;
5115 int status;
5117 JS_SET_RVAL(cx, vp, JSVAL_VOID);
5119 fun = JS_ValueToFunction(cx, vp[0]);
5120 if (!fun)
5121 return JS_FALSE;
5122 if (!fun->atom)
5123 return JS_TRUE;
5125 nargc = 1 + argc;
5127 /* nargc + 1 accounts for the terminating NULL. */
5128 nargv = new (char *)[nargc + 1];
5129 if (!nargv)
5130 return JS_FALSE;
5131 memset(nargv, 0, sizeof(nargv[0]) * (nargc + 1));
5132 nargv[0] = name;
5133 jsval *argv = JS_ARGV(cx, vp);
5134 for (i = 0; i < nargc; i++) {
5135 str = (i == 0) ? ATOM_TO_STRING(fun->atom) : JS_ValueToString(cx, argv[i-1]);
5136 if (!str) {
5137 ok = false;
5138 goto done;
5140 nargv[i] = JS_EncodeString(cx, str);
5141 if (!nargv[i]) {
5142 ok = false;
5143 goto done;
5146 pid = fork();
5147 switch (pid) {
5148 case -1:
5149 perror("js");
5150 break;
5151 case 0:
5152 (void) execvp(name, (char **)nargv);
5153 perror("js");
5154 exit(127);
5155 default:
5156 while (waitpid(pid, &status, 0) < 0 && errno == EINTR)
5157 continue;
5158 break;
5160 ok = true;
5162 done:
5163 for (i = 0; i < nargc; i++)
5164 JS_free(cx, nargv[i]);
5165 delete[] nargv;
5166 return ok;
5168 #endif
5170 static JSBool
5171 global_enumerate(JSContext *cx, JSObject *obj)
5173 #ifdef LAZY_STANDARD_CLASSES
5174 return JS_EnumerateStandardClasses(cx, obj);
5175 #else
5176 return JS_TRUE;
5177 #endif
5180 static JSBool
5181 global_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
5182 JSObject **objp)
5184 #ifdef LAZY_STANDARD_CLASSES
5185 JSBool resolved;
5187 if (!ResolveClass(cx, obj, id, &resolved))
5188 return JS_FALSE;
5189 if (resolved) {
5190 *objp = obj;
5191 return JS_TRUE;
5193 #endif
5195 #if defined(SHELL_HACK) && defined(DEBUG) && defined(XP_UNIX)
5196 if (!(flags & JSRESOLVE_QUALIFIED)) {
5198 * Do this expensive hack only for unoptimized Unix builds, which are
5199 * not used for benchmarking.
5201 char *path, *comp, *full;
5202 const char *name;
5203 JSBool ok, found;
5204 JSFunction *fun;
5206 if (!JSVAL_IS_STRING(id))
5207 return JS_TRUE;
5208 path = getenv("PATH");
5209 if (!path)
5210 return JS_TRUE;
5211 path = JS_strdup(cx, path);
5212 if (!path)
5213 return JS_FALSE;
5214 JSAutoByteString name(cx, JSVAL_TO_STRING(id));
5215 if (!name)
5216 return JS_FALSE;
5217 ok = JS_TRUE;
5218 for (comp = strtok(path, ":"); comp; comp = strtok(NULL, ":")) {
5219 if (*comp != '\0') {
5220 full = JS_smprintf("%s/%s", comp, name.ptr());
5221 if (!full) {
5222 JS_ReportOutOfMemory(cx);
5223 ok = JS_FALSE;
5224 break;
5226 } else {
5227 full = (char *)name;
5229 found = (access(full, X_OK) == 0);
5230 if (*comp != '\0')
5231 free(full);
5232 if (found) {
5233 fun = JS_DefineFunction(cx, obj, name, Exec, 0,
5234 JSPROP_ENUMERATE);
5235 ok = (fun != NULL);
5236 if (ok)
5237 *objp = obj;
5238 break;
5241 JS_free(cx, path);
5242 return ok;
5244 #else
5245 return JS_TRUE;
5246 #endif
5249 JSClass global_class = {
5250 "global", JSCLASS_NEW_RESOLVE | JSCLASS_GLOBAL_FLAGS | JSCLASS_HAS_PRIVATE,
5251 JS_PropertyStub, JS_PropertyStub,
5252 JS_PropertyStub, JS_StrictPropertyStub,
5253 global_enumerate, (JSResolveOp) global_resolve,
5254 JS_ConvertStub, its_finalize,
5255 JSCLASS_NO_OPTIONAL_MEMBERS
5258 static JSBool
5259 env_setProperty(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp)
5261 /* XXX porting may be easy, but these don't seem to supply setenv by default */
5262 #if !defined XP_BEOS && !defined XP_OS2 && !defined SOLARIS
5263 int rv;
5265 IdToString idstr(cx, id, JS_TRUE);
5266 if (idstr.threw())
5267 return JS_FALSE;
5268 ToString valstr(cx, *vp, JS_TRUE);
5269 if (valstr.threw())
5270 return JS_FALSE;
5271 #if defined XP_WIN || defined HPUX || defined OSF1 || defined IRIX
5273 char *waste = JS_smprintf("%s=%s", idstr.getBytes(), valstr.getBytes());
5274 if (!waste) {
5275 JS_ReportOutOfMemory(cx);
5276 return JS_FALSE;
5278 rv = putenv(waste);
5279 #ifdef XP_WIN
5281 * HPUX9 at least still has the bad old non-copying putenv.
5283 * Per mail from <s.shanmuganathan@digital.com>, OSF1 also has a putenv
5284 * that will crash if you pass it an auto char array (so it must place
5285 * its argument directly in the char *environ[] array).
5287 JS_smprintf_free(waste);
5288 #endif
5290 #else
5291 rv = setenv(idstr.getBytes(), valstr.getBytes(), 1);
5292 #endif
5293 if (rv < 0) {
5294 JS_ReportError(cx, "can't set env variable %s to %s", idstr.getBytes(), valstr.getBytes());
5295 return JS_FALSE;
5297 *vp = valstr.getJSVal();
5298 #endif /* !defined XP_BEOS && !defined XP_OS2 && !defined SOLARIS */
5299 return JS_TRUE;
5302 static JSBool
5303 env_enumerate(JSContext *cx, JSObject *obj)
5305 static JSBool reflected;
5306 char **evp, *name, *value;
5307 JSString *valstr;
5308 JSBool ok;
5310 if (reflected)
5311 return JS_TRUE;
5313 for (evp = (char **)JS_GetPrivate(cx, obj); (name = *evp) != NULL; evp++) {
5314 value = strchr(name, '=');
5315 if (!value)
5316 continue;
5317 *value++ = '\0';
5318 valstr = JS_NewStringCopyZ(cx, value);
5319 if (!valstr) {
5320 ok = JS_FALSE;
5321 } else {
5322 ok = JS_DefineProperty(cx, obj, name, STRING_TO_JSVAL(valstr),
5323 NULL, NULL, JSPROP_ENUMERATE);
5325 value[-1] = '=';
5326 if (!ok)
5327 return JS_FALSE;
5330 reflected = JS_TRUE;
5331 return JS_TRUE;
5334 static JSBool
5335 env_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
5336 JSObject **objp)
5338 JSString *valstr;
5339 const char *name, *value;
5341 if (flags & JSRESOLVE_ASSIGNING)
5342 return JS_TRUE;
5344 IdToString idstr(cx, id, JS_TRUE);
5345 if (idstr.threw())
5346 return JS_FALSE;
5348 name = idstr.getBytes();
5349 value = getenv(name);
5350 if (value) {
5351 valstr = JS_NewStringCopyZ(cx, value);
5352 if (!valstr)
5353 return JS_FALSE;
5354 if (!JS_DefineProperty(cx, obj, name, STRING_TO_JSVAL(valstr),
5355 NULL, NULL, JSPROP_ENUMERATE)) {
5356 return JS_FALSE;
5358 *objp = obj;
5360 return JS_TRUE;
5363 static JSClass env_class = {
5364 "environment", JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE,
5365 JS_PropertyStub, JS_PropertyStub,
5366 JS_PropertyStub, env_setProperty,
5367 env_enumerate, (JSResolveOp) env_resolve,
5368 JS_ConvertStub, NULL,
5369 JSCLASS_NO_OPTIONAL_MEMBERS
5373 * Avoid a reentrancy hazard.
5375 * The non-JS_THREADSAFE shell uses a signal handler to implement timeout().
5376 * The JS engine is not really reentrant, but JS_TriggerAllOperationCallbacks
5377 * is mostly safe--the only danger is that we might interrupt JS_NewContext or
5378 * JS_DestroyContext while the context list is being modified. Therefore we
5379 * disable the signal handler around calls to those functions.
5381 #ifdef JS_THREADSAFE
5382 # define WITH_SIGNALS_DISABLED(x) x
5383 #else
5384 # define WITH_SIGNALS_DISABLED(x) \
5385 JS_BEGIN_MACRO \
5386 ScheduleWatchdog(gRuntime, -1); \
5387 x; \
5388 ScheduleWatchdog(gRuntime, gTimeoutInterval); \
5389 JS_END_MACRO
5390 #endif
5392 static JSContext *
5393 NewContext(JSRuntime *rt)
5395 JSContext *cx;
5396 WITH_SIGNALS_DISABLED(cx = JS_NewContext(rt, gStackChunkSize));
5397 if (!cx)
5398 return NULL;
5400 JSShellContextData *data = NewContextData();
5401 if (!data) {
5402 DestroyContext(cx, false);
5403 return NULL;
5406 JS_SetContextPrivate(cx, data);
5407 JS_SetErrorReporter(cx, my_ErrorReporter);
5408 JS_SetVersion(cx, JSVERSION_LATEST);
5409 SetContextOptions(cx);
5410 if (enableTraceJit)
5411 JS_ToggleOptions(cx, JSOPTION_JIT);
5412 if (enableMethodJit)
5413 JS_ToggleOptions(cx, JSOPTION_METHODJIT);
5414 return cx;
5417 static void
5418 DestroyContext(JSContext *cx, bool withGC)
5420 JSShellContextData *data = GetContextData(cx);
5421 JS_SetContextPrivate(cx, NULL);
5422 free(data);
5423 WITH_SIGNALS_DISABLED(withGC ? JS_DestroyContext(cx) : JS_DestroyContextNoGC(cx));
5426 static JSObject *
5427 NewGlobalObject(JSContext *cx)
5429 JSObject *glob = JS_NewCompartmentAndGlobalObject(cx, &global_class, NULL);
5430 if (!glob)
5431 return NULL;
5433 JSAutoEnterCompartment ac;
5434 if (!ac.enter(cx, glob))
5435 return NULL;
5437 #ifdef LAZY_STANDARD_CLASSES
5438 JS_SetGlobalObject(cx, glob);
5439 #else
5440 if (!JS_InitStandardClasses(cx, glob))
5441 return NULL;
5442 #endif
5443 #ifdef JS_HAS_CTYPES
5444 if (!JS_InitCTypesClass(cx, glob))
5445 return NULL;
5446 #endif
5447 if (!JS::RegisterPerfMeasurement(cx, glob))
5448 return NULL;
5449 if (!JS_DefineFunctions(cx, glob, shell_functions) ||
5450 !JS_DefineProfilingFunctions(cx, glob)) {
5451 return NULL;
5454 JSObject *it = JS_DefineObject(cx, glob, "it", &its_class, NULL, 0);
5455 if (!it)
5456 return NULL;
5457 if (!JS_DefineProperties(cx, it, its_props))
5458 return NULL;
5459 if (!JS_DefineFunctions(cx, it, its_methods))
5460 return NULL;
5462 if (!JS_DefineProperty(cx, glob, "custom", JSVAL_VOID, its_getter,
5463 its_setter, 0))
5464 return NULL;
5465 if (!JS_DefineProperty(cx, glob, "customRdOnly", JSVAL_VOID, its_getter,
5466 its_setter, JSPROP_READONLY))
5467 return NULL;
5469 return glob;
5473 Shell(JSContext *cx, int argc, char **argv, char **envp)
5475 JSAutoRequest ar(cx);
5477 JSObject *glob = NewGlobalObject(cx);
5478 if (!glob)
5479 return 1;
5481 JSAutoEnterCompartment ac;
5482 if (!ac.enter(cx, glob))
5483 return 1;
5485 JSObject *envobj = JS_DefineObject(cx, glob, "environment", &env_class, NULL, 0);
5486 if (!envobj || !JS_SetPrivate(cx, envobj, envp))
5487 return 1;
5489 #ifdef JSDEBUGGER
5491 * XXX A command line option to enable debugging (or not) would be good
5493 jsdc = JSD_DebuggerOnForUser(rt, NULL, NULL);
5494 if (!jsdc)
5495 return 1;
5496 JSD_JSContextInUse(jsdc, cx);
5497 #ifdef JSD_LOWLEVEL_SOURCE
5498 JS_SetSourceHandler(rt, SendSourceToJSDebugger, jsdc);
5499 #endif /* JSD_LOWLEVEL_SOURCE */
5500 #ifdef JSDEBUGGER_JAVA_UI
5501 jsdjc = JSDJ_CreateContext();
5502 if (! jsdjc)
5503 return 1;
5504 JSDJ_SetJSDContext(jsdjc, jsdc);
5505 java_env = JSDJ_CreateJavaVMAndStartDebugger(jsdjc);
5507 * XXX This would be the place to wait for the debugger to start.
5508 * Waiting would be nice in general, but especially when a js file
5509 * is passed on the cmd line.
5511 #endif /* JSDEBUGGER_JAVA_UI */
5512 #ifdef JSDEBUGGER_C_UI
5513 jsdbc = JSDB_InitDebugger(rt, jsdc, 0);
5514 #endif /* JSDEBUGGER_C_UI */
5515 #endif /* JSDEBUGGER */
5517 #ifdef JS_THREADSAFE
5518 class ShellWorkerHooks : public js::workers::WorkerHooks {
5519 public:
5520 JSObject *newGlobalObject(JSContext *cx) {
5521 return NewGlobalObject(cx);
5524 ShellWorkerHooks hooks;
5525 if (!JS_AddNamedObjectRoot(cx, &gWorkers, "Workers") ||
5526 (gWorkerThreadPool = js::workers::init(cx, &hooks, glob, &gWorkers)) == NULL) {
5527 return 1;
5529 #endif
5531 int result = ProcessArgs(cx, glob, argv, argc);
5533 #ifdef JS_THREADSAFE
5534 js::workers::finish(cx, gWorkerThreadPool);
5535 JS_RemoveObjectRoot(cx, &gWorkers);
5536 if (result == 0)
5537 result = gExitCode;
5538 #endif
5540 #ifdef JSDEBUGGER
5541 if (jsdc) {
5542 #ifdef JSDEBUGGER_C_UI
5543 if (jsdbc)
5544 JSDB_TermDebugger(jsdc);
5545 #endif /* JSDEBUGGER_C_UI */
5546 JSD_DebuggerOff(jsdc);
5548 #endif /* JSDEBUGGER */
5550 return result;
5553 static void
5554 MaybeOverrideOutFileFromEnv(const char* const envVar,
5555 FILE* defaultOut,
5556 FILE** outFile)
5558 const char* outPath = getenv(envVar);
5559 if (!outPath || !*outPath || !(*outFile = fopen(outPath, "w"))) {
5560 *outFile = defaultOut;
5565 main(int argc, char **argv, char **envp)
5567 int stackDummy;
5568 JSRuntime *rt;
5569 JSContext *cx;
5570 int result;
5571 #ifdef JSDEBUGGER
5572 JSDContext *jsdc;
5573 #ifdef JSDEBUGGER_JAVA_UI
5574 JNIEnv *java_env;
5575 JSDJContext *jsdjc;
5576 #endif
5577 #ifdef JSDEBUGGER_C_UI
5578 JSBool jsdbc;
5579 #endif /* JSDEBUGGER_C_UI */
5580 #endif /* JSDEBUGGER */
5581 #ifdef XP_WIN
5583 const char *crash_option = getenv("XRE_NO_WINDOWS_CRASH_DIALOG");
5584 if (crash_option && strncmp(crash_option, "1", 1)) {
5585 DWORD oldmode = SetErrorMode(SEM_NOGPFAULTERRORBOX);
5586 SetErrorMode(oldmode | SEM_NOGPFAULTERRORBOX);
5589 #endif
5591 CheckHelpMessages();
5592 #ifdef HAVE_SETLOCALE
5593 setlocale(LC_ALL, "");
5594 #endif
5596 #ifdef JS_THREADSAFE
5597 if (PR_FAILURE == PR_NewThreadPrivateIndex(&gStackBaseThreadIndex, NULL) ||
5598 PR_FAILURE == PR_SetThreadPrivate(gStackBaseThreadIndex, &stackDummy)) {
5599 return 1;
5601 #else
5602 gStackBase = (jsuword) &stackDummy;
5603 #endif
5605 #ifdef XP_OS2
5606 /* these streams are normally line buffered on OS/2 and need a \n, *
5607 * so we need to unbuffer then to get a reasonable prompt */
5608 setbuf(stdout,0);
5609 setbuf(stderr,0);
5610 #endif
5612 MaybeOverrideOutFileFromEnv("JS_STDERR", stderr, &gErrFile);
5613 MaybeOverrideOutFileFromEnv("JS_STDOUT", stdout, &gOutFile);
5615 argc--;
5616 argv++;
5618 #ifdef XP_WIN
5619 // Set the timer calibration delay count to 0 so we get high
5620 // resolution right away, which we need for precise benchmarking.
5621 extern int CALIBRATION_DELAY_COUNT;
5622 CALIBRATION_DELAY_COUNT = 0;
5623 #endif
5625 rt = JS_NewRuntime(160L * 1024L * 1024L);
5626 if (!rt)
5627 return 1;
5629 if (!InitWatchdog(rt))
5630 return 1;
5632 cx = NewContext(rt);
5633 if (!cx)
5634 return 1;
5636 JS_SetOptions(cx, JS_GetOptions(cx) | JSOPTION_ANONFUNFIX);
5637 JS_SetGCParameterForThread(cx, JSGC_MAX_CODE_CACHE_BYTES, 16 * 1024 * 1024);
5639 result = Shell(cx, argc, argv, envp);
5641 DestroyContext(cx, true);
5643 KillWatchdog();
5645 JS_DestroyRuntime(rt);
5646 JS_ShutDown();
5647 return result;