#ifndef vm_BytecodeUtil_h
#define vm_BytecodeUtil_h
#include "mozilla/Attributes.h"
#include "mozilla/EndianUtils.h"
#include <algorithm>
#include "jstypes.h"
#include "NamespaceImports.h"
#include "frontend/SourceNotes.h"
#include "js/TypeDecls.h"
#include "js/UniquePtr.h"
#include "vm/Opcodes.h"
#include "vm/Printer.h"
typedef enum JSOp {
#define ENUMERATE_OPCODE(op, val, ...) op = val,
FOR_EACH_OPCODE(ENUMERATE_OPCODE)
#undef ENUMERATE_OPCODE
JSOP_LIMIT
} JSOp;
enum {
JOF_BYTE = 0,
JOF_UINT8 = 1,
JOF_UINT16 = 2,
JOF_UINT24 = 3,
JOF_UINT32 = 4,
JOF_INT8 = 5,
JOF_INT32 = 6,
JOF_JUMP = 7,
JOF_TABLESWITCH = 8,
JOF_ENVCOORD = 9,
JOF_ARGC = 10,
JOF_QARG = 11,
JOF_LOCAL = 12,
JOF_RESUMEINDEX = 13,
JOF_ATOM = 14,
JOF_OBJECT = 15,
JOF_REGEXP = 16,
JOF_DOUBLE = 17,
JOF_SCOPE = 18,
JOF_CODE_OFFSET = 19,
JOF_ICINDEX = 20,
JOF_LOOPENTRY = 21,
JOF_BIGINT = 22,
JOF_TYPEMASK = 0x001f,
JOF_NAME = 1 << 5,
JOF_PROP = 2 << 5,
JOF_ELEM = 3 << 5,
JOF_MODEMASK = 3 << 5,
JOF_PROPSET = 1 << 7,
JOF_PROPINIT = 1 << 8,
JOF_DETECTING = 1 << 9,
JOF_CHECKSLOPPY = 1 << 10,
JOF_CHECKSTRICT = 1 << 11,
JOF_INVOKE = 1 << 12,
JOF_GNAME = 1 << 13,
JOF_TYPESET = 1 << 14,
JOF_IC = 1 << 15,
};
static inline uint32_t JOF_TYPE(uint32_t fmt) { return fmt & JOF_TYPEMASK; }
static inline uint32_t JOF_MODE(uint32_t fmt) { return fmt & JOF_MODEMASK; }
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);
}
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) {
#if MOZ_LITTLE_ENDIAN
uint16_t result;
memcpy(&result, pc + 1, sizeof(result));
return result;
#else
return uint16_t((pc[2] << 8) | pc[1]);
#endif
}
static MOZ_ALWAYS_INLINE void SET_UINT16(jsbytecode* pc, uint16_t i) {
#if MOZ_LITTLE_ENDIAN
memcpy(pc + 1, &i, sizeof(i));
#else
pc[1] = UINT16_LO(i);
pc[2] = UINT16_HI(i);
#endif
}
static const unsigned UINT16_LIMIT = 1 << 16;
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 uint32_t GET_UINT24(const jsbytecode* pc) {
#if MOZ_LITTLE_ENDIAN
uint32_t result;
memcpy(&result, pc, 4);
return result >> 8;
#else
return unsigned((pc[3] << 16) | (pc[2] << 8) | pc[1]);
#endif
}
static MOZ_ALWAYS_INLINE void SET_UINT24(jsbytecode* pc, uint32_t i) {
MOZ_ASSERT(i < (1 << 24));
#if MOZ_LITTLE_ENDIAN
memcpy(pc + 1, &i, 3);
#else
pc[1] = jsbytecode(i);
pc[2] = jsbytecode(i >> 8);
pc[3] = jsbytecode(i >> 16);
#endif
}
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) {
#if MOZ_LITTLE_ENDIAN
uint32_t result;
memcpy(&result, pc + 1, sizeof(result));
return result;
#else
return (uint32_t(pc[4]) << 24) | (uint32_t(pc[3]) << 16) |
(uint32_t(pc[2]) << 8) | uint32_t(pc[1]);
#endif
}
static MOZ_ALWAYS_INLINE void SET_UINT32(jsbytecode* pc, uint32_t u) {
#if MOZ_LITTLE_ENDIAN
memcpy(pc + 1, &u, sizeof(u));
#else
pc[1] = jsbytecode(u);
pc[2] = jsbytecode(u >> 8);
pc[3] = jsbytecode(u >> 16);
pc[4] = jsbytecode(u >> 24);
#endif
}
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));
}
static MOZ_ALWAYS_INLINE int32_t GET_JUMP_OFFSET(jsbytecode* pc) {
return GET_INT32(pc);
}
static MOZ_ALWAYS_INLINE void SET_JUMP_OFFSET(jsbytecode* pc, int32_t off) {
SET_INT32(pc, off);
}
static MOZ_ALWAYS_INLINE int32_t GET_CODE_OFFSET(jsbytecode* pc) {
return GET_INT32(pc);
}
static MOZ_ALWAYS_INLINE void SET_CODE_OFFSET(jsbytecode* pc, int32_t off) {
SET_INT32(pc, off);
}
static const unsigned UINT32_INDEX_LEN = 4;
static MOZ_ALWAYS_INLINE uint32_t GET_UINT32_INDEX(const jsbytecode* pc) {
return GET_UINT32(pc);
}
static MOZ_ALWAYS_INLINE void SET_UINT32_INDEX(jsbytecode* pc, uint32_t index) {
SET_UINT32(pc, index);
}
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 uint32_t GET_RESUMEINDEX(const jsbytecode* pc) {
return GET_UINT24(pc);
}
static inline void SET_RESUMEINDEX(jsbytecode* pc, uint32_t resumeIndex) {
SET_UINT24(pc, resumeIndex);
}
static inline uint32_t GET_ICINDEX(const jsbytecode* pc) {
return GET_UINT32(pc);
}
static inline void SET_ICINDEX(jsbytecode* pc, uint32_t icIndex) {
SET_UINT32(pc, icIndex);
}
static inline unsigned LoopEntryDepthHint(jsbytecode* pc) {
MOZ_ASSERT(*pc == JSOP_LOOPENTRY);
return GET_UINT8(pc + 4) & 0x7f;
}
static inline bool LoopEntryCanIonOsr(jsbytecode* pc) {
MOZ_ASSERT(*pc == JSOP_LOOPENTRY);
return GET_UINT8(pc + 4) & 0x80;
}
static inline void SetLoopEntryDepthHintAndFlags(jsbytecode* pc,
unsigned loopDepth,
bool canIonOsr) {
MOZ_ASSERT(*pc == JSOP_LOOPENTRY);
uint8_t data = std::min(loopDepth, unsigned(0x7f)) | (canIonOsr ? 0x80 : 0);
SET_UINT8(pc + 4, data);
}
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;
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 {
uint8_t length;
int8_t nuses;
int8_t ndefs;
uint32_t format;
uint32_t type() const { return JOF_TYPE(format); }
};
namespace js {
extern const JSCodeSpec CodeSpec[];
extern const char* const CodeName[];
static inline uint32_t JOF_OPTYPE(JSOp op) {
return JOF_TYPE(CodeSpec[op].format);
}
static inline bool IsJumpOpcode(JSOp op) { return JOF_OPTYPE(op) == JOF_JUMP; }
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_THROWMSG:
case JSOP_TABLESWITCH:
return false;
case JSOP_GOSUB:
return true;
default:
return true;
}
}
static inline bool BytecodeIsJumpTarget(JSOp op) {
switch (op) {
case JSOP_JUMPTARGET:
case JSOP_LOOPHEAD:
case JSOP_LOOPENTRY:
return true;
default:
return false;
}
}
MOZ_ALWAYS_INLINE unsigned StackUses(jsbytecode* pc) {
JSOp op = JSOp(*pc);
int nuses = CodeSpec[op].nuses;
if (nuses >= 0) {
return nuses;
}
MOZ_ASSERT(nuses == -1);
switch (op) {
case JSOP_POPN:
return GET_UINT16(pc);
case JSOP_NEW:
case JSOP_SUPERCALL:
return 2 + GET_ARGC(pc) + 1;
default:
MOZ_ASSERT(op == JSOP_CALL || op == JSOP_CALL_IGNORES_RV ||
op == JSOP_EVAL || op == JSOP_CALLITER ||
op == JSOP_STRICTEVAL || op == JSOP_FUNCALL ||
op == JSOP_FUNAPPLY);
return 2 + GET_ARGC(pc);
}
}
MOZ_ALWAYS_INLINE unsigned StackDefs(jsbytecode* pc) {
int ndefs = CodeSpec[*pc].ndefs;
MOZ_ASSERT(ndefs >= 0);
return ndefs;
}
#if defined(DEBUG) || defined(JS_JITSPEW)
extern bool ReconstructStackDepth(JSContext* cx, JSScript* script,
jsbytecode* pc, uint32_t* depth,
bool* reachablePC);
#endif
}
#define JSDVG_IGNORE_STACK 0
#define JSDVG_SEARCH_STACK 1
namespace js {
UniqueChars DecompileValueGenerator(JSContext* cx, int spindex, HandleValue v,
HandleString fallback,
int skipStackHits = 0);
JSString* DecompileArgument(JSContext* cx, int formalIndex, HandleValue v);
static inline unsigned GetBytecodeLength(jsbytecode* pc) {
JSOp op = (JSOp)*pc;
MOZ_ASSERT(op < JSOP_LIMIT);
MOZ_ASSERT(CodeSpec[op].length > 0);
return CodeSpec[op].length;
}
static inline bool BytecodeIsPopped(jsbytecode* pc) {
jsbytecode* next = pc + GetBytecodeLength(pc);
return JSOp(*next) == JSOP_POP;
}
static inline bool BytecodeFlowsToBitop(jsbytecode* pc) {
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) {
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 IsPropertySetOp(JSOp op) {
return CodeSpec[op].format & JOF_PROPSET;
}
inline bool IsPropertyInitOp(JSOp op) {
return CodeSpec[op].format & JOF_PROPINIT;
}
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 IsElemPC(jsbytecode* pc) { return CodeSpec[*pc].format & JOF_ELEM; }
inline bool IsCallOp(JSOp op) { return CodeSpec[op].format & JOF_INVOKE; }
inline bool IsCallPC(jsbytecode* pc) { return IsCallOp(JSOp(*pc)); }
inline bool IsStrictEvalPC(jsbytecode* pc) {
JSOp op = JSOp(*pc);
return op == JSOP_STRICTEVAL || op == JSOP_STRICTSPREADEVAL;
}
inline bool IsConstructorCallOp(JSOp op) {
return op == JSOP_NEW || op == JSOP_SUPERCALL || op == JSOP_SPREADNEW ||
op == JSOP_SPREADSUPERCALL;
}
inline bool IsConstructorCallPC(const jsbytecode* pc) {
return IsConstructorCallOp(JSOp(*pc));
}
inline bool IsSpreadCallPC(jsbytecode* pc) {
JSOp op = JSOp(*pc);
return op == JSOP_SPREADCALL || op == JSOP_SPREADNEW ||
op == JSOP_SPREADSUPERCALL || op == JSOP_SPREADEVAL ||
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");
}
}
inline bool BytecodeOpHasIC(JSOp op) { return CodeSpec[op].format & JOF_IC; }
class PCCounts {
size_t pcOffset_;
uint64_t numExec_;
public:
explicit PCCounts(size_t off) : pcOffset_(off), numExec_(0) {}
size_t pcOffset() const { return pcOffset_; }
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);
}
typedef Vector<jsbytecode*, 4, SystemAllocPolicy> PcVector;
bool GetSuccessorBytecodes(JSScript* script, jsbytecode* pc,
PcVector& successors);
bool GetPredecessorBytecodes(JSScript* script, jsbytecode* pc,
PcVector& predecessors);
#if defined(DEBUG) || defined(JS_JITSPEW)
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 DumpRealmPCCounts(JSContext* cx);
}
#endif