use core::fmt::Debug; pub struct Bytecode { pub bytes: Box<[u8]> } impl Bytecode { pub fn next_instruction(&self, offset: usize) -> (Instruction, usize) { let opcode = self.bytes[offset]; match opcode { 0x00 => (Instruction::NoOperation(), 1), 0x01 => (Instruction::PushNull(), 1), 0x02 => (Instruction::PushConstIntM1(), 1), 0x03 => (Instruction::PushConstInt0(), 1), 0x04 => (Instruction::PushConstInt1(), 1), 0x05 => (Instruction::PushConstInt2(), 1), 0x06 => (Instruction::PushConstInt3(), 1), 0x07 => (Instruction::PushConstInt4(), 1), 0x08 => (Instruction::PushConstInt5(), 1), 0x0E => (Instruction::PushConstDouble0(), 1), 0x0F => (Instruction::PushConstDouble1(), 1), 0x10 => (Instruction::LoadByteImmediate(self.bytes[offset+1]), 2), 0x11 => (Instruction::LoadShortImmediate((self.bytes[offset+1] as u16) << 8 | self.bytes[offset+2] as u16), 3), 0x12 => (Instruction::LoadConstant(self.bytes[offset+1]), 2), 0x13 => (Instruction::LoadCostantWide((self.bytes[offset+1] as u16) << 8 | self.bytes[offset+2] as u16), 3), 0x14 => (Instruction::LoadConstant64((self.bytes[offset+1] as u16) << 8 | self.bytes[offset+2] as u16), 3), 0x1A => (Instruction::LoadLocalInt0(), 1), 0x1B => (Instruction::LoadLocalInt1(), 1), 0x1C => (Instruction::LoadLocalInt2(), 1), 0x1D => (Instruction::LoadLocalInt3(), 1), 0x26 => (Instruction::LoadLocalDouble0(), 1), 0x27 => (Instruction::LoadLocalDouble1(), 1), 0x28 => (Instruction::LoadLocalDouble2(), 1), 0x29 => (Instruction::LoadLocalDouble3(), 1), 0x2A => (Instruction::LoadLocalReference0(), 1), 0x2B => (Instruction::LoadLocalReference1(), 1), 0x2C => (Instruction::LoadLocalReference2(), 1), 0x2D => (Instruction::LoadLocalReference3(), 1), 0x32 => (Instruction::ArrayElement(), 1), 0x36 => (Instruction::StoreLocalInt(self.bytes[offset+1]), 2), 0x3B => (Instruction::StoreLocalInt0(), 1), 0x3C => (Instruction::StoreLocalInt1(), 1), 0x3D => (Instruction::StoreLocalInt2(), 1), 0x3E => (Instruction::StoreLocalInt3(), 1), 0x4B => (Instruction::StoreReference0(), 1), 0x4C => (Instruction::StoreReference1(), 1), 0x4D => (Instruction::StoreReference2(), 1), 0x4E => (Instruction::StoreReference3(), 1), 0x53 => (Instruction::StoreIntoRArray(), 1), 0x54 => (Instruction::StoreIntoBArray(), 1), 0x57 => (Instruction::Pop(), 1), 0x59 => (Instruction::Duplicate(), 1), 0x68 => (Instruction::MultiplyInt(), 1), 0x6C => (Instruction::DivideInt(), 1), 0x6D => (Instruction::DivideLong(), 1), 0x7A => (Instruction::ShiftIntRight(), 1), 0x80 => (Instruction::OrInt(), 1), 0x99 => { let bytes = [self.bytes[offset+1], self.bytes[offset+2]]; (Instruction::BranchZero(i16::from_be_bytes(bytes)), 3) } 0x9A => { let bytes = [self.bytes[offset+1], self.bytes[offset+2]]; (Instruction::BranchNonZero(i16::from_be_bytes(bytes)), 3) } 0x9B => { let bytes = [self.bytes[offset+1], self.bytes[offset+2]]; (Instruction::BranchNegative(i16::from_be_bytes(bytes)), 3) } 0x9C => { let bytes = [self.bytes[offset+1], self.bytes[offset+2]]; (Instruction::BranchNonPositive(i16::from_be_bytes(bytes)), 3) } 0x9D => { let bytes = [self.bytes[offset+1], self.bytes[offset+2]]; (Instruction::BranchPositive(i16::from_be_bytes(bytes)), 3) } 0x9E => { let bytes = [self.bytes[offset+1], self.bytes[offset+2]]; (Instruction::BranchNonNegative(i16::from_be_bytes(bytes)), 3) } 0x9F => { let bytes = [self.bytes[offset+1], self.bytes[offset+2]]; (Instruction::BranchIntEquality(i16::from_be_bytes(bytes)), 3) } 0xA0 => { let bytes = [self.bytes[offset+1], self.bytes[offset+2]]; (Instruction::BranchIntInequality(i16::from_be_bytes(bytes)), 3) } 0xA1 => { let bytes = [self.bytes[offset+1], self.bytes[offset+2]]; (Instruction::BranchIntLessThan(i16::from_be_bytes(bytes)), 3) } 0xA2 => { let bytes = [self.bytes[offset+1], self.bytes[offset+2]]; (Instruction::BranchIntGreaterEquals(i16::from_be_bytes(bytes)), 3) } 0xA3 => { let bytes = [self.bytes[offset+1], self.bytes[offset+2]]; (Instruction::BranchIntGreaterThan(i16::from_be_bytes(bytes)), 3) } 0xA4 => { let bytes = [self.bytes[offset+1], self.bytes[offset+2]]; (Instruction::BranchIntLessEquals(i16::from_be_bytes(bytes)), 3) } 0xAC => (Instruction::ReturnInt(), 1), 0xA7 => { let bytes = [self.bytes[offset+1], self.bytes[offset+2]]; (Instruction::BranchAlways(i16::from_be_bytes(bytes)), 3) } 0xB0 => (Instruction::ReturnReference(), 1), 0xB1 => (Instruction::ReturnVoid(), 1), 0xB2 => (Instruction::GetStatic((self.bytes[offset+1] as u16) << 8 | self.bytes[offset+2] as u16), 3), 0xB3 => (Instruction::PutStatic((self.bytes[offset+1] as u16) << 8 | self.bytes[offset+2] as u16), 3), 0xB4 => (Instruction::GetField((self.bytes[offset+1] as u16) << 8 | self.bytes[offset+2] as u16), 3), 0xB5 => (Instruction::PutField((self.bytes[offset+1] as u16) << 8 | self.bytes[offset+2] as u16), 3), 0xB6 => (Instruction::InvokeVirtual((self.bytes[offset+1] as u16) << 8 | self.bytes[offset+2] as u16), 3), 0xB7 => (Instruction::InvokeSpecial((self.bytes[offset+1] as u16) << 8 | self.bytes[offset+2] as u16), 3), 0xB8 => (Instruction::InvokeStatic((self.bytes[offset+1] as u16) << 8 | self.bytes[offset+2] as u16), 3), 0xBA => (Instruction::InvokeDynamic((self.bytes[offset+1] as u16) << 8 | self.bytes[offset+2] as u16, (self.bytes[offset+3] as u16) << 8 | self.bytes[offset+4] as u16), 5), 0xBB => (Instruction::NewObject((self.bytes[offset+1] as u16) << 8 | self.bytes[offset+2] as u16), 3), 0xBC => (Instruction::NewPrimitiveArray(self.bytes[offset+1]), 2), 0xBD => (Instruction::NewArray((self.bytes[offset+1] as u16) << 8 | self.bytes[offset+2] as u16), 3), 0xBE => (Instruction::ArrayLength(), 1), 0xC2 => (Instruction::EnterMonitor(), 1), 0xC3 => (Instruction::ExitMonitor(), 1), 0xC6 => { let bytes = [self.bytes[offset+1], self.bytes[offset+2]]; (Instruction::BranchNull(i16::from_be_bytes(bytes)), 3) } 0xC7 => { let bytes = [self.bytes[offset+1], self.bytes[offset+2]]; (Instruction::BranchNonNull(i16::from_be_bytes(bytes)), 3) } _ => (Instruction::Unknown(opcode), 1) } } pub fn instructions(&self) -> Box<[Instruction]> { let mut v = Vec::with_capacity(self.bytes.len()); let mut i = 0; while i < self.bytes.len() { let (instruction, offset) = self.next_instruction(i); v.push(instruction); i += offset; } v.into_boxed_slice() } } impl Debug for Bytecode { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { f.debug_list() .entries(self.instructions()) .finish() } } #[derive(Debug)] #[repr(u8)] pub enum Instruction { NoOperation() = 0x00, // No-Operation PushNull() = 0x01, // ..., arrayref, index, value PushConstIntM1() = 0x02, // Push -1 PushConstInt0() = 0x03, // Push 0 PushConstInt1() = 0x04, // Push 1 PushConstInt2() = 0x05, // Push 2 PushConstInt3() = 0x06, // Push 3 PushConstInt4() = 0x07, // Push 4 PushConstInt5() = 0x08, // Push 5 PushConstDouble0() = 0x0E, // Push 0.0 PushConstDouble1() = 0x0F, // Push 1.0 LoadByteImmediate(u8) = 0x10, // push immediate short LoadShortImmediate(u16) = 0x11, // push immediate short LoadConstant(u8) = 0x12, // Push from constant pool LoadCostantWide(u16) = 0x13, // Push from constant pool with wide index, don't load // double or long or whatever LoadConstant64(u16) = 0x14, // Push Long or Double from constant pool LoadLocalInt0() = 0x1A, // Load int from local variable LoadLocalInt1() = 0x1B, // Load int from local variable LoadLocalInt2() = 0x1C, // Load int from local variable LoadLocalInt3() = 0x1D, // Load int from local variable LoadLocalDouble0() = 0x26, // Load local double variable reference onto stack LoadLocalDouble1() = 0x27, // Load local double variable reference onto stack LoadLocalDouble2() = 0x28, // Load local double variable reference onto stack LoadLocalDouble3() = 0x29, // Load local double variable reference onto stack LoadLocalReference0() = 0x2A, // Load local reference variable reference onto stack LoadLocalReference1() = 0x2B, // Load local reference variable reference onto stack LoadLocalReference2() = 0x2C, // Load local reference variable reference onto stack LoadLocalReference3() = 0x2D, // Load local reference variable reference onto stack ArrayElement() = 0x32, // load element from array StoreLocalInt(u8) = 0x36, // store into indexed local variable StoreLocalInt0() = 0x3B, // store int into local variable StoreLocalInt1() = 0x3C, // store int into local variable StoreLocalInt2() = 0x3D, // store int into local variable StoreLocalInt3() = 0x3E, // store int into local variable StoreReference0() = 0x4B, // store reference into local variable StoreReference1() = 0x4C, // store reference into local variable StoreReference2() = 0x4D, // store reference into local variable StoreReference3() = 0x4E, // store reference into local variable StoreIntoRArray() = 0x53, // store value into reference array StoreIntoBArray() = 0x54, // store value into byte or boolean array Pop() = 0x57, // Pop top stack value Duplicate() = 0x59, // duplicate top stack value MultiplyInt() = 0x68, // int multiplication DivideInt() = 0x6C, // integer division, round toward zero and more rules DivideLong() = 0x6D, // long division ShiftIntRight() = 0x7a, // shift int OrInt() = 0x80, // value, value => or BranchZero(i16) = 0x99, // branch if value == 0 BranchNonZero(i16) = 0x9A, // branch if value != 0 BranchNegative(i16) = 0x9B, // branch if value < 0 BranchNonPositive(i16) = 0x9C, // branch if value <= 0 BranchPositive(i16) = 0x9D, // branch if value > 0 BranchNonNegative(i16) = 0x9E, // branch if value >= 0 BranchIntEquality(i16) = 0x9F, BranchIntInequality(i16) = 0xA0, BranchIntLessThan(i16) = 0xA1, BranchIntGreaterEquals(i16) = 0xA2, BranchIntGreaterThan(i16) = 0xA3, BranchIntLessEquals(i16) = 0xA4, BranchAlways(i16) = 0xA7, // branch if true ReturnInt() = 0xAC, // return integer from function ReturnReference() = 0xB0, // return top-ref from current function ReturnVoid() = 0xB1, // return void from function GetStatic(u16) = 0xB2, // get static field from class PutStatic(u16) = 0xB3, // set static field on class GetField(u16) = 0xB4, // get field from class PutField(u16) = 0xB5, // set field to a value InvokeVirtual(u16) = 0xB6, // invoke function on a class InvokeSpecial(u16) = 0xB7, // invoke instance method InvokeStatic(u16) = 0xB8, // invoke static function InvokeDynamic(u16, u16) = 0xBA, // invoke dynamic function NewObject(u16) = 0xBB, // Create a new object from a constant-pool class reference NewPrimitiveArray(u8) = 0xBC, // make a primitive array NewArray(u16) = 0xBD, // Create a new array from a constant-pool component class reference ArrayLength() = 0xBE, // Get length from array reference EnterMonitor() = 0xC2, // enter the synchronization monitor of an object ExitMonitor() = 0xC3, // exit the synchronization monitor of an object BranchNull(i16) = 0xC6, // branch if Null BranchNonNull(i16) = 0xC7, // branch if Null Unknown(u8), }