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

Annotation of /trunk/js/js.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 460 - (hide annotations)
Sat Sep 26 23:15:22 2009 UTC (12 years, 9 months ago) by siliconforks
File size: 130955 byte(s)
Upgrade to SpiderMonkey from Firefox 3.5.3.

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