From f00cfadb7652b286504ee2e268b9b55e0f6f66b5 Mon Sep 17 00:00:00 2001 From: james Date: Sat, 20 Dec 2025 22:15:20 +1030 Subject: [PATCH] Implement thread management, and improve VM initialization Initphase 1 supported --- Cargo.toml | 2 +- crates/core/Cargo.toml | 1 + crates/core/src/lib.rs | 62 ++++++++++- crates/core/src/main.rs | 26 ++++- crates/core/src/native/jni.rs | 26 +++-- crates/core/src/native/unsafe.rs | 24 +++-- crates/core/src/objects/object_manager.rs | 67 ++++++++++++ crates/core/src/thread.rs | 79 +++++++------- crates/core/src/vm.rs | 14 ++- crates/roast-vm-sys/src/lib.rs | 126 +--------------------- crates/roast-vm-sys/src/misc_unsafe.rs | 16 +++ crates/roast-vm-sys/src/object.rs | 31 +++++- crates/roast-vm-sys/src/system.rs | 10 +- crates/roast-vm-sys/src/system_props.rs | 50 +++++++++ crates/roast-vm-sys/src/thread.rs | 64 ++++++++++- 15 files changed, 400 insertions(+), 198 deletions(-) create mode 100644 crates/roast-vm-sys/src/system_props.rs diff --git a/Cargo.toml b/Cargo.toml index 803f4ff..9f3192f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,7 @@ roast-vm-core = { path = "crates/core" } colored = "3.0.0" parking_lot = "0.12" cesu8 = "1.1.0" - +windows = { version = "0.62.0", features = ["Win32_System_Diagnostics_Debug", "Win32_System_Kernel"] } [profile.dev-opt] inherits = "dev" opt-level = 2 # 0=none, 1=basic, 2=good, 3=aggressive diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index b162e26..969a4c5 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -18,6 +18,7 @@ itertools = { workspace = true } colored = { workspace = true } parking_lot = { workspace = true } cesu8 = { workspace = true } +windows = { workspace = true } diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index d4b9007..4dafa4b 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -15,6 +15,7 @@ //! - [`MethodDescriptor`] - Method signature information //! - [`FieldType`] - Field type information +use crate::class_file::MethodRef; use crate::class_file::constant_pool::ConstantPoolExt; use crate::class_file::constant_pool::ConstantPoolGet; pub use crate::thread::VmThread; @@ -24,10 +25,12 @@ use frame::Frame; use itertools::Itertools; use log::error; use std::borrow::Cow; +use std::cell::RefCell; use std::fmt::{Debug, Display, Formatter}; use std::io::Read; use std::ops::{BitAnd, Deref}; use value::Value; + mod attributes; mod bimage; mod class; @@ -127,13 +130,13 @@ pub struct MethodDescriptor { } impl MethodDescriptor { - fn void() -> Self { + pub fn void() -> Self { Self { parameters: vec![], return_type: None, } } - fn psvm() -> Self { + pub fn psvm() -> Self { MethodDescriptor::parse("([Ljava/lang/String;)V").unwrap() } @@ -250,3 +253,58 @@ impl FieldType { } } } + +pub fn generate_jni_method_name(method_ref: &MethodRef, with_type: bool) -> String { + if with_type { + generate_jni_long_name( + &method_ref.class, + &method_ref.name, + &method_ref.desc.param_string(), + ) + } else { + generate_jni_short_name(&method_ref.class, &method_ref.name) + } +} + +pub fn generate_jni_long_name(class: &str, method: &str, params: &str) -> String { + let mut name = generate_jni_short_name(class, method); + let params = jni_escape(params); + name.push_str(&format!("__{params}")); + name +} + +pub fn generate_jni_short_name(class: &str, method: &str) -> String { + let class_name = jni_escape(class); + let method_name = jni_escape(method); + format!("Java_{class_name}_{method_name}") +} + +pub fn jni_escape(s: &str) -> String { + s.chars().map(jni_escape_char).join("") +} + +pub fn jni_escape_char(c: char) -> String { + match c { + '/' => "_".to_string(), + '_' => "_1".to_string(), + ';' => "_2".to_string(), + '[' => "_3".to_string(), + c if c.is_ascii_alphanumeric() => c.to_string(), + c => format!("_0{:04x}", c as u32), + } +} + +thread_local! { + pub static LAST_NATIVE_SYMBOL: RefCell = RefCell::new(String::new()); +} +fn set_last_native(name: &str) { + LAST_NATIVE_SYMBOL.with(|cell| { + let mut s = cell.borrow_mut(); + s.clear(); + s.push_str(name); + }); +} + +pub fn get_last_native() -> String { + LAST_NATIVE_SYMBOL.with(|cell| cell.borrow().clone()) +} diff --git a/crates/core/src/main.rs b/crates/core/src/main.rs index 7bc6088..317d833 100644 --- a/crates/core/src/main.rs +++ b/crates/core/src/main.rs @@ -2,8 +2,8 @@ use colored::Colorize; use libloading::Library; use log::{LevelFilter, error}; use roast_vm_core::error::VmError; -use roast_vm_core::stack_used; -use roast_vm_core::vm::Vm; +use roast_vm_core::vm::{LIB_RESOLVE_COUNTS, Vm}; +use roast_vm_core::{get_last_native, stack_used}; use std::path::PathBuf; use std::time::Instant; @@ -28,6 +28,7 @@ fn run() { let mut vm = Vm::new(); fetch_libs(&vm); let start = Instant::now(); + install_crash_handler(); match vm.run("org/example/Main") { Ok(_) => { println!("took {:?}", start.elapsed()); @@ -39,6 +40,9 @@ fn run() { println!("took {:?}", start.elapsed()); let access = vm.loader.lock().access_time(); println!("total module access time was {:?}", access); + for entry in LIB_RESOLVE_COUNTS.iter() { + println!("{}: {}", entry.key(), entry.value()); + } let thread = vm.threads.get(&vm.main_thread_id).unwrap(); let objs = thread .gc @@ -99,7 +103,7 @@ fn os_lib_name(name: &str) -> String { format!("lib{name}.so") } -fn fetch_libs(mut vm: &Vm) { +fn fetch_libs(vm: &Vm) { let exe_path = std::env::current_exe().expect("get exe path"); let roast_name = os_lib_name("roast_vm"); let roast_path = exe_path.parent().unwrap().join(roast_name.clone()); @@ -146,3 +150,19 @@ fn format_bytes(bytes: usize) -> String { format!(" {:.2} {}", size, unit) } +use windows::Win32::Foundation::EXCEPTION_ACCESS_VIOLATION; +use windows::Win32::System::Diagnostics::Debug::*; +unsafe extern "system" fn crash_handler(info: *mut EXCEPTION_POINTERS) -> i32 { + let record = (*info).ExceptionRecord; + if (*record).ExceptionCode == EXCEPTION_ACCESS_VIOLATION { + eprintln!("ACCESS VIOLATION during native call: {}", get_last_native()); + } + EXCEPTION_CONTINUE_SEARCH +} + +// Call once at VM startup: +fn install_crash_handler() { + unsafe { + AddVectoredExceptionHandler(1, Some(crash_handler)); + } +} diff --git a/crates/core/src/native/jni.rs b/crates/core/src/native/jni.rs index 60be2f5..ebacd5d 100644 --- a/crates/core/src/native/jni.rs +++ b/crates/core/src/native/jni.rs @@ -7,7 +7,7 @@ use crate::objects::object::{ObjectReference, ReferenceKind}; use crate::prim::jboolean; use crate::thread::VmThread; use crate::value::{Primitive, Value}; -use crate::{BaseType, FieldType, MethodDescriptor}; +use crate::{BaseType, FieldType, MethodDescriptor, generate_jni_short_name}; use itertools::Itertools; use jni::sys::*; use jni::sys::{JNIEnv, jstring}; @@ -380,13 +380,11 @@ unsafe extern "system" fn register_natives( ) -> jint { trace!("register_natives"); let thread = &*get_thread(env); - let gc = thread.gc.read(); let class_id = clazz as u32; - let ReferenceKind::ObjectReference(class_ref) = gc.get(class_id) else { + let Some(runtime_class) = thread.loader.lock().class_from_mirror_id(class_id) else { return JNI_ERR; }; - let class_name = class_ref.lock().class.this_class.clone(); - let class_name = class_name.replace("/", "_"); + let class_name = runtime_class.this_class.clone(); // let boop = JClass::from_raw(clazz); for i in 0..n_methods as usize { @@ -397,7 +395,7 @@ unsafe extern "system" fn register_natives( let signature = CStr::from_ptr(native_method.signature).to_str().unwrap(); let fn_ptr = native_method.fnPtr; - let full_name = format!("Java_{class_name}_{name}"); + let full_name = generate_jni_short_name(&class_name, name); if thread.vm.native_methods.contains_key(&full_name) { warn!("Native method already registered {full_name}") @@ -408,7 +406,18 @@ unsafe extern "system" fn register_natives( continue; } - if full_name.contains("getDeclaredConstructors0") { + const DISALLOWED_METHODS: &[&str] = &[ + "getDeclaredConstructors0", + "Java_java_lang_Thread_currentThread", + "Java_java_lang_Thread_getNextThreadIdOffset", + "Java_java_lang_Thread_setPriority0", + "Java_java_lang_Thread_start0", + ]; + + if DISALLOWED_METHODS + .iter() + .any(|&method| full_name.contains(method)) + { continue; } @@ -420,7 +429,8 @@ unsafe extern "system" fn register_natives( trace!( "name:{name}, signature:{signature}, fn_ptr{}", fn_ptr.is_null() - ) + ); + println!("JNI register: {full_name}"); } JNI_OK } diff --git a/crates/core/src/native/unsafe.rs b/crates/core/src/native/unsafe.rs index e13475e..5d6bdef 100644 --- a/crates/core/src/native/unsafe.rs +++ b/crates/core/src/native/unsafe.rs @@ -1,9 +1,6 @@ -// unsafe_support.rs or similar - +use crate::prim::jlong; use std::collections::HashMap; use std::sync::atomic::{AtomicI64, Ordering}; -use crate::prim::jlong; - #[derive(Default)] pub struct UnsafeSupport { @@ -27,15 +24,17 @@ const FIELD_OFFSET_BASE: jlong = 0x1_0000_0000; impl UnsafeSupport { pub fn register_field_offset(&mut self, class: &str, field: &str, is_static: bool) -> jlong { let offset = self.next_field_offset.fetch_add(1, Ordering::Relaxed); - self.field_offsets.insert(offset, FieldKey { - class_name: class.to_string(), - field_name: field.to_string(), - is_static, - }); + self.field_offsets.insert( + offset, + FieldKey { + class_name: class.to_string(), + field_name: field.to_string(), + is_static, + }, + ); offset } - pub fn resolve_field(&self, offset: jlong) -> Option { self.field_offsets.get(&offset).cloned() } @@ -44,4 +43,7 @@ impl UnsafeSupport { pub enum OffsetKind { ArrayIndex(jlong), Field(FieldKey), -} \ No newline at end of file +} +// SAFETY: All access is through RwLock in Vm +unsafe impl Send for UnsafeSupport {} +unsafe impl Sync for UnsafeSupport {} diff --git a/crates/core/src/objects/object_manager.rs b/crates/core/src/objects/object_manager.rs index d4d466b..bb640e2 100644 --- a/crates/core/src/objects/object_manager.rs +++ b/crates/core/src/objects/object_manager.rs @@ -325,6 +325,69 @@ impl ObjectManager { Ok(()) } + pub fn wait( + &self, + thread_id: ThreadId, + obj_id: u32, + timeout_millis: jlong, + ) -> Result<(), VmError> { + let monitor = self + .monitors + .get(&obj_id) + .ok_or_else(|| VmError::InvariantError("Monitor not found".into()))? + .clone(); + + let mut inner = monitor.inner.lock(); + + if inner.owner != Some(thread_id) { + return Err(VmError::InvariantError( + "IllegalMonitorStateException".into(), + )); + } + + // Save entry count, release monitor + let saved_count = inner.entry_count; + inner.entry_count = 0; + inner.owner = None; + inner.wait_count += 1; + + // Wake a thread waiting to enter + monitor.condvar.notify_one(); + + // Wait for notify + if timeout_millis > 0 { + monitor.wait_condvar.wait_for( + &mut inner, + std::time::Duration::from_millis(timeout_millis as u64), + ); + } else { + monitor.wait_condvar.wait(&mut inner); + } + + inner.wait_count -= 1; + + // Reacquire monitor + while inner.owner.is_some() { + monitor.condvar.wait(&mut inner); + } + + inner.owner = Some(thread_id); + inner.entry_count = saved_count; + + Ok(()) + } + + pub fn notify_one(&self, obj_id: u32) { + if let Some(monitor) = self.monitors.get(&obj_id) { + monitor.wait_condvar.notify_one(); + } + } + + pub fn notify_all(&self, obj_id: u32) { + if let Some(monitor) = self.monitors.get(&obj_id) { + monitor.wait_condvar.notify_all(); + } + } pub fn clone_object(&mut self, source: &ReferenceKind) -> Result { match source { @@ -454,11 +517,13 @@ impl ObjectManager { pub struct Monitor { inner: Mutex, condvar: Condvar, + wait_condvar: Condvar, } struct MonitorInner { owner: Option, entry_count: u32, + wait_count: u32, } impl Monitor { @@ -467,8 +532,10 @@ impl Monitor { inner: Mutex::new(MonitorInner { owner: None, entry_count: 0, + wait_count: 0, }), condvar: Condvar::new(), + wait_condvar: Condvar::new(), } } } diff --git a/crates/core/src/thread.rs b/crates/core/src/thread.rs index 8f7468b..d9f15a1 100644 --- a/crates/core/src/thread.rs +++ b/crates/core/src/thread.rs @@ -8,7 +8,10 @@ use crate::objects::object::{ObjectReference, ReferenceKind}; use crate::objects::object_manager::ObjectManager; use crate::value::{Primitive, Value}; use crate::vm::Vm; -use crate::{BaseType, FieldType, MethodDescriptor, ThreadId, stack_used}; +use crate::{ + BaseType, FieldType, MethodDescriptor, ThreadId, generate_jni_method_name, set_last_native, + stack_used, +}; use deku::DekuError::Incomplete; use itertools::Itertools; use jni::sys::{JNIEnv, jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jobject, jshort}; @@ -16,7 +19,7 @@ use libffi::low::call; use libffi::middle::*; use log::{LevelFilter, info, trace, warn}; -use parking_lot::{Mutex, Once, ReentrantMutex, RwLock}; +use parking_lot::{Condvar, Mutex, Once, ReentrantMutex, RwLock}; use std::any::Any; use std::cell::RefCell; use std::collections::VecDeque; @@ -47,6 +50,7 @@ pub struct VmThread { pub gc: Arc>, pub jni_env: JNIEnv, pub mirror: OnceLock, + init_condvar: Condvar, } impl VmThread { @@ -65,6 +69,7 @@ impl VmThread { gc, jni_env, mirror: Default::default(), + init_condvar: Default::default(), } }) } @@ -198,6 +203,9 @@ impl VmThread { pub fn ensure_initialised(&self, class: &Arc) -> Result<(), VmError> { let current_thread = thread::current().id(); + if class.this_class.contains("Thread") { + println!("Yep"); + } { let mut state = class.init_state.lock(); @@ -205,10 +213,21 @@ impl VmThread { InitState::Initialized => return Ok(()), InitState::Initializing(tid) if *tid == current_thread => return Ok(()), InitState::Initializing(_) => { - return Err(VmError::LoaderError(format!( - "Class {} is being initialized by another thread", - class.this_class - ))); + // Wait for the other thread to finish + while matches!(&*state, InitState::Initializing(_)) { + self.init_condvar.wait(&mut state); + } + // Re-check state after waking + match &*state { + InitState::Initialized => return Ok(()), + InitState::Error(msg) => { + return Err(VmError::LoaderError(format!( + "Class {} initialization previously failed: {}", + class.this_class, msg + ))); + } + _ => unreachable!(), + } } InitState::Error(msg) => { return Err(VmError::LoaderError(format!( @@ -236,6 +255,9 @@ impl VmThread { } if let Some(method) = class.methods.iter().find(|m| m.name == "") { + if class.this_class.contains("Thread") { + println!("executing init for: {}", class.this_class); + } self.execute_method(class, method, vec![])?; } Ok(()) @@ -287,7 +309,7 @@ impl VmThread { } pub fn invoke(&self, method_reference: MethodRef, args: Vec) -> MethodCallResult { - if self.gc.read().objects.len() > 2235 { + if self.gc.read().objects.len() > 2387 { INIT_LOGGER.call_once(|| { env_logger::Builder::from_default_env() .filter_level(LevelFilter::Trace) @@ -321,10 +343,11 @@ impl VmThread { pub fn invoke_native(&self, method: &MethodRef, mut args: Vec) -> MethodCallResult { let symbol_name = generate_jni_method_name(method, false); - if symbol_name.contains("Java_jdk_internal_misc_Unsafe_getLongVolatile") { - return Err(VmError::Debug( - "RoastVM specific implementation required for Java_jdk_internal_misc_Unsafe_getLongVolatile", - )); + if symbol_name.contains("Java_java_lang_Thread_start0") { + // return Err(VmError::Debug( + // "RoastVM specific implementation required for Java_java_lang_Thread_start0", + // )); + println!() } if symbol_name.contains("Java_java_lang_Class_desiredAssertionStatus0") { @@ -356,7 +379,7 @@ impl VmThread { &self.jni_env as *const _ as *mut JNIEnv, ); let cif = method.build_cif(); - + set_last_native(&symbol_name); match &method.desc.return_type { None => { cif.call::<()>(cp, built_args.as_ref()); @@ -521,33 +544,6 @@ fn build_args<'a>( storage.iter().map(|boxed| arg(&**boxed)).collect() } -pub fn generate_jni_method_name(method_ref: &MethodRef, with_type: bool) -> String { - let class_name = jni_escape(&method_ref.class); - let method_name = jni_escape(&method_ref.name); - - let mut name = format!("Java_{class_name}_{method_name}"); - if with_type { - let params = jni_escape(&method_ref.desc.param_string()); - let str = format!("__{}", params); - name.push_str(&str) - } - name -} - -pub fn jni_escape_char(c: char) -> String { - match c { - '/' => "_".to_string(), - '_' => "_1".to_string(), - ';' => "_2".to_string(), - '[' => "_3".to_string(), - c if c.is_ascii_alphanumeric() => c.to_string(), - c => format!("_0{:04x}", c as u32), - } -} -pub fn jni_escape(s: &str) -> String { - s.chars().map(jni_escape_char).join("") -} - impl From for Type { fn from(value: FieldType) -> Self { match value { @@ -625,3 +621,8 @@ impl VmThread { self.mirror.set(thread.lock().id).unwrap(); } } +// SAFETY: +// - jni_env pointer is owned by this VmThread and only used by its OS thread +// - Frame stack is behind Mutex +unsafe impl Send for VmThread {} +unsafe impl Sync for VmThread {} diff --git a/crates/core/src/vm.rs b/crates/core/src/vm.rs index d5153e9..e8870f0 100644 --- a/crates/core/src/vm.rs +++ b/crates/core/src/vm.rs @@ -20,11 +20,12 @@ use dashmap::DashMap; use imp::Library; use log::trace; use parking_lot::{Mutex, RwLock}; -use std::sync::Arc; use std::sync::atomic::AtomicU64; +use std::sync::{Arc, LazyLock}; type NativeLibraries = Arc>>; +pub static LIB_RESOLVE_COUNTS: LazyLock> = LazyLock::new(DashMap::new); // struct AbstractObject<'a> {} pub struct Vm { // Thread registry - maps ThreadId to VmThread @@ -78,9 +79,10 @@ impl Vm { let res = unsafe { lib.get::(name.as_bytes()) }; if res.is_ok() { println!( - "Invoke native for native symbol: {} out of: {}", + "Register native for native symbol: {} out of: {}", &name, lib_name ); + *LIB_RESOLVE_COUNTS.entry(lib_name.clone()).or_insert(0) += 1; let symbol = res.unwrap(); let ptr = *symbol as *const c_void; self.native_methods.insert(name.to_string(), ptr); @@ -168,7 +170,7 @@ impl Vm { .unwrap() .bootstrap_mirror(); thread.invoke(phase1ref, Vec::new())?; - panic!("HOLY FUCKING SHIT, DID WE PHASE 1!?"); + // panic!("HOLY FUCKING SHIT, DID WE PHASE 1!?"); Ok(()) } @@ -179,3 +181,9 @@ impl Vm { thread.invoke_main(what) } } + +// SAFETY: +// - native_methods contains function pointers from loaded libraries, safe to share +// - All mutable state is behind DashMap/RwLock/Mutex +unsafe impl Send for Vm {} +unsafe impl Sync for Vm {} diff --git a/crates/roast-vm-sys/src/lib.rs b/crates/roast-vm-sys/src/lib.rs index 7b1f348..7a57eb4 100644 --- a/crates/roast-vm-sys/src/lib.rs +++ b/crates/roast-vm-sys/src/lib.rs @@ -9,6 +9,7 @@ mod runtime; mod scoped_memory_access; mod signal; mod system; +mod system_props; mod thread; use jni::objects::{JClass, JObject, JString}; @@ -41,52 +42,6 @@ pub extern "system" fn Java_org_example_MockIO_print<'local>( // println!("Yeetus bageetus! Im printing from rust!") } -/*#[unsafe(no_mangle)] -pub extern "system" fn Java_org_example_MockIO_registerNatives<'local>( - mut env: JNIEnv<'local>, - jclass: JClass<'local>, -) { - let system_methods = vec![NativeMethod { - name: JNIString::from("currentTimeMillis"), - sig: JNIString::from("()J"), - fn_ptr: current_time_millis as *mut c_void, - }]; - let system_class = env - .find_class("java/lang/System") - .expect("Failed to find system class"); - - env.register_native_methods(system_class, &system_methods) - .expect("failed to register method"); - - let library_methods = vec![NativeMethod { - name: JNIString::from("findEntry0"), - sig: JNIString::from("(JLjava/lang/String;)J"), - fn_ptr: findEntry0 as *mut c_void, - }]; - - let library_class = env - .find_class("jdk/internal/loader/NativeLibrary") - .expect("Failed to find system class"); - - // env.register_native_methods(library_class, &library_methods) - // .expect("failed to register method"); -}*/ - -#[unsafe(no_mangle)] -pub extern "system" fn findEntry0<'local>( - mut env: JNIEnv<'local>, - jclass: JClass<'local>, - handle: jlong, - name: JString<'local>, -) -> jlong { - let name: String = env - .get_string(&name) - .expect("Couldn't get java string!") - .into(); - println!("Name: {}, Handle: {}", name, handle); - 0i64 -} - #[unsafe(no_mangle)] pub extern "system" fn Java_org_example_Main_getTime<'local>( mut env: JNIEnv<'local>, @@ -111,38 +66,6 @@ pub extern "system" fn Java_org_example_MockIO_println<'local>( println!("{input}") } -// #[unsafe(no_mangle)] -// pub extern "system" fn Java_java_lang_Class_registerNatives<'local>( -// mut env: JNIEnv<'local>, -// jclass: JClass<'local>, -// ) { -// println!("Java_java_lang_Class_registerNatives is NOP") -// } -// -// #[unsafe(no_mangle)] -// pub extern "system" fn Java_java_lang_Object_getClass<'local>( -// mut env: JNIEnv<'local>, -// this: JObject<'local>, -// ) -> JClass<'local> { -// unsafe { -// if this.is_null() { -// env.throw_new( -// "java/lang/NullPointerException", -// "Cannot invoke getClass() on null", -// ) -// .unwrap(); -// return JClass::from_raw(std::ptr::null_mut()); -// } -// } -// match env.get_object_class(this) { -// Ok(c) => c, -// Err(e) => { -// eprintln!("get_object_class failed: {:?}", e); -// panic!("debug"); -// } -// } -// } - unsafe fn get_thread(env: *mut jni::sys::JNIEnv) -> *const VmThread { let thread = (**env).reserved0 as *const VmThread; VmThread::set_current((*thread).id); @@ -171,50 +94,3 @@ fn resolve_reference(thread: &VmThread, obj: jobject) -> Option { let gc = thread.gc.read(); Some(gc.get(obj as u32).clone()) } - -#[unsafe(no_mangle)] -pub extern "system" fn Java_jdk_internal_util_SystemProps_00024Raw_vmProperties<'local>( - mut env: JNIEnv<'local>, - _class: JClass<'local>, -) -> jobjectArray { - let props = [ - "java.vm.specification.name", - "Java Virtual Machine Specification", - "java.vm.specification.vendor", - "Oracle Corporation", - "java.vm.specification.version", - "25", - "java.vm.name", - "RoastVM", - "java.vm.vendor", - "infernap12", - "java.vm.version", - "0.1.0", - "java.vm.info", - "interpreted mode", - "jdk.debug", - "release", - "java.home", - "C:\\Program Files\\Java\\jdk-25", // adjust - "java.library.path", - "", - "sun.boot.library.path", - "", - "java.class.path", - ".", - ]; - - let string_class = env.find_class("java/lang/String").unwrap(); - // +1 for null terminator - let arr = env - .new_object_array((props.len() + 1) as i32, string_class, JObject::null()) - .unwrap(); - - for (i, s) in props.iter().enumerate() { - let jstr = env.new_string(s).unwrap(); - env.set_object_array_element(&arr, i as i32, jstr).unwrap(); - } - // Last element stays null (terminator) - - arr.into_raw() -} diff --git a/crates/roast-vm-sys/src/misc_unsafe.rs b/crates/roast-vm-sys/src/misc_unsafe.rs index 69c82f9..1fe2331 100644 --- a/crates/roast-vm-sys/src/misc_unsafe.rs +++ b/crates/roast-vm-sys/src/misc_unsafe.rs @@ -1,3 +1,4 @@ +use crate::thread::THREAD_NEXT_ID_SENTINEL; use crate::{get_thread, resolve_array, resolve_object}; use jni::sys::{JNI_FALSE, JNI_TRUE, JNIEnv, jboolean, jclass, jint, jlong, jobject, jstring}; use log::warn; @@ -6,6 +7,7 @@ use roast_vm_core::objects::ReferenceKind; use roast_vm_core::objects::array::ArrayReference; use roast_vm_core::objects::object::string_from_bytes; use roast_vm_core::value::Value; +use std::sync::atomic::Ordering; #[unsafe(no_mangle)] pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_registerNatives( @@ -240,6 +242,17 @@ pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_compareAndSetLong( new_val: jlong, ) -> jboolean { let thread = &*get_thread(env); + if offset == THREAD_NEXT_ID_SENTINEL { + return match thread.vm.NEXT_ID.compare_exchange( + expected as u64, + new_val as u64, + Ordering::SeqCst, + Ordering::Relaxed, + ) { + Ok(_) => JNI_TRUE, + Err(_) => JNI_FALSE, + }; + } let reference = thread.gc.read().get(obj as u32); match reference { @@ -580,6 +593,9 @@ pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_getLongVolatile( offset: jlong, ) -> jlong { let thread = &*get_thread(env); + if obj.is_null() && offset == THREAD_NEXT_ID_SENTINEL { + return thread.vm.NEXT_ID.load(Ordering::SeqCst) as jlong; + } let reference = thread.gc.read().get(obj as u32); match reference { diff --git a/crates/roast-vm-sys/src/object.rs b/crates/roast-vm-sys/src/object.rs index 74e840a..3058651 100644 --- a/crates/roast-vm-sys/src/object.rs +++ b/crates/roast-vm-sys/src/object.rs @@ -1,5 +1,5 @@ use crate::get_thread; -use jni::sys::{jint, jobject, JNIEnv}; +use jni::sys::{JNIEnv, jint, jlong, jobject}; #[unsafe(no_mangle)] pub unsafe extern "system" fn Java_java_lang_Object_hashCode( @@ -27,3 +27,32 @@ pub unsafe extern "system" fn Java_java_lang_Object_clone( } } } + +#[unsafe(no_mangle)] +unsafe extern "system" fn Java_java_lang_Object_notifyAll(env: *mut JNIEnv, this: jobject) { + // Wake all threads waiting on this object's monitor + let thread = &*get_thread(env); + thread.gc.read().notify_all(this as u32); +} + +#[unsafe(no_mangle)] +unsafe extern "system" fn Java_java_lang_Object_notify(env: *mut JNIEnv, this: jobject) { + // Wake one thread waiting on this object's monitor + let thread = &*get_thread(env); + thread.gc.read().notify_one(this as u32); +} + +#[unsafe(no_mangle)] +unsafe extern "system" fn Java_java_lang_Object_wait( + env: *mut JNIEnv, + this: jobject, + timeout_millis: jlong, +) { + let thread = &*get_thread(env); + let thread_id = thread.id; + thread + .gc + .read() + .wait(thread_id, this as u32, timeout_millis) + .unwrap(); +} diff --git a/crates/roast-vm-sys/src/system.rs b/crates/roast-vm-sys/src/system.rs index cb8e4c5..764b1fe 100644 --- a/crates/roast-vm-sys/src/system.rs +++ b/crates/roast-vm-sys/src/system.rs @@ -1,6 +1,6 @@ use crate::{get_thread, resolve_array, resolve_object}; use jni::objects::JClass; -use jni::sys::{jclass, jint, jlong, jobject, JNIEnv}; +use jni::sys::{JNIEnv, jclass, jint, jlong, jobject}; use log::warn; use std::time::{SystemTime, UNIX_EPOCH}; @@ -80,3 +80,11 @@ pub extern "system" fn Java_java_lang_System_nanoTime<'local>( .unwrap() .as_nanos() as jlong } + +#[unsafe(no_mangle)] +pub extern "system" fn Java_java_lang_System_registerNatives<'local>( + mut env: jni::JNIEnv<'local>, + jclass: JClass<'local>, +) { + // No-op: native methods are handled by roast +} diff --git a/crates/roast-vm-sys/src/system_props.rs b/crates/roast-vm-sys/src/system_props.rs new file mode 100644 index 0000000..796595a --- /dev/null +++ b/crates/roast-vm-sys/src/system_props.rs @@ -0,0 +1,50 @@ +use jni::JNIEnv; +use jni::objects::{JClass, JObject}; +use jni::sys::jobjectArray; + +#[unsafe(no_mangle)] +pub extern "system" fn Java_jdk_internal_util_SystemProps_00024Raw_vmProperties<'local>( + mut env: JNIEnv<'local>, + _class: JClass<'local>, +) -> jobjectArray { + let props = [ + "java.vm.specification.name", + "Java Virtual Machine Specification", + "java.vm.specification.vendor", + "Oracle Corporation", + "java.vm.specification.version", + "25", + "java.vm.name", + "RoastVM", + "java.vm.vendor", + "infernap12", + "java.vm.version", + "0.1.0", + "java.vm.info", + "interpreted mode", + "jdk.debug", + "release", + "java.home", + "C:\\Program Files\\Java\\jdk-25", // adjust + "java.library.path", + "", + "sun.boot.library.path", + "", + "java.class.path", + ".", + ]; + + let string_class = env.find_class("java/lang/String").unwrap(); + // +1 for null terminator + let arr = env + .new_object_array((props.len() + 1) as i32, string_class, JObject::null()) + .unwrap(); + + for (i, s) in props.iter().enumerate() { + let jstr = env.new_string(s).unwrap(); + env.set_object_array_element(&arr, i as i32, jstr).unwrap(); + } + // Last element stays null (terminator) + + arr.into_raw() +} diff --git a/crates/roast-vm-sys/src/thread.rs b/crates/roast-vm-sys/src/thread.rs index 13791be..fcf9b64 100644 --- a/crates/roast-vm-sys/src/thread.rs +++ b/crates/roast-vm-sys/src/thread.rs @@ -1,6 +1,8 @@ use crate::get_thread; use jni::sys::{JNIEnv, jclass, jint, jlong, jobject}; -use std::sync::atomic::Ordering; +use roast_vm_core::class_file::MethodRef; +use roast_vm_core::value::Value; +use roast_vm_core::{MethodDescriptor, VmThread}; #[unsafe(no_mangle)] unsafe extern "system" fn Java_java_lang_Thread_currentThread( @@ -12,11 +14,65 @@ unsafe extern "system" fn Java_java_lang_Thread_currentThread( *thread_id_obj as jobject } +pub const THREAD_NEXT_ID_SENTINEL: jlong = 0xFFFFFFFF_FFFFFFFFu64 as i64; + #[unsafe(no_mangle)] unsafe extern "system" fn Java_java_lang_Thread_getNextThreadIdOffset( - env: *mut JNIEnv, + _env: *mut JNIEnv, _cls: jclass, ) -> jlong { - let thread = &*get_thread(env); - thread.vm.NEXT_ID.fetch_add(1, Ordering::Relaxed) as jlong + THREAD_NEXT_ID_SENTINEL +} + +#[unsafe(no_mangle)] +unsafe extern "system" fn Java_java_lang_Thread_setPriority0( + env: *mut JNIEnv, + _this: jobject, + priority: jint, +) { + // Get the current thread and update its priority + // let thread = &*get_thread(env); + // Store priority in thread state (implementation depends on thread structure) + // For now, this is a no-op placeholder as the actual thread priority field + // would need to be added to the thread structure +} + +#[unsafe(no_mangle)] +unsafe extern "system" fn Java_java_lang_Thread_start0(env: *mut JNIEnv, this: jobject) { + let parent_thread = &*get_thread(env); + let vm = parent_thread.vm.clone(); + let thread_obj_id = this as u32; + + std::thread::spawn(move || { + // Create new VmThread + let new_thread = VmThread::new(vm.clone(), None); + let thread_id = new_thread.id; + + // Set mirror to the java.lang.Thread object passed in + new_thread.mirror.set(thread_obj_id).unwrap(); + + // Register with VM + vm.threads.insert(thread_id, new_thread.clone()); + + // Set as current thread for this OS thread + VmThread::set_current(thread_id); + + // Call this.run() + let thread_class = new_thread.get_class("java/lang/Thread").unwrap(); + let run_ref = MethodRef { + class: "java/lang/Thread".to_string(), + name: "run".to_string(), + desc: MethodDescriptor::void(), + }; + + // Get the Thread object as a Value + let thread_value = Value::Reference(Some(new_thread.gc.read().get(thread_obj_id))); + + if let Err(e) = new_thread.invoke(run_ref, vec![thread_value]) { + eprintln!("Thread {} died with: {:?}", thread_id.0, e); + } + + // Cleanup: remove from VM thread registry + vm.threads.remove(&thread_id); + }); }