Code

fix include paths
[inkscape.git] / src / dom / js / js.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  *
3  * ***** BEGIN LICENSE BLOCK *****
4  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5  *
6  * The contents of this file are subject to the Mozilla Public License Version
7  * 1.1 (the "License"); you may not use this file except in compliance with
8  * the License. You may obtain a copy of the License at
9  * http://www.mozilla.org/MPL/
10  *
11  * Software distributed under the License is distributed on an "AS IS" basis,
12  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13  * for the specific language governing rights and limitations under the
14  * License.
15  *
16  * The Original Code is Mozilla Communicator client code, released
17  * March 31, 1998.
18  *
19  * The Initial Developer of the Original Code is
20  * Netscape Communications Corporation.
21  * Portions created by the Initial Developer are Copyright (C) 1998
22  * the Initial Developer. All Rights Reserved.
23  *
24  * Contributor(s):
25  *
26  * Alternatively, the contents of this file may be used under the terms of
27  * either of the GNU General Public License Version 2 or later (the "GPL"),
28  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29  * in which case the provisions of the GPL or the LGPL are applicable instead
30  * of those above. If you wish to allow use of your version of this file only
31  * under the terms of either the GPL or the LGPL, and not to allow others to
32  * use your version of this file under the terms of the MPL, indicate your
33  * decision by deleting the provisions above and replace them with the notice
34  * and other provisions required by the GPL or the LGPL. If you do not delete
35  * the provisions above, a recipient may use your version of this file under
36  * the terms of any one of the MPL, the GPL or the LGPL.
37  *
38  * ***** END LICENSE BLOCK ***** */
40 /*
41  * JS shell.
42  */
43 #include "jsstddef.h"
44 #include <errno.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include "jstypes.h"
49 #include "jsarena.h"
50 #include "jsutil.h"
51 #include "jsprf.h"
52 #include "jsapi.h"
53 #include "jsatom.h"
54 #include "jscntxt.h"
55 #include "jsdbgapi.h"
56 #include "jsemit.h"
57 #include "jsfun.h"
58 #include "jsgc.h"
59 #include "jslock.h"
60 #include "jsobj.h"
61 #include "jsparse.h"
62 #include "jsscope.h"
63 #include "jsscript.h"
65 #ifdef PERLCONNECT
66 #include "perlconnect/jsperl.h"
67 #endif
69 #ifdef LIVECONNECT
70 #include "jsjava.h"
71 #endif
73 #ifdef JSDEBUGGER
74 #include "jsdebug.h"
75 #ifdef JSDEBUGGER_JAVA_UI
76 #include "jsdjava.h"
77 #endif /* JSDEBUGGER_JAVA_UI */
78 #ifdef JSDEBUGGER_C_UI
79 #include "jsdb.h"
80 #endif /* JSDEBUGGER_C_UI */
81 #endif /* JSDEBUGGER */
83 #ifdef XP_UNIX
84 #include <unistd.h>
85 #include <sys/types.h>
86 #include <sys/wait.h>
87 #endif
89 #if defined(XP_WIN) || defined(XP_OS2)
90 #include <io.h>     /* for isatty() */
91 #endif
93 #define EXITCODE_RUNTIME_ERROR 3
94 #define EXITCODE_FILE_NOT_FOUND 4
96 size_t gStackChunkSize = 8192;
97 static size_t gMaxStackSize = 0;
98 static jsuword gStackBase;
99 int gExitCode = 0;
100 JSBool gQuitting = JS_FALSE;
101 FILE *gErrFile = NULL;
102 FILE *gOutFile = NULL;
104 #ifdef XP_MAC
105 #if defined(MAC_TEST_HACK) || defined(XP_MAC_MPW)
106 /* this is the data file that all Print strings will be echoed into */
107 FILE *gTestResultFile = NULL;
108 #define isatty(f) 0
109 #else
110 #define isatty(f) 1
111 #endif
113 char *strdup(const char *str)
115     char *copy = (char *) malloc(strlen(str)+1);
116     if (copy)
117         strcpy(copy, str);
118     return copy;
121 #ifdef XP_MAC_MPW
122 /* Macintosh MPW replacements for the ANSI routines.  These translate LF's to CR's because
123    the MPW libraries supplied by Metrowerks don't do that for some reason.  */
124 static void translateLFtoCR(char *str, int length)
126     char *limit = str + length;
127     while (str != limit) {
128         if (*str == '\n')
129             *str = '\r';
130         str++;
131     }
134 int fputc(int c, FILE *file)
136     char buffer = c;
137     if (buffer == '\n')
138         buffer = '\r';
139     return fwrite(&buffer, 1, 1, file);
142 int fputs(const char *s, FILE *file)
144     char buffer[4096];
145     int n = strlen(s);
146     int extra = 0;
148     while (n > sizeof buffer) {
149         memcpy(buffer, s, sizeof buffer);
150         translateLFtoCR(buffer, sizeof buffer);
151         extra += fwrite(buffer, 1, sizeof buffer, file);
152         n -= sizeof buffer;
153         s += sizeof buffer;
154     }
155     memcpy(buffer, s, n);
156     translateLFtoCR(buffer, n);
157     return extra + fwrite(buffer, 1, n, file);
160 int fprintf(FILE* file, const char *format, ...)
162     va_list args;
163     char smallBuffer[4096];
164     int n;
165     int bufferSize = sizeof smallBuffer;
166     char *buffer = smallBuffer;
167     int result;
169     va_start(args, format);
170     n = vsnprintf(buffer, bufferSize, format, args);
171     va_end(args);
172     while (n < 0) {
173         if (buffer != smallBuffer)
174             free(buffer);
175         bufferSize <<= 1;
176         buffer = malloc(bufferSize);
177         if (!buffer) {
178             JS_ASSERT(JS_FALSE);
179             return 0;
180         }
181         va_start(args, format);
182         n = vsnprintf(buffer, bufferSize, format, args);
183         va_end(args);
184     }
185     translateLFtoCR(buffer, n);
186     result = fwrite(buffer, 1, n, file);
187     if (buffer != smallBuffer)
188         free(buffer);
189     return result;
193 #else
194 #include <SIOUX.h>
195 #include <MacTypes.h>
197 static char* mac_argv[] = { "js", NULL };
199 static void initConsole(StringPtr consoleName, const char* startupMessage, int *argc, char** *argv)
201     SIOUXSettings.autocloseonquit = true;
202     SIOUXSettings.asktosaveonclose = false;
203     /* SIOUXSettings.initializeTB = false;
204      SIOUXSettings.showstatusline = true;*/
205     puts(startupMessage);
206     SIOUXSetTitle(consoleName);
208     /* set up a buffer for stderr (otherwise it's a pig). */
209     setvbuf(stderr, (char *) malloc(BUFSIZ), _IOLBF, BUFSIZ);
211     *argc = 1;
212     *argv = mac_argv;
215 #ifdef LIVECONNECT
216 /* Little hack to provide a default CLASSPATH on the Mac. */
217 #define getenv(var) mac_getenv(var)
218 static char* mac_getenv(const char* var)
220     if (strcmp(var, "CLASSPATH") == 0) {
221         static char class_path[] = "liveconnect.jar";
222         return class_path;
223     }
224     return NULL;
226 #endif /* LIVECONNECT */
228 #endif
229 #endif
231 #ifdef JSDEBUGGER
232 static JSDContext *_jsdc;
233 #ifdef JSDEBUGGER_JAVA_UI
234 static JSDJContext *_jsdjc;
235 #endif /* JSDEBUGGER_JAVA_UI */
236 #endif /* JSDEBUGGER */
238 static JSBool reportWarnings = JS_TRUE;
240 typedef enum JSShellErrNum {
241 #define MSG_DEF(name, number, count, exception, format) \
242     name = number,
243 #include "jsshell.msg"
244 #undef MSG_DEF
245     JSShellErr_Limit
246 #undef MSGDEF
247 } JSShellErrNum;
249 static const JSErrorFormatString *
250 my_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber);
252 #ifdef EDITLINE
253 extern char     *readline(const char *prompt);
254 extern void     add_history(char *line);
255 #endif
257 static JSBool
258 GetLine(JSContext *cx, char *bufp, FILE *file, const char *prompt) {
259 #ifdef EDITLINE
260     /*
261      * Use readline only if file is stdin, because there's no way to specify
262      * another handle.  Are other filehandles interactive?
263      */
264     if (file == stdin) {
265         char *linep = readline(prompt);
266         if (!linep)
267             return JS_FALSE;
268         if (linep[0] != '\0')
269             add_history(linep);
270         strcpy(bufp, linep);
271         JS_free(cx, linep);
272         bufp += strlen(bufp);
273         *bufp++ = '\n';
274         *bufp = '\0';
275     } else
276 #endif
277     {
278         char line[256];
279         fprintf(gOutFile, prompt);
280         fflush(gOutFile);
281 #ifdef XP_MAC_MPW
282         /* Print a CR after the prompt because MPW grabs the entire line when entering an interactive command */
283         fputc('\n', gOutFile);
284 #endif
285         if (!fgets(line, sizeof line, file))
286             return JS_FALSE;
287         strcpy(bufp, line);
288     }
289     return JS_TRUE;
292 static void
293 Process(JSContext *cx, JSObject *obj, char *filename)
295     JSBool ok, hitEOF;
296     JSScript *script;
297     jsval result;
298     JSString *str;
299     char buffer[4096];
300     char *bufp;
301     int lineno;
302     int startline;
303     FILE *file;
304     jsuword stackLimit;
306     if (!filename || strcmp(filename, "-") == 0) {
307         file = stdin;
308     } else {
309         file = fopen(filename, "r");
310         if (!file) {
311             JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
312                                  JSSMSG_CANT_OPEN, filename, strerror(errno));
313             gExitCode = EXITCODE_FILE_NOT_FOUND;
314             return;
315         }
316     }
318     if (gMaxStackSize == 0) {
319         /*
320          * Disable checking for stack overflow if limit is zero.
321          */
322         stackLimit = 0;
323     } else {
324 #if JS_STACK_GROWTH_DIRECTION > 0
325         stackLimit = gStackBase + gMaxStackSize;
326 #else
327         stackLimit = gStackBase - gMaxStackSize;
328 #endif
329     }
330     JS_SetThreadStackLimit(cx, stackLimit);
332     if (!isatty(fileno(file))) {
333         /*
334          * It's not interactive - just execute it.
335          *
336          * Support the UNIX #! shell hack; gobble the first line if it starts
337          * with '#'.  TODO - this isn't quite compatible with sharp variables,
338          * as a legal js program (using sharp variables) might start with '#'.
339          * But that would require multi-character lookahead.
340          */
341         int ch = fgetc(file);
342         if (ch == '#') {
343             while((ch = fgetc(file)) != EOF) {
344                 if (ch == '\n' || ch == '\r')
345                     break;
346             }
347         }
348         ungetc(ch, file);
349         script = JS_CompileFileHandle(cx, obj, filename, file);
350         if (script) {
351             (void)JS_ExecuteScript(cx, obj, script, &result);
352             JS_DestroyScript(cx, script);
353         }
354         return;
355     }
357     /* It's an interactive filehandle; drop into read-eval-print loop. */
358     lineno = 1;
359     hitEOF = JS_FALSE;
360     do {
361         bufp = buffer;
362         *bufp = '\0';
364         /*
365          * Accumulate lines until we get a 'compilable unit' - one that either
366          * generates an error (before running out of source) or that compiles
367          * cleanly.  This should be whenever we get a complete statement that
368          * coincides with the end of a line.
369          */
370         startline = lineno;
371         do {
372             if (!GetLine(cx, bufp, file, startline == lineno ? "js> " : "")) {
373                 hitEOF = JS_TRUE;
374                 break;
375             }
376             bufp += strlen(bufp);
377             lineno++;
378         } while (!JS_BufferIsCompilableUnit(cx, obj, buffer, strlen(buffer)));
380         /* Clear any pending exception from previous failed compiles.  */
381         JS_ClearPendingException(cx);
382         script = JS_CompileScript(cx, obj, buffer, strlen(buffer),
383 #ifdef JSDEBUGGER
384                                   "typein",
385 #else
386                                   NULL,
387 #endif
388                                   startline);
389         if (script) {
390             ok = JS_ExecuteScript(cx, obj, script, &result);
391             if (ok && result != JSVAL_VOID) {
392                 str = JS_ValueToString(cx, result);
393                 if (str)
394                     fprintf(gOutFile, "%s\n", JS_GetStringBytes(str));
395                 else
396                     ok = JS_FALSE;
397             }
398             JS_DestroyScript(cx, script);
399         }
400     } while (!hitEOF && !gQuitting);
401     fprintf(gOutFile, "\n");
402     return;
405 static int
406 usage(void)
408     fprintf(gErrFile, "%s\n", JS_GetImplementationVersion());
409     fprintf(gErrFile, "usage: js [-PswW] [-b branchlimit] [-c stackchunksize] [-v version] [-f scriptfile] [-S maxstacksize] [scriptfile] [scriptarg...]\n");
410     return 2;
413 static uint32 gBranchCount;
414 static uint32 gBranchLimit;
416 static JSBool
417 my_BranchCallback(JSContext *cx, JSScript *script)
419     if (++gBranchCount == gBranchLimit) {
420         if (script->filename)
421             fprintf(gErrFile, "%s:", script->filename);
422         fprintf(gErrFile, "%u: script branches too much (%u callbacks)\n",
423                 script->lineno, gBranchLimit);
424         gBranchCount = 0;
425         return JS_FALSE;
426     }
427     if ((gBranchCount & 0x3fff) == 1)
428         JS_MaybeGC(cx);
429     return JS_TRUE;
432 extern JSClass global_class;
434 static int
435 ProcessArgs(JSContext *cx, JSObject *obj, char **argv, int argc)
437     int i, j, length;
438     JSObject *argsObj;
439     char *filename = NULL;
440     JSBool isInteractive = JS_TRUE;
442     /*
443      * Scan past all optional arguments so we can create the arguments object
444      * before processing any -f options, which must interleave properly with
445      * -v and -w options.  This requires two passes, and without getopt, we'll
446      * have to keep the option logic here and in the second for loop in sync.
447      */
448     for (i = 0; i < argc; i++) {
449         if (argv[i][0] != '-' || argv[i][1] == '\0') {
450             ++i;
451             break;
452         }
453         switch (argv[i][1]) {
454           case 'b':
455           case 'c':
456           case 'f':
457           case 'v':
458           case 'S':
459             ++i;
460             break;
461         }
462     }
464     /*
465      * Create arguments early and define it to root it, so it's safe from any
466      * GC calls nested below, and so it is available to -f <file> arguments.
467      */
468     argsObj = JS_NewArrayObject(cx, 0, NULL);
469     if (!argsObj)
470         return 1;
471     if (!JS_DefineProperty(cx, obj, "arguments", OBJECT_TO_JSVAL(argsObj),
472                            NULL, NULL, 0)) {
473         return 1;
474     }
476     length = argc - i;
477     for (j = 0; j < length; j++) {
478         JSString *str = JS_NewStringCopyZ(cx, argv[i++]);
479         if (!str)
480             return 1;
481         if (!JS_DefineElement(cx, argsObj, j, STRING_TO_JSVAL(str),
482                               NULL, NULL, JSPROP_ENUMERATE)) {
483             return 1;
484         }
485     }
487     for (i = 0; i < argc; i++) {
488         if (argv[i][0] != '-' || argv[i][1] == '\0') {
489             filename = argv[i++];
490             isInteractive = JS_FALSE;
491             break;
492         }
494         switch (argv[i][1]) {
495         case 'v':
496             if (++i == argc) {
497                 return usage();
498             }
499             JS_SetVersion(cx, (JSVersion) atoi(argv[i]));
500             break;
502         case 'w':
503             reportWarnings = JS_TRUE;
504             break;
506         case 'W':
507             reportWarnings = JS_FALSE;
508             break;
510         case 's':
511             JS_ToggleOptions(cx, JSOPTION_STRICT);
512             break;
514         case 'P':
515             if (JS_GET_CLASS(cx, JS_GetPrototype(cx, obj)) != &global_class) {
516                 JSObject *gobj;
518                 if (!JS_SealObject(cx, obj, JS_TRUE))
519                     return JS_FALSE;
520                 gobj = JS_NewObject(cx, &global_class, NULL, NULL);
521                 if (!gobj)
522                     return JS_FALSE;
523                 if (!JS_SetPrototype(cx, gobj, obj))
524                     return JS_FALSE;
525                 JS_SetParent(cx, gobj, NULL);
526                 JS_SetGlobalObject(cx, gobj);
527                 obj = gobj;
528             }
529             break;
531         case 'b':
532             gBranchLimit = atoi(argv[++i]);
533             JS_SetBranchCallback(cx, my_BranchCallback);
534             break;
536         case 'c':
537             /* set stack chunk size */
538             gStackChunkSize = atoi(argv[++i]);
539             break;
541         case 'f':
542             if (++i == argc) {
543                 return usage();
544             }
545             Process(cx, obj, argv[i]);
546             /*
547              * XXX: js -f foo.js should interpret foo.js and then
548              * drop into interactive mode, but that breaks test
549              * harness. Just execute foo.js for now.
550              */
551             isInteractive = JS_FALSE;
552             break;
554         case 'S':
555             if (++i == argc) {
556                 return usage();
557             }
558             /* Set maximum stack size. */
559             gMaxStackSize = atoi(argv[i]);
560             break;
562         default:
563             return usage();
564         }
565     }
567     if (filename || isInteractive)
568         Process(cx, obj, filename);
569     return gExitCode;
573 static JSBool
574 Version(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
576     if (argc > 0 && JSVAL_IS_INT(argv[0]))
577         *rval = INT_TO_JSVAL(JS_SetVersion(cx, (JSVersion) JSVAL_TO_INT(argv[0])));
578     else
579         *rval = INT_TO_JSVAL(JS_GetVersion(cx));
580     return JS_TRUE;
583 static struct {
584     const char  *name;
585     uint32      flag;
586 } js_options[] = {
587     {"strict",          JSOPTION_STRICT},
588     {"werror",          JSOPTION_WERROR},
589     {"atline",          JSOPTION_ATLINE},
590     {0,                 0}
591 };
593 static JSBool
594 Options(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
596     uint32 optset, flag;
597     uintN i, j, found;
598     JSString *str;
599     const char *opt;
600     char *names;
602     optset = 0;
603     for (i = 0; i < argc; i++) {
604         str = JS_ValueToString(cx, argv[i]);
605         if (!str)
606             return JS_FALSE;
607         opt = JS_GetStringBytes(str);
608         for (j = 0; js_options[j].name; j++) {
609             if (strcmp(js_options[j].name, opt) == 0) {
610                 optset |= js_options[j].flag;
611                 break;
612             }
613         }
614     }
615     optset = JS_ToggleOptions(cx, optset);
617     names = NULL;
618     found = 0;
619     while (optset != 0) {
620         flag = optset;
621         optset &= optset - 1;
622         flag &= ~optset;
623         for (j = 0; js_options[j].name; j++) {
624             if (js_options[j].flag == flag) {
625                 names = JS_sprintf_append(names, "%s%s",
626                                           names ? "," : "", js_options[j].name);
627                 found++;
628                 break;
629             }
630         }
631     }
632     if (!found)
633         names = strdup("");
634     if (!names) {
635         JS_ReportOutOfMemory(cx);
636         return JS_FALSE;
637     }
639     str = JS_NewString(cx, names, strlen(names));
640     if (!str) {
641         free(names);
642         return JS_FALSE;
643     }
644     *rval = STRING_TO_JSVAL(str);
645     return JS_TRUE;
648 static void
649 my_LoadErrorReporter(JSContext *cx, const char *message, JSErrorReport *report);
651 static void
652 my_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report);
654 static JSBool
655 Load(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
657     uintN i;
658     JSString *str;
659     const char *filename;
660     JSScript *script;
661     JSBool ok;
662     jsval result;
663     JSErrorReporter older;
664     uint32 oldopts;
666     for (i = 0; i < argc; i++) {
667         str = JS_ValueToString(cx, argv[i]);
668         if (!str)
669             return JS_FALSE;
670         argv[i] = STRING_TO_JSVAL(str);
671         filename = JS_GetStringBytes(str);
672         errno = 0;
673         older = JS_SetErrorReporter(cx, my_LoadErrorReporter);
674         oldopts = JS_GetOptions(cx);
675         JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO);
676         script = JS_CompileFile(cx, obj, filename);
677         if (!script) {
678             ok = JS_FALSE;
679         } else {
680             ok = JS_ExecuteScript(cx, obj, script, &result);
681             JS_DestroyScript(cx, script);
682         }
683         JS_SetOptions(cx, oldopts);
684         JS_SetErrorReporter(cx, older);
685         if (!ok)
686             return JS_FALSE;
687     }
689     return JS_TRUE;
692 static JSBool
693 Print(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
695     uintN i, n;
696     JSString *str;
698     for (i = n = 0; i < argc; i++) {
699         str = JS_ValueToString(cx, argv[i]);
700         if (!str)
701             return JS_FALSE;
702         fprintf(gOutFile, "%s%s", i ? " " : "", JS_GetStringBytes(str));
703     }
704     n++;
705     if (n)
706         fputc('\n', gOutFile);
707     return JS_TRUE;
710 static JSBool
711 Help(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
713 static JSBool
714 Quit(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
716 #ifdef LIVECONNECT
717     JSJ_SimpleShutdown();
718 #endif
720     JS_ConvertArguments(cx, argc, argv,"/ i", &gExitCode);
722     gQuitting = JS_TRUE;
723     return JS_FALSE;
726 #ifdef GC_MARK_DEBUG
727 extern JS_FRIEND_DATA(FILE *) js_DumpGCHeap;
728 #endif
730 static JSBool
731 GC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
733     JSRuntime *rt;
734     uint32 preBytes;
736     rt = cx->runtime;
737     preBytes = rt->gcBytes;
738 #ifdef GC_MARK_DEBUG
739     if (argc && JSVAL_IS_STRING(argv[0])) {
740         char *name = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
741         FILE *file = fopen(name, "w");
742         if (!file) {
743             fprintf(gErrFile, "gc: can't open %s: %s\n", strerror(errno));
744             return JS_FALSE;
745         }
746         js_DumpGCHeap = file;
747     } else {
748         js_DumpGCHeap = stdout;
749     }
750 #endif
751     JS_GC(cx);
752 #ifdef GC_MARK_DEBUG
753     if (js_DumpGCHeap != stdout)
754         fclose(js_DumpGCHeap);
755     js_DumpGCHeap = NULL;
756 #endif
757     fprintf(gOutFile, "before %lu, after %lu, break %08lx\n",
758             (unsigned long)preBytes, (unsigned long)rt->gcBytes,
759 #ifdef XP_UNIX
760             (unsigned long)sbrk(0)
761 #else
762             0
763 #endif
764             );
765 #ifdef JS_GCMETER
766     js_DumpGCStats(rt, stdout);
767 #endif
768     return JS_TRUE;
771 static JSScript *
772 ValueToScript(JSContext *cx, jsval v)
774     JSScript *script;
775     JSFunction *fun;
777     if (JSVAL_IS_OBJECT(v) &&
778         JS_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_ScriptClass) {
779         script = (JSScript *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v));
780     } else {
781         fun = JS_ValueToFunction(cx, v);
782         if (!fun)
783             return NULL;
784         script = FUN_SCRIPT(fun);
785     }
786     return script;
789 static JSBool
790 GetTrapArgs(JSContext *cx, uintN argc, jsval *argv, JSScript **scriptp,
791             int32 *ip)
793     uintN intarg;
794     JSScript *script;
796     *scriptp = cx->fp->down->script;
797     *ip = 0;
798     if (argc != 0) {
799         intarg = 0;
800         if (JS_TypeOfValue(cx, argv[0]) == JSTYPE_FUNCTION) {
801             script = ValueToScript(cx, argv[0]);
802             if (!script)
803                 return JS_FALSE;
804             *scriptp = script;
805             intarg++;
806         }
807         if (argc > intarg) {
808             if (!JS_ValueToInt32(cx, argv[intarg], ip))
809                 return JS_FALSE;
810         }
811     }
812     return JS_TRUE;
815 static JSTrapStatus
816 TrapHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval,
817             void *closure)
819     JSString *str;
820     JSStackFrame *caller;
822     str = (JSString *) closure;
823     caller = JS_GetScriptedCaller(cx, NULL);
824     if (!JS_EvaluateScript(cx, caller->scopeChain,
825                            JS_GetStringBytes(str), JS_GetStringLength(str),
826                            caller->script->filename, caller->script->lineno,
827                            rval)) {
828         return JSTRAP_ERROR;
829     }
830     if (*rval != JSVAL_VOID)
831         return JSTRAP_RETURN;
832     return JSTRAP_CONTINUE;
835 static JSBool
836 Trap(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
838     JSString *str;
839     JSScript *script;
840     int32 i;
842     if (argc == 0) {
843         JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_TRAP_USAGE);
844         return JS_FALSE;
845     }
846     argc--;
847     str = JS_ValueToString(cx, argv[argc]);
848     if (!str)
849         return JS_FALSE;
850     argv[argc] = STRING_TO_JSVAL(str);
851     if (!GetTrapArgs(cx, argc, argv, &script, &i))
852         return JS_FALSE;
853     return JS_SetTrap(cx, script, script->code + i, TrapHandler, str);
856 static JSBool
857 Untrap(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
859     JSScript *script;
860     int32 i;
862     if (!GetTrapArgs(cx, argc, argv, &script, &i))
863         return JS_FALSE;
864     JS_ClearTrap(cx, script, script->code + i, NULL, NULL);
865     return JS_TRUE;
868 static JSBool
869 LineToPC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
871     JSScript *script;
872     int32 i;
873     uintN lineno;
874     jsbytecode *pc;
876     if (argc == 0) {
877         JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_LINE2PC_USAGE);
878         return JS_FALSE;
879     }
880     script = cx->fp->down->script;
881     if (!GetTrapArgs(cx, argc, argv, &script, &i))
882         return JS_FALSE;
883     lineno = (i == 0) ? script->lineno : (uintN)i;
884     pc = JS_LineNumberToPC(cx, script, lineno);
885     if (!pc)
886         return JS_FALSE;
887     *rval = INT_TO_JSVAL(PTRDIFF(pc, script->code, jsbytecode));
888     return JS_TRUE;
891 static JSBool
892 PCToLine(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
894     JSScript *script;
895     int32 i;
896     uintN lineno;
898     if (!GetTrapArgs(cx, argc, argv, &script, &i))
899         return JS_FALSE;
900     lineno = JS_PCToLineNumber(cx, script, script->code + i);
901     if (!lineno)
902         return JS_FALSE;
903     *rval = INT_TO_JSVAL(lineno);
904     return JS_TRUE;
907 #ifdef DEBUG
909 static void
910 SrcNotes(JSContext *cx, JSScript *script)
912     uintN offset, delta, caseOff;
913     jssrcnote *notes, *sn;
914     JSSrcNoteType type;
915     jsatomid atomIndex;
916     JSAtom *atom;
918     fprintf(gOutFile, "\nSource notes:\n");
919     offset = 0;
920     notes = SCRIPT_NOTES(script);
921     for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
922         delta = SN_DELTA(sn);
923         offset += delta;
924         fprintf(gOutFile, "%3u: %5u [%4u] %-8s",
925                 PTRDIFF(sn, notes, jssrcnote), offset, delta,
926                 js_SrcNoteSpec[SN_TYPE(sn)].name);
927         type = (JSSrcNoteType) SN_TYPE(sn);
928         switch (type) {
929           case SRC_SETLINE:
930             fprintf(gOutFile, " lineno %u", (uintN) js_GetSrcNoteOffset(sn, 0));
931             break;
932           case SRC_FOR:
933             fprintf(gOutFile, " cond %u update %u tail %u",
934                    (uintN) js_GetSrcNoteOffset(sn, 0),
935                    (uintN) js_GetSrcNoteOffset(sn, 1),
936                    (uintN) js_GetSrcNoteOffset(sn, 2));
937             break;
938           case SRC_COND:
939           case SRC_IF_ELSE:
940           case SRC_WHILE:
941           case SRC_PCBASE:
942           case SRC_PCDELTA:
943             fprintf(gOutFile, " offset %u", (uintN) js_GetSrcNoteOffset(sn, 0));
944             break;
945           case SRC_LABEL:
946           case SRC_LABELBRACE:
947           case SRC_BREAK2LABEL:
948           case SRC_CONT2LABEL:
949           case SRC_FUNCDEF: {
950             const char *bytes;
951             JSFunction *fun;
952             JSString *str;
954             atomIndex = (jsatomid) js_GetSrcNoteOffset(sn, 0);
955             atom = js_GetAtom(cx, &script->atomMap, atomIndex);
956             if (type != SRC_FUNCDEF) {
957                 bytes = js_AtomToPrintableString(cx, atom);
958             } else {
959                 fun = (JSFunction *)
960                     JS_GetPrivate(cx, ATOM_TO_OBJECT(atom));
961                 str = JS_DecompileFunction(cx, fun, JS_DONT_PRETTY_PRINT);
962                 bytes = str ? JS_GetStringBytes(str) : "N/A";
963             }
964             fprintf(gOutFile, " atom %u (%s)", (uintN)atomIndex, bytes);
965             break;
966           }
967           case SRC_SWITCH:
968             fprintf(gOutFile, " length %u", (uintN) js_GetSrcNoteOffset(sn, 0));
969             caseOff = (uintN) js_GetSrcNoteOffset(sn, 1);
970             if (caseOff)
971                 fprintf(gOutFile, " first case offset %u", caseOff);
972             break;
973           case SRC_CATCH:
974             delta = (uintN) js_GetSrcNoteOffset(sn, 0);
975             if (delta)
976                 fprintf(gOutFile, " guard size %u", delta);
977             break;
978           default:;
979         }
980         fputc('\n', gOutFile);
981     }
984 static JSBool
985 Notes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
987     uintN i;
988     JSScript *script;
990     for (i = 0; i < argc; i++) {
991         script = ValueToScript(cx, argv[i]);
992         if (!script)
993             continue;
995         SrcNotes(cx, script);
996     }
997     return JS_TRUE;
1000 static JSBool
1001 TryNotes(JSContext *cx, JSScript *script)
1003     JSTryNote *tn = script->trynotes;
1005     if (!tn)
1006         return JS_TRUE;
1007     fprintf(gOutFile, "\nException table:\nstart\tend\tcatch\n");
1008     while (tn->start && tn->catchStart) {
1009         fprintf(gOutFile, "  %d\t%d\t%d\n",
1010                tn->start, tn->start + tn->length, tn->catchStart);
1011         tn++;
1012     }
1013     return JS_TRUE;
1016 static JSBool
1017 Disassemble(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1019     JSBool lines;
1020     uintN i;
1021     JSScript *script;
1023     if (argc > 0 &&
1024         JSVAL_IS_STRING(argv[0]) &&
1025         !strcmp(JS_GetStringBytes(JSVAL_TO_STRING(argv[0])), "-l")) {
1026         lines = JS_TRUE;
1027         argv++, argc--;
1028     } else {
1029         lines = JS_FALSE;
1030     }
1031     for (i = 0; i < argc; i++) {
1032         script = ValueToScript(cx, argv[i]);
1033         if (!script)
1034             continue;
1036         if (JSVAL_IS_FUNCTION(cx, argv[i])) {
1037             JSFunction *fun = JS_ValueToFunction(cx, argv[i]);
1038             if (fun && (fun->flags & JSFUN_FLAGS_MASK)) {
1039                 uint8 flags = fun->flags;
1040                 fputs("flags:", stdout);
1042 #define SHOW_FLAG(flag) if (flags & JSFUN_##flag) fputs(" " #flag, stdout);
1044                 SHOW_FLAG(LAMBDA);
1045                 SHOW_FLAG(SETTER);
1046                 SHOW_FLAG(GETTER);
1047                 SHOW_FLAG(BOUND_METHOD);
1048                 SHOW_FLAG(HEAVYWEIGHT);
1050 #undef SHOW_FLAG
1051                 putchar('\n');
1052             }
1053         }
1055         js_Disassemble(cx, script, lines, stdout);
1056         SrcNotes(cx, script);
1057         TryNotes(cx, script);
1058     }
1059     return JS_TRUE;
1062 static JSBool
1063 DisassWithSrc(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1064               jsval *rval)
1066 #define LINE_BUF_LEN 512
1067     uintN i, len, line1, line2, bupline;
1068     JSScript *script;
1069     FILE *file;
1070     char linebuf[LINE_BUF_LEN];
1071     jsbytecode *pc, *end;
1072     static char sep[] = ";-------------------------";
1074     for (i = 0; i < argc; i++) {
1075         script = ValueToScript(cx, argv[i]);
1076         if (!script)
1077             continue;
1079         if (!script || !script->filename) {
1080             JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
1081                                             JSSMSG_FILE_SCRIPTS_ONLY);
1082             return JS_FALSE;
1083         }
1085         file = fopen(script->filename, "r");
1086         if (!file) {
1087             JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
1088                             JSSMSG_CANT_OPEN,
1089                             script->filename, strerror(errno));
1090             return JS_FALSE;
1091         }
1093         pc = script->code;
1094         end = pc + script->length;
1096         /* burn the leading lines */
1097         line2 = JS_PCToLineNumber(cx, script, pc);
1098         for (line1 = 0; line1 < line2 - 1; line1++)
1099             fgets(linebuf, LINE_BUF_LEN, file);
1101         bupline = 0;
1102         while (pc < end) {
1103             line2 = JS_PCToLineNumber(cx, script, pc);
1105             if (line2 < line1) {
1106                 if (bupline != line2) {
1107                     bupline = line2;
1108                     fprintf(gOutFile, "%s %3u: BACKUP\n", sep, line2);
1109                 }
1110             } else {
1111                 if (bupline && line1 == line2)
1112                     fprintf(gOutFile, "%s %3u: RESTORE\n", sep, line2);
1113                 bupline = 0;
1114                 while (line1 < line2) {
1115                     if (!fgets(linebuf, LINE_BUF_LEN, file)) {
1116                         JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
1117                                        JSSMSG_UNEXPECTED_EOF,
1118                                        script->filename);
1119                         goto bail;
1120                     }
1121                     line1++;
1122                     fprintf(gOutFile, "%s %3u: %s", sep, line1, linebuf);
1123                 }
1124             }
1126             len = js_Disassemble1(cx, script, pc,
1127                                   PTRDIFF(pc, script->code, jsbytecode),
1128                                   JS_TRUE, stdout);
1129             if (!len)
1130                 return JS_FALSE;
1131             pc += len;
1132         }
1134       bail:
1135         fclose(file);
1136     }
1137     return JS_TRUE;
1138 #undef LINE_BUF_LEN
1141 static JSBool
1142 Tracing(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1144     JSBool bval;
1145     JSString *str;
1147     if (argc == 0) {
1148         *rval = BOOLEAN_TO_JSVAL(cx->tracefp != 0);
1149         return JS_TRUE;
1150     }
1152     switch (JS_TypeOfValue(cx, argv[0])) {
1153       case JSTYPE_NUMBER:
1154         bval = JSVAL_IS_INT(argv[0])
1155                ? JSVAL_TO_INT(argv[0])
1156                : (jsint) *JSVAL_TO_DOUBLE(argv[0]);
1157         break;
1158       case JSTYPE_BOOLEAN:
1159         bval = JSVAL_TO_BOOLEAN(argv[0]);
1160         break;
1161       default:
1162         str = JS_ValueToString(cx, argv[0]);
1163         if (!str)
1164             return JS_FALSE;
1165         fprintf(gErrFile, "tracing: illegal argument %s\n",
1166                 JS_GetStringBytes(str));
1167         return JS_TRUE;
1168     }
1169     cx->tracefp = bval ? stderr : NULL;
1170     return JS_TRUE;
1173 typedef struct DumpAtomArgs {
1174     JSContext   *cx;
1175     FILE        *fp;
1176 } DumpAtomArgs;
1178 static int
1179 DumpAtom(JSHashEntry *he, int i, void *arg)
1181     DumpAtomArgs *args = (DumpAtomArgs *)arg;
1182     FILE *fp = args->fp;
1183     JSAtom *atom = (JSAtom *)he;
1185     fprintf(fp, "%3d %08x %5lu ",
1186             i, (uintN)he->keyHash, (unsigned long)atom->number);
1187     if (ATOM_IS_STRING(atom))
1188         fprintf(fp, "\"%s\"\n", js_AtomToPrintableString(args->cx, atom));
1189     else if (ATOM_IS_INT(atom))
1190         fprintf(fp, "%ld\n", (long)ATOM_TO_INT(atom));
1191     else
1192         fprintf(fp, "%.16g\n", *ATOM_TO_DOUBLE(atom));
1193     return HT_ENUMERATE_NEXT;
1196 static void
1197 DumpScope(JSContext *cx, JSObject *obj, FILE *fp)
1199     uintN i;
1200     JSScope *scope;
1201     JSScopeProperty *sprop;
1203     i = 0;
1204     scope = OBJ_SCOPE(obj);
1205     for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) {
1206         if (SCOPE_HAD_MIDDLE_DELETE(scope) && !SCOPE_HAS_PROPERTY(scope, sprop))
1207             continue;
1208         fprintf(fp, "%3u %p", i, sprop);
1209         if (JSVAL_IS_INT(sprop->id)) {
1210             fprintf(fp, " [%ld]", (long)JSVAL_TO_INT(sprop->id));
1211         } else {
1212             fprintf(fp, " \"%s\"",
1213                     js_AtomToPrintableString(cx, (JSAtom *)sprop->id));
1214         }
1216 #define DUMP_ATTR(name) if (sprop->attrs & JSPROP_##name) fputs(" " #name, fp)
1217         DUMP_ATTR(ENUMERATE);
1218         DUMP_ATTR(READONLY);
1219         DUMP_ATTR(PERMANENT);
1220         DUMP_ATTR(EXPORTED);
1221         DUMP_ATTR(GETTER);
1222         DUMP_ATTR(SETTER);
1223 #undef  DUMP_ATTR
1225         fprintf(fp, " slot %lu flags %x shortid %d\n",
1226                 sprop->slot, sprop->flags, sprop->shortid);
1227     }
1230 static JSBool
1231 DumpStats(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1233     uintN i;
1234     JSString *str;
1235     const char *bytes;
1236     JSAtom *atom;
1237     JSObject *obj2;
1238     JSProperty *prop;
1239     jsval value;
1241     for (i = 0; i < argc; i++) {
1242         str = JS_ValueToString(cx, argv[i]);
1243         if (!str)
1244             return JS_FALSE;
1245         bytes = JS_GetStringBytes(str);
1246         if (strcmp(bytes, "arena") == 0) {
1247 #ifdef JS_ARENAMETER
1248             JS_DumpArenaStats(stdout);
1249 #endif
1250         } else if (strcmp(bytes, "atom") == 0) {
1251             DumpAtomArgs args;
1253             fprintf(gOutFile, "\natom table contents:\n");
1254             args.cx = cx;
1255             args.fp = stdout;
1256             JS_HashTableEnumerateEntries(cx->runtime->atomState.table,
1257                                          DumpAtom,
1258                                          &args);
1259 #ifdef HASHMETER
1260             JS_HashTableDumpMeter(cx->runtime->atomState.table,
1261                                   DumpAtom,
1262                                   stdout);
1263 #endif
1264         } else if (strcmp(bytes, "global") == 0) {
1265             DumpScope(cx, cx->globalObject, stdout);
1266         } else {
1267             atom = js_Atomize(cx, bytes, JS_GetStringLength(str), 0);
1268             if (!atom)
1269                 return JS_FALSE;
1270             if (!js_FindProperty(cx, (jsid)atom, &obj, &obj2, &prop))
1271                 return JS_FALSE;
1272             if (prop) {
1273                 OBJ_DROP_PROPERTY(cx, obj2, prop);
1274                 if (!OBJ_GET_PROPERTY(cx, obj, (jsid)atom, &value))
1275                     return JS_FALSE;
1276             }
1277             if (!prop || !JSVAL_IS_OBJECT(value)) {
1278                 fprintf(gErrFile, "js: invalid stats argument %s\n",
1279                         bytes);
1280                 continue;
1281             }
1282             obj = JSVAL_TO_OBJECT(value);
1283             if (obj)
1284                 DumpScope(cx, obj, stdout);
1285         }
1286     }
1287     return JS_TRUE;
1290 #endif /* DEBUG */
1292 #ifdef TEST_EXPORT
1293 static JSBool
1294 DoExport(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1296     JSAtom *atom;
1297     JSObject *obj2;
1298     JSProperty *prop;
1299     JSBool ok;
1300     uintN attrs;
1302     if (argc != 2) {
1303         JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_DOEXP_USAGE);
1304         return JS_FALSE;
1305     }
1306     if (!JS_ValueToObject(cx, argv[0], &obj))
1307         return JS_FALSE;
1308     argv[0] = OBJECT_TO_JSVAL(obj);
1309     atom = js_ValueToStringAtom(cx, argv[1]);
1310     if (!atom)
1311         return JS_FALSE;
1312     if (!OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &obj2, &prop))
1313         return JS_FALSE;
1314     if (!prop) {
1315         ok = OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, NULL, NULL,
1316                                  JSPROP_EXPORTED, NULL);
1317     } else {
1318         ok = OBJ_GET_ATTRIBUTES(cx, obj, (jsid)atom, prop, &attrs);
1319         if (ok) {
1320             attrs |= JSPROP_EXPORTED;
1321             ok = OBJ_SET_ATTRIBUTES(cx, obj, (jsid)atom, prop, &attrs);
1322         }
1323         OBJ_DROP_PROPERTY(cx, obj2, prop);
1324     }
1325     return ok;
1327 #endif
1329 #ifdef TEST_CVTARGS
1330 #include <ctype.h>
1332 static const char *
1333 EscapeWideString(jschar *w)
1335     static char enuf[80];
1336     static char hex[] = "0123456789abcdef";
1337     jschar u;
1338     unsigned char b, c;
1339     int i, j;
1341     if (!w)
1342         return "";
1343     for (i = j = 0; i < sizeof enuf - 1; i++, j++) {
1344         u = w[j];
1345         if (u == 0)
1346             break;
1347         b = (unsigned char)(u >> 8);
1348         c = (unsigned char)(u);
1349         if (b) {
1350             if (i >= sizeof enuf - 6)
1351                 break;
1352             enuf[i++] = '\\';
1353             enuf[i++] = 'u';
1354             enuf[i++] = hex[b >> 4];
1355             enuf[i++] = hex[b & 15];
1356             enuf[i++] = hex[c >> 4];
1357             enuf[i] = hex[c & 15];
1358         } else if (!isprint(c)) {
1359             if (i >= sizeof enuf - 4)
1360                 break;
1361             enuf[i++] = '\\';
1362             enuf[i++] = 'x';
1363             enuf[i++] = hex[c >> 4];
1364             enuf[i] = hex[c & 15];
1365         } else {
1366             enuf[i] = (char)c;
1367         }
1368     }
1369     enuf[i] = 0;
1370     return enuf;
1373 #include <stdarg.h>
1375 static JSBool
1376 ZZ_formatter(JSContext *cx, const char *format, JSBool fromJS, jsval **vpp,
1377              va_list *app)
1379     jsval *vp;
1380     va_list ap;
1381     jsdouble re, im;
1383     printf("entering ZZ_formatter");
1384     vp = *vpp;
1385     ap = *app;
1386     if (fromJS) {
1387         if (!JS_ValueToNumber(cx, vp[0], &re))
1388             return JS_FALSE;
1389         if (!JS_ValueToNumber(cx, vp[1], &im))
1390             return JS_FALSE;
1391         *va_arg(ap, jsdouble *) = re;
1392         *va_arg(ap, jsdouble *) = im;
1393     } else {
1394         re = va_arg(ap, jsdouble);
1395         im = va_arg(ap, jsdouble);
1396         if (!JS_NewNumberValue(cx, re, &vp[0]))
1397             return JS_FALSE;
1398         if (!JS_NewNumberValue(cx, im, &vp[1]))
1399             return JS_FALSE;
1400     }
1401     *vpp = vp + 2;
1402     *app = ap;
1403     printf("leaving ZZ_formatter");
1404     return JS_TRUE;
1407 static JSBool
1408 ConvertArgs(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1410     JSBool b = JS_FALSE;
1411     jschar c = 0;
1412     int32 i = 0, j = 0;
1413     uint32 u = 0;
1414     jsdouble d = 0, I = 0, re = 0, im = 0;
1415     char *s = NULL;
1416     JSString *str = NULL;
1417     jschar *w = NULL;
1418     JSObject *obj2 = NULL;
1419     JSFunction *fun = NULL;
1420     jsval v = JSVAL_VOID;
1421     JSBool ok;
1423     if (!JS_AddArgumentFormatter(cx, "ZZ", ZZ_formatter))
1424         return JS_FALSE;;
1425     ok = JS_ConvertArguments(cx, argc, argv, "b/ciujdIsSWofvZZ*",
1426                              &b, &c, &i, &u, &j, &d, &I, &s, &str, &w, &obj2,
1427                              &fun, &v, &re, &im);
1428     JS_RemoveArgumentFormatter(cx, "ZZ");
1429     if (!ok)
1430         return JS_FALSE;
1431     fprintf(gOutFile,
1432             "b %u, c %x (%c), i %ld, u %lu, j %ld\n",
1433             b, c, (char)c, i, u, j);
1434     fprintf(gOutFile,
1435             "d %g, I %g, s %s, S %s, W %s, obj %s, fun %s\n"
1436             "v %s, re %g, im %g\n",
1437             d, I, s, str ? JS_GetStringBytes(str) : "", EscapeWideString(w),
1438             JS_GetStringBytes(JS_ValueToString(cx, OBJECT_TO_JSVAL(obj2))),
1439             fun ? JS_GetStringBytes(JS_DecompileFunction(cx, fun, 4)) : "",
1440             JS_GetStringBytes(JS_ValueToString(cx, v)), re, im);
1441     return JS_TRUE;
1443 #endif
1445 static JSBool
1446 BuildDate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1448     fprintf(gOutFile, "built on %s at %s\n", __DATE__, __TIME__);
1449     return JS_TRUE;
1452 static JSBool
1453 Clear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1455     if (argc != 0 && !JS_ValueToObject(cx, argv[0], &obj))
1456         return JS_FALSE;
1457     JS_ClearScope(cx, obj);
1458     return JS_TRUE;
1461 static JSBool
1462 Intern(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1464     JSString *str;
1466     str = JS_ValueToString(cx, argv[0]);
1467     if (!str)
1468         return JS_FALSE;
1469     if (!JS_InternUCStringN(cx, JS_GetStringChars(str),
1470                                 JS_GetStringLength(str))) {
1471         return JS_FALSE;
1472     }
1473     return JS_TRUE;
1476 static JSBool
1477 Clone(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1479     JSFunction *fun;
1480     JSObject *funobj, *parent, *clone;
1482     fun = JS_ValueToFunction(cx, argv[0]);
1483     if (!fun)
1484         return JS_FALSE;
1485     funobj = JS_GetFunctionObject(fun);
1486     if (argc > 1) {
1487         if (!JS_ValueToObject(cx, argv[1], &parent))
1488             return JS_FALSE;
1489     } else {
1490         parent = JS_GetParent(cx, funobj);
1491     }
1492     clone = JS_CloneFunctionObject(cx, funobj, parent);
1493     if (!clone)
1494         return JS_FALSE;
1495     *rval = OBJECT_TO_JSVAL(clone);
1496     return JS_TRUE;
1499 static JSBool
1500 Seal(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1502     JSObject *target;
1503     JSBool deep = JS_FALSE;
1505     if (!JS_ConvertArguments(cx, argc, argv, "o/b", &target, &deep))
1506         return JS_FALSE;
1507     if (!target)
1508         return JS_TRUE;
1509     return JS_SealObject(cx, target, deep);
1512 static JSFunctionSpec shell_functions[] = {
1513     {"version",         Version,        0},
1514     {"options",         Options,        0},
1515     {"load",            Load,           1},
1516     {"print",           Print,          0},
1517     {"help",            Help,           0},
1518     {"quit",            Quit,           0},
1519     {"gc",              GC,             0},
1520     {"trap",            Trap,           3},
1521     {"untrap",          Untrap,         2},
1522     {"line2pc",         LineToPC,       0},
1523     {"pc2line",         PCToLine,       0},
1524 #ifdef DEBUG
1525     {"dis",             Disassemble,    1},
1526     {"dissrc",          DisassWithSrc,  1},
1527     {"notes",           Notes,          1},
1528     {"tracing",         Tracing,        0},
1529     {"stats",           DumpStats,      1},
1530 #endif
1531 #ifdef TEST_EXPORT
1532     {"xport",           DoExport,       2},
1533 #endif
1534 #ifdef TEST_CVTARGS
1535     {"cvtargs",         ConvertArgs,    0, 0, 12},
1536 #endif
1537     {"build",           BuildDate,      0},
1538     {"clear",           Clear,          0},
1539     {"intern",          Intern,         1},
1540     {"clone",           Clone,          1},
1541     {"seal",            Seal,           1, 0, 1},
1542     {0}
1543 };
1545 /* NOTE: These must be kept in sync with the above. */
1547 static char *shell_help_messages[] = {
1548     "version([number])      Get or set JavaScript version number",
1549     "options([option ...])  Get or toggle JavaScript options",
1550     "load(['foo.js' ...])   Load files named by string arguments",
1551     "print([exp ...])       Evaluate and print expressions",
1552     "help([name ...])       Display usage and help messages",
1553     "quit()                 Quit the shell",
1554     "gc()                   Run the garbage collector",
1555     "trap([fun, [pc,]] exp) Trap bytecode execution",
1556     "untrap(fun[, pc])      Remove a trap",
1557     "line2pc([fun,] line)   Map line number to PC",
1558     "pc2line(fun[, pc])     Map PC to line number",
1559 #ifdef DEBUG
1560     "dis([fun])             Disassemble functions into bytecodes",
1561     "dissrc([fun])          Disassemble functions with source lines",
1562     "notes([fun])           Show source notes for functions",
1563     "tracing([toggle])      Turn tracing on or off",
1564     "stats([string ...])    Dump 'arena', 'atom', 'global' stats",
1565 #endif
1566 #ifdef TEST_EXPORT
1567     "xport(obj, id)         Export identified property from object",
1568 #endif
1569 #ifdef TEST_CVTARGS
1570     "cvtargs(b, c, ...)     Test JS_ConvertArguments",
1571 #endif
1572     "build()                Show build date and time",
1573     "clear([obj])           Clear properties of object",
1574     "intern(str)            Internalize str in the atom table",
1575     "clone(fun[, scope])    Clone function object",
1576     "seal(obj[, deep])      Seal object, or object graph if deep",
1577     0
1578 };
1580 static void
1581 ShowHelpHeader(void)
1583     fprintf(gOutFile, "%-9s %-22s %s\n", "Command", "Usage", "Description");
1584     fprintf(gOutFile, "%-9s %-22s %s\n", "=======", "=====", "===========");
1587 static void
1588 ShowHelpForCommand(uintN n)
1590     fprintf(gOutFile, "%-9.9s %s\n", shell_functions[n].name, shell_help_messages[n]);
1593 static JSBool
1594 Help(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1596     uintN i, j;
1597     int did_header, did_something;
1598     JSType type;
1599     JSFunction *fun;
1600     JSString *str;
1601     const char *bytes;
1603     fprintf(gOutFile, "%s\n", JS_GetImplementationVersion());
1604     if (argc == 0) {
1605         ShowHelpHeader();
1606         for (i = 0; shell_functions[i].name; i++)
1607             ShowHelpForCommand(i);
1608     } else {
1609         did_header = 0;
1610         for (i = 0; i < argc; i++) {
1611             did_something = 0;
1612             type = JS_TypeOfValue(cx, argv[i]);
1613             if (type == JSTYPE_FUNCTION) {
1614                 fun = JS_ValueToFunction(cx, argv[i]);
1615                 str = fun->atom ? ATOM_TO_STRING(fun->atom) : NULL;
1616             } else if (type == JSTYPE_STRING) {
1617                 str = JSVAL_TO_STRING(argv[i]);
1618             } else {
1619                 str = NULL;
1620             }
1621             if (str) {
1622                 bytes = JS_GetStringBytes(str);
1623                 for (j = 0; shell_functions[j].name; j++) {
1624                     if (!strcmp(bytes, shell_functions[j].name)) {
1625                         if (!did_header) {
1626                             did_header = 1;
1627                             ShowHelpHeader();
1628                         }
1629                         did_something = 1;
1630                         ShowHelpForCommand(j);
1631                         break;
1632                     }
1633                 }
1634             }
1635             if (!did_something) {
1636                 str = JS_ValueToString(cx, argv[i]);
1637                 if (!str)
1638                     return JS_FALSE;
1639                 fprintf(gErrFile, "Sorry, no help for %s\n",
1640                         JS_GetStringBytes(str));
1641             }
1642         }
1643     }
1644     return JS_TRUE;
1647 /*
1648  * Define a JS object called "it".  Give it class operations that printf why
1649  * they're being called for tutorial purposes.
1650  */
1651 enum its_tinyid {
1652     ITS_COLOR, ITS_HEIGHT, ITS_WIDTH, ITS_FUNNY, ITS_ARRAY, ITS_RDONLY
1653 };
1655 static JSPropertySpec its_props[] = {
1656     {"color",           ITS_COLOR,      JSPROP_ENUMERATE},
1657     {"height",          ITS_HEIGHT,     JSPROP_ENUMERATE},
1658     {"width",           ITS_WIDTH,      JSPROP_ENUMERATE},
1659     {"funny",           ITS_FUNNY,      JSPROP_ENUMERATE},
1660     {"array",           ITS_ARRAY,      JSPROP_ENUMERATE},
1661     {"rdonly",          ITS_RDONLY,     JSPROP_READONLY},
1662     {0}
1663 };
1665 static JSBool
1666 its_item(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1668     *rval = OBJECT_TO_JSVAL(obj);
1669     if (argc != 0)
1670         JS_SetCallReturnValue2(cx, argv[0]);
1671     return JS_TRUE;
1674 static JSFunctionSpec its_methods[] = {
1675     {"item",            its_item,       0},
1676     {0}
1677 };
1679 #ifdef JSD_LOWLEVEL_SOURCE
1680 /*
1681  * This facilitates sending source to JSD (the debugger system) in the shell
1682  * where the source is loaded using the JSFILE hack in jsscan. The function
1683  * below is used as a callback for the jsdbgapi JS_SetSourceHandler hook.
1684  * A more normal embedding (e.g. mozilla) loads source itself and can send
1685  * source directly to JSD without using this hook scheme.
1686  */
1687 static void
1688 SendSourceToJSDebugger(const char *filename, uintN lineno,
1689                        jschar *str, size_t length,
1690                        void **listenerTSData, JSDContext* jsdc)
1692     JSDSourceText *jsdsrc = (JSDSourceText *) *listenerTSData;
1694     if (!jsdsrc) {
1695         if (!filename)
1696             filename = "typein";
1697         if (1 == lineno) {
1698             jsdsrc = JSD_NewSourceText(jsdc, filename);
1699         } else {
1700             jsdsrc = JSD_FindSourceForURL(jsdc, filename);
1701             if (jsdsrc && JSD_SOURCE_PARTIAL !=
1702                 JSD_GetSourceStatus(jsdc, jsdsrc)) {
1703                 jsdsrc = NULL;
1704             }
1705         }
1706     }
1707     if (jsdsrc) {
1708         jsdsrc = JSD_AppendUCSourceText(jsdc,jsdsrc, str, length,
1709                                         JSD_SOURCE_PARTIAL);
1710     }
1711     *listenerTSData = jsdsrc;
1713 #endif /* JSD_LOWLEVEL_SOURCE */
1715 static JSBool its_noisy;    /* whether to be noisy when finalizing it */
1717 static JSBool
1718 its_addProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
1720     if (its_noisy) {
1721         fprintf(gOutFile, "adding its property %s,",
1722                JS_GetStringBytes(JS_ValueToString(cx, id)));
1723         fprintf(gOutFile, " initial value %s\n",
1724                JS_GetStringBytes(JS_ValueToString(cx, *vp)));
1725     }
1726     return JS_TRUE;
1729 static JSBool
1730 its_delProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
1732     if (its_noisy) {
1733         fprintf(gOutFile, "deleting its property %s,",
1734                JS_GetStringBytes(JS_ValueToString(cx, id)));
1735         fprintf(gOutFile, " current value %s\n",
1736                JS_GetStringBytes(JS_ValueToString(cx, *vp)));
1737     }
1738     return JS_TRUE;
1741 static JSBool
1742 its_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
1744     if (its_noisy) {
1745         fprintf(gOutFile, "getting its property %s,",
1746                JS_GetStringBytes(JS_ValueToString(cx, id)));
1747         fprintf(gOutFile, " current value %s\n",
1748                JS_GetStringBytes(JS_ValueToString(cx, *vp)));
1749     }
1750     return JS_TRUE;
1753 static JSBool
1754 its_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
1756     if (its_noisy) {
1757         fprintf(gOutFile, "setting its property %s,",
1758                JS_GetStringBytes(JS_ValueToString(cx, id)));
1759         fprintf(gOutFile, " new value %s\n",
1760                JS_GetStringBytes(JS_ValueToString(cx, *vp)));
1761     }
1762     if (JSVAL_IS_STRING(id) &&
1763         !strcmp(JS_GetStringBytes(JSVAL_TO_STRING(id)), "noisy")) {
1764         return JS_ValueToBoolean(cx, *vp, &its_noisy);
1765     }
1766     return JS_TRUE;
1769 static JSBool
1770 its_enumerate(JSContext *cx, JSObject *obj)
1772     if (its_noisy)
1773         fprintf(gOutFile, "enumerate its properties\n");
1774     return JS_TRUE;
1777 static JSBool
1778 its_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
1779             JSObject **objp)
1781     if (its_noisy) {
1782         fprintf(gOutFile, "resolving its property %s, flags {%s,%s,%s}\n",
1783                JS_GetStringBytes(JS_ValueToString(cx, id)),
1784                (flags & JSRESOLVE_QUALIFIED) ? "qualified" : "",
1785                (flags & JSRESOLVE_ASSIGNING) ? "assigning" : "",
1786                (flags & JSRESOLVE_DETECTING) ? "detecting" : "");
1787     }
1788     return JS_TRUE;
1791 static JSBool
1792 its_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
1794     if (its_noisy)
1795         fprintf(gOutFile, "converting it to %s type\n", JS_GetTypeName(cx, type));
1796     return JS_TRUE;
1799 static void
1800 its_finalize(JSContext *cx, JSObject *obj)
1802     if (its_noisy)
1803         fprintf(gOutFile, "finalizing it\n");
1806 static JSClass its_class = {
1807     "It", JSCLASS_NEW_RESOLVE,
1808     its_addProperty,  its_delProperty,  its_getProperty,  its_setProperty,
1809     its_enumerate,    (JSResolveOp)its_resolve,
1810                                         its_convert,      its_finalize
1811 };
1813 JSErrorFormatString jsShell_ErrorFormatString[JSErr_Limit] = {
1814 #if JS_HAS_DFLT_MSG_STRINGS
1815 #define MSG_DEF(name, number, count, exception, format) \
1816     { format, count } ,
1817 #else
1818 #define MSG_DEF(name, number, count, exception, format) \
1819     { NULL, count } ,
1820 #endif
1821 #include "jsshell.msg"
1822 #undef MSG_DEF
1823 };
1825 static const JSErrorFormatString *
1826 my_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber)
1828     if ((errorNumber > 0) && (errorNumber < JSShellErr_Limit))
1829         return &jsShell_ErrorFormatString[errorNumber];
1830     return NULL;
1833 static void
1834 my_LoadErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
1836     if (!report) {
1837         fprintf(gErrFile, "%s\n", message);
1838         return;
1839     }
1841     /* Ignore any exceptions */
1842     if (JSREPORT_IS_EXCEPTION(report->flags))
1843         return;
1845     /* Otherwise, fall back to the ordinary error reporter. */
1846     my_ErrorReporter(cx, message, report);
1849 static void
1850 my_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
1852     int i, j, k, n;
1853     char *prefix, *tmp;
1854     const char *ctmp;
1856     if (!report) {
1857         fprintf(gErrFile, "%s\n", message);
1858         return;
1859     }
1861     /* Conditionally ignore reported warnings. */
1862     if (JSREPORT_IS_WARNING(report->flags) && !reportWarnings)
1863         return;
1865     prefix = NULL;
1866     if (report->filename)
1867         prefix = JS_smprintf("%s:", report->filename);
1868     if (report->lineno) {
1869         tmp = prefix;
1870         prefix = JS_smprintf("%s%u: ", tmp ? tmp : "", report->lineno);
1871         JS_free(cx, tmp);
1872     }
1873     if (JSREPORT_IS_WARNING(report->flags)) {
1874         tmp = prefix;
1875         prefix = JS_smprintf("%s%swarning: ",
1876                              tmp ? tmp : "",
1877                              JSREPORT_IS_STRICT(report->flags) ? "strict " : "");
1878         JS_free(cx, tmp);
1879     }
1881     /* embedded newlines -- argh! */
1882     while ((ctmp = strchr(message, '\n')) != 0) {
1883         ctmp++;
1884         if (prefix)
1885             fputs(prefix, gErrFile);
1886         fwrite(message, 1, ctmp - message, gErrFile);
1887         message = ctmp;
1888     }
1890     /* If there were no filename or lineno, the prefix might be empty */
1891     if (prefix)
1892         fputs(prefix, gErrFile);
1893     fputs(message, gErrFile);
1895     if (!report->linebuf) {
1896         fputc('\n', gErrFile);
1897         goto out;
1898     }
1900     /* report->linebuf usually ends with a newline. */
1901     n = strlen(report->linebuf);
1902     fprintf(gErrFile, ":\n%s%s%s%s",
1903             prefix,
1904             report->linebuf,
1905             (n > 0 && report->linebuf[n-1] == '\n') ? "" : "\n",
1906             prefix);
1907     n = PTRDIFF(report->tokenptr, report->linebuf, char);
1908     for (i = j = 0; i < n; i++) {
1909         if (report->linebuf[i] == '\t') {
1910             for (k = (j + 8) & ~7; j < k; j++) {
1911                 fputc('.', gErrFile);
1912             }
1913             continue;
1914         }
1915         fputc('.', gErrFile);
1916         j++;
1917     }
1918     fputs("^\n", gErrFile);
1919  out:
1920     if (!JSREPORT_IS_WARNING(report->flags))
1921         gExitCode = EXITCODE_RUNTIME_ERROR;
1922     JS_free(cx, prefix);
1925 #if defined(SHELL_HACK) && defined(DEBUG) && defined(XP_UNIX)
1926 static JSBool
1927 Exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1929     JSFunction *fun;
1930     const char *name, **nargv;
1931     uintN i, nargc;
1932     JSString *str;
1933     pid_t pid;
1934     int status;
1936     fun = JS_ValueToFunction(cx, argv[-2]);
1937     if (!fun)
1938         return JS_FALSE;
1939     if (!fun->atom)
1940         return JS_TRUE;
1941     name = JS_GetStringBytes(ATOM_TO_STRING(fun->atom));
1942     nargc = 1 + argc;
1943     nargv = JS_malloc(cx, (nargc + 1) * sizeof(char *));
1944     if (!nargv)
1945         return JS_FALSE;
1946     nargv[0] = name;
1947     for (i = 1; i < nargc; i++) {
1948         str = JS_ValueToString(cx, argv[i-1]);
1949         if (!str) {
1950             JS_free(cx, nargv);
1951             return JS_FALSE;
1952         }
1953         nargv[i] = JS_GetStringBytes(str);
1954     }
1955     nargv[nargc] = 0;
1956     pid = fork();
1957     switch (pid) {
1958       case -1:
1959         perror("js");
1960         break;
1961       case 0:
1962         (void) execvp(name, (char **)nargv);
1963         perror("js");
1964         exit(127);
1965       default:
1966         while (waitpid(pid, &status, 0) < 0 && errno == EINTR)
1967             continue;
1968         break;
1969     }
1970     JS_free(cx, nargv);
1971     return JS_TRUE;
1973 #endif
1975 #define LAZY_STANDARD_CLASSES
1977 static JSBool
1978 global_enumerate(JSContext *cx, JSObject *obj)
1980 #ifdef LAZY_STANDARD_CLASSES
1981     return JS_EnumerateStandardClasses(cx, obj);
1982 #else
1983     return JS_TRUE;
1984 #endif
1987 static JSBool
1988 global_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
1989                JSObject **objp)
1991 #ifdef LAZY_STANDARD_CLASSES
1992     if ((flags & JSRESOLVE_ASSIGNING) == 0) {
1993         JSBool resolved;
1995         if (!JS_ResolveStandardClass(cx, obj, id, &resolved))
1996             return JS_FALSE;
1997         if (resolved) {
1998             *objp = obj;
1999             return JS_TRUE;
2000         }
2001     }
2002 #endif
2004 #if defined(SHELL_HACK) && defined(DEBUG) && defined(XP_UNIX)
2005     if ((flags & (JSRESOLVE_QUALIFIED | JSRESOLVE_ASSIGNING)) == 0) {
2006         /*
2007          * Do this expensive hack only for unoptimized Unix builds, which are
2008          * not used for benchmarking.
2009          */
2010         char *path, *comp, *full;
2011         const char *name;
2012         JSBool ok, found;
2013         JSFunction *fun;
2015         if (!JSVAL_IS_STRING(id))
2016             return JS_TRUE;
2017         path = getenv("PATH");
2018         if (!path)
2019             return JS_TRUE;
2020         path = JS_strdup(cx, path);
2021         if (!path)
2022             return JS_FALSE;
2023         name = JS_GetStringBytes(JSVAL_TO_STRING(id));
2024         ok = JS_TRUE;
2025         for (comp = strtok(path, ":"); comp; comp = strtok(NULL, ":")) {
2026             if (*comp != '\0') {
2027                 full = JS_smprintf("%s/%s", comp, name);
2028                 if (!full) {
2029                     JS_ReportOutOfMemory(cx);
2030                     ok = JS_FALSE;
2031                     break;
2032                 }
2033             } else {
2034                 full = (char *)name;
2035             }
2036             found = (access(full, X_OK) == 0);
2037             if (*comp != '\0')
2038                 free(full);
2039             if (found) {
2040                 fun = JS_DefineFunction(cx, obj, name, Exec, 0,
2041                                         JSPROP_ENUMERATE);
2042                 ok = (fun != NULL);
2043                 if (ok)
2044                     *objp = obj;
2045                 break;
2046             }
2047         }
2048         JS_free(cx, path);
2049         return ok;
2050     }
2051 #else
2052     return JS_TRUE;
2053 #endif
2056 JSClass global_class = {
2057     "global", JSCLASS_NEW_RESOLVE,
2058     JS_PropertyStub,  JS_PropertyStub,
2059     JS_PropertyStub,  JS_PropertyStub,
2060     global_enumerate, (JSResolveOp) global_resolve,
2061     JS_ConvertStub,   JS_FinalizeStub
2062 };
2064 static JSBool
2065 env_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
2067 /* XXX porting may be easy, but these don't seem to supply setenv by default */
2068 #if !defined XP_BEOS && !defined XP_OS2 && !defined SOLARIS
2069     JSString *idstr, *valstr;
2070     const char *name, *value;
2071     int rv;
2073     idstr = JS_ValueToString(cx, id);
2074     valstr = JS_ValueToString(cx, *vp);
2075     if (!idstr || !valstr)
2076         return JS_FALSE;
2077     name = JS_GetStringBytes(idstr);
2078     value = JS_GetStringBytes(valstr);
2079 #if defined XP_WIN || defined HPUX || defined OSF1 || defined IRIX
2080     {
2081         char *waste = JS_smprintf("%s=%s", name, value);
2082         if (!waste) {
2083             JS_ReportOutOfMemory(cx);
2084             return JS_FALSE;
2085         }
2086         rv = putenv(waste);
2087 #ifdef XP_WIN
2088         /*
2089          * HPUX9 at least still has the bad old non-copying putenv.
2090          *
2091          * Per mail from <s.shanmuganathan@digital.com>, OSF1 also has a putenv
2092          * that will crash if you pass it an auto char array (so it must place
2093          * its argument directly in the char *environ[] array).
2094          */
2095         free(waste);
2096 #endif
2097     }
2098 #else
2099     rv = setenv(name, value, 1);
2100 #endif
2101     if (rv < 0) {
2102         JS_ReportError(cx, "can't set envariable %s to %s", name, value);
2103         return JS_FALSE;
2104     }
2105     *vp = STRING_TO_JSVAL(valstr);
2106 #endif /* !defined XP_BEOS && !defined XP_OS2 && !defined SOLARIS */
2107     return JS_TRUE;
2110 static JSBool
2111 env_enumerate(JSContext *cx, JSObject *obj)
2113     static JSBool reflected;
2114     char **evp, *name, *value;
2115     JSString *valstr;
2116     JSBool ok;
2118     if (reflected)
2119         return JS_TRUE;
2121     for (evp = (char **)JS_GetPrivate(cx, obj); (name = *evp) != NULL; evp++) {
2122         value = strchr(name, '=');
2123         if (!value)
2124             continue;
2125         *value++ = '\0';
2126         valstr = JS_NewStringCopyZ(cx, value);
2127         if (!valstr) {
2128             ok = JS_FALSE;
2129         } else {
2130             ok = JS_DefineProperty(cx, obj, name, STRING_TO_JSVAL(valstr),
2131                                    NULL, NULL, JSPROP_ENUMERATE);
2132         }
2133         value[-1] = '=';
2134         if (!ok)
2135             return JS_FALSE;
2136     }
2138     reflected = JS_TRUE;
2139     return JS_TRUE;
2142 static JSBool
2143 env_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
2144             JSObject **objp)
2146     JSString *idstr, *valstr;
2147     const char *name, *value;
2149     if (flags & JSRESOLVE_ASSIGNING)
2150         return JS_TRUE;
2152     idstr = JS_ValueToString(cx, id);
2153     if (!idstr)
2154         return JS_FALSE;
2155     name = JS_GetStringBytes(idstr);
2156     value = getenv(name);
2157     if (value) {
2158         valstr = JS_NewStringCopyZ(cx, value);
2159         if (!valstr)
2160             return JS_FALSE;
2161         if (!JS_DefineProperty(cx, obj, name, STRING_TO_JSVAL(valstr),
2162                                NULL, NULL, JSPROP_ENUMERATE)) {
2163             return JS_FALSE;
2164         }
2165         *objp = obj;
2166     }
2167     return JS_TRUE;
2170 static JSClass env_class = {
2171     "environment", JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE,
2172     JS_PropertyStub,  JS_PropertyStub,
2173     JS_PropertyStub,  env_setProperty,
2174     env_enumerate, (JSResolveOp) env_resolve,
2175     JS_ConvertStub,   JS_FinalizeStub
2176 };
2178 #ifdef NARCISSUS
2180 static JSBool
2181 defineProperty(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
2182                jsval *rval)
2184     JSString *str;
2185     jsval value;
2186     JSBool dontDelete, readOnly, dontEnum;
2187     const jschar *chars;
2188     size_t length;
2189     uintN attrs;
2191     dontDelete = readOnly = dontEnum = JS_FALSE;
2192     if (!JS_ConvertArguments(cx, argc, argv, "Sv/bbb",
2193                              &str, &value, &dontDelete, &readOnly, &dontEnum)) {
2194         return JS_FALSE;
2195     }
2196     chars = JS_GetStringChars(str);
2197     length = JS_GetStringLength(str);
2198     attrs = dontEnum ? 0 : JSPROP_ENUMERATE;
2199     if (dontDelete)
2200         attrs |= JSPROP_PERMANENT;
2201     if (readOnly)
2202         attrs |= JSPROP_READONLY;
2203     return JS_DefineUCProperty(cx, obj, chars, length, value, NULL, NULL,
2204                                attrs);
2207 #include <fcntl.h>
2208 #include <sys/stat.h>
2210 static JSBool
2211 snarf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
2213     JSString *str;
2214     const char *filename;
2215     int fd, cc;
2216     JSBool ok;
2217     size_t len;
2218     char *buf;
2219     struct stat sb;
2221     str = JS_ValueToString(cx, argv[0]);
2222     if (!str)
2223         return JS_FALSE;
2224     filename = JS_GetStringBytes(str);
2225     fd = open(filename, O_RDONLY);
2226     ok = JS_TRUE;
2227     len = 0;
2228     buf = NULL;
2229     if (fd < 0) {
2230         JS_ReportError(cx, "can't open %s: %s", filename, strerror(errno));
2231         ok = JS_FALSE;
2232     } else if (fstat(fd, &sb) < 0) {
2233         JS_ReportError(cx, "can't stat %s", filename);
2234         ok = JS_FALSE;
2235     } else {
2236         len = sb.st_size;
2237         buf = JS_malloc(cx, len + 1);
2238         if (!buf) {
2239             ok = JS_FALSE;
2240         } else if ((cc = read(fd, buf, len)) != len) {
2241             JS_free(cx, buf);
2242             JS_ReportError(cx, "can't read %s: %s", filename,
2243                            (cc < 0) ? strerror(errno) : "short read");
2244             ok = JS_FALSE;
2245         }
2246     }
2247     close(fd);
2248     if (!ok)
2249         return ok;
2250     buf[len] = '\0';
2251     str = JS_NewString(cx, buf, len);
2252     if (!str) {
2253         JS_free(cx, buf);
2254         return JS_FALSE;
2255     }
2256     *rval = STRING_TO_JSVAL(str);
2257     return JS_TRUE;
2260 #endif /* NARCISSUS */
2262 int
2263 main(int argc, char **argv, char **envp)
2265     int stackDummy;
2266     JSVersion version;
2267     JSRuntime *rt;
2268     JSContext *cx;
2269     JSObject *glob, *it, *envobj;
2270     int result;
2271 #ifdef LIVECONNECT
2272     JavaVM *java_vm = NULL;
2273 #endif
2274 #ifdef JSDEBUGGER_JAVA_UI
2275     JNIEnv *java_env;
2276 #endif
2278     gStackBase = (jsuword)&stackDummy;
2280 #ifdef XP_OS2
2281    /* these streams are normally line buffered on OS/2 and need a \n, *
2282     * so we need to unbuffer then to get a reasonable prompt          */
2283     setbuf(stdout,0);
2284     setbuf(stderr,0);
2285 #endif
2287     gErrFile = stderr;
2288     gOutFile = stdout;
2290 #ifdef XP_MAC
2291 #ifndef XP_MAC_MPW
2292     initConsole("\pJavaScript Shell", "Welcome to js shell.", &argc, &argv);
2293 #endif
2294 #endif
2296 #ifdef MAC_TEST_HACK
2297 /*
2298     Open a file "testArgs.txt" and read each line into argc/argv.
2299     Re-direct all output to "results.txt"
2300 */
2301     {
2302         char argText[256];
2303         FILE *f = fopen("testargs.txt", "r");
2304         if (f) {
2305             int maxArgs = 32; /* arbitrary max !!! */
2306             int argText_strlen;
2307             argc = 1;
2308             argv = malloc(sizeof(char *) * maxArgs);
2309             argv[0] = NULL;
2310             while (fgets(argText, 255, f)) {
2311                  /* argText includes '\n' */
2312                 argText_strlen = strlen(argText);
2313                 argv[argc] = malloc(argText_strlen);
2314                 strncpy(argv[argc], argText, argText_strlen - 1);
2315                 argv[argc][argText_strlen - 1] = '\0';
2316                 argc++;
2317                 if (argc >= maxArgs)
2318                     break;
2319             }
2320             fclose(f);
2321         }
2322         gTestResultFile = fopen("results.txt", "w");
2323     }
2325     gErrFile = gTestResultFile;
2326     gOutFile = gTestResultFile;
2327 #endif
2329     version = JSVERSION_DEFAULT;
2331     argc--;
2332     argv++;
2334     rt = JS_NewRuntime(64L * 1024L * 1024L);
2335     if (!rt)
2336         return 1;
2338     cx = JS_NewContext(rt, gStackChunkSize);
2339     if (!cx)
2340         return 1;
2341     JS_SetErrorReporter(cx, my_ErrorReporter);
2343     glob = JS_NewObject(cx, &global_class, NULL, NULL);
2344     if (!glob)
2345         return 1;
2346 #ifdef LAZY_STANDARD_CLASSES
2347     JS_SetGlobalObject(cx, glob);
2348 #else
2349     if (!JS_InitStandardClasses(cx, glob))
2350         return 1;
2351 #endif
2352     if (!JS_DefineFunctions(cx, glob, shell_functions))
2353         return 1;
2355     /* Set version only after there is a global object. */
2356     if (version != JSVERSION_DEFAULT)
2357         JS_SetVersion(cx, version);
2359     it = JS_DefineObject(cx, glob, "it", &its_class, NULL, 0);
2360     if (!it)
2361         return 1;
2362     if (!JS_DefineProperties(cx, it, its_props))
2363         return 1;
2364     if (!JS_DefineFunctions(cx, it, its_methods))
2365         return 1;
2367 #ifdef PERLCONNECT
2368     if (!JS_InitPerlClass(cx, glob))
2369         return 1;
2370 #endif
2372 #ifdef JSDEBUGGER
2373     /*
2374     * XXX A command line option to enable debugging (or not) would be good
2375     */
2376     _jsdc = JSD_DebuggerOnForUser(rt, NULL, NULL);
2377     if (!_jsdc)
2378         return 1;
2379     JSD_JSContextInUse(_jsdc, cx);
2380 #ifdef JSD_LOWLEVEL_SOURCE
2381     JS_SetSourceHandler(rt, SendSourceToJSDebugger, _jsdc);
2382 #endif /* JSD_LOWLEVEL_SOURCE */
2383 #ifdef JSDEBUGGER_JAVA_UI
2384     _jsdjc = JSDJ_CreateContext();
2385     if (! _jsdjc)
2386         return 1;
2387     JSDJ_SetJSDContext(_jsdjc, _jsdc);
2388     java_env = JSDJ_CreateJavaVMAndStartDebugger(_jsdjc);
2389 #ifdef LIVECONNECT
2390     if (java_env)
2391         (*java_env)->GetJavaVM(java_env, &java_vm);
2392 #endif
2393     /*
2394     * XXX This would be the place to wait for the debugger to start.
2395     * Waiting would be nice in general, but especially when a js file
2396     * is passed on the cmd line.
2397     */
2398 #endif /* JSDEBUGGER_JAVA_UI */
2399 #ifdef JSDEBUGGER_C_UI
2400     JSDB_InitDebugger(rt, _jsdc, 0);
2401 #endif /* JSDEBUGGER_C_UI */
2402 #endif /* JSDEBUGGER */
2404 #ifdef LIVECONNECT
2405     if (!JSJ_SimpleInit(cx, glob, java_vm, getenv("CLASSPATH")))
2406         return 1;
2407 #endif
2409     envobj = JS_DefineObject(cx, glob, "environment", &env_class, NULL, 0);
2410     if (!envobj || !JS_SetPrivate(cx, envobj, envp))
2411         return 1;
2413 #ifdef NARCISSUS
2414     {
2415         jsval v;
2416         static const char Object_prototype[] = "Object.prototype";
2418         if (!JS_DefineFunction(cx, glob, "snarf", snarf, 1, 0))
2419             return 1;
2420         if (!JS_EvaluateScript(cx, glob,
2421                                Object_prototype, sizeof Object_prototype - 1,
2422                                NULL, 0, &v)) {
2423             return 1;
2424         }
2425         if (!JS_DefineFunction(cx, JSVAL_TO_OBJECT(v), "__defineProperty__",
2426                                defineProperty, 5, 0)) {
2427             return 1;
2428         }
2429     }
2430 #endif
2432     result = ProcessArgs(cx, glob, argv, argc);
2434 #ifdef JSDEBUGGER
2435     if (_jsdc)
2436         JSD_DebuggerOff(_jsdc);
2437 #endif  /* JSDEBUGGER */
2439 #ifdef MAC_TEST_HACK
2440     fclose(gTestResultFile);
2441 #endif
2443     JS_DestroyContext(cx);
2444     JS_DestroyRuntime(rt);
2445     JS_ShutDown();
2446     return result;