Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion java_runtime/src/classes/java/io/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ impl File {
RuntimeClassProto {
name: "java/io/File",
parent_class: Some("java/lang/Object"),
interfaces: vec![],
interfaces: vec!["java/io/Serializable"],
methods: vec![
JavaMethodProto::new("<clinit>", "()V", Self::clinit, MethodAccessFlags::STATIC),
JavaMethodProto::new("<init>", "(Ljava/lang/String;)V", Self::init, Default::default()),
Expand Down
2 changes: 1 addition & 1 deletion java_runtime/src/classes/java/lang/class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ impl Class {
RuntimeClassProto {
name: "java/lang/Class",
parent_class: Some("java/lang/Object"),
interfaces: vec![],
interfaces: vec!["java/io/Serializable"],
methods: vec![
JavaMethodProto::new("<init>", "()V", Self::init, Default::default()),
JavaMethodProto::new("getName", "()Ljava/lang/String;", Self::get_name, Default::default()),
Expand Down
2 changes: 1 addition & 1 deletion java_runtime/src/classes/java/lang/throwable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ impl Throwable {
RuntimeClassProto {
name: "java/lang/Throwable",
parent_class: Some("java/lang/Object"),
interfaces: vec![],
interfaces: vec!["java/io/Serializable"],
methods: vec![
JavaMethodProto::new("<init>", "()V", Self::init, Default::default()),
JavaMethodProto::new("<init>", "(Ljava/lang/String;)V", Self::init_with_message, Default::default()),
Expand Down
2 changes: 1 addition & 1 deletion java_runtime/src/classes/java/net/url.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ impl URL {
RuntimeClassProto {
name: "java/net/URL",
parent_class: Some("java/lang/Object"),
interfaces: vec![],
interfaces: vec!["java/io/Serializable"],
methods: vec![
JavaMethodProto::new("<init>", "(Ljava/lang/String;)V", Self::init_with_spec, Default::default()),
JavaMethodProto::new(
Expand Down
2 changes: 1 addition & 1 deletion java_runtime/src/classes/java/util/array_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ impl ArrayList {
RuntimeClassProto {
name: "java/util/ArrayList",
parent_class: Some("java/util/AbstractList"),
interfaces: vec![],
interfaces: vec!["java/lang/Cloneable", "java/io/Serializable"],
methods: vec![
JavaMethodProto::new("<init>", "()V", Self::init, Default::default()),
JavaMethodProto::new("<init>", "(I)V", Self::init_with_capacity, Default::default()),
Expand Down
2 changes: 1 addition & 1 deletion java_runtime/src/classes/java/util/calendar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ impl Calendar {
RuntimeClassProto {
name: "java/util/Calendar",
parent_class: Some("java/lang/Object"),
interfaces: vec![],
interfaces: vec!["java/io/Serializable", "java/lang/Cloneable"],
methods: vec![
JavaMethodProto::new("<init>", "()V", Self::init, Default::default()),
JavaMethodProto::new("getInstance", "()Ljava/util/Calendar;", Self::get_instance, MethodAccessFlags::STATIC),
Expand Down
2 changes: 1 addition & 1 deletion java_runtime/src/classes/java/util/hash_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ impl HashMap {
RuntimeClassProto {
name: "java/util/HashMap",
parent_class: Some("java/util/AbstractMap"),
interfaces: vec![],
interfaces: vec!["java/lang/Cloneable", "java/io/Serializable"],
methods: vec![
JavaMethodProto::new("<init>", "()V", Self::init, MethodAccessFlags::PUBLIC),
JavaMethodProto::new("<init>", "(I)V", Self::init_with_capacity, MethodAccessFlags::PUBLIC),
Expand Down
2 changes: 1 addition & 1 deletion java_runtime/src/classes/java/util/hash_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ impl HashSet {
RuntimeClassProto {
name: "java/util/HashSet",
parent_class: Some("java/util/AbstractSet"),
interfaces: vec![],
interfaces: vec!["java/lang/Cloneable", "java/io/Serializable"],
methods: vec![
JavaMethodProto::new("<init>", "()V", Self::init, Default::default()),
JavaMethodProto::new("<init>", "(I)V", Self::init_with_capacity, Default::default()),
Expand Down
2 changes: 1 addition & 1 deletion java_runtime/src/classes/java/util/hashtable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ impl Hashtable {
RuntimeClassProto {
name: "java/util/Hashtable",
parent_class: Some("java/util/Dictionary"),
interfaces: vec!["java/util/Map"],
interfaces: vec!["java/util/Map", "java/lang/Cloneable", "java/io/Serializable"],
methods: vec![
JavaMethodProto::new("<init>", "()V", Self::init, Default::default()),
JavaMethodProto::new("<init>", "(I)V", Self::init_with_capacity, Default::default()),
Expand Down
2 changes: 1 addition & 1 deletion java_runtime/src/classes/java/util/jar/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ impl Attributes {
RuntimeClassProto {
name: "java/util/jar/Attributes",
parent_class: Some("java/lang/Object"),
interfaces: vec![],
interfaces: vec!["java/lang/Cloneable"],
methods: vec![
JavaMethodProto::new("<init>", "()V", Self::init, Default::default()),
JavaMethodProto::new(
Expand Down
2 changes: 1 addition & 1 deletion java_runtime/src/classes/java/util/jar/manifest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ impl Manifest {
RuntimeClassProto {
name: "java/util/jar/Manifest",
parent_class: Some("java/lang/Object"),
interfaces: vec![],
interfaces: vec!["java/lang/Cloneable"],
methods: vec![
JavaMethodProto::new("<init>", "(Ljava/io/InputStream;)V", Self::init, Default::default()),
JavaMethodProto::new("read", "(Ljava/io/InputStream;)V", Self::read, Default::default()),
Expand Down
2 changes: 1 addition & 1 deletion java_runtime/src/classes/java/util/logging/level.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ impl Level {
RuntimeClassProto {
name: "java/util/logging/Level",
parent_class: Some("java/lang/Object"),
interfaces: vec![],
interfaces: vec!["java/io/Serializable"],
methods: vec![
JavaMethodProto::new("<clinit>", "()V", Self::clinit, MethodAccessFlags::STATIC),
JavaMethodProto::new("<init>", "(Ljava/lang/String;I)V", Self::init, MethodAccessFlags::PROTECTED),
Expand Down
2 changes: 1 addition & 1 deletion java_runtime/src/classes/java/util/logging/log_record.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ impl LogRecord {
RuntimeClassProto {
name: "java/util/logging/LogRecord",
parent_class: Some("java/lang/Object"),
interfaces: vec![],
interfaces: vec!["java/io/Serializable"],
methods: vec![
JavaMethodProto::new(
"<init>",
Expand Down
2 changes: 1 addition & 1 deletion java_runtime/src/classes/java/util/vector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ impl Vector {
RuntimeClassProto {
name: "java/util/Vector",
parent_class: Some("java/util/AbstractList"),
interfaces: vec!["java/util/List"],
interfaces: vec!["java/util/List", "java/lang/Cloneable", "java/io/Serializable"],
methods: vec![
JavaMethodProto::new("<init>", "()V", Self::init, Default::default()),
JavaMethodProto::new("<init>", "(I)V", Self::init_with_capacity, Default::default()),
Expand Down
2 changes: 1 addition & 1 deletion java_runtime/src/classes/java/util/zip/zip_entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ impl ZipEntry {
RuntimeClassProto {
name: "java/util/zip/ZipEntry",
parent_class: Some("java/lang/Object"),
interfaces: vec![],
interfaces: vec!["java/lang/Cloneable"],
Comment thread
dlunch marked this conversation as resolved.
methods: vec![
JavaMethodProto::new("<init>", "(Ljava/lang/String;)V", Self::init, Default::default()),
JavaMethodProto::new("<init>", "(Ljava/util/zip/ZipEntry;)V", Self::init_with_zip_entry, Default::default()),
Expand Down
1 change: 1 addition & 0 deletions java_runtime/tests/classes/java/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
mod io;
mod lang;
mod net;
mod test_marker_interfaces;
mod text;
mod util;
105 changes: 105 additions & 0 deletions java_runtime/tests/classes/java/test_marker_interfaces.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
use java_runtime::classes::java::lang::Class;
use jvm::{Array, ClassInstanceRef, Result, runtime::JavaLangClass};
use test_utils::test_jvm;

#[tokio::test]
async fn marker_interfaces_match_public_class_declarations() -> Result<()> {
let jvm = test_jvm().await?;

let direct_markers: [(&str, &[&str]); 15] = [
("java/lang/Throwable", &["java/io/Serializable"]),
("java/lang/Class", &["java/io/Serializable"]),
("java/io/File", &["java/io/Serializable"]),
("java/net/URL", &["java/io/Serializable"]),
("java/util/Calendar", &["java/io/Serializable", "java/lang/Cloneable"]),
("java/util/ArrayList", &["java/lang/Cloneable", "java/io/Serializable"]),
("java/util/HashMap", &["java/lang/Cloneable", "java/io/Serializable"]),
("java/util/HashSet", &["java/lang/Cloneable", "java/io/Serializable"]),
("java/util/Hashtable", &["java/lang/Cloneable", "java/io/Serializable"]),
("java/util/Vector", &["java/lang/Cloneable", "java/io/Serializable"]),
("java/util/jar/Attributes", &["java/lang/Cloneable"]),
("java/util/jar/Manifest", &["java/lang/Cloneable"]),
("java/util/logging/Level", &["java/io/Serializable"]),
("java/util/logging/LogRecord", &["java/io/Serializable"]),
("java/util/zip/ZipEntry", &["java/lang/Cloneable"]),
];
Comment thread
dlunch marked this conversation as resolved.
Outdated

for (class_name, expected_markers) in direct_markers {
let class = jvm.resolve_class(class_name).await?.java_class();
let interfaces: ClassInstanceRef<Array<Class>> = jvm.invoke_virtual(&class, "getInterfaces", "()[Ljava/lang/Class;", ()).await?;
let interfaces: Vec<ClassInstanceRef<Class>> = jvm.load_array(&interfaces, 0, jvm.array_length(&interfaces).await?).await?;
let mut actual_markers = Vec::new();
for interface in interfaces {
let interface_name = JavaLangClass::name(&jvm, &interface).await?;
if interface_name == "java/lang/Cloneable" || interface_name == "java/io/Serializable" {
actual_markers.push(interface_name);
}
}
assert_eq!(
actual_markers.iter().map(String::as_str).collect::<Vec<_>>(),
expected_markers,
"incorrect direct marker interfaces for {class_name}"
);

for marker_name in expected_markers {
let marker = jvm.resolve_class(marker_name).await?.java_class();
assert!(
jvm.invoke_virtual::<_, bool>(&marker, "isAssignableFrom", "(Ljava/lang/Class;)Z", (class.clone(),))
.await?,
"{class_name} must implement {marker_name}"
);
}
}

let inherited_markers: [(&str, &[&str]); 6] = [
("java/lang/RuntimeException", &["java/io/Serializable"]),
("java/util/GregorianCalendar", &["java/io/Serializable", "java/lang/Cloneable"]),
("java/util/Properties", &["java/lang/Cloneable", "java/io/Serializable"]),
("java/util/Stack", &["java/lang/Cloneable", "java/io/Serializable"]),
("java/util/LinkedHashMap", &["java/lang/Cloneable", "java/io/Serializable"]),
("java/util/jar/JarEntry", &["java/lang/Cloneable"]),
];

for (class_name, expected_markers) in inherited_markers {
let class = jvm.resolve_class(class_name).await?.java_class();
let interfaces: ClassInstanceRef<Array<Class>> = jvm.invoke_virtual(&class, "getInterfaces", "()[Ljava/lang/Class;", ()).await?;
let interfaces: Vec<ClassInstanceRef<Class>> = jvm.load_array(&interfaces, 0, jvm.array_length(&interfaces).await?).await?;
for interface in interfaces {
let interface_name = JavaLangClass::name(&jvm, &interface).await?;
assert!(
interface_name != "java/lang/Cloneable" && interface_name != "java/io/Serializable",
"{class_name} must inherit {interface_name}, not declare it directly"
);
}

for marker_name in expected_markers {
let marker = jvm.resolve_class(marker_name).await?.java_class();
assert!(
jvm.invoke_virtual::<_, bool>(&marker, "isAssignableFrom", "(Ljava/lang/Class;)Z", (class.clone(),))
.await?,
"{class_name} must inherit {marker_name}"
);
}
}

for class_name in ["java/text/FieldPosition", "java/text/ParsePosition"] {
let class = jvm.resolve_class(class_name).await?.java_class();
let interfaces: ClassInstanceRef<Array<Class>> = jvm.invoke_virtual(&class, "getInterfaces", "()[Ljava/lang/Class;", ()).await?;
let interfaces: Vec<ClassInstanceRef<Class>> = jvm.load_array(&interfaces, 0, jvm.array_length(&interfaces).await?).await?;
for interface in interfaces {
let interface_name = JavaLangClass::name(&jvm, &interface).await?;
assert!(interface_name != "java/lang/Cloneable" && interface_name != "java/io/Serializable");
}

for marker_name in ["java/lang/Cloneable", "java/io/Serializable"] {
let marker = jvm.resolve_class(marker_name).await?.java_class();
assert!(
!jvm.invoke_virtual::<_, bool>(&marker, "isAssignableFrom", "(Ljava/lang/Class;)Z", (class.clone(),))
.await?,
"{class_name} must not implement {marker_name}"
);
}
}

Ok(())
}
9 changes: 8 additions & 1 deletion jvm/src/jvm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,14 @@ impl Jvm {
};

// load bootstrap classes
let bootstrap_classes = ["java/lang/Object", "java/lang/Runnable", "java/lang/Thread", "[B", "java/lang/Class"];
let bootstrap_classes = [
"java/lang/Object",
"java/lang/Runnable",
"java/lang/Thread",
"[B",
"java/io/Serializable",
"java/lang/Class",
];
for class_name in bootstrap_classes.iter() {
let class_definition = jvm.inner.bootstrap_class_loader.load_class(&jvm, class_name).await?.unwrap();
let class = Class::new(class_definition, None);
Expand Down