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/. */
7 #ifndef vm_BytecodeUtil_h
8 #define vm_BytecodeUtil_h
11 * JS bytecode definitions.
14 #include "mozilla/Assertions.h"
15 #include "mozilla/Attributes.h"
16 #include "mozilla/EndianUtils.h"
23 #include "NamespaceImports.h"
25 #include "js/TypeDecls.h"
26 #include "js/Utility.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
35 class JS_PUBLIC_API StringPrinter
;
38 /* Shorthand for type from format. */
40 static inline uint32_t JOF_TYPE(uint32_t fmt
) { return fmt
& JOF_TYPEMASK
; }
43 * Immediate operand getters, setters, and bounds.
46 static MOZ_ALWAYS_INLINE
uint8_t GET_UINT8(jsbytecode
* pc
) {
47 return uint8_t(pc
[1]);
50 static MOZ_ALWAYS_INLINE
void SET_UINT8(jsbytecode
* pc
, uint8_t u
) {
51 pc
[1] = jsbytecode(u
);
54 /* Common uint16_t immediate format helpers. */
56 static inline jsbytecode
UINT16_HI(uint16_t i
) { return jsbytecode(i
>> 8); }
58 static inline jsbytecode
UINT16_LO(uint16_t i
) { return jsbytecode(i
); }
60 static MOZ_ALWAYS_INLINE
uint16_t GET_UINT16(const jsbytecode
* pc
) {
62 mozilla::NativeEndian::copyAndSwapFromLittleEndian(&result
, pc
+ 1, 1);
66 static MOZ_ALWAYS_INLINE
void SET_UINT16(jsbytecode
* pc
, uint16_t i
) {
67 mozilla::NativeEndian::copyAndSwapToLittleEndian(pc
+ 1, &i
, 1);
70 static const unsigned UINT16_LIMIT
= 1 << 16;
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
;
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
82 memcpy(&result
, pc
, 4);
85 return uint32_t((pc
[3] << 16) | (pc
[2] << 8) | pc
[1]);
89 static MOZ_ALWAYS_INLINE
void SET_UINT24(jsbytecode
* pc
, uint32_t i
) {
90 MOZ_ASSERT(i
< (1 << 24));
92 #if MOZ_LITTLE_ENDIAN()
93 memcpy(pc
+ 1, &i
, 3);
95 pc
[1] = jsbytecode(i
);
96 pc
[2] = jsbytecode(i
>> 8);
97 pc
[3] = jsbytecode(i
>> 16);
101 static MOZ_ALWAYS_INLINE
int8_t GET_INT8(const jsbytecode
* pc
) {
102 return int8_t(pc
[1]);
105 static MOZ_ALWAYS_INLINE
uint32_t GET_UINT32(const jsbytecode
* pc
) {
107 mozilla::NativeEndian::copyAndSwapFromLittleEndian(&result
, pc
+ 1, 1);
111 static MOZ_ALWAYS_INLINE
void SET_UINT32(jsbytecode
* pc
, uint32_t u
) {
112 mozilla::NativeEndian::copyAndSwapToLittleEndian(pc
+ 1, &u
, 1);
115 static MOZ_ALWAYS_INLINE
JS::Value
GET_INLINE_VALUE(const jsbytecode
* pc
) {
117 mozilla::NativeEndian::copyAndSwapFromLittleEndian(&raw
, pc
+ 1, 1);
118 return JS::Value::fromRawBits(raw
);
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);
127 static MOZ_ALWAYS_INLINE
int32_t GET_INT32(const jsbytecode
* pc
) {
128 return static_cast<int32_t>(GET_UINT32(pc
));
131 static MOZ_ALWAYS_INLINE
void SET_INT32(jsbytecode
* pc
, int32_t i
) {
132 SET_UINT32(pc
, static_cast<uint32_t>(i
));
135 static MOZ_ALWAYS_INLINE
int32_t GET_JUMP_OFFSET(jsbytecode
* pc
) {
136 return GET_INT32(pc
);
139 static MOZ_ALWAYS_INLINE
void SET_JUMP_OFFSET(jsbytecode
* pc
, int32_t off
) {
143 static const unsigned GCTHING_INDEX_LEN
= 4;
145 static MOZ_ALWAYS_INLINE
js::GCThingIndex
GET_GCTHING_INDEX(
146 const jsbytecode
* pc
) {
147 return js::GCThingIndex(GET_UINT32(pc
));
150 static MOZ_ALWAYS_INLINE
void SET_GCTHING_INDEX(jsbytecode
* pc
,
151 js::GCThingIndex index
) {
152 SET_UINT32(pc
, index
.index
);
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
;
160 static inline jsbytecode
ARGC_HI(uint16_t argc
) { return UINT16_HI(argc
); }
162 static inline jsbytecode
ARGC_LO(uint16_t argc
) { return UINT16_LO(argc
); }
164 static inline uint16_t GET_ARGC(const jsbytecode
* pc
) { return GET_UINT16(pc
); }
166 static const unsigned ARGC_LIMIT
= UINT16_LIMIT
;
168 static inline uint16_t GET_ARGNO(const jsbytecode
* pc
) {
169 return GET_UINT16(pc
);
172 static inline void SET_ARGNO(jsbytecode
* pc
, uint16_t argno
) {
173 SET_UINT16(pc
, argno
);
176 static const unsigned ARGNO_LEN
= 2;
177 static const unsigned ARGNO_LIMIT
= UINT16_LIMIT
;
179 static inline uint32_t GET_LOCALNO(const jsbytecode
* pc
) {
180 return GET_UINT24(pc
);
183 static inline void SET_LOCALNO(jsbytecode
* pc
, uint32_t varno
) {
184 SET_UINT24(pc
, varno
);
187 static const unsigned LOCALNO_LEN
= 3;
188 static const unsigned LOCALNO_BITS
= 24;
189 static const uint32_t LOCALNO_LIMIT
= 1 << LOCALNO_BITS
;
191 static inline uint32_t GET_RESUMEINDEX(const jsbytecode
* pc
) {
192 return GET_UINT24(pc
);
195 static inline void SET_RESUMEINDEX(jsbytecode
* pc
, uint32_t resumeIndex
) {
196 SET_UINT24(pc
, resumeIndex
);
199 static const unsigned ICINDEX_LEN
= 4;
201 static inline uint32_t GET_ICINDEX(const jsbytecode
* pc
) {
202 return GET_UINT32(pc
);
205 static inline void SET_ICINDEX(jsbytecode
* pc
, uint32_t icIndex
) {
206 SET_UINT32(pc
, icIndex
);
209 static inline unsigned LoopHeadDepthHint(jsbytecode
* pc
) {
210 MOZ_ASSERT(JSOp(*pc
) == JSOp::LoopHead
);
211 return GET_UINT8(pc
+ 4);
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
);
220 static inline bool IsBackedgePC(jsbytecode
* pc
) {
223 case JSOp::JumpIfTrue
:
224 return GET_JUMP_OFFSET(pc
) < 0;
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
;
236 * Describes the 'hops' component of a JOF_ENVCOORD opcode.
238 * Note: this component is only 8 bits wide, limiting the maximum number of
239 * scopes between a use and def to roughly 255. This is a pretty small limit but
240 * note that SpiderMonkey's recursive descent parser can only parse about this
241 * many functions before hitting the C-stack recursion limit so this shouldn't
242 * be a significant limitation in practice.
245 static inline uint8_t GET_ENVCOORD_HOPS(jsbytecode
* pc
) {
246 return GET_UINT8(pc
);
249 static inline void SET_ENVCOORD_HOPS(jsbytecode
* pc
, uint8_t hops
) {
253 static const unsigned ENVCOORD_HOPS_LEN
= 1;
254 static const unsigned ENVCOORD_HOPS_BITS
= 8;
255 static const unsigned ENVCOORD_HOPS_LIMIT
= 1 << ENVCOORD_HOPS_BITS
;
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
);
262 static inline void SET_ENVCOORD_SLOT(jsbytecode
* pc
, uint32_t slot
) {
263 SET_UINT24(pc
, slot
);
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
;
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 */
279 extern const JSCodeSpec CodeSpecTable
[];
281 inline const JSCodeSpec
& CodeSpec(JSOp op
) {
282 return CodeSpecTable
[uint8_t(op
)];
285 extern const char* const CodeNameTable
[];
287 inline const char* CodeName(JSOp op
) { return CodeNameTable
[uint8_t(op
)]; }
289 /* Shorthand for type from opcode. */
291 static inline uint32_t JOF_OPTYPE(JSOp op
) {
292 return JOF_TYPE(CodeSpec(op
).format
);
295 static inline bool IsJumpOpcode(JSOp op
) { return JOF_OPTYPE(op
) == JOF_JUMP
; }
297 static inline bool BytecodeFallsThrough(JSOp op
) {
299 // * JSOp::Yield/JSOp::Await is considered to fall through, like JSOp::Call.
305 case JSOp::FinalYieldRval
:
307 case JSOp::ThrowWithStack
:
309 case JSOp::ThrowSetConst
:
310 case JSOp::TableSwitch
:
317 static inline bool BytecodeIsJumpTarget(JSOp op
) {
319 case JSOp::JumpTarget
:
321 case JSOp::AfterYield
:
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
;
337 MOZ_ASSERT(nuses
== -1);
340 return GET_UINT16(pc
);
342 case JSOp::NewContent
:
343 case JSOp::SuperCall
:
344 return 2 + GET_ARGC(pc
) + 1;
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
);
355 MOZ_ALWAYS_INLINE
unsigned StackDefs(JSOp op
) {
356 int ndefs
= CodeSpec(op
).ndefs
;
357 MOZ_ASSERT(ndefs
>= 0);
361 #if defined(DEBUG) || defined(JS_JITSPEW)
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
367 extern bool ReconstructStackDepth(JSContext
* cx
, JSScript
* script
,
368 jsbytecode
* pc
, uint32_t* depth
,
374 #define JSDVG_IGNORE_STACK 0
375 #define JSDVG_SEARCH_STACK 1
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.
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.
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.
394 * The caller must call JS_free on the result after a successful call.
396 UniqueChars
DecompileValueGenerator(JSContext
* cx
, int spindex
, HandleValue v
,
397 HandleString fallback
,
398 int skipStackHits
= 0);
401 * Decompile the formal argument at formalIndex in the nearest non-builtin
402 * stack frame, falling back with converting v to source.
404 JSString
* DecompileArgument(JSContext
* cx
, int formalIndex
, HandleValue v
);
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
;
412 static inline unsigned GetBytecodeLength(const jsbytecode
* pc
) {
414 return GetOpLength(op
);
417 static inline bool BytecodeIsPopped(jsbytecode
* pc
) {
418 jsbytecode
* next
= pc
+ GetBytecodeLength(pc
);
419 return JSOp(*next
) == JSOp::Pop
;
422 extern bool IsValidBytecodeOffset(JSContext
* cx
, JSScript
* script
,
425 inline bool IsArgOp(JSOp op
) { return JOF_OPTYPE(op
) == JOF_QARG
; }
427 inline bool IsLocalOp(JSOp op
) { return JOF_OPTYPE(op
) == JOF_LOCAL
; }
429 inline bool IsAliasedVarOp(JSOp op
) { return JOF_OPTYPE(op
) == JOF_ENVCOORD
; }
431 inline bool IsGlobalOp(JSOp op
) { return CodeSpec(op
).format
& JOF_GNAME
; }
433 inline bool IsPropertySetOp(JSOp op
) {
434 return CodeSpec(op
).format
& JOF_PROPSET
;
437 inline bool IsPropertyInitOp(JSOp op
) {
438 return CodeSpec(op
).format
& JOF_PROPINIT
;
441 inline bool IsLooseEqualityOp(JSOp op
) {
442 return op
== JSOp::Eq
|| op
== JSOp::Ne
;
445 inline bool IsStrictEqualityOp(JSOp op
) {
446 return op
== JSOp::StrictEq
|| op
== JSOp::StrictNe
;
449 inline bool IsEqualityOp(JSOp op
) {
450 return IsLooseEqualityOp(op
) || IsStrictEqualityOp(op
);
453 inline bool IsRelationalOp(JSOp op
) {
454 return op
== JSOp::Lt
|| op
== JSOp::Le
|| op
== JSOp::Gt
|| op
== JSOp::Ge
;
457 inline bool IsCheckStrictOp(JSOp op
) {
458 return CodeSpec(op
).format
& JOF_CHECKSTRICT
;
462 inline bool IsCheckSloppyOp(JSOp op
) {
463 return CodeSpec(op
).format
& JOF_CHECKSLOPPY
;
467 inline bool IsAtomOp(JSOp op
) { return JOF_OPTYPE(op
) == JOF_ATOM
; }
469 inline bool IsGetPropOp(JSOp op
) { return op
== JSOp::GetProp
; }
471 inline bool IsGetPropPC(const jsbytecode
* pc
) { return IsGetPropOp(JSOp(*pc
)); }
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
;
479 inline bool IsLockedInitOp(JSOp op
) {
480 return op
== JSOp::InitLockedProp
|| op
== JSOp::InitLockedElem
;
483 inline bool IsStrictSetPC(jsbytecode
* pc
) {
485 return op
== JSOp::StrictSetProp
|| op
== JSOp::StrictSetName
||
486 op
== JSOp::StrictSetGName
|| op
== JSOp::StrictSetElem
;
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
;
495 inline bool IsSetPropPC(const jsbytecode
* pc
) { return IsSetPropOp(JSOp(*pc
)); }
497 inline bool IsGetElemOp(JSOp op
) { return op
== JSOp::GetElem
; }
499 inline bool IsGetElemPC(const jsbytecode
* pc
) { return IsGetElemOp(JSOp(*pc
)); }
501 inline bool IsSetElemOp(JSOp op
) {
502 return op
== JSOp::SetElem
|| op
== JSOp::StrictSetElem
;
505 inline bool IsSetElemPC(const jsbytecode
* pc
) { return IsSetElemOp(JSOp(*pc
)); }
507 inline bool IsInvokeOp(JSOp op
) { return CodeSpec(op
).format
& JOF_INVOKE
; }
509 inline bool IsInvokePC(jsbytecode
* pc
) { return IsInvokeOp(JSOp(*pc
)); }
511 inline bool IsStrictEvalPC(jsbytecode
* pc
) {
513 return op
== JSOp::StrictEval
|| op
== JSOp::StrictSpreadEval
;
516 inline bool IsConstructOp(JSOp op
) {
517 return CodeSpec(op
).format
& JOF_CONSTRUCT
;
519 inline bool IsConstructPC(const jsbytecode
* pc
) {
520 return IsConstructOp(JSOp(*pc
));
523 inline bool IsSpreadOp(JSOp op
) { return CodeSpec(op
).format
& JOF_SPREAD
; }
525 inline bool IsSpreadPC(const jsbytecode
* pc
) { return IsSpreadOp(JSOp(*pc
)); }
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
;
533 inline bool OpUsesEnvironmentChain(JSOp op
) {
534 return CodeSpec(op
).format
& JOF_USES_ENV
;
537 static inline int32_t GetBytecodeInteger(jsbytecode
* pc
) {
544 return GET_UINT16(pc
);
546 return GET_UINT24(pc
);
550 return GET_INT32(pc
);
556 inline bool BytecodeOpHasIC(JSOp op
) { return CodeSpec(op
).format
& JOF_IC
; }
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));
564 MOZ_ASSERT(JSOp(*pc
) == JSOp::CheckPrivateField
);
565 uint8_t throwConditionByte
= GET_UINT8(pc
);
566 uint8_t throwKindByte
= GET_UINT8(pc
+ 1);
568 *throwCondition
= static_cast<ThrowCondition
>(throwConditionByte
);
569 *throwKind
= static_cast<ThrowMsgKind
>(throwKindByte
);
571 MOZ_ASSERT(*throwCondition
== ThrowCondition::ThrowHas
||
572 *throwCondition
== ThrowCondition::ThrowHasNot
||
573 *throwCondition
== ThrowCondition::OnlyCheckRhs
);
575 MOZ_ASSERT(*throwKind
== ThrowMsgKind::PrivateDoubleInit
||
576 *throwKind
== ThrowMsgKind::PrivateBrandDoubleInit
||
577 *throwKind
== ThrowMsgKind::MissingPrivateOnGet
||
578 *throwKind
== ThrowMsgKind::MissingPrivateOnSet
);
581 // Return true iff the combination of the ThrowCondition and hasOwn result
582 // will throw an exception.
583 static inline bool CheckPrivateFieldWillThrow(ThrowCondition condition
,
585 if ((condition
== ThrowCondition::ThrowHasNot
&& !hasOwn
) ||
586 (condition
== ThrowCondition::ThrowHas
&& hasOwn
)) {
587 // Met a throw condition.
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
601 * Offset of the pc inside the script. This fields is used to lookup opcode
602 * which have annotations.
607 * Record the number of execution of one instruction, or the number of
613 explicit PCCounts(size_t off
) : pcOffset_(off
), numExec_(0) {}
615 size_t pcOffset() const { return pcOffset_
; }
617 // Used for sorting and searching.
618 bool operator<(const PCCounts
& rhs
) const {
619 return pcOffset_
< rhs
.pcOffset_
;
622 uint64_t& numExec() { return numExec_
; }
623 uint64_t numExec() const { return numExec_
; }
625 static const char numExecName
[];
628 static inline jsbytecode
* GetNextPc(jsbytecode
* pc
) {
629 return pc
+ GetBytecodeLength(pc
);
632 inline GeneratorResumeKind
IntToResumeKind(int32_t value
) {
633 MOZ_ASSERT(uint32_t(value
) <= uint32_t(GeneratorResumeKind::Return
));
634 return static_cast<GeneratorResumeKind
>(value
);
637 inline GeneratorResumeKind
ResumeKindFromPC(jsbytecode
* pc
) {
638 MOZ_ASSERT(JSOp(*pc
) == JSOp::ResumeKind
);
639 return IntToResumeKind(GET_UINT8(pc
));
642 #if defined(DEBUG) || defined(JS_JITSPEW)
644 enum class DisassembleSkeptically
{ No
, Yes
};
647 * Disassemblers, for debugging only.
649 [[nodiscard
]] extern bool Disassemble(
650 JSContext
* cx
, JS::Handle
<JSScript
*> script
, bool lines
, StringPrinter
* sp
,
651 DisassembleSkeptically skeptically
= DisassembleSkeptically::No
);
653 unsigned Disassemble1(JSContext
* cx
, JS::Handle
<JSScript
*> script
,
654 jsbytecode
* pc
, unsigned loc
, bool lines
,
657 extern UniqueChars
ToDisassemblySource(JSContext
* cx
, HandleValue v
);
661 [[nodiscard
]] extern bool DumpRealmPCCounts(JSContext
* cx
);
665 #endif /* vm_BytecodeUtil_h */