Skip to content

Commit

Permalink
Add support of Class.getDeclaringClass()
Browse files Browse the repository at this point in the history
  • Loading branch information
hextriclosan committed Dec 22, 2024
1 parent 452133c commit 2f4bac0
Show file tree
Hide file tree
Showing 18 changed files with 170 additions and 2 deletions.
20 changes: 20 additions & 0 deletions tests/integration_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
"#,
);
}
72 changes: 72 additions & 0 deletions tests/test_data/GetDeclaringClassExample.java
Original file line number Diff line number Diff line change
@@ -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());
}

}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
6 changes: 5 additions & 1 deletion vm/src/execution_engine/system_native_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -94,6 +94,10 @@ static SYSTEM_NATIVE_TABLE: Lazy<HashMap<&'static str, NativeMethod>> = 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),
Expand Down
10 changes: 9 additions & 1 deletion vm/src/method_area/attributes_helper.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use jclass::attributes::Attribute;
use jclass::attributes::{Attribute, InnerClassRecord};
use std::collections::HashMap;

pub struct AttributesHelper {
Expand Down Expand Up @@ -109,6 +109,14 @@ impl AttributesHelper {
_ => None,
}
}

pub fn get_inner_class_records(&self) -> Option<Vec<InnerClassRecord>> {
match self.data.get(&AttributeType::InnerClasses)? {
Attribute::InnerClasses { classes } => Some(classes.clone()),
_ => None,
}
}

}

#[cfg(test)]
Expand Down
7 changes: 7 additions & 0 deletions vm/src/method_area/java_class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ pub(crate) struct JavaClass {

instance_fields_hierarchy: OnceCell<IndexMap<ClassName, IndexMap<FieldNameType, Field>>>,
fields_offset_mapping: OnceCell<IndexSet<FullyQualifiedFieldName>>,
declaring_class: Option<String>,
}

#[derive(Debug)]
Expand Down Expand Up @@ -71,6 +72,7 @@ impl JavaClass {
parent: Option<String>,
interfaces: IndexSet<String>,
access_flags: u16,
declaring_class: Option<String>,
) -> Self {
let external_name = PRIMITIVE_TYPE_BY_CODE
.get(this_class_name)
Expand All @@ -90,6 +92,7 @@ impl JavaClass {
static_fields_initialized: AtomicBool::new(false),
instance_fields_hierarchy: OnceCell::new(),
fields_offset_mapping: OnceCell::new(),
declaring_class,
}
}

Expand Down Expand Up @@ -235,6 +238,10 @@ impl JavaClass {
pub fn external_name(&self) -> &str {
&self.external_name
}

pub fn declaring_class(&self) -> &Option<String> {
&self.declaring_class
}
}

impl Methods {
Expand Down
33 changes: 33 additions & 0 deletions vm/src/method_area/method_area.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<MethodArea> = OnceCell::new();

Expand Down Expand Up @@ -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(
Expand All @@ -174,6 +181,7 @@ impl MethodArea {
super_class_name,
interface_names,
access_flags,
declaring_class,
)),
))
}
Expand Down Expand Up @@ -451,6 +459,7 @@ impl MethodArea {
None,
IndexSet::new(),
PUBLIC | FINAL | ABSTRACT,
None,
))
}

Expand All @@ -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,
))
}

Expand Down Expand Up @@ -503,4 +513,27 @@ impl MethodArea {
*guard = Some(thread_id);
Ok(())
}

fn get_declaring_class(
attributes: &[Attribute],
cpool_helper: &CPoolHelper,
class_name: &str,
) -> Option<String> {
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
}
})
}
}
24 changes: 24 additions & 0 deletions vm/src/system_native/class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,3 +194,27 @@ fn get_interfaces0(class_ref: i32) -> crate::error::Result<i32> {
});
Ok(result_ref)
}

pub(crate) fn get_declaring_class0_wrp(args: &[i32]) -> crate::error::Result<Vec<i32>> {
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<i32> {
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
}

0 comments on commit 2f4bac0

Please sign in to comment.