// Opcode constants and human-readable names for M1's bytecode instruction set.
//
// Design constraints (see design.md §4.3):
//
// - Encoding: each instruction is a 32-bit `UInt`, laid out
// `[opcode:8 | A:8 | B:8 | C:8]` (little-endian byte view when serialised).
// - Numbering: M1 occupies `0x00..0x7F`. Later milestones append to `0x80..`
// and MUST NOT reuse an M1 opcode number.
// - Wide prefix (`OP_WIDE = 0xFE`): signals "the next instruction's operand A
// is actually 24 bits, formed by concatenating THIS instruction's A/B/C
// with the next instruction's A byte". Convention documented in
// `chunk.mbt` where the emit helpers live.
//
// The `opcode_name` helper below drives the disassembler (see
// `disassemble.mbt`). Adding a new opcode requires:
// 1. A new `OP_*` constant here with a comment linking to design.md §4.3.
// 2. A match arm in `opcode_name`.
// 3. A case in `Chunk::disassemble`'s operand printer if the operand shape
// differs from an existing entry.
// ---------------------------------------------------------------------------
// 0x00-0x0F: push / stack
// ---------------------------------------------------------------------------
///|
/// No-op. Handy as a padding instruction for jump patching that later needs to
/// be widened; the compiler occasionally emits `nop`s to reserve wide slots.
pub const OP_NOP : Byte = 0x00
///|
pub const OP_PUSH_UNDEF : Byte = 0x01
///|
pub const OP_PUSH_NULL : Byte = 0x02
///|
pub const OP_PUSH_TRUE : Byte = 0x03
///|
pub const OP_PUSH_FALSE : Byte = 0x04
///|
/// Push a 32-bit signed integer. Operand encoding:
/// `push_i32 hi mid lo` — a 24-bit signed value (fits in one instruction).
/// `wide W_HI W_MID W_LO; push_i32 LOW8 0 0` — 32-bit signed (two-instr).
/// See `Chunk::emit_wide_i32` / `Chunk::read_operand_i24`.
pub const OP_PUSH_I32 : Byte = 0x05
///|
/// Push a value from the chunk's constant pool. Operand = const pool index.
pub const OP_PUSH_CONST : Byte = 0x06
///|
pub const OP_DUP : Byte = 0x07
///|
pub const OP_DROP : Byte = 0x08
///|
pub const OP_SWAP : Byte = 0x09
///|
/// Alias of `drop`, kept for QuickJS-name compatibility in the disassembler.
pub const OP_POP : Byte = 0x0A
// ---------------------------------------------------------------------------
// 0x10-0x1F: variable access
// ---------------------------------------------------------------------------
///|
pub const OP_GET_LOCAL : Byte = 0x10
///|
pub const OP_SET_LOCAL : Byte = 0x11
///|
pub const OP_GET_UPVALUE : Byte = 0x12
///|
pub const OP_SET_UPVALUE : Byte = 0x13
///|
pub const OP_GET_GLOBAL : Byte = 0x14
///|
pub const OP_SET_GLOBAL : Byte = 0x15
///|
pub const OP_DECLARE_GLOBAL : Byte = 0x16
///|
/// Push the global's value if it exists, or `Undefined` if it does not.
///
/// Distinct from `OP_GET_GLOBAL`, which throws `ReferenceError` on a miss. The
/// compiler emits `OP_GET_GLOBAL_OR_UNDEF` for `typeof foo` and related
/// side-effect-free reads that must succeed with `undefined` for
/// never-declared identifiers per ES semantics (`typeof undeclared === "undefined"`
/// must not throw). Introduced in M1 Step 7; see design.md §4.3 (0x17
/// allocation) and Step 7 notes on `typeof` emission.
pub const OP_GET_GLOBAL_OR_UNDEF : Byte = 0x17
///|
/// Push the current frame's `this` value. Introduced alongside
/// `OP_GET_GLOBAL_OR_UNDEF` (M1 Step 7) so the compiler doesn't have to encode
/// `this` as a local slot — VM Step 8 stores `this` in a dedicated
/// `Frame.this_val` field per design.md §8.1.
pub const OP_GET_THIS : Byte = 0x18
///|
/// Coerce the top-of-stack value to a JS Number (per `ToNumber` in ES spec).
/// Emitted by the compiler for unary `+x` — where `x` might be any JS value
/// including a String (in which case JS `+` on strings concatenates, so we
/// cannot lower `+x` to `0 + x`; that would produce `"0" + "5" = "05"`
/// instead of `5`). Introduced in M1 Step 7 fix; see design.md §4.3.
pub const OP_TO_NUMBER : Byte = 0x19
// ---------------------------------------------------------------------------
// 0x20-0x2F: arithmetic & bitwise
// ---------------------------------------------------------------------------
///|
pub const OP_ADD : Byte = 0x20
///|
pub const OP_SUB : Byte = 0x21
///|
pub const OP_MUL : Byte = 0x22
///|
pub const OP_DIV : Byte = 0x23
///|
pub const OP_MOD : Byte = 0x24
///|
pub const OP_POW : Byte = 0x25
///|
pub const OP_NEG : Byte = 0x26
///|
pub const OP_BNOT : Byte = 0x27
///|
pub const OP_BAND : Byte = 0x28
///|
pub const OP_BOR : Byte = 0x29
///|
pub const OP_BXOR : Byte = 0x2A
///|
pub const OP_SHL : Byte = 0x2B
///|
pub const OP_SHR : Byte = 0x2C
///|
pub const OP_USHR : Byte = 0x2D
///|
pub const OP_EQ : Byte = 0x2E
///|
pub const OP_NE : Byte = 0x2F
// ---------------------------------------------------------------------------
// 0x30-0x3F: comparison / logical / type
// ---------------------------------------------------------------------------
///|
pub const OP_SEQ : Byte = 0x30
///|
pub const OP_SNE : Byte = 0x31
///|
pub const OP_LT : Byte = 0x32
///|
pub const OP_LE : Byte = 0x33
///|
pub const OP_GT : Byte = 0x34
///|
pub const OP_GE : Byte = 0x35
///|
pub const OP_NOT : Byte = 0x36
///|
pub const OP_TYPEOF : Byte = 0x37
///|
pub const OP_INSTANCEOF : Byte = 0x38
///|
pub const OP_IN : Byte = 0x39
// ---------------------------------------------------------------------------
// 0x40-0x4F: objects & arrays
// ---------------------------------------------------------------------------
///|
pub const OP_NEW_OBJECT : Byte = 0x40
///|
pub const OP_DEFINE_PROP : Byte = 0x41
///|
pub const OP_GET_PROP : Byte = 0x42
///|
pub const OP_SET_PROP : Byte = 0x43
///|
pub const OP_GET_ELEM : Byte = 0x44
///|
pub const OP_SET_ELEM : Byte = 0x45
///|
pub const OP_DELETE_PROP : Byte = 0x46
///|
pub const OP_DELETE_ELEM : Byte = 0x47
///|
pub const OP_NEW_ARRAY : Byte = 0x48
///|
pub const OP_ARRAY_PUSH : Byte = 0x49
// ---------------------------------------------------------------------------
// 0x50-0x5F: function / call / return
// ---------------------------------------------------------------------------
///|
pub const OP_NEW_CLOSURE : Byte = 0x50
///|
pub const OP_CALL : Byte = 0x51
///|
pub const OP_CALL_METHOD : Byte = 0x52
///|
pub const OP_CONSTRUCT : Byte = 0x53
///|
pub const OP_RETURN_VAL : Byte = 0x54
///|
pub const OP_RETURN_UNDEF : Byte = 0x55
// ---------------------------------------------------------------------------
// 0x60-0x6F: control flow
// ---------------------------------------------------------------------------
///|
pub const OP_JUMP : Byte = 0x60
///|
pub const OP_JUMP_IF_TRUE : Byte = 0x61
///|
pub const OP_JUMP_IF_FALSE : Byte = 0x62
// ---------------------------------------------------------------------------
// 0x70-0x7F: exception
// ---------------------------------------------------------------------------
///|
pub const OP_THROW : Byte = 0x70
///|
pub const OP_ENTER_TRY : Byte = 0x71
///|
pub const OP_LEAVE_TRY : Byte = 0x72
// ---------------------------------------------------------------------------
// prefix / meta
// ---------------------------------------------------------------------------
///|
/// Prefix opcode used to widen the operand of the following instruction to a
/// full 32-bit value. See `chunk.mbt` for the exact convention.
pub const OP_WIDE : Byte = 0xFE
///|
/// Reserved terminator opcode. Never emitted by the M1 compiler; useful as a
/// sentinel in tests to check "we ran past the end of the code array".
pub const OP_HALT : Byte = 0xFF
///|
/// Human-readable name of an opcode, used by the disassembler. Unknown /
/// reserved opcodes render as ``.
///
/// New opcodes must be added here manually — an if-chain has no compile-time
/// exhaustiveness check, so the disassembler tests in `bytecode_test.mbt`
/// double as a coverage tripwire when adding a new constant.
pub fn opcode_name(op : Byte) -> String {
if op == OP_NOP {
"NOP"
} else if op == OP_PUSH_UNDEF {
"PUSH_UNDEF"
} else if op == OP_PUSH_NULL {
"PUSH_NULL"
} else if op == OP_PUSH_TRUE {
"PUSH_TRUE"
} else if op == OP_PUSH_FALSE {
"PUSH_FALSE"
} else if op == OP_PUSH_I32 {
"PUSH_I32"
} else if op == OP_PUSH_CONST {
"PUSH_CONST"
} else if op == OP_DUP {
"DUP"
} else if op == OP_DROP {
"DROP"
} else if op == OP_SWAP {
"SWAP"
} else if op == OP_POP {
"POP"
} else if op == OP_GET_LOCAL {
"GET_LOCAL"
} else if op == OP_SET_LOCAL {
"SET_LOCAL"
} else if op == OP_GET_UPVALUE {
"GET_UPVALUE"
} else if op == OP_SET_UPVALUE {
"SET_UPVALUE"
} else if op == OP_GET_GLOBAL {
"GET_GLOBAL"
} else if op == OP_SET_GLOBAL {
"SET_GLOBAL"
} else if op == OP_DECLARE_GLOBAL {
"DECLARE_GLOBAL"
} else if op == OP_GET_GLOBAL_OR_UNDEF {
"GET_GLOBAL_OR_UNDEF"
} else if op == OP_GET_THIS {
"GET_THIS"
} else if op == OP_TO_NUMBER {
"TO_NUMBER"
} else if op == OP_ADD {
"ADD"
} else if op == OP_SUB {
"SUB"
} else if op == OP_MUL {
"MUL"
} else if op == OP_DIV {
"DIV"
} else if op == OP_MOD {
"MOD"
} else if op == OP_POW {
"POW"
} else if op == OP_NEG {
"NEG"
} else if op == OP_BNOT {
"BNOT"
} else if op == OP_BAND {
"BAND"
} else if op == OP_BOR {
"BOR"
} else if op == OP_BXOR {
"BXOR"
} else if op == OP_SHL {
"SHL"
} else if op == OP_SHR {
"SHR"
} else if op == OP_USHR {
"USHR"
} else if op == OP_EQ {
"EQ"
} else if op == OP_NE {
"NE"
} else if op == OP_SEQ {
"SEQ"
} else if op == OP_SNE {
"SNE"
} else if op == OP_LT {
"LT"
} else if op == OP_LE {
"LE"
} else if op == OP_GT {
"GT"
} else if op == OP_GE {
"GE"
} else if op == OP_NOT {
"NOT"
} else if op == OP_TYPEOF {
"TYPEOF"
} else if op == OP_INSTANCEOF {
"INSTANCEOF"
} else if op == OP_IN {
"IN"
} else if op == OP_NEW_OBJECT {
"NEW_OBJECT"
} else if op == OP_DEFINE_PROP {
"DEFINE_PROP"
} else if op == OP_GET_PROP {
"GET_PROP"
} else if op == OP_SET_PROP {
"SET_PROP"
} else if op == OP_GET_ELEM {
"GET_ELEM"
} else if op == OP_SET_ELEM {
"SET_ELEM"
} else if op == OP_DELETE_PROP {
"DELETE_PROP"
} else if op == OP_DELETE_ELEM {
"DELETE_ELEM"
} else if op == OP_NEW_ARRAY {
"NEW_ARRAY"
} else if op == OP_ARRAY_PUSH {
"ARRAY_PUSH"
} else if op == OP_NEW_CLOSURE {
"NEW_CLOSURE"
} else if op == OP_CALL {
"CALL"
} else if op == OP_CALL_METHOD {
"CALL_METHOD"
} else if op == OP_CONSTRUCT {
"CONSTRUCT"
} else if op == OP_RETURN_VAL {
"RETURN_VAL"
} else if op == OP_RETURN_UNDEF {
"RETURN_UNDEF"
} else if op == OP_JUMP {
"JUMP"
} else if op == OP_JUMP_IF_TRUE {
"JUMP_IF_TRUE"
} else if op == OP_JUMP_IF_FALSE {
"JUMP_IF_FALSE"
} else if op == OP_THROW {
"THROW"
} else if op == OP_ENTER_TRY {
"ENTER_TRY"
} else if op == OP_LEAVE_TRY {
"LEAVE_TRY"
} else if op == OP_WIDE {
"WIDE"
} else if op == OP_HALT {
"HALT"
} else {
""
}
}