/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 * vim: set ts=8 sts=4 et sw=4 tw=99:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef jsopcode_h
#define jsopcode_h

/*
 * JS bytecode definitions.
 */

#include "jsbytecode.h"
#include "jstypes.h"
#include "NamespaceImports.h"

#include "frontend/SourceNotes.h"
#include "js/UniquePtr.h"
#include "vm/Opcodes.h"
#include "vm/Printer.h"

/*
 * JS operation bytecodes.
 */
typedef enum JSOp {
#define ENUMERATE_OPCODE(op, val, ...) op = val,
FOR_EACH_OPCODE(ENUMERATE_OPCODE)
#undef ENUMERATE_OPCODE

    JSOP_LIMIT
} JSOp;

/*
 * JS bytecode formats.
 */
enum {
    JOF_BYTE            = 0,        /* single bytecode, no immediates */
    JOF_JUMP            = 1,        /* signed 16-bit jump offset immediate */
    JOF_ATOM            = 2,        /* unsigned 16-bit constant index */
    JOF_UINT16          = 3,        /* unsigned 16-bit immediate operand */
    JOF_TABLESWITCH     = 4,        /* table switch */
    /* 5 is unused */
    JOF_QARG            = 6,        /* quickened get/set function argument ops */
    JOF_LOCAL           = 7,        /* var or block-local variable */
    JOF_DOUBLE          = 8,        /* uint32_t index for double value */
    JOF_UINT24          = 12,       /* extended unsigned 24-bit literal (index) */
    JOF_UINT8           = 13,       /* uint8_t immediate, e.g. top 8 bits of 24-bit
                                       atom index */
    JOF_INT32           = 14,       /* int32_t immediate operand */
    JOF_UINT32          = 15,       /* uint32_t immediate operand */
    JOF_OBJECT          = 16,       /* unsigned 16-bit object index */
    JOF_REGEXP          = 17,       /* unsigned 32-bit regexp index */
    JOF_INT8            = 18,       /* int8_t immediate operand */
    JOF_ATOMOBJECT      = 19,       /* uint16_t constant index + object index */
    /* 20 is unused */
    JOF_SCOPECOORD      = 21,       /* embedded ScopeCoordinate immediate */
    JOF_TYPEMASK        = 0x001f,   /* mask for above immediate types */

    JOF_NAME            = 1 << 5,   /* name operation */
    JOF_PROP            = 2 << 5,   /* obj.prop operation */
    JOF_ELEM            = 3 << 5,   /* obj[index] operation */
    JOF_MODEMASK        = 7 << 5,   /* mask for above addressing modes */
    JOF_SET             = 1 << 8,   /* set (i.e., assignment) operation */
    /* 1 << 9 is unused */
    /* 1 << 10 is unused */
    /* 1 << 11 is unused */
    /* 1 << 12 is unused */
    /* 1 << 13 is unused */
    JOF_DETECTING       = 1 << 14,  /* object detection for warning-quelling */
    /* 1 << 15 is unused */
    JOF_LEFTASSOC       = 1 << 16,  /* left-associative operator */
    /* 1 << 17 is unused */
    /* 1 << 18 is unused */
    JOF_CHECKSLOPPY     = 1 << 19,  /* Op can only be generated in sloppy mode */
    JOF_CHECKSTRICT     = 1 << 20,  /* Op can only be generated in strict mode */
    JOF_INVOKE          = 1 << 21,  /* JSOP_CALL, JSOP_FUNCALL, JSOP_FUNAPPLY,
                                       JSOP_NEW, JSOP_EVAL, JSOP_CALLITER */
    /* 1 << 22 is unused */
    /* 1 << 23 is unused */
    /* 1 << 24 is unused */
    JOF_GNAME           = 1 << 25,  /* predicted global name */
    JOF_TYPESET         = 1 << 26,  /* has an entry in a script's type sets */
    JOF_ARITH           = 1 << 27   /* unary or binary arithmetic opcode */
};

