Skip to content

Commit

Permalink
Add support of Class.getInterfaces()
Browse files Browse the repository at this point in the history
  • Loading branch information
hextriclosan committed Dec 22, 2024
1 parent 51a3ae9 commit 452133c
Show file tree
Hide file tree
Showing 8 changed files with 93 additions and 14 deletions.
20 changes: 20 additions & 0 deletions tests/integration_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -634,3 +634,23 @@ int.class is annotation: false
"#,
);
}

#[test]
fn should_get_class_interfaces() {
assert_success(
"samples.reflection.trivial.getinterfacesexample.GetInterfacesExample",
r#"Interfaces implemented by java.util.Map:
Interfaces implemented by samples.reflection.trivial.getinterfacesexample.GetInterfacesExample$ChildInterface:
java.util.Map
java.lang.Runnable
Interfaces implemented by java.util.HashMap:
java.util.Map
java.lang.Cloneable
java.io.Serializable
Interfaces implemented by [Ljava.lang.String;:
java.lang.Cloneable
java.io.Serializable
Interfaces implemented by int:
"#,
);
}
34 changes: 34 additions & 0 deletions tests/test_data/GetInterfacesExample.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package samples.reflection.trivial.getinterfacesexample;

import java.util.HashMap;
import java.util.Map;

public class GetInterfacesExample {
public static void main(String[] args) {
// Interface itself (does not have interfaces, unless it extends another)
printInterfaces(Map.class);

// Interface extending another interface
printInterfaces(ChildInterface.class);

printInterfaces(HashMap.class);

printInterfaces(String[].class);

printInterfaces(int.class);
}

static void printInterfaces(Class<?> clazz) {
System.out.print("Interfaces implemented by ");
System.out.print(clazz.getName());
System.out.println(":");
Class<?>[] interfaces = clazz.getInterfaces();
for (Class<?> iface : interfaces) {
System.out.print('\t');
System.out.println(iface.getName());
}
}

interface ChildInterface<K, V> extends Map<K, V>, Runnable {
}
}
Binary file not shown.
Binary file not shown.
9 changes: 5 additions & 4 deletions vm/src/execution_engine/system_native_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +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,
};
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::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 @@ -93,6 +90,10 @@ static SYSTEM_NATIVE_TABLE: Lazy<HashMap<&'static str, NativeMethod>> = Lazy::ne
"java/lang/Class:initClassName:()Ljava/lang/String;",
Basic(class_init_class_name_wrp),
);
table.insert(
"java/lang/Class:getInterfaces0:()[Ljava/lang/Class;",
Basic(get_interfaces0_wrp),
);
table.insert(
"jdk/internal/misc/Unsafe:registerNatives:()V",
Basic(void_stub),
Expand Down
8 changes: 4 additions & 4 deletions vm/src/method_area/java_class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::method_area::primitives_helper::PRIMITIVE_TYPE_BY_CODE;
use indexmap::{IndexMap, IndexSet};
use jdescriptor::TypeDescriptor;
use once_cell::sync::OnceCell;
use std::collections::{HashMap, HashSet};
use std::collections::HashMap;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;

Expand All @@ -25,7 +25,7 @@ pub(crate) struct JavaClass {
this_class_name: String,
external_name: String,
parent: Option<String>,
interfaces: HashSet<String>,
interfaces: IndexSet<String>,
access_flags: u16,

static_fields_initialized: AtomicBool,
Expand Down Expand Up @@ -69,7 +69,7 @@ impl JavaClass {
cpool_helper: CPoolHelper,
this_class_name: &str,
parent: Option<String>,
interfaces: HashSet<String>,
interfaces: IndexSet<String>,
access_flags: u16,
) -> Self {
let external_name = PRIMITIVE_TYPE_BY_CODE
Expand Down Expand Up @@ -101,7 +101,7 @@ impl JavaClass {
&self.parent
}

pub fn interfaces(&self) -> &HashSet<String> {
pub fn interfaces(&self) -> &IndexSet<String> {
&self.interfaces
}

Expand Down
12 changes: 6 additions & 6 deletions vm/src/method_area/method_area.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ use crate::method_area::field::Field;
use crate::method_area::java_class::{FieldDescriptors, Fields, JavaClass, Methods};
use crate::method_area::java_method::{CodeContext, JavaMethod};
use crate::method_area::primitives_helper::PRIMITIVE_TYPE_BY_CODE;
use indexmap::IndexMap;
use indexmap::{IndexMap, IndexSet};
use jclass::class_file::{parse, ClassFile};
use jclass::fields::{FieldFlags, FieldInfo};
use jclass::methods::{MethodFlags, MethodInfo};
use jdescriptor::TypeDescriptor;
use once_cell::sync::OnceCell;
use std::collections::{HashMap, HashSet};
use std::collections::HashMap;
use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};
Expand Down Expand Up @@ -155,7 +155,7 @@ impl MethodArea {
Error::new_constant_pool(&format!("Error getting interface by index={index}"))
})
})
.collect::<crate::error::Result<HashSet<String>>>()?;
.collect::<crate::error::Result<IndexSet<String>>>()?;

let methods = Self::get_methods(&class_file.methods(), &cpool_helper, &class_name)?;
let (non_static_field_descriptors, static_fields) =
Expand Down Expand Up @@ -333,7 +333,7 @@ impl MethodArea {

fn lookup_in_interface_hierarchy(
&self,
interfaces: &HashSet<String>,
interfaces: &IndexSet<String>,
full_method_signature: &str,
) -> Option<Arc<JavaMethod>> {
for interface_name in interfaces.iter() {
Expand Down Expand Up @@ -449,7 +449,7 @@ impl MethodArea {
CPoolHelper::new(&Vec::new()),
class_name,
None,
HashSet::new(),
IndexSet::new(),
PUBLIC | FINAL | ABSTRACT,
))
}
Expand All @@ -465,7 +465,7 @@ impl MethodArea {
CPoolHelper::new(&Vec::new()),
array_class_name,
Some("java/lang/Object".to_string()),
HashSet::new(),
IndexSet::from(["java/lang/Cloneable".to_string(), "java/io/Serializable".to_string()]),
PUBLIC | FINAL | ABSTRACT,
))
}
Expand Down
24 changes: 24 additions & 0 deletions vm/src/system_native/class.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::error::Error;
use crate::execution_engine::string_pool_helper::StringPoolHelper;
use crate::heap::heap::with_heap_write_lock;
use crate::method_area::method_area::with_method_area;
use crate::method_area::primitives_helper::{PRIMITIVE_CODE_BY_TYPE, PRIMITIVE_TYPE_BY_CODE};
use crate::system_native::string::get_utf8_string_by_ref;
Expand Down Expand Up @@ -170,3 +171,26 @@ fn for_name0(

Ok(reflection_ref)
}
pub(crate) fn get_interfaces0_wrp(args: &[i32]) -> crate::error::Result<Vec<i32>> {
let class_ref = args[0];
let interfaces_ref = get_interfaces0(class_ref)?;
Ok(vec![interfaces_ref])
}
fn get_interfaces0(class_ref: i32) -> crate::error::Result<i32> {
let interface_refs = with_method_area(|method_area| {
let class_name = method_area.get_from_reflection_table(class_ref)?;
let jc = method_area.get(&class_name)?;
let interfaces = jc.interfaces();

let interface_refs = interfaces
.iter()
.map(|interface| method_area.load_reflection_class(interface))
.collect::<crate::error::Result<Vec<i32>>>();
interface_refs
})?;

let result_ref = with_heap_write_lock(|heap| {
heap.create_array_with_values("[Ljava/lang/Class;", &interface_refs)
});
Ok(result_ref)
}

0 comments on commit 452133c

Please sign in to comment.