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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 585 - (hide annotations)
Sun Sep 12 15:13:23 2010 UTC (10 years ago) by siliconforks
File size: 135245 byte(s)
Update to SpiderMonkey from Firefox 3.6.9.

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