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