diff --git a/src/class.rs b/src/class.rs index 313efbc..08f09df 100644 --- a/src/class.rs +++ b/src/class.rs @@ -3,7 +3,6 @@ use std::fmt; use std::rc::Rc; use crate::CpEntry; -use crate::heap::Object; use crate::io::read_u16; #[derive(Debug)] @@ -75,7 +74,7 @@ impl Method { pub struct Field { constant_pool: Rc>, access_flags: u16, - name_index: u16, + pub(crate) name_index: u16, descriptor_index: u16, attributes: HashMap, } @@ -97,20 +96,18 @@ impl Field { } pub fn name(&self) -> String { - let mut full_name = get_modifier(self.access_flags); + let mut name = String::new(); - if let CpEntry::Utf8(s) = &self.constant_pool.get(&self.descriptor_index).unwrap() { - full_name.push_str(s); - } - full_name.push(' '); + name.push(' '); if let CpEntry::Utf8(s) = &self.constant_pool.get(&self.name_index).unwrap() { - full_name.push_str(s); + name.push_str(s); } - full_name + name } pub fn type_of(&self) -> &String { + println!("{}", self.name_index); if let CpEntry::Utf8(s) = &self.constant_pool.get(&self.descriptor_index).unwrap() { return s; } @@ -221,4 +218,6 @@ pub enum Value { I64(i64), F32(f32), F64(f64), + BOOL(bool), + CHAR(char) } \ No newline at end of file diff --git a/src/classloader.rs b/src/classloader.rs new file mode 100644 index 0000000..2f3b8c6 --- /dev/null +++ b/src/classloader.rs @@ -0,0 +1,268 @@ +use std::collections::HashMap; +use std::rc::Rc; +use crate::io::{read_f32, read_f64, read_i32, read_i64, read_u16, read_u32}; +use crate::class::{AttributeType, Class, MethodCode, Exception, Field, Method}; + +pub fn load_class(bytecode: Vec) -> Option { + check_magic(&bytecode); + + let constant_pool_count = read_u16(&bytecode, 8); + // println!("cp count: {}", constant_pool_count); + let mut index = 10; + let mut constant_pool: HashMap = HashMap::with_capacity(constant_pool_count as usize); + let mut cp_index = 1; + while cp_index < constant_pool_count { + // println!("cp#{}", cp_index); + constant_pool.insert(cp_index, read_constant_pool_entry(&mut cp_index, &mut index, &bytecode)); + cp_index += 1; + } + + let constant_pool = Rc::new(constant_pool); + + let access_flags = read_u16(&bytecode, index); + let this_class = read_u16(&bytecode, index + 2); + let super_class = read_u16(&bytecode, index + 4); + + let interfaces_count = read_u16(&bytecode, index + 6); + // println!("interfaces count: {}", interfaces_count); + index += 8; + let mut interfaces = vec![]; + for _ in 0..interfaces_count { + interfaces.push(read_u16(&bytecode, index)); + index += 2; + } + + let fields_count = read_u16(&bytecode, index); + index += 2; + let mut fields = vec![]; + for _ in 0..fields_count { + fields.push(read_field(constant_pool.clone(), &mut index, &bytecode)); + } + + let methods_count = read_u16(&bytecode, index); + index += 2; + let mut methods = HashMap::new(); + for _ in 0..methods_count { + let m = read_method(constant_pool.clone(), &mut index, &bytecode); + methods.insert(m.name(), m); + } + + let attributes_count = read_u16(&bytecode, index); + index += 2; + let mut attributes = HashMap::new(); + for _ in 0..attributes_count { + let some = read_attribute(constant_pool.clone(), &bytecode, &mut index); + if let Some(att) = some { + attributes.insert(att.0, att.1); + } else { + panic!(); // bug/not-implemented + } + } + + Some(Class { + minor_version: read_u16(&bytecode, 4), + major_version: read_u16(&bytecode, 6), + constant_pool, + access_flags, + this_class, + super_class, + interfaces, + fields, + methods, + attributes, + }) +} + +fn check_magic(bytecode: &[u8]) { + if bytecode[0..4] != [0xCA, 0xFE, 0xBA, 0xBE] { + panic!("Invalid class file"); + } +} + +fn read_constant_pool_entry(cp_index: &mut u16, index: &mut usize, bytecode: &[u8]) -> CpEntry { + let tag = bytecode[*index]; + // println!("#{}: {}", cp_index, tag); + match tag { + 1 => { + let len = read_u16(bytecode, *index + 1) as usize; + let utf: Vec = Vec::from(&bytecode[*index + 3..*index + 3 + len]); + *index += len + 3; + CpEntry::Utf8(String::from_utf8(utf).unwrap()) + } + 3 => { + let value = read_i32(bytecode, *index + 1); + *index += 5; + CpEntry::Integer(value) + } + 4 => { + let value = read_f32(bytecode, *index + 1); + *index += 5; + CpEntry::Float(value) + } + 5 => { + let value = read_i64(bytecode, *index + 1); + *index += 9; + let r = CpEntry::Long(value); + *cp_index += 1; + r + } + 6 => { + let value = read_f64(bytecode, *index + 1); + *index += 9; + let r = CpEntry::Double(value); + *cp_index += 1; + r + } + 7 => { + let name_index = read_u16(bytecode, *index + 1); + *index += 3; + CpEntry::ClassRef(name_index) + } + 8 => { + let string_index = read_u16(bytecode, *index + 1); + *index += 3; + CpEntry::StringRef(string_index) + } + 9 => { + let class_index = read_u16(bytecode, *index + 1); + let name_and_type_index = read_u16(bytecode, *index + 3); + *index += 5; + CpEntry::Fieldref(class_index, name_and_type_index) + } + 10 => { + let class_index = read_u16(bytecode, *index + 1); + let name_and_type_index = read_u16(bytecode, *index + 3); + *index += 5; + CpEntry::MethodRef(class_index, name_and_type_index) + } + 11 => { + let class_index = read_u16(bytecode, *index + 1); + let name_and_type_index = read_u16(bytecode, *index + 3); + *index += 5; + CpEntry::InterfaceMethodref(class_index, name_and_type_index) + } + 12 => { + let name_index = read_u16(bytecode, *index + 1) as usize; + let descriptor_index = read_u16(bytecode, *index + 3) as usize; + *index += 5; + CpEntry::NameAndType(name_index, descriptor_index) + } + // 15 MethodHandle, + // 16 MethodType, + // 17 Dynamic, + // 18 InvokeDynamic, + // 19 Module, + // 20 Package, + + _ => panic!("cp entry type not recognized") + } +} + +fn read_field(constant_pool: Rc>, index: &mut usize, bytecode: &[u8]) -> Field { + let access_flags = read_u16(bytecode, *index); + let name_index = read_u16(bytecode, *index + 2); + let descriptor_index = read_u16(bytecode, *index + 4); + let attributes_count = read_u16(bytecode, *index + 6); + *index += 8; + let mut attributes = HashMap::new(); + for _ in 0..attributes_count { + if let Some(att) = read_attribute(constant_pool.clone(), bytecode, index) { + attributes.insert(att.0, att.1); + } else { + panic!("attribute not recognized"); // bug/not-implemented + } + } + Field::new( + constant_pool, + access_flags, + name_index, + descriptor_index, + attributes, + ) +} + +fn read_method(constant_pool: Rc>, index: &mut usize, bytecode: &[u8]) -> Method { + let access_flags = read_u16(bytecode, *index); + let name_index = read_u16(bytecode, *index + 2); + let descriptor_index = read_u16(bytecode, *index + 4); + let attributes_count = read_u16(bytecode, *index + 6); + *index += 8; + + let mut attributes = HashMap::new(); + for _ in 0..attributes_count { + if let Some(att) = read_attribute(constant_pool.clone(), bytecode, index) { + attributes.insert(att.0, att.1); + } + } + + Method::new( + constant_pool, + access_flags, + name_index, + descriptor_index, + attributes, + ) +} + +fn read_attribute(constant_pool: Rc>, bytecode: &[u8], index: &mut usize) -> Option<(String, AttributeType)> { + let attribute_name_index = read_u16(bytecode, *index); + *index += 2; + let attribute_length = read_u32(bytecode, *index) as usize; + *index += 4; + let info: Vec = Vec::from(&bytecode[*index..*index + attribute_length]); + *index += attribute_length; + + + if let CpEntry::Utf8(s) = &constant_pool.get(&attribute_name_index).unwrap() { + // println!("Att [{}]", s); + return match s.as_str() { + "ConstantValue" => { + assert_eq!(info.len(), 2); + Some(("ConstantValue".into(), AttributeType::ConstantValue(read_u16(&info, 0)))) + } + "Code" => { + let max_stack = read_u16(&info, 0); + let max_locals = read_u16(&info, 2); + let code_length = read_u32(&info, 4) as usize; + let code = Vec::from(&info[8..8 + code_length]); + let exception_table_length = read_u16(&info, 8 + code_length) as usize; + + let mut code_index = 10 + code_length; + let mut exception_table = vec![]; + for _ in 0..exception_table_length { + exception_table.push(Exception::read(&info, code_index)); + code_index += 8; + } + let attribute_count = read_u16(&info, code_index); + code_index += 2; + let mut code_attributes = HashMap::new(); + for _ in 0..attribute_count { + if let Some(att) = read_attribute(constant_pool.clone(), &info, &mut code_index) { + code_attributes.insert(att.0, att.1); + } + } + Some(("Code".into(), AttributeType::Code(MethodCode::new(max_stack, max_locals, code, exception_table, code_attributes)))) + } + "SourceFile" => Some(("SourceFile".into(), AttributeType::SourceFile)), + "LineNumberTable" => Some(("SourceFile".into(), AttributeType::LineNumberTable)), + _ => None + }; + } + None +} + +#[derive(Debug)] +pub enum CpEntry { + Utf8(String), + Integer(i32), + Float(f32), + Long(i64), + Double(f64), + ClassRef(u16), + StringRef(u16), + Fieldref(u16, u16), + MethodRef(u16, u16), + InterfaceMethodref(u16, u16), + NameAndType(usize, usize), +} + diff --git a/src/heap.rs b/src/heap.rs index 4b3f12f..0a35833 100644 --- a/src/heap.rs +++ b/src/heap.rs @@ -1,15 +1,16 @@ +use std::collections::HashMap; use std::rc::Rc; -use crate::class::Class; +use crate::class::{Class, Value}; pub(crate) struct Object { // locked: bool, // hashcode: i32, class: Rc, - data: Vec, + data: HashMap, //TODO optimize } impl Object { - pub fn new(class: Rc, data: Vec) -> Self { + pub fn new(class: Rc, data: HashMap) -> Self { Self { class, data, diff --git a/src/lib.rs b/src/lib.rs index 27529bd..3594e23 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,272 +3,5 @@ pub mod io; pub mod opcodes; pub mod vm; mod heap; - -use std::collections::HashMap; -use std::rc::Rc; -use crate::io::{read_f32, read_f64, read_i32, read_i64, read_u16, read_u32}; -use crate::class::{AttributeType, Class, MethodCode, Exception, Field, Method}; - -pub fn get_class(bytecode: Vec) -> Option { - check_magic(&bytecode); - - let constant_pool_count = read_u16(&bytecode, 8); - // println!("cp count: {}", constant_pool_count); - let mut index = 10; - let mut constant_pool: HashMap = HashMap::with_capacity(constant_pool_count as usize); - let mut cp_index = 1; - while cp_index < constant_pool_count { - // println!("cp#{}", cp_index); - constant_pool.insert(cp_index, read_constant_pool_entry(&mut cp_index, &mut index, &bytecode)); - cp_index += 1; - } - - let constant_pool = Rc::new(constant_pool); - - let access_flags = read_u16(&bytecode, index); - let this_class = read_u16(&bytecode, index + 2); - let super_class = read_u16(&bytecode, index + 4); - - let interfaces_count = read_u16(&bytecode, index + 6); - // println!("interfaces count: {}", interfaces_count); - index += 8; - let mut interfaces = vec![]; - for _ in 0..interfaces_count { - interfaces.push(read_u16(&bytecode, index)); - index += 2; - } - - let fields_count = read_u16(&bytecode, index); - index += 2; - let mut fields = vec![]; - for _ in 0..fields_count { - fields.push(read_field(constant_pool.clone(), &mut index, &bytecode)); - } - - let methods_count = read_u16(&bytecode, index); - index += 2; - let mut methods = HashMap::new(); - for _ in 0..methods_count { - let m = read_method(constant_pool.clone(), &mut index, &bytecode); - methods.insert(m.name(), m); - } - - let attributes_count = read_u16(&bytecode, index); - index += 2; - let mut attributes = HashMap::new(); - for _ in 0..attributes_count { - let some = read_attribute(constant_pool.clone(), &bytecode, &mut index); - if let Some(att) = some { - attributes.insert(att.0, att.1); - } else { - panic!(); // bug/not-implemented - } - } - - Some(Class { - minor_version: read_u16(&bytecode, 4), - major_version: read_u16(&bytecode, 6), - constant_pool, - access_flags, - this_class, - super_class, - interfaces, - fields, - methods, - attributes, - }) -} - -fn check_magic(bytecode: &[u8]) { - if bytecode[0..4] != [0xCA, 0xFE, 0xBA, 0xBE] { - panic!("Invalid class file"); - } -} - -fn read_constant_pool_entry(cp_index: &mut u16, index: &mut usize, bytecode: &[u8]) -> CpEntry { - let tag = bytecode[*index]; - // println!("#{}: {}", cp_index, tag); - match tag { - 1 => { - let len = read_u16(bytecode, *index + 1) as usize; - let utf: Vec = Vec::from(&bytecode[*index + 3..*index + 3 + len]); - *index += len + 3; - CpEntry::Utf8(String::from_utf8(utf).unwrap()) - } - 3 => { - let value = read_i32(bytecode, *index + 1); - *index += 5; - CpEntry::Integer(value) - } - 4 => { - let value = read_f32(bytecode, *index + 1); - *index += 5; - CpEntry::Float(value) - } - 5 => { - let value = read_i64(bytecode, *index + 1); - *index += 9; - let r = CpEntry::Long(value); - *cp_index += 1; - r - } - 6 => { - let value = read_f64(bytecode, *index + 1); - *index += 9; - let r = CpEntry::Double(value); - *cp_index += 1; - r - } - 7 => { - let name_index = read_u16(bytecode, *index + 1); - *index += 3; - CpEntry::ClassRef(name_index) - } - 8 => { - let string_index = read_u16(bytecode, *index + 1); - *index += 3; - CpEntry::StringRef(string_index) - } - 9 => { - let class_index = read_u16(bytecode, *index + 1); - let name_and_type_index = read_u16(bytecode, *index + 3); - *index += 5; - CpEntry::Fieldref(class_index, name_and_type_index) - } - 10 => { - let class_index = read_u16(bytecode, *index + 1); - let name_and_type_index = read_u16(bytecode, *index + 3); - *index += 5; - CpEntry::MethodRef(class_index, name_and_type_index) - } - 11 => { - let class_index = read_u16(bytecode, *index + 1); - let name_and_type_index = read_u16(bytecode, *index + 3); - *index += 5; - CpEntry::InterfaceMethodref(class_index, name_and_type_index) - } - 12 => { - let name_index = read_u16(bytecode, *index + 1) as usize; - let descriptor_index = read_u16(bytecode, *index + 3) as usize; - *index += 5; - CpEntry::NameAndType(name_index, descriptor_index) - } - // 15 MethodHandle, - // 16 MethodType, - // 17 Dynamic, - // 18 InvokeDynamic, - // 19 Module, - // 20 Package, - - _ => panic!("cp entry type not recognized") - } -} - -fn read_field(constant_pool: Rc>, index: &mut usize, bytecode: &[u8]) -> Field { - let access_flags = read_u16(bytecode, *index); - let name_index = read_u16(bytecode, *index + 2); - let descriptor_index = read_u16(bytecode, *index + 4); - let attributes_count = read_u16(bytecode, *index + 6); - *index += 8; - let mut attributes = HashMap::new(); - for _ in 0..attributes_count { - if let Some(att) = read_attribute(constant_pool.clone(), bytecode, index) { - attributes.insert(att.0, att.1); - } else { - panic!("attribute not recognized"); // bug/not-implemented - } - } - Field::new( - constant_pool, - access_flags, - name_index, - descriptor_index, - attributes, - ) -} - -fn read_method(constant_pool: Rc>, index: &mut usize, bytecode: &[u8]) -> Method { - let access_flags = read_u16(bytecode, *index); - let name_index = read_u16(bytecode, *index + 2); - let descriptor_index = read_u16(bytecode, *index + 4); - let attributes_count = read_u16(bytecode, *index + 6); - *index += 8; - - let mut attributes = HashMap::new(); - for _ in 0..attributes_count { - if let Some(att) = read_attribute(constant_pool.clone(), bytecode, index) { - attributes.insert(att.0, att.1); - } - } - - Method::new( - constant_pool, - access_flags, - name_index, - descriptor_index, - attributes, - ) -} - -fn read_attribute(constant_pool: Rc>, bytecode: &[u8], index: &mut usize) -> Option<(String, AttributeType)> { - let attribute_name_index = read_u16(bytecode, *index); - *index += 2; - let attribute_length = read_u32(bytecode, *index) as usize; - *index += 4; - let info: Vec = Vec::from(&bytecode[*index..*index + attribute_length]); - *index += attribute_length; - - - if let CpEntry::Utf8(s) = &constant_pool.get(&attribute_name_index).unwrap() { - // println!("Att [{}]", s); - return match s.as_str() { - "ConstantValue" => { - assert_eq!(info.len(), 2); - Some(("ConstantValue".into(), AttributeType::ConstantValue(read_u16(&info, 0)))) - } - "Code" => { - let max_stack = read_u16(&info, 0); - let max_locals = read_u16(&info, 2); - let code_length = read_u32(&info, 4) as usize; - let code = Vec::from(&info[8..8 + code_length]); - let exception_table_length = read_u16(&info, 8 + code_length) as usize; - - let mut code_index = 10 + code_length; - let mut exception_table = vec![]; - for _ in 0..exception_table_length { - exception_table.push(Exception::read(&info, code_index)); - code_index += 8; - } - let attribute_count = read_u16(&info, code_index); - code_index += 2; - let mut code_attributes = HashMap::new(); - for _ in 0..attribute_count { - if let Some(att) = read_attribute(constant_pool.clone(), &info, &mut code_index) { - code_attributes.insert(att.0, att.1); - } - } - Some(("Code".into(), AttributeType::Code(MethodCode::new(max_stack, max_locals, code, exception_table, code_attributes)))) - } - "SourceFile" => Some(("SourceFile".into(), AttributeType::SourceFile)), - "LineNumberTable" => Some(("SourceFile".into(), AttributeType::LineNumberTable)), - _ => None - }; - } - None -} - -#[derive(Debug)] -pub enum CpEntry { - Utf8(String), - Integer(i32), - Float(f32), - Long(i64), - Double(f64), - ClassRef(u16), - StringRef(u16), - Fieldref(u16, u16), - MethodRef(u16, u16), - InterfaceMethodref(u16, u16), - NameAndType(usize, usize), -} +mod classloader; diff --git a/src/vm.rs b/src/vm.rs index fbabd98..b22b158 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -1,9 +1,11 @@ use std::collections::HashMap; use std::rc::Rc; -use crate::{CpEntry, opcodes}; + +use crate::opcodes; +use crate::class::{AttributeType, Class, Method, Value}; +use crate::classloader::{CpEntry, load_class}; use crate::heap::{Heap, Object}; use crate::io::*; -use crate::class::{AttributeType, Class, Method, Value}; struct StackFrame { data: Vec, @@ -26,7 +28,7 @@ impl StackFrame { } pub struct Vm { - classes: HashMap, + classes: HashMap, //TODO implement classloader heap: Heap, } @@ -38,11 +40,27 @@ impl Vm { } } - pub fn new_instance(&self, class: &Class) { + pub fn load_class(name: String){ + load_class() + } + + pub fn new_instance(&self, class: Rc) { + let mut data = HashMap::new(); for f in &class.fields { - println!("{}", f.type_of()); + let value = match f.type_of().as_str(){ + "Z" => Value::BOOL(false), + "B" => Value::I32(0), + "S" => Value::I32(0), + "I" => Value::I32(0), + "J" => Value::I64(0), + "F" => Value::F32(0.0), + "D" => Value::F64(0.0), + //ref + _ => Value::Void + }; + data.insert(f.name_index, value); } - // Object::new(Rc::new(class)) + Object::new(class.clone(), data); } pub fn execute(&mut self, method: &Method) -> Option { diff --git a/tests/class_tests.rs b/tests/class_tests.rs index 909e58f..55227de 100644 --- a/tests/class_tests.rs +++ b/tests/class_tests.rs @@ -1,4 +1,5 @@ mod test { + use std::rc::Rc; use classfile_reader::{get_class, io}; use classfile_reader::class::Value; use classfile_reader::vm::Vm; @@ -30,7 +31,7 @@ mod test { #[test] fn get_constant_foat() { let class = get_class(io::read_class_file("tests/Float.class")).unwrap(); - Vm::new().new_instance(&class); + Vm::new().new_instance(Rc::new(class)); // assert_eq!((55, 0), class.get_version()); // if let Value::F32(v) = Vm::new().execute(class.methods.get("public static getF()F").unwrap()).unwrap() { // assert_eq!(v, 42.0);