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 */