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