1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sw=4 et tw=99:
3 : *
4 : * ***** BEGIN LICENSE BLOCK *****
5 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 : *
7 : * The contents of this file are subject to the Mozilla Public License Version
8 : * 1.1 (the "License"); you may not use this file except in compliance with
9 : * the License. You may obtain a copy of the License at
10 : * http://www.mozilla.org/MPL/
11 : *
12 : * Software distributed under the License is distributed on an "AS IS" basis,
13 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 : * for the specific language governing rights and limitations under the
15 : * License.
16 : *
17 : * The Original Code is Mozilla Communicator client code, released
18 : * March 31, 1998.
19 : *
20 : * The Initial Developer of the Original Code is
21 : * Netscape Communications Corporation.
22 : * Portions created by the Initial Developer are Copyright (C) 1998
23 : * the Initial Developer. All Rights Reserved.
24 : *
25 : * Contributor(s):
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either of the GNU General Public License Version 2 or later (the "GPL"),
29 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : /*
42 : * JS shell.
43 : */
44 : #include <errno.h>
45 : #include <math.h>
46 : #include <stdio.h>
47 : #include <stdlib.h>
48 : #include <string.h>
49 : #include <signal.h>
50 : #include <locale.h>
51 :
52 : #include "mozilla/Util.h"
53 :
54 : #include "jstypes.h"
55 : #include "jsutil.h"
56 : #include "jsprf.h"
57 : #include "jswrapper.h"
58 : #include "jsapi.h"
59 : #include "jsarray.h"
60 : #include "jsatom.h"
61 : #include "jscntxt.h"
62 : #include "jsdate.h"
63 : #include "jsdbgapi.h"
64 : #include "jsfun.h"
65 : #include "jsgc.h"
66 : #include "jsiter.h"
67 : #include "jslock.h"
68 : #include "jsnum.h"
69 : #include "jsobj.h"
70 : #include "json.h"
71 : #include "jsreflect.h"
72 : #include "jsscope.h"
73 : #include "jsscript.h"
74 : #include "jstypedarray.h"
75 : #include "jstypedarrayinlines.h"
76 : #include "jsxml.h"
77 : #include "jsperf.h"
78 :
79 : #include "builtin/TestingFunctions.h"
80 : #include "frontend/BytecodeEmitter.h"
81 : #include "frontend/Parser.h"
82 : #include "methodjit/MethodJIT.h"
83 :
84 : #include "prmjtime.h"
85 :
86 : #ifdef JSDEBUGGER
87 : #include "jsdebug.h"
88 : #ifdef JSDEBUGGER_JAVA_UI
89 : #include "jsdjava.h"
90 : #endif /* JSDEBUGGER_JAVA_UI */
91 : #ifdef JSDEBUGGER_C_UI
92 : #include "jsdb.h"
93 : #endif /* JSDEBUGGER_C_UI */
94 : #endif /* JSDEBUGGER */
95 :
96 : #include "jsoptparse.h"
97 : #include "jsworkers.h"
98 : #include "jsheaptools.h"
99 :
100 : #include "jsinferinlines.h"
101 : #include "jsinterpinlines.h"
102 : #include "jsobjinlines.h"
103 : #include "jsscriptinlines.h"
104 :
105 : #ifdef XP_UNIX
106 : #include <unistd.h>
107 : #include <sys/types.h>
108 : #include <sys/wait.h>
109 : #endif
110 :
111 : #if defined(XP_WIN) || defined(XP_OS2)
112 : #include <io.h> /* for isatty() */
113 : #endif
114 :
115 : #ifdef XP_WIN
116 : #include "jswin.h"
117 : #endif
118 :
119 : using namespace mozilla;
120 : using namespace js;
121 : using namespace js::cli;
122 :
123 : typedef enum JSShellExitCode {
124 : EXITCODE_RUNTIME_ERROR = 3,
125 : EXITCODE_FILE_NOT_FOUND = 4,
126 : EXITCODE_OUT_OF_MEMORY = 5,
127 : EXITCODE_TIMEOUT = 6
128 : } JSShellExitCode;
129 :
130 : size_t gStackChunkSize = 8192;
131 :
132 : /* Assume that we can not use more than 5e5 bytes of C stack by default. */
133 : #if (defined(DEBUG) && defined(__SUNPRO_CC)) || defined(JS_CPU_SPARC)
134 : /* Sun compiler uses larger stack space for js_Interpret() with debug
135 : Use a bigger gMaxStackSize to make "make check" happy. */
136 : #define DEFAULT_MAX_STACK_SIZE 5000000
137 : #else
138 : #define DEFAULT_MAX_STACK_SIZE 500000
139 : #endif
140 :
141 : size_t gMaxStackSize = DEFAULT_MAX_STACK_SIZE;
142 :
143 :
144 : #ifdef JS_THREADSAFE
145 : static PRUintn gStackBaseThreadIndex;
146 : #else
147 : static uintptr_t gStackBase;
148 : #endif
149 :
150 : /*
151 : * Limit the timeout to 30 minutes to prevent an overflow on platfoms
152 : * that represent the time internally in microseconds using 32-bit int.
153 : */
154 : static double MAX_TIMEOUT_INTERVAL = 1800.0;
155 : static double gTimeoutInterval = -1.0;
156 : static volatile bool gCanceled = false;
157 :
158 : static bool enableMethodJit = false;
159 : static bool enableTypeInference = false;
160 : static bool enableDisassemblyDumps = false;
161 :
162 : static bool printTiming = false;
163 :
164 : static JSBool
165 : SetTimeoutValue(JSContext *cx, double t);
166 :
167 : static bool
168 : InitWatchdog(JSRuntime *rt);
169 :
170 : static void
171 : KillWatchdog();
172 :
173 : static bool
174 : ScheduleWatchdog(JSRuntime *rt, double t);
175 :
176 : static void
177 : CancelExecution(JSRuntime *rt);
178 :
179 : /*
180 : * Watchdog thread state.
181 : */
182 : #ifdef JS_THREADSAFE
183 :
184 : static PRLock *gWatchdogLock = NULL;
185 : static PRCondVar *gWatchdogWakeup = NULL;
186 : static PRThread *gWatchdogThread = NULL;
187 : static bool gWatchdogHasTimeout = false;
188 : static PRIntervalTime gWatchdogTimeout = 0;
189 :
190 : static PRCondVar *gSleepWakeup = NULL;
191 :
192 : #else
193 :
194 : static JSRuntime *gRuntime = NULL;
195 :
196 : #endif
197 :
198 : int gExitCode = 0;
199 : JSBool gQuitting = JS_FALSE;
200 : FILE *gErrFile = NULL;
201 : FILE *gOutFile = NULL;
202 : #ifdef JS_THREADSAFE
203 : JSObject *gWorkers = NULL;
204 : js::workers::ThreadPool *gWorkerThreadPool = NULL;
205 : #endif
206 :
207 : static JSBool reportWarnings = JS_TRUE;
208 : static JSBool compileOnly = JS_FALSE;
209 :
210 : #ifdef DEBUG
211 : static JSBool OOM_printAllocationCount = JS_FALSE;
212 : #endif
213 :
214 : typedef enum JSShellErrNum {
215 : #define MSG_DEF(name, number, count, exception, format) \
216 : name = number,
217 : #include "jsshell.msg"
218 : #undef MSG_DEF
219 : JSShellErr_Limit
220 : } JSShellErrNum;
221 :
222 : static JSContext *
223 : NewContext(JSRuntime *rt);
224 :
225 : static void
226 : DestroyContext(JSContext *cx, bool withGC);
227 :
228 : static const JSErrorFormatString *
229 : my_GetErrorMessage(void *userRef, const char *locale, const unsigned errorNumber);
230 :
231 : #ifdef EDITLINE
232 : JS_BEGIN_EXTERN_C
233 : extern JS_EXPORT_API(char *) readline(const char *prompt);
234 : extern JS_EXPORT_API(void) add_history(char *line);
235 : JS_END_EXTERN_C
236 : #endif
237 :
238 : static void
239 0 : ReportException(JSContext *cx)
240 : {
241 0 : if (JS_IsExceptionPending(cx)) {
242 0 : if (!JS_ReportPendingException(cx))
243 0 : JS_ClearPendingException(cx);
244 : }
245 0 : }
246 :
247 : class ToStringHelper {
248 : public:
249 0 : ToStringHelper(JSContext *aCx, jsval v, JSBool aThrow = JS_FALSE)
250 0 : : cx(aCx), mThrow(aThrow)
251 : {
252 0 : mStr = JS_ValueToString(cx, v);
253 0 : if (!aThrow && !mStr)
254 0 : ReportException(cx);
255 0 : JS_AddNamedStringRoot(cx, &mStr, "Value ToString helper");
256 0 : }
257 0 : ~ToStringHelper() {
258 0 : JS_RemoveStringRoot(cx, &mStr);
259 0 : }
260 0 : JSBool threw() { return !mStr; }
261 0 : jsval getJSVal() { return STRING_TO_JSVAL(mStr); }
262 0 : const char *getBytes() {
263 0 : if (mStr && (mBytes.ptr() || mBytes.encode(cx, mStr)))
264 0 : return mBytes.ptr();
265 0 : return "(error converting value)";
266 : }
267 : private:
268 : JSContext *cx;
269 : JSString *mStr;
270 : JSBool mThrow;
271 : JSAutoByteString mBytes;
272 : };
273 :
274 0 : class IdStringifier : public ToStringHelper {
275 : public:
276 0 : IdStringifier(JSContext *cx, jsid id, JSBool aThrow = JS_FALSE)
277 0 : : ToStringHelper(cx, IdToJsval(id), aThrow)
278 0 : { }
279 : };
280 :
281 : static char *
282 0 : GetLine(FILE *file, const char * prompt)
283 : {
284 : size_t size;
285 : char *buffer;
286 : #ifdef EDITLINE
287 : /*
288 : * Use readline only if file is stdin, because there's no way to specify
289 : * another handle. Are other filehandles interactive?
290 : */
291 0 : if (file == stdin) {
292 0 : char *linep = readline(prompt);
293 : /*
294 : * We set it to zero to avoid complaining about inappropriate ioctl
295 : * for device in the case of EOF. Looks like errno == 251 if line is
296 : * finished with EOF and errno == 25 (EINVAL on Mac) if there is
297 : * nothing left to read.
298 : */
299 0 : if (errno == 251 || errno == 25 || errno == EINVAL)
300 0 : errno = 0;
301 0 : if (!linep)
302 0 : return NULL;
303 0 : if (linep[0] != '\0')
304 0 : add_history(linep);
305 0 : return linep;
306 : }
307 : #endif
308 0 : size_t len = 0;
309 0 : if (*prompt != '\0') {
310 0 : fprintf(gOutFile, "%s", prompt);
311 0 : fflush(gOutFile);
312 : }
313 0 : size = 80;
314 0 : buffer = (char *) malloc(size);
315 0 : if (!buffer)
316 0 : return NULL;
317 0 : char *current = buffer;
318 0 : while (fgets(current, size - len, file)) {
319 0 : len += strlen(current);
320 0 : char *t = buffer + len - 1;
321 0 : if (*t == '\n') {
322 : /* Line was read. We remove '\n' and exit. */
323 0 : *t = '\0';
324 0 : return buffer;
325 : }
326 0 : if (len + 1 == size) {
327 0 : size = size * 2;
328 0 : char *tmp = (char *) realloc(buffer, size);
329 0 : if (!tmp) {
330 0 : free(buffer);
331 0 : return NULL;
332 : }
333 0 : buffer = tmp;
334 : }
335 0 : current = buffer + len;
336 : }
337 0 : if (len && !ferror(file))
338 0 : return buffer;
339 0 : free(buffer);
340 0 : return NULL;
341 : }
342 :
343 : /*
344 : * State to store as JSContext private.
345 : *
346 : * We declare such timestamp as volatile as they are updated in the operation
347 : * callback without taking any locks. Any possible race can only lead to more
348 : * frequent callback calls. This is safe as the callback does everything based
349 : * on timing.
350 : */
351 : struct JSShellContextData {
352 : volatile JSIntervalTime startTime;
353 : };
354 :
355 : static JSShellContextData *
356 18666 : NewContextData()
357 : {
358 : /* Prevent creation of new contexts after we have been canceled. */
359 18666 : if (gCanceled)
360 0 : return NULL;
361 :
362 : JSShellContextData *data = (JSShellContextData *)
363 18666 : calloc(sizeof(JSShellContextData), 1);
364 18666 : if (!data)
365 0 : return NULL;
366 18666 : data->startTime = js_IntervalNow();
367 18666 : return data;
368 : }
369 :
370 : static inline JSShellContextData *
371 18666 : GetContextData(JSContext *cx)
372 : {
373 18666 : JSShellContextData *data = (JSShellContextData *) JS_GetContextPrivate(cx);
374 :
375 18666 : JS_ASSERT(data);
376 18666 : return data;
377 : }
378 :
379 : static JSBool
380 160 : ShellOperationCallback(JSContext *cx)
381 : {
382 160 : if (!gCanceled)
383 160 : return JS_TRUE;
384 :
385 0 : JS_ClearPendingException(cx);
386 0 : return JS_FALSE;
387 : }
388 :
389 : static void
390 55998 : SetContextOptions(JSContext *cx)
391 : {
392 55998 : JS_SetOperationCallback(cx, ShellOperationCallback);
393 55998 : }
394 :
395 : /*
396 : * Some UTF-8 files, notably those written using Notepad, have a Unicode
397 : * Byte-Order-Mark (BOM) as their first character. This is useless (byte-order
398 : * is meaningless for UTF-8) but causes a syntax error unless we skip it.
399 : */
400 : static void
401 37332 : SkipUTF8BOM(FILE* file)
402 : {
403 37332 : if (!js_CStringsAreUTF8)
404 37332 : return;
405 :
406 0 : int ch1 = fgetc(file);
407 0 : int ch2 = fgetc(file);
408 0 : int ch3 = fgetc(file);
409 :
410 : // Skip the BOM
411 0 : if (ch1 == 0xEF && ch2 == 0xBB && ch3 == 0xBF)
412 0 : return;
413 :
414 : // No BOM - revert
415 0 : if (ch3 != EOF)
416 0 : ungetc(ch3, file);
417 0 : if (ch2 != EOF)
418 0 : ungetc(ch2, file);
419 0 : if (ch1 != EOF)
420 0 : ungetc(ch1, file);
421 : }
422 :
423 : static void
424 37332 : Process(JSContext *cx, JSObject *obj, const char *filename, bool forceTTY)
425 : {
426 : JSBool ok, hitEOF;
427 : JSScript *script;
428 : jsval result;
429 : JSString *str;
430 : char *buffer;
431 : size_t size;
432 : jschar *uc_buffer;
433 : size_t uc_len;
434 : int lineno;
435 : int startline;
436 : FILE *file;
437 : uint32_t oldopts;
438 :
439 74664 : RootObject root(cx, &obj);
440 :
441 37332 : if (forceTTY || !filename || strcmp(filename, "-") == 0) {
442 0 : file = stdin;
443 : } else {
444 37332 : file = fopen(filename, "r");
445 37332 : if (!file) {
446 : JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
447 0 : JSSMSG_CANT_OPEN, filename, strerror(errno));
448 0 : gExitCode = EXITCODE_FILE_NOT_FOUND;
449 : return;
450 : }
451 : }
452 :
453 37332 : SetContextOptions(cx);
454 :
455 37332 : if (!forceTTY && !isatty(fileno(file)))
456 : {
457 37332 : SkipUTF8BOM(file);
458 :
459 : /*
460 : * It's not interactive - just execute it. Support the UNIX #! shell
461 : * hack, and gobble the first line if it starts with '#'.
462 : */
463 37332 : int ch = fgetc(file);
464 37332 : if (ch == '#') {
465 0 : while((ch = fgetc(file)) != EOF) {
466 0 : if (ch == '\n' || ch == '\r')
467 0 : break;
468 : }
469 : }
470 37332 : ungetc(ch, file);
471 :
472 37332 : int64_t t1 = PRMJ_Now();
473 37332 : oldopts = JS_GetOptions(cx);
474 37332 : JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO | JSOPTION_NO_SCRIPT_RVAL);
475 37332 : script = JS_CompileUTF8FileHandle(cx, obj, filename, file);
476 37332 : JS_SetOptions(cx, oldopts);
477 37332 : if (script && !compileOnly) {
478 37287 : if (!JS_ExecuteScript(cx, obj, script, NULL)) {
479 828 : if (!gQuitting && !gCanceled)
480 801 : gExitCode = EXITCODE_RUNTIME_ERROR;
481 : }
482 37287 : int64_t t2 = PRMJ_Now() - t1;
483 37287 : if (printTiming)
484 0 : printf("runtime = %.3f ms\n", double(t2) / PRMJ_USEC_PER_MSEC);
485 : }
486 :
487 37332 : goto cleanup;
488 : }
489 :
490 : /* It's an interactive filehandle; drop into read-eval-print loop. */
491 0 : lineno = 1;
492 0 : hitEOF = JS_FALSE;
493 0 : buffer = NULL;
494 0 : size = 0; /* assign here to avoid warnings */
495 0 : do {
496 : /*
497 : * Accumulate lines until we get a 'compilable unit' - one that either
498 : * generates an error (before running out of source) or that compiles
499 : * cleanly. This should be whenever we get a complete statement that
500 : * coincides with the end of a line.
501 : */
502 0 : startline = lineno;
503 0 : size_t len = 0; /* initialize to avoid warnings */
504 0 : do {
505 0 : ScheduleWatchdog(cx->runtime, -1);
506 0 : gCanceled = false;
507 0 : errno = 0;
508 :
509 : char *line;
510 : {
511 0 : JSAutoSuspendRequest suspended(cx);
512 0 : line = GetLine(file, startline == lineno ? "js> " : "");
513 : }
514 0 : if (!line) {
515 0 : if (errno) {
516 0 : JS_ReportError(cx, strerror(errno));
517 0 : free(buffer);
518 0 : goto cleanup;
519 : }
520 0 : hitEOF = JS_TRUE;
521 0 : break;
522 : }
523 0 : if (!buffer) {
524 0 : buffer = line;
525 0 : len = strlen(buffer);
526 0 : size = len + 1;
527 : } else {
528 : /*
529 : * len + 1 is required to store '\n' in the end of line.
530 : */
531 0 : size_t newlen = strlen(line) + (len ? len + 1 : 0);
532 0 : if (newlen + 1 > size) {
533 0 : size = newlen + 1 > size * 2 ? newlen + 1 : size * 2;
534 0 : char *newBuf = (char *) realloc(buffer, size);
535 0 : if (!newBuf) {
536 0 : free(buffer);
537 0 : free(line);
538 0 : JS_ReportOutOfMemory(cx);
539 0 : goto cleanup;
540 : }
541 0 : buffer = newBuf;
542 : }
543 0 : char *current = buffer + len;
544 0 : if (startline != lineno)
545 0 : *current++ = '\n';
546 0 : strcpy(current, line);
547 0 : len = newlen;
548 0 : free(line);
549 : }
550 0 : lineno++;
551 0 : if (!ScheduleWatchdog(cx->runtime, gTimeoutInterval)) {
552 0 : hitEOF = JS_TRUE;
553 0 : break;
554 : }
555 0 : } while (!JS_BufferIsCompilableUnit(cx, JS_TRUE, obj, buffer, len));
556 :
557 0 : if (hitEOF && !buffer)
558 0 : break;
559 :
560 0 : if (!JS_DecodeUTF8(cx, buffer, len, NULL, &uc_len)) {
561 0 : JS_ReportError(cx, "Invalid UTF-8 in input");
562 0 : gExitCode = EXITCODE_RUNTIME_ERROR;
563 : return;
564 : }
565 :
566 0 : uc_buffer = (jschar*)malloc(uc_len * sizeof(jschar));
567 0 : JS_DecodeUTF8(cx, buffer, len, uc_buffer, &uc_len);
568 :
569 : /* Clear any pending exception from previous failed compiles. */
570 0 : JS_ClearPendingException(cx);
571 :
572 : /* Even though we're interactive, we have a compile-n-go opportunity. */
573 0 : oldopts = JS_GetOptions(cx);
574 0 : if (!compileOnly)
575 0 : JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO);
576 0 : script = JS_CompileUCScript(cx, obj, uc_buffer, uc_len, "typein", startline);
577 0 : if (!compileOnly)
578 0 : JS_SetOptions(cx, oldopts);
579 :
580 0 : if (script && !compileOnly) {
581 0 : ok = JS_ExecuteScript(cx, obj, script, &result);
582 0 : if (ok && !JSVAL_IS_VOID(result)) {
583 0 : str = JS_ValueToSource(cx, result);
584 0 : ok = !!str;
585 0 : if (ok) {
586 0 : JSAutoByteString bytes(cx, str);
587 0 : ok = !!bytes;
588 0 : if (ok)
589 0 : fprintf(gOutFile, "%s\n", bytes.ptr());
590 : }
591 : }
592 : }
593 0 : *buffer = '\0';
594 0 : free(uc_buffer);
595 0 : } while (!hitEOF && !gQuitting);
596 :
597 0 : free(buffer);
598 0 : fprintf(gOutFile, "\n");
599 : cleanup:
600 37332 : if (file != stdin)
601 37332 : fclose(file);
602 : return;
603 : }
604 :
605 : /*
606 : * JSContext option name to flag map. The option names are in alphabetical
607 : * order for better reporting.
608 : */
609 : static const struct JSOption {
610 : const char *name;
611 : uint32_t flag;
612 : } js_options[] = {
613 : {"atline", JSOPTION_ATLINE},
614 : {"methodjit", JSOPTION_METHODJIT},
615 : {"methodjit_always",JSOPTION_METHODJIT_ALWAYS},
616 : {"relimit", JSOPTION_RELIMIT},
617 : {"strict", JSOPTION_STRICT},
618 : {"typeinfer", JSOPTION_TYPE_INFERENCE},
619 : {"werror", JSOPTION_WERROR},
620 : {"xml", JSOPTION_XML},
621 : };
622 :
623 : static uint32_t
624 45 : MapContextOptionNameToFlag(JSContext* cx, const char* name)
625 : {
626 240 : for (size_t i = 0; i < ArrayLength(js_options); ++i) {
627 240 : if (strcmp(name, js_options[i].name) == 0)
628 45 : return js_options[i].flag;
629 : }
630 :
631 : char* msg = JS_sprintf_append(NULL,
632 : "unknown option name '%s'."
633 0 : " The valid names are ", name);
634 0 : for (size_t i = 0; i < ArrayLength(js_options); ++i) {
635 0 : if (!msg)
636 0 : break;
637 : msg = JS_sprintf_append(msg, "%s%s", js_options[i].name,
638 0 : (i + 2 < ArrayLength(js_options)
639 : ? ", "
640 0 : : i + 2 == ArrayLength(js_options)
641 : ? " and "
642 0 : : "."));
643 : }
644 0 : if (!msg) {
645 0 : JS_ReportOutOfMemory(cx);
646 : } else {
647 0 : JS_ReportError(cx, msg);
648 0 : free(msg);
649 : }
650 0 : return 0;
651 : }
652 :
653 : extern JSClass global_class;
654 :
655 : static JSBool
656 54 : Version(JSContext *cx, unsigned argc, jsval *vp)
657 : {
658 54 : jsval *argv = JS_ARGV(cx, vp);
659 54 : if (argc == 0 || JSVAL_IS_VOID(argv[0])) {
660 : /* Get version. */
661 0 : *vp = INT_TO_JSVAL(JS_GetVersion(cx));
662 : } else {
663 : /* Set version. */
664 54 : int32_t v = -1;
665 54 : if (JSVAL_IS_INT(argv[0])) {
666 54 : v = JSVAL_TO_INT(argv[0]);
667 0 : } else if (JSVAL_IS_DOUBLE(argv[0])) {
668 0 : double fv = JSVAL_TO_DOUBLE(argv[0]);
669 0 : if (int32_t(fv) == fv)
670 0 : v = int32_t(fv);
671 : }
672 54 : if (v < 0 || v > JSVERSION_LATEST) {
673 0 : JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_INVALID_ARGS, "version");
674 0 : return false;
675 : }
676 54 : *vp = INT_TO_JSVAL(JS_SetVersion(cx, JSVersion(v)));
677 : }
678 54 : return true;
679 : }
680 :
681 : static JSBool
682 36 : RevertVersion(JSContext *cx, unsigned argc, jsval *vp)
683 : {
684 36 : js_RevertVersion(cx);
685 36 : JS_SET_RVAL(cx, vp, JSVAL_VOID);
686 36 : return JS_TRUE;
687 : }
688 :
689 : static JSBool
690 45 : Options(JSContext *cx, unsigned argc, jsval *vp)
691 : {
692 : uint32_t optset, flag;
693 : JSString *str;
694 : char *names;
695 : JSBool found;
696 :
697 45 : optset = 0;
698 45 : jsval *argv = JS_ARGV(cx, vp);
699 90 : for (unsigned i = 0; i < argc; i++) {
700 45 : str = JS_ValueToString(cx, argv[i]);
701 45 : if (!str)
702 0 : return JS_FALSE;
703 45 : argv[i] = STRING_TO_JSVAL(str);
704 90 : JSAutoByteString opt(cx, str);
705 45 : if (!opt)
706 0 : return JS_FALSE;
707 45 : flag = MapContextOptionNameToFlag(cx, opt.ptr());
708 45 : if (!flag)
709 0 : return JS_FALSE;
710 90 : optset |= flag;
711 : }
712 45 : optset = JS_ToggleOptions(cx, optset);
713 :
714 45 : names = NULL;
715 45 : found = JS_FALSE;
716 405 : for (size_t i = 0; i < ArrayLength(js_options); i++) {
717 360 : if (js_options[i].flag & optset) {
718 90 : found = JS_TRUE;
719 : names = JS_sprintf_append(names, "%s%s",
720 90 : names ? "," : "", js_options[i].name);
721 90 : if (!names)
722 0 : break;
723 : }
724 : }
725 45 : if (!found)
726 3 : names = strdup("");
727 45 : if (!names) {
728 0 : JS_ReportOutOfMemory(cx);
729 0 : return JS_FALSE;
730 : }
731 45 : str = JS_NewStringCopyZ(cx, names);
732 45 : free(names);
733 45 : if (!str)
734 0 : return JS_FALSE;
735 45 : *vp = STRING_TO_JSVAL(str);
736 45 : return JS_TRUE;
737 : }
738 :
739 : static JSBool
740 945 : Load(JSContext *cx, unsigned argc, jsval *vp)
741 : {
742 945 : JSObject *thisobj = JS_THIS_OBJECT(cx, vp);
743 945 : if (!thisobj)
744 0 : return JS_FALSE;
745 :
746 945 : jsval *argv = JS_ARGV(cx, vp);
747 1890 : for (unsigned i = 0; i < argc; i++) {
748 945 : JSString *str = JS_ValueToString(cx, argv[i]);
749 945 : if (!str)
750 0 : return false;
751 945 : argv[i] = STRING_TO_JSVAL(str);
752 1890 : JSAutoByteString filename(cx, str);
753 945 : if (!filename)
754 0 : return JS_FALSE;
755 945 : errno = 0;
756 945 : uint32_t oldopts = JS_GetOptions(cx);
757 945 : JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO | JSOPTION_NO_SCRIPT_RVAL);
758 945 : JSScript *script = JS_CompileUTF8File(cx, thisobj, filename.ptr());
759 945 : JS_SetOptions(cx, oldopts);
760 945 : if (!script)
761 0 : return false;
762 :
763 945 : if (!compileOnly && !JS_ExecuteScript(cx, thisobj, script, NULL))
764 0 : return false;
765 : }
766 :
767 945 : JS_SET_RVAL(cx, vp, JSVAL_VOID);
768 945 : return true;
769 : }
770 :
771 : static JSBool
772 0 : EvaluateWithLocation(JSContext *cx, unsigned argc, jsval *vp)
773 : {
774 0 : if (argc != 3) {
775 : JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
776 : argc > 3 ? JSSMSG_TOO_MANY_ARGS : JSSMSG_NOT_ENOUGH_ARGS,
777 0 : "evalWithLocation");
778 0 : return false;
779 : }
780 :
781 0 : JSString *code = JS_ValueToString(cx, JS_ARGV(cx, vp)[0]);
782 0 : if (!code)
783 0 : return false;
784 0 : JS::Anchor<JSString *> a_code(code);
785 :
786 : size_t codeLength;
787 0 : const jschar *codeChars = JS_GetStringCharsAndLength(cx, code, &codeLength);
788 0 : if (!codeChars)
789 0 : return false;
790 :
791 0 : JSString *filename = JS_ValueToString(cx, JS_ARGV(cx, vp)[1]);
792 0 : if (!filename)
793 0 : return false;
794 :
795 : uint32_t lineno;
796 0 : if (!JS_ValueToECMAUint32(cx, JS_ARGV(cx, vp)[2], &lineno))
797 0 : return false;
798 :
799 0 : JSObject *thisobj = JS_THIS_OBJECT(cx, vp);
800 0 : if (!thisobj)
801 0 : return false;
802 :
803 0 : if ((JS_GetClass(thisobj)->flags & JSCLASS_IS_GLOBAL) != JSCLASS_IS_GLOBAL) {
804 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_UNEXPECTED_TYPE,
805 0 : "this-value passed to evalWithLocation()", "not a global object");
806 0 : return false;
807 : }
808 :
809 0 : char *filenameBytes = JS_EncodeString(cx, filename);
810 0 : if (!filenameBytes)
811 0 : return false;
812 :
813 : jsval rval;
814 0 : bool ok = JS_EvaluateUCScript(cx, thisobj, codeChars, codeLength, filenameBytes, lineno, &rval);
815 0 : JS_free(cx, filenameBytes);
816 :
817 0 : if (!ok)
818 0 : return false;
819 :
820 0 : JS_SET_RVAL(cx, vp, rval);
821 0 : return true;
822 : }
823 :
824 : static JSBool
825 333 : Evaluate(JSContext *cx, unsigned argc, jsval *vp)
826 : {
827 333 : if (argc != 1 || !JSVAL_IS_STRING(JS_ARGV(cx, vp)[0])) {
828 : JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
829 : (argc != 1) ? JSSMSG_NOT_ENOUGH_ARGS : JSSMSG_INVALID_ARGS,
830 0 : "evaluate");
831 0 : return false;
832 : }
833 :
834 333 : JSString *code = JSVAL_TO_STRING(JS_ARGV(cx, vp)[0]);
835 :
836 : size_t codeLength;
837 333 : const jschar *codeChars = JS_GetStringCharsAndLength(cx, code, &codeLength);
838 333 : if (!codeChars)
839 0 : return false;
840 :
841 333 : JSObject *thisobj = JS_THIS_OBJECT(cx, vp);
842 333 : if (!thisobj)
843 0 : return false;
844 :
845 333 : if ((JS_GetClass(thisobj)->flags & JSCLASS_IS_GLOBAL) != JSCLASS_IS_GLOBAL) {
846 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_UNEXPECTED_TYPE,
847 0 : "this-value passed to evaluate()", "not a global object");
848 0 : return false;
849 : }
850 :
851 333 : return JS_EvaluateUCScript(cx, thisobj, codeChars, codeLength, "@evaluate", 0, vp);
852 : }
853 :
854 : static JSString *
855 0 : FileAsString(JSContext *cx, const char *pathname)
856 : {
857 : FILE *file;
858 0 : JSString *str = NULL;
859 : size_t len, cc;
860 : char *buf;
861 :
862 0 : file = fopen(pathname, "rb");
863 0 : if (!file) {
864 0 : JS_ReportError(cx, "can't open %s: %s", pathname, strerror(errno));
865 0 : return NULL;
866 : }
867 :
868 0 : if (fseek(file, 0, SEEK_END) != 0) {
869 0 : JS_ReportError(cx, "can't seek end of %s", pathname);
870 : } else {
871 0 : len = ftell(file);
872 0 : if (fseek(file, 0, SEEK_SET) != 0) {
873 0 : JS_ReportError(cx, "can't seek start of %s", pathname);
874 : } else {
875 0 : buf = (char*) JS_malloc(cx, len + 1);
876 0 : if (buf) {
877 0 : cc = fread(buf, 1, len, file);
878 0 : if (cc != len) {
879 : JS_ReportError(cx, "can't read %s: %s", pathname,
880 0 : (ptrdiff_t(cc) < 0) ? strerror(errno) : "short read");
881 : } else {
882 : jschar *ucbuf;
883 : size_t uclen;
884 :
885 0 : len = (size_t)cc;
886 :
887 0 : if (!JS_DecodeUTF8(cx, buf, len, NULL, &uclen)) {
888 0 : JS_ReportError(cx, "Invalid UTF-8 in file '%s'", pathname);
889 0 : gExitCode = EXITCODE_RUNTIME_ERROR;
890 0 : return NULL;
891 : }
892 :
893 0 : ucbuf = (jschar*)malloc(uclen * sizeof(jschar));
894 0 : JS_DecodeUTF8(cx, buf, len, ucbuf, &uclen);
895 0 : str = JS_NewUCStringCopyN(cx, ucbuf, uclen);
896 0 : free(ucbuf);
897 : }
898 0 : JS_free(cx, buf);
899 : }
900 : }
901 : }
902 0 : fclose(file);
903 :
904 0 : return str;
905 : }
906 :
907 : static JSObject *
908 0 : FileAsTypedArray(JSContext *cx, const char *pathname)
909 : {
910 0 : FILE *file = fopen(pathname, "rb");
911 0 : if (!file) {
912 0 : JS_ReportError(cx, "can't open %s: %s", pathname, strerror(errno));
913 0 : return NULL;
914 : }
915 :
916 0 : JSObject *obj = NULL;
917 0 : if (fseek(file, 0, SEEK_END) != 0) {
918 0 : JS_ReportError(cx, "can't seek end of %s", pathname);
919 : } else {
920 0 : size_t len = ftell(file);
921 0 : if (fseek(file, 0, SEEK_SET) != 0) {
922 0 : JS_ReportError(cx, "can't seek start of %s", pathname);
923 : } else {
924 0 : obj = js_CreateTypedArray(cx, TypedArray::TYPE_UINT8, len);
925 0 : if (!obj)
926 0 : return NULL;
927 0 : char *buf = (char *) TypedArray::getDataOffset(TypedArray::getTypedArray(obj));
928 0 : size_t cc = fread(buf, 1, len, file);
929 0 : if (cc != len) {
930 : JS_ReportError(cx, "can't read %s: %s", pathname,
931 0 : (ptrdiff_t(cc) < 0) ? strerror(errno) : "short read");
932 0 : obj = NULL;
933 : }
934 : }
935 : }
936 0 : fclose(file);
937 0 : return obj;
938 : }
939 :
940 : /*
941 : * Function to run scripts and return compilation + execution time. Semantics
942 : * are closely modelled after the equivalent function in WebKit, as this is used
943 : * to produce benchmark timings by SunSpider.
944 : */
945 : static JSBool
946 0 : Run(JSContext *cx, unsigned argc, jsval *vp)
947 : {
948 0 : if (argc != 1) {
949 0 : JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_INVALID_ARGS, "run");
950 0 : return false;
951 : }
952 :
953 0 : JSObject *thisobj = JS_THIS_OBJECT(cx, vp);
954 0 : if (!thisobj)
955 0 : return false;
956 :
957 0 : jsval *argv = JS_ARGV(cx, vp);
958 0 : JSString *str = JS_ValueToString(cx, argv[0]);
959 0 : if (!str)
960 0 : return false;
961 0 : argv[0] = STRING_TO_JSVAL(str);
962 0 : JSAutoByteString filename(cx, str);
963 0 : if (!filename)
964 0 : return false;
965 :
966 0 : const jschar *ucbuf = NULL;
967 : size_t buflen;
968 0 : str = FileAsString(cx, filename.ptr());
969 0 : if (str)
970 0 : ucbuf = JS_GetStringCharsAndLength(cx, str, &buflen);
971 0 : if (!ucbuf)
972 0 : return false;
973 :
974 0 : JS::Anchor<JSString *> a_str(str);
975 0 : uint32_t oldopts = JS_GetOptions(cx);
976 0 : JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO | JSOPTION_NO_SCRIPT_RVAL);
977 :
978 0 : int64_t startClock = PRMJ_Now();
979 0 : JSScript *script = JS_CompileUCScript(cx, thisobj, ucbuf, buflen, filename.ptr(), 1);
980 0 : JS_SetOptions(cx, oldopts);
981 0 : if (!script || !JS_ExecuteScript(cx, thisobj, script, NULL))
982 0 : return false;
983 :
984 0 : int64_t endClock = PRMJ_Now();
985 0 : JS_SET_RVAL(cx, vp, DOUBLE_TO_JSVAL((endClock - startClock) / double(PRMJ_USEC_PER_MSEC)));
986 0 : return true;
987 : }
988 :
989 : /*
990 : * function readline()
991 : * Provides a hook for scripts to read a line from stdin.
992 : */
993 : static JSBool
994 0 : ReadLine(JSContext *cx, unsigned argc, jsval *vp)
995 : {
996 : #define BUFSIZE 256
997 : FILE *from;
998 : char *buf, *tmp;
999 : size_t bufsize, buflength, gotlength;
1000 : JSBool sawNewline;
1001 : JSString *str;
1002 :
1003 0 : from = stdin;
1004 0 : buflength = 0;
1005 0 : bufsize = BUFSIZE;
1006 0 : buf = (char *) JS_malloc(cx, bufsize);
1007 0 : if (!buf)
1008 0 : return JS_FALSE;
1009 :
1010 0 : sawNewline = JS_FALSE;
1011 0 : while ((gotlength =
1012 0 : js_fgets(buf + buflength, bufsize - buflength, from)) > 0) {
1013 0 : buflength += gotlength;
1014 :
1015 : /* Are we done? */
1016 0 : if (buf[buflength - 1] == '\n') {
1017 0 : buf[buflength - 1] = '\0';
1018 0 : sawNewline = JS_TRUE;
1019 0 : break;
1020 0 : } else if (buflength < bufsize - 1) {
1021 0 : break;
1022 : }
1023 :
1024 : /* Else, grow our buffer for another pass. */
1025 0 : bufsize *= 2;
1026 0 : if (bufsize > buflength) {
1027 0 : tmp = (char *) JS_realloc(cx, buf, bufsize);
1028 : } else {
1029 0 : JS_ReportOutOfMemory(cx);
1030 0 : tmp = NULL;
1031 : }
1032 :
1033 0 : if (!tmp) {
1034 0 : JS_free(cx, buf);
1035 0 : return JS_FALSE;
1036 : }
1037 :
1038 0 : buf = tmp;
1039 : }
1040 :
1041 : /* Treat the empty string specially. */
1042 0 : if (buflength == 0) {
1043 0 : *vp = feof(from) ? JSVAL_NULL : JS_GetEmptyStringValue(cx);
1044 0 : JS_free(cx, buf);
1045 0 : return JS_TRUE;
1046 : }
1047 :
1048 : /* Shrink the buffer to the real size. */
1049 0 : tmp = (char *) JS_realloc(cx, buf, buflength);
1050 0 : if (!tmp) {
1051 0 : JS_free(cx, buf);
1052 0 : return JS_FALSE;
1053 : }
1054 :
1055 0 : buf = tmp;
1056 :
1057 : /*
1058 : * Turn buf into a JSString. Note that buflength includes the trailing null
1059 : * character.
1060 : */
1061 0 : str = JS_NewStringCopyN(cx, buf, sawNewline ? buflength - 1 : buflength);
1062 0 : JS_free(cx, buf);
1063 0 : if (!str)
1064 0 : return JS_FALSE;
1065 :
1066 0 : *vp = STRING_TO_JSVAL(str);
1067 0 : return JS_TRUE;
1068 : }
1069 :
1070 : static JSBool
1071 0 : PutStr(JSContext *cx, unsigned argc, jsval *vp)
1072 : {
1073 : jsval *argv;
1074 : JSString *str;
1075 : char *bytes;
1076 :
1077 0 : if (argc != 0) {
1078 0 : argv = JS_ARGV(cx, vp);
1079 0 : str = JS_ValueToString(cx, argv[0]);
1080 0 : if (!str)
1081 0 : return JS_FALSE;
1082 0 : bytes = JS_EncodeString(cx, str);
1083 0 : if (!bytes)
1084 0 : return JS_FALSE;
1085 0 : fputs(bytes, gOutFile);
1086 0 : JS_free(cx, bytes);
1087 0 : fflush(gOutFile);
1088 : }
1089 :
1090 0 : JS_SET_RVAL(cx, vp, JSVAL_VOID);
1091 0 : return JS_TRUE;
1092 : }
1093 :
1094 : static JSBool
1095 0 : Now(JSContext *cx, unsigned argc, jsval *vp)
1096 : {
1097 0 : double now = PRMJ_Now() / double(PRMJ_USEC_PER_MSEC);
1098 0 : JS_SET_RVAL(cx, vp, DOUBLE_TO_JSVAL(now));
1099 0 : return true;
1100 : }
1101 :
1102 : static JSBool
1103 9828 : PrintInternal(JSContext *cx, unsigned argc, jsval *vp, FILE *file)
1104 : {
1105 : jsval *argv;
1106 : unsigned i;
1107 : JSString *str;
1108 : char *bytes;
1109 :
1110 9828 : argv = JS_ARGV(cx, vp);
1111 32607 : for (i = 0; i < argc; i++) {
1112 22779 : str = JS_ValueToString(cx, argv[i]);
1113 22779 : if (!str)
1114 0 : return JS_FALSE;
1115 22779 : bytes = JS_EncodeString(cx, str);
1116 22779 : if (!bytes)
1117 0 : return JS_FALSE;
1118 22779 : fprintf(file, "%s%s", i ? " " : "", bytes);
1119 22779 : JS_free(cx, bytes);
1120 : }
1121 :
1122 9828 : fputc('\n', file);
1123 9828 : fflush(file);
1124 :
1125 9828 : JS_SET_RVAL(cx, vp, JSVAL_VOID);
1126 9828 : return JS_TRUE;
1127 : }
1128 :
1129 : static JSBool
1130 9828 : Print(JSContext *cx, unsigned argc, jsval *vp)
1131 : {
1132 9828 : return PrintInternal(cx, argc, vp, gOutFile);
1133 : }
1134 :
1135 : static JSBool
1136 0 : PrintErr(JSContext *cx, unsigned argc, jsval *vp)
1137 : {
1138 0 : return PrintInternal(cx, argc, vp, gErrFile);
1139 : }
1140 :
1141 : static JSBool
1142 : Help(JSContext *cx, unsigned argc, jsval *vp);
1143 :
1144 : static JSBool
1145 27 : Quit(JSContext *cx, unsigned argc, jsval *vp)
1146 : {
1147 27 : JS_ConvertArguments(cx, argc, JS_ARGV(cx, vp), "/ i", &gExitCode);
1148 :
1149 27 : gQuitting = JS_TRUE;
1150 : #ifdef JS_THREADSAFE
1151 27 : if (gWorkerThreadPool)
1152 27 : js::workers::terminateAll(gWorkerThreadPool);
1153 : #endif
1154 27 : return JS_FALSE;
1155 : }
1156 :
1157 : static const char *
1158 486 : ToSource(JSContext *cx, jsval *vp, JSAutoByteString *bytes)
1159 : {
1160 486 : JSString *str = JS_ValueToSource(cx, *vp);
1161 486 : if (str) {
1162 486 : *vp = STRING_TO_JSVAL(str);
1163 486 : if (bytes->encode(cx, str))
1164 486 : return bytes->ptr();
1165 : }
1166 0 : JS_ClearPendingException(cx);
1167 0 : return "<<error converting value to string>>";
1168 : }
1169 :
1170 : static JSBool
1171 4106291 : AssertEq(JSContext *cx, unsigned argc, jsval *vp)
1172 : {
1173 4106291 : if (!(argc == 2 || (argc == 3 && JSVAL_IS_STRING(JS_ARGV(cx, vp)[2])))) {
1174 : JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
1175 : (argc < 2)
1176 : ? JSSMSG_NOT_ENOUGH_ARGS
1177 : : (argc == 3)
1178 : ? JSSMSG_INVALID_ARGS
1179 : : JSSMSG_TOO_MANY_ARGS,
1180 27 : "assertEq");
1181 27 : return JS_FALSE;
1182 : }
1183 :
1184 4106264 : jsval *argv = JS_ARGV(cx, vp);
1185 : JSBool same;
1186 4106264 : if (!JS_SameValue(cx, argv[0], argv[1], &same))
1187 0 : return JS_FALSE;
1188 4106264 : if (!same) {
1189 486 : JSAutoByteString bytes0, bytes1;
1190 243 : const char *actual = ToSource(cx, &argv[0], &bytes0);
1191 243 : const char *expected = ToSource(cx, &argv[1], &bytes1);
1192 243 : if (argc == 2) {
1193 : JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_ASSERT_EQ_FAILED,
1194 243 : actual, expected);
1195 : } else {
1196 0 : JSAutoByteString bytes2(cx, JSVAL_TO_STRING(argv[2]));
1197 0 : if (!bytes2)
1198 0 : return JS_FALSE;
1199 : JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_ASSERT_EQ_FAILED_MSG,
1200 0 : actual, expected, bytes2.ptr());
1201 : }
1202 243 : return JS_FALSE;
1203 : }
1204 4106021 : JS_SET_RVAL(cx, vp, JSVAL_VOID);
1205 4106021 : return JS_TRUE;
1206 : }
1207 :
1208 : static JSBool
1209 81 : AssertJit(JSContext *cx, unsigned argc, jsval *vp)
1210 : {
1211 : #ifdef JS_METHODJIT
1212 81 : if (JS_GetOptions(cx) & JSOPTION_METHODJIT) {
1213 : /*
1214 : * :XXX: Ignore calls to this native when inference is enabled,
1215 : * with METHODJIT_ALWAYS recompilation can happen and discard the
1216 : * script's jitcode.
1217 : */
1218 90 : if (!cx->typeInferenceEnabled() &&
1219 27 : !cx->fp()->script()->getJIT(cx->fp()->isConstructing())) {
1220 0 : JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_ASSERT_JIT_FAILED);
1221 0 : return JS_FALSE;
1222 : }
1223 : }
1224 : #endif
1225 :
1226 81 : JS_SET_RVAL(cx, vp, JSVAL_VOID);
1227 81 : return JS_TRUE;
1228 : }
1229 :
1230 : static JSScript *
1231 441 : ValueToScript(JSContext *cx, jsval v, JSFunction **funp = NULL)
1232 : {
1233 441 : JSScript *script = NULL;
1234 441 : JSFunction *fun = NULL;
1235 :
1236 441 : if (!JSVAL_IS_PRIMITIVE(v)) {
1237 441 : JSObject *obj = JSVAL_TO_OBJECT(v);
1238 441 : JSClass *clasp = JS_GetClass(obj);
1239 :
1240 441 : if (clasp == Jsvalify(&GeneratorClass)) {
1241 9 : if (JSGenerator *gen = (JSGenerator *) JS_GetPrivate(obj)) {
1242 0 : fun = gen->floatingFrame()->fun();
1243 0 : script = fun->script();
1244 : }
1245 : }
1246 : }
1247 :
1248 441 : if (!script) {
1249 441 : fun = JS_ValueToFunction(cx, v);
1250 441 : if (!fun)
1251 9 : return NULL;
1252 432 : script = fun->maybeScript();
1253 432 : if (!script) {
1254 : JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
1255 0 : JSSMSG_SCRIPTS_ONLY);
1256 : }
1257 : }
1258 432 : if (fun && funp)
1259 18 : *funp = fun;
1260 :
1261 432 : return script;
1262 : }
1263 :
1264 : static JSBool
1265 279 : SetDebug(JSContext *cx, unsigned argc, jsval *vp)
1266 : {
1267 279 : jsval *argv = JS_ARGV(cx, vp);
1268 279 : if (argc == 0 || !JSVAL_IS_BOOLEAN(argv[0])) {
1269 : JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
1270 0 : JSSMSG_NOT_ENOUGH_ARGS, "setDebug");
1271 0 : return JS_FALSE;
1272 : }
1273 :
1274 : /*
1275 : * Debug mode can only be set when there is no JS code executing on the
1276 : * stack. Unfortunately, that currently means that this call will fail
1277 : * unless debug mode is already set to what you're trying to set it to.
1278 : * In the future, this restriction may be lifted.
1279 : */
1280 :
1281 279 : JSBool ok = JS_SetDebugMode(cx, JSVAL_TO_BOOLEAN(argv[0]));
1282 279 : if (ok)
1283 279 : JS_SET_RVAL(cx, vp, JSVAL_TRUE);
1284 279 : return ok;
1285 : }
1286 :
1287 : static JSScript *
1288 423 : GetTopScript(JSContext *cx)
1289 : {
1290 : JSScript *script;
1291 423 : JS_DescribeScriptedCaller(cx, &script, NULL);
1292 423 : return script;
1293 : }
1294 :
1295 : static JSBool
1296 414 : GetScriptAndPCArgs(JSContext *cx, unsigned argc, jsval *argv, JSScript **scriptp,
1297 : int32_t *ip)
1298 : {
1299 414 : JSScript *script = GetTopScript(cx);
1300 414 : *ip = 0;
1301 414 : if (argc != 0) {
1302 405 : jsval v = argv[0];
1303 405 : unsigned intarg = 0;
1304 810 : if (!JSVAL_IS_PRIMITIVE(v) &&
1305 405 : JS_GetClass(JSVAL_TO_OBJECT(v)) == Jsvalify(&FunctionClass)) {
1306 405 : script = ValueToScript(cx, v);
1307 405 : if (!script)
1308 0 : return JS_FALSE;
1309 405 : intarg++;
1310 : }
1311 405 : if (argc > intarg) {
1312 405 : if (!JS_ValueToInt32(cx, argv[intarg], ip))
1313 0 : return JS_FALSE;
1314 405 : if ((uint32_t)*ip >= script->length) {
1315 0 : JS_ReportError(cx, "Invalid PC");
1316 0 : return JS_FALSE;
1317 : }
1318 : }
1319 : }
1320 :
1321 414 : *scriptp = script;
1322 :
1323 414 : return JS_TRUE;
1324 : }
1325 :
1326 : static JSTrapStatus
1327 378 : TrapHandler(JSContext *cx, JSScript *, jsbytecode *pc, jsval *rval,
1328 : jsval closure)
1329 : {
1330 378 : JSString *str = JSVAL_TO_STRING(closure);
1331 :
1332 378 : FrameRegsIter iter(cx);
1333 378 : JS_ASSERT(!iter.done());
1334 :
1335 378 : JSStackFrame *caller = Jsvalify(iter.fp());
1336 378 : JSScript *script = iter.script();
1337 :
1338 : size_t length;
1339 378 : const jschar *chars = JS_GetStringCharsAndLength(cx, str, &length);
1340 378 : if (!chars)
1341 0 : return JSTRAP_ERROR;
1342 :
1343 378 : if (!JS_EvaluateUCInStackFrame(cx, caller, chars, length,
1344 : script->filename,
1345 : script->lineno,
1346 378 : rval)) {
1347 0 : return JSTRAP_ERROR;
1348 : }
1349 378 : if (!JSVAL_IS_VOID(*rval))
1350 63 : return JSTRAP_RETURN;
1351 315 : return JSTRAP_CONTINUE;
1352 : }
1353 :
1354 : static JSBool
1355 369 : Trap(JSContext *cx, unsigned argc, jsval *vp)
1356 : {
1357 : JSString *str;
1358 : JSScript *script;
1359 : int32_t i;
1360 :
1361 369 : jsval *argv = JS_ARGV(cx, vp);
1362 369 : if (argc == 0) {
1363 0 : JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_TRAP_USAGE);
1364 0 : return JS_FALSE;
1365 : }
1366 369 : argc--;
1367 369 : str = JS_ValueToString(cx, argv[argc]);
1368 369 : if (!str)
1369 0 : return JS_FALSE;
1370 369 : argv[argc] = STRING_TO_JSVAL(str);
1371 369 : if (!GetScriptAndPCArgs(cx, argc, argv, &script, &i))
1372 0 : return JS_FALSE;
1373 369 : if (uint32_t(i) >= script->length) {
1374 0 : JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_TRAP_USAGE);
1375 0 : return JS_FALSE;
1376 : }
1377 369 : JS_SET_RVAL(cx, vp, JSVAL_VOID);
1378 369 : return JS_SetTrap(cx, script, script->code + i, TrapHandler, STRING_TO_JSVAL(str));
1379 : }
1380 :
1381 : static JSBool
1382 36 : Untrap(JSContext *cx, unsigned argc, jsval *vp)
1383 : {
1384 : JSScript *script;
1385 : int32_t i;
1386 :
1387 36 : if (!GetScriptAndPCArgs(cx, argc, JS_ARGV(cx, vp), &script, &i))
1388 0 : return JS_FALSE;
1389 36 : JS_ClearTrap(cx, script, script->code + i, NULL, NULL);
1390 36 : JS_SET_RVAL(cx, vp, JSVAL_VOID);
1391 36 : return JS_TRUE;
1392 : }
1393 :
1394 : static JSTrapStatus
1395 36 : DebuggerAndThrowHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval,
1396 : void *closure)
1397 : {
1398 36 : return TrapHandler(cx, script, pc, rval, STRING_TO_JSVAL((JSString *)closure));
1399 : }
1400 :
1401 : static JSBool
1402 18 : SetDebuggerHandler(JSContext *cx, unsigned argc, jsval *vp)
1403 : {
1404 : JSString *str;
1405 18 : if (argc == 0) {
1406 : JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
1407 0 : JSSMSG_NOT_ENOUGH_ARGS, "setDebuggerHandler");
1408 0 : return JS_FALSE;
1409 : }
1410 :
1411 18 : str = JS_ValueToString(cx, JS_ARGV(cx, vp)[0]);
1412 18 : if (!str)
1413 0 : return JS_FALSE;
1414 :
1415 18 : JS_SetDebuggerHandler(cx->runtime, DebuggerAndThrowHandler, str);
1416 18 : JS_SET_RVAL(cx, vp, JSVAL_VOID);
1417 18 : return JS_TRUE;
1418 : }
1419 :
1420 : static JSBool
1421 18 : SetThrowHook(JSContext *cx, unsigned argc, jsval *vp)
1422 : {
1423 : JSString *str;
1424 18 : if (argc == 0) {
1425 : JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
1426 0 : JSSMSG_NOT_ENOUGH_ARGS, "setThrowHook");
1427 0 : return JS_FALSE;
1428 : }
1429 :
1430 18 : str = JS_ValueToString(cx, JS_ARGV(cx, vp)[0]);
1431 18 : if (!str)
1432 0 : return JS_FALSE;
1433 :
1434 18 : JS_SetThrowHook(cx->runtime, DebuggerAndThrowHandler, str);
1435 18 : JS_SET_RVAL(cx, vp, JSVAL_VOID);
1436 18 : return JS_TRUE;
1437 : }
1438 :
1439 : static JSBool
1440 9 : LineToPC(JSContext *cx, unsigned argc, jsval *vp)
1441 : {
1442 : JSScript *script;
1443 9 : int32_t lineArg = 0;
1444 : uint32_t lineno;
1445 : jsbytecode *pc;
1446 :
1447 9 : if (argc == 0) {
1448 0 : JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_LINE2PC_USAGE);
1449 0 : return JS_FALSE;
1450 : }
1451 9 : script = GetTopScript(cx);
1452 9 : jsval v = JS_ARGV(cx, vp)[0];
1453 18 : if (!JSVAL_IS_PRIMITIVE(v) &&
1454 9 : JS_GetClass(JSVAL_TO_OBJECT(v)) == Jsvalify(&FunctionClass))
1455 : {
1456 9 : script = ValueToScript(cx, v);
1457 9 : if (!script)
1458 0 : return JS_FALSE;
1459 9 : lineArg++;
1460 : }
1461 9 : if (!JS_ValueToECMAUint32(cx, JS_ARGV(cx, vp)[lineArg], &lineno))
1462 0 : return JS_FALSE;
1463 9 : pc = JS_LineNumberToPC(cx, script, lineno);
1464 9 : if (!pc)
1465 0 : return JS_FALSE;
1466 9 : *vp = INT_TO_JSVAL(pc - script->code);
1467 9 : return JS_TRUE;
1468 : }
1469 :
1470 : static JSBool
1471 9 : PCToLine(JSContext *cx, unsigned argc, jsval *vp)
1472 : {
1473 : JSScript *script;
1474 : int32_t i;
1475 : unsigned lineno;
1476 :
1477 9 : if (!GetScriptAndPCArgs(cx, argc, JS_ARGV(cx, vp), &script, &i))
1478 0 : return JS_FALSE;
1479 9 : lineno = JS_PCToLineNumber(cx, script, script->code + i);
1480 9 : if (!lineno)
1481 0 : return JS_FALSE;
1482 9 : *vp = INT_TO_JSVAL(lineno);
1483 9 : return JS_TRUE;
1484 : }
1485 :
1486 : #ifdef DEBUG
1487 :
1488 : static void
1489 9 : UpdateSwitchTableBounds(JSContext *cx, JSScript *script, unsigned offset,
1490 : unsigned *start, unsigned *end)
1491 : {
1492 : jsbytecode *pc;
1493 : JSOp op;
1494 : ptrdiff_t jmplen;
1495 : int32_t low, high, n;
1496 :
1497 9 : pc = script->code + offset;
1498 9 : op = JSOp(*pc);
1499 9 : switch (op) {
1500 : case JSOP_TABLESWITCH:
1501 9 : jmplen = JUMP_OFFSET_LEN;
1502 9 : pc += jmplen;
1503 9 : low = GET_JUMP_OFFSET(pc);
1504 9 : pc += JUMP_OFFSET_LEN;
1505 9 : high = GET_JUMP_OFFSET(pc);
1506 9 : pc += JUMP_OFFSET_LEN;
1507 9 : n = high - low + 1;
1508 9 : break;
1509 :
1510 : case JSOP_LOOKUPSWITCH:
1511 0 : jmplen = JUMP_OFFSET_LEN;
1512 0 : pc += jmplen;
1513 0 : n = GET_UINT16(pc);
1514 0 : pc += UINT16_LEN;
1515 0 : jmplen += JUMP_OFFSET_LEN;
1516 0 : break;
1517 :
1518 : default:
1519 : /* [condswitch] switch does not have any jump or lookup tables. */
1520 0 : JS_ASSERT(op == JSOP_CONDSWITCH);
1521 0 : return;
1522 : }
1523 :
1524 9 : *start = (unsigned)(pc - script->code);
1525 9 : *end = *start + (unsigned)(n * jmplen);
1526 : }
1527 :
1528 : static void
1529 18 : SrcNotes(JSContext *cx, JSScript *script, Sprinter *sp)
1530 : {
1531 18 : Sprint(sp, "\nSource notes:\n");
1532 : Sprint(sp, "%4s %4s %5s %6s %-8s %s\n",
1533 18 : "ofs", "line", "pc", "delta", "desc", "args");
1534 18 : Sprint(sp, "---- ---- ----- ------ -------- ------\n");
1535 18 : unsigned offset = 0;
1536 18 : unsigned lineno = script->lineno;
1537 18 : jssrcnote *notes = script->notes();
1538 18 : unsigned switchTableEnd = 0, switchTableStart = 0;
1539 110664 : for (jssrcnote *sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
1540 110646 : unsigned delta = SN_DELTA(sn);
1541 110646 : offset += delta;
1542 110646 : SrcNoteType type = (SrcNoteType) SN_TYPE(sn);
1543 110646 : const char *name = js_SrcNoteSpec[type].name;
1544 110646 : if (type == SRC_LABEL) {
1545 : /* Check if the source note is for a switch case. */
1546 0 : if (switchTableStart <= offset && offset < switchTableEnd) {
1547 0 : name = "case";
1548 : } else {
1549 0 : JSOp op = JSOp(script->code[offset]);
1550 0 : JS_ASSERT(op == JSOP_LABEL);
1551 : }
1552 : }
1553 110646 : Sprint(sp, "%3u: %4u %5u [%4u] %-8s", unsigned(sn - notes), lineno, offset, delta, name);
1554 110646 : switch (type) {
1555 : case SRC_SETLINE:
1556 0 : lineno = js_GetSrcNoteOffset(sn, 0);
1557 0 : Sprint(sp, " lineno %u", lineno);
1558 0 : break;
1559 : case SRC_NEWLINE:
1560 36 : ++lineno;
1561 36 : break;
1562 : case SRC_FOR:
1563 : Sprint(sp, " cond %u update %u tail %u",
1564 0 : unsigned(js_GetSrcNoteOffset(sn, 0)),
1565 0 : unsigned(js_GetSrcNoteOffset(sn, 1)),
1566 0 : unsigned(js_GetSrcNoteOffset(sn, 2)));
1567 0 : break;
1568 : case SRC_IF_ELSE:
1569 : Sprint(sp, " else %u elseif %u",
1570 0 : unsigned(js_GetSrcNoteOffset(sn, 0)),
1571 0 : unsigned(js_GetSrcNoteOffset(sn, 1)));
1572 0 : break;
1573 : case SRC_COND:
1574 : case SRC_WHILE:
1575 : case SRC_PCBASE:
1576 : case SRC_PCDELTA:
1577 : case SRC_DECL:
1578 : case SRC_BRACE:
1579 36 : Sprint(sp, " offset %u", unsigned(js_GetSrcNoteOffset(sn, 0)));
1580 36 : break;
1581 : case SRC_LABEL:
1582 : case SRC_LABELBRACE:
1583 : case SRC_BREAK2LABEL:
1584 : case SRC_CONT2LABEL: {
1585 0 : uint32_t index = js_GetSrcNoteOffset(sn, 0);
1586 0 : JSAtom *atom = script->getAtom(index);
1587 0 : Sprint(sp, " atom %u (", index);
1588 0 : size_t len = PutEscapedString(NULL, 0, atom, '\0');
1589 0 : if (char *buf = sp->reserve(len)) {
1590 0 : PutEscapedString(buf, len, atom, 0);
1591 0 : buf[len] = 0;
1592 : }
1593 0 : Sprint(sp, ")");
1594 0 : break;
1595 : }
1596 : case SRC_FUNCDEF: {
1597 0 : uint32_t index = js_GetSrcNoteOffset(sn, 0);
1598 0 : JSObject *obj = script->getObject(index);
1599 0 : JSFunction *fun = obj->toFunction();
1600 0 : JSString *str = JS_DecompileFunction(cx, fun, JS_DONT_PRETTY_PRINT);
1601 0 : JSAutoByteString bytes;
1602 0 : if (!str || !bytes.encode(cx, str))
1603 0 : ReportException(cx);
1604 0 : Sprint(sp, " function %u (%s)", index, !!bytes ? bytes.ptr() : "N/A");
1605 : break;
1606 : }
1607 : case SRC_SWITCH: {
1608 18 : JSOp op = JSOp(script->code[offset]);
1609 18 : if (op == JSOP_GOTO)
1610 9 : break;
1611 9 : Sprint(sp, " length %u", unsigned(js_GetSrcNoteOffset(sn, 0)));
1612 9 : unsigned caseOff = (unsigned) js_GetSrcNoteOffset(sn, 1);
1613 9 : if (caseOff)
1614 0 : Sprint(sp, " first case offset %u", caseOff);
1615 : UpdateSwitchTableBounds(cx, script, offset,
1616 9 : &switchTableStart, &switchTableEnd);
1617 9 : break;
1618 : }
1619 : case SRC_CATCH:
1620 36846 : delta = (unsigned) js_GetSrcNoteOffset(sn, 0);
1621 36846 : if (delta) {
1622 18423 : if (script->main()[offset] == JSOP_LEAVEBLOCK)
1623 18423 : Sprint(sp, " stack depth %u", delta);
1624 : else
1625 0 : Sprint(sp, " guard delta %u", delta);
1626 : }
1627 36846 : break;
1628 : default:;
1629 : }
1630 110646 : Sprint(sp, "\n");
1631 : }
1632 18 : }
1633 :
1634 : static JSBool
1635 0 : Notes(JSContext *cx, unsigned argc, jsval *vp)
1636 : {
1637 0 : Sprinter sprinter(cx);
1638 0 : if (!sprinter.init())
1639 0 : return JS_FALSE;
1640 :
1641 0 : jsval *argv = JS_ARGV(cx, vp);
1642 0 : for (unsigned i = 0; i < argc; i++) {
1643 0 : JSScript *script = ValueToScript(cx, argv[i]);
1644 0 : if (!script)
1645 0 : continue;
1646 :
1647 0 : SrcNotes(cx, script, &sprinter);
1648 : }
1649 :
1650 0 : JSString *str = JS_NewStringCopyZ(cx, sprinter.string());
1651 0 : if (!str)
1652 0 : return JS_FALSE;
1653 0 : JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(str));
1654 0 : return JS_TRUE;
1655 : }
1656 :
1657 : JS_STATIC_ASSERT(JSTRY_CATCH == 0);
1658 : JS_STATIC_ASSERT(JSTRY_FINALLY == 1);
1659 : JS_STATIC_ASSERT(JSTRY_ITER == 2);
1660 :
1661 : static const char* const TryNoteNames[] = { "catch", "finally", "iter" };
1662 :
1663 : static JSBool
1664 18 : TryNotes(JSContext *cx, JSScript *script, Sprinter *sp)
1665 : {
1666 : JSTryNote *tn, *tnlimit;
1667 :
1668 18 : if (!JSScript::isValidOffset(script->trynotesOffset))
1669 9 : return JS_TRUE;
1670 :
1671 9 : tn = script->trynotes()->vector;
1672 9 : tnlimit = tn + script->trynotes()->length;
1673 9 : Sprint(sp, "\nException table:\nkind stack start end\n");
1674 18423 : do {
1675 18423 : JS_ASSERT(tn->kind < ArrayLength(TryNoteNames));
1676 : Sprint(sp, " %-7s %6u %8u %8u\n",
1677 18423 : TryNoteNames[tn->kind], tn->stackDepth,
1678 36846 : tn->start, tn->start + tn->length);
1679 : } while (++tn != tnlimit);
1680 9 : return JS_TRUE;
1681 : }
1682 :
1683 : static bool
1684 18 : DisassembleScript(JSContext *cx, JSScript *script, JSFunction *fun, bool lines, bool recursive,
1685 : Sprinter *sp)
1686 : {
1687 18 : if (fun && (fun->flags & ~7U)) {
1688 18 : uint16_t flags = fun->flags;
1689 18 : Sprint(sp, "flags:");
1690 :
1691 : #define SHOW_FLAG(flag) if (flags & JSFUN_##flag) Sprint(sp, " " #flag);
1692 :
1693 18 : SHOW_FLAG(LAMBDA);
1694 18 : SHOW_FLAG(HEAVYWEIGHT);
1695 18 : SHOW_FLAG(EXPR_CLOSURE);
1696 :
1697 : #undef SHOW_FLAG
1698 :
1699 18 : if (fun->isNullClosure())
1700 9 : Sprint(sp, " NULL_CLOSURE");
1701 :
1702 18 : Sprint(sp, "\n");
1703 : }
1704 :
1705 18 : if (!js_Disassemble(cx, script, lines, sp))
1706 0 : return false;
1707 18 : SrcNotes(cx, script, sp);
1708 18 : TryNotes(cx, script, sp);
1709 :
1710 18 : if (recursive && JSScript::isValidOffset(script->objectsOffset)) {
1711 9 : JSObjectArray *objects = script->objects();
1712 18432 : for (unsigned i = 0; i != objects->length; ++i) {
1713 18423 : JSObject *obj = objects->vector[i];
1714 18423 : if (obj->isFunction()) {
1715 0 : Sprint(sp, "\n");
1716 0 : JSFunction *fun = obj->toFunction();
1717 0 : JSScript *nested = fun->maybeScript();
1718 0 : if (!DisassembleScript(cx, nested, fun, lines, recursive, sp))
1719 0 : return false;
1720 : }
1721 : }
1722 : }
1723 18 : return true;
1724 : }
1725 :
1726 : namespace {
1727 :
1728 : struct DisassembleOptionParser {
1729 : unsigned argc;
1730 : jsval *argv;
1731 : bool lines;
1732 : bool recursive;
1733 :
1734 27 : DisassembleOptionParser(unsigned argc, jsval *argv)
1735 27 : : argc(argc), argv(argv), lines(false), recursive(false) {}
1736 :
1737 27 : bool parse(JSContext *cx) {
1738 : /* Read options off early arguments */
1739 63 : while (argc > 0 && JSVAL_IS_STRING(argv[0])) {
1740 9 : JSString *str = JSVAL_TO_STRING(argv[0]);
1741 9 : JSFlatString *flatStr = JS_FlattenString(cx, str);
1742 9 : if (!flatStr)
1743 0 : return false;
1744 9 : if (JS_FlatStringEqualsAscii(flatStr, "-l"))
1745 0 : lines = true;
1746 9 : else if (JS_FlatStringEqualsAscii(flatStr, "-r"))
1747 9 : recursive = true;
1748 : else
1749 0 : break;
1750 9 : argv++, argc--;
1751 : }
1752 27 : return true;
1753 : }
1754 : };
1755 :
1756 : } /* anonymous namespace */
1757 :
1758 : static JSBool
1759 9 : DisassembleToString(JSContext *cx, unsigned argc, jsval *vp)
1760 : {
1761 9 : DisassembleOptionParser p(argc, JS_ARGV(cx, vp));
1762 9 : if (!p.parse(cx))
1763 0 : return false;
1764 :
1765 18 : Sprinter sprinter(cx);
1766 9 : if (!sprinter.init())
1767 0 : return false;
1768 :
1769 9 : bool ok = true;
1770 9 : if (p.argc == 0) {
1771 : /* Without arguments, disassemble the current script. */
1772 0 : if (JSScript *script = GetTopScript(cx)) {
1773 0 : if (js_Disassemble(cx, script, p.lines, &sprinter)) {
1774 0 : SrcNotes(cx, script, &sprinter);
1775 0 : TryNotes(cx, script, &sprinter);
1776 : } else {
1777 0 : ok = false;
1778 : }
1779 : }
1780 : } else {
1781 18 : for (unsigned i = 0; i < p.argc; i++) {
1782 : JSFunction *fun;
1783 9 : JSScript *script = ValueToScript(cx, p.argv[i], &fun);
1784 9 : ok = ok && script && DisassembleScript(cx, script, fun, p.lines, p.recursive, &sprinter);
1785 : }
1786 : }
1787 :
1788 9 : JSString *str = ok ? JS_NewStringCopyZ(cx, sprinter.string()) : NULL;
1789 9 : if (!str)
1790 0 : return false;
1791 9 : JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(str));
1792 9 : return true;
1793 : }
1794 :
1795 : static JSBool
1796 18 : Disassemble(JSContext *cx, unsigned argc, jsval *vp)
1797 : {
1798 18 : DisassembleOptionParser p(argc, JS_ARGV(cx, vp));
1799 18 : if (!p.parse(cx))
1800 0 : return false;
1801 :
1802 36 : Sprinter sprinter(cx);
1803 18 : if (!sprinter.init())
1804 0 : return false;
1805 :
1806 18 : bool ok = true;
1807 18 : if (p.argc == 0) {
1808 : /* Without arguments, disassemble the current script. */
1809 0 : if (JSScript *script = GetTopScript(cx)) {
1810 0 : if (js_Disassemble(cx, script, p.lines, &sprinter)) {
1811 0 : SrcNotes(cx, script, &sprinter);
1812 0 : TryNotes(cx, script, &sprinter);
1813 : } else {
1814 0 : ok = false;
1815 : }
1816 : }
1817 : } else {
1818 36 : for (unsigned i = 0; i < p.argc; i++) {
1819 : JSFunction *fun;
1820 18 : JSScript *script = ValueToScript(cx, p.argv[i], &fun);
1821 18 : ok = ok && script && DisassembleScript(cx, script, fun, p.lines, p.recursive, &sprinter);
1822 : }
1823 : }
1824 :
1825 18 : if (ok)
1826 9 : fprintf(stdout, "%s\n", sprinter.string());
1827 18 : JS_SET_RVAL(cx, vp, JSVAL_VOID);
1828 18 : return ok;
1829 : }
1830 :
1831 : static JSBool
1832 0 : DisassFile(JSContext *cx, unsigned argc, jsval *vp)
1833 : {
1834 : /* Support extra options at the start, just like Dissassemble. */
1835 0 : DisassembleOptionParser p(argc, JS_ARGV(cx, vp));
1836 0 : if (!p.parse(cx))
1837 0 : return false;
1838 :
1839 0 : if (!p.argc) {
1840 0 : JS_SET_RVAL(cx, vp, JSVAL_VOID);
1841 0 : return JS_TRUE;
1842 : }
1843 :
1844 0 : JSObject *thisobj = JS_THIS_OBJECT(cx, vp);
1845 0 : if (!thisobj)
1846 0 : return JS_FALSE;
1847 :
1848 0 : JSString *str = JS_ValueToString(cx, p.argv[0]);
1849 0 : if (!str)
1850 0 : return JS_FALSE;
1851 0 : JSAutoByteString filename(cx, str);
1852 0 : if (!filename)
1853 0 : return JS_FALSE;
1854 :
1855 0 : uint32_t oldopts = JS_GetOptions(cx);
1856 0 : JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO | JSOPTION_NO_SCRIPT_RVAL);
1857 0 : JSScript *script = JS_CompileUTF8File(cx, thisobj, filename.ptr());
1858 0 : JS_SetOptions(cx, oldopts);
1859 0 : if (!script)
1860 0 : return false;
1861 :
1862 0 : Sprinter sprinter(cx);
1863 0 : if (!sprinter.init())
1864 0 : return false;
1865 0 : bool ok = DisassembleScript(cx, script, NULL, p.lines, p.recursive, &sprinter);
1866 0 : if (ok)
1867 0 : fprintf(stdout, "%s\n", sprinter.string());
1868 0 : if (!ok)
1869 0 : return false;
1870 :
1871 0 : JS_SET_RVAL(cx, vp, JSVAL_VOID);
1872 0 : return true;
1873 : }
1874 :
1875 : static JSBool
1876 0 : DisassWithSrc(JSContext *cx, unsigned argc, jsval *vp)
1877 : {
1878 : #define LINE_BUF_LEN 512
1879 : unsigned i, len, line1, line2, bupline;
1880 : JSScript *script;
1881 : FILE *file;
1882 : char linebuf[LINE_BUF_LEN];
1883 : jsbytecode *pc, *end;
1884 : JSBool ok;
1885 : static char sep[] = ";-------------------------";
1886 :
1887 0 : ok = JS_TRUE;
1888 0 : jsval *argv = JS_ARGV(cx, vp);
1889 0 : for (i = 0; ok && i < argc; i++) {
1890 0 : script = ValueToScript(cx, argv[i]);
1891 0 : if (!script)
1892 0 : return JS_FALSE;
1893 :
1894 0 : if (!script->filename) {
1895 : JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
1896 0 : JSSMSG_FILE_SCRIPTS_ONLY);
1897 0 : return JS_FALSE;
1898 : }
1899 :
1900 0 : file = fopen(script->filename, "r");
1901 0 : if (!file) {
1902 : JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
1903 : JSSMSG_CANT_OPEN, script->filename,
1904 0 : strerror(errno));
1905 0 : return JS_FALSE;
1906 : }
1907 :
1908 0 : pc = script->code;
1909 0 : end = pc + script->length;
1910 :
1911 0 : Sprinter sprinter(cx);
1912 0 : if (!sprinter.init()) {
1913 0 : ok = JS_FALSE;
1914 0 : goto bail;
1915 : }
1916 :
1917 : /* burn the leading lines */
1918 0 : line2 = JS_PCToLineNumber(cx, script, pc);
1919 0 : for (line1 = 0; line1 < line2 - 1; line1++) {
1920 0 : char *tmp = fgets(linebuf, LINE_BUF_LEN, file);
1921 0 : if (!tmp) {
1922 0 : JS_ReportError(cx, "failed to read %s fully", script->filename);
1923 0 : ok = JS_FALSE;
1924 0 : goto bail;
1925 : }
1926 : }
1927 :
1928 0 : bupline = 0;
1929 0 : while (pc < end) {
1930 0 : line2 = JS_PCToLineNumber(cx, script, pc);
1931 :
1932 0 : if (line2 < line1) {
1933 0 : if (bupline != line2) {
1934 0 : bupline = line2;
1935 0 : Sprint(&sprinter, "%s %3u: BACKUP\n", sep, line2);
1936 : }
1937 : } else {
1938 0 : if (bupline && line1 == line2)
1939 0 : Sprint(&sprinter, "%s %3u: RESTORE\n", sep, line2);
1940 0 : bupline = 0;
1941 0 : while (line1 < line2) {
1942 0 : if (!fgets(linebuf, LINE_BUF_LEN, file)) {
1943 : JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
1944 : JSSMSG_UNEXPECTED_EOF,
1945 0 : script->filename);
1946 0 : ok = JS_FALSE;
1947 0 : goto bail;
1948 : }
1949 0 : line1++;
1950 0 : Sprint(&sprinter, "%s %3u: %s", sep, line1, linebuf);
1951 : }
1952 : }
1953 :
1954 0 : len = js_Disassemble1(cx, script, pc, pc - script->code, JS_TRUE, &sprinter);
1955 0 : if (!len) {
1956 0 : ok = JS_FALSE;
1957 0 : goto bail;
1958 : }
1959 0 : pc += len;
1960 : }
1961 :
1962 : bail:
1963 0 : fclose(file);
1964 : }
1965 0 : JS_SET_RVAL(cx, vp, JSVAL_VOID);
1966 0 : return ok;
1967 : #undef LINE_BUF_LEN
1968 : }
1969 :
1970 : static void
1971 0 : DumpScope(JSContext *cx, JSObject *obj, FILE *fp)
1972 : {
1973 0 : unsigned i = 0;
1974 0 : for (JSScopeProperty *sprop = NULL; JS_PropertyIterator(obj, &sprop);) {
1975 0 : fprintf(fp, "%3u %p ", i++, (void *) sprop);
1976 0 : ((Shape *) sprop)->dump(cx, fp);
1977 : }
1978 0 : }
1979 :
1980 : static JSBool
1981 0 : DumpStats(JSContext *cx, unsigned argc, jsval *vp)
1982 : {
1983 0 : jsval *argv = JS_ARGV(cx, vp);
1984 0 : for (unsigned i = 0; i < argc; i++) {
1985 0 : JSString *str = JS_ValueToString(cx, argv[i]);
1986 0 : if (!str)
1987 0 : return JS_FALSE;
1988 0 : argv[i] = STRING_TO_JSVAL(str);
1989 0 : JSFlatString *flatStr = JS_FlattenString(cx, str);
1990 0 : if (!flatStr)
1991 0 : return JS_FALSE;
1992 0 : if (JS_FlatStringEqualsAscii(flatStr, "atom")) {
1993 0 : js_DumpAtoms(cx, gOutFile);
1994 0 : } else if (JS_FlatStringEqualsAscii(flatStr, "global")) {
1995 0 : DumpScope(cx, cx->globalObject, stdout);
1996 : } else {
1997 0 : fputs("js: invalid stats argument ", gErrFile);
1998 0 : JS_FileEscapedString(gErrFile, str, 0);
1999 0 : putc('\n', gErrFile);
2000 0 : continue;
2001 : }
2002 : }
2003 0 : JS_SET_RVAL(cx, vp, JSVAL_VOID);
2004 0 : return JS_TRUE;
2005 : }
2006 :
2007 : static JSBool
2008 0 : DumpHeap(JSContext *cx, unsigned argc, jsval *vp)
2009 : {
2010 : jsval v;
2011 : void* startThing;
2012 : JSGCTraceKind startTraceKind;
2013 : const char *badTraceArg;
2014 : void *thingToFind;
2015 : size_t maxDepth;
2016 : void *thingToIgnore;
2017 : FILE *dumpFile;
2018 : JSBool ok;
2019 :
2020 0 : const char *fileName = NULL;
2021 0 : JSAutoByteString fileNameBytes;
2022 0 : if (argc > 0) {
2023 0 : v = JS_ARGV(cx, vp)[0];
2024 0 : if (!JSVAL_IS_NULL(v)) {
2025 : JSString *str;
2026 :
2027 0 : str = JS_ValueToString(cx, v);
2028 0 : if (!str)
2029 0 : return JS_FALSE;
2030 0 : JS_ARGV(cx, vp)[0] = STRING_TO_JSVAL(str);
2031 0 : if (!fileNameBytes.encode(cx, str))
2032 0 : return JS_FALSE;
2033 0 : fileName = fileNameBytes.ptr();
2034 : }
2035 : }
2036 :
2037 0 : startThing = NULL;
2038 0 : startTraceKind = JSTRACE_OBJECT;
2039 0 : if (argc > 1) {
2040 0 : v = JS_ARGV(cx, vp)[1];
2041 0 : if (JSVAL_IS_TRACEABLE(v)) {
2042 0 : startThing = JSVAL_TO_TRACEABLE(v);
2043 0 : startTraceKind = JSVAL_TRACE_KIND(v);
2044 0 : } else if (!JSVAL_IS_NULL(v)) {
2045 0 : badTraceArg = "start";
2046 0 : goto not_traceable_arg;
2047 : }
2048 : }
2049 :
2050 0 : thingToFind = NULL;
2051 0 : if (argc > 2) {
2052 0 : v = JS_ARGV(cx, vp)[2];
2053 0 : if (JSVAL_IS_TRACEABLE(v)) {
2054 0 : thingToFind = JSVAL_TO_TRACEABLE(v);
2055 0 : } else if (!JSVAL_IS_NULL(v)) {
2056 0 : badTraceArg = "toFind";
2057 0 : goto not_traceable_arg;
2058 : }
2059 : }
2060 :
2061 0 : maxDepth = (size_t)-1;
2062 0 : if (argc > 3) {
2063 0 : v = JS_ARGV(cx, vp)[3];
2064 0 : if (!JSVAL_IS_NULL(v)) {
2065 : uint32_t depth;
2066 :
2067 0 : if (!JS_ValueToECMAUint32(cx, v, &depth))
2068 0 : return JS_FALSE;
2069 0 : maxDepth = depth;
2070 : }
2071 : }
2072 :
2073 0 : thingToIgnore = NULL;
2074 0 : if (argc > 4) {
2075 0 : v = JS_ARGV(cx, vp)[4];
2076 0 : if (JSVAL_IS_TRACEABLE(v)) {
2077 0 : thingToIgnore = JSVAL_TO_TRACEABLE(v);
2078 0 : } else if (!JSVAL_IS_NULL(v)) {
2079 0 : badTraceArg = "toIgnore";
2080 0 : goto not_traceable_arg;
2081 : }
2082 : }
2083 :
2084 0 : if (!fileName) {
2085 0 : dumpFile = stdout;
2086 : } else {
2087 0 : dumpFile = fopen(fileName, "w");
2088 0 : if (!dumpFile) {
2089 0 : JS_ReportError(cx, "can't open %s: %s", fileName, strerror(errno));
2090 0 : return JS_FALSE;
2091 : }
2092 : }
2093 :
2094 : ok = JS_DumpHeap(JS_GetRuntime(cx), dumpFile, startThing, startTraceKind, thingToFind,
2095 0 : maxDepth, thingToIgnore);
2096 0 : if (dumpFile != stdout)
2097 0 : fclose(dumpFile);
2098 0 : if (!ok) {
2099 0 : JS_ReportOutOfMemory(cx);
2100 0 : return false;
2101 : }
2102 0 : JS_SET_RVAL(cx, vp, JSVAL_VOID);
2103 0 : return true;
2104 :
2105 : not_traceable_arg:
2106 : JS_ReportError(cx, "argument '%s' is not null or a heap-allocated thing",
2107 0 : badTraceArg);
2108 0 : return JS_FALSE;
2109 : }
2110 :
2111 : JSBool
2112 0 : DumpObject(JSContext *cx, unsigned argc, jsval *vp)
2113 : {
2114 0 : JSObject *arg0 = NULL;
2115 0 : if (!JS_ConvertArguments(cx, argc, JS_ARGV(cx, vp), "o", &arg0))
2116 0 : return JS_FALSE;
2117 :
2118 0 : js_DumpObject(arg0);
2119 :
2120 0 : JS_SET_RVAL(cx, vp, JSVAL_VOID);
2121 0 : return JS_TRUE;
2122 : }
2123 :
2124 : #endif /* DEBUG */
2125 :
2126 : /*
2127 : * This shell function is temporary (used by testStackIter.js) and should be
2128 : * removed once JSD2 lands wholly subsumes the functionality here.
2129 : */
2130 : JSBool
2131 594 : DumpStack(JSContext *cx, unsigned argc, Value *vp)
2132 : {
2133 594 : JSObject *arr = JS_NewArrayObject(cx, 0, NULL);
2134 594 : if (!arr)
2135 0 : return false;
2136 :
2137 594 : JSString *evalStr = JS_NewStringCopyZ(cx, "eval-code");
2138 594 : if (!evalStr)
2139 0 : return false;
2140 :
2141 594 : JSString *globalStr = JS_NewStringCopyZ(cx, "global-code");
2142 594 : if (!globalStr)
2143 0 : return false;
2144 :
2145 594 : StackIter iter(cx);
2146 594 : JS_ASSERT(iter.nativeArgs().callee().toFunction()->native() == DumpStack);
2147 594 : ++iter;
2148 :
2149 594 : uint32_t index = 0;
2150 4229 : for (; !iter.done(); ++index, ++iter) {
2151 : Value v;
2152 3635 : if (iter.isScript()) {
2153 2925 : if (iter.fp()->isNonEvalFunctionFrame()) {
2154 2088 : v = ObjectValue(iter.fp()->callee());
2155 837 : } else if (iter.fp()->isEvalFrame()) {
2156 243 : v = StringValue(evalStr);
2157 : } else {
2158 594 : v = StringValue(globalStr);
2159 : }
2160 : } else {
2161 710 : v = iter.nativeArgs().calleev();
2162 : }
2163 3635 : if (!JS_SetElement(cx, arr, index, &v))
2164 0 : return false;
2165 : }
2166 :
2167 594 : JS_SET_RVAL(cx, vp, ObjectValue(*arr));
2168 594 : return true;
2169 : }
2170 :
2171 : #ifdef TEST_CVTARGS
2172 : #include <ctype.h>
2173 :
2174 : static const char *
2175 : EscapeWideString(jschar *w)
2176 : {
2177 : static char enuf[80];
2178 : static char hex[] = "0123456789abcdef";
2179 : jschar u;
2180 : unsigned char b, c;
2181 : int i, j;
2182 :
2183 : if (!w)
2184 : return "";
2185 : for (i = j = 0; i < sizeof enuf - 1; i++, j++) {
2186 : u = w[j];
2187 : if (u == 0)
2188 : break;
2189 : b = (unsigned char)(u >> 8);
2190 : c = (unsigned char)(u);
2191 : if (b) {
2192 : if (i >= sizeof enuf - 6)
2193 : break;
2194 : enuf[i++] = '\\';
2195 : enuf[i++] = 'u';
2196 : enuf[i++] = hex[b >> 4];
2197 : enuf[i++] = hex[b & 15];
2198 : enuf[i++] = hex[c >> 4];
2199 : enuf[i] = hex[c & 15];
2200 : } else if (!isprint(c)) {
2201 : if (i >= sizeof enuf - 4)
2202 : break;
2203 : enuf[i++] = '\\';
2204 : enuf[i++] = 'x';
2205 : enuf[i++] = hex[c >> 4];
2206 : enuf[i] = hex[c & 15];
2207 : } else {
2208 : enuf[i] = (char)c;
2209 : }
2210 : }
2211 : enuf[i] = 0;
2212 : return enuf;
2213 : }
2214 :
2215 : #include <stdarg.h>
2216 :
2217 : static JSBool
2218 : ZZ_formatter(JSContext *cx, const char *format, JSBool fromJS, jsval **vpp,
2219 : va_list *app)
2220 : {
2221 : jsval *vp;
2222 : va_list ap;
2223 : double re, im;
2224 :
2225 : printf("entering ZZ_formatter");
2226 : vp = *vpp;
2227 : ap = *app;
2228 : if (fromJS) {
2229 : if (!JS_ValueToNumber(cx, vp[0], &re))
2230 : return JS_FALSE;
2231 : if (!JS_ValueToNumber(cx, vp[1], &im))
2232 : return JS_FALSE;
2233 : *va_arg(ap, double *) = re;
2234 : *va_arg(ap, double *) = im;
2235 : } else {
2236 : re = va_arg(ap, double);
2237 : im = va_arg(ap, double);
2238 : if (!JS_NewNumberValue(cx, re, &vp[0]))
2239 : return JS_FALSE;
2240 : if (!JS_NewNumberValue(cx, im, &vp[1]))
2241 : return JS_FALSE;
2242 : }
2243 : *vpp = vp + 2;
2244 : *app = ap;
2245 : printf("leaving ZZ_formatter");
2246 : return JS_TRUE;
2247 : }
2248 :
2249 : static JSBool
2250 : ConvertArgs(JSContext *cx, unsigned argc, jsval *vp)
2251 : {
2252 : JSBool b = JS_FALSE;
2253 : jschar c = 0;
2254 : int32_t i = 0, j = 0;
2255 : uint32_t u = 0;
2256 : double d = 0, I = 0, re = 0, im = 0;
2257 : JSString *str = NULL;
2258 : jschar *w = NULL;
2259 : JSObject *obj2 = NULL;
2260 : JSFunction *fun = NULL;
2261 : jsval v = JSVAL_VOID;
2262 : JSBool ok;
2263 :
2264 : if (!JS_AddArgumentFormatter(cx, "ZZ", ZZ_formatter))
2265 : return JS_FALSE;
2266 : ok = JS_ConvertArguments(cx, argc, JS_ARGV(cx, vp), "b/ciujdISWofvZZ*",
2267 : &b, &c, &i, &u, &j, &d, &I, &str, &w, &obj2,
2268 : &fun, &v, &re, &im);
2269 : JS_RemoveArgumentFormatter(cx, "ZZ");
2270 : if (!ok)
2271 : return JS_FALSE;
2272 : fprintf(gOutFile,
2273 : "b %u, c %x (%c), i %ld, u %lu, j %ld\n",
2274 : b, c, (char)c, i, u, j);
2275 : ToStringHelper obj2string(cx, obj2);
2276 : ToStringHelper valueString(cx, v);
2277 : JSAutoByteString strBytes;
2278 : if (str)
2279 : strBytes.encode(cx, str);
2280 : JSString *tmpstr = JS_DecompileFunction(cx, fun, 4);
2281 : JSAutoByteString func;
2282 : if (!tmpstr || !func.encode(cx, tmpstr))
2283 : ReportException(cx);
2284 : fprintf(gOutFile,
2285 : "d %g, I %g, S %s, W %s, obj %s, fun %s\n"
2286 : "v %s, re %g, im %g\n",
2287 : d, I, !!strBytes ? strBytes.ptr() : "", EscapeWideString(w),
2288 : obj2string.getBytes(),
2289 : fun ? (!!func ? func.ptr() : "error decompiling fun") : "",
2290 : valueString.getBytes(), re, im);
2291 : JS_SET_RVAL(cx, vp, JSVAL_VOID);
2292 : return JS_TRUE;
2293 : }
2294 : #endif
2295 :
2296 : static JSBool
2297 0 : BuildDate(JSContext *cx, unsigned argc, jsval *vp)
2298 : {
2299 0 : char version[20] = "\n";
2300 : #if JS_VERSION < 150
2301 : sprintf(version, " for version %d\n", JS_VERSION);
2302 : #endif
2303 0 : fprintf(gOutFile, "built on %s at %s%s", __DATE__, __TIME__, version);
2304 0 : *vp = JSVAL_VOID;
2305 0 : return JS_TRUE;
2306 : }
2307 :
2308 : static JSBool
2309 0 : Clear(JSContext *cx, unsigned argc, jsval *vp)
2310 : {
2311 : JSObject *obj;
2312 0 : if (argc == 0) {
2313 0 : obj = JS_GetGlobalForScopeChain(cx);
2314 0 : if (!obj)
2315 0 : return false;
2316 0 : } else if (!JS_ValueToObject(cx, JS_ARGV(cx, vp)[0], &obj)) {
2317 0 : return false;
2318 : }
2319 0 : JS_ClearScope(cx, obj);
2320 0 : JS_SET_RVAL(cx, vp, JSVAL_VOID);
2321 0 : return true;
2322 : }
2323 :
2324 : static JSBool
2325 0 : Intern(JSContext *cx, unsigned argc, jsval *vp)
2326 : {
2327 0 : JSString *str = JS_ValueToString(cx, argc == 0 ? JSVAL_VOID : vp[2]);
2328 0 : if (!str)
2329 0 : return false;
2330 :
2331 : size_t length;
2332 0 : const jschar *chars = JS_GetStringCharsAndLength(cx, str, &length);
2333 0 : if (!chars)
2334 0 : return false;
2335 :
2336 0 : if (!JS_InternUCStringN(cx, chars, length))
2337 0 : return false;
2338 :
2339 0 : JS_SET_RVAL(cx, vp, JSVAL_VOID);
2340 0 : return true;
2341 : }
2342 :
2343 : static JSBool
2344 2367 : Clone(JSContext *cx, unsigned argc, jsval *vp)
2345 : {
2346 : JSObject *funobj, *parent, *clone;
2347 :
2348 2367 : if (!argc) {
2349 0 : JS_ReportError(cx, "Invalid arguments to clone");
2350 0 : return JS_FALSE;
2351 : }
2352 :
2353 2367 : jsval *argv = JS_ARGV(cx, vp);
2354 : {
2355 4734 : JSAutoEnterCompartment ac;
2356 4734 : if (!JSVAL_IS_PRIMITIVE(argv[0]) &&
2357 2367 : IsCrossCompartmentWrapper(JSVAL_TO_OBJECT(argv[0])))
2358 : {
2359 2367 : JSObject *obj = UnwrapObject(JSVAL_TO_OBJECT(argv[0]));
2360 2367 : if (!ac.enter(cx, obj))
2361 0 : return JS_FALSE;
2362 2367 : argv[0] = OBJECT_TO_JSVAL(obj);
2363 : }
2364 2367 : if (!JSVAL_IS_PRIMITIVE(argv[0]) && JSVAL_TO_OBJECT(argv[0])->isFunction()) {
2365 2367 : funobj = JSVAL_TO_OBJECT(argv[0]);
2366 : } else {
2367 0 : JSFunction *fun = JS_ValueToFunction(cx, argv[0]);
2368 0 : if (!fun)
2369 0 : return JS_FALSE;
2370 0 : funobj = JS_GetFunctionObject(fun);
2371 : }
2372 : }
2373 2367 : if (funobj->compartment() != cx->compartment) {
2374 2367 : JSFunction *fun = funobj->toFunction();
2375 2367 : if (fun->isInterpreted() && fun->script()->compileAndGo) {
2376 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_UNEXPECTED_TYPE,
2377 0 : "function", "compile-and-go");
2378 0 : return JS_FALSE;
2379 : }
2380 : }
2381 :
2382 2367 : if (argc > 1) {
2383 0 : if (!JS_ValueToObject(cx, argv[1], &parent))
2384 0 : return JS_FALSE;
2385 : } else {
2386 2367 : parent = JS_GetParent(JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)));
2387 : }
2388 :
2389 2367 : clone = JS_CloneFunctionObject(cx, funobj, parent);
2390 2367 : if (!clone)
2391 0 : return JS_FALSE;
2392 2367 : *vp = OBJECT_TO_JSVAL(clone);
2393 2367 : return JS_TRUE;
2394 : }
2395 :
2396 : static JSBool
2397 0 : GetPDA(JSContext *cx, unsigned argc, jsval *vp)
2398 : {
2399 : JSObject *vobj, *aobj, *pdobj;
2400 : JSBool ok;
2401 : JSPropertyDescArray pda;
2402 : JSPropertyDesc *pd;
2403 : jsval v;
2404 :
2405 0 : if (!JS_ValueToObject(cx, argc == 0 ? JSVAL_VOID : vp[2], &vobj))
2406 0 : return JS_FALSE;
2407 0 : if (!vobj) {
2408 0 : *vp = JSVAL_VOID;
2409 0 : return JS_TRUE;
2410 : }
2411 :
2412 0 : aobj = JS_NewArrayObject(cx, 0, NULL);
2413 0 : if (!aobj)
2414 0 : return JS_FALSE;
2415 0 : *vp = OBJECT_TO_JSVAL(aobj);
2416 :
2417 0 : ok = JS_GetPropertyDescArray(cx, vobj, &pda);
2418 0 : if (!ok)
2419 0 : return JS_FALSE;
2420 0 : pd = pda.array;
2421 0 : for (uint32_t i = 0; i < pda.length; i++, pd++) {
2422 0 : pdobj = JS_NewObject(cx, NULL, NULL, NULL);
2423 0 : if (!pdobj) {
2424 0 : ok = JS_FALSE;
2425 0 : break;
2426 : }
2427 :
2428 : /* Protect pdobj from GC by setting it as an element of aobj now */
2429 0 : v = OBJECT_TO_JSVAL(pdobj);
2430 0 : ok = JS_SetElement(cx, aobj, i, &v);
2431 0 : if (!ok)
2432 0 : break;
2433 :
2434 0 : ok = JS_SetProperty(cx, pdobj, "id", &pd->id) &&
2435 0 : JS_SetProperty(cx, pdobj, "value", &pd->value) &&
2436 0 : (v = INT_TO_JSVAL(pd->flags),
2437 0 : JS_SetProperty(cx, pdobj, "flags", &v)) &&
2438 0 : (v = INT_TO_JSVAL(pd->slot),
2439 0 : JS_SetProperty(cx, pdobj, "slot", &v)) &&
2440 0 : JS_SetProperty(cx, pdobj, "alias", &pd->alias);
2441 0 : if (!ok)
2442 0 : break;
2443 : }
2444 0 : JS_PutPropertyDescArray(cx, &pda);
2445 0 : return ok;
2446 : }
2447 :
2448 : static JSBool
2449 0 : GetSLX(JSContext *cx, unsigned argc, jsval *vp)
2450 : {
2451 : JSScript *script;
2452 :
2453 0 : script = ValueToScript(cx, argc == 0 ? JSVAL_VOID : vp[2]);
2454 0 : if (!script)
2455 0 : return JS_FALSE;
2456 0 : *vp = INT_TO_JSVAL(js_GetScriptLineExtent(script));
2457 0 : return JS_TRUE;
2458 : }
2459 :
2460 : static JSBool
2461 0 : ToInt32(JSContext *cx, unsigned argc, jsval *vp)
2462 : {
2463 : int32_t i;
2464 :
2465 0 : if (!JS_ValueToInt32(cx, argc == 0 ? JSVAL_VOID : vp[2], &i))
2466 0 : return JS_FALSE;
2467 0 : return JS_NewNumberValue(cx, i, vp);
2468 : }
2469 :
2470 : static JSBool
2471 0 : StringsAreUTF8(JSContext *cx, unsigned argc, jsval *vp)
2472 : {
2473 0 : *vp = JS_CStringsAreUTF8() ? JSVAL_TRUE : JSVAL_FALSE;
2474 0 : return JS_TRUE;
2475 : }
2476 :
2477 : static const char* badUTF8 = "...\xC0...";
2478 : static const char* bigUTF8 = "...\xFB\xBF\xBF\xBF\xBF...";
2479 : static const jschar badSurrogate[] = { 'A', 'B', 'C', 0xDEEE, 'D', 'E', 0 };
2480 :
2481 : static JSBool
2482 0 : TestUTF8(JSContext *cx, unsigned argc, jsval *vp)
2483 : {
2484 0 : int32_t mode = 1;
2485 : jschar chars[20];
2486 0 : size_t charsLength = 5;
2487 : char bytes[20];
2488 0 : size_t bytesLength = 20;
2489 0 : if (argc && !JS_ValueToInt32(cx, *JS_ARGV(cx, vp), &mode))
2490 0 : return JS_FALSE;
2491 :
2492 : /* The following throw errors if compiled with UTF-8. */
2493 0 : switch (mode) {
2494 : /* mode 1: malformed UTF-8 string. */
2495 : case 1:
2496 0 : JS_NewStringCopyZ(cx, badUTF8);
2497 0 : break;
2498 : /* mode 2: big UTF-8 character. */
2499 : case 2:
2500 0 : JS_NewStringCopyZ(cx, bigUTF8);
2501 0 : break;
2502 : /* mode 3: bad surrogate character. */
2503 : case 3:
2504 0 : JS_EncodeCharacters(cx, badSurrogate, 6, bytes, &bytesLength);
2505 0 : break;
2506 : /* mode 4: use a too small buffer. */
2507 : case 4:
2508 0 : JS_DecodeBytes(cx, "1234567890", 10, chars, &charsLength);
2509 0 : break;
2510 : default:
2511 0 : JS_ReportError(cx, "invalid mode parameter");
2512 0 : return JS_FALSE;
2513 : }
2514 0 : JS_SET_RVAL(cx, vp, JSVAL_VOID);
2515 0 : return !JS_IsExceptionPending (cx);
2516 : }
2517 :
2518 : static JSBool
2519 0 : ThrowError(JSContext *cx, unsigned argc, jsval *vp)
2520 : {
2521 0 : JS_ReportError(cx, "This is an error");
2522 0 : return JS_FALSE;
2523 : }
2524 :
2525 : #define LAZY_STANDARD_CLASSES
2526 :
2527 : /* A class for easily testing the inner/outer object callbacks. */
2528 : typedef struct ComplexObject {
2529 : JSBool isInner;
2530 : JSBool frozen;
2531 : JSObject *inner;
2532 : JSObject *outer;
2533 : } ComplexObject;
2534 :
2535 : static JSBool
2536 45 : sandbox_enumerate(JSContext *cx, JSObject *obj)
2537 : {
2538 : jsval v;
2539 : JSBool b;
2540 :
2541 45 : if (!JS_GetProperty(cx, obj, "lazy", &v))
2542 0 : return JS_FALSE;
2543 :
2544 45 : JS_ValueToBoolean(cx, v, &b);
2545 45 : return !b || JS_EnumerateStandardClasses(cx, obj);
2546 : }
2547 :
2548 : static JSBool
2549 9648 : sandbox_resolve(JSContext *cx, JSObject *obj, jsid id, unsigned flags,
2550 : JSObject **objp)
2551 : {
2552 : jsval v;
2553 : JSBool b, resolved;
2554 :
2555 9648 : if (!JS_GetProperty(cx, obj, "lazy", &v))
2556 0 : return JS_FALSE;
2557 :
2558 9648 : JS_ValueToBoolean(cx, v, &b);
2559 9648 : if (b && (flags & JSRESOLVE_ASSIGNING) == 0) {
2560 72 : if (!JS_ResolveStandardClass(cx, obj, id, &resolved))
2561 0 : return JS_FALSE;
2562 72 : if (resolved) {
2563 27 : *objp = obj;
2564 27 : return JS_TRUE;
2565 : }
2566 : }
2567 9621 : *objp = NULL;
2568 9621 : return JS_TRUE;
2569 : }
2570 :
2571 : static JSClass sandbox_class = {
2572 : "sandbox",
2573 : JSCLASS_NEW_RESOLVE | JSCLASS_GLOBAL_FLAGS,
2574 : JS_PropertyStub, JS_PropertyStub,
2575 : JS_PropertyStub, JS_StrictPropertyStub,
2576 : sandbox_enumerate, (JSResolveOp)sandbox_resolve,
2577 : JS_ConvertStub
2578 : };
2579 :
2580 : static JSObject *
2581 225 : NewSandbox(JSContext *cx, bool lazy)
2582 : {
2583 225 : JSObject *obj = JS_NewCompartmentAndGlobalObject(cx, &sandbox_class, NULL);
2584 225 : if (!obj)
2585 0 : return NULL;
2586 :
2587 : {
2588 450 : JSAutoEnterCompartment ac;
2589 225 : if (!ac.enter(cx, obj))
2590 0 : return NULL;
2591 :
2592 225 : if (!lazy && !JS_InitStandardClasses(cx, obj))
2593 0 : return NULL;
2594 :
2595 450 : AutoValueRooter root(cx, BooleanValue(lazy));
2596 225 : if (!JS_SetProperty(cx, obj, "lazy", root.jsval_addr()))
2597 0 : return NULL;
2598 : }
2599 :
2600 225 : if (!cx->compartment->wrap(cx, &obj))
2601 0 : return NULL;
2602 225 : return obj;
2603 : }
2604 :
2605 : static JSBool
2606 342 : EvalInContext(JSContext *cx, unsigned argc, jsval *vp)
2607 : {
2608 : JSString *str;
2609 342 : JSObject *sobj = NULL;
2610 342 : if (!JS_ConvertArguments(cx, argc, JS_ARGV(cx, vp), "S / o", &str, &sobj))
2611 0 : return false;
2612 :
2613 : size_t srclen;
2614 342 : const jschar *src = JS_GetStringCharsAndLength(cx, str, &srclen);
2615 342 : if (!src)
2616 0 : return false;
2617 :
2618 342 : bool lazy = false;
2619 342 : if (srclen == 4) {
2620 63 : if (src[0] == 'l' && src[1] == 'a' && src[2] == 'z' && src[3] == 'y') {
2621 63 : lazy = true;
2622 63 : srclen = 0;
2623 : }
2624 : }
2625 :
2626 342 : if (!sobj) {
2627 225 : sobj = NewSandbox(cx, lazy);
2628 225 : if (!sobj)
2629 0 : return false;
2630 : }
2631 :
2632 342 : if (srclen == 0) {
2633 189 : JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(sobj));
2634 189 : return true;
2635 : }
2636 :
2637 : JSScript *script;
2638 : unsigned lineno;
2639 :
2640 153 : JS_DescribeScriptedCaller(cx, &script, &lineno);
2641 : jsval rval;
2642 : {
2643 306 : JSAutoEnterCompartment ac;
2644 : unsigned flags;
2645 153 : JSObject *unwrapped = UnwrapObject(sobj, true, &flags);
2646 153 : if (flags & Wrapper::CROSS_COMPARTMENT) {
2647 135 : sobj = unwrapped;
2648 135 : if (!ac.enter(cx, sobj))
2649 0 : return false;
2650 : }
2651 :
2652 153 : OBJ_TO_INNER_OBJECT(cx, sobj);
2653 153 : if (!sobj)
2654 0 : return false;
2655 153 : if (!(sobj->getClass()->flags & JSCLASS_IS_GLOBAL)) {
2656 0 : JS_ReportError(cx, "Invalid scope argument to evalcx");
2657 0 : return false;
2658 : }
2659 153 : if (!JS_EvaluateUCScript(cx, sobj, src, srclen,
2660 : script->filename,
2661 : lineno,
2662 153 : &rval)) {
2663 45 : return false;
2664 : }
2665 : }
2666 :
2667 108 : if (!cx->compartment->wrap(cx, &rval))
2668 0 : return false;
2669 :
2670 108 : JS_SET_RVAL(cx, vp, rval);
2671 108 : return true;
2672 : }
2673 :
2674 : static JSBool
2675 594 : EvalInFrame(JSContext *cx, unsigned argc, jsval *vp)
2676 : {
2677 594 : jsval *argv = JS_ARGV(cx, vp);
2678 1782 : if (argc < 2 ||
2679 594 : !JSVAL_IS_INT(argv[0]) ||
2680 594 : !JSVAL_IS_STRING(argv[1])) {
2681 0 : JS_ReportError(cx, "Invalid arguments to evalInFrame");
2682 0 : return JS_FALSE;
2683 : }
2684 :
2685 594 : uint32_t upCount = JSVAL_TO_INT(argv[0]);
2686 594 : JSString *str = JSVAL_TO_STRING(argv[1]);
2687 :
2688 189 : bool saveCurrent = (argc >= 3 && JSVAL_IS_BOOLEAN(argv[2]))
2689 189 : ? !!(JSVAL_TO_BOOLEAN(argv[2]))
2690 972 : : false;
2691 :
2692 594 : JS_ASSERT(cx->hasfp());
2693 :
2694 594 : FrameRegsIter fi(cx);
2695 1152 : for (uint32_t i = 0; i < upCount; ++i, ++fi) {
2696 558 : if (!fi.fp()->prev())
2697 0 : break;
2698 : }
2699 :
2700 594 : StackFrame *const fp = fi.fp();
2701 594 : if (!fp->isScriptFrame()) {
2702 0 : JS_ReportError(cx, "cannot eval in non-script frame");
2703 0 : return JS_FALSE;
2704 : }
2705 :
2706 594 : JSBool saved = JS_FALSE;;
2707 594 : if (saveCurrent)
2708 144 : saved = JS_SaveFrameChain(cx);
2709 :
2710 : size_t length;
2711 594 : const jschar *chars = JS_GetStringCharsAndLength(cx, str, &length);
2712 594 : if (!chars)
2713 0 : return JS_FALSE;
2714 :
2715 : JSBool ok = JS_EvaluateUCInStackFrame(cx, Jsvalify(fp), chars, length,
2716 594 : fp->script()->filename,
2717 : JS_PCToLineNumber(cx, fp->script(),
2718 : fi.pc()),
2719 1188 : vp);
2720 :
2721 594 : if (saved)
2722 144 : JS_RestoreFrameChain(cx);
2723 :
2724 594 : return ok;
2725 : }
2726 :
2727 : static JSBool
2728 342 : ShapeOf(JSContext *cx, unsigned argc, jsval *vp)
2729 : {
2730 : jsval v;
2731 342 : if (argc < 1 || !JSVAL_IS_OBJECT(v = JS_ARGV(cx, vp)[0])) {
2732 0 : JS_ReportError(cx, "shapeOf: object expected");
2733 0 : return JS_FALSE;
2734 : }
2735 342 : JSObject *obj = JSVAL_TO_OBJECT(v);
2736 342 : if (!obj) {
2737 0 : *vp = JSVAL_ZERO;
2738 0 : return JS_TRUE;
2739 : }
2740 342 : return JS_NewNumberValue(cx, (double) ((uintptr_t)obj->lastProperty() >> 3), vp);
2741 : }
2742 :
2743 : /*
2744 : * If referent has an own property named id, copy that property to obj[id].
2745 : * Since obj is native, this isn't totally transparent; properties of a
2746 : * non-native referent may be simplified to data properties.
2747 : */
2748 : static JSBool
2749 0 : CopyProperty(JSContext *cx, JSObject *obj, JSObject *referent, jsid id,
2750 : unsigned lookupFlags, JSObject **objp)
2751 : {
2752 : JSProperty *prop;
2753 : PropertyDescriptor desc;
2754 0 : unsigned propFlags = 0;
2755 : JSObject *obj2;
2756 :
2757 0 : *objp = NULL;
2758 0 : if (referent->isNative()) {
2759 0 : if (!LookupPropertyWithFlags(cx, referent, id, lookupFlags, &obj2, &prop))
2760 0 : return false;
2761 0 : if (obj2 != referent)
2762 0 : return true;
2763 :
2764 0 : const Shape *shape = (Shape *) prop;
2765 0 : if (shape->hasSlot()) {
2766 0 : desc.value = referent->nativeGetSlot(shape->slot());
2767 : } else {
2768 0 : desc.value.setUndefined();
2769 : }
2770 :
2771 0 : desc.attrs = shape->attributes();
2772 0 : desc.getter = shape->getter();
2773 0 : if (!desc.getter && !(desc.attrs & JSPROP_GETTER))
2774 0 : desc.getter = JS_PropertyStub;
2775 0 : desc.setter = shape->setter();
2776 0 : if (!desc.setter && !(desc.attrs & JSPROP_SETTER))
2777 0 : desc.setter = JS_StrictPropertyStub;
2778 0 : desc.shortid = shape->shortid();
2779 0 : propFlags = shape->getFlags();
2780 0 : } else if (IsProxy(referent)) {
2781 : PropertyDescriptor desc;
2782 0 : if (!Proxy::getOwnPropertyDescriptor(cx, referent, id, false, &desc))
2783 0 : return false;
2784 0 : if (!desc.obj)
2785 0 : return true;
2786 : } else {
2787 0 : if (!referent->lookupGeneric(cx, id, objp, &prop))
2788 0 : return false;
2789 0 : if (*objp != referent)
2790 0 : return true;
2791 0 : if (!referent->getGeneric(cx, id, &desc.value) ||
2792 0 : !referent->getGenericAttributes(cx, id, &desc.attrs)) {
2793 0 : return false;
2794 : }
2795 0 : desc.attrs &= JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT;
2796 0 : desc.getter = JS_PropertyStub;
2797 0 : desc.setter = JS_StrictPropertyStub;
2798 0 : desc.shortid = 0;
2799 : }
2800 :
2801 0 : *objp = obj;
2802 : return !!DefineNativeProperty(cx, obj, id, desc.value, desc.getter, desc.setter,
2803 0 : desc.attrs, propFlags, desc.shortid);
2804 : }
2805 :
2806 : static JSBool
2807 0 : resolver_resolve(JSContext *cx, JSObject *obj, jsid id, unsigned flags, JSObject **objp)
2808 : {
2809 0 : jsval v = JS_GetReservedSlot(obj, 0);
2810 0 : return CopyProperty(cx, obj, JSVAL_TO_OBJECT(v), id, flags, objp);
2811 : }
2812 :
2813 : static JSBool
2814 0 : resolver_enumerate(JSContext *cx, JSObject *obj)
2815 : {
2816 0 : jsval v = JS_GetReservedSlot(obj, 0);
2817 0 : JSObject *referent = JSVAL_TO_OBJECT(v);
2818 :
2819 0 : AutoIdArray ida(cx, JS_Enumerate(cx, referent));
2820 0 : bool ok = !!ida;
2821 : JSObject *ignore;
2822 0 : for (size_t i = 0; ok && i < ida.length(); i++)
2823 0 : ok = CopyProperty(cx, obj, referent, ida[i], JSRESOLVE_QUALIFIED, &ignore);
2824 0 : return ok;
2825 : }
2826 :
2827 : static JSClass resolver_class = {
2828 : "resolver",
2829 : JSCLASS_NEW_RESOLVE | JSCLASS_HAS_RESERVED_SLOTS(1),
2830 : JS_PropertyStub, JS_PropertyStub,
2831 : JS_PropertyStub, JS_StrictPropertyStub,
2832 : resolver_enumerate, (JSResolveOp)resolver_resolve,
2833 : JS_ConvertStub
2834 : };
2835 :
2836 :
2837 : static JSBool
2838 0 : Resolver(JSContext *cx, unsigned argc, jsval *vp)
2839 : {
2840 0 : JSObject *referent, *proto = NULL;
2841 0 : if (!JS_ConvertArguments(cx, argc, JS_ARGV(cx, vp), "o/o", &referent, &proto))
2842 0 : return false;
2843 :
2844 : JSObject *result = (argc > 1
2845 : ? JS_NewObjectWithGivenProto
2846 0 : : JS_NewObject)(cx, &resolver_class, proto, JS_GetParent(referent));
2847 0 : if (!result)
2848 0 : return false;
2849 :
2850 0 : JS_SetReservedSlot(result, 0, OBJECT_TO_JSVAL(referent));
2851 0 : JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
2852 0 : return true;
2853 : }
2854 :
2855 : #ifdef JS_THREADSAFE
2856 :
2857 : /*
2858 : * Check that t1 comes strictly before t2. The function correctly deals with
2859 : * PRIntervalTime wrap-around between t2 and t1 assuming that t2 and t1 stays
2860 : * within INT32_MAX from each other. We use MAX_TIMEOUT_INTERVAL to enforce
2861 : * this restriction.
2862 : */
2863 : static bool
2864 0 : IsBefore(PRIntervalTime t1, PRIntervalTime t2)
2865 : {
2866 0 : return int32_t(t1 - t2) < 0;
2867 : }
2868 :
2869 : static JSBool
2870 0 : Sleep_fn(JSContext *cx, unsigned argc, jsval *vp)
2871 : {
2872 : PRIntervalTime t_ticks;
2873 :
2874 0 : if (argc == 0) {
2875 0 : t_ticks = 0;
2876 : } else {
2877 : double t_secs;
2878 :
2879 0 : if (!JS_ValueToNumber(cx, argc == 0 ? JSVAL_VOID : vp[2], &t_secs))
2880 0 : return JS_FALSE;
2881 :
2882 : /* NB: The next condition also filter out NaNs. */
2883 0 : if (!(t_secs <= MAX_TIMEOUT_INTERVAL)) {
2884 0 : JS_ReportError(cx, "Excessive sleep interval");
2885 0 : return JS_FALSE;
2886 : }
2887 : t_ticks = (t_secs <= 0.0)
2888 : ? 0
2889 0 : : PRIntervalTime(PR_TicksPerSecond() * t_secs);
2890 : }
2891 0 : if (t_ticks == 0) {
2892 0 : JS_YieldRequest(cx);
2893 : } else {
2894 0 : JSAutoSuspendRequest suspended(cx);
2895 0 : PR_Lock(gWatchdogLock);
2896 0 : PRIntervalTime to_wakeup = PR_IntervalNow() + t_ticks;
2897 0 : for (;;) {
2898 0 : PR_WaitCondVar(gSleepWakeup, t_ticks);
2899 0 : if (gCanceled)
2900 0 : break;
2901 0 : PRIntervalTime now = PR_IntervalNow();
2902 0 : if (!IsBefore(now, to_wakeup))
2903 0 : break;
2904 0 : t_ticks = to_wakeup - now;
2905 : }
2906 0 : PR_Unlock(gWatchdogLock);
2907 : }
2908 0 : return !gCanceled;
2909 : }
2910 :
2911 : static bool
2912 18666 : InitWatchdog(JSRuntime *rt)
2913 : {
2914 18666 : JS_ASSERT(!gWatchdogThread);
2915 18666 : gWatchdogLock = PR_NewLock();
2916 18666 : if (gWatchdogLock) {
2917 18666 : gWatchdogWakeup = PR_NewCondVar(gWatchdogLock);
2918 18666 : if (gWatchdogWakeup) {
2919 18666 : gSleepWakeup = PR_NewCondVar(gWatchdogLock);
2920 18666 : if (gSleepWakeup)
2921 18666 : return true;
2922 0 : PR_DestroyCondVar(gWatchdogWakeup);
2923 : }
2924 0 : PR_DestroyLock(gWatchdogLock);
2925 : }
2926 0 : return false;
2927 : }
2928 :
2929 : static void
2930 18666 : KillWatchdog()
2931 : {
2932 : PRThread *thread;
2933 :
2934 18666 : PR_Lock(gWatchdogLock);
2935 18666 : thread = gWatchdogThread;
2936 18666 : if (thread) {
2937 : /*
2938 : * The watchdog thread is running, tell it to terminate waking it up
2939 : * if necessary.
2940 : */
2941 0 : gWatchdogThread = NULL;
2942 0 : PR_NotifyCondVar(gWatchdogWakeup);
2943 : }
2944 18666 : PR_Unlock(gWatchdogLock);
2945 18666 : if (thread)
2946 0 : PR_JoinThread(thread);
2947 18666 : PR_DestroyCondVar(gSleepWakeup);
2948 18666 : PR_DestroyCondVar(gWatchdogWakeup);
2949 18666 : PR_DestroyLock(gWatchdogLock);
2950 18666 : }
2951 :
2952 : static void
2953 0 : WatchdogMain(void *arg)
2954 : {
2955 0 : JSRuntime *rt = (JSRuntime *) arg;
2956 :
2957 0 : PR_Lock(gWatchdogLock);
2958 0 : while (gWatchdogThread) {
2959 0 : PRIntervalTime now = PR_IntervalNow();
2960 0 : if (gWatchdogHasTimeout && !IsBefore(now, gWatchdogTimeout)) {
2961 : /*
2962 : * The timeout has just expired. Trigger the operation callback
2963 : * outside the lock.
2964 : */
2965 0 : gWatchdogHasTimeout = false;
2966 0 : PR_Unlock(gWatchdogLock);
2967 0 : CancelExecution(rt);
2968 0 : PR_Lock(gWatchdogLock);
2969 :
2970 : /* Wake up any threads doing sleep. */
2971 0 : PR_NotifyAllCondVar(gSleepWakeup);
2972 : } else {
2973 : PRIntervalTime sleepDuration = gWatchdogHasTimeout
2974 : ? gWatchdogTimeout - now
2975 0 : : PR_INTERVAL_NO_TIMEOUT;
2976 : DebugOnly<PRStatus> status =
2977 0 : PR_WaitCondVar(gWatchdogWakeup, sleepDuration);
2978 0 : JS_ASSERT(status == PR_SUCCESS);
2979 : }
2980 : }
2981 0 : PR_Unlock(gWatchdogLock);
2982 0 : }
2983 :
2984 : static bool
2985 0 : ScheduleWatchdog(JSRuntime *rt, double t)
2986 : {
2987 0 : if (t <= 0) {
2988 0 : PR_Lock(gWatchdogLock);
2989 0 : gWatchdogHasTimeout = false;
2990 0 : PR_Unlock(gWatchdogLock);
2991 0 : return true;
2992 : }
2993 :
2994 0 : PRIntervalTime interval = PRIntervalTime(ceil(t * PR_TicksPerSecond()));
2995 0 : PRIntervalTime timeout = PR_IntervalNow() + interval;
2996 0 : PR_Lock(gWatchdogLock);
2997 0 : if (!gWatchdogThread) {
2998 0 : JS_ASSERT(!gWatchdogHasTimeout);
2999 : gWatchdogThread = PR_CreateThread(PR_USER_THREAD,
3000 : WatchdogMain,
3001 : rt,
3002 : PR_PRIORITY_NORMAL,
3003 : PR_LOCAL_THREAD,
3004 : PR_JOINABLE_THREAD,
3005 0 : 0);
3006 0 : if (!gWatchdogThread) {
3007 0 : PR_Unlock(gWatchdogLock);
3008 0 : return false;
3009 : }
3010 0 : } else if (!gWatchdogHasTimeout || IsBefore(timeout, gWatchdogTimeout)) {
3011 0 : PR_NotifyCondVar(gWatchdogWakeup);
3012 : }
3013 0 : gWatchdogHasTimeout = true;
3014 0 : gWatchdogTimeout = timeout;
3015 0 : PR_Unlock(gWatchdogLock);
3016 0 : return true;
3017 : }
3018 :
3019 : #else /* !JS_THREADSAFE */
3020 :
3021 : #ifdef XP_WIN
3022 : static HANDLE gTimerHandle = 0;
3023 :
3024 : VOID CALLBACK
3025 : TimerCallback(PVOID lpParameter, BOOLEAN TimerOrWaitFired)
3026 : {
3027 : CancelExecution((JSRuntime *) lpParameter);
3028 : }
3029 :
3030 : #else
3031 :
3032 : static void
3033 : AlarmHandler(int sig)
3034 : {
3035 : CancelExecution(gRuntime);
3036 : }
3037 :
3038 : #endif
3039 :
3040 : static bool
3041 : InitWatchdog(JSRuntime *rt)
3042 : {
3043 : gRuntime = rt;
3044 : return true;
3045 : }
3046 :
3047 : static void
3048 : KillWatchdog()
3049 : {
3050 : ScheduleWatchdog(gRuntime, -1);
3051 : }
3052 :
3053 : static bool
3054 : ScheduleWatchdog(JSRuntime *rt, double t)
3055 : {
3056 : #ifdef XP_WIN
3057 : if (gTimerHandle) {
3058 : DeleteTimerQueueTimer(NULL, gTimerHandle, NULL);
3059 : gTimerHandle = 0;
3060 : }
3061 : if (t > 0 &&
3062 : !CreateTimerQueueTimer(&gTimerHandle,
3063 : NULL,
3064 : (WAITORTIMERCALLBACK)TimerCallback,
3065 : rt,
3066 : DWORD(ceil(t * 1000.0)),
3067 : 0,
3068 : WT_EXECUTEINTIMERTHREAD | WT_EXECUTEONLYONCE)) {
3069 : gTimerHandle = 0;
3070 : return false;
3071 : }
3072 : #else
3073 : /* FIXME: use setitimer when available for sub-second resolution. */
3074 : if (t <= 0) {
3075 : alarm(0);
3076 : signal(SIGALRM, NULL);
3077 : } else {
3078 : signal(SIGALRM, AlarmHandler); /* set the Alarm signal capture */
3079 : alarm(ceil(t));
3080 : }
3081 : #endif
3082 : return true;
3083 : }
3084 :
3085 : #endif /* !JS_THREADSAFE */
3086 :
3087 : static void
3088 0 : CancelExecution(JSRuntime *rt)
3089 : {
3090 0 : gCanceled = true;
3091 0 : if (gExitCode == 0)
3092 0 : gExitCode = EXITCODE_TIMEOUT;
3093 : #ifdef JS_THREADSAFE
3094 0 : if (gWorkerThreadPool)
3095 0 : js::workers::terminateAll(gWorkerThreadPool);
3096 : #endif
3097 0 : JS_TriggerOperationCallback(rt);
3098 :
3099 : static const char msg[] = "Script runs for too long, terminating.\n";
3100 : #if defined(XP_UNIX) && !defined(JS_THREADSAFE)
3101 : /* It is not safe to call fputs from signals. */
3102 : /* Dummy assignment avoids GCC warning on "attribute warn_unused_result" */
3103 : ssize_t dummy = write(2, msg, sizeof(msg) - 1);
3104 : (void)dummy;
3105 : #else
3106 0 : fputs(msg, stderr);
3107 : #endif
3108 0 : }
3109 :
3110 : static JSBool
3111 0 : SetTimeoutValue(JSContext *cx, double t)
3112 : {
3113 : /* NB: The next condition also filter out NaNs. */
3114 0 : if (!(t <= MAX_TIMEOUT_INTERVAL)) {
3115 0 : JS_ReportError(cx, "Excessive timeout value");
3116 0 : return JS_FALSE;
3117 : }
3118 0 : gTimeoutInterval = t;
3119 0 : if (!ScheduleWatchdog(cx->runtime, t)) {
3120 0 : JS_ReportError(cx, "Failed to create the watchdog");
3121 0 : return JS_FALSE;
3122 : }
3123 0 : return JS_TRUE;
3124 : }
3125 :
3126 : static JSBool
3127 0 : Timeout(JSContext *cx, unsigned argc, jsval *vp)
3128 : {
3129 0 : if (argc == 0)
3130 0 : return JS_NewNumberValue(cx, gTimeoutInterval, vp);
3131 :
3132 0 : if (argc > 1) {
3133 0 : JS_ReportError(cx, "Wrong number of arguments");
3134 0 : return JS_FALSE;
3135 : }
3136 :
3137 : double t;
3138 0 : if (!JS_ValueToNumber(cx, JS_ARGV(cx, vp)[0], &t))
3139 0 : return JS_FALSE;
3140 :
3141 0 : *vp = JSVAL_VOID;
3142 0 : return SetTimeoutValue(cx, t);
3143 : }
3144 :
3145 : static JSBool
3146 0 : Elapsed(JSContext *cx, unsigned argc, jsval *vp)
3147 : {
3148 0 : if (argc == 0) {
3149 0 : double d = 0.0;
3150 0 : JSShellContextData *data = GetContextData(cx);
3151 0 : if (data)
3152 0 : d = js_IntervalNow() - data->startTime;
3153 0 : return JS_NewNumberValue(cx, d, vp);
3154 : }
3155 0 : JS_ReportError(cx, "Wrong number of arguments");
3156 0 : return JS_FALSE;
3157 : }
3158 :
3159 : static JSBool
3160 0 : Parent(JSContext *cx, unsigned argc, jsval *vp)
3161 : {
3162 0 : if (argc != 1) {
3163 0 : JS_ReportError(cx, "Wrong number of arguments");
3164 0 : return JS_FALSE;
3165 : }
3166 :
3167 0 : jsval v = JS_ARGV(cx, vp)[0];
3168 0 : if (JSVAL_IS_PRIMITIVE(v)) {
3169 0 : JS_ReportError(cx, "Only objects have parents!");
3170 0 : return JS_FALSE;
3171 : }
3172 :
3173 0 : JSObject *parent = JS_GetParent(JSVAL_TO_OBJECT(v));
3174 0 : *vp = OBJECT_TO_JSVAL(parent);
3175 :
3176 : /* Outerize if necessary. Embrace the ugliness! */
3177 0 : if (parent) {
3178 0 : if (JSObjectOp op = parent->getClass()->ext.outerObject)
3179 0 : *vp = OBJECT_TO_JSVAL(op(cx, parent));
3180 : }
3181 :
3182 0 : return JS_TRUE;
3183 : }
3184 :
3185 : #ifdef XP_UNIX
3186 :
3187 : #include <fcntl.h>
3188 : #include <sys/stat.h>
3189 :
3190 : /*
3191 : * Returns a JS_malloc'd string (that the caller needs to JS_free)
3192 : * containing the directory (non-leaf) part of |from| prepended to |leaf|.
3193 : * If |from| is empty or a leaf, MakeAbsolutePathname returns a copy of leaf.
3194 : * Returns NULL to indicate an error.
3195 : */
3196 : static char *
3197 0 : MakeAbsolutePathname(JSContext *cx, const char *from, const char *leaf)
3198 : {
3199 : size_t dirlen;
3200 : char *dir;
3201 0 : const char *slash = NULL, *cp;
3202 :
3203 0 : if (*leaf == '/') {
3204 : /* We were given an absolute pathname. */
3205 0 : return JS_strdup(cx, leaf);
3206 : }
3207 :
3208 0 : cp = from;
3209 0 : while (*cp) {
3210 0 : if (*cp == '/') {
3211 0 : slash = cp;
3212 : }
3213 :
3214 0 : ++cp;
3215 : }
3216 :
3217 0 : if (!slash) {
3218 : /* We were given a leaf or |from| was empty. */
3219 0 : return JS_strdup(cx, leaf);
3220 : }
3221 :
3222 : /* Else, we were given a real pathname, return that + the leaf. */
3223 0 : dirlen = slash - from + 1;
3224 0 : dir = (char*) JS_malloc(cx, dirlen + strlen(leaf) + 1);
3225 0 : if (!dir)
3226 0 : return NULL;
3227 :
3228 0 : strncpy(dir, from, dirlen);
3229 0 : strcpy(dir + dirlen, leaf); /* Note: we can't use strcat here. */
3230 :
3231 0 : return dir;
3232 : }
3233 :
3234 : #endif // XP_UNIX
3235 :
3236 : static JSBool
3237 0 : Compile(JSContext *cx, unsigned argc, jsval *vp)
3238 : {
3239 0 : if (argc < 1) {
3240 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
3241 0 : "compile", "0", "s");
3242 0 : return JS_FALSE;
3243 : }
3244 0 : jsval arg0 = JS_ARGV(cx, vp)[0];
3245 0 : if (!JSVAL_IS_STRING(arg0)) {
3246 0 : const char *typeName = JS_GetTypeName(cx, JS_TypeOfValue(cx, arg0));
3247 0 : JS_ReportError(cx, "expected string to compile, got %s", typeName);
3248 0 : return JS_FALSE;
3249 : }
3250 :
3251 : static JSClass dummy_class = {
3252 : "jdummy",
3253 : JSCLASS_GLOBAL_FLAGS,
3254 : JS_PropertyStub, JS_PropertyStub,
3255 : JS_PropertyStub, JS_StrictPropertyStub,
3256 : JS_EnumerateStub, JS_ResolveStub,
3257 : JS_ConvertStub
3258 : };
3259 :
3260 0 : JSObject *fakeGlobal = JS_NewGlobalObject(cx, &dummy_class);
3261 0 : if (!fakeGlobal)
3262 0 : return JS_FALSE;
3263 :
3264 0 : JSString *scriptContents = JSVAL_TO_STRING(arg0);
3265 :
3266 0 : unsigned oldopts = JS_GetOptions(cx);
3267 0 : JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO | JSOPTION_NO_SCRIPT_RVAL);
3268 : bool ok = JS_CompileUCScript(cx, fakeGlobal, JS_GetStringCharsZ(cx, scriptContents),
3269 0 : JS_GetStringLength(scriptContents), "<string>", 0);
3270 0 : JS_SetOptions(cx, oldopts);
3271 :
3272 0 : JS_SET_RVAL(cx, vp, JSVAL_VOID);
3273 0 : return ok;
3274 : }
3275 :
3276 : static JSBool
3277 0 : Parse(JSContext *cx, unsigned argc, jsval *vp)
3278 : {
3279 0 : if (argc < 1) {
3280 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
3281 0 : "compile", "0", "s");
3282 0 : return JS_FALSE;
3283 : }
3284 0 : jsval arg0 = JS_ARGV(cx, vp)[0];
3285 0 : if (!JSVAL_IS_STRING(arg0)) {
3286 0 : const char *typeName = JS_GetTypeName(cx, JS_TypeOfValue(cx, arg0));
3287 0 : JS_ReportError(cx, "expected string to parse, got %s", typeName);
3288 0 : return JS_FALSE;
3289 : }
3290 :
3291 0 : JSString *scriptContents = JSVAL_TO_STRING(arg0);
3292 0 : js::Parser parser(cx);
3293 : parser.init(JS_GetStringCharsZ(cx, scriptContents), JS_GetStringLength(scriptContents),
3294 0 : "<string>", 0, cx->findVersion());
3295 0 : ParseNode *pn = parser.parse(NULL);
3296 0 : if (!pn)
3297 0 : return JS_FALSE;
3298 : #ifdef DEBUG
3299 0 : DumpParseTree(pn);
3300 : #endif
3301 0 : JS_SET_RVAL(cx, vp, JSVAL_VOID);
3302 0 : return JS_TRUE;
3303 : }
3304 :
3305 : struct FreeOnReturn {
3306 : JSContext *cx;
3307 : const char *ptr;
3308 : JS_DECL_USE_GUARD_OBJECT_NOTIFIER
3309 :
3310 0 : FreeOnReturn(JSContext *cx, const char *ptr = NULL JS_GUARD_OBJECT_NOTIFIER_PARAM)
3311 0 : : cx(cx), ptr(ptr) {
3312 0 : JS_GUARD_OBJECT_NOTIFIER_INIT;
3313 0 : }
3314 :
3315 0 : void init(const char *ptr) {
3316 0 : JS_ASSERT(!this->ptr);
3317 0 : this->ptr = ptr;
3318 0 : }
3319 :
3320 0 : ~FreeOnReturn() {
3321 0 : JS_free(cx, (void*)ptr);
3322 0 : }
3323 : };
3324 :
3325 : static JSBool
3326 0 : Snarf(JSContext *cx, unsigned argc, jsval *vp)
3327 : {
3328 : JSString *str;
3329 :
3330 0 : if (!argc)
3331 0 : return JS_FALSE;
3332 :
3333 0 : str = JS_ValueToString(cx, JS_ARGV(cx, vp)[0]);
3334 0 : if (!str)
3335 0 : return JS_FALSE;
3336 0 : JSAutoByteString filename(cx, str);
3337 0 : if (!filename)
3338 0 : return JS_FALSE;
3339 :
3340 : /* Get the currently executing script's name. */
3341 0 : JSScript *script = GetTopScript(cx);
3342 0 : JS_ASSERT(script->filename);
3343 0 : const char *pathname = filename.ptr();
3344 : #ifdef XP_UNIX
3345 0 : FreeOnReturn pnGuard(cx);
3346 0 : if (pathname[0] != '/') {
3347 0 : pathname = MakeAbsolutePathname(cx, script->filename, pathname);
3348 0 : if (!pathname)
3349 0 : return JS_FALSE;
3350 0 : pnGuard.init(pathname);
3351 : }
3352 : #endif
3353 :
3354 0 : if (argc > 1) {
3355 0 : JSString *opt = JS_ValueToString(cx, JS_ARGV(cx, vp)[1]);
3356 0 : if (!opt)
3357 0 : return JS_FALSE;
3358 : JSBool match;
3359 0 : if (!JS_StringEqualsAscii(cx, opt, "binary", &match))
3360 0 : return JS_FALSE;
3361 0 : if (match) {
3362 : JSObject *obj;
3363 0 : if (!(obj = FileAsTypedArray(cx, pathname)))
3364 0 : return JS_FALSE;
3365 0 : *vp = OBJECT_TO_JSVAL(obj);
3366 0 : return JS_TRUE;
3367 : }
3368 : }
3369 :
3370 0 : if (!(str = FileAsString(cx, pathname)))
3371 0 : return JS_FALSE;
3372 0 : *vp = STRING_TO_JSVAL(str);
3373 0 : return JS_TRUE;
3374 : }
3375 :
3376 : JSBool
3377 27 : Wrap(JSContext *cx, unsigned argc, jsval *vp)
3378 : {
3379 27 : jsval v = argc > 0 ? JS_ARGV(cx, vp)[0] : JSVAL_VOID;
3380 27 : if (JSVAL_IS_PRIMITIVE(v)) {
3381 9 : JS_SET_RVAL(cx, vp, v);
3382 9 : return true;
3383 : }
3384 :
3385 18 : JSObject *obj = JSVAL_TO_OBJECT(v);
3386 18 : JSObject *wrapped = Wrapper::New(cx, obj, obj->getProto(), &obj->global(),
3387 18 : &Wrapper::singleton);
3388 18 : if (!wrapped)
3389 0 : return false;
3390 :
3391 18 : JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(wrapped));
3392 18 : return true;
3393 : }
3394 :
3395 : JSBool
3396 54 : Serialize(JSContext *cx, unsigned argc, jsval *vp)
3397 : {
3398 54 : jsval v = argc > 0 ? JS_ARGV(cx, vp)[0] : JSVAL_VOID;
3399 : uint64_t *datap;
3400 : size_t nbytes;
3401 54 : if (!JS_WriteStructuredClone(cx, v, &datap, &nbytes, NULL, NULL))
3402 0 : return false;
3403 :
3404 54 : JSObject *arrayobj = js_CreateTypedArray(cx, TypedArray::TYPE_UINT8, nbytes);
3405 54 : if (!arrayobj) {
3406 0 : JS_free(cx, datap);
3407 0 : return false;
3408 : }
3409 54 : JSObject *array = TypedArray::getTypedArray(arrayobj);
3410 54 : JS_ASSERT((uintptr_t(TypedArray::getDataOffset(array)) & 7) == 0);
3411 54 : js_memcpy(TypedArray::getDataOffset(array), datap, nbytes);
3412 54 : JS_free(cx, datap);
3413 54 : JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(arrayobj));
3414 54 : return true;
3415 : }
3416 :
3417 : JSBool
3418 9 : Deserialize(JSContext *cx, unsigned argc, jsval *vp)
3419 : {
3420 9 : jsval v = argc > 0 ? JS_ARGV(cx, vp)[0] : JSVAL_VOID;
3421 : JSObject *obj;
3422 9 : if (JSVAL_IS_PRIMITIVE(v) || !js_IsTypedArray((obj = JSVAL_TO_OBJECT(v)))) {
3423 0 : JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_INVALID_ARGS, "deserialize");
3424 0 : return false;
3425 : }
3426 9 : JSObject *array = TypedArray::getTypedArray(obj);
3427 9 : if ((TypedArray::getByteLength(array) & 7) != 0) {
3428 0 : JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_INVALID_ARGS, "deserialize");
3429 0 : return false;
3430 : }
3431 9 : if ((uintptr_t(TypedArray::getDataOffset(array)) & 7) != 0) {
3432 0 : JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_BAD_ALIGNMENT);
3433 0 : return false;
3434 : }
3435 :
3436 18 : if (!JS_ReadStructuredClone(cx, (uint64_t *) TypedArray::getDataOffset(array), TypedArray::getByteLength(array),
3437 18 : JS_STRUCTURED_CLONE_VERSION, &v, NULL, NULL)) {
3438 0 : return false;
3439 : }
3440 9 : JS_SET_RVAL(cx, vp, v);
3441 9 : return true;
3442 : }
3443 :
3444 : enum CompartmentKind { SAME_COMPARTMENT, NEW_COMPARTMENT };
3445 :
3446 : static JSObject *
3447 : NewGlobalObject(JSContext *cx, CompartmentKind compartment);
3448 :
3449 : JSBool
3450 4662 : NewGlobal(JSContext *cx, unsigned argc, jsval *vp)
3451 : {
3452 4662 : if (argc != 1 || !JSVAL_IS_STRING(JS_ARGV(cx, vp)[0])) {
3453 0 : JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_INVALID_ARGS, "newGlobal");
3454 0 : return false;
3455 : }
3456 :
3457 4662 : JSString *str = JSVAL_TO_STRING(JS_ARGV(cx, vp)[0]);
3458 :
3459 4662 : JSBool equalSame = JS_FALSE, equalNew = JS_FALSE;
3460 9324 : if (!JS_StringEqualsAscii(cx, str, "same-compartment", &equalSame) ||
3461 4662 : !JS_StringEqualsAscii(cx, str, "new-compartment", &equalNew)) {
3462 0 : return false;
3463 : }
3464 :
3465 4662 : if (!equalSame && !equalNew) {
3466 0 : JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_INVALID_ARGS, "newGlobal");
3467 0 : return false;
3468 : }
3469 :
3470 4662 : JSObject *global = NewGlobalObject(cx, equalSame ? SAME_COMPARTMENT : NEW_COMPARTMENT);
3471 4662 : if (!global)
3472 0 : return false;
3473 :
3474 4662 : JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(global));
3475 4662 : return true;
3476 : }
3477 :
3478 : static JSBool
3479 0 : ParseLegacyJSON(JSContext *cx, unsigned argc, jsval *vp)
3480 : {
3481 0 : if (argc != 1 || !JSVAL_IS_STRING(JS_ARGV(cx, vp)[0])) {
3482 0 : JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_INVALID_ARGS, "parseLegacyJSON");
3483 0 : return false;
3484 : }
3485 :
3486 0 : JSString *str = JSVAL_TO_STRING(JS_ARGV(cx, vp)[0]);
3487 :
3488 : size_t length;
3489 0 : const jschar *chars = JS_GetStringCharsAndLength(cx, str, &length);
3490 0 : if (!chars)
3491 0 : return false;
3492 0 : return js::ParseJSONWithReviver(cx, chars, length, js::NullValue(), vp, LEGACY);
3493 : }
3494 :
3495 : static JSBool
3496 0 : EnableStackWalkingAssertion(JSContext *cx, unsigned argc, jsval *vp)
3497 : {
3498 0 : if (argc == 0 || !JSVAL_IS_BOOLEAN(JS_ARGV(cx, vp)[0])) {
3499 : JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_INVALID_ARGS,
3500 0 : "enableStackWalkingAssertion");
3501 0 : return false;
3502 : }
3503 :
3504 : #ifdef DEBUG
3505 0 : cx->stackIterAssertionEnabled = JSVAL_TO_BOOLEAN(JS_ARGV(cx, vp)[0]);
3506 : #endif
3507 :
3508 0 : JS_SET_RVAL(cx, vp, JSVAL_VOID);
3509 0 : return true;
3510 : }
3511 :
3512 : static JSBool
3513 234 : GetMaxArgs(JSContext *cx, unsigned arg, jsval *vp)
3514 : {
3515 234 : JS_SET_RVAL(cx, vp, INT_TO_JSVAL(StackSpace::ARGS_LENGTH_MAX));
3516 234 : return JS_TRUE;
3517 : }
3518 :
3519 : static JSFunctionSpecWithHelp shell_functions[] = {
3520 : JS_FN_HELP("version", Version, 0, 0,
3521 : "version([number])",
3522 : " Get or force a script compilation version number."),
3523 :
3524 : JS_FN_HELP("revertVersion", RevertVersion, 0, 0,
3525 : "revertVersion()",
3526 : " Revert previously set version number."),
3527 :
3528 : JS_FN_HELP("options", Options, 0, 0,
3529 : "options([option ...])",
3530 : " Get or toggle JavaScript options."),
3531 :
3532 : JS_FN_HELP("load", Load, 1, 0,
3533 : "load(['foo.js' ...])",
3534 : " Load files named by string arguments."),
3535 :
3536 : JS_FN_HELP("evaluate", Evaluate, 1, 0,
3537 : "evaluate(code)",
3538 : " Evaluate code as though it were the contents of a file."),
3539 :
3540 : JS_FN_HELP("evalWithLocation", EvaluateWithLocation, 3, 0,
3541 : "evalWithLocation(code, filename, lineno)",
3542 : " Eval code as if loaded from the given filename and line number."),
3543 :
3544 : JS_FN_HELP("run", Run, 1, 0,
3545 : "run('foo.js')",
3546 : " Run the file named by the first argument, returning the number of\n"
3547 : " of milliseconds spent compiling and executing it."),
3548 :
3549 : JS_FN_HELP("readline", ReadLine, 0, 0,
3550 : "readline()",
3551 : " Read a single line from stdin."),
3552 :
3553 : JS_FN_HELP("print", Print, 0, 0,
3554 : "print([exp ...])",
3555 : " Evaluate and print expressions to stdout."),
3556 :
3557 : JS_FN_HELP("printErr", PrintErr, 0, 0,
3558 : "printErr([exp ...])",
3559 : " Evaluate and print expressions to stderr."),
3560 :
3561 : JS_FN_HELP("putstr", PutStr, 0, 0,
3562 : "putstr([exp])",
3563 : " Evaluate and print expression without newline."),
3564 :
3565 : JS_FN_HELP("dateNow", Now, 0, 0,
3566 : "dateNow()",
3567 : " Return the current time with sub-ms precision."),
3568 :
3569 : JS_FN_HELP("help", Help, 0, 0,
3570 : "help([name ...])",
3571 : " Display usage and help messages."),
3572 :
3573 : JS_FN_HELP("quit", Quit, 0, 0,
3574 : "quit()",
3575 : " Quit the shell."),
3576 :
3577 : JS_FN_HELP("assertEq", AssertEq, 2, 0,
3578 : "assertEq(actual, expected[, msg])",
3579 : " Throw if the first two arguments are not the same (both +0 or both -0,\n"
3580 : " both NaN, or non-zero and ===)."),
3581 :
3582 : JS_FN_HELP("assertJit", AssertJit, 0, 0,
3583 : "assertJit()",
3584 : " Throw if the calling function failed to JIT."),
3585 :
3586 : JS_FN_HELP("setDebug", SetDebug, 1, 0,
3587 : "setDebug(debug)",
3588 : " Set debug mode."),
3589 :
3590 : JS_FN_HELP("setDebuggerHandler", SetDebuggerHandler, 1, 0,
3591 : "setDebuggerHandler(f)",
3592 : " Set handler for debugger keyword to f."),
3593 :
3594 : JS_FN_HELP("setThrowHook", SetThrowHook, 1, 0,
3595 : "setThrowHook(f)",
3596 : " Set throw hook to f."),
3597 :
3598 : JS_FN_HELP("trap", Trap, 3, 0,
3599 : "trap([fun, [pc,]] exp)",
3600 : " Trap bytecode execution."),
3601 :
3602 : JS_FN_HELP("untrap", Untrap, 2, 0,
3603 : "untrap(fun[, pc])",
3604 : " Remove a trap."),
3605 :
3606 : JS_FN_HELP("line2pc", LineToPC, 0, 0,
3607 : "line2pc([fun,] line)",
3608 : " Map line number to PC."),
3609 :
3610 : JS_FN_HELP("pc2line", PCToLine, 0, 0,
3611 : "pc2line(fun[, pc])",
3612 : " Map PC to line number."),
3613 :
3614 : JS_FN_HELP("stringsAreUTF8", StringsAreUTF8, 0, 0,
3615 : "stringsAreUTF8()",
3616 : " Check if strings are UTF-8 encoded."),
3617 :
3618 : JS_FN_HELP("testUTF8", TestUTF8, 1, 0,
3619 : "testUTF8(mode)",
3620 : " Perform UTF-8 tests (modes are 1 to 4)."),
3621 :
3622 : JS_FN_HELP("throwError", ThrowError, 0, 0,
3623 : "throwError()",
3624 : " Throw an error from JS_ReportError."),
3625 :
3626 : #ifdef DEBUG
3627 : JS_FN_HELP("disassemble", DisassembleToString, 1, 0,
3628 : "disassemble([fun])",
3629 : " Return the disassembly for the given function."),
3630 :
3631 : JS_FN_HELP("dis", Disassemble, 1, 0,
3632 : "dis([fun])",
3633 : " Disassemble functions into bytecodes."),
3634 :
3635 : JS_FN_HELP("disfile", DisassFile, 1, 0,
3636 : "disfile('foo.js')",
3637 : " Disassemble script file into bytecodes.\n"
3638 : " dis and disfile take these options as preceeding string arguments:\n"
3639 : " \"-r\" (disassemble recursively)\n"
3640 : " \"-l\" (show line numbers)"),
3641 :
3642 : JS_FN_HELP("dissrc", DisassWithSrc, 1, 0,
3643 : "dissrc([fun])",
3644 : " Disassemble functions with source lines."),
3645 :
3646 : JS_FN_HELP("dumpHeap", DumpHeap, 0, 0,
3647 : "dumpHeap([fileName[, start[, toFind[, maxDepth[, toIgnore]]]]])",
3648 : " Interface to JS_DumpHeap with output sent to file."),
3649 :
3650 : JS_FN_HELP("dumpObject", DumpObject, 1, 0,
3651 : "dumpObject()",
3652 : " Dump an internal representation of an object."),
3653 :
3654 : JS_FN_HELP("notes", Notes, 1, 0,
3655 : "notes([fun])",
3656 : " Show source notes for functions."),
3657 :
3658 : JS_FN_HELP("stats", DumpStats, 1, 0,
3659 : "stats([string ...])",
3660 : " Dump 'atom' or 'global' stats."),
3661 :
3662 : JS_FN_HELP("findReferences", FindReferences, 1, 0,
3663 : "findReferences(target)",
3664 : " Walk the entire heap, looking for references to |target|, and return a\n"
3665 : " \"references object\" describing what we found.\n"
3666 : "\n"
3667 : " Each property of the references object describes one kind of reference. The\n"
3668 : " property's name is the label supplied to MarkObject, JS_CALL_TRACER, or what\n"
3669 : " have you, prefixed with \"edge: \" to avoid collisions with system properties\n"
3670 : " (like \"toString\" and \"__proto__\"). The property's value is an array of things\n"
3671 : " that refer to |thing| via that kind of reference. Ordinary references from\n"
3672 : " one object to another are named after the property name (with the \"edge: \"\n"
3673 : " prefix).\n"
3674 : "\n"
3675 : " Garbage collection roots appear as references from 'null'. We use the name\n"
3676 : " given to the root (with the \"edge: \" prefix) as the name of the reference.\n"
3677 : "\n"
3678 : " Note that the references object does record references from objects that are\n"
3679 : " only reachable via |thing| itself, not just the references reachable\n"
3680 : " themselves from roots that keep |thing| from being collected. (We could make\n"
3681 : " this distinction if it is useful.)\n"
3682 : "\n"
3683 : " If any references are found by the conservative scanner, the references\n"
3684 : " object will have a property named \"edge: machine stack\"; the referrers will\n"
3685 : " be 'null', because they are roots."),
3686 :
3687 : #endif
3688 : JS_FN_HELP("dumpStack", DumpStack, 1, 0,
3689 : "dumpStack()",
3690 : " Dump the stack as an array of callees (youngest first)."),
3691 :
3692 : #ifdef TEST_CVTARGS
3693 : JS_FN_HELP("cvtargs", ConvertArgs, 0, 0,
3694 : "cvtargs(arg1..., arg12)",
3695 : " Test argument formatter."),
3696 :
3697 : #endif
3698 : JS_FN_HELP("build", BuildDate, 0, 0,
3699 : "build()",
3700 : " Show build date and time."),
3701 :
3702 : JS_FN_HELP("clear", Clear, 0, 0,
3703 : "clear([obj])",
3704 : " Clear properties of object."),
3705 :
3706 : JS_FN_HELP("intern", Intern, 1, 0,
3707 : "intern(str)",
3708 : " Internalize str in the atom table."),
3709 :
3710 : JS_FN_HELP("clone", Clone, 1, 0,
3711 : "clone(fun[, scope])",
3712 : " Clone function object."),
3713 :
3714 : JS_FN_HELP("getpda", GetPDA, 1, 0,
3715 : "getpda(obj)",
3716 : " Get the property descriptors for obj."),
3717 :
3718 : JS_FN_HELP("getslx", GetSLX, 1, 0,
3719 : "getslx(obj)",
3720 : " Get script line extent."),
3721 :
3722 : JS_FN_HELP("toint32", ToInt32, 1, 0,
3723 : "toint32(n)",
3724 : " Testing hook for JS_ValueToInt32."),
3725 :
3726 : JS_FN_HELP("evalcx", EvalInContext, 1, 0,
3727 : "evalcx(s[, o])",
3728 : " Evaluate s in optional sandbox object o.\n"
3729 : " if (s == '' && !o) return new o with eager standard classes\n"
3730 : " if (s == 'lazy' && !o) return new o with lazy standard classes"),
3731 :
3732 : JS_FN_HELP("evalInFrame", EvalInFrame, 2, 0,
3733 : "evalInFrame(n,str,save)",
3734 : " Evaluate 'str' in the nth up frame.\n"
3735 : " If 'save' (default false), save the frame chain."),
3736 :
3737 : JS_FN_HELP("shapeOf", ShapeOf, 1, 0,
3738 : "shapeOf(obj)",
3739 : " Get the shape of obj (an implementation detail)."),
3740 :
3741 : JS_FN_HELP("resolver", Resolver, 1, 0,
3742 : "resolver(src[, proto])",
3743 : " Create object with resolve hook that copies properties\n"
3744 : " from src. If proto is omitted, use Object.prototype."),
3745 :
3746 : #ifdef DEBUG
3747 : JS_FN_HELP("arrayInfo", js_ArrayInfo, 1, 0,
3748 : "arrayInfo(a1, a2, ...)",
3749 : " Report statistics about arrays."),
3750 :
3751 : #endif
3752 : #ifdef JS_THREADSAFE
3753 : JS_FN_HELP("sleep", Sleep_fn, 1, 0,
3754 : "sleep(dt)",
3755 : " Sleep for dt seconds."),
3756 :
3757 : #endif
3758 : JS_FN_HELP("snarf", Snarf, 0, 0,
3759 : "snarf(filename)",
3760 : " Read filename into returned string."),
3761 :
3762 : JS_FN_HELP("read", Snarf, 0, 0,
3763 : "read(filename)",
3764 : " Synonym for snarf."),
3765 :
3766 : JS_FN_HELP("compile", Compile, 1, 0,
3767 : "compile(code)",
3768 : " Compiles a string to bytecode, potentially throwing."),
3769 :
3770 : JS_FN_HELP("parse", Parse, 1, 0,
3771 : "parse(code)",
3772 : " Parses a string, potentially throwing."),
3773 :
3774 : JS_FN_HELP("timeout", Timeout, 1, 0,
3775 : "timeout([seconds])",
3776 : " Get/Set the limit in seconds for the execution time for the current context.\n"
3777 : " A negative value (default) means that the execution time is unlimited."),
3778 :
3779 : JS_FN_HELP("elapsed", Elapsed, 0, 0,
3780 : "elapsed()",
3781 : " Execution time elapsed for the current context."),
3782 :
3783 : JS_FN_HELP("parent", Parent, 1, 0,
3784 : "parent(obj)",
3785 : " Returns the parent of obj."),
3786 :
3787 : JS_FN_HELP("wrap", Wrap, 1, 0,
3788 : "wrap(obj)",
3789 : " Wrap an object into a noop wrapper."),
3790 :
3791 : JS_FN_HELP("serialize", Serialize, 1, 0,
3792 : "serialize(sd)",
3793 : " Serialize sd using JS_WriteStructuredClone. Returns a TypedArray."),
3794 :
3795 : JS_FN_HELP("deserialize", Deserialize, 1, 0,
3796 : "deserialize(a)",
3797 : " Deserialize data generated by serialize."),
3798 :
3799 : JS_FN_HELP("newGlobal", NewGlobal, 1, 0,
3800 : "newGlobal(kind)",
3801 : " Return a new global object, in the current\n"
3802 : " compartment if kind === 'same-compartment' or in a\n"
3803 : " new compartment if kind === 'new-compartment'."),
3804 :
3805 : JS_FN_HELP("parseLegacyJSON", ParseLegacyJSON, 1, 0,
3806 : "parseLegacyJSON(str)",
3807 : " Parse str as legacy JSON, returning the result if the\n"
3808 : " parse succeeded and throwing a SyntaxError if not."),
3809 :
3810 : JS_FN_HELP("enableStackWalkingAssertion", EnableStackWalkingAssertion, 1, 0,
3811 : "enableStackWalkingAssertion(enabled)",
3812 : " Enables or disables a particularly expensive assertion in stack-walking\n"
3813 : " code. If your test isn't ridiculously thorough, such that performing this\n"
3814 : " assertion increases test duration by an order of magnitude, you shouldn't\n"
3815 : " use this."),
3816 :
3817 : JS_FN_HELP("getMaxArgs", GetMaxArgs, 0, 0,
3818 : "getMaxArgs()",
3819 : " Return the maximum number of supported args for a call."),
3820 :
3821 : JS_FS_END
3822 : };
3823 : #ifdef MOZ_PROFILING
3824 : # define PROFILING_FUNCTION_COUNT 5
3825 : # ifdef MOZ_CALLGRIND
3826 : # define CALLGRIND_FUNCTION_COUNT 3
3827 : # else
3828 : # define CALLGRIND_FUNCTION_COUNT 0
3829 : # endif
3830 : # ifdef MOZ_VTUNE
3831 : # define VTUNE_FUNCTION_COUNT 4
3832 : # else
3833 : # define VTUNE_FUNCTION_COUNT 0
3834 : # endif
3835 : # define EXTERNAL_FUNCTION_COUNT (PROFILING_FUNCTION_COUNT + CALLGRIND_FUNCTION_COUNT + VTUNE_FUNCTION_COUNT)
3836 : #else
3837 : # define EXTERNAL_FUNCTION_COUNT 0
3838 : #endif
3839 :
3840 : #undef PROFILING_FUNCTION_COUNT
3841 : #undef CALLGRIND_FUNCTION_COUNT
3842 : #undef VTUNE_FUNCTION_COUNT
3843 : #undef EXTERNAL_FUNCTION_COUNT
3844 :
3845 : static bool
3846 0 : PrintHelpString(JSContext *cx, jsval v)
3847 : {
3848 0 : JSString *str = JSVAL_TO_STRING(v);
3849 0 : JS::Anchor<JSString *> a_str(str);
3850 0 : const jschar *chars = JS_GetStringCharsZ(cx, str);
3851 0 : if (!chars)
3852 0 : return false;
3853 :
3854 0 : for (const jschar *p = chars; *p; p++)
3855 0 : fprintf(gOutFile, "%c", char(*p));
3856 :
3857 0 : fprintf(gOutFile, "\n");
3858 :
3859 0 : return true;
3860 : }
3861 :
3862 : static bool
3863 0 : PrintHelp(JSContext *cx, JSObject *obj)
3864 : {
3865 : jsval usage, help;
3866 0 : if (!JS_LookupProperty(cx, obj, "usage", &usage))
3867 0 : return false;
3868 0 : if (!JS_LookupProperty(cx, obj, "help", &help))
3869 0 : return false;
3870 :
3871 0 : if (JSVAL_IS_VOID(usage) || JSVAL_IS_VOID(help))
3872 0 : return true;
3873 :
3874 0 : return PrintHelpString(cx, usage) && PrintHelpString(cx, help);
3875 : }
3876 :
3877 : static JSBool
3878 0 : Help(JSContext *cx, unsigned argc, jsval *vp)
3879 : {
3880 0 : fprintf(gOutFile, "%s\n", JS_GetImplementationVersion());
3881 :
3882 0 : if (argc == 0) {
3883 0 : JSObject *global = JS_GetGlobalObject(cx);
3884 0 : AutoIdArray ida(cx, JS_Enumerate(cx, global));
3885 0 : if (!ida)
3886 0 : return false;
3887 :
3888 0 : for (size_t i = 0; i < ida.length(); i++) {
3889 : jsval v;
3890 0 : if (!JS_LookupPropertyById(cx, global, ida[i], &v))
3891 0 : return false;
3892 0 : if (JSVAL_IS_OBJECT(v) && !PrintHelp(cx, JSVAL_TO_OBJECT(v)))
3893 0 : return false;
3894 : }
3895 : } else {
3896 0 : jsval *argv = JS_ARGV(cx, vp);
3897 0 : for (unsigned i = 0; i < argc; i++) {
3898 0 : if (JSVAL_IS_OBJECT(argv[i]) && !PrintHelp(cx, JSVAL_TO_OBJECT(argv[i])))
3899 0 : return false;
3900 : }
3901 : }
3902 :
3903 0 : JS_SET_RVAL(cx, vp, JSVAL_VOID);
3904 0 : return true;
3905 : }
3906 :
3907 : /*
3908 : * Define a JS object called "it". Give it class operations that printf why
3909 : * they're being called for tutorial purposes.
3910 : */
3911 : enum its_tinyid {
3912 : ITS_COLOR, ITS_HEIGHT, ITS_WIDTH, ITS_FUNNY, ITS_ARRAY, ITS_RDONLY,
3913 : ITS_CUSTOM, ITS_CUSTOMRDONLY
3914 : };
3915 :
3916 : static JSBool
3917 : its_getter(JSContext *cx, JSObject *obj, jsid id, jsval *vp);
3918 :
3919 : static JSBool
3920 : its_setter(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp);
3921 :
3922 : static JSPropertySpec its_props[] = {
3923 : {"color", ITS_COLOR, JSPROP_ENUMERATE, NULL, NULL},
3924 : {"height", ITS_HEIGHT, JSPROP_ENUMERATE, NULL, NULL},
3925 : {"width", ITS_WIDTH, JSPROP_ENUMERATE, NULL, NULL},
3926 : {"funny", ITS_FUNNY, JSPROP_ENUMERATE, NULL, NULL},
3927 : {"array", ITS_ARRAY, JSPROP_ENUMERATE, NULL, NULL},
3928 : {"rdonly", ITS_RDONLY, JSPROP_READONLY, NULL, NULL},
3929 : {"custom", ITS_CUSTOM, JSPROP_ENUMERATE,
3930 : its_getter, its_setter},
3931 : {"customRdOnly", ITS_CUSTOMRDONLY, JSPROP_ENUMERATE | JSPROP_READONLY,
3932 : its_getter, its_setter},
3933 : {NULL,0,0,NULL,NULL}
3934 : };
3935 :
3936 : static JSBool its_noisy; /* whether to be noisy when finalizing it */
3937 : static JSBool its_enum_fail;/* whether to fail when enumerating it */
3938 :
3939 : static JSBool
3940 186624 : its_addProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
3941 : {
3942 186624 : if (!its_noisy)
3943 186624 : return JS_TRUE;
3944 :
3945 0 : IdStringifier idString(cx, id);
3946 0 : fprintf(gOutFile, "adding its property %s,", idString.getBytes());
3947 0 : ToStringHelper valueString(cx, *vp);
3948 0 : fprintf(gOutFile, " initial value %s\n", valueString.getBytes());
3949 0 : return JS_TRUE;
3950 : }
3951 :
3952 : static JSBool
3953 0 : its_delProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
3954 : {
3955 0 : if (!its_noisy)
3956 0 : return JS_TRUE;
3957 :
3958 0 : IdStringifier idString(cx, id);
3959 0 : fprintf(gOutFile, "deleting its property %s,", idString.getBytes());
3960 0 : ToStringHelper valueString(cx, *vp);
3961 0 : fprintf(gOutFile, " initial value %s\n", valueString.getBytes());
3962 0 : return JS_TRUE;
3963 : }
3964 :
3965 : static JSBool
3966 0 : its_getProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
3967 : {
3968 0 : if (!its_noisy)
3969 0 : return JS_TRUE;
3970 :
3971 0 : IdStringifier idString(cx, id);
3972 0 : fprintf(gOutFile, "getting its property %s,", idString.getBytes());
3973 0 : ToStringHelper valueString(cx, *vp);
3974 0 : fprintf(gOutFile, " initial value %s\n", valueString.getBytes());
3975 0 : return JS_TRUE;
3976 : }
3977 :
3978 : static JSBool
3979 0 : its_setProperty(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp)
3980 : {
3981 0 : IdStringifier idString(cx, id);
3982 0 : if (its_noisy) {
3983 0 : fprintf(gOutFile, "setting its property %s,", idString.getBytes());
3984 0 : ToStringHelper valueString(cx, *vp);
3985 0 : fprintf(gOutFile, " new value %s\n", valueString.getBytes());
3986 : }
3987 :
3988 0 : if (!JSID_IS_ATOM(id))
3989 0 : return JS_TRUE;
3990 :
3991 0 : if (!strcmp(idString.getBytes(), "noisy"))
3992 0 : JS_ValueToBoolean(cx, *vp, &its_noisy);
3993 0 : else if (!strcmp(idString.getBytes(), "enum_fail"))
3994 0 : JS_ValueToBoolean(cx, *vp, &its_enum_fail);
3995 :
3996 0 : return JS_TRUE;
3997 : }
3998 :
3999 : /*
4000 : * Its enumerator, implemented using the "new" enumerate API,
4001 : * see class flags.
4002 : */
4003 : static JSBool
4004 0 : its_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
4005 : jsval *statep, jsid *idp)
4006 : {
4007 : JSObject *iterator;
4008 :
4009 0 : switch (enum_op) {
4010 : case JSENUMERATE_INIT:
4011 : case JSENUMERATE_INIT_ALL:
4012 0 : if (its_noisy)
4013 0 : fprintf(gOutFile, "enumerate its properties\n");
4014 :
4015 0 : iterator = JS_NewPropertyIterator(cx, obj);
4016 0 : if (!iterator)
4017 0 : return JS_FALSE;
4018 :
4019 0 : *statep = OBJECT_TO_JSVAL(iterator);
4020 0 : if (idp)
4021 0 : *idp = INT_TO_JSID(0);
4022 0 : break;
4023 :
4024 : case JSENUMERATE_NEXT:
4025 0 : if (its_enum_fail) {
4026 0 : JS_ReportError(cx, "its enumeration failed");
4027 0 : return JS_FALSE;
4028 : }
4029 :
4030 0 : iterator = (JSObject *) JSVAL_TO_OBJECT(*statep);
4031 0 : if (!JS_NextProperty(cx, iterator, idp))
4032 0 : return JS_FALSE;
4033 :
4034 0 : if (!JSID_IS_VOID(*idp))
4035 0 : break;
4036 : /* Fall through. */
4037 :
4038 : case JSENUMERATE_DESTROY:
4039 : /* Allow our iterator object to be GC'd. */
4040 0 : *statep = JSVAL_NULL;
4041 0 : break;
4042 : }
4043 :
4044 0 : return JS_TRUE;
4045 : }
4046 :
4047 : static JSBool
4048 0 : its_resolve(JSContext *cx, JSObject *obj, jsid id, unsigned flags,
4049 : JSObject **objp)
4050 : {
4051 0 : if (its_noisy) {
4052 0 : IdStringifier idString(cx, id);
4053 : fprintf(gOutFile, "resolving its property %s, flags {%s,%s,%s}\n",
4054 : idString.getBytes(),
4055 : (flags & JSRESOLVE_QUALIFIED) ? "qualified" : "",
4056 : (flags & JSRESOLVE_ASSIGNING) ? "assigning" : "",
4057 0 : (flags & JSRESOLVE_DETECTING) ? "detecting" : "");
4058 : }
4059 0 : return JS_TRUE;
4060 : }
4061 :
4062 : static JSBool
4063 0 : its_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
4064 : {
4065 0 : if (its_noisy)
4066 0 : fprintf(gOutFile, "converting it to %s type\n", JS_GetTypeName(cx, type));
4067 0 : return JS_ConvertStub(cx, obj, type, vp);
4068 : }
4069 :
4070 : static void
4071 46656 : its_finalize(JSFreeOp *fop, JSObject *obj)
4072 : {
4073 : jsval *rootedVal;
4074 46656 : if (its_noisy)
4075 0 : fprintf(gOutFile, "finalizing it\n");
4076 46656 : rootedVal = (jsval *) JS_GetPrivate(obj);
4077 46656 : if (rootedVal) {
4078 0 : JS_RemoveValueRootRT(fop->runtime(), rootedVal);
4079 0 : JS_SetPrivate(obj, NULL);
4080 0 : delete rootedVal;
4081 : }
4082 46656 : }
4083 :
4084 : static JSClass its_class = {
4085 : "It", JSCLASS_NEW_RESOLVE | JSCLASS_NEW_ENUMERATE | JSCLASS_HAS_PRIVATE,
4086 : its_addProperty, its_delProperty, its_getProperty, its_setProperty,
4087 : (JSEnumerateOp)its_enumerate, (JSResolveOp)its_resolve,
4088 : its_convert, its_finalize
4089 : };
4090 :
4091 : static JSBool
4092 144 : its_getter(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
4093 : {
4094 144 : if (JS_GetClass(obj) == &its_class) {
4095 0 : jsval *val = (jsval *) JS_GetPrivate(obj);
4096 0 : *vp = val ? *val : JSVAL_VOID;
4097 : } else {
4098 144 : *vp = JSVAL_VOID;
4099 : }
4100 :
4101 144 : return JS_TRUE;
4102 : }
4103 :
4104 : static JSBool
4105 0 : its_setter(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp)
4106 : {
4107 0 : if (JS_GetClass(obj) != &its_class)
4108 0 : return JS_TRUE;
4109 :
4110 0 : jsval *val = (jsval *) JS_GetPrivate(obj);
4111 0 : if (val) {
4112 0 : *val = *vp;
4113 0 : return JS_TRUE;
4114 : }
4115 :
4116 0 : val = new jsval;
4117 0 : if (!val) {
4118 0 : JS_ReportOutOfMemory(cx);
4119 0 : return JS_FALSE;
4120 : }
4121 :
4122 0 : if (!JS_AddValueRoot(cx, val)) {
4123 0 : delete val;
4124 0 : return JS_FALSE;
4125 : }
4126 :
4127 0 : JS_SetPrivate(obj, (void*)val);
4128 :
4129 0 : *val = *vp;
4130 0 : return JS_TRUE;
4131 : }
4132 :
4133 : JSErrorFormatString jsShell_ErrorFormatString[JSShellErr_Limit] = {
4134 : #define MSG_DEF(name, number, count, exception, format) \
4135 : { format, count, JSEXN_ERR } ,
4136 : #include "jsshell.msg"
4137 : #undef MSG_DEF
4138 : };
4139 :
4140 : static const JSErrorFormatString *
4141 540 : my_GetErrorMessage(void *userRef, const char *locale, const unsigned errorNumber)
4142 : {
4143 540 : if (errorNumber == 0 || errorNumber >= JSShellErr_Limit)
4144 0 : return NULL;
4145 :
4146 540 : return &jsShell_ErrorFormatString[errorNumber];
4147 : }
4148 :
4149 : static void
4150 927 : my_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
4151 : {
4152 : int i, j, k, n;
4153 : char *prefix, *tmp;
4154 : const char *ctmp;
4155 :
4156 927 : if (!report) {
4157 0 : fprintf(gErrFile, "%s\n", message);
4158 0 : fflush(gErrFile);
4159 0 : return;
4160 : }
4161 :
4162 : /* Conditionally ignore reported warnings. */
4163 927 : if (JSREPORT_IS_WARNING(report->flags) && !reportWarnings)
4164 0 : return;
4165 :
4166 927 : prefix = NULL;
4167 927 : if (report->filename)
4168 783 : prefix = JS_smprintf("%s:", report->filename);
4169 927 : if (report->lineno) {
4170 783 : tmp = prefix;
4171 783 : prefix = JS_smprintf("%s%u: ", tmp ? tmp : "", report->lineno);
4172 783 : JS_free(cx, tmp);
4173 : }
4174 927 : if (JSREPORT_IS_WARNING(report->flags)) {
4175 72 : tmp = prefix;
4176 : prefix = JS_smprintf("%s%swarning: ",
4177 : tmp ? tmp : "",
4178 72 : JSREPORT_IS_STRICT(report->flags) ? "strict " : "");
4179 72 : JS_free(cx, tmp);
4180 : }
4181 :
4182 : /* embedded newlines -- argh! */
4183 1854 : while ((ctmp = strchr(message, '\n')) != 0) {
4184 0 : ctmp++;
4185 0 : if (prefix)
4186 0 : fputs(prefix, gErrFile);
4187 0 : fwrite(message, 1, ctmp - message, gErrFile);
4188 0 : message = ctmp;
4189 : }
4190 :
4191 : /* If there were no filename or lineno, the prefix might be empty */
4192 927 : if (prefix)
4193 783 : fputs(prefix, gErrFile);
4194 927 : fputs(message, gErrFile);
4195 :
4196 927 : if (!report->linebuf) {
4197 882 : fputc('\n', gErrFile);
4198 882 : goto out;
4199 : }
4200 :
4201 : /* report->linebuf usually ends with a newline. */
4202 45 : n = strlen(report->linebuf);
4203 : fprintf(gErrFile, ":\n%s%s%s%s",
4204 : prefix,
4205 : report->linebuf,
4206 18 : (n > 0 && report->linebuf[n-1] == '\n') ? "" : "\n",
4207 63 : prefix);
4208 45 : n = report->tokenptr - report->linebuf;
4209 414 : for (i = j = 0; i < n; i++) {
4210 369 : if (report->linebuf[i] == '\t') {
4211 0 : for (k = (j + 8) & ~7; j < k; j++) {
4212 0 : fputc('.', gErrFile);
4213 : }
4214 0 : continue;
4215 : }
4216 369 : fputc('.', gErrFile);
4217 369 : j++;
4218 : }
4219 45 : fputs("^\n", gErrFile);
4220 : out:
4221 927 : fflush(gErrFile);
4222 927 : if (!JSREPORT_IS_WARNING(report->flags)) {
4223 855 : if (report->errorNumber == JSMSG_OUT_OF_MEMORY) {
4224 18 : gExitCode = EXITCODE_OUT_OF_MEMORY;
4225 : } else {
4226 837 : gExitCode = EXITCODE_RUNTIME_ERROR;
4227 : }
4228 : }
4229 927 : JS_free(cx, prefix);
4230 : }
4231 :
4232 : #if defined(SHELL_HACK) && defined(DEBUG) && defined(XP_UNIX)
4233 : static JSBool
4234 : Exec(JSContext *cx, unsigned argc, jsval *vp)
4235 : {
4236 : JSFunction *fun;
4237 : const char *name, **nargv;
4238 : unsigned i, nargc;
4239 : JSString *str;
4240 : bool ok;
4241 : pid_t pid;
4242 : int status;
4243 :
4244 : JS_SET_RVAL(cx, vp, JSVAL_VOID);
4245 :
4246 : fun = JS_ValueToFunction(cx, vp[0]);
4247 : if (!fun)
4248 : return JS_FALSE;
4249 : if (!fun->atom)
4250 : return JS_TRUE;
4251 :
4252 : nargc = 1 + argc;
4253 :
4254 : /* nargc + 1 accounts for the terminating NULL. */
4255 : nargv = new (char *)[nargc + 1];
4256 : if (!nargv)
4257 : return JS_FALSE;
4258 : memset(nargv, 0, sizeof(nargv[0]) * (nargc + 1));
4259 : nargv[0] = name;
4260 : jsval *argv = JS_ARGV(cx, vp);
4261 : for (i = 0; i < nargc; i++) {
4262 : str = (i == 0) ? fun->atom : JS_ValueToString(cx, argv[i-1]);
4263 : if (!str) {
4264 : ok = false;
4265 : goto done;
4266 : }
4267 : nargv[i] = JS_EncodeString(cx, str);
4268 : if (!nargv[i]) {
4269 : ok = false;
4270 : goto done;
4271 : }
4272 : }
4273 : pid = fork();
4274 : switch (pid) {
4275 : case -1:
4276 : perror("js");
4277 : break;
4278 : case 0:
4279 : (void) execvp(name, (char **)nargv);
4280 : perror("js");
4281 : exit(127);
4282 : default:
4283 : while (waitpid(pid, &status, 0) < 0 && errno == EINTR)
4284 : continue;
4285 : break;
4286 : }
4287 : ok = true;
4288 :
4289 : done:
4290 : for (i = 0; i < nargc; i++)
4291 : JS_free(cx, nargv[i]);
4292 : delete[] nargv;
4293 : return ok;
4294 : }
4295 : #endif
4296 :
4297 : static JSBool
4298 144729 : global_enumerate(JSContext *cx, JSObject *obj)
4299 : {
4300 : #ifdef LAZY_STANDARD_CLASSES
4301 144729 : return JS_EnumerateStandardClasses(cx, obj);
4302 : #else
4303 : return JS_TRUE;
4304 : #endif
4305 : }
4306 :
4307 : static JSBool
4308 649062 : global_resolve(JSContext *cx, JSObject *obj, jsid id, unsigned flags,
4309 : JSObject **objp)
4310 : {
4311 : #ifdef LAZY_STANDARD_CLASSES
4312 : JSBool resolved;
4313 :
4314 649062 : if (!JS_ResolveStandardClass(cx, obj, id, &resolved))
4315 0 : return JS_FALSE;
4316 649062 : if (resolved) {
4317 7485 : *objp = obj;
4318 7485 : return JS_TRUE;
4319 : }
4320 : #endif
4321 :
4322 : #if defined(SHELL_HACK) && defined(DEBUG) && defined(XP_UNIX)
4323 : if (!(flags & JSRESOLVE_QUALIFIED)) {
4324 : /*
4325 : * Do this expensive hack only for unoptimized Unix builds, which are
4326 : * not used for benchmarking.
4327 : */
4328 : char *path, *comp, *full;
4329 : const char *name;
4330 : JSBool ok, found;
4331 : JSFunction *fun;
4332 :
4333 : if (!JSVAL_IS_STRING(id))
4334 : return JS_TRUE;
4335 : path = getenv("PATH");
4336 : if (!path)
4337 : return JS_TRUE;
4338 : path = JS_strdup(cx, path);
4339 : if (!path)
4340 : return JS_FALSE;
4341 : JSAutoByteString name(cx, JSVAL_TO_STRING(id));
4342 : if (!name)
4343 : return JS_FALSE;
4344 : ok = JS_TRUE;
4345 : for (comp = strtok(path, ":"); comp; comp = strtok(NULL, ":")) {
4346 : if (*comp != '\0') {
4347 : full = JS_smprintf("%s/%s", comp, name.ptr());
4348 : if (!full) {
4349 : JS_ReportOutOfMemory(cx);
4350 : ok = JS_FALSE;
4351 : break;
4352 : }
4353 : } else {
4354 : full = (char *)name;
4355 : }
4356 : found = (access(full, X_OK) == 0);
4357 : if (*comp != '\0')
4358 : free(full);
4359 : if (found) {
4360 : fun = JS_DefineFunction(cx, obj, name, Exec, 0,
4361 : JSPROP_ENUMERATE);
4362 : ok = (fun != NULL);
4363 : if (ok)
4364 : *objp = obj;
4365 : break;
4366 : }
4367 : }
4368 : JS_free(cx, path);
4369 : return ok;
4370 : }
4371 : #else
4372 641577 : return JS_TRUE;
4373 : #endif
4374 : }
4375 :
4376 : JSClass global_class = {
4377 : "global", JSCLASS_NEW_RESOLVE | JSCLASS_GLOBAL_FLAGS | JSCLASS_HAS_PRIVATE,
4378 : JS_PropertyStub, JS_PropertyStub,
4379 : JS_PropertyStub, JS_StrictPropertyStub,
4380 : global_enumerate, (JSResolveOp) global_resolve,
4381 : JS_ConvertStub, its_finalize
4382 : };
4383 :
4384 : static JSBool
4385 0 : env_setProperty(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp)
4386 : {
4387 : /* XXX porting may be easy, but these don't seem to supply setenv by default */
4388 : #if !defined XP_OS2 && !defined SOLARIS
4389 : int rv;
4390 :
4391 0 : IdStringifier idstr(cx, id, JS_TRUE);
4392 0 : if (idstr.threw())
4393 0 : return JS_FALSE;
4394 0 : ToStringHelper valstr(cx, *vp, JS_TRUE);
4395 0 : if (valstr.threw())
4396 0 : return JS_FALSE;
4397 : #if defined XP_WIN || defined HPUX || defined OSF1
4398 : {
4399 : char *waste = JS_smprintf("%s=%s", idstr.getBytes(), valstr.getBytes());
4400 : if (!waste) {
4401 : JS_ReportOutOfMemory(cx);
4402 : return JS_FALSE;
4403 : }
4404 : rv = putenv(waste);
4405 : #ifdef XP_WIN
4406 : /*
4407 : * HPUX9 at least still has the bad old non-copying putenv.
4408 : *
4409 : * Per mail from <s.shanmuganathan@digital.com>, OSF1 also has a putenv
4410 : * that will crash if you pass it an auto char array (so it must place
4411 : * its argument directly in the char *environ[] array).
4412 : */
4413 : JS_smprintf_free(waste);
4414 : #endif
4415 : }
4416 : #else
4417 0 : rv = setenv(idstr.getBytes(), valstr.getBytes(), 1);
4418 : #endif
4419 0 : if (rv < 0) {
4420 0 : JS_ReportError(cx, "can't set env variable %s to %s", idstr.getBytes(), valstr.getBytes());
4421 0 : return JS_FALSE;
4422 : }
4423 0 : *vp = valstr.getJSVal();
4424 : #endif /* !defined XP_OS2 && !defined SOLARIS */
4425 0 : return JS_TRUE;
4426 : }
4427 :
4428 : static JSBool
4429 0 : env_enumerate(JSContext *cx, JSObject *obj)
4430 : {
4431 : static JSBool reflected;
4432 : char **evp, *name, *value;
4433 : JSString *valstr;
4434 : JSBool ok;
4435 :
4436 0 : if (reflected)
4437 0 : return JS_TRUE;
4438 :
4439 0 : for (evp = (char **)JS_GetPrivate(obj); (name = *evp) != NULL; evp++) {
4440 0 : value = strchr(name, '=');
4441 0 : if (!value)
4442 0 : continue;
4443 0 : *value++ = '\0';
4444 0 : valstr = JS_NewStringCopyZ(cx, value);
4445 0 : if (!valstr) {
4446 0 : ok = JS_FALSE;
4447 : } else {
4448 : ok = JS_DefineProperty(cx, obj, name, STRING_TO_JSVAL(valstr),
4449 0 : NULL, NULL, JSPROP_ENUMERATE);
4450 : }
4451 0 : value[-1] = '=';
4452 0 : if (!ok)
4453 0 : return JS_FALSE;
4454 : }
4455 :
4456 0 : reflected = JS_TRUE;
4457 0 : return JS_TRUE;
4458 : }
4459 :
4460 : static JSBool
4461 0 : env_resolve(JSContext *cx, JSObject *obj, jsid id, unsigned flags,
4462 : JSObject **objp)
4463 : {
4464 : JSString *valstr;
4465 : const char *name, *value;
4466 :
4467 0 : if (flags & JSRESOLVE_ASSIGNING)
4468 0 : return JS_TRUE;
4469 :
4470 0 : IdStringifier idstr(cx, id, JS_TRUE);
4471 0 : if (idstr.threw())
4472 0 : return JS_FALSE;
4473 :
4474 0 : name = idstr.getBytes();
4475 0 : value = getenv(name);
4476 0 : if (value) {
4477 0 : valstr = JS_NewStringCopyZ(cx, value);
4478 0 : if (!valstr)
4479 0 : return JS_FALSE;
4480 0 : if (!JS_DefineProperty(cx, obj, name, STRING_TO_JSVAL(valstr),
4481 0 : NULL, NULL, JSPROP_ENUMERATE)) {
4482 0 : return JS_FALSE;
4483 : }
4484 0 : *objp = obj;
4485 : }
4486 0 : return JS_TRUE;
4487 : }
4488 :
4489 : static JSClass env_class = {
4490 : "environment", JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE,
4491 : JS_PropertyStub, JS_PropertyStub,
4492 : JS_PropertyStub, env_setProperty,
4493 : env_enumerate, (JSResolveOp) env_resolve,
4494 : JS_ConvertStub
4495 : };
4496 :
4497 : /*
4498 : * Avoid a reentrancy hazard.
4499 : *
4500 : * The non-JS_THREADSAFE shell uses a signal handler to implement timeout().
4501 : * The JS engine is not really reentrant, but JS_TriggerAllOperationCallbacks
4502 : * is mostly safe--the only danger is that we might interrupt JS_NewContext or
4503 : * JS_DestroyContext while the context list is being modified. Therefore we
4504 : * disable the signal handler around calls to those functions.
4505 : */
4506 : #ifdef JS_THREADSAFE
4507 : # define WITH_SIGNALS_DISABLED(x) x
4508 : #else
4509 : # define WITH_SIGNALS_DISABLED(x) \
4510 : JS_BEGIN_MACRO \
4511 : ScheduleWatchdog(gRuntime, -1); \
4512 : x; \
4513 : ScheduleWatchdog(gRuntime, gTimeoutInterval); \
4514 : JS_END_MACRO
4515 : #endif
4516 :
4517 : static JSContext *
4518 18666 : NewContext(JSRuntime *rt)
4519 : {
4520 : JSContext *cx;
4521 18666 : WITH_SIGNALS_DISABLED(cx = JS_NewContext(rt, gStackChunkSize));
4522 18666 : if (!cx)
4523 0 : return NULL;
4524 :
4525 18666 : JSShellContextData *data = NewContextData();
4526 18666 : if (!data) {
4527 0 : DestroyContext(cx, false);
4528 0 : return NULL;
4529 : }
4530 :
4531 18666 : JS_SetContextPrivate(cx, data);
4532 18666 : JS_SetErrorReporter(cx, my_ErrorReporter);
4533 18666 : JS_SetVersion(cx, JSVERSION_LATEST);
4534 18666 : SetContextOptions(cx);
4535 18666 : if (enableMethodJit)
4536 0 : JS_ToggleOptions(cx, JSOPTION_METHODJIT);
4537 18666 : if (enableTypeInference)
4538 0 : JS_ToggleOptions(cx, JSOPTION_TYPE_INFERENCE);
4539 18666 : return cx;
4540 : }
4541 :
4542 : static void
4543 18666 : DestroyContext(JSContext *cx, bool withGC)
4544 : {
4545 18666 : JSShellContextData *data = GetContextData(cx);
4546 18666 : JS_SetContextPrivate(cx, NULL);
4547 18666 : free(data);
4548 18666 : WITH_SIGNALS_DISABLED(withGC ? JS_DestroyContext(cx) : JS_DestroyContextNoGC(cx));
4549 18666 : }
4550 :
4551 : static JSObject *
4552 23328 : NewGlobalObject(JSContext *cx, CompartmentKind compartment)
4553 : {
4554 46656 : RootedVarObject glob(cx);
4555 :
4556 : glob = (compartment == NEW_COMPARTMENT)
4557 : ? JS_NewCompartmentAndGlobalObject(cx, &global_class, NULL)
4558 23328 : : JS_NewGlobalObject(cx, &global_class);
4559 23328 : if (!glob)
4560 0 : return NULL;
4561 :
4562 : {
4563 46656 : JSAutoEnterCompartment ac;
4564 23328 : if (!ac.enter(cx, glob))
4565 0 : return NULL;
4566 :
4567 : #ifndef LAZY_STANDARD_CLASSES
4568 : if (!JS_InitStandardClasses(cx, glob))
4569 : return NULL;
4570 : #endif
4571 :
4572 : #ifdef JS_HAS_CTYPES
4573 23328 : if (!JS_InitCTypesClass(cx, glob))
4574 0 : return NULL;
4575 : #endif
4576 23328 : if (!JS_InitReflect(cx, glob))
4577 0 : return NULL;
4578 23328 : if (!JS_DefineDebuggerObject(cx, glob))
4579 0 : return NULL;
4580 23328 : if (!JS::RegisterPerfMeasurement(cx, glob))
4581 0 : return NULL;
4582 46656 : if (!JS_DefineFunctionsWithHelp(cx, glob, shell_functions) ||
4583 23328 : !JS_DefineProfilingFunctions(cx, glob)) {
4584 0 : return NULL;
4585 : }
4586 23328 : if (!js::DefineTestingFunctions(cx, glob))
4587 0 : return NULL;
4588 :
4589 23328 : JSObject *it = JS_DefineObject(cx, glob, "it", &its_class, NULL, 0);
4590 23328 : if (!it)
4591 0 : return NULL;
4592 23328 : if (!JS_DefineProperties(cx, it, its_props))
4593 0 : return NULL;
4594 :
4595 23328 : if (!JS_DefineProperty(cx, glob, "custom", JSVAL_VOID, its_getter,
4596 23328 : its_setter, 0))
4597 0 : return NULL;
4598 23328 : if (!JS_DefineProperty(cx, glob, "customRdOnly", JSVAL_VOID, its_getter,
4599 23328 : its_setter, JSPROP_READONLY))
4600 0 : return NULL;
4601 : }
4602 :
4603 23328 : if (compartment == NEW_COMPARTMENT && !JS_WrapObject(cx, glob.address()))
4604 0 : return NULL;
4605 :
4606 23328 : return glob;
4607 : }
4608 :
4609 : static bool
4610 18666 : BindScriptArgs(JSContext *cx, JSObject *obj, OptionParser *op)
4611 : {
4612 37332 : RootObject root(cx, &obj);
4613 :
4614 18666 : MultiStringRange msr = op->getMultiStringArg("scriptArgs");
4615 37332 : RootedVarObject scriptArgs(cx);
4616 18666 : scriptArgs = JS_NewArrayObject(cx, 0, NULL);
4617 18666 : if (!scriptArgs)
4618 0 : return false;
4619 :
4620 : /*
4621 : * Script arguments are bound as a normal |arguments| property on the
4622 : * global object. It has no special significance, like |arguments| in
4623 : * function scope does -- this identifier is used de-facto across shell
4624 : * implementations, see bug 675269.
4625 : */
4626 18666 : if (!JS_DefineProperty(cx, obj, "arguments", OBJECT_TO_JSVAL(scriptArgs), NULL, NULL, 0))
4627 0 : return false;
4628 :
4629 18666 : for (size_t i = 0; !msr.empty(); msr.popFront(), ++i) {
4630 0 : const char *scriptArg = msr.front();
4631 0 : JSString *str = JS_NewStringCopyZ(cx, scriptArg);
4632 0 : if (!str ||
4633 : !JS_DefineElement(cx, scriptArgs, i, STRING_TO_JSVAL(str), NULL, NULL,
4634 0 : JSPROP_ENUMERATE)) {
4635 0 : return false;
4636 : }
4637 : }
4638 :
4639 18666 : return true;
4640 : }
4641 :
4642 : static int
4643 18666 : ProcessArgs(JSContext *cx, JSObject *obj, OptionParser *op)
4644 : {
4645 37332 : RootObject root(cx, &obj);
4646 :
4647 18666 : if (op->getBoolOption('a'))
4648 8376 : JS_ToggleOptions(cx, JSOPTION_METHODJIT_ALWAYS);
4649 :
4650 18666 : if (op->getBoolOption('c'))
4651 0 : compileOnly = true;
4652 :
4653 18666 : if (op->getBoolOption('m')) {
4654 14520 : enableMethodJit = true;
4655 14520 : JS_ToggleOptions(cx, JSOPTION_METHODJIT);
4656 : }
4657 :
4658 18666 : if (op->getBoolOption('d')) {
4659 6654 : JS_SetRuntimeDebugMode(JS_GetRuntime(cx), true);
4660 6654 : JS_SetDebugMode(cx, true);
4661 : }
4662 :
4663 18666 : if (op->getBoolOption('b'))
4664 0 : printTiming = true;
4665 :
4666 18666 : if (op->getBoolOption('D'))
4667 0 : enableDisassemblyDumps = true;
4668 :
4669 : /* |scriptArgs| gets bound on the global before any code is run. */
4670 18666 : if (!BindScriptArgs(cx, obj, op))
4671 0 : return EXIT_FAILURE;
4672 :
4673 18666 : MultiStringRange filePaths = op->getMultiStringOption('f');
4674 18666 : MultiStringRange codeChunks = op->getMultiStringOption('e');
4675 :
4676 18666 : if (filePaths.empty() && codeChunks.empty() && !op->getStringArg("script")) {
4677 0 : Process(cx, obj, NULL, true); /* Interactive. */
4678 0 : return gExitCode;
4679 : }
4680 :
4681 92475 : while (!filePaths.empty() || !codeChunks.empty()) {
4682 55998 : size_t fpArgno = filePaths.empty() ? -1 : filePaths.argno();
4683 55998 : size_t ccArgno = codeChunks.empty() ? -1 : codeChunks.argno();
4684 55998 : if (fpArgno < ccArgno) {
4685 37332 : char *path = filePaths.front();
4686 37332 : Process(cx, obj, path, false);
4687 37332 : if (gExitCode)
4688 855 : return gExitCode;
4689 36477 : filePaths.popFront();
4690 : } else {
4691 18666 : const char *code = codeChunks.front();
4692 : jsval rval;
4693 18666 : if (!JS_EvaluateScript(cx, obj, code, strlen(code), "-e", 1, &rval))
4694 0 : return EXIT_FAILURE;
4695 18666 : codeChunks.popFront();
4696 : }
4697 : }
4698 :
4699 : /* The |script| argument is processed after all options. */
4700 17811 : if (const char *path = op->getStringArg("script")) {
4701 0 : Process(cx, obj, path, false);
4702 0 : if (gExitCode)
4703 0 : return gExitCode;
4704 : }
4705 :
4706 17811 : if (op->getBoolOption('i'))
4707 0 : Process(cx, obj, NULL, true);
4708 :
4709 17811 : return gExitCode ? gExitCode : EXIT_SUCCESS;
4710 : }
4711 :
4712 : int
4713 18666 : Shell(JSContext *cx, OptionParser *op, char **envp)
4714 : {
4715 37332 : JSAutoRequest ar(cx);
4716 :
4717 : /*
4718 : * First check to see if type inference is enabled. This flag must be set
4719 : * on the compartment when it is constructed.
4720 : */
4721 18666 : if (op->getBoolOption('n')) {
4722 10370 : enableTypeInference = !enableTypeInference;
4723 10370 : JS_ToggleOptions(cx, JSOPTION_TYPE_INFERENCE);
4724 : }
4725 :
4726 37332 : RootedVarObject glob(cx);
4727 18666 : glob = NewGlobalObject(cx, NEW_COMPARTMENT);
4728 18666 : if (!glob)
4729 0 : return 1;
4730 :
4731 37332 : JSAutoEnterCompartment ac;
4732 18666 : if (!ac.enter(cx, glob))
4733 0 : return 1;
4734 :
4735 18666 : JS_SetGlobalObject(cx, glob);
4736 :
4737 18666 : JSObject *envobj = JS_DefineObject(cx, glob, "environment", &env_class, NULL, 0);
4738 18666 : if (!envobj)
4739 0 : return 1;
4740 18666 : JS_SetPrivate(envobj, envp);
4741 :
4742 : #ifdef JSDEBUGGER
4743 : /*
4744 : * XXX A command line option to enable debugging (or not) would be good
4745 : */
4746 : jsdc = JSD_DebuggerOnForUser(rt, NULL, NULL);
4747 : if (!jsdc)
4748 : return 1;
4749 : JSD_JSContextInUse(jsdc, cx);
4750 : #ifdef JSD_LOWLEVEL_SOURCE
4751 : JS_SetSourceHandler(rt, SendSourceToJSDebugger, jsdc);
4752 : #endif /* JSD_LOWLEVEL_SOURCE */
4753 : #ifdef JSDEBUGGER_JAVA_UI
4754 : jsdjc = JSDJ_CreateContext();
4755 : if (! jsdjc)
4756 : return 1;
4757 : JSDJ_SetJSDContext(jsdjc, jsdc);
4758 : java_env = JSDJ_CreateJavaVMAndStartDebugger(jsdjc);
4759 : /*
4760 : * XXX This would be the place to wait for the debugger to start.
4761 : * Waiting would be nice in general, but especially when a js file
4762 : * is passed on the cmd line.
4763 : */
4764 : #endif /* JSDEBUGGER_JAVA_UI */
4765 : #ifdef JSDEBUGGER_C_UI
4766 : jsdbc = JSDB_InitDebugger(rt, jsdc, 0);
4767 : #endif /* JSDEBUGGER_C_UI */
4768 : #endif /* JSDEBUGGER */
4769 :
4770 : #ifdef JS_THREADSAFE
4771 37332 : class ShellWorkerHooks : public js::workers::WorkerHooks {
4772 : public:
4773 0 : JSObject *newGlobalObject(JSContext *cx) {
4774 0 : return NewGlobalObject(cx, NEW_COMPARTMENT);
4775 : }
4776 : };
4777 37332 : ShellWorkerHooks hooks;
4778 37332 : if (!JS_AddNamedObjectRoot(cx, &gWorkers, "Workers") ||
4779 18666 : (gWorkerThreadPool = js::workers::init(cx, &hooks, glob, &gWorkers)) == NULL) {
4780 0 : return 1;
4781 : }
4782 : #endif
4783 :
4784 18666 : int result = ProcessArgs(cx, glob, op);
4785 :
4786 : #ifdef JS_THREADSAFE
4787 18666 : js::workers::finish(cx, gWorkerThreadPool);
4788 18666 : JS_RemoveObjectRoot(cx, &gWorkers);
4789 18666 : if (result == 0)
4790 17811 : result = gExitCode;
4791 : #endif
4792 :
4793 : #ifdef JSDEBUGGER
4794 : if (jsdc) {
4795 : #ifdef JSDEBUGGER_C_UI
4796 : if (jsdbc)
4797 : JSDB_TermDebugger(jsdc);
4798 : #endif /* JSDEBUGGER_C_UI */
4799 : JSD_DebuggerOff(jsdc);
4800 : }
4801 : #endif /* JSDEBUGGER */
4802 :
4803 18666 : if (enableDisassemblyDumps)
4804 0 : JS_DumpCompartmentPCCounts(cx);
4805 :
4806 18666 : return result;
4807 : }
4808 :
4809 : static void
4810 37332 : MaybeOverrideOutFileFromEnv(const char* const envVar,
4811 : FILE* defaultOut,
4812 : FILE** outFile)
4813 : {
4814 37332 : const char* outPath = getenv(envVar);
4815 37332 : if (!outPath || !*outPath || !(*outFile = fopen(outPath, "w"))) {
4816 37332 : *outFile = defaultOut;
4817 : }
4818 37332 : }
4819 :
4820 : /* Set the initial counter to 1 so the principal will never be destroyed. */
4821 : JSPrincipals shellTrustedPrincipals = { 1 };
4822 :
4823 : JSBool
4824 5928361 : CheckObjectAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, jsval *vp)
4825 : {
4826 5928361 : return true;
4827 : }
4828 :
4829 : JSSecurityCallbacks securityCallbacks = {
4830 : CheckObjectAccess,
4831 : NULL,
4832 : NULL,
4833 : NULL
4834 : };
4835 :
4836 : int
4837 18666 : main(int argc, char **argv, char **envp)
4838 : {
4839 : int stackDummy;
4840 : JSRuntime *rt;
4841 : JSContext *cx;
4842 : int result;
4843 : #ifdef JSDEBUGGER
4844 : JSDContext *jsdc;
4845 : #ifdef JSDEBUGGER_JAVA_UI
4846 : JNIEnv *java_env;
4847 : JSDJContext *jsdjc;
4848 : #endif
4849 : #ifdef JSDEBUGGER_C_UI
4850 : JSBool jsdbc;
4851 : #endif /* JSDEBUGGER_C_UI */
4852 : #endif /* JSDEBUGGER */
4853 : #ifdef XP_WIN
4854 : {
4855 : const char *crash_option = getenv("XRE_NO_WINDOWS_CRASH_DIALOG");
4856 : if (crash_option && strncmp(crash_option, "1", 1)) {
4857 : DWORD oldmode = SetErrorMode(SEM_NOGPFAULTERRORBOX);
4858 : SetErrorMode(oldmode | SEM_NOGPFAULTERRORBOX);
4859 : }
4860 : }
4861 : #endif
4862 :
4863 : #ifdef HAVE_SETLOCALE
4864 18666 : setlocale(LC_ALL, "");
4865 : #endif
4866 :
4867 : #ifdef JS_THREADSAFE
4868 37332 : if (PR_FAILURE == PR_NewThreadPrivateIndex(&gStackBaseThreadIndex, NULL) ||
4869 18666 : PR_FAILURE == PR_SetThreadPrivate(gStackBaseThreadIndex, &stackDummy)) {
4870 0 : return 1;
4871 : }
4872 : #else
4873 : gStackBase = (uintptr_t) &stackDummy;
4874 : #endif
4875 :
4876 : #ifdef XP_OS2
4877 : /* these streams are normally line buffered on OS/2 and need a \n, *
4878 : * so we need to unbuffer then to get a reasonable prompt */
4879 : setbuf(stdout,0);
4880 : setbuf(stderr,0);
4881 : #endif
4882 :
4883 18666 : MaybeOverrideOutFileFromEnv("JS_STDERR", stderr, &gErrFile);
4884 18666 : MaybeOverrideOutFileFromEnv("JS_STDOUT", stdout, &gOutFile);
4885 :
4886 37332 : OptionParser op("Usage: {progname} [options] [[script] scriptArgs*]");
4887 :
4888 : op.setDescription("The SpiderMonkey shell provides a command line interface to the "
4889 : "JavaScript engine. Code and file options provided via the command line are "
4890 : "run left to right. If provided, the optional script argument is run after "
4891 : "all options have been processed. Just-In-Time compilation modes may be enabled via "
4892 18666 : "command line options.");
4893 18666 : op.setDescriptionWidth(72);
4894 18666 : op.setHelpWidth(80);
4895 18666 : op.setVersion(JS_GetImplementationVersion());
4896 :
4897 298656 : if (!op.addMultiStringOption('f', "file", "PATH", "File path to run")
4898 18666 : || !op.addMultiStringOption('e', "execute", "CODE", "Inline code to run")
4899 18666 : || !op.addBoolOption('i', "shell", "Enter prompt after running code")
4900 18666 : || !op.addBoolOption('m', "methodjit", "Enable the JaegerMonkey method JIT")
4901 18666 : || !op.addBoolOption('n', "typeinfer", "Enable type inference")
4902 18666 : || !op.addBoolOption('c', "compileonly", "Only compile, don't run (syntax checking mode)")
4903 18666 : || !op.addBoolOption('d', "debugjit", "Enable runtime debug mode for method JIT code")
4904 : || !op.addBoolOption('a', "always-mjit",
4905 18666 : "Do not try to run in the interpreter before method jitting.")
4906 18666 : || !op.addBoolOption('D', "dump-bytecode", "Dump bytecode with exec count for all scripts")
4907 18666 : || !op.addBoolOption('b', "print-timing", "Print sub-ms runtime for each file that's run")
4908 : #ifdef DEBUG
4909 18666 : || !op.addIntOption('A', "oom-after", "COUNT", "Trigger OOM after COUNT allocations", -1)
4910 18666 : || !op.addBoolOption('O', "print-alloc", "Print the number of allocations at exit")
4911 : #endif
4912 18666 : || !op.addBoolOption('U', "utf8", "C strings passed to the JSAPI are UTF-8 encoded")
4913 : #ifdef JS_GC_ZEAL
4914 : || !op.addStringOption('Z', "gc-zeal", "N[,F[,C]]",
4915 : "N indicates \"zealousness\":\n"
4916 : " 0: no additional GCs\n"
4917 : " 1: additional GCs at common danger points\n"
4918 : " 2: GC every F allocations (default: 100)\n"
4919 18666 : "If C is 1, compartmental GCs are performed; otherwise, full")
4920 : #endif
4921 18666 : || !op.addOptionalStringArg("script", "A script to execute (after all options)")
4922 : || !op.addOptionalMultiStringArg("scriptArgs",
4923 : "String arguments to bind as |arguments| in the "
4924 18666 : "shell's global")) {
4925 0 : return EXIT_FAILURE;
4926 : }
4927 :
4928 18666 : op.setArgTerminatesOptions("script", true);
4929 :
4930 18666 : switch (op.parseArgs(argc, argv)) {
4931 : case OptionParser::ParseHelp:
4932 0 : return EXIT_SUCCESS;
4933 : case OptionParser::ParseError:
4934 0 : op.printHelp(argv[0]);
4935 0 : return EXIT_FAILURE;
4936 : case OptionParser::Fail:
4937 0 : return EXIT_FAILURE;
4938 : case OptionParser::Okay:
4939 : break;
4940 : }
4941 :
4942 18666 : if (op.getHelpOption())
4943 0 : return EXIT_SUCCESS;
4944 :
4945 : #ifdef DEBUG
4946 : /*
4947 : * Process OOM options as early as possible so that we can observe as many
4948 : * allocations as possible.
4949 : */
4950 18666 : if (op.getIntOption('A') >= 0)
4951 0 : OOM_maxAllocations = op.getIntOption('A');
4952 18666 : if (op.getBoolOption('O'))
4953 0 : OOM_printAllocationCount = true;
4954 : #endif
4955 :
4956 : /* Must be done before we create the JSRuntime. */
4957 18666 : if (op.getBoolOption('U'))
4958 0 : JS_SetCStringsAreUTF8();
4959 :
4960 : #ifdef XP_WIN
4961 : // Set the timer calibration delay count to 0 so we get high
4962 : // resolution right away, which we need for precise benchmarking.
4963 : extern int CALIBRATION_DELAY_COUNT;
4964 : CALIBRATION_DELAY_COUNT = 0;
4965 : #endif
4966 :
4967 : /* Use the same parameters as the browser in xpcjsruntime.cpp. */
4968 18666 : rt = JS_NewRuntime(32L * 1024L * 1024L);
4969 18666 : if (!rt)
4970 0 : return 1;
4971 :
4972 18666 : JS_SetGCParameter(rt, JSGC_MAX_BYTES, 0xffffffff);
4973 :
4974 18666 : JS_SetTrustedPrincipals(rt, &shellTrustedPrincipals);
4975 18666 : JS_SetSecurityCallbacks(rt, &securityCallbacks);
4976 :
4977 18666 : JS_SetNativeStackQuota(rt, gMaxStackSize);
4978 :
4979 18666 : if (!InitWatchdog(rt))
4980 0 : return 1;
4981 :
4982 18666 : cx = NewContext(rt);
4983 18666 : if (!cx)
4984 0 : return 1;
4985 :
4986 18666 : JS_SetGCParameter(rt, JSGC_MODE, JSGC_MODE_INCREMENTAL);
4987 18666 : JS_SetGCParameterForThread(cx, JSGC_MAX_CODE_CACHE_BYTES, 16 * 1024 * 1024);
4988 :
4989 : /* Must be done before creating the global object */
4990 18666 : if (op.getBoolOption('D'))
4991 0 : JS_ToggleOptions(cx, JSOPTION_PCCOUNT);
4992 :
4993 18666 : result = Shell(cx, &op, envp);
4994 :
4995 : #ifdef DEBUG
4996 18666 : if (OOM_printAllocationCount)
4997 0 : printf("OOM max count: %u\n", OOM_counter);
4998 : #endif
4999 :
5000 18666 : DestroyContext(cx, true);
5001 :
5002 18666 : KillWatchdog();
5003 :
5004 18666 : JS_DestroyRuntime(rt);
5005 18666 : JS_ShutDown();
5006 18666 : return result;
5007 : }
|