tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

BytecodeUtil.h (19774B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sts=2 et sw=2 tw=80:
      3 * This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #ifndef vm_BytecodeUtil_h
      8 #define vm_BytecodeUtil_h
      9 
     10 /*
     11 * JS bytecode definitions.
     12 */
     13 
     14 #include "mozilla/Assertions.h"
     15 #include "mozilla/Attributes.h"
     16 #include "mozilla/EndianUtils.h"
     17 
     18 #include <algorithm>
     19 #include <stddef.h>
     20 #include <stdint.h>
     21 
     22 #include "jstypes.h"
     23 #include "NamespaceImports.h"
     24 
     25 #include "js/TypeDecls.h"
     26 #include "js/Utility.h"
     27 #include "js/Value.h"
     28 #include "vm/BytecodeFormatFlags.h"  // JOF_*
     29 #include "vm/GeneratorResumeKind.h"
     30 #include "vm/Opcodes.h"
     31 #include "vm/SharedStencil.h"  // js::GCThingIndex
     32 #include "vm/ThrowMsgKind.h"   // ThrowMsgKind, ThrowCondition
     33 
     34 namespace js {
     35 class JS_PUBLIC_API StringPrinter;
     36 }  // namespace js
     37 
     38 /* Shorthand for type from format. */
     39 
     40 static inline uint32_t JOF_TYPE(uint32_t fmt) { return fmt & JOF_TYPEMASK; }
     41 
     42 /*
     43 * Immediate operand getters, setters, and bounds.
     44 */
     45 
     46 static MOZ_ALWAYS_INLINE uint8_t GET_UINT8(const jsbytecode* pc) {
     47  return uint8_t(pc[1]);
     48 }
     49 
     50 static MOZ_ALWAYS_INLINE void SET_UINT8(jsbytecode* pc, uint8_t u) {
     51  pc[1] = jsbytecode(u);
     52 }
     53 
     54 /* Common uint16_t immediate format helpers. */
     55 
     56 static inline jsbytecode UINT16_HI(uint16_t i) { return jsbytecode(i >> 8); }
     57 
     58 static inline jsbytecode UINT16_LO(uint16_t i) { return jsbytecode(i); }
     59 
     60 static MOZ_ALWAYS_INLINE uint16_t GET_UINT16(const jsbytecode* pc) {
     61  uint16_t result;
     62  mozilla::NativeEndian::copyAndSwapFromLittleEndian(&result, pc + 1, 1);
     63  return result;
     64 }
     65 
     66 static MOZ_ALWAYS_INLINE void SET_UINT16(jsbytecode* pc, uint16_t i) {
     67  mozilla::NativeEndian::copyAndSwapToLittleEndian(pc + 1, &i, 1);
     68 }
     69 
     70 static const unsigned UINT16_LIMIT = 1 << 16;
     71 
     72 /* Helpers for accessing the offsets of jump opcodes. */
     73 static const unsigned JUMP_OFFSET_LEN = 4;
     74 static const int32_t JUMP_OFFSET_MIN = INT32_MIN;
     75 static const int32_t JUMP_OFFSET_MAX = INT32_MAX;
     76 
     77 static MOZ_ALWAYS_INLINE uint32_t GET_UINT24(const jsbytecode* pc) {
     78 #if MOZ_LITTLE_ENDIAN()
     79  // Do a single 32-bit load (for opcode and operand), then shift off the
     80  // opcode.
     81  uint32_t result;
     82  memcpy(&result, pc, 4);
     83  return result >> 8;
     84 #else
     85  return uint32_t((pc[3] << 16) | (pc[2] << 8) | pc[1]);
     86 #endif
     87 }
     88 
     89 static MOZ_ALWAYS_INLINE void SET_UINT24(jsbytecode* pc, uint32_t i) {
     90  MOZ_ASSERT(i < (1 << 24));
     91 
     92 #if MOZ_LITTLE_ENDIAN()
     93  memcpy(pc + 1, &i, 3);
     94 #else
     95  pc[1] = jsbytecode(i);
     96  pc[2] = jsbytecode(i >> 8);
     97  pc[3] = jsbytecode(i >> 16);
     98 #endif
     99 }
    100 
    101 static MOZ_ALWAYS_INLINE int8_t GET_INT8(const jsbytecode* pc) {
    102  return int8_t(pc[1]);
    103 }
    104 
    105 static MOZ_ALWAYS_INLINE uint32_t GET_UINT32(const jsbytecode* pc) {
    106  uint32_t result;
    107  mozilla::NativeEndian::copyAndSwapFromLittleEndian(&result, pc + 1, 1);
    108  return result;
    109 }
    110 
    111 static MOZ_ALWAYS_INLINE void SET_UINT32(jsbytecode* pc, uint32_t u) {
    112  mozilla::NativeEndian::copyAndSwapToLittleEndian(pc + 1, &u, 1);
    113 }
    114 
    115 static MOZ_ALWAYS_INLINE JS::Value GET_INLINE_VALUE(const jsbytecode* pc) {
    116  uint64_t raw;
    117  mozilla::NativeEndian::copyAndSwapFromLittleEndian(&raw, pc + 1, 1);
    118  return JS::Value::fromRawBits(raw);
    119 }
    120 
    121 static MOZ_ALWAYS_INLINE void SET_INLINE_VALUE(jsbytecode* pc,
    122                                               const JS::Value& v) {
    123  uint64_t raw = v.asRawBits();
    124  mozilla::NativeEndian::copyAndSwapToLittleEndian(pc + 1, &raw, 1);
    125 }
    126 
    127 static MOZ_ALWAYS_INLINE int32_t GET_INT32(const jsbytecode* pc) {
    128  return static_cast<int32_t>(GET_UINT32(pc));
    129 }
    130 
    131 static MOZ_ALWAYS_INLINE void SET_INT32(jsbytecode* pc, int32_t i) {
    132  SET_UINT32(pc, static_cast<uint32_t>(i));
    133 }
    134 
    135 static MOZ_ALWAYS_INLINE int32_t GET_JUMP_OFFSET(jsbytecode* pc) {
    136  return GET_INT32(pc);
    137 }
    138 
    139 static MOZ_ALWAYS_INLINE void SET_JUMP_OFFSET(jsbytecode* pc, int32_t off) {
    140  SET_INT32(pc, off);
    141 }
    142 
    143 static const unsigned GCTHING_INDEX_LEN = 4;
    144 
    145 static MOZ_ALWAYS_INLINE js::GCThingIndex GET_GCTHING_INDEX(
    146    const jsbytecode* pc) {
    147  return js::GCThingIndex(GET_UINT32(pc));
    148 }
    149 
    150 static MOZ_ALWAYS_INLINE void SET_GCTHING_INDEX(jsbytecode* pc,
    151                                                js::GCThingIndex index) {
    152  SET_UINT32(pc, index.index);
    153 }
    154 
    155 // Index limit is determined by SrcNote::FourByteOffsetFlag, see
    156 // frontend/BytecodeEmitter.h.
    157 static const unsigned INDEX_LIMIT_LOG2 = 31;
    158 static const uint32_t INDEX_LIMIT = uint32_t(1) << INDEX_LIMIT_LOG2;
    159 
    160 static inline jsbytecode ARGC_HI(uint16_t argc) { return UINT16_HI(argc); }
    161 
    162 static inline jsbytecode ARGC_LO(uint16_t argc) { return UINT16_LO(argc); }
    163 
    164 static inline uint16_t GET_ARGC(const jsbytecode* pc) { return GET_UINT16(pc); }
    165 
    166 static const unsigned ARGC_LIMIT = UINT16_LIMIT;
    167 
    168 static inline uint16_t GET_ARGNO(const jsbytecode* pc) {
    169  return GET_UINT16(pc);
    170 }
    171 
    172 static inline void SET_ARGNO(jsbytecode* pc, uint16_t argno) {
    173  SET_UINT16(pc, argno);
    174 }
    175 
    176 static const unsigned ARGNO_LEN = 2;
    177 static const unsigned ARGNO_LIMIT = UINT16_LIMIT;
    178 
    179 static inline uint32_t GET_LOCALNO(const jsbytecode* pc) {
    180  return GET_UINT24(pc);
    181 }
    182 
    183 static inline void SET_LOCALNO(jsbytecode* pc, uint32_t varno) {
    184  SET_UINT24(pc, varno);
    185 }
    186 
    187 static const unsigned LOCALNO_LEN = 3;
    188 static const unsigned LOCALNO_BITS = 24;
    189 static const uint32_t LOCALNO_LIMIT = 1 << LOCALNO_BITS;
    190 
    191 static inline uint32_t GET_RESUMEINDEX(const jsbytecode* pc) {
    192  return GET_UINT24(pc);
    193 }
    194 
    195 static inline void SET_RESUMEINDEX(jsbytecode* pc, uint32_t resumeIndex) {
    196  SET_UINT24(pc, resumeIndex);
    197 }
    198 
    199 static const unsigned ICINDEX_LEN = 4;
    200 
    201 static inline uint32_t GET_ICINDEX(const jsbytecode* pc) {
    202  return GET_UINT32(pc);
    203 }
    204 
    205 static inline void SET_ICINDEX(jsbytecode* pc, uint32_t icIndex) {
    206  SET_UINT32(pc, icIndex);
    207 }
    208 
    209 static inline unsigned LoopHeadDepthHint(jsbytecode* pc) {
    210  MOZ_ASSERT(JSOp(*pc) == JSOp::LoopHead);
    211  return GET_UINT8(pc + 4);
    212 }
    213 
    214 static inline void SetLoopHeadDepthHint(jsbytecode* pc, unsigned loopDepth) {
    215  MOZ_ASSERT(JSOp(*pc) == JSOp::LoopHead);
    216  uint8_t data = std::min(loopDepth, unsigned(UINT8_MAX));
    217  SET_UINT8(pc + 4, data);
    218 }
    219 
    220 static inline bool IsBackedgePC(jsbytecode* pc) {
    221  switch (JSOp(*pc)) {
    222    case JSOp::Goto:
    223    case JSOp::JumpIfTrue:
    224      return GET_JUMP_OFFSET(pc) < 0;
    225    default:
    226      return false;
    227  }
    228 }
    229 
    230 static inline bool IsBackedgeForLoopHead(jsbytecode* pc, jsbytecode* loopHead) {
    231  MOZ_ASSERT(JSOp(*loopHead) == JSOp::LoopHead);
    232  return IsBackedgePC(pc) && pc + GET_JUMP_OFFSET(pc) == loopHead;
    233 }
    234 
    235 /*
    236 * Describes the 'hops' component of a JOF_ENVCOORD opcode.
    237 *
    238 * Note: this component is only 16 bits wide, limiting the maximum number of
    239 * scopes between a use and def to roughly 65535. This is a pretty small limit
    240 * but note that SpiderMonkey's recursive descent parser can't parse too many
    241 * functions before hitting the C-stack recursion limit so this shouldn't
    242 * be a significant limitation in practice.
    243 */
    244 
    245 static inline uint16_t GET_ENVCOORD_HOPS(jsbytecode* pc) {
    246  return GET_UINT16(pc);
    247 }
    248 
    249 static inline void SET_ENVCOORD_HOPS(jsbytecode* pc, uint16_t hops) {
    250  SET_UINT16(pc, hops);
    251 }
    252 
    253 static const unsigned ENVCOORD_HOPS_LEN = 2;
    254 static const unsigned ENVCOORD_HOPS_BITS = 16;
    255 static const unsigned ENVCOORD_HOPS_LIMIT = 1 << ENVCOORD_HOPS_BITS;
    256 
    257 /* Describes the 'slot' component of a JOF_ENVCOORD opcode. */
    258 static inline uint32_t GET_ENVCOORD_SLOT(const jsbytecode* pc) {
    259  return GET_UINT24(pc);
    260 }
    261 
    262 static inline void SET_ENVCOORD_SLOT(jsbytecode* pc, uint32_t slot) {
    263  SET_UINT24(pc, slot);
    264 }
    265 
    266 static const unsigned ENVCOORD_SLOT_LEN = 3;
    267 static const unsigned ENVCOORD_SLOT_BITS = 24;
    268 static const uint32_t ENVCOORD_SLOT_LIMIT = 1 << ENVCOORD_SLOT_BITS;
    269 
    270 struct JSCodeSpec {
    271  uint8_t length;  /* length including opcode byte */
    272  int8_t nuses;    /* arity, -1 if variadic */
    273  int8_t ndefs;    /* number of stack results */
    274  uint32_t format; /* immediate operand format */
    275 };
    276 
    277 namespace js {
    278 
    279 extern const JSCodeSpec CodeSpecTable[];
    280 
    281 inline const JSCodeSpec& CodeSpec(JSOp op) {
    282  return CodeSpecTable[uint8_t(op)];
    283 }
    284 
    285 extern const char* const CodeNameTable[];
    286 
    287 inline const char* CodeName(JSOp op) { return CodeNameTable[uint8_t(op)]; }
    288 
    289 /* Shorthand for type from opcode. */
    290 
    291 static inline uint32_t JOF_OPTYPE(JSOp op) {
    292  return JOF_TYPE(CodeSpec(op).format);
    293 }
    294 
    295 static inline bool IsJumpOpcode(JSOp op) { return JOF_OPTYPE(op) == JOF_JUMP; }
    296 
    297 static inline bool BytecodeFallsThrough(JSOp op) {
    298  // Note:
    299  // * JSOp::Yield/JSOp::Await is considered to fall through, like JSOp::Call.
    300  switch (op) {
    301    case JSOp::Goto:
    302    case JSOp::Default:
    303    case JSOp::Return:
    304    case JSOp::RetRval:
    305    case JSOp::FinalYieldRval:
    306    case JSOp::Throw:
    307    case JSOp::ThrowWithStack:
    308    case JSOp::ThrowMsg:
    309    case JSOp::ThrowSetConst:
    310    case JSOp::TableSwitch:
    311      return false;
    312    default:
    313      return true;
    314  }
    315 }
    316 
    317 static inline bool BytecodeIsJumpTarget(JSOp op) {
    318  switch (op) {
    319    case JSOp::JumpTarget:
    320    case JSOp::LoopHead:
    321    case JSOp::AfterYield:
    322      return true;
    323    default:
    324      return false;
    325  }
    326 }
    327 
    328 // The JSOp argument is superflous, but we are using it to avoid a
    329 // store forwarding Bug on some Android phones; see Bug 1833315
    330 MOZ_ALWAYS_INLINE unsigned StackUses(JSOp op, jsbytecode* pc) {
    331  MOZ_ASSERT(op == JSOp(*pc));
    332  int nuses = CodeSpec(op).nuses;
    333  if (nuses >= 0) {
    334    return nuses;
    335  }
    336 
    337  MOZ_ASSERT(nuses == -1);
    338  switch (op) {
    339    case JSOp::PopN:
    340      return GET_UINT16(pc);
    341    case JSOp::New:
    342    case JSOp::NewContent:
    343    case JSOp::SuperCall:
    344      return 2 + GET_ARGC(pc) + 1;
    345    default:
    346      /* stack: fun, this, [argc arguments] */
    347      MOZ_ASSERT(op == JSOp::Call || op == JSOp::CallContent ||
    348                 op == JSOp::CallIgnoresRv || op == JSOp::Eval ||
    349                 op == JSOp::CallIter || op == JSOp::CallContentIter ||
    350                 op == JSOp::StrictEval);
    351      return 2 + GET_ARGC(pc);
    352  }
    353 }
    354 
    355 MOZ_ALWAYS_INLINE unsigned StackDefs(JSOp op) {
    356  int ndefs = CodeSpec(op).ndefs;
    357  MOZ_ASSERT(ndefs >= 0);
    358  return ndefs;
    359 }
    360 
    361 #if defined(DEBUG) || defined(JS_JITSPEW)
    362 /*
    363 * Given bytecode address pc in script's main program code, compute the operand
    364 * stack depth just before (JSOp) *pc executes.  If *pc is not reachable, return
    365 * false.
    366 */
    367 extern bool ReconstructStackDepth(JSContext* cx, JSScript* script,
    368                                  jsbytecode* pc, uint32_t* depth,
    369                                  bool* reachablePC);
    370 #endif
    371 
    372 } /* namespace js */
    373 
    374 #define JSDVG_IGNORE_STACK 0
    375 #define JSDVG_SEARCH_STACK 1
    376 
    377 namespace js {
    378 
    379 /*
    380 * Find the source expression that resulted in v, and return a newly allocated
    381 * C-string containing it.  Fall back on v's string conversion (fallback) if we
    382 * can't find the bytecode that generated and pushed v on the operand stack.
    383 *
    384 * Search the current stack frame if spindex is JSDVG_SEARCH_STACK.  Don't
    385 * look for v on the stack if spindex is JSDVG_IGNORE_STACK.  Otherwise,
    386 * spindex is the negative index of v, measured from cx->fp->sp, or from a
    387 * lower frame's sp if cx->fp is native.
    388 *
    389 * The optional argument skipStackHits can be used to skip a hit in the stack
    390 * frame. This can be useful in self-hosted code that wants to report value
    391 * errors containing decompiled values that are useful for the user, instead of
    392 * values used internally by the self-hosted code.
    393 *
    394 * The caller must call JS_free on the result after a successful call.
    395 */
    396 UniqueChars DecompileValueGenerator(JSContext* cx, int spindex, HandleValue v,
    397                                    HandleString fallback,
    398                                    int skipStackHits = 0);
    399 
    400 /*
    401 * Decompile the formal argument at formalIndex in the nearest non-builtin
    402 * stack frame, falling back with converting v to source.
    403 */
    404 JSString* DecompileArgument(JSContext* cx, int formalIndex, HandleValue v);
    405 
    406 static inline unsigned GetOpLength(JSOp op) {
    407  MOZ_ASSERT(uint8_t(op) < JSOP_LIMIT);
    408  MOZ_ASSERT(CodeSpec(op).length > 0);
    409  return CodeSpec(op).length;
    410 }
    411 
    412 static inline unsigned GetBytecodeLength(const jsbytecode* pc) {
    413  JSOp op = (JSOp)*pc;
    414  return GetOpLength(op);
    415 }
    416 
    417 static inline bool BytecodeIsPopped(jsbytecode* pc) {
    418  jsbytecode* next = pc + GetBytecodeLength(pc);
    419  return JSOp(*next) == JSOp::Pop;
    420 }
    421 
    422 extern bool IsValidBytecodeOffset(JSContext* cx, JSScript* script,
    423                                  size_t offset);
    424 
    425 inline bool IsArgOp(JSOp op) { return JOF_OPTYPE(op) == JOF_QARG; }
    426 
    427 inline bool IsLocalOp(JSOp op) { return JOF_OPTYPE(op) == JOF_LOCAL; }
    428 
    429 inline bool IsAliasedVarOp(JSOp op) { return JOF_OPTYPE(op) == JOF_ENVCOORD; }
    430 
    431 inline bool IsGlobalOp(JSOp op) { return CodeSpec(op).format & JOF_GNAME; }
    432 
    433 inline bool IsPropertySetOp(JSOp op) {
    434  return CodeSpec(op).format & JOF_PROPSET;
    435 }
    436 
    437 inline bool IsPropertyInitOp(JSOp op) {
    438  return CodeSpec(op).format & JOF_PROPINIT;
    439 }
    440 
    441 inline bool IsLooseEqualityOp(JSOp op) {
    442  return op == JSOp::Eq || op == JSOp::Ne;
    443 }
    444 
    445 inline bool IsStrictEqualityOp(JSOp op) {
    446  return op == JSOp::StrictEq || op == JSOp::StrictNe;
    447 }
    448 
    449 inline bool IsEqualityOp(JSOp op) {
    450  return IsLooseEqualityOp(op) || IsStrictEqualityOp(op);
    451 }
    452 
    453 inline bool IsRelationalOp(JSOp op) {
    454  return op == JSOp::Lt || op == JSOp::Le || op == JSOp::Gt || op == JSOp::Ge;
    455 }
    456 
    457 inline bool IsCheckStrictOp(JSOp op) {
    458  return CodeSpec(op).format & JOF_CHECKSTRICT;
    459 }
    460 
    461 #ifdef DEBUG
    462 inline bool IsCheckSloppyOp(JSOp op) {
    463  return CodeSpec(op).format & JOF_CHECKSLOPPY;
    464 }
    465 #endif
    466 
    467 inline bool IsAtomOp(JSOp op) { return JOF_OPTYPE(op) == JOF_ATOM; }
    468 
    469 inline bool IsGetPropOp(JSOp op) { return op == JSOp::GetProp; }
    470 
    471 inline bool IsGetPropPC(const jsbytecode* pc) { return IsGetPropOp(JSOp(*pc)); }
    472 
    473 inline bool IsHiddenInitOp(JSOp op) {
    474  return op == JSOp::InitHiddenProp || op == JSOp::InitHiddenElem ||
    475         op == JSOp::InitHiddenPropGetter || op == JSOp::InitHiddenElemGetter ||
    476         op == JSOp::InitHiddenPropSetter || op == JSOp::InitHiddenElemSetter;
    477 }
    478 
    479 inline bool IsLockedInitOp(JSOp op) {
    480  return op == JSOp::InitLockedProp || op == JSOp::InitLockedElem;
    481 }
    482 
    483 inline bool IsStrictSetPC(jsbytecode* pc) {
    484  JSOp op = JSOp(*pc);
    485  return op == JSOp::StrictSetProp || op == JSOp::StrictSetName ||
    486         op == JSOp::StrictSetGName || op == JSOp::StrictSetElem;
    487 }
    488 
    489 inline bool IsSetPropOp(JSOp op) {
    490  return op == JSOp::SetProp || op == JSOp::StrictSetProp ||
    491         op == JSOp::SetName || op == JSOp::StrictSetName ||
    492         op == JSOp::SetGName || op == JSOp::StrictSetGName;
    493 }
    494 
    495 inline bool IsSetPropPC(const jsbytecode* pc) { return IsSetPropOp(JSOp(*pc)); }
    496 
    497 inline bool IsGetElemOp(JSOp op) { return op == JSOp::GetElem; }
    498 
    499 inline bool IsGetElemPC(const jsbytecode* pc) { return IsGetElemOp(JSOp(*pc)); }
    500 
    501 inline bool IsSetElemOp(JSOp op) {
    502  return op == JSOp::SetElem || op == JSOp::StrictSetElem;
    503 }
    504 
    505 inline bool IsSetElemPC(const jsbytecode* pc) { return IsSetElemOp(JSOp(*pc)); }
    506 
    507 inline bool IsInvokeOp(JSOp op) { return CodeSpec(op).format & JOF_INVOKE; }
    508 
    509 inline bool IsInvokePC(jsbytecode* pc) { return IsInvokeOp(JSOp(*pc)); }
    510 
    511 inline bool IsStrictEvalPC(jsbytecode* pc) {
    512  JSOp op = JSOp(*pc);
    513  return op == JSOp::StrictEval || op == JSOp::StrictSpreadEval;
    514 }
    515 
    516 inline bool IsConstructOp(JSOp op) {
    517  return CodeSpec(op).format & JOF_CONSTRUCT;
    518 }
    519 inline bool IsConstructPC(const jsbytecode* pc) {
    520  return IsConstructOp(JSOp(*pc));
    521 }
    522 
    523 inline bool IsSpreadOp(JSOp op) { return CodeSpec(op).format & JOF_SPREAD; }
    524 
    525 inline bool IsSpreadPC(const jsbytecode* pc) { return IsSpreadOp(JSOp(*pc)); }
    526 
    527 // Returns true if the specified opcode is for `typeof name` where `name` is
    528 // single identifier.
    529 inline bool IsTypeOfNameOp(JSOp op) {
    530  return op == JSOp::Typeof || op == JSOp::TypeofEq;
    531 }
    532 
    533 inline bool OpUsesEnvironmentChain(JSOp op) {
    534  return CodeSpec(op).format & JOF_USES_ENV;
    535 }
    536 
    537 static inline int32_t GetBytecodeInteger(jsbytecode* pc) {
    538  switch (JSOp(*pc)) {
    539    case JSOp::Zero:
    540      return 0;
    541    case JSOp::One:
    542      return 1;
    543    case JSOp::Uint16:
    544      return GET_UINT16(pc);
    545    case JSOp::Uint24:
    546      return GET_UINT24(pc);
    547    case JSOp::Int8:
    548      return GET_INT8(pc);
    549    case JSOp::Int32:
    550      return GET_INT32(pc);
    551    default:
    552      MOZ_CRASH("Bad op");
    553  }
    554 }
    555 
    556 inline bool BytecodeOpHasIC(JSOp op) { return CodeSpec(op).format & JOF_IC; }
    557 
    558 inline void GetCheckPrivateFieldOperands(jsbytecode* pc,
    559                                         ThrowCondition* throwCondition,
    560                                         ThrowMsgKind* throwKind) {
    561  static_assert(sizeof(ThrowCondition) == sizeof(uint8_t));
    562  static_assert(sizeof(ThrowMsgKind) == sizeof(uint8_t));
    563 
    564  MOZ_ASSERT(JSOp(*pc) == JSOp::CheckPrivateField);
    565  uint8_t throwConditionByte = GET_UINT8(pc);
    566  uint8_t throwKindByte = GET_UINT8(pc + 1);
    567 
    568  *throwCondition = static_cast<ThrowCondition>(throwConditionByte);
    569  *throwKind = static_cast<ThrowMsgKind>(throwKindByte);
    570 
    571  MOZ_ASSERT(*throwCondition == ThrowCondition::ThrowHas ||
    572             *throwCondition == ThrowCondition::ThrowHasNot ||
    573             *throwCondition == ThrowCondition::OnlyCheckRhs);
    574 
    575  MOZ_ASSERT(*throwKind == ThrowMsgKind::PrivateDoubleInit ||
    576             *throwKind == ThrowMsgKind::PrivateBrandDoubleInit ||
    577             *throwKind == ThrowMsgKind::MissingPrivateOnGet ||
    578             *throwKind == ThrowMsgKind::MissingPrivateOnSet);
    579 }
    580 
    581 // Return true iff the combination of the ThrowCondition and hasOwn result
    582 // will throw an exception.
    583 static inline bool CheckPrivateFieldWillThrow(ThrowCondition condition,
    584                                              bool hasOwn) {
    585  if ((condition == ThrowCondition::ThrowHasNot && !hasOwn) ||
    586      (condition == ThrowCondition::ThrowHas && hasOwn)) {
    587    // Met a throw condition.
    588    return true;
    589  }
    590 
    591  return false;
    592 }
    593 
    594 /*
    595 * Counts accumulated for a single opcode in a script. The counts tracked vary
    596 * between opcodes, and this structure ensures that counts are accessed in a
    597 * coherent fashion.
    598 */
    599 class PCCounts {
    600  /*
    601   * Offset of the pc inside the script. This fields is used to lookup opcode
    602   * which have annotations.
    603   */
    604  size_t pcOffset_;
    605 
    606  /*
    607   * Record the number of execution of one instruction, or the number of
    608   * throws executed.
    609   */
    610  uint64_t numExec_;
    611 
    612 public:
    613  explicit PCCounts(size_t off) : pcOffset_(off), numExec_(0) {}
    614 
    615  size_t pcOffset() const { return pcOffset_; }
    616 
    617  // Used for sorting and searching.
    618  bool operator<(const PCCounts& rhs) const {
    619    return pcOffset_ < rhs.pcOffset_;
    620  }
    621 
    622  uint64_t& numExec() { return numExec_; }
    623  uint64_t numExec() const { return numExec_; }
    624 
    625  static const char numExecName[];
    626 };
    627 
    628 static inline jsbytecode* GetNextPc(jsbytecode* pc) {
    629  return pc + GetBytecodeLength(pc);
    630 }
    631 
    632 inline GeneratorResumeKind IntToResumeKind(int32_t value) {
    633  MOZ_ASSERT(uint32_t(value) <= uint32_t(GeneratorResumeKind::Return));
    634  return static_cast<GeneratorResumeKind>(value);
    635 }
    636 
    637 inline GeneratorResumeKind ResumeKindFromPC(jsbytecode* pc) {
    638  MOZ_ASSERT(JSOp(*pc) == JSOp::ResumeKind);
    639  return IntToResumeKind(GET_UINT8(pc));
    640 }
    641 
    642 #if defined(DEBUG) || defined(JS_JITSPEW)
    643 
    644 enum class DisassembleSkeptically { No, Yes };
    645 
    646 /*
    647 * Disassemblers, for debugging only.
    648 */
    649 [[nodiscard]] extern bool Disassemble(
    650    JSContext* cx, JS::Handle<JSScript*> script, bool lines, StringPrinter* sp,
    651    DisassembleSkeptically skeptically = DisassembleSkeptically::No);
    652 
    653 unsigned Disassemble1(JSContext* cx, JS::Handle<JSScript*> script,
    654                      jsbytecode* pc, unsigned loc, bool lines,
    655                      StringPrinter* sp);
    656 
    657 extern UniqueChars ToDisassemblySource(JSContext* cx, HandleValue v);
    658 
    659 #endif
    660 
    661 [[nodiscard]] extern bool DumpRealmPCCounts(JSContext* cx);
    662 
    663 }  // namespace js
    664 
    665 #endif /* vm_BytecodeUtil_h */