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

107
src/accessmasks.rs Normal file
View file

@ -0,0 +1,107 @@
use core::fmt::{Display, Formatter, Debug};
#[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 strict.
Synthetic = 0x1000, // Declared synthetic; not present in the source code.
}
pub struct MethodAccessFlagMask {
pub 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 {
pub 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()
}
}
#[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 {
pub 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()
}
}

View file

@ -1,5 +1,4 @@
use core::fmt::Debug;
use core::fmt;
pub struct Bytecode {
pub code: Box<[u8]>
@ -14,20 +13,57 @@ impl Bytecode {
let opcode = self.code[i];
let (instruction, offset) = match opcode {
0x00 => (Instruction::NoOperation(), 1),
0x01 => (Instruction::StoreIntoIntArray(), 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),
0x11 => (Instruction::LoadShortImmediate((self.code[i+1] as u16) << 8 | self.code[i+2] as u16), 3),
0x12 => (Instruction::LoadConstant(self.code[i+1]), 2),
0x14 => (Instruction::LoadConstant64((self.code[i+1] as u16) << 8 | self.code[i+2] as u16), 3),
0x26 => (Instruction::LoadDouble0(), 1),
0x27 => (Instruction::LoadDouble1(), 1),
0x28 => (Instruction::LoadDouble2(), 1),
0x29 => (Instruction::LoadDouble3(), 1),
0x2A => (Instruction::LoadReference0(), 1),
0x2B => (Instruction::LoadReference1(), 1),
0x2C => (Instruction::LoadReference2(), 1),
0x2D => (Instruction::LoadReference3(), 1),
0x4B => (Instruction::StoreReference0(), 1),
0x4C => (Instruction::StoreReference1(), 1),
0x4D => (Instruction::StoreReference2(), 1),
0x4E => (Instruction::StoreReference3(), 1),
0x57 => (Instruction::Pop(), 1),
0x59 => (Instruction::Duplicate(), 1),
0x6D => (Instruction::DivideLong(), 1),
0x7A => (Instruction::ShiftIntRight(), 1),
0x80 => (Instruction::OrInt(), 1),
0xAC => (Instruction::ReturnInt(), 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),
0xB2 => (Instruction::GetStatic((self.code[i+1] as u16) << 8 | self.code[i+2] as u16), 3),
0xB3 => (Instruction::PutStatic((self.code[i+1] as u16) << 8 | self.code[i+2] as u16), 3),
0xB4 => (Instruction::GetField((self.code[i+1] as u16) << 8 | self.code[i+2] as u16), 3),
0xB5 => (Instruction::PutField((self.code[i+1] as u16) << 8 | self.code[i+2] as u16), 3),
0xB6 => (Instruction::InvokeVirtual((self.code[i+1] as u16) << 8 | self.code[i+2] as u16), 3),
0xB7 => (Instruction::InvokeSpecial((self.code[i+1] as u16) << 8 | self.code[i+2] as u16), 3),
0xBA => (Instruction::InvokeDynamic((self.code[i+1] as u16) << 8 | self.code[i+2] as u16, (self.code[i+3] as u16) << 8 | self.code[i+4] as u16), 5),
0xBB => (Instruction::NewObject((self.code[i+1] as u16) << 8 | self.code[i+2] as u16), 3),
_ => (Instruction::Unknown(opcode), 1)
};
@ -50,19 +86,56 @@ impl Debug for Bytecode {
#[derive(Debug)]
#[repr(u8)]
pub enum Instruction {
NoOperation() = 0x00, // No-Operation
StoreIntoIntArray() = 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
LoadShortImmediate(u16) = 0x11, // push immediate short
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
LoadConstant64(u16) = 0x14, // Push Long or Double from constant pool
LoadDouble0() = 0x26, // Load local double variable reference onto stack
LoadDouble1() = 0x27, // Load local double variable reference onto stack
LoadDouble2() = 0x28, // Load local double variable reference onto stack
LoadDouble3() = 0x29, // Load local double variable reference onto stack
LoadReference0() = 0x2A, // Load local reference variable reference onto stack
LoadReference1() = 0x2B, // Load local reference variable reference onto stack
LoadReference2() = 0x2C, // Load local reference variable reference onto stack
LoadReference3() = 0x2D, // Load local reference variable reference onto stack
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
Pop() = 0x57, // Pop top stack value
Duplicate() = 0x59, // duplicate top stack value
DivideLong() = 0x6D, // long division
ShiftIntRight() = 0x7a, // shift int
OrInt() = 0x80, // value, value => or
ReturnInt() = 0xAC, // return integer from function
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
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
InvokeDynamic(u16, u16) = 0xBA, // invoke dynamic function
NewObject(u16) = 0xBB, // Create a new object from a constant-pool reference
Unknown(u8),
}

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]>,
pub constant_pool: Box<[ConstantPoolInfo]>,
access_flags: ClassAccessFlagMask,
pub access_flags: ClassAccessFlagMask,
this_class: u16,
super_class: u16,
pub this_class: u16,
pub super_class: u16,
interfaces: Box<[u16]>,
pub interfaces: Box<[u16]>,
fields: Box<[FieldInfo]>,
pub fields: Box<[FieldInfo]>,
methods: Box<[MethodInfo]>,
attributes: Box<[AttributeInfo]>,
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 {
}
}
#[repr(u8)]
#[derive(Debug)]
pub struct MethodInfo {
access_flags: MethodAccessFlagMask,
name_index: u16,
descriptor_index: u16,
attributes: Box<[AttributeInfo]>,
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)?;
let descriptor_entry = pool_entry(pool, descriptor_index.into())?;
match descriptor_entry {
ConstantPoolInfo::Utf8(utf8info) => {
let borrow = &utf8info.utf8;
borrow.try_into()?
}
f.debug_list().entries(flag_vec)
.finish()
_ => 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)?;
#[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.
}
let code_attribute_index = attributes.iter()
.position(|info| match info.data {AttributeData::Code(_) => true, _ => false })
.unwrap_or(attributes.len());
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)
Ok(
MethodInfo {
access_flags,
name,
descriptor,
code_attribute_index,
attributes
}
}
f.debug_list().entries(flag_vec)
.finish()
)
}
}
@ -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]);
}

