use std::io::Read; 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 { BadFileError(String), IOError(std::io::Error), Utf8Error(Utf8Error), PlatformIntError(core::num::TryFromIntError), BadEnumError(String), } impl ErrorTrait for Error {} impl Display for Error { fn fmt(&self, formatter: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { writeln!(formatter, "{self}")?; if let Some(e) = self.source() { writeln!(formatter, "\tCaused by: {e:?}")?; } Ok(()) } } impl From for Error { fn from(value: std::io::Error) -> Self { return Error::IOError( value ); } } impl From for Error { fn from(value: Utf8Error) -> Self { return Error::Utf8Error(value); } } impl From for Error { fn from (value: core::num::TryFromIntError) -> Self { return Error::PlatformIntError(value); } } #[derive(Debug)] pub struct JavaClassFile { minor_version: u16, major_version: u16, constant_pool: Box<[ConstantPoolInfo]>, access_flags: ClassAccessFlagMask, this_class: u16, super_class: u16, interfaces: Box<[u16]>, fields: Box<[FieldInfo]>, methods: Box<[MethodInfo]>, attributes: Box<[AttributeInfo]>, } impl JavaClassFile { pub fn new(reader: &mut dyn Read) -> Result { { let magic = read_u32(reader)?; if magic != 0xCAFEBABE { return Err( Error::BadFileError( format!("Expected magic bytes 0xCAFEBABE but found {:#X}", magic) ) ) } } let minor_version = read_u16(reader)?; let major_version = read_u16(reader)?; let constant_pool = { let constant_pool_count = read_u16(reader)?; let constant_pool_size = constant_pool_count - 1; let mut constant_pool_vec = Vec::with_capacity(constant_pool_size.into()); let mut i = 0; while i < constant_pool_size { let cpinfo = ConstantPoolInfo::from_reader(reader)?; let delta_i = match cpinfo { ConstantPoolInfo::Double(_) | ConstantPoolInfo::Long(_) => 2, _ => 1 }; constant_pool_vec.push(cpinfo); if delta_i == 2 { constant_pool_vec.push(ConstantPoolInfo::Nothing()); } i += delta_i; } constant_pool_vec.into_boxed_slice() }; let access_flags = ClassAccessFlagMask { mask: read_u16(reader)? }; let this_class = read_u16(reader)?; let super_class = read_u16(reader)?; let interfaces = { let length = read_u16(reader)?; let mut if_vec = Vec::::with_capacity(length.into()); for _i in 0..length { if_vec.push(read_u16(reader)?); } if_vec.into_boxed_slice() }; let fields = { let length = read_u16(reader)?; let mut fields_vec = Vec::with_capacity(length.into()); for _i in 0..length { fields_vec.push(FieldInfo::from_reader(reader, &constant_pool)?); } fields_vec.into_boxed_slice() }; let methods = { let length = read_u16(reader)?; let mut methods_vec = Vec::with_capacity(length.into()); for _i in 0..length { methods_vec.push(MethodInfo::from_reader(reader, &constant_pool)?); } methods_vec.into_boxed_slice() }; let attributes = AttributeInfo::array_from_reader(reader, &constant_pool, true)?; Ok( JavaClassFile { minor_version, major_version, constant_pool, access_flags, this_class, super_class, interfaces, fields, methods, attributes, } ) } } #[derive(Debug)] pub struct ConstantClassInfo { name_index: u16, } #[derive(Debug)] pub struct ConstantFieldRefInfo { class_index: u16, name_and_type_index: u16, } #[derive(Debug)] pub struct ConstantMethodRefInfo { class_index: u16, name_and_type_index: u16, } #[derive(Debug)] pub struct ConstantInterfaceMethodRefInfo { class_index: u16, name_and_type_index: u16, } #[derive(Debug)] pub struct ConstantStringInfo { string_index: u16, } #[derive(Debug)] pub struct ConstantIntegerInfo { value: i32, } #[derive(Debug)] pub struct ConstantFloatInfo { value: f32, } #[derive(Debug)] pub struct ConstantLongInfo { value: u64, } #[derive(Debug)] pub struct ConstantDoubleInfo { value: f64, } #[derive(Debug)] pub struct ConstantNameAndTypeInfo { name_index: u16, descriptor_index: u16, } #[derive(Debug)] pub struct ConstantUtf8Info { utf8: String } #[derive(Debug)] #[repr(u8)] pub enum ConstantMethodHandleType { RefGetField(u8) = 1, RefGetStatic(u8) = 2, RefPutField(u8) = 3, RefPutStatic(u8) = 4, RefInvokeVirtual(u8) = 5, RefInvokeStatic(u8) = 6, RefInvokeSpecial(u8) = 7, RefNewInvokeSpecial(u8) = 8, RefInvokeInterface(u8) = 9, } impl TryFrom for ConstantMethodHandleType { type Error = Error; fn try_from(value: u8) -> Result { return match value { 1 => Ok(Self::RefGetField(value)), 2 => Ok(Self::RefGetStatic(value)), 3 => Ok(Self::RefPutField(value)), 4 => Ok(Self::RefPutStatic(value)), 5 => Ok(Self::RefInvokeVirtual(value)), 6 => Ok(Self::RefInvokeStatic(value)), 7 => Ok(Self::RefInvokeSpecial(value)), 8 => Ok(Self::RefNewInvokeSpecial(value)), 9 => Ok(Self::RefInvokeInterface(value)), _ => Err( Error::BadEnumError(format!("Unexpected MethodHandleType: {}", value)) ), } } } #[derive(Debug)] pub struct ConstantMethodHandleInfo { reference_kind: ConstantMethodHandleType, reference_index: u16, } #[derive(Debug)] pub struct ConstantMethodTypeInfo { descriptor_index: u16, } #[derive(Debug)] pub struct ConstantInvokeDynamicInfo { bootstrap_method_attr_index: u16, name_and_type_index: u16, } #[derive(Debug)] #[repr(u8)] pub enum ConstantPoolInfo { Class(ConstantClassInfo) = 7, FieldRef(ConstantFieldRefInfo) = 9, MethodRef(ConstantMethodRefInfo) = 10, InterfaceMethodRef(ConstantInterfaceMethodRefInfo) = 11, String(ConstantStringInfo) = 8, Integer(ConstantIntegerInfo) = 3, Float(ConstantFloatInfo) = 4, Long(ConstantLongInfo) = 5, Double(ConstantDoubleInfo) = 6, NameAndType(ConstantNameAndTypeInfo) = 12, Utf8(ConstantUtf8Info) = 1, MethodHandle(ConstantMethodHandleInfo) = 15, MethodType(ConstantMethodTypeInfo) = 16, InvokeDynamic(ConstantInvokeDynamicInfo) = 18, Nothing() = 255, } impl ConstantPoolInfo { fn from_reader(reader: &mut dyn Read) -> Result { let tag = read_u8(reader)?; Ok( match tag { 1 => { ConstantPoolInfo::Utf8( ConstantUtf8Info { utf8: { let length = read_u16(reader)?; let mut buffer: Box<[u8]> = vec![0; length.into()].into_boxed_slice(); reader.read_exact(&mut buffer)?; std::str::from_utf8(&buffer)?.to_string() } } ) } 3 => { ConstantPoolInfo::Integer( ConstantIntegerInfo { value: read_i32(reader)? } ) } 4 => { ConstantPoolInfo::Float( ConstantFloatInfo { value: read_f32(reader)? } ) } 5 => { ConstantPoolInfo::Long( ConstantLongInfo { value: read_u64(reader)? } ) } 6 => { ConstantPoolInfo::Double( ConstantDoubleInfo { value: read_f64(reader)? } ) } 7 => { ConstantPoolInfo::Class( ConstantClassInfo { name_index: read_u16(reader)? } ) } 8 => { ConstantPoolInfo::String( ConstantStringInfo { string_index: read_u16(reader)? } ) } 9 => { ConstantPoolInfo::FieldRef( ConstantFieldRefInfo { class_index: read_u16(reader)?, name_and_type_index: read_u16(reader)? } ) } 10 => { ConstantPoolInfo::MethodRef( ConstantMethodRefInfo { class_index: read_u16(reader)?, name_and_type_index: read_u16(reader)? } ) } 11 => { ConstantPoolInfo::InterfaceMethodRef( ConstantInterfaceMethodRefInfo { class_index: read_u16(reader)?, name_and_type_index: read_u16(reader)? } ) } 12 => { ConstantPoolInfo::NameAndType( ConstantNameAndTypeInfo { name_index: read_u16(reader)?, descriptor_index: read_u16(reader)? } ) } 15 => { ConstantPoolInfo::MethodHandle( ConstantMethodHandleInfo { reference_kind: ConstantMethodHandleType::try_from(read_u8(reader)?)?, reference_index: read_u16(reader)?, } ) } 16 => { ConstantPoolInfo::MethodType( ConstantMethodTypeInfo { descriptor_index: read_u16(reader)?, } ) } 18 => { ConstantPoolInfo::InvokeDynamic( ConstantInvokeDynamicInfo { bootstrap_method_attr_index: read_u16(reader)?, name_and_type_index: read_u16(reader)?, } ) } _ => unreachable!() } ) } } #[derive(Debug, Clone, Copy)] #[repr(u16)] pub enum FieldAccessFlag { Public = 0x0001, // Declared public; may be accessed from outside its package. Private = 0x0002, // Declared private; accessible only within the defining class and other classes belonging to the same nest (§5.4.4). Protected = 0x0004, // Declared protected; may be accessed within subclasses. Static = 0x0008, // Declared static. Final = 0x0010, // Declared final; never directly assigned to after object construction (JLS §17.5). Volatile = 0x0040, // Declared volatile; cannot be cached. Transient = 0x0080, // Declared transient; not written or read by a persistent object manager. Synthetic = 0x1000, // Declared synthetic; not present in the source code. Enum = 0x4000, // Declared as an element of an enum class. } pub struct FieldAccessFlagMask { mask: u16 } impl Debug for FieldAccessFlagMask { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { let mut flag_vec = Vec::new(); let flags = [FieldAccessFlag::Public,FieldAccessFlag::Private,FieldAccessFlag::Protected,FieldAccessFlag::Static,FieldAccessFlag::Final,FieldAccessFlag::Volatile,FieldAccessFlag::Transient,FieldAccessFlag::Synthetic,FieldAccessFlag::Enum,]; for flag in flags { if (flag as u16 & self.mask) != 0 { flag_vec.push(flag) } } f.debug_list().entries(flag_vec) .finish() } } #[derive(Debug)] pub struct FieldInfo { access_flags: FieldAccessFlagMask, name_index: u16, descriptor_index: u16, attributes: Box<[AttributeInfo]>, } impl FieldInfo { fn from_reader(reader: &mut dyn Read, pool: &Box<[ConstantPoolInfo]>) -> Result { Ok( FieldInfo { access_flags: FieldAccessFlagMask { mask: read_u16(reader)? }, name_index: read_u16(reader)?, descriptor_index: read_u16(reader)?, attributes: AttributeInfo::array_from_reader(reader, pool, true)?, } ) } } #[derive(Debug)] pub struct ConstantValueAttributeData { constant_value_index: u16, } #[derive(Debug)] pub struct UnknownAttributeData { info: Box<[u8]> } #[derive(Debug)] pub struct LineNumberTableEntry { start_pc: u16, line_number: u16, } impl LineNumberTableEntry { fn from_reader(reader: &mut dyn Read) -> Result { let start_pc = read_u16(reader)?; let line_number = read_u16(reader)?; Ok( LineNumberTableEntry { start_pc, line_number } ) } } #[derive(Debug)] pub struct LineNumberTableAttributeData { entries: Box<[LineNumberTableEntry]> } impl LineNumberTableAttributeData { fn from_reader(reader: &mut dyn Read) -> Result { let length = read_u16(reader)?; let mut entry_vec = Vec::with_capacity(length.into()); for _i in 0..length { entry_vec.push(LineNumberTableEntry::from_reader(reader)?); } Ok( LineNumberTableAttributeData { entries: entry_vec.into_boxed_slice() } ) } } #[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), Unknown(UnknownAttributeData), } #[derive(Debug)] pub struct AttributeInfo { attribute_name_index: u16, data: AttributeData } impl AttributeInfo { 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, allow_code_attr)?; attr_vec.push(attribute); } Ok(attr_vec.into_boxed_slice()) } 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]; let utf8 = match name_entry { ConstantPoolInfo::Utf8( ConstantUtf8Info { utf8 } ) => utf8, _ => return Err(Error::BadFileError(format!("Bad name index for attribute info: {}", attribute_name_index))), }; match &utf8[..] { "ConstantValue" => AttributeData::ConstantValue( ConstantValueAttributeData { constant_value_index: read_u16(reader)?, } ), "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)?, } ) } }; Ok( AttributeInfo { attribute_name_index, data } ) } } #[derive(Debug)] pub struct MethodInfo { access_flags: MethodAccessFlagMask, name_index: u16, descriptor_index: u16, attributes: Box<[AttributeInfo]>, } impl MethodInfo { fn from_reader(reader: &mut dyn Read, pool: &Box<[ConstantPoolInfo]>) -> Result { Ok( MethodInfo { access_flags: MethodAccessFlagMask { mask: read_u16(reader)? }, name_index: read_u16(reader)?, descriptor_index: read_u16(reader)?, attributes: AttributeInfo::array_from_reader(reader, pool, true)? } ) } } #[derive(Debug, Copy, Clone)] pub enum MethodAccessFlag { Public = 0x0001, // Declared public; may be accessed from outside its package. Private = 0x0002, // Declared private; accessible only within the defining class and other classes belonging to the same nest (§5.4.4). Protected = 0x0004, // Declared protected; may be accessed within subclasses. Static = 0x0008, // Declared static. Final = 0x0010, // Declared final; must not be overridden (§5.4.5). Synchronized = 0x0020, // Declared synchronized; invocation is wrapped by a monitor use. Bridge = 0x0040, // A bridge method, generated by the compiler. Varargs = 0x0080, // Declared with variable number of arguments. Native = 0x0100, // Declared native; implemented in a language other than the Java programming language. Abstract = 0x0400, // Declared abstract; no implementation is provided. Strict = 0x0800, // In a class file whose major version number is at least 46 and at most 60: Declared strictfp. Synthetic = 0x1000, // Declared synthetic; not present in the source code. } pub struct MethodAccessFlagMask { mask: u16, } impl Debug for MethodAccessFlagMask { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { let mut flag_vec = Vec::new(); let flags = [MethodAccessFlag::Public,MethodAccessFlag::Private,MethodAccessFlag::Protected,MethodAccessFlag::Static,MethodAccessFlag::Final,MethodAccessFlag::Synchronized,MethodAccessFlag::Bridge,MethodAccessFlag::Varargs,MethodAccessFlag::Native,MethodAccessFlag::Abstract,MethodAccessFlag::Strict,MethodAccessFlag::Synthetic]; for flag in flags { if (flag as u16 & self.mask) != 0 { flag_vec.push(flag) } } f.debug_list().entries(flag_vec) .finish() } } #[derive(Debug, Copy, Clone)] pub enum ClassAccessFlag { Public = 0x0001, // Declared public; may be accessed from outside its package. Final = 0x0010, // Declared final; no subclasses allowed. Super = 0x0020, // Treat superclass methods specially when invoked by the invokespecial instruction. Interface = 0x0200, // Is an interface, not a class. Abstract = 0x0400, // Declared abstract; must not be instantiated. Synthetic = 0x1000, // Declared synthetic; not present in the source code. Annotation = 0x2000, // Declared as an annotation interface. Enum = 0x4000, // Declared as an enum class. Module = 0x8000, // Is a module, not a class or interface. } pub struct ClassAccessFlagMask { mask: u16, } impl Debug for ClassAccessFlagMask { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { let mut flag_vec = Vec::new(); let flags = [ClassAccessFlag::Public,ClassAccessFlag::Final,ClassAccessFlag::Super,ClassAccessFlag::Interface,ClassAccessFlag::Abstract,ClassAccessFlag::Synthetic,ClassAccessFlag::Annotation,ClassAccessFlag::Enum,ClassAccessFlag::Module]; for flag in flags { if (flag as u16 & self.mask) != 0 { flag_vec.push(flag) } } f.debug_list().entries(flag_vec) .finish() } } fn read_buffer(reader: &mut dyn Read, size: usize) -> Result, Error> { let mut buffer: Box<[u8]> = vec![0; size].into_boxed_slice(); reader.read_exact(&mut buffer)?; Ok(buffer) } fn read_f64(reader: &mut dyn Read) -> Result { // let bytes = read_u64(reader)?; // // let sign = if (bytes >> 63) == 0 { 1 } else { -1 } as f64; // let exponent: f64 = ((bytes >> 52) & 0x7FF) as f64; // let mantissa: f64 = if exponent == 0.0 { // (bytes & 0xfffffffffffff) << 1 // } else { // (bytes & 0xfffffffffffff) | 0x10000000000000 // } as f64; // let base: f64 = 2.0; // // // return Ok(sign * mantissa * base.powf(exponent-1075.0)); let mut buffer: [u8; 8] = [0; 8]; reader.read_exact(&mut buffer)?; return Ok(f64::from_be_bytes(buffer)); } fn read_f32(reader: &mut dyn Read) -> Result { let mut buffer: [u8; 4] = [0; 4]; reader.read_exact(&mut buffer)?; return Ok(f32::from_be_bytes(buffer)); } fn read_i32(reader: &mut dyn Read) -> Result { let mut buffer: [u8; 4] = [0; 4]; reader.read_exact(&mut buffer)?; return Ok(i32::from_be_bytes(buffer)); } fn read_u64(reader: &mut dyn Read) -> Result { let high_bytes: u64 = read_u32(reader)?.into(); let low_bytes: u64 = read_u32(reader)?.into(); return Ok((high_bytes << 32) | low_bytes); } fn read_u32(reader: &mut dyn Read) -> Result { let mut u32_buffer: [u8; 4] = [0; 4]; reader.read_exact(&mut u32_buffer)?; return Ok(u32::from_be_bytes(u32_buffer)); } fn read_u16(reader: &mut dyn Read) -> Result { let mut u16_buffer: [u8; 2] = [0; 2]; reader.read_exact(&mut u16_buffer)?; return Ok(u16::from_be_bytes(u16_buffer)); } fn read_u8(reader: &mut dyn Read) -> Result { let mut u8_buffer: [u8; 1] = [0; 1]; reader.read_exact(&mut u8_buffer)?; return Ok(u8::from_be_bytes(u8_buffer)); }