PageRenderTime 1675ms CodeModel.GetById 141ms app.highlight 607ms RepoModel.GetById 145ms app.codeStats 692ms

/js/lib/Socket.IO-node/support/expresso/deps/jscoverage/js/js.cpp

http://github.com/onedayitwillmake/RealtimeMultiplayerNodeJs
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