/* Shorthand for type from format. */

static inline uint32_t
JOF_TYPE(uint32_t fmt)
{
    return fmt & JOF_TYPEMASK;
}

/* Shorthand for mode from format. */

static inline uint32_t
JOF_MODE(uint32_t fmt)
{
    return fmt & JOF_MODEMASK;
}

/*
 * Immediate operand getters, setters, and bounds.
 */

static MOZ_ALWAYS_INLINE uint8_t
GET_UINT8(jsbytecode* pc)
{
    return uint8_t(pc[1]);
}

static MOZ_ALWAYS_INLINE void
SET_UINT8(jsbytecode* pc, uint8_t u)
{
    pc[1] = jsbytecode(u);
}

/* Common uint16_t immediate format helpers. */

static inline jsbytecode
UINT16_HI(uint16_t i)
{
    return jsbytecode(i >> 8);
}

static inline jsbytecode
UINT16_LO(uint16_t i)
{
    return jsbytecode(i);
}

static MOZ_ALWAYS_INLINE uint16_t
GET_UINT16(const jsbytecode* pc)
{
    return uint16_t((pc[1] << 8) | pc[2]);
}

static MOZ_ALWAYS_INLINE void
SET_UINT16(jsbytecode* pc, uint16_t i)
{
    pc[1] = UINT16_HI(i);
    pc[2] = UINT16_LO(i);
}

static const unsigned UINT16_LEN        = 2;
static const unsigned UINT16_LIMIT      = 1 << 16;

/* Helpers for accessing the offsets of jump opcodes. */
static const unsigned JUMP_OFFSET_LEN   = 4;
static const int32_t JUMP_OFFSET_MIN    = INT32_MIN;
static const int32_t JUMP_OFFSET_MAX    = INT32_MAX;

static MOZ_ALWAYS_INLINE int32_t
GET_JUMP_OFFSET(jsbytecode* pc)
{
    return (pc[1] << 24) | (pc[2] << 16) | (pc[3] << 8) | pc[4];
}

static MOZ_ALWAYS_INLINE void
SET_JUMP_OFFSET(jsbytecode* pc, int32_t off)
{
    pc[1] = jsbytecode(off >> 24);
    pc[2] = jsbytecode(off >> 16);
    pc[3] = jsbytecode(off >> 8);
    pc[4] = jsbytecode(off);
}

static const unsigned UINT32_INDEX_LEN  = 4;

static MOZ_ALWAYS_INLINE uint32_t
GET_UINT32_INDEX(const jsbytecode* pc)
{
    return (pc[1] << 24) | (pc[2] << 16) | (pc[3] << 8) | pc[4];
}

static MOZ_ALWAYS_INLINE void
SET_UINT32_INDEX(jsbytecode* pc, uint32_t index)
{
    pc[1] = jsbytecode(index >> 24);
    pc[2] = jsbytecode(index >> 16);
    pc[3] = jsbytecode(index >> 8);
    pc[4] = jsbytecode(index);
}

static inline jsbytecode
UINT24_HI(unsigned i)
{
    return jsbytecode(i >> 16);
}

static inline jsbytecode
UINT24_MID(unsigned i)
{
    return jsbytecode(i >> 8);
}

static inline jsbytecode
UINT24_LO(unsigned i)
{
    return jsbytecode(i);
}

static MOZ_ALWAYS_INLINE unsigned
GET_UINT24(const jsbytecode* pc)
{
    return unsigned((pc[1] << 16) | (pc[2] << 8) | pc[3]);
}

static MOZ_ALWAYS_INLINE void
SET_UINT24(jsbytecode* pc, unsigned i)
{
    MOZ_ASSERT(i < (1 << 24));
    pc[1] = UINT24_HI(i);
    pc[2] = UINT24_MID(i);
    pc[3] = UINT24_LO(i);
}

