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 ***** */
41 #define __STDC_LIMIT_MACROS
58 #include "jswrapper.h"
62 #include "jsbuiltins.h"
83 #ifdef JSDEBUGGER_JAVA_UI
85 #endif /* JSDEBUGGER_JAVA_UI */
86 #ifdef JSDEBUGGER_C_UI
88 #endif /* JSDEBUGGER_C_UI */
89 #endif /* JSDEBUGGER */
91 #include "jsworkers.h"
93 #include "jsobjinlines.h"
94 #include "jsscriptinlines.h"
98 #include <sys/types.h>
102 #if defined(XP_WIN) || defined(XP_OS2)
103 #include <io.h> /* for isatty() */
112 typedef enum JSShellExitCode
{
113 EXITCODE_RUNTIME_ERROR
= 3,
114 EXITCODE_FILE_NOT_FOUND
= 4,
115 EXITCODE_OUT_OF_MEMORY
= 5,
119 size_t gStackChunkSize
= 8192;
121 /* Assume that we can not use more than 5e5 bytes of C stack by default. */
122 #if defined(DEBUG) && defined(__SUNPRO_CC)
123 /* Sun compiler uses larger stack space for js_Interpret() with debug
124 Use a bigger gMaxStackSize to make "make check" happy. */
125 size_t gMaxStackSize
= 5000000;
127 size_t gMaxStackSize
= 500000;
132 static PRUintn gStackBaseThreadIndex
;
134 static jsuword gStackBase
;
137 static size_t gScriptStackQuota
= JS_DEFAULT_SCRIPT_STACK_QUOTA
;
140 * Limit the timeout to 30 minutes to prevent an overflow on platfoms
141 * that represent the time internally in microseconds using 32-bit int.
143 static jsdouble MAX_TIMEOUT_INTERVAL
= 1800.0;
144 static jsdouble gTimeoutInterval
= -1.0;
145 static volatile bool gCanceled
= false;
147 static bool enableJit
= false;
150 SetTimeoutValue(JSContext
*cx
, jsdouble t
);
153 InitWatchdog(JSRuntime
*rt
);
159 ScheduleWatchdog(JSRuntime
*rt
, jsdouble t
);
162 CancelExecution(JSRuntime
*rt
);
165 * Watchdog thread state.
169 static PRLock
*gWatchdogLock
= NULL
;
170 static PRCondVar
*gWatchdogWakeup
= NULL
;
171 static PRThread
*gWatchdogThread
= NULL
;
172 static bool gWatchdogHasTimeout
= false;
173 static PRIntervalTime gWatchdogTimeout
= 0;
175 static PRCondVar
*gSleepWakeup
= NULL
;
179 static JSRuntime
*gRuntime
= NULL
;
184 JSBool gQuitting
= JS_FALSE
;
185 FILE *gErrFile
= NULL
;
186 FILE *gOutFile
= NULL
;
188 JSObject
*gWorkers
= NULL
;
191 static JSBool reportWarnings
= JS_TRUE
;
192 static JSBool compileOnly
= JS_FALSE
;
194 typedef enum JSShellErrNum
{
195 #define MSG_DEF(name, number, count, exception, format) \
197 #include "jsshell.msg"
204 NewContext(JSRuntime
*rt
);
207 DestroyContext(JSContext
*cx
, bool withGC
);
209 static const JSErrorFormatString
*
210 my_GetErrorMessage(void *userRef
, const char *locale
, const uintN errorNumber
);
213 split_setup(JSContext
*cx
, JSBool evalcx
);
217 JS_EXTERN_API(char) *readline(const char *prompt
);
218 JS_EXTERN_API(void) add_history(char *line
);
223 ReportException(JSContext
*cx
)
225 if (JS_IsExceptionPending(cx
)) {
226 if (!JS_ReportPendingException(cx
))
227 JS_ClearPendingException(cx
);
233 ToString(JSContext
*aCx
, jsval v
, JSBool aThrow
= JS_FALSE
)
237 mStr
= JS_ValueToString(cx
, v
);
238 if (!aThrow
&& !mStr
)
240 JS_AddNamedStringRoot(cx
, &mStr
, "Value ToString helper");
243 JS_RemoveStringRoot(cx
, &mStr
);
245 JSBool
threw() { return !mStr
; }
246 jsval
getJSVal() { return STRING_TO_JSVAL(mStr
); }
247 const char *getBytes() {
248 return mStr
? JS_GetStringBytes(mStr
) : "(error converting value)";
256 class IdToString
: public ToString
{
258 IdToString(JSContext
*cx
, jsid id
, JSBool aThrow
= JS_FALSE
)
259 : ToString(cx
, IdToJsval(id
), aThrow
)
264 GetLine(FILE *file
, const char * prompt
)
270 * Use readline only if file is stdin, because there's no way to specify
271 * another handle. Are other filehandles interactive?
274 char *linep
= readline(prompt
);
276 * We set it to zero to avoid complaining about inappropriate ioctl
277 * for device in the case of EOF. Looks like errno == 251 if line is
278 * finished with EOF and errno == 25 (EINVAL on Mac) if there is
279 * nothing left to read.
281 if (errno
== 251 || errno
== 25 || errno
== EINVAL
)
285 if (linep
[0] != '\0')
291 if (*prompt
!= '\0') {
292 fprintf(gOutFile
, "%s", prompt
);
296 buffer
= (char *) malloc(size
);
299 char *current
= buffer
;
300 while (fgets(current
, size
- len
, file
)) {
301 len
+= strlen(current
);
302 char *t
= buffer
+ len
- 1;
304 /* Line was read. We remove '\n' and exit. */
308 if (len
+ 1 == size
) {
310 char *tmp
= (char *) realloc(buffer
, size
);
317 current
= buffer
+ len
;
319 if (len
&& !ferror(file
))
326 * State to store as JSContext private.
328 * We declare such timestamp as volatile as they are updated in the operation
329 * callback without taking any locks. Any possible race can only lead to more
330 * frequent callback calls. This is safe as the callback does everything based
333 struct JSShellContextData
{
334 volatile JSIntervalTime startTime
;
337 static JSShellContextData
*
340 /* Prevent creation of new contexts after we have been canceled. */
344 JSShellContextData
*data
= (JSShellContextData
*)
345 calloc(sizeof(JSShellContextData
), 1);
348 data
->startTime
= js_IntervalNow();
352 static inline JSShellContextData
*
353 GetContextData(JSContext
*cx
)
355 JSShellContextData
*data
= (JSShellContextData
*) JS_GetContextPrivate(cx
);
362 ShellOperationCallback(JSContext
*cx
)
367 JS_ClearPendingException(cx
);
372 SetContextOptions(JSContext
*cx
)
374 JS_SetNativeStackQuota(cx
, gMaxStackSize
);
375 JS_SetScriptStackQuota(cx
, gScriptStackQuota
);
376 JS_SetOperationCallback(cx
, ShellOperationCallback
);
384 Process(JSContext
*cx
, JSObject
*obj
, char *filename
, JSBool forceTTY
)
397 if (forceTTY
|| !filename
|| strcmp(filename
, "-") == 0) {
400 file
= fopen(filename
, "r");
402 JS_ReportErrorNumber(cx
, my_GetErrorMessage
, NULL
,
403 JSSMSG_CANT_OPEN
, filename
, strerror(errno
));
404 gExitCode
= EXITCODE_FILE_NOT_FOUND
;
409 SetContextOptions(cx
);
412 /* windows mobile (and possibly other os's) does not have a TTY */
413 if (!forceTTY
&& !isatty(fileno(file
)))
417 * It's not interactive - just execute it.
419 * Support the UNIX #! shell hack; gobble the first line if it starts
420 * with '#'. TODO - this isn't quite compatible with sharp variables,
421 * as a legal js program (using sharp variables) might start with '#'.
422 * But that would require multi-character lookahead.
424 int ch
= fgetc(file
);
426 while((ch
= fgetc(file
)) != EOF
) {
427 if (ch
== '\n' || ch
== '\r')
433 oldopts
= JS_GetOptions(cx
);
434 JS_SetOptions(cx
, oldopts
| JSOPTION_COMPILE_N_GO
| JSOPTION_NO_SCRIPT_RVAL
);
435 script
= JS_CompileFileHandle(cx
, obj
, filename
, file
);
436 JS_SetOptions(cx
, oldopts
);
439 (void)JS_ExecuteScript(cx
, obj
, script
, NULL
);
440 JS_DestroyScript(cx
, script
);
448 /* It's an interactive filehandle; drop into read-eval-print loop. */
452 size
= 0; /* assign here to avoid warnings */
455 * Accumulate lines until we get a 'compilable unit' - one that either
456 * generates an error (before running out of source) or that compiles
457 * cleanly. This should be whenever we get a complete statement that
458 * coincides with the end of a line.
461 size_t len
= 0; /* initialize to avoid warnings */
463 ScheduleWatchdog(cx
->runtime
, -1);
469 JSAutoSuspendRequest
suspended(cx
);
470 line
= GetLine(file
, startline
== lineno
? "js> " : "");
474 JS_ReportError(cx
, strerror(errno
));
483 len
= strlen(buffer
);
487 * len + 1 is required to store '\n' in the end of line.
489 size_t newlen
= strlen(line
) + (len
? len
+ 1 : 0);
490 if (newlen
+ 1 > size
) {
491 size
= newlen
+ 1 > size
* 2 ? newlen
+ 1 : size
* 2;
492 char *newBuf
= (char *) realloc(buffer
, size
);
496 JS_ReportOutOfMemory(cx
);
501 char *current
= buffer
+ len
;
502 if (startline
!= lineno
)
504 strcpy(current
, line
);
509 if (!ScheduleWatchdog(cx
->runtime
, gTimeoutInterval
)) {
513 } while (!JS_BufferIsCompilableUnit(cx
, obj
, buffer
, len
));
515 if (hitEOF
&& !buffer
)
518 /* Clear any pending exception from previous failed compiles. */
519 JS_ClearPendingException(cx
);
521 /* Even though we're interactive, we have a compile-n-go opportunity. */
522 oldopts
= JS_GetOptions(cx
);
524 JS_SetOptions(cx
, oldopts
| JSOPTION_COMPILE_N_GO
);
525 script
= JS_CompileScript(cx
, obj
, buffer
, len
, "typein",
528 JS_SetOptions(cx
, oldopts
);
532 ok
= JS_ExecuteScript(cx
, obj
, script
, &result
);
533 if (ok
&& !JSVAL_IS_VOID(result
)) {
534 str
= JS_ValueToSource(cx
, result
);
536 fprintf(gOutFile
, "%s\n", JS_GetStringBytes(str
));
541 JS_DestroyScript(cx
, script
);
544 } while (!hitEOF
&& !gQuitting
);
547 fprintf(gOutFile
, "\n");
556 fprintf(gErrFile
, "%s\n", JS_GetImplementationVersion());
557 fprintf(gErrFile
, "usage: js [-zKPswWxCij] [-t timeoutSeconds] [-c stackchunksize] [-o option] [-v version] [-f scriptfile] [-e script] [-S maxstacksize] "
562 "[-T TraceVisFileName] "
564 "[scriptfile] [scriptarg...]\n");
569 * JSContext option name to flag map. The option names are in alphabetical
570 * order for better reporting.
572 static const struct {
576 {"anonfunfix", JSOPTION_ANONFUNFIX
},
577 {"atline", JSOPTION_ATLINE
},
578 {"jit", JSOPTION_JIT
},
579 {"relimit", JSOPTION_RELIMIT
},
580 {"strict", JSOPTION_STRICT
},
581 {"werror", JSOPTION_WERROR
},
582 {"xml", JSOPTION_XML
},
586 MapContextOptionNameToFlag(JSContext
* cx
, const char* name
)
588 for (size_t i
= 0; i
!= JS_ARRAY_LENGTH(js_options
); ++i
) {
589 if (strcmp(name
, js_options
[i
].name
) == 0)
590 return js_options
[i
].flag
;
593 char* msg
= JS_sprintf_append(NULL
,
594 "unknown option name '%s'."
595 " The valid names are ", name
);
596 for (size_t i
= 0; i
!= JS_ARRAY_LENGTH(js_options
); ++i
) {
599 msg
= JS_sprintf_append(msg
, "%s%s", js_options
[i
].name
,
600 (i
+ 2 < JS_ARRAY_LENGTH(js_options
)
602 : i
+ 2 == JS_ARRAY_LENGTH(js_options
)
607 JS_ReportOutOfMemory(cx
);
609 JS_ReportError(cx
, msg
);
615 extern JSClass global_class
;
617 #if defined(JS_TRACER) && defined(DEBUG)
619 extern struct JSClass jitstats_class
;
620 void InitJITStatsClass(JSContext
*cx
, JSObject
*glob
);
625 ProcessArgs(JSContext
*cx
, JSObject
*obj
, char **argv
, int argc
)
629 char *filename
= NULL
;
630 JSBool isInteractive
= JS_TRUE
;
631 JSBool forceTTY
= JS_FALSE
;
634 * Scan past all optional arguments so we can create the arguments object
635 * before processing any -f options, which must interleave properly with
636 * -v and -w options. This requires two passes, and without getopt, we'll
637 * have to keep the option logic here and in the second for loop in sync.
639 for (i
= 0; i
< argc
; i
++) {
640 if (argv
[i
][0] != '-' || argv
[i
][1] == '\0') {
644 switch (argv
[i
][1]) {
664 * Create arguments early and define it to root it, so it's safe from any
665 * GC calls nested below, and so it is available to -f <file> arguments.
667 argsObj
= JS_NewArrayObject(cx
, 0, NULL
);
670 if (!JS_DefineProperty(cx
, obj
, "arguments", OBJECT_TO_JSVAL(argsObj
),
676 for (j
= 0; j
< length
; j
++) {
677 JSString
*str
= JS_NewStringCopyZ(cx
, argv
[i
++]);
680 if (!JS_DefineElement(cx
, argsObj
, j
, STRING_TO_JSVAL(str
),
681 NULL
, NULL
, JSPROP_ENUMERATE
)) {
686 for (i
= 0; i
< argc
; i
++) {
687 if (argv
[i
][0] != '-' || argv
[i
][1] == '\0') {
688 filename
= argv
[i
++];
689 isInteractive
= JS_FALSE
;
693 switch (argv
[i
][1]) {
698 JS_SetVersion(cx
, (JSVersion
) atoi(argv
[i
]));
705 JS_SetGCZeal(cx
, !!(atoi(argv
[i
])));
710 reportWarnings
= JS_TRUE
;
714 reportWarnings
= JS_FALSE
;
718 JS_ToggleOptions(cx
, JSOPTION_STRICT
);
722 JS_ToggleOptions(cx
, JSOPTION_RELIMIT
);
726 JS_ToggleOptions(cx
, JSOPTION_XML
);
730 enableJit
= !enableJit
;
731 JS_ToggleOptions(cx
, JSOPTION_JIT
);
732 #if defined(JS_TRACER) && defined(DEBUG)
733 js::InitJITStatsClass(cx
, JS_GetGlobalObject(cx
));
734 JS_DefineObject(cx
, JS_GetGlobalObject(cx
), "tracemonkey",
735 &js::jitstats_class
, NULL
, 0);
744 uint32 flag
= MapContextOptionNameToFlag(cx
, argv
[i
]);
747 JS_ToggleOptions(cx
, flag
);
751 if (JS_GET_CLASS(cx
, JS_GetPrototype(cx
, obj
)) != &global_class
) {
754 if (!JS_SealObject(cx
, obj
, JS_TRUE
))
756 gobj
= JS_NewGlobalObject(cx
, &global_class
);
759 if (!JS_SetPrototype(cx
, gobj
, obj
))
761 JS_SetParent(cx
, gobj
, NULL
);
762 JS_SetGlobalObject(cx
, gobj
);
771 if (!SetTimeoutValue(cx
, atof(argv
[i
])))
777 /* set stack chunk size */
778 gStackChunkSize
= atoi(argv
[++i
]);
785 Process(cx
, obj
, argv
[i
], JS_FALSE
);
790 * XXX: js -f foo.js should interpret foo.js and then
791 * drop into interactive mode, but that breaks the test
792 * harness. Just execute foo.js for now.
794 isInteractive
= JS_FALSE
;
804 /* Pass a filename of -e to imitate PERL */
805 JS_EvaluateScript(cx
, obj
, argv
[i
], strlen(argv
[i
]),
808 isInteractive
= JS_FALSE
;
813 compileOnly
= JS_TRUE
;
814 isInteractive
= JS_FALSE
;
818 isInteractive
= forceTTY
= JS_TRUE
;
825 /* Set maximum stack size. */
826 gMaxStackSize
= atoi(argv
[i
]);
830 obj
= split_setup(cx
, JS_FALSE
);
844 StartTraceVis(argv
[i
]);
852 if (filename
|| isInteractive
)
853 Process(cx
, obj
, filename
, forceTTY
);
858 Version(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
860 if (argc
> 0 && JSVAL_IS_INT(argv
[0]))
861 *rval
= INT_TO_JSVAL(JS_SetVersion(cx
, (JSVersion
) JSVAL_TO_INT(argv
[0])));
863 *rval
= INT_TO_JSVAL(JS_GetVersion(cx
));
868 Options(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
877 for (uintN i
= 0; i
< argc
; i
++) {
878 str
= JS_ValueToString(cx
, argv
[i
]);
881 argv
[i
] = STRING_TO_JSVAL(str
);
882 opt
= JS_GetStringBytes(str
);
885 flag
= MapContextOptionNameToFlag(cx
, opt
);
890 optset
= JS_ToggleOptions(cx
, optset
);
894 for (size_t i
= 0; i
!= JS_ARRAY_LENGTH(js_options
); i
++) {
895 if (js_options
[i
].flag
& optset
) {
897 names
= JS_sprintf_append(names
, "%s%s",
898 names
? "," : "", js_options
[i
].name
);
906 JS_ReportOutOfMemory(cx
);
909 str
= JS_NewString(cx
, names
, strlen(names
));
914 *rval
= STRING_TO_JSVAL(str
);
919 Load(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
923 const char *filename
;
928 for (i
= 0; i
< argc
; i
++) {
929 str
= JS_ValueToString(cx
, argv
[i
]);
932 argv
[i
] = STRING_TO_JSVAL(str
);
933 filename
= JS_GetStringBytes(str
);
935 oldopts
= JS_GetOptions(cx
);
936 JS_SetOptions(cx
, oldopts
| JSOPTION_COMPILE_N_GO
| JSOPTION_NO_SCRIPT_RVAL
);
937 script
= JS_CompileFile(cx
, obj
, filename
);
938 JS_SetOptions(cx
, oldopts
);
943 ? JS_ExecuteScript(cx
, obj
, script
, NULL
)
945 JS_DestroyScript(cx
, script
);
955 * function readline()
956 * Provides a hook for scripts to read a line from stdin.
959 ReadLine(JSContext
*cx
, uintN argc
, jsval
*vp
)
964 size_t bufsize
, buflength
, gotlength
;
971 buf
= (char *) JS_malloc(cx
, bufsize
);
975 sawNewline
= JS_FALSE
;
977 js_fgets(buf
+ buflength
, bufsize
- buflength
, from
)) > 0) {
978 buflength
+= gotlength
;
981 if (buf
[buflength
- 1] == '\n') {
982 buf
[buflength
- 1] = '\0';
983 sawNewline
= JS_TRUE
;
985 } else if (buflength
< bufsize
- 1) {
989 /* Else, grow our buffer for another pass. */
991 if (bufsize
> buflength
) {
992 tmp
= (char *) JS_realloc(cx
, buf
, bufsize
);
994 JS_ReportOutOfMemory(cx
);
1006 /* Treat the empty string specially. */
1007 if (buflength
== 0) {
1008 *vp
= feof(from
) ? JSVAL_NULL
: JS_GetEmptyStringValue(cx
);
1013 /* Shrink the buffer to the real size. */
1014 tmp
= (char *) JS_realloc(cx
, buf
, buflength
);
1023 * Turn buf into a JSString. Note that buflength includes the trailing null
1026 str
= JS_NewString(cx
, buf
, sawNewline
? buflength
- 1 : buflength
);
1032 *vp
= STRING_TO_JSVAL(str
);
1037 Print(JSContext
*cx
, uintN argc
, jsval
*vp
)
1044 argv
= JS_ARGV(cx
, vp
);
1045 for (i
= 0; i
< argc
; i
++) {
1046 str
= JS_ValueToString(cx
, argv
[i
]);
1049 bytes
= JS_EncodeString(cx
, str
);
1052 fprintf(gOutFile
, "%s%s", i
? " " : "", bytes
);
1056 fputc('\n', gOutFile
);
1059 JS_SET_RVAL(cx
, vp
, JSVAL_VOID
);
1064 Help(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
);
1067 Quit(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
1069 JS_ConvertArguments(cx
, argc
, argv
,"/ i", &gExitCode
);
1071 gQuitting
= JS_TRUE
;
1072 #ifdef JS_THREADSAFE
1074 js::workers::terminateAll(cx
, gWorkers
);
1080 ToSource(JSContext
*cx
, jsval
*vp
)
1082 JSString
*str
= JS_ValueToSource(cx
, *vp
);
1084 *vp
= STRING_TO_JSVAL(str
);
1085 return JS_GetStringBytes(str
);
1087 JS_ClearPendingException(cx
);
1088 return "<<error converting value to string>>";
1092 AssertEq(JSContext
*cx
, uintN argc
, jsval
*vp
)
1094 if (!(argc
== 2 || (argc
== 3 && JSVAL_IS_STRING(JS_ARGV(cx
, vp
)[2])))) {
1095 JS_ReportErrorNumber(cx
, my_GetErrorMessage
, NULL
,
1097 ? JSSMSG_NOT_ENOUGH_ARGS
1099 ? JSSMSG_INVALID_ARGS
1100 : JSSMSG_TOO_MANY_ARGS
,
1105 jsval
*argv
= JS_ARGV(cx
, vp
);
1106 if (!JS_SameValue(cx
, argv
[0], argv
[1])) {
1107 const char *actual
= ToSource(cx
, &argv
[0]);
1108 const char *expected
= ToSource(cx
, &argv
[1]);
1110 JS_ReportErrorNumber(cx
, my_GetErrorMessage
, NULL
, JSSMSG_ASSERT_EQ_FAILED
,
1113 JS_ReportErrorNumber(cx
, my_GetErrorMessage
, NULL
, JSSMSG_ASSERT_EQ_FAILED_MSG
,
1114 actual
, expected
, JS_GetStringBytes(JSVAL_TO_STRING(argv
[2])));
1118 JS_SET_RVAL(cx
, vp
, JSVAL_VOID
);
1123 GC(JSContext
*cx
, uintN argc
, jsval
*vp
)
1125 size_t preBytes
= cx
->runtime
->gcBytes
;
1129 JS_snprintf(buf
, sizeof(buf
), "before %lu, after %lu, break %08lx\n",
1130 (unsigned long)preBytes
, (unsigned long)cx
->runtime
->gcBytes
,
1132 (unsigned long)sbrk(0)
1137 *vp
= STRING_TO_JSVAL(JS_NewStringCopyZ(cx
, buf
));
1143 GCStats(JSContext
*cx
, uintN argc
, jsval
*vp
)
1145 js_DumpGCStats(cx
->runtime
, stdout
);
1152 GCParameter(JSContext
*cx
, uintN argc
, jsval
*vp
)
1155 const char *paramName
;
1160 str
= JS_ValueToString(cx
, JSVAL_VOID
);
1163 str
= JS_ValueToString(cx
, vp
[2]);
1166 vp
[2] = STRING_TO_JSVAL(str
);
1168 paramName
= JS_GetStringBytes(str
);
1171 if (strcmp(paramName
, "maxBytes") == 0) {
1172 param
= JSGC_MAX_BYTES
;
1173 } else if (strcmp(paramName
, "maxMallocBytes") == 0) {
1174 param
= JSGC_MAX_MALLOC_BYTES
;
1175 } else if (strcmp(paramName
, "gcStackpoolLifespan") == 0) {
1176 param
= JSGC_STACKPOOL_LIFESPAN
;
1177 } else if (strcmp(paramName
, "gcBytes") == 0) {
1179 } else if (strcmp(paramName
, "gcNumber") == 0) {
1180 param
= JSGC_NUMBER
;
1181 } else if (strcmp(paramName
, "gcTriggerFactor") == 0) {
1182 param
= JSGC_TRIGGER_FACTOR
;
1185 "the first argument argument must be maxBytes, "
1186 "maxMallocBytes, gcStackpoolLifespan, gcBytes, "
1187 "gcNumber or gcTriggerFactor");
1192 value
= JS_GetGCParameter(cx
->runtime
, param
);
1193 return JS_NewNumberValue(cx
, value
, &vp
[0]);
1196 if (param
== JSGC_NUMBER
||
1197 param
== JSGC_BYTES
) {
1198 JS_ReportError(cx
, "Attempt to change read-only parameter %s",
1203 if (!JS_ValueToECMAUint32(cx
, vp
[3], &value
)) {
1205 "the second argument must be convertable to uint32 "
1206 "with non-zero value");
1209 if (param
== JSGC_TRIGGER_FACTOR
&& value
< 100) {
1211 "the gcTriggerFactor value must be >= 100");
1214 JS_SetGCParameter(cx
->runtime
, param
, value
);
1221 GCZeal(JSContext
*cx
, uintN argc
, jsval
*vp
)
1225 if (!JS_ValueToECMAUint32(cx
, argc
== 0 ? JSVAL_VOID
: vp
[2], &zeal
))
1227 JS_SetGCZeal(cx
, (uint8
)zeal
);
1231 #endif /* JS_GC_ZEAL */
1233 typedef struct JSCountHeapNode JSCountHeapNode
;
1235 struct JSCountHeapNode
{
1238 JSCountHeapNode
*next
;
1241 typedef struct JSCountHeapTracer
{
1243 JSDHashTable visited
;
1245 JSCountHeapNode
*traceList
;
1246 JSCountHeapNode
*recycleList
;
1247 } JSCountHeapTracer
;
1250 CountHeapNotify(JSTracer
*trc
, void *thing
, uint32 kind
)
1252 JSCountHeapTracer
*countTracer
;
1253 JSDHashEntryStub
*entry
;
1254 JSCountHeapNode
*node
;
1256 JS_ASSERT(trc
->callback
== CountHeapNotify
);
1257 countTracer
= (JSCountHeapTracer
*)trc
;
1258 if (!countTracer
->ok
)
1261 entry
= (JSDHashEntryStub
*)
1262 JS_DHashTableOperate(&countTracer
->visited
, thing
, JS_DHASH_ADD
);
1264 JS_ReportOutOfMemory(trc
->context
);
1265 countTracer
->ok
= JS_FALSE
;
1272 node
= countTracer
->recycleList
;
1274 countTracer
->recycleList
= node
->next
;
1276 node
= (JSCountHeapNode
*) JS_malloc(trc
->context
, sizeof *node
);
1278 countTracer
->ok
= JS_FALSE
;
1282 node
->thing
= thing
;
1284 node
->next
= countTracer
->traceList
;
1285 countTracer
->traceList
= node
;
1289 CountHeap(JSContext
*cx
, uintN argc
, jsval
*vp
)
1292 int32 startTraceKind
;
1297 JSCountHeapTracer countTracer
;
1298 JSCountHeapNode
*node
;
1301 static const struct {
1304 } traceKindNames
[] = {
1306 { "object", JSTRACE_OBJECT
},
1307 { "string", JSTRACE_STRING
},
1308 #if JS_HAS_XML_SUPPORT
1309 { "xml", JSTRACE_XML
},
1316 v
= JS_ARGV(cx
, vp
)[0];
1317 if (JSVAL_IS_TRACEABLE(v
)) {
1318 startThing
= JSVAL_TO_TRACEABLE(v
);
1319 startTraceKind
= JSVAL_TRACE_KIND(v
);
1320 } else if (!JSVAL_IS_NULL(v
)) {
1322 "the first argument is not null or a heap-allocated "
1330 str
= JS_ValueToString(cx
, JS_ARGV(cx
, vp
)[1]);
1333 bytes
= JS_GetStringBytes(str
);
1337 if (strcmp(bytes
, traceKindNames
[i
].name
) == 0) {
1338 traceKind
= traceKindNames
[i
].kind
;
1341 if (++i
== JS_ARRAY_LENGTH(traceKindNames
)) {
1342 JS_ReportError(cx
, "trace kind name '%s' is unknown", bytes
);
1348 JS_TRACER_INIT(&countTracer
.base
, cx
, CountHeapNotify
);
1349 if (!JS_DHashTableInit(&countTracer
.visited
, JS_DHashGetStubOps(),
1350 NULL
, sizeof(JSDHashEntryStub
),
1351 JS_DHASH_DEFAULT_CAPACITY(100))) {
1352 JS_ReportOutOfMemory(cx
);
1355 countTracer
.ok
= JS_TRUE
;
1356 countTracer
.traceList
= NULL
;
1357 countTracer
.recycleList
= NULL
;
1360 JS_TraceRuntime(&countTracer
.base
);
1362 JS_SET_TRACING_NAME(&countTracer
.base
, "root");
1363 JS_CallTracer(&countTracer
.base
, startThing
, startTraceKind
);
1367 while ((node
= countTracer
.traceList
) != NULL
) {
1368 if (traceKind
== -1 || node
->kind
== traceKind
)
1370 countTracer
.traceList
= node
->next
;
1371 node
->next
= countTracer
.recycleList
;
1372 countTracer
.recycleList
= node
;
1373 JS_TraceChildren(&countTracer
.base
, node
->thing
, node
->kind
);
1375 while ((node
= countTracer
.recycleList
) != NULL
) {
1376 countTracer
.recycleList
= node
->next
;
1379 JS_DHashTableFinish(&countTracer
.visited
);
1381 return countTracer
.ok
&& JS_NewNumberValue(cx
, (jsdouble
) counter
, vp
);
1385 ValueToScript(JSContext
*cx
, jsval v
)
1387 JSScript
*script
= NULL
;
1390 if (!JSVAL_IS_PRIMITIVE(v
)) {
1391 JSObject
*obj
= JSVAL_TO_OBJECT(v
);
1392 JSClass
*clasp
= JS_GET_CLASS(cx
, obj
);
1394 if (clasp
== Jsvalify(&js_ScriptClass
)) {
1395 script
= (JSScript
*) JS_GetPrivate(cx
, obj
);
1396 } else if (clasp
== Jsvalify(&js_GeneratorClass
.base
)) {
1397 JSGenerator
*gen
= (JSGenerator
*) JS_GetPrivate(cx
, obj
);
1398 fun
= gen
->getFloatingFrame()->fun
;
1399 script
= FUN_SCRIPT(fun
);
1404 fun
= JS_ValueToFunction(cx
, v
);
1407 script
= FUN_SCRIPT(fun
);
1409 JS_ReportErrorNumber(cx
, my_GetErrorMessage
, NULL
,
1410 JSSMSG_SCRIPTS_ONLY
);
1418 GetTrapArgs(JSContext
*cx
, uintN argc
, jsval
*argv
, JSScript
**scriptp
,
1425 *scriptp
= JS_GetScriptedCaller(cx
, NULL
)->script
;
1430 if (!JSVAL_IS_PRIMITIVE(v
) &&
1431 (JS_GET_CLASS(cx
, JSVAL_TO_OBJECT(v
)) == Jsvalify(&js_FunctionClass
) ||
1432 JS_GET_CLASS(cx
, JSVAL_TO_OBJECT(v
)) == Jsvalify(&js_ScriptClass
))) {
1433 script
= ValueToScript(cx
, v
);
1439 if (argc
> intarg
) {
1440 if (!JS_ValueToInt32(cx
, argv
[intarg
], ip
))
1448 TrapHandler(JSContext
*cx
, JSScript
*script
, jsbytecode
*pc
, jsval
*rval
,
1451 JSString
*str
= JSVAL_TO_STRING(closure
);
1452 JSStackFrame
*caller
= JS_GetScriptedCaller(cx
, NULL
);
1453 if (!JS_EvaluateUCInStackFrame(cx
, caller
,
1454 JS_GetStringChars(str
), JS_GetStringLength(str
),
1455 caller
->script
->filename
, caller
->script
->lineno
,
1457 return JSTRAP_ERROR
;
1459 if (!JSVAL_IS_VOID(*rval
))
1460 return JSTRAP_RETURN
;
1461 return JSTRAP_CONTINUE
;
1465 Trap(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
1472 JS_ReportErrorNumber(cx
, my_GetErrorMessage
, NULL
, JSSMSG_TRAP_USAGE
);
1476 str
= JS_ValueToString(cx
, argv
[argc
]);
1479 argv
[argc
] = STRING_TO_JSVAL(str
);
1480 if (!GetTrapArgs(cx
, argc
, argv
, &script
, &i
))
1482 return JS_SetTrap(cx
, script
, script
->code
+ i
, TrapHandler
, STRING_TO_JSVAL(str
));
1486 Untrap(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
1491 if (!GetTrapArgs(cx
, argc
, argv
, &script
, &i
))
1493 JS_ClearTrap(cx
, script
, script
->code
+ i
, NULL
, NULL
);
1498 LineToPC(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
1506 JS_ReportErrorNumber(cx
, my_GetErrorMessage
, NULL
, JSSMSG_LINE2PC_USAGE
);
1509 script
= JS_GetScriptedCaller(cx
, NULL
)->script
;
1510 if (!GetTrapArgs(cx
, argc
, argv
, &script
, &i
))
1512 lineno
= (i
== 0) ? script
->lineno
: (uintN
)i
;
1513 pc
= JS_LineNumberToPC(cx
, script
, lineno
);
1516 *rval
= INT_TO_JSVAL(pc
- script
->code
);
1521 PCToLine(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
1527 if (!GetTrapArgs(cx
, argc
, argv
, &script
, &i
))
1529 lineno
= JS_PCToLineNumber(cx
, script
, script
->code
+ i
);
1532 *rval
= INT_TO_JSVAL(lineno
);
1539 UpdateSwitchTableBounds(JSContext
*cx
, JSScript
*script
, uintN offset
,
1540 uintN
*start
, uintN
*end
)
1547 pc
= script
->code
+ offset
;
1548 op
= js_GetOpcode(cx
, script
, pc
);
1550 case JSOP_TABLESWITCHX
:
1551 jmplen
= JUMPX_OFFSET_LEN
;
1553 case JSOP_TABLESWITCH
:
1554 jmplen
= JUMP_OFFSET_LEN
;
1557 low
= GET_JUMP_OFFSET(pc
);
1558 pc
+= JUMP_OFFSET_LEN
;
1559 high
= GET_JUMP_OFFSET(pc
);
1560 pc
+= JUMP_OFFSET_LEN
;
1564 case JSOP_LOOKUPSWITCHX
:
1565 jmplen
= JUMPX_OFFSET_LEN
;
1567 case JSOP_LOOKUPSWITCH
:
1568 jmplen
= JUMP_OFFSET_LEN
;
1573 jmplen
+= JUMP_OFFSET_LEN
;
1577 /* [condswitch] switch does not have any jump or lookup tables. */
1578 JS_ASSERT(op
== JSOP_CONDSWITCH
);
1582 *start
= (uintN
)(pc
- script
->code
);
1583 *end
= *start
+ (uintN
)(n
* jmplen
);
1587 SrcNotes(JSContext
*cx
, JSScript
*script
)
1589 uintN offset
, delta
, caseOff
, switchTableStart
, switchTableEnd
;
1590 jssrcnote
*notes
, *sn
;
1597 fprintf(gOutFile
, "\nSource notes:\n");
1599 notes
= script
->notes();
1600 switchTableEnd
= switchTableStart
= 0;
1601 for (sn
= notes
; !SN_IS_TERMINATOR(sn
); sn
= SN_NEXT(sn
)) {
1602 delta
= SN_DELTA(sn
);
1604 type
= (JSSrcNoteType
) SN_TYPE(sn
);
1605 name
= js_SrcNoteSpec
[type
].name
;
1606 if (type
== SRC_LABEL
) {
1607 /* Check if the source note is for a switch case. */
1608 if (switchTableStart
<= offset
&& offset
< switchTableEnd
) {
1611 JS_ASSERT(js_GetOpcode(cx
, script
, script
->code
+ offset
) == JSOP_NOP
);
1614 fprintf(gOutFile
, "%3u: %5u [%4u] %-8s",
1615 (uintN
) (sn
- notes
), offset
, delta
, name
);
1618 fprintf(gOutFile
, " lineno %u", (uintN
) js_GetSrcNoteOffset(sn
, 0));
1621 fprintf(gOutFile
, " cond %u update %u tail %u",
1622 (uintN
) js_GetSrcNoteOffset(sn
, 0),
1623 (uintN
) js_GetSrcNoteOffset(sn
, 1),
1624 (uintN
) js_GetSrcNoteOffset(sn
, 2));
1627 fprintf(gOutFile
, " else %u elseif %u",
1628 (uintN
) js_GetSrcNoteOffset(sn
, 0),
1629 (uintN
) js_GetSrcNoteOffset(sn
, 1));
1637 fprintf(gOutFile
, " offset %u", (uintN
) js_GetSrcNoteOffset(sn
, 0));
1640 case SRC_LABELBRACE
:
1641 case SRC_BREAK2LABEL
:
1642 case SRC_CONT2LABEL
:
1643 index
= js_GetSrcNoteOffset(sn
, 0);
1644 JS_GET_SCRIPT_ATOM(script
, NULL
, index
, atom
);
1645 str
= ATOM_TO_STRING(atom
);
1646 fprintf(gOutFile
, " atom %u (", index
);
1647 js_FileEscapedString(gOutFile
, str
, 0);
1648 putc(')', gOutFile
);
1655 index
= js_GetSrcNoteOffset(sn
, 0);
1656 obj
= script
->getObject(index
);
1657 fun
= (JSFunction
*) JS_GetPrivate(cx
, obj
);
1658 str
= JS_DecompileFunction(cx
, fun
, JS_DONT_PRETTY_PRINT
);
1660 bytes
= JS_GetStringBytes(str
);
1662 ReportException(cx
);
1665 fprintf(gOutFile
, " function %u (%s)", index
, bytes
);
1669 fprintf(gOutFile
, " length %u", (uintN
) js_GetSrcNoteOffset(sn
, 0));
1670 caseOff
= (uintN
) js_GetSrcNoteOffset(sn
, 1);
1672 fprintf(gOutFile
, " first case offset %u", caseOff
);
1673 UpdateSwitchTableBounds(cx
, script
, offset
,
1674 &switchTableStart
, &switchTableEnd
);
1677 delta
= (uintN
) js_GetSrcNoteOffset(sn
, 0);
1679 if (script
->main
[offset
] == JSOP_LEAVEBLOCK
)
1680 fprintf(gOutFile
, " stack depth %u", delta
);
1682 fprintf(gOutFile
, " guard delta %u", delta
);
1687 fputc('\n', gOutFile
);
1692 Notes(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
1697 for (i
= 0; i
< argc
; i
++) {
1698 script
= ValueToScript(cx
, argv
[i
]);
1702 SrcNotes(cx
, script
);
1707 JS_STATIC_ASSERT(JSTRY_CATCH
== 0);
1708 JS_STATIC_ASSERT(JSTRY_FINALLY
== 1);
1709 JS_STATIC_ASSERT(JSTRY_ITER
== 2);
1711 static const char* const TryNoteNames
[] = { "catch", "finally", "iter" };
1714 TryNotes(JSContext
*cx
, JSScript
*script
)
1716 JSTryNote
*tn
, *tnlimit
;
1718 if (script
->trynotesOffset
== 0)
1721 tn
= script
->trynotes()->vector
;
1722 tnlimit
= tn
+ script
->trynotes()->length
;
1723 fprintf(gOutFile
, "\nException table:\n"
1724 "kind stack start end\n");
1726 JS_ASSERT(tn
->kind
< JS_ARRAY_LENGTH(TryNoteNames
));
1727 fprintf(gOutFile
, " %-7s %6u %8u %8u\n",
1728 TryNoteNames
[tn
->kind
], tn
->stackDepth
,
1729 tn
->start
, tn
->start
+ tn
->length
);
1730 } while (++tn
!= tnlimit
);
1735 DisassembleValue(JSContext
*cx
, jsval v
, bool lines
, bool recursive
)
1737 JSScript
*script
= ValueToScript(cx
, v
);
1740 if (VALUE_IS_FUNCTION(cx
, v
)) {
1741 JSFunction
*fun
= JS_ValueToFunction(cx
, v
);
1742 if (fun
&& (fun
->flags
& ~7U)) {
1743 uint16 flags
= fun
->flags
;
1744 fputs("flags:", stdout
);
1746 #define SHOW_FLAG(flag) if (flags & JSFUN_##flag) fputs(" " #flag, stdout);
1751 SHOW_FLAG(BOUND_METHOD
);
1752 SHOW_FLAG(HEAVYWEIGHT
);
1753 SHOW_FLAG(THISP_STRING
);
1754 SHOW_FLAG(THISP_NUMBER
);
1755 SHOW_FLAG(THISP_BOOLEAN
);
1756 SHOW_FLAG(EXPR_CLOSURE
);
1761 if (FUN_INTERPRETED(fun
)) {
1762 if (FUN_NULL_CLOSURE(fun
))
1763 fputs(" NULL_CLOSURE", stdout
);
1764 else if (FUN_FLAT_CLOSURE(fun
))
1765 fputs(" FLAT_CLOSURE", stdout
);
1767 if (fun
->u
.i
.nupvars
) {
1768 fputs("\nupvars: {\n", stdout
);
1770 void *mark
= JS_ARENA_MARK(&cx
->tempPool
);
1771 jsuword
*localNames
= js_GetLocalNameArray(cx
, fun
, &cx
->tempPool
);
1775 JSUpvarArray
*uva
= fun
->u
.i
.script
->upvars();
1776 uintN upvar_base
= fun
->countArgsAndVars();
1778 for (uint32 i
= 0, n
= uva
->length
; i
< n
; i
++) {
1779 JSAtom
*atom
= JS_LOCAL_NAME_TO_ATOM(localNames
[upvar_base
+ i
]);
1780 UpvarCookie cookie
= uva
->vector
[i
];
1782 printf(" %s: {skip:%u, slot:%u},\n",
1783 js_AtomToPrintableString(cx
, atom
), cookie
.level(), cookie
.slot());
1786 JS_ARENA_RELEASE(&cx
->tempPool
, mark
);
1794 if (!js_Disassemble(cx
, script
, lines
, stdout
))
1796 SrcNotes(cx
, script
);
1797 TryNotes(cx
, script
);
1799 if (recursive
&& script
->objectsOffset
!= 0) {
1800 JSObjectArray
*objects
= script
->objects();
1801 for (uintN i
= 0; i
!= objects
->length
; ++i
) {
1802 JSObject
*obj
= objects
->vector
[i
];
1803 if (obj
->isFunction()) {
1805 if (!DisassembleValue(cx
, OBJECT_TO_JSVAL(obj
),
1806 lines
, recursive
)) {
1816 Disassemble(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
1818 bool lines
= false, recursive
= false;
1820 while (argc
> 0 && JSVAL_IS_STRING(argv
[0])) {
1821 const char *bytes
= JS_GetStringBytes(JSVAL_TO_STRING(argv
[0]));
1822 lines
= !strcmp(bytes
, "-l");
1823 recursive
= !strcmp(bytes
, "-r");
1824 if (!lines
&& !recursive
)
1829 for (uintN i
= 0; i
< argc
; i
++) {
1830 if (!DisassembleValue(cx
, argv
[i
], lines
, recursive
))
1837 DisassFile(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
1840 const char *filename
;
1848 str
= JS_ValueToString(cx
, argv
[0]);
1851 argv
[0] = STRING_TO_JSVAL(str
);
1853 filename
= JS_GetStringBytes(str
);
1854 oldopts
= JS_GetOptions(cx
);
1855 JS_SetOptions(cx
, oldopts
| JSOPTION_COMPILE_N_GO
| JSOPTION_NO_SCRIPT_RVAL
);
1856 script
= JS_CompileFile(cx
, obj
, filename
);
1857 JS_SetOptions(cx
, oldopts
);
1861 if (script
->isEmpty())
1864 obj
= JS_NewScriptObject(cx
, script
);
1868 *rval
= OBJECT_TO_JSVAL(obj
); /* I like to root it, root it. */
1869 ok
= Disassemble(cx
, obj
, 1, rval
, rval
); /* gross, but works! */
1876 DisassWithSrc(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
,
1879 #define LINE_BUF_LEN 512
1880 uintN i
, len
, line1
, line2
, bupline
;
1883 char linebuf
[LINE_BUF_LEN
];
1884 jsbytecode
*pc
, *end
;
1886 static char sep
[] = ";-------------------------";
1889 for (i
= 0; ok
&& i
< argc
; i
++) {
1890 script
= ValueToScript(cx
, argv
[i
]);
1894 if (!script
->filename
) {
1895 JS_ReportErrorNumber(cx
, my_GetErrorMessage
, NULL
,
1896 JSSMSG_FILE_SCRIPTS_ONLY
);
1900 file
= fopen(script
->filename
, "r");
1902 JS_ReportErrorNumber(cx
, my_GetErrorMessage
, NULL
,
1903 JSSMSG_CANT_OPEN
, script
->filename
,
1909 end
= pc
+ script
->length
;
1911 /* burn the leading lines */
1912 line2
= JS_PCToLineNumber(cx
, script
, pc
);
1913 for (line1
= 0; line1
< line2
- 1; line1
++) {
1914 char *tmp
= fgets(linebuf
, LINE_BUF_LEN
, file
);
1916 JS_ReportError(cx
, "failed to read %s fully",
1925 line2
= JS_PCToLineNumber(cx
, script
, pc
);
1927 if (line2
< line1
) {
1928 if (bupline
!= line2
) {
1930 fprintf(gOutFile
, "%s %3u: BACKUP\n", sep
, line2
);
1933 if (bupline
&& line1
== line2
)
1934 fprintf(gOutFile
, "%s %3u: RESTORE\n", sep
, line2
);
1936 while (line1
< line2
) {
1937 if (!fgets(linebuf
, LINE_BUF_LEN
, file
)) {
1938 JS_ReportErrorNumber(cx
, my_GetErrorMessage
, NULL
,
1939 JSSMSG_UNEXPECTED_EOF
,
1945 fprintf(gOutFile
, "%s %3u: %s", sep
, line1
, linebuf
);
1949 len
= js_Disassemble1(cx
, script
, pc
,
1967 Tracing(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
1972 *rval
= BOOLEAN_TO_JSVAL(cx
->tracefp
!= 0);
1976 switch (JS_TypeOfValue(cx
, argv
[0])) {
1978 case JSTYPE_BOOLEAN
: {
1980 JS_ValueToBoolean(cx
, argv
[0], &bval
);
1981 file
= bval
? stderr
: NULL
;
1984 case JSTYPE_STRING
: {
1985 char *name
= JS_GetStringBytes(JSVAL_TO_STRING(argv
[0]));
1986 file
= fopen(name
, "w");
1988 JS_ReportError(cx
, "tracing: couldn't open output file %s: %s",
1989 name
, strerror(errno
));
1997 if (cx
->tracefp
&& cx
->tracefp
!= stderr
)
1998 fclose((FILE *)cx
->tracefp
);
2000 cx
->tracePrevPc
= NULL
;
2004 JSString
*str
= JS_ValueToString(cx
, argv
[0]);
2007 JS_ReportError(cx
, "tracing: illegal argument %s",
2008 JS_GetStringBytes(str
));
2013 DumpScope(JSContext
*cx
, JSObject
*obj
, FILE *fp
)
2016 for (JSScopeProperty
*sprop
= NULL
; JS_PropertyIterator(obj
, &sprop
);) {
2017 fprintf(fp
, "%3u %p ", i
++, (void *) sprop
);
2018 sprop
->dump(cx
, fp
);
2023 DumpStats(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
2033 for (i
= 0; i
< argc
; i
++) {
2034 str
= JS_ValueToString(cx
, argv
[i
]);
2037 argv
[i
] = STRING_TO_JSVAL(str
);
2038 bytes
= JS_GetStringBytes(str
);
2039 if (strcmp(bytes
, "arena") == 0) {
2040 #ifdef JS_ARENAMETER
2041 JS_DumpArenaStats(stdout
);
2043 } else if (strcmp(bytes
, "atom") == 0) {
2044 js_DumpAtoms(cx
, gOutFile
);
2045 } else if (strcmp(bytes
, "global") == 0) {
2046 DumpScope(cx
, cx
->globalObject
, stdout
);
2048 if (!JS_ValueToId(cx
, STRING_TO_JSVAL(str
), &id
))
2050 if (!js_FindProperty(cx
, id
, &obj
, &obj2
, &prop
))
2053 obj2
->dropProperty(cx
, prop
);
2054 if (!obj
->getProperty(cx
, id
, &value
))
2057 if (!prop
|| !value
.isObjectOrNull()) {
2058 fprintf(gErrFile
, "js: invalid stats argument %s\n",
2062 obj
= value
.toObjectOrNull();
2064 DumpScope(cx
, obj
, stdout
);
2071 DumpHeap(JSContext
*cx
, uintN argc
, jsval
*vp
)
2076 uint32 startTraceKind
;
2077 const char *badTraceArg
;
2080 void *thingToIgnore
;
2086 v
= JS_ARGV(cx
, vp
)[0];
2087 if (!JSVAL_IS_NULL(v
)) {
2090 str
= JS_ValueToString(cx
, v
);
2093 JS_ARGV(cx
, vp
)[0] = STRING_TO_JSVAL(str
);
2094 fileName
= JS_GetStringBytes(str
);
2101 v
= JS_ARGV(cx
, vp
)[1];
2102 if (JSVAL_IS_TRACEABLE(v
)) {
2103 startThing
= JSVAL_TO_TRACEABLE(v
);
2104 startTraceKind
= JSVAL_TRACE_KIND(v
);
2105 } else if (!JSVAL_IS_NULL(v
)) {
2106 badTraceArg
= "start";
2107 goto not_traceable_arg
;
2113 v
= JS_ARGV(cx
, vp
)[2];
2114 if (JSVAL_IS_TRACEABLE(v
)) {
2115 thingToFind
= JSVAL_TO_TRACEABLE(v
);
2116 } else if (!JSVAL_IS_NULL(v
)) {
2117 badTraceArg
= "toFind";
2118 goto not_traceable_arg
;
2122 maxDepth
= (size_t)-1;
2124 v
= JS_ARGV(cx
, vp
)[3];
2125 if (!JSVAL_IS_NULL(v
)) {
2128 if (!JS_ValueToECMAUint32(cx
, v
, &depth
))
2134 thingToIgnore
= NULL
;
2136 v
= JS_ARGV(cx
, vp
)[4];
2137 if (JSVAL_IS_TRACEABLE(v
)) {
2138 thingToIgnore
= JSVAL_TO_TRACEABLE(v
);
2139 } else if (!JSVAL_IS_NULL(v
)) {
2140 badTraceArg
= "toIgnore";
2141 goto not_traceable_arg
;
2148 dumpFile
= fopen(fileName
, "w");
2150 JS_ReportError(cx
, "can't open %s: %s", fileName
, strerror(errno
));
2155 ok
= JS_DumpHeap(cx
, dumpFile
, startThing
, startTraceKind
, thingToFind
,
2156 maxDepth
, thingToIgnore
);
2157 if (dumpFile
!= stdout
)
2162 JS_ReportError(cx
, "argument '%s' is not null or a heap-allocated thing",
2173 EscapeWideString(jschar
*w
)
2175 static char enuf
[80];
2176 static char hex
[] = "0123456789abcdef";
2183 for (i
= j
= 0; i
< sizeof enuf
- 1; i
++, j
++) {
2187 b
= (unsigned char)(u
>> 8);
2188 c
= (unsigned char)(u
);
2190 if (i
>= sizeof enuf
- 6)
2194 enuf
[i
++] = hex
[b
>> 4];
2195 enuf
[i
++] = hex
[b
& 15];
2196 enuf
[i
++] = hex
[c
>> 4];
2197 enuf
[i
] = hex
[c
& 15];
2198 } else if (!isprint(c
)) {
2199 if (i
>= sizeof enuf
- 4)
2203 enuf
[i
++] = hex
[c
>> 4];
2204 enuf
[i
] = hex
[c
& 15];
2216 ZZ_formatter(JSContext
*cx
, const char *format
, JSBool fromJS
, jsval
**vpp
,
2223 printf("entering ZZ_formatter");
2227 if (!JS_ValueToNumber(cx
, vp
[0], &re
))
2229 if (!JS_ValueToNumber(cx
, vp
[1], &im
))
2231 *va_arg(ap
, jsdouble
*) = re
;
2232 *va_arg(ap
, jsdouble
*) = im
;
2234 re
= va_arg(ap
, jsdouble
);
2235 im
= va_arg(ap
, jsdouble
);
2236 if (!JS_NewNumberValue(cx
, re
, &vp
[0]))
2238 if (!JS_NewNumberValue(cx
, im
, &vp
[1]))
2243 printf("leaving ZZ_formatter");
2248 ConvertArgs(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
2250 JSBool b
= JS_FALSE
;
2254 jsdouble d
= 0, I
= 0, re
= 0, im
= 0;
2256 JSString
*str
= NULL
;
2258 JSObject
*obj2
= NULL
;
2259 JSFunction
*fun
= NULL
;
2260 jsval v
= JSVAL_VOID
;
2263 if (!JS_AddArgumentFormatter(cx
, "ZZ", ZZ_formatter
))
2265 ok
= JS_ConvertArguments(cx
, argc
, argv
, "b/ciujdIsSWofvZZ*",
2266 &b
, &c
, &i
, &u
, &j
, &d
, &I
, &s
, &str
, &w
, &obj2
,
2267 &fun
, &v
, &re
, &im
);
2268 JS_RemoveArgumentFormatter(cx
, "ZZ");
2272 "b %u, c %x (%c), i %ld, u %lu, j %ld\n",
2273 b
, c
, (char)c
, i
, u
, j
);
2274 ToString
obj2string(cx
, obj2
);
2275 ToString
valueString(cx
, v
);
2276 JSString
*tmpstr
= JS_DecompileFunction(cx
, fun
, 4);
2279 func
= JS_GetStringBytes(tmpstr
);
2281 ReportException(cx
);
2282 func
= "error decompiling fun";
2285 "d %g, I %g, s %s, S %s, W %s, obj %s, fun %s\n"
2286 "v %s, re %g, im %g\n",
2287 d
, I
, s
, str
? JS_GetStringBytes(str
) : "", EscapeWideString(w
),
2288 obj2string
.getBytes(),
2290 valueString
.getBytes(), re
, im
);
2296 BuildDate(JSContext
*cx
, uintN argc
, jsval
*vp
)
2298 char version
[20] = "\n";
2299 #if JS_VERSION < 150
2300 sprintf(version
, " for version %d\n", JS_VERSION
);
2302 fprintf(gOutFile
, "built on %s at %s%s", __DATE__
, __TIME__
, version
);
2308 Clear(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
2310 if (argc
!= 0 && !JS_ValueToObject(cx
, argv
[0], &obj
))
2312 JS_ClearScope(cx
, obj
);
2317 Intern(JSContext
*cx
, uintN argc
, jsval
*vp
)
2321 str
= JS_ValueToString(cx
, argc
== 0 ? JSVAL_VOID
: vp
[2]);
2324 if (!JS_InternUCStringN(cx
, JS_GetStringChars(str
),
2325 JS_GetStringLength(str
))) {
2333 Clone(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
2335 JSObject
*funobj
, *parent
, *clone
;
2337 if (VALUE_IS_FUNCTION(cx
, argv
[0])) {
2338 funobj
= JSVAL_TO_OBJECT(argv
[0]);
2340 JSFunction
*fun
= JS_ValueToFunction(cx
, argv
[0]);
2343 funobj
= JS_GetFunctionObject(fun
);
2346 if (!JS_ValueToObject(cx
, argv
[1], &parent
))
2349 parent
= JS_GetParent(cx
, funobj
);
2351 clone
= JS_CloneFunctionObject(cx
, funobj
, parent
);
2354 *rval
= OBJECT_TO_JSVAL(clone
);
2359 Seal(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
2362 JSBool deep
= JS_FALSE
;
2364 if (!JS_ConvertArguments(cx
, argc
, argv
, "o/b", &target
, &deep
))
2368 return JS_SealObject(cx
, target
, deep
);
2372 GetPDA(JSContext
*cx
, uintN argc
, jsval
*vp
)
2374 JSObject
*vobj
, *aobj
, *pdobj
;
2376 JSPropertyDescArray pda
;
2381 if (!JS_ValueToObject(cx
, argc
== 0 ? JSVAL_VOID
: vp
[2], &vobj
))
2388 aobj
= JS_NewArrayObject(cx
, 0, NULL
);
2391 *vp
= OBJECT_TO_JSVAL(aobj
);
2393 ok
= JS_GetPropertyDescArray(cx
, vobj
, &pda
);
2397 for (i
= 0; i
< pda
.length
; i
++, pd
++) {
2398 pdobj
= JS_NewObject(cx
, NULL
, NULL
, NULL
);
2404 /* Protect pdobj from GC by setting it as an element of aobj now */
2405 v
= OBJECT_TO_JSVAL(pdobj
);
2406 ok
= JS_SetElement(cx
, aobj
, i
, &v
);
2410 ok
= JS_SetProperty(cx
, pdobj
, "id", &pd
->id
) &&
2411 JS_SetProperty(cx
, pdobj
, "value", &pd
->value
) &&
2412 (v
= INT_TO_JSVAL(pd
->flags
),
2413 JS_SetProperty(cx
, pdobj
, "flags", &v
)) &&
2414 (v
= INT_TO_JSVAL(pd
->slot
),
2415 JS_SetProperty(cx
, pdobj
, "slot", &v
)) &&
2416 JS_SetProperty(cx
, pdobj
, "alias", &pd
->alias
);
2420 JS_PutPropertyDescArray(cx
, &pda
);
2425 GetSLX(JSContext
*cx
, uintN argc
, jsval
*vp
)
2429 script
= ValueToScript(cx
, argc
== 0 ? JSVAL_VOID
: vp
[2]);
2432 *vp
= INT_TO_JSVAL(js_GetScriptLineExtent(script
));
2437 ToInt32(JSContext
*cx
, uintN argc
, jsval
*vp
)
2441 if (!JS_ValueToInt32(cx
, argc
== 0 ? JSVAL_VOID
: vp
[2], &i
))
2443 return JS_NewNumberValue(cx
, i
, vp
);
2447 StringsAreUTF8(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
,
2450 *rval
= JS_CStringsAreUTF8() ? JSVAL_TRUE
: JSVAL_FALSE
;
2455 StackQuota(JSContext
*cx
, uintN argc
, jsval
*vp
)
2460 return JS_NewNumberValue(cx
, (double) gScriptStackQuota
, vp
);
2461 if (!JS_ValueToECMAUint32(cx
, JS_ARGV(cx
, vp
)[0], &n
))
2463 gScriptStackQuota
= n
;
2464 JS_SetScriptStackQuota(cx
, gScriptStackQuota
);
2465 JS_SET_RVAL(cx
, vp
, JSVAL_VOID
);
2469 static const char* badUTF8
= "...\xC0...";
2470 static const char* bigUTF8
= "...\xFB\xBF\xBF\xBF\xBF...";
2471 static const jschar badSurrogate
[] = { 'A', 'B', 'C', 0xDEEE, 'D', 'E', 0 };
2474 TestUTF8(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
2478 size_t charsLength
= 5;
2480 size_t bytesLength
= 20;
2481 if (argc
&& !JS_ValueToInt32(cx
, *argv
, &mode
))
2484 /* The following throw errors if compiled with UTF-8. */
2486 /* mode 1: malformed UTF-8 string. */
2488 JS_NewStringCopyZ(cx
, badUTF8
);
2490 /* mode 2: big UTF-8 character. */
2492 JS_NewStringCopyZ(cx
, bigUTF8
);
2494 /* mode 3: bad surrogate character. */
2496 JS_EncodeCharacters(cx
, badSurrogate
, 6, bytes
, &bytesLength
);
2498 /* mode 4: use a too small buffer. */
2500 JS_DecodeBytes(cx
, "1234567890", 10, chars
, &charsLength
);
2503 JS_ReportError(cx
, "invalid mode parameter");
2506 return !JS_IsExceptionPending (cx
);
2510 ThrowError(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
2512 JS_ReportError(cx
, "This is an error");
2516 #define LAZY_STANDARD_CLASSES
2518 /* A class for easily testing the inner/outer object callbacks. */
2519 typedef struct ComplexObject
{
2527 split_create_outer(JSContext
*cx
);
2530 split_create_inner(JSContext
*cx
, JSObject
*outer
);
2532 static ComplexObject
*
2533 split_get_private(JSContext
*cx
, JSObject
*obj
);
2536 split_addProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
2540 cpx
= split_get_private(cx
, obj
);
2543 if (!cpx
->isInner
&& cpx
->inner
) {
2544 /* Make sure to define this property on the inner object. */
2545 return JS_DefinePropertyById(cx
, cpx
->inner
, id
, *vp
, NULL
, NULL
, JSPROP_ENUMERATE
);
2551 split_getProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
2555 cpx
= split_get_private(cx
, obj
);
2559 if (JSID_IS_ATOM(id
) &&
2560 !strcmp(JS_GetStringBytes(JSID_TO_STRING(id
)), "isInner")) {
2561 *vp
= BOOLEAN_TO_JSVAL(cpx
->isInner
);
2565 if (!cpx
->isInner
&& cpx
->inner
) {
2566 if (JSID_IS_ATOM(id
)) {
2569 str
= JSID_TO_STRING(id
);
2570 return JS_GetUCProperty(cx
, cpx
->inner
, JS_GetStringChars(str
),
2571 JS_GetStringLength(str
), vp
);
2573 if (JSID_IS_INT(id
))
2574 return JS_GetElement(cx
, cpx
->inner
, JSID_TO_INT(id
), vp
);
2582 split_setProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
2586 cpx
= split_get_private(cx
, obj
);
2589 if (!cpx
->isInner
&& cpx
->inner
) {
2590 if (JSID_IS_ATOM(id
)) {
2593 str
= JSID_TO_STRING(id
);
2594 return JS_SetUCProperty(cx
, cpx
->inner
, JS_GetStringChars(str
),
2595 JS_GetStringLength(str
), vp
);
2597 if (JSID_IS_INT(id
))
2598 return JS_SetElement(cx
, cpx
->inner
, JSID_TO_INT(id
), vp
);
2606 split_delProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
2611 cpx
= split_get_private(cx
, obj
);
2614 if (!cpx
->isInner
&& cpx
->inner
) {
2615 /* Make sure to define this property on the inner object. */
2616 if (!JS_ValueToId(cx
, *vp
, &asId
))
2618 return cpx
->inner
->deleteProperty(cx
, asId
, Valueify(vp
));
2624 split_enumerate(JSContext
*cx
, JSObject
*obj
, JSIterateOp enum_op
,
2625 jsval
*statep
, jsid
*idp
)
2631 case JSENUMERATE_INIT
:
2632 case JSENUMERATE_INIT_ALL
:
2633 cpx
= (ComplexObject
*) JS_GetPrivate(cx
, obj
);
2635 if (!cpx
->isInner
&& cpx
->inner
)
2638 iterator
= JS_NewPropertyIterator(cx
, obj
);
2642 *statep
= OBJECT_TO_JSVAL(iterator
);
2644 *idp
= INT_TO_JSID(0);
2647 case JSENUMERATE_NEXT
:
2648 iterator
= (JSObject
*)JSVAL_TO_OBJECT(*statep
);
2649 if (!JS_NextProperty(cx
, iterator
, idp
))
2652 if (!JSID_IS_VOID(*idp
))
2656 case JSENUMERATE_DESTROY
:
2657 /* Let GC at our iterator object. */
2658 *statep
= JSVAL_NULL
;
2666 split_resolve(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN flags
, JSObject
**objp
)
2670 if (JSID_IS_ATOM(id
) &&
2671 !strcmp(JS_GetStringBytes(JSID_TO_STRING(id
)), "isInner")) {
2673 return JS_DefineProperty(cx
, obj
, "isInner", JSVAL_VOID
, NULL
, NULL
,
2677 cpx
= split_get_private(cx
, obj
);
2680 if (!cpx
->isInner
&& cpx
->inner
) {
2683 if (!cpx
->inner
->lookupProperty(cx
, id
, objp
, &prop
))
2686 cpx
->inner
->dropProperty(cx
, prop
);
2691 #ifdef LAZY_STANDARD_CLASSES
2692 if (!(flags
& JSRESOLVE_ASSIGNING
)) {
2695 if (!JS_ResolveStandardClass(cx
, obj
, id
, &resolved
))
2705 /* XXX For additional realism, let's resolve some random property here. */
2710 split_finalize(JSContext
*cx
, JSObject
*obj
)
2712 JS_free(cx
, JS_GetPrivate(cx
, obj
));
2716 split_mark(JSContext
*cx
, JSObject
*obj
, void *arg
)
2720 cpx
= (ComplexObject
*) JS_GetPrivate(cx
, obj
);
2722 if (!cpx
->isInner
&& cpx
->inner
) {
2723 /* Mark the inner object. */
2724 JS_MarkGCThing(cx
, OBJECT_TO_JSVAL(cpx
->inner
), "ComplexObject.inner", arg
);
2731 split_outerObject(JSContext
*cx
, JSObject
*obj
)
2735 cpx
= (ComplexObject
*) JS_GetPrivate(cx
, obj
);
2736 return cpx
->isInner
? cpx
->outer
: obj
;
2740 split_thisObject(JSContext
*cx
, JSObject
*obj
)
2742 OBJ_TO_OUTER_OBJECT(cx
, obj
);
2748 static JSObjectOps split_objectops
;
2750 static JSObjectOps
*
2751 split_getObjectOps(JSContext
*cx
, JSClass
*clasp
)
2753 if (!split_objectops
.thisObject
) {
2754 memcpy(&split_objectops
, &js_ObjectOps
, sizeof split_objectops
);
2755 split_objectops
.thisObject
= split_thisObject
;
2758 return &split_objectops
;
2762 split_equality(JSContext
*cx
, JSObject
*obj
, const jsval
*v
, JSBool
*bp
);
2765 split_innerObject(JSContext
*cx
, JSObject
*obj
)
2769 cpx
= (ComplexObject
*) JS_GetPrivate(cx
, obj
);
2771 JS_ASSERT(!cpx
->isInner
);
2774 return !cpx
->isInner
? cpx
->inner
: obj
;
2777 static JSExtendedClass split_global_class
= {
2779 JSCLASS_NEW_RESOLVE
| JSCLASS_NEW_ENUMERATE
| JSCLASS_HAS_PRIVATE
|
2780 JSCLASS_GLOBAL_FLAGS
| JSCLASS_IS_EXTENDED
,
2781 split_addProperty
, split_delProperty
,
2782 split_getProperty
, split_setProperty
,
2783 (JSEnumerateOp
)split_enumerate
,
2784 (JSResolveOp
)split_resolve
,
2785 JS_ConvertStub
, split_finalize
,
2786 split_getObjectOps
, NULL
, NULL
, NULL
, NULL
, NULL
,
2788 split_equality
, split_outerObject
, split_innerObject
,
2789 NULL
, NULL
, NULL
, NULL
, NULL
2793 split_equality(JSContext
*cx
, JSObject
*obj
, const jsval
*v
, JSBool
*bp
)
2796 if (JSVAL_IS_PRIMITIVE(*v
))
2799 JSObject
*obj2
= JSVAL_TO_OBJECT(*v
);
2800 if (JS_GET_CLASS(cx
, obj2
) != &split_global_class
.base
)
2803 ComplexObject
*cpx
= (ComplexObject
*) JS_GetPrivate(cx
, obj2
);
2804 JS_ASSERT(!cpx
->isInner
);
2806 ComplexObject
*ourCpx
= (ComplexObject
*) JS_GetPrivate(cx
, obj
);
2807 JS_ASSERT(!ourCpx
->isInner
);
2809 *bp
= (cpx
== ourCpx
);
2814 split_create_outer(JSContext
*cx
)
2819 cpx
= (ComplexObject
*) JS_malloc(cx
, sizeof *obj
);
2822 cpx
->isInner
= JS_FALSE
;
2823 cpx
->frozen
= JS_TRUE
;
2827 obj
= JS_NewGlobalObject(cx
, &split_global_class
.base
);
2828 if (!obj
|| !JS_SetParent(cx
, obj
, NULL
)) {
2833 if (!JS_SetPrivate(cx
, obj
, cpx
)) {
2842 split_create_inner(JSContext
*cx
, JSObject
*outer
)
2844 ComplexObject
*cpx
, *outercpx
;
2847 JS_ASSERT(JS_GET_CLASS(cx
, outer
) == &split_global_class
.base
);
2849 cpx
= (ComplexObject
*) JS_malloc(cx
, sizeof *cpx
);
2852 cpx
->isInner
= JS_TRUE
;
2853 cpx
->frozen
= JS_FALSE
;
2857 obj
= JS_NewGlobalObject(cx
, &split_global_class
.base
);
2858 if (!obj
|| !JS_SetParent(cx
, obj
, NULL
) || !JS_SetPrivate(cx
, obj
, cpx
)) {
2863 outercpx
= (ComplexObject
*) JS_GetPrivate(cx
, outer
);
2864 outercpx
->inner
= obj
;
2865 outercpx
->frozen
= JS_FALSE
;
2870 static ComplexObject
*
2871 split_get_private(JSContext
*cx
, JSObject
*obj
)
2874 if (JS_GET_CLASS(cx
, obj
) == &split_global_class
.base
)
2875 return (ComplexObject
*) JS_GetPrivate(cx
, obj
);
2876 obj
= JS_GetParent(cx
, obj
);
2883 sandbox_enumerate(JSContext
*cx
, JSObject
*obj
)
2888 if (!JS_GetProperty(cx
, obj
, "lazy", &v
))
2891 JS_ValueToBoolean(cx
, v
, &b
);
2892 return !b
|| JS_EnumerateStandardClasses(cx
, obj
);
2896 sandbox_resolve(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN flags
,
2902 if (!JS_GetProperty(cx
, obj
, "lazy", &v
))
2905 JS_ValueToBoolean(cx
, v
, &b
);
2906 if (b
&& (flags
& JSRESOLVE_ASSIGNING
) == 0) {
2907 if (!JS_ResolveStandardClass(cx
, obj
, id
, &resolved
))
2918 static JSClass sandbox_class
= {
2920 JSCLASS_NEW_RESOLVE
| JSCLASS_GLOBAL_FLAGS
,
2921 JS_PropertyStub
, JS_PropertyStub
,
2922 JS_PropertyStub
, JS_PropertyStub
,
2923 sandbox_enumerate
, (JSResolveOp
)sandbox_resolve
,
2924 JS_ConvertStub
, NULL
,
2925 JSCLASS_NO_OPTIONAL_MEMBERS
2929 NewSandbox(JSContext
*cx
, bool lazy
, bool split
)
2931 JSObject
*obj
= JS_NewCompartmentAndGlobalObject(cx
, &sandbox_class
, NULL
);
2936 JSAutoCrossCompartmentCall ac
;
2937 if (!ac
.enter(cx
, obj
))
2941 obj
= split_setup(cx
, JS_TRUE
);
2945 if (!lazy
&& !JS_InitStandardClasses(cx
, obj
))
2948 AutoValueRooter
root(cx
, BooleanValue(lazy
));
2949 if (!JS_SetProperty(cx
, obj
, "lazy", root
.jsval_addr()))
2953 obj
= split_outerObject(cx
, obj
);
2956 AutoObjectRooter
objroot(cx
, obj
);
2957 if (!cx
->compartment
->wrap(cx
, objroot
.addr()))
2959 return objroot
.object();
2963 EvalInContext(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
,
2967 JSObject
*sobj
= NULL
;
2968 if (!JS_ConvertArguments(cx
, argc
, argv
, "S / o", &str
, &sobj
))
2971 const jschar
*src
= JS_GetStringChars(str
);
2972 size_t srclen
= JS_GetStringLength(str
);
2973 bool split
= false, lazy
= false;
2975 if (src
[0] == 'l' && src
[1] == 'a' && src
[2] == 'z' && src
[3] == 'y') {
2979 } else if (srclen
== 5) {
2980 if (src
[0] == 's' && src
[1] == 'p' && src
[2] == 'l' && src
[3] == 'i' && src
[4] == 't') {
2981 split
= lazy
= true;
2987 sobj
= NewSandbox(cx
, lazy
, split
);
2992 *rval
= OBJECT_TO_JSVAL(sobj
);
2996 JSStackFrame
*fp
= JS_GetScriptedCaller(cx
, NULL
);
2998 JSAutoCrossCompartmentCall ac
;
2999 if (JSCrossCompartmentWrapper::isCrossCompartmentWrapper(sobj
)) {
3000 sobj
= sobj
->unwrap();
3001 if (!ac
.enter(cx
, sobj
))
3005 OBJ_TO_INNER_OBJECT(cx
, sobj
);
3008 if (!(sobj
->getClass()->flags
& JSCLASS_IS_GLOBAL
)) {
3009 JS_ReportError(cx
, "Invalid scope argument to evalcx");
3012 if (!JS_EvaluateUCScript(cx
, sobj
, src
, srclen
,
3013 fp
->script
->filename
,
3014 JS_PCToLineNumber(cx
, fp
->script
, fp
->pc(cx
)),
3019 return cx
->compartment
->wrap(cx
, Valueify(rval
));
3023 EvalInFrame(JSContext
*cx
, uintN argc
, jsval
*vp
)
3025 jsval
*argv
= JS_ARGV(cx
, vp
);
3027 !JSVAL_IS_INT(argv
[0]) ||
3028 !JSVAL_IS_STRING(argv
[1])) {
3029 JS_ReportError(cx
, "Invalid arguments to evalInFrame");
3033 uint32 upCount
= JSVAL_TO_INT(argv
[0]);
3034 JSString
*str
= JSVAL_TO_STRING(argv
[1]);
3036 bool saveCurrent
= (argc
>= 3 && JSVAL_IS_BOOLEAN(argv
[2]))
3037 ? !!(JSVAL_TO_BOOLEAN(argv
[2]))
3042 FrameRegsIter
fi(cx
);
3043 for (uint32 i
= 0; i
< upCount
; ++i
, ++fi
) {
3048 JSStackFrame
*const fp
= fi
.fp();
3050 JS_ReportError(cx
, "cannot eval in non-script frame");
3054 JSStackFrame
*oldfp
= NULL
;
3056 oldfp
= JS_SaveFrameChain(cx
);
3058 JSBool ok
= JS_EvaluateUCInStackFrame(cx
, fp
, str
->chars(), str
->length(),
3059 fp
->script
->filename
,
3060 JS_PCToLineNumber(cx
, fp
->script
,
3065 JS_RestoreFrameChain(cx
, oldfp
);
3071 ShapeOf(JSContext
*cx
, uintN argc
, jsval
*vp
)
3073 jsval v
= JS_ARGV(cx
, vp
)[0];
3074 if (!JSVAL_IS_OBJECT(v
)) {
3075 JS_ReportError(cx
, "shapeOf: object expected");
3078 JSObject
*obj
= JSVAL_TO_OBJECT(v
);
3083 if (!obj
->isNative()) {
3084 *vp
= INT_TO_JSVAL(-1);
3087 return JS_NewNumberValue(cx
, obj
->shape(), vp
);
3090 #ifdef JS_THREADSAFE
3093 * Check that t1 comes strictly before t2. The function correctly deals with
3094 * PRIntervalTime wrap-around between t2 and t1 assuming that t2 and t1 stays
3095 * within INT32_MAX from each other. We use MAX_TIMEOUT_INTERVAL to enforce
3099 IsBefore(PRIntervalTime t1
, PRIntervalTime t2
)
3101 return int32(t1
- t2
) < 0;
3105 Sleep_fn(JSContext
*cx
, uintN argc
, jsval
*vp
)
3107 PRIntervalTime t_ticks
;
3114 if (!JS_ValueToNumber(cx
, argc
== 0 ? JSVAL_VOID
: vp
[2], &t_secs
))
3117 /* NB: The next condition also filter out NaNs. */
3118 if (!(t_secs
<= MAX_TIMEOUT_INTERVAL
)) {
3119 JS_ReportError(cx
, "Excessive sleep interval");
3122 t_ticks
= (t_secs
<= 0.0)
3124 : PRIntervalTime(PR_TicksPerSecond() * t_secs
);
3127 JS_YieldRequest(cx
);
3129 JSAutoSuspendRequest
suspended(cx
);
3130 PR_Lock(gWatchdogLock
);
3131 PRIntervalTime to_wakeup
= PR_IntervalNow() + t_ticks
;
3133 PR_WaitCondVar(gSleepWakeup
, t_ticks
);
3136 PRIntervalTime now
= PR_IntervalNow();
3137 if (!IsBefore(now
, to_wakeup
))
3139 t_ticks
= to_wakeup
- now
;
3141 PR_Unlock(gWatchdogLock
);
3146 typedef struct ScatterThreadData ScatterThreadData
;
3147 typedef struct ScatterData ScatterData
;
3149 typedef enum ScatterStatus
{
3155 struct ScatterData
{
3156 ScatterThreadData
*threads
;
3160 ScatterStatus status
;
3163 struct ScatterThreadData
{
3165 ScatterData
*shared
;
3172 DoScatteredWork(JSContext
*cx
, ScatterThreadData
*td
)
3174 jsval
*rval
= &td
->shared
->results
[td
->index
];
3176 if (!JS_CallFunctionValue(cx
, NULL
, td
->fn
, 0, NULL
, rval
)) {
3178 JS_GetPendingException(cx
, rval
);
3179 JS_ClearPendingException(cx
);
3184 RunScatterThread(void *arg
)
3187 ScatterThreadData
*td
;
3191 if (PR_FAILURE
== PR_SetThreadPrivate(gStackBaseThreadIndex
, &stackDummy
))
3194 td
= (ScatterThreadData
*)arg
;
3197 /* Wait for our signal. */
3198 PR_Lock(td
->shared
->lock
);
3199 while ((st
= td
->shared
->status
) == SCATTER_WAIT
)
3200 PR_WaitCondVar(td
->shared
->cvar
, PR_INTERVAL_NO_TIMEOUT
);
3201 PR_Unlock(td
->shared
->lock
);
3203 if (st
== SCATTER_CANCEL
)
3206 /* We are good to go. */
3207 JS_SetContextThread(cx
);
3208 JS_SetNativeStackQuota(cx
, gMaxStackSize
);
3209 JS_BeginRequest(cx
);
3210 DoScatteredWork(cx
, td
);
3212 JS_ClearContextThread(cx
);
3216 * scatter(fnArray) - Call each function in `fnArray` without arguments, each
3217 * in a different thread. When all threads have finished, return an array: the
3218 * return values. Errors are not propagated; if any of the function calls
3219 * fails, the corresponding element in the results array gets the exception
3220 * object, if any, else (undefined).
3223 Scatter(JSContext
*cx
, uintN argc
, jsval
*vp
)
3226 jsuint n
; /* number of threads */
3237 sd
.status
= SCATTER_WAIT
;
3239 if (argc
== 0 || JSVAL_IS_PRIMITIVE(JS_ARGV(cx
, vp
)[0])) {
3240 JS_ReportError(cx
, "the first argument must be an object");
3244 inArr
= JSVAL_TO_OBJECT(JS_ARGV(cx
, vp
)[0]);
3245 ok
= JS_GetArrayLength(cx
, inArr
, &n
);
3251 sd
.lock
= PR_NewLock();
3255 sd
.cvar
= PR_NewCondVar(sd
.lock
);
3259 sd
.results
= (jsval
*) malloc(n
* sizeof(jsval
));
3262 for (i
= 0; i
< n
; i
++) {
3263 sd
.results
[i
] = JSVAL_VOID
;
3264 ok
= JS_AddValueRoot(cx
, &sd
.results
[i
]);
3267 JS_RemoveValueRoot(cx
, &sd
.results
[i
]);
3274 sd
.threads
= (ScatterThreadData
*) malloc(n
* sizeof(ScatterThreadData
));
3277 for (i
= 0; i
< n
; i
++) {
3278 sd
.threads
[i
].index
= i
;
3279 sd
.threads
[i
].shared
= &sd
;
3280 sd
.threads
[i
].thr
= NULL
;
3281 sd
.threads
[i
].cx
= NULL
;
3282 sd
.threads
[i
].fn
= JSVAL_NULL
;
3284 ok
= JS_AddValueRoot(cx
, &sd
.threads
[i
].fn
);
3285 if (ok
&& !JS_GetElement(cx
, inArr
, (jsint
) i
, &sd
.threads
[i
].fn
)) {
3286 JS_RemoveValueRoot(cx
, &sd
.threads
[i
].fn
);
3291 JS_RemoveValueRoot(cx
, &sd
.threads
[i
].fn
);
3298 global
= JS_GetGlobalObject(cx
);
3299 for (i
= 1; i
< n
; i
++) {
3300 JSContext
*newcx
= NewContext(JS_GetRuntime(cx
));
3305 JSAutoRequest
req(newcx
);
3306 JS_SetGlobalObject(newcx
, global
);
3308 JS_ClearContextThread(newcx
);
3309 sd
.threads
[i
].cx
= newcx
;
3312 for (i
= 1; i
< n
; i
++) {
3313 PRThread
*t
= PR_CreateThread(PR_USER_THREAD
,
3321 /* Failed to start thread. */
3323 sd
.status
= SCATTER_CANCEL
;
3324 PR_NotifyAllCondVar(sd
.cvar
);
3327 PR_JoinThread(sd
.threads
[i
].thr
);
3331 sd
.threads
[i
].thr
= t
;
3334 sd
.status
= SCATTER_GO
;
3335 PR_NotifyAllCondVar(sd
.cvar
);
3338 DoScatteredWork(cx
, &sd
.threads
[0]);
3341 JSAutoSuspendRequest
suspended(cx
);
3342 for (i
= 1; i
< n
; i
++) {
3343 PR_JoinThread(sd
.threads
[i
].thr
);
3348 arr
= JS_NewArrayObject(cx
, n
, sd
.results
);
3351 *vp
= OBJECT_TO_JSVAL(arr
);
3358 for (i
= 0; i
< n
; i
++) {
3359 JS_RemoveValueRoot(cx
, &sd
.threads
[i
].fn
);
3360 acx
= sd
.threads
[i
].cx
;
3362 JS_SetContextThread(acx
);
3363 DestroyContext(acx
, true);
3369 for (i
= 0; i
< n
; i
++)
3370 JS_RemoveValueRoot(cx
, &sd
.results
[i
]);
3374 PR_DestroyCondVar(sd
.cvar
);
3376 PR_DestroyLock(sd
.lock
);
3386 InitWatchdog(JSRuntime
*rt
)
3388 JS_ASSERT(!gWatchdogThread
);
3389 gWatchdogLock
= PR_NewLock();
3390 if (gWatchdogLock
) {
3391 gWatchdogWakeup
= PR_NewCondVar(gWatchdogLock
);
3392 if (gWatchdogWakeup
) {
3393 gSleepWakeup
= PR_NewCondVar(gWatchdogLock
);
3396 PR_DestroyCondVar(gWatchdogWakeup
);
3398 PR_DestroyLock(gWatchdogLock
);
3408 PR_Lock(gWatchdogLock
);
3409 thread
= gWatchdogThread
;
3412 * The watchdog thread is running, tell it to terminate waking it up
3415 gWatchdogThread
= NULL
;
3416 PR_NotifyCondVar(gWatchdogWakeup
);
3418 PR_Unlock(gWatchdogLock
);
3420 PR_JoinThread(thread
);
3421 PR_DestroyCondVar(gSleepWakeup
);
3422 PR_DestroyCondVar(gWatchdogWakeup
);
3423 PR_DestroyLock(gWatchdogLock
);
3427 WatchdogMain(void *arg
)
3429 JSRuntime
*rt
= (JSRuntime
*) arg
;
3431 PR_Lock(gWatchdogLock
);
3432 while (gWatchdogThread
) {
3433 PRIntervalTime now
= PR_IntervalNow();
3434 if (gWatchdogHasTimeout
&& !IsBefore(now
, gWatchdogTimeout
)) {
3436 * The timeout has just expired. Trigger the operation callback
3439 gWatchdogHasTimeout
= false;
3440 PR_Unlock(gWatchdogLock
);
3441 CancelExecution(rt
);
3442 PR_Lock(gWatchdogLock
);
3444 /* Wake up any threads doing sleep. */
3445 PR_NotifyAllCondVar(gSleepWakeup
);
3447 PRIntervalTime sleepDuration
= gWatchdogHasTimeout
3448 ? gWatchdogTimeout
- now
3449 : PR_INTERVAL_NO_TIMEOUT
;
3453 PR_WaitCondVar(gWatchdogWakeup
, sleepDuration
);
3454 JS_ASSERT(status
== PR_SUCCESS
);
3457 PR_Unlock(gWatchdogLock
);
3461 ScheduleWatchdog(JSRuntime
*rt
, jsdouble t
)
3464 PR_Lock(gWatchdogLock
);
3465 gWatchdogHasTimeout
= false;
3466 PR_Unlock(gWatchdogLock
);
3470 PRIntervalTime interval
= PRIntervalTime(ceil(t
* PR_TicksPerSecond()));
3471 PRIntervalTime timeout
= PR_IntervalNow() + interval
;
3472 PR_Lock(gWatchdogLock
);
3473 if (!gWatchdogThread
) {
3474 JS_ASSERT(!gWatchdogHasTimeout
);
3475 gWatchdogThread
= PR_CreateThread(PR_USER_THREAD
,
3482 if (!gWatchdogThread
) {
3483 PR_Unlock(gWatchdogLock
);
3486 } else if (!gWatchdogHasTimeout
|| IsBefore(timeout
, gWatchdogTimeout
)) {
3487 PR_NotifyCondVar(gWatchdogWakeup
);
3489 gWatchdogHasTimeout
= true;
3490 gWatchdogTimeout
= timeout
;
3491 PR_Unlock(gWatchdogLock
);
3495 #else /* !JS_THREADSAFE */
3498 static HANDLE gTimerHandle
= 0;
3501 TimerCallback(PVOID lpParameter
, BOOLEAN TimerOrWaitFired
)
3503 CancelExecution((JSRuntime
*) lpParameter
);
3509 AlarmHandler(int sig
)
3511 CancelExecution(gRuntime
);
3517 InitWatchdog(JSRuntime
*rt
)
3526 ScheduleWatchdog(gRuntime
, -1);
3530 ScheduleWatchdog(JSRuntime
*rt
, jsdouble t
)
3534 DeleteTimerQueueTimer(NULL
, gTimerHandle
, NULL
);
3538 !CreateTimerQueueTimer(&gTimerHandle
,
3540 (WAITORTIMERCALLBACK
)TimerCallback
,
3542 DWORD(ceil(t
* 1000.0)),
3544 WT_EXECUTEINTIMERTHREAD
| WT_EXECUTEONLYONCE
)) {
3549 /* FIXME: use setitimer when available for sub-second resolution. */
3552 signal(SIGALRM
, NULL
);
3554 signal(SIGALRM
, AlarmHandler
); /* set the Alarm signal capture */
3561 #endif /* !JS_THREADSAFE */
3564 CancelExecution(JSRuntime
*rt
)
3568 gExitCode
= EXITCODE_TIMEOUT
;
3569 #ifdef JS_THREADSAFE
3571 JSContext
*cx
= JS_NewContext(rt
, 8192);
3573 js::workers::terminateAll(cx
, gWorkers
);
3574 JS_DestroyContextNoGC(cx
);
3578 JS_TriggerAllOperationCallbacks(rt
);
3580 static const char msg
[] = "Script runs for too long, terminating.\n";
3581 #if defined(XP_UNIX) && !defined(JS_THREADSAFE)
3582 /* It is not safe to call fputs from signals. */
3583 /* Dummy assignment avoids GCC warning on "attribute warn_unused_result" */
3584 ssize_t dummy
= write(2, msg
, sizeof(msg
) - 1);
3592 SetTimeoutValue(JSContext
*cx
, jsdouble t
)
3594 /* NB: The next condition also filter out NaNs. */
3595 if (!(t
<= MAX_TIMEOUT_INTERVAL
)) {
3596 JS_ReportError(cx
, "Excessive timeout value");
3599 gTimeoutInterval
= t
;
3600 if (!ScheduleWatchdog(cx
->runtime
, t
)) {
3601 JS_ReportError(cx
, "Failed to create the watchdog");
3608 Timeout(JSContext
*cx
, uintN argc
, jsval
*vp
)
3611 return JS_NewNumberValue(cx
, gTimeoutInterval
, vp
);
3614 JS_ReportError(cx
, "Wrong number of arguments");
3619 if (!JS_ValueToNumber(cx
, JS_ARGV(cx
, vp
)[0], &t
))
3623 return SetTimeoutValue(cx
, t
);
3627 Elapsed(JSContext
*cx
, uintN argc
, jsval
*vp
)
3631 JSShellContextData
*data
= GetContextData(cx
);
3633 d
= js_IntervalNow() - data
->startTime
;
3634 return JS_NewNumberValue(cx
, d
, vp
);
3636 JS_ReportError(cx
, "Wrong number of arguments");
3641 Parent(JSContext
*cx
, uintN argc
, jsval
*vp
)
3644 JS_ReportError(cx
, "Wrong number of arguments");
3648 jsval v
= JS_ARGV(cx
, vp
)[0];
3649 if (JSVAL_IS_PRIMITIVE(v
)) {
3650 JS_ReportError(cx
, "Only objects have parents!");
3654 JSObject
*parent
= JS_GetParent(cx
, JSVAL_TO_OBJECT(v
));
3655 *vp
= OBJECT_TO_JSVAL(parent
);
3657 /* Outerize if necessary. Embrace the ugliness! */
3659 JSClass
*clasp
= JS_GET_CLASS(cx
, parent
);
3660 if (clasp
->flags
& JSCLASS_IS_EXTENDED
) {
3661 JSExtendedClass
*xclasp
= reinterpret_cast<JSExtendedClass
*>(clasp
);
3662 if (JSObjectOp outerize
= xclasp
->outerObject
)
3663 *vp
= OBJECT_TO_JSVAL(outerize(cx
, parent
));
3673 #include <sys/stat.h>
3676 * Returns a JS_malloc'd string (that the caller needs to JS_free)
3677 * containing the directory (non-leaf) part of |from| prepended to |leaf|.
3678 * If |from| is empty or a leaf, MakeAbsolutePathname returns a copy of leaf.
3679 * Returns NULL to indicate an error.
3682 MakeAbsolutePathname(JSContext
*cx
, const char *from
, const char *leaf
)
3686 const char *slash
= NULL
, *cp
;
3698 /* We were given a leaf or |from| was empty. */
3699 return JS_strdup(cx
, leaf
);
3702 /* Else, we were given a real pathname, return that + the leaf. */
3703 dirlen
= slash
- from
+ 1;
3704 dir
= (char*) JS_malloc(cx
, dirlen
+ strlen(leaf
) + 1);
3708 strncpy(dir
, from
, dirlen
);
3709 strcpy(dir
+ dirlen
, leaf
); /* Note: we can't use strcat here. */
3717 Compile(JSContext
*cx
, uintN argc
, jsval
*vp
)
3720 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_MORE_ARGS_NEEDED
,
3721 "compile", "0", "s");
3724 jsval arg0
= JS_ARGV(cx
, vp
)[0];
3725 if (!JSVAL_IS_STRING(arg0
)) {
3726 const char *typeName
= JS_GetTypeName(cx
, JS_TypeOfValue(cx
, arg0
));
3727 JS_ReportError(cx
, "expected string to compile, got %s", typeName
);
3731 JSString
*scriptContents
= JSVAL_TO_STRING(arg0
);
3732 JSScript
*result
= JS_CompileUCScript(cx
, NULL
, JS_GetStringCharsZ(cx
, scriptContents
),
3733 JS_GetStringLength(scriptContents
), "<string>", 0);
3737 JS_DestroyScript(cx
, result
);
3738 JS_SET_RVAL(cx
, vp
, JSVAL_VOID
);
3743 Parse(JSContext
*cx
, uintN argc
, jsval
*vp
)
3746 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_MORE_ARGS_NEEDED
,
3747 "compile", "0", "s");
3750 jsval arg0
= JS_ARGV(cx
, vp
)[0];
3751 if (!JSVAL_IS_STRING(arg0
)) {
3752 const char *typeName
= JS_GetTypeName(cx
, JS_TypeOfValue(cx
, arg0
));
3753 JS_ReportError(cx
, "expected string to parse, got %s", typeName
);
3757 JSString
*scriptContents
= JSVAL_TO_STRING(arg0
);
3758 js::Parser
parser(cx
);
3759 parser
.init(JS_GetStringCharsZ(cx
, scriptContents
), JS_GetStringLength(scriptContents
),
3760 NULL
, "<string>", 0);
3761 if (!parser
.parse(NULL
))
3763 JS_SET_RVAL(cx
, vp
, JSVAL_VOID
);
3768 Snarf(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
3771 const char *filename
;
3772 const char *pathname
;
3779 str
= JS_ValueToString(cx
, argv
[0]);
3782 filename
= JS_GetStringBytes(str
);
3784 /* Get the currently executing script's name. */
3785 fp
= JS_GetScriptedCaller(cx
, NULL
);
3786 JS_ASSERT(fp
&& fp
->script
->filename
);
3788 pathname
= MakeAbsolutePathname(cx
, fp
->script
->filename
, filename
);
3792 pathname
= filename
;
3798 file
= fopen(pathname
, "rb");
3800 JS_ReportError(cx
, "can't open %s: %s", pathname
, strerror(errno
));
3802 if (fseek(file
, 0, SEEK_END
) == EOF
) {
3803 JS_ReportError(cx
, "can't seek end of %s", pathname
);
3806 if (fseek(file
, 0, SEEK_SET
) == EOF
) {
3807 JS_ReportError(cx
, "can't seek start of %s", pathname
);
3809 buf
= (char*) JS_malloc(cx
, len
+ 1);
3811 cc
= fread(buf
, 1, len
, file
);
3813 JS_ReportError(cx
, "can't read %s: %s", pathname
,
3814 (ptrdiff_t(cc
) < 0) ? strerror(errno
) : "short read");
3824 JS_free(cx
, (void*)pathname
);
3831 str
= JS_NewString(cx
, buf
, len
);
3836 *rval
= STRING_TO_JSVAL(str
);
3841 Wrap(JSContext
*cx
, uintN argc
, jsval
*vp
)
3843 jsval v
= argc
> 0 ? JS_ARGV(cx
, vp
)[0] : JSVAL_VOID
;
3844 if (JSVAL_IS_PRIMITIVE(v
)) {
3845 JS_SET_RVAL(cx
, vp
, v
);
3849 JSObject
*obj
= JSVAL_TO_OBJECT(v
);
3850 JSObject
*wrapped
= JSWrapper::New(cx
, obj
, obj
->getProto(), obj
->getParent(),
3851 &JSWrapper::singleton
);
3855 JS_SET_RVAL(cx
, vp
, OBJECT_TO_JSVAL(wrapped
));
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),
3943 JS_FN("wrap", Wrap
, 1,0),
3947 static const char shell_help_header
[] =
3948 "Command Description\n"
3949 "======= ===========\n";
3951 static const char *const shell_help_messages
[] = {
3952 "version([number]) Get or set JavaScript version number",
3953 "options([option ...]) Get or toggle JavaScript options",
3954 "load(['foo.js' ...]) Load files named by string arguments",
3955 "readline() Read a single line from stdin",
3956 "print([exp ...]) Evaluate and print expressions",
3957 "help([name ...]) Display usage and help messages",
3958 "quit() Quit the shell",
3959 "assertEq(actual, expected[, msg])\n"
3960 " Throw if the first two arguments are not the same (both +0 or both -0,\n"
3961 " both NaN, or non-zero and ===)",
3962 "gc() Run the garbage collector",
3964 "gcstats() Print garbage collector statistics",
3966 "gcparam(name, value)\n"
3967 " Wrapper for JS_SetGCParameter. The name must be either 'maxBytes' or\n"
3968 " 'maxMallocBytes' and the value must be convertable to a positive uint32",
3969 "countHeap([start[, kind]])\n"
3970 " Count the number of live GC things in the heap or things reachable from\n"
3971 " start when it is given and is not null. kind is either 'all' (default) to\n"
3972 " count all things or one of 'object', 'double', 'string', 'function',\n"
3973 " 'qname', 'namespace', 'xml' to count only things of that kind",
3975 "gczeal(level) How zealous the garbage collector should be",
3977 "trap([fun, [pc,]] exp) Trap bytecode execution",
3978 "untrap(fun[, pc]) Remove a trap",
3979 "line2pc([fun,] line) Map line number to PC",
3980 "pc2line(fun[, pc]) Map PC to line number",
3981 "stackQuota([number]) Query/set script stack quota",
3982 "stringsAreUTF8() Check if strings are UTF-8 encoded",
3983 "testUTF8(mode) Perform UTF-8 tests (modes are 1 to 4)",
3984 "throwError() Throw an error from JS_ReportError",
3986 "dis([fun]) Disassemble functions into bytecodes\n"
3987 "dis('-r', fun) Disassembles recursively",
3988 "disfile('foo.js') Disassemble script file into bytecodes",
3989 "dissrc([fun]) Disassemble functions with source lines",
3990 "dumpHeap([fileName[, start[, toFind[, maxDepth[, toIgnore]]]]])\n"
3991 " Interface to JS_DumpHeap with output sent to file",
3992 "notes([fun]) Show source notes for functions",
3993 "tracing([true|false|filename]) Turn bytecode execution tracing on/off.\n"
3994 " With filename, send to file.\n",
3995 "stats([string ...]) Dump 'arena', 'atom', 'global' stats",
3998 "cvtargs(arg1..., arg12) Test argument formatter",
4000 "build() Show build date and time",
4001 "clear([obj]) Clear properties of object",
4002 "intern(str) Internalize str in the atom table",
4003 "clone(fun[, scope]) Clone function object",
4004 "seal(obj[, deep]) Seal object, or object graph if deep",
4005 "getpda(obj) Get the property descriptors for obj",
4006 "getslx(obj) Get script line extent",
4007 "toint32(n) Testing hook for JS_ValueToInt32",
4009 " Evaluate s in optional sandbox object o\n"
4010 " if (s == '' && !o) return new o with eager standard classes\n"
4011 " if (s == 'lazy' && !o) return new o with lazy standard classes\n"
4012 " if (s == 'split' && !o) return new split-object o with lazy standard classes",
4013 "evalInFrame(n,str,save) Evaluate 'str' in the nth up frame.\n"
4014 " If 'save' (default false), save the frame chain",
4015 "shapeOf(obj) Get the shape of obj (an implementation detail)",
4017 "startShark() Start a Shark session.\n"
4018 " Shark must be running with programatic sampling",
4019 "stopShark() Stop a running Shark session",
4020 "connectShark() Connect to Shark.\n"
4021 " The -k switch does this automatically",
4022 "disconnectShark() Disconnect from Shark",
4024 #ifdef MOZ_CALLGRIND
4025 "startCallgrind() Start callgrind instrumentation",
4026 "stopCallgrind() Stop callgrind instrumentation",
4027 "dumpCallgrind([name]) Dump callgrind counters",
4030 "startVtune([filename]) Start vtune instrumentation",
4031 "stopVtune() Stop vtune instrumentation",
4032 "pauseVtune() Pause vtune collection",
4033 "resumeVtune() Resume vtune collection",
4036 "startTraceVis(filename) Start TraceVis recording (stops any current recording)",
4037 "stopTraceVis() Stop TraceVis recording",
4040 "arrayInfo(a1, a2, ...) Report statistics about arrays",
4042 #ifdef JS_THREADSAFE
4043 "sleep(dt) Sleep for dt seconds",
4044 "scatter(fns) Call functions concurrently (ignoring errors)",
4046 "snarf(filename) Read filename into returned string",
4047 "compile(code) Compiles a string to bytecode, potentially throwing",
4048 "parse(code) Parses a string, potentially throwing",
4049 "timeout([seconds])\n"
4050 " Get/Set the limit in seconds for the execution time for the current context.\n"
4051 " A negative value (default) means that the execution time is unlimited.",
4052 "elapsed() Execution time elapsed for the current context.",
4053 "parent(obj) Returns the parent of obj.\n",
4054 "wrap(obj) Wrap an object into a noop wrapper.\n"
4057 /* Help messages must match shell functions. */
4058 JS_STATIC_ASSERT(JS_ARRAY_LENGTH(shell_help_messages
) + 1 ==
4059 JS_ARRAY_LENGTH(shell_functions
));
4065 const char *const *m
;
4068 /* Each message must begin with "function_name(" prefix. */
4069 for (m
= shell_help_messages
; m
!= JS_ARRAY_END(shell_help_messages
); ++m
) {
4070 lp
= strchr(*m
, '(');
4072 JS_ASSERT(memcmp(shell_functions
[m
- shell_help_messages
].name
,
4077 # define CheckHelpMessages() ((void) 0)
4081 Help(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
4084 int did_header
, did_something
;
4090 fprintf(gOutFile
, "%s\n", JS_GetImplementationVersion());
4092 fputs(shell_help_header
, gOutFile
);
4093 for (i
= 0; shell_functions
[i
].name
; i
++)
4094 fprintf(gOutFile
, "%s\n", shell_help_messages
[i
]);
4097 for (i
= 0; i
< argc
; i
++) {
4099 type
= JS_TypeOfValue(cx
, argv
[i
]);
4100 if (type
== JSTYPE_FUNCTION
) {
4101 fun
= JS_ValueToFunction(cx
, argv
[i
]);
4102 str
= fun
->atom
? ATOM_TO_STRING(fun
->atom
) : NULL
;
4103 } else if (type
== JSTYPE_STRING
) {
4104 str
= JSVAL_TO_STRING(argv
[i
]);
4109 bytes
= JS_GetStringBytes(str
);
4110 for (j
= 0; shell_functions
[j
].name
; j
++) {
4111 if (!strcmp(bytes
, shell_functions
[j
].name
)) {
4114 fputs(shell_help_header
, gOutFile
);
4117 fprintf(gOutFile
, "%s\n", shell_help_messages
[j
]);
4122 if (!did_something
) {
4123 str
= JS_ValueToString(cx
, argv
[i
]);
4126 fprintf(gErrFile
, "Sorry, no help for %s\n",
4127 JS_GetStringBytes(str
));
4135 split_setup(JSContext
*cx
, JSBool evalcx
)
4137 JSObject
*outer
, *inner
, *arguments
;
4139 outer
= split_create_outer(cx
);
4142 AutoObjectRooter
root(cx
, outer
);
4144 JS_SetGlobalObject(cx
, outer
);
4146 inner
= split_create_inner(cx
, outer
);
4151 if (!JS_DefineFunctions(cx
, inner
, shell_functions
))
4154 /* Create a dummy arguments object. */
4155 arguments
= JS_NewArrayObject(cx
, 0, NULL
);
4157 !JS_DefineProperty(cx
, inner
, "arguments", OBJECT_TO_JSVAL(arguments
),
4163 JS_ClearScope(cx
, outer
);
4165 #ifndef LAZY_STANDARD_CLASSES
4166 if (!JS_InitStandardClasses(cx
, inner
))
4174 * Define a JS object called "it". Give it class operations that printf why
4175 * they're being called for tutorial purposes.
4178 ITS_COLOR
, ITS_HEIGHT
, ITS_WIDTH
, ITS_FUNNY
, ITS_ARRAY
, ITS_RDONLY
,
4179 ITS_CUSTOM
, ITS_CUSTOMRDONLY
4183 its_getter(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
4185 jsval
*val
= (jsval
*) JS_GetPrivate(cx
, obj
);
4186 *vp
= val
? *val
: JSVAL_VOID
;
4191 its_setter(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
4193 jsval
*val
= (jsval
*) JS_GetPrivate(cx
, obj
);
4201 JS_ReportOutOfMemory(cx
);
4205 if (!JS_AddValueRoot(cx
, val
)) {
4210 if (!JS_SetPrivate(cx
, obj
, (void*)val
)) {
4211 JS_RemoveValueRoot(cx
, val
);
4220 static JSPropertySpec its_props
[] = {
4221 {"color", ITS_COLOR
, JSPROP_ENUMERATE
, NULL
, NULL
},
4222 {"height", ITS_HEIGHT
, JSPROP_ENUMERATE
, NULL
, NULL
},
4223 {"width", ITS_WIDTH
, JSPROP_ENUMERATE
, NULL
, NULL
},
4224 {"funny", ITS_FUNNY
, JSPROP_ENUMERATE
, NULL
, NULL
},
4225 {"array", ITS_ARRAY
, JSPROP_ENUMERATE
, NULL
, NULL
},
4226 {"rdonly", ITS_RDONLY
, JSPROP_READONLY
, NULL
, NULL
},
4227 {"custom", ITS_CUSTOM
, JSPROP_ENUMERATE
,
4228 its_getter
, its_setter
},
4229 {"customRdOnly", ITS_CUSTOMRDONLY
, JSPROP_ENUMERATE
| JSPROP_READONLY
,
4230 its_getter
, its_setter
},
4231 {NULL
,0,0,NULL
,NULL
}
4235 its_bindMethod(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
,
4241 if (!JS_ConvertArguments(cx
, argc
, argv
, "so", &name
, &method
))
4244 *rval
= OBJECT_TO_JSVAL(method
);
4246 if (JS_TypeOfValue(cx
, *rval
) != JSTYPE_FUNCTION
) {
4247 JSString
*valstr
= JS_ValueToString(cx
, *rval
);
4249 JS_ReportError(cx
, "can't bind method %s to non-callable object %s",
4250 name
, JS_GetStringBytes(valstr
));
4255 if (!JS_DefineProperty(cx
, obj
, name
, *rval
, NULL
, NULL
, JSPROP_ENUMERATE
))
4258 return JS_SetParent(cx
, method
, obj
);
4261 static JSFunctionSpec its_methods
[] = {
4262 {"bindMethod", its_bindMethod
, 2,0,0},
4266 #ifdef JSD_LOWLEVEL_SOURCE
4268 * This facilitates sending source to JSD (the debugger system) in the shell
4269 * where the source is loaded using the JSFILE hack in jsscan. The function
4270 * below is used as a callback for the jsdbgapi JS_SetSourceHandler hook.
4271 * A more normal embedding (e.g. mozilla) loads source itself and can send
4272 * source directly to JSD without using this hook scheme.
4275 SendSourceToJSDebugger(const char *filename
, uintN lineno
,
4276 jschar
*str
, size_t length
,
4277 void **listenerTSData
, JSDContext
* jsdc
)
4279 JSDSourceText
*jsdsrc
= (JSDSourceText
*) *listenerTSData
;
4283 filename
= "typein";
4285 jsdsrc
= JSD_NewSourceText(jsdc
, filename
);
4287 jsdsrc
= JSD_FindSourceForURL(jsdc
, filename
);
4288 if (jsdsrc
&& JSD_SOURCE_PARTIAL
!=
4289 JSD_GetSourceStatus(jsdc
, jsdsrc
)) {
4295 jsdsrc
= JSD_AppendUCSourceText(jsdc
,jsdsrc
, str
, length
,
4296 JSD_SOURCE_PARTIAL
);
4298 *listenerTSData
= jsdsrc
;
4300 #endif /* JSD_LOWLEVEL_SOURCE */
4302 static JSBool its_noisy
; /* whether to be noisy when finalizing it */
4303 static JSBool its_enum_fail
;/* whether to fail when enumerating it */
4306 its_addProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
4311 IdToString
idString(cx
, id
);
4312 fprintf(gOutFile
, "adding its property %s,", idString
.getBytes());
4313 ToString
valueString(cx
, *vp
);
4314 fprintf(gOutFile
, " initial value %s\n", valueString
.getBytes());
4319 its_delProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
4324 IdToString
idString(cx
, id
);
4325 fprintf(gOutFile
, "deleting its property %s,", idString
.getBytes());
4326 ToString
valueString(cx
, *vp
);
4327 fprintf(gOutFile
, " initial value %s\n", valueString
.getBytes());
4332 its_getProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
4337 IdToString
idString(cx
, id
);
4338 fprintf(gOutFile
, "getting its property %s,", idString
.getBytes());
4339 ToString
valueString(cx
, *vp
);
4340 fprintf(gOutFile
, " initial value %s\n", valueString
.getBytes());
4345 its_setProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
4347 IdToString
idString(cx
, id
);
4349 fprintf(gOutFile
, "setting its property %s,", idString
.getBytes());
4350 ToString
valueString(cx
, *vp
);
4351 fprintf(gOutFile
, " new value %s\n", valueString
.getBytes());
4354 if (!JSID_IS_ATOM(id
))
4357 if (!strcmp(idString
.getBytes(), "noisy"))
4358 JS_ValueToBoolean(cx
, *vp
, &its_noisy
);
4359 else if (!strcmp(idString
.getBytes(), "enum_fail"))
4360 JS_ValueToBoolean(cx
, *vp
, &its_enum_fail
);
4366 * Its enumerator, implemented using the "new" enumerate API,
4370 its_enumerate(JSContext
*cx
, JSObject
*obj
, JSIterateOp enum_op
,
4371 jsval
*statep
, jsid
*idp
)
4376 case JSENUMERATE_INIT
:
4377 case JSENUMERATE_INIT_ALL
:
4379 fprintf(gOutFile
, "enumerate its properties\n");
4381 iterator
= JS_NewPropertyIterator(cx
, obj
);
4385 *statep
= OBJECT_TO_JSVAL(iterator
);
4387 *idp
= INT_TO_JSID(0);
4390 case JSENUMERATE_NEXT
:
4391 if (its_enum_fail
) {
4392 JS_ReportError(cx
, "its enumeration failed");
4396 iterator
= (JSObject
*) JSVAL_TO_OBJECT(*statep
);
4397 if (!JS_NextProperty(cx
, iterator
, idp
))
4400 if (!JSID_IS_VOID(*idp
))
4404 case JSENUMERATE_DESTROY
:
4405 /* Allow our iterator object to be GC'd. */
4406 *statep
= JSVAL_NULL
;
4414 its_resolve(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN flags
,
4418 IdToString
idString(cx
, id
);
4419 fprintf(gOutFile
, "resolving its property %s, flags {%s,%s,%s}\n",
4420 idString
.getBytes(),
4421 (flags
& JSRESOLVE_QUALIFIED
) ? "qualified" : "",
4422 (flags
& JSRESOLVE_ASSIGNING
) ? "assigning" : "",
4423 (flags
& JSRESOLVE_DETECTING
) ? "detecting" : "");
4429 its_convert(JSContext
*cx
, JSObject
*obj
, JSType type
, jsval
*vp
)
4432 fprintf(gOutFile
, "converting it to %s type\n", JS_GetTypeName(cx
, type
));
4437 its_finalize(JSContext
*cx
, JSObject
*obj
)
4441 fprintf(gOutFile
, "finalizing it\n");
4442 rootedVal
= (jsval
*) JS_GetPrivate(cx
, obj
);
4444 JS_RemoveValueRoot(cx
, rootedVal
);
4445 JS_SetPrivate(cx
, obj
, NULL
);
4450 static JSClass its_class
= {
4451 "It", JSCLASS_NEW_RESOLVE
| JSCLASS_NEW_ENUMERATE
| JSCLASS_HAS_PRIVATE
,
4452 its_addProperty
, its_delProperty
, its_getProperty
, its_setProperty
,
4453 (JSEnumerateOp
)its_enumerate
, (JSResolveOp
)its_resolve
,
4454 its_convert
, its_finalize
,
4455 JSCLASS_NO_OPTIONAL_MEMBERS
4458 JSErrorFormatString jsShell_ErrorFormatString
[JSErr_Limit
] = {
4459 #define MSG_DEF(name, number, count, exception, format) \
4460 { format, count, JSEXN_ERR } ,
4461 #include "jsshell.msg"
4465 static const JSErrorFormatString
*
4466 my_GetErrorMessage(void *userRef
, const char *locale
, const uintN errorNumber
)
4468 if ((errorNumber
> 0) && (errorNumber
< JSShellErr_Limit
))
4469 return &jsShell_ErrorFormatString
[errorNumber
];
4474 my_ErrorReporter(JSContext
*cx
, const char *message
, JSErrorReport
*report
)
4481 fprintf(gErrFile
, "%s\n", message
);
4485 /* Conditionally ignore reported warnings. */
4486 if (JSREPORT_IS_WARNING(report
->flags
) && !reportWarnings
)
4490 if (report
->filename
)
4491 prefix
= JS_smprintf("%s:", report
->filename
);
4492 if (report
->lineno
) {
4494 prefix
= JS_smprintf("%s%u: ", tmp
? tmp
: "", report
->lineno
);
4497 if (JSREPORT_IS_WARNING(report
->flags
)) {
4499 prefix
= JS_smprintf("%s%swarning: ",
4501 JSREPORT_IS_STRICT(report
->flags
) ? "strict " : "");
4505 /* embedded newlines -- argh! */
4506 while ((ctmp
= strchr(message
, '\n')) != 0) {
4509 fputs(prefix
, gErrFile
);
4510 fwrite(message
, 1, ctmp
- message
, gErrFile
);
4514 /* If there were no filename or lineno, the prefix might be empty */
4516 fputs(prefix
, gErrFile
);
4517 fputs(message
, gErrFile
);
4519 if (!report
->linebuf
) {
4520 fputc('\n', gErrFile
);
4524 /* report->linebuf usually ends with a newline. */
4525 n
= strlen(report
->linebuf
);
4526 fprintf(gErrFile
, ":\n%s%s%s%s",
4529 (n
> 0 && report
->linebuf
[n
-1] == '\n') ? "" : "\n",
4531 n
= report
->tokenptr
- report
->linebuf
;
4532 for (i
= j
= 0; i
< n
; i
++) {
4533 if (report
->linebuf
[i
] == '\t') {
4534 for (k
= (j
+ 8) & ~7; j
< k
; j
++) {
4535 fputc('.', gErrFile
);
4539 fputc('.', gErrFile
);
4542 fputs("^\n", gErrFile
);
4544 if (!JSREPORT_IS_WARNING(report
->flags
)) {
4545 if (report
->errorNumber
== JSMSG_OUT_OF_MEMORY
) {
4546 gExitCode
= EXITCODE_OUT_OF_MEMORY
;
4548 gExitCode
= EXITCODE_RUNTIME_ERROR
;
4551 JS_free(cx
, prefix
);
4554 #if defined(SHELL_HACK) && defined(DEBUG) && defined(XP_UNIX)
4556 Exec(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
4559 const char *name
, **nargv
;
4565 fun
= JS_ValueToFunction(cx
, argv
[-2]);
4570 name
= JS_GetStringBytes(ATOM_TO_STRING(fun
->atom
));
4572 nargv
= JS_malloc(cx
, (nargc
+ 1) * sizeof(char *));
4576 for (i
= 1; i
< nargc
; i
++) {
4577 str
= JS_ValueToString(cx
, argv
[i
-1]);
4582 nargv
[i
] = JS_GetStringBytes(str
);
4591 (void) execvp(name
, (char **)nargv
);
4595 while (waitpid(pid
, &status
, 0) < 0 && errno
== EINTR
)
4605 global_enumerate(JSContext
*cx
, JSObject
*obj
)
4607 #ifdef LAZY_STANDARD_CLASSES
4608 return JS_EnumerateStandardClasses(cx
, obj
);
4615 global_resolve(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN flags
,
4618 #ifdef LAZY_STANDARD_CLASSES
4621 if (!JS_ResolveStandardClass(cx
, obj
, id
, &resolved
))
4629 #if defined(SHELL_HACK) && defined(DEBUG) && defined(XP_UNIX)
4630 if (!(flags
& JSRESOLVE_QUALIFIED
)) {
4632 * Do this expensive hack only for unoptimized Unix builds, which are
4633 * not used for benchmarking.
4635 char *path
, *comp
, *full
;
4640 if (!JSVAL_IS_STRING(id
))
4642 path
= getenv("PATH");
4645 path
= JS_strdup(cx
, path
);
4648 name
= JS_GetStringBytes(JSVAL_TO_STRING(id
));
4650 for (comp
= strtok(path
, ":"); comp
; comp
= strtok(NULL
, ":")) {
4651 if (*comp
!= '\0') {
4652 full
= JS_smprintf("%s/%s", comp
, name
);
4654 JS_ReportOutOfMemory(cx
);
4659 full
= (char *)name
;
4661 found
= (access(full
, X_OK
) == 0);
4665 fun
= JS_DefineFunction(cx
, obj
, name
, Exec
, 0,
4681 JSClass global_class
= {
4682 "global", JSCLASS_NEW_RESOLVE
| JSCLASS_GLOBAL_FLAGS
| JSCLASS_HAS_PRIVATE
,
4683 JS_PropertyStub
, JS_PropertyStub
,
4684 JS_PropertyStub
, JS_PropertyStub
,
4685 global_enumerate
, (JSResolveOp
) global_resolve
,
4686 JS_ConvertStub
, its_finalize
,
4687 JSCLASS_NO_OPTIONAL_MEMBERS
4691 env_setProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
4693 /* XXX porting may be easy, but these don't seem to supply setenv by default */
4694 #if !defined XP_BEOS && !defined XP_OS2 && !defined SOLARIS
4697 IdToString
idstr(cx
, id
, JS_TRUE
);
4700 ToString
valstr(cx
, *vp
, JS_TRUE
);
4703 #if defined XP_WIN || defined HPUX || defined OSF1 || defined IRIX
4705 char *waste
= JS_smprintf("%s=%s", idstr
.getBytes(), valstr
.getBytes());
4707 JS_ReportOutOfMemory(cx
);
4713 * HPUX9 at least still has the bad old non-copying putenv.
4715 * Per mail from <s.shanmuganathan@digital.com>, OSF1 also has a putenv
4716 * that will crash if you pass it an auto char array (so it must place
4717 * its argument directly in the char *environ[] array).
4719 JS_smprintf_free(waste
);
4723 rv
= setenv(idstr
.getBytes(), valstr
.getBytes(), 1);
4726 JS_ReportError(cx
, "can't set env variable %s to %s", idstr
.getBytes(), valstr
.getBytes());
4729 *vp
= valstr
.getJSVal();
4730 #endif /* !defined XP_BEOS && !defined XP_OS2 && !defined SOLARIS */
4735 env_enumerate(JSContext
*cx
, JSObject
*obj
)
4737 static JSBool reflected
;
4738 char **evp
, *name
, *value
;
4745 for (evp
= (char **)JS_GetPrivate(cx
, obj
); (name
= *evp
) != NULL
; evp
++) {
4746 value
= strchr(name
, '=');
4750 valstr
= JS_NewStringCopyZ(cx
, value
);
4754 ok
= JS_DefineProperty(cx
, obj
, name
, STRING_TO_JSVAL(valstr
),
4755 NULL
, NULL
, JSPROP_ENUMERATE
);
4762 reflected
= JS_TRUE
;
4767 env_resolve(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN flags
,
4771 const char *name
, *value
;
4773 if (flags
& JSRESOLVE_ASSIGNING
)
4776 IdToString
idstr(cx
, id
, JS_TRUE
);
4780 name
= idstr
.getBytes();
4781 value
= getenv(name
);
4783 valstr
= JS_NewStringCopyZ(cx
, value
);
4786 if (!JS_DefineProperty(cx
, obj
, name
, STRING_TO_JSVAL(valstr
),
4787 NULL
, NULL
, JSPROP_ENUMERATE
)) {
4795 static JSClass env_class
= {
4796 "environment", JSCLASS_HAS_PRIVATE
| JSCLASS_NEW_RESOLVE
,
4797 JS_PropertyStub
, JS_PropertyStub
,
4798 JS_PropertyStub
, env_setProperty
,
4799 env_enumerate
, (JSResolveOp
) env_resolve
,
4800 JS_ConvertStub
, NULL
,
4801 JSCLASS_NO_OPTIONAL_MEMBERS
4805 * Avoid a reentrancy hazard.
4807 * The non-JS_THREADSAFE shell uses a signal handler to implement timeout().
4808 * The JS engine is not really reentrant, but JS_TriggerAllOperationCallbacks
4809 * is mostly safe--the only danger is that we might interrupt JS_NewContext or
4810 * JS_DestroyContext while the context list is being modified. Therefore we
4811 * disable the signal handler around calls to those functions.
4813 #ifdef JS_THREADSAFE
4814 # define WITH_SIGNALS_DISABLED(x) x
4816 # define WITH_SIGNALS_DISABLED(x) \
4818 ScheduleWatchdog(gRuntime, -1); \
4820 ScheduleWatchdog(gRuntime, gTimeoutInterval); \
4825 NewContext(JSRuntime
*rt
)
4828 WITH_SIGNALS_DISABLED(cx
= JS_NewContext(rt
, gStackChunkSize
));
4832 JSShellContextData
*data
= NewContextData();
4834 DestroyContext(cx
, false);
4838 JS_SetContextPrivate(cx
, data
);
4839 JS_SetErrorReporter(cx
, my_ErrorReporter
);
4840 JS_SetVersion(cx
, JSVERSION_LATEST
);
4841 SetContextOptions(cx
);
4843 JS_ToggleOptions(cx
, JSOPTION_JIT
);
4848 DestroyContext(JSContext
*cx
, bool withGC
)
4850 JSShellContextData
*data
= GetContextData(cx
);
4851 JS_SetContextPrivate(cx
, NULL
);
4853 WITH_SIGNALS_DISABLED(withGC
? JS_DestroyContext(cx
) : JS_DestroyContextNoGC(cx
));
4857 NewGlobalObject(JSContext
*cx
, JSAutoCrossCompartmentCall
&call
)
4859 JSObject
*glob
= JS_NewCompartmentAndGlobalObject(cx
, &global_class
, NULL
);
4862 if (!call
.enter(cx
, glob
))
4865 #ifdef LAZY_STANDARD_CLASSES
4866 JS_SetGlobalObject(cx
, glob
);
4868 if (!JS_InitStandardClasses(cx
, glob
))
4871 #ifdef JS_HAS_CTYPES
4872 if (!JS_InitCTypesClass(cx
, glob
))
4875 if (!JS_DefineFunctions(cx
, glob
, shell_functions
))
4878 JSObject
*it
= JS_DefineObject(cx
, glob
, "it", &its_class
, NULL
, 0);
4881 if (!JS_DefineProperties(cx
, it
, its_props
))
4883 if (!JS_DefineFunctions(cx
, it
, its_methods
))
4886 if (!JS_DefineProperty(cx
, glob
, "custom", JSVAL_VOID
, its_getter
,
4889 if (!JS_DefineProperty(cx
, glob
, "customRdOnly", JSVAL_VOID
, its_getter
,
4890 its_setter
, JSPROP_READONLY
))
4897 shell(JSContext
*cx
, int argc
, char **argv
, char **envp
)
4899 JSAutoRequest
ar(cx
);
4900 JSAutoCrossCompartmentCall ac
;
4902 JSObject
*glob
= NewGlobalObject(cx
, ac
);
4906 JSObject
*envobj
= JS_DefineObject(cx
, glob
, "environment", &env_class
, NULL
, 0);
4907 if (!envobj
|| !JS_SetPrivate(cx
, envobj
, envp
))
4912 * XXX A command line option to enable debugging (or not) would be good
4914 jsdc
= JSD_DebuggerOnForUser(rt
, NULL
, NULL
);
4917 JSD_JSContextInUse(jsdc
, cx
);
4918 #ifdef JSD_LOWLEVEL_SOURCE
4919 JS_SetSourceHandler(rt
, SendSourceToJSDebugger
, jsdc
);
4920 #endif /* JSD_LOWLEVEL_SOURCE */
4921 #ifdef JSDEBUGGER_JAVA_UI
4922 jsdjc
= JSDJ_CreateContext();
4925 JSDJ_SetJSDContext(jsdjc
, jsdc
);
4926 java_env
= JSDJ_CreateJavaVMAndStartDebugger(jsdjc
);
4928 * XXX This would be the place to wait for the debugger to start.
4929 * Waiting would be nice in general, but especially when a js file
4930 * is passed on the cmd line.
4932 #endif /* JSDEBUGGER_JAVA_UI */
4933 #ifdef JSDEBUGGER_C_UI
4934 jsdbc
= JSDB_InitDebugger(rt
, jsdc
, 0);
4935 #endif /* JSDEBUGGER_C_UI */
4936 #endif /* JSDEBUGGER */
4938 #ifdef JS_THREADSAFE
4939 class ShellWorkerHooks
: public js::workers::WorkerHooks
{
4941 JSObject
*newGlobalObject(JSContext
*cx
) {
4942 JSAutoCrossCompartmentCall ac
;
4943 return NewGlobalObject(cx
, ac
);
4946 ShellWorkerHooks hooks
;
4947 if (!JS_AddNamedObjectRoot(cx
, &gWorkers
, "Workers") ||
4948 !js::workers::init(cx
, &hooks
, glob
, &gWorkers
)) {
4953 int result
= ProcessArgs(cx
, glob
, argv
, argc
);
4955 #ifdef JS_THREADSAFE
4956 js::workers::finish(cx
, gWorkers
);
4957 JS_RemoveObjectRoot(cx
, &gWorkers
);
4964 #ifdef JSDEBUGGER_C_UI
4966 JSDB_TermDebugger(jsdc
);
4967 #endif /* JSDEBUGGER_C_UI */
4968 JSD_DebuggerOff(jsdc
);
4970 #endif /* JSDEBUGGER */
4976 main(int argc
, char **argv
, char **envp
)
4984 #ifdef JSDEBUGGER_JAVA_UI
4988 #ifdef JSDEBUGGER_C_UI
4990 #endif /* JSDEBUGGER_C_UI */
4991 #endif /* JSDEBUGGER */
4993 CheckHelpMessages();
4994 #ifdef HAVE_SETLOCALE
4995 setlocale(LC_ALL
, "");
4998 #ifdef JS_THREADSAFE
4999 if (PR_FAILURE
== PR_NewThreadPrivateIndex(&gStackBaseThreadIndex
, NULL
) ||
5000 PR_FAILURE
== PR_SetThreadPrivate(gStackBaseThreadIndex
, &stackDummy
)) {
5004 gStackBase
= (jsuword
) &stackDummy
;
5008 /* these streams are normally line buffered on OS/2 and need a \n, *
5009 * so we need to unbuffer then to get a reasonable prompt */
5021 // Set the timer calibration delay count to 0 so we get high
5022 // resolution right away, which we need for precise benchmarking.
5023 extern int CALIBRATION_DELAY_COUNT
;
5024 CALIBRATION_DELAY_COUNT
= 0;
5027 rt
= JS_NewRuntime(64L * 1024L * 1024L);
5031 if (!InitWatchdog(rt
))
5034 cx
= NewContext(rt
);
5038 JS_SetGCParameterForThread(cx
, JSGC_MAX_CODE_CACHE_BYTES
, 16 * 1024 * 1024);
5040 result
= shell(cx
, argc
, argv
, envp
);
5042 JS_CommenceRuntimeShutDown(rt
);
5044 DestroyContext(cx
, true);
5048 JS_DestroyRuntime(rt
);