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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 535 - (show annotations)
Fri Apr 2 16:18:55 2010 UTC (10 years, 6 months ago) by siliconforks
File size: 135183 byte(s)
Update SpiderMonkey from Firefox 3.6.2.

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