LCOV - code coverage report
Current view: directory - js/src - jsopcode.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 3126 2064 66.0 %
Date: 2012-06-02 Functions: 113 93 82.3 %

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

Generated by: LCOV version 1.7