diff --git a/src/classfile.rs b/src/classfile.rs index 9ae184a..0b6ec53 100644 --- a/src/classfile.rs +++ b/src/classfile.rs @@ -854,6 +854,24 @@ impl Into for &AbstractTypeDescription { impl AbstractTypeDescription { + pub fn storage_size(&self) -> u8 { + match self.array_level { + 0 => match self.kind { + AbstractTypeKind::Void() => 0, + AbstractTypeKind::Byte() => 1, + AbstractTypeKind::Char() => 2, + AbstractTypeKind::Double() => 8, + AbstractTypeKind::Float() => 4, + AbstractTypeKind::Int() => 4, + AbstractTypeKind::Long() => 8, + AbstractTypeKind::Short() => 2, + AbstractTypeKind::Boolean() => 1, + AbstractTypeKind::Classname(_) => 8 + } + _ => 8 + } + } + pub fn super_component(&self) -> Self { AbstractTypeDescription { array_level: 0, @@ -927,6 +945,14 @@ impl AbstractTypeDescription { kind: AbstractTypeKind::Classname(name.to_string()), } } + + pub fn extract_class_name(&self) -> &String { + match self.kind { + AbstractTypeKind::Classname(ref name) => name, + _ => unreachable!() + } + } + } #[derive(Debug, Eq, PartialEq)] diff --git a/src/classstore.rs b/src/classstore.rs index b37e7e2..19a753c 100644 --- a/src/classstore.rs +++ b/src/classstore.rs @@ -261,6 +261,13 @@ impl ClassStore { return self.array_classes.get(type_desc).copied(); } + 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 + } + } + pub fn class_ref_for_type(&self, r#type: AbstractTypeDescription) -> Option { match (r#type.array_level, &r#type.kind) { (0, AbstractTypeKind::Classname(ref name)) => { diff --git a/src/heap_area.rs b/src/heap_area.rs index 5242190..5ade8ee 100644 --- a/src/heap_area.rs +++ b/src/heap_area.rs @@ -62,6 +62,24 @@ impl HeapArea { array_ref } + pub fn decode_java_string(&mut self, string_ref: ObjectReference, class_store: &ClassStore) -> String { + let byte_array_reference = self.object_area.get_object_field(string_ref, "value", self.object_area.get_object_class_index(string_ref), class_store).unwrap().expect_reference(); + let byte_array_length = self.object_area.get_array_length(byte_array_reference); + + let mut utf16_bytes = Vec::with_capacity(byte_array_length / 2); + for index in 0..byte_array_length/2 { + let i0 = self.object_area.get_array_element(byte_array_reference, (index * 2) as i32).expect_byte(); + let i1 = self.object_area.get_array_element(byte_array_reference, (index * 2 + 1) as i32).expect_byte(); + + let u0 = u8::from_ne_bytes(i0.to_ne_bytes()); + let u1 = u8::from_ne_bytes(i1.to_ne_bytes()); + + utf16_bytes.push(u16::from_ne_bytes([u0, u1])); + } + + String::from_utf16(&utf16_bytes).unwrap() + } + pub fn make_handmade_string(&mut self, s: &String, class_store: &ClassStore) -> ObjectReference { let utf16_bytes = { let utf16 = s.encode_utf16(); @@ -713,6 +731,20 @@ impl FieldValue { } } + fn expect_reference(&self) -> ObjectReference { + match self { + Self::Reference(r) => *r, + _ => unreachable!() + } + } + + fn expect_byte(&self) -> i8 { + match self { + Self::Byte(b) => *b, + _ => unreachable!() + } + } + fn default_for(t: &AbstractTypeDescription) -> Self { if t.array_level != 0 { return Self::Reference(ObjectReference::NULL); diff --git a/src/jvm.rs b/src/jvm.rs index f2ad7eb..aadd7b3 100644 --- a/src/jvm.rs +++ b/src/jvm.rs @@ -474,8 +474,7 @@ impl JVM { let class_name = waiting_queue.pop_front().unwrap(); if ! self.class_store.have_class(&class_name) { - println!("Loading Class {class_name}"); - self.load_class(&class_name)?; + let new_class_index = self.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()); @@ -485,6 +484,7 @@ impl JVM { let interface_name = file.gather_class(*interface_index)?; waiting_queue.push_back(interface_name.to_string()); } + println!("Loaded Class {class_name} ({new_class_index})"); } } @@ -672,7 +672,7 @@ impl JVM { let (instruction, offset) = bytecode.next_instruction(frame.instruction_pointer as usize); frame.instruction_pointer += offset as u32; - //println!("{} locals: {:?}", " ".repeat(frame_index), frame.locals); + println!("{} locals: {:?}", " ".repeat(frame_index), frame.locals); println!("{} stack: {:?}", " ".repeat(frame_index), frame.operand_stack); println!("{}{}.{}:{:<10}{instruction:?}\n", " ".repeat(frame_index), class.get_classname().unwrap(), method.name, frame.instruction_pointer); @@ -1484,6 +1484,19 @@ impl JVM { array_ref } else { let mut test_type = array_type_desc.super_component(); + + if component_name.len() != 1 { + let base_type_name = test_type.extract_class_name(); + if ! self.class_store.have_class(base_type_name) { + frame.instruction_pointer -= offset as u32; + return Ok(JVMCallbackOperation::LoadClass(base_type_name.to_string())); + } + if ! self.class_store.was_init(base_type_name).unwrap() { + frame.instruction_pointer -= offset as u32; + return Ok(JVMCallbackOperation::InitClass(base_type_name.to_string())); + } + } + let mut test_object_ref = self.class_store.class_ref_for_type(test_type.clone()).unwrap(); while let Some(new_test_object_ref) = self.class_store.class_ref_for_type(test_type.array()) { @@ -1844,7 +1857,12 @@ impl JVM { let value = match expected_field_descriptor.as_str() { "J" | "D" => wrap_stackframe_error(class, method, frame.operand_stack.pop_computational_2(0))?, _ => match wrap_stackframe_error(class, method, frame.operand_stack.pop_computational_1(0))? { - StackValue::Int(i) => FieldValue::Int(i), + StackValue::Int(i) => { + match expected_field_descriptor.as_str() { + "Z" => FieldValue::Boolean(i != 0), + _ => FieldValue::Int(i) + } + } StackValue::Reference(r) => FieldValue::Reference(r), StackValue::Float(f) => FieldValue::Float(f), StackValue::Byte(b) => FieldValue::Byte(b), diff --git a/src/native_methods.rs b/src/native_methods.rs index f87c138..b64b88a 100644 --- a/src/native_methods.rs +++ b/src/native_methods.rs @@ -1,9 +1,9 @@ - use crate::stackframe::StackFrame; use crate::heap_area::ObjectReference; use crate::heap_area::FieldValue; use crate::jvm::wrap_stackframe_error; use crate::stackframe::StackValue; +use crate::iterators::ClassFieldIterator; use crate::classfile::{ AbstractTypeDescription, AbstractTypeKind, MethodDescriptor }; use crate::native_registry::NativeMethodCallable; use crate::jvm::JVM; @@ -328,6 +328,14 @@ impl JavaLangDouble { } } +struct JavaLangRuntime {} + +impl JavaLangRuntime { + pub fn available_processors(_jvm: &mut JVM) -> Result { + Ok(JVMCallbackOperation::ReturnFrame(FieldValue::Int(1))) + } +} + struct JavaLangStringUTF16 {} impl JavaLangStringUTF16 { @@ -339,6 +347,29 @@ impl JavaLangStringUTF16 { struct JdkInternalMiscUnsafe {} impl JdkInternalMiscUnsafe { + + pub fn object_field_offset_1(jvm: &mut JVM) -> Result { + // args: Class class, String fieldName + let frame = { + let frame_index = jvm.stack_frames.len() - 1; + &mut jvm.stack_frames[frame_index] + }; + + let class_object_reference = frame.load_local_reference(1).unwrap(); + let class_descriptor = jvm.heap_area.object_area.get_class_ref_native_class_name(class_object_reference, &jvm.class_store); + let class_index = jvm.class_store.class_index_for_type(AbstractTypeDescription::parse_full(class_descriptor).unwrap()).unwrap(); + let field_name_string_reference = frame.load_local_reference(2).unwrap(); + + let rust_field_name_string = jvm.heap_area.decode_java_string(field_name_string_reference, &jvm.class_store); + + let byte_offset: i64 = ClassFieldIterator::new(class_index as usize, &jvm.class_store) + .take_while(|f| f.name != rust_field_name_string) + .map(|f| f.descriptor.storage_size() as i64) + .sum(); + + Ok(JVMCallbackOperation::ReturnFrame(FieldValue::Long(byte_offset))) + } + pub fn array_index_scale_0(jvm: &mut JVM) -> Result { let frame = { let frame_index = jvm.stack_frames.len() - 1; @@ -1127,6 +1158,71 @@ pub fn function_for(class_name: &str, m: &crate::classfile::MethodInfo) -> Resul Ok(todo_call) } + ("java/lang/Runtime", "availableProcessors") => { + let expected_descriptor = MethodDescriptor { + argument_types: Box::new([]), + return_type: AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Int()}, + }; + + if m.descriptor != expected_descriptor { + return Err(Error::RunTimeError(format!("Native descriptor mismatch for method '{class_name}.{method_name}': found '{}' but expected '{}'", m.descriptor.source_string(), expected_descriptor.source_string()))); + } + + Ok(JavaLangRuntime::available_processors) + } + + ("java/lang/Runtime", "freeMemory") => { + let expected_descriptor = MethodDescriptor { + argument_types: Box::new([]), + return_type: AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Long()}, + }; + + if m.descriptor != expected_descriptor { + return Err(Error::RunTimeError(format!("Native descriptor mismatch for method '{class_name}.{method_name}': found '{}' but expected '{}'", m.descriptor.source_string(), expected_descriptor.source_string()))); + } + + Ok(todo_call) + } + + ("java/lang/Runtime", "gc") => { + let expected_descriptor = MethodDescriptor { + argument_types: Box::new([]), + return_type: AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Void()}, + }; + + if m.descriptor != expected_descriptor { + return Err(Error::RunTimeError(format!("Native descriptor mismatch for method '{class_name}.{method_name}': found '{}' but expected '{}'", m.descriptor.source_string(), expected_descriptor.source_string()))); + } + + Ok(todo_call) + } + + ("java/lang/Runtime", "maxMemory") => { + let expected_descriptor = MethodDescriptor { + argument_types: Box::new([]), + return_type: AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Long()}, + }; + + if m.descriptor != expected_descriptor { + return Err(Error::RunTimeError(format!("Native descriptor mismatch for method '{class_name}.{method_name}': found '{}' but expected '{}'", m.descriptor.source_string(), expected_descriptor.source_string()))); + } + + Ok(todo_call) + } + + ("java/lang/Runtime", "totalMemory") => { + let expected_descriptor = MethodDescriptor { + argument_types: Box::new([]), + return_type: AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Long()}, + }; + + if m.descriptor != expected_descriptor { + return Err(Error::RunTimeError(format!("Native descriptor mismatch for method '{class_name}.{method_name}': found '{}' but expected '{}'", m.descriptor.source_string(), expected_descriptor.source_string()))); + } + + Ok(todo_call) + } + ("java/lang/System", "arraycopy") => { let expected_descriptor = MethodDescriptor { argument_types: Box::new([ @@ -1263,6 +1359,19 @@ pub fn function_for(class_name: &str, m: &crate::classfile::MethodInfo) -> Resul Ok(todo_call) } + ("java/lang/Thread", "registerNatives") => { + let expected_descriptor = MethodDescriptor { + argument_types: Box::new([]), + return_type: AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Void()}, + }; + + if m.descriptor != expected_descriptor { + return Err(Error::RunTimeError(format!("Native descriptor mismatch for method '{class_name}.{method_name}': found '{}' but expected '{}'", m.descriptor.source_string(), expected_descriptor.source_string()))); + } + + Ok(ignore_call) + } + ("jdk/internal/misc/Unsafe", "arrayBaseOffset0") => { let expected_descriptor = MethodDescriptor { argument_types: Box::new([ @@ -2149,7 +2258,7 @@ pub fn function_for(class_name: &str, m: &crate::classfile::MethodInfo) -> Resul return Err(Error::RunTimeError(format!("Native descriptor mismatch for method '{class_name}.{method_name}': found '{}' but expected '{}'", m.descriptor.source_string(), expected_descriptor.source_string()))); } - Ok(todo_call) + Ok(JdkInternalMiscUnsafe::object_field_offset_1) } ("jdk/internal/misc/Unsafe", "registerNatives") => {