diff --git a/src/bytecode.rs b/src/bytecode.rs index c6190c5..759b1b1 100644 --- a/src/bytecode.rs +++ b/src/bytecode.rs @@ -72,6 +72,7 @@ impl Bytecode { 0xB8 => (Instruction::InvokeStatic((self.bytes[offset+1] as u16) << 8 | self.bytes[offset+2] as u16), 3), 0xBA => (Instruction::InvokeDynamic((self.bytes[offset+1] as u16) << 8 | self.bytes[offset+2] as u16, (self.bytes[offset+3] as u16) << 8 | self.bytes[offset+4] as u16), 5), 0xBB => (Instruction::NewObject((self.bytes[offset+1] as u16) << 8 | self.bytes[offset+2] as u16), 3), + 0xBD => (Instruction::NewArray((self.bytes[offset+1] as u16) << 8 | self.bytes[offset+2] as u16), 3), _ => (Instruction::Unknown(opcode), 1) } } @@ -164,6 +165,7 @@ pub enum Instruction { InvokeSpecial(u16) = 0xB7, // invoke instance method InvokeStatic(u16) = 0xB8, // invoke static function InvokeDynamic(u16, u16) = 0xBA, // invoke dynamic function - NewObject(u16) = 0xBB, // Create a new object from a constant-pool reference + NewObject(u16) = 0xBB, // Create a new object from a constant-pool class reference + NewArray(u16) = 0xBD, // Create a new array from a constant-pool component class reference Unknown(u8), } diff --git a/src/classfile.rs b/src/classfile.rs index 94496eb..4057f0e 100644 --- a/src/classfile.rs +++ b/src/classfile.rs @@ -5,7 +5,7 @@ use core::str::Utf8Error; use crate::accessmasks::*; use crate::bytecode::Bytecode; -use crate::constantpool::{ ConstantPoolInfo, ConstantUtf8Info, ConstantMethodRefInfo, ConstantClassInfo, ConstantNameAndTypeInfo, ConstantIntegerInfo }; +use crate::constantpool::{ ConstantFieldRefInfo, ConstantPoolInfo, ConstantUtf8Info, ConstantStringInfo, ConstantMethodRefInfo, ConstantClassInfo, ConstantNameAndTypeInfo, ConstantIntegerInfo, ConstantLongInfo }; #[derive(Debug)] pub enum Error { @@ -217,6 +217,17 @@ impl JavaClassFile { return Ok(methodref_entry); } + pub fn pool_fieldref_entry(&self, index: u16) -> Result<&ConstantFieldRefInfo, Error> { + let pool_entry = self.pool_entry(index)?; + + let fieldref_entry = match pool_entry { + ConstantPoolInfo::FieldRef(data) => data, + _ => unreachable!(), + }; + + return Ok(fieldref_entry); + } + pub fn pool_class_entry(&self, index: u16) -> Result<&ConstantClassInfo, Error> { let pool_entry = self.pool_entry(index)?; @@ -235,6 +246,15 @@ impl JavaClassFile { }; } + pub fn pool_long_entry(&self, index: u16) -> Result<&ConstantLongInfo, Error> { + let pool_entry = self.pool_entry(index)?; + + return match pool_entry { + ConstantPoolInfo::Long(data) => Ok(data), + _ => Err(Error::BadFileError(format!("Expected constant pool entry {} in class {} to be of type Long but found {:?}", index, self.get_classname()?, pool_entry))) + }; + } + pub fn pool_int_entry(&self, index: u16) -> Result<&ConstantIntegerInfo, Error> { let pool_entry = self.pool_entry(index)?; @@ -253,6 +273,47 @@ impl JavaClassFile { }; } + pub fn pool_string_entry(&self, index: u16) -> Result<&ConstantStringInfo, Error> { + let pool_entry = self.pool_entry(index)?; + + return match pool_entry { + ConstantPoolInfo::String(data) => Ok(data), + _ => Err(Error::BadFileError(format!("Expected constant pool entry {} in class {} to be of type String but found {:?}", index, self.get_classname()?, pool_entry))) + }; + } + + pub fn gather_string(&self, index: u16) -> Result<&String, Error> { + let string = self.pool_string_entry(index)?; + + return Ok(&(self.pool_utf8_entry(string.string_index)?.utf8)); + } + + pub fn gather_nameandtype(&self, index: u16) -> Result<(&String, &String), Error> { + let nameandtype = self.pool_nameandtype_entry(index)?; + + let name = &self.pool_utf8_entry(nameandtype.name_index)?.utf8; + let descriptor = &self.pool_utf8_entry(nameandtype.descriptor_index)?.utf8; + + Ok((name, descriptor)) + } + + pub fn gather_fieldref(&self, index: u16) -> Result<(&String, &String, &String), Error> { + let fieldref = self.pool_fieldref_entry(index)?; + + let (field_name, field_descriptor) = self.gather_nameandtype(fieldref.name_and_type_index)?; + let class_name = self.gather_class(fieldref.class_index)?; + + Ok((class_name, field_name, field_descriptor)) + } + + pub fn gather_class(&self, index: u16) -> Result<&String, Error> { + let class_entry = self.pool_class_entry(index)?; + let class_name_entry = self.pool_utf8_entry(class_entry.name_index)?; + let class_name = &class_name_entry.utf8; + + Ok(class_name) + } + pub fn gather_methodref(&self, index: u16) -> Result<(&String, &String, &String), Error> { let methodref = self.pool_methodref_entry(index)?; let class_entry = self.pool_class_entry(methodref.class_index)?; @@ -671,7 +732,7 @@ impl ExceptionAttributeData { } #[repr(u8)] -#[derive(Debug, Eq, PartialEq, Clone)] +#[derive(Debug, Eq, PartialEq, Clone, Hash)] pub enum AbstractTypeKind { Void() = b'V', // void Byte() = b'B', // signed byte @@ -702,7 +763,7 @@ impl Into for &AbstractTypeKind { } } -#[derive(Debug, Eq, PartialEq, Clone)] +#[derive(Debug, Eq, PartialEq, Clone, Hash)] pub struct AbstractTypeDescription { pub array_level: u8, pub kind: AbstractTypeKind, @@ -725,7 +786,7 @@ impl Into for &AbstractTypeDescription { } impl AbstractTypeDescription { - fn parse_first(s: &str) -> Result<(usize, Self), Error> { + pub 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(); @@ -757,7 +818,7 @@ impl AbstractTypeDescription { AbstractTypeKind::Classname(classname_string.to_string()) } - _ => return Err(Error::BadFileError(format!("Invalid Type character: '{}' in string \"{}\"", type_char, s))), + _ => return Err(Error::BadFileError(format!("Invalid Type character: '{}' in string '{}'", type_char, s))), }; return Ok((offset, AbstractTypeDescription { array_level, kind })) @@ -809,13 +870,14 @@ impl TryFrom<&String> for MethodDescriptor { s.get(total_offset..).unwrap() .strip_prefix(")") + // unreachable? .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))) + return Err(Error::BadFileError(format!("Trailing characters in method descriptor string: '{}'", s))) } Ok( @@ -942,11 +1004,11 @@ pub fn read_i32(reader: &mut dyn Read) -> Result { return Ok(i32::from_be_bytes(buffer)); } -pub fn read_u64(reader: &mut dyn Read) -> Result { - let high_bytes: u64 = read_u32(reader)?.into(); - let low_bytes: u64 = read_u32(reader)?.into(); +pub fn read_i64(reader: &mut dyn Read) -> Result { + let mut buffer: [u8; 8] = [0; 8]; + reader.read_exact(&mut buffer)?; - return Ok((high_bytes << 32) | low_bytes); + return Ok(i64::from_be_bytes(buffer)); } pub fn read_u32(reader: &mut dyn Read) -> Result { diff --git a/src/classstore.rs b/src/classstore.rs index 3c67ddd..1cc171f 100644 --- a/src/classstore.rs +++ b/src/classstore.rs @@ -5,14 +5,25 @@ use std::error::Error as ErrorTrait; use std::fs::File; use std::path::PathBuf; -use crate::classfile::JavaClassFile; +use crate::classfile::{ JavaClassFile, AbstractTypeDescription, AbstractTypeKind }; use crate::classfile; +use crate::heap_area::ObjectReference; +use crate::iterators::CompatibleTypesIterator; #[derive(Debug)] pub struct ClassStore { class_ids: HashMap, - classes: Vec<(bool, JavaClassFile)>, // was_init, class_file + array_classes: HashMap, + classes: Vec, class_path_fragments: Vec, + native_class_names: Vec, +} + +#[derive(Debug)] +struct ClassStoreEntry { + was_init: bool, + class_object: ObjectReference, + class_file: JavaClassFile, } #[derive(Debug)] @@ -56,8 +67,10 @@ impl ClassStore { ClassStore { class_ids: HashMap::new(), + array_classes: HashMap::new(), classes: Vec::new(), class_path_fragments: vec![current_dir_path], + native_class_names: Vec::new(), } } @@ -65,19 +78,34 @@ impl ClassStore { return self.classes.len(); } - pub fn add_class(&mut self, class_file: JavaClassFile, was_init: bool) { - return self.classes.push((was_init, class_file)); + pub fn add_class(&mut self, class_file: JavaClassFile, was_init: bool) -> Result { + let classname = class_file.get_classname()?; + self.class_ids.insert(classname.to_string(), self.classes.len()); + let entry = ClassStoreEntry { + was_init, + class_object: ObjectReference::NULL, + class_file + }; + self.classes.push(entry); + + return Ok(self.classes.len() - 1); + } + + pub fn add_native_class_name(&mut self, name: String) -> usize { + self.native_class_names.push(name); + return self.native_class_names.len() - 1; + } + + pub fn get_native_class_name(&self, index: usize) -> &String { + return &self.native_class_names[index]; } pub fn load_class_from_file(&mut self, class_file_path: &PathBuf) -> Result { let mut file_reader = File::open(class_file_path)?; - let classfile = JavaClassFile::new(&mut file_reader)?; + let class_file = JavaClassFile::new(&mut file_reader)?; - let classname = classfile.get_classname()?; - self.class_ids.insert(classname.to_string(), self.classes.len()); - self.classes.push((false, classfile)); - return Ok(self.classes.len() - 1); + return self.add_class(class_file, false); } pub fn load_class(&mut self, classname: &String) -> Result { @@ -96,6 +124,28 @@ impl ClassStore { return Err(Error::ClassNotFoundError(format!("Could not find class '{}' in classpath", classname))); } + pub fn are_types_compatible(&self, my_type: &AbstractTypeDescription, other_type: &AbstractTypeDescription) -> bool { + if my_type == other_type { return true; } + if my_type.array_level != other_type.array_level { return false; } + if my_type.kind == other_type.kind { return true; } + + let my_type_name = match &my_type.kind { + AbstractTypeKind::Classname(name) => name, + _ => unreachable!(), + }; + let my_type_index = self.class_idx_from_name(&my_type_name).unwrap(); + let other_type_name = match &other_type.kind { + AbstractTypeKind::Classname(name) => name, + _ => unreachable!(), + }; + + let compatible_count = CompatibleTypesIterator::new(my_type_index, self) + .filter(|type_name| *type_name == other_type_name) + .count(); + + compatible_count == 0 + } + pub fn have_class(&self, classname: &String) -> bool { return self.class_ids.contains_key(classname); } @@ -104,7 +154,7 @@ impl ClassStore { let class_id = self.class_ids.get(classname); return match class_id { - Some(id) => Ok((&self.classes[*id].1, *id)), + Some(id) => Ok((&self.classes[*id].class_file, *id)), None => Err(Error::ClassNotFoundError(format!("Could not locate class '{}'", classname))), } } @@ -114,13 +164,13 @@ impl ClassStore { return Ok(self.get_class(classname)?); } else { let class_idx = self.load_class(classname)?; - return Ok((&self.classes[class_idx].1, class_idx)); + return Ok((&self.classes[class_idx].class_file, class_idx)); } } pub fn class_file_from_idx(&self, idx: usize) -> Option<&JavaClassFile> { return match self.classes.get(idx) { - Some((_was_init, class_file)) => Some(class_file), + Some(entry) => Some(&entry.class_file), None => None, } } @@ -130,15 +180,29 @@ impl ClassStore { } pub fn was_init(&self, classname: &String) -> Option { - let (was_init, _) = self.classes.get(self.class_idx_from_name(classname).unwrap()).unwrap(); + let entry = self.classes.get(self.class_idx_from_name(classname).unwrap()).unwrap(); - return Some(*was_init); + return Some(entry.was_init); } pub fn set_init(&mut self, class_idx: usize, was_init: bool) { - let pair = self.classes.get_mut(class_idx).unwrap(); - pair.0 = was_init; + let entry = self.classes.get_mut(class_idx).unwrap(); + entry.was_init = was_init; + } + + pub fn put_array_class_ref(&mut self, type_desc: AbstractTypeDescription, class_ref: ObjectReference) { + self.array_classes.insert(type_desc, class_ref); + } + + pub fn set_class_objectref_by_index(&mut self, index: usize, class_objref: ObjectReference) { + self.classes.get_mut(index).unwrap().class_object = class_objref; + } + + pub fn get_class_objectref_from_index(&self, index: usize) -> ObjectReference { + self.classes[index].class_object + } + + pub fn get_array_class_ref(&self, type_desc: &AbstractTypeDescription) -> Option { + return self.array_classes.get(type_desc).copied(); } } - - diff --git a/src/constantpool.rs b/src/constantpool.rs index 88f869b..1e8e66a 100644 --- a/src/constantpool.rs +++ b/src/constantpool.rs @@ -1,7 +1,7 @@ use std::io::Read; use crate::classfile::Error; -use crate::classfile::{read_u16, read_u8, read_f32, read_f64, read_u64, read_i32}; +use crate::classfile::{read_u16, read_u8, read_f32, read_f64, read_i64, read_i32}; #[derive(Debug, Copy, Clone)] pub struct ConstantClassInfo { @@ -43,7 +43,7 @@ pub struct ConstantFloatInfo { #[derive(Debug, Copy, Clone)] pub struct ConstantLongInfo { - pub value: u64, + pub value: i64, } #[derive(Debug, Copy, Clone)] @@ -176,7 +176,7 @@ impl ConstantPoolInfo { 5 => { ConstantPoolInfo::Long( ConstantLongInfo { - value: read_u64(reader)? + value: read_i64(reader)? } ) } diff --git a/src/heap_area.rs b/src/heap_area.rs index e448e9d..5fc7afa 100644 --- a/src/heap_area.rs +++ b/src/heap_area.rs @@ -5,6 +5,7 @@ use core::fmt::Debug as DebugTrait; use crate::accessmasks::FieldAccessFlag; use crate::classfile::{ JavaClassFile, AbstractTypeDescription, MethodInfo, AbstractTypeKind }; use crate::classstore::ClassStore; +use crate::iterators::ClassFieldIterator; use crate::jvm::Error; #[derive(Debug)] @@ -32,66 +33,153 @@ impl HeapArea { return object_ref; } + pub fn make_empty_array(&mut self, class_store: &ClassStore, element_type_desc: AbstractTypeDescription, capacity: usize) -> ObjectReference { + let (array_ref, array_size) = self.object_area.make_empty_array(class_store, element_type_desc, capacity); + self.memory_used += array_size; + + return array_ref; + } + + pub fn make_array(&mut self, class_store: &ClassStore, elements: Box<[ObjectReference]>) -> ObjectReference { + let (array_ref, array_size) = self.object_area.make_array(class_store, elements); + self.memory_used += array_size; + + return array_ref; + } + pub fn make_static(&mut self, class: &JavaClassFile, class_index: usize) { self.memory_used += self.static_area.make(class, class_index); } + + pub fn byte_array_from_rust_string(&mut self, string: &str, class_store: &ClassStore) -> ObjectReference { + let mut byte_obj_vec = Vec::::with_capacity(string.len()); + for byte in string.as_bytes() { + // TODO: Take bytes from ByteCache + + let byte_class_idx = class_store.class_idx_from_name(&"java/lang/Byte".to_string()).unwrap(); + let byte_obj = self.make_object(&class_store, byte_class_idx); + self.object_area.set_object_field(byte_obj, "value", FieldValue::Byte(*byte), byte_class_idx, &class_store).unwrap(); + + byte_obj_vec.push(byte_obj); + } + + let byte_array = self.make_array(&class_store, byte_obj_vec.into_boxed_slice()); + + byte_array + } + } -pub type ObjectReference=u32; +#[derive(Debug, Clone, Copy)] +pub struct ObjectReference(u32); -const DEFAULT_COMPARTMENT_CAPACITY: usize = u16::MAX as usize; + +impl ObjectReference { + pub const NULL: ObjectReference = ObjectReference(0); +} + + +const DEFAULT_COMPARTMENT_CAPACITY: u32 = u16::MAX as u32; const INVALID_FIRST_OBJECT: usize = u16::MAX as usize; const INVALID_NEXT_COMPARTMENT: usize = 0; #[derive(Default, Debug)] pub struct ObjectArea { + memory_used: usize, compartments: Vec, first_free_compartment: usize, } impl ObjectArea { - pub fn make(&mut self, class_store: &ClassStore, target_class_index: usize) -> (ObjectReference, usize) { - let mut fields = Vec::new(); - let mut current_class_index; - let mut next_class_index = target_class_index; + fn make_empty_array(&mut self, class_store: &ClassStore, element_type_desc: AbstractTypeDescription, capacity: usize) -> (ObjectReference, usize) { + // + // make new type desc + let array_type_desc = AbstractTypeDescription { + array_level: 1 + element_type_desc.array_level, + kind: element_type_desc.kind, + }; - let mut object_size = 0; + let array_class_ref = class_store.get_array_class_ref(&array_type_desc).unwrap(); - while next_class_index != class_store.class_count() { - current_class_index = next_class_index; + let array_object = HeapArray { + class_ref: array_class_ref, + content: vec![ObjectReference::NULL; capacity].into_boxed_slice(), + }; - let class = class_store.class_file_from_idx(current_class_index).unwrap(); + let array_size = std::mem::size_of::() + std::mem::size_of::() * array_object.content.len(); - for field in &class.fields { - let new_field = ObjectField { - value: FieldValue::default_for(&field.descriptor) - }; - object_size += std::mem::size_of_val(&new_field); + let array_object_ref = self.store_entry(CompartmentEntry::Array(array_object)); - fields.push(new_field); - } + self.memory_used += array_size; - if class.has_super_class() { - next_class_index = class_store.class_idx_from_name(class.get_super_class_name().unwrap()).unwrap(); - } else { - next_class_index = class_store.class_count(); - } + (array_object_ref, array_size) + } + + fn make_array(&mut self, class_store: &ClassStore, elements: Box<[ObjectReference]>) -> (ObjectReference, usize) { + assert!(elements.len() != 0); + let array_element_class_name = self.get_reference_native_class_name(elements[0], class_store); + let array_element_type_desc = AbstractTypeDescription::parse_first(array_element_class_name).unwrap().1; + + let (array_object_ref, array_size) = self.make_empty_array(class_store, array_element_type_desc, elements.len()); + + for (index, element) in elements.iter().enumerate() { + self.set_array_element(array_object_ref, index, *element); } + return (array_object_ref, array_size); + } + + pub fn make(&mut self, class_store: &ClassStore, target_class_index: usize) -> (ObjectReference, usize) { + let fields: Vec<_> = ClassFieldIterator::new(target_class_index, class_store) + .filter(|f| ! (f.access_flags & FieldAccessFlag::Static)) + .map(|f| ObjectField { value: FieldValue::default_for(&f.descriptor) }) + .collect(); + + let object_size = std::mem::size_of::() + std::mem::size_of::() * fields.len(); + let new_object = HeapObject { class_index: target_class_index, fields: fields.into_boxed_slice(), }; - object_size += std::mem::size_of_val(&new_object); - object_size += std::mem::size_of_val(&new_object.fields); - let object_ref = self.store_object(new_object); + let object_ref = self.store_entry(CompartmentEntry::Object(new_object)); + + self.memory_used += object_size; return (object_ref, object_size); } - fn store_object(&mut self, object: HeapObject) -> ObjectReference { + pub fn get_reference_native_class_name<'a>(&self, reference: ObjectReference, class_store: &'a ClassStore) -> &'a String { + let class_ref = self.get_reference_class_ref(reference, class_store); + let class_data_ref = match self.get_object_field(class_ref, "classData", self.get_object_class_index(class_ref), class_store).unwrap() { + FieldValue::Reference(r) => r, + _ => unreachable!(), + }; + let native_name_index = match self.get_object_field(class_data_ref, "native_class_descriptor_index", self.get_object_class_index(class_data_ref), class_store).unwrap() { + FieldValue::Int(i) => i, + _ => unreachable!(), + }; + + return class_store.get_native_class_name(native_name_index as usize); + } + + fn get_object_class_index(&self, reference: ObjectReference) -> usize { + match self.get_object(reference) { + CompartmentEntry::Object(o) => o.class_index, + _ => unreachable!(), + } + } + + fn get_reference_class_ref(&self, reference: ObjectReference, class_store: &ClassStore) -> ObjectReference { + match self.get_object(reference) { + CompartmentEntry::Object(o) => class_store.get_class_objectref_from_index(o.class_index), + CompartmentEntry::Array(a) => a.class_ref, + _ => unreachable!(), + } + } + + fn store_entry(&mut self, object: CompartmentEntry) -> ObjectReference { if self.first_free_compartment == 0 { self.compartments.push(ObjectCompartment::new(INVALID_NEXT_COMPARTMENT)); self.first_free_compartment = self.compartments.len(); @@ -103,12 +191,116 @@ impl ObjectArea { self.first_free_compartment = self.compartments[compartment_index].next_free_compartment; } - return object_index + (DEFAULT_COMPARTMENT_CAPACITY as u32 * (compartment_index+1) as u32); + return ObjectReference(object_index + (DEFAULT_COMPARTMENT_CAPACITY * (compartment_index as u32 + 1))); + } + + fn get_object(&self, reference: ObjectReference) -> &CompartmentEntry { + let index = reference.0; + let compartment_index: u32 = (index / DEFAULT_COMPARTMENT_CAPACITY) - 1; + let object_index: u32 = index % DEFAULT_COMPARTMENT_CAPACITY; + + let compartment = self.compartments.get(compartment_index as usize).unwrap(); + let object = compartment.objects.get(object_index as usize).unwrap(); + + return object; + } + + fn get_object_mut(&mut self, reference: ObjectReference) -> &mut CompartmentEntry { + let index = reference.0; + let compartment_index: u32 = (index / DEFAULT_COMPARTMENT_CAPACITY) - 1; + let object_index: u32 = index % DEFAULT_COMPARTMENT_CAPACITY; + + let compartment = self.compartments.get_mut(compartment_index as usize).unwrap(); + let object = compartment.objects.get_mut(object_index as usize).unwrap(); + + return object; + } + + pub fn get_object_field(&self, reference: ObjectReference, field_name: &str, accessing_class_idx: usize, class_store: &ClassStore) -> Result { + // TODO: Check access rights + let object = match self.get_object(reference) { + CompartmentEntry::Object(o) => o, + _ => unreachable!(), + }; + + let field_option = ClassFieldIterator::new(object.class_index, class_store) + .filter(|f| ! (f.access_flags & FieldAccessFlag::Static)) + .filter(|f| f.name == field_name) + .enumerate() + .next(); + + match field_option { + Some((field_index, _)) => { + Ok(object.fields[field_index].value) + }, + None => Err( + Error::RunTimeError( + format!( + "Trying to get field '{}' on instance of '{}' but there is no such field", + field_name, + class_store.class_file_from_idx(object.class_index).unwrap().get_classname().unwrap() + ) + ) + ) + } + } + + pub fn set_array_element(&mut self, array_reference: ObjectReference, index: usize, element: ObjectReference) { + let array = match self.get_object_mut(array_reference) { + CompartmentEntry::Array(a) => a, + _ => unreachable!(), + }; + + let array_element = array.content.get_mut(index).unwrap(); + + *array_element = element; + } + + pub fn set_object_field(&mut self, reference: ObjectReference, field_name: &str, value: FieldValue, accessing_class_idx: usize, class_store: &ClassStore) -> Result<(), Error> { + // TODO: Check access rights + let object = match self.get_object_mut(reference) { + CompartmentEntry::Object(o) => o, + _ => unreachable!(), + }; + + let field_option = ClassFieldIterator::new(object.class_index, class_store) + .filter(|f| ! (f.access_flags & FieldAccessFlag::Static)) + .filter(|f| f.name == field_name) + .enumerate() + .next(); + + match field_option { + Some((field_index, field_info)) => { + let object_field = object.fields.get_mut(field_index).unwrap(); + if std::mem::discriminant(&value) == std::mem::discriminant(&object_field.value) { + // TODO: Check reference types + object_field.value = value; + + Ok(()) + } else { + Err( + Error::RunTimeError( + format!( + "Trying set value '{:?}' on a field with descriptor '{:?}' in instance of '{}'", + value, + field_info.descriptor, + class_store.class_file_from_idx(object.class_index).unwrap().get_classname().unwrap() + ) + ) + ) + } + }, + None => Err(Error::RunTimeError(format!( + "Trying to set field '{}' on instance of class '{}' but there is no such field", + field_name, + class_store.class_file_from_idx(object.class_index).unwrap().get_classname().unwrap() + ))) + } } } pub struct ObjectCompartment { - objects: Box<[ObjectCompartmentEntry]>, + objects: Box<[CompartmentEntry]>, first_free: usize, reserved_count: usize, next_free_compartment: usize, @@ -116,11 +308,11 @@ pub struct ObjectCompartment { impl ObjectCompartment { fn new(next_free: usize) -> Self { - let mut os = Vec::with_capacity(DEFAULT_COMPARTMENT_CAPACITY); + let mut os = Vec::with_capacity(DEFAULT_COMPARTMENT_CAPACITY as usize); for i in 0..DEFAULT_COMPARTMENT_CAPACITY-1 { - os.push(ObjectCompartmentEntry::EmptyNext(i+1)); + os.push(CompartmentEntry::EmptyNext(i as usize + 1)); } - os.push(ObjectCompartmentEntry::EmptyTail()); + os.push(CompartmentEntry::EmptyTail()); ObjectCompartment { objects: os.into_boxed_slice(), @@ -130,22 +322,24 @@ impl ObjectCompartment { } } - fn store(&mut self, object: HeapObject) -> ObjectReference { + fn store(&mut self, object: CompartmentEntry) -> u32 { let store_slot = self.objects.get(self.first_free).unwrap(); let compartment_index = self.first_free; match store_slot { - ObjectCompartmentEntry::Valid(_) => unreachable!(), - ObjectCompartmentEntry::EmptyNext(next) => { + CompartmentEntry::EmptyNext(next) => { self.first_free = *next; }, - ObjectCompartmentEntry::EmptyTail() => { + CompartmentEntry::EmptyTail() => { // TODO: Maybe change to something else self.first_free = INVALID_FIRST_OBJECT; }, + + CompartmentEntry::Object(_) => unreachable!(), + CompartmentEntry::Array(_) => unreachable!(), } - *self.objects.get_mut(compartment_index).unwrap() = ObjectCompartmentEntry::Valid(object); + *self.objects.get_mut(compartment_index).unwrap() = object; self.reserved_count += 1; return compartment_index as u32; @@ -161,18 +355,24 @@ impl DebugTrait for ObjectCompartment { .field("objects", &self.objects .iter() .enumerate() - .filter(|e| match e { (_, ObjectCompartmentEntry::Valid(_)) => true, _ => false}) - .map(|e| match e { (i, ObjectCompartmentEntry::Valid(o)) => (i, o), _ => unreachable!()}) - .collect::>() + .filter(|e| match e { (_, CompartmentEntry::EmptyNext(_)) | (_, CompartmentEntry::EmptyTail()) => false, _ => true}) + .collect::>() ).finish() } } #[derive(Debug)] -pub enum ObjectCompartmentEntry { - Valid(HeapObject), +pub enum CompartmentEntry { + Object(HeapObject), + Array(HeapArray), EmptyNext(usize), - EmptyTail(), // next empty value + EmptyTail(), // last empty value +} + +#[derive(Debug)] +pub struct HeapArray { + class_ref: ObjectReference, + content: Box<[ObjectReference]>, } #[derive(Debug)] @@ -183,12 +383,14 @@ pub struct HeapObject { #[derive(Default, Debug)] pub struct StaticArea { + memory_used: usize, static_objects: HashMap, } impl StaticArea { pub fn set(&mut self, class_name: &String, field_name: &String, field_value: FieldValue) -> Result<(), Error> { + // TODO: Access permission checking let static_object = match self.static_objects.get_mut(class_name) { Some(o) => o, None => return Err(Error::RunTimeError(format!("Trying to set '{}.{}={:?}' but there is no such static object.", class_name, field_name, field_value))), @@ -234,7 +436,11 @@ impl StaticArea { let _ = self.static_objects.insert(class.get_classname().unwrap().to_string(), new_object); - return object_memory_size + field_array_size + fields_cumulative_size + method_array_size; + let total_object_size = object_memory_size + field_array_size + fields_cumulative_size + method_array_size; + + self.memory_used += total_object_size; + + return total_object_size; } } @@ -268,7 +474,7 @@ pub struct ObjectField { pub value: FieldValue, } -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] pub enum FieldValue { Boolean(bool), Byte(u8), @@ -294,7 +500,7 @@ impl FieldValue { fn default_for(t: &AbstractTypeDescription) -> Self { if t.array_level != 0 { - return Self::Reference(0); + return Self::Reference(ObjectReference::NULL); } match &t.kind { AbstractTypeKind::Void() => unreachable!(), @@ -304,7 +510,7 @@ impl FieldValue { AbstractTypeKind::Float() => Self::Float(0.0), AbstractTypeKind::Int() => Self::Int(0), AbstractTypeKind::Long() => Self::Long(0), - AbstractTypeKind::Classname(_) => Self::Reference(0), + AbstractTypeKind::Classname(_) => Self::Reference(ObjectReference::NULL), AbstractTypeKind::Short() => Self::Short(0), AbstractTypeKind::Boolean() => Self::Boolean(true), } diff --git a/src/iterators.rs b/src/iterators.rs new file mode 100644 index 0000000..8f958db --- /dev/null +++ b/src/iterators.rs @@ -0,0 +1,130 @@ +use crate::classstore::ClassStore; +use crate::classfile::{ FieldInfo, MethodInfo }; + +// Iterator over all the fields of a class object including superclass fields +pub struct ClassFieldIterator<'i> { + field_index: usize, + class_index: usize, + class_store: &'i ClassStore, +} + +impl <'i>ClassFieldIterator<'i> { + pub fn new(class_index: usize, class_store: &'i ClassStore) -> Self { + ClassFieldIterator { + field_index: 0, + class_index, + class_store, + } + } +} + +impl <'i> Iterator for ClassFieldIterator<'i>{ + type Item = &'i FieldInfo; + + fn next(&mut self) -> Option { + let mut class_file = self.class_store.class_file_from_idx(self.class_index).unwrap(); + + while class_file.has_super_class() && class_file.fields.len() == self.field_index { + let super_class_name = class_file.get_super_class_name().unwrap(); + let (super_class_file, super_class_index) = self.class_store.get_class(super_class_name).unwrap(); + + class_file = super_class_file; + self.class_index = super_class_index; + self.field_index = 0; + } + + if class_file.fields.len() == self.field_index { + return None; + } else { + let field_info = &class_file.fields[self.field_index]; + self.field_index += 1; + return Some(field_info); + } + } +} + +pub struct CompatibleTypesIterator<'i> { + class_stack: Vec<(usize, usize)>, // class_index, interface_index + class_store: &'i ClassStore, +} + +impl <'i>CompatibleTypesIterator<'i> { + pub fn new(class_index: usize, class_store: &'i ClassStore) -> Self { + CompatibleTypesIterator { + class_stack: vec![(class_index, 0)], + class_store, + } + } +} + +impl <'i> Iterator for CompatibleTypesIterator<'i>{ + type Item = &'i String; // class index, method index, method info + + fn next(&mut self) -> Option { + let (class_index, interface_index) = match self.class_stack.pop() { + None => return None, + Some(a) => a, + }; + let class_file = self.class_store.class_file_from_idx(class_index).unwrap(); + + if class_file.interfaces.len() == interface_index { + if class_file.has_super_class() { + let super_class_index = self.class_store.class_idx_from_name(class_file.get_super_class_name().unwrap()).unwrap(); + self.class_stack.push((super_class_index, 0)); + + Some(class_file.get_super_class_name().unwrap()) + } else { + None + } + } else { + let interface_name = class_file.gather_class(class_file.interfaces[interface_index]).unwrap(); + let interface_index = self.class_store.class_idx_from_name(interface_name).unwrap(); + + self.class_stack.push((class_index, interface_index + 1)); + self.class_stack.push((interface_index, 0)); + + Some(interface_name) + } + } +} + +pub struct ClassMethodIterator<'i> { + method_index: usize, + class_index: usize, + class_store: &'i ClassStore, +} + +impl <'i>ClassMethodIterator<'i> { + pub fn new(class_index: usize, class_store: &'i ClassStore) -> Self { + ClassMethodIterator { + method_index: 0, + class_index, + class_store, + } + } +} + +impl <'i> Iterator for ClassMethodIterator<'i>{ + type Item = (usize, usize, &'i MethodInfo); // class index, method index, method info + + fn next(&mut self) -> Option { + let mut class_file = self.class_store.class_file_from_idx(self.class_index).unwrap(); + + while class_file.has_super_class() && class_file.methods.len() == self.method_index { + let super_class_name = class_file.get_super_class_name().unwrap(); + let (super_class_file, super_class_index) = self.class_store.get_class(super_class_name).unwrap(); + + class_file = super_class_file; + self.class_index = super_class_index; + self.method_index = 0; + } + + if class_file.methods.len() == self.method_index { + return None; + } else { + let method_info = &class_file.methods[self.method_index]; + self.method_index += 1; + return Some((self.class_index, self.method_index - 1, method_info)); + } + } +} diff --git a/src/jvm.rs b/src/jvm.rs index 052d5e2..b3d5e95 100644 --- a/src/jvm.rs +++ b/src/jvm.rs @@ -3,14 +3,15 @@ use core::fmt::{Display, Formatter}; use std::collections::VecDeque; use std::error::Error as ErrorTrait; -use crate::accessmasks::{ ClassAccessFlagMask, ClassAccessFlag, MethodAccessFlagMask, MethodAccessFlag, FieldAccessFlag }; +use crate::accessmasks::{ ClassAccessFlagMask, ClassAccessFlag, MethodAccessFlagMask, MethodAccessFlag, FieldAccessFlag, FieldAccessFlagMask }; use crate::bytecode::{ Bytecode, Instruction }; use crate::classfile; -use crate::classfile::{ JavaClassFile, MethodInfo, MethodDescriptor, AbstractTypeDescription, AbstractTypeKind, AttributeInfo, AttributeData, CodeAttributeData, ConstantValueAttributeData }; +use crate::classfile::{ JavaClassFile, FieldInfo, MethodInfo, MethodDescriptor, AbstractTypeDescription, AbstractTypeKind, AttributeInfo, AttributeData, CodeAttributeData, ConstantValueAttributeData }; use crate::classstore; use crate::classstore::ClassStore; use crate::constantpool::{ ConstantPoolInfo, ConstantClassInfo, ConstantUtf8Info, ConstantMethodRefInfo, ConstantNameAndTypeInfo}; use crate::heap_area::{ HeapArea, FieldValue, ObjectReference }; +use crate::iterators::{ ClassMethodIterator, ClassFieldIterator, CompatibleTypesIterator }; use crate::stackframe; use crate::stackframe::{ StackFrame, StackValue, OperandStack }; @@ -63,7 +64,124 @@ impl JVM { } } - pub fn entrypoint(&mut self, class_name: &String, method_name: &String, arguments: &[StackValue]) -> Result<(), Error> { + fn class_native_class_data() -> JavaClassFile { + JavaClassFile { + minor_version: 0, + major_version: 0, + constant_pool: Box::new([ + ConstantPoolInfo::Class(ConstantClassInfo { name_index: 2 }), + ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "::NativeClassData".to_string() }), + ConstantPoolInfo::Class(ConstantClassInfo { name_index: 4 }), + ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "java/lang/Object".to_string() }), + ] + ), + access_flags: ClassAccessFlagMask { mask: ClassAccessFlag::Super.discriminant() }, + this_class: 1, + super_class: 3, + interfaces: Box::new([]), + fields: Box::new([ + FieldInfo { + access_flags: FieldAccessFlagMask { + mask: FieldAccessFlag::Public.discriminant() + }, + name: "native_class_descriptor_index".to_string(), + descriptor: AbstractTypeDescription { + array_level: 0, + kind: AbstractTypeKind::Int(), + }, + attributes: Box::new([]), + }, + ]), + methods: Box::new([]), + attributes: Box::new([]), + } + } + + fn make_class_class(&mut self, class_name: &str) { + let class_object = self.heap_area.make_object(&self.class_store, self.class_store.class_idx_from_name(&"java/lang/Class".to_string()).unwrap()); + let class_data_object = self.heap_area.make_object(&self.class_store, self.class_store.class_idx_from_name(&"::NativeClassData".to_string()).unwrap()); + + self.heap_area.object_area.set_object_field( + class_object, + "classData", + FieldValue::Reference(class_data_object), + self.class_store.class_idx_from_name(&"java/lang/Class".to_string()).unwrap(), + &self.class_store, + ).unwrap(); + // set native name index on class data + self.heap_area.object_area.set_object_field( + class_data_object, + "native_class_descriptor_index", + FieldValue::Int(self.class_store.add_native_class_name(class_name.to_string()) as i32), + self.class_store.class_idx_from_name(&"::NativeClassData".to_string()).unwrap(), + &self.class_store, + ).unwrap(); + + let parsed_class_name = match AbstractTypeDescription::parse_first(class_name).unwrap() { + (_, desc) => { + assert!(desc.array_level == 0); + match desc.kind { + AbstractTypeKind::Classname(name) => name, + _ => unreachable!(), + } + } + }; + self.class_store.set_class_objectref_by_index(self.class_store.class_idx_from_name(&parsed_class_name).unwrap(), class_object); + } + + fn make_array_class(&mut self, element_descriptor_string: &str) { + let (chars_consumed, element_descriptor) = AbstractTypeDescription::parse_first(element_descriptor_string).unwrap(); + assert!(chars_consumed == element_descriptor_string.len()); + let element_class_ref = if element_descriptor.array_level == 0 { + match element_descriptor.kind { + AbstractTypeKind::Classname(ref name) => { + self.class_store.get_class_objectref_from_index( + self.class_store.class_idx_from_name(&name).unwrap() + ) + } + _ => unreachable!(), + } + } else { + self.class_store.get_array_class_ref(&element_descriptor).unwrap() + }; + + let array_class_object = self.heap_area.make_object(&self.class_store, 6); + let array_class_data_object = self.heap_area.make_object(&self.class_store, 1); + let array_type_description = AbstractTypeDescription { + array_level: 1 + element_descriptor.array_level, + kind: element_descriptor.kind, + }; + // set component type + self.heap_area.object_area.set_object_field( + array_class_object, + "componentType", + FieldValue::Reference(element_class_ref), + self.class_store.class_idx_from_name(&"java/lang/Class".to_string()).unwrap(), + &self.class_store, + ).unwrap(); + // set classdata object + self.heap_area.object_area.set_object_field( + array_class_object, + "classData", + FieldValue::Reference(array_class_data_object), + self.class_store.class_idx_from_name(&"java/lang/Class".to_string()).unwrap(), + &self.class_store, + ).unwrap(); + // set native name index on class data + self.heap_area.object_area.set_object_field( + array_class_data_object, + "native_class_descriptor_index", + FieldValue::Int(self.class_store.add_native_class_name((&array_type_description).into()) as i32), + self.class_store.class_idx_from_name(&"::NativeClassData".to_string()).unwrap(), + &self.class_store, + ).unwrap(); + self.class_store.put_array_class_ref( + array_type_description, + array_class_object, + ); + } + + pub fn entrypoint(&mut self, class_name: &String, method_name: &String, arguments: &[&str]) -> Result<(), Error> { let entry_class = JavaClassFile { minor_version: 0, major_version: 0, @@ -72,11 +190,17 @@ impl JVM { ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "::EntryPoint".to_string() }), ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "Code".to_string() }), ConstantPoolInfo::MethodRef(ConstantMethodRefInfo { class_index: 5, name_and_type_index: 6}), - ConstantPoolInfo::Class(ConstantClassInfo { name_index: 7 }), + ConstantPoolInfo::Class(ConstantClassInfo { name_index: 7 }), // 5 ConstantPoolInfo::NameAndType(ConstantNameAndTypeInfo { name_index: 8, descriptor_index: 9 }), ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: class_name.to_string() }), ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: method_name.to_string() }), ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "()V".to_string() }), + ConstantPoolInfo::MethodRef(ConstantMethodRefInfo { class_index: 11, name_and_type_index: 13}), // 10 + ConstantPoolInfo::Class(ConstantClassInfo { name_index: 12 } ), + ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "java/lang/String".to_string() }), + ConstantPoolInfo::NameAndType(ConstantNameAndTypeInfo { name_index: 14, descriptor_index: 15 }), + ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "".to_string() }), + ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "([B)V".to_string() }), // 15 ] ), access_flags: ClassAccessFlagMask { mask: ClassAccessFlag::Super.discriminant() }, @@ -104,7 +228,7 @@ impl JVM { data: AttributeData::Code( CodeAttributeData { max_stack: 0, - max_locals: 0, + max_locals: 1, code: Bytecode { bytes: Box::new([ 0xB8_u8.to_be(), // invokestatic @@ -118,26 +242,92 @@ impl JVM { ) } ]) - } + }, + MethodInfo { + access_flags: MethodAccessFlagMask { + mask: MethodAccessFlag::Public.discriminant() | MethodAccessFlag::Static.discriminant() + }, + name: "make_arg_string".to_string(), + descriptor: MethodDescriptor { + argument_types: Box::new([]), + return_type: AbstractTypeDescription { + array_level: 0, + kind: AbstractTypeKind::Void(), + } + }, + code_attribute_index: 0, + attributes: Box::new([ + AttributeInfo { + attribute_name_index: 3, + data: AttributeData::Code( + CodeAttributeData { + max_stack: 3, + max_locals: 3, + code: Bytecode { + bytes: Box::new([ + 0xBB_u8.to_be(), // new + 11_u16.to_be_bytes()[0], // index 11 into the constant + 11_u16.to_be_bytes()[1], // pool + + 0x59_u8.to_be(), // dup + + 0x2A_u8.to_be(), // aload 0 + + 0xB7_u8.to_be(), // invokespecial + 10_u16.to_be_bytes()[0], // index 10 into the constant + 10_u16.to_be_bytes()[1], // pool + ]) + }, + exception_table: Box::new([]), + attributes: Box::new([]), + } + ) + } + ]) + } ]), attributes: Box::new([]), }; + let entry_frame = StackFrame::new(&entry_class, 0, 0, &[]); + self.class_store.add_class(entry_class, true)?; // 0 + self.class_store.add_class(JVM::class_native_class_data(), true)?; // 1 + self.class_store.load_class(&"java/lang/Object".to_string())?; // 2 + self.class_store.load_class(&"java/lang/Number".to_string())?; // 3 + self.class_store.load_class(&"java/lang/Byte".to_string())?; // 4 + self.class_store.load_class(&"java/lang/String".to_string())?; // 5 + self.class_store.load_class(&"java/lang/Class".to_string())?; // 6 - self.stack_frames.push( - StackFrame::new(&entry_class, 0, 0, arguments), - ); + self.make_class_class("Ljava/lang/Byte;"); + self.make_class_class("Ljava/lang/String;"); + self.make_array_class("Ljava/lang/Byte;"); + self.make_array_class("Ljava/lang/String;"); - self.class_store.add_class(entry_class, true); - self.class_store.load_class(&"java/lang/Class".to_string())?; - self.class_store.load_class(&"java/lang/Object".to_string())?; + let argument_array = self.heap_area.make_empty_array(&self.class_store, AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Classname("java/lang/String".into()) }, arguments.len()); + for (arg_index, argument) in arguments.iter().enumerate() { + let byte_array = self.heap_area.byte_array_from_rust_string(argument, &self.class_store); + self.stack_frames.push( + StackFrame::new( + self.class_store.get_class(&"::EntryPoint".to_string()).unwrap().0, + self.class_store.class_idx_from_name(&"::EntryPoint".to_string()).unwrap(), + 1, // method index + &[StackValue::Reference(byte_array), StackValue::Reference(argument_array), StackValue::Int(arg_index as i32)], + ) + ); - self.new_object(&"java/lang/Class".to_string())?; + self.run()?; + } + + + // push the entry frame which will call main + self.stack_frames.push(entry_frame); Ok(()) } pub fn run(&mut self) -> Result<(), Error> { while self.stack_frames.len() != 0 { + println!("Enter bytecode loop:"); + let jvm_op = self.bytecode_loop()?; match jvm_op { JVMCallbackOperation::PopFrame() => { @@ -167,6 +357,10 @@ impl JVM { JVMCallbackOperation::InitClass(name) => { // TODO: throw exception self.init_class_hierarchy(&name)?; + }, + + JVMCallbackOperation::MakeArrayClass(component_descriptor_string) => { + self.make_array_class(&component_descriptor_string); } } } @@ -174,39 +368,25 @@ impl JVM { Ok(()) } - fn new_object(&mut self, class_name: &String) -> Result { - let (_, class_idx) = self.class_store.get_class(class_name)?; - Ok(self.heap_area.make_object(&mut self.class_store, class_idx)) - } - fn load_class_hierarchy(&mut self, name: &String) -> Result<(), Error> { - self.class_store.load_class(&name)?; - let super_class_name = { - let (file, _) = self.class_store.get_class(&name)?; - file.get_super_class_name()?.clone() - }; - let mut super_classes = vec![super_class_name]; + let mut waiting_queue = VecDeque::new(); + waiting_queue.push_back(name.clone()); - while super_classes.len() != 0 { - let current_super = super_classes.pop().unwrap(); - let have_super_super = { - let (super_file, _) = self.class_store.get_class(¤t_super)?; - super_file.has_super_class() - }; + while waiting_queue.len() != 0 { + let class_name = waiting_queue.pop_front().unwrap(); - if have_super_super { - let super_super_name = { - let (super_file, _) = self.class_store.get_class(¤t_super)?; - super_file.get_super_class_name()? - }; - if self.class_store.have_class(super_super_name) { - self.class_store.load_class(¤t_super)?; - } else { - super_classes.push(current_super); - super_classes.push(super_super_name.to_string()); + if ! self.class_store.have_class(&class_name) { + println!("Loading Class {class_name}"); + self.class_store.load_class(&class_name)?; + let (file, _) = self.class_store.get_class(&class_name).unwrap(); + if file.has_super_class() { + waiting_queue.push_back(file.get_super_class_name()?.to_string()); + } + + for interface_index in &file.interfaces { + let interface_name = file.gather_class(*interface_index)?; + waiting_queue.push_back(interface_name.to_string()); } - } else { - self.class_store.load_class(¤t_super)?; } } @@ -245,53 +425,125 @@ impl JVM { } fn init_class(&mut self, class_idx: usize) -> Result<(), Error> { - let class_file = self.class_store.class_file_from_idx(class_idx).unwrap(); - let clinit_idx = class_file.find_method_index(&"".to_string()); + { + let class_file = self.class_store.class_file_from_idx(class_idx).unwrap(); - self.heap_area.make_static(class_file, class_idx); + self.heap_area.make_static(class_file, class_idx); - for field in &class_file.fields { - if field.access_flags & FieldAccessFlag::Static { - let cvalue_attrs: Vec<&ConstantValueAttributeData> = (&field.attributes).iter() - .filter(|a| match a.data { AttributeData::ConstantValue(_) => true, _ => false }) - .map(|a| match &a.data { AttributeData::ConstantValue(c) => c, _ => unreachable!() }) - .collect(); + for field in &class_file.fields { + if field.access_flags & FieldAccessFlag::Static { + let cvalue_attrs: Vec<&ConstantValueAttributeData> = (&field.attributes).iter() + .filter(|a| match a.data { AttributeData::ConstantValue(_) => true, _ => false }) + .map(|a| match &a.data { AttributeData::ConstantValue(c) => c, _ => unreachable!() }) + .collect(); - assert!(cvalue_attrs.len() < 2); - // TODO: Throw error + assert!(cvalue_attrs.len() < 2); + // TODO: Throw error - if cvalue_attrs.len() == 1 { - let constant_value_info = cvalue_attrs[0]; + if cvalue_attrs.len() == 1 { + let constant_value_info = cvalue_attrs[0]; - assert!(field.descriptor.array_level == 0); - // TODO: Throw Error + assert!(field.descriptor.array_level == 0); + // TODO: Throw Error - let field_value = match field.descriptor.kind { - AbstractTypeKind::Boolean() => { - let int_entry = class_file.pool_int_entry(constant_value_info.constant_value_index)?; + let field_value = match field.descriptor.kind { + AbstractTypeKind::Boolean() => { + let int_entry = class_file.pool_int_entry(constant_value_info.constant_value_index)?; - FieldValue::Boolean(int_entry.value != 0) - }, - AbstractTypeKind::Int() => { - let int_entry = class_file.pool_int_entry(constant_value_info.constant_value_index)?; + FieldValue::Boolean(int_entry.value != 0) + }, + AbstractTypeKind::Byte() => { + let int_entry = class_file.pool_int_entry(constant_value_info.constant_value_index)?; - FieldValue::Int(int_entry.value) - }, - AbstractTypeKind::Short() => { - let int_entry = class_file.pool_int_entry(constant_value_info.constant_value_index)?; + FieldValue::Byte(int_entry.value as u8) + }, + AbstractTypeKind::Char() => { + let int_entry = class_file.pool_int_entry(constant_value_info.constant_value_index)?; - FieldValue::Short(int_entry.value as i16) - }, - _ => todo!() - }; + FieldValue::Char(int_entry.value as u16) + }, + AbstractTypeKind::Int() => { + let int_entry = class_file.pool_int_entry(constant_value_info.constant_value_index)?; - self.heap_area.static_area.set(class_file.get_classname()?, &field.name, field_value)?; + FieldValue::Int(int_entry.value) + }, + AbstractTypeKind::Short() => { + let int_entry = class_file.pool_int_entry(constant_value_info.constant_value_index)?; + + FieldValue::Short(int_entry.value as i16) + }, + AbstractTypeKind::Long() => { + let long_entry = class_file.pool_long_entry(constant_value_info.constant_value_index)?; + + FieldValue::Long(long_entry.value) + }, + AbstractTypeKind::Classname(ref name) => { + if name == "java/lang/String" { + let string_entry = class_file.gather_string(constant_value_info.constant_value_index)?; + + let (string_class_file, string_class_index) = self.class_store.get_class(name).unwrap(); + let string_object = self.heap_area.make_object(&self.class_store, string_class_index); + // TODO: Review this if it looks like there will be stack + // overflows in initialization + + let string_init_function_index = match ClassMethodIterator::new(string_class_index, &self.class_store) + .filter(|( cid, _mid, _minfo)| *cid == string_class_index) + .filter(|(_cid, _mid, minfo)| minfo.name == "") + .filter(|(_cid, _mid, minfo)| minfo.descriptor.argument_types.len() == 1) + .filter(|(_cid, _mid, minfo)| minfo.descriptor.argument_types[0] == AbstractTypeDescription { array_level: 1, kind: AbstractTypeKind::Byte() }) + .next() { + Some((_, method_index, _)) => method_index, + None => unreachable!(), + }; + + let byte_array = self.heap_area.byte_array_from_rust_string(string_entry, &self.class_store); + + self.stack_frames.push( + StackFrame::new( + string_class_file, + string_class_index, + string_init_function_index as u16, + &[StackValue::Reference(byte_array)], + ) + ); + + FieldValue::Reference(string_object) + } else { + todo!() + } + }, + + _ => { + println!("{:?}", field.descriptor.kind); + todo!() + }, + }; + + self.heap_area.static_area.set(class_file.get_classname()?, &field.name, field_value)?; + } } } } - // TODO: Push clinit function + // class object + self.make_class_class(&("L".to_owned() + &self.class_store.class_file_from_idx(class_idx).unwrap().get_classname()?.clone() + ";")); + // Push clinit function + let class_file = self.class_store.class_file_from_idx(class_idx).unwrap(); + let clinit_idx = class_file.find_method_index(&"".to_string()); + + if let Some(method_index) = clinit_idx { + let clinit_frame = StackFrame::new( + class_file, + class_idx, + method_index as u16, + &[], + ); + + self.stack_frames.push(clinit_frame); + } + + // finish self.class_store.set_init(class_idx, true); Ok(()) @@ -312,7 +564,67 @@ impl JVM { let (instruction, offset) = bytecode.next_instruction(frame.instruction_pointer as usize); frame.instruction_pointer += offset as u32; + println!("{:25}.{:15}:{:<10}{instruction:?}", class.get_classname().unwrap(), method.name, frame.instruction_pointer); + match instruction { + + Instruction::Duplicate() => { + let popped_value = wrap_stackframe_error(class, method, frame.operand_stack.pop_computational_1(0))?; + wrap_stackframe_error(class, method, frame.operand_stack.push(popped_value))?; + wrap_stackframe_error(class, method, frame.operand_stack.push(popped_value))?; + } + + Instruction::InvokeSpecial(methodref_index) => { + let (supplied_class_name, supplied_method_name, supplied_descriptor_string) = class.gather_methodref(methodref_index)?; + + if ! self.class_store.have_class(supplied_class_name) { + // rewind the bytecode offset, I'll need to execute this instruction again + frame.instruction_pointer -= offset as u32; + + return Ok(JVMCallbackOperation::LoadClass(supplied_class_name.to_string())); + } + if ! self.class_store.was_init(supplied_class_name).unwrap() { + // rewind the bytecode offset, I'll need to execute this instruction again + frame.instruction_pointer -= offset as u32; + + return Ok(JVMCallbackOperation::InitClass(supplied_class_name.to_string())); + } + + let target_class_index = self.class_store.class_idx_from_name(supplied_class_name).unwrap(); + + let parsed_expected_descriptor: MethodDescriptor = MethodDescriptor::try_from(supplied_descriptor_string)?; + let (class_index, method_index, method_info) = match ClassMethodIterator::new(target_class_index, &self.class_store) + .filter(|(_cid, _mid, minfo)| minfo.name == *supplied_method_name) + .filter(|(_cid, _mid, minfo)| minfo.descriptor == parsed_expected_descriptor) + .next() { + Some(m) => m, + None => { + // TODO: Throw exception + return Err(Error::RunTimeError(format!("InvokeSpecial: Failed to find requested method '{}' with descriptor '{}' in the class '{}'", supplied_method_name, supplied_descriptor_string, supplied_class_name))); + } + }; + + let mut arguments = VecDeque::new(); + fill_arguments(class, method, &mut arguments, &method_info.descriptor.argument_types, &mut frame.operand_stack)?; + + // this + arguments.push_front( + StackValue::Reference( + wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))? + ) + ); + + + let new_frame = StackFrame::new( + self.class_store.class_file_from_idx(class_index).unwrap(), + class_index, + method_index as u16, + &arguments.make_contiguous(), + ); + + return Ok(JVMCallbackOperation::PushFrame(new_frame)); + }, + Instruction::InvokeStatic(methodref_index) => { let (supplied_class_name, supplied_method_name, supplied_descriptor_string) = class.gather_methodref(methodref_index)?; @@ -397,18 +709,159 @@ impl JVM { load_local_int(class, method, frame, 3)?; } + Instruction::LoadLocalReference0() => { + load_local_reference(class, method, frame, 0)?; + } + Instruction::MultiplyInt() => { let factor_1 = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?; let factor_2 = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?; wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(factor_1 * factor_2)))?; } - Instruction::PushConstInt5() => { - let frame_result = frame.operand_stack.push(StackValue::Int(5)); - match frame_result { - Ok(_) => (), - Err(err) => return Err(Error::StackFrameError(err, format!("in '{}', in class '{}'", method.name, class.get_classname().unwrap()))), + Instruction::NewArray(class_index) => { + // construct single level array + let class_name = class.gather_class(class_index)?; + + if ! self.class_store.have_class(class_name) { + // rewind the bytecode offset, I'll need to execute this instruction again + frame.instruction_pointer -= offset as u32; + return Ok(JVMCallbackOperation::LoadClass(class_name.to_string())); } + if ! self.class_store.was_init(class_name).unwrap() { + // rewind the bytecode offset, I'll need to execute this instruction again + frame.instruction_pointer -= offset as u32; + return Ok(JVMCallbackOperation::InitClass(class_name.to_string())); + } + + let class_descriptor = AbstractTypeDescription { + array_level: 0, + kind: match class_name { + _ => AbstractTypeKind::Classname(class_name.to_string()) + } + }; + + let array_descriptor = AbstractTypeDescription { + array_level: 1, + kind: class_descriptor.kind.clone(), + }; + + if let None = self.class_store.get_array_class_ref(&array_descriptor) { + let complete_type_descriptor = format!("L{};", class_name); + + // rewind the bytecode offset, I'll need to execute this instruction again + frame.instruction_pointer -= offset as u32; + + return Ok(JVMCallbackOperation::MakeArrayClass(complete_type_descriptor)); + } + + let array_capacity = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?; + let array_ref = self.heap_area.make_empty_array(&self.class_store, class_descriptor, array_capacity as usize); + + wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Reference(array_ref)))?; + } + + Instruction::NewObject(class_index) => { + let class_name = class.gather_class(class_index)?; + + if ! self.class_store.have_class(class_name) { + // rewind the bytecode offset, I'll need to execute this instruction again + frame.instruction_pointer -= offset as u32; + + return Ok(JVMCallbackOperation::LoadClass(class_name.to_string())); + } + if ! self.class_store.was_init(class_name).unwrap() { + // rewind the bytecode offset, I'll need to execute this instruction again + frame.instruction_pointer -= offset as u32; + + return Ok(JVMCallbackOperation::InitClass(class_name.to_string())); + } + + let class_index = self.class_store.class_idx_from_name(class_name).unwrap(); + let new_object = self.heap_area.make_object(&self.class_store, class_index); + + wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Reference(new_object)))?; + }, + + Instruction::PushConstInt0() => { + wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(0)))?; + } + Instruction::PushConstInt1() => { + wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(1)))?; + } + Instruction::PushConstInt2() => { + wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(2)))?; + } + Instruction::PushConstInt3() => { + wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(3)))?; + } + Instruction::PushConstInt4() => { + wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(4)))?; + } + Instruction::PushConstInt5() => { + wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(5)))?; + } + + Instruction::PutStatic(fieldref_index) => { + let (target_class_name, target_field_name, expected_field_descriptor) = class.gather_fieldref(fieldref_index)?; + + if ! self.class_store.have_class(target_class_name) { + // rewind the bytecode offset, I'll need to execute this instruction again + frame.instruction_pointer -= offset as u32; + + return Ok(JVMCallbackOperation::LoadClass(target_class_name.to_string())); + } + if ! self.class_store.was_init(target_class_name).unwrap() { + // rewind the bytecode offset, I'll need to execute this instruction again + frame.instruction_pointer -= offset as u32; + + return Ok(JVMCallbackOperation::InitClass(target_class_name.to_string())); + } + + let matched_field = match ClassFieldIterator::new(self.class_store.class_idx_from_name(target_class_name).unwrap(), &self.class_store) + .filter(|f| f.name == *target_field_name) + .filter(|f| f.access_flags & FieldAccessFlag::Static) + .next() { + Some(f) => f, + None => return Err(Error::RunTimeError(format!("PutStatic: Trying to set field '{}' on class '{}' but there is no such static field", target_field_name, target_class_name))) + }; + + + let (consumed_chars, expected_descriptor) = AbstractTypeDescription::parse_first(expected_field_descriptor)?; + assert!(expected_field_descriptor.len() == consumed_chars); + + // TODO: Throw exception on fail + if matched_field.descriptor != expected_descriptor { + return Err(Error::RunTimeError(format!("PutStatic: Field descriptor mismatch: '{expected_descriptor:?}' but found '{:?}'", matched_field.descriptor))) + } + + let set_value = match (matched_field.descriptor.array_level, &matched_field.descriptor.kind) { + + (0, AbstractTypeKind::Boolean()) => { + let int_value = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?; + + FieldValue::Boolean((int_value & 1) != 0) + }, + + (0..=255, AbstractTypeKind::Classname(_field_type_name)) => { + let ref_value = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?; + + let value_native_name = self.heap_area.object_area.get_reference_native_class_name(ref_value, &self.class_store); + let parsed_native_name = AbstractTypeDescription::parse_first(value_native_name).unwrap().1; + + if ! self.class_store.are_types_compatible(&matched_field.descriptor, &parsed_native_name) { + return Err(Error::RunTimeError(format!("PutStatic: Trying to set a value with type '{parsed_native_name:?}' on a field with type '{:?}'", matched_field.descriptor))); + } + + FieldValue::Reference(ref_value) + } + _ => { + println!("{:?}", matched_field); + todo!() + }, + }; + + self.heap_area.static_area.set(target_class_name, target_field_name, set_value)?; } Instruction::ReturnInt() => { @@ -443,19 +896,19 @@ impl JVM { wrap_stackframe_error(class, method, frame.store_local(0, StackValue::Int(int)))?; }, Instruction::StoreLocalInt1() => { - let int = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(1))?; + let int = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?; - wrap_stackframe_error(class, method, frame.store_local(0, StackValue::Int(int)))?; + wrap_stackframe_error(class, method, frame.store_local(1, StackValue::Int(int)))?; }, Instruction::StoreLocalInt2() => { - let int = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(2))?; + let int = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?; - wrap_stackframe_error(class, method, frame.store_local(0, StackValue::Int(int)))?; + wrap_stackframe_error(class, method, frame.store_local(2, StackValue::Int(int)))?; }, Instruction::StoreLocalInt3() => { - let int = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(3))?; + let int = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?; - wrap_stackframe_error(class, method, frame.store_local(0, StackValue::Int(int)))?; + wrap_stackframe_error(class, method, frame.store_local(3, StackValue::Int(int)))?; }, _ => { @@ -477,22 +930,23 @@ enum JVMCallbackOperation { PushFrame(StackFrame), LoadClass(String), InitClass(String), + MakeArrayClass(String), +} + +fn load_local_reference(class: &JavaClassFile, method: &MethodInfo, frame: &mut StackFrame, index: usize) -> Result<(), Error> { + let loaded_value = wrap_stackframe_error(class, method, frame.load_local_reference(index as u16))?; + + wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Reference(loaded_value)))?; + + Ok(()) } fn load_local_int(class: &JavaClassFile, method: &MethodInfo, frame: &mut StackFrame, index: usize) -> Result<(), Error> { - let frame_result = frame.load_local_int(index as u16); - let local_int = match frame_result { - Ok(i) => { - i - }, - Err(err) => return Err(Error::StackFrameError(err, format!("in '{}', in class '{}'", method.name, class.get_classname().unwrap()))), - }; + let loaded_value = wrap_stackframe_error(class, method, frame.load_local_int(index as u16))?; - let frame_result = frame.operand_stack.push(StackValue::Int(local_int)); - match frame_result { - Ok(_) => Ok(()), - Err(err) => return Err(Error::StackFrameError(err, format!("in '{}', in class '{}'", method.name, class.get_classname().unwrap()))), - } + wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(loaded_value)))?; + + Ok(()) } fn fill_arguments(class: &JavaClassFile, method: &MethodInfo, arguments: &mut VecDeque, argument_types: &Box<[AbstractTypeDescription]>, stack: &mut OperandStack) -> Result<(), Error> { diff --git a/src/main.rs b/src/main.rs index 0ad7fba..ccaa99e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,26 +6,32 @@ mod stackframe; mod accessmasks; mod constantpool; mod heap_area; +mod iterators; use std::fs::File; - -use crate::stackframe::StackValue; +// +use crate::accessmasks::FieldAccessFlag; +//use crate::stackframe::StackValue; use crate::classfile::JavaClassFile; fn main() { + //println!("{:#?}", JavaClassFile::new(&mut File::open("java/Main.class").unwrap()).unwrap()); + let mut jvm = jvm::JVM::new(); - jvm.entrypoint( + match jvm.entrypoint( &"java/Math".to_string(), &"v".to_string(), - &[]//&[Value::Int(1), Value::Int(2)], - ).expect("failed to call main() on supplied class"); - - match jvm.run() { + &["Hello World"], + ) { Ok(()) => (), Err(e) => println!("{:#?}", e), - }; + } - //println!("{:#?}", jvm.heap_area); - //println!("{:#?}", JavaClassFile::new(&mut File::open("java/Class.class").unwrap())); + // match jvm.run() { + // Ok(()) => (), + // Err(e) => println!("{:#?}", e), + // }; + + //println!("{:#?}", jvm.heap_area.static_area); } diff --git a/src/stackframe.rs b/src/stackframe.rs index 64f1a39..8b2095a 100644 --- a/src/stackframe.rs +++ b/src/stackframe.rs @@ -48,6 +48,19 @@ impl OperandStack { Ok(()) } + + pub fn pop_computational_1(&mut self, index: usize) -> Result { + let absolute_index = self.depth as usize - 1 - index; + let value = self.stack[absolute_index]; + self.depth -= 1; + + match value { + StackValue::Long0(_) | StackValue::Long1(_) | StackValue::Double0(_) | StackValue::Double1(_) => { + Err(Error::LocalError(format!("Mismatched type at index {} of the function operand stack, expected type with computational type 1 but found '{:?}'", index, value))) + }, + _ => Ok(value), + } + } pub fn pop_boolean(&mut self, index: usize) -> Result { let absolute_index = self.depth as usize - 1 - index; @@ -239,6 +252,14 @@ impl StackFrame { } } + pub fn load_local_reference(&self, index: u16) -> Result { + let local = self.locals[index as usize]; + match local { + StackValue::Reference(r) => Ok(r), + _ => Err(Error::LocalError(format!("Mismatched type at index {} of the function locals, expected Reference but found '{:?}'", index, local))) + } + } + pub fn store_local(&mut self, index: u16, value: StackValue) -> Result<(), Error> { let field = self.locals.get_mut(index as usize); match field {