static MOZ_ALWAYS_INLINE int8_t
GET_INT8(const jsbytecode* pc)
{
    return int8_t(pc[1]);
}

static MOZ_ALWAYS_INLINE uint32_t
GET_UINT32(const jsbytecode* pc)
{
    return  (uint32_t(pc[1]) << 24) |
            (uint32_t(pc[2]) << 16) |
            (uint32_t(pc[3]) << 8)  |
            uint32_t(pc[4]);
}

static MOZ_ALWAYS_INLINE void
SET_UINT32(jsbytecode* pc, uint32_t u)
{
    pc[1] = jsbytecode(u >> 24);
    pc[2] = jsbytecode(u >> 16);
    pc[3] = jsbytecode(u >> 8);
    pc[4] = jsbytecode(u);
}

static MOZ_ALWAYS_INLINE int32_t
GET_INT32(const jsbytecode* pc)
{
    return static_cast<int32_t>(GET_UINT32(pc));
}

static MOZ_ALWAYS_INLINE void
SET_INT32(jsbytecode* pc, int32_t i)
{
    SET_UINT32(pc, static_cast<uint32_t>(i));
}

/* Index limit is determined by SN_4BYTE_OFFSET_FLAG, see frontend/BytecodeEmitter.h. */
static const unsigned INDEX_LIMIT_LOG2  = 31;
static const uint32_t INDEX_LIMIT       = uint32_t(1) << INDEX_LIMIT_LOG2;

static inline jsbytecode
ARGC_HI(uint16_t argc)
{
    return UINT16_HI(argc);
}

static inline jsbytecode
ARGC_LO(uint16_t argc)
{
    return UINT16_LO(argc);
}

static inline uint16_t
GET_ARGC(const jsbytecode* pc)
{
    return GET_UINT16(pc);
}

static const unsigned ARGC_LIMIT        = UINT16_LIMIT;

static inline uint16_t
GET_ARGNO(const jsbytecode* pc)
{
    return GET_UINT16(pc);
}

static inline void
SET_ARGNO(jsbytecode* pc, uint16_t argno)
{
    SET_UINT16(pc, argno);
}

static const unsigned ARGNO_LEN         = 2;
static const unsigned ARGNO_LIMIT       = UINT16_LIMIT;

static inline uint32_t
GET_LOCALNO(const jsbytecode* pc)
{
    return GET_UINT24(pc);
}

static inline void
SET_LOCALNO(jsbytecode* pc, uint32_t varno)
{
    SET_UINT24(pc, varno);
}

static const unsigned LOCALNO_LEN       = 3;
static const unsigned LOCALNO_BITS      = 24;
static const uint32_t LOCALNO_LIMIT     = 1 << LOCALNO_BITS;

static inline unsigned
LoopEntryDepthHint(jsbytecode* pc)
{
    MOZ_ASSERT(*pc == JSOP_LOOPENTRY);
    return GET_UINT8(pc) & 0x7f;
}

static inline bool
LoopEntryCanIonOsr(jsbytecode* pc)
{
    MOZ_ASSERT(*pc == JSOP_LOOPENTRY);
    return GET_UINT8(pc) & 0x80;
}

static inline uint8_t
PackLoopEntryDepthHintAndFlags(unsigned loopDepth, bool canIonOsr)
{
    return (loopDepth < 0x80 ? uint8_t(loopDepth) : 0x7f) | (canIonOsr ? 0x80 : 0);
}

/*
 * Describes the 'hops' component of a JOF_SCOPECOORD opcode.
 *
 * Note: this component is only 8 bits wide, limiting the maximum number of
 * scopes between a use and def to roughly 255. This is a pretty small limit but
 * note that SpiderMonkey's recursive descent parser can only parse about this
 * many functions before hitting the C-stack recursion limit so this shouldn't
 * be a significant limitation in practice.
 */

static inline uint8_t
GET_SCOPECOORD_HOPS(jsbytecode* pc)
{
    return GET_UINT8(pc);
}

