Parent Directory
|
Revision Log
Add SpiderMonkey from Firefox 3.1b1. The following directories and files were removed: correct/, correct.js liveconnect/ nanojit/ t/ v8/ vprof/ xpconnect/ all JavaScript files (Y.js, call.js, if.js, math-partial-sums.js, md5.js, perfect.js, trace-test.js, trace.js)
1 | siliconforks | 332 | /* -*- 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 "jsstddef.h" | ||
45 | #include <errno.h> | ||
46 | #include <stdio.h> | ||
47 | #include <stdlib.h> | ||
48 | #include <string.h> | ||
49 | #include <locale.h> | ||
50 | #include "jstypes.h" | ||
51 | #include "jsarena.h" | ||
52 | #include "jsutil.h" | ||
53 | #include "jsprf.h" | ||
54 | #include "jsapi.h" | ||
55 | #include "jsarray.h" | ||
56 | #include "jsatom.h" | ||
57 | #include "jscntxt.h" | ||
58 | #include "jsdbgapi.h" | ||
59 | #include "jsemit.h" | ||
60 | #include "jsfun.h" | ||
61 | #include "jsgc.h" | ||
62 | #include "jslock.h" | ||
63 | #include "jsnum.h" | ||
64 | #include "jsobj.h" | ||
65 | #include "jsparse.h" | ||
66 | #include "jsscope.h" | ||
67 | #include "jsscript.h" | ||
68 | |||
69 | #ifdef LIVECONNECT | ||
70 | #include "jsjava.h" | ||
71 | #endif | ||
72 | |||
73 | #ifdef JSDEBUGGER | ||
74 | #include "jsdebug.h" | ||
75 | #ifdef JSDEBUGGER_JAVA_UI | ||
76 | #include "jsdjava.h" | ||
77 | #endif /* JSDEBUGGER_JAVA_UI */ | ||
78 | #ifdef JSDEBUGGER_C_UI | ||
79 | #include "jsdb.h" | ||
80 | #endif /* JSDEBUGGER_C_UI */ | ||
81 | #endif /* JSDEBUGGER */ | ||
82 | |||
83 | #ifdef XP_UNIX | ||
84 | #include <unistd.h> | ||
85 | #include <sys/types.h> | ||
86 | #include <sys/wait.h> | ||
87 | #endif | ||
88 | |||
89 | #if defined(XP_WIN) || defined(XP_OS2) | ||
90 | #include <io.h> /* for isatty() */ | ||
91 | #endif | ||
92 | |||
93 | typedef enum JSShellExitCode { | ||
94 | EXITCODE_RUNTIME_ERROR = 3, | ||
95 | EXITCODE_FILE_NOT_FOUND = 4, | ||
96 | EXITCODE_OUT_OF_MEMORY = 5 | ||
97 | } JSShellExitCode; | ||
98 | |||
99 | size_t gStackChunkSize = 8192; | ||
100 | |||
101 | /* Assume that we can not use more than 5e5 bytes of C stack by default. */ | ||
102 | static size_t gMaxStackSize = 500000; | ||
103 | static jsuword gStackBase; | ||
104 | |||
105 | static size_t gScriptStackQuota = JS_DEFAULT_SCRIPT_STACK_QUOTA; | ||
106 | |||
107 | static JSBool gEnableBranchCallback = JS_FALSE; | ||
108 | static uint32 gBranchCount; | ||
109 | static uint32 gBranchLimit; | ||
110 | |||
111 | int gExitCode = 0; | ||
112 | JSBool gQuitting = JS_FALSE; | ||
113 | FILE *gErrFile = NULL; | ||
114 | FILE *gOutFile = NULL; | ||
115 | |||
116 | static JSBool reportWarnings = JS_TRUE; | ||
117 | static JSBool compileOnly = JS_FALSE; | ||
118 | |||
119 | typedef enum JSShellErrNum { | ||
120 | #define MSG_DEF(name, number, count, exception, format) \ | ||
121 | name = number, | ||
122 | #include "jsshell.msg" | ||
123 | #undef MSG_DEF | ||
124 | JSShellErr_Limit | ||
125 | #undef MSGDEF | ||
126 | } JSShellErrNum; | ||
127 | |||
128 | static const JSErrorFormatString * | ||
129 | my_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber); | ||
130 | static JSObject * | ||
131 | split_setup(JSContext *cx); | ||
132 | |||
133 | #ifdef EDITLINE | ||
134 | JS_BEGIN_EXTERN_C | ||
135 | extern char *readline(const char *prompt); | ||
136 | extern void add_history(char *line); | ||
137 | JS_END_EXTERN_C | ||
138 | #endif | ||
139 | |||
140 | static JSBool | ||
141 | GetLine(JSContext *cx, char *bufp, FILE *file, const char *prompt) { | ||
142 | #ifdef EDITLINE | ||
143 | /* | ||
144 | * Use readline only if file is stdin, because there's no way to specify | ||
145 | * another handle. Are other filehandles interactive? | ||
146 | */ | ||
147 | if (file == stdin) { | ||
148 | char *linep = readline(prompt); | ||
149 | if (!linep) | ||
150 | return JS_FALSE; | ||
151 | if (linep[0] != '\0') | ||
152 | add_history(linep); | ||
153 | strcpy(bufp, linep); | ||
154 | JS_free(cx, linep); | ||
155 | bufp += strlen(bufp); | ||
156 | *bufp++ = '\n'; | ||
157 | *bufp = '\0'; | ||
158 | } else | ||
159 | #endif | ||
160 | { | ||
161 | char line[256]; | ||
162 | fprintf(gOutFile, prompt); | ||
163 | fflush(gOutFile); | ||
164 | if (!fgets(line, sizeof line, file)) | ||
165 | return JS_FALSE; | ||
166 | strcpy(bufp, line); | ||
167 | } | ||
168 | return JS_TRUE; | ||
169 | } | ||
170 | |||
171 | static JSBool | ||
172 | my_BranchCallback(JSContext *cx, JSScript *script) | ||
173 | { | ||
174 | if (++gBranchCount == gBranchLimit) { | ||
175 | if (script) { | ||
176 | if (script->filename) | ||
177 | fprintf(gErrFile, "%s:", script->filename); | ||
178 | fprintf(gErrFile, "%u: script branch callback (%u callbacks)\n", | ||
179 | script->lineno, gBranchLimit); | ||
180 | } else { | ||
181 | fprintf(gErrFile, "native branch callback (%u callbacks)\n", | ||
182 | gBranchLimit); | ||
183 | } | ||
184 | gBranchCount = 0; | ||
185 | return JS_FALSE; | ||
186 | } | ||
187 | #ifdef JS_THREADSAFE | ||
188 | if ((gBranchCount & 0xff) == 1) { | ||
189 | #endif | ||
190 | if ((gBranchCount & 0x3fff) == 1) | ||
191 | JS_MaybeGC(cx); | ||
192 | #ifdef JS_THREADSAFE | ||
193 | else | ||
194 | JS_YieldRequest(cx); | ||
195 | } | ||
196 | #endif | ||
197 | return JS_TRUE; | ||
198 | } | ||
199 | |||
200 | static void | ||
201 | SetContextOptions(JSContext *cx) | ||
202 | { | ||
203 | jsuword stackLimit; | ||
204 | |||
205 | if (gMaxStackSize == 0) { | ||
206 | /* | ||
207 | * Disable checking for stack overflow if limit is zero. | ||
208 | */ | ||
209 | stackLimit = 0; | ||
210 | } else { | ||
211 | #if JS_STACK_GROWTH_DIRECTION > 0 | ||
212 | stackLimit = gStackBase + gMaxStackSize; | ||
213 | #else | ||
214 | stackLimit = gStackBase - gMaxStackSize; | ||
215 | #endif | ||
216 | } | ||
217 | JS_SetThreadStackLimit(cx, stackLimit); | ||
218 | JS_SetScriptStackQuota(cx, gScriptStackQuota); | ||
219 | if (gEnableBranchCallback) { | ||
220 | JS_SetBranchCallback(cx, my_BranchCallback); | ||
221 | JS_ToggleOptions(cx, JSOPTION_NATIVE_BRANCH_CALLBACK); | ||
222 | } | ||
223 | } | ||
224 | |||
225 | static void | ||
226 | Process(JSContext *cx, JSObject *obj, char *filename, JSBool forceTTY) | ||
227 | { | ||
228 | JSBool ok, hitEOF; | ||
229 | JSScript *script; | ||
230 | jsval result; | ||
231 | JSString *str; | ||
232 | char buffer[4096]; | ||
233 | char *bufp; | ||
234 | int lineno; | ||
235 | int startline; | ||
236 | FILE *file; | ||
237 | uint32 oldopts; | ||
238 | |||
239 | if (forceTTY || !filename || strcmp(filename, "-") == 0) { | ||
240 | file = stdin; | ||
241 | } else { | ||
242 | file = fopen(filename, "r"); | ||
243 | if (!file) { | ||
244 | JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, | ||
245 | JSSMSG_CANT_OPEN, filename, strerror(errno)); | ||
246 | gExitCode = EXITCODE_FILE_NOT_FOUND; | ||
247 | return; | ||
248 | } | ||
249 | } | ||
250 | |||
251 | SetContextOptions(cx); | ||
252 | |||
253 | if (!forceTTY && !isatty(fileno(file))) { | ||
254 | /* | ||
255 | * It's not interactive - just execute it. | ||
256 | * | ||
257 | * Support the UNIX #! shell hack; gobble the first line if it starts | ||
258 | * with '#'. TODO - this isn't quite compatible with sharp variables, | ||
259 | * as a legal js program (using sharp variables) might start with '#'. | ||
260 | * But that would require multi-character lookahead. | ||
261 | */ | ||
262 | int ch = fgetc(file); | ||
263 | if (ch == '#') { | ||
264 | while((ch = fgetc(file)) != EOF) { | ||
265 | if (ch == '\n' || ch == '\r') | ||
266 | break; | ||
267 | } | ||
268 | } | ||
269 | ungetc(ch, file); | ||
270 | |||
271 | oldopts = JS_GetOptions(cx); | ||
272 | JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO | JSOPTION_NO_SCRIPT_RVAL); | ||
273 | script = JS_CompileFileHandle(cx, obj, filename, file); | ||
274 | JS_SetOptions(cx, oldopts); | ||
275 | if (script) { | ||
276 | if (!compileOnly) | ||
277 | (void)JS_ExecuteScript(cx, obj, script, NULL); | ||
278 | JS_DestroyScript(cx, script); | ||
279 | } | ||
280 | |||
281 | if (file != stdin) | ||
282 | fclose(file); | ||
283 | return; | ||
284 | } | ||
285 | |||
286 | /* It's an interactive filehandle; drop into read-eval-print loop. */ | ||
287 | lineno = 1; | ||
288 | hitEOF = JS_FALSE; | ||
289 | do { | ||
290 | bufp = buffer; | ||
291 | *bufp = '\0'; | ||
292 | |||
293 | /* | ||
294 | * Accumulate lines until we get a 'compilable unit' - one that either | ||
295 | * generates an error (before running out of source) or that compiles | ||
296 | * cleanly. This should be whenever we get a complete statement that | ||
297 | * coincides with the end of a line. | ||
298 | */ | ||
299 | startline = lineno; | ||
300 | do { | ||
301 | if (!GetLine(cx, bufp, file, startline == lineno ? "js> " : "")) { | ||
302 | hitEOF = JS_TRUE; | ||
303 | break; | ||
304 | } | ||
305 | bufp += strlen(bufp); | ||
306 | lineno++; | ||
307 | } while (!JS_BufferIsCompilableUnit(cx, obj, buffer, strlen(buffer))); | ||
308 | |||
309 | /* Clear any pending exception from previous failed compiles. */ | ||
310 | JS_ClearPendingException(cx); | ||
311 | script = JS_CompileScript(cx, obj, buffer, strlen(buffer), "typein", | ||
312 | startline); | ||
313 | if (script) { | ||
314 | if (!compileOnly) { | ||
315 | ok = JS_ExecuteScript(cx, obj, script, &result); | ||
316 | if (ok && !JSVAL_IS_VOID(result)) { | ||
317 | str = JS_ValueToString(cx, result); | ||
318 | if (str) | ||
319 | fprintf(gOutFile, "%s\n", JS_GetStringBytes(str)); | ||
320 | else | ||
321 | ok = JS_FALSE; | ||
322 | } | ||
323 | } | ||
324 | JS_DestroyScript(cx, script); | ||
325 | } | ||
326 | } while (!hitEOF && !gQuitting); | ||
327 | fprintf(gOutFile, "\n"); | ||
328 | if (file != stdin) | ||
329 | fclose(file); | ||
330 | return; | ||
331 | } | ||
332 | |||
333 | static int | ||
334 | usage(void) | ||
335 | { | ||
336 | fprintf(gErrFile, "%s\n", JS_GetImplementationVersion()); | ||
337 | fprintf(gErrFile, "usage: js [-zKPswWxCi] [-b branchlimit] [-c stackchunksize] [-o option] [-v version] [-f scriptfile] [-e script] [-S maxstacksize] " | ||
338 | #ifdef JS_GC_ZEAL | ||
339 | "[-Z gczeal] " | ||
340 | #endif | ||
341 | "[scriptfile] [scriptarg...]\n"); | ||
342 | return 2; | ||
343 | } | ||
344 | |||
345 | static struct { | ||
346 | const char *name; | ||
347 | uint32 flag; | ||
348 | } js_options[] = { | ||
349 | {"strict", JSOPTION_STRICT}, | ||
350 | {"werror", JSOPTION_WERROR}, | ||
351 | {"atline", JSOPTION_ATLINE}, | ||
352 | {"xml", JSOPTION_XML}, | ||
353 | {"relimit", JSOPTION_RELIMIT}, | ||
354 | {"anonfunfix", JSOPTION_ANONFUNFIX}, | ||
355 | {"jit", JSOPTION_JIT}, | ||
356 | {NULL, 0} | ||
357 | }; | ||
358 | |||
359 | extern JSClass global_class; | ||
360 | |||
361 | static int | ||
362 | ProcessArgs(JSContext *cx, JSObject *obj, char **argv, int argc) | ||
363 | { | ||
364 | int i, j, length; | ||
365 | JSObject *argsObj; | ||
366 | char *filename = NULL; | ||
367 | JSBool isInteractive = JS_TRUE; | ||
368 | JSBool forceTTY = JS_FALSE; | ||
369 | |||
370 | /* | ||
371 | * Scan past all optional arguments so we can create the arguments object | ||
372 | * before processing any -f options, which must interleave properly with | ||
373 | * -v and -w options. This requires two passes, and without getopt, we'll | ||
374 | * have to keep the option logic here and in the second for loop in sync. | ||
375 | */ | ||
376 | for (i = 0; i < argc; i++) { | ||
377 | if (argv[i][0] != '-' || argv[i][1] == '\0') { | ||
378 | ++i; | ||
379 | break; | ||
380 | } | ||
381 | switch (argv[i][1]) { | ||
382 | case 'b': | ||
383 | case 'c': | ||
384 | case 'f': | ||
385 | case 'e': | ||
386 | case 'v': | ||
387 | case 'S': | ||
388 | #ifdef JS_GC_ZEAL | ||
389 | case 'Z': | ||
390 | #endif | ||
391 | ++i; | ||
392 | break; | ||
393 | default:; | ||
394 | } | ||
395 | } | ||
396 | |||
397 | /* | ||
398 | * Create arguments early and define it to root it, so it's safe from any | ||
399 | * GC calls nested below, and so it is available to -f <file> arguments. | ||
400 | */ | ||
401 | argsObj = JS_NewArrayObject(cx, 0, NULL); | ||
402 | if (!argsObj) | ||
403 | return 1; | ||
404 | if (!JS_DefineProperty(cx, obj, "arguments", OBJECT_TO_JSVAL(argsObj), | ||
405 | NULL, NULL, 0)) { | ||
406 | return 1; | ||
407 | } | ||
408 | |||
409 | length = argc - i; | ||
410 | for (j = 0; j < length; j++) { | ||
411 | JSString *str = JS_NewStringCopyZ(cx, argv[i++]); | ||
412 | if (!str) | ||
413 | return 1; | ||
414 | if (!JS_DefineElement(cx, argsObj, j, STRING_TO_JSVAL(str), | ||
415 | NULL, NULL, JSPROP_ENUMERATE)) { | ||
416 | return 1; | ||
417 | } | ||
418 | } | ||
419 | |||
420 | for (i = 0; i < argc; i++) { | ||
421 | if (argv[i][0] != '-' || argv[i][1] == '\0') { | ||
422 | filename = argv[i++]; | ||
423 | isInteractive = JS_FALSE; | ||
424 | break; | ||
425 | } | ||
426 | |||
427 | switch (argv[i][1]) { | ||
428 | case 'v': | ||
429 | if (++i == argc) | ||
430 | return usage(); | ||
431 | |||
432 | JS_SetVersion(cx, (JSVersion) atoi(argv[i])); | ||
433 | break; | ||
434 | |||
435 | #ifdef JS_GC_ZEAL | ||
436 | case 'Z': | ||
437 | if (++i == argc) | ||
438 | return usage(); | ||
439 | JS_SetGCZeal(cx, atoi(argv[i])); | ||
440 | break; | ||
441 | #endif | ||
442 | |||
443 | case 'w': | ||
444 | reportWarnings = JS_TRUE; | ||
445 | break; | ||
446 | |||
447 | case 'W': | ||
448 | reportWarnings = JS_FALSE; | ||
449 | break; | ||
450 | |||
451 | case 's': | ||
452 | JS_ToggleOptions(cx, JSOPTION_STRICT); | ||
453 | break; | ||
454 | |||
455 | case 'E': | ||
456 | JS_ToggleOptions(cx, JSOPTION_RELIMIT); | ||
457 | break; | ||
458 | |||
459 | case 'x': | ||
460 | JS_ToggleOptions(cx, JSOPTION_XML); | ||
461 | break; | ||
462 | |||
463 | case 'j': | ||
464 | JS_ToggleOptions(cx, JSOPTION_JIT); | ||
465 | #if defined(JS_TRACER) && defined(DEBUG) | ||
466 | extern struct JSClass jitstats_class; | ||
467 | extern void js_InitJITStatsClass(JSContext *cx, JSObject *glob); | ||
468 | js_InitJITStatsClass(cx, JS_GetGlobalObject(cx)); | ||
469 | JS_DefineObject(cx, JS_GetGlobalObject(cx), "tracemonkey", | ||
470 | &jitstats_class, NULL, 0); | ||
471 | #endif | ||
472 | break; | ||
473 | |||
474 | case 'o': | ||
475 | if (++i == argc) | ||
476 | return usage(); | ||
477 | |||
478 | for (j = 0; js_options[j].name; ++j) { | ||
479 | if (strcmp(js_options[j].name, argv[i]) == 0) { | ||
480 | JS_ToggleOptions(cx, js_options[j].flag); | ||
481 | break; | ||
482 | } | ||
483 | } | ||
484 | break; | ||
485 | |||
486 | case 'P': | ||
487 | if (JS_GET_CLASS(cx, JS_GetPrototype(cx, obj)) != &global_class) { | ||
488 | JSObject *gobj; | ||
489 | |||
490 | if (!JS_SealObject(cx, obj, JS_TRUE)) | ||
491 | return JS_FALSE; | ||
492 | gobj = JS_NewObject(cx, &global_class, NULL, NULL); | ||
493 | if (!gobj) | ||
494 | return JS_FALSE; | ||
495 | if (!JS_SetPrototype(cx, gobj, obj)) | ||
496 | return JS_FALSE; | ||
497 | JS_SetParent(cx, gobj, NULL); | ||
498 | JS_SetGlobalObject(cx, gobj); | ||
499 | obj = gobj; | ||
500 | } | ||
501 | break; | ||
502 | |||
503 | case 'b': | ||
504 | gBranchLimit = atoi(argv[++i]); | ||
505 | gEnableBranchCallback = (gBranchLimit != 0); | ||
506 | break; | ||
507 | |||
508 | case 'c': | ||
509 | /* set stack chunk size */ | ||
510 | gStackChunkSize = atoi(argv[++i]); | ||
511 | break; | ||
512 | |||
513 | case 'f': | ||
514 | if (++i == argc) | ||
515 | return usage(); | ||
516 | |||
517 | Process(cx, obj, argv[i], JS_FALSE); | ||
518 | |||
519 | /* | ||
520 | * XXX: js -f foo.js should interpret foo.js and then | ||
521 | * drop into interactive mode, but that breaks the test | ||
522 | * harness. Just execute foo.js for now. | ||
523 | */ | ||
524 | isInteractive = JS_FALSE; | ||
525 | break; | ||
526 | |||
527 | case 'e': | ||
528 | { | ||
529 | jsval rval; | ||
530 | |||
531 | if (++i == argc) | ||
532 | return usage(); | ||
533 | |||
534 | /* Pass a filename of -e to imitate PERL */ | ||
535 | JS_EvaluateScript(cx, obj, argv[i], strlen(argv[i]), | ||
536 | "-e", 1, &rval); | ||
537 | |||
538 | isInteractive = JS_FALSE; | ||
539 | break; | ||
540 | |||
541 | } | ||
542 | case 'C': | ||
543 | compileOnly = JS_TRUE; | ||
544 | isInteractive = JS_FALSE; | ||
545 | break; | ||
546 | |||
547 | case 'i': | ||
548 | isInteractive = forceTTY = JS_TRUE; | ||
549 | break; | ||
550 | |||
551 | case 'S': | ||
552 | if (++i == argc) | ||
553 | return usage(); | ||
554 | |||
555 | /* Set maximum stack size. */ | ||
556 | gMaxStackSize = atoi(argv[i]); | ||
557 | break; | ||
558 | |||
559 | case 'z': | ||
560 | obj = split_setup(cx); | ||
561 | if (!obj) | ||
562 | return gExitCode; | ||
563 | break; | ||
564 | #ifdef MOZ_SHARK | ||
565 | case 'k': | ||
566 | JS_ConnectShark(); | ||
567 | break; | ||
568 | #endif | ||
569 | default: | ||
570 | return usage(); | ||
571 | } | ||
572 | } | ||
573 | |||
574 | if (filename || isInteractive) | ||
575 | Process(cx, obj, filename, forceTTY); | ||
576 | return gExitCode; | ||
577 | } | ||
578 | |||
579 | static JSBool | ||
580 | Version(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) | ||
581 | { | ||
582 | if (argc > 0 && JSVAL_IS_INT(argv[0])) | ||
583 | *rval = INT_TO_JSVAL(JS_SetVersion(cx, (JSVersion) JSVAL_TO_INT(argv[0]))); | ||
584 | else | ||
585 | *rval = INT_TO_JSVAL(JS_GetVersion(cx)); | ||
586 | return JS_TRUE; | ||
587 | } | ||
588 | |||
589 | static JSBool | ||
590 | Options(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) | ||
591 | { | ||
592 | uint32 optset, flag; | ||
593 | uintN i, j, found; | ||
594 | JSString *str; | ||
595 | const char *opt; | ||
596 | char *names; | ||
597 | |||
598 | optset = 0; | ||
599 | for (i = 0; i < argc; i++) { | ||
600 | str = JS_ValueToString(cx, argv[i]); | ||
601 | if (!str) | ||
602 | return JS_FALSE; | ||
603 | opt = JS_GetStringBytes(str); | ||
604 | for (j = 0; js_options[j].name; j++) { | ||
605 | if (strcmp(js_options[j].name, opt) == 0) { | ||
606 | optset |= js_options[j].flag; | ||
607 | break; | ||
608 | } | ||
609 | } | ||
610 | } | ||
611 | optset = JS_ToggleOptions(cx, optset); | ||
612 | |||
613 | names = NULL; | ||
614 | found = 0; | ||
615 | while (optset != 0) { | ||
616 | flag = optset; | ||
617 | optset &= optset - 1; | ||
618 | flag &= ~optset; | ||
619 | for (j = 0; js_options[j].name; j++) { | ||
620 | if (js_options[j].flag == flag) { | ||
621 | names = JS_sprintf_append(names, "%s%s", | ||
622 | names ? "," : "", js_options[j].name); | ||
623 | found++; | ||
624 | break; | ||
625 | } | ||
626 | } | ||
627 | } | ||
628 | if (!found) | ||
629 | names = strdup(""); | ||
630 | if (!names) { | ||
631 | JS_ReportOutOfMemory(cx); | ||
632 | return JS_FALSE; | ||
633 | } | ||
634 | |||
635 | str = JS_NewString(cx, names, strlen(names)); | ||
636 | if (!str) { | ||
637 | free(names); | ||
638 | return JS_FALSE; | ||
639 | } | ||
640 | *rval = STRING_TO_JSVAL(str); | ||
641 | return JS_TRUE; | ||
642 | } | ||
643 | |||
644 | static JSBool | ||
645 | Load(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) | ||
646 | { | ||
647 | uintN i; | ||
648 | JSString *str; | ||
649 | const char *filename; | ||
650 | JSScript *script; | ||
651 | JSBool ok; | ||
652 | uint32 oldopts; | ||
653 | |||
654 | for (i = 0; i < argc; i++) { | ||
655 | str = JS_ValueToString(cx, argv[i]); | ||
656 | if (!str) | ||
657 | return JS_FALSE; | ||
658 | argv[i] = STRING_TO_JSVAL(str); | ||
659 | filename = JS_GetStringBytes(str); | ||
660 | errno = 0; | ||
661 | oldopts = JS_GetOptions(cx); | ||
662 | JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO | JSOPTION_NO_SCRIPT_RVAL); | ||
663 | script = JS_CompileFile(cx, obj, filename); | ||
664 | JS_SetOptions(cx, oldopts); | ||
665 | if (!script) { | ||
666 | ok = JS_FALSE; | ||
667 | } else { | ||
668 | ok = !compileOnly | ||
669 | ? JS_ExecuteScript(cx, obj, script, NULL) | ||
670 | : JS_TRUE; | ||
671 | JS_DestroyScript(cx, script); | ||
672 | } | ||
673 | if (!ok) | ||
674 | return JS_FALSE; | ||
675 | } | ||
676 | |||
677 | return JS_TRUE; | ||
678 | } | ||
679 | |||
680 | /* | ||
681 | * function readline() | ||
682 | * Provides a hook for scripts to read a line from stdin. | ||
683 | */ | ||
684 | static JSBool | ||
685 | ReadLine(JSContext *cx, uintN argc, jsval *vp) | ||
686 | { | ||
687 | #define BUFSIZE 256 | ||
688 | FILE *from; | ||
689 | char *buf, *tmp; | ||
690 | size_t bufsize, buflength, gotlength; | ||
691 | JSBool sawNewline; | ||
692 | JSString *str; | ||
693 | |||
694 | from = stdin; | ||
695 | buflength = 0; | ||
696 | bufsize = BUFSIZE; | ||
697 | buf = (char *) JS_malloc(cx, bufsize); | ||
698 | if (!buf) | ||
699 | return JS_FALSE; | ||
700 | |||
701 | sawNewline = JS_FALSE; | ||
702 | while ((gotlength = | ||
703 | js_fgets(buf + buflength, bufsize - buflength, from)) > 0) { | ||
704 | buflength += gotlength; | ||
705 | |||
706 | /* Are we done? */ | ||
707 | if (buf[buflength - 1] == '\n') { | ||
708 | buf[buflength - 1] = '\0'; | ||
709 | sawNewline = JS_TRUE; | ||
710 | break; | ||
711 | } else if (buflength < bufsize - 1) { | ||
712 | break; | ||
713 | } | ||
714 | |||
715 | /* Else, grow our buffer for another pass. */ | ||
716 | bufsize *= 2; | ||
717 | if (bufsize > buflength) { | ||
718 | tmp = (char *) JS_realloc(cx, buf, bufsize); | ||
719 | } else { | ||
720 | JS_ReportOutOfMemory(cx); | ||
721 | tmp = NULL; | ||
722 | } | ||
723 | |||
724 | if (!tmp) { | ||
725 | JS_free(cx, buf); | ||
726 | return JS_FALSE; | ||
727 | } | ||
728 | |||
729 | buf = tmp; | ||
730 | } | ||
731 | |||
732 | /* Treat the empty string specially. */ | ||
733 | if (buflength == 0) { | ||
734 | *vp = feof(from) ? JSVAL_NULL : JS_GetEmptyStringValue(cx); | ||
735 | JS_free(cx, buf); | ||
736 | return JS_TRUE; | ||
737 | } | ||
738 | |||
739 | /* Shrink the buffer to the real size. */ | ||
740 | tmp = (char *) JS_realloc(cx, buf, buflength); | ||
741 | if (!tmp) { | ||
742 | JS_free(cx, buf); | ||
743 | return JS_FALSE; | ||
744 | } | ||
745 | |||
746 | buf = tmp; | ||
747 | |||
748 | /* | ||
749 | * Turn buf into a JSString. Note that buflength includes the trailing null | ||
750 | * character. | ||
751 | */ | ||
752 | str = JS_NewString(cx, buf, sawNewline ? buflength - 1 : buflength); | ||
753 | if (!str) { | ||
754 | JS_free(cx, buf); | ||
755 | return JS_FALSE; | ||
756 | } | ||
757 | |||
758 | *vp = STRING_TO_JSVAL(str); | ||
759 | return JS_TRUE; | ||
760 | } | ||
761 | |||
762 | static JSBool | ||
763 | Print(JSContext *cx, uintN argc, jsval *vp) | ||
764 | { | ||
765 | jsval *argv; | ||
766 | uintN i; | ||
767 | JSString *str; | ||
768 | char *bytes; | ||
769 | |||
770 | argv = JS_ARGV(cx, vp); | ||
771 | for (i = 0; i < argc; i++) { | ||
772 | str = JS_ValueToString(cx, argv[i]); | ||
773 | if (!str) | ||
774 | return JS_FALSE; | ||
775 | bytes = JS_EncodeString(cx, str); | ||
776 | if (!bytes) | ||
777 | return JS_FALSE; | ||
778 | fprintf(gOutFile, "%s%s", i ? " " : "", bytes); | ||
779 | JS_free(cx, bytes); | ||
780 | } | ||
781 | |||
782 | fputc('\n', gOutFile); | ||
783 | fflush(gOutFile); | ||
784 | |||
785 | JS_SET_RVAL(cx, vp, JSVAL_VOID); | ||
786 | return JS_TRUE; | ||
787 | } | ||
788 | |||
789 | static JSBool | ||
790 | Help(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); | ||
791 | |||
792 | static JSBool | ||
793 | Quit(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) | ||
794 | { | ||
795 | #ifdef LIVECONNECT | ||
796 | JSJ_SimpleShutdown(); | ||
797 | #endif | ||
798 | |||
799 | JS_ConvertArguments(cx, argc, argv,"/ i", &gExitCode); | ||
800 | |||
801 | gQuitting = JS_TRUE; | ||
802 | return JS_FALSE; | ||
803 | } | ||
804 | |||
805 | static JSBool | ||
806 | GC(JSContext *cx, uintN argc, jsval *vp) | ||
807 | { | ||
808 | JSRuntime *rt; | ||
809 | uint32 preBytes; | ||
810 | |||
811 | rt = cx->runtime; | ||
812 | preBytes = rt->gcBytes; | ||
813 | JS_GC(cx); | ||
814 | |||
815 | fprintf(gOutFile, "before %lu, after %lu, break %08lx\n", | ||
816 | (unsigned long)preBytes, (unsigned long)rt->gcBytes, | ||
817 | #ifdef XP_UNIX | ||
818 | (unsigned long)sbrk(0) | ||
819 | #else | ||
820 | 0 | ||
821 | #endif | ||
822 | ); | ||
823 | #ifdef JS_GCMETER | ||
824 | js_DumpGCStats(rt, stdout); | ||
825 | #endif | ||
826 | *vp = JSVAL_VOID; | ||
827 | return JS_TRUE; | ||
828 | } | ||
829 | |||
830 | static JSBool | ||
831 | GCParameter(JSContext *cx, uintN argc, jsval *vp) | ||
832 | { | ||
833 | JSString *str; | ||
834 | const char *paramName; | ||
835 | JSGCParamKey param; | ||
836 | uint32 value; | ||
837 | |||
838 | if (argc == 0) { | ||
839 | str = JS_ValueToString(cx, JSVAL_VOID); | ||
840 | JS_ASSERT(str); | ||
841 | } else { | ||
842 | str = JS_ValueToString(cx, vp[2]); | ||
843 | if (!str) | ||
844 | return JS_FALSE; | ||
845 | vp[2] = STRING_TO_JSVAL(str); | ||
846 | } | ||
847 | paramName = JS_GetStringBytes(str); | ||
848 | if (!paramName) | ||
849 | return JS_FALSE; | ||
850 | if (strcmp(paramName, "maxBytes") == 0) { | ||
851 | param = JSGC_MAX_BYTES; | ||
852 | } else if (strcmp(paramName, "maxMallocBytes") == 0) { | ||
853 | param = JSGC_MAX_MALLOC_BYTES; | ||
854 | } else { | ||
855 | JS_ReportError(cx, | ||
856 | "the first argument argument must be either maxBytes " | ||
857 | "or maxMallocBytes"); | ||
858 | return JS_FALSE; | ||
859 | } | ||
860 | |||
861 | if (!JS_ValueToECMAUint32(cx, argc < 2 ? JSVAL_VOID : vp[3], &value)) | ||
862 | return JS_FALSE; | ||
863 | if (value == 0) { | ||
864 | JS_ReportError(cx, | ||
865 | "the second argument must be convertable to uint32 with " | ||
866 | "non-zero value"); | ||
867 | return JS_FALSE; | ||
868 | } | ||
869 | JS_SetGCParameter(cx->runtime, param, value); | ||
870 | *vp = JSVAL_VOID; | ||
871 | return JS_TRUE; | ||
872 | } | ||
873 | |||
874 | #ifdef JS_GC_ZEAL | ||
875 | static JSBool | ||
876 | GCZeal(JSContext *cx, uintN argc, jsval *vp) | ||
877 | { | ||
878 | uint32 zeal; | ||
879 | |||
880 | if (!JS_ValueToECMAUint32(cx, argc == 0 ? JSVAL_VOID : vp[2], &zeal)) | ||
881 | return JS_FALSE; | ||
882 | JS_SetGCZeal(cx, (uint8)zeal); | ||
883 | *vp = JSVAL_VOID; | ||
884 | return JS_TRUE; | ||
885 | } | ||
886 | #endif /* JS_GC_ZEAL */ | ||
887 | |||
888 | typedef struct JSCountHeapNode JSCountHeapNode; | ||
889 | |||
890 | struct JSCountHeapNode { | ||
891 | void *thing; | ||
892 | int32 kind; | ||
893 | JSCountHeapNode *next; | ||
894 | }; | ||
895 | |||
896 | typedef struct JSCountHeapTracer { | ||
897 | JSTracer base; | ||
898 | JSDHashTable visited; | ||
899 | JSBool ok; | ||
900 | JSCountHeapNode *traceList; | ||
901 | JSCountHeapNode *recycleList; | ||
902 | } JSCountHeapTracer; | ||
903 | |||
904 | static void | ||
905 | CountHeapNotify(JSTracer *trc, void *thing, uint32 kind) | ||
906 | { | ||
907 | JSCountHeapTracer *countTracer; | ||
908 | JSDHashEntryStub *entry; | ||
909 | JSCountHeapNode *node; | ||
910 | |||
911 | JS_ASSERT(trc->callback == CountHeapNotify); | ||
912 | countTracer = (JSCountHeapTracer *)trc; | ||
913 | if (!countTracer->ok) | ||
914 | return; | ||
915 | |||
916 | entry = (JSDHashEntryStub *) | ||
917 | JS_DHashTableOperate(&countTracer->visited, thing, JS_DHASH_ADD); | ||
918 | if (!entry) { | ||
919 | JS_ReportOutOfMemory(trc->context); | ||
920 | countTracer->ok = JS_FALSE; | ||
921 | return; | ||
922 | } | ||
923 | if (entry->key) | ||
924 | return; | ||
925 | entry->key = thing; | ||
926 | |||
927 | node = countTracer->recycleList; | ||
928 | if (node) { | ||
929 | countTracer->recycleList = node->next; | ||
930 | } else { | ||
931 | node = (JSCountHeapNode *) JS_malloc(trc->context, sizeof *node); | ||
932 | if (!node) { | ||
933 | countTracer->ok = JS_FALSE; | ||
934 | return; | ||
935 | } | ||
936 | } | ||
937 | node->thing = thing; | ||
938 | node->kind = kind; | ||
939 | node->next = countTracer->traceList; | ||
940 | countTracer->traceList = node; | ||
941 | } | ||
942 | |||
943 | static JSBool | ||
944 | CountHeap(JSContext *cx, uintN argc, jsval *vp) | ||
945 | { | ||
946 | void* startThing; | ||
947 | int32 startTraceKind; | ||
948 | jsval v; | ||
949 | int32 traceKind, i; | ||
950 | JSString *str; | ||
951 | char *bytes; | ||
952 | JSCountHeapTracer countTracer; | ||
953 | JSCountHeapNode *node; | ||
954 | size_t counter; | ||
955 | |||
956 | static const struct { | ||
957 | const char *name; | ||
958 | int32 kind; | ||
959 | } traceKindNames[] = { | ||
960 | { "all", -1 }, | ||
961 | { "object", JSTRACE_OBJECT }, | ||
962 | { "double", JSTRACE_DOUBLE }, | ||
963 | { "string", JSTRACE_STRING }, | ||
964 | #if JS_HAS_XML_SUPPORT | ||
965 | { "xml", JSTRACE_XML }, | ||
966 | #endif | ||
967 | }; | ||
968 | |||
969 | startThing = NULL; | ||
970 | startTraceKind = 0; | ||
971 | if (argc > 0) { | ||
972 | v = JS_ARGV(cx, vp)[0]; | ||
973 | if (JSVAL_IS_TRACEABLE(v)) { | ||
974 | startThing = JSVAL_TO_TRACEABLE(v); | ||
975 | startTraceKind = JSVAL_TRACE_KIND(v); | ||
976 | } else if (v != JSVAL_NULL) { | ||
977 | JS_ReportError(cx, | ||
978 | "the first argument is not null or a heap-allocated " | ||
979 | "thing"); | ||
980 | return JS_FALSE; | ||
981 | } | ||
982 | } | ||
983 | |||
984 | traceKind = -1; | ||
985 | if (argc > 1) { | ||
986 | str = JS_ValueToString(cx, JS_ARGV(cx, vp)[1]); | ||
987 | if (!str) | ||
988 | return JS_FALSE; | ||
989 | bytes = JS_GetStringBytes(str); | ||
990 | if (!bytes) | ||
991 | return JS_FALSE; | ||
992 | for (i = 0; ;) { | ||
993 | if (strcmp(bytes, traceKindNames[i].name) == 0) { | ||
994 | traceKind = traceKindNames[i].kind; | ||
995 | break; | ||
996 | } | ||
997 | if (++i == JS_ARRAY_LENGTH(traceKindNames)) { | ||
998 | JS_ReportError(cx, "trace kind name '%s' is unknown", bytes); | ||
999 | return JS_FALSE; | ||
1000 | } | ||
1001 | } | ||
1002 | } | ||
1003 | |||
1004 | JS_TRACER_INIT(&countTracer.base, cx, CountHeapNotify); | ||
1005 | if (!JS_DHashTableInit(&countTracer.visited, JS_DHashGetStubOps(), | ||
1006 | NULL, sizeof(JSDHashEntryStub), | ||
1007 | JS_DHASH_DEFAULT_CAPACITY(100))) { | ||
1008 | JS_ReportOutOfMemory(cx); | ||
1009 | return JS_FALSE; | ||
1010 | } | ||
1011 | countTracer.ok = JS_TRUE; | ||
1012 | countTracer.traceList = NULL; | ||
1013 | countTracer.recycleList = NULL; | ||
1014 | |||
1015 | if (!startThing) { | ||
1016 | JS_TraceRuntime(&countTracer.base); | ||
1017 | } else { | ||
1018 | JS_SET_TRACING_NAME(&countTracer.base, "root"); | ||
1019 | JS_CallTracer(&countTracer.base, startThing, startTraceKind); | ||
1020 | } | ||
1021 | |||
1022 | counter = 0; | ||
1023 | while ((node = countTracer.traceList) != NULL) { | ||
1024 | if (traceKind == -1 || node->kind == traceKind) | ||
1025 | counter++; | ||
1026 | countTracer.traceList = node->next; | ||
1027 | node->next = countTracer.recycleList; | ||
1028 | countTracer.recycleList = node; | ||
1029 | JS_TraceChildren(&countTracer.base, node->thing, node->kind); | ||
1030 | } | ||
1031 | while ((node = countTracer.recycleList) != NULL) { | ||
1032 | countTracer.recycleList = node->next; | ||
1033 | JS_free(cx, node); | ||
1034 | } | ||
1035 | JS_DHashTableFinish(&countTracer.visited); | ||
1036 | |||
1037 | return countTracer.ok && JS_NewNumberValue(cx, (jsdouble) counter, vp); | ||
1038 | } | ||
1039 | |||
1040 | static JSScript * | ||
1041 | ValueToScript(JSContext *cx, jsval v) | ||
1042 | { | ||
1043 | JSScript *script; | ||
1044 | JSFunction *fun; | ||
1045 | |||
1046 | if (!JSVAL_IS_PRIMITIVE(v) && | ||
1047 | JS_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_ScriptClass) { | ||
1048 | script = (JSScript *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); | ||
1049 | } else { | ||
1050 | fun = JS_ValueToFunction(cx, v); | ||
1051 | if (!fun) | ||
1052 | return NULL; | ||
1053 | script = FUN_SCRIPT(fun); | ||
1054 | } | ||
1055 | |||
1056 | if (!script) { | ||
1057 | JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, | ||
1058 | JSSMSG_SCRIPTS_ONLY); | ||
1059 | } | ||
1060 | |||
1061 | return script; | ||
1062 | } | ||
1063 | |||
1064 | static JSBool | ||
1065 | GetTrapArgs(JSContext *cx, uintN argc, jsval *argv, JSScript **scriptp, | ||
1066 | int32 *ip) | ||
1067 | { | ||
1068 | jsval v; | ||
1069 | uintN intarg; | ||
1070 | JSScript *script; | ||
1071 | |||
1072 | *scriptp = cx->fp->down->script; | ||
1073 | *ip = 0; | ||
1074 | if (argc != 0) { | ||
1075 | v = argv[0]; | ||
1076 | intarg = 0; | ||
1077 | if (!JSVAL_IS_PRIMITIVE(v) && | ||
1078 | (JS_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_FunctionClass || | ||
1079 | JS_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_ScriptClass)) { | ||
1080 | script = ValueToScript(cx, v); | ||
1081 | if (!script) | ||
1082 | return JS_FALSE; | ||
1083 | *scriptp = script; | ||
1084 | intarg++; | ||
1085 | } | ||
1086 | if (argc > intarg) { | ||
1087 | if (!JS_ValueToInt32(cx, argv[intarg], ip)) | ||
1088 | return JS_FALSE; | ||
1089 | } | ||
1090 | } | ||
1091 | return JS_TRUE; | ||
1092 | } | ||
1093 | |||
1094 | static JSTrapStatus | ||
1095 | TrapHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval, | ||
1096 | void *closure) | ||
1097 | { | ||
1098 | JSString *str; | ||
1099 | JSStackFrame *caller; | ||
1100 | |||
1101 | str = (JSString *) closure; | ||
1102 | caller = JS_GetScriptedCaller(cx, NULL); | ||
1103 | if (!JS_EvaluateScript(cx, caller->scopeChain, | ||
1104 | JS_GetStringBytes(str), JS_GetStringLength(str), | ||
1105 | caller->script->filename, caller->script->lineno, | ||
1106 | rval)) { | ||
1107 | return JSTRAP_ERROR; | ||
1108 | } | ||
1109 | if (!JSVAL_IS_VOID(*rval)) | ||
1110 | return JSTRAP_RETURN; | ||
1111 | return JSTRAP_CONTINUE; | ||
1112 | } | ||
1113 | |||
1114 | static JSBool | ||
1115 | Trap(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) | ||
1116 | { | ||
1117 | JSString *str; | ||
1118 | JSScript *script; | ||
1119 | int32 i; | ||
1120 | |||
1121 | if (argc == 0) { | ||
1122 | JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_TRAP_USAGE); | ||
1123 | return JS_FALSE; | ||
1124 | } | ||
1125 | argc--; | ||
1126 | str = JS_ValueToString(cx, argv[argc]); | ||
1127 | if (!str) | ||
1128 | return JS_FALSE; | ||
1129 | argv[argc] = STRING_TO_JSVAL(str); | ||
1130 | if (!GetTrapArgs(cx, argc, argv, &script, &i)) | ||
1131 | return JS_FALSE; | ||
1132 | return JS_SetTrap(cx, script, script->code + i, TrapHandler, str); | ||
1133 | } | ||
1134 | |||
1135 | static JSBool | ||
1136 | Untrap(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) | ||
1137 | { | ||
1138 | JSScript *script; | ||
1139 | int32 i; | ||
1140 | |||
1141 | if (!GetTrapArgs(cx, argc, argv, &script, &i)) | ||
1142 | return JS_FALSE; | ||
1143 | JS_ClearTrap(cx, script, script->code + i, NULL, NULL); | ||
1144 | return JS_TRUE; | ||
1145 | } | ||
1146 | |||
1147 | static JSBool | ||
1148 | LineToPC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) | ||
1149 | { | ||
1150 | JSScript *script; | ||
1151 | int32 i; | ||
1152 | uintN lineno; | ||
1153 | jsbytecode *pc; | ||
1154 | |||
1155 | if (argc == 0) { | ||
1156 | JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_LINE2PC_USAGE); | ||
1157 | return JS_FALSE; | ||
1158 | } | ||
1159 | script = cx->fp->down->script; | ||
1160 | if (!GetTrapArgs(cx, argc, argv, &script, &i)) | ||
1161 | return JS_FALSE; | ||
1162 | lineno = (i == 0) ? script->lineno : (uintN)i; | ||
1163 | pc = JS_LineNumberToPC(cx, script, lineno); | ||
1164 | if (!pc) | ||
1165 | return JS_FALSE; | ||
1166 | *rval = INT_TO_JSVAL(PTRDIFF(pc, script->code, jsbytecode)); | ||
1167 | return JS_TRUE; | ||
1168 | } | ||
1169 | |||
1170 | static JSBool | ||
1171 | PCToLine(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) | ||
1172 | { | ||
1173 | JSScript *script; | ||
1174 | int32 i; | ||
1175 | uintN lineno; | ||
1176 | |||
1177 | if (!GetTrapArgs(cx, argc, argv, &script, &i)) | ||
1178 | return JS_FALSE; | ||
1179 | lineno = JS_PCToLineNumber(cx, script, script->code + i); | ||
1180 | if (!lineno) | ||
1181 | return JS_FALSE; | ||
1182 | *rval = INT_TO_JSVAL(lineno); | ||
1183 | return JS_TRUE; | ||
1184 | } | ||
1185 | |||
1186 | #ifdef DEBUG | ||
1187 | |||
1188 | static void | ||
1189 | UpdateSwitchTableBounds(JSScript *script, uintN offset, | ||
1190 | uintN *start, uintN *end) | ||
1191 | { | ||
1192 | jsbytecode *pc; | ||
1193 | JSOp op; | ||
1194 | ptrdiff_t jmplen; | ||
1195 | jsint low, high, n; | ||
1196 | |||
1197 | pc = script->code + offset; | ||
1198 | op = (JSOp) *pc; | ||
1199 | switch (op) { | ||
1200 | case JSOP_TABLESWITCHX: | ||
1201 | jmplen = JUMPX_OFFSET_LEN; | ||
1202 | goto jump_table; | ||
1203 | case JSOP_TABLESWITCH: | ||
1204 | jmplen = JUMP_OFFSET_LEN; | ||
1205 | jump_table: | ||
1206 | pc += jmplen; | ||
1207 | low = GET_JUMP_OFFSET(pc); | ||
1208 | pc += JUMP_OFFSET_LEN; | ||
1209 | high = GET_JUMP_OFFSET(pc); | ||
1210 | pc += JUMP_OFFSET_LEN; | ||
1211 | n = high - low + 1; | ||
1212 | break; | ||
1213 | |||
1214 | case JSOP_LOOKUPSWITCHX: | ||
1215 | jmplen = JUMPX_OFFSET_LEN; | ||
1216 | goto lookup_table; | ||
1217 | case JSOP_LOOKUPSWITCH: | ||
1218 | jmplen = JUMP_OFFSET_LEN; | ||
1219 | lookup_table: | ||
1220 | pc += jmplen; | ||
1221 | n = GET_INDEX(pc); | ||
1222 | pc += INDEX_LEN; | ||
1223 | jmplen += JUMP_OFFSET_LEN; | ||
1224 | break; | ||
1225 | |||
1226 | default: | ||
1227 | /* [condswitch] switch does not have any jump or lookup tables. */ | ||
1228 | JS_ASSERT(op == JSOP_CONDSWITCH); | ||
1229 | return; | ||
1230 | } | ||
1231 | |||
1232 | *start = (uintN)(pc - script->code); | ||
1233 | *end = *start + (uintN)(n * jmplen); | ||
1234 | } | ||
1235 | |||
1236 | static void | ||
1237 | SrcNotes(JSContext *cx, JSScript *script) | ||
1238 | { | ||
1239 | uintN offset, delta, caseOff, switchTableStart, switchTableEnd; | ||
1240 | jssrcnote *notes, *sn; | ||
1241 | JSSrcNoteType type; | ||
1242 | const char *name; | ||
1243 | uint32 index; | ||
1244 | JSAtom *atom; | ||
1245 | JSString *str; | ||
1246 | |||
1247 | fprintf(gOutFile, "\nSource notes:\n"); | ||
1248 | offset = 0; | ||
1249 | notes = SCRIPT_NOTES(script); | ||
1250 | switchTableEnd = switchTableStart = 0; | ||
1251 | for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) { | ||
1252 | delta = SN_DELTA(sn); | ||
1253 | offset += delta; | ||
1254 | type = (JSSrcNoteType) SN_TYPE(sn); | ||
1255 | name = js_SrcNoteSpec[type].name; | ||
1256 | if (type == SRC_LABEL) { | ||
1257 | /* Check if the source note is for a switch case. */ | ||
1258 | if (switchTableStart <= offset && offset < switchTableEnd) { | ||
1259 | name = "case"; | ||
1260 | } else { | ||
1261 | JS_ASSERT(script->code[offset] == JSOP_NOP); | ||
1262 | } | ||
1263 | } | ||
1264 | fprintf(gOutFile, "%3u: %5u [%4u] %-8s", | ||
1265 | (uintN) PTRDIFF(sn, notes, jssrcnote), offset, delta, name); | ||
1266 | switch (type) { | ||
1267 | case SRC_SETLINE: | ||
1268 | fprintf(gOutFile, " lineno %u", (uintN) js_GetSrcNoteOffset(sn, 0)); | ||
1269 | break; | ||
1270 | case SRC_FOR: | ||
1271 | fprintf(gOutFile, " cond %u update %u tail %u", | ||
1272 | (uintN) js_GetSrcNoteOffset(sn, 0), | ||
1273 | (uintN) js_GetSrcNoteOffset(sn, 1), | ||
1274 | (uintN) js_GetSrcNoteOffset(sn, 2)); | ||
1275 | break; | ||
1276 | case SRC_IF_ELSE: | ||
1277 | fprintf(gOutFile, " else %u elseif %u", | ||
1278 | (uintN) js_GetSrcNoteOffset(sn, 0), | ||
1279 | (uintN) js_GetSrcNoteOffset(sn, 1)); | ||
1280 | break; | ||
1281 | case SRC_COND: | ||
1282 | case SRC_WHILE: | ||
1283 | case SRC_PCBASE: | ||
1284 | case SRC_PCDELTA: | ||
1285 | case SRC_DECL: | ||
1286 | case SRC_BRACE: | ||
1287 | fprintf(gOutFile, " offset %u", (uintN) js_GetSrcNoteOffset(sn, 0)); | ||
1288 | break; | ||
1289 | case SRC_LABEL: | ||
1290 | case SRC_LABELBRACE: | ||
1291 | case SRC_BREAK2LABEL: | ||
1292 | case SRC_CONT2LABEL: | ||
1293 | index = js_GetSrcNoteOffset(sn, 0); | ||
1294 | JS_GET_SCRIPT_ATOM(script, index, atom); | ||
1295 | JS_ASSERT(ATOM_IS_STRING(atom)); | ||
1296 | str = ATOM_TO_STRING(atom); | ||
1297 | fprintf(gOutFile, " atom %u (", index); | ||
1298 | js_FileEscapedString(gOutFile, str, 0); | ||
1299 | putc(')', gOutFile); | ||
1300 | break; | ||
1301 | case SRC_FUNCDEF: { | ||
1302 | const char *bytes; | ||
1303 | JSObject *obj; | ||
1304 | JSFunction *fun; | ||
1305 | |||
1306 | index = js_GetSrcNoteOffset(sn, 0); | ||
1307 | JS_GET_SCRIPT_OBJECT(script, index, obj); | ||
1308 | fun = (JSFunction *) JS_GetPrivate(cx, obj); | ||
1309 | str = JS_DecompileFunction(cx, fun, JS_DONT_PRETTY_PRINT); | ||
1310 | bytes = str ? JS_GetStringBytes(str) : "N/A"; | ||
1311 | fprintf(gOutFile, " function %u (%s)", index, bytes); | ||
1312 | break; | ||
1313 | } | ||
1314 | case SRC_SWITCH: | ||
1315 | fprintf(gOutFile, " length %u", (uintN) js_GetSrcNoteOffset(sn, 0)); | ||
1316 | caseOff = (uintN) js_GetSrcNoteOffset(sn, 1); | ||
1317 | if (caseOff) | ||
1318 | fprintf(gOutFile, " first case offset %u", caseOff); | ||
1319 | UpdateSwitchTableBounds(script, offset, | ||
1320 | &switchTableStart, &switchTableEnd); | ||
1321 | break; | ||
1322 | case SRC_CATCH: | ||
1323 | delta = (uintN) js_GetSrcNoteOffset(sn, 0); | ||
1324 | if (delta) { | ||
1325 | if (script->main[offset] == JSOP_LEAVEBLOCK) | ||
1326 | fprintf(gOutFile, " stack depth %u", delta); | ||
1327 | else | ||
1328 | fprintf(gOutFile, " guard delta %u", delta); | ||
1329 | } | ||
1330 | break; | ||
1331 | default:; | ||
1332 | } | ||
1333 | fputc('\n', gOutFile); | ||
1334 | } | ||
1335 | } | ||
1336 | |||
1337 | static JSBool | ||
1338 | Notes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) | ||
1339 | { | ||
1340 | uintN i; | ||
1341 | JSScript *script; | ||
1342 | |||
1343 | for (i = 0; i < argc; i++) { | ||
1344 | script = ValueToScript(cx, argv[i]); | ||
1345 | if (!script) | ||
1346 | continue; | ||
1347 | |||
1348 | SrcNotes(cx, script); | ||
1349 | } | ||
1350 | return JS_TRUE; | ||
1351 | } | ||
1352 | |||
1353 | JS_STATIC_ASSERT(JSTN_CATCH == 0); | ||
1354 | JS_STATIC_ASSERT(JSTN_FINALLY == 1); | ||
1355 | JS_STATIC_ASSERT(JSTN_ITER == 2); | ||
1356 | |||
1357 | static const char* const TryNoteNames[] = { "catch", "finally", "iter" }; | ||
1358 | |||
1359 | static JSBool | ||
1360 | TryNotes(JSContext *cx, JSScript *script) | ||
1361 | { | ||
1362 | JSTryNote *tn, *tnlimit; | ||
1363 | |||
1364 | if (script->trynotesOffset == 0) | ||
1365 | return JS_TRUE; | ||
1366 | |||
1367 | tn = JS_SCRIPT_TRYNOTES(script)->vector; | ||
1368 | tnlimit = tn + JS_SCRIPT_TRYNOTES(script)->length; | ||
1369 | fprintf(gOutFile, "\nException table:\n" | ||
1370 | "kind stack start end\n"); | ||
1371 | do { | ||
1372 | JS_ASSERT(tn->kind < JS_ARRAY_LENGTH(TryNoteNames)); | ||
1373 | fprintf(gOutFile, " %-7s %6u %8u %8u\n", | ||
1374 | TryNoteNames[tn->kind], tn->stackDepth, | ||
1375 | tn->start, tn->start + tn->length); | ||
1376 | } while (++tn != tnlimit); | ||
1377 | return JS_TRUE; | ||
1378 | } | ||
1379 | |||
1380 | static JSBool | ||
1381 | Disassemble(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) | ||
1382 | { | ||
1383 | JSBool lines; | ||
1384 | uintN i; | ||
1385 | JSScript *script; | ||
1386 | |||
1387 | if (argc > 0 && | ||
1388 | JSVAL_IS_STRING(argv[0]) && | ||
1389 | !strcmp(JS_GetStringBytes(JSVAL_TO_STRING(argv[0])), "-l")) { | ||
1390 | lines = JS_TRUE; | ||
1391 | argv++, argc--; | ||
1392 | } else { | ||
1393 | lines = JS_FALSE; | ||
1394 | } | ||
1395 | for (i = 0; i < argc; i++) { | ||
1396 | script = ValueToScript(cx, argv[i]); | ||
1397 | if (!script) | ||
1398 | return JS_FALSE; | ||
1399 | if (VALUE_IS_FUNCTION(cx, argv[i])) { | ||
1400 | JSFunction *fun = JS_ValueToFunction(cx, argv[i]); | ||
1401 | if (fun && (fun->flags & JSFUN_FLAGS_MASK)) { | ||
1402 | uint16 flags = fun->flags; | ||
1403 | fputs("flags:", stdout); | ||
1404 | |||
1405 | #define SHOW_FLAG(flag) if (flags & JSFUN_##flag) fputs(" " #flag, stdout); | ||
1406 | |||
1407 | SHOW_FLAG(LAMBDA); | ||
1408 | SHOW_FLAG(SETTER); | ||
1409 | SHOW_FLAG(GETTER); | ||
1410 | SHOW_FLAG(BOUND_METHOD); | ||
1411 | SHOW_FLAG(HEAVYWEIGHT); | ||
1412 | SHOW_FLAG(THISP_STRING); | ||
1413 | SHOW_FLAG(THISP_NUMBER); | ||
1414 | SHOW_FLAG(THISP_BOOLEAN); | ||
1415 | SHOW_FLAG(EXPR_CLOSURE); | ||
1416 | SHOW_FLAG(INTERPRETED); | ||
1417 | |||
1418 | #undef SHOW_FLAG | ||
1419 | putchar('\n'); | ||
1420 | } | ||
1421 | } | ||
1422 | |||
1423 | if (!js_Disassemble(cx, script, lines, stdout)) | ||
1424 | return JS_FALSE; | ||
1425 | SrcNotes(cx, script); | ||
1426 | TryNotes(cx, script); | ||
1427 | } | ||
1428 | return JS_TRUE; | ||
1429 | } | ||
1430 | |||
1431 | static JSBool | ||
1432 | DisassFile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) | ||
1433 | { | ||
1434 | JSString *str; | ||
1435 | const char *filename; | ||
1436 | JSScript *script; | ||
1437 | JSBool ok; | ||
1438 | uint32 oldopts; | ||
1439 | |||
1440 | if (!argc) | ||
1441 | return JS_TRUE; | ||
1442 | |||
1443 | str = JS_ValueToString(cx, argv[0]); | ||
1444 | if (!str) | ||
1445 | return JS_FALSE; | ||
1446 | argv[0] = STRING_TO_JSVAL(str); | ||
1447 | |||
1448 | filename = JS_GetStringBytes(str); | ||
1449 | oldopts = JS_GetOptions(cx); | ||
1450 | JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO | JSOPTION_NO_SCRIPT_RVAL); | ||
1451 | script = JS_CompileFile(cx, obj, filename); | ||
1452 | JS_SetOptions(cx, oldopts); | ||
1453 | if (!script) | ||
1454 | return JS_FALSE; | ||
1455 | |||
1456 | obj = JS_NewScriptObject(cx, script); | ||
1457 | if (!obj) | ||
1458 | return JS_FALSE; | ||
1459 | |||
1460 | *rval = OBJECT_TO_JSVAL(obj); /* I like to root it, root it. */ | ||
1461 | ok = Disassemble(cx, obj, 1, rval, rval); /* gross, but works! */ | ||
1462 | *rval = JSVAL_VOID; | ||
1463 | |||
1464 | return ok; | ||
1465 | } | ||
1466 | |||
1467 | static JSBool | ||
1468 | DisassWithSrc(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, | ||
1469 | jsval *rval) | ||
1470 | { | ||
1471 | #define LINE_BUF_LEN 512 | ||
1472 | uintN i, len, line1, line2, bupline; | ||
1473 | JSScript *script; | ||
1474 | FILE *file; | ||
1475 | char linebuf[LINE_BUF_LEN]; | ||
1476 | jsbytecode *pc, *end; | ||
1477 | JSBool ok; | ||
1478 | static char sep[] = ";-------------------------"; | ||
1479 | |||
1480 | ok = JS_TRUE; | ||
1481 | for (i = 0; ok && i < argc; i++) { | ||
1482 | script = ValueToScript(cx, argv[i]); | ||
1483 | if (!script) | ||
1484 | return JS_FALSE; | ||
1485 | |||
1486 | if (!script->filename) { | ||
1487 | JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, | ||
1488 | JSSMSG_FILE_SCRIPTS_ONLY); | ||
1489 | return JS_FALSE; | ||
1490 | } | ||
1491 | |||
1492 | file = fopen(script->filename, "r"); | ||
1493 | if (!file) { | ||
1494 | JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, | ||
1495 | JSSMSG_CANT_OPEN, script->filename, | ||
1496 | strerror(errno)); | ||
1497 | return JS_FALSE; | ||
1498 | } | ||
1499 | |||
1500 | pc = script->code; | ||
1501 | end = pc + script->length; | ||
1502 | |||
1503 | /* burn the leading lines */ | ||
1504 | line2 = JS_PCToLineNumber(cx, script, pc); | ||
1505 | for (line1 = 0; line1 < line2 - 1; line1++) | ||
1506 | fgets(linebuf, LINE_BUF_LEN, file); | ||
1507 | |||
1508 | bupline = 0; | ||
1509 | while (pc < end) { | ||
1510 | line2 = JS_PCToLineNumber(cx, script, pc); | ||
1511 | |||
1512 | if (line2 < line1) { | ||
1513 | if (bupline != line2) { | ||
1514 | bupline = line2; | ||
1515 | fprintf(gOutFile, "%s %3u: BACKUP\n", sep, line2); | ||
1516 | } | ||
1517 | } else { | ||
1518 | if (bupline && line1 == line2) | ||
1519 | fprintf(gOutFile, "%s %3u: RESTORE\n", sep, line2); | ||
1520 | bupline = 0; | ||
1521 | while (line1 < line2) { | ||
1522 | if (!fgets(linebuf, LINE_BUF_LEN, file)) { | ||
1523 | JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, | ||
1524 | JSSMSG_UNEXPECTED_EOF, | ||
1525 | script->filename); | ||
1526 | ok = JS_FALSE; | ||
1527 | goto bail; | ||
1528 | } | ||
1529 | line1++; | ||
1530 | fprintf(gOutFile, "%s %3u: %s", sep, line1, linebuf); | ||
1531 | } | ||
1532 | } | ||
1533 | |||
1534 | len = js_Disassemble1(cx, script, pc, | ||
1535 | PTRDIFF(pc, script->code, jsbytecode), | ||
1536 | JS_TRUE, stdout); | ||
1537 | if (!len) { | ||
1538 | ok = JS_FALSE; | ||
1539 | goto bail; | ||
1540 | } | ||
1541 | pc += len; | ||
1542 | } | ||
1543 | |||
1544 | bail: | ||
1545 | fclose(file); | ||
1546 | } | ||
1547 | return ok; | ||
1548 | #undef LINE_BUF_LEN | ||
1549 | } | ||
1550 | |||
1551 | static JSBool | ||
1552 | Tracing(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) | ||
1553 | { | ||
1554 | JSBool bval; | ||
1555 | JSString *str; | ||
1556 | |||
1557 | #if JS_THREADED_INTERP | ||
1558 | JS_ReportError(cx, "tracing not supported in JS_THREADED_INTERP builds"); | ||
1559 | return JS_FALSE; | ||
1560 | #else | ||
1561 | if (argc == 0) { | ||
1562 | *rval = BOOLEAN_TO_JSVAL(cx->tracefp != 0); | ||
1563 | return JS_TRUE; | ||
1564 | } | ||
1565 | |||
1566 | switch (JS_TypeOfValue(cx, argv[0])) { | ||
1567 | case JSTYPE_NUMBER: | ||
1568 | bval = JSVAL_IS_INT(argv[0]) | ||
1569 | ? JSVAL_TO_INT(argv[0]) | ||
1570 | : (jsint) *JSVAL_TO_DOUBLE(argv[0]); | ||
1571 | break; | ||
1572 | case JSTYPE_BOOLEAN: | ||
1573 | bval = JSVAL_TO_BOOLEAN(argv[0]); | ||
1574 | break; | ||
1575 | default: | ||
1576 | str = JS_ValueToString(cx, argv[0]); | ||
1577 | if (!str) | ||
1578 | return JS_FALSE; | ||
1579 | JS_ReportError(cx, "tracing: illegal argument %s", | ||
1580 | JS_GetStringBytes(str)); | ||
1581 | return JS_FALSE; | ||
1582 | } | ||
1583 | cx->tracefp = bval ? stderr : NULL; | ||
1584 | return JS_TRUE; | ||
1585 | #endif | ||
1586 | } | ||
1587 | |||
1588 | static void | ||
1589 | DumpScope(JSContext *cx, JSObject *obj, FILE *fp) | ||
1590 | { | ||
1591 | uintN i; | ||
1592 | JSScope *scope; | ||
1593 | JSScopeProperty *sprop; | ||
1594 | jsval v; | ||
1595 | JSString *str; | ||
1596 | |||
1597 | i = 0; | ||
1598 | scope = OBJ_SCOPE(obj); | ||
1599 | for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) { | ||
1600 | if (SCOPE_HAD_MIDDLE_DELETE(scope) && !SCOPE_HAS_PROPERTY(scope, sprop)) | ||
1601 | continue; | ||
1602 | fprintf(fp, "%3u %p ", i, (void *)sprop); | ||
1603 | |||
1604 | v = ID_TO_VALUE(sprop->id); | ||
1605 | if (JSID_IS_INT(sprop->id)) { | ||
1606 | fprintf(fp, "[%ld]", (long)JSVAL_TO_INT(v)); | ||
1607 | } else { | ||
1608 | if (JSID_IS_ATOM(sprop->id)) { | ||
1609 | str = JSVAL_TO_STRING(v); | ||
1610 | } else { | ||
1611 | JS_ASSERT(JSID_IS_OBJECT(sprop->id)); | ||
1612 | str = js_ValueToString(cx, v); | ||
1613 | fputs("object ", fp); | ||
1614 | } | ||
1615 | if (!str) | ||
1616 | fputs("<error>", fp); | ||
1617 | else | ||
1618 | js_FileEscapedString(fp, str, '"'); | ||
1619 | } | ||
1620 | #define DUMP_ATTR(name) if (sprop->attrs & JSPROP_##name) fputs(" " #name, fp) | ||
1621 | DUMP_ATTR(ENUMERATE); | ||
1622 | DUMP_ATTR(READONLY); | ||
1623 | DUMP_ATTR(PERMANENT); | ||
1624 | DUMP_ATTR(GETTER); | ||
1625 | DUMP_ATTR(SETTER); | ||
1626 | #undef DUMP_ATTR | ||
1627 | |||
1628 | fprintf(fp, " slot %lu flags %x shortid %d\n", | ||
1629 | (unsigned long)sprop->slot, sprop->flags, sprop->shortid); | ||
1630 | } | ||
1631 | } | ||
1632 | |||
1633 | static JSBool | ||
1634 | DumpStats(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) | ||
1635 | { | ||
1636 | uintN i; | ||
1637 | JSString *str; | ||
1638 | const char *bytes; | ||
1639 | jsid id; | ||
1640 | JSObject *obj2; | ||
1641 | JSProperty *prop; | ||
1642 | jsval value; | ||
1643 | |||
1644 | for (i = 0; i < argc; i++) { | ||
1645 | str = JS_ValueToString(cx, argv[i]); | ||
1646 | if (!str) | ||
1647 | return JS_FALSE; | ||
1648 | argv[i] = STRING_TO_JSVAL(str); | ||
1649 | bytes = JS_GetStringBytes(str); | ||
1650 | if (strcmp(bytes, "arena") == 0) { | ||
1651 | #ifdef JS_ARENAMETER | ||
1652 | JS_DumpArenaStats(stdout); | ||
1653 | #endif | ||
1654 | } else if (strcmp(bytes, "atom") == 0) { | ||
1655 | js_DumpAtoms(cx, gOutFile); | ||
1656 | } else if (strcmp(bytes, "global") == 0) { | ||
1657 | DumpScope(cx, cx->globalObject, stdout); | ||
1658 | } else { | ||
1659 | if (!JS_ValueToId(cx, STRING_TO_JSVAL(str), &id)) | ||
1660 | return JS_FALSE; | ||
1661 | if (!js_FindProperty(cx, id, &obj, &obj2, &prop)) | ||
1662 | return JS_FALSE; | ||
1663 | if (prop) { | ||
1664 | OBJ_DROP_PROPERTY(cx, obj2, prop); | ||
1665 | if (!OBJ_GET_PROPERTY(cx, obj, id, &value)) | ||
1666 | return JS_FALSE; | ||
1667 | } | ||
1668 | if (!prop || !JSVAL_IS_OBJECT(value)) { | ||
1669 | fprintf(gErrFile, "js: invalid stats argument %s\n", | ||
1670 | bytes); | ||
1671 | continue; | ||
1672 | } | ||
1673 | obj = JSVAL_TO_OBJECT(value); | ||
1674 | if (obj) | ||
1675 | DumpScope(cx, obj, stdout); | ||
1676 | } | ||
1677 | } | ||
1678 | return JS_TRUE; | ||
1679 | } | ||
1680 | |||
1681 | static JSBool | ||
1682 | DumpHeap(JSContext *cx, uintN argc, jsval *vp) | ||
1683 | { | ||
1684 | char *fileName; | ||
1685 | jsval v; | ||
1686 | void* startThing; | ||
1687 | uint32 startTraceKind; | ||
1688 | const char *badTraceArg; | ||
1689 | void *thingToFind; | ||
1690 | size_t maxDepth; | ||
1691 | void *thingToIgnore; | ||
1692 | FILE *dumpFile; | ||
1693 | JSBool ok; | ||
1694 | |||
1695 | fileName = NULL; | ||
1696 | if (argc > 0) { | ||
1697 | v = JS_ARGV(cx, vp)[0]; | ||
1698 | if (v != JSVAL_NULL) { | ||
1699 | JSString *str; | ||
1700 | |||
1701 | str = JS_ValueToString(cx, v); | ||
1702 | if (!str) | ||
1703 | return JS_FALSE; | ||
1704 | JS_ARGV(cx, vp)[0] = STRING_TO_JSVAL(str); | ||
1705 | fileName = JS_GetStringBytes(str); | ||
1706 | } | ||
1707 | } | ||
1708 | |||
1709 | startThing = NULL; | ||
1710 | startTraceKind = 0; | ||
1711 | if (argc > 1) { | ||
1712 | v = JS_ARGV(cx, vp)[1]; | ||
1713 | if (JSVAL_IS_TRACEABLE(v)) { | ||
1714 | startThing = JSVAL_TO_TRACEABLE(v); | ||
1715 | startTraceKind = JSVAL_TRACE_KIND(v); | ||
1716 | } else if (v != JSVAL_NULL) { | ||
1717 | badTraceArg = "start"; | ||
1718 | goto not_traceable_arg; | ||
1719 | } | ||
1720 | } | ||
1721 | |||
1722 | thingToFind = NULL; | ||
1723 | if (argc > 2) { | ||
1724 | v = JS_ARGV(cx, vp)[2]; | ||
1725 | if (JSVAL_IS_TRACEABLE(v)) { | ||
1726 | thingToFind = JSVAL_TO_TRACEABLE(v); | ||
1727 | } else if (v != JSVAL_NULL) { | ||
1728 | badTraceArg = "toFind"; | ||
1729 | goto not_traceable_arg; | ||
1730 | } | ||
1731 | } | ||
1732 | |||
1733 | maxDepth = (size_t)-1; | ||
1734 | if (argc > 3) { | ||
1735 | v = JS_ARGV(cx, vp)[3]; | ||
1736 | if (v != JSVAL_NULL) { | ||
1737 | uint32 depth; | ||
1738 | |||
1739 | if (!JS_ValueToECMAUint32(cx, v, &depth)) | ||
1740 | return JS_FALSE; | ||
1741 | maxDepth = depth; | ||
1742 | } | ||
1743 | } | ||
1744 | |||
1745 | thingToIgnore = NULL; | ||
1746 | if (argc > 4) { | ||
1747 | v = JS_ARGV(cx, vp)[4]; | ||
1748 | if (JSVAL_IS_TRACEABLE(v)) { | ||
1749 | thingToIgnore = JSVAL_TO_TRACEABLE(v); | ||
1750 | } else if (v != JSVAL_NULL) { | ||
1751 | badTraceArg = "toIgnore"; | ||
1752 | goto not_traceable_arg; | ||
1753 | } | ||
1754 | } | ||
1755 | |||
1756 | if (!fileName) { | ||
1757 | dumpFile = stdout; | ||
1758 | } else { | ||
1759 | dumpFile = fopen(fileName, "w"); | ||
1760 | if (!dumpFile) { | ||
1761 | JS_ReportError(cx, "can't open %s: %s", fileName, strerror(errno)); | ||
1762 | return JS_FALSE; | ||
1763 | } | ||
1764 | } | ||
1765 | |||
1766 | ok = JS_DumpHeap(cx, dumpFile, startThing, startTraceKind, thingToFind, | ||
1767 | maxDepth, thingToIgnore); | ||
1768 | if (dumpFile != stdout) | ||
1769 | fclose(dumpFile); | ||
1770 | return ok; | ||
1771 | |||
1772 | not_traceable_arg: | ||
1773 | JS_ReportError(cx, "argument '%s' is not null or a heap-allocated thing", | ||
1774 | badTraceArg); | ||
1775 | return JS_FALSE; | ||
1776 | } | ||
1777 | |||
1778 | #endif /* DEBUG */ | ||
1779 | |||
1780 | #ifdef TEST_CVTARGS | ||
1781 | #include <ctype.h> | ||
1782 | |||
1783 | static const char * | ||
1784 | EscapeWideString(jschar *w) | ||
1785 | { | ||
1786 | static char enuf[80]; | ||
1787 | static char hex[] = "0123456789abcdef"; | ||
1788 | jschar u; | ||
1789 | unsigned char b, c; | ||
1790 | int i, j; | ||
1791 | |||
1792 | if (!w) | ||
1793 | return ""; | ||
1794 | for (i = j = 0; i < sizeof enuf - 1; i++, j++) { | ||
1795 | u = w[j]; | ||
1796 | if (u == 0) | ||
1797 | break; | ||
1798 | b = (unsigned char)(u >> 8); | ||
1799 | c = (unsigned char)(u); | ||
1800 | if (b) { | ||
1801 | if (i >= sizeof enuf - 6) | ||
1802 | break; | ||
1803 | enuf[i++] = '\\'; | ||
1804 | enuf[i++] = 'u'; | ||
1805 | enuf[i++] = hex[b >> 4]; | ||
1806 | enuf[i++] = hex[b & 15]; | ||
1807 | enuf[i++] = hex[c >> 4]; | ||
1808 | enuf[i] = hex[c & 15]; | ||
1809 | } else if (!isprint(c)) { | ||
1810 | if (i >= sizeof enuf - 4) | ||
1811 | break; | ||
1812 | enuf[i++] = '\\'; | ||
1813 | enuf[i++] = 'x'; | ||
1814 | enuf[i++] = hex[c >> 4]; | ||
1815 | enuf[i] = hex[c & 15]; | ||
1816 | } else { | ||
1817 | enuf[i] = (char)c; | ||
1818 | } | ||
1819 | } | ||
1820 | enuf[i] = 0; | ||
1821 | return enuf; | ||
1822 | } | ||
1823 | |||
1824 | #include <stdarg.h> | ||
1825 | |||
1826 | static JSBool | ||
1827 | ZZ_formatter(JSContext *cx, const char *format, JSBool fromJS, jsval **vpp, | ||
1828 | va_list *app) | ||
1829 | { | ||
1830 | jsval *vp; | ||
1831 | va_list ap; | ||
1832 | jsdouble re, im; | ||
1833 | |||
1834 | printf("entering ZZ_formatter"); | ||
1835 | vp = *vpp; | ||
1836 | ap = *app; | ||
1837 | if (fromJS) { | ||
1838 | if (!JS_ValueToNumber(cx, vp[0], &re)) | ||
1839 | return JS_FALSE; | ||
1840 | if (!JS_ValueToNumber(cx, vp[1], &im)) | ||
1841 | return JS_FALSE; | ||
1842 | *va_arg(ap, jsdouble *) = re; | ||
1843 | *va_arg(ap, jsdouble *) = im; | ||
1844 | } else { | ||
1845 | re = va_arg(ap, jsdouble); | ||
1846 | im = va_arg(ap, jsdouble); | ||
1847 | if (!JS_NewNumberValue(cx, re, &vp[0])) | ||
1848 | return JS_FALSE; | ||
1849 | if (!JS_NewNumberValue(cx, im, &vp[1])) | ||
1850 | return JS_FALSE; | ||
1851 | } | ||
1852 | *vpp = vp + 2; | ||
1853 | *app = ap; | ||
1854 | printf("leaving ZZ_formatter"); | ||
1855 | return JS_TRUE; | ||
1856 | } | ||
1857 | |||
1858 | static JSBool | ||
1859 | ConvertArgs(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) | ||
1860 | { | ||
1861 | JSBool b = JS_FALSE; | ||
1862 | jschar c = 0; | ||
1863 | int32 i = 0, j = 0; | ||
1864 | uint32 u = 0; | ||
1865 | jsdouble d = 0, I = 0, re = 0, im = 0; | ||
1866 | char *s = NULL; | ||
1867 | JSString *str = NULL; | ||
1868 | jschar *w = NULL; | ||
1869 | JSObject *obj2 = NULL; | ||
1870 | JSFunction *fun = NULL; | ||
1871 | jsval v = JSVAL_VOID; | ||
1872 | JSBool ok; | ||
1873 | |||
1874 | if (!JS_AddArgumentFormatter(cx, "ZZ", ZZ_formatter)) | ||
1875 | return JS_FALSE;; | ||
1876 | ok = JS_ConvertArguments(cx, argc, argv, "b/ciujdIsSWofvZZ*", | ||
1877 | &b, &c, &i, &u, &j, &d, &I, &s, &str, &w, &obj2, | ||
1878 | &fun, &v, &re, &im); | ||
1879 | JS_RemoveArgumentFormatter(cx, "ZZ"); | ||
1880 | if (!ok) | ||
1881 | return JS_FALSE; | ||
1882 | fprintf(gOutFile, | ||
1883 | "b %u, c %x (%c), i %ld, u %lu, j %ld\n", | ||
1884 | b, c, (char)c, i, u, j); | ||
1885 | fprintf(gOutFile, | ||
1886 | "d %g, I %g, s %s, S %s, W %s, obj %s, fun %s\n" | ||
1887 | "v %s, re %g, im %g\n", | ||
1888 | d, I, s, str ? JS_GetStringBytes(str) : "", EscapeWideString(w), | ||
1889 | JS_GetStringBytes(JS_ValueToString(cx, OBJECT_TO_JSVAL(obj2))), | ||
1890 | fun ? JS_GetStringBytes(JS_DecompileFunction(cx, fun, 4)) : "", | ||
1891 | JS_GetStringBytes(JS_ValueToString(cx, v)), re, im); | ||
1892 | return JS_TRUE; | ||
1893 | } | ||
1894 | #endif | ||
1895 | |||
1896 | static JSBool | ||
1897 | BuildDate(JSContext *cx, uintN argc, jsval *vp) | ||
1898 | { | ||
1899 | char version[20] = "\n"; | ||
1900 | #if JS_VERSION < 150 | ||
1901 | sprintf(version, " for version %d\n", JS_VERSION); | ||
1902 | #endif | ||
1903 | fprintf(gOutFile, "built on %s at %s%s", __DATE__, __TIME__, version); | ||
1904 | *vp = JSVAL_VOID; | ||
1905 | return JS_TRUE; | ||
1906 | } | ||
1907 | |||
1908 | static JSBool | ||
1909 | Clear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) | ||
1910 | { | ||
1911 | if (argc != 0 && !JS_ValueToObject(cx, argv[0], &obj)) | ||
1912 | return JS_FALSE; | ||
1913 | JS_ClearScope(cx, obj); | ||
1914 | return JS_TRUE; | ||
1915 | } | ||
1916 | |||
1917 | static JSBool | ||
1918 | Intern(JSContext *cx, uintN argc, jsval *vp) | ||
1919 | { | ||
1920 | JSString *str; | ||
1921 | |||
1922 | str = JS_ValueToString(cx, argc == 0 ? JSVAL_VOID : vp[2]); | ||
1923 | if (!str) | ||
1924 | return JS_FALSE; | ||
1925 | if (!JS_InternUCStringN(cx, JS_GetStringChars(str), | ||
1926 | JS_GetStringLength(str))) { | ||
1927 | return JS_FALSE; | ||
1928 | } | ||
1929 | *vp = JSVAL_VOID; | ||
1930 | return JS_TRUE; | ||
1931 | } | ||
1932 | |||
1933 | static JSBool | ||
1934 | Clone(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) | ||
1935 | { | ||
1936 | JSFunction *fun; | ||
1937 | JSObject *funobj, *parent, *clone; | ||
1938 | |||
1939 | fun = JS_ValueToFunction(cx, argv[0]); | ||
1940 | if (!fun) | ||
1941 | return JS_FALSE; | ||
1942 | funobj = JS_GetFunctionObject(fun); | ||
1943 | if (argc > 1) { | ||
1944 | if (!JS_ValueToObject(cx, argv[1], &parent)) | ||
1945 | return JS_FALSE; | ||
1946 | } else { | ||
1947 | parent = JS_GetParent(cx, funobj); | ||
1948 | } | ||
1949 | clone = JS_CloneFunctionObject(cx, funobj, parent); | ||
1950 | if (!clone) | ||
1951 | return JS_FALSE; | ||
1952 | *rval = OBJECT_TO_JSVAL(clone); | ||
1953 | return JS_TRUE; | ||
1954 | } | ||
1955 | |||
1956 | static JSBool | ||
1957 | Seal(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) | ||
1958 | { | ||
1959 | JSObject *target; | ||
1960 | JSBool deep = JS_FALSE; | ||
1961 | |||
1962 | if (!JS_ConvertArguments(cx, argc, argv, "o/b", &target, &deep)) | ||
1963 | return JS_FALSE; | ||
1964 | if (!target) | ||
1965 | return JS_TRUE; | ||
1966 | return JS_SealObject(cx, target, deep); | ||
1967 | } | ||
1968 | |||
1969 | static JSBool | ||
1970 | GetPDA(JSContext *cx, uintN argc, jsval *vp) | ||
1971 | { | ||
1972 | JSObject *vobj, *aobj, *pdobj; | ||
1973 | JSBool ok; | ||
1974 | JSPropertyDescArray pda; | ||
1975 | JSPropertyDesc *pd; | ||
1976 | uint32 i; | ||
1977 | jsval v; | ||
1978 | |||
1979 | if (!JS_ValueToObject(cx, argc == 0 ? JSVAL_VOID : vp[2], &vobj)) | ||
1980 | return JS_FALSE; | ||
1981 | if (!vobj) | ||
1982 | return JS_TRUE; | ||
1983 | |||
1984 | aobj = JS_NewArrayObject(cx, 0, NULL); | ||
1985 | if (!aobj) | ||
1986 | return JS_FALSE; | ||
1987 | *vp = OBJECT_TO_JSVAL(aobj); | ||
1988 | |||
1989 | ok = JS_GetPropertyDescArray(cx, vobj, &pda); | ||
1990 | if (!ok) | ||
1991 | return JS_FALSE; | ||
1992 | pd = pda.array; | ||
1993 | for (i = 0; i < pda.length; i++) { | ||
1994 | pdobj = JS_NewObject(cx, NULL, NULL, NULL); | ||
1995 | if (!pdobj) { | ||
1996 | ok = JS_FALSE; | ||
1997 | break; | ||
1998 | } | ||
1999 | |||
2000 | /* Protect pdobj from GC by setting it as an element of aobj now */ | ||
2001 | v = OBJECT_TO_JSVAL(pdobj); | ||
2002 | ok = JS_SetElement(cx, aobj, i, &v); | ||
2003 | if (!ok) | ||
2004 | break; | ||
2005 | |||
2006 | ok = JS_SetProperty(cx, pdobj, "id", &pd->id) && | ||
2007 | JS_SetProperty(cx, pdobj, "value", &pd->value) && | ||
2008 | (v = INT_TO_JSVAL(pd->flags), | ||
2009 | JS_SetProperty(cx, pdobj, "flags", &v)) && | ||
2010 | (v = INT_TO_JSVAL(pd->slot), | ||
2011 | JS_SetProperty(cx, pdobj, "slot", &v)) && | ||
2012 | JS_SetProperty(cx, pdobj, "alias", &pd->alias); | ||
2013 | if (!ok) | ||
2014 | break; | ||
2015 | } | ||
2016 | JS_PutPropertyDescArray(cx, &pda); | ||
2017 | return ok; | ||
2018 | } | ||
2019 | |||
2020 | static JSBool | ||
2021 | GetSLX(JSContext *cx, uintN argc, jsval *vp) | ||
2022 | { | ||
2023 | JSScript *script; | ||
2024 | |||
2025 | script = ValueToScript(cx, argc == 0 ? JSVAL_VOID : vp[2]); | ||
2026 | if (!script) | ||
2027 | return JS_FALSE; | ||
2028 | *vp = INT_TO_JSVAL(js_GetScriptLineExtent(script)); | ||
2029 | return JS_TRUE; | ||
2030 | } | ||
2031 | |||
2032 | static JSBool | ||
2033 | ToInt32(JSContext *cx, uintN argc, jsval *vp) | ||
2034 | { | ||
2035 | int32 i; | ||
2036 | |||
2037 | if (!JS_ValueToInt32(cx, argc == 0 ? JSVAL_VOID : vp[2], &i)) | ||
2038 | return JS_FALSE; | ||
2039 | return JS_NewNumberValue(cx, i, vp); | ||
2040 | } | ||
2041 | |||
2042 | static JSBool | ||
2043 | StringsAreUTF8(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, | ||
2044 | jsval *rval) | ||
2045 | { | ||
2046 | *rval = JS_CStringsAreUTF8() ? JSVAL_TRUE : JSVAL_FALSE; | ||
2047 | return JS_TRUE; | ||
2048 | } | ||
2049 | |||
2050 | static JSBool | ||
2051 | StackQuota(JSContext *cx, uintN argc, jsval *vp) | ||
2052 | { | ||
2053 | uint32 n; | ||
2054 | |||
2055 | if (argc == 0) | ||
2056 | return JS_NewNumberValue(cx, (double) gScriptStackQuota, vp); | ||
2057 | if (!JS_ValueToECMAUint32(cx, JS_ARGV(cx, vp)[0], &n)) | ||
2058 | return JS_FALSE; | ||
2059 | gScriptStackQuota = n; | ||
2060 | JS_SetScriptStackQuota(cx, gScriptStackQuota); | ||
2061 | JS_SET_RVAL(cx, vp, JSVAL_VOID); | ||
2062 | return JS_TRUE; | ||
2063 | } | ||
2064 | |||
2065 | static const char* badUTF8 = "...\xC0..."; | ||
2066 | static const char* bigUTF8 = "...\xFB\xBF\xBF\xBF\xBF..."; | ||
2067 | static const jschar badSurrogate[] = { 'A', 'B', 'C', 0xDEEE, 'D', 'E', 0 }; | ||
2068 | |||
2069 | static JSBool | ||
2070 | TestUTF8(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) | ||
2071 | { | ||
2072 | int32 mode = 1; | ||
2073 | jschar chars[20]; | ||
2074 | size_t charsLength = 5; | ||
2075 | char bytes[20]; | ||
2076 | size_t bytesLength = 20; | ||
2077 | if (argc && !JS_ValueToInt32(cx, *argv, &mode)) | ||
2078 | return JS_FALSE; | ||
2079 | |||
2080 | /* The following throw errors if compiled with UTF-8. */ | ||
2081 | switch (mode) { | ||
2082 | /* mode 1: malformed UTF-8 string. */ | ||
2083 | case 1: | ||
2084 | JS_NewStringCopyZ(cx, badUTF8); | ||
2085 | break; | ||
2086 | /* mode 2: big UTF-8 character. */ | ||
2087 | case 2: | ||
2088 | JS_NewStringCopyZ(cx, bigUTF8); | ||
2089 | break; | ||
2090 | /* mode 3: bad surrogate character. */ | ||
2091 | case 3: | ||
2092 | JS_EncodeCharacters(cx, badSurrogate, 6, bytes, &bytesLength); | ||
2093 | break; | ||
2094 | /* mode 4: use a too small buffer. */ | ||
2095 | case 4: | ||
2096 | JS_DecodeBytes(cx, "1234567890", 10, chars, &charsLength); | ||
2097 | break; | ||
2098 | default: | ||
2099 | JS_ReportError(cx, "invalid mode parameter"); | ||
2100 | return JS_FALSE; | ||
2101 | } | ||
2102 | return !JS_IsExceptionPending (cx); | ||
2103 | } | ||
2104 | |||
2105 | static JSBool | ||
2106 | ThrowError(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) | ||
2107 | { | ||
2108 | JS_ReportError(cx, "This is an error"); | ||
2109 | return JS_FALSE; | ||
2110 | } | ||
2111 | |||
2112 | #define LAZY_STANDARD_CLASSES | ||
2113 | |||
2114 | /* A class for easily testing the inner/outer object callbacks. */ | ||
2115 | typedef struct ComplexObject { | ||
2116 | JSBool isInner; | ||
2117 | JSBool frozen; | ||
2118 | JSObject *inner; | ||
2119 | JSObject *outer; | ||
2120 | } ComplexObject; | ||
2121 | |||
2122 | static JSObject * | ||
2123 | split_create_outer(JSContext *cx); | ||
2124 | |||
2125 | static JSObject * | ||
2126 | split_create_inner(JSContext *cx, JSObject *outer); | ||
2127 | |||
2128 | static ComplexObject * | ||
2129 | split_get_private(JSContext *cx, JSObject *obj); | ||
2130 | |||
2131 | static JSBool | ||
2132 | split_addProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) | ||
2133 | { | ||
2134 | ComplexObject *cpx; | ||
2135 | jsid asId; | ||
2136 | |||
2137 | cpx = split_get_private(cx, obj); | ||
2138 | if (!cpx) | ||
2139 | return JS_TRUE; | ||
2140 | if (!cpx->isInner && cpx->inner) { | ||
2141 | /* Make sure to define this property on the inner object. */ | ||
2142 | if (!JS_ValueToId(cx, *vp, &asId)) | ||
2143 | return JS_FALSE; | ||
2144 | return OBJ_DEFINE_PROPERTY(cx, cpx->inner, asId, *vp, NULL, NULL, | ||
2145 | JSPROP_ENUMERATE, NULL); | ||
2146 | } | ||
2147 | return JS_TRUE; | ||
2148 | } | ||
2149 | |||
2150 | static JSBool | ||
2151 | split_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) | ||
2152 | { | ||
2153 | ComplexObject *cpx; | ||
2154 | |||
2155 | cpx = split_get_private(cx, obj); | ||
2156 | if (!cpx) | ||
2157 | return JS_TRUE; | ||
2158 | if (!cpx->isInner && cpx->inner) { | ||
2159 | if (JSVAL_IS_STRING(id)) { | ||
2160 | JSString *str; | ||
2161 | |||
2162 | str = JSVAL_TO_STRING(id); | ||
2163 | return JS_GetUCProperty(cx, cpx->inner, JS_GetStringChars(str), | ||
2164 | JS_GetStringLength(str), vp); | ||
2165 | } | ||
2166 | if (JSVAL_IS_INT(id)) | ||
2167 | return JS_GetElement(cx, cpx->inner, JSVAL_TO_INT(id), vp); | ||
2168 | return JS_TRUE; | ||
2169 | } | ||
2170 | |||
2171 | return JS_TRUE; | ||
2172 | } | ||
2173 | |||
2174 | static JSBool | ||
2175 | split_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) | ||
2176 | { | ||
2177 | ComplexObject *cpx; | ||
2178 | |||
2179 | cpx = split_get_private(cx, obj); | ||
2180 | if (!cpx) | ||
2181 | return JS_TRUE; | ||
2182 | if (!cpx->isInner && cpx->inner) { | ||
2183 | if (JSVAL_IS_STRING(id)) { | ||
2184 | JSString *str; | ||
2185 | |||
2186 | str = JSVAL_TO_STRING(id); | ||
2187 | return JS_SetUCProperty(cx, cpx->inner, JS_GetStringChars(str), | ||
2188 | JS_GetStringLength(str), vp); | ||
2189 | } | ||
2190 | if (JSVAL_IS_INT(id)) | ||
2191 | return JS_SetElement(cx, cpx->inner, JSVAL_TO_INT(id), vp); | ||
2192 | return JS_TRUE; | ||
2193 | } | ||
2194 | |||
2195 | return JS_TRUE; | ||
2196 | } | ||
2197 | |||
2198 | static JSBool | ||
2199 | split_delProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) | ||
2200 | { | ||
2201 | ComplexObject *cpx; | ||
2202 | jsid asId; | ||
2203 | |||
2204 | cpx = split_get_private(cx, obj); | ||
2205 | if (!cpx) | ||
2206 | return JS_TRUE; | ||
2207 | if (!cpx->isInner && cpx->inner) { | ||
2208 | /* Make sure to define this property on the inner object. */ | ||
2209 | if (!JS_ValueToId(cx, *vp, &asId)) | ||
2210 | return JS_FALSE; | ||
2211 | return OBJ_DELETE_PROPERTY(cx, cpx->inner, asId, vp); | ||
2212 | } | ||
2213 | return JS_TRUE; | ||
2214 | } | ||
2215 | |||
2216 | static JSBool | ||
2217 | split_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, | ||
2218 | jsval *statep, jsid *idp) | ||
2219 | { | ||
2220 | ComplexObject *cpx; | ||
2221 | JSObject *iterator; | ||
2222 | |||
2223 | switch (enum_op) { | ||
2224 | case JSENUMERATE_INIT: | ||
2225 | cpx = (ComplexObject *) JS_GetPrivate(cx, obj); | ||
2226 | |||
2227 | if (!cpx->isInner && cpx->inner) | ||
2228 | obj = cpx->inner; | ||
2229 | |||
2230 | iterator = JS_NewPropertyIterator(cx, obj); | ||
2231 | if (!iterator) | ||
2232 | return JS_FALSE; | ||
2233 | |||
2234 | *statep = OBJECT_TO_JSVAL(iterator); | ||
2235 | if (idp) | ||
2236 | *idp = JSVAL_ZERO; | ||
2237 | break; | ||
2238 | |||
2239 | case JSENUMERATE_NEXT: | ||
2240 | iterator = (JSObject*)JSVAL_TO_OBJECT(*statep); | ||
2241 | if (!JS_NextProperty(cx, iterator, idp)) | ||
2242 | return JS_FALSE; | ||
2243 | |||
2244 | if (!JSVAL_IS_VOID(*idp)) | ||
2245 | break; | ||
2246 | /* Fall through. */ | ||
2247 | |||
2248 | case JSENUMERATE_DESTROY: | ||
2249 | /* Let GC at our iterator object. */ | ||
2250 | *statep = JSVAL_NULL; | ||
2251 | break; | ||
2252 | } | ||
2253 | |||
2254 | return JS_TRUE; | ||
2255 | } | ||
2256 | |||
2257 | static JSBool | ||
2258 | split_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, | ||
2259 | JSObject **objp) | ||
2260 | { | ||
2261 | ComplexObject *cpx; | ||
2262 | |||
2263 | cpx = split_get_private(cx, obj); | ||
2264 | if (!cpx) | ||
2265 | return JS_TRUE; | ||
2266 | if (!cpx->isInner && cpx->inner) { | ||
2267 | jsid asId; | ||
2268 | JSProperty *prop; | ||
2269 | |||
2270 | if (!JS_ValueToId(cx, id, &asId)) | ||
2271 | return JS_FALSE; | ||
2272 | |||
2273 | if (!OBJ_LOOKUP_PROPERTY(cx, cpx->inner, asId, objp, &prop)) | ||
2274 | return JS_FALSE; | ||
2275 | if (prop) | ||
2276 | OBJ_DROP_PROPERTY(cx, cpx->inner, prop); | ||
2277 | |||
2278 | return JS_TRUE; | ||
2279 | } | ||
2280 | |||
2281 | #ifdef LAZY_STANDARD_CLASSES | ||
2282 | if (!(flags & JSRESOLVE_ASSIGNING)) { | ||
2283 | JSBool resolved; | ||
2284 | |||
2285 | if (!JS_ResolveStandardClass(cx, obj, id, &resolved)) | ||
2286 | return JS_FALSE; | ||
2287 | |||
2288 | if (resolved) { | ||
2289 | *objp = obj; | ||
2290 | return JS_TRUE; | ||
2291 | } | ||
2292 | } | ||
2293 | #endif | ||
2294 | |||
2295 | /* XXX For additional realism, let's resolve some random property here. */ | ||
2296 | return JS_TRUE; | ||
2297 | } | ||
2298 | |||
2299 | static void | ||
2300 | split_finalize(JSContext *cx, JSObject *obj) | ||
2301 | { | ||
2302 | JS_free(cx, JS_GetPrivate(cx, obj)); | ||
2303 | } | ||
2304 | |||
2305 | static uint32 | ||
2306 | split_mark(JSContext *cx, JSObject *obj, void *arg) | ||
2307 | { | ||
2308 | ComplexObject *cpx; | ||
2309 | |||
2310 | cpx = (ComplexObject *) JS_GetPrivate(cx, obj); | ||
2311 | |||
2312 | if (!cpx->isInner && cpx->inner) { | ||
2313 | /* Mark the inner object. */ | ||
2314 | JS_MarkGCThing(cx, cpx->inner, "ComplexObject.inner", arg); | ||
2315 | } | ||
2316 | |||
2317 | return 0; | ||
2318 | } | ||
2319 | |||
2320 | static JSObject * | ||
2321 | split_outerObject(JSContext *cx, JSObject *obj) | ||
2322 | { | ||
2323 | ComplexObject *cpx; | ||
2324 | |||
2325 | cpx = (ComplexObject *) JS_GetPrivate(cx, obj); | ||
2326 | return cpx->isInner ? cpx->outer : obj; | ||
2327 | } | ||
2328 | |||
2329 | static JSObject * | ||
2330 | split_innerObject(JSContext *cx, JSObject *obj) | ||
2331 | { | ||
2332 | ComplexObject *cpx; | ||
2333 | |||
2334 | cpx = (ComplexObject *) JS_GetPrivate(cx, obj); | ||
2335 | if (cpx->frozen) { | ||
2336 | JS_ASSERT(!cpx->isInner); | ||
2337 | return obj; | ||
2338 | } | ||
2339 | return !cpx->isInner ? cpx->inner : obj; | ||
2340 | } | ||
2341 | |||
2342 | static JSExtendedClass split_global_class = { | ||
2343 | {"split_global", | ||
2344 | JSCLASS_NEW_RESOLVE | JSCLASS_NEW_ENUMERATE | JSCLASS_HAS_PRIVATE | | ||
2345 | JSCLASS_GLOBAL_FLAGS | JSCLASS_IS_EXTENDED, | ||
2346 | split_addProperty, split_delProperty, | ||
2347 | split_getProperty, split_setProperty, | ||
2348 | (JSEnumerateOp)split_enumerate, | ||
2349 | (JSResolveOp)split_resolve, | ||
2350 | JS_ConvertStub, split_finalize, | ||
2351 | NULL, NULL, NULL, NULL, NULL, NULL, | ||
2352 | split_mark, NULL}, | ||
2353 | NULL, split_outerObject, split_innerObject, | ||
2354 | NULL, NULL, NULL, NULL, NULL | ||
2355 | }; | ||
2356 | |||
2357 | JSObject * | ||
2358 | split_create_outer(JSContext *cx) | ||
2359 | { | ||
2360 | ComplexObject *cpx; | ||
2361 | JSObject *obj; | ||
2362 | |||
2363 | cpx = (ComplexObject *) JS_malloc(cx, sizeof *obj); | ||
2364 | if (!cpx) | ||
2365 | return NULL; | ||
2366 | cpx->isInner = JS_FALSE; | ||
2367 | cpx->frozen = JS_TRUE; | ||
2368 | cpx->inner = NULL; | ||
2369 | cpx->outer = NULL; | ||
2370 | |||
2371 | obj = JS_NewObject(cx, &split_global_class.base, NULL, NULL); | ||
2372 | if (!obj || !JS_SetParent(cx, obj, NULL)) { | ||
2373 | JS_free(cx, cpx); | ||
2374 | return NULL; | ||
2375 | } | ||
2376 | |||
2377 | if (!JS_SetPrivate(cx, obj, cpx)) { | ||
2378 | JS_free(cx, cpx); | ||
2379 | return NULL; | ||
2380 | } | ||
2381 | |||
2382 | return obj; | ||
2383 | } | ||
2384 | |||
2385 | static JSObject * | ||
2386 | split_create_inner(JSContext *cx, JSObject *outer) | ||
2387 | { | ||
2388 | ComplexObject *cpx, *outercpx; | ||
2389 | JSObject *obj; | ||
2390 | |||
2391 | JS_ASSERT(JS_GET_CLASS(cx, outer) == &split_global_class.base); | ||
2392 | |||
2393 | cpx = (ComplexObject *) JS_malloc(cx, sizeof *cpx); | ||
2394 | if (!cpx) | ||
2395 | return NULL; | ||
2396 | cpx->isInner = JS_TRUE; | ||
2397 | cpx->frozen = JS_FALSE; | ||
2398 | cpx->inner = NULL; | ||
2399 | cpx->outer = outer; | ||
2400 | |||
2401 | obj = JS_NewObject(cx, &split_global_class.base, NULL, NULL); | ||
2402 | if (!obj || !JS_SetParent(cx, obj, NULL) || !JS_SetPrivate(cx, obj, cpx)) { | ||
2403 | JS_free(cx, cpx); | ||
2404 | return NULL; | ||
2405 | } | ||
2406 | |||
2407 | outercpx = (ComplexObject *) JS_GetPrivate(cx, outer); | ||
2408 | outercpx->inner = obj; | ||
2409 | outercpx->frozen = JS_FALSE; | ||
2410 | |||
2411 | return obj; | ||
2412 | } | ||
2413 | |||
2414 | static ComplexObject * | ||
2415 | split_get_private(JSContext *cx, JSObject *obj) | ||
2416 | { | ||
2417 | do { | ||
2418 | if (JS_GET_CLASS(cx, obj) == &split_global_class.base) | ||
2419 | return (ComplexObject *) JS_GetPrivate(cx, obj); | ||
2420 | obj = JS_GetParent(cx, obj); | ||
2421 | } while (obj); | ||
2422 | |||
2423 | return NULL; | ||
2424 | } | ||
2425 | |||
2426 | static JSBool | ||
2427 | sandbox_enumerate(JSContext *cx, JSObject *obj) | ||
2428 | { | ||
2429 | jsval v; | ||
2430 | JSBool b; | ||
2431 | |||
2432 | if (!JS_GetProperty(cx, obj, "lazy", &v) || !JS_ValueToBoolean(cx, v, &b)) | ||
2433 | return JS_FALSE; | ||
2434 | return !b || JS_EnumerateStandardClasses(cx, obj); | ||
2435 | } | ||
2436 | |||
2437 | static JSBool | ||
2438 | sandbox_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, | ||
2439 | JSObject **objp) | ||
2440 | { | ||
2441 | jsval v; | ||
2442 | JSBool b, resolved; | ||
2443 | |||
2444 | if (!JS_GetProperty(cx, obj, "lazy", &v) || !JS_ValueToBoolean(cx, v, &b)) | ||
2445 | return JS_FALSE; | ||
2446 | if (b && (flags & JSRESOLVE_ASSIGNING) == 0) { | ||
2447 | if (!JS_ResolveStandardClass(cx, obj, id, &resolved)) | ||
2448 | return JS_FALSE; | ||
2449 | if (resolved) { | ||
2450 | *objp = obj; | ||
2451 | return JS_TRUE; | ||
2452 | } | ||
2453 | } | ||
2454 | *objp = NULL; | ||
2455 | return JS_TRUE; | ||
2456 | } | ||
2457 | |||
2458 | static JSClass sandbox_class = { | ||
2459 | "sandbox", | ||
2460 | JSCLASS_NEW_RESOLVE, | ||
2461 | JS_PropertyStub, JS_PropertyStub, | ||
2462 | JS_PropertyStub, JS_PropertyStub, | ||
2463 | sandbox_enumerate, (JSResolveOp)sandbox_resolve, | ||
2464 | JS_ConvertStub, JS_FinalizeStub, | ||
2465 | JSCLASS_NO_OPTIONAL_MEMBERS | ||
2466 | }; | ||
2467 | |||
2468 | static JSBool | ||
2469 | EvalInContext(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, | ||
2470 | jsval *rval) | ||
2471 | { | ||
2472 | JSString *str; | ||
2473 | JSObject *sobj; | ||
2474 | JSContext *scx; | ||
2475 | const jschar *src; | ||
2476 | size_t srclen; | ||
2477 | JSBool lazy, ok; | ||
2478 | jsval v; | ||
2479 | JSStackFrame *fp; | ||
2480 | |||
2481 | sobj = NULL; | ||
2482 | if (!JS_ConvertArguments(cx, argc, argv, "S / o", &str, &sobj)) | ||
2483 | return JS_FALSE; | ||
2484 | |||
2485 | scx = JS_NewContext(JS_GetRuntime(cx), gStackChunkSize); | ||
2486 | if (!scx) { | ||
2487 | JS_ReportOutOfMemory(cx); | ||
2488 | return JS_FALSE; | ||
2489 | } | ||
2490 | |||
2491 | #ifdef JS_THREADSAFE | ||
2492 | JS_BeginRequest(scx); | ||
2493 | #endif | ||
2494 | src = JS_GetStringChars(str); | ||
2495 | srclen = JS_GetStringLength(str); | ||
2496 | lazy = JS_FALSE; | ||
2497 | if (srclen == 4 && | ||
2498 | src[0] == 'l' && src[1] == 'a' && src[2] == 'z' && src[3] == 'y') { | ||
2499 | lazy = JS_TRUE; | ||
2500 | srclen = 0; | ||
2501 | } | ||
2502 | |||
2503 | if (!sobj) { | ||
2504 | sobj = JS_NewObject(scx, &sandbox_class, NULL, NULL); | ||
2505 | if (!sobj || (!lazy && !JS_InitStandardClasses(scx, sobj))) { | ||
2506 | ok = JS_FALSE; | ||
2507 | goto out; | ||
2508 | } | ||
2509 | v = BOOLEAN_TO_JSVAL(lazy); | ||
2510 | ok = JS_SetProperty(cx, sobj, "lazy", &v); | ||
2511 | if (!ok) | ||
2512 | goto out; | ||
2513 | } | ||
2514 | |||
2515 | if (srclen == 0) { | ||
2516 | *rval = OBJECT_TO_JSVAL(sobj); | ||
2517 | ok = JS_TRUE; | ||
2518 | } else { | ||
2519 | fp = JS_GetScriptedCaller(cx, NULL); | ||
2520 | JS_SetGlobalObject(scx, sobj); | ||
2521 | JS_ToggleOptions(scx, JSOPTION_DONT_REPORT_UNCAUGHT); | ||
2522 | ok = JS_EvaluateUCScript(scx, sobj, src, srclen, | ||
2523 | fp->script->filename, | ||
2524 | JS_PCToLineNumber(cx, fp->script, | ||
2525 | fp->regs->pc), | ||
2526 | rval); | ||
2527 | if (!ok) { | ||
2528 | if (JS_GetPendingException(scx, &v)) | ||
2529 | JS_SetPendingException(cx, v); | ||
2530 | else | ||
2531 | JS_ReportOutOfMemory(cx); | ||
2532 | } | ||
2533 | } | ||
2534 | |||
2535 | out: | ||
2536 | #ifdef JS_THREADSAFE | ||
2537 | JS_EndRequest(scx); | ||
2538 | #endif | ||
2539 | JS_DestroyContext(scx); | ||
2540 | return ok; | ||
2541 | } | ||
2542 | |||
2543 | #ifdef JS_THREADSAFE | ||
2544 | |||
2545 | static JSBool | ||
2546 | Sleep(JSContext *cx, uintN argc, jsval *vp) | ||
2547 | { | ||
2548 | jsdouble t_secs; | ||
2549 | PRUint32 t_ticks; | ||
2550 | jsrefcount rc; | ||
2551 | |||
2552 | if (!JS_ValueToNumber(cx, argc == 0 ? JSVAL_VOID : vp[2], &t_secs)) | ||
2553 | return JS_FALSE; | ||
2554 | |||
2555 | if (t_secs < 0 || JSDOUBLE_IS_NaN |