Code

fix 1243587 and misc fixes
[inkscape.git] / src / dom / js / js.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  * vim: set ts=8 sw=4 et tw=80:
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 ***** */
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 "jstypes.h"
50 #include "jsarena.h"
51 #include "jsutil.h"
52 #include "jsprf.h"
53 #include "jsapi.h"
54 #include "jsatom.h"
55 #include "jscntxt.h"
56 #include "jsdbgapi.h"
57 #include "jsemit.h"
58 #include "jsfun.h"
59 #include "jsgc.h"
60 #include "jslock.h"
61 #include "jsobj.h"
62 #include "jsparse.h"
63 #include "jsscope.h"
64 #include "jsscript.h"
66 #ifdef PERLCONNECT
67 #include "perlconnect/jsperl.h"
68 #endif
70 #ifdef LIVECONNECT
71 #include "jsjava.h"
72 #endif
74 #ifdef JSDEBUGGER
75 #include "jsdebug.h"
76 #ifdef JSDEBUGGER_JAVA_UI
77 #include "jsdjava.h"
78 #endif /* JSDEBUGGER_JAVA_UI */
79 #ifdef JSDEBUGGER_C_UI
80 #include "jsdb.h"
81 #endif /* JSDEBUGGER_C_UI */
82 #endif /* JSDEBUGGER */
84 #ifdef XP_UNIX
85 #include <unistd.h>
86 #include <sys/types.h>
87 #include <sys/wait.h>
88 #endif
90 #if defined(XP_WIN) || defined(XP_OS2)
91 #include <io.h>     /* for isatty() */
92 #endif
94 typedef enum JSShellExitCode {
95     EXITCODE_RUNTIME_ERROR      = 3,
96     EXITCODE_FILE_NOT_FOUND     = 4,
97     EXITCODE_OUT_OF_MEMORY      = 5
98 } JSShellExitCode;
100 size_t gStackChunkSize = 8192;
101 static size_t gMaxStackSize = 0;
102 static jsuword gStackBase;
103 int gExitCode = 0;
104 JSBool gQuitting = JS_FALSE;
105 FILE *gErrFile = NULL;
106 FILE *gOutFile = NULL;
108 #ifdef JSDEBUGGER
109 static JSDContext *_jsdc;
110 #ifdef JSDEBUGGER_JAVA_UI
111 static JSDJContext *_jsdjc;
112 #endif /* JSDEBUGGER_JAVA_UI */
113 #endif /* JSDEBUGGER */
115 static JSBool reportWarnings = JS_TRUE;
116 static JSBool compileOnly = JS_FALSE;
118 typedef enum JSShellErrNum {
119 #define MSG_DEF(name, number, count, exception, format) \
120     name = number,
121 #include "jsshell.msg"
122 #undef MSG_DEF
123     JSShellErr_Limit
124 #undef MSGDEF
125 } JSShellErrNum;
127 static const JSErrorFormatString *
128 my_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber);
130 #ifdef EDITLINE
131 extern char     *readline(const char *prompt);
132 extern void     add_history(char *line);
133 #endif
135 static JSBool
136 GetLine(JSContext *cx, char *bufp, FILE *file, const char *prompt) {
137 #ifdef EDITLINE
138     /*
139      * Use readline only if file is stdin, because there's no way to specify
140      * another handle.  Are other filehandles interactive?
141      */
142     if (file == stdin) {
143         char *linep = readline(prompt);
144         if (!linep)
145             return JS_FALSE;
146         if (linep[0] != '\0')
147             add_history(linep);
148         strcpy(bufp, linep);
149         JS_free(cx, linep);
150         bufp += strlen(bufp);
151         *bufp++ = '\n';
152         *bufp = '\0';
153     } else
154 #endif
155     {
156         char line[256];
157         fprintf(gOutFile, prompt);
158         fflush(gOutFile);
159         if (!fgets(line, sizeof line, file))
160             return JS_FALSE;
161         strcpy(bufp, line);
162     }
163     return JS_TRUE;
166 static void
167 Process(JSContext *cx, JSObject *obj, char *filename)
169     JSBool ok, hitEOF;
170     JSScript *script;
171     jsval result;
172     JSString *str;
173     char buffer[4096];
174     char *bufp;
175     int lineno;
176     int startline;
177     FILE *file;
178     jsuword stackLimit;
180     if (!filename || strcmp(filename, "-") == 0) {
181         file = stdin;
182     } else {
183         file = fopen(filename, "r");
184         if (!file) {
185             JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
186                                  JSSMSG_CANT_OPEN, filename, strerror(errno));
187             gExitCode = EXITCODE_FILE_NOT_FOUND;
188             return;
189         }
190     }
192     if (gMaxStackSize == 0) {
193         /*
194          * Disable checking for stack overflow if limit is zero.
195          */
196         stackLimit = 0;
197     } else {
198 #if JS_STACK_GROWTH_DIRECTION > 0
199         stackLimit = gStackBase + gMaxStackSize;
200 #else
201         stackLimit = gStackBase - gMaxStackSize;
202 #endif
203     }
204     JS_SetThreadStackLimit(cx, stackLimit);
206     if (!isatty(fileno(file))) {
207         /*
208          * It's not interactive - just execute it.
209          *
210          * Support the UNIX #! shell hack; gobble the first line if it starts
211          * with '#'.  TODO - this isn't quite compatible with sharp variables,
212          * as a legal js program (using sharp variables) might start with '#'.
213          * But that would require multi-character lookahead.
214          */
215         int ch = fgetc(file);
216         if (ch == '#') {
217             while((ch = fgetc(file)) != EOF) {
218                 if (ch == '\n' || ch == '\r')
219                     break;
220             }
221         }
222         ungetc(ch, file);
223         script = JS_CompileFileHandle(cx, obj, filename, file);
224         if (script) {
225             if (!compileOnly)
226                 (void)JS_ExecuteScript(cx, obj, script, &result);
227             JS_DestroyScript(cx, script);
228         }
229         return;
230     }
232     /* It's an interactive filehandle; drop into read-eval-print loop. */
233     lineno = 1;
234     hitEOF = JS_FALSE;
235     do {
236         bufp = buffer;
237         *bufp = '\0';
239         /*
240          * Accumulate lines until we get a 'compilable unit' - one that either
241          * generates an error (before running out of source) or that compiles
242          * cleanly.  This should be whenever we get a complete statement that
243          * coincides with the end of a line.
244          */
245         startline = lineno;
246         do {
247             if (!GetLine(cx, bufp, file, startline == lineno ? "js> " : "")) {
248                 hitEOF = JS_TRUE;
249                 break;
250             }
251             bufp += strlen(bufp);
252             lineno++;
253         } while (!JS_BufferIsCompilableUnit(cx, obj, buffer, strlen(buffer)));
255         /* Clear any pending exception from previous failed compiles.  */
256         JS_ClearPendingException(cx);
257         script = JS_CompileScript(cx, obj, buffer, strlen(buffer), "typein",
258                                   startline);
259         if (script) {
260             if (!compileOnly) {
261                 ok = JS_ExecuteScript(cx, obj, script, &result);
262                 if (ok && result != JSVAL_VOID) {
263                     str = JS_ValueToString(cx, result);
264                     if (str)
265                         fprintf(gOutFile, "%s\n", JS_GetStringBytes(str));
266                     else
267                         ok = JS_FALSE;
268                 }
269             }
270             JS_DestroyScript(cx, script);
271         }
272     } while (!hitEOF && !gQuitting);
273     fprintf(gOutFile, "\n");
274     return;
277 static int
278 usage(void)
280     fprintf(gErrFile, "%s\n", JS_GetImplementationVersion());
281     fprintf(gErrFile, "usage: js [-PswWxC] [-b branchlimit] [-c stackchunksize] [-v version] [-f scriptfile] [-e script] [-S maxstacksize] [scriptfile] [scriptarg...]\n");
282     return 2;
285 static uint32 gBranchCount;
286 static uint32 gBranchLimit;
288 static JSBool
289 my_BranchCallback(JSContext *cx, JSScript *script)
291     if (++gBranchCount == gBranchLimit) {
292         if (script) {
293             if (script->filename)
294                 fprintf(gErrFile, "%s:", script->filename);
295             fprintf(gErrFile, "%u: script branch callback (%u callbacks)\n",
296                     script->lineno, gBranchLimit);
297         } else {
298             fprintf(gErrFile, "native branch callback (%u callbacks)\n",
299                     gBranchLimit);
300         }
301         gBranchCount = 0;
302         return JS_FALSE;
303     }
304     if ((gBranchCount & 0x3fff) == 1)
305         JS_MaybeGC(cx);
306     return JS_TRUE;
309 extern JSClass global_class;
311 static int
312 ProcessArgs(JSContext *cx, JSObject *obj, char **argv, int argc)
314     int i, j, length;
315     JSObject *argsObj;
316     char *filename = NULL;
317     JSBool isInteractive = JS_TRUE;
319     /*
320      * Scan past all optional arguments so we can create the arguments object
321      * before processing any -f options, which must interleave properly with
322      * -v and -w options.  This requires two passes, and without getopt, we'll
323      * have to keep the option logic here and in the second for loop in sync.
324      */
325     for (i = 0; i < argc; i++) {
326         if (argv[i][0] != '-' || argv[i][1] == '\0') {
327             ++i;
328             break;
329         }
330         switch (argv[i][1]) {
331           case 'b':
332           case 'c':
333           case 'f':
334           case 'e':
335           case 'v':
336           case 'S':
337             ++i;
338             break;
339           default:;
340         }
341     }
343     /*
344      * Create arguments early and define it to root it, so it's safe from any
345      * GC calls nested below, and so it is available to -f <file> arguments.
346      */
347     argsObj = JS_NewArrayObject(cx, 0, NULL);
348     if (!argsObj)
349         return 1;
350     if (!JS_DefineProperty(cx, obj, "arguments", OBJECT_TO_JSVAL(argsObj),
351                            NULL, NULL, 0)) {
352         return 1;
353     }
355     length = argc - i;
356     for (j = 0; j < length; j++) {
357         JSString *str = JS_NewStringCopyZ(cx, argv[i++]);
358         if (!str)
359             return 1;
360         if (!JS_DefineElement(cx, argsObj, j, STRING_TO_JSVAL(str),
361                               NULL, NULL, JSPROP_ENUMERATE)) {
362             return 1;
363         }
364     }
366     for (i = 0; i < argc; i++) {
367         if (argv[i][0] != '-' || argv[i][1] == '\0') {
368             filename = argv[i++];
369             isInteractive = JS_FALSE;
370             break;
371         }
373         switch (argv[i][1]) {
374         case 'v':
375             if (++i == argc) {
376                 return usage();
377             }
378             JS_SetVersion(cx, (JSVersion) atoi(argv[i]));
379             break;
381         case 'w':
382             reportWarnings = JS_TRUE;
383             break;
385         case 'W':
386             reportWarnings = JS_FALSE;
387             break;
389         case 's':
390             JS_ToggleOptions(cx, JSOPTION_STRICT);
391             break;
393         case 'x':
394             JS_ToggleOptions(cx, JSOPTION_XML);
395             break;
397         case 'P':
398             if (JS_GET_CLASS(cx, JS_GetPrototype(cx, obj)) != &global_class) {
399                 JSObject *gobj;
401                 if (!JS_SealObject(cx, obj, JS_TRUE))
402                     return JS_FALSE;
403                 gobj = JS_NewObject(cx, &global_class, NULL, NULL);
404                 if (!gobj)
405                     return JS_FALSE;
406                 if (!JS_SetPrototype(cx, gobj, obj))
407                     return JS_FALSE;
408                 JS_SetParent(cx, gobj, NULL);
409                 JS_SetGlobalObject(cx, gobj);
410                 obj = gobj;
411             }
412             break;
414         case 'b':
415             gBranchLimit = atoi(argv[++i]);
416             JS_SetBranchCallback(cx, my_BranchCallback);
417             JS_ToggleOptions(cx, JSOPTION_NATIVE_BRANCH_CALLBACK);
418             break;
420         case 'c':
421             /* set stack chunk size */
422             gStackChunkSize = atoi(argv[++i]);
423             break;
425         case 'f':
426             if (++i == argc) {
427                 return usage();
428             }
429             Process(cx, obj, argv[i]);
430             /*
431              * XXX: js -f foo.js should interpret foo.js and then
432              * drop into interactive mode, but that breaks the test
433              * harness. Just execute foo.js for now.
434              */
435             isInteractive = JS_FALSE;
436             break;
438         case 'e':
439         {
440             jsval rval;
442             if (++i == argc) {
443                 return usage();
444             }
446             /* Pass a filename of -e to imitate PERL */
447             JS_EvaluateScript(cx, obj, argv[i], strlen(argv[i]),
448                               "-e", 1, &rval);
450             isInteractive = JS_FALSE;
451             break;
453         }
454         case 'C':
455             compileOnly = JS_TRUE;
456             isInteractive = JS_FALSE;
457             break;
459         case 'S':
460             if (++i == argc) {
461                 return usage();
462             }
463             /* Set maximum stack size. */
464             gMaxStackSize = atoi(argv[i]);
465             break;
467         default:
468             return usage();
469         }
470     }
472     if (filename || isInteractive)
473         Process(cx, obj, filename);
474     return gExitCode;
478 static JSBool
479 Version(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
481     if (argc > 0 && JSVAL_IS_INT(argv[0]))
482         *rval = INT_TO_JSVAL(JS_SetVersion(cx, (JSVersion) JSVAL_TO_INT(argv[0])));
483     else
484         *rval = INT_TO_JSVAL(JS_GetVersion(cx));
485     return JS_TRUE;
488 static struct {
489     const char  *name;
490     uint32      flag;
491 } js_options[] = {
492     {"strict",          JSOPTION_STRICT},
493     {"werror",          JSOPTION_WERROR},
494     {"atline",          JSOPTION_ATLINE},
495     {"xml",             JSOPTION_XML},
496     {0,                 0}
497 };
499 static JSBool
500 Options(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
502     uint32 optset, flag;
503     uintN i, j, found;
504     JSString *str;
505     const char *opt;
506     char *names;
508     optset = 0;
509     for (i = 0; i < argc; i++) {
510         str = JS_ValueToString(cx, argv[i]);
511         if (!str)
512             return JS_FALSE;
513         opt = JS_GetStringBytes(str);
514         for (j = 0; js_options[j].name; j++) {
515             if (strcmp(js_options[j].name, opt) == 0) {
516                 optset |= js_options[j].flag;
517                 break;
518             }
519         }
520     }
521     optset = JS_ToggleOptions(cx, optset);
523     names = NULL;
524     found = 0;
525     while (optset != 0) {
526         flag = optset;
527         optset &= optset - 1;
528         flag &= ~optset;
529         for (j = 0; js_options[j].name; j++) {
530             if (js_options[j].flag == flag) {
531                 names = JS_sprintf_append(names, "%s%s",
532                                           names ? "," : "", js_options[j].name);
533                 found++;
534                 break;
535             }
536         }
537     }
538     if (!found)
539         names = strdup("");
540     if (!names) {
541         JS_ReportOutOfMemory(cx);
542         return JS_FALSE;
543     }
545     str = JS_NewString(cx, names, strlen(names));
546     if (!str) {
547         free(names);
548         return JS_FALSE;
549     }
550     *rval = STRING_TO_JSVAL(str);
551     return JS_TRUE;
554 static void
555 my_LoadErrorReporter(JSContext *cx, const char *message, JSErrorReport *report);
557 static void
558 my_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report);
560 static JSBool
561 Load(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
563     uintN i;
564     JSString *str;
565     const char *filename;
566     JSScript *script;
567     JSBool ok;
568     jsval result;
569     JSErrorReporter older;
570     uint32 oldopts;
572     for (i = 0; i < argc; i++) {
573         str = JS_ValueToString(cx, argv[i]);
574         if (!str)
575             return JS_FALSE;
576         argv[i] = STRING_TO_JSVAL(str);
577         filename = JS_GetStringBytes(str);
578         errno = 0;
579         older = JS_SetErrorReporter(cx, my_LoadErrorReporter);
580         oldopts = JS_GetOptions(cx);
581         JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO);
582         script = JS_CompileFile(cx, obj, filename);
583         if (!script) {
584             ok = JS_FALSE;
585         } else {
586             ok = !compileOnly
587                  ? JS_ExecuteScript(cx, obj, script, &result)
588                  : JS_TRUE;
589             JS_DestroyScript(cx, script);
590         }
591         JS_SetOptions(cx, oldopts);
592         JS_SetErrorReporter(cx, older);
593         if (!ok)
594             return JS_FALSE;
595     }
597     return JS_TRUE;
600 /*
601  * function readline()
602  * Provides a hook for scripts to read a line from stdin.
603  */
604 static JSBool
605 ReadLine(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
607 #define BUFSIZE 256
608     FILE *from;
609     char *buf, *tmp;
610     size_t bufsize, buflength, gotlength;
611     JSString *str;
613     from = stdin;
614     buflength = 0;
615     bufsize = BUFSIZE;
616     buf = JS_malloc(cx, bufsize);
617     if (!buf)
618         return JS_FALSE;
620     while ((gotlength = 
621             js_fgets(buf + buflength, bufsize - buflength, from)) > 0) {
622         buflength += gotlength;
624         /* Are we done? */
625         if (buf[buflength - 1] == '\n') {
626             buf[buflength - 1] = '\0';
627             break;
628         }
630         /* Else, grow our buffer for another pass. */
631         tmp = JS_realloc(cx, buf, bufsize * 2);
632         if (!tmp) {
633             JS_free(cx, buf);
634             return JS_FALSE;
635         }
637         bufsize *= 2;
638         buf = tmp;
639     }
641     /* Treat the empty string specially. */
642     if (buflength == 0) {
643         *rval = JS_GetEmptyStringValue(cx);
644         JS_free(cx, buf);
645         return JS_TRUE;
646     }
648     /* Shrink the buffer to the real size. */
649     tmp = JS_realloc(cx, buf, buflength);
650     if (!tmp) {
651         JS_free(cx, buf);
652         return JS_FALSE;
653     }
655     buf = tmp;
657     /* 
658      * Turn buf into a JSString. Note that buflength includes the trailing null
659      * character.
660      */
661     str = JS_NewString(cx, buf, buflength - 1);
662     if (!str) {
663         JS_free(cx, buf);
664         return JS_FALSE;
665     }
667     *rval = STRING_TO_JSVAL(str);
668     return JS_TRUE;
671 static JSBool
672 Print(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
674     uintN i, n;
675     JSString *str;
677     for (i = n = 0; i < argc; i++) {
678         str = JS_ValueToString(cx, argv[i]);
679         if (!str)
680             return JS_FALSE;
681         fprintf(gOutFile, "%s%s", i ? " " : "", JS_GetStringBytes(str));
682     }
683     n++;
684     if (n)
685         fputc('\n', gOutFile);
686     return JS_TRUE;
689 static JSBool
690 Help(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
692 static JSBool
693 Quit(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
695 #ifdef LIVECONNECT
696     JSJ_SimpleShutdown();
697 #endif
699     JS_ConvertArguments(cx, argc, argv,"/ i", &gExitCode);
701     gQuitting = JS_TRUE;
702     return JS_FALSE;
705 static JSBool
706 GC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
708     JSRuntime *rt;
709     uint32 preBytes;
711     rt = cx->runtime;
712     preBytes = rt->gcBytes;
713 #ifdef GC_MARK_DEBUG
714     if (argc && JSVAL_IS_STRING(argv[0])) {
715         char *name = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
716         FILE *file = fopen(name, "w");
717         if (!file) {
718             fprintf(gErrFile, "gc: can't open %s: %s\n", strerror(errno));
719             return JS_FALSE;
720         }
721         js_DumpGCHeap = file;
722     } else {
723         js_DumpGCHeap = stdout;
724     }
725 #endif
726     JS_GC(cx);
727 #ifdef GC_MARK_DEBUG
728     if (js_DumpGCHeap != stdout)
729         fclose(js_DumpGCHeap);
730     js_DumpGCHeap = NULL;
731 #endif
732     fprintf(gOutFile, "before %lu, after %lu, break %08lx\n",
733             (unsigned long)preBytes, (unsigned long)rt->gcBytes,
734 #ifdef XP_UNIX
735             (unsigned long)sbrk(0)
736 #else
737             0
738 #endif
739             );
740 #ifdef JS_GCMETER
741     js_DumpGCStats(rt, stdout);
742 #endif
743     return JS_TRUE;
746 static JSScript *
747 ValueToScript(JSContext *cx, jsval v)
749     JSScript *script;
750     JSFunction *fun;
752     if (!JSVAL_IS_PRIMITIVE(v) &&
753         JS_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_ScriptClass) {
754         script = (JSScript *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v));
755     } else {
756         fun = JS_ValueToFunction(cx, v);
757         if (!fun)
758             return NULL;
759         script = FUN_SCRIPT(fun);
760     }
761     return script;
764 static JSBool
765 GetTrapArgs(JSContext *cx, uintN argc, jsval *argv, JSScript **scriptp,
766             int32 *ip)
768     jsval v;
769     uintN intarg;
770     JSScript *script;
772     *scriptp = cx->fp->down->script;
773     *ip = 0;
774     if (argc != 0) {
775         v = argv[0];
776         intarg = 0;
777         if (!JSVAL_IS_PRIMITIVE(v) &&
778             (JS_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_FunctionClass ||
779              JS_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_ScriptClass)) {
780             script = ValueToScript(cx, v);
781             if (!script)
782                 return JS_FALSE;
783             *scriptp = script;
784             intarg++;
785         }
786         if (argc > intarg) {
787             if (!JS_ValueToInt32(cx, argv[intarg], ip))
788                 return JS_FALSE;
789         }
790     }
791     return JS_TRUE;
794 static JSTrapStatus
795 TrapHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval,
796             void *closure)
798     JSString *str;
799     JSStackFrame *caller;
801     str = (JSString *) closure;
802     caller = JS_GetScriptedCaller(cx, NULL);
803     if (!JS_EvaluateScript(cx, caller->scopeChain,
804                            JS_GetStringBytes(str), JS_GetStringLength(str),
805                            caller->script->filename, caller->script->lineno,
806                            rval)) {
807         return JSTRAP_ERROR;
808     }
809     if (*rval != JSVAL_VOID)
810         return JSTRAP_RETURN;
811     return JSTRAP_CONTINUE;
814 static JSBool
815 Trap(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
817     JSString *str;
818     JSScript *script;
819     int32 i;
821     if (argc == 0) {
822         JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_TRAP_USAGE);
823         return JS_FALSE;
824     }
825     argc--;
826     str = JS_ValueToString(cx, argv[argc]);
827     if (!str)
828         return JS_FALSE;
829     argv[argc] = STRING_TO_JSVAL(str);
830     if (!GetTrapArgs(cx, argc, argv, &script, &i))
831         return JS_FALSE;
832     return JS_SetTrap(cx, script, script->code + i, TrapHandler, str);
835 static JSBool
836 Untrap(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
838     JSScript *script;
839     int32 i;
841     if (!GetTrapArgs(cx, argc, argv, &script, &i))
842         return JS_FALSE;
843     JS_ClearTrap(cx, script, script->code + i, NULL, NULL);
844     return JS_TRUE;
847 static JSBool
848 LineToPC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
850     JSScript *script;
851     int32 i;
852     uintN lineno;
853     jsbytecode *pc;
855     if (argc == 0) {
856         JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_LINE2PC_USAGE);
857         return JS_FALSE;
858     }
859     script = cx->fp->down->script;
860     if (!GetTrapArgs(cx, argc, argv, &script, &i))
861         return JS_FALSE;
862     lineno = (i == 0) ? script->lineno : (uintN)i;
863     pc = JS_LineNumberToPC(cx, script, lineno);
864     if (!pc)
865         return JS_FALSE;
866     *rval = INT_TO_JSVAL(PTRDIFF(pc, script->code, jsbytecode));
867     return JS_TRUE;
870 static JSBool
871 PCToLine(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
873     JSScript *script;
874     int32 i;
875     uintN lineno;
877     if (!GetTrapArgs(cx, argc, argv, &script, &i))
878         return JS_FALSE;
879     lineno = JS_PCToLineNumber(cx, script, script->code + i);
880     if (!lineno)
881         return JS_FALSE;
882     *rval = INT_TO_JSVAL(lineno);
883     return JS_TRUE;
886 #ifdef DEBUG
888 static void
889 SrcNotes(JSContext *cx, JSScript *script)
891     uintN offset, delta, caseOff;
892     jssrcnote *notes, *sn;
893     JSSrcNoteType type;
894     jsatomid atomIndex;
895     JSAtom *atom;
897     fprintf(gOutFile, "\nSource notes:\n");
898     offset = 0;
899     notes = SCRIPT_NOTES(script);
900     for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
901         delta = SN_DELTA(sn);
902         offset += delta;
903         fprintf(gOutFile, "%3u: %5u [%4u] %-8s",
904                 PTRDIFF(sn, notes, jssrcnote), offset, delta,
905                 js_SrcNoteSpec[SN_TYPE(sn)].name);
906         type = (JSSrcNoteType) SN_TYPE(sn);
907         switch (type) {
908           case SRC_SETLINE:
909             fprintf(gOutFile, " lineno %u", (uintN) js_GetSrcNoteOffset(sn, 0));
910             break;
911           case SRC_FOR:
912             fprintf(gOutFile, " cond %u update %u tail %u",
913                    (uintN) js_GetSrcNoteOffset(sn, 0),
914                    (uintN) js_GetSrcNoteOffset(sn, 1),
915                    (uintN) js_GetSrcNoteOffset(sn, 2));
916             break;
917           case SRC_COND:
918           case SRC_IF_ELSE:
919           case SRC_WHILE:
920           case SRC_PCBASE:
921           case SRC_PCDELTA:
922             fprintf(gOutFile, " offset %u", (uintN) js_GetSrcNoteOffset(sn, 0));
923             break;
924           case SRC_LABEL:
925           case SRC_LABELBRACE:
926           case SRC_BREAK2LABEL:
927           case SRC_CONT2LABEL:
928           case SRC_FUNCDEF: {
929             const char *bytes;
930             JSFunction *fun;
931             JSString *str;
933             atomIndex = (jsatomid) js_GetSrcNoteOffset(sn, 0);
934             atom = js_GetAtom(cx, &script->atomMap, atomIndex);
935             if (type != SRC_FUNCDEF) {
936                 bytes = js_AtomToPrintableString(cx, atom);
937             } else {
938                 fun = (JSFunction *)
939                     JS_GetPrivate(cx, ATOM_TO_OBJECT(atom));
940                 str = JS_DecompileFunction(cx, fun, JS_DONT_PRETTY_PRINT);
941                 bytes = str ? JS_GetStringBytes(str) : "N/A";
942             }
943             fprintf(gOutFile, " atom %u (%s)", (uintN)atomIndex, bytes);
944             break;
945           }
946           case SRC_SWITCH:
947             fprintf(gOutFile, " length %u", (uintN) js_GetSrcNoteOffset(sn, 0));
948             caseOff = (uintN) js_GetSrcNoteOffset(sn, 1);
949             if (caseOff)
950                 fprintf(gOutFile, " first case offset %u", caseOff);
951             break;
952           case SRC_CATCH:
953             delta = (uintN) js_GetSrcNoteOffset(sn, 0);
954             if (delta)
955                 fprintf(gOutFile, " guard size %u", delta);
956             break;
957           default:;
958         }
959         fputc('\n', gOutFile);
960     }
963 static JSBool
964 Notes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
966     uintN i;
967     JSScript *script;
969     for (i = 0; i < argc; i++) {
970         script = ValueToScript(cx, argv[i]);
971         if (!script)
972             continue;
974         SrcNotes(cx, script);
975     }
976     return JS_TRUE;
979 static JSBool
980 TryNotes(JSContext *cx, JSScript *script)
982     JSTryNote *tn = script->trynotes;
984     if (!tn)
985         return JS_TRUE;
986     fprintf(gOutFile, "\nException table:\nstart\tend\tcatch\n");
987     while (tn->start && tn->catchStart) {
988         fprintf(gOutFile, "  %d\t%d\t%d\n",
989                tn->start, tn->start + tn->length, tn->catchStart);
990         tn++;
991     }
992     return JS_TRUE;
995 static JSBool
996 Disassemble(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
998     JSBool lines;
999     uintN i;
1000     JSScript *script;
1002     if (argc > 0 &&
1003         JSVAL_IS_STRING(argv[0]) &&
1004         !strcmp(JS_GetStringBytes(JSVAL_TO_STRING(argv[0])), "-l")) {
1005         lines = JS_TRUE;
1006         argv++, argc--;
1007     } else {
1008         lines = JS_FALSE;
1009     }
1010     for (i = 0; i < argc; i++) {
1011         script = ValueToScript(cx, argv[i]);
1012         if (!script)
1013             continue;
1015         if (JSVAL_IS_FUNCTION(cx, argv[i])) {
1016             JSFunction *fun = JS_ValueToFunction(cx, argv[i]);
1017             if (fun && (fun->flags & JSFUN_FLAGS_MASK)) {
1018                 uint8 flags = fun->flags;
1019                 fputs("flags:", stdout);
1021 #define SHOW_FLAG(flag) if (flags & JSFUN_##flag) fputs(" " #flag, stdout);
1023                 SHOW_FLAG(LAMBDA);
1024                 SHOW_FLAG(SETTER);
1025                 SHOW_FLAG(GETTER);
1026                 SHOW_FLAG(BOUND_METHOD);
1027                 SHOW_FLAG(HEAVYWEIGHT);
1029 #undef SHOW_FLAG
1030                 putchar('\n');
1031             }
1032         }
1034         if (!js_Disassemble(cx, script, lines, stdout))
1035             return JS_FALSE;
1036         SrcNotes(cx, script);
1037         TryNotes(cx, script);
1038     }
1039     return JS_TRUE;
1042 static JSBool
1043 DisassWithSrc(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1044               jsval *rval)
1046 #define LINE_BUF_LEN 512
1047     uintN i, len, line1, line2, bupline;
1048     JSScript *script;
1049     FILE *file;
1050     char linebuf[LINE_BUF_LEN];
1051     jsbytecode *pc, *end;
1052     static char sep[] = ";-------------------------";
1054     for (i = 0; i < argc; i++) {
1055         script = ValueToScript(cx, argv[i]);
1056         if (!script)
1057             continue;
1059         if (!script || !script->filename) {
1060             JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
1061                                             JSSMSG_FILE_SCRIPTS_ONLY);
1062             return JS_FALSE;
1063         }
1065         file = fopen(script->filename, "r");
1066         if (!file) {
1067             JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
1068                             JSSMSG_CANT_OPEN,
1069                             script->filename, strerror(errno));
1070             return JS_FALSE;
1071         }
1073         pc = script->code;
1074         end = pc + script->length;
1076         /* burn the leading lines */
1077         line2 = JS_PCToLineNumber(cx, script, pc);
1078         for (line1 = 0; line1 < line2 - 1; line1++)
1079             fgets(linebuf, LINE_BUF_LEN, file);
1081         bupline = 0;
1082         while (pc < end) {
1083             line2 = JS_PCToLineNumber(cx, script, pc);
1085             if (line2 < line1) {
1086                 if (bupline != line2) {
1087                     bupline = line2;
1088                     fprintf(gOutFile, "%s %3u: BACKUP\n", sep, line2);
1089                 }
1090             } else {
1091                 if (bupline && line1 == line2)
1092                     fprintf(gOutFile, "%s %3u: RESTORE\n", sep, line2);
1093                 bupline = 0;
1094                 while (line1 < line2) {
1095                     if (!fgets(linebuf, LINE_BUF_LEN, file)) {
1096                         JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
1097                                              JSSMSG_UNEXPECTED_EOF,
1098                                              script->filename);
1099                         goto bail;
1100                     }
1101                     line1++;
1102                     fprintf(gOutFile, "%s %3u: %s", sep, line1, linebuf);
1103                 }
1104             }
1106             len = js_Disassemble1(cx, script, pc,
1107                                   PTRDIFF(pc, script->code, jsbytecode),
1108                                   JS_TRUE, stdout);
1109             if (!len)
1110                 return JS_FALSE;
1111             pc += len;
1112         }
1114       bail:
1115         fclose(file);
1116     }
1117     return JS_TRUE;
1118 #undef LINE_BUF_LEN
1121 static JSBool
1122 Tracing(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1124     JSBool bval;
1125     JSString *str;
1127     if (argc == 0) {
1128         *rval = BOOLEAN_TO_JSVAL(cx->tracefp != 0);
1129         return JS_TRUE;
1130     }
1132     switch (JS_TypeOfValue(cx, argv[0])) {
1133       case JSTYPE_NUMBER:
1134         bval = JSVAL_IS_INT(argv[0])
1135                ? JSVAL_TO_INT(argv[0])
1136                : (jsint) *JSVAL_TO_DOUBLE(argv[0]);
1137         break;
1138       case JSTYPE_BOOLEAN:
1139         bval = JSVAL_TO_BOOLEAN(argv[0]);
1140         break;
1141       default:
1142         str = JS_ValueToString(cx, argv[0]);
1143         if (!str)
1144             return JS_FALSE;
1145         fprintf(gErrFile, "tracing: illegal argument %s\n",
1146                 JS_GetStringBytes(str));
1147         return JS_TRUE;
1148     }
1149     cx->tracefp = bval ? stderr : NULL;
1150     return JS_TRUE;
1153 typedef struct DumpAtomArgs {
1154     JSContext   *cx;
1155     FILE        *fp;
1156 } DumpAtomArgs;
1158 static int
1159 DumpAtom(JSHashEntry *he, int i, void *arg)
1161     DumpAtomArgs *args = (DumpAtomArgs *)arg;
1162     FILE *fp = args->fp;
1163     JSAtom *atom = (JSAtom *)he;
1165     fprintf(fp, "%3d %08x %5lu ",
1166             i, (uintN)he->keyHash, (unsigned long)atom->number);
1167     if (ATOM_IS_STRING(atom))
1168         fprintf(fp, "\"%s\"\n", js_AtomToPrintableString(args->cx, atom));
1169     else if (ATOM_IS_INT(atom))
1170         fprintf(fp, "%ld\n", (long)ATOM_TO_INT(atom));
1171     else
1172         fprintf(fp, "%.16g\n", *ATOM_TO_DOUBLE(atom));
1173     return HT_ENUMERATE_NEXT;
1176 static void
1177 DumpScope(JSContext *cx, JSObject *obj, FILE *fp)
1179     uintN i;
1180     JSScope *scope;
1181     JSScopeProperty *sprop;
1183     i = 0;
1184     scope = OBJ_SCOPE(obj);
1185     for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) {
1186         if (SCOPE_HAD_MIDDLE_DELETE(scope) && !SCOPE_HAS_PROPERTY(scope, sprop))
1187             continue;
1188         fprintf(fp, "%3u %p", i, sprop);
1189         if (JSID_IS_INT(sprop->id)) {
1190             fprintf(fp, " [%ld]", (long)JSVAL_TO_INT(sprop->id));
1191         } else if (JSID_IS_ATOM(sprop->id)) {
1192             JSAtom *atom = JSID_TO_ATOM(sprop->id);
1193             fprintf(fp, " \"%s\"", js_AtomToPrintableString(cx, atom));
1194         } else {
1195             jsval v = OBJECT_TO_JSVAL(JSID_TO_OBJECT(sprop->id));
1196             fprintf(fp, " \"%s\"", js_ValueToPrintableString(cx, v));
1197         }
1199 #define DUMP_ATTR(name) if (sprop->attrs & JSPROP_##name) fputs(" " #name, fp)
1200         DUMP_ATTR(ENUMERATE);
1201         DUMP_ATTR(READONLY);
1202         DUMP_ATTR(PERMANENT);
1203         DUMP_ATTR(EXPORTED);
1204         DUMP_ATTR(GETTER);
1205         DUMP_ATTR(SETTER);
1206 #undef  DUMP_ATTR
1208         fprintf(fp, " slot %lu flags %x shortid %d\n",
1209                 sprop->slot, sprop->flags, sprop->shortid);
1210     }
1213 static JSBool
1214 DumpStats(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1216     uintN i;
1217     JSString *str;
1218     const char *bytes;
1219     JSAtom *atom;
1220     JSObject *obj2;
1221     JSProperty *prop;
1222     jsval value;
1224     for (i = 0; i < argc; i++) {
1225         str = JS_ValueToString(cx, argv[i]);
1226         if (!str)
1227             return JS_FALSE;
1228         bytes = JS_GetStringBytes(str);
1229         if (strcmp(bytes, "arena") == 0) {
1230 #ifdef JS_ARENAMETER
1231             JS_DumpArenaStats(stdout);
1232 #endif
1233         } else if (strcmp(bytes, "atom") == 0) {
1234             DumpAtomArgs args;
1236             fprintf(gOutFile, "\natom table contents:\n");
1237             args.cx = cx;
1238             args.fp = stdout;
1239             JS_HashTableEnumerateEntries(cx->runtime->atomState.table,
1240                                          DumpAtom,
1241                                          &args);
1242 #ifdef HASHMETER
1243             JS_HashTableDumpMeter(cx->runtime->atomState.table,
1244                                   DumpAtom,
1245                                   stdout);
1246 #endif
1247         } else if (strcmp(bytes, "global") == 0) {
1248             DumpScope(cx, cx->globalObject, stdout);
1249         } else {
1250             atom = js_Atomize(cx, bytes, JS_GetStringLength(str), 0);
1251             if (!atom)
1252                 return JS_FALSE;
1253             if (!js_FindProperty(cx, ATOM_TO_JSID(atom), &obj, &obj2, &prop))
1254                 return JS_FALSE;
1255             if (prop) {
1256                 OBJ_DROP_PROPERTY(cx, obj2, prop);
1257                 if (!OBJ_GET_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &value))
1258                     return JS_FALSE;
1259             }
1260             if (!prop || !JSVAL_IS_OBJECT(value)) {
1261                 fprintf(gErrFile, "js: invalid stats argument %s\n",
1262                         bytes);
1263                 continue;
1264             }
1265             obj = JSVAL_TO_OBJECT(value);
1266             if (obj)
1267                 DumpScope(cx, obj, stdout);
1268         }
1269     }
1270     return JS_TRUE;
1273 #endif /* DEBUG */
1275 #ifdef TEST_EXPORT
1276 static JSBool
1277 DoExport(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1279     JSAtom *atom;
1280     JSObject *obj2;
1281     JSProperty *prop;
1282     JSBool ok;
1283     uintN attrs;
1285     if (argc != 2) {
1286         JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_DOEXP_USAGE);
1287         return JS_FALSE;
1288     }
1289     if (!JS_ValueToObject(cx, argv[0], &obj))
1290         return JS_FALSE;
1291     argv[0] = OBJECT_TO_JSVAL(obj);
1292     atom = js_ValueToStringAtom(cx, argv[1]);
1293     if (!atom)
1294         return JS_FALSE;
1295     if (!OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &obj2, &prop))
1296         return JS_FALSE;
1297     if (!prop) {
1298         ok = OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, NULL, NULL,
1299                                  JSPROP_EXPORTED, NULL);
1300     } else {
1301         ok = OBJ_GET_ATTRIBUTES(cx, obj, ATOM_TO_JSID(atom), prop, &attrs);
1302         if (ok) {
1303             attrs |= JSPROP_EXPORTED;
1304             ok = OBJ_SET_ATTRIBUTES(cx, obj, ATOM_TO_JSID(atom), prop, &attrs);
1305         }
1306         OBJ_DROP_PROPERTY(cx, obj2, prop);
1307     }
1308     return ok;
1310 #endif
1312 #ifdef TEST_CVTARGS
1313 #include <ctype.h>
1315 static const char *
1316 EscapeWideString(jschar *w)
1318     static char enuf[80];
1319     static char hex[] = "0123456789abcdef";
1320     jschar u;
1321     unsigned char b, c;
1322     int i, j;
1324     if (!w)
1325         return "";
1326     for (i = j = 0; i < sizeof enuf - 1; i++, j++) {
1327         u = w[j];
1328         if (u == 0)
1329             break;
1330         b = (unsigned char)(u >> 8);
1331         c = (unsigned char)(u);
1332         if (b) {
1333             if (i >= sizeof enuf - 6)
1334                 break;
1335             enuf[i++] = '\\';
1336             enuf[i++] = 'u';
1337             enuf[i++] = hex[b >> 4];
1338             enuf[i++] = hex[b & 15];
1339             enuf[i++] = hex[c >> 4];
1340             enuf[i] = hex[c & 15];
1341         } else if (!isprint(c)) {
1342             if (i >= sizeof enuf - 4)
1343                 break;
1344             enuf[i++] = '\\';
1345             enuf[i++] = 'x';
1346             enuf[i++] = hex[c >> 4];
1347             enuf[i] = hex[c & 15];
1348         } else {
1349             enuf[i] = (char)c;
1350         }
1351     }
1352     enuf[i] = 0;
1353     return enuf;
1356 #include <stdarg.h>
1358 static JSBool
1359 ZZ_formatter(JSContext *cx, const char *format, JSBool fromJS, jsval **vpp,
1360              va_list *app)
1362     jsval *vp;
1363     va_list ap;
1364     jsdouble re, im;
1366     printf("entering ZZ_formatter");
1367     vp = *vpp;
1368     ap = *app;
1369     if (fromJS) {
1370         if (!JS_ValueToNumber(cx, vp[0], &re))
1371             return JS_FALSE;
1372         if (!JS_ValueToNumber(cx, vp[1], &im))
1373             return JS_FALSE;
1374         *va_arg(ap, jsdouble *) = re;
1375         *va_arg(ap, jsdouble *) = im;
1376     } else {
1377         re = va_arg(ap, jsdouble);
1378         im = va_arg(ap, jsdouble);
1379         if (!JS_NewNumberValue(cx, re, &vp[0]))
1380             return JS_FALSE;
1381         if (!JS_NewNumberValue(cx, im, &vp[1]))
1382             return JS_FALSE;
1383     }
1384     *vpp = vp + 2;
1385     *app = ap;
1386     printf("leaving ZZ_formatter");
1387     return JS_TRUE;
1390 static JSBool
1391 ConvertArgs(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1393     JSBool b = JS_FALSE;
1394     jschar c = 0;
1395     int32 i = 0, j = 0;
1396     uint32 u = 0;
1397     jsdouble d = 0, I = 0, re = 0, im = 0;
1398     char *s = NULL;
1399     JSString *str = NULL;
1400     jschar *w = NULL;
1401     JSObject *obj2 = NULL;
1402     JSFunction *fun = NULL;
1403     jsval v = JSVAL_VOID;
1404     JSBool ok;
1406     if (!JS_AddArgumentFormatter(cx, "ZZ", ZZ_formatter))
1407         return JS_FALSE;;
1408     ok = JS_ConvertArguments(cx, argc, argv, "b/ciujdIsSWofvZZ*",
1409                              &b, &c, &i, &u, &j, &d, &I, &s, &str, &w, &obj2,
1410                              &fun, &v, &re, &im);
1411     JS_RemoveArgumentFormatter(cx, "ZZ");
1412     if (!ok)
1413         return JS_FALSE;
1414     fprintf(gOutFile,
1415             "b %u, c %x (%c), i %ld, u %lu, j %ld\n",
1416             b, c, (char)c, i, u, j);
1417     fprintf(gOutFile,
1418             "d %g, I %g, s %s, S %s, W %s, obj %s, fun %s\n"
1419             "v %s, re %g, im %g\n",
1420             d, I, s, str ? JS_GetStringBytes(str) : "", EscapeWideString(w),
1421             JS_GetStringBytes(JS_ValueToString(cx, OBJECT_TO_JSVAL(obj2))),
1422             fun ? JS_GetStringBytes(JS_DecompileFunction(cx, fun, 4)) : "",
1423             JS_GetStringBytes(JS_ValueToString(cx, v)), re, im);
1424     return JS_TRUE;
1426 #endif
1428 static JSBool
1429 BuildDate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1431     fprintf(gOutFile, "built on %s at %s\n", __DATE__, __TIME__);
1432     return JS_TRUE;
1435 static JSBool
1436 Clear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1438     if (argc != 0 && !JS_ValueToObject(cx, argv[0], &obj))
1439         return JS_FALSE;
1440     JS_ClearScope(cx, obj);
1441     return JS_TRUE;
1444 static JSBool
1445 Intern(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1447     JSString *str;
1449     str = JS_ValueToString(cx, argv[0]);
1450     if (!str)
1451         return JS_FALSE;
1452     if (!JS_InternUCStringN(cx, JS_GetStringChars(str),
1453                                 JS_GetStringLength(str))) {
1454         return JS_FALSE;
1455     }
1456     return JS_TRUE;
1459 static JSBool
1460 Clone(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1462     JSFunction *fun;
1463     JSObject *funobj, *parent, *clone;
1465     fun = JS_ValueToFunction(cx, argv[0]);
1466     if (!fun)
1467         return JS_FALSE;
1468     funobj = JS_GetFunctionObject(fun);
1469     if (argc > 1) {
1470         if (!JS_ValueToObject(cx, argv[1], &parent))
1471             return JS_FALSE;
1472     } else {
1473         parent = JS_GetParent(cx, funobj);
1474     }
1475     clone = JS_CloneFunctionObject(cx, funobj, parent);
1476     if (!clone)
1477         return JS_FALSE;
1478     *rval = OBJECT_TO_JSVAL(clone);
1479     return JS_TRUE;
1482 static JSBool
1483 Seal(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1485     JSObject *target;
1486     JSBool deep = JS_FALSE;
1488     if (!JS_ConvertArguments(cx, argc, argv, "o/b", &target, &deep))
1489         return JS_FALSE;
1490     if (!target)
1491         return JS_TRUE;
1492     return JS_SealObject(cx, target, deep);
1495 static JSBool
1496 GetPDA(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1498     JSObject *vobj, *aobj, *pdobj;
1499     JSBool ok;
1500     JSPropertyDescArray pda;
1501     JSPropertyDesc *pd;
1502     uint32 i;
1503     jsval v;
1505     if (!JS_ValueToObject(cx, argv[0], &vobj))
1506         return JS_FALSE;
1507     if (!vobj)
1508         return JS_TRUE;
1510     aobj = JS_NewArrayObject(cx, 0, NULL);
1511     if (!aobj)
1512         return JS_FALSE;
1513     *rval = OBJECT_TO_JSVAL(aobj);
1515     ok = JS_GetPropertyDescArray(cx, vobj, &pda);
1516     if (!ok)
1517         return JS_FALSE;
1518     pd = pda.array;
1519     for (i = 0; i < pda.length; i++) {
1520         pdobj = JS_NewObject(cx, NULL, NULL, NULL);
1521         if (!pdobj) {
1522             ok = JS_FALSE;
1523             break;
1524         }
1526         ok = JS_SetProperty(cx, pdobj, "id", &pd->id) &&
1527              JS_SetProperty(cx, pdobj, "value", &pd->value) &&
1528              (v = INT_TO_JSVAL(pd->flags),
1529               JS_SetProperty(cx, pdobj, "flags", &v)) &&
1530              (v = INT_TO_JSVAL(pd->slot),
1531               JS_SetProperty(cx, pdobj, "slot", &v)) &&
1532              JS_SetProperty(cx, pdobj, "alias", &pd->alias);
1533         if (!ok)
1534             break;
1536         v = OBJECT_TO_JSVAL(pdobj);
1537         ok = JS_SetElement(cx, aobj, i, &v);
1538         if (!ok)
1539             break;
1540     }
1541     JS_PutPropertyDescArray(cx, &pda);
1542     return ok;
1545 static JSBool
1546 GetSLX(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1548     JSScript *script;
1550     script = ValueToScript(cx, argv[0]);
1551     if (!script)
1552         return JS_FALSE;
1553     *rval = INT_TO_JSVAL(js_GetScriptLineExtent(script));
1554     return JS_TRUE;
1557 static JSBool
1558 ToInt32(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1560     int32 i;
1562     if (!JS_ValueToInt32(cx, argv[0], &i))
1563         return JS_FALSE;
1564     return JS_NewNumberValue(cx, i, rval);
1567 static JSBool
1568 StringsAreUtf8(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1569                jsval *rval)
1571     *rval = JS_StringsAreUTF8 () ? JSVAL_TRUE : JSVAL_FALSE;
1572     return JS_TRUE;
1575 static const char* badUtf8 = "...\xC0...";
1576 static const char* bigUtf8 = "...\xFB\xBF\xBF\xBF\xBF...";
1577 static const jschar badSurrogate[] = { 'A', 'B', 'C', 0xDEEE, 'D', 'E', 0 };
1579 static JSBool
1580 TestUtf8(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1582     intN mode = 1;
1583     jschar chars[20];
1584     size_t charsLength = 5;
1585     char bytes[20];
1586     size_t bytesLength = 20;
1587     if (argc && !JS_ValueToInt32(cx, *argv, &mode))
1588         return JS_FALSE;
1590     /* The following throw errors if compiled with UTF-8. */
1591     switch (mode) {
1592       /* mode 1: malformed UTF-8 string. */
1593       case 1: 
1594         JS_NewStringCopyZ(cx, badUtf8); 
1595         break;
1596       /* mode 2: big UTF-8 character. */
1597       case 2: 
1598         JS_NewStringCopyZ(cx, bigUtf8); 
1599         break;
1600       /* mode 3: bad surrogate character. */
1601       case 3: 
1602         JS_EncodeCharacters(cx, badSurrogate, 6, bytes, &bytesLength); 
1603         break;
1604       /* mode 4: use a too small buffer. */
1605       case 4: 
1606         JS_DecodeBytes(cx, "1234567890", 10, chars, &charsLength); 
1607         break;
1608       default:
1609         JS_ReportError(cx, "invalid mode parameter");
1610         return JS_FALSE;
1611     }
1612     return !JS_IsExceptionPending (cx);
1615 static JSFunctionSpec shell_functions[] = {
1616     {"version",         Version,        0},
1617     {"options",         Options,        0},
1618     {"load",            Load,           1},
1619     {"readline",        ReadLine,       0},
1620     {"print",           Print,          0},
1621     {"help",            Help,           0},
1622     {"quit",            Quit,           0},
1623     {"gc",              GC,             0},
1624     {"trap",            Trap,           3},
1625     {"untrap",          Untrap,         2},
1626     {"line2pc",         LineToPC,       0},
1627     {"pc2line",         PCToLine,       0},
1628     {"stringsAreUtf8",  StringsAreUtf8, 0},
1629     {"testUtf8",        TestUtf8,       1},
1630 #ifdef DEBUG
1631     {"dis",             Disassemble,    1},
1632     {"dissrc",          DisassWithSrc,  1},
1633     {"notes",           Notes,          1},
1634     {"tracing",         Tracing,        0},
1635     {"stats",           DumpStats,      1},
1636 #endif
1637 #ifdef TEST_EXPORT
1638     {"xport",           DoExport,       2},
1639 #endif
1640 #ifdef TEST_CVTARGS
1641     {"cvtargs",         ConvertArgs,    0, 0, 12},
1642 #endif
1643     {"build",           BuildDate,      0},
1644     {"clear",           Clear,          0},
1645     {"intern",          Intern,         1},
1646     {"clone",           Clone,          1},
1647     {"seal",            Seal,           1, 0, 1},
1648     {"getpda",          GetPDA,         1},
1649     {"getslx",          GetSLX,         1},
1650     {"toint32",         ToInt32,        1},
1651     {0}
1652 };
1654 /* NOTE: These must be kept in sync with the above. */
1656 static char *shell_help_messages[] = {
1657     "version([number])      Get or set JavaScript version number",
1658     "options([option ...])  Get or toggle JavaScript options",
1659     "load(['foo.js' ...])   Load files named by string arguments",
1660     "readline()             Read a single line from stdin",
1661     "print([exp ...])       Evaluate and print expressions",
1662     "help([name ...])       Display usage and help messages",
1663     "quit()                 Quit the shell",
1664     "gc()                   Run the garbage collector",
1665     "trap([fun, [pc,]] exp) Trap bytecode execution",
1666     "untrap(fun[, pc])      Remove a trap",
1667     "line2pc([fun,] line)   Map line number to PC",
1668     "pc2line(fun[, pc])     Map PC to line number",
1669     "stringsAreUTF8()       Check if strings are UTF-8 encoded",
1670     "testUTF8(mode)         Perform UTF-8 tests (modes are 1 to 4)",
1671 #ifdef DEBUG
1672     "dis([fun])             Disassemble functions into bytecodes",
1673     "dissrc([fun])          Disassemble functions with source lines",
1674     "notes([fun])           Show source notes for functions",
1675     "tracing([toggle])      Turn tracing on or off",
1676     "stats([string ...])    Dump 'arena', 'atom', 'global' stats",
1677 #endif
1678 #ifdef TEST_EXPORT
1679     "xport(obj, id)         Export identified property from object",
1680 #endif
1681 #ifdef TEST_CVTARGS
1682     "cvtargs(b, c, ...)     Test JS_ConvertArguments",
1683 #endif
1684     "build()                Show build date and time",
1685     "clear([obj])           Clear properties of object",
1686     "intern(str)            Internalize str in the atom table",
1687     "clone(fun[, scope])    Clone function object",
1688     "seal(obj[, deep])      Seal object, or object graph if deep",
1689     "getpda(obj)            Get the property descriptors for obj",
1690     "getslx(obj)            Get script line extent",
1691     "toint32(n)             Testing hook for JS_ValueToInt32",
1692     0
1693 };
1695 static void
1696 ShowHelpHeader(void)
1698     fprintf(gOutFile, "%-14s %-22s %s\n", "Command", "Usage", "Description");
1699     fprintf(gOutFile, "%-14s %-22s %s\n", "=======", "=====", "===========");
1702 static void
1703 ShowHelpForCommand(uintN n)
1705     fprintf(gOutFile, "%-14.14s %s\n", shell_functions[n].name, shell_help_messages[n]);
1708 static JSBool
1709 Help(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1711     uintN i, j;
1712     int did_header, did_something;
1713     JSType type;
1714     JSFunction *fun;
1715     JSString *str;
1716     const char *bytes;
1718     fprintf(gOutFile, "%s\n", JS_GetImplementationVersion());
1719     if (argc == 0) {
1720         ShowHelpHeader();
1721         for (i = 0; shell_functions[i].name; i++)
1722             ShowHelpForCommand(i);
1723     } else {
1724         did_header = 0;
1725         for (i = 0; i < argc; i++) {
1726             did_something = 0;
1727             type = JS_TypeOfValue(cx, argv[i]);
1728             if (type == JSTYPE_FUNCTION) {
1729                 fun = JS_ValueToFunction(cx, argv[i]);
1730                 str = fun->atom ? ATOM_TO_STRING(fun->atom) : NULL;
1731             } else if (type == JSTYPE_STRING) {
1732                 str = JSVAL_TO_STRING(argv[i]);
1733             } else {
1734                 str = NULL;
1735             }
1736             if (str) {
1737                 bytes = JS_GetStringBytes(str);
1738                 for (j = 0; shell_functions[j].name; j++) {
1739                     if (!strcmp(bytes, shell_functions[j].name)) {
1740                         if (!did_header) {
1741                             did_header = 1;
1742                             ShowHelpHeader();
1743                         }
1744                         did_something = 1;
1745                         ShowHelpForCommand(j);
1746                         break;
1747                     }
1748                 }
1749             }
1750             if (!did_something) {
1751                 str = JS_ValueToString(cx, argv[i]);
1752                 if (!str)
1753                     return JS_FALSE;
1754                 fprintf(gErrFile, "Sorry, no help for %s\n",
1755                         JS_GetStringBytes(str));
1756             }
1757         }
1758     }
1759     return JS_TRUE;
1762 /*
1763  * Define a JS object called "it".  Give it class operations that printf why
1764  * they're being called for tutorial purposes.
1765  */
1766 enum its_tinyid {
1767     ITS_COLOR, ITS_HEIGHT, ITS_WIDTH, ITS_FUNNY, ITS_ARRAY, ITS_RDONLY
1768 };
1770 static JSPropertySpec its_props[] = {
1771     {"color",           ITS_COLOR,      JSPROP_ENUMERATE},
1772     {"height",          ITS_HEIGHT,     JSPROP_ENUMERATE},
1773     {"width",           ITS_WIDTH,      JSPROP_ENUMERATE},
1774     {"funny",           ITS_FUNNY,      JSPROP_ENUMERATE},
1775     {"array",           ITS_ARRAY,      JSPROP_ENUMERATE},
1776     {"rdonly",          ITS_RDONLY,     JSPROP_READONLY},
1777     {0}
1778 };
1780 static JSBool
1781 its_item(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1783     *rval = OBJECT_TO_JSVAL(obj);
1784     if (argc != 0)
1785         JS_SetCallReturnValue2(cx, argv[0]);
1786     return JS_TRUE;
1789 static JSBool
1790 its_bindMethod(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1791                jsval *rval)
1793     char *name;
1794     JSObject *method;
1796     if (!JS_ConvertArguments(cx, argc, argv, "so", &name, &method))
1797         return JS_FALSE;
1799     *rval = OBJECT_TO_JSVAL(method);
1801     if (JS_TypeOfValue(cx, *rval) != JSTYPE_FUNCTION) {
1802         JSString *valstr = JS_ValueToString(cx, *rval);
1803         if (valstr) {
1804             JS_ReportError(cx, "can't bind method %s to non-callable object %s",
1805                            name, JS_GetStringBytes(valstr));
1806         }
1807         return JS_FALSE;
1808     }
1810     if (!JS_DefineProperty(cx, obj, name, *rval, NULL, NULL, JSPROP_ENUMERATE))
1811         return JS_FALSE;
1813     return JS_SetParent(cx, method, obj);
1816 static JSFunctionSpec its_methods[] = {
1817     {"item",            its_item,       0},
1818     {"bindMethod",      its_bindMethod, 2},
1819     {0}
1820 };
1822 #ifdef JSD_LOWLEVEL_SOURCE
1823 /*
1824  * This facilitates sending source to JSD (the debugger system) in the shell
1825  * where the source is loaded using the JSFILE hack in jsscan. The function
1826  * below is used as a callback for the jsdbgapi JS_SetSourceHandler hook.
1827  * A more normal embedding (e.g. mozilla) loads source itself and can send
1828  * source directly to JSD without using this hook scheme.
1829  */
1830 static void
1831 SendSourceToJSDebugger(const char *filename, uintN lineno,
1832                        jschar *str, size_t length,
1833                        void **listenerTSData, JSDContext* jsdc)
1835     JSDSourceText *jsdsrc = (JSDSourceText *) *listenerTSData;
1837     if (!jsdsrc) {
1838         if (!filename)
1839             filename = "typein";
1840         if (1 == lineno) {
1841             jsdsrc = JSD_NewSourceText(jsdc, filename);
1842         } else {
1843             jsdsrc = JSD_FindSourceForURL(jsdc, filename);
1844             if (jsdsrc && JSD_SOURCE_PARTIAL !=
1845                 JSD_GetSourceStatus(jsdc, jsdsrc)) {
1846                 jsdsrc = NULL;
1847             }
1848         }
1849     }
1850     if (jsdsrc) {
1851         jsdsrc = JSD_AppendUCSourceText(jsdc,jsdsrc, str, length,
1852                                         JSD_SOURCE_PARTIAL);
1853     }
1854     *listenerTSData = jsdsrc;
1856 #endif /* JSD_LOWLEVEL_SOURCE */
1858 static JSBool its_noisy;    /* whether to be noisy when finalizing it */
1860 static JSBool
1861 its_addProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
1863     if (its_noisy) {
1864         fprintf(gOutFile, "adding its property %s,",
1865                JS_GetStringBytes(JS_ValueToString(cx, id)));
1866         fprintf(gOutFile, " initial value %s\n",
1867                JS_GetStringBytes(JS_ValueToString(cx, *vp)));
1868     }
1869     return JS_TRUE;
1872 static JSBool
1873 its_delProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
1875     if (its_noisy) {
1876         fprintf(gOutFile, "deleting its property %s,",
1877                JS_GetStringBytes(JS_ValueToString(cx, id)));
1878         fprintf(gOutFile, " current value %s\n",
1879                JS_GetStringBytes(JS_ValueToString(cx, *vp)));
1880     }
1881     return JS_TRUE;
1884 static JSBool
1885 its_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
1887     if (its_noisy) {
1888         fprintf(gOutFile, "getting its property %s,",
1889                JS_GetStringBytes(JS_ValueToString(cx, id)));
1890         fprintf(gOutFile, " current value %s\n",
1891                JS_GetStringBytes(JS_ValueToString(cx, *vp)));
1892     }
1893     return JS_TRUE;
1896 static JSBool
1897 its_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
1899     if (its_noisy) {
1900         fprintf(gOutFile, "setting its property %s,",
1901                JS_GetStringBytes(JS_ValueToString(cx, id)));
1902         fprintf(gOutFile, " new value %s\n",
1903                JS_GetStringBytes(JS_ValueToString(cx, *vp)));
1904     }
1905     if (JSVAL_IS_STRING(id) &&
1906         !strcmp(JS_GetStringBytes(JSVAL_TO_STRING(id)), "noisy")) {
1907         return JS_ValueToBoolean(cx, *vp, &its_noisy);
1908     }
1909     return JS_TRUE;
1912 static JSBool
1913 its_enumerate(JSContext *cx, JSObject *obj)
1915     if (its_noisy)
1916         fprintf(gOutFile, "enumerate its properties\n");
1917     return JS_TRUE;
1920 static JSBool
1921 its_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
1922             JSObject **objp)
1924     if (its_noisy) {
1925         fprintf(gOutFile, "resolving its property %s, flags {%s,%s,%s}\n",
1926                JS_GetStringBytes(JS_ValueToString(cx, id)),
1927                (flags & JSRESOLVE_QUALIFIED) ? "qualified" : "",
1928                (flags & JSRESOLVE_ASSIGNING) ? "assigning" : "",
1929                (flags & JSRESOLVE_DETECTING) ? "detecting" : "");
1930     }
1931     return JS_TRUE;
1934 static JSBool
1935 its_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
1937     if (its_noisy)
1938         fprintf(gOutFile, "converting it to %s type\n", JS_GetTypeName(cx, type));
1939     return JS_TRUE;
1942 static void
1943 its_finalize(JSContext *cx, JSObject *obj)
1945     if (its_noisy)
1946         fprintf(gOutFile, "finalizing it\n");
1949 static JSClass its_class = {
1950     "It", JSCLASS_NEW_RESOLVE,
1951     its_addProperty,  its_delProperty,  its_getProperty,  its_setProperty,
1952     its_enumerate,    (JSResolveOp)its_resolve,
1953                                         its_convert,      its_finalize
1954 };
1956 JSErrorFormatString jsShell_ErrorFormatString[JSErr_Limit] = {
1957 #if JS_HAS_DFLT_MSG_STRINGS
1958 #define MSG_DEF(name, number, count, exception, format) \
1959     { format, count } ,
1960 #else
1961 #define MSG_DEF(name, number, count, exception, format) \
1962     { NULL, count } ,
1963 #endif
1964 #include "jsshell.msg"
1965 #undef MSG_DEF
1966 };
1968 static const JSErrorFormatString *
1969 my_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber)
1971     if ((errorNumber > 0) && (errorNumber < JSShellErr_Limit))
1972         return &jsShell_ErrorFormatString[errorNumber];
1973     return NULL;
1976 static void
1977 my_LoadErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
1979     if (!report) {
1980         fprintf(gErrFile, "%s\n", message);
1981         return;
1982     }
1984     /* Ignore any exceptions */
1985     if (JSREPORT_IS_EXCEPTION(report->flags))
1986         return;
1988     /* Otherwise, fall back to the ordinary error reporter. */
1989     my_ErrorReporter(cx, message, report);
1992 static void
1993 my_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
1995     int i, j, k, n;
1996     char *prefix, *tmp;
1997     const char *ctmp;
1999     if (!report) {
2000         fprintf(gErrFile, "%s\n", message);
2001         return;
2002     }
2004     /* Conditionally ignore reported warnings. */
2005     if (JSREPORT_IS_WARNING(report->flags) && !reportWarnings)
2006         return;
2008     prefix = NULL;
2009     if (report->filename)
2010         prefix = JS_smprintf("%s:", report->filename);
2011     if (report->lineno) {
2012         tmp = prefix;
2013         prefix = JS_smprintf("%s%u: ", tmp ? tmp : "", report->lineno);
2014         JS_free(cx, tmp);
2015     }
2016     if (JSREPORT_IS_WARNING(report->flags)) {
2017         tmp = prefix;
2018         prefix = JS_smprintf("%s%swarning: ",
2019                              tmp ? tmp : "",
2020                              JSREPORT_IS_STRICT(report->flags) ? "strict " : "");
2021         JS_free(cx, tmp);
2022     }
2024     /* embedded newlines -- argh! */
2025     while ((ctmp = strchr(message, '\n')) != 0) {
2026         ctmp++;
2027         if (prefix)
2028             fputs(prefix, gErrFile);
2029         fwrite(message, 1, ctmp - message, gErrFile);
2030         message = ctmp;
2031     }
2033     /* If there were no filename or lineno, the prefix might be empty */
2034     if (prefix)
2035         fputs(prefix, gErrFile);
2036     fputs(message, gErrFile);
2038     if (!report->linebuf) {
2039         fputc('\n', gErrFile);
2040         goto out;
2041     }
2043     /* report->linebuf usually ends with a newline. */
2044     n = strlen(report->linebuf);
2045     fprintf(gErrFile, ":\n%s%s%s%s",
2046             prefix,
2047             report->linebuf,
2048             (n > 0 && report->linebuf[n-1] == '\n') ? "" : "\n",
2049             prefix);
2050     n = PTRDIFF(report->tokenptr, report->linebuf, char);
2051     for (i = j = 0; i < n; i++) {
2052         if (report->linebuf[i] == '\t') {
2053             for (k = (j + 8) & ~7; j < k; j++) {
2054                 fputc('.', gErrFile);
2055             }
2056             continue;
2057         }
2058         fputc('.', gErrFile);
2059         j++;
2060     }
2061     fputs("^\n", gErrFile);
2062  out:
2063     if (!JSREPORT_IS_WARNING(report->flags)) {
2064         if (report->errorNumber == JSMSG_OUT_OF_MEMORY) {
2065             gExitCode = EXITCODE_OUT_OF_MEMORY;
2066         } else {
2067             gExitCode = EXITCODE_RUNTIME_ERROR;
2068         }
2069     }
2070     JS_free(cx, prefix);
2073 #if defined(SHELL_HACK) && defined(DEBUG) && defined(XP_UNIX)
2074 static JSBool
2075 Exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
2077     JSFunction *fun;
2078     const char *name, **nargv;
2079     uintN i, nargc;
2080     JSString *str;
2081     pid_t pid;
2082     int status;
2084     fun = JS_ValueToFunction(cx, argv[-2]);
2085     if (!fun)
2086         return JS_FALSE;
2087     if (!fun->atom)
2088         return JS_TRUE;
2089     name = JS_GetStringBytes(ATOM_TO_STRING(fun->atom));
2090     nargc = 1 + argc;
2091     nargv = JS_malloc(cx, (nargc + 1) * sizeof(char *));
2092     if (!nargv)
2093         return JS_FALSE;
2094     nargv[0] = name;
2095     for (i = 1; i < nargc; i++) {
2096         str = JS_ValueToString(cx, argv[i-1]);
2097         if (!str) {
2098             JS_free(cx, nargv);
2099             return JS_FALSE;
2100         }
2101         nargv[i] = JS_GetStringBytes(str);
2102     }
2103     nargv[nargc] = 0;
2104     pid = fork();
2105     switch (pid) {
2106       case -1:
2107         perror("js");
2108         break;
2109       case 0:
2110         (void) execvp(name, (char **)nargv);
2111         perror("js");
2112         exit(127);
2113       default:
2114         while (waitpid(pid, &status, 0) < 0 && errno == EINTR)
2115             continue;
2116         break;
2117     }
2118     JS_free(cx, nargv);
2119     return JS_TRUE;
2121 #endif
2123 #define LAZY_STANDARD_CLASSES
2125 static JSBool
2126 global_enumerate(JSContext *cx, JSObject *obj)
2128 #ifdef LAZY_STANDARD_CLASSES
2129     return JS_EnumerateStandardClasses(cx, obj);
2130 #else
2131     return JS_TRUE;
2132 #endif
2135 static JSBool
2136 global_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
2137                JSObject **objp)
2139 #ifdef LAZY_STANDARD_CLASSES
2140     if ((flags & JSRESOLVE_ASSIGNING) == 0) {
2141         JSBool resolved;
2143         if (!JS_ResolveStandardClass(cx, obj, id, &resolved))
2144             return JS_FALSE;
2145         if (resolved) {
2146             *objp = obj;
2147             return JS_TRUE;
2148         }
2149     }
2150 #endif
2152 #if defined(SHELL_HACK) && defined(DEBUG) && defined(XP_UNIX)
2153     if ((flags & (JSRESOLVE_QUALIFIED | JSRESOLVE_ASSIGNING)) == 0) {
2154         /*
2155          * Do this expensive hack only for unoptimized Unix builds, which are
2156          * not used for benchmarking.
2157          */
2158         char *path, *comp, *full;
2159         const char *name;
2160         JSBool ok, found;
2161         JSFunction *fun;
2163         if (!JSVAL_IS_STRING(id))
2164             return JS_TRUE;
2165         path = getenv("PATH");
2166         if (!path)
2167             return JS_TRUE;
2168         path = JS_strdup(cx, path);
2169         if (!path)
2170             return JS_FALSE;
2171         name = JS_GetStringBytes(JSVAL_TO_STRING(id));
2172         ok = JS_TRUE;
2173         for (comp = strtok(path, ":"); comp; comp = strtok(NULL, ":")) {
2174             if (*comp != '\0') {
2175                 full = JS_smprintf("%s/%s", comp, name);
2176                 if (!full) {
2177                     JS_ReportOutOfMemory(cx);
2178                     ok = JS_FALSE;
2179                     break;
2180                 }
2181             } else {
2182                 full = (char *)name;
2183             }
2184             found = (access(full, X_OK) == 0);
2185             if (*comp != '\0')
2186                 free(full);
2187             if (found) {
2188                 fun = JS_DefineFunction(cx, obj, name, Exec, 0,
2189                                         JSPROP_ENUMERATE);
2190                 ok = (fun != NULL);
2191                 if (ok)
2192                     *objp = obj;
2193                 break;
2194             }
2195         }
2196         JS_free(cx, path);
2197         return ok;
2198     }
2199 #else
2200     return JS_TRUE;
2201 #endif
2204 JSClass global_class = {
2205     "global", JSCLASS_NEW_RESOLVE,
2206     JS_PropertyStub,  JS_PropertyStub,
2207     JS_PropertyStub,  JS_PropertyStub,
2208     global_enumerate, (JSResolveOp) global_resolve,
2209     JS_ConvertStub,   JS_FinalizeStub
2210 };
2212 static JSBool
2213 env_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
2215 /* XXX porting may be easy, but these don't seem to supply setenv by default */
2216 #if !defined XP_BEOS && !defined XP_OS2 && !defined SOLARIS
2217     JSString *idstr, *valstr;
2218     const char *name, *value;
2219     int rv;
2221     idstr = JS_ValueToString(cx, id);
2222     valstr = JS_ValueToString(cx, *vp);
2223     if (!idstr || !valstr)
2224         return JS_FALSE;
2225     name = JS_GetStringBytes(idstr);
2226     value = JS_GetStringBytes(valstr);
2227 #if defined XP_WIN || defined HPUX || defined OSF1 || defined IRIX
2228     {
2229         char *waste = JS_smprintf("%s=%s", name, value);
2230         if (!waste) {
2231             JS_ReportOutOfMemory(cx);
2232             return JS_FALSE;
2233         }
2234         rv = putenv(waste);
2235 #ifdef XP_WIN
2236         /*
2237          * HPUX9 at least still has the bad old non-copying putenv.
2238          *
2239          * Per mail from <s.shanmuganathan@digital.com>, OSF1 also has a putenv
2240          * that will crash if you pass it an auto char array (so it must place
2241          * its argument directly in the char *environ[] array).
2242          */
2243         free(waste);
2244 #endif
2245     }
2246 #else
2247     rv = setenv(name, value, 1);
2248 #endif
2249     if (rv < 0) {
2250         JS_ReportError(cx, "can't set envariable %s to %s", name, value);
2251         return JS_FALSE;
2252     }
2253     *vp = STRING_TO_JSVAL(valstr);
2254 #endif /* !defined XP_BEOS && !defined XP_OS2 && !defined SOLARIS */
2255     return JS_TRUE;
2258 static JSBool
2259 env_enumerate(JSContext *cx, JSObject *obj)
2261     static JSBool reflected;
2262     char **evp, *name, *value;
2263     JSString *valstr;
2264     JSBool ok;
2266     if (reflected)
2267         return JS_TRUE;
2269     for (evp = (char **)JS_GetPrivate(cx, obj); (name = *evp) != NULL; evp++) {
2270         value = strchr(name, '=');
2271         if (!value)
2272             continue;
2273         *value++ = '\0';
2274         valstr = JS_NewStringCopyZ(cx, value);
2275         if (!valstr) {
2276             ok = JS_FALSE;
2277         } else {
2278             ok = JS_DefineProperty(cx, obj, name, STRING_TO_JSVAL(valstr),
2279                                    NULL, NULL, JSPROP_ENUMERATE);
2280         }
2281         value[-1] = '=';
2282         if (!ok)
2283             return JS_FALSE;
2284     }
2286     reflected = JS_TRUE;
2287     return JS_TRUE;
2290 static JSBool
2291 env_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
2292             JSObject **objp)
2294     JSString *idstr, *valstr;
2295     const char *name, *value;
2297     if (flags & JSRESOLVE_ASSIGNING)
2298         return JS_TRUE;
2300     idstr = JS_ValueToString(cx, id);
2301     if (!idstr)
2302         return JS_FALSE;
2303     name = JS_GetStringBytes(idstr);
2304     value = getenv(name);
2305     if (value) {
2306         valstr = JS_NewStringCopyZ(cx, value);
2307         if (!valstr)
2308             return JS_FALSE;
2309         if (!JS_DefineProperty(cx, obj, name, STRING_TO_JSVAL(valstr),
2310                                NULL, NULL, JSPROP_ENUMERATE)) {
2311             return JS_FALSE;
2312         }
2313         *objp = obj;
2314     }
2315     return JS_TRUE;
2318 static JSClass env_class = {
2319     "environment", JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE,
2320     JS_PropertyStub,  JS_PropertyStub,
2321     JS_PropertyStub,  env_setProperty,
2322     env_enumerate, (JSResolveOp) env_resolve,
2323     JS_ConvertStub,   JS_FinalizeStub
2324 };
2326 #ifdef NARCISSUS
2328 static JSBool
2329 defineProperty(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
2330                jsval *rval)
2332     JSString *str;
2333     jsval value;
2334     JSBool dontDelete, readOnly, dontEnum;
2335     const jschar *chars;
2336     size_t length;
2337     uintN attrs;
2339     dontDelete = readOnly = dontEnum = JS_FALSE;
2340     if (!JS_ConvertArguments(cx, argc, argv, "Sv/bbb",
2341                              &str, &value, &dontDelete, &readOnly, &dontEnum)) {
2342         return JS_FALSE;
2343     }
2344     chars = JS_GetStringChars(str);
2345     length = JS_GetStringLength(str);
2346     attrs = dontEnum ? 0 : JSPROP_ENUMERATE;
2347     if (dontDelete)
2348         attrs |= JSPROP_PERMANENT;
2349     if (readOnly)
2350         attrs |= JSPROP_READONLY;
2351     return JS_DefineUCProperty(cx, obj, chars, length, value, NULL, NULL,
2352                                attrs);
2355 static JSBool
2356 Evaluate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
2358     /* function evaluate(source, filename, lineno) { ... } */
2359     JSString *source;
2360     const char *filename = "";
2361     jsuint lineno = 0;
2362     uint32 oldopts;
2363     JSBool ok;
2365     if (argc == 0) {
2366         *rval = JSVAL_VOID;
2367         return JS_TRUE;
2368     }
2370     if (!JS_ConvertArguments(cx, argc, argv, "S/su",
2371                              &source, &filename, &lineno)) {
2372         return JS_FALSE;
2373     }
2375     oldopts = JS_GetOptions(cx);
2376     JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO);
2377     ok = JS_EvaluateUCScript(cx, obj, JS_GetStringChars(source),
2378                              JS_GetStringLength(source), filename,
2379                              lineno, rval);
2380     JS_SetOptions(cx, oldopts);
2381     
2382     return ok;
2385 #include <fcntl.h>
2386 #include <sys/stat.h>
2388 /*
2389  * Returns a JS_malloc'd string (that the caller needs to JS_free)
2390  * containing the directory (non-leaf) part of |from| prepended to |leaf|.
2391  * If |from| is empty or a leaf, MakeAbsolutePathname returns a copy of leaf.
2392  * Returns NULL to indicate an error.
2393  */
2394 static char *
2395 MakeAbsolutePathname(JSContext *cx, const char *from, const char *leaf)
2397     size_t dirlen;
2398     char *dir;
2399     const char *slash = NULL, *cp;
2401     cp = from;
2402     while (*cp) {
2403         if (*cp == '/'
2404 #ifdef XP_WIN
2405             || *cp == '\\'
2406 #endif
2407            ) {
2408             slash = cp;
2409         }
2411         ++cp;
2412     }
2414     if (!slash) {
2415         /* We were given a leaf or |from| was empty. */
2416         return JS_strdup(cx, leaf);
2417     }
2419     /* Else, we were given a real pathname, return that + the leaf. */
2420     dirlen = slash - from + 1;
2421     dir = JS_malloc(cx, dirlen + strlen(leaf) + 1);
2422     if (!dir)
2423         return NULL;
2425     strncpy(dir, from, dirlen);
2426     strcpy(dir + dirlen, leaf); /* Note: we can't use strcat here. */
2428     return dir;
2431 static JSBool
2432 snarf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
2434     JSString *str;
2435     const char *filename;
2436     char *pathname;
2437     JSStackFrame *fp;
2438     int fd, cc;
2439     JSBool ok;
2440     size_t len;
2441     char *buf;
2442     struct stat sb;
2444     str = JS_ValueToString(cx, argv[0]);
2445     if (!str)
2446         return JS_FALSE;
2447     filename = JS_GetStringBytes(str);
2448     
2449     /* Get the currently executing script's name. */
2450     fp = JS_GetScriptedCaller(cx, NULL);
2451     JS_ASSERT(fp && fp->script->filename);
2452     pathname = MakeAbsolutePathname(cx, fp->script->filename, filename);
2453     if (!pathname)
2454         return JS_FALSE;
2456     fd = open(pathname, O_RDONLY);
2457     ok = JS_TRUE;
2458     len = 0;
2459     buf = NULL;
2460     if (fd < 0) {
2461         JS_ReportError(cx, "can't open %s: %s", pathname, strerror(errno));
2462         ok = JS_FALSE;
2463     } else if (fstat(fd, &sb) < 0) {
2464         JS_ReportError(cx, "can't stat %s", pathname);
2465         ok = JS_FALSE;
2466     } else {
2467         len = sb.st_size;
2468         buf = JS_malloc(cx, len + 1);
2469         if (!buf) {
2470             ok = JS_FALSE;
2471         } else if ((cc = read(fd, buf, len)) != len) {
2472             JS_free(cx, buf);
2473             JS_ReportError(cx, "can't read %s: %s", pathname,
2474                            (cc < 0) ? strerror(errno) : "short read");
2475             ok = JS_FALSE;
2476         }
2477     }
2478     close(fd);
2479     JS_free(cx, pathname);
2480     if (!ok)
2481         return ok;
2482     buf[len] = '\0';
2483     str = JS_NewString(cx, buf, len);
2484     if (!str) {
2485         JS_free(cx, buf);
2486         return JS_FALSE;
2487     }
2488     *rval = STRING_TO_JSVAL(str);
2489     return JS_TRUE;
2492 #endif /* NARCISSUS */
2494 int
2495 main(int argc, char **argv, char **envp)
2497     int stackDummy;
2498     JSRuntime *rt;
2499     JSContext *cx;
2500     JSObject *glob, *it, *envobj;
2501     int result;
2502 #ifdef LIVECONNECT
2503     JavaVM *java_vm = NULL;
2504 #endif
2505 #ifdef JSDEBUGGER_JAVA_UI
2506     JNIEnv *java_env;
2507 #endif
2509     gStackBase = (jsuword)&stackDummy;
2511 #ifdef XP_OS2
2512    /* these streams are normally line buffered on OS/2 and need a \n, *
2513     * so we need to unbuffer then to get a reasonable prompt          */
2514     setbuf(stdout,0);
2515     setbuf(stderr,0);
2516 #endif
2518     gErrFile = stderr;
2519     gOutFile = stdout;
2521     argc--;
2522     argv++;
2524     rt = JS_NewRuntime(64L * 1024L * 1024L);
2525     if (!rt)
2526         return 1;
2528     cx = JS_NewContext(rt, gStackChunkSize);
2529     if (!cx)
2530         return 1;
2531     JS_SetErrorReporter(cx, my_ErrorReporter);
2533     glob = JS_NewObject(cx, &global_class, NULL, NULL);
2534     if (!glob)
2535         return 1;
2536 #ifdef LAZY_STANDARD_CLASSES
2537     JS_SetGlobalObject(cx, glob);
2538 #else
2539     if (!JS_InitStandardClasses(cx, glob))
2540         return 1;
2541 #endif
2542     if (!JS_DefineFunctions(cx, glob, shell_functions))
2543         return 1;
2545     it = JS_DefineObject(cx, glob, "it", &its_class, NULL, 0);
2546     if (!it)
2547         return 1;
2548     if (!JS_DefineProperties(cx, it, its_props))
2549         return 1;
2550     if (!JS_DefineFunctions(cx, it, its_methods))
2551         return 1;
2553 #ifdef PERLCONNECT
2554     if (!JS_InitPerlClass(cx, glob))
2555         return 1;
2556 #endif
2558 #ifdef JSDEBUGGER
2559     /*
2560     * XXX A command line option to enable debugging (or not) would be good
2561     */
2562     _jsdc = JSD_DebuggerOnForUser(rt, NULL, NULL);
2563     if (!_jsdc)
2564         return 1;
2565     JSD_JSContextInUse(_jsdc, cx);
2566 #ifdef JSD_LOWLEVEL_SOURCE
2567     JS_SetSourceHandler(rt, SendSourceToJSDebugger, _jsdc);
2568 #endif /* JSD_LOWLEVEL_SOURCE */
2569 #ifdef JSDEBUGGER_JAVA_UI
2570     _jsdjc = JSDJ_CreateContext();
2571     if (! _jsdjc)
2572         return 1;
2573     JSDJ_SetJSDContext(_jsdjc, _jsdc);
2574     java_env = JSDJ_CreateJavaVMAndStartDebugger(_jsdjc);
2575 #ifdef LIVECONNECT
2576     if (java_env)
2577         (*java_env)->GetJavaVM(java_env, &java_vm);
2578 #endif
2579     /*
2580     * XXX This would be the place to wait for the debugger to start.
2581     * Waiting would be nice in general, but especially when a js file
2582     * is passed on the cmd line.
2583     */
2584 #endif /* JSDEBUGGER_JAVA_UI */
2585 #ifdef JSDEBUGGER_C_UI
2586     JSDB_InitDebugger(rt, _jsdc, 0);
2587 #endif /* JSDEBUGGER_C_UI */
2588 #endif /* JSDEBUGGER */
2590 #ifdef LIVECONNECT
2591     if (!JSJ_SimpleInit(cx, glob, java_vm, getenv("CLASSPATH")))
2592         return 1;
2593 #endif
2595     envobj = JS_DefineObject(cx, glob, "environment", &env_class, NULL, 0);
2596     if (!envobj || !JS_SetPrivate(cx, envobj, envp))
2597         return 1;
2599 #ifdef NARCISSUS
2600     {
2601         jsval v;
2602         static const char Object_prototype[] = "Object.prototype";
2604         if (!JS_DefineFunction(cx, glob, "snarf", snarf, 1, 0))
2605             return 1;
2606         if (!JS_DefineFunction(cx, glob, "evaluate", Evaluate, 3, 0))
2607             return 1;
2609         if (!JS_EvaluateScript(cx, glob,
2610                                Object_prototype, sizeof Object_prototype - 1,
2611                                NULL, 0, &v)) {
2612             return 1;
2613         }
2614         if (!JS_DefineFunction(cx, JSVAL_TO_OBJECT(v), "__defineProperty__",
2615                                defineProperty, 5, 0)) {
2616             return 1;
2617         }
2618     }
2619 #endif
2621     result = ProcessArgs(cx, glob, argv, argc);
2623 #ifdef JSDEBUGGER
2624     if (_jsdc)
2625         JSD_DebuggerOff(_jsdc);
2626 #endif  /* JSDEBUGGER */
2628     JS_DestroyContext(cx);
2629     JS_DestroyRuntime(rt);
2630     JS_ShutDown();
2631     return result;