LCOV - code coverage report
Current view: directory - js/src - jsopcode.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 3098 2053 66.3 %
Date: 2012-04-07 Functions: 110 90 81.8 %

       1                 : /* -*- Mode: C++; tab-width: 4; 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                 : #ifdef HAVE_MEMORY_H
      45                 : #include <memory.h>
      46                 : #endif
      47                 : #include <stdarg.h>
      48                 : #include <stdio.h>
      49                 : #include <stdlib.h>
      50                 : #include <string.h>
      51                 : 
      52                 : #include "mozilla/Util.h"
      53                 : 
      54                 : #include "jstypes.h"
      55                 : #include "jsutil.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 "jsfun.h"
      63                 : #include "jsiter.h"
      64                 : #include "jsnum.h"
      65                 : #include "jsobj.h"
      66                 : #include "jsopcode.h"
      67                 : #include "jsscope.h"
      68                 : #include "jsscript.h"
      69                 : #include "jsstr.h"
      70                 : 
      71                 : #include "ds/Sort.h"
      72                 : 
      73                 : #include "frontend/BytecodeEmitter.h"
      74                 : #include "frontend/TokenStream.h"
      75                 : #include "vm/Debugger.h"
      76                 : #include "vm/StringBuffer.h"
      77                 : 
      78                 : #include "jscntxtinlines.h"
      79                 : #include "jsobjinlines.h"
      80                 : #include "jsopcodeinlines.h"
      81                 : 
      82                 : #include "jsautooplen.h"
      83                 : 
      84                 : #include "vm/RegExpObject-inl.h"
      85                 : 
      86                 : using namespace mozilla;
      87                 : using namespace js;
      88                 : using namespace js::gc;
      89                 : 
      90                 : /*
      91                 :  * Index limit must stay within 32 bits.
      92                 :  */
      93                 : JS_STATIC_ASSERT(sizeof(uint32_t) * JS_BITS_PER_BYTE >= INDEX_LIMIT_LOG2 + 1);
      94                 : 
      95                 : /* Verify JSOP_XXX_LENGTH constant definitions. */
      96                 : #define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format)               \
      97                 :     JS_STATIC_ASSERT(op##_LENGTH == length);
      98                 : #include "jsopcode.tbl"
      99                 : #undef OPDEF
     100                 : 
     101                 : static const char js_incop_strs[][3] = {"++", "--"};
     102                 : static const char js_for_each_str[]  = "for each";
     103                 : 
     104                 : const JSCodeSpec js_CodeSpec[] = {
     105                 : #define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
     106                 :     {length,nuses,ndefs,prec,format},
     107                 : #include "jsopcode.tbl"
     108                 : #undef OPDEF
     109                 : };
     110                 : 
     111                 : unsigned js_NumCodeSpecs = JS_ARRAY_LENGTH(js_CodeSpec);
     112                 : 
     113                 : /*
     114                 :  * Each element of the array is either a source literal associated with JS
     115                 :  * bytecode or null.
     116                 :  */
     117                 : static const char *CodeToken[] = {
     118                 : #define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
     119                 :     token,
     120                 : #include "jsopcode.tbl"
     121                 : #undef OPDEF
     122                 : };
     123                 : 
     124                 : /*
     125                 :  * Array of JS bytecode names used by PC count JSON, DEBUG-only js_Disassemble
     126                 :  * and JIT debug spew.
     127                 :  */
     128                 : const char *js_CodeName[] = {
     129                 : #define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
     130                 :     name,
     131                 : #include "jsopcode.tbl"
     132                 : #undef OPDEF
     133                 : };
     134                 : 
     135                 : /************************************************************************/
     136                 : 
     137                 : #define COUNTS_LEN 16
     138                 : 
     139                 : typedef Vector<char, 8> DupBuffer;
     140                 : 
     141                 : static bool
     142            6597 : Dup(const char *chars, DupBuffer *cb)
     143                 : {
     144            6597 :     return cb->append(chars, strlen(chars) + 1);
     145                 : }
     146                 : 
     147                 : size_t
     148            4081 : js_GetVariableBytecodeLength(jsbytecode *pc)
     149                 : {
     150                 :     unsigned ncases;
     151                 :     int32_t low, high;
     152                 : 
     153            4081 :     JSOp op = JSOp(*pc);
     154            4081 :     JS_ASSERT(js_CodeSpec[op].length == -1);
     155            4081 :     switch (op) {
     156                 :       case JSOP_TABLESWITCH:
     157                 :         /* Structure: default-jump case-low case-high case1-jump ... */
     158            2959 :         pc += JUMP_OFFSET_LEN;
     159            2959 :         low = GET_JUMP_OFFSET(pc);
     160            2959 :         pc += JUMP_OFFSET_LEN;
     161            2959 :         high = GET_JUMP_OFFSET(pc);
     162            2959 :         ncases = (unsigned)(high - low + 1);
     163            2959 :         return 1 + 3 * JUMP_OFFSET_LEN + ncases * JUMP_OFFSET_LEN;
     164                 : 
     165                 :       default:
     166                 :         /* Structure: default-jump case-count (case1-value case1-jump) ... */
     167            1122 :         JS_ASSERT(op == JSOP_LOOKUPSWITCH);
     168            1122 :         pc += JUMP_OFFSET_LEN;
     169            1122 :         ncases = GET_UINT16(pc);
     170            1122 :         return 1 + JUMP_OFFSET_LEN + UINT16_LEN + ncases * (UINT32_INDEX_LEN + JUMP_OFFSET_LEN);
     171                 :     }
     172                 : }
     173                 : 
     174                 : static uint32_t
     175          231330 : NumBlockSlots(JSScript *script, jsbytecode *pc)
     176                 : {
     177          231330 :     JS_ASSERT(*pc == JSOP_ENTERBLOCK || *pc == JSOP_ENTERLET0 || *pc == JSOP_ENTERLET1);
     178                 :     JS_STATIC_ASSERT(JSOP_ENTERBLOCK_LENGTH == JSOP_ENTERLET0_LENGTH);
     179                 :     JS_STATIC_ASSERT(JSOP_ENTERBLOCK_LENGTH == JSOP_ENTERLET1_LENGTH);
     180                 : 
     181          231330 :     return script->getObject(GET_UINT32_INDEX(pc))->asStaticBlock().slotCount();
     182                 : }
     183                 : 
     184                 : unsigned
     185        48442478 : js::StackUses(JSScript *script, jsbytecode *pc)
     186                 : {
     187        48442478 :     JSOp op = (JSOp) *pc;
     188        48442478 :     const JSCodeSpec &cs = js_CodeSpec[op];
     189        48442478 :     if (cs.nuses >= 0)
     190        46152051 :         return cs.nuses;
     191                 : 
     192         2290427 :     JS_ASSERT(js_CodeSpec[op].nuses == -1);
     193         2290427 :     switch (op) {
     194                 :       case JSOP_POPN:
     195            4446 :         return GET_UINT16(pc);
     196                 :       case JSOP_LEAVEBLOCK:
     197          242210 :         return GET_UINT16(pc);
     198                 :       case JSOP_LEAVEBLOCKEXPR:
     199            4445 :         return GET_UINT16(pc) + 1;
     200                 :       case JSOP_ENTERLET0:
     201           16860 :         return NumBlockSlots(script, pc);
     202                 :       case JSOP_ENTERLET1:
     203            2498 :         return NumBlockSlots(script, pc) + 1;
     204                 :       default:
     205                 :         /* stack: fun, this, [argc arguments] */
     206               0 :         JS_ASSERT(op == JSOP_NEW || op == JSOP_CALL || op == JSOP_EVAL ||
     207         2019968 :                   op == JSOP_FUNCALL || op == JSOP_FUNAPPLY);
     208         2019968 :         return 2 + GET_ARGC(pc);
     209                 :     }
     210                 : }
     211                 : 
     212                 : unsigned
     213        89025531 : js::StackDefs(JSScript *script, jsbytecode *pc)
     214                 : {
     215        89025531 :     JSOp op = (JSOp) *pc;
     216        89025531 :     const JSCodeSpec &cs = js_CodeSpec[op];
     217        89025531 :     if (cs.ndefs >= 0)
     218        88813559 :         return cs.ndefs;
     219                 : 
     220          211972 :     uint32_t n = NumBlockSlots(script, pc);
     221          211972 :     return op == JSOP_ENTERLET1 ? n + 1 : n;
     222                 : }
     223                 : 
     224                 : static const char * countBaseNames[] = {
     225                 :     "interp",
     226                 :     "mjit",
     227                 :     "mjit_calls",
     228                 :     "mjit_code",
     229                 :     "mjit_pics"
     230                 : };
     231                 : 
     232                 : JS_STATIC_ASSERT(JS_ARRAY_LENGTH(countBaseNames) == PCCounts::BASE_LIMIT);
     233                 : 
     234                 : static const char * countAccessNames[] = {
     235                 :     "infer_mono",
     236                 :     "infer_di",
     237                 :     "infer_poly",
     238                 :     "infer_barrier",
     239                 :     "infer_nobarrier",
     240                 :     "observe_undefined",
     241                 :     "observe_null",
     242                 :     "observe_boolean",
     243                 :     "observe_int32",
     244                 :     "observe_double",
     245                 :     "observe_string",
     246                 :     "observe_object"
     247                 : };
     248                 : 
     249                 : JS_STATIC_ASSERT(JS_ARRAY_LENGTH(countBaseNames) +
     250                 :                  JS_ARRAY_LENGTH(countAccessNames) == PCCounts::ACCESS_LIMIT);
     251                 : 
     252                 : static const char * countElementNames[] = {
     253                 :     "id_int",
     254                 :     "id_double",
     255                 :     "id_other",
     256                 :     "id_unknown",
     257                 :     "elem_typed",
     258                 :     "elem_packed",
     259                 :     "elem_dense",
     260                 :     "elem_other"
     261                 : };
     262                 : 
     263                 : JS_STATIC_ASSERT(JS_ARRAY_LENGTH(countBaseNames) +
     264                 :                  JS_ARRAY_LENGTH(countAccessNames) +
     265                 :                  JS_ARRAY_LENGTH(countElementNames) == PCCounts::ELEM_LIMIT);
     266                 : 
     267                 : static const char * countPropertyNames[] = {
     268                 :     "prop_static",
     269                 :     "prop_definite",
     270                 :     "prop_other"
     271                 : };
     272                 : 
     273                 : JS_STATIC_ASSERT(JS_ARRAY_LENGTH(countBaseNames) +
     274                 :                  JS_ARRAY_LENGTH(countAccessNames) +
     275                 :                  JS_ARRAY_LENGTH(countPropertyNames) == PCCounts::PROP_LIMIT);
     276                 : 
     277                 : static const char * countArithNames[] = {
     278                 :     "arith_int",
     279                 :     "arith_double",
     280                 :     "arith_other",
     281                 :     "arith_unknown",
     282                 : };
     283                 : 
     284                 : JS_STATIC_ASSERT(JS_ARRAY_LENGTH(countBaseNames) +
     285                 :                  JS_ARRAY_LENGTH(countArithNames) == PCCounts::ARITH_LIMIT);
     286                 : 
     287                 : /* static */ const char *
     288               0 : PCCounts::countName(JSOp op, size_t which)
     289                 : {
     290               0 :     JS_ASSERT(which < numCounts(op));
     291                 : 
     292               0 :     if (which < BASE_LIMIT)
     293               0 :         return countBaseNames[which];
     294                 : 
     295               0 :     if (accessOp(op)) {
     296               0 :         if (which < ACCESS_LIMIT)
     297               0 :             return countAccessNames[which - BASE_LIMIT];
     298               0 :         if (elementOp(op))
     299               0 :             return countElementNames[which - ACCESS_LIMIT];
     300               0 :         if (propertyOp(op))
     301               0 :             return countPropertyNames[which - ACCESS_LIMIT];
     302               0 :         JS_NOT_REACHED("bad op");
     303                 :         return NULL;
     304                 :     }
     305                 : 
     306               0 :     if (arithOp(op))
     307               0 :         return countArithNames[which - BASE_LIMIT];
     308                 : 
     309               0 :     JS_NOT_REACHED("bad op");
     310                 :     return NULL;
     311                 : }
     312                 : 
     313                 : #ifdef DEBUG
     314                 : 
     315                 : JS_FRIEND_API(void)
     316               0 : js_DumpPCCounts(JSContext *cx, JSScript *script, js::Sprinter *sp)
     317                 : {
     318               0 :     JS_ASSERT(script->scriptCounts);
     319                 : 
     320               0 :     jsbytecode *pc = script->code;
     321               0 :     while (pc < script->code + script->length) {
     322               0 :         JSOp op = JSOp(*pc);
     323                 : 
     324               0 :         int len = js_CodeSpec[op].length;
     325               0 :         jsbytecode *next = (len != -1) ? pc + len : pc + js_GetVariableBytecodeLength(pc);
     326                 : 
     327               0 :         if (!js_Disassemble1(cx, script, pc, pc - script->code, true, sp))
     328               0 :             return;
     329                 : 
     330               0 :         size_t total = PCCounts::numCounts(op);
     331               0 :         double *raw = script->getPCCounts(pc).rawCounts();
     332                 : 
     333               0 :         Sprint(sp, "                  {");
     334               0 :         bool printed = false;
     335               0 :         for (size_t i = 0; i < total; i++) {
     336               0 :             double val = raw[i];
     337               0 :             if (val) {
     338               0 :                 if (printed)
     339               0 :                     Sprint(sp, ", ");
     340               0 :                 Sprint(sp, "\"%s\": %.0f", PCCounts::countName(op, i), val);
     341               0 :                 printed = true;
     342                 :             }
     343                 :         }
     344               0 :         Sprint(sp, "}\n");
     345                 : 
     346               0 :         pc = next;
     347                 :     }
     348                 : }
     349                 : 
     350                 : /*
     351                 :  * If pc != NULL, include a prefix indicating whether the PC is at the current line.
     352                 :  * If counts != NULL, include a counter of the number of times each op was executed.
     353                 :  */
     354                 : JS_FRIEND_API(JSBool)
     355              18 : js_DisassembleAtPC(JSContext *cx, JSScript *script, JSBool lines, jsbytecode *pc, Sprinter *sp)
     356                 : {
     357                 :     jsbytecode *next, *end;
     358                 :     unsigned len;
     359                 : 
     360              18 :     sp->put("loc   ");
     361              18 :     if (lines)
     362               0 :         sp->put("line");
     363              18 :     sp->put("  op\n");
     364              18 :     sp->put("----- ");
     365              18 :     if (lines)
     366               0 :         sp->put("----");
     367              18 :     sp->put("  --\n");
     368                 : 
     369              18 :     next = script->code;
     370              18 :     end = next + script->length;
     371          165987 :     while (next < end) {
     372          165951 :         if (next == script->main())
     373              18 :             sp->put("main:\n");
     374          165951 :         if (pc != NULL) {
     375               0 :             if (pc == next)
     376               0 :                 sp->put("--> ");
     377                 :             else
     378               0 :                 sp->put("    ");
     379                 :         }
     380          165951 :         len = js_Disassemble1(cx, script, next, next - script->code, lines, sp);
     381          165951 :         if (!len)
     382               0 :             return JS_FALSE;
     383          165951 :         next += len;
     384                 :     }
     385              18 :     return JS_TRUE;
     386                 : }
     387                 : 
     388                 : JS_FRIEND_API(JSBool)
     389              18 : js_Disassemble(JSContext *cx, JSScript *script, JSBool lines, Sprinter *sp)
     390                 : {
     391              18 :     return js_DisassembleAtPC(cx, script, lines, NULL, sp);
     392                 : }
     393                 : 
     394                 : JS_FRIEND_API(JSBool)
     395               0 : js_DumpPC(JSContext *cx)
     396                 : {
     397               0 :     Sprinter sprinter(cx);
     398               0 :     if (!sprinter.init())
     399               0 :         return JS_FALSE;
     400               0 :     JSBool ok = js_DisassembleAtPC(cx, cx->fp()->script(), true, cx->regs().pc, &sprinter);
     401               0 :     fprintf(stdout, "%s", sprinter.string());
     402               0 :     return ok;
     403                 : }
     404                 : 
     405                 : JSBool
     406               0 : js_DumpScript(JSContext *cx, JSScript *script)
     407                 : {
     408               0 :     Sprinter sprinter(cx);
     409               0 :     if (!sprinter.init())
     410               0 :         return JS_FALSE;
     411               0 :     JSBool ok = js_Disassemble(cx, script, true, &sprinter);
     412               0 :     fprintf(stdout, "%s", sprinter.string());
     413               0 :     return ok;
     414                 : }
     415                 : 
     416                 : static char *
     417                 : QuoteString(Sprinter *sp, JSString *str, uint32_t quote);
     418                 : 
     419                 : static bool
     420           18459 : ToDisassemblySource(JSContext *cx, jsval v, JSAutoByteString *bytes)
     421                 : {
     422           18459 :     if (JSVAL_IS_STRING(v)) {
     423              36 :         Sprinter sprinter(cx);
     424              18 :         if (!sprinter.init())
     425               0 :             return false;
     426              18 :         char *nbytes = QuoteString(&sprinter, JSVAL_TO_STRING(v), '"');
     427              18 :         if (!nbytes)
     428               0 :             return false;
     429              18 :         nbytes = JS_sprintf_append(NULL, "%s", nbytes);
     430              18 :         if (!nbytes)
     431               0 :             return false;
     432              18 :         bytes->initBytes(nbytes);
     433              18 :         return true;
     434                 :     }
     435                 : 
     436           18441 :     if (cx->runtime->gcRunning || cx->runtime->noGCOrAllocationCheck) {
     437               0 :         char *source = JS_sprintf_append(NULL, "<value>");
     438               0 :         if (!source)
     439               0 :             return false;
     440               0 :         bytes->initBytes(source);
     441               0 :         return true;
     442                 :     }
     443                 : 
     444           18441 :     if (!JSVAL_IS_PRIMITIVE(v)) {
     445           18441 :         JSObject *obj = JSVAL_TO_OBJECT(v);
     446           18441 :         if (obj->isBlock()) {
     447           18432 :             char *source = JS_sprintf_append(NULL, "depth %d {", obj->asBlock().stackDepth());
     448           18432 :             if (!source)
     449               0 :                 return false;
     450                 : 
     451           18432 :             Shape::Range r = obj->lastProperty()->all();
     452           55296 :             while (!r.empty()) {
     453           18432 :                 const Shape &shape = r.front();
     454           18432 :                 JSAtom *atom = JSID_IS_INT(shape.propid())
     455                 :                                ? cx->runtime->atomState.emptyAtom
     456           18432 :                                : JSID_TO_ATOM(shape.propid());
     457                 : 
     458           36864 :                 JSAutoByteString bytes;
     459           18432 :                 if (!js_AtomToPrintableString(cx, atom, &bytes))
     460               0 :                     return false;
     461                 : 
     462           18432 :                 r.popFront();
     463                 :                 source = JS_sprintf_append(source, "%s: %d%s",
     464           18432 :                                            bytes.ptr(), shape.shortid(),
     465           36864 :                                            !r.empty() ? ", " : "");
     466           18432 :                 if (!source)
     467               0 :                     return false;
     468                 :             }
     469                 : 
     470           18432 :             source = JS_sprintf_append(source, "}");
     471           18432 :             if (!source)
     472               0 :                 return false;
     473           18432 :             bytes->initBytes(source);
     474           18432 :             return true;
     475                 :         }
     476                 : 
     477               9 :         if (obj->isFunction()) {
     478               9 :             JSString *str = JS_DecompileFunction(cx, obj->toFunction(), JS_DONT_PRETTY_PRINT);
     479               9 :             if (!str)
     480               0 :                 return false;
     481               9 :             return bytes->encode(cx, str);
     482                 :         }
     483                 : 
     484               0 :         if (obj->isRegExp()) {
     485               0 :             JSString *source = obj->asRegExp().toString(cx);
     486               0 :             if (!source)
     487               0 :                 return false;
     488               0 :             JS::Anchor<JSString *> anchor(source);
     489               0 :             return bytes->encode(cx, source);
     490                 :         }
     491                 :     }
     492                 : 
     493               0 :     return !!js_ValueToPrintable(cx, v, bytes, true);
     494                 : }
     495                 : 
     496                 : JS_FRIEND_API(unsigned)
     497          165951 : js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc,
     498                 :                 unsigned loc, JSBool lines, Sprinter *sp)
     499                 : {
     500          165951 :     JSOp op = (JSOp)*pc;
     501          165951 :     if (op >= JSOP_LIMIT) {
     502                 :         char numBuf1[12], numBuf2[12];
     503               0 :         JS_snprintf(numBuf1, sizeof numBuf1, "%d", op);
     504               0 :         JS_snprintf(numBuf2, sizeof numBuf2, "%d", JSOP_LIMIT);
     505                 :         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
     506               0 :                              JSMSG_BYTECODE_TOO_BIG, numBuf1, numBuf2);
     507               0 :         return 0;
     508                 :     }
     509          165951 :     const JSCodeSpec *cs = &js_CodeSpec[op];
     510          165951 :     ptrdiff_t len = (ptrdiff_t) cs->length;
     511          165951 :     Sprint(sp, "%05u:", loc);
     512          165951 :     if (lines)
     513               0 :         Sprint(sp, "%4u", JS_PCToLineNumber(cx, script, pc));
     514          165951 :     Sprint(sp, "  %s", js_CodeName[op]);
     515                 : 
     516          165951 :     switch (JOF_TYPE(cs->format)) {
     517                 :       case JOF_BYTE:
     518                 :           // Scan the trynotes to find the associated catch block
     519                 :           // and make the try opcode look like a jump instruction
     520                 :           // with an offset. This simplifies code coverage analysis
     521                 :           // based on this disassembled output.
     522           73746 :           if (op == JSOP_TRY) {
     523           18423 :               JSTryNoteArray *trynotes = script->trynotes();
     524                 :               uint32_t i;
     525        18865152 :               for(i = 0; i < trynotes->length; i++) {
     526        18865152 :                   JSTryNote note = trynotes->vector[i];
     527        18865152 :                   if (note.kind == JSTRY_CATCH && note.start == loc + 1) {
     528                 :                       Sprint(sp, " %u (%+d)",
     529                 :                              (unsigned int) (loc+note.length+1),
     530           18423 :                              (int) (note.length+1));
     531           18423 :                       break;
     532                 :                   }
     533                 :               }
     534                 :           }
     535           73746 :         break;
     536                 : 
     537                 :       case JOF_JUMP: {
     538           36855 :         ptrdiff_t off = GET_JUMP_OFFSET(pc);
     539           36855 :         Sprint(sp, " %u (%+d)", loc + (int) off, (int) off);
     540           36855 :         break;
     541                 :       }
     542                 : 
     543                 :       case JOF_ATOM: {
     544              18 :         Value v = StringValue(script->getAtom(GET_UINT32_INDEX(pc)));
     545              36 :         JSAutoByteString bytes;
     546              18 :         if (!ToDisassemblySource(cx, v, &bytes))
     547               0 :             return 0;
     548              18 :         Sprint(sp, " %s", bytes.ptr());
     549              18 :         break;
     550                 :       }
     551                 : 
     552                 :       case JOF_DOUBLE: {
     553               0 :         Value v = script->getConst(GET_UINT32_INDEX(pc));
     554               0 :         JSAutoByteString bytes;
     555               0 :         if (!ToDisassemblySource(cx, v, &bytes))
     556               0 :             return 0;
     557               0 :         Sprint(sp, " %s", bytes.ptr());
     558               0 :         break;
     559                 :       }
     560                 : 
     561                 :       case JOF_OBJECT: {
     562                 :         /* Don't call obj.toSource if analysis/inference is active. */
     563           18441 :         if (cx->compartment->activeAnalysis) {
     564               0 :             Sprint(sp, " object");
     565               0 :             break;
     566                 :         }
     567                 : 
     568           18441 :         JSObject *obj = script->getObject(GET_UINT32_INDEX(pc));
     569                 :         {
     570           36882 :             JSAutoByteString bytes;
     571           18441 :             if (!ToDisassemblySource(cx, ObjectValue(*obj), &bytes))
     572               0 :                 return 0;
     573           36882 :             Sprint(sp, " %s", bytes.ptr());
     574                 :         }
     575           18441 :         break;
     576                 :       }
     577                 : 
     578                 :       case JOF_REGEXP: {
     579               0 :         JSObject *obj = script->getRegExp(GET_UINT32_INDEX(pc));
     580               0 :         JSAutoByteString bytes;
     581               0 :         if (!ToDisassemblySource(cx, ObjectValue(*obj), &bytes))
     582               0 :             return 0;
     583               0 :         Sprint(sp, " %s", bytes.ptr());
     584               0 :         break;
     585                 :       }
     586                 : 
     587                 :       case JOF_TABLESWITCH:
     588                 :       {
     589                 :         int32_t i, low, high;
     590                 : 
     591               9 :         ptrdiff_t off = GET_JUMP_OFFSET(pc);
     592               9 :         jsbytecode *pc2 = pc + JUMP_OFFSET_LEN;
     593               9 :         low = GET_JUMP_OFFSET(pc2);
     594               9 :         pc2 += JUMP_OFFSET_LEN;
     595               9 :         high = GET_JUMP_OFFSET(pc2);
     596               9 :         pc2 += JUMP_OFFSET_LEN;
     597               9 :         Sprint(sp, " defaultOffset %d low %d high %d", int(off), low, high);
     598               9 :         for (i = low; i <= high; i++) {
     599               0 :             off = GET_JUMP_OFFSET(pc2);
     600               0 :             Sprint(sp, "\n\t%d: %d", i, int(off));
     601               0 :             pc2 += JUMP_OFFSET_LEN;
     602                 :         }
     603               9 :         len = 1 + pc2 - pc;
     604               9 :         break;
     605                 :       }
     606                 : 
     607                 :       case JOF_LOOKUPSWITCH:
     608                 :       {
     609                 :         jsatomid npairs;
     610                 : 
     611               0 :         ptrdiff_t off = GET_JUMP_OFFSET(pc);
     612               0 :         jsbytecode *pc2 = pc + JUMP_OFFSET_LEN;
     613               0 :         npairs = GET_UINT16(pc2);
     614               0 :         pc2 += UINT16_LEN;
     615               0 :         Sprint(sp, " offset %d npairs %u", int(off), unsigned(npairs));
     616               0 :         while (npairs) {
     617               0 :             uint32_t constIndex = GET_UINT32_INDEX(pc2);
     618               0 :             pc2 += UINT32_INDEX_LEN;
     619               0 :             off = GET_JUMP_OFFSET(pc2);
     620               0 :             pc2 += JUMP_OFFSET_LEN;
     621                 : 
     622               0 :             JSAutoByteString bytes;
     623               0 :             if (!ToDisassemblySource(cx, script->getConst(constIndex), &bytes))
     624               0 :                 return 0;
     625               0 :             Sprint(sp, "\n\t%s: %d", bytes.ptr(), int(off));
     626               0 :             npairs--;
     627                 :         }
     628               0 :         len = 1 + pc2 - pc;
     629               0 :         break;
     630                 :       }
     631                 : 
     632                 :       case JOF_QARG:
     633               0 :         Sprint(sp, " %u", GET_ARGNO(pc));
     634               0 :         break;
     635                 : 
     636                 :       case JOF_LOCAL:
     637           18432 :         Sprint(sp, " %u", GET_SLOTNO(pc));
     638           18432 :         break;
     639                 : 
     640                 :       case JOF_SLOTOBJECT: {
     641               0 :         Sprint(sp, " %u", GET_SLOTNO(pc));
     642               0 :         JSObject *obj = script->getObject(GET_UINT32_INDEX(pc + SLOTNO_LEN));
     643               0 :         JSAutoByteString bytes;
     644               0 :         if (!ToDisassemblySource(cx, ObjectValue(*obj), &bytes))
     645               0 :             return 0;
     646               0 :         Sprint(sp, " %s", bytes.ptr());
     647               0 :         break;
     648                 :       }
     649                 : 
     650                 :       {
     651                 :         int i;
     652                 : 
     653                 :       case JOF_UINT16PAIR:
     654               0 :         i = (int)GET_UINT16(pc);
     655               0 :         Sprint(sp, " %d", i);
     656               0 :         pc += UINT16_LEN;
     657                 :         /* FALL THROUGH */
     658                 : 
     659                 :       case JOF_UINT16:
     660           18450 :         i = (int)GET_UINT16(pc);
     661           18450 :         goto print_int;
     662                 : 
     663                 :       case JOF_UINT24:
     664               0 :         JS_ASSERT(op == JSOP_UINT24 || op == JSOP_NEWARRAY);
     665               0 :         i = (int)GET_UINT24(pc);
     666               0 :         goto print_int;
     667                 : 
     668                 :       case JOF_UINT8:
     669               0 :         i = GET_UINT8(pc);
     670               0 :         goto print_int;
     671                 : 
     672                 :       case JOF_INT8:
     673               0 :         i = GET_INT8(pc);
     674               0 :         goto print_int;
     675                 : 
     676                 :       case JOF_INT32:
     677               0 :         JS_ASSERT(op == JSOP_INT32);
     678               0 :         i = GET_INT32(pc);
     679                 :       print_int:
     680           18450 :         Sprint(sp, " %d", i);
     681           18450 :         break;
     682                 :       }
     683                 : 
     684                 :       default: {
     685                 :         char numBuf[12];
     686               0 :         JS_snprintf(numBuf, sizeof numBuf, "%lx", (unsigned long) cs->format);
     687                 :         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
     688               0 :                              JSMSG_UNKNOWN_FORMAT, numBuf);
     689               0 :         return 0;
     690                 :       }
     691                 :     }
     692          165951 :     sp->put("\n");
     693          165951 :     return len;
     694                 : }
     695                 : 
     696                 : #endif /* DEBUG */
     697                 : 
     698                 : /************************************************************************/
     699                 : 
     700                 : const size_t Sprinter::DefaultSize = 64;
     701                 : 
     702                 : bool 
     703            6633 : Sprinter::realloc_(size_t newSize)
     704                 : {
     705            6633 :     JS_ASSERT(newSize > (size_t) offset);
     706            6633 :     char *newBuf = (char *) context->realloc_(base, newSize);
     707            6633 :     if (!newBuf)
     708               0 :         return false;
     709            6633 :     base = newBuf;
     710            6633 :     size = newSize;
     711            6633 :     base[size - 1] = 0;
     712            6633 :     return true;
     713                 : }
     714                 : 
     715           54492 : Sprinter::Sprinter(JSContext *cx)
     716                 :   : context(cx),
     717                 : #ifdef DEBUG
     718                 :     initialized(false),
     719                 : #endif
     720           54492 :     base(NULL), size(0), offset(0)
     721           54492 : { }
     722                 : 
     723           54492 : Sprinter::~Sprinter()
     724                 : {
     725                 : #ifdef DEBUG
     726           54492 :     if (initialized)
     727           45987 :         checkInvariants();
     728                 : #endif
     729           54492 :     context->free_(base);
     730           54492 : }
     731                 : 
     732                 : bool
     733           45987 : Sprinter::init()
     734                 : {
     735           45987 :     JS_ASSERT(!initialized);
     736           45987 :     base = (char *) context->malloc_(DefaultSize);
     737           45987 :     if (!base)
     738               0 :         return false;
     739                 : #ifdef DEBUG
     740           45987 :     initialized = true;
     741                 : #endif
     742           45987 :     *base = 0;
     743           45987 :     size = DefaultSize;
     744           45987 :     base[size - 1] = 0;
     745           45987 :     return true;
     746                 : }
     747                 : 
     748                 : void
     749         5026403 : Sprinter::checkInvariants() const
     750                 : {
     751         5026403 :     JS_ASSERT(initialized);
     752         5026403 :     JS_ASSERT((size_t) offset < size);
     753         5026403 :     JS_ASSERT(base[size - 1] == 0);
     754         5026403 : }
     755                 : 
     756                 : const char *
     757           13210 : Sprinter::string() const
     758                 : {
     759           13210 :     return base;
     760                 : }
     761                 : 
     762                 : const char *
     763               0 : Sprinter::stringEnd() const
     764                 : {
     765               0 :     return base + offset;
     766                 : }
     767                 : 
     768                 : char *
     769          190464 : Sprinter::stringAt(ptrdiff_t off) const
     770                 : {
     771          190464 :     JS_ASSERT(off >= 0 && (size_t) off < size);
     772          190464 :     return base + off;
     773                 : }
     774                 : 
     775                 : char &
     776          377761 : Sprinter::operator[](size_t off)
     777                 : {
     778          377761 :     JS_ASSERT(off < size);
     779          377761 :     return *(base + off);
     780                 : }
     781                 : 
     782                 : bool
     783              99 : Sprinter::empty() const
     784                 : {
     785              99 :     return *base == 0;
     786                 : }
     787                 : 
     788                 : char *
     789         1331561 : Sprinter::reserve(size_t len)
     790                 : {
     791         2663122 :     InvariantChecker ic(this);
     792                 : 
     793         1331561 :     while (len + 1 > size - offset) { /* Include trailing \0 */
     794            6633 :         if (!realloc_(size * 2))
     795               0 :             return NULL;
     796                 :     }
     797                 : 
     798         1331561 :     char *sb = base + offset;
     799         1331561 :     offset += len;
     800         1331561 :     return sb;
     801                 : }
     802                 : 
     803                 : char *
     804           88738 : Sprinter::reserveAndClear(size_t len)
     805                 : {
     806           88738 :     char *sb = reserve(len);
     807           88738 :     if (sb)
     808           88738 :         memset(sb, 0, len);
     809           88738 :     return sb;
     810                 : }
     811                 : 
     812                 : ptrdiff_t
     813         1158251 : Sprinter::put(const char *s, size_t len)
     814                 : {
     815         2316502 :     InvariantChecker ic(this);
     816                 : 
     817         1158251 :     const char *oldBase = base;
     818         1158251 :     const char *oldEnd = base + size;
     819                 : 
     820         1158251 :     ptrdiff_t oldOffset = offset;
     821         1158251 :     char *bp = reserve(len);
     822         1158251 :     if (!bp)
     823               0 :         return -1;
     824                 : 
     825                 :     /* s is within the buffer already */
     826         1158251 :     if (s >= oldBase && s < oldEnd) {
     827                 :         /* buffer was realloc'ed */
     828           17973 :         if (base != oldBase)
     829             171 :             s = stringAt(s - oldBase);  /* this is where it lives now */
     830           17973 :         memmove(bp, s, len);
     831                 :     } else {
     832         1140278 :         js_memcpy(bp, s, len);
     833                 :     }
     834                 : 
     835         1158251 :     bp[len] = 0;
     836         1158251 :     return oldOffset;
     837                 : }
     838                 : 
     839                 : ptrdiff_t
     840         1025584 : Sprinter::put(const char *s)
     841                 : {
     842         1025584 :     return put(s, strlen(s));
     843                 : }
     844                 : 
     845                 : ptrdiff_t
     846             396 : Sprinter::putString(JSString *s)
     847                 : {
     848             792 :     InvariantChecker ic(this);
     849                 : 
     850             396 :     size_t length = s->length();
     851             396 :     const jschar *chars = s->getChars(context);
     852             396 :     if (!chars)
     853               0 :         return -1;
     854                 : 
     855             396 :     size_t size = GetDeflatedStringLength(context, chars, length);
     856             396 :     if (size == (size_t) -1)
     857               0 :         return -1;
     858                 : 
     859             396 :     ptrdiff_t oldOffset = offset;
     860             396 :     char *buffer = reserve(size);
     861             396 :     if (!buffer)
     862               0 :         return -1;
     863             396 :     DeflateStringToBuffer(context, chars, length, buffer, &size);
     864             396 :     buffer[size] = 0;
     865                 : 
     866             396 :     return oldOffset;
     867                 : }
     868                 : 
     869                 : int
     870               0 : Sprinter::printf(const char *fmt, ...)
     871                 : {
     872               0 :     InvariantChecker ic(this);
     873                 : 
     874               0 :     do {
     875                 :         va_list va;
     876               0 :         va_start(va, fmt);
     877               0 :         int i = vsnprintf(base + offset, size - offset, fmt, va);
     878               0 :         va_end(va);
     879                 : 
     880               0 :         if (i > -1 && (size_t) i < size - offset) {
     881               0 :             offset += i;
     882               0 :             return i;
     883                 :         }
     884               0 :     } while (realloc_(size * 2));
     885                 : 
     886               0 :     return -1;
     887                 : }
     888                 : 
     889                 : void
     890           19996 : Sprinter::setOffset(const char *end)
     891                 : {
     892           19996 :     JS_ASSERT(end >= base && end < base + size);
     893           19996 :     offset = end - base;
     894           19996 : }
     895                 : 
     896                 : void
     897          104432 : Sprinter::setOffset(ptrdiff_t off)
     898                 : {
     899          104432 :     JS_ASSERT(off >= 0 && (size_t) off < size);
     900          104432 :     offset = off;
     901          104432 : }
     902                 : 
     903                 : ptrdiff_t
     904          351420 : Sprinter::getOffset() const
     905                 : {
     906          351420 :     return offset;
     907                 : }
     908                 : 
     909                 : ptrdiff_t
     910           12510 : Sprinter::getOffsetOf(const char *string) const
     911                 : {
     912           12510 :     JS_ASSERT(string >= base && string < base + size);
     913           12510 :     return string - base;
     914                 : }
     915                 : 
     916                 : ptrdiff_t
     917          768877 : js::Sprint(Sprinter *sp, const char *format, ...)
     918                 : {
     919                 :     va_list ap;
     920                 :     char *bp;
     921                 :     ptrdiff_t offset;
     922                 : 
     923          768877 :     va_start(ap, format);
     924          768877 :     bp = JS_vsmprintf(format, ap);      /* XXX vsaprintf */
     925          768877 :     va_end(ap);
     926          768877 :     if (!bp) {
     927               0 :         JS_ReportOutOfMemory(sp->context);
     928               0 :         return -1;
     929                 :     }
     930          768877 :     offset = sp->put(bp);
     931          768877 :     sp->context->free_(bp);
     932          768877 :     return offset;
     933                 : }
     934                 : 
     935                 : const char js_EscapeMap[] = {
     936                 :     '\b', 'b',
     937                 :     '\f', 'f',
     938                 :     '\n', 'n',
     939                 :     '\r', 'r',
     940                 :     '\t', 't',
     941                 :     '\v', 'v',
     942                 :     '"',  '"',
     943                 :     '\'', '\'',
     944                 :     '\\', '\\',
     945                 :     '\0'
     946                 : };
     947                 : 
     948                 : #define DONT_ESCAPE     0x10000
     949                 : 
     950                 : static char *
     951           85958 : QuoteString(Sprinter *sp, JSString *str, uint32_t quote)
     952                 : {
     953                 :     /* Sample off first for later return value pointer computation. */
     954           85958 :     JSBool dontEscape = (quote & DONT_ESCAPE) != 0;
     955           85958 :     jschar qc = (jschar) quote;
     956           85958 :     ptrdiff_t offset = sp->getOffset();
     957           85958 :     if (qc && Sprint(sp, "%c", (char)qc) < 0)
     958               0 :         return NULL;
     959                 : 
     960           85958 :     const jschar *s = str->getChars(sp->context);
     961           85958 :     if (!s)
     962               0 :         return NULL;
     963           85958 :     const jschar *z = s + str->length();
     964                 : 
     965                 :     /* Loop control variables: z points at end of string sentinel. */
     966           86390 :     for (const jschar *t = s; t < z; s = ++t) {
     967                 :         /* Move t forward from s past un-quote-worthy characters. */
     968           84149 :         jschar c = *t;
     969          356674 :         while (c < 127 && isprint(c) && c != qc && c != '\\' && c != '\t') {
     970          272093 :             c = *++t;
     971          272093 :             if (t == z)
     972           83717 :                 break;
     973                 :         }
     974                 : 
     975                 :         {
     976           84149 :             ptrdiff_t len = t - s;
     977           84149 :             ptrdiff_t base = sp->getOffset();
     978           84149 :             char *bp = sp->reserve(len);
     979           84149 :             if (!bp)
     980               0 :                 return NULL;
     981                 : 
     982          356242 :             for (ptrdiff_t i = 0; i < len; ++i)
     983          272093 :                 (*sp)[base + i] = (char) *s++;
     984           84149 :             (*sp)[base + len] = 0;
     985                 :         }
     986                 : 
     987           84149 :         if (t == z)
     988           83717 :             break;
     989                 : 
     990                 :         /* Use js_EscapeMap, \u, or \x only if necessary. */
     991                 :         bool ok;
     992                 :         const char *e;
     993             432 :         if (!(c >> 8) && c != 0 && (e = strchr(js_EscapeMap, (int)c)) != NULL) {
     994                 :             ok = dontEscape
     995               0 :                  ? Sprint(sp, "%c", (char)c) >= 0
     996             432 :                  : Sprint(sp, "\\%c", e[1]) >= 0;
     997                 :         } else {
     998                 :             /*
     999                 :              * Use \x only if the high byte is 0 and we're in a quoted string,
    1000                 :              * because ECMA-262 allows only \u, not \x, in Unicode identifiers
    1001                 :              * (see bug 621814).
    1002                 :              */
    1003               0 :             ok = Sprint(sp, (qc && !(c >> 8)) ? "\\x%02X" : "\\u%04X", c) >= 0;
    1004                 :         }
    1005             432 :         if (!ok)
    1006               0 :             return NULL;
    1007                 :     }
    1008                 : 
    1009                 :     /* Sprint the closing quote and return the quoted string. */
    1010           85958 :     if (qc && Sprint(sp, "%c", (char)qc) < 0)
    1011               0 :         return NULL;
    1012                 : 
    1013                 :     /*
    1014                 :      * If we haven't Sprint'd anything yet, Sprint an empty string so that
    1015                 :      * the return below gives a valid result.
    1016                 :      */
    1017           85958 :     if (offset == sp->getOffset() && Sprint(sp, "") < 0)
    1018               0 :         return NULL;
    1019                 : 
    1020           85958 :     return sp->stringAt(offset);
    1021                 : }
    1022                 : 
    1023                 : JSString *
    1024           22915 : js_QuoteString(JSContext *cx, JSString *str, jschar quote)
    1025                 : {
    1026           45830 :     Sprinter sprinter(cx);
    1027           22915 :     if (!sprinter.init())
    1028               0 :         return NULL;
    1029           22915 :     char *bytes = QuoteString(&sprinter, str, quote);
    1030           22915 :     JSString *escstr = bytes ? JS_NewStringCopyZ(cx, bytes) : NULL;
    1031           22915 :     return escstr;
    1032                 : }
    1033                 : 
    1034                 : /************************************************************************/
    1035                 : 
    1036                 : /*
    1037                 :  * Information for associating the decompilation of each opcode in a script
    1038                 :  * with the place where it appears in the text for the decompilation of the
    1039                 :  * entire script (or the function containing the script).
    1040                 :  */
    1041                 : struct DecompiledOpcode
    1042               0 : {
    1043                 :     /* Decompiled text of this opcode. */
    1044                 :     const char *text;
    1045                 : 
    1046                 :     /* Bytecode into which this opcode was nested, or NULL. */
    1047                 :     jsbytecode *parent;
    1048                 : 
    1049                 :     /*
    1050                 :      * Offset into the parent's decompiled text of the decompiled text of this
    1051                 :      * opcode. For opcodes with a NULL parent, this was emitted directly into
    1052                 :      * the permanent output at the given offset.
    1053                 :      */
    1054                 :     int32_t parentOffset;
    1055                 : 
    1056                 :     /*
    1057                 :      * Surrounded by parentheses when printed, which parentOffset does not
    1058                 :      * account for.
    1059                 :      */
    1060                 :     bool parenthesized;
    1061                 : 
    1062               0 :     DecompiledOpcode()
    1063               0 :         : text(NULL), parent(NULL), parentOffset(-1), parenthesized(false)
    1064               0 :     {}
    1065                 : };
    1066                 : 
    1067                 : struct JSPrinter
    1068                 : {
    1069                 :     Sprinter        sprinter;       /* base class state */
    1070                 :     LifoAlloc       pool;           /* string allocation pool */
    1071                 :     unsigned           indent;         /* indentation in spaces */
    1072                 :     bool            pretty;         /* pretty-print: indent, use newlines */
    1073                 :     bool            grouped;        /* in parenthesized expression context */
    1074                 :     bool            strict;         /* in code marked strict */
    1075                 :     JSScript        *script;        /* script being printed */
    1076                 :     jsbytecode      *dvgfence;      /* DecompileExpression fencepost */
    1077                 :     jsbytecode      **pcstack;      /* DecompileExpression modeled stack */
    1078                 :     JSFunction      *fun;           /* interpreted function */
    1079                 :     Vector<JSAtom *> *localNames;   /* argument and variable names */
    1080                 :     Vector<DecompiledOpcode> *decompiledOpcodes; /* optional state for decompiled ops */
    1081                 : 
    1082               0 :     DecompiledOpcode &decompiled(jsbytecode *pc) {
    1083               0 :         JS_ASSERT(decompiledOpcodes);
    1084               0 :         return (*decompiledOpcodes)[pc - script->code];
    1085                 :     }
    1086                 : };
    1087                 : 
    1088                 : JSPrinter *
    1089           13156 : js_NewPrinter(JSContext *cx, const char *name, JSFunction *fun,
    1090                 :               unsigned indent, JSBool pretty, JSBool grouped, JSBool strict)
    1091                 : {
    1092           13156 :     JSPrinter *jp = (JSPrinter *) cx->malloc_(sizeof(JSPrinter));
    1093           13156 :     if (!jp)
    1094               0 :         return NULL;
    1095           13156 :     new (&jp->sprinter) Sprinter(cx);
    1096           13156 :     if (!jp->sprinter.init())
    1097               0 :         return NULL;
    1098           13156 :     new (&jp->pool) LifoAlloc(1024);
    1099           13156 :     jp->indent = indent;
    1100           13156 :     jp->pretty = !!pretty;
    1101           13156 :     jp->grouped = !!grouped;
    1102           13156 :     jp->strict = !!strict;
    1103           13156 :     jp->script = NULL;
    1104           13156 :     jp->dvgfence = NULL;
    1105           13156 :     jp->pcstack = NULL;
    1106           13156 :     jp->fun = fun;
    1107           13156 :     jp->localNames = NULL;
    1108           13156 :     jp->decompiledOpcodes = NULL;
    1109           13156 :     if (fun && fun->isInterpreted() && fun->script()->bindings.hasLocalNames()) {
    1110            8123 :         jp->localNames = cx->new_<Vector<JSAtom *> >(cx);
    1111            8123 :         if (!jp->localNames || !fun->script()->bindings.getLocalNameArray(cx, jp->localNames)) {
    1112               0 :             js_DestroyPrinter(jp);
    1113               0 :             return NULL;
    1114                 :         }
    1115                 :     }
    1116           13156 :     return jp;
    1117                 : }
    1118                 : 
    1119                 : void
    1120           13156 : js_DestroyPrinter(JSPrinter *jp)
    1121                 : {
    1122           13156 :     JSContext *cx = jp->sprinter.context;
    1123           13156 :     jp->pool.freeAll();
    1124           13156 :     Foreground::delete_(jp->localNames);
    1125           13156 :     jp->sprinter.Sprinter::~Sprinter();
    1126           13156 :     cx->free_(jp);
    1127           13156 : }
    1128                 : 
    1129                 : JSString *
    1130           11862 : js_GetPrinterOutput(JSPrinter *jp)
    1131                 : {
    1132           11862 :     JSContext *cx = jp->sprinter.context;
    1133           11862 :     return JS_NewStringCopyZ(cx, jp->sprinter.string());
    1134                 : }
    1135                 : 
    1136                 : /* Mark the parent and offset into the parent's text for a printed opcode. */
    1137                 : static inline void
    1138           46035 : UpdateDecompiledParent(JSPrinter *jp, jsbytecode *pc, jsbytecode *parent, size_t offset)
    1139                 : {
    1140           46035 :     if (jp->decompiledOpcodes && pc) {
    1141               0 :         jp->decompiled(pc).parent = parent;
    1142               0 :         jp->decompiled(pc).parentOffset = offset;
    1143                 :     }
    1144           46035 : }
    1145                 : 
    1146                 : /*
    1147                 :  * NB: Indexed by SRC_DECL_* defines from frontend/BytecodeEmitter.h.
    1148                 :  */
    1149                 : static const char * const var_prefix[] = {"var ", "const ", "let "};
    1150                 : 
    1151                 : static const char *
    1152           30823 : VarPrefix(jssrcnote *sn)
    1153                 : {
    1154           30823 :     if (sn && (SN_TYPE(sn) == SRC_DECL || SN_TYPE(sn) == SRC_GROUPASSIGN)) {
    1155            5373 :         ptrdiff_t type = js_GetSrcNoteOffset(sn, 0);
    1156            5373 :         if ((unsigned)type <= SRC_DECL_LET)
    1157            4806 :             return var_prefix[type];
    1158                 :     }
    1159           26017 :     return "";
    1160                 : }
    1161                 : 
    1162                 : int
    1163          111076 : js_printf(JSPrinter *jp, const char *format, ...)
    1164                 : {
    1165                 :     va_list ap;
    1166                 :     char *bp, *fp;
    1167                 :     int cc;
    1168                 : 
    1169          111076 :     if (*format == '\0')
    1170               0 :         return 0;
    1171                 : 
    1172          111076 :     va_start(ap, format);
    1173                 : 
    1174                 :     /* If pretty-printing, expand magic tab into a run of jp->indent spaces. */
    1175          111076 :     if (*format == '\t') {
    1176           41886 :         format++;
    1177           41886 :         if (jp->pretty && Sprint(&jp->sprinter, "%*s", jp->indent, "") < 0) {
    1178               0 :             va_end(ap);
    1179               0 :             return -1;
    1180                 :         }
    1181                 :     }
    1182                 : 
    1183                 :     /* Suppress newlines (must be once per format, at the end) if not pretty. */
    1184          111076 :     fp = NULL;
    1185          111076 :     if (!jp->pretty && format[cc = strlen(format) - 1] == '\n') {
    1186           38997 :         fp = JS_strdup(jp->sprinter.context, format);
    1187           38997 :         if (!fp) {
    1188               0 :             va_end(ap);
    1189               0 :             return -1;
    1190                 :         }
    1191           38997 :         fp[cc] = '\0';
    1192           38997 :         format = fp;
    1193                 :     }
    1194                 : 
    1195                 :     /* Allocate temp space, convert format, and put. */
    1196          111076 :     bp = JS_vsmprintf(format, ap);      /* XXX vsaprintf */
    1197          111076 :     if (fp) {
    1198           38997 :         jp->sprinter.context->free_(fp);
    1199           38997 :         format = NULL;
    1200                 :     }
    1201          111076 :     if (!bp) {
    1202               0 :         JS_ReportOutOfMemory(jp->sprinter.context);
    1203               0 :         va_end(ap);
    1204               0 :         return -1;
    1205                 :     }
    1206                 : 
    1207          111076 :     cc = strlen(bp);
    1208          111076 :     if (jp->sprinter.put(bp, (size_t)cc) < 0)
    1209               0 :         cc = -1;
    1210          111076 :     jp->sprinter.context->free_(bp);
    1211                 : 
    1212          111076 :     va_end(ap);
    1213          111076 :     return cc;
    1214                 : }
    1215                 : 
    1216                 : JSBool
    1217           28368 : js_puts(JSPrinter *jp, const char *s)
    1218                 : {
    1219           28368 :     return jp->sprinter.put(s) >= 0;
    1220                 : }
    1221                 : 
    1222                 : /************************************************************************/
    1223                 : 
    1224                 : struct SprintStack
    1225           18376 : {
    1226                 :     Sprinter    sprinter;       /* sprinter for postfix to infix buffering */
    1227                 :     ptrdiff_t   *offsets;       /* stack of postfix string offsets */
    1228                 :     jsbytecode  *opcodes;       /* parallel stack of JS opcodes */
    1229                 :     jsbytecode  **bytecodes;    /* actual script bytecode pushing the value */
    1230                 :     unsigned       top;            /* top of stack index */
    1231                 :     unsigned       inArrayInit;    /* array initialiser/comprehension level */
    1232                 :     JSBool      inGenExp;       /* in generator expression */
    1233                 :     JSPrinter   *printer;       /* permanent output goes here */
    1234                 : 
    1235           18376 :     explicit SprintStack(JSContext *cx)
    1236                 :       : sprinter(cx), offsets(NULL),
    1237                 :         opcodes(NULL), bytecodes(NULL), top(0), inArrayInit(0),
    1238           18376 :         inGenExp(JS_FALSE), printer(NULL)
    1239           18376 :     { }
    1240                 : };
    1241                 : 
    1242                 : /*
    1243                 :  * Set the decompiled text of an opcode, according to an offset into the
    1244                 :  * print stack's sprinter buffer.
    1245                 :  */
    1246                 : static inline bool
    1247           67012 : UpdateDecompiledText(SprintStack *ss, jsbytecode *pc, ptrdiff_t todo)
    1248                 : {
    1249           67012 :     JSPrinter *jp = ss->printer;
    1250                 : 
    1251           67012 :     if (jp->decompiledOpcodes && jp->decompiled(pc).text == NULL) {
    1252               0 :         const char *text = ss->sprinter.stringAt(todo);
    1253               0 :         size_t len = strlen(text) + 1;
    1254                 : 
    1255               0 :         char *ntext = ss->printer->pool.newArrayUninitialized<char>(len);
    1256               0 :         if (!ntext) {
    1257               0 :             js_ReportOutOfMemory(ss->sprinter.context);
    1258               0 :             return false;
    1259                 :         }
    1260                 : 
    1261               0 :         js_memcpy(ntext, text, len);
    1262               0 :         jp->decompiled(pc).text = const_cast<const char *>(ntext);
    1263                 :     }
    1264                 : 
    1265           67012 :     return true;
    1266                 : }
    1267                 : 
    1268                 : static inline const char *
    1269           26109 : SprintDupeStr(SprintStack *ss, const char *str)
    1270                 : {
    1271           26109 :     size_t len = strlen(str) + 1;
    1272                 : 
    1273           26109 :     const char *nstr = ss->printer->pool.newArrayUninitialized<char>(len);
    1274           26109 :     if (nstr) {
    1275           26109 :         js_memcpy((char *) nstr, str, len);
    1276                 :     } else {
    1277               0 :         js_ReportOutOfMemory(ss->sprinter.context);
    1278               0 :         nstr = "";
    1279                 :     }
    1280                 : 
    1281           26109 :     return nstr;
    1282                 : }
    1283                 : 
    1284                 : /* Place an opcode's decompiled text into a printer's permanent output. */
    1285                 : static inline void
    1286           17028 : SprintOpcodePermanent(JSPrinter *jp, const char *str, jsbytecode *pc)
    1287                 : {
    1288           17028 :     ptrdiff_t offset = jp->sprinter.getOffset();
    1289           17028 :     UpdateDecompiledParent(jp, pc, NULL, offset);
    1290           17028 :     js_printf(jp, "%s", str);
    1291           17028 : }
    1292                 : 
    1293                 : /*
    1294                 :  * Place an opcode's decompiled text into the printed output for another
    1295                 :  * opcode parentpc, where startOffset indicates the printer offset for the
    1296                 :  * start of parentpc.
    1297                 :  */
    1298                 : static inline void
    1299           27783 : SprintOpcode(SprintStack *ss, const char *str, jsbytecode *pc,
    1300                 :              jsbytecode *parentpc, ptrdiff_t startOffset)
    1301                 : {
    1302           27783 :     if (startOffset < 0) {
    1303               0 :         JS_ASSERT(ss->sprinter.context->isExceptionPending());
    1304               0 :         return;
    1305                 :     }
    1306           27783 :     ptrdiff_t offset = ss->sprinter.getOffset();
    1307           27783 :     UpdateDecompiledParent(ss->printer, pc, parentpc, offset - startOffset);
    1308           27783 :     ss->sprinter.put(str);
    1309                 : }
    1310                 : 
    1311                 : /*
    1312                 :  * Copy the decompiled text for an opcode to all other ops which it was
    1313                 :  * decomposed into.
    1314                 :  */
    1315                 : static inline void
    1316              72 : CopyDecompiledTextForDecomposedOp(JSPrinter *jp, jsbytecode *pc)
    1317                 : {
    1318              72 :     JS_ASSERT(js_CodeSpec[*pc].format & JOF_DECOMPOSE);
    1319                 : 
    1320              72 :     if (jp->decompiledOpcodes) {
    1321               0 :         size_t len = GetDecomposeLength(pc, js_CodeSpec[*pc].length);
    1322                 : 
    1323               0 :         const char *text = jp->decompiled(pc).text;
    1324                 : 
    1325               0 :         jsbytecode *pc2 = pc + GetBytecodeLength(pc);
    1326               0 :         for (; pc2 < pc + len; pc2 += GetBytecodeLength(pc2)) {
    1327               0 :             jp->decompiled(pc2).text = text;
    1328               0 :             jp->decompiled(pc2).parent = pc;
    1329               0 :             jp->decompiled(pc2).parentOffset = 0;
    1330                 :         }
    1331                 :     }
    1332              72 : }
    1333                 : 
    1334                 : /*
    1335                 :  * Find the depth of the operand stack when the interpreter reaches the given
    1336                 :  * pc in script. pcstack must have space for least script->depth elements. On
    1337                 :  * return it will contain pointers to opcodes that populated the interpreter's
    1338                 :  * current operand stack.
    1339                 :  *
    1340                 :  * This function cannot raise an exception or error. However, due to a risk of
    1341                 :  * potential bugs when modeling the stack, the function returns -1 if it
    1342                 :  * detects an inconsistency in the model. Such an inconsistency triggers an
    1343                 :  * assert in a debug build.
    1344                 :  */
    1345                 : static int
    1346                 : ReconstructPCStack(JSContext *cx, JSScript *script, jsbytecode *pc,
    1347                 :                    jsbytecode **pcstack, jsbytecode **lastDecomposedPC);
    1348                 : 
    1349                 : #define FAILED_EXPRESSION_DECOMPILER ((char *) 1)
    1350                 : 
    1351                 : /*
    1352                 :  * Decompile a part of expression up to the given pc. The function returns
    1353                 :  * NULL on out-of-memory, or the FAILED_EXPRESSION_DECOMPILER sentinel when
    1354                 :  * the decompiler fails due to a bug and/or unimplemented feature, or the
    1355                 :  * decompiled string on success.
    1356                 :  */
    1357                 : static char *
    1358                 : DecompileExpression(JSContext *cx, JSScript *script, JSFunction *fun,
    1359                 :                     jsbytecode *pc);
    1360                 : 
    1361                 : /*
    1362                 :  * Get a stacked offset from ss->sprinter.base, or if the stacked value |off|
    1363                 :  * is negative, fetch the generating pc from printer->pcstack[-2 - off] and
    1364                 :  * decompile the code that generated the missing value.  This is used when
    1365                 :  * reporting errors, where the model stack will lack |pcdepth| non-negative
    1366                 :  * offsets (see DecompileExpression and DecompileCode).
    1367                 :  *
    1368                 :  * If the stacked offset is -1, return 0 to index the NUL padding at the start
    1369                 :  * of ss->sprinter.base.  If this happens, it means there is a decompiler bug
    1370                 :  * to fix, but it won't violate memory safety.
    1371                 :  */
    1372                 : static ptrdiff_t
    1373           93256 : GetOff(SprintStack *ss, unsigned i)
    1374                 : {
    1375                 :     ptrdiff_t off;
    1376                 :     jsbytecode *pc;
    1377                 :     char *bytes;
    1378                 : 
    1379           93256 :     off = ss->offsets[i];
    1380           93256 :     if (off >= 0)
    1381           93202 :         return off;
    1382                 : 
    1383              54 :     JS_ASSERT(ss->printer->pcstack);
    1384              54 :     if (off <= -2 && ss->printer->pcstack) {
    1385              45 :         pc = ss->printer->pcstack[-2 - off];
    1386                 :         bytes = DecompileExpression(ss->sprinter.context, ss->printer->script,
    1387              45 :                                     ss->printer->fun, pc);
    1388              45 :         if (!bytes)
    1389               0 :             return 0;
    1390              45 :         if (bytes != FAILED_EXPRESSION_DECOMPILER) {
    1391               9 :             off = ss->sprinter.put(bytes);
    1392               9 :             if (off < 0)
    1393               0 :                 off = 0;
    1394               9 :             ss->offsets[i] = off;
    1395               9 :             ss->sprinter.context->free_(bytes);
    1396               9 :             return off;
    1397                 :         }
    1398                 : 
    1399              36 :         if (!*ss->sprinter.string()) {
    1400              36 :             memset(ss->sprinter.stringAt(0), 0, ss->sprinter.getOffset());
    1401              36 :             ss->offsets[i] = -1;
    1402                 :         }
    1403                 :     }
    1404              45 :     return 0;
    1405                 : }
    1406                 : 
    1407                 : static const char *
    1408            8019 : GetStr(SprintStack *ss, unsigned i)
    1409                 : {
    1410            8019 :     ptrdiff_t off = GetOff(ss, i);
    1411            8019 :     return ss->sprinter.stringAt(off);
    1412                 : }
    1413                 : 
    1414                 : /*
    1415                 :  * Gap between stacked strings to allow for insertion of parens and commas
    1416                 :  * when auto-parenthesizing expressions and decompiling array initialisers.
    1417                 :  */
    1418                 : #define PAREN_SLOP      (2 + 1)
    1419                 : 
    1420                 : /* Fake opcodes (see jsopcode.h) must not overflow unsigned 8-bit space. */
    1421                 : JS_STATIC_ASSERT(JSOP_FAKE_LIMIT <= 255);
    1422                 : 
    1423                 : static inline void
    1424           88738 : AddParenSlop(SprintStack *ss)
    1425                 : {
    1426           88738 :     ss->sprinter.reserveAndClear(PAREN_SLOP);
    1427           88738 : }
    1428                 : 
    1429                 : static JSBool
    1430           88729 : PushOff(SprintStack *ss, ptrdiff_t off, JSOp op, jsbytecode *pc = NULL)
    1431                 : {
    1432                 :     unsigned top;
    1433                 : 
    1434                 :     /* ss->top points to the next free slot; be paranoid about overflow. */
    1435           88729 :     top = ss->top;
    1436           88729 :     JS_ASSERT(top < StackDepth(ss->printer->script));
    1437           88729 :     if (top >= StackDepth(ss->printer->script)) {
    1438               0 :         JS_ReportOutOfMemory(ss->sprinter.context);
    1439               0 :         return JS_FALSE;
    1440                 :     }
    1441                 : 
    1442                 :     /* The opcodes stack must contain real bytecodes that index js_CodeSpec. */
    1443           88729 :     ss->offsets[top] = off;
    1444           88729 :     ss->opcodes[top] = jsbytecode((op == JSOP_GETPROP2) ? JSOP_GETPROP
    1445                 :                                 : (op == JSOP_GETELEM2) ? JSOP_GETELEM
    1446           88729 :                                 : op);
    1447           88729 :     ss->bytecodes[top] = pc;
    1448           88729 :     ss->top = ++top;
    1449                 : 
    1450           88729 :     AddParenSlop(ss);
    1451           88729 :     return JS_TRUE;
    1452                 : }
    1453                 : 
    1454                 : static bool
    1455            1386 : PushStr(SprintStack *ss, const char *str, JSOp op)
    1456                 : {
    1457            1386 :     ptrdiff_t off = ss->sprinter.put(str);
    1458            1386 :     if (off < 0)
    1459               0 :         return false;
    1460            1386 :     return PushOff(ss, off, op);
    1461                 : }
    1462                 : 
    1463                 : static ptrdiff_t
    1464           78361 : PopOffPrec(SprintStack *ss, uint8_t prec, jsbytecode **ppc = NULL)
    1465                 : {
    1466                 :     unsigned top;
    1467                 :     const JSCodeSpec *topcs;
    1468                 :     ptrdiff_t off;
    1469                 : 
    1470           78361 :     if (ppc)
    1471           46674 :         *ppc = NULL;
    1472                 : 
    1473                 :     /* ss->top points to the next free slot; be paranoid about underflow. */
    1474           78361 :     top = ss->top;
    1475           78361 :     JS_ASSERT(top != 0);
    1476           78361 :     if (top == 0)
    1477               0 :         return 0;
    1478                 : 
    1479           78361 :     ss->top = --top;
    1480           78361 :     off = GetOff(ss, top);
    1481           78361 :     topcs = &js_CodeSpec[ss->opcodes[top]];
    1482                 : 
    1483           78361 :     jsbytecode *pc = ss->bytecodes[top];
    1484           78361 :     if (ppc)
    1485           46674 :         *ppc = pc;
    1486                 : 
    1487           78361 :     if (topcs->prec != 0 && topcs->prec < prec) {
    1488             864 :         ss->offsets[top] = off - 2;
    1489             864 :         ss->sprinter.setOffset(off - 2);
    1490             864 :         off = Sprint(&ss->sprinter, "(%s)", ss->sprinter.stringAt(off));
    1491            1728 :         if (ss->printer->decompiledOpcodes && pc)
    1492               0 :             ss->printer->decompiled(pc).parenthesized = true;
    1493                 :     } else {
    1494           77497 :         ss->sprinter.setOffset(off);
    1495                 :     }
    1496           78361 :     return off;
    1497                 : }
    1498                 : 
    1499                 : static const char *
    1500           72243 : PopStrPrec(SprintStack *ss, uint8_t prec, jsbytecode **ppc = NULL)
    1501                 : {
    1502                 :     ptrdiff_t off;
    1503                 : 
    1504           72243 :     off = PopOffPrec(ss, prec, ppc);
    1505           72243 :     return ss->sprinter.stringAt(off);
    1506                 : }
    1507                 : 
    1508                 : /*
    1509                 :  * As for PopStrPrec, but duplicates the string into the printer's arena.
    1510                 :  * Strings returned by PopStrPrec are otherwise invalidated if any new text
    1511                 :  * is printed into ss.
    1512                 :  */
    1513                 : static const char *
    1514           18585 : PopStrPrecDupe(SprintStack *ss, uint8_t prec, jsbytecode **ppc = NULL)
    1515                 : {
    1516           18585 :     const char *str = PopStrPrec(ss, prec, ppc);
    1517           18585 :     return SprintDupeStr(ss, str);
    1518                 : }
    1519                 : 
    1520                 : static ptrdiff_t
    1521            6118 : PopOff(SprintStack *ss, JSOp op, jsbytecode **ppc = NULL)
    1522                 : {
    1523            6118 :     return PopOffPrec(ss, js_CodeSpec[op].prec, ppc);
    1524                 : }
    1525                 : 
    1526                 : static const char *
    1527           50274 : PopStr(SprintStack *ss, JSOp op, jsbytecode **ppc = NULL)
    1528                 : {
    1529           50274 :     return PopStrPrec(ss, js_CodeSpec[op].prec, ppc);
    1530                 : }
    1531                 : 
    1532                 : static const char *
    1533           15201 : PopStrDupe(SprintStack *ss, JSOp op, jsbytecode **ppc = NULL)
    1534                 : {
    1535           15201 :     return PopStrPrecDupe(ss, js_CodeSpec[op].prec, ppc);
    1536                 : }
    1537                 : 
    1538                 : /*
    1539                 :  * Pop a condition expression for if/while. JSOP_IFEQ's precedence forces
    1540                 :  * extra parens around assignment, which avoids a strict-mode warning.
    1541                 :  */
    1542                 : static const char *
    1543            1404 : PopCondStr(SprintStack *ss, jsbytecode **ppc = NULL)
    1544                 : {
    1545            1404 :     JSOp op = (js_CodeSpec[ss->opcodes[ss->top - 1]].format & JOF_SET)
    1546                 :               ? JSOP_IFEQ
    1547            1404 :               : JSOP_NOP;
    1548            1404 :     return PopStr(ss, op, ppc);
    1549                 : }
    1550                 : 
    1551                 : static inline bool
    1552            1602 : IsInitializerOp(unsigned char op)
    1553                 : {
    1554            1602 :     return op == JSOP_NEWINIT || op == JSOP_NEWARRAY || op == JSOP_NEWOBJECT;
    1555                 : }
    1556                 : 
    1557                 : struct TableEntry {
    1558                 :     jsval       key;
    1559                 :     ptrdiff_t   offset;
    1560                 :     JSAtom      *label;
    1561                 :     int         order;          /* source order for stable tableswitch sort */
    1562                 : };
    1563                 : 
    1564                 : inline bool
    1565              27 : CompareTableEntries(const TableEntry &a, const TableEntry &b, bool *lessOrEqualp)
    1566                 : {
    1567              27 :     *lessOrEqualp = (a.offset != b.offset) ? a.offset <= b.offset : a.order <= b.order;
    1568              27 :     return true;
    1569                 : }
    1570                 : 
    1571                 : static ptrdiff_t
    1572               9 : SprintDoubleValue(Sprinter *sp, jsval v, JSOp *opp)
    1573                 : {
    1574                 :     double d;
    1575                 :     ptrdiff_t todo;
    1576                 :     char *s;
    1577                 : 
    1578               9 :     JS_ASSERT(JSVAL_IS_DOUBLE(v));
    1579               9 :     d = JSVAL_TO_DOUBLE(v);
    1580               9 :     if (JSDOUBLE_IS_NEGZERO(d)) {
    1581               0 :         todo = sp->put("-0");
    1582               0 :         *opp = JSOP_NEG;
    1583               9 :     } else if (!JSDOUBLE_IS_FINITE(d)) {
    1584                 :         /* Don't use Infinity and NaN, as local variables may shadow them. */
    1585               9 :         todo = sp->put(JSDOUBLE_IS_NaN(d)
    1586                 :                        ? "0 / 0"
    1587                 :                        : (d < 0)
    1588                 :                        ? "1 / -0"
    1589               9 :                        : "1 / 0");
    1590               9 :         *opp = JSOP_DIV;
    1591                 :     } else {
    1592               0 :         ToCStringBuf cbuf;
    1593               0 :         s = NumberToCString(sp->context, &cbuf, d);
    1594               0 :         if (!s) {
    1595               0 :             JS_ReportOutOfMemory(sp->context);
    1596               0 :             return -1;
    1597                 :         }
    1598               0 :         JS_ASSERT(strcmp(s, "Infinity") &&
    1599                 :                   (*s != '-' ||
    1600                 :                    strcmp(s + 1, "Infinity")) &&
    1601               0 :                   strcmp(s, "NaN"));
    1602               0 :         todo = Sprint(sp, s);
    1603                 :     }
    1604               9 :     return todo;
    1605                 : }
    1606                 : 
    1607                 : static jsbytecode *
    1608                 : Decompile(SprintStack *ss, jsbytecode *pc, int nb);
    1609                 : 
    1610                 : static JSBool
    1611             117 : DecompileSwitch(SprintStack *ss, TableEntry *table, unsigned tableLength,
    1612                 :                 jsbytecode *pc, ptrdiff_t switchLength,
    1613                 :                 ptrdiff_t defaultOffset, JSBool isCondSwitch)
    1614                 : {
    1615                 :     JSContext *cx;
    1616                 :     JSPrinter *jp;
    1617                 :     ptrdiff_t off, off2, diff, caseExprOff, todo;
    1618                 :     const char *rval;
    1619                 :     unsigned i;
    1620                 :     jsval key;
    1621                 :     JSString *str;
    1622                 : 
    1623             117 :     cx = ss->sprinter.context;
    1624             117 :     jp = ss->printer;
    1625                 : 
    1626                 :     jsbytecode *lvalpc;
    1627             117 :     const char *lval = PopStr(ss, JSOP_NOP, &lvalpc);
    1628                 : 
    1629                 :     /* JSOP_CONDSWITCH doesn't pop, unlike JSOP_{LOOKUP,TABLE}SWITCH. */
    1630             117 :     if (isCondSwitch)
    1631               0 :         ss->top++;
    1632                 : 
    1633             117 :     js_printf(jp, "\tswitch (");
    1634             117 :     SprintOpcodePermanent(jp, lval, lvalpc);
    1635             117 :     js_printf(jp, ") {\n");
    1636                 : 
    1637             117 :     if (tableLength) {
    1638             117 :         diff = table[0].offset - defaultOffset;
    1639             117 :         if (diff > 0) {
    1640               0 :             jp->indent += 2;
    1641               0 :             js_printf(jp, "\t%s:\n", js_default_str);
    1642               0 :             jp->indent += 2;
    1643               0 :             if (!Decompile(ss, pc + defaultOffset, diff))
    1644               0 :                 return JS_FALSE;
    1645               0 :             jp->indent -= 4;
    1646                 :         }
    1647                 : 
    1648             117 :         caseExprOff = isCondSwitch ? JSOP_CONDSWITCH_LENGTH : 0;
    1649                 : 
    1650             270 :         for (i = 0; i < tableLength; i++) {
    1651             153 :             off = table[i].offset;
    1652             153 :             off2 = (i + 1 < tableLength) ? table[i + 1].offset : switchLength;
    1653                 : 
    1654             153 :             key = table[i].key;
    1655             153 :             if (isCondSwitch) {
    1656                 :                 ptrdiff_t nextCaseExprOff;
    1657                 : 
    1658                 :                 /*
    1659                 :                  * key encodes the JSOP_CASE bytecode's offset from switchtop.
    1660                 :                  * The next case expression follows immediately, unless we are
    1661                 :                  * at the last case.
    1662                 :                  */
    1663               0 :                 nextCaseExprOff = (ptrdiff_t)JSVAL_TO_INT(key);
    1664               0 :                 nextCaseExprOff += js_CodeSpec[pc[nextCaseExprOff]].length;
    1665               0 :                 jp->indent += 2;
    1666               0 :                 if (!Decompile(ss, pc + caseExprOff, nextCaseExprOff - caseExprOff))
    1667               0 :                     return JS_FALSE;
    1668               0 :                 caseExprOff = nextCaseExprOff;
    1669                 : 
    1670                 :                 /* Balance the stack as if this JSOP_CASE matched. */
    1671               0 :                 --ss->top;
    1672                 :             } else {
    1673                 :                 /*
    1674                 :                  * key comes from an atom, not the decompiler, so we need to
    1675                 :                  * quote it if it's a string literal.  But if table[i].label
    1676                 :                  * is non-null, key was constant-propagated and label is the
    1677                 :                  * name of the const we should show as the case label.  We set
    1678                 :                  * key to undefined so this identifier is escaped, if required
    1679                 :                  * by non-ASCII characters, but not quoted, by QuoteString.
    1680                 :                  */
    1681             153 :                 todo = -1;
    1682             153 :                 if (table[i].label) {
    1683               0 :                     str = table[i].label;
    1684               0 :                     key = JSVAL_VOID;
    1685             153 :                 } else if (JSVAL_IS_DOUBLE(key)) {
    1686                 :                     JSOp junk;
    1687                 : 
    1688               0 :                     todo = SprintDoubleValue(&ss->sprinter, key, &junk);
    1689               0 :                     if (todo < 0)
    1690               0 :                         return JS_FALSE;
    1691               0 :                     str = NULL;
    1692                 :                 } else {
    1693             153 :                     str = ToString(cx, key);
    1694             153 :                     if (!str)
    1695               0 :                         return JS_FALSE;
    1696                 :                 }
    1697             153 :                 if (todo >= 0) {
    1698               0 :                     rval = ss->sprinter.stringAt(todo);
    1699                 :                 } else {
    1700                 :                     rval = QuoteString(&ss->sprinter, str, (jschar)
    1701             153 :                                        (JSVAL_IS_STRING(key) ? '"' : 0));
    1702             153 :                     if (!rval)
    1703               0 :                         return JS_FALSE;
    1704                 :                 }
    1705             153 :                 ss->sprinter.setOffset(rval);
    1706             153 :                 jp->indent += 2;
    1707             153 :                 js_printf(jp, "\tcase %s:\n", rval);
    1708                 :             }
    1709                 : 
    1710             153 :             jp->indent += 2;
    1711             153 :             if (off <= defaultOffset && defaultOffset < off2) {
    1712              81 :                 diff = defaultOffset - off;
    1713              81 :                 if (diff != 0) {
    1714              54 :                     if (!Decompile(ss, pc + off, diff))
    1715               0 :                         return JS_FALSE;
    1716              54 :                     off = defaultOffset;
    1717                 :                 }
    1718              81 :                 jp->indent -= 2;
    1719              81 :                 js_printf(jp, "\t%s:\n", js_default_str);
    1720              81 :                 jp->indent += 2;
    1721                 :             }
    1722             153 :             if (!Decompile(ss, pc + off, off2 - off))
    1723               0 :                 return JS_FALSE;
    1724             153 :             jp->indent -= 4;
    1725                 : 
    1726                 :             /* Re-balance as if last JSOP_CASE or JSOP_DEFAULT mismatched. */
    1727             153 :             if (isCondSwitch)
    1728               0 :                 ++ss->top;
    1729                 :         }
    1730                 :     }
    1731                 : 
    1732             117 :     if (defaultOffset == switchLength) {
    1733              36 :         jp->indent += 2;
    1734              36 :         js_printf(jp, "\t%s:;\n", js_default_str);
    1735              36 :         jp->indent -= 2;
    1736                 :     }
    1737             117 :     js_printf(jp, "\t}\n");
    1738                 : 
    1739                 :     /* By the end of a JSOP_CONDSWITCH, the discriminant has been popped. */
    1740             117 :     if (isCondSwitch)
    1741               0 :         --ss->top;
    1742             117 :     return JS_TRUE;
    1743                 : }
    1744                 : 
    1745                 : #define LOCAL_ASSERT_CUSTOM(expr, BAD_EXIT)                                   \
    1746                 :     JS_BEGIN_MACRO                                                            \
    1747                 :         JS_ASSERT(expr);                                                      \
    1748                 :         if (!(expr)) { BAD_EXIT; }                                            \
    1749                 :     JS_END_MACRO
    1750                 : 
    1751                 : #define LOCAL_ASSERT_RV(expr, rv)                                             \
    1752                 :     LOCAL_ASSERT_CUSTOM(expr, return (rv))
    1753                 : 
    1754                 : static JSAtom *
    1755           25952 : GetArgOrVarAtom(JSPrinter *jp, unsigned slot)
    1756                 : {
    1757           25952 :     LOCAL_ASSERT_RV(jp->fun, NULL);
    1758           25952 :     LOCAL_ASSERT_RV(slot < jp->fun->script()->bindings.countLocalNames(), NULL);
    1759           25952 :     JSAtom *name = (*jp->localNames)[slot];
    1760                 : #if !JS_HAS_DESTRUCTURING
    1761                 :     LOCAL_ASSERT_RV(name, NULL);
    1762                 : #endif
    1763           25952 :     return name;
    1764                 : }
    1765                 : 
    1766                 : #define LOCAL_ASSERT(expr)      LOCAL_ASSERT_RV(expr, "")
    1767                 : 
    1768                 : static const char *
    1769              27 : GetLocalInSlot(SprintStack *ss, int i, int slot, JSObject *obj)
    1770                 : {
    1771              45 :     for (Shape::Range r(obj->lastProperty()); !r.empty(); r.popFront()) {
    1772              27 :         const Shape &shape = r.front();
    1773                 : 
    1774              27 :         if (shape.shortid() == slot) {
    1775                 :             /* Ignore the empty destructuring dummy. */
    1776              27 :             if (!JSID_IS_ATOM(shape.propid()))
    1777              18 :                 continue;
    1778                 : 
    1779               9 :             JSAtom *atom = JSID_TO_ATOM(shape.propid());
    1780               9 :             const char *rval = QuoteString(&ss->sprinter, atom, 0);
    1781               9 :             if (!rval)
    1782               0 :                 return NULL;
    1783                 : 
    1784               9 :             ss->sprinter.setOffset(rval);
    1785               9 :             return rval;
    1786                 :         }
    1787                 :     }
    1788                 : 
    1789              18 :     return GetStr(ss, i);
    1790                 : }
    1791                 : 
    1792                 : const char *
    1793            8217 : GetLocal(SprintStack *ss, int i)
    1794                 : {
    1795            8217 :     ptrdiff_t off = ss->offsets[i];
    1796            8217 :     if (off >= 0)
    1797            8190 :         return ss->sprinter.stringAt(off);
    1798                 : 
    1799                 :     /*
    1800                 :      * We must be called from js_DecompileValueGenerator (via Decompile) when
    1801                 :      * dereferencing a local that's undefined or null. Search script->objects
    1802                 :      * for the block containing this local by its stack index, i.
    1803                 :      *
    1804                 :      * In case of destructuring's use of JSOP_GETLOCAL, however, there may be
    1805                 :      * no such local. This could mean no blocks (no script objects at all, or
    1806                 :      * none of the script's object literals are blocks), or the stack slot i is
    1807                 :      * not in a block. In either case, return GetStr(ss, i).
    1808                 :      */
    1809              27 :     JSScript *script = ss->printer->script;
    1810              27 :     if (!JSScript::isValidOffset(script->objectsOffset))
    1811               0 :         return GetStr(ss, i);
    1812                 : 
    1813                 :     // In case of a let variable, the stack points to a JSOP_ENTERBLOCK opcode.
    1814                 :     // Get the object number from the block instead of iterating all objects and
    1815                 :     // hoping the right object is found.
    1816              27 :     if (off <= -2 && ss->printer->pcstack) {
    1817              18 :         jsbytecode *pc = ss->printer->pcstack[-2 - off];
    1818                 : 
    1819              18 :         JS_ASSERT(ss->printer->script->code <= pc);
    1820              18 :         JS_ASSERT(pc < (ss->printer->script->code + ss->printer->script->length));
    1821                 : 
    1822              18 :         if (JSOP_ENTERBLOCK == (JSOp)*pc) {
    1823               9 :             JSObject *obj = script->getObject(GET_UINT32_INDEX(pc));
    1824                 : 
    1825               9 :             if (obj->isBlock()) {
    1826               9 :                 uint32_t depth = obj->asBlock().stackDepth();
    1827               9 :                 uint32_t count = obj->asBlock().slotCount();
    1828               9 :                 if (uint32_t(i - depth) < uint32_t(count))
    1829               9 :                     return GetLocalInSlot(ss, i, int(i - depth), obj);
    1830                 :             }
    1831                 :         }
    1832                 :     }
    1833                 : 
    1834                 :     // Iterate over all objects.
    1835              36 :     for (jsatomid j = 0, n = script->objects()->length; j != n; j++) {
    1836              36 :         JSObject *obj = script->getObject(j);
    1837                 : 
    1838              36 :         if (obj->isBlock()) {
    1839              18 :             uint32_t depth = obj->asBlock().stackDepth();
    1840              18 :             uint32_t count = obj->asBlock().slotCount();
    1841              18 :             if (uint32_t(i - depth) < uint32_t(count))
    1842              18 :                 return GetLocalInSlot(ss, i, int(i - depth), obj);
    1843                 :         }
    1844                 :     }
    1845                 : 
    1846               0 :     return GetStr(ss, i);
    1847                 : }
    1848                 : 
    1849                 : #undef LOCAL_ASSERT
    1850                 : 
    1851                 : static JSBool
    1852           14715 : IsVarSlot(JSPrinter *jp, jsbytecode *pc, int *indexp)
    1853                 : {
    1854                 :     unsigned slot;
    1855                 : 
    1856           14715 :     slot = GET_SLOTNO(pc);
    1857           14715 :     if (slot < jp->script->nfixed) {
    1858                 :         /* The slot refers to a variable with name stored in jp->localNames. */
    1859            6012 :         *indexp = jp->fun->nargs + slot;
    1860            6012 :         return JS_TRUE;
    1861                 :     }
    1862                 : 
    1863                 :     /* We have a local which index is relative to the stack base. */
    1864            8703 :     slot -= jp->script->nfixed;
    1865            8703 :     JS_ASSERT(slot < StackDepth(jp->script));
    1866            8703 :     *indexp = slot;
    1867            8703 :     return JS_FALSE;
    1868                 : }
    1869                 : 
    1870                 : #define LOAD_ATOM(PCOFF) (atom = (jp->script->getAtom(GET_UINT32_INDEX((pc) + PCOFF))))
    1871                 : 
    1872                 : typedef Vector<JSAtom *, 8> AtomVector;
    1873                 : typedef AtomVector::Range AtomRange;
    1874                 : 
    1875                 : #if JS_HAS_DESTRUCTURING
    1876                 : 
    1877                 : #define LOCAL_ASSERT(expr)  LOCAL_ASSERT_RV(expr, NULL)
    1878                 : #define LOAD_OP_DATA(pc)    (oplen = (cs = &js_CodeSpec[op=(JSOp)*pc])->length)
    1879                 : 
    1880                 : static jsbytecode *
    1881                 : DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc,
    1882                 :                        AtomRange *letNames = NULL);
    1883                 : 
    1884                 : /*
    1885                 :  * Decompile a single element of a compound {}/[] destructuring lhs, sprinting
    1886                 :  * the result in-place (without pushing/popping the stack) and advancing the pc
    1887                 :  * to either the next element or the final pop.
    1888                 :  *
    1889                 :  * For normal (SRC_DESTRUCT) destructuring, the names of assigned/initialized
    1890                 :  * variables are read from their slots. However, for SRC_DESTRUCTLET, the slots
    1891                 :  * have not been pushed yet; the caller must pass the names to use via
    1892                 :  * 'letNames'. Each variable initialized in this destructuring lhs results in
    1893                 :  * popping a name from 'letNames'.
    1894                 :  */
    1895                 : static jsbytecode *
    1896            8901 : DecompileDestructuringLHS(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, JSBool *hole,
    1897                 :                           AtomRange *letNames = NULL)
    1898                 : {
    1899                 :     JSPrinter *jp;
    1900                 :     JSOp op;
    1901                 :     const JSCodeSpec *cs;
    1902                 :     unsigned oplen;
    1903                 :     int i;
    1904                 :     const char *lval, *xval;
    1905                 :     JSAtom *atom;
    1906                 : 
    1907            8901 :     *hole = JS_FALSE;
    1908            8901 :     jp = ss->printer;
    1909            8901 :     LOAD_OP_DATA(pc);
    1910                 : 
    1911            8901 :     switch (op) {
    1912                 :       case JSOP_POP:
    1913            1026 :         *hole = JS_TRUE;
    1914            1026 :         if (ss->sprinter.put(", ", 2) < 0)
    1915               0 :             return NULL;
    1916            1026 :         break;
    1917                 : 
    1918                 :       case JSOP_PICK:
    1919                 :         /*
    1920                 :          * For 'let ([x, y] = y)', the emitter generates
    1921                 :          *
    1922                 :          *     push evaluation of y
    1923                 :          *     dup
    1924                 :          *   1 one
    1925                 :          *   2 getelem
    1926                 :          *   3 pick
    1927                 :          *   4 two
    1928                 :          *     getelem
    1929                 :          *     pick
    1930                 :          *     pop
    1931                 :          *
    1932                 :          * Thus 'x' consists of 1 - 3. The caller (DecompileDestructuring or
    1933                 :          * DecompileGroupAssignment) will have taken care of 1 - 2, so pc is
    1934                 :          * now pointing at 3. The pick indicates a primitive let var init so
    1935                 :          * pop a name and advance the pc to 4.
    1936                 :          */
    1937            1494 :         LOCAL_ASSERT(letNames && !letNames->empty());
    1938            1494 :         if (!QuoteString(&ss->sprinter, letNames->popCopyFront(), 0))
    1939               0 :             return NULL;
    1940            1494 :         break;
    1941                 : 
    1942                 :       case JSOP_DUP:
    1943                 :       {
    1944                 :         /* Compound lhs, e.g., '[x,y]' in 'let [[x,y], z] = a;'. */
    1945            4671 :         pc = DecompileDestructuring(ss, pc, endpc, letNames);
    1946            4671 :         if (!pc)
    1947               0 :             return NULL;
    1948            4671 :         if (pc == endpc)
    1949               0 :             return pc;
    1950            4671 :         LOAD_OP_DATA(pc);
    1951                 : 
    1952                 :         /*
    1953                 :          * By its post-condition, DecompileDestructuring pushed one string
    1954                 :          * containing the whole decompiled lhs. Our post-condition is to sprint
    1955                 :          * in-place so pop/concat this pushed string.
    1956                 :          */
    1957            4671 :         lval = PopStr(ss, JSOP_NOP);
    1958            4671 :         if (ss->sprinter.put(lval) < 0)
    1959               0 :             return NULL;
    1960                 : 
    1961            4671 :         LOCAL_ASSERT(*pc == JSOP_POP);
    1962                 : 
    1963                 :         /*
    1964                 :          * To put block slots in the right place, the emitter follows a
    1965                 :          * compound lhs with a pick (if at least one slot was pushed). The pick
    1966                 :          * is not part of the compound lhs so DecompileDestructuring did not
    1967                 :          * advance over it but it is part of the lhs so advance over it here.
    1968                 :          */
    1969            4671 :         jsbytecode *nextpc = pc + JSOP_POP_LENGTH;
    1970            4671 :         LOCAL_ASSERT(nextpc <= endpc);
    1971            4671 :         if (letNames && *nextpc == JSOP_PICK) {
    1972             567 :             LOCAL_ASSERT(nextpc < endpc);
    1973             567 :             pc = nextpc;
    1974             567 :             LOAD_OP_DATA(pc);
    1975                 :         }
    1976            4671 :         break;
    1977                 :       }
    1978                 : 
    1979                 :       case JSOP_SETARG:
    1980                 :       case JSOP_SETLOCAL:
    1981            1683 :         LOCAL_ASSERT(!letNames);
    1982            1683 :         LOCAL_ASSERT(pc[oplen] == JSOP_POP || pc[oplen] == JSOP_POPN);
    1983            1683 :         if (op == JSOP_SETARG) {
    1984             198 :             atom = GetArgOrVarAtom(jp, GET_SLOTNO(pc));
    1985             198 :             LOCAL_ASSERT(atom);
    1986             198 :             if (!QuoteString(&ss->sprinter, atom, 0))
    1987               0 :                 return NULL;
    1988            1485 :         } else if (IsVarSlot(jp, pc, &i)) {
    1989             558 :             atom = GetArgOrVarAtom(jp, i);
    1990             558 :             LOCAL_ASSERT(atom);
    1991             558 :             if (!QuoteString(&ss->sprinter, atom, 0))
    1992               0 :                 return NULL;
    1993                 :         } else {
    1994             927 :             lval = GetLocal(ss, i);
    1995             927 :             if (!lval || ss->sprinter.put(lval) < 0)
    1996               0 :                 return NULL;
    1997                 :         }
    1998            1683 :         pc += oplen;
    1999            1683 :         if (pc == endpc)
    2000               0 :             return pc;
    2001            1683 :         LOAD_OP_DATA(pc);
    2002            1683 :         if (op == JSOP_POPN)
    2003               0 :             return pc;
    2004            1683 :         LOCAL_ASSERT(op == JSOP_POP);
    2005            1683 :         break;
    2006                 : 
    2007                 :       default: {
    2008              27 :         LOCAL_ASSERT(!letNames);
    2009                 :         /*
    2010                 :          * We may need to auto-parenthesize the left-most value decompiled
    2011                 :          * here, so add back PAREN_SLOP temporarily.  Then decompile until the
    2012                 :          * opcode that would reduce the stack depth to (ss->top-1), which we
    2013                 :          * pass to Decompile encoded as -(ss->top-1) - 1 or just -ss->top for
    2014                 :          * the nb parameter.
    2015                 :          */
    2016              27 :         ptrdiff_t todo = ss->sprinter.getOffset();
    2017              27 :         ss->sprinter.reserve(PAREN_SLOP);
    2018              27 :         pc = Decompile(ss, pc, -((int)ss->top));
    2019              27 :         if (!pc)
    2020               0 :             return NULL;
    2021              27 :         if (pc == endpc)
    2022               0 :             return pc;
    2023              27 :         LOAD_OP_DATA(pc);
    2024              27 :         LOCAL_ASSERT(op == JSOP_ENUMELEM || op == JSOP_ENUMCONSTELEM);
    2025              27 :         xval = PopStr(ss, JSOP_NOP);
    2026              27 :         lval = PopStr(ss, JSOP_GETPROP);
    2027              27 :         ss->sprinter.setOffset(todo);
    2028              27 :         if (*lval == '\0') {
    2029                 :             /* lval is from JSOP_BINDNAME, so just print xval. */
    2030              27 :             todo = ss->sprinter.put(xval);
    2031               0 :         } else if (*xval == '\0') {
    2032                 :             /* xval is from JSOP_SETCALL or JSOP_BINDXMLNAME, print lval. */
    2033               0 :             todo = ss->sprinter.put(lval);
    2034                 :         } else {
    2035                 :             todo = Sprint(&ss->sprinter,
    2036               0 :                           (JOF_OPMODE(ss->opcodes[ss->top+1]) == JOF_XMLNAME)
    2037                 :                           ? "%s.%s"
    2038                 :                           : "%s[%s]",
    2039               0 :                           lval, xval);
    2040                 :         }
    2041              27 :         if (todo < 0)
    2042               0 :             return NULL;
    2043              27 :         break;
    2044                 :       }
    2045                 :     }
    2046                 : 
    2047            8901 :     LOCAL_ASSERT(pc < endpc);
    2048            8901 :     pc += oplen;
    2049            8901 :     return pc;
    2050                 : }
    2051                 : 
    2052                 : /*
    2053                 :  * Decompile a destructuring lhs object or array initialiser, including nested
    2054                 :  * destructuring initialisers. On return a single string is pushed containing
    2055                 :  * the entire lhs (regardless of how many variables were bound). Thus, the
    2056                 :  * caller must take care of fixing up the decompiler stack.
    2057                 :  *
    2058                 :  * See DecompileDestructuringLHS for description of 'letNames'.
    2059                 :  */
    2060                 : static jsbytecode *
    2061            8010 : DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc,
    2062                 :                        AtomRange *letNames)
    2063                 : {
    2064            8010 :     LOCAL_ASSERT(*pc == JSOP_DUP);
    2065            8010 :     pc += JSOP_DUP_LENGTH;
    2066                 : 
    2067            8010 :     JSContext *cx = ss->sprinter.context;
    2068            8010 :     JSPrinter *jp = ss->printer;
    2069            8010 :     jsbytecode *startpc = pc;
    2070                 : 
    2071                 :     /*
    2072                 :      * Set head so we can rewrite '[' to '{' as needed.  Back up PAREN_SLOP
    2073                 :      * chars so the destructuring decompilation accumulates contiguously in
    2074                 :      * ss->sprinter starting with "[".
    2075                 :      */
    2076            8010 :     ptrdiff_t head = ss->sprinter.put("[", 1);
    2077            8010 :     if (head < 0 || !PushOff(ss, head, JSOP_NOP))
    2078               0 :         return NULL;
    2079            8010 :     ss->sprinter.setOffset(ss->sprinter.getOffset() - PAREN_SLOP);
    2080            8010 :     LOCAL_ASSERT(head == ss->sprinter.getOffset() - 1);
    2081            8010 :     LOCAL_ASSERT(ss->sprinter[head] == '[');
    2082                 : 
    2083            8010 :     int lasti = -1;
    2084                 : 
    2085           18324 :     while (pc < endpc) {
    2086                 : #if JS_HAS_DESTRUCTURING_SHORTHAND
    2087           10314 :         ptrdiff_t nameoff = -1;
    2088                 : #endif
    2089                 : 
    2090                 :         const JSCodeSpec *cs;
    2091                 :         unsigned oplen;
    2092                 :         JSOp op;
    2093           10314 :         LOAD_OP_DATA(pc);
    2094                 : 
    2095                 :         int i;
    2096                 :         double d;
    2097           10314 :         switch (op) {
    2098                 :           case JSOP_POP:
    2099                 :             /* Empty destructuring lhs. */
    2100            2223 :             LOCAL_ASSERT(startpc == pc);
    2101            2223 :             pc += oplen;
    2102            2223 :             goto out;
    2103                 : 
    2104                 :           /* Handle the optimized number-pushing opcodes. */
    2105            4554 :           case JSOP_ZERO:   d = i = 0; goto do_getelem;
    2106            1359 :           case JSOP_ONE:    d = i = 1; goto do_getelem;
    2107               0 :           case JSOP_UINT16: d = i = GET_UINT16(pc); goto do_getelem;
    2108               0 :           case JSOP_UINT24: d = i = GET_UINT24(pc); goto do_getelem;
    2109             540 :           case JSOP_INT8:   d = i = GET_INT8(pc);   goto do_getelem;
    2110               0 :           case JSOP_INT32:  d = i = GET_INT32(pc);  goto do_getelem;
    2111                 : 
    2112                 :           case JSOP_DOUBLE:
    2113               0 :             d = jp->script->getConst(GET_UINT32_INDEX(pc)).toDouble();
    2114               0 :             LOCAL_ASSERT(JSDOUBLE_IS_FINITE(d) && !JSDOUBLE_IS_NEGZERO(d));
    2115               0 :             i = (int)d;
    2116                 : 
    2117                 :           do_getelem:
    2118                 :           {
    2119            6453 :             jssrcnote *sn = js_GetSrcNote(jp->script, pc);
    2120            6453 :             pc += oplen;
    2121            6453 :             if (pc == endpc)
    2122               0 :                 return pc;
    2123            6453 :             LOAD_OP_DATA(pc);
    2124            6453 :             LOCAL_ASSERT(op == JSOP_GETELEM);
    2125                 : 
    2126                 :             /* Distinguish object from array by opcode or source note. */
    2127            6453 :             if (sn && SN_TYPE(sn) == SRC_INITPROP) {
    2128             162 :                 ss->sprinter[head] = '{';
    2129             324 :                 if (Sprint(&ss->sprinter, "%g: ", d) < 0)
    2130               0 :                     return NULL;
    2131                 :             } else {
    2132                 :                 /* Sanity check for the gnarly control flow above. */
    2133            6291 :                 LOCAL_ASSERT(i == d);
    2134                 : 
    2135                 :                 /* Fill in any holes (holes at the end don't matter). */
    2136           12582 :                 while (++lasti < i) {
    2137               0 :                     if (ss->sprinter.put(", ", 2) < 0)
    2138               0 :                         return NULL;
    2139                 :                 }
    2140                 :             }
    2141            6453 :             break;
    2142                 :           }
    2143                 : 
    2144                 :           case JSOP_GETPROP:
    2145                 :           case JSOP_LENGTH:
    2146                 :           {
    2147                 :             JSAtom *atom;
    2148            1638 :             LOAD_ATOM(0);
    2149            1638 :             ss->sprinter[head] = '{';
    2150                 : #if JS_HAS_DESTRUCTURING_SHORTHAND
    2151            1638 :             nameoff = ss->sprinter.getOffset();
    2152                 : #endif
    2153            1638 :             if (!QuoteString(&ss->sprinter, atom, IsIdentifier(atom) ? 0 : (jschar)'\''))
    2154               0 :                 return NULL;
    2155            1638 :             if (ss->sprinter.put(": ", 2) < 0)
    2156               0 :                 return NULL;
    2157            1638 :             break;
    2158                 :           }
    2159                 : 
    2160                 :           default:
    2161               0 :             LOCAL_ASSERT(0);
    2162                 :         }
    2163                 : 
    2164            8091 :         pc += oplen;
    2165            8091 :         if (pc == endpc)
    2166               0 :             return pc;
    2167                 : 
    2168                 :         /*
    2169                 :          * Decompile the left-hand side expression whose bytecode starts at pc
    2170                 :          * and continues for a bounded number of bytecodes or stack operations
    2171                 :          * (and which in any event stops before endpc).
    2172                 :          */
    2173                 :         JSBool hole;
    2174            8091 :         pc = DecompileDestructuringLHS(ss, pc, endpc, &hole, letNames);
    2175            8091 :         if (!pc)
    2176               0 :             return NULL;
    2177                 : 
    2178                 : #if JS_HAS_DESTRUCTURING_SHORTHAND
    2179            8091 :         if (nameoff >= 0) {
    2180                 :             ptrdiff_t offset, initlen;
    2181                 : 
    2182            1638 :             offset = ss->sprinter.getOffset();
    2183            1638 :             LOCAL_ASSERT(ss->sprinter[offset] == '\0');
    2184            1638 :             initlen = offset - nameoff;
    2185            1638 :             LOCAL_ASSERT(initlen >= 4);
    2186                 : 
    2187                 :             /* Early check to rule out odd "name: lval" length. */
    2188            1638 :             if (((size_t)initlen & 1) == 0) {
    2189                 :                 size_t namelen;
    2190                 :                 const char *name;
    2191                 : 
    2192                 :                 /*
    2193                 :                  * Even "name: lval" string length: check for "x: x" and the
    2194                 :                  * like, and apply the shorthand if we can.
    2195                 :                  */
    2196             774 :                 namelen = (size_t)(initlen - 2) >> 1;
    2197             774 :                 name = ss->sprinter.stringAt(nameoff);
    2198            1332 :                 if (!strncmp(name + namelen, ": ", 2) &&
    2199             558 :                     !strncmp(name, name + namelen + 2, namelen)) {
    2200             171 :                     offset -= namelen + 2;
    2201             171 :                     ss->sprinter[offset] = '\0';
    2202             171 :                     ss->sprinter.setOffset(offset);
    2203                 :                 }
    2204                 :             }
    2205                 :         }
    2206                 : #endif
    2207                 : 
    2208            8091 :         if (pc == endpc || *pc != JSOP_DUP)
    2209            5652 :             break;
    2210                 : 
    2211                 :         /*
    2212                 :          * We should stop if JSOP_DUP is either without notes or its note is
    2213                 :          * not SRC_CONTINUE. The former happens when JSOP_DUP duplicates the
    2214                 :          * last destructuring reference implementing an op= assignment like in
    2215                 :          * '([t] = z).y += x'. In the latter case the note is SRC_DESTRUCT and
    2216                 :          * means another destructuring initialiser abuts this one like in
    2217                 :          * '[a] = [b] = c'.
    2218                 :          */
    2219            2439 :         jssrcnote *sn = js_GetSrcNote(jp->script, pc);
    2220            2439 :         if (!sn)
    2221               0 :             break;
    2222            2439 :         if (SN_TYPE(sn) != SRC_CONTINUE) {
    2223             135 :             LOCAL_ASSERT(SN_TYPE(sn) == SRC_DESTRUCT || SN_TYPE(sn) == SRC_DESTRUCTLET);
    2224             135 :             break;
    2225                 :         }
    2226                 : 
    2227            2304 :         if (!hole && ss->sprinter.put(", ", 2) < 0)
    2228               0 :             return NULL;
    2229                 : 
    2230            2304 :         pc += JSOP_DUP_LENGTH;
    2231                 :     }
    2232                 : 
    2233                 : out:
    2234            8010 :     const char *lval = ss->sprinter.stringAt(head);
    2235            8010 :     if (ss->sprinter.put((*lval == '[') ? "]" : "}", 1) < 0)
    2236               0 :         return NULL;
    2237            8010 :     return pc;
    2238                 : }
    2239                 : 
    2240                 : static jsbytecode *
    2241             486 : DecompileGroupAssignment(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc,
    2242                 :                          jssrcnote *sn, ptrdiff_t *todop)
    2243                 : {
    2244                 :     JSOp op;
    2245                 :     const JSCodeSpec *cs;
    2246                 :     unsigned oplen, start, end, i;
    2247                 :     ptrdiff_t todo;
    2248                 :     JSBool hole;
    2249                 :     const char *rval;
    2250                 : 
    2251             486 :     LOAD_OP_DATA(pc);
    2252             486 :     LOCAL_ASSERT(op == JSOP_GETLOCAL);
    2253                 : 
    2254             486 :     todo = Sprint(&ss->sprinter, "%s[", VarPrefix(sn));
    2255             486 :     if (todo < 0 || !PushOff(ss, todo, JSOP_NOP))
    2256               0 :         return NULL;
    2257             486 :     ss->sprinter.setOffset(ss->sprinter.getOffset() - PAREN_SLOP);
    2258                 : 
    2259             324 :     for (;;) {
    2260             810 :         pc += oplen;
    2261             810 :         if (pc == endpc)
    2262               0 :             return pc;
    2263             810 :         pc = DecompileDestructuringLHS(ss, pc, endpc, &hole);
    2264             810 :         if (!pc)
    2265               0 :             return NULL;
    2266             810 :         if (pc == endpc)
    2267               0 :             return pc;
    2268             810 :         LOAD_OP_DATA(pc);
    2269             810 :         if (op != JSOP_GETLOCAL)
    2270                 :             break;
    2271             324 :         if (!hole && ss->sprinter.put(", ", 2) < 0)
    2272               0 :             return NULL;
    2273                 :     }
    2274                 : 
    2275             486 :     LOCAL_ASSERT(op == JSOP_POPN);
    2276             486 :     if (ss->sprinter.put("] = [", 5) < 0)
    2277               0 :         return NULL;
    2278                 : 
    2279             486 :     end = ss->top - 1;
    2280             486 :     start = end - GET_UINT16(pc);
    2281            1296 :     for (i = start; i < end; i++) {
    2282             810 :         rval = GetStr(ss, i);
    2283             810 :         if (Sprint(&ss->sprinter,
    2284                 :                    (i == start) ? "%s" : ", %s",
    2285             810 :                    (i == end - 1 && *rval == '\0') ? ", " : rval) < 0) {
    2286               0 :             return NULL;
    2287                 :         }
    2288                 :     }
    2289                 : 
    2290             486 :     if (ss->sprinter.put("]", 1) < 0)
    2291               0 :         return NULL;
    2292             486 :     ss->sprinter.setOffset(ss->offsets[i]);
    2293             486 :     ss->top = start;
    2294             486 :     *todop = todo;
    2295             486 :     return pc;
    2296                 : }
    2297                 : 
    2298                 : #undef LOCAL_ASSERT
    2299                 : #undef LOAD_OP_DATA
    2300                 : 
    2301                 : #endif /* JS_HAS_DESTRUCTURING */
    2302                 : 
    2303                 : #define LOCAL_ASSERT(expr)    LOCAL_ASSERT_RV(expr, false)
    2304                 : 
    2305                 : /*
    2306                 :  * The names of the vars of a let block/expr are stored as the ids of the
    2307                 :  * shapes of the block object. Shapes are stored in a singly-linked list in
    2308                 :  * reverse order of addition. This function takes care of putting the names
    2309                 :  * back in declaration order.
    2310                 :  */
    2311                 : static bool
    2312            8784 : GetBlockNames(JSContext *cx, StaticBlockObject &blockObj, AtomVector *atoms)
    2313                 : {
    2314            8784 :     size_t numAtoms = blockObj.slotCount();
    2315            8784 :     LOCAL_ASSERT(numAtoms > 0);
    2316            8784 :     if (!atoms->resize(numAtoms))
    2317               0 :         return false;
    2318                 : 
    2319            8784 :     unsigned i = numAtoms;
    2320           21519 :     for (Shape::Range r = blockObj.lastProperty()->all(); !r.empty(); r.popFront()) {
    2321           12735 :         const Shape &shape = r.front();
    2322           12735 :         LOCAL_ASSERT(shape.hasShortID());
    2323           12735 :         --i;
    2324           12735 :         LOCAL_ASSERT((unsigned)shape.shortid() == i);
    2325           12735 :         (*atoms)[i] = JSID_IS_INT(shape.propid())
    2326                 :                       ? cx->runtime->atomState.emptyAtom
    2327           12735 :                       : JSID_TO_ATOM(shape.propid());
    2328                 :     }
    2329                 : 
    2330            8784 :     LOCAL_ASSERT(i == 0);
    2331            8784 :     return true;
    2332                 : }
    2333                 : 
    2334                 : static bool
    2335            6876 : PushBlockNames(JSContext *cx, SprintStack *ss, const AtomVector &atoms)
    2336                 : {
    2337           16119 :     for (size_t i = 0; i < atoms.length(); i++) {
    2338            9243 :         const char *name = QuoteString(&ss->sprinter, atoms[i], 0);
    2339            9243 :         if (!name || !PushOff(ss, ss->sprinter.getOffsetOf(name), JSOP_ENTERBLOCK))
    2340               0 :             return false;
    2341                 :     }
    2342            6876 :     return true;
    2343                 : }
    2344                 : 
    2345                 : /*
    2346                 :  * In the scope of a let, the variables' (decompiler) stack slots must contain
    2347                 :  * the corresponding variable's name. This function updates the N top slots
    2348                 :  * with the N variable names stored in 'atoms'.
    2349                 :  */
    2350                 : static bool
    2351            4689 : AssignBlockNamesToPushedSlots(JSContext *cx, SprintStack *ss, const AtomVector &atoms)
    2352                 : {
    2353                 :     /* For simplicity, just pop and push. */
    2354            4689 :     LOCAL_ASSERT(atoms.length() <= (unsigned)ss->top);
    2355           11214 :     for (size_t i = 0; i < atoms.length(); ++i)
    2356            6525 :         PopStr(ss, JSOP_NOP);
    2357            4689 :     return PushBlockNames(cx, ss, atoms);
    2358                 : }
    2359                 : 
    2360                 : static const char SkipString[] = "/*skip*/";
    2361                 : static const char DestructuredString[] = "/*destructured*/";
    2362           18667 : static const unsigned DestructuredStringLength = ArrayLength(DestructuredString) - 1;
    2363                 : 
    2364                 : static ptrdiff_t
    2365            3294 : SprintLetBody(JSContext *cx, JSPrinter *jp, SprintStack *ss, jsbytecode *pc, ptrdiff_t bodyLength,
    2366                 :               const char *headChars)
    2367                 : {
    2368            3294 :     if (pc[bodyLength] == JSOP_LEAVEBLOCK) {
    2369            1602 :         js_printf(jp, "\tlet (%s) {\n", headChars);
    2370            1602 :         jp->indent += 4;
    2371            1602 :         if (!Decompile(ss, pc, bodyLength))
    2372               0 :             return -1;
    2373            1602 :         jp->indent -= 4;
    2374            1602 :         js_printf(jp, "\t}\n");
    2375            1602 :         return -2;
    2376                 :     }
    2377                 : 
    2378            1692 :     LOCAL_ASSERT_RV(pc[bodyLength] == JSOP_LEAVEBLOCKEXPR, -1);
    2379            1692 :     if (!Decompile(ss, pc, bodyLength))
    2380               0 :         return -1;
    2381                 : 
    2382            1692 :     const char *bodyChars = PopStr(ss, JSOP_SETNAME);
    2383            1692 :     const char *format = *bodyChars == '{' ? "let (%s) (%s)" : "let (%s) %s";
    2384            1692 :     return Sprint(&ss->sprinter, format, headChars, bodyChars);
    2385                 : }
    2386                 : 
    2387                 : /*
    2388                 :  * Get the token to prefix the '=' in an assignment operation, checking whether
    2389                 :  * the last operation was a getter, setter or compound assignment. For compound
    2390                 :  * assignments, marks parents for the lhs and rhs of the operation in the
    2391                 :  * compound assign. For an assignment such as 'a += b', the lhs will appear
    2392                 :  * twice in the bytecode, in read and write operations. We defer generation of
    2393                 :  * the offsets for the initial arithmetic operation until the entire compound
    2394                 :  * assign has been processed.
    2395                 :  */
    2396                 : static const char *
    2397             846 : GetTokenForAssignment(JSPrinter *jp, jssrcnote *sn, JSOp lastop,
    2398                 :                       jsbytecode *pc, jsbytecode *rvalpc,
    2399                 :                       jsbytecode **lastlvalpc, jsbytecode **lastrvalpc)
    2400                 : {
    2401                 :     const char *token;
    2402             846 :     if (sn && SN_TYPE(sn) == SRC_ASSIGNOP) {
    2403            1224 :         if (lastop == JSOP_GETTER) {
    2404               0 :             token = js_getter_str;
    2405             612 :         } else if (lastop == JSOP_SETTER) {
    2406               0 :             token = js_setter_str;
    2407                 :         } else {
    2408             612 :             token = CodeToken[lastop];
    2409             612 :             if (*lastlvalpc && *lastrvalpc) {
    2410             612 :                 UpdateDecompiledParent(jp, *lastlvalpc, pc, 0);
    2411             612 :                 UpdateDecompiledParent(jp, *lastrvalpc, rvalpc, 0);
    2412                 :             }
    2413                 :         }
    2414                 :     } else {
    2415             234 :         token = "";
    2416                 :     }
    2417             846 :     *lastlvalpc = NULL;
    2418             846 :     *lastrvalpc = NULL;
    2419             846 :     return token;
    2420                 : }
    2421                 : 
    2422                 : static ptrdiff_t
    2423            1089 : SprintNormalFor(JSContext *cx, JSPrinter *jp, SprintStack *ss, const char *initPrefix,
    2424                 :                 const char *init, jsbytecode *initpc, jsbytecode **ppc, ptrdiff_t *plen)
    2425                 : {
    2426            1089 :     jsbytecode *pc = *ppc;
    2427            1089 :     jssrcnote *sn = js_GetSrcNote(jp->script, pc);
    2428            1089 :     JS_ASSERT(SN_TYPE(sn) == SRC_FOR);
    2429                 : 
    2430                 :     /* Print the keyword and the possibly empty init-part. */
    2431            1089 :     js_printf(jp, "\tfor (%s", initPrefix);
    2432            1089 :     SprintOpcodePermanent(jp, init, initpc);
    2433            1089 :     js_printf(jp, ";");
    2434                 : 
    2435                 :     /* Skip the JSOP_NOP or JSOP_POP bytecode. */
    2436            1089 :     JS_ASSERT(*pc == JSOP_NOP || *pc == JSOP_POP);
    2437            1089 :     pc += JSOP_NOP_LENGTH;
    2438                 : 
    2439                 :     /* Get the cond, next, and loop-closing tail offsets. */
    2440            1089 :     ptrdiff_t cond = js_GetSrcNoteOffset(sn, 0);
    2441            1089 :     ptrdiff_t next = js_GetSrcNoteOffset(sn, 1);
    2442            1089 :     ptrdiff_t tail = js_GetSrcNoteOffset(sn, 2);
    2443                 : 
    2444                 :     /* Find the loop head, skipping over any leading GOTO or NOP. */
    2445            1089 :     jsbytecode *pc2 = pc;
    2446            1089 :     if (*pc == JSOP_GOTO || *pc == JSOP_NOP)
    2447             738 :         pc2 += GetBytecodeLength(pc);
    2448            1089 :     LOCAL_ASSERT(tail + GET_JUMP_OFFSET(pc + tail) == pc2 - pc);
    2449                 : 
    2450            1089 :     if (cond != tail) {
    2451                 :         /* Decompile the loop condition. */
    2452             684 :         if (!Decompile(ss, pc + cond, tail - cond))
    2453               0 :             return -1;
    2454             684 :         js_printf(jp, " ");
    2455                 :         jsbytecode *condpc;
    2456             684 :         const char *cond = PopStr(ss, JSOP_NOP, &condpc);
    2457             684 :         SprintOpcodePermanent(jp, cond, condpc);
    2458                 :     }
    2459                 : 
    2460                 :     /* Need a semicolon whether or not there was a cond. */
    2461            1089 :     js_puts(jp, ";");
    2462                 : 
    2463            1089 :     if (next != cond) {
    2464                 :         /*
    2465                 :          * Decompile the loop updater. It may end in a JSOP_POP
    2466                 :          * that we skip; or in a JSOP_POPN that we do not skip,
    2467                 :          * followed by a JSOP_NOP (skipped as if it's a POP).
    2468                 :          * We cope with the difference between these two cases
    2469                 :          * by checking for stack imbalance and popping if there
    2470                 :          * is an rval.
    2471                 :          */
    2472             603 :         unsigned saveTop = ss->top;
    2473                 : 
    2474             603 :         if (!Decompile(ss, pc + next, cond - next - JSOP_POP_LENGTH))
    2475               0 :             return -1;
    2476             603 :         LOCAL_ASSERT(ss->top - saveTop <= 1U);
    2477             603 :         jsbytecode *updatepc = NULL;
    2478                 :         const char *update = (ss->top == saveTop)
    2479               0 :                              ? ss->sprinter.stringEnd()
    2480             603 :                              : PopStr(ss, JSOP_NOP, &updatepc);
    2481             603 :         js_printf(jp, " ");
    2482             603 :         SprintOpcodePermanent(jp, update, updatepc);
    2483                 :     }
    2484                 : 
    2485                 :     /* Do the loop body. */
    2486            1089 :     js_printf(jp, ") {\n");
    2487            1089 :     jp->indent += 4;
    2488            1089 :     next -= pc2 - pc;
    2489            1089 :     if (!Decompile(ss, pc2, next))
    2490               0 :         return -1;
    2491            1089 :     jp->indent -= 4;
    2492            1089 :     js_printf(jp, "\t}\n");
    2493                 : 
    2494                 :     /* Set len so pc skips over the entire loop. */
    2495            1089 :     *ppc = pc;
    2496            1089 :     *plen = tail + js_CodeSpec[pc[tail]].length;
    2497            1089 :     return -2;
    2498                 : }
    2499                 : 
    2500                 : #undef LOCAL_ASSERT
    2501                 : 
    2502                 : static JSBool
    2503            9871 : InitSprintStack(JSContext *cx, SprintStack *ss, JSPrinter *jp, unsigned depth)
    2504                 : {
    2505            9871 :     if (!ss->sprinter.init())
    2506               0 :         return JS_FALSE;
    2507            9871 :     ss->sprinter.setOffset(PAREN_SLOP);
    2508                 : 
    2509                 :     /* Allocate the parallel (to avoid padding) offset, opcode and bytecode stacks. */
    2510            9871 :     size_t offsetsz = depth * sizeof(ptrdiff_t);
    2511            9871 :     size_t opcodesz = depth * sizeof(jsbytecode);
    2512            9871 :     size_t bytecodesz = depth * sizeof(jsbytecode *);
    2513            9871 :     void *space = cx->tempLifoAlloc().alloc(offsetsz + opcodesz + bytecodesz);
    2514            9871 :     if (!space) {
    2515               0 :         js_ReportOutOfMemory(cx);
    2516               0 :         return JS_FALSE;
    2517                 :     }
    2518            9871 :     ss->offsets = (ptrdiff_t *) space;
    2519            9871 :     ss->opcodes = (jsbytecode *) ((char *)space + offsetsz);
    2520            9871 :     ss->bytecodes = (jsbytecode **) ((char *)space + offsetsz + opcodesz);
    2521                 : 
    2522            9871 :     ss->top = ss->inArrayInit = 0;
    2523            9871 :     ss->inGenExp = JS_FALSE;
    2524            9871 :     ss->printer = jp;
    2525            9871 :     return JS_TRUE;
    2526                 : }
    2527                 : 
    2528                 : /*
    2529                 :  * If nb is non-negative, decompile nb bytecodes starting at pc.  Otherwise
    2530                 :  * the decompiler starts at pc and continues until it reaches an opcode for
    2531                 :  * which decompiling would result in the stack depth equaling -(nb + 1).
    2532                 :  */
    2533                 : static jsbytecode *
    2534           20860 : Decompile(SprintStack *ss, jsbytecode *pc, int nb)
    2535                 : {
    2536                 :     JSContext *cx;
    2537                 :     JSPrinter *jp, *jp2;
    2538                 :     jsbytecode *startpc, *endpc, *pc2, *done, *lvalpc, *rvalpc, *xvalpc;
    2539                 :     ptrdiff_t tail, todo, len, oplen, cond, next;
    2540                 :     JSOp op, lastop, saveop;
    2541                 :     const JSCodeSpec *cs;
    2542                 :     jssrcnote *sn, *sn2;
    2543                 :     const char *lval, *rval, *xval, *fmt, *token;
    2544                 :     unsigned nuses;
    2545                 :     int i, argc;
    2546                 :     JSAtom *atom;
    2547                 :     JSObject *obj;
    2548           20860 :     JSFunction *fun = NULL; /* init to shut GCC up */
    2549                 :     JSString *str;
    2550                 :     JSBool ok;
    2551                 : #if JS_HAS_XML_SUPPORT
    2552                 :     JSBool foreach, inXML, quoteAttr;
    2553                 : #else
    2554                 : #define inXML JS_FALSE
    2555                 : #endif
    2556                 :     jsval val;
    2557                 : 
    2558                 :     static const char exception_cookie[] = "/*EXCEPTION*/";
    2559                 :     static const char retsub_pc_cookie[] = "/*RETSUB_PC*/";
    2560                 :     static const char forelem_cookie[]   = "/*FORELEM*/";
    2561                 :     static const char with_cookie[]      = "/*WITH*/";
    2562                 :     static const char dot_format[]       = "%s.%s";
    2563                 :     static const char index_format[]     = "%s[%s]";
    2564                 :     static const char predot_format[]    = "%s%s.%s";
    2565                 :     static const char postdot_format[]   = "%s.%s%s";
    2566                 :     static const char preindex_format[]  = "%s%s[%s]";
    2567                 :     static const char postindex_format[] = "%s[%s]%s";
    2568                 :     static const char ss_format[]        = "%s%s";
    2569                 :     static const char sss_format[]       = "%s%s%s";
    2570                 : 
    2571                 :     /* Argument and variables decompilation uses the following to share code. */
    2572                 :     JS_STATIC_ASSERT(ARGNO_LEN == SLOTNO_LEN);
    2573                 : 
    2574                 : /*
    2575                 :  * Local macros
    2576                 :  */
    2577                 : #define LOCAL_ASSERT(expr)    LOCAL_ASSERT_RV(expr, NULL)
    2578                 : #define DECOMPILE_CODE_CLEANUP(pc,nb,cleanup) if (!Decompile(ss, pc, nb)) cleanup
    2579                 : #define DECOMPILE_CODE(pc,nb) DECOMPILE_CODE_CLEANUP(pc,nb,return NULL)
    2580                 : #define TOP_STR()             GetStr(ss, ss->top - 1)
    2581                 : #define POP_STR()             PopStr(ss, op)
    2582                 : #define POP_STR_PREC(prec)    PopStrPrec(ss, prec)
    2583                 : 
    2584                 : /*
    2585                 :  * Given an atom already fetched from jp->script's atom map, quote/escape its
    2586                 :  * string appropriately into rval, and select fmt from the quoted and unquoted
    2587                 :  * alternatives.
    2588                 :  */
    2589                 : #define GET_QUOTE_AND_FMT(qfmt, ufmt, rval)                                   \
    2590                 :     JS_BEGIN_MACRO                                                            \
    2591                 :         jschar quote_;                                                        \
    2592                 :         if (!IsIdentifier(atom)) {                                            \
    2593                 :             quote_ = '\'';                                                    \
    2594                 :             fmt = qfmt;                                                       \
    2595                 :         } else {                                                              \
    2596                 :             quote_ = 0;                                                       \
    2597                 :             fmt = ufmt;                                                       \
    2598                 :         }                                                                     \
    2599                 :         rval = QuoteString(&ss->sprinter, atom, quote_);                      \
    2600                 :         rval = SprintDupeStr(ss, rval);                                       \
    2601                 :         if (!rval)                                                            \
    2602                 :             return NULL;                                                      \
    2603                 :     JS_END_MACRO
    2604                 : 
    2605                 : #define GET_SOURCE_NOTE_ATOM(sn, atom)                                        \
    2606                 :     JS_BEGIN_MACRO                                                            \
    2607                 :         jsatomid atomIndex_ = (jsatomid) js_GetSrcNoteOffset((sn), 0);        \
    2608                 :                                                                               \
    2609                 :         LOCAL_ASSERT(atomIndex_ < jp->script->natoms);                        \
    2610                 :         (atom) = jp->script->atoms[atomIndex_];                               \
    2611                 :     JS_END_MACRO
    2612                 : 
    2613                 : /*
    2614                 :  * Get atom from jp->script's atom map, quote/escape its string appropriately
    2615                 :  * into rval, and select fmt from the quoted and unquoted alternatives.
    2616                 :  */
    2617                 : #define GET_ATOM_QUOTE_AND_FMT(qfmt, ufmt, rval)                              \
    2618                 :     JS_BEGIN_MACRO                                                            \
    2619                 :         LOAD_ATOM(0);                                                         \
    2620                 :         GET_QUOTE_AND_FMT(qfmt, ufmt, rval);                                  \
    2621                 :     JS_END_MACRO
    2622                 : 
    2623                 : /*
    2624                 :  * Per spec, new x(y).z means (new x(y))).z. For example new (x(y).z) must
    2625                 :  * decompile with the constructor parenthesized, but new x.z should not. The
    2626                 :  * normal rules give x(y).z and x.z identical precedence: both are produced by
    2627                 :  * JSOP_GETPROP.
    2628                 :  *
    2629                 :  * Therefore, we need to know in case JSOP_NEW whether the constructor
    2630                 :  * expression contains any unparenthesized function calls. So when building a
    2631                 :  * MemberExpression or CallExpression, we set ss->opcodes[n] to JSOP_CALL if
    2632                 :  * this is true. x(y).z gets JSOP_CALL, not JSOP_GETPROP.
    2633                 :  */
    2634                 : #define PROPAGATE_CALLNESS()                                                  \
    2635                 :     JS_BEGIN_MACRO                                                            \
    2636                 :         if (ss->opcodes[ss->top - 1] == JSOP_CALL ||                          \
    2637                 :             ss->opcodes[ss->top - 1] == JSOP_EVAL ||                          \
    2638                 :             ss->opcodes[ss->top - 1] == JSOP_FUNCALL ||                       \
    2639                 :             ss->opcodes[ss->top - 1] == JSOP_FUNAPPLY) {                      \
    2640                 :             saveop = JSOP_CALL;                                               \
    2641                 :         }                                                                     \
    2642                 :     JS_END_MACRO
    2643                 : 
    2644           20860 :     jsbytecode *lastlvalpc = NULL, *lastrvalpc = NULL;
    2645                 : 
    2646           20860 :     cx = ss->sprinter.context;
    2647           20860 :     JS_CHECK_RECURSION(cx, return NULL);
    2648                 : 
    2649           20860 :     jp = ss->printer;
    2650           20860 :     startpc = pc;
    2651           20860 :     endpc = (nb < 0) ? jp->script->code + jp->script->length : pc + nb;
    2652           20860 :     tail = -1;
    2653           20860 :     todo = -2;                  /* NB: different from Sprint() error return. */
    2654           20860 :     saveop = JSOP_NOP;
    2655           20860 :     sn = NULL;
    2656           20860 :     rval = NULL;
    2657           20860 :     bool forOf = false;
    2658                 : #if JS_HAS_XML_SUPPORT
    2659           20860 :     foreach = inXML = quoteAttr = JS_FALSE;
    2660                 : #endif
    2661                 : 
    2662          167097 :     while (nb < 0 || pc < endpc) {
    2663                 :         /*
    2664                 :          * Move saveop to lastop so prefixed bytecodes can take special action
    2665                 :          * while sharing maximal code.  Set op and saveop to the new bytecode,
    2666                 :          * use op in POP_STR to trigger automatic parenthesization, but push
    2667                 :          * saveop at the bottom of the loop if this op pushes.  Thus op may be
    2668                 :          * set to nop or otherwise mutated to suppress auto-parens.
    2669                 :          */
    2670          125539 :         lastop = saveop;
    2671          125539 :         op = (JSOp) *pc;
    2672          125539 :         cs = &js_CodeSpec[op];
    2673          125539 :         saveop = op;
    2674          125539 :         len = oplen = cs->length;
    2675          125539 :         nuses = StackUses(jp->script, pc);
    2676                 : 
    2677                 :         /*
    2678                 :          * Here it is possible that nuses > ss->top when the op has a hidden
    2679                 :          * source note. But when nb < 0 we assume that the caller knows that
    2680                 :          * Decompile would never meet such opcodes.
    2681                 :          */
    2682          125539 :         if (nb < 0) {
    2683              81 :             LOCAL_ASSERT(ss->top >= nuses);
    2684              81 :             unsigned ndefs = StackDefs(jp->script, pc);
    2685              81 :             if ((unsigned) -(nb + 1) == ss->top - nuses + ndefs)
    2686              27 :                 return pc;
    2687                 :         }
    2688                 : 
    2689                 :         /*
    2690                 :          * Save source literal associated with JS now before the following
    2691                 :          * rewrite changes op. See bug 380197.
    2692                 :          */
    2693          125512 :         token = CodeToken[op];
    2694                 : 
    2695          125512 :         if (pc + oplen == jp->dvgfence) {
    2696                 :             /*
    2697                 :              * Rewrite non-get ops to their "get" format if the error is in
    2698                 :              * the bytecode at pc, or if at an inner opcode of a 'fat' outer
    2699                 :              * opcode at pc, so we don't decompile more than the error
    2700                 :              * expression.
    2701                 :              */
    2702            1195 :             uint32_t format = cs->format;
    2703            1195 :             bool matchPC = false;
    2704            1195 :             FrameRegsIter iter(cx);
    2705            1195 :             if (!iter.done()) {
    2706            1195 :                 jsbytecode *npc = iter.pc();
    2707            1195 :                 if (pc == npc) {
    2708              45 :                     matchPC = true;
    2709            1150 :                 } else if (format & JOF_DECOMPOSE) {
    2710               0 :                     if (unsigned(npc - pc) < GetDecomposeLength(pc, js_CodeSpec[*pc].length))
    2711               0 :                         matchPC = true;
    2712                 :                 }
    2713                 :             }
    2714            1195 :             if ((matchPC || (pc == startpc && nuses != 0)) &&
    2715                 :                 format & (JOF_SET|JOF_DEL|JOF_INCDEC|JOF_VARPROP)) {
    2716              45 :                 uint32_t mode = JOF_MODE(format);
    2717              45 :                 if (mode == JOF_NAME) {
    2718                 :                     /*
    2719                 :                      * JOF_NAME does not imply JOF_ATOM, so we must check for
    2720                 :                      * the QARG and QVAR format types, and translate those to
    2721                 :                      * JSOP_GETARG or JSOP_GETLOCAL appropriately, instead of
    2722                 :                      * to JSOP_NAME.
    2723                 :                      */
    2724              27 :                     uint32_t type = JOF_TYPE(format);
    2725                 :                     op = (type == JOF_QARG)
    2726                 :                          ? JSOP_GETARG
    2727                 :                          : (type == JOF_LOCAL)
    2728                 :                          ? JSOP_GETLOCAL
    2729              27 :                          : JSOP_NAME;
    2730                 : 
    2731              27 :                     JS_ASSERT(js_CodeSpec[op].nuses >= 0);
    2732              27 :                     i = nuses - js_CodeSpec[op].nuses;
    2733              90 :                     while (--i >= 0)
    2734              36 :                         PopOff(ss, JSOP_NOP);
    2735                 :                 } else {
    2736                 :                     /*
    2737                 :                      * We must replace the faulting pc's bytecode with a
    2738                 :                      * corresponding JSOP_GET* code.  For JSOP_SET{PROP,ELEM},
    2739                 :                      * we must use the "2nd" form of JSOP_GET{PROP,ELEM}, to
    2740                 :                      * throw away the assignment op's right-hand operand and
    2741                 :                      * decompile it as if it were a GET of its left-hand
    2742                 :                      * operand.
    2743                 :                      */
    2744              18 :                     if (mode == JOF_PROP) {
    2745                 :                         op = (JSOp) ((format & JOF_SET)
    2746                 :                                      ? JSOP_GETPROP2
    2747               9 :                                      : JSOP_GETPROP);
    2748               9 :                     } else if (mode == JOF_ELEM) {
    2749                 :                         op = (JSOp) ((format & JOF_SET)
    2750                 :                                      ? JSOP_GETELEM2
    2751               9 :                                      : JSOP_GETELEM);
    2752                 :                     } else {
    2753                 :                         /*
    2754                 :                          * Unknown mode (including mode 0) means that op is
    2755                 :                          * uncategorized for our purposes, so we must write
    2756                 :                          * per-op special case code here.
    2757                 :                          */
    2758               0 :                         switch (op) {
    2759                 :                           case JSOP_ENUMELEM:
    2760                 :                           case JSOP_ENUMCONSTELEM:
    2761               0 :                             op = JSOP_GETELEM;
    2762               0 :                             break;
    2763                 :                           case JSOP_SETXMLNAME:
    2764               0 :                             op = JSOp(JSOP_GETELEM2);
    2765               0 :                             break;
    2766                 :                           default:
    2767               0 :                             LOCAL_ASSERT(0);
    2768                 :                         }
    2769                 :                     }
    2770                 :                 }
    2771                 :             }
    2772                 : 
    2773            1195 :             saveop = op;
    2774            1195 :             if (op >= JSOP_LIMIT) {
    2775               9 :                 if (op == JSOP_GETPROP2)
    2776               9 :                     saveop = JSOP_GETPROP;
    2777               0 :                 else if (op == JSOP_GETELEM2)
    2778               0 :                     saveop = JSOP_GETELEM;
    2779                 :             }
    2780                 : 
    2781            1195 :             jp->dvgfence = NULL;
    2782                 :         }
    2783                 : 
    2784          125512 :         jsbytecode *pushpc = pc;
    2785                 : 
    2786          125512 :         if (token) {
    2787           14427 :             switch (nuses) {
    2788                 :               case 2:
    2789            3996 :                 sn = js_GetSrcNote(jp->script, pc);
    2790            3996 :                 if (sn && SN_TYPE(sn) == SRC_ASSIGNOP) {
    2791                 :                     /*
    2792                 :                      * Avoid over-parenthesizing y in x op= y based on its
    2793                 :                      * expansion: x = x op y (replace y by z = w to see the
    2794                 :                      * problem).
    2795                 :                      */
    2796             612 :                     op = (JSOp) pc[oplen];
    2797             612 :                     rval = PopStr(ss, op, &lastrvalpc);
    2798             612 :                     (void)PopStr(ss, op, &lastlvalpc);
    2799                 : 
    2800                 :                     /* Print only the right operand of the assignment-op. */
    2801             612 :                     todo = ss->sprinter.put(rval);
    2802            3384 :                 } else if (!inXML) {
    2803            3384 :                     rval = PopStrPrecDupe(ss, cs->prec + !!(cs->format & JOF_LEFTASSOC), &rvalpc);
    2804            3384 :                     lval = PopStrPrec(ss, cs->prec + !(cs->format & JOF_LEFTASSOC), &lvalpc);
    2805            3384 :                     todo = ss->sprinter.getOffset();
    2806            3384 :                     SprintOpcode(ss, lval, lvalpc, pc, todo);
    2807            3384 :                     Sprint(&ss->sprinter, " %s ", token);
    2808            3384 :                     SprintOpcode(ss, rval, rvalpc, pc, todo);
    2809                 :                 } else {
    2810                 :                     /* In XML, just concatenate the two operands. */
    2811               0 :                     LOCAL_ASSERT(op == JSOP_ADD);
    2812               0 :                     rval = POP_STR();
    2813               0 :                     lval = POP_STR();
    2814               0 :                     todo = Sprint(&ss->sprinter, ss_format, lval, rval);
    2815                 :                 }
    2816            3996 :                 break;
    2817                 : 
    2818                 :               case 1:
    2819              36 :                 rval = PopStrDupe(ss, op, &rvalpc);
    2820              36 :                 todo = ss->sprinter.put(token);
    2821              36 :                 SprintOpcode(ss, rval, rvalpc, pc, todo);
    2822              36 :                 break;
    2823                 : 
    2824                 :               case 0:
    2825           10395 :                 sn = js_GetSrcNote(jp->script, pc);
    2826           10395 :                 if (sn && SN_TYPE(sn) == SRC_CONTINUE) {
    2827                 :                     /* Hoisted let decl (e.g. 'y' in 'let (x) { let y; }'). */
    2828             270 :                     todo = ss->sprinter.put(SkipString);
    2829             270 :                     break;
    2830                 :                 }
    2831           10125 :                 todo = ss->sprinter.put(token);
    2832           10125 :                 break;
    2833                 : 
    2834                 :               default:
    2835               0 :                 todo = -2;
    2836               0 :                 break;
    2837                 :             }
    2838                 :         } else {
    2839          111085 :             switch (op) {
    2840                 :               case JSOP_NOP:
    2841                 :                 /*
    2842                 :                  * Check for a do-while loop, a for-loop with an empty
    2843                 :                  * initializer part, a labeled statement, a function
    2844                 :                  * definition, or try/finally.
    2845                 :                  */
    2846             558 :                 sn = js_GetSrcNote(jp->script, pc);
    2847             558 :                 todo = -2;
    2848             558 :                 switch (sn ? SN_TYPE(sn) : SRC_NULL) {
    2849                 :                   case SRC_WHILE:
    2850                 :                     /* First instuction (NOP) contains offset to condition */
    2851               0 :                     ++pc;
    2852                 :                     /* Second instruction (TRACE) contains offset to JSOP_IFNE */
    2853               0 :                     sn = js_GetSrcNote(jp->script, pc);
    2854               0 :                     tail = js_GetSrcNoteOffset(sn, 0);
    2855               0 :                     LOCAL_ASSERT(pc[tail] == JSOP_IFNE);
    2856               0 :                     js_printf(jp, "\tdo {\n");
    2857               0 :                     jp->indent += 4;
    2858               0 :                     DECOMPILE_CODE(pc, tail);
    2859               0 :                     jp->indent -= 4;
    2860               0 :                     js_printf(jp, "\t} while (");
    2861               0 :                     rval = PopCondStr(ss, &rvalpc);
    2862               0 :                     SprintOpcodePermanent(jp, rval, rvalpc);
    2863               0 :                     js_printf(jp, ");\n");
    2864               0 :                     pc += tail;
    2865               0 :                     len = js_CodeSpec[*pc].length;
    2866               0 :                     todo = -2;
    2867               0 :                     break;
    2868                 : 
    2869                 :                   case SRC_FOR:
    2870                 :                     /* for loop with empty initializer. */
    2871              45 :                     todo = SprintNormalFor(cx, jp, ss, "", "", NULL, &pc, &len);
    2872              45 :                     break;
    2873                 : 
    2874                 :                   case SRC_ENDBRACE:
    2875             414 :                     jp->indent -= 4;
    2876             414 :                     js_printf(jp, "\t}\n");
    2877             414 :                     break;
    2878                 : 
    2879                 :                   case SRC_FUNCDEF:
    2880              99 :                     fun = jp->script->getFunction(js_GetSrcNoteOffset(sn, 0));
    2881                 :                   do_function:
    2882              99 :                     js_puts(jp, "\n");
    2883                 :                     jp2 = js_NewPrinter(cx, "nested_function", fun,
    2884                 :                                         jp->indent, jp->pretty, jp->grouped,
    2885              99 :                                         jp->strict);
    2886              99 :                     if (!jp2)
    2887               0 :                         return NULL;
    2888              99 :                     ok = js_DecompileFunction(jp2);
    2889              99 :                     if (ok && !jp2->sprinter.empty())
    2890              99 :                         js_puts(jp, jp2->sprinter.string());
    2891              99 :                     js_DestroyPrinter(jp2);
    2892              99 :                     if (!ok)
    2893               0 :                         return NULL;
    2894              99 :                     js_puts(jp, "\n\n");
    2895              99 :                     break;
    2896                 : 
    2897                 :                   case SRC_BRACE:
    2898               0 :                     js_printf(jp, "\t{\n");
    2899               0 :                     jp->indent += 4;
    2900               0 :                     len = js_GetSrcNoteOffset(sn, 0);
    2901               0 :                     DECOMPILE_CODE(pc + oplen, len - oplen);
    2902               0 :                     jp->indent -= 4;
    2903               0 :                     js_printf(jp, "\t}\n");
    2904               0 :                     break;
    2905                 : 
    2906                 :                   default:;
    2907                 :                 }
    2908             558 :                 break;
    2909                 : 
    2910                 :               case JSOP_LABEL:
    2911              54 :                 sn = js_GetSrcNote(jp->script, pc);
    2912              54 :                 todo = -2;
    2913              54 :                 switch (sn ? SN_TYPE(sn) : SRC_NULL) {
    2914                 :                   case SRC_LABEL:
    2915              54 :                     GET_SOURCE_NOTE_ATOM(sn, atom);
    2916              54 :                     jp->indent -= 4;
    2917              54 :                     rval = QuoteString(&ss->sprinter, atom, 0);
    2918              54 :                     if (!rval)
    2919               0 :                         return NULL;
    2920              54 :                     ss->sprinter.setOffset(rval);
    2921              54 :                     js_printf(jp, "\t%s:\n", rval);
    2922              54 :                     jp->indent += 4;
    2923              54 :                     break;
    2924                 : 
    2925                 :                   case SRC_LABELBRACE:
    2926               0 :                     GET_SOURCE_NOTE_ATOM(sn, atom);
    2927               0 :                     rval = QuoteString(&ss->sprinter, atom, 0);
    2928               0 :                     if (!rval)
    2929               0 :                         return NULL;
    2930               0 :                     ss->sprinter.setOffset(rval);
    2931               0 :                     js_printf(jp, "\t%s: {\n", rval);
    2932               0 :                     jp->indent += 4;
    2933               0 :                     break;
    2934                 : 
    2935                 :                   default:
    2936               0 :                     JS_NOT_REACHED("JSOP_LABEL without source note");
    2937                 :                     break;
    2938                 :                 }
    2939              54 :                 break;
    2940                 : 
    2941                 :               case JSOP_BINDNAME:
    2942                 :               case JSOP_BINDGNAME:
    2943             189 :                 todo = Sprint(&ss->sprinter, "");
    2944             189 :                 break;
    2945                 : 
    2946                 :               case JSOP_TRY:
    2947             414 :                 js_printf(jp, "\ttry {\n");
    2948             414 :                 jp->indent += 4;
    2949             414 :                 todo = -2;
    2950             414 :                 break;
    2951                 : 
    2952                 :               case JSOP_FINALLY:
    2953               0 :                 jp->indent -= 4;
    2954               0 :                 js_printf(jp, "\t} finally {\n");
    2955               0 :                 jp->indent += 4;
    2956                 : 
    2957                 :                 /*
    2958                 :                  * We push push the pair of exception/restsub cookies to
    2959                 :                  * simulate the effects [gosub] or control transfer during
    2960                 :                  * exception capturing on the stack.
    2961                 :                  */
    2962               0 :                 todo = Sprint(&ss->sprinter, exception_cookie);
    2963               0 :                 if (todo < 0 || !PushOff(ss, todo, op))
    2964               0 :                     return NULL;
    2965               0 :                 todo = Sprint(&ss->sprinter, retsub_pc_cookie);
    2966               0 :                 break;
    2967                 : 
    2968                 :               case JSOP_RETSUB:
    2969               0 :                 rval = POP_STR();
    2970               0 :                 LOCAL_ASSERT(strcmp(rval, retsub_pc_cookie) == 0);
    2971               0 :                 lval = POP_STR();
    2972               0 :                 LOCAL_ASSERT(strcmp(lval, exception_cookie) == 0);
    2973               0 :                 todo = -2;
    2974               0 :                 break;
    2975                 : 
    2976                 :               case JSOP_GOSUB:
    2977                 :                 /*
    2978                 :                  * JSOP_GOSUB has no effect on the decompiler's string stack
    2979                 :                  * because the next op in bytecode order finds the stack
    2980                 :                  * balanced by a JSOP_RETSUB executed elsewhere.
    2981                 :                  */
    2982               0 :                 todo = -2;
    2983               0 :                 break;
    2984                 : 
    2985                 :               case JSOP_POPN:
    2986                 :               {
    2987                 :                 unsigned newtop, oldtop;
    2988                 : 
    2989                 :                 /*
    2990                 :                  * The compiler models operand stack depth and fixes the stack
    2991                 :                  * pointer on entry to a catch clause based on its depth model.
    2992                 :                  * The decompiler must match the code generator's model, which
    2993                 :                  * is why JSOP_FINALLY pushes a cookie that JSOP_RETSUB pops.
    2994                 :                  */
    2995            1098 :                 oldtop = ss->top;
    2996            1098 :                 newtop = oldtop - GET_UINT16(pc);
    2997            1098 :                 LOCAL_ASSERT(newtop <= oldtop);
    2998            1098 :                 todo = -2;
    2999                 : 
    3000            1098 :                 sn = js_GetSrcNote(jp->script, pc);
    3001            1098 :                 if (sn && SN_TYPE(sn) == SRC_HIDDEN)
    3002             405 :                     break;
    3003                 : #if JS_HAS_DESTRUCTURING
    3004             693 :                 if (sn && SN_TYPE(sn) == SRC_GROUPASSIGN) {
    3005                 :                     todo = Sprint(&ss->sprinter, "%s[] = [",
    3006               0 :                                   VarPrefix(sn));
    3007               0 :                     if (todo < 0)
    3008               0 :                         return NULL;
    3009               0 :                     for (unsigned i = newtop; i < oldtop; i++) {
    3010               0 :                         rval = ss->sprinter.stringAt(ss->offsets[i]);
    3011               0 :                         if (Sprint(&ss->sprinter, ss_format,
    3012                 :                                    (i == newtop) ? "" : ", ",
    3013                 :                                    (i == oldtop - 1 && *rval == '\0')
    3014               0 :                                    ? ", " : rval) < 0) {
    3015               0 :                             return NULL;
    3016                 :                         }
    3017                 :                     }
    3018               0 :                     if (ss->sprinter.put("]", 1) < 0)
    3019               0 :                         return NULL;
    3020                 : 
    3021                 :                     /*
    3022                 :                      * If this is an empty group assignment, we have no stack
    3023                 :                      * budget into which we can push our result string. Adjust
    3024                 :                      * ss->sprinter.offset so that our consumer can find the
    3025                 :                      * empty group assignment decompilation.
    3026                 :                      */
    3027               0 :                     if (newtop == oldtop) {
    3028               0 :                         ss->sprinter.setOffset(todo);
    3029                 :                     } else {
    3030                 :                         /*
    3031                 :                          * Kill newtop before the end_groupassignment: label by
    3032                 :                          * retracting/popping early.
    3033                 :                          */
    3034               0 :                         LOCAL_ASSERT(newtop < oldtop);
    3035               0 :                         ss->sprinter.setOffset(GetOff(ss, newtop));
    3036               0 :                         ss->top = newtop;
    3037                 :                     }
    3038                 : 
    3039                 :                   end_groupassignment:
    3040             486 :                     LOCAL_ASSERT(*pc == JSOP_POPN);
    3041                 : 
    3042                 :                     /*
    3043                 :                      * Thread directly to the next opcode if we can, to handle
    3044                 :                      * the special cases of a group assignment in the first or
    3045                 :                      * last part of a for(;;) loop head, or in a let block or
    3046                 :                      * expression head.
    3047                 :                      *
    3048                 :                      * NB: todo at this point indexes space in ss->sprinter
    3049                 :                      * that is liable to be overwritten.  The code below knows
    3050                 :                      * exactly how long rval lives, or else copies it down via
    3051                 :                      * Sprinter::put.
    3052                 :                      */
    3053             486 :                     rval = ss->sprinter.stringAt(todo);
    3054             486 :                     rvalpc = NULL;
    3055             486 :                     todo = -2;
    3056             486 :                     pc2 = pc + oplen;
    3057                 : 
    3058             486 :                     if (*pc2 == JSOP_NOP) {
    3059               0 :                         sn = js_GetSrcNote(jp->script, pc2);
    3060               0 :                         if (sn) {
    3061               0 :                             if (SN_TYPE(sn) == SRC_FOR) {
    3062               0 :                                 op = JSOP_NOP;
    3063               0 :                                 pc = pc2;
    3064               0 :                                 todo = SprintNormalFor(cx, jp, ss, "", rval, rvalpc, &pc, &len);
    3065               0 :                                 break;
    3066                 :                             }
    3067                 :                         } else {
    3068                 :                             /*
    3069                 :                              * An unnannotated NOP following a POPN must be the
    3070                 :                              * third part of for(;;) loop head. If the POPN's
    3071                 :                              * immediate operand is 0, then we may have no slot
    3072                 :                              * on the sprint-stack in which to push our result
    3073                 :                              * string. In this case the result can be recovered
    3074                 :                              * at ss->sprinter.base + ss->sprinter.offset.
    3075                 :                              */
    3076               0 :                             if (GET_UINT16(pc) == 0)
    3077               0 :                                 break;
    3078               0 :                             todo = ss->sprinter.put(rval);
    3079               0 :                             saveop = JSOP_NOP;
    3080                 :                         }
    3081                 :                     }
    3082                 : 
    3083                 :                     /*
    3084                 :                      * If control flow reaches this point with todo still -2,
    3085                 :                      * just print rval as an expression statement.
    3086                 :                      */
    3087             486 :                     if (todo == -2)
    3088             486 :                         js_printf(jp, "\t%s;\n", rval);
    3089             486 :                     break;
    3090                 :                 }
    3091                 : #endif
    3092             693 :                 if (newtop < oldtop) {
    3093             693 :                     ss->sprinter.setOffset(GetOff(ss, newtop));
    3094             693 :                     ss->top = newtop;
    3095                 :                 }
    3096             693 :                 break;
    3097                 :               }
    3098                 : 
    3099                 :               case JSOP_EXCEPTION:
    3100                 :                 /* The catch decompiler handles this op itself. */
    3101               0 :                 LOCAL_ASSERT(JS_FALSE);
    3102                 :                 break;
    3103                 : 
    3104                 :               case JSOP_POP:
    3105                 :                 /*
    3106                 :                  * By default, do not automatically parenthesize when popping
    3107                 :                  * a stacked expression decompilation.  We auto-parenthesize
    3108                 :                  * only when JSOP_POP is annotated with SRC_PCDELTA, meaning
    3109                 :                  * comma operator.
    3110                 :                  */
    3111            7065 :                 op = JSOP_POPV;
    3112                 :                 /* FALL THROUGH */
    3113                 : 
    3114                 :               case JSOP_POPV:
    3115            7065 :                 sn = js_GetSrcNote(jp->script, pc);
    3116            7065 :                 switch (sn ? SN_TYPE(sn) : SRC_NULL) {
    3117                 :                   case SRC_FOR:
    3118                 :                     /* Force parens around 'in' expression at 'for' front. */
    3119             450 :                     if (ss->opcodes[ss->top-1] == JSOP_IN)
    3120               0 :                         op = JSOP_LSH;
    3121             450 :                     rval = PopStr(ss, op, &rvalpc);
    3122             450 :                     todo = SprintNormalFor(cx, jp, ss, "", rval, rvalpc, &pc, &len);
    3123             450 :                     break;
    3124                 : 
    3125                 :                   case SRC_PCDELTA:
    3126                 :                     /* Comma operator: use JSOP_POP for correct precedence. */
    3127            1440 :                     op = JSOP_POP;
    3128                 : 
    3129                 :                     /* Pop and save to avoid blowing stack depth budget. */
    3130            1440 :                     lval = PopStrDupe(ss, op, &lvalpc);
    3131                 : 
    3132                 :                     /*
    3133                 :                      * The offset tells distance to the end of the right-hand
    3134                 :                      * operand of the comma operator.
    3135                 :                      */
    3136            1440 :                     pushpc = pc;
    3137            1440 :                     done = pc + len;
    3138            1440 :                     pc += js_GetSrcNoteOffset(sn, 0);
    3139            1440 :                     len = 0;
    3140                 : 
    3141            1440 :                     if (!Decompile(ss, done, pc - done))
    3142               0 :                         return NULL;
    3143                 : 
    3144                 :                     /* Pop Decompile result and print comma expression. */
    3145            1440 :                     rval = PopStrDupe(ss, op, &rvalpc);
    3146            1440 :                     todo = ss->sprinter.getOffset();
    3147            1440 :                     SprintOpcode(ss, lval, lvalpc, pushpc, todo);
    3148            1440 :                     ss->sprinter.put(", ");
    3149            1440 :                     SprintOpcode(ss, rval, rvalpc, pushpc, todo);
    3150            1440 :                     break;
    3151                 : 
    3152                 :                   case SRC_HIDDEN:
    3153                 :                     /* Hide this pop, it's from a goto in a with or for/in. */
    3154               0 :                     todo = -2;
    3155               0 :                     break;
    3156                 : 
    3157                 :                   case SRC_CONTINUE:
    3158                 :                     /* Pop the stack, don't print: end of a for-let-in. */
    3159               0 :                     (void) PopOff(ss, op);
    3160               0 :                     todo = -2;
    3161               0 :                     break;
    3162                 : 
    3163                 :                   default:
    3164                 :                   {
    3165                 :                     /* Turn off parens around a yield statement. */
    3166            5175 :                     if (ss->opcodes[ss->top-1] == JSOP_YIELD)
    3167               0 :                         op = JSOP_NOP;
    3168                 : 
    3169                 :                     jsbytecode *rvalpc;
    3170            5175 :                     rval = PopStr(ss, op, &rvalpc);
    3171                 : 
    3172                 :                     /*
    3173                 :                      * Don't emit decompiler-pushed strings that are not
    3174                 :                      * handled by other opcodes. They are pushed onto the
    3175                 :                      * stack to help model the interpreter stack and should
    3176                 :                      * not appear in the decompiler's output.
    3177                 :                      */
    3178            5175 :                     if (*rval != '\0' && (rval[0] != '/' || rval[1] != '*')) {
    3179                 :                         bool parens =
    3180                 :                             *rval == '{' ||
    3181            5175 :                             (strncmp(rval, js_function_str, 8) == 0 &&
    3182           10350 :                              rval[8] == ' ');
    3183            5175 :                         js_printf(jp, parens ? "\t(" : "\t");
    3184            5175 :                         SprintOpcodePermanent(jp, rval, rvalpc);
    3185            5175 :                         js_printf(jp, parens ? ");\n" : ";\n");
    3186                 :                     } else {
    3187               0 :                         LOCAL_ASSERT(*rval == '\0' ||
    3188                 :                                      strcmp(rval, exception_cookie) == 0);
    3189                 :                     }
    3190            5175 :                     todo = -2;
    3191            5175 :                     break;
    3192                 :                   }
    3193                 :                 }
    3194            7065 :                 sn = NULL;
    3195            7065 :                 break;
    3196                 : 
    3197                 :               case JSOP_ENTERWITH:
    3198               0 :                 LOCAL_ASSERT(!js_GetSrcNote(jp->script, pc));
    3199               0 :                 rval = PopStr(ss, op, &rvalpc);
    3200               0 :                 js_printf(jp, "\twith (");
    3201               0 :                 SprintOpcodePermanent(jp, rval, rvalpc);
    3202               0 :                 js_printf(jp, ") {\n");
    3203               0 :                 jp->indent += 4;
    3204               0 :                 todo = Sprint(&ss->sprinter, with_cookie);
    3205               0 :                 break;
    3206                 : 
    3207                 :               case JSOP_LEAVEWITH:
    3208               0 :                 sn = js_GetSrcNote(jp->script, pc);
    3209               0 :                 todo = -2;
    3210               0 :                 if (sn && SN_TYPE(sn) == SRC_HIDDEN)
    3211               0 :                     break;
    3212               0 :                 rval = POP_STR();
    3213               0 :                 LOCAL_ASSERT(strcmp(rval, with_cookie) == 0);
    3214               0 :                 jp->indent -= 4;
    3215               0 :                 js_printf(jp, "\t}\n");
    3216               0 :                 break;
    3217                 : 
    3218                 :               case JSOP_ENTERBLOCK:
    3219                 :               {
    3220            2187 :                 obj = jp->script->getObject(GET_UINT32_INDEX(pc));
    3221            4374 :                 AtomVector atoms(cx);
    3222            2187 :                 StaticBlockObject &blockObj = obj->asStaticBlock();
    3223                 : 
    3224            2187 :                 if (!GetBlockNames(cx, blockObj, &atoms) || !PushBlockNames(cx, ss, atoms))
    3225               0 :                     return NULL;
    3226                 : 
    3227            2187 :                 sn = js_GetSrcNote(jp->script, pc);
    3228            2187 :                 switch (sn ? SN_TYPE(sn) : SRC_NULL) {
    3229                 : #if JS_HAS_BLOCK_SCOPE
    3230                 :                   case SRC_BRACE:
    3231              27 :                     js_printf(jp, "\t{\n");
    3232              27 :                     jp->indent += 4;
    3233              27 :                     len = js_GetSrcNoteOffset(sn, 0);
    3234              27 :                     if (!Decompile(ss, pc + oplen, len - oplen))
    3235               0 :                         return NULL;
    3236              27 :                     jp->indent -= 4;
    3237              27 :                     js_printf(jp, "\t}\n");
    3238              27 :                     break;
    3239                 : #endif
    3240                 : 
    3241                 :                   case SRC_CATCH:
    3242             414 :                     jp->indent -= 4;
    3243             414 :                     js_printf(jp, "\t} catch (");
    3244                 : 
    3245             414 :                     pc2 = pc;
    3246             414 :                     pc += oplen;
    3247             414 :                     LOCAL_ASSERT(*pc == JSOP_EXCEPTION);
    3248             414 :                     pc += JSOP_EXCEPTION_LENGTH;
    3249             414 :                     todo = Sprint(&ss->sprinter, exception_cookie);
    3250             414 :                     if (todo < 0 || !PushOff(ss, todo, JSOP_EXCEPTION))
    3251               0 :                         return NULL;
    3252                 : 
    3253             414 :                     if (*pc == JSOP_DUP) {
    3254               0 :                         sn2 = js_GetSrcNote(jp->script, pc);
    3255               0 :                         if (!sn2 || SN_TYPE(sn2) != SRC_DESTRUCT) {
    3256                 :                             /*
    3257                 :                              * This is a dup to save the exception for later.
    3258                 :                              * It is emitted only when the catch head contains
    3259                 :                              * an exception guard.
    3260                 :                              */
    3261               0 :                             LOCAL_ASSERT(js_GetSrcNoteOffset(sn, 0) != 0);
    3262               0 :                             pc += JSOP_DUP_LENGTH;
    3263               0 :                             todo = Sprint(&ss->sprinter, exception_cookie);
    3264               0 :                             if (todo < 0 || !PushOff(ss, todo, JSOP_EXCEPTION))
    3265               0 :                                 return NULL;
    3266                 :                         }
    3267                 :                     }
    3268                 : 
    3269                 : #if JS_HAS_DESTRUCTURING
    3270             414 :                     if (*pc == JSOP_DUP) {
    3271               0 :                         pc = DecompileDestructuring(ss, pc, endpc);
    3272               0 :                         if (!pc)
    3273               0 :                             return NULL;
    3274               0 :                         LOCAL_ASSERT(*pc == JSOP_POP);
    3275               0 :                         pc += JSOP_POP_LENGTH;
    3276               0 :                         lval = PopStr(ss, JSOP_NOP);
    3277               0 :                         js_puts(jp, lval);
    3278                 :                     } else {
    3279                 : #endif
    3280             414 :                         LOCAL_ASSERT(*pc == JSOP_SETLOCAL);
    3281             414 :                         pc += JSOP_SETLOCAL_LENGTH;
    3282             414 :                         LOCAL_ASSERT(*pc == JSOP_POP);
    3283             414 :                         pc += JSOP_POP_LENGTH;
    3284             414 :                         LOCAL_ASSERT(blockObj.slotCount() >= 1);
    3285             414 :                         if (!QuoteString(&jp->sprinter, atoms[0], 0))
    3286               0 :                             return NULL;
    3287                 : #if JS_HAS_DESTRUCTURING
    3288                 :                     }
    3289                 : #endif
    3290                 : 
    3291                 :                     /*
    3292                 :                      * Pop the exception_cookie (or its dup in the case of a
    3293                 :                      * guarded catch head) off the stack now.
    3294                 :                      */
    3295             414 :                     rval = PopStr(ss, JSOP_NOP);
    3296             414 :                     LOCAL_ASSERT(strcmp(rval, exception_cookie) == 0);
    3297                 : 
    3298             414 :                     len = js_GetSrcNoteOffset(sn, 0);
    3299             414 :                     if (len) {
    3300               0 :                         len -= pc - pc2;
    3301               0 :                         LOCAL_ASSERT(len > 0);
    3302               0 :                         js_printf(jp, " if ");
    3303               0 :                         if (!Decompile(ss, pc, len))
    3304               0 :                             return NULL;
    3305               0 :                         js_printf(jp, "%s", POP_STR());
    3306               0 :                         pc += len;
    3307               0 :                         LOCAL_ASSERT(*pc == JSOP_IFEQ);
    3308               0 :                         pc += js_CodeSpec[*pc].length;
    3309                 :                     }
    3310                 : 
    3311             414 :                     js_printf(jp, ") {\n");
    3312             414 :                     jp->indent += 4;
    3313             414 :                     len = 0;
    3314             414 :                     break;
    3315                 :                   default:;
    3316                 :                 }
    3317                 : 
    3318            4374 :                 todo = -2;
    3319                 :               }
    3320            2187 :               break;
    3321                 : 
    3322                 :               case JSOP_LEAVEBLOCK:
    3323                 :               case JSOP_LEAVEBLOCKEXPR:
    3324                 :               {
    3325                 :                 unsigned top, depth;
    3326                 : 
    3327            9999 :                 sn = js_GetSrcNote(jp->script, pc);
    3328            9999 :                 todo = -2;
    3329            9999 :                 if (op == JSOP_LEAVEBLOCKEXPR) {
    3330            1692 :                     LOCAL_ASSERT(SN_TYPE(sn) == SRC_PCBASE);
    3331            1692 :                     rval = POP_STR();
    3332            8307 :                 } else if (sn) {
    3333            4230 :                     LOCAL_ASSERT(op == JSOP_LEAVEBLOCK);
    3334            4230 :                     if (SN_TYPE(sn) == SRC_HIDDEN)
    3335            3816 :                         break;
    3336                 : 
    3337                 :                     /*
    3338                 :                      * This JSOP_LEAVEBLOCK must be for a catch block. If sn's
    3339                 :                      * offset does not equal the model stack depth, there must
    3340                 :                      * be a copy of the exception value on the stack due to a
    3341                 :                      * catch guard (see above, the JSOP_ENTERBLOCK + SRC_CATCH
    3342                 :                      * case code).
    3343                 :                      */
    3344             414 :                     LOCAL_ASSERT(SN_TYPE(sn) == SRC_CATCH);
    3345             414 :                     if ((unsigned)js_GetSrcNoteOffset(sn, 0) != ss->top) {
    3346               0 :                         LOCAL_ASSERT((unsigned)js_GetSrcNoteOffset(sn, 0)
    3347                 :                                      == ss->top - 1);
    3348               0 :                         rval = POP_STR();
    3349               0 :                         LOCAL_ASSERT(strcmp(rval, exception_cookie) == 0);
    3350                 :                     }
    3351                 :                 }
    3352            6183 :                 top = ss->top;
    3353            6183 :                 depth = GET_UINT16(pc);
    3354            6183 :                 LOCAL_ASSERT(top >= depth);
    3355            6183 :                 top -= depth;
    3356            6183 :                 ss->top = top;
    3357            6183 :                 ss->sprinter.setOffset(GetOff(ss, top));
    3358            6183 :                 if (op == JSOP_LEAVEBLOCKEXPR)
    3359            1692 :                     todo = ss->sprinter.put(rval);
    3360            6183 :                 break;
    3361                 :               }
    3362                 : 
    3363                 :               case JSOP_ENTERLET0:
    3364                 :               {
    3365            3888 :                 obj = jp->script->getObject(GET_UINT32_INDEX(pc));
    3366            3888 :                 StaticBlockObject &blockObj = obj->asStaticBlock();
    3367                 : 
    3368            7776 :                 AtomVector atoms(cx);
    3369            3888 :                 if (!GetBlockNames(cx, blockObj, &atoms))
    3370               0 :                     return NULL;
    3371                 : 
    3372            3888 :                 sn = js_GetSrcNote(jp->script, pc);
    3373            3888 :                 LOCAL_ASSERT(SN_TYPE(sn) == SRC_DECL);
    3374            3888 :                 ptrdiff_t letData = js_GetSrcNoteOffset(sn, 0);
    3375            3888 :                 bool groupAssign = LetDataToGroupAssign(letData);
    3376            3888 :                 unsigned letDepth = blockObj.stackDepth();
    3377            3888 :                 LOCAL_ASSERT(letDepth == (unsigned)ss->top - blockObj.slotCount());
    3378            3888 :                 LOCAL_ASSERT(atoms.length() == blockObj.slotCount());
    3379                 : 
    3380                 :                 /*
    3381                 :                  * Build the list of decompiled rhs expressions. Do this before
    3382                 :                  * sprinting the let-head since GetStr can inject stuff on top
    3383                 :                  * of the stack (in case js_DecompileValueGenerator).
    3384                 :                  */
    3385            7776 :                 Vector<const char *> rhsExprs(cx);
    3386            3888 :                 if (!rhsExprs.resize(atoms.length()))
    3387               0 :                     return NULL;
    3388            9441 :                 for (size_t i = 0; i < rhsExprs.length(); ++i)
    3389            5553 :                     rhsExprs[i] = SprintDupeStr(ss, GetStr(ss, letDepth + i));
    3390                 : 
    3391                 :                 /* Build the let head starting at headBegin. */
    3392            3888 :                 ptrdiff_t headBegin = ss->sprinter.getOffset();
    3393                 : 
    3394                 :                 /*
    3395                 :                  * For group assignment, prepend the '[lhs-vars] = [' here,
    3396                 :                  * append rhsExprs in the next loop and append ']' after.
    3397                 :                  */
    3398            3888 :                 if (groupAssign) {
    3399             270 :                     if (Sprint(&ss->sprinter, "[") < 0)
    3400               0 :                         return NULL;
    3401             756 :                     for (size_t i = 0; i < atoms.length(); ++i) {
    3402             486 :                         if (i && Sprint(&ss->sprinter, ", ") < 0)
    3403               0 :                             return NULL;
    3404             486 :                         if (!QuoteString(&ss->sprinter, atoms[i], 0))
    3405               0 :                             return NULL;
    3406                 :                     }
    3407             270 :                     if (Sprint(&ss->sprinter, "] = [") < 0)
    3408               0 :                         return NULL;
    3409                 :                 }
    3410                 : 
    3411            9441 :                 for (size_t i = 0; i < atoms.length(); ++i) {
    3412            5553 :                     const char *rhs = rhsExprs[i];
    3413            5553 :                     if (!strcmp(rhs, SkipString))
    3414             855 :                         continue;
    3415                 : 
    3416            4698 :                     if (i && Sprint(&ss->sprinter, ", ") < 0)
    3417               0 :                         return NULL;
    3418                 : 
    3419            4698 :                     if (groupAssign) {
    3420             486 :                         if (ss->sprinter.put(rhs) < 0)
    3421               0 :                             return NULL;
    3422            4212 :                     } else if (!strncmp(rhs, DestructuredString, DestructuredStringLength)) {
    3423            1908 :                         if (ss->sprinter.put(rhs + DestructuredStringLength) < 0)
    3424               0 :                             return NULL;
    3425                 :                     } else {
    3426            2304 :                         JS_ASSERT(atoms[i] != cx->runtime->atomState.emptyAtom);
    3427            2304 :                         if (!QuoteString(&ss->sprinter, atoms[i], 0))
    3428               0 :                             return NULL;
    3429            2304 :                         if (*rhs) {
    3430            2106 :                             uint8_t prec = js_CodeSpec[ss->opcodes[letDepth + i]].prec;
    3431                 :                             const char *fmt = prec && prec < js_CodeSpec[JSOP_SETLOCAL].prec
    3432                 :                                               ? " = (%s)"
    3433            2106 :                                               : " = %s";
    3434            2106 :                             if (Sprint(&ss->sprinter, fmt, rhs) < 0)
    3435               0 :                                 return NULL;
    3436                 :                         }
    3437                 :                     }
    3438                 :                 }
    3439                 : 
    3440            3888 :                 if (groupAssign && Sprint(&ss->sprinter, "]") < 0)
    3441               0 :                     return NULL;
    3442                 : 
    3443                 :                 /* Clone the let head chars before clobbering the stack. */
    3444            7776 :                 DupBuffer head(cx);
    3445            3888 :                 if (!Dup(ss->sprinter.stringAt(headBegin), &head))
    3446               0 :                     return NULL;
    3447            3888 :                 if (!AssignBlockNamesToPushedSlots(cx, ss, atoms))
    3448               0 :                     return NULL;
    3449                 : 
    3450                 :                 /* Detect 'for (let ...)' desugared into 'let (...) {for}'. */
    3451            3888 :                 jsbytecode *nextpc = pc + JSOP_ENTERLET0_LENGTH;
    3452            3888 :                 if (*nextpc == JSOP_NOP) {
    3453             594 :                     jssrcnote *nextsn = js_GetSrcNote(jp->script, nextpc);
    3454             594 :                     if (nextsn && SN_TYPE(nextsn) == SRC_FOR) {
    3455             594 :                         pc = nextpc;
    3456             594 :                         todo = SprintNormalFor(cx, jp, ss, "let ", head.begin(), pc, &pc, &len);
    3457                 :                         break;
    3458                 :                     }
    3459                 :                 }
    3460                 : 
    3461                 :                 /* Decompile the body and then complete the let block/expr. */
    3462            3294 :                 len = LetDataToOffset(letData);
    3463            3294 :                 pc = nextpc;
    3464            3294 :                 saveop = (JSOp) pc[len];
    3465            3294 :                 todo = SprintLetBody(cx, jp, ss, pc, len, head.begin());
    3466            3888 :                 break;
    3467                 :               }
    3468                 : 
    3469                 :               /*
    3470                 :                * With 'for (let lhs in rhs)' and 'switch (c) { let-decl }',
    3471                 :                * placeholder slots have already been pushed (by JSOP_UNDEFINED).
    3472                 :                * In both the for-let-in and switch-hoisted-let cases:
    3473                 :                *  - there is a non-let slot on top of the stack (hence enterlet1)
    3474                 :                *  - there is no further special let-handling required:
    3475                 :                *    for-let-in will decompile the let head when it decompiles
    3476                 :                *    the loop body prologue; there is no let head to decompile
    3477                 :                *    with switch.
    3478                 :                * Hence, the only thing to do is update the let vars' slots with
    3479                 :                * their names, taking care to preserve the iter/condition value
    3480                 :                * on top of the stack.
    3481                 :                */
    3482                 :               case JSOP_ENTERLET1:
    3483                 :               {
    3484             801 :                 obj = jp->script->getObject(GET_UINT32_INDEX(pc));
    3485             801 :                 StaticBlockObject &blockObj = obj->asStaticBlock();
    3486                 : 
    3487            1602 :                 AtomVector atoms(cx);
    3488             801 :                 if (!GetBlockNames(cx, blockObj, &atoms))
    3489               0 :                     return NULL;
    3490                 : 
    3491             801 :                 LOCAL_ASSERT(js_GetSrcNote(jp->script, pc) == NULL);
    3492             801 :                 LOCAL_ASSERT(ss->top - 1 == blockObj.stackDepth() + blockObj.slotCount());
    3493             801 :                 jsbytecode *nextpc = pc + JSOP_ENTERLET1_LENGTH;
    3494             801 :                 if (*nextpc == JSOP_GOTO) {
    3495             693 :                     LOCAL_ASSERT(SN_TYPE(js_GetSrcNote(jp->script, nextpc)) == SRC_FOR_IN);
    3496                 :                 } else {
    3497             108 :                     LOCAL_ASSERT(*nextpc == JSOP_CONDSWITCH ||
    3498                 :                                  *nextpc == JSOP_TABLESWITCH ||
    3499                 :                                  *nextpc == JSOP_LOOKUPSWITCH);
    3500                 :                 }
    3501                 : 
    3502            1602 :                 DupBuffer rhs(cx);
    3503             801 :                 if (!Dup(PopStr(ss, JSOP_NOP), &rhs))
    3504               0 :                     return NULL;
    3505             801 :                 if (!AssignBlockNamesToPushedSlots(cx, ss, atoms))
    3506               0 :                     return NULL;
    3507             801 :                 if (!PushStr(ss, rhs.begin(), op))
    3508               0 :                     return NULL;
    3509             801 :                 todo = -2;
    3510             801 :                 break;
    3511                 :               }
    3512                 : 
    3513                 :               case JSOP_CALLLOCAL:
    3514                 :               case JSOP_GETLOCAL:
    3515            8559 :                 if (IsVarSlot(jp, pc, &i)) {
    3516            2907 :                     atom = GetArgOrVarAtom(jp, i);
    3517            2907 :                     LOCAL_ASSERT(atom);
    3518            2907 :                     goto do_name;
    3519                 :                 }
    3520            5652 :                 LOCAL_ASSERT((unsigned)i < ss->top);
    3521            5652 :                 sn = js_GetSrcNote(jp->script, pc);
    3522                 : 
    3523                 : #if JS_HAS_DESTRUCTURING
    3524            5652 :                 if (sn && SN_TYPE(sn) == SRC_GROUPASSIGN) {
    3525                 :                     /*
    3526                 :                      * Distinguish a js_DecompileValueGenerator call that
    3527                 :                      * targets op alone, from decompilation of a full group
    3528                 :                      * assignment sequence, triggered by SRC_GROUPASSIGN
    3529                 :                      * annotating the first JSOP_GETLOCAL in the sequence.
    3530                 :                      */
    3531             486 :                     if (endpc - pc > JSOP_GETLOCAL_LENGTH || pc > startpc) {
    3532             486 :                         pc = DecompileGroupAssignment(ss, pc, endpc, sn, &todo);
    3533             486 :                         if (!pc)
    3534               0 :                             return NULL;
    3535             486 :                         LOCAL_ASSERT(*pc == JSOP_POPN);
    3536             486 :                         len = oplen = JSOP_POPN_LENGTH;
    3537             486 :                         goto end_groupassignment;
    3538                 :                     }
    3539                 : 
    3540                 :                     /* Null sn to prevent bogus VarPrefix'ing below. */
    3541               0 :                     sn = NULL;
    3542                 :                 }
    3543                 : #endif
    3544                 : 
    3545            5166 :                 rval = GetLocal(ss, i);
    3546            5166 :                 todo = Sprint(&ss->sprinter, ss_format, VarPrefix(sn), rval);
    3547            5166 :                 break;
    3548                 : 
    3549                 :               case JSOP_SETLOCAL:
    3550            4050 :                 if (IsVarSlot(jp, pc, &i)) {
    3551            2223 :                     atom = GetArgOrVarAtom(jp, i);
    3552            2223 :                     LOCAL_ASSERT(atom);
    3553            2223 :                     goto do_setname;
    3554                 :                 }
    3555            1827 :                 lval = GetLocal(ss, i);
    3556            1827 :                 rval = PopStrDupe(ss, op, &rvalpc);
    3557            1827 :                 goto do_setlval;
    3558                 : 
    3559                 :               case JSOP_INCLOCAL:
    3560                 :               case JSOP_DECLOCAL:
    3561             522 :                 if (IsVarSlot(jp, pc, &i)) {
    3562             279 :                     atom = GetArgOrVarAtom(jp, i);
    3563             279 :                     LOCAL_ASSERT(atom);
    3564             279 :                     goto do_incatom;
    3565                 :                 }
    3566             243 :                 lval = GetLocal(ss, i);
    3567             243 :                 goto do_inclval;
    3568                 : 
    3569                 :               case JSOP_LOCALINC:
    3570                 :               case JSOP_LOCALDEC:
    3571              99 :                 if (IsVarSlot(jp, pc, &i)) {
    3572              45 :                     atom = GetArgOrVarAtom(jp, i);
    3573              45 :                     LOCAL_ASSERT(atom);
    3574              45 :                     goto do_atominc;
    3575                 :                 }
    3576              54 :                 lval = GetLocal(ss, i);
    3577              54 :                 goto do_lvalinc;
    3578                 : 
    3579                 :               case JSOP_RETRVAL:
    3580            3735 :                 todo = -2;
    3581            3735 :                 break;
    3582                 : 
    3583                 :               case JSOP_RETURN:
    3584            4122 :                 LOCAL_ASSERT(jp->fun);
    3585            4122 :                 fun = jp->fun;
    3586            4122 :                 if (fun->flags & JSFUN_EXPR_CLOSURE) {
    3587                 :                     /* Turn on parens around comma-expression here. */
    3588              36 :                     op = JSOP_SETNAME;
    3589              36 :                     rval = PopStr(ss, op, &rvalpc);
    3590              36 :                     bool parens = (*rval == '{');
    3591              36 :                     if (parens)
    3592               0 :                         js_printf(jp, "(");
    3593              36 :                     SprintOpcodePermanent(jp, rval, rvalpc);
    3594                 :                     js_printf(jp, parens ? ")%s" : "%s",
    3595               0 :                               ((fun->flags & JSFUN_LAMBDA) || !fun->atom)
    3596                 :                               ? ""
    3597              36 :                               : ";");
    3598              36 :                     todo = -2;
    3599              36 :                     break;
    3600                 :                 }
    3601                 :                 /* FALL THROUGH */
    3602                 : 
    3603                 :               case JSOP_SETRVAL:
    3604            7821 :                 rval = PopStr(ss, op, &rvalpc);
    3605            7821 :                 if (*rval != '\0') {
    3606            7812 :                     js_printf(jp, "\t%s ", js_return_str);
    3607            7812 :                     SprintOpcodePermanent(jp, rval, rvalpc);
    3608            7812 :                     js_printf(jp, ";\n");
    3609                 :                 } else {
    3610               9 :                     js_printf(jp, "\t%s;\n", js_return_str);
    3611                 :                 }
    3612            7821 :                 todo = -2;
    3613            7821 :                 break;
    3614                 : 
    3615                 : #if JS_HAS_GENERATORS
    3616                 :               case JSOP_YIELD:
    3617                 : #if JS_HAS_GENERATOR_EXPRS
    3618             135 :                 if (!ss->inGenExp || !(sn = js_GetSrcNote(jp->script, pc)))
    3619                 : #endif
    3620                 :                 {
    3621                 :                     /* Turn off most parens. */
    3622               0 :                     op = JSOP_SETNAME;
    3623               0 :                     rval = POP_STR();
    3624                 :                     todo = (*rval != '\0')
    3625                 :                            ? Sprint(&ss->sprinter,
    3626               0 :                                     (strncmp(rval, js_yield_str, 5) == 0 &&
    3627               0 :                                      (rval[5] == ' ' || rval[5] == '\0'))
    3628                 :                                     ? "%s (%s)"
    3629                 :                                     : "%s %s",
    3630               0 :                                     js_yield_str, rval)
    3631               0 :                            : ss->sprinter.put(js_yield_str);
    3632               0 :                     break;
    3633                 :                 }
    3634                 : 
    3635                 : #if JS_HAS_GENERATOR_EXPRS
    3636             135 :                 LOCAL_ASSERT(SN_TYPE(sn) == SRC_HIDDEN);
    3637                 :                 /* FALL THROUGH */
    3638                 : #endif
    3639                 : 
    3640                 :               case JSOP_ARRAYPUSH:
    3641                 :               {
    3642                 :                 /* Turn off most parens. */
    3643             261 :                 op = JSOP_SETNAME;
    3644                 : 
    3645                 :                 /* Pop the expression being pushed or yielded. */
    3646             261 :                 rval = POP_STR();
    3647                 : 
    3648                 :                 /*
    3649                 :                  * Skip the for loop head stacked by JSOP_GOTO:SRC_FOR_IN until
    3650                 :                  * we hit a block local slot (note empty destructuring patterns
    3651                 :                  * result in unit-count blocks).
    3652                 :                  */
    3653             261 :                 unsigned pos = ss->top;
    3654             783 :                 while (pos != 0) {
    3655             522 :                     op = (JSOp) ss->opcodes[--pos];
    3656             522 :                     if (op == JSOP_ENTERBLOCK)
    3657             261 :                         break;
    3658                 :                 }
    3659             261 :                 JS_ASSERT(op == JSOP_ENTERBLOCK);
    3660                 : 
    3661                 :                 /*
    3662                 :                  * Here, forpos must index the space before the left-most |for|
    3663                 :                  * in the single string of accumulated |for| heads and optional
    3664                 :                  * final |if (condition)|.
    3665                 :                  */
    3666             261 :                 unsigned forpos = pos + 1;
    3667             261 :                 LOCAL_ASSERT(forpos < ss->top);
    3668                 : 
    3669                 :                 /*
    3670                 :                  * Now move pos downward over the block's local slots. Even an
    3671                 :                  * empty destructuring pattern has one (dummy) local.
    3672                 :                  */
    3673             657 :                 while (ss->opcodes[pos] == JSOP_ENTERBLOCK) {
    3674             270 :                     if (pos == 0)
    3675             135 :                         break;
    3676             135 :                     --pos;
    3677                 :                 }
    3678                 : 
    3679                 : #if JS_HAS_GENERATOR_EXPRS
    3680             261 :                 if (saveop == JSOP_YIELD) {
    3681                 :                     /*
    3682                 :                      * Generator expression: decompile just rval followed by
    3683                 :                      * the string starting at forpos. Leave the result string
    3684                 :                      * in ss->offsets[0] so it can be recovered by our caller
    3685                 :                      * (the JSOP_ANONFUNOBJ with SRC_GENEXP case). Bump the
    3686                 :                      * top of stack to balance yield, which is an expression
    3687                 :                      * (so has neutral stack balance).
    3688                 :                      */
    3689             135 :                     LOCAL_ASSERT(pos == 0);
    3690             135 :                     xval = ss->sprinter.stringAt(ss->offsets[forpos]);
    3691             135 :                     ss->sprinter.setOffset(PAREN_SLOP);
    3692             135 :                     todo = Sprint(&ss->sprinter, ss_format, rval, xval);
    3693             135 :                     if (todo < 0)
    3694               0 :                         return NULL;
    3695             135 :                     ss->offsets[0] = todo;
    3696             135 :                     ++ss->top;
    3697             135 :                     return pc;
    3698                 :                 }
    3699                 : #endif /* JS_HAS_GENERATOR_EXPRS */
    3700                 : 
    3701                 :                 /*
    3702                 :                  * Array comprehension: retract the sprinter to the beginning
    3703                 :                  * of the array initialiser and decompile "[<rval> for ...]".
    3704                 :                  */
    3705             126 :                 JS_ASSERT(jp->script->nfixed + pos == GET_UINT16(pc));
    3706             126 :                 LOCAL_ASSERT(ss->opcodes[pos] == JSOP_NEWINIT);
    3707                 : 
    3708             126 :                 ptrdiff_t start = ss->offsets[pos];
    3709             126 :                 LOCAL_ASSERT(ss->sprinter[start] == '[' ||
    3710                 :                              ss->sprinter[start] == '#');
    3711             126 :                 LOCAL_ASSERT(forpos < ss->top);
    3712             126 :                 xval = ss->sprinter.stringAt(ss->offsets[forpos]);
    3713             126 :                 lval = ss->sprinter.stringAt(start);
    3714             126 :                 ss->sprinter.setOffset(lval);
    3715                 : 
    3716             126 :                 todo = Sprint(&ss->sprinter, sss_format, lval, rval, xval);
    3717             126 :                 if (todo < 0)
    3718               0 :                     return NULL;
    3719             126 :                 ss->offsets[pos] = todo;
    3720             126 :                 todo = -2;
    3721             126 :                 break;
    3722                 :               }
    3723                 : #endif /* JS_HAS_GENERATORS */
    3724                 : 
    3725                 :               case JSOP_THROWING:
    3726               0 :                 todo = -2;
    3727               0 :                 break;
    3728                 : 
    3729                 :               case JSOP_THROW:
    3730             117 :                 sn = js_GetSrcNote(jp->script, pc);
    3731             117 :                 todo = -2;
    3732             117 :                 if (sn && SN_TYPE(sn) == SRC_HIDDEN)
    3733               0 :                     break;
    3734             117 :                 rval = PopStr(ss, op, &rvalpc);
    3735             117 :                 js_printf(jp, "\t%s ", js_throw_str);
    3736             117 :                 SprintOpcodePermanent(jp, rval, rvalpc);
    3737             117 :                 js_printf(jp, ";\n");
    3738             117 :                 break;
    3739                 : 
    3740                 :               case JSOP_ITER:
    3741            1062 :                 forOf = (GET_UINT8(pc) == JSITER_FOR_OF);
    3742            1062 :                 foreach = (GET_UINT8(pc) & (JSITER_FOREACH | JSITER_KEYVALUE)) ==
    3743            1062 :                           JSITER_FOREACH;
    3744            1062 :                 todo = -2;
    3745            1062 :                 break;
    3746                 : 
    3747                 :               case JSOP_MOREITER:
    3748               0 :                 JS_NOT_REACHED("JSOP_MOREITER");
    3749                 :                 break;
    3750                 : 
    3751                 :               case JSOP_ENDITER:
    3752            1494 :                 sn = js_GetSrcNote(jp->script, pc);
    3753            1494 :                 todo = -2;
    3754            1494 :                 if (sn && SN_TYPE(sn) == SRC_HIDDEN)
    3755             432 :                     break;
    3756            1062 :                 (void) PopOff(ss, op);
    3757            1062 :                 break;
    3758                 : 
    3759                 :               case JSOP_GOTO:
    3760            2115 :                 sn = js_GetSrcNote(jp->script, pc);
    3761            2115 :                 switch (sn ? SN_TYPE(sn) : SRC_NULL) {
    3762                 :                   case SRC_FOR_IN:
    3763                 :                     /*
    3764                 :                      * The bytecode around pc looks like this:
    3765                 :                      *     <<RHS>>
    3766                 :                      *     iter
    3767                 :                      * pc: goto/gotox C         [src_for_in(B, D)]
    3768                 :                      *  A: <<LHS = iternext>>
    3769                 :                      *  B: pop                  [maybe a src_decl_var/let]
    3770                 :                      *     <<S>>
    3771                 :                      *  C: moreiter
    3772                 :                      *     ifne/ifnex A
    3773                 :                      *     enditer
    3774                 :                      *  D: ...
    3775                 :                      *
    3776                 :                      * In an array comprehension or generator expression, we
    3777                 :                      * construct the for-head and store it in the slot pushed
    3778                 :                      * by JSOP_ITER, then recurse to decompile S. The
    3779                 :                      * culminating JSOP_ARRAYPUSH or JSOP_YIELD instruction
    3780                 :                      * (which S must contain, by construction) glues all the
    3781                 :                      * clauses together.
    3782                 :                      *
    3783                 :                      * Otherwise this is a for-in statement. We eagerly output
    3784                 :                      * the for-head and recurse to decompile the controlled
    3785                 :                      * statement S.
    3786                 :                      *
    3787                 :                      * We never decompile the obligatory JSOP_POP,
    3788                 :                      * JSOP_MOREITER or JSOP_IFNE, though we do quick asserts
    3789                 :                      * to check that they are there.
    3790                 :                      */
    3791            1062 :                     cond = GET_JUMP_OFFSET(pc);
    3792            1062 :                     next = js_GetSrcNoteOffset(sn, 0);
    3793            1062 :                     tail = js_GetSrcNoteOffset(sn, 1);
    3794            1062 :                     JS_ASSERT(pc[next] == JSOP_POP);
    3795            1062 :                     JS_ASSERT(pc[cond] == JSOP_LOOPENTRY);
    3796            1062 :                     cond += JSOP_LOOPENTRY_LENGTH;
    3797            1062 :                     JS_ASSERT(pc[cond] == JSOP_MOREITER);
    3798            1062 :                     DECOMPILE_CODE(pc + oplen, next - oplen);
    3799            1062 :                     lval = POP_STR();
    3800                 : 
    3801                 :                     /*
    3802                 :                      * This string "<next>" comes from jsopcode.tbl. It stands
    3803                 :                      * for the result pushed by JSOP_ITERNEXT.
    3804                 :                      */
    3805            1062 :                     JS_ASSERT(strcmp(lval + strlen(lval) - 9, " = <next>") == 0);
    3806            1062 :                     const_cast<char *>(lval)[strlen(lval) - 9] = '\0';
    3807            1062 :                     LOCAL_ASSERT(ss->top >= 1);
    3808                 : 
    3809            1062 :                     if (ss->inArrayInit || ss->inGenExp) {
    3810             261 :                         rval = POP_STR();
    3811             261 :                         if (ss->top >= 1 && ss->opcodes[ss->top - 1] == JSOP_FORLOCAL) {
    3812               0 :                             ss->sprinter.setOffset(ss->offsets[ss->top] - PAREN_SLOP);
    3813               0 :                             if (Sprint(&ss->sprinter, " %s (%s %s %s)",
    3814                 :                                        foreach ? js_for_each_str : js_for_str,
    3815                 :                                        lval,
    3816                 :                                        forOf ? "of" : "in",
    3817               0 :                                        rval) < 0) {
    3818               0 :                                 return NULL;
    3819                 :                             }
    3820                 : 
    3821                 :                             /*
    3822                 :                              * Do not AddParenSlop here, as we will push the
    3823                 :                              * top-most offset again, which will add paren slop
    3824                 :                              * for us. We must push to balance the stack budget
    3825                 :                              * when nesting for heads in a comprehension.
    3826                 :                              */
    3827               0 :                             todo = ss->offsets[ss->top - 1];
    3828                 :                         } else {
    3829                 :                             todo = Sprint(&ss->sprinter, " %s (%s %s %s)",
    3830                 :                                           foreach ? js_for_each_str : js_for_str,
    3831                 :                                           lval,
    3832                 :                                           forOf ? "of" : "in",
    3833             261 :                                           rval);
    3834                 :                         }
    3835             261 :                         if (todo < 0 || !PushOff(ss, todo, JSOP_FORLOCAL))
    3836               0 :                             return NULL;
    3837             261 :                         DECOMPILE_CODE(pc + next + JSOP_POP_LENGTH, cond - next - JSOP_POP_LENGTH);
    3838                 :                     } else {
    3839                 :                         /*
    3840                 :                          * As above, rval or an extension of it must remain
    3841                 :                          * stacked during loop body decompilation.
    3842                 :                          */
    3843             801 :                         rval = GetStr(ss, ss->top - 1);
    3844             801 :                         xval = VarPrefix(js_GetSrcNote(jp->script, pc + next));
    3845                 :                         js_printf(jp, "\t%s (%s%s %s %s) {\n",
    3846                 :                                   foreach ? js_for_each_str : js_for_str,
    3847                 :                                   xval,
    3848                 :                                   lval,
    3849                 :                                   forOf ? "of" : "in",
    3850             801 :                                   rval);
    3851             801 :                         jp->indent += 4;
    3852             801 :                         DECOMPILE_CODE(pc + next + JSOP_POP_LENGTH, cond - next - JSOP_POP_LENGTH);
    3853             801 :                         jp->indent -= 4;
    3854             801 :                         js_printf(jp, "\t}\n");
    3855                 :                     }
    3856                 : 
    3857            1062 :                     pc += tail;
    3858            1062 :                     LOCAL_ASSERT(*pc == JSOP_IFNE);
    3859            1062 :                     len = js_CodeSpec[*pc].length;
    3860            1062 :                     break;
    3861                 : 
    3862                 :                   case SRC_WHILE:
    3863               9 :                     cond = GET_JUMP_OFFSET(pc);
    3864               9 :                     tail = js_GetSrcNoteOffset(sn, 0);
    3865               9 :                     DECOMPILE_CODE(pc + cond, tail - cond);
    3866               9 :                     js_printf(jp, "\twhile (");
    3867               9 :                     rval = PopCondStr(ss, &rvalpc);
    3868               9 :                     SprintOpcodePermanent(jp, rval, rvalpc);
    3869               9 :                     js_printf(jp, ") {\n");
    3870               9 :                     jp->indent += 4;
    3871               9 :                     DECOMPILE_CODE(pc + oplen, cond - oplen);
    3872               9 :                     jp->indent -= 4;
    3873               9 :                     js_printf(jp, "\t}\n");
    3874               9 :                     pc += tail;
    3875               9 :                     LOCAL_ASSERT(*pc == JSOP_IFNE);
    3876               9 :                     len = js_CodeSpec[*pc].length;
    3877               9 :                     todo = -2;
    3878               9 :                     break;
    3879                 : 
    3880                 :                   case SRC_CONT2LABEL:
    3881               0 :                     GET_SOURCE_NOTE_ATOM(sn, atom);
    3882               0 :                     rval = QuoteString(&ss->sprinter, atom, 0);
    3883               0 :                     if (!rval)
    3884               0 :                         return NULL;
    3885               0 :                     ss->sprinter.setOffset(rval);
    3886               0 :                     js_printf(jp, "\tcontinue %s;\n", rval);
    3887               0 :                     break;
    3888                 : 
    3889                 :                   case SRC_CONTINUE:
    3890               0 :                     js_printf(jp, "\tcontinue;\n");
    3891               0 :                     break;
    3892                 : 
    3893                 :                   case SRC_BREAK2LABEL:
    3894              54 :                     GET_SOURCE_NOTE_ATOM(sn, atom);
    3895              54 :                     rval = QuoteString(&ss->sprinter, atom, 0);
    3896              54 :                     if (!rval)
    3897               0 :                         return NULL;
    3898              54 :                     ss->sprinter.setOffset(rval);
    3899              54 :                     js_printf(jp, "\tbreak %s;\n", rval);
    3900              54 :                     break;
    3901                 : 
    3902                 :                   case SRC_HIDDEN:
    3903             828 :                     break;
    3904                 : 
    3905                 :                   default:
    3906             162 :                     js_printf(jp, "\tbreak;\n");
    3907             162 :                     break;
    3908                 :                 }
    3909            2115 :                 todo = -2;
    3910            2115 :                 break;
    3911                 : 
    3912                 :               case JSOP_IFEQ:
    3913                 :               {
    3914            1413 :                 JSBool elseif = JS_FALSE;
    3915                 : 
    3916                 :               if_again:
    3917            1413 :                 len = GET_JUMP_OFFSET(pc);
    3918            1413 :                 sn = js_GetSrcNote(jp->script, pc);
    3919                 : 
    3920            1413 :                 switch (sn ? SN_TYPE(sn) : SRC_NULL) {
    3921                 :                   case SRC_IF:
    3922                 :                   case SRC_IF_ELSE:
    3923            1395 :                     rval = PopCondStr(ss, &rvalpc);
    3924            1395 :                     if (ss->inArrayInit || ss->inGenExp) {
    3925               9 :                         LOCAL_ASSERT(SN_TYPE(sn) == SRC_IF);
    3926               9 :                         ss->sprinter.setOffset(ss->sprinter.getOffset() - PAREN_SLOP);
    3927               9 :                         if (Sprint(&ss->sprinter, " if (%s)", rval) < 0)
    3928               0 :                             return NULL;
    3929               9 :                         AddParenSlop(ss);
    3930                 :                     } else {
    3931            1386 :                         js_printf(jp, elseif ? " if (" : "\tif (");
    3932            1386 :                         SprintOpcodePermanent(jp, rval, rvalpc);
    3933            1386 :                         js_printf(jp, ") {\n");
    3934            1386 :                         jp->indent += 4;
    3935                 :                     }
    3936                 : 
    3937            1395 :                     if (SN_TYPE(sn) == SRC_IF) {
    3938            1368 :                         DECOMPILE_CODE(pc + oplen, len - oplen);
    3939                 :                     } else {
    3940              27 :                         LOCAL_ASSERT(!ss->inArrayInit && !ss->inGenExp);
    3941              27 :                         tail = js_GetSrcNoteOffset(sn, 0);
    3942              27 :                         DECOMPILE_CODE(pc + oplen, tail - oplen);
    3943              27 :                         jp->indent -= 4;
    3944              27 :                         pc += tail;
    3945              27 :                         LOCAL_ASSERT(*pc == JSOP_GOTO);
    3946              27 :                         oplen = js_CodeSpec[*pc].length;
    3947              27 :                         len = GET_JUMP_OFFSET(pc);
    3948              27 :                         js_printf(jp, "\t} else");
    3949                 : 
    3950                 :                         /*
    3951                 :                          * If the second offset for sn is non-zero, it tells
    3952                 :                          * the distance from the goto around the else, to the
    3953                 :                          * ifeq for the if inside the else that forms an "if
    3954                 :                          * else if" chain.  Thus cond spans the condition of
    3955                 :                          * the second if, so we simply decompile it and start
    3956                 :                          * over at label if_again.
    3957                 :                          */
    3958              27 :                         cond = js_GetSrcNoteOffset(sn, 1);
    3959              27 :                         if (cond != 0) {
    3960               0 :                             cond -= tail;
    3961               0 :                             DECOMPILE_CODE(pc + oplen, cond - oplen);
    3962               0 :                             pc += cond;
    3963               0 :                             oplen = js_CodeSpec[*pc].length;
    3964               0 :                             elseif = JS_TRUE;
    3965               0 :                             goto if_again;
    3966                 :                         }
    3967                 : 
    3968              27 :                         js_printf(jp, " {\n");
    3969              27 :                         jp->indent += 4;
    3970              27 :                         DECOMPILE_CODE(pc + oplen, len - oplen);
    3971                 :                     }
    3972                 : 
    3973            1395 :                     if (!ss->inArrayInit && !ss->inGenExp) {
    3974            1386 :                         jp->indent -= 4;
    3975            1386 :                         js_printf(jp, "\t}\n");
    3976                 :                     }
    3977            1395 :                     todo = -2;
    3978            1395 :                     break;
    3979                 : 
    3980                 :                   case SRC_COND:
    3981              18 :                     xval = PopStrDupe(ss, op, &xvalpc);
    3982              18 :                     len = js_GetSrcNoteOffset(sn, 0);
    3983              18 :                     DECOMPILE_CODE(pc + oplen, len - oplen);
    3984              18 :                     lval = PopStrDupe(ss, op, &lvalpc);
    3985              18 :                     pushpc = pc;
    3986              18 :                     pc += len;
    3987              18 :                     LOCAL_ASSERT(*pc == JSOP_GOTO);
    3988              18 :                     oplen = js_CodeSpec[*pc].length;
    3989              18 :                     len = GET_JUMP_OFFSET(pc);
    3990              18 :                     DECOMPILE_CODE(pc + oplen, len - oplen);
    3991              18 :                     rval = PopStrDupe(ss, op, &rvalpc);
    3992              18 :                     todo = ss->sprinter.getOffset();
    3993              18 :                     SprintOpcode(ss, xval, xvalpc, pushpc, todo);
    3994              18 :                     ss->sprinter.put(" ? ");
    3995              18 :                     SprintOpcode(ss, lval, lvalpc, pushpc, todo);
    3996              18 :                     ss->sprinter.put(" : ");
    3997              18 :                     SprintOpcode(ss, rval, rvalpc, pushpc, todo);
    3998              18 :                     break;
    3999                 : 
    4000                 :                   default:
    4001               0 :                     break;
    4002                 :                 }
    4003            1413 :                 break;
    4004                 :               }
    4005                 : 
    4006                 :               case JSOP_IFNE:
    4007               0 :                 LOCAL_ASSERT(0);
    4008                 :                 break;
    4009                 : 
    4010                 :               case JSOP_OR:
    4011               9 :                 xval = "||";
    4012                 : 
    4013                 :               do_logical_connective:
    4014                 :                 /* Top of stack is the first clause in a disjunction (||). */
    4015              36 :                 lval = PopStrDupe(ss, op, &lvalpc);
    4016              36 :                 done = pc + GET_JUMP_OFFSET(pc);
    4017              36 :                 pushpc = pc;
    4018              36 :                 pc += len;
    4019              36 :                 JS_ASSERT(*pc == JSOP_POP);
    4020              36 :                 pc += JSOP_POP_LENGTH;
    4021              36 :                 len = done - pc;
    4022              36 :                 if (!Decompile(ss, pc, len))
    4023               0 :                     return NULL;
    4024              36 :                 rval = PopStrDupe(ss, op, &rvalpc);
    4025              36 :                 if (!rval)
    4026               0 :                     return NULL;
    4027              36 :                 todo = ss->sprinter.getOffset();
    4028              36 :                 SprintOpcode(ss, lval, lvalpc, pushpc, todo);
    4029              90 :                 if (jp->pretty &&
    4030              54 :                     jp->indent + 4 + strlen(lval) + 4 + strlen(rval) > 75) {
    4031               0 :                     Sprint(&ss->sprinter, " %s\n", xval);
    4032               0 :                     Sprint(&ss->sprinter, "%*s", jp->indent + 4, "");
    4033                 :                 } else {
    4034              36 :                     Sprint(&ss->sprinter, " %s ", xval);
    4035                 :                 }
    4036              36 :                 SprintOpcode(ss, rval, rvalpc, pushpc, todo);
    4037              36 :                 break;
    4038                 : 
    4039                 :               case JSOP_AND:
    4040              27 :                 xval = "&&";
    4041              27 :                 goto do_logical_connective;
    4042                 : 
    4043                 :               case JSOP_ENUMELEM:
    4044                 :               case JSOP_ENUMCONSTELEM:
    4045                 :                 /*
    4046                 :                  * The stack has the object under the (top) index expression.
    4047                 :                  * The "rval" property id is underneath those two on the stack.
    4048                 :                  * The for loop body net and gross lengths can now be adjusted
    4049                 :                  * to account for the length of the indexing expression that
    4050                 :                  * came after JSOP_FORELEM and before JSOP_ENUMELEM.
    4051                 :                  */
    4052               0 :                 atom = NULL;
    4053               0 :                 op = JSOP_NOP;          /* turn off parens around xval */
    4054               0 :                 xval = POP_STR();
    4055               0 :                 op = JSOP_GETELEM;      /* lval must have high precedence */
    4056               0 :                 lval = POP_STR();
    4057               0 :                 op = saveop;
    4058               0 :                 rval = POP_STR();
    4059               0 :                 LOCAL_ASSERT(strcmp(rval, forelem_cookie) == 0);
    4060               0 :                 if (*xval == '\0') {
    4061               0 :                     todo = ss->sprinter.put(lval);
    4062                 :                 } else {
    4063                 :                     todo = Sprint(&ss->sprinter,
    4064                 :                                   (JOF_OPMODE(lastop) == JOF_XMLNAME)
    4065                 :                                   ? dot_format
    4066                 :                                   : index_format,
    4067               0 :                                   lval, xval);
    4068                 :                 }
    4069               0 :                 break;
    4070                 : 
    4071                 :               case JSOP_GETTER:
    4072                 :               case JSOP_SETTER:
    4073               0 :                 todo = -2;
    4074               0 :                 break;
    4075                 : 
    4076                 :               case JSOP_DUP2:
    4077               0 :                 rval = GetStr(ss, ss->top-2);
    4078               0 :                 todo = ss->sprinter.put(rval);
    4079               0 :                 if (todo < 0 || !PushOff(ss, todo,
    4080               0 :                                          (JSOp) ss->opcodes[ss->top-2])) {
    4081               0 :                     return NULL;
    4082                 :                 }
    4083                 :                 /* FALL THROUGH */
    4084                 : 
    4085                 :               case JSOP_DUP:
    4086                 : #if JS_HAS_DESTRUCTURING
    4087            4149 :                 sn = js_GetSrcNote(jp->script, pc);
    4088            4149 :                 if (sn) {
    4089            3312 :                     if (SN_TYPE(sn) == SRC_DESTRUCT) {
    4090            1404 :                         pc = DecompileDestructuring(ss, pc, endpc);
    4091            1404 :                         if (!pc)
    4092               0 :                             return NULL;
    4093                 : 
    4094                 :                         /* Left-hand side never needs parens. */
    4095            1404 :                         JS_ASSERT(js_CodeSpec[JSOP_POP].prec <= 3);
    4096            1404 :                         lval = PopStr(ss, JSOP_POP);
    4097                 : 
    4098                 :                         /* Make sure comma-expression on rhs gets parens. */
    4099            1404 :                         JS_ASSERT(js_CodeSpec[JSOP_SETNAME].prec > js_CodeSpec[JSOP_POP].prec);
    4100            1404 :                         rval = PopStr(ss, JSOP_SETNAME);
    4101                 : 
    4102            1404 :                         if (strcmp(rval, forelem_cookie) == 0) {
    4103                 :                             todo = Sprint(&ss->sprinter, ss_format,
    4104               0 :                                           VarPrefix(sn), lval);
    4105                 : 
    4106                 :                             /* Skip POP so the SRC_FOR_IN code can pop for itself. */
    4107               0 :                             if (*pc == JSOP_POP)
    4108               0 :                                 len = JSOP_POP_LENGTH;
    4109                 :                         } else {
    4110                 :                             todo = Sprint(&ss->sprinter, "%s%s = %s",
    4111            1404 :                                           VarPrefix(sn), lval, rval);
    4112                 :                         }
    4113                 : 
    4114            1404 :                         op = saveop = JSOP_ENUMELEM;
    4115            1404 :                         len = 0;
    4116                 :                     } else {
    4117            1908 :                         LOCAL_ASSERT(SN_TYPE(sn) == SRC_DESTRUCTLET);
    4118                 : 
    4119            1908 :                         ptrdiff_t offsetToLet = js_GetSrcNoteOffset(sn, 0);
    4120            1908 :                         LOCAL_ASSERT(*(pc + offsetToLet) == JSOP_ENTERLET0);
    4121                 : 
    4122            1908 :                         obj = jp->script->getObject(GET_UINT32_INDEX(pc + offsetToLet));
    4123            1908 :                         StaticBlockObject &blockObj = obj->asStaticBlock();
    4124                 : 
    4125            1908 :                         uint32_t blockDepth = blockObj.stackDepth();
    4126            1908 :                         LOCAL_ASSERT(blockDepth < ss->top);
    4127            1908 :                         LOCAL_ASSERT(ss->top <= blockDepth + blockObj.slotCount());
    4128                 : 
    4129            3816 :                         AtomVector atoms(cx);
    4130            1908 :                         if (!GetBlockNames(cx, blockObj, &atoms))
    4131               0 :                             return NULL;
    4132                 : 
    4133                 :                         /*
    4134                 :                          * Skip any initializers preceding this one. E.g., in
    4135                 :                          *   let (w=1, x=2, [y,z] = a) { ... }
    4136                 :                          * skip 'w' and 'x' for the JSOP_DUP of '[y,z] = a'.
    4137                 :                          */
    4138            1908 :                         AtomRange letNames = atoms.all();
    4139            1908 :                         uint32_t curDepth = ss->top - 1 /* initializer */;
    4140            2367 :                         for (uint32_t i = blockDepth; i < curDepth; ++i)
    4141             459 :                             letNames.popFront();
    4142                 : 
    4143                 :                         /*
    4144                 :                          * Pop and copy the rhs before it gets clobbered.
    4145                 :                          * Use JSOP_SETLOCAL's precedence since this is =.
    4146                 :                          */
    4147            3816 :                         DupBuffer rhs(cx);
    4148            1908 :                         if (!Dup(PopStr(ss, JSOP_SETLOCAL), &rhs))
    4149               0 :                             return NULL;
    4150                 : 
    4151                 :                         /* Destructure, tracking how many vars were bound. */
    4152            1908 :                         size_t remainBefore = letNames.remain();
    4153            1908 :                         pc = DecompileDestructuring(ss, pc, endpc, &letNames);
    4154            1908 :                         if (!pc)
    4155               0 :                             return NULL;
    4156            1908 :                         size_t remainAfter = letNames.remain();
    4157                 : 
    4158                 :                         /*
    4159                 :                          * Merge the lhs and rhs and prefix with a cookie to
    4160                 :                          * tell enterlet0 not to prepend "name = ".
    4161                 :                          */
    4162            1908 :                         const char *lhs = PopStr(ss, JSOP_NOP);
    4163                 :                         ptrdiff_t off = Sprint(&ss->sprinter, "%s%s = %s",
    4164            1908 :                                                DestructuredString, lhs, rhs.begin());
    4165            1908 :                         if (off < 0 || !PushOff(ss, off, JSOP_NOP))
    4166               0 :                             return NULL;
    4167                 : 
    4168                 :                         /*
    4169                 :                          * Only one slot has been pushed (holding the entire
    4170                 :                          * decompiled destructuring expression). However, the
    4171                 :                          * abstract depth needs one slot per bound var, so push
    4172                 :                          * empty strings for the remainder. We don't have to
    4173                 :                          * worry about empty destructuring because the parser
    4174                 :                          * ensures that there is always at least one pushed
    4175                 :                          * slot for each destructuring lhs.
    4176                 :                          */
    4177            1908 :                         LOCAL_ASSERT(remainBefore >= remainAfter);
    4178            1908 :                         LOCAL_ASSERT(remainBefore > remainAfter || remainAfter > 0);
    4179            2493 :                         for (size_t i = remainBefore - 1; i > remainAfter; --i) {
    4180             585 :                             if (!PushStr(ss, SkipString, JSOP_NOP))
    4181               0 :                                 return NULL;
    4182                 :                         }
    4183                 : 
    4184            1908 :                         LOCAL_ASSERT(*pc == JSOP_POP);
    4185            1908 :                         pc += JSOP_POP_LENGTH;
    4186                 : 
    4187                 :                         /* Eat up the JSOP_UNDEFINED following empty destructuring. */
    4188            1908 :                         if (remainBefore == remainAfter) {
    4189             999 :                             LOCAL_ASSERT(*pc == JSOP_UNDEFINED);
    4190             999 :                             pc += JSOP_UNDEFINED_LENGTH;
    4191                 :                         }
    4192                 : 
    4193            1908 :                         len = 0;
    4194            3816 :                         todo = -2;
    4195                 :                     }
    4196            3312 :                     break;
    4197                 :                 }
    4198                 : #endif
    4199                 : 
    4200             837 :                 rval = GetStr(ss, ss->top-1);
    4201             837 :                 saveop = (JSOp) ss->opcodes[ss->top-1];
    4202             837 :                 todo = ss->sprinter.put(rval);
    4203             837 :                 break;
    4204                 : 
    4205                 :               case JSOP_SWAP:
    4206             747 :                 Swap(ss->offsets[ss->top-1], ss->offsets[ss->top-2]);
    4207             747 :                 Swap(ss->opcodes[ss->top-1], ss->opcodes[ss->top-2]);
    4208             747 :                 Swap(ss->bytecodes[ss->top-1], ss->bytecodes[ss->top-2]);
    4209             747 :                 todo = -2;
    4210             747 :                 break;
    4211                 : 
    4212                 :               case JSOP_SETARG:
    4213             117 :                 atom = GetArgOrVarAtom(jp, GET_ARGNO(pc));
    4214             117 :                 LOCAL_ASSERT(atom);
    4215             117 :                 goto do_setname;
    4216                 : 
    4217                 :               case JSOP_SETCONST:
    4218                 :               case JSOP_SETNAME:
    4219                 :               case JSOP_SETGNAME:
    4220             162 :                 LOAD_ATOM(0);
    4221                 : 
    4222                 :               do_setname:
    4223            2502 :                 lval = QuoteString(&ss->sprinter, atom, 0);
    4224            2502 :                 if (!lval)
    4225               0 :                     return NULL;
    4226            2502 :                 rval = PopStrDupe(ss, op, &rvalpc);
    4227            2502 :                 if (op == JSOP_SETNAME || op == JSOP_SETGNAME)
    4228             162 :                     (void) PopOff(ss, op);
    4229                 : 
    4230                 :               do_setlval:
    4231            4329 :                 sn = js_GetSrcNote(jp->script, pc - 1);
    4232            4329 :                 todo = ss->sprinter.getOffset();
    4233            4329 :                 if (sn && SN_TYPE(sn) == SRC_ASSIGNOP) {
    4234                 :                     const char *token =
    4235                 :                         GetTokenForAssignment(jp, sn, lastop, pc, rvalpc,
    4236             540 :                                               &lastlvalpc, &lastrvalpc);
    4237             540 :                     Sprint(&ss->sprinter, "%s %s= ", lval, token);
    4238             540 :                     SprintOpcode(ss, rval, rvalpc, pc, todo);
    4239                 :                 } else {
    4240            3789 :                     sn = js_GetSrcNote(jp->script, pc);
    4241            3789 :                     const char *prefix = VarPrefix(sn);
    4242            3789 :                     Sprint(&ss->sprinter, "%s%s = ", prefix, lval);
    4243            3789 :                     SprintOpcode(ss, rval, rvalpc, pc, todo);
    4244                 :                 }
    4245            4329 :                 break;
    4246                 : 
    4247                 :               case JSOP_NEW:
    4248                 :               case JSOP_CALL:
    4249                 :               case JSOP_EVAL:
    4250                 :               case JSOP_FUNCALL:
    4251                 :               case JSOP_FUNAPPLY:
    4252                 :               {
    4253            3411 :                 argc = GET_ARGC(pc);
    4254                 :                 const char **argv = (const char **)
    4255            3411 :                     cx->malloc_((size_t)(argc + 1) * sizeof *argv);
    4256            3411 :                 if (!argv)
    4257               0 :                     return NULL;
    4258                 :                 jsbytecode **argbytecodes = (jsbytecode **)
    4259            3411 :                     cx->malloc_((size_t)(argc + 1) * sizeof *argbytecodes);
    4260            3411 :                 if (!argbytecodes) {
    4261               0 :                     cx->free_(argv);
    4262               0 :                     return NULL;
    4263                 :                 }
    4264                 : 
    4265            3411 :                 op = JSOP_SETNAME;
    4266            6687 :                 for (i = argc; i > 0; i--)
    4267            3276 :                     argv[i] = PopStrDupe(ss, op, &argbytecodes[i]);
    4268                 : 
    4269                 :                 /* Skip the JSOP_PUSHOBJ-created empty string. */
    4270            3411 :                 LOCAL_ASSERT(ss->top >= 2);
    4271            3411 :                 (void) PopOff(ss, op);
    4272                 : 
    4273                 :                 /*
    4274                 :                  * Special case: new (x(y)(z)) must be parenthesized like so.
    4275                 :                  * Same for new (x(y).z) -- contrast with new x(y).z.
    4276                 :                  * See PROPAGATE_CALLNESS.
    4277                 :                  */
    4278            3411 :                 op = (JSOp) ss->opcodes[ss->top - 1];
    4279                 :                 argv[0] = PopStrDupe(ss,
    4280                 :                                      (saveop == JSOP_NEW &&
    4281                 :                                       (op == JSOP_CALL ||
    4282                 :                                        op == JSOP_EVAL ||
    4283                 :                                        op == JSOP_FUNCALL ||
    4284                 :                                        op == JSOP_FUNAPPLY ||
    4285                 :                                        op == JSOP_CALLPROP ||
    4286                 :                                        op == JSOP_CALLELEM))
    4287                 :                                      ? JSOP_NAME
    4288                 :                                      : saveop,
    4289            3411 :                                      &lvalpc);
    4290            3411 :                 op = saveop;
    4291                 : 
    4292            3411 :                 lval = "(", rval = ")";
    4293            3411 :                 todo = ss->sprinter.getOffset();
    4294            3411 :                 if (op == JSOP_NEW) {
    4295             252 :                     if (argc == 0)
    4296             108 :                         lval = rval = "";
    4297             252 :                     Sprint(&ss->sprinter, "%s ", js_new_str);
    4298                 :                 }
    4299            3411 :                 SprintOpcode(ss, argv[0], lvalpc, pc, todo);
    4300            3411 :                 ss->sprinter.put(lval);
    4301                 : 
    4302            6687 :                 for (i = 1; i <= argc; i++) {
    4303            3276 :                     SprintOpcode(ss, argv[i], argbytecodes[i], pc, todo);
    4304            3276 :                     if (i < argc)
    4305             342 :                         ss->sprinter.put(", ");
    4306                 :                 }
    4307            3411 :                 ss->sprinter.put(rval);
    4308                 : 
    4309            3411 :                 cx->free_(argv);
    4310            3411 :                 cx->free_(argbytecodes);
    4311                 : 
    4312            3411 :                 break;
    4313                 :               }
    4314                 : 
    4315                 :               case JSOP_SETCALL:
    4316               0 :                 todo = Sprint(&ss->sprinter, "");
    4317               0 :                 break;
    4318                 : 
    4319                 :               case JSOP_DELNAME:
    4320               0 :                 LOAD_ATOM(0);
    4321               0 :                 lval = QuoteString(&ss->sprinter, atom, 0);
    4322               0 :                 if (!lval)
    4323               0 :                     return NULL;
    4324               0 :                 ss->sprinter.setOffset(lval);
    4325                 :               do_delete_lval:
    4326               0 :                 todo = Sprint(&ss->sprinter, "%s %s", js_delete_str, lval);
    4327               0 :                 break;
    4328                 : 
    4329                 :               case JSOP_DELPROP:
    4330               0 :                 GET_ATOM_QUOTE_AND_FMT("%s %s[%s]", "%s %s.%s", rval);
    4331               0 :                 op = JSOP_GETPROP;
    4332               0 :                 lval = POP_STR();
    4333               0 :                 todo = Sprint(&ss->sprinter, fmt, js_delete_str, lval, rval);
    4334               0 :                 break;
    4335                 : 
    4336                 :               case JSOP_DELELEM:
    4337               0 :                 op = JSOP_NOP;          /* turn off parens */
    4338               0 :                 xval = POP_STR();
    4339               0 :                 op = JSOP_GETPROP;
    4340               0 :                 lval = POP_STR();
    4341               0 :                 if (*xval == '\0')
    4342               0 :                     goto do_delete_lval;
    4343                 :                 todo = Sprint(&ss->sprinter,
    4344                 :                               (JOF_OPMODE(lastop) == JOF_XMLNAME)
    4345                 :                               ? "%s %s.%s"
    4346                 :                               : "%s %s[%s]",
    4347               0 :                               js_delete_str, lval, xval);
    4348               0 :                 break;
    4349                 : 
    4350                 : #if JS_HAS_XML_SUPPORT
    4351                 :               case JSOP_DELDESC:
    4352               0 :                 xval = POP_STR();
    4353               0 :                 op = JSOP_GETPROP;
    4354               0 :                 lval = POP_STR();
    4355                 :                 todo = Sprint(&ss->sprinter, "%s %s..%s",
    4356               0 :                               js_delete_str, lval, xval);
    4357               0 :                 break;
    4358                 : #endif
    4359                 : 
    4360                 :               case JSOP_TYPEOFEXPR:
    4361                 :               case JSOP_TYPEOF:
    4362                 :               case JSOP_VOID:
    4363                 :               {
    4364               0 :                 const char *prefix = (op == JSOP_VOID) ? js_void_str : js_typeof_str;
    4365               0 :                 rval = PopStrDupe(ss, op, &rvalpc);
    4366               0 :                 todo = ss->sprinter.getOffset();
    4367               0 :                 Sprint(&ss->sprinter, "%s ", prefix);
    4368               0 :                 SprintOpcode(ss, rval, rvalpc, pc, todo);
    4369               0 :                 break;
    4370                 :               }
    4371                 : 
    4372                 :               case JSOP_INCARG:
    4373                 :               case JSOP_DECARG:
    4374              54 :                 atom = GetArgOrVarAtom(jp, GET_ARGNO(pc));
    4375              54 :                 LOCAL_ASSERT(atom);
    4376              54 :                 goto do_incatom;
    4377                 : 
    4378                 :               case JSOP_INCNAME:
    4379                 :               case JSOP_DECNAME:
    4380                 :               case JSOP_INCGNAME:
    4381                 :               case JSOP_DECGNAME:
    4382               0 :                 LOAD_ATOM(0);
    4383                 :               do_incatom:
    4384             333 :                 lval = QuoteString(&ss->sprinter, atom, 0);
    4385             333 :                 if (!lval)
    4386               0 :                     return NULL;
    4387             333 :                 ss->sprinter.setOffset(lval);
    4388                 :               do_inclval:
    4389                 :                 todo = Sprint(&ss->sprinter, ss_format,
    4390             576 :                               js_incop_strs[!(cs->format & JOF_INC)], lval);
    4391             576 :                 if (js_CodeSpec[*pc].format & JOF_DECOMPOSE)
    4392               0 :                     len += GetDecomposeLength(pc, js_CodeSpec[*pc].length);
    4393             576 :                 break;
    4394                 : 
    4395                 :               case JSOP_INCPROP:
    4396                 :               case JSOP_DECPROP:
    4397               0 :                 GET_ATOM_QUOTE_AND_FMT(preindex_format, predot_format, rval);
    4398                 : 
    4399                 :                 /*
    4400                 :                  * Force precedence below the numeric literal opcodes, so that
    4401                 :                  * 42..foo or 10000..toString(16), e.g., decompile with parens
    4402                 :                  * around the left-hand side of dot.
    4403                 :                  */
    4404               0 :                 op = JSOP_GETPROP;
    4405               0 :                 lval = POP_STR();
    4406                 :                 todo = Sprint(&ss->sprinter, fmt,
    4407               0 :                               js_incop_strs[!(cs->format & JOF_INC)],
    4408               0 :                               lval, rval);
    4409               0 :                 len += GetDecomposeLength(pc, JSOP_INCPROP_LENGTH);
    4410               0 :                 break;
    4411                 : 
    4412                 :               case JSOP_INCELEM:
    4413                 :               case JSOP_DECELEM:
    4414               0 :                 op = JSOP_NOP;          /* turn off parens */
    4415               0 :                 xval = POP_STR();
    4416               0 :                 op = JSOP_GETELEM;
    4417               0 :                 lval = POP_STR();
    4418               0 :                 if (*xval != '\0') {
    4419                 :                     todo = Sprint(&ss->sprinter,
    4420                 :                                   (JOF_OPMODE(lastop) == JOF_XMLNAME)
    4421                 :                                   ? predot_format
    4422                 :                                   : preindex_format,
    4423               0 :                                   js_incop_strs[!(cs->format & JOF_INC)],
    4424               0 :                                   lval, xval);
    4425                 :                 } else {
    4426                 :                     todo = Sprint(&ss->sprinter, ss_format,
    4427               0 :                                   js_incop_strs[!(cs->format & JOF_INC)], lval);
    4428                 :                 }
    4429               0 :                 len += GetDecomposeLength(pc, JSOP_INCELEM_LENGTH);
    4430               0 :                 break;
    4431                 : 
    4432                 :               case JSOP_ARGINC:
    4433                 :               case JSOP_ARGDEC:
    4434               0 :                 atom = GetArgOrVarAtom(jp, GET_ARGNO(pc));
    4435               0 :                 LOCAL_ASSERT(atom);
    4436               0 :                 goto do_atominc;
    4437                 : 
    4438                 :               case JSOP_NAMEINC:
    4439                 :               case JSOP_NAMEDEC:
    4440                 :               case JSOP_GNAMEINC:
    4441                 :               case JSOP_GNAMEDEC:
    4442              36 :                 LOAD_ATOM(0);
    4443                 :               do_atominc:
    4444              81 :                 lval = QuoteString(&ss->sprinter, atom, 0);
    4445              81 :                 if (!lval)
    4446               0 :                     return NULL;
    4447              81 :                 ss->sprinter.setOffset(lval);
    4448                 :               do_lvalinc:
    4449                 :                 todo = Sprint(&ss->sprinter, ss_format,
    4450             135 :                               lval, js_incop_strs[!(cs->format & JOF_INC)]);
    4451             135 :                 if (js_CodeSpec[*pc].format & JOF_DECOMPOSE)
    4452              36 :                     len += GetDecomposeLength(pc, js_CodeSpec[*pc].length);
    4453             135 :                 break;
    4454                 : 
    4455                 :               case JSOP_PROPINC:
    4456                 :               case JSOP_PROPDEC:
    4457               0 :                 GET_ATOM_QUOTE_AND_FMT(postindex_format, postdot_format, rval);
    4458                 : 
    4459                 :                 /*
    4460                 :                  * Force precedence below the numeric literal opcodes, so that
    4461                 :                  * 42..foo or 10000..toString(16), e.g., decompile with parens
    4462                 :                  * around the left-hand side of dot.
    4463                 :                  */
    4464               0 :                 op = JSOP_GETPROP;
    4465               0 :                 lval = POP_STR();
    4466                 :                 todo = Sprint(&ss->sprinter, fmt, lval, rval,
    4467               0 :                               js_incop_strs[!(cs->format & JOF_INC)]);
    4468               0 :                 len += GetDecomposeLength(pc, JSOP_PROPINC_LENGTH);
    4469               0 :                 break;
    4470                 : 
    4471                 :               case JSOP_ELEMINC:
    4472                 :               case JSOP_ELEMDEC:
    4473              18 :                 op = JSOP_NOP;          /* turn off parens */
    4474              18 :                 xval = POP_STR();
    4475              18 :                 op = JSOP_GETELEM;
    4476              18 :                 lval = POP_STR();
    4477              18 :                 if (*xval != '\0') {
    4478                 :                     todo = Sprint(&ss->sprinter,
    4479                 :                                   (JOF_OPMODE(lastop) == JOF_XMLNAME)
    4480                 :                                   ? postdot_format
    4481                 :                                   : postindex_format,
    4482                 :                                   lval, xval,
    4483               9 :                                   js_incop_strs[!(cs->format & JOF_INC)]);
    4484                 :                 } else {
    4485                 :                     todo = Sprint(&ss->sprinter, ss_format,
    4486               9 :                                   lval, js_incop_strs[!(cs->format & JOF_INC)]);
    4487                 :                 }
    4488              18 :                 len += GetDecomposeLength(pc, JSOP_ELEMINC_LENGTH);
    4489              18 :                 break;
    4490                 : 
    4491                 :               case JSOP_GETPROP2:
    4492               9 :                 op = JSOP_GETPROP;
    4493               9 :                 (void) PopOff(ss, lastop);
    4494                 :                 /* FALL THROUGH */
    4495                 : 
    4496                 :               case JSOP_CALLPROP:
    4497                 :               case JSOP_GETPROP:
    4498                 :               case JSOP_GETXPROP:
    4499                 :               case JSOP_LENGTH:
    4500            1692 :                 LOAD_ATOM(0);
    4501                 : 
    4502            1692 :                 GET_QUOTE_AND_FMT("[%s]", ".%s", rval);
    4503            1692 :                 PROPAGATE_CALLNESS();
    4504            1692 :                 lval = PopStr(ss, op, &lvalpc);
    4505            1692 :                 todo = ss->sprinter.getOffset();
    4506            1692 :                 SprintOpcode(ss, lval, lvalpc, pc, todo);
    4507            1692 :                 Sprint(&ss->sprinter, fmt, rval);
    4508            1692 :                 break;
    4509                 : 
    4510                 :               case JSOP_SETPROP:
    4511                 :               {
    4512             279 :                 LOAD_ATOM(0);
    4513             279 :                 GET_QUOTE_AND_FMT("[%s] %s= ", ".%s %s= ", xval);
    4514             279 :                 rval = PopStrDupe(ss, op, &rvalpc);
    4515                 : 
    4516                 :                 /*
    4517                 :                  * Force precedence below the numeric literal opcodes, so that
    4518                 :                  * 42..foo or 10000..toString(16), e.g., decompile with parens
    4519                 :                  * around the left-hand side of dot.
    4520                 :                  */
    4521             279 :                 op = JSOP_GETPROP;
    4522             279 :                 lval = PopStr(ss, op, &lvalpc);
    4523             279 :                 sn = js_GetSrcNote(jp->script, pc - 1);
    4524                 :                 const char *token =
    4525                 :                     GetTokenForAssignment(jp, sn, lastop, pc, rvalpc,
    4526             279 :                                           &lastlvalpc, &lastrvalpc);
    4527             279 :                 todo = ss->sprinter.getOffset();
    4528             279 :                 SprintOpcode(ss, lval, lvalpc, pc, todo);
    4529             279 :                 Sprint(&ss->sprinter, fmt, xval, token);
    4530             279 :                 SprintOpcode(ss, rval, rvalpc, pc, todo);
    4531             279 :                 break;
    4532                 :               }
    4533                 : 
    4534                 :               case JSOP_GETELEM2:
    4535               0 :                 (void) PopOff(ss, lastop);
    4536                 :                 /* FALL THROUGH */
    4537                 :               case JSOP_CALLELEM:
    4538                 :               case JSOP_GETELEM:
    4539             513 :                 op = JSOP_NOP;          /* turn off parens */
    4540             513 :                 xval = PopStrDupe(ss, op, &xvalpc);
    4541             513 :                 op = saveop;
    4542             513 :                 PROPAGATE_CALLNESS();
    4543             513 :                 lval = PopStr(ss, op, &lvalpc);
    4544             513 :                 todo = ss->sprinter.getOffset();
    4545             513 :                 SprintOpcode(ss, lval, lvalpc, pc, todo);
    4546             513 :                 if (*xval != '\0') {
    4547             504 :                     bool xml = (JOF_OPMODE(lastop) == JOF_XMLNAME);
    4548             504 :                     ss->sprinter.put(xml ? "." : "[");
    4549             504 :                     SprintOpcode(ss, xval, xvalpc, pc, todo);
    4550             504 :                     ss->sprinter.put(xml ? "" : "]");
    4551                 :                 }
    4552             513 :                 break;
    4553                 : 
    4554                 :               case JSOP_SETELEM:
    4555                 :               {
    4556              27 :                 rval = PopStrDupe(ss, op, &rvalpc);
    4557              27 :                 op = JSOP_NOP;          /* turn off parens */
    4558              27 :                 xval = PopStrDupe(ss, op, &xvalpc);
    4559              27 :                 cs = &js_CodeSpec[ss->opcodes[ss->top]];
    4560              27 :                 op = JSOP_GETELEM;      /* lval must have high precedence */
    4561              27 :                 lval = PopStr(ss, op, &lvalpc);
    4562              27 :                 op = saveop;
    4563              27 :                 if (*xval == '\0')
    4564               0 :                     goto do_setlval;
    4565              27 :                 sn = js_GetSrcNote(jp->script, pc - 1);
    4566              27 :                 bool xml = (JOF_MODE(cs->format) == JOF_XMLNAME);
    4567                 :                 const char *token =
    4568                 :                     GetTokenForAssignment(jp, sn, lastop, pc, rvalpc,
    4569              27 :                                           &lastlvalpc, &lastrvalpc);
    4570              27 :                 todo = ss->sprinter.getOffset();
    4571              27 :                 SprintOpcode(ss, lval, lvalpc, pc, todo);
    4572              27 :                 ss->sprinter.put(xml ? "." : "[");
    4573              27 :                 SprintOpcode(ss, xval, xvalpc, pc, todo);
    4574              27 :                 ss->sprinter.put(xml ? "" : "]");
    4575              27 :                 Sprint(&ss->sprinter, " %s= ", token);
    4576              27 :                 SprintOpcode(ss, rval, rvalpc, pc, todo);
    4577              27 :                 break;
    4578                 :               }
    4579                 : 
    4580                 :               case JSOP_CALLARG:
    4581                 :               case JSOP_GETARG:
    4582           11876 :                 i = GET_ARGNO(pc);
    4583           11876 :                 atom = GetArgOrVarAtom(jp, i);
    4584                 : #if JS_HAS_DESTRUCTURING
    4585           11876 :                 if (!atom) {
    4586               0 :                     todo = Sprint(&ss->sprinter, "%s[%d]", js_arguments_str, i);
    4587               0 :                     break;
    4588                 :                 }
    4589                 : #else
    4590                 :                 LOCAL_ASSERT(atom);
    4591                 : #endif
    4592           11876 :                 goto do_name;
    4593                 : 
    4594                 :               case JSOP_CALLNAME:
    4595                 :               case JSOP_NAME:
    4596                 :               case JSOP_GETGNAME:
    4597                 :               case JSOP_CALLGNAME:
    4598            4358 :                 LOAD_ATOM(0);
    4599                 :               do_name:
    4600           19168 :                 lval = "";
    4601                 : #if JS_HAS_XML_SUPPORT
    4602                 :               do_qname:
    4603                 : #endif
    4604           19177 :                 sn = js_GetSrcNote(jp->script, pc);
    4605           19177 :                 rval = QuoteString(&ss->sprinter, atom, inXML ? DONT_ESCAPE : 0);
    4606           19177 :                 if (!rval)
    4607               0 :                     return NULL;
    4608           19177 :                 ss->sprinter.setOffset(rval);
    4609                 :                 todo = Sprint(&ss->sprinter, sss_format,
    4610           19177 :                               VarPrefix(sn), lval, rval);
    4611           19177 :                 break;
    4612                 : 
    4613                 :               case JSOP_UINT16:
    4614              27 :                 i = (int) GET_UINT16(pc);
    4615              27 :                 goto do_sprint_int;
    4616                 : 
    4617                 :               case JSOP_UINT24:
    4618               0 :                 i = (int) GET_UINT24(pc);
    4619               0 :                 goto do_sprint_int;
    4620                 : 
    4621                 :               case JSOP_INT8:
    4622            1179 :                 i = GET_INT8(pc);
    4623            1179 :                 goto do_sprint_int;
    4624                 : 
    4625                 :               case JSOP_INT32:
    4626               0 :                 i = GET_INT32(pc);
    4627                 :               do_sprint_int:
    4628            1206 :                 todo = Sprint(&ss->sprinter, "%d", i);
    4629            1206 :                 break;
    4630                 : 
    4631                 :               case JSOP_DOUBLE:
    4632                 :               {
    4633               9 :                 val = jp->script->getConst(GET_UINT32_INDEX(pc));
    4634               9 :                 todo = SprintDoubleValue(&ss->sprinter, val, &saveop);
    4635               9 :                 break;
    4636                 :               }
    4637                 : 
    4638                 :               case JSOP_STRING:
    4639            3267 :                 LOAD_ATOM(0);
    4640            3267 :                 rval = QuoteString(&ss->sprinter, atom, inXML ? DONT_ESCAPE : '"');
    4641            3267 :                 if (!rval)
    4642               0 :                     return NULL;
    4643            3267 :                 todo = ss->sprinter.getOffsetOf(rval);
    4644            3267 :                 break;
    4645                 : 
    4646                 :               case JSOP_LAMBDA:
    4647                 : #if JS_HAS_GENERATOR_EXPRS
    4648             558 :                 sn = js_GetSrcNote(jp->script, pc);
    4649             558 :                 if (sn && SN_TYPE(sn) == SRC_GENEXP) {
    4650                 :                     Vector<JSAtom *> *innerLocalNames;
    4651                 :                     Vector<JSAtom *> *outerLocalNames;
    4652                 :                     JSScript *inner, *outer;
    4653                 :                     Vector<DecompiledOpcode> *decompiledOpcodes;
    4654             270 :                     SprintStack ss2(cx);
    4655                 :                     JSFunction *outerfun;
    4656                 : 
    4657             135 :                     fun = jp->script->getFunction(GET_UINT32_INDEX(pc));
    4658                 : 
    4659                 :                     /*
    4660                 :                      * All allocation when decompiling is LIFO, using malloc or,
    4661                 :                      * more commonly, arena-allocating from cx->tempLifoAlloc
    4662                 :                      * Therefore after InitSprintStack succeeds, we must release
    4663                 :                      * to mark before returning.
    4664                 :                      */
    4665             270 :                     LifoAllocScope las(&cx->tempLifoAlloc());
    4666             135 :                     if (fun->script()->bindings.hasLocalNames()) {
    4667               0 :                         innerLocalNames = cx->new_<Vector<JSAtom *> >(cx);
    4668               0 :                         if (!innerLocalNames ||
    4669               0 :                             !fun->script()->bindings.getLocalNameArray(cx, innerLocalNames))
    4670                 :                         {
    4671               0 :                             return NULL;
    4672                 :                         }
    4673                 :                     } else {
    4674             135 :                         innerLocalNames = NULL;
    4675                 :                     }
    4676             135 :                     inner = fun->script();
    4677             135 :                     if (!InitSprintStack(cx, &ss2, jp, StackDepth(inner)))
    4678               0 :                         return NULL;
    4679             135 :                     ss2.inGenExp = JS_TRUE;
    4680                 : 
    4681                 :                     /*
    4682                 :                      * Recursively decompile this generator function as an
    4683                 :                      * un-parenthesized generator expression. The ss->inGenExp
    4684                 :                      * special case of JSOP_YIELD shares array comprehension
    4685                 :                      * decompilation code that leaves the result as the single
    4686                 :                      * string pushed on ss2.
    4687                 :                      */
    4688             135 :                     outer = jp->script;
    4689             135 :                     outerfun = jp->fun;
    4690             135 :                     outerLocalNames = jp->localNames;
    4691             135 :                     decompiledOpcodes = jp->decompiledOpcodes;
    4692             135 :                     LOCAL_ASSERT(UnsignedPtrDiff(pc, outer->code) <= outer->length);
    4693             135 :                     jp->script = inner;
    4694             135 :                     jp->fun = fun;
    4695             135 :                     jp->localNames = innerLocalNames;
    4696             135 :                     jp->decompiledOpcodes = NULL;
    4697                 : 
    4698                 :                     /*
    4699                 :                      * Decompile only the main bytecode, to avoid tripping over
    4700                 :                      * new prolog ops that have stack effects.
    4701                 :                      */
    4702             135 :                     ok = Decompile(&ss2, inner->main(), inner->length - inner->mainOffset)
    4703             135 :                          != NULL;
    4704             135 :                     jp->script = outer;
    4705             135 :                     jp->fun = outerfun;
    4706             135 :                     jp->localNames = outerLocalNames;
    4707             135 :                     jp->decompiledOpcodes = decompiledOpcodes;
    4708             135 :                     if (!ok)
    4709               0 :                         return NULL;
    4710                 : 
    4711                 :                     /*
    4712                 :                      * Advance over this op and its global |this| push, and
    4713                 :                      * arrange to advance over the call to this lambda.
    4714                 :                      */
    4715             135 :                     pc += len;
    4716             135 :                     LOCAL_ASSERT(*pc == JSOP_UNDEFINED);
    4717             135 :                     pc += JSOP_UNDEFINED_LENGTH;
    4718             135 :                     LOCAL_ASSERT(*pc == JSOP_CALL);
    4719             135 :                     LOCAL_ASSERT(GET_ARGC(pc) == 0);
    4720             135 :                     len = JSOP_CALL_LENGTH;
    4721                 : 
    4722                 :                     /*
    4723                 :                      * Arrange to parenthesize this genexp unless:
    4724                 :                      *
    4725                 :                      *  1. It is the complete expression consumed by a control
    4726                 :                      *     flow bytecode such as JSOP_TABLESWITCH whose syntax
    4727                 :                      *     always parenthesizes the controlling expression.
    4728                 :                      *  2. It is the sole argument to a function call.
    4729                 :                      *
    4730                 :                      * But if this genexp runs up against endpc, parenthesize
    4731                 :                      * regardless.  (This can happen if we are called from
    4732                 :                      * DecompileExpression or recursively from case
    4733                 :                      * JSOP_{NOP,AND,OR}.)
    4734                 :                      *
    4735                 :                      * There's no special case for |if (genexp)| because the
    4736                 :                      * compiler optimizes that to |if (true)|.
    4737                 :                      */
    4738             135 :                     pc2 = pc + len;
    4739             135 :                     op = JSOp(*pc2);
    4740             135 :                     if (op == JSOP_LOOPHEAD || op == JSOP_NOP)
    4741               0 :                         pc2 += JSOP_NOP_LENGTH;
    4742             135 :                     LOCAL_ASSERT(pc2 < endpc ||
    4743                 :                                  endpc < outer->code + outer->length);
    4744             135 :                     LOCAL_ASSERT(ss2.top == 1);
    4745             135 :                     ss2.opcodes[0] = JSOP_POP;
    4746             135 :                     if (pc2 == endpc) {
    4747               0 :                         op = JSOP_SETNAME;
    4748                 :                     } else {
    4749             135 :                         op = (JSOp) *pc2;
    4750                 :                         op = ((js_CodeSpec[op].format & JOF_PARENHEAD) ||
    4751               0 :                               ((js_CodeSpec[op].format & JOF_INVOKE) && GET_ARGC(pc2) == 1))
    4752                 :                              ? JSOP_POP
    4753             135 :                              : JSOP_SETNAME;
    4754                 : 
    4755                 :                         /*
    4756                 :                          * Stack this result as if it's a name and not an
    4757                 :                          * anonymous function, so it doesn't get decompiled as
    4758                 :                          * a generator function in a getter or setter context.
    4759                 :                          * The precedence level is the same for JSOP_NAME and
    4760                 :                          * JSOP_LAMBDA.
    4761                 :                          */
    4762             135 :                         LOCAL_ASSERT(js_CodeSpec[JSOP_NAME].prec ==
    4763                 :                                      js_CodeSpec[saveop].prec);
    4764             135 :                         saveop = JSOP_NAME;
    4765                 :                     }
    4766                 : 
    4767                 :                     /*
    4768                 :                      * Alas, we have to malloc a copy of the result left on the
    4769                 :                      * top of ss2 because both ss and ss2 arena-allocate from
    4770                 :                      * cx's tempLifoAlloc
    4771                 :                      */
    4772             135 :                     rval = JS_strdup(cx, PopStr(&ss2, op));
    4773             135 :                     las.releaseEarly();
    4774             135 :                     if (!rval)
    4775               0 :                         return NULL;
    4776             135 :                     todo = ss->sprinter.put(rval);
    4777             135 :                     cx->free_((void *)rval);
    4778             135 :                     break;
    4779                 :                 }
    4780                 : #endif /* JS_HAS_GENERATOR_EXPRS */
    4781             423 :                 else if (sn && SN_TYPE(sn) == SRC_CONTINUE) {
    4782                 :                     /*
    4783                 :                      * Local function definitions have a lambda;setlocal;pop
    4784                 :                      * triple (annotated with SRC_CONTINUE) in the function
    4785                 :                      * prologue and a nop (annotated with SRC_FUNCDEF) at the
    4786                 :                      * actual position where the function definition should
    4787                 :                      * syntactically appear.
    4788                 :                      */
    4789              99 :                     LOCAL_ASSERT(pc[JSOP_LAMBDA_LENGTH] == JSOP_SETLOCAL);
    4790              99 :                     LOCAL_ASSERT(pc[JSOP_LAMBDA_LENGTH + JSOP_SETLOCAL_LENGTH] == JSOP_POP);
    4791              99 :                     len = JSOP_LAMBDA_LENGTH + JSOP_SETLOCAL_LENGTH + JSOP_POP_LENGTH;
    4792              99 :                     todo = -2;
    4793              99 :                     break;
    4794                 :                 }
    4795                 : 
    4796                 :                 /* Otherwise, this is a lambda expression. */
    4797             324 :                 fun = jp->script->getFunction(GET_UINT32_INDEX(pc));
    4798                 :                 {
    4799                 :                     /*
    4800                 :                      * Always parenthesize expression closures. We can't force
    4801                 :                      * saveop to a low-precedence op to arrange for auto-magic
    4802                 :                      * parenthesization without confusing getter/setter code
    4803                 :                      * that checks for JSOP_LAMBDA.
    4804                 :                      */
    4805             324 :                     bool grouped = !(fun->flags & JSFUN_EXPR_CLOSURE);
    4806             324 :                     bool strict = jp->script->strictModeCode;
    4807                 :                     str = js_DecompileToString(cx, "lambda", fun, 0, 
    4808                 :                                                false, grouped, strict,
    4809             324 :                                                js_DecompileFunction);
    4810             324 :                     if (!str)
    4811               0 :                         return NULL;
    4812                 :                 }
    4813                 :               sprint_string:
    4814             342 :                 todo = ss->sprinter.putString(str);
    4815             342 :                 break;
    4816                 : 
    4817                 :               case JSOP_CALLEE:
    4818              54 :                 JS_ASSERT(jp->fun && jp->fun->atom);
    4819              54 :                 todo = ss->sprinter.putString(jp->fun->atom);
    4820              54 :                 break;
    4821                 : 
    4822                 :               case JSOP_OBJECT:
    4823              18 :                 obj = jp->script->getObject(GET_UINT32_INDEX(pc));
    4824              18 :                 str = js_ValueToSource(cx, ObjectValue(*obj));
    4825              18 :                 if (!str)
    4826               0 :                     return NULL;
    4827              18 :                 goto sprint_string;
    4828                 : 
    4829                 :               case JSOP_REGEXP:
    4830               0 :                 obj = jp->script->getRegExp(GET_UINT32_INDEX(pc));
    4831               0 :                 str = obj->asRegExp().toString(cx);
    4832               0 :                 if (!str)
    4833               0 :                     return NULL;
    4834               0 :                 goto sprint_string;
    4835                 : 
    4836                 :               case JSOP_TABLESWITCH:
    4837                 :               {
    4838                 :                 ptrdiff_t off, off2;
    4839                 :                 int32_t j, n, low, high;
    4840                 :                 TableEntry *table, *tmp;
    4841                 : 
    4842             108 :                 sn = js_GetSrcNote(jp->script, pc);
    4843             108 :                 LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH);
    4844             108 :                 len = js_GetSrcNoteOffset(sn, 0);
    4845             108 :                 off = GET_JUMP_OFFSET(pc);
    4846             108 :                 pc2 = pc + JUMP_OFFSET_LEN;
    4847             108 :                 low = GET_JUMP_OFFSET(pc2);
    4848             108 :                 pc2 += JUMP_OFFSET_LEN;
    4849             108 :                 high = GET_JUMP_OFFSET(pc2);
    4850             108 :                 pc2 += JUMP_OFFSET_LEN;
    4851                 : 
    4852             108 :                 n = high - low + 1;
    4853             108 :                 if (n == 0) {
    4854               0 :                     table = NULL;
    4855               0 :                     j = 0;
    4856               0 :                     ok = true;
    4857                 :                 } else {
    4858                 :                     table = (TableEntry *)
    4859             108 :                             cx->malloc_((size_t)n * sizeof *table);
    4860             108 :                     if (!table)
    4861               0 :                         return NULL;
    4862             243 :                     for (i = j = 0; i < n; i++) {
    4863             135 :                         table[j].label = NULL;
    4864             135 :                         off2 = GET_JUMP_OFFSET(pc2);
    4865             135 :                         if (off2) {
    4866             135 :                             sn = js_GetSrcNote(jp->script, pc2);
    4867             135 :                             if (sn) {
    4868               0 :                                 LOCAL_ASSERT(SN_TYPE(sn) == SRC_LABEL);
    4869               0 :                                 GET_SOURCE_NOTE_ATOM(sn, table[j].label);
    4870                 :                             }
    4871             135 :                             table[j].key = INT_TO_JSVAL(low + i);
    4872             135 :                             table[j].offset = off2;
    4873             135 :                             table[j].order = j;
    4874             135 :                             j++;
    4875                 :                         }
    4876             135 :                         pc2 += JUMP_OFFSET_LEN;
    4877                 :                     }
    4878                 :                     tmp = (TableEntry *)
    4879             108 :                           cx->malloc_((size_t)j * sizeof *table);
    4880             108 :                     if (tmp) {
    4881             108 :                         MergeSort(table, size_t(j), tmp, CompareTableEntries);
    4882             108 :                         Foreground::free_(tmp);
    4883             108 :                         ok = true;
    4884                 :                     } else {
    4885               0 :                         ok = false;
    4886                 :                     }
    4887                 :                 }
    4888                 : 
    4889             108 :                 if (ok)
    4890             108 :                     ok = DecompileSwitch(ss, table, (unsigned)j, pc, len, off, false);
    4891             108 :                 cx->free_(table);
    4892             108 :                 if (!ok)
    4893               0 :                     return NULL;
    4894             108 :                 todo = -2;
    4895             108 :                 break;
    4896                 :               }
    4897                 : 
    4898                 :               case JSOP_LOOKUPSWITCH:
    4899                 :               {
    4900                 :                 ptrdiff_t off, off2;
    4901                 :                 jsatomid npairs, k;
    4902                 :                 TableEntry *table;
    4903                 : 
    4904               9 :                 sn = js_GetSrcNote(jp->script, pc);
    4905               9 :                 LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH);
    4906               9 :                 len = js_GetSrcNoteOffset(sn, 0);
    4907               9 :                 off = GET_JUMP_OFFSET(pc);
    4908               9 :                 pc2 = pc + JUMP_OFFSET_LEN;
    4909               9 :                 npairs = GET_UINT16(pc2);
    4910               9 :                 pc2 += UINT16_LEN;
    4911                 : 
    4912                 :                 table = (TableEntry *)
    4913               9 :                     cx->malloc_((size_t)npairs * sizeof *table);
    4914               9 :                 if (!table)
    4915               0 :                     return NULL;
    4916              27 :                 for (k = 0; k < npairs; k++) {
    4917              18 :                     sn = js_GetSrcNote(jp->script, pc2);
    4918              18 :                     if (sn) {
    4919               0 :                         LOCAL_ASSERT(SN_TYPE(sn) == SRC_LABEL);
    4920               0 :                         GET_SOURCE_NOTE_ATOM(sn, table[k].label);
    4921                 :                     } else {
    4922              18 :                         table[k].label = NULL;
    4923                 :                     }
    4924              18 :                     uint32_t constIndex = GET_UINT32_INDEX(pc2);
    4925              18 :                     pc2 += UINT32_INDEX_LEN;
    4926              18 :                     off2 = GET_JUMP_OFFSET(pc2);
    4927              18 :                     pc2 += JUMP_OFFSET_LEN;
    4928              18 :                     table[k].key = jp->script->getConst(constIndex);
    4929              18 :                     table[k].offset = off2;
    4930                 :                 }
    4931                 : 
    4932                 :                 ok = DecompileSwitch(ss, table, (unsigned)npairs, pc, len, off,
    4933               9 :                                      JS_FALSE);
    4934               9 :                 cx->free_(table);
    4935               9 :                 if (!ok)
    4936               0 :                     return NULL;
    4937               9 :                 todo = -2;
    4938               9 :                 break;
    4939                 :               }
    4940                 : 
    4941                 :               case JSOP_CONDSWITCH:
    4942                 :               {
    4943                 :                 ptrdiff_t off, off2, caseOff;
    4944                 :                 int ncases;
    4945                 :                 TableEntry *table;
    4946                 : 
    4947               0 :                 sn = js_GetSrcNote(jp->script, pc);
    4948               0 :                 LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH);
    4949               0 :                 len = js_GetSrcNoteOffset(sn, 0);
    4950               0 :                 off = js_GetSrcNoteOffset(sn, 1);
    4951                 : 
    4952                 :                 /*
    4953                 :                  * Count the cases using offsets from switch to first case,
    4954                 :                  * and case to case, stored in srcnote immediates.
    4955                 :                  */
    4956               0 :                 pc2 = pc;
    4957               0 :                 off2 = off;
    4958               0 :                 for (ncases = 0; off2 != 0; ncases++) {
    4959               0 :                     pc2 += off2;
    4960               0 :                     LOCAL_ASSERT(*pc2 == JSOP_CASE || *pc2 == JSOP_DEFAULT);
    4961               0 :                     if (*pc2 == JSOP_DEFAULT) {
    4962                 :                         /* End of cases, but count default as a case. */
    4963               0 :                         off2 = 0;
    4964                 :                     } else {
    4965               0 :                         sn = js_GetSrcNote(jp->script, pc2);
    4966               0 :                         LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_PCDELTA);
    4967               0 :                         off2 = js_GetSrcNoteOffset(sn, 0);
    4968                 :                     }
    4969                 :                 }
    4970                 : 
    4971                 :                 /*
    4972                 :                  * Allocate table and rescan the cases using their srcnotes,
    4973                 :                  * stashing each case's delta from switch top in table[i].key,
    4974                 :                  * and the distance to its statements in table[i].offset.
    4975                 :                  */
    4976                 :                 table = (TableEntry *)
    4977               0 :                     cx->malloc_((size_t)ncases * sizeof *table);
    4978               0 :                 if (!table)
    4979               0 :                     return NULL;
    4980               0 :                 pc2 = pc;
    4981               0 :                 off2 = off;
    4982               0 :                 for (i = 0; i < ncases; i++) {
    4983               0 :                     pc2 += off2;
    4984               0 :                     LOCAL_ASSERT(*pc2 == JSOP_CASE || *pc2 == JSOP_DEFAULT);
    4985               0 :                     caseOff = pc2 - pc;
    4986               0 :                     table[i].key = INT_TO_JSVAL((int32_t) caseOff);
    4987               0 :                     table[i].offset = caseOff + GET_JUMP_OFFSET(pc2);
    4988               0 :                     if (*pc2 == JSOP_CASE) {
    4989               0 :                         sn = js_GetSrcNote(jp->script, pc2);
    4990               0 :                         LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_PCDELTA);
    4991               0 :                         off2 = js_GetSrcNoteOffset(sn, 0);
    4992                 :                     }
    4993                 :                 }
    4994                 : 
    4995                 :                 /*
    4996                 :                  * Find offset of default code by fetching the default offset
    4997                 :                  * from the end of table.  JSOP_CONDSWITCH always has a default
    4998                 :                  * case at the end.
    4999                 :                  */
    5000               0 :                 off = JSVAL_TO_INT(table[ncases-1].key);
    5001               0 :                 pc2 = pc + off;
    5002               0 :                 off += GET_JUMP_OFFSET(pc2);
    5003                 : 
    5004                 :                 ok = DecompileSwitch(ss, table, (unsigned)ncases, pc, len, off,
    5005               0 :                                      JS_TRUE);
    5006               0 :                 cx->free_(table);
    5007               0 :                 if (!ok)
    5008               0 :                     return NULL;
    5009               0 :                 todo = -2;
    5010               0 :                 break;
    5011                 :               }
    5012                 : 
    5013                 :               case JSOP_CASE:
    5014                 :               {
    5015               0 :                 lval = PopStr(ss, op, &lvalpc);
    5016               0 :                 if (!lval)
    5017               0 :                     return NULL;
    5018               0 :                 js_printf(jp, "\tcase ");
    5019               0 :                 SprintOpcodePermanent(jp, lval, lvalpc);
    5020               0 :                 js_printf(jp, ":\n");
    5021               0 :                 todo = -2;
    5022               0 :                 break;
    5023                 :               }
    5024                 : 
    5025                 :               case JSOP_DEFFUN:
    5026               0 :                 fun = jp->script->getFunction(GET_UINT32_INDEX(pc));
    5027               0 :                 todo = -2;
    5028               0 :                 goto do_function;
    5029                 : 
    5030                 :               case JSOP_HOLE:
    5031               0 :                 todo = ss->sprinter.put("", 0);
    5032               0 :                 break;
    5033                 : 
    5034                 :               case JSOP_NEWINIT:
    5035                 :               {
    5036             504 :                 i = GET_UINT8(pc);
    5037             504 :                 LOCAL_ASSERT(i == JSProto_Array || i == JSProto_Object);
    5038                 : 
    5039             504 :                 todo = ss->sprinter.getOffset();
    5040             504 :                 if (i == JSProto_Array) {
    5041             126 :                     ++ss->inArrayInit;
    5042             126 :                     if (ss->sprinter.put("[") < 0)
    5043               0 :                         return NULL;
    5044                 :                 } else {
    5045             378 :                     if (ss->sprinter.put("{") < 0)
    5046               0 :                         return NULL;
    5047                 :                 }
    5048             504 :                 break;
    5049                 :               }
    5050                 : 
    5051                 :               case JSOP_NEWARRAY:
    5052                 :               {
    5053            1080 :                 todo = ss->sprinter.getOffset();
    5054            1080 :                 ++ss->inArrayInit;
    5055            1080 :                 if (ss->sprinter.put("[") < 0)
    5056               0 :                     return NULL;
    5057            1080 :                 break;
    5058                 :               }
    5059                 : 
    5060                 :               case JSOP_NEWOBJECT:
    5061                 :               {
    5062              72 :                 todo = ss->sprinter.getOffset();
    5063              72 :                 if (ss->sprinter.put("{") < 0)
    5064               0 :                     return NULL;
    5065              72 :                 break;
    5066                 :               }
    5067                 : 
    5068                 :               case JSOP_ENDINIT:
    5069                 :               {
    5070                 :                 JSBool inArray;
    5071                 : 
    5072            1656 :                 op = JSOP_NOP;           /* turn off parens */
    5073            1656 :                 rval = PopStr(ss, op, &rvalpc);
    5074            1656 :                 sn = js_GetSrcNote(jp->script, pc);
    5075                 : 
    5076                 :                 /* Skip any #n= prefix to find the opening bracket. */
    5077            1656 :                 for (xval = rval; *xval != '[' && *xval != '{'; xval++)
    5078               0 :                     continue;
    5079            1656 :                 inArray = (*xval == '[');
    5080            1656 :                 if (inArray)
    5081            1206 :                     --ss->inArrayInit;
    5082            1656 :                 todo = ss->sprinter.getOffset();
    5083            1656 :                 SprintOpcode(ss, rval, rvalpc, pc, todo);
    5084                 :                 Sprint(&ss->sprinter, "%s%c",
    5085                 :                        (sn && SN_TYPE(sn) == SRC_CONTINUE) ? ", " : "",
    5086            1656 :                        inArray ? ']' : '}');
    5087            1656 :                 break;
    5088                 :               }
    5089                 : 
    5090                 :               {
    5091                 :                 JSBool isFirst;
    5092                 :                 const char *maybeComma;
    5093                 : 
    5094                 :               case JSOP_INITELEM:
    5095            1305 :                 isFirst = IsInitializerOp(ss->opcodes[ss->top - 3]);
    5096                 : 
    5097                 :                 /* Turn off most parens. */
    5098            1305 :                 rval = PopStr(ss, JSOP_SETNAME, &rvalpc);
    5099                 : 
    5100                 :                 /* Turn off all parens for xval and lval, which we control. */
    5101            1305 :                 xval = PopStr(ss, JSOP_NOP);
    5102            1305 :                 lval = PopStr(ss, JSOP_NOP, &lvalpc);
    5103            1305 :                 sn = js_GetSrcNote(jp->script, pc);
    5104                 : 
    5105            1305 :                 if (sn && SN_TYPE(sn) == SRC_INITPROP) {
    5106              54 :                     atom = NULL;
    5107              54 :                     goto do_initprop;
    5108                 :                 }
    5109            1251 :                 maybeComma = isFirst ? "" : ", ";
    5110            1251 :                 todo = Sprint(&ss->sprinter, "%s%s", lval, maybeComma);
    5111            1251 :                 SprintOpcode(ss, rval, rvalpc, pc, todo);
    5112            1251 :                 break;
    5113                 : 
    5114                 :               case JSOP_INITPROP:
    5115             297 :                 LOAD_ATOM(0);
    5116             297 :                 xval = QuoteString(&ss->sprinter, atom, jschar(IsIdentifier(atom) ? 0 : '\''));
    5117             297 :                 if (!xval)
    5118               0 :                     return NULL;
    5119             297 :                 isFirst = IsInitializerOp(ss->opcodes[ss->top - 2]);
    5120             297 :                 rval = PopStrDupe(ss, op, &rvalpc);
    5121             297 :                 lval = PopStr(ss, op, &lvalpc);
    5122                 :                 /* fall through */
    5123                 : 
    5124                 :               do_initprop:
    5125             351 :                 todo = ss->sprinter.getOffset();
    5126             351 :                 SprintOpcode(ss, lval, lvalpc, pc, todo);
    5127             351 :                 maybeComma = isFirst ? "" : ", ";
    5128             351 :                 if (lastop == JSOP_GETTER || lastop == JSOP_SETTER) {
    5129               0 :                     const char *end = rval + strlen(rval);
    5130                 : 
    5131               0 :                     if (*rval == '(')
    5132               0 :                         ++rval, --end;
    5133               0 :                     LOCAL_ASSERT(strncmp(rval, js_function_str, 8) == 0);
    5134               0 :                     LOCAL_ASSERT(rval[8] == ' ');
    5135               0 :                     rval += 8 + 1;
    5136               0 :                     LOCAL_ASSERT(*end ? *end == ')' : end[-1] == '}');
    5137                 :                     Sprint(&ss->sprinter, "%s%s %s%s%.*s",
    5138                 :                            maybeComma,
    5139                 :                            (lastop == JSOP_GETTER)
    5140                 :                            ? js_get_str : js_set_str,
    5141                 :                            xval,
    5142               0 :                            (rval[0] != '(') ? " " : "",
    5143               0 :                            end - rval, rval);
    5144                 :                 } else {
    5145             351 :                     Sprint(&ss->sprinter, "%s%s: ", maybeComma, xval);
    5146             351 :                     SprintOpcode(ss, rval, rvalpc, pc, todo);
    5147                 :                 }
    5148             351 :                 break;
    5149                 :               }
    5150                 : 
    5151                 :               case JSOP_DEBUGGER:
    5152              99 :                 js_printf(jp, "\tdebugger;\n");
    5153              99 :                 todo = -2;
    5154              99 :                 break;
    5155                 : 
    5156                 : #if JS_HAS_XML_SUPPORT
    5157                 :               case JSOP_STARTXML:
    5158                 :               case JSOP_STARTXMLEXPR:
    5159               0 :                 inXML = op == JSOP_STARTXML;
    5160               0 :                 todo = -2;
    5161               0 :                 break;
    5162                 : 
    5163                 :               case JSOP_DEFXMLNS:
    5164               0 :                 rval = POP_STR();
    5165                 :                 js_printf(jp, "\t%s %s %s = %s;\n",
    5166               0 :                           js_default_str, js_xml_str, js_namespace_str, rval);
    5167               0 :                 todo = -2;
    5168               0 :                 break;
    5169                 : 
    5170                 :               case JSOP_ANYNAME:
    5171               9 :                 if (pc[JSOP_ANYNAME_LENGTH] == JSOP_TOATTRNAME) {
    5172               0 :                     len += JSOP_TOATTRNAME_LENGTH;
    5173               0 :                     todo = ss->sprinter.put("@*", 2);
    5174                 :                 } else {
    5175               9 :                     todo = ss->sprinter.put("*", 1);
    5176                 :                 }
    5177               9 :                 break;
    5178                 : 
    5179                 :               case JSOP_QNAMEPART:
    5180              36 :                 LOAD_ATOM(0);
    5181              36 :                 if (pc[JSOP_QNAMEPART_LENGTH] == JSOP_TOATTRNAME) {
    5182               9 :                     saveop = JSOP_TOATTRNAME;
    5183               9 :                     len += JSOP_TOATTRNAME_LENGTH;
    5184               9 :                     lval = "@";
    5185               9 :                     goto do_qname;
    5186                 :                 }
    5187              27 :                 goto do_name;
    5188                 : 
    5189                 :               case JSOP_QNAMECONST:
    5190               9 :                 LOAD_ATOM(0);
    5191               9 :                 rval = QuoteString(&ss->sprinter, atom, 0);
    5192               9 :                 if (!rval)
    5193               0 :                     return NULL;
    5194               9 :                 ss->sprinter.setOffset(rval);
    5195               9 :                 lval = POP_STR();
    5196               9 :                 todo = Sprint(&ss->sprinter, "%s::%s", lval, rval);
    5197               9 :                 break;
    5198                 : 
    5199                 :               case JSOP_QNAME:
    5200               0 :                 rval = POP_STR();
    5201               0 :                 lval = POP_STR();
    5202               0 :                 todo = Sprint(&ss->sprinter, "%s::[%s]", lval, rval);
    5203               0 :                 break;
    5204                 : 
    5205                 :               case JSOP_TOATTRNAME:
    5206               0 :                 op = JSOP_NOP;           /* turn off parens */
    5207               0 :                 rval = POP_STR();
    5208               0 :                 todo = Sprint(&ss->sprinter, "@[%s]", rval);
    5209               0 :                 break;
    5210                 : 
    5211                 :               case JSOP_TOATTRVAL:
    5212               0 :                 todo = -2;
    5213               0 :                 break;
    5214                 : 
    5215                 :               case JSOP_ADDATTRNAME:
    5216               0 :                 rval = POP_STR();
    5217               0 :                 lval = POP_STR();
    5218               0 :                 todo = Sprint(&ss->sprinter, "%s %s", lval, rval);
    5219                 :                 /* This gets reset by all XML tag expressions. */
    5220               0 :                 quoteAttr = JS_TRUE;
    5221               0 :                 break;
    5222                 : 
    5223                 :               case JSOP_ADDATTRVAL:
    5224               0 :                 rval = POP_STR();
    5225               0 :                 lval = POP_STR();
    5226               0 :                 if (quoteAttr)
    5227               0 :                     todo = Sprint(&ss->sprinter, "%s=\"%s\"", lval, rval);
    5228                 :                 else
    5229               0 :                     todo = Sprint(&ss->sprinter, "%s=%s", lval, rval);
    5230               0 :                 break;
    5231                 : 
    5232                 :               case JSOP_BINDXMLNAME:
    5233                 :                 /* Leave the name stacked and push a dummy string. */
    5234               9 :                 todo = Sprint(&ss->sprinter, "");
    5235               9 :                 break;
    5236                 : 
    5237                 :               case JSOP_SETXMLNAME:
    5238                 :                 /* Pop the r.h.s., the dummy string, and the name. */
    5239               0 :                 rval = PopStrDupe(ss, op, &rvalpc);
    5240               0 :                 (void) PopOff(ss, op);
    5241               0 :                 lval = POP_STR();
    5242               0 :                 goto do_setlval;
    5243                 : 
    5244                 :               case JSOP_XMLELTEXPR:
    5245                 :               case JSOP_XMLTAGEXPR:
    5246               0 :                 todo = Sprint(&ss->sprinter, "{%s}", POP_STR());
    5247               0 :                 inXML = JS_TRUE;
    5248                 :                 /* If we're an attribute value, we shouldn't quote this. */
    5249               0 :                 quoteAttr = JS_FALSE;
    5250               0 :                 break;
    5251                 : 
    5252                 :               case JSOP_TOXMLLIST:
    5253               0 :                 op = JSOP_NOP;           /* turn off parens */
    5254               0 :                 todo = Sprint(&ss->sprinter, "<>%s</>", POP_STR());
    5255               0 :                 inXML = JS_FALSE;
    5256               0 :                 break;
    5257                 : 
    5258                 :               case JSOP_TOXML:
    5259                 :               case JSOP_CALLXMLNAME:
    5260                 :               case JSOP_XMLNAME:
    5261                 :               case JSOP_FILTER:
    5262                 :                 /* These ops indicate the end of XML expressions. */
    5263               9 :                 inXML = JS_FALSE;
    5264               9 :                 todo = -2;
    5265               9 :                 break;
    5266                 : 
    5267                 :               case JSOP_ENDFILTER:
    5268               0 :                 rval = POP_STR();
    5269               0 :                 PROPAGATE_CALLNESS();
    5270               0 :                 lval = POP_STR();
    5271               0 :                 todo = Sprint(&ss->sprinter, "%s.(%s)", lval, rval);
    5272               0 :                 break;
    5273                 : 
    5274                 :               case JSOP_DESCENDANTS:
    5275               0 :                 rval = POP_STR();
    5276               0 :                 PROPAGATE_CALLNESS();
    5277               0 :                 lval = POP_STR();
    5278               0 :                 todo = Sprint(&ss->sprinter, "%s..%s", lval, rval);
    5279               0 :                 break;
    5280                 : 
    5281                 :               case JSOP_XMLCDATA:
    5282               0 :                 LOAD_ATOM(0);
    5283               0 :                 todo = ss->sprinter.put("<![CDATA[", 9);
    5284               0 :                 if (!QuoteString(&ss->sprinter, atom, DONT_ESCAPE))
    5285               0 :                     return NULL;
    5286               0 :                 ss->sprinter.put("]]>", 3);
    5287               0 :                 break;
    5288                 : 
    5289                 :               case JSOP_XMLCOMMENT:
    5290               0 :                 LOAD_ATOM(0);
    5291               0 :                 todo = ss->sprinter.put("<!--", 4);
    5292               0 :                 if (!QuoteString(&ss->sprinter, atom, DONT_ESCAPE))
    5293               0 :                     return NULL;
    5294               0 :                 ss->sprinter.put("-->", 3);
    5295               0 :                 break;
    5296                 : 
    5297                 :               case JSOP_XMLPI:
    5298               0 :                 LOAD_ATOM(0);
    5299               0 :                 rval = JS_strdup(cx, POP_STR());
    5300               0 :                 if (!rval)
    5301               0 :                     return NULL;
    5302               0 :                 todo = ss->sprinter.put("<?", 2);
    5303               0 :                 ok = QuoteString(&ss->sprinter, atom, 0) &&
    5304                 :                      (*rval == '\0' ||
    5305               0 :                       (ss->sprinter.put(" ", 1) >= 0 &&
    5306               0 :                        ss->sprinter.put(rval)));
    5307               0 :                 cx->free_((char *)rval);
    5308               0 :                 if (!ok)
    5309               0 :                     return NULL;
    5310               0 :                 ss->sprinter.put("?>", 2);
    5311               0 :                 break;
    5312                 : 
    5313                 :               case JSOP_GETFUNNS:
    5314               0 :                 todo = ss->sprinter.put(js_function_str, 8);
    5315               0 :                 break;
    5316                 : #endif /* JS_HAS_XML_SUPPORT */
    5317                 : 
    5318                 :               default:
    5319           15759 :                 todo = -2;
    5320           15759 :                 break;
    5321                 :             }
    5322                 :         }
    5323                 : 
    5324          125377 :         if (cx->isExceptionPending()) {
    5325                 :             /* OOMs while printing to a string do not immediately return. */
    5326               0 :             return NULL;
    5327                 :         }
    5328                 : 
    5329          125377 :         if (todo < 0) {
    5330                 :             /* -2 means "don't push", -1 means reported error. */
    5331           58365 :             JS_ASSERT(todo == -2);
    5332           58365 :             if (todo == -1)
    5333               0 :                 return NULL;
    5334                 :         } else {
    5335           67012 :             if (!UpdateDecompiledText(ss, pushpc, todo))
    5336               0 :                 return NULL;
    5337           67012 :             if (!PushOff(ss, todo, saveop, pushpc))
    5338               0 :                 return NULL;
    5339           67012 :             if (js_CodeSpec[*pc].format & JOF_DECOMPOSE)
    5340              72 :                 CopyDecompiledTextForDecomposedOp(jp, pc);
    5341                 :         }
    5342                 : 
    5343          125377 :         if (op == JSOP_CALLXMLNAME) {
    5344               9 :             todo = Sprint(&ss->sprinter, "");
    5345               9 :             if (todo < 0 || !PushOff(ss, todo, saveop))
    5346               0 :                 return NULL;
    5347                 :         }
    5348                 : 
    5349          125377 :         pc += len;
    5350                 :     }
    5351                 : 
    5352                 : /*
    5353                 :  * Undefine local macros.
    5354                 :  */
    5355                 : #undef inXML
    5356                 : #undef DECOMPILE_CODE
    5357                 : #undef TOP_STR
    5358                 : #undef POP_STR
    5359                 : #undef POP_STR_PREC
    5360                 : #undef LOCAL_ASSERT
    5361                 : #undef GET_QUOTE_AND_FMT
    5362                 : #undef GET_ATOM_QUOTE_AND_FMT
    5363                 : 
    5364           20698 :     return pc;
    5365                 : }
    5366                 : 
    5367                 : static JSBool
    5368            9718 : DecompileCode(JSPrinter *jp, JSScript *script, jsbytecode *pc, unsigned len,
    5369                 :               unsigned pcdepth)
    5370                 : {
    5371            9718 :     JSContext *cx = jp->sprinter.context;
    5372                 : 
    5373            9718 :     unsigned depth = StackDepth(script);
    5374            9718 :     JS_ASSERT(pcdepth <= depth);
    5375                 : 
    5376                 :     /* Initialize a sprinter for use with the offset stack. */
    5377           19436 :     LifoAllocScope las(&cx->tempLifoAlloc());
    5378           19436 :     SprintStack ss(cx);
    5379            9718 :     if (!InitSprintStack(cx, &ss, jp, depth))
    5380               0 :         return false;
    5381                 : 
    5382                 :     /*
    5383                 :      * If we are called from js_DecompileValueGenerator with a portion of
    5384                 :      * script's bytecode that starts with a non-zero model stack depth given
    5385                 :      * by pcdepth, attempt to initialize the missing string offsets in ss to
    5386                 :      * |spindex| negative indexes from fp->sp for the activation fp in which
    5387                 :      * the error arose.
    5388                 :      *
    5389                 :      * See js_DecompileValueGenerator for how its |spindex| parameter is used,
    5390                 :      * and see also GetOff, which makes use of the ss.offsets[i] < -1 that are
    5391                 :      * potentially stored below.
    5392                 :      */
    5393            9718 :     ss.top = pcdepth;
    5394            9718 :     if (pcdepth != 0) {
    5395            1138 :         for (unsigned i = 0; i < pcdepth; i++) {
    5396             668 :             ss.offsets[i] = -2 - (ptrdiff_t)i;
    5397             668 :             ss.opcodes[i] = *jp->pcstack[i];
    5398                 :         }
    5399                 :     }
    5400                 : 
    5401                 :     /* Call recursive subroutine to do the hard work. */
    5402            9718 :     JSScript *oldscript = jp->script;
    5403            9718 :     jp->script = script;
    5404            9718 :     bool ok = Decompile(&ss, pc, len) != NULL;
    5405            9718 :     jp->script = oldscript;
    5406                 : 
    5407                 :     /* If the given code didn't empty the stack, do it now. */
    5408            9718 :     if (ok && ss.top) {
    5409                 :         const char *last;
    5410            1438 :         do {
    5411            1438 :             last = ss.sprinter.stringAt(PopOff(&ss, JSOP_POP));
    5412                 :         } while (ss.top > pcdepth);
    5413            1195 :         js_printf(jp, "%s", last);
    5414                 :     }
    5415                 : 
    5416            9718 :     return ok;
    5417                 : }
    5418                 : 
    5419                 : /*
    5420                 :  * Decompile a function body, expression closure expression, or complete
    5421                 :  * script. Start at |pc|; go to the end of |script|. Include a directive
    5422                 :  * prologue, if appropriate.
    5423                 :  */
    5424                 : static JSBool
    5425            8523 : DecompileBody(JSPrinter *jp, JSScript *script, jsbytecode *pc)
    5426                 : {
    5427                 :     /* Print a strict mode code directive, if needed. */
    5428            8523 :     if (script->strictModeCode && !jp->strict) {
    5429             135 :         if (jp->fun && (jp->fun->flags & JSFUN_EXPR_CLOSURE)) {
    5430                 :             /*
    5431                 :              * We have no syntax for strict function expressions;
    5432                 :              * at least give a hint.
    5433                 :              */
    5434               0 :             js_printf(jp, "\t/* use strict */ \n");
    5435                 :         } else {
    5436             135 :             js_printf(jp, "\t\"use strict\";\n");
    5437                 :         }
    5438             135 :         jp->strict = true;
    5439                 :     }
    5440                 : 
    5441            8523 :     jsbytecode *end = script->code + script->length;
    5442            8523 :     return DecompileCode(jp, script, pc, end - pc, 0);
    5443                 : }
    5444                 : 
    5445                 : JSBool
    5446               0 : js_DecompileScript(JSPrinter *jp, JSScript *script)
    5447                 : {
    5448               0 :     return DecompileBody(jp, script, script->code);
    5449                 : }
    5450                 : 
    5451                 : JSString *
    5452           11862 : js_DecompileToString(JSContext *cx, const char *name, JSFunction *fun,
    5453                 :                      unsigned indent, JSBool pretty, JSBool grouped, JSBool strict,
    5454                 :                      JSDecompilerPtr decompiler)
    5455                 : {
    5456                 :     JSPrinter *jp;
    5457                 :     JSString *str;
    5458                 : 
    5459           11862 :     jp = js_NewPrinter(cx, name, fun, indent, pretty, grouped, strict);
    5460           11862 :     if (!jp)
    5461               0 :         return NULL;
    5462           11862 :     if (decompiler(jp))
    5463           11862 :         str = js_GetPrinterOutput(jp);
    5464                 :     else
    5465               0 :         str = NULL;
    5466           11862 :     js_DestroyPrinter(jp);
    5467           11862 :     return str;
    5468                 : }
    5469                 : 
    5470                 : static const char native_code_str[] = "\t[native code]\n";
    5471                 : 
    5472                 : JSBool
    5473               0 : js_DecompileFunctionBody(JSPrinter *jp)
    5474                 : {
    5475                 :     JSScript *script;
    5476                 : 
    5477               0 :     JS_ASSERT(jp->fun);
    5478               0 :     JS_ASSERT(!jp->script);
    5479               0 :     if (!jp->fun->isInterpreted()) {
    5480               0 :         js_printf(jp, native_code_str);
    5481               0 :         return JS_TRUE;
    5482                 :     }
    5483                 : 
    5484               0 :     script = jp->fun->script();
    5485               0 :     return DecompileBody(jp, script, script->code);
    5486                 : }
    5487                 : 
    5488                 : JSBool
    5489           11961 : js_DecompileFunction(JSPrinter *jp)
    5490                 : {
    5491           11961 :     JSFunction *fun = jp->fun;
    5492           11961 :     JS_ASSERT(fun);
    5493           11961 :     JS_ASSERT(!jp->script);
    5494                 : 
    5495                 :     /*
    5496                 :      * If pretty, conform to ECMA-262 Edition 3, 15.3.4.2, by decompiling a
    5497                 :      * FunctionDeclaration.  Otherwise, check the JSFUN_LAMBDA flag and force
    5498                 :      * an expression by parenthesizing.
    5499                 :      */
    5500           11961 :     if (jp->pretty) {
    5501             819 :         js_printf(jp, "\t");
    5502                 :     } else {
    5503           11142 :         if (!jp->grouped && (fun->flags & JSFUN_LAMBDA))
    5504            7371 :             js_puts(jp, "(");
    5505                 :     }
    5506                 : 
    5507           11961 :     js_printf(jp, "%s ", js_function_str);
    5508           11961 :     if (fun->atom && !QuoteString(&jp->sprinter, fun->atom, 0))
    5509               0 :         return JS_FALSE;
    5510           11961 :     js_puts(jp, "(");
    5511                 : 
    5512           11961 :     if (!fun->isInterpreted()) {
    5513            3438 :         js_printf(jp, ") {\n");
    5514            3438 :         jp->indent += 4;
    5515            3438 :         js_printf(jp, native_code_str);
    5516            3438 :         jp->indent -= 4;
    5517            3438 :         js_printf(jp, "\t}");
    5518                 :     } else {
    5519            8523 :         JSScript *script = fun->script();
    5520                 : #if JS_HAS_DESTRUCTURING
    5521           17046 :         SprintStack ss(jp->sprinter.context);
    5522                 : #endif
    5523                 : 
    5524                 :         /* Print the parameters. */
    5525            8523 :         jsbytecode *pc = script->main();
    5526            8523 :         jsbytecode *endpc = pc + script->length;
    5527            8523 :         JSBool ok = JS_TRUE;
    5528                 : 
    5529                 : #if JS_HAS_DESTRUCTURING
    5530            8523 :         ss.printer = NULL;
    5531            8523 :         jp->script = script;
    5532                 : #endif
    5533                 : 
    5534           16218 :         for (unsigned i = 0; i < fun->nargs; i++) {
    5535            7695 :             if (i > 0)
    5536             279 :                 js_puts(jp, ", ");
    5537                 : 
    5538            7695 :             JSAtom *param = GetArgOrVarAtom(jp, i);
    5539                 : 
    5540                 : #if JS_HAS_DESTRUCTURING
    5541                 : #define LOCAL_ASSERT(expr)      LOCAL_ASSERT_RV(expr, JS_FALSE)
    5542                 : 
    5543            7695 :             if (!param) {
    5544                 :                 ptrdiff_t todo;
    5545                 :                 const char *lval;
    5546                 : 
    5547              27 :                 LOCAL_ASSERT(*pc == JSOP_GETARG);
    5548              27 :                 pc += JSOP_GETARG_LENGTH;
    5549              27 :                 LOCAL_ASSERT(*pc == JSOP_DUP);
    5550              27 :                 if (!ss.printer) {
    5551              18 :                     ok = InitSprintStack(jp->sprinter.context, &ss, jp, StackDepth(script));
    5552              18 :                     if (!ok)
    5553               0 :                         break;
    5554                 :                 }
    5555              27 :                 pc = DecompileDestructuring(&ss, pc, endpc);
    5556              27 :                 if (!pc) {
    5557               0 :                     ok = JS_FALSE;
    5558               0 :                     break;
    5559                 :                 }
    5560              27 :                 LOCAL_ASSERT(*pc == JSOP_POP);
    5561              27 :                 pc += JSOP_POP_LENGTH;
    5562              27 :                 lval = PopStr(&ss, JSOP_NOP);
    5563              27 :                 todo = jp->sprinter.put(lval);
    5564              27 :                 if (todo < 0) {
    5565               0 :                     ok = JS_FALSE;
    5566               0 :                     break;
    5567                 :                 }
    5568              27 :                 continue;
    5569                 :             }
    5570                 : 
    5571                 : #undef LOCAL_ASSERT
    5572                 : #endif
    5573                 : 
    5574            7668 :             if (!QuoteString(&jp->sprinter, param, 0)) {
    5575               0 :                 ok = JS_FALSE;
    5576               0 :                 break;
    5577                 :             }
    5578                 :         }
    5579                 : 
    5580                 : #if JS_HAS_DESTRUCTURING
    5581            8523 :         jp->script = NULL;
    5582                 : #endif
    5583            8523 :         if (!ok)
    5584               0 :             return JS_FALSE;
    5585            8523 :         js_printf(jp, ") ");
    5586            8523 :         if (!(fun->flags & JSFUN_EXPR_CLOSURE)) {
    5587            8487 :             js_printf(jp, "{\n");
    5588            8487 :             jp->indent += 4;
    5589                 :         }
    5590                 : 
    5591            8523 :         ok = DecompileBody(jp, script, pc);
    5592            8523 :         if (!ok)
    5593               0 :             return JS_FALSE;
    5594                 : 
    5595            8523 :         if (!(fun->flags & JSFUN_EXPR_CLOSURE)) {
    5596            8487 :             jp->indent -= 4;
    5597            8487 :             js_printf(jp, "\t}");
    5598                 :         }
    5599                 :     }
    5600                 : 
    5601           11961 :     if (!jp->pretty && !jp->grouped && (fun->flags & JSFUN_LAMBDA))
    5602            7371 :         js_puts(jp, ")");
    5603                 : 
    5604           11961 :     return JS_TRUE;
    5605                 : }
    5606                 : 
    5607                 : char *
    5608            3518 : js_DecompileValueGenerator(JSContext *cx, int spindex, jsval v,
    5609                 :                            JSString *fallback)
    5610                 : {
    5611                 :     StackFrame *fp;
    5612                 :     JSScript *script;
    5613                 :     jsbytecode *pc;
    5614                 : 
    5615               0 :     JS_ASSERT(spindex < 0 ||
    5616                 :               spindex == JSDVG_IGNORE_STACK ||
    5617            3518 :               spindex == JSDVG_SEARCH_STACK);
    5618                 : 
    5619            3518 :     if (!cx->hasfp() || !cx->fp()->isScriptFrame())
    5620            1836 :         goto do_fallback;
    5621                 : 
    5622            1682 :     fp = js_GetTopStackFrame(cx, FRAME_EXPAND_ALL);
    5623            1682 :     script = fp->script();
    5624            1682 :     pc = cx->regs().pc;
    5625            1682 :     JS_ASSERT(script->code <= pc && pc < script->code + script->length);
    5626                 : 
    5627            1682 :     if (pc < script->main())
    5628               0 :         goto do_fallback;
    5629                 :     
    5630            1682 :     if (spindex != JSDVG_IGNORE_STACK) {
    5631                 :         jsbytecode **pcstack;
    5632                 : 
    5633                 :         /*
    5634                 :          * Prepare computing pcstack containing pointers to opcodes that
    5635                 :          * populated interpreter's stack with its current content.
    5636                 :          */
    5637                 :         pcstack = (jsbytecode **)
    5638            1637 :                   cx->malloc_(StackDepth(script) * sizeof *pcstack);
    5639            1637 :         if (!pcstack)
    5640               0 :             return NULL;
    5641            1637 :         jsbytecode *lastDecomposedPC = NULL;
    5642            1637 :         int pcdepth = ReconstructPCStack(cx, script, pc, pcstack, &lastDecomposedPC);
    5643            1637 :         if (pcdepth < 0)
    5644               0 :             goto release_pcstack;
    5645                 : 
    5646            1637 :         if (spindex != JSDVG_SEARCH_STACK) {
    5647             793 :             JS_ASSERT(spindex < 0);
    5648             793 :             pcdepth += spindex;
    5649             793 :             if (pcdepth < 0)
    5650               0 :                 goto release_pcstack;
    5651             793 :             pc = pcstack[pcdepth];
    5652                 :         } else {
    5653                 :             /*
    5654                 :              * We search from fp->sp to base to find the most recently
    5655                 :              * calculated value matching v under assumption that it is
    5656                 :              * it that caused exception, see bug 328664.
    5657                 :              */
    5658             844 :             Value *stackBase = fp->base();
    5659             844 :             Value *sp = cx->regs().sp;
    5660            1296 :             do {
    5661            1466 :                 if (sp == stackBase) {
    5662             170 :                     pcdepth = -1;
    5663             170 :                     goto release_pcstack;
    5664                 :                 }
    5665                 :             } while (*--sp != v);
    5666                 : 
    5667                 :             /*
    5668                 :              * The value may have come from beyond stackBase + pcdepth, meaning
    5669                 :              * that it came from a temporary slot pushed by the interpreter or
    5670                 :              * arguments pushed for an Invoke call. Only update pc if beneath
    5671                 :              * stackBase + pcdepth. If above, the value may or may not be
    5672                 :              * produced by the current pc. Since it takes a fairly contrived
    5673                 :              * combination of calls to produce a situation where this is not
    5674                 :              * what we want, we just use the current pc.
    5675                 :              *
    5676                 :              * If we are in the middle of a decomposed opcode, use the outer
    5677                 :              * 'fat' opcode itself. Any source notes for the operation which
    5678                 :              * are needed during decompilation will be associated with the
    5679                 :              * outer opcode.
    5680                 :              */
    5681             674 :             if (sp < stackBase + pcdepth) {
    5682             674 :                 pc = pcstack[sp - stackBase];
    5683             674 :                 if (lastDecomposedPC) {
    5684                 :                     size_t len = GetDecomposeLength(lastDecomposedPC,
    5685               9 :                                                     js_CodeSpec[*lastDecomposedPC].length);
    5686               9 :                     if (unsigned(pc - lastDecomposedPC) < len)
    5687               0 :                         pc = lastDecomposedPC;
    5688                 :                 }
    5689                 :             }
    5690                 :         }
    5691                 : 
    5692                 :       release_pcstack:
    5693            1637 :         cx->free_(pcstack);
    5694            1637 :         if (pcdepth < 0)
    5695             170 :             goto do_fallback;
    5696                 :     }
    5697                 : 
    5698                 :     {
    5699            1512 :         char *name = DecompileExpression(cx, script, fp->maybeFun(), pc);
    5700            1512 :         if (name != FAILED_EXPRESSION_DECOMPILER)
    5701            1222 :             return name;
    5702                 :     }
    5703                 : 
    5704                 :   do_fallback:
    5705            2296 :     if (!fallback) {
    5706            2287 :         fallback = js_ValueToSource(cx, v);
    5707            2287 :         if (!fallback)
    5708               0 :             return NULL;
    5709                 :     }
    5710            2296 :     size_t length = fallback->length();
    5711            2296 :     const jschar *chars = fallback->getChars(cx);
    5712            2296 :     if (!chars)
    5713               0 :         return NULL;
    5714            2296 :     return DeflateString(cx, chars, length);
    5715                 : }
    5716                 : 
    5717                 : static char *
    5718            1557 : DecompileExpression(JSContext *cx, JSScript *script, JSFunction *fun,
    5719                 :                     jsbytecode *pc)
    5720                 : {
    5721            1557 :     JS_ASSERT(script->code <= pc && pc < script->code + script->length);
    5722                 : 
    5723            1557 :     JSOp op = (JSOp) *pc;
    5724                 : 
    5725                 :     /* None of these stack-writing ops generates novel values. */
    5726            1557 :     JS_ASSERT(op != JSOP_CASE && op != JSOP_DUP && op != JSOP_DUP2);
    5727                 : 
    5728                 :     /*
    5729                 :      * |this| could convert to a very long object initialiser, so cite it by
    5730                 :      * its keyword name instead.
    5731                 :      */
    5732            1557 :     if (op == JSOP_THIS)
    5733              36 :         return JS_strdup(cx, js_this_str);
    5734                 : 
    5735                 :     /*
    5736                 :      * JSOP_BINDNAME is special: it generates a value, the base object of a
    5737                 :      * reference.  But if it is the generating op for a diagnostic produced by
    5738                 :      * js_DecompileValueGenerator, the name being bound is irrelevant.  Just
    5739                 :      * fall back to the base object.
    5740                 :      */
    5741            1521 :     if (op == JSOP_BINDNAME)
    5742               0 :         return FAILED_EXPRESSION_DECOMPILER;
    5743                 : 
    5744                 :     /* NAME ops are self-contained, others require left or right context. */
    5745            1521 :     const JSCodeSpec *cs = &js_CodeSpec[op];
    5746            1521 :     jsbytecode *begin = pc;
    5747            1521 :     jsbytecode *end = pc + cs->length;
    5748            1521 :     switch (JOF_MODE(cs->format)) {
    5749                 :       case JOF_PROP:
    5750                 :       case JOF_ELEM:
    5751                 :       case JOF_XMLNAME:
    5752                 :       case 0: {
    5753             956 :         jssrcnote *sn = js_GetSrcNote(script, pc);
    5754             956 :         if (!sn)
    5755             317 :             return FAILED_EXPRESSION_DECOMPILER;
    5756             639 :         switch (SN_TYPE(sn)) {
    5757                 :           case SRC_PCBASE:
    5758             630 :             begin -= js_GetSrcNoteOffset(sn, 0);
    5759             630 :             break;
    5760                 :           case SRC_PCDELTA:
    5761               0 :             end = begin + js_GetSrcNoteOffset(sn, 0);
    5762               0 :             begin += cs->length;
    5763               0 :             break;
    5764                 :           default:
    5765               9 :             return FAILED_EXPRESSION_DECOMPILER;
    5766                 :         }
    5767             630 :         break;
    5768                 :       }
    5769                 :       default:;
    5770                 :     }
    5771                 : 
    5772                 :     /*
    5773                 :      * Include the trailing SWAP when decompiling CALLPROP or CALLELEM ops,
    5774                 :      * so that the result is the entire access rather than the lvalue.
    5775                 :      */
    5776            1195 :     if (op == JSOP_CALLPROP || op == JSOP_CALLELEM) {
    5777             243 :         JS_ASSERT(*end == JSOP_SWAP);
    5778             243 :         end += JSOP_SWAP_LENGTH;
    5779                 :     }
    5780                 : 
    5781            1195 :     ptrdiff_t len = end - begin;
    5782            1195 :     if (len <= 0)
    5783               0 :         return FAILED_EXPRESSION_DECOMPILER;
    5784                 : 
    5785                 :     struct Guard {
    5786                 :         jsbytecode **pcstack;
    5787                 :         JSPrinter *printer;
    5788            1195 :         Guard() : pcstack(NULL), printer(NULL) {}
    5789            1195 :         ~Guard() {
    5790            1195 :             if (printer)
    5791            1195 :                 js_DestroyPrinter(printer);
    5792            1195 :             Foreground::free_(pcstack);
    5793            1195 :         }
    5794            2390 :     } g;
    5795                 : 
    5796            1195 :     g.pcstack = (jsbytecode **)OffTheBooks::malloc_(StackDepth(script) * sizeof *g.pcstack);
    5797            1195 :     if (!g.pcstack)
    5798               0 :         return NULL;
    5799                 : 
    5800            1195 :     int pcdepth = ReconstructPCStack(cx, script, begin, g.pcstack, NULL);
    5801            1195 :     if (pcdepth < 0)
    5802               0 :          return FAILED_EXPRESSION_DECOMPILER;
    5803                 : 
    5804            1195 :     g.printer = js_NewPrinter(cx, "js_DecompileValueGenerator", fun, 0, false, false, false);
    5805            1195 :     if (!g.printer)
    5806               0 :         return NULL;
    5807                 : 
    5808            1195 :     g.printer->dvgfence = end;
    5809            1195 :     g.printer->pcstack = g.pcstack;
    5810            1195 :     if (!DecompileCode(g.printer, script, begin, (unsigned) len, (unsigned) pcdepth))
    5811               0 :         return NULL;
    5812                 : 
    5813            1195 :     return JS_strdup(cx, g.printer->sprinter.string());
    5814                 : }
    5815                 : 
    5816                 : unsigned
    5817         5509414 : js_ReconstructStackDepth(JSContext *cx, JSScript *script, jsbytecode *pc)
    5818                 : {
    5819         5509414 :     return ReconstructPCStack(cx, script, pc, NULL, NULL);
    5820                 : }
    5821                 : 
    5822                 : #define LOCAL_ASSERT(expr)      LOCAL_ASSERT_RV(expr, -1);
    5823                 : 
    5824                 : static int
    5825        38215837 : SimulateOp(JSContext *cx, JSScript *script, JSOp op, const JSCodeSpec *cs,
    5826                 :            jsbytecode *pc, jsbytecode **pcstack, unsigned &pcdepth)
    5827                 : {
    5828        38215837 :     unsigned nuses = StackUses(script, pc);
    5829        38215837 :     unsigned ndefs = StackDefs(script, pc);
    5830        38215837 :     LOCAL_ASSERT(pcdepth >= nuses);
    5831        38215837 :     pcdepth -= nuses;
    5832        38215837 :     LOCAL_ASSERT(pcdepth + ndefs <= StackDepth(script));
    5833                 : 
    5834                 :     /*
    5835                 :      * Fill the slots that the opcode defines withs its pc unless it just
    5836                 :      * reshuffles the stack. In the latter case we want to preserve the
    5837                 :      * opcode that generated the original value.
    5838                 :      */
    5839        38215837 :     switch (op) {
    5840                 :       default:
    5841        37917967 :         if (pcstack) {
    5842           90642 :             for (unsigned i = 0; i != ndefs; ++i)
    5843           38116 :                 pcstack[pcdepth + i] = pc;
    5844                 :         }
    5845        37917967 :         break;
    5846                 : 
    5847                 :       case JSOP_CASE:
    5848                 :         /* Keep the switch value. */
    5849               0 :         JS_ASSERT(ndefs == 1);
    5850               0 :         break;
    5851                 : 
    5852                 :       case JSOP_DUP:
    5853          149142 :         JS_ASSERT(ndefs == 2);
    5854          149142 :         if (pcstack)
    5855            1216 :             pcstack[pcdepth + 1] = pcstack[pcdepth];
    5856          149142 :         break;
    5857                 : 
    5858                 :       case JSOP_DUP2:
    5859              54 :         JS_ASSERT(ndefs == 4);
    5860              54 :         if (pcstack) {
    5861              18 :             pcstack[pcdepth + 2] = pcstack[pcdepth];
    5862              18 :             pcstack[pcdepth + 3] = pcstack[pcdepth + 1];
    5863                 :         }
    5864              54 :         break;
    5865                 : 
    5866                 :       case JSOP_SWAP:
    5867          148674 :         JS_ASSERT(ndefs == 2);
    5868          148674 :         if (pcstack) {
    5869            1099 :             jsbytecode *tmp = pcstack[pcdepth + 1];
    5870            1099 :             pcstack[pcdepth + 1] = pcstack[pcdepth];
    5871            1099 :             pcstack[pcdepth] = tmp;
    5872                 :         }
    5873          148674 :         break;
    5874                 :     }
    5875        38215837 :     pcdepth += ndefs;
    5876        38215837 :     return pcdepth;
    5877                 : }
    5878                 : 
    5879                 : static int
    5880         5512246 : ReconstructPCStack(JSContext *cx, JSScript *script, jsbytecode *target,
    5881                 :                    jsbytecode **pcstack, jsbytecode **lastDecomposedPC)
    5882                 : {
    5883                 :     /*
    5884                 :      * Walk forward from script->main and compute the stack depth and stack of
    5885                 :      * operand-generating opcode PCs in pcstack.
    5886                 :      *
    5887                 :      * FIXME: Code to compute oplen copied from js_Disassemble1 and reduced.
    5888                 :      * FIXME: Optimize to use last empty-stack sequence point.
    5889                 :      */
    5890                 : 
    5891         5512246 :     LOCAL_ASSERT(script->code <= target && target < script->code + script->length);
    5892         5512246 :     jsbytecode *pc = script->code;
    5893         5512246 :     unsigned pcdepth = 0;
    5894                 :     ptrdiff_t oplen;
    5895        43749121 :     for (; pc < target; pc += oplen) {
    5896        38236875 :         JSOp op = JSOp(*pc);
    5897        38236875 :         const JSCodeSpec *cs = &js_CodeSpec[op];
    5898        38236875 :         oplen = cs->length;
    5899        38236875 :         if (oplen < 0)
    5900               0 :             oplen = js_GetVariableBytecodeLength(pc);
    5901                 : 
    5902        38236875 :         if (cs->format & JOF_DECOMPOSE) {
    5903            1242 :             if (lastDecomposedPC)
    5904              63 :                 *lastDecomposedPC = pc;
    5905            1242 :             continue;
    5906                 :         }
    5907                 : 
    5908                 :         /*
    5909                 :          * A (C ? T : E) expression requires skipping either T (if target is in
    5910                 :          * E) or both T and E (if target is after the whole expression) before
    5911                 :          * adjusting pcdepth based on the JSOP_IFEQ at pc that tests condition
    5912                 :          * C. We know that the stack depth can't change from what it was with
    5913                 :          * C on top of stack.
    5914                 :          */
    5915        38235633 :         jssrcnote *sn = js_GetSrcNote(script, pc);
    5916        38235633 :         if (sn && SN_TYPE(sn) == SRC_COND) {
    5917             418 :             ptrdiff_t jmpoff = js_GetSrcNoteOffset(sn, 0);
    5918             418 :             if (pc + jmpoff < target) {
    5919             418 :                 pc += jmpoff;
    5920             418 :                 op = JSOp(*pc);
    5921             418 :                 JS_ASSERT(op == JSOP_GOTO);
    5922             418 :                 cs = &js_CodeSpec[op];
    5923             418 :                 oplen = cs->length;
    5924             418 :                 JS_ASSERT(oplen > 0);
    5925             418 :                 ptrdiff_t jmplen = GET_JUMP_OFFSET(pc);
    5926             418 :                 if (pc + jmplen < target) {
    5927             382 :                     oplen = (unsigned) jmplen;
    5928             382 :                     continue;
    5929                 :                 }
    5930                 : 
    5931                 :                 /*
    5932                 :                  * Ok, target lies in E. Manually pop C off the model stack,
    5933                 :                  * since we have moved beyond the IFEQ now.
    5934                 :                  */
    5935              36 :                 LOCAL_ASSERT(pcdepth != 0);
    5936              36 :                 --pcdepth;
    5937                 :             }
    5938                 :         }
    5939                 : 
    5940                 :         /* Ignore early-exit code, which is annotated SRC_HIDDEN. */
    5941        38235251 :         if (sn && SN_TYPE(sn) == SRC_HIDDEN)
    5942           19414 :             continue;
    5943                 : 
    5944        38215837 :         if (SimulateOp(cx, script, op, cs, pc, pcstack, pcdepth) < 0)
    5945               0 :             return -1;
    5946                 : 
    5947                 :     }
    5948         5512246 :     LOCAL_ASSERT(pc == target);
    5949         5512246 :     return pcdepth;
    5950                 : }
    5951                 : 
    5952                 : #undef LOCAL_ASSERT
    5953                 : #undef LOCAL_ASSERT_RV
    5954                 : 
    5955                 : namespace js {
    5956                 : 
    5957                 : bool
    5958            4382 : CallResultEscapes(jsbytecode *pc)
    5959                 : {
    5960                 :     /*
    5961                 :      * If we see any of these sequences, the result is unused:
    5962                 :      * - call / pop
    5963                 :      *
    5964                 :      * If we see any of these sequences, the result is only tested for nullness:
    5965                 :      * - call / ifeq
    5966                 :      * - call / not / ifeq
    5967                 :      */
    5968                 : 
    5969            4382 :     if (*pc != JSOP_CALL)
    5970               1 :         return true;
    5971                 : 
    5972            4381 :     pc += JSOP_CALL_LENGTH;
    5973                 : 
    5974            4381 :     if (*pc == JSOP_POP)
    5975            4366 :         return false;
    5976                 : 
    5977              15 :     if (*pc == JSOP_NOT)
    5978               9 :         pc += JSOP_NOT_LENGTH;
    5979                 : 
    5980              15 :     return (*pc != JSOP_IFEQ);
    5981                 : }
    5982                 : 
    5983                 : extern bool
    5984            2727 : IsValidBytecodeOffset(JSContext *cx, JSScript *script, size_t offset)
    5985                 : {
    5986                 :     // This could be faster (by following jump instructions if the target is <= offset).
    5987         5935140 :     for (BytecodeRange r(script); !r.empty(); r.popFront()) {
    5988         5935131 :         size_t here = r.frontOffset();
    5989         5935131 :         if (here >= offset)
    5990            2718 :             return here == offset;
    5991                 :     }
    5992               9 :     return false;
    5993                 : }
    5994                 : 
    5995                 : JS_FRIEND_API(size_t)
    5996               0 : GetPCCountScriptCount(JSContext *cx)
    5997                 : {
    5998               0 :     JSRuntime *rt = cx->runtime;
    5999                 : 
    6000               0 :     if (!rt->scriptAndCountsVector)
    6001               0 :         return 0;
    6002                 : 
    6003               0 :     return rt->scriptAndCountsVector->length();
    6004                 : }
    6005                 : 
    6006                 : enum MaybeComma {NO_COMMA, COMMA};
    6007                 : 
    6008                 : static void
    6009               0 : AppendJSONProperty(StringBuffer &buf, const char *name, MaybeComma comma = COMMA)
    6010                 : {
    6011               0 :     if (comma)
    6012               0 :         buf.append(',');
    6013                 : 
    6014               0 :     buf.append('\"');
    6015               0 :     buf.appendInflated(name, strlen(name));
    6016               0 :     buf.appendInflated("\":", 2);
    6017               0 : }
    6018                 : 
    6019                 : static void
    6020               0 : AppendArrayJSONProperties(JSContext *cx, StringBuffer &buf,
    6021                 :                           double *values, const char **names, unsigned count, MaybeComma &comma)
    6022                 : {
    6023               0 :     for (unsigned i = 0; i < count; i++) {
    6024               0 :         if (values[i]) {
    6025               0 :             AppendJSONProperty(buf, names[i], comma);
    6026               0 :             comma = COMMA;
    6027               0 :             NumberValueToStringBuffer(cx, DoubleValue(values[i]), buf);
    6028                 :         }
    6029                 :     }
    6030               0 : }
    6031                 : 
    6032                 : JS_FRIEND_API(JSString *)
    6033               0 : GetPCCountScriptSummary(JSContext *cx, size_t index)
    6034                 : {
    6035               0 :     JSRuntime *rt = cx->runtime;
    6036                 : 
    6037               0 :     if (!rt->scriptAndCountsVector || index >= rt->scriptAndCountsVector->length()) {
    6038               0 :         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BUFFER_TOO_SMALL);
    6039               0 :         return NULL;
    6040                 :     }
    6041                 : 
    6042               0 :     ScriptAndCounts info = (*rt->scriptAndCountsVector)[index];
    6043               0 :     JSScript *script = info.script;
    6044                 : 
    6045                 :     /*
    6046                 :      * OOM on buffer appends here will not be caught immediately, but since
    6047                 :      * StringBuffer uses a ContextAllocPolicy will trigger an exception on the
    6048                 :      * context if they occur, which we'll catch before returning.
    6049                 :      */
    6050               0 :     StringBuffer buf(cx);
    6051                 : 
    6052               0 :     buf.append('{');
    6053                 : 
    6054               0 :     AppendJSONProperty(buf, "file", NO_COMMA);
    6055               0 :     JSString *str = JS_NewStringCopyZ(cx, script->filename);
    6056               0 :     if (!str || !(str = JS_ValueToSource(cx, StringValue(str))))
    6057               0 :         return NULL;
    6058               0 :     buf.append(str);
    6059                 : 
    6060               0 :     AppendJSONProperty(buf, "line");
    6061               0 :     NumberValueToStringBuffer(cx, Int32Value(script->lineno), buf);
    6062                 : 
    6063               0 :     if (script->function()) {
    6064               0 :         JSAtom *atom = script->function()->atom;
    6065               0 :         if (atom) {
    6066               0 :             AppendJSONProperty(buf, "name");
    6067               0 :             if (!(str = JS_ValueToSource(cx, StringValue(atom))))
    6068               0 :                 return NULL;
    6069               0 :             buf.append(str);
    6070                 :         }
    6071                 :     }
    6072                 : 
    6073               0 :     double baseTotals[PCCounts::BASE_LIMIT] = {0.0};
    6074               0 :     double accessTotals[PCCounts::ACCESS_LIMIT - PCCounts::BASE_LIMIT] = {0.0};
    6075               0 :     double elementTotals[PCCounts::ELEM_LIMIT - PCCounts::ACCESS_LIMIT] = {0.0};
    6076               0 :     double propertyTotals[PCCounts::PROP_LIMIT - PCCounts::ACCESS_LIMIT] = {0.0};
    6077               0 :     double arithTotals[PCCounts::ARITH_LIMIT - PCCounts::BASE_LIMIT] = {0.0};
    6078                 : 
    6079               0 :     for (unsigned i = 0; i < script->length; i++) {
    6080               0 :         PCCounts &counts = info.getPCCounts(script->code + i);
    6081               0 :         if (!counts)
    6082               0 :             continue;
    6083                 : 
    6084               0 :         JSOp op = (JSOp)script->code[i];
    6085               0 :         unsigned numCounts = PCCounts::numCounts(op);
    6086                 : 
    6087               0 :         for (unsigned j = 0; j < numCounts; j++) {
    6088               0 :             double value = counts.get(j);
    6089               0 :             if (j < PCCounts::BASE_LIMIT) {
    6090               0 :                 baseTotals[j] += value;
    6091               0 :             } else if (PCCounts::accessOp(op)) {
    6092               0 :                 if (j < PCCounts::ACCESS_LIMIT)
    6093               0 :                     accessTotals[j - PCCounts::BASE_LIMIT] += value;
    6094               0 :                 else if (PCCounts::elementOp(op))
    6095               0 :                     elementTotals[j - PCCounts::ACCESS_LIMIT] += value;
    6096               0 :                 else if (PCCounts::propertyOp(op))
    6097               0 :                     propertyTotals[j - PCCounts::ACCESS_LIMIT] += value;
    6098                 :                 else
    6099               0 :                     JS_NOT_REACHED("Bad opcode");
    6100               0 :             } else if (PCCounts::arithOp(op)) {
    6101               0 :                 arithTotals[j - PCCounts::BASE_LIMIT] += value;
    6102                 :             } else {
    6103               0 :                 JS_NOT_REACHED("Bad opcode");
    6104                 :             }
    6105                 :         }
    6106                 :     }
    6107                 : 
    6108               0 :     AppendJSONProperty(buf, "totals");
    6109               0 :     buf.append('{');
    6110                 : 
    6111               0 :     MaybeComma comma = NO_COMMA;
    6112                 : 
    6113                 :     AppendArrayJSONProperties(cx, buf, baseTotals, countBaseNames,
    6114               0 :                               JS_ARRAY_LENGTH(baseTotals), comma);
    6115                 :     AppendArrayJSONProperties(cx, buf, accessTotals, countAccessNames,
    6116               0 :                               JS_ARRAY_LENGTH(accessTotals), comma);
    6117                 :     AppendArrayJSONProperties(cx, buf, elementTotals, countElementNames,
    6118               0 :                               JS_ARRAY_LENGTH(elementTotals), comma);
    6119                 :     AppendArrayJSONProperties(cx, buf, propertyTotals, countPropertyNames,
    6120               0 :                               JS_ARRAY_LENGTH(propertyTotals), comma);
    6121                 :     AppendArrayJSONProperties(cx, buf, arithTotals, countArithNames,
    6122               0 :                               JS_ARRAY_LENGTH(arithTotals), comma);
    6123                 : 
    6124               0 :     buf.append('}');
    6125               0 :     buf.append('}');
    6126                 : 
    6127               0 :     if (cx->isExceptionPending())
    6128               0 :         return NULL;
    6129                 : 
    6130               0 :     return buf.finishString();
    6131                 : }
    6132                 : 
    6133                 : struct AutoDestroyPrinter
    6134                 : {
    6135                 :     JSPrinter *jp;
    6136               0 :     AutoDestroyPrinter(JSPrinter *jp) : jp(jp) {}
    6137               0 :     ~AutoDestroyPrinter() { js_DestroyPrinter(jp); }
    6138                 : };
    6139                 : 
    6140                 : static bool
    6141               0 : GetPCCountJSON(JSContext *cx, const ScriptAndCounts &info, StringBuffer &buf)
    6142                 : {
    6143               0 :     JSScript *script = info.script;
    6144                 : 
    6145               0 :     buf.append('{');
    6146               0 :     AppendJSONProperty(buf, "text", NO_COMMA);
    6147                 : 
    6148               0 :     Vector<DecompiledOpcode> decompiledOpcodes(cx);
    6149               0 :     if (!decompiledOpcodes.reserve(script->length))
    6150               0 :         return false;
    6151                 : 
    6152               0 :     for (unsigned i = 0; i < script->length; i++)
    6153               0 :         decompiledOpcodes.infallibleAppend(DecompiledOpcode());
    6154                 : 
    6155               0 :     JSFunction *fun = script->function();
    6156               0 :     JSPrinter *jp = js_NewPrinter(cx, "", fun, 4, true, false, false);
    6157               0 :     if (!jp)
    6158               0 :         return false;
    6159               0 :     AutoDestroyPrinter destroy(jp);
    6160                 : 
    6161               0 :     jp->decompiledOpcodes = &decompiledOpcodes;
    6162                 : 
    6163               0 :     if (fun) {
    6164               0 :         if (!js_DecompileFunction(jp))
    6165               0 :             return false;
    6166                 :     } else {
    6167               0 :         if (!js_DecompileScript(jp, script))
    6168               0 :             return false;
    6169                 :     }
    6170               0 :     JSString *str = js_GetPrinterOutput(jp);
    6171               0 :     if (!str || !(str = JS_ValueToSource(cx, StringValue(str))))
    6172               0 :         return false;
    6173                 : 
    6174               0 :     buf.append(str);
    6175                 : 
    6176               0 :     AppendJSONProperty(buf, "opcodes");
    6177               0 :     buf.append('[');
    6178               0 :     bool comma = false;
    6179                 : 
    6180               0 :     SrcNoteLineScanner scanner(script->notes(), script->lineno);
    6181                 : 
    6182               0 :     for (jsbytecode *pc = script->code;
    6183               0 :          pc < script->code + script->length;
    6184               0 :          pc += GetBytecodeLength(pc))
    6185                 :     {
    6186               0 :         size_t offset = pc - script->code;
    6187                 : 
    6188               0 :         JSOp op = (JSOp) *pc;
    6189                 : 
    6190               0 :         if (comma)
    6191               0 :             buf.append(',');
    6192               0 :         comma = true;
    6193                 : 
    6194               0 :         buf.append('{');
    6195                 : 
    6196               0 :         AppendJSONProperty(buf, "id", NO_COMMA);
    6197               0 :         NumberValueToStringBuffer(cx, Int32Value(pc - script->code), buf);
    6198                 : 
    6199               0 :         scanner.advanceTo(offset);
    6200                 : 
    6201               0 :         AppendJSONProperty(buf, "line");
    6202               0 :         NumberValueToStringBuffer(cx, Int32Value(scanner.getLine()), buf);
    6203                 : 
    6204                 :         {
    6205               0 :             const char *name = js_CodeName[op];
    6206               0 :             AppendJSONProperty(buf, "name");
    6207               0 :             buf.append('\"');
    6208               0 :             buf.appendInflated(name, strlen(name));
    6209               0 :             buf.append('\"');
    6210                 :         }
    6211                 : 
    6212               0 :         DecompiledOpcode *search = &decompiledOpcodes[offset];
    6213               0 :         size_t textBias = 0;
    6214               0 :         while (search->parent) {
    6215               0 :             textBias += search->parentOffset;
    6216               0 :             if (search->parenthesized)
    6217               0 :                 textBias++;
    6218               0 :             search = &decompiledOpcodes[search->parent - script->code];
    6219                 :         }
    6220                 : 
    6221               0 :         int32_t printedOffset = search->parentOffset;
    6222               0 :         if (printedOffset != -1) {
    6223               0 :             printedOffset += textBias;
    6224               0 :             if (search->parenthesized)
    6225               0 :                 printedOffset++;
    6226               0 :             AppendJSONProperty(buf, "textOffset");
    6227               0 :             NumberValueToStringBuffer(cx, Int32Value(printedOffset), buf);
    6228                 :         }
    6229                 : 
    6230               0 :         const char *text = decompiledOpcodes[offset].text;
    6231               0 :         if (text && *text != 0) {
    6232               0 :             AppendJSONProperty(buf, "text");
    6233               0 :             JSString *str = JS_NewStringCopyZ(cx, text);
    6234               0 :             if (!str || !(str = JS_ValueToSource(cx, StringValue(str))))
    6235               0 :                 return false;
    6236               0 :             buf.append(str);
    6237                 :         }
    6238                 : 
    6239               0 :         PCCounts &counts = info.getPCCounts(pc);
    6240               0 :         unsigned numCounts = PCCounts::numCounts(op);
    6241                 : 
    6242               0 :         AppendJSONProperty(buf, "counts");
    6243               0 :         buf.append('{');
    6244                 : 
    6245               0 :         MaybeComma comma = NO_COMMA;
    6246               0 :         for (unsigned i = 0; i < numCounts; i++) {
    6247               0 :             double value = counts.get(i);
    6248               0 :             if (value > 0) {
    6249               0 :                 AppendJSONProperty(buf, PCCounts::countName(op, i), comma);
    6250               0 :                 comma = COMMA;
    6251               0 :                 NumberValueToStringBuffer(cx, DoubleValue(value), buf);
    6252                 :             }
    6253                 :         }
    6254                 : 
    6255               0 :         buf.append('}');
    6256               0 :         buf.append('}');
    6257                 :     }
    6258                 : 
    6259               0 :     buf.append(']');
    6260               0 :     buf.append('}');
    6261                 : 
    6262               0 :     return !cx->isExceptionPending();
    6263                 : }
    6264                 : 
    6265                 : JS_FRIEND_API(JSString *)
    6266               0 : GetPCCountScriptContents(JSContext *cx, size_t index)
    6267                 : {
    6268               0 :     JSRuntime *rt = cx->runtime;
    6269                 : 
    6270               0 :     if (!rt->scriptAndCountsVector || index >= rt->scriptAndCountsVector->length()) {
    6271               0 :         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BUFFER_TOO_SMALL);
    6272               0 :         return NULL;
    6273                 :     }
    6274                 : 
    6275               0 :     const ScriptAndCounts &info = (*rt->scriptAndCountsVector)[index];
    6276               0 :     JSScript *script = info.script;
    6277                 : 
    6278               0 :     StringBuffer buf(cx);
    6279                 : 
    6280               0 :     if (!script->function() && !script->compileAndGo)
    6281               0 :         return buf.finishString();
    6282                 : 
    6283                 :     {
    6284               0 :         JSAutoEnterCompartment ac;
    6285               0 :         if (!ac.enter(cx, script->function() ? (JSObject *) script->function() : script->global()))
    6286               0 :             return NULL;
    6287                 : 
    6288               0 :         if (!GetPCCountJSON(cx, info, buf))
    6289               0 :             return NULL;
    6290                 :     }
    6291                 : 
    6292               0 :     return buf.finishString();
    6293                 : }
    6294                 : 
    6295           56001 : } // namespace js

Generated by: LCOV version 1.7