diff --git a/src/bytecode.rs b/src/bytecode.rs new file mode 100644 index 0000000..78ac273 --- /dev/null +++ b/src/bytecode.rs @@ -0,0 +1,68 @@ +use core::fmt::Debug; +use core::fmt; + +pub struct Bytecode { + pub code: Box<[u8]> +} + +impl Bytecode { + pub fn opcodes(&self) -> Box<[Instruction]> { + let mut v = Vec::with_capacity(self.code.len()); + + let mut i = 0; + while i < self.code.len() { + let opcode = self.code[i]; + + let (instruction, offset) = match opcode { + 0x12 => (Instruction::LoadConstant(self.code[i+1]), 2), + 0x2A => (Instruction::LoadReference0(), 1), + 0x2B => (Instruction::LoadReference1(), 1), + 0x2C => (Instruction::LoadReference2(), 1), + 0x2D => (Instruction::LoadReference3(), 1), + 0x59 => (Instruction::Duplicate(), 1), + 0xB0 => (Instruction::ReturnReference(), 1), + 0xB1 => (Instruction::ReturnVoid(), 1), + 0xB2 => (Instruction::GetStatic(self.code[i+1], self.code[i+2]), 3), + 0xB4 => (Instruction::GetField(self.code[i+1], self.code[i+2]), 3), + 0xB5 => (Instruction::PutField(self.code[i+1], self.code[i+2]), 3), + 0xB6 => (Instruction::InvokeVirtual(self.code[i+1], self.code[i+2]), 3), + 0xB7 => (Instruction::InvokeSpecial(self.code[i+1], self.code[i+2]), 3), + 0xBB => (Instruction::NewObject(self.code[i+1], self.code[i+2]), 3), + _ => (Instruction::Unknown(opcode), 1) + }; + + 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.opcodes()) + .finish() + } +} + +#[derive(Debug)] +#[repr(u8)] +pub enum Instruction { + LoadConstant(u8) = 0x12, // Push from constant pool + LoadReference0() = 0x2A, // Load local variable reference onto stack + LoadReference1() = 0x2B, // Load local variable reference onto stack + LoadReference2() = 0x2C, // Load local variable reference onto stack + LoadReference3() = 0x2D, // Load local variable reference onto stack + Duplicate() = 0x59, // duplicate top stack value + ReturnReference() = 0xB0, // return top-ref from current function + ReturnVoid() = 0xB1, // return void from function + GetStatic(u8, u8) = 0xB2, // get static field from class + GetField(u8, u8) = 0xB4, // get field from class + PutField(u8, u8) = 0xB5, // set field to a value + InvokeVirtual(u8, u8) = 0xB6, // invoke function on a class + InvokeSpecial(u8, u8) = 0xB7, // invoke instance method + NewObject(u8, u8) = 0xBB, // Create a new object from a constant-pool reference + Unknown(u8), +} diff --git a/src/classfile.rs b/src/classfile.rs index 7859d9a..1569e4c 100644 --- a/src/classfile.rs +++ b/src/classfile.rs @@ -3,6 +3,7 @@ use std::error::Error as ErrorTrait; use core::fmt::{Display, Formatter, Debug}; use core::str::Utf8Error; +use crate::bytecode::Bytecode; #[derive(Debug)] pub enum Error { @@ -135,7 +136,7 @@ impl JavaClassFile { methods_vec.into_boxed_slice() }; - let attributes = AttributeInfo::array_from_reader(reader, &constant_pool)?; + let attributes = AttributeInfo::array_from_reader(reader, &constant_pool, true)?; Ok( JavaClassFile { @@ -472,7 +473,7 @@ impl FieldInfo { access_flags: AccessFlagMask { mask: read_u16(reader)? }, name_index: read_u16(reader)?, descriptor_index: read_u16(reader)?, - attributes: AttributeInfo::array_from_reader(reader, pool)?, + attributes: AttributeInfo::array_from_reader(reader, pool, true)?, } ) } @@ -530,11 +531,178 @@ impl LineNumberTableAttributeData { } } +#[derive(Debug)] +pub struct ExceptionTableEntry { + start_pc: u16, + end_pc: u16, + handler_pc: u16, + catch_type: u16, +} + +impl ExceptionTableEntry { + fn from_reader(reader: &mut dyn Read) -> Result { + Ok( + ExceptionTableEntry { + start_pc: read_u16(reader)?, + end_pc: read_u16(reader)?, + handler_pc: read_u16(reader)?, + catch_type: read_u16(reader)?, + } + ) + } +} + +#[derive(Debug)] +pub struct CodeAttributeData { + max_stack: u16, + max_locals: u16, + code: Bytecode, + exception_table: Box<[ExceptionTableEntry]>, + attributes: Box<[AttributeInfo]>, +} + +impl CodeAttributeData { + fn from_reader(reader: &mut dyn Read, pool: &Box<[ConstantPoolInfo]>) -> Result { + let max_stack = read_u16(reader)?; + let max_locals = read_u16(reader)?; + + let code_length = read_u32(reader)?; + let code = read_buffer(reader, code_length.try_into()?)?; + + let exception_length = read_u16(reader)?; + let exception_table = { + let mut v = Vec::with_capacity(exception_length.into()); + + for _i in 0..exception_length { + v.push(ExceptionTableEntry::from_reader(reader)?); + } + + v.into_boxed_slice() + }; + + let attributes = AttributeInfo::array_from_reader(reader, pool, false)?; + + Ok( + CodeAttributeData { + max_stack, + max_locals, + code: Bytecode { code }, + exception_table, + attributes + } + ) + } +} + +#[derive(Debug)] +pub struct SourceFileAttributeData { + source_file_index: u16, +} + +impl SourceFileAttributeData { + fn from_reader(reader: &mut dyn Read) -> Result { + Ok( + SourceFileAttributeData { + source_file_index: read_u16(reader)?, + } + ) + } +} + +#[derive(Debug)] +pub struct SignatureAttributeData { + signature_index: u16, +} + +impl SignatureAttributeData { + fn from_reader(reader: &mut dyn Read) -> Result { + Ok( + SignatureAttributeData { + signature_index: read_u16(reader)?, + } + ) + } +} + +#[derive(Debug)] +pub struct InnerClassesAttributeEntry { + inner_class_info_index: u16, + outer_class_info_index: u16, + inner_name_index: u16, + outer_name_index: u16, +} + +impl InnerClassesAttributeEntry { + fn from_reader(reader: &mut dyn Read) -> Result { + Ok( + InnerClassesAttributeEntry { + inner_class_info_index: read_u16(reader)?, + outer_class_info_index: read_u16(reader)?, + inner_name_index: read_u16(reader)?, + outer_name_index: read_u16(reader)?, + } + ) + } +} + +#[derive(Debug)] +pub struct InnerClassesAttributeData { + classes: Box<[InnerClassesAttributeEntry]> +} + +impl InnerClassesAttributeData { + fn from_reader(reader: &mut dyn Read) -> Result { + Ok( + InnerClassesAttributeData { + classes: { + let length = read_u16(reader)?; + let mut v = Vec::with_capacity(length.into()); + + for _i in 0..length { + v.push(InnerClassesAttributeEntry::from_reader(reader)?); + } + + v.into_boxed_slice() + } + } + ) + } +} + +#[derive(Debug)] +pub struct NestMembersAttributeData { + class_indices: Box<[u16]> +} + +impl NestMembersAttributeData { + fn from_reader(reader: &mut dyn Read) -> Result { + Ok( + NestMembersAttributeData { + class_indices: { + let length = read_u16(reader)?; + let mut v = Vec::with_capacity(length.into()); + + for _i in 0..length { + v.push(read_u16(reader)?); + } + + v.into_boxed_slice() + } + } + ) + } +} + #[derive(Debug)] pub enum AttributeData { + Code(CodeAttributeData), + Signature(SignatureAttributeData), + NestMembers(NestMembersAttributeData), + SourceFile(SourceFileAttributeData), + InnerClasses(InnerClassesAttributeData), ConstantValue(ConstantValueAttributeData), LineNumberTable(LineNumberTableAttributeData), - UnknownAttribute(UnknownAttributeData), + Unknown(UnknownAttributeData), } #[derive(Debug)] @@ -544,22 +712,21 @@ pub struct AttributeInfo { } impl AttributeInfo { - fn array_from_reader(reader: &mut dyn Read, pool: &Box<[ConstantPoolInfo]>) -> Result, Error> { + fn array_from_reader(reader: &mut dyn Read, pool: &Box<[ConstantPoolInfo]>, allow_code_attr: bool) -> Result, Error> { let length = read_u16(reader)?; let mut attr_vec = Vec::with_capacity(length.into()); for _i in 0..length { - let attribute = AttributeInfo::from_reader(reader, &pool)?; - println!("{:#?}", attribute); + let attribute = AttributeInfo::from_reader(reader, &pool, allow_code_attr)?; attr_vec.push(attribute); } Ok(attr_vec.into_boxed_slice()) } - fn from_reader(reader: &mut dyn Read, pool: &Box<[ConstantPoolInfo]>) -> Result { - let attribute_name_index: u16 = read_u16(reader)?; - let attribute_byte_size: usize = read_u32(reader)?.try_into()?; + fn from_reader(reader: &mut dyn Read, pool: &Box<[ConstantPoolInfo]>, allow_code_attr: bool) -> Result { + let attribute_name_index: u16 = read_u16(reader)? - 1; + let _attribute_byte_size: usize = read_u32(reader)?.try_into()?; let data = { let name_entry = &pool[attribute_name_index as usize]; @@ -569,19 +736,35 @@ impl AttributeInfo { }; match &utf8[..] { -// "ConstantValue" => AttributeData::ConstantValue( -// ConstantValueAttributeData { -// constant_value_index: read_u16(reader)?, -// } -// ), -// -// "LineNumberTable" => AttributeData::LineNumberTable( -// LineNumberTableAttributeData::from_reader(reader)? -// ), + "ConstantValue" => AttributeData::ConstantValue( + ConstantValueAttributeData { + constant_value_index: read_u16(reader)?, + } + ), - &_ => AttributeData::UnknownAttribute( + "LineNumberTable" => AttributeData::LineNumberTable( + LineNumberTableAttributeData::from_reader(reader)? + ), + + "Code" => if allow_code_attr { + AttributeData::Code( + CodeAttributeData::from_reader(reader, pool)? + ) + } else { + return Err(Error::BadFileError("Nested Code attributes are forbidden.".to_string())); + }, + + "SourceFile" => AttributeData::SourceFile(SourceFileAttributeData::from_reader(reader)?), + + "Signature" => AttributeData::Signature(SignatureAttributeData::from_reader(reader)?), + + "InnerClasses" => AttributeData::InnerClasses(InnerClassesAttributeData::from_reader(reader)?), + + "NestMembers" => AttributeData::NestMembers(NestMembersAttributeData::from_reader(reader)?), + + &_ => AttributeData::Unknown( UnknownAttributeData { - info: read_buffer(reader, attribute_byte_size)?, + info: read_buffer(reader, _attribute_byte_size)?, } ) } @@ -611,7 +794,7 @@ impl MethodInfo { access_flags: AccessFlagMask { mask: read_u16(reader)? }, name_index: read_u16(reader)?, descriptor_index: read_u16(reader)?, - attributes: AttributeInfo::array_from_reader(reader, pool)? + attributes: AttributeInfo::array_from_reader(reader, pool, true)? } ) } diff --git a/src/main.rs b/src/main.rs index e100b2c..8a84f78 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,10 @@ use std::fs::File; mod classfile; +mod bytecode; fn main() { - let class_file = classfile::JavaClassFile::new(&mut File::open("Main.class").unwrap()).unwrap(); + let class_file = classfile::JavaClassFile::new(&mut File::open("class/HashMap.class").unwrap()).unwrap(); println!("{:#?}", class_file); }