diff --git a/classfile/src/attribute.rs b/classfile/src/attribute.rs index 9dbf6ea2..645abe01 100644 --- a/classfile/src/attribute.rs +++ b/classfile/src/attribute.rs @@ -146,6 +146,7 @@ pub enum AttributeInfo { MethodParameters(Vec), // TODO NestMembers(Vec), // TODO NestHost(Vec), // TODO + Unknown(Arc, Vec), } impl AttributeInfo { @@ -170,7 +171,8 @@ impl AttributeInfo { "MethodParameters" => AttributeInfo::MethodParameters(info.to_vec()), "NestMembers" => AttributeInfo::NestMembers(info.to_vec()), "NestHost" => AttributeInfo::NestHost(info.to_vec()), - _ => return Err(nom::Err::Error(nom::error_position!(info, nom::error::ErrorKind::Switch))), + // unrecognized attributes must be silently ignored (JVMS 4.7.1) + _ => AttributeInfo::Unknown(name.clone(), info.to_vec()), }) }, ) diff --git a/java_runtime/src/classes/java/lang/thread.rs b/java_runtime/src/classes/java/lang/thread.rs index d40447d0..aa8ab1f0 100644 --- a/java_runtime/src/classes/java/lang/thread.rs +++ b/java_runtime/src/classes/java/lang/thread.rs @@ -91,7 +91,7 @@ impl Thread { async fn call(&self) -> Result<()> { tracing::trace!("Thread start"); - self.jvm.attach_thread()?; + self.jvm.attach_thread(self.this.instance.clone()).await?; let result: Result<()> = self.jvm.invoke_virtual(&self.this, "run", "()V", []).await; @@ -202,10 +202,8 @@ impl Thread { } async fn current_thread(jvm: &Jvm, _: &mut RuntimeContext) -> Result> { - tracing::warn!("stub java.lang.Thread::currentThread()"); + tracing::debug!("java.lang.Thread::currentThread()"); - let thread = jvm.new_class("java/lang/Thread", "(Z)V", (true,)).await?; - - Ok(thread.into()) + Ok(jvm.current_java_thread().into()) } } diff --git a/java_runtime/tests/classes/java/lang/test_object.rs b/java_runtime/tests/classes/java/lang/test_object.rs index ba0b4a85..ef3c7f33 100644 --- a/java_runtime/tests/classes/java/lang/test_object.rs +++ b/java_runtime/tests/classes/java/lang/test_object.rs @@ -30,7 +30,7 @@ async fn test_wait() -> Result<()> { #[async_trait::async_trait] impl SpawnCallback for Notifier { async fn call(&self) -> Result<()> { - self.jvm.attach_thread()?; + self.jvm.attach_thread(None).await?; self.runtime.sleep(Duration::from_millis(100)).await; self.notified.store(true, Ordering::Relaxed); @@ -77,7 +77,7 @@ async fn test_wait_timeout() -> Result<()> { #[async_trait::async_trait] impl SpawnCallback for Notifier { async fn call(&self) -> Result<()> { - self.jvm.attach_thread()?; + self.jvm.attach_thread(None).await?; self.runtime.sleep(Duration::from_millis(1000)).await; self.notified.store(true, Ordering::Relaxed); diff --git a/jvm/src/garbage_collector.rs b/jvm/src/garbage_collector.rs index fb1dec92..7cc76493 100644 --- a/jvm/src/garbage_collector.rs +++ b/jvm/src/garbage_collector.rs @@ -26,6 +26,10 @@ pub fn determine_garbage( find_reachable_objects(jvm, x, &mut reachable_objects); }); + threads.values().filter_map(|thread| thread.java_thread()).for_each(|x| { + find_reachable_objects(jvm, x, &mut reachable_objects); + }); + interned_strings.iter().for_each(|x| { find_reachable_objects(jvm, x, &mut reachable_objects); }); diff --git a/jvm/src/jvm.rs b/jvm/src/jvm.rs index 41d965d9..8a777f09 100644 --- a/jvm/src/jvm.rs +++ b/jvm/src/jvm.rs @@ -79,7 +79,7 @@ impl Jvm { } // init startup thread - jvm.attach_thread()?; + jvm.attach_thread(None).await?; // set java class for bootstrap classes let classes = jvm.inner.classes.read().values().cloned().collect::>(); @@ -822,11 +822,19 @@ impl Jvm { Ok(()) } - pub fn attach_thread(&self) -> Result<()> { + // every attached thread owns a java/lang/Thread instance; pass the instance for threads + // started from java (Thread.start), or None to create one + pub async fn attach_thread(&self, java_thread: Option>) -> Result<()> { let thread_id = (self.inner.get_current_thread_id)(); self.inner.threads.write().insert(thread_id, JvmThread::new()); self.push_native_frame(); + let java_thread = match java_thread { + Some(x) => x, + None => self.new_class("java/lang/Thread", "(Z)V", (true,)).await?, + }; + self.inner.threads.write().get_mut(&thread_id).unwrap().set_java_thread(java_thread); + Ok(()) } @@ -837,6 +845,11 @@ impl Jvm { Ok(()) } + pub fn current_java_thread(&self) -> Box { + let thread_id = (self.inner.get_current_thread_id)(); + self.inner.threads.read().get(&thread_id).unwrap().java_thread().unwrap().clone() + } + // TODO we need safe, ergonomic api.. pub fn push_native_frame(&self) { let thread_id = (self.inner.get_current_thread_id)(); diff --git a/jvm/src/thread.rs b/jvm/src/thread.rs index e335eeab..77e76d53 100644 --- a/jvm/src/thread.rs +++ b/jvm/src/thread.rs @@ -29,11 +29,24 @@ impl StackFrame { pub struct JvmThread { stack: Vec, + java_thread: Option>, } impl JvmThread { pub fn new() -> Self { - Self { stack: Vec::new() } + Self { + stack: Vec::new(), + java_thread: None, + } + } + + #[allow(clippy::borrowed_box)] // same as jvm.rs; callers pass it to &Box-taking apis + pub fn java_thread(&self) -> Option<&Box> { + self.java_thread.as_ref() + } + + pub fn set_java_thread(&mut self, java_thread: Box) { + self.java_thread = Some(java_thread); } pub fn push_java_frame(&mut self, class: &Class, class_instance: Option>, method: &str) { diff --git a/test_data/CurrentThread$1.class b/test_data/CurrentThread$1.class new file mode 100644 index 00000000..a0170410 Binary files /dev/null and b/test_data/CurrentThread$1.class differ diff --git a/test_data/CurrentThread.class b/test_data/CurrentThread.class new file mode 100644 index 00000000..cafa82c1 Binary files /dev/null and b/test_data/CurrentThread.class differ diff --git a/test_data/CurrentThread.txt b/test_data/CurrentThread.txt new file mode 100644 index 00000000..9e8a46ac --- /dev/null +++ b/test_data/CurrentThread.txt @@ -0,0 +1,3 @@ +true +true +false diff --git a/test_data/src/CurrentThread.java b/test_data/src/CurrentThread.java new file mode 100644 index 00000000..c32b02a0 --- /dev/null +++ b/test_data/src/CurrentThread.java @@ -0,0 +1,18 @@ +public class CurrentThread { + public static void main(String[] args) throws Exception { + Thread a = Thread.currentThread(); + Thread b = Thread.currentThread(); + System.out.println(a == b); + + final Thread[] seen = new Thread[1]; + Thread t = new Thread(new Runnable() { + public void run() { + seen[0] = Thread.currentThread(); + } + }); + t.start(); + t.join(); + System.out.println(seen[0] == t); + System.out.println(seen[0] == a); + } +}