Bug 578272: Remove Algol-like display optimization. (r=brendan)
[mozilla-central.git] / js / src / shell / js.cpp
blobd634f94b520ac335c6eff9d5d26dec7afcb135a1
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sw=4 et tw=99:
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
15 * License.
17 * The Original Code is Mozilla Communicator client code, released
18 * March 31, 1998.
20 * The Initial Developer of the Original Code is
21 * Netscape Communications Corporation.
22 * Portions created by the Initial Developer are Copyright (C) 1998
23 * the Initial Developer. All Rights Reserved.
25 * Contributor(s):
27 * Alternatively, the contents of this file may be used under the terms of
28 * either of the GNU General Public License Version 2 or later (the "GPL"),
29 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
41 #define __STDC_LIMIT_MACROS
44 * JS shell.
46 #include <errno.h>
47 #include <math.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <signal.h>
52 #include <locale.h>
53 #include "jstypes.h"
54 #include "jsstdint.h"
55 #include "jsarena.h"
56 #include "jsutil.h"
57 #include "jsprf.h"
58 #include "jswrapper.h"
59 #include "jsapi.h"
60 #include "jsarray.h"
61 #include "jsatom.h"
62 #include "jsbuiltins.h"
63 #include "jscntxt.h"
64 #include "jsdate.h"
65 #include "jsdbgapi.h"
66 #include "jsemit.h"
67 #include "jsfun.h"
68 #include "jsgc.h"
69 #include "jsiter.h"
70 #include "jslock.h"
71 #include "jsnum.h"
72 #include "jsobj.h"
73 #include "jsparse.h"
74 #include "jsscope.h"
75 #include "jsscript.h"
76 #include "jstracer.h"
77 #include "jsxml.h"
79 #include "prmjtime.h"
81 #ifdef JSDEBUGGER
82 #include "jsdebug.h"
83 #ifdef JSDEBUGGER_JAVA_UI
84 #include "jsdjava.h"
85 #endif /* JSDEBUGGER_JAVA_UI */
86 #ifdef JSDEBUGGER_C_UI
87 #include "jsdb.h"
88 #endif /* JSDEBUGGER_C_UI */
89 #endif /* JSDEBUGGER */
91 #include "jsworkers.h"
93 #include "jsobjinlines.h"
94 #include "jsscriptinlines.h"
96 #ifdef XP_UNIX
97 #include <unistd.h>
98 #include <sys/types.h>
99 #include <sys/wait.h>
100 #endif
102 #if defined(XP_WIN) || defined(XP_OS2)
103 #include <io.h> /* for isatty() */
104 #endif
106 #ifdef XP_WIN
107 #include <windows.h>
108 #endif
110 using namespace js;
112 typedef enum JSShellExitCode {
113 EXITCODE_RUNTIME_ERROR = 3,
114 EXITCODE_FILE_NOT_FOUND = 4,
115 EXITCODE_OUT_OF_MEMORY = 5,
116 EXITCODE_TIMEOUT = 6
117 } JSShellExitCode;
119 size_t gStackChunkSize = 8192;
121 /* Assume that we can not use more than 5e5 bytes of C stack by default. */
122 #if defined(DEBUG) && defined(__SUNPRO_CC)
123 /* Sun compiler uses larger stack space for js_Interpret() with debug
124 Use a bigger gMaxStackSize to make "make check" happy. */
125 size_t gMaxStackSize = 5000000;
126 #else
127 size_t gMaxStackSize = 500000;
128 #endif
131 #ifdef JS_THREADSAFE
132 static PRUintn gStackBaseThreadIndex;
133 #else
134 static jsuword gStackBase;
135 #endif
137 static size_t gScriptStackQuota = JS_DEFAULT_SCRIPT_STACK_QUOTA;
140 * Limit the timeout to 30 minutes to prevent an overflow on platfoms
141 * that represent the time internally in microseconds using 32-bit int.
143 static jsdouble MAX_TIMEOUT_INTERVAL = 1800.0;
144 static jsdouble gTimeoutInterval = -1.0;
145 static volatile bool gCanceled = false;
147 static bool enableJit = false;
149 static JSBool
150 SetTimeoutValue(JSContext *cx, jsdouble t);
152 static bool
153 InitWatchdog(JSRuntime *rt);
155 static void
156 KillWatchdog();
158 static bool
159 ScheduleWatchdog(JSRuntime *rt, jsdouble t);
161 static void
162 CancelExecution(JSRuntime *rt);
165 * Watchdog thread state.
167 #ifdef JS_THREADSAFE
169 static PRLock *gWatchdogLock = NULL;
170 static PRCondVar *gWatchdogWakeup = NULL;
171 static PRThread *gWatchdogThread = NULL;
172 static bool gWatchdogHasTimeout = false;
173 static PRIntervalTime gWatchdogTimeout = 0;
175 static PRCondVar *gSleepWakeup = NULL;
177 #else
179 static JSRuntime *gRuntime = NULL;
181 #endif
183 int gExitCode = 0;
184 JSBool gQuitting = JS_FALSE;
185 FILE *gErrFile = NULL;
186 FILE *gOutFile = NULL;
187 #ifdef JS_THREADSAFE
188 JSObject *gWorkers = NULL;
189 #endif
191 static JSBool reportWarnings = JS_TRUE;
192 static JSBool compileOnly = JS_FALSE;
194 typedef enum JSShellErrNum {
195 #define MSG_DEF(name, number, count, exception, format) \
196 name = number,
197 #include "jsshell.msg"
198 #undef MSG_DEF
199 JSShellErr_Limit
200 #undef MSGDEF
201 } JSShellErrNum;
203 static JSContext *
204 NewContext(JSRuntime *rt);
206 static void
207 DestroyContext(JSContext *cx, bool withGC);
209 static const JSErrorFormatString *
210 my_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber);
212 static JSObject *
213 split_setup(JSContext *cx, JSBool evalcx);
215 #ifdef EDITLINE
216 JS_BEGIN_EXTERN_C
217 JS_EXTERN_API(char) *readline(const char *prompt);
218 JS_EXTERN_API(void) add_history(char *line);
219 JS_END_EXTERN_C
220 #endif
222 static void
223 ReportException(JSContext *cx)
225 if (JS_IsExceptionPending(cx)) {
226 if (!JS_ReportPendingException(cx))
227 JS_ClearPendingException(cx);
231 class ToString {
232 public:
233 ToString(JSContext *aCx, jsval v, JSBool aThrow = JS_FALSE)
234 : cx(aCx)
235 , mThrow(aThrow)
237 mStr = JS_ValueToString(cx, v);
238 if (!aThrow && !mStr)
239 ReportException(cx);
240 JS_AddNamedStringRoot(cx, &mStr, "Value ToString helper");
242 ~ToString() {
243 JS_RemoveStringRoot(cx, &mStr);
245 JSBool threw() { return !mStr; }
246 jsval getJSVal() { return STRING_TO_JSVAL(mStr); }
247 const char *getBytes() {
248 return mStr ? JS_GetStringBytes(mStr) : "(error converting value)";
250 private:
251 JSContext *cx;
252 JSString *mStr;
253 JSBool mThrow;
256 class IdToString : public ToString {
257 public:
258 IdToString(JSContext *cx, jsid id, JSBool aThrow = JS_FALSE)
259 : ToString(cx, IdToJsval(id), aThrow)
263 static char *
264 GetLine(FILE *file, const char * prompt)
266 size_t size;
267 char *buffer;
268 #ifdef EDITLINE
270 * Use readline only if file is stdin, because there's no way to specify
271 * another handle. Are other filehandles interactive?
273 if (file == stdin) {
274 char *linep = readline(prompt);
276 * We set it to zero to avoid complaining about inappropriate ioctl
277 * for device in the case of EOF. Looks like errno == 251 if line is
278 * finished with EOF and errno == 25 (EINVAL on Mac) if there is
279 * nothing left to read.
281 if (errno == 251 || errno == 25 || errno == EINVAL)
282 errno = 0;
283 if (!linep)
284 return NULL;
285 if (linep[0] != '\0')
286 add_history(linep);
287 return linep;
289 #endif
290 size_t len = 0;
291 if (*prompt != '\0') {
292 fprintf(gOutFile, "%s", prompt);
293 fflush(gOutFile);
295 size = 80;
296 buffer = (char *) malloc(size);
297 if (!buffer)
298 return NULL;
299 char *current = buffer;
300 while (fgets(current, size - len, file)) {
301 len += strlen(current);
302 char *t = buffer + len - 1;
303 if (*t == '\n') {
304 /* Line was read. We remove '\n' and exit. */
305 *t = '\0';
306 return buffer;
308 if (len + 1 == size) {
309 size = size * 2;
310 char *tmp = (char *) realloc(buffer, size);
311 if (!tmp) {
312 free(buffer);
313 return NULL;
315 buffer = tmp;
317 current = buffer + len;
319 if (len && !ferror(file))
320 return buffer;
321 free(buffer);
322 return NULL;
326 * State to store as JSContext private.
328 * We declare such timestamp as volatile as they are updated in the operation
329 * callback without taking any locks. Any possible race can only lead to more
330 * frequent callback calls. This is safe as the callback does everything based
331 * on timing.
333 struct JSShellContextData {
334 volatile JSIntervalTime startTime;
337 static JSShellContextData *
338 NewContextData()
340 /* Prevent creation of new contexts after we have been canceled. */
341 if (gCanceled)
342 return NULL;
344 JSShellContextData *data = (JSShellContextData *)
345 calloc(sizeof(JSShellContextData), 1);
346 if (!data)
347 return NULL;
348 data->startTime = js_IntervalNow();
349 return data;
352 static inline JSShellContextData *
353 GetContextData(JSContext *cx)
355 JSShellContextData *data = (JSShellContextData *) JS_GetContextPrivate(cx);
357 JS_ASSERT(data);
358 return data;
361 static JSBool
362 ShellOperationCallback(JSContext *cx)
364 if (!gCanceled)
365 return JS_TRUE;
367 JS_ClearPendingException(cx);
368 return JS_FALSE;
371 static void
372 SetContextOptions(JSContext *cx)
374 JS_SetNativeStackQuota(cx, gMaxStackSize);
375 JS_SetScriptStackQuota(cx, gScriptStackQuota);
376 JS_SetOperationCallback(cx, ShellOperationCallback);
379 #ifdef WINCE
380 int errno;
381 #endif
383 static void
384 Process(JSContext *cx, JSObject *obj, char *filename, JSBool forceTTY)
386 JSBool ok, hitEOF;
387 JSScript *script;
388 jsval result;
389 JSString *str;
390 char *buffer;
391 size_t size;
392 int lineno;
393 int startline;
394 FILE *file;
395 uint32 oldopts;
397 if (forceTTY || !filename || strcmp(filename, "-") == 0) {
398 file = stdin;
399 } else {
400 file = fopen(filename, "r");
401 if (!file) {
402 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
403 JSSMSG_CANT_OPEN, filename, strerror(errno));
404 gExitCode = EXITCODE_FILE_NOT_FOUND;
405 return;
409 SetContextOptions(cx);
411 #ifndef WINCE
412 /* windows mobile (and possibly other os's) does not have a TTY */
413 if (!forceTTY && !isatty(fileno(file)))
414 #endif
417 * It's not interactive - just execute it.
419 * Support the UNIX #! shell hack; gobble the first line if it starts
420 * with '#'. TODO - this isn't quite compatible with sharp variables,
421 * as a legal js program (using sharp variables) might start with '#'.
422 * But that would require multi-character lookahead.
424 int ch = fgetc(file);
425 if (ch == '#') {
426 while((ch = fgetc(file)) != EOF) {
427 if (ch == '\n' || ch == '\r')
428 break;
431 ungetc(ch, file);
433 oldopts = JS_GetOptions(cx);
434 JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO | JSOPTION_NO_SCRIPT_RVAL);
435 script = JS_CompileFileHandle(cx, obj, filename, file);
436 JS_SetOptions(cx, oldopts);
437 if (script) {
438 if (!compileOnly)
439 (void)JS_ExecuteScript(cx, obj, script, NULL);
440 JS_DestroyScript(cx, script);
443 if (file != stdin)
444 fclose(file);
445 return;
448 /* It's an interactive filehandle; drop into read-eval-print loop. */
449 lineno = 1;
450 hitEOF = JS_FALSE;
451 buffer = NULL;
452 size = 0; /* assign here to avoid warnings */
453 do {
455 * Accumulate lines until we get a 'compilable unit' - one that either
456 * generates an error (before running out of source) or that compiles
457 * cleanly. This should be whenever we get a complete statement that
458 * coincides with the end of a line.
460 startline = lineno;
461 size_t len = 0; /* initialize to avoid warnings */
462 do {
463 ScheduleWatchdog(cx->runtime, -1);
464 gCanceled = false;
465 errno = 0;
467 char *line;
469 JSAutoSuspendRequest suspended(cx);
470 line = GetLine(file, startline == lineno ? "js> " : "");
472 if (!line) {
473 if (errno) {
474 JS_ReportError(cx, strerror(errno));
475 free(buffer);
476 return;
478 hitEOF = JS_TRUE;
479 break;
481 if (!buffer) {
482 buffer = line;
483 len = strlen(buffer);
484 size = len + 1;
485 } else {
487 * len + 1 is required to store '\n' in the end of line.
489 size_t newlen = strlen(line) + (len ? len + 1 : 0);
490 if (newlen + 1 > size) {
491 size = newlen + 1 > size * 2 ? newlen + 1 : size * 2;
492 char *newBuf = (char *) realloc(buffer, size);
493 if (!newBuf) {
494 free(buffer);
495 free(line);
496 JS_ReportOutOfMemory(cx);
497 return;
499 buffer = newBuf;
501 char *current = buffer + len;
502 if (startline != lineno)
503 *current++ = '\n';
504 strcpy(current, line);
505 len = newlen;
506 free(line);
508 lineno++;
509 if (!ScheduleWatchdog(cx->runtime, gTimeoutInterval)) {
510 hitEOF = JS_TRUE;
511 break;
513 } while (!JS_BufferIsCompilableUnit(cx, obj, buffer, len));
515 if (hitEOF && !buffer)
516 break;
518 /* Clear any pending exception from previous failed compiles. */
519 JS_ClearPendingException(cx);
521 /* Even though we're interactive, we have a compile-n-go opportunity. */
522 oldopts = JS_GetOptions(cx);
523 if (!compileOnly)
524 JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO);
525 script = JS_CompileScript(cx, obj, buffer, len, "typein",
526 startline);
527 if (!compileOnly)
528 JS_SetOptions(cx, oldopts);
530 if (script) {
531 if (!compileOnly) {
532 ok = JS_ExecuteScript(cx, obj, script, &result);
533 if (ok && !JSVAL_IS_VOID(result)) {
534 str = JS_ValueToSource(cx, result);
535 if (str)
536 fprintf(gOutFile, "%s\n", JS_GetStringBytes(str));
537 else
538 ok = JS_FALSE;
541 JS_DestroyScript(cx, script);
543 *buffer = '\0';
544 } while (!hitEOF && !gQuitting);
546 free(buffer);
547 fprintf(gOutFile, "\n");
548 if (file != stdin)
549 fclose(file);
550 return;
553 static int
554 usage(void)
556 fprintf(gErrFile, "%s\n", JS_GetImplementationVersion());
557 fprintf(gErrFile, "usage: js [-zKPswWxCij] [-t timeoutSeconds] [-c stackchunksize] [-o option] [-v version] [-f scriptfile] [-e script] [-S maxstacksize] "
558 #ifdef JS_GC_ZEAL
559 "[-Z gczeal] "
560 #endif
561 #ifdef MOZ_TRACEVIS
562 "[-T TraceVisFileName] "
563 #endif
564 "[scriptfile] [scriptarg...]\n");
565 return 2;
569 * JSContext option name to flag map. The option names are in alphabetical
570 * order for better reporting.
572 static const struct {
573 const char *name;
574 uint32 flag;
575 } js_options[] = {
576 {"anonfunfix", JSOPTION_ANONFUNFIX},
577 {"atline", JSOPTION_ATLINE},
578 {"jit", JSOPTION_JIT},
579 {"relimit", JSOPTION_RELIMIT},
580 {"strict", JSOPTION_STRICT},
581 {"werror", JSOPTION_WERROR},
582 {"xml", JSOPTION_XML},
585 static uint32
586 MapContextOptionNameToFlag(JSContext* cx, const char* name)
588 for (size_t i = 0; i != JS_ARRAY_LENGTH(js_options); ++i) {
589 if (strcmp(name, js_options[i].name) == 0)
590 return js_options[i].flag;
593 char* msg = JS_sprintf_append(NULL,
594 "unknown option name '%s'."
595 " The valid names are ", name);
596 for (size_t i = 0; i != JS_ARRAY_LENGTH(js_options); ++i) {
597 if (!msg)
598 break;
599 msg = JS_sprintf_append(msg, "%s%s", js_options[i].name,
600 (i + 2 < JS_ARRAY_LENGTH(js_options)
601 ? ", "
602 : i + 2 == JS_ARRAY_LENGTH(js_options)
603 ? " and "
604 : "."));
606 if (!msg) {
607 JS_ReportOutOfMemory(cx);
608 } else {
609 JS_ReportError(cx, msg);
610 free(msg);
612 return 0;
615 extern JSClass global_class;
617 #if defined(JS_TRACER) && defined(DEBUG)
618 namespace js {
619 extern struct JSClass jitstats_class;
620 void InitJITStatsClass(JSContext *cx, JSObject *glob);
622 #endif
624 static int
625 ProcessArgs(JSContext *cx, JSObject *obj, char **argv, int argc)
627 int i, j, length;
628 JSObject *argsObj;
629 char *filename = NULL;
630 JSBool isInteractive = JS_TRUE;
631 JSBool forceTTY = JS_FALSE;
634 * Scan past all optional arguments so we can create the arguments object
635 * before processing any -f options, which must interleave properly with
636 * -v and -w options. This requires two passes, and without getopt, we'll
637 * have to keep the option logic here and in the second for loop in sync.
639 for (i = 0; i < argc; i++) {
640 if (argv[i][0] != '-' || argv[i][1] == '\0') {
641 ++i;
642 break;
644 switch (argv[i][1]) {
645 case 'c':
646 case 'f':
647 case 'e':
648 case 'v':
649 case 'S':
650 case 't':
651 #ifdef JS_GC_ZEAL
652 case 'Z':
653 #endif
654 #ifdef MOZ_TRACEVIS
655 case 'T':
656 #endif
657 ++i;
658 break;
659 default:;
664 * Create arguments early and define it to root it, so it's safe from any
665 * GC calls nested below, and so it is available to -f <file> arguments.
667 argsObj = JS_NewArrayObject(cx, 0, NULL);
668 if (!argsObj)
669 return 1;
670 if (!JS_DefineProperty(cx, obj, "arguments", OBJECT_TO_JSVAL(argsObj),
671 NULL, NULL, 0)) {
672 return 1;
675 length = argc - i;
676 for (j = 0; j < length; j++) {
677 JSString *str = JS_NewStringCopyZ(cx, argv[i++]);
678 if (!str)
679 return 1;
680 if (!JS_DefineElement(cx, argsObj, j, STRING_TO_JSVAL(str),
681 NULL, NULL, JSPROP_ENUMERATE)) {
682 return 1;
686 for (i = 0; i < argc; i++) {
687 if (argv[i][0] != '-' || argv[i][1] == '\0') {
688 filename = argv[i++];
689 isInteractive = JS_FALSE;
690 break;
693 switch (argv[i][1]) {
694 case 'v':
695 if (++i == argc)
696 return usage();
698 JS_SetVersion(cx, (JSVersion) atoi(argv[i]));
699 break;
701 #ifdef JS_GC_ZEAL
702 case 'Z':
703 if (++i == argc)
704 return usage();
705 JS_SetGCZeal(cx, !!(atoi(argv[i])));
706 break;
707 #endif
709 case 'w':
710 reportWarnings = JS_TRUE;
711 break;
713 case 'W':
714 reportWarnings = JS_FALSE;
715 break;
717 case 's':
718 JS_ToggleOptions(cx, JSOPTION_STRICT);
719 break;
721 case 'E':
722 JS_ToggleOptions(cx, JSOPTION_RELIMIT);
723 break;
725 case 'x':
726 JS_ToggleOptions(cx, JSOPTION_XML);
727 break;
729 case 'j':
730 enableJit = !enableJit;
731 JS_ToggleOptions(cx, JSOPTION_JIT);
732 #if defined(JS_TRACER) && defined(DEBUG)
733 js::InitJITStatsClass(cx, JS_GetGlobalObject(cx));
734 JS_DefineObject(cx, JS_GetGlobalObject(cx), "tracemonkey",
735 &js::jitstats_class, NULL, 0);
736 #endif
737 break;
739 case 'o':
741 if (++i == argc)
742 return usage();
744 uint32 flag = MapContextOptionNameToFlag(cx, argv[i]);
745 if (flag == 0)
746 return gExitCode;
747 JS_ToggleOptions(cx, flag);
748 break;
750 case 'P':
751 if (JS_GET_CLASS(cx, JS_GetPrototype(cx, obj)) != &global_class) {
752 JSObject *gobj;
754 if (!JS_SealObject(cx, obj, JS_TRUE))
755 return JS_FALSE;
756 gobj = JS_NewGlobalObject(cx, &global_class);
757 if (!gobj)
758 return JS_FALSE;
759 if (!JS_SetPrototype(cx, gobj, obj))
760 return JS_FALSE;
761 JS_SetParent(cx, gobj, NULL);
762 JS_SetGlobalObject(cx, gobj);
763 obj = gobj;
765 break;
767 case 't':
768 if (++i == argc)
769 return usage();
771 if (!SetTimeoutValue(cx, atof(argv[i])))
772 return JS_FALSE;
774 break;
776 case 'c':
777 /* set stack chunk size */
778 gStackChunkSize = atoi(argv[++i]);
779 break;
781 case 'f':
782 if (++i == argc)
783 return usage();
785 Process(cx, obj, argv[i], JS_FALSE);
786 if (gExitCode != 0)
787 return gExitCode;
790 * XXX: js -f foo.js should interpret foo.js and then
791 * drop into interactive mode, but that breaks the test
792 * harness. Just execute foo.js for now.
794 isInteractive = JS_FALSE;
795 break;
797 case 'e':
799 jsval rval;
801 if (++i == argc)
802 return usage();
804 /* Pass a filename of -e to imitate PERL */
805 JS_EvaluateScript(cx, obj, argv[i], strlen(argv[i]),
806 "-e", 1, &rval);
808 isInteractive = JS_FALSE;
809 break;
812 case 'C':
813 compileOnly = JS_TRUE;
814 isInteractive = JS_FALSE;
815 break;
817 case 'i':
818 isInteractive = forceTTY = JS_TRUE;
819 break;
821 case 'S':
822 if (++i == argc)
823 return usage();
825 /* Set maximum stack size. */
826 gMaxStackSize = atoi(argv[i]);
827 break;
829 case 'z':
830 obj = split_setup(cx, JS_FALSE);
831 if (!obj)
832 return gExitCode;
833 break;
834 #ifdef MOZ_SHARK
835 case 'k':
836 JS_ConnectShark();
837 break;
838 #endif
839 #ifdef MOZ_TRACEVIS
840 case 'T':
841 if (++i == argc)
842 return usage();
844 StartTraceVis(argv[i]);
845 break;
846 #endif
847 default:
848 return usage();
852 if (filename || isInteractive)
853 Process(cx, obj, filename, forceTTY);
854 return gExitCode;
857 static JSBool
858 Version(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
860 if (argc > 0 && JSVAL_IS_INT(argv[0]))
861 *rval = INT_TO_JSVAL(JS_SetVersion(cx, (JSVersion) JSVAL_TO_INT(argv[0])));
862 else
863 *rval = INT_TO_JSVAL(JS_GetVersion(cx));
864 return JS_TRUE;
867 static JSBool
868 Options(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
870 uint32 optset, flag;
871 JSString *str;
872 const char *opt;
873 char *names;
874 JSBool found;
876 optset = 0;
877 for (uintN i = 0; i < argc; i++) {
878 str = JS_ValueToString(cx, argv[i]);
879 if (!str)
880 return JS_FALSE;
881 argv[i] = STRING_TO_JSVAL(str);
882 opt = JS_GetStringBytes(str);
883 if (!opt)
884 return JS_FALSE;
885 flag = MapContextOptionNameToFlag(cx, opt);
886 if (!flag)
887 return JS_FALSE;
888 optset |= flag;
890 optset = JS_ToggleOptions(cx, optset);
892 names = NULL;
893 found = JS_FALSE;
894 for (size_t i = 0; i != JS_ARRAY_LENGTH(js_options); i++) {
895 if (js_options[i].flag & optset) {
896 found = JS_TRUE;
897 names = JS_sprintf_append(names, "%s%s",
898 names ? "," : "", js_options[i].name);
899 if (!names)
900 break;
903 if (!found)
904 names = strdup("");
905 if (!names) {
906 JS_ReportOutOfMemory(cx);
907 return JS_FALSE;
909 str = JS_NewString(cx, names, strlen(names));
910 if (!str) {
911 free(names);
912 return JS_FALSE;
914 *rval = STRING_TO_JSVAL(str);
915 return JS_TRUE;
918 static JSBool
919 Load(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
921 uintN i;
922 JSString *str;
923 const char *filename;
924 JSScript *script;
925 JSBool ok;
926 uint32 oldopts;
928 for (i = 0; i < argc; i++) {
929 str = JS_ValueToString(cx, argv[i]);
930 if (!str)
931 return JS_FALSE;
932 argv[i] = STRING_TO_JSVAL(str);
933 filename = JS_GetStringBytes(str);
934 errno = 0;
935 oldopts = JS_GetOptions(cx);
936 JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO | JSOPTION_NO_SCRIPT_RVAL);
937 script = JS_CompileFile(cx, obj, filename);
938 JS_SetOptions(cx, oldopts);
939 if (!script) {
940 ok = JS_FALSE;
941 } else {
942 ok = !compileOnly
943 ? JS_ExecuteScript(cx, obj, script, NULL)
944 : JS_TRUE;
945 JS_DestroyScript(cx, script);
947 if (!ok)
948 return JS_FALSE;
951 return JS_TRUE;
955 * function readline()
956 * Provides a hook for scripts to read a line from stdin.
958 static JSBool
959 ReadLine(JSContext *cx, uintN argc, jsval *vp)
961 #define BUFSIZE 256
962 FILE *from;
963 char *buf, *tmp;
964 size_t bufsize, buflength, gotlength;
965 JSBool sawNewline;
966 JSString *str;
968 from = stdin;
969 buflength = 0;
970 bufsize = BUFSIZE;
971 buf = (char *) JS_malloc(cx, bufsize);
972 if (!buf)
973 return JS_FALSE;
975 sawNewline = JS_FALSE;
976 while ((gotlength =
977 js_fgets(buf + buflength, bufsize - buflength, from)) > 0) {
978 buflength += gotlength;
980 /* Are we done? */
981 if (buf[buflength - 1] == '\n') {
982 buf[buflength - 1] = '\0';
983 sawNewline = JS_TRUE;
984 break;
985 } else if (buflength < bufsize - 1) {
986 break;
989 /* Else, grow our buffer for another pass. */
990 bufsize *= 2;
991 if (bufsize > buflength) {
992 tmp = (char *) JS_realloc(cx, buf, bufsize);
993 } else {
994 JS_ReportOutOfMemory(cx);
995 tmp = NULL;
998 if (!tmp) {
999 JS_free(cx, buf);
1000 return JS_FALSE;
1003 buf = tmp;
1006 /* Treat the empty string specially. */
1007 if (buflength == 0) {
1008 *vp = feof(from) ? JSVAL_NULL : JS_GetEmptyStringValue(cx);
1009 JS_free(cx, buf);
1010 return JS_TRUE;
1013 /* Shrink the buffer to the real size. */
1014 tmp = (char *) JS_realloc(cx, buf, buflength);
1015 if (!tmp) {
1016 JS_free(cx, buf);
1017 return JS_FALSE;
1020 buf = tmp;
1023 * Turn buf into a JSString. Note that buflength includes the trailing null
1024 * character.
1026 str = JS_NewString(cx, buf, sawNewline ? buflength - 1 : buflength);
1027 if (!str) {
1028 JS_free(cx, buf);
1029 return JS_FALSE;
1032 *vp = STRING_TO_JSVAL(str);
1033 return JS_TRUE;
1036 static JSBool
1037 Print(JSContext *cx, uintN argc, jsval *vp)
1039 jsval *argv;
1040 uintN i;
1041 JSString *str;
1042 char *bytes;
1044 argv = JS_ARGV(cx, vp);
1045 for (i = 0; i < argc; i++) {
1046 str = JS_ValueToString(cx, argv[i]);
1047 if (!str)
1048 return JS_FALSE;
1049 bytes = JS_EncodeString(cx, str);
1050 if (!bytes)
1051 return JS_FALSE;
1052 fprintf(gOutFile, "%s%s", i ? " " : "", bytes);
1053 JS_free(cx, bytes);
1056 fputc('\n', gOutFile);
1057 fflush(gOutFile);
1059 JS_SET_RVAL(cx, vp, JSVAL_VOID);
1060 return JS_TRUE;
1063 static JSBool
1064 Help(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
1066 static JSBool
1067 Quit(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1069 JS_ConvertArguments(cx, argc, argv,"/ i", &gExitCode);
1071 gQuitting = JS_TRUE;
1072 #ifdef JS_THREADSAFE
1073 if (gWorkers)
1074 js::workers::terminateAll(cx, gWorkers);
1075 #endif
1076 return JS_FALSE;
1079 static const char *
1080 ToSource(JSContext *cx, jsval *vp)
1082 JSString *str = JS_ValueToSource(cx, *vp);
1083 if (str) {
1084 *vp = STRING_TO_JSVAL(str);
1085 return JS_GetStringBytes(str);
1087 JS_ClearPendingException(cx);
1088 return "<<error converting value to string>>";
1091 static JSBool
1092 AssertEq(JSContext *cx, uintN argc, jsval *vp)
1094 if (!(argc == 2 || (argc == 3 && JSVAL_IS_STRING(JS_ARGV(cx, vp)[2])))) {
1095 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
1096 (argc < 2)
1097 ? JSSMSG_NOT_ENOUGH_ARGS
1098 : (argc == 3)
1099 ? JSSMSG_INVALID_ARGS
1100 : JSSMSG_TOO_MANY_ARGS,
1101 "assertEq");
1102 return JS_FALSE;
1105 jsval *argv = JS_ARGV(cx, vp);
1106 if (!JS_SameValue(cx, argv[0], argv[1])) {
1107 const char *actual = ToSource(cx, &argv[0]);
1108 const char *expected = ToSource(cx, &argv[1]);
1109 if (argc == 2) {
1110 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_ASSERT_EQ_FAILED,
1111 actual, expected);
1112 } else {
1113 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_ASSERT_EQ_FAILED_MSG,
1114 actual, expected, JS_GetStringBytes(JSVAL_TO_STRING(argv[2])));
1116 return JS_FALSE;
1118 JS_SET_RVAL(cx, vp, JSVAL_VOID);
1119 return JS_TRUE;
1122 static JSBool
1123 GC(JSContext *cx, uintN argc, jsval *vp)
1125 size_t preBytes = cx->runtime->gcBytes;
1126 JS_GC(cx);
1128 char buf[256];
1129 JS_snprintf(buf, sizeof(buf), "before %lu, after %lu, break %08lx\n",
1130 (unsigned long)preBytes, (unsigned long)cx->runtime->gcBytes,
1131 #ifdef HAVE_SBRK
1132 (unsigned long)sbrk(0)
1133 #else
1135 #endif
1137 *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, buf));
1138 return true;
1141 #ifdef JS_GCMETER
1142 static JSBool
1143 GCStats(JSContext *cx, uintN argc, jsval *vp)
1145 js_DumpGCStats(cx->runtime, stdout);
1146 *vp = JSVAL_VOID;
1147 return true;
1149 #endif
1151 static JSBool
1152 GCParameter(JSContext *cx, uintN argc, jsval *vp)
1154 JSString *str;
1155 const char *paramName;
1156 JSGCParamKey param;
1157 uint32 value;
1159 if (argc == 0) {
1160 str = JS_ValueToString(cx, JSVAL_VOID);
1161 JS_ASSERT(str);
1162 } else {
1163 str = JS_ValueToString(cx, vp[2]);
1164 if (!str)
1165 return JS_FALSE;
1166 vp[2] = STRING_TO_JSVAL(str);
1168 paramName = JS_GetStringBytes(str);
1169 if (!paramName)
1170 return JS_FALSE;
1171 if (strcmp(paramName, "maxBytes") == 0) {
1172 param = JSGC_MAX_BYTES;
1173 } else if (strcmp(paramName, "maxMallocBytes") == 0) {
1174 param = JSGC_MAX_MALLOC_BYTES;
1175 } else if (strcmp(paramName, "gcStackpoolLifespan") == 0) {
1176 param = JSGC_STACKPOOL_LIFESPAN;
1177 } else if (strcmp(paramName, "gcBytes") == 0) {
1178 param = JSGC_BYTES;
1179 } else if (strcmp(paramName, "gcNumber") == 0) {
1180 param = JSGC_NUMBER;
1181 } else if (strcmp(paramName, "gcTriggerFactor") == 0) {
1182 param = JSGC_TRIGGER_FACTOR;
1183 } else {
1184 JS_ReportError(cx,
1185 "the first argument argument must be maxBytes, "
1186 "maxMallocBytes, gcStackpoolLifespan, gcBytes, "
1187 "gcNumber or gcTriggerFactor");
1188 return JS_FALSE;
1191 if (argc == 1) {
1192 value = JS_GetGCParameter(cx->runtime, param);
1193 return JS_NewNumberValue(cx, value, &vp[0]);
1196 if (param == JSGC_NUMBER ||
1197 param == JSGC_BYTES) {
1198 JS_ReportError(cx, "Attempt to change read-only parameter %s",
1199 paramName);
1200 return JS_FALSE;
1203 if (!JS_ValueToECMAUint32(cx, vp[3], &value)) {
1204 JS_ReportError(cx,
1205 "the second argument must be convertable to uint32 "
1206 "with non-zero value");
1207 return JS_FALSE;
1209 if (param == JSGC_TRIGGER_FACTOR && value < 100) {
1210 JS_ReportError(cx,
1211 "the gcTriggerFactor value must be >= 100");
1212 return JS_FALSE;
1214 JS_SetGCParameter(cx->runtime, param, value);
1215 *vp = JSVAL_VOID;
1216 return JS_TRUE;
1219 #ifdef JS_GC_ZEAL
1220 static JSBool
1221 GCZeal(JSContext *cx, uintN argc, jsval *vp)
1223 uint32 zeal;
1225 if (!JS_ValueToECMAUint32(cx, argc == 0 ? JSVAL_VOID : vp[2], &zeal))
1226 return JS_FALSE;
1227 JS_SetGCZeal(cx, (uint8)zeal);
1228 *vp = JSVAL_VOID;
1229 return JS_TRUE;
1231 #endif /* JS_GC_ZEAL */
1233 typedef struct JSCountHeapNode JSCountHeapNode;
1235 struct JSCountHeapNode {
1236 void *thing;
1237 int32 kind;
1238 JSCountHeapNode *next;
1241 typedef struct JSCountHeapTracer {
1242 JSTracer base;
1243 JSDHashTable visited;
1244 JSBool ok;
1245 JSCountHeapNode *traceList;
1246 JSCountHeapNode *recycleList;
1247 } JSCountHeapTracer;
1249 static void
1250 CountHeapNotify(JSTracer *trc, void *thing, uint32 kind)
1252 JSCountHeapTracer *countTracer;
1253 JSDHashEntryStub *entry;
1254 JSCountHeapNode *node;
1256 JS_ASSERT(trc->callback == CountHeapNotify);
1257 countTracer = (JSCountHeapTracer *)trc;
1258 if (!countTracer->ok)
1259 return;
1261 entry = (JSDHashEntryStub *)
1262 JS_DHashTableOperate(&countTracer->visited, thing, JS_DHASH_ADD);
1263 if (!entry) {
1264 JS_ReportOutOfMemory(trc->context);
1265 countTracer->ok = JS_FALSE;
1266 return;
1268 if (entry->key)
1269 return;
1270 entry->key = thing;
1272 node = countTracer->recycleList;
1273 if (node) {
1274 countTracer->recycleList = node->next;
1275 } else {
1276 node = (JSCountHeapNode *) JS_malloc(trc->context, sizeof *node);
1277 if (!node) {
1278 countTracer->ok = JS_FALSE;
1279 return;
1282 node->thing = thing;
1283 node->kind = kind;
1284 node->next = countTracer->traceList;
1285 countTracer->traceList = node;
1288 static JSBool
1289 CountHeap(JSContext *cx, uintN argc, jsval *vp)
1291 void* startThing;
1292 int32 startTraceKind;
1293 jsval v;
1294 int32 traceKind, i;
1295 JSString *str;
1296 char *bytes;
1297 JSCountHeapTracer countTracer;
1298 JSCountHeapNode *node;
1299 size_t counter;
1301 static const struct {
1302 const char *name;
1303 int32 kind;
1304 } traceKindNames[] = {
1305 { "all", -1 },
1306 { "object", JSTRACE_OBJECT },
1307 { "string", JSTRACE_STRING },
1308 #if JS_HAS_XML_SUPPORT
1309 { "xml", JSTRACE_XML },
1310 #endif
1313 startThing = NULL;
1314 startTraceKind = 0;
1315 if (argc > 0) {
1316 v = JS_ARGV(cx, vp)[0];
1317 if (JSVAL_IS_TRACEABLE(v)) {
1318 startThing = JSVAL_TO_TRACEABLE(v);
1319 startTraceKind = JSVAL_TRACE_KIND(v);
1320 } else if (!JSVAL_IS_NULL(v)) {
1321 JS_ReportError(cx,
1322 "the first argument is not null or a heap-allocated "
1323 "thing");
1324 return JS_FALSE;
1328 traceKind = -1;
1329 if (argc > 1) {
1330 str = JS_ValueToString(cx, JS_ARGV(cx, vp)[1]);
1331 if (!str)
1332 return JS_FALSE;
1333 bytes = JS_GetStringBytes(str);
1334 if (!bytes)
1335 return JS_FALSE;
1336 for (i = 0; ;) {
1337 if (strcmp(bytes, traceKindNames[i].name) == 0) {
1338 traceKind = traceKindNames[i].kind;
1339 break;
1341 if (++i == JS_ARRAY_LENGTH(traceKindNames)) {
1342 JS_ReportError(cx, "trace kind name '%s' is unknown", bytes);
1343 return JS_FALSE;
1348 JS_TRACER_INIT(&countTracer.base, cx, CountHeapNotify);
1349 if (!JS_DHashTableInit(&countTracer.visited, JS_DHashGetStubOps(),
1350 NULL, sizeof(JSDHashEntryStub),
1351 JS_DHASH_DEFAULT_CAPACITY(100))) {
1352 JS_ReportOutOfMemory(cx);
1353 return JS_FALSE;
1355 countTracer.ok = JS_TRUE;
1356 countTracer.traceList = NULL;
1357 countTracer.recycleList = NULL;
1359 if (!startThing) {
1360 JS_TraceRuntime(&countTracer.base);
1361 } else {
1362 JS_SET_TRACING_NAME(&countTracer.base, "root");
1363 JS_CallTracer(&countTracer.base, startThing, startTraceKind);
1366 counter = 0;
1367 while ((node = countTracer.traceList) != NULL) {
1368 if (traceKind == -1 || node->kind == traceKind)
1369 counter++;
1370 countTracer.traceList = node->next;
1371 node->next = countTracer.recycleList;
1372 countTracer.recycleList = node;
1373 JS_TraceChildren(&countTracer.base, node->thing, node->kind);
1375 while ((node = countTracer.recycleList) != NULL) {
1376 countTracer.recycleList = node->next;
1377 JS_free(cx, node);
1379 JS_DHashTableFinish(&countTracer.visited);
1381 return countTracer.ok && JS_NewNumberValue(cx, (jsdouble) counter, vp);
1384 static JSScript *
1385 ValueToScript(JSContext *cx, jsval v)
1387 JSScript *script = NULL;
1388 JSFunction *fun;
1390 if (!JSVAL_IS_PRIMITIVE(v)) {
1391 JSObject *obj = JSVAL_TO_OBJECT(v);
1392 JSClass *clasp = JS_GET_CLASS(cx, obj);
1394 if (clasp == Jsvalify(&js_ScriptClass)) {
1395 script = (JSScript *) JS_GetPrivate(cx, obj);
1396 } else if (clasp == Jsvalify(&js_GeneratorClass.base)) {
1397 JSGenerator *gen = (JSGenerator *) JS_GetPrivate(cx, obj);
1398 fun = gen->getFloatingFrame()->fun;
1399 script = FUN_SCRIPT(fun);
1403 if (!script) {
1404 fun = JS_ValueToFunction(cx, v);
1405 if (!fun)
1406 return NULL;
1407 script = FUN_SCRIPT(fun);
1408 if (!script) {
1409 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
1410 JSSMSG_SCRIPTS_ONLY);
1414 return script;
1417 static JSBool
1418 GetTrapArgs(JSContext *cx, uintN argc, jsval *argv, JSScript **scriptp,
1419 int32 *ip)
1421 jsval v;
1422 uintN intarg;
1423 JSScript *script;
1425 *scriptp = JS_GetScriptedCaller(cx, NULL)->script;
1426 *ip = 0;
1427 if (argc != 0) {
1428 v = argv[0];
1429 intarg = 0;
1430 if (!JSVAL_IS_PRIMITIVE(v) &&
1431 (JS_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == Jsvalify(&js_FunctionClass) ||
1432 JS_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == Jsvalify(&js_ScriptClass))) {
1433 script = ValueToScript(cx, v);
1434 if (!script)
1435 return JS_FALSE;
1436 *scriptp = script;
1437 intarg++;
1439 if (argc > intarg) {
1440 if (!JS_ValueToInt32(cx, argv[intarg], ip))
1441 return JS_FALSE;
1444 return JS_TRUE;
1447 static JSTrapStatus
1448 TrapHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval,
1449 jsval closure)
1451 JSString *str = JSVAL_TO_STRING(closure);
1452 JSStackFrame *caller = JS_GetScriptedCaller(cx, NULL);
1453 if (!JS_EvaluateUCInStackFrame(cx, caller,
1454 JS_GetStringChars(str), JS_GetStringLength(str),
1455 caller->script->filename, caller->script->lineno,
1456 rval)) {
1457 return JSTRAP_ERROR;
1459 if (!JSVAL_IS_VOID(*rval))
1460 return JSTRAP_RETURN;
1461 return JSTRAP_CONTINUE;
1464 static JSBool
1465 Trap(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1467 JSString *str;
1468 JSScript *script;
1469 int32 i;
1471 if (argc == 0) {
1472 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_TRAP_USAGE);
1473 return JS_FALSE;
1475 argc--;
1476 str = JS_ValueToString(cx, argv[argc]);
1477 if (!str)
1478 return JS_FALSE;
1479 argv[argc] = STRING_TO_JSVAL(str);
1480 if (!GetTrapArgs(cx, argc, argv, &script, &i))
1481 return JS_FALSE;
1482 return JS_SetTrap(cx, script, script->code + i, TrapHandler, STRING_TO_JSVAL(str));
1485 static JSBool
1486 Untrap(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1488 JSScript *script;
1489 int32 i;
1491 if (!GetTrapArgs(cx, argc, argv, &script, &i))
1492 return JS_FALSE;
1493 JS_ClearTrap(cx, script, script->code + i, NULL, NULL);
1494 return JS_TRUE;
1497 static JSBool
1498 LineToPC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1500 JSScript *script;
1501 int32 i;
1502 uintN lineno;
1503 jsbytecode *pc;
1505 if (argc == 0) {
1506 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_LINE2PC_USAGE);
1507 return JS_FALSE;
1509 script = JS_GetScriptedCaller(cx, NULL)->script;
1510 if (!GetTrapArgs(cx, argc, argv, &script, &i))
1511 return JS_FALSE;
1512 lineno = (i == 0) ? script->lineno : (uintN)i;
1513 pc = JS_LineNumberToPC(cx, script, lineno);
1514 if (!pc)
1515 return JS_FALSE;
1516 *rval = INT_TO_JSVAL(pc - script->code);
1517 return JS_TRUE;
1520 static JSBool
1521 PCToLine(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1523 JSScript *script;
1524 int32 i;
1525 uintN lineno;
1527 if (!GetTrapArgs(cx, argc, argv, &script, &i))
1528 return JS_FALSE;
1529 lineno = JS_PCToLineNumber(cx, script, script->code + i);
1530 if (!lineno)
1531 return JS_FALSE;
1532 *rval = INT_TO_JSVAL(lineno);
1533 return JS_TRUE;
1536 #ifdef DEBUG
1538 static void
1539 UpdateSwitchTableBounds(JSContext *cx, JSScript *script, uintN offset,
1540 uintN *start, uintN *end)
1542 jsbytecode *pc;
1543 JSOp op;
1544 ptrdiff_t jmplen;
1545 jsint low, high, n;
1547 pc = script->code + offset;
1548 op = js_GetOpcode(cx, script, pc);
1549 switch (op) {
1550 case JSOP_TABLESWITCHX:
1551 jmplen = JUMPX_OFFSET_LEN;
1552 goto jump_table;
1553 case JSOP_TABLESWITCH:
1554 jmplen = JUMP_OFFSET_LEN;
1555 jump_table:
1556 pc += jmplen;
1557 low = GET_JUMP_OFFSET(pc);
1558 pc += JUMP_OFFSET_LEN;
1559 high = GET_JUMP_OFFSET(pc);
1560 pc += JUMP_OFFSET_LEN;
1561 n = high - low + 1;
1562 break;
1564 case JSOP_LOOKUPSWITCHX:
1565 jmplen = JUMPX_OFFSET_LEN;
1566 goto lookup_table;
1567 case JSOP_LOOKUPSWITCH:
1568 jmplen = JUMP_OFFSET_LEN;
1569 lookup_table:
1570 pc += jmplen;
1571 n = GET_INDEX(pc);
1572 pc += INDEX_LEN;
1573 jmplen += JUMP_OFFSET_LEN;
1574 break;
1576 default:
1577 /* [condswitch] switch does not have any jump or lookup tables. */
1578 JS_ASSERT(op == JSOP_CONDSWITCH);
1579 return;
1582 *start = (uintN)(pc - script->code);
1583 *end = *start + (uintN)(n * jmplen);
1586 static void
1587 SrcNotes(JSContext *cx, JSScript *script)
1589 uintN offset, delta, caseOff, switchTableStart, switchTableEnd;
1590 jssrcnote *notes, *sn;
1591 JSSrcNoteType type;
1592 const char *name;
1593 uint32 index;
1594 JSAtom *atom;
1595 JSString *str;
1597 fprintf(gOutFile, "\nSource notes:\n");
1598 offset = 0;
1599 notes = script->notes();
1600 switchTableEnd = switchTableStart = 0;
1601 for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
1602 delta = SN_DELTA(sn);
1603 offset += delta;
1604 type = (JSSrcNoteType) SN_TYPE(sn);
1605 name = js_SrcNoteSpec[type].name;
1606 if (type == SRC_LABEL) {
1607 /* Check if the source note is for a switch case. */
1608 if (switchTableStart <= offset && offset < switchTableEnd) {
1609 name = "case";
1610 } else {
1611 JS_ASSERT(js_GetOpcode(cx, script, script->code + offset) == JSOP_NOP);
1614 fprintf(gOutFile, "%3u: %5u [%4u] %-8s",
1615 (uintN) (sn - notes), offset, delta, name);
1616 switch (type) {
1617 case SRC_SETLINE:
1618 fprintf(gOutFile, " lineno %u", (uintN) js_GetSrcNoteOffset(sn, 0));
1619 break;
1620 case SRC_FOR:
1621 fprintf(gOutFile, " cond %u update %u tail %u",
1622 (uintN) js_GetSrcNoteOffset(sn, 0),
1623 (uintN) js_GetSrcNoteOffset(sn, 1),
1624 (uintN) js_GetSrcNoteOffset(sn, 2));
1625 break;
1626 case SRC_IF_ELSE:
1627 fprintf(gOutFile, " else %u elseif %u",
1628 (uintN) js_GetSrcNoteOffset(sn, 0),
1629 (uintN) js_GetSrcNoteOffset(sn, 1));
1630 break;
1631 case SRC_COND:
1632 case SRC_WHILE:
1633 case SRC_PCBASE:
1634 case SRC_PCDELTA:
1635 case SRC_DECL:
1636 case SRC_BRACE:
1637 fprintf(gOutFile, " offset %u", (uintN) js_GetSrcNoteOffset(sn, 0));
1638 break;
1639 case SRC_LABEL:
1640 case SRC_LABELBRACE:
1641 case SRC_BREAK2LABEL:
1642 case SRC_CONT2LABEL:
1643 index = js_GetSrcNoteOffset(sn, 0);
1644 JS_GET_SCRIPT_ATOM(script, NULL, index, atom);
1645 str = ATOM_TO_STRING(atom);
1646 fprintf(gOutFile, " atom %u (", index);
1647 js_FileEscapedString(gOutFile, str, 0);
1648 putc(')', gOutFile);
1649 break;
1650 case SRC_FUNCDEF: {
1651 const char *bytes;
1652 JSObject *obj;
1653 JSFunction *fun;
1655 index = js_GetSrcNoteOffset(sn, 0);
1656 obj = script->getObject(index);
1657 fun = (JSFunction *) JS_GetPrivate(cx, obj);
1658 str = JS_DecompileFunction(cx, fun, JS_DONT_PRETTY_PRINT);
1659 if (str) {
1660 bytes = JS_GetStringBytes(str);
1661 } else {
1662 ReportException(cx);
1663 bytes = "N/A";
1665 fprintf(gOutFile, " function %u (%s)", index, bytes);
1666 break;
1668 case SRC_SWITCH:
1669 fprintf(gOutFile, " length %u", (uintN) js_GetSrcNoteOffset(sn, 0));
1670 caseOff = (uintN) js_GetSrcNoteOffset(sn, 1);
1671 if (caseOff)
1672 fprintf(gOutFile, " first case offset %u", caseOff);
1673 UpdateSwitchTableBounds(cx, script, offset,
1674 &switchTableStart, &switchTableEnd);
1675 break;
1676 case SRC_CATCH:
1677 delta = (uintN) js_GetSrcNoteOffset(sn, 0);
1678 if (delta) {
1679 if (script->main[offset] == JSOP_LEAVEBLOCK)
1680 fprintf(gOutFile, " stack depth %u", delta);
1681 else
1682 fprintf(gOutFile, " guard delta %u", delta);
1684 break;
1685 default:;
1687 fputc('\n', gOutFile);
1691 static JSBool
1692 Notes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1694 uintN i;
1695 JSScript *script;
1697 for (i = 0; i < argc; i++) {
1698 script = ValueToScript(cx, argv[i]);
1699 if (!script)
1700 continue;
1702 SrcNotes(cx, script);
1704 return JS_TRUE;
1707 JS_STATIC_ASSERT(JSTRY_CATCH == 0);
1708 JS_STATIC_ASSERT(JSTRY_FINALLY == 1);
1709 JS_STATIC_ASSERT(JSTRY_ITER == 2);
1711 static const char* const TryNoteNames[] = { "catch", "finally", "iter" };
1713 static JSBool
1714 TryNotes(JSContext *cx, JSScript *script)
1716 JSTryNote *tn, *tnlimit;
1718 if (script->trynotesOffset == 0)
1719 return JS_TRUE;
1721 tn = script->trynotes()->vector;
1722 tnlimit = tn + script->trynotes()->length;
1723 fprintf(gOutFile, "\nException table:\n"
1724 "kind stack start end\n");
1725 do {
1726 JS_ASSERT(tn->kind < JS_ARRAY_LENGTH(TryNoteNames));
1727 fprintf(gOutFile, " %-7s %6u %8u %8u\n",
1728 TryNoteNames[tn->kind], tn->stackDepth,
1729 tn->start, tn->start + tn->length);
1730 } while (++tn != tnlimit);
1731 return JS_TRUE;
1734 static bool
1735 DisassembleValue(JSContext *cx, jsval v, bool lines, bool recursive)
1737 JSScript *script = ValueToScript(cx, v);
1738 if (!script)
1739 return false;
1740 if (VALUE_IS_FUNCTION(cx, v)) {
1741 JSFunction *fun = JS_ValueToFunction(cx, v);
1742 if (fun && (fun->flags & ~7U)) {
1743 uint16 flags = fun->flags;
1744 fputs("flags:", stdout);
1746 #define SHOW_FLAG(flag) if (flags & JSFUN_##flag) fputs(" " #flag, stdout);
1748 SHOW_FLAG(LAMBDA);
1749 SHOW_FLAG(SETTER);
1750 SHOW_FLAG(GETTER);
1751 SHOW_FLAG(BOUND_METHOD);
1752 SHOW_FLAG(HEAVYWEIGHT);
1753 SHOW_FLAG(THISP_STRING);
1754 SHOW_FLAG(THISP_NUMBER);
1755 SHOW_FLAG(THISP_BOOLEAN);
1756 SHOW_FLAG(EXPR_CLOSURE);
1757 SHOW_FLAG(TRCINFO);
1759 #undef SHOW_FLAG
1761 if (FUN_INTERPRETED(fun)) {
1762 if (FUN_NULL_CLOSURE(fun))
1763 fputs(" NULL_CLOSURE", stdout);
1764 else if (FUN_FLAT_CLOSURE(fun))
1765 fputs(" FLAT_CLOSURE", stdout);
1767 if (fun->u.i.nupvars) {
1768 fputs("\nupvars: {\n", stdout);
1770 void *mark = JS_ARENA_MARK(&cx->tempPool);
1771 jsuword *localNames = js_GetLocalNameArray(cx, fun, &cx->tempPool);
1772 if (!localNames)
1773 return false;
1775 JSUpvarArray *uva = fun->u.i.script->upvars();
1776 uintN upvar_base = fun->countArgsAndVars();
1778 for (uint32 i = 0, n = uva->length; i < n; i++) {
1779 JSAtom *atom = JS_LOCAL_NAME_TO_ATOM(localNames[upvar_base + i]);
1780 UpvarCookie cookie = uva->vector[i];
1782 printf(" %s: {skip:%u, slot:%u},\n",
1783 js_AtomToPrintableString(cx, atom), cookie.level(), cookie.slot());
1786 JS_ARENA_RELEASE(&cx->tempPool, mark);
1787 putchar('}');
1790 putchar('\n');
1794 if (!js_Disassemble(cx, script, lines, stdout))
1795 return false;
1796 SrcNotes(cx, script);
1797 TryNotes(cx, script);
1799 if (recursive && script->objectsOffset != 0) {
1800 JSObjectArray *objects = script->objects();
1801 for (uintN i = 0; i != objects->length; ++i) {
1802 JSObject *obj = objects->vector[i];
1803 if (obj->isFunction()) {
1804 putchar('\n');
1805 if (!DisassembleValue(cx, OBJECT_TO_JSVAL(obj),
1806 lines, recursive)) {
1807 return false;
1812 return true;
1815 static JSBool
1816 Disassemble(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1818 bool lines = false, recursive = false;
1820 while (argc > 0 && JSVAL_IS_STRING(argv[0])) {
1821 const char *bytes = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
1822 lines = !strcmp(bytes, "-l");
1823 recursive = !strcmp(bytes, "-r");
1824 if (!lines && !recursive)
1825 break;
1826 argv++, argc--;
1829 for (uintN i = 0; i < argc; i++) {
1830 if (!DisassembleValue(cx, argv[i], lines, recursive))
1831 return false;
1833 return true;
1836 static JSBool
1837 DisassFile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1839 JSString *str;
1840 const char *filename;
1841 JSScript *script;
1842 JSBool ok;
1843 uint32 oldopts;
1845 if (!argc)
1846 return JS_TRUE;
1848 str = JS_ValueToString(cx, argv[0]);
1849 if (!str)
1850 return JS_FALSE;
1851 argv[0] = STRING_TO_JSVAL(str);
1853 filename = JS_GetStringBytes(str);
1854 oldopts = JS_GetOptions(cx);
1855 JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO | JSOPTION_NO_SCRIPT_RVAL);
1856 script = JS_CompileFile(cx, obj, filename);
1857 JS_SetOptions(cx, oldopts);
1858 if (!script)
1859 return JS_FALSE;
1861 if (script->isEmpty())
1862 return JS_TRUE;
1864 obj = JS_NewScriptObject(cx, script);
1865 if (!obj)
1866 return JS_FALSE;
1868 *rval = OBJECT_TO_JSVAL(obj); /* I like to root it, root it. */
1869 ok = Disassemble(cx, obj, 1, rval, rval); /* gross, but works! */
1870 *rval = JSVAL_VOID;
1872 return ok;
1875 static JSBool
1876 DisassWithSrc(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1877 jsval *rval)
1879 #define LINE_BUF_LEN 512
1880 uintN i, len, line1, line2, bupline;
1881 JSScript *script;
1882 FILE *file;
1883 char linebuf[LINE_BUF_LEN];
1884 jsbytecode *pc, *end;
1885 JSBool ok;
1886 static char sep[] = ";-------------------------";
1888 ok = JS_TRUE;
1889 for (i = 0; ok && i < argc; i++) {
1890 script = ValueToScript(cx, argv[i]);
1891 if (!script)
1892 return JS_FALSE;
1894 if (!script->filename) {
1895 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
1896 JSSMSG_FILE_SCRIPTS_ONLY);
1897 return JS_FALSE;
1900 file = fopen(script->filename, "r");
1901 if (!file) {
1902 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
1903 JSSMSG_CANT_OPEN, script->filename,
1904 strerror(errno));
1905 return JS_FALSE;
1908 pc = script->code;
1909 end = pc + script->length;
1911 /* burn the leading lines */
1912 line2 = JS_PCToLineNumber(cx, script, pc);
1913 for (line1 = 0; line1 < line2 - 1; line1++) {
1914 char *tmp = fgets(linebuf, LINE_BUF_LEN, file);
1915 if (!tmp) {
1916 JS_ReportError(cx, "failed to read %s fully",
1917 script->filename);
1918 ok = JS_FALSE;
1919 goto bail;
1923 bupline = 0;
1924 while (pc < end) {
1925 line2 = JS_PCToLineNumber(cx, script, pc);
1927 if (line2 < line1) {
1928 if (bupline != line2) {
1929 bupline = line2;
1930 fprintf(gOutFile, "%s %3u: BACKUP\n", sep, line2);
1932 } else {
1933 if (bupline && line1 == line2)
1934 fprintf(gOutFile, "%s %3u: RESTORE\n", sep, line2);
1935 bupline = 0;
1936 while (line1 < line2) {
1937 if (!fgets(linebuf, LINE_BUF_LEN, file)) {
1938 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
1939 JSSMSG_UNEXPECTED_EOF,
1940 script->filename);
1941 ok = JS_FALSE;
1942 goto bail;
1944 line1++;
1945 fprintf(gOutFile, "%s %3u: %s", sep, line1, linebuf);
1949 len = js_Disassemble1(cx, script, pc,
1950 pc - script->code,
1951 JS_TRUE, stdout);
1952 if (!len) {
1953 ok = JS_FALSE;
1954 goto bail;
1956 pc += len;
1959 bail:
1960 fclose(file);
1962 return ok;
1963 #undef LINE_BUF_LEN
1966 static JSBool
1967 Tracing(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1969 FILE *file;
1971 if (argc == 0) {
1972 *rval = BOOLEAN_TO_JSVAL(cx->tracefp != 0);
1973 return JS_TRUE;
1976 switch (JS_TypeOfValue(cx, argv[0])) {
1977 case JSTYPE_NUMBER:
1978 case JSTYPE_BOOLEAN: {
1979 JSBool bval;
1980 JS_ValueToBoolean(cx, argv[0], &bval);
1981 file = bval ? stderr : NULL;
1982 break;
1984 case JSTYPE_STRING: {
1985 char *name = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
1986 file = fopen(name, "w");
1987 if (!file) {
1988 JS_ReportError(cx, "tracing: couldn't open output file %s: %s",
1989 name, strerror(errno));
1990 return JS_FALSE;
1992 break;
1994 default:
1995 goto bad_argument;
1997 if (cx->tracefp && cx->tracefp != stderr)
1998 fclose((FILE *)cx->tracefp);
1999 cx->tracefp = file;
2000 cx->tracePrevPc = NULL;
2001 return JS_TRUE;
2003 bad_argument:
2004 JSString *str = JS_ValueToString(cx, argv[0]);
2005 if (!str)
2006 return JS_FALSE;
2007 JS_ReportError(cx, "tracing: illegal argument %s",
2008 JS_GetStringBytes(str));
2009 return JS_FALSE;
2012 static void
2013 DumpScope(JSContext *cx, JSObject *obj, FILE *fp)
2015 uintN i = 0;
2016 for (JSScopeProperty *sprop = NULL; JS_PropertyIterator(obj, &sprop);) {
2017 fprintf(fp, "%3u %p ", i++, (void *) sprop);
2018 sprop->dump(cx, fp);
2022 static JSBool
2023 DumpStats(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
2025 uintN i;
2026 JSString *str;
2027 const char *bytes;
2028 jsid id;
2029 JSObject *obj2;
2030 JSProperty *prop;
2031 Value value;
2033 for (i = 0; i < argc; i++) {
2034 str = JS_ValueToString(cx, argv[i]);
2035 if (!str)
2036 return JS_FALSE;
2037 argv[i] = STRING_TO_JSVAL(str);
2038 bytes = JS_GetStringBytes(str);
2039 if (strcmp(bytes, "arena") == 0) {
2040 #ifdef JS_ARENAMETER
2041 JS_DumpArenaStats(stdout);
2042 #endif
2043 } else if (strcmp(bytes, "atom") == 0) {
2044 js_DumpAtoms(cx, gOutFile);
2045 } else if (strcmp(bytes, "global") == 0) {
2046 DumpScope(cx, cx->globalObject, stdout);
2047 } else {
2048 if (!JS_ValueToId(cx, STRING_TO_JSVAL(str), &id))
2049 return JS_FALSE;
2050 if (!js_FindProperty(cx, id, &obj, &obj2, &prop))
2051 return JS_FALSE;
2052 if (prop) {
2053 obj2->dropProperty(cx, prop);
2054 if (!obj->getProperty(cx, id, &value))
2055 return JS_FALSE;
2057 if (!prop || !value.isObjectOrNull()) {
2058 fprintf(gErrFile, "js: invalid stats argument %s\n",
2059 bytes);
2060 continue;
2062 obj = value.toObjectOrNull();
2063 if (obj)
2064 DumpScope(cx, obj, stdout);
2067 return JS_TRUE;
2070 static JSBool
2071 DumpHeap(JSContext *cx, uintN argc, jsval *vp)
2073 char *fileName;
2074 jsval v;
2075 void* startThing;
2076 uint32 startTraceKind;
2077 const char *badTraceArg;
2078 void *thingToFind;
2079 size_t maxDepth;
2080 void *thingToIgnore;
2081 FILE *dumpFile;
2082 JSBool ok;
2084 fileName = NULL;
2085 if (argc > 0) {
2086 v = JS_ARGV(cx, vp)[0];
2087 if (!JSVAL_IS_NULL(v)) {
2088 JSString *str;
2090 str = JS_ValueToString(cx, v);
2091 if (!str)
2092 return JS_FALSE;
2093 JS_ARGV(cx, vp)[0] = STRING_TO_JSVAL(str);
2094 fileName = JS_GetStringBytes(str);
2098 startThing = NULL;
2099 startTraceKind = 0;
2100 if (argc > 1) {
2101 v = JS_ARGV(cx, vp)[1];
2102 if (JSVAL_IS_TRACEABLE(v)) {
2103 startThing = JSVAL_TO_TRACEABLE(v);
2104 startTraceKind = JSVAL_TRACE_KIND(v);
2105 } else if (!JSVAL_IS_NULL(v)) {
2106 badTraceArg = "start";
2107 goto not_traceable_arg;
2111 thingToFind = NULL;
2112 if (argc > 2) {
2113 v = JS_ARGV(cx, vp)[2];
2114 if (JSVAL_IS_TRACEABLE(v)) {
2115 thingToFind = JSVAL_TO_TRACEABLE(v);
2116 } else if (!JSVAL_IS_NULL(v)) {
2117 badTraceArg = "toFind";
2118 goto not_traceable_arg;
2122 maxDepth = (size_t)-1;
2123 if (argc > 3) {
2124 v = JS_ARGV(cx, vp)[3];
2125 if (!JSVAL_IS_NULL(v)) {
2126 uint32 depth;
2128 if (!JS_ValueToECMAUint32(cx, v, &depth))
2129 return JS_FALSE;
2130 maxDepth = depth;
2134 thingToIgnore = NULL;
2135 if (argc > 4) {
2136 v = JS_ARGV(cx, vp)[4];
2137 if (JSVAL_IS_TRACEABLE(v)) {
2138 thingToIgnore = JSVAL_TO_TRACEABLE(v);
2139 } else if (!JSVAL_IS_NULL(v)) {
2140 badTraceArg = "toIgnore";
2141 goto not_traceable_arg;
2145 if (!fileName) {
2146 dumpFile = stdout;
2147 } else {
2148 dumpFile = fopen(fileName, "w");
2149 if (!dumpFile) {
2150 JS_ReportError(cx, "can't open %s: %s", fileName, strerror(errno));
2151 return JS_FALSE;
2155 ok = JS_DumpHeap(cx, dumpFile, startThing, startTraceKind, thingToFind,
2156 maxDepth, thingToIgnore);
2157 if (dumpFile != stdout)
2158 fclose(dumpFile);
2159 return ok;
2161 not_traceable_arg:
2162 JS_ReportError(cx, "argument '%s' is not null or a heap-allocated thing",
2163 badTraceArg);
2164 return JS_FALSE;
2167 #endif /* DEBUG */
2169 #ifdef TEST_CVTARGS
2170 #include <ctype.h>
2172 static const char *
2173 EscapeWideString(jschar *w)
2175 static char enuf[80];
2176 static char hex[] = "0123456789abcdef";
2177 jschar u;
2178 unsigned char b, c;
2179 int i, j;
2181 if (!w)
2182 return "";
2183 for (i = j = 0; i < sizeof enuf - 1; i++, j++) {
2184 u = w[j];
2185 if (u == 0)
2186 break;
2187 b = (unsigned char)(u >> 8);
2188 c = (unsigned char)(u);
2189 if (b) {
2190 if (i >= sizeof enuf - 6)
2191 break;
2192 enuf[i++] = '\\';
2193 enuf[i++] = 'u';
2194 enuf[i++] = hex[b >> 4];
2195 enuf[i++] = hex[b & 15];
2196 enuf[i++] = hex[c >> 4];
2197 enuf[i] = hex[c & 15];
2198 } else if (!isprint(c)) {
2199 if (i >= sizeof enuf - 4)
2200 break;
2201 enuf[i++] = '\\';
2202 enuf[i++] = 'x';
2203 enuf[i++] = hex[c >> 4];
2204 enuf[i] = hex[c & 15];
2205 } else {
2206 enuf[i] = (char)c;
2209 enuf[i] = 0;
2210 return enuf;
2213 #include <stdarg.h>
2215 static JSBool
2216 ZZ_formatter(JSContext *cx, const char *format, JSBool fromJS, jsval **vpp,
2217 va_list *app)
2219 jsval *vp;
2220 va_list ap;
2221 jsdouble re, im;
2223 printf("entering ZZ_formatter");
2224 vp = *vpp;
2225 ap = *app;
2226 if (fromJS) {
2227 if (!JS_ValueToNumber(cx, vp[0], &re))
2228 return JS_FALSE;
2229 if (!JS_ValueToNumber(cx, vp[1], &im))
2230 return JS_FALSE;
2231 *va_arg(ap, jsdouble *) = re;
2232 *va_arg(ap, jsdouble *) = im;
2233 } else {
2234 re = va_arg(ap, jsdouble);
2235 im = va_arg(ap, jsdouble);
2236 if (!JS_NewNumberValue(cx, re, &vp[0]))
2237 return JS_FALSE;
2238 if (!JS_NewNumberValue(cx, im, &vp[1]))
2239 return JS_FALSE;
2241 *vpp = vp + 2;
2242 *app = ap;
2243 printf("leaving ZZ_formatter");
2244 return JS_TRUE;
2247 static JSBool
2248 ConvertArgs(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
2250 JSBool b = JS_FALSE;
2251 jschar c = 0;
2252 int32 i = 0, j = 0;
2253 uint32 u = 0;
2254 jsdouble d = 0, I = 0, re = 0, im = 0;
2255 char *s = NULL;
2256 JSString *str = NULL;
2257 jschar *w = NULL;
2258 JSObject *obj2 = NULL;
2259 JSFunction *fun = NULL;
2260 jsval v = JSVAL_VOID;
2261 JSBool ok;
2263 if (!JS_AddArgumentFormatter(cx, "ZZ", ZZ_formatter))
2264 return JS_FALSE;
2265 ok = JS_ConvertArguments(cx, argc, argv, "b/ciujdIsSWofvZZ*",
2266 &b, &c, &i, &u, &j, &d, &I, &s, &str, &w, &obj2,
2267 &fun, &v, &re, &im);
2268 JS_RemoveArgumentFormatter(cx, "ZZ");
2269 if (!ok)
2270 return JS_FALSE;
2271 fprintf(gOutFile,
2272 "b %u, c %x (%c), i %ld, u %lu, j %ld\n",
2273 b, c, (char)c, i, u, j);
2274 ToString obj2string(cx, obj2);
2275 ToString valueString(cx, v);
2276 JSString *tmpstr = JS_DecompileFunction(cx, fun, 4);
2277 const char *func;
2278 if (tmpstr) {
2279 func = JS_GetStringBytes(tmpstr);
2280 } else {
2281 ReportException(cx);
2282 func = "error decompiling fun";
2284 fprintf(gOutFile,
2285 "d %g, I %g, s %s, S %s, W %s, obj %s, fun %s\n"
2286 "v %s, re %g, im %g\n",
2287 d, I, s, str ? JS_GetStringBytes(str) : "", EscapeWideString(w),
2288 obj2string.getBytes(),
2289 fun ? func : "",
2290 valueString.getBytes(), re, im);
2291 return JS_TRUE;
2293 #endif
2295 static JSBool
2296 BuildDate(JSContext *cx, uintN argc, jsval *vp)
2298 char version[20] = "\n";
2299 #if JS_VERSION < 150
2300 sprintf(version, " for version %d\n", JS_VERSION);
2301 #endif
2302 fprintf(gOutFile, "built on %s at %s%s", __DATE__, __TIME__, version);
2303 *vp = JSVAL_VOID;
2304 return JS_TRUE;
2307 static JSBool
2308 Clear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
2310 if (argc != 0 && !JS_ValueToObject(cx, argv[0], &obj))
2311 return JS_FALSE;
2312 JS_ClearScope(cx, obj);
2313 return JS_TRUE;
2316 static JSBool
2317 Intern(JSContext *cx, uintN argc, jsval *vp)
2319 JSString *str;
2321 str = JS_ValueToString(cx, argc == 0 ? JSVAL_VOID : vp[2]);
2322 if (!str)
2323 return JS_FALSE;
2324 if (!JS_InternUCStringN(cx, JS_GetStringChars(str),
2325 JS_GetStringLength(str))) {
2326 return JS_FALSE;
2328 *vp = JSVAL_VOID;
2329 return JS_TRUE;
2332 static JSBool
2333 Clone(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
2335 JSObject *funobj, *parent, *clone;
2337 if (VALUE_IS_FUNCTION(cx, argv[0])) {
2338 funobj = JSVAL_TO_OBJECT(argv[0]);
2339 } else {
2340 JSFunction *fun = JS_ValueToFunction(cx, argv[0]);
2341 if (!fun)
2342 return JS_FALSE;
2343 funobj = JS_GetFunctionObject(fun);
2345 if (argc > 1) {
2346 if (!JS_ValueToObject(cx, argv[1], &parent))
2347 return JS_FALSE;
2348 } else {
2349 parent = JS_GetParent(cx, funobj);
2351 clone = JS_CloneFunctionObject(cx, funobj, parent);
2352 if (!clone)
2353 return JS_FALSE;
2354 *rval = OBJECT_TO_JSVAL(clone);
2355 return JS_TRUE;
2358 static JSBool
2359 Seal(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
2361 JSObject *target;
2362 JSBool deep = JS_FALSE;
2364 if (!JS_ConvertArguments(cx, argc, argv, "o/b", &target, &deep))
2365 return JS_FALSE;
2366 if (!target)
2367 return JS_TRUE;
2368 return JS_SealObject(cx, target, deep);
2371 static JSBool
2372 GetPDA(JSContext *cx, uintN argc, jsval *vp)
2374 JSObject *vobj, *aobj, *pdobj;
2375 JSBool ok;
2376 JSPropertyDescArray pda;
2377 JSPropertyDesc *pd;
2378 uint32 i;
2379 jsval v;
2381 if (!JS_ValueToObject(cx, argc == 0 ? JSVAL_VOID : vp[2], &vobj))
2382 return JS_FALSE;
2383 if (!vobj) {
2384 *vp = JSVAL_VOID;
2385 return JS_TRUE;
2388 aobj = JS_NewArrayObject(cx, 0, NULL);
2389 if (!aobj)
2390 return JS_FALSE;
2391 *vp = OBJECT_TO_JSVAL(aobj);
2393 ok = JS_GetPropertyDescArray(cx, vobj, &pda);
2394 if (!ok)
2395 return JS_FALSE;
2396 pd = pda.array;
2397 for (i = 0; i < pda.length; i++, pd++) {
2398 pdobj = JS_NewObject(cx, NULL, NULL, NULL);
2399 if (!pdobj) {
2400 ok = JS_FALSE;
2401 break;
2404 /* Protect pdobj from GC by setting it as an element of aobj now */
2405 v = OBJECT_TO_JSVAL(pdobj);
2406 ok = JS_SetElement(cx, aobj, i, &v);
2407 if (!ok)
2408 break;
2410 ok = JS_SetProperty(cx, pdobj, "id", &pd->id) &&
2411 JS_SetProperty(cx, pdobj, "value", &pd->value) &&
2412 (v = INT_TO_JSVAL(pd->flags),
2413 JS_SetProperty(cx, pdobj, "flags", &v)) &&
2414 (v = INT_TO_JSVAL(pd->slot),
2415 JS_SetProperty(cx, pdobj, "slot", &v)) &&
2416 JS_SetProperty(cx, pdobj, "alias", &pd->alias);
2417 if (!ok)
2418 break;
2420 JS_PutPropertyDescArray(cx, &pda);
2421 return ok;
2424 static JSBool
2425 GetSLX(JSContext *cx, uintN argc, jsval *vp)
2427 JSScript *script;
2429 script = ValueToScript(cx, argc == 0 ? JSVAL_VOID : vp[2]);
2430 if (!script)
2431 return JS_FALSE;
2432 *vp = INT_TO_JSVAL(js_GetScriptLineExtent(script));
2433 return JS_TRUE;
2436 static JSBool
2437 ToInt32(JSContext *cx, uintN argc, jsval *vp)
2439 int32 i;
2441 if (!JS_ValueToInt32(cx, argc == 0 ? JSVAL_VOID : vp[2], &i))
2442 return JS_FALSE;
2443 return JS_NewNumberValue(cx, i, vp);
2446 static JSBool
2447 StringsAreUTF8(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
2448 jsval *rval)
2450 *rval = JS_CStringsAreUTF8() ? JSVAL_TRUE : JSVAL_FALSE;
2451 return JS_TRUE;
2454 static JSBool
2455 StackQuota(JSContext *cx, uintN argc, jsval *vp)
2457 uint32 n;
2459 if (argc == 0)
2460 return JS_NewNumberValue(cx, (double) gScriptStackQuota, vp);
2461 if (!JS_ValueToECMAUint32(cx, JS_ARGV(cx, vp)[0], &n))
2462 return JS_FALSE;
2463 gScriptStackQuota = n;
2464 JS_SetScriptStackQuota(cx, gScriptStackQuota);
2465 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2466 return JS_TRUE;
2469 static const char* badUTF8 = "...\xC0...";
2470 static const char* bigUTF8 = "...\xFB\xBF\xBF\xBF\xBF...";
2471 static const jschar badSurrogate[] = { 'A', 'B', 'C', 0xDEEE, 'D', 'E', 0 };
2473 static JSBool
2474 TestUTF8(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
2476 int32 mode = 1;
2477 jschar chars[20];
2478 size_t charsLength = 5;
2479 char bytes[20];
2480 size_t bytesLength = 20;
2481 if (argc && !JS_ValueToInt32(cx, *argv, &mode))
2482 return JS_FALSE;
2484 /* The following throw errors if compiled with UTF-8. */
2485 switch (mode) {
2486 /* mode 1: malformed UTF-8 string. */
2487 case 1:
2488 JS_NewStringCopyZ(cx, badUTF8);
2489 break;
2490 /* mode 2: big UTF-8 character. */
2491 case 2:
2492 JS_NewStringCopyZ(cx, bigUTF8);
2493 break;
2494 /* mode 3: bad surrogate character. */
2495 case 3:
2496 JS_EncodeCharacters(cx, badSurrogate, 6, bytes, &bytesLength);
2497 break;
2498 /* mode 4: use a too small buffer. */
2499 case 4:
2500 JS_DecodeBytes(cx, "1234567890", 10, chars, &charsLength);
2501 break;
2502 default:
2503 JS_ReportError(cx, "invalid mode parameter");
2504 return JS_FALSE;
2506 return !JS_IsExceptionPending (cx);
2509 static JSBool
2510 ThrowError(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
2512 JS_ReportError(cx, "This is an error");
2513 return JS_FALSE;
2516 #define LAZY_STANDARD_CLASSES
2518 /* A class for easily testing the inner/outer object callbacks. */
2519 typedef struct ComplexObject {
2520 JSBool isInner;
2521 JSBool frozen;
2522 JSObject *inner;
2523 JSObject *outer;
2524 } ComplexObject;
2526 static JSObject *
2527 split_create_outer(JSContext *cx);
2529 static JSObject *
2530 split_create_inner(JSContext *cx, JSObject *outer);
2532 static ComplexObject *
2533 split_get_private(JSContext *cx, JSObject *obj);
2535 static JSBool
2536 split_addProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
2538 ComplexObject *cpx;
2540 cpx = split_get_private(cx, obj);
2541 if (!cpx)
2542 return JS_TRUE;
2543 if (!cpx->isInner && cpx->inner) {
2544 /* Make sure to define this property on the inner object. */
2545 return JS_DefinePropertyById(cx, cpx->inner, id, *vp, NULL, NULL, JSPROP_ENUMERATE);
2547 return JS_TRUE;
2550 static JSBool
2551 split_getProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
2553 ComplexObject *cpx;
2555 cpx = split_get_private(cx, obj);
2556 if (!cpx)
2557 return JS_TRUE;
2559 if (JSID_IS_ATOM(id) &&
2560 !strcmp(JS_GetStringBytes(JSID_TO_STRING(id)), "isInner")) {
2561 *vp = BOOLEAN_TO_JSVAL(cpx->isInner);
2562 return JS_TRUE;
2565 if (!cpx->isInner && cpx->inner) {
2566 if (JSID_IS_ATOM(id)) {
2567 JSString *str;
2569 str = JSID_TO_STRING(id);
2570 return JS_GetUCProperty(cx, cpx->inner, JS_GetStringChars(str),
2571 JS_GetStringLength(str), vp);
2573 if (JSID_IS_INT(id))
2574 return JS_GetElement(cx, cpx->inner, JSID_TO_INT(id), vp);
2575 return JS_TRUE;
2578 return JS_TRUE;
2581 static JSBool
2582 split_setProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
2584 ComplexObject *cpx;
2586 cpx = split_get_private(cx, obj);
2587 if (!cpx)
2588 return JS_TRUE;
2589 if (!cpx->isInner && cpx->inner) {
2590 if (JSID_IS_ATOM(id)) {
2591 JSString *str;
2593 str = JSID_TO_STRING(id);
2594 return JS_SetUCProperty(cx, cpx->inner, JS_GetStringChars(str),
2595 JS_GetStringLength(str), vp);
2597 if (JSID_IS_INT(id))
2598 return JS_SetElement(cx, cpx->inner, JSID_TO_INT(id), vp);
2599 return JS_TRUE;
2602 return JS_TRUE;
2605 static JSBool
2606 split_delProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
2608 ComplexObject *cpx;
2609 jsid asId;
2611 cpx = split_get_private(cx, obj);
2612 if (!cpx)
2613 return JS_TRUE;
2614 if (!cpx->isInner && cpx->inner) {
2615 /* Make sure to define this property on the inner object. */
2616 if (!JS_ValueToId(cx, *vp, &asId))
2617 return JS_FALSE;
2618 return cpx->inner->deleteProperty(cx, asId, Valueify(vp));
2620 return JS_TRUE;
2623 static JSBool
2624 split_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
2625 jsval *statep, jsid *idp)
2627 ComplexObject *cpx;
2628 JSObject *iterator;
2630 switch (enum_op) {
2631 case JSENUMERATE_INIT:
2632 case JSENUMERATE_INIT_ALL:
2633 cpx = (ComplexObject *) JS_GetPrivate(cx, obj);
2635 if (!cpx->isInner && cpx->inner)
2636 obj = cpx->inner;
2638 iterator = JS_NewPropertyIterator(cx, obj);
2639 if (!iterator)
2640 return JS_FALSE;
2642 *statep = OBJECT_TO_JSVAL(iterator);
2643 if (idp)
2644 *idp = INT_TO_JSID(0);
2645 break;
2647 case JSENUMERATE_NEXT:
2648 iterator = (JSObject*)JSVAL_TO_OBJECT(*statep);
2649 if (!JS_NextProperty(cx, iterator, idp))
2650 return JS_FALSE;
2652 if (!JSID_IS_VOID(*idp))
2653 break;
2654 /* Fall through. */
2656 case JSENUMERATE_DESTROY:
2657 /* Let GC at our iterator object. */
2658 *statep = JSVAL_NULL;
2659 break;
2662 return JS_TRUE;
2665 static JSBool
2666 split_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject **objp)
2668 ComplexObject *cpx;
2670 if (JSID_IS_ATOM(id) &&
2671 !strcmp(JS_GetStringBytes(JSID_TO_STRING(id)), "isInner")) {
2672 *objp = obj;
2673 return JS_DefineProperty(cx, obj, "isInner", JSVAL_VOID, NULL, NULL,
2674 JSPROP_SHARED);
2677 cpx = split_get_private(cx, obj);
2678 if (!cpx)
2679 return JS_TRUE;
2680 if (!cpx->isInner && cpx->inner) {
2681 JSProperty *prop;
2683 if (!cpx->inner->lookupProperty(cx, id, objp, &prop))
2684 return JS_FALSE;
2685 if (prop)
2686 cpx->inner->dropProperty(cx, prop);
2688 return JS_TRUE;
2691 #ifdef LAZY_STANDARD_CLASSES
2692 if (!(flags & JSRESOLVE_ASSIGNING)) {
2693 JSBool resolved;
2695 if (!JS_ResolveStandardClass(cx, obj, id, &resolved))
2696 return JS_FALSE;
2698 if (resolved) {
2699 *objp = obj;
2700 return JS_TRUE;
2703 #endif
2705 /* XXX For additional realism, let's resolve some random property here. */
2706 return JS_TRUE;
2709 static void
2710 split_finalize(JSContext *cx, JSObject *obj)
2712 JS_free(cx, JS_GetPrivate(cx, obj));
2715 static uint32
2716 split_mark(JSContext *cx, JSObject *obj, void *arg)
2718 ComplexObject *cpx;
2720 cpx = (ComplexObject *) JS_GetPrivate(cx, obj);
2722 if (!cpx->isInner && cpx->inner) {
2723 /* Mark the inner object. */
2724 JS_MarkGCThing(cx, OBJECT_TO_JSVAL(cpx->inner), "ComplexObject.inner", arg);
2727 return 0;
2730 static JSObject *
2731 split_outerObject(JSContext *cx, JSObject *obj)
2733 ComplexObject *cpx;
2735 cpx = (ComplexObject *) JS_GetPrivate(cx, obj);
2736 return cpx->isInner ? cpx->outer : obj;
2739 static JSObject *
2740 split_thisObject(JSContext *cx, JSObject *obj)
2742 OBJ_TO_OUTER_OBJECT(cx, obj);
2743 if (!obj)
2744 return NULL;
2745 return obj;
2748 static JSObjectOps split_objectops;
2750 static JSObjectOps *
2751 split_getObjectOps(JSContext *cx, JSClass *clasp)
2753 if (!split_objectops.thisObject) {
2754 memcpy(&split_objectops, &js_ObjectOps, sizeof split_objectops);
2755 split_objectops.thisObject = split_thisObject;
2758 return &split_objectops;
2761 static JSBool
2762 split_equality(JSContext *cx, JSObject *obj, const jsval *v, JSBool *bp);
2764 static JSObject *
2765 split_innerObject(JSContext *cx, JSObject *obj)
2767 ComplexObject *cpx;
2769 cpx = (ComplexObject *) JS_GetPrivate(cx, obj);
2770 if (cpx->frozen) {
2771 JS_ASSERT(!cpx->isInner);
2772 return obj;
2774 return !cpx->isInner ? cpx->inner : obj;
2777 static JSExtendedClass split_global_class = {
2778 {"split_global",
2779 JSCLASS_NEW_RESOLVE | JSCLASS_NEW_ENUMERATE | JSCLASS_HAS_PRIVATE |
2780 JSCLASS_GLOBAL_FLAGS | JSCLASS_IS_EXTENDED,
2781 split_addProperty, split_delProperty,
2782 split_getProperty, split_setProperty,
2783 (JSEnumerateOp)split_enumerate,
2784 (JSResolveOp)split_resolve,
2785 JS_ConvertStub, split_finalize,
2786 split_getObjectOps, NULL, NULL, NULL, NULL, NULL,
2787 split_mark, NULL},
2788 split_equality, split_outerObject, split_innerObject,
2789 NULL, NULL, NULL, NULL, NULL
2792 static JSBool
2793 split_equality(JSContext *cx, JSObject *obj, const jsval *v, JSBool *bp)
2795 *bp = JS_FALSE;
2796 if (JSVAL_IS_PRIMITIVE(*v))
2797 return JS_TRUE;
2799 JSObject *obj2 = JSVAL_TO_OBJECT(*v);
2800 if (JS_GET_CLASS(cx, obj2) != &split_global_class.base)
2801 return JS_TRUE;
2803 ComplexObject *cpx = (ComplexObject *) JS_GetPrivate(cx, obj2);
2804 JS_ASSERT(!cpx->isInner);
2806 ComplexObject *ourCpx = (ComplexObject *) JS_GetPrivate(cx, obj);
2807 JS_ASSERT(!ourCpx->isInner);
2809 *bp = (cpx == ourCpx);
2810 return JS_TRUE;
2813 JSObject *
2814 split_create_outer(JSContext *cx)
2816 ComplexObject *cpx;
2817 JSObject *obj;
2819 cpx = (ComplexObject *) JS_malloc(cx, sizeof *obj);
2820 if (!cpx)
2821 return NULL;
2822 cpx->isInner = JS_FALSE;
2823 cpx->frozen = JS_TRUE;
2824 cpx->inner = NULL;
2825 cpx->outer = NULL;
2827 obj = JS_NewGlobalObject(cx, &split_global_class.base);
2828 if (!obj || !JS_SetParent(cx, obj, NULL)) {
2829 JS_free(cx, cpx);
2830 return NULL;
2833 if (!JS_SetPrivate(cx, obj, cpx)) {
2834 JS_free(cx, cpx);
2835 return NULL;
2838 return obj;
2841 static JSObject *
2842 split_create_inner(JSContext *cx, JSObject *outer)
2844 ComplexObject *cpx, *outercpx;
2845 JSObject *obj;
2847 JS_ASSERT(JS_GET_CLASS(cx, outer) == &split_global_class.base);
2849 cpx = (ComplexObject *) JS_malloc(cx, sizeof *cpx);
2850 if (!cpx)
2851 return NULL;
2852 cpx->isInner = JS_TRUE;
2853 cpx->frozen = JS_FALSE;
2854 cpx->inner = NULL;
2855 cpx->outer = outer;
2857 obj = JS_NewGlobalObject(cx, &split_global_class.base);
2858 if (!obj || !JS_SetParent(cx, obj, NULL) || !JS_SetPrivate(cx, obj, cpx)) {
2859 JS_free(cx, cpx);
2860 return NULL;
2863 outercpx = (ComplexObject *) JS_GetPrivate(cx, outer);
2864 outercpx->inner = obj;
2865 outercpx->frozen = JS_FALSE;
2867 return obj;
2870 static ComplexObject *
2871 split_get_private(JSContext *cx, JSObject *obj)
2873 do {
2874 if (JS_GET_CLASS(cx, obj) == &split_global_class.base)
2875 return (ComplexObject *) JS_GetPrivate(cx, obj);
2876 obj = JS_GetParent(cx, obj);
2877 } while (obj);
2879 return NULL;
2882 static JSBool
2883 sandbox_enumerate(JSContext *cx, JSObject *obj)
2885 jsval v;
2886 JSBool b;
2888 if (!JS_GetProperty(cx, obj, "lazy", &v))
2889 return JS_FALSE;
2891 JS_ValueToBoolean(cx, v, &b);
2892 return !b || JS_EnumerateStandardClasses(cx, obj);
2895 static JSBool
2896 sandbox_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
2897 JSObject **objp)
2899 jsval v;
2900 JSBool b, resolved;
2902 if (!JS_GetProperty(cx, obj, "lazy", &v))
2903 return JS_FALSE;
2905 JS_ValueToBoolean(cx, v, &b);
2906 if (b && (flags & JSRESOLVE_ASSIGNING) == 0) {
2907 if (!JS_ResolveStandardClass(cx, obj, id, &resolved))
2908 return JS_FALSE;
2909 if (resolved) {
2910 *objp = obj;
2911 return JS_TRUE;
2914 *objp = NULL;
2915 return JS_TRUE;
2918 static JSClass sandbox_class = {
2919 "sandbox",
2920 JSCLASS_NEW_RESOLVE | JSCLASS_GLOBAL_FLAGS,
2921 JS_PropertyStub, JS_PropertyStub,
2922 JS_PropertyStub, JS_PropertyStub,
2923 sandbox_enumerate, (JSResolveOp)sandbox_resolve,
2924 JS_ConvertStub, NULL,
2925 JSCLASS_NO_OPTIONAL_MEMBERS
2928 static JSObject *
2929 NewSandbox(JSContext *cx, bool lazy, bool split)
2931 JSObject *obj = JS_NewCompartmentAndGlobalObject(cx, &sandbox_class, NULL);
2932 if (!obj)
2933 return NULL;
2936 JSAutoCrossCompartmentCall ac;
2937 if (!ac.enter(cx, obj))
2938 return NULL;
2940 if (split) {
2941 obj = split_setup(cx, JS_TRUE);
2942 if (!obj)
2943 return NULL;
2945 if (!lazy && !JS_InitStandardClasses(cx, obj))
2946 return NULL;
2948 AutoValueRooter root(cx, BooleanValue(lazy));
2949 if (!JS_SetProperty(cx, obj, "lazy", root.jsval_addr()))
2950 return NULL;
2952 if (split)
2953 obj = split_outerObject(cx, obj);
2956 AutoObjectRooter objroot(cx, obj);
2957 if (!cx->compartment->wrap(cx, objroot.addr()))
2958 return NULL;
2959 return objroot.object();
2962 static JSBool
2963 EvalInContext(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
2964 jsval *rval)
2966 JSString *str;
2967 JSObject *sobj = NULL;
2968 if (!JS_ConvertArguments(cx, argc, argv, "S / o", &str, &sobj))
2969 return false;
2971 const jschar *src = JS_GetStringChars(str);
2972 size_t srclen = JS_GetStringLength(str);
2973 bool split = false, lazy = false;
2974 if (srclen == 4) {
2975 if (src[0] == 'l' && src[1] == 'a' && src[2] == 'z' && src[3] == 'y') {
2976 lazy = true;
2977 srclen = 0;
2979 } else if (srclen == 5) {
2980 if (src[0] == 's' && src[1] == 'p' && src[2] == 'l' && src[3] == 'i' && src[4] == 't') {
2981 split = lazy = true;
2982 srclen = 0;
2986 if (!sobj) {
2987 sobj = NewSandbox(cx, lazy, split);
2988 if (!sobj)
2989 return false;
2992 *rval = OBJECT_TO_JSVAL(sobj);
2993 if (srclen == 0)
2994 return true;
2996 JSStackFrame *fp = JS_GetScriptedCaller(cx, NULL);
2998 JSAutoCrossCompartmentCall ac;
2999 if (JSCrossCompartmentWrapper::isCrossCompartmentWrapper(sobj)) {
3000 sobj = sobj->unwrap();
3001 if (!ac.enter(cx, sobj))
3002 return false;
3005 OBJ_TO_INNER_OBJECT(cx, sobj);
3006 if (!sobj)
3007 return false;
3008 if (!(sobj->getClass()->flags & JSCLASS_IS_GLOBAL)) {
3009 JS_ReportError(cx, "Invalid scope argument to evalcx");
3010 return false;
3012 if (!JS_EvaluateUCScript(cx, sobj, src, srclen,
3013 fp->script->filename,
3014 JS_PCToLineNumber(cx, fp->script, fp->pc(cx)),
3015 rval)) {
3016 return false;
3019 return cx->compartment->wrap(cx, Valueify(rval));
3022 static JSBool
3023 EvalInFrame(JSContext *cx, uintN argc, jsval *vp)
3025 jsval *argv = JS_ARGV(cx, vp);
3026 if (argc < 2 ||
3027 !JSVAL_IS_INT(argv[0]) ||
3028 !JSVAL_IS_STRING(argv[1])) {
3029 JS_ReportError(cx, "Invalid arguments to evalInFrame");
3030 return JS_FALSE;
3033 uint32 upCount = JSVAL_TO_INT(argv[0]);
3034 JSString *str = JSVAL_TO_STRING(argv[1]);
3036 bool saveCurrent = (argc >= 3 && JSVAL_IS_BOOLEAN(argv[2]))
3037 ? !!(JSVAL_TO_BOOLEAN(argv[2]))
3038 : false;
3040 JS_ASSERT(cx->fp);
3042 FrameRegsIter fi(cx);
3043 for (uint32 i = 0; i < upCount; ++i, ++fi) {
3044 if (!fi.fp()->down)
3045 break;
3048 JSStackFrame *const fp = fi.fp();
3049 if (!fp->script) {
3050 JS_ReportError(cx, "cannot eval in non-script frame");
3051 return JS_FALSE;
3054 JSStackFrame *oldfp = NULL;
3055 if (saveCurrent)
3056 oldfp = JS_SaveFrameChain(cx);
3058 JSBool ok = JS_EvaluateUCInStackFrame(cx, fp, str->chars(), str->length(),
3059 fp->script->filename,
3060 JS_PCToLineNumber(cx, fp->script,
3061 fi.pc()),
3062 vp);
3064 if (saveCurrent)
3065 JS_RestoreFrameChain(cx, oldfp);
3067 return ok;
3070 static JSBool
3071 ShapeOf(JSContext *cx, uintN argc, jsval *vp)
3073 jsval v = JS_ARGV(cx, vp)[0];
3074 if (!JSVAL_IS_OBJECT(v)) {
3075 JS_ReportError(cx, "shapeOf: object expected");
3076 return JS_FALSE;
3078 JSObject *obj = JSVAL_TO_OBJECT(v);
3079 if (!obj) {
3080 *vp = JSVAL_ZERO;
3081 return JS_TRUE;
3083 if (!obj->isNative()) {
3084 *vp = INT_TO_JSVAL(-1);
3085 return JS_TRUE;
3087 return JS_NewNumberValue(cx, obj->shape(), vp);
3090 #ifdef JS_THREADSAFE
3093 * Check that t1 comes strictly before t2. The function correctly deals with
3094 * PRIntervalTime wrap-around between t2 and t1 assuming that t2 and t1 stays
3095 * within INT32_MAX from each other. We use MAX_TIMEOUT_INTERVAL to enforce
3096 * this restriction.
3098 static bool
3099 IsBefore(PRIntervalTime t1, PRIntervalTime t2)
3101 return int32(t1 - t2) < 0;
3104 static JSBool
3105 Sleep_fn(JSContext *cx, uintN argc, jsval *vp)
3107 PRIntervalTime t_ticks;
3109 if (argc == 0) {
3110 t_ticks = 0;
3111 } else {
3112 jsdouble t_secs;
3114 if (!JS_ValueToNumber(cx, argc == 0 ? JSVAL_VOID : vp[2], &t_secs))
3115 return JS_FALSE;
3117 /* NB: The next condition also filter out NaNs. */
3118 if (!(t_secs <= MAX_TIMEOUT_INTERVAL)) {
3119 JS_ReportError(cx, "Excessive sleep interval");
3120 return JS_FALSE;
3122 t_ticks = (t_secs <= 0.0)
3124 : PRIntervalTime(PR_TicksPerSecond() * t_secs);
3126 if (t_ticks == 0) {
3127 JS_YieldRequest(cx);
3128 } else {
3129 JSAutoSuspendRequest suspended(cx);
3130 PR_Lock(gWatchdogLock);
3131 PRIntervalTime to_wakeup = PR_IntervalNow() + t_ticks;
3132 for (;;) {
3133 PR_WaitCondVar(gSleepWakeup, t_ticks);
3134 if (gCanceled)
3135 break;
3136 PRIntervalTime now = PR_IntervalNow();
3137 if (!IsBefore(now, to_wakeup))
3138 break;
3139 t_ticks = to_wakeup - now;
3141 PR_Unlock(gWatchdogLock);
3143 return !gCanceled;
3146 typedef struct ScatterThreadData ScatterThreadData;
3147 typedef struct ScatterData ScatterData;
3149 typedef enum ScatterStatus {
3150 SCATTER_WAIT,
3151 SCATTER_GO,
3152 SCATTER_CANCEL
3153 } ScatterStatus;
3155 struct ScatterData {
3156 ScatterThreadData *threads;
3157 jsval *results;
3158 PRLock *lock;
3159 PRCondVar *cvar;
3160 ScatterStatus status;
3163 struct ScatterThreadData {
3164 jsint index;
3165 ScatterData *shared;
3166 PRThread *thr;
3167 JSContext *cx;
3168 jsval fn;
3171 static void
3172 DoScatteredWork(JSContext *cx, ScatterThreadData *td)
3174 jsval *rval = &td->shared->results[td->index];
3176 if (!JS_CallFunctionValue(cx, NULL, td->fn, 0, NULL, rval)) {
3177 *rval = JSVAL_VOID;
3178 JS_GetPendingException(cx, rval);
3179 JS_ClearPendingException(cx);
3183 static void
3184 RunScatterThread(void *arg)
3186 int stackDummy;
3187 ScatterThreadData *td;
3188 ScatterStatus st;
3189 JSContext *cx;
3191 if (PR_FAILURE == PR_SetThreadPrivate(gStackBaseThreadIndex, &stackDummy))
3192 return;
3194 td = (ScatterThreadData *)arg;
3195 cx = td->cx;
3197 /* Wait for our signal. */
3198 PR_Lock(td->shared->lock);
3199 while ((st = td->shared->status) == SCATTER_WAIT)
3200 PR_WaitCondVar(td->shared->cvar, PR_INTERVAL_NO_TIMEOUT);
3201 PR_Unlock(td->shared->lock);
3203 if (st == SCATTER_CANCEL)
3204 return;
3206 /* We are good to go. */
3207 JS_SetContextThread(cx);
3208 JS_SetNativeStackQuota(cx, gMaxStackSize);
3209 JS_BeginRequest(cx);
3210 DoScatteredWork(cx, td);
3211 JS_EndRequest(cx);
3212 JS_ClearContextThread(cx);
3216 * scatter(fnArray) - Call each function in `fnArray` without arguments, each
3217 * in a different thread. When all threads have finished, return an array: the
3218 * return values. Errors are not propagated; if any of the function calls
3219 * fails, the corresponding element in the results array gets the exception
3220 * object, if any, else (undefined).
3222 static JSBool
3223 Scatter(JSContext *cx, uintN argc, jsval *vp)
3225 jsuint i;
3226 jsuint n; /* number of threads */
3227 JSObject *inArr;
3228 JSObject *arr;
3229 JSObject *global;
3230 ScatterData sd;
3231 JSBool ok;
3233 sd.lock = NULL;
3234 sd.cvar = NULL;
3235 sd.results = NULL;
3236 sd.threads = NULL;
3237 sd.status = SCATTER_WAIT;
3239 if (argc == 0 || JSVAL_IS_PRIMITIVE(JS_ARGV(cx, vp)[0])) {
3240 JS_ReportError(cx, "the first argument must be an object");
3241 goto fail;
3244 inArr = JSVAL_TO_OBJECT(JS_ARGV(cx, vp)[0]);
3245 ok = JS_GetArrayLength(cx, inArr, &n);
3246 if (!ok)
3247 goto out;
3248 if (n == 0)
3249 goto success;
3251 sd.lock = PR_NewLock();
3252 if (!sd.lock)
3253 goto fail;
3255 sd.cvar = PR_NewCondVar(sd.lock);
3256 if (!sd.cvar)
3257 goto fail;
3259 sd.results = (jsval *) malloc(n * sizeof(jsval));
3260 if (!sd.results)
3261 goto fail;
3262 for (i = 0; i < n; i++) {
3263 sd.results[i] = JSVAL_VOID;
3264 ok = JS_AddValueRoot(cx, &sd.results[i]);
3265 if (!ok) {
3266 while (i-- > 0)
3267 JS_RemoveValueRoot(cx, &sd.results[i]);
3268 free(sd.results);
3269 sd.results = NULL;
3270 goto fail;
3274 sd.threads = (ScatterThreadData *) malloc(n * sizeof(ScatterThreadData));
3275 if (!sd.threads)
3276 goto fail;
3277 for (i = 0; i < n; i++) {
3278 sd.threads[i].index = i;
3279 sd.threads[i].shared = &sd;
3280 sd.threads[i].thr = NULL;
3281 sd.threads[i].cx = NULL;
3282 sd.threads[i].fn = JSVAL_NULL;
3284 ok = JS_AddValueRoot(cx, &sd.threads[i].fn);
3285 if (ok && !JS_GetElement(cx, inArr, (jsint) i, &sd.threads[i].fn)) {
3286 JS_RemoveValueRoot(cx, &sd.threads[i].fn);
3287 ok = JS_FALSE;
3289 if (!ok) {
3290 while (i-- > 0)
3291 JS_RemoveValueRoot(cx, &sd.threads[i].fn);
3292 free(sd.threads);
3293 sd.threads = NULL;
3294 goto fail;
3298 global = JS_GetGlobalObject(cx);
3299 for (i = 1; i < n; i++) {
3300 JSContext *newcx = NewContext(JS_GetRuntime(cx));
3301 if (!newcx)
3302 goto fail;
3305 JSAutoRequest req(newcx);
3306 JS_SetGlobalObject(newcx, global);
3308 JS_ClearContextThread(newcx);
3309 sd.threads[i].cx = newcx;
3312 for (i = 1; i < n; i++) {
3313 PRThread *t = PR_CreateThread(PR_USER_THREAD,
3314 RunScatterThread,
3315 &sd.threads[i],
3316 PR_PRIORITY_NORMAL,
3317 PR_GLOBAL_THREAD,
3318 PR_JOINABLE_THREAD,
3320 if (!t) {
3321 /* Failed to start thread. */
3322 PR_Lock(sd.lock);
3323 sd.status = SCATTER_CANCEL;
3324 PR_NotifyAllCondVar(sd.cvar);
3325 PR_Unlock(sd.lock);
3326 while (i-- > 1)
3327 PR_JoinThread(sd.threads[i].thr);
3328 goto fail;
3331 sd.threads[i].thr = t;
3333 PR_Lock(sd.lock);
3334 sd.status = SCATTER_GO;
3335 PR_NotifyAllCondVar(sd.cvar);
3336 PR_Unlock(sd.lock);
3338 DoScatteredWork(cx, &sd.threads[0]);
3341 JSAutoSuspendRequest suspended(cx);
3342 for (i = 1; i < n; i++) {
3343 PR_JoinThread(sd.threads[i].thr);
3347 success:
3348 arr = JS_NewArrayObject(cx, n, sd.results);
3349 if (!arr)
3350 goto fail;
3351 *vp = OBJECT_TO_JSVAL(arr);
3352 ok = JS_TRUE;
3354 out:
3355 if (sd.threads) {
3356 JSContext *acx;
3358 for (i = 0; i < n; i++) {
3359 JS_RemoveValueRoot(cx, &sd.threads[i].fn);
3360 acx = sd.threads[i].cx;
3361 if (acx) {
3362 JS_SetContextThread(acx);
3363 DestroyContext(acx, true);
3366 free(sd.threads);
3368 if (sd.results) {
3369 for (i = 0; i < n; i++)
3370 JS_RemoveValueRoot(cx, &sd.results[i]);
3371 free(sd.results);
3373 if (sd.cvar)
3374 PR_DestroyCondVar(sd.cvar);
3375 if (sd.lock)
3376 PR_DestroyLock(sd.lock);
3378 return ok;
3380 fail:
3381 ok = JS_FALSE;
3382 goto out;
3385 static bool
3386 InitWatchdog(JSRuntime *rt)
3388 JS_ASSERT(!gWatchdogThread);
3389 gWatchdogLock = PR_NewLock();
3390 if (gWatchdogLock) {
3391 gWatchdogWakeup = PR_NewCondVar(gWatchdogLock);
3392 if (gWatchdogWakeup) {
3393 gSleepWakeup = PR_NewCondVar(gWatchdogLock);
3394 if (gSleepWakeup)
3395 return true;
3396 PR_DestroyCondVar(gWatchdogWakeup);
3398 PR_DestroyLock(gWatchdogLock);
3400 return false;
3403 static void
3404 KillWatchdog()
3406 PRThread *thread;
3408 PR_Lock(gWatchdogLock);
3409 thread = gWatchdogThread;
3410 if (thread) {
3412 * The watchdog thread is running, tell it to terminate waking it up
3413 * if necessary.
3415 gWatchdogThread = NULL;
3416 PR_NotifyCondVar(gWatchdogWakeup);
3418 PR_Unlock(gWatchdogLock);
3419 if (thread)
3420 PR_JoinThread(thread);
3421 PR_DestroyCondVar(gSleepWakeup);
3422 PR_DestroyCondVar(gWatchdogWakeup);
3423 PR_DestroyLock(gWatchdogLock);
3426 static void
3427 WatchdogMain(void *arg)
3429 JSRuntime *rt = (JSRuntime *) arg;
3431 PR_Lock(gWatchdogLock);
3432 while (gWatchdogThread) {
3433 PRIntervalTime now = PR_IntervalNow();
3434 if (gWatchdogHasTimeout && !IsBefore(now, gWatchdogTimeout)) {
3436 * The timeout has just expired. Trigger the operation callback
3437 * outside the lock.
3439 gWatchdogHasTimeout = false;
3440 PR_Unlock(gWatchdogLock);
3441 CancelExecution(rt);
3442 PR_Lock(gWatchdogLock);
3444 /* Wake up any threads doing sleep. */
3445 PR_NotifyAllCondVar(gSleepWakeup);
3446 } else {
3447 PRIntervalTime sleepDuration = gWatchdogHasTimeout
3448 ? gWatchdogTimeout - now
3449 : PR_INTERVAL_NO_TIMEOUT;
3450 #ifdef DEBUG
3451 PRStatus status =
3452 #endif
3453 PR_WaitCondVar(gWatchdogWakeup, sleepDuration);
3454 JS_ASSERT(status == PR_SUCCESS);
3457 PR_Unlock(gWatchdogLock);
3460 static bool
3461 ScheduleWatchdog(JSRuntime *rt, jsdouble t)
3463 if (t <= 0) {
3464 PR_Lock(gWatchdogLock);
3465 gWatchdogHasTimeout = false;
3466 PR_Unlock(gWatchdogLock);
3467 return true;
3470 PRIntervalTime interval = PRIntervalTime(ceil(t * PR_TicksPerSecond()));
3471 PRIntervalTime timeout = PR_IntervalNow() + interval;
3472 PR_Lock(gWatchdogLock);
3473 if (!gWatchdogThread) {
3474 JS_ASSERT(!gWatchdogHasTimeout);
3475 gWatchdogThread = PR_CreateThread(PR_USER_THREAD,
3476 WatchdogMain,
3478 PR_PRIORITY_NORMAL,
3479 PR_LOCAL_THREAD,
3480 PR_JOINABLE_THREAD,
3482 if (!gWatchdogThread) {
3483 PR_Unlock(gWatchdogLock);
3484 return false;
3486 } else if (!gWatchdogHasTimeout || IsBefore(timeout, gWatchdogTimeout)) {
3487 PR_NotifyCondVar(gWatchdogWakeup);
3489 gWatchdogHasTimeout = true;
3490 gWatchdogTimeout = timeout;
3491 PR_Unlock(gWatchdogLock);
3492 return true;
3495 #else /* !JS_THREADSAFE */
3497 #ifdef XP_WIN
3498 static HANDLE gTimerHandle = 0;
3500 VOID CALLBACK
3501 TimerCallback(PVOID lpParameter, BOOLEAN TimerOrWaitFired)
3503 CancelExecution((JSRuntime *) lpParameter);
3506 #else
3508 static void
3509 AlarmHandler(int sig)
3511 CancelExecution(gRuntime);
3514 #endif
3516 static bool
3517 InitWatchdog(JSRuntime *rt)
3519 gRuntime = rt;
3520 return true;
3523 static void
3524 KillWatchdog()
3526 ScheduleWatchdog(gRuntime, -1);
3529 static bool
3530 ScheduleWatchdog(JSRuntime *rt, jsdouble t)
3532 #ifdef XP_WIN
3533 if (gTimerHandle) {
3534 DeleteTimerQueueTimer(NULL, gTimerHandle, NULL);
3535 gTimerHandle = 0;
3537 if (t > 0 &&
3538 !CreateTimerQueueTimer(&gTimerHandle,
3539 NULL,
3540 (WAITORTIMERCALLBACK)TimerCallback,
3542 DWORD(ceil(t * 1000.0)),
3544 WT_EXECUTEINTIMERTHREAD | WT_EXECUTEONLYONCE)) {
3545 gTimerHandle = 0;
3546 return false;
3548 #else
3549 /* FIXME: use setitimer when available for sub-second resolution. */
3550 if (t <= 0) {
3551 alarm(0);
3552 signal(SIGALRM, NULL);
3553 } else {
3554 signal(SIGALRM, AlarmHandler); /* set the Alarm signal capture */
3555 alarm(ceil(t));
3557 #endif
3558 return true;
3561 #endif /* !JS_THREADSAFE */
3563 static void
3564 CancelExecution(JSRuntime *rt)
3566 gCanceled = true;
3567 if (gExitCode == 0)
3568 gExitCode = EXITCODE_TIMEOUT;
3569 #ifdef JS_THREADSAFE
3570 if (gWorkers) {
3571 JSContext *cx = JS_NewContext(rt, 8192);
3572 if (cx) {
3573 js::workers::terminateAll(cx, gWorkers);
3574 JS_DestroyContextNoGC(cx);
3577 #endif
3578 JS_TriggerAllOperationCallbacks(rt);
3580 static const char msg[] = "Script runs for too long, terminating.\n";
3581 #if defined(XP_UNIX) && !defined(JS_THREADSAFE)
3582 /* It is not safe to call fputs from signals. */
3583 /* Dummy assignment avoids GCC warning on "attribute warn_unused_result" */
3584 ssize_t dummy = write(2, msg, sizeof(msg) - 1);
3585 (void)dummy;
3586 #else
3587 fputs(msg, stderr);
3588 #endif
3591 static JSBool
3592 SetTimeoutValue(JSContext *cx, jsdouble t)
3594 /* NB: The next condition also filter out NaNs. */
3595 if (!(t <= MAX_TIMEOUT_INTERVAL)) {
3596 JS_ReportError(cx, "Excessive timeout value");
3597 return JS_FALSE;
3599 gTimeoutInterval = t;
3600 if (!ScheduleWatchdog(cx->runtime, t)) {
3601 JS_ReportError(cx, "Failed to create the watchdog");
3602 return JS_FALSE;
3604 return JS_TRUE;
3607 static JSBool
3608 Timeout(JSContext *cx, uintN argc, jsval *vp)
3610 if (argc == 0)
3611 return JS_NewNumberValue(cx, gTimeoutInterval, vp);
3613 if (argc > 1) {
3614 JS_ReportError(cx, "Wrong number of arguments");
3615 return JS_FALSE;
3618 jsdouble t;
3619 if (!JS_ValueToNumber(cx, JS_ARGV(cx, vp)[0], &t))
3620 return JS_FALSE;
3622 *vp = JSVAL_VOID;
3623 return SetTimeoutValue(cx, t);
3626 static JSBool
3627 Elapsed(JSContext *cx, uintN argc, jsval *vp)
3629 if (argc == 0) {
3630 double d = 0.0;
3631 JSShellContextData *data = GetContextData(cx);
3632 if (data)
3633 d = js_IntervalNow() - data->startTime;
3634 return JS_NewNumberValue(cx, d, vp);
3636 JS_ReportError(cx, "Wrong number of arguments");
3637 return JS_FALSE;
3640 static JSBool
3641 Parent(JSContext *cx, uintN argc, jsval *vp)
3643 if (argc != 1) {
3644 JS_ReportError(cx, "Wrong number of arguments");
3645 return JS_FALSE;
3648 jsval v = JS_ARGV(cx, vp)[0];
3649 if (JSVAL_IS_PRIMITIVE(v)) {
3650 JS_ReportError(cx, "Only objects have parents!");
3651 return JS_FALSE;
3654 JSObject *parent = JS_GetParent(cx, JSVAL_TO_OBJECT(v));
3655 *vp = OBJECT_TO_JSVAL(parent);
3657 /* Outerize if necessary. Embrace the ugliness! */
3658 if (parent) {
3659 JSClass *clasp = JS_GET_CLASS(cx, parent);
3660 if (clasp->flags & JSCLASS_IS_EXTENDED) {
3661 JSExtendedClass *xclasp = reinterpret_cast<JSExtendedClass *>(clasp);
3662 if (JSObjectOp outerize = xclasp->outerObject)
3663 *vp = OBJECT_TO_JSVAL(outerize(cx, parent));
3667 return JS_TRUE;
3670 #ifdef XP_UNIX
3672 #include <fcntl.h>
3673 #include <sys/stat.h>
3676 * Returns a JS_malloc'd string (that the caller needs to JS_free)
3677 * containing the directory (non-leaf) part of |from| prepended to |leaf|.
3678 * If |from| is empty or a leaf, MakeAbsolutePathname returns a copy of leaf.
3679 * Returns NULL to indicate an error.
3681 static char *
3682 MakeAbsolutePathname(JSContext *cx, const char *from, const char *leaf)
3684 size_t dirlen;
3685 char *dir;
3686 const char *slash = NULL, *cp;
3688 cp = from;
3689 while (*cp) {
3690 if (*cp == '/') {
3691 slash = cp;
3694 ++cp;
3697 if (!slash) {
3698 /* We were given a leaf or |from| was empty. */
3699 return JS_strdup(cx, leaf);
3702 /* Else, we were given a real pathname, return that + the leaf. */
3703 dirlen = slash - from + 1;
3704 dir = (char*) JS_malloc(cx, dirlen + strlen(leaf) + 1);
3705 if (!dir)
3706 return NULL;
3708 strncpy(dir, from, dirlen);
3709 strcpy(dir + dirlen, leaf); /* Note: we can't use strcat here. */
3711 return dir;
3714 #endif // XP_UNIX
3716 static JSBool
3717 Compile(JSContext *cx, uintN argc, jsval *vp)
3719 if (argc < 1) {
3720 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
3721 "compile", "0", "s");
3722 return JS_FALSE;
3724 jsval arg0 = JS_ARGV(cx, vp)[0];
3725 if (!JSVAL_IS_STRING(arg0)) {
3726 const char *typeName = JS_GetTypeName(cx, JS_TypeOfValue(cx, arg0));
3727 JS_ReportError(cx, "expected string to compile, got %s", typeName);
3728 return JS_FALSE;
3731 JSString *scriptContents = JSVAL_TO_STRING(arg0);
3732 JSScript *result = JS_CompileUCScript(cx, NULL, JS_GetStringCharsZ(cx, scriptContents),
3733 JS_GetStringLength(scriptContents), "<string>", 0);
3734 if (!result)
3735 return JS_FALSE;
3737 JS_DestroyScript(cx, result);
3738 JS_SET_RVAL(cx, vp, JSVAL_VOID);
3739 return JS_TRUE;
3742 static JSBool
3743 Parse(JSContext *cx, uintN argc, jsval *vp)
3745 if (argc < 1) {
3746 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
3747 "compile", "0", "s");
3748 return JS_FALSE;
3750 jsval arg0 = JS_ARGV(cx, vp)[0];
3751 if (!JSVAL_IS_STRING(arg0)) {
3752 const char *typeName = JS_GetTypeName(cx, JS_TypeOfValue(cx, arg0));
3753 JS_ReportError(cx, "expected string to parse, got %s", typeName);
3754 return JS_FALSE;
3757 JSString *scriptContents = JSVAL_TO_STRING(arg0);
3758 js::Parser parser(cx);
3759 parser.init(JS_GetStringCharsZ(cx, scriptContents), JS_GetStringLength(scriptContents),
3760 NULL, "<string>", 0);
3761 if (!parser.parse(NULL))
3762 return JS_FALSE;
3763 JS_SET_RVAL(cx, vp, JSVAL_VOID);
3764 return JS_TRUE;
3767 static JSBool
3768 Snarf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
3770 JSString *str;
3771 const char *filename;
3772 const char *pathname;
3773 JSStackFrame *fp;
3774 JSBool ok;
3775 size_t cc, len;
3776 char *buf;
3777 FILE *file;
3779 str = JS_ValueToString(cx, argv[0]);
3780 if (!str)
3781 return JS_FALSE;
3782 filename = JS_GetStringBytes(str);
3784 /* Get the currently executing script's name. */
3785 fp = JS_GetScriptedCaller(cx, NULL);
3786 JS_ASSERT(fp && fp->script->filename);
3787 #ifdef XP_UNIX
3788 pathname = MakeAbsolutePathname(cx, fp->script->filename, filename);
3789 if (!pathname)
3790 return JS_FALSE;
3791 #else
3792 pathname = filename;
3793 #endif
3795 ok = JS_FALSE;
3796 len = 0;
3797 buf = NULL;
3798 file = fopen(pathname, "rb");
3799 if (!file) {
3800 JS_ReportError(cx, "can't open %s: %s", pathname, strerror(errno));
3801 } else {
3802 if (fseek(file, 0, SEEK_END) == EOF) {
3803 JS_ReportError(cx, "can't seek end of %s", pathname);
3804 } else {
3805 len = ftell(file);
3806 if (fseek(file, 0, SEEK_SET) == EOF) {
3807 JS_ReportError(cx, "can't seek start of %s", pathname);
3808 } else {
3809 buf = (char*) JS_malloc(cx, len + 1);
3810 if (buf) {
3811 cc = fread(buf, 1, len, file);
3812 if (cc != len) {
3813 JS_ReportError(cx, "can't read %s: %s", pathname,
3814 (ptrdiff_t(cc) < 0) ? strerror(errno) : "short read");
3815 } else {
3816 len = (size_t)cc;
3817 ok = JS_TRUE;
3822 fclose(file);
3824 JS_free(cx, (void*)pathname);
3825 if (!ok) {
3826 JS_free(cx, buf);
3827 return ok;
3830 buf[len] = '\0';
3831 str = JS_NewString(cx, buf, len);
3832 if (!str) {
3833 JS_free(cx, buf);
3834 return JS_FALSE;
3836 *rval = STRING_TO_JSVAL(str);
3837 return JS_TRUE;
3840 JSBool
3841 Wrap(JSContext *cx, uintN argc, jsval *vp)
3843 jsval v = argc > 0 ? JS_ARGV(cx, vp)[0] : JSVAL_VOID;
3844 if (JSVAL_IS_PRIMITIVE(v)) {
3845 JS_SET_RVAL(cx, vp, v);
3846 return true;
3849 JSObject *obj = JSVAL_TO_OBJECT(v);
3850 JSObject *wrapped = JSWrapper::New(cx, obj, obj->getProto(), obj->getParent(),
3851 &JSWrapper::singleton);
3852 if (!wrapped)
3853 return false;
3855 JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(wrapped));
3856 return 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_FN("wrap", Wrap, 1,0),
3944 JS_FS_END
3947 static const char shell_help_header[] =
3948 "Command Description\n"
3949 "======= ===========\n";
3951 static const char *const shell_help_messages[] = {
3952 "version([number]) Get or set JavaScript version number",
3953 "options([option ...]) Get or toggle JavaScript options",
3954 "load(['foo.js' ...]) Load files named by string arguments",
3955 "readline() Read a single line from stdin",
3956 "print([exp ...]) Evaluate and print expressions",
3957 "help([name ...]) Display usage and help messages",
3958 "quit() Quit the shell",
3959 "assertEq(actual, expected[, msg])\n"
3960 " Throw if the first two arguments are not the same (both +0 or both -0,\n"
3961 " both NaN, or non-zero and ===)",
3962 "gc() Run the garbage collector",
3963 #ifdef JS_GCMETER
3964 "gcstats() Print garbage collector statistics",
3965 #endif
3966 "gcparam(name, value)\n"
3967 " Wrapper for JS_SetGCParameter. The name must be either 'maxBytes' or\n"
3968 " 'maxMallocBytes' and the value must be convertable to a positive uint32",
3969 "countHeap([start[, kind]])\n"
3970 " Count the number of live GC things in the heap or things reachable from\n"
3971 " start when it is given and is not null. kind is either 'all' (default) to\n"
3972 " count all things or one of 'object', 'double', 'string', 'function',\n"
3973 " 'qname', 'namespace', 'xml' to count only things of that kind",
3974 #ifdef JS_GC_ZEAL
3975 "gczeal(level) How zealous the garbage collector should be",
3976 #endif
3977 "trap([fun, [pc,]] exp) Trap bytecode execution",
3978 "untrap(fun[, pc]) Remove a trap",
3979 "line2pc([fun,] line) Map line number to PC",
3980 "pc2line(fun[, pc]) Map PC to line number",
3981 "stackQuota([number]) Query/set script stack quota",
3982 "stringsAreUTF8() Check if strings are UTF-8 encoded",
3983 "testUTF8(mode) Perform UTF-8 tests (modes are 1 to 4)",
3984 "throwError() Throw an error from JS_ReportError",
3985 #ifdef DEBUG
3986 "dis([fun]) Disassemble functions into bytecodes\n"
3987 "dis('-r', fun) Disassembles recursively",
3988 "disfile('foo.js') Disassemble script file into bytecodes",
3989 "dissrc([fun]) Disassemble functions with source lines",
3990 "dumpHeap([fileName[, start[, toFind[, maxDepth[, toIgnore]]]]])\n"
3991 " Interface to JS_DumpHeap with output sent to file",
3992 "notes([fun]) Show source notes for functions",
3993 "tracing([true|false|filename]) Turn bytecode execution tracing on/off.\n"
3994 " With filename, send to file.\n",
3995 "stats([string ...]) Dump 'arena', 'atom', 'global' stats",
3996 #endif
3997 #ifdef TEST_CVTARGS
3998 "cvtargs(arg1..., arg12) Test argument formatter",
3999 #endif
4000 "build() Show build date and time",
4001 "clear([obj]) Clear properties of object",
4002 "intern(str) Internalize str in the atom table",
4003 "clone(fun[, scope]) Clone function object",
4004 "seal(obj[, deep]) Seal object, or object graph if deep",
4005 "getpda(obj) Get the property descriptors for obj",
4006 "getslx(obj) Get script line extent",
4007 "toint32(n) Testing hook for JS_ValueToInt32",
4008 "evalcx(s[, o])\n"
4009 " Evaluate s in optional sandbox object o\n"
4010 " if (s == '' && !o) return new o with eager standard classes\n"
4011 " if (s == 'lazy' && !o) return new o with lazy standard classes\n"
4012 " if (s == 'split' && !o) return new split-object o with lazy standard classes",
4013 "evalInFrame(n,str,save) Evaluate 'str' in the nth up frame.\n"
4014 " If 'save' (default false), save the frame chain",
4015 "shapeOf(obj) Get the shape of obj (an implementation detail)",
4016 #ifdef MOZ_SHARK
4017 "startShark() Start a Shark session.\n"
4018 " Shark must be running with programatic sampling",
4019 "stopShark() Stop a running Shark session",
4020 "connectShark() Connect to Shark.\n"
4021 " The -k switch does this automatically",
4022 "disconnectShark() Disconnect from Shark",
4023 #endif
4024 #ifdef MOZ_CALLGRIND
4025 "startCallgrind() Start callgrind instrumentation",
4026 "stopCallgrind() Stop callgrind instrumentation",
4027 "dumpCallgrind([name]) Dump callgrind counters",
4028 #endif
4029 #ifdef MOZ_VTUNE
4030 "startVtune([filename]) Start vtune instrumentation",
4031 "stopVtune() Stop vtune instrumentation",
4032 "pauseVtune() Pause vtune collection",
4033 "resumeVtune() Resume vtune collection",
4034 #endif
4035 #ifdef MOZ_TRACEVIS
4036 "startTraceVis(filename) Start TraceVis recording (stops any current recording)",
4037 "stopTraceVis() Stop TraceVis recording",
4038 #endif
4039 #ifdef DEBUG_ARRAYS
4040 "arrayInfo(a1, a2, ...) Report statistics about arrays",
4041 #endif
4042 #ifdef JS_THREADSAFE
4043 "sleep(dt) Sleep for dt seconds",
4044 "scatter(fns) Call functions concurrently (ignoring errors)",
4045 #endif
4046 "snarf(filename) Read filename into returned string",
4047 "compile(code) Compiles a string to bytecode, potentially throwing",
4048 "parse(code) Parses a string, potentially throwing",
4049 "timeout([seconds])\n"
4050 " Get/Set the limit in seconds for the execution time for the current context.\n"
4051 " A negative value (default) means that the execution time is unlimited.",
4052 "elapsed() Execution time elapsed for the current context.",
4053 "parent(obj) Returns the parent of obj.\n",
4054 "wrap(obj) Wrap an object into a noop wrapper.\n"
4057 /* Help messages must match shell functions. */
4058 JS_STATIC_ASSERT(JS_ARRAY_LENGTH(shell_help_messages) + 1 ==
4059 JS_ARRAY_LENGTH(shell_functions));
4061 #ifdef DEBUG
4062 static void
4063 CheckHelpMessages()
4065 const char *const *m;
4066 const char *lp;
4068 /* Each message must begin with "function_name(" prefix. */
4069 for (m = shell_help_messages; m != JS_ARRAY_END(shell_help_messages); ++m) {
4070 lp = strchr(*m, '(');
4071 JS_ASSERT(lp);
4072 JS_ASSERT(memcmp(shell_functions[m - shell_help_messages].name,
4073 *m, lp - *m) == 0);
4076 #else
4077 # define CheckHelpMessages() ((void) 0)
4078 #endif
4080 static JSBool
4081 Help(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
4083 uintN i, j;
4084 int did_header, did_something;
4085 JSType type;
4086 JSFunction *fun;
4087 JSString *str;
4088 const char *bytes;
4090 fprintf(gOutFile, "%s\n", JS_GetImplementationVersion());
4091 if (argc == 0) {
4092 fputs(shell_help_header, gOutFile);
4093 for (i = 0; shell_functions[i].name; i++)
4094 fprintf(gOutFile, "%s\n", shell_help_messages[i]);
4095 } else {
4096 did_header = 0;
4097 for (i = 0; i < argc; i++) {
4098 did_something = 0;
4099 type = JS_TypeOfValue(cx, argv[i]);
4100 if (type == JSTYPE_FUNCTION) {
4101 fun = JS_ValueToFunction(cx, argv[i]);
4102 str = fun->atom ? ATOM_TO_STRING(fun->atom) : NULL;
4103 } else if (type == JSTYPE_STRING) {
4104 str = JSVAL_TO_STRING(argv[i]);
4105 } else {
4106 str = NULL;
4108 if (str) {
4109 bytes = JS_GetStringBytes(str);
4110 for (j = 0; shell_functions[j].name; j++) {
4111 if (!strcmp(bytes, shell_functions[j].name)) {
4112 if (!did_header) {
4113 did_header = 1;
4114 fputs(shell_help_header, gOutFile);
4116 did_something = 1;
4117 fprintf(gOutFile, "%s\n", shell_help_messages[j]);
4118 break;
4122 if (!did_something) {
4123 str = JS_ValueToString(cx, argv[i]);
4124 if (!str)
4125 return JS_FALSE;
4126 fprintf(gErrFile, "Sorry, no help for %s\n",
4127 JS_GetStringBytes(str));
4131 return JS_TRUE;
4134 static JSObject *
4135 split_setup(JSContext *cx, JSBool evalcx)
4137 JSObject *outer, *inner, *arguments;
4139 outer = split_create_outer(cx);
4140 if (!outer)
4141 return NULL;
4142 AutoObjectRooter root(cx, outer);
4143 if (!evalcx)
4144 JS_SetGlobalObject(cx, outer);
4146 inner = split_create_inner(cx, outer);
4147 if (!inner)
4148 return NULL;
4150 if (!evalcx) {
4151 if (!JS_DefineFunctions(cx, inner, shell_functions))
4152 return NULL;
4154 /* Create a dummy arguments object. */
4155 arguments = JS_NewArrayObject(cx, 0, NULL);
4156 if (!arguments ||
4157 !JS_DefineProperty(cx, inner, "arguments", OBJECT_TO_JSVAL(arguments),
4158 NULL, NULL, 0)) {
4159 return NULL;
4163 JS_ClearScope(cx, outer);
4165 #ifndef LAZY_STANDARD_CLASSES
4166 if (!JS_InitStandardClasses(cx, inner))
4167 return NULL;
4168 #endif
4170 return inner;
4174 * Define a JS object called "it". Give it class operations that printf why
4175 * they're being called for tutorial purposes.
4177 enum its_tinyid {
4178 ITS_COLOR, ITS_HEIGHT, ITS_WIDTH, ITS_FUNNY, ITS_ARRAY, ITS_RDONLY,
4179 ITS_CUSTOM, ITS_CUSTOMRDONLY
4182 static JSBool
4183 its_getter(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
4185 jsval *val = (jsval *) JS_GetPrivate(cx, obj);
4186 *vp = val ? *val : JSVAL_VOID;
4187 return JS_TRUE;
4190 static JSBool
4191 its_setter(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
4193 jsval *val = (jsval *) JS_GetPrivate(cx, obj);
4194 if (val) {
4195 *val = *vp;
4196 return JS_TRUE;
4199 val = new jsval;
4200 if (!val) {
4201 JS_ReportOutOfMemory(cx);
4202 return JS_FALSE;
4205 if (!JS_AddValueRoot(cx, val)) {
4206 delete val;
4207 return JS_FALSE;
4210 if (!JS_SetPrivate(cx, obj, (void*)val)) {
4211 JS_RemoveValueRoot(cx, val);
4212 delete val;
4213 return JS_FALSE;
4216 *val = *vp;
4217 return JS_TRUE;
4220 static JSPropertySpec its_props[] = {
4221 {"color", ITS_COLOR, JSPROP_ENUMERATE, NULL, NULL},
4222 {"height", ITS_HEIGHT, JSPROP_ENUMERATE, NULL, NULL},
4223 {"width", ITS_WIDTH, JSPROP_ENUMERATE, NULL, NULL},
4224 {"funny", ITS_FUNNY, JSPROP_ENUMERATE, NULL, NULL},
4225 {"array", ITS_ARRAY, JSPROP_ENUMERATE, NULL, NULL},
4226 {"rdonly", ITS_RDONLY, JSPROP_READONLY, NULL, NULL},
4227 {"custom", ITS_CUSTOM, JSPROP_ENUMERATE,
4228 its_getter, its_setter},
4229 {"customRdOnly", ITS_CUSTOMRDONLY, JSPROP_ENUMERATE | JSPROP_READONLY,
4230 its_getter, its_setter},
4231 {NULL,0,0,NULL,NULL}
4234 static JSBool
4235 its_bindMethod(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
4236 jsval *rval)
4238 char *name;
4239 JSObject *method;
4241 if (!JS_ConvertArguments(cx, argc, argv, "so", &name, &method))
4242 return JS_FALSE;
4244 *rval = OBJECT_TO_JSVAL(method);
4246 if (JS_TypeOfValue(cx, *rval) != JSTYPE_FUNCTION) {
4247 JSString *valstr = JS_ValueToString(cx, *rval);
4248 if (valstr) {
4249 JS_ReportError(cx, "can't bind method %s to non-callable object %s",
4250 name, JS_GetStringBytes(valstr));
4252 return JS_FALSE;
4255 if (!JS_DefineProperty(cx, obj, name, *rval, NULL, NULL, JSPROP_ENUMERATE))
4256 return JS_FALSE;
4258 return JS_SetParent(cx, method, obj);
4261 static JSFunctionSpec its_methods[] = {
4262 {"bindMethod", its_bindMethod, 2,0,0},
4263 {NULL,NULL,0,0,0}
4266 #ifdef JSD_LOWLEVEL_SOURCE
4268 * This facilitates sending source to JSD (the debugger system) in the shell
4269 * where the source is loaded using the JSFILE hack in jsscan. The function
4270 * below is used as a callback for the jsdbgapi JS_SetSourceHandler hook.
4271 * A more normal embedding (e.g. mozilla) loads source itself and can send
4272 * source directly to JSD without using this hook scheme.
4274 static void
4275 SendSourceToJSDebugger(const char *filename, uintN lineno,
4276 jschar *str, size_t length,
4277 void **listenerTSData, JSDContext* jsdc)
4279 JSDSourceText *jsdsrc = (JSDSourceText *) *listenerTSData;
4281 if (!jsdsrc) {
4282 if (!filename)
4283 filename = "typein";
4284 if (1 == lineno) {
4285 jsdsrc = JSD_NewSourceText(jsdc, filename);
4286 } else {
4287 jsdsrc = JSD_FindSourceForURL(jsdc, filename);
4288 if (jsdsrc && JSD_SOURCE_PARTIAL !=
4289 JSD_GetSourceStatus(jsdc, jsdsrc)) {
4290 jsdsrc = NULL;
4294 if (jsdsrc) {
4295 jsdsrc = JSD_AppendUCSourceText(jsdc,jsdsrc, str, length,
4296 JSD_SOURCE_PARTIAL);
4298 *listenerTSData = jsdsrc;
4300 #endif /* JSD_LOWLEVEL_SOURCE */
4302 static JSBool its_noisy; /* whether to be noisy when finalizing it */
4303 static JSBool its_enum_fail;/* whether to fail when enumerating it */
4305 static JSBool
4306 its_addProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
4308 if (!its_noisy)
4309 return JS_TRUE;
4311 IdToString idString(cx, id);
4312 fprintf(gOutFile, "adding its property %s,", idString.getBytes());
4313 ToString valueString(cx, *vp);
4314 fprintf(gOutFile, " initial value %s\n", valueString.getBytes());
4315 return JS_TRUE;
4318 static JSBool
4319 its_delProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
4321 if (!its_noisy)
4322 return JS_TRUE;
4324 IdToString idString(cx, id);
4325 fprintf(gOutFile, "deleting its property %s,", idString.getBytes());
4326 ToString valueString(cx, *vp);
4327 fprintf(gOutFile, " initial value %s\n", valueString.getBytes());
4328 return JS_TRUE;
4331 static JSBool
4332 its_getProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
4334 if (!its_noisy)
4335 return JS_TRUE;
4337 IdToString idString(cx, id);
4338 fprintf(gOutFile, "getting its property %s,", idString.getBytes());
4339 ToString valueString(cx, *vp);
4340 fprintf(gOutFile, " initial value %s\n", valueString.getBytes());
4341 return JS_TRUE;
4344 static JSBool
4345 its_setProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
4347 IdToString idString(cx, id);
4348 if (its_noisy) {
4349 fprintf(gOutFile, "setting its property %s,", idString.getBytes());
4350 ToString valueString(cx, *vp);
4351 fprintf(gOutFile, " new value %s\n", valueString.getBytes());
4354 if (!JSID_IS_ATOM(id))
4355 return JS_TRUE;
4357 if (!strcmp(idString.getBytes(), "noisy"))
4358 JS_ValueToBoolean(cx, *vp, &its_noisy);
4359 else if (!strcmp(idString.getBytes(), "enum_fail"))
4360 JS_ValueToBoolean(cx, *vp, &its_enum_fail);
4362 return JS_TRUE;
4366 * Its enumerator, implemented using the "new" enumerate API,
4367 * see class flags.
4369 static JSBool
4370 its_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
4371 jsval *statep, jsid *idp)
4373 JSObject *iterator;
4375 switch (enum_op) {
4376 case JSENUMERATE_INIT:
4377 case JSENUMERATE_INIT_ALL:
4378 if (its_noisy)
4379 fprintf(gOutFile, "enumerate its properties\n");
4381 iterator = JS_NewPropertyIterator(cx, obj);
4382 if (!iterator)
4383 return JS_FALSE;
4385 *statep = OBJECT_TO_JSVAL(iterator);
4386 if (idp)
4387 *idp = INT_TO_JSID(0);
4388 break;
4390 case JSENUMERATE_NEXT:
4391 if (its_enum_fail) {
4392 JS_ReportError(cx, "its enumeration failed");
4393 return JS_FALSE;
4396 iterator = (JSObject *) JSVAL_TO_OBJECT(*statep);
4397 if (!JS_NextProperty(cx, iterator, idp))
4398 return JS_FALSE;
4400 if (!JSID_IS_VOID(*idp))
4401 break;
4402 /* Fall through. */
4404 case JSENUMERATE_DESTROY:
4405 /* Allow our iterator object to be GC'd. */
4406 *statep = JSVAL_NULL;
4407 break;
4410 return JS_TRUE;
4413 static JSBool
4414 its_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
4415 JSObject **objp)
4417 if (its_noisy) {
4418 IdToString idString(cx, id);
4419 fprintf(gOutFile, "resolving its property %s, flags {%s,%s,%s}\n",
4420 idString.getBytes(),
4421 (flags & JSRESOLVE_QUALIFIED) ? "qualified" : "",
4422 (flags & JSRESOLVE_ASSIGNING) ? "assigning" : "",
4423 (flags & JSRESOLVE_DETECTING) ? "detecting" : "");
4425 return JS_TRUE;
4428 static JSBool
4429 its_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
4431 if (its_noisy)
4432 fprintf(gOutFile, "converting it to %s type\n", JS_GetTypeName(cx, type));
4433 return JS_TRUE;
4436 static void
4437 its_finalize(JSContext *cx, JSObject *obj)
4439 jsval *rootedVal;
4440 if (its_noisy)
4441 fprintf(gOutFile, "finalizing it\n");
4442 rootedVal = (jsval *) JS_GetPrivate(cx, obj);
4443 if (rootedVal) {
4444 JS_RemoveValueRoot(cx, rootedVal);
4445 JS_SetPrivate(cx, obj, NULL);
4446 delete rootedVal;
4450 static JSClass its_class = {
4451 "It", JSCLASS_NEW_RESOLVE | JSCLASS_NEW_ENUMERATE | JSCLASS_HAS_PRIVATE,
4452 its_addProperty, its_delProperty, its_getProperty, its_setProperty,
4453 (JSEnumerateOp)its_enumerate, (JSResolveOp)its_resolve,
4454 its_convert, its_finalize,
4455 JSCLASS_NO_OPTIONAL_MEMBERS
4458 JSErrorFormatString jsShell_ErrorFormatString[JSErr_Limit] = {
4459 #define MSG_DEF(name, number, count, exception, format) \
4460 { format, count, JSEXN_ERR } ,
4461 #include "jsshell.msg"
4462 #undef MSG_DEF
4465 static const JSErrorFormatString *
4466 my_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber)
4468 if ((errorNumber > 0) && (errorNumber < JSShellErr_Limit))
4469 return &jsShell_ErrorFormatString[errorNumber];
4470 return NULL;
4473 static void
4474 my_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
4476 int i, j, k, n;
4477 char *prefix, *tmp;
4478 const char *ctmp;
4480 if (!report) {
4481 fprintf(gErrFile, "%s\n", message);
4482 return;
4485 /* Conditionally ignore reported warnings. */
4486 if (JSREPORT_IS_WARNING(report->flags) && !reportWarnings)
4487 return;
4489 prefix = NULL;
4490 if (report->filename)
4491 prefix = JS_smprintf("%s:", report->filename);
4492 if (report->lineno) {
4493 tmp = prefix;
4494 prefix = JS_smprintf("%s%u: ", tmp ? tmp : "", report->lineno);
4495 JS_free(cx, tmp);
4497 if (JSREPORT_IS_WARNING(report->flags)) {
4498 tmp = prefix;
4499 prefix = JS_smprintf("%s%swarning: ",
4500 tmp ? tmp : "",
4501 JSREPORT_IS_STRICT(report->flags) ? "strict " : "");
4502 JS_free(cx, tmp);
4505 /* embedded newlines -- argh! */
4506 while ((ctmp = strchr(message, '\n')) != 0) {
4507 ctmp++;
4508 if (prefix)
4509 fputs(prefix, gErrFile);
4510 fwrite(message, 1, ctmp - message, gErrFile);
4511 message = ctmp;
4514 /* If there were no filename or lineno, the prefix might be empty */
4515 if (prefix)
4516 fputs(prefix, gErrFile);
4517 fputs(message, gErrFile);
4519 if (!report->linebuf) {
4520 fputc('\n', gErrFile);
4521 goto out;
4524 /* report->linebuf usually ends with a newline. */
4525 n = strlen(report->linebuf);
4526 fprintf(gErrFile, ":\n%s%s%s%s",
4527 prefix,
4528 report->linebuf,
4529 (n > 0 && report->linebuf[n-1] == '\n') ? "" : "\n",
4530 prefix);
4531 n = report->tokenptr - report->linebuf;
4532 for (i = j = 0; i < n; i++) {
4533 if (report->linebuf[i] == '\t') {
4534 for (k = (j + 8) & ~7; j < k; j++) {
4535 fputc('.', gErrFile);
4537 continue;
4539 fputc('.', gErrFile);
4540 j++;
4542 fputs("^\n", gErrFile);
4543 out:
4544 if (!JSREPORT_IS_WARNING(report->flags)) {
4545 if (report->errorNumber == JSMSG_OUT_OF_MEMORY) {
4546 gExitCode = EXITCODE_OUT_OF_MEMORY;
4547 } else {
4548 gExitCode = EXITCODE_RUNTIME_ERROR;
4551 JS_free(cx, prefix);
4554 #if defined(SHELL_HACK) && defined(DEBUG) && defined(XP_UNIX)
4555 static JSBool
4556 Exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
4558 JSFunction *fun;
4559 const char *name, **nargv;
4560 uintN i, nargc;
4561 JSString *str;
4562 pid_t pid;
4563 int status;
4565 fun = JS_ValueToFunction(cx, argv[-2]);
4566 if (!fun)
4567 return JS_FALSE;
4568 if (!fun->atom)
4569 return JS_TRUE;
4570 name = JS_GetStringBytes(ATOM_TO_STRING(fun->atom));
4571 nargc = 1 + argc;
4572 nargv = JS_malloc(cx, (nargc + 1) * sizeof(char *));
4573 if (!nargv)
4574 return JS_FALSE;
4575 nargv[0] = name;
4576 for (i = 1; i < nargc; i++) {
4577 str = JS_ValueToString(cx, argv[i-1]);
4578 if (!str) {
4579 JS_free(cx, nargv);
4580 return JS_FALSE;
4582 nargv[i] = JS_GetStringBytes(str);
4584 nargv[nargc] = 0;
4585 pid = fork();
4586 switch (pid) {
4587 case -1:
4588 perror("js");
4589 break;
4590 case 0:
4591 (void) execvp(name, (char **)nargv);
4592 perror("js");
4593 exit(127);
4594 default:
4595 while (waitpid(pid, &status, 0) < 0 && errno == EINTR)
4596 continue;
4597 break;
4599 JS_free(cx, nargv);
4600 return JS_TRUE;
4602 #endif
4604 static JSBool
4605 global_enumerate(JSContext *cx, JSObject *obj)
4607 #ifdef LAZY_STANDARD_CLASSES
4608 return JS_EnumerateStandardClasses(cx, obj);
4609 #else
4610 return JS_TRUE;
4611 #endif
4614 static JSBool
4615 global_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
4616 JSObject **objp)
4618 #ifdef LAZY_STANDARD_CLASSES
4619 JSBool resolved;
4621 if (!JS_ResolveStandardClass(cx, obj, id, &resolved))
4622 return JS_FALSE;
4623 if (resolved) {
4624 *objp = obj;
4625 return JS_TRUE;
4627 #endif
4629 #if defined(SHELL_HACK) && defined(DEBUG) && defined(XP_UNIX)
4630 if (!(flags & JSRESOLVE_QUALIFIED)) {
4632 * Do this expensive hack only for unoptimized Unix builds, which are
4633 * not used for benchmarking.
4635 char *path, *comp, *full;
4636 const char *name;
4637 JSBool ok, found;
4638 JSFunction *fun;
4640 if (!JSVAL_IS_STRING(id))
4641 return JS_TRUE;
4642 path = getenv("PATH");
4643 if (!path)
4644 return JS_TRUE;
4645 path = JS_strdup(cx, path);
4646 if (!path)
4647 return JS_FALSE;
4648 name = JS_GetStringBytes(JSVAL_TO_STRING(id));
4649 ok = JS_TRUE;
4650 for (comp = strtok(path, ":"); comp; comp = strtok(NULL, ":")) {
4651 if (*comp != '\0') {
4652 full = JS_smprintf("%s/%s", comp, name);
4653 if (!full) {
4654 JS_ReportOutOfMemory(cx);
4655 ok = JS_FALSE;
4656 break;
4658 } else {
4659 full = (char *)name;
4661 found = (access(full, X_OK) == 0);
4662 if (*comp != '\0')
4663 free(full);
4664 if (found) {
4665 fun = JS_DefineFunction(cx, obj, name, Exec, 0,
4666 JSPROP_ENUMERATE);
4667 ok = (fun != NULL);
4668 if (ok)
4669 *objp = obj;
4670 break;
4673 JS_free(cx, path);
4674 return ok;
4676 #else
4677 return JS_TRUE;
4678 #endif
4681 JSClass global_class = {
4682 "global", JSCLASS_NEW_RESOLVE | JSCLASS_GLOBAL_FLAGS | JSCLASS_HAS_PRIVATE,
4683 JS_PropertyStub, JS_PropertyStub,
4684 JS_PropertyStub, JS_PropertyStub,
4685 global_enumerate, (JSResolveOp) global_resolve,
4686 JS_ConvertStub, its_finalize,
4687 JSCLASS_NO_OPTIONAL_MEMBERS
4690 static JSBool
4691 env_setProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
4693 /* XXX porting may be easy, but these don't seem to supply setenv by default */
4694 #if !defined XP_BEOS && !defined XP_OS2 && !defined SOLARIS
4695 int rv;
4697 IdToString idstr(cx, id, JS_TRUE);
4698 if (idstr.threw())
4699 return JS_FALSE;
4700 ToString valstr(cx, *vp, JS_TRUE);
4701 if (valstr.threw())
4702 return JS_FALSE;
4703 #if defined XP_WIN || defined HPUX || defined OSF1 || defined IRIX
4705 char *waste = JS_smprintf("%s=%s", idstr.getBytes(), valstr.getBytes());
4706 if (!waste) {
4707 JS_ReportOutOfMemory(cx);
4708 return JS_FALSE;
4710 rv = putenv(waste);
4711 #ifdef XP_WIN
4713 * HPUX9 at least still has the bad old non-copying putenv.
4715 * Per mail from <s.shanmuganathan@digital.com>, OSF1 also has a putenv
4716 * that will crash if you pass it an auto char array (so it must place
4717 * its argument directly in the char *environ[] array).
4719 JS_smprintf_free(waste);
4720 #endif
4722 #else
4723 rv = setenv(idstr.getBytes(), valstr.getBytes(), 1);
4724 #endif
4725 if (rv < 0) {
4726 JS_ReportError(cx, "can't set env variable %s to %s", idstr.getBytes(), valstr.getBytes());
4727 return JS_FALSE;
4729 *vp = valstr.getJSVal();
4730 #endif /* !defined XP_BEOS && !defined XP_OS2 && !defined SOLARIS */
4731 return JS_TRUE;
4734 static JSBool
4735 env_enumerate(JSContext *cx, JSObject *obj)
4737 static JSBool reflected;
4738 char **evp, *name, *value;
4739 JSString *valstr;
4740 JSBool ok;
4742 if (reflected)
4743 return JS_TRUE;
4745 for (evp = (char **)JS_GetPrivate(cx, obj); (name = *evp) != NULL; evp++) {
4746 value = strchr(name, '=');
4747 if (!value)
4748 continue;
4749 *value++ = '\0';
4750 valstr = JS_NewStringCopyZ(cx, value);
4751 if (!valstr) {
4752 ok = JS_FALSE;
4753 } else {
4754 ok = JS_DefineProperty(cx, obj, name, STRING_TO_JSVAL(valstr),
4755 NULL, NULL, JSPROP_ENUMERATE);
4757 value[-1] = '=';
4758 if (!ok)
4759 return JS_FALSE;
4762 reflected = JS_TRUE;
4763 return JS_TRUE;
4766 static JSBool
4767 env_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
4768 JSObject **objp)
4770 JSString *valstr;
4771 const char *name, *value;
4773 if (flags & JSRESOLVE_ASSIGNING)
4774 return JS_TRUE;
4776 IdToString idstr(cx, id, JS_TRUE);
4777 if (idstr.threw())
4778 return JS_FALSE;
4780 name = idstr.getBytes();
4781 value = getenv(name);
4782 if (value) {
4783 valstr = JS_NewStringCopyZ(cx, value);
4784 if (!valstr)
4785 return JS_FALSE;
4786 if (!JS_DefineProperty(cx, obj, name, STRING_TO_JSVAL(valstr),
4787 NULL, NULL, JSPROP_ENUMERATE)) {
4788 return JS_FALSE;
4790 *objp = obj;
4792 return JS_TRUE;
4795 static JSClass env_class = {
4796 "environment", JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE,
4797 JS_PropertyStub, JS_PropertyStub,
4798 JS_PropertyStub, env_setProperty,
4799 env_enumerate, (JSResolveOp) env_resolve,
4800 JS_ConvertStub, NULL,
4801 JSCLASS_NO_OPTIONAL_MEMBERS
4805 * Avoid a reentrancy hazard.
4807 * The non-JS_THREADSAFE shell uses a signal handler to implement timeout().
4808 * The JS engine is not really reentrant, but JS_TriggerAllOperationCallbacks
4809 * is mostly safe--the only danger is that we might interrupt JS_NewContext or
4810 * JS_DestroyContext while the context list is being modified. Therefore we
4811 * disable the signal handler around calls to those functions.
4813 #ifdef JS_THREADSAFE
4814 # define WITH_SIGNALS_DISABLED(x) x
4815 #else
4816 # define WITH_SIGNALS_DISABLED(x) \
4817 JS_BEGIN_MACRO \
4818 ScheduleWatchdog(gRuntime, -1); \
4819 x; \
4820 ScheduleWatchdog(gRuntime, gTimeoutInterval); \
4821 JS_END_MACRO
4822 #endif
4824 static JSContext *
4825 NewContext(JSRuntime *rt)
4827 JSContext *cx;
4828 WITH_SIGNALS_DISABLED(cx = JS_NewContext(rt, gStackChunkSize));
4829 if (!cx)
4830 return NULL;
4832 JSShellContextData *data = NewContextData();
4833 if (!data) {
4834 DestroyContext(cx, false);
4835 return NULL;
4838 JS_SetContextPrivate(cx, data);
4839 JS_SetErrorReporter(cx, my_ErrorReporter);
4840 JS_SetVersion(cx, JSVERSION_LATEST);
4841 SetContextOptions(cx);
4842 if (enableJit)
4843 JS_ToggleOptions(cx, JSOPTION_JIT);
4844 return cx;
4847 static void
4848 DestroyContext(JSContext *cx, bool withGC)
4850 JSShellContextData *data = GetContextData(cx);
4851 JS_SetContextPrivate(cx, NULL);
4852 free(data);
4853 WITH_SIGNALS_DISABLED(withGC ? JS_DestroyContext(cx) : JS_DestroyContextNoGC(cx));
4856 static JSObject *
4857 NewGlobalObject(JSContext *cx, JSAutoCrossCompartmentCall &call)
4859 JSObject *glob = JS_NewCompartmentAndGlobalObject(cx, &global_class, NULL);
4860 if (!glob)
4861 return NULL;
4862 if (!call.enter(cx, glob))
4863 return NULL;
4865 #ifdef LAZY_STANDARD_CLASSES
4866 JS_SetGlobalObject(cx, glob);
4867 #else
4868 if (!JS_InitStandardClasses(cx, glob))
4869 return NULL;
4870 #endif
4871 #ifdef JS_HAS_CTYPES
4872 if (!JS_InitCTypesClass(cx, glob))
4873 return NULL;
4874 #endif
4875 if (!JS_DefineFunctions(cx, glob, shell_functions))
4876 return NULL;
4878 JSObject *it = JS_DefineObject(cx, glob, "it", &its_class, NULL, 0);
4879 if (!it)
4880 return NULL;
4881 if (!JS_DefineProperties(cx, it, its_props))
4882 return NULL;
4883 if (!JS_DefineFunctions(cx, it, its_methods))
4884 return NULL;
4886 if (!JS_DefineProperty(cx, glob, "custom", JSVAL_VOID, its_getter,
4887 its_setter, 0))
4888 return NULL;
4889 if (!JS_DefineProperty(cx, glob, "customRdOnly", JSVAL_VOID, its_getter,
4890 its_setter, JSPROP_READONLY))
4891 return NULL;
4893 return glob;
4897 shell(JSContext *cx, int argc, char **argv, char **envp)
4899 JSAutoRequest ar(cx);
4900 JSAutoCrossCompartmentCall ac;
4902 JSObject *glob = NewGlobalObject(cx, ac);
4903 if (!glob)
4904 return 1;
4906 JSObject *envobj = JS_DefineObject(cx, glob, "environment", &env_class, NULL, 0);
4907 if (!envobj || !JS_SetPrivate(cx, envobj, envp))
4908 return 1;
4910 #ifdef JSDEBUGGER
4912 * XXX A command line option to enable debugging (or not) would be good
4914 jsdc = JSD_DebuggerOnForUser(rt, NULL, NULL);
4915 if (!jsdc)
4916 return 1;
4917 JSD_JSContextInUse(jsdc, cx);
4918 #ifdef JSD_LOWLEVEL_SOURCE
4919 JS_SetSourceHandler(rt, SendSourceToJSDebugger, jsdc);
4920 #endif /* JSD_LOWLEVEL_SOURCE */
4921 #ifdef JSDEBUGGER_JAVA_UI
4922 jsdjc = JSDJ_CreateContext();
4923 if (! jsdjc)
4924 return 1;
4925 JSDJ_SetJSDContext(jsdjc, jsdc);
4926 java_env = JSDJ_CreateJavaVMAndStartDebugger(jsdjc);
4928 * XXX This would be the place to wait for the debugger to start.
4929 * Waiting would be nice in general, but especially when a js file
4930 * is passed on the cmd line.
4932 #endif /* JSDEBUGGER_JAVA_UI */
4933 #ifdef JSDEBUGGER_C_UI
4934 jsdbc = JSDB_InitDebugger(rt, jsdc, 0);
4935 #endif /* JSDEBUGGER_C_UI */
4936 #endif /* JSDEBUGGER */
4938 #ifdef JS_THREADSAFE
4939 class ShellWorkerHooks : public js::workers::WorkerHooks {
4940 public:
4941 JSObject *newGlobalObject(JSContext *cx) {
4942 JSAutoCrossCompartmentCall ac;
4943 return NewGlobalObject(cx, ac);
4946 ShellWorkerHooks hooks;
4947 if (!JS_AddNamedObjectRoot(cx, &gWorkers, "Workers") ||
4948 !js::workers::init(cx, &hooks, glob, &gWorkers)) {
4949 return 1;
4951 #endif
4953 int result = ProcessArgs(cx, glob, argv, argc);
4955 #ifdef JS_THREADSAFE
4956 js::workers::finish(cx, gWorkers);
4957 JS_RemoveObjectRoot(cx, &gWorkers);
4958 if (result == 0)
4959 result = gExitCode;
4960 #endif
4962 #ifdef JSDEBUGGER
4963 if (jsdc) {
4964 #ifdef JSDEBUGGER_C_UI
4965 if (jsdbc)
4966 JSDB_TermDebugger(jsdc);
4967 #endif /* JSDEBUGGER_C_UI */
4968 JSD_DebuggerOff(jsdc);
4970 #endif /* JSDEBUGGER */
4972 return result;
4976 main(int argc, char **argv, char **envp)
4978 int stackDummy;
4979 JSRuntime *rt;
4980 JSContext *cx;
4981 int result;
4982 #ifdef JSDEBUGGER
4983 JSDContext *jsdc;
4984 #ifdef JSDEBUGGER_JAVA_UI
4985 JNIEnv *java_env;
4986 JSDJContext *jsdjc;
4987 #endif
4988 #ifdef JSDEBUGGER_C_UI
4989 JSBool jsdbc;
4990 #endif /* JSDEBUGGER_C_UI */
4991 #endif /* JSDEBUGGER */
4993 CheckHelpMessages();
4994 #ifdef HAVE_SETLOCALE
4995 setlocale(LC_ALL, "");
4996 #endif
4998 #ifdef JS_THREADSAFE
4999 if (PR_FAILURE == PR_NewThreadPrivateIndex(&gStackBaseThreadIndex, NULL) ||
5000 PR_FAILURE == PR_SetThreadPrivate(gStackBaseThreadIndex, &stackDummy)) {
5001 return 1;
5003 #else
5004 gStackBase = (jsuword) &stackDummy;
5005 #endif
5007 #ifdef XP_OS2
5008 /* these streams are normally line buffered on OS/2 and need a \n, *
5009 * so we need to unbuffer then to get a reasonable prompt */
5010 setbuf(stdout,0);
5011 setbuf(stderr,0);
5012 #endif
5014 gErrFile = stderr;
5015 gOutFile = stdout;
5017 argc--;
5018 argv++;
5020 #ifdef XP_WIN
5021 // Set the timer calibration delay count to 0 so we get high
5022 // resolution right away, which we need for precise benchmarking.
5023 extern int CALIBRATION_DELAY_COUNT;
5024 CALIBRATION_DELAY_COUNT = 0;
5025 #endif
5027 rt = JS_NewRuntime(64L * 1024L * 1024L);
5028 if (!rt)
5029 return 1;
5031 if (!InitWatchdog(rt))
5032 return 1;
5034 cx = NewContext(rt);
5035 if (!cx)
5036 return 1;
5038 JS_SetGCParameterForThread(cx, JSGC_MAX_CODE_CACHE_BYTES, 16 * 1024 * 1024);
5040 result = shell(cx, argc, argv, envp);
5042 JS_CommenceRuntimeShutDown(rt);
5044 DestroyContext(cx, true);
5046 KillWatchdog();
5048 JS_DestroyRuntime(rt);
5049 JS_ShutDown();
5050 return result;