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