static inline void
SET_SCOPECOORD_HOPS(jsbytecode* pc, uint8_t hops)
{
    SET_UINT8(pc, hops);
}

static const unsigned SCOPECOORD_HOPS_LEN   = 1;
static const unsigned SCOPECOORD_HOPS_BITS  = 8;
static const unsigned SCOPECOORD_HOPS_LIMIT = 1 << SCOPECOORD_HOPS_BITS;

/* Describes the 'slot' component of a JOF_SCOPECOORD opcode. */
static inline uint32_t
GET_SCOPECOORD_SLOT(const jsbytecode* pc)
{
    return GET_UINT24(pc);
}

static inline void
SET_SCOPECOORD_SLOT(jsbytecode* pc, uint32_t slot)
{
    SET_UINT24(pc, slot);
}

static const unsigned SCOPECOORD_SLOT_LEN   = 3;
static const unsigned SCOPECOORD_SLOT_BITS  = 24;
static const uint32_t SCOPECOORD_SLOT_LIMIT = 1 << SCOPECOORD_SLOT_BITS;

struct JSCodeSpec {
    int8_t              length;         /* length including opcode byte */
    int8_t              nuses;          /* arity, -1 if variadic */
    int8_t              ndefs;          /* number of stack results */
    uint32_t            format;         /* immediate operand format */

    uint32_t type() const { return JOF_TYPE(format); }
};

/* Silence unreferenced formal parameter warnings */
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4100)
#endif

namespace js {

extern const JSCodeSpec CodeSpec[];
extern const unsigned   NumCodeSpecs;
extern const char       * const CodeName[];

/* Shorthand for type from opcode. */

static inline uint32_t
JOF_OPTYPE(JSOp op)
{
    return JOF_TYPE(CodeSpec[op].format);
}

static inline bool
IsJumpOpcode(JSOp op)
{
    uint32_t type = JOF_TYPE(CodeSpec[op].format);

    /*
     * LABEL opcodes have type JOF_JUMP but are no-ops, don't treat them as
     * jumps to avoid degrading precision.
     */
    return type == JOF_JUMP && op != JSOP_LABEL;
}

static inline bool
BytecodeFallsThrough(JSOp op)
{
    switch (op) {
      case JSOP_GOTO:
      case JSOP_DEFAULT:
      case JSOP_RETURN:
      case JSOP_RETRVAL:
      case JSOP_FINALYIELDRVAL:
      case JSOP_THROW:
      case JSOP_TABLESWITCH:
        return false;
      case JSOP_GOSUB:
        /* These fall through indirectly, after executing a 'finally'. */
        return true;
      default:
        return true;
    }
}

static inline bool
BytecodeIsJumpTarget(JSOp op)
{
    switch (op) {
      case JSOP_JUMPTARGET:
      case JSOP_LOOPHEAD:
      case JSOP_LOOPENTRY:
      case JSOP_ENDITER:
      case JSOP_TRY:
        return true;
      default:
        return false;
    }
}

class SrcNoteLineScanner
{
    /* offset of the current JSOp in the bytecode */
    ptrdiff_t offset;

    /* next src note to process */
    jssrcnote* sn;

    /* line number of the current JSOp */
    uint32_t lineno;

    /*
     * Is the current op the first one after a line change directive? Note that
     * multiple ops may be "first" if a line directive is used to return to a
     * previous line (eg, with a for loop increment expression.)
     */
    bool lineHeader;

  public:
    SrcNoteLineScanner(jssrcnote* sn, uint32_t lineno)
        : offset(0), sn(sn), lineno(lineno)
    {
    }

