Implement thread management, and improve VM initialization
Initphase 1 supported
This commit is contained in:
parent
94f43066f9
commit
f00cfadb76
@ -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
|
||||
|
||||
@ -18,6 +18,7 @@ itertools = { workspace = true }
|
||||
colored = { workspace = true }
|
||||
parking_lot = { workspace = true }
|
||||
cesu8 = { workspace = true }
|
||||
windows = { workspace = true }
|
||||
|
||||
|
||||
|
||||
|
||||
@ -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<String> = 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())
|
||||
}
|
||||
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
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<FieldKey> {
|
||||
self.field_offsets.get(&offset).cloned()
|
||||
}
|
||||
@ -45,3 +44,6 @@ pub enum OffsetKind {
|
||||
ArrayIndex(jlong),
|
||||
Field(FieldKey),
|
||||
}
|
||||
// SAFETY: All access is through RwLock<UnsafeSupport> in Vm
|
||||
unsafe impl Send for UnsafeSupport {}
|
||||
unsafe impl Sync for UnsafeSupport {}
|
||||
|
||||
@ -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<ReferenceKind, VmError> {
|
||||
match source {
|
||||
@ -454,11 +517,13 @@ impl ObjectManager {
|
||||
pub struct Monitor {
|
||||
inner: Mutex<MonitorInner>,
|
||||
condvar: Condvar,
|
||||
wait_condvar: Condvar,
|
||||
}
|
||||
|
||||
struct MonitorInner {
|
||||
owner: Option<ThreadId>,
|
||||
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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<RwLock<ObjectManager>>,
|
||||
pub jni_env: JNIEnv,
|
||||
pub mirror: OnceLock<u32>,
|
||||
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<RuntimeClass>) -> Result<(), VmError> {
|
||||
let current_thread = thread::current().id();
|
||||
if class.this_class.contains("Thread") {
|
||||
println!("Yep");
|
||||
}
|
||||
|
||||
{
|
||||
let mut state = class.init_state.lock();
|
||||
@ -205,11 +213,22 @@ impl VmThread {
|
||||
InitState::Initialized => return Ok(()),
|
||||
InitState::Initializing(tid) if *tid == current_thread => return Ok(()),
|
||||
InitState::Initializing(_) => {
|
||||
// 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 {} is being initialized by another thread",
|
||||
class.this_class
|
||||
"Class {} initialization previously failed: {}",
|
||||
class.this_class, msg
|
||||
)));
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
InitState::Error(msg) => {
|
||||
return Err(VmError::LoaderError(format!(
|
||||
"Class {} initialization previously failed: {}",
|
||||
@ -236,6 +255,9 @@ impl VmThread {
|
||||
}
|
||||
|
||||
if let Some(method) = class.methods.iter().find(|m| m.name == "<clinit>") {
|
||||
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<Value>) -> 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<Value>) -> 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<FieldType> 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 {}
|
||||
|
||||
@ -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<RwLock<Vec<(String, Library)>>>;
|
||||
|
||||
pub static LIB_RESOLVE_COUNTS: LazyLock<DashMap<String, u32>> = 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::<unsafe extern "system" fn()>(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 {}
|
||||
|
||||
@ -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<ReferenceKind> {
|
||||
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()
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
50
crates/roast-vm-sys/src/system_props.rs
Normal file
50
crates/roast-vm-sys/src/system_props.rs
Normal file
@ -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()
|
||||
}
|
||||
@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user