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)
114 {
115 char *copy = (char *) malloc(strlen(str)+1);
116 if (copy)
117 strcpy(copy, str);
118 return copy;
119 }
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)
125 {
126 char *limit = str + length;
127 while (str != limit) {
128 if (*str == '\n')
129 *str = '\r';
130 str++;
131 }
132 }
134 int fputc(int c, FILE *file)
135 {
136 char buffer = c;
137 if (buffer == '\n')
138 buffer = '\r';
139 return fwrite(&buffer, 1, 1, file);
140 }
142 int fputs(const char *s, FILE *file)
143 {
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);
158 }
160 int fprintf(FILE* file, const char *format, ...)
161 {
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;
190 }
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)
200 {
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;
213 }
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)
219 {
220 if (strcmp(var, "CLASSPATH") == 0) {
221 static char class_path[] = "liveconnect.jar";
222 return class_path;
223 }
224 return NULL;
225 }
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;
290 }
292 static void
293 Process(JSContext *cx, JSObject *obj, char *filename)
294 {
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;
403 }
405 static int
406 usage(void)
407 {
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;
411 }
413 static uint32 gBranchCount;
414 static uint32 gBranchLimit;
416 static JSBool
417 my_BranchCallback(JSContext *cx, JSScript *script)
418 {
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;
430 }
432 extern JSClass global_class;
434 static int
435 ProcessArgs(JSContext *cx, JSObject *obj, char **argv, int argc)
436 {
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;
570 }
573 static JSBool
574 Version(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
575 {
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;
581 }
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)
595 {
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;
646 }
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)
656 {
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;
690 }
692 static JSBool
693 Print(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
694 {
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;
708 }
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)
715 {
716 #ifdef LIVECONNECT
717 JSJ_SimpleShutdown();
718 #endif
720 JS_ConvertArguments(cx, argc, argv,"/ i", &gExitCode);
722 gQuitting = JS_TRUE;
723 return JS_FALSE;
724 }
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)
732 {
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;
769 }
771 static JSScript *
772 ValueToScript(JSContext *cx, jsval v)
773 {
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;
787 }
789 static JSBool
790 GetTrapArgs(JSContext *cx, uintN argc, jsval *argv, JSScript **scriptp,
791 int32 *ip)
792 {
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;
813 }
815 static JSTrapStatus
816 TrapHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval,
817 void *closure)
818 {
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;
833 }
835 static JSBool
836 Trap(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
837 {
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);
854 }
856 static JSBool
857 Untrap(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
858 {
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;
866 }
868 static JSBool
869 LineToPC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
870 {
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;
889 }
891 static JSBool
892 PCToLine(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
893 {
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;
905 }
907 #ifdef DEBUG
909 static void
910 SrcNotes(JSContext *cx, JSScript *script)
911 {
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 }
982 }
984 static JSBool
985 Notes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
986 {
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;
998 }
1000 static JSBool
1001 TryNotes(JSContext *cx, JSScript *script)
1002 {
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;
1014 }
1016 static JSBool
1017 Disassemble(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1018 {
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;
1060 }
1062 static JSBool
1063 DisassWithSrc(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1064 jsval *rval)
1065 {
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
1139 }
1141 static JSBool
1142 Tracing(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1143 {
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;
1171 }
1173 typedef struct DumpAtomArgs {
1174 JSContext *cx;
1175 FILE *fp;
1176 } DumpAtomArgs;
1178 static int
1179 DumpAtom(JSHashEntry *he, int i, void *arg)
1180 {
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;
1194 }
1196 static void
1197 DumpScope(JSContext *cx, JSObject *obj, FILE *fp)
1198 {
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 }
1228 }
1230 static JSBool
1231 DumpStats(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1232 {
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;
1288 }
1290 #endif /* DEBUG */
1292 #ifdef TEST_EXPORT
1293 static JSBool
1294 DoExport(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1295 {
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;
1326 }
1327 #endif
1329 #ifdef TEST_CVTARGS
1330 #include <ctype.h>
1332 static const char *
1333 EscapeWideString(jschar *w)
1334 {
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;
1371 }
1373 #include <stdarg.h>
1375 static JSBool
1376 ZZ_formatter(JSContext *cx, const char *format, JSBool fromJS, jsval **vpp,
1377 va_list *app)
1378 {
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;
1405 }
1407 static JSBool
1408 ConvertArgs(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1409 {
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;
1442 }
1443 #endif
1445 static JSBool
1446 BuildDate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1447 {
1448 fprintf(gOutFile, "built on %s at %s\n", __DATE__, __TIME__);
1449 return JS_TRUE;
1450 }
1452 static JSBool
1453 Clear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1454 {
1455 if (argc != 0 && !JS_ValueToObject(cx, argv[0], &obj))
1456 return JS_FALSE;
1457 JS_ClearScope(cx, obj);
1458 return JS_TRUE;
1459 }
1461 static JSBool
1462 Intern(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1463 {
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;
1474 }
1476 static JSBool
1477 Clone(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1478 {
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;
1497 }
1499 static JSBool
1500 Seal(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1501 {
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);
1510 }
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)
1582 {
1583 fprintf(gOutFile, "%-9s %-22s %s\n", "Command", "Usage", "Description");
1584 fprintf(gOutFile, "%-9s %-22s %s\n", "=======", "=====", "===========");
1585 }
1587 static void
1588 ShowHelpForCommand(uintN n)
1589 {
1590 fprintf(gOutFile, "%-9.9s %s\n", shell_functions[n].name, shell_help_messages[n]);
1591 }
1593 static JSBool
1594 Help(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1595 {
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;
1645 }
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)
1667 {
1668 *rval = OBJECT_TO_JSVAL(obj);
1669 if (argc != 0)
1670 JS_SetCallReturnValue2(cx, argv[0]);
1671 return JS_TRUE;
1672 }
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)
1691 {
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;
1712 }
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)
1719 {
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;
1727 }
1729 static JSBool
1730 its_delProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
1731 {
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;
1739 }
1741 static JSBool
1742 its_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
1743 {
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;
1751 }
1753 static JSBool
1754 its_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
1755 {
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;
1767 }
1769 static JSBool
1770 its_enumerate(JSContext *cx, JSObject *obj)
1771 {
1772 if (its_noisy)
1773 fprintf(gOutFile, "enumerate its properties\n");
1774 return JS_TRUE;
1775 }
1777 static JSBool
1778 its_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
1779 JSObject **objp)
1780 {
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;
1789 }
1791 static JSBool
1792 its_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
1793 {
1794 if (its_noisy)
1795 fprintf(gOutFile, "converting it to %s type\n", JS_GetTypeName(cx, type));
1796 return JS_TRUE;
1797 }
1799 static void
1800 its_finalize(JSContext *cx, JSObject *obj)
1801 {
1802 if (its_noisy)
1803 fprintf(gOutFile, "finalizing it\n");
1804 }
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)
1827 {
1828 if ((errorNumber > 0) && (errorNumber < JSShellErr_Limit))
1829 return &jsShell_ErrorFormatString[errorNumber];
1830 return NULL;
1831 }
1833 static void
1834 my_LoadErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
1835 {
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);
1847 }
1849 static void
1850 my_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
1851 {
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);
1923 }
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)
1928 {
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;
1972 }
1973 #endif
1975 #define LAZY_STANDARD_CLASSES
1977 static JSBool
1978 global_enumerate(JSContext *cx, JSObject *obj)
1979 {
1980 #ifdef LAZY_STANDARD_CLASSES
1981 return JS_EnumerateStandardClasses(cx, obj);
1982 #else
1983 return JS_TRUE;
1984 #endif
1985 }
1987 static JSBool
1988 global_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
1989 JSObject **objp)
1990 {
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
2054 }
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)
2066 {
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;
2108 }
2110 static JSBool
2111 env_enumerate(JSContext *cx, JSObject *obj)
2112 {
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;
2140 }
2142 static JSBool
2143 env_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
2144 JSObject **objp)
2145 {
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;
2168 }
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)
2183 {
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);
2205 }
2207 #include <fcntl.h>
2208 #include <sys/stat.h>
2210 static JSBool
2211 snarf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
2212 {
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;
2258 }
2260 #endif /* NARCISSUS */
2262 int
2263 main(int argc, char **argv, char **envp)
2264 {
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;
2447 }