#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum OpCode {
Nop = 0x00,
Const = 0x01,
LoadLocal = 0x02,
StoreLocal = 0x03,
LoadGlobal = 0x04,
StoreGlobal = 0x05,
LoadField = 0x06,
StoreField = 0x07,
LoadIndex = 0x08,
StoreIndex = 0x09,
LoadUpvalue = 0x0A,
StoreUpvalue = 0x0B,
Move = 0x0C,
Pop = 0x0D,
Dup = 0x0E,
Swap = 0x0F,
Add = 0x10,
Sub = 0x11,
Mul = 0x12,
Div = 0x13,
Mod = 0x14,
Neg = 0x15,
BitAnd = 0x16,
BitOr = 0x17,
BitXor = 0x18,
BitNot = 0x19,
ShiftLeft = 0x1A,
ShiftRight = 0x1B,
NewObject = 0x1C,
NewArray = 0x1D,
NewClosure = 0x1E,
GetType = 0x1F,
Equal = 0x20,
NotEqual = 0x21,
Greater = 0x22,
GreaterEqual = 0x23,
Less = 0x24,
LessEqual = 0x25,
Not = 0x26,
And = 0x27,
Or = 0x28,
InstanceOf = 0x29,
InlineCache = 0x2A,
Specialize = 0x2B,
Deoptimize = 0x2C,
NewTuple = 0x2D,
Jump = 0x30,
JumpIfTrue = 0x31,
JumpIfFalse = 0x32,
Call = 0x33,
TailCall = 0x34,
Return = 0x35,
Throw = 0x36,
EnterTry = 0x37,
ExitTry = 0x38,
For = 0x39,
MethodCall = 0x3A,
Match = 0x3B,
}
impl OpCode {
#[inline]
pub const fn to_u8(self) -> u8 {
self as u8
}
pub fn from_u8(value: u8) -> Option<Self> {
match value {
0x00 => Some(Self::Nop),
0x01 => Some(Self::Const),
0x02 => Some(Self::LoadLocal),
0x03 => Some(Self::StoreLocal),
0x04 => Some(Self::LoadGlobal),
0x05 => Some(Self::StoreGlobal),
0x06 => Some(Self::LoadField),
0x07 => Some(Self::StoreField),
0x08 => Some(Self::LoadIndex),
0x09 => Some(Self::StoreIndex),
0x0A => Some(Self::LoadUpvalue),
0x0B => Some(Self::StoreUpvalue),
0x0C => Some(Self::Move),
0x0D => Some(Self::Pop),
0x0E => Some(Self::Dup),
0x0F => Some(Self::Swap),
0x10 => Some(Self::Add),
0x11 => Some(Self::Sub),
0x12 => Some(Self::Mul),
0x13 => Some(Self::Div),
0x14 => Some(Self::Mod),
0x15 => Some(Self::Neg),
0x16 => Some(Self::BitAnd),
0x17 => Some(Self::BitOr),
0x18 => Some(Self::BitXor),
0x19 => Some(Self::BitNot),
0x1A => Some(Self::ShiftLeft),
0x1B => Some(Self::ShiftRight),
0x1C => Some(Self::NewObject),
0x1D => Some(Self::NewArray),
0x1E => Some(Self::NewClosure),
0x1F => Some(Self::GetType),
0x20 => Some(Self::Equal),
0x21 => Some(Self::NotEqual),
0x22 => Some(Self::Greater),
0x23 => Some(Self::GreaterEqual),
0x24 => Some(Self::Less),
0x25 => Some(Self::LessEqual),
0x26 => Some(Self::Not),
0x27 => Some(Self::And),
0x28 => Some(Self::Or),
0x29 => Some(Self::InstanceOf),
0x2A => Some(Self::InlineCache),
0x2B => Some(Self::Specialize),
0x2C => Some(Self::Deoptimize),
0x2D => Some(Self::NewTuple),
0x30 => Some(Self::Jump),
0x31 => Some(Self::JumpIfTrue),
0x32 => Some(Self::JumpIfFalse),
0x33 => Some(Self::Call),
0x34 => Some(Self::TailCall),
0x35 => Some(Self::Return),
0x36 => Some(Self::Throw),
0x37 => Some(Self::EnterTry),
0x38 => Some(Self::ExitTry),
0x39 => Some(Self::For),
0x3A => Some(Self::MethodCall),
0x3B => Some(Self::Match),
_ => None,
}
}
pub const fn name(self) -> &'static str {
match self {
Self::Nop => "Nop",
Self::Const => "Const",
Self::LoadLocal => "LoadLocal",
Self::StoreLocal => "StoreLocal",
Self::LoadGlobal => "LoadGlobal",
Self::StoreGlobal => "StoreGlobal",
Self::LoadField => "LoadField",
Self::StoreField => "StoreField",
Self::LoadIndex => "LoadIndex",
Self::StoreIndex => "StoreIndex",
Self::LoadUpvalue => "LoadUpvalue",
Self::StoreUpvalue => "StoreUpvalue",
Self::Move => "Move",
Self::Pop => "Pop",
Self::Dup => "Dup",
Self::Swap => "Swap",
Self::Add => "Add",
Self::Sub => "Sub",
Self::Mul => "Mul",
Self::Div => "Div",
Self::Mod => "Mod",
Self::Neg => "Neg",
Self::BitAnd => "BitAnd",
Self::BitOr => "BitOr",
Self::BitXor => "BitXor",
Self::BitNot => "BitNot",
Self::ShiftLeft => "ShiftLeft",
Self::ShiftRight => "ShiftRight",
Self::Equal => "Equal",
Self::NotEqual => "NotEqual",
Self::Greater => "Greater",
Self::GreaterEqual => "GreaterEqual",
Self::Less => "Less",
Self::LessEqual => "LessEqual",
Self::Not => "Not",
Self::And => "And",
Self::Or => "Or",
Self::Jump => "Jump",
Self::JumpIfTrue => "JumpIfTrue",
Self::JumpIfFalse => "JumpIfFalse",
Self::Call => "Call",
Self::TailCall => "TailCall",
Self::Return => "Return",
Self::Throw => "Throw",
Self::EnterTry => "EnterTry",
Self::ExitTry => "ExitTry",
Self::For => "For",
Self::MethodCall => "MethodCall",
Self::Match => "Match",
Self::NewObject => "NewObject",
Self::NewArray => "NewArray",
Self::NewClosure => "NewClosure",
Self::GetType => "GetType",
Self::InstanceOf => "InstanceOf",
Self::InlineCache => "InlineCache",
Self::Specialize => "Specialize",
Self::Deoptimize => "Deoptimize",
Self::NewTuple => "NewTuple",
}
}
}
#[cfg(test)]
mod tests {
use super::*;
const ALL_OPCODES: &[OpCode] = &[
OpCode::Nop,
OpCode::Const,
OpCode::LoadLocal,
OpCode::StoreLocal,
OpCode::LoadGlobal,
OpCode::StoreGlobal,
OpCode::LoadField,
OpCode::StoreField,
OpCode::LoadIndex,
OpCode::StoreIndex,
OpCode::LoadUpvalue,
OpCode::StoreUpvalue,
OpCode::Move,
OpCode::Pop,
OpCode::Dup,
OpCode::Swap,
OpCode::Add,
OpCode::Sub,
OpCode::Mul,
OpCode::Div,
OpCode::Mod,
OpCode::Neg,
OpCode::BitAnd,
OpCode::BitOr,
OpCode::BitXor,
OpCode::BitNot,
OpCode::ShiftLeft,
OpCode::ShiftRight,
OpCode::NewObject,
OpCode::NewArray,
OpCode::NewClosure,
OpCode::GetType,
OpCode::Equal,
OpCode::NotEqual,
OpCode::Greater,
OpCode::GreaterEqual,
OpCode::Less,
OpCode::LessEqual,
OpCode::Not,
OpCode::And,
OpCode::Or,
OpCode::InstanceOf,
OpCode::InlineCache,
OpCode::Specialize,
OpCode::Deoptimize,
OpCode::NewTuple,
OpCode::Jump,
OpCode::JumpIfTrue,
OpCode::JumpIfFalse,
OpCode::Call,
OpCode::TailCall,
OpCode::Return,
OpCode::Throw,
OpCode::EnterTry,
OpCode::ExitTry,
OpCode::For,
OpCode::MethodCall,
OpCode::Match,
];
#[test]
fn test_opcode_to_u8_roundtrip_all() {
for opcode in ALL_OPCODES {
let u8_val = opcode.to_u8();
let recovered = OpCode::from_u8(u8_val).expect("Failed to recover opcode");
assert_eq!(*opcode, recovered, "Opcode roundtrip failed for {opcode:?}");
}
}
#[test]
fn test_invalid_opcode() {
assert!(OpCode::from_u8(0xFF).is_none());
assert!(OpCode::from_u8(0x60).is_none());
assert!(OpCode::from_u8(0xAA).is_none());
assert!(OpCode::from_u8(0x3C).is_none()); assert!(OpCode::from_u8(0x2E).is_none()); }
#[test]
fn test_opcode_names_all() {
for opcode in ALL_OPCODES {
let name = opcode.name();
assert!(!name.is_empty(), "Opcode {opcode:?} has empty name");
}
}
#[test]
fn test_opcode_names_specific() {
assert_eq!(OpCode::Nop.name(), "Nop");
assert_eq!(OpCode::Const.name(), "Const");
assert_eq!(OpCode::LoadLocal.name(), "LoadLocal");
assert_eq!(OpCode::StoreLocal.name(), "StoreLocal");
assert_eq!(OpCode::LoadGlobal.name(), "LoadGlobal");
assert_eq!(OpCode::StoreGlobal.name(), "StoreGlobal");
assert_eq!(OpCode::LoadField.name(), "LoadField");
assert_eq!(OpCode::StoreField.name(), "StoreField");
assert_eq!(OpCode::LoadIndex.name(), "LoadIndex");
assert_eq!(OpCode::StoreIndex.name(), "StoreIndex");
assert_eq!(OpCode::LoadUpvalue.name(), "LoadUpvalue");
assert_eq!(OpCode::StoreUpvalue.name(), "StoreUpvalue");
assert_eq!(OpCode::Move.name(), "Move");
assert_eq!(OpCode::Pop.name(), "Pop");
assert_eq!(OpCode::Dup.name(), "Dup");
assert_eq!(OpCode::Swap.name(), "Swap");
assert_eq!(OpCode::Add.name(), "Add");
assert_eq!(OpCode::Sub.name(), "Sub");
assert_eq!(OpCode::Mul.name(), "Mul");
assert_eq!(OpCode::Div.name(), "Div");
assert_eq!(OpCode::Mod.name(), "Mod");
assert_eq!(OpCode::Neg.name(), "Neg");
assert_eq!(OpCode::BitAnd.name(), "BitAnd");
assert_eq!(OpCode::BitOr.name(), "BitOr");
assert_eq!(OpCode::BitXor.name(), "BitXor");
assert_eq!(OpCode::BitNot.name(), "BitNot");
assert_eq!(OpCode::ShiftLeft.name(), "ShiftLeft");
assert_eq!(OpCode::ShiftRight.name(), "ShiftRight");
assert_eq!(OpCode::NewObject.name(), "NewObject");
assert_eq!(OpCode::NewArray.name(), "NewArray");
assert_eq!(OpCode::NewClosure.name(), "NewClosure");
assert_eq!(OpCode::GetType.name(), "GetType");
assert_eq!(OpCode::Equal.name(), "Equal");
assert_eq!(OpCode::NotEqual.name(), "NotEqual");
assert_eq!(OpCode::Greater.name(), "Greater");
assert_eq!(OpCode::GreaterEqual.name(), "GreaterEqual");
assert_eq!(OpCode::Less.name(), "Less");
assert_eq!(OpCode::LessEqual.name(), "LessEqual");
assert_eq!(OpCode::Not.name(), "Not");
assert_eq!(OpCode::And.name(), "And");
assert_eq!(OpCode::Or.name(), "Or");
assert_eq!(OpCode::InstanceOf.name(), "InstanceOf");
assert_eq!(OpCode::InlineCache.name(), "InlineCache");
assert_eq!(OpCode::Specialize.name(), "Specialize");
assert_eq!(OpCode::Deoptimize.name(), "Deoptimize");
assert_eq!(OpCode::NewTuple.name(), "NewTuple");
assert_eq!(OpCode::Jump.name(), "Jump");
assert_eq!(OpCode::JumpIfTrue.name(), "JumpIfTrue");
assert_eq!(OpCode::JumpIfFalse.name(), "JumpIfFalse");
assert_eq!(OpCode::Call.name(), "Call");
assert_eq!(OpCode::TailCall.name(), "TailCall");
assert_eq!(OpCode::Return.name(), "Return");
assert_eq!(OpCode::Throw.name(), "Throw");
assert_eq!(OpCode::EnterTry.name(), "EnterTry");
assert_eq!(OpCode::ExitTry.name(), "ExitTry");
assert_eq!(OpCode::For.name(), "For");
assert_eq!(OpCode::MethodCall.name(), "MethodCall");
assert_eq!(OpCode::Match.name(), "Match");
}
#[test]
fn test_opcode_u8_values() {
assert_eq!(OpCode::Nop.to_u8(), 0x00);
assert_eq!(OpCode::Swap.to_u8(), 0x0F);
assert_eq!(OpCode::Add.to_u8(), 0x10);
assert_eq!(OpCode::GetType.to_u8(), 0x1F);
assert_eq!(OpCode::Equal.to_u8(), 0x20);
assert_eq!(OpCode::NewTuple.to_u8(), 0x2D);
assert_eq!(OpCode::Jump.to_u8(), 0x30);
assert_eq!(OpCode::Match.to_u8(), 0x3B);
}
#[test]
fn test_from_u8_boundary_values() {
assert!(OpCode::from_u8(0x00).is_some()); assert!(OpCode::from_u8(0x0F).is_some()); assert!(OpCode::from_u8(0x10).is_some()); assert!(OpCode::from_u8(0x1F).is_some()); assert!(OpCode::from_u8(0x20).is_some()); assert!(OpCode::from_u8(0x2D).is_some()); assert!(OpCode::from_u8(0x2E).is_none()); assert!(OpCode::from_u8(0x2F).is_none()); assert!(OpCode::from_u8(0x30).is_some()); assert!(OpCode::from_u8(0x3B).is_some()); assert!(OpCode::from_u8(0x3C).is_none()); }
#[test]
fn test_opcode_clone() {
let op = OpCode::Add;
let cloned = op.clone();
assert_eq!(op, cloned);
}
#[test]
fn test_opcode_copy() {
let op = OpCode::Sub;
let copied = op;
assert_eq!(op, copied);
}
#[test]
fn test_opcode_eq() {
assert_eq!(OpCode::Add, OpCode::Add);
assert_ne!(OpCode::Add, OpCode::Sub);
}
#[test]
fn test_opcode_hash() {
use std::collections::HashSet;
let mut set = HashSet::new();
set.insert(OpCode::Add);
set.insert(OpCode::Sub);
assert!(set.contains(&OpCode::Add));
assert!(set.contains(&OpCode::Sub));
assert!(!set.contains(&OpCode::Mul));
}
#[test]
fn test_opcode_debug() {
let debug_str = format!("{:?}", OpCode::Add);
assert!(debug_str.contains("Add"));
}
#[test]
fn test_stack_operations_range() {
for i in 0x00..=0x0F {
assert!(
OpCode::from_u8(i).is_some(),
"Stack op 0x{:02X} should exist",
i
);
}
}
#[test]
fn test_arithmetic_operations_range() {
for i in 0x10..=0x1F {
assert!(
OpCode::from_u8(i).is_some(),
"Arithmetic op 0x{:02X} should exist",
i
);
}
}
#[test]
fn test_control_flow_operations_range() {
for i in 0x30..=0x3B {
assert!(
OpCode::from_u8(i).is_some(),
"Control flow op 0x{:02X} should exist",
i
);
}
}
#[test]
fn test_opcode_nop_is_zero() {
assert_eq!(OpCode::Nop.to_u8(), 0);
}
#[test]
fn test_opcode_category_stack() {
let stack_ops = [
OpCode::Nop,
OpCode::Const,
OpCode::LoadLocal,
OpCode::StoreLocal,
OpCode::LoadGlobal,
OpCode::StoreGlobal,
OpCode::LoadField,
OpCode::StoreField,
OpCode::LoadIndex,
OpCode::StoreIndex,
OpCode::LoadUpvalue,
OpCode::StoreUpvalue,
OpCode::Move,
OpCode::Pop,
OpCode::Dup,
OpCode::Swap,
];
for op in stack_ops {
assert!(op.to_u8() <= 0x0F, "{:?} should be in stack range", op);
}
}
#[test]
fn test_opcode_category_arithmetic() {
let arith_ops = [
OpCode::Add,
OpCode::Sub,
OpCode::Mul,
OpCode::Div,
OpCode::Mod,
OpCode::Neg,
OpCode::BitAnd,
OpCode::BitOr,
OpCode::BitXor,
OpCode::BitNot,
OpCode::ShiftLeft,
OpCode::ShiftRight,
];
for op in arith_ops {
let val = op.to_u8();
assert!(
val >= 0x10 && val <= 0x1F,
"{:?} should be in arithmetic range",
op
);
}
}
#[test]
fn test_opcode_category_comparison() {
let cmp_ops = [
OpCode::Equal,
OpCode::NotEqual,
OpCode::Greater,
OpCode::GreaterEqual,
OpCode::Less,
OpCode::LessEqual,
OpCode::Not,
OpCode::And,
OpCode::Or,
];
for op in cmp_ops {
let val = op.to_u8();
assert!(
val >= 0x20 && val <= 0x2F,
"{:?} should be in comparison range",
op
);
}
}
#[test]
fn test_opcode_category_control() {
let ctrl_ops = [
OpCode::Jump,
OpCode::JumpIfTrue,
OpCode::JumpIfFalse,
OpCode::Call,
OpCode::TailCall,
OpCode::Return,
OpCode::Throw,
OpCode::EnterTry,
OpCode::ExitTry,
OpCode::For,
OpCode::MethodCall,
OpCode::Match,
];
for op in ctrl_ops {
let val = op.to_u8();
assert!(
val >= 0x30 && val <= 0x3F,
"{:?} should be in control range",
op
);
}
}
#[test]
fn test_all_opcodes_count() {
assert_eq!(ALL_OPCODES.len(), 58);
}
#[test]
fn test_opcode_unique_values() {
use std::collections::HashSet;
let mut values = HashSet::new();
for op in ALL_OPCODES {
let val = op.to_u8();
assert!(values.insert(val), "Duplicate opcode value 0x{:02X}", val);
}
}
}