| /* -*- 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/UniquePtr.h" |
| |
| #include "jsbytecode.h" |
| #include "jstypes.h" |
| #include "NamespaceImports.h" |
| |
| #include "frontend/SourceNotes.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; |
| } |
| } |
| |
| 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. |
| */ |
| mozilla::UniquePtr<char[], JS::FreePolicy> |
| 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, jit::IonScriptCounts* ionCounts); |
| |
| void |
| DumpCompartmentPCCounts(JSContext* cx); |
| } // namespace js |
| |
| #endif /* jsopcode_h */ |