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

Contents of /trunk/js/js.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 462 - (show annotations)
Sun Sep 27 02:22:42 2009 UTC (10 years, 1 month ago) by siliconforks
File size: 130976 byte(s)
Fixes for compiling under Windows.

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