Make the shell's -z option work again. bug 441361, r=crowder
[mozilla-central.git] / js / src / js.cpp
blobf43ff3e1fca39a6fb8c54022f24cc6e55d47cc30
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sw=4 et tw=78:
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
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/
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.
17 * The Original Code is Mozilla Communicator client code, released
18 * March 31, 1998.
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.
25 * Contributor(s):
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.
39 * ***** END LICENSE BLOCK ***** */
42 * JS shell.
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 "jsarray.h"
56 #include "jsatom.h"
57 #include "jscntxt.h"
58 #include "jsdbgapi.h"
59 #include "jsemit.h"
60 #include "jsfun.h"
61 #include "jsgc.h"
62 #include "jslock.h"
63 #include "jsnum.h"
64 #include "jsobj.h"
65 #include "jsparse.h"
66 #include "jsscope.h"
67 #include "jsscript.h"
69 #ifdef LIVECONNECT
70 #include "jsjava.h"
71 #endif
73 #ifdef JSDEBUGGER
74 #include "jsdebug.h"
75 #ifdef JSDEBUGGER_JAVA_UI
76 #include "jsdjava.h"
77 #endif /* JSDEBUGGER_JAVA_UI */
78 #ifdef JSDEBUGGER_C_UI
79 #include "jsdb.h"
80 #endif /* JSDEBUGGER_C_UI */
81 #endif /* JSDEBUGGER */
83 #ifdef XP_UNIX
84 #include <unistd.h>
85 #include <sys/types.h>
86 #include <sys/wait.h>
87 #endif
89 #if defined(XP_WIN) || defined(XP_OS2)
90 #include <io.h> /* for isatty() */
91 #endif
93 typedef enum JSShellExitCode {
94 EXITCODE_RUNTIME_ERROR = 3,
95 EXITCODE_FILE_NOT_FOUND = 4,
96 EXITCODE_OUT_OF_MEMORY = 5
97 } JSShellExitCode;
99 size_t gStackChunkSize = 8192;
101 /* Assume that we can not use more than 5e5 bytes of C stack by default. */
102 static size_t gMaxStackSize = 500000;
103 static jsuword gStackBase;
105 static size_t gScriptStackQuota = JS_DEFAULT_SCRIPT_STACK_QUOTA;
107 static JSBool gEnableBranchCallback = JS_FALSE;
108 static uint32 gBranchCount;
109 static uint32 gBranchLimit;
111 int gExitCode = 0;
112 JSBool gQuitting = JS_FALSE;
113 FILE *gErrFile = NULL;
114 FILE *gOutFile = NULL;
116 static JSBool reportWarnings = JS_TRUE;
117 static JSBool compileOnly = JS_FALSE;
119 typedef enum JSShellErrNum {
120 #define MSG_DEF(name, number, count, exception, format) \
121 name = number,
122 #include "jsshell.msg"
123 #undef MSG_DEF
124 JSShellErr_Limit
125 #undef MSGDEF
126 } JSShellErrNum;
128 static const JSErrorFormatString *
129 my_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber);
130 static JSObject *
131 split_setup(JSContext *cx);
133 #ifdef EDITLINE
134 JS_BEGIN_EXTERN_C
135 extern char *readline(const char *prompt);
136 extern void add_history(char *line);
137 JS_END_EXTERN_C
138 #endif
140 static JSBool
141 GetLine(JSContext *cx, char *bufp, FILE *file, const char *prompt) {
142 #ifdef EDITLINE
144 * Use readline only if file is stdin, because there's no way to specify
145 * another handle. Are other filehandles interactive?
147 if (file == stdin) {
148 char *linep = readline(prompt);
149 if (!linep)
150 return JS_FALSE;
151 if (linep[0] != '\0')
152 add_history(linep);
153 strcpy(bufp, linep);
154 JS_free(cx, linep);
155 bufp += strlen(bufp);
156 *bufp++ = '\n';
157 *bufp = '\0';
158 } else
159 #endif
161 char line[256];
162 fprintf(gOutFile, prompt);
163 fflush(gOutFile);
164 if (!fgets(line, sizeof line, file))
165 return JS_FALSE;
166 strcpy(bufp, line);
168 return JS_TRUE;
171 static JSBool
172 my_BranchCallback(JSContext *cx, JSScript *script)
174 if (++gBranchCount == gBranchLimit) {
175 if (script) {
176 if (script->filename)
177 fprintf(gErrFile, "%s:", script->filename);
178 fprintf(gErrFile, "%u: script branch callback (%u callbacks)\n",
179 script->lineno, gBranchLimit);
180 } else {
181 fprintf(gErrFile, "native branch callback (%u callbacks)\n",
182 gBranchLimit);
184 gBranchCount = 0;
185 return JS_FALSE;
187 #ifdef JS_THREADSAFE
188 if ((gBranchCount & 0xff) == 1) {
189 #endif
190 if ((gBranchCount & 0x3fff) == 1)
191 JS_MaybeGC(cx);
192 #ifdef JS_THREADSAFE
193 else
194 JS_YieldRequest(cx);
196 #endif
197 return JS_TRUE;
200 static void
201 SetContextOptions(JSContext *cx)
203 jsuword stackLimit;
205 if (gMaxStackSize == 0) {
207 * Disable checking for stack overflow if limit is zero.
209 stackLimit = 0;
210 } else {
211 #if JS_STACK_GROWTH_DIRECTION > 0
212 stackLimit = gStackBase + gMaxStackSize;
213 #else
214 stackLimit = gStackBase - gMaxStackSize;
215 #endif
217 JS_SetThreadStackLimit(cx, stackLimit);
218 JS_SetScriptStackQuota(cx, gScriptStackQuota);
219 if (gEnableBranchCallback) {
220 JS_SetBranchCallback(cx, my_BranchCallback);
221 JS_ToggleOptions(cx, JSOPTION_NATIVE_BRANCH_CALLBACK);
225 static void
226 Process(JSContext *cx, JSObject *obj, char *filename, JSBool forceTTY)
228 JSBool ok, hitEOF;
229 JSScript *script;
230 jsval result;
231 JSString *str;
232 char buffer[4096];
233 char *bufp;
234 int lineno;
235 int startline;
236 FILE *file;
238 if (forceTTY || !filename || strcmp(filename, "-") == 0) {
239 file = stdin;
240 } else {
241 file = fopen(filename, "r");
242 if (!file) {
243 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
244 JSSMSG_CANT_OPEN, filename, strerror(errno));
245 gExitCode = EXITCODE_FILE_NOT_FOUND;
246 return;
250 SetContextOptions(cx);
252 if (!forceTTY && !isatty(fileno(file))) {
254 * It's not interactive - just execute it.
256 * Support the UNIX #! shell hack; gobble the first line if it starts
257 * with '#'. TODO - this isn't quite compatible with sharp variables,
258 * as a legal js program (using sharp variables) might start with '#'.
259 * But that would require multi-character lookahead.
261 int ch = fgetc(file);
262 if (ch == '#') {
263 while((ch = fgetc(file)) != EOF) {
264 if (ch == '\n' || ch == '\r')
265 break;
268 ungetc(ch, file);
269 script = JS_CompileFileHandle(cx, obj, filename, file);
270 if (script) {
271 if (!compileOnly)
272 (void)JS_ExecuteScript(cx, obj, script, &result);
273 JS_DestroyScript(cx, script);
276 if (file != stdin)
277 fclose(file);
278 return;
281 /* It's an interactive filehandle; drop into read-eval-print loop. */
282 lineno = 1;
283 hitEOF = JS_FALSE;
284 do {
285 bufp = buffer;
286 *bufp = '\0';
289 * Accumulate lines until we get a 'compilable unit' - one that either
290 * generates an error (before running out of source) or that compiles
291 * cleanly. This should be whenever we get a complete statement that
292 * coincides with the end of a line.
294 startline = lineno;
295 do {
296 if (!GetLine(cx, bufp, file, startline == lineno ? "js> " : "")) {
297 hitEOF = JS_TRUE;
298 break;
300 bufp += strlen(bufp);
301 lineno++;
302 } while (!JS_BufferIsCompilableUnit(cx, obj, buffer, strlen(buffer)));
304 /* Clear any pending exception from previous failed compiles. */
305 JS_ClearPendingException(cx);
306 script = JS_CompileScript(cx, obj, buffer, strlen(buffer), "typein",
307 startline);
308 if (script) {
309 if (!compileOnly) {
310 ok = JS_ExecuteScript(cx, obj, script, &result);
311 if (ok && !JSVAL_IS_VOID(result)) {
312 str = JS_ValueToString(cx, result);
313 if (str)
314 fprintf(gOutFile, "%s\n", JS_GetStringBytes(str));
315 else
316 ok = JS_FALSE;
319 JS_DestroyScript(cx, script);
321 } while (!hitEOF && !gQuitting);
322 fprintf(gOutFile, "\n");
323 if (file != stdin)
324 fclose(file);
325 return;
328 static int
329 usage(void)
331 fprintf(gErrFile, "%s\n", JS_GetImplementationVersion());
332 fprintf(gErrFile, "usage: js [-zKPswWxCi] [-b branchlimit] [-c stackchunksize] [-o option] [-v version] [-f scriptfile] [-e script] [-S maxstacksize] "
333 #ifdef JS_GC_ZEAL
334 "[-Z gczeal] "
335 #endif
336 "[scriptfile] [scriptarg...]\n");
337 return 2;
340 static struct {
341 const char *name;
342 uint32 flag;
343 } js_options[] = {
344 {"strict", JSOPTION_STRICT},
345 {"werror", JSOPTION_WERROR},
346 {"atline", JSOPTION_ATLINE},
347 {"xml", JSOPTION_XML},
348 {"relimit", JSOPTION_RELIMIT},
349 {"anonfunfix", JSOPTION_ANONFUNFIX},
350 {NULL, 0}
353 extern JSClass global_class;
355 static int
356 ProcessArgs(JSContext *cx, JSObject *obj, char **argv, int argc)
358 int i, j, length;
359 JSObject *argsObj;
360 char *filename = NULL;
361 JSBool isInteractive = JS_TRUE;
362 JSBool forceTTY = JS_FALSE;
365 * Scan past all optional arguments so we can create the arguments object
366 * before processing any -f options, which must interleave properly with
367 * -v and -w options. This requires two passes, and without getopt, we'll
368 * have to keep the option logic here and in the second for loop in sync.
370 for (i = 0; i < argc; i++) {
371 if (argv[i][0] != '-' || argv[i][1] == '\0') {
372 ++i;
373 break;
375 switch (argv[i][1]) {
376 case 'b':
377 case 'c':
378 case 'f':
379 case 'e':
380 case 'v':
381 case 'S':
382 #ifdef JS_GC_ZEAL
383 case 'Z':
384 #endif
385 ++i;
386 break;
387 default:;
392 * Create arguments early and define it to root it, so it's safe from any
393 * GC calls nested below, and so it is available to -f <file> arguments.
395 argsObj = JS_NewArrayObject(cx, 0, NULL);
396 if (!argsObj)
397 return 1;
398 if (!JS_DefineProperty(cx, obj, "arguments", OBJECT_TO_JSVAL(argsObj),
399 NULL, NULL, 0)) {
400 return 1;
403 length = argc - i;
404 for (j = 0; j < length; j++) {
405 JSString *str = JS_NewStringCopyZ(cx, argv[i++]);
406 if (!str)
407 return 1;
408 if (!JS_DefineElement(cx, argsObj, j, STRING_TO_JSVAL(str),
409 NULL, NULL, JSPROP_ENUMERATE)) {
410 return 1;
414 for (i = 0; i < argc; i++) {
415 if (argv[i][0] != '-' || argv[i][1] == '\0') {
416 filename = argv[i++];
417 isInteractive = JS_FALSE;
418 break;
421 switch (argv[i][1]) {
422 case 'v':
423 if (++i == argc)
424 return usage();
426 JS_SetVersion(cx, (JSVersion) atoi(argv[i]));
427 break;
429 #ifdef JS_GC_ZEAL
430 case 'Z':
431 if (++i == argc)
432 return usage();
433 JS_SetGCZeal(cx, atoi(argv[i]));
434 break;
435 #endif
437 case 'w':
438 reportWarnings = JS_TRUE;
439 break;
441 case 'W':
442 reportWarnings = JS_FALSE;
443 break;
445 case 's':
446 JS_ToggleOptions(cx, JSOPTION_STRICT);
447 break;
449 case 'E':
450 JS_ToggleOptions(cx, JSOPTION_RELIMIT);
451 break;
453 case 'x':
454 JS_ToggleOptions(cx, JSOPTION_XML);
455 break;
457 case 'o':
458 if (++i == argc)
459 return usage();
461 for (j = 0; js_options[j].name; ++j) {
462 if (strcmp(js_options[j].name, argv[i]) == 0) {
463 JS_ToggleOptions(cx, js_options[j].flag);
464 break;
467 break;
469 case 'P':
470 if (JS_GET_CLASS(cx, JS_GetPrototype(cx, obj)) != &global_class) {
471 JSObject *gobj;
473 if (!JS_SealObject(cx, obj, JS_TRUE))
474 return JS_FALSE;
475 gobj = JS_NewObject(cx, &global_class, NULL, NULL);
476 if (!gobj)
477 return JS_FALSE;
478 if (!JS_SetPrototype(cx, gobj, obj))
479 return JS_FALSE;
480 JS_SetParent(cx, gobj, NULL);
481 JS_SetGlobalObject(cx, gobj);
482 obj = gobj;
484 break;
486 case 'b':
487 gBranchLimit = atoi(argv[++i]);
488 gEnableBranchCallback = (gBranchLimit != 0);
489 break;
491 case 'c':
492 /* set stack chunk size */
493 gStackChunkSize = atoi(argv[++i]);
494 break;
496 case 'f':
497 if (++i == argc)
498 return usage();
500 Process(cx, obj, argv[i], JS_FALSE);
503 * XXX: js -f foo.js should interpret foo.js and then
504 * drop into interactive mode, but that breaks the test
505 * harness. Just execute foo.js for now.
507 isInteractive = JS_FALSE;
508 break;
510 case 'e':
512 jsval rval;
514 if (++i == argc)
515 return usage();
517 /* Pass a filename of -e to imitate PERL */
518 JS_EvaluateScript(cx, obj, argv[i], strlen(argv[i]),
519 "-e", 1, &rval);
521 isInteractive = JS_FALSE;
522 break;
525 case 'C':
526 compileOnly = JS_TRUE;
527 isInteractive = JS_FALSE;
528 break;
530 case 'i':
531 isInteractive = forceTTY = JS_TRUE;
532 break;
534 case 'S':
535 if (++i == argc)
536 return usage();
538 /* Set maximum stack size. */
539 gMaxStackSize = atoi(argv[i]);
540 break;
542 case 'z':
543 obj = split_setup(cx);
544 if (!obj)
545 return gExitCode;
546 break;
547 #ifdef MOZ_SHARK
548 case 'k':
549 JS_ConnectShark();
550 break;
551 #endif
552 default:
553 return usage();
557 if (filename || isInteractive)
558 Process(cx, obj, filename, forceTTY);
559 return gExitCode;
562 static JSBool
563 Version(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
565 if (argc > 0 && JSVAL_IS_INT(argv[0]))
566 *rval = INT_TO_JSVAL(JS_SetVersion(cx, (JSVersion) JSVAL_TO_INT(argv[0])));
567 else
568 *rval = INT_TO_JSVAL(JS_GetVersion(cx));
569 return JS_TRUE;
572 static JSBool
573 Options(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
575 uint32 optset, flag;
576 uintN i, j, found;
577 JSString *str;
578 const char *opt;
579 char *names;
581 optset = 0;
582 for (i = 0; i < argc; i++) {
583 str = JS_ValueToString(cx, argv[i]);
584 if (!str)
585 return JS_FALSE;
586 opt = JS_GetStringBytes(str);
587 for (j = 0; js_options[j].name; j++) {
588 if (strcmp(js_options[j].name, opt) == 0) {
589 optset |= js_options[j].flag;
590 break;
594 optset = JS_ToggleOptions(cx, optset);
596 names = NULL;
597 found = 0;
598 while (optset != 0) {
599 flag = optset;
600 optset &= optset - 1;
601 flag &= ~optset;
602 for (j = 0; js_options[j].name; j++) {
603 if (js_options[j].flag == flag) {
604 names = JS_sprintf_append(names, "%s%s",
605 names ? "," : "", js_options[j].name);
606 found++;
607 break;
611 if (!found)
612 names = strdup("");
613 if (!names) {
614 JS_ReportOutOfMemory(cx);
615 return JS_FALSE;
618 str = JS_NewString(cx, names, strlen(names));
619 if (!str) {
620 free(names);
621 return JS_FALSE;
623 *rval = STRING_TO_JSVAL(str);
624 return JS_TRUE;
627 static JSBool
628 Load(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
630 uintN i;
631 JSString *str;
632 const char *filename;
633 JSScript *script;
634 JSBool ok;
635 jsval result;
636 uint32 oldopts;
638 for (i = 0; i < argc; i++) {
639 str = JS_ValueToString(cx, argv[i]);
640 if (!str)
641 return JS_FALSE;
642 argv[i] = STRING_TO_JSVAL(str);
643 filename = JS_GetStringBytes(str);
644 errno = 0;
645 oldopts = JS_GetOptions(cx);
646 JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO);
647 script = JS_CompileFile(cx, obj, filename);
648 if (!script) {
649 ok = JS_FALSE;
650 } else {
651 ok = !compileOnly
652 ? JS_ExecuteScript(cx, obj, script, &result)
653 : JS_TRUE;
654 JS_DestroyScript(cx, script);
656 JS_SetOptions(cx, oldopts);
657 if (!ok)
658 return JS_FALSE;
661 return JS_TRUE;
665 * function readline()
666 * Provides a hook for scripts to read a line from stdin.
668 static JSBool
669 ReadLine(JSContext *cx, uintN argc, jsval *vp)
671 #define BUFSIZE 256
672 FILE *from;
673 char *buf, *tmp;
674 size_t bufsize, buflength, gotlength;
675 JSBool sawNewline;
676 JSString *str;
678 from = stdin;
679 buflength = 0;
680 bufsize = BUFSIZE;
681 buf = (char *) JS_malloc(cx, bufsize);
682 if (!buf)
683 return JS_FALSE;
685 sawNewline = JS_FALSE;
686 while ((gotlength =
687 js_fgets(buf + buflength, bufsize - buflength, from)) > 0) {
688 buflength += gotlength;
690 /* Are we done? */
691 if (buf[buflength - 1] == '\n') {
692 buf[buflength - 1] = '\0';
693 sawNewline = JS_TRUE;
694 break;
695 } else if (buflength < bufsize - 1) {
696 break;
699 /* Else, grow our buffer for another pass. */
700 bufsize *= 2;
701 if (bufsize > buflength) {
702 tmp = (char *) JS_realloc(cx, buf, bufsize);
703 } else {
704 JS_ReportOutOfMemory(cx);
705 tmp = NULL;
708 if (!tmp) {
709 JS_free(cx, buf);
710 return JS_FALSE;
713 buf = tmp;
716 /* Treat the empty string specially. */
717 if (buflength == 0) {
718 *vp = feof(from) ? JSVAL_NULL : JS_GetEmptyStringValue(cx);
719 JS_free(cx, buf);
720 return JS_TRUE;
723 /* Shrink the buffer to the real size. */
724 tmp = (char *) JS_realloc(cx, buf, buflength);
725 if (!tmp) {
726 JS_free(cx, buf);
727 return JS_FALSE;
730 buf = tmp;
733 * Turn buf into a JSString. Note that buflength includes the trailing null
734 * character.
736 str = JS_NewString(cx, buf, sawNewline ? buflength - 1 : buflength);
737 if (!str) {
738 JS_free(cx, buf);
739 return JS_FALSE;
742 *vp = STRING_TO_JSVAL(str);
743 return JS_TRUE;
746 static JSBool
747 Print(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
749 uintN i;
750 JSString *str;
751 char *bytes;
753 for (i = 0; i < argc; i++) {
754 str = JS_ValueToString(cx, argv[i]);
755 if (!str)
756 return JS_FALSE;
757 bytes = JS_EncodeString(cx, str);
758 if (!bytes)
759 return JS_FALSE;
760 fprintf(gOutFile, "%s%s", i ? " " : "", bytes);
761 JS_free(cx, bytes);
764 fputc('\n', gOutFile);
765 fflush(gOutFile);
767 return JS_TRUE;
770 static JSBool
771 Help(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
773 static JSBool
774 Quit(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
776 #ifdef LIVECONNECT
777 JSJ_SimpleShutdown();
778 #endif
780 JS_ConvertArguments(cx, argc, argv,"/ i", &gExitCode);
782 gQuitting = JS_TRUE;
783 return JS_FALSE;
786 static JSBool
787 GC(JSContext *cx, uintN argc, jsval *vp)
789 JSRuntime *rt;
790 uint32 preBytes;
792 rt = cx->runtime;
793 preBytes = rt->gcBytes;
794 JS_GC(cx);
796 fprintf(gOutFile, "before %lu, after %lu, break %08lx\n",
797 (unsigned long)preBytes, (unsigned long)rt->gcBytes,
798 #ifdef XP_UNIX
799 (unsigned long)sbrk(0)
800 #else
802 #endif
804 #ifdef JS_GCMETER
805 js_DumpGCStats(rt, stdout);
806 #endif
807 *vp = JSVAL_VOID;
808 return JS_TRUE;
811 static JSBool
812 GCParameter(JSContext *cx, uintN argc, jsval *vp)
814 jsval *argv;
815 JSString *str;
816 const char *paramName;
817 JSGCParamKey param;
818 uint32 value;
820 argv = JS_ARGV(cx, vp);
821 str = JS_ValueToString(cx, argv[0]);
822 if (!str)
823 return JS_FALSE;
824 argv[0] = STRING_TO_JSVAL(str);
825 paramName = JS_GetStringBytes(str);
826 if (!paramName)
827 return JS_FALSE;
828 if (strcmp(paramName, "maxBytes") == 0) {
829 param = JSGC_MAX_BYTES;
830 } else if (strcmp(paramName, "maxMallocBytes") == 0) {
831 param = JSGC_MAX_MALLOC_BYTES;
832 } else {
833 JS_ReportError(cx,
834 "the first argument argument must be either maxBytes "
835 "or maxMallocBytes");
836 return JS_FALSE;
839 if (!JS_ValueToECMAUint32(cx, argv[1], &value))
840 return JS_FALSE;
841 if (value == 0) {
842 JS_ReportError(cx,
843 "the second argument must be convertable to uint32 with "
844 "non-zero value");
845 return JS_FALSE;
847 JS_SetGCParameter(cx->runtime, param, value);
848 *vp = JSVAL_VOID;
849 return JS_TRUE;
852 #ifdef JS_GC_ZEAL
853 static JSBool
854 GCZeal(JSContext *cx, uintN argc, jsval *vp)
856 uintN zeal;
858 if (!JS_ValueToECMAUint32(cx, vp[2], &zeal))
859 return JS_FALSE;
860 JS_SetGCZeal(cx, zeal);
861 *vp = JSVAL_VOID;
862 return JS_TRUE;
864 #endif /* JS_GC_ZEAL */
866 typedef struct JSCountHeapNode JSCountHeapNode;
868 struct JSCountHeapNode {
869 void *thing;
870 int32 kind;
871 JSCountHeapNode *next;
874 typedef struct JSCountHeapTracer {
875 JSTracer base;
876 JSDHashTable visited;
877 JSBool ok;
878 JSCountHeapNode *traceList;
879 JSCountHeapNode *recycleList;
880 } JSCountHeapTracer;
882 static void
883 CountHeapNotify(JSTracer *trc, void *thing, uint32 kind)
885 JSCountHeapTracer *countTracer;
886 JSDHashEntryStub *entry;
887 JSCountHeapNode *node;
889 JS_ASSERT(trc->callback == CountHeapNotify);
890 countTracer = (JSCountHeapTracer *)trc;
891 if (!countTracer->ok)
892 return;
894 entry = (JSDHashEntryStub *)
895 JS_DHashTableOperate(&countTracer->visited, thing, JS_DHASH_ADD);
896 if (!entry) {
897 JS_ReportOutOfMemory(trc->context);
898 countTracer->ok = JS_FALSE;
899 return;
901 if (entry->key)
902 return;
903 entry->key = thing;
905 node = countTracer->recycleList;
906 if (node) {
907 countTracer->recycleList = node->next;
908 } else {
909 node = (JSCountHeapNode *) JS_malloc(trc->context, sizeof *node);
910 if (!node) {
911 countTracer->ok = JS_FALSE;
912 return;
915 node->thing = thing;
916 node->kind = kind;
917 node->next = countTracer->traceList;
918 countTracer->traceList = node;
921 static JSBool
922 CountHeap(JSContext *cx, uintN argc, jsval *vp)
924 void* startThing;
925 int32 startTraceKind;
926 jsval v;
927 int32 traceKind, i;
928 JSString *str;
929 char *bytes;
930 JSCountHeapTracer countTracer;
931 JSCountHeapNode *node;
932 size_t counter;
934 static const struct {
935 const char *name;
936 int32 kind;
937 } traceKindNames[] = {
938 { "all", -1 },
939 { "object", JSTRACE_OBJECT },
940 { "double", JSTRACE_DOUBLE },
941 { "string", JSTRACE_STRING },
942 #if JS_HAS_XML_SUPPORT
943 { "namespace", JSTRACE_NAMESPACE },
944 { "qname", JSTRACE_QNAME },
945 { "xml", JSTRACE_XML },
946 #endif
949 startThing = NULL;
950 startTraceKind = 0;
951 if (argc > 0) {
952 v = JS_ARGV(cx, vp)[0];
953 if (JSVAL_IS_TRACEABLE(v)) {
954 startThing = JSVAL_TO_TRACEABLE(v);
955 startTraceKind = JSVAL_TRACE_KIND(v);
956 } else if (v != JSVAL_NULL) {
957 JS_ReportError(cx,
958 "the first argument is not null or a heap-allocated "
959 "thing");
960 return JS_FALSE;
964 traceKind = -1;
965 if (argc > 1) {
966 str = JS_ValueToString(cx, JS_ARGV(cx, vp)[1]);
967 if (!str)
968 return JS_FALSE;
969 bytes = JS_GetStringBytes(str);
970 if (!bytes)
971 return JS_FALSE;
972 for (i = 0; ;) {
973 if (strcmp(bytes, traceKindNames[i].name) == 0) {
974 traceKind = traceKindNames[i].kind;
975 break;
977 if (++i == JS_ARRAY_LENGTH(traceKindNames)) {
978 JS_ReportError(cx, "trace kind name '%s' is unknown", bytes);
979 return JS_FALSE;
984 JS_TRACER_INIT(&countTracer.base, cx, CountHeapNotify);
985 if (!JS_DHashTableInit(&countTracer.visited, JS_DHashGetStubOps(),
986 NULL, sizeof(JSDHashEntryStub),
987 JS_DHASH_DEFAULT_CAPACITY(100))) {
988 JS_ReportOutOfMemory(cx);
989 return JS_FALSE;
991 countTracer.ok = JS_TRUE;
992 countTracer.traceList = NULL;
993 countTracer.recycleList = NULL;
995 if (!startThing) {
996 JS_TraceRuntime(&countTracer.base);
997 } else {
998 JS_SET_TRACING_NAME(&countTracer.base, "root");
999 JS_CallTracer(&countTracer.base, startThing, startTraceKind);
1002 counter = 0;
1003 while ((node = countTracer.traceList) != NULL) {
1004 if (traceKind == -1 || node->kind == traceKind)
1005 counter++;
1006 countTracer.traceList = node->next;
1007 node->next = countTracer.recycleList;
1008 countTracer.recycleList = node;
1009 JS_TraceChildren(&countTracer.base, node->thing, node->kind);
1011 while ((node = countTracer.recycleList) != NULL) {
1012 countTracer.recycleList = node->next;
1013 JS_free(cx, node);
1015 JS_DHashTableFinish(&countTracer.visited);
1017 return countTracer.ok && JS_NewNumberValue(cx, (jsdouble) counter, vp);
1020 static JSScript *
1021 ValueToScript(JSContext *cx, jsval v)
1023 JSScript *script;
1024 JSFunction *fun;
1026 if (!JSVAL_IS_PRIMITIVE(v) &&
1027 JS_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_ScriptClass) {
1028 script = (JSScript *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v));
1029 } else {
1030 fun = JS_ValueToFunction(cx, v);
1031 if (!fun)
1032 return NULL;
1033 script = FUN_SCRIPT(fun);
1035 return script;
1038 static JSBool
1039 GetTrapArgs(JSContext *cx, uintN argc, jsval *argv, JSScript **scriptp,
1040 int32 *ip)
1042 jsval v;
1043 uintN intarg;
1044 JSScript *script;
1046 *scriptp = cx->fp->down->script;
1047 *ip = 0;
1048 if (argc != 0) {
1049 v = argv[0];
1050 intarg = 0;
1051 if (!JSVAL_IS_PRIMITIVE(v) &&
1052 (JS_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_FunctionClass ||
1053 JS_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_ScriptClass)) {
1054 script = ValueToScript(cx, v);
1055 if (!script)
1056 return JS_FALSE;
1057 *scriptp = script;
1058 intarg++;
1060 if (argc > intarg) {
1061 if (!JS_ValueToInt32(cx, argv[intarg], ip))
1062 return JS_FALSE;
1065 return JS_TRUE;
1068 static JSTrapStatus
1069 TrapHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval,
1070 void *closure)
1072 JSString *str;
1073 JSStackFrame *caller;
1075 str = (JSString *) closure;
1076 caller = JS_GetScriptedCaller(cx, NULL);
1077 if (!JS_EvaluateScript(cx, caller->scopeChain,
1078 JS_GetStringBytes(str), JS_GetStringLength(str),
1079 caller->script->filename, caller->script->lineno,
1080 rval)) {
1081 return JSTRAP_ERROR;
1083 if (!JSVAL_IS_VOID(*rval))
1084 return JSTRAP_RETURN;
1085 return JSTRAP_CONTINUE;
1088 static JSBool
1089 Trap(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1091 JSString *str;
1092 JSScript *script;
1093 int32 i;
1095 if (argc == 0) {
1096 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_TRAP_USAGE);
1097 return JS_FALSE;
1099 argc--;
1100 str = JS_ValueToString(cx, argv[argc]);
1101 if (!str)
1102 return JS_FALSE;
1103 argv[argc] = STRING_TO_JSVAL(str);
1104 if (!GetTrapArgs(cx, argc, argv, &script, &i))
1105 return JS_FALSE;
1106 return JS_SetTrap(cx, script, script->code + i, TrapHandler, str);
1109 static JSBool
1110 Untrap(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1112 JSScript *script;
1113 int32 i;
1115 if (!GetTrapArgs(cx, argc, argv, &script, &i))
1116 return JS_FALSE;
1117 JS_ClearTrap(cx, script, script->code + i, NULL, NULL);
1118 return JS_TRUE;
1121 static JSBool
1122 LineToPC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1124 JSScript *script;
1125 int32 i;
1126 uintN lineno;
1127 jsbytecode *pc;
1129 if (argc == 0) {
1130 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_LINE2PC_USAGE);
1131 return JS_FALSE;
1133 script = cx->fp->down->script;
1134 if (!GetTrapArgs(cx, argc, argv, &script, &i))
1135 return JS_FALSE;
1136 lineno = (i == 0) ? script->lineno : (uintN)i;
1137 pc = JS_LineNumberToPC(cx, script, lineno);
1138 if (!pc)
1139 return JS_FALSE;
1140 *rval = INT_TO_JSVAL(PTRDIFF(pc, script->code, jsbytecode));
1141 return JS_TRUE;
1144 static JSBool
1145 PCToLine(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1147 JSScript *script;
1148 int32 i;
1149 uintN lineno;
1151 if (!GetTrapArgs(cx, argc, argv, &script, &i))
1152 return JS_FALSE;
1153 lineno = JS_PCToLineNumber(cx, script, script->code + i);
1154 if (!lineno)
1155 return JS_FALSE;
1156 *rval = INT_TO_JSVAL(lineno);
1157 return JS_TRUE;
1160 #ifdef DEBUG
1162 static void
1163 UpdateSwitchTableBounds(JSScript *script, uintN offset,
1164 uintN *start, uintN *end)
1166 jsbytecode *pc;
1167 JSOp op;
1168 ptrdiff_t jmplen;
1169 jsint low, high, n;
1171 pc = script->code + offset;
1172 op = (JSOp) *pc;
1173 switch (op) {
1174 case JSOP_TABLESWITCHX:
1175 jmplen = JUMPX_OFFSET_LEN;
1176 goto jump_table;
1177 case JSOP_TABLESWITCH:
1178 jmplen = JUMP_OFFSET_LEN;
1179 jump_table:
1180 pc += jmplen;
1181 low = GET_JUMP_OFFSET(pc);
1182 pc += JUMP_OFFSET_LEN;
1183 high = GET_JUMP_OFFSET(pc);
1184 pc += JUMP_OFFSET_LEN;
1185 n = high - low + 1;
1186 break;
1188 case JSOP_LOOKUPSWITCHX:
1189 jmplen = JUMPX_OFFSET_LEN;
1190 goto lookup_table;
1191 case JSOP_LOOKUPSWITCH:
1192 jmplen = JUMP_OFFSET_LEN;
1193 lookup_table:
1194 pc += jmplen;
1195 n = GET_INDEX(pc);
1196 pc += INDEX_LEN;
1197 jmplen += JUMP_OFFSET_LEN;
1198 break;
1200 default:
1201 /* [condswitch] switch does not have any jump or lookup tables. */
1202 JS_ASSERT(op == JSOP_CONDSWITCH);
1203 return;
1206 *start = (uintN)(pc - script->code);
1207 *end = *start + (uintN)(n * jmplen);
1210 static void
1211 SrcNotes(JSContext *cx, JSScript *script)
1213 uintN offset, delta, caseOff, switchTableStart, switchTableEnd;
1214 jssrcnote *notes, *sn;
1215 JSSrcNoteType type;
1216 const char *name;
1217 uint32 index;
1218 JSAtom *atom;
1219 JSString *str;
1221 fprintf(gOutFile, "\nSource notes:\n");
1222 offset = 0;
1223 notes = SCRIPT_NOTES(script);
1224 switchTableEnd = switchTableStart = 0;
1225 for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
1226 delta = SN_DELTA(sn);
1227 offset += delta;
1228 type = (JSSrcNoteType) SN_TYPE(sn);
1229 name = js_SrcNoteSpec[type].name;
1230 if (type == SRC_LABEL) {
1231 /* Check if the source note is for a switch case. */
1232 if (switchTableStart <= offset && offset < switchTableEnd) {
1233 name = "case";
1234 } else {
1235 JS_ASSERT(script->code[offset] == JSOP_NOP);
1238 fprintf(gOutFile, "%3u: %5u [%4u] %-8s",
1239 PTRDIFF(sn, notes, jssrcnote), offset, delta, name);
1240 switch (type) {
1241 case SRC_SETLINE:
1242 fprintf(gOutFile, " lineno %u", (uintN) js_GetSrcNoteOffset(sn, 0));
1243 break;
1244 case SRC_FOR:
1245 fprintf(gOutFile, " cond %u update %u tail %u",
1246 (uintN) js_GetSrcNoteOffset(sn, 0),
1247 (uintN) js_GetSrcNoteOffset(sn, 1),
1248 (uintN) js_GetSrcNoteOffset(sn, 2));
1249 break;
1250 case SRC_IF_ELSE:
1251 fprintf(gOutFile, " else %u elseif %u",
1252 (uintN) js_GetSrcNoteOffset(sn, 0),
1253 (uintN) js_GetSrcNoteOffset(sn, 1));
1254 break;
1255 case SRC_COND:
1256 case SRC_WHILE:
1257 case SRC_PCBASE:
1258 case SRC_PCDELTA:
1259 case SRC_DECL:
1260 case SRC_BRACE:
1261 fprintf(gOutFile, " offset %u", (uintN) js_GetSrcNoteOffset(sn, 0));
1262 break;
1263 case SRC_LABEL:
1264 case SRC_LABELBRACE:
1265 case SRC_BREAK2LABEL:
1266 case SRC_CONT2LABEL:
1267 index = js_GetSrcNoteOffset(sn, 0);
1268 JS_GET_SCRIPT_ATOM(script, index, atom);
1269 JS_ASSERT(ATOM_IS_STRING(atom));
1270 str = ATOM_TO_STRING(atom);
1271 fprintf(gOutFile, " atom %u (", index);
1272 js_FileEscapedString(gOutFile, str, 0);
1273 putc(')', gOutFile);
1274 break;
1275 case SRC_FUNCDEF: {
1276 const char *bytes;
1277 JSObject *obj;
1278 JSFunction *fun;
1280 index = js_GetSrcNoteOffset(sn, 0);
1281 JS_GET_SCRIPT_OBJECT(script, index, obj);
1282 fun = (JSFunction *) JS_GetPrivate(cx, obj);
1283 str = JS_DecompileFunction(cx, fun, JS_DONT_PRETTY_PRINT);
1284 bytes = str ? JS_GetStringBytes(str) : "N/A";
1285 fprintf(gOutFile, " function %u (%s)", index, bytes);
1286 break;
1288 case SRC_SWITCH:
1289 fprintf(gOutFile, " length %u", (uintN) js_GetSrcNoteOffset(sn, 0));
1290 caseOff = (uintN) js_GetSrcNoteOffset(sn, 1);
1291 if (caseOff)
1292 fprintf(gOutFile, " first case offset %u", caseOff);
1293 UpdateSwitchTableBounds(script, offset,
1294 &switchTableStart, &switchTableEnd);
1295 break;
1296 case SRC_CATCH:
1297 delta = (uintN) js_GetSrcNoteOffset(sn, 0);
1298 if (delta) {
1299 if (script->main[offset] == JSOP_LEAVEBLOCK)
1300 fprintf(gOutFile, " stack depth %u", delta);
1301 else
1302 fprintf(gOutFile, " guard delta %u", delta);
1304 break;
1305 default:;
1307 fputc('\n', gOutFile);
1311 static JSBool
1312 Notes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1314 uintN i;
1315 JSScript *script;
1317 for (i = 0; i < argc; i++) {
1318 script = ValueToScript(cx, argv[i]);
1319 if (!script)
1320 continue;
1322 SrcNotes(cx, script);
1324 return JS_TRUE;
1327 JS_STATIC_ASSERT(JSTN_CATCH == 0);
1328 JS_STATIC_ASSERT(JSTN_FINALLY == 1);
1329 JS_STATIC_ASSERT(JSTN_ITER == 2);
1331 static const char* const TryNoteNames[] = { "catch", "finally", "iter" };
1333 static JSBool
1334 TryNotes(JSContext *cx, JSScript *script)
1336 JSTryNote *tn, *tnlimit;
1338 if (script->trynotesOffset == 0)
1339 return JS_TRUE;
1341 tn = JS_SCRIPT_TRYNOTES(script)->vector;
1342 tnlimit = tn + JS_SCRIPT_TRYNOTES(script)->length;
1343 fprintf(gOutFile, "\nException table:\n"
1344 "kind stack start end\n");
1345 do {
1346 JS_ASSERT(tn->kind < JS_ARRAY_LENGTH(TryNoteNames));
1347 fprintf(gOutFile, " %-7s %6u %8u %8u\n",
1348 TryNoteNames[tn->kind], tn->stackDepth,
1349 tn->start, tn->start + tn->length);
1350 } while (++tn != tnlimit);
1351 return JS_TRUE;
1354 static JSBool
1355 Disassemble(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1357 JSBool lines;
1358 uintN i;
1359 JSScript *script;
1361 if (argc > 0 &&
1362 JSVAL_IS_STRING(argv[0]) &&
1363 !strcmp(JS_GetStringBytes(JSVAL_TO_STRING(argv[0])), "-l")) {
1364 lines = JS_TRUE;
1365 argv++, argc--;
1366 } else {
1367 lines = JS_FALSE;
1369 for (i = 0; i < argc; i++) {
1370 script = ValueToScript(cx, argv[i]);
1371 if (!script)
1372 return JS_FALSE;
1374 if (VALUE_IS_FUNCTION(cx, argv[i])) {
1375 JSFunction *fun = JS_ValueToFunction(cx, argv[i]);
1376 if (fun && (fun->flags & JSFUN_FLAGS_MASK)) {
1377 uint16 flags = fun->flags;
1378 fputs("flags:", stdout);
1380 #define SHOW_FLAG(flag) if (flags & JSFUN_##flag) fputs(" " #flag, stdout);
1382 SHOW_FLAG(LAMBDA);
1383 SHOW_FLAG(SETTER);
1384 SHOW_FLAG(GETTER);
1385 SHOW_FLAG(BOUND_METHOD);
1386 SHOW_FLAG(HEAVYWEIGHT);
1387 SHOW_FLAG(THISP_STRING);
1388 SHOW_FLAG(THISP_NUMBER);
1389 SHOW_FLAG(THISP_BOOLEAN);
1390 SHOW_FLAG(EXPR_CLOSURE);
1391 SHOW_FLAG(INTERPRETED);
1393 #undef SHOW_FLAG
1394 putchar('\n');
1398 if (!js_Disassemble(cx, script, lines, stdout))
1399 return JS_FALSE;
1400 SrcNotes(cx, script);
1401 TryNotes(cx, script);
1403 return JS_TRUE;
1406 static JSBool
1407 DisassWithSrc(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1408 jsval *rval)
1410 #define LINE_BUF_LEN 512
1411 uintN i, len, line1, line2, bupline;
1412 JSScript *script;
1413 FILE *file;
1414 char linebuf[LINE_BUF_LEN];
1415 jsbytecode *pc, *end;
1416 static char sep[] = ";-------------------------";
1418 for (i = 0; i < argc; i++) {
1419 script = ValueToScript(cx, argv[i]);
1420 if (!script)
1421 return JS_FALSE;
1423 if (!script || !script->filename) {
1424 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
1425 JSSMSG_FILE_SCRIPTS_ONLY);
1426 return JS_FALSE;
1429 file = fopen(script->filename, "r");
1430 if (!file) {
1431 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
1432 JSSMSG_CANT_OPEN,
1433 script->filename, strerror(errno));
1434 return JS_FALSE;
1437 pc = script->code;
1438 end = pc + script->length;
1440 /* burn the leading lines */
1441 line2 = JS_PCToLineNumber(cx, script, pc);
1442 for (line1 = 0; line1 < line2 - 1; line1++)
1443 fgets(linebuf, LINE_BUF_LEN, file);
1445 bupline = 0;
1446 while (pc < end) {
1447 line2 = JS_PCToLineNumber(cx, script, pc);
1449 if (line2 < line1) {
1450 if (bupline != line2) {
1451 bupline = line2;
1452 fprintf(gOutFile, "%s %3u: BACKUP\n", sep, line2);
1454 } else {
1455 if (bupline && line1 == line2)
1456 fprintf(gOutFile, "%s %3u: RESTORE\n", sep, line2);
1457 bupline = 0;
1458 while (line1 < line2) {
1459 if (!fgets(linebuf, LINE_BUF_LEN, file)) {
1460 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
1461 JSSMSG_UNEXPECTED_EOF,
1462 script->filename);
1463 goto bail;
1465 line1++;
1466 fprintf(gOutFile, "%s %3u: %s", sep, line1, linebuf);
1470 len = js_Disassemble1(cx, script, pc,
1471 PTRDIFF(pc, script->code, jsbytecode),
1472 JS_TRUE, stdout);
1473 if (!len)
1474 return JS_FALSE;
1475 pc += len;
1478 bail:
1479 fclose(file);
1481 return JS_TRUE;
1482 #undef LINE_BUF_LEN
1485 static JSBool
1486 Tracing(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1488 JSBool bval;
1489 JSString *str;
1491 #if JS_THREADED_INTERP
1492 JS_ReportError(cx, "tracing not supported in JS_THREADED_INTERP builds");
1493 return JS_FALSE;
1494 #else
1495 if (argc == 0) {
1496 *rval = BOOLEAN_TO_JSVAL(cx->tracefp != 0);
1497 return JS_TRUE;
1500 switch (JS_TypeOfValue(cx, argv[0])) {
1501 case JSTYPE_NUMBER:
1502 bval = JSVAL_IS_INT(argv[0])
1503 ? JSVAL_TO_INT(argv[0])
1504 : (jsint) *JSVAL_TO_DOUBLE(argv[0]);
1505 break;
1506 case JSTYPE_BOOLEAN:
1507 bval = JSVAL_TO_BOOLEAN(argv[0]);
1508 break;
1509 default:
1510 str = JS_ValueToString(cx, argv[0]);
1511 if (!str)
1512 return JS_FALSE;
1513 JS_ReportError(cx, "tracing: illegal argument %s",
1514 JS_GetStringBytes(str));
1515 return JS_FALSE;
1517 cx->tracefp = bval ? stderr : NULL;
1518 return JS_TRUE;
1519 #endif
1522 static void
1523 DumpScope(JSContext *cx, JSObject *obj, FILE *fp)
1525 uintN i;
1526 JSScope *scope;
1527 JSScopeProperty *sprop;
1528 jsval v;
1529 JSString *str;
1531 i = 0;
1532 scope = OBJ_SCOPE(obj);
1533 for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) {
1534 if (SCOPE_HAD_MIDDLE_DELETE(scope) && !SCOPE_HAS_PROPERTY(scope, sprop))
1535 continue;
1536 fprintf(fp, "%3u %p ", i, (void *)sprop);
1538 v = ID_TO_VALUE(sprop->id);
1539 if (JSID_IS_INT(sprop->id)) {
1540 fprintf(fp, "[%ld]", (long)JSVAL_TO_INT(v));
1541 } else {
1542 if (JSID_IS_ATOM(sprop->id)) {
1543 str = JSVAL_TO_STRING(v);
1544 } else {
1545 JS_ASSERT(JSID_IS_OBJECT(sprop->id));
1546 str = js_ValueToString(cx, v);
1547 fputs("object ", fp);
1549 if (!str)
1550 fputs("<error>", fp);
1551 else
1552 js_FileEscapedString(fp, str, '"');
1554 #define DUMP_ATTR(name) if (sprop->attrs & JSPROP_##name) fputs(" " #name, fp)
1555 DUMP_ATTR(ENUMERATE);
1556 DUMP_ATTR(READONLY);
1557 DUMP_ATTR(PERMANENT);
1558 DUMP_ATTR(EXPORTED);
1559 DUMP_ATTR(GETTER);
1560 DUMP_ATTR(SETTER);
1561 #undef DUMP_ATTR
1563 fprintf(fp, " slot %lu flags %x shortid %d\n",
1564 (unsigned long)sprop->slot, sprop->flags, sprop->shortid);
1568 static JSBool
1569 DumpStats(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1571 uintN i;
1572 JSString *str;
1573 const char *bytes;
1574 jsid id;
1575 JSObject *obj2;
1576 JSProperty *prop;
1577 jsval value;
1579 for (i = 0; i < argc; i++) {
1580 str = JS_ValueToString(cx, argv[i]);
1581 if (!str)
1582 return JS_FALSE;
1583 argv[i] = STRING_TO_JSVAL(str);
1584 bytes = JS_GetStringBytes(str);
1585 if (strcmp(bytes, "arena") == 0) {
1586 #ifdef JS_ARENAMETER
1587 JS_DumpArenaStats(stdout);
1588 #endif
1589 } else if (strcmp(bytes, "atom") == 0) {
1590 js_DumpAtoms(cx, gOutFile);
1591 } else if (strcmp(bytes, "global") == 0) {
1592 DumpScope(cx, cx->globalObject, stdout);
1593 } else {
1594 if (!JS_ValueToId(cx, STRING_TO_JSVAL(str), &id))
1595 return JS_FALSE;
1596 if (!js_FindProperty(cx, id, &obj, &obj2, &prop))
1597 return JS_FALSE;
1598 if (prop) {
1599 OBJ_DROP_PROPERTY(cx, obj2, prop);
1600 if (!OBJ_GET_PROPERTY(cx, obj, id, &value))
1601 return JS_FALSE;
1603 if (!prop || !JSVAL_IS_OBJECT(value)) {
1604 fprintf(gErrFile, "js: invalid stats argument %s\n",
1605 bytes);
1606 continue;
1608 obj = JSVAL_TO_OBJECT(value);
1609 if (obj)
1610 DumpScope(cx, obj, stdout);
1613 return JS_TRUE;
1616 static JSBool
1617 DumpHeap(JSContext *cx, uintN argc, jsval *vp)
1619 char *fileName;
1620 jsval v;
1621 void* startThing;
1622 uint32 startTraceKind;
1623 const char *badTraceArg;
1624 void *thingToFind;
1625 size_t maxDepth;
1626 void *thingToIgnore;
1627 FILE *dumpFile;
1628 JSBool ok;
1630 fileName = NULL;
1631 if (argc > 0) {
1632 v = JS_ARGV(cx, vp)[0];
1633 if (v != JSVAL_NULL) {
1634 JSString *str;
1636 str = JS_ValueToString(cx, v);
1637 if (!str)
1638 return JS_FALSE;
1639 JS_ARGV(cx, vp)[0] = STRING_TO_JSVAL(str);
1640 fileName = JS_GetStringBytes(str);
1644 startThing = NULL;
1645 startTraceKind = 0;
1646 if (argc > 1) {
1647 v = JS_ARGV(cx, vp)[1];
1648 if (JSVAL_IS_TRACEABLE(v)) {
1649 startThing = JSVAL_TO_TRACEABLE(v);
1650 startTraceKind = JSVAL_TRACE_KIND(v);
1651 } else if (v != JSVAL_NULL) {
1652 badTraceArg = "start";
1653 goto not_traceable_arg;
1657 thingToFind = NULL;
1658 if (argc > 2) {
1659 v = JS_ARGV(cx, vp)[2];
1660 if (JSVAL_IS_TRACEABLE(v)) {
1661 thingToFind = JSVAL_TO_TRACEABLE(v);
1662 } else if (v != JSVAL_NULL) {
1663 badTraceArg = "toFind";
1664 goto not_traceable_arg;
1668 maxDepth = (size_t)-1;
1669 if (argc > 3) {
1670 v = JS_ARGV(cx, vp)[3];
1671 if (v != JSVAL_NULL) {
1672 uint32 depth;
1674 if (!JS_ValueToECMAUint32(cx, v, &depth))
1675 return JS_FALSE;
1676 maxDepth = depth;
1680 thingToIgnore = NULL;
1681 if (argc > 4) {
1682 v = JS_ARGV(cx, vp)[4];
1683 if (JSVAL_IS_TRACEABLE(v)) {
1684 thingToIgnore = JSVAL_TO_TRACEABLE(v);
1685 } else if (v != JSVAL_NULL) {
1686 badTraceArg = "toIgnore";
1687 goto not_traceable_arg;
1691 if (!fileName) {
1692 dumpFile = stdout;
1693 } else {
1694 dumpFile = fopen(fileName, "w");
1695 if (!dumpFile) {
1696 JS_ReportError(cx, "can't open %s: %s", fileName, strerror(errno));
1697 return JS_FALSE;
1701 ok = JS_DumpHeap(cx, dumpFile, startThing, startTraceKind, thingToFind,
1702 maxDepth, thingToIgnore);
1703 if (dumpFile != stdout)
1704 fclose(dumpFile);
1705 return ok;
1707 not_traceable_arg:
1708 JS_ReportError(cx, "argument '%s' is not null or a heap-allocated thing",
1709 badTraceArg);
1710 return JS_FALSE;
1713 #endif /* DEBUG */
1715 #ifdef TEST_EXPORT
1716 static JSBool
1717 DoExport(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1719 jsid id;
1720 JSObject *obj2;
1721 JSProperty *prop;
1722 JSBool ok;
1723 uintN attrs;
1725 if (argc != 2) {
1726 JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_DOEXP_USAGE);
1727 return JS_FALSE;
1729 if (!JS_ValueToObject(cx, argv[0], &obj))
1730 return JS_FALSE;
1731 argv[0] = OBJECT_TO_JSVAL(obj);
1732 if (!js_ValueToStringId(cx, argv[1], &id))
1733 return JS_FALSE;
1734 if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
1735 return JS_FALSE;
1736 if (!prop) {
1737 ok = OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, NULL, NULL,
1738 JSPROP_EXPORTED, NULL);
1739 } else {
1740 ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, &attrs);
1741 if (ok) {
1742 attrs |= JSPROP_EXPORTED;
1743 ok = OBJ_SET_ATTRIBUTES(cx, obj, id, prop, &attrs);
1745 OBJ_DROP_PROPERTY(cx, obj2, prop);
1747 return ok;
1749 #endif
1751 #ifdef TEST_CVTARGS
1752 #include <ctype.h>
1754 static const char *
1755 EscapeWideString(jschar *w)
1757 static char enuf[80];
1758 static char hex[] = "0123456789abcdef";
1759 jschar u;
1760 unsigned char b, c;
1761 int i, j;
1763 if (!w)
1764 return "";
1765 for (i = j = 0; i < sizeof enuf - 1; i++, j++) {
1766 u = w[j];
1767 if (u == 0)
1768 break;
1769 b = (unsigned char)(u >> 8);
1770 c = (unsigned char)(u);
1771 if (b) {
1772 if (i >= sizeof enuf - 6)
1773 break;
1774 enuf[i++] = '\\';
1775 enuf[i++] = 'u';
1776 enuf[i++] = hex[b >> 4];
1777 enuf[i++] = hex[b & 15];
1778 enuf[i++] = hex[c >> 4];
1779 enuf[i] = hex[c & 15];
1780 } else if (!isprint(c)) {
1781 if (i >= sizeof enuf - 4)
1782 break;
1783 enuf[i++] = '\\';
1784 enuf[i++] = 'x';
1785 enuf[i++] = hex[c >> 4];
1786 enuf[i] = hex[c & 15];
1787 } else {
1788 enuf[i] = (char)c;
1791 enuf[i] = 0;
1792 return enuf;
1795 #include <stdarg.h>
1797 static JSBool
1798 ZZ_formatter(JSContext *cx, const char *format, JSBool fromJS, jsval **vpp,
1799 va_list *app)
1801 jsval *vp;
1802 va_list ap;
1803 jsdouble re, im;
1805 printf("entering ZZ_formatter");
1806 vp = *vpp;
1807 ap = *app;
1808 if (fromJS) {
1809 if (!JS_ValueToNumber(cx, vp[0], &re))
1810 return JS_FALSE;
1811 if (!JS_ValueToNumber(cx, vp[1], &im))
1812 return JS_FALSE;
1813 *va_arg(ap, jsdouble *) = re;
1814 *va_arg(ap, jsdouble *) = im;
1815 } else {
1816 re = va_arg(ap, jsdouble);
1817 im = va_arg(ap, jsdouble);
1818 if (!JS_NewNumberValue(cx, re, &vp[0]))
1819 return JS_FALSE;
1820 if (!JS_NewNumberValue(cx, im, &vp[1]))
1821 return JS_FALSE;
1823 *vpp = vp + 2;
1824 *app = ap;
1825 printf("leaving ZZ_formatter");
1826 return JS_TRUE;
1829 static JSBool
1830 ConvertArgs(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1832 JSBool b = JS_FALSE;
1833 jschar c = 0;
1834 int32 i = 0, j = 0;
1835 uint32 u = 0;
1836 jsdouble d = 0, I = 0, re = 0, im = 0;
1837 char *s = NULL;
1838 JSString *str = NULL;
1839 jschar *w = NULL;
1840 JSObject *obj2 = NULL;
1841 JSFunction *fun = NULL;
1842 jsval v = JSVAL_VOID;
1843 JSBool ok;
1845 if (!JS_AddArgumentFormatter(cx, "ZZ", ZZ_formatter))
1846 return JS_FALSE;;
1847 ok = JS_ConvertArguments(cx, argc, argv, "b/ciujdIsSWofvZZ*",
1848 &b, &c, &i, &u, &j, &d, &I, &s, &str, &w, &obj2,
1849 &fun, &v, &re, &im);
1850 JS_RemoveArgumentFormatter(cx, "ZZ");
1851 if (!ok)
1852 return JS_FALSE;
1853 fprintf(gOutFile,
1854 "b %u, c %x (%c), i %ld, u %lu, j %ld\n",
1855 b, c, (char)c, i, u, j);
1856 fprintf(gOutFile,
1857 "d %g, I %g, s %s, S %s, W %s, obj %s, fun %s\n"
1858 "v %s, re %g, im %g\n",
1859 d, I, s, str ? JS_GetStringBytes(str) : "", EscapeWideString(w),
1860 JS_GetStringBytes(JS_ValueToString(cx, OBJECT_TO_JSVAL(obj2))),
1861 fun ? JS_GetStringBytes(JS_DecompileFunction(cx, fun, 4)) : "",
1862 JS_GetStringBytes(JS_ValueToString(cx, v)), re, im);
1863 return JS_TRUE;
1865 #endif
1867 static JSBool
1868 BuildDate(JSContext *cx, uintN argc, jsval *vp)
1870 char version[20] = "\n";
1871 #if JS_VERSION < 150
1872 sprintf(version, " for version %d\n", JS_VERSION);
1873 #endif
1874 fprintf(gOutFile, "built on %s at %s%s", __DATE__, __TIME__, version);
1875 *vp = JSVAL_VOID;
1876 return JS_TRUE;
1879 static JSBool
1880 Clear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1882 if (argc != 0 && !JS_ValueToObject(cx, argv[0], &obj))
1883 return JS_FALSE;
1884 JS_ClearScope(cx, obj);
1885 return JS_TRUE;
1888 static JSBool
1889 Intern(JSContext *cx, uintN argc, jsval *vp)
1891 JSString *str;
1893 str = JS_ValueToString(cx, vp[2]);
1894 if (!str)
1895 return JS_FALSE;
1896 if (!JS_InternUCStringN(cx, JS_GetStringChars(str),
1897 JS_GetStringLength(str))) {
1898 return JS_FALSE;
1900 *vp = JSVAL_VOID;
1901 return JS_TRUE;
1904 static JSBool
1905 Clone(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1907 JSFunction *fun;
1908 JSObject *funobj, *parent, *clone;
1910 fun = JS_ValueToFunction(cx, argv[0]);
1911 if (!fun)
1912 return JS_FALSE;
1913 funobj = JS_GetFunctionObject(fun);
1914 if (argc > 1) {
1915 if (!JS_ValueToObject(cx, argv[1], &parent))
1916 return JS_FALSE;
1917 } else {
1918 parent = JS_GetParent(cx, funobj);
1920 clone = JS_CloneFunctionObject(cx, funobj, parent);
1921 if (!clone)
1922 return JS_FALSE;
1923 *rval = OBJECT_TO_JSVAL(clone);
1924 return JS_TRUE;
1927 static JSBool
1928 Seal(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1930 JSObject *target;
1931 JSBool deep = JS_FALSE;
1933 if (!JS_ConvertArguments(cx, argc, argv, "o/b", &target, &deep))
1934 return JS_FALSE;
1935 if (!target)
1936 return JS_TRUE;
1937 return JS_SealObject(cx, target, deep);
1940 static JSBool
1941 GetPDA(JSContext *cx, uintN argc, jsval *vp)
1943 JSObject *vobj, *aobj, *pdobj;
1944 JSBool ok;
1945 JSPropertyDescArray pda;
1946 JSPropertyDesc *pd;
1947 uint32 i;
1948 jsval v;
1950 if (!JS_ValueToObject(cx, vp[2], &vobj))
1951 return JS_FALSE;
1952 if (!vobj)
1953 return JS_TRUE;
1955 aobj = JS_NewArrayObject(cx, 0, NULL);
1956 if (!aobj)
1957 return JS_FALSE;
1958 *vp = OBJECT_TO_JSVAL(aobj);
1960 ok = JS_GetPropertyDescArray(cx, vobj, &pda);
1961 if (!ok)
1962 return JS_FALSE;
1963 pd = pda.array;
1964 for (i = 0; i < pda.length; i++) {
1965 pdobj = JS_NewObject(cx, NULL, NULL, NULL);
1966 if (!pdobj) {
1967 ok = JS_FALSE;
1968 break;
1971 /* Protect pdobj from GC by setting it as an element of aobj now */
1972 v = OBJECT_TO_JSVAL(pdobj);
1973 ok = JS_SetElement(cx, aobj, i, &v);
1974 if (!ok)
1975 break;
1977 ok = JS_SetProperty(cx, pdobj, "id", &pd->id) &&
1978 JS_SetProperty(cx, pdobj, "value", &pd->value) &&
1979 (v = INT_TO_JSVAL(pd->flags),
1980 JS_SetProperty(cx, pdobj, "flags", &v)) &&
1981 (v = INT_TO_JSVAL(pd->slot),
1982 JS_SetProperty(cx, pdobj, "slot", &v)) &&
1983 JS_SetProperty(cx, pdobj, "alias", &pd->alias);
1984 if (!ok)
1985 break;
1987 JS_PutPropertyDescArray(cx, &pda);
1988 return ok;
1991 static JSBool
1992 GetSLX(JSContext *cx, uintN argc, jsval *vp)
1994 JSScript *script;
1996 script = ValueToScript(cx, vp[2]);
1997 if (!script)
1998 return JS_FALSE;
1999 *vp = INT_TO_JSVAL(js_GetScriptLineExtent(script));
2000 return JS_TRUE;
2003 static JSBool
2004 ToInt32(JSContext *cx, uintN argc, jsval *vp)
2006 int32 i;
2008 if (!JS_ValueToInt32(cx, vp[2], &i))
2009 return JS_FALSE;
2010 return JS_NewNumberValue(cx, i, vp);
2013 static JSBool
2014 StringsAreUTF8(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
2015 jsval *rval)
2017 *rval = JS_CStringsAreUTF8() ? JSVAL_TRUE : JSVAL_FALSE;
2018 return JS_TRUE;
2021 static JSBool
2022 StackQuota(JSContext *cx, uintN argc, jsval *vp)
2024 uint32 n;
2026 if (argc == 0)
2027 return JS_NewNumberValue(cx, (double) gScriptStackQuota, vp);
2028 if (!JS_ValueToECMAUint32(cx, JS_ARGV(cx, vp)[0], &n))
2029 return JS_FALSE;
2030 gScriptStackQuota = n;
2031 JS_SetScriptStackQuota(cx, gScriptStackQuota);
2032 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2033 return JS_TRUE;
2036 static const char* badUTF8 = "...\xC0...";
2037 static const char* bigUTF8 = "...\xFB\xBF\xBF\xBF\xBF...";
2038 static const jschar badSurrogate[] = { 'A', 'B', 'C', 0xDEEE, 'D', 'E', 0 };
2040 static JSBool
2041 TestUTF8(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
2043 intN mode = 1;
2044 jschar chars[20];
2045 size_t charsLength = 5;
2046 char bytes[20];
2047 size_t bytesLength = 20;
2048 if (argc && !JS_ValueToInt32(cx, *argv, &mode))
2049 return JS_FALSE;
2051 /* The following throw errors if compiled with UTF-8. */
2052 switch (mode) {
2053 /* mode 1: malformed UTF-8 string. */
2054 case 1:
2055 JS_NewStringCopyZ(cx, badUTF8);
2056 break;
2057 /* mode 2: big UTF-8 character. */
2058 case 2:
2059 JS_NewStringCopyZ(cx, bigUTF8);
2060 break;
2061 /* mode 3: bad surrogate character. */
2062 case 3:
2063 JS_EncodeCharacters(cx, badSurrogate, 6, bytes, &bytesLength);
2064 break;
2065 /* mode 4: use a too small buffer. */
2066 case 4:
2067 JS_DecodeBytes(cx, "1234567890", 10, chars, &charsLength);
2068 break;
2069 default:
2070 JS_ReportError(cx, "invalid mode parameter");
2071 return JS_FALSE;
2073 return !JS_IsExceptionPending (cx);
2076 static JSBool
2077 ThrowError(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
2079 JS_ReportError(cx, "This is an error");
2080 return JS_FALSE;
2083 #define LAZY_STANDARD_CLASSES
2085 /* A class for easily testing the inner/outer object callbacks. */
2086 typedef struct ComplexObject {
2087 JSBool isInner;
2088 JSBool frozen;
2089 JSObject *inner;
2090 JSObject *outer;
2091 } ComplexObject;
2093 static JSObject *
2094 split_create_outer(JSContext *cx);
2096 static JSObject *
2097 split_create_inner(JSContext *cx, JSObject *outer);
2099 static ComplexObject *
2100 split_get_private(JSContext *cx, JSObject *obj);
2102 JS_STATIC_DLL_CALLBACK(JSBool)
2103 split_addProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
2105 ComplexObject *cpx;
2106 jsid asId;
2108 cpx = split_get_private(cx, obj);
2109 if (!cpx)
2110 return JS_TRUE;
2111 if (!cpx->isInner && cpx->inner) {
2112 /* Make sure to define this property on the inner object. */
2113 if (!JS_ValueToId(cx, *vp, &asId))
2114 return JS_FALSE;
2115 return OBJ_DEFINE_PROPERTY(cx, cpx->inner, asId, *vp, NULL, NULL,
2116 JSPROP_ENUMERATE, NULL);
2118 return JS_TRUE;
2121 JS_STATIC_DLL_CALLBACK(JSBool)
2122 split_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
2124 ComplexObject *cpx;
2126 cpx = split_get_private(cx, obj);
2127 if (!cpx)
2128 return JS_TRUE;
2129 if (!cpx->isInner && cpx->inner) {
2130 if (JSVAL_IS_STRING(id)) {
2131 JSString *str;
2133 str = JSVAL_TO_STRING(id);
2134 return JS_GetUCProperty(cx, cpx->inner, JS_GetStringChars(str),
2135 JS_GetStringLength(str), vp);
2137 if (JSVAL_IS_INT(id))
2138 return JS_GetElement(cx, cpx->inner, JSVAL_TO_INT(id), vp);
2139 return JS_TRUE;
2142 return JS_TRUE;
2145 JS_STATIC_DLL_CALLBACK(JSBool)
2146 split_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
2148 ComplexObject *cpx;
2150 cpx = split_get_private(cx, obj);
2151 if (!cpx)
2152 return JS_TRUE;
2153 if (!cpx->isInner && cpx->inner) {
2154 if (JSVAL_IS_STRING(id)) {
2155 JSString *str;
2157 str = JSVAL_TO_STRING(id);
2158 return JS_SetUCProperty(cx, cpx->inner, JS_GetStringChars(str),
2159 JS_GetStringLength(str), vp);
2161 if (JSVAL_IS_INT(id))
2162 return JS_SetElement(cx, cpx->inner, JSVAL_TO_INT(id), vp);
2163 return JS_TRUE;
2166 return JS_TRUE;
2169 JS_STATIC_DLL_CALLBACK(JSBool)
2170 split_delProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
2172 ComplexObject *cpx;
2173 jsid asId;
2175 cpx = split_get_private(cx, obj);
2176 if (!cpx)
2177 return JS_TRUE;
2178 if (!cpx->isInner && cpx->inner) {
2179 /* Make sure to define this property on the inner object. */
2180 if (!JS_ValueToId(cx, *vp, &asId))
2181 return JS_FALSE;
2182 return OBJ_DELETE_PROPERTY(cx, cpx->inner, asId, vp);
2184 return JS_TRUE;
2187 JS_STATIC_DLL_CALLBACK(JSBool)
2188 split_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
2189 jsval *statep, jsid *idp)
2191 ComplexObject *cpx;
2192 JSObject *iterator;
2194 switch (enum_op) {
2195 case JSENUMERATE_INIT:
2196 cpx = (ComplexObject *) JS_GetPrivate(cx, obj);
2198 if (!cpx->isInner && cpx->inner)
2199 obj = cpx->inner;
2201 iterator = JS_NewPropertyIterator(cx, obj);
2202 if (!iterator)
2203 return JS_FALSE;
2205 *statep = OBJECT_TO_JSVAL(iterator);
2206 if (idp)
2207 *idp = JSVAL_ZERO;
2208 break;
2210 case JSENUMERATE_NEXT:
2211 iterator = (JSObject*)JSVAL_TO_OBJECT(*statep);
2212 if (!JS_NextProperty(cx, iterator, idp))
2213 return JS_FALSE;
2215 if (!JSVAL_IS_VOID(*idp))
2216 break;
2217 /* Fall through. */
2219 case JSENUMERATE_DESTROY:
2220 /* Let GC at our iterator object. */
2221 *statep = JSVAL_NULL;
2222 break;
2225 return JS_TRUE;
2228 JS_STATIC_DLL_CALLBACK(JSBool)
2229 split_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
2230 JSObject **objp)
2232 ComplexObject *cpx;
2234 cpx = split_get_private(cx, obj);
2235 if (!cpx)
2236 return JS_TRUE;
2237 if (!cpx->isInner && cpx->inner) {
2238 jsid asId;
2239 JSProperty *prop;
2241 if (!JS_ValueToId(cx, id, &asId))
2242 return JS_FALSE;
2244 if (!OBJ_LOOKUP_PROPERTY(cx, cpx->inner, asId, objp, &prop))
2245 return JS_FALSE;
2246 if (prop)
2247 OBJ_DROP_PROPERTY(cx, cpx->inner, prop);
2249 return JS_TRUE;
2252 #ifdef LAZY_STANDARD_CLASSES
2253 if (!(flags & JSRESOLVE_ASSIGNING)) {
2254 JSBool resolved;
2256 if (!JS_ResolveStandardClass(cx, obj, id, &resolved))
2257 return JS_FALSE;
2259 if (resolved) {
2260 *objp = obj;
2261 return JS_TRUE;
2264 #endif
2266 /* XXX For additional realism, let's resolve some random property here. */
2267 return JS_TRUE;
2270 JS_STATIC_DLL_CALLBACK(void)
2271 split_finalize(JSContext *cx, JSObject *obj)
2273 JS_free(cx, JS_GetPrivate(cx, obj));
2276 JS_STATIC_DLL_CALLBACK(uint32)
2277 split_mark(JSContext *cx, JSObject *obj, void *arg)
2279 ComplexObject *cpx;
2281 cpx = (ComplexObject *) JS_GetPrivate(cx, obj);
2283 if (!cpx->isInner && cpx->inner) {
2284 /* Mark the inner object. */
2285 JS_MarkGCThing(cx, cpx->inner, "ComplexObject.inner", arg);
2288 return 0;
2291 JS_STATIC_DLL_CALLBACK(JSObject *)
2292 split_outerObject(JSContext *cx, JSObject *obj)
2294 ComplexObject *cpx;
2296 cpx = (ComplexObject *) JS_GetPrivate(cx, obj);
2297 return cpx->isInner ? cpx->outer : obj;
2300 JS_STATIC_DLL_CALLBACK(JSObject *)
2301 split_innerObject(JSContext *cx, JSObject *obj)
2303 ComplexObject *cpx;
2305 cpx = (ComplexObject *) JS_GetPrivate(cx, obj);
2306 if (cpx->frozen) {
2307 JS_ASSERT(!cpx->isInner);
2308 return obj;
2310 return !cpx->isInner ? cpx->inner : obj;
2313 static JSExtendedClass split_global_class = {
2314 {"split_global",
2315 JSCLASS_NEW_RESOLVE | JSCLASS_NEW_ENUMERATE | JSCLASS_HAS_PRIVATE |
2316 JSCLASS_IS_EXTENDED,
2317 split_addProperty, split_delProperty,
2318 split_getProperty, split_setProperty,
2319 (JSEnumerateOp)split_enumerate,
2320 (JSResolveOp)split_resolve,
2321 JS_ConvertStub, split_finalize,
2322 NULL, NULL, NULL, NULL, NULL, NULL,
2323 split_mark, NULL},
2324 NULL, split_outerObject, split_innerObject,
2325 NULL, NULL, NULL, NULL, NULL
2328 JSObject *
2329 split_create_outer(JSContext *cx)
2331 ComplexObject *cpx;
2332 JSObject *obj;
2334 cpx = (ComplexObject *) JS_malloc(cx, sizeof *obj);
2335 if (!cpx)
2336 return NULL;
2337 cpx->isInner = JS_FALSE;
2338 cpx->frozen = JS_TRUE;
2339 cpx->inner = NULL;
2340 cpx->outer = NULL;
2342 obj = JS_NewObject(cx, &split_global_class.base, NULL, NULL);
2343 if (!obj || !JS_SetParent(cx, obj, NULL)) {
2344 JS_free(cx, cpx);
2345 return NULL;
2348 if (!JS_SetPrivate(cx, obj, cpx)) {
2349 JS_free(cx, cpx);
2350 return NULL;
2353 return obj;
2356 static JSObject *
2357 split_create_inner(JSContext *cx, JSObject *outer)
2359 ComplexObject *cpx, *outercpx;
2360 JSObject *obj;
2362 JS_ASSERT(JS_GET_CLASS(cx, outer) == &split_global_class.base);
2364 cpx = (ComplexObject *) JS_malloc(cx, sizeof *cpx);
2365 if (!cpx)
2366 return NULL;
2367 cpx->isInner = JS_TRUE;
2368 cpx->frozen = JS_FALSE;
2369 cpx->inner = NULL;
2370 cpx->outer = outer;
2372 obj = JS_NewObject(cx, &split_global_class.base, NULL, NULL);
2373 if (!obj || !JS_SetParent(cx, obj, NULL) || !JS_SetPrivate(cx, obj, cpx)) {
2374 JS_free(cx, cpx);
2375 return NULL;
2378 outercpx = (ComplexObject *) JS_GetPrivate(cx, outer);
2379 outercpx->inner = obj;
2380 outercpx->frozen = JS_FALSE;
2382 return obj;
2385 static ComplexObject *
2386 split_get_private(JSContext *cx, JSObject *obj)
2388 do {
2389 if (JS_GET_CLASS(cx, obj) == &split_global_class.base)
2390 return (ComplexObject *) JS_GetPrivate(cx, obj);
2391 obj = JS_GetParent(cx, obj);
2392 } while (obj);
2394 return NULL;
2397 static JSBool
2398 sandbox_enumerate(JSContext *cx, JSObject *obj)
2400 jsval v;
2401 JSBool b;
2403 if (!JS_GetProperty(cx, obj, "lazy", &v) || !JS_ValueToBoolean(cx, v, &b))
2404 return JS_FALSE;
2405 return !b || JS_EnumerateStandardClasses(cx, obj);
2408 static JSBool
2409 sandbox_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
2410 JSObject **objp)
2412 jsval v;
2413 JSBool b, resolved;
2415 if (!JS_GetProperty(cx, obj, "lazy", &v) || !JS_ValueToBoolean(cx, v, &b))
2416 return JS_FALSE;
2417 if (b && (flags & JSRESOLVE_ASSIGNING) == 0) {
2418 if (!JS_ResolveStandardClass(cx, obj, id, &resolved))
2419 return JS_FALSE;
2420 if (resolved) {
2421 *objp = obj;
2422 return JS_TRUE;
2425 *objp = NULL;
2426 return JS_TRUE;
2429 static JSClass sandbox_class = {
2430 "sandbox",
2431 JSCLASS_NEW_RESOLVE,
2432 JS_PropertyStub, JS_PropertyStub,
2433 JS_PropertyStub, JS_PropertyStub,
2434 sandbox_enumerate, (JSResolveOp)sandbox_resolve,
2435 JS_ConvertStub, JS_FinalizeStub,
2436 JSCLASS_NO_OPTIONAL_MEMBERS
2439 static JSBool
2440 EvalInContext(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
2441 jsval *rval)
2443 JSString *str;
2444 JSObject *sobj;
2445 JSContext *scx;
2446 const jschar *src;
2447 size_t srclen;
2448 JSBool lazy, ok;
2449 jsval v;
2450 JSStackFrame *fp;
2452 sobj = NULL;
2453 if (!JS_ConvertArguments(cx, argc, argv, "S / o", &str, &sobj))
2454 return JS_FALSE;
2456 scx = JS_NewContext(JS_GetRuntime(cx), gStackChunkSize);
2457 if (!scx) {
2458 JS_ReportOutOfMemory(cx);
2459 return JS_FALSE;
2462 #ifdef JS_THREADSAFE
2463 JS_BeginRequest(scx);
2464 #endif
2465 src = JS_GetStringChars(str);
2466 srclen = JS_GetStringLength(str);
2467 lazy = JS_FALSE;
2468 if (srclen == 4 &&
2469 src[0] == 'l' && src[1] == 'a' && src[2] == 'z' && src[3] == 'y') {
2470 lazy = JS_TRUE;
2471 srclen = 0;
2474 if (!sobj) {
2475 sobj = JS_NewObject(scx, &sandbox_class, NULL, NULL);
2476 if (!sobj || (!lazy && !JS_InitStandardClasses(scx, sobj))) {
2477 ok = JS_FALSE;
2478 goto out;
2480 v = BOOLEAN_TO_JSVAL(lazy);
2481 ok = JS_SetProperty(cx, sobj, "lazy", &v);
2482 if (!ok)
2483 goto out;
2486 if (srclen == 0) {
2487 *rval = OBJECT_TO_JSVAL(sobj);
2488 ok = JS_TRUE;
2489 } else {
2490 fp = JS_GetScriptedCaller(cx, NULL);
2491 JS_SetGlobalObject(scx, sobj);
2492 JS_ToggleOptions(scx, JSOPTION_DONT_REPORT_UNCAUGHT);
2493 ok = JS_EvaluateUCScript(scx, sobj, src, srclen,
2494 fp->script->filename,
2495 JS_PCToLineNumber(cx, fp->script,
2496 fp->regs->pc),
2497 rval);
2498 if (!ok) {
2499 if (JS_GetPendingException(scx, &v))
2500 JS_SetPendingException(cx, v);
2501 else
2502 JS_ReportOutOfMemory(cx);
2506 out:
2507 #ifdef JS_THREADSAFE
2508 JS_EndRequest(scx);
2509 #endif
2510 JS_DestroyContext(scx);
2511 return ok;
2514 #ifdef JS_THREADSAFE
2516 static JSBool
2517 Sleep(JSContext *cx, uintN argc, jsval *vp)
2519 jsdouble t_secs;
2520 PRUint32 t_ticks;
2521 jsrefcount rc;
2523 if (!JS_ConvertArguments(cx, argc, JS_ARGV(cx, vp), "d", &t_secs))
2524 return JS_FALSE;
2526 if (t_secs < 0 || JSDOUBLE_IS_NaN(t_secs))
2527 t_secs = 0;
2529 rc = JS_SuspendRequest(cx);
2530 t_ticks = (PRUint32)(PR_TicksPerSecond() * t_secs);
2531 if (PR_Sleep(t_ticks) == PR_SUCCESS)
2532 *vp = JSVAL_TRUE;
2533 else
2534 *vp = JSVAL_FALSE;
2535 JS_ResumeRequest(cx, rc);
2536 return JS_TRUE;
2539 typedef struct ScatterThreadData ScatterThreadData;
2540 typedef struct ScatterData ScatterData;
2542 typedef enum ScatterStatus {
2543 SCATTER_WAIT,
2544 SCATTER_GO,
2545 SCATTER_CANCEL
2546 } ScatterStatus;
2548 struct ScatterData {
2549 ScatterThreadData *threads;
2550 jsval *results;
2551 PRLock *lock;
2552 PRCondVar *cvar;
2553 ScatterStatus status;
2556 struct ScatterThreadData {
2557 jsint index;
2558 ScatterData *shared;
2559 PRThread *thr;
2560 JSContext *cx;
2561 jsval fn;
2564 static void
2565 DoScatteredWork(JSContext *cx, ScatterThreadData *td)
2567 jsval *rval = &td->shared->results[td->index];
2569 if (!JS_CallFunctionValue(cx, NULL, td->fn, 0, NULL, rval)) {
2570 *rval = JSVAL_VOID;
2571 JS_GetPendingException(cx, rval);
2572 JS_ClearPendingException(cx);
2576 static void
2577 RunScatterThread(void *arg)
2579 ScatterThreadData *td;
2580 ScatterStatus st;
2581 JSContext *cx;
2583 td = (ScatterThreadData *)arg;
2584 cx = td->cx;
2586 /* Wait for go signal. */
2587 PR_Lock(td->shared->lock);
2588 while ((st = td->shared->status) == SCATTER_WAIT)
2589 PR_WaitCondVar(td->shared->cvar, PR_INTERVAL_NO_TIMEOUT);
2590 PR_Unlock(td->shared->lock);
2592 if (st == SCATTER_CANCEL)
2593 return;
2595 /* We are go. */
2596 JS_SetContextThread(cx);
2597 JS_SetThreadStackLimit(cx, 0);
2598 JS_BeginRequest(cx);
2599 DoScatteredWork(cx, td);
2600 JS_EndRequest(cx);
2601 JS_ClearContextThread(cx);
2605 * scatter(fnArray) - Call each function in `fnArray` without arguments, each
2606 * in a different thread. When all threads have finished, return an array: the
2607 * return values. Errors are not propagated; if any of the function calls
2608 * fails, the corresponding element in the results array gets the exception
2609 * object, if any, else (undefined).
2611 static JSBool
2612 Scatter(JSContext *cx, uintN argc, jsval *vp)
2614 jsuint i;
2615 jsuint n; /* number of threads */
2616 JSObject *inArr;
2617 JSObject *arr;
2618 ScatterData sd;
2619 JSBool ok;
2620 jsrefcount rc;
2622 if (!gEnableBranchCallback) {
2623 /* Enable the branch callback, for periodic scope-sharing. */
2624 gEnableBranchCallback = JS_TRUE;
2625 JS_SetBranchCallback(cx, my_BranchCallback);
2626 JS_ToggleOptions(cx, JSOPTION_NATIVE_BRANCH_CALLBACK);
2629 sd.lock = NULL;
2630 sd.cvar = NULL;
2631 sd.results = NULL;
2632 sd.threads = NULL;
2633 sd.status = SCATTER_WAIT;
2635 if (JSVAL_IS_PRIMITIVE(JS_ARGV(cx, vp)[0]))
2636 goto fail;
2638 inArr = JSVAL_TO_OBJECT(JS_ARGV(cx, vp)[0]);
2639 ok = JS_GetArrayLength(cx, inArr, &n);
2640 if (!ok)
2641 goto out;
2642 if (n == 0)
2643 goto success;
2645 sd.lock = PR_NewLock();
2646 if (!sd.lock)
2647 goto fail;
2649 sd.cvar = PR_NewCondVar(sd.lock);
2650 if (!sd.cvar)
2651 goto fail;
2653 sd.results = (jsval *) malloc(n * sizeof(jsval));
2654 if (!sd.results)
2655 goto fail;
2656 for (i = 0; i < n; i++) {
2657 sd.results[i] = JSVAL_VOID;
2658 ok = JS_AddRoot(cx, &sd.results[i]);
2659 if (!ok) {
2660 while (i-- > 0)
2661 JS_RemoveRoot(cx, &sd.results[i]);
2662 free(sd.results);
2663 sd.results = NULL;
2664 goto fail;
2668 sd.threads = (ScatterThreadData *) malloc(n * sizeof(ScatterThreadData));
2669 if (!sd.threads)
2670 goto fail;
2671 for (i = 0; i < n; i++) {
2672 sd.threads[i].index = i;
2673 sd.threads[i].shared = &sd;
2674 sd.threads[i].thr = NULL;
2675 sd.threads[i].cx = NULL;
2676 sd.threads[i].fn = JSVAL_NULL;
2678 ok = JS_AddRoot(cx, &sd.threads[i].fn);
2679 if (ok && !JS_GetElement(cx, inArr, (jsint) i, &sd.threads[i].fn)) {
2680 JS_RemoveRoot(cx, &sd.threads[i].fn);
2681 ok = JS_FALSE;
2683 if (!ok) {
2684 while (i-- > 0)
2685 JS_RemoveRoot(cx, &sd.threads[i].fn);
2686 free(sd.threads);
2687 sd.threads = NULL;
2688 goto fail;
2692 for (i = 1; i < n; i++) {
2693 JSContext *newcx = JS_NewContext(JS_GetRuntime(cx), 8192);
2694 if (!newcx)
2695 goto fail;
2696 JS_SetGlobalObject(newcx, JS_GetGlobalObject(cx));
2697 JS_ClearContextThread(newcx);
2698 sd.threads[i].cx = newcx;
2701 for (i = 1; i < n; i++) {
2702 PRThread *t = PR_CreateThread(PR_USER_THREAD,
2703 RunScatterThread,
2704 &sd.threads[i],
2705 PR_PRIORITY_NORMAL,
2706 PR_GLOBAL_THREAD,
2707 PR_JOINABLE_THREAD,
2709 if (!t) {
2710 /* Failed to start thread. */
2711 PR_Lock(sd.lock);
2712 sd.status = SCATTER_CANCEL;
2713 PR_NotifyAllCondVar(sd.cvar);
2714 PR_Unlock(sd.lock);
2715 while (i-- > 1)
2716 PR_JoinThread(sd.threads[i].thr);
2717 goto fail;
2720 sd.threads[i].thr = t;
2722 PR_Lock(sd.lock);
2723 sd.status = SCATTER_GO;
2724 PR_NotifyAllCondVar(sd.cvar);
2725 PR_Unlock(sd.lock);
2727 DoScatteredWork(cx, &sd.threads[0]);
2729 rc = JS_SuspendRequest(cx);
2730 for (i = 1; i < n; i++) {
2731 PR_JoinThread(sd.threads[i].thr);
2733 JS_ResumeRequest(cx, rc);
2735 success:
2736 arr = JS_NewArrayObject(cx, n, sd.results);
2737 if (!arr)
2738 goto fail;
2739 *vp = OBJECT_TO_JSVAL(arr);
2740 ok = JS_TRUE;
2742 out:
2743 if (sd.threads) {
2744 JSContext *acx;
2746 for (i = 0; i < n; i++) {
2747 JS_RemoveRoot(cx, &sd.threads[i].fn);
2748 acx = sd.threads[i].cx;
2749 if (acx) {
2750 JS_SetContextThread(acx);
2751 JS_DestroyContext(acx);
2754 free(sd.threads);
2756 if (sd.results) {
2757 for (i = 0; i < n; i++)
2758 JS_RemoveRoot(cx, &sd.results[i]);
2759 free(sd.results);
2761 if (sd.cvar)
2762 PR_DestroyCondVar(sd.cvar);
2763 if (sd.lock)
2764 PR_DestroyLock(sd.lock);
2766 return ok;
2768 fail:
2769 ok = JS_FALSE;
2770 goto out;
2773 #endif
2775 /* We use a mix of JS_FS and JS_FN to test both kinds of natives. */
2776 static JSFunctionSpec shell_functions[] = {
2777 JS_FS("version", Version, 0,0,0),
2778 JS_FS("options", Options, 0,0,0),
2779 JS_FS("load", Load, 1,0,0),
2780 JS_FN("readline", ReadLine, 0,0,0),
2781 JS_FS("print", Print, 0,0,0),
2782 JS_FS("help", Help, 0,0,0),
2783 JS_FS("quit", Quit, 0,0,0),
2784 JS_FN("gc", GC, 0,0,0),
2785 JS_FN("gcparam", GCParameter, 2,2,0),
2786 JS_FN("countHeap", CountHeap, 0,0,0),
2787 #ifdef JS_GC_ZEAL
2788 JS_FN("gczeal", GCZeal, 1,1,0),
2789 #endif
2790 JS_FS("trap", Trap, 3,0,0),
2791 JS_FS("untrap", Untrap, 2,0,0),
2792 JS_FS("line2pc", LineToPC, 0,0,0),
2793 JS_FS("pc2line", PCToLine, 0,0,0),
2794 JS_FN("stackQuota", StackQuota, 0,0,0),
2795 JS_FS("stringsAreUTF8", StringsAreUTF8, 0,0,0),
2796 JS_FS("testUTF8", TestUTF8, 1,0,0),
2797 JS_FS("throwError", ThrowError, 0,0,0),
2798 #ifdef DEBUG
2799 JS_FS("dis", Disassemble, 1,0,0),
2800 JS_FS("dissrc", DisassWithSrc, 1,0,0),
2801 JS_FN("dumpHeap", DumpHeap, 0,0,0),
2802 JS_FS("notes", Notes, 1,0,0),
2803 JS_FS("tracing", Tracing, 0,0,0),
2804 JS_FS("stats", DumpStats, 1,0,0),
2805 #endif
2806 #ifdef TEST_EXPORT
2807 JS_FS("xport", DoExport, 2,0,0),
2808 #endif
2809 #ifdef TEST_CVTARGS
2810 JS_FS("cvtargs", ConvertArgs, 0,0,12),
2811 #endif
2812 JS_FN("build", BuildDate, 0,0,0),
2813 JS_FS("clear", Clear, 0,0,0),
2814 JS_FN("intern", Intern, 1,1,0),
2815 JS_FS("clone", Clone, 1,0,0),
2816 JS_FS("seal", Seal, 1,0,1),
2817 JS_FN("getpda", GetPDA, 1,1,0),
2818 JS_FN("getslx", GetSLX, 1,1,0),
2819 JS_FN("toint32", ToInt32, 1,1,0),
2820 JS_FS("evalcx", EvalInContext, 1,0,0),
2821 #ifdef MOZ_SHARK
2822 JS_FS("startShark", js_StartShark, 0,0,0),
2823 JS_FS("stopShark", js_StopShark, 0,0,0),
2824 JS_FS("connectShark", js_ConnectShark, 0,0,0),
2825 JS_FS("disconnectShark", js_DisconnectShark, 0,0,0),
2826 #endif
2827 #ifdef DEBUG_ARRAYS
2828 JS_FS("arrayInfo", js_ArrayInfo, 1,0,0),
2829 #endif
2830 #ifdef JS_THREADSAFE
2831 JS_FN("sleep", Sleep, 1,1,0),
2832 JS_FN("scatter", Scatter, 1,1,0),
2833 #endif
2834 JS_FS_END
2837 static const char shell_help_header[] =
2838 "Command Description\n"
2839 "======= ===========\n";
2841 static const char *const shell_help_messages[] = {
2842 "version([number]) Get or set JavaScript version number",
2843 "options([option ...]) Get or toggle JavaScript options",
2844 "load(['foo.js' ...]) Load files named by string arguments",
2845 "readline() Read a single line from stdin",
2846 "print([exp ...]) Evaluate and print expressions",
2847 "help([name ...]) Display usage and help messages",
2848 "quit() Quit the shell",
2849 "gc() Run the garbage collector",
2850 "gcparam(name, value)\n"
2851 " Wrapper for JS_SetGCParameter. The name must be either 'maxBytes' or\n"
2852 " 'maxMallocBytes' and the value must be convertable to a positive uint32",
2853 "countHeap([start[, kind]])\n"
2854 " Count the number of live GC things in the heap or things reachable from\n"
2855 " start when it is given and is not null. kind is either 'all' (default) to\n"
2856 " count all things or one of 'object', 'double', 'string', 'function',\n"
2857 " 'qname', 'namespace', 'xml' to count only things of that kind",
2858 #ifdef JS_GC_ZEAL
2859 "gczeal(level) How zealous the garbage collector should be",
2860 #endif
2861 "trap([fun, [pc,]] exp) Trap bytecode execution",
2862 "untrap(fun[, pc]) Remove a trap",
2863 "line2pc([fun,] line) Map line number to PC",
2864 "pc2line(fun[, pc]) Map PC to line number",
2865 "stackQuota([number]) Query/set script stack quota",
2866 "stringsAreUTF8() Check if strings are UTF-8 encoded",
2867 "testUTF8(mode) Perform UTF-8 tests (modes are 1 to 4)",
2868 "throwError() Throw an error from JS_ReportError",
2869 #ifdef DEBUG
2870 "dis([fun]) Disassemble functions into bytecodes",
2871 "dissrc([fun]) Disassemble functions with source lines",
2872 "dumpHeap([fileName[, start[, toFind[, maxDepth[, toIgnore]]]]])\n"
2873 " Interface to JS_DumpHeap with output sent to file",
2874 "notes([fun]) Show source notes for functions",
2875 "tracing([toggle]) Turn tracing on or off",
2876 "stats([string ...]) Dump 'arena', 'atom', 'global' stats",
2877 #endif
2878 #ifdef TEST_EXPORT
2879 "xport(obj, property) Export the given property of obj",
2880 #endif
2881 #ifdef TEST_CVTARGS
2882 "cvtargs(arg1..., arg12) Test argument formater",
2883 #endif
2884 "build() Show build date and time",
2885 "clear([obj]) Clear properties of object",
2886 "intern(str) Internalize str in the atom table",
2887 "clone(fun[, scope]) Clone function object",
2888 "seal(obj[, deep]) Seal object, or object graph if deep",
2889 "getpda(obj) Get the property descriptors for obj",
2890 "getslx(obj) Get script line extent",
2891 "toint32(n) Testing hook for JS_ValueToInt32",
2892 "evalcx(s[, o])\n"
2893 " Evaluate s in optional sandbox object o\n"
2894 " if (s == '' && !o) return new o with eager standard classes\n"
2895 " if (s == 'lazy' && !o) return new o with lazy standard classes",
2896 #ifdef MOZ_SHARK
2897 "startShark() Start a Shark session.\n"
2898 " Shark must be running with programatic sampling.",
2899 "stopShark() Stop a running Shark session.",
2900 "connectShark() Connect to Shark.\n"
2901 " The -k switch does this automatically.",
2902 "disconnectShark() Disconnect from Shark.",
2903 #endif
2904 #ifdef DEBUG_ARRAYS
2905 "arrayInfo(a1, a2, ...) Report statistics about arrays.",
2906 #endif
2907 #ifdef JS_THREADSAFE
2908 "sleep(dt) Sleep for dt seconds",
2909 "scatter(fns) Call functions concurrently (ignoring errors)",
2910 #endif
2913 /* Help messages must match shell functions. */
2914 JS_STATIC_ASSERT(JS_ARRAY_LENGTH(shell_help_messages) + 1 ==
2915 JS_ARRAY_LENGTH(shell_functions));
2917 #ifdef DEBUG
2918 static void
2919 CheckHelpMessages()
2921 const char *const *m;
2922 const char *lp;
2924 /* Each message must begin with "function_name(" prefix. */
2925 for (m = shell_help_messages; m != JS_ARRAY_END(shell_help_messages); ++m) {
2926 lp = strchr(*m, '(');
2927 JS_ASSERT(lp);
2928 JS_ASSERT(memcmp(shell_functions[m - shell_help_messages].name,
2929 *m, lp - *m) == 0);
2932 #else
2933 # define CheckHelpMessages() ((void) 0)
2934 #endif
2936 static JSBool
2937 Help(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
2939 uintN i, j;
2940 int did_header, did_something;
2941 JSType type;
2942 JSFunction *fun;
2943 JSString *str;
2944 const char *bytes;
2946 fprintf(gOutFile, "%s\n", JS_GetImplementationVersion());
2947 if (argc == 0) {
2948 fputs(shell_help_header, gOutFile);
2949 for (i = 0; shell_functions[i].name; i++)
2950 fprintf(gOutFile, "%s\n", shell_help_messages[i]);
2951 } else {
2952 did_header = 0;
2953 for (i = 0; i < argc; i++) {
2954 did_something = 0;
2955 type = JS_TypeOfValue(cx, argv[i]);
2956 if (type == JSTYPE_FUNCTION) {
2957 fun = JS_ValueToFunction(cx, argv[i]);
2958 str = fun->atom ? ATOM_TO_STRING(fun->atom) : NULL;
2959 } else if (type == JSTYPE_STRING) {
2960 str = JSVAL_TO_STRING(argv[i]);
2961 } else {
2962 str = NULL;
2964 if (str) {
2965 bytes = JS_GetStringBytes(str);
2966 for (j = 0; shell_functions[j].name; j++) {
2967 if (!strcmp(bytes, shell_functions[j].name)) {
2968 if (!did_header) {
2969 did_header = 1;
2970 fputs(shell_help_header, gOutFile);
2972 did_something = 1;
2973 fprintf(gOutFile, "%s\n", shell_help_messages[j]);
2974 break;
2978 if (!did_something) {
2979 str = JS_ValueToString(cx, argv[i]);
2980 if (!str)
2981 return JS_FALSE;
2982 fprintf(gErrFile, "Sorry, no help for %s\n",
2983 JS_GetStringBytes(str));
2987 return JS_TRUE;
2990 static JSObject *
2991 split_setup(JSContext *cx)
2993 JSObject *outer, *inner, *arguments;
2995 outer = split_create_outer(cx);
2996 if (!outer)
2997 return NULL;
2998 JS_SetGlobalObject(cx, outer);
3000 inner = split_create_inner(cx, outer);
3001 if (!inner)
3002 return NULL;
3004 if (!JS_DefineFunctions(cx, inner, shell_functions))
3005 return NULL;
3006 JS_ClearScope(cx, outer);
3008 /* Create a dummy arguments object. */
3009 arguments = JS_NewArrayObject(cx, 0, NULL);
3010 if (!arguments ||
3011 !JS_DefineProperty(cx, inner, "arguments", OBJECT_TO_JSVAL(arguments),
3012 NULL, NULL, 0)) {
3013 return NULL;
3016 #ifndef LAZY_STANDARD_CLASSES
3017 if (!JS_InitStandardClasses(cx, inner))
3018 return NULL;
3019 #endif
3021 return inner;
3025 * Define a JS object called "it". Give it class operations that printf why
3026 * they're being called for tutorial purposes.
3028 enum its_tinyid {
3029 ITS_COLOR, ITS_HEIGHT, ITS_WIDTH, ITS_FUNNY, ITS_ARRAY, ITS_RDONLY
3032 static JSPropertySpec its_props[] = {
3033 {"color", ITS_COLOR, JSPROP_ENUMERATE, NULL, NULL},
3034 {"height", ITS_HEIGHT, JSPROP_ENUMERATE, NULL, NULL},
3035 {"width", ITS_WIDTH, JSPROP_ENUMERATE, NULL, NULL},
3036 {"funny", ITS_FUNNY, JSPROP_ENUMERATE, NULL, NULL},
3037 {"array", ITS_ARRAY, JSPROP_ENUMERATE, NULL, NULL},
3038 {"rdonly", ITS_RDONLY, JSPROP_READONLY, NULL, NULL},
3039 {NULL,0,0,NULL,NULL}
3042 static JSBool
3043 its_item(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
3045 *rval = OBJECT_TO_JSVAL(obj);
3046 if (argc != 0)
3047 JS_SetCallReturnValue2(cx, argv[0]);
3048 return JS_TRUE;
3051 static JSBool
3052 its_bindMethod(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
3053 jsval *rval)
3055 char *name;
3056 JSObject *method;
3058 if (!JS_ConvertArguments(cx, argc, argv, "so", &name, &method))
3059 return JS_FALSE;
3061 *rval = OBJECT_TO_JSVAL(method);
3063 if (JS_TypeOfValue(cx, *rval) != JSTYPE_FUNCTION) {
3064 JSString *valstr = JS_ValueToString(cx, *rval);
3065 if (valstr) {
3066 JS_ReportError(cx, "can't bind method %s to non-callable object %s",
3067 name, JS_GetStringBytes(valstr));
3069 return JS_FALSE;
3072 if (!JS_DefineProperty(cx, obj, name, *rval, NULL, NULL, JSPROP_ENUMERATE))
3073 return JS_FALSE;
3075 return JS_SetParent(cx, method, obj);
3078 static JSFunctionSpec its_methods[] = {
3079 {"item", its_item, 0,0,0},
3080 {"bindMethod", its_bindMethod, 2,0,0},
3081 {NULL,NULL,0,0,0}
3084 #ifdef JSD_LOWLEVEL_SOURCE
3086 * This facilitates sending source to JSD (the debugger system) in the shell
3087 * where the source is loaded using the JSFILE hack in jsscan. The function
3088 * below is used as a callback for the jsdbgapi JS_SetSourceHandler hook.
3089 * A more normal embedding (e.g. mozilla) loads source itself and can send
3090 * source directly to JSD without using this hook scheme.
3092 static void
3093 SendSourceToJSDebugger(const char *filename, uintN lineno,
3094 jschar *str, size_t length,
3095 void **listenerTSData, JSDContext* jsdc)
3097 JSDSourceText *jsdsrc = (JSDSourceText *) *listenerTSData;
3099 if (!jsdsrc) {
3100 if (!filename)
3101 filename = "typein";
3102 if (1 == lineno) {
3103 jsdsrc = JSD_NewSourceText(jsdc, filename);
3104 } else {
3105 jsdsrc = JSD_FindSourceForURL(jsdc, filename);
3106 if (jsdsrc && JSD_SOURCE_PARTIAL !=
3107 JSD_GetSourceStatus(jsdc, jsdsrc)) {
3108 jsdsrc = NULL;
3112 if (jsdsrc) {
3113 jsdsrc = JSD_AppendUCSourceText(jsdc,jsdsrc, str, length,
3114 JSD_SOURCE_PARTIAL);
3116 *listenerTSData = jsdsrc;
3118 #endif /* JSD_LOWLEVEL_SOURCE */
3120 static JSBool its_noisy; /* whether to be noisy when finalizing it */
3121 static JSBool its_enum_fail;/* whether to fail when enumerating it */
3123 static JSBool
3124 its_addProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
3126 if (its_noisy) {
3127 fprintf(gOutFile, "adding its property %s,",
3128 JS_GetStringBytes(JS_ValueToString(cx, id)));
3129 fprintf(gOutFile, " initial value %s\n",
3130 JS_GetStringBytes(JS_ValueToString(cx, *vp)));
3132 return JS_TRUE;
3135 static JSBool
3136 its_delProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
3138 if (its_noisy) {
3139 fprintf(gOutFile, "deleting its property %s,",
3140 JS_GetStringBytes(JS_ValueToString(cx, id)));
3141 fprintf(gOutFile, " current value %s\n",
3142 JS_GetStringBytes(JS_ValueToString(cx, *vp)));
3144 return JS_TRUE;
3147 static JSBool
3148 its_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
3150 if (its_noisy) {
3151 fprintf(gOutFile, "getting its property %s,",
3152 JS_GetStringBytes(JS_ValueToString(cx, id)));
3153 fprintf(gOutFile, " current value %s\n",
3154 JS_GetStringBytes(JS_ValueToString(cx, *vp)));
3156 return JS_TRUE;
3159 static JSBool
3160 its_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
3162 char *str;
3163 if (its_noisy) {
3164 fprintf(gOutFile, "setting its property %s,",
3165 JS_GetStringBytes(JS_ValueToString(cx, id)));
3166 fprintf(gOutFile, " new value %s\n",
3167 JS_GetStringBytes(JS_ValueToString(cx, *vp)));
3170 if (!JSVAL_IS_STRING(id))
3171 return JS_TRUE;
3173 str = JS_GetStringBytes(JSVAL_TO_STRING(id));
3174 if (!strcmp(str, "noisy"))
3175 return JS_ValueToBoolean(cx, *vp, &its_noisy);
3176 else if (!strcmp(str, "enum_fail"))
3177 return JS_ValueToBoolean(cx, *vp, &its_enum_fail);
3179 return JS_TRUE;
3183 * Its enumerator, implemented using the "new" enumerate API,
3184 * see class flags.
3186 static JSBool
3187 its_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
3188 jsval *statep, jsid *idp)
3190 JSObject *iterator;
3192 switch (enum_op) {
3193 case JSENUMERATE_INIT:
3194 if (its_noisy)
3195 fprintf(gOutFile, "enumerate its properties\n");
3197 iterator = JS_NewPropertyIterator(cx, obj);
3198 if (!iterator)
3199 return JS_FALSE;
3201 *statep = OBJECT_TO_JSVAL(iterator);
3202 if (idp)
3203 *idp = JSVAL_ZERO;
3204 break;
3206 case JSENUMERATE_NEXT:
3207 if (its_enum_fail) {
3208 JS_ReportError(cx, "its enumeration failed");
3209 return JS_FALSE;
3212 iterator = (JSObject *) JSVAL_TO_OBJECT(*statep);
3213 if (!JS_NextProperty(cx, iterator, idp))
3214 return JS_FALSE;
3216 if (!JSVAL_IS_VOID(*idp))
3217 break;
3218 /* Fall through. */
3220 case JSENUMERATE_DESTROY:
3221 /* Allow our iterator object to be GC'd. */
3222 *statep = JSVAL_NULL;
3223 break;
3226 return JS_TRUE;
3229 static JSBool
3230 its_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
3231 JSObject **objp)
3233 if (its_noisy) {
3234 fprintf(gOutFile, "resolving its property %s, flags {%s,%s,%s}\n",
3235 JS_GetStringBytes(JS_ValueToString(cx, id)),
3236 (flags & JSRESOLVE_QUALIFIED) ? "qualified" : "",
3237 (flags & JSRESOLVE_ASSIGNING) ? "assigning" : "",
3238 (flags & JSRESOLVE_DETECTING) ? "detecting" : "");
3240 return JS_TRUE;
3243 static JSBool
3244 its_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
3246 if (its_noisy)
3247 fprintf(gOutFile, "converting it to %s type\n", JS_GetTypeName(cx, type));
3248 return JS_TRUE;
3251 static void
3252 its_finalize(JSContext *cx, JSObject *obj)
3254 if (its_noisy)
3255 fprintf(gOutFile, "finalizing it\n");
3258 static JSClass its_class = {
3259 "It", JSCLASS_NEW_RESOLVE | JSCLASS_NEW_ENUMERATE,
3260 its_addProperty, its_delProperty, its_getProperty, its_setProperty,
3261 (JSEnumerateOp)its_enumerate, (JSResolveOp)its_resolve,
3262 its_convert, its_finalize,
3263 JSCLASS_NO_OPTIONAL_MEMBERS
3266 JSErrorFormatString jsShell_ErrorFormatString[JSErr_Limit] = {
3267 #define MSG_DEF(name, number, count, exception, format) \
3268 { format, count, JSEXN_ERR } ,
3269 #include "jsshell.msg"
3270 #undef MSG_DEF
3273 static const JSErrorFormatString *
3274 my_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber)
3276 if ((errorNumber > 0) && (errorNumber < JSShellErr_Limit))
3277 return &jsShell_ErrorFormatString[errorNumber];
3278 return NULL;
3281 static void
3282 my_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
3284 int i, j, k, n;
3285 char *prefix, *tmp;
3286 const char *ctmp;
3288 if (!report) {
3289 fprintf(gErrFile, "%s\n", message);
3290 return;
3293 /* Conditionally ignore reported warnings. */
3294 if (JSREPORT_IS_WARNING(report->flags) && !reportWarnings)
3295 return;
3297 prefix = NULL;
3298 if (report->filename)
3299 prefix = JS_smprintf("%s:", report->filename);
3300 if (report->lineno) {
3301 tmp = prefix;
3302 prefix = JS_smprintf("%s%u: ", tmp ? tmp : "", report->lineno);
3303 JS_free(cx, tmp);
3305 if (JSREPORT_IS_WARNING(report->flags)) {
3306 tmp = prefix;
3307 prefix = JS_smprintf("%s%swarning: ",
3308 tmp ? tmp : "",
3309 JSREPORT_IS_STRICT(report->flags) ? "strict " : "");
3310 JS_free(cx, tmp);
3313 /* embedded newlines -- argh! */
3314 while ((ctmp = strchr(message, '\n')) != 0) {
3315 ctmp++;
3316 if (prefix)
3317 fputs(prefix, gErrFile);
3318 fwrite(message, 1, ctmp - message, gErrFile);
3319 message = ctmp;
3322 /* If there were no filename or lineno, the prefix might be empty */
3323 if (prefix)
3324 fputs(prefix, gErrFile);
3325 fputs(message, gErrFile);
3327 if (!report->linebuf) {
3328 fputc('\n', gErrFile);
3329 goto out;
3332 /* report->linebuf usually ends with a newline. */
3333 n = strlen(report->linebuf);
3334 fprintf(gErrFile, ":\n%s%s%s%s",
3335 prefix,
3336 report->linebuf,
3337 (n > 0 && report->linebuf[n-1] == '\n') ? "" : "\n",
3338 prefix);
3339 n = PTRDIFF(report->tokenptr, report->linebuf, char);
3340 for (i = j = 0; i < n; i++) {
3341 if (report->linebuf[i] == '\t') {
3342 for (k = (j + 8) & ~7; j < k; j++) {
3343 fputc('.', gErrFile);
3345 continue;
3347 fputc('.', gErrFile);
3348 j++;
3350 fputs("^\n", gErrFile);
3351 out:
3352 if (!JSREPORT_IS_WARNING(report->flags)) {
3353 if (report->errorNumber == JSMSG_OUT_OF_MEMORY) {
3354 gExitCode = EXITCODE_OUT_OF_MEMORY;
3355 } else {
3356 gExitCode = EXITCODE_RUNTIME_ERROR;
3359 JS_free(cx, prefix);
3362 #if defined(SHELL_HACK) && defined(DEBUG) && defined(XP_UNIX)
3363 static JSBool
3364 Exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
3366 JSFunction *fun;
3367 const char *name, **nargv;
3368 uintN i, nargc;
3369 JSString *str;
3370 pid_t pid;
3371 int status;
3373 fun = JS_ValueToFunction(cx, argv[-2]);
3374 if (!fun)
3375 return JS_FALSE;
3376 if (!fun->atom)
3377 return JS_TRUE;
3378 name = JS_GetStringBytes(ATOM_TO_STRING(fun->atom));
3379 nargc = 1 + argc;
3380 nargv = JS_malloc(cx, (nargc + 1) * sizeof(char *));
3381 if (!nargv)
3382 return JS_FALSE;
3383 nargv[0] = name;
3384 for (i = 1; i < nargc; i++) {
3385 str = JS_ValueToString(cx, argv[i-1]);
3386 if (!str) {
3387 JS_free(cx, nargv);
3388 return JS_FALSE;
3390 nargv[i] = JS_GetStringBytes(str);
3392 nargv[nargc] = 0;
3393 pid = fork();
3394 switch (pid) {
3395 case -1:
3396 perror("js");
3397 break;
3398 case 0:
3399 (void) execvp(name, (char **)nargv);
3400 perror("js");
3401 exit(127);
3402 default:
3403 while (waitpid(pid, &status, 0) < 0 && errno == EINTR)
3404 continue;
3405 break;
3407 JS_free(cx, nargv);
3408 return JS_TRUE;
3410 #endif
3412 static JSBool
3413 global_enumerate(JSContext *cx, JSObject *obj)
3415 #ifdef LAZY_STANDARD_CLASSES
3416 return JS_EnumerateStandardClasses(cx, obj);
3417 #else
3418 return JS_TRUE;
3419 #endif
3422 static JSBool
3423 global_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
3424 JSObject **objp)
3426 #ifdef LAZY_STANDARD_CLASSES
3427 if ((flags & JSRESOLVE_ASSIGNING) == 0) {
3428 JSBool resolved;
3430 if (!JS_ResolveStandardClass(cx, obj, id, &resolved))
3431 return JS_FALSE;
3432 if (resolved) {
3433 *objp = obj;
3434 return JS_TRUE;
3437 #endif
3439 #if defined(SHELL_HACK) && defined(DEBUG) && defined(XP_UNIX)
3440 if ((flags & (JSRESOLVE_QUALIFIED | JSRESOLVE_ASSIGNING)) == 0) {
3442 * Do this expensive hack only for unoptimized Unix builds, which are
3443 * not used for benchmarking.
3445 char *path, *comp, *full;
3446 const char *name;
3447 JSBool ok, found;
3448 JSFunction *fun;
3450 if (!JSVAL_IS_STRING(id))
3451 return JS_TRUE;
3452 path = getenv("PATH");
3453 if (!path)
3454 return JS_TRUE;
3455 path = JS_strdup(cx, path);
3456 if (!path)
3457 return JS_FALSE;
3458 name = JS_GetStringBytes(JSVAL_TO_STRING(id));
3459 ok = JS_TRUE;
3460 for (comp = strtok(path, ":"); comp; comp = strtok(NULL, ":")) {
3461 if (*comp != '\0') {
3462 full = JS_smprintf("%s/%s", comp, name);
3463 if (!full) {
3464 JS_ReportOutOfMemory(cx);
3465 ok = JS_FALSE;
3466 break;
3468 } else {
3469 full = (char *)name;
3471 found = (access(full, X_OK) == 0);
3472 if (*comp != '\0')
3473 free(full);
3474 if (found) {
3475 fun = JS_DefineFunction(cx, obj, name, Exec, 0,
3476 JSPROP_ENUMERATE);
3477 ok = (fun != NULL);
3478 if (ok)
3479 *objp = obj;
3480 break;
3483 JS_free(cx, path);
3484 return ok;
3486 #else
3487 return JS_TRUE;
3488 #endif
3491 JSClass global_class = {
3492 "global", JSCLASS_NEW_RESOLVE | JSCLASS_GLOBAL_FLAGS,
3493 JS_PropertyStub, JS_PropertyStub,
3494 JS_PropertyStub, JS_PropertyStub,
3495 global_enumerate, (JSResolveOp) global_resolve,
3496 JS_ConvertStub, JS_FinalizeStub,
3497 JSCLASS_NO_OPTIONAL_MEMBERS
3500 static JSBool
3501 env_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
3503 /* XXX porting may be easy, but these don't seem to supply setenv by default */
3504 #if !defined XP_BEOS && !defined XP_OS2 && !defined SOLARIS
3505 JSString *idstr, *valstr;
3506 const char *name, *value;
3507 int rv;
3509 idstr = JS_ValueToString(cx, id);
3510 valstr = JS_ValueToString(cx, *vp);
3511 if (!idstr || !valstr)
3512 return JS_FALSE;
3513 name = JS_GetStringBytes(idstr);
3514 value = JS_GetStringBytes(valstr);
3515 #if defined XP_WIN || defined HPUX || defined OSF1 || defined IRIX
3517 char *waste = JS_smprintf("%s=%s", name, value);
3518 if (!waste) {
3519 JS_ReportOutOfMemory(cx);
3520 return JS_FALSE;
3522 rv = putenv(waste);
3523 #ifdef XP_WIN
3525 * HPUX9 at least still has the bad old non-copying putenv.
3527 * Per mail from <s.shanmuganathan@digital.com>, OSF1 also has a putenv
3528 * that will crash if you pass it an auto char array (so it must place
3529 * its argument directly in the char *environ[] array).
3531 free(waste);
3532 #endif
3534 #else
3535 rv = setenv(name, value, 1);
3536 #endif
3537 if (rv < 0) {
3538 JS_ReportError(cx, "can't set envariable %s to %s", name, value);
3539 return JS_FALSE;
3541 *vp = STRING_TO_JSVAL(valstr);
3542 #endif /* !defined XP_BEOS && !defined XP_OS2 && !defined SOLARIS */
3543 return JS_TRUE;
3546 static JSBool
3547 env_enumerate(JSContext *cx, JSObject *obj)
3549 static JSBool reflected;
3550 char **evp, *name, *value;
3551 JSString *valstr;
3552 JSBool ok;
3554 if (reflected)
3555 return JS_TRUE;
3557 for (evp = (char **)JS_GetPrivate(cx, obj); (name = *evp) != NULL; evp++) {
3558 value = strchr(name, '=');
3559 if (!value)
3560 continue;
3561 *value++ = '\0';
3562 valstr = JS_NewStringCopyZ(cx, value);
3563 if (!valstr) {
3564 ok = JS_FALSE;
3565 } else {
3566 ok = JS_DefineProperty(cx, obj, name, STRING_TO_JSVAL(valstr),
3567 NULL, NULL, JSPROP_ENUMERATE);
3569 value[-1] = '=';
3570 if (!ok)
3571 return JS_FALSE;
3574 reflected = JS_TRUE;
3575 return JS_TRUE;
3578 static JSBool
3579 env_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
3580 JSObject **objp)
3582 JSString *idstr, *valstr;
3583 const char *name, *value;
3585 if (flags & JSRESOLVE_ASSIGNING)
3586 return JS_TRUE;
3588 idstr = JS_ValueToString(cx, id);
3589 if (!idstr)
3590 return JS_FALSE;
3591 name = JS_GetStringBytes(idstr);
3592 value = getenv(name);
3593 if (value) {
3594 valstr = JS_NewStringCopyZ(cx, value);
3595 if (!valstr)
3596 return JS_FALSE;
3597 if (!JS_DefineProperty(cx, obj, name, STRING_TO_JSVAL(valstr),
3598 NULL, NULL, JSPROP_ENUMERATE)) {
3599 return JS_FALSE;
3601 *objp = obj;
3603 return JS_TRUE;
3606 static JSClass env_class = {
3607 "environment", JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE,
3608 JS_PropertyStub, JS_PropertyStub,
3609 JS_PropertyStub, env_setProperty,
3610 env_enumerate, (JSResolveOp) env_resolve,
3611 JS_ConvertStub, JS_FinalizeStub,
3612 JSCLASS_NO_OPTIONAL_MEMBERS
3615 #ifdef NARCISSUS
3617 static JSBool
3618 defineProperty(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
3619 jsval *rval)
3621 JSString *str;
3622 jsval value;
3623 JSBool dontDelete, readOnly, dontEnum;
3624 const jschar *chars;
3625 size_t length;
3626 uintN attrs;
3628 dontDelete = readOnly = dontEnum = JS_FALSE;
3629 if (!JS_ConvertArguments(cx, argc, argv, "Sv/bbb",
3630 &str, &value, &dontDelete, &readOnly, &dontEnum)) {
3631 return JS_FALSE;
3633 chars = JS_GetStringChars(str);
3634 length = JS_GetStringLength(str);
3635 attrs = dontEnum ? 0 : JSPROP_ENUMERATE;
3636 if (dontDelete)
3637 attrs |= JSPROP_PERMANENT;
3638 if (readOnly)
3639 attrs |= JSPROP_READONLY;
3640 return JS_DefineUCProperty(cx, obj, chars, length, value, NULL, NULL,
3641 attrs);
3644 static JSBool
3645 Evaluate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
3647 /* function evaluate(source, filename, lineno) { ... } */
3648 JSString *source;
3649 const char *filename = "";
3650 jsuint lineno = 0;
3651 uint32 oldopts;
3652 JSBool ok;
3654 if (argc == 0) {
3655 *rval = JSVAL_VOID;
3656 return JS_TRUE;
3659 if (!JS_ConvertArguments(cx, argc, argv, "S/su",
3660 &source, &filename, &lineno)) {
3661 return JS_FALSE;
3664 oldopts = JS_GetOptions(cx);
3665 JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO);
3666 ok = JS_EvaluateUCScript(cx, obj, JS_GetStringChars(source),
3667 JS_GetStringLength(source), filename,
3668 lineno, rval);
3669 JS_SetOptions(cx, oldopts);
3671 return ok;
3674 #include <fcntl.h>
3675 #include <sys/stat.h>
3678 * Returns a JS_malloc'd string (that the caller needs to JS_free)
3679 * containing the directory (non-leaf) part of |from| prepended to |leaf|.
3680 * If |from| is empty or a leaf, MakeAbsolutePathname returns a copy of leaf.
3681 * Returns NULL to indicate an error.
3683 static char *
3684 MakeAbsolutePathname(JSContext *cx, const char *from, const char *leaf)
3686 size_t dirlen;
3687 char *dir;
3688 const char *slash = NULL, *cp;
3690 cp = from;
3691 while (*cp) {
3692 if (*cp == '/'
3693 #ifdef XP_WIN
3694 || *cp == '\\'
3695 #endif
3697 slash = cp;
3700 ++cp;
3703 if (!slash) {
3704 /* We were given a leaf or |from| was empty. */
3705 return JS_strdup(cx, leaf);
3708 /* Else, we were given a real pathname, return that + the leaf. */
3709 dirlen = slash - from + 1;
3710 dir = JS_malloc(cx, dirlen + strlen(leaf) + 1);
3711 if (!dir)
3712 return NULL;
3714 strncpy(dir, from, dirlen);
3715 strcpy(dir + dirlen, leaf); /* Note: we can't use strcat here. */
3717 return dir;
3720 static JSBool
3721 snarf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
3723 JSString *str;
3724 const char *filename;
3725 char *pathname;
3726 JSStackFrame *fp;
3727 JSBool ok;
3728 off_t cc, len;
3729 char *buf;
3730 FILE *file;
3732 str = JS_ValueToString(cx, argv[0]);
3733 if (!str)
3734 return JS_FALSE;
3735 filename = JS_GetStringBytes(str);
3737 /* Get the currently executing script's name. */
3738 fp = JS_GetScriptedCaller(cx, NULL);
3739 JS_ASSERT(fp && fp->script->filename);
3740 pathname = MakeAbsolutePathname(cx, fp->script->filename, filename);
3741 if (!pathname)
3742 return JS_FALSE;
3744 ok = JS_FALSE;
3745 len = 0;
3746 buf = NULL;
3747 file = fopen(pathname, "rb");
3748 if (!file) {
3749 JS_ReportError(cx, "can't open %s: %s", pathname, strerror(errno));
3750 } else {
3751 if (fseek(file, 0, SEEK_END) == EOF) {
3752 JS_ReportError(cx, "can't seek end of %s", pathname);
3753 } else {
3754 len = ftell(file);
3755 if (len == -1 || fseek(file, 0, SEEK_SET) == EOF) {
3756 JS_ReportError(cx, "can't seek start of %s", pathname);
3757 } else {
3758 buf = JS_malloc(cx, len + 1);
3759 if (buf) {
3760 cc = fread(buf, 1, len, file);
3761 if (cc != len) {
3762 JS_free(cx, buf);
3763 JS_ReportError(cx, "can't read %s: %s", pathname,
3764 (cc < 0) ? strerror(errno)
3765 : "short read");
3766 } else {
3767 len = (size_t)cc;
3768 ok = JS_TRUE;
3773 fclose(file);
3775 JS_free(cx, pathname);
3776 if (!ok) {
3777 JS_free(cx, buf);
3778 return ok;
3781 buf[len] = '\0';
3782 str = JS_NewString(cx, buf, len);
3783 if (!str) {
3784 JS_free(cx, buf);
3785 return JS_FALSE;
3787 *rval = STRING_TO_JSVAL(str);
3788 return JS_TRUE;
3791 #endif /* NARCISSUS */
3793 static JSBool
3794 ContextCallback(JSContext *cx, uintN contextOp)
3796 if (contextOp == JSCONTEXT_NEW) {
3797 JS_SetErrorReporter(cx, my_ErrorReporter);
3798 JS_SetVersion(cx, JSVERSION_LATEST);
3799 SetContextOptions(cx);
3801 return JS_TRUE;
3805 main(int argc, char **argv, char **envp)
3807 int stackDummy;
3808 JSRuntime *rt;
3809 JSContext *cx;
3810 JSObject *glob, *it, *envobj;
3811 int result;
3812 #ifdef LIVECONNECT
3813 JavaVM *java_vm = NULL;
3814 #endif
3815 #ifdef JSDEBUGGER
3816 JSDContext *jsdc;
3817 #ifdef JSDEBUGGER_JAVA_UI
3818 JNIEnv *java_env;
3819 JSDJContext *jsdjc;
3820 #endif
3821 #ifdef JSDEBUGGER_C_UI
3822 JSBool jsdbc;
3823 #endif /* JSDEBUGGER_C_UI */
3824 #endif /* JSDEBUGGER */
3826 CheckHelpMessages();
3827 setlocale(LC_ALL, "");
3829 gStackBase = (jsuword)&stackDummy;
3831 #ifdef XP_OS2
3832 /* these streams are normally line buffered on OS/2 and need a \n, *
3833 * so we need to unbuffer then to get a reasonable prompt */
3834 setbuf(stdout,0);
3835 setbuf(stderr,0);
3836 #endif
3838 gErrFile = stderr;
3839 gOutFile = stdout;
3841 argc--;
3842 argv++;
3844 rt = JS_NewRuntime(64L * 1024L * 1024L);
3845 if (!rt)
3846 return 1;
3847 JS_SetContextCallback(rt, ContextCallback);
3849 cx = JS_NewContext(rt, gStackChunkSize);
3850 if (!cx)
3851 return 1;
3853 #ifdef JS_THREADSAFE
3854 JS_BeginRequest(cx);
3855 #endif
3857 glob = JS_NewObject(cx, &global_class, NULL, NULL);
3858 if (!glob)
3859 return 1;
3860 #ifdef LAZY_STANDARD_CLASSES
3861 JS_SetGlobalObject(cx, glob);
3862 #else
3863 if (!JS_InitStandardClasses(cx, glob))
3864 return 1;
3865 #endif
3866 if (!JS_DefineFunctions(cx, glob, shell_functions))
3867 return 1;
3869 it = JS_DefineObject(cx, glob, "it", &its_class, NULL, 0);
3870 if (!it)
3871 return 1;
3872 if (!JS_DefineProperties(cx, it, its_props))
3873 return 1;
3874 if (!JS_DefineFunctions(cx, it, its_methods))
3875 return 1;
3877 #ifdef JSDEBUGGER
3879 * XXX A command line option to enable debugging (or not) would be good
3881 jsdc = JSD_DebuggerOnForUser(rt, NULL, NULL);
3882 if (!jsdc)
3883 return 1;
3884 JSD_JSContextInUse(jsdc, cx);
3885 #ifdef JSD_LOWLEVEL_SOURCE
3886 JS_SetSourceHandler(rt, SendSourceToJSDebugger, jsdc);
3887 #endif /* JSD_LOWLEVEL_SOURCE */
3888 #ifdef JSDEBUGGER_JAVA_UI
3889 jsdjc = JSDJ_CreateContext();
3890 if (! jsdjc)
3891 return 1;
3892 JSDJ_SetJSDContext(jsdjc, jsdc);
3893 java_env = JSDJ_CreateJavaVMAndStartDebugger(jsdjc);
3894 #ifdef LIVECONNECT
3895 if (java_env)
3896 (*java_env)->GetJavaVM(java_env, &java_vm);
3897 #endif
3899 * XXX This would be the place to wait for the debugger to start.
3900 * Waiting would be nice in general, but especially when a js file
3901 * is passed on the cmd line.
3903 #endif /* JSDEBUGGER_JAVA_UI */
3904 #ifdef JSDEBUGGER_C_UI
3905 jsdbc = JSDB_InitDebugger(rt, jsdc, 0);
3906 #endif /* JSDEBUGGER_C_UI */
3907 #endif /* JSDEBUGGER */
3909 #ifdef LIVECONNECT
3910 if (!JSJ_SimpleInit(cx, glob, java_vm, getenv("CLASSPATH")))
3911 return 1;
3912 #endif
3914 envobj = JS_DefineObject(cx, glob, "environment", &env_class, NULL, 0);
3915 if (!envobj || !JS_SetPrivate(cx, envobj, envp))
3916 return 1;
3918 #ifdef NARCISSUS
3920 jsval v;
3921 static const char Object_prototype[] = "Object.prototype";
3923 if (!JS_DefineFunction(cx, glob, "snarf", snarf, 1, 0))
3924 return 1;
3925 if (!JS_DefineFunction(cx, glob, "evaluate", Evaluate, 3, 0))
3926 return 1;
3928 if (!JS_EvaluateScript(cx, glob,
3929 Object_prototype, sizeof Object_prototype - 1,
3930 NULL, 0, &v)) {
3931 return 1;
3933 if (!JS_DefineFunction(cx, JSVAL_TO_OBJECT(v), "__defineProperty__",
3934 defineProperty, 5, 0)) {
3935 return 1;
3938 #endif
3940 result = ProcessArgs(cx, glob, argv, argc);
3942 #ifdef JSDEBUGGER
3943 if (jsdc) {
3944 #ifdef JSDEBUGGER_C_UI
3945 if (jsdbc)
3946 JSDB_TermDebugger(jsdc);
3947 #endif /* JSDEBUGGER_C_UI */
3948 JSD_DebuggerOff(jsdc);
3950 #endif /* JSDEBUGGER */
3952 #ifdef JS_THREADSAFE
3953 JS_EndRequest(cx);
3954 #endif
3956 JS_DestroyContext(cx);
3957 JS_DestroyRuntime(rt);
3958 JS_ShutDown();
3959 return result;