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

Annotation of /trunk/js/shell/js.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 507 - (hide annotations)
Sun Jan 10 07:23:34 2010 UTC (10 years, 8 months ago) by siliconforks
File size: 135159 byte(s)
Update SpiderMonkey from Firefox 3.6rc1.

1 siliconforks 507 /* -*- 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 <errno.h>
45     #include <math.h>
46     #include <stdio.h>
47     #include <stdlib.h>
48     #include <string.h>
49     #include <signal.h>
50     #include <locale.h>
51     #include "jstypes.h"
52     #include "jsarena.h"
53     #include "jsutil.h"
54     #include "jsprf.h"
55     #include "jsapi.h"
56     #include "jsarray.h"
57     #include "jsatom.h"
58     #include "jsbuiltins.h"
59     #include "jscntxt.h"
60     #include "jsdate.h"
61     #include "jsdbgapi.h"
62     #include "jsemit.h"
63     #include "jsfun.h"
64     #include "jsgc.h"
65     #include "jsiter.h"
66     #include "jslock.h"
67     #include "jsnum.h"
68     #include "jsobj.h"
69     #include "jsparse.h"
70     #include "jsscope.h"
71     #include "jsscript.h"
72     #include "jstracer.h"
73    
74     #include "prmjtime.h"
75    
76     #ifdef JSDEBUGGER
77     #include "jsdebug.h"
78     #ifdef JSDEBUGGER_JAVA_UI
79     #include "jsdjava.h"
80     #endif /* JSDEBUGGER_JAVA_UI */
81     #ifdef JSDEBUGGER_C_UI
82     #include "jsdb.h"
83     #endif /* JSDEBUGGER_C_UI */
84     #endif /* JSDEBUGGER */
85    
86     #ifdef XP_UNIX
87     #include <unistd.h>
88     #include <sys/types.h>
89     #include <sys/wait.h>
90     #endif
91    
92     #if defined(XP_WIN) || defined(XP_OS2)
93     #include <io.h> /* for isatty() */
94     #endif
95    
96     #ifdef XP_WIN
97     #include <windows.h>
98     #endif
99    
100     typedef enum JSShellExitCode {
101     EXITCODE_RUNTIME_ERROR = 3,
102     EXITCODE_FILE_NOT_FOUND = 4,
103     EXITCODE_OUT_OF_MEMORY = 5,
104     EXITCODE_TIMEOUT = 6
105     } JSShellExitCode;
106    
107     size_t gStackChunkSize = 8192;
108    
109     /* Assume that we can not use more than 5e5 bytes of C stack by default. */
110     static size_t gMaxStackSize = 500000;
111    
112     #ifdef JS_THREADSAFE
113     static PRUintn gStackBaseThreadIndex;
114     #else
115     static jsuword gStackBase;
116     #endif
117    
118     static size_t gScriptStackQuota = JS_DEFAULT_SCRIPT_STACK_QUOTA;
119    
120     /*
121     * Limit the timeout to 30 minutes to prevent an overflow on platfoms
122     * that represent the time internally in microseconds using 32-bit int.
123     */
124     static jsdouble MAX_TIMEOUT_INTERVAL = 1800.0;
125     static jsdouble gTimeoutInterval = -1.0;
126     static volatile bool gCanceled = false;
127    
128     static JSBool
129     SetTimeoutValue(JSContext *cx, jsdouble t);
130    
131     static bool
132     InitWatchdog(JSRuntime *rt);
133    
134     static void
135     KillWatchdog();
136    
137     static bool
138     ScheduleWatchdog(JSRuntime *rt, jsdouble t);
139    
140     static void
141     CancelExecution(JSRuntime *rt);
142    
143     /*
144     * Watchdog thread state.
145     */
146     #ifdef JS_THREADSAFE
147    
148     static PRLock *gWatchdogLock = NULL;
149     static PRCondVar *gWatchdogWakeup = NULL;
150     static PRThread *gWatchdogThread = NULL;
151     static bool gWatchdogHasTimeout = false;
152     static PRIntervalTime gWatchdogTimeout = 0;
153    
154     static PRCondVar *gSleepWakeup = NULL;
155    
156     /*
157     * Holding the gcLock already guarantees that the context list is locked when
158     * the watchdog thread walks it.
159     */
160    
161     #define WITH_LOCKED_CONTEXT_LIST(x) \
162     JS_BEGIN_MACRO \
163     x; \
164     JS_END_MACRO
165    
166     #else
167    
168     static JSRuntime *gRuntime = NULL;
169    
170     /*
171     * Since signal handlers can't block, we must disable them before manipulating
172     * the context list.
173     */
174     #define WITH_LOCKED_CONTEXT_LIST(x) \
175     JS_BEGIN_MACRO \
176     ScheduleWatchdog(gRuntime, -1); \
177     x; \
178     ScheduleWatchdog(gRuntime, gTimeoutInterval); \
179     JS_END_MACRO
180     #endif
181    
182     int gExitCode = 0;
183     JSBool gQuitting = JS_FALSE;
184     FILE *gErrFile = NULL;
185     FILE *gOutFile = NULL;
186    
187     static JSBool reportWarnings = JS_TRUE;
188     static JSBool compileOnly = JS_FALSE;
189    
190     typedef enum JSShellErrNum {
191     #define MSG_DEF(name, number, count, exception, format) \
192     name = number,
193     #include "jsshell.msg"
194     #undef MSG_DEF
195     JSShellErr_Limit
196     #undef MSGDEF
197     } JSShellErrNum;
198    
199     static const JSErrorFormatString *
200     my_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber);
201     static JSObject *
202     split_setup(JSContext *cx);
203    
204     #ifdef EDITLINE
205     JS_BEGIN_EXTERN_C
206     JS_EXTERN_API(char) *readline(const char *prompt);
207     JS_EXTERN_API(void) add_history(char *line);
208     JS_END_EXTERN_C
209     #endif
210    
211     class ToString {
212     public:
213     ToString(JSContext *aCx, jsval v, JSBool aThrow = JS_FALSE)
214     : cx(aCx)
215     , mThrow(aThrow)
216     {
217     mStr = JS_ValueToString(cx, v);
218     if (!aThrow && !mStr && JS_IsExceptionPending(cx)) {
219     if (!JS_ReportPendingException(cx))
220     JS_ClearPendingException(cx);
221     }
222     JS_AddNamedRoot(cx, &mStr, "Value ToString helper");
223     }
224     ~ToString() {
225     JS_RemoveRoot(cx, &mStr);
226     }
227     JSBool threw() { return !mStr; }
228     jsval getJSVal() { return STRING_TO_JSVAL(mStr); }
229     const char *getBytes() {
230     return mStr ? JS_GetStringBytes(mStr) : "(error converting value)";
231     }
232     private:
233     JSContext *cx;
234     JSString *mStr;
235     JSBool mThrow;
236     };
237    
238     static char *
239     GetLine(FILE *file, const char * prompt)
240     {
241     size_t size;
242     char *buffer;
243     #ifdef EDITLINE
244     /*
245     * Use readline only if file is stdin, because there's no way to specify
246     * another handle. Are other filehandles interactive?
247     */
248     if (file == stdin) {
249     char *linep = readline(prompt);
250     /*
251     * We set it to zero to avoid complaining about inappropriate ioctl
252     * for device in the case of EOF. Looks like errno == 251 if line is
253     * finished with EOF and errno == 25 (EINVAL on Mac) if there is
254     * nothing left to read.
255     */
256     if (errno == 251 || errno == 25 || errno == EINVAL)
257     errno = 0;
258     if (!linep)
259     return NULL;
260     if (linep[0] != '\0')
261     add_history(linep);
262     return linep;
263     }
264     #endif
265     size_t len = 0;
266     if (*prompt != '\0') {
267     fprintf(gOutFile, "%s", prompt);
268     fflush(gOutFile);
269     }
270     size = 80;
271     buffer = (char *) malloc(size);
272     if (!buffer)
273     return NULL;
274     char *current = buffer;
275     while (fgets(current, size - len, file)) {
276     len += strlen(current);
277     char *t = buffer + len - 1;
278     if (*t == '\n') {
279     /* Line was read. We remove '\n' and exit. */
280     *t = '\0';
281     return buffer;
282     }
283     if (len + 1 == size) {
284     size = size * 2;
285     char *tmp = (char *) realloc(buffer, size);
286     if (!tmp) {
287     free(buffer);
288     return NULL;
289     }
290     buffer = tmp;
291     }
292     current = buffer + len;
293     }
294     if (len && !ferror(file))
295     return buffer;
296     free(buffer);
297     return NULL;
298     }
299    
300     /*
301     * State to store as JSContext private.
302     *
303     * We declare such timestamp as volatile as they are updated in the operation
304     * callback without taking any locks. Any possible race can only lead to more
305     * frequent callback calls. This is safe as the callback does everything based
306     * on timing.
307     */
308     struct JSShellContextData {
309     volatile JSIntervalTime startTime;
310     };
311    
312     static JSShellContextData *
313     NewContextData()
314     {
315     /* Prevent creation of new contexts after we have been canceled. */
316     if (gCanceled)
317     return NULL;
318    
319     JSShellContextData *data = (JSShellContextData *)
320     calloc(sizeof(JSShellContextData), 1);
321     if (!data)
322     return NULL;
323     data->startTime = js_IntervalNow();
324     return data;
325     }
326    
327     static inline JSShellContextData *
328     GetContextData(JSContext *cx)
329     {
330     JSShellContextData *data = (JSShellContextData *) JS_GetContextPrivate(cx);
331    
332     JS_ASSERT(data);
333     return data;
334     }
335    
336     static JSBool
337     ShellOperationCallback(JSContext *cx)
338     {
339     if (!gCanceled)
340     return JS_TRUE;
341    
342     JS_ClearPendingException(cx);
343     return JS_FALSE;
344     }
345    
346     static void
347     SetThreadStackLimit(JSContext *cx)
348     {
349     jsuword stackLimit;
350    
351     if (gMaxStackSize == 0) {
352     /*
353     * Disable checking for stack overflow if limit is zero.
354     */
355     stackLimit = 0;
356     } else {
357     jsuword stackBase;
358     #ifdef JS_THREADSAFE
359     stackBase = (jsuword) PR_GetThreadPrivate(gStackBaseThreadIndex);
360     #else
361     stackBase = gStackBase;
362     #endif
363     JS_ASSERT(stackBase != 0);
364     #if JS_STACK_GROWTH_DIRECTION > 0
365     stackLimit = stackBase + gMaxStackSize;
366     #else
367     stackLimit = stackBase - gMaxStackSize;
368     #endif
369     }
370     JS_SetThreadStackLimit(cx, stackLimit);
371    
372     }
373    
374     static void
375     SetContextOptions(JSContext *cx)
376     {
377     SetThreadStackLimit(cx);
378     JS_SetScriptStackQuota(cx, gScriptStackQuota);
379     JS_SetOperationCallback(cx, ShellOperationCallback);
380     }
381    
382     static void
383     Process(JSContext *cx, JSObject *obj, char *filename, JSBool forceTTY)
384     {
385     JSBool ok, hitEOF;
386     JSScript *script;
387     jsval result;
388     JSString *str;
389     char *buffer;
390     size_t size;
391     int lineno;
392     int startline;
393     FILE *file;
394     uint32 oldopts;
395    
396     if (forceTTY || !filename || strcmp(filename, "-") == 0) {
397     file = stdin;
398     } else {
399     file = fopen(filename, "r");
400     if (!file) {
401     JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
402     JSSMSG_CANT_OPEN, filename, strerror(errno));
403     gExitCode = EXITCODE_FILE_NOT_FOUND;
404     return;
405     }
406     }
407    
408     SetContextOptions(cx);
409    
410     #ifndef WINCE
411     /* windows mobile (and possibly other os's) does not have a TTY */
412     if (!forceTTY && !isatty(fileno(file))) {
413     /*
414     * It's not interactive - just execute it.
415     *
416     * Support the UNIX #! shell hack; gobble the first line if it starts
417     * with '#'. TODO - this isn't quite compatible with sharp variables,
418     * as a legal js program (using sharp variables) might start with '#'.
419     * But that would require multi-character lookahead.
420     */
421     int ch = fgetc(file);
422     if (ch == '#') {
423     while((ch = fgetc(file)) != EOF) {
424     if (ch == '\n' || ch == '\r')
425     break;
426     }
427     }
428     ungetc(ch, file);
429    
430     oldopts = JS_GetOptions(cx);
431     JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO | JSOPTION_NO_SCRIPT_RVAL);
432     script = JS_CompileFileHandle(cx, obj, filename, file);
433     JS_SetOptions(cx, oldopts);
434     if (script) {
435     if (!compileOnly)
436     (void)JS_ExecuteScript(cx, obj, script, NULL);
437     JS_DestroyScript(cx, script);
438     }
439    
440     if (file != stdin)
441     fclose(file);
442     return;
443     }
444     #endif /* WINCE */
445    
446     /* It's an interactive filehandle; drop into read-eval-print loop. */
447     lineno = 1;
448     hitEOF = JS_FALSE;
449     buffer = NULL;
450     size = 0; /* assign here to avoid warnings */
451     do {
452     /*
453     * Accumulate lines until we get a 'compilable unit' - one that either
454     * generates an error (before running out of source) or that compiles
455     * cleanly. This should be whenever we get a complete statement that
456     * coincides with the end of a line.
457     */
458     startline = lineno;
459     size_t len = 0; /* initialize to avoid warnings */
460     do {
461     ScheduleWatchdog(cx->runtime, -1);
462     jsrefcount rc = JS_SuspendRequest(cx);
463     gCanceled = false;
464     errno = 0;
465     char *line = GetLine(file, startline == lineno ? "js> " : "");
466     if (!line) {
467     JS_ResumeRequest(cx, rc);
468     if (errno) {
469     JS_ReportError(cx, strerror(errno));
470     free(buffer);
471     return;
472     }
473     hitEOF = JS_TRUE;
474     break;
475     }
476     if (!buffer) {
477     buffer = line;
478     len = strlen(buffer);
479     size = len + 1;
480     } else {
481     /*
482     * len + 1 is required to store '\n' in the end of line.
483     */
484     size_t newlen = strlen(line) + (len ? len + 1 : 0);
485     if (newlen + 1 > size) {
486     size = newlen + 1 > size * 2 ? newlen + 1 : size * 2;
487     char *newBuf = (char *) realloc(buffer, size);
488     if (!newBuf) {
489     free(buffer);
490     free(line);
491     JS_ResumeRequest(cx, rc);
492     JS_ReportOutOfMemory(cx);
493     return;
494     }
495     buffer = newBuf;
496     }
497     char *current = buffer + len;
498     if (startline != lineno)
499     *current++ = '\n';
500     strcpy(current, line);
501     len = newlen;
502     free(line);
503     }
504     lineno++;
505     JS_ResumeRequest(cx, rc);
506     if (!ScheduleWatchdog(cx->runtime, gTimeoutInterval)) {
507     hitEOF = JS_TRUE;
508     break;
509     }
510     } while (!JS_BufferIsCompilableUnit(cx, obj, buffer, len));
511    
512     if (hitEOF && !buffer)
513     break;
514    
515     /* Clear any pending exception from previous failed compiles. */
516     JS_ClearPendingException(cx);
517    
518     /* Even though we're interactive, we have a compile-n-go opportunity. */
519     oldopts = JS_GetOptions(cx);
520     if (!compileOnly)
521     JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO);
522     script = JS_CompileScript(cx, obj, buffer, len, "typein",
523     startline);
524     if (!compileOnly)
525     JS_SetOptions(cx, oldopts);
526    
527     if (script) {
528     if (!compileOnly) {
529     ok = JS_ExecuteScript(cx, obj, script, &result);
530     if (ok && !JSVAL_IS_VOID(result)) {
531     str = JS_ValueToSource(cx, result);
532     if (str)
533     fprintf(gOutFile, "%s\n", JS_GetStringBytes(str));
534     else
535     ok = JS_FALSE;
536     }
537     }
538     JS_DestroyScript(cx, script);
539     }
540     *buffer = '\0';
541     } while (!hitEOF && !gQuitting);
542    
543     free(buffer);
544     fprintf(gOutFile, "\n");
545     if (file != stdin)
546     fclose(file);
547     return;
548     }
549    
550     static int
551     usage(void)
552     {
553     fprintf(gErrFile, "%s\n", JS_GetImplementationVersion());
554     fprintf(gErrFile, "usage: js [-zKPswWxCij] [-t timeoutSeconds] [-c stackchunksize] [-o option] [-v version] [-f scriptfile] [-e script] [-S maxstacksize] "
555     #ifdef JS_GC_ZEAL
556     "[-Z gczeal] "
557     #endif
558     #ifdef MOZ_TRACEVIS
559     "[-T TraceVisFileName] "
560     #endif
561     "[scriptfile] [scriptarg...]\n");
562     return 2;
563     }
564    
565     /*
566     * JSContext option name to flag map. The option names are in alphabetical
567     * order for better reporting.
568     */
569     static const struct {
570     const char *name;
571     uint32 flag;
572     } js_options[] = {
573     {"anonfunfix", JSOPTION_ANONFUNFIX},
574     {"atline", JSOPTION_ATLINE},
575     {"jit", JSOPTION_JIT},
576     {"relimit", JSOPTION_RELIMIT},
577     {"strict", JSOPTION_STRICT},
578     {"werror", JSOPTION_WERROR},
579     {"xml", JSOPTION_XML},
580     };
581    
582     static uint32
583     MapContextOptionNameToFlag(JSContext* cx, const char* name)
584     {
585     for (size_t i = 0; i != JS_ARRAY_LENGTH(js_options); ++i) {
586     if (strcmp(name, js_options[i].name) == 0)
587     return js_options[i].flag;
588     }
589    
590     char* msg = JS_sprintf_append(NULL,
591     "unknown option name '%s'."
592     " The valid names are ", name);
593     for (size_t i = 0; i != JS_ARRAY_LENGTH(js_options); ++i) {
594     if (!msg)
595     break;
596     msg = JS_sprintf_append(msg, "%s%s", js_options[i].name,
597     (i + 2 < JS_ARRAY_LENGTH(js_options)
598     ? ", "
599     : i + 2 == JS_ARRAY_LENGTH(js_options)
600     ? " and "
601     : "."));
602     }
603     if (!msg) {
604     JS_ReportOutOfMemory(cx);
605     } else {
606     JS_ReportError(cx, msg);
607     free(msg);
608     }
609     return 0;
610     }
611    
612     extern JSClass global_class;
613    
614     static int
615     ProcessArgs(JSContext *cx, JSObject *obj, char **argv, int argc)
616     {
617     int i, j, length;
618     JSObject *argsObj;
619     char *filename = NULL;
620     JSBool isInteractive = JS_TRUE;
621     JSBool forceTTY = JS_FALSE;
622    
623     /*
624     * Scan past all optional arguments so we can create the arguments object
625     * before processing any -f options, which must interleave properly with
626     * -v and -w options. This requires two passes, and without getopt, we'll
627     * have to keep the option logic here and in the second for loop in sync.
628     */
629     for (i = 0; i < argc; i++) {
630     if (argv[i][0] != '-' || argv[i][1] == '\0') {
631     ++i;
632     break;
633     }
634     switch (argv[i][1]) {
635     case 'c':
636     case 'f':
637     case 'e':
638     case 'v':
639     case 'S':
640     case 't':
641     #ifdef JS_GC_ZEAL
642     case 'Z':
643     #endif
644     #ifdef MOZ_TRACEVIS
645     case 'T':
646     #endif
647     ++i;
648     break;
649     default:;
650     }
651     }
652    
653     /*
654     * Create arguments early and define it to root it, so it's safe from any
655     * GC calls nested below, and so it is available to -f <file> arguments.
656     */
657     argsObj = JS_NewArrayObject(cx, 0, NULL);
658     if (!argsObj)
659     return 1;
660     if (!JS_DefineProperty(cx, obj, "arguments", OBJECT_TO_JSVAL(argsObj),
661     NULL, NULL, 0)) {
662     return 1;
663     }
664    
665     length = argc - i;
666     for (j = 0; j < length; j++) {
667     JSString *str = JS_NewStringCopyZ(cx, argv[i++]);
668     if (!str)
669     return 1;
670     if (!JS_DefineElement(cx, argsObj, j, STRING_TO_JSVAL(str),
671     NULL, NULL, JSPROP_ENUMERATE)) {
672     return 1;
673     }
674     }
675    
676     for (i = 0; i < argc; i++) {
677     if (argv[i][0] != '-' || argv[i][1] == '\0') {
678     filename = argv[i++];
679     isInteractive = JS_FALSE;
680     break;
681     }
682    
683     switch (argv[i][1]) {
684     case 'v':
685     if (++i == argc)
686     return usage();
687    
688     JS_SetVersion(cx, (JSVersion) atoi(argv[i]));
689     break;
690    
691     #ifdef JS_GC_ZEAL
692     case 'Z':
693     if (++i == argc)
694     return usage();
695     JS_SetGCZeal(cx, atoi(argv[i]));
696     break;
697     #endif
698    
699     case 'w':
700     reportWarnings = JS_TRUE;
701     break;
702    
703     case 'W':
704     reportWarnings = JS_FALSE;
705     break;
706    
707     case 's':
708     JS_ToggleOptions(cx, JSOPTION_STRICT);
709     break;
710    
711     case 'E':
712     JS_ToggleOptions(cx, JSOPTION_RELIMIT);
713     break;
714    
715     case 'x':
716     JS_ToggleOptions(cx, JSOPTION_XML);
717     break;
718    
719     case 'j':
720     JS_ToggleOptions(cx, JSOPTION_JIT);
721     #if defined(JS_TRACER) && defined(DEBUG)
722     extern struct JSClass jitstats_class;
723     extern void js_InitJITStatsClass(JSContext *cx, JSObject *glob);
724     js_InitJITStatsClass(cx, JS_GetGlobalObject(cx));
725     JS_DefineObject(cx, JS_GetGlobalObject(cx), "tracemonkey",
726     &jitstats_class, NULL, 0);
727     #endif
728     break;
729    
730     case 'o':
731     {
732     if (++i == argc)
733     return usage();
734    
735     uint32 flag = MapContextOptionNameToFlag(cx, argv[i]);
736     if (flag == 0)
737     return gExitCode;
738     JS_ToggleOptions(cx, flag);
739     break;
740     }
741     case 'P':
742     if (JS_GET_CLASS(cx, JS_GetPrototype(cx, obj)) != &global_class) {
743     JSObject *gobj;
744    
745     if (!JS_SealObject(cx, obj, JS_TRUE))
746     return JS_FALSE;
747     gobj = JS_NewObject(cx, &global_class, NULL, NULL);
748     if (!gobj)
749     return JS_FALSE;
750     if (!JS_SetPrototype(cx, gobj, obj))
751     return JS_FALSE;
752     JS_SetParent(cx, gobj, NULL);
753     JS_SetGlobalObject(cx, gobj);
754     obj = gobj;
755     }
756     break;
757    
758     case 't':
759     if (++i == argc)
760     return usage();
761    
762     if (!SetTimeoutValue(cx, atof(argv[i])))
763     return JS_FALSE;
764    
765     break;
766    
767     case 'c':
768     /* set stack chunk size */
769     gStackChunkSize = atoi(argv[++i]);
770     break;
771    
772     case 'f':
773     if (++i == argc)
774     return usage();
775    
776     Process(cx, obj, argv[i], JS_FALSE);
777     if (gExitCode != 0)
778     return gExitCode;
779    
780     /*
781     * XXX: js -f foo.js should interpret foo.js and then
782     * drop into interactive mode, but that breaks the test
783     * harness. Just execute foo.js for now.
784     */
785     isInteractive = JS_FALSE;
786     break;
787    
788     case 'e':
789     {
790     jsval rval;
791    
792     if (++i == argc)
793     return usage();
794    
795     /* Pass a filename of -e to imitate PERL */
796     JS_EvaluateScript(cx, obj, argv[i], strlen(argv[i]),
797     "-e", 1, &rval);
798    
799     isInteractive = JS_FALSE;
800     break;
801    
802     }
803     case 'C':
804     compileOnly = JS_TRUE;
805     isInteractive = JS_FALSE;
806     break;
807    
808     case 'i':
809     isInteractive = forceTTY = JS_TRUE;
810     break;
811    
812     case 'S':
813     if (++i == argc)
814     return usage();
815    
816     /* Set maximum stack size. */
817     gMaxStackSize = atoi(argv[i]);
818     break;
819    
820     case 'z':
821     obj = split_setup(cx);
822     if (!obj)
823     return gExitCode;
824     break;
825     #ifdef MOZ_SHARK
826     case 'k':
827     JS_ConnectShark();
828     break;
829     #endif
830     #ifdef MOZ_TRACEVIS
831     case 'T':
832     if (++i == argc)
833     return usage();
834    
835     JS_StartTraceVis(argv[i]);
836     break;
837     #endif
838     default:
839     return usage();
840     }
841     }
842    
843     if (filename || isInteractive)
844     Process(cx, obj, filename, forceTTY);
845     return gExitCode;
846     }
847    
848     static JSBool
849     Version(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
850     {
851     if (argc > 0 && JSVAL_IS_INT(argv[0]))
852     *rval = INT_TO_JSVAL(JS_SetVersion(cx, (JSVersion) JSVAL_TO_INT(argv[0])));
853     else
854     *rval = INT_TO_JSVAL(JS_GetVersion(cx));
855     return JS_TRUE;
856     }
857    
858     static JSBool
859     Options(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
860     {
861     uint32 optset, flag;
862     JSString *str;
863     const char *opt;
864     char *names;
865     JSBool found;
866    
867     optset = 0;
868     for (uintN i = 0; i < argc; i++) {
869     str = JS_ValueToString(cx, argv[i]);
870     if (!str)
871     return JS_FALSE;
872     argv[i] = STRING_TO_JSVAL(str);
873     opt = JS_GetStringBytes(str);
874     if (!opt)
875     return JS_FALSE;
876     flag = MapContextOptionNameToFlag(cx, opt);
877     if (!flag)
878     return JS_FALSE;
879     optset |= flag;
880     }
881     optset = JS_ToggleOptions(cx, optset);
882    
883     names = NULL;
884     found = JS_FALSE;
885     for (size_t i = 0; i != JS_ARRAY_LENGTH(js_options); i++) {
886     if (js_options[i].flag & optset) {
887     found = JS_TRUE;
888     names = JS_sprintf_append(names, "%s%s",
889     names ? "," : "", js_options[i].name);
890     if (!names)
891     break;
892     }
893     }
894     if (!found)
895     names = strdup("");
896     if (!names) {
897     JS_ReportOutOfMemory(cx);
898     return JS_FALSE;
899     }
900     str = JS_NewString(cx, names, strlen(names));
901     if (!str) {
902     free(names);
903     return JS_FALSE;
904     }
905     *rval = STRING_TO_JSVAL(str);
906     return JS_TRUE;
907     }
908    
909     static JSBool
910     Load(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
911     {
912     uintN i;
913     JSString *str;
914     const char *filename;
915     JSScript *script;
916     JSBool ok;
917     uint32 oldopts;
918    
919     for (i = 0; i < argc; i++) {
920     str = JS_ValueToString(cx, argv[i]);
921     if (!str)
922     return JS_FALSE;
923     argv[i] = STRING_TO_JSVAL(str);
924     filename = JS_GetStringBytes(str);
925     errno = 0;
926     oldopts = JS_GetOptions(cx);
927     JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO | JSOPTION_NO_SCRIPT_RVAL);
928     script = JS_CompileFile(cx, obj, filename);
929     JS_SetOptions(cx, oldopts);
930     if (!script) {
931     ok = JS_FALSE;
932     } else {
933     ok = !compileOnly
934     ? JS_ExecuteScript(cx, obj, script, NULL)
935     : JS_TRUE;
936     JS_DestroyScript(cx, script);
937     }
938     if (!ok)
939     return JS_FALSE;
940     }
941    
942     return JS_TRUE;
943     }
944    
945     /*
946     * function readline()
947     * Provides a hook for scripts to read a line from stdin.
948     */
949     static JSBool
950     ReadLine(JSContext *cx, uintN argc, jsval *vp)
951     {
952     #define BUFSIZE 256
953     FILE *from;
954     char *buf, *tmp;
955     size_t bufsize, buflength, gotlength;
956     JSBool sawNewline;
957     JSString *str;
958    
959     from = stdin;
960     buflength = 0;
961     bufsize = BUFSIZE;
962     buf = (char *) JS_malloc(cx, bufsize);
963     if (!buf)
964     return JS_FALSE;
965    
966     sawNewline = JS_FALSE;
967     while ((gotlength =
968     js_fgets(buf + buflength, bufsize - buflength, from)) > 0) {
969     buflength += gotlength;
970    
971     /* Are we done? */
972     if (buf[buflength - 1] == '\n') {
973     buf[buflength - 1] = '\0';
974     sawNewline = JS_TRUE;
975     break;
976     } else if (buflength < bufsize - 1) {
977     break;
978     }
979    
980     /* Else, grow our buffer for another pass. */
981     bufsize *= 2;
982     if (bufsize > buflength) {
983     tmp = (char *) JS_realloc(cx, buf, bufsize);
984     } else {
985     JS_ReportOutOfMemory(cx);
986     tmp = NULL;
987     }
988    
989     if (!tmp) {
990     JS_free(cx, buf);
991     return JS_FALSE;
992     }
993    
994     buf = tmp;
995     }
996    
997     /* Treat the empty string specially. */
998     if (buflength == 0) {
999     *vp = feof(from) ? JSVAL_NULL : JS_GetEmptyStringValue(cx);
1000     JS_free(cx, buf);
1001     return JS_TRUE;
1002     }
1003    
1004     /* Shrink the buffer to the real size. */
1005     tmp = (char *) JS_realloc(cx, buf, buflength);
1006     if (!tmp) {
1007     JS_free(cx, buf);
1008     return JS_FALSE;
1009     }
1010    
1011     buf = tmp;
1012    
1013     /*
1014     * Turn buf into a JSString. Note that buflength includes the trailing null
1015     * character.
1016     */
1017     str = JS_NewString(cx, buf, sawNewline ? buflength - 1 : buflength);
1018     if (!str) {
1019     JS_free(cx, buf);
1020     return JS_FALSE;
1021     }
1022    
1023     *vp = STRING_TO_JSVAL(str);
1024     return JS_TRUE;
1025     }
1026    
1027     static JSBool
1028     Print(JSContext *cx, uintN argc, jsval *vp)
1029     {
1030     jsval *argv;
1031     uintN i;
1032     JSString *str;
1033     char *bytes;
1034    
1035     argv = JS_ARGV(cx, vp);
1036     for (i = 0; i < argc; i++) {
1037     str = JS_ValueToString(cx, argv[i]);
1038     if (!str)
1039     return JS_FALSE;
1040     bytes = JS_EncodeString(cx, str);
1041     if (!bytes)
1042     return JS_FALSE;
1043     fprintf(gOutFile, "%s%s", i ? " " : "", bytes);
1044     JS_free(cx, bytes);
1045     }
1046    
1047     fputc('\n', gOutFile);
1048     fflush(gOutFile);
1049    
1050     JS_SET_RVAL(cx, vp, JSVAL_VOID);
1051     return JS_TRUE;
1052     }
1053    
1054     static JSBool
1055     Help(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
1056    
1057     static JSBool
1058     Quit(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1059     {
1060     JS_ConvertArguments(cx, argc, argv,"/ i", &gExitCode);
1061    
1062     gQuitting = JS_TRUE;
1063     return JS_FALSE;
1064     }
1065    
1066     static const char *
1067     ToSource(JSContext *cx, jsval *vp)
1068     {
1069     JSString *str = JS_ValueToSource(cx, *vp);
1070     if (str) {
1071     *vp = STRING_TO_JSVAL(str);
1072     return JS_GetStringBytes(str);
1073     }
1074     JS_ClearPendingException(cx);
1075     return "<<error converting value to string>>";
1076     }
1077    
1078     static JSBool
1079     AssertEq(JSContext *cx, uintN argc, jsval *vp)
1080     {
1081     if (!(argc == 2 || (argc == 3 && JSVAL_IS_STRING(JS_ARGV(cx, vp)[2])))) {
1082     JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
1083     (argc < 2)
1084     ? JSSMSG_NOT_ENOUGH_ARGS
1085     : (argc == 3)
1086     ? JSSMSG_INVALID_ARGS
1087     : JSSMSG_TOO_MANY_ARGS,
1088     "assertEq");
1089     return JS_FALSE;
1090     }
1091    
1092     jsval *argv = JS_ARGV(cx, vp);
1093     if (!JS_SameValue(cx, argv[0], argv[1])) {
1094     const char *actual = ToSource(cx, &argv[0]);
1095     const char *expected = ToSource(cx, &argv[1]);
1096     if (argc == 2) {
1097     JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_ASSERT_EQ_FAILED,
1098     actual, expected);
1099     } else {
1100     JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_ASSERT_EQ_FAILED_MSG,
1101     actual, expected, JS_GetStringBytes(JSVAL_TO_STRING(argv[2])));
1102     }
1103     return JS_FALSE;
1104     }
1105     JS_SET_RVAL(cx, vp, JSVAL_VOID);
1106     return JS_TRUE;
1107     }
1108    
1109     static JSBool
1110     GC(JSContext *cx, uintN argc, jsval *vp)
1111     {
1112     JSRuntime *rt;
1113     uint32 preBytes;
1114    
1115     rt = cx->runtime;
1116     preBytes = rt->gcBytes;
1117     JS_GC(cx);
1118    
1119     char buf[256];
1120     JS_snprintf(buf, sizeof(buf), "before %lu, after %lu, break %08lx\n",
1121     (unsigned long)preBytes, (unsigned long)rt->gcBytes,
1122     #ifdef HAVE_SBRK
1123     (unsigned long)sbrk(0)
1124     #else
1125     0
1126     #endif
1127     );
1128     #ifdef JS_GCMETER
1129     js_DumpGCStats(rt, stdout);
1130     #endif
1131     *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, buf));
1132     return JS_TRUE;
1133     }
1134    
1135     static JSBool
1136     GCParameter(JSContext *cx, uintN argc, jsval *vp)
1137     {
1138     JSString *str;
1139     const char *paramName;
1140     JSGCParamKey param;
1141     uint32 value;
1142    
1143     if (argc == 0) {
1144     str = JS_ValueToString(cx, JSVAL_VOID);
1145     JS_ASSERT(str);
1146     } else {
1147     str = JS_ValueToString(cx, vp[2]);
1148     if (!str)
1149     return JS_FALSE;
1150     vp[2] = STRING_TO_JSVAL(str);
1151     }
1152     paramName = JS_GetStringBytes(str);
1153     if (!paramName)
1154     return JS_FALSE;
1155     if (strcmp(paramName, "maxBytes") == 0) {
1156     param = JSGC_MAX_BYTES;
1157     } else if (strcmp(paramName, "maxMallocBytes") == 0) {
1158     param = JSGC_MAX_MALLOC_BYTES;
1159     } else if (strcmp(paramName, "gcStackpoolLifespan") == 0) {
1160     param = JSGC_STACKPOOL_LIFESPAN;
1161     } else if (strcmp(paramName, "gcBytes") == 0) {
1162     param = JSGC_BYTES;
1163     } else if (strcmp(paramName, "gcNumber") == 0) {
1164     param = JSGC_NUMBER;
1165     } else if (strcmp(paramName, "gcTriggerFactor") == 0) {
1166     param = JSGC_TRIGGER_FACTOR;
1167     } else {
1168     JS_ReportError(cx,
1169     "the first argument argument must be maxBytes, "
1170     "maxMallocBytes, gcStackpoolLifespan, gcBytes, "
1171     "gcNumber or gcTriggerFactor");
1172     return JS_FALSE;
1173     }
1174    
1175     if (argc == 1) {
1176     value = JS_GetGCParameter(cx->runtime, param);
1177     return JS_NewNumberValue(cx, value, &vp[0]);
1178     }
1179    
1180     if (param == JSGC_NUMBER ||
1181     param == JSGC_BYTES) {
1182     JS_ReportError(cx, "Attempt to change read-only parameter %s",
1183     paramName);
1184     return JS_FALSE;
1185     }
1186    
1187     if (!JS_ValueToECMAUint32(cx, vp[3], &value)) {
1188     JS_ReportError(cx,
1189     "the second argument must be convertable to uint32 "
1190     "with non-zero value");
1191     return JS_FALSE;
1192     }
1193     if (param == JSGC_TRIGGER_FACTOR && value < 100) {
1194     JS_ReportError(cx,
1195     "the gcTriggerFactor value must be >= 100");
1196     return JS_FALSE;
1197     }
1198     JS_SetGCParameter(cx->runtime, param, value);
1199     *vp = JSVAL_VOID;
1200     return JS_TRUE;
1201     }
1202    
1203     #ifdef JS_GC_ZEAL
1204     static JSBool
1205     GCZeal(JSContext *cx, uintN argc, jsval *vp)
1206     {
1207     uint32 zeal;
1208    
1209     if (!JS_ValueToECMAUint32(cx, argc == 0 ? JSVAL_VOID : vp[2], &zeal))
1210     return JS_FALSE;
1211     JS_SetGCZeal(cx, (uint8)zeal);
1212     *vp = JSVAL_VOID;
1213     return JS_TRUE;
1214     }
1215     #endif /* JS_GC_ZEAL */
1216    
1217     typedef struct JSCountHeapNode JSCountHeapNode;
1218    
1219     struct JSCountHeapNode {
1220     void *thing;
1221     int32 kind;
1222     JSCountHeapNode *next;
1223     };
1224    
1225     typedef struct JSCountHeapTracer {
1226     JSTracer base;
1227     JSDHashTable visited;
1228     JSBool ok;
1229     JSCountHeapNode *traceList;
1230     JSCountHeapNode *recycleList;
1231     } JSCountHeapTracer;
1232    
1233     static void
1234     CountHeapNotify(JSTracer *trc, void *thing, uint32 kind)
1235     {
1236     JSCountHeapTracer *countTracer;
1237     JSDHashEntryStub *entry;
1238     JSCountHeapNode *node;
1239    
1240     JS_ASSERT(trc->callback == CountHeapNotify);
1241     countTracer = (JSCountHeapTracer *)trc;
1242     if (!countTracer->ok)
1243     return;
1244    
1245     entry = (JSDHashEntryStub *)
1246     JS_DHashTableOperate(&countTracer->visited, thing, JS_DHASH_ADD);
1247     if (!entry) {
1248     JS_ReportOutOfMemory(trc->context);
1249     countTracer->ok = JS_FALSE;
1250     return;
1251     }
1252     if (entry->key)
1253     return;
1254     entry->key = thing;
1255    
1256     node = countTracer->recycleList;
1257     if (node) {
1258     countTracer->recycleList = node->next;
1259     } else {
1260     node = (JSCountHeapNode *) JS_malloc(trc->context, sizeof *node);
1261     if (!node) {
1262     countTracer->ok = JS_FALSE;
1263     return;
1264     }
1265     }
1266     node->thing = thing;
1267     node->kind = kind;
1268     node->next = countTracer->traceList;
1269     countTracer->traceList = node;
1270     }
1271    
1272     static JSBool
1273     CountHeap(JSContext *cx, uintN argc, jsval *vp)
1274     {
1275     void* startThing;
1276     int32 startTraceKind;
1277     jsval v;
1278     int32 traceKind, i;
1279     JSString *str;
1280     char *bytes;
1281     JSCountHeapTracer countTracer;
1282     JSCountHeapNode *node;
1283     size_t counter;
1284    
1285     static const struct {
1286     const char *name;
1287     int32 kind;
1288     } traceKindNames[] = {
1289     { "all", -1 },
1290     { "object", JSTRACE_OBJECT },
1291     { "double", JSTRACE_DOUBLE },
1292     { "string", JSTRACE_STRING },
1293     #if JS_HAS_XML_SUPPORT
1294     { "xml", JSTRACE_XML },
1295     #endif
1296     };
1297    
1298     startThing = NULL;
1299     startTraceKind = 0;
1300     if (argc > 0) {
1301     v = JS_ARGV(cx, vp)[0];
1302     if (JSVAL_IS_TRACEABLE(v)) {
1303     startThing = JSVAL_TO_TRACEABLE(v);
1304     startTraceKind = JSVAL_TRACE_KIND(v);
1305     } else if (v != JSVAL_NULL) {
1306     JS_ReportError(cx,
1307     "the first argument is not null or a heap-allocated "
1308     "thing");
1309     return JS_FALSE;
1310     }
1311     }
1312    
1313     traceKind = -1;
1314     if (argc > 1) {
1315     str = JS_ValueToString(cx, JS_ARGV(cx, vp)[1]);
1316     if (!str)
1317     return JS_FALSE;
1318     bytes = JS_GetStringBytes(str);
1319     if (!bytes)
1320     return JS_FALSE;
1321     for (i = 0; ;) {
1322     if (strcmp(bytes, traceKindNames[i].name) == 0) {
1323     traceKind = traceKindNames[i].kind;
1324     break;
1325     }
1326     if (++i == JS_ARRAY_LENGTH(traceKindNames)) {
1327     JS_ReportError(cx, "trace kind name '%s' is unknown", bytes);
1328     return JS_FALSE;
1329     }
1330     }
1331     }
1332    
1333     JS_TRACER_INIT(&countTracer.base, cx, CountHeapNotify);
1334     if (!JS_DHashTableInit(&countTracer.visited, JS_DHashGetStubOps(),
1335     NULL, sizeof(JSDHashEntryStub),
1336     JS_DHASH_DEFAULT_CAPACITY(100))) {
1337     JS_ReportOutOfMemory(cx);
1338     return JS_FALSE;
1339     }
1340     countTracer.ok = JS_TRUE;
1341     countTracer.traceList = NULL;
1342     countTracer.recycleList = NULL;
1343    
1344     if (!startThing) {
1345     JS_TraceRuntime(&countTracer.base);
1346     } else {
1347     JS_SET_TRACING_NAME(&countTracer.base, "root");
1348     JS_CallTracer(&countTracer.base, startThing, startTraceKind);
1349     }
1350    
1351     counter = 0;
1352     while ((node = countTracer.traceList) != NULL) {
1353     if (traceKind == -1 || node->kind == traceKind)
1354     counter++;
1355     countTracer.traceList = node->next;
1356     node->next = countTracer.recycleList;
1357     countTracer.recycleList = node;
1358     JS_TraceChildren(&countTracer.base, node->thing, node->kind);
1359     }
1360     while ((node = countTracer.recycleList) != NULL) {
1361     countTracer.recycleList = node->next;
1362     JS_free(cx, node);
1363     }
1364     JS_DHashTableFinish(&countTracer.visited);
1365    
1366     return countTracer.ok && JS_NewNumberValue(cx, (jsdouble) counter, vp);
1367     }
1368    
1369     static JSScript *
1370     ValueToScript(JSContext *cx, jsval v)
1371     {
1372     JSScript *script = NULL;
1373     JSFunction *fun;
1374    
1375     if (!JSVAL_IS_PRIMITIVE(v)) {
1376     JSObject *obj = JSVAL_TO_OBJECT(v);
1377     JSClass *clasp = JS_GET_CLASS(cx, obj);
1378    
1379     if (clasp == &js_ScriptClass) {
1380     script = (JSScript *) JS_GetPrivate(cx, obj);
1381     } else if (clasp == &js_GeneratorClass) {
1382     JSGenerator *gen = (JSGenerator *) JS_GetPrivate(cx, obj);
1383     fun = gen->frame.fun;
1384     script = FUN_SCRIPT(fun);
1385     }
1386     }
1387    
1388     if (!script) {
1389     fun = JS_ValueToFunction(cx, v);
1390     if (!fun)
1391     return NULL;
1392     script = FUN_SCRIPT(fun);
1393     if (!script) {
1394     JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
1395     JSSMSG_SCRIPTS_ONLY);
1396     }
1397     }
1398    
1399     return script;
1400     }
1401    
1402     static JSBool
1403     GetTrapArgs(JSContext *cx, uintN argc, jsval *argv, JSScript **scriptp,
1404     int32 *ip)
1405     {
1406     jsval v;
1407     uintN intarg;
1408     JSScript *script;
1409    
1410     *scriptp = JS_GetScriptedCaller(cx, NULL)->script;
1411     *ip = 0;
1412     if (argc != 0) {
1413     v = argv[0];
1414     intarg = 0;
1415     if (!JSVAL_IS_PRIMITIVE(v) &&
1416     (JS_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_FunctionClass ||
1417     JS_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_ScriptClass)) {
1418     script = ValueToScript(cx, v);
1419     if (!script)
1420     return JS_FALSE;
1421     *scriptp = script;
1422     intarg++;
1423     }
1424     if (argc > intarg) {
1425     if (!JS_ValueToInt32(cx, argv[intarg], ip))
1426     return JS_FALSE;
1427     }
1428     }
1429     return JS_TRUE;
1430     }
1431    
1432     static JSTrapStatus
1433     TrapHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval,
1434     void *closure)
1435     {
1436     JSString *str;
1437     JSStackFrame *caller;
1438    
1439     str = (JSString *) closure;
1440     caller = JS_GetScriptedCaller(cx, NULL);
1441     if (!JS_EvaluateScript(cx, caller->scopeChain,
1442     JS_GetStringBytes(str), JS_GetStringLength(str),
1443     caller->script->filename, caller->script->lineno,
1444     rval)) {
1445     return JSTRAP_ERROR;
1446     }
1447     if (!JSVAL_IS_VOID(*rval))
1448     return JSTRAP_RETURN;
1449     return JSTRAP_CONTINUE;
1450     }
1451    
1452     static JSBool
1453     Trap(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1454     {
1455     JSString *str;
1456     JSScript *script;
1457     int32 i;
1458    
1459     if (argc == 0) {
1460     JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_TRAP_USAGE);
1461     return JS_FALSE;
1462     }
1463     argc--;
1464     str = JS_ValueToString(cx, argv[argc]);
1465     if (!str)
1466     return JS_FALSE;
1467     argv[argc] = STRING_TO_JSVAL(str);
1468     if (!GetTrapArgs(cx, argc, argv, &script, &i))
1469     return JS_FALSE;
1470     return JS_SetTrap(cx, script, script->code + i, TrapHandler, str);
1471     }
1472    
1473     static JSBool
1474     Untrap(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1475     {
1476     JSScript *script;
1477     int32 i;
1478    
1479     if (!GetTrapArgs(cx, argc, argv, &script, &i))
1480     return JS_FALSE;
1481     JS_ClearTrap(cx, script, script->code + i, NULL, NULL);
1482     return JS_TRUE;
1483     }
1484    
1485     static JSBool
1486     LineToPC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1487     {
1488     JSScript *script;
1489     int32 i;
1490     uintN lineno;
1491     jsbytecode *pc;
1492    
1493     if (argc == 0) {
1494     JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_LINE2PC_USAGE);
1495     return JS_FALSE;
1496     }
1497     script = JS_GetScriptedCaller(cx, NULL)->script;
1498     if (!GetTrapArgs(cx, argc, argv, &script, &i))
1499     return JS_FALSE;
1500     lineno = (i == 0) ? script->lineno : (uintN)i;
1501     pc = JS_LineNumberToPC(cx, script, lineno);
1502     if (!pc)
1503     return JS_FALSE;
1504     *rval = INT_TO_JSVAL(pc - script->code);
1505     return JS_TRUE;
1506     }
1507    
1508     static JSBool
1509     PCToLine(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1510     {
1511     JSScript *script;
1512     int32 i;
1513     uintN lineno;
1514    
1515     if (!GetTrapArgs(cx, argc, argv, &script, &i))
1516     return JS_FALSE;
1517     lineno = JS_PCToLineNumber(cx, script, script->code + i);
1518     if (!lineno)
1519     return JS_FALSE;
1520     *rval = INT_TO_JSVAL(lineno);
1521     return JS_TRUE;
1522     }
1523    
1524     #ifdef DEBUG
1525    
1526     static void
1527     UpdateSwitchTableBounds(JSContext *cx, JSScript *script, uintN offset,
1528     uintN *start, uintN *end)
1529     {
1530     jsbytecode *pc;
1531     JSOp op;
1532     ptrdiff_t jmplen;
1533     jsint low, high, n;
1534    
1535     pc = script->code + offset;
1536     op = js_GetOpcode(cx, script, pc);
1537     switch (op) {
1538     case JSOP_TABLESWITCHX:
1539     jmplen = JUMPX_OFFSET_LEN;
1540     goto jump_table;
1541     case JSOP_TABLESWITCH:
1542     jmplen = JUMP_OFFSET_LEN;
1543     jump_table:
1544     pc += jmplen;
1545     low = GET_JUMP_OFFSET(pc);
1546     pc += JUMP_OFFSET_LEN;
1547     high = GET_JUMP_OFFSET(pc);
1548     pc += JUMP_OFFSET_LEN;
1549     n = high - low + 1;
1550     break;
1551    
1552     case JSOP_LOOKUPSWITCHX:
1553     jmplen = JUMPX_OFFSET_LEN;
1554     goto lookup_table;
1555     case JSOP_LOOKUPSWITCH:
1556     jmplen = JUMP_OFFSET_LEN;
1557     lookup_table:
1558     pc += jmplen;
1559     n = GET_INDEX(pc);
1560     pc += INDEX_LEN;
1561     jmplen += JUMP_OFFSET_LEN;
1562     break;
1563    
1564     default:
1565     /* [condswitch] switch does not have any jump or lookup tables. */
1566     JS_ASSERT(op == JSOP_CONDSWITCH);
1567     return;
1568     }
1569    
1570     *start = (uintN)(pc - script->code);
1571     *end = *start + (uintN)(n * jmplen);
1572     }
1573    
1574     static void
1575     SrcNotes(JSContext *cx, JSScript *script)
1576     {
1577     uintN offset, delta, caseOff, switchTableStart, switchTableEnd;
1578     jssrcnote *notes, *sn;
1579     JSSrcNoteType type;
1580     const char *name;
1581     uint32 index;
1582     JSAtom *atom;
1583     JSString *str;
1584    
1585     fprintf(gOutFile, "\nSource notes:\n");
1586     offset = 0;
1587     notes = script->notes();
1588     switchTableEnd = switchTableStart = 0;
1589     for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
1590     delta = SN_DELTA(sn);
1591     offset += delta;
1592     type = (JSSrcNoteType) SN_TYPE(sn);
1593     name = js_SrcNoteSpec[type].name;
1594     if (type == SRC_LABEL) {
1595     /* Check if the source note is for a switch case. */
1596     if (switchTableStart <= offset && offset < switchTableEnd) {
1597     name = "case";
1598     } else {
1599     JS_ASSERT(js_GetOpcode(cx, script, script->code + offset) == JSOP_NOP);
1600     }
1601     }
1602     fprintf(gOutFile, "%3u: %5u [%4u] %-8s",
1603     (uintN) (sn - notes), offset, delta, name);
1604     switch (type) {
1605     case SRC_SETLINE:
1606     fprintf(gOutFile, " lineno %u", (uintN) js_GetSrcNoteOffset(sn, 0));
1607     break;
1608     case SRC_FOR:
1609     fprintf(gOutFile, " cond %u update %u tail %u",
1610     (uintN) js_GetSrcNoteOffset(sn, 0),
1611     (uintN) js_GetSrcNoteOffset(sn, 1),
1612     (uintN) js_GetSrcNoteOffset(sn, 2));
1613     break;
1614     case SRC_IF_ELSE:
1615     fprintf(gOutFile, " else %u elseif %u",
1616     (uintN) js_GetSrcNoteOffset(sn, 0),
1617     (uintN) js_GetSrcNoteOffset(sn, 1));
1618     break;
1619     case SRC_COND:
1620     case SRC_WHILE:
1621     case SRC_PCBASE:
1622     case SRC_PCDELTA:
1623     case SRC_DECL:
1624     case SRC_BRACE:
1625     fprintf(gOutFile, " offset %u", (uintN) js_GetSrcNoteOffset(sn, 0));
1626     break;
1627     case SRC_LABEL:
1628     case SRC_LABELBRACE:
1629     case SRC_BREAK2LABEL:
1630     case SRC_CONT2LABEL:
1631     index = js_GetSrcNoteOffset(sn, 0);
1632     JS_GET_SCRIPT_ATOM(script, NULL, index, atom);
1633     JS_ASSERT(ATOM_IS_STRING(atom));
1634     str = ATOM_TO_STRING(atom);
1635     fprintf(gOutFile, " atom %u (", index);
1636     js_FileEscapedString(gOutFile, str, 0);
1637     putc(')', gOutFile);
1638     break;
1639     case SRC_FUNCDEF: {
1640     const char *bytes;
1641     JSObject *obj;
1642     JSFunction *fun;
1643    
1644     index = js_GetSrcNoteOffset(sn, 0);
1645     obj = script->getObject(index);
1646     fun = (JSFunction *) JS_GetPrivate(cx, obj);
1647     str = JS_DecompileFunction(cx, fun, JS_DONT_PRETTY_PRINT);
1648     if (str) {
1649     bytes = JS_GetStringBytes(str);
1650     } else {
1651     if (JS_IsExceptionPending(cx)) {
1652     if (!JS_ReportPendingException(cx))
1653     JS_ClearPendingException(cx);
1654     }
1655     bytes = "N/A";
1656     }
1657     fprintf(gOutFile, " function %u (%s)", index, bytes);
1658     break;
1659     }
1660     case SRC_SWITCH:
1661     fprintf(gOutFile, " length %u", (uintN) js_GetSrcNoteOffset(sn, 0));
1662     caseOff = (uintN) js_GetSrcNoteOffset(sn, 1);
1663     if (caseOff)
1664     fprintf(gOutFile, " first case offset %u", caseOff);
1665     UpdateSwitchTableBounds(cx, script, offset,
1666     &switchTableStart, &switchTableEnd);
1667     break;
1668     case SRC_CATCH:
1669     delta = (uintN) js_GetSrcNoteOffset(sn, 0);
1670     if (delta) {
1671     if (script->main[offset] == JSOP_LEAVEBLOCK)
1672     fprintf(gOutFile, " stack depth %u", delta);
1673     else
1674     fprintf(gOutFile, " guard delta %u", delta);
1675     }
1676     break;
1677     default:;
1678     }
1679     fputc('\n', gOutFile);
1680     }
1681     }
1682    
1683     static JSBool
1684     Notes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1685     {
1686     uintN i;
1687     JSScript *script;
1688    
1689     for (i = 0; i < argc; i++) {
1690     script = ValueToScript(cx, argv[i]);
1691     if (!script)
1692     continue;
1693    
1694     SrcNotes(cx, script);
1695     }
1696     return JS_TRUE;
1697     }
1698    
1699     JS_STATIC_ASSERT(JSTRY_CATCH == 0);
1700     JS_STATIC_ASSERT(JSTRY_FINALLY == 1);
1701     JS_STATIC_ASSERT(JSTRY_ITER == 2);
1702    
1703     static const char* const TryNoteNames[] = { "catch", "finally", "iter" };
1704    
1705     static JSBool
1706     TryNotes(JSContext *cx, JSScript *script)
1707     {
1708     JSTryNote *tn, *tnlimit;
1709    
1710     if (script->trynotesOffset == 0)
1711     return JS_TRUE;
1712    
1713     tn = script->trynotes()->vector;
1714     tnlimit = tn + script->trynotes()->length;
1715     fprintf(gOutFile, "\nException table:\n"
1716     "kind stack start end\n");
1717     do {
1718     JS_ASSERT(tn->kind < JS_ARRAY_LENGTH(TryNoteNames));
1719     fprintf(gOutFile, " %-7s %6u %8u %8u\n",
1720     TryNoteNames[tn->kind], tn->stackDepth,
1721     tn->start, tn->start + tn->length);
1722     } while (++tn != tnlimit);
1723     return JS_TRUE;
1724     }
1725    
1726     static bool
1727     DisassembleValue(JSContext *cx, jsval v, bool lines, bool recursive)
1728     {
1729     JSScript *script = ValueToScript(cx, v);
1730     if (!script)
1731     return false;
1732     if (VALUE_IS_FUNCTION(cx, v)) {
1733     JSFunction *fun = JS_ValueToFunction(cx, v);
1734     if (fun && (fun->flags & ~7U)) {
1735     uint16 flags = fun->flags;
1736     fputs("flags:", stdout);
1737    
1738     #define SHOW_FLAG(flag) if (flags & JSFUN_##flag) fputs(" " #flag, stdout);
1739    
1740     SHOW_FLAG(LAMBDA);
1741     SHOW_FLAG(SETTER);
1742     SHOW_FLAG(GETTER);
1743     SHOW_FLAG(BOUND_METHOD);
1744     SHOW_FLAG(HEAVYWEIGHT);
1745     SHOW_FLAG(THISP_STRING);
1746     SHOW_FLAG(THISP_NUMBER);
1747     SHOW_FLAG(THISP_BOOLEAN);
1748     SHOW_FLAG(EXPR_CLOSURE);
1749     SHOW_FLAG(TRCINFO);
1750    
1751     #undef SHOW_FLAG
1752    
1753     if (FUN_NULL_CLOSURE(fun))
1754     fputs(" NULL_CLOSURE", stdout);
1755     else if (FUN_FLAT_CLOSURE(fun))
1756     fputs(" FLAT_CLOSURE", stdout);
1757     putchar('\n');
1758     }
1759     }
1760    
1761     if (!js_Disassemble(cx, script, lines, stdout))
1762     return false;
1763     SrcNotes(cx, script);
1764     TryNotes(cx, script);
1765    
1766     if (recursive && script->objectsOffset != 0) {
1767     JSObjectArray *objects = script->objects();
1768     for (uintN i = 0; i != objects->length; ++i) {
1769     JSObject *obj = objects->vector[i];
1770     if (HAS_FUNCTION_CLASS(obj)) {
1771     putchar('\n');
1772     if (!DisassembleValue(cx, OBJECT_TO_JSVAL(obj),
1773     lines, recursive)) {
1774     return false;
1775     }
1776     }
1777     }
1778     }
1779     return true;
1780     }
1781    
1782     static JSBool
1783     Disassemble(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1784     {
1785     bool lines = false, recursive = false;
1786    
1787     while (argc > 0 && JSVAL_IS_STRING(argv[0])) {
1788     const char *bytes = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
1789     lines = !strcmp(bytes, "-l");
1790     recursive = !strcmp(bytes, "-r");
1791     if (!lines && !recursive)
1792     break;
1793     argv++, argc--;
1794     }
1795    
1796     for (uintN i = 0; i < argc; i++) {
1797     if (!DisassembleValue(cx, argv[i], lines, recursive))
1798     return false;
1799     }
1800     return true;
1801     }
1802    
1803     static JSBool
1804     DisassFile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1805     {
1806     JSString *str;
1807     const char *filename;
1808     JSScript *script;
1809     JSBool ok;
1810     uint32 oldopts;
1811    
1812     if (!argc)
1813     return JS_TRUE;
1814    
1815     str = JS_ValueToString(cx, argv[0]);
1816     if (!str)
1817     return JS_FALSE;
1818     argv[0] = STRING_TO_JSVAL(str);
1819    
1820     filename = JS_GetStringBytes(str);
1821     oldopts = JS_GetOptions(cx);
1822     JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO | JSOPTION_NO_SCRIPT_RVAL);
1823     script = JS_CompileFile(cx, obj, filename);
1824     JS_SetOptions(cx, oldopts);
1825     if (!script)
1826     return JS_FALSE;
1827    
1828     obj = JS_NewScriptObject(cx, script);
1829     if (!obj)
1830     return JS_FALSE;
1831    
1832     *rval = OBJECT_TO_JSVAL(obj); /* I like to root it, root it. */
1833     ok = Disassemble(cx, obj, 1, rval, rval); /* gross, but works! */
1834     *rval = JSVAL_VOID;
1835    
1836     return ok;
1837     }
1838    
1839     static JSBool
1840     DisassWithSrc(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1841     jsval *rval)
1842     {
1843     #define LINE_BUF_LEN 512
1844     uintN i, len, line1, line2, bupline;
1845     JSScript *script;
1846     FILE *file;
1847     char linebuf[LINE_BUF_LEN];
1848     jsbytecode *pc, *end;
1849     JSBool ok;
1850     static char sep[] = ";-------------------------";
1851    
1852     ok = JS_TRUE;
1853     for (i = 0; ok && i < argc; i++) {
1854     script = ValueToScript(cx, argv[i]);
1855     if (!script)
1856     return JS_FALSE;
1857    
1858     if (!script->filename) {
1859     JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
1860     JSSMSG_FILE_SCRIPTS_ONLY);
1861     return JS_FALSE;
1862     }
1863    
1864     file = fopen(script->filename, "r");
1865     if (!file) {
1866     JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
1867     JSSMSG_CANT_OPEN, script->filename,
1868     strerror(errno));
1869     return JS_FALSE;
1870     }
1871    
1872     pc = script->code;
1873     end = pc + script->length;
1874    
1875     /* burn the leading lines */
1876     line2 = JS_PCToLineNumber(cx, script, pc);
1877     for (line1 = 0; line1 < line2 - 1; line1++)
1878     fgets(linebuf, LINE_BUF_LEN, file);
1879    
1880     bupline = 0;
1881     while (pc < end) {
1882     line2 = JS_PCToLineNumber(cx, script, pc);
1883    
1884     if (line2 < line1) {
1885     if (bupline != line2) {
1886     bupline = line2;
1887     fprintf(gOutFile, "%s %3u: BACKUP\n", sep, line2);
1888     }
1889     } else {
1890     if (bupline && line1 == line2)
1891     fprintf(gOutFile, "%s %3u: RESTORE\n", sep, line2);
1892     bupline = 0;
1893     while (line1 < line2) {
1894     if (!fgets(linebuf, LINE_BUF_LEN, file)) {
1895     JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
1896     JSSMSG_UNEXPECTED_EOF,
1897     script->filename);
1898     ok = JS_FALSE;
1899     goto bail;
1900     }
1901     line1++;
1902     fprintf(gOutFile, "%s %3u: %s", sep, line1, linebuf);
1903     }
1904     }
1905    
1906     len = js_Disassemble1(cx, script, pc,
1907     pc - script->code,
1908     JS_TRUE, stdout);
1909     if (!len) {
1910     ok = JS_FALSE;
1911     goto bail;
1912     }
1913     pc += len;
1914     }
1915    
1916     bail:
1917     fclose(file);
1918     }
1919     return ok;
1920     #undef LINE_BUF_LEN
1921     }
1922    
1923     static JSBool
1924     Tracing(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1925     {
1926     FILE *file;
1927    
1928     if (argc == 0) {
1929     *rval = BOOLEAN_TO_JSVAL(cx->tracefp != 0);
1930     return JS_TRUE;
1931     }
1932    
1933     switch (JS_TypeOfValue(cx, argv[0])) {
1934     case JSTYPE_NUMBER:
1935     case JSTYPE_BOOLEAN: {
1936     JSBool bval;
1937     JS_ValueToBoolean(cx, argv[0], &bval);
1938     file = bval ? stderr : NULL;
1939     break;
1940     }
1941     case JSTYPE_STRING: {
1942     char *name = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
1943     file = fopen(name, "w");
1944     if (!file) {
1945     JS_ReportError(cx, "tracing: couldn't open output file %s: %s",
1946     name, strerror(errno));
1947     return JS_FALSE;
1948     }
1949     break;
1950     }
1951     default:
1952     goto bad_argument;
1953     }
1954     if (cx->tracefp && cx->tracefp != stderr)
1955     fclose((FILE *)cx->tracefp);
1956     cx->tracefp = file;
1957     cx->tracePrevPc = NULL;
1958     return JS_TRUE;
1959    
1960     bad_argument:
1961     JSString *str = JS_ValueToString(cx, argv[0]);
1962     if (!str)
1963     return JS_FALSE;
1964     JS_ReportError(cx, "tracing: illegal argument %s",
1965     JS_GetStringBytes(str));
1966     return JS_FALSE;
1967     }
1968    
1969     static void
1970     DumpScope(JSContext *cx, JSObject *obj, FILE *fp)
1971     {
1972     uintN i;
1973     JSScopeProperty *sprop;
1974     jsval v;
1975     JSString *str;
1976    
1977     i = 0;
1978     sprop = NULL;
1979     while (JS_PropertyIterator(obj, &sprop)) {
1980     fprintf(fp, "%3u %p ", i, (void *)sprop);
1981    
1982     v = ID_TO_VALUE(sprop->id);
1983     if (JSID_IS_INT(sprop->id)) {
1984     fprintf(fp, "[%ld]", (long)JSVAL_TO_INT(v));
1985     } else {
1986     if (JSID_IS_ATOM(sprop->id)) {
1987     str = JSVAL_TO_STRING(v);
1988     } else {
1989     JS_ASSERT(JSID_IS_OBJECT(sprop->id));
1990     str = js_ValueToString(cx, v);
1991     fputs("object ", fp);
1992     }
1993     if (!str)
1994     fputs("<error>", fp);
1995     else
1996     js_FileEscapedString(fp, str, '"');
1997     }
1998     #define DUMP_ATTR(name) if (sprop->attrs & JSPROP_##name) fputs(" " #name, fp)
1999     DUMP_ATTR(ENUMERATE);
2000     DUMP_ATTR(READONLY);
2001     DUMP_ATTR(PERMANENT);
2002     DUMP_ATTR(GETTER);
2003     DUMP_ATTR(SETTER);
2004     #undef DUMP_ATTR
2005    
2006     fprintf(fp, " slot %lu flags %x shortid %d\n",
2007     (unsigned long)sprop->slot, sprop->flags, sprop->shortid);
2008     }
2009     }
2010    
2011     static JSBool
2012     DumpStats(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
2013     {
2014     uintN i;
2015     JSString *str;
2016     const char *bytes;
2017     jsid id;
2018     JSObject *obj2;
2019     JSProperty *prop;
2020     jsval value;
2021    
2022     for (i = 0; i < argc; i++) {
2023     str = JS_ValueToString(cx, argv[i]);
2024     if (!str)
2025     return JS_FALSE;
2026     argv[i] = STRING_TO_JSVAL(str);
2027     bytes = JS_GetStringBytes(str);
2028     if (strcmp(bytes, "arena") == 0) {
2029     #ifdef JS_ARENAMETER
2030     JS_DumpArenaStats(stdout);
2031     #endif
2032     } else if (strcmp(bytes, "atom") == 0) {
2033     js_DumpAtoms(cx, gOutFile);
2034     } else if (strcmp(bytes, "global") == 0) {
2035     DumpScope(cx, cx->globalObject, stdout);
2036     } else {
2037     if (!JS_ValueToId(cx, STRING_TO_JSVAL(str), &id))
2038     return JS_FALSE;
2039     if (!js_FindProperty(cx, id, &obj, &obj2, &prop))
2040     return JS_FALSE;
2041     if (prop) {
2042     obj2->dropProperty(cx, prop);
2043     if (!obj->getProperty(cx, id, &value))
2044     return JS_FALSE;
2045     }
2046     if (!prop || !JSVAL_IS_OBJECT(value)) {
2047     fprintf(gErrFile, "js: invalid stats argument %s\n",
2048     bytes);
2049     continue;
2050     }
2051     obj = JSVAL_TO_OBJECT(value);
2052     if (obj)
2053     DumpScope(cx, obj, stdout);
2054     }
2055     }
2056     return JS_TRUE;
2057     }
2058    
2059     static JSBool
2060     DumpHeap(JSContext *cx, uintN argc, jsval *vp)
2061     {
2062     char *fileName;
2063     jsval v;
2064     void* startThing;
2065     uint32 startTraceKind;
2066     const char *badTraceArg;
2067     void *thingToFind;
2068     size_t maxDepth;
2069     void *thingToIgnore;
2070     FILE *dumpFile;
2071     JSBool ok;
2072    
2073     fileName = NULL;
2074     if (argc > 0) {
2075     v = JS_ARGV(cx, vp)[0];
2076     if (v != JSVAL_NULL) {
2077     JSString *str;
2078    
2079     str = JS_ValueToString(cx, v);
2080     if (!str)
2081     return JS_FALSE;
2082     JS_ARGV(cx, vp)[0] = STRING_TO_JSVAL(str);
2083     fileName = JS_GetStringBytes(str);
2084     }
2085     }
2086    
2087     startThing = NULL;
2088     startTraceKind = 0;
2089     if (argc > 1) {
2090     v = JS_ARGV(cx, vp)[1];
2091     if (JSVAL_IS_TRACEABLE(v)) {
2092     startThing = JSVAL_TO_TRACEABLE(v);
2093     startTraceKind = JSVAL_TRACE_KIND(v);
2094     } else if (v != JSVAL_NULL) {
2095     badTraceArg = "start";
2096     goto not_traceable_arg;
2097     }
2098     }
2099    
2100     thingToFind = NULL;
2101     if (argc > 2) {
2102     v = JS_ARGV(cx, vp)[2];
2103     if (JSVAL_IS_TRACEABLE(v)) {
2104     thingToFind = JSVAL_TO_TRACEABLE(v);
2105     } else if (v != JSVAL_NULL) {
2106     badTraceArg = "toFind";
2107     goto not_traceable_arg;
2108     }
2109     }
2110    
2111     maxDepth = (size_t)-1;
2112     if (argc > 3) {
2113     v = JS_ARGV(cx, vp)[3];
2114     if (v != JSVAL_NULL) {
2115     uint32 depth;
2116    
2117     if (!JS_ValueToECMAUint32(cx, v, &depth))
2118     return JS_FALSE;
2119     maxDepth = depth;
2120     }
2121     }
2122    
2123     thingToIgnore = NULL;
2124     if (argc > 4) {
2125     v = JS_ARGV(cx, vp)[4];
2126     if (JSVAL_IS_TRACEABLE(v)) {
2127     thingToIgnore = JSVAL_TO_TRACEABLE(v);
2128     } else if (v != JSVAL_NULL) {
2129     badTraceArg = "toIgnore";
2130     goto not_traceable_arg;
2131     }
2132     }
2133    
2134     if (!fileName) {
2135     dumpFile = stdout;
2136     } else {
2137     dumpFile = fopen(fileName, "w");
2138     if (!dumpFile) {
2139     JS_ReportError(cx, "can't open %s: %s", fileName, strerror(errno));
2140     return JS_FALSE;
2141     }
2142     }
2143    
2144     ok = JS_DumpHeap(cx, dumpFile, startThing, startTraceKind, thingToFind,
2145     maxDepth, thingToIgnore);
2146     if (dumpFile != stdout)
2147     fclose(dumpFile);
2148     return ok;
2149    
2150     not_traceable_arg:
2151     JS_ReportError(cx, "argument '%s' is not null or a heap-allocated thing",
2152     badTraceArg);
2153     return JS_FALSE;
2154     }
2155    
2156     #endif /* DEBUG */
2157    
2158     #ifdef TEST_CVTARGS
2159     #include <ctype.h>
2160    
2161     static const char *
2162     EscapeWideString(jschar *w)
2163     {
2164     static char enuf[80];
2165     static char hex[] = "0123456789abcdef";
2166     jschar u;
2167     unsigned char b, c;
2168     int i, j;
2169    
2170     if (!w)
2171     return "";
2172     for (i = j = 0; i < sizeof enuf - 1; i++, j++) {
2173     u = w[j];
2174     if (u == 0)
2175     break;
2176     b = (unsigned char)(u >> 8);
2177     c = (unsigned char)(u);
2178     if (b) {
2179     if (i >= sizeof enuf - 6)
2180     break;
2181     enuf[i++] = '\\';
2182     enuf[i++] = 'u';
2183     enuf[i++] = hex[b >> 4];
2184     enuf[i++] = hex[b & 15];
2185     enuf[i++] = hex[c >> 4];
2186     enuf[i] = hex[c & 15];
2187     } else if (!isprint(c)) {
2188     if (i >= sizeof enuf - 4)
2189     break;
2190     enuf[i++] = '\\';
2191     enuf[i++] = 'x';
2192     enuf[i++] = hex[c >> 4];
2193     enuf[i] = hex[c & 15];
2194     } else {
2195     enuf[i] = (char)c;
2196     }
2197     }
2198     enuf[i] = 0;
2199     return enuf;
2200     }
2201    
2202     #include <stdarg.h>
2203    
2204     static JSBool
2205     ZZ_formatter(JSContext *cx, const char *format, JSBool fromJS, jsval **vpp,
2206     va_list *app)
2207     {
2208     jsval *vp;
2209     va_list ap;
2210     jsdouble re, im;
2211    
2212     printf("entering ZZ_formatter");
2213     vp = *vpp;
2214     ap = *app;
2215     if (fromJS) {
2216     if (!JS_ValueToNumber(cx, vp[0], &re))
2217     return JS_FALSE;
2218     if (!JS_ValueToNumber(cx, vp[1], &im))
2219     return JS_FALSE;
2220     *va_arg(ap, jsdouble *) = re;
2221     *va_arg(ap, jsdouble *) = im;
2222     } else {
2223     re = va_arg(ap, jsdouble);
2224     im = va_arg(ap, jsdouble);
2225     if (!JS_NewNumberValue(cx, re, &vp[0]))
2226     return JS_FALSE;
2227     if (!JS_NewNumberValue(cx, im, &vp[1]))
2228     return JS_FALSE;
2229     }
2230     *vpp = vp + 2;
2231     *app = ap;
2232     printf("leaving ZZ_formatter");
2233     return JS_TRUE;
2234     }
2235    
2236     static JSBool
2237     ConvertArgs(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
2238     {
2239     JSBool b = JS_FALSE;
2240     jschar c = 0;
2241     int32 i = 0, j = 0;
2242     uint32 u = 0;
2243     jsdouble d = 0, I = 0, re = 0, im = 0;
2244     char *s = NULL;
2245     JSString *str = NULL;
2246     jschar *w = NULL;
2247     JSObject *obj2 = NULL;
2248     JSFunction *fun = NULL;
2249     jsval v = JSVAL_VOID;
2250     JSBool ok;
2251    
2252     if (!JS_AddArgumentFormatter(cx, "ZZ", ZZ_formatter))
2253     return JS_FALSE;
2254     ok = JS_ConvertArguments(cx, argc, argv, "b/ciujdIsSWofvZZ*",
2255     &b, &c, &i, &u, &j, &d, &I, &s, &str, &w, &obj2,
2256     &fun, &v, &re, &im);
2257     JS_RemoveArgumentFormatter(cx, "ZZ");
2258     if (!ok)
2259     return JS_FALSE;
2260     fprintf(gOutFile,
2261     "b %u, c %x (%c), i %ld, u %lu, j %ld\n",
2262     b, c, (char)c, i, u, j);
2263     ToString obj2string(cx, obj2);
2264     ToString valueString(cx, v);
2265     JSString *tmpstr = JS_DecompileFunction(cx, fun, 4);
2266     const char *func;
2267     if (tmpstr) {
2268     func = JS_GetStringBytes(tmpstr);
2269     } else {
2270     if (JS_IsExceptionPending(cx)) {
2271     if (!JS_ReportPendingException(cx))
2272     JS_ClearPendingException(cx);
2273     }
2274     func = "error decompiling fun";
2275     }
2276     fprintf(gOutFile,
2277     "d %g, I %g, s %s, S %s, W %s, obj %s, fun %s\n"
2278     "v %s, re %g, im %g\n",
2279     d, I, s, str ? JS_GetStringBytes(str) : "", EscapeWideString(w),
2280     obj2string.getBytes(),
2281     fun ? func : "",
2282     valueString.getBytes(), re, im);
2283     return JS_TRUE;
2284     }
2285     #endif
2286    
2287     static JSBool
2288     BuildDate(JSContext *cx, uintN argc, jsval *vp)
2289     {
2290     char version[20] = "\n";
2291     #if JS_VERSION < 150
2292     sprintf(version, " for version %d\n", JS_VERSION);
2293     #endif
2294     fprintf(gOutFile, "built on %s at %s%s", __DATE__, __TIME__, version);
2295     *vp = JSVAL_VOID;
2296     return JS_TRUE;
2297     }
2298    
2299     static JSBool
2300     Clear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
2301     {
2302     if (argc != 0 && !JS_ValueToObject(cx, argv[0], &obj))
2303     return JS_FALSE;
2304     JS_ClearScope(cx, obj);
2305     return JS_TRUE;
2306     }
2307    
2308     static JSBool
2309     Intern(JSContext *cx, uintN argc, jsval *vp)
2310     {
2311     JSString *str;
2312    
2313     str = JS_ValueToString(cx, argc == 0 ? JSVAL_VOID : vp[2]);
2314     if (!str)
2315     return JS_FALSE;
2316     if (!JS_InternUCStringN(cx, JS_GetStringChars(str),
2317     JS_GetStringLength(str))) {
2318     return JS_FALSE;
2319     }
2320     *vp = JSVAL_VOID;
2321     return JS_TRUE;
2322     }
2323    
2324     static JSBool
2325     Clone(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
2326     {
2327     JSObject *funobj, *parent, *clone;
2328    
2329     if (VALUE_IS_FUNCTION(cx, argv[0])) {
2330     funobj = JSVAL_TO_OBJECT(argv[0]);
2331     } else {
2332     JSFunction *fun = JS_ValueToFunction(cx, argv[0]);
2333     if (!fun)
2334     return JS_FALSE;
2335     funobj = JS_GetFunctionObject(fun);
2336     }
2337     if (argc > 1) {
2338     if (!JS_ValueToObject(cx, argv[1], &parent))
2339     return JS_FALSE;
2340     } else {
2341     parent = JS_GetParent(cx, funobj);
2342     }
2343     clone = JS_CloneFunctionObject(cx, funobj, parent);
2344     if (!clone)
2345     return JS_FALSE;
2346     *rval = OBJECT_TO_JSVAL(clone);
2347     return JS_TRUE;
2348     }
2349    
2350     static JSBool
2351     Seal(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
2352     {
2353     JSObject *target;
2354     JSBool deep = JS_FALSE;
2355    
2356     if (!JS_ConvertArguments(cx, argc, argv, "o/b", &target, &deep))
2357     return JS_FALSE;
2358     if (!target)
2359     return JS_TRUE;
2360     return JS_SealObject(cx, target, deep);
2361     }
2362    
2363     static JSBool
2364     GetPDA(JSContext *cx, uintN argc, jsval *vp)
2365     {
2366     JSObject *vobj, *aobj, *pdobj;
2367     JSBool ok;
2368     JSPropertyDescArray pda;
2369     JSPropertyDesc *pd;
2370     uint32 i;
2371     jsval v;
2372    
2373     if (!JS_ValueToObject(cx, argc == 0 ? JSVAL_VOID : vp[2], &vobj))
2374     return JS_FALSE;
2375     if (!vobj) {
2376     *vp = JSVAL_VOID;
2377     return JS_TRUE;
2378     }
2379    
2380     aobj = JS_NewArrayObject(cx, 0, NULL);
2381     if (!aobj)
2382     return JS_FALSE;
2383     *vp = OBJECT_TO_JSVAL(aobj);
2384    
2385     ok = JS_GetPropertyDescArray(cx, vobj, &pda);
2386     if (!ok)
2387     return JS_FALSE;
2388     pd = pda.array;
2389     for (i = 0; i < pda.length; i++, pd++) {
2390     pdobj = JS_NewObject(cx, NULL, NULL, NULL);
2391     if (!pdobj) {
2392     ok = JS_FALSE;
2393     break;
2394     }
2395    
2396     /* Protect pdobj from GC by setting it as an element of aobj now */
2397     v = OBJECT_TO_JSVAL(pdobj);
2398     ok = JS_SetElement(cx, aobj, i, &v);
2399     if (!ok)
2400     break;
2401    
2402     ok = JS_SetProperty(cx, pdobj, "id", &pd->id) &&
2403     JS_SetProperty(cx, pdobj, "value", &pd->value) &&
2404     (v = INT_TO_JSVAL(pd->flags),
2405     JS_SetProperty(cx, pdobj, "flags", &v)) &&
2406     (v = INT_TO_JSVAL(pd->slot),
2407     JS_SetProperty(cx, pdobj, "slot", &v)) &&
2408     JS_SetProperty(cx, pdobj, "alias", &pd->alias);
2409     if (!ok)
2410     break;
2411     }
2412     JS_PutPropertyDescArray(cx, &pda);
2413     return ok;
2414     }
2415    
2416     static JSBool
2417     GetSLX(JSContext *cx, uintN argc, jsval *vp)
2418     {
2419     JSScript *script;
2420    
2421     script = ValueToScript(cx, argc == 0 ? JSVAL_VOID : vp[2]);
2422     if (!script)
2423     return JS_FALSE;
2424     *vp = INT_TO_JSVAL(js_GetScriptLineExtent(script));
2425     return JS_TRUE;
2426     }
2427    
2428     static JSBool
2429     ToInt32(JSContext *cx, uintN argc, jsval *vp)
2430     {
2431     int32 i;
2432    
2433     if (!JS_ValueToInt32(cx, argc == 0 ? JSVAL_VOID : vp[2], &i))
2434     return JS_FALSE;
2435     return JS_NewNumberValue(cx, i, vp);
2436     }
2437    
2438     static JSBool
2439     StringsAreUTF8(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
2440     jsval *rval)
2441     {
2442     *rval = JS_CStringsAreUTF8() ? JSVAL_TRUE : JSVAL_FALSE;
2443     return JS_TRUE;
2444     }
2445    
2446     static JSBool
2447     StackQuota(JSContext *cx, uintN argc, jsval *vp)
2448     {
2449     uint32 n;
2450    
2451     if (argc == 0)
2452     return JS_NewNumberValue(cx, (double) gScriptStackQuota, vp);
2453     if (!JS_ValueToECMAUint32(cx, JS_ARGV(cx, vp)[0], &n))
2454     return JS_FALSE;
2455     gScriptStackQuota = n;
2456     JS_SetScriptStackQuota(cx, gScriptStackQuota);
2457     JS_SET_RVAL(cx, vp, JSVAL_VOID);
2458     return JS_TRUE;
2459     }
2460    
2461     static const char* badUTF8 = "...\xC0...";
2462     static const char* bigUTF8 = "...\xFB\xBF\xBF\xBF\xBF...";
2463     static const jschar badSurrogate[] = { 'A', 'B', 'C', 0xDEEE, 'D', 'E', 0 };
2464    
2465     static JSBool
2466     TestUTF8(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
2467     {
2468     int32 mode = 1;
2469     jschar chars[20];
2470     size_t charsLength = 5;
2471     char bytes[20];
2472     size_t bytesLength = 20;
2473     if (argc && !JS_ValueToInt32(cx, *argv, &mode))
2474     return JS_FALSE;
2475    
2476     /* The following throw errors if compiled with UTF-8. */
2477     switch (mode) {
2478     /* mode 1: malformed UTF-8 string. */
2479     case 1:
2480     JS_NewStringCopyZ(cx, badUTF8);
2481     break;
2482     /* mode 2: big UTF-8 character. */
2483     case 2:
2484     JS_NewStringCopyZ(cx, bigUTF8);
2485     break;
2486     /* mode 3: bad surrogate character. */
2487     case 3:
2488     JS_EncodeCharacters(cx, badSurrogate, 6, bytes, &bytesLength);
2489     break;
2490     /* mode 4: use a too small buffer. */
2491     case 4:
2492     JS_DecodeBytes(cx, "1234567890", 10, chars, &charsLength);
2493     break;
2494     default:
2495     JS_ReportError(cx, "invalid mode parameter");
2496     return JS_FALSE;
2497     }
2498     return !JS_IsExceptionPending (cx);
2499     }
2500    
2501     static JSBool
2502     ThrowError(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
2503     {
2504     JS_ReportError(cx, "This is an error");
2505     return JS_FALSE;
2506     }
2507    
2508     #define LAZY_STANDARD_CLASSES
2509    
2510     /* A class for easily testing the inner/outer object callbacks. */
2511     typedef struct ComplexObject {
2512     JSBool isInner;
2513     JSBool frozen;
2514     JSObject *inner;
2515     JSObject *outer;
2516     } ComplexObject;
2517    
2518     static JSObject *
2519     split_create_outer(JSContext *cx);
2520    
2521     static JSObject *
2522     split_create_inner(JSContext *cx, JSObject *outer);
2523    
2524     static ComplexObject *
2525     split_get_private(JSContext *cx, JSObject *obj);
2526    
2527     static JSBool
2528     split_addProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
2529     {
2530     ComplexObject *cpx;
2531     jsid asId;
2532    
2533     cpx = split_get_private(cx, obj);
2534     if (!cpx)
2535     return JS_TRUE;
2536     if (!cpx->isInner && cpx->inner) {
2537     /* Make sure to define this property on the inner object. */
2538     if (!JS_ValueToId(cx, *vp, &asId))
2539     return JS_FALSE;
2540     return JS_DefinePropertyById(cx, cpx->inner, asId, *vp, NULL, NULL, JSPROP_ENUMERATE);
2541     }
2542     return JS_TRUE;
2543     }
2544    
2545     static JSBool
2546     split_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
2547     {
2548     ComplexObject *cpx;
2549    
2550     cpx = split_get_private(cx, obj);
2551     if (!cpx)
2552     return JS_TRUE;
2553    
2554     if (JSVAL_IS_STRING(id) &&
2555     !strcmp(JS_GetStringBytes(JSVAL_TO_STRING(id)), "isInner")) {
2556     *vp = BOOLEAN_TO_JSVAL(cpx->isInner);
2557     return JS_TRUE;
2558     }
2559    
2560     if (!cpx->isInner && cpx->inner) {
2561     if (JSVAL_IS_STRING(id)) {
2562     JSString *str;
2563    
2564     str = JSVAL_TO_STRING(id);
2565     return JS_GetUCProperty(cx, cpx->inner