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

Annotation of /trunk/js/js.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 332 - (hide annotations)
Thu Oct 23 19:03:33 2008 UTC (13 years, 8 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 siliconforks 332 /* -*- 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