PageRenderTime 101ms CodeModel.GetById 14ms app.highlight 74ms RepoModel.GetById 1ms app.codeStats 1ms

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

http://github.com/onedayitwillmake/RealtimeMultiplayerNodeJs
C++ | 2044 lines | 1761 code | 132 blank | 151 comment | 168 complexity | 37f3a9e7670e3289ef5fc12c8b095fde 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 sw=4 ts=8 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 bytecode descriptors, disassemblers, and decompilers.
  43 */
  44#include "jsstddef.h"
  45#ifdef HAVE_MEMORY_H
  46#include <memory.h>
  47#endif
  48#include <stdarg.h>
  49#include <stdio.h>
  50#include <stdlib.h>
  51#include <string.h>
  52#include "jstypes.h"
  53#include "jsarena.h" /* Added by JSIFY */
  54#include "jsutil.h" /* Added by JSIFY */
  55#include "jsdtoa.h"
  56#include "jsprf.h"
  57#include "jsapi.h"
  58#include "jsarray.h"
  59#include "jsatom.h"
  60#include "jscntxt.h"
  61#include "jsversion.h"
  62#include "jsdbgapi.h"
  63#include "jsemit.h"
  64#include "jsfun.h"
  65#include "jsiter.h"
  66#include "jsnum.h"
  67#include "jsobj.h"
  68#include "jsopcode.h"
  69#include "jsregexp.h"
  70#include "jsscan.h"
  71#include "jsscope.h"
  72#include "jsscript.h"
  73#include "jsstr.h"
  74#include "jsstaticcheck.h"
  75#include "jstracer.h"
  76
  77#include "jsautooplen.h"
  78
  79/* Verify JSOP_XXX_LENGTH constant definitions. */
  80#define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format)               \
  81    JS_STATIC_ASSERT(op##_LENGTH == length);
  82#include "jsopcode.tbl"
  83#undef OPDEF
  84
  85static const char js_incop_strs[][3] = {"++", "--"};
  86static const char js_for_each_str[]  = "for each";
  87
  88const JSCodeSpec js_CodeSpec[] = {
  89#define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
  90    {length,nuses,ndefs,prec,format},
  91#include "jsopcode.tbl"
  92#undef OPDEF
  93};
  94
  95uintN js_NumCodeSpecs = JS_ARRAY_LENGTH(js_CodeSpec);
  96
  97/*
  98 * Each element of the array is either a source literal associated with JS
  99 * bytecode or null.
 100 */
 101static const char *CodeToken[] = {
 102#define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
 103    token,
 104#include "jsopcode.tbl"
 105#undef OPDEF
 106};
 107
 108#if defined(DEBUG) || defined(JS_JIT_SPEW)
 109/*
 110 * Array of JS bytecode names used by DEBUG-only js_Disassemble and by
 111 * JIT debug spew.
 112 */
 113const char *js_CodeName[] = {
 114#define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
 115    name,
 116#include "jsopcode.tbl"
 117#undef OPDEF
 118};
 119#endif
 120
 121/************************************************************************/
 122
 123static ptrdiff_t
 124GetJumpOffset(jsbytecode *pc, jsbytecode *pc2)
 125{
 126    uint32 type;
 127
 128    type = JOF_OPTYPE(*pc);
 129    if (JOF_TYPE_IS_EXTENDED_JUMP(type))
 130        return GET_JUMPX_OFFSET(pc2);
 131    return GET_JUMP_OFFSET(pc2);
 132}
 133
 134uintN
 135js_GetIndexFromBytecode(JSContext *cx, JSScript *script, jsbytecode *pc,
 136                        ptrdiff_t pcoff)
 137{
 138    JSOp op;
 139    uintN span, base;
 140
 141    op = (JSOp)*pc;
 142    if (op == JSOP_TRAP)
 143        op = JS_GetTrapOpcode(cx, script, pc);
 144    JS_ASSERT(js_CodeSpec[op].length >= 1 + pcoff + UINT16_LEN);
 145
 146    /*
 147     * We need to detect index base prefix. It presents when resetbase
 148     * follows the bytecode.
 149     */
 150    span = js_CodeSpec[op].length;
 151    base = 0;
 152    if (pc - script->code + span < script->length) {
 153        if (pc[span] == JSOP_RESETBASE) {
 154            base = GET_INDEXBASE(pc - JSOP_INDEXBASE_LENGTH);
 155        } else if (pc[span] == JSOP_RESETBASE0) {
 156            JS_ASSERT(JSOP_INDEXBASE1 <= pc[-1] || pc[-1] <= JSOP_INDEXBASE3);
 157            base = (pc[-1] - JSOP_INDEXBASE1 + 1) << 16;
 158        }
 159    }
 160    return base + GET_UINT16(pc + pcoff);
 161}
 162
 163uintN
 164js_GetVariableBytecodeLength(jsbytecode *pc)
 165{
 166    JSOp op;
 167    uintN jmplen, ncases;
 168    jsint low, high;
 169
 170    op = (JSOp) *pc;
 171    JS_ASSERT(js_CodeSpec[op].length == -1);
 172    switch (op) {
 173      case JSOP_TABLESWITCHX:
 174        jmplen = JUMPX_OFFSET_LEN;
 175        goto do_table;
 176      case JSOP_TABLESWITCH:
 177        jmplen = JUMP_OFFSET_LEN;
 178      do_table:
 179        /* Structure: default-jump case-low case-high case1-jump ... */
 180        pc += jmplen;
 181        low = GET_JUMP_OFFSET(pc);
 182        pc += JUMP_OFFSET_LEN;
 183        high = GET_JUMP_OFFSET(pc);
 184        ncases = (uintN)(high - low + 1);
 185        return 1 + jmplen + INDEX_LEN + INDEX_LEN + ncases * jmplen;
 186
 187      case JSOP_LOOKUPSWITCHX:
 188        jmplen = JUMPX_OFFSET_LEN;
 189        goto do_lookup;
 190      default:
 191        JS_ASSERT(op == JSOP_LOOKUPSWITCH);
 192        jmplen = JUMP_OFFSET_LEN;
 193      do_lookup:
 194        /* Structure: default-jump case-count (case1-value case1-jump) ... */
 195        pc += jmplen;
 196        ncases = GET_UINT16(pc);
 197        return 1 + jmplen + INDEX_LEN + ncases * (INDEX_LEN + jmplen);
 198    }
 199}
 200
 201uintN
 202js_GetVariableStackUseLength(JSOp op, jsbytecode *pc)
 203{
 204    JS_ASSERT(*pc == op || *pc == JSOP_TRAP);
 205    JS_ASSERT(js_CodeSpec[op].nuses == -1);
 206    switch (op) {
 207      case JSOP_POPN:
 208        return GET_UINT16(pc);
 209      case JSOP_LEAVEBLOCK:
 210        return GET_UINT16(pc);
 211      case JSOP_LEAVEBLOCKEXPR:
 212        return GET_UINT16(pc) + 1;
 213      case JSOP_NEWARRAY:
 214        return GET_UINT24(pc);
 215      default:
 216        /* stack: fun, this, [argc arguments] */
 217        JS_ASSERT(op == JSOP_NEW || op == JSOP_CALL ||
 218                  op == JSOP_EVAL || op == JSOP_SETCALL ||
 219                  op == JSOP_APPLY);
 220        return 2 + GET_ARGC(pc);
 221    }
 222}
 223
 224#ifdef DEBUG
 225
 226JS_FRIEND_API(JSBool)
 227js_Disassemble(JSContext *cx, JSScript *script, JSBool lines, FILE *fp)
 228{
 229    jsbytecode *pc, *end;
 230    uintN len;
 231
 232    pc = script->code;
 233    end = pc + script->length;
 234    while (pc < end) {
 235        if (pc == script->main)
 236            fputs("main:\n", fp);
 237        len = js_Disassemble1(cx, script, pc,
 238                              PTRDIFF(pc, script->code, jsbytecode),
 239                              lines, fp);
 240        if (!len)
 241            return JS_FALSE;
 242        pc += len;
 243    }
 244    return JS_TRUE;
 245}
 246
 247const char *
 248ToDisassemblySource(JSContext *cx, jsval v)
 249{
 250    JSObject *obj;
 251    JSScopeProperty *sprop;
 252    char *source;
 253    const char *bytes;
 254    JSString *str;
 255
 256    if (!JSVAL_IS_PRIMITIVE(v)) {
 257        obj = JSVAL_TO_OBJECT(v);
 258        if (OBJ_GET_CLASS(cx, obj) == &js_BlockClass) {
 259            source = JS_sprintf_append(NULL, "depth %d {",
 260                                       OBJ_BLOCK_DEPTH(cx, obj));
 261            for (sprop = OBJ_SCOPE(obj)->lastProp; sprop;
 262                 sprop = sprop->parent) {
 263                bytes = js_AtomToPrintableString(cx, JSID_TO_ATOM(sprop->id));
 264                if (!bytes)
 265                    return NULL;
 266                source = JS_sprintf_append(source, "%s: %d%s",
 267                                           bytes, sprop->shortid,
 268                                           sprop->parent ? ", " : "");
 269            }
 270            source = JS_sprintf_append(source, "}");
 271            if (!source)
 272                return NULL;
 273            str = JS_NewString(cx, source, strlen(source));
 274            if (!str)
 275                return NULL;
 276            return js_GetStringBytes(cx, str);
 277        }
 278    }
 279    return js_ValueToPrintableSource(cx, v);
 280}
 281
 282JS_FRIEND_API(uintN)
 283js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc,
 284                uintN loc, JSBool lines, FILE *fp)
 285{
 286    JSOp op;
 287    const JSCodeSpec *cs;
 288    ptrdiff_t len, off, jmplen;
 289    uint32 type;
 290    JSAtom *atom;
 291    uintN index;
 292    JSObject *obj;
 293    jsval v;
 294    const char *bytes;
 295    jsint i;
 296
 297    op = (JSOp)*pc;
 298    if (op >= JSOP_LIMIT) {
 299        char numBuf1[12], numBuf2[12];
 300        JS_snprintf(numBuf1, sizeof numBuf1, "%d", op);
 301        JS_snprintf(numBuf2, sizeof numBuf2, "%d", JSOP_LIMIT);
 302        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
 303                             JSMSG_BYTECODE_TOO_BIG, numBuf1, numBuf2);
 304        return 0;
 305    }
 306    cs = &js_CodeSpec[op];
 307    len = (ptrdiff_t) cs->length;
 308    fprintf(fp, "%05u:", loc);
 309    if (lines)
 310        fprintf(fp, "%4u", JS_PCToLineNumber(cx, script, pc));
 311    fprintf(fp, "  %s", js_CodeName[op]);
 312    type = JOF_TYPE(cs->format);
 313    switch (type) {
 314      case JOF_BYTE:
 315        if (op == JSOP_TRAP) {
 316            op = JS_GetTrapOpcode(cx, script, pc);
 317            len = (ptrdiff_t) js_CodeSpec[op].length;
 318        }
 319        break;
 320
 321      case JOF_JUMP:
 322      case JOF_JUMPX:
 323        off = GetJumpOffset(pc, pc);
 324        fprintf(fp, " %u (%d)", loc + (intN) off, (intN) off);
 325        break;
 326
 327      case JOF_ATOM:
 328      case JOF_OBJECT:
 329      case JOF_REGEXP:
 330        index = js_GetIndexFromBytecode(cx, script, pc, 0);
 331        if (type == JOF_ATOM) {
 332            JS_GET_SCRIPT_ATOM(script, index, atom);
 333            v = ATOM_KEY(atom);
 334        } else {
 335            if (type == JOF_OBJECT)
 336                JS_GET_SCRIPT_OBJECT(script, index, obj);
 337            else
 338                JS_GET_SCRIPT_REGEXP(script, index, obj);
 339            v = OBJECT_TO_JSVAL(obj);
 340        }
 341        bytes = ToDisassemblySource(cx, v);
 342        if (!bytes)
 343            return 0;
 344        fprintf(fp, " %s", bytes);
 345        break;
 346
 347      case JOF_UINT16:
 348        i = (jsint)GET_UINT16(pc);
 349        goto print_int;
 350
 351      case JOF_TABLESWITCH:
 352      case JOF_TABLESWITCHX:
 353      {
 354        jsbytecode *pc2;
 355        jsint i, low, high;
 356
 357        jmplen = (type == JOF_TABLESWITCH) ? JUMP_OFFSET_LEN
 358                                           : JUMPX_OFFSET_LEN;
 359        pc2 = pc;
 360        off = GetJumpOffset(pc, pc2);
 361        pc2 += jmplen;
 362        low = GET_JUMP_OFFSET(pc2);
 363        pc2 += JUMP_OFFSET_LEN;
 364        high = GET_JUMP_OFFSET(pc2);
 365        pc2 += JUMP_OFFSET_LEN;
 366        fprintf(fp, " defaultOffset %d low %d high %d", (intN) off, low, high);
 367        for (i = low; i <= high; i++) {
 368            off = GetJumpOffset(pc, pc2);
 369            fprintf(fp, "\n\t%d: %d", i, (intN) off);
 370            pc2 += jmplen;
 371        }
 372        len = 1 + pc2 - pc;
 373        break;
 374      }
 375
 376      case JOF_LOOKUPSWITCH:
 377      case JOF_LOOKUPSWITCHX:
 378      {
 379        jsbytecode *pc2;
 380        jsatomid npairs;
 381
 382        jmplen = (type == JOF_LOOKUPSWITCH) ? JUMP_OFFSET_LEN
 383                                            : JUMPX_OFFSET_LEN;
 384        pc2 = pc;
 385        off = GetJumpOffset(pc, pc2);
 386        pc2 += jmplen;
 387        npairs = GET_UINT16(pc2);
 388        pc2 += UINT16_LEN;
 389        fprintf(fp, " offset %d npairs %u", (intN) off, (uintN) npairs);
 390        while (npairs) {
 391            JS_GET_SCRIPT_ATOM(script, GET_INDEX(pc2), atom);
 392            pc2 += INDEX_LEN;
 393            off = GetJumpOffset(pc, pc2);
 394            pc2 += jmplen;
 395
 396            bytes = ToDisassemblySource(cx, ATOM_KEY(atom));
 397            if (!bytes)
 398                return 0;
 399            fprintf(fp, "\n\t%s: %d", bytes, (intN) off);
 400            npairs--;
 401        }
 402        len = 1 + pc2 - pc;
 403        break;
 404      }
 405
 406      case JOF_QARG:
 407        fprintf(fp, " %u", GET_ARGNO(pc));
 408        break;
 409
 410      case JOF_LOCAL:
 411        fprintf(fp, " %u", GET_SLOTNO(pc));
 412        break;
 413
 414      case JOF_SLOTATOM:
 415      case JOF_SLOTOBJECT:
 416        fprintf(fp, " %u", GET_SLOTNO(pc));
 417        index = js_GetIndexFromBytecode(cx, script, pc, SLOTNO_LEN);
 418        if (type == JOF_SLOTATOM) {
 419            JS_GET_SCRIPT_ATOM(script, index, atom);
 420            v = ATOM_KEY(atom);
 421        } else {
 422            JS_GET_SCRIPT_OBJECT(script, index, obj);
 423            v = OBJECT_TO_JSVAL(obj);
 424        }
 425        bytes = ToDisassemblySource(cx, v);
 426        if (!bytes)
 427            return 0;
 428        fprintf(fp, " %s", bytes);
 429        break;
 430
 431      case JOF_UINT24:
 432        JS_ASSERT(op == JSOP_UINT24 || op == JSOP_NEWARRAY);
 433        i = (jsint)GET_UINT24(pc);
 434        goto print_int;
 435
 436      case JOF_UINT8:
 437        i = pc[1];
 438        goto print_int;
 439
 440      case JOF_INT8:
 441        i = GET_INT8(pc);
 442        goto print_int;
 443
 444      case JOF_INT32:
 445        JS_ASSERT(op == JSOP_INT32);
 446        i = GET_INT32(pc);
 447      print_int:
 448        fprintf(fp, " %d", i);
 449        break;
 450
 451      default: {
 452        char numBuf[12];
 453        JS_snprintf(numBuf, sizeof numBuf, "%lx", (unsigned long) cs->format);
 454        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
 455                             JSMSG_UNKNOWN_FORMAT, numBuf);
 456        return 0;
 457      }
 458    }
 459    fputs("\n", fp);
 460    return len;
 461}
 462
 463#endif /* DEBUG */
 464
 465/************************************************************************/
 466
 467/*
 468 * Sprintf, but with unlimited and automatically allocated buffering.
 469 */
 470typedef struct Sprinter {
 471    JSContext       *context;       /* context executing the decompiler */
 472    JSArenaPool     *pool;          /* string allocation pool */
 473    char            *base;          /* base address of buffer in pool */
 474    size_t          size;           /* size of buffer allocated at base */
 475    ptrdiff_t       offset;         /* offset of next free char in buffer */
 476} Sprinter;
 477
 478#define INIT_SPRINTER(cx, sp, ap, off) \
 479    ((sp)->context = cx, (sp)->pool = ap, (sp)->base = NULL, (sp)->size = 0,  \
 480     (sp)->offset = off)
 481
 482#define OFF2STR(sp,off) ((sp)->base + (off))
 483#define STR2OFF(sp,str) ((str) - (sp)->base)
 484#define RETRACT(sp,str) ((sp)->offset = STR2OFF(sp, str))
 485
 486static JSBool
 487SprintEnsureBuffer(Sprinter *sp, size_t len)
 488{
 489    ptrdiff_t nb;
 490    char *base;
 491
 492    nb = (sp->offset + len + 1) - sp->size;
 493    if (nb < 0)
 494        return JS_TRUE;
 495    base = sp->base;
 496    if (!base) {
 497        JS_ARENA_ALLOCATE_CAST(base, char *, sp->pool, nb);
 498    } else {
 499        JS_ARENA_GROW_CAST(base, char *, sp->pool, sp->size, nb);
 500    }
 501    if (!base) {
 502        js_ReportOutOfScriptQuota(sp->context);
 503        return JS_FALSE;
 504    }
 505    sp->base = base;
 506    sp->size += nb;
 507    return JS_TRUE;
 508}
 509
 510static ptrdiff_t
 511SprintPut(Sprinter *sp, const char *s, size_t len)
 512{
 513    ptrdiff_t offset;
 514    char *bp;
 515
 516    /* Allocate space for s, including the '\0' at the end. */
 517    if (!SprintEnsureBuffer(sp, len))
 518        return -1;
 519
 520    /* Advance offset and copy s into sp's buffer. */
 521    offset = sp->offset;
 522    sp->offset += len;
 523    bp = sp->base + offset;
 524    memmove(bp, s, len);
 525    bp[len] = 0;
 526    return offset;
 527}
 528
 529static ptrdiff_t
 530SprintCString(Sprinter *sp, const char *s)
 531{
 532    return SprintPut(sp, s, strlen(s));
 533}
 534
 535static ptrdiff_t
 536SprintString(Sprinter *sp, JSString *str)
 537{
 538    jschar *chars;
 539    size_t length, size;
 540    ptrdiff_t offset;
 541
 542    JSSTRING_CHARS_AND_LENGTH(str, chars, length);
 543    if (length == 0)
 544        return sp->offset;
 545
 546    size = js_GetDeflatedStringLength(sp->context, chars, length);
 547    if (size == (size_t)-1 || !SprintEnsureBuffer(sp, size))
 548        return -1;
 549
 550    offset = sp->offset;
 551    sp->offset += size;
 552    js_DeflateStringToBuffer(sp->context, chars, length, sp->base + offset,
 553                             &size);
 554    sp->base[sp->offset] = 0;
 555    return offset;
 556}
 557
 558
 559static ptrdiff_t
 560Sprint(Sprinter *sp, const char *format, ...)
 561{
 562    va_list ap;
 563    char *bp;
 564    ptrdiff_t offset;
 565
 566    va_start(ap, format);
 567    bp = JS_vsmprintf(format, ap);      /* XXX vsaprintf */
 568    va_end(ap);
 569    if (!bp) {
 570        JS_ReportOutOfMemory(sp->context);
 571        return -1;
 572    }
 573    offset = SprintCString(sp, bp);
 574    free(bp);
 575    return offset;
 576}
 577
 578const char js_EscapeMap[] = {
 579    '\b', 'b',
 580    '\f', 'f',
 581    '\n', 'n',
 582    '\r', 'r',
 583    '\t', 't',
 584    '\v', 'v',
 585    '"',  '"',
 586    '\'', '\'',
 587    '\\', '\\',
 588    '\0', '0'
 589};
 590
 591#define DONT_ESCAPE     0x10000
 592
 593static char *
 594QuoteString(Sprinter *sp, JSString *str, uint32 quote)
 595{
 596    JSBool dontEscape, ok;
 597    jschar qc, c;
 598    ptrdiff_t off, len;
 599    const jschar *s, *t, *z;
 600    const char *e;
 601    char *bp;
 602
 603    /* Sample off first for later return value pointer computation. */
 604    dontEscape = (quote & DONT_ESCAPE) != 0;
 605    qc = (jschar) quote;
 606    off = sp->offset;
 607    if (qc && Sprint(sp, "%c", (char)qc) < 0)
 608        return NULL;
 609
 610    /* Loop control variables: z points at end of string sentinel. */
 611    JSSTRING_CHARS_AND_END(str, s, z);
 612    for (t = s; t < z; s = ++t) {
 613        /* Move t forward from s past un-quote-worthy characters. */
 614        c = *t;
 615        while (JS_ISPRINT(c) && c != qc && c != '\\' && c != '\t' &&
 616               !(c >> 8)) {
 617            c = *++t;
 618            if (t == z)
 619                break;
 620        }
 621        len = PTRDIFF(t, s, jschar);
 622
 623        /* Allocate space for s, including the '\0' at the end. */
 624        if (!SprintEnsureBuffer(sp, len))
 625            return NULL;
 626
 627        /* Advance sp->offset and copy s into sp's buffer. */
 628        bp = sp->base + sp->offset;
 629        sp->offset += len;
 630        while (--len >= 0)
 631            *bp++ = (char) *s++;
 632        *bp = '\0';
 633
 634        if (t == z)
 635            break;
 636
 637        /* Use js_EscapeMap, \u, or \x only if necessary. */
 638        if (!(c >> 8) && (e = strchr(js_EscapeMap, (int)c)) != NULL) {
 639            ok = dontEscape
 640                 ? Sprint(sp, "%c", (char)c) >= 0
 641                 : Sprint(sp, "\\%c", e[1]) >= 0;
 642        } else {
 643            ok = Sprint(sp, (c >> 8) ? "\\u%04X" : "\\x%02X", c) >= 0;
 644        }
 645        if (!ok)
 646            return NULL;
 647    }
 648
 649    /* Sprint the closing quote and return the quoted string. */
 650    if (qc && Sprint(sp, "%c", (char)qc) < 0)
 651        return NULL;
 652
 653    /*
 654     * If we haven't Sprint'd anything yet, Sprint an empty string so that
 655     * the OFF2STR below gives a valid result.
 656     */
 657    if (off == sp->offset && Sprint(sp, "") < 0)
 658        return NULL;
 659    return OFF2STR(sp, off);
 660}
 661
 662JSString *
 663js_QuoteString(JSContext *cx, JSString *str, jschar quote)
 664{
 665    void *mark;
 666    Sprinter sprinter;
 667    char *bytes;
 668    JSString *escstr;
 669
 670    mark = JS_ARENA_MARK(&cx->tempPool);
 671    INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0);
 672    bytes = QuoteString(&sprinter, str, quote);
 673    escstr = bytes ? JS_NewStringCopyZ(cx, bytes) : NULL;
 674    JS_ARENA_RELEASE(&cx->tempPool, mark);
 675    return escstr;
 676}
 677
 678/************************************************************************/
 679
 680struct JSPrinter {
 681    Sprinter        sprinter;       /* base class state */
 682    JSArenaPool     pool;           /* string allocation pool */
 683    uintN           indent;         /* indentation in spaces */
 684    JSPackedBool    pretty;         /* pretty-print: indent, use newlines */
 685    JSPackedBool    grouped;        /* in parenthesized expression context */
 686    JSScript        *script;        /* script being printed */
 687    jsbytecode      *dvgfence;      /* DecompileExpression fencepost */
 688    jsbytecode      **pcstack;      /* DecompileExpression modeled stack */
 689    JSFunction      *fun;           /* interpreted function */
 690    jsuword         *localNames;    /* argument and variable names */
 691};
 692
 693/*
 694 * Hack another flag, a la JS_DONT_PRETTY_PRINT, into uintN indent parameters
 695 * to functions such as js_DecompileFunction and js_NewPrinter.  This time, as
 696 * opposed to JS_DONT_PRETTY_PRINT back in the dark ages, we can assume that a
 697 * uintN is at least 32 bits.
 698 */
 699#define JS_IN_GROUP_CONTEXT 0x10000
 700
 701JSPrinter *
 702JS_NEW_PRINTER(JSContext *cx, const char *name, JSFunction *fun,
 703               uintN indent, JSBool pretty)
 704{
 705    JSPrinter *jp;
 706
 707    jp = (JSPrinter *) JS_malloc(cx, sizeof(JSPrinter));
 708    if (!jp)
 709        return NULL;
 710    INIT_SPRINTER(cx, &jp->sprinter, &jp->pool, 0);
 711    JS_INIT_ARENA_POOL(&jp->pool, name, 256, 1, &cx->scriptStackQuota);
 712    jp->indent = indent & ~JS_IN_GROUP_CONTEXT;
 713    jp->pretty = pretty;
 714    jp->grouped = (indent & JS_IN_GROUP_CONTEXT) != 0;
 715    jp->script = NULL;
 716    jp->dvgfence = NULL;
 717    jp->pcstack = NULL;
 718    jp->fun = fun;
 719    jp->localNames = NULL;
 720    if (fun && FUN_INTERPRETED(fun) && JS_GET_LOCAL_NAME_COUNT(fun)) {
 721        jp->localNames = js_GetLocalNameArray(cx, fun, &jp->pool);
 722        if (!jp->localNames) {
 723            js_DestroyPrinter(jp);
 724            return NULL;
 725        }
 726    }
 727    return jp;
 728}
 729
 730void
 731js_DestroyPrinter(JSPrinter *jp)
 732{
 733    JS_FinishArenaPool(&jp->pool);
 734    JS_free(jp->sprinter.context, jp);
 735}
 736
 737JSString *
 738js_GetPrinterOutput(JSPrinter *jp)
 739{
 740    JSContext *cx;
 741    JSString *str;
 742
 743    cx = jp->sprinter.context;
 744    if (!jp->sprinter.base)
 745        return cx->runtime->emptyString;
 746    str = JS_NewStringCopyZ(cx, jp->sprinter.base);
 747    if (!str)
 748        return NULL;
 749    JS_FreeArenaPool(&jp->pool);
 750    INIT_SPRINTER(cx, &jp->sprinter, &jp->pool, 0);
 751    return str;
 752}
 753
 754/*
 755 * NB: Indexed by SRC_DECL_* defines from jsemit.h.
 756 */
 757static const char * const var_prefix[] = {"var ", "const ", "let "};
 758
 759static const char *
 760VarPrefix(jssrcnote *sn)
 761{
 762    if (sn && (SN_TYPE(sn) == SRC_DECL || SN_TYPE(sn) == SRC_GROUPASSIGN)) {
 763        ptrdiff_t type = js_GetSrcNoteOffset(sn, 0);
 764        if ((uintN)type <= SRC_DECL_LET)
 765            return var_prefix[type];
 766    }
 767    return "";
 768}
 769
 770int
 771js_printf(JSPrinter *jp, const char *format, ...)
 772{
 773    va_list ap;
 774    char *bp, *fp;
 775    int cc;
 776
 777    if (*format == '\0')
 778        return 0;
 779
 780    va_start(ap, format);
 781
 782    /* If pretty-printing, expand magic tab into a run of jp->indent spaces. */
 783    if (*format == '\t') {
 784        format++;
 785        if (jp->pretty && Sprint(&jp->sprinter, "%*s", jp->indent, "") < 0)
 786            return -1;
 787    }
 788
 789    /* Suppress newlines (must be once per format, at the end) if not pretty. */
 790    fp = NULL;
 791    if (!jp->pretty && format[cc = strlen(format) - 1] == '\n') {
 792        fp = JS_strdup(jp->sprinter.context, format);
 793        if (!fp)
 794            return -1;
 795        fp[cc] = '\0';
 796        format = fp;
 797    }
 798
 799    /* Allocate temp space, convert format, and put. */
 800    bp = JS_vsmprintf(format, ap);      /* XXX vsaprintf */
 801    if (fp) {
 802        JS_free(jp->sprinter.context, fp);
 803        format = NULL;
 804    }
 805    if (!bp) {
 806        JS_ReportOutOfMemory(jp->sprinter.context);
 807        return -1;
 808    }
 809
 810    cc = strlen(bp);
 811    if (SprintPut(&jp->sprinter, bp, (size_t)cc) < 0)
 812        cc = -1;
 813    free(bp);
 814
 815    va_end(ap);
 816    return cc;
 817}
 818
 819JSBool
 820js_puts(JSPrinter *jp, const char *s)
 821{
 822    return SprintCString(&jp->sprinter, s) >= 0;
 823}
 824
 825/************************************************************************/
 826
 827typedef struct SprintStack {
 828    Sprinter    sprinter;       /* sprinter for postfix to infix buffering */
 829    ptrdiff_t   *offsets;       /* stack of postfix string offsets */
 830    jsbytecode  *opcodes;       /* parallel stack of JS opcodes */
 831    uintN       top;            /* top of stack index */
 832    uintN       inArrayInit;    /* array initialiser/comprehension level */
 833    JSBool      inGenExp;       /* in generator expression */
 834    JSPrinter   *printer;       /* permanent output goes here */
 835} SprintStack;
 836
 837/*
 838 * Find the depth of the operand stack when the interpreter reaches the given
 839 * pc in script. pcstack must have space for least script->depth elements. On
 840 * return it will contain pointers to opcodes that populated the interpreter's
 841 * current operand stack.
 842 *
 843 * This function cannot raise an exception or error. However, due to a risk of
 844 * potential bugs when modeling the stack, the function returns -1 if it
 845 * detects an inconsistency in the model. Such an inconsistency triggers an
 846 * assert in a debug build.
 847 */
 848static intN
 849ReconstructPCStack(JSContext *cx, JSScript *script, jsbytecode *pc,
 850                   jsbytecode **pcstack);
 851
 852#define FAILED_EXPRESSION_DECOMPILER ((char *) 1)
 853
 854/*
 855 * Decompile a part of expression up to the given pc. The function returns
 856 * NULL on out-of-memory, or the FAILED_EXPRESSION_DECOMPILER sentinel when
 857 * the decompiler fails due to a bug and/or unimplemented feature, or the
 858 * decompiled string on success.
 859 */
 860static char *
 861DecompileExpression(JSContext *cx, JSScript *script, JSFunction *fun,
 862                    jsbytecode *pc);
 863
 864/*
 865 * Get a stacked offset from ss->sprinter.base, or if the stacked value |off|
 866 * is negative, fetch the generating pc from printer->pcstack[-2 - off] and
 867 * decompile the code that generated the missing value.  This is used when
 868 * reporting errors, where the model stack will lack |pcdepth| non-negative
 869 * offsets (see DecompileExpression and DecompileCode).
 870 *
 871 * If the stacked offset is -1, return 0 to index the NUL padding at the start
 872 * of ss->sprinter.base.  If this happens, it means there is a decompiler bug
 873 * to fix, but it won't violate memory safety.
 874 */
 875static ptrdiff_t
 876GetOff(SprintStack *ss, uintN i)
 877{
 878    ptrdiff_t off;
 879    jsbytecode *pc;
 880    char *bytes;
 881
 882    off = ss->offsets[i];
 883    if (off >= 0)
 884        return off;
 885
 886    JS_ASSERT(off <= -2);
 887    JS_ASSERT(ss->printer->pcstack);
 888    if (off < -2 && ss->printer->pcstack) {
 889        pc = ss->printer->pcstack[-2 - off];
 890        bytes = DecompileExpression(ss->sprinter.context, ss->printer->script,
 891                                    ss->printer->fun, pc);
 892        if (!bytes)
 893            return 0;
 894        if (bytes != FAILED_EXPRESSION_DECOMPILER) {
 895            off = SprintCString(&ss->sprinter, bytes);
 896            if (off < 0)
 897                off = 0;
 898            ss->offsets[i] = off;
 899            JS_free(ss->sprinter.context, bytes);
 900            return off;
 901        }
 902        if (!ss->sprinter.base && SprintPut(&ss->sprinter, "", 0) >= 0) {
 903            memset(ss->sprinter.base, 0, ss->sprinter.offset);
 904            ss->offsets[i] = -1;
 905        }
 906    }
 907    return 0;
 908}
 909
 910static const char *
 911GetStr(SprintStack *ss, uintN i)
 912{
 913    ptrdiff_t off;
 914
 915    /*
 916     * Must call GetOff before using ss->sprinter.base, since it may be null
 917     * until bootstrapped by GetOff.
 918     */
 919    off = GetOff(ss, i);
 920    return OFF2STR(&ss->sprinter, off);
 921}
 922
 923/*
 924 * Gap between stacked strings to allow for insertion of parens and commas
 925 * when auto-parenthesizing expressions and decompiling array initialisers
 926 * (see the JSOP_NEWARRAY case in Decompile).
 927 */
 928#define PAREN_SLOP      (2 + 1)
 929
 930/*
 931 * These pseudo-ops help js_DecompileValueGenerator decompile JSOP_SETNAME,
 932 * JSOP_SETPROP, and JSOP_SETELEM, respectively.  They are never stored in
 933 * bytecode, so they don't preempt valid opcodes.
 934 */
 935#define JSOP_GETPROP2   256
 936#define JSOP_GETELEM2   257
 937
 938static void
 939AddParenSlop(SprintStack *ss)
 940{
 941    memset(OFF2STR(&ss->sprinter, ss->sprinter.offset), 0, PAREN_SLOP);
 942    ss->sprinter.offset += PAREN_SLOP;
 943}
 944
 945static JSBool
 946PushOff(SprintStack *ss, ptrdiff_t off, JSOp op)
 947{
 948    uintN top;
 949
 950    if (!SprintEnsureBuffer(&ss->sprinter, PAREN_SLOP))
 951        return JS_FALSE;
 952
 953    /* ss->top points to the next free slot; be paranoid about overflow. */
 954    top = ss->top;
 955    JS_ASSERT(top < StackDepth(ss->printer->script));
 956    if (top >= StackDepth(ss->printer->script)) {
 957        JS_ReportOutOfMemory(ss->sprinter.context);
 958        return JS_FALSE;
 959    }
 960
 961    /* The opcodes stack must contain real bytecodes that index js_CodeSpec. */
 962    ss->offsets[top] = off;
 963    ss->opcodes[top] = (op == JSOP_GETPROP2) ? JSOP_GETPROP
 964                     : (op == JSOP_GETELEM2) ? JSOP_GETELEM
 965                     : (jsbytecode) op;
 966    ss->top = ++top;
 967    AddParenSlop(ss);
 968    return JS_TRUE;
 969}
 970
 971static ptrdiff_t
 972PopOffPrec(SprintStack *ss, uint8 prec)
 973{
 974    uintN top;
 975    const JSCodeSpec *topcs;
 976    ptrdiff_t off;
 977
 978    /* ss->top points to the next free slot; be paranoid about underflow. */
 979    top = ss->top;
 980    JS_ASSERT(top != 0);
 981    if (top == 0)
 982        return 0;
 983
 984    ss->top = --top;
 985    off = GetOff(ss, top);
 986    topcs = &js_CodeSpec[ss->opcodes[top]];
 987    if (topcs->prec != 0 && topcs->prec < prec) {
 988        ss->sprinter.offset = ss->offsets[top] = off - 2;
 989        off = Sprint(&ss->sprinter, "(%s)", OFF2STR(&ss->sprinter, off));
 990    } else {
 991        ss->sprinter.offset = off;
 992    }
 993    return off;
 994}
 995
 996static const char *
 997PopStrPrec(SprintStack *ss, uint8 prec)
 998{
 999    ptrdiff_t off;
1000
1001    off = PopOffPrec(ss, prec);
1002    return OFF2STR(&ss->sprinter, off);
1003}
1004
1005static ptrdiff_t
1006PopOff(SprintStack *ss, JSOp op)
1007{
1008    return PopOffPrec(ss, js_CodeSpec[op].prec);
1009}
1010
1011static const char *
1012PopStr(SprintStack *ss, JSOp op)
1013{
1014    return PopStrPrec(ss, js_CodeSpec[op].prec);
1015}
1016
1017typedef struct TableEntry {
1018    jsval       key;
1019    ptrdiff_t   offset;
1020    JSAtom      *label;
1021    jsint       order;          /* source order for stable tableswitch sort */
1022} TableEntry;
1023
1024static JSBool
1025CompareOffsets(void *arg, const void *v1, const void *v2, int *result)
1026{
1027    ptrdiff_t offset_diff;
1028    const TableEntry *te1 = (const TableEntry *) v1,
1029                     *te2 = (const TableEntry *) v2;
1030
1031    offset_diff = te1->offset - te2->offset;
1032    *result = (offset_diff == 0 ? te1->order - te2->order
1033               : offset_diff < 0 ? -1
1034               : 1);
1035    return JS_TRUE;
1036}
1037
1038static ptrdiff_t
1039SprintDoubleValue(Sprinter *sp, jsval v, JSOp *opp)
1040{
1041    jsdouble d;
1042    ptrdiff_t todo;
1043    char *s, buf[DTOSTR_STANDARD_BUFFER_SIZE];
1044
1045    JS_ASSERT(JSVAL_IS_DOUBLE(v));
1046    d = *JSVAL_TO_DOUBLE(v);
1047    if (JSDOUBLE_IS_NEGZERO(d)) {
1048        todo = SprintCString(sp, "-0");
1049        *opp = JSOP_NEG;
1050    } else if (!JSDOUBLE_IS_FINITE(d)) {
1051        /* Don't use Infinity and NaN, they're mutable. */
1052        todo = SprintCString(sp,
1053                             JSDOUBLE_IS_NaN(d)
1054                             ? "0 / 0"
1055                             : (d < 0)
1056                             ? "1 / -0"
1057                             : "1 / 0");
1058        *opp = JSOP_DIV;
1059    } else {
1060        s = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD, 0, d);
1061        if (!s) {
1062            JS_ReportOutOfMemory(sp->context);
1063            return -1;
1064        }
1065        JS_ASSERT(strcmp(s, js_Infinity_str) &&
1066                  (*s != '-' ||
1067                   strcmp(s + 1, js_Infinity_str)) &&
1068                  strcmp(s, js_NaN_str));
1069        todo = Sprint(sp, s);
1070    }
1071    return todo;
1072}
1073
1074static jsbytecode *
1075Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop);
1076
1077static JSBool
1078DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength,
1079                jsbytecode *pc, ptrdiff_t switchLength,
1080                ptrdiff_t defaultOffset, JSBool isCondSwitch)
1081{
1082    JSContext *cx;
1083    JSPrinter *jp;
1084    ptrdiff_t off, off2, diff, caseExprOff, todo;
1085    char *lval, *rval;
1086    uintN i;
1087    jsval key;
1088    JSString *str;
1089
1090    cx = ss->sprinter.context;
1091    jp = ss->printer;
1092
1093    /* JSOP_CONDSWITCH doesn't pop, unlike JSOP_{LOOKUP,TABLE}SWITCH. */
1094    off = isCondSwitch ? GetOff(ss, ss->top-1) : PopOff(ss, JSOP_NOP);
1095    lval = OFF2STR(&ss->sprinter, off);
1096
1097    js_printf(jp, "\tswitch (%s) {\n", lval);
1098
1099    if (tableLength) {
1100        diff = table[0].offset - defaultOffset;
1101        if (diff > 0) {
1102            jp->indent += 2;
1103            js_printf(jp, "\t%s:\n", js_default_str);
1104            jp->indent += 2;
1105            if (!Decompile(ss, pc + defaultOffset, diff, JSOP_NOP))
1106                return JS_FALSE;
1107            jp->indent -= 4;
1108        }
1109
1110        caseExprOff = isCondSwitch ? JSOP_CONDSWITCH_LENGTH : 0;
1111
1112        for (i = 0; i < tableLength; i++) {
1113            off = table[i].offset;
1114            off2 = (i + 1 < tableLength) ? table[i + 1].offset : switchLength;
1115
1116            key = table[i].key;
1117            if (isCondSwitch) {
1118                ptrdiff_t nextCaseExprOff;
1119
1120                /*
1121                 * key encodes the JSOP_CASE bytecode's offset from switchtop.
1122                 * The next case expression follows immediately, unless we are
1123                 * at the last case.
1124                 */
1125                nextCaseExprOff = (ptrdiff_t)JSVAL_TO_INT(key);
1126                nextCaseExprOff += js_CodeSpec[pc[nextCaseExprOff]].length;
1127                jp->indent += 2;
1128                if (!Decompile(ss, pc + caseExprOff,
1129                               nextCaseExprOff - caseExprOff, JSOP_NOP)) {
1130                    return JS_FALSE;
1131                }
1132                caseExprOff = nextCaseExprOff;
1133
1134                /* Balance the stack as if this JSOP_CASE matched. */
1135                --ss->top;
1136            } else {
1137                /*
1138                 * key comes from an atom, not the decompiler, so we need to
1139                 * quote it if it's a string literal.  But if table[i].label
1140                 * is non-null, key was constant-propagated and label is the
1141                 * name of the const we should show as the case label.  We set
1142                 * key to undefined so this identifier is escaped, if required
1143                 * by non-ASCII characters, but not quoted, by QuoteString.
1144                 */
1145                todo = -1;
1146                if (table[i].label) {
1147                    str = ATOM_TO_STRING(table[i].label);
1148                    key = JSVAL_VOID;
1149                } else if (JSVAL_IS_DOUBLE(key)) {
1150                    JSOp junk;
1151
1152                    todo = SprintDoubleValue(&ss->sprinter, key, &junk);
1153                    str = NULL;
1154                } else {
1155                    str = js_ValueToString(cx, key);
1156                    if (!str)
1157                        return JS_FALSE;
1158                }
1159                if (todo >= 0) {
1160                    rval = OFF2STR(&ss->sprinter, todo);
1161                } else {
1162                    rval = QuoteString(&ss->sprinter, str, (jschar)
1163                                       (JSVAL_IS_STRING(key) ? '"' : 0));
1164                    if (!rval)
1165                        return JS_FALSE;
1166                }
1167                RETRACT(&ss->sprinter, rval);
1168                jp->indent += 2;
1169                js_printf(jp, "\tcase %s:\n", rval);
1170            }
1171
1172            jp->indent += 2;
1173            if (off <= defaultOffset && defaultOffset < off2) {
1174                diff = defaultOffset - off;
1175                if (diff != 0) {
1176                    if (!Decompile(ss, pc + off, diff, JSOP_NOP))
1177                        return JS_FALSE;
1178                    off = defaultOffset;
1179                }
1180                jp->indent -= 2;
1181                js_printf(jp, "\t%s:\n", js_default_str);
1182                jp->indent += 2;
1183            }
1184            if (!Decompile(ss, pc + off, off2 - off, JSOP_NOP))
1185                return JS_FALSE;
1186            jp->indent -= 4;
1187
1188            /* Re-balance as if last JSOP_CASE or JSOP_DEFAULT mismatched. */
1189            if (isCondSwitch)
1190                ++ss->top;
1191        }
1192    }
1193
1194    if (defaultOffset == switchLength) {
1195        jp->indent += 2;
1196        js_printf(jp, "\t%s:;\n", js_default_str);
1197        jp->indent -= 2;
1198    }
1199    js_printf(jp, "\t}\n");
1200
1201    /* By the end of a JSOP_CONDSWITCH, the discriminant has been popped. */
1202    if (isCondSwitch)
1203        --ss->top;
1204    return JS_TRUE;
1205}
1206
1207#define LOCAL_ASSERT_CUSTOM(expr, BAD_EXIT)                                   \
1208    JS_BEGIN_MACRO                                                            \
1209        JS_ASSERT(expr);                                                      \
1210        if (!(expr)) { BAD_EXIT; }                                            \
1211    JS_END_MACRO
1212
1213#define LOCAL_ASSERT_RV(expr, rv)                                             \
1214    LOCAL_ASSERT_CUSTOM(expr, return (rv))
1215
1216static JSAtom *
1217GetArgOrVarAtom(JSPrinter *jp, uintN slot)
1218{
1219    JSAtom *name;
1220
1221    LOCAL_ASSERT_RV(jp->fun, NULL);
1222    LOCAL_ASSERT_RV(slot < (uintN) JS_GET_LOCAL_NAME_COUNT(jp->fun), NULL);
1223    name = JS_LOCAL_NAME_TO_ATOM(jp->localNames[slot]);
1224#if !JS_HAS_DESTRUCTURING
1225    LOCAL_ASSERT_RV(name, NULL);
1226#endif
1227    return name;
1228}
1229
1230const char *
1231GetLocal(SprintStack *ss, jsint i)
1232{
1233    ptrdiff_t off;
1234    JSContext *cx;
1235    JSScript *script;
1236    jsatomid j, n;
1237    JSAtom *atom;
1238    JSObject *obj;
1239    jsint depth, count;
1240    JSScopeProperty *sprop;
1241    const char *rval;
1242
1243#define LOCAL_ASSERT(expr)      LOCAL_ASSERT_RV(expr, "")
1244
1245    off = ss->offsets[i];
1246    if (off >= 0)
1247        return OFF2STR(&ss->sprinter, off);
1248
1249    /*
1250     * We must be called from js_DecompileValueGenerator (via Decompile) when
1251     * dereferencing a local that's undefined or null. Search script->objects
1252     * for the block containing this local by its stack index, i.
1253     */
1254    cx = ss->sprinter.context;
1255    script = ss->printer->script;
1256    LOCAL_ASSERT(script->objectsOffset != 0);
1257    for (j = 0, n = JS_SCRIPT_OBJECTS(script)->length; ; j++) {
1258        LOCAL_ASSERT(j < n);
1259        JS_GET_SCRIPT_OBJECT(script, j, obj);
1260        if (OBJ_GET_CLASS(cx, obj) == &js_BlockClass) {
1261            depth = OBJ_BLOCK_DEPTH(cx, obj);
1262            count = OBJ_BLOCK_COUNT(cx, obj);
1263            if ((jsuint)(i - depth) < (jsuint)count)
1264                break;
1265        }
1266    }
1267
1268    i -= depth;
1269    for (sprop = OBJ_SCOPE(obj)->lastProp; sprop; sprop = sprop->parent) {
1270        if (sprop->shortid == i)
1271            break;
1272    }
1273
1274    LOCAL_ASSERT(sprop && JSID_IS_ATOM(sprop->id));
1275    atom = JSID_TO_ATOM(sprop->id);
1276    rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
1277    if (!rval)
1278        return NULL;
1279    RETRACT(&ss->sprinter, rval);
1280    return rval;
1281
1282#undef LOCAL_ASSERT
1283}
1284
1285static JSBool
1286IsVarSlot(JSPrinter *jp, jsbytecode *pc, jsint *indexp)
1287{
1288    uintN slot;
1289
1290    slot = GET_SLOTNO(pc);
1291    if (slot < jp->script->nfixed) {
1292        /* The slot refers to a variable with name stored in jp->localNames. */
1293        *indexp = jp->fun->nargs + slot;
1294        return JS_TRUE;
1295    }
1296
1297    /* We have a local which index is relative to the stack base. */
1298    slot -= jp->script->nfixed;
1299    JS_ASSERT(slot < StackDepth(jp->script));
1300    *indexp = slot;
1301    return JS_FALSE;
1302}
1303
1304#if JS_HAS_DESTRUCTURING
1305
1306#define LOCAL_ASSERT(expr)  LOCAL_ASSERT_RV(expr, NULL)
1307#define LOAD_OP_DATA(pc)    (oplen = (cs = &js_CodeSpec[op=(JSOp)*pc])->length)
1308
1309static jsbytecode *
1310DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc);
1311
1312static jsbytecode *
1313DecompileDestructuringLHS(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc,
1314                          JSBool *hole)
1315{
1316    JSContext *cx;
1317    JSPrinter *jp;
1318    JSOp op;
1319    const JSCodeSpec *cs;
1320    uintN oplen;
1321    jsint i;
1322    const char *lval, *xval;
1323    ptrdiff_t todo;
1324    JSAtom *atom;
1325
1326    *hole = JS_FALSE;
1327    cx = ss->sprinter.context;
1328    jp = ss->printer;
1329    LOAD_OP_DATA(pc);
1330
1331    switch (op) {
1332      case JSOP_POP:
1333        *hole = JS_TRUE;
1334        todo = SprintPut(&ss->sprinter, ", ", 2);
1335        break;
1336
1337      case JSOP_DUP:
1338        pc = DecompileDestructuring(ss, pc, endpc);
1339        if (!pc)
1340            return NULL;
1341        if (pc == endpc)
1342            return pc;
1343        LOAD_OP_DATA(pc);
1344        lval = PopStr(ss, JSOP_NOP);
1345        todo = SprintCString(&ss->sprinter, lval);
1346        if (op == JSOP_POPN)
1347            return pc;
1348        LOCAL_ASSERT(*pc == JSOP_POP);
1349        break;
1350
1351      case JSOP_SETARG:
1352      case JSOP_SETGVAR:
1353      case JSOP_SETLOCAL:
1354        LOCAL_ASSERT(pc[oplen] == JSOP_POP || pc[oplen] == JSOP_POPN);
1355        /* FALL THROUGH */
1356
1357      case JSOP_SETLOCALPOP:
1358        atom = NULL;
1359        lval = NULL;
1360        if (op == JSOP_SETARG) {
1361            atom = GetArgOrVarAtom(jp, GET_SLOTNO(pc));
1362            LOCAL_ASSERT(atom);
1363        } else if (op == JSOP_SETGVAR) {
1364            GET_ATOM_FROM_BYTECODE(jp->script, pc, 0, atom);
1365        } else if (IsVarSlot(jp, pc, &i)) {
1366            atom = GetArgOrVarAtom(jp, i);
1367            LOCAL_ASSERT(atom);
1368        } else {
1369            lval = GetLocal(ss, i);
1370        }
1371        if (atom)
1372            lval = js_AtomToPrintableString(cx, atom);
1373        LOCAL_ASSERT(lval);
1374        todo = SprintCString(&ss->sprinter, lval);
1375        if (op != JSOP_SETLOCALPOP) {
1376            pc += oplen;
1377            if (pc == endpc)
1378                return pc;
1379            LOAD_OP_DATA(pc);
1380            if (op == JSOP_POPN)
1381                return pc;
1382            LOCAL_ASSERT(op == JSOP_POP);
1383        }
1384        break;
1385
1386      default:
1387        /*
1388         * We may need to auto-parenthesize the left-most value decompiled
1389         * here, so add back PAREN_SLOP temporarily.  Then decompile until the
1390         * opcode that would reduce the stack depth to (ss->top-1), which we
1391         * pass to Decompile encoded as -(ss->top-1) - 1 or just -ss->top for
1392         * the nb parameter.
1393         */
1394        todo = ss->sprinter.offset;
1395        ss->sprinter.offset = todo + PAREN_SLOP;
1396        pc = Decompile(ss, pc, -((intN)ss->top), JSOP_NOP);
1397        if (!pc)
1398            return NULL;
1399        if (pc == endpc)
1400            return pc;
1401        LOAD_OP_DATA(pc);
1402        LOCAL_ASSERT(op == JSOP_ENUMELEM || op == JSOP_ENUMCONSTELEM);
1403        xval = PopStr(ss, JSOP_NOP);
1404        lval = PopStr(ss, JSOP_GETPROP);
1405        ss->sprinter.offset = todo;
1406        if (*lval == '\0') {
1407            /* lval is from JSOP_BINDNAME, so just print xval. */
1408            todo = SprintCString(&ss->sprinter, xval);
1409        } else if (*xval == '\0') {
1410            /* xval is from JSOP_SETCALL or JSOP_BINDXMLNAME, print lval. */
1411            todo = SprintCString(&ss->sprinter, lval);
1412        } else {
1413            todo = Sprint(&ss->sprinter,
1414                          (JOF_OPMODE(ss->opcodes[ss->top+1]) == JOF_XMLNAME)
1415                          ? "%s.%s"
1416                          : "%s[%s]",
1417                          lval, xval);
1418        }
1419        break;
1420    }
1421
1422    if (todo < 0)
1423        return NULL;
1424
1425    LOCAL_ASSERT(pc < endpc);
1426    pc += oplen;
1427    return pc;
1428}
1429
1430/*
1431 * Starting with a SRC_DESTRUCT-annotated JSOP_DUP, decompile a destructuring
1432 * left-hand side object or array initialiser, including nested destructuring
1433 * initialisers.  On successful return, the decompilation will be pushed on ss
1434 * and the return value will point to the POP or GROUP bytecode following the
1435 * destructuring expression.
1436 *
1437 * At any point, if pc is equal to endpc and would otherwise advance, we stop
1438 * immediately and return endpc.
1439 */
1440static jsbytecode *
1441DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc)
1442{
1443    ptrdiff_t head;
1444    JSContext *cx;
1445    JSPrinter *jp;
1446    JSOp op, saveop;
1447    const JSCodeSpec *cs;
1448    uintN oplen;
1449    jsint i, lasti;
1450    jsdouble d;
1451    const char *lval;
1452    JSAtom *atom;
1453    jssrcnote *sn;
1454    JSString *str;
1455    JSBool hole;
1456
1457    LOCAL_ASSERT(*pc == JSOP_DUP);
1458    pc += JSOP_DUP_LENGTH;
1459
1460    /*
1461     * Set head so we can rewrite '[' to '{' as needed.  Back up PAREN_SLOP
1462     * chars so the destructuring decompilation accumulates contiguously in
1463     * ss->sprinter starting with "[".
1464     */
1465    head = SprintPut(&ss->sprinter, "[", 1);
1466    if (head < 0 || !PushOff(ss, head, JSOP_NOP))
1467        return NULL;
1468    ss->sprinter.offset -= PAREN_SLOP;
1469    LOCAL_ASSERT(head == ss->sprinter.offset - 1);
1470    LOCAL_ASSERT(*OFF2STR(&ss->sprinter, head) == '[');
1471
1472    cx = ss->sprinter.context;
1473    jp = ss->printer;
1474    lasti = -1;
1475
1476    while (pc < endpc) {
1477#if JS_HAS_DESTRUCTURING_SHORTHAND
1478        ptrdiff_t nameoff = -1;
1479#endif
1480
1481        LOAD_OP_DATA(pc);
1482        saveop = op;
1483
1484        switch (op) {
1485          case JSOP_POP:
1486            pc += oplen;
1487            goto out;
1488
1489          /* Handle the optimized number-pushing opcodes. */
1490          case JSOP_ZERO:   d = i = 0; goto do_getelem;
1491          case JSOP_ONE:    d = i = 1; goto do_getelem;
1492          case JSOP_UINT16: d = i = GET_UINT16(pc); goto do_getelem;
1493          case JSOP_UINT24: d = i = GET_UINT24(pc); goto do_getelem;
1494          case JSOP_INT8:   d = i = GET_INT8(pc);   goto do_getelem;
1495          case JSOP_INT32:  d = i = GET_INT32(pc);  goto do_getelem;
1496
1497          case JSOP_DOUBLE:
1498            GET_ATOM_FROM_BYTECODE(jp->script, pc, 0, atom);
1499            d = *ATOM_TO_DOUBLE(atom);
1500            LOCAL_ASSERT(JSDOUBLE_IS_FINITE(d) && !JSDOUBLE_IS_NEGZERO(d));
1501            i = (jsint)d;
1502
1503          do_getelem:
1504            sn = js_GetSrcNote(jp->script, pc);
1505            pc += oplen;
1506            if (pc == endpc)
1507                return pc;
1508            LOAD_OP_DATA(pc);
1509            LOCAL_ASSERT(op == JSOP_GETELEM);
1510
1511            /* Distinguish object from array by opcode or source note. */
1512            if (sn && SN_TYPE(sn) == SRC_INITPROP) {
1513                *OFF2STR(&ss->sprinter, head) = '{';
1514                if (Sprint(&ss->sprinter, "%g: ", d) < 0)
1515                    return NULL;
1516            } else {
1517                /* Sanity check for the gnarly control flow above. */
1518                LOCAL_ASSERT(i == d);
1519
1520                /* Fill in any holes (holes at the end don't matter). */
1521                while (++lasti < i) {
1522                    if (SprintPut(&ss->sprinter, ", ", 2) < 0)
1523                        return NULL;
1524                }
1525            }
1526            break;
1527
1528          case JSOP_LENGTH:
1529            atom = cx->runtime->atomState.lengthAtom;
1530            goto do_destructure_atom;
1531
1532          case JSOP_CALLPROP:
1533          case JSOP_GETPROP:
1534            GET_ATOM_FROM_BYTECODE(jp->script, pc, 0, atom);
1535          do_destructure_atom:
1536            *OFF2STR(&ss->sprinter, head) = '{';
1537            str = ATOM_TO_STRING(atom);
1538#if JS_HAS_DESTRUCTURING_SHORTHAND
1539            nameoff = ss->sprinter.offset;
1540#endif
1541            if (!QuoteString(&ss->sprinter, str,
1542                             js_IsIdentifier(str) ? 0 : (jschar)'\'')) {
1543                return NULL;
1544            }
1545            if (SprintPut(&ss->sprinter, ": ", 2) < 0)
1546                return NULL;
1547            break;
1548
1549          default:
1550            LOCAL_ASSERT(0);
1551        }
1552
1553        pc += oplen;
1554        if (pc == endpc)
1555            return pc;
1556
1557        /*
1558         * Decompile the left-hand side expression whose bytecode starts at pc
1559         * and continues for a bounded number of bytecodes or stack operations
1560         * (and which in any event stops before endpc).
1561         */
1562        pc = DecompileDestructuringLHS(ss, pc, endpc, &hole);
1563        if (!pc)
1564            return NULL;
1565
1566#if JS_HAS_DESTRUCTURING_SHORTHAND
1567        if (nameoff >= 0) {
1568            ptrdiff_t offset, initlen;
1569
1570            offset = ss->sprinter.offset;
1571            LOCAL_ASSERT(*OFF2STR(&ss->sprinter, offset) == '\0');
1572            initlen = offset - nameoff;
1573            LOCAL_ASSERT(initlen >= 4);
1574
1575            /* Early check to rule out odd "name: lval" length. */
1576            if (((size_t)initlen & 1) == 0) {
1577                size_t namelen;
1578                const char *name;
1579
1580                /*
1581                 * Even "name: lval" string length: check for "x: x" and the
1582                 * like, and apply the shorthand if we can.
1583                 */
1584                namelen = (size_t)(initlen - 2) >> 1;
1585                name = OFF2STR(&ss->sprinter, nameoff);
1586                if (!strncmp(name + namelen, ": ", 2) &&
1587                    !strncmp(name, name + namelen + 2, namelen)) {
1588                    offset -= namelen + 2;
1589                    *OFF2STR(&ss->sprinter, offset) = '\0';
1590                    ss->sprinter.offset = offset;
1591                }
1592            }
1593        }
1594#endif
1595
1596        if (pc == endpc || *pc != JSOP_DUP)
1597            break;
1598
1599        /*
1600         * Check for SRC_DESTRUCT on this JSOP_DUP, which would mean another
1601         * destructuring initialiser abuts this one, and we should stop.  This
1602         * happens with source of the form '[a] = [b] = c'.
1603         */
1604        sn = js_GetSrcNote(jp->script, pc);
1605        if (sn && SN_TYPE(sn) == SRC_DESTRUCT)
1606            break;
1607
1608        if (!hole && SprintPut(&ss->sprinter, ", ", 2) < 0)
1609            return NULL;
1610
1611        pc += JSOP_DUP_LENGTH;
1612    }
1613
1614out:
1615    lval = OFF2STR(&ss->sprinter, head);
1616    if (SprintPut(&ss->sprinter, (*lval == '[') ? "]" : "}", 1) < 0)
1617        return NULL;
1618    return pc;
1619}
1620
1621static jsbytecode *
1622DecompileGroupAssignment(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc,
1623                         jssrcnote *sn, ptrdiff_t *todop)
1624{
1625    JSOp op;
1626    const JSCodeSpec *cs;
1627    uintN oplen, start, end, i;
1628    ptrdiff_t todo;
1629    JSBool hole;
1630    const char *rval;
1631
1632    LOAD_OP_DATA(pc);
1633    LOCAL_ASSERT(op == JSOP_PUSH || op == JSOP_GETLOCAL);
1634
1635    todo = Sprint(&ss->sprinter, "%s[", VarPrefix(sn));
1636    if (todo < 0 || !PushOff(ss, todo, JSOP_NOP))
1637        return NULL;
1638    ss->sprinter.offset -= PAREN_SLOP;
1639
1640    for (;;) {
1641        pc += oplen;
1642        if (pc == endpc)
1643            return pc;
1644        pc = DecompileDestructuringLHS(ss, pc, endpc, &hole);
1645        if (!pc)
1646            return NULL;
1647        if (pc == endpc)
1648            return pc;
1649        LOAD_OP_DATA(pc);
1650        if (op != JSOP_PUSH && op != JSOP_GETLOCAL)
1651            break;
1652        if (!hole && SprintPut(&ss->sprinter, ", ", 2) < 0)
1653            return NULL;
1654    }
1655
1656    LOCAL_ASSERT(op == JSOP_POPN);
1657    if (SprintPut(&ss->sprinter, "] = [", 5) < 0)
1658        return NULL;
1659
1660    end = ss->top - 1;
1661    start = end - GET_UINT16(pc);
1662    for (i = start; i < end; i++) {
1663        rval = GetStr(ss, i);
1664        if (Sprint(&ss->sprinter,
1665                   (i == start) ? "%s" : ", %s",
1666                   (i == end - 1 && *rval == '\0') ? ", " : rval) < 0) {
1667            return NULL;
1668        }
1669    }
1670
1671    if (SprintPut(&ss->sprinter, "]", 1) < 0)
1672        return NULL;
1673    ss->sprinter.offset = ss->offsets[i];
1674    ss->top = start;
1675    *todop = todo;
1676    return pc;
1677}
1678
1679#undef LOCAL_ASSERT
1680#undef LOAD_…

Large files files are truncated, but you can click here to view the full file