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