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

Contents of /trunk/js/js.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 460 - (show annotations)
Sat Sep 26 23:15:22 2009 UTC (10 years, 1 month ago) by siliconforks
File size: 130955 byte(s)
Upgrade to SpiderMonkey from Firefox 3.5.3.

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