From 6cf0365dd0709a9100fd25b1491de0fc4d88f8e2 Mon Sep 17 00:00:00 2001 From: Sander Hautvast Date: Fri, 13 Oct 2023 17:00:44 +0200 Subject: [PATCH] proper implementation for non-statics --- Cargo.lock | 7 ++ Cargo.toml | 1 + src/class.rs | 157 +++++++++++++++++++++++++++++++++++------ src/classloader.rs | 49 +++++++++---- src/heap.rs | 73 ++++++++++++++----- src/opcodes.rs | 2 +- src/vm.rs | 133 ++++++++++++++++++---------------- tests/Inheritance.java | 44 ++++++++++++ tests/Main.class | Bin 476 -> 328 bytes tests/Main.java | 2 +- 10 files changed, 353 insertions(+), 115 deletions(-) create mode 100644 tests/Inheritance.java diff --git a/Cargo.lock b/Cargo.lock index 1701c19..04166b4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -196,6 +196,7 @@ name = "java_rs" version = "0.1.0" dependencies = [ "anyhow", + "once_cell", "zip", ] @@ -223,6 +224,12 @@ dependencies = [ "adler", ] +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + [[package]] name = "password-hash" version = "0.4.2" diff --git a/Cargo.toml b/Cargo.toml index d99ee17..a361b28 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,4 +5,5 @@ edition = "2021" [dependencies] anyhow = { version = "1.0", features = ["default"] } +once_cell = { version = "1.18.0", features = [] } zip = { version = "0.6", features = ["zstd"] } \ No newline at end of file diff --git a/src/class.rs b/src/class.rs index f8f9e8a..41207b6 100644 --- a/src/class.rs +++ b/src/class.rs @@ -4,27 +4,97 @@ use crate::heap::{Object, ObjectRef}; use anyhow::{anyhow, Error}; use std::collections::HashMap; use std::fmt; +use std::hash::Hash; use std::rc::Rc; use std::sync::Arc; use crate::io::read_u16; +// the class definition as read from the class file + derived values #[derive(Debug)] -//TODO create factory function pub struct Class { pub minor_version: u16, pub major_version: u16, pub constant_pool: Rc>, pub access_flags: u16, - pub this_class: u16, - pub super_class: u16, - pub interfaces: Vec, - pub fields: Vec, + pub name: String, + pub super_class_name: Option, + pub super_class: Option>, + pub interface_indices: Vec, + pub interfaces: Vec, + pub fields: HashMap, pub methods: HashMap, pub attributes: HashMap, + pub(crate) field_mapping: Option>>, // first key: this/super/supersuper-name(etc), second key: fieldname, value (type, index) } impl Class { + pub fn new(minor_version: u16, + major_version: u16, + constant_pool: Rc>, + access_flags: u16, + this_class: u16, + super_class_index: u16, + interface_indices: Vec, + fields: HashMap, + methods: HashMap, + attributes: HashMap) -> Self { + let name = Class::class_name(this_class, constant_pool.clone()).unwrap(); + let super_class_name = Class::class_name(super_class_index, constant_pool.clone()); + + Self { + major_version, + minor_version, + constant_pool, + access_flags, + name, + super_class_name, + super_class: None, // has to be instantiated later, because it involves classloading. maybe not store it here + interface_indices, + interfaces: vec![], // same + fields, + methods, + attributes, + field_mapping: None, + } + } + + pub(crate) fn n_fields(&self) -> usize { + self.field_mapping.as_ref().map_or(0, |m| m.len()) + } + + // Create a mapping per field(name) to an index in the storage vector that contains the instance data. + // When a field is stored, first the index will be looked up, using the qualified name (from the FieldRef) + // The qualified name is the combination of class name and field name. + // The class name is needed as part of the key to separate class from superclass fields + // (duplicates in the singular field name are allowed). + // This way `this.a` can be differentiated from `super.a`. + // + // this method looks up this and super classes and calls map_fields for each. + pub fn initialize(&mut self) { + let mut field_mapping = HashMap::new(); + let mut field_map_index: usize = 0; + + Class::map_fields(&mut field_mapping, self, &mut field_map_index); + let mut sooper = &self.super_class; + while let Some(super_class) = sooper { + Class::map_fields(&mut field_mapping, super_class, &mut field_map_index); + sooper = &super_class.super_class; + } + self.field_mapping = Some(field_mapping); + } + + // part of the initialize procedure + fn map_fields(field_mapping: &mut HashMap>, class: &Class, field_map_index: &mut usize) { + let mut this_fields = HashMap::new(); //fields in class are stored per class and every superclass. + for field in &class.fields { + this_fields.insert(field.0.to_owned(), (field.1.type_of().to_owned(), *field_map_index)); //name => (type,index) + *field_map_index += 1; + } + let this_name = class.name.to_owned(); + field_mapping.insert(this_name, this_fields); + } + pub fn get_version(&self) -> (u16, u16) { (self.major_version, self.minor_version) } @@ -35,16 +105,58 @@ impl Class { .ok_or(anyhow!("Method {} not found", name)) } - pub fn get_name(&self) -> &str { - if let CpEntry::ClassRef(name_index ) = self.constant_pool.get(&self.this_class).unwrap(){ - if let CpEntry::Utf8(name) = self.constant_pool.get(name_index).unwrap(){ - return name; + fn class_name(super_class_index: u16, constant_pool: Rc>) -> Option { + if super_class_index == 0 { + None + } else if let CpEntry::ClassRef(name_index) = constant_pool.get(&super_class_index).unwrap() { + if let CpEntry::Utf8(name) = constant_pool.get(name_index).unwrap() { + Some(name.to_owned()) + } else { + None } + } else { + None } - panic!(); } + + // convienence methods for data from the constantpool + + pub fn get_field_ref(&self, index: &u16) -> Option<(&u16, &u16)> { + if let CpEntry::Fieldref(class_index, name_and_type_index) = self.constant_pool.get(index).unwrap() { + Some((class_index, name_and_type_index)) + } else { + None + } + } + + pub fn get_class_ref(&self, index: &u16) -> Option<&u16> { + if let CpEntry::ClassRef(name_index) = self.constant_pool.get(index).unwrap() { + Some(name_index) + } else { + None + } + } + + pub fn get_utf8(&self, index: &u16) -> Option<&String> { + if let CpEntry::Utf8(utf8) = self.constant_pool.get(index).unwrap() { + Some(utf8) + } else { + None + } + } + + pub fn get_name_and_type(&self, index: &u16) -> Option<(&u16, &u16)> { + if let CpEntry::NameAndType(name_index, type_index) = self.constant_pool.get(index).unwrap(){ + Some((name_index, type_index)) + } else { + None + } + } + } + unsafe impl Send for Class {} + unsafe impl Sync for Class {} pub struct Method { @@ -101,6 +213,7 @@ pub struct Field { pub(crate) name_index: u16, descriptor_index: u16, attributes: HashMap, + index: u16, } impl fmt::Debug for Field { @@ -120,6 +233,7 @@ impl Field { name_index: u16, descriptor_index: u16, attributes: HashMap, + field_index: u16, ) -> Self { Field { constant_pool, @@ -127,18 +241,15 @@ impl Field { name_index, descriptor_index, attributes, + index: field_index, } } - pub fn name(&self) -> String { - let mut name = String::new(); - - name.push(' '); - if let CpEntry::Utf8(s) = &self.constant_pool.get(&self.name_index).unwrap() { - name.push_str(s); + pub fn name(&self) -> &String { + if let CpEntry::Utf8(utf8) = &self.constant_pool.get(&self.name_index).unwrap() { + return utf8; } - - name + unreachable!() } pub fn type_of(&self) -> &String { @@ -174,10 +285,11 @@ pub fn get_modifier(modifier: u16) -> String { output } +//TODO implement more types #[derive(Debug)] pub enum AttributeType { ConstantValue(u16), - Code(MethodCode), + Code(Box), StackMapTable, BootstrapMethods, NestHost, @@ -256,8 +368,10 @@ impl MethodCode { #[derive(Debug)] pub enum Value { - Void, // variant returned for void methods - Null, // 'pointer' to nothing + Void, + // variant returned for void methods + Null, + // 'pointer' to nothing I32(i32), I64(i64), F32(f32), @@ -268,4 +382,5 @@ pub enum Value { } unsafe impl Send for Value {} + unsafe impl Sync for Value {} diff --git a/src/classloader.rs b/src/classloader.rs index 783cf57..3ab8f0a 100644 --- a/src/classloader.rs +++ b/src/classloader.rs @@ -4,6 +4,8 @@ use anyhow::Error; use std::collections::HashMap; use std::rc::Rc; + +// The native classoader pub fn load_class(bytecode: Vec) -> Result { let pos = &mut 0; check_magic(&bytecode, pos); @@ -37,9 +39,10 @@ pub fn load_class(bytecode: Vec) -> Result { } let fields_count = read_u16(&bytecode, pos); - let mut fields = vec![]; - for _ in 0..fields_count { - fields.push(read_field(constant_pool.clone(), pos, &bytecode)); + let mut fields = HashMap::new(); + for i in 0..fields_count { + let field = read_field(constant_pool.clone(), pos, &bytecode, i); + fields.insert(field.name().to_owned(), field); } let methods_count = read_u16(&bytecode, pos); @@ -56,11 +59,11 @@ pub fn load_class(bytecode: Vec) -> Result { if let Some(att) = some { attributes.insert(att.0, att.1); } else { - panic!(); // bug/not-implemented + panic!("attribute not found"); // bug/not-implemented } } - Ok(Class { + Ok(Class::new( minor_version, major_version, constant_pool, @@ -71,7 +74,7 @@ pub fn load_class(bytecode: Vec) -> Result { fields, methods, attributes, - }) + )) } fn check_magic(bytecode: &[u8], pos: &mut usize) { @@ -83,6 +86,7 @@ fn check_magic(bytecode: &[u8], pos: &mut usize) { fn read_constant_pool_entry(cp_index: &mut u16, index: &mut usize, bytecode: &[u8]) -> CpEntry { let tag = read_u8(bytecode, index); + // println!("tag {}", tag); match tag { 1 => { let len = read_u16(bytecode, index) as usize; @@ -137,10 +141,21 @@ fn read_constant_pool_entry(cp_index: &mut u16, index: &mut usize, bytecode: &[u let descriptor_index = read_u16(bytecode, index); CpEntry::NameAndType(name_index, descriptor_index) } - // 15 MethodHandle, - // 16 MethodType, + 15 =>{ + let reference_kind = read_u8(bytecode, index); + let reference_index = read_u16(bytecode, index); + CpEntry::MethodHandle(reference_kind, reference_index) + } + 16 => { + let descriptor_index = read_u16(bytecode, index); + CpEntry::MethodType(descriptor_index) + } // 17 Dynamic, - // 18 InvokeDynamic, + 18 => { + let bootstrap_method_attr_index = read_u16(bytecode, index); + let name_and_type_index = read_u16(bytecode, index); + CpEntry::InvokeDynamic(bootstrap_method_attr_index, name_and_type_index) + } // 19 Module, // 20 Package, _ => panic!("cp entry type not recognized"), @@ -151,6 +166,7 @@ fn read_field( constant_pool: Rc>, index: &mut usize, bytecode: &[u8], + field_index: u16, ) -> Field { let access_flags = read_u16(bytecode, index); let name_index = read_u16(bytecode, index); @@ -170,6 +186,7 @@ fn read_field( name_index, descriptor_index, attributes, + field_index, ) } @@ -210,7 +227,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); @@ -241,17 +258,22 @@ fn read_attribute( } Some(( "Code".into(), - AttributeType::Code(MethodCode::new( + AttributeType::Code(Box::new(MethodCode::new( max_stack, max_locals, code, exception_table, code_attributes, - )), + ))), )) } "SourceFile" => Some(("SourceFile".into(), AttributeType::SourceFile)), "LineNumberTable" => Some(("SourceFile".into(), AttributeType::LineNumberTable)), + "RuntimeVisibleAnnotations" => Some(("".into(), AttributeType::RuntimeInvisibleAnnotations)), //stub + "NestMembers" => Some(("".into(), AttributeType::NestMembers)),//stub + "BootstrapMethods" => Some(("".into(), AttributeType::BootstrapMethods)),//stub + "InnerClasses" => Some(("".into(), AttributeType::InnerClasses)),//stub + //TODO more actual attribute implementations _ => None, }; } @@ -271,4 +293,7 @@ pub enum CpEntry { MethodRef(u16, u16), InterfaceMethodref(u16, u16), NameAndType(u16, u16), + MethodHandle(u8, u16), + MethodType(u16), + InvokeDynamic(u16, u16), } diff --git a/src/heap.rs b/src/heap.rs index f60ab37..7b37629 100644 --- a/src/heap.rs +++ b/src/heap.rs @@ -7,15 +7,17 @@ use std::sync::Arc; use crate::class::{Class, Value}; use crate::classloader::CpEntry; +// trying to implement efficient object instance storage pub struct Object { // locked: bool, // hashcode: i32, pub class: Rc, - pub data: HashMap>>, //TODO optimize + pub data: Option>>>, }//arrays +// can contain object or array #[derive(Debug)] -pub enum ObjectRef{ +pub enum ObjectRef { ByteArray(Vec), ShortArray(Vec), IntArray(Vec), @@ -28,45 +30,78 @@ pub enum ObjectRef{ Object(Box), } - unsafe impl Send for Object {} unsafe impl Sync for Object {} +// object, not array impl Object { - pub fn new(class: Rc, data: HashMap>>) -> Self { - Self { class, data } + pub fn new(class: Rc) -> Self { + Self { class, data: None } } - fn get_field(&self, cp_index: &u16) -> &str { + // initializes all non-static fields to their default values + pub(crate) fn init_fields(&mut self) { + let mut field_data = Vec::with_capacity(self.class.n_fields()); + + for (_, fields) in self.class.field_mapping.as_ref().unwrap() { + for (_, (fieldtype, _)) in fields { + let value = match fieldtype.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), + "L" => Value::Null, + _ => Value::Void, + }; + field_data.push(Arc::new(UnsafeCell::new(value))); + } + } + + self.data = Some(field_data); + } + + pub fn set(&mut self, class_name: &String, field_name: &String, value: Arc>) { + let p = self.class.field_mapping.as_ref().unwrap(); + let p2 = p.get(class_name).unwrap(); + let (_type, index) = p2.get(field_name).unwrap(); + self.data.as_mut().unwrap()[*index] = value; + } + + pub fn get(&mut self, class_name: &String, field_name: &String) -> &Arc> { + let (_type, index) = &self.class.field_mapping.as_ref().unwrap().get(class_name).unwrap().get(field_name).unwrap(); // (index, type) + &self.data.as_ref().unwrap()[*index] + } + + fn get_field_name(&self, cp_index: &u16) -> &str { if let CpEntry::Utf8(name) = self.class.constant_pool.get(cp_index).unwrap() { return name; } panic!() } - - unsafe fn get_mut(ptr: &UnsafeCell) -> &mut T { - unsafe { &mut *ptr.get() } - } } impl fmt::Debug for Object { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let fields: Vec = self.data.iter().map(|(k, v)| { - let mut r: String = self.get_field(k).into(); - r.push(':'); - r.push_str(format!("{:?}", v).as_str()); - r - } - ).collect(); + // let fields: Vec = self.data.unwrap().iter().map(|(k)| { + // // let mut r: String = self.get_field_name(k).into(); + // // r.push(':'); + // // r.push_str(format!("{:?}").as_str()); + // // r + // } + // ).collect(); write!( f, - "{} {{ {:?} }}", - self.class.get_name(), fields + "{}", + self.class.name ) } } +// will using Arc's enable a GC-less heap???? pub(crate) struct Heap { objects: Vec>>, } diff --git a/src/opcodes.rs b/src/opcodes.rs index b2854a9..e53d7bd 100644 --- a/src/opcodes.rs +++ b/src/opcodes.rs @@ -121,7 +121,7 @@ pub const FRETURN: u8 = 174; // (0xae) Return float from method pub const DRETURN: u8 = 175; // (0xaf) Return double from method // pub const areturn: u8 = 176; //(0xb0) return reference pub const RETURN_VOID: u8 = 177; // (0xb1) Return void from method (actually 'return' but that's a keyword) - // pub const getstatic: u8 = 178; // (0xb2) Get static field from class +pub const GETSTATIC: u8 = 178; // (0xb2) Get static field from class pub const GETFIELD: u8 = 180; // (0xb4) Fetch field from object3 pub const PUTFIELD: u8 = 181; // (0xb5) Set field in object pub const INVOKEVIRTUAL: u8 = 182; // (0xb6) Invoke instance method; dispatch based on class diff --git a/src/vm.rs b/src/vm.rs index 5ad9aa4..0c9883c 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -4,6 +4,7 @@ use std::rc::Rc; use std::sync::Arc; use anyhow::{anyhow, Error}; +use once_cell::unsync::Lazy; use crate::class::{AttributeType, Class, Value}; use crate::class::Value::Void; @@ -18,6 +19,7 @@ struct StackFrame { data: Vec>>, } +// maybe just call frame impl StackFrame { fn new(at_class: &str, at_method: &str) -> Self { let mut at: String = at_class.into(); @@ -39,9 +41,11 @@ impl StackFrame { } } +//trying to be ready for multithreaded as much as possible, using Arc's and all, but it will still require (a lot of) extra work +static mut CLASSDEFS: Lazy>> = Lazy::new(|| HashMap::new()); //TODO add mutex...and Arc most likely + pub struct Vm { classpath: Vec, - classes: HashMap>, heap: Heap, stack: Vec, } @@ -52,7 +56,7 @@ const PATH_SEPARATOR: char = ':'; #[cfg(target_family = "windows")] const PATH_SEPARATOR: char = ';'; - +// The singlethreaded VM (maybe a future Thread) impl Vm { fn local_stack(&mut self) -> &mut StackFrame { let i = self.stack.len() - 1; @@ -62,49 +66,51 @@ impl Vm { pub fn new(classpath: &'static str) -> Self { Self { classpath: classpath.split(PATH_SEPARATOR).map(|s| s.to_owned()).collect(), - classes: HashMap::new(), heap: Heap::new(), stack: vec![], } } - /// parse the binary data into a Class struct - /// gets the file from cache, or reads it from classpath - /// Vm keeps ownership of the class and hands out Arc references to it - pub fn get_class(&mut self, class_name: &str) -> Result, Error> { + // parse the binary data into a Class struct + // gets the file from cache, or reads it from classpath + // Vm keeps ownership of the class and hands out Arc references to it + pub fn get_class(&self, class_name: &str) -> Result, Error> { println!("get_class {}", class_name); - let entry = self.classes.entry(class_name.into()); - let entry = entry.or_insert_with(|| { - // print!("read class {} ", class_name); - let resolved_path = find_class(&self.classpath, class_name).expect("Class not found"); - // println!("full path {}", resolved_path); - let bytecode = read_bytecode(resolved_path).unwrap(); - Rc::new(load_class(bytecode).unwrap()) - }); - Ok(entry.clone()) + unsafe { + let entry = CLASSDEFS.entry(class_name.into()); + let entry = entry.or_insert_with(|| { + // print!("read class {} ", class_name); + let resolved_path = find_class(&self.classpath, class_name).unwrap(); + // println!("full path {}", resolved_path); + let bytecode = read_bytecode(resolved_path).unwrap(); + 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) = self.get_class(&super_class_name) { + class.super_class = Some(super_class.clone()); + } + } + class.initialize(); + + Rc::new(class) + }); + Ok(entry.clone()) + } } - pub fn new_instance(&self, class: Rc) -> Object { - //TODO add fields from superclasses - let mut data = HashMap::new(); - for f in &class.fields { - 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), - "L" => Value::Null, - _ => Value::Void, - }; - data.insert(f.name_index, Arc::new(UnsafeCell::new(value))); - } - Object::new(class.clone(), data) + pub fn new_instance(class: Rc) -> Object { + let mut class = class; + // let mut outer = HashMap::new(); + //TODO + + let mut instance = Object::new(class.clone()); + instance.init_fields(); + instance } + /// execute the bytecode + /// contains unsafe, as I think that mimics not-synchronized memory access in the original JVM pub fn execute( &mut self, class_name: &str, @@ -269,41 +275,45 @@ impl Vm { self.stack.pop(); // Void is also returned as a value return Ok(Arc::new(UnsafeCell::new(Void))); } + GETSTATIC => { + let cp_index = read_u16(&code.opcodes, pc); + let (class_index, _field_name_and_type_index) = class.get_field_ref(&cp_index).unwrap(); + let class_name_index = class.get_class_ref(class_index).unwrap(); + let class_name = class.get_utf8(class_name_index).unwrap(); + let class = self.get_class(class_name.as_str())?; + println!("{:?}", class); //TODO + } GETFIELD => { unsafe { let cp_index = read_u16(&code.opcodes, pc); - if let CpEntry::Fieldref(_class_index, name_and_type_index) = - method.constant_pool.get(&cp_index).unwrap() - { - if let Value::Ref(instance) = &*self.local_stack().pop()?.get() { - if let CpEntry::NameAndType(name, _) = - method.constant_pool.get(name_and_type_index).unwrap() - { - let objectref = &(*instance.get()); - if let ObjectRef::Object(object) = objectref { - let value = object.data.get(name).unwrap(); - self.local_stack().push_arc(Arc::clone(value)); - } - } + let (class_index, field_name_and_type_index) = class.get_field_ref(&cp_index).unwrap(); + let (field_name_index, _) = class.get_name_and_type(field_name_and_type_index).unwrap(); + let class_name_index = class.get_class_ref(class_index).unwrap(); + let class_name = class.get_utf8(class_name_index).unwrap(); + let field_name = class.get_utf8(field_name_index).unwrap(); + + let mut objectref = self.local_stack().pop()?; + if let Value::Ref(instance) = &mut *objectref.get() { + if let ObjectRef::Object(ref mut object) = &mut *instance.get() { + let value = object.get(class_name, field_name); + self.local_stack().push_arc(Arc::clone(value)); } } } } PUTFIELD => unsafe { let cp_index = read_u16(&code.opcodes, pc); - if let CpEntry::Fieldref(_class_index, name_and_type_index) = - method.constant_pool.get(&cp_index).unwrap() - { - if let CpEntry::NameAndType(name_index, _) = method.constant_pool.get(name_and_type_index).unwrap() { - let value = self.local_stack().pop()?; - let mut objectref = self.local_stack().pop()?; - if let Value::Ref(instance) = &mut *objectref.get() { - if let ObjectRef::Object(ref mut object) = &mut *instance.get() { - object.data.insert(*name_index, value); - } else { - panic!("not an object, maybe array"); - } - } // else? + let (class_index, field_name_and_type_index) = class.get_field_ref(&cp_index).unwrap(); + let (field_name_index, _) = class.get_name_and_type(field_name_and_type_index).unwrap(); + let class_name_index = class.get_class_ref(class_index).unwrap(); + let class_name = class.get_utf8(class_name_index).unwrap(); + let field_name = class.get_utf8(field_name_index).unwrap(); + + let value = self.local_stack().pop()?; + let mut objectref = self.local_stack().pop()?; + if let Value::Ref(instance) = &mut *objectref.get() { + if let ObjectRef::Object(ref mut object) = &mut *instance.get() { + object.set(class_name, field_name, value); } } } @@ -348,7 +358,7 @@ impl Vm { { println!("new {}", new_class); let class = self.get_class(new_class)?; - let object = Arc::new(UnsafeCell::new(ObjectRef::Object(Box::new(self.new_instance(class))))); + let object = Arc::new(UnsafeCell::new(ObjectRef::Object(Box::new(Vm::new_instance(class))))); self.local_stack().push(Value::Ref(Arc::clone(&object))); self.heap.new_object(object); } @@ -523,6 +533,7 @@ fn get_name_and_type(cp: Rc>, index: u16) -> Option usize { let mut num = 0; let mut i = 1; diff --git a/tests/Inheritance.java b/tests/Inheritance.java new file mode 100644 index 0000000..0a7b427 --- /dev/null +++ b/tests/Inheritance.java @@ -0,0 +1,44 @@ +public class Inheritance { + + public static void main(String[] args) { + + Father father = new Son(); + System.out.println(father.i); //why 1? + System.out.println(father.getI()); //2 + System.out.println(father.j); //why 10? + System.out.println(father.getJ()); //why 10? + + System.out.println(); + + Son son = new Son(); + System.out.println(son.i); //2 + System.out.println(son.getI()); //2 + System.out.println(son.j); //20 + System.out.println(son.getJ()); //why 10? + } +} + +class Son extends Father { + + int i = 2; + int j = 20; + + @Override + public int getI() { + return i; + } +} + +class Father { + + int i = 1; + int j = 10; + + public int getI() { + return i; + } + + public int getJ() { + return j; + } +} \ No newline at end of file diff --git a/tests/Main.class b/tests/Main.class index 5b773bcd0f801c15768ecdf073de223b094e644a..91bf6e231a2ea46c3af78c883b38d5cfe0b0f45f 100644 GIT binary patch delta 198 zcmYL?y$%6U5QV>c_vhNR>{^RTp(03h^joOBfN~=`p;3E>eH4vNNE9mZ44y^I)tKU( zGc)JRyq%{WzCPDGu%H)WNDQqIP2%PwuqrjQ4OuzgWHkasu-qMrW4H5M-*c(@KS~2-k}x- z1+AlJXy02zccf((?!D*UbI#>{=&!`l=g;dqaLm4gL&31@peb1GIx=gDHY=>gsN_&) z&9H9Rm>yg;7H}1ICm4BA+xJ433RT0V%N8}4ZFUU1E_+m`kB1M6+_iV-wE{2fw|e)t zkv~)VN#w8@hD<4;AeWm|qjU%Zg$HuKhu