Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
3 changes: 3 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,6 @@
- Keep `java_runtime/tests/classes` limited to Java standard library class and API behavior.
- Test JVM and interpreter semantics, including class initialization, bytecode execution, and monitor behavior, with compiled Java fixtures under `test_data/src` and expected output under `test_data`, executed by `tests/test_class.rs`.
- Do not place JVM core behavior tests in the `java_runtime` standard library test tree.

## Compatibility Sources
- Implement Java compatibility from public specifications, Javadocs, and observable behavior tests. Do not consult or reproduce OpenJDK or other Java runtime implementation source code; keep the implementation independent to avoid licensing and provenance concerns.
12 changes: 12 additions & 0 deletions java_runtime/src/classes/java/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ mod illegal_format_flags_exception;
mod illegal_format_precision_exception;
mod illegal_format_width_exception;
mod iterator;
mod linked_hash_map;
mod linked_hash_map_entry;
mod linked_hash_map_entry_iterator;
mod linked_hash_map_key_iterator;
mod linked_hash_map_linked_hash_iterator;
mod linked_hash_map_value_iterator;
mod linked_list;
mod linked_list_entry;
mod linked_list_itr;
Expand Down Expand Up @@ -171,6 +177,12 @@ pub use self::{
illegal_format_precision_exception::IllegalFormatPrecisionException,
illegal_format_width_exception::IllegalFormatWidthException,
iterator::Iterator,
linked_hash_map::LinkedHashMap,
linked_hash_map_entry::LinkedHashMapEntry,
linked_hash_map_entry_iterator::LinkedHashMapEntryIterator,
linked_hash_map_key_iterator::LinkedHashMapKeyIterator,
linked_hash_map_linked_hash_iterator::LinkedHashMapLinkedHashIterator,
linked_hash_map_value_iterator::LinkedHashMapValueIterator,
linked_list::LinkedList,
linked_list_entry::LinkedListEntry,
linked_list_itr::LinkedListItr,
Expand Down
258 changes: 206 additions & 52 deletions java_runtime/src/classes/java/util/hash_map.rs

Large diffs are not rendered by default.

23 changes: 20 additions & 3 deletions java_runtime/src/classes/java/util/hash_map_entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ use jvm::{ClassInstanceRef, Jvm, Result};

use crate::{RuntimeClassProto, RuntimeContext, classes::java::lang::Object};

use super::HashMap;

// class java.util.HashMap$Entry
pub struct HashMapEntry;

Expand All @@ -22,11 +24,18 @@ impl HashMapEntry {
Self::init,
Default::default(),
),
JavaMethodProto::new("getKey", "()Ljava/lang/Object;", Self::get_key, Default::default()),
JavaMethodProto::new("getValue", "()Ljava/lang/Object;", Self::get_value, Default::default()),
JavaMethodProto::new("setValue", "(Ljava/lang/Object;)Ljava/lang/Object;", Self::set_value, Default::default()),
JavaMethodProto::new("getKey", "()Ljava/lang/Object;", Self::get_key, MethodAccessFlags::PUBLIC),
JavaMethodProto::new("getValue", "()Ljava/lang/Object;", Self::get_value, MethodAccessFlags::PUBLIC),
JavaMethodProto::new(
"setValue",
"(Ljava/lang/Object;)Ljava/lang/Object;",
Self::set_value,
MethodAccessFlags::PUBLIC,
),
JavaMethodProto::new("equals", "(Ljava/lang/Object;)Z", Self::equals, MethodAccessFlags::PUBLIC),
JavaMethodProto::new("hashCode", "()I", Self::hash_code, MethodAccessFlags::PUBLIC),
JavaMethodProto::new("onAccess", "(Ljava/util/HashMap;)V", Self::on_access, Default::default()),
JavaMethodProto::new("onRemoval", "(Ljava/util/HashMap;)V", Self::on_removal, Default::default()),
],
fields: vec![
JavaFieldProto::new("hash", "I", Default::default()),
Expand Down Expand Up @@ -125,4 +134,12 @@ impl HashMapEntry {
};
Ok(key_hash ^ value_hash)
}

async fn on_access(_: &Jvm, _: &mut RuntimeContext, _: ClassInstanceRef<Self>, _: ClassInstanceRef<HashMap>) -> Result<()> {
Ok(())
}

async fn on_removal(_: &Jvm, _: &mut RuntimeContext, _: ClassInstanceRef<Self>, _: ClassInstanceRef<HashMap>) -> Result<()> {
Ok(())
}
}
6 changes: 1 addition & 5 deletions java_runtime/src/classes/java/util/hash_map_entry_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,12 +126,8 @@ impl HashMapEntrySet {
tracing::debug!("java.util.HashMap$EntrySet::iterator({this:?})");

let map: ClassInstanceRef<HashMap> = jvm.get_field(&this, "map", "Ljava/util/HashMap;").await?;
let snapshot = HashMap::entries_snapshot(jvm, &map).await?;
let iterator = jvm
.new_class("java/util/HashMap$EntryIterator", "([Ljava/lang/Object;)V", (snapshot,))
.await?;

Ok(iterator.into())
jvm.invoke_virtual(&map, "entryIterator", "()Ljava/util/Iterator;", ()).await
}

async fn object_equals(jvm: &Jvm, left: &ClassInstanceRef<Object>, right: &ClassInstanceRef<Object>) -> Result<bool> {
Expand Down
6 changes: 1 addition & 5 deletions java_runtime/src/classes/java/util/hash_map_key_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,7 @@ impl HashMapKeySet {
tracing::debug!("java.util.HashMap$KeySet::iterator({this:?})");

let map: ClassInstanceRef<HashMap> = jvm.get_field(&this, "map", "Ljava/util/HashMap;").await?;
let snapshot = HashMap::keys_snapshot(jvm, &map).await?;
let iterator = jvm
.new_class("java/util/HashMap$KeyIterator", "([Ljava/lang/Object;)V", (snapshot,))
.await?;

Ok(iterator.into())
jvm.invoke_virtual(&map, "keyIterator", "()Ljava/util/Iterator;", ()).await
}
}
12 changes: 4 additions & 8 deletions java_runtime/src/classes/java/util/hash_map_values.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,9 @@ impl HashMapValues {
tracing::debug!("java.util.HashMap$Values::remove({this:?}, {value:?})");

let map: ClassInstanceRef<HashMap> = jvm.get_field(&this, "map", "Ljava/util/HashMap;").await?;
let entries = HashMap::entries_snapshot(jvm, &map).await?;
let count = jvm.array_length(&entries).await?;
for entry in jvm.load_array::<ClassInstanceRef<Object>>(&entries, 0, count).await? {
let iterator: ClassInstanceRef<Object> = jvm.invoke_virtual(&map, "entryIterator", "()Ljava/util/Iterator;", ()).await?;
while jvm.invoke_virtual::<_, bool>(&iterator, "hasNext", "()Z", ()).await? {
let entry: ClassInstanceRef<Object> = jvm.invoke_virtual(&iterator, "next", "()Ljava/lang/Object;", ()).await?;
let entry_value: ClassInstanceRef<Object> = jvm.invoke_virtual(&entry, "getValue", "()Ljava/lang/Object;", ()).await?;
let equal = if value.is_null() {
entry_value.is_null()
Expand Down Expand Up @@ -103,11 +103,7 @@ impl HashMapValues {
tracing::debug!("java.util.HashMap$Values::iterator({this:?})");

let map: ClassInstanceRef<HashMap> = jvm.get_field(&this, "map", "Ljava/util/HashMap;").await?;
let snapshot = HashMap::values_snapshot(jvm, &map).await?;
let iterator = jvm
.new_class("java/util/HashMap$ValueIterator", "([Ljava/lang/Object;)V", (snapshot,))
.await?;

Ok(iterator.into())
jvm.invoke_virtual(&map, "valueIterator", "()Ljava/util/Iterator;", ()).await
}
}
Loading