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

Contents of /trunk/js/js.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 399 - (show annotations)
Tue Dec 9 03:37:47 2008 UTC (10 years, 11 months ago) by siliconforks
File size: 112382 byte(s)
Use SpiderMonkey from Firefox 3.1b2.

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