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

Contents of /trunk/js/js.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 332 - (show annotations)
Thu Oct 23 19:03:33 2008 UTC (10 years, 10 months ago) by siliconforks
File size: 111268 byte(s)
Add SpiderMonkey from Firefox 3.1b1.

The following directories and files were removed:
correct/, correct.js
liveconnect/
nanojit/
t/
v8/
vprof/
xpconnect/
all JavaScript files (Y.js, call.js, if.js, math-partial-sums.js, md5.js, perfect.js, trace-test.js, trace.js)


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