Skip to content

Commit 54fec16

Browse files
committed
Collect class instances to be used in gc
1 parent a72a641 commit 54fec16

File tree

9 files changed

+112
-26
lines changed

9 files changed

+112
-26
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ async-trait = { version = "^0.1", default-features = false }
1313
bytemuck = { version = "^1.20", default-features = false, features = ["extern_crate_alloc"] }
1414
dyn-clone = { version = "^1.0", default-features = false }
1515
dyn-hash = { version = "^0.2", default-features = false }
16+
hashbrown = { version = "^0.15", features = ["default-hasher"], default-features = false }
1617
nom = { version = "^7.1", default-features = false, features = ["alloc"] }
1718
parking_lot = { version = "^0.12", default-features = false }
1819
tracing = { version = "^0.1", default-features = false, features = ["attributes"] }

java_runtime/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,14 @@ license.workspace = true
88
async-trait = { workspace = true }
99
bytemuck = { workspace = true }
1010
dyn-clone = { workspace = true }
11+
hashbrown = { workspace = true }
1112
parking_lot = { workspace = true }
1213
tracing = { workspace = true }
1314

1415
async-lock = { version = "^3.4", default-features = false }
1516
chrono = { version = "^0.4", default-features = false }
1617
encoding_rs = { version = "^0.8", features = ["alloc"], default-features = false }
1718
event-listener = { version = "^5.3", default-features = false }
18-
hashbrown = { version = "^0.15", features = ["default-hasher"], default-features = false }
1919
zip = { version = "^2.2", features = ["deflate"], default-features = false }
2020
url = { version = "^2.5", default-features = false }
2121

jvm/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ async-trait = { workspace = true }
1111
bytemuck = { workspace = true }
1212
dyn-clone = { workspace = true }
1313
dyn-hash = { workspace = true }
14+
hashbrown = { workspace = true }
1415
nom = { workspace = true }
1516
parking_lot = { workspace = true }
1617
tracing = { workspace = true }

jvm/src/class_instance.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,13 @@ pub trait ClassInstance: Sync + Send + AsAny + Debug + DynHash + DynClone + 'sta
2828
clone_trait_object!(ClassInstance);
2929
hash_trait_object!(ClassInstance);
3030

31+
impl Eq for dyn ClassInstance {}
32+
impl PartialEq for dyn ClassInstance {
33+
fn eq(&self, other: &Self) -> bool {
34+
self.equals(other).unwrap()
35+
}
36+
}
37+
3138
// array wrapper for ClassInstanceRef
3239
pub struct Array<T>(PhantomData<T>);
3340

jvm/src/jvm.rs

Lines changed: 45 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use core::{
1010

1111
use bytemuck::cast_slice;
1212
use dyn_clone::clone_box;
13+
use hashbrown::HashSet;
1314
use parking_lot::RwLock;
1415

1516
use java_constants::MethodAccessFlags;
@@ -33,6 +34,7 @@ use crate::{
3334
struct JvmInner {
3435
classes: RwLock<BTreeMap<String, Class>>,
3536
threads: RwLock<BTreeMap<u64, JvmThread>>,
37+
all_objects: RwLock<HashSet<Box<dyn ClassInstance>>>,
3638
get_current_thread_id: Box<dyn Fn() -> u64 + Sync + Send>,
3739
bootstrap_class_loader: Box<dyn BootstrapClassLoader>,
3840
bootstrapping: AtomicBool,
@@ -53,6 +55,7 @@ impl Jvm {
5355
inner: Arc::new(JvmInner {
5456
classes: RwLock::new(BTreeMap::new()),
5557
threads: RwLock::new(BTreeMap::new()),
58+
all_objects: RwLock::new(HashSet::new()),
5659
get_current_thread_id: Box::new(get_current_thread_id),
5760
bootstrap_class_loader: Box::new(bootstrap_class_loader),
5861
bootstrapping: AtomicBool::new(true),
@@ -108,6 +111,13 @@ impl Jvm {
108111

109112
let instance = class.definition.instantiate()?;
110113

114+
let thread_id = (self.inner.get_current_thread_id)();
115+
let mut threads = self.inner.threads.write();
116+
let thread = threads.get_mut(&thread_id).unwrap();
117+
118+
thread.top_frame_mut().local_variables().push(instance.clone());
119+
self.inner.all_objects.write().insert(instance.clone());
120+
111121
Ok(instance)
112122
}
113123

@@ -131,6 +141,14 @@ impl Jvm {
131141
let array_class = class.as_array_class_definition().unwrap();
132142

133143
let instance = array_class.instantiate_array(length)?;
144+
145+
let thread_id = (self.inner.get_current_thread_id)();
146+
let mut threads = self.inner.threads.write();
147+
let thread = threads.get_mut(&thread_id).unwrap();
148+
149+
thread.top_frame_mut().local_variables().push(instance.clone());
150+
self.inner.all_objects.write().insert(instance.clone());
151+
134152
Ok(instance)
135153
}
136154

@@ -423,10 +441,10 @@ impl Jvm {
423441
}
424442
}
425443

426-
// temporary until we have working gc
427444
pub fn destroy(&self, instance: Box<dyn ClassInstance>) -> Result<()> {
428445
tracing::debug!("Destroy {}", instance.class_definition().name());
429446

447+
self.inner.all_objects.write().remove(&instance);
430448
instance.destroy();
431449

432450
Ok(())
@@ -496,7 +514,7 @@ impl Jvm {
496514
let threads = self.inner.threads.read();
497515
let thread = threads.get(&thread_id).unwrap();
498516

499-
Ok(thread.top_frame().map(|x| (x.class.clone(), x.class_instance.clone())))
517+
Ok(thread.top_java_frame().map(|x| (x.class.clone(), x.class_instance.clone())))
500518
}
501519

502520
pub async fn register_class(
@@ -553,18 +571,18 @@ impl Jvm {
553571
let threads = self.inner.threads.read();
554572
let thread = threads.get(&thread_id).unwrap();
555573

556-
let mut result = Vec::with_capacity(thread.stack.len());
557-
558-
for item in thread.stack.iter().rev() {
559-
// skip exception classes
560-
if self.is_inherited_from(&*item.class.definition, "java/lang/Throwable") {
561-
continue;
562-
}
563-
564-
result.push(format!("{}.{}", item.class.definition.name(), item.method));
565-
}
566-
567-
result
574+
thread
575+
.iter_java_frame()
576+
.rev()
577+
.filter_map(|x| {
578+
// skip exception classes
579+
if self.is_inherited_from(&*x.class.definition, "java/lang/Throwable") {
580+
None
581+
} else {
582+
Some(format!("{}.{}", x.class.definition.name(), x.method))
583+
}
584+
})
585+
.collect()
568586
}
569587

570588
async fn register_class_internal(&self, class: Class, class_loader_wrapper: Option<&dyn ClassLoaderWrapper>) -> Result<()> {
@@ -624,6 +642,7 @@ impl Jvm {
624642
pub fn attach_thread(&self) -> Result<()> {
625643
let thread_id = (self.inner.get_current_thread_id)();
626644
self.inner.threads.write().insert(thread_id, JvmThread::new());
645+
self.push_native_frame();
627646

628647
Ok(())
629648
}
@@ -635,6 +654,17 @@ impl Jvm {
635654
Ok(())
636655
}
637656

657+
// TODO we need safe, ergonomic api..
658+
pub fn push_native_frame(&self) {
659+
let thread_id = (self.inner.get_current_thread_id)();
660+
self.inner.threads.write().get_mut(&thread_id).unwrap().push_native_frame();
661+
}
662+
663+
pub fn pop_frame(&self) {
664+
let thread_id = (self.inner.get_current_thread_id)();
665+
self.inner.threads.write().get_mut(&thread_id).unwrap().pop_frame();
666+
}
667+
638668
pub async fn current_class_loader(&self) -> Result<Box<dyn ClassInstance>> {
639669
let calling_class = self.find_calling_class()?;
640670

@@ -703,7 +733,7 @@ impl Jvm {
703733
.write()
704734
.get_mut(&thread_id)
705735
.unwrap()
706-
.push_frame(class, class_instance, &method_str);
736+
.push_java_frame(class, class_instance, &method_str);
707737

708738
let result = method.run(self, args).await;
709739

jvm/src/thread.rs

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,34 +6,72 @@ use alloc::{
66

77
use crate::{class_loader::Class, ClassInstance};
88

9+
pub enum StackFrame {
10+
Java(JavaStackFrame),
11+
Native(NativeStackFrame),
12+
}
13+
14+
impl StackFrame {
15+
pub fn local_variables(&mut self) -> &mut Vec<Box<dyn ClassInstance>> {
16+
match self {
17+
StackFrame::Java(java_frame) => &mut java_frame.local_variables,
18+
StackFrame::Native(native_frame) => &mut native_frame.local_variables,
19+
}
20+
}
21+
}
22+
923
pub struct JvmThread {
10-
pub stack: Vec<JvmStackFrame>,
24+
stack: Vec<StackFrame>,
1125
}
1226

1327
impl JvmThread {
1428
pub fn new() -> Self {
1529
Self { stack: Vec::new() }
1630
}
1731

18-
pub fn push_frame(&mut self, class: &Class, class_instance: Option<Box<dyn ClassInstance>>, method: &str) {
19-
self.stack.push(JvmStackFrame {
32+
pub fn push_java_frame(&mut self, class: &Class, class_instance: Option<Box<dyn ClassInstance>>, method: &str) {
33+
self.stack.push(StackFrame::Java(JavaStackFrame {
2034
class: class.clone(),
2135
class_instance,
2236
method: method.to_string(),
23-
});
37+
local_variables: Vec::new(),
38+
}));
39+
}
40+
41+
pub fn push_native_frame(&mut self) {
42+
self.stack.push(StackFrame::Native(NativeStackFrame { local_variables: Vec::new() }));
2443
}
2544

26-
pub fn pop_frame(&mut self) -> Option<JvmStackFrame> {
45+
pub fn pop_frame(&mut self) -> Option<StackFrame> {
2746
self.stack.pop()
2847
}
2948

30-
pub fn top_frame(&self) -> Option<&JvmStackFrame> {
31-
self.stack.last()
49+
pub fn top_frame_mut(&mut self) -> &mut StackFrame {
50+
self.stack.last_mut().unwrap()
51+
}
52+
53+
pub fn top_java_frame(&self) -> Option<&JavaStackFrame> {
54+
self.stack.iter().rev().find_map(|frame| match frame {
55+
StackFrame::Java(java_frame) => Some(java_frame),
56+
_ => None,
57+
})
58+
}
59+
60+
pub fn iter_java_frame(&self) -> impl DoubleEndedIterator<Item = &JavaStackFrame> {
61+
self.stack.iter().filter_map(|frame| match frame {
62+
StackFrame::Java(java_frame) => Some(java_frame),
63+
_ => None,
64+
})
3265
}
3366
}
3467

35-
pub struct JvmStackFrame {
68+
pub struct JavaStackFrame {
3669
pub class: Class,
3770
pub class_instance: Option<Box<dyn ClassInstance>>,
3871
pub method: String,
72+
pub local_variables: Vec<Box<dyn ClassInstance>>,
73+
}
74+
75+
pub struct NativeStackFrame {
76+
pub local_variables: Vec<Box<dyn ClassInstance>>,
3977
}

jvm_rust/src/array_class_instance.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,11 @@ impl ArrayClassInstance for ArrayClassInstanceImpl {
119119
fn destroy(self: Box<Self>) {}
120120

121121
fn equals(&self, other: &dyn ClassInstance) -> Result<bool> {
122-
let other = other.as_any().downcast_ref::<ArrayClassInstanceImpl>().unwrap();
122+
let other = other.as_any().downcast_ref::<ArrayClassInstanceImpl>();
123+
if other.is_none() {
124+
return Ok(false);
125+
}
126+
let other = other.unwrap();
123127

124128
Ok(Arc::ptr_eq(&self.inner, &other.inner))
125129
}

jvm_rust/src/class_instance.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,11 @@ impl ClassInstance for ClassInstanceImpl {
4040
}
4141

4242
fn equals(&self, other: &dyn ClassInstance) -> Result<bool> {
43-
let other = other.as_any().downcast_ref::<ClassInstanceImpl>().unwrap();
43+
let other = other.as_any().downcast_ref::<ClassInstanceImpl>();
44+
if other.is_none() {
45+
return Ok(false);
46+
}
47+
let other = other.unwrap();
4448

4549
Ok(Arc::ptr_eq(&self.inner, &other.inner))
4650
}

0 commit comments

Comments
 (0)