diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 759a8f8..0632571 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -654,3 +654,23 @@ Interfaces implemented by int: "#, ); } + +#[test] +fn should_return_declaring_class() { + assert_success( + "samples.reflection.trivial.classgetdeclaringclassexample.GetDeclaringClassExample", + r#"double: null +[Ljava.lang.String;: null +samples.reflection.trivial.classgetdeclaringclassexample.GetDeclaringClassExample: null +samples.reflection.trivial.classgetdeclaringclassexample.GetDeclaringClassExample$TopLevel: class samples.reflection.trivial.classgetdeclaringclassexample.GetDeclaringClassExample +samples.reflection.trivial.classgetdeclaringclassexample.GetDeclaringClassExample$SimpleNested$Inner: class samples.reflection.trivial.classgetdeclaringclassexample.GetDeclaringClassExample$SimpleNested +samples.reflection.trivial.classgetdeclaringclassexample.GetDeclaringClassExample$SimpleNested$StaticNested: class samples.reflection.trivial.classgetdeclaringclassexample.GetDeclaringClassExample$SimpleNested +samples.reflection.trivial.classgetdeclaringclassexample.GetDeclaringClassExample$1: null +samples.reflection.trivial.classgetdeclaringclassexample.GetDeclaringClassExample$1LocalClass: null +samples.reflection.trivial.classgetdeclaringclassexample.GetDeclaringClassExample$1StaticMethodInner: null +samples.reflection.trivial.classgetdeclaringclassexample.GetDeclaringClassExample$DeepNesting$Inner: class samples.reflection.trivial.classgetdeclaringclassexample.GetDeclaringClassExample$DeepNesting +samples.reflection.trivial.classgetdeclaringclassexample.GetDeclaringClassExample$DeepNesting$Inner$DeepInner: class samples.reflection.trivial.classgetdeclaringclassexample.GetDeclaringClassExample$DeepNesting$Inner +samples.reflection.trivial.classgetdeclaringclassexample.GetDeclaringClassExample$DeepNesting$Inner$DeepInner: class samples.reflection.trivial.classgetdeclaringclassexample.GetDeclaringClassExample$DeepNesting$Inner +"#, + ); +} diff --git a/tests/test_data/GetDeclaringClassExample.java b/tests/test_data/GetDeclaringClassExample.java new file mode 100644 index 0000000..f4a15c0 --- /dev/null +++ b/tests/test_data/GetDeclaringClassExample.java @@ -0,0 +1,72 @@ +package samples.reflection.trivial.classgetdeclaringclassexample; + +public class GetDeclaringClassExample { + public static class TopLevel { + } + + public static class SimpleNested { + public class Inner { + } + + public static class StaticNested { + } + } + + public static void main(String[] args) { + print(double.class); + + print(String[].class); + + print(GetDeclaringClassExample.class); + + print(TopLevel.class); + + print(SimpleNested.Inner.class); + + print(SimpleNested.StaticNested.class); + + Runnable anonymous = new Runnable() { + @Override + public void run() { + } + }; + print(anonymous.getClass()); + + class LocalClass { + } + print(LocalClass.class); + + testStaticMethodInner(); + + DeepNesting outer = new DeepNesting(); + DeepNesting.Inner inner = outer.new Inner(); + DeepNesting.Inner.DeepInner deepInner = inner.new DeepInner(); + print(DeepNesting.Inner.class); + print(DeepNesting.Inner.DeepInner.class); + print(deepInner.getClass()); + +// Runnable lambda = () -> { +// }; +// print(lambda.getClass()); // Execution Error: Unknown reference opcode: 186 + } + + private static void testStaticMethodInner() { + class StaticMethodInner { + } + print(StaticMethodInner.class); + } + + static class DeepNesting { + public class Inner { + public class DeepInner { + } + } + } + + private static void print(Class clazz) { + System.out.print(clazz.getName()); + System.out.print(": "); + System.out.println(clazz.getDeclaringClass()); + } + +} diff --git a/tests/test_data/samples/reflection/trivial/classgetdeclaringclassexample/GetDeclaringClassExample$1.class b/tests/test_data/samples/reflection/trivial/classgetdeclaringclassexample/GetDeclaringClassExample$1.class new file mode 100644 index 0000000..0045578 Binary files /dev/null and b/tests/test_data/samples/reflection/trivial/classgetdeclaringclassexample/GetDeclaringClassExample$1.class differ diff --git a/tests/test_data/samples/reflection/trivial/classgetdeclaringclassexample/GetDeclaringClassExample$1LocalClass.class b/tests/test_data/samples/reflection/trivial/classgetdeclaringclassexample/GetDeclaringClassExample$1LocalClass.class new file mode 100644 index 0000000..c0c0025 Binary files /dev/null and b/tests/test_data/samples/reflection/trivial/classgetdeclaringclassexample/GetDeclaringClassExample$1LocalClass.class differ diff --git a/tests/test_data/samples/reflection/trivial/classgetdeclaringclassexample/GetDeclaringClassExample$1StaticMethodInner.class b/tests/test_data/samples/reflection/trivial/classgetdeclaringclassexample/GetDeclaringClassExample$1StaticMethodInner.class new file mode 100644 index 0000000..c196905 Binary files /dev/null and b/tests/test_data/samples/reflection/trivial/classgetdeclaringclassexample/GetDeclaringClassExample$1StaticMethodInner.class differ diff --git a/tests/test_data/samples/reflection/trivial/classgetdeclaringclassexample/GetDeclaringClassExample$DeepNesting$Inner$DeepInner.class b/tests/test_data/samples/reflection/trivial/classgetdeclaringclassexample/GetDeclaringClassExample$DeepNesting$Inner$DeepInner.class new file mode 100644 index 0000000..ef9c13a Binary files /dev/null and b/tests/test_data/samples/reflection/trivial/classgetdeclaringclassexample/GetDeclaringClassExample$DeepNesting$Inner$DeepInner.class differ diff --git a/tests/test_data/samples/reflection/trivial/classgetdeclaringclassexample/GetDeclaringClassExample$DeepNesting$Inner.class b/tests/test_data/samples/reflection/trivial/classgetdeclaringclassexample/GetDeclaringClassExample$DeepNesting$Inner.class new file mode 100644 index 0000000..54d0b2c Binary files /dev/null and b/tests/test_data/samples/reflection/trivial/classgetdeclaringclassexample/GetDeclaringClassExample$DeepNesting$Inner.class differ diff --git a/tests/test_data/samples/reflection/trivial/classgetdeclaringclassexample/GetDeclaringClassExample$DeepNesting.class b/tests/test_data/samples/reflection/trivial/classgetdeclaringclassexample/GetDeclaringClassExample$DeepNesting.class new file mode 100644 index 0000000..7ffd3cd Binary files /dev/null and b/tests/test_data/samples/reflection/trivial/classgetdeclaringclassexample/GetDeclaringClassExample$DeepNesting.class differ diff --git a/tests/test_data/samples/reflection/trivial/classgetdeclaringclassexample/GetDeclaringClassExample$SimpleNested$Inner.class b/tests/test_data/samples/reflection/trivial/classgetdeclaringclassexample/GetDeclaringClassExample$SimpleNested$Inner.class new file mode 100644 index 0000000..77a914e Binary files /dev/null and b/tests/test_data/samples/reflection/trivial/classgetdeclaringclassexample/GetDeclaringClassExample$SimpleNested$Inner.class differ diff --git a/tests/test_data/samples/reflection/trivial/classgetdeclaringclassexample/GetDeclaringClassExample$SimpleNested$StaticNested.class b/tests/test_data/samples/reflection/trivial/classgetdeclaringclassexample/GetDeclaringClassExample$SimpleNested$StaticNested.class new file mode 100644 index 0000000..f36489a Binary files /dev/null and b/tests/test_data/samples/reflection/trivial/classgetdeclaringclassexample/GetDeclaringClassExample$SimpleNested$StaticNested.class differ diff --git a/tests/test_data/samples/reflection/trivial/classgetdeclaringclassexample/GetDeclaringClassExample$SimpleNested.class b/tests/test_data/samples/reflection/trivial/classgetdeclaringclassexample/GetDeclaringClassExample$SimpleNested.class new file mode 100644 index 0000000..123a8ff Binary files /dev/null and b/tests/test_data/samples/reflection/trivial/classgetdeclaringclassexample/GetDeclaringClassExample$SimpleNested.class differ diff --git a/tests/test_data/samples/reflection/trivial/classgetdeclaringclassexample/GetDeclaringClassExample$TopLevel.class b/tests/test_data/samples/reflection/trivial/classgetdeclaringclassexample/GetDeclaringClassExample$TopLevel.class new file mode 100644 index 0000000..6568cba Binary files /dev/null and b/tests/test_data/samples/reflection/trivial/classgetdeclaringclassexample/GetDeclaringClassExample$TopLevel.class differ diff --git a/tests/test_data/samples/reflection/trivial/classgetdeclaringclassexample/GetDeclaringClassExample.class b/tests/test_data/samples/reflection/trivial/classgetdeclaringclassexample/GetDeclaringClassExample.class new file mode 100644 index 0000000..0a0a4a0 Binary files /dev/null and b/tests/test_data/samples/reflection/trivial/classgetdeclaringclassexample/GetDeclaringClassExample.class differ diff --git a/vm/src/execution_engine/system_native_table.rs b/vm/src/execution_engine/system_native_table.rs index 4361c3b..fa03889 100644 --- a/vm/src/execution_engine/system_native_table.rs +++ b/vm/src/execution_engine/system_native_table.rs @@ -2,7 +2,7 @@ use crate::error::Error; use crate::execution_engine::system_native_table::NativeMethod::{Basic, WithStackFrames}; use crate::helper::i64_to_vec; use crate::stack::stack_frame::StackFrames; -use crate::system_native::class::{class_init_class_name_wrp, for_name0_wrp, get_modifiers_wrp, get_primitive_class_wrp, is_array_wrp, is_interface_wrp, is_primitive_wrp, get_superclass_wrp, get_interfaces0_wrp}; +use crate::system_native::class::{class_init_class_name_wrp, for_name0_wrp, get_modifiers_wrp, get_primitive_class_wrp, is_array_wrp, is_interface_wrp, is_primitive_wrp, get_superclass_wrp, get_interfaces0_wrp, get_declaring_class0_wrp}; use crate::system_native::file_descriptor::{file_descriptor_close0_wrp, get_handle_wrp}; use crate::system_native::file_output_stream::{ file_output_stream_open0_wrp, file_output_stream_write_bytes_wrp, file_output_stream_write_wrp, @@ -94,6 +94,10 @@ static SYSTEM_NATIVE_TABLE: Lazy> = Lazy::ne "java/lang/Class:getInterfaces0:()[Ljava/lang/Class;", Basic(get_interfaces0_wrp), ); + table.insert( + "java/lang/Class:getDeclaringClass0:()Ljava/lang/Class;", + Basic(get_declaring_class0_wrp), + ); table.insert( "jdk/internal/misc/Unsafe:registerNatives:()V", Basic(void_stub), diff --git a/vm/src/method_area/attributes_helper.rs b/vm/src/method_area/attributes_helper.rs index d687548..400efa5 100644 --- a/vm/src/method_area/attributes_helper.rs +++ b/vm/src/method_area/attributes_helper.rs @@ -1,4 +1,4 @@ -use jclass::attributes::Attribute; +use jclass::attributes::{Attribute, InnerClassRecord}; use std::collections::HashMap; pub struct AttributesHelper { @@ -109,6 +109,14 @@ impl AttributesHelper { _ => None, } } + + pub fn get_inner_class_records(&self) -> Option> { + match self.data.get(&AttributeType::InnerClasses)? { + Attribute::InnerClasses { classes } => Some(classes.clone()), + _ => None, + } + } + } #[cfg(test)] diff --git a/vm/src/method_area/java_class.rs b/vm/src/method_area/java_class.rs index 8997880..4978baa 100644 --- a/vm/src/method_area/java_class.rs +++ b/vm/src/method_area/java_class.rs @@ -32,6 +32,7 @@ pub(crate) struct JavaClass { instance_fields_hierarchy: OnceCell>>, fields_offset_mapping: OnceCell>, + declaring_class: Option, } #[derive(Debug)] @@ -71,6 +72,7 @@ impl JavaClass { parent: Option, interfaces: IndexSet, access_flags: u16, + declaring_class: Option, ) -> Self { let external_name = PRIMITIVE_TYPE_BY_CODE .get(this_class_name) @@ -90,6 +92,7 @@ impl JavaClass { static_fields_initialized: AtomicBool::new(false), instance_fields_hierarchy: OnceCell::new(), fields_offset_mapping: OnceCell::new(), + declaring_class, } } @@ -235,6 +238,10 @@ impl JavaClass { pub fn external_name(&self) -> &str { &self.external_name } + + pub fn declaring_class(&self) -> &Option { + &self.declaring_class + } } impl Methods { diff --git a/vm/src/method_area/method_area.rs b/vm/src/method_area/method_area.rs index 9d22217..ed21223 100644 --- a/vm/src/method_area/method_area.rs +++ b/vm/src/method_area/method_area.rs @@ -19,6 +19,7 @@ use std::io::Read; use std::path::{Path, PathBuf}; use std::sync::{Arc, RwLock}; use tracing::trace; +use jclass::attributes::Attribute; static METHOD_AREA: OnceCell = OnceCell::new(); @@ -163,6 +164,12 @@ impl MethodArea { let access_flags = class_file.access_flags().bits(); + let declaring_class = Self::get_declaring_class( + &class_file.attributes(), + &cpool_helper, + class_name.as_str(), + ); + Ok(( class_name.clone(), Arc::new(JavaClass::new( @@ -174,6 +181,7 @@ impl MethodArea { super_class_name, interface_names, access_flags, + declaring_class, )), )) } @@ -451,6 +459,7 @@ impl MethodArea { None, IndexSet::new(), PUBLIC | FINAL | ABSTRACT, + None, )) } @@ -467,6 +476,7 @@ impl MethodArea { Some("java/lang/Object".to_string()), IndexSet::from(["java/lang/Cloneable".to_string(), "java/io/Serializable".to_string()]), PUBLIC | FINAL | ABSTRACT, + None, )) } @@ -503,4 +513,27 @@ impl MethodArea { *guard = Some(thread_id); Ok(()) } + + fn get_declaring_class( + attributes: &[Attribute], + cpool_helper: &CPoolHelper, + class_name: &str, + ) -> Option { + let attribute_helper = AttributesHelper::new(attributes); + let inner_class_records = attribute_helper.get_inner_class_records()?; + + inner_class_records.iter().find_map(|inner_class_record| { + let inner_class_info_index = inner_class_record.inner_class_info_index(); + let inner_class_info = cpool_helper.get_class_name(inner_class_info_index)?; + + if class_name == inner_class_info { + let outer_class_info_index = inner_class_record.outer_class_info_index(); + let outer_class_info = cpool_helper.get_class_name(outer_class_info_index)?; + + Some(outer_class_info) + } else { + None + } + }) + } } diff --git a/vm/src/system_native/class.rs b/vm/src/system_native/class.rs index 2e27c66..30bd7dd 100644 --- a/vm/src/system_native/class.rs +++ b/vm/src/system_native/class.rs @@ -194,3 +194,27 @@ fn get_interfaces0(class_ref: i32) -> crate::error::Result { }); Ok(result_ref) } + +pub(crate) fn get_declaring_class0_wrp(args: &[i32]) -> crate::error::Result> { + let clazz_ref = args[0]; + let declaring_class_ref = get_declaring_class0(clazz_ref)?; + Ok(vec![declaring_class_ref]) +} +fn get_declaring_class0(clazz_ref: i32) -> crate::error::Result { + let declaring_class_ref = with_method_area(|method_area| { + let class_name = method_area.get_from_reflection_table(clazz_ref)?; + let jc = method_area.get(&class_name)?; + let declaring_class_ref = jc + .declaring_class() + .as_ref() + .map(|declaring_class| { + let declaring_class_ref = method_area.load_reflection_class(declaring_class); + declaring_class_ref + }) + .unwrap_or(Ok(0)); + + declaring_class_ref + }); + + declaring_class_ref +}