Bug 559408: Arena pool macros to methods. (r=gal)
[mozilla-central.git] / js / src / shell / js.cpp
blob9f38732ac8578c547f4d07d412f8b23a2e38710e
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 ***** */
42 * JS shell.
44 #include <errno.h>
45 #include <math.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <signal.h>
50 #include <locale.h>
51 #include "jstypes.h"
52 #include "jsstdint.h"
53 #include "jsarena.h"
54 #include "jsutil.h"
55 #include "jsprf.h"
56 #include "jsapi.h"
57 #include "jsarray.h"
58 #include "jsatom.h"
59 #include "jsbuiltins.h"
60 #include "jscntxt.h"
61 #include "jsdate.h"
62 #include "jsdbgapi.h"
63 #include "jsemit.h"
64 #include "jsfun.h"
65 #include "jsgc.h"
66 #include "jsiter.h"
67 #include "jslock.h"
68 #include "jsnum.h"
69 #include "jsobj.h"
70 #include "jsparse.h"
71 #include "jsscope.h"
72 #include "jsscript.h"
73 #include "jstracer.h"
75 #include "prmjtime.h"
77 #ifdef JSDEBUGGER
78 #include "jsdebug.h"
79 #ifdef JSDEBUGGER_JAVA_UI
80 #include "jsdjava.h"
81 #endif /* JSDEBUGGER_JAVA_UI */
82 #ifdef JSDEBUGGER_C_UI
83 #include "jsdb.h"
84 #endif /* JSDEBUGGER_C_UI */
85 #endif /* JSDEBUGGER */
87 #include "jsworkers.h"
89 #ifdef XP_UNIX
90 #include <unistd.h>
91 #include <sys/types.h>
92 #include <sys/wait.h>
93 #endif
95 #if defined(XP_WIN) || defined(XP_OS2)
96 #include <io.h> /* for isatty() */
97 #endif
99 #ifdef XP_WIN
100 #include <windows.h>
101 #endif
103 using namespace js;
105 typedef enum JSShellExitCode {
106 EXITCODE_RUNTIME_ERROR = 3,
107 EXITCODE_FILE_NOT_FOUND = 4,
108 EXITCODE_OUT_OF_MEMORY = 5,
109 EXITCODE_TIMEOUT = 6
110 } JSShellExitCode;
112 size_t gStackChunkSize = 8192;
114 /* Assume that we can not use more than 5e5 bytes of C stack by default. */
115 #if defined(DEBUG) && defined(__SUNPRO_CC)
116 /* Sun compiler uses larger stack space for js_Interpret() with debug
117 Use a bigger gMaxStackSize to make "make check" happy. */
118 static size_t gMaxStackSize = 5000000;
119 #else
120 static size_t gMaxStackSize = 500000;
121 #endif
124 #ifdef JS_THREADSAFE
125 static PRUintn gStackBaseThreadIndex;
126 #else
127 static jsuword gStackBase;
128 #endif
130 static size_t gScriptStackQuota = JS_DEFAULT_SCRIPT_STACK_QUOTA;
133 * Limit the timeout to 30 minutes to prevent an overflow on platfoms
134 * that represent the time internally in microseconds using 32-bit int.
136 static jsdouble MAX_TIMEOUT_INTERVAL = 1800.0;
137 static jsdouble gTimeoutInterval = -1.0;
138 static volatile bool gCanceled = false;
140 static bool enableJit = false;
142 static JSBool
143 SetTimeoutValue(JSContext *cx, jsdouble t);
145 static bool
146 InitWatchdog(JSRuntime *rt);
148 static void
149 KillWatchdog();
151 static bool
152 ScheduleWatchdog(JSRuntime *rt, jsdouble t);
154 static void
155 CancelExecution(JSRuntime *rt);
158 * Watchdog thread state.
160 #ifdef JS_THREADSAFE
162 static PRLock *gWatchdogLock = NULL;
163 static PRCondVar *gWatchdogWakeup = NULL;
164 static PRThread *gWatchdogThread = NULL;
165 static bool gWatchdogHasTimeout = false;
166 static PRIntervalTime gWatchdogTimeout = 0;
168 static PRCondVar *gSleepWakeup = NULL;
170 #else
172 static JSRuntime *gRuntime = NULL;
174 #endif
176 int gExitCode = 0;
177 JSBool gQuitting = JS_FALSE;
178 FILE *gErrFile = NULL;
179 FILE *gOutFile = NULL;
180 #ifdef JS_THREADSAFE
181 JSObject *gWorkers = NULL;
182 #endif
184 static JSBool reportWarnings = JS_TRUE;
185 static JSBool compileOnly = JS_FALSE;
187 typedef enum JSShellErrNum {
188 #define MSG_DEF(name, number, count, exception, format) \
189 name = number,
190 #include "jsshell.msg"
191 #undef MSG_DEF
192 JSShellErr_Limit
193 #undef MSGDEF
194 } JSShellErrNum;
196 static JSContext *
197 NewContext(JSRuntime *rt);
199 static void
200 DestroyContext(JSContext *cx, bool withGC);
202 static const JSErrorFormatString *
203 my_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber);
205 static JSObject *
206 split_setup(JSContext *cx, JSBool evalcx);
208 #ifdef EDITLINE
209 JS_BEGIN_EXTERN_C
210 JS_EXTERN_API(char) *readline(const char *prompt);
211 JS_EXTERN_API(void) add_history(char *line);
212 JS_END_EXTERN_C
213 #endif
215 class ToString {
216 public:
217 ToString(JSContext *aCx, jsval v, JSBool aThrow = JS_FALSE)
218 : cx(aCx)
219 , mThrow(aThrow)
221 mStr = JS_ValueToString(cx, v);
222 if (!aThrow && !mStr && JS_IsExceptionPending(cx)) {
223 if (!JS_ReportPendingException(cx))
224 JS_ClearPendingException(cx);
226 JS_AddNamedRoot(cx, &mStr, "Value ToString helper");
228 ~ToString() {
229 JS_RemoveRoot(cx, &mStr);
231 JSBool threw() { return !mStr; }
232 jsval getJSVal() { return STRING_TO_JSVAL(mStr); }
233 const char *getBytes() {
234 return mStr ? JS_GetStringBytes(mStr) : "(error converting value)";
236 private:
237 JSContext *cx;
238 JSString *mStr;
239 JSBool mThrow;
242 static char *
243 GetLine(FILE *file, const char * prompt)
245 size_t size;
246 char *buffer;
247 #ifdef EDITLINE
249 * Use readline only if file is stdin, because there's no way to specify
250 * another handle. Are other filehandles interactive?
252 if (file == stdin) {
253 char *linep = readline(prompt);
255 * We set it to zero to avoid complaining about inappropriate ioctl
256 * for device in the case of EOF. Looks like errno == 251 if line is
257 * finished with EOF and errno == 25 (EINVAL on Mac) if there is
258 * nothing left to read.
260 if (errno == 251 || errno == 25 || errno == EINVAL)
261 errno = 0;
262 if (!linep)
263 return NULL;
264 if (linep[0] != '\0')
265 add_history(linep);
266 return linep;
268 #endif
269 size_t len = 0;
270 if (*prompt != '\0') {
271 fprintf(gOutFile, "%s", prompt);
272 fflush(gOutFile);
274 size = 80;
275 buffer = (char *) malloc(size);
276 if (!buffer)
277 return NULL;
278 char *current = buffer;
279 while (fgets(current, size - len, file)) {
280 len += strlen(current);
281 char *t = buffer + len - 1;
282 if (*t == '\n') {
283 /* Line was read. We remove '\n' and exit. */
284 *t = '\0';
285 return buffer;
287 if (len + 1 == size) {
288 size = size * 2;
289 char *tmp = (char *) realloc(buffer, size);
290 if (!tmp) {
291 free(buffer);
292 return NULL;
294 buffer = tmp;
296 current = buffer + len;
298 if (len && !ferror(file))
299 return buffer;
300 free(buffer);
301 return NULL;
305 * State to store as JSContext private.
307 * We declare such timestamp as volatile as they are updated in the operation
308 * callback without taking any locks. Any possible race can only lead to more
309 * frequent callback calls. This is safe as the callback does everything based
310 * on timing.
312 struct JSShellContextData {
313 volatile JSIntervalTime startTime;
316 static JSShellContextData *
317 NewContextData()
319 /* Prevent creation of new contexts after we have been canceled. */
320 if (gCanceled)
321 return NULL;
323 JSShellContextData *data = (JSShellContextData *)
324 calloc(sizeof(JSShellContextData), 1);
325 if (!data)
326 return NULL;
327 data->startTime = js_IntervalNow();
328 return data;
331 static inline JSShellContextData *
332 GetContextData(JSContext *cx)
334 JSShellContextData *data = (JSShellContextData *) JS_GetContextPrivate(cx);
336 JS_ASSERT(data);
337 return data;
340 static JSBool
341 ShellOperationCallback(JSContext *cx)
343 if (!gCanceled)
344 return JS_TRUE;
346 JS_ClearPendingException(cx);
347 return JS_FALSE;
350 static void
351 SetThreadStackLimit(JSContext *cx)
353 jsuword stackLimit;
355 if (gMaxStackSize == 0) {
357 * Disable checking for stack overflow if limit is zero.
359 stackLimit = 0;
360 } else {
361 jsuword stackBase;
362 #ifdef JS_THREADSAFE
363 stackBase = (jsuword) PR_GetThreadPrivate(gStackBaseThreadIndex);
364 #else
365 stackBase = gStackBase;
366 #endif
367 if (stackBase) {
368 #if JS_STACK_GROWTH_DIRECTION > 0
369 stackLimit = stackBase + gMaxStackSize;
370 #else
371 stackLimit = stackBase - gMaxStackSize;
372 #endif
373 } else {
374 stackLimit = 0;
377 JS_SetThreadStackLimit(cx, stackLimit);
381 static void
382 SetContextOptions(JSContext *cx)
384 SetThreadStackLimit(cx);
385 JS_SetScriptStackQuota(cx, gScriptStackQuota);
386 JS_SetOperationCallback(cx, ShellOperationCallback);
389 #ifdef WINCE
390 int errno;
391 #endif
393 static void
394 Process(JSContext *cx, JSObject *obj, char *filename, JSBool forceTTY)
396 JSBool ok, hitEOF;
397 JSScript *script;
398 jsval result;
399 JSString *str;
400 char *buffer;
401 size_t size;
402 int lineno;
403 int startline;
404 FILE *file;
405 uint32 oldopts;
407 if (forceTTY || !filename || strcmp(filename, "-") == 0) {
408 file = stdin;
409 } else {
410 file = fopen(filename, "r");
411 if (!file) {
412 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
413 JSSMSG_CANT_OPEN, filename, strerror(errno));
414 gExitCode = EXITCODE_FILE_NOT_FOUND;
415 return;
419 SetContextOptions(cx);
421 #ifndef WINCE
422 /* windows mobile (and possibly other os's) does not have a TTY */
423 if (!forceTTY && !isatty(fileno(file)))
424 #endif
427 * It's not interactive - just execute it.
429 * Support the UNIX #! shell hack; gobble the first line if it starts
430 * with '#'. TODO - this isn't quite compatible with sharp variables,
431 * as a legal js program (using sharp variables) might start with '#'.
432 * But that would require multi-character lookahead.
434 int ch = fgetc(file);
435 if (ch == '#') {
436 while((ch = fgetc(file)) != EOF) {
437 if (ch == '\n' || ch == '\r')
438 break;
441 ungetc(ch, file);
443 oldopts = JS_GetOptions(cx);
444 JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO | JSOPTION_NO_SCRIPT_RVAL);
445 script = JS_CompileFileHandle(cx, obj, filename, file);
446 JS_SetOptions(cx, oldopts);
447 if (script) {
448 if (!compileOnly)
449 (void)JS_ExecuteScript(cx, obj, script, NULL);
450 JS_DestroyScript(cx, script);
453 if (file != stdin)
454 fclose(file);
455 return;
458 /* It's an interactive filehandle; drop into read-eval-print loop. */
459 lineno = 1;
460 hitEOF = JS_FALSE;
461 buffer = NULL;
462 size = 0; /* assign here to avoid warnings */
463 do {
465 * Accumulate lines until we get a 'compilable unit' - one that either
466 * generates an error (before running out of source) or that compiles
467 * cleanly. This should be whenever we get a complete statement that
468 * coincides with the end of a line.
470 startline = lineno;
471 size_t len = 0; /* initialize to avoid warnings */
472 do {
473 ScheduleWatchdog(cx->runtime, -1);
474 gCanceled = false;
475 errno = 0;
477 char *line;
479 JSAutoSuspendRequest suspended(cx);
480 line = GetLine(file, startline == lineno ? "js> " : "");
482 if (!line) {
483 if (errno) {
484 JS_ReportError(cx, strerror(errno));
485 free(buffer);
486 return;
488 hitEOF = JS_TRUE;
489 break;
491 if (!buffer) {
492 buffer = line;
493 len = strlen(buffer);
494 size = len + 1;
495 } else {
497 * len + 1 is required to store '\n' in the end of line.
499 size_t newlen = strlen(line) + (len ? len + 1 : 0);
500 if (newlen + 1 > size) {
501 size = newlen + 1 > size * 2 ? newlen + 1 : size * 2;
502 char *newBuf = (char *) realloc(buffer, size);
503 if (!newBuf) {
504 free(buffer);
505 free(line);
506 JS_ReportOutOfMemory(cx);
507 return;
509 buffer = newBuf;
511 char *current = buffer + len;
512 if (startline != lineno)
513 *current++ = '\n';
514 strcpy(current, line);
515 len = newlen;
516 free(line);
518 lineno++;
519 if (!ScheduleWatchdog(cx->runtime, gTimeoutInterval)) {
520 hitEOF = JS_TRUE;
521 break;
523 } while (!JS_BufferIsCompilableUnit(cx, obj, buffer, len));
525 if (hitEOF && !buffer)
526 break;
528 /* Clear any pending exception from previous failed compiles. */
529 JS_ClearPendingException(cx);
531 /* Even though we're interactive, we have a compile-n-go opportunity. */
532 oldopts = JS_GetOptions(cx);
533 if (!compileOnly)
534 JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO);
535 script = JS_CompileScript(cx, obj, buffer, len, "typein",
536 startline);
537 if (!compileOnly)
538 JS_SetOptions(cx, oldopts);
540 if (script) {
541 if (!compileOnly) {
542 ok = JS_ExecuteScript(cx, obj, script, &result);
543 if (ok && !JSVAL_IS_VOID(result)) {
544 str = JS_ValueToSource(cx, result);
545 if (str)
546 fprintf(gOutFile, "%s\n", JS_GetStringBytes(str));
547 else
548 ok = JS_FALSE;
551 JS_DestroyScript(cx, script);
553 *buffer = '\0';
554 } while (!hitEOF && !gQuitting);
556 free(buffer);
557 fprintf(gOutFile, "\n");
558 if (file != stdin)
559 fclose(file);
560 return;
563 static int
564 usage(void)
566 fprintf(gErrFile, "%s\n", JS_GetImplementationVersion());
567 fprintf(gErrFile, "usage: js [-zKPswWxCij] [-t timeoutSeconds] [-c stackchunksize] [-o option] [-v version] [-f scriptfile] [-e script] [-S maxstacksize] "
568 #ifdef JS_GC_ZEAL
569 "[-Z gczeal] "
570 #endif
571 #ifdef MOZ_TRACEVIS
572 "[-T TraceVisFileName] "
573 #endif
574 "[scriptfile] [scriptarg...]\n");
575 return 2;
579 * JSContext option name to flag map. The option names are in alphabetical
580 * order for better reporting.
582 static const struct {
583 const char *name;
584 uint32 flag;
585 } js_options[] = {
586 {"anonfunfix", JSOPTION_ANONFUNFIX},
587 {"atline", JSOPTION_ATLINE},
588 {"jit", JSOPTION_JIT},
589 {"relimit", JSOPTION_RELIMIT},
590 {"strict", JSOPTION_STRICT},
591 {"werror", JSOPTION_WERROR},
592 {"xml", JSOPTION_XML},
595 static uint32
596 MapContextOptionNameToFlag(JSContext* cx, const char* name)
598 for (size_t i = 0; i != JS_ARRAY_LENGTH(js_options); ++i) {
599 if (strcmp(name, js_options[i].name) == 0)
600 return js_options[i].flag;
603 char* msg = JS_sprintf_append(NULL,
604 "unknown option name '%s'."
605 " The valid names are ", name);
606 for (size_t i = 0; i != JS_ARRAY_LENGTH(js_options); ++i) {
607 if (!msg)
608 break;
609 msg = JS_sprintf_append(msg, "%s%s", js_options[i].name,
610 (i + 2 < JS_ARRAY_LENGTH(js_options)
611 ? ", "
612 : i + 2 == JS_ARRAY_LENGTH(js_options)
613 ? " and "
614 : "."));
616 if (!msg) {
617 JS_ReportOutOfMemory(cx);
618 } else {
619 JS_ReportError(cx, msg);
620 free(msg);
622 return 0;
625 extern JSClass global_class;
627 #if defined(JS_TRACER) && defined(DEBUG)
628 namespace js {
629 extern struct JSClass jitstats_class;
630 void InitJITStatsClass(JSContext *cx, JSObject *glob);
632 #endif
634 static int
635 ProcessArgs(JSContext *cx, JSObject *obj, char **argv, int argc)
637 int i, j, length;
638 JSObject *argsObj;
639 char *filename = NULL;
640 JSBool isInteractive = JS_TRUE;
641 JSBool forceTTY = JS_FALSE;
644 * Scan past all optional arguments so we can create the arguments object
645 * before processing any -f options, which must interleave properly with
646 * -v and -w options. This requires two passes, and without getopt, we'll
647 * have to keep the option logic here and in the second for loop in sync.
649 for (i = 0; i < argc; i++) {
650 if (argv[i][0] != '-' || argv[i][1] == '\0') {
651 ++i;
652 break;
654 switch (argv[i][1]) {
655 case 'c':
656 case 'f':
657 case 'e':
658 case 'v':
659 case 'S':
660 case 't':
661 #ifdef JS_GC_ZEAL
662 case 'Z':
663 #endif
664 #ifdef MOZ_TRACEVIS
665 case 'T':
666 #endif
667 ++i;
668 break;
669 default:;
674 * Create arguments early and define it to root it, so it's safe from any
675 * GC calls nested below, and so it is available to -f <file> arguments.
677 argsObj = JS_NewArrayObject(cx, 0, NULL);
678 if (!argsObj)
679 return 1;
680 if (!JS_DefineProperty(cx, obj, "arguments", OBJECT_TO_JSVAL(argsObj),
681 NULL, NULL, 0)) {
682 return 1;
685 length = argc - i;
686 for (j = 0; j < length; j++) {
687 JSString *str = JS_NewStringCopyZ(cx, argv[i++]);
688 if (!str)
689 return 1;
690 if (!JS_DefineElement(cx, argsObj, j, STRING_TO_JSVAL(str),
691 NULL, NULL, JSPROP_ENUMERATE)) {
692 return 1;
696 for (i = 0; i < argc; i++) {
697 if (argv[i][0] != '-' || argv[i][1] == '\0') {
698 filename = argv[i++];
699 isInteractive = JS_FALSE;
700 break;
703 switch (argv[i][1]) {
704 case 'v':
705 if (++i == argc)
706 return usage();
708 JS_SetVersion(cx, (JSVersion) atoi(argv[i]));
709 break;
711 #ifdef JS_GC_ZEAL
712 case 'Z':
713 if (++i == argc)
714 return usage();
715 JS_SetGCZeal(cx, !!(atoi(argv[i])));
716 break;
717 #endif
719 case 'w':
720 reportWarnings = JS_TRUE;
721 break;
723 case 'W':
724 reportWarnings = JS_FALSE;
725 break;
727 case 's':
728 JS_ToggleOptions(cx, JSOPTION_STRICT);
729 break;
731 case 'E':
732 JS_ToggleOptions(cx, JSOPTION_RELIMIT);
733 break;
735 case 'x':
736 JS_ToggleOptions(cx, JSOPTION_XML);
737 break;
739 case 'j':
740 enableJit = !enableJit;
741 JS_ToggleOptions(cx, JSOPTION_JIT);
742 #if defined(JS_TRACER) && defined(DEBUG)
743 js::InitJITStatsClass(cx, JS_GetGlobalObject(cx));
744 JS_DefineObject(cx, JS_GetGlobalObject(cx), "tracemonkey",
745 &js::jitstats_class, NULL, 0);
746 #endif
747 break;
749 case 'o':
751 if (++i == argc)
752 return usage();
754 uint32 flag = MapContextOptionNameToFlag(cx, argv[i]);
755 if (flag == 0)
756 return gExitCode;
757 JS_ToggleOptions(cx, flag);
758 break;
760 case 'P':
761 if (JS_GET_CLASS(cx, JS_GetPrototype(cx, obj)) != &global_class) {
762 JSObject *gobj;
764 if (!JS_SealObject(cx, obj, JS_TRUE))
765 return JS_FALSE;
766 gobj = JS_NewObject(cx, &global_class, NULL, NULL);
767 if (!gobj)
768 return JS_FALSE;
769 if (!JS_SetPrototype(cx, gobj, obj))
770 return JS_FALSE;
771 JS_SetParent(cx, gobj, NULL);
772 JS_SetGlobalObject(cx, gobj);
773 obj = gobj;
775 break;
777 case 't':
778 if (++i == argc)
779 return usage();
781 if (!SetTimeoutValue(cx, atof(argv[i])))
782 return JS_FALSE;
784 break;
786 case 'c':
787 /* set stack chunk size */
788 gStackChunkSize = atoi(argv[++i]);
789 break;
791 case 'f':
792 if (++i == argc)
793 return usage();
795 Process(cx, obj, argv[i], JS_FALSE);
796 if (gExitCode != 0)
797 return gExitCode;
800 * XXX: js -f foo.js should interpret foo.js and then
801 * drop into interactive mode, but that breaks the test
802 * harness. Just execute foo.js for now.
804 isInteractive = JS_FALSE;
805 break;
807 case 'e':
809 jsval rval;
811 if (++i == argc)
812 return usage();
814 /* Pass a filename of -e to imitate PERL */
815 JS_EvaluateScript(cx, obj, argv[i], strlen(argv[i]),
816 "-e", 1, &rval);
818 isInteractive = JS_FALSE;
819 break;
822 case 'C':
823 compileOnly = JS_TRUE;
824 isInteractive = JS_FALSE;
825 break;
827 case 'i':
828 isInteractive = forceTTY = JS_TRUE;
829 break;
831 case 'S':
832 if (++i == argc)
833 return usage();
835 /* Set maximum stack size. */
836 gMaxStackSize = atoi(argv[i]);
837 break;
839 case 'z':
840 obj = split_setup(cx, JS_FALSE);
841 if (!obj)
842 return gExitCode;
843 break;
844 #ifdef MOZ_SHARK
845 case 'k':
846 JS_ConnectShark();
847 break;
848 #endif
849 #ifdef MOZ_TRACEVIS
850 case 'T':
851 if (++i == argc)
852 return usage();
854 StartTraceVis(argv[i]);
855 break;
856 #endif
857 default:
858 return usage();
862 if (filename || isInteractive)
863 Process(cx, obj, filename, forceTTY);
864 return gExitCode;
867 static JSBool
868 Version(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
870 if (argc > 0 && JSVAL_IS_INT(argv[0]))
871 *rval = INT_TO_JSVAL(JS_SetVersion(cx, (JSVersion) JSVAL_TO_INT(argv[0])));
872 else
873 *rval = INT_TO_JSVAL(JS_GetVersion(cx));
874 return JS_TRUE;
877 static JSBool
878 Options(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
880 uint32 optset, flag;
881 JSString *str;
882 const char *opt;
883 char *names;
884 JSBool found;
886 optset = 0;
887 for (uintN i = 0; i < argc; i++) {
888 str = JS_ValueToString(cx, argv[i]);
889 if (!str)
890 return JS_FALSE;
891 argv[i] = STRING_TO_JSVAL(str);
892 opt = JS_GetStringBytes(str);
893 if (!opt)
894 return JS_FALSE;
895 flag = MapContextOptionNameToFlag(cx, opt);
896 if (!flag)
897 return JS_FALSE;
898 optset |= flag;
900 optset = JS_ToggleOptions(cx, optset);
902 names = NULL;
903 found = JS_FALSE;
904 for (size_t i = 0; i != JS_ARRAY_LENGTH(js_options); i++) {
905 if (js_options[i].flag & optset) {
906 found = JS_TRUE;
907 names = JS_sprintf_append(names, "%s%s",
908 names ? "," : "", js_options[i].name);
909 if (!names)
910 break;
913 if (!found)
914 names = strdup("");
915 if (!names) {
916 JS_ReportOutOfMemory(cx);
917 return JS_FALSE;
919 str = JS_NewString(cx, names, strlen(names));
920 if (!str) {
921 free(names);
922 return JS_FALSE;
924 *rval = STRING_TO_JSVAL(str);
925 return JS_TRUE;
928 static JSBool
929 Load(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
931 uintN i;
932 JSString *str;
933 const char *filename;
934 JSScript *script;
935 JSBool ok;
936 uint32 oldopts;
938 for (i = 0; i < argc; i++) {
939 str = JS_ValueToString(cx, argv[i]);
940 if (!str)
941 return JS_FALSE;
942 argv[i] = STRING_TO_JSVAL(str);
943 filename = JS_GetStringBytes(str);
944 errno = 0;
945 oldopts = JS_GetOptions(cx);
946 JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO | JSOPTION_NO_SCRIPT_RVAL);
947 script = JS_CompileFile(cx, obj, filename);
948 JS_SetOptions(cx, oldopts);
949 if (!script) {
950 ok = JS_FALSE;
951 } else {
952 ok = !compileOnly
953 ? JS_ExecuteScript(cx, obj, script, NULL)
954 : JS_TRUE;
955 JS_DestroyScript(cx, script);
957 if (!ok)
958 return JS_FALSE;
961 return JS_TRUE;
965 * function readline()
966 * Provides a hook for scripts to read a line from stdin.
968 static JSBool
969 ReadLine(JSContext *cx, uintN argc, jsval *vp)
971 #define BUFSIZE 256
972 FILE *from;
973 char *buf, *tmp;
974 size_t bufsize, buflength, gotlength;
975 JSBool sawNewline;
976 JSString *str;
978 from = stdin;
979 buflength = 0;
980 bufsize = BUFSIZE;
981 buf = (char *) JS_malloc(cx, bufsize);
982 if (!buf)
983 return JS_FALSE;
985 sawNewline = JS_FALSE;
986 while ((gotlength =
987 js_fgets(buf + buflength, bufsize - buflength, from)) > 0) {
988 buflength += gotlength;
990 /* Are we done? */
991 if (buf[buflength - 1] == '\n') {
992 buf[buflength - 1] = '\0';
993 sawNewline = JS_TRUE;
994 break;
995 } else if (buflength < bufsize - 1) {
996 break;
999 /* Else, grow our buffer for another pass. */
1000 bufsize *= 2;
1001 if (bufsize > buflength) {
1002 tmp = (char *) JS_realloc(cx, buf, bufsize);
1003 } else {
1004 JS_ReportOutOfMemory(cx);
1005 tmp = NULL;
1008 if (!tmp) {
1009 JS_free(cx, buf);
1010 return JS_FALSE;
1013 buf = tmp;
1016 /* Treat the empty string specially. */
1017 if (buflength == 0) {
1018 *vp = feof(from) ? JSVAL_NULL : JS_GetEmptyStringValue(cx);
1019 JS_free(cx, buf);
1020 return JS_TRUE;
1023 /* Shrink the buffer to the real size. */
1024 tmp = (char *) JS_realloc(cx, buf, buflength);
1025 if (!tmp) {
1026 JS_free(cx, buf);
1027 return JS_FALSE;
1030 buf = tmp;
1033 * Turn buf into a JSString. Note that buflength includes the trailing null
1034 * character.
1036 str = JS_NewString(cx, buf, sawNewline ? buflength - 1 : buflength);
1037 if (!str) {
1038 JS_free(cx, buf);
1039 return JS_FALSE;
1042 *vp = STRING_TO_JSVAL(str);
1043 return JS_TRUE;
1046 static JSBool
1047 Print(JSContext *cx, uintN argc, jsval *vp)
1049 jsval *argv;
1050 uintN i;
1051 JSString *str;
1052 char *bytes;
1054 argv = JS_ARGV(cx, vp);
1055 for (i = 0; i < argc; i++) {
1056 str = JS_ValueToString(cx, argv[i]);
1057 if (!str)
1058 return JS_FALSE;
1059 bytes = JS_EncodeString(cx, str);
1060 if (!bytes)
1061 return JS_FALSE;
1062 fprintf(gOutFile, "%s%s", i ? " " : "", bytes);
1063 JS_free(cx, bytes);
1066 fputc('\n', gOutFile);
1067 fflush(gOutFile);
1069 JS_SET_RVAL(cx, vp, JSVAL_VOID);
1070 return JS_TRUE;
1073 static JSBool
1074 Help(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
1076 static JSBool
1077 Quit(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1079 JS_ConvertArguments(cx, argc, argv,"/ i", &gExitCode);
1081 gQuitting = JS_TRUE;
1082 #ifdef JS_THREADSAFE
1083 if (gWorkers)
1084 js::workers::terminateAll(cx, gWorkers);
1085 #endif
1086 return JS_FALSE;
1089 static const char *
1090 ToSource(JSContext *cx, jsval *vp)
1092 JSString *str = JS_ValueToSource(cx, *vp);
1093 if (str) {
1094 *vp = STRING_TO_JSVAL(str);
1095 return JS_GetStringBytes(str);
1097 JS_ClearPendingException(cx);
1098 return "<<error converting value to string>>";
1101 static JSBool
1102 AssertEq(JSContext *cx, uintN argc, jsval *vp)
1104 if (!(argc == 2 || (argc == 3 && JSVAL_IS_STRING(JS_ARGV(cx, vp)[2])))) {
1105 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
1106 (argc < 2)
1107 ? JSSMSG_NOT_ENOUGH_ARGS
1108 : (argc == 3)
1109 ? JSSMSG_INVALID_ARGS
1110 : JSSMSG_TOO_MANY_ARGS,
1111 "assertEq");
1112 return JS_FALSE;
1115 jsval *argv = JS_ARGV(cx, vp);
1116 if (!JS_SameValue(cx, argv[0], argv[1])) {
1117 const char *actual = ToSource(cx, &argv[0]);
1118 const char *expected = ToSource(cx, &argv[1]);
1119 if (argc == 2) {
1120 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_ASSERT_EQ_FAILED,
1121 actual, expected);
1122 } else {
1123 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_ASSERT_EQ_FAILED_MSG,
1124 actual, expected, JS_GetStringBytes(JSVAL_TO_STRING(argv[2])));
1126 return JS_FALSE;
1128 JS_SET_RVAL(cx, vp, JSVAL_VOID);
1129 return JS_TRUE;
1132 static JSBool
1133 GC(JSContext *cx, uintN argc, jsval *vp)
1135 size_t preBytes = cx->runtime->gcBytes;
1136 JS_GC(cx);
1138 char buf[256];
1139 JS_snprintf(buf, sizeof(buf), "before %lu, after %lu, break %08lx\n",
1140 (unsigned long)preBytes, (unsigned long)cx->runtime->gcBytes,
1141 #ifdef HAVE_SBRK
1142 (unsigned long)sbrk(0)
1143 #else
1145 #endif
1147 *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, buf));
1148 return true;
1151 #ifdef JS_GCMETER
1152 static JSBool
1153 GCStats(JSContext *cx, uintN argc, jsval *vp)
1155 js_DumpGCStats(cx->runtime, stdout);
1156 *vp = JSVAL_VOID;
1157 return true;
1159 #endif
1161 static JSBool
1162 GCParameter(JSContext *cx, uintN argc, jsval *vp)
1164 JSString *str;
1165 const char *paramName;
1166 JSGCParamKey param;
1167 uint32 value;
1169 if (argc == 0) {
1170 str = JS_ValueToString(cx, JSVAL_VOID);
1171 JS_ASSERT(str);
1172 } else {
1173 str = JS_ValueToString(cx, vp[2]);
1174 if (!str)
1175 return JS_FALSE;
1176 vp[2] = STRING_TO_JSVAL(str);
1178 paramName = JS_GetStringBytes(str);
1179 if (!paramName)
1180 return JS_FALSE;
1181 if (strcmp(paramName, "maxBytes") == 0) {
1182 param = JSGC_MAX_BYTES;
1183 } else if (strcmp(paramName, "maxMallocBytes") == 0) {
1184 param = JSGC_MAX_MALLOC_BYTES;
1185 } else if (strcmp(paramName, "gcStackpoolLifespan") == 0) {
1186 param = JSGC_STACKPOOL_LIFESPAN;
1187 } else if (strcmp(paramName, "gcBytes") == 0) {
1188 param = JSGC_BYTES;
1189 } else if (strcmp(paramName, "gcNumber") == 0) {
1190 param = JSGC_NUMBER;
1191 } else if (strcmp(paramName, "gcTriggerFactor") == 0) {
1192 param = JSGC_TRIGGER_FACTOR;
1193 } else {
1194 JS_ReportError(cx,
1195 "the first argument argument must be maxBytes, "
1196 "maxMallocBytes, gcStackpoolLifespan, gcBytes, "
1197 "gcNumber or gcTriggerFactor");
1198 return JS_FALSE;
1201 if (argc == 1) {
1202 value = JS_GetGCParameter(cx->runtime, param);
1203 return JS_NewNumberValue(cx, value, &vp[0]);
1206 if (param == JSGC_NUMBER ||
1207 param == JSGC_BYTES) {
1208 JS_ReportError(cx, "Attempt to change read-only parameter %s",
1209 paramName);
1210 return JS_FALSE;
1213 if (!JS_ValueToECMAUint32(cx, vp[3], &value)) {
1214 JS_ReportError(cx,
1215 "the second argument must be convertable to uint32 "
1216 "with non-zero value");
1217 return JS_FALSE;
1219 if (param == JSGC_TRIGGER_FACTOR && value < 100) {
1220 JS_ReportError(cx,
1221 "the gcTriggerFactor value must be >= 100");
1222 return JS_FALSE;
1224 JS_SetGCParameter(cx->runtime, param, value);
1225 *vp = JSVAL_VOID;
1226 return JS_TRUE;
1229 #ifdef JS_GC_ZEAL
1230 static JSBool
1231 GCZeal(JSContext *cx, uintN argc, jsval *vp)
1233 uint32 zeal;
1235 if (!JS_ValueToECMAUint32(cx, argc == 0 ? JSVAL_VOID : vp[2], &zeal))
1236 return JS_FALSE;
1237 JS_SetGCZeal(cx, (uint8)zeal);
1238 *vp = JSVAL_VOID;
1239 return JS_TRUE;
1241 #endif /* JS_GC_ZEAL */
1243 typedef struct JSCountHeapNode JSCountHeapNode;
1245 struct JSCountHeapNode {
1246 void *thing;
1247 int32 kind;
1248 JSCountHeapNode *next;
1251 typedef struct JSCountHeapTracer {
1252 JSTracer base;
1253 JSDHashTable visited;
1254 JSBool ok;
1255 JSCountHeapNode *traceList;
1256 JSCountHeapNode *recycleList;
1257 } JSCountHeapTracer;
1259 static void
1260 CountHeapNotify(JSTracer *trc, void *thing, uint32 kind)
1262 JSCountHeapTracer *countTracer;
1263 JSDHashEntryStub *entry;
1264 JSCountHeapNode *node;
1266 JS_ASSERT(trc->callback == CountHeapNotify);
1267 countTracer = (JSCountHeapTracer *)trc;
1268 if (!countTracer->ok)
1269 return;
1271 entry = (JSDHashEntryStub *)
1272 JS_DHashTableOperate(&countTracer->visited, thing, JS_DHASH_ADD);
1273 if (!entry) {
1274 JS_ReportOutOfMemory(trc->context);
1275 countTracer->ok = JS_FALSE;
1276 return;
1278 if (entry->key)
1279 return;
1280 entry->key = thing;
1282 node = countTracer->recycleList;
1283 if (node) {
1284 countTracer->recycleList = node->next;
1285 } else {
1286 node = (JSCountHeapNode *) JS_malloc(trc->context, sizeof *node);
1287 if (!node) {
1288 countTracer->ok = JS_FALSE;
1289 return;
1292 node->thing = thing;
1293 node->kind = kind;
1294 node->next = countTracer->traceList;
1295 countTracer->traceList = node;
1298 static JSBool
1299 CountHeap(JSContext *cx, uintN argc, jsval *vp)
1301 void* startThing;
1302 int32 startTraceKind;
1303 jsval v;
1304 int32 traceKind, i;
1305 JSString *str;
1306 char *bytes;
1307 JSCountHeapTracer countTracer;
1308 JSCountHeapNode *node;
1309 size_t counter;
1311 static const struct {
1312 const char *name;
1313 int32 kind;
1314 } traceKindNames[] = {
1315 { "all", -1 },
1316 { "object", JSTRACE_OBJECT },
1317 { "double", JSTRACE_DOUBLE },
1318 { "string", JSTRACE_STRING },
1319 #if JS_HAS_XML_SUPPORT
1320 { "xml", JSTRACE_XML },
1321 #endif
1324 startThing = NULL;
1325 startTraceKind = 0;
1326 if (argc > 0) {
1327 v = JS_ARGV(cx, vp)[0];
1328 if (JSVAL_IS_TRACEABLE(v)) {
1329 startThing = JSVAL_TO_TRACEABLE(v);
1330 startTraceKind = JSVAL_TRACE_KIND(v);
1331 } else if (v != JSVAL_NULL) {
1332 JS_ReportError(cx,
1333 "the first argument is not null or a heap-allocated "
1334 "thing");
1335 return JS_FALSE;
1339 traceKind = -1;
1340 if (argc > 1) {
1341 str = JS_ValueToString(cx, JS_ARGV(cx, vp)[1]);
1342 if (!str)
1343 return JS_FALSE;
1344 bytes = JS_GetStringBytes(str);
1345 if (!bytes)
1346 return JS_FALSE;
1347 for (i = 0; ;) {
1348 if (strcmp(bytes, traceKindNames[i].name) == 0) {
1349 traceKind = traceKindNames[i].kind;
1350 break;
1352 if (++i == JS_ARRAY_LENGTH(traceKindNames)) {
1353 JS_ReportError(cx, "trace kind name '%s' is unknown", bytes);
1354 return JS_FALSE;
1359 JS_TRACER_INIT(&countTracer.base, cx, CountHeapNotify);
1360 if (!JS_DHashTableInit(&countTracer.visited, JS_DHashGetStubOps(),
1361 NULL, sizeof(JSDHashEntryStub),
1362 JS_DHASH_DEFAULT_CAPACITY(100))) {
1363 JS_ReportOutOfMemory(cx);
1364 return JS_FALSE;
1366 countTracer.ok = JS_TRUE;
1367 countTracer.traceList = NULL;
1368 countTracer.recycleList = NULL;
1370 if (!startThing) {
1371 JS_TraceRuntime(&countTracer.base);
1372 } else {
1373 JS_SET_TRACING_NAME(&countTracer.base, "root");
1374 JS_CallTracer(&countTracer.base, startThing, startTraceKind);
1377 counter = 0;
1378 while ((node = countTracer.traceList) != NULL) {
1379 if (traceKind == -1 || node->kind == traceKind)
1380 counter++;
1381 countTracer.traceList = node->next;
1382 node->next = countTracer.recycleList;
1383 countTracer.recycleList = node;
1384 JS_TraceChildren(&countTracer.base, node->thing, node->kind);
1386 while ((node = countTracer.recycleList) != NULL) {
1387 countTracer.recycleList = node->next;
1388 JS_free(cx, node);
1390 JS_DHashTableFinish(&countTracer.visited);
1392 return countTracer.ok && JS_NewNumberValue(cx, (jsdouble) counter, vp);
1395 static JSScript *
1396 ValueToScript(JSContext *cx, jsval v)
1398 JSScript *script = NULL;
1399 JSFunction *fun;
1401 if (!JSVAL_IS_PRIMITIVE(v)) {
1402 JSObject *obj = JSVAL_TO_OBJECT(v);
1403 JSClass *clasp = JS_GET_CLASS(cx, obj);
1405 if (clasp == &js_ScriptClass) {
1406 script = (JSScript *) JS_GetPrivate(cx, obj);
1407 } else if (clasp == &js_GeneratorClass.base) {
1408 JSGenerator *gen = (JSGenerator *) JS_GetPrivate(cx, obj);
1409 fun = gen->frame.fun;
1410 script = FUN_SCRIPT(fun);
1414 if (!script) {
1415 fun = JS_ValueToFunction(cx, v);
1416 if (!fun)
1417 return NULL;
1418 script = FUN_SCRIPT(fun);
1419 if (!script) {
1420 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
1421 JSSMSG_SCRIPTS_ONLY);
1425 return script;
1428 static JSBool
1429 GetTrapArgs(JSContext *cx, uintN argc, jsval *argv, JSScript **scriptp,
1430 int32 *ip)
1432 jsval v;
1433 uintN intarg;
1434 JSScript *script;
1436 *scriptp = JS_GetScriptedCaller(cx, NULL)->script;
1437 *ip = 0;
1438 if (argc != 0) {
1439 v = argv[0];
1440 intarg = 0;
1441 if (!JSVAL_IS_PRIMITIVE(v) &&
1442 (JS_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_FunctionClass ||
1443 JS_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_ScriptClass)) {
1444 script = ValueToScript(cx, v);
1445 if (!script)
1446 return JS_FALSE;
1447 *scriptp = script;
1448 intarg++;
1450 if (argc > intarg) {
1451 if (!JS_ValueToInt32(cx, argv[intarg], ip))
1452 return JS_FALSE;
1455 return JS_TRUE;
1458 static JSTrapStatus
1459 TrapHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval,
1460 jsval closure)
1462 JSString *str;
1463 JSStackFrame *caller;
1465 str = JSVAL_TO_STRING(closure);
1466 caller = JS_GetScriptedCaller(cx, NULL);
1467 if (!JS_EvaluateUCInStackFrame(cx, caller,
1468 JS_GetStringChars(str), JS_GetStringLength(str),
1469 caller->script->filename, caller->script->lineno,
1470 rval)) {
1471 return JSTRAP_ERROR;
1473 if (!JSVAL_IS_VOID(*rval))
1474 return JSTRAP_RETURN;
1475 return JSTRAP_CONTINUE;
1478 static JSBool
1479 Trap(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1481 JSString *str;
1482 JSScript *script;
1483 int32 i;
1485 if (argc == 0) {
1486 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_TRAP_USAGE);
1487 return JS_FALSE;
1489 argc--;
1490 str = JS_ValueToString(cx, argv[argc]);
1491 if (!str)
1492 return JS_FALSE;
1493 argv[argc] = STRING_TO_JSVAL(str);
1494 if (!GetTrapArgs(cx, argc, argv, &script, &i))
1495 return JS_FALSE;
1496 return JS_SetTrap(cx, script, script->code + i, TrapHandler, STRING_TO_JSVAL(str));
1499 static JSBool
1500 Untrap(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1502 JSScript *script;
1503 int32 i;
1505 if (!GetTrapArgs(cx, argc, argv, &script, &i))
1506 return JS_FALSE;
1507 JS_ClearTrap(cx, script, script->code + i, NULL, NULL);
1508 return JS_TRUE;
1511 static JSBool
1512 LineToPC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1514 JSScript *script;
1515 int32 i;
1516 uintN lineno;
1517 jsbytecode *pc;
1519 if (argc == 0) {
1520 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_LINE2PC_USAGE);
1521 return JS_FALSE;
1523 script = JS_GetScriptedCaller(cx, NULL)->script;
1524 if (!GetTrapArgs(cx, argc, argv, &script, &i))
1525 return JS_FALSE;
1526 lineno = (i == 0) ? script->lineno : (uintN)i;
1527 pc = JS_LineNumberToPC(cx, script, lineno);
1528 if (!pc)
1529 return JS_FALSE;
1530 *rval = INT_TO_JSVAL(pc - script->code);
1531 return JS_TRUE;
1534 static JSBool
1535 PCToLine(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1537 JSScript *script;
1538 int32 i;
1539 uintN lineno;
1541 if (!GetTrapArgs(cx, argc, argv, &script, &i))
1542 return JS_FALSE;
1543 lineno = JS_PCToLineNumber(cx, script, script->code + i);
1544 if (!lineno)
1545 return JS_FALSE;
1546 *rval = INT_TO_JSVAL(lineno);
1547 return JS_TRUE;
1550 #ifdef DEBUG
1552 static void
1553 UpdateSwitchTableBounds(JSContext *cx, JSScript *script, uintN offset,
1554 uintN *start, uintN *end)
1556 jsbytecode *pc;
1557 JSOp op;
1558 ptrdiff_t jmplen;
1559 jsint low, high, n;
1561 pc = script->code + offset;
1562 op = js_GetOpcode(cx, script, pc);
1563 switch (op) {
1564 case JSOP_TABLESWITCHX:
1565 jmplen = JUMPX_OFFSET_LEN;
1566 goto jump_table;
1567 case JSOP_TABLESWITCH:
1568 jmplen = JUMP_OFFSET_LEN;
1569 jump_table:
1570 pc += jmplen;
1571 low = GET_JUMP_OFFSET(pc);
1572 pc += JUMP_OFFSET_LEN;
1573 high = GET_JUMP_OFFSET(pc);
1574 pc += JUMP_OFFSET_LEN;
1575 n = high - low + 1;
1576 break;
1578 case JSOP_LOOKUPSWITCHX:
1579 jmplen = JUMPX_OFFSET_LEN;
1580 goto lookup_table;
1581 case JSOP_LOOKUPSWITCH:
1582 jmplen = JUMP_OFFSET_LEN;
1583 lookup_table:
1584 pc += jmplen;
1585 n = GET_INDEX(pc);
1586 pc += INDEX_LEN;
1587 jmplen += JUMP_OFFSET_LEN;
1588 break;
1590 default:
1591 /* [condswitch] switch does not have any jump or lookup tables. */
1592 JS_ASSERT(op == JSOP_CONDSWITCH);
1593 return;
1596 *start = (uintN)(pc - script->code);
1597 *end = *start + (uintN)(n * jmplen);
1600 static void
1601 SrcNotes(JSContext *cx, JSScript *script)
1603 uintN offset, delta, caseOff, switchTableStart, switchTableEnd;
1604 jssrcnote *notes, *sn;
1605 JSSrcNoteType type;
1606 const char *name;
1607 uint32 index;
1608 JSAtom *atom;
1609 JSString *str;
1611 fprintf(gOutFile, "\nSource notes:\n");
1612 offset = 0;
1613 notes = script->notes();
1614 switchTableEnd = switchTableStart = 0;
1615 for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
1616 delta = SN_DELTA(sn);
1617 offset += delta;
1618 type = (JSSrcNoteType) SN_TYPE(sn);
1619 name = js_SrcNoteSpec[type].name;
1620 if (type == SRC_LABEL) {
1621 /* Check if the source note is for a switch case. */
1622 if (switchTableStart <= offset && offset < switchTableEnd) {
1623 name = "case";
1624 } else {
1625 JS_ASSERT(js_GetOpcode(cx, script, script->code + offset) == JSOP_NOP);
1628 fprintf(gOutFile, "%3u: %5u [%4u] %-8s",
1629 (uintN) (sn - notes), offset, delta, name);
1630 switch (type) {
1631 case SRC_SETLINE:
1632 fprintf(gOutFile, " lineno %u", (uintN) js_GetSrcNoteOffset(sn, 0));
1633 break;
1634 case SRC_FOR:
1635 fprintf(gOutFile, " cond %u update %u tail %u",
1636 (uintN) js_GetSrcNoteOffset(sn, 0),
1637 (uintN) js_GetSrcNoteOffset(sn, 1),
1638 (uintN) js_GetSrcNoteOffset(sn, 2));
1639 break;
1640 case SRC_IF_ELSE:
1641 fprintf(gOutFile, " else %u elseif %u",
1642 (uintN) js_GetSrcNoteOffset(sn, 0),
1643 (uintN) js_GetSrcNoteOffset(sn, 1));
1644 break;
1645 case SRC_COND:
1646 case SRC_WHILE:
1647 case SRC_PCBASE:
1648 case SRC_PCDELTA:
1649 case SRC_DECL:
1650 case SRC_BRACE:
1651 fprintf(gOutFile, " offset %u", (uintN) js_GetSrcNoteOffset(sn, 0));
1652 break;
1653 case SRC_LABEL:
1654 case SRC_LABELBRACE:
1655 case SRC_BREAK2LABEL:
1656 case SRC_CONT2LABEL:
1657 index = js_GetSrcNoteOffset(sn, 0);
1658 JS_GET_SCRIPT_ATOM(script, NULL, index, atom);
1659 JS_ASSERT(ATOM_IS_STRING(atom));
1660 str = ATOM_TO_STRING(atom);
1661 fprintf(gOutFile, " atom %u (", index);
1662 js_FileEscapedString(gOutFile, str, 0);
1663 putc(')', gOutFile);
1664 break;
1665 case SRC_FUNCDEF: {
1666 const char *bytes;
1667 JSObject *obj;
1668 JSFunction *fun;
1670 index = js_GetSrcNoteOffset(sn, 0);
1671 obj = script->getObject(index);
1672 fun = (JSFunction *) JS_GetPrivate(cx, obj);
1673 str = JS_DecompileFunction(cx, fun, JS_DONT_PRETTY_PRINT);
1674 if (str) {
1675 bytes = JS_GetStringBytes(str);
1676 } else {
1677 if (JS_IsExceptionPending(cx)) {
1678 if (!JS_ReportPendingException(cx))
1679 JS_ClearPendingException(cx);
1681 bytes = "N/A";
1683 fprintf(gOutFile, " function %u (%s)", index, bytes);
1684 break;
1686 case SRC_SWITCH:
1687 fprintf(gOutFile, " length %u", (uintN) js_GetSrcNoteOffset(sn, 0));
1688 caseOff = (uintN) js_GetSrcNoteOffset(sn, 1);
1689 if (caseOff)
1690 fprintf(gOutFile, " first case offset %u", caseOff);
1691 UpdateSwitchTableBounds(cx, script, offset,
1692 &switchTableStart, &switchTableEnd);
1693 break;
1694 case SRC_CATCH:
1695 delta = (uintN) js_GetSrcNoteOffset(sn, 0);
1696 if (delta) {
1697 if (script->main[offset] == JSOP_LEAVEBLOCK)
1698 fprintf(gOutFile, " stack depth %u", delta);
1699 else
1700 fprintf(gOutFile, " guard delta %u", delta);
1702 break;
1703 default:;
1705 fputc('\n', gOutFile);
1709 static JSBool
1710 Notes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1712 uintN i;
1713 JSScript *script;
1715 for (i = 0; i < argc; i++) {
1716 script = ValueToScript(cx, argv[i]);
1717 if (!script)
1718 continue;
1720 SrcNotes(cx, script);
1722 return JS_TRUE;
1725 JS_STATIC_ASSERT(JSTRY_CATCH == 0);
1726 JS_STATIC_ASSERT(JSTRY_FINALLY == 1);
1727 JS_STATIC_ASSERT(JSTRY_ITER == 2);
1729 static const char* const TryNoteNames[] = { "catch", "finally", "iter" };
1731 static JSBool
1732 TryNotes(JSContext *cx, JSScript *script)
1734 JSTryNote *tn, *tnlimit;
1736 if (script->trynotesOffset == 0)
1737 return JS_TRUE;
1739 tn = script->trynotes()->vector;
1740 tnlimit = tn + script->trynotes()->length;
1741 fprintf(gOutFile, "\nException table:\n"
1742 "kind stack start end\n");
1743 do {
1744 JS_ASSERT(tn->kind < JS_ARRAY_LENGTH(TryNoteNames));
1745 fprintf(gOutFile, " %-7s %6u %8u %8u\n",
1746 TryNoteNames[tn->kind], tn->stackDepth,
1747 tn->start, tn->start + tn->length);
1748 } while (++tn != tnlimit);
1749 return JS_TRUE;
1752 static bool
1753 DisassembleValue(JSContext *cx, jsval v, bool lines, bool recursive)
1755 JSScript *script = ValueToScript(cx, v);
1756 if (!script)
1757 return false;
1758 if (VALUE_IS_FUNCTION(cx, v)) {
1759 JSFunction *fun = JS_ValueToFunction(cx, v);
1760 if (fun && (fun->flags & ~7U)) {
1761 uint16 flags = fun->flags;
1762 fputs("flags:", stdout);
1764 #define SHOW_FLAG(flag) if (flags & JSFUN_##flag) fputs(" " #flag, stdout);
1766 SHOW_FLAG(LAMBDA);
1767 SHOW_FLAG(SETTER);
1768 SHOW_FLAG(GETTER);
1769 SHOW_FLAG(BOUND_METHOD);
1770 SHOW_FLAG(HEAVYWEIGHT);
1771 SHOW_FLAG(THISP_STRING);
1772 SHOW_FLAG(THISP_NUMBER);
1773 SHOW_FLAG(THISP_BOOLEAN);
1774 SHOW_FLAG(EXPR_CLOSURE);
1775 SHOW_FLAG(TRCINFO);
1777 #undef SHOW_FLAG
1779 if (FUN_INTERPRETED(fun)) {
1780 if (FUN_NULL_CLOSURE(fun))
1781 fputs(" NULL_CLOSURE", stdout);
1782 else if (FUN_FLAT_CLOSURE(fun))
1783 fputs(" FLAT_CLOSURE", stdout);
1785 if (fun->u.i.nupvars) {
1786 fputs("\nupvars: {\n", stdout);
1788 void *mark = cx->tempPool.getMark();
1789 jsuword *localNames = js_GetLocalNameArray(cx, fun, &cx->tempPool);
1790 if (!localNames)
1791 return false;
1793 JSUpvarArray *uva = fun->u.i.script->upvars();
1794 uintN upvar_base = fun->countArgsAndVars();
1796 for (uint32 i = 0, n = uva->length; i < n; i++) {
1797 JSAtom *atom = JS_LOCAL_NAME_TO_ATOM(localNames[upvar_base + i]);
1798 uint32 cookie = uva->vector[i];
1800 printf(" %s: {skip:%u, slot:%u},\n",
1801 js_AtomToPrintableString(cx, atom),
1802 UPVAR_FRAME_SKIP(cookie),
1803 UPVAR_FRAME_SLOT(cookie));
1806 cx->tempPool.release(mark);
1807 putchar('}');
1810 putchar('\n');
1814 if (!js_Disassemble(cx, script, lines, stdout))
1815 return false;
1816 SrcNotes(cx, script);
1817 TryNotes(cx, script);
1819 if (recursive && script->objectsOffset != 0) {
1820 JSObjectArray *objects = script->objects();
1821 for (uintN i = 0; i != objects->length; ++i) {
1822 JSObject *obj = objects->vector[i];
1823 if (obj->isFunction()) {
1824 putchar('\n');
1825 if (!DisassembleValue(cx, OBJECT_TO_JSVAL(obj),
1826 lines, recursive)) {
1827 return false;
1832 return true;
1835 static JSBool
1836 Disassemble(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1838 bool lines = false, recursive = false;
1840 while (argc > 0 && JSVAL_IS_STRING(argv[0])) {
1841 const char *bytes = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
1842 lines = !strcmp(bytes, "-l");
1843 recursive = !strcmp(bytes, "-r");
1844 if (!lines && !recursive)
1845 break;
1846 argv++, argc--;
1849 for (uintN i = 0; i < argc; i++) {
1850 if (!DisassembleValue(cx, argv[i], lines, recursive))
1851 return false;
1853 return true;
1856 static JSBool
1857 DisassFile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1859 JSString *str;
1860 const char *filename;
1861 JSScript *script;
1862 JSBool ok;
1863 uint32 oldopts;
1865 if (!argc)
1866 return JS_TRUE;
1868 str = JS_ValueToString(cx, argv[0]);
1869 if (!str)
1870 return JS_FALSE;
1871 argv[0] = STRING_TO_JSVAL(str);
1873 filename = JS_GetStringBytes(str);
1874 oldopts = JS_GetOptions(cx);
1875 JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO | JSOPTION_NO_SCRIPT_RVAL);
1876 script = JS_CompileFile(cx, obj, filename);
1877 JS_SetOptions(cx, oldopts);
1878 if (!script)
1879 return JS_FALSE;
1881 obj = JS_NewScriptObject(cx, script);
1882 if (!obj)
1883 return JS_FALSE;
1885 *rval = OBJECT_TO_JSVAL(obj); /* I like to root it, root it. */
1886 ok = Disassemble(cx, obj, 1, rval, rval); /* gross, but works! */
1887 *rval = JSVAL_VOID;
1889 return ok;
1892 static JSBool
1893 DisassWithSrc(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1894 jsval *rval)
1896 #define LINE_BUF_LEN 512
1897 uintN i, len, line1, line2, bupline;
1898 JSScript *script;
1899 FILE *file;
1900 char linebuf[LINE_BUF_LEN];
1901 jsbytecode *pc, *end;
1902 JSBool ok;
1903 static char sep[] = ";-------------------------";
1905 ok = JS_TRUE;
1906 for (i = 0; ok && i < argc; i++) {
1907 script = ValueToScript(cx, argv[i]);
1908 if (!script)
1909 return JS_FALSE;
1911 if (!script->filename) {
1912 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
1913 JSSMSG_FILE_SCRIPTS_ONLY);
1914 return JS_FALSE;
1917 file = fopen(script->filename, "r");
1918 if (!file) {
1919 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
1920 JSSMSG_CANT_OPEN, script->filename,
1921 strerror(errno));
1922 return JS_FALSE;
1925 pc = script->code;
1926 end = pc + script->length;
1928 /* burn the leading lines */
1929 line2 = JS_PCToLineNumber(cx, script, pc);
1930 for (line1 = 0; line1 < line2 - 1; line1++) {
1931 char *tmp = fgets(linebuf, LINE_BUF_LEN, file);
1932 if (!tmp) {
1933 JS_ReportError(cx, "failed to read %s fully",
1934 script->filename);
1935 ok = JS_FALSE;
1936 goto bail;
1940 bupline = 0;
1941 while (pc < end) {
1942 line2 = JS_PCToLineNumber(cx, script, pc);
1944 if (line2 < line1) {
1945 if (bupline != line2) {
1946 bupline = line2;
1947 fprintf(gOutFile, "%s %3u: BACKUP\n", sep, line2);
1949 } else {
1950 if (bupline && line1 == line2)
1951 fprintf(gOutFile, "%s %3u: RESTORE\n", sep, line2);
1952 bupline = 0;
1953 while (line1 < line2) {
1954 if (!fgets(linebuf, LINE_BUF_LEN, file)) {
1955 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
1956 JSSMSG_UNEXPECTED_EOF,
1957 script->filename);
1958 ok = JS_FALSE;
1959 goto bail;
1961 line1++;
1962 fprintf(gOutFile, "%s %3u: %s", sep, line1, linebuf);
1966 len = js_Disassemble1(cx, script, pc,
1967 pc - script->code,
1968 JS_TRUE, stdout);
1969 if (!len) {
1970 ok = JS_FALSE;
1971 goto bail;
1973 pc += len;
1976 bail:
1977 fclose(file);
1979 return ok;
1980 #undef LINE_BUF_LEN
1983 static JSBool
1984 Tracing(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1986 FILE *file;
1988 if (argc == 0) {
1989 *rval = BOOLEAN_TO_JSVAL(cx->tracefp != 0);
1990 return JS_TRUE;
1993 switch (JS_TypeOfValue(cx, argv[0])) {
1994 case JSTYPE_NUMBER:
1995 case JSTYPE_BOOLEAN: {
1996 JSBool bval;
1997 JS_ValueToBoolean(cx, argv[0], &bval);
1998 file = bval ? stderr : NULL;
1999 break;
2001 case JSTYPE_STRING: {
2002 char *name = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
2003 file = fopen(name, "w");
2004 if (!file) {
2005 JS_ReportError(cx, "tracing: couldn't open output file %s: %s",
2006 name, strerror(errno));
2007 return JS_FALSE;
2009 break;
2011 default:
2012 goto bad_argument;
2014 if (cx->tracefp && cx->tracefp != stderr)
2015 fclose((FILE *)cx->tracefp);
2016 cx->tracefp = file;
2017 cx->tracePrevPc = NULL;
2018 return JS_TRUE;
2020 bad_argument:
2021 JSString *str = JS_ValueToString(cx, argv[0]);
2022 if (!str)
2023 return JS_FALSE;
2024 JS_ReportError(cx, "tracing: illegal argument %s",
2025 JS_GetStringBytes(str));
2026 return JS_FALSE;
2029 static void
2030 DumpScope(JSContext *cx, JSObject *obj, FILE *fp)
2032 uintN i = 0;
2033 for (JSScopeProperty *sprop = NULL; JS_PropertyIterator(obj, &sprop);) {
2034 fprintf(fp, "%3u %p ", i++, (void *) sprop);
2035 sprop->dump(cx, fp);
2039 static JSBool
2040 DumpStats(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
2042 uintN i;
2043 JSString *str;
2044 const char *bytes;
2045 jsid id;
2046 JSObject *obj2;
2047 JSProperty *prop;
2048 jsval value;
2050 for (i = 0; i < argc; i++) {
2051 str = JS_ValueToString(cx, argv[i]);
2052 if (!str)
2053 return JS_FALSE;
2054 argv[i] = STRING_TO_JSVAL(str);
2055 bytes = JS_GetStringBytes(str);
2056 if (strcmp(bytes, "arena") == 0) {
2057 #ifdef JS_ARENAMETER
2058 JS_DumpArenaStats(stdout);
2059 #endif
2060 } else if (strcmp(bytes, "atom") == 0) {
2061 js_DumpAtoms(cx, gOutFile);
2062 } else if (strcmp(bytes, "global") == 0) {
2063 DumpScope(cx, cx->globalObject, stdout);
2064 } else {
2065 if (!JS_ValueToId(cx, STRING_TO_JSVAL(str), &id))
2066 return JS_FALSE;
2067 if (!js_FindProperty(cx, id, &obj, &obj2, &prop))
2068 return JS_FALSE;
2069 if (prop) {
2070 obj2->dropProperty(cx, prop);
2071 if (!obj->getProperty(cx, id, &value))
2072 return JS_FALSE;
2074 if (!prop || !JSVAL_IS_OBJECT(value)) {
2075 fprintf(gErrFile, "js: invalid stats argument %s\n",
2076 bytes);
2077 continue;
2079 obj = JSVAL_TO_OBJECT(value);
2080 if (obj)
2081 DumpScope(cx, obj, stdout);
2084 return JS_TRUE;
2087 static JSBool
2088 DumpHeap(JSContext *cx, uintN argc, jsval *vp)
2090 char *fileName;
2091 jsval v;
2092 void* startThing;
2093 uint32 startTraceKind;
2094 const char *badTraceArg;
2095 void *thingToFind;
2096 size_t maxDepth;
2097 void *thingToIgnore;
2098 FILE *dumpFile;
2099 JSBool ok;
2101 fileName = NULL;
2102 if (argc > 0) {
2103 v = JS_ARGV(cx, vp)[0];
2104 if (v != JSVAL_NULL) {
2105 JSString *str;
2107 str = JS_ValueToString(cx, v);
2108 if (!str)
2109 return JS_FALSE;
2110 JS_ARGV(cx, vp)[0] = STRING_TO_JSVAL(str);
2111 fileName = JS_GetStringBytes(str);
2115 startThing = NULL;
2116 startTraceKind = 0;
2117 if (argc > 1) {
2118 v = JS_ARGV(cx, vp)[1];
2119 if (JSVAL_IS_TRACEABLE(v)) {
2120 startThing = JSVAL_TO_TRACEABLE(v);
2121 startTraceKind = JSVAL_TRACE_KIND(v);
2122 } else if (v != JSVAL_NULL) {
2123 badTraceArg = "start";
2124 goto not_traceable_arg;
2128 thingToFind = NULL;
2129 if (argc > 2) {
2130 v = JS_ARGV(cx, vp)[2];
2131 if (JSVAL_IS_TRACEABLE(v)) {
2132 thingToFind = JSVAL_TO_TRACEABLE(v);
2133 } else if (v != JSVAL_NULL) {
2134 badTraceArg = "toFind";
2135 goto not_traceable_arg;
2139 maxDepth = (size_t)-1;
2140 if (argc > 3) {
2141 v = JS_ARGV(cx, vp)[3];
2142 if (v != JSVAL_NULL) {
2143 uint32 depth;
2145 if (!JS_ValueToECMAUint32(cx, v, &depth))
2146 return JS_FALSE;
2147 maxDepth = depth;
2151 thingToIgnore = NULL;
2152 if (argc > 4) {
2153 v = JS_ARGV(cx, vp)[4];
2154 if (JSVAL_IS_TRACEABLE(v)) {
2155 thingToIgnore = JSVAL_TO_TRACEABLE(v);
2156 } else if (v != JSVAL_NULL) {
2157 badTraceArg = "toIgnore";
2158 goto not_traceable_arg;
2162 if (!fileName) {
2163 dumpFile = stdout;
2164 } else {
2165 dumpFile = fopen(fileName, "w");
2166 if (!dumpFile) {
2167 JS_ReportError(cx, "can't open %s: %s", fileName, strerror(errno));
2168 return JS_FALSE;
2172 ok = JS_DumpHeap(cx, dumpFile, startThing, startTraceKind, thingToFind,
2173 maxDepth, thingToIgnore);
2174 if (dumpFile != stdout)
2175 fclose(dumpFile);
2176 return ok;
2178 not_traceable_arg:
2179 JS_ReportError(cx, "argument '%s' is not null or a heap-allocated thing",
2180 badTraceArg);
2181 return JS_FALSE;
2184 #endif /* DEBUG */
2186 #ifdef TEST_CVTARGS
2187 #include <ctype.h>
2189 static const char *
2190 EscapeWideString(jschar *w)
2192 static char enuf[80];
2193 static char hex[] = "0123456789abcdef";
2194 jschar u;
2195 unsigned char b, c;
2196 int i, j;
2198 if (!w)
2199 return "";
2200 for (i = j = 0; i < sizeof enuf - 1; i++, j++) {
2201 u = w[j];
2202 if (u == 0)
2203 break;
2204 b = (unsigned char)(u >> 8);
2205 c = (unsigned char)(u);
2206 if (b) {
2207 if (i >= sizeof enuf - 6)
2208 break;
2209 enuf[i++] = '\\';
2210 enuf[i++] = 'u';
2211 enuf[i++] = hex[b >> 4];
2212 enuf[i++] = hex[b & 15];
2213 enuf[i++] = hex[c >> 4];
2214 enuf[i] = hex[c & 15];
2215 } else if (!isprint(c)) {
2216 if (i >= sizeof enuf - 4)
2217 break;
2218 enuf[i++] = '\\';
2219 enuf[i++] = 'x';
2220 enuf[i++] = hex[c >> 4];
2221 enuf[i] = hex[c & 15];
2222 } else {
2223 enuf[i] = (char)c;
2226 enuf[i] = 0;
2227 return enuf;
2230 #include <stdarg.h>
2232 static JSBool
2233 ZZ_formatter(JSContext *cx, const char *format, JSBool fromJS, jsval **vpp,
2234 va_list *app)
2236 jsval *vp;
2237 va_list ap;
2238 jsdouble re, im;
2240 printf("entering ZZ_formatter");
2241 vp = *vpp;
2242 ap = *app;
2243 if (fromJS) {
2244 if (!JS_ValueToNumber(cx, vp[0], &re))
2245 return JS_FALSE;
2246 if (!JS_ValueToNumber(cx, vp[1], &im))
2247 return JS_FALSE;
2248 *va_arg(ap, jsdouble *) = re;
2249 *va_arg(ap, jsdouble *) = im;
2250 } else {
2251 re = va_arg(ap, jsdouble);
2252 im = va_arg(ap, jsdouble);
2253 if (!JS_NewNumberValue(cx, re, &vp[0]))
2254 return JS_FALSE;
2255 if (!JS_NewNumberValue(cx, im, &vp[1]))
2256 return JS_FALSE;
2258 *vpp = vp + 2;
2259 *app = ap;
2260 printf("leaving ZZ_formatter");
2261 return JS_TRUE;
2264 static JSBool
2265 ConvertArgs(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
2267 JSBool b = JS_FALSE;
2268 jschar c = 0;
2269 int32 i = 0, j = 0;
2270 uint32 u = 0;
2271 jsdouble d = 0, I = 0, re = 0, im = 0;
2272 char *s = NULL;
2273 JSString *str = NULL;
2274 jschar *w = NULL;
2275 JSObject *obj2 = NULL;
2276 JSFunction *fun = NULL;
2277 jsval v = JSVAL_VOID;
2278 JSBool ok;
2280 if (!JS_AddArgumentFormatter(cx, "ZZ", ZZ_formatter))
2281 return JS_FALSE;
2282 ok = JS_ConvertArguments(cx, argc, argv, "b/ciujdIsSWofvZZ*",
2283 &b, &c, &i, &u, &j, &d, &I, &s, &str, &w, &obj2,
2284 &fun, &v, &re, &im);
2285 JS_RemoveArgumentFormatter(cx, "ZZ");
2286 if (!ok)
2287 return JS_FALSE;
2288 fprintf(gOutFile,
2289 "b %u, c %x (%c), i %ld, u %lu, j %ld\n",
2290 b, c, (char)c, i, u, j);
2291 ToString obj2string(cx, obj2);
2292 ToString valueString(cx, v);
2293 JSString *tmpstr = JS_DecompileFunction(cx, fun, 4);
2294 const char *func;
2295 if (tmpstr) {
2296 func = JS_GetStringBytes(tmpstr);
2297 } else {
2298 if (JS_IsExceptionPending(cx)) {
2299 if (!JS_ReportPendingException(cx))
2300 JS_ClearPendingException(cx);
2302 func = "error decompiling fun";
2304 fprintf(gOutFile,
2305 "d %g, I %g, s %s, S %s, W %s, obj %s, fun %s\n"
2306 "v %s, re %g, im %g\n",
2307 d, I, s, str ? JS_GetStringBytes(str) : "", EscapeWideString(w),
2308 obj2string.getBytes(),
2309 fun ? func : "",
2310 valueString.getBytes(), re, im);
2311 return JS_TRUE;
2313 #endif
2315 static JSBool
2316 BuildDate(JSContext *cx, uintN argc, jsval *vp)
2318 char version[20] = "\n";
2319 #if JS_VERSION < 150
2320 sprintf(version, " for version %d\n", JS_VERSION);
2321 #endif
2322 fprintf(gOutFile, "built on %s at %s%s", __DATE__, __TIME__, version);
2323 *vp = JSVAL_VOID;
2324 return JS_TRUE;
2327 static JSBool
2328 Clear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
2330 if (argc != 0 && !JS_ValueToObject(cx, argv[0], &obj))
2331 return JS_FALSE;
2332 JS_ClearScope(cx, obj);
2333 return JS_TRUE;
2336 static JSBool
2337 Intern(JSContext *cx, uintN argc, jsval *vp)
2339 JSString *str;
2341 str = JS_ValueToString(cx, argc == 0 ? JSVAL_VOID : vp[2]);
2342 if (!str)
2343 return JS_FALSE;
2344 if (!JS_InternUCStringN(cx, JS_GetStringChars(str),
2345 JS_GetStringLength(str))) {
2346 return JS_FALSE;
2348 *vp = JSVAL_VOID;
2349 return JS_TRUE;
2352 static JSBool
2353 Clone(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
2355 JSObject *funobj, *parent, *clone;
2357 if (VALUE_IS_FUNCTION(cx, argv[0])) {
2358 funobj = JSVAL_TO_OBJECT(argv[0]);
2359 } else {
2360 JSFunction *fun = JS_ValueToFunction(cx, argv[0]);
2361 if (!fun)
2362 return JS_FALSE;
2363 funobj = JS_GetFunctionObject(fun);
2365 if (argc > 1) {
2366 if (!JS_ValueToObject(cx, argv[1], &parent))
2367 return JS_FALSE;
2368 } else {
2369 parent = JS_GetParent(cx, funobj);
2371 clone = JS_CloneFunctionObject(cx, funobj, parent);
2372 if (!clone)
2373 return JS_FALSE;
2374 *rval = OBJECT_TO_JSVAL(clone);
2375 return JS_TRUE;
2378 static JSBool
2379 Seal(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
2381 JSObject *target;
2382 JSBool deep = JS_FALSE;
2384 if (!JS_ConvertArguments(cx, argc, argv, "o/b", &target, &deep))
2385 return JS_FALSE;
2386 if (!target)
2387 return JS_TRUE;
2388 return JS_SealObject(cx, target, deep);
2391 static JSBool
2392 GetPDA(JSContext *cx, uintN argc, jsval *vp)
2394 JSObject *vobj, *aobj, *pdobj;
2395 JSBool ok;
2396 JSPropertyDescArray pda;
2397 JSPropertyDesc *pd;
2398 uint32 i;
2399 jsval v;
2401 if (!JS_ValueToObject(cx, argc == 0 ? JSVAL_VOID : vp[2], &vobj))
2402 return JS_FALSE;
2403 if (!vobj) {
2404 *vp = JSVAL_VOID;
2405 return JS_TRUE;
2408 aobj = JS_NewArrayObject(cx, 0, NULL);
2409 if (!aobj)
2410 return JS_FALSE;
2411 *vp = OBJECT_TO_JSVAL(aobj);
2413 ok = JS_GetPropertyDescArray(cx, vobj, &pda);
2414 if (!ok)
2415 return JS_FALSE;
2416 pd = pda.array;
2417 for (i = 0; i < pda.length; i++, pd++) {
2418 pdobj = JS_NewObject(cx, NULL, NULL, NULL);
2419 if (!pdobj) {
2420 ok = JS_FALSE;
2421 break;
2424 /* Protect pdobj from GC by setting it as an element of aobj now */
2425 v = OBJECT_TO_JSVAL(pdobj);
2426 ok = JS_SetElement(cx, aobj, i, &v);
2427 if (!ok)
2428 break;
2430 ok = JS_SetProperty(cx, pdobj, "id", &pd->id) &&
2431 JS_SetProperty(cx, pdobj, "value", &pd->value) &&
2432 (v = INT_TO_JSVAL(pd->flags),
2433 JS_SetProperty(cx, pdobj, "flags", &v)) &&
2434 (v = INT_TO_JSVAL(pd->slot),
2435 JS_SetProperty(cx, pdobj, "slot", &v)) &&
2436 JS_SetProperty(cx, pdobj, "alias", &pd->alias);
2437 if (!ok)
2438 break;
2440 JS_PutPropertyDescArray(cx, &pda);
2441 return ok;
2444 static JSBool
2445 GetSLX(JSContext *cx, uintN argc, jsval *vp)
2447 JSScript *script;
2449 script = ValueToScript(cx, argc == 0 ? JSVAL_VOID : vp[2]);
2450 if (!script)
2451 return JS_FALSE;
2452 *vp = INT_TO_JSVAL(js_GetScriptLineExtent(script));
2453 return JS_TRUE;
2456 static JSBool
2457 ToInt32(JSContext *cx, uintN argc, jsval *vp)
2459 int32 i;
2461 if (!JS_ValueToInt32(cx, argc == 0 ? JSVAL_VOID : vp[2], &i))
2462 return JS_FALSE;
2463 return JS_NewNumberValue(cx, i, vp);
2466 static JSBool
2467 StringsAreUTF8(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
2468 jsval *rval)
2470 *rval = JS_CStringsAreUTF8() ? JSVAL_TRUE : JSVAL_FALSE;
2471 return JS_TRUE;
2474 static JSBool
2475 StackQuota(JSContext *cx, uintN argc, jsval *vp)
2477 uint32 n;
2479 if (argc == 0)
2480 return JS_NewNumberValue(cx, (double) gScriptStackQuota, vp);
2481 if (!JS_ValueToECMAUint32(cx, JS_ARGV(cx, vp)[0], &n))
2482 return JS_FALSE;
2483 gScriptStackQuota = n;
2484 JS_SetScriptStackQuota(cx, gScriptStackQuota);
2485 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2486 return JS_TRUE;
2489 static const char* badUTF8 = "...\xC0...";
2490 static const char* bigUTF8 = "...\xFB\xBF\xBF\xBF\xBF...";
2491 static const jschar badSurrogate[] = { 'A', 'B', 'C', 0xDEEE, 'D', 'E', 0 };
2493 static JSBool
2494 TestUTF8(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
2496 int32 mode = 1;
2497 jschar chars[20];
2498 size_t charsLength = 5;
2499 char bytes[20];
2500 size_t bytesLength = 20;
2501 if (argc && !JS_ValueToInt32(cx, *argv, &mode))
2502 return JS_FALSE;
2504 /* The following throw errors if compiled with UTF-8. */
2505 switch (mode) {
2506 /* mode 1: malformed UTF-8 string. */
2507 case 1:
2508 JS_NewStringCopyZ(cx, badUTF8);
2509 break;
2510 /* mode 2: big UTF-8 character. */
2511 case 2:
2512 JS_NewStringCopyZ(cx, bigUTF8);
2513 break;
2514 /* mode 3: bad surrogate character. */
2515 case 3:
2516 JS_EncodeCharacters(cx, badSurrogate, 6, bytes, &bytesLength);
2517 break;
2518 /* mode 4: use a too small buffer. */
2519 case 4:
2520 JS_DecodeBytes(cx, "1234567890", 10, chars, &charsLength);
2521 break;
2522 default:
2523 JS_ReportError(cx, "invalid mode parameter");
2524 return JS_FALSE;
2526 return !JS_IsExceptionPending (cx);
2529 static JSBool
2530 ThrowError(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
2532 JS_ReportError(cx, "This is an error");
2533 return JS_FALSE;
2536 #define LAZY_STANDARD_CLASSES
2538 /* A class for easily testing the inner/outer object callbacks. */
2539 typedef struct ComplexObject {
2540 JSBool isInner;
2541 JSBool frozen;
2542 JSObject *inner;
2543 JSObject *outer;
2544 } ComplexObject;
2546 static JSObject *
2547 split_create_outer(JSContext *cx);
2549 static JSObject *
2550 split_create_inner(JSContext *cx, JSObject *outer);
2552 static ComplexObject *
2553 split_get_private(JSContext *cx, JSObject *obj);
2555 static JSBool
2556 split_addProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
2558 ComplexObject *cpx;
2559 jsid asId;
2561 cpx = split_get_private(cx, obj);
2562 if (!cpx)
2563 return JS_TRUE;
2564 if (!cpx->isInner && cpx->inner) {
2565 /* Make sure to define this property on the inner object. */
2566 if (!JS_ValueToId(cx, id, &asId))
2567 return JS_FALSE;
2568 return JS_DefinePropertyById(cx, cpx->inner, asId, *vp, NULL, NULL, JSPROP_ENUMERATE);
2570 return JS_TRUE;
2573 static JSBool
2574 split_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
2576 ComplexObject *cpx;
2578 cpx = split_get_private(cx, obj);
2579 if (!cpx)
2580 return JS_TRUE;
2582 if (JSVAL_IS_STRING(id) &&
2583 !strcmp(JS_GetStringBytes(JSVAL_TO_STRING(id)), "isInner")) {
2584 *vp = BOOLEAN_TO_JSVAL(cpx->isInner);
2585 return JS_TRUE;
2588 if (!cpx->isInner && cpx->inner) {
2589 if (JSVAL_IS_STRING(id)) {
2590 JSString *str;
2592 str = JSVAL_TO_STRING(id);
2593 return JS_GetUCProperty(cx, cpx->inner, JS_GetStringChars(str),
2594 JS_GetStringLength(str), vp);
2596 if (JSVAL_IS_INT(id))
2597 return JS_GetElement(cx, cpx->inner, JSVAL_TO_INT(id), vp);
2598 return JS_TRUE;
2601 return JS_TRUE;
2604 static JSBool
2605 split_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
2607 ComplexObject *cpx;
2609 cpx = split_get_private(cx, obj);
2610 if (!cpx)
2611 return JS_TRUE;
2612 if (!cpx->isInner && cpx->inner) {
2613 if (JSVAL_IS_STRING(id)) {
2614 JSString *str;
2616 str = JSVAL_TO_STRING(id);
2617 return JS_SetUCProperty(cx, cpx->inner, JS_GetStringChars(str),
2618 JS_GetStringLength(str), vp);
2620 if (JSVAL_IS_INT(id))
2621 return JS_SetElement(cx, cpx->inner, JSVAL_TO_INT(id), vp);
2622 return JS_TRUE;
2625 return JS_TRUE;
2628 static JSBool
2629 split_delProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
2631 ComplexObject *cpx;
2632 jsid asId;
2634 cpx = split_get_private(cx, obj);
2635 if (!cpx)
2636 return JS_TRUE;
2637 if (!cpx->isInner && cpx->inner) {
2638 /* Make sure to define this property on the inner object. */
2639 if (!JS_ValueToId(cx, *vp, &asId))
2640 return JS_FALSE;
2641 return cpx->inner->deleteProperty(cx, asId, vp);
2643 return JS_TRUE;
2646 static JSBool
2647 split_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
2648 jsval *statep, jsid *idp)
2650 ComplexObject *cpx;
2651 JSObject *iterator;
2653 switch (enum_op) {
2654 case JSENUMERATE_INIT:
2655 cpx = (ComplexObject *) JS_GetPrivate(cx, obj);
2657 if (!cpx->isInner && cpx->inner)
2658 obj = cpx->inner;
2660 iterator = JS_NewPropertyIterator(cx, obj);
2661 if (!iterator)
2662 return JS_FALSE;
2664 *statep = OBJECT_TO_JSVAL(iterator);
2665 if (idp)
2666 *idp = JSVAL_ZERO;
2667 break;
2669 case JSENUMERATE_NEXT:
2670 iterator = (JSObject*)JSVAL_TO_OBJECT(*statep);
2671 if (!JS_NextProperty(cx, iterator, idp))
2672 return JS_FALSE;
2674 if (!JSVAL_IS_VOID(*idp))
2675 break;
2676 /* Fall through. */
2678 case JSENUMERATE_DESTROY:
2679 /* Let GC at our iterator object. */
2680 *statep = JSVAL_NULL;
2681 break;
2684 return JS_TRUE;
2687 static JSBool
2688 split_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp)
2690 ComplexObject *cpx;
2692 if (JSVAL_IS_STRING(id) &&
2693 !strcmp(JS_GetStringBytes(JSVAL_TO_STRING(id)), "isInner")) {
2694 *objp = obj;
2695 return JS_DefineProperty(cx, obj, "isInner", JSVAL_VOID, NULL, NULL,
2696 JSPROP_SHARED);
2699 cpx = split_get_private(cx, obj);
2700 if (!cpx)
2701 return JS_TRUE;
2702 if (!cpx->isInner && cpx->inner) {
2703 jsid asId;
2704 JSProperty *prop;
2706 if (!JS_ValueToId(cx, id, &asId))
2707 return JS_FALSE;
2709 if (!cpx->inner->lookupProperty(cx, asId, objp, &prop))
2710 return JS_FALSE;
2711 if (prop)
2712 cpx->inner->dropProperty(cx, prop);
2714 return JS_TRUE;
2717 #ifdef LAZY_STANDARD_CLASSES
2718 if (!(flags & JSRESOLVE_ASSIGNING)) {
2719 JSBool resolved;
2721 if (!JS_ResolveStandardClass(cx, obj, id, &resolved))
2722 return JS_FALSE;
2724 if (resolved) {
2725 *objp = obj;
2726 return JS_TRUE;
2729 #endif
2731 /* XXX For additional realism, let's resolve some random property here. */
2732 return JS_TRUE;
2735 static void
2736 split_finalize(JSContext *cx, JSObject *obj)
2738 JS_free(cx, JS_GetPrivate(cx, obj));
2741 static uint32
2742 split_mark(JSContext *cx, JSObject *obj, void *arg)
2744 ComplexObject *cpx;
2746 cpx = (ComplexObject *) JS_GetPrivate(cx, obj);
2748 if (!cpx->isInner && cpx->inner) {
2749 /* Mark the inner object. */
2750 JS_MarkGCThing(cx, cpx->inner, "ComplexObject.inner", arg);
2753 return 0;
2756 static JSObject *
2757 split_outerObject(JSContext *cx, JSObject *obj)
2759 ComplexObject *cpx;
2761 cpx = (ComplexObject *) JS_GetPrivate(cx, obj);
2762 return cpx->isInner ? cpx->outer : obj;
2765 static JSObject *
2766 split_thisObject(JSContext *cx, JSObject *obj)
2768 OBJ_TO_OUTER_OBJECT(cx, obj);
2769 if (!obj)
2770 return NULL;
2771 return obj;
2774 static JSObjectOps split_objectops;
2776 static JSObjectOps *
2777 split_getObjectOps(JSContext *cx, JSClass *clasp)
2779 if (!split_objectops.thisObject) {
2780 memcpy(&split_objectops, &js_ObjectOps, sizeof split_objectops);
2781 split_objectops.thisObject = split_thisObject;
2782 split_objectops.call = NULL;
2783 split_objectops.construct = NULL;
2786 return &split_objectops;
2789 static JSBool
2790 split_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp);
2792 static JSObject *
2793 split_innerObject(JSContext *cx, JSObject *obj)
2795 ComplexObject *cpx;
2797 cpx = (ComplexObject *) JS_GetPrivate(cx, obj);
2798 if (cpx->frozen) {
2799 JS_ASSERT(!cpx->isInner);
2800 return obj;
2802 return !cpx->isInner ? cpx->inner : obj;
2805 static JSExtendedClass split_global_class = {
2806 {"split_global",
2807 JSCLASS_NEW_RESOLVE | JSCLASS_NEW_ENUMERATE | JSCLASS_HAS_PRIVATE |
2808 JSCLASS_GLOBAL_FLAGS | JSCLASS_IS_EXTENDED,
2809 split_addProperty, split_delProperty,
2810 split_getProperty, split_setProperty,
2811 (JSEnumerateOp)split_enumerate,
2812 (JSResolveOp)split_resolve,
2813 JS_ConvertStub, split_finalize,
2814 split_getObjectOps, NULL, NULL, NULL, NULL, NULL,
2815 split_mark, NULL},
2816 split_equality, split_outerObject, split_innerObject,
2817 NULL, NULL, NULL, NULL, NULL
2820 static JSBool
2821 split_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
2823 *bp = JS_FALSE;
2824 if (JSVAL_IS_PRIMITIVE(v))
2825 return JS_TRUE;
2827 JSObject *obj2 = JSVAL_TO_OBJECT(v);
2828 if (JS_GET_CLASS(cx, obj2) != &split_global_class.base)
2829 return JS_TRUE;
2831 ComplexObject *cpx = (ComplexObject *) JS_GetPrivate(cx, obj2);
2832 JS_ASSERT(!cpx->isInner);
2834 ComplexObject *ourCpx = (ComplexObject *) JS_GetPrivate(cx, obj);
2835 JS_ASSERT(!ourCpx->isInner);
2837 *bp = (cpx == ourCpx);
2838 return JS_TRUE;
2841 JSObject *
2842 split_create_outer(JSContext *cx)
2844 ComplexObject *cpx;
2845 JSObject *obj;
2847 cpx = (ComplexObject *) JS_malloc(cx, sizeof *obj);
2848 if (!cpx)
2849 return NULL;
2850 cpx->isInner = JS_FALSE;
2851 cpx->frozen = JS_TRUE;
2852 cpx->inner = NULL;
2853 cpx->outer = NULL;
2855 obj = JS_NewObject(cx, &split_global_class.base, NULL, NULL);
2856 if (!obj || !JS_SetParent(cx, obj, NULL)) {
2857 JS_free(cx, cpx);
2858 return NULL;
2861 if (!JS_SetPrivate(cx, obj, cpx)) {
2862 JS_free(cx, cpx);
2863 return NULL;
2866 return obj;
2869 static JSObject *
2870 split_create_inner(JSContext *cx, JSObject *outer)
2872 ComplexObject *cpx, *outercpx;
2873 JSObject *obj;
2875 JS_ASSERT(JS_GET_CLASS(cx, outer) == &split_global_class.base);
2877 cpx = (ComplexObject *) JS_malloc(cx, sizeof *cpx);
2878 if (!cpx)
2879 return NULL;
2880 cpx->isInner = JS_TRUE;
2881 cpx->frozen = JS_FALSE;
2882 cpx->inner = NULL;
2883 cpx->outer = outer;
2885 obj = JS_NewObject(cx, &split_global_class.base, NULL, NULL);
2886 if (!obj || !JS_SetParent(cx, obj, NULL) || !JS_SetPrivate(cx, obj, cpx)) {
2887 JS_free(cx, cpx);
2888 return NULL;
2891 outercpx = (ComplexObject *) JS_GetPrivate(cx, outer);
2892 outercpx->inner = obj;
2893 outercpx->frozen = JS_FALSE;
2895 return obj;
2898 static ComplexObject *
2899 split_get_private(JSContext *cx, JSObject *obj)
2901 do {
2902 if (JS_GET_CLASS(cx, obj) == &split_global_class.base)
2903 return (ComplexObject *) JS_GetPrivate(cx, obj);
2904 obj = JS_GetParent(cx, obj);
2905 } while (obj);
2907 return NULL;
2910 static JSBool
2911 sandbox_enumerate(JSContext *cx, JSObject *obj)
2913 jsval v;
2914 JSBool b;
2916 if (!JS_GetProperty(cx, obj, "lazy", &v))
2917 return JS_FALSE;
2919 JS_ValueToBoolean(cx, v, &b);
2920 return !b || JS_EnumerateStandardClasses(cx, obj);
2923 static JSBool
2924 sandbox_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
2925 JSObject **objp)
2927 jsval v;
2928 JSBool b, resolved;
2930 if (!JS_GetProperty(cx, obj, "lazy", &v))
2931 return JS_FALSE;
2933 JS_ValueToBoolean(cx, v, &b);
2934 if (b && (flags & JSRESOLVE_ASSIGNING) == 0) {
2935 if (!JS_ResolveStandardClass(cx, obj, id, &resolved))
2936 return JS_FALSE;
2937 if (resolved) {
2938 *objp = obj;
2939 return JS_TRUE;
2942 *objp = NULL;
2943 return JS_TRUE;
2946 static JSClass sandbox_class = {
2947 "sandbox",
2948 JSCLASS_NEW_RESOLVE | JSCLASS_GLOBAL_FLAGS,
2949 JS_PropertyStub, JS_PropertyStub,
2950 JS_PropertyStub, JS_PropertyStub,
2951 sandbox_enumerate, (JSResolveOp)sandbox_resolve,
2952 JS_ConvertStub, NULL,
2953 JSCLASS_NO_OPTIONAL_MEMBERS
2956 static JSBool
2957 EvalInContext(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
2958 jsval *rval)
2960 JSString *str;
2961 JSObject *sobj;
2962 JSContext *scx;
2963 const jschar *src;
2964 size_t srclen;
2965 JSBool lazy, split, ok;
2966 JSStackFrame *fp;
2968 sobj = NULL;
2969 if (!JS_ConvertArguments(cx, argc, argv, "S / o", &str, &sobj))
2970 return JS_FALSE;
2972 scx = NewContext(JS_GetRuntime(cx));
2973 if (!scx) {
2974 JS_ReportOutOfMemory(cx);
2975 return JS_FALSE;
2977 JS_SetOptions(scx, JS_GetOptions(cx));
2979 JS_TransferRequest(cx, scx);
2980 src = JS_GetStringChars(str);
2981 srclen = JS_GetStringLength(str);
2982 split = lazy = JS_FALSE;
2983 if (srclen == 4) {
2984 if (src[0] == 'l' && src[1] == 'a' && src[2] == 'z' && src[3] == 'y') {
2985 lazy = JS_TRUE;
2986 srclen = 0;
2988 } else if (srclen == 5) {
2989 if (src[0] == 's' && src[1] == 'p' && src[2] == 'l' && src[3] == 'i' && src[4] == 't') {
2990 split = lazy = JS_TRUE;
2991 srclen = 0;
2995 if (!sobj) {
2996 sobj = split
2997 ? split_setup(scx, JS_TRUE)
2998 : JS_NewObject(scx, &sandbox_class, NULL, NULL);
2999 if (!sobj || (!lazy && !JS_InitStandardClasses(scx, sobj))) {
3000 ok = JS_FALSE;
3001 goto out;
3003 AutoValueRooter root(scx, BOOLEAN_TO_JSVAL(lazy));
3004 ok = JS_SetProperty(scx, sobj, "lazy", root.addr());
3005 if (!ok)
3006 goto out;
3007 if (split)
3008 sobj = split_outerObject(scx, sobj);
3011 if (srclen == 0) {
3012 *rval = OBJECT_TO_JSVAL(sobj);
3013 ok = JS_TRUE;
3014 } else {
3015 fp = JS_GetScriptedCaller(cx, NULL);
3016 JS_SetGlobalObject(scx, sobj);
3017 JS_ToggleOptions(scx, JSOPTION_DONT_REPORT_UNCAUGHT);
3018 OBJ_TO_INNER_OBJECT(scx, sobj);
3019 if (!sobj) {
3020 ok = JS_FALSE;
3021 goto out;
3023 ok = JS_EvaluateUCScript(scx, sobj, src, srclen,
3024 fp->script->filename,
3025 JS_PCToLineNumber(cx, fp->script,
3026 fp->regs->pc),
3027 rval);
3030 out:
3031 jsval exceptionValue = JSVAL_NULL;
3032 JSBool exception = !ok && JS_GetPendingException(scx, &exceptionValue);
3034 JS_TransferRequest(scx, cx);
3035 if (exception)
3036 JS_SetPendingException(cx, exceptionValue);
3037 else if (!ok)
3038 JS_ClearPendingException(cx);
3040 DestroyContext(scx, false);
3041 return ok;
3044 static JSBool
3045 EvalInFrame(JSContext *cx, uintN argc, jsval *vp)
3047 jsval *argv = JS_ARGV(cx, vp);
3048 if (argc < 2 ||
3049 !JSVAL_IS_INT(argv[0]) ||
3050 !JSVAL_IS_STRING(argv[1])) {
3051 JS_ReportError(cx, "Invalid arguments to evalInFrame");
3052 return JS_FALSE;
3055 uint32 upCount = JSVAL_TO_INT(argv[0]);
3056 JSString *str = JSVAL_TO_STRING(argv[1]);
3058 bool saveCurrent = (argc >= 3 && JSVAL_IS_BOOLEAN(argv[2]))
3059 ? !!(JSVAL_TO_SPECIAL(argv[2]))
3060 : false;
3062 JS_ASSERT(cx->fp);
3064 JSStackFrame *fp = cx->fp;
3065 for (uint32 i = 0; i < upCount; ++i) {
3066 if (!fp->down)
3067 break;
3068 fp = fp->down;
3071 if (!fp->script) {
3072 JS_ReportError(cx, "cannot eval in non-script frame");
3073 return JS_FALSE;
3076 JSStackFrame *oldfp = NULL;
3077 if (saveCurrent)
3078 oldfp = JS_SaveFrameChain(cx);
3080 JSBool ok = JS_EvaluateUCInStackFrame(cx, fp, str->chars(), str->length(),
3081 fp->script->filename,
3082 JS_PCToLineNumber(cx, fp->script,
3083 fp->regs->pc),
3084 vp);
3086 if (saveCurrent)
3087 JS_RestoreFrameChain(cx, oldfp);
3089 return ok;
3092 static JSBool
3093 ShapeOf(JSContext *cx, uintN argc, jsval *vp)
3095 jsval v = JS_ARGV(cx, vp)[0];
3096 if (!JSVAL_IS_OBJECT(v)) {
3097 JS_ReportError(cx, "shapeOf: object expected");
3098 return JS_FALSE;
3100 JSObject *obj = JSVAL_TO_OBJECT(v);
3101 if (!obj) {
3102 *vp = JSVAL_ZERO;
3103 return JS_TRUE;
3105 if (!obj->isNative()) {
3106 *vp = INT_TO_JSVAL(-1);
3107 return JS_TRUE;
3109 return JS_NewNumberValue(cx, obj->shape(), vp);
3112 #ifdef JS_THREADSAFE
3115 * Check that t1 comes strictly before t2. The function correctly deals with
3116 * PRIntervalTime wrap-around between t2 and t1 assuming that t2 and t1 stays
3117 * within INT32_MAX from each other. We use MAX_TIMEOUT_INTERVAL to enforce
3118 * this restriction.
3120 static bool
3121 IsBefore(PRIntervalTime t1, PRIntervalTime t2)
3123 return int32(t1 - t2) < 0;
3126 static JSBool
3127 Sleep_fn(JSContext *cx, uintN argc, jsval *vp)
3129 PRIntervalTime t_ticks;
3131 if (argc == 0) {
3132 t_ticks = 0;
3133 } else {
3134 jsdouble t_secs;
3136 if (!JS_ValueToNumber(cx, argc == 0 ? JSVAL_VOID : vp[2], &t_secs))
3137 return JS_FALSE;
3139 /* NB: The next condition also filter out NaNs. */
3140 if (!(t_secs <= MAX_TIMEOUT_INTERVAL)) {
3141 JS_ReportError(cx, "Excessive sleep interval");
3142 return JS_FALSE;
3144 t_ticks = (t_secs <= 0.0)
3146 : PRIntervalTime(PR_TicksPerSecond() * t_secs);
3148 if (t_ticks == 0) {
3149 JS_YieldRequest(cx);
3150 } else {
3151 JSAutoSuspendRequest suspended(cx);
3152 PR_Lock(gWatchdogLock);
3153 PRIntervalTime to_wakeup = PR_IntervalNow() + t_ticks;
3154 for (;;) {
3155 PR_WaitCondVar(gSleepWakeup, t_ticks);
3156 if (gCanceled)
3157 break;
3158 PRIntervalTime now = PR_IntervalNow();
3159 if (!IsBefore(now, to_wakeup))
3160 break;
3161 t_ticks = to_wakeup - now;
3163 PR_Unlock(gWatchdogLock);
3165 return !gCanceled;
3168 typedef struct ScatterThreadData ScatterThreadData;
3169 typedef struct ScatterData ScatterData;
3171 typedef enum ScatterStatus {
3172 SCATTER_WAIT,
3173 SCATTER_GO,
3174 SCATTER_CANCEL
3175 } ScatterStatus;
3177 struct ScatterData {
3178 ScatterThreadData *threads;
3179 jsval *results;
3180 PRLock *lock;
3181 PRCondVar *cvar;
3182 ScatterStatus status;
3185 struct ScatterThreadData {
3186 jsint index;
3187 ScatterData *shared;
3188 PRThread *thr;
3189 JSContext *cx;
3190 jsval fn;
3193 static void
3194 DoScatteredWork(JSContext *cx, ScatterThreadData *td)
3196 jsval *rval = &td->shared->results[td->index];
3198 if (!JS_CallFunctionValue(cx, NULL, td->fn, 0, NULL, rval)) {
3199 *rval = JSVAL_VOID;
3200 JS_GetPendingException(cx, rval);
3201 JS_ClearPendingException(cx);
3205 static void
3206 RunScatterThread(void *arg)
3208 int stackDummy;
3209 ScatterThreadData *td;
3210 ScatterStatus st;
3211 JSContext *cx;
3213 if (PR_FAILURE == PR_SetThreadPrivate(gStackBaseThreadIndex, &stackDummy))
3214 return;
3216 td = (ScatterThreadData *)arg;
3217 cx = td->cx;
3219 /* Wait for our signal. */
3220 PR_Lock(td->shared->lock);
3221 while ((st = td->shared->status) == SCATTER_WAIT)
3222 PR_WaitCondVar(td->shared->cvar, PR_INTERVAL_NO_TIMEOUT);
3223 PR_Unlock(td->shared->lock);
3225 if (st == SCATTER_CANCEL)
3226 return;
3228 /* We are good to go. */
3229 JS_SetContextThread(cx);
3230 SetThreadStackLimit(cx);
3231 JS_BeginRequest(cx);
3232 DoScatteredWork(cx, td);
3233 JS_EndRequest(cx);
3234 JS_ClearContextThread(cx);
3238 * scatter(fnArray) - Call each function in `fnArray` without arguments, each
3239 * in a different thread. When all threads have finished, return an array: the
3240 * return values. Errors are not propagated; if any of the function calls
3241 * fails, the corresponding element in the results array gets the exception
3242 * object, if any, else (undefined).
3244 static JSBool
3245 Scatter(JSContext *cx, uintN argc, jsval *vp)
3247 jsuint i;
3248 jsuint n; /* number of threads */
3249 JSObject *inArr;
3250 JSObject *arr;
3251 JSObject *global;
3252 ScatterData sd;
3253 JSBool ok;
3255 sd.lock = NULL;
3256 sd.cvar = NULL;
3257 sd.results = NULL;
3258 sd.threads = NULL;
3259 sd.status = SCATTER_WAIT;
3261 if (argc == 0 || JSVAL_IS_PRIMITIVE(JS_ARGV(cx, vp)[0])) {
3262 JS_ReportError(cx, "the first argument must be an object");
3263 goto fail;
3266 inArr = JSVAL_TO_OBJECT(JS_ARGV(cx, vp)[0]);
3267 ok = JS_GetArrayLength(cx, inArr, &n);
3268 if (!ok)
3269 goto out;
3270 if (n == 0)
3271 goto success;
3273 sd.lock = PR_NewLock();
3274 if (!sd.lock)
3275 goto fail;
3277 sd.cvar = PR_NewCondVar(sd.lock);
3278 if (!sd.cvar)
3279 goto fail;
3281 sd.results = (jsval *) malloc(n * sizeof(jsval));
3282 if (!sd.results)
3283 goto fail;
3284 for (i = 0; i < n; i++) {
3285 sd.results[i] = JSVAL_VOID;
3286 ok = JS_AddRoot(cx, &sd.results[i]);
3287 if (!ok) {
3288 while (i-- > 0)
3289 JS_RemoveRoot(cx, &sd.results[i]);
3290 free(sd.results);
3291 sd.results = NULL;
3292 goto fail;
3296 sd.threads = (ScatterThreadData *) malloc(n * sizeof(ScatterThreadData));
3297 if (!sd.threads)
3298 goto fail;
3299 for (i = 0; i < n; i++) {
3300 sd.threads[i].index = i;
3301 sd.threads[i].shared = &sd;
3302 sd.threads[i].thr = NULL;
3303 sd.threads[i].cx = NULL;
3304 sd.threads[i].fn = JSVAL_NULL;
3306 ok = JS_AddRoot(cx, &sd.threads[i].fn);
3307 if (ok && !JS_GetElement(cx, inArr, (jsint) i, &sd.threads[i].fn)) {
3308 JS_RemoveRoot(cx, &sd.threads[i].fn);
3309 ok = JS_FALSE;
3311 if (!ok) {
3312 while (i-- > 0)
3313 JS_RemoveRoot(cx, &sd.threads[i].fn);
3314 free(sd.threads);
3315 sd.threads = NULL;
3316 goto fail;
3320 global = JS_GetGlobalObject(cx);
3321 for (i = 1; i < n; i++) {
3322 JSContext *newcx = NewContext(JS_GetRuntime(cx));
3323 if (!newcx)
3324 goto fail;
3327 JSAutoTransferRequest transfer(cx, newcx);
3328 JS_SetGlobalObject(newcx, global);
3330 JS_ClearContextThread(newcx);
3331 sd.threads[i].cx = newcx;
3334 for (i = 1; i < n; i++) {
3335 PRThread *t = PR_CreateThread(PR_USER_THREAD,
3336 RunScatterThread,
3337 &sd.threads[i],
3338 PR_PRIORITY_NORMAL,
3339 PR_GLOBAL_THREAD,
3340 PR_JOINABLE_THREAD,
3342 if (!t) {
3343 /* Failed to start thread. */
3344 PR_Lock(sd.lock);
3345 sd.status = SCATTER_CANCEL;
3346 PR_NotifyAllCondVar(sd.cvar);
3347 PR_Unlock(sd.lock);
3348 while (i-- > 1)
3349 PR_JoinThread(sd.threads[i].thr);
3350 goto fail;
3353 sd.threads[i].thr = t;
3355 PR_Lock(sd.lock);
3356 sd.status = SCATTER_GO;
3357 PR_NotifyAllCondVar(sd.cvar);
3358 PR_Unlock(sd.lock);
3360 DoScatteredWork(cx, &sd.threads[0]);
3363 JSAutoSuspendRequest suspended(cx);
3364 for (i = 1; i < n; i++) {
3365 PR_JoinThread(sd.threads[i].thr);
3369 success:
3370 arr = JS_NewArrayObject(cx, n, sd.results);
3371 if (!arr)
3372 goto fail;
3373 *vp = OBJECT_TO_JSVAL(arr);
3374 ok = JS_TRUE;
3376 out:
3377 if (sd.threads) {
3378 JSContext *acx;
3380 for (i = 0; i < n; i++) {
3381 JS_RemoveRoot(cx, &sd.threads[i].fn);
3382 acx = sd.threads[i].cx;
3383 if (acx) {
3384 JS_SetContextThread(acx);
3385 DestroyContext(acx, true);
3388 free(sd.threads);
3390 if (sd.results) {
3391 for (i = 0; i < n; i++)
3392 JS_RemoveRoot(cx, &sd.results[i]);
3393 free(sd.results);
3395 if (sd.cvar)
3396 PR_DestroyCondVar(sd.cvar);
3397 if (sd.lock)
3398 PR_DestroyLock(sd.lock);
3400 return ok;
3402 fail:
3403 ok = JS_FALSE;
3404 goto out;
3407 static bool
3408 InitWatchdog(JSRuntime *rt)
3410 JS_ASSERT(!gWatchdogThread);
3411 gWatchdogLock = PR_NewLock();
3412 if (gWatchdogLock) {
3413 gWatchdogWakeup = PR_NewCondVar(gWatchdogLock);
3414 if (gWatchdogWakeup) {
3415 gSleepWakeup = PR_NewCondVar(gWatchdogLock);
3416 if (gSleepWakeup)
3417 return true;
3418 PR_DestroyCondVar(gWatchdogWakeup);
3420 PR_DestroyLock(gWatchdogLock);
3422 return false;
3425 static void
3426 KillWatchdog()
3428 PRThread *thread;
3430 PR_Lock(gWatchdogLock);
3431 thread = gWatchdogThread;
3432 if (thread) {
3434 * The watchdog thread is running, tell it to terminate waking it up
3435 * if necessary.
3437 gWatchdogThread = NULL;
3438 PR_NotifyCondVar(gWatchdogWakeup);
3440 PR_Unlock(gWatchdogLock);
3441 if (thread)
3442 PR_JoinThread(thread);
3443 PR_DestroyCondVar(gSleepWakeup);
3444 PR_DestroyCondVar(gWatchdogWakeup);
3445 PR_DestroyLock(gWatchdogLock);
3448 static void
3449 WatchdogMain(void *arg)
3451 JSRuntime *rt = (JSRuntime *) arg;
3453 PR_Lock(gWatchdogLock);
3454 while (gWatchdogThread) {
3455 PRIntervalTime now = PR_IntervalNow();
3456 if (gWatchdogHasTimeout && !IsBefore(now, gWatchdogTimeout)) {
3458 * The timeout has just expired. Trigger the operation callback
3459 * outside the lock.
3461 gWatchdogHasTimeout = false;
3462 PR_Unlock(gWatchdogLock);
3463 CancelExecution(rt);
3464 PR_Lock(gWatchdogLock);
3466 /* Wake up any threads doing sleep. */
3467 PR_NotifyAllCondVar(gSleepWakeup);
3468 } else {
3469 PRIntervalTime sleepDuration = gWatchdogHasTimeout
3470 ? gWatchdogTimeout - now
3471 : PR_INTERVAL_NO_TIMEOUT;
3472 #ifdef DEBUG
3473 PRStatus status =
3474 #endif
3475 PR_WaitCondVar(gWatchdogWakeup, sleepDuration);
3476 JS_ASSERT(status == PR_SUCCESS);
3479 PR_Unlock(gWatchdogLock);
3482 static bool
3483 ScheduleWatchdog(JSRuntime *rt, jsdouble t)
3485 if (t <= 0) {
3486 PR_Lock(gWatchdogLock);
3487 gWatchdogHasTimeout = false;
3488 PR_Unlock(gWatchdogLock);
3489 return true;
3492 PRIntervalTime interval = PRIntervalTime(ceil(t * PR_TicksPerSecond()));
3493 PRIntervalTime timeout = PR_IntervalNow() + interval;
3494 PR_Lock(gWatchdogLock);
3495 if (!gWatchdogThread) {
3496 JS_ASSERT(!gWatchdogHasTimeout);
3497 gWatchdogThread = PR_CreateThread(PR_USER_THREAD,
3498 WatchdogMain,
3500 PR_PRIORITY_NORMAL,
3501 PR_LOCAL_THREAD,
3502 PR_JOINABLE_THREAD,
3504 if (!gWatchdogThread) {
3505 PR_Unlock(gWatchdogLock);
3506 return false;
3508 } else if (!gWatchdogHasTimeout || IsBefore(timeout, gWatchdogTimeout)) {
3509 PR_NotifyCondVar(gWatchdogWakeup);
3511 gWatchdogHasTimeout = true;
3512 gWatchdogTimeout = timeout;
3513 PR_Unlock(gWatchdogLock);
3514 return true;
3517 #else /* !JS_THREADSAFE */
3519 #ifdef XP_WIN
3520 static HANDLE gTimerHandle = 0;
3522 VOID CALLBACK
3523 TimerCallback(PVOID lpParameter, BOOLEAN TimerOrWaitFired)
3525 CancelExecution((JSRuntime *) lpParameter);
3528 #else
3530 static void
3531 AlarmHandler(int sig)
3533 CancelExecution(gRuntime);
3536 #endif
3538 static bool
3539 InitWatchdog(JSRuntime *rt)
3541 gRuntime = rt;
3542 return true;
3545 static void
3546 KillWatchdog()
3548 ScheduleWatchdog(gRuntime, -1);
3551 static bool
3552 ScheduleWatchdog(JSRuntime *rt, jsdouble t)
3554 #ifdef XP_WIN
3555 if (gTimerHandle) {
3556 DeleteTimerQueueTimer(NULL, gTimerHandle, NULL);
3557 gTimerHandle = 0;
3559 if (t > 0 &&
3560 !CreateTimerQueueTimer(&gTimerHandle,
3561 NULL,
3562 (WAITORTIMERCALLBACK)TimerCallback,
3564 DWORD(ceil(t * 1000.0)),
3566 WT_EXECUTEINTIMERTHREAD | WT_EXECUTEONLYONCE)) {
3567 gTimerHandle = 0;
3568 return false;
3570 #else
3571 /* FIXME: use setitimer when available for sub-second resolution. */
3572 if (t <= 0) {
3573 alarm(0);
3574 signal(SIGALRM, NULL);
3575 } else {
3576 signal(SIGALRM, AlarmHandler); /* set the Alarm signal capture */
3577 alarm(ceil(t));
3579 #endif
3580 return true;
3583 #endif /* !JS_THREADSAFE */
3585 static void
3586 CancelExecution(JSRuntime *rt)
3588 gCanceled = true;
3589 if (gExitCode == 0)
3590 gExitCode = EXITCODE_TIMEOUT;
3591 #ifdef JS_THREADSAFE
3592 if (gWorkers) {
3593 JSContext *cx = JS_NewContext(rt, 8192);
3594 if (cx)
3595 js::workers::terminateAll(cx, gWorkers);
3596 JS_DestroyContextNoGC(cx);
3598 #endif
3599 JS_TriggerAllOperationCallbacks(rt);
3601 static const char msg[] = "Script runs for too long, terminating.\n";
3602 #if defined(XP_UNIX) && !defined(JS_THREADSAFE)
3603 /* It is not safe to call fputs from signals. */
3604 /* Dummy assignment avoids GCC warning on "attribute warn_unused_result" */
3605 ssize_t dummy = write(2, msg, sizeof(msg) - 1);
3606 (void)dummy;
3607 #else
3608 fputs(msg, stderr);
3609 #endif
3612 static JSBool
3613 SetTimeoutValue(JSContext *cx, jsdouble t)
3615 /* NB: The next condition also filter out NaNs. */
3616 if (!(t <= MAX_TIMEOUT_INTERVAL)) {
3617 JS_ReportError(cx, "Excessive timeout value");
3618 return JS_FALSE;
3620 gTimeoutInterval = t;
3621 if (!ScheduleWatchdog(cx->runtime, t)) {
3622 JS_ReportError(cx, "Failed to create the watchdog");
3623 return JS_FALSE;
3625 return JS_TRUE;
3628 static JSBool
3629 Timeout(JSContext *cx, uintN argc, jsval *vp)
3631 if (argc == 0)
3632 return JS_NewNumberValue(cx, gTimeoutInterval, vp);
3634 if (argc > 1) {
3635 JS_ReportError(cx, "Wrong number of arguments");
3636 return JS_FALSE;
3639 jsdouble t;
3640 if (!JS_ValueToNumber(cx, JS_ARGV(cx, vp)[0], &t))
3641 return JS_FALSE;
3643 *vp = JSVAL_VOID;
3644 return SetTimeoutValue(cx, t);
3647 static JSBool
3648 Elapsed(JSContext *cx, uintN argc, jsval *vp)
3650 if (argc == 0) {
3651 double d = 0.0;
3652 JSShellContextData *data = GetContextData(cx);
3653 if (data)
3654 d = js_IntervalNow() - data->startTime;
3655 return JS_NewNumberValue(cx, d, vp);
3657 JS_ReportError(cx, "Wrong number of arguments");
3658 return JS_FALSE;
3661 static JSBool
3662 Parent(JSContext *cx, uintN argc, jsval *vp)
3664 if (argc != 1) {
3665 JS_ReportError(cx, "Wrong number of arguments");
3666 return JS_FALSE;
3669 jsval v = JS_ARGV(cx, vp)[0];
3670 if (JSVAL_IS_PRIMITIVE(v)) {
3671 JS_ReportError(cx, "Only objects have parents!");
3672 return JS_FALSE;
3675 JSObject *parent = JS_GetParent(cx, JSVAL_TO_OBJECT(v));
3676 *vp = OBJECT_TO_JSVAL(parent);
3678 /* Outerize if necessary. Embrace the ugliness! */
3679 JSClass *clasp = JS_GET_CLASS(cx, parent);
3680 if (clasp->flags & JSCLASS_IS_EXTENDED) {
3681 JSExtendedClass *xclasp = reinterpret_cast<JSExtendedClass *>(clasp);
3682 if (JSObjectOp outerize = xclasp->outerObject)
3683 *vp = OBJECT_TO_JSVAL(outerize(cx, parent));
3686 return JS_TRUE;
3689 #ifdef XP_UNIX
3691 #include <fcntl.h>
3692 #include <sys/stat.h>
3695 * Returns a JS_malloc'd string (that the caller needs to JS_free)
3696 * containing the directory (non-leaf) part of |from| prepended to |leaf|.
3697 * If |from| is empty or a leaf, MakeAbsolutePathname returns a copy of leaf.
3698 * Returns NULL to indicate an error.
3700 static char *
3701 MakeAbsolutePathname(JSContext *cx, const char *from, const char *leaf)
3703 size_t dirlen;
3704 char *dir;
3705 const char *slash = NULL, *cp;
3707 cp = from;
3708 while (*cp) {
3709 if (*cp == '/') {
3710 slash = cp;
3713 ++cp;
3716 if (!slash) {
3717 /* We were given a leaf or |from| was empty. */
3718 return JS_strdup(cx, leaf);
3721 /* Else, we were given a real pathname, return that + the leaf. */
3722 dirlen = slash - from + 1;
3723 dir = (char*) JS_malloc(cx, dirlen + strlen(leaf) + 1);
3724 if (!dir)
3725 return NULL;
3727 strncpy(dir, from, dirlen);
3728 strcpy(dir + dirlen, leaf); /* Note: we can't use strcat here. */
3730 return dir;
3733 #endif // XP_UNIX
3735 static JSBool
3736 Compile(JSContext *cx, uintN argc, jsval *vp)
3738 if (argc < 1) {
3739 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
3740 "compile", "0", "s");
3741 return JS_FALSE;
3743 jsval arg0 = JS_ARGV(cx, vp)[0];
3744 if (!JSVAL_IS_STRING(arg0)) {
3745 const char *typeName = JS_GetTypeName(cx, JS_TypeOfValue(cx, arg0));
3746 JS_ReportError(cx, "expected string to compile, got %s", typeName);
3747 return JS_FALSE;
3750 JSString *scriptContents = JSVAL_TO_STRING(arg0);
3751 JSScript *result = JS_CompileUCScript(cx, NULL, JS_GetStringCharsZ(cx, scriptContents),
3752 JS_GetStringLength(scriptContents), "<string>", 0);
3753 if (!result)
3754 return JS_FALSE;
3756 JS_DestroyScript(cx, result);
3757 JS_SET_RVAL(cx, vp, JSVAL_VOID);
3758 return JS_TRUE;
3761 static JSBool
3762 Parse(JSContext *cx, uintN argc, jsval *vp)
3764 if (argc < 1) {
3765 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
3766 "compile", "0", "s");
3767 return JS_FALSE;
3769 jsval arg0 = JS_ARGV(cx, vp)[0];
3770 if (!JSVAL_IS_STRING(arg0)) {
3771 const char *typeName = JS_GetTypeName(cx, JS_TypeOfValue(cx, arg0));
3772 JS_ReportError(cx, "expected string to parse, got %s", typeName);
3773 return JS_FALSE;
3776 JSString *scriptContents = JSVAL_TO_STRING(arg0);
3777 js::Parser parser(cx);
3778 parser.init(JS_GetStringCharsZ(cx, scriptContents), JS_GetStringLength(scriptContents),
3779 NULL, "<string>", 0);
3780 if (!parser.parse(NULL))
3781 return JS_FALSE;
3782 JS_SET_RVAL(cx, vp, JSVAL_VOID);
3783 return JS_TRUE;
3786 static JSBool
3787 Snarf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
3789 JSString *str;
3790 const char *filename;
3791 const char *pathname;
3792 JSStackFrame *fp;
3793 JSBool ok;
3794 size_t cc, len;
3795 char *buf;
3796 FILE *file;
3798 str = JS_ValueToString(cx, argv[0]);
3799 if (!str)
3800 return JS_FALSE;
3801 filename = JS_GetStringBytes(str);
3803 /* Get the currently executing script's name. */
3804 fp = JS_GetScriptedCaller(cx, NULL);
3805 JS_ASSERT(fp && fp->script->filename);
3806 #ifdef XP_UNIX
3807 pathname = MakeAbsolutePathname(cx, fp->script->filename, filename);
3808 if (!pathname)
3809 return JS_FALSE;
3810 #else
3811 pathname = filename;
3812 #endif
3814 ok = JS_FALSE;
3815 len = 0;
3816 buf = NULL;
3817 file = fopen(pathname, "rb");
3818 if (!file) {
3819 JS_ReportError(cx, "can't open %s: %s", pathname, strerror(errno));
3820 } else {
3821 if (fseek(file, 0, SEEK_END) == EOF) {
3822 JS_ReportError(cx, "can't seek end of %s", pathname);
3823 } else {
3824 len = ftell(file);
3825 if (fseek(file, 0, SEEK_SET) == EOF) {
3826 JS_ReportError(cx, "can't seek start of %s", pathname);
3827 } else {
3828 buf = (char*) JS_malloc(cx, len + 1);
3829 if (buf) {
3830 cc = fread(buf, 1, len, file);
3831 if (cc != len) {
3832 JS_ReportError(cx, "can't read %s: %s", pathname,
3833 (ptrdiff_t(cc) < 0) ? strerror(errno) : "short read");
3834 } else {
3835 len = (size_t)cc;
3836 ok = JS_TRUE;
3841 fclose(file);
3843 JS_free(cx, (void*)pathname);
3844 if (!ok) {
3845 JS_free(cx, buf);
3846 return ok;
3849 buf[len] = '\0';
3850 str = JS_NewString(cx, buf, len);
3851 if (!str) {
3852 JS_free(cx, buf);
3853 return JS_FALSE;
3855 *rval = STRING_TO_JSVAL(str);
3856 return JS_TRUE;
3859 /* We use a mix of JS_FS and JS_FN to test both kinds of natives. */
3860 static JSFunctionSpec shell_functions[] = {
3861 JS_FS("version", Version, 0,0,0),
3862 JS_FS("options", Options, 0,0,0),
3863 JS_FS("load", Load, 1,0,0),
3864 JS_FN("readline", ReadLine, 0,0),
3865 JS_FN("print", Print, 0,0),
3866 JS_FS("help", Help, 0,0,0),
3867 JS_FS("quit", Quit, 0,0,0),
3868 JS_FN("assertEq", AssertEq, 2,0),
3869 JS_FN("gc", ::GC, 0,0),
3870 #ifdef JS_GCMETER
3871 JS_FN("gcstats", GCStats, 0,0),
3872 #endif
3873 JS_FN("gcparam", GCParameter, 2,0),
3874 JS_FN("countHeap", CountHeap, 0,0),
3875 #ifdef JS_GC_ZEAL
3876 JS_FN("gczeal", GCZeal, 1,0),
3877 #endif
3878 JS_FS("trap", Trap, 3,0,0),
3879 JS_FS("untrap", Untrap, 2,0,0),
3880 JS_FS("line2pc", LineToPC, 0,0,0),
3881 JS_FS("pc2line", PCToLine, 0,0,0),
3882 JS_FN("stackQuota", StackQuota, 0,0),
3883 JS_FS("stringsAreUTF8", StringsAreUTF8, 0,0,0),
3884 JS_FS("testUTF8", TestUTF8, 1,0,0),
3885 JS_FS("throwError", ThrowError, 0,0,0),
3886 #ifdef DEBUG
3887 JS_FS("dis", Disassemble, 1,0,0),
3888 JS_FS("disfile", DisassFile, 1,0,0),
3889 JS_FS("dissrc", DisassWithSrc, 1,0,0),
3890 JS_FN("dumpHeap", DumpHeap, 0,0),
3891 JS_FS("notes", Notes, 1,0,0),
3892 JS_FS("tracing", Tracing, 0,0,0),
3893 JS_FS("stats", DumpStats, 1,0,0),
3894 #endif
3895 #ifdef TEST_CVTARGS
3896 JS_FS("cvtargs", ConvertArgs, 0,0,12),
3897 #endif
3898 JS_FN("build", BuildDate, 0,0),
3899 JS_FS("clear", Clear, 0,0,0),
3900 JS_FN("intern", Intern, 1,0),
3901 JS_FS("clone", Clone, 1,0,0),
3902 JS_FS("seal", Seal, 1,0,1),
3903 JS_FN("getpda", GetPDA, 1,0),
3904 JS_FN("getslx", GetSLX, 1,0),
3905 JS_FN("toint32", ToInt32, 1,0),
3906 JS_FS("evalcx", EvalInContext, 1,0,0),
3907 JS_FN("evalInFrame", EvalInFrame, 2,0),
3908 JS_FN("shapeOf", ShapeOf, 1,0),
3909 #ifdef MOZ_SHARK
3910 JS_FS("startShark", js_StartShark, 0,0,0),
3911 JS_FS("stopShark", js_StopShark, 0,0,0),
3912 JS_FS("connectShark", js_ConnectShark, 0,0,0),
3913 JS_FS("disconnectShark",js_DisconnectShark, 0,0,0),
3914 #endif
3915 #ifdef MOZ_CALLGRIND
3916 JS_FS("startCallgrind", js_StartCallgrind, 0,0,0),
3917 JS_FS("stopCallgrind", js_StopCallgrind, 0,0,0),
3918 JS_FS("dumpCallgrind", js_DumpCallgrind, 1,0,0),
3919 #endif
3920 #ifdef MOZ_VTUNE
3921 JS_FS("startVtune", js_StartVtune, 1,0,0),
3922 JS_FS("stopVtune", js_StopVtune, 0,0,0),
3923 JS_FS("pauseVtune", js_PauseVtune, 0,0,0),
3924 JS_FS("resumeVtune", js_ResumeVtune, 0,0,0),
3925 #endif
3926 #ifdef MOZ_TRACEVIS
3927 JS_FS("startTraceVis", StartTraceVisNative, 1,0,0),
3928 JS_FS("stopTraceVis", StopTraceVisNative, 0,0,0),
3929 #endif
3930 #ifdef DEBUG_ARRAYS
3931 JS_FS("arrayInfo", js_ArrayInfo, 1,0,0),
3932 #endif
3933 #ifdef JS_THREADSAFE
3934 JS_FN("sleep", Sleep_fn, 1,0),
3935 JS_FN("scatter", Scatter, 1,0),
3936 #endif
3937 JS_FS("snarf", Snarf, 0,0,0),
3938 JS_FN("compile", Compile, 1,0),
3939 JS_FN("parse", Parse, 1,0),
3940 JS_FN("timeout", Timeout, 1,0),
3941 JS_FN("elapsed", Elapsed, 0,0),
3942 JS_FN("parent", Parent, 1,0),
3943 JS_FS_END
3946 static const char shell_help_header[] =
3947 "Command Description\n"
3948 "======= ===========\n";
3950 static const char *const shell_help_messages[] = {
3951 "version([number]) Get or set JavaScript version number",
3952 "options([option ...]) Get or toggle JavaScript options",
3953 "load(['foo.js' ...]) Load files named by string arguments",
3954 "readline() Read a single line from stdin",
3955 "print([exp ...]) Evaluate and print expressions",
3956 "help([name ...]) Display usage and help messages",
3957 "quit() Quit the shell",
3958 "assertEq(actual, expected[, msg])\n"
3959 " Throw if the first two arguments are not the same (both +0 or both -0,\n"
3960 " both NaN, or non-zero and ===)",
3961 "gc() Run the garbage collector",
3962 #ifdef JS_GCMETER
3963 "gcstats() Print garbage collector statistics",
3964 #endif
3965 "gcparam(name, value)\n"
3966 " Wrapper for JS_SetGCParameter. The name must be either 'maxBytes' or\n"
3967 " 'maxMallocBytes' and the value must be convertable to a positive uint32",
3968 "countHeap([start[, kind]])\n"
3969 " Count the number of live GC things in the heap or things reachable from\n"
3970 " start when it is given and is not null. kind is either 'all' (default) to\n"
3971 " count all things or one of 'object', 'double', 'string', 'function',\n"
3972 " 'qname', 'namespace', 'xml' to count only things of that kind",
3973 #ifdef JS_GC_ZEAL
3974 "gczeal(level) How zealous the garbage collector should be",
3975 #endif
3976 "trap([fun, [pc,]] exp) Trap bytecode execution",
3977 "untrap(fun[, pc]) Remove a trap",
3978 "line2pc([fun,] line) Map line number to PC",
3979 "pc2line(fun[, pc]) Map PC to line number",
3980 "stackQuota([number]) Query/set script stack quota",
3981 "stringsAreUTF8() Check if strings are UTF-8 encoded",
3982 "testUTF8(mode) Perform UTF-8 tests (modes are 1 to 4)",
3983 "throwError() Throw an error from JS_ReportError",
3984 #ifdef DEBUG
3985 "dis([fun]) Disassemble functions into bytecodes",
3986 "disfile('foo.js') Disassemble script file into bytecodes",
3987 "dissrc([fun]) Disassemble functions with source lines",
3988 "dumpHeap([fileName[, start[, toFind[, maxDepth[, toIgnore]]]]])\n"
3989 " Interface to JS_DumpHeap with output sent to file",
3990 "notes([fun]) Show source notes for functions",
3991 "tracing([true|false|filename]) Turn bytecode execution tracing on/off.\n"
3992 " With filename, send to file.\n",
3993 "stats([string ...]) Dump 'arena', 'atom', 'global' stats",
3994 #endif
3995 #ifdef TEST_CVTARGS
3996 "cvtargs(arg1..., arg12) Test argument formatter",
3997 #endif
3998 "build() Show build date and time",
3999 "clear([obj]) Clear properties of object",
4000 "intern(str) Internalize str in the atom table",
4001 "clone(fun[, scope]) Clone function object",
4002 "seal(obj[, deep]) Seal object, or object graph if deep",
4003 "getpda(obj) Get the property descriptors for obj",
4004 "getslx(obj) Get script line extent",
4005 "toint32(n) Testing hook for JS_ValueToInt32",
4006 "evalcx(s[, o])\n"
4007 " Evaluate s in optional sandbox object o\n"
4008 " if (s == '' && !o) return new o with eager standard classes\n"
4009 " if (s == 'lazy' && !o) return new o with lazy standard classes\n"
4010 " if (s == 'split' && !o) return new split-object o with lazy standard classes",
4011 "evalInFrame(n,str,save) Evaluate 'str' in the nth up frame.\n"
4012 " If 'save' (default false), save the frame chain",
4013 "shapeOf(obj) Get the shape of obj (an implementation detail)",
4014 #ifdef MOZ_SHARK
4015 "startShark() Start a Shark session.\n"
4016 " Shark must be running with programatic sampling",
4017 "stopShark() Stop a running Shark session",
4018 "connectShark() Connect to Shark.\n"
4019 " The -k switch does this automatically",
4020 "disconnectShark() Disconnect from Shark",
4021 #endif
4022 #ifdef MOZ_CALLGRIND
4023 "startCallgrind() Start callgrind instrumentation",
4024 "stopCallgrind() Stop callgrind instrumentation",
4025 "dumpCallgrind([name]) Dump callgrind counters",
4026 #endif
4027 #ifdef MOZ_VTUNE
4028 "startVtune([filename]) Start vtune instrumentation",
4029 "stopVtune() Stop vtune instrumentation",
4030 "pauseVtune() Pause vtune collection",
4031 "resumeVtune() Resume vtune collection",
4032 #endif
4033 #ifdef MOZ_TRACEVIS
4034 "startTraceVis(filename) Start TraceVis recording (stops any current recording)",
4035 "stopTraceVis() Stop TraceVis recording",
4036 #endif
4037 #ifdef DEBUG_ARRAYS
4038 "arrayInfo(a1, a2, ...) Report statistics about arrays",
4039 #endif
4040 #ifdef JS_THREADSAFE
4041 "sleep(dt) Sleep for dt seconds",
4042 "scatter(fns) Call functions concurrently (ignoring errors)",
4043 #endif
4044 "snarf(filename) Read filename into returned string",
4045 "compile(code) Compiles a string to bytecode, potentially throwing",
4046 "parse(code) Parses a string, potentially throwing",
4047 "timeout([seconds])\n"
4048 " Get/Set the limit in seconds for the execution time for the current context.\n"
4049 " A negative value (default) means that the execution time is unlimited.",
4050 "elapsed() Execution time elapsed for the current context.",
4051 "parent(obj) Returns the parent of obj.\n",
4054 /* Help messages must match shell functions. */
4055 JS_STATIC_ASSERT(JS_ARRAY_LENGTH(shell_help_messages) + 1 ==
4056 JS_ARRAY_LENGTH(shell_functions));
4058 #ifdef DEBUG
4059 static void
4060 CheckHelpMessages()
4062 const char *const *m;
4063 const char *lp;
4065 /* Each message must begin with "function_name(" prefix. */
4066 for (m = shell_help_messages; m != JS_ARRAY_END(shell_help_messages); ++m) {
4067 lp = strchr(*m, '(');
4068 JS_ASSERT(lp);
4069 JS_ASSERT(memcmp(shell_functions[m - shell_help_messages].name,
4070 *m, lp - *m) == 0);
4073 #else
4074 # define CheckHelpMessages() ((void) 0)
4075 #endif
4077 static JSBool
4078 Help(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
4080 uintN i, j;
4081 int did_header, did_something;
4082 JSType type;
4083 JSFunction *fun;
4084 JSString *str;
4085 const char *bytes;
4087 fprintf(gOutFile, "%s\n", JS_GetImplementationVersion());
4088 if (argc == 0) {
4089 fputs(shell_help_header, gOutFile);
4090 for (i = 0; shell_functions[i].name; i++)
4091 fprintf(gOutFile, "%s\n", shell_help_messages[i]);
4092 } else {
4093 did_header = 0;
4094 for (i = 0; i < argc; i++) {
4095 did_something = 0;
4096 type = JS_TypeOfValue(cx, argv[i]);
4097 if (type == JSTYPE_FUNCTION) {
4098 fun = JS_ValueToFunction(cx, argv[i]);
4099 str = fun->atom ? ATOM_TO_STRING(fun->atom) : NULL;
4100 } else if (type == JSTYPE_STRING) {
4101 str = JSVAL_TO_STRING(argv[i]);
4102 } else {
4103 str = NULL;
4105 if (str) {
4106 bytes = JS_GetStringBytes(str);
4107 for (j = 0; shell_functions[j].name; j++) {
4108 if (!strcmp(bytes, shell_functions[j].name)) {
4109 if (!did_header) {
4110 did_header = 1;
4111 fputs(shell_help_header, gOutFile);
4113 did_something = 1;
4114 fprintf(gOutFile, "%s\n", shell_help_messages[j]);
4115 break;
4119 if (!did_something) {
4120 str = JS_ValueToString(cx, argv[i]);
4121 if (!str)
4122 return JS_FALSE;
4123 fprintf(gErrFile, "Sorry, no help for %s\n",
4124 JS_GetStringBytes(str));
4128 return JS_TRUE;
4131 static JSObject *
4132 split_setup(JSContext *cx, JSBool evalcx)
4134 JSObject *outer, *inner, *arguments;
4136 outer = split_create_outer(cx);
4137 if (!outer)
4138 return NULL;
4139 JS_SetGlobalObject(cx, outer);
4141 inner = split_create_inner(cx, outer);
4142 if (!inner)
4143 return NULL;
4145 if (!evalcx) {
4146 if (!JS_DefineFunctions(cx, inner, shell_functions))
4147 return NULL;
4149 /* Create a dummy arguments object. */
4150 arguments = JS_NewArrayObject(cx, 0, NULL);
4151 if (!arguments ||
4152 !JS_DefineProperty(cx, inner, "arguments", OBJECT_TO_JSVAL(arguments),
4153 NULL, NULL, 0)) {
4154 return NULL;
4158 JS_ClearScope(cx, outer);
4160 #ifndef LAZY_STANDARD_CLASSES
4161 if (!JS_InitStandardClasses(cx, inner))
4162 return NULL;
4163 #endif
4165 return inner;
4169 * Define a JS object called "it". Give it class operations that printf why
4170 * they're being called for tutorial purposes.
4172 enum its_tinyid {
4173 ITS_COLOR, ITS_HEIGHT, ITS_WIDTH, ITS_FUNNY, ITS_ARRAY, ITS_RDONLY,
4174 ITS_CUSTOM, ITS_CUSTOMRDONLY
4177 static JSBool
4178 its_getter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
4180 jsval *val = (jsval *) JS_GetPrivate(cx, obj);
4181 *vp = val ? *val : JSVAL_VOID;
4182 return JS_TRUE;
4185 static JSBool
4186 its_setter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
4188 jsval *val = (jsval *) JS_GetPrivate(cx, obj);
4189 if (val) {
4190 *val = *vp;
4191 return JS_TRUE;
4194 val = new jsval;
4195 if (!val) {
4196 JS_ReportOutOfMemory(cx);
4197 return JS_FALSE;
4200 if (!JS_AddRoot(cx, val)) {
4201 delete val;
4202 return JS_FALSE;
4205 if (!JS_SetPrivate(cx, obj, (void*)val)) {
4206 JS_RemoveRoot(cx, val);
4207 delete val;
4208 return JS_FALSE;
4211 *val = *vp;
4212 return JS_TRUE;
4215 static JSPropertySpec its_props[] = {
4216 {"color", ITS_COLOR, JSPROP_ENUMERATE, NULL, NULL},
4217 {"height", ITS_HEIGHT, JSPROP_ENUMERATE, NULL, NULL},
4218 {"width", ITS_WIDTH, JSPROP_ENUMERATE, NULL, NULL},
4219 {"funny", ITS_FUNNY, JSPROP_ENUMERATE, NULL, NULL},
4220 {"array", ITS_ARRAY, JSPROP_ENUMERATE, NULL, NULL},
4221 {"rdonly", ITS_RDONLY, JSPROP_READONLY, NULL, NULL},
4222 {"custom", ITS_CUSTOM, JSPROP_ENUMERATE,
4223 its_getter, its_setter},
4224 {"customRdOnly", ITS_CUSTOMRDONLY, JSPROP_ENUMERATE | JSPROP_READONLY,
4225 its_getter, its_setter},
4226 {NULL,0,0,NULL,NULL}
4229 static JSBool
4230 its_bindMethod(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
4231 jsval *rval)
4233 char *name;
4234 JSObject *method;
4236 if (!JS_ConvertArguments(cx, argc, argv, "so", &name, &method))
4237 return JS_FALSE;
4239 *rval = OBJECT_TO_JSVAL(method);
4241 if (JS_TypeOfValue(cx, *rval) != JSTYPE_FUNCTION) {
4242 JSString *valstr = JS_ValueToString(cx, *rval);
4243 if (valstr) {
4244 JS_ReportError(cx, "can't bind method %s to non-callable object %s",
4245 name, JS_GetStringBytes(valstr));
4247 return JS_FALSE;
4250 if (!JS_DefineProperty(cx, obj, name, *rval, NULL, NULL, JSPROP_ENUMERATE))
4251 return JS_FALSE;
4253 return JS_SetParent(cx, method, obj);
4256 static JSFunctionSpec its_methods[] = {
4257 {"bindMethod", its_bindMethod, 2,0,0},
4258 {NULL,NULL,0,0,0}
4261 #ifdef JSD_LOWLEVEL_SOURCE
4263 * This facilitates sending source to JSD (the debugger system) in the shell
4264 * where the source is loaded using the JSFILE hack in jsscan. The function
4265 * below is used as a callback for the jsdbgapi JS_SetSourceHandler hook.
4266 * A more normal embedding (e.g. mozilla) loads source itself and can send
4267 * source directly to JSD without using this hook scheme.
4269 static void
4270 SendSourceToJSDebugger(const char *filename, uintN lineno,
4271 jschar *str, size_t length,
4272 void **listenerTSData, JSDContext* jsdc)
4274 JSDSourceText *jsdsrc = (JSDSourceText *) *listenerTSData;
4276 if (!jsdsrc) {
4277 if (!filename)
4278 filename = "typein";
4279 if (1 == lineno) {
4280 jsdsrc = JSD_NewSourceText(jsdc, filename);
4281 } else {
4282 jsdsrc = JSD_FindSourceForURL(jsdc, filename);
4283 if (jsdsrc && JSD_SOURCE_PARTIAL !=
4284 JSD_GetSourceStatus(jsdc, jsdsrc)) {
4285 jsdsrc = NULL;
4289 if (jsdsrc) {
4290 jsdsrc = JSD_AppendUCSourceText(jsdc,jsdsrc, str, length,
4291 JSD_SOURCE_PARTIAL);
4293 *listenerTSData = jsdsrc;
4295 #endif /* JSD_LOWLEVEL_SOURCE */
4297 static JSBool its_noisy; /* whether to be noisy when finalizing it */
4298 static JSBool its_enum_fail;/* whether to fail when enumerating it */
4300 static JSBool
4301 its_addProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
4303 if (!its_noisy)
4304 return JS_TRUE;
4306 ToString idString(cx, id);
4307 fprintf(gOutFile, "adding its property %s,", idString.getBytes());
4308 ToString valueString(cx, *vp);
4309 fprintf(gOutFile, " initial value %s\n", valueString.getBytes());
4310 return JS_TRUE;
4313 static JSBool
4314 its_delProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
4316 if (!its_noisy)
4317 return JS_TRUE;
4319 ToString idString(cx, id);
4320 fprintf(gOutFile, "deleting its property %s,", idString.getBytes());
4321 ToString valueString(cx, *vp);
4322 fprintf(gOutFile, " initial value %s\n", valueString.getBytes());
4323 return JS_TRUE;
4326 static JSBool
4327 its_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
4329 if (!its_noisy)
4330 return JS_TRUE;
4332 ToString idString(cx, id);
4333 fprintf(gOutFile, "getting its property %s,", idString.getBytes());
4334 ToString valueString(cx, *vp);
4335 fprintf(gOutFile, " initial value %s\n", valueString.getBytes());
4336 return JS_TRUE;
4339 static JSBool
4340 its_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
4342 ToString idString(cx, id);
4343 if (its_noisy) {
4344 fprintf(gOutFile, "setting its property %s,", idString.getBytes());
4345 ToString valueString(cx, *vp);
4346 fprintf(gOutFile, " new value %s\n", valueString.getBytes());
4349 if (!JSVAL_IS_STRING(id))
4350 return JS_TRUE;
4352 if (!strcmp(idString.getBytes(), "noisy"))
4353 JS_ValueToBoolean(cx, *vp, &its_noisy);
4354 else if (!strcmp(idString.getBytes(), "enum_fail"))
4355 JS_ValueToBoolean(cx, *vp, &its_enum_fail);
4357 return JS_TRUE;
4361 * Its enumerator, implemented using the "new" enumerate API,
4362 * see class flags.
4364 static JSBool
4365 its_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
4366 jsval *statep, jsid *idp)
4368 JSObject *iterator;
4370 switch (enum_op) {
4371 case JSENUMERATE_INIT:
4372 if (its_noisy)
4373 fprintf(gOutFile, "enumerate its properties\n");
4375 iterator = JS_NewPropertyIterator(cx, obj);
4376 if (!iterator)
4377 return JS_FALSE;
4379 *statep = OBJECT_TO_JSVAL(iterator);
4380 if (idp)
4381 *idp = JSVAL_ZERO;
4382 break;
4384 case JSENUMERATE_NEXT:
4385 if (its_enum_fail) {
4386 JS_ReportError(cx, "its enumeration failed");
4387 return JS_FALSE;
4390 iterator = (JSObject *) JSVAL_TO_OBJECT(*statep);
4391 if (!JS_NextProperty(cx, iterator, idp))
4392 return JS_FALSE;
4394 if (!JSVAL_IS_VOID(*idp))
4395 break;
4396 /* Fall through. */
4398 case JSENUMERATE_DESTROY:
4399 /* Allow our iterator object to be GC'd. */
4400 *statep = JSVAL_NULL;
4401 break;
4404 return JS_TRUE;
4407 static JSBool
4408 its_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
4409 JSObject **objp)
4411 if (its_noisy) {
4412 ToString idString(cx, id);
4413 fprintf(gOutFile, "resolving its property %s, flags {%s,%s,%s}\n",
4414 idString.getBytes(),
4415 (flags & JSRESOLVE_QUALIFIED) ? "qualified" : "",
4416 (flags & JSRESOLVE_ASSIGNING) ? "assigning" : "",
4417 (flags & JSRESOLVE_DETECTING) ? "detecting" : "");
4419 return JS_TRUE;
4422 static JSBool
4423 its_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
4425 if (its_noisy)
4426 fprintf(gOutFile, "converting it to %s type\n", JS_GetTypeName(cx, type));
4427 return JS_TRUE;
4430 static void
4431 its_finalize(JSContext *cx, JSObject *obj)
4433 jsval *rootedVal;
4434 if (its_noisy)
4435 fprintf(gOutFile, "finalizing it\n");
4436 rootedVal = (jsval *) JS_GetPrivate(cx, obj);
4437 if (rootedVal) {
4438 JS_RemoveRoot(cx, rootedVal);
4439 JS_SetPrivate(cx, obj, NULL);
4440 delete rootedVal;
4444 static JSClass its_class = {
4445 "It", JSCLASS_NEW_RESOLVE | JSCLASS_NEW_ENUMERATE | JSCLASS_HAS_PRIVATE,
4446 its_addProperty, its_delProperty, its_getProperty, its_setProperty,
4447 (JSEnumerateOp)its_enumerate, (JSResolveOp)its_resolve,
4448 its_convert, its_finalize,
4449 JSCLASS_NO_OPTIONAL_MEMBERS
4452 JSErrorFormatString jsShell_ErrorFormatString[JSErr_Limit] = {
4453 #define MSG_DEF(name, number, count, exception, format) \
4454 { format, count, JSEXN_ERR } ,
4455 #include "jsshell.msg"
4456 #undef MSG_DEF
4459 static const JSErrorFormatString *
4460 my_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber)
4462 if ((errorNumber > 0) && (errorNumber < JSShellErr_Limit))
4463 return &jsShell_ErrorFormatString[errorNumber];
4464 return NULL;
4467 static void
4468 my_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
4470 int i, j, k, n;
4471 char *prefix, *tmp;
4472 const char *ctmp;
4474 if (!report) {
4475 fprintf(gErrFile, "%s\n", message);
4476 return;
4479 /* Conditionally ignore reported warnings. */
4480 if (JSREPORT_IS_WARNING(report->flags) && !reportWarnings)
4481 return;
4483 prefix = NULL;
4484 if (report->filename)
4485 prefix = JS_smprintf("%s:", report->filename);
4486 if (report->lineno) {
4487 tmp = prefix;
4488 prefix = JS_smprintf("%s%u: ", tmp ? tmp : "", report->lineno);
4489 JS_free(cx, tmp);
4491 if (JSREPORT_IS_WARNING(report->flags)) {
4492 tmp = prefix;
4493 prefix = JS_smprintf("%s%swarning: ",
4494 tmp ? tmp : "",
4495 JSREPORT_IS_STRICT(report->flags) ? "strict " : "");
4496 JS_free(cx, tmp);
4499 /* embedded newlines -- argh! */
4500 while ((ctmp = strchr(message, '\n')) != 0) {
4501 ctmp++;
4502 if (prefix)
4503 fputs(prefix, gErrFile);
4504 fwrite(message, 1, ctmp - message, gErrFile);
4505 message = ctmp;
4508 /* If there were no filename or lineno, the prefix might be empty */
4509 if (prefix)
4510 fputs(prefix, gErrFile);
4511 fputs(message, gErrFile);
4513 if (!report->linebuf) {
4514 fputc('\n', gErrFile);
4515 goto out;
4518 /* report->linebuf usually ends with a newline. */
4519 n = strlen(report->linebuf);
4520 fprintf(gErrFile, ":\n%s%s%s%s",
4521 prefix,
4522 report->linebuf,
4523 (n > 0 && report->linebuf[n-1] == '\n') ? "" : "\n",
4524 prefix);
4525 n = report->tokenptr - report->linebuf;
4526 for (i = j = 0; i < n; i++) {
4527 if (report->linebuf[i] == '\t') {
4528 for (k = (j + 8) & ~7; j < k; j++) {
4529 fputc('.', gErrFile);
4531 continue;
4533 fputc('.', gErrFile);
4534 j++;
4536 fputs("^\n", gErrFile);
4537 out:
4538 if (!JSREPORT_IS_WARNING(report->flags)) {
4539 if (report->errorNumber == JSMSG_OUT_OF_MEMORY) {
4540 gExitCode = EXITCODE_OUT_OF_MEMORY;
4541 } else {
4542 gExitCode = EXITCODE_RUNTIME_ERROR;
4545 JS_free(cx, prefix);
4548 #if defined(SHELL_HACK) && defined(DEBUG) && defined(XP_UNIX)
4549 static JSBool
4550 Exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
4552 JSFunction *fun;
4553 const char *name, **nargv;
4554 uintN i, nargc;
4555 JSString *str;
4556 pid_t pid;
4557 int status;
4559 fun = JS_ValueToFunction(cx, argv[-2]);
4560 if (!fun)
4561 return JS_FALSE;
4562 if (!fun->atom)
4563 return JS_TRUE;
4564 name = JS_GetStringBytes(ATOM_TO_STRING(fun->atom));
4565 nargc = 1 + argc;
4566 nargv = JS_malloc(cx, (nargc + 1) * sizeof(char *));
4567 if (!nargv)
4568 return JS_FALSE;
4569 nargv[0] = name;
4570 for (i = 1; i < nargc; i++) {
4571 str = JS_ValueToString(cx, argv[i-1]);
4572 if (!str) {
4573 JS_free(cx, nargv);
4574 return JS_FALSE;
4576 nargv[i] = JS_GetStringBytes(str);
4578 nargv[nargc] = 0;
4579 pid = fork();
4580 switch (pid) {
4581 case -1:
4582 perror("js");
4583 break;
4584 case 0:
4585 (void) execvp(name, (char **)nargv);
4586 perror("js");
4587 exit(127);
4588 default:
4589 while (waitpid(pid, &status, 0) < 0 && errno == EINTR)
4590 continue;
4591 break;
4593 JS_free(cx, nargv);
4594 return JS_TRUE;
4596 #endif
4598 static JSBool
4599 global_enumerate(JSContext *cx, JSObject *obj)
4601 #ifdef LAZY_STANDARD_CLASSES
4602 return JS_EnumerateStandardClasses(cx, obj);
4603 #else
4604 return JS_TRUE;
4605 #endif
4608 static JSBool
4609 global_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
4610 JSObject **objp)
4612 #ifdef LAZY_STANDARD_CLASSES
4613 if ((flags & JSRESOLVE_ASSIGNING) == 0) {
4614 JSBool resolved;
4616 if (!JS_ResolveStandardClass(cx, obj, id, &resolved))
4617 return JS_FALSE;
4618 if (resolved) {
4619 *objp = obj;
4620 return JS_TRUE;
4623 #endif
4625 #if defined(SHELL_HACK) && defined(DEBUG) && defined(XP_UNIX)
4626 if ((flags & (JSRESOLVE_QUALIFIED | JSRESOLVE_ASSIGNING)) == 0) {
4628 * Do this expensive hack only for unoptimized Unix builds, which are
4629 * not used for benchmarking.
4631 char *path, *comp, *full;
4632 const char *name;
4633 JSBool ok, found;
4634 JSFunction *fun;
4636 if (!JSVAL_IS_STRING(id))
4637 return JS_TRUE;
4638 path = getenv("PATH");
4639 if (!path)
4640 return JS_TRUE;
4641 path = JS_strdup(cx, path);
4642 if (!path)
4643 return JS_FALSE;
4644 name = JS_GetStringBytes(JSVAL_TO_STRING(id));
4645 ok = JS_TRUE;
4646 for (comp = strtok(path, ":"); comp; comp = strtok(NULL, ":")) {
4647 if (*comp != '\0') {
4648 full = JS_smprintf("%s/%s", comp, name);
4649 if (!full) {
4650 JS_ReportOutOfMemory(cx);
4651 ok = JS_FALSE;
4652 break;
4654 } else {
4655 full = (char *)name;
4657 found = (access(full, X_OK) == 0);
4658 if (*comp != '\0')
4659 free(full);
4660 if (found) {
4661 fun = JS_DefineFunction(cx, obj, name, Exec, 0,
4662 JSPROP_ENUMERATE);
4663 ok = (fun != NULL);
4664 if (ok)
4665 *objp = obj;
4666 break;
4669 JS_free(cx, path);
4670 return ok;
4672 #else
4673 return JS_TRUE;
4674 #endif
4677 JSClass global_class = {
4678 "global", JSCLASS_NEW_RESOLVE | JSCLASS_GLOBAL_FLAGS | JSCLASS_HAS_PRIVATE,
4679 JS_PropertyStub, JS_PropertyStub,
4680 JS_PropertyStub, JS_PropertyStub,
4681 global_enumerate, (JSResolveOp) global_resolve,
4682 JS_ConvertStub, its_finalize,
4683 JSCLASS_NO_OPTIONAL_MEMBERS
4686 static JSBool
4687 env_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
4689 /* XXX porting may be easy, but these don't seem to supply setenv by default */
4690 #if !defined XP_BEOS && !defined XP_OS2 && !defined SOLARIS
4691 int rv;
4693 ToString idstr(cx, id, JS_TRUE);
4694 if (idstr.threw())
4695 return JS_FALSE;
4696 ToString valstr(cx, *vp, JS_TRUE);
4697 if (valstr.threw())
4698 return JS_FALSE;
4699 #if defined XP_WIN || defined HPUX || defined OSF1 || defined IRIX
4701 char *waste = JS_smprintf("%s=%s", idstr.getBytes(), valstr.getBytes());
4702 if (!waste) {
4703 JS_ReportOutOfMemory(cx);
4704 return JS_FALSE;
4706 rv = putenv(waste);
4707 #ifdef XP_WIN
4709 * HPUX9 at least still has the bad old non-copying putenv.
4711 * Per mail from <s.shanmuganathan@digital.com>, OSF1 also has a putenv
4712 * that will crash if you pass it an auto char array (so it must place
4713 * its argument directly in the char *environ[] array).
4715 JS_smprintf_free(waste);
4716 #endif
4718 #else
4719 rv = setenv(idstr.getBytes(), valstr.getBytes(), 1);
4720 #endif
4721 if (rv < 0) {
4722 JS_ReportError(cx, "can't set env variable %s to %s", idstr.getBytes(), valstr.getBytes());
4723 return JS_FALSE;
4725 *vp = valstr.getJSVal();
4726 #endif /* !defined XP_BEOS && !defined XP_OS2 && !defined SOLARIS */
4727 return JS_TRUE;
4730 static JSBool
4731 env_enumerate(JSContext *cx, JSObject *obj)
4733 static JSBool reflected;
4734 char **evp, *name, *value;
4735 JSString *valstr;
4736 JSBool ok;
4738 if (reflected)
4739 return JS_TRUE;
4741 for (evp = (char **)JS_GetPrivate(cx, obj); (name = *evp) != NULL; evp++) {
4742 value = strchr(name, '=');
4743 if (!value)
4744 continue;
4745 *value++ = '\0';
4746 valstr = JS_NewStringCopyZ(cx, value);
4747 if (!valstr) {
4748 ok = JS_FALSE;
4749 } else {
4750 ok = JS_DefineProperty(cx, obj, name, STRING_TO_JSVAL(valstr),
4751 NULL, NULL, JSPROP_ENUMERATE);
4753 value[-1] = '=';
4754 if (!ok)
4755 return JS_FALSE;
4758 reflected = JS_TRUE;
4759 return JS_TRUE;
4762 static JSBool
4763 env_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
4764 JSObject **objp)
4766 JSString *valstr;
4767 const char *name, *value;
4769 if (flags & JSRESOLVE_ASSIGNING)
4770 return JS_TRUE;
4772 ToString idstr(cx, id, JS_TRUE);
4773 if (idstr.threw())
4774 return JS_FALSE;
4776 name = idstr.getBytes();
4777 value = getenv(name);
4778 if (value) {
4779 valstr = JS_NewStringCopyZ(cx, value);
4780 if (!valstr)
4781 return JS_FALSE;
4782 if (!JS_DefineProperty(cx, obj, name, STRING_TO_JSVAL(valstr),
4783 NULL, NULL, JSPROP_ENUMERATE)) {
4784 return JS_FALSE;
4786 *objp = obj;
4788 return JS_TRUE;
4791 static JSClass env_class = {
4792 "environment", JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE,
4793 JS_PropertyStub, JS_PropertyStub,
4794 JS_PropertyStub, env_setProperty,
4795 env_enumerate, (JSResolveOp) env_resolve,
4796 JS_ConvertStub, NULL,
4797 JSCLASS_NO_OPTIONAL_MEMBERS
4800 #ifdef NARCISSUS
4802 static JSBool
4803 defineProperty(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
4804 jsval *rval)
4806 JSString *str;
4807 jsval value;
4808 JSBool dontDelete, readOnly, dontEnum;
4809 const jschar *chars;
4810 size_t length;
4811 uintN attrs;
4813 dontDelete = readOnly = dontEnum = JS_FALSE;
4814 if (!JS_ConvertArguments(cx, argc, argv, "Sv/bbb",
4815 &str, &value, &dontDelete, &readOnly, &dontEnum)) {
4816 return JS_FALSE;
4818 chars = JS_GetStringChars(str);
4819 length = JS_GetStringLength(str);
4820 attrs = dontEnum ? 0 : JSPROP_ENUMERATE;
4821 if (dontDelete)
4822 attrs |= JSPROP_PERMANENT;
4823 if (readOnly)
4824 attrs |= JSPROP_READONLY;
4825 return JS_DefineUCProperty(cx, obj, chars, length, value, NULL, NULL,
4826 attrs);
4829 static JSBool
4830 Evaluate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
4832 /* function evaluate(source, filename, lineno) { ... } */
4833 JSString *source;
4834 const char *filename = "";
4835 jsuint lineno = 0;
4836 uint32 oldopts;
4837 JSBool ok;
4839 if (argc == 0) {
4840 *rval = JSVAL_VOID;
4841 return JS_TRUE;
4844 if (!JS_ConvertArguments(cx, argc, argv, "S/su",
4845 &source, &filename, &lineno)) {
4846 return JS_FALSE;
4849 oldopts = JS_GetOptions(cx);
4850 JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO);
4851 ok = JS_EvaluateUCScript(cx, obj, JS_GetStringChars(source),
4852 JS_GetStringLength(source), filename,
4853 lineno, rval);
4854 JS_SetOptions(cx, oldopts);
4856 return ok;
4859 #endif /* NARCISSUS */
4862 * Avoid a reentrancy hazard.
4864 * The non-JS_THREADSAFE shell uses a signal handler to implement timeout().
4865 * The JS engine is not really reentrant, but JS_TriggerAllOperationCallbacks
4866 * is mostly safe--the only danger is that we might interrupt JS_NewContext or
4867 * JS_DestroyContext while the context list is being modified. Therefore we
4868 * disable the signal handler around calls to those functions.
4870 #ifdef JS_THREADSAFE
4871 # define WITH_SIGNALS_DISABLED(x) x
4872 #else
4873 # define WITH_SIGNALS_DISABLED(x) \
4874 JS_BEGIN_MACRO \
4875 ScheduleWatchdog(gRuntime, -1); \
4876 x; \
4877 ScheduleWatchdog(gRuntime, gTimeoutInterval); \
4878 JS_END_MACRO
4879 #endif
4881 static JSContext *
4882 NewContext(JSRuntime *rt)
4884 JSContext *cx;
4885 WITH_SIGNALS_DISABLED(cx = JS_NewContext(rt, gStackChunkSize));
4886 if (!cx)
4887 return NULL;
4889 JSShellContextData *data = NewContextData();
4890 if (!data) {
4891 DestroyContext(cx, false);
4892 return NULL;
4895 JS_SetContextPrivate(cx, data);
4896 JS_SetErrorReporter(cx, my_ErrorReporter);
4897 JS_SetVersion(cx, JSVERSION_LATEST);
4898 SetContextOptions(cx);
4899 if (enableJit)
4900 JS_ToggleOptions(cx, JSOPTION_JIT);
4901 return cx;
4904 static void
4905 DestroyContext(JSContext *cx, bool withGC)
4907 JSShellContextData *data = GetContextData(cx);
4908 JS_SetContextPrivate(cx, NULL);
4909 free(data);
4910 WITH_SIGNALS_DISABLED(withGC ? JS_DestroyContext(cx) : JS_DestroyContextNoGC(cx));
4913 static JSObject *
4914 NewGlobalObject(JSContext *cx)
4916 JSObject *glob = JS_NewObject(cx, &global_class, NULL, NULL);
4917 #ifdef LAZY_STANDARD_CLASSES
4918 JS_SetGlobalObject(cx, glob);
4919 #else
4920 if (!JS_InitStandardClasses(cx, glob))
4921 return NULL;
4922 #endif
4923 #ifdef JS_HAS_CTYPES
4924 if (!JS_InitCTypesClass(cx, glob))
4925 return NULL;
4926 #endif
4927 if (!JS_DefineFunctions(cx, glob, shell_functions))
4928 return NULL;
4930 JSObject *it = JS_DefineObject(cx, glob, "it", &its_class, NULL, 0);
4931 if (!it)
4932 return NULL;
4933 if (!JS_DefineProperties(cx, it, its_props))
4934 return NULL;
4935 if (!JS_DefineFunctions(cx, it, its_methods))
4936 return NULL;
4938 if (!JS_DefineProperty(cx, glob, "custom", JSVAL_VOID, its_getter,
4939 its_setter, 0))
4940 return NULL;
4941 if (!JS_DefineProperty(cx, glob, "customRdOnly", JSVAL_VOID, its_getter,
4942 its_setter, JSPROP_READONLY))
4943 return NULL;
4945 #ifdef NARCISSUS
4947 jsval v;
4948 static const char Object_prototype[] = "Object.prototype";
4950 if (!JS_DefineFunction(cx, glob, "evaluate", Evaluate, 3, 0))
4951 return NULL;
4953 if (!JS_EvaluateScript(cx, glob,
4954 Object_prototype, sizeof Object_prototype - 1,
4955 NULL, 0, &v)) {
4956 return NULL;
4958 if (!JS_DefineFunction(cx, JSVAL_TO_OBJECT(v), "__defineProperty__",
4959 defineProperty, 5, 0)) {
4960 return NULL;
4963 #endif
4965 return glob;
4969 main(int argc, char **argv, char **envp)
4971 int stackDummy;
4972 JSRuntime *rt;
4973 JSContext *cx;
4974 int result;
4975 #ifdef JSDEBUGGER
4976 JSDContext *jsdc;
4977 #ifdef JSDEBUGGER_JAVA_UI
4978 JNIEnv *java_env;
4979 JSDJContext *jsdjc;
4980 #endif
4981 #ifdef JSDEBUGGER_C_UI
4982 JSBool jsdbc;
4983 #endif /* JSDEBUGGER_C_UI */
4984 #endif /* JSDEBUGGER */
4986 CheckHelpMessages();
4987 #ifdef HAVE_SETLOCALE
4988 setlocale(LC_ALL, "");
4989 #endif
4991 #ifdef JS_THREADSAFE
4992 if (PR_FAILURE == PR_NewThreadPrivateIndex(&gStackBaseThreadIndex, NULL) ||
4993 PR_FAILURE == PR_SetThreadPrivate(gStackBaseThreadIndex, &stackDummy)) {
4994 return 1;
4996 #else
4997 gStackBase = (jsuword) &stackDummy;
4998 #endif
5000 #ifdef XP_OS2
5001 /* these streams are normally line buffered on OS/2 and need a \n, *
5002 * so we need to unbuffer then to get a reasonable prompt */
5003 setbuf(stdout,0);
5004 setbuf(stderr,0);
5005 #endif
5007 gErrFile = stderr;
5008 gOutFile = stdout;
5010 argc--;
5011 argv++;
5013 #ifdef XP_WIN
5014 // Set the timer calibration delay count to 0 so we get high
5015 // resolution right away, which we need for precise benchmarking.
5016 extern int CALIBRATION_DELAY_COUNT;
5017 CALIBRATION_DELAY_COUNT = 0;
5018 #endif
5020 rt = JS_NewRuntime(64L * 1024L * 1024L);
5021 if (!rt)
5022 return 1;
5024 if (!InitWatchdog(rt))
5025 return 1;
5027 cx = NewContext(rt);
5028 if (!cx)
5029 return 1;
5031 JS_SetGCParameterForThread(cx, JSGC_MAX_CODE_CACHE_BYTES, 16 * 1024 * 1024);
5033 JS_BeginRequest(cx);
5035 JSObject *glob = NewGlobalObject(cx);
5036 if (!glob)
5037 return 1;
5039 JSObject *envobj = JS_DefineObject(cx, glob, "environment", &env_class, NULL, 0);
5040 if (!envobj || !JS_SetPrivate(cx, envobj, envp))
5041 return 1;
5043 #ifdef JSDEBUGGER
5045 * XXX A command line option to enable debugging (or not) would be good
5047 jsdc = JSD_DebuggerOnForUser(rt, NULL, NULL);
5048 if (!jsdc)
5049 return 1;
5050 JSD_JSContextInUse(jsdc, cx);
5051 #ifdef JSD_LOWLEVEL_SOURCE
5052 JS_SetSourceHandler(rt, SendSourceToJSDebugger, jsdc);
5053 #endif /* JSD_LOWLEVEL_SOURCE */
5054 #ifdef JSDEBUGGER_JAVA_UI
5055 jsdjc = JSDJ_CreateContext();
5056 if (! jsdjc)
5057 return 1;
5058 JSDJ_SetJSDContext(jsdjc, jsdc);
5059 java_env = JSDJ_CreateJavaVMAndStartDebugger(jsdjc);
5061 * XXX This would be the place to wait for the debugger to start.
5062 * Waiting would be nice in general, but especially when a js file
5063 * is passed on the cmd line.
5065 #endif /* JSDEBUGGER_JAVA_UI */
5066 #ifdef JSDEBUGGER_C_UI
5067 jsdbc = JSDB_InitDebugger(rt, jsdc, 0);
5068 #endif /* JSDEBUGGER_C_UI */
5069 #endif /* JSDEBUGGER */
5071 #ifdef JS_THREADSAFE
5072 class ShellWorkerHooks : public js::workers::WorkerHooks {
5073 public:
5074 JSObject *newGlobalObject(JSContext *cx) { return NewGlobalObject(cx); }
5076 ShellWorkerHooks hooks;
5077 if (!JS_AddNamedRoot(cx, &gWorkers, "Workers") ||
5078 !js::workers::init(cx, &hooks, glob, &gWorkers)) {
5079 return 1;
5081 #endif
5083 result = ProcessArgs(cx, glob, argv, argc);
5085 #ifdef JS_THREADSAFE
5086 js::workers::finish(cx, gWorkers);
5087 JS_RemoveRoot(cx, &gWorkers);
5088 if (result == 0)
5089 result = gExitCode;
5090 #endif
5092 #ifdef JSDEBUGGER
5093 if (jsdc) {
5094 #ifdef JSDEBUGGER_C_UI
5095 if (jsdbc)
5096 JSDB_TermDebugger(jsdc);
5097 #endif /* JSDEBUGGER_C_UI */
5098 JSD_DebuggerOff(jsdc);
5100 #endif /* JSDEBUGGER */
5102 JS_EndRequest(cx);
5104 JS_CommenceRuntimeShutDown(rt);
5106 DestroyContext(cx, true);
5108 KillWatchdog();
5110 JS_DestroyRuntime(rt);
5111 JS_ShutDown();
5112 return result;