/[jscoverage]/trunk/js/shell/js.cpp
ViewVC logotype

Contents of /trunk/js/shell/js.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 507 - (show annotations)
Sun Jan 10 07:23:34 2010 UTC (10 years, 8 months ago) by siliconforks
File size: 135159 byte(s)
Update SpiderMonkey from Firefox 3.6rc1.

1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sw=4 et tw=99:
3 *
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 *
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/
11 *
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.
16 *
17 * The Original Code is Mozilla Communicator client code, released
18 * March 31, 1998.
19 *
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.
24 *
25 * Contributor(s):
26 *
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.
38 *
39 * ***** END LICENSE BLOCK ***** */
40
41 /*
42 * JS shell.
43 */
44 #include <errno.h>
45 #include <math.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <signal.h>
50 #include <locale.h>
51 #include "jstypes.h"
52 #include "jsarena.h"
53 #include "jsutil.h"
54 #include "jsprf.h"
55 #include "jsapi.h"
56 #include "jsarray.h"
57 #include "jsatom.h"
58 #include "jsbuiltins.h"
59 #include "jscntxt.h"
60 #include "jsdate.h"
61 #include "jsdbgapi.h"
62 #include "jsemit.h"
63 #include "jsfun.h"
64 #include "jsgc.h"
65 #include "jsiter.h"
66 #include "jslock.h"
67 #include "jsnum.h"
68 #include "jsobj.h"
69 #include "jsparse.h"
70 #include "jsscope.h"
71 #include "jsscript.h"
72 #include "jstracer.h"
73
74 #include "prmjtime.h"
75
76 #ifdef JSDEBUGGER
77 #include "jsdebug.h"
78 #ifdef JSDEBUGGER_JAVA_UI
79 #include "jsdjava.h"
80 #endif /* JSDEBUGGER_JAVA_UI */
81 #ifdef JSDEBUGGER_C_UI
82 #include "jsdb.h"
83 #endif /* JSDEBUGGER_C_UI */
84 #endif /* JSDEBUGGER */
85
86 #ifdef XP_UNIX
87 #include <unistd.h>
88 #include <sys/types.h>
89 #include <sys/wait.h>
90 #endif
91
92 #if defined(XP_WIN) || defined(XP_OS2)
93 #include <io.h> /* for isatty() */
94 #endif
95
96 #ifdef XP_WIN
97 #include <windows.h>
98 #endif
99
100 typedef enum JSShellExitCode {
101 EXITCODE_RUNTIME_ERROR = 3,
102 EXITCODE_FILE_NOT_FOUND = 4,
103 EXITCODE_OUT_OF_MEMORY = 5,
104 EXITCODE_TIMEOUT = 6
105 } JSShellExitCode;
106
107 size_t gStackChunkSize = 8192;
108
109 /* Assume that we can not use more than 5e5 bytes of C stack by default. */
110 static size_t gMaxStackSize = 500000;
111
112 #ifdef JS_THREADSAFE
113 static PRUintn gStackBaseThreadIndex;
114 #else
115 static jsuword gStackBase;
116 #endif
117
118 static size_t gScriptStackQuota = JS_DEFAULT_SCRIPT_STACK_QUOTA;
119
120 /*
121 * Limit the timeout to 30 minutes to prevent an overflow on platfoms
122 * that represent the time internally in microseconds using 32-bit int.
123 */
124 static jsdouble MAX_TIMEOUT_INTERVAL = 1800.0;
125 static jsdouble gTimeoutInterval = -1.0;
126 static volatile bool gCanceled = false;
127
128 static JSBool
129 SetTimeoutValue(JSContext *cx, jsdouble t);
130
131 static bool
132 InitWatchdog(JSRuntime *rt);
133
134 static void
135 KillWatchdog();
136
137 static bool
138 ScheduleWatchdog(JSRuntime *rt, jsdouble t);
139
140 static void
141 CancelExecution(JSRuntime *rt);
142
143 /*
144 * Watchdog thread state.
145 */
146 #ifdef JS_THREADSAFE
147
148 static PRLock *gWatchdogLock = NULL;
149 static PRCondVar *gWatchdogWakeup = NULL;
150 static PRThread *gWatchdogThread = NULL;
151 static bool gWatchdogHasTimeout = false;
152 static PRIntervalTime gWatchdogTimeout = 0;
153
154 static PRCondVar *gSleepWakeup = NULL;
155
156 /*
157 * Holding the gcLock already guarantees that the context list is locked when
158 * the watchdog thread walks it.
159 */
160
161 #define WITH_LOCKED_CONTEXT_LIST(x) \
162 JS_BEGIN_MACRO \
163 x; \
164 JS_END_MACRO
165
166 #else
167
168 static JSRuntime *gRuntime = NULL;
169
170 /*
171 * Since signal handlers can't block, we must disable them before manipulating
172 * the context list.
173 */
174 #define WITH_LOCKED_CONTEXT_LIST(x) \
175 JS_BEGIN_MACRO \
176 ScheduleWatchdog(gRuntime, -1); \
177 x; \
178 ScheduleWatchdog(gRuntime, gTimeoutInterval); \
179 JS_END_MACRO
180 #endif
181
182 int gExitCode = 0;
183 JSBool gQuitting = JS_FALSE;
184 FILE *gErrFile = NULL;
185 FILE *gOutFile = NULL;
186
187 static JSBool reportWarnings = JS_TRUE;
188 static JSBool compileOnly = JS_FALSE;
189
190 typedef enum JSShellErrNum {
191 #define MSG_DEF(name, number, count, exception, format) \
192 name = number,
193 #include "jsshell.msg"
194 #undef MSG_DEF
195 JSShellErr_Limit
196 #undef MSGDEF
197 } JSShellErrNum;
198
199 static const JSErrorFormatString *
200 my_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber);
201 static JSObject *
202 split_setup(JSContext *cx);
203
204 #ifdef EDITLINE
205 JS_BEGIN_EXTERN_C
206 JS_EXTERN_API(char) *readline(const char *prompt);
207 JS_EXTERN_API(void) add_history(char *line);
208 JS_END_EXTERN_C
209 #endif
210
211 class ToString {
212 public:
213 ToString(JSContext *aCx, jsval v, JSBool aThrow = JS_FALSE)
214 : cx(aCx)
215 , mThrow(aThrow)
216 {
217 mStr = JS_ValueToString(cx, v);
218 if (!aThrow && !mStr && JS_IsExceptionPending(cx)) {
219 if (!JS_ReportPendingException(cx))
220 JS_ClearPendingException(cx);
221 }
222 JS_AddNamedRoot(cx, &mStr, "Value ToString helper");
223 }
224 ~ToString() {
225 JS_RemoveRoot(cx, &mStr);
226 }
227 JSBool threw() { return !mStr; }
228 jsval getJSVal() { return STRING_TO_JSVAL(mStr); }
229 const char *getBytes() {
230 return mStr ? JS_GetStringBytes(mStr) : "(error converting value)";
231 }
232 private:
233 JSContext *cx;
234 JSString *mStr;
235 JSBool mThrow;
236 };
237
238 static char *
239 GetLine(FILE *file, const char * prompt)
240 {
241 size_t size;
242 char *buffer;
243 #ifdef EDITLINE
244 /*
245 * Use readline only if file is stdin, because there's no way to specify
246 * another handle. Are other filehandles interactive?
247 */
248 if (file == stdin) {
249 char *linep = readline(prompt);
250 /*
251 * We set it to zero to avoid complaining about inappropriate ioctl
252 * for device in the case of EOF. Looks like errno == 251 if line is
253 * finished with EOF and errno == 25 (EINVAL on Mac) if there is
254 * nothing left to read.
255 */
256 if (errno == 251 || errno == 25 || errno == EINVAL)
257 errno = 0;
258 if (!linep)
259 return NULL;
260 if (linep[0] != '\0')
261 add_history(linep);
262 return linep;
263 }
264 #endif
265 size_t len = 0;
266 if (*prompt != '\0') {
267 fprintf(gOutFile, "%s", prompt);
268 fflush(gOutFile);
269 }
270 size = 80;
271 buffer = (char *) malloc(size);
272 if (!buffer)
273 return NULL;
274 char *current = buffer;
275 while (fgets(current, size - len, file)) {
276 len += strlen(current);
277 char *t = buffer + len - 1;
278 if (*t == '\n') {
279 /* Line was read. We remove '\n' and exit. */
280 *t = '\0';
281 return buffer;
282 }
283 if (len + 1 == size) {
284 size = size * 2;
285 char *tmp = (char *) realloc(buffer, size);
286 if (!tmp) {
287 free(buffer);
288 return NULL;
289 }
290 buffer = tmp;
291 }
292 current = buffer + len;
293 }
294 if (len && !ferror(file))
295 return buffer;
296 free(buffer);
297 return NULL;
298 }
299
300 /*
301 * State to store as JSContext private.
302 *
303 * We declare such timestamp as volatile as they are updated in the operation
304 * callback without taking any locks. Any possible race can only lead to more
305 * frequent callback calls. This is safe as the callback does everything based
306 * on timing.
307 */
308 struct JSShellContextData {
309 volatile JSIntervalTime startTime;
310 };
311
312 static JSShellContextData *
313 NewContextData()
314 {
315 /* Prevent creation of new contexts after we have been canceled. */
316 if (gCanceled)
317 return NULL;
318
319 JSShellContextData *data = (JSShellContextData *)
320 calloc(sizeof(JSShellContextData), 1);
321 if (!data)
322 return NULL;
323 data->startTime = js_IntervalNow();
324 return data;
325 }
326
327 static inline JSShellContextData *
328 GetContextData(JSContext *cx)
329 {
330 JSShellContextData *data = (JSShellContextData *) JS_GetContextPrivate(cx);
331
332 JS_ASSERT(data);
333 return data;
334 }
335
336 static JSBool
337 ShellOperationCallback(JSContext *cx)
338 {
339 if (!gCanceled)
340 return JS_TRUE;
341
342 JS_ClearPendingException(cx);
343 return JS_FALSE;
344 }
345
346 static void
347 SetThreadStackLimit(JSContext *cx)
348 {
349 jsuword stackLimit;
350
351 if (gMaxStackSize == 0) {
352 /*
353 * Disable checking for stack overflow if limit is zero.
354 */
355 stackLimit = 0;
356 } else {
357 jsuword stackBase;
358 #ifdef JS_THREADSAFE
359 stackBase = (jsuword) PR_GetThreadPrivate(gStackBaseThreadIndex);
360 #else
361 stackBase = gStackBase;
362 #endif
363 JS_ASSERT(stackBase != 0);
364 #if JS_STACK_GROWTH_DIRECTION > 0
365 stackLimit = stackBase + gMaxStackSize;
366 #else
367 stackLimit = stackBase - gMaxStackSize;
368 #endif
369 }
370 JS_SetThreadStackLimit(cx, stackLimit);
371
372 }
373
374 static void
375 SetContextOptions(JSContext *cx)
376 {
377 SetThreadStackLimit(cx);
378 JS_SetScriptStackQuota(cx, gScriptStackQuota);
379 JS_SetOperationCallback(cx, ShellOperationCallback);
380 }
381
382 static void
383 Process(JSContext *cx, JSObject *obj, char *filename, JSBool forceTTY)
384 {
385 JSBool ok, hitEOF;
386 JSScript *script;
387 jsval result;
388 JSString *str;
389 char *buffer;
390 size_t size;
391 int lineno;
392 int startline;
393 FILE *file;
394 uint32 oldopts;
395
396 if (forceTTY || !filename || strcmp(filename, "-") == 0) {
397 file = stdin;
398 } else {
399 file = fopen(filename, "r");
400 if (!file) {
401 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
402 JSSMSG_CANT_OPEN, filename, strerror(errno));
403 gExitCode = EXITCODE_FILE_NOT_FOUND;
404 return;
405 }
406 }
407
408 SetContextOptions(cx);
409
410 #ifndef WINCE
411 /* windows mobile (and possibly other os's) does not have a TTY */
412 if (!forceTTY && !isatty(fileno(file))) {
413 /*
414 * It's not interactive - just execute it.
415 *
416 * Support the UNIX #! shell hack; gobble the first line if it starts
417 * with '#'. TODO - this isn't quite compatible with sharp variables,
418 * as a legal js program (using sharp variables) might start with '#'.
419 * But that would require multi-character lookahead.
420 */
421 int ch = fgetc(file);
422 if (ch == '#') {
423 while((ch = fgetc(file)) != EOF) {
424 if (ch == '\n' || ch == '\r')
425 break;
426 }
427 }
428 ungetc(ch, file);
429
430 oldopts = JS_GetOptions(cx);
431 JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO | JSOPTION_NO_SCRIPT_RVAL);
432 script = JS_CompileFileHandle(cx, obj, filename, file);
433 JS_SetOptions(cx, oldopts);
434 if (script) {
435 if (!compileOnly)
436 (void)JS_ExecuteScript(cx, obj, script, NULL);
437 JS_DestroyScript(cx, script);
438 }
439
440 if (file != stdin)
441 fclose(file);
442 return;
443 }
444 #endif /* WINCE */
445
446 /* It's an interactive filehandle; drop into read-eval-print loop. */
447 lineno = 1;
448 hitEOF = JS_FALSE;
449 buffer = NULL;
450 size = 0; /* assign here to avoid warnings */
451 do {
452 /*
453 * Accumulate lines until we get a 'compilable unit' - one that either
454 * generates an error (before running out of source) or that compiles
455 * cleanly. This should be whenever we get a complete statement that
456 * coincides with the end of a line.
457 */
458 startline = lineno;
459 size_t len = 0; /* initialize to avoid warnings */
460 do {
461 ScheduleWatchdog(cx->runtime, -1);
462 jsrefcount rc = JS_SuspendRequest(cx);
463 gCanceled = false;
464 errno = 0;
465 char *line = GetLine(file, startline == lineno ? "js> " : "");
466 if (!line) {
467 JS_ResumeRequest(cx, rc);
468 if (errno) {
469 JS_ReportError(cx, strerror(errno));
470 free(buffer);
471 return;
472 }
473 hitEOF = JS_TRUE;
474 break;
475 }
476 if (!buffer) {
477 buffer = line;
478 len = strlen(buffer);
479 size = len + 1;
480 } else {
481 /*
482 * len + 1 is required to store '\n' in the end of line.
483 */
484 size_t newlen = strlen(line) + (len ? len + 1 : 0);
485 if (newlen + 1 > size) {
486 size = newlen + 1 > size * 2 ? newlen + 1 : size * 2;
487 char *newBuf = (char *) realloc(buffer, size);
488 if (!newBuf) {
489 free(buffer);
490 free(line);
491 JS_ResumeRequest(cx, rc);
492 JS_ReportOutOfMemory(cx);
493 return;
494 }
495 buffer = newBuf;
496 }
497 char *current = buffer + len;
498 if (startline != lineno)
499 *current++ = '\n';
500 strcpy(current, line);
501 len = newlen;
502 free(line);
503 }
504 lineno++;
505 JS_ResumeRequest(cx, rc);
506 if (!ScheduleWatchdog(cx->runtime, gTimeoutInterval)) {
507 hitEOF = JS_TRUE;
508 break;
509 }
510 } while (!JS_BufferIsCompilableUnit(cx, obj, buffer, len));
511
512 if (hitEOF && !buffer)
513 break;
514
515 /* Clear any pending exception from previous failed compiles. */
516 JS_ClearPendingException(cx);
517
518 /* Even though we're interactive, we have a compile-n-go opportunity. */
519 oldopts = JS_GetOptions(cx);
520 if (!compileOnly)
521 JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO);
522 script = JS_CompileScript(cx, obj, buffer, len, "typein",
523 startline);
524 if (!compileOnly)
525 JS_SetOptions(cx, oldopts);
526
527 if (script) {
528 if (!compileOnly) {
529 ok = JS_ExecuteScript(cx, obj, script, &result);
530 if (ok && !JSVAL_IS_VOID(result)) {
531 str = JS_ValueToSource(cx, result);
532 if (str)
533 fprintf(gOutFile, "%s\n", JS_GetStringBytes(str));
534 else
535 ok = JS_FALSE;
536 }
537 }
538 JS_DestroyScript(cx, script);
539 }
540 *buffer = '\0';
541 } while (!hitEOF && !gQuitting);
542
543 free(buffer);
544 fprintf(gOutFile, "\n");
545 if (file != stdin)
546 fclose(file);
547 return;
548 }
549
550 static int
551 usage(void)
552 {
553 fprintf(gErrFile, "%s\n", JS_GetImplementationVersion());
554 fprintf(gErrFile, "usage: js [-zKPswWxCij] [-t timeoutSeconds] [-c stackchunksize] [-o option] [-v version] [-f scriptfile] [-e script] [-S maxstacksize] "
555 #ifdef JS_GC_ZEAL
556 "[-Z gczeal] "
557 #endif
558 #ifdef MOZ_TRACEVIS
559 "[-T TraceVisFileName] "
560 #endif
561 "[scriptfile] [scriptarg...]\n");
562 return 2;
563 }
564
565 /*
566 * JSContext option name to flag map. The option names are in alphabetical
567 * order for better reporting.
568 */
569 static const struct {
570 const char *name;
571 uint32 flag;
572 } js_options[] = {
573 {"anonfunfix", JSOPTION_ANONFUNFIX},
574 {"atline", JSOPTION_ATLINE},
575 {"jit", JSOPTION_JIT},
576 {"relimit", JSOPTION_RELIMIT},
577 {"strict", JSOPTION_STRICT},
578 {"werror", JSOPTION_WERROR},
579 {"xml", JSOPTION_XML},
580 };
581
582 static uint32
583 MapContextOptionNameToFlag(JSContext* cx, const char* name)
584 {
585 for (size_t i = 0; i != JS_ARRAY_LENGTH(js_options); ++i) {
586 if (strcmp(name, js_options[i].name) == 0)
587 return js_options[i].flag;
588 }
589
590 char* msg = JS_sprintf_append(NULL,
591 "unknown option name '%s'."
592 " The valid names are ", name);
593 for (size_t i = 0; i != JS_ARRAY_LENGTH(js_options); ++i) {
594 if (!msg)
595 break;
596 msg = JS_sprintf_append(msg, "%s%s", js_options[i].name,
597 (i + 2 < JS_ARRAY_LENGTH(js_options)
598 ? ", "
599 : i + 2 == JS_ARRAY_LENGTH(js_options)
600 ? " and "
601 : "."));
602 }
603 if (!msg) {
604 JS_ReportOutOfMemory(cx);
605 } else {
606 JS_ReportError(cx, msg);
607 free(msg);
608 }
609 return 0;
610 }
611
612 extern JSClass global_class;
613
614 static int
615 ProcessArgs(JSContext *cx, JSObject *obj, char **argv, int argc)
616 {
617 int i, j, length;
618 JSObject *argsObj;
619 char *filename = NULL;
620 JSBool isInteractive = JS_TRUE;
621 JSBool forceTTY = JS_FALSE;
622
623 /*
624 * Scan past all optional arguments so we can create the arguments object
625 * before processing any -f options, which must interleave properly with
626 * -v and -w options. This requires two passes, and without getopt, we'll
627 * have to keep the option logic here and in the second for loop in sync.
628 */
629 for (i = 0; i < argc; i++) {
630 if (argv[i][0] != '-' || argv[i][1] == '\0') {
631 ++i;
632 break;
633 }
634 switch (argv[i][1]) {
635 case 'c':
636 case 'f':
637 case 'e':
638 case 'v':
639 case 'S':
640 case 't':
641 #ifdef JS_GC_ZEAL
642 case 'Z':
643 #endif
644 #ifdef MOZ_TRACEVIS
645 case 'T':
646 #endif
647 ++i;
648 break;
649 default:;
650 }
651 }
652
653 /*
654 * Create arguments early and define it to root it, so it's safe from any
655 * GC calls nested below, and so it is available to -f <file> arguments.
656 */
657 argsObj = JS_NewArrayObject(cx, 0, NULL);
658 if (!argsObj)
659 return 1;
660 if (!JS_DefineProperty(cx, obj, "arguments", OBJECT_TO_JSVAL(argsObj),
661 NULL, NULL, 0)) {
662 return 1;
663 }
664
665 length = argc - i;
666 for (j = 0; j < length; j++) {
667 JSString *str = JS_NewStringCopyZ(cx, argv[i++]);
668 if (!str)
669 return 1;
670 if (!JS_DefineElement(cx, argsObj, j, STRING_TO_JSVAL(str),
671 NULL, NULL, JSPROP_ENUMERATE)) {
672 return 1;
673 }
674 }
675
676 for (i = 0; i < argc; i++) {
677 if (argv[i][0] != '-' || argv[i][1] == '\0') {
678 filename = argv[i++];
679 isInteractive = JS_FALSE;
680 break;
681 }
682
683 switch (argv[i][1]) {
684 case 'v':
685 if (++i == argc)
686 return usage();
687
688 JS_SetVersion(cx, (JSVersion) atoi(argv[i]));
689 break;
690
691 #ifdef JS_GC_ZEAL
692 case 'Z':
693 if (++i == argc)
694 return usage();
695 JS_SetGCZeal(cx, atoi(argv[i]));
696 break;
697 #endif
698
699 case 'w':
700 reportWarnings = JS_TRUE;
701 break;
702
703 case 'W':
704 reportWarnings = JS_FALSE;
705 break;
706
707 case 's':
708 JS_ToggleOptions(cx, JSOPTION_STRICT);
709 break;
710
711 case 'E':
712 JS_ToggleOptions(cx, JSOPTION_RELIMIT);
713 break;
714
715 case 'x':
716 JS_ToggleOptions(cx, JSOPTION_XML);
717 break;
718
719 case 'j':
720 JS_ToggleOptions(cx, JSOPTION_JIT);
721 #if defined(JS_TRACER) && defined(DEBUG)
722 extern struct JSClass jitstats_class;
723 extern void js_InitJITStatsClass(JSContext *cx, JSObject *glob);
724 js_InitJITStatsClass(cx, JS_GetGlobalObject(cx));
725 JS_DefineObject(cx, JS_GetGlobalObject(cx), "tracemonkey",
726 &jitstats_class, NULL, 0);
727 #endif
728 break;
729
730 case 'o':
731 {
732 if (++i == argc)
733 return usage();
734
735 uint32 flag = MapContextOptionNameToFlag(cx, argv[i]);
736 if (flag == 0)
737 return gExitCode;
738 JS_ToggleOptions(cx, flag);
739 break;
740 }
741 case 'P':
742 if (JS_GET_CLASS(cx, JS_GetPrototype(cx, obj)) != &global_class) {
743 JSObject *gobj;
744
745 if (!JS_SealObject(cx, obj, JS_TRUE))
746 return JS_FALSE;
747 gobj = JS_NewObject(cx, &global_class, NULL, NULL);
748 if (!gobj)
749 return JS_FALSE;
750 if (!JS_SetPrototype(cx, gobj, obj))
751 return JS_FALSE;
752 JS_SetParent(cx, gobj, NULL);
753 JS_SetGlobalObject(cx, gobj);
754 obj = gobj;
755 }
756 break;
757
758 case 't':
759 if (++i == argc)
760 return usage();
761
762 if (!SetTimeoutValue(cx, atof(argv[i])))
763 return JS_FALSE;
764
765 break;
766
767 case 'c':
768 /* set stack chunk size */
769 gStackChunkSize = atoi(argv[++i]);
770 break;
771
772 case 'f':
773 if (++i == argc)
774 return usage();
775
776 Process(cx, obj, argv[i], JS_FALSE);
777 if (gExitCode != 0)
778 return gExitCode;
779
780 /*
781 * XXX: js -f foo.js should interpret foo.js and then
782 * drop into interactive mode, but that breaks the test
783 * harness. Just execute foo.js for now.
784 */
785 isInteractive = JS_FALSE;
786 break;
787
788 case 'e':
789 {
790 jsval rval;
791
792 if (++i == argc)
793 return usage();
794
795 /* Pass a filename of -e to imitate PERL */
796 JS_EvaluateScript(cx, obj, argv[i], strlen(argv[i]),
797 "-e", 1, &rval);
798
799 isInteractive = JS_FALSE;
800 break;
801
802 }
803 case 'C':
804 compileOnly = JS_TRUE;
805 isInteractive = JS_FALSE;
806 break;
807
808 case 'i':
809 isInteractive = forceTTY = JS_TRUE;
810 break;
811
812 case 'S':
813 if (++i == argc)
814 return usage();
815
816 /* Set maximum stack size. */
817 gMaxStackSize = atoi(argv[i]);
818 break;
819
820 case 'z':
821 obj = split_setup(cx);
822 if (!obj)
823 return gExitCode;
824 break;
825 #ifdef MOZ_SHARK
826 case 'k':
827 JS_ConnectShark();
828 break;
829 #endif
830 #ifdef MOZ_TRACEVIS
831 case 'T':
832 if (++i == argc)
833 return usage();
834
835 JS_StartTraceVis(argv[i]);
836 break;
837 #endif
838 default:
839 return usage();
840 }
841 }
842
843 if (filename || isInteractive)
844 Process(cx, obj, filename, forceTTY);
845 return gExitCode;
846 }
847
848 static JSBool
849 Version(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
850 {
851 if (argc > 0 && JSVAL_IS_INT(argv[0]))
852 *rval = INT_TO_JSVAL(JS_SetVersion(cx, (JSVersion) JSVAL_TO_INT(argv[0])));
853 else
854 *rval = INT_TO_JSVAL(JS_GetVersion(cx));
855 return JS_TRUE;
856 }
857
858 static JSBool
859 Options(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
860 {
861 uint32 optset, flag;
862 JSString *str;
863 const char *opt;
864 char *names;
865 JSBool found;
866
867 optset = 0;
868 for (uintN i = 0; i < argc; i++) {
869 str = JS_ValueToString(cx, argv[i]);
870 if (!str)
871 return JS_FALSE;
872 argv[i] = STRING_TO_JSVAL(str);
873 opt = JS_GetStringBytes(str);
874 if (!opt)
875 return JS_FALSE;
876 flag = MapContextOptionNameToFlag(cx, opt);
877 if (!flag)
878 return JS_FALSE;
879 optset |= flag;
880 }
881 optset = JS_ToggleOptions(cx, optset);
882
883 names = NULL;
884 found = JS_FALSE;
885 for (size_t i = 0; i != JS_ARRAY_LENGTH(js_options); i++) {
886 if (js_options[i].flag & optset) {
887 found = JS_TRUE;
888 names = JS_sprintf_append(names, "%s%s",
889 names ? "," : "", js_options[i].name);
890 if (!names)
891 break;
892 }
893 }
894 if (!found)
895 names = strdup("");
896 if (!names) {
897 JS_ReportOutOfMemory(cx);
898 return JS_FALSE;
899 }
900 str = JS_NewString(cx, names, strlen(names));
901 if (!str) {
902 free(names);
903 return JS_FALSE;
904 }
905 *rval = STRING_TO_JSVAL(str);
906 return JS_TRUE;
907 }
908
909 static JSBool
910 Load(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
911 {
912 uintN i;
913 JSString *str;
914 const char *filename;
915 JSScript *script;
916 JSBool ok;
917 uint32 oldopts;
918
919 for (i = 0; i < argc; i++) {
920 str = JS_ValueToString(cx, argv[i]);
921 if (!str)
922 return JS_FALSE;
923 argv[i] = STRING_TO_JSVAL(str);
924 filename = JS_GetStringBytes(str);
925 errno = 0;
926 oldopts = JS_GetOptions(cx);
927 JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO | JSOPTION_NO_SCRIPT_RVAL);
928 script = JS_CompileFile(cx, obj, filename);
929 JS_SetOptions(cx, oldopts);
930 if (!script) {
931 ok = JS_FALSE;
932 } else {
933 ok = !compileOnly
934 ? JS_ExecuteScript(cx, obj, script, NULL)
935 : JS_TRUE;
936 JS_DestroyScript(cx, script);
937 }
938 if (!ok)
939 return JS_FALSE;
940 }
941
942 return JS_TRUE;
943 }
944
945 /*
946 * function readline()
947 * Provides a hook for scripts to read a line from stdin.
948 */
949 static JSBool
950 ReadLine(JSContext *cx, uintN argc, jsval *vp)
951 {
952 #define BUFSIZE 256
953 FILE *from;
954 char *buf, *tmp;
955 size_t bufsize, buflength, gotlength;
956 JSBool sawNewline;
957 JSString *str;
958
959 from = stdin;
960 buflength = 0;
961 bufsize = BUFSIZE;
962 buf = (char *) JS_malloc(cx, bufsize);
963 if (!buf)
964 return JS_FALSE;
965
966 sawNewline = JS_FALSE;
967 while ((gotlength =
968 js_fgets(buf + buflength, bufsize - buflength, from)) > 0) {
969 buflength += gotlength;
970
971 /* Are we done? */
972 if (buf[buflength - 1] == '\n') {
973 buf[buflength - 1] = '\0';
974 sawNewline = JS_TRUE;
975 break;
976 } else if (buflength < bufsize - 1) {
977 break;
978 }
979
980 /* Else, grow our buffer for another pass. */
981 bufsize *= 2;
982 if (bufsize > buflength) {
983 tmp = (char *) JS_realloc(cx, buf, bufsize);
984 } else {
985 JS_ReportOutOfMemory(cx);
986 tmp = NULL;
987 }
988
989 if (!tmp) {
990 JS_free(cx, buf);
991 return JS_FALSE;
992 }
993
994 buf = tmp;
995 }
996
997 /* Treat the empty string specially. */
998 if (buflength == 0) {
999 *vp = feof(from) ? JSVAL_NULL : JS_GetEmptyStringValue(cx);
1000 JS_free(cx, buf);
1001 return JS_TRUE;
1002 }
1003
1004 /* Shrink the buffer to the real size. */
1005 tmp = (char *) JS_realloc(cx, buf, buflength);
1006 if (!tmp) {
1007 JS_free(cx, buf);
1008 return JS_FALSE;
1009 }
1010
1011 buf = tmp;
1012
1013 /*
1014 * Turn buf into a JSString. Note that buflength includes the trailing null
1015 * character.
1016 */
1017 str = JS_NewString(cx, buf, sawNewline ? buflength - 1 : buflength);
1018 if (!str) {
1019 JS_free(cx, buf);
1020 return JS_FALSE;
1021 }
1022
1023 *vp = STRING_TO_JSVAL(str);
1024 return JS_TRUE;
1025 }
1026
1027 static JSBool
1028 Print(JSContext *cx, uintN argc, jsval *vp)
1029 {
1030 jsval *argv;
1031 uintN i;
1032 JSString *str;
1033 char *bytes;
1034
1035 argv = JS_ARGV(cx, vp);
1036 for (i = 0; i < argc; i++) {
1037 str = JS_ValueToString(cx, argv[i]);
1038 if (!str)
1039 return JS_FALSE;
1040 bytes = JS_EncodeString(cx, str);
1041 if (!bytes)
1042 return JS_FALSE;
1043 fprintf(gOutFile, "%s%s", i ? " " : "", bytes);
1044 JS_free(cx, bytes);
1045 }
1046
1047 fputc('\n', gOutFile);
1048 fflush(gOutFile);
1049
1050 JS_SET_RVAL(cx, vp, JSVAL_VOID);
1051 return JS_TRUE;
1052 }
1053
1054 static JSBool
1055 Help(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
1056
1057 static JSBool
1058 Quit(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1059 {
1060 JS_ConvertArguments(cx, argc, argv,"/ i", &gExitCode);
1061
1062 gQuitting = JS_TRUE;
1063 return JS_FALSE;
1064 }
1065
1066 static const char *
1067 ToSource(JSContext *cx, jsval *vp)
1068 {
1069 JSString *str = JS_ValueToSource(cx, *vp);
1070 if (str) {
1071 *vp = STRING_TO_JSVAL(str);
1072 return JS_GetStringBytes(str);
1073 }
1074 JS_ClearPendingException(cx);
1075 return "<<error converting value to string>>";
1076 }
1077
1078 static JSBool
1079 AssertEq(JSContext *cx, uintN argc, jsval *vp)
1080 {
1081 if (!(argc == 2 || (argc == 3 && JSVAL_IS_STRING(JS_ARGV(cx, vp)[2])))) {
1082 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
1083 (argc < 2)
1084 ? JSSMSG_NOT_ENOUGH_ARGS
1085 : (argc == 3)
1086 ? JSSMSG_INVALID_ARGS
1087 : JSSMSG_TOO_MANY_ARGS,
1088 "assertEq");
1089 return JS_FALSE;
1090 }
1091
1092 jsval *argv = JS_ARGV(cx, vp);
1093 if (!JS_SameValue(cx, argv[0], argv[1])) {
1094 const char *actual = ToSource(cx, &argv[0]);
1095 const char *expected = ToSource(cx, &argv[1]);
1096 if (argc == 2) {
1097 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_ASSERT_EQ_FAILED,
1098 actual, expected);
1099 } else {
1100 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_ASSERT_EQ_FAILED_MSG,
1101 actual, expected, JS_GetStringBytes(JSVAL_TO_STRING(argv[2])));
1102 }
1103 return JS_FALSE;
1104 }
1105 JS_SET_RVAL(cx, vp, JSVAL_VOID);
1106 return JS_TRUE;
1107 }
1108
1109 static JSBool
1110 GC(JSContext *cx, uintN argc, jsval *vp)
1111 {
1112 JSRuntime *rt;
1113 uint32 preBytes;
1114
1115 rt = cx->runtime;
1116 preBytes = rt->gcBytes;
1117 JS_GC(cx);
1118
1119 char buf[256];
1120 JS_snprintf(buf, sizeof(buf), "before %lu, after %lu, break %08lx\n",
1121 (unsigned long)preBytes, (unsigned long)rt->gcBytes,
1122 #ifdef HAVE_SBRK
1123 (unsigned long)sbrk(0)
1124 #else
1125 0
1126 #endif
1127 );
1128 #ifdef JS_GCMETER
1129 js_DumpGCStats(rt, stdout);
1130 #endif
1131 *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, buf));
1132 return JS_TRUE;
1133 }
1134
1135 static JSBool
1136 GCParameter(JSContext *cx, uintN argc, jsval *vp)
1137 {
1138 JSString *str;
1139 const char *paramName;
1140 JSGCParamKey param;
1141 uint32 value;
1142
1143 if (argc == 0) {
1144 str = JS_ValueToString(cx, JSVAL_VOID);
1145 JS_ASSERT(str);
1146 } else {
1147 str = JS_ValueToString(cx, vp[2]);
1148 if (!str)
1149 return JS_FALSE;
1150 vp[2] = STRING_TO_JSVAL(str);
1151 }
1152 paramName = JS_GetStringBytes(str);
1153 if (!paramName)
1154 return JS_FALSE;
1155 if (strcmp(paramName, "maxBytes") == 0) {
1156 param = JSGC_MAX_BYTES;
1157 } else if (strcmp(paramName, "maxMallocBytes") == 0) {
1158 param = JSGC_MAX_MALLOC_BYTES;
1159 } else if (strcmp(paramName, "gcStackpoolLifespan") == 0) {
1160 param = JSGC_STACKPOOL_LIFESPAN;
1161 } else if (strcmp(paramName, "gcBytes") == 0) {
1162 param = JSGC_BYTES;
1163 } else if (strcmp(paramName, "gcNumber") == 0) {
1164 param = JSGC_NUMBER;
1165 } else if (strcmp(paramName, "gcTriggerFactor") == 0) {
1166 param = JSGC_TRIGGER_FACTOR;
1167 } else {
1168 JS_ReportError(cx,
1169 "the first argument argument must be maxBytes, "
1170 "maxMallocBytes, gcStackpoolLifespan, gcBytes, "
1171 "gcNumber or gcTriggerFactor");
1172 return JS_FALSE;
1173 }
1174
1175 if (argc == 1) {
1176 value = JS_GetGCParameter(cx->runtime, param);
1177 return JS_NewNumberValue(cx, value, &vp[0]);
1178 }
1179
1180 if (param == JSGC_NUMBER ||
1181 param == JSGC_BYTES) {
1182 JS_ReportError(cx, "Attempt to change read-only parameter %s",
1183 paramName);
1184 return JS_FALSE;
1185 }
1186
1187 if (!JS_ValueToECMAUint32(cx, vp[3], &value)) {
1188 JS_ReportError(cx,
1189 "the second argument must be convertable to uint32 "
1190 "with non-zero value");
1191 return JS_FALSE;
1192 }
1193 if (param == JSGC_TRIGGER_FACTOR && value < 100) {
1194 JS_ReportError(cx,
1195 "the gcTriggerFactor value must be >= 100");
1196 return JS_FALSE;
1197 }
1198 JS_SetGCParameter(cx->runtime, param, value);
1199 *vp = JSVAL_VOID;
1200 return JS_TRUE;
1201 }
1202
1203 #ifdef JS_GC_ZEAL
1204 static JSBool
1205 GCZeal(JSContext *cx, uintN argc, jsval *vp)
1206 {
1207 uint32 zeal;
1208
1209 if (!JS_ValueToECMAUint32(cx, argc == 0 ? JSVAL_VOID : vp[2], &zeal))
1210 return JS_FALSE;
1211 JS_SetGCZeal(cx, (uint8)zeal);
1212 *vp = JSVAL_VOID;
1213 return JS_TRUE;
1214 }
1215 #endif /* JS_GC_ZEAL */
1216
1217 typedef struct JSCountHeapNode JSCountHeapNode;
1218
1219 struct JSCountHeapNode {
1220 void *thing;
1221 int32 kind;
1222 JSCountHeapNode *next;
1223 };
1224
1225 typedef struct JSCountHeapTracer {
1226 JSTracer base;
1227 JSDHashTable visited;
1228 JSBool ok;
1229 JSCountHeapNode *traceList;
1230 JSCountHeapNode *recycleList;
1231 } JSCountHeapTracer;
1232
1233 static void
1234 CountHeapNotify(JSTracer *trc, void *thing, uint32 kind)
1235 {
1236 JSCountHeapTracer *countTracer;
1237 JSDHashEntryStub *entry;
1238 JSCountHeapNode *node;
1239
1240 JS_ASSERT(trc->callback == CountHeapNotify);
1241 countTracer = (JSCountHeapTracer *)trc;
1242 if (!countTracer->ok)
1243 return;
1244
1245 entry = (JSDHashEntryStub *)
1246 JS_DHashTableOperate(&countTracer->visited, thing, JS_DHASH_ADD);
1247 if (!entry) {
1248 JS_ReportOutOfMemory(trc->context);
1249 countTracer->ok = JS_FALSE;
1250 return;
1251 }
1252 if (entry->key)
1253 return;
1254 entry->key = thing;
1255
1256 node = countTracer->recycleList;
1257 if (node) {
1258 countTracer->recycleList = node->next;
1259 } else {
1260 node = (JSCountHeapNode *) JS_malloc(trc->context, sizeof *node);
1261 if (!node) {
1262 countTracer->ok = JS_FALSE;
1263 return;
1264 }
1265 }
1266 node->thing = thing;
1267 node->kind = kind;
1268 node->next = countTracer->traceList;
1269 countTracer->traceList = node;
1270 }
1271
1272 static JSBool
1273 CountHeap(JSContext *cx, uintN argc, jsval *vp)
1274 {
1275 void* startThing;
1276 int32 startTraceKind;
1277 jsval v;
1278 int32 traceKind, i;
1279 JSString *str;
1280 char *bytes;
1281 JSCountHeapTracer countTracer;
1282 JSCountHeapNode *node;
1283 size_t counter;
1284
1285 static const struct {
1286 const char *name;
1287 int32 kind;
1288 } traceKindNames[] = {
1289 { "all", -1 },
1290 { "object", JSTRACE_OBJECT },
1291 { "double", JSTRACE_DOUBLE },
1292 { "string", JSTRACE_STRING },
1293 #if JS_HAS_XML_SUPPORT
1294 { "xml", JSTRACE_XML },
1295 #endif
1296 };
1297
1298 startThing = NULL;
1299 startTraceKind = 0;
1300 if (argc > 0) {
1301 v = JS_ARGV(cx, vp)[0];
1302 if (JSVAL_IS_TRACEABLE(v)) {
1303 startThing = JSVAL_TO_TRACEABLE(v);
1304 startTraceKind = JSVAL_TRACE_KIND(v);
1305 } else if (v != JSVAL_NULL) {
1306 JS_ReportError(cx,
1307 "the first argument is not null or a heap-allocated "
1308 "thing");
1309 return JS_FALSE;
1310 }
1311 }
1312
1313 traceKind = -1;
1314 if (argc > 1) {
1315 str = JS_ValueToString(cx, JS_ARGV(cx, vp)[1]);
1316 if (!str)
1317 return JS_FALSE;
1318 bytes = JS_GetStringBytes(str);
1319 if (!bytes)
1320 return JS_FALSE;
1321 for (i = 0; ;) {
1322 if (strcmp(bytes, traceKindNames[i].name) == 0) {
1323 traceKind = traceKindNames[i].kind;
1324 break;
1325 }
1326 if (++i == JS_ARRAY_LENGTH(traceKindNames)) {
1327 JS_ReportError(cx, "trace kind name '%s' is unknown", bytes);
1328 return JS_FALSE;
1329 }
1330 }
1331 }
1332
1333 JS_TRACER_INIT(&countTracer.base, cx, CountHeapNotify);
1334 if (!JS_DHashTableInit(&countTracer.visited, JS_DHashGetStubOps(),
1335 NULL, sizeof(JSDHashEntryStub),
1336 JS_DHASH_DEFAULT_CAPACITY(100))) {
1337 JS_ReportOutOfMemory(cx);
1338 return JS_FALSE;
1339 }
1340 countTracer.ok = JS_TRUE;
1341 countTracer.traceList = NULL;
1342 countTracer.recycleList = NULL;
1343
1344 if (!startThing) {
1345 JS_TraceRuntime(&countTracer.base);
1346 } else {
1347 JS_SET_TRACING_NAME(&countTracer.base, "root");
1348 JS_CallTracer(&countTracer.base, startThing, startTraceKind);
1349 }
1350
1351 counter = 0;
1352 while ((node = countTracer.traceList) != NULL) {
1353 if (traceKind == -1 || node->kind == traceKind)
1354 counter++;
1355 countTracer.traceList = node->next;
1356 node->next = countTracer.recycleList;
1357 countTracer.recycleList = node;
1358 JS_TraceChildren(&countTracer.base, node->thing, node->kind);
1359 }
1360 while ((node = countTracer.recycleList) != NULL) {
1361 countTracer.recycleList = node->next;
1362 JS_free(cx, node);
1363 }
1364 JS_DHashTableFinish(&countTracer.visited);
1365
1366 return countTracer.ok && JS_NewNumberValue(cx, (jsdouble) counter, vp);
1367 }
1368
1369 static JSScript *
1370 ValueToScript(JSContext *cx, jsval v)
1371 {
1372 JSScript *script = NULL;
1373 JSFunction *fun;
1374
1375 if (!JSVAL_IS_PRIMITIVE(v)) {
1376 JSObject *obj = JSVAL_TO_OBJECT(v);
1377 JSClass *clasp = JS_GET_CLASS(cx, obj);
1378
1379 if (clasp == &js_ScriptClass) {
1380 script = (JSScript *) JS_GetPrivate(cx, obj);
1381 } else if (clasp == &js_GeneratorClass) {
1382 JSGenerator *gen = (JSGenerator *) JS_GetPrivate(cx, obj);
1383 fun = gen->frame.fun;
1384 script = FUN_SCRIPT(fun);
1385 }
1386 }
1387
1388 if (!script) {
1389 fun = JS_ValueToFunction(cx, v);
1390 if (!fun)
1391 return NULL;
1392 script = FUN_SCRIPT(fun);
1393 if (!script) {
1394 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
1395 JSSMSG_SCRIPTS_ONLY);
1396 }
1397 }
1398
1399 return script;
1400 }
1401
1402 static JSBool
1403 GetTrapArgs(JSContext *cx, uintN argc, jsval *argv, JSScript **scriptp,
1404 int32 *ip)
1405 {
1406 jsval v;
1407 uintN intarg;
1408 JSScript *script;
1409
1410 *scriptp = JS_GetScriptedCaller(cx, NULL)->script;
1411 *ip = 0;
1412 if (argc != 0) {
1413 v = argv[0];
1414 intarg = 0;
1415 if (!JSVAL_IS_PRIMITIVE(v) &&
1416 (JS_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_FunctionClass ||
1417 JS_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_ScriptClass)) {
1418 script = ValueToScript(cx, v);
1419 if (!script)
1420 return JS_FALSE;
1421 *scriptp = script;
1422 intarg++;
1423 }
1424 if (argc > intarg) {
1425 if (!JS_ValueToInt32(cx, argv[intarg], ip))
1426 return JS_FALSE;
1427 }
1428 }
1429 return JS_TRUE;
1430 }
1431
1432 static JSTrapStatus
1433 TrapHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval,
1434 void *closure)
1435 {
1436 JSString *str;
1437 JSStackFrame *caller;
1438
1439 str = (JSString *) closure;
1440 caller = JS_GetScriptedCaller(cx, NULL);
1441 if (!JS_EvaluateScript(cx, caller->scopeChain,
1442 JS_GetStringBytes(str), JS_GetStringLength(str),
1443 caller->script->filename, caller->script->lineno,
1444 rval)) {
1445 return JSTRAP_ERROR;
1446 }
1447 if (!JSVAL_IS_VOID(*rval))
1448 return JSTRAP_RETURN;
1449 return JSTRAP_CONTINUE;
1450 }
1451
1452 static JSBool
1453 Trap(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1454 {
1455 JSString *str;
1456 JSScript *script;
1457 int32 i;
1458
1459 if (argc == 0) {
1460 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_TRAP_USAGE);
1461 return JS_FALSE;
1462 }
1463 argc--;
1464 str = JS_ValueToString(cx, argv[argc]);
1465 if (!str)
1466 return JS_FALSE;
1467 argv[argc] = STRING_TO_JSVAL(str);
1468 if (!GetTrapArgs(cx, argc, argv, &script, &i))
1469 return JS_FALSE;
1470 return JS_SetTrap(cx, script, script->code + i, TrapHandler, str);
1471 }
1472
1473 static JSBool
1474 Untrap(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1475 {
1476 JSScript *script;
1477 int32 i;
1478
1479 if (!GetTrapArgs(cx, argc, argv, &script, &i))
1480 return JS_FALSE;
1481 JS_ClearTrap(cx, script, script->code + i, NULL, NULL);
1482 return JS_TRUE;
1483 }
1484
1485 static JSBool
1486 LineToPC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1487 {
1488 JSScript *script;
1489 int32 i;
1490 uintN lineno;
1491 jsbytecode *pc;
1492
1493 if (argc == 0) {
1494 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_LINE2PC_USAGE);
1495 return JS_FALSE;
1496 }
1497 script = JS_GetScriptedCaller(cx, NULL)->script;
1498 if (!GetTrapArgs(cx, argc, argv, &script, &i))
1499 return JS_FALSE;
1500 lineno = (i == 0) ? script->lineno : (uintN)i;
1501 pc = JS_LineNumberToPC(cx, script, lineno);
1502 if (!pc)
1503 return JS_FALSE;
1504 *rval = INT_TO_JSVAL(pc - script->code);
1505 return JS_TRUE;
1506 }
1507
1508 static JSBool
1509 PCToLine(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1510 {
1511 JSScript *script;
1512 int32 i;
1513 uintN lineno;
1514
1515 if (!GetTrapArgs(cx, argc, argv, &script, &i))
1516 return JS_FALSE;
1517 lineno = JS_PCToLineNumber(cx, script, script->code + i);
1518 if (!lineno)
1519 return JS_FALSE;
1520 *rval = INT_TO_JSVAL(lineno);
1521 return JS_TRUE;
1522 }
1523
1524 #ifdef DEBUG
1525
1526 static void
1527 UpdateSwitchTableBounds(JSContext *cx, JSScript *script, uintN offset,
1528 uintN *start, uintN *end)
1529 {
1530 jsbytecode *pc;
1531 JSOp op;
1532 ptrdiff_t jmplen;
1533 jsint low, high, n;
1534
1535 pc = script->code + offset;
1536 op = js_GetOpcode(cx, script, pc);
1537 switch (op) {
1538 case JSOP_TABLESWITCHX:
1539 jmplen = JUMPX_OFFSET_LEN;
1540 goto jump_table;
1541 case JSOP_TABLESWITCH:
1542 jmplen = JUMP_OFFSET_LEN;
1543 jump_table:
1544 pc += jmplen;
1545 low = GET_JUMP_OFFSET(pc);
1546 pc += JUMP_OFFSET_LEN;
1547 high = GET_JUMP_OFFSET(pc);
1548 pc += JUMP_OFFSET_LEN;
1549 n = high - low + 1;
1550 break;
1551
1552 case JSOP_LOOKUPSWITCHX:
1553 jmplen = JUMPX_OFFSET_LEN;
1554 goto lookup_table;
1555 case JSOP_LOOKUPSWITCH:
1556 jmplen = JUMP_OFFSET_LEN;
1557 lookup_table:
1558 pc += jmplen;
1559 n = GET_INDEX(pc);
1560 pc += INDEX_LEN;
1561 jmplen += JUMP_OFFSET_LEN;
1562 break;
1563
1564 default:
1565 /* [condswitch] switch does not have any jump or lookup tables. */
1566 JS_ASSERT(op == JSOP_CONDSWITCH);
1567 return;
1568 }
1569
1570 *start = (uintN)(pc - script->code);
1571 *end = *start + (uintN)(n * jmplen);
1572 }
1573
1574 static void
1575 SrcNotes(JSContext *cx, JSScript *script)
1576 {
1577 uintN offset, delta, caseOff, switchTableStart, switchTableEnd;
1578 jssrcnote *notes, *sn;
1579 JSSrcNoteType type;
1580 const char *name;
1581 uint32 index;
1582 JSAtom *atom;
1583 JSString *str;
1584
1585 fprintf(gOutFile, "\nSource notes:\n");
1586 offset = 0;
1587 notes = script->notes();
1588 switchTableEnd = switchTableStart = 0;
1589 for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
1590 delta = SN_DELTA(sn);
1591 offset += delta;
1592 type = (JSSrcNoteType) SN_TYPE(sn);
1593 name = js_SrcNoteSpec[type].name;
1594 if (type == SRC_LABEL) {
1595 /* Check if the source note is for a switch case. */
1596 if (switchTableStart <= offset && offset < switchTableEnd) {
1597 name = "case";
1598 } else {
1599 JS_ASSERT(js_GetOpcode(cx, script, script->code + offset) == JSOP_NOP);
1600 }
1601 }
1602 fprintf(gOutFile, "%3u: %5u [%4u] %-8s",
1603 (uintN) (sn - notes), offset, delta, name);
1604 switch (type) {
1605 case SRC_SETLINE:
1606 fprintf(gOutFile, " lineno %u", (uintN) js_GetSrcNoteOffset(sn, 0));
1607 break;
1608 case SRC_FOR:
1609 fprintf(gOutFile, " cond %u update %u tail %u",
1610 (uintN) js_GetSrcNoteOffset(sn, 0),
1611 (uintN) js_GetSrcNoteOffset(sn, 1),
1612 (uintN) js_GetSrcNoteOffset(sn, 2));
1613 break;
1614 case SRC_IF_ELSE:
1615 fprintf(gOutFile, " else %u elseif %u",
1616 (uintN) js_GetSrcNoteOffset(sn, 0),
1617 (uintN) js_GetSrcNoteOffset(sn, 1));
1618 break;
1619 case SRC_COND:
1620 case SRC_WHILE:
1621 case SRC_PCBASE:
1622 case SRC_PCDELTA:
1623 case SRC_DECL:
1624 case SRC_BRACE:
1625 fprintf(gOutFile, " offset %u", (uintN) js_GetSrcNoteOffset(sn, 0));
1626 break;
1627 case SRC_LABEL:
1628 case SRC_LABELBRACE:
1629 case SRC_BREAK2LABEL:
1630 case SRC_CONT2LABEL:
1631 index = js_GetSrcNoteOffset(sn, 0);
1632 JS_GET_SCRIPT_ATOM(script, NULL, index, atom);
1633 JS_ASSERT(ATOM_IS_STRING(atom));
1634 str = ATOM_TO_STRING(atom);
1635 fprintf(gOutFile, " atom %u (", index);
1636 js_FileEscapedString(gOutFile, str, 0);
1637 putc(')', gOutFile);
1638 break;
1639 case SRC_FUNCDEF: {
1640 const char *bytes;
1641 JSObject *obj;
1642 JSFunction *fun;
1643
1644 index = js_GetSrcNoteOffset(sn, 0);
1645 obj = script->getObject(index);
1646 fun = (JSFunction *) JS_GetPrivate(cx, obj);
1647 str = JS_DecompileFunction(cx, fun, JS_DONT_PRETTY_PRINT);
1648 if (str) {
1649 bytes = JS_GetStringBytes(str);
1650 } else {
1651 if (JS_IsExceptionPending(cx)) {
1652 if (!JS_ReportPendingException(cx))
1653 JS_ClearPendingException(cx);
1654 }
1655 bytes = "N/A";
1656 }
1657 fprintf(gOutFile, " function %u (%s)", index, bytes);
1658 break;
1659 }
1660 case SRC_SWITCH:
1661 fprintf(gOutFile, " length %u", (uintN) js_GetSrcNoteOffset(sn, 0));
1662 caseOff = (uintN) js_GetSrcNoteOffset(sn, 1);
1663 if (caseOff)
1664 fprintf(gOutFile, " first case offset %u", caseOff);
1665 UpdateSwitchTableBounds(cx, script, offset,
1666 &switchTableStart, &switchTableEnd);
1667 break;
1668 case SRC_CATCH:
1669 delta = (uintN) js_GetSrcNoteOffset(sn, 0);
1670 if (delta) {
1671 if (script->main[offset] == JSOP_LEAVEBLOCK)
1672 fprintf(gOutFile, " stack depth %u", delta);
1673 else
1674 fprintf(gOutFile, " guard delta %u", delta);
1675 }
1676 break;
1677 default:;
1678 }
1679 fputc('\n', gOutFile);
1680 }
1681 }
1682
1683 static JSBool
1684 Notes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1685 {
1686 uintN i;
1687 JSScript *script;
1688
1689 for (i = 0; i < argc; i++) {
1690 script = ValueToScript(cx, argv[i]);
1691 if (!script)
1692 continue;
1693
1694 SrcNotes(cx, script);
1695 }
1696 return JS_TRUE;
1697 }
1698
1699 JS_STATIC_ASSERT(JSTRY_CATCH == 0);
1700 JS_STATIC_ASSERT(JSTRY_FINALLY == 1);
1701 JS_STATIC_ASSERT(JSTRY_ITER == 2);
1702
1703 static const char* const TryNoteNames[] = { "catch", "finally", "iter" };
1704
1705 static JSBool
1706 TryNotes(JSContext *cx, JSScript *script)
1707 {
1708 JSTryNote *tn, *tnlimit;
1709
1710 if (script->trynotesOffset == 0)
1711 return JS_TRUE;
1712
1713 tn = script->trynotes()->vector;
1714 tnlimit = tn + script->trynotes()->length;
1715 fprintf(gOutFile, "\nException table:\n"
1716 "kind stack start end\n");
1717 do {
1718 JS_ASSERT(tn->kind < JS_ARRAY_LENGTH(TryNoteNames));
1719 fprintf(gOutFile, " %-7s %6u %8u %8u\n",
1720 TryNoteNames[tn->kind], tn->stackDepth,
1721 tn->start, tn->start + tn->length);
1722 } while (++tn != tnlimit);
1723 return JS_TRUE;
1724 }
1725
1726 static bool
1727 DisassembleValue(JSContext *cx, jsval v, bool lines, bool recursive)
1728 {
1729 JSScript *script = ValueToScript(cx, v);
1730 if (!script)
1731 return false;
1732 if (VALUE_IS_FUNCTION(cx, v)) {
1733 JSFunction *fun = JS_ValueToFunction(cx, v);
1734 if (fun && (fun->flags & ~7U)) {
1735 uint16 flags = fun->flags;
1736 fputs("flags:", stdout);
1737
1738 #define SHOW_FLAG(flag) if (flags & JSFUN_##flag) fputs(" " #flag, stdout);
1739
1740 SHOW_FLAG(LAMBDA);
1741 SHOW_FLAG(SETTER);
1742 SHOW_FLAG(GETTER);
1743 SHOW_FLAG(BOUND_METHOD);
1744 SHOW_FLAG(HEAVYWEIGHT);
1745 SHOW_FLAG(THISP_STRING);
1746 SHOW_FLAG(THISP_NUMBER);
1747 SHOW_FLAG(THISP_BOOLEAN);
1748 SHOW_FLAG(EXPR_CLOSURE);
1749 SHOW_FLAG(TRCINFO);
1750
1751 #undef SHOW_FLAG
1752
1753 if (FUN_NULL_CLOSURE(fun))
1754 fputs(" NULL_CLOSURE", stdout);
1755 else if (FUN_FLAT_CLOSURE(fun))
1756 fputs(" FLAT_CLOSURE", stdout);
1757 putchar('\n');
1758 }
1759 }
1760
1761 if (!js_Disassemble(cx, script, lines, stdout))
1762 return false;
1763 SrcNotes(cx, script);
1764 TryNotes(cx, script);
1765
1766 if (recursive && script->objectsOffset != 0) {
1767 JSObjectArray *objects = script->objects();
1768 for (uintN i = 0; i != objects->length; ++i) {
1769 JSObject *obj = objects->vector[i];
1770 if (HAS_FUNCTION_CLASS(obj)) {
1771 putchar('\n');
1772 if (!DisassembleValue(cx, OBJECT_TO_JSVAL(obj),
1773 lines, recursive)) {
1774 return false;
1775 }
1776 }
1777 }
1778 }
1779 return true;
1780 }
1781
1782 static JSBool
1783 Disassemble(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1784 {
1785 bool lines = false, recursive = false;
1786
1787 while (argc > 0 && JSVAL_IS_STRING(argv[0])) {
1788 const char *bytes = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
1789 lines = !strcmp(bytes, "-l");
1790 recursive = !strcmp(bytes, "-r");
1791 if (!lines && !recursive)
1792 break;
1793 argv++, argc--;
1794 }
1795
1796 for (uintN i = 0; i < argc; i++) {
1797 if (!DisassembleValue(cx, argv[i], lines, recursive))
1798 return false;
1799 }
1800 return true;
1801 }
1802
1803 static JSBool
1804 DisassFile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1805 {
1806 JSString *str;
1807 const char *filename;
1808 JSScript *script;
1809 JSBool ok;
1810 uint32 oldopts;
1811
1812 if (!argc)
1813 return JS_TRUE;
1814
1815 str = JS_ValueToString(cx, argv[0]);
1816 if (!str)
1817 return JS_FALSE;
1818 argv[0] = STRING_TO_JSVAL(str);
1819
1820 filename = JS_GetStringBytes(str);
1821 oldopts = JS_GetOptions(cx);
1822 JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO | JSOPTION_NO_SCRIPT_RVAL);
1823 script = JS_CompileFile(cx, obj, filename);
1824 JS_SetOptions(cx, oldopts);
1825 if (!script)
1826 return JS_FALSE;
1827
1828 obj = JS_NewScriptObject(cx, script);
1829 if (!obj)
1830 return JS_FALSE;
1831
1832 *rval = OBJECT_TO_JSVAL(obj); /* I like to root it, root it. */
1833 ok = Disassemble(cx, obj, 1, rval, rval); /* gross, but works! */
1834 *rval = JSVAL_VOID;
1835
1836 return ok;
1837 }
1838
1839 static JSBool
1840 DisassWithSrc(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1841 jsval *rval)
1842 {
1843 #define LINE_BUF_LEN 512
1844 uintN i, len, line1, line2, bupline;
1845 JSScript *script;
1846 FILE *file;
1847 char linebuf[LINE_BUF_LEN];
1848 jsbytecode *pc, *end;
1849 JSBool ok;
1850 static char sep[] = ";-------------------------";
1851
1852 ok = JS_TRUE;
1853 for (i = 0; ok && i < argc; i++) {
1854 script = ValueToScript(cx, argv[i]);
1855 if (!script)
1856 return JS_FALSE;
1857
1858 if (!script->filename) {
1859 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
1860 JSSMSG_FILE_SCRIPTS_ONLY);
1861 return JS_FALSE;
1862 }
1863
1864 file = fopen(script->filename, "r");
1865 if (!file) {
1866 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
1867 JSSMSG_CANT_OPEN, script->filename,
1868 strerror(errno));
1869 return JS_FALSE;
1870 }
1871
1872 pc = script->code;
1873 end = pc + script->length;
1874
1875 /* burn the leading lines */
1876 line2 = JS_PCToLineNumber(cx, script, pc);
1877 for (line1 = 0; line1 < line2 - 1; line1++)
1878 fgets(linebuf, LINE_BUF_LEN, file);
1879
1880 bupline = 0;
1881 while (pc < end) {
1882 line2 = JS_PCToLineNumber(cx, script, pc);
1883
1884 if (line2 < line1) {
1885 if (bupline != line2) {
1886 bupline = line2;
1887 fprintf(gOutFile, "%s %3u: BACKUP\n", sep, line2);
1888 }
1889 } else {
1890 if (bupline && line1 == line2)
1891 fprintf(gOutFile, "%s %3u: RESTORE\n", sep, line2);
1892 bupline = 0;
1893 while (line1 < line2) {
1894 if (!fgets(linebuf, LINE_BUF_LEN, file)) {
1895 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
1896 JSSMSG_UNEXPECTED_EOF,
1897 script->filename);
1898 ok = JS_FALSE;
1899 goto bail;
1900 }
1901 line1++;
1902 fprintf(gOutFile, "%s %3u: %s", sep, line1, linebuf);
1903 }
1904 }
1905
1906 len = js_Disassemble1(cx, script, pc,
1907 pc - script->code,
1908 JS_TRUE, stdout);
1909 if (!len) {
1910 ok = JS_FALSE;
1911 goto bail;
1912 }
1913 pc += len;
1914 }
1915
1916 bail:
1917 fclose(file);
1918 }
1919 return ok;
1920 #undef LINE_BUF_LEN
1921 }
1922
1923 static JSBool
1924 Tracing(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1925 {
1926 FILE *file;
1927
1928 if (argc == 0) {
1929 *rval = BOOLEAN_TO_JSVAL(cx->tracefp != 0);
1930 return JS_TRUE;
1931 }
1932
1933 switch (JS_TypeOfValue(cx, argv[0])) {
1934 case JSTYPE_NUMBER:
1935 case JSTYPE_BOOLEAN: {
1936 JSBool bval;
1937 JS_ValueToBoolean(cx, argv[0], &bval);
1938 file = bval ? stderr : NULL;
1939 break;
1940 }
1941 case JSTYPE_STRING: {
1942 char *name = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
1943 file = fopen(name, "w");
1944 if (!file) {
1945 JS_ReportError(cx, "tracing: couldn't open output file %s: %s",
1946 name, strerror(errno));
1947 return JS_FALSE;
1948 }
1949 break;
1950 }
1951 default:
1952 goto bad_argument;
1953 }
1954 if (cx->tracefp && cx->tracefp != stderr)
1955 fclose((FILE *)cx->tracefp);
1956 cx->tracefp = file;
1957 cx->tracePrevPc = NULL;
1958 return JS_TRUE;
1959
1960 bad_argument:
1961 JSString *str = JS_ValueToString(cx, argv[0]);
1962 if (!str)
1963 return JS_FALSE;
1964 JS_ReportError(cx, "tracing: illegal argument %s",
1965 JS_GetStringBytes(str));
1966 return JS_FALSE;
1967 }
1968
1969 static void
1970 DumpScope(JSContext *cx, JSObject *obj, FILE *fp)
1971 {
1972 uintN i;
1973 JSScopeProperty *sprop;
1974 jsval v;
1975 JSString *str;
1976
1977 i = 0;
1978 sprop = NULL;
1979 while (JS_PropertyIterator(obj, &sprop)) {
1980 fprintf(fp, "%3u %p ", i, (void *)sprop);
1981
1982 v = ID_TO_VALUE(sprop->id);
1983 if (JSID_IS_INT(sprop->id)) {
1984 fprintf(fp, "[%ld]", (long)JSVAL_TO_INT(v));
1985 } else {
1986 if (JSID_IS_ATOM(sprop->id)) {
1987 str = JSVAL_TO_STRING(v);
1988 } else {
1989 JS_ASSERT(JSID_IS_OBJECT(sprop->id));
1990 str = js_ValueToString(cx, v);
1991 fputs("object ", fp);
1992 }
1993 if (!str)
1994 fputs("<error>", fp);
1995 else
1996 js_FileEscapedString(fp, str, '"');
1997 }
1998 #define DUMP_ATTR(name) if (sprop->attrs & JSPROP_##name) fputs(" " #name, fp)
1999 DUMP_ATTR(ENUMERATE);
2000 DUMP_ATTR(READONLY);
2001 DUMP_ATTR(PERMANENT);
2002 DUMP_ATTR(GETTER);
2003 DUMP_ATTR(SETTER);
2004 #undef DUMP_ATTR
2005
2006 fprintf(fp, " slot %lu flags %x shortid %d\n",
2007 (unsigned long)sprop->slot, sprop->flags, sprop->shortid);
2008 }
2009 }
2010
2011 static JSBool
2012 DumpStats(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
2013 {
2014 uintN i;
2015 JSString *str;
2016 const char *bytes;
2017 jsid id;
2018 JSObject *obj2;
2019 JSProperty *prop;
2020 jsval value;
2021
2022 for (i = 0; i < argc; i++) {
2023 str = JS_ValueToString(cx, argv[i]);
2024 if (!str)
2025 return JS_FALSE;
2026 argv[i] = STRING_TO_JSVAL(str);
2027 bytes = JS_GetStringBytes(str);
2028 if (strcmp(bytes, "arena") == 0) {
2029 #ifdef JS_ARENAMETER
2030 JS_DumpArenaStats(stdout);
2031 #endif
2032 } else if (strcmp(bytes, "atom") == 0) {
2033 js_DumpAtoms(cx, gOutFile);
2034 } else if (strcmp(bytes, "global") == 0) {
2035 DumpScope(cx, cx->globalObject, stdout);
2036 } else {
2037 if (!JS_ValueToId(cx, STRING_TO_JSVAL(str), &id))
2038 return JS_FALSE;
2039 if (!js_FindProperty(cx, id, &obj, &obj2, &prop))
2040 return JS_FALSE;
2041 if (prop) {
2042 obj2->dropProperty(cx, prop);
2043 if (!obj->getProperty(cx, id, &value))
2044 return JS_FALSE;
2045 }
2046 if (!prop || !JSVAL_IS_OBJECT(value)) {
2047 fprintf(gErrFile, "js: invalid stats argument %s\n",
2048 bytes);
2049 continue;
2050 }
2051 obj = JSVAL_TO_OBJECT(value);
2052 if (obj)
2053 DumpScope(cx, obj, stdout);
2054 }
2055 }
2056 return JS_TRUE;
2057 }
2058
2059 static JSBool
2060 DumpHeap(JSContext *cx, uintN argc, jsval *vp)
2061 {
2062 char *fileName;
2063 jsval v;
2064 void* startThing;
2065 uint32 startTraceKind;
2066 const char *badTraceArg;
2067 void *thingToFind;
2068 size_t maxDepth;
2069 void *thingToIgnore;
2070 FILE *dumpFile;
2071 JSBool ok;
2072
2073 fileName = NULL;
2074 if (argc > 0) {
2075 v = JS_ARGV(cx, vp)[0];
2076 if (v != JSVAL_NULL) {
2077 JSString *str;
2078
2079 str = JS_ValueToString(cx, v);
2080 if (!str)
2081 return JS_FALSE;
2082 JS_ARGV(cx, vp)[0] = STRING_TO_JSVAL(str);
2083 fileName = JS_GetStringBytes(str);
2084 }
2085 }
2086
2087 startThing = NULL;
2088 startTraceKind = 0;
2089 if (argc > 1) {
2090 v = JS_ARGV(cx, vp)[1];
2091 if (JSVAL_IS_TRACEABLE(v)) {
2092 startThing = JSVAL_TO_TRACEABLE(v);
2093 startTraceKind = JSVAL_TRACE_KIND(v);
2094 } else if (v != JSVAL_NULL) {
2095 badTraceArg = "start";
2096 goto not_traceable_arg;
2097 }
2098 }
2099
2100 thingToFind = NULL;
2101 if (argc > 2) {
2102 v = JS_ARGV(cx, vp)[2];
2103 if (JSVAL_IS_TRACEABLE(v)) {
2104 thingToFind = JSVAL_TO_TRACEABLE(v);
2105 } else if (v != JSVAL_NULL) {
2106 badTraceArg = "toFind";
2107 goto not_traceable_arg;
2108 }
2109 }
2110
2111 maxDepth = (size_t)-1;
2112 if (argc > 3) {
2113 v = JS_ARGV(cx, vp)[3];
2114 if (v != JSVAL_NULL) {
2115 uint32 depth;
2116
2117 if (!JS_ValueToECMAUint32(cx, v, &depth))
2118 return JS_FALSE;
2119 maxDepth = depth;
2120 }
2121 }
2122
2123 thingToIgnore = NULL;
2124 if (argc > 4) {
2125 v = JS_ARGV(cx, vp)[4];
2126 if (JSVAL_IS_TRACEABLE(v)) {
2127 thingToIgnore = JSVAL_TO_TRACEABLE(v);
2128 } else if (v != JSVAL_NULL) {
2129 badTraceArg = "toIgnore";
2130 goto not_traceable_arg;
2131 }
2132 }
2133
2134 if (!fileName) {
2135 dumpFile = stdout;
2136 } else {
2137 dumpFile = fopen(fileName, "w");
2138 if (!dumpFile) {
2139 JS_ReportError(cx, "can't open %s: %s", fileName, strerror(errno));
2140 return JS_FALSE;
2141 }
2142 }
2143
2144 ok = JS_DumpHeap(cx, dumpFile, startThing, startTraceKind, thingToFind,
2145 maxDepth, thingToIgnore);
2146 if (dumpFile != stdout)
2147 fclose(dumpFile);
2148 return ok;
2149
2150 not_traceable_arg:
2151 JS_ReportError(cx, "argument '%s' is not null or a heap-allocated thing",
2152 badTraceArg);
2153 return JS_FALSE;
2154 }
2155
2156 #endif /* DEBUG */
2157
2158 #ifdef TEST_CVTARGS
2159 #include <ctype.h>
2160
2161 static const char *
2162 EscapeWideString(jschar *w)
2163 {
2164 static char enuf[80];
2165 static char hex[] = "0123456789abcdef";
2166 jschar u;
2167 unsigned char b, c;
2168 int i, j;
2169
2170 if (!w)
2171 return "";
2172 for (i = j = 0; i < sizeof enuf - 1; i++, j++) {
2173 u = w[j];
2174 if (u == 0)
2175 break;
2176 b = (unsigned char)(u >> 8);
2177 c = (unsigned char)(u);
2178 if (b) {
2179 if (i >= sizeof enuf - 6)
2180 break;
2181 enuf[i++] = '\\';
2182 enuf[i++] = 'u';
2183 enuf[i++] = hex[b >> 4];
2184 enuf[i++] = hex[b & 15];
2185 enuf[i++] = hex[c >> 4];
2186 enuf[i] = hex[c & 15];
2187 } else if (!isprint(c)) {
2188 if (i >= sizeof enuf - 4)
2189 break;
2190 enuf[i++] = '\\';
2191 enuf[i++] = 'x';
2192 enuf[i++] = hex[c >> 4];
2193 enuf[i] = hex[c & 15];
2194 } else {
2195 enuf[i] = (char)c;
2196 }
2197 }
2198 enuf[i] = 0;
2199 return enuf;
2200 }
2201
2202 #include <stdarg.h>
2203
2204 static JSBool
2205 ZZ_formatter(JSContext *cx, const char *format, JSBool fromJS, jsval **vpp,
2206 va_list *app)
2207 {
2208 jsval *vp;
2209 va_list ap;
2210 jsdouble re, im;
2211
2212 printf("entering ZZ_formatter");
2213 vp = *vpp;
2214 ap = *app;
2215 if (fromJS) {
2216 if (!JS_ValueToNumber(cx, vp[0], &re))
2217 return JS_FALSE;
2218 if (!JS_ValueToNumber(cx, vp[1], &im))
2219 return JS_FALSE;
2220 *va_arg(ap, jsdouble *) = re;
2221 *va_arg(ap, jsdouble *) = im;
2222 } else {
2223 re = va_arg(ap, jsdouble);
2224 im = va_arg(ap, jsdouble);
2225 if (!JS_NewNumberValue(cx, re, &vp[0]))
2226 return JS_FALSE;
2227 if (!JS_NewNumberValue(cx, im, &vp[1]))
2228 return JS_FALSE;
2229 }
2230 *vpp = vp + 2;
2231 *app = ap;
2232 printf("leaving ZZ_formatter");
2233 return JS_TRUE;
2234 }
2235
2236 static JSBool
2237 ConvertArgs(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
2238 {
2239 JSBool b = JS_FALSE;
2240 jschar c = 0;
2241 int32 i = 0, j = 0;
2242 uint32 u = 0;
2243 jsdouble d = 0, I = 0, re = 0, im = 0;
2244 char *s = NULL;
2245 JSString *str = NULL;
2246 jschar *w = NULL;
2247 JSObject *obj2 = NULL;
2248 JSFunction *fun = NULL;
2249 jsval v = JSVAL_VOID;
2250 JSBool ok;
2251
2252 if (!JS_AddArgumentFormatter(cx, "ZZ", ZZ_formatter))
2253 return JS_FALSE;
2254 ok = JS_ConvertArguments(cx, argc, argv, "b/ciujdIsSWofvZZ*",
2255 &b, &c, &i, &u, &j, &d, &I, &s, &str, &w, &obj2,
2256 &fun, &v, &re, &im);
2257 JS_RemoveArgumentFormatter(cx, "ZZ");
2258 if (!ok)
2259 return JS_FALSE;
2260 fprintf(gOutFile,
2261 "b %u, c %x (%c), i %ld, u %lu, j %ld\n",
2262 b, c, (char)c, i, u, j);
2263 ToString obj2string(cx, obj2);
2264 ToString valueString(cx, v);
2265 JSString *tmpstr = JS_DecompileFunction(cx, fun, 4);
2266 const char *func;
2267 if (tmpstr) {
2268 func = JS_GetStringBytes(tmpstr);
2269 } else {
2270 if (JS_IsExceptionPending(cx)) {
2271 if (!JS_ReportPendingException(cx))
2272 JS_ClearPendingException(cx);
2273 }
2274 func = "error decompiling fun";
2275 }
2276 fprintf(gOutFile,
2277 "d %g, I %g, s %s, S %s, W %s, obj %s, fun %s\n"
2278 "v %s, re %g, im %g\n",
2279 d, I, s, str ? JS_GetStringBytes(str) : "", EscapeWideString(w),
2280 obj2string.getBytes(),
2281 fun ? func : "",
2282 valueString.getBytes(), re, im);
2283 return JS_TRUE;
2284 }
2285 #endif
2286
2287 static JSBool
2288 BuildDate(JSContext *cx, uintN argc, jsval *vp)
2289 {
2290 char version[20] = "\n";
2291 #if JS_VERSION < 150
2292 sprintf(version, " for version %d\n", JS_VERSION);
2293 #endif
2294 fprintf(gOutFile, "built on %s at %s%s", __DATE__, __TIME__, version);
2295 *vp = JSVAL_VOID;
2296 return JS_TRUE;
2297 }
2298
2299 static JSBool
2300 Clear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
2301 {
2302 if (argc != 0 && !JS_ValueToObject(cx, argv[0], &obj))
2303 return JS_FALSE;
2304 JS_ClearScope(cx, obj);
2305 return JS_TRUE;
2306 }
2307
2308 static JSBool
2309 Intern(JSContext *cx, uintN argc, jsval *vp)
2310 {
2311 JSString *str;
2312
2313 str = JS_ValueToString(cx, argc == 0 ? JSVAL_VOID : vp[2]);
2314 if (!str)
2315 return JS_FALSE;
2316 if (!JS_InternUCStringN(cx, JS_GetStringChars(str),
2317 JS_GetStringLength(str))) {
2318 return JS_FALSE;
2319 }
2320 *vp = JSVAL_VOID;
2321 return JS_TRUE;
2322 }
2323
2324 static JSBool
2325 Clone(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
2326 {
2327 JSObject *funobj, *parent, *clone;
2328
2329 if (VALUE_IS_FUNCTION(cx, argv[0])) {
2330 funobj = JSVAL_TO_OBJECT(argv[0]);
2331 } else {
2332 JSFunction *fun = JS_ValueToFunction(cx, argv[0]);
2333 if (!fun)
2334 return JS_FALSE;
2335 funobj = JS_GetFunctionObject(fun);
2336 }
2337 if (argc > 1) {
2338 if (!JS_ValueToObject(cx, argv[1], &parent))
2339 return JS_FALSE;
2340 } else {
2341 parent = JS_GetParent(cx, funobj);
2342 }
2343 clone = JS_CloneFunctionObject(cx, funobj, parent);
2344 if (!clone)
2345 return JS_FALSE;
2346 *rval = OBJECT_TO_JSVAL(clone);
2347 return JS_TRUE;
2348 }
2349
2350 static JSBool
2351 Seal(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
2352 {
2353 JSObject *target;
2354 JSBool deep = JS_FALSE;
2355
2356 if (!JS_ConvertArguments(cx, argc, argv, "o/b", &target, &deep))
2357 return JS_FALSE;
2358 if (!target)
2359 return JS_TRUE;
2360 return JS_SealObject(cx, target, deep);
2361 }
2362
2363 static JSBool
2364 GetPDA(JSContext *cx, uintN argc, jsval *vp)
2365 {
2366 JSObject *vobj, *aobj, *pdobj;
2367 JSBool ok;
2368 JSPropertyDescArray pda;
2369 JSPropertyDesc *pd;
2370 uint32 i;
2371 jsval v;
2372
2373 if (!JS_ValueToObject(cx, argc == 0 ? JSVAL_VOID : vp[2], &vobj))
2374 return JS_FALSE;
2375 if (!vobj) {
2376 *vp = JSVAL_VOID;
2377 return JS_TRUE;
2378 }
2379
2380 aobj = JS_NewArrayObject(cx, 0, NULL);
2381 if (!aobj)
2382 return JS_FALSE;
2383 *vp = OBJECT_TO_JSVAL(aobj);
2384
2385 ok = JS_GetPropertyDescArray(cx, vobj, &pda);
2386 if (!ok)
2387 return JS_FALSE;
2388 pd = pda.array;
2389 for (i = 0; i < pda.length; i++, pd++) {
2390 pdobj = JS_NewObject(cx, NULL, NULL, NULL);
2391 if (!pdobj) {
2392 ok = JS_FALSE;
2393 break;
2394 }
2395
2396 /* Protect pdobj from GC by setting it as an element of aobj now */
2397 v = OBJECT_TO_JSVAL(pdobj);
2398 ok = JS_SetElement(cx, aobj, i, &v);
2399 if (!ok)
2400 break;
2401
2402 ok = JS_SetProperty(cx, pdobj, "id", &pd->id) &&
2403 JS_SetProperty(cx, pdobj, "value", &pd->value) &&
2404 (v = INT_TO_JSVAL(pd->flags),
2405 JS_SetProperty(cx, pdobj, "flags", &v)) &&
2406 (v = INT_TO_JSVAL(pd->slot),
2407 JS_SetProperty(cx, pdobj, "slot", &v)) &&
2408 JS_SetProperty(cx, pdobj, "alias", &pd->alias);
2409 if (!ok)
2410 break;
2411 }
2412 JS_PutPropertyDescArray(cx, &pda);
2413 return ok;
2414 }
2415
2416 static JSBool
2417 GetSLX(JSContext *cx, uintN argc, jsval *vp)
2418 {
2419 JSScript *script;
2420
2421 script = ValueToScript(cx, argc == 0 ? JSVAL_VOID : vp[2]);
2422 if (!script)
2423 return JS_FALSE;
2424 *vp = INT_TO_JSVAL(js_GetScriptLineExtent(script));
2425 return JS_TRUE;
2426 }
2427
2428 static JSBool
2429 ToInt32(JSContext *cx, uintN argc, jsval *vp)
2430 {
2431 int32 i;
2432
2433 if (!JS_ValueToInt32(cx, argc == 0 ? JSVAL_VOID : vp[2], &i))
2434 return JS_FALSE;
2435 return JS_NewNumberValue(cx, i, vp);
2436 }
2437
2438 static JSBool
2439 StringsAreUTF8(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
2440 jsval *rval)
2441 {
2442 *rval = JS_CStringsAreUTF8() ? JSVAL_TRUE : JSVAL_FALSE;
2443 return JS_TRUE;
2444 }
2445
2446 static JSBool
2447 StackQuota(JSContext *cx, uintN argc, jsval *vp)
2448 {
2449 uint32 n;
2450
2451 if (argc == 0)
2452 return JS_NewNumberValue(cx, (double) gScriptStackQuota, vp);
2453 if (!JS_ValueToECMAUint32(cx, JS_ARGV(cx, vp)[0], &n))
2454 return JS_FALSE;
2455 gScriptStackQuota = n;
2456 JS_SetScriptStackQuota(cx, gScriptStackQuota);
2457 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2458 return JS_TRUE;
2459 }
2460
2461 static const char* badUTF8 = "...\xC0...";
2462 static const char* bigUTF8 = "...\xFB\xBF\xBF\xBF\xBF...";
2463 static const jschar badSurrogate[] = { 'A', 'B', 'C', 0xDEEE, 'D', 'E', 0 };
2464
2465 static JSBool
2466 TestUTF8(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
2467 {
2468 int32 mode = 1;
2469 jschar chars[20];
2470 size_t charsLength = 5;
2471 char bytes[20];
2472 size_t bytesLength = 20;
2473 if (argc && !JS_ValueToInt32(cx, *argv, &mode))
2474 return JS_FALSE;
2475
2476 /* The following throw errors if compiled with UTF-8. */
2477 switch (mode) {
2478 /* mode 1: malformed UTF-8 string. */
2479 case 1:
2480 JS_NewStringCopyZ(cx, badUTF8);
2481 break;
2482 /* mode 2: big UTF-8 character. */
2483 case 2:
2484 JS_NewStringCopyZ(cx, bigUTF8);
2485 break;
2486 /* mode 3: bad surrogate character. */
2487 case 3:
2488 JS_EncodeCharacters(cx, badSurrogate, 6, bytes, &bytesLength);
2489 break;
2490 /* mode 4: use a too small buffer. */
2491 case 4:
2492 JS_DecodeBytes(cx, "1234567890", 10, chars, &charsLength);
2493 break;
2494 default:
2495 JS_ReportError(cx, "invalid mode parameter");
2496 return JS_FALSE;
2497 }
2498 return !JS_IsExceptionPending (cx);
2499 }
2500
2501 static JSBool
2502 ThrowError(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
2503 {
2504 JS_ReportError(cx, "This is an error");
2505 return JS_FALSE;
2506 }
2507
2508 #define LAZY_STANDARD_CLASSES
2509
2510 /* A class for easily testing the inner/outer object callbacks. */
2511 typedef struct ComplexObject {
2512 JSBool isInner;
2513 JSBool frozen;
2514 JSObject *inner;
2515 JSObject *outer;
2516 } ComplexObject;
2517
2518 static JSObject *
2519 split_create_outer(JSContext *cx);
2520
2521 static JSObject *
2522 split_create_inner(JSContext *cx, JSObject *outer);
2523
2524 static ComplexObject *
2525 split_get_private(JSContext *cx, JSObject *obj);
2526
2527 static JSBool
2528 split_addProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
2529 {
2530 ComplexObject *cpx;
2531 jsid asId;
2532
2533 cpx = split_get_private(cx, obj);
2534 if (!cpx)
2535 return JS_TRUE;
2536 if (!cpx->isInner && cpx->inner) {
2537 /* Make sure to define this property on the inner object. */
2538 if (!JS_ValueToId(cx, *vp, &asId))
2539 return JS_FALSE;
2540 return JS_DefinePropertyById(cx, cpx->inner, asId, *vp, NULL, NULL, JSPROP_ENUMERATE);
2541 }
2542 return JS_TRUE;
2543 }
2544
2545 static JSBool
2546 split_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
2547 {
2548 ComplexObject *cpx;
2549
2550 cpx = split_get_private(cx, obj);
2551 if (!cpx)
2552 return JS_TRUE;
2553
2554 if (JSVAL_IS_STRING(id) &&
2555 !strcmp(JS_GetStringBytes(JSVAL_TO_STRING(id)), "isInner")) {
2556 *vp = BOOLEAN_TO_JSVAL(cpx->isInner);
2557 return JS_TRUE;
2558 }
2559
2560 if (!cpx->isInner && cpx->inner) {
2561 if (JSVAL_IS_STRING(id)) {
2562 JSString *str;
2563
2564 str = JSVAL_TO_STRING(id);
2565 return JS_GetUCProperty(cx, cpx->inner, JS_GetStringChars(str),
2566 JS_GetStringLength(str), vp);
2567 }
2568 if (JSVAL_IS_INT(id))
2569 return JS_GetElement(cx, cpx->inner, JSVAL_TO_INT(id), vp);
2570 return JS_TRUE;
2571 }
2572
2573 return JS_TRUE;
2574 }
2575
2576 static JSBool
2577 split_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
2578 {
2579 ComplexObject *cpx;
2580
2581 cpx = split_get_private(cx, obj);
2582 if (!cpx)
2583 return JS_TRUE;
2584 if (!cpx->isInner && cpx->inner) {
2585 if (JSVAL_IS_STRING(id)) {
2586 JSString *str;
2587
2588 str = JSVAL_TO_STRING(id);
2589 return JS_SetUCProperty(cx, cpx->inner, JS_GetStringChars(str),
2590 JS_GetStringLength(str), vp);
2591 }
2592 if (JSVAL_IS_INT(id))
2593 return JS_SetElement(cx, cpx->inner, JSVAL_TO_INT(id), vp);
2594 return JS_TRUE;
2595 }
2596
2597 return JS_TRUE;
2598 }
2599
2600 static JSBool
2601 split_delProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
2602 {
2603 ComplexObject *cpx;
2604 jsid asId;
2605
2606 cpx = split_get_private(cx, obj);
2607 if (!cpx)
2608 return JS_TRUE;
2609 if (!cpx->isInner && cpx->inner) {
2610 /* Make sure to define this property on the inner object. */
2611 if (!JS_ValueToId(cx, *vp, &asId))
2612 return JS_FALSE;
2613 return cpx->inner->deleteProperty(cx, asId, vp);
2614 }
2615 return JS_TRUE;
2616 }
2617
2618 static JSBool
2619 split_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
2620 jsval *statep, jsid *idp)
2621 {
2622 ComplexObject *cpx;
2623 JSObject *iterator;
2624
2625 switch (enum_op) {
2626 case JSENUMERATE_INIT:
2627 cpx = (ComplexObject *) JS_GetPrivate(cx, obj);
2628
2629 if (!cpx->isInner && cpx->inner)
2630 obj = cpx->inner;
2631
2632 iterator = JS_NewPropertyIterator(cx, obj);
2633 if (!iterator)
2634 return JS_FALSE;
2635
2636 *statep = OBJECT_TO_JSVAL(iterator);
2637 if (idp)
2638 *idp = JSVAL_ZERO;
2639 break;
2640
2641 case JSENUMERATE_NEXT:
2642 iterator = (JSObject*)JSVAL_TO_OBJECT(*statep);
2643 if (!JS_NextProperty(cx, iterator, idp))
2644 return JS_FALSE;
2645
2646 if (!JSVAL_IS_VOID(*idp))
2647 break;
2648 /* Fall through. */
2649
2650 case JSENUMERATE_DESTROY:
2651 /* Let GC at our iterator object. */
2652 *statep = JSVAL_NULL;
2653 break;
2654 }
2655
2656 return JS_TRUE;
2657 }
2658
2659 static JSBool
2660 split_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
2661 JSObject **objp)
2662 {
2663 ComplexObject *cpx;
2664
2665 if (JSVAL_IS_STRING(id) &&
2666 !strcmp(JS_GetStringBytes(JSVAL_TO_STRING(id)), "isInner")) {
2667 *objp = obj;
2668 return JS_DefineProperty(cx, obj, "isInner", JSVAL_VOID, NULL, NULL,
2669 JSPROP_SHARED);
2670 }
2671
2672 cpx = split_get_private(cx, obj);
2673 if (!cpx)
2674 return JS_TRUE;
2675 if (!cpx->isInner && cpx->inner) {
2676 jsid asId;
2677 JSProperty *prop;
2678
2679 if (!JS_ValueToId(cx, id, &asId))
2680 return JS_FALSE;
2681
2682 if (!cpx->inner->lookupProperty(cx, asId, objp, &prop))
2683 return JS_FALSE;
2684 if (prop)
2685 cpx->inner->dropProperty(cx, prop);
2686
2687 return JS_TRUE;
2688 }
2689
2690 #ifdef LAZY_STANDARD_CLASSES
2691 if (!(flags & JSRESOLVE_ASSIGNING)) {
2692 JSBool resolved;
2693
2694 if (!JS_ResolveStandardClass(cx, obj, id, &resolved))
2695 return JS_FALSE;
2696
2697 if (resolved) {
2698 *objp = obj;
2699 return JS_TRUE;
2700 }
2701 }
2702 #endif
2703
2704 /* XXX For additional realism, let's resolve some random property here. */
2705 return JS_TRUE;
2706 }
2707
2708 static void
2709 split_finalize(JSContext *cx, JSObject *obj)
2710 {
2711 JS_free(cx, JS_GetPrivate(cx, obj));
2712 }
2713
2714 static uint32
2715 split_mark(JSContext *cx, JSObject *obj, void *arg)
2716 {
2717 ComplexObject *cpx;
2718
2719 cpx = (ComplexObject *) JS_GetPrivate(cx, obj);
2720
2721 if (!cpx->isInner && cpx->inner) {
2722 /* Mark the inner object. */
2723 JS_MarkGCThing(cx, cpx->inner, "ComplexObject.inner", arg);
2724 }
2725
2726 return 0;
2727 }
2728
2729 static JSObject *
2730 split_outerObject(JSContext *cx, JSObject *obj)
2731 {
2732 ComplexObject *cpx;
2733
2734 cpx = (ComplexObject *) JS_GetPrivate(cx, obj);
2735 return cpx->isInner ? cpx->outer : obj;
2736 }
2737
2738 static JSObject *
2739 split_thisObject(JSContext *cx, JSObject *obj)
2740 {
2741 OBJ_TO_OUTER_OBJECT(cx, obj);
2742 if (!obj)
2743 return NULL;
2744 return obj;
2745 }
2746
2747 static JSObjectOps split_objectops;
2748
2749 static JSObjectOps *
2750 split_getObjectOps(JSContext *cx, JSClass *clasp)
2751 {
2752 if (!split_objectops.thisObject) {
2753 memcpy(&split_objectops, &js_ObjectOps, sizeof split_objectops);
2754 split_objectops.thisObject = split_thisObject;
2755 }
2756
2757 return &split_objectops;
2758 }
2759
2760 static JSBool
2761 split_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp);
2762
2763 static JSObject *
2764 split_innerObject(JSContext *cx, JSObject *obj)
2765 {
2766 ComplexObject *cpx;
2767
2768 cpx = (ComplexObject *) JS_GetPrivate(cx, obj);
2769 if (cpx->frozen) {
2770 JS_ASSERT(!cpx->isInner);
2771 return obj;
2772 }
2773 return !cpx->isInner ? cpx->inner : obj;
2774 }
2775
2776 static JSExtendedClass split_global_class = {
2777 {"split_global",
2778 JSCLASS_NEW_RESOLVE | JSCLASS_NEW_ENUMERATE | JSCLASS_HAS_PRIVATE |
2779 JSCLASS_GLOBAL_FLAGS | JSCLASS_IS_EXTENDED,
2780 split_addProperty, split_delProperty,
2781 split_getProperty, split_setProperty,
2782 (JSEnumerateOp)split_enumerate,
2783 (JSResolveOp)split_resolve,
2784 JS_ConvertStub, split_finalize,
2785 split_getObjectOps, NULL, NULL, NULL, NULL, NULL,
2786 split_mark, NULL},
2787 split_equality, split_outerObject, split_innerObject,
2788 NULL, NULL, NULL, NULL, NULL
2789 };
2790
2791 static JSBool
2792 split_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
2793 {
2794 *bp = JS_FALSE;
2795 if (JSVAL_IS_PRIMITIVE(v))
2796 return JS_TRUE;
2797
2798 JSObject *obj2 = JSVAL_TO_OBJECT(v);
2799 if (JS_GET_CLASS(cx, obj2) != &split_global_class.base)
2800 return JS_TRUE;
2801
2802 ComplexObject *cpx = (ComplexObject *) JS_GetPrivate(cx, obj2);
2803 JS_ASSERT(!cpx->isInner);
2804
2805 ComplexObject *ourCpx = (ComplexObject *) JS_GetPrivate(cx, obj);
2806 JS_ASSERT(!ourCpx->isInner);
2807
2808 *bp = (cpx == ourCpx);
2809 return JS_TRUE;
2810 }
2811
2812 JSObject *
2813 split_create_outer(JSContext *cx)
2814 {
2815 ComplexObject *cpx;
2816 JSObject *obj;
2817
2818 cpx = (ComplexObject *) JS_malloc(cx, sizeof *obj);
2819 if (!cpx)
2820 return NULL;
2821 cpx->isInner = JS_FALSE;
2822 cpx->frozen = JS_TRUE;
2823 cpx->inner = NULL;
2824 cpx->outer = NULL;
2825
2826 obj = JS_NewObject(cx, &split_global_class.base, NULL, NULL);
2827 if (!obj || !JS_SetParent(cx, obj, NULL)) {
2828 JS_free(cx, cpx);
2829 return NULL;
2830 }
2831
2832 if (!JS_SetPrivate(cx, obj, cpx)) {
2833 JS_free(cx, cpx);
2834 return NULL;
2835 }
2836
2837 return obj;
2838 }
2839
2840 static JSObject *
2841 split_create_inner(JSContext *cx, JSObject *outer)
2842 {
2843 ComplexObject *cpx, *outercpx;
2844 JSObject *obj;
2845
2846 JS_ASSERT(JS_GET_CLASS(cx, outer) == &split_global_class.base);
2847
2848 cpx = (ComplexObject *) JS_malloc(cx, sizeof *cpx);
2849 if (!cpx)
2850 return NULL;
2851 cpx->isInner = JS_TRUE;
2852 cpx->frozen = JS_FALSE;
2853 cpx->inner = NULL;
2854 cpx->outer = outer;
2855
2856 obj = JS_NewObject(cx, &split_global_class.base, NULL, NULL);
2857 if (!obj || !JS_SetParent(cx, obj, NULL) || !JS_SetPrivate(cx, obj, cpx)) {
2858 JS_free(cx, cpx);
2859 return NULL;
2860 }
2861
2862 outercpx = (ComplexObject *) JS_GetPrivate(cx, outer);
2863 outercpx->inner = obj;
2864 outercpx->frozen = JS_FALSE;
2865
2866 return obj;
2867 }
2868
2869 static ComplexObject *
2870 split_get_private(JSContext *cx, JSObject *obj)
2871 {
2872 do {
2873 if (JS_GET_CLASS(cx, obj) == &split_global_class.base)
2874 return (ComplexObject *) JS_GetPrivate(cx, obj);
2875 obj = JS_GetParent(cx, obj);
2876 } while (obj);
2877
2878 return NULL;
2879 }
2880
2881 static JSBool
2882 sandbox_enumerate(JSContext *cx, JSObject *obj)
2883 {
2884 jsval v;
2885 JSBool b;
2886
2887 if (!JS_GetProperty(cx, obj, "lazy", &v))
2888 return JS_FALSE;
2889
2890 JS_ValueToBoolean(cx, v, &b);
2891 return !b || JS_EnumerateStandardClasses(cx, obj);
2892 }
2893
2894 static JSBool
2895 sandbox_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
2896 JSObject **objp)
2897 {
2898 jsval v;
2899 JSBool b, resolved;
2900
2901 if (!JS_GetProperty(cx, obj, "lazy", &v))
2902 return JS_FALSE;
2903
2904 JS_ValueToBoolean(cx, v, &b);
2905 if (b && (flags & JSRESOLVE_ASSIGNING) == 0) {
2906 if (!JS_ResolveStandardClass(cx, obj, id, &resolved))
2907 return JS_FALSE;
2908 if (resolved) {
2909 *objp = obj;
2910 return JS_TRUE;
2911 }
2912 }
2913 *objp = NULL;
2914 return JS_TRUE;
2915 }
2916
2917 static JSClass sandbox_class = {
2918 "sandbox",
2919 JSCLASS_NEW_RESOLVE,
2920 JS_PropertyStub, JS_PropertyStub,
2921 JS_PropertyStub, JS_PropertyStub,
2922 sandbox_enumerate, (JSResolveOp)sandbox_resolve,
2923 JS_ConvertStub, NULL,
2924 JSCLASS_NO_OPTIONAL_MEMBERS
2925 };
2926
2927 static JSBool
2928 EvalInContext(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
2929 jsval *rval)
2930 {
2931 JSString *str;
2932 JSObject *sobj;
2933 JSContext *scx;
2934 const jschar *src;
2935 size_t srclen;
2936 JSBool lazy, ok;
2937 jsval v;
2938 JSStackFrame *fp;
2939
2940 sobj = NULL;
2941 if (!JS_ConvertArguments(cx, argc, argv, "S / o", &str, &sobj))
2942 return JS_FALSE;
2943
2944 WITH_LOCKED_CONTEXT_LIST(
2945 scx = JS_NewContext(JS_GetRuntime(cx), gStackChunkSize)
2946 );
2947 if (!scx) {
2948 JS_ReportOutOfMemory(cx);
2949 return JS_FALSE;
2950 }
2951 JS_SetOptions(scx, JS_GetOptions(cx));
2952
2953 JS_BeginRequest(scx);
2954 src = JS_GetStringChars(str);
2955 srclen = JS_GetStringLength(str);
2956 lazy = JS_FALSE;
2957 if (srclen == 4 &&
2958 src[0] == 'l' && src[1] == 'a' && src[2] == 'z' && src[3] == 'y') {
2959 lazy = JS_TRUE;
2960 srclen = 0;
2961 }
2962
2963 if (!sobj) {
2964 sobj = JS_NewObject(scx, &sandbox_class, NULL, NULL);
2965 if (!sobj || (!lazy && !JS_InitStandardClasses(scx, sobj))) {
2966 ok = JS_FALSE;
2967 goto out;
2968 }
2969 v = BOOLEAN_TO_JSVAL(lazy);
2970 ok = JS_SetProperty(cx, sobj, "lazy", &v);
2971 if (!ok)
2972 goto out;
2973 }
2974
2975 if (srclen == 0) {
2976 *rval = OBJECT_TO_JSVAL(sobj);
2977 ok = JS_TRUE;
2978 } else {
2979 fp = JS_GetScriptedCaller(cx, NULL);
2980 JS_SetGlobalObject(scx, sobj);
2981 JS_ToggleOptions(scx, JSOPTION_DONT_REPORT_UNCAUGHT);
2982 ok = JS_EvaluateUCScript(scx, sobj, src, srclen,
2983 fp->script->filename,
2984 JS_PCToLineNumber(cx, fp->script,
2985 fp->regs->pc),
2986 rval);
2987 if (!ok) {
2988 if (JS_GetPendingException(scx, &v))
2989 JS_SetPendingException(cx, v);
2990 else
2991 JS_ReportOutOfMemory(cx);
2992 }
2993 }
2994
2995 out:
2996 JS_EndRequest(scx);
2997 WITH_LOCKED_CONTEXT_LIST(
2998 JS_DestroyContextNoGC(scx)
2999 );
3000 return ok;
3001 }
3002
3003 static JSBool
3004 ShapeOf(JSContext *cx, uintN argc, jsval *vp)
3005 {
3006 jsval v = JS_ARGV(cx, vp)[0];
3007 if (!JSVAL_IS_OBJECT(v)) {
3008 JS_ReportError(cx, "shapeOf: object expected");
3009 return JS_FALSE;
3010 }
3011 JSObject *obj = JSVAL_TO_OBJECT(v);
3012 if (!obj) {
3013 *vp = JSVAL_ZERO;
3014 return JS_TRUE;
3015 }
3016 if (!OBJ_IS_NATIVE(obj)) {
3017 *vp = INT_TO_JSVAL(-1);
3018 return JS_TRUE;
3019 }
3020 return JS_NewNumberValue(cx, OBJ_SHAPE(obj), vp);
3021 }
3022
3023 #ifdef JS_THREADSAFE
3024
3025 /*
3026 * Check that t1 comes strictly before t2. The function correctly deals with
3027 * PRIntervalTime wrap-around between t2 and t1 assuming that t2 and t1 stays
3028 * within INT32_MAX from each other. We use MAX_TIMEOUT_INTERVAL to enforce
3029 * this restriction.
3030 */
3031 static bool
3032 IsBefore(PRIntervalTime t1, PRIntervalTime t2)
3033 {
3034 return int32(t1 - t2) < 0;
3035 }
3036
3037 static JSBool
3038 Sleep_fn(JSContext *cx, uintN argc, jsval *vp)
3039 {
3040 PRIntervalTime t_ticks;
3041
3042 if (argc == 0) {
3043 t_ticks = 0;
3044 } else {
3045 jsdouble t_secs;
3046
3047 if (!JS_ValueToNumber(cx, argc == 0 ? JSVAL_VOID : vp[2], &t_secs))
3048 return JS_FALSE;
3049
3050 /* NB: The next condition also filter out NaNs. */
3051 if (!(t_secs <= MAX_TIMEOUT_INTERVAL)) {
3052 JS_ReportError(cx, "Excessive sleep interval");
3053 return JS_FALSE;
3054 }
3055 t_ticks = (t_secs <= 0.0)
3056 ? 0
3057 : PRIntervalTime(PR_TicksPerSecond() * t_secs);
3058 }
3059 if (t_ticks == 0) {
3060 JS_YieldRequest(cx);
3061 } else {
3062 jsrefcount rc = JS_SuspendRequest(cx);
3063 PR_Lock(gWatchdogLock);
3064 PRIntervalTime to_wakeup = PR_IntervalNow() + t_ticks;
3065 for (;;) {
3066 PR_WaitCondVar(gSleepWakeup, t_ticks);
3067 if (gCanceled)
3068 break;
3069 PRIntervalTime now = PR_IntervalNow();
3070 if (!IsBefore(now, to_wakeup))
3071 break;
3072 t_ticks = to_wakeup - now;
3073 }
3074 PR_Unlock(gWatchdogLock);
3075 JS_ResumeRequest(cx, rc);
3076 }
3077 return !gCanceled;
3078 }
3079
3080 typedef struct ScatterThreadData ScatterThreadData;
3081 typedef struct ScatterData ScatterData;
3082
3083 typedef enum ScatterStatus {
3084 SCATTER_WAIT,
3085 SCATTER_GO,
3086 SCATTER_CANCEL
3087 } ScatterStatus;
3088
3089 struct ScatterData {
3090 ScatterThreadData *threads;
3091 jsval *results;
3092 PRLock *lock;
3093 PRCondVar *cvar;
3094 ScatterStatus status;
3095 };
3096
3097 struct ScatterThreadData {
3098 jsint index;
3099 ScatterData *shared;
3100 PRThread *thr;
3101 JSContext *cx;
3102 jsval fn;
3103 };
3104
3105 static void
3106 DoScatteredWork(JSContext *cx, ScatterThreadData *td)
3107 {
3108 jsval *rval = &td->shared->results[td->index];
3109
3110 if (!JS_CallFunctionValue(cx, NULL, td->fn, 0, NULL, rval)) {
3111 *rval = JSVAL_VOID;
3112 JS_GetPendingException(cx, rval);
3113 JS_ClearPendingException(cx);
3114 }
3115 }
3116
3117 static void
3118 RunScatterThread(void *arg)
3119 {
3120 int stackDummy;
3121 ScatterThreadData *td;
3122 ScatterStatus st;
3123 JSContext *cx;
3124
3125 if (PR_FAILURE == PR_SetThreadPrivate(gStackBaseThreadIndex, &stackDummy))
3126 return;
3127
3128 td = (ScatterThreadData *)arg;
3129 cx = td->cx;
3130
3131 /* Wait for our signal. */
3132 PR_Lock(td->shared->lock);
3133 while ((st = td->shared->status) == SCATTER_WAIT)
3134 PR_WaitCondVar(td->shared->cvar, PR_INTERVAL_NO_TIMEOUT);
3135 PR_Unlock(td->shared->lock);
3136
3137 if (st == SCATTER_CANCEL)
3138 return;
3139
3140 /* We are good to go. */
3141 JS_SetContextThread(cx);
3142 SetThreadStackLimit(cx);
3143 JS_BeginRequest(cx);
3144 DoScatteredWork(cx, td);
3145 JS_EndRequest(cx);
3146 JS_ClearContextThread(cx);
3147 }
3148
3149 /*
3150 * scatter(fnArray) - Call each function in `fnArray` without arguments, each
3151 * in a different thread. When all threads have finished, return an array: the
3152 * return values. Errors are not propagated; if any of the function calls
3153 * fails, the corresponding element in the results array gets the exception
3154 * object, if any, else (undefined).
3155 */
3156 static JSBool
3157 Scatter(JSContext *cx, uintN argc, jsval *vp)
3158 {
3159 jsuint i;
3160 jsuint n; /* number of threads */
3161 JSObject *inArr;
3162 JSObject *arr;
3163 ScatterData sd;
3164 JSBool ok;
3165 jsrefcount rc;
3166
3167 sd.lock = NULL;
3168 sd.cvar = NULL;
3169 sd.results = NULL;
3170 sd.threads = NULL;
3171 sd.status = SCATTER_WAIT;
3172
3173 if (argc == 0 || JSVAL_IS_PRIMITIVE(JS_ARGV(cx, vp)[0])) {
3174 JS_ReportError(cx, "the first argument must be an object");
3175 goto fail;
3176 }
3177
3178 inArr = JSVAL_TO_OBJECT(JS_ARGV(cx, vp)[0]);
3179 ok = JS_GetArrayLength(cx, inArr, &n);
3180 if (!ok)
3181 goto out;
3182 if (n == 0)
3183 goto success;
3184
3185 sd.lock = PR_NewLock();
3186 if (!sd.lock)
3187 goto fail;
3188
3189 sd.cvar = PR_NewCondVar(sd.lock);
3190 if (!sd.cvar)
3191 goto fail;
3192
3193 sd.results = (jsval *) malloc(n * sizeof(jsval));
3194 if (!sd.results)
3195 goto fail;
3196 for (i = 0; i < n; i++) {
3197 sd.results[i] = JSVAL_VOID;
3198 ok = JS_AddRoot(cx, &sd.results[i]);
3199 if (!ok) {
3200 while (i-- > 0)
3201 JS_RemoveRoot(cx, &sd.results[i]);
3202 free(sd.results);
3203 sd.results = NULL;
3204 goto fail;
3205 }
3206 }
3207
3208 sd.threads = (ScatterThreadData *) malloc(n * sizeof(ScatterThreadData));
3209 if (!sd.threads)
3210 goto fail;
3211 for (i = 0; i < n; i++) {
3212 sd.threads[i].index = i;
3213 sd.threads[i].shared = &sd;
3214 sd.threads[i].thr = NULL;
3215 sd.threads[i].cx = NULL;
3216 sd.threads[i].fn = JSVAL_NULL;
3217
3218 ok = JS_AddRoot(cx, &sd.threads[i].fn);
3219 if (ok && !JS_GetElement(cx, inArr, (jsint) i, &sd.threads[i].fn)) {
3220 JS_RemoveRoot(cx, &sd.threads[i].fn);
3221 ok = JS_FALSE;
3222 }
3223 if (!ok) {
3224 while (i-- > 0)
3225 JS_RemoveRoot(cx, &sd.threads[i].fn);
3226 free(sd.threads);
3227 sd.threads = NULL;
3228 goto fail;
3229 }
3230 }
3231
3232 for (i = 1; i < n; i++) {
3233 JSContext *newcx;
3234 WITH_LOCKED_CONTEXT_LIST(
3235 newcx = JS_NewContext(JS_GetRuntime(cx), 8192)
3236 );
3237 if (!newcx)
3238 goto fail;
3239 JS_SetGlobalObject(newcx, JS_GetGlobalObject(cx));
3240 JS_ClearContextThread(newcx);
3241 sd.threads[i].cx = newcx;
3242 }
3243
3244 for (i = 1; i < n; i++) {
3245 PRThread *t = PR_CreateThread(PR_USER_THREAD,
3246 RunScatterThread,
3247 &sd.threads[i],
3248 PR_PRIORITY_NORMAL,
3249 PR_GLOBAL_THREAD,
3250 PR_JOINABLE_THREAD,
3251 0);
3252 if (!t) {
3253 /* Failed to start thread. */
3254 PR_Lock(sd.lock);
3255 sd.status = SCATTER_CANCEL;
3256 PR_NotifyAllCondVar(sd.cvar);
3257 PR_Unlock(sd.lock);
3258 while (i-- > 1)
3259 PR_JoinThread(sd.threads[i].thr);
3260 goto fail;
3261 }
3262
3263 sd.threads[i].thr = t;
3264 }
3265 PR_Lock(sd.lock);
3266 sd.status = SCATTER_GO;
3267 PR_NotifyAllCondVar(sd.cvar);
3268 PR_Unlock(sd.lock);
3269
3270 DoScatteredWork(cx, &sd.threads[0]);
3271
3272 rc = JS_SuspendRequest(cx);
3273 for (i = 1; i < n; i++) {
3274 PR_JoinThread(sd.threads[i].thr);
3275 }
3276 JS_ResumeRequest(cx, rc);
3277
3278 success:
3279 arr = JS_NewArrayObject(cx, n, sd.results);
3280 if (!arr)
3281 goto fail;
3282 *vp = OBJECT_TO_JSVAL(arr);
3283 ok = JS_TRUE;
3284
3285 out:
3286 if (sd.threads) {
3287 JSContext *acx;
3288
3289 for (i = 0; i < n; i++) {
3290 JS_RemoveR