From 9ccb383cd9533a7eeb54352cff2f716cb743d645 Mon Sep 17 00:00:00 2001 From: Inseok Lee Date: Thu, 16 Jul 2026 14:36:38 +0900 Subject: [PATCH] Return the same Thread object from Thread.currentThread() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every attached thread now owns its java/lang/Thread instance: attach takes the instance for threads started via Thread.start (so currentThread() inside run() is the started Thread object) and creates one otherwise (bootstrap, external attachers). currentThread() returns the stored instance, and the GC roots it per thread. Also parse unrecognized classfile attributes as an opaque Unknown variant instead of failing — JVMS 4.7.1 requires silently ignoring them, and the anonymous-class fixture carries EnclosingMethod and Signature attributes the parser rejected. Expected output for the fixture is generated by a real JVM. --- classfile/src/attribute.rs | 4 +++- java_runtime/src/classes/java/lang/thread.rs | 8 +++----- .../tests/classes/java/lang/test_object.rs | 4 ++-- jvm/src/garbage_collector.rs | 4 ++++ jvm/src/jvm.rs | 17 +++++++++++++++-- jvm/src/thread.rs | 15 ++++++++++++++- test_data/CurrentThread$1.class | Bin 0 -> 592 bytes test_data/CurrentThread.class | Bin 0 -> 1009 bytes test_data/CurrentThread.txt | 3 +++ test_data/src/CurrentThread.java | 18 ++++++++++++++++++ 10 files changed, 62 insertions(+), 11 deletions(-) create mode 100644 test_data/CurrentThread$1.class create mode 100644 test_data/CurrentThread.class create mode 100644 test_data/CurrentThread.txt create mode 100644 test_data/src/CurrentThread.java 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 0000000000000000000000000000000000000000..a0170410f7f5593065bf049022bac98dc4beda38 GIT binary patch literal 592 zcmZuuT}uK%6g}f^uDh%?uxzAZGdS~t`|e2eLWbnIE8XydK`U1tEi5Bzz_GD{+(P+G zk0I0h-2p?sT>0y2AkUDSdp$%_@~$Ts3gv&AtUNO44ZlzMY}1vZ6^*+>b!H5+?G7Xl zBSl$FMRZTA?MJF7uH9HLw=j{@*ppPQWY6;hR}St(IQ07lcB%7)N*nt)Alor_Npi8R zcBqJ2kK0PML=fKi0S(H&l~SmN$Adrw#7@v?Y3N{%6F&~USAtd=YlH>b9TLWWtm?@t zSoICYGo2XLDJBU){0)i;*mT;Q#+Y{uZJM>JFJRg`GVM9fnkm90A01m$_BM71bri8j Qp0QpLM}!us#QyaC0FkYGF8}}l literal 0 HcmV?d00001 diff --git a/test_data/CurrentThread.class b/test_data/CurrentThread.class new file mode 100644 index 0000000000000000000000000000000000000000..cafa82c16a6ac6da94d865f1c7e36dbff72b89e9 GIT binary patch literal 1009 zcmZuwYi|-k6g>kA1KUNgr4OoAt5tavkk&WG#1Ku4fQ>Xws?lVjle!4AWOp&@57N*2 zp%@$e0sbiQ&O%$TE-*WL?z#7#IeYp2=f^Jqk8n>%06`ATKnP)mMB99C7CWZgFFtR! zMayFdKd_|bJ!A-`GdnsWh;rx#W?)Rwx8FL#++&Ef`i>){cl4Z0XR0SfM`$^UGl+8} z44lR6RGG#{*Arc8X7{Q0{3zA3i(8H*y@p3Ux@8?n%yTRlSi};;oL_#L!@zqgqazu# z^vlc+$1+2_GO2B?q$7p%>f|d73+Y#iuq0INPcs1-hn1!lxzC;VrpO?Wj=0 zGh6R!X3yty6v>21^7T&Zeg^&VhnDDhmMvY5n@1k{@BKJJtcKlpTH=YNM9ogA zFDTO)X6nNAYC>su8H}e=3a8RBU01kR#dR7kbp=4iAWUn9o>u72=pM=rz`oLkAxoMl z2Uu0h8u9bNjg&T#GJGc8`MQ3zcxwlE!qcg R8+XQ7ij;!R!f3$#>n|lj#bE#d literal 0 HcmV?d00001 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); + } +}