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: | 3098 | 2053 | 66.3 %
| Date: | 2012-04-07 | Functions: | 110 | 90 | 81.8 %

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

Generated by: LCOV version 1.7