LCOV - code coverage report
Current view: top level - js/src/vm - BytecodeUtil.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 140 1187 11.8 %
Date: 2018-08-07 16:35:00 Functions: 0 0 -
Legend: Lines: hit not hit

          Line data    Source code
       1             :         /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2             :  * vim: set ts=8 sts=4 et sw=4 tw=99:
       3             :  * This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : /*
       8             :  * JS bytecode descriptors, disassemblers, and (expression) decompilers.
       9             :  */
      10             : 
      11             : #include "vm/BytecodeUtil-inl.h"
      12             : 
      13             : #define __STDC_FORMAT_MACROS
      14             : 
      15             : #include "mozilla/ArrayUtils.h"
      16             : #include "mozilla/Attributes.h"
      17             : #include "mozilla/Sprintf.h"
      18             : #include "mozilla/Vector.h"
      19             : 
      20             : #include <algorithm>
      21             : #include <ctype.h>
      22             : #include <inttypes.h>
      23             : #include <stdio.h>
      24             : #include <string.h>
      25             : 
      26             : #include "jsapi.h"
      27             : #include "jsnum.h"
      28             : #include "jstypes.h"
      29             : #include "jsutil.h"
      30             : 
      31             : #include "builtin/String.h"
      32             : #include "frontend/BytecodeCompiler.h"
      33             : #include "frontend/SourceNotes.h"
      34             : #include "gc/FreeOp.h"
      35             : #include "gc/GCInternals.h"
      36             : #include "js/CharacterEncoding.h"
      37             : #include "js/Printf.h"
      38             : #include "util/StringBuffer.h"
      39             : #include "util/Text.h"
      40             : #include "vm/CodeCoverage.h"
      41             : #include "vm/EnvironmentObject.h"
      42             : #include "vm/JSAtom.h"
      43             : #include "vm/JSContext.h"
      44             : #include "vm/JSFunction.h"
      45             : #include "vm/JSObject.h"
      46             : #include "vm/JSScript.h"
      47             : #include "vm/Opcodes.h"
      48             : #include "vm/Realm.h"
      49             : #include "vm/Shape.h"
      50             : 
      51             : #include "gc/PrivateIterators-inl.h"
      52             : #include "vm/JSContext-inl.h"
      53             : #include "vm/JSObject-inl.h"
      54             : #include "vm/JSScript-inl.h"
      55             : #include "vm/Realm-inl.h"
      56             : 
      57             : using namespace js;
      58             : using namespace js::gc;
      59             : 
      60             : using js::frontend::IsIdentifier;
      61             : 
      62             : /*
      63             :  * Index limit must stay within 32 bits.
      64             :  */
      65             : JS_STATIC_ASSERT(sizeof(uint32_t) * CHAR_BIT >= INDEX_LIMIT_LOG2 + 1);
      66             : 
      67             : const JSCodeSpec js::CodeSpec[] = {
      68             : #define MAKE_CODESPEC(op,val,name,token,length,nuses,ndefs,format)  {length,nuses,ndefs,format},
      69             :     FOR_EACH_OPCODE(MAKE_CODESPEC)
      70             : #undef MAKE_CODESPEC
      71             : };
      72             : 
      73             : const unsigned js::NumCodeSpecs = mozilla::ArrayLength(CodeSpec);
      74             : 
      75             : /*
      76             :  * Each element of the array is either a source literal associated with JS
      77             :  * bytecode or null.
      78             :  */
      79             : static const char * const CodeToken[] = {
      80             : #define TOKEN(op, val, name, token, ...)  token,
      81             :     FOR_EACH_OPCODE(TOKEN)
      82             : #undef TOKEN
      83             : };
      84             : 
      85             : /*
      86             :  * Array of JS bytecode names used by PC count JSON, DEBUG-only Disassemble
      87             :  * and JIT debug spew.
      88             :  */
      89             : const char * const js::CodeName[] = {
      90             : #define OPNAME(op, val, name, ...)  name,
      91             :     FOR_EACH_OPCODE(OPNAME)
      92             : #undef OPNAME
      93             : };
      94             : 
      95             : /************************************************************************/
      96             : 
      97             : static bool
      98             : DecompileArgumentFromStack(JSContext* cx, int formalIndex, char** res);
      99             : 
     100             : size_t
     101         580 : js::GetVariableBytecodeLength(jsbytecode* pc)
     102             : {
     103           0 :     JSOp op = JSOp(*pc);
     104           0 :     MOZ_ASSERT(CodeSpec[op].length == -1);
     105         580 :     switch (op) {
     106             :       case JSOP_TABLESWITCH: {
     107             :         /* Structure: default-jump case-low case-high case1-jump ... */
     108           0 :         pc += JUMP_OFFSET_LEN;
     109           0 :         int32_t low = GET_JUMP_OFFSET(pc);
     110           0 :         pc += JUMP_OFFSET_LEN;
     111           0 :         int32_t high = GET_JUMP_OFFSET(pc);
     112           0 :         unsigned ncases = unsigned(high - low + 1);
     113         580 :         return 1 + 3 * JUMP_OFFSET_LEN + ncases * JUMP_OFFSET_LEN;
     114             :       }
     115             :       default:
     116           0 :         MOZ_CRASH("Unexpected op");
     117             :     }
     118             : }
     119             : 
     120             : const char * PCCounts::numExecName = "interp";
     121             : 
     122             : static MOZ_MUST_USE bool
     123           0 : DumpIonScriptCounts(Sprinter* sp, HandleScript script, jit::IonScriptCounts* ionCounts)
     124             : {
     125           0 :     if (!sp->jsprintf("IonScript [%zu blocks]:\n", ionCounts->numBlocks()))
     126             :         return false;
     127             : 
     128           0 :     for (size_t i = 0; i < ionCounts->numBlocks(); i++) {
     129           0 :         const jit::IonBlockCounts& block = ionCounts->block(i);
     130           0 :         unsigned lineNumber = 0, columnNumber = 0;
     131           0 :         lineNumber = PCToLineNumber(script, script->offsetToPC(block.offset()), &columnNumber);
     132           0 :         if (!sp->jsprintf("BB #%" PRIu32 " [%05u,%u,%u]",
     133             :                           block.id(), block.offset(), lineNumber, columnNumber))
     134             :         {
     135           0 :             return false;
     136             :         }
     137           0 :         if (block.description()) {
     138           0 :             if (!sp->jsprintf(" [inlined %s]", block.description()))
     139             :                 return false;
     140             :         }
     141           0 :         for (size_t j = 0; j < block.numSuccessors(); j++) {
     142           0 :             if (!sp->jsprintf(" -> #%" PRIu32, block.successor(j)))
     143             :                 return false;
     144             :         }
     145           0 :         if (!sp->jsprintf(" :: %" PRIu64 " hits\n", block.hitCount()))
     146             :             return false;
     147           0 :         if (!sp->jsprintf("%s\n", block.code()))
     148             :             return false;
     149             :     }
     150             : 
     151             :     return true;
     152             : }
     153             : 
     154             : static MOZ_MUST_USE bool
     155           0 : DumpPCCounts(JSContext* cx, HandleScript script, Sprinter* sp)
     156             : {
     157           0 :     MOZ_ASSERT(script->hasScriptCounts());
     158             : 
     159             : #ifdef DEBUG
     160           0 :     jsbytecode* pc = script->code();
     161           0 :     while (pc < script->codeEnd()) {
     162           0 :         jsbytecode* next = GetNextPc(pc);
     163             : 
     164           0 :         if (!Disassemble1(cx, script, pc, script->pcToOffset(pc), true, sp))
     165             :             return false;
     166             : 
     167           0 :         if (!sp->put("                  {"))
     168             :             return false;
     169             : 
     170           0 :         PCCounts* counts = script->maybeGetPCCounts(pc);
     171           0 :         if (double val = counts ? counts->numExec() : 0.0) {
     172           0 :             if (!sp->jsprintf("\"%s\": %.0f", PCCounts::numExecName, val))
     173             :                 return false;
     174             :         }
     175           0 :         if (!sp->put("}\n"))
     176             :             return false;
     177             : 
     178             :         pc = next;
     179             :     }
     180             : #endif
     181             : 
     182           0 :     jit::IonScriptCounts* ionCounts = script->getIonCounts();
     183           0 :     while (ionCounts) {
     184           0 :         if (!DumpIonScriptCounts(sp, script, ionCounts))
     185             :             return false;
     186             : 
     187           0 :         ionCounts = ionCounts->previous();
     188             :     }
     189             : 
     190             :     return true;
     191             : }
     192             : 
     193             : bool
     194           0 : js::DumpCompartmentPCCounts(JSContext* cx)
     195             : {
     196           0 :     Rooted<GCVector<JSScript*>> scripts(cx, GCVector<JSScript*>(cx));
     197           0 :     for (auto iter = cx->zone()->cellIter<JSScript>(); !iter.done(); iter.next()) {
     198           0 :         JSScript* script = iter;
     199           0 :         if (script->compartment() != cx->compartment())
     200           0 :             continue;
     201           0 :         if (script->hasScriptCounts()) {
     202           0 :             if (!scripts.append(script))
     203           0 :                 return false;
     204             :         }
     205             :     }
     206             : 
     207           0 :     for (uint32_t i = 0; i < scripts.length(); i++) {
     208           0 :         HandleScript script = scripts[i];
     209           0 :         Sprinter sprinter(cx);
     210           0 :         if (!sprinter.init())
     211           0 :             return false;
     212             : 
     213           0 :         fprintf(stdout, "--- SCRIPT %s:%u ---\n", script->filename(), script->lineno());
     214           0 :         if (!DumpPCCounts(cx, script, &sprinter))
     215             :             return false;
     216           0 :         fputs(sprinter.string(), stdout);
     217           0 :         fprintf(stdout, "--- END SCRIPT %s:%u ---\n", script->filename(), script->lineno());
     218             :     }
     219             : 
     220             :     return true;
     221             : }
     222             : 
     223             : /////////////////////////////////////////////////////////////////////
     224             : // Bytecode Parser
     225             : /////////////////////////////////////////////////////////////////////
     226             : 
     227             : // Stores the information about the stack slot, where the value comes from.
     228             : // Elements of BytecodeParser::Bytecode.{offsetStack,offsetStackAfter} arrays.
     229             : struct OffsetAndDefIndex {
     230             :     // To make this struct a POD type, keep these properties public.
     231             :     // Use accessors instead of directly accessing them.
     232             : 
     233             :     // The offset of the PC that pushed the value for this slot.
     234             :     uint32_t offset_;
     235             : 
     236             :     // The index in `ndefs` for the PC (0-origin)
     237             :     uint8_t defIndex_;
     238             : 
     239             :     enum : uint8_t {
     240             :         Normal = 0,
     241             : 
     242             :         // Ignored this value in the expression decompilation.
     243             :         // Used by JSOP_NOP_DESTRUCTURING.  See BytecodeParser::simulateOp.
     244             :         Ignored,
     245             : 
     246             :         // The value in this slot comes from 2 or more paths.
     247             :         // offset_ and defIndex_ holds the information for the path that
     248             :         // reaches here first.
     249             :         Merged,
     250             :     } type_;
     251             : 
     252           0 :     uint32_t offset() const {
     253           0 :         MOZ_ASSERT(!isSpecial());
     254           3 :         return offset_;
     255             :     };
     256           0 :     uint32_t specialOffset() const {
     257           0 :         MOZ_ASSERT(isSpecial());
     258           0 :         return offset_;
     259             :     };
     260             : 
     261           0 :     uint8_t defIndex() const {
     262           0 :         MOZ_ASSERT(!isSpecial());
     263           3 :         return defIndex_;
     264             :     }
     265           0 :     uint8_t specialDefIndex() const {
     266           0 :         MOZ_ASSERT(isSpecial());
     267           0 :         return defIndex_;
     268             :     }
     269             : 
     270             :     bool isSpecial() const {
     271           6 :         return type_ != Normal;
     272             :     }
     273             :     bool isMerged() const {
     274             :         return type_ == Merged;
     275             :     }
     276             :     bool isIgnored() const {
     277             :         return type_ == Ignored;
     278             :     }
     279             : 
     280             :     void set(uint32_t aOffset, uint8_t aDefIndex) {
     281           0 :         offset_ = aOffset;
     282           0 :         defIndex_ = aDefIndex;
     283        2963 :         type_ = Normal;
     284             :     }
     285             : 
     286             :     // Keep offset_ and defIndex_ values for stack dump.
     287             :     void setMerged() {
     288         209 :         type_ = Merged;
     289             :     }
     290             :     void setIgnored() {
     291           0 :         type_ = Ignored;
     292             :     }
     293             : 
     294             :     bool operator==(const OffsetAndDefIndex& rhs) const {
     295           0 :         return offset_ == rhs.offset_ &&
     296         684 :                defIndex_ == rhs.defIndex_;
     297             :     }
     298             : 
     299             :     bool operator!=(const OffsetAndDefIndex& rhs) const {
     300         893 :         return !(*this == rhs);
     301             :     }
     302             : };
     303             : 
     304             : namespace mozilla {
     305             : 
     306             : template <>
     307             : struct IsPod<OffsetAndDefIndex> : TrueType {};
     308             : 
     309             : } // namespace mozilla
     310             : 
     311             : namespace {
     312             : 
     313          34 : class BytecodeParser
     314             : {
     315             :   public:
     316             :     enum class JumpKind {
     317             :         Simple,
     318             :         SwitchCase,
     319             :         SwitchDefault,
     320             :         TryCatch,
     321             :         TryFinally
     322             :     };
     323             : 
     324             :   private:
     325             :     class Bytecode
     326             :     {
     327             :       public:
     328             :         explicit Bytecode(const LifoAllocPolicy<Fallible>& alloc)
     329        6372 :           : parsed(false),
     330             :             stackDepth(0),
     331             :             offsetStack(nullptr)
     332             : #ifdef DEBUG
     333             :             ,
     334             :             stackDepthAfter(0),
     335             :             offsetStackAfter(nullptr),
     336       12744 :             jumpOrigins(alloc)
     337             : #endif /* DEBUG */
     338             :         {}
     339             : 
     340             :         // Whether this instruction has been analyzed to get its output defines
     341             :         // and stack.
     342             :         bool parsed : 1;
     343             : 
     344             :         // Stack depth before this opcode.
     345             :         uint32_t stackDepth;
     346             : 
     347             :         // Pointer to array of |stackDepth| offsets.  An element at position N
     348             :         // in the array is the offset of the opcode that defined the
     349             :         // corresponding stack slot.  The top of the stack is at position
     350             :         // |stackDepth - 1|.
     351             :         OffsetAndDefIndex* offsetStack;
     352             : 
     353             : #ifdef DEBUG
     354             :         // stack depth after this opcode.
     355             :         uint32_t stackDepthAfter;
     356             : 
     357             :         // Pointer to array of |stackDepthAfter| offsets.
     358             :         OffsetAndDefIndex* offsetStackAfter;
     359             : 
     360             :         struct JumpInfo {
     361             :             uint32_t from;
     362             :             JumpKind kind;
     363             : 
     364             :             JumpInfo(uint32_t from_, JumpKind kind_)
     365           0 :               : from(from_),
     366           0 :                 kind(kind_)
     367             :             {}
     368             :         };
     369             : 
     370             :         // A list of offsets of the bytecode that jumps to this bytecode,
     371             :         // exclusing previous bytecode.
     372             :         Vector<JumpInfo, 0, LifoAllocPolicy<Fallible>> jumpOrigins;
     373             : #endif /* DEBUG */
     374             : 
     375           0 :         bool captureOffsetStack(LifoAlloc& alloc, const OffsetAndDefIndex* stack, uint32_t depth) {
     376           0 :             stackDepth = depth;
     377           0 :             offsetStack = alloc.newArray<OffsetAndDefIndex>(stackDepth);
     378        6338 :             if (!offsetStack)
     379             :                 return false;
     380           0 :             if (stackDepth) {
     381           0 :                 for (uint32_t n = 0; n < stackDepth; n++)
     382       20435 :                     offsetStack[n] = stack[n];
     383             :             }
     384             :             return true;
     385             :         }
     386             : 
     387             : #ifdef DEBUG
     388           0 :         bool captureOffsetStackAfter(LifoAlloc& alloc, const OffsetAndDefIndex* stack,
     389             :                                      uint32_t depth) {
     390           0 :             stackDepthAfter = depth;
     391           0 :             offsetStackAfter = alloc.newArray<OffsetAndDefIndex>(stackDepthAfter);
     392           0 :             if (!offsetStackAfter)
     393             :                 return false;
     394           0 :             if (stackDepthAfter) {
     395           0 :                 for (uint32_t n = 0; n < stackDepthAfter; n++)
     396           0 :                     offsetStackAfter[n] = stack[n];
     397             :             }
     398             :             return true;
     399             :         }
     400             : 
     401             :         bool addJump(uint32_t from, JumpKind kind) {
     402           0 :             return jumpOrigins.append(JumpInfo(from, kind));
     403             :         }
     404             : #endif /* DEBUG */
     405             : 
     406             :         // When control-flow merges, intersect the stacks, marking slots that
     407             :         // are defined by different offsets and/or defIndices merged.
     408             :         // This is sufficient for forward control-flow.  It doesn't grok loops
     409             :         // -- for that you would have to iterate to a fixed point -- but there
     410             :         // shouldn't be operands on the stack at a loop back-edge anyway.
     411           0 :         void mergeOffsetStack(const OffsetAndDefIndex* stack, uint32_t depth) {
     412           0 :             MOZ_ASSERT(depth == stackDepth);
     413           0 :             for (uint32_t n = 0; n < stackDepth; n++) {
     414         893 :                 if (stack[n].isIgnored())
     415             :                     continue;
     416           0 :                 if (offsetStack[n].isIgnored())
     417           0 :                     offsetStack[n] = stack[n];
     418           0 :                 if (offsetStack[n] != stack[n])
     419         209 :                     offsetStack[n].setMerged();
     420             :             }
     421         301 :         }
     422             :     };
     423             : 
     424             :     JSContext* cx_;
     425             :     LifoAllocScope allocScope_;
     426             :     RootedScript script_;
     427             : 
     428             :     Bytecode** codeArray_;
     429             : 
     430             : #ifdef DEBUG
     431             :     // Dedicated mode for stack dump.
     432             :     // Capture stack after each opcode, and also enable special handling for
     433             :     // some opcodes to make stack transition clearer.
     434             :     bool isStackDump;
     435             : #endif /* DEBUG */
     436             : 
     437             :   public:
     438           0 :     BytecodeParser(JSContext* cx, JSScript* script)
     439           0 :       : cx_(cx),
     440          68 :         allocScope_(&cx->tempLifoAlloc()),
     441             :         script_(cx, script),
     442             :         codeArray_(nullptr)
     443             : #ifdef DEBUG
     444             :         ,
     445         102 :         isStackDump(false)
     446             : #endif /* DEBUG */
     447          34 :     {}
     448             : 
     449             :     bool parse();
     450             : 
     451             : #ifdef DEBUG
     452          30 :     bool isReachable(const jsbytecode* pc) { return maybeCode(pc); }
     453             : #endif /* DEBUG */
     454             : 
     455             :     uint32_t stackDepthAtPC(uint32_t offset) {
     456             :         // Sometimes the code generator in debug mode asks about the stack depth
     457             :         // of unreachable code (bug 932180 comment 22).  Assume that unreachable
     458             :         // code has no operands on the stack.
     459          34 :         return getCode(offset).stackDepth;
     460             :     }
     461           0 :     uint32_t stackDepthAtPC(const jsbytecode* pc) {
     462         102 :         return stackDepthAtPC(script_->pcToOffset(pc));
     463             :     }
     464             : 
     465             : #ifdef DEBUG
     466             :     uint32_t stackDepthAfterPC(uint32_t offset) {
     467           0 :         return getCode(offset).stackDepthAfter;
     468             :     }
     469           0 :     uint32_t stackDepthAfterPC(const jsbytecode* pc) {
     470           0 :         return stackDepthAfterPC(script_->pcToOffset(pc));
     471             :     }
     472             : #endif
     473             : 
     474           0 :     const OffsetAndDefIndex& offsetForStackOperand(uint32_t offset, int operand) {
     475           0 :         Bytecode& code = getCode(offset);
     476           0 :         if (operand < 0) {
     477           0 :             operand += code.stackDepth;
     478           1 :             MOZ_ASSERT(operand >= 0);
     479             :         }
     480           0 :         MOZ_ASSERT(uint32_t(operand) < code.stackDepth);
     481           3 :         return code.offsetStack[operand];
     482             :     }
     483           0 :     jsbytecode* pcForStackOperand(jsbytecode* pc, int operand, uint8_t* defIndex) {
     484           0 :         size_t offset = script_->pcToOffset(pc);
     485           0 :         const OffsetAndDefIndex& offsetAndDefIndex = offsetForStackOperand(offset, operand);
     486           2 :         if (offsetAndDefIndex.isSpecial())
     487             :             return nullptr;
     488           0 :         *defIndex = offsetAndDefIndex.defIndex();
     489           4 :         return script_->offsetToPC(offsetAndDefIndex.offset());
     490             :     }
     491             : 
     492             : #ifdef DEBUG
     493           0 :     const OffsetAndDefIndex& offsetForStackOperandAfterPC(uint32_t offset, int operand) {
     494           0 :         Bytecode& code = getCode(offset);
     495           0 :         if (operand < 0) {
     496           0 :             operand += code.stackDepthAfter;
     497           0 :             MOZ_ASSERT(operand >= 0);
     498             :         }
     499           0 :         MOZ_ASSERT(uint32_t(operand) < code.stackDepthAfter);
     500           0 :         return code.offsetStackAfter[operand];
     501             :     }
     502             : 
     503             :     template <typename Callback>
     504           0 :     bool forEachJumpOrigins(jsbytecode* pc, Callback callback) {
     505           0 :         Bytecode& code = getCode(script_->pcToOffset(pc));
     506             : 
     507           0 :         for (Bytecode::JumpInfo& info : code.jumpOrigins) {
     508           0 :             if (!callback(script_->offsetToPC(info.from), info.kind))
     509             :                 return false;
     510             :         }
     511             : 
     512             :         return true;
     513             :     }
     514             : 
     515             :     void setStackDump() {
     516           0 :         isStackDump = true;
     517             :     }
     518             : #endif /* DEBUG */
     519             : 
     520             :   private:
     521             :     LifoAlloc& alloc() {
     522       19150 :         return allocScope_.alloc();
     523             :     }
     524             : 
     525             :     void reportOOM() {
     526           0 :         allocScope_.releaseEarly();
     527           0 :         ReportOutOfMemory(cx_);
     528             :     }
     529             : 
     530             :     uint32_t maximumStackDepth() {
     531       19320 :         return script_->nslots() - script_->nfixed();
     532             :     }
     533             : 
     534           0 :     Bytecode& getCode(uint32_t offset) {
     535           0 :         MOZ_ASSERT(offset < script_->length());
     536           0 :         MOZ_ASSERT(codeArray_[offset]);
     537          37 :         return *codeArray_[offset];
     538             :     }
     539             : 
     540           0 :     Bytecode* maybeCode(uint32_t offset) {
     541           0 :         MOZ_ASSERT(offset < script_->length());
     542        9528 :         return codeArray_[offset];
     543             :     }
     544             : 
     545             : #ifdef DEBUG
     546          60 :     Bytecode* maybeCode(const jsbytecode* pc) { return maybeCode(script_->pcToOffset(pc)); }
     547             : #endif
     548             : 
     549             :     uint32_t simulateOp(JSOp op, uint32_t offset, OffsetAndDefIndex* offsetStack,
     550             :                         uint32_t stackDepth);
     551             : 
     552             :     inline bool recordBytecode(uint32_t offset, const OffsetAndDefIndex* offsetStack,
     553             :                                uint32_t stackDepth);
     554             : 
     555             :     inline bool addJump(uint32_t offset, uint32_t* currentOffset,
     556             :                         uint32_t stackDepth, const OffsetAndDefIndex* offsetStack,
     557             :                         jsbytecode* pc, JumpKind kind);
     558             : };
     559             : 
     560             : }  // anonymous namespace
     561             : 
     562             : uint32_t
     563        6372 : BytecodeParser::simulateOp(JSOp op, uint32_t offset, OffsetAndDefIndex* offsetStack,
     564             :                            uint32_t stackDepth)
     565             : {
     566           0 :     jsbytecode* pc = script_->offsetToPC(offset);
     567           0 :     uint32_t nuses = GetUseCount(pc);
     568        6372 :     uint32_t ndefs = GetDefCount(pc);
     569             : 
     570           0 :     MOZ_ASSERT(stackDepth >= nuses);
     571           0 :     stackDepth -= nuses;
     572       12744 :     MOZ_ASSERT(stackDepth + ndefs <= maximumStackDepth());
     573             : 
     574             : #ifdef DEBUG
     575        6372 :     if (isStackDump) {
     576             :         // Opcodes that modifies the object but keeps it on the stack while
     577             :         // initialization should be listed here instead of switch below.
     578             :         // For error message, they shouldn't be shown as the original object
     579             :         // after adding properties.
     580             :         // For stack dump, keeping the input is better.
     581           0 :         switch (op) {
     582             :           case JSOP_INITHIDDENPROP:
     583             :           case JSOP_INITHIDDENPROP_GETTER:
     584             :           case JSOP_INITHIDDENPROP_SETTER:
     585             :           case JSOP_INITLOCKEDPROP:
     586             :           case JSOP_INITPROP:
     587             :           case JSOP_INITPROP_GETTER:
     588             :           case JSOP_INITPROP_SETTER:
     589             :           case JSOP_SETFUNNAME:
     590             :             // Keep the second value.
     591           0 :             MOZ_ASSERT(nuses == 2);
     592           0 :             MOZ_ASSERT(ndefs == 1);
     593             :             goto end;
     594             : 
     595             :           case JSOP_INITELEM:
     596             :           case JSOP_INITELEM_GETTER:
     597             :           case JSOP_INITELEM_SETTER:
     598             :           case JSOP_INITHIDDENELEM:
     599             :           case JSOP_INITHIDDENELEM_GETTER:
     600             :           case JSOP_INITHIDDENELEM_SETTER:
     601             :             // Keep the third value.
     602           0 :             MOZ_ASSERT(nuses == 3);
     603           0 :             MOZ_ASSERT(ndefs == 1);
     604             :             goto end;
     605             : 
     606             :           default:
     607             :             break;
     608             :         }
     609             :     }
     610             : #endif /* DEBUG */
     611             : 
     612             :     // Mark the current offset as defining its values on the offset stack,
     613             :     // unless it just reshuffles the stack.  In that case we want to preserve
     614             :     // the opcode that generated the original value.
     615        6372 :     switch (op) {
     616             :       default:
     617           0 :         for (uint32_t n = 0; n != ndefs; ++n)
     618        5926 :             offsetStack[stackDepth + n].set(offset, n);
     619             :         break;
     620             : 
     621             :       case JSOP_NOP_DESTRUCTURING:
     622             :         // Poison the last offset to not obfuscate the error message.
     623           0 :         offsetStack[stackDepth - 1].setIgnored();
     624             :         break;
     625             : 
     626             :       case JSOP_CASE:
     627             :         // Keep the switch value.
     628           0 :         MOZ_ASSERT(ndefs == 1);
     629             :         break;
     630             : 
     631             :       case JSOP_DUP:
     632           0 :         MOZ_ASSERT(ndefs == 2);
     633           0 :         offsetStack[stackDepth + 1] = offsetStack[stackDepth];
     634         283 :         break;
     635             : 
     636             :       case JSOP_DUP2:
     637           0 :         MOZ_ASSERT(ndefs == 4);
     638           0 :         offsetStack[stackDepth + 2] = offsetStack[stackDepth];
     639           0 :         offsetStack[stackDepth + 3] = offsetStack[stackDepth + 1];
     640          18 :         break;
     641             : 
     642             :       case JSOP_DUPAT: {
     643           0 :         MOZ_ASSERT(ndefs == 1);
     644           0 :         unsigned n = GET_UINT24(pc);
     645           0 :         MOZ_ASSERT(n < stackDepth);
     646           0 :         offsetStack[stackDepth] = offsetStack[stackDepth - 1 - n];
     647         141 :         break;
     648             :       }
     649             : 
     650             :       case JSOP_SWAP: {
     651           0 :         MOZ_ASSERT(ndefs == 2);
     652           0 :         OffsetAndDefIndex tmp = offsetStack[stackDepth + 1];
     653           0 :         offsetStack[stackDepth + 1] = offsetStack[stackDepth];
     654         199 :         offsetStack[stackDepth] = tmp;
     655             :         break;
     656             :       }
     657             : 
     658             :       case JSOP_PICK: {
     659           0 :         unsigned n = GET_UINT8(pc);
     660           0 :         MOZ_ASSERT(ndefs == n + 1);
     661           0 :         uint32_t top = stackDepth + n;
     662           0 :         OffsetAndDefIndex tmp = offsetStack[stackDepth];
     663           0 :         for (uint32_t i = stackDepth; i < top; i++)
     664           0 :             offsetStack[i] = offsetStack[i + 1];
     665          88 :         offsetStack[top] = tmp;
     666             :         break;
     667             :       }
     668             : 
     669             :       case JSOP_UNPICK: {
     670           0 :         unsigned n = GET_UINT8(pc);
     671           0 :         MOZ_ASSERT(ndefs == n + 1);
     672           0 :         uint32_t top = stackDepth + n;
     673           0 :         OffsetAndDefIndex tmp = offsetStack[top];
     674           0 :         for (uint32_t i = top; i > stackDepth; i--)
     675           0 :             offsetStack[i] = offsetStack[i - 1];
     676          18 :         offsetStack[stackDepth] = tmp;
     677             :         break;
     678             :       }
     679             : 
     680             :       case JSOP_AND:
     681             :       case JSOP_CHECKISOBJ:
     682             :       case JSOP_CHECKISCALLABLE:
     683             :       case JSOP_CHECKOBJCOERCIBLE:
     684             :       case JSOP_CHECKTHIS:
     685             :       case JSOP_CHECKTHISREINIT:
     686             :       case JSOP_CHECKCLASSHERITAGE:
     687             :       case JSOP_DEBUGCHECKSELFHOSTED:
     688             :       case JSOP_INITGLEXICAL:
     689             :       case JSOP_INITLEXICAL:
     690             :       case JSOP_OR:
     691             :       case JSOP_SETALIASEDVAR:
     692             :       case JSOP_SETARG:
     693             :       case JSOP_SETINTRINSIC:
     694             :       case JSOP_SETLOCAL:
     695             :       case JSOP_THROWSETALIASEDCONST:
     696             :       case JSOP_THROWSETCALLEE:
     697             :       case JSOP_THROWSETCONST:
     698             :       case JSOP_INITALIASEDLEXICAL:
     699             :       case JSOP_INITIALYIELD:
     700             :       case JSOP_ITERNEXT:
     701             :         // Keep the top value.
     702           0 :         MOZ_ASSERT(nuses == 1);
     703         511 :         MOZ_ASSERT(ndefs == 1);
     704             :         break;
     705             : 
     706             :       case JSOP_INITHOMEOBJECT:
     707             :         // Keep the top 2 values.
     708           0 :         MOZ_ASSERT(nuses == 2);
     709           0 :         MOZ_ASSERT(ndefs == 2);
     710             :         break;
     711             : 
     712             :       case JSOP_SETGNAME:
     713             :       case JSOP_SETNAME:
     714             :       case JSOP_SETPROP:
     715             :       case JSOP_STRICTSETGNAME:
     716             :       case JSOP_STRICTSETNAME:
     717             :       case JSOP_STRICTSETPROP:
     718             :         // Keep the top value, removing other 1 value.
     719           0 :         MOZ_ASSERT(nuses == 2);
     720           0 :         MOZ_ASSERT(ndefs == 1);
     721           0 :         offsetStack[stackDepth] = offsetStack[stackDepth + 1];
     722          38 :         break;
     723             : 
     724             :       case JSOP_SETPROP_SUPER:
     725             :       case JSOP_STRICTSETPROP_SUPER:
     726             :         // Keep the top value, removing other 2 values.
     727           0 :         MOZ_ASSERT(nuses == 3);
     728           0 :         MOZ_ASSERT(ndefs == 1);
     729           0 :         offsetStack[stackDepth] = offsetStack[stackDepth + 2];
     730           0 :         break;
     731             : 
     732             :       case JSOP_SETELEM_SUPER:
     733             :       case JSOP_STRICTSETELEM_SUPER:
     734             :         // Keep the top value, removing other 3 values.
     735           0 :         MOZ_ASSERT(nuses == 4);
     736           0 :         MOZ_ASSERT(ndefs == 1);
     737           0 :         offsetStack[stackDepth] = offsetStack[stackDepth + 3];
     738           0 :         break;
     739             : 
     740             :       case JSOP_ISGENCLOSING:
     741             :       case JSOP_ISNOITER:
     742             :       case JSOP_MOREITER:
     743             :       case JSOP_OPTIMIZE_SPREADCALL:
     744             :         // Keep the top value and push one more value.
     745           0 :         MOZ_ASSERT(nuses == 1);
     746           0 :         MOZ_ASSERT(ndefs == 2);
     747           0 :         offsetStack[stackDepth + 1].set(offset, 1);
     748             :         break;
     749             :     }
     750             : 
     751             : #ifdef DEBUG
     752             :   end:
     753             : #endif /* DEBUG */
     754             : 
     755           0 :     stackDepth += ndefs;
     756        6372 :     return stackDepth;
     757             : }
     758             : 
     759             : bool
     760        6639 : BytecodeParser::recordBytecode(uint32_t offset, const OffsetAndDefIndex* offsetStack,
     761             :                                uint32_t stackDepth)
     762             : {
     763       13278 :     MOZ_ASSERT(offset < script_->length());
     764             : 
     765           0 :     Bytecode*& code = codeArray_[offset];
     766           0 :     if (!code) {
     767           0 :         code = alloc().new_<Bytecode>(alloc());
     768           0 :         if (!code ||
     769        6338 :             !code->captureOffsetStack(alloc(), offsetStack, stackDepth))
     770             :         {
     771           0 :             reportOOM();
     772           0 :             return false;
     773             :         }
     774             :     } else {
     775         301 :         code->mergeOffsetStack(offsetStack, stackDepth);
     776             :     }
     777             : 
     778             :     return true;
     779             : }
     780             : 
     781             : bool
     782         514 : BytecodeParser::addJump(uint32_t offset, uint32_t* currentOffset,
     783             :                         uint32_t stackDepth, const OffsetAndDefIndex* offsetStack,
     784             :                         jsbytecode* pc, JumpKind kind)
     785             : {
     786         514 :     if (!recordBytecode(offset, offsetStack, stackDepth))
     787             :         return false;
     788             : 
     789             : #ifdef DEBUG
     790           0 :     if (isStackDump) {
     791           0 :         if (!codeArray_[offset]->addJump(script_->pcToOffset(pc), kind)) {
     792           0 :             reportOOM();
     793           0 :             return false;
     794             :         }
     795             :     }
     796             : #endif /* DEBUG */
     797             : 
     798           0 :     Bytecode*& code = codeArray_[offset];
     799         514 :     if (offset < *currentOffset && !code->parsed) {
     800             :         // Backedge in a while/for loop, whose body has not been parsed due
     801             :         // to a lack of fallthrough at the loop head. Roll back the offset
     802             :         // to analyze the body.
     803          50 :         *currentOffset = offset;
     804             :     }
     805             : 
     806             :     return true;
     807             : }
     808             : 
     809             : bool
     810          34 : BytecodeParser::parse()
     811             : {
     812          34 :     MOZ_ASSERT(!codeArray_);
     813             : 
     814           0 :     uint32_t length = script_->length();
     815         102 :     codeArray_ = alloc().newArray<Bytecode*>(length);
     816             : 
     817           0 :     if (!codeArray_) {
     818           0 :         reportOOM();
     819           0 :         return false;
     820             :     }
     821             : 
     822          68 :     mozilla::PodZero(codeArray_, length);
     823             : 
     824             :     // Fill in stack depth and definitions at initial bytecode.
     825           0 :     Bytecode* startcode = alloc().new_<Bytecode>(alloc());
     826           0 :     if (!startcode) {
     827           0 :         reportOOM();
     828           0 :         return false;
     829             :     }
     830             : 
     831             :     // Fill in stack depth and definitions at initial bytecode.
     832           0 :     OffsetAndDefIndex* offsetStack = alloc().newArray<OffsetAndDefIndex>(maximumStackDepth());
     833           0 :     if (maximumStackDepth() && !offsetStack) {
     834           0 :         reportOOM();
     835           0 :         return false;
     836             :     }
     837             : 
     838           0 :     startcode->stackDepth = 0;
     839          34 :     codeArray_[0] = startcode;
     840             : 
     841           0 :     uint32_t offset, nextOffset = 0;
     842           0 :     while (nextOffset < length) {
     843        9498 :         offset = nextOffset;
     844             : 
     845           0 :         Bytecode* code = maybeCode(offset);
     846       18996 :         jsbytecode* pc = script_->offsetToPC(offset);
     847             : 
     848           0 :         JSOp op = (JSOp)*pc;
     849        9498 :         MOZ_ASSERT(op < JSOP_LIMIT);
     850             : 
     851             :         // Immediate successor of this bytecode.
     852        9498 :         uint32_t successorOffset = offset + GetBytecodeLength(pc);
     853             : 
     854             :         // Next bytecode to analyze.  This is either the successor, or is an
     855             :         // earlier bytecode if this bytecode has a loop backedge.
     856        9498 :         nextOffset = successorOffset;
     857             : 
     858        9498 :         if (!code) {
     859             :             // Haven't found a path by which this bytecode is reachable.
     860             :             continue;
     861             :         }
     862             : 
     863             :         // On a jump target, we reload the offsetStack saved for the current
     864             :         // bytecode, as it contains either the original offset stack, or the
     865             :         // merged offset stack.
     866           0 :         if (BytecodeIsJumpTarget(op)) {
     867           0 :             for (uint32_t n = 0; n < code->stackDepth; ++n)
     868        2674 :                 offsetStack[n] = code->offsetStack[n];
     869             :         }
     870             : 
     871        6662 :         if (code->parsed) {
     872             :             // No need to reparse.
     873             :             continue;
     874             :         }
     875             : 
     876        6372 :         code->parsed = true;
     877             : 
     878        6372 :         uint32_t stackDepth = simulateOp(op, offset, offsetStack, code->stackDepth);
     879             : 
     880             : #ifdef DEBUG
     881           0 :         if (isStackDump) {
     882           0 :             if (!code->captureOffsetStackAfter(alloc(), offsetStack, stackDepth)) {
     883           0 :                 reportOOM();
     884           0 :                 return false;
     885             :             }
     886             :         }
     887             : #endif /* DEBUG */
     888             : 
     889        6372 :         switch (op) {
     890             :           case JSOP_TABLESWITCH: {
     891           0 :             uint32_t defaultOffset = offset + GET_JUMP_OFFSET(pc);
     892           0 :             jsbytecode* pc2 = pc + JUMP_OFFSET_LEN;
     893           0 :             int32_t low = GET_JUMP_OFFSET(pc2);
     894           0 :             pc2 += JUMP_OFFSET_LEN;
     895           0 :             int32_t high = GET_JUMP_OFFSET(pc2);
     896           0 :             pc2 += JUMP_OFFSET_LEN;
     897             : 
     898           0 :             if (!addJump(defaultOffset, &nextOffset, stackDepth, offsetStack,
     899             :                          pc, JumpKind::SwitchDefault))
     900             :             {
     901             :                 return false;
     902             :             }
     903             : 
     904           0 :             for (int32_t i = low; i <= high; i++) {
     905           0 :                 uint32_t targetOffset = offset + GET_JUMP_OFFSET(pc2);
     906           0 :                 if (targetOffset != offset) {
     907           0 :                     if (!addJump(targetOffset, &nextOffset, stackDepth, offsetStack,
     908             :                                  pc, JumpKind::SwitchCase))
     909             :                     {
     910             :                         return false;
     911             :                     }
     912             :                 }
     913           0 :                 pc2 += JUMP_OFFSET_LEN;
     914             :             }
     915             :             break;
     916             :           }
     917             : 
     918             :           case JSOP_TRY: {
     919             :             // Everything between a try and corresponding catch or finally is conditional.
     920             :             // Note that there is no problem with code which is skipped by a thrown
     921             :             // exception but is not caught by a later handler in the same function:
     922             :             // no more code will execute, and it does not matter what is defined.
     923           0 :             JSTryNote* tn = script_->trynotes()->vector;
     924           0 :             JSTryNote* tnlimit = tn + script_->trynotes()->length;
     925           0 :             for (; tn < tnlimit; tn++) {
     926           0 :                 uint32_t startOffset = script_->mainOffset() + tn->start;
     927           0 :                 if (startOffset == offset + 1) {
     928           0 :                     uint32_t catchOffset = startOffset + tn->length;
     929           0 :                     if (tn->kind == JSTRY_CATCH) {
     930          38 :                         if (!addJump(catchOffset, &nextOffset, stackDepth, offsetStack,
     931             :                                      pc, JumpKind::TryCatch))
     932             :                         {
     933             :                             return false;
     934             :                         }
     935           0 :                     } else if (tn->kind == JSTRY_FINALLY) {
     936           0 :                         if (!addJump(catchOffset, &nextOffset, stackDepth, offsetStack,
     937             :                                      pc, JumpKind::TryFinally))
     938             :                         {
     939             :                             return false;
     940             :                         }
     941             :                     }
     942             :                 }
     943             :             }
     944             :             break;
     945             :           }
     946             : 
     947             :           default:
     948             :             break;
     949             :         }
     950             : 
     951             :         // Check basic jump opcodes, which may or may not have a fallthrough.
     952        6372 :         if (IsJumpOpcode(op)) {
     953             :             // Case instructions do not push the lvalue back when branching.
     954           0 :             uint32_t newStackDepth = stackDepth;
     955           0 :             if (op == JSOP_CASE)
     956           0 :                 newStackDepth--;
     957             : 
     958           0 :             uint32_t targetOffset = offset + GET_JUMP_OFFSET(pc);
     959         476 :             if (!addJump(targetOffset, &nextOffset, newStackDepth, offsetStack,
     960             :                          pc, JumpKind::Simple))
     961             :                 return false;
     962             :         }
     963             : 
     964             :         // Handle any fallthrough from this opcode.
     965           0 :         if (BytecodeFallsThrough(op)) {
     966        6125 :             if (!recordBytecode(successorOffset, offsetStack, stackDepth))
     967             :                 return false;
     968             :         }
     969             :     }
     970             : 
     971             :     return true;
     972             : }
     973             : 
     974             : #ifdef DEBUG
     975             : 
     976             : bool
     977          30 : js::ReconstructStackDepth(JSContext* cx, JSScript* script, jsbytecode* pc, uint32_t* depth, bool* reachablePC)
     978             : {
     979           0 :     BytecodeParser parser(cx, script);
     980          30 :     if (!parser.parse())
     981             :         return false;
     982             : 
     983          30 :     *reachablePC = parser.isReachable(pc);
     984             : 
     985           0 :     if (*reachablePC)
     986          30 :         *depth = parser.stackDepthAtPC(pc);
     987             : 
     988             :     return true;
     989             : }
     990             : 
     991             : static unsigned
     992             : Disassemble1(JSContext* cx, HandleScript script, jsbytecode* pc,
     993             :              unsigned loc, bool lines, BytecodeParser* parser, Sprinter* sp);
     994             : 
     995             : /*
     996             :  * If pc != nullptr, include a prefix indicating whether the PC is at the
     997             :  * current line. If showAll is true, include the source note type and the
     998             :  * entry stack depth.
     999             :  */
    1000             : static MOZ_MUST_USE bool
    1001           0 : DisassembleAtPC(JSContext* cx, JSScript* scriptArg, bool lines,
    1002             :                 jsbytecode* pc, bool showAll, Sprinter* sp)
    1003             : {
    1004           0 :     RootedScript script(cx, scriptArg);
    1005           0 :     BytecodeParser parser(cx, script);
    1006           0 :     parser.setStackDump();
    1007           0 :     if (!parser.parse())
    1008             :         return false;
    1009             : 
    1010           0 :     if (showAll) {
    1011           0 :         if (!sp->jsprintf("%s:%u\n", script->filename(), unsigned(script->lineno())))
    1012             :             return false;
    1013             :     }
    1014             : 
    1015           0 :     if (pc != nullptr) {
    1016           0 :         if (!sp->put("    "))
    1017             :             return false;
    1018             :     }
    1019           0 :     if (showAll) {
    1020           0 :         if (!sp->put("sn stack "))
    1021             :             return false;
    1022             :     }
    1023           0 :     if (!sp->put("loc   "))
    1024             :         return false;
    1025           0 :     if (lines) {
    1026           0 :         if (!sp->put("line"))
    1027             :             return false;
    1028             :     }
    1029           0 :     if (!sp->put("  op\n"))
    1030             :         return false;
    1031             : 
    1032           0 :     if (pc != nullptr) {
    1033           0 :         if (!sp->put("    "))
    1034             :             return false;
    1035             :     }
    1036           0 :     if (showAll) {
    1037           0 :         if (!sp->put("-- ----- "))
    1038             :             return false;
    1039             :     }
    1040           0 :     if (!sp->put("----- "))
    1041             :         return false;
    1042           0 :     if (lines) {
    1043           0 :         if (!sp->put("----"))
    1044             :             return false;
    1045             :     }
    1046           0 :     if (!sp->put("  --\n"))
    1047             :         return false;
    1048             : 
    1049           0 :     jsbytecode* next = script->code();
    1050           0 :     jsbytecode* end = script->codeEnd();
    1051           0 :     while (next < end) {
    1052           0 :         if (next == script->main()) {
    1053           0 :             if (!sp->put("main:\n"))
    1054             :                 return false;
    1055             :         }
    1056           0 :         if (pc != nullptr) {
    1057           0 :             if (!sp->put(pc == next ? "--> " : "    "))
    1058             :                 return false;
    1059             :         }
    1060           0 :         if (showAll) {
    1061           0 :             jssrcnote* sn = GetSrcNote(cx, script, next);
    1062           0 :             if (sn) {
    1063           0 :                 MOZ_ASSERT(!SN_IS_TERMINATOR(sn));
    1064           0 :                 jssrcnote* next = SN_NEXT(sn);
    1065           0 :                 while (!SN_IS_TERMINATOR(next) && SN_DELTA(next) == 0) {
    1066           0 :                     if (!sp->jsprintf("%02u\n    ", SN_TYPE(sn)))
    1067             :                         return false;
    1068           0 :                     sn = next;
    1069           0 :                     next = SN_NEXT(sn);
    1070             :                 }
    1071           0 :                 if (!sp->jsprintf("%02u ", SN_TYPE(sn)))
    1072             :                     return false;
    1073             :             } else {
    1074           0 :                 if (!sp->put("   "))
    1075             :                     return false;
    1076             :             }
    1077           0 :             if (parser.isReachable(next)) {
    1078           0 :                 if (!sp->jsprintf("%05u ", parser.stackDepthAtPC(next)))
    1079             :                     return false;
    1080             :             } else {
    1081           0 :                 if (!sp->put("      "))
    1082             :                     return false;
    1083             :             }
    1084             :         }
    1085           0 :         unsigned len = Disassemble1(cx, script, next, script->pcToOffset(next), lines,
    1086           0 :                                     &parser, sp);
    1087           0 :         if (!len)
    1088             :             return false;
    1089             : 
    1090           0 :         next += len;
    1091             :     }
    1092             : 
    1093             :     return true;
    1094             : }
    1095             : 
    1096             : bool
    1097           0 : js::Disassemble(JSContext* cx, HandleScript script, bool lines, Sprinter* sp)
    1098             : {
    1099           0 :     return DisassembleAtPC(cx, script, lines, nullptr, false, sp);
    1100             : }
    1101             : 
    1102             : JS_FRIEND_API(bool)
    1103           0 : js::DumpPC(JSContext* cx, FILE* fp)
    1104             : {
    1105           0 :     gc::AutoSuppressGC suppressGC(cx);
    1106           0 :     Sprinter sprinter(cx);
    1107           0 :     if (!sprinter.init())
    1108             :         return false;
    1109           0 :     ScriptFrameIter iter(cx);
    1110           0 :     if (iter.done()) {
    1111           0 :         fprintf(fp, "Empty stack.\n");
    1112           0 :         return true;
    1113             :     }
    1114           0 :     RootedScript script(cx, iter.script());
    1115           0 :     bool ok = DisassembleAtPC(cx, script, true, iter.pc(), false, &sprinter);
    1116           0 :     fprintf(fp, "%s", sprinter.string());
    1117             :     return ok;
    1118             : }
    1119             : 
    1120             : JS_FRIEND_API(bool)
    1121           0 : js::DumpScript(JSContext* cx, JSScript* scriptArg, FILE* fp)
    1122             : {
    1123           0 :     gc::AutoSuppressGC suppressGC(cx);
    1124           0 :     Sprinter sprinter(cx);
    1125           0 :     if (!sprinter.init())
    1126             :         return false;
    1127           0 :     RootedScript script(cx, scriptArg);
    1128           0 :     bool ok = Disassemble(cx, script, true, &sprinter);
    1129           0 :     fprintf(fp, "%s", sprinter.string());
    1130             :     return ok;
    1131             : }
    1132             : 
    1133             : static bool
    1134           0 : ToDisassemblySource(JSContext* cx, HandleValue v, JSAutoByteString* bytes)
    1135             : {
    1136           0 :     if (v.isString()) {
    1137           0 :         Sprinter sprinter(cx);
    1138           0 :         if (!sprinter.init())
    1139             :             return false;
    1140           0 :         char* nbytes = QuoteString(&sprinter, v.toString(), '"');
    1141           0 :         if (!nbytes)
    1142             :             return false;
    1143           0 :         UniqueChars copy = JS_smprintf("%s", nbytes);
    1144           0 :         if (!copy) {
    1145           0 :             ReportOutOfMemory(cx);
    1146           0 :             return false;
    1147             :         }
    1148           0 :         bytes->initBytes(std::move(copy));
    1149           0 :         return true;
    1150             :     }
    1151             : 
    1152           0 :     if (JS::RuntimeHeapIsBusy() || !cx->isAllocAllowed()) {
    1153           0 :         UniqueChars source = JS_smprintf("<value>");
    1154           0 :         if (!source) {
    1155           0 :             ReportOutOfMemory(cx);
    1156           0 :             return false;
    1157             :         }
    1158           0 :         bytes->initBytes(std::move(source));
    1159           0 :         return true;
    1160             :     }
    1161             : 
    1162           0 :     if (v.isObject()) {
    1163           0 :         JSObject& obj = v.toObject();
    1164             : 
    1165           0 :         if (obj.is<JSFunction>()) {
    1166           0 :             RootedFunction fun(cx, &obj.as<JSFunction>());
    1167             :             JSString* str = JS_DecompileFunction(cx, fun);
    1168           0 :             if (!str)
    1169             :                 return false;
    1170           0 :             return bytes->encodeLatin1(cx, str);
    1171             :         }
    1172             : 
    1173           0 :         if (obj.is<RegExpObject>()) {
    1174           0 :             JSString* source = obj.as<RegExpObject>().toString(cx);
    1175           0 :             if (!source)
    1176             :                 return false;
    1177           0 :             return bytes->encodeLatin1(cx, source);
    1178             :         }
    1179             :     }
    1180             : 
    1181           0 :     return !!ValueToPrintableLatin1(cx, v, bytes, true);
    1182             : }
    1183             : 
    1184             : static bool
    1185           0 : ToDisassemblySource(JSContext* cx, HandleScope scope, JSAutoByteString* bytes)
    1186             : {
    1187           0 :     UniqueChars source = JS_smprintf("%s {", ScopeKindString(scope->kind()));
    1188           0 :     if (!source) {
    1189           0 :         ReportOutOfMemory(cx);
    1190           0 :         return false;
    1191             :     }
    1192             : 
    1193           0 :     for (Rooted<BindingIter> bi(cx, BindingIter(scope)); bi; bi++) {
    1194           0 :         JSAutoByteString nameBytes;
    1195           0 :         if (!AtomToPrintableString(cx, bi.name(), &nameBytes))
    1196           0 :             return false;
    1197             : 
    1198           0 :         source = JS_sprintf_append(std::move(source), "%s: ", nameBytes.ptr());
    1199           0 :         if (!source) {
    1200           0 :             ReportOutOfMemory(cx);
    1201           0 :             return false;
    1202             :         }
    1203             : 
    1204           0 :         BindingLocation loc = bi.location();
    1205           0 :         switch (loc.kind()) {
    1206             :           case BindingLocation::Kind::Global:
    1207           0 :             source = JS_sprintf_append(std::move(source), "global");
    1208           0 :             break;
    1209             : 
    1210             :           case BindingLocation::Kind::Frame:
    1211           0 :             source = JS_sprintf_append(std::move(source), "frame slot %u", loc.slot());
    1212           0 :             break;
    1213             : 
    1214             :           case BindingLocation::Kind::Environment:
    1215           0 :             source = JS_sprintf_append(std::move(source), "env slot %u", loc.slot());
    1216           0 :             break;
    1217             : 
    1218             :           case BindingLocation::Kind::Argument:
    1219           0 :             source = JS_sprintf_append(std::move(source), "arg slot %u", loc.slot());
    1220           0 :             break;
    1221             : 
    1222             :           case BindingLocation::Kind::NamedLambdaCallee:
    1223           0 :             source = JS_sprintf_append(std::move(source), "named lambda callee");
    1224           0 :             break;
    1225             : 
    1226             :           case BindingLocation::Kind::Import:
    1227           0 :             source = JS_sprintf_append(std::move(source), "import");
    1228           0 :             break;
    1229             :         }
    1230             : 
    1231           0 :         if (!source) {
    1232           0 :             ReportOutOfMemory(cx);
    1233           0 :             return false;
    1234             :         }
    1235             : 
    1236           0 :         if (!bi.isLast()) {
    1237           0 :             source = JS_sprintf_append(std::move(source), ", ");
    1238           0 :             if (!source) {
    1239           0 :                 ReportOutOfMemory(cx);
    1240           0 :                 return false;
    1241             :             }
    1242             :         }
    1243             :     }
    1244             : 
    1245           0 :     source = JS_sprintf_append(std::move(source), "}");
    1246           0 :     if (!source) {
    1247           0 :         ReportOutOfMemory(cx);
    1248           0 :         return false;
    1249             :     }
    1250             : 
    1251           0 :     bytes->initBytes(std::move(source));
    1252           0 :     return true;
    1253             : }
    1254             : 
    1255             : static bool
    1256           0 : DumpJumpOrigins(HandleScript script, jsbytecode* pc, BytecodeParser* parser, Sprinter* sp)
    1257             : {
    1258           0 :     bool called = false;
    1259           0 :     auto callback = [&script, &sp, &called](jsbytecode* pc, BytecodeParser::JumpKind kind) {
    1260           0 :         if (!called) {
    1261           0 :             called = true;
    1262           0 :             if (!sp->put("\n# "))
    1263             :                 return false;
    1264             :         } else {
    1265           0 :             if (!sp->put(", "))
    1266             :                 return false;
    1267             :         }
    1268             : 
    1269           0 :         switch (kind) {
    1270             :           case BytecodeParser::JumpKind::Simple:
    1271             :           break;
    1272             : 
    1273             :           case BytecodeParser::JumpKind::SwitchCase:
    1274           0 :           if (!sp->put("switch-case "))
    1275             :               return false;
    1276             :           break;
    1277             : 
    1278             :           case BytecodeParser::JumpKind::SwitchDefault:
    1279           0 :           if (!sp->put("switch-default "))
    1280             :               return false;
    1281             :           break;
    1282             : 
    1283             :           case BytecodeParser::JumpKind::TryCatch:
    1284           0 :           if (!sp->put("try-catch "))
    1285             :               return false;
    1286             :           break;
    1287             : 
    1288             :           case BytecodeParser::JumpKind::TryFinally:
    1289           0 :           if (!sp->put("try-finally "))
    1290             :               return false;
    1291             :           break;
    1292             :         }
    1293             : 
    1294           0 :         if (!sp->jsprintf("from %s @ %05u", CodeName[*pc], unsigned(script->pcToOffset(pc))))
    1295             :             return false;
    1296             : 
    1297           0 :         return true;
    1298           0 :     };
    1299           0 :     if (!parser->forEachJumpOrigins(pc, callback))
    1300             :         return false;
    1301           0 :     if (called) {
    1302           0 :         if (!sp->put("\n"))
    1303             :             return false;
    1304             :     }
    1305             : 
    1306             :     return true;
    1307             : }
    1308             : 
    1309             : static bool
    1310             : DecompileAtPCForStackDump(JSContext* cx, HandleScript script,
    1311             :                           const OffsetAndDefIndex& offsetAndDefIndex, Sprinter* sp);
    1312             : 
    1313             : static unsigned
    1314           0 : Disassemble1(JSContext* cx, HandleScript script, jsbytecode* pc,
    1315             :              unsigned loc, bool lines, BytecodeParser* parser, Sprinter* sp)
    1316             : {
    1317           0 :     if (parser && parser->isReachable(pc)) {
    1318           0 :         if (!DumpJumpOrigins(script, pc, parser, sp))
    1319             :             return 0;
    1320             :     }
    1321             : 
    1322           0 :     size_t before = sp->stringEnd() - sp->string();
    1323           0 :     bool stackDumped = false;
    1324           0 :     auto dumpStack = [&cx, &script, &pc, &parser, &sp, &before, &stackDumped]() {
    1325           0 :         if (!parser)
    1326             :             return true;
    1327           0 :         if (stackDumped)
    1328             :             return true;
    1329           0 :         stackDumped = true;
    1330             : 
    1331           0 :         size_t after = sp->stringEnd() - sp->string();
    1332           0 :         MOZ_ASSERT(after >= before);
    1333             : 
    1334             :         static const size_t stack_column = 40;
    1335           0 :         for (size_t i = after - before; i < stack_column - 1; i++) {
    1336           0 :             if (!sp->put(" "))
    1337             :                 return false;
    1338             :         }
    1339             : 
    1340           0 :         if (!sp->put(" # "))
    1341             :             return false;
    1342             : 
    1343           0 :         if (!parser->isReachable(pc)) {
    1344           0 :             if (!sp->put("!!! UNREACHABLE !!!"))
    1345             :                 return false;
    1346             :         } else {
    1347           0 :             uint32_t depth = parser->stackDepthAfterPC(pc);
    1348             : 
    1349           0 :             for (uint32_t i = 0; i < depth; i++) {
    1350           0 :                 if (i) {
    1351           0 :                     if (!sp->put(" "))
    1352             :                         return false;
    1353             :                 }
    1354             : 
    1355             :                 const OffsetAndDefIndex& offsetAndDefIndex
    1356           0 :                     = parser->offsetForStackOperandAfterPC(script->pcToOffset(pc), i);
    1357             :                 // This will decompile the stack for the same PC many times.
    1358             :                 // We'll avoid optimizing it since this is a testing function
    1359             :                 // and it won't be worth managing cached expression here.
    1360           0 :                 if (!DecompileAtPCForStackDump(cx, script, offsetAndDefIndex, sp))
    1361             :                     return false;
    1362             :             }
    1363             :         }
    1364             : 
    1365             :         return true;
    1366           0 :     };
    1367             : 
    1368           0 :     JSOp op = (JSOp)*pc;
    1369           0 :     if (op >= JSOP_LIMIT) {
    1370             :         char numBuf1[12], numBuf2[12];
    1371           0 :         SprintfLiteral(numBuf1, "%d", op);
    1372           0 :         SprintfLiteral(numBuf2, "%d", JSOP_LIMIT);
    1373             :         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BYTECODE_TOO_BIG,
    1374           0 :                                   numBuf1, numBuf2);
    1375             :         return 0;
    1376             :     }
    1377           0 :     const JSCodeSpec* cs = &CodeSpec[op];
    1378           0 :     ptrdiff_t len = (ptrdiff_t) cs->length;
    1379           0 :     if (!sp->jsprintf("%05u:", loc))
    1380             :         return 0;
    1381           0 :     if (lines) {
    1382           0 :         if (!sp->jsprintf("%4u", PCToLineNumber(script, pc)))
    1383             :             return 0;
    1384             :     }
    1385           0 :     if (!sp->jsprintf("  %s", CodeName[op]))
    1386             :         return 0;
    1387             : 
    1388             :     int i;
    1389           0 :     switch (JOF_TYPE(cs->format)) {
    1390             :       case JOF_BYTE:
    1391             :           // Scan the trynotes to find the associated catch block
    1392             :           // and make the try opcode look like a jump instruction
    1393             :           // with an offset. This simplifies code coverage analysis
    1394             :           // based on this disassembled output.
    1395           0 :           if (op == JSOP_TRY) {
    1396           0 :               TryNoteArray* trynotes = script->trynotes();
    1397             :               uint32_t i;
    1398           0 :               size_t mainOffset = script->mainOffset();
    1399           0 :               for(i = 0; i < trynotes->length; i++) {
    1400           0 :                   JSTryNote note = trynotes->vector[i];
    1401           0 :                   if (note.kind == JSTRY_CATCH && note.start + mainOffset == loc + 1) {
    1402           0 :                       if (!sp->jsprintf(" %u (%+d)",
    1403           0 :                                         unsigned(loc + note.length + 1),
    1404           0 :                                         int(note.length + 1)))
    1405             :                       {
    1406             :                           return 0;
    1407             :                       }
    1408             :                       break;
    1409             :                   }
    1410             :               }
    1411             :           }
    1412             :         break;
    1413             : 
    1414             :       case JOF_JUMP: {
    1415           0 :         ptrdiff_t off = GET_JUMP_OFFSET(pc);
    1416           0 :         if (!sp->jsprintf(" %u (%+d)", unsigned(loc + int(off)), int(off)))
    1417             :             return 0;
    1418             :         break;
    1419             :       }
    1420             : 
    1421             :       case JOF_SCOPE: {
    1422           0 :         RootedScope scope(cx, script->getScope(GET_UINT32_INDEX(pc)));
    1423           0 :         JSAutoByteString bytes;
    1424           0 :         if (!ToDisassemblySource(cx, scope, &bytes))
    1425           0 :             return 0;
    1426           0 :         if (!sp->jsprintf(" %s", bytes.ptr()))
    1427             :             return 0;
    1428           0 :         break;
    1429             :       }
    1430             : 
    1431             :       case JOF_ENVCOORD: {
    1432             :         RootedValue v(cx,
    1433           0 :             StringValue(EnvironmentCoordinateName(cx->caches().envCoordinateNameCache, script, pc)));
    1434           0 :         JSAutoByteString bytes;
    1435           0 :         if (!ToDisassemblySource(cx, v, &bytes))
    1436           0 :             return 0;
    1437           0 :         EnvironmentCoordinate ec(pc);
    1438           0 :         if (!sp->jsprintf(" %s (hops = %u, slot = %u)", bytes.ptr(), ec.hops(), ec.slot()))
    1439             :             return 0;
    1440           0 :         break;
    1441             :       }
    1442             : 
    1443             :       case JOF_ATOM: {
    1444           0 :         RootedValue v(cx, StringValue(script->getAtom(GET_UINT32_INDEX(pc))));
    1445           0 :         JSAutoByteString bytes;
    1446           0 :         if (!ToDisassemblySource(cx, v, &bytes))
    1447           0 :             return 0;
    1448           0 :         if (!sp->jsprintf(" %s", bytes.ptr()))
    1449             :             return 0;
    1450           0 :         break;
    1451             :       }
    1452             : 
    1453             :       case JOF_DOUBLE: {
    1454           0 :         RootedValue v(cx, script->getConst(GET_UINT32_INDEX(pc)));
    1455           0 :         JSAutoByteString bytes;
    1456           0 :         if (!ToDisassemblySource(cx, v, &bytes))
    1457           0 :             return 0;
    1458           0 :         if (!sp->jsprintf(" %s", bytes.ptr()))
    1459             :             return 0;
    1460           0 :         break;
    1461             :       }
    1462             : 
    1463             :       case JOF_OBJECT: {
    1464             :         /* Don't call obj.toSource if analysis/inference is active. */
    1465           0 :         if (script->zone()->types.activeAnalysis) {
    1466           0 :             if (!sp->jsprintf(" object"))
    1467             :                 return 0;
    1468             :             break;
    1469             :         }
    1470             : 
    1471           0 :         JSObject* obj = script->getObject(GET_UINT32_INDEX(pc));
    1472             :         {
    1473           0 :             JSAutoByteString bytes;
    1474           0 :             RootedValue v(cx, ObjectValue(*obj));
    1475           0 :             if (!ToDisassemblySource(cx, v, &bytes))
    1476           0 :                 return 0;
    1477           0 :             if (!sp->jsprintf(" %s", bytes.ptr()))
    1478             :                 return 0;
    1479             :         }
    1480             :         break;
    1481             :       }
    1482             : 
    1483             :       case JOF_REGEXP: {
    1484           0 :         js::RegExpObject* obj = script->getRegExp(pc);
    1485           0 :         JSAutoByteString bytes;
    1486           0 :         RootedValue v(cx, ObjectValue(*obj));
    1487           0 :         if (!ToDisassemblySource(cx, v, &bytes))
    1488           0 :             return 0;
    1489           0 :         if (!sp->jsprintf(" %s", bytes.ptr()))
    1490             :             return 0;
    1491           0 :         break;
    1492             :       }
    1493             : 
    1494             :       case JOF_TABLESWITCH:
    1495             :       {
    1496             :         int32_t i, low, high;
    1497             : 
    1498           0 :         ptrdiff_t off = GET_JUMP_OFFSET(pc);
    1499           0 :         jsbytecode* pc2 = pc + JUMP_OFFSET_LEN;
    1500           0 :         low = GET_JUMP_OFFSET(pc2);
    1501           0 :         pc2 += JUMP_OFFSET_LEN;
    1502           0 :         high = GET_JUMP_OFFSET(pc2);
    1503           0 :         pc2 += JUMP_OFFSET_LEN;
    1504           0 :         if (!sp->jsprintf(" defaultOffset %d low %d high %d", int(off), low, high))
    1505             :             return 0;
    1506             : 
    1507             :         // Display stack dump before diplaying the offsets for each case.
    1508           0 :         if (!dumpStack())
    1509             :             return 0;
    1510             : 
    1511           0 :         for (i = low; i <= high; i++) {
    1512           0 :             off = GET_JUMP_OFFSET(pc2);
    1513           0 :             if (!sp->jsprintf("\n\t%d: %d", i, int(off)))
    1514             :                 return 0;
    1515           0 :             pc2 += JUMP_OFFSET_LEN;
    1516             :         }
    1517           0 :         len = 1 + pc2 - pc;
    1518           0 :         break;
    1519             :       }
    1520             : 
    1521             :       case JOF_QARG:
    1522           0 :         if (!sp->jsprintf(" %u", GET_ARGNO(pc)))
    1523             :             return 0;
    1524             :         break;
    1525             : 
    1526             :       case JOF_LOCAL:
    1527           0 :         if (!sp->jsprintf(" %u", GET_LOCALNO(pc)))
    1528             :             return 0;
    1529             :         break;
    1530             : 
    1531             :       case JOF_UINT32:
    1532           0 :         if (!sp->jsprintf(" %u", GET_UINT32(pc)))
    1533             :             return 0;
    1534             :         break;
    1535             : 
    1536             :       case JOF_UINT16:
    1537           0 :         i = (int)GET_UINT16(pc);
    1538           0 :         goto print_int;
    1539             : 
    1540             :       case JOF_UINT24:
    1541           0 :         MOZ_ASSERT(len == 4);
    1542           0 :         i = (int)GET_UINT24(pc);
    1543           0 :         goto print_int;
    1544             : 
    1545             :       case JOF_UINT8:
    1546           0 :         i = GET_UINT8(pc);
    1547           0 :         goto print_int;
    1548             : 
    1549             :       case JOF_INT8:
    1550           0 :         i = GET_INT8(pc);
    1551           0 :         goto print_int;
    1552             : 
    1553             :       case JOF_INT32:
    1554           0 :         MOZ_ASSERT(op == JSOP_INT32);
    1555           0 :         i = GET_INT32(pc);
    1556             :       print_int:
    1557           0 :         if (!sp->jsprintf(" %d", i))
    1558             :             return 0;
    1559             :         break;
    1560             : 
    1561             :       default: {
    1562             :         char numBuf[12];
    1563           0 :         SprintfLiteral(numBuf, "%x", cs->format);
    1564           0 :         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_UNKNOWN_FORMAT, numBuf);
    1565             :         return 0;
    1566             :       }
    1567             :     }
    1568             : 
    1569           0 :     if (!dumpStack())
    1570             :         return 0;
    1571             : 
    1572           0 :     if (!sp->put("\n"))
    1573             :         return 0;
    1574           0 :     return len;
    1575             : }
    1576             : 
    1577             : unsigned
    1578           0 : js::Disassemble1(JSContext* cx, JS::Handle<JSScript*> script, jsbytecode* pc, unsigned loc,
    1579             :                  bool lines, Sprinter* sp)
    1580             : {
    1581           0 :     return Disassemble1(cx, script, pc, loc, lines, nullptr, sp);
    1582             : }
    1583             : 
    1584             : #endif /* DEBUG */
    1585             : 
    1586             : namespace {
    1587             : /*
    1588             :  * The expression decompiler is invoked by error handling code to produce a
    1589             :  * string representation of the erroring expression. As it's only a debugging
    1590             :  * tool, it only supports basic expressions. For anything complicated, it simply
    1591             :  * puts "(intermediate value)" into the error result.
    1592             :  *
    1593             :  * Here's the basic algorithm:
    1594             :  *
    1595             :  * 1. Find the stack location of the value whose expression we wish to
    1596             :  * decompile. The error handler can explicitly pass this as an
    1597             :  * argument. Otherwise, we search backwards down the stack for the offending
    1598             :  * value.
    1599             :  *
    1600             :  * 2. Instantiate and run a BytecodeParser for the current frame. This creates a
    1601             :  * stack of pcs parallel to the interpreter stack; given an interpreter stack
    1602             :  * location, the corresponding pc stack location contains the opcode that pushed
    1603             :  * the value in the interpreter. Now, with the result of step 1, we have the
    1604             :  * opcode responsible for pushing the value we want to decompile.
    1605             :  *
    1606             :  * 3. Pass the opcode to decompilePC. decompilePC is the main decompiler
    1607             :  * routine, responsible for a string representation of the expression that
    1608             :  * generated a certain stack location. decompilePC looks at one opcode and
    1609             :  * returns the JS source equivalent of that opcode.
    1610             :  *
    1611             :  * 4. Expressions can, of course, contain subexpressions. For example, the
    1612             :  * literals "4" and "5" are subexpressions of the addition operator in "4 +
    1613             :  * 5". If we need to decompile a subexpression, we call decompilePC (step 2)
    1614             :  * recursively on the operands' pcs. The result is a depth-first traversal of
    1615             :  * the expression tree.
    1616             :  *
    1617             :  */
    1618           4 : struct ExpressionDecompiler
    1619             : {
    1620             :     JSContext* cx;
    1621             :     RootedScript script;
    1622             :     BytecodeParser parser;
    1623             :     Sprinter sprinter;
    1624             : 
    1625             : #ifdef DEBUG
    1626             :     // Dedicated mode for stack dump.
    1627             :     // Generates an expression for stack dump, including internal state,
    1628             :     // and also disables special handling for self-hosted code.
    1629             :     bool isStackDump;
    1630             : #endif /* DEBUG */
    1631             : 
    1632           0 :     ExpressionDecompiler(JSContext* cx, JSScript* script)
    1633           2 :         : cx(cx),
    1634             :           script(cx, script),
    1635             :           parser(cx, script),
    1636             :           sprinter(cx)
    1637             : #ifdef DEBUG
    1638             :           ,
    1639           2 :           isStackDump(false)
    1640             : #endif /* DEBUG */
    1641           2 :     {}
    1642             :     bool init();
    1643             :     bool decompilePCForStackOperand(jsbytecode* pc, int i);
    1644             :     bool decompilePC(jsbytecode* pc, uint8_t defIndex);
    1645             :     bool decompilePC(const OffsetAndDefIndex& offsetAndDefIndex);
    1646             :     JSAtom* getArg(unsigned slot);
    1647             :     JSAtom* loadAtom(jsbytecode* pc);
    1648             :     bool quote(JSString* s, uint32_t quote);
    1649             :     bool write(const char* s);
    1650             :     bool write(JSString* str);
    1651             :     bool getOutput(char** out);
    1652             : #ifdef DEBUG
    1653             :     void setStackDump() {
    1654           0 :         isStackDump = true;
    1655           0 :         parser.setStackDump();
    1656             :     }
    1657             : #endif /* DEBUG */
    1658             : };
    1659             : 
    1660             : bool
    1661           1 : ExpressionDecompiler::decompilePCForStackOperand(jsbytecode* pc, int i)
    1662             : {
    1663           2 :     return decompilePC(parser.offsetForStackOperand(script->pcToOffset(pc), i));
    1664             : }
    1665             : 
    1666             : bool
    1667           3 : ExpressionDecompiler::decompilePC(jsbytecode* pc, uint8_t defIndex)
    1668             : {
    1669           6 :     MOZ_ASSERT(script->containsPC(pc));
    1670             : 
    1671           3 :     JSOp op = (JSOp)*pc;
    1672             : 
    1673           0 :     if (const char* token = CodeToken[op]) {
    1674           0 :         MOZ_ASSERT(defIndex == 0);
    1675           0 :         MOZ_ASSERT(CodeSpec[op].ndefs == 1);
    1676             : 
    1677             :         // Handle simple cases of binary and unary operators.
    1678           0 :         switch (CodeSpec[op].nuses) {
    1679             :           case 2: {
    1680           0 :             jssrcnote* sn = GetSrcNote(cx, script, pc);
    1681           0 :             if (!sn || SN_TYPE(sn) != SRC_ASSIGNOP)
    1682           0 :                 return write("(") &&
    1683           0 :                        decompilePCForStackOperand(pc, -2) &&
    1684           0 :                        write(" ") &&
    1685           0 :                        write(token) &&
    1686           0 :                        write(" ") &&
    1687           0 :                        decompilePCForStackOperand(pc, -1) &&
    1688           0 :                        write(")");
    1689             :             break;
    1690             :           }
    1691             :           case 1:
    1692           0 :             return write("(") &&
    1693           0 :                    write(token) &&
    1694           0 :                    decompilePCForStackOperand(pc, -1) &&
    1695           0 :                    write(")");
    1696             :           default:
    1697             :             break;
    1698             :         }
    1699             :     }
    1700             : 
    1701           3 :     switch (op) {
    1702             :       case JSOP_DELNAME:
    1703           0 :         return write("(delete ") &&
    1704           0 :                write(loadAtom(pc)) &&
    1705           0 :                write(")");
    1706             : 
    1707             :       case JSOP_GETGNAME:
    1708             :       case JSOP_GETNAME:
    1709             :       case JSOP_GETINTRINSIC:
    1710           1 :         return write(loadAtom(pc));
    1711             :       case JSOP_GETARG: {
    1712           1 :         unsigned slot = GET_ARGNO(pc);
    1713             : 
    1714             :         // For self-hosted scripts that are called from non-self-hosted code,
    1715             :         // decompiling the parameter name in the self-hosted script is
    1716             :         // unhelpful. Decompile the argument name instead.
    1717           4 :         if (script->selfHosted()
    1718             : #ifdef DEBUG
    1719             :             // For stack dump, argument name is not necessary.
    1720           1 :             && !isStackDump
    1721             : #endif /* DEBUG */
    1722             :             )
    1723             :         {
    1724             :             char* result;
    1725           0 :             if (!DecompileArgumentFromStack(cx, slot, &result))
    1726           0 :                 return false;
    1727             : 
    1728             :             // Note that decompiling the argument in the parent frame might
    1729             :             // not succeed.
    1730           0 :             if (result) {
    1731           0 :                 bool ok = write(result);
    1732           0 :                 js_free(result);
    1733           0 :                 return ok;
    1734             :             }
    1735             :         }
    1736             : 
    1737           0 :         JSAtom* atom = getArg(slot);
    1738           1 :         if (!atom)
    1739             :             return false;
    1740           1 :         return write(atom);
    1741             :       }
    1742             :       case JSOP_GETLOCAL: {
    1743           0 :         JSAtom* atom = FrameSlotName(script, pc);
    1744           0 :         MOZ_ASSERT(atom);
    1745           0 :         return write(atom);
    1746             :       }
    1747             :       case JSOP_GETALIASEDVAR: {
    1748           0 :         JSAtom* atom = EnvironmentCoordinateName(cx->caches().envCoordinateNameCache, script, pc);
    1749           0 :         MOZ_ASSERT(atom);
    1750           0 :         return write(atom);
    1751             :       }
    1752             : 
    1753             :       case JSOP_DELPROP:
    1754             :       case JSOP_STRICTDELPROP:
    1755             :       case JSOP_LENGTH:
    1756             :       case JSOP_GETPROP:
    1757             :       case JSOP_GETBOUNDNAME:
    1758             :       case JSOP_CALLPROP: {
    1759           0 :         bool hasDelete = op == JSOP_DELPROP || op == JSOP_STRICTDELPROP;
    1760           0 :         RootedAtom prop(cx, (op == JSOP_LENGTH) ? cx->names().length : loadAtom(pc));
    1761           0 :         MOZ_ASSERT(prop);
    1762           0 :         return (hasDelete ? write("(delete ") : true) &&
    1763           0 :                decompilePCForStackOperand(pc, -1) &&
    1764           0 :                (IsIdentifier(prop)
    1765           0 :                 ? write(".") && quote(prop, '\0')
    1766           0 :                 : write("[") && quote(prop, '\'') && write("]")) &&
    1767           0 :                (hasDelete ? write(")") : true);
    1768             :       }
    1769             :       case JSOP_GETPROP_SUPER:
    1770             :       {
    1771           0 :         RootedAtom prop(cx, loadAtom(pc));
    1772           0 :         return write("super.") &&
    1773           0 :                quote(prop, '\0');
    1774             :       }
    1775             :       case JSOP_SETELEM:
    1776             :       case JSOP_STRICTSETELEM:
    1777             :         // NOTE: We don't show the right hand side of the operation because
    1778             :         // it's used in error messages like: "a[0] is not readable".
    1779             :         //
    1780             :         // We could though.
    1781           0 :         return decompilePCForStackOperand(pc, -3) &&
    1782           0 :                write("[") &&
    1783           0 :                decompilePCForStackOperand(pc, -2) &&
    1784           0 :                write("]");
    1785             : 
    1786             :       case JSOP_DELELEM:
    1787             :       case JSOP_STRICTDELELEM:
    1788             :       case JSOP_GETELEM:
    1789             :       case JSOP_CALLELEM: {
    1790           0 :         bool hasDelete = (op == JSOP_DELELEM || op == JSOP_STRICTDELELEM);
    1791           0 :         return (hasDelete ? write("(delete ") : true) &&
    1792           0 :                decompilePCForStackOperand(pc, -2) &&
    1793           0 :                write("[") &&
    1794           0 :                decompilePCForStackOperand(pc, -1) &&
    1795           0 :                write("]") &&
    1796           0 :                (hasDelete ? write(")") : true);
    1797             :       }
    1798             : 
    1799             :       case JSOP_GETELEM_SUPER:
    1800           0 :         return write("super[") &&
    1801           0 :                decompilePCForStackOperand(pc, -3) &&
    1802           0 :                write("]");
    1803             :       case JSOP_NULL:
    1804           0 :         return write(js_null_str);
    1805             :       case JSOP_TRUE:
    1806           0 :         return write(js_true_str);
    1807             :       case JSOP_FALSE:
    1808           0 :         return write(js_false_str);
    1809             :       case JSOP_ZERO:
    1810             :       case JSOP_ONE:
    1811             :       case JSOP_INT8:
    1812             :       case JSOP_UINT16:
    1813             :       case JSOP_UINT24:
    1814             :       case JSOP_INT32:
    1815           0 :         return sprinter.printf("%d", GetBytecodeInteger(pc));
    1816             :       case JSOP_STRING:
    1817           0 :         return quote(loadAtom(pc), '"');
    1818             :       case JSOP_SYMBOL: {
    1819           0 :         unsigned i = uint8_t(pc[1]);
    1820           0 :         MOZ_ASSERT(i < JS::WellKnownSymbolLimit);
    1821           0 :         if (i < JS::WellKnownSymbolLimit)
    1822           0 :             return write(cx->names().wellKnownSymbolDescriptions()[i]);
    1823             :         break;
    1824             :       }
    1825             :       case JSOP_UNDEFINED:
    1826           0 :         return write(js_undefined_str);
    1827             :       case JSOP_GLOBALTHIS:
    1828             :         // |this| could convert to a very long object initialiser, so cite it by
    1829             :         // its keyword name.
    1830           0 :         return write(js_this_str);
    1831             :       case JSOP_NEWTARGET:
    1832           0 :         return write("new.target");
    1833             :       case JSOP_CALL:
    1834             :       case JSOP_CALL_IGNORES_RV:
    1835             :       case JSOP_CALLITER:
    1836             :       case JSOP_FUNCALL:
    1837             :       case JSOP_FUNAPPLY:
    1838           0 :         return decompilePCForStackOperand(pc, -int32_t(GET_ARGC(pc) + 2)) &&
    1839           0 :                write("(...)");
    1840             :       case JSOP_SPREADCALL:
    1841           0 :         return decompilePCForStackOperand(pc, -3) &&
    1842           0 :                write("(...)");
    1843             :       case JSOP_NEWARRAY:
    1844           0 :         return write("[]");
    1845             :       case JSOP_REGEXP: {
    1846           0 :         RootedObject obj(cx, script->getObject(GET_UINT32_INDEX(pc)));
    1847           0 :         JSString* str = obj->as<RegExpObject>().toString(cx);
    1848           0 :         if (!str)
    1849             :             return false;
    1850           0 :         return write(str);
    1851             :       }
    1852             :       case JSOP_NEWARRAY_COPYONWRITE: {
    1853           0 :         RootedObject obj(cx, script->getObject(GET_UINT32_INDEX(pc)));
    1854           0 :         Handle<ArrayObject*> aobj = obj.as<ArrayObject>();
    1855           0 :         if (!write("["))
    1856             :             return false;
    1857           0 :         for (size_t i = 0; i < aobj->getDenseInitializedLength(); i++) {
    1858           0 :             if (i > 0 && !write(", "))
    1859           0 :                 return false;
    1860             : 
    1861           0 :             RootedValue v(cx, aobj->getDenseElement(i));
    1862           0 :             MOZ_RELEASE_ASSERT(v.isPrimitive() && !v.isMagic());
    1863             : 
    1864           0 :             JSString* str = ValueToSource(cx, v);
    1865           0 :             if (!str || !write(str))
    1866           0 :                 return false;
    1867             :         }
    1868           0 :         return write("]");
    1869             :       }
    1870             :       case JSOP_OBJECT: {
    1871           0 :         JSObject* obj = script->getObject(GET_UINT32_INDEX(pc));
    1872           0 :         RootedValue objv(cx, ObjectValue(*obj));
    1873           0 :         JSString* str = ValueToSource(cx, objv);
    1874           0 :         if (!str)
    1875             :             return false;
    1876           0 :         return write(str);
    1877             :       }
    1878             :       case JSOP_VOID:
    1879           0 :         return write("(void ") &&
    1880           0 :                decompilePCForStackOperand(pc, -1) &&
    1881           0 :                write(")");
    1882             : 
    1883             :       case JSOP_SUPERCALL:
    1884             :       case JSOP_SPREADSUPERCALL:
    1885           0 :         return write("super(...)");
    1886             :       case JSOP_SUPERFUN:
    1887           0 :         return write("super");
    1888             : 
    1889             :       case JSOP_EVAL:
    1890             :       case JSOP_SPREADEVAL:
    1891             :       case JSOP_STRICTEVAL:
    1892             :       case JSOP_STRICTSPREADEVAL:
    1893           0 :         return write("eval(...)");
    1894             : 
    1895             :       case JSOP_NEW:
    1896           0 :         return write("(new ") &&
    1897           0 :                decompilePCForStackOperand(pc, -int32_t(GET_ARGC(pc) + 3)) &&
    1898           0 :                write("(...))");
    1899             : 
    1900             :       case JSOP_SPREADNEW:
    1901           0 :         return write("(new ") &&
    1902           0 :                decompilePCForStackOperand(pc, -4) &&
    1903           0 :                write("(...))");
    1904             : 
    1905             :       case JSOP_TYPEOF:
    1906             :       case JSOP_TYPEOFEXPR:
    1907           0 :         return write("(typeof ") &&
    1908           0 :                decompilePCForStackOperand(pc, -1) &&
    1909           0 :                write(")");
    1910             : 
    1911             :       case JSOP_INITELEM_ARRAY:
    1912           0 :         return write("[...]");
    1913             : 
    1914             :       case JSOP_INITELEM_INC:
    1915           0 :         if (defIndex == 0)
    1916           0 :             return write("[...]");
    1917           0 :         MOZ_ASSERT(defIndex == 1);
    1918             : #ifdef DEBUG
    1919             :         // INDEX won't be be exposed to error message.
    1920           0 :         if (isStackDump)
    1921           0 :             return write("INDEX");
    1922             : #endif
    1923             :         break;
    1924             : 
    1925             :       default:
    1926             :         break;
    1927             :     }
    1928             : 
    1929             : #ifdef DEBUG
    1930           0 :     if (isStackDump) {
    1931             :         // Special decompilation for stack dump.
    1932           0 :         switch (op) {
    1933             :           case JSOP_ARGUMENTS:
    1934           0 :             return write("arguments");
    1935             : 
    1936             :           case JSOP_BINDGNAME:
    1937           0 :             return write("GLOBAL");
    1938             : 
    1939             :           case JSOP_BINDNAME:
    1940             :           case JSOP_BINDVAR:
    1941           0 :             return write("ENV");
    1942             : 
    1943             :           case JSOP_CALLEE:
    1944           0 :             return write("CALLEE");
    1945             : 
    1946             :           case JSOP_CALLSITEOBJ:
    1947           0 :             return write("OBJ");
    1948             : 
    1949             :           case JSOP_CLASSCONSTRUCTOR:
    1950             :           case JSOP_DERIVEDCONSTRUCTOR:
    1951           0 :             return write("CONSTRUCTOR");
    1952             : 
    1953             :           case JSOP_DOUBLE:
    1954           0 :             return sprinter.printf("%lf", script->getConst(GET_UINT32_INDEX(pc)).toDouble());
    1955             : 
    1956             :           case JSOP_EXCEPTION:
    1957           0 :             return write("EXCEPTION");
    1958             : 
    1959             :           case JSOP_FINALLY:
    1960           0 :             if (defIndex == 0)
    1961           0 :                 return write("THROWING");
    1962           0 :             MOZ_ASSERT(defIndex == 1);
    1963           0 :             return write("PC");
    1964             : 
    1965             :           case JSOP_GIMPLICITTHIS:
    1966             :           case JSOP_FUNCTIONTHIS:
    1967             :           case JSOP_IMPLICITTHIS:
    1968           0 :             return write("THIS");
    1969             : 
    1970             :           case JSOP_FUNWITHPROTO:
    1971           0 :             return write("FUN");
    1972             : 
    1973             :           case JSOP_GENERATOR:
    1974           0 :             return write("GENERATOR");
    1975             : 
    1976             :           case JSOP_GETIMPORT:
    1977           0 :             return write("VAL");
    1978             : 
    1979             :           case JSOP_GETRVAL:
    1980           0 :             return write("RVAL");
    1981             : 
    1982             :           case JSOP_HOLE:
    1983           0 :             return write("HOLE");
    1984             : 
    1985             :           case JSOP_ISGENCLOSING:
    1986             :             // For stack dump, defIndex == 0 is not used.
    1987           0 :             MOZ_ASSERT(defIndex == 1);
    1988           0 :             return write("ISGENCLOSING");
    1989             : 
    1990             :           case JSOP_ISNOITER:
    1991             :             // For stack dump, defIndex == 0 is not used.
    1992           0 :             MOZ_ASSERT(defIndex == 1);
    1993           0 :             return write("ISNOITER");
    1994             : 
    1995             :           case JSOP_IS_CONSTRUCTING:
    1996           0 :             return write("JS_IS_CONSTRUCTING");
    1997             : 
    1998             :           case JSOP_ITER:
    1999           0 :             return write("ITER");
    2000             : 
    2001             :           case JSOP_LAMBDA:
    2002             :           case JSOP_LAMBDA_ARROW:
    2003             :           case JSOP_TOASYNC:
    2004             :           case JSOP_TOASYNCGEN:
    2005           0 :             return write("FUN");
    2006             : 
    2007             :           case JSOP_TOASYNCITER:
    2008           0 :             return write("ASYNCITER");
    2009             : 
    2010             :           case JSOP_MOREITER:
    2011             :             // For stack dump, defIndex == 0 is not used.
    2012           0 :             MOZ_ASSERT(defIndex == 1);
    2013           0 :             return write("MOREITER");
    2014             : 
    2015             :           case JSOP_MUTATEPROTO:
    2016           0 :             return write("SUCCEEDED");
    2017             : 
    2018             :           case JSOP_NEWINIT:
    2019             :           case JSOP_NEWOBJECT:
    2020             :           case JSOP_OBJWITHPROTO:
    2021           0 :             return write("OBJ");
    2022             : 
    2023             :           case JSOP_OPTIMIZE_SPREADCALL:
    2024             :             // For stack dump, defIndex == 0 is not used.
    2025           0 :             MOZ_ASSERT(defIndex == 1);
    2026           0 :             return write("OPTIMIZED");
    2027             : 
    2028             :           case JSOP_REST:
    2029           0 :             return write("REST");
    2030             : 
    2031             :           case JSOP_RESUME:
    2032           0 :             return write("RVAL");
    2033             : 
    2034             :           case JSOP_SUPERBASE:
    2035           0 :             return write("HOMEOBJECTPROTO");
    2036             : 
    2037             :           case JSOP_TOID:
    2038           0 :             return write("TOID(") &&
    2039           0 :                    decompilePCForStackOperand(pc, -1) &&
    2040           0 :                    write(")");
    2041             :           case JSOP_TOSTRING:
    2042           0 :             return write("TOSTRING(") &&
    2043           0 :                    decompilePCForStackOperand(pc, -1) &&
    2044           0 :                    write(")");
    2045             : 
    2046             :           case JSOP_UNINITIALIZED:
    2047           0 :             return write("UNINITIALIZED");
    2048             : 
    2049             :           case JSOP_AWAIT:
    2050             :           case JSOP_YIELD:
    2051             :             // Printing "yield SOMETHING" is confusing since the operand doesn't
    2052             :             // match to the syntax, since the stack operand for "yield 10" is
    2053             :             // the result object, not 10.
    2054           0 :             return write("RVAL");
    2055             : 
    2056             :           default:
    2057             :             break;
    2058             :         }
    2059           0 :         return write("<unknown>");
    2060             :     }
    2061             : #endif /* DEBUG */
    2062             : 
    2063           0 :     return write("(intermediate value)");
    2064             : }
    2065             : 
    2066             : bool
    2067           1 : ExpressionDecompiler::decompilePC(const OffsetAndDefIndex& offsetAndDefIndex)
    2068             : {
    2069           1 :     if (offsetAndDefIndex.isSpecial()) {
    2070             : #ifdef DEBUG
    2071           0 :         if (isStackDump) {
    2072           0 :             if (offsetAndDefIndex.isMerged()) {
    2073           0 :                 if (!write("merged<"))
    2074             :                     return false;
    2075           0 :             } else if (offsetAndDefIndex.isIgnored()) {
    2076           0 :                 if (!write("ignored<"))
    2077             :                     return false;
    2078             :             }
    2079             : 
    2080           0 :             if (!decompilePC(script->offsetToPC(offsetAndDefIndex.specialOffset()),
    2081           0 :                              offsetAndDefIndex.specialDefIndex()))
    2082             :             {
    2083             :                 return false;
    2084             :             }
    2085             : 
    2086           0 :             if (!write(">"))
    2087             :                 return false;
    2088             : 
    2089           0 :             return true;
    2090             :         }
    2091             : #endif /* DEBUG */
    2092           0 :         return write("(intermediate value)");
    2093             :     }
    2094             : 
    2095           0 :     return decompilePC(script->offsetToPC(offsetAndDefIndex.offset()),
    2096           2 :                        offsetAndDefIndex.defIndex());
    2097             : }
    2098             : 
    2099             : bool
    2100           2 : ExpressionDecompiler::init()
    2101             : {
    2102           2 :     assertSameCompartment(cx, script);
    2103             : 
    2104           2 :     if (!sprinter.init())
    2105             :         return false;
    2106             : 
    2107           2 :     if (!parser.parse())
    2108             :         return false;
    2109             : 
    2110           2 :     return true;
    2111             : }
    2112             : 
    2113             : bool
    2114             : ExpressionDecompiler::write(const char* s)
    2115             : {
    2116           2 :     return sprinter.put(s);
    2117             : }
    2118             : 
    2119             : bool
    2120           2 : ExpressionDecompiler::write(JSString* str)
    2121             : {
    2122           0 :     if (str == cx->names().dotThis)
    2123           0 :         return write("this");
    2124           2 :     return sprinter.putString(str);
    2125             : }
    2126             : 
    2127             : bool
    2128             : ExpressionDecompiler::quote(JSString* s, uint32_t quote)
    2129             : {
    2130           1 :     return QuoteString(&sprinter, s, quote) != nullptr;
    2131             : }
    2132             : 
    2133             : JSAtom*
    2134             : ExpressionDecompiler::loadAtom(jsbytecode* pc)
    2135             : {
    2136           6 :     return script->getAtom(pc);
    2137             : }
    2138             : 
    2139             : JSAtom*
    2140           1 : ExpressionDecompiler::getArg(unsigned slot)
    2141             : {
    2142           0 :     MOZ_ASSERT(script->functionNonDelazifying());
    2143           2 :     MOZ_ASSERT(slot < script->numArgs());
    2144             : 
    2145           0 :     for (PositionalFormalParameterIter fi(script); fi; fi++) {
    2146           0 :         if (fi.argumentSlot() == slot) {
    2147           0 :             if (!fi.isDestructured())
    2148           2 :                 return fi.name();
    2149             : 
    2150             :             // Destructured arguments have no single binding name.
    2151             :             static const char destructuredParam[] = "(destructured parameter)";
    2152           0 :             return Atomize(cx, destructuredParam, strlen(destructuredParam));
    2153             :         }
    2154             :     }
    2155             : 
    2156           0 :     MOZ_CRASH("No binding");
    2157             : }
    2158             : 
    2159             : bool
    2160           2 : ExpressionDecompiler::getOutput(char** res)
    2161             : {
    2162           0 :     ptrdiff_t len = sprinter.stringEnd() - sprinter.stringAt(0);
    2163           0 :     *res = cx->pod_malloc<char>(len + 1);
    2164           2 :     if (!*res)
    2165             :         return false;
    2166           0 :     js_memcpy(*res, sprinter.stringAt(0), len);
    2167           0 :     (*res)[len] = 0;
    2168           2 :     return true;
    2169             : }
    2170             : 
    2171             : }  // anonymous namespace
    2172             : 
    2173             : #ifdef DEBUG
    2174             : static bool
    2175           0 : DecompileAtPCForStackDump(JSContext* cx, HandleScript script,
    2176             :                           const OffsetAndDefIndex& offsetAndDefIndex, Sprinter* sp)
    2177             : {
    2178           0 :     ExpressionDecompiler ed(cx, script);
    2179           0 :     ed.setStackDump();
    2180           0 :     if (!ed.init())
    2181             :         return false;
    2182             : 
    2183           0 :     if (!ed.decompilePC(offsetAndDefIndex))
    2184             :         return false;
    2185             : 
    2186             :     char* result;
    2187           0 :     if (!ed.getOutput(&result))
    2188             :         return false;
    2189             : 
    2190           0 :     if (!sp->put(result))
    2191             :         return false;
    2192             : 
    2193           0 :     return true;
    2194             : }
    2195             : #endif /* DEBUG */
    2196             : 
    2197             : static bool
    2198           2 : FindStartPC(JSContext* cx, const FrameIter& iter, int spindex, int skipStackHits, const Value& v,
    2199             :             jsbytecode** valuepc, uint8_t* defIndex)
    2200             : {
    2201           0 :     jsbytecode* current = *valuepc;
    2202           0 :     *valuepc = nullptr;
    2203           2 :     *defIndex = 0;
    2204             : 
    2205           2 :     if (spindex == JSDVG_IGNORE_STACK)
    2206             :         return true;
    2207             : 
    2208             :     /*
    2209             :      * FIXME: Fall back if iter.isIon(), since the stack snapshot may be for the
    2210             :      * previous pc (see bug 831120).
    2211             :      */
    2212           2 :     if (iter.isIon())
    2213             :         return true;
    2214             : 
    2215           0 :     BytecodeParser parser(cx, iter.script());
    2216           2 :     if (!parser.parse())
    2217             :         return false;
    2218             : 
    2219           0 :     if (spindex < 0 && spindex + int(parser.stackDepthAtPC(current)) < 0)
    2220           0 :         spindex = JSDVG_SEARCH_STACK;
    2221             : 
    2222           0 :     if (spindex == JSDVG_SEARCH_STACK) {
    2223           2 :         size_t index = iter.numFrameSlots();
    2224             : 
    2225             :         // The decompiler may be called from inside functions that are not
    2226             :         // called from script, but via the C++ API directly, such as
    2227             :         // Invoke. In that case, the youngest script frame may have a
    2228             :         // completely unrelated pc and stack depth, so we give up.
    2229           2 :         if (index < size_t(parser.stackDepthAtPC(current)))
    2230             :             return true;
    2231             : 
    2232             :         // We search from fp->sp to base to find the most recently calculated
    2233             :         // value matching v under assumption that it is the value that caused
    2234             :         // the exception.
    2235             :         int stackHits = 0;
    2236             :         Value s;
    2237           0 :         do {
    2238           2 :             if (!index)
    2239             :                 return true;
    2240           0 :             s = iter.frameSlotValue(--index);
    2241           2 :         } while (s != v || stackHits++ != skipStackHits);
    2242             : 
    2243             : 
    2244             :         // If the current PC has fewer values on the stack than the index we are
    2245             :         // looking for, the blamed value must be one pushed by the current
    2246             :         // bytecode (e.g. JSOP_MOREITER), so restore *valuepc.
    2247           0 :         if (index < size_t(parser.stackDepthAtPC(current))) {
    2248           2 :             *valuepc = parser.pcForStackOperand(current, index, defIndex);
    2249             :         } else {
    2250           0 :             *valuepc = current;
    2251           0 :             *defIndex = index - size_t(parser.stackDepthAtPC(current));
    2252             :         }
    2253             :     } else {
    2254           0 :         *valuepc = parser.pcForStackOperand(current, spindex, defIndex);
    2255             :     }
    2256             :     return true;
    2257             : }
    2258             : 
    2259             : static bool
    2260           2 : DecompileExpressionFromStack(JSContext* cx, int spindex, int skipStackHits, HandleValue v, char** res)
    2261             : {
    2262           2 :     MOZ_ASSERT(spindex < 0 ||
    2263             :                spindex == JSDVG_IGNORE_STACK ||
    2264             :                spindex == JSDVG_SEARCH_STACK);
    2265             : 
    2266           2 :     *res = nullptr;
    2267             : 
    2268             : #ifdef JS_MORE_DETERMINISTIC
    2269             :     /*
    2270             :      * Give up if we need deterministic behavior for differential testing.
    2271             :      * IonMonkey doesn't use InterpreterFrames and this ensures we get the same
    2272             :      * error messages.
    2273             :      */
    2274             :     return true;
    2275             : #endif
    2276             : 
    2277           4 :     FrameIter frameIter(cx);
    2278             : 
    2279           6 :     if (frameIter.done() || !frameIter.hasScript() || frameIter.compartment() != cx->compartment())
    2280             :         return true;
    2281             : 
    2282           0 :     RootedScript script(cx, frameIter.script());
    2283           2 :     jsbytecode* valuepc = frameIter.pc();
    2284             : 
    2285           4 :     MOZ_ASSERT(script->containsPC(valuepc));
    2286             : 
    2287             :     // Give up if in prologue.
    2288           2 :     if (valuepc < script->main())
    2289             :         return true;
    2290             : 
    2291             :     uint8_t defIndex;
    2292           2 :     if (!FindStartPC(cx, frameIter, spindex, skipStackHits, v, &valuepc, &defIndex))
    2293             :         return false;
    2294           2 :     if (!valuepc)
    2295             :         return true;
    2296             : 
    2297           0 :     ExpressionDecompiler ed(cx, script);
    2298           2 :     if (!ed.init())
    2299             :         return false;
    2300           2 :     if (!ed.decompilePC(valuepc, defIndex))
    2301             :         return false;
    2302             : 
    2303           2 :     return ed.getOutput(res);
    2304             : }
    2305             : 
    2306             : UniqueChars
    2307           2 : js::DecompileValueGenerator(JSContext* cx, int spindex, HandleValue v,
    2308             :                             HandleString fallbackArg, int skipStackHits)
    2309             : {
    2310           4 :     RootedString fallback(cx, fallbackArg);
    2311             :     {
    2312             :         char* result;
    2313           0 :         if (!DecompileExpressionFromStack(cx, spindex, skipStackHits, v, &result))
    2314           0 :             return nullptr;
    2315           0 :         if (result) {
    2316           0 :             if (strcmp(result, "(intermediate value)"))
    2317           0 :                 return UniqueChars(result);
    2318           0 :             js_free(result);
    2319             :         }
    2320             :     }
    2321           0 :     if (!fallback) {
    2322           0 :         if (v.isUndefined())
    2323           0 :             return DuplicateString(cx, js_undefined_str); // Prevent users from seeing "(void 0)"
    2324           0 :         fallback = ValueToSource(cx, v);
    2325           0 :         if (!fallback)
    2326             :             return nullptr;
    2327             :     }
    2328             : 
    2329           0 :     return UniqueChars(JS_EncodeString(cx, fallback));
    2330             : }
    2331             : 
    2332             : static bool
    2333           0 : DecompileArgumentFromStack(JSContext* cx, int formalIndex, char** res)
    2334             : {
    2335           0 :     MOZ_ASSERT(formalIndex >= 0);
    2336             : 
    2337           0 :     *res = nullptr;
    2338             : 
    2339             : #ifdef JS_MORE_DETERMINISTIC
    2340             :     /* See note in DecompileExpressionFromStack. */
    2341             :     return true;
    2342             : #endif
    2343             : 
    2344             :     /*
    2345             :      * Settle on the nearest script frame, which should be the builtin that
    2346             :      * called the intrinsic.
    2347             :      */
    2348           0 :     FrameIter frameIter(cx);
    2349           0 :     MOZ_ASSERT(!frameIter.done());
    2350           0 :     MOZ_ASSERT(frameIter.script()->selfHosted());
    2351             : 
    2352             :     /*
    2353             :      * Get the second-to-top frame, the non-self-hosted caller of the builtin
    2354             :      * that called the intrinsic.
    2355             :      */
    2356           0 :     ++frameIter;
    2357           0 :     if (frameIter.done() ||
    2358           0 :         !frameIter.hasScript() ||
    2359           0 :         frameIter.script()->selfHosted() ||
    2360           0 :         frameIter.compartment() != cx->compartment())
    2361             :     {
    2362             :         return true;
    2363             :     }
    2364             : 
    2365           0 :     RootedScript script(cx, frameIter.script());
    2366           0 :     jsbytecode* current = frameIter.pc();
    2367             : 
    2368           0 :     MOZ_ASSERT(script->containsPC(current));
    2369             : 
    2370           0 :     if (current < script->main())
    2371             :         return true;
    2372             : 
    2373             :     /* Don't handle getters, setters or calls from fun.call/fun.apply. */
    2374           0 :     JSOp op = JSOp(*current);
    2375           0 :     if (op != JSOP_CALL && op != JSOP_CALL_IGNORES_RV && op != JSOP_NEW)
    2376             :         return true;
    2377             : 
    2378           0 :     if (static_cast<unsigned>(formalIndex) >= GET_ARGC(current))
    2379             :         return true;
    2380             : 
    2381           0 :     BytecodeParser parser(cx, script);
    2382           0 :     if (!parser.parse())
    2383             :         return false;
    2384             : 
    2385           0 :     bool pushedNewTarget = op == JSOP_NEW;
    2386           0 :     int formalStackIndex = parser.stackDepthAtPC(current) - GET_ARGC(current) - pushedNewTarget +
    2387           0 :                            formalIndex;
    2388           0 :     MOZ_ASSERT(formalStackIndex >= 0);
    2389           0 :     if (uint32_t(formalStackIndex) >= parser.stackDepthAtPC(current))
    2390             :         return true;
    2391             : 
    2392           0 :     ExpressionDecompiler ed(cx, script);
    2393           0 :     if (!ed.init())
    2394             :         return false;
    2395           0 :     if (!ed.decompilePCForStackOperand(current, formalStackIndex))
    2396             :         return false;
    2397             : 
    2398           0 :     return ed.getOutput(res);
    2399             : }
    2400             : 
    2401             : UniqueChars
    2402           0 : js::DecompileArgument(JSContext* cx, int formalIndex, HandleValue v)
    2403             : {
    2404             :     {
    2405             :         char* result;
    2406           0 :         if (!DecompileArgumentFromStack(cx, formalIndex, &result))
    2407           0 :             return nullptr;
    2408           0 :         if (result) {
    2409           0 :             if (strcmp(result, "(intermediate value)"))
    2410           0 :                 return UniqueChars(result);
    2411           0 :             js_free(result);
    2412             :         }
    2413             :     }
    2414           0 :     if (v.isUndefined())
    2415           0 :         return DuplicateString(cx, js_undefined_str); // Prevent users from seeing "(void 0)"
    2416             : 
    2417           0 :     RootedString fallback(cx, ValueToSource(cx, v));
    2418           0 :     if (!fallback)
    2419             :         return nullptr;
    2420             : 
    2421           0 :     return UniqueChars(JS_EncodeString(cx, fallback));
    2422             : }
    2423             : 
    2424             : bool
    2425           0 : js::CallResultEscapes(jsbytecode* pc)
    2426             : {
    2427             :     /*
    2428             :      * If we see any of these sequences, the result is unused:
    2429             :      * - call / pop
    2430             :      *
    2431             :      * If we see any of these sequences, the result is only tested for nullness:
    2432             :      * - call / ifeq
    2433             :      * - call / not / ifeq
    2434             :      */
    2435             : 
    2436           0 :     if (*pc == JSOP_CALL)
    2437           0 :         pc += JSOP_CALL_LENGTH;
    2438           0 :     else if (*pc == JSOP_CALL_IGNORES_RV)
    2439           0 :         pc += JSOP_CALL_IGNORES_RV_LENGTH;
    2440           0 :     else if (*pc == JSOP_SPREADCALL)
    2441           0 :         pc += JSOP_SPREADCALL_LENGTH;
    2442             :     else
    2443             :         return true;
    2444             : 
    2445           0 :     if (*pc == JSOP_POP)
    2446             :         return false;
    2447             : 
    2448           0 :     if (*pc == JSOP_NOT)
    2449           0 :         pc += JSOP_NOT_LENGTH;
    2450             : 
    2451           0 :     return *pc != JSOP_IFEQ;
    2452             : }
    2453             : 
    2454             : extern bool
    2455           0 : js::IsValidBytecodeOffset(JSContext* cx, JSScript* script, size_t offset)
    2456             : {
    2457             :     // This could be faster (by following jump instructions if the target is <= offset).
    2458           0 :     for (BytecodeRange r(cx, script); !r.empty(); r.popFront()) {
    2459           0 :         size_t here = r.frontOffset();
    2460           0 :         if (here >= offset)
    2461           0 :             return here == offset;
    2462             :     }
    2463           0 :     return false;
    2464             : }
    2465             : 
    2466             : /*
    2467             :  * There are three possible PCCount profiling states:
    2468             :  *
    2469             :  * 1. None: Neither scripts nor the runtime have count information.
    2470             :  * 2. Profile: Active scripts have count information, the runtime does not.
    2471             :  * 3. Query: Scripts do not have count information, the runtime does.
    2472             :  *
    2473             :  * When starting to profile scripts, counting begins immediately, with all JIT
    2474             :  * code discarded and recompiled with counts as necessary. Active interpreter
    2475             :  * frames will not begin profiling until they begin executing another script
    2476             :  * (via a call or return).
    2477             :  *
    2478             :  * The below API functions manage transitions to new states, according
    2479             :  * to the table below.
    2480             :  *
    2481             :  *                                  Old State
    2482             :  *                          -------------------------
    2483             :  * Function                 None      Profile   Query
    2484             :  * --------
    2485             :  * StartPCCountProfiling    Profile   Profile   Profile
    2486             :  * StopPCCountProfiling     None      Query     Query
    2487             :  * PurgePCCounts            None      None      None
    2488             :  */
    2489             : 
    2490             : static void
    2491           0 : ReleaseScriptCounts(FreeOp* fop)
    2492             : {
    2493           0 :     JSRuntime* rt = fop->runtime();
    2494           0 :     MOZ_ASSERT(rt->scriptAndCountsVector);
    2495             : 
    2496           0 :     fop->delete_(rt->scriptAndCountsVector.ref());
    2497           0 :     rt->scriptAndCountsVector = nullptr;
    2498           0 : }
    2499             : 
    2500             : JS_FRIEND_API(void)
    2501           0 : js::StartPCCountProfiling(JSContext* cx)
    2502             : {
    2503           0 :     JSRuntime* rt = cx->runtime();
    2504             : 
    2505           0 :     if (rt->profilingScripts)
    2506             :         return;
    2507             : 
    2508           0 :     if (rt->scriptAndCountsVector)
    2509           0 :         ReleaseScriptCounts(rt->defaultFreeOp());
    2510             : 
    2511           0 :     ReleaseAllJITCode(rt->defaultFreeOp());
    2512             : 
    2513           0 :     rt->profilingScripts = true;
    2514             : }
    2515             : 
    2516             : JS_FRIEND_API(void)
    2517           0 : js::StopPCCountProfiling(JSContext* cx)
    2518             : {
    2519           0 :     JSRuntime* rt = cx->runtime();
    2520             : 
    2521           0 :     if (!rt->profilingScripts)
    2522             :         return;
    2523           0 :     MOZ_ASSERT(!rt->scriptAndCountsVector);
    2524             : 
    2525           0 :     ReleaseAllJITCode(rt->defaultFreeOp());
    2526             : 
    2527           0 :     auto* vec = cx->new_<PersistentRooted<ScriptAndCountsVector>>(cx,
    2528           0 :         ScriptAndCountsVector(SystemAllocPolicy()));
    2529           0 :     if (!vec)
    2530             :         return;
    2531             : 
    2532           0 :     for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
    2533           0 :         for (auto script = zone->cellIter<JSScript>(); !script.done(); script.next()) {
    2534           0 :             AutoSweepTypeScript sweep(script);
    2535           0 :             if (script->hasScriptCounts() && script->types(sweep)) {
    2536           0 :                 if (!vec->append(script))
    2537           0 :                     return;
    2538             :             }
    2539             :         }
    2540             :     }
    2541             : 
    2542           0 :     rt->profilingScripts = false;
    2543           0 :     rt->scriptAndCountsVector = vec;
    2544             : }
    2545             : 
    2546             : JS_FRIEND_API(void)
    2547           0 : js::PurgePCCounts(JSContext* cx)
    2548             : {
    2549           0 :     JSRuntime* rt = cx->runtime();
    2550             : 
    2551           0 :     if (!rt->scriptAndCountsVector)
    2552             :         return;
    2553           0 :     MOZ_ASSERT(!rt->profilingScripts);
    2554             : 
    2555           0 :     ReleaseScriptCounts(rt->defaultFreeOp());
    2556             : }
    2557             : 
    2558             : JS_FRIEND_API(size_t)
    2559           0 : js::GetPCCountScriptCount(JSContext* cx)
    2560             : {
    2561           0 :     JSRuntime* rt = cx->runtime();
    2562             : 
    2563           0 :     if (!rt->scriptAndCountsVector)
    2564             :         return 0;
    2565             : 
    2566           0 :     return rt->scriptAndCountsVector->length();
    2567             : }
    2568             : 
    2569             : enum MaybeComma {NO_COMMA, COMMA};
    2570             : 
    2571             : static MOZ_MUST_USE bool
    2572           0 : AppendJSONProperty(StringBuffer& buf, const char* name, MaybeComma comma = COMMA)
    2573             : {
    2574           0 :     if (comma && !buf.append(','))
    2575             :         return false;
    2576             : 
    2577           0 :     return buf.append('\"') &&
    2578           0 :            buf.append(name, strlen(name)) &&
    2579           0 :            buf.append("\":", 2);
    2580             : }
    2581             : 
    2582             : JS_FRIEND_API(JSString*)
    2583           0 : js::GetPCCountScriptSummary(JSContext* cx, size_t index)
    2584             : {
    2585           0 :     JSRuntime* rt = cx->runtime();
    2586             : 
    2587           0 :     if (!rt->scriptAndCountsVector || index >= rt->scriptAndCountsVector->length()) {
    2588           0 :         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BUFFER_TOO_SMALL);
    2589           0 :         return nullptr;
    2590             :     }
    2591             : 
    2592           0 :     const ScriptAndCounts& sac = (*rt->scriptAndCountsVector)[index];
    2593           0 :     RootedScript script(cx, sac.script);
    2594             : 
    2595             :     /*
    2596             :      * OOM on buffer appends here will not be caught immediately, but since
    2597             :      * StringBuffer uses a TempAllocPolicy will trigger an exception on the
    2598             :      * context if they occur, which we'll catch before returning.
    2599             :      */
    2600           0 :     StringBuffer buf(cx);
    2601             : 
    2602           0 :     if (!buf.append('{'))
    2603             :         return nullptr;
    2604             : 
    2605           0 :     if (!AppendJSONProperty(buf, "file", NO_COMMA))
    2606             :         return nullptr;
    2607           0 :     JSString* str = JS_NewStringCopyZ(cx, script->filename());
    2608           0 :     if (!str || !(str = StringToSource(cx, str)))
    2609             :         return nullptr;
    2610           0 :     if (!buf.append(str))
    2611             :         return nullptr;
    2612             : 
    2613           0 :     if (!AppendJSONProperty(buf, "line"))
    2614             :         return nullptr;
    2615           0 :     if (!NumberValueToStringBuffer(cx, Int32Value(script->lineno()), buf)) {
    2616             :         return nullptr;
    2617             :     }
    2618             : 
    2619           0 :     if (script->functionNonDelazifying()) {
    2620           0 :         JSAtom* atom = script->functionNonDelazifying()->displayAtom();
    2621           0 :         if (atom) {
    2622           0 :             if (!AppendJSONProperty(buf, "name"))
    2623             :                 return nullptr;
    2624           0 :             if (!(str = StringToSource(cx, atom)))
    2625             :                 return nullptr;
    2626           0 :             if (!buf.append(str))
    2627             :                 return nullptr;
    2628             :         }
    2629             :     }
    2630             : 
    2631           0 :     uint64_t total = 0;
    2632             : 
    2633           0 :     jsbytecode* codeEnd = script->codeEnd();
    2634           0 :     for (jsbytecode* pc = script->code(); pc < codeEnd; pc = GetNextPc(pc)) {
    2635           0 :         const PCCounts* counts = sac.maybeGetPCCounts(pc);
    2636           0 :         if (!counts)
    2637             :             continue;
    2638           0 :         total += counts->numExec();
    2639             :     }
    2640             : 
    2641           0 :     if (!AppendJSONProperty(buf, "totals"))
    2642             :         return nullptr;
    2643           0 :     if (!buf.append('{'))
    2644             :         return nullptr;
    2645             : 
    2646           0 :     if (!AppendJSONProperty(buf, PCCounts::numExecName, NO_COMMA))
    2647             :         return nullptr;
    2648           0 :     if (!NumberValueToStringBuffer(cx, DoubleValue(total), buf))
    2649             :         return nullptr;
    2650             : 
    2651           0 :     uint64_t ionActivity = 0;
    2652           0 :     jit::IonScriptCounts* ionCounts = sac.getIonCounts();
    2653           0 :     while (ionCounts) {
    2654           0 :         for (size_t i = 0; i < ionCounts->numBlocks(); i++)
    2655           0 :             ionActivity += ionCounts->block(i).hitCount();
    2656           0 :         ionCounts = ionCounts->previous();
    2657             :     }
    2658           0 :     if (ionActivity) {
    2659           0 :         if (!AppendJSONProperty(buf, "ion", COMMA))
    2660             :             return nullptr;
    2661           0 :         if (!NumberValueToStringBuffer(cx, DoubleValue(ionActivity), buf))
    2662             :             return nullptr;
    2663             :     }
    2664             : 
    2665           0 :     if (!buf.append('}'))
    2666             :         return nullptr;
    2667           0 :     if (!buf.append('}'))
    2668             :         return nullptr;
    2669             : 
    2670           0 :     MOZ_ASSERT(!cx->isExceptionPending());
    2671             : 
    2672           0 :     return buf.finishString();
    2673             : }
    2674             : 
    2675             : static bool
    2676           0 : GetPCCountJSON(JSContext* cx, const ScriptAndCounts& sac, StringBuffer& buf)
    2677             : {
    2678           0 :     RootedScript script(cx, sac.script);
    2679             : 
    2680           0 :     if (!buf.append('{'))
    2681             :         return false;
    2682           0 :     if (!AppendJSONProperty(buf, "text", NO_COMMA))
    2683             :         return false;
    2684             : 
    2685           0 :     JSString* str = JS_DecompileScript(cx, script);
    2686           0 :     if (!str || !(str = StringToSource(cx, str)))
    2687             :         return false;
    2688             : 
    2689           0 :     if (!buf.append(str))
    2690             :         return false;
    2691             : 
    2692           0 :     if (!AppendJSONProperty(buf, "line"))
    2693             :         return false;
    2694           0 :     if (!NumberValueToStringBuffer(cx, Int32Value(script->lineno()), buf))
    2695             :         return false;
    2696             : 
    2697           0 :     if (!AppendJSONProperty(buf, "opcodes"))
    2698             :         return false;
    2699           0 :     if (!buf.append('['))
    2700             :         return false;
    2701           0 :     bool comma = false;
    2702             : 
    2703           0 :     SrcNoteLineScanner scanner(script->notes(), script->lineno());
    2704           0 :     uint64_t hits = 0;
    2705             : 
    2706           0 :     jsbytecode* end = script->codeEnd();
    2707           0 :     for (jsbytecode* pc = script->code(); pc < end; pc = GetNextPc(pc)) {
    2708           0 :         size_t offset = script->pcToOffset(pc);
    2709           0 :         JSOp op = JSOp(*pc);
    2710             : 
    2711             :         // If the current instruction is a jump target,
    2712             :         // then update the number of hits.
    2713           0 :         const PCCounts* counts = sac.maybeGetPCCounts(pc);
    2714           0 :         if (counts)
    2715           0 :             hits = counts->numExec();
    2716             : 
    2717           0 :         if (comma && !buf.append(','))
    2718             :             return false;
    2719           0 :         comma = true;
    2720             : 
    2721           0 :         if (!buf.append('{'))
    2722             :             return false;
    2723             : 
    2724           0 :         if (!AppendJSONProperty(buf, "id", NO_COMMA))
    2725             :             return false;
    2726           0 :         if (!NumberValueToStringBuffer(cx, Int32Value(offset), buf))
    2727             :             return false;
    2728             : 
    2729           0 :         scanner.advanceTo(offset);
    2730             : 
    2731           0 :         if (!AppendJSONProperty(buf, "line"))
    2732             :             return false;
    2733           0 :         if (!NumberValueToStringBuffer(cx, Int32Value(scanner.getLine()), buf))
    2734             :             return false;
    2735             : 
    2736             :         {
    2737           0 :             const char* name = CodeName[op];
    2738           0 :             if (!AppendJSONProperty(buf, "name"))
    2739             :                 return false;
    2740           0 :             if (!buf.append('\"'))
    2741             :                 return false;
    2742           0 :             if (!buf.append(name, strlen(name)))
    2743             :                 return false;
    2744           0 :             if (!buf.append('\"'))
    2745             :                 return false;
    2746             :         }
    2747             : 
    2748             :         {
    2749           0 :             ExpressionDecompiler ed(cx, script);
    2750           0 :             if (!ed.init())
    2751           0 :                 return false;
    2752             :             // defIndex passed here is not used.
    2753           0 :             if (!ed.decompilePC(pc, /* defIndex = */ 0))
    2754             :                 return false;
    2755             :             char* text;
    2756           0 :             if (!ed.getOutput(&text))
    2757             :                 return false;
    2758           0 :             JSString* str = JS_NewStringCopyZ(cx, text);
    2759           0 :             js_free(text);
    2760           0 :             if (!AppendJSONProperty(buf, "text"))
    2761             :                 return false;
    2762           0 :             if (!str || !(str = StringToSource(cx, str)))
    2763             :                 return false;
    2764           0 :             if (!buf.append(str))
    2765             :                 return false;
    2766             :         }
    2767             : 
    2768           0 :         if (!AppendJSONProperty(buf, "counts"))
    2769             :             return false;
    2770           0 :         if (!buf.append('{'))
    2771             :             return false;
    2772             : 
    2773           0 :         if (hits > 0) {
    2774           0 :             if (!AppendJSONProperty(buf, PCCounts::numExecName, NO_COMMA))
    2775             :                 return false;
    2776           0 :             if (!NumberValueToStringBuffer(cx, DoubleValue(hits), buf))
    2777             :                 return false;
    2778             :         }
    2779             : 
    2780           0 :         if (!buf.append('}'))
    2781             :             return false;
    2782           0 :         if (!buf.append('}'))
    2783             :             return false;
    2784             : 
    2785             :         // If the current instruction has thrown,
    2786             :         // then decrement the hit counts with the number of throws.
    2787           0 :         counts = sac.maybeGetThrowCounts(pc);
    2788           0 :         if (counts)
    2789           0 :             hits -= counts->numExec();
    2790             :     }
    2791             : 
    2792           0 :     if (!buf.append(']'))
    2793             :         return false;
    2794             : 
    2795           0 :     jit::IonScriptCounts* ionCounts = sac.getIonCounts();
    2796           0 :     if (ionCounts) {
    2797           0 :         if (!AppendJSONProperty(buf, "ion"))
    2798             :             return false;
    2799           0 :         if (!buf.append('['))
    2800             :             return false;
    2801             :         bool comma = false;
    2802           0 :         while (ionCounts) {
    2803           0 :             if (comma && !buf.append(','))
    2804             :                 return false;
    2805           0 :             comma = true;
    2806             : 
    2807           0 :             if (!buf.append('['))
    2808             :                 return false;
    2809           0 :             for (size_t i = 0; i < ionCounts->numBlocks(); i++) {
    2810           0 :                 if (i && !buf.append(','))
    2811             :                     return false;
    2812           0 :                 const jit::IonBlockCounts& block = ionCounts->block(i);
    2813             : 
    2814           0 :                 if (!buf.append('{'))
    2815             :                     return false;
    2816           0 :                 if (!AppendJSONProperty(buf, "id", NO_COMMA))
    2817             :                     return false;
    2818           0 :                 if (!NumberValueToStringBuffer(cx, Int32Value(block.id()), buf))
    2819             :                     return false;
    2820           0 :                 if (!AppendJSONProperty(buf, "offset"))
    2821             :                     return false;
    2822           0 :                 if (!NumberValueToStringBuffer(cx, Int32Value(block.offset()), buf))
    2823             :                     return false;
    2824           0 :                 if (!AppendJSONProperty(buf, "successors"))
    2825             :                     return false;
    2826           0 :                 if (!buf.append('['))
    2827             :                     return false;
    2828           0 :                 for (size_t j = 0; j < block.numSuccessors(); j++) {
    2829           0 :                     if (j && !buf.append(','))
    2830             :                         return false;
    2831           0 :                     if (!NumberValueToStringBuffer(cx, Int32Value(block.successor(j)), buf))
    2832             :                         return false;
    2833             :                 }
    2834           0 :                 if (!buf.append(']'))
    2835             :                     return false;
    2836           0 :                 if (!AppendJSONProperty(buf, "hits"))
    2837             :                     return false;
    2838           0 :                 if (!NumberValueToStringBuffer(cx, DoubleValue(block.hitCount()), buf))
    2839             :                     return false;
    2840             : 
    2841           0 :                 if (!AppendJSONProperty(buf, "code"))
    2842             :                     return false;
    2843           0 :                 JSString* str = JS_NewStringCopyZ(cx, block.code());
    2844           0 :                 if (!str || !(str = StringToSource(cx, str)))
    2845             :                     return false;
    2846           0 :                 if (!buf.append(str))
    2847             :                     return false;
    2848           0 :                 if (!buf.append('}'))
    2849             :                     return false;
    2850             :             }
    2851           0 :             if (!buf.append(']'))
    2852             :                 return false;
    2853             : 
    2854           0 :             ionCounts = ionCounts->previous();
    2855             :         }
    2856           0 :         if (!buf.append(']'))
    2857             :             return false;
    2858             :     }
    2859             : 
    2860           0 :     if (!buf.append('}'))
    2861             :         return false;
    2862             : 
    2863           0 :     MOZ_ASSERT(!cx->isExceptionPending());
    2864             :     return true;
    2865             : }
    2866             : 
    2867             : JS_FRIEND_API(JSString*)
    2868           0 : js::GetPCCountScriptContents(JSContext* cx, size_t index)
    2869             : {
    2870           0 :     JSRuntime* rt = cx->runtime();
    2871             : 
    2872           0 :     if (!rt->scriptAndCountsVector || index >= rt->scriptAndCountsVector->length()) {
    2873           0 :         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BUFFER_TOO_SMALL);
    2874           0 :         return nullptr;
    2875             :     }
    2876             : 
    2877           0 :     const ScriptAndCounts& sac = (*rt->scriptAndCountsVector)[index];
    2878           0 :     JSScript* script = sac.script;
    2879             : 
    2880           0 :     StringBuffer buf(cx);
    2881             : 
    2882             :     {
    2883           0 :         AutoRealm ar(cx, &script->global());
    2884           0 :         if (!GetPCCountJSON(cx, sac, buf))
    2885           0 :             return nullptr;
    2886             :     }
    2887             : 
    2888           0 :     return buf.finishString();
    2889             : }
    2890             : 
    2891             : static bool
    2892           0 : GenerateLcovInfo(JSContext* cx, JS::Realm* realm, GenericPrinter& out)
    2893             : {
    2894           0 :     JSRuntime* rt = cx->runtime();
    2895             : 
    2896             :     // Collect the list of scripts which are part of the current realm.
    2897             :     {
    2898           0 :         js::gc::AutoPrepareForTracing apft(cx);
    2899             :     }
    2900           0 :     Rooted<ScriptVector> topScripts(cx, ScriptVector(cx));
    2901           0 :     for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
    2902           0 :         for (auto script = zone->cellIter<JSScript>(); !script.done(); script.next()) {
    2903           0 :             if (script->realm() != realm ||
    2904           0 :                 !script->isTopLevel() ||
    2905           0 :                 !script->filename())
    2906             :             {
    2907             :                 continue;
    2908             :             }
    2909             : 
    2910           0 :             if (!topScripts.append(script))
    2911           0 :                 return false;
    2912             :         }
    2913             :     }
    2914             : 
    2915           0 :     if (topScripts.length() == 0)
    2916             :         return true;
    2917             : 
    2918             :     // Collect code coverage info for one realm.
    2919           0 :     coverage::LCovRealm realmCover;
    2920           0 :     for (JSScript* topLevel: topScripts) {
    2921           0 :         RootedScript topScript(cx, topLevel);
    2922             : 
    2923             :         // We found the top-level script, visit all the functions reachable
    2924             :         // from the top-level function, and delazify them.
    2925           0 :         Rooted<ScriptVector> queue(cx, ScriptVector(cx));
    2926           0 :         if (!queue.append(topLevel))
    2927           0 :             return false;
    2928             : 
    2929           0 :         RootedScript script(cx);
    2930           0 :         RootedFunction fun(cx);
    2931           0 :         do {
    2932           0 :             script = queue.popCopy();
    2933           0 :             if (script->filename())
    2934           0 :                 realmCover.collectCodeCoverageInfo(realm, script, script->filename());
    2935             : 
    2936             :             // Iterate from the last to the first object in order to have
    2937             :             // the functions them visited in the opposite order when popping
    2938             :             // elements from the stack of remaining scripts, such that the
    2939             :             // functions are more-less listed with increasing line numbers.
    2940           0 :             if (!script->hasObjects())
    2941             :                 continue;
    2942           0 :             size_t idx = script->objects()->length;
    2943           0 :             while (idx--) {
    2944           0 :                 JSObject* obj = script->getObject(idx);
    2945             : 
    2946             :                 // Only continue on JSFunction objects.
    2947           0 :                 if (!obj->is<JSFunction>())
    2948           0 :                     continue;
    2949           0 :                 fun = &obj->as<JSFunction>();
    2950             : 
    2951             :                 // Let's skip wasm for now.
    2952           0 :                 if (!fun->isInterpreted())
    2953             :                     continue;
    2954             : 
    2955             :                 // Queue the script in the list of script associated to the
    2956             :                 // current source.
    2957           0 :                 JSScript* childScript = JSFunction::getOrCreateScript(cx, fun);
    2958           0 :                 if (!childScript || !queue.append(childScript))
    2959           0 :                     return false;
    2960             :             }
    2961           0 :         } while (!queue.empty());
    2962             :     }
    2963             : 
    2964           0 :     bool isEmpty = true;
    2965           0 :     realmCover.exportInto(out, &isEmpty);
    2966           0 :     if (out.hadOutOfMemory())
    2967             :         return false;
    2968           0 :     return true;
    2969             : }
    2970             : 
    2971             : JS_FRIEND_API(char*)
    2972           0 : js::GetCodeCoverageSummary(JSContext* cx, size_t* length)
    2973             : {
    2974           0 :     Sprinter out(cx);
    2975             : 
    2976           0 :     if (!out.init())
    2977             :         return nullptr;
    2978             : 
    2979           0 :     if (!GenerateLcovInfo(cx, cx->realm(), out)) {
    2980           0 :         JS_ReportOutOfMemory(cx);
    2981           0 :         return nullptr;
    2982             :     }
    2983             : 
    2984           0 :     if (out.hadOutOfMemory()) {
    2985           0 :         JS_ReportOutOfMemory(cx);
    2986           0 :         return nullptr;
    2987             :     }
    2988             : 
    2989           0 :     ptrdiff_t len = out.stringEnd() - out.string();
    2990           0 :     char* res = cx->pod_malloc<char>(len + 1);
    2991           0 :     if (!res) {
    2992           0 :         JS_ReportOutOfMemory(cx);
    2993           0 :         return nullptr;
    2994             :     }
    2995             : 
    2996           0 :     js_memcpy(res, out.string(), len);
    2997           0 :     res[len] = 0;
    2998           0 :     if (length)
    2999             :         *length = len;
    3000             :     return res;
    3001             : }

Generated by: LCOV version 1.13-14-ga5dd952