    /*
     * This is called repeatedly with always-advancing relpc values. The src
     * notes are tuples of <PC offset from prev src note, type, args>. Scan
     * through, updating the lineno, until the next src note is for a later
     * bytecode.
     *
     * When looking at the desired PC offset ('relpc'), the op is first in that
     * line iff there is a SRC_SETLINE or SRC_NEWLINE src note for that exact
     * bytecode.
     *
     * Note that a single bytecode may have multiple line-modifying notes (even
     * though only one should ever be needed.)
     */
    void advanceTo(ptrdiff_t relpc) {
        // Must always advance! If the same or an earlier PC is erroneously
        // passed in, we will already be past the relevant src notes
        MOZ_ASSERT_IF(offset > 0, relpc > offset);

        // Next src note should be for after the current offset
        MOZ_ASSERT_IF(offset > 0, SN_IS_TERMINATOR(sn) || SN_DELTA(sn) > 0);

        // The first PC requested is always considered to be a line header
        lineHeader = (offset == 0);

        if (SN_IS_TERMINATOR(sn))
            return;

        ptrdiff_t nextOffset;
        while ((nextOffset = offset + SN_DELTA(sn)) <= relpc && !SN_IS_TERMINATOR(sn)) {
            offset = nextOffset;
            SrcNoteType type = (SrcNoteType) SN_TYPE(sn);
            if (type == SRC_SETLINE || type == SRC_NEWLINE) {
                if (type == SRC_SETLINE)
                    lineno = GetSrcNoteOffset(sn, 0);
                else
                    lineno++;

                if (offset == relpc)
                    lineHeader = true;
            }

            sn = SN_NEXT(sn);
        }
    }

    bool isLineHeader() const {
        return lineHeader;
    }

    uint32_t getLine() const { return lineno; }
};

extern unsigned
StackUses(JSScript* script, jsbytecode* pc);

extern unsigned
StackDefs(JSScript* script, jsbytecode* pc);

#ifdef DEBUG
/*
 * Given bytecode address pc in script's main program code, compute the operand
 * stack depth just before (JSOp) *pc executes.  If *pc is not reachable, return
 * false.
 */
extern bool
ReconstructStackDepth(JSContext* cx, JSScript* script, jsbytecode* pc, uint32_t* depth, bool* reachablePC);
#endif

}  /* namespace js */

#ifdef _MSC_VER
#pragma warning(pop)
#endif

#define JSDVG_IGNORE_STACK      0
#define JSDVG_SEARCH_STACK      1

namespace js {

/*
 * Get the length of variable-length bytecode like JSOP_TABLESWITCH.
 */
extern size_t
GetVariableBytecodeLength(jsbytecode* pc);

/*
 * Find the source expression that resulted in v, and return a newly allocated
 * C-string containing it.  Fall back on v's string conversion (fallback) if we
 * can't find the bytecode that generated and pushed v on the operand stack.
 *
 * Search the current stack frame if spindex is JSDVG_SEARCH_STACK.  Don't
 * look for v on the stack if spindex is JSDVG_IGNORE_STACK.  Otherwise,
 * spindex is the negative index of v, measured from cx->fp->sp, or from a
 * lower frame's sp if cx->fp is native.
 *
 * The optional argument skipStackHits can be used to skip a hit in the stack
 * frame. This can be useful in self-hosted code that wants to report value
 * errors containing decompiled values that are useful for the user, instead of
 * values used internally by the self-hosted code.
 *
 * The caller must call JS_free on the result after a successful call.
 */
UniqueChars
DecompileValueGenerator(JSContext* cx, int spindex, HandleValue v,
                        HandleString fallback, int skipStackHits = 0);

/*
 * Decompile the formal argument at formalIndex in the nearest non-builtin
 * stack frame, falling back with converting v to source.
 */
char*
DecompileArgument(JSContext* cx, int formalIndex, HandleValue v);

extern bool
CallResultEscapes(jsbytecode* pc);

static inline unsigned
GetDecomposeLength(jsbytecode* pc, size_t len)
{
    /*
     * The last byte of a DECOMPOSE op stores the decomposed length.  This is a
     * constant: perhaps we should just hardcode values instead?
     */
    MOZ_ASSERT(size_t(CodeSpec[*pc].length) == len);
    return (unsigned) pc[len - 1];
}

static inline unsigned
GetBytecodeLength(jsbytecode* pc)
{
    JSOp op = (JSOp)*pc;
    MOZ_ASSERT(op < JSOP_LIMIT);

    if (CodeSpec[op].length != -1)
        return CodeSpec[op].length;
    return GetVariableBytecodeLength(pc);
}

static inline bool
BytecodeIsPopped(jsbytecode* pc)
{
    jsbytecode* next = pc + GetBytecodeLength(pc);
    return JSOp(*next) == JSOP_POP;
}

static inline bool
BytecodeFlowsToBitop(jsbytecode* pc)
{
    // Look for simple bytecode for integer conversions like (x | 0) or (x & -1).
    jsbytecode* next = pc + GetBytecodeLength(pc);
    if (*next == JSOP_BITOR || *next == JSOP_BITAND)
        return true;
    if (*next == JSOP_INT8 && GET_INT8(next) == -1) {
        next += GetBytecodeLength(next);
        if (*next == JSOP_BITAND)
            return true;
        return false;
    }
    if (*next == JSOP_ONE) {
        next += GetBytecodeLength(next);
        if (*next == JSOP_NEG) {
            next += GetBytecodeLength(next);
            if (*next == JSOP_BITAND)
                return true;
        }
        return false;
    }
    if (*next == JSOP_ZERO) {
        next += GetBytecodeLength(next);
        if (*next == JSOP_BITOR)
            return true;
        return false;
    }
    return false;
}

extern bool
IsValidBytecodeOffset(JSContext* cx, JSScript* script, size_t offset);

inline bool
FlowsIntoNext(JSOp op)
{
    /* JSOP_YIELD is considered to flow into the next instruction, like JSOP_CALL. */
    switch (op) {
      case JSOP_RETRVAL:
      case JSOP_RETURN:
      case JSOP_THROW:
      case JSOP_GOTO:
      case JSOP_RETSUB:
      case JSOP_FINALYIELDRVAL:
        return false;
      default:
        return true;
    }
}

inline bool
IsArgOp(JSOp op)
{
    return JOF_OPTYPE(op) == JOF_QARG;
}

inline bool
IsLocalOp(JSOp op)
{
    return JOF_OPTYPE(op) == JOF_LOCAL;
}

inline bool
IsAliasedVarOp(JSOp op)
{
    return JOF_OPTYPE(op) == JOF_SCOPECOORD;
}

inline bool
IsGlobalOp(JSOp op)
{
    return CodeSpec[op].format & JOF_GNAME;
}

inline bool
IsEqualityOp(JSOp op)
{
    return op == JSOP_EQ || op == JSOP_NE || op == JSOP_STRICTEQ || op == JSOP_STRICTNE;
}

inline bool
IsCheckStrictOp(JSOp op)
{
    return CodeSpec[op].format & JOF_CHECKSTRICT;
}

#ifdef DEBUG
inline bool
IsCheckSloppyOp(JSOp op)
{
    return CodeSpec[op].format & JOF_CHECKSLOPPY;
}
#endif

inline bool
IsAtomOp(JSOp op)
{
    return JOF_OPTYPE(op) == JOF_ATOM;
}

inline bool
IsGetPropPC(jsbytecode* pc)
{
    JSOp op = JSOp(*pc);
    return op == JSOP_LENGTH  || op == JSOP_GETPROP || op == JSOP_CALLPROP;
}

inline bool
IsHiddenInitOp(JSOp op)
{
    return op == JSOP_INITHIDDENPROP || op == JSOP_INITHIDDENELEM ||
           op == JSOP_INITHIDDENPROP_GETTER || op == JSOP_INITHIDDENELEM_GETTER ||
           op == JSOP_INITHIDDENPROP_SETTER || op == JSOP_INITHIDDENELEM_SETTER;
}

inline bool
IsStrictSetPC(jsbytecode* pc)
{
    JSOp op = JSOp(*pc);
    return op == JSOP_STRICTSETPROP ||
           op == JSOP_STRICTSETNAME ||
           op == JSOP_STRICTSETGNAME ||
           op == JSOP_STRICTSETELEM;
}

inline bool
IsSetPropPC(jsbytecode* pc)
{
    JSOp op = JSOp(*pc);
    return op == JSOP_SETPROP || op == JSOP_STRICTSETPROP ||
           op == JSOP_SETNAME || op == JSOP_STRICTSETNAME ||
           op == JSOP_SETGNAME || op == JSOP_STRICTSETGNAME;
}

inline bool
IsGetElemPC(jsbytecode* pc)
{
    JSOp op = JSOp(*pc);
    return op == JSOP_GETELEM || op == JSOP_CALLELEM;
}

inline bool
IsSetElemPC(jsbytecode* pc)
{
    JSOp op = JSOp(*pc);
    return op == JSOP_SETELEM ||
           op == JSOP_STRICTSETELEM;
}

inline bool
IsCallPC(jsbytecode* pc)
{
    return CodeSpec[*pc].format & JOF_INVOKE;
}

inline bool
IsStrictEvalPC(jsbytecode* pc)
{
    JSOp op = JSOp(*pc);
    return op == JSOP_STRICTEVAL || op == JSOP_STRICTSPREADEVAL;
}

static inline int32_t
GetBytecodeInteger(jsbytecode* pc)
{
    switch (JSOp(*pc)) {
      case JSOP_ZERO:   return 0;
      case JSOP_ONE:    return 1;
      case JSOP_UINT16: return GET_UINT16(pc);
      case JSOP_UINT24: return GET_UINT24(pc);
      case JSOP_INT8:   return GET_INT8(pc);
      case JSOP_INT32:  return GET_INT32(pc);
      default:
        MOZ_CRASH("Bad op");
    }
}

/*
 * Counts accumulated for a single opcode in a script. The counts tracked vary
 * between opcodes, and this structure ensures that counts are accessed in a
 * coherent fashion.
 */
class PCCounts
{
    /*
     * Offset of the pc inside the script. This fields is used to lookup opcode
     * which have annotations.
     */
    size_t pcOffset_;

    /*
     * Record the number of execution of one instruction, or the number of
     * throws executed.
     */
    uint64_t numExec_;

 public:
    explicit PCCounts(size_t off)
      : pcOffset_(off),
        numExec_(0)
    {}

    size_t pcOffset() const {
        return pcOffset_;
    }

    // Used for sorting and searching.
    bool operator<(const PCCounts& rhs) const {
        return pcOffset_ < rhs.pcOffset_;
    }

    uint64_t& numExec() {
        return numExec_;
    }
    uint64_t numExec() const {
        return numExec_;
    }

    static const char* numExecName;
};

static inline jsbytecode*
GetNextPc(jsbytecode* pc)
{
    return pc + GetBytecodeLength(pc);
}

#if defined(DEBUG)
/*
 * Disassemblers, for debugging only.
 */
bool
Disassemble(JSContext* cx, JS::Handle<JSScript*> script, bool lines, Sprinter* sp);

unsigned
Disassemble1(JSContext* cx, JS::Handle<JSScript*> script, jsbytecode* pc, unsigned loc,
             bool lines, Sprinter* sp);

#endif

void
DumpPCCounts(JSContext* cx, JS::Handle<JSScript*> script, Sprinter* sp);

namespace jit { struct IonScriptCounts; }
void
DumpIonScriptCounts(js::Sprinter* sp, HandleScript script,
                    jit::IonScriptCounts* ionCounts);

void
DumpCompartmentPCCounts(JSContext* cx);
} // namespace js

#endif /* jsopcode_h */
