Code cleanup
This commit is contained in:
parent
c1eca63f5a
commit
30fe5036d4
7 changed files with 691 additions and 158 deletions
107
src/accessmasks.rs
Normal file
107
src/accessmasks.rs
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
107
src/bytecode.rs
107
src/bytecode.rs
|
@ -1,5 +1,4 @@
|
||||||
use core::fmt::Debug;
|
use core::fmt::Debug;
|
||||||
use core::fmt;
|
|
||||||
|
|
||||||
pub struct Bytecode {
|
pub struct Bytecode {
|
||||||
pub code: Box<[u8]>
|
pub code: Box<[u8]>
|
||||||
|
@ -14,20 +13,57 @@ impl Bytecode {
|
||||||
let opcode = self.code[i];
|
let opcode = self.code[i];
|
||||||
|
|
||||||
let (instruction, offset) = match opcode {
|
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),
|
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),
|
0x2A => (Instruction::LoadReference0(), 1),
|
||||||
0x2B => (Instruction::LoadReference1(), 1),
|
0x2B => (Instruction::LoadReference1(), 1),
|
||||||
0x2C => (Instruction::LoadReference2(), 1),
|
0x2C => (Instruction::LoadReference2(), 1),
|
||||||
0x2D => (Instruction::LoadReference3(), 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),
|
0x59 => (Instruction::Duplicate(), 1),
|
||||||
|
|
||||||
|
0x6D => (Instruction::DivideLong(), 1),
|
||||||
|
|
||||||
|
0x7A => (Instruction::ShiftIntRight(), 1),
|
||||||
|
|
||||||
|
0x80 => (Instruction::OrInt(), 1),
|
||||||
|
|
||||||
|
0xAC => (Instruction::ReturnInt(), 1),
|
||||||
|
|
||||||
0xB0 => (Instruction::ReturnReference(), 1),
|
0xB0 => (Instruction::ReturnReference(), 1),
|
||||||
0xB1 => (Instruction::ReturnVoid(), 1),
|
0xB1 => (Instruction::ReturnVoid(), 1),
|
||||||
0xB2 => (Instruction::GetStatic(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),
|
||||||
0xB4 => (Instruction::GetField(self.code[i+1], self.code[i+2]), 3),
|
0xB3 => (Instruction::PutStatic((self.code[i+1] as u16) << 8 | self.code[i+2] as u16), 3),
|
||||||
0xB5 => (Instruction::PutField(self.code[i+1], self.code[i+2]), 3),
|
0xB4 => (Instruction::GetField((self.code[i+1] as u16) << 8 | self.code[i+2] as u16), 3),
|
||||||
0xB6 => (Instruction::InvokeVirtual(self.code[i+1], self.code[i+2]), 3),
|
0xB5 => (Instruction::PutField((self.code[i+1] as u16) << 8 | self.code[i+2] as u16), 3),
|
||||||
0xB7 => (Instruction::InvokeSpecial(self.code[i+1], self.code[i+2]), 3),
|
0xB6 => (Instruction::InvokeVirtual((self.code[i+1] as u16) << 8 | self.code[i+2] as u16), 3),
|
||||||
0xBB => (Instruction::NewObject(self.code[i+1], self.code[i+2]), 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)
|
_ => (Instruction::Unknown(opcode), 1)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -50,19 +86,56 @@ impl Debug for Bytecode {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum Instruction {
|
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
|
LoadConstant(u8) = 0x12, // Push from constant pool
|
||||||
LoadReference0() = 0x2A, // Load local variable reference onto stack
|
LoadConstant64(u16) = 0x14, // Push Long or Double from constant pool
|
||||||
LoadReference1() = 0x2B, // Load local variable reference onto stack
|
|
||||||
LoadReference2() = 0x2C, // Load local variable reference onto stack
|
LoadDouble0() = 0x26, // Load local double variable reference onto stack
|
||||||
LoadReference3() = 0x2D, // Load local 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
|
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
|
ReturnReference() = 0xB0, // return top-ref from current function
|
||||||
ReturnVoid() = 0xB1, // return void from function
|
ReturnVoid() = 0xB1, // return void from function
|
||||||
GetStatic(u8, u8) = 0xB2, // get static field from class
|
GetStatic(u16) = 0xB2, // get static field from class
|
||||||
GetField(u8, u8) = 0xB4, // get field from class
|
PutStatic(u16) = 0xB3, // set static field on class
|
||||||
PutField(u8, u8) = 0xB5, // set field to a value
|
GetField(u16) = 0xB4, // get field from class
|
||||||
InvokeVirtual(u8, u8) = 0xB6, // invoke function on a class
|
PutField(u16) = 0xB5, // set field to a value
|
||||||
InvokeSpecial(u8, u8) = 0xB7, // invoke instance method
|
InvokeVirtual(u16) = 0xB6, // invoke function on a class
|
||||||
NewObject(u8, u8) = 0xBB, // Create a new object from a constant-pool reference
|
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),
|
Unknown(u8),
|
||||||
}
|
}
|
||||||
|
|
362
src/classfile.rs
362
src/classfile.rs
|
@ -4,6 +4,7 @@ use core::fmt::{Display, Formatter, Debug};
|
||||||
use core::str::Utf8Error;
|
use core::str::Utf8Error;
|
||||||
|
|
||||||
use crate::bytecode::Bytecode;
|
use crate::bytecode::Bytecode;
|
||||||
|
use crate::accessmasks::*;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
@ -48,24 +49,23 @@ impl From<core::num::TryFromIntError> for Error {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct JavaClassFile {
|
pub struct JavaClassFile {
|
||||||
minor_version: u16,
|
pub minor_version: u16,
|
||||||
major_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,
|
pub this_class: u16,
|
||||||
super_class: u16,
|
pub super_class: u16,
|
||||||
|
|
||||||
interfaces: Box<[u16]>,
|
pub interfaces: Box<[u16]>,
|
||||||
|
|
||||||
fields: Box<[FieldInfo]>,
|
pub fields: Box<[FieldInfo]>,
|
||||||
|
|
||||||
methods: Box<[MethodInfo]>,
|
pub methods: Box<[MethodInfo]>,
|
||||||
|
|
||||||
attributes: Box<[AttributeInfo]>,
|
|
||||||
|
|
||||||
|
pub attributes: Box<[AttributeInfo]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl JavaClassFile {
|
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)]
|
#[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)]
|
#[derive(Debug)]
|
||||||
pub struct FieldInfo {
|
pub struct FieldInfo {
|
||||||
access_flags: FieldAccessFlagMask,
|
access_flags: FieldAccessFlagMask,
|
||||||
name_index: u16,
|
name: String,
|
||||||
descriptor_index: u16,
|
descriptor: AbstractTypeDescription,
|
||||||
attributes: Box<[AttributeInfo]>,
|
attributes: Box<[AttributeInfo]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FieldInfo {
|
impl FieldInfo {
|
||||||
fn from_reader(reader: &mut dyn Read, pool: &Box<[ConstantPoolInfo]>) -> Result<Self, Error> {
|
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(
|
Ok(
|
||||||
FieldInfo {
|
FieldInfo {
|
||||||
access_flags: FieldAccessFlagMask { mask: read_u16(reader)? },
|
access_flags,
|
||||||
name_index: read_u16(reader)?,
|
name,
|
||||||
descriptor_index: read_u16(reader)?,
|
descriptor,
|
||||||
attributes: AttributeInfo::array_from_reader(reader, pool, true)?,
|
attributes
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -563,11 +589,11 @@ impl ExceptionTableEntry {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct CodeAttributeData {
|
pub struct CodeAttributeData {
|
||||||
max_stack: u16,
|
pub max_stack: u16,
|
||||||
max_locals: u16,
|
pub max_locals: u16,
|
||||||
code: Bytecode,
|
pub code: Bytecode,
|
||||||
exception_table: Box<[ExceptionTableEntry]>,
|
pub exception_table: Box<[ExceptionTableEntry]>,
|
||||||
attributes: Box<[AttributeInfo]>,
|
pub attributes: Box<[AttributeInfo]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CodeAttributeData {
|
impl CodeAttributeData {
|
||||||
|
@ -716,8 +742,8 @@ pub enum AttributeData {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct AttributeInfo {
|
pub struct AttributeInfo {
|
||||||
attribute_name_index: u16,
|
pub attribute_name_index: u16,
|
||||||
data: AttributeData
|
pub data: AttributeData
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AttributeInfo {
|
impl AttributeInfo {
|
||||||
|
@ -788,94 +814,163 @@ impl AttributeInfo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct MethodInfo {
|
pub enum AbstractTypeKind {
|
||||||
access_flags: MethodAccessFlagMask,
|
Void() = b'V', // void
|
||||||
name_index: u16,
|
Byte() = b'B', // signed byte
|
||||||
descriptor_index: u16,
|
Char() = b'C', // Unicode character code point in the Basic Multilingual Plane, encoded with UTF-16
|
||||||
attributes: Box<[AttributeInfo]>,
|
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 {
|
#[derive(Debug)]
|
||||||
fn from_reader(reader: &mut dyn Read, pool: &Box<[ConstantPoolInfo]>) -> Result<Self, Error> {
|
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(
|
Ok(
|
||||||
MethodInfo {
|
MethodDescriptor {
|
||||||
access_flags: MethodAccessFlagMask { mask: read_u16(reader)? },
|
argument_types: args.into_boxed_slice(),
|
||||||
name_index: read_u16(reader)?,
|
return_type,
|
||||||
descriptor_index: read_u16(reader)?,
|
|
||||||
attributes: AttributeInfo::array_from_reader(reader, pool, true)?
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug)]
|
||||||
pub enum MethodAccessFlag {
|
pub struct MethodInfo {
|
||||||
Public = 0x0001, // Declared public; may be accessed from outside its package.
|
pub access_flags: MethodAccessFlagMask,
|
||||||
Private = 0x0002, // Declared private; accessible only within the defining class and other classes belonging to the same nest (§5.4.4).
|
pub name: String,
|
||||||
Protected = 0x0004, // Declared protected; may be accessed within subclasses.
|
pub descriptor: MethodDescriptor,
|
||||||
Static = 0x0008, // Declared static.
|
pub code_attribute_index: usize,
|
||||||
Final = 0x0010, // Declared final; must not be overridden (§5.4.5).
|
pub attributes: Box<[AttributeInfo]>,
|
||||||
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 {
|
impl MethodInfo {
|
||||||
mask: u16,
|
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 {
|
let name_entry = pool_entry(pool, name_index.into())?;
|
||||||
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];
|
match name_entry {
|
||||||
|
ConstantPoolInfo::Utf8(utf8info) => utf8info.utf8.clone(),
|
||||||
for flag in flags {
|
_ => return Err(Error::BadFileError(format!("Bad index into constant pool, expected type Utf8 but found {:?}", name_entry))),
|
||||||
if (flag as u16 & self.mask) != 0 {
|
|
||||||
flag_vec.push(flag)
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
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()?
|
||||||
}
|
}
|
||||||
|
_ => return Err(Error::BadFileError(format!("Bad index into constant pool, expected type Utf8 but found {:?}", descriptor_entry))),
|
||||||
f.debug_list().entries(flag_vec)
|
|
||||||
.finish()
|
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
let attributes = AttributeInfo::array_from_reader(reader, pool, true)?;
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
let code_attribute_index = attributes.iter()
|
||||||
pub enum ClassAccessFlag {
|
.position(|info| match info.data {AttributeData::Code(_) => true, _ => false })
|
||||||
Public = 0x0001, // Declared public; may be accessed from outside its package.
|
.unwrap_or(attributes.len());
|
||||||
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 {
|
Ok(
|
||||||
mask: u16,
|
MethodInfo {
|
||||||
}
|
access_flags,
|
||||||
|
name,
|
||||||
impl Debug for ClassAccessFlagMask {
|
descriptor,
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
|
code_attribute_index,
|
||||||
let mut flag_vec = Vec::new();
|
attributes
|
||||||
|
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -953,3 +1048,14 @@ fn read_u8(reader: &mut dyn Read) -> Result<u8, std::io::Error> {
|
||||||
|
|
||||||
return Ok(u8::from_be_bytes(u8_buffer));
|
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
118
src/classstore.rs
Normal 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
62
src/jvm.rs
Normal 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(());
|
||||||
|
}
|
||||||
|
}
|
12
src/main.rs
12
src/main.rs
|
@ -1,10 +1,16 @@
|
||||||
use std::fs::File;
|
|
||||||
|
|
||||||
mod classfile;
|
mod classfile;
|
||||||
|
mod classstore;
|
||||||
mod bytecode;
|
mod bytecode;
|
||||||
|
mod jvm;
|
||||||
|
mod stackframe;
|
||||||
|
mod accessmasks;
|
||||||
|
|
||||||
fn main() {
|
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
61
src/stackframe.rs
Normal 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue