diff --git a/src/class.rs b/src/class.rs index 15699b3..9e45136 100644 --- a/src/class.rs +++ b/src/class.rs @@ -20,14 +20,21 @@ pub struct Class { pub initialized: bool, pub name: String, pub superclass: Option, - pub parents: LinkedList, + pub all_superclasses: LinkedList, // all superclasses in a flat list pub interfaces: Vec, + pub all_interfaces: Vec, // all interfaces and their parents in a flat list // lookup index and type from the name of the declared class and then field pub(crate) object_field_mapping: HashMap>, pub(crate) static_field_mapping: HashMap>, // pub(crate) static_field_data: Vec // moved to classmanager } +impl PartialEq for Class { + fn eq(&self, other: &Self) -> bool { + self.id == other.id + } +} + impl Class { /// gets the number of non-static fields on the class pub(crate) fn n_object_fields(&self) -> usize { diff --git a/src/classloader/classdef.rs b/src/classloader/classdef.rs index 3bd4149..e27ca50 100644 --- a/src/classloader/classdef.rs +++ b/src/classloader/classdef.rs @@ -94,6 +94,7 @@ impl ClassDef { } } + // gets the class name if the index is a class ref index pub fn cp_class_name(&self, index: &u16) -> &String { let cr = self.cp_class_ref(index); self.cp_utf8(cr) @@ -338,7 +339,7 @@ impl Debug for Method { } impl Method { - pub(crate) fn new( + pub fn new( constant_pool: Rc>, access_flags: u16, name_index: u16, diff --git a/src/classloader/mod.rs b/src/classloader/mod.rs index 03792a5..2be09f5 100644 --- a/src/classloader/mod.rs +++ b/src/classloader/mod.rs @@ -10,14 +10,14 @@ use log::debug; use crate::classloader::classdef::{ AttributeType, ClassDef, CpEntry, Exception, Field, Method, MethodCode, }; -use crate::classloader::code_parser::parse_code; use crate::classloader::io::{ find_class, read_bytes, read_f32, read_f64, read_i32, read_i64, read_u16, read_u32, read_u8, }; +use crate::classloader::opcode_parser::parse_opcodes; pub mod classdef; -mod code_parser; pub(crate) mod io; +mod opcode_parser; pub(crate) fn get_classdef(classpath: &Vec, class_name: &str) -> Result { debug!("read class {} ", class_name); @@ -250,7 +250,7 @@ fn read_method( } let code = if let Some(AttributeType::Code(code)) = attributes.get("Code") { - parse_code(&code.opcodes) + parse_opcodes(&code.opcodes) } else { vec![] }; diff --git a/src/classloader/code_parser.rs b/src/classloader/opcode_parser.rs similarity index 97% rename from src/classloader/code_parser.rs rename to src/classloader/opcode_parser.rs index de25a82..20da170 100644 --- a/src/classloader/code_parser.rs +++ b/src/classloader/opcode_parser.rs @@ -6,7 +6,7 @@ use crate::classloader::io::{ }; use crate::vm::opcodes::Opcode::{self, *}; -pub(crate) fn parse_code(opcodes: &[u8]) -> Vec { +pub(crate) fn parse_opcodes(opcodes: &[u8]) -> Vec { let mut code: BTreeMap = BTreeMap::new(); let mut c = 0; let mut opcode_index: u16 = 0; @@ -39,7 +39,8 @@ pub(crate) fn parse_code(opcodes: &[u8]) -> Vec { IFLT(goto) => IFLT(code2.get(&goto).unwrap().0), IFLE(goto) => IFLE(code2.get(&goto).unwrap().0), GOTO(goto) => GOTO(code2.get(&goto).unwrap().0), - + IF_ACMPEQ(goto) => GOTO(code2.get(&goto).unwrap().0), + IF_ACMPNE(goto) => GOTO(code2.get(&goto).unwrap().0), //TODO more jump instructions _ => opcode, }) diff --git a/src/classmanager.rs b/src/classmanager.rs index 03c531e..c01a3b7 100644 --- a/src/classmanager.rs +++ b/src/classmanager.rs @@ -138,7 +138,7 @@ impl ClassManager { } pub fn get_class_by_name(&self, name: &str) -> Option<&Class> { - debug!("{}", name); + debug!("get class by name {}", name); let id = self.names.get(name); self.classes.get(id.unwrap()) } @@ -199,6 +199,9 @@ impl ClassManager { .map(|n| *self.names.get(n.as_str()).unwrap()) .collect(); + let mut all_interfaces = Vec::new(); + self.get_all_interfaces(&interface_ids, &mut all_interfaces); + // initial values for static fields (before static init) self.static_class_data .insert(this_classid, Self::set_field_data(&static_field_mapping)); @@ -210,8 +213,9 @@ impl ClassManager { initialized: false, name: name.into(), superclass: superclass_id, - parents, + all_superclasses: parents, interfaces: interface_ids, + all_interfaces, object_field_mapping, static_field_mapping, }, @@ -235,6 +239,16 @@ impl ClassManager { this_classid } + fn get_all_interfaces(&self, ids: &[ClassId], output: &mut Vec) { + for id in ids { + output.push(*id); + let interfaces = self.classes.get(id).map(|c| c.interfaces.to_vec()); + if let Some(intf) = interfaces { + self.get_all_interfaces(&intf, output); + } + } + } + /// like described above fn add_fields_for_this_or_parents( object_field_mapping: &mut HashMap>, @@ -460,8 +474,9 @@ mod test { initialized: false, name: "".into(), superclass: None, - parents: LinkedList::new(), + all_superclasses: LinkedList::new(), interfaces: vec![], + all_interfaces: vec![], object_field_mapping: class_field_mapping, static_field_mapping: HashMap::new(), }, diff --git a/src/value.rs b/src/value.rs index 58289d1..eb08289 100644 --- a/src/value.rs +++ b/src/value.rs @@ -77,10 +77,12 @@ impl Value { } pub fn into_object(self) -> ObjectRef { - if let Value::Ref(v) = self { + if let Value::Null = self { + ObjectRef::Null + } else if let Value::Ref(v) = self { v } else { - panic!(); + panic!("{:?}", self); } } diff --git a/src/vm/object.rs b/src/vm/object.rs index 575b6c4..31a859b 100644 --- a/src/vm/object.rs +++ b/src/vm/object.rs @@ -7,7 +7,7 @@ use std::cell::RefCell; use std::fmt::{Debug, Formatter, Pointer}; use std::rc::Rc; -#[derive(Clone)] +#[derive(Clone, PartialEq)] pub struct Array where T: Clone, @@ -58,7 +58,7 @@ where } } -#[derive(Clone)] +#[derive(Clone, PartialEq)] pub enum ObjectRef { ByteArray(Array), //maybe use arrays? @@ -176,6 +176,12 @@ pub struct Object { pub data: Vec, } +impl PartialEq for Object { + fn eq(&self, other: &Self) -> bool { + self.id == other.id + } +} + // object, not array impl Object { pub fn new(class: &Class) -> Self { diff --git a/src/vm/runtime.rs b/src/vm/runtime.rs index af659f5..ac2ca59 100644 --- a/src/vm/runtime.rs +++ b/src/vm/runtime.rs @@ -15,7 +15,7 @@ use crate::vm::array::{array_load, array_store}; use crate::vm::native::invoke_native; use crate::vm::object; use crate::vm::object::ObjectRef; -use crate::vm::object::ObjectRef::Object; +use crate::vm::object::ObjectRef::{Class, Object}; use crate::vm::opcodes::Opcode; use crate::vm::opcodes::Opcode::*; use std::io::Write; @@ -551,7 +551,7 @@ impl Stackframe { class_manager.load_class_by_name(&name); let class = class_manager.get_class_by_name(&name).unwrap(); - for parent_id in &class.parents { + for parent_id in &class.all_superclasses { let classdef = class_manager.get_classdef(parent_id); if classdef.has_method(method_name) { invoke_class = Some(*parent_id); @@ -712,6 +712,8 @@ impl Stackframe { let object = object.borrow(); let value = object.get(runtime_type, &declared_type, &field_name); self.push(value.clone()); + } else if let ObjectRef::Null = instance { + self.push(Null); } else { unreachable!() } @@ -907,8 +909,65 @@ impl Stackframe { let value1 = self.pop().into_i64(); self.push(I64(value1 ^ value2)); } + CHECKCAST(_) => { //? + } + IF_ACMPEQ(jmp_to) => { + let value2 = self.pop().into_object(); + let value1 = self.pop().into_object(); + if value1 == value2 { + self.pc = *jmp_to as usize; + } + } + IF_ACMPNE(jmp_to) => { + let value2 = self.pop().into_object(); + let value1 = self.pop().into_object(); + if value1 != value2 { + self.pc = *jmp_to as usize; + } + } + INSTANCEOF(class_index_tca) => { + // check object.getClass against class_tca (to check against) + // 1. either class == class_tca + // 2. or class.one_of_its_interfaces == class_tca // recursive up until there is no super + // 3. or class.superclass = class_tca // recursive up until (excluding) object - CHECKCAST(_) => {} + let object = self.pop().into_object(); + let class_name = class_manager + .get_classdef(&class_id) + .cp_class_name(class_index_tca); + let classid_tca = class_manager.get_class_by_name(class_name).map(|c| c.id); + if let Some(classid_tca) = classid_tca { + if let Object(o) = object { + let class_id = &o.borrow().class_id; + let mut outcome = 0; + if let Some(class_to_check) = class_manager.get_class_by_id(class_id) { + // 1. + if class_to_check.id == classid_tca { + outcome = 1; + } else { + // 2. + for intf_id in &class_to_check.all_interfaces { + if *intf_id == classid_tca { + outcome = 1; + break; + } + } + // 3. + for super_id in &class_to_check.all_superclasses { + if *super_id == classid_tca { + outcome = 1; + break; + } + } + } + debug!("{}", outcome); + self.push(I32(outcome)); + } + } else { + self.push(I32(0)); + } + } + } _ => { panic!("opcode not implemented") } diff --git a/tests/method_tests.rs b/tests/method_tests.rs index d725553..f8c65ea 100644 --- a/tests/method_tests.rs +++ b/tests/method_tests.rs @@ -11,6 +11,7 @@ mod test { 0, 0, HashMap::new(), + vec![], ); assert!(m.is(Modifier::Public)); assert!(m.is(Modifier::Static));