diff --git a/src/class.rs b/src/class.rs index a8b877a..a557b57 100644 --- a/src/class.rs +++ b/src/class.rs @@ -2,6 +2,7 @@ use std::cell::{Ref, RefCell, RefMut, UnsafeCell}; use std::collections::HashMap; use std::fmt; use std::hash::Hash; +use std::os::macos::raw::stat; use std::rc::Rc; use std::sync::Arc; @@ -20,9 +21,17 @@ static mut CLASSDEFS: Lazy>>> = Lazy::new(|| // gets the Class from cache, or reads it from classpath, // then parses the binary data into a Class struct // Vm keeps ownership of the class and hands out Arc references to it -pub fn get_class(vm: &mut Vm, class_name: &str) -> Result>, Error> { +pub fn get_class(vm: &mut Vm, calling_class_name: Option<&str>, class_name: &str) -> Result>, Error> { println!("get_class {}", class_name); + unsafe { + // not pretty...sorry + if let Some(calling_class_name) = calling_class_name { + if class_name == calling_class_name { // works around the situation that static initializer needs a ref to the class it's in + return Ok(CLASSDEFS.get(class_name.into()).unwrap().clone()); // in that case the class is guaranteed to be here + } + } + let new_class = CLASSDEFS.entry(class_name.into()).or_insert_with(|| { println!("read class {} ", class_name); let resolved_path = find_class(&vm.classpath, class_name).unwrap(); @@ -31,17 +40,24 @@ pub fn get_class(vm: &mut Vm, class_name: &str) -> Result>, E let mut class = load_class(bytecode).unwrap(); let super_class_name = class.super_class_name.as_ref(); if let Some(super_class_name) = super_class_name { - if let Ok(super_class) = get_class(vm, &super_class_name) { + if let Ok(super_class) = get_class(vm, Some(class_name), &super_class_name) { class.super_class = Some(super_class); } } - Arc::new(RefCell::new(class)) + + let class = Arc::new(RefCell::new(class)); + Class::initialize_fields(class.clone()); + class }); - Class::initialize_fields(new_class.clone()); - let exec = new_class.borrow().methods.contains_key("()V"); - if exec { + // calling clinit before the end of this function has been a PITA + // 1. infinite recursion + // panic after second borrow. + // the problem is pretty fundamental: method (clinit) should be called before the class is returned, + // but the executing code needs a reference to itself. So get_class is called recursively, but clinit must be called exactly once! + // putting the call to clinit in the closure above is way nicer, but the signature change (wrap it in Arc) + if new_class.borrow().methods.contains_key("()V") { vm.execute_class(new_class.clone(), "()V", vec![]).unwrap(); } @@ -70,7 +86,7 @@ pub struct Class { // first key: this/super/supersuper-name(etc), second key: fieldname, value (type, index) pub(crate) static_field_mapping: HashMap>, // first key: this/super/supersuper-name(etc), second key: fieldname, value (type, index) - pub(crate) static_data: Vec, + pub(crate) static_data: Vec>, } impl Class { @@ -109,11 +125,11 @@ impl Class { } pub(crate) fn n_object_fields(&self) -> usize { - self.object_field_mapping.len() + self.object_field_mapping.iter().map(|(_,v)|v.len()).reduce(|acc, e| acc + e).unwrap() } pub(crate) fn n_static_fields(&self) -> usize { - self.object_field_mapping.len() + self.static_field_mapping.iter().map(|(_,v)|v.len()).reduce(|acc, e| acc + e).unwrap() } // Create a mapping per field(name) to an index in the storage vector that contains the instance data. @@ -127,9 +143,10 @@ impl Class { pub fn initialize_fields(class: Arc>) { let mut this_field_mapping = HashMap::new(); let mut static_field_mapping = HashMap::new(); - let mut field_map_index: usize = 0; + let mut object_field_map_index: usize = 0; + let mut static_field_map_index: usize = 0; - Class::add_field_mappings(&mut this_field_mapping, &mut static_field_mapping, class.clone(), &mut field_map_index); + Class::add_field_mappings(&mut this_field_mapping, &mut static_field_mapping, class.clone(), &mut object_field_map_index, &mut static_field_map_index); class.borrow_mut().object_field_mapping = this_field_mapping; class.borrow_mut().static_field_mapping = static_field_mapping; @@ -140,22 +157,25 @@ impl Class { fn add_field_mappings(this_field_mapping: &mut HashMap>, static_field_mapping: &mut HashMap>, - class: Arc>, field_map_index: &mut usize) { - let (o, s) = Class::map_fields(class.clone(), field_map_index); + class: Arc>, + object_field_map_index: &mut usize, + static_field_map_index: &mut usize) { + let (o, s) = Class::map_fields(class.clone(), object_field_map_index, static_field_map_index); let borrow = class.borrow(); let name = &borrow.name; this_field_mapping.insert(name.to_owned(), o); static_field_mapping.insert(name.to_owned(), s); if let Some(super_class) = class.borrow().super_class.as_ref() { - Class::add_field_mappings(this_field_mapping, static_field_mapping, super_class.clone(), field_map_index); + Class::add_field_mappings(this_field_mapping, static_field_mapping, super_class.clone(), object_field_map_index, static_field_map_index); } } // part of the initialize procedure fn map_fields( class: Arc>, - field_map_index: &mut usize, + object_field_map_index: &mut usize, + static_field_map_index: &mut usize, ) -> (HashMap, HashMap) { let mut this_fields = HashMap::new(); //fields in class are stored per class and every superclass. let mut static_fields = HashMap::new(); //fields in class are stored per class and every superclass. @@ -164,15 +184,17 @@ impl Class { if field.is(Modifier::Static) { static_fields.insert( name.to_owned(), - (field.type_of().to_owned(), *field_map_index), + (field.type_of().to_owned(), *static_field_map_index), ); + *static_field_map_index += 1; } else { this_fields.insert( name.to_owned(), - (field.type_of().to_owned(), *field_map_index), + (field.type_of().to_owned(), *object_field_map_index), ); //name => (type,index) + *object_field_map_index += 1; } - *field_map_index += 1; + } (this_fields, static_fields) } @@ -205,11 +227,11 @@ impl Class { } } - pub(crate) fn set_field_data(class: Arc>) -> Vec { - let mut field_data = Vec::with_capacity(class.borrow().n_object_fields()); + pub(crate) fn set_field_data(class: Arc>) -> Vec> { + let mut field_data = vec![None; class.borrow().n_static_fields()]; - for (_, fields) in &class.borrow().static_field_mapping { - for (_, (fieldtype, _)) in fields { + for (_, this_class) in &class.borrow().static_field_mapping { + for (name, (fieldtype, index)) in this_class { let value = match fieldtype.as_str() { "Z" => Value::BOOL(false), "B" => Value::I32(0), @@ -218,10 +240,10 @@ impl Class { "J" => Value::I64(0), "F" => Value::F32(0.0), "D" => Value::F64(0.0), - "L" => Value::Null, - _ => Value::Void, + _ => Value::Null, }; - field_data.push(value.into()); + println!("{} = {:?}", name, value ); + field_data[*index] = Some(value.into()); } } diff --git a/src/classloader.rs b/src/classloader.rs index 45f3d0a..c6ca86b 100644 --- a/src/classloader.rs +++ b/src/classloader.rs @@ -226,7 +226,7 @@ fn read_attribute( *index += attribute_length; if let CpEntry::Utf8(s) = &constant_pool.get(&attribute_name_index).unwrap() { - // println!("Att [{}]", s); + println!("Att [{}]", s); return match s.as_str() { "ConstantValue" => { assert_eq!(info.len(), 2); @@ -273,6 +273,7 @@ fn read_attribute( "NestMembers" => Some(("".into(), AttributeType::NestMembers)), //stub "BootstrapMethods" => Some(("".into(), AttributeType::BootstrapMethods)), //stub "InnerClasses" => Some(("".into(), AttributeType::InnerClasses)), //stub + "Signature" => Some(("".into(), AttributeType::Signature)), //stub //TODO more actual attribute implementations _ => None, }; diff --git a/src/heap.rs b/src/heap.rs index 6bb6735..4ddedce 100644 --- a/src/heap.rs +++ b/src/heap.rs @@ -56,8 +56,7 @@ impl Object { "J" => Value::I64(0), "F" => Value::F32(0.0), "D" => Value::F64(0.0), - "L" => Value::Null, - _ => Value::Void, + _ => Value::Null, }; field_data.push(value.into()); } diff --git a/src/main.rs b/src/main.rs index 72649c2..94df0b8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,7 +9,7 @@ fn main() -> Result<(), Error> { // let main_class = "Inheritance"; let main_class = "Main"; - vm.execute(main_class, "main([Ljava/lang/String;)V", vec![]) + vm.execute(None,main_class, "main([Ljava/lang/String;)V", vec![]) .unwrap(); Ok(()) } diff --git a/src/opcodes.rs b/src/opcodes.rs index 17cc95d..2d209de 100644 --- a/src/opcodes.rs +++ b/src/opcodes.rs @@ -132,7 +132,7 @@ pub const INVOKEVIRTUAL: u8 = 182; // (0xb6) Invoke instance method; dispatch ba pub const INVOKESPECIAL: u8 = 183; // (0xb7) // nvoke instance method; direct invocation of instance initialization methods and methods of the current class and its supertypes pub const INVOKESTATIC: u8 = 184; // (0xb8) Invoke a class (static) method pub const NEW: u8 = 187; // (0xbb) Create new object -pub const anewarray: u8 = 189; // (0xbd) +pub const ANEWARRAY: u8 = 189; // (0xbd) pub const arraylength: u8 = 190; // (0xbe) pub const athrow: u8 = 191; // (0xbf) pub const checkcast: u8 = 192; // (0xc0) diff --git a/src/vm.rs b/src/vm.rs index 39c2d80..84191d0 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -1,5 +1,6 @@ use std::cell::{RefCell, UnsafeCell}; use std::collections::HashMap; +use std::ops::Deref; use std::rc::Rc; use std::sync::Arc; @@ -39,6 +40,10 @@ impl StackFrame { fn pop(&mut self) -> Result { Ok(self.data.pop().unwrap()) } + + fn len(&self) -> usize { + self.data.len() + } } @@ -84,11 +89,12 @@ impl Vm { /// contains unsafe, as I think that mimics not-synchronized memory access in the original JVM pub fn execute( &mut self, + calling_class_name: Option<&str>, class_name: &str, method_name: &str, args: Vec, ) -> Result { - let class = get_class(self, class_name)?; + let class = get_class(self, calling_class_name, class_name)?; self.execute_class(class, method_name, args) } @@ -114,7 +120,7 @@ impl Vm { let mut pc = &mut 0; while *pc < code.opcodes.len() { let opcode = read_u8(&code.opcodes, pc); - println!("opcode {} ", opcode); + println!("stack {} opcode {} ", self.local_stack().len(), opcode); match opcode { ACONST_NULL => { self.local_stack().push(Value::Null); @@ -171,13 +177,25 @@ impl Vm { } LDC => { let cp_index = read_u8(&code.opcodes, pc) as u16; - match method.constant_pool.get(&cp_index).unwrap() { + let c = method.constant_pool.get(&cp_index).unwrap(); + println!("{:?}", c); + match c { CpEntry::Integer(i) => { self.local_stack().push(Value::I32(*i)); } CpEntry::Float(f) => { self.local_stack().push(Value::F32(*f)); } + CpEntry::Double(d) => { + self.local_stack().push(Value::F64(*d)); + } + CpEntry::StringRef(utf8) => { + let string = get_class(self, Some(&this_class.borrow().name), "java/lang/String").unwrap(); + self.local_stack().push(Value::Ref(Arc::new(UnsafeCell::new(ObjectRef::Object(Box::new(Object::new(string))))))) + } + CpEntry::Long(l) => { + self.local_stack().push(Value::I64(*l)); + } _ => {} } } @@ -275,12 +293,14 @@ impl Vm { borrow.cp_field_ref(&cp_index).unwrap(); // all these unwraps are safe as long as the class is valid let (name_index, _) = borrow.cp_name_and_type(field_name_and_type_index).unwrap(); let (name) = borrow.cp_utf8(name_index).unwrap(); - let class_name_index = borrow.cp_class_ref(class_index).unwrap(); - let class_name = borrow.cp_utf8(class_name_index).unwrap(); - let that = get_class(self, class_name.as_str())?; - let borrow = that.borrow(); - let (_, val_index) = borrow.static_field_mapping.get(class_name).unwrap().get(name).unwrap(); - self.local_stack().push_arc(this_class.borrow().static_data.get(*val_index).unwrap().clone()); + + let that_class_name_index = borrow.cp_class_ref(class_index).unwrap(); + let that_class_name = borrow.cp_utf8(that_class_name_index).unwrap(); + let that = get_class(self, Some(&borrow.name), that_class_name.as_str())?; + let that_borrow = that.borrow(); + let (_, val_index) = that_borrow.static_field_mapping.get(that_class_name).unwrap().get(name).unwrap(); + println!("get static field {}", name); + self.local_stack().push_arc(borrow.static_data.get(*val_index).unwrap().as_ref().unwrap().clone()); } PUTSTATIC => { println!("putstatic"); @@ -291,12 +311,21 @@ impl Vm { let (name_index, _) = borrow.cp_name_and_type(field_name_and_type_index).unwrap(); let (name) = borrow.cp_utf8(name_index).unwrap(); let class_name_index = borrow.cp_class_ref(class_index).unwrap(); - let class_name = borrow.cp_utf8(class_name_index).unwrap(); - let that = get_class(self, class_name.as_str())?; - let that_borrow = that.borrow(); - let (_, val_index) = that_borrow.static_field_mapping.get(class_name).unwrap().get(name).unwrap(); - let value = self.local_stack().pop()?; - borrow.static_data[*val_index] = value; + println!("field {}", name); + let that_class_name = borrow.cp_utf8(class_name_index).unwrap(); + + if &borrow.name == that_class_name { + let (_, val_index) = borrow.static_field_mapping.get(that_class_name).unwrap().get(name).as_ref().unwrap(); + let val_index = *val_index; + let value = self.local_stack().pop()?; + borrow.static_data[val_index] = Some(value); + } else { + let that = get_class(self, Some(&borrow.name), that_class_name.as_str())?; + let that_borrow = that.borrow(); // if already borrowed, then that_class == this_class + let (_, val_index) = that_borrow.static_field_mapping.get(that_class_name).unwrap().get(name).unwrap(); + let value = self.local_stack().pop()?; + borrow.static_data[*val_index] = Some(value); + } } GETFIELD => unsafe { let borrow = this_class.borrow(); @@ -347,6 +376,7 @@ impl Vm { } args.insert(0, self.local_stack().pop()?); let mut return_value = self.execute( + Some(this_class.borrow().name.as_str()), &invocation.class_name, &invocation.method.name, args, @@ -369,6 +399,7 @@ impl Vm { args.insert(0, self.local_stack().pop()?); } let mut returnvalue = self.execute( + Some(this_class.borrow().name.as_str()), &invocation.class_name, &invocation.method.name, args, @@ -386,7 +417,7 @@ impl Vm { let borrow = this_class.borrow(); let class_name_index = borrow.cp_class_ref(class_index).unwrap(); let class_name = borrow.cp_utf8(class_name_index).unwrap(); - let class_to_instantiate = get_class(self, class_name)?; + let class_to_instantiate = get_class(self, Some(&borrow.name), class_name)?; let object = Arc::new(UnsafeCell::new(ObjectRef::Object(Box::new( Vm::new_instance(class_to_instantiate), diff --git a/tests/Main.class b/tests/Main.class index ba1b541..7474a5a 100644 Binary files a/tests/Main.class and b/tests/Main.class differ diff --git a/tests/Main.java b/tests/Main.java index 38521d5..8f08233 100644 --- a/tests/Main.java +++ b/tests/Main.java @@ -9,6 +9,6 @@ public class Main { public static void main(String[] args){ FloatBean f = new FloatBean(); f.setValue(42F); -// System.out.println(f.getValue()); + System.out.println(f.getValue()); } }