From 5cf17d5ca39a50b173302bcda213c11d5451f483 Mon Sep 17 00:00:00 2001 From: VegOwOtenks Date: Thu, 14 Nov 2024 18:32:25 +0100 Subject: [PATCH] Real classes for arrays, initialization order --- src/classstore.rs | 2 +- src/heap_area.rs | 24 +++++++++--- src/jvm.rs | 85 ++++++++++++++++++++++++++++++------------- src/native_methods.rs | 84 +++++++++++++++++++++++++++++++++++++++++- 4 files changed, 160 insertions(+), 35 deletions(-) diff --git a/src/classstore.rs b/src/classstore.rs index 87393fc..e9dfc8d 100644 --- a/src/classstore.rs +++ b/src/classstore.rs @@ -271,7 +271,7 @@ impl ClassStore { pub fn class_index_for_type(&self, r#type: AbstractTypeDescription) -> Option { match (r#type.array_level, &r#type.kind) { (0, AbstractTypeKind::Classname(ref name)) => Some(self.class_idx_from_name(name).unwrap()), - _ => None + (_, _) => Some(self.class_idx_from_name(&(&r#type).into())?) } } diff --git a/src/heap_area.rs b/src/heap_area.rs index 1f4b24b..69e5b99 100644 --- a/src/heap_area.rs +++ b/src/heap_area.rs @@ -273,6 +273,11 @@ impl ObjectArea { self.get_class_ref_native_class_name(class_ref, class_store) } + pub fn get_reference_class_index(&self, reference: ObjectReference, class_store: &ClassStore) -> usize { + let class_desc = self.get_reference_native_class_name(reference, class_store); + class_store.class_index_for_type(AbstractTypeDescription::parse_full(class_desc).unwrap()).unwrap() + } + pub fn get_object_class_index(&self, reference: ObjectReference) -> usize { match self.get_entry(reference) { CompartmentEntry::Object(o) => o.class_index, @@ -518,6 +523,13 @@ impl ObjectArea { (array_ref, array_size) } + + pub fn clone_(&mut self, this: ObjectReference) -> ObjectReference { + let clone = self.get_entry(this).clone(); + let clone_ref = self.store_entry(clone); + + clone_ref + } } pub struct ObjectCompartment { @@ -585,7 +597,7 @@ impl DebugTrait for ObjectCompartment { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum CompartmentEntry { Object(HeapObject), ReferenceArray(ReferenceArray), @@ -596,31 +608,31 @@ pub enum CompartmentEntry { EmptyTail(), // last empty value } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct CharArray { class_ref: ObjectReference, content: Box<[u16]>, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct IntArray { class_ref: ObjectReference, content: Box<[i32]>, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct ByteArray { class_ref: ObjectReference, content: Box<[i8]>, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct ReferenceArray { class_ref: ObjectReference, content: Box<[ObjectReference]>, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct HeapObject { class_index: usize, fields: Box<[ObjectField]>, diff --git a/src/jvm.rs b/src/jvm.rs index d726863..917ae57 100644 --- a/src/jvm.rs +++ b/src/jvm.rs @@ -12,7 +12,7 @@ use crate::classstore; use crate::classstore::ClassStore; use crate::constantpool::{ ConstantClassInfo, ConstantInterfaceMethodRefInfo, ConstantMethodRefInfo, ConstantNameAndTypeInfo, ConstantPoolInfo, ConstantUtf8Info}; use crate::heap_area::{ HeapArea, FieldValue, ObjectReference, CompartmentEntry }; -use crate::iterators::{ ClassMethodIterator, ClassFieldIterator }; +use crate::iterators::{ ClassFieldIterator, ClassMethodIterator, CompatibleTypesIterator }; use crate::native_methods; use crate::native_methods::EntryPoint; use crate::native_registry::NativeRegistry; @@ -188,6 +188,31 @@ impl JVM { self.class_store.class_idx_from_name(&"::NativeClassData".to_string()).unwrap(), &self.class_store, ).unwrap(); + + let array_class_file = JavaClassFile { + minor_version: 0, + major_version: 63, + constant_pool: Box::new( + [ + ConstantPoolInfo::Class(ConstantClassInfo { name_index: 2 }), + ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: (&array_type_description).into() }), + ConstantPoolInfo::Class(ConstantClassInfo { name_index: 4 }), + ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "java/lang/Object".to_string() }), + ] + ), + access_flags: ClassAccessFlagMask { + mask: ClassAccessFlag::Public.discriminant() | ClassAccessFlag::Synthetic.discriminant() + }, + this_class: 1, + super_class: 3, + interfaces: Box::new([]), + fields: Box::new([]), + methods: Box::new([]), + attributes: Box::new([]), + }; + + self.class_store.add_class(array_class_file, true).unwrap(); + self.class_store.put_array_class_ref( array_type_description, array_class_object, @@ -557,30 +582,18 @@ impl JVM { } fn init_class_hierarchy(&mut self, name: &String) -> Result<(), Error> { - let mut class_stack = vec![name.to_string()]; + // TODO: Work around the clones - while class_stack.len() != 0 { - let current_name = class_stack.pop().unwrap(); - let was_super_init = { - let (file, _) = self.class_store.get_class(¤t_name)?; - if ! file.has_super_class() { - true - } else { - let super_name = file.get_super_class_name()?; - self.class_store.was_init(super_name).unwrap() - } - }; + let mut current_name = name.clone(); - if was_super_init { - let class_idx = self.class_store.class_idx_from_name(¤t_name).unwrap(); - self.init_class(class_idx)?; - } else { - let super_name = { - let (file, _) = self.class_store.get_class(¤t_name)?; - file.get_super_class_name()? - }; - class_stack.push(current_name); - class_stack.push(super_name.to_string()); + while ! self.class_store.was_init(¤t_name).unwrap() { + let class_index = self.class_store.class_idx_from_name(¤t_name).unwrap(); + self.init_class(class_index)?; + + let class_file = self.class_store.class_file_from_idx(class_index).unwrap(); + + if class_file.has_super_class() { + current_name = class_file.get_super_class_name().unwrap().clone(); } } @@ -1310,11 +1323,31 @@ impl JVM { return Ok(JVMCallbackOperation::InitClass(class_name.to_string())); } + let class_index = self.class_store.class_idx_from_name(class_name).unwrap(); + let owning_class = CompatibleTypesIterator::new(class_index, &self.class_store) + .filter(|name| { + let index = self.class_store.class_idx_from_name(name).unwrap(); + let class_file = self.class_store.class_file_from_idx(index).unwrap(); + class_file.fields.iter() + .filter(|finfo| finfo.access_flags & FieldAccessFlag::Static) + .filter(|finfo| finfo.name == *field_name) + .next() + .is_some() + }) + .next() + .unwrap_or_else(|| class_name); + + if ! self.class_store.was_init(owning_class).unwrap() { + // rewind the bytecode offset, I'll need to execute this instruction again + frame.instruction_pointer -= offset as u32; + return Ok(JVMCallbackOperation::InitClass(owning_class.to_string())); + } + // TODO: Throw error let parsed_field_descriptor = AbstractTypeDescription::parse_full(field_descriptor).unwrap(); // TODO: Throw error - let fetched_value = self.heap_area.static_area.get(class_name, field_name, parsed_field_descriptor).unwrap(); + let fetched_value = self.heap_area.static_area.get(owning_class, field_name, parsed_field_descriptor).unwrap(); wrap_stackframe_error(class, method, frame.operand_stack.push_field_value(fetched_value))?; } @@ -1569,8 +1602,8 @@ impl JVM { let this_object = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?; arguments.push_front(StackValue::Reference(this_object)); - // TODO: Are there any methods callable on arrays? - let this_object_class_index = self.heap_area.object_area.get_object_class_index(this_object); + // Are there any methods callable on arrays?, turns out there are + let this_object_class_index = self.heap_area.object_area.get_reference_class_index(this_object, &self.class_store); let this_object_class_name = self.class_store.class_name_from_index(this_object_class_index).unwrap().to_string(); let this_object_descriptor = AbstractTypeDescription { array_level: 0, diff --git a/src/native_methods.rs b/src/native_methods.rs index 8255dda..42b3fd2 100644 --- a/src/native_methods.rs +++ b/src/native_methods.rs @@ -364,6 +364,17 @@ impl JavaLangDouble { struct JavaLangObject {} impl JavaLangObject { + fn clone(jvm: &mut JVM) -> Result { + let frame = { + let frame_index = jvm.stack_frames.len() - 1; + &mut jvm.stack_frames[frame_index] + }; + let this = frame.load_local_reference(0).unwrap(); + + let clone = jvm.heap_area.object_area.clone_(this); + + Ok(JVMCallbackOperation::ReturnFrame(clone.into())) + } fn get_class(jvm: &mut JVM) -> Result { let frame = { @@ -455,6 +466,23 @@ impl JavaLangStringUTF16 { struct JavaLangSystem {} impl JavaLangSystem { + fn set_in_0(jvm: &mut JVM) -> Result { + let frame = { + let frame_index = jvm.stack_frames.len() - 1; + &mut jvm.stack_frames[frame_index] + }; + let input_stream = frame.load_local_reference(0).unwrap(); + + // TODO: Bypass final + jvm.heap_area.static_area.set( + &String::from("java/lang/System"), + &String::from("in"), + FieldValue::Reference(input_stream) + )?; + + Ok(JVMCallbackOperation::PopFrame()) + } + pub fn arraycopy(jvm: &mut JVM) -> Result { let frame = { let frame_index = jvm.stack_frames.len() - 1; @@ -1401,7 +1429,7 @@ pub fn function_for(class_name: &str, m: &crate::classfile::MethodInfo) -> Resul argument_types: Box::new([]), return_type: AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Classname(String::from("java/lang/Object"))}, }, - todo_call + JavaLangObject::clone ), ( @@ -1588,7 +1616,7 @@ pub fn function_for(class_name: &str, m: &crate::classfile::MethodInfo) -> Resul ]), return_type: AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Void()}, }, - todo_call + JavaLangSystem::set_in_0 ), ( @@ -3766,6 +3794,58 @@ pub fn function_for(class_name: &str, m: &crate::classfile::MethodInfo) -> Resul }, todo_call ), + + ( + "java/io/FileOutputStream", + "open0", + MethodDescriptor { + argument_types: Box::new([ + AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Classname("java/lang/String".to_string())}, + AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Boolean() }, + ]), + return_type: AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Void() }, + }, + todo_call + ), + + ( + "java/io/FileOutputStream", + "write", + MethodDescriptor { + argument_types: Box::new([ + AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Int() }, + AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Boolean() }, + ]), + return_type: AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Void() }, + }, + todo_call + ), + + ( + "java/io/FileOutputStream", + "writeBytes", + MethodDescriptor { + argument_types: Box::new([ + AbstractTypeDescription { array_level: 1, kind: AbstractTypeKind::Byte() }, + AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Int() }, + AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Int() }, + AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Boolean() }, + ]), + return_type: AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Void() }, + }, + todo_call + ), + + ( + "java/io/FileOutputStream", + "initIDs", + MethodDescriptor { + argument_types: Box::new([ + ]), + return_type: AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Void() }, + }, + ignore_call + ), ]; for (classname, methodname, methoddescriptor, binding) in native_mappings {