1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sw=4 et tw=99:
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
17 * The Original Code is Mozilla Communicator client code, released
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.
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 ***** */
59 #include "jsbuiltins.h"
79 #ifdef JSDEBUGGER_JAVA_UI
81 #endif /* JSDEBUGGER_JAVA_UI */
82 #ifdef JSDEBUGGER_C_UI
84 #endif /* JSDEBUGGER_C_UI */
85 #endif /* JSDEBUGGER */
87 #include "jsworkers.h"
91 #include <sys/types.h>
95 #if defined(XP_WIN) || defined(XP_OS2)
96 #include <io.h> /* for isatty() */
105 typedef enum JSShellExitCode
{
106 EXITCODE_RUNTIME_ERROR
= 3,
107 EXITCODE_FILE_NOT_FOUND
= 4,
108 EXITCODE_OUT_OF_MEMORY
= 5,
112 size_t gStackChunkSize
= 8192;
114 /* Assume that we can not use more than 5e5 bytes of C stack by default. */
115 #if defined(DEBUG) && defined(__SUNPRO_CC)
116 /* Sun compiler uses larger stack space for js_Interpret() with debug
117 Use a bigger gMaxStackSize to make "make check" happy. */
118 static size_t gMaxStackSize
= 5000000;
120 static size_t gMaxStackSize
= 500000;
125 static PRUintn gStackBaseThreadIndex
;
127 static jsuword gStackBase
;
130 static size_t gScriptStackQuota
= JS_DEFAULT_SCRIPT_STACK_QUOTA
;
133 * Limit the timeout to 30 minutes to prevent an overflow on platfoms
134 * that represent the time internally in microseconds using 32-bit int.
136 static jsdouble MAX_TIMEOUT_INTERVAL
= 1800.0;
137 static jsdouble gTimeoutInterval
= -1.0;
138 static volatile bool gCanceled
= false;
140 static bool enableJit
= false;
143 SetTimeoutValue(JSContext
*cx
, jsdouble t
);
146 InitWatchdog(JSRuntime
*rt
);
152 ScheduleWatchdog(JSRuntime
*rt
, jsdouble t
);
155 CancelExecution(JSRuntime
*rt
);
158 * Watchdog thread state.
162 static PRLock
*gWatchdogLock
= NULL
;
163 static PRCondVar
*gWatchdogWakeup
= NULL
;
164 static PRThread
*gWatchdogThread
= NULL
;
165 static bool gWatchdogHasTimeout
= false;
166 static PRIntervalTime gWatchdogTimeout
= 0;
168 static PRCondVar
*gSleepWakeup
= NULL
;
172 static JSRuntime
*gRuntime
= NULL
;
177 JSBool gQuitting
= JS_FALSE
;
178 FILE *gErrFile
= NULL
;
179 FILE *gOutFile
= NULL
;
181 JSObject
*gWorkers
= NULL
;
184 static JSBool reportWarnings
= JS_TRUE
;
185 static JSBool compileOnly
= JS_FALSE
;
187 typedef enum JSShellErrNum
{
188 #define MSG_DEF(name, number, count, exception, format) \
190 #include "jsshell.msg"
197 NewContext(JSRuntime
*rt
);
200 DestroyContext(JSContext
*cx
, bool withGC
);
202 static const JSErrorFormatString
*
203 my_GetErrorMessage(void *userRef
, const char *locale
, const uintN errorNumber
);
206 split_setup(JSContext
*cx
, JSBool evalcx
);
210 JS_EXTERN_API(char) *readline(const char *prompt
);
211 JS_EXTERN_API(void) add_history(char *line
);
217 ToString(JSContext
*aCx
, jsval v
, JSBool aThrow
= JS_FALSE
)
221 mStr
= JS_ValueToString(cx
, v
);
222 if (!aThrow
&& !mStr
&& JS_IsExceptionPending(cx
)) {
223 if (!JS_ReportPendingException(cx
))
224 JS_ClearPendingException(cx
);
226 JS_AddNamedRoot(cx
, &mStr
, "Value ToString helper");
229 JS_RemoveRoot(cx
, &mStr
);
231 JSBool
threw() { return !mStr
; }
232 jsval
getJSVal() { return STRING_TO_JSVAL(mStr
); }
233 const char *getBytes() {
234 return mStr
? JS_GetStringBytes(mStr
) : "(error converting value)";
243 GetLine(FILE *file
, const char * prompt
)
249 * Use readline only if file is stdin, because there's no way to specify
250 * another handle. Are other filehandles interactive?
253 char *linep
= readline(prompt
);
255 * We set it to zero to avoid complaining about inappropriate ioctl
256 * for device in the case of EOF. Looks like errno == 251 if line is
257 * finished with EOF and errno == 25 (EINVAL on Mac) if there is
258 * nothing left to read.
260 if (errno
== 251 || errno
== 25 || errno
== EINVAL
)
264 if (linep
[0] != '\0')
270 if (*prompt
!= '\0') {
271 fprintf(gOutFile
, "%s", prompt
);
275 buffer
= (char *) malloc(size
);
278 char *current
= buffer
;
279 while (fgets(current
, size
- len
, file
)) {
280 len
+= strlen(current
);
281 char *t
= buffer
+ len
- 1;
283 /* Line was read. We remove '\n' and exit. */
287 if (len
+ 1 == size
) {
289 char *tmp
= (char *) realloc(buffer
, size
);
296 current
= buffer
+ len
;
298 if (len
&& !ferror(file
))
305 * State to store as JSContext private.
307 * We declare such timestamp as volatile as they are updated in the operation
308 * callback without taking any locks. Any possible race can only lead to more
309 * frequent callback calls. This is safe as the callback does everything based
312 struct JSShellContextData
{
313 volatile JSIntervalTime startTime
;
316 static JSShellContextData
*
319 /* Prevent creation of new contexts after we have been canceled. */
323 JSShellContextData
*data
= (JSShellContextData
*)
324 calloc(sizeof(JSShellContextData
), 1);
327 data
->startTime
= js_IntervalNow();
331 static inline JSShellContextData
*
332 GetContextData(JSContext
*cx
)
334 JSShellContextData
*data
= (JSShellContextData
*) JS_GetContextPrivate(cx
);
341 ShellOperationCallback(JSContext
*cx
)
346 JS_ClearPendingException(cx
);
351 SetThreadStackLimit(JSContext
*cx
)
355 if (gMaxStackSize
== 0) {
357 * Disable checking for stack overflow if limit is zero.
363 stackBase
= (jsuword
) PR_GetThreadPrivate(gStackBaseThreadIndex
);
365 stackBase
= gStackBase
;
368 #if JS_STACK_GROWTH_DIRECTION > 0
369 stackLimit
= stackBase
+ gMaxStackSize
;
371 stackLimit
= stackBase
- gMaxStackSize
;
377 JS_SetThreadStackLimit(cx
, stackLimit
);
382 SetContextOptions(JSContext
*cx
)
384 SetThreadStackLimit(cx
);
385 JS_SetScriptStackQuota(cx
, gScriptStackQuota
);
386 JS_SetOperationCallback(cx
, ShellOperationCallback
);
394 Process(JSContext
*cx
, JSObject
*obj
, char *filename
, JSBool forceTTY
)
407 if (forceTTY
|| !filename
|| strcmp(filename
, "-") == 0) {
410 file
= fopen(filename
, "r");
412 JS_ReportErrorNumber(cx
, my_GetErrorMessage
, NULL
,
413 JSSMSG_CANT_OPEN
, filename
, strerror(errno
));
414 gExitCode
= EXITCODE_FILE_NOT_FOUND
;
419 SetContextOptions(cx
);
422 /* windows mobile (and possibly other os's) does not have a TTY */
423 if (!forceTTY
&& !isatty(fileno(file
)))
427 * It's not interactive - just execute it.
429 * Support the UNIX #! shell hack; gobble the first line if it starts
430 * with '#'. TODO - this isn't quite compatible with sharp variables,
431 * as a legal js program (using sharp variables) might start with '#'.
432 * But that would require multi-character lookahead.
434 int ch
= fgetc(file
);
436 while((ch
= fgetc(file
)) != EOF
) {
437 if (ch
== '\n' || ch
== '\r')
443 oldopts
= JS_GetOptions(cx
);
444 JS_SetOptions(cx
, oldopts
| JSOPTION_COMPILE_N_GO
| JSOPTION_NO_SCRIPT_RVAL
);
445 script
= JS_CompileFileHandle(cx
, obj
, filename
, file
);
446 JS_SetOptions(cx
, oldopts
);
449 (void)JS_ExecuteScript(cx
, obj
, script
, NULL
);
450 JS_DestroyScript(cx
, script
);
458 /* It's an interactive filehandle; drop into read-eval-print loop. */
462 size
= 0; /* assign here to avoid warnings */
465 * Accumulate lines until we get a 'compilable unit' - one that either
466 * generates an error (before running out of source) or that compiles
467 * cleanly. This should be whenever we get a complete statement that
468 * coincides with the end of a line.
471 size_t len
= 0; /* initialize to avoid warnings */
473 ScheduleWatchdog(cx
->runtime
, -1);
479 JSAutoSuspendRequest
suspended(cx
);
480 line
= GetLine(file
, startline
== lineno
? "js> " : "");
484 JS_ReportError(cx
, strerror(errno
));
493 len
= strlen(buffer
);
497 * len + 1 is required to store '\n' in the end of line.
499 size_t newlen
= strlen(line
) + (len
? len
+ 1 : 0);
500 if (newlen
+ 1 > size
) {
501 size
= newlen
+ 1 > size
* 2 ? newlen
+ 1 : size
* 2;
502 char *newBuf
= (char *) realloc(buffer
, size
);
506 JS_ReportOutOfMemory(cx
);
511 char *current
= buffer
+ len
;
512 if (startline
!= lineno
)
514 strcpy(current
, line
);
519 if (!ScheduleWatchdog(cx
->runtime
, gTimeoutInterval
)) {
523 } while (!JS_BufferIsCompilableUnit(cx
, obj
, buffer
, len
));
525 if (hitEOF
&& !buffer
)
528 /* Clear any pending exception from previous failed compiles. */
529 JS_ClearPendingException(cx
);
531 /* Even though we're interactive, we have a compile-n-go opportunity. */
532 oldopts
= JS_GetOptions(cx
);
534 JS_SetOptions(cx
, oldopts
| JSOPTION_COMPILE_N_GO
);
535 script
= JS_CompileScript(cx
, obj
, buffer
, len
, "typein",
538 JS_SetOptions(cx
, oldopts
);
542 ok
= JS_ExecuteScript(cx
, obj
, script
, &result
);
543 if (ok
&& !JSVAL_IS_VOID(result
)) {
544 str
= JS_ValueToSource(cx
, result
);
546 fprintf(gOutFile
, "%s\n", JS_GetStringBytes(str
));
551 JS_DestroyScript(cx
, script
);
554 } while (!hitEOF
&& !gQuitting
);
557 fprintf(gOutFile
, "\n");
566 fprintf(gErrFile
, "%s\n", JS_GetImplementationVersion());
567 fprintf(gErrFile
, "usage: js [-zKPswWxCij] [-t timeoutSeconds] [-c stackchunksize] [-o option] [-v version] [-f scriptfile] [-e script] [-S maxstacksize] "
572 "[-T TraceVisFileName] "
574 "[scriptfile] [scriptarg...]\n");
579 * JSContext option name to flag map. The option names are in alphabetical
580 * order for better reporting.
582 static const struct {
586 {"anonfunfix", JSOPTION_ANONFUNFIX
},
587 {"atline", JSOPTION_ATLINE
},
588 {"jit", JSOPTION_JIT
},
589 {"relimit", JSOPTION_RELIMIT
},
590 {"strict", JSOPTION_STRICT
},
591 {"werror", JSOPTION_WERROR
},
592 {"xml", JSOPTION_XML
},
596 MapContextOptionNameToFlag(JSContext
* cx
, const char* name
)
598 for (size_t i
= 0; i
!= JS_ARRAY_LENGTH(js_options
); ++i
) {
599 if (strcmp(name
, js_options
[i
].name
) == 0)
600 return js_options
[i
].flag
;
603 char* msg
= JS_sprintf_append(NULL
,
604 "unknown option name '%s'."
605 " The valid names are ", name
);
606 for (size_t i
= 0; i
!= JS_ARRAY_LENGTH(js_options
); ++i
) {
609 msg
= JS_sprintf_append(msg
, "%s%s", js_options
[i
].name
,
610 (i
+ 2 < JS_ARRAY_LENGTH(js_options
)
612 : i
+ 2 == JS_ARRAY_LENGTH(js_options
)
617 JS_ReportOutOfMemory(cx
);
619 JS_ReportError(cx
, msg
);
625 extern JSClass global_class
;
627 #if defined(JS_TRACER) && defined(DEBUG)
629 extern struct JSClass jitstats_class
;
630 void InitJITStatsClass(JSContext
*cx
, JSObject
*glob
);
635 ProcessArgs(JSContext
*cx
, JSObject
*obj
, char **argv
, int argc
)
639 char *filename
= NULL
;
640 JSBool isInteractive
= JS_TRUE
;
641 JSBool forceTTY
= JS_FALSE
;
644 * Scan past all optional arguments so we can create the arguments object
645 * before processing any -f options, which must interleave properly with
646 * -v and -w options. This requires two passes, and without getopt, we'll
647 * have to keep the option logic here and in the second for loop in sync.
649 for (i
= 0; i
< argc
; i
++) {
650 if (argv
[i
][0] != '-' || argv
[i
][1] == '\0') {
654 switch (argv
[i
][1]) {
674 * Create arguments early and define it to root it, so it's safe from any
675 * GC calls nested below, and so it is available to -f <file> arguments.
677 argsObj
= JS_NewArrayObject(cx
, 0, NULL
);
680 if (!JS_DefineProperty(cx
, obj
, "arguments", OBJECT_TO_JSVAL(argsObj
),
686 for (j
= 0; j
< length
; j
++) {
687 JSString
*str
= JS_NewStringCopyZ(cx
, argv
[i
++]);
690 if (!JS_DefineElement(cx
, argsObj
, j
, STRING_TO_JSVAL(str
),
691 NULL
, NULL
, JSPROP_ENUMERATE
)) {
696 for (i
= 0; i
< argc
; i
++) {
697 if (argv
[i
][0] != '-' || argv
[i
][1] == '\0') {
698 filename
= argv
[i
++];
699 isInteractive
= JS_FALSE
;
703 switch (argv
[i
][1]) {
708 JS_SetVersion(cx
, (JSVersion
) atoi(argv
[i
]));
715 JS_SetGCZeal(cx
, !!(atoi(argv
[i
])));
720 reportWarnings
= JS_TRUE
;
724 reportWarnings
= JS_FALSE
;
728 JS_ToggleOptions(cx
, JSOPTION_STRICT
);
732 JS_ToggleOptions(cx
, JSOPTION_RELIMIT
);
736 JS_ToggleOptions(cx
, JSOPTION_XML
);
740 enableJit
= !enableJit
;
741 JS_ToggleOptions(cx
, JSOPTION_JIT
);
742 #if defined(JS_TRACER) && defined(DEBUG)
743 js::InitJITStatsClass(cx
, JS_GetGlobalObject(cx
));
744 JS_DefineObject(cx
, JS_GetGlobalObject(cx
), "tracemonkey",
745 &js::jitstats_class
, NULL
, 0);
754 uint32 flag
= MapContextOptionNameToFlag(cx
, argv
[i
]);
757 JS_ToggleOptions(cx
, flag
);
761 if (JS_GET_CLASS(cx
, JS_GetPrototype(cx
, obj
)) != &global_class
) {
764 if (!JS_SealObject(cx
, obj
, JS_TRUE
))
766 gobj
= JS_NewObject(cx
, &global_class
, NULL
, NULL
);
769 if (!JS_SetPrototype(cx
, gobj
, obj
))
771 JS_SetParent(cx
, gobj
, NULL
);
772 JS_SetGlobalObject(cx
, gobj
);
781 if (!SetTimeoutValue(cx
, atof(argv
[i
])))
787 /* set stack chunk size */
788 gStackChunkSize
= atoi(argv
[++i
]);
795 Process(cx
, obj
, argv
[i
], JS_FALSE
);
800 * XXX: js -f foo.js should interpret foo.js and then
801 * drop into interactive mode, but that breaks the test
802 * harness. Just execute foo.js for now.
804 isInteractive
= JS_FALSE
;
814 /* Pass a filename of -e to imitate PERL */
815 JS_EvaluateScript(cx
, obj
, argv
[i
], strlen(argv
[i
]),
818 isInteractive
= JS_FALSE
;
823 compileOnly
= JS_TRUE
;
824 isInteractive
= JS_FALSE
;
828 isInteractive
= forceTTY
= JS_TRUE
;
835 /* Set maximum stack size. */
836 gMaxStackSize
= atoi(argv
[i
]);
840 obj
= split_setup(cx
, JS_FALSE
);
854 StartTraceVis(argv
[i
]);
862 if (filename
|| isInteractive
)
863 Process(cx
, obj
, filename
, forceTTY
);
868 Version(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
870 if (argc
> 0 && JSVAL_IS_INT(argv
[0]))
871 *rval
= INT_TO_JSVAL(JS_SetVersion(cx
, (JSVersion
) JSVAL_TO_INT(argv
[0])));
873 *rval
= INT_TO_JSVAL(JS_GetVersion(cx
));
878 Options(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
887 for (uintN i
= 0; i
< argc
; i
++) {
888 str
= JS_ValueToString(cx
, argv
[i
]);
891 argv
[i
] = STRING_TO_JSVAL(str
);
892 opt
= JS_GetStringBytes(str
);
895 flag
= MapContextOptionNameToFlag(cx
, opt
);
900 optset
= JS_ToggleOptions(cx
, optset
);
904 for (size_t i
= 0; i
!= JS_ARRAY_LENGTH(js_options
); i
++) {
905 if (js_options
[i
].flag
& optset
) {
907 names
= JS_sprintf_append(names
, "%s%s",
908 names
? "," : "", js_options
[i
].name
);
916 JS_ReportOutOfMemory(cx
);
919 str
= JS_NewString(cx
, names
, strlen(names
));
924 *rval
= STRING_TO_JSVAL(str
);
929 Load(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
933 const char *filename
;
938 for (i
= 0; i
< argc
; i
++) {
939 str
= JS_ValueToString(cx
, argv
[i
]);
942 argv
[i
] = STRING_TO_JSVAL(str
);
943 filename
= JS_GetStringBytes(str
);
945 oldopts
= JS_GetOptions(cx
);
946 JS_SetOptions(cx
, oldopts
| JSOPTION_COMPILE_N_GO
| JSOPTION_NO_SCRIPT_RVAL
);
947 script
= JS_CompileFile(cx
, obj
, filename
);
948 JS_SetOptions(cx
, oldopts
);
953 ? JS_ExecuteScript(cx
, obj
, script
, NULL
)
955 JS_DestroyScript(cx
, script
);
965 * function readline()
966 * Provides a hook for scripts to read a line from stdin.
969 ReadLine(JSContext
*cx
, uintN argc
, jsval
*vp
)
974 size_t bufsize
, buflength
, gotlength
;
981 buf
= (char *) JS_malloc(cx
, bufsize
);
985 sawNewline
= JS_FALSE
;
987 js_fgets(buf
+ buflength
, bufsize
- buflength
, from
)) > 0) {
988 buflength
+= gotlength
;
991 if (buf
[buflength
- 1] == '\n') {
992 buf
[buflength
- 1] = '\0';
993 sawNewline
= JS_TRUE
;
995 } else if (buflength
< bufsize
- 1) {
999 /* Else, grow our buffer for another pass. */
1001 if (bufsize
> buflength
) {
1002 tmp
= (char *) JS_realloc(cx
, buf
, bufsize
);
1004 JS_ReportOutOfMemory(cx
);
1016 /* Treat the empty string specially. */
1017 if (buflength
== 0) {
1018 *vp
= feof(from
) ? JSVAL_NULL
: JS_GetEmptyStringValue(cx
);
1023 /* Shrink the buffer to the real size. */
1024 tmp
= (char *) JS_realloc(cx
, buf
, buflength
);
1033 * Turn buf into a JSString. Note that buflength includes the trailing null
1036 str
= JS_NewString(cx
, buf
, sawNewline
? buflength
- 1 : buflength
);
1042 *vp
= STRING_TO_JSVAL(str
);
1047 Print(JSContext
*cx
, uintN argc
, jsval
*vp
)
1054 argv
= JS_ARGV(cx
, vp
);
1055 for (i
= 0; i
< argc
; i
++) {
1056 str
= JS_ValueToString(cx
, argv
[i
]);
1059 bytes
= JS_EncodeString(cx
, str
);
1062 fprintf(gOutFile
, "%s%s", i
? " " : "", bytes
);
1066 fputc('\n', gOutFile
);
1069 JS_SET_RVAL(cx
, vp
, JSVAL_VOID
);
1074 Help(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
);
1077 Quit(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
1079 JS_ConvertArguments(cx
, argc
, argv
,"/ i", &gExitCode
);
1081 gQuitting
= JS_TRUE
;
1082 #ifdef JS_THREADSAFE
1084 js::workers::terminateAll(cx
, gWorkers
);
1090 ToSource(JSContext
*cx
, jsval
*vp
)
1092 JSString
*str
= JS_ValueToSource(cx
, *vp
);
1094 *vp
= STRING_TO_JSVAL(str
);
1095 return JS_GetStringBytes(str
);
1097 JS_ClearPendingException(cx
);
1098 return "<<error converting value to string>>";
1102 AssertEq(JSContext
*cx
, uintN argc
, jsval
*vp
)
1104 if (!(argc
== 2 || (argc
== 3 && JSVAL_IS_STRING(JS_ARGV(cx
, vp
)[2])))) {
1105 JS_ReportErrorNumber(cx
, my_GetErrorMessage
, NULL
,
1107 ? JSSMSG_NOT_ENOUGH_ARGS
1109 ? JSSMSG_INVALID_ARGS
1110 : JSSMSG_TOO_MANY_ARGS
,
1115 jsval
*argv
= JS_ARGV(cx
, vp
);
1116 if (!JS_SameValue(cx
, argv
[0], argv
[1])) {
1117 const char *actual
= ToSource(cx
, &argv
[0]);
1118 const char *expected
= ToSource(cx
, &argv
[1]);
1120 JS_ReportErrorNumber(cx
, my_GetErrorMessage
, NULL
, JSSMSG_ASSERT_EQ_FAILED
,
1123 JS_ReportErrorNumber(cx
, my_GetErrorMessage
, NULL
, JSSMSG_ASSERT_EQ_FAILED_MSG
,
1124 actual
, expected
, JS_GetStringBytes(JSVAL_TO_STRING(argv
[2])));
1128 JS_SET_RVAL(cx
, vp
, JSVAL_VOID
);
1133 GC(JSContext
*cx
, uintN argc
, jsval
*vp
)
1135 size_t preBytes
= cx
->runtime
->gcBytes
;
1139 JS_snprintf(buf
, sizeof(buf
), "before %lu, after %lu, break %08lx\n",
1140 (unsigned long)preBytes
, (unsigned long)cx
->runtime
->gcBytes
,
1142 (unsigned long)sbrk(0)
1147 *vp
= STRING_TO_JSVAL(JS_NewStringCopyZ(cx
, buf
));
1153 GCStats(JSContext
*cx
, uintN argc
, jsval
*vp
)
1155 js_DumpGCStats(cx
->runtime
, stdout
);
1162 GCParameter(JSContext
*cx
, uintN argc
, jsval
*vp
)
1165 const char *paramName
;
1170 str
= JS_ValueToString(cx
, JSVAL_VOID
);
1173 str
= JS_ValueToString(cx
, vp
[2]);
1176 vp
[2] = STRING_TO_JSVAL(str
);
1178 paramName
= JS_GetStringBytes(str
);
1181 if (strcmp(paramName
, "maxBytes") == 0) {
1182 param
= JSGC_MAX_BYTES
;
1183 } else if (strcmp(paramName
, "maxMallocBytes") == 0) {
1184 param
= JSGC_MAX_MALLOC_BYTES
;
1185 } else if (strcmp(paramName
, "gcStackpoolLifespan") == 0) {
1186 param
= JSGC_STACKPOOL_LIFESPAN
;
1187 } else if (strcmp(paramName
, "gcBytes") == 0) {
1189 } else if (strcmp(paramName
, "gcNumber") == 0) {
1190 param
= JSGC_NUMBER
;
1191 } else if (strcmp(paramName
, "gcTriggerFactor") == 0) {
1192 param
= JSGC_TRIGGER_FACTOR
;
1195 "the first argument argument must be maxBytes, "
1196 "maxMallocBytes, gcStackpoolLifespan, gcBytes, "
1197 "gcNumber or gcTriggerFactor");
1202 value
= JS_GetGCParameter(cx
->runtime
, param
);
1203 return JS_NewNumberValue(cx
, value
, &vp
[0]);
1206 if (param
== JSGC_NUMBER
||
1207 param
== JSGC_BYTES
) {
1208 JS_ReportError(cx
, "Attempt to change read-only parameter %s",
1213 if (!JS_ValueToECMAUint32(cx
, vp
[3], &value
)) {
1215 "the second argument must be convertable to uint32 "
1216 "with non-zero value");
1219 if (param
== JSGC_TRIGGER_FACTOR
&& value
< 100) {
1221 "the gcTriggerFactor value must be >= 100");
1224 JS_SetGCParameter(cx
->runtime
, param
, value
);
1231 GCZeal(JSContext
*cx
, uintN argc
, jsval
*vp
)
1235 if (!JS_ValueToECMAUint32(cx
, argc
== 0 ? JSVAL_VOID
: vp
[2], &zeal
))
1237 JS_SetGCZeal(cx
, (uint8
)zeal
);
1241 #endif /* JS_GC_ZEAL */
1243 typedef struct JSCountHeapNode JSCountHeapNode
;
1245 struct JSCountHeapNode
{
1248 JSCountHeapNode
*next
;
1251 typedef struct JSCountHeapTracer
{
1253 JSDHashTable visited
;
1255 JSCountHeapNode
*traceList
;
1256 JSCountHeapNode
*recycleList
;
1257 } JSCountHeapTracer
;
1260 CountHeapNotify(JSTracer
*trc
, void *thing
, uint32 kind
)
1262 JSCountHeapTracer
*countTracer
;
1263 JSDHashEntryStub
*entry
;
1264 JSCountHeapNode
*node
;
1266 JS_ASSERT(trc
->callback
== CountHeapNotify
);
1267 countTracer
= (JSCountHeapTracer
*)trc
;
1268 if (!countTracer
->ok
)
1271 entry
= (JSDHashEntryStub
*)
1272 JS_DHashTableOperate(&countTracer
->visited
, thing
, JS_DHASH_ADD
);
1274 JS_ReportOutOfMemory(trc
->context
);
1275 countTracer
->ok
= JS_FALSE
;
1282 node
= countTracer
->recycleList
;
1284 countTracer
->recycleList
= node
->next
;
1286 node
= (JSCountHeapNode
*) JS_malloc(trc
->context
, sizeof *node
);
1288 countTracer
->ok
= JS_FALSE
;
1292 node
->thing
= thing
;
1294 node
->next
= countTracer
->traceList
;
1295 countTracer
->traceList
= node
;
1299 CountHeap(JSContext
*cx
, uintN argc
, jsval
*vp
)
1302 int32 startTraceKind
;
1307 JSCountHeapTracer countTracer
;
1308 JSCountHeapNode
*node
;
1311 static const struct {
1314 } traceKindNames
[] = {
1316 { "object", JSTRACE_OBJECT
},
1317 { "double", JSTRACE_DOUBLE
},
1318 { "string", JSTRACE_STRING
},
1319 #if JS_HAS_XML_SUPPORT
1320 { "xml", JSTRACE_XML
},
1327 v
= JS_ARGV(cx
, vp
)[0];
1328 if (JSVAL_IS_TRACEABLE(v
)) {
1329 startThing
= JSVAL_TO_TRACEABLE(v
);
1330 startTraceKind
= JSVAL_TRACE_KIND(v
);
1331 } else if (v
!= JSVAL_NULL
) {
1333 "the first argument is not null or a heap-allocated "
1341 str
= JS_ValueToString(cx
, JS_ARGV(cx
, vp
)[1]);
1344 bytes
= JS_GetStringBytes(str
);
1348 if (strcmp(bytes
, traceKindNames
[i
].name
) == 0) {
1349 traceKind
= traceKindNames
[i
].kind
;
1352 if (++i
== JS_ARRAY_LENGTH(traceKindNames
)) {
1353 JS_ReportError(cx
, "trace kind name '%s' is unknown", bytes
);
1359 JS_TRACER_INIT(&countTracer
.base
, cx
, CountHeapNotify
);
1360 if (!JS_DHashTableInit(&countTracer
.visited
, JS_DHashGetStubOps(),
1361 NULL
, sizeof(JSDHashEntryStub
),
1362 JS_DHASH_DEFAULT_CAPACITY(100))) {
1363 JS_ReportOutOfMemory(cx
);
1366 countTracer
.ok
= JS_TRUE
;
1367 countTracer
.traceList
= NULL
;
1368 countTracer
.recycleList
= NULL
;
1371 JS_TraceRuntime(&countTracer
.base
);
1373 JS_SET_TRACING_NAME(&countTracer
.base
, "root");
1374 JS_CallTracer(&countTracer
.base
, startThing
, startTraceKind
);
1378 while ((node
= countTracer
.traceList
) != NULL
) {
1379 if (traceKind
== -1 || node
->kind
== traceKind
)
1381 countTracer
.traceList
= node
->next
;
1382 node
->next
= countTracer
.recycleList
;
1383 countTracer
.recycleList
= node
;
1384 JS_TraceChildren(&countTracer
.base
, node
->thing
, node
->kind
);
1386 while ((node
= countTracer
.recycleList
) != NULL
) {
1387 countTracer
.recycleList
= node
->next
;
1390 JS_DHashTableFinish(&countTracer
.visited
);
1392 return countTracer
.ok
&& JS_NewNumberValue(cx
, (jsdouble
) counter
, vp
);
1396 ValueToScript(JSContext
*cx
, jsval v
)
1398 JSScript
*script
= NULL
;
1401 if (!JSVAL_IS_PRIMITIVE(v
)) {
1402 JSObject
*obj
= JSVAL_TO_OBJECT(v
);
1403 JSClass
*clasp
= JS_GET_CLASS(cx
, obj
);
1405 if (clasp
== &js_ScriptClass
) {
1406 script
= (JSScript
*) JS_GetPrivate(cx
, obj
);
1407 } else if (clasp
== &js_GeneratorClass
.base
) {
1408 JSGenerator
*gen
= (JSGenerator
*) JS_GetPrivate(cx
, obj
);
1409 fun
= gen
->frame
.fun
;
1410 script
= FUN_SCRIPT(fun
);
1415 fun
= JS_ValueToFunction(cx
, v
);
1418 script
= FUN_SCRIPT(fun
);
1420 JS_ReportErrorNumber(cx
, my_GetErrorMessage
, NULL
,
1421 JSSMSG_SCRIPTS_ONLY
);
1429 GetTrapArgs(JSContext
*cx
, uintN argc
, jsval
*argv
, JSScript
**scriptp
,
1436 *scriptp
= JS_GetScriptedCaller(cx
, NULL
)->script
;
1441 if (!JSVAL_IS_PRIMITIVE(v
) &&
1442 (JS_GET_CLASS(cx
, JSVAL_TO_OBJECT(v
)) == &js_FunctionClass
||
1443 JS_GET_CLASS(cx
, JSVAL_TO_OBJECT(v
)) == &js_ScriptClass
)) {
1444 script
= ValueToScript(cx
, v
);
1450 if (argc
> intarg
) {
1451 if (!JS_ValueToInt32(cx
, argv
[intarg
], ip
))
1459 TrapHandler(JSContext
*cx
, JSScript
*script
, jsbytecode
*pc
, jsval
*rval
,
1463 JSStackFrame
*caller
;
1465 str
= JSVAL_TO_STRING(closure
);
1466 caller
= JS_GetScriptedCaller(cx
, NULL
);
1467 if (!JS_EvaluateUCInStackFrame(cx
, caller
,
1468 JS_GetStringChars(str
), JS_GetStringLength(str
),
1469 caller
->script
->filename
, caller
->script
->lineno
,
1471 return JSTRAP_ERROR
;
1473 if (!JSVAL_IS_VOID(*rval
))
1474 return JSTRAP_RETURN
;
1475 return JSTRAP_CONTINUE
;
1479 Trap(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
1486 JS_ReportErrorNumber(cx
, my_GetErrorMessage
, NULL
, JSSMSG_TRAP_USAGE
);
1490 str
= JS_ValueToString(cx
, argv
[argc
]);
1493 argv
[argc
] = STRING_TO_JSVAL(str
);
1494 if (!GetTrapArgs(cx
, argc
, argv
, &script
, &i
))
1496 return JS_SetTrap(cx
, script
, script
->code
+ i
, TrapHandler
, STRING_TO_JSVAL(str
));
1500 Untrap(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
1505 if (!GetTrapArgs(cx
, argc
, argv
, &script
, &i
))
1507 JS_ClearTrap(cx
, script
, script
->code
+ i
, NULL
, NULL
);
1512 LineToPC(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
1520 JS_ReportErrorNumber(cx
, my_GetErrorMessage
, NULL
, JSSMSG_LINE2PC_USAGE
);
1523 script
= JS_GetScriptedCaller(cx
, NULL
)->script
;
1524 if (!GetTrapArgs(cx
, argc
, argv
, &script
, &i
))
1526 lineno
= (i
== 0) ? script
->lineno
: (uintN
)i
;
1527 pc
= JS_LineNumberToPC(cx
, script
, lineno
);
1530 *rval
= INT_TO_JSVAL(pc
- script
->code
);
1535 PCToLine(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
1541 if (!GetTrapArgs(cx
, argc
, argv
, &script
, &i
))
1543 lineno
= JS_PCToLineNumber(cx
, script
, script
->code
+ i
);
1546 *rval
= INT_TO_JSVAL(lineno
);
1553 UpdateSwitchTableBounds(JSContext
*cx
, JSScript
*script
, uintN offset
,
1554 uintN
*start
, uintN
*end
)
1561 pc
= script
->code
+ offset
;
1562 op
= js_GetOpcode(cx
, script
, pc
);
1564 case JSOP_TABLESWITCHX
:
1565 jmplen
= JUMPX_OFFSET_LEN
;
1567 case JSOP_TABLESWITCH
:
1568 jmplen
= JUMP_OFFSET_LEN
;
1571 low
= GET_JUMP_OFFSET(pc
);
1572 pc
+= JUMP_OFFSET_LEN
;
1573 high
= GET_JUMP_OFFSET(pc
);
1574 pc
+= JUMP_OFFSET_LEN
;
1578 case JSOP_LOOKUPSWITCHX
:
1579 jmplen
= JUMPX_OFFSET_LEN
;
1581 case JSOP_LOOKUPSWITCH
:
1582 jmplen
= JUMP_OFFSET_LEN
;
1587 jmplen
+= JUMP_OFFSET_LEN
;
1591 /* [condswitch] switch does not have any jump or lookup tables. */
1592 JS_ASSERT(op
== JSOP_CONDSWITCH
);
1596 *start
= (uintN
)(pc
- script
->code
);
1597 *end
= *start
+ (uintN
)(n
* jmplen
);
1601 SrcNotes(JSContext
*cx
, JSScript
*script
)
1603 uintN offset
, delta
, caseOff
, switchTableStart
, switchTableEnd
;
1604 jssrcnote
*notes
, *sn
;
1611 fprintf(gOutFile
, "\nSource notes:\n");
1613 notes
= script
->notes();
1614 switchTableEnd
= switchTableStart
= 0;
1615 for (sn
= notes
; !SN_IS_TERMINATOR(sn
); sn
= SN_NEXT(sn
)) {
1616 delta
= SN_DELTA(sn
);
1618 type
= (JSSrcNoteType
) SN_TYPE(sn
);
1619 name
= js_SrcNoteSpec
[type
].name
;
1620 if (type
== SRC_LABEL
) {
1621 /* Check if the source note is for a switch case. */
1622 if (switchTableStart
<= offset
&& offset
< switchTableEnd
) {
1625 JS_ASSERT(js_GetOpcode(cx
, script
, script
->code
+ offset
) == JSOP_NOP
);
1628 fprintf(gOutFile
, "%3u: %5u [%4u] %-8s",
1629 (uintN
) (sn
- notes
), offset
, delta
, name
);
1632 fprintf(gOutFile
, " lineno %u", (uintN
) js_GetSrcNoteOffset(sn
, 0));
1635 fprintf(gOutFile
, " cond %u update %u tail %u",
1636 (uintN
) js_GetSrcNoteOffset(sn
, 0),
1637 (uintN
) js_GetSrcNoteOffset(sn
, 1),
1638 (uintN
) js_GetSrcNoteOffset(sn
, 2));
1641 fprintf(gOutFile
, " else %u elseif %u",
1642 (uintN
) js_GetSrcNoteOffset(sn
, 0),
1643 (uintN
) js_GetSrcNoteOffset(sn
, 1));
1651 fprintf(gOutFile
, " offset %u", (uintN
) js_GetSrcNoteOffset(sn
, 0));
1654 case SRC_LABELBRACE
:
1655 case SRC_BREAK2LABEL
:
1656 case SRC_CONT2LABEL
:
1657 index
= js_GetSrcNoteOffset(sn
, 0);
1658 JS_GET_SCRIPT_ATOM(script
, NULL
, index
, atom
);
1659 JS_ASSERT(ATOM_IS_STRING(atom
));
1660 str
= ATOM_TO_STRING(atom
);
1661 fprintf(gOutFile
, " atom %u (", index
);
1662 js_FileEscapedString(gOutFile
, str
, 0);
1663 putc(')', gOutFile
);
1670 index
= js_GetSrcNoteOffset(sn
, 0);
1671 obj
= script
->getObject(index
);
1672 fun
= (JSFunction
*) JS_GetPrivate(cx
, obj
);
1673 str
= JS_DecompileFunction(cx
, fun
, JS_DONT_PRETTY_PRINT
);
1675 bytes
= JS_GetStringBytes(str
);
1677 if (JS_IsExceptionPending(cx
)) {
1678 if (!JS_ReportPendingException(cx
))
1679 JS_ClearPendingException(cx
);
1683 fprintf(gOutFile
, " function %u (%s)", index
, bytes
);
1687 fprintf(gOutFile
, " length %u", (uintN
) js_GetSrcNoteOffset(sn
, 0));
1688 caseOff
= (uintN
) js_GetSrcNoteOffset(sn
, 1);
1690 fprintf(gOutFile
, " first case offset %u", caseOff
);
1691 UpdateSwitchTableBounds(cx
, script
, offset
,
1692 &switchTableStart
, &switchTableEnd
);
1695 delta
= (uintN
) js_GetSrcNoteOffset(sn
, 0);
1697 if (script
->main
[offset
] == JSOP_LEAVEBLOCK
)
1698 fprintf(gOutFile
, " stack depth %u", delta
);
1700 fprintf(gOutFile
, " guard delta %u", delta
);
1705 fputc('\n', gOutFile
);
1710 Notes(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
1715 for (i
= 0; i
< argc
; i
++) {
1716 script
= ValueToScript(cx
, argv
[i
]);
1720 SrcNotes(cx
, script
);
1725 JS_STATIC_ASSERT(JSTRY_CATCH
== 0);
1726 JS_STATIC_ASSERT(JSTRY_FINALLY
== 1);
1727 JS_STATIC_ASSERT(JSTRY_ITER
== 2);
1729 static const char* const TryNoteNames
[] = { "catch", "finally", "iter" };
1732 TryNotes(JSContext
*cx
, JSScript
*script
)
1734 JSTryNote
*tn
, *tnlimit
;
1736 if (script
->trynotesOffset
== 0)
1739 tn
= script
->trynotes()->vector
;
1740 tnlimit
= tn
+ script
->trynotes()->length
;
1741 fprintf(gOutFile
, "\nException table:\n"
1742 "kind stack start end\n");
1744 JS_ASSERT(tn
->kind
< JS_ARRAY_LENGTH(TryNoteNames
));
1745 fprintf(gOutFile
, " %-7s %6u %8u %8u\n",
1746 TryNoteNames
[tn
->kind
], tn
->stackDepth
,
1747 tn
->start
, tn
->start
+ tn
->length
);
1748 } while (++tn
!= tnlimit
);
1753 DisassembleValue(JSContext
*cx
, jsval v
, bool lines
, bool recursive
)
1755 JSScript
*script
= ValueToScript(cx
, v
);
1758 if (VALUE_IS_FUNCTION(cx
, v
)) {
1759 JSFunction
*fun
= JS_ValueToFunction(cx
, v
);
1760 if (fun
&& (fun
->flags
& ~7U)) {
1761 uint16 flags
= fun
->flags
;
1762 fputs("flags:", stdout
);
1764 #define SHOW_FLAG(flag) if (flags & JSFUN_##flag) fputs(" " #flag, stdout);
1769 SHOW_FLAG(BOUND_METHOD
);
1770 SHOW_FLAG(HEAVYWEIGHT
);
1771 SHOW_FLAG(THISP_STRING
);
1772 SHOW_FLAG(THISP_NUMBER
);
1773 SHOW_FLAG(THISP_BOOLEAN
);
1774 SHOW_FLAG(EXPR_CLOSURE
);
1779 if (FUN_INTERPRETED(fun
)) {
1780 if (FUN_NULL_CLOSURE(fun
))
1781 fputs(" NULL_CLOSURE", stdout
);
1782 else if (FUN_FLAT_CLOSURE(fun
))
1783 fputs(" FLAT_CLOSURE", stdout
);
1785 if (fun
->u
.i
.nupvars
) {
1786 fputs("\nupvars: {\n", stdout
);
1788 void *mark
= cx
->tempPool
.getMark();
1789 jsuword
*localNames
= js_GetLocalNameArray(cx
, fun
, &cx
->tempPool
);
1793 JSUpvarArray
*uva
= fun
->u
.i
.script
->upvars();
1794 uintN upvar_base
= fun
->countArgsAndVars();
1796 for (uint32 i
= 0, n
= uva
->length
; i
< n
; i
++) {
1797 JSAtom
*atom
= JS_LOCAL_NAME_TO_ATOM(localNames
[upvar_base
+ i
]);
1798 uint32 cookie
= uva
->vector
[i
];
1800 printf(" %s: {skip:%u, slot:%u},\n",
1801 js_AtomToPrintableString(cx
, atom
),
1802 UPVAR_FRAME_SKIP(cookie
),
1803 UPVAR_FRAME_SLOT(cookie
));
1806 cx
->tempPool
.release(mark
);
1814 if (!js_Disassemble(cx
, script
, lines
, stdout
))
1816 SrcNotes(cx
, script
);
1817 TryNotes(cx
, script
);
1819 if (recursive
&& script
->objectsOffset
!= 0) {
1820 JSObjectArray
*objects
= script
->objects();
1821 for (uintN i
= 0; i
!= objects
->length
; ++i
) {
1822 JSObject
*obj
= objects
->vector
[i
];
1823 if (obj
->isFunction()) {
1825 if (!DisassembleValue(cx
, OBJECT_TO_JSVAL(obj
),
1826 lines
, recursive
)) {
1836 Disassemble(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
1838 bool lines
= false, recursive
= false;
1840 while (argc
> 0 && JSVAL_IS_STRING(argv
[0])) {
1841 const char *bytes
= JS_GetStringBytes(JSVAL_TO_STRING(argv
[0]));
1842 lines
= !strcmp(bytes
, "-l");
1843 recursive
= !strcmp(bytes
, "-r");
1844 if (!lines
&& !recursive
)
1849 for (uintN i
= 0; i
< argc
; i
++) {
1850 if (!DisassembleValue(cx
, argv
[i
], lines
, recursive
))
1857 DisassFile(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
1860 const char *filename
;
1868 str
= JS_ValueToString(cx
, argv
[0]);
1871 argv
[0] = STRING_TO_JSVAL(str
);
1873 filename
= JS_GetStringBytes(str
);
1874 oldopts
= JS_GetOptions(cx
);
1875 JS_SetOptions(cx
, oldopts
| JSOPTION_COMPILE_N_GO
| JSOPTION_NO_SCRIPT_RVAL
);
1876 script
= JS_CompileFile(cx
, obj
, filename
);
1877 JS_SetOptions(cx
, oldopts
);
1881 obj
= JS_NewScriptObject(cx
, script
);
1885 *rval
= OBJECT_TO_JSVAL(obj
); /* I like to root it, root it. */
1886 ok
= Disassemble(cx
, obj
, 1, rval
, rval
); /* gross, but works! */
1893 DisassWithSrc(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
,
1896 #define LINE_BUF_LEN 512
1897 uintN i
, len
, line1
, line2
, bupline
;
1900 char linebuf
[LINE_BUF_LEN
];
1901 jsbytecode
*pc
, *end
;
1903 static char sep
[] = ";-------------------------";
1906 for (i
= 0; ok
&& i
< argc
; i
++) {
1907 script
= ValueToScript(cx
, argv
[i
]);
1911 if (!script
->filename
) {
1912 JS_ReportErrorNumber(cx
, my_GetErrorMessage
, NULL
,
1913 JSSMSG_FILE_SCRIPTS_ONLY
);
1917 file
= fopen(script
->filename
, "r");
1919 JS_ReportErrorNumber(cx
, my_GetErrorMessage
, NULL
,
1920 JSSMSG_CANT_OPEN
, script
->filename
,
1926 end
= pc
+ script
->length
;
1928 /* burn the leading lines */
1929 line2
= JS_PCToLineNumber(cx
, script
, pc
);
1930 for (line1
= 0; line1
< line2
- 1; line1
++) {
1931 char *tmp
= fgets(linebuf
, LINE_BUF_LEN
, file
);
1933 JS_ReportError(cx
, "failed to read %s fully",
1942 line2
= JS_PCToLineNumber(cx
, script
, pc
);
1944 if (line2
< line1
) {
1945 if (bupline
!= line2
) {
1947 fprintf(gOutFile
, "%s %3u: BACKUP\n", sep
, line2
);
1950 if (bupline
&& line1
== line2
)
1951 fprintf(gOutFile
, "%s %3u: RESTORE\n", sep
, line2
);
1953 while (line1
< line2
) {
1954 if (!fgets(linebuf
, LINE_BUF_LEN
, file
)) {
1955 JS_ReportErrorNumber(cx
, my_GetErrorMessage
, NULL
,
1956 JSSMSG_UNEXPECTED_EOF
,
1962 fprintf(gOutFile
, "%s %3u: %s", sep
, line1
, linebuf
);
1966 len
= js_Disassemble1(cx
, script
, pc
,
1984 Tracing(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
1989 *rval
= BOOLEAN_TO_JSVAL(cx
->tracefp
!= 0);
1993 switch (JS_TypeOfValue(cx
, argv
[0])) {
1995 case JSTYPE_BOOLEAN
: {
1997 JS_ValueToBoolean(cx
, argv
[0], &bval
);
1998 file
= bval
? stderr
: NULL
;
2001 case JSTYPE_STRING
: {
2002 char *name
= JS_GetStringBytes(JSVAL_TO_STRING(argv
[0]));
2003 file
= fopen(name
, "w");
2005 JS_ReportError(cx
, "tracing: couldn't open output file %s: %s",
2006 name
, strerror(errno
));
2014 if (cx
->tracefp
&& cx
->tracefp
!= stderr
)
2015 fclose((FILE *)cx
->tracefp
);
2017 cx
->tracePrevPc
= NULL
;
2021 JSString
*str
= JS_ValueToString(cx
, argv
[0]);
2024 JS_ReportError(cx
, "tracing: illegal argument %s",
2025 JS_GetStringBytes(str
));
2030 DumpScope(JSContext
*cx
, JSObject
*obj
, FILE *fp
)
2033 for (JSScopeProperty
*sprop
= NULL
; JS_PropertyIterator(obj
, &sprop
);) {
2034 fprintf(fp
, "%3u %p ", i
++, (void *) sprop
);
2035 sprop
->dump(cx
, fp
);
2040 DumpStats(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
2050 for (i
= 0; i
< argc
; i
++) {
2051 str
= JS_ValueToString(cx
, argv
[i
]);
2054 argv
[i
] = STRING_TO_JSVAL(str
);
2055 bytes
= JS_GetStringBytes(str
);
2056 if (strcmp(bytes
, "arena") == 0) {
2057 #ifdef JS_ARENAMETER
2058 JS_DumpArenaStats(stdout
);
2060 } else if (strcmp(bytes
, "atom") == 0) {
2061 js_DumpAtoms(cx
, gOutFile
);
2062 } else if (strcmp(bytes
, "global") == 0) {
2063 DumpScope(cx
, cx
->globalObject
, stdout
);
2065 if (!JS_ValueToId(cx
, STRING_TO_JSVAL(str
), &id
))
2067 if (!js_FindProperty(cx
, id
, &obj
, &obj2
, &prop
))
2070 obj2
->dropProperty(cx
, prop
);
2071 if (!obj
->getProperty(cx
, id
, &value
))
2074 if (!prop
|| !JSVAL_IS_OBJECT(value
)) {
2075 fprintf(gErrFile
, "js: invalid stats argument %s\n",
2079 obj
= JSVAL_TO_OBJECT(value
);
2081 DumpScope(cx
, obj
, stdout
);
2088 DumpHeap(JSContext
*cx
, uintN argc
, jsval
*vp
)
2093 uint32 startTraceKind
;
2094 const char *badTraceArg
;
2097 void *thingToIgnore
;
2103 v
= JS_ARGV(cx
, vp
)[0];
2104 if (v
!= JSVAL_NULL
) {
2107 str
= JS_ValueToString(cx
, v
);
2110 JS_ARGV(cx
, vp
)[0] = STRING_TO_JSVAL(str
);
2111 fileName
= JS_GetStringBytes(str
);
2118 v
= JS_ARGV(cx
, vp
)[1];
2119 if (JSVAL_IS_TRACEABLE(v
)) {
2120 startThing
= JSVAL_TO_TRACEABLE(v
);
2121 startTraceKind
= JSVAL_TRACE_KIND(v
);
2122 } else if (v
!= JSVAL_NULL
) {
2123 badTraceArg
= "start";
2124 goto not_traceable_arg
;
2130 v
= JS_ARGV(cx
, vp
)[2];
2131 if (JSVAL_IS_TRACEABLE(v
)) {
2132 thingToFind
= JSVAL_TO_TRACEABLE(v
);
2133 } else if (v
!= JSVAL_NULL
) {
2134 badTraceArg
= "toFind";
2135 goto not_traceable_arg
;
2139 maxDepth
= (size_t)-1;
2141 v
= JS_ARGV(cx
, vp
)[3];
2142 if (v
!= JSVAL_NULL
) {
2145 if (!JS_ValueToECMAUint32(cx
, v
, &depth
))
2151 thingToIgnore
= NULL
;
2153 v
= JS_ARGV(cx
, vp
)[4];
2154 if (JSVAL_IS_TRACEABLE(v
)) {
2155 thingToIgnore
= JSVAL_TO_TRACEABLE(v
);
2156 } else if (v
!= JSVAL_NULL
) {
2157 badTraceArg
= "toIgnore";
2158 goto not_traceable_arg
;
2165 dumpFile
= fopen(fileName
, "w");
2167 JS_ReportError(cx
, "can't open %s: %s", fileName
, strerror(errno
));
2172 ok
= JS_DumpHeap(cx
, dumpFile
, startThing
, startTraceKind
, thingToFind
,
2173 maxDepth
, thingToIgnore
);
2174 if (dumpFile
!= stdout
)
2179 JS_ReportError(cx
, "argument '%s' is not null or a heap-allocated thing",
2190 EscapeWideString(jschar
*w
)
2192 static char enuf
[80];
2193 static char hex
[] = "0123456789abcdef";
2200 for (i
= j
= 0; i
< sizeof enuf
- 1; i
++, j
++) {
2204 b
= (unsigned char)(u
>> 8);
2205 c
= (unsigned char)(u
);
2207 if (i
>= sizeof enuf
- 6)
2211 enuf
[i
++] = hex
[b
>> 4];
2212 enuf
[i
++] = hex
[b
& 15];
2213 enuf
[i
++] = hex
[c
>> 4];
2214 enuf
[i
] = hex
[c
& 15];
2215 } else if (!isprint(c
)) {
2216 if (i
>= sizeof enuf
- 4)
2220 enuf
[i
++] = hex
[c
>> 4];
2221 enuf
[i
] = hex
[c
& 15];
2233 ZZ_formatter(JSContext
*cx
, const char *format
, JSBool fromJS
, jsval
**vpp
,
2240 printf("entering ZZ_formatter");
2244 if (!JS_ValueToNumber(cx
, vp
[0], &re
))
2246 if (!JS_ValueToNumber(cx
, vp
[1], &im
))
2248 *va_arg(ap
, jsdouble
*) = re
;
2249 *va_arg(ap
, jsdouble
*) = im
;
2251 re
= va_arg(ap
, jsdouble
);
2252 im
= va_arg(ap
, jsdouble
);
2253 if (!JS_NewNumberValue(cx
, re
, &vp
[0]))
2255 if (!JS_NewNumberValue(cx
, im
, &vp
[1]))
2260 printf("leaving ZZ_formatter");
2265 ConvertArgs(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
2267 JSBool b
= JS_FALSE
;
2271 jsdouble d
= 0, I
= 0, re
= 0, im
= 0;
2273 JSString
*str
= NULL
;
2275 JSObject
*obj2
= NULL
;
2276 JSFunction
*fun
= NULL
;
2277 jsval v
= JSVAL_VOID
;
2280 if (!JS_AddArgumentFormatter(cx
, "ZZ", ZZ_formatter
))
2282 ok
= JS_ConvertArguments(cx
, argc
, argv
, "b/ciujdIsSWofvZZ*",
2283 &b
, &c
, &i
, &u
, &j
, &d
, &I
, &s
, &str
, &w
, &obj2
,
2284 &fun
, &v
, &re
, &im
);
2285 JS_RemoveArgumentFormatter(cx
, "ZZ");
2289 "b %u, c %x (%c), i %ld, u %lu, j %ld\n",
2290 b
, c
, (char)c
, i
, u
, j
);
2291 ToString
obj2string(cx
, obj2
);
2292 ToString
valueString(cx
, v
);
2293 JSString
*tmpstr
= JS_DecompileFunction(cx
, fun
, 4);
2296 func
= JS_GetStringBytes(tmpstr
);
2298 if (JS_IsExceptionPending(cx
)) {
2299 if (!JS_ReportPendingException(cx
))
2300 JS_ClearPendingException(cx
);
2302 func
= "error decompiling fun";
2305 "d %g, I %g, s %s, S %s, W %s, obj %s, fun %s\n"
2306 "v %s, re %g, im %g\n",
2307 d
, I
, s
, str
? JS_GetStringBytes(str
) : "", EscapeWideString(w
),
2308 obj2string
.getBytes(),
2310 valueString
.getBytes(), re
, im
);
2316 BuildDate(JSContext
*cx
, uintN argc
, jsval
*vp
)
2318 char version
[20] = "\n";
2319 #if JS_VERSION < 150
2320 sprintf(version
, " for version %d\n", JS_VERSION
);
2322 fprintf(gOutFile
, "built on %s at %s%s", __DATE__
, __TIME__
, version
);
2328 Clear(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
2330 if (argc
!= 0 && !JS_ValueToObject(cx
, argv
[0], &obj
))
2332 JS_ClearScope(cx
, obj
);
2337 Intern(JSContext
*cx
, uintN argc
, jsval
*vp
)
2341 str
= JS_ValueToString(cx
, argc
== 0 ? JSVAL_VOID
: vp
[2]);
2344 if (!JS_InternUCStringN(cx
, JS_GetStringChars(str
),
2345 JS_GetStringLength(str
))) {
2353 Clone(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
2355 JSObject
*funobj
, *parent
, *clone
;
2357 if (VALUE_IS_FUNCTION(cx
, argv
[0])) {
2358 funobj
= JSVAL_TO_OBJECT(argv
[0]);
2360 JSFunction
*fun
= JS_ValueToFunction(cx
, argv
[0]);
2363 funobj
= JS_GetFunctionObject(fun
);
2366 if (!JS_ValueToObject(cx
, argv
[1], &parent
))
2369 parent
= JS_GetParent(cx
, funobj
);
2371 clone
= JS_CloneFunctionObject(cx
, funobj
, parent
);
2374 *rval
= OBJECT_TO_JSVAL(clone
);
2379 Seal(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
2382 JSBool deep
= JS_FALSE
;
2384 if (!JS_ConvertArguments(cx
, argc
, argv
, "o/b", &target
, &deep
))
2388 return JS_SealObject(cx
, target
, deep
);
2392 GetPDA(JSContext
*cx
, uintN argc
, jsval
*vp
)
2394 JSObject
*vobj
, *aobj
, *pdobj
;
2396 JSPropertyDescArray pda
;
2401 if (!JS_ValueToObject(cx
, argc
== 0 ? JSVAL_VOID
: vp
[2], &vobj
))
2408 aobj
= JS_NewArrayObject(cx
, 0, NULL
);
2411 *vp
= OBJECT_TO_JSVAL(aobj
);
2413 ok
= JS_GetPropertyDescArray(cx
, vobj
, &pda
);
2417 for (i
= 0; i
< pda
.length
; i
++, pd
++) {
2418 pdobj
= JS_NewObject(cx
, NULL
, NULL
, NULL
);
2424 /* Protect pdobj from GC by setting it as an element of aobj now */
2425 v
= OBJECT_TO_JSVAL(pdobj
);
2426 ok
= JS_SetElement(cx
, aobj
, i
, &v
);
2430 ok
= JS_SetProperty(cx
, pdobj
, "id", &pd
->id
) &&
2431 JS_SetProperty(cx
, pdobj
, "value", &pd
->value
) &&
2432 (v
= INT_TO_JSVAL(pd
->flags
),
2433 JS_SetProperty(cx
, pdobj
, "flags", &v
)) &&
2434 (v
= INT_TO_JSVAL(pd
->slot
),
2435 JS_SetProperty(cx
, pdobj
, "slot", &v
)) &&
2436 JS_SetProperty(cx
, pdobj
, "alias", &pd
->alias
);
2440 JS_PutPropertyDescArray(cx
, &pda
);
2445 GetSLX(JSContext
*cx
, uintN argc
, jsval
*vp
)
2449 script
= ValueToScript(cx
, argc
== 0 ? JSVAL_VOID
: vp
[2]);
2452 *vp
= INT_TO_JSVAL(js_GetScriptLineExtent(script
));
2457 ToInt32(JSContext
*cx
, uintN argc
, jsval
*vp
)
2461 if (!JS_ValueToInt32(cx
, argc
== 0 ? JSVAL_VOID
: vp
[2], &i
))
2463 return JS_NewNumberValue(cx
, i
, vp
);
2467 StringsAreUTF8(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
,
2470 *rval
= JS_CStringsAreUTF8() ? JSVAL_TRUE
: JSVAL_FALSE
;
2475 StackQuota(JSContext
*cx
, uintN argc
, jsval
*vp
)
2480 return JS_NewNumberValue(cx
, (double) gScriptStackQuota
, vp
);
2481 if (!JS_ValueToECMAUint32(cx
, JS_ARGV(cx
, vp
)[0], &n
))
2483 gScriptStackQuota
= n
;
2484 JS_SetScriptStackQuota(cx
, gScriptStackQuota
);
2485 JS_SET_RVAL(cx
, vp
, JSVAL_VOID
);
2489 static const char* badUTF8
= "...\xC0...";
2490 static const char* bigUTF8
= "...\xFB\xBF\xBF\xBF\xBF...";
2491 static const jschar badSurrogate
[] = { 'A', 'B', 'C', 0xDEEE, 'D', 'E', 0 };
2494 TestUTF8(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
2498 size_t charsLength
= 5;
2500 size_t bytesLength
= 20;
2501 if (argc
&& !JS_ValueToInt32(cx
, *argv
, &mode
))
2504 /* The following throw errors if compiled with UTF-8. */
2506 /* mode 1: malformed UTF-8 string. */
2508 JS_NewStringCopyZ(cx
, badUTF8
);
2510 /* mode 2: big UTF-8 character. */
2512 JS_NewStringCopyZ(cx
, bigUTF8
);
2514 /* mode 3: bad surrogate character. */
2516 JS_EncodeCharacters(cx
, badSurrogate
, 6, bytes
, &bytesLength
);
2518 /* mode 4: use a too small buffer. */
2520 JS_DecodeBytes(cx
, "1234567890", 10, chars
, &charsLength
);
2523 JS_ReportError(cx
, "invalid mode parameter");
2526 return !JS_IsExceptionPending (cx
);
2530 ThrowError(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
2532 JS_ReportError(cx
, "This is an error");
2536 #define LAZY_STANDARD_CLASSES
2538 /* A class for easily testing the inner/outer object callbacks. */
2539 typedef struct ComplexObject
{
2547 split_create_outer(JSContext
*cx
);
2550 split_create_inner(JSContext
*cx
, JSObject
*outer
);
2552 static ComplexObject
*
2553 split_get_private(JSContext
*cx
, JSObject
*obj
);
2556 split_addProperty(JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
2561 cpx
= split_get_private(cx
, obj
);
2564 if (!cpx
->isInner
&& cpx
->inner
) {
2565 /* Make sure to define this property on the inner object. */
2566 if (!JS_ValueToId(cx
, id
, &asId
))
2568 return JS_DefinePropertyById(cx
, cpx
->inner
, asId
, *vp
, NULL
, NULL
, JSPROP_ENUMERATE
);
2574 split_getProperty(JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
2578 cpx
= split_get_private(cx
, obj
);
2582 if (JSVAL_IS_STRING(id
) &&
2583 !strcmp(JS_GetStringBytes(JSVAL_TO_STRING(id
)), "isInner")) {
2584 *vp
= BOOLEAN_TO_JSVAL(cpx
->isInner
);
2588 if (!cpx
->isInner
&& cpx
->inner
) {
2589 if (JSVAL_IS_STRING(id
)) {
2592 str
= JSVAL_TO_STRING(id
);
2593 return JS_GetUCProperty(cx
, cpx
->inner
, JS_GetStringChars(str
),
2594 JS_GetStringLength(str
), vp
);
2596 if (JSVAL_IS_INT(id
))
2597 return JS_GetElement(cx
, cpx
->inner
, JSVAL_TO_INT(id
), vp
);
2605 split_setProperty(JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
2609 cpx
= split_get_private(cx
, obj
);
2612 if (!cpx
->isInner
&& cpx
->inner
) {
2613 if (JSVAL_IS_STRING(id
)) {
2616 str
= JSVAL_TO_STRING(id
);
2617 return JS_SetUCProperty(cx
, cpx
->inner
, JS_GetStringChars(str
),
2618 JS_GetStringLength(str
), vp
);
2620 if (JSVAL_IS_INT(id
))
2621 return JS_SetElement(cx
, cpx
->inner
, JSVAL_TO_INT(id
), vp
);
2629 split_delProperty(JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
2634 cpx
= split_get_private(cx
, obj
);
2637 if (!cpx
->isInner
&& cpx
->inner
) {
2638 /* Make sure to define this property on the inner object. */
2639 if (!JS_ValueToId(cx
, *vp
, &asId
))
2641 return cpx
->inner
->deleteProperty(cx
, asId
, vp
);
2647 split_enumerate(JSContext
*cx
, JSObject
*obj
, JSIterateOp enum_op
,
2648 jsval
*statep
, jsid
*idp
)
2654 case JSENUMERATE_INIT
:
2655 cpx
= (ComplexObject
*) JS_GetPrivate(cx
, obj
);
2657 if (!cpx
->isInner
&& cpx
->inner
)
2660 iterator
= JS_NewPropertyIterator(cx
, obj
);
2664 *statep
= OBJECT_TO_JSVAL(iterator
);
2669 case JSENUMERATE_NEXT
:
2670 iterator
= (JSObject
*)JSVAL_TO_OBJECT(*statep
);
2671 if (!JS_NextProperty(cx
, iterator
, idp
))
2674 if (!JSVAL_IS_VOID(*idp
))
2678 case JSENUMERATE_DESTROY
:
2679 /* Let GC at our iterator object. */
2680 *statep
= JSVAL_NULL
;
2688 split_resolve(JSContext
*cx
, JSObject
*obj
, jsval id
, uintN flags
, JSObject
**objp
)
2692 if (JSVAL_IS_STRING(id
) &&
2693 !strcmp(JS_GetStringBytes(JSVAL_TO_STRING(id
)), "isInner")) {
2695 return JS_DefineProperty(cx
, obj
, "isInner", JSVAL_VOID
, NULL
, NULL
,
2699 cpx
= split_get_private(cx
, obj
);
2702 if (!cpx
->isInner
&& cpx
->inner
) {
2706 if (!JS_ValueToId(cx
, id
, &asId
))
2709 if (!cpx
->inner
->lookupProperty(cx
, asId
, objp
, &prop
))
2712 cpx
->inner
->dropProperty(cx
, prop
);
2717 #ifdef LAZY_STANDARD_CLASSES
2718 if (!(flags
& JSRESOLVE_ASSIGNING
)) {
2721 if (!JS_ResolveStandardClass(cx
, obj
, id
, &resolved
))
2731 /* XXX For additional realism, let's resolve some random property here. */
2736 split_finalize(JSContext
*cx
, JSObject
*obj
)
2738 JS_free(cx
, JS_GetPrivate(cx
, obj
));
2742 split_mark(JSContext
*cx
, JSObject
*obj
, void *arg
)
2746 cpx
= (ComplexObject
*) JS_GetPrivate(cx
, obj
);
2748 if (!cpx
->isInner
&& cpx
->inner
) {
2749 /* Mark the inner object. */
2750 JS_MarkGCThing(cx
, cpx
->inner
, "ComplexObject.inner", arg
);
2757 split_outerObject(JSContext
*cx
, JSObject
*obj
)
2761 cpx
= (ComplexObject
*) JS_GetPrivate(cx
, obj
);
2762 return cpx
->isInner
? cpx
->outer
: obj
;
2766 split_thisObject(JSContext
*cx
, JSObject
*obj
)
2768 OBJ_TO_OUTER_OBJECT(cx
, obj
);
2774 static JSObjectOps split_objectops
;
2776 static JSObjectOps
*
2777 split_getObjectOps(JSContext
*cx
, JSClass
*clasp
)
2779 if (!split_objectops
.thisObject
) {
2780 memcpy(&split_objectops
, &js_ObjectOps
, sizeof split_objectops
);
2781 split_objectops
.thisObject
= split_thisObject
;
2782 split_objectops
.call
= NULL
;
2783 split_objectops
.construct
= NULL
;
2786 return &split_objectops
;
2790 split_equality(JSContext
*cx
, JSObject
*obj
, jsval v
, JSBool
*bp
);
2793 split_innerObject(JSContext
*cx
, JSObject
*obj
)
2797 cpx
= (ComplexObject
*) JS_GetPrivate(cx
, obj
);
2799 JS_ASSERT(!cpx
->isInner
);
2802 return !cpx
->isInner
? cpx
->inner
: obj
;
2805 static JSExtendedClass split_global_class
= {
2807 JSCLASS_NEW_RESOLVE
| JSCLASS_NEW_ENUMERATE
| JSCLASS_HAS_PRIVATE
|
2808 JSCLASS_GLOBAL_FLAGS
| JSCLASS_IS_EXTENDED
,
2809 split_addProperty
, split_delProperty
,
2810 split_getProperty
, split_setProperty
,
2811 (JSEnumerateOp
)split_enumerate
,
2812 (JSResolveOp
)split_resolve
,
2813 JS_ConvertStub
, split_finalize
,
2814 split_getObjectOps
, NULL
, NULL
, NULL
, NULL
, NULL
,
2816 split_equality
, split_outerObject
, split_innerObject
,
2817 NULL
, NULL
, NULL
, NULL
, NULL
2821 split_equality(JSContext
*cx
, JSObject
*obj
, jsval v
, JSBool
*bp
)
2824 if (JSVAL_IS_PRIMITIVE(v
))
2827 JSObject
*obj2
= JSVAL_TO_OBJECT(v
);
2828 if (JS_GET_CLASS(cx
, obj2
) != &split_global_class
.base
)
2831 ComplexObject
*cpx
= (ComplexObject
*) JS_GetPrivate(cx
, obj2
);
2832 JS_ASSERT(!cpx
->isInner
);
2834 ComplexObject
*ourCpx
= (ComplexObject
*) JS_GetPrivate(cx
, obj
);
2835 JS_ASSERT(!ourCpx
->isInner
);
2837 *bp
= (cpx
== ourCpx
);
2842 split_create_outer(JSContext
*cx
)
2847 cpx
= (ComplexObject
*) JS_malloc(cx
, sizeof *obj
);
2850 cpx
->isInner
= JS_FALSE
;
2851 cpx
->frozen
= JS_TRUE
;
2855 obj
= JS_NewObject(cx
, &split_global_class
.base
, NULL
, NULL
);
2856 if (!obj
|| !JS_SetParent(cx
, obj
, NULL
)) {
2861 if (!JS_SetPrivate(cx
, obj
, cpx
)) {
2870 split_create_inner(JSContext
*cx
, JSObject
*outer
)
2872 ComplexObject
*cpx
, *outercpx
;
2875 JS_ASSERT(JS_GET_CLASS(cx
, outer
) == &split_global_class
.base
);
2877 cpx
= (ComplexObject
*) JS_malloc(cx
, sizeof *cpx
);
2880 cpx
->isInner
= JS_TRUE
;
2881 cpx
->frozen
= JS_FALSE
;
2885 obj
= JS_NewObject(cx
, &split_global_class
.base
, NULL
, NULL
);
2886 if (!obj
|| !JS_SetParent(cx
, obj
, NULL
) || !JS_SetPrivate(cx
, obj
, cpx
)) {
2891 outercpx
= (ComplexObject
*) JS_GetPrivate(cx
, outer
);
2892 outercpx
->inner
= obj
;
2893 outercpx
->frozen
= JS_FALSE
;
2898 static ComplexObject
*
2899 split_get_private(JSContext
*cx
, JSObject
*obj
)
2902 if (JS_GET_CLASS(cx
, obj
) == &split_global_class
.base
)
2903 return (ComplexObject
*) JS_GetPrivate(cx
, obj
);
2904 obj
= JS_GetParent(cx
, obj
);
2911 sandbox_enumerate(JSContext
*cx
, JSObject
*obj
)
2916 if (!JS_GetProperty(cx
, obj
, "lazy", &v
))
2919 JS_ValueToBoolean(cx
, v
, &b
);
2920 return !b
|| JS_EnumerateStandardClasses(cx
, obj
);
2924 sandbox_resolve(JSContext
*cx
, JSObject
*obj
, jsval id
, uintN flags
,
2930 if (!JS_GetProperty(cx
, obj
, "lazy", &v
))
2933 JS_ValueToBoolean(cx
, v
, &b
);
2934 if (b
&& (flags
& JSRESOLVE_ASSIGNING
) == 0) {
2935 if (!JS_ResolveStandardClass(cx
, obj
, id
, &resolved
))
2946 static JSClass sandbox_class
= {
2948 JSCLASS_NEW_RESOLVE
| JSCLASS_GLOBAL_FLAGS
,
2949 JS_PropertyStub
, JS_PropertyStub
,
2950 JS_PropertyStub
, JS_PropertyStub
,
2951 sandbox_enumerate
, (JSResolveOp
)sandbox_resolve
,
2952 JS_ConvertStub
, NULL
,
2953 JSCLASS_NO_OPTIONAL_MEMBERS
2957 EvalInContext(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
,
2965 JSBool lazy
, split
, ok
;
2969 if (!JS_ConvertArguments(cx
, argc
, argv
, "S / o", &str
, &sobj
))
2972 scx
= NewContext(JS_GetRuntime(cx
));
2974 JS_ReportOutOfMemory(cx
);
2977 JS_SetOptions(scx
, JS_GetOptions(cx
));
2979 JS_TransferRequest(cx
, scx
);
2980 src
= JS_GetStringChars(str
);
2981 srclen
= JS_GetStringLength(str
);
2982 split
= lazy
= JS_FALSE
;
2984 if (src
[0] == 'l' && src
[1] == 'a' && src
[2] == 'z' && src
[3] == 'y') {
2988 } else if (srclen
== 5) {
2989 if (src
[0] == 's' && src
[1] == 'p' && src
[2] == 'l' && src
[3] == 'i' && src
[4] == 't') {
2990 split
= lazy
= JS_TRUE
;
2997 ? split_setup(scx
, JS_TRUE
)
2998 : JS_NewObject(scx
, &sandbox_class
, NULL
, NULL
);
2999 if (!sobj
|| (!lazy
&& !JS_InitStandardClasses(scx
, sobj
))) {
3003 AutoValueRooter
root(scx
, BOOLEAN_TO_JSVAL(lazy
));
3004 ok
= JS_SetProperty(scx
, sobj
, "lazy", root
.addr());
3008 sobj
= split_outerObject(scx
, sobj
);
3012 *rval
= OBJECT_TO_JSVAL(sobj
);
3015 fp
= JS_GetScriptedCaller(cx
, NULL
);
3016 JS_SetGlobalObject(scx
, sobj
);
3017 JS_ToggleOptions(scx
, JSOPTION_DONT_REPORT_UNCAUGHT
);
3018 OBJ_TO_INNER_OBJECT(scx
, sobj
);
3023 ok
= JS_EvaluateUCScript(scx
, sobj
, src
, srclen
,
3024 fp
->script
->filename
,
3025 JS_PCToLineNumber(cx
, fp
->script
,
3031 jsval exceptionValue
= JSVAL_NULL
;
3032 JSBool exception
= !ok
&& JS_GetPendingException(scx
, &exceptionValue
);
3034 JS_TransferRequest(scx
, cx
);
3036 JS_SetPendingException(cx
, exceptionValue
);
3038 JS_ClearPendingException(cx
);
3040 DestroyContext(scx
, false);
3045 EvalInFrame(JSContext
*cx
, uintN argc
, jsval
*vp
)
3047 jsval
*argv
= JS_ARGV(cx
, vp
);
3049 !JSVAL_IS_INT(argv
[0]) ||
3050 !JSVAL_IS_STRING(argv
[1])) {
3051 JS_ReportError(cx
, "Invalid arguments to evalInFrame");
3055 uint32 upCount
= JSVAL_TO_INT(argv
[0]);
3056 JSString
*str
= JSVAL_TO_STRING(argv
[1]);
3058 bool saveCurrent
= (argc
>= 3 && JSVAL_IS_BOOLEAN(argv
[2]))
3059 ? !!(JSVAL_TO_SPECIAL(argv
[2]))
3064 JSStackFrame
*fp
= cx
->fp
;
3065 for (uint32 i
= 0; i
< upCount
; ++i
) {
3072 JS_ReportError(cx
, "cannot eval in non-script frame");
3076 JSStackFrame
*oldfp
= NULL
;
3078 oldfp
= JS_SaveFrameChain(cx
);
3080 JSBool ok
= JS_EvaluateUCInStackFrame(cx
, fp
, str
->chars(), str
->length(),
3081 fp
->script
->filename
,
3082 JS_PCToLineNumber(cx
, fp
->script
,
3087 JS_RestoreFrameChain(cx
, oldfp
);
3093 ShapeOf(JSContext
*cx
, uintN argc
, jsval
*vp
)
3095 jsval v
= JS_ARGV(cx
, vp
)[0];
3096 if (!JSVAL_IS_OBJECT(v
)) {
3097 JS_ReportError(cx
, "shapeOf: object expected");
3100 JSObject
*obj
= JSVAL_TO_OBJECT(v
);
3105 if (!obj
->isNative()) {
3106 *vp
= INT_TO_JSVAL(-1);
3109 return JS_NewNumberValue(cx
, obj
->shape(), vp
);
3112 #ifdef JS_THREADSAFE
3115 * Check that t1 comes strictly before t2. The function correctly deals with
3116 * PRIntervalTime wrap-around between t2 and t1 assuming that t2 and t1 stays
3117 * within INT32_MAX from each other. We use MAX_TIMEOUT_INTERVAL to enforce
3121 IsBefore(PRIntervalTime t1
, PRIntervalTime t2
)
3123 return int32(t1
- t2
) < 0;
3127 Sleep_fn(JSContext
*cx
, uintN argc
, jsval
*vp
)
3129 PRIntervalTime t_ticks
;
3136 if (!JS_ValueToNumber(cx
, argc
== 0 ? JSVAL_VOID
: vp
[2], &t_secs
))
3139 /* NB: The next condition also filter out NaNs. */
3140 if (!(t_secs
<= MAX_TIMEOUT_INTERVAL
)) {
3141 JS_ReportError(cx
, "Excessive sleep interval");
3144 t_ticks
= (t_secs
<= 0.0)
3146 : PRIntervalTime(PR_TicksPerSecond() * t_secs
);
3149 JS_YieldRequest(cx
);
3151 JSAutoSuspendRequest
suspended(cx
);
3152 PR_Lock(gWatchdogLock
);
3153 PRIntervalTime to_wakeup
= PR_IntervalNow() + t_ticks
;
3155 PR_WaitCondVar(gSleepWakeup
, t_ticks
);
3158 PRIntervalTime now
= PR_IntervalNow();
3159 if (!IsBefore(now
, to_wakeup
))
3161 t_ticks
= to_wakeup
- now
;
3163 PR_Unlock(gWatchdogLock
);
3168 typedef struct ScatterThreadData ScatterThreadData
;
3169 typedef struct ScatterData ScatterData
;
3171 typedef enum ScatterStatus
{
3177 struct ScatterData
{
3178 ScatterThreadData
*threads
;
3182 ScatterStatus status
;
3185 struct ScatterThreadData
{
3187 ScatterData
*shared
;
3194 DoScatteredWork(JSContext
*cx
, ScatterThreadData
*td
)
3196 jsval
*rval
= &td
->shared
->results
[td
->index
];
3198 if (!JS_CallFunctionValue(cx
, NULL
, td
->fn
, 0, NULL
, rval
)) {
3200 JS_GetPendingException(cx
, rval
);
3201 JS_ClearPendingException(cx
);
3206 RunScatterThread(void *arg
)
3209 ScatterThreadData
*td
;
3213 if (PR_FAILURE
== PR_SetThreadPrivate(gStackBaseThreadIndex
, &stackDummy
))
3216 td
= (ScatterThreadData
*)arg
;
3219 /* Wait for our signal. */
3220 PR_Lock(td
->shared
->lock
);
3221 while ((st
= td
->shared
->status
) == SCATTER_WAIT
)
3222 PR_WaitCondVar(td
->shared
->cvar
, PR_INTERVAL_NO_TIMEOUT
);
3223 PR_Unlock(td
->shared
->lock
);
3225 if (st
== SCATTER_CANCEL
)
3228 /* We are good to go. */
3229 JS_SetContextThread(cx
);
3230 SetThreadStackLimit(cx
);
3231 JS_BeginRequest(cx
);
3232 DoScatteredWork(cx
, td
);
3234 JS_ClearContextThread(cx
);
3238 * scatter(fnArray) - Call each function in `fnArray` without arguments, each
3239 * in a different thread. When all threads have finished, return an array: the
3240 * return values. Errors are not propagated; if any of the function calls
3241 * fails, the corresponding element in the results array gets the exception
3242 * object, if any, else (undefined).
3245 Scatter(JSContext
*cx
, uintN argc
, jsval
*vp
)
3248 jsuint n
; /* number of threads */
3259 sd
.status
= SCATTER_WAIT
;
3261 if (argc
== 0 || JSVAL_IS_PRIMITIVE(JS_ARGV(cx
, vp
)[0])) {
3262 JS_ReportError(cx
, "the first argument must be an object");
3266 inArr
= JSVAL_TO_OBJECT(JS_ARGV(cx
, vp
)[0]);
3267 ok
= JS_GetArrayLength(cx
, inArr
, &n
);
3273 sd
.lock
= PR_NewLock();
3277 sd
.cvar
= PR_NewCondVar(sd
.lock
);
3281 sd
.results
= (jsval
*) malloc(n
* sizeof(jsval
));
3284 for (i
= 0; i
< n
; i
++) {
3285 sd
.results
[i
] = JSVAL_VOID
;
3286 ok
= JS_AddRoot(cx
, &sd
.results
[i
]);
3289 JS_RemoveRoot(cx
, &sd
.results
[i
]);
3296 sd
.threads
= (ScatterThreadData
*) malloc(n
* sizeof(ScatterThreadData
));
3299 for (i
= 0; i
< n
; i
++) {
3300 sd
.threads
[i
].index
= i
;
3301 sd
.threads
[i
].shared
= &sd
;
3302 sd
.threads
[i
].thr
= NULL
;
3303 sd
.threads
[i
].cx
= NULL
;
3304 sd
.threads
[i
].fn
= JSVAL_NULL
;
3306 ok
= JS_AddRoot(cx
, &sd
.threads
[i
].fn
);
3307 if (ok
&& !JS_GetElement(cx
, inArr
, (jsint
) i
, &sd
.threads
[i
].fn
)) {
3308 JS_RemoveRoot(cx
, &sd
.threads
[i
].fn
);
3313 JS_RemoveRoot(cx
, &sd
.threads
[i
].fn
);
3320 global
= JS_GetGlobalObject(cx
);
3321 for (i
= 1; i
< n
; i
++) {
3322 JSContext
*newcx
= NewContext(JS_GetRuntime(cx
));
3327 JSAutoTransferRequest
transfer(cx
, newcx
);
3328 JS_SetGlobalObject(newcx
, global
);
3330 JS_ClearContextThread(newcx
);
3331 sd
.threads
[i
].cx
= newcx
;
3334 for (i
= 1; i
< n
; i
++) {
3335 PRThread
*t
= PR_CreateThread(PR_USER_THREAD
,
3343 /* Failed to start thread. */
3345 sd
.status
= SCATTER_CANCEL
;
3346 PR_NotifyAllCondVar(sd
.cvar
);
3349 PR_JoinThread(sd
.threads
[i
].thr
);
3353 sd
.threads
[i
].thr
= t
;
3356 sd
.status
= SCATTER_GO
;
3357 PR_NotifyAllCondVar(sd
.cvar
);
3360 DoScatteredWork(cx
, &sd
.threads
[0]);
3363 JSAutoSuspendRequest
suspended(cx
);
3364 for (i
= 1; i
< n
; i
++) {
3365 PR_JoinThread(sd
.threads
[i
].thr
);
3370 arr
= JS_NewArrayObject(cx
, n
, sd
.results
);
3373 *vp
= OBJECT_TO_JSVAL(arr
);
3380 for (i
= 0; i
< n
; i
++) {
3381 JS_RemoveRoot(cx
, &sd
.threads
[i
].fn
);
3382 acx
= sd
.threads
[i
].cx
;
3384 JS_SetContextThread(acx
);
3385 DestroyContext(acx
, true);
3391 for (i
= 0; i
< n
; i
++)
3392 JS_RemoveRoot(cx
, &sd
.results
[i
]);
3396 PR_DestroyCondVar(sd
.cvar
);
3398 PR_DestroyLock(sd
.lock
);
3408 InitWatchdog(JSRuntime
*rt
)
3410 JS_ASSERT(!gWatchdogThread
);
3411 gWatchdogLock
= PR_NewLock();
3412 if (gWatchdogLock
) {
3413 gWatchdogWakeup
= PR_NewCondVar(gWatchdogLock
);
3414 if (gWatchdogWakeup
) {
3415 gSleepWakeup
= PR_NewCondVar(gWatchdogLock
);
3418 PR_DestroyCondVar(gWatchdogWakeup
);
3420 PR_DestroyLock(gWatchdogLock
);
3430 PR_Lock(gWatchdogLock
);
3431 thread
= gWatchdogThread
;
3434 * The watchdog thread is running, tell it to terminate waking it up
3437 gWatchdogThread
= NULL
;
3438 PR_NotifyCondVar(gWatchdogWakeup
);
3440 PR_Unlock(gWatchdogLock
);
3442 PR_JoinThread(thread
);
3443 PR_DestroyCondVar(gSleepWakeup
);
3444 PR_DestroyCondVar(gWatchdogWakeup
);
3445 PR_DestroyLock(gWatchdogLock
);
3449 WatchdogMain(void *arg
)
3451 JSRuntime
*rt
= (JSRuntime
*) arg
;
3453 PR_Lock(gWatchdogLock
);
3454 while (gWatchdogThread
) {
3455 PRIntervalTime now
= PR_IntervalNow();
3456 if (gWatchdogHasTimeout
&& !IsBefore(now
, gWatchdogTimeout
)) {
3458 * The timeout has just expired. Trigger the operation callback
3461 gWatchdogHasTimeout
= false;
3462 PR_Unlock(gWatchdogLock
);
3463 CancelExecution(rt
);
3464 PR_Lock(gWatchdogLock
);
3466 /* Wake up any threads doing sleep. */
3467 PR_NotifyAllCondVar(gSleepWakeup
);
3469 PRIntervalTime sleepDuration
= gWatchdogHasTimeout
3470 ? gWatchdogTimeout
- now
3471 : PR_INTERVAL_NO_TIMEOUT
;
3475 PR_WaitCondVar(gWatchdogWakeup
, sleepDuration
);
3476 JS_ASSERT(status
== PR_SUCCESS
);
3479 PR_Unlock(gWatchdogLock
);
3483 ScheduleWatchdog(JSRuntime
*rt
, jsdouble t
)
3486 PR_Lock(gWatchdogLock
);
3487 gWatchdogHasTimeout
= false;
3488 PR_Unlock(gWatchdogLock
);
3492 PRIntervalTime interval
= PRIntervalTime(ceil(t
* PR_TicksPerSecond()));
3493 PRIntervalTime timeout
= PR_IntervalNow() + interval
;
3494 PR_Lock(gWatchdogLock
);
3495 if (!gWatchdogThread
) {
3496 JS_ASSERT(!gWatchdogHasTimeout
);
3497 gWatchdogThread
= PR_CreateThread(PR_USER_THREAD
,
3504 if (!gWatchdogThread
) {
3505 PR_Unlock(gWatchdogLock
);
3508 } else if (!gWatchdogHasTimeout
|| IsBefore(timeout
, gWatchdogTimeout
)) {
3509 PR_NotifyCondVar(gWatchdogWakeup
);
3511 gWatchdogHasTimeout
= true;
3512 gWatchdogTimeout
= timeout
;
3513 PR_Unlock(gWatchdogLock
);
3517 #else /* !JS_THREADSAFE */
3520 static HANDLE gTimerHandle
= 0;
3523 TimerCallback(PVOID lpParameter
, BOOLEAN TimerOrWaitFired
)
3525 CancelExecution((JSRuntime
*) lpParameter
);
3531 AlarmHandler(int sig
)
3533 CancelExecution(gRuntime
);
3539 InitWatchdog(JSRuntime
*rt
)
3548 ScheduleWatchdog(gRuntime
, -1);
3552 ScheduleWatchdog(JSRuntime
*rt
, jsdouble t
)
3556 DeleteTimerQueueTimer(NULL
, gTimerHandle
, NULL
);
3560 !CreateTimerQueueTimer(&gTimerHandle
,
3562 (WAITORTIMERCALLBACK
)TimerCallback
,
3564 DWORD(ceil(t
* 1000.0)),
3566 WT_EXECUTEINTIMERTHREAD
| WT_EXECUTEONLYONCE
)) {
3571 /* FIXME: use setitimer when available for sub-second resolution. */
3574 signal(SIGALRM
, NULL
);
3576 signal(SIGALRM
, AlarmHandler
); /* set the Alarm signal capture */
3583 #endif /* !JS_THREADSAFE */
3586 CancelExecution(JSRuntime
*rt
)
3590 gExitCode
= EXITCODE_TIMEOUT
;
3591 #ifdef JS_THREADSAFE
3593 JSContext
*cx
= JS_NewContext(rt
, 8192);
3595 js::workers::terminateAll(cx
, gWorkers
);
3596 JS_DestroyContextNoGC(cx
);
3599 JS_TriggerAllOperationCallbacks(rt
);
3601 static const char msg
[] = "Script runs for too long, terminating.\n";
3602 #if defined(XP_UNIX) && !defined(JS_THREADSAFE)
3603 /* It is not safe to call fputs from signals. */
3604 /* Dummy assignment avoids GCC warning on "attribute warn_unused_result" */
3605 ssize_t dummy
= write(2, msg
, sizeof(msg
) - 1);
3613 SetTimeoutValue(JSContext
*cx
, jsdouble t
)
3615 /* NB: The next condition also filter out NaNs. */
3616 if (!(t
<= MAX_TIMEOUT_INTERVAL
)) {
3617 JS_ReportError(cx
, "Excessive timeout value");
3620 gTimeoutInterval
= t
;
3621 if (!ScheduleWatchdog(cx
->runtime
, t
)) {
3622 JS_ReportError(cx
, "Failed to create the watchdog");
3629 Timeout(JSContext
*cx
, uintN argc
, jsval
*vp
)
3632 return JS_NewNumberValue(cx
, gTimeoutInterval
, vp
);
3635 JS_ReportError(cx
, "Wrong number of arguments");
3640 if (!JS_ValueToNumber(cx
, JS_ARGV(cx
, vp
)[0], &t
))
3644 return SetTimeoutValue(cx
, t
);
3648 Elapsed(JSContext
*cx
, uintN argc
, jsval
*vp
)
3652 JSShellContextData
*data
= GetContextData(cx
);
3654 d
= js_IntervalNow() - data
->startTime
;
3655 return JS_NewNumberValue(cx
, d
, vp
);
3657 JS_ReportError(cx
, "Wrong number of arguments");
3662 Parent(JSContext
*cx
, uintN argc
, jsval
*vp
)
3665 JS_ReportError(cx
, "Wrong number of arguments");
3669 jsval v
= JS_ARGV(cx
, vp
)[0];
3670 if (JSVAL_IS_PRIMITIVE(v
)) {
3671 JS_ReportError(cx
, "Only objects have parents!");
3675 JSObject
*parent
= JS_GetParent(cx
, JSVAL_TO_OBJECT(v
));
3676 *vp
= OBJECT_TO_JSVAL(parent
);
3678 /* Outerize if necessary. Embrace the ugliness! */
3679 JSClass
*clasp
= JS_GET_CLASS(cx
, parent
);
3680 if (clasp
->flags
& JSCLASS_IS_EXTENDED
) {
3681 JSExtendedClass
*xclasp
= reinterpret_cast<JSExtendedClass
*>(clasp
);
3682 if (JSObjectOp outerize
= xclasp
->outerObject
)
3683 *vp
= OBJECT_TO_JSVAL(outerize(cx
, parent
));
3692 #include <sys/stat.h>
3695 * Returns a JS_malloc'd string (that the caller needs to JS_free)
3696 * containing the directory (non-leaf) part of |from| prepended to |leaf|.
3697 * If |from| is empty or a leaf, MakeAbsolutePathname returns a copy of leaf.
3698 * Returns NULL to indicate an error.
3701 MakeAbsolutePathname(JSContext
*cx
, const char *from
, const char *leaf
)
3705 const char *slash
= NULL
, *cp
;
3717 /* We were given a leaf or |from| was empty. */
3718 return JS_strdup(cx
, leaf
);
3721 /* Else, we were given a real pathname, return that + the leaf. */
3722 dirlen
= slash
- from
+ 1;
3723 dir
= (char*) JS_malloc(cx
, dirlen
+ strlen(leaf
) + 1);
3727 strncpy(dir
, from
, dirlen
);
3728 strcpy(dir
+ dirlen
, leaf
); /* Note: we can't use strcat here. */
3736 Compile(JSContext
*cx
, uintN argc
, jsval
*vp
)
3739 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_MORE_ARGS_NEEDED
,
3740 "compile", "0", "s");
3743 jsval arg0
= JS_ARGV(cx
, vp
)[0];
3744 if (!JSVAL_IS_STRING(arg0
)) {
3745 const char *typeName
= JS_GetTypeName(cx
, JS_TypeOfValue(cx
, arg0
));
3746 JS_ReportError(cx
, "expected string to compile, got %s", typeName
);
3750 JSString
*scriptContents
= JSVAL_TO_STRING(arg0
);
3751 JSScript
*result
= JS_CompileUCScript(cx
, NULL
, JS_GetStringCharsZ(cx
, scriptContents
),
3752 JS_GetStringLength(scriptContents
), "<string>", 0);
3756 JS_DestroyScript(cx
, result
);
3757 JS_SET_RVAL(cx
, vp
, JSVAL_VOID
);
3762 Parse(JSContext
*cx
, uintN argc
, jsval
*vp
)
3765 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_MORE_ARGS_NEEDED
,
3766 "compile", "0", "s");
3769 jsval arg0
= JS_ARGV(cx
, vp
)[0];
3770 if (!JSVAL_IS_STRING(arg0
)) {
3771 const char *typeName
= JS_GetTypeName(cx
, JS_TypeOfValue(cx
, arg0
));
3772 JS_ReportError(cx
, "expected string to parse, got %s", typeName
);
3776 JSString
*scriptContents
= JSVAL_TO_STRING(arg0
);
3777 js::Parser
parser(cx
);
3778 parser
.init(JS_GetStringCharsZ(cx
, scriptContents
), JS_GetStringLength(scriptContents
),
3779 NULL
, "<string>", 0);
3780 if (!parser
.parse(NULL
))
3782 JS_SET_RVAL(cx
, vp
, JSVAL_VOID
);
3787 Snarf(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
3790 const char *filename
;
3791 const char *pathname
;
3798 str
= JS_ValueToString(cx
, argv
[0]);
3801 filename
= JS_GetStringBytes(str
);
3803 /* Get the currently executing script's name. */
3804 fp
= JS_GetScriptedCaller(cx
, NULL
);
3805 JS_ASSERT(fp
&& fp
->script
->filename
);
3807 pathname
= MakeAbsolutePathname(cx
, fp
->script
->filename
, filename
);
3811 pathname
= filename
;
3817 file
= fopen(pathname
, "rb");
3819 JS_ReportError(cx
, "can't open %s: %s", pathname
, strerror(errno
));
3821 if (fseek(file
, 0, SEEK_END
) == EOF
) {
3822 JS_ReportError(cx
, "can't seek end of %s", pathname
);
3825 if (fseek(file
, 0, SEEK_SET
) == EOF
) {
3826 JS_ReportError(cx
, "can't seek start of %s", pathname
);
3828 buf
= (char*) JS_malloc(cx
, len
+ 1);
3830 cc
= fread(buf
, 1, len
, file
);
3832 JS_ReportError(cx
, "can't read %s: %s", pathname
,
3833 (ptrdiff_t(cc
) < 0) ? strerror(errno
) : "short read");
3843 JS_free(cx
, (void*)pathname
);
3850 str
= JS_NewString(cx
, buf
, len
);
3855 *rval
= STRING_TO_JSVAL(str
);
3859 /* We use a mix of JS_FS and JS_FN to test both kinds of natives. */
3860 static JSFunctionSpec shell_functions
[] = {
3861 JS_FS("version", Version
, 0,0,0),
3862 JS_FS("options", Options
, 0,0,0),
3863 JS_FS("load", Load
, 1,0,0),
3864 JS_FN("readline", ReadLine
, 0,0),
3865 JS_FN("print", Print
, 0,0),
3866 JS_FS("help", Help
, 0,0,0),
3867 JS_FS("quit", Quit
, 0,0,0),
3868 JS_FN("assertEq", AssertEq
, 2,0),
3869 JS_FN("gc", ::GC
, 0,0),
3871 JS_FN("gcstats", GCStats
, 0,0),
3873 JS_FN("gcparam", GCParameter
, 2,0),
3874 JS_FN("countHeap", CountHeap
, 0,0),
3876 JS_FN("gczeal", GCZeal
, 1,0),
3878 JS_FS("trap", Trap
, 3,0,0),
3879 JS_FS("untrap", Untrap
, 2,0,0),
3880 JS_FS("line2pc", LineToPC
, 0,0,0),
3881 JS_FS("pc2line", PCToLine
, 0,0,0),
3882 JS_FN("stackQuota", StackQuota
, 0,0),
3883 JS_FS("stringsAreUTF8", StringsAreUTF8
, 0,0,0),
3884 JS_FS("testUTF8", TestUTF8
, 1,0,0),
3885 JS_FS("throwError", ThrowError
, 0,0,0),
3887 JS_FS("dis", Disassemble
, 1,0,0),
3888 JS_FS("disfile", DisassFile
, 1,0,0),
3889 JS_FS("dissrc", DisassWithSrc
, 1,0,0),
3890 JS_FN("dumpHeap", DumpHeap
, 0,0),
3891 JS_FS("notes", Notes
, 1,0,0),
3892 JS_FS("tracing", Tracing
, 0,0,0),
3893 JS_FS("stats", DumpStats
, 1,0,0),
3896 JS_FS("cvtargs", ConvertArgs
, 0,0,12),
3898 JS_FN("build", BuildDate
, 0,0),
3899 JS_FS("clear", Clear
, 0,0,0),
3900 JS_FN("intern", Intern
, 1,0),
3901 JS_FS("clone", Clone
, 1,0,0),
3902 JS_FS("seal", Seal
, 1,0,1),
3903 JS_FN("getpda", GetPDA
, 1,0),
3904 JS_FN("getslx", GetSLX
, 1,0),
3905 JS_FN("toint32", ToInt32
, 1,0),
3906 JS_FS("evalcx", EvalInContext
, 1,0,0),
3907 JS_FN("evalInFrame", EvalInFrame
, 2,0),
3908 JS_FN("shapeOf", ShapeOf
, 1,0),
3910 JS_FS("startShark", js_StartShark
, 0,0,0),
3911 JS_FS("stopShark", js_StopShark
, 0,0,0),
3912 JS_FS("connectShark", js_ConnectShark
, 0,0,0),
3913 JS_FS("disconnectShark",js_DisconnectShark
, 0,0,0),
3915 #ifdef MOZ_CALLGRIND
3916 JS_FS("startCallgrind", js_StartCallgrind
, 0,0,0),
3917 JS_FS("stopCallgrind", js_StopCallgrind
, 0,0,0),
3918 JS_FS("dumpCallgrind", js_DumpCallgrind
, 1,0,0),
3921 JS_FS("startVtune", js_StartVtune
, 1,0,0),
3922 JS_FS("stopVtune", js_StopVtune
, 0,0,0),
3923 JS_FS("pauseVtune", js_PauseVtune
, 0,0,0),
3924 JS_FS("resumeVtune", js_ResumeVtune
, 0,0,0),
3927 JS_FS("startTraceVis", StartTraceVisNative
, 1,0,0),
3928 JS_FS("stopTraceVis", StopTraceVisNative
, 0,0,0),
3931 JS_FS("arrayInfo", js_ArrayInfo
, 1,0,0),
3933 #ifdef JS_THREADSAFE
3934 JS_FN("sleep", Sleep_fn
, 1,0),
3935 JS_FN("scatter", Scatter
, 1,0),
3937 JS_FS("snarf", Snarf
, 0,0,0),
3938 JS_FN("compile", Compile
, 1,0),
3939 JS_FN("parse", Parse
, 1,0),
3940 JS_FN("timeout", Timeout
, 1,0),
3941 JS_FN("elapsed", Elapsed
, 0,0),
3942 JS_FN("parent", Parent
, 1,0),
3946 static const char shell_help_header
[] =
3947 "Command Description\n"
3948 "======= ===========\n";
3950 static const char *const shell_help_messages
[] = {
3951 "version([number]) Get or set JavaScript version number",
3952 "options([option ...]) Get or toggle JavaScript options",
3953 "load(['foo.js' ...]) Load files named by string arguments",
3954 "readline() Read a single line from stdin",
3955 "print([exp ...]) Evaluate and print expressions",
3956 "help([name ...]) Display usage and help messages",
3957 "quit() Quit the shell",
3958 "assertEq(actual, expected[, msg])\n"
3959 " Throw if the first two arguments are not the same (both +0 or both -0,\n"
3960 " both NaN, or non-zero and ===)",
3961 "gc() Run the garbage collector",
3963 "gcstats() Print garbage collector statistics",
3965 "gcparam(name, value)\n"
3966 " Wrapper for JS_SetGCParameter. The name must be either 'maxBytes' or\n"
3967 " 'maxMallocBytes' and the value must be convertable to a positive uint32",
3968 "countHeap([start[, kind]])\n"
3969 " Count the number of live GC things in the heap or things reachable from\n"
3970 " start when it is given and is not null. kind is either 'all' (default) to\n"
3971 " count all things or one of 'object', 'double', 'string', 'function',\n"
3972 " 'qname', 'namespace', 'xml' to count only things of that kind",
3974 "gczeal(level) How zealous the garbage collector should be",
3976 "trap([fun, [pc,]] exp) Trap bytecode execution",
3977 "untrap(fun[, pc]) Remove a trap",
3978 "line2pc([fun,] line) Map line number to PC",
3979 "pc2line(fun[, pc]) Map PC to line number",
3980 "stackQuota([number]) Query/set script stack quota",
3981 "stringsAreUTF8() Check if strings are UTF-8 encoded",
3982 "testUTF8(mode) Perform UTF-8 tests (modes are 1 to 4)",
3983 "throwError() Throw an error from JS_ReportError",
3985 "dis([fun]) Disassemble functions into bytecodes",
3986 "disfile('foo.js') Disassemble script file into bytecodes",
3987 "dissrc([fun]) Disassemble functions with source lines",
3988 "dumpHeap([fileName[, start[, toFind[, maxDepth[, toIgnore]]]]])\n"
3989 " Interface to JS_DumpHeap with output sent to file",
3990 "notes([fun]) Show source notes for functions",
3991 "tracing([true|false|filename]) Turn bytecode execution tracing on/off.\n"
3992 " With filename, send to file.\n",
3993 "stats([string ...]) Dump 'arena', 'atom', 'global' stats",
3996 "cvtargs(arg1..., arg12) Test argument formatter",
3998 "build() Show build date and time",
3999 "clear([obj]) Clear properties of object",
4000 "intern(str) Internalize str in the atom table",
4001 "clone(fun[, scope]) Clone function object",
4002 "seal(obj[, deep]) Seal object, or object graph if deep",
4003 "getpda(obj) Get the property descriptors for obj",
4004 "getslx(obj) Get script line extent",
4005 "toint32(n) Testing hook for JS_ValueToInt32",
4007 " Evaluate s in optional sandbox object o\n"
4008 " if (s == '' && !o) return new o with eager standard classes\n"
4009 " if (s == 'lazy' && !o) return new o with lazy standard classes\n"
4010 " if (s == 'split' && !o) return new split-object o with lazy standard classes",
4011 "evalInFrame(n,str,save) Evaluate 'str' in the nth up frame.\n"
4012 " If 'save' (default false), save the frame chain",
4013 "shapeOf(obj) Get the shape of obj (an implementation detail)",
4015 "startShark() Start a Shark session.\n"
4016 " Shark must be running with programatic sampling",
4017 "stopShark() Stop a running Shark session",
4018 "connectShark() Connect to Shark.\n"
4019 " The -k switch does this automatically",
4020 "disconnectShark() Disconnect from Shark",
4022 #ifdef MOZ_CALLGRIND
4023 "startCallgrind() Start callgrind instrumentation",
4024 "stopCallgrind() Stop callgrind instrumentation",
4025 "dumpCallgrind([name]) Dump callgrind counters",
4028 "startVtune([filename]) Start vtune instrumentation",
4029 "stopVtune() Stop vtune instrumentation",
4030 "pauseVtune() Pause vtune collection",
4031 "resumeVtune() Resume vtune collection",
4034 "startTraceVis(filename) Start TraceVis recording (stops any current recording)",
4035 "stopTraceVis() Stop TraceVis recording",
4038 "arrayInfo(a1, a2, ...) Report statistics about arrays",
4040 #ifdef JS_THREADSAFE
4041 "sleep(dt) Sleep for dt seconds",
4042 "scatter(fns) Call functions concurrently (ignoring errors)",
4044 "snarf(filename) Read filename into returned string",
4045 "compile(code) Compiles a string to bytecode, potentially throwing",
4046 "parse(code) Parses a string, potentially throwing",
4047 "timeout([seconds])\n"
4048 " Get/Set the limit in seconds for the execution time for the current context.\n"
4049 " A negative value (default) means that the execution time is unlimited.",
4050 "elapsed() Execution time elapsed for the current context.",
4051 "parent(obj) Returns the parent of obj.\n",
4054 /* Help messages must match shell functions. */
4055 JS_STATIC_ASSERT(JS_ARRAY_LENGTH(shell_help_messages
) + 1 ==
4056 JS_ARRAY_LENGTH(shell_functions
));
4062 const char *const *m
;
4065 /* Each message must begin with "function_name(" prefix. */
4066 for (m
= shell_help_messages
; m
!= JS_ARRAY_END(shell_help_messages
); ++m
) {
4067 lp
= strchr(*m
, '(');
4069 JS_ASSERT(memcmp(shell_functions
[m
- shell_help_messages
].name
,
4074 # define CheckHelpMessages() ((void) 0)
4078 Help(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
4081 int did_header
, did_something
;
4087 fprintf(gOutFile
, "%s\n", JS_GetImplementationVersion());
4089 fputs(shell_help_header
, gOutFile
);
4090 for (i
= 0; shell_functions
[i
].name
; i
++)
4091 fprintf(gOutFile
, "%s\n", shell_help_messages
[i
]);
4094 for (i
= 0; i
< argc
; i
++) {
4096 type
= JS_TypeOfValue(cx
, argv
[i
]);
4097 if (type
== JSTYPE_FUNCTION
) {
4098 fun
= JS_ValueToFunction(cx
, argv
[i
]);
4099 str
= fun
->atom
? ATOM_TO_STRING(fun
->atom
) : NULL
;
4100 } else if (type
== JSTYPE_STRING
) {
4101 str
= JSVAL_TO_STRING(argv
[i
]);
4106 bytes
= JS_GetStringBytes(str
);
4107 for (j
= 0; shell_functions
[j
].name
; j
++) {
4108 if (!strcmp(bytes
, shell_functions
[j
].name
)) {
4111 fputs(shell_help_header
, gOutFile
);
4114 fprintf(gOutFile
, "%s\n", shell_help_messages
[j
]);
4119 if (!did_something
) {
4120 str
= JS_ValueToString(cx
, argv
[i
]);
4123 fprintf(gErrFile
, "Sorry, no help for %s\n",
4124 JS_GetStringBytes(str
));
4132 split_setup(JSContext
*cx
, JSBool evalcx
)
4134 JSObject
*outer
, *inner
, *arguments
;
4136 outer
= split_create_outer(cx
);
4139 JS_SetGlobalObject(cx
, outer
);
4141 inner
= split_create_inner(cx
, outer
);
4146 if (!JS_DefineFunctions(cx
, inner
, shell_functions
))
4149 /* Create a dummy arguments object. */
4150 arguments
= JS_NewArrayObject(cx
, 0, NULL
);
4152 !JS_DefineProperty(cx
, inner
, "arguments", OBJECT_TO_JSVAL(arguments
),
4158 JS_ClearScope(cx
, outer
);
4160 #ifndef LAZY_STANDARD_CLASSES
4161 if (!JS_InitStandardClasses(cx
, inner
))
4169 * Define a JS object called "it". Give it class operations that printf why
4170 * they're being called for tutorial purposes.
4173 ITS_COLOR
, ITS_HEIGHT
, ITS_WIDTH
, ITS_FUNNY
, ITS_ARRAY
, ITS_RDONLY
,
4174 ITS_CUSTOM
, ITS_CUSTOMRDONLY
4178 its_getter(JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
4180 jsval
*val
= (jsval
*) JS_GetPrivate(cx
, obj
);
4181 *vp
= val
? *val
: JSVAL_VOID
;
4186 its_setter(JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
4188 jsval
*val
= (jsval
*) JS_GetPrivate(cx
, obj
);
4196 JS_ReportOutOfMemory(cx
);
4200 if (!JS_AddRoot(cx
, val
)) {
4205 if (!JS_SetPrivate(cx
, obj
, (void*)val
)) {
4206 JS_RemoveRoot(cx
, val
);
4215 static JSPropertySpec its_props
[] = {
4216 {"color", ITS_COLOR
, JSPROP_ENUMERATE
, NULL
, NULL
},
4217 {"height", ITS_HEIGHT
, JSPROP_ENUMERATE
, NULL
, NULL
},
4218 {"width", ITS_WIDTH
, JSPROP_ENUMERATE
, NULL
, NULL
},
4219 {"funny", ITS_FUNNY
, JSPROP_ENUMERATE
, NULL
, NULL
},
4220 {"array", ITS_ARRAY
, JSPROP_ENUMERATE
, NULL
, NULL
},
4221 {"rdonly", ITS_RDONLY
, JSPROP_READONLY
, NULL
, NULL
},
4222 {"custom", ITS_CUSTOM
, JSPROP_ENUMERATE
,
4223 its_getter
, its_setter
},
4224 {"customRdOnly", ITS_CUSTOMRDONLY
, JSPROP_ENUMERATE
| JSPROP_READONLY
,
4225 its_getter
, its_setter
},
4226 {NULL
,0,0,NULL
,NULL
}
4230 its_bindMethod(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
,
4236 if (!JS_ConvertArguments(cx
, argc
, argv
, "so", &name
, &method
))
4239 *rval
= OBJECT_TO_JSVAL(method
);
4241 if (JS_TypeOfValue(cx
, *rval
) != JSTYPE_FUNCTION
) {
4242 JSString
*valstr
= JS_ValueToString(cx
, *rval
);
4244 JS_ReportError(cx
, "can't bind method %s to non-callable object %s",
4245 name
, JS_GetStringBytes(valstr
));
4250 if (!JS_DefineProperty(cx
, obj
, name
, *rval
, NULL
, NULL
, JSPROP_ENUMERATE
))
4253 return JS_SetParent(cx
, method
, obj
);
4256 static JSFunctionSpec its_methods
[] = {
4257 {"bindMethod", its_bindMethod
, 2,0,0},
4261 #ifdef JSD_LOWLEVEL_SOURCE
4263 * This facilitates sending source to JSD (the debugger system) in the shell
4264 * where the source is loaded using the JSFILE hack in jsscan. The function
4265 * below is used as a callback for the jsdbgapi JS_SetSourceHandler hook.
4266 * A more normal embedding (e.g. mozilla) loads source itself and can send
4267 * source directly to JSD without using this hook scheme.
4270 SendSourceToJSDebugger(const char *filename
, uintN lineno
,
4271 jschar
*str
, size_t length
,
4272 void **listenerTSData
, JSDContext
* jsdc
)
4274 JSDSourceText
*jsdsrc
= (JSDSourceText
*) *listenerTSData
;
4278 filename
= "typein";
4280 jsdsrc
= JSD_NewSourceText(jsdc
, filename
);
4282 jsdsrc
= JSD_FindSourceForURL(jsdc
, filename
);
4283 if (jsdsrc
&& JSD_SOURCE_PARTIAL
!=
4284 JSD_GetSourceStatus(jsdc
, jsdsrc
)) {
4290 jsdsrc
= JSD_AppendUCSourceText(jsdc
,jsdsrc
, str
, length
,
4291 JSD_SOURCE_PARTIAL
);
4293 *listenerTSData
= jsdsrc
;
4295 #endif /* JSD_LOWLEVEL_SOURCE */
4297 static JSBool its_noisy
; /* whether to be noisy when finalizing it */
4298 static JSBool its_enum_fail
;/* whether to fail when enumerating it */
4301 its_addProperty(JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
4306 ToString
idString(cx
, id
);
4307 fprintf(gOutFile
, "adding its property %s,", idString
.getBytes());
4308 ToString
valueString(cx
, *vp
);
4309 fprintf(gOutFile
, " initial value %s\n", valueString
.getBytes());
4314 its_delProperty(JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
4319 ToString
idString(cx
, id
);
4320 fprintf(gOutFile
, "deleting its property %s,", idString
.getBytes());
4321 ToString
valueString(cx
, *vp
);
4322 fprintf(gOutFile
, " initial value %s\n", valueString
.getBytes());
4327 its_getProperty(JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
4332 ToString
idString(cx
, id
);
4333 fprintf(gOutFile
, "getting its property %s,", idString
.getBytes());
4334 ToString
valueString(cx
, *vp
);
4335 fprintf(gOutFile
, " initial value %s\n", valueString
.getBytes());
4340 its_setProperty(JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
4342 ToString
idString(cx
, id
);
4344 fprintf(gOutFile
, "setting its property %s,", idString
.getBytes());
4345 ToString
valueString(cx
, *vp
);
4346 fprintf(gOutFile
, " new value %s\n", valueString
.getBytes());
4349 if (!JSVAL_IS_STRING(id
))
4352 if (!strcmp(idString
.getBytes(), "noisy"))
4353 JS_ValueToBoolean(cx
, *vp
, &its_noisy
);
4354 else if (!strcmp(idString
.getBytes(), "enum_fail"))
4355 JS_ValueToBoolean(cx
, *vp
, &its_enum_fail
);
4361 * Its enumerator, implemented using the "new" enumerate API,
4365 its_enumerate(JSContext
*cx
, JSObject
*obj
, JSIterateOp enum_op
,
4366 jsval
*statep
, jsid
*idp
)
4371 case JSENUMERATE_INIT
:
4373 fprintf(gOutFile
, "enumerate its properties\n");
4375 iterator
= JS_NewPropertyIterator(cx
, obj
);
4379 *statep
= OBJECT_TO_JSVAL(iterator
);
4384 case JSENUMERATE_NEXT
:
4385 if (its_enum_fail
) {
4386 JS_ReportError(cx
, "its enumeration failed");
4390 iterator
= (JSObject
*) JSVAL_TO_OBJECT(*statep
);
4391 if (!JS_NextProperty(cx
, iterator
, idp
))
4394 if (!JSVAL_IS_VOID(*idp
))
4398 case JSENUMERATE_DESTROY
:
4399 /* Allow our iterator object to be GC'd. */
4400 *statep
= JSVAL_NULL
;
4408 its_resolve(JSContext
*cx
, JSObject
*obj
, jsval id
, uintN flags
,
4412 ToString
idString(cx
, id
);
4413 fprintf(gOutFile
, "resolving its property %s, flags {%s,%s,%s}\n",
4414 idString
.getBytes(),
4415 (flags
& JSRESOLVE_QUALIFIED
) ? "qualified" : "",
4416 (flags
& JSRESOLVE_ASSIGNING
) ? "assigning" : "",
4417 (flags
& JSRESOLVE_DETECTING
) ? "detecting" : "");
4423 its_convert(JSContext
*cx
, JSObject
*obj
, JSType type
, jsval
*vp
)
4426 fprintf(gOutFile
, "converting it to %s type\n", JS_GetTypeName(cx
, type
));
4431 its_finalize(JSContext
*cx
, JSObject
*obj
)
4435 fprintf(gOutFile
, "finalizing it\n");
4436 rootedVal
= (jsval
*) JS_GetPrivate(cx
, obj
);
4438 JS_RemoveRoot(cx
, rootedVal
);
4439 JS_SetPrivate(cx
, obj
, NULL
);
4444 static JSClass its_class
= {
4445 "It", JSCLASS_NEW_RESOLVE
| JSCLASS_NEW_ENUMERATE
| JSCLASS_HAS_PRIVATE
,
4446 its_addProperty
, its_delProperty
, its_getProperty
, its_setProperty
,
4447 (JSEnumerateOp
)its_enumerate
, (JSResolveOp
)its_resolve
,
4448 its_convert
, its_finalize
,
4449 JSCLASS_NO_OPTIONAL_MEMBERS
4452 JSErrorFormatString jsShell_ErrorFormatString
[JSErr_Limit
] = {
4453 #define MSG_DEF(name, number, count, exception, format) \
4454 { format, count, JSEXN_ERR } ,
4455 #include "jsshell.msg"
4459 static const JSErrorFormatString
*
4460 my_GetErrorMessage(void *userRef
, const char *locale
, const uintN errorNumber
)
4462 if ((errorNumber
> 0) && (errorNumber
< JSShellErr_Limit
))
4463 return &jsShell_ErrorFormatString
[errorNumber
];
4468 my_ErrorReporter(JSContext
*cx
, const char *message
, JSErrorReport
*report
)
4475 fprintf(gErrFile
, "%s\n", message
);
4479 /* Conditionally ignore reported warnings. */
4480 if (JSREPORT_IS_WARNING(report
->flags
) && !reportWarnings
)
4484 if (report
->filename
)
4485 prefix
= JS_smprintf("%s:", report
->filename
);
4486 if (report
->lineno
) {
4488 prefix
= JS_smprintf("%s%u: ", tmp
? tmp
: "", report
->lineno
);
4491 if (JSREPORT_IS_WARNING(report
->flags
)) {
4493 prefix
= JS_smprintf("%s%swarning: ",
4495 JSREPORT_IS_STRICT(report
->flags
) ? "strict " : "");
4499 /* embedded newlines -- argh! */
4500 while ((ctmp
= strchr(message
, '\n')) != 0) {
4503 fputs(prefix
, gErrFile
);
4504 fwrite(message
, 1, ctmp
- message
, gErrFile
);
4508 /* If there were no filename or lineno, the prefix might be empty */
4510 fputs(prefix
, gErrFile
);
4511 fputs(message
, gErrFile
);
4513 if (!report
->linebuf
) {
4514 fputc('\n', gErrFile
);
4518 /* report->linebuf usually ends with a newline. */
4519 n
= strlen(report
->linebuf
);
4520 fprintf(gErrFile
, ":\n%s%s%s%s",
4523 (n
> 0 && report
->linebuf
[n
-1] == '\n') ? "" : "\n",
4525 n
= report
->tokenptr
- report
->linebuf
;
4526 for (i
= j
= 0; i
< n
; i
++) {
4527 if (report
->linebuf
[i
] == '\t') {
4528 for (k
= (j
+ 8) & ~7; j
< k
; j
++) {
4529 fputc('.', gErrFile
);
4533 fputc('.', gErrFile
);
4536 fputs("^\n", gErrFile
);
4538 if (!JSREPORT_IS_WARNING(report
->flags
)) {
4539 if (report
->errorNumber
== JSMSG_OUT_OF_MEMORY
) {
4540 gExitCode
= EXITCODE_OUT_OF_MEMORY
;
4542 gExitCode
= EXITCODE_RUNTIME_ERROR
;
4545 JS_free(cx
, prefix
);
4548 #if defined(SHELL_HACK) && defined(DEBUG) && defined(XP_UNIX)
4550 Exec(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
4553 const char *name
, **nargv
;
4559 fun
= JS_ValueToFunction(cx
, argv
[-2]);
4564 name
= JS_GetStringBytes(ATOM_TO_STRING(fun
->atom
));
4566 nargv
= JS_malloc(cx
, (nargc
+ 1) * sizeof(char *));
4570 for (i
= 1; i
< nargc
; i
++) {
4571 str
= JS_ValueToString(cx
, argv
[i
-1]);
4576 nargv
[i
] = JS_GetStringBytes(str
);
4585 (void) execvp(name
, (char **)nargv
);
4589 while (waitpid(pid
, &status
, 0) < 0 && errno
== EINTR
)
4599 global_enumerate(JSContext
*cx
, JSObject
*obj
)
4601 #ifdef LAZY_STANDARD_CLASSES
4602 return JS_EnumerateStandardClasses(cx
, obj
);
4609 global_resolve(JSContext
*cx
, JSObject
*obj
, jsval id
, uintN flags
,
4612 #ifdef LAZY_STANDARD_CLASSES
4613 if ((flags
& JSRESOLVE_ASSIGNING
) == 0) {
4616 if (!JS_ResolveStandardClass(cx
, obj
, id
, &resolved
))
4625 #if defined(SHELL_HACK) && defined(DEBUG) && defined(XP_UNIX)
4626 if ((flags
& (JSRESOLVE_QUALIFIED
| JSRESOLVE_ASSIGNING
)) == 0) {
4628 * Do this expensive hack only for unoptimized Unix builds, which are
4629 * not used for benchmarking.
4631 char *path
, *comp
, *full
;
4636 if (!JSVAL_IS_STRING(id
))
4638 path
= getenv("PATH");
4641 path
= JS_strdup(cx
, path
);
4644 name
= JS_GetStringBytes(JSVAL_TO_STRING(id
));
4646 for (comp
= strtok(path
, ":"); comp
; comp
= strtok(NULL
, ":")) {
4647 if (*comp
!= '\0') {
4648 full
= JS_smprintf("%s/%s", comp
, name
);
4650 JS_ReportOutOfMemory(cx
);
4655 full
= (char *)name
;
4657 found
= (access(full
, X_OK
) == 0);
4661 fun
= JS_DefineFunction(cx
, obj
, name
, Exec
, 0,
4677 JSClass global_class
= {
4678 "global", JSCLASS_NEW_RESOLVE
| JSCLASS_GLOBAL_FLAGS
| JSCLASS_HAS_PRIVATE
,
4679 JS_PropertyStub
, JS_PropertyStub
,
4680 JS_PropertyStub
, JS_PropertyStub
,
4681 global_enumerate
, (JSResolveOp
) global_resolve
,
4682 JS_ConvertStub
, its_finalize
,
4683 JSCLASS_NO_OPTIONAL_MEMBERS
4687 env_setProperty(JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
4689 /* XXX porting may be easy, but these don't seem to supply setenv by default */
4690 #if !defined XP_BEOS && !defined XP_OS2 && !defined SOLARIS
4693 ToString
idstr(cx
, id
, JS_TRUE
);
4696 ToString
valstr(cx
, *vp
, JS_TRUE
);
4699 #if defined XP_WIN || defined HPUX || defined OSF1 || defined IRIX
4701 char *waste
= JS_smprintf("%s=%s", idstr
.getBytes(), valstr
.getBytes());
4703 JS_ReportOutOfMemory(cx
);
4709 * HPUX9 at least still has the bad old non-copying putenv.
4711 * Per mail from <s.shanmuganathan@digital.com>, OSF1 also has a putenv
4712 * that will crash if you pass it an auto char array (so it must place
4713 * its argument directly in the char *environ[] array).
4715 JS_smprintf_free(waste
);
4719 rv
= setenv(idstr
.getBytes(), valstr
.getBytes(), 1);
4722 JS_ReportError(cx
, "can't set env variable %s to %s", idstr
.getBytes(), valstr
.getBytes());
4725 *vp
= valstr
.getJSVal();
4726 #endif /* !defined XP_BEOS && !defined XP_OS2 && !defined SOLARIS */
4731 env_enumerate(JSContext
*cx
, JSObject
*obj
)
4733 static JSBool reflected
;
4734 char **evp
, *name
, *value
;
4741 for (evp
= (char **)JS_GetPrivate(cx
, obj
); (name
= *evp
) != NULL
; evp
++) {
4742 value
= strchr(name
, '=');
4746 valstr
= JS_NewStringCopyZ(cx
, value
);
4750 ok
= JS_DefineProperty(cx
, obj
, name
, STRING_TO_JSVAL(valstr
),
4751 NULL
, NULL
, JSPROP_ENUMERATE
);
4758 reflected
= JS_TRUE
;
4763 env_resolve(JSContext
*cx
, JSObject
*obj
, jsval id
, uintN flags
,
4767 const char *name
, *value
;
4769 if (flags
& JSRESOLVE_ASSIGNING
)
4772 ToString
idstr(cx
, id
, JS_TRUE
);
4776 name
= idstr
.getBytes();
4777 value
= getenv(name
);
4779 valstr
= JS_NewStringCopyZ(cx
, value
);
4782 if (!JS_DefineProperty(cx
, obj
, name
, STRING_TO_JSVAL(valstr
),
4783 NULL
, NULL
, JSPROP_ENUMERATE
)) {
4791 static JSClass env_class
= {
4792 "environment", JSCLASS_HAS_PRIVATE
| JSCLASS_NEW_RESOLVE
,
4793 JS_PropertyStub
, JS_PropertyStub
,
4794 JS_PropertyStub
, env_setProperty
,
4795 env_enumerate
, (JSResolveOp
) env_resolve
,
4796 JS_ConvertStub
, NULL
,
4797 JSCLASS_NO_OPTIONAL_MEMBERS
4803 defineProperty(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
,
4808 JSBool dontDelete
, readOnly
, dontEnum
;
4809 const jschar
*chars
;
4813 dontDelete
= readOnly
= dontEnum
= JS_FALSE
;
4814 if (!JS_ConvertArguments(cx
, argc
, argv
, "Sv/bbb",
4815 &str
, &value
, &dontDelete
, &readOnly
, &dontEnum
)) {
4818 chars
= JS_GetStringChars(str
);
4819 length
= JS_GetStringLength(str
);
4820 attrs
= dontEnum
? 0 : JSPROP_ENUMERATE
;
4822 attrs
|= JSPROP_PERMANENT
;
4824 attrs
|= JSPROP_READONLY
;
4825 return JS_DefineUCProperty(cx
, obj
, chars
, length
, value
, NULL
, NULL
,
4830 Evaluate(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
4832 /* function evaluate(source, filename, lineno) { ... } */
4834 const char *filename
= "";
4844 if (!JS_ConvertArguments(cx
, argc
, argv
, "S/su",
4845 &source
, &filename
, &lineno
)) {
4849 oldopts
= JS_GetOptions(cx
);
4850 JS_SetOptions(cx
, oldopts
| JSOPTION_COMPILE_N_GO
);
4851 ok
= JS_EvaluateUCScript(cx
, obj
, JS_GetStringChars(source
),
4852 JS_GetStringLength(source
), filename
,
4854 JS_SetOptions(cx
, oldopts
);
4859 #endif /* NARCISSUS */
4862 * Avoid a reentrancy hazard.
4864 * The non-JS_THREADSAFE shell uses a signal handler to implement timeout().
4865 * The JS engine is not really reentrant, but JS_TriggerAllOperationCallbacks
4866 * is mostly safe--the only danger is that we might interrupt JS_NewContext or
4867 * JS_DestroyContext while the context list is being modified. Therefore we
4868 * disable the signal handler around calls to those functions.
4870 #ifdef JS_THREADSAFE
4871 # define WITH_SIGNALS_DISABLED(x) x
4873 # define WITH_SIGNALS_DISABLED(x) \
4875 ScheduleWatchdog(gRuntime, -1); \
4877 ScheduleWatchdog(gRuntime, gTimeoutInterval); \
4882 NewContext(JSRuntime
*rt
)
4885 WITH_SIGNALS_DISABLED(cx
= JS_NewContext(rt
, gStackChunkSize
));
4889 JSShellContextData
*data
= NewContextData();
4891 DestroyContext(cx
, false);
4895 JS_SetContextPrivate(cx
, data
);
4896 JS_SetErrorReporter(cx
, my_ErrorReporter
);
4897 JS_SetVersion(cx
, JSVERSION_LATEST
);
4898 SetContextOptions(cx
);
4900 JS_ToggleOptions(cx
, JSOPTION_JIT
);
4905 DestroyContext(JSContext
*cx
, bool withGC
)
4907 JSShellContextData
*data
= GetContextData(cx
);
4908 JS_SetContextPrivate(cx
, NULL
);
4910 WITH_SIGNALS_DISABLED(withGC
? JS_DestroyContext(cx
) : JS_DestroyContextNoGC(cx
));
4914 NewGlobalObject(JSContext
*cx
)
4916 JSObject
*glob
= JS_NewObject(cx
, &global_class
, NULL
, NULL
);
4917 #ifdef LAZY_STANDARD_CLASSES
4918 JS_SetGlobalObject(cx
, glob
);
4920 if (!JS_InitStandardClasses(cx
, glob
))
4923 #ifdef JS_HAS_CTYPES
4924 if (!JS_InitCTypesClass(cx
, glob
))
4927 if (!JS_DefineFunctions(cx
, glob
, shell_functions
))
4930 JSObject
*it
= JS_DefineObject(cx
, glob
, "it", &its_class
, NULL
, 0);
4933 if (!JS_DefineProperties(cx
, it
, its_props
))
4935 if (!JS_DefineFunctions(cx
, it
, its_methods
))
4938 if (!JS_DefineProperty(cx
, glob
, "custom", JSVAL_VOID
, its_getter
,
4941 if (!JS_DefineProperty(cx
, glob
, "customRdOnly", JSVAL_VOID
, its_getter
,
4942 its_setter
, JSPROP_READONLY
))
4948 static const char Object_prototype
[] = "Object.prototype";
4950 if (!JS_DefineFunction(cx
, glob
, "evaluate", Evaluate
, 3, 0))
4953 if (!JS_EvaluateScript(cx
, glob
,
4954 Object_prototype
, sizeof Object_prototype
- 1,
4958 if (!JS_DefineFunction(cx
, JSVAL_TO_OBJECT(v
), "__defineProperty__",
4959 defineProperty
, 5, 0)) {
4969 main(int argc
, char **argv
, char **envp
)
4977 #ifdef JSDEBUGGER_JAVA_UI
4981 #ifdef JSDEBUGGER_C_UI
4983 #endif /* JSDEBUGGER_C_UI */
4984 #endif /* JSDEBUGGER */
4986 CheckHelpMessages();
4987 #ifdef HAVE_SETLOCALE
4988 setlocale(LC_ALL
, "");
4991 #ifdef JS_THREADSAFE
4992 if (PR_FAILURE
== PR_NewThreadPrivateIndex(&gStackBaseThreadIndex
, NULL
) ||
4993 PR_FAILURE
== PR_SetThreadPrivate(gStackBaseThreadIndex
, &stackDummy
)) {
4997 gStackBase
= (jsuword
) &stackDummy
;
5001 /* these streams are normally line buffered on OS/2 and need a \n, *
5002 * so we need to unbuffer then to get a reasonable prompt */
5014 // Set the timer calibration delay count to 0 so we get high
5015 // resolution right away, which we need for precise benchmarking.
5016 extern int CALIBRATION_DELAY_COUNT
;
5017 CALIBRATION_DELAY_COUNT
= 0;
5020 rt
= JS_NewRuntime(64L * 1024L * 1024L);
5024 if (!InitWatchdog(rt
))
5027 cx
= NewContext(rt
);
5031 JS_SetGCParameterForThread(cx
, JSGC_MAX_CODE_CACHE_BYTES
, 16 * 1024 * 1024);
5033 JS_BeginRequest(cx
);
5035 JSObject
*glob
= NewGlobalObject(cx
);
5039 JSObject
*envobj
= JS_DefineObject(cx
, glob
, "environment", &env_class
, NULL
, 0);
5040 if (!envobj
|| !JS_SetPrivate(cx
, envobj
, envp
))
5045 * XXX A command line option to enable debugging (or not) would be good
5047 jsdc
= JSD_DebuggerOnForUser(rt
, NULL
, NULL
);
5050 JSD_JSContextInUse(jsdc
, cx
);
5051 #ifdef JSD_LOWLEVEL_SOURCE
5052 JS_SetSourceHandler(rt
, SendSourceToJSDebugger
, jsdc
);
5053 #endif /* JSD_LOWLEVEL_SOURCE */
5054 #ifdef JSDEBUGGER_JAVA_UI
5055 jsdjc
= JSDJ_CreateContext();
5058 JSDJ_SetJSDContext(jsdjc
, jsdc
);
5059 java_env
= JSDJ_CreateJavaVMAndStartDebugger(jsdjc
);
5061 * XXX This would be the place to wait for the debugger to start.
5062 * Waiting would be nice in general, but especially when a js file
5063 * is passed on the cmd line.
5065 #endif /* JSDEBUGGER_JAVA_UI */
5066 #ifdef JSDEBUGGER_C_UI
5067 jsdbc
= JSDB_InitDebugger(rt
, jsdc
, 0);
5068 #endif /* JSDEBUGGER_C_UI */
5069 #endif /* JSDEBUGGER */
5071 #ifdef JS_THREADSAFE
5072 class ShellWorkerHooks
: public js::workers::WorkerHooks
{
5074 JSObject
*newGlobalObject(JSContext
*cx
) { return NewGlobalObject(cx
); }
5076 ShellWorkerHooks hooks
;
5077 if (!JS_AddNamedRoot(cx
, &gWorkers
, "Workers") ||
5078 !js::workers::init(cx
, &hooks
, glob
, &gWorkers
)) {
5083 result
= ProcessArgs(cx
, glob
, argv
, argc
);
5085 #ifdef JS_THREADSAFE
5086 js::workers::finish(cx
, gWorkers
);
5087 JS_RemoveRoot(cx
, &gWorkers
);
5094 #ifdef JSDEBUGGER_C_UI
5096 JSDB_TermDebugger(jsdc
);
5097 #endif /* JSDEBUGGER_C_UI */
5098 JSD_DebuggerOff(jsdc
);
5100 #endif /* JSDEBUGGER */
5104 JS_CommenceRuntimeShutDown(rt
);
5106 DestroyContext(cx
, true);
5110 JS_DestroyRuntime(rt
);