LCOV - app.info - js/src/jsopcode.cpp

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-04-21 | 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           12182 : js_GetVariableBytecodeLength(jsbytecode *pc)
         149                 : {
         150                 :     unsigned ncases;
         151                 :     int32_t low, high;
         152                 :
         153           12182 :     JSOp op = JSOp(*pc);
         154           12182 :     JS_ASSERT(js_CodeSpec[op].length == -1);
         155           12182 :     switch (op) {
         156                 :       case JSOP_TABLESWITCH:
         157                 :         /* Structure: default-jump case-low case-high case1-jump ... */
         158            3173 :         pc += JUMP_OFFSET_LEN;
         159            3173 :         low = GET_JUMP_OFFSET(pc);
         160            3173 :         pc += JUMP_OFFSET_LEN;
         161            3173 :         high = GET_JUMP_OFFSET(pc);
         162            3173 :         ncases = (unsigned)(high - low + 1);
         163            3173 :         return 1 + 3 * JUMP_OFFSET_LEN + ncases * JUMP_OFFSET_LEN;
         164                 :
         165                 :       default:
         166                 :         /* Structure: default-jump case-count (case1-value case1-jump) ... */
         167            9009 :         JS_ASSERT(op == JSOP_LOOKUPSWITCH);
         168            9009 :         pc += JUMP_OFFSET_LEN;
         169            9009 :         ncases = GET_UINT16(pc);
         170            9009 :         return 1 + JUMP_OFFSET_LEN + UINT16_LEN + ncases * (UINT32_INDEX_LEN + JUMP_OFFSET_LEN);
         171                 :     }
         172                 : }
         173                 :
         174                 : static uint32_t
         175          597620 : NumBlockSlots(JSScript *script, jsbytecode *pc)
         176                 : {
         177          597620 :     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          597620 :     return script->getObject(GET_UINT32_INDEX(pc))->asStaticBlock().slotCount();
         182                 : }
         183                 :
         184                 : unsigned
         185       147019240 : js::StackUses(JSScript *script, jsbytecode *pc)
         186                 : {
         187       147019240 :     JSOp op = (JSOp) *pc;
         188       147019240 :     const JSCodeSpec &cs = js_CodeSpec[op];
         189       147019240 :     if (cs.nuses >= 0)
         190       137756659 :         return cs.nuses;
         191                 :
         192         9262581 :     JS_ASSERT(js_CodeSpec[op].nuses == -1);
         193         9262581 :     switch (op) {
         194                 :       case JSOP_POPN:
         195           69184 :         return GET_UINT16(pc);
         196                 :       case JSOP_LEAVEBLOCK:
         197          989716 :         return GET_UINT16(pc);
         198                 :       case JSOP_LEAVEBLOCKEXPR:
         199            4559 :         return GET_UINT16(pc) + 1;
         200                 :       case JSOP_ENTERLET0:
         201           36137 :         return NumBlockSlots(script, pc);
         202                 :       case JSOP_ENTERLET1:
         203           32465 :         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         8130520 :                   op == JSOP_FUNCALL || op == JSOP_FUNAPPLY);
         208         8130520 :         return 2 + GET_ARGC(pc);
         209                 :     }
         210                 : }
         211                 :
         212                 : unsigned
         213       297075437 : js::StackDefs(JSScript *script, jsbytecode *pc)
         214                 : {
         215       297075437 :     JSOp op = (JSOp) *pc;
         216       297075437 :     const JSCodeSpec &cs = js_CodeSpec[op];
         217       297075437 :     if (cs.ndefs >= 0)
         218       296546419 :         return cs.ndefs;
         219                 :
         220          529018 :     uint32_t n = NumBlockSlots(script, pc);
         221          529018 :     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            6128 : Sprinter::realloc_(size_t newSize)
         704                 : {
         705            6128 :     JS_ASSERT(newSize > (size_t) offset);
         706            6128 :     char *newBuf = (char *) context->realloc_(base, newSize);
         707            6128 :     if (!newBuf)
         708               0 :         return false;
         709            6128 :     base = newBuf;
         710            6128 :     size = newSize;
         711            6128 :     base[size - 1] = 0;
         712            6128 :     return true;
         713                 : }
         714                 :
         715           59178 : Sprinter::Sprinter(JSContext *cx)
         716                 :   : context(cx),
         717                 : #ifdef DEBUG
         718                 :     initialized(false),
         719                 : #endif
         720           59178 :     base(NULL), size(0), offset(0)
         721           59178 : { }
         722                 :
         723           59178 : Sprinter::~Sprinter()
         724                 : {
         725                 : #ifdef DEBUG
         726           59178 :     if (initialized)
         727           50787 :         checkInvariants();
         728                 : #endif
         729           59178 :     context->free_(base);
         730           59178 : }
         731                 :
         732                 : bool
         733           50787 : Sprinter::init()
         734                 : {
         735           50787 :     JS_ASSERT(!initialized);
         736           50787 :     base = (char *) context->malloc_(DefaultSize);
         737           50787 :     if (!base)
         738               0 :         return false;
         739                 : #ifdef DEBUG
         740           50787 :     initialized = true;
         741                 : #endif
         742           50787 :     *base = 0;
         743           50787 :     size = DefaultSize;
         744           50787 :     base[size - 1] = 0;
         745           50787 :     return true;
         746                 : }
         747                 :
         748                 : void
         749         4853785 : Sprinter::checkInvariants() const
         750                 : {
         751         4853785 :     JS_ASSERT(initialized);
         752         4853785 :     JS_ASSERT((size_t) offset < size);
         753         4853785 :     JS_ASSERT(base[size - 1] == 0);
         754         4853785 : }
         755                 :
         756                 : const char *
         757           15435 : Sprinter::string() const
         758                 : {
         759           15435 :     return base;
         760                 : }
         761                 :
         762                 : const char *
         763               0 : Sprinter::stringEnd() const
         764                 : {
         765               0 :     return base + offset;
         766                 : }
         767                 :
         768                 : char *
         769          193745 : Sprinter::stringAt(ptrdiff_t off) const
         770                 : {
         771          193745 :     JS_ASSERT(off >= 0 && (size_t) off < size);
         772          193745 :     return base + off;
         773                 : }
         774                 :
         775                 : char &
         776          384589 : Sprinter::operator[](size_t off)
         777                 : {
         778          384589 :     JS_ASSERT(off >= 0 && (size_t) off < size);
         779          384589 :     return *(base + off);
         780                 : }
         781                 :
         782                 : bool
         783             126 : Sprinter::empty() const
         784                 : {
         785             126 :     return *base == 0;
         786                 : }
         787                 :
         788                 : char *
         789         1289158 : Sprinter::reserve(size_t len)
         790                 : {
         791         2578316 :     InvariantChecker ic(this);
         792                 :
         793         1289158 :     while (len + 1 > size - offset) { /* Include trailing \0 */
         794            6128 :         if (!realloc_(size * 2))
         795               0 :             return NULL;
         796                 :     }
         797                 :
         798         1289158 :     char *sb = base + offset;
         799         1289158 :     offset += len;
         800         1289158 :     return sb;
         801                 : }
         802                 :
         803                 : char *
         804           89079 : Sprinter::reserveAndClear(size_t len)
         805                 : {
         806           89079 :     char *sb = reserve(len);
         807           89079 :     if (sb)
         808           89079 :         memset(sb, 0, len);
         809           89079 :     return sb;
         810                 : }
         811                 :
         812                 : ptrdiff_t
         813         1111941 : Sprinter::put(const char *s, size_t len)
         814                 : {
         815         2223882 :     InvariantChecker ic(this);
         816                 :
         817         1111941 :     const char *oldBase = base;
         818         1111941 :     const char *oldEnd = base + size;
         819                 :
         820         1111941 :     ptrdiff_t oldOffset = offset;
         821         1111941 :     char *bp = reserve(len);
         822         1111941 :     if (!bp)
         823               0 :         return -1;
         824                 :
         825                 :     /* s is within the buffer already */
         826         1111941 :     if (s >= oldBase && s < oldEnd) {
         827                 :         /* buffer was realloc'ed */
         828           19133 :         if (base != oldBase)
         829             173 :             s = stringAt(s - oldBase);  /* this is where it lives now */
         830           19133 :         memmove(bp, s, len);
         831                 :     } else {
         832         1092808 :         js_memcpy(bp, s, len);
         833                 :     }
         834                 :
         835         1111941 :     bp[len] = 0;
         836         1111941 :     return oldOffset;
         837                 : }
         838                 :
         839                 : ptrdiff_t
         840          974722 : Sprinter::put(const char *s)
         841                 : {
         842          974722 :     return put(s, strlen(s));
         843                 : }
         844                 :
         845                 : ptrdiff_t
         846             400 : Sprinter::putString(JSString *s)
         847                 : {
         848             800 :     InvariantChecker ic(this);
         849                 :
         850             400 :     size_t length = s->length();
         851             400 :     const jschar *chars = s->getChars(context);
         852             400 :     if (!chars)
         853               0 :         return -1;
         854                 :
         855             400 :     size_t size = GetDeflatedStringLength(context, chars, length);
         856             400 :     if (size == (size_t) -1)
         857               0 :         return -1;
         858                 :
         859             400 :     ptrdiff_t oldOffset = offset;
         860             400 :     char *buffer = reserve(size);
         861             400 :     if (!buffer)
         862               0 :         return -1;
         863             400 :     DeflateStringToBuffer(context, chars, length, buffer, &size);
         864             400 :     buffer[size] = 0;
         865                 :
         866             400 :     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           20944 : Sprinter::setOffset(const char *end)
         891                 : {
         892           20944 :     JS_ASSERT(end >= base && end < base + size);
         893           20944 :     offset = end - base;
         894           20944 : }
         895                 :
         896                 : void
         897          105661 : Sprinter::setOffset(ptrdiff_t off)
         898                 : {
         899          105661 :     JS_ASSERT(off >= 0 && (size_t) off < size);
         900          105661 :     offset = off;
         901          105661 : }
         902                 :
         903                 : ptrdiff_t
         904          361299 : Sprinter::getOffset() const
         905                 : {
         906          361299 :     return offset;
         907                 : }
         908                 :
         909                 : ptrdiff_t
         910           12330 : Sprinter::getOffsetOf(const char *string) const
         911                 : {
         912           12330 :     JS_ASSERT(string >= base && string < base + size);
         913           12330 :     return string - base;
         914                 : }
         915                 :
         916                 : ptrdiff_t
         917          736065 : js::Sprint(Sprinter *sp, const char *format, ...)
         918                 : {
         919                 :     va_list ap;
         920                 :     char *bp;
         921                 :     ptrdiff_t offset;
         922                 :
         923          736065 :     va_start(ap, format);
         924          736065 :     bp = JS_vsmprintf(format, ap);      /* XXX vsaprintf */
         925          736065 :     va_end(ap);
         926          736065 :     if (!bp) {
         927               0 :         JS_ReportOutOfMemory(sp->context);
         928               0 :         return -1;
         929                 :     }
         930          736065 :     offset = sp->put(bp);
         931          736065 :     sp->context->free_(bp);
         932          736065 :     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           89488 : QuoteString(Sprinter *sp, JSString *str, uint32_t quote)
         952                 : {
         953                 :     /* Sample off first for later return value pointer computation. */
         954           89488 :     JSBool dontEscape = (quote & DONT_ESCAPE) != 0;
         955           89488 :     jschar qc = (jschar) quote;
         956           89488 :     ptrdiff_t offset = sp->getOffset();
         957           89488 :     if (qc && Sprint(sp, "%c", (char)qc) < 0)
         958               0 :         return NULL;
         959                 :
         960           89488 :     const jschar *s = str->getChars(sp->context);
         961           89488 :     if (!s)
         962               0 :         return NULL;
         963           89488 :     const jschar *z = s + str->length();
         964                 :
         965                 :     /* Loop control variables: z points at end of string sentinel. */
         966           89982 :     for (const jschar *t = s; t < z; s = ++t) {
         967                 :         /* Move t forward from s past un-quote-worthy characters. */
         968           87711 :         jschar c = *t;
         969          364104 :         while (c < 127 && isprint(c) && c != qc && c != '\\' && c != '\t') {
         970          275899 :             c = *++t;
         971          275899 :             if (t == z)
         972           87217 :                 break;
         973                 :         }
         974                 :
         975                 :         {
         976           87711 :             ptrdiff_t len = t - s;
         977           87711 :             ptrdiff_t base = sp->getOffset();
         978           87711 :             char *bp = sp->reserve(len);
         979           87711 :             if (!bp)
         980               0 :                 return NULL;
         981                 :
         982          363610 :             for (ptrdiff_t i = 0; i < len; ++i)
         983          275899 :                 (*sp)[base + i] = (char) *s++;
         984           87711 :             (*sp)[base + len] = 0;
         985                 :         }
         986                 :
         987           87711 :         if (t == z)
         988           87217 :             break;
         989                 :
         990                 :         /* Use js_EscapeMap, \u, or \x only if necessary. */
         991                 :         bool ok;
         992                 :         const char *e;
         993             494 :         if (!(c >> 8) && c != 0 && (e = strchr(js_EscapeMap, (int)c)) != NULL) {
         994                 :             ok = dontEscape
         995               0 :                  ? Sprint(sp, "%c", (char)c) >= 0
         996             436 :                  : 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             494 :         if (!ok)
        1006               0 :             return NULL;
        1007                 :     }
        1008                 :
        1009                 :     /* Sprint the closing quote and return the quoted string. */
        1010           89488 :     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           89488 :     if (offset == sp->getOffset() && Sprint(sp, "") < 0)
        1018               0 :         return NULL;
        1019                 :
        1020           89488 :     return sp->stringAt(offset);
        1021                 : }
        1022                 :
        1023                 : JSString *
        1024           24344 : js_QuoteString(JSContext *cx, JSString *str, jschar quote)
        1025                 : {
        1026           48688 :     Sprinter sprinter(cx);
        1027           24344 :     if (!sprinter.init())
        1028               0 :         return NULL;
        1029           24344 :     char *bytes = QuoteString(&sprinter, str, quote);
        1030           24344 :     JSString *escstr = bytes ? JS_NewStringCopyZ(cx, bytes) : NULL;
        1031           24344 :     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           15389 : js_NewPrinter(JSContext *cx, const char *name, JSFunction *fun,
        1090                 :               unsigned indent, JSBool pretty, JSBool grouped, JSBool strict)
        1091                 : {
        1092           15389 :     JSPrinter *jp = (JSPrinter *) cx->malloc_(sizeof(JSPrinter));
        1093           15389 :     if (!jp)
        1094               0 :         return NULL;
        1095           15389 :     new (&jp->sprinter) Sprinter(cx);
        1096           15389 :     if (!jp->sprinter.init())
        1097               0 :         return NULL;
        1098           15389 :     new (&jp->pool) LifoAlloc(1024);
        1099           15389 :     jp->indent = indent;
        1100           15389 :     jp->pretty = !!pretty;
        1101           15389 :     jp->grouped = !!grouped;
        1102           15389 :     jp->strict = !!strict;
        1103           15389 :     jp->script = NULL;
        1104           15389 :     jp->dvgfence = NULL;
        1105           15389 :     jp->pcstack = NULL;
        1106           15389 :     jp->fun = fun;
        1107           15389 :     jp->localNames = NULL;
        1108           15389 :     jp->decompiledOpcodes = NULL;
        1109           15389 :     if (fun && fun->isInterpreted() && fun->script()->bindings.hasLocalNames()) {
        1110            9156 :         jp->localNames = cx->new_<Vector<JSAtom *> >(cx);
        1111            9156 :         if (!jp->localNames || !fun->script()->bindings.getLocalNameArray(cx, jp->localNames)) {
        1112               0 :             js_DestroyPrinter(jp);
        1113               0 :             return NULL;
        1114                 :         }
        1115                 :     }
        1116           15389 :     return jp;
        1117                 : }
        1118                 :
        1119                 : void
        1120           15389 : js_DestroyPrinter(JSPrinter *jp)
        1121                 : {
        1122           15389 :     JSContext *cx = jp->sprinter.context;
        1123           15389 :     jp->pool.freeAll();
        1124           15389 :     Foreground::delete_(jp->localNames);
        1125           15389 :     jp->sprinter.Sprinter::~Sprinter();
        1126           15389 :     cx->free_(jp);
        1127           15389 : }
        1128                 :
        1129                 : JSString *
        1130           12798 : js_GetPrinterOutput(JSPrinter *jp)
        1131                 : {
        1132           12798 :     JSContext *cx = jp->sprinter.context;
        1133           12798 :     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           45855 : UpdateDecompiledParent(JSPrinter *jp, jsbytecode *pc, jsbytecode *parent, size_t offset)
        1139                 : {
        1140           45855 :     if (jp->decompiledOpcodes && pc) {
        1141               0 :         jp->decompiled(pc).parent = parent;
        1142               0 :         jp->decompiled(pc).parentOffset = offset;
        1143                 :     }
        1144           45855 : }
        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           31455 : VarPrefix(jssrcnote *sn)
        1153                 : {
        1154           31455 :     if (sn && (SN_TYPE(sn) == SRC_DECL || SN_TYPE(sn) == SRC_GROUPASSIGN)) {
        1155            5278 :         ptrdiff_t type = js_GetSrcNoteOffset(sn, 0);
        1156            5278 :         if ((unsigned)type <= SRC_DECL_LET)
        1157            4711 :             return var_prefix[type];
        1158                 :     }
        1159           26744 :     return "";
        1160                 : }
        1161                 :
        1162                 : int
        1163          116168 : js_printf(JSPrinter *jp, const char *format, ...)
        1164                 : {
        1165                 :     va_list ap;
        1166                 :     char *bp, *fp;
        1167                 :     int cc;
        1168                 :
        1169          116168 :     if (*format == '\0')
        1170               0 :         return 0;
        1171                 :
        1172          116168 :     va_start(ap, format);
        1173                 :
        1174                 :     /* If pretty-printing, expand magic tab into a run of jp->indent spaces. */
        1175          116168 :     if (*format == '\t') {
        1176           44637 :         format++;
        1177           44637 :         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          116168 :     fp = NULL;
        1185          116168 :     if (!jp->pretty && format[cc = strlen(format) - 1] == '\n') {
        1186           38131 :         fp = JS_strdup(jp->sprinter.context, format);
        1187           38131 :         if (!fp) {
        1188               0 :             va_end(ap);
        1189               0 :             return -1;
        1190                 :         }
        1191           38131 :         fp[cc] = '\0';
        1192           38131 :         format = fp;
        1193                 :     }
        1194                 :
        1195                 :     /* Allocate temp space, convert format, and put. */
        1196          116168 :     bp = JS_vsmprintf(format, ap);      /* XXX vsaprintf */
        1197          116168 :     if (fp) {
        1198           38131 :         jp->sprinter.context->free_(fp);
        1199           38131 :         format = NULL;
        1200                 :     }
        1201          116168 :     if (!bp) {
        1202               0 :         JS_ReportOutOfMemory(jp->sprinter.context);
        1203               0 :         va_end(ap);
        1204               0 :         return -1;
        1205                 :     }
        1206                 :
        1207          116168 :     cc = strlen(bp);
        1208          116168 :     if (jp->sprinter.put(bp, (size_t)cc) < 0)
        1209               0 :         cc = -1;
        1210          116168 :     jp->sprinter.context->free_(bp);
        1211                 :
        1212          116168 :     va_end(ap);
        1213          116168 :     return cc;
        1214                 : }
        1215                 :
        1216                 : JSBool
        1217           28959 : js_puts(JSPrinter *jp, const char *s)
        1218                 : {
        1219           28959 :     return jp->sprinter.put(s) >= 0;
        1220                 : }
        1221                 :
        1222                 : /************************************************************************/
        1223                 :
        1224                 : struct SprintStack
        1225           19418 : {
        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           19418 :     explicit SprintStack(JSContext *cx)
        1236                 :       : sprinter(cx), offsets(NULL),
        1237                 :         opcodes(NULL), bytecodes(NULL), top(0), inArrayInit(0),
        1238           19418 :         inGenExp(JS_FALSE), printer(NULL)
        1239           19418 :     { }
        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           68001 : UpdateDecompiledText(SprintStack *ss, jsbytecode *pc, ptrdiff_t todo)
        1248                 : {
        1249           68001 :     JSPrinter *jp = ss->printer;
        1250                 :
        1251           68001 :     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           68001 :     return true;
        1266                 : }
        1267                 :
        1268                 : static inline const char *
        1269           26131 : SprintDupeStr(SprintStack *ss, const char *str)
        1270                 : {
        1271           26131 :     size_t len = strlen(str) + 1;
        1272                 :
        1273           26131 :     const char *nstr = ss->printer->pool.newArrayUninitialized<char>(len);
        1274           26131 :     if (nstr) {
        1275           26131 :         js_memcpy((char *) nstr, str, len);
        1276                 :     } else {
        1277               0 :         js_ReportOutOfMemory(ss->sprinter.context);
        1278               0 :         nstr = "";
        1279                 :     }
        1280                 :
        1281           26131 :     return nstr;
        1282                 : }
        1283                 :
        1284                 : /* Place an opcode's decompiled text into a printer's permanent output. */
        1285                 : static inline void
        1286           16680 : SprintOpcodePermanent(JSPrinter *jp, const char *str, jsbytecode *pc)
        1287                 : {
        1288           16680 :     ptrdiff_t offset = jp->sprinter.getOffset();
        1289           16680 :     UpdateDecompiledParent(jp, pc, NULL, offset);
        1290           16680 :     js_printf(jp, "%s", str);
        1291           16680 : }
        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           27951 : SprintOpcode(SprintStack *ss, const char *str, jsbytecode *pc,
        1300                 :              jsbytecode *parentpc, ptrdiff_t startOffset)
        1301                 : {
        1302           27951 :     if (startOffset < 0) {
        1303               0 :         JS_ASSERT(ss->sprinter.context->isExceptionPending());
        1304               0 :         return;
        1305                 :     }
        1306           27951 :     ptrdiff_t offset = ss->sprinter.getOffset();
        1307           27951 :     UpdateDecompiledParent(ss->printer, pc, parentpc, offset - startOffset);
        1308           27951 :     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           93860 : GetOff(SprintStack *ss, unsigned i)
        1374                 : {
        1375                 :     ptrdiff_t off;
        1376                 :     jsbytecode *pc;
        1377                 :     char *bytes;
        1378                 :
        1379           93860 :     off = ss->offsets[i];
        1380           93860 :     if (off >= 0)
        1381           93804 :         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            8280 : GetStr(SprintStack *ss, unsigned i)
        1409                 : {
        1410            8280 :     ptrdiff_t off = GetOff(ss, i);
        1411            8280 :     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           89079 : AddParenSlop(SprintStack *ss)
        1425                 : {
        1426           89079 :     ss->sprinter.reserveAndClear(PAREN_SLOP);
        1427           89079 : }
        1428                 :
        1429                 : static JSBool
        1430           89070 : 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           89070 :     top = ss->top;
        1436           89070 :     JS_ASSERT(top < StackDepth(ss->printer->script));
        1437           89070 :     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           89070 :     ss->offsets[top] = off;
        1444           89070 :     ss->opcodes[top] = jsbytecode((op == JSOP_GETPROP2) ? JSOP_GETPROP
        1445                 :                                 : (op == JSOP_GETELEM2) ? JSOP_GETELEM
        1446           89070 :                                 : op);
        1447           89070 :     ss->bytecodes[top] = pc;
        1448           89070 :     ss->top = ++top;
        1449                 :
        1450           89070 :     AddParenSlop(ss);
        1451           89070 :     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           78920 : PopOffPrec(SprintStack *ss, uint8_t prec, jsbytecode **ppc = NULL)
        1465                 : {
        1466                 :     unsigned top;
        1467                 :     const JSCodeSpec *topcs;
        1468                 :     ptrdiff_t off;
        1469                 :
        1470           78920 :     if (ppc)
        1471           46417 :         *ppc = NULL;
        1472                 :
        1473                 :     /* ss->top points to the next free slot; be paranoid about underflow. */
        1474           78920 :     top = ss->top;
        1475           78920 :     JS_ASSERT(top != 0);
        1476           78920 :     if (top == 0)
        1477               0 :         return 0;
        1478                 :
        1479           78920 :     ss->top = --top;
        1480           78920 :     off = GetOff(ss, top);
        1481           78920 :     topcs = &js_CodeSpec[ss->opcodes[top]];
        1482                 :
        1483           78920 :     jsbytecode *pc = ss->bytecodes[top];
        1484           78920 :     if (ppc)
        1485           46417 :         *ppc = pc;
        1486                 :
        1487           78920 :     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           78335 :         ss->sprinter.setOffset(off);
        1495                 :     }
        1496           78920 :     return off;
        1497                 : }
        1498                 :
        1499                 : static const char *
        1500           71043 : PopStrPrec(SprintStack *ss, uint8_t prec, jsbytecode **ppc = NULL)
        1501                 : {
        1502                 :     ptrdiff_t off;
        1503                 :
        1504           71043 :     off = PopOffPrec(ss, prec, ppc);
        1505           71043 :     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           17908 : PopStrPrecDupe(SprintStack *ss, uint8_t prec, jsbytecode **ppc = NULL)
        1515                 : {
        1516           17908 :     const char *str = PopStrPrec(ss, prec, ppc);
        1517           17908 :     return SprintDupeStr(ss, str);
        1518                 : }
        1519                 :
        1520                 : static ptrdiff_t
        1521            7877 : PopOff(SprintStack *ss, JSOp op, jsbytecode **ppc = NULL)
        1522                 : {
        1523            7877 :     return PopOffPrec(ss, js_CodeSpec[op].prec, ppc);
        1524                 : }
        1525                 :
        1526                 : static const char *
        1527           49743 : PopStr(SprintStack *ss, JSOp op, jsbytecode **ppc = NULL)
        1528                 : {
        1529           49743 :     return PopStrPrec(ss, js_CodeSpec[op].prec, ppc);
        1530                 : }
        1531                 :
        1532                 : static const char *
        1533           14516 : PopStrDupe(SprintStack *ss, JSOp op, jsbytecode **ppc = NULL)
        1534                 : {
        1535           14516 :     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            1354 : PopCondStr(SprintStack *ss, jsbytecode **ppc = NULL)
        1544                 : {
        1545            1354 :     JSOp op = (js_CodeSpec[ss->opcodes[ss->top - 1]].format & JOF_SET)
        1546                 :               ? JSOP_IFEQ
        1547            1354 :               : JSOP_NOP;
        1548            1354 :     return PopStr(ss, op, ppc);
        1549                 : }
        1550                 :
        1551                 : static inline bool
        1552            1485 : IsInitializerOp(unsigned char op)
        1553                 : {
        1554            1485 :     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           26236 : GetArgOrVarAtom(JSPrinter *jp, unsigned slot)
        1756                 : {
        1757           26236 :     LOCAL_ASSERT_RV(jp->fun, NULL);
        1758           26236 :     LOCAL_ASSERT_RV(slot < jp->fun->script()->bindings.countLocalNames(), NULL);
        1759           26236 :     JSAtom *name = (*jp->localNames)[slot];
        1760                 : #if !JS_HAS_DESTRUCTURING
        1761                 :     LOCAL_ASSERT_RV(name, NULL);
        1762                 : #endif
        1763           26236 :     return name;
        1764                 : }
        1765                 :
        1766                 : #define LOCAL_ASSERT(expr)      LOCAL_ASSERT_RV(expr, "")
        1767                 :
        1768                 : static const char *
        1769             206 : GetLocalInSlot(SprintStack *ss, int i, int slot, JSObject *obj)
        1770                 : {
        1771             255 :     for (Shape::Range r(obj->lastProperty()); !r.empty(); r.popFront()) {
        1772             237 :         const Shape &shape = r.front();
        1773                 :
        1774             237 :         if (shape.shortid() == slot) {
        1775                 :             /* Ignore the empty destructuring dummy. */
        1776             206 :             if (!JSID_IS_ATOM(shape.propid()))
        1777              18 :                 continue;
        1778                 :
        1779             188 :             JSAtom *atom = JSID_TO_ATOM(shape.propid());
        1780             188 :             const char *rval = QuoteString(&ss->sprinter, atom, 0);
        1781             188 :             if (!rval)
        1782               0 :                 return NULL;
        1783                 :
        1784             188 :             ss->sprinter.setOffset(rval);
        1785             188 :             return rval;
        1786                 :         }
        1787                 :     }
        1788                 :
        1789              18 :     return GetStr(ss, i);
        1790                 : }
        1791                 :
        1792                 : const char *
        1793            8126 : GetLocal(SprintStack *ss, int i)
        1794                 : {
        1795            8126 :     ptrdiff_t off = ss->offsets[i];
        1796            8126 :     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             206 :     JSScript *script = ss->printer->script;
        1810             206 :     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             206 :     if (off <= -2 && ss->printer->pcstack) {
        1817             197 :         jsbytecode *pc = ss->printer->pcstack[-2 - off];
        1818                 :
        1819             197 :         JS_ASSERT(ss->printer->script->code <= pc);
        1820             197 :         JS_ASSERT(pc < (ss->printer->script->code + ss->printer->script->length));
        1821                 :
        1822             197 :         if (JSOP_ENTERBLOCK == (JSOp)*pc) {
        1823              40 :             JSObject *obj = script->getObject(GET_UINT32_INDEX(pc));
        1824                 :
        1825              40 :             if (obj->isBlock()) {
        1826              40 :                 uint32_t depth = obj->asBlock().stackDepth();
        1827              40 :                 uint32_t count = obj->asBlock().slotCount();
        1828              40 :                 if (uint32_t(i - depth) < uint32_t(count))
        1829              40 :                     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           14784 : IsVarSlot(JSPrinter *jp, jsbytecode *pc, int *indexp)
        1853                 : {
        1854                 :     unsigned slot;
        1855                 :
        1856           14784 :     slot = GET_SLOTNO(pc);
        1857           14784 :     if (slot < jp->script->nfixed) {
        1858                 :         /* The slot refers to a variable with name stored in jp->localNames. */
        1859            6172 :         *indexp = jp->fun->nargs + slot;
        1860            6172 :         return JS_TRUE;
        1861                 :     }
        1862                 :
        1863                 :     /* We have a local which index is relative to the stack base. */
        1864            8612 :     slot -= jp->script->nfixed;
        1865            8612 :     JS_ASSERT(slot < StackDepth(jp->script));
        1866            8612 :     *indexp = slot;
        1867            8612 :     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           19801 : 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             847 : GetTokenForAssignment(JSPrinter *jp, jssrcnote *sn, JSOp lastop,
        2403                 :                       jsbytecode *pc, jsbytecode *rvalpc,
        2404                 :                       jsbytecode **lastlvalpc, jsbytecode **lastrvalpc)
        2405                 : {
        2406                 :     const char *token;
        2407             847 :     if (sn && SN_TYPE(sn) == SRC_ASSIGNOP) {
        2408            1224 :         if (lastop == JSOP_GETTER) {
        2409               0 :             token = js_getter_str;
        2410             612 :         } else if (lastop == JSOP_SETTER) {
        2411               0 :             token = js_setter_str;
        2412                 :         } else {
        2413             612 :             token = CodeToken[lastop];
        2414             612 :             if (*lastlvalpc && *lastrvalpc) {
        2415             612 :                 UpdateDecompiledParent(jp, *lastlvalpc, pc, 0);
        2416             612 :                 UpdateDecompiledParent(jp, *lastrvalpc, rvalpc, 0);
        2417                 :             }
        2418                 :         }
        2419                 :     } else {
        2420             235 :         token = "";
        2421                 :     }
        2422             847 :     *lastlvalpc = NULL;
        2423             847 :     *lastrvalpc = NULL;
        2424             847 :     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           11027 : InitSprintStack(JSContext *cx, SprintStack *ss, JSPrinter *jp, unsigned depth)
        2509                 : {
        2510           11027 :     if (!ss->sprinter.init())
        2511               0 :         return JS_FALSE;
        2512           11027 :     ss->sprinter.setOffset(PAREN_SLOP);
        2513                 :
        2514                 :     /* Allocate the parallel (to avoid padding) offset, opcode and bytecode stacks. */
        2515           11027 :     size_t offsetsz = depth * sizeof(ptrdiff_t);
        2516           11027 :     size_t opcodesz = depth * sizeof(jsbytecode);
        2517           11027 :     size_t bytecodesz = depth * sizeof(jsbytecode *);
        2518           11027 :     void *space = cx->tempLifoAlloc().alloc(offsetsz + opcodesz + bytecodesz);
        2519           11027 :     if (!space) {
        2520               0 :         js_ReportOutOfMemory(cx);
        2521               0 :         return JS_FALSE;
        2522                 :     }
        2523           11027 :     ss->offsets = (ptrdiff_t *) space;
        2524           11027 :     ss->opcodes = (jsbytecode *) ((char *)space + offsetsz);
        2525           11027 :     ss->bytecodes = (jsbytecode **) ((char *)space + offsetsz + opcodesz);
        2526                 :
        2527           11027 :     ss->top = ss->inArrayInit = 0;
        2528           11027 :     ss->inGenExp = JS_FALSE;
        2529           11027 :     ss->printer = jp;
        2530           11027 :     return JS_TRUE;
        2531                 : }
        2532                 :
        2533                 : template <typename T>
        2534                 : void
        2535            3510 : Swap(T &a, T &b)
        2536                 : {
        2537            3510 :     T tmp = a;
        2538            3510 :     a = b;
        2539            3510 :     b = tmp;
        2540            3510 : }
        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           21258 : 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           21258 :     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           21258 :     jsbytecode *lastlvalpc = NULL, *lastrvalpc = NULL;
        2659                 :
        2660           21258 :     cx = ss->sprinter.context;
        2661           21258 :     JS_CHECK_RECURSION(cx, return NULL);
        2662                 :
        2663           21258 :     jp = ss->printer;
        2664           21258 :     startpc = pc;
        2665           21258 :     endpc = (nb < 0) ? jp->script->code + jp->script->length : pc + nb;
        2666           21258 :     tail = -1;
        2667           21258 :     todo = -2;                  /* NB: different from Sprint() error return. */
        2668           21258 :     saveop = JSOP_NOP;
        2669           21258 :     sn = NULL;
        2670           21258 :     rval = NULL;
        2671           21258 :     bool forOf = false;
        2672                 : #if JS_HAS_XML_SUPPORT
        2673           21258 :     foreach = inXML = quoteAttr = JS_FALSE;
        2674                 : #endif
        2675                 :
        2676          168095 :     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          125741 :         lastop = saveop;
        2685          125741 :         op = (JSOp) *pc;
        2686          125741 :         cs = &js_CodeSpec[op];
        2687          125741 :         saveop = op;
        2688          125741 :         len = oplen = cs->length;
        2689          125741 :         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          125741 :         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          125714 :         token = CodeToken[op];
        2708                 :
        2709          125714 :         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            2465 :             uint32_t format = cs->format;
        2717            2465 :             bool matchPC = false;
        2718            2465 :             FrameRegsIter iter(cx);
        2719            2465 :             if (!iter.done()) {
        2720            2465 :                 jsbytecode *npc = iter.pc();
        2721            2465 :                 if (pc == npc) {
        2722             467 :                     matchPC = true;
        2723            1998 :                 } else if (format & JOF_DECOMPOSE) {
        2724               0 :                     if (unsigned(npc - pc) < GetDecomposeLength(pc, js_CodeSpec[*pc].length))
        2725               0 :                         matchPC = true;
        2726                 :                 }
        2727                 :             }
        2728            2465 :             if ((matchPC || (pc == startpc && nuses != 0)) &&
        2729                 :                 format & (JOF_SET|JOF_DEL|JOF_INCDEC|JOF_VARPROP)) {
        2730             241 :                 uint32_t mode = JOF_MODE(format);
        2731             241 :                 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             213 :                     if (mode == JOF_PROP) {
        2759                 :                         op = (JSOp) ((format & JOF_SET)
        2760                 :                                      ? JSOP_GETPROP2
        2761              18 :                                      : JSOP_GETPROP);
        2762             195 :                     } else if (mode == JOF_ELEM) {
        2763                 :                         op = (JSOp) ((format & JOF_SET)
        2764                 :                                      ? JSOP_GETELEM2
        2765             195 :                                      : 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            2465 :             saveop = op;
        2788            2465 :             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            2465 :             jp->dvgfence = NULL;
        2796                 :         }
        2797                 :
        2798          125714 :         jsbytecode *pushpc = pc;
        2799                 :
        2800          125714 :         if (token) {
        2801           14098 :             switch (nuses) {
        2802                 :               case 2:
        2803            4004 :                 sn = js_GetSrcNote(jp->script, pc);
        2804            4004 :                 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             612 :                     op = (JSOp) pc[oplen];
        2811             612 :                     rval = PopStr(ss, op, &lastrvalpc);
        2812             612 :                     (void)PopStr(ss, op, &lastlvalpc);
        2813                 :
        2814                 :                     /* Print only the right operand of the assignment-op. */
        2815             612 :                     todo = ss->sprinter.put(rval);
        2816            3392 :                 } else if (!inXML) {
        2817            3392 :                     rval = PopStrPrecDupe(ss, cs->prec + !!(cs->format & JOF_LEFTASSOC), &rvalpc);
        2818            3392 :                     lval = PopStrPrec(ss, cs->prec + !(cs->format & JOF_LEFTASSOC), &lvalpc);
        2819            3392 :                     todo = ss->sprinter.getOffset();
        2820            3392 :                     SprintOpcode(ss, lval, lvalpc, pc, todo);
        2821            3392 :                     Sprint(&ss->sprinter, " %s ", token);
        2822            3392 :                     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            4004 :                 break;
        2831                 :
        2832                 :               case 1:
        2833              36 :                 rval = PopStrDupe(ss, op, &rvalpc);
        2834              36 :                 todo = ss->sprinter.put(token);
        2835              36 :                 SprintOpcode(ss, rval, rvalpc, pc, todo);
        2836              36 :                 break;
        2837                 :
        2838                 :               case 0:
        2839           10058 :                 sn = js_GetSrcNote(jp->script, pc);
        2840           10058 :                 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            9788 :                 todo = ss->sprinter.put(token);
        2846            9788 :                 break;
        2847                 :
        2848                 :               default:
        2849               0 :                 todo = -2;
        2850               0 :                 break;
        2851                 :             }
        2852                 :         } else {
        2853          111616 :             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             189 :                 todo = Sprint(&ss->sprinter, "");
        2958             189 :                 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            6484 :                 op = JSOP_POPV;
        3126                 :                 /* FALL THROUGH */
        3127                 :
        3128                 :               case JSOP_POPV:
        3129            6484 :                 sn = js_GetSrcNote(jp->script, pc);
        3130            6484 :                 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            5143 :                     if (ss->opcodes[ss->top-1] == JSOP_YIELD)
        3181               0 :                         op = JSOP_NOP;
        3182                 :
        3183                 :                     jsbytecode *rvalpc;
        3184            5143 :                     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            5143 :                     if (*rval != '\0' && (rval[0] != '/' || rval[1] != '*')) {
        3193                 :                         bool parens =
        3194                 :                             *rval == '{' ||
        3195            5143 :                             (strncmp(rval, js_function_str, 8) == 0 &&
        3196           10286 :                              rval[8] == ' ');
        3197            5143 :                         js_printf(jp, parens ? "\t(" : "\t");
        3198            5143 :                         SprintOpcodePermanent(jp, rval, rvalpc);
        3199            5143 :                         js_printf(jp, parens ? ");\n" : ";\n");
        3200                 :                     } else {
        3201               0 :                         LOCAL_ASSERT(*rval == '\0' ||
        3202                 :                                      strcmp(rval, exception_cookie) == 0);
        3203                 :                     }
        3204            5143 :                     todo = -2;
        3205            5143 :                     break;
        3206                 :                   }
        3207                 :                 }
        3208            6484 :                 sn = NULL;
        3209            6484 :                 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             181 :                 if (!jp->fun)
        3529               0 :                     jp->fun = jp->script->getCallerFunction();
        3530                 :
        3531             181 :                 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             181 :                 unsigned index = GET_UINT16(pc);
        3542             181 :                 if (index < jp->fun->script()->bindings.countUpvars()) {
        3543             181 :                     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             181 :                 atom = GetArgOrVarAtom(jp, index);
        3571             181 :                 goto do_name;
        3572                 :               }
        3573                 :
        3574                 :               case JSOP_CALLLOCAL:
        3575                 :               case JSOP_GETLOCAL:
        3576            8724 :                 if (IsVarSlot(jp, pc, &i)) {
        3577            3109 :                     atom = GetArgOrVarAtom(jp, i);
        3578            3109 :                     LOCAL_ASSERT(atom);
        3579            3109 :                     goto do_name;
        3580                 :                 }
        3581            5615 :                 LOCAL_ASSERT((unsigned)i < ss->top);
        3582            5615 :                 sn = js_GetSrcNote(jp->script, pc);
        3583                 :
        3584                 : #if JS_HAS_DESTRUCTURING
        3585            5615 :                 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            5129 :                 rval = GetLocal(ss, i);
        3607            5129 :                 todo = Sprint(&ss->sprinter, ss_format, VarPrefix(sn), rval);
        3608            5129 :                 break;
        3609                 :
        3610                 :               case JSOP_SETLOCAL:
        3611                 :               case JSOP_SETLOCALPOP:
        3612            4062 :                 if (IsVarSlot(jp, pc, &i)) {
        3613            2235 :                     atom = GetArgOrVarAtom(jp, i);
        3614            2235 :                     LOCAL_ASSERT(atom);
        3615            2235 :                     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            4076 :                 LOCAL_ASSERT(jp->fun);
        3647            4076 :                 fun = jp->fun;
        3648            4076 :                 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            7622 :                 rval = PopStr(ss, op, &rvalpc);
        3667            7622 :                 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              13 :                     js_printf(jp, "\t%s;\n", js_return_str);
        3673                 :                 }
        3674            7622 :                 todo = -2;
        3675            7622 :                 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            1364 :                 JSBool elseif = JS_FALSE;
        3977                 :
        3978                 :               if_again:
        3979            1364 :                 len = GET_JUMP_OFFSET(pc);
        3980            1364 :                 sn = js_GetSrcNote(jp->script, pc);
        3981                 :
        3982            1364 :                 switch (sn ? SN_TYPE(sn) : SRC_NULL) {
        3983                 :                   case SRC_IF:
        3984                 :                   case SRC_IF_ELSE:
        3985            1345 :                     rval = PopCondStr(ss, &rvalpc);
        3986            1345 :                     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            1336 :                         js_printf(jp, elseif ? " if (" : "\tif (");
        3994            1336 :                         SprintOpcodePermanent(jp, rval, rvalpc);
        3995            1336 :                         js_printf(jp, ") {\n");
        3996            1336 :                         jp->indent += 4;
        3997                 :                     }
        3998                 :
        3999            1345 :                     if (SN_TYPE(sn) == SRC_IF) {
        4000            1318 :                         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            1345 :                     if (!ss->inArrayInit && !ss->inGenExp) {
        4036            1336 :                         jp->indent -= 4;
        4037            1336 :                         js_printf(jp, "\t}\n");
        4038                 :                     }
        4039            1345 :                     todo = -2;
        4040            1345 :                     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            1364 :                 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            4302 :                 sn = js_GetSrcNote(jp->script, pc);
        4150            4302 :                 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            1260 :                 rval = GetStr(ss, ss->top-1);
        4258            1260 :                 saveop = (JSOp) ss->opcodes[ss->top-1];
        4259            1260 :                 todo = ss->sprinter.put(rval);
        4260            1260 :                 break;
        4261                 :
        4262                 :               case JSOP_SWAP:
        4263            1170 :                 Swap(ss->offsets[ss->top-1], ss->offsets[ss->top-2]);
        4264            1170 :                 Swap(ss->opcodes[ss->top-1], ss->opcodes[ss->top-2]);
        4265            1170 :                 Swap(ss->bytecodes[ss->top-1], ss->bytecodes[ss->top-2]);
        4266            1170 :                 todo = -2;
        4267            1170 :                 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             162 :                 LOAD_ATOM(0);
        4278                 :
        4279                 :               do_setname:
        4280            2514 :                 lval = QuoteString(&ss->sprinter, atom, 0);
        4281            2514 :                 if (!lval)
        4282               0 :                     return NULL;
        4283            2514 :                 rval = PopStrDupe(ss, op, &rvalpc);
        4284            2514 :                 if (op == JSOP_SETNAME || op == JSOP_SETGNAME)
        4285             162 :                     (void) PopOff(ss, op);
        4286                 :
        4287                 :               do_setlval:
        4288            4341 :                 sn = js_GetSrcNote(jp->script, pc - 1);
        4289            4341 :                 todo = ss->sprinter.getOffset();
        4290            4341 :                 if (sn && SN_TYPE(sn) == SRC_ASSIGNOP) {
        4291                 :                     const char *token =
        4292                 :                         GetTokenForAssignment(jp, sn, lastop, pc, rvalpc,
        4293             540 :                                               &lastlvalpc, &lastrvalpc);
        4294             540 :                     Sprint(&ss->sprinter, "%s %s= ", lval, token);
        4295             540 :                     SprintOpcode(ss, rval, rvalpc, pc, todo);
        4296                 :                 } else {
        4297            3801 :                     sn = js_GetSrcNote(jp->script, pc);
        4298            3801 :                     const char *prefix = VarPrefix(sn);
        4299            3801 :                     Sprint(&ss->sprinter, "%s%s = ", prefix, lval);
        4300            3801 :                     SprintOpcode(ss, rval, rvalpc, pc, todo);
        4301                 :                 }
        4302            4341 :                 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            4341 :                 break;
        4311                 :
        4312                 :               case JSOP_NEW:
        4313                 :               case JSOP_CALL:
        4314                 :               case JSOP_EVAL:
        4315                 :               case JSOP_FUNCALL:
        4316                 :               case JSOP_FUNAPPLY:
        4317                 :               {
        4318            3491 :                 argc = GET_ARGC(pc);
        4319                 :                 const char **argv = (const char **)
        4320            3491 :                     cx->malloc_((size_t)(argc + 1) * sizeof *argv);
        4321            3491 :                 if (!argv)
        4322               0 :                     return NULL;
        4323                 :                 jsbytecode **argbytecodes = (jsbytecode **)
        4324            3491 :                     cx->malloc_((size_t)(argc + 1) * sizeof *argbytecodes);
        4325            3491 :                 if (!argbytecodes) {
        4326               0 :                     cx->free_(argv);
        4327               0 :                     return NULL;
        4328                 :                 }
        4329                 :
        4330            3491 :                 op = JSOP_SETNAME;
        4331            6854 :                 for (i = argc; i > 0; i--)
        4332            3363 :                     argv[i] = PopStrDupe(ss, op, &argbytecodes[i]);
        4333                 :
        4334                 :                 /* Skip the JSOP_PUSHOBJ-created empty string. */
        4335            3491 :                 LOCAL_ASSERT(ss->top >= 2);
        4336            3491 :                 (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            3491 :                 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            3491 :                                      &lvalpc);
        4355            3491 :                 op = saveop;
        4356                 :
        4357            3491 :                 lval = "(", rval = ")";
        4358            3491 :                 todo = ss->sprinter.getOffset();
        4359            3491 :                 if (op == JSOP_NEW) {
        4360             252 :                     if (argc == 0)
        4361             108 :                         lval = rval = "";
        4362             252 :                     Sprint(&ss->sprinter, "%s ", js_new_str);
        4363                 :                 }
        4364            3491 :                 SprintOpcode(ss, argv[0], lvalpc, pc, todo);
        4365            3491 :                 ss->sprinter.put(lval);
        4366                 :
        4367            6854 :                 for (i = 1; i <= argc; i++) {
        4368            3363 :                     SprintOpcode(ss, argv[i], argbytecodes[i], pc, todo);
        4369            3363 :                     if (i < argc)
        4370             376 :                         ss->sprinter.put(", ");
        4371                 :                 }
        4372            3491 :                 ss->sprinter.put(rval);
        4373                 :
        4374            3491 :                 cx->free_(argv);
        4375            3491 :                 cx->free_(argbytecodes);
        4376                 :
        4377            3491 :                 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            2552 :                 LOAD_ATOM(0);
        4566                 :
        4567            2552 :                 GET_QUOTE_AND_FMT("[%s]", ".%s", rval);
        4568            2552 :                 PROPAGATE_CALLNESS();
        4569            2552 :                 lval = PopStr(ss, op, &lvalpc);
        4570            2552 :                 todo = ss->sprinter.getOffset();
        4571            2552 :                 SprintOpcode(ss, lval, lvalpc, pc, todo);
        4572            2552 :                 Sprint(&ss->sprinter, fmt, rval);
        4573            2552 :                 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             723 :                 op = JSOP_NOP;          /* turn off parens */
        4606             723 :                 xval = PopStrDupe(ss, op, &xvalpc);
        4607             723 :                 op = saveop;
        4608             723 :                 PROPAGATE_CALLNESS();
        4609             723 :                 lval = PopStr(ss, op, &lvalpc);
        4610             723 :                 todo = ss->sprinter.getOffset();
        4611             723 :                 SprintOpcode(ss, lval, lvalpc, pc, todo);
        4612             723 :                 if (*xval != '\0') {
        4613             714 :                     bool xml = (JOF_OPMODE(lastop) == JOF_XMLNAME);
        4614             714 :                     ss->sprinter.put(xml ? "." : "[");
        4615             714 :                     SprintOpcode(ss, xval, xvalpc, pc, todo);
        4616             714 :                     ss->sprinter.put(xml ? "" : "]");
        4617                 :                 }
        4618             723 :                 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           12030 :                 i = GET_ARGNO(pc);
        4649           12030 :                 atom = GetArgOrVarAtom(jp, i);
        4650                 : #if JS_HAS_DESTRUCTURING
        4651           12030 :                 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           12030 :                 goto do_name;
        4659                 :
        4660                 :               case JSOP_CALLNAME:
        4661                 :               case JSOP_NAME:
        4662                 :               case JSOP_GETGNAME:
        4663                 :               case JSOP_CALLGNAME:
        4664            4586 :                 LOAD_ATOM(0);
        4665                 :               do_name:
        4666           19933 :                 lval = "";
        4667                 : #if JS_HAS_XML_SUPPORT
        4668                 :               do_qname:
        4669                 : #endif
        4670           19942 :                 sn = js_GetSrcNote(jp->script, pc);
        4671           19942 :                 rval = QuoteString(&ss->sprinter, atom, inXML ? DONT_ESCAPE : 0);
        4672           19942 :                 if (!rval)
        4673               0 :                     return NULL;
        4674           19942 :                 ss->sprinter.setOffset(rval);
        4675                 :                 todo = Sprint(&ss->sprinter, sss_format,
        4676           19942 :                               VarPrefix(sn), lval, rval);
        4677           19942 :                 break;
        4678                 :
        4679                 :               case JSOP_UINT16:
        4680              27 :                 i = (int) GET_UINT16(pc);
        4681              27 :                 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            1252 :                 i = GET_INT8(pc);
        4689            1252 :                 goto do_sprint_int;
        4690                 :
        4691                 :               case JSOP_INT32:
        4692               0 :                 i = GET_INT32(pc);
        4693                 :               do_sprint_int:
        4694            1279 :                 todo = Sprint(&ss->sprinter, "%d", i);
        4695            1279 :                 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            3303 :                 LOAD_ATOM(0);
        4706            3303 :                 rval = QuoteString(&ss->sprinter, atom, inXML ? DONT_ESCAPE : '"');
        4707            3303 :                 if (!rval)
        4708               0 :                     return NULL;
        4709            3303 :                 todo = ss->sprinter.getOffsetOf(rval);
        4710            3303 :                 break;
        4711                 :
        4712                 :               case JSOP_LAMBDA:
        4713                 :               case JSOP_LAMBDA_FC:
        4714                 : #if JS_HAS_GENERATOR_EXPRS
        4715             463 :                 sn = js_GetSrcNote(jp->script, pc);
        4716             463 :                 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             328 :                 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             328 :                     bool grouped = !(fun->flags & JSFUN_EXPR_CLOSURE);
        4859             328 :                     bool strict = jp->script->strictModeCode;
        4860                 :                     str = js_DecompileToString(cx, "lambda", fun, 0,
        4861                 :                                                false, grouped, strict,
        4862             328 :                                                js_DecompileFunction);
        4863             328 :                     if (!str)
        4864               0 :                         return NULL;
        4865                 :                 }
        4866                 :               sprint_string:
        4867             346 :                 todo = ss->sprinter.putString(str);
        4868             346 :                 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             522 :                 i = GET_UINT8(pc);
        5090             522 :                 LOCAL_ASSERT(i == JSProto_Array || i == JSProto_Object);
        5091                 :
        5092             522 :                 todo = ss->sprinter.getOffset();
        5093             522 :                 if (i == JSProto_Array) {
        5094             126 :                     ++ss->inArrayInit;
        5095             126 :                     if (ss->sprinter.put("[") < 0)
        5096               0 :                         return NULL;
        5097                 :                 } else {
        5098             396 :                     if (ss->sprinter.put("{") < 0)
        5099               0 :                         return NULL;
        5100                 :                 }
        5101             522 :                 break;
        5102                 :               }
        5103                 :
        5104                 :               case JSOP_NEWARRAY:
        5105                 :               {
        5106             945 :                 todo = ss->sprinter.getOffset();
        5107             945 :                 ++ss->inArrayInit;
        5108             945 :                 if (ss->sprinter.put("[") < 0)
        5109               0 :                     return NULL;
        5110             945 :                 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            1539 :                 op = JSOP_NOP;           /* turn off parens */
        5126            1539 :                 rval = PopStr(ss, op, &rvalpc);
        5127            1539 :                 sn = js_GetSrcNote(jp->script, pc);
        5128                 :
        5129                 :                 /* Skip any #n= prefix to find the opening bracket. */
        5130            1539 :                 for (xval = rval; *xval != '[' && *xval != '{'; xval++)
        5131               0 :                     continue;
        5132            1539 :                 inArray = (*xval == '[');
        5133            1539 :                 if (inArray)
        5134            1071 :                     --ss->inArrayInit;
        5135            1539 :                 todo = ss->sprinter.getOffset();
        5136            1539 :                 SprintOpcode(ss, rval, rvalpc, pc, todo);
        5137                 :                 Sprint(&ss->sprinter, "%s%c",
        5138                 :                        (sn && SN_TYPE(sn) == SRC_CONTINUE) ? ", " : "",
        5139            1539 :                        inArray ? ']' : '}');
        5140            1539 :                 break;
        5141                 :               }
        5142                 :
        5143                 :               {
        5144                 :                 JSBool isFirst;
        5145                 :                 const char *maybeComma;
        5146                 :
        5147                 :               case JSOP_INITELEM:
        5148            1170 :                 isFirst = IsInitializerOp(ss->opcodes[ss->top - 3]);
        5149                 :
        5150                 :                 /* Turn off most parens. */
        5151            1170 :                 rval = PopStr(ss, JSOP_SETNAME, &rvalpc);
        5152                 :
        5153                 :                 /* Turn off all parens for xval and lval, which we control. */
        5154            1170 :                 xval = PopStr(ss, JSOP_NOP);
        5155            1170 :                 lval = PopStr(ss, JSOP_NOP, &lvalpc);
        5156            1170 :                 sn = js_GetSrcNote(jp->script, pc);
        5157                 :
        5158            1170 :                 if (sn && SN_TYPE(sn) == SRC_INITPROP) {
        5159              54 :                     atom = NULL;
        5160              54 :                     goto do_initprop;
        5161                 :                 }
        5162            1116 :                 maybeComma = isFirst ? "" : ", ";
        5163            1116 :                 todo = Sprint(&ss->sprinter, "%s%s", lval, maybeComma);
        5164            1116 :                 SprintOpcode(ss, rval, rvalpc, pc, todo);
        5165            1116 :                 break;
        5166                 :
        5167                 :               case JSOP_INITPROP:
        5168                 :               case JSOP_INITMETHOD:
        5169             315 :                 LOAD_ATOM(0);
        5170             315 :                 xval = QuoteString(&ss->sprinter, atom, jschar(IsIdentifier(atom) ? 0 : '\''));
        5171             315 :                 if (!xval)
        5172               0 :                     return NULL;
        5173             315 :                 isFirst = IsInitializerOp(ss->opcodes[ss->top - 2]);
        5174             315 :                 rval = PopStrDupe(ss, op, &rvalpc);
        5175             315 :                 lval = PopStr(ss, op, &lvalpc);
        5176                 :                 /* fall through */
        5177                 :
        5178                 :               do_initprop:
        5179             369 :                 todo = ss->sprinter.getOffset();
        5180             369 :                 SprintOpcode(ss, lval, lvalpc, pc, todo);
        5181             369 :                 maybeComma = isFirst ? "" : ", ";
        5182             369 :                 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             369 :                     Sprint(&ss->sprinter, "%s%s: ", maybeComma, xval);
        5200             369 :                     SprintOpcode(ss, rval, rvalpc, pc, todo);
        5201                 :                 }
        5202             369 :                 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           15663 :                 todo = -2;
        5374           15663 :                 break;
        5375                 :             }
        5376                 :         }
        5377                 :
        5378          125579 :         if (cx->isExceptionPending()) {
        5379                 :             /* OOMs while printing to a string do not immediately return. */
        5380               0 :             return NULL;
        5381                 :         }
        5382                 :
        5383          125579 :         if (todo < 0) {
        5384                 :             /* -2 means "don't push", -1 means reported error. */
        5385           57578 :             JS_ASSERT(todo == -2);
        5386           57578 :             if (todo == -1)
        5387               0 :                 return NULL;
        5388                 :         } else {
        5389           68001 :             if (!UpdateDecompiledText(ss, pushpc, todo))
        5390               0 :                 return NULL;
        5391           68001 :             if (!PushOff(ss, todo, saveop, pushpc))
        5392               0 :                 return NULL;
        5393           68001 :             if (js_CodeSpec[*pc].format & JOF_DECOMPOSE)
        5394              77 :                 CopyDecompiledTextForDecomposedOp(jp, pc);
        5395                 :         }
        5396                 :
        5397          125579 :         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          125579 :         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           21096 :     return pc;
        5419                 : }
        5420                 :
        5421                 : static JSBool
        5422           10874 : DecompileCode(JSPrinter *jp, JSScript *script, jsbytecode *pc, unsigned len,
        5423                 :               unsigned pcdepth)
        5424                 : {
        5425           10874 :     JSContext *cx = jp->sprinter.context;
        5426                 :
        5427           10874 :     unsigned depth = StackDepth(script);
        5428           10874 :     JS_ASSERT(pcdepth <= depth);
        5429                 :
        5430                 :     /* Initialize a sprinter for use with the offset stack. */
        5431           21748 :     LifoAllocScope las(&cx->tempLifoAlloc());
        5432           21748 :     SprintStack ss(cx);
        5433           10874 :     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           10874 :     ss.top = pcdepth;
        5448           10874 :     if (pcdepth != 0) {
        5449            3064 :         for (unsigned i = 0; i < pcdepth; i++) {
        5450            2259 :             ss.offsets[i] = -2 - (ptrdiff_t)i;
        5451            2259 :             ss.opcodes[i] = *jp->pcstack[i];
        5452                 :         }
        5453                 :     }
        5454                 :
        5455                 :     /* Call recursive subroutine to do the hard work. */
        5456           10874 :     JSScript *oldscript = jp->script;
        5457           10874 :     jp->script = script;
        5458           10874 :     bool ok = Decompile(&ss, pc, len) != NULL;
        5459           10874 :     jp->script = oldscript;
        5460                 :
        5461                 :     /* If the given code didn't empty the stack, do it now. */
        5462           10874 :     if (ok && ss.top) {
        5463                 :         const char *last;
        5464            3106 :         do {
        5465            3106 :             last = ss.sprinter.stringAt(PopOff(&ss, JSOP_POP));
        5466                 :         } while (ss.top > pcdepth);
        5467            2465 :         js_printf(jp, "%s", last);
        5468                 :     }
        5469                 :
        5470           10874 :     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            8409 : DecompileBody(JSPrinter *jp, JSScript *script, jsbytecode *pc)
        5480                 : {
        5481                 :     /* Print a strict mode code directive, if needed. */
        5482            8409 :     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            8409 :     jsbytecode *end = script->code + script->length;
        5496            8409 :     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           12798 : 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           12798 :     jp = js_NewPrinter(cx, name, fun, indent, pretty, grouped, strict);
        5514           12798 :     if (!jp)
        5515               0 :         return NULL;
        5516           12798 :     if (decompiler(jp))
        5517           12798 :         str = js_GetPrinterOutput(jp);
        5518                 :     else
        5519               0 :         str = NULL;
        5520           12798 :     js_DestroyPrinter(jp);
        5521           12798 :     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           12924 : js_DecompileFunction(JSPrinter *jp)
        5544                 : {
        5545           12924 :     JSFunction *fun = jp->fun;
        5546           12924 :     JS_ASSERT(fun);
        5547           12924 :     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           12924 :     if (jp->pretty) {
        5555            2009 :         js_printf(jp, "\t");
        5556                 :     } else {
        5557           10915 :         if (!jp->grouped && (fun->flags & JSFUN_LAMBDA))
        5558            7158 :             js_puts(jp, "(");
        5559                 :     }
        5560                 :
        5561           12924 :     js_printf(jp, "%s ", js_function_str);
        5562           12924 :     if (fun->atom && !QuoteString(&jp->sprinter, fun->atom, 0))
        5563               0 :         return JS_FALSE;
        5564           12924 :     js_puts(jp, "(");
        5565                 :
        5566           12924 :     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            8409 :         JSScript *script = fun->script();
        5574                 : #if JS_HAS_DESTRUCTURING
        5575           16818 :         SprintStack ss(jp->sprinter.context);
        5576                 : #endif
        5577                 :
        5578                 :         /* Print the parameters. */
        5579            8409 :         jsbytecode *pc = script->main();
        5580            8409 :         jsbytecode *endpc = pc + script->length;
        5581            8409 :         JSBool ok = JS_TRUE;
        5582                 :
        5583                 : #if JS_HAS_DESTRUCTURING
        5584            8409 :         ss.printer = NULL;
        5585            8409 :         jp->script = script;
        5586                 : #endif
        5587                 :
        5588           15893 :         for (unsigned i = 0; i < fun->nargs; i++) {
        5589            7484 :             if (i > 0)
        5590             306 :                 js_puts(jp, ", ");
        5591                 :
        5592            7484 :             JSAtom *param = GetArgOrVarAtom(jp, i);
        5593                 :
        5594                 : #if JS_HAS_DESTRUCTURING
        5595                 : #define LOCAL_ASSERT(expr)      LOCAL_ASSERT_RV(expr, JS_FALSE)
        5596                 :
        5597            7484 :             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            7457 :             if (!QuoteString(&jp->sprinter, param, 0)) {
        5629               0 :                 ok = JS_FALSE;
        5630               0 :                 break;
        5631                 :             }
        5632                 :         }
        5633                 :
        5634                 : #if JS_HAS_DESTRUCTURING
        5635            8409 :         jp->script = NULL;
        5636                 : #endif
        5637            8409 :         if (!ok)
        5638               0 :             return JS_FALSE;
        5639            8409 :         js_printf(jp, ") ");
        5640            8409 :         if (!(fun->flags & JSFUN_EXPR_CLOSURE)) {
        5641            8382 :             js_printf(jp, "{\n");
        5642            8382 :             jp->indent += 4;
        5643                 :         }
        5644                 :
        5645            8409 :         ok = DecompileBody(jp, script, pc);
        5646            8409 :         if (!ok)
        5647               0 :             return JS_FALSE;
        5648                 :
        5649            8409 :         if (!(fun->flags & JSFUN_EXPR_CLOSURE)) {
        5650            8382 :             jp->indent -= 4;
        5651            8382 :             js_printf(jp, "\t}");
        5652                 :         }
        5653                 :     }
        5654                 :
        5655           12924 :     if (!jp->pretty && !jp->grouped && (fun->flags & JSFUN_LAMBDA))
        5656            7158 :         js_puts(jp, ")");
        5657                 :
        5658           12924 :     return JS_TRUE;
        5659                 : }
        5660                 :
        5661                 : char *
        5662            4811 : 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            4811 :               spindex == JSDVG_SEARCH_STACK);
        5672                 :
        5673            4811 :     if (!cx->hasfp() || !cx->fp()->isScriptFrame())
        5674            1818 :         goto do_fallback;
        5675                 :
        5676            2993 :     fp = js_GetTopStackFrame(cx, FRAME_EXPAND_ALL);
        5677            2993 :     script = fp->script();
        5678            2993 :     pc = cx->regs().pc;
        5679            2993 :     JS_ASSERT(script->code <= pc && pc < script->code + script->length);
        5680                 :
        5681            2993 :     if (pc < script->main())
        5682               0 :         goto do_fallback;
        5683                 :
        5684            2993 :     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            2479 :                   cx->malloc_(StackDepth(script) * sizeof *pcstack);
        5693            2479 :         if (!pcstack)
        5694               0 :             return NULL;
        5695            2479 :         jsbytecode *lastDecomposedPC = NULL;
        5696            2479 :         int pcdepth = ReconstructPCStack(cx, script, pc, pcstack, &lastDecomposedPC);
        5697            2479 :         if (pcdepth < 0)
        5698               0 :             goto release_pcstack;
        5699                 :
        5700            2479 :         if (spindex != JSDVG_SEARCH_STACK) {
        5701            1191 :             JS_ASSERT(spindex < 0);
        5702            1191 :             pcdepth += spindex;
        5703            1191 :             if (pcdepth < 0)
        5704               0 :                 goto release_pcstack;
        5705            1191 :             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            1288 :             Value *stackBase = fp->base();
        5713            1288 :             Value *sp = cx->regs().sp;
        5714            1999 :             do {
        5715            2169 :                 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            1118 :             if (sp < stackBase + pcdepth) {
        5736            1118 :                 pc = pcstack[sp - stackBase];
        5737            1118 :                 if (lastDecomposedPC) {
        5738                 :                     size_t len = GetDecomposeLength(lastDecomposedPC,
        5739              77 :                                                     js_CodeSpec[*lastDecomposedPC].length);
        5740              77 :                     if (unsigned(pc - lastDecomposedPC) < len)
        5741               0 :                         pc = lastDecomposedPC;
        5742                 :                 }
        5743                 :             }
        5744                 :         }
        5745                 :
        5746                 :       release_pcstack:
        5747            2479 :         cx->free_(pcstack);
        5748            2479 :         if (pcdepth < 0)
        5749             170 :             goto do_fallback;
        5750                 :     }
        5751                 :
        5752                 :     {
        5753            2823 :         char *name = DecompileExpression(cx, script, fp->maybeFun(), pc);
        5754            2823 :         if (name != FAILED_EXPRESSION_DECOMPILER)
        5755            2495 :             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            2870 : DecompileExpression(JSContext *cx, JSScript *script, JSFunction *fun,
        5773                 :                     jsbytecode *pc)
        5774                 : {
        5775            2870 :     JS_ASSERT(script->code <= pc && pc < script->code + script->length);
        5776                 :
        5777            2870 :     JSOp op = (JSOp) *pc;
        5778                 :
        5779                 :     /* None of these stack-writing ops generates novel values. */
        5780            2870 :     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            2870 :     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            2830 :     if (op == JSOP_BINDNAME)
        5796               1 :         return FAILED_EXPRESSION_DECOMPILER;
        5797                 :
        5798                 :     /* NAME ops are self-contained, others require left or right context. */
        5799            2829 :     const JSCodeSpec *cs = &js_CodeSpec[op];
        5800            2829 :     jsbytecode *begin = pc;
        5801            2829 :     jsbytecode *end = pc + cs->length;
        5802            2829 :     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            2465 :     if (op == JSOP_CALLPROP || op == JSOP_CALLELEM) {
        5831             641 :         JS_ASSERT(*end == JSOP_SWAP);
        5832             641 :         end += JSOP_SWAP_LENGTH;
        5833                 :     }
        5834                 :
        5835            2465 :     ptrdiff_t len = end - begin;
        5836            2465 :     if (len <= 0)
        5837               0 :         return FAILED_EXPRESSION_DECOMPILER;
        5838                 :
        5839                 :     struct Guard {
        5840                 :         jsbytecode **pcstack;
        5841                 :         JSPrinter *printer;
        5842            2465 :         Guard() : pcstack(NULL), printer(NULL) {}
        5843            2465 :         ~Guard() {
        5844            2465 :             if (printer)
        5845            2465 :                 js_DestroyPrinter(printer);
        5846            2465 :             Foreground::free_(pcstack);
        5847            2465 :         }
        5848            4930 :     } g;
        5849                 :
        5850            2465 :     g.pcstack = (jsbytecode **)OffTheBooks::malloc_(StackDepth(script) * sizeof *g.pcstack);
        5851            2465 :     if (!g.pcstack)
        5852               0 :         return NULL;
        5853                 :
        5854            2465 :     int pcdepth = ReconstructPCStack(cx, script, begin, g.pcstack, NULL);
        5855            2465 :     if (pcdepth < 0)
        5856               0 :          return FAILED_EXPRESSION_DECOMPILER;
        5857                 :
        5858            2465 :     g.printer = js_NewPrinter(cx, "js_DecompileValueGenerator", fun, 0, false, false, false);
        5859            2465 :     if (!g.printer)
        5860               0 :         return NULL;
        5861                 :
        5862            2465 :     g.printer->dvgfence = end;
        5863            2465 :     g.printer->pcstack = g.pcstack;
        5864            2465 :     if (!DecompileCode(g.printer, script, begin, (unsigned) len, (unsigned) pcdepth))
        5865               0 :         return NULL;
        5866                 :
        5867            2465 :     return JS_strdup(cx, g.printer->sprinter.string());
        5868                 : }
        5869                 :
        5870                 : unsigned
        5871         4106133 : js_ReconstructStackDepth(JSContext *cx, JSScript *script, jsbytecode *pc)
        5872                 : {
        5873         4106133 :     return ReconstructPCStack(cx, script, pc, NULL, NULL);
        5874                 : }
        5875                 :
        5876                 : #define LOCAL_ASSERT(expr)      LOCAL_ASSERT_RV(expr, -1);
        5877                 :
        5878                 : static int
        5879        35014291 : SimulateOp(JSContext *cx, JSScript *script, JSOp op, const JSCodeSpec *cs,
        5880                 :            jsbytecode *pc, jsbytecode **pcstack, unsigned &pcdepth)
        5881                 : {
        5882        35014291 :     unsigned nuses = StackUses(script, pc);
        5883        35014291 :     unsigned ndefs = StackDefs(script, pc);
        5884        35014291 :     LOCAL_ASSERT(pcdepth >= nuses);
        5885        35014291 :     pcdepth -= nuses;
        5886        35014291 :     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        35014291 :     switch (op) {
        5894                 :       default:
        5895        34537303 :         if (pcstack) {
        5896          451000 :             for (unsigned i = 0; i != ndefs; ++i)
        5897          197460 :                 pcstack[pcdepth + i] = pc;
        5898                 :         }
        5899        34537303 :         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          239891 :         JS_ASSERT(ndefs == 2);
        5908          239891 :         if (pcstack)
        5909            9776 :             pcstack[pcdepth + 1] = pcstack[pcdepth];
        5910          239891 :         break;
        5911                 :
        5912                 :       case JSOP_DUP2:
        5913           45263 :         JS_ASSERT(ndefs == 4);
        5914           45263 :         if (pcstack) {
        5915            4096 :             pcstack[pcdepth + 2] = pcstack[pcdepth];
        5916            4096 :             pcstack[pcdepth + 3] = pcstack[pcdepth + 1];
        5917                 :         }
        5918           45263 :         break;
        5919                 :
        5920                 :       case JSOP_SWAP:
        5921          190809 :         JS_ASSERT(ndefs == 2);
        5922          190809 :         if (pcstack) {
        5923            4822 :             jsbytecode *tmp = pcstack[pcdepth + 1];
        5924            4822 :             pcstack[pcdepth + 1] = pcstack[pcdepth];
        5925            4822 :             pcstack[pcdepth] = tmp;
        5926                 :         }
        5927          190809 :         break;
        5928                 :     }
        5929        35014291 :     pcdepth += ndefs;
        5930        35014291 :     return pcdepth;
        5931                 : }
        5932                 :
        5933                 : static int
        5934         4111077 : 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         4111077 :     LOCAL_ASSERT(script->code <= target && target < script->code + script->length);
        5946         4111077 :     jsbytecode *pc = script->code;
        5947         4111077 :     unsigned pcdepth = 0;
        5948                 :     ptrdiff_t oplen;
        5949        39195262 :     for (; pc < target; pc += oplen) {
        5950        35084185 :         JSOp op = JSOp(*pc);
        5951        35084185 :         const JSCodeSpec *cs = &js_CodeSpec[op];
        5952        35084185 :         oplen = cs->length;
        5953        35084185 :         if (oplen < 0)
        5954             506 :             oplen = js_GetVariableBytecodeLength(pc);
        5955                 :
        5956        35084185 :         if (cs->format & JOF_DECOMPOSE) {
        5957           46649 :             if (lastDecomposedPC)
        5958            1434 :                 *lastDecomposedPC = pc;
        5959           46649 :             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        35037536 :         jssrcnote *sn = js_GetSrcNote(script, pc);
        5970        35037536 :         if (sn && SN_TYPE(sn) == SRC_COND) {
        5971            1215 :             ptrdiff_t jmpoff = js_GetSrcNoteOffset(sn, 0);
        5972            1215 :             if (pc + jmpoff < target) {
        5973            1215 :                 pc += jmpoff;
        5974            1215 :                 op = JSOp(*pc);
        5975            1215 :                 JS_ASSERT(op == JSOP_GOTO);
        5976            1215 :                 cs = &js_CodeSpec[op];
        5977            1215 :                 oplen = cs->length;
        5978            1215 :                 JS_ASSERT(oplen > 0);
        5979            1215 :                 ptrdiff_t jmplen = GET_JUMP_OFFSET(pc);
        5980            1215 :                 if (pc + jmplen < target) {
        5981            1166 :                     oplen = (unsigned) jmplen;
        5982            1166 :                     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        35036370 :         if (sn && SN_TYPE(sn) == SRC_HIDDEN)
        5996           22079 :             continue;
        5997                 :
        5998        35014291 :         if (SimulateOp(cx, script, op, cs, pc, pcstack, pcdepth) < 0)
        5999               0 :             return -1;
        6000                 :
        6001                 :     }
        6002         4111077 :     LOCAL_ASSERT(pc == target);
        6003         4111077 :     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           59403 : } // namespace js

Generated by: LCOV version 1.7