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