118
src/classstore.rs Normal file
View file

@ -0,0 +1,118 @@
use core::fmt::{Display, Formatter};
use std::collections::HashMap;
use std::error::Error as ErrorTrait;
use std::fs::File;
use std::path::PathBuf;
use crate::classfile::JavaClassFile;
use crate::classfile;
#[derive(Debug)]
pub struct ClassStore {
class_ids: HashMap<String, usize>,
classes: Vec<JavaClassFile>,
class_path_fragments: Vec<PathBuf>,
}
#[derive(Debug)]
pub enum Error {
ClassNotFoundError(String),
IOError(std::io::Error),
ClassFileError(String, classfile::Error),
}
impl From<std::io::Error> for Error {
fn from(value: std::io::Error) -> Self {
return Error::IOError(
value
);
}
}
impl From<classfile::Error> for Error {
fn from(value: classfile::Error) -> Self {
return Error::ClassFileError(
"An error occured while loading a classfile".to_string(),
value
);
}
}
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 ClassStore {
pub fn new() -> Self {
let current_dir_path = PathBuf::from("./");
ClassStore {
class_ids: HashMap::new(),
classes: Vec::new(),
class_path_fragments: vec![current_dir_path],
}
}
pub fn load_class_from_file(&mut self, class_file_path: &PathBuf) -> Result<String, Error> {
let mut file_reader = File::open(class_file_path)?;
let classfile = JavaClassFile::new(&mut file_reader)?;
let classname = classfile.get_classname()?;
self.class_ids.insert(classname.clone(), self.classes.len());
self.classes.push(classfile);
return Ok(classname);
}
pub fn load_class(&mut self, classname: &String) -> Result<String, Error> {
let mut path_buf = PathBuf::new();
for class_path in &self.class_path_fragments {
path_buf.push(class_path);
path_buf.push(&classname);
path_buf.set_extension("class");
if path_buf.is_file() {
return self.load_class_from_file(&path_buf);
}
};
return Err(Error::ClassNotFoundError(format!("Could not find class '{}' in classpath", classname)));
}
pub fn have_class(&mut self, classname: &String) -> bool {
return self.class_ids.contains_key(classname);
}
pub fn get_class(&mut self, classname: &String) -> Result<(&JavaClassFile, usize), Error> {
let class_id = self.class_ids.get(classname);
return match class_id {
Some(id) => Ok((&self.classes[*id], *id)),
None => Err(Error::ClassNotFoundError(format!("Could not locate class '{}'", classname))),
}
}
pub fn get_or_load_class(&mut self, classname: &String) -> Result<(&JavaClassFile, usize), Error> {
if self.have_class(classname) {
return Ok(self.get_class(classname)?);
} else {
let real_class_name = self.load_class(classname)?;
return Ok(self.get_class(&real_class_name)?);
}
}
pub fn class_id_from_name(&mut self, classname: &String) -> Option<&usize> {
return self.class_ids.get(classname);
}
}

62
src/jvm.rs Normal file
View file

@ -0,0 +1,62 @@
use core::fmt::{Display, Formatter};
use std::error::Error as ErrorTrait;
use crate::classstore;
use crate::classstore::ClassStore;
use crate::stackframe::StackFrame;
#[derive(Debug)]
pub enum Error {
ClassStoreError(classstore::Error),
BadNameError(String),
}
impl From<classstore::Error> for Error {
fn from(value: classstore::Error) -> Self {
return Error::ClassStoreError(value);
}
}
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(())
}
}
#[derive(Debug)]
pub struct JVM {
class_store: ClassStore,
stack_frames: Vec<StackFrame>,
}
impl JVM {
pub fn new() -> Self {
return JVM {
class_store: ClassStore::new(),
stack_frames: Vec::new(),
}
}
pub fn load_class(&mut self, name: &String) -> Result<String, classstore::Error> {
return self.class_store.load_class(name);
}
pub fn invoke_static(&mut self, class_name: &String, method_name: &String) -> Result<(), Error> {
let (class_file, class_index) = self.class_store.get_or_load_class(class_name)?;
let method_index = class_file.find_method_index(method_name)
.ok_or(Error::BadNameError(format!("Could not find method '{}' in class '{}'", method_name, class_name)))?;
let new_frame = StackFrame::new(class_file, class_index, method_index.try_into().expect(&format!("Bad method index: {}", method_index)));
self.stack_frames.push(new_frame);
return Ok(());
}
}

View file

@ -1,10 +1,16 @@
use std::fs::File;
mod classfile;
mod classstore;
mod bytecode;
mod jvm;
mod stackframe;
mod accessmasks;
fn main() {
let class_file = classfile::JavaClassFile::new(&mut File::open("class/Enumerator$EnumeratorIterator.class").unwrap()).unwrap();
let mut jvm = jvm::JVM::new();
let loaded_name = jvm.load_class(&"class/Main".to_string()).expect("Could not load class");
println!("{:#?}", class_file);
jvm.invoke_static(&loaded_name, &"main".to_string()).expect("failed to call main() on supplied class");
println!("{:#?}", jvm);
}

61
src/stackframe.rs Normal file
View file

@ -0,0 +1,61 @@
use crate::classfile::{ JavaClassFile, AttributeData };
#[derive(Copy, Clone, Debug)]
pub enum LocalVariable {
Boolean(bool),
Byte(u8),
Char(u16),
Short(u16),
Int(u32),
Float(u32),
Reference(u32),
ReturnAddress(u32),
Double0(u32),
Double1(u32),
Long0(u32),
Long1(u32),
Empty(),
}
#[derive(Debug)]
pub struct OperandStack {
stack: Box<[LocalVariable]>,
depth: u16,
}
impl OperandStack {
fn new(size: u16) -> Self {
return OperandStack {
stack: vec![LocalVariable::Empty(); size.into()].into_boxed_slice(),
depth: 0,
}
}
}
#[derive(Debug)]
pub struct StackFrame {
locals: Box<[LocalVariable]>,
operand_stack: OperandStack,
class_id: usize,
method_index: u16,
instruction_pointer: u32,
}
impl StackFrame {
pub fn new(classfile: &JavaClassFile, class_id: usize, method_index: u16) -> Self {
let method_info = &classfile.methods[method_index as usize];
let code_data = match &method_info.attributes[method_info.code_attribute_index].data {
AttributeData::Code(data) => data,
_ => unreachable!(),
};
StackFrame {
locals: vec![LocalVariable::Empty(); code_data.max_locals.into()].into_boxed_slice(),
operand_stack: OperandStack::new(code_data.max_stack),
class_id,
method_index,
instruction_pointer: 0,
}
}
}