/js/lib/Socket.IO-node/support/expresso/deps/jscoverage/js/js.cpp
C++ | 2422 lines | 2145 code | 178 blank | 99 comment | 272 complexity | cd90b6c90083454ce65be2119e7ba9a5 MD5 | raw file
Large files files are truncated, but you can click here to view the full file
1/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 2 * vim: set ts=8 sw=4 et tw=99: 3 * 4 * ***** BEGIN LICENSE BLOCK ***** 5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1 6 * 7 * The contents of this file are subject to the Mozilla Public License Version 8 * 1.1 (the "License"); you may not use this file except in compliance with 9 * the License. You may obtain a copy of the License at 10 * http://www.mozilla.org/MPL/ 11 * 12 * Software distributed under the License is distributed on an "AS IS" basis, 13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 14 * for the specific language governing rights and limitations under the 15 * License. 16 * 17 * The Original Code is Mozilla Communicator client code, released 18 * March 31, 1998. 19 * 20 * The Initial Developer of the Original Code is 21 * Netscape Communications Corporation. 22 * Portions created by the Initial Developer are Copyright (C) 1998 23 * the Initial Developer. All Rights Reserved. 24 * 25 * Contributor(s): 26 * 27 * Alternatively, the contents of this file may be used under the terms of 28 * either of the GNU General Public License Version 2 or later (the "GPL"), 29 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), 30 * in which case the provisions of the GPL or the LGPL are applicable instead 31 * of those above. If you wish to allow use of your version of this file only 32 * under the terms of either the GPL or the LGPL, and not to allow others to 33 * use your version of this file under the terms of the MPL, indicate your 34 * decision by deleting the provisions above and replace them with the notice 35 * and other provisions required by the GPL or the LGPL. If you do not delete 36 * the provisions above, a recipient may use your version of this file under 37 * the terms of any one of the MPL, the GPL or the LGPL. 38 * 39 * ***** END LICENSE BLOCK ***** */ 40 41/* 42 * JS shell. 43 */ 44#include "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 "jsbuiltins.h" 58#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 94typedef enum JSShellExitCode { 95 EXITCODE_RUNTIME_ERROR = 3, 96 EXITCODE_FILE_NOT_FOUND = 4, 97 EXITCODE_OUT_OF_MEMORY = 5 98} JSShellExitCode; 99 100size_t gStackChunkSize = 8192; 101 102/* Assume that we can not use more than 5e5 bytes of C stack by default. */ 103static size_t gMaxStackSize = 500000; 104static jsuword gStackBase; 105 106static size_t gScriptStackQuota = JS_DEFAULT_SCRIPT_STACK_QUOTA; 107 108static JSBool gEnableBranchCallback = JS_FALSE; 109static uint32 gBranchCount; 110static uint32 gBranchLimit; 111 112int gExitCode = 0; 113JSBool gQuitting = JS_FALSE; 114FILE *gErrFile = NULL; 115FILE *gOutFile = NULL; 116 117static JSBool reportWarnings = JS_TRUE; 118static JSBool compileOnly = JS_FALSE; 119 120typedef 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 129static const JSErrorFormatString * 130my_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber); 131static JSObject * 132split_setup(JSContext *cx); 133 134#ifdef EDITLINE 135JS_BEGIN_EXTERN_C 136extern char *readline(const char *prompt); 137extern void add_history(char *line); 138JS_END_EXTERN_C 139#endif 140 141static JSBool 142GetLine(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 172static JSBool 173my_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 201static void 202SetContextOptions(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 226static void 227Process(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 334static int 335usage(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 346static 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 360extern JSClass global_class; 361 362static int 363ProcessArgs(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) 467extern struct JSClass jitstats_class; 468extern 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 580static JSBool 581Version(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 590static JSBool 591Options(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 645static JSBool 646Load(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 */ 685static JSBool 686ReadLine(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#ifdef JS_TRACER 764static jsval JS_FASTCALL 765Print_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 777static JSBool 778Print(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 804static JSBool 805Help(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); 806 807static JSBool 808Quit(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 820static JSBool 821GC(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 845static JSBool 846GCParameter(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 890static JSBool 891GCZeal(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 903typedef struct JSCountHeapNode JSCountHeapNode; 904 905struct JSCountHeapNode { 906 void *thing; 907 int32 kind; 908 JSCountHeapNode *next; 909}; 910 911typedef struct JSCountHeapTracer { 912 JSTracer base; 913 JSDHashTable visited; 914 JSBool ok; 915 JSCountHeapNode *traceList; 916 JSCountHeapNode *recycleList; 917} JSCountHeapTracer; 918 919static void 920CountHeapNotify(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 958static JSBool 959CountHeap(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 1055static JSScript * 1056ValueToScript(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 1079static JSBool 1080GetTrapArgs(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 1109static JSTrapStatus 1110TrapHandler(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 1129static JSBool 1130Trap(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 1150static JSBool 1151Untrap(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 1162static JSBool 1163LineToPC(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 1185static JSBool 1186PCToLine(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 1203static void 1204UpdateSwitchTableBounds(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 1251static void 1252SrcNotes(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 1352static JSBool 1353Notes(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 1368JS_STATIC_ASSERT(JSTRY_CATCH == 0); 1369JS_STATIC_ASSERT(JSTRY_FINALLY == 1); 1370JS_STATIC_ASSERT(JSTRY_ITER == 2); 1371 1372static const char* const TryNoteNames[] = { "catch", "finally", "iter" }; 1373 1374static JSBool 1375TryNotes(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 1395static JSBool 1396Disassemble(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 1446static JSBool 1447DisassFile(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 1482static JSBool 1483DisassWithSrc(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 1566static JSBool 1567Tracing(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 1603static void 1604DumpScope(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 1648static JSBool 1649DumpStats(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 1696static JSBool 1697DumpHeap(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 1798static const char * 1799EscapeWideString(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++…
Large files files are truncated, but you can click here to view the full file