Code cleanup

This commit is contained in:
VegOwOtenks 2024-08-30 15:33:54 +02:00
parent c1eca63f5a
commit 30fe5036d4
7 changed files with 691 additions and 158 deletions

View file

@ -4,6 +4,7 @@ use core::fmt::{Display, Formatter, Debug};
use core::str::Utf8Error;
use crate::bytecode::Bytecode;
use crate::accessmasks::*;
#[derive(Debug)]
pub enum Error {
@ -48,24 +49,23 @@ impl From<core::num::TryFromIntError> for Error {
#[derive(Debug)]
pub struct JavaClassFile {
minor_version: u16,
major_version: u16,
pub minor_version: u16,
pub 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]>,
pub constant_pool: Box<[ConstantPoolInfo]>,
pub access_flags: ClassAccessFlagMask,
pub this_class: u16,
pub super_class: u16,
pub interfaces: Box<[u16]>,
pub fields: Box<[FieldInfo]>,
pub methods: Box<[MethodInfo]>,
pub attributes: Box<[AttributeInfo]>,
}
impl JavaClassFile {
@ -160,6 +160,35 @@ impl JavaClassFile {
}
)
}
pub fn get_classname(&self) -> Result<String, Error> {
let class_info_entry = pool_entry(&self.constant_pool, self.this_class as usize)?;
let class_info_entry = match class_info_entry {
ConstantPoolInfo::Class(data) => data,
_ => return Err(Error::BadFileError(format!("Invalid this_class index, expected index to ClassInfo but found {:?}", class_info_entry)))
};
let name_entry = pool_entry(&self.constant_pool, class_info_entry.name_index.into())?;
let name_entry = match name_entry {
ConstantPoolInfo::Utf8(utf8data) => utf8data,
_ => return Err(Error::BadFileError(format!("Invalid name_index class_info from this_class, expected index to Utf8 but found {:?}", name_entry)))
};
return Ok(name_entry.utf8.clone());
}
pub fn find_method_index(&self, name: &String) -> Option<usize> {
for (index, method_info) in self.methods.iter().enumerate() {
if method_info.name == *name {
return Some(index);
}
}
return None;
}
}
#[derive(Debug)]
@ -432,57 +461,54 @@ impl ConstantPoolInfo {
}
}
#[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,
name: String,
descriptor: AbstractTypeDescription,
attributes: Box<[AttributeInfo]>,
}
impl FieldInfo {
fn from_reader(reader: &mut dyn Read, pool: &Box<[ConstantPoolInfo]>) -> Result<Self, Error> {
let access_flags = FieldAccessFlagMask { mask: read_u16(reader)? };
let name = {
let name_index = read_u16(reader)?;
let name_entry = pool_entry(pool, name_index.into())?;
match name_entry {
ConstantPoolInfo::Utf8(utf8info) => utf8info.utf8.clone(),
_ => return Err(Error::BadFileError(format!("Bad index into constant pool, expected type Utf8 but found {:?}", name_entry))),
}
};
let descriptor: AbstractTypeDescription = {
let descriptor_index = read_u16(reader)?;
let descriptor_entry = pool_entry(pool, descriptor_index.into())?;
match descriptor_entry {
ConstantPoolInfo::Utf8(utf8info) => {
let borrow = &utf8info.utf8;
let (length_parsed, type_desc) = AbstractTypeDescription::parse_first(borrow)?;
if length_parsed != borrow.len() {
Err(Error::BadFileError(format!("Bad field descriptor found: {}", borrow)))?
}
type_desc
}
_ => return Err(Error::BadFileError(format!("Bad index into constant pool, expected type Utf8 but found {:?}", descriptor_entry))),
}
};
let attributes = AttributeInfo::array_from_reader(reader, pool, true)?;
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)?,
access_flags,
name,
descriptor,
attributes
}
)
}
@ -563,11 +589,11 @@ impl ExceptionTableEntry {
#[derive(Debug)]
pub struct CodeAttributeData {
max_stack: u16,
max_locals: u16,
code: Bytecode,
exception_table: Box<[ExceptionTableEntry]>,
attributes: Box<[AttributeInfo]>,
pub max_stack: u16,
pub max_locals: u16,
pub code: Bytecode,
pub exception_table: Box<[ExceptionTableEntry]>,
pub attributes: Box<[AttributeInfo]>,
}
impl CodeAttributeData {
@ -716,8 +742,8 @@ pub enum AttributeData {
#[derive(Debug)]
pub struct AttributeInfo {
attribute_name_index: u16,
data: AttributeData
pub attribute_name_index: u16,
pub data: AttributeData
}
impl AttributeInfo {
@ -788,94 +814,163 @@ impl AttributeInfo {
}
}
#[derive(Debug)]
pub struct MethodInfo {
access_flags: MethodAccessFlagMask,
name_index: u16,
descriptor_index: u16,
attributes: Box<[AttributeInfo]>,
#[repr(u8)]
#[derive(Debug)]
pub enum AbstractTypeKind {
Void() = b'V', // void
Byte() = b'B', // signed byte
Char() = b'C', // Unicode character code point in the Basic Multilingual Plane, encoded with UTF-16
Double() = b'D', // double-precision floating-point value
Float() = b'F', // single-precision floating-point value
Int() = b'I', // integer
Long() = b'J', // long integer
Classname(String) = b'L', // an instance of class ClassName
Short() = b'S', // signed short
Boolean() = b'Z', // true or false
}
impl MethodInfo {
fn from_reader(reader: &mut dyn Read, pool: &Box<[ConstantPoolInfo]>) -> Result<Self, Error> {
#[derive(Debug)]
pub struct AbstractTypeDescription {
array_level: u8,
kind: AbstractTypeKind,
}
impl AbstractTypeDescription {
fn parse_first(s: &str) -> Result<(usize, Self), Error> {
let mut offset: usize = 0;
let arrays_parsed = s.trim_start_matches("[");
let array_level = (s.len() - arrays_parsed.len()).try_into();
let array_level: u8 = match array_level {
Ok(s) => s,
Err(_e) => return Err(Error::BadFileError(format!("Too many array levels in method descriptor! Max is 255 but found {}", s.len() - arrays_parsed.len()))),
};
offset += array_level as usize;
let type_char = arrays_parsed.chars().nth(0).ok_or(Error::BadFileError("Missing type char in method descriptor".to_string()))?;
offset += 1;
let kind = match type_char {
'B' => AbstractTypeKind::Byte(),
'C' => AbstractTypeKind::Char(),
'D' => AbstractTypeKind::Double(),
'F' => AbstractTypeKind::Float(),
'I' => AbstractTypeKind::Int(),
'J' => AbstractTypeKind::Long(),
'S' => AbstractTypeKind::Short(),
'Z' => AbstractTypeKind::Boolean(),
'V' => AbstractTypeKind::Void(),
'L' => {
let semicolon_index = s.get(offset..).unwrap().find(";").ok_or(Error::BadFileError(format!("Missing ';' in type descriptor: {}", s)))?;
let classname_start = offset;
let classname_end = offset + semicolon_index;
let classname_string = s.get(classname_start..classname_end).unwrap();
offset += classname_string.len() + 1;
AbstractTypeKind::Classname(classname_string.to_string())
}
_ => return Err(Error::BadFileError(format!("Invalid Type character: '{}' in string \"{}\"", type_char, s))),
};
return Ok((offset, AbstractTypeDescription { array_level, kind }))
}
}
#[derive(Debug)]
pub struct MethodDescriptor {
argument_types: Box<[AbstractTypeDescription]>,
return_type: AbstractTypeDescription,
}
impl TryFrom<&String> for MethodDescriptor {
type Error = Error;
fn try_from(s: &String) -> Result<Self, Error> {
let mut total_offset: usize = 0;
s.strip_prefix("(")
.ok_or(Error::BadFileError(format!("Bad method descriptor: '{}'", s)))?;
total_offset += 1;
let mut args = Vec::new();
while ! s.get(total_offset..).unwrap().starts_with(")") {
let (offset, arg_type) = AbstractTypeDescription::parse_first(s.get(total_offset..).unwrap())?;
total_offset += offset;
args.push(arg_type);
}
s.get(total_offset..).unwrap()
.strip_prefix(")")
.ok_or(Error::BadFileError(format!("Bad method descriptor")))?;
total_offset += 1;
let (offset, return_type) = AbstractTypeDescription::parse_first(s.get(total_offset..).unwrap())?;
if offset != s.get(total_offset..).unwrap().len() {
return Err(Error::BadFileError(format!("Trailing characters in method descriptor string: \"{}\"", s)))
}
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)?
MethodDescriptor {
argument_types: args.into_boxed_slice(),
return_type,
}
)
}
}
#[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.
#[derive(Debug)]
pub struct MethodInfo {
pub access_flags: MethodAccessFlagMask,
pub name: String,
pub descriptor: MethodDescriptor,
pub code_attribute_index: usize,
pub attributes: Box<[AttributeInfo]>,
}
pub struct MethodAccessFlagMask {
mask: u16,
}
impl MethodInfo {
fn from_reader(reader: &mut dyn Read, pool: &Box<[ConstantPoolInfo]>) -> Result<Self, Error> {
let access_flags = MethodAccessFlagMask { mask: read_u16(reader)? };
let name = {
let name_index = read_u16(reader)?;
impl Debug for MethodAccessFlagMask {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
let mut flag_vec = Vec::new();
let name_entry = pool_entry(pool, name_index.into())?;
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)
match name_entry {
ConstantPoolInfo::Utf8(utf8info) => utf8info.utf8.clone(),
_ => return Err(Error::BadFileError(format!("Bad index into constant pool, expected type Utf8 but found {:?}", name_entry))),
}
}
};
let descriptor: MethodDescriptor = {
let descriptor_index = read_u16(reader)?;
f.debug_list().entries(flag_vec)
.finish()
}
}
let descriptor_entry = pool_entry(pool, descriptor_index.into())?;
#[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.
}
match descriptor_entry {
ConstantPoolInfo::Utf8(utf8info) => {
let borrow = &utf8info.utf8;
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)
borrow.try_into()?
}
_ => return Err(Error::BadFileError(format!("Bad index into constant pool, expected type Utf8 but found {:?}", descriptor_entry))),
}
}
};
let attributes = AttributeInfo::array_from_reader(reader, pool, true)?;
f.debug_list().entries(flag_vec)
.finish()
let code_attribute_index = attributes.iter()
.position(|info| match info.data {AttributeData::Code(_) => true, _ => false })
.unwrap_or(attributes.len());
Ok(
MethodInfo {
access_flags,
name,
descriptor,
code_attribute_index,
attributes
}
)
}
}
@ -953,3 +1048,14 @@ fn read_u8(reader: &mut dyn Read) -> Result<u8, std::io::Error> {
return Ok(u8::from_be_bytes(u8_buffer));
}
fn pool_entry<'a>(pool: &Box<[ConstantPoolInfo]>, index: usize) -> Result<&ConstantPoolInfo, Error> {
if index == 0 {
return Err(Error::BadFileError(format!("Bad pool index: 0")));
}
if index - 1 >= pool.len() {
return Err(Error::BadFileError(format!("Bad pool index: {}", index - 1)));
}
return Ok(&pool[index - 1]);
}