diff options
Diffstat (limited to 'js/src/jsopcode.h')
-rw-r--r-- | js/src/jsopcode.h | 866 |
1 files changed, 866 insertions, 0 deletions
diff --git a/js/src/jsopcode.h b/js/src/jsopcode.h new file mode 100644 index 000000000..4f7859665 --- /dev/null +++ b/js/src/jsopcode.h @@ -0,0 +1,866 @@ +/* -*- 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 "mozilla/Attributes.h" + +#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 32-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 */ + JOF_SCOPE = 20, /* unsigned 32-bit scope index */ + JOF_ENVCOORD = 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_ENVCOORD 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_ENVCOORD_HOPS(jsbytecode* pc) +{ + return GET_UINT8(pc); +} + +static inline void +SET_ENVCOORD_HOPS(jsbytecode* pc, uint8_t hops) +{ + SET_UINT8(pc, hops); +} + +static const unsigned ENVCOORD_HOPS_LEN = 1; +static const unsigned ENVCOORD_HOPS_BITS = 8; +static const unsigned ENVCOORD_HOPS_LIMIT = 1 << ENVCOORD_HOPS_BITS; + +/* Describes the 'slot' component of a JOF_ENVCOORD opcode. */ +static inline uint32_t +GET_ENVCOORD_SLOT(const jsbytecode* pc) +{ + return GET_UINT24(pc); +} + +static inline void +SET_ENVCOORD_SLOT(jsbytecode* pc, uint32_t slot) +{ + SET_UINT24(pc, slot); +} + +static const unsigned ENVCOORD_SLOT_LEN = 3; +static const unsigned ENVCOORD_SLOT_BITS = 24; +static const uint32_t ENVCOORD_SLOT_LIMIT = 1 << ENVCOORD_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_ENVCOORD; +} + +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. + */ +extern MOZ_MUST_USE 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 + +extern MOZ_MUST_USE bool +DumpCompartmentPCCounts(JSContext* cx); + +} // namespace js + +#endif /* jsopcode_h */ |