summaryrefslogtreecommitdiffstats
path: root/js/src/vm/BytecodeUtil.h
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/vm/BytecodeUtil.h')
-rw-r--r--js/src/vm/BytecodeUtil.h659
1 files changed, 659 insertions, 0 deletions
diff --git a/js/src/vm/BytecodeUtil.h b/js/src/vm/BytecodeUtil.h
new file mode 100644
index 0000000000..5035ac2810
--- /dev/null
+++ b/js/src/vm/BytecodeUtil.h
@@ -0,0 +1,659 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * 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 vm_BytecodeUtil_h
+#define vm_BytecodeUtil_h
+
+/*
+ * JS bytecode definitions.
+ */
+
+#include "mozilla/Assertions.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/EndianUtils.h"
+
+#include <algorithm>
+#include <stddef.h>
+#include <stdint.h>
+
+#include "jstypes.h"
+#include "NamespaceImports.h"
+
+#include "js/TypeDecls.h"
+#include "js/Utility.h"
+#include "js/Value.h"
+#include "vm/BytecodeFormatFlags.h" // JOF_*
+#include "vm/GeneratorResumeKind.h"
+#include "vm/Opcodes.h"
+#include "vm/SharedStencil.h" // js::GCThingIndex
+#include "vm/ThrowMsgKind.h" // ThrowMsgKind, ThrowCondition
+
+namespace js {
+class Sprinter;
+} // namespace js
+
+/* 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) {
+ uint16_t result;
+ mozilla::NativeEndian::copyAndSwapFromLittleEndian(&result, pc + 1, 1);
+ return result;
+}
+
+static MOZ_ALWAYS_INLINE void SET_UINT16(jsbytecode* pc, uint16_t i) {
+ mozilla::NativeEndian::copyAndSwapToLittleEndian(pc + 1, &i, 1);
+}
+
+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 uint32_t GET_UINT24(const jsbytecode* pc) {
+#if MOZ_LITTLE_ENDIAN()
+ // Do a single 32-bit load (for opcode and operand), then shift off the
+ // opcode.
+ uint32_t result;
+ memcpy(&result, pc, 4);
+ return result >> 8;
+#else
+ return uint32_t((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) {
+ uint32_t result;
+ mozilla::NativeEndian::copyAndSwapFromLittleEndian(&result, pc + 1, 1);
+ return result;
+}
+
+static MOZ_ALWAYS_INLINE void SET_UINT32(jsbytecode* pc, uint32_t u) {
+ mozilla::NativeEndian::copyAndSwapToLittleEndian(pc + 1, &u, 1);
+}
+
+static MOZ_ALWAYS_INLINE JS::Value GET_INLINE_VALUE(const jsbytecode* pc) {
+ uint64_t raw;
+ mozilla::NativeEndian::copyAndSwapFromLittleEndian(&raw, pc + 1, 1);
+ return JS::Value::fromRawBits(raw);
+}
+
+static MOZ_ALWAYS_INLINE void SET_INLINE_VALUE(jsbytecode* pc,
+ const JS::Value& v) {
+ uint64_t raw = v.asRawBits();
+ mozilla::NativeEndian::copyAndSwapToLittleEndian(pc + 1, &raw, 1);
+}
+
+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 const unsigned GCTHING_INDEX_LEN = 4;
+
+static MOZ_ALWAYS_INLINE js::GCThingIndex GET_GCTHING_INDEX(
+ const jsbytecode* pc) {
+ return js::GCThingIndex(GET_UINT32(pc));
+}
+
+static MOZ_ALWAYS_INLINE void SET_GCTHING_INDEX(jsbytecode* pc,
+ js::GCThingIndex index) {
+ SET_UINT32(pc, index.index);
+}
+
+// Index limit is determined by SrcNote::FourByteOffsetFlag, 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 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 const unsigned ICINDEX_LEN = 4;
+
+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 LoopHeadDepthHint(jsbytecode* pc) {
+ MOZ_ASSERT(JSOp(*pc) == JSOp::LoopHead);
+ return GET_UINT8(pc + 4);
+}
+
+static inline void SetLoopHeadDepthHint(jsbytecode* pc, unsigned loopDepth) {
+ MOZ_ASSERT(JSOp(*pc) == JSOp::LoopHead);
+ uint8_t data = std::min(loopDepth, unsigned(UINT8_MAX));
+ SET_UINT8(pc + 4, data);
+}
+
+static inline bool IsBackedgePC(jsbytecode* pc) {
+ switch (JSOp(*pc)) {
+ case JSOp::Goto:
+ case JSOp::JumpIfTrue:
+ return GET_JUMP_OFFSET(pc) < 0;
+ default:
+ return false;
+ }
+}
+
+static inline bool IsBackedgeForLoopHead(jsbytecode* pc, jsbytecode* loopHead) {
+ MOZ_ASSERT(JSOp(*loopHead) == JSOp::LoopHead);
+ return IsBackedgePC(pc) && pc + GET_JUMP_OFFSET(pc) == loopHead;
+}
+
+/*
+ * 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 {
+ uint8_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 */
+};
+
+namespace js {
+
+extern const JSCodeSpec CodeSpecTable[];
+
+inline const JSCodeSpec& CodeSpec(JSOp op) {
+ return CodeSpecTable[uint8_t(op)];
+}
+
+extern const char* const CodeNameTable[];
+
+inline const char* CodeName(JSOp op) { return CodeNameTable[uint8_t(op)]; }
+
+/* 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) { return JOF_OPTYPE(op) == JOF_JUMP; }
+
+static inline bool BytecodeFallsThrough(JSOp op) {
+ // Note:
+ // * JSOp::Yield/JSOp::Await is considered to fall through, like JSOp::Call.
+ 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::ThrowSetConst:
+ case JSOp::TableSwitch:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static inline bool BytecodeIsJumpTarget(JSOp op) {
+ switch (op) {
+ case JSOp::JumpTarget:
+ case JSOp::LoopHead:
+ case JSOp::AfterYield:
+ 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::NewContent:
+ case JSOp::SuperCall:
+ return 2 + GET_ARGC(pc) + 1;
+ default:
+ /* stack: fun, this, [argc arguments] */
+ MOZ_ASSERT(op == JSOp::Call || op == JSOp::CallContent ||
+ op == JSOp::CallIgnoresRv || op == JSOp::Eval ||
+ op == JSOp::CallIter || op == JSOp::CallContentIter ||
+ op == JSOp::StrictEval);
+ return 2 + GET_ARGC(pc);
+ }
+}
+
+MOZ_ALWAYS_INLINE unsigned StackDefs(jsbytecode* pc) {
+ int ndefs = CodeSpec(JSOp(*pc)).ndefs;
+ MOZ_ASSERT(ndefs >= 0);
+ return ndefs;
+}
+
+#if defined(DEBUG) || defined(JS_JITSPEW)
+/*
+ * 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 */
+
+#define JSDVG_IGNORE_STACK 0
+#define JSDVG_SEARCH_STACK 1
+
+namespace js {
+
+/*
+ * 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.
+ */
+JSString* DecompileArgument(JSContext* cx, int formalIndex, HandleValue v);
+
+static inline unsigned GetOpLength(JSOp op) {
+ MOZ_ASSERT(uint8_t(op) < JSOP_LIMIT);
+ MOZ_ASSERT(CodeSpec(op).length > 0);
+ return CodeSpec(op).length;
+}
+
+static inline unsigned GetBytecodeLength(const jsbytecode* pc) {
+ JSOp op = (JSOp)*pc;
+ return GetOpLength(op);
+}
+
+static inline bool BytecodeIsPopped(jsbytecode* pc) {
+ jsbytecode* next = pc + GetBytecodeLength(pc);
+ return JSOp(*next) == JSOp::Pop;
+}
+
+extern bool IsValidBytecodeOffset(JSContext* cx, JSScript* script,
+ size_t offset);
+
+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 IsLooseEqualityOp(JSOp op) {
+ return op == JSOp::Eq || op == JSOp::Ne;
+}
+
+inline bool IsStrictEqualityOp(JSOp op) {
+ return op == JSOp::StrictEq || op == JSOp::StrictNe;
+}
+
+inline bool IsEqualityOp(JSOp op) {
+ return IsLooseEqualityOp(op) || IsStrictEqualityOp(op);
+}
+
+inline bool IsRelationalOp(JSOp op) {
+ return op == JSOp::Lt || op == JSOp::Le || op == JSOp::Gt || op == JSOp::Ge;
+}
+
+inline bool IsCheckStrictOp(JSOp op) {
+ return CodeSpec(op).format & JOF_CHECKSTRICT;
+}
+
+inline bool IsNameOp(JSOp op) { return CodeSpec(op).format & JOF_NAME; }
+
+#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 IsGetPropOp(JSOp op) { return op == JSOp::GetProp; }
+
+inline bool IsGetPropPC(const jsbytecode* pc) { return IsGetPropOp(JSOp(*pc)); }
+
+inline bool IsHiddenInitOp(JSOp op) {
+ return op == JSOp::InitHiddenProp || op == JSOp::InitHiddenElem ||
+ op == JSOp::InitHiddenPropGetter || op == JSOp::InitHiddenElemGetter ||
+ op == JSOp::InitHiddenPropSetter || op == JSOp::InitHiddenElemSetter;
+}
+
+inline bool IsLockedInitOp(JSOp op) {
+ return op == JSOp::InitLockedProp || op == JSOp::InitLockedElem;
+}
+
+inline bool IsStrictSetPC(jsbytecode* pc) {
+ JSOp op = JSOp(*pc);
+ return op == JSOp::StrictSetProp || op == JSOp::StrictSetName ||
+ op == JSOp::StrictSetGName || op == JSOp::StrictSetElem;
+}
+
+inline bool IsSetPropOp(JSOp op) {
+ return op == JSOp::SetProp || op == JSOp::StrictSetProp ||
+ op == JSOp::SetName || op == JSOp::StrictSetName ||
+ op == JSOp::SetGName || op == JSOp::StrictSetGName;
+}
+
+inline bool IsSetPropPC(const jsbytecode* pc) { return IsSetPropOp(JSOp(*pc)); }
+
+inline bool IsGetElemOp(JSOp op) { return op == JSOp::GetElem; }
+
+inline bool IsGetElemPC(const jsbytecode* pc) { return IsGetElemOp(JSOp(*pc)); }
+
+inline bool IsSetElemOp(JSOp op) {
+ return op == JSOp::SetElem || op == JSOp::StrictSetElem;
+}
+
+inline bool IsSetElemPC(const jsbytecode* pc) { return IsSetElemOp(JSOp(*pc)); }
+
+inline bool IsElemPC(const jsbytecode* pc) {
+ return CodeSpec(JSOp(*pc)).format & JOF_ELEM;
+}
+
+inline bool IsInvokeOp(JSOp op) { return CodeSpec(op).format & JOF_INVOKE; }
+
+inline bool IsInvokePC(jsbytecode* pc) { return IsInvokeOp(JSOp(*pc)); }
+
+inline bool IsStrictEvalPC(jsbytecode* pc) {
+ JSOp op = JSOp(*pc);
+ return op == JSOp::StrictEval || op == JSOp::StrictSpreadEval;
+}
+
+inline bool IsConstructOp(JSOp op) {
+ return CodeSpec(op).format & JOF_CONSTRUCT;
+}
+inline bool IsConstructPC(const jsbytecode* pc) {
+ return IsConstructOp(JSOp(*pc));
+}
+
+inline bool IsSpreadOp(JSOp op) { return CodeSpec(op).format & JOF_SPREAD; }
+
+inline bool IsSpreadPC(const jsbytecode* pc) { return IsSpreadOp(JSOp(*pc)); }
+
+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; }
+
+inline void GetCheckPrivateFieldOperands(jsbytecode* pc,
+ ThrowCondition* throwCondition,
+ ThrowMsgKind* throwKind) {
+ static_assert(sizeof(ThrowCondition) == sizeof(uint8_t));
+ static_assert(sizeof(ThrowMsgKind) == sizeof(uint8_t));
+
+ MOZ_ASSERT(JSOp(*pc) == JSOp::CheckPrivateField);
+ uint8_t throwConditionByte = GET_UINT8(pc);
+ uint8_t throwKindByte = GET_UINT8(pc + 1);
+
+ *throwCondition = static_cast<ThrowCondition>(throwConditionByte);
+ *throwKind = static_cast<ThrowMsgKind>(throwKindByte);
+
+ MOZ_ASSERT(*throwCondition == ThrowCondition::ThrowHas ||
+ *throwCondition == ThrowCondition::ThrowHasNot ||
+ *throwCondition == ThrowCondition::OnlyCheckRhs);
+
+ MOZ_ASSERT(*throwKind == ThrowMsgKind::PrivateDoubleInit ||
+ *throwKind == ThrowMsgKind::PrivateBrandDoubleInit ||
+ *throwKind == ThrowMsgKind::MissingPrivateOnGet ||
+ *throwKind == ThrowMsgKind::MissingPrivateOnSet);
+}
+
+// Return true iff the combination of the ThrowCondition and hasOwn result
+// will throw an exception.
+static inline bool CheckPrivateFieldWillThrow(ThrowCondition condition,
+ bool hasOwn) {
+ if ((condition == ThrowCondition::ThrowHasNot && !hasOwn) ||
+ (condition == ThrowCondition::ThrowHas && hasOwn)) {
+ // Met a throw condition.
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * 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);
+}
+
+inline GeneratorResumeKind IntToResumeKind(int32_t value) {
+ MOZ_ASSERT(uint32_t(value) <= uint32_t(GeneratorResumeKind::Return));
+ return static_cast<GeneratorResumeKind>(value);
+}
+
+inline GeneratorResumeKind ResumeKindFromPC(jsbytecode* pc) {
+ MOZ_ASSERT(JSOp(*pc) == JSOp::ResumeKind);
+ return IntToResumeKind(GET_UINT8(pc));
+}
+
+#if defined(DEBUG) || defined(JS_JITSPEW)
+
+enum class DisassembleSkeptically { No, Yes };
+
+/*
+ * Disassemblers, for debugging only.
+ */
+[[nodiscard]] extern bool Disassemble(
+ JSContext* cx, JS::Handle<JSScript*> script, bool lines, Sprinter* sp,
+ DisassembleSkeptically skeptically = DisassembleSkeptically::No);
+
+unsigned Disassemble1(JSContext* cx, JS::Handle<JSScript*> script,
+ jsbytecode* pc, unsigned loc, bool lines, Sprinter* sp);
+
+#endif
+
+[[nodiscard]] extern bool DumpRealmPCCounts(JSContext* cx);
+
+} // namespace js
+
+#endif /* vm_BytecodeUtil_h */