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
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