From 911bb1be82433e874fb03d4a37f064ae03421569 Mon Sep 17 00:00:00 2001 From: james Date: Mon, 29 Dec 2025 06:51:03 +1030 Subject: [PATCH] Sync for jsr 292 --- .idea/dictionaries/project.xml | 7 + .idea/inspectionProfiles/Project_Default.xml | 6 + Cargo.toml | 3 +- crates/core/Cargo.toml | 1 + crates/core/src/class.rs | 3 +- crates/core/src/class_file/class_file.rs | 296 +++++++++-- crates/core/src/class_file/constant_pool.rs | 117 ++++- crates/core/src/class_loader.rs | 13 +- crates/core/src/error.rs | 34 +- crates/core/src/frame/frame.rs | 488 +++++++++++++++---- crates/core/src/lib.rs | 103 ++-- crates/core/src/macros.rs | 6 + crates/core/src/main.rs | 14 +- crates/core/src/native/jni.rs | 11 +- crates/core/src/objects/array.rs | 7 + crates/core/src/objects/object.rs | 7 +- crates/core/src/objects/object_manager.rs | 4 +- crates/core/src/symbols.rs | 103 ++++ crates/core/src/thread.rs | 93 ++-- crates/core/src/value.rs | 20 + crates/core/src/vm.rs | 122 +++-- crates/roast-vm-sys/src/class.rs | 5 +- crates/roast-vm-sys/src/lib.rs | 1 - crates/roast-vm-sys/src/reflect/array.rs | 9 +- crates/roast-vm-sys/src/reflection.rs | 7 +- crates/roast-vm-sys/src/system_props.rs | 3 +- crates/roast-vm-sys/src/thread.rs | 17 +- 27 files changed, 1203 insertions(+), 297 deletions(-) create mode 100644 .idea/dictionaries/project.xml create mode 100644 crates/core/src/symbols.rs diff --git a/.idea/dictionaries/project.xml b/.idea/dictionaries/project.xml new file mode 100644 index 0000000..f26bfd0 --- /dev/null +++ b/.idea/dictionaries/project.xml @@ -0,0 +1,7 @@ + + + + stacktraceelement + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml index 146e386..cfd99d5 100644 --- a/.idea/inspectionProfiles/Project_Default.xml +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -2,5 +2,11 @@ \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 9f3192f..3049bbd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,8 @@ 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"] } +windows = { version = "0.62.0", features = ["Win32_System_Diagnostics_Debug", "Win32_System_Kernel", "Win32_System_Threading"] } +libc = "0.2.178" [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 ac91bc0..60c7139 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -19,6 +19,7 @@ colored = { workspace = true } parking_lot = { workspace = true } cesu8 = { workspace = true } windows = { workspace = true } +libc = { workspace = true } diff --git a/crates/core/src/class.rs b/crates/core/src/class.rs index 114f5e8..8b14909 100644 --- a/crates/core/src/class.rs +++ b/crates/core/src/class.rs @@ -1,13 +1,12 @@ use crate::class_file::attributes::BootstrapMethodsAttribute; use crate::class_file::{ClassFlags, ConstantPoolEntry, FieldData, MethodData}; use crate::error::VmError; -use crate::{FieldType, MethodDescriptor}; +use crate::{FieldType, MethodDescriptor, ThreadId}; use log::trace; use parking_lot::Mutex; use std::hash::{Hash, Hasher}; use std::sync::atomic::AtomicBool; use std::sync::{Arc, OnceLock}; -use std::thread::ThreadId; /// JVM Spec 5.5: Initialization states for a class #[derive(Debug, Clone, PartialEq)] diff --git a/crates/core/src/class_file/class_file.rs b/crates/core/src/class_file/class_file.rs index d42938b..15048dc 100644 --- a/crates/core/src/class_file/class_file.rs +++ b/crates/core/src/class_file/class_file.rs @@ -2,6 +2,7 @@ use crate::class_file::attributes::{ Attribute, AttributeInfo, CodeAttribute, LineNumberTableEntry, }; use crate::class_file::constant_pool::{ConstantPoolExt, ConstantPoolOwned}; +use crate::class_file::resolved::{InterfaceMethodRef, MethodRef}; use crate::instructions::Ops; use crate::value::Value; use crate::{BaseType, FieldType, MethodDescriptor}; @@ -17,6 +18,8 @@ use std::ops::Deref; use std::str::Chars; use std::sync::Arc; +pub type FieldDescriptor = FieldType; + #[derive(Debug, PartialEq, DekuRead)] #[deku(magic = b"\xCA\xFE\xBA\xBE", endian = "big")] pub struct ClassFile { @@ -648,12 +651,6 @@ pub(crate) struct Bytecode { // } // } -#[derive(Debug, Clone)] -pub struct MethodRef { - pub class: String, - pub name: String, - pub desc: MethodDescriptor, -} #[derive(Debug, Clone)] pub struct MethodData { pub name: String, @@ -669,41 +666,6 @@ pub struct MethodData { // pub method_parameters: Option<_> } -impl From for MethodRef { - fn from(value: MethodData) -> Self { - Self { - class: value.class, - name: value.name, - desc: value.desc, - } - } -} - -impl From<&MethodData> for MethodRef { - fn from(value: &MethodData) -> Self { - value.clone().into() - } -} - -#[derive(Debug)] -pub struct FieldRef { - pub class: String, - pub name: String, - pub desc: FieldType, -} - -impl FieldRef { - pub fn new(class: &str, name: &str, field: FieldType) -> Self { - let class = class.to_string(); - let name = name.to_string(); - Self { - class, - name, - desc: field, - } - } -} - #[derive(Debug)] pub struct FieldData { pub name: String, @@ -1010,3 +972,255 @@ impl FieldType { }) } } + +pub mod resolved { + use crate::class_file::MethodData; + use crate::class_file::class_file::FieldDescriptor; + use crate::{FieldType, MethodDescriptor}; + + #[derive(Debug, Clone)] + pub struct FieldRef { + pub class: String, + pub name: String, + pub desc: FieldDescriptor, + } + + impl FieldRef { + pub(crate) fn from_symbols(class: &str, name: &str, descriptor_class_name: &str) -> Self { + let class = class.to_string(); + let name = name.to_string(); + let desc = FieldDescriptor::from_symbols(descriptor_class_name); + Self { class, name, desc } + } + } + + impl FieldRef { + pub fn new(class: &str, name: &str, field: FieldType) -> Self { + let class = class.to_string(); + let name = name.to_string(); + Self { + class, + name, + desc: field, + } + } + } + + #[derive(Debug, Clone)] + pub struct MethodRef { + pub class: String, + pub name: String, + pub desc: MethodDescriptor, + } + + impl MethodRef { + pub fn from_symbols( + class: &str, + name: &str, + params: &[&str], + return_type: Option<&str>, + ) -> Self { + let class = class.to_string(); + let name = name.to_string(); + let desc = MethodDescriptor::from_symbols(params, return_type); + Self { class, name, desc } + } + } + + impl From<&MethodData> for MethodRef { + fn from(value: &MethodData) -> Self { + value.clone().into() + } + } + + impl From for MethodRef { + fn from(value: MethodData) -> Self { + Self { + class: value.class, + name: value.name, + desc: value.desc, + } + } + } + + #[derive(Debug, Clone)] + pub struct InterfaceMethodRef { + pub class: String, + pub name: String, + pub desc: MethodDescriptor, + } + + #[derive(Debug, Clone)] + pub struct MethodHandle { + pub kind: MethodHandleKind, + } + + impl From for MethodHandle { + fn from(kind: MethodHandleKind) -> Self { + Self { kind } + } + } + + #[derive(Debug, Clone)] + pub struct MethodType { + pub desc: MethodDescriptor, + } + impl From for MethodType { + fn from(desc: MethodDescriptor) -> Self { + Self { desc } + } + } + + #[derive(Debug, Clone)] + pub struct Dynamic { + pub method: BootstrapMethod, + pub name: String, + pub desc: FieldDescriptor, + } + #[derive(Debug, Clone)] + pub struct InvokeDynamic { + pub method: BootstrapMethod, + pub name: String, + pub desc: MethodDescriptor, + } + #[derive(Debug, Clone)] + pub struct BootstrapMethod { + pub handle: MethodHandle, + pub args: Vec, + } + + #[derive(Debug, Clone)] + pub enum MethodHandleKind { + GetField(FieldRef), + GetStatic(FieldRef), + PutField(FieldRef), + PutStatic(FieldRef), + InvokeVirtual(MethodRef), + InvokeStatic(InterfaceOrMethod), + InvokeSpecial(InterfaceOrMethod), + NewInvokeSpecial(MethodRef), + InvokeInterface(InterfaceMethodRef), + } + + impl MethodHandleKind { + pub fn lookup_descriptor(&self) -> String { + match self { + MethodHandleKind::GetField(fr) => fr.desc.to_string(), + MethodHandleKind::GetStatic(fr) => fr.desc.to_string(), + MethodHandleKind::PutField(fr) => fr.desc.to_string(), + MethodHandleKind::PutStatic(fr) => fr.desc.to_string(), + MethodHandleKind::InvokeVirtual(mr) => mr.desc.to_string(), + MethodHandleKind::InvokeStatic(iom) => iom.desc().to_string(), + MethodHandleKind::InvokeSpecial(iom) => iom.desc().to_string(), + MethodHandleKind::NewInvokeSpecial(mr) => mr.desc.to_string(), + MethodHandleKind::InvokeInterface(imf) => imf.desc.to_string(), + } + } + + pub fn class(&self) -> String { + match self { + MethodHandleKind::GetField(fr) => fr.class.to_string(), + MethodHandleKind::GetStatic(fr) => fr.class.to_string(), + MethodHandleKind::PutField(fr) => fr.class.to_string(), + MethodHandleKind::PutStatic(fr) => fr.class.to_string(), + MethodHandleKind::InvokeVirtual(mr) => mr.class.to_string(), + MethodHandleKind::InvokeStatic(iom) => iom.class().to_string(), + MethodHandleKind::InvokeSpecial(iom) => iom.class().to_string(), + MethodHandleKind::NewInvokeSpecial(mr) => mr.class.to_string(), + MethodHandleKind::InvokeInterface(imf) => imf.class.to_string(), + } + } + + pub fn name(&self) -> String { + match self { + MethodHandleKind::GetField(fr) => fr.name.to_string(), + MethodHandleKind::GetStatic(fr) => fr.name.to_string(), + MethodHandleKind::PutField(fr) => fr.name.to_string(), + MethodHandleKind::PutStatic(fr) => fr.name.to_string(), + MethodHandleKind::InvokeVirtual(mr) => mr.name.to_string(), + MethodHandleKind::InvokeStatic(iom) => iom.name().to_string(), + MethodHandleKind::InvokeSpecial(iom) => iom.name().to_string(), + MethodHandleKind::NewInvokeSpecial(mr) => mr.name.to_string(), + MethodHandleKind::InvokeInterface(imf) => imf.name.to_string(), + } + } + + pub fn is_interface(&self) -> bool { + match self { + MethodHandleKind::InvokeStatic(InterfaceOrMethod::Interface(_)) => true, + MethodHandleKind::InvokeSpecial(InterfaceOrMethod::Interface(_)) => true, + MethodHandleKind::InvokeInterface(_) => true, + _ => false, + } + } + + pub fn ordinal(&self) -> u8 { + match self { + MethodHandleKind::GetField(_) => 1, + MethodHandleKind::GetStatic(_) => 2, + MethodHandleKind::PutField(_) => 3, + MethodHandleKind::PutStatic(_) => 4, + MethodHandleKind::InvokeVirtual(_) => 5, + MethodHandleKind::InvokeStatic(_) => 6, + MethodHandleKind::InvokeSpecial(_) => 7, + MethodHandleKind::NewInvokeSpecial(_) => 8, + MethodHandleKind::InvokeInterface(_) => 9, + } + } + } + #[derive(Debug, Clone)] + pub enum InterfaceOrMethod { + Method(MethodRef), + Interface(InterfaceMethodRef), + } + impl InterfaceOrMethod { + pub fn name(&self) -> &String { + match self { + InterfaceOrMethod::Method(method_ref) => &method_ref.name, + InterfaceOrMethod::Interface(method_ref) => &method_ref.name, + } + } + pub fn class(&self) -> &String { + match self { + InterfaceOrMethod::Method(method_ref) => &method_ref.class, + InterfaceOrMethod::Interface(method_ref) => &method_ref.class, + } + } + pub fn desc(&self) -> &MethodDescriptor { + match self { + InterfaceOrMethod::Method(method_ref) => &method_ref.desc, + InterfaceOrMethod::Interface(method_ref) => &method_ref.desc, + } + } + } + + impl From for InterfaceOrMethod { + fn from(value: MethodRef) -> Self { + Self::Method(value) + } + } + impl From for InterfaceOrMethod { + fn from(value: InterfaceMethodRef) -> Self { + Self::Interface(value) + } + } + + #[derive(Debug, Clone)] + pub enum Loadable { + Integer(i32), + Float(f32), + Long(i64), + Double(f64), + Class(String), + String(String), + MethodHandle(MethodHandle), + MethodType(MethodType), + Dynamic(Dynamic), + } + + impl From for Loadable { + fn from(handle: MethodHandle) -> Self { + Self::MethodHandle(handle) + } + } +} diff --git a/crates/core/src/class_file/constant_pool.rs b/crates/core/src/class_file/constant_pool.rs index d8afa01..de403f8 100644 --- a/crates/core/src/class_file/constant_pool.rs +++ b/crates/core/src/class_file/constant_pool.rs @@ -1,19 +1,25 @@ +use crate::class::RuntimeClass; use crate::class_file::attributes::{ Attribute, AttributeInfo, BootstrapMethodsAttribute, CodeAttribute, LineNumberTableAttribute, LocalVariableTableAttribute, }; +use crate::class_file::resolved::{ + BootstrapMethod, Dynamic, FieldRef, InterfaceMethodRef, InterfaceOrMethod, Loadable, + MethodHandleKind, MethodRef, MethodType, +}; use crate::class_file::{ ConstantClassInfo, ConstantDynamicInfo, ConstantFieldrefInfo, ConstantInterfaceMethodrefInfo, ConstantInvokeDynamicInfo, ConstantMethodHandleInfo, ConstantMethodTypeInfo, ConstantMethodrefInfo, ConstantModuleInfo, ConstantNameAndTypeInfo, ConstantPackageInfo, - ConstantPoolEntry, ConstantStringInfo, ConstantUtf8Info, DescParseError, FieldInfo, FieldRef, - MethodRef, + ConstantPoolEntry, ConstantStringInfo, ConstantUtf8Info, DescParseError, FieldDescriptor, + FieldInfo, }; use crate::error::VmError; use crate::{FieldType, MethodDescriptor, pool_get_impl}; use cesu8::{Cesu8DecodingError, from_java_cesu8}; use deku::DekuContainerRead; use std::fmt::{Display, Formatter}; +use std::sync::Arc; pub type ConstantPoolSlice = [ConstantPoolEntry]; pub type ConstantPoolOwned = Vec; @@ -99,14 +105,17 @@ pub trait ConstantPoolExt: ConstantPoolGet { Ok(MethodRef { class, name, desc }) } - fn resolve_interface_method_ref(&self, index: u16) -> Result { + fn resolve_interface_method_ref( + &self, + index: u16, + ) -> Result { let mr = self.get_interface_method_ref(index)?; let class = self.resolve_class_name(mr.class_index)?; let name_and_type = self.get_name_and_type_info(mr.name_and_type_index)?; let name = self.get_string(name_and_type.name_index)?; let desc = self.get_string(name_and_type.descriptor_index)?; let desc = MethodDescriptor::parse(&desc)?; - Ok(MethodRef { class, name, desc }) + Ok(InterfaceMethodRef { class, name, desc }) } /*// (name, desc) @@ -133,6 +142,106 @@ pub trait ConstantPoolExt: ConstantPoolGet { }) } + fn resolve_either_ref(&self, idx: u16) -> Result { + match self.get_constant(idx)? { + ConstantPoolEntry::MethodRef(_) => { + Ok(InterfaceOrMethod::Method(self.resolve_method_ref(idx)?)) + } + ConstantPoolEntry::InterfaceMethodRef(_) => Ok(InterfaceOrMethod::Interface( + self.resolve_interface_method_ref(idx)?, + )), + _ => unreachable!(), + } + } + + fn resolve_loadable( + &self, + idx: u16, + class: Arc, + ) -> Result { + match self.get_constant(idx)? { + ConstantPoolEntry::Integer(x) => Ok(Loadable::Integer(*x)), + ConstantPoolEntry::Float(f) => Ok(Loadable::Float(*f)), + ConstantPoolEntry::Long(j) => Ok(Loadable::Long(*j)), + ConstantPoolEntry::Double(d) => Ok(Loadable::Double(*d)), + ConstantPoolEntry::Class(s) => Ok(Loadable::Class(self.get_string(s.name_index)?)), + ConstantPoolEntry::String(s) => Ok(Loadable::String(self.get_string(s.string_index)?)), + ConstantPoolEntry::MethodHandle(_) => Ok(Loadable::MethodHandle( + self.resolve_handle_kind(idx)?.into(), + )), + ConstantPoolEntry::MethodType(t) => { + let desc = self.get_string(t.descriptor_index)?; + let desc = MethodDescriptor::parse(&desc)?; + let typ: MethodType = desc.into(); + Ok(Loadable::MethodType(typ)) + } + ConstantPoolEntry::Dynamic(d) => { + let name_and_type = self.get_name_and_type_info(d.name_and_type_index)?; + let name = self.get_string(name_and_type.name_index)?; + let desc = + FieldDescriptor::parse(&self.get_string(name_and_type.descriptor_index)?)?; + let boot_index = d.bootstrap_method_attr_index; + let boot_method_info = class + .bootstrap_attribute + .as_ref() + .unwrap() + .bootstrap_methods + .get(boot_index as usize) + .unwrap(); + let args: Vec<_> = boot_method_info + .bootstrap_arguments + .iter() + .map(|idx| self.resolve_loadable(*idx, class.clone())) + .collect::>()?; + let boot_method = BootstrapMethod { + handle: self + .resolve_handle_kind(boot_method_info.bootstrap_method_ref)? + .into(), + args, + }; + + let dynamic = Dynamic { + method: boot_method, + name, + desc, + }; + Ok(Loadable::Dynamic(dynamic)) + } + + _ => Err(ConstantPoolError::Generic("Not loadable".into())), + } + } + + fn resolve_handle_kind(&self, idx: u16) -> Result { + let info = self.get_method_handle_info(idx)?; + let (kind, idx) = (info.reference_kind, info.reference_index); + match kind { + 1 => Ok(MethodHandleKind::GetField(self.resolve_field(idx)?)), + 2 => Ok(MethodHandleKind::GetStatic(self.resolve_field(idx)?)), + 3 => Ok(MethodHandleKind::PutField(self.resolve_field(idx)?)), + 4 => Ok(MethodHandleKind::PutStatic(self.resolve_field(idx)?)), + 5 => Ok(MethodHandleKind::InvokeVirtual( + self.resolve_method_ref(idx)?, + )), + 6 => Ok(MethodHandleKind::InvokeStatic( + self.resolve_method_ref(idx)?.into(), + )), + 7 => Ok(MethodHandleKind::InvokeSpecial( + self.resolve_method_ref(idx)?.into(), + )), + 8 => Ok(MethodHandleKind::NewInvokeSpecial( + self.resolve_method_ref(idx)?, + )), + 9 => Ok(MethodHandleKind::InvokeInterface( + self.resolve_interface_method_ref(idx)?, + )), + _ => Err(ConstantPoolError::Generic(format!( + "Invalid method handle kind: {}", + kind + ))), + } + } + fn parse_attribute(&self, a: AttributeInfo) -> Result { let name = self.get_string(a.attribute_name_index)?; // trace!("Parsing attribute with name: {}", name); diff --git a/crates/core/src/class_loader.rs b/crates/core/src/class_loader.rs index 5fb751d..4b7d552 100644 --- a/crates/core/src/class_loader.rs +++ b/crates/core/src/class_loader.rs @@ -6,7 +6,7 @@ use crate::class_file::{ ClassFile, ClassFlags, Constant, FieldData, FieldFlags, MethodData, MethodFlags, }; use crate::error::VmError; -use crate::{FieldType, MethodDescriptor}; +use crate::{FieldType, MethodDescriptor, VmSymbols}; use dashmap::DashMap; use deku::DekuContainerRead; use log::warn; @@ -250,8 +250,8 @@ impl ClassLoader { name }; let super_class = { - if this_class.eq("java/lang/Object") { - debug_assert_eq!(this_class, "java/lang/Object"); + if this_class.eq(VmSymbols::OBJECT) { + debug_assert_eq!(this_class, VmSymbols::OBJECT); debug_assert_eq!(class_file.super_class, 0u16); None } else { @@ -424,11 +424,10 @@ impl ClassLoader { // } pub fn create_array_class(&mut self, component: Arc) -> Arc { - // let name = format!("[{}", component.descriptor()); // e.g., "[Ljava/lang/String;" - let object_class: Arc = self.get_or_load("java/lang/Object", None).unwrap(); - let cloneable: Arc = self.get_or_load("java/lang/Cloneable", None).unwrap(); + let object_class: Arc = self.get_or_load(VmSymbols::OBJECT, None).unwrap(); + let cloneable: Arc = self.get_or_load(VmSymbols::CLONEABLE, None).unwrap(); let serializable: Arc = - self.get_or_load("java/io/Serializable", None).unwrap(); + self.get_or_load(VmSymbols::SERIALIZABLE, None).unwrap(); let name = match component.this_class.as_str() { "byte" => "[B".to_string(), "char" => "[C".to_string(), diff --git a/crates/core/src/error.rs b/crates/core/src/error.rs index a2c3e71..a0de563 100644 --- a/crates/core/src/error.rs +++ b/crates/core/src/error.rs @@ -7,7 +7,7 @@ pub enum VmError { ConstantPoolError(String), StackError(String), InvariantError(String), - Debug(&'static str), + Debug(String), DekuError(DekuError), LoaderError(String), ExecutionError, @@ -43,6 +43,38 @@ impl Display for VmError { } } } +const CARGO_LINE: &str = "\n --> {}:{}:{}\n{}"; +const BARE_LINE: &str = "\n{}:{}:{}\n{}"; +const PANIC_LINE: &str = "\nthread '{}' panicked at {}:{}:{}\n{}"; +#[macro_export] +macro_rules! dbg_panic { + ($msg:expr) => { + Result::Err(VmError::Debug(format!( + "\nthread '{}' panicked at {}:{}:{}\n{}", + thread::current().name().unwrap_or("unnamed"), + file!(), + line!(), + column!(), + $msg.to_string() + ))) + }; +} + +#[macro_export] +macro_rules! todoo { + ($msg:expr) => { + Result::Err(VmError::NotImplemented(format!( + "\n --> {}:{}:{}\n{}", + file!(), + line!(), + column!(), + $msg.to_string() + ))) + }; +} + +pub use dbg_panic; +pub use todoo; impl From for VmError { fn from(value: ConstantPoolError) -> Self { diff --git a/crates/core/src/frame/frame.rs b/crates/core/src/frame/frame.rs index aec5e72..0b8a692 100644 --- a/crates/core/src/frame/frame.rs +++ b/crates/core/src/frame/frame.rs @@ -1,7 +1,7 @@ use crate::class::RuntimeClass; use crate::class_file::attributes::{CodeAttribute, LineNumberTableEntry}; use crate::class_file::constant_pool::{ConstantPoolError, ConstantPoolExt, ConstantPoolGet}; -use crate::class_file::{Bytecode, ConstantPoolEntry, MethodRef}; +use crate::class_file::{Bytecode, ConstantPoolEntry, FieldDescriptor}; use crate::error::{StackTraceElement, VmError}; use crate::instructions::{Ops, WideData}; use crate::objects::ReferenceKind; @@ -10,17 +10,21 @@ use crate::prim::*; use crate::value::{Primitive, Value}; use crate::vm::Vm; use crate::{ - BaseType, FieldType, MethodDescriptor, VmThread, array_store, array_store_cast, binary_op, - convert_float_to_int, convert_int_narrow, convert_simple, float_cmp, if_int_cmp, if_int_zero, - int_div_rem, load, shift_op, store, unary_op, + BaseType, FieldType, VmSymbols, VmThread, array_store, array_store_cast, binary_op, + convert_float_to_int, convert_int_narrow, convert_simple, error, float_cmp, if_int_cmp, + if_int_zero, int_div_rem, jsr292, load, shift_op, store, unary_op, }; +use std::thread; use deku::DekuContainerRead; use log::{trace, warn}; +use crate::class_file::resolved::InterfaceOrMethod::Method; +use crate::class_file::resolved::{FieldRef, Loadable, MethodRef}; use crate::frame::local_vars::LocalVariables; use crate::frame::operand_stack::OperandStack; use std::fmt::{Display, Formatter}; +use std::process::id; use std::sync::Arc; /// Represents a JVM stack frame for method execution. @@ -47,7 +51,7 @@ pub struct Frame { bytecode: Bytecode, /// The thread executing this frame - thread: Arc, + pub(crate) thread: Arc, // The mod being invoked pub method_ref: MethodRef, @@ -114,7 +118,7 @@ impl Frame { line_number_table: Option>, ) -> Self { // Get current thread from thread-local storage - let thread = VmThread::current(&vm); + let thread = vm.current_thread(); let max_stack = code_attr.max_stack as usize; let max_local = code_attr.max_locals as usize; @@ -1124,7 +1128,7 @@ impl Frame { let result = self .thread - .invoke_virtual(method_ref, class.clone(), args)?; + .invoke_virtual(method_ref.into(), class.clone(), args)?; if let Some(val) = result { self.push(val) } @@ -1134,17 +1138,14 @@ impl Frame { Ops::invokespecial(index) => { // todo verify change to interface method ref - let method_ref = self - .pool - .resolve_method_ref(index) - .or_else(|e| self.pool.resolve_interface_method_ref(index).map_err(|_| e))?; + let either = self.pool.resolve_either_ref(index)?; // the 1 represents the receiver // arg width?????? - let args_count = method_ref.desc.parameters.len() + 1; + let args_count = either.desc().parameters.len() + 1; let args = self.stack.pop_n(args_count)?; - let result = self.thread.invoke(method_ref, args)?; + let result = self.thread.invoke(either, args)?; if let Some(val) = result { self.push(val) } @@ -1153,17 +1154,14 @@ impl Frame { } Ops::invokestatic(index) => { - let method_ref = self - .pool - .resolve_method_ref(index) - .or_else(|e| self.pool.resolve_interface_method_ref(index).map_err(|_| e))?; - let class = self.thread.get_class(&method_ref.class)?; + let either = self.pool.resolve_either_ref(index)?; + let class = self.thread.get_class(&either.class())?; self.thread.ensure_initialised(&class)?; - let args_count = method_ref.desc.parameters.len(); + let args_count = either.desc().parameters.len(); let args = self.stack.pop_n(args_count)?; - let result = self.thread.invoke(method_ref, args)?; + let result = self.thread.invoke(either, args)?; if let Some(val) = result { self.push(val) } @@ -1171,10 +1169,10 @@ impl Frame { } Ops::invokeinterface(index, _count, _zero) => { - let method_ref = self.pool.resolve_interface_method_ref(index)?; + let interface_method_ref = self.pool.resolve_interface_method_ref(index)?; // the 1 represents the receiver - let args_count = method_ref.desc.parameters.len() + 1; + let args_count = interface_method_ref.desc.parameters.len() + 1; let args = self.stack.pop_n(args_count)?; let refe = args .first() @@ -1183,9 +1181,9 @@ impl Frame { .expect("Must be ref"); let class = refe.class(); - let result = self - .thread - .invoke_virtual(method_ref, class.clone(), args)?; + let result = + self.thread + .invoke_virtual(interface_method_ref.into(), class.clone(), args)?; if let Some(val) = result { self.push(val) } @@ -1195,9 +1193,9 @@ impl Frame { Ops::invokedynamic(index, _b) => { debug_assert_eq!(_b, 0u16); + /* - // Check if already resolved (you'll want to cache this) - // For now, let's just resolve it every time + // TODO: Cache resolved CallSites let dyninfo = self.pool.get_invoke_dynamic_info(index)?; let bootstrap_attr = self.class.bootstrap_attribute.as_ref().unwrap(); @@ -1212,84 +1210,266 @@ impl Frame { let call_site_method_type = MethodDescriptor::parse(&call_site_desc) .map_err(ConstantPoolError::DescriptorParseError)?; - // The bootstrap method handle reference - let bsm_handle = self.pool.get_method_handle_info(bsm.bootstrap_method_ref)?; - let kind = bsm_handle.reference_kind; - let heck = self.pool.resolve_method_ref(bsm_handle.reference_index)?; - println!("Bootstrap method handle: kind={:?}, ref={:?}", kind, heck); - // Resolve static arguments from constant pool - let static_args: Vec = Vec::new(); - for arg_index in &bsm.bootstrap_arguments { - let info = self.pool.get_string_info(*arg_index)?; - let string = self.pool.get_string(info.string_index)?; - println!("{}", string); - // static_args.push(arg); - } + // 1. Lookup + let lookup = { + let refe = MethodRef { + class: "java/lang/invoke/MethodHandles".to_string(), + name: "lookup".to_string(), + desc: MethodDescriptor::parse("()Ljava/lang/invoke/MethodHandles$Lookup;") + .unwrap(), + }; + self.thread.invoke(refe, vec![])?.unwrap() + }; - let class_objects = call_site_method_type - .parameters - .iter() - .map(|param| { - let name = param.as_class_name(); - let klass = self.thread.get_class(&name)?; - let obj = self.thread.gc.read().get(*klass.mirror.get().unwrap()); - Ok(Some(obj)) - }) - .collect::>, VmError>>()?; - let class_array_class = self.thread.get_class("[Ljava/lang/Class;")?; + // 2. Call site name as String + let name_string = self.thread.intern_string(&call_site_name); - let ptypes = self + // 3. Call site MethodType + let call_site_mt: Value = { + let class_objects: Vec> = call_site_method_type + .parameters + .iter() + .map(|param| { + let name = param.as_class_name(); + let klass = self.thread.get_class(&name)?; + let obj = self.thread.gc.read().get(*klass.mirror.get().unwrap()); + Ok(Some(obj)) + }) + .collect::>()?; + + let class_array_class = self.thread.get_class("[Ljava/lang/Class;")?; + let ptypes = self + .thread + .gc + .write() + .new_object_array_from(class_array_class, class_objects.into_boxed_slice()); + + let rtype: Value = match &call_site_method_type.return_type { + Some(rt) => { + let klass = self.thread.get_class(&rt.as_class_name())?; + self.thread + .gc + .read() + .get(*klass.mirror.get().unwrap()) + .into() + } + None => { + let void_class = self.thread.get_class("void")?; + self.thread + .gc + .read() + .get(*void_class.mirror.get().unwrap()) + .into() + } + }; + + let mt_ref = MethodRef { + class: "java/lang/invoke/MethodType".to_string(), + name: "methodType".to_string(), + desc: MethodDescriptor::parse( + "(Ljava/lang/Class;[Ljava/lang/Class;)Ljava/lang/invoke/MethodType;", + ) + .unwrap(), + }; + self.thread + .invoke(mt_ref, vec![rtype, ptypes.into()])? + .unwrap() + .into() + }; + + // 4. Bootstrap MethodHandle + let bsm_method_handle: Value = { + let bsm_handle_info = + self.pool.get_method_handle_info(bsm.bootstrap_method_ref)?; + let kind = bsm_handle_info.reference_kind; + let method_ref = self + .pool + .resolve_method_ref(bsm_handle_info.reference_index)?; + + let bsm_class = self.thread.get_class(&method_ref.class)?; + let bsm_class_mirror: Value = self + .thread + .gc + .read() + .get(*bsm_class.mirror.get().unwrap()) + .into(); + + let bsm_name: Value = self.thread.intern_string(&method_ref.name).into(); + + // MethodType for the bootstrap method itself + let bsm_desc = method_ref.desc; + let bsm_mt: Value = { + let params: Vec> = bsm_desc + .parameters + .iter() + .map(|p| { + let klass = self.thread.get_class(&p.as_class_name())?; + Ok(Some( + self.thread.gc.read().get(*klass.mirror.get().unwrap()), + )) + }) + .collect::>()?; + + let class_array_class = self.thread.get_class("[Ljava/lang/Class;")?; + let ptypes = self + .thread + .gc + .write() + .new_object_array_from(class_array_class, params.into_boxed_slice()); + + let rtype: Value = match &bsm_desc.return_type { + Some(rt) => { + let klass = self.thread.get_class(&rt.as_class_name())?; + self.thread + .gc + .read() + .get(*klass.mirror.get().unwrap()) + .into() + } + None => { + let void_class = self.thread.get_class("void")?; + self.thread + .gc + .read() + .get(*void_class.mirror.get().unwrap()) + .into() + } + }; + + let mt_ref = MethodRef { + class: "java/lang/invoke/MethodType".to_string(), + name: "methodType".to_string(), + desc: MethodDescriptor::parse( + "(Ljava/lang/Class;[Ljava/lang/Class;)Ljava/lang/invoke/MethodType;", + ) + .unwrap(), + }; + self.thread + .invoke(mt_ref, vec![rtype, ptypes.into()])? + .unwrap() + .into() + }; + + // Call appropriate Lookup.findXxx based on kind + match kind { + 6 => { + // REF_invokeStatic + let find_static = MethodRef { + class: "java/lang/invoke/MethodHandles$Lookup".to_string(), + name: "findStatic".to_string(), + desc: MethodDescriptor::parse("(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;").unwrap(), + }; + self.thread + .invoke_virtual( + find_static, + lookup.clone(), + vec![bsm_class_mirror, bsm_name, bsm_mt], + )? + .unwrap() + .into() + } + 5 | 9 => { + // REF_invokeVirtual | REF_invokeInterface + let find_virtual = MethodRef { + class: "java/lang/invoke/MethodHandles$Lookup".to_string(), + name: "findVirtual".to_string(), + desc: MethodDescriptor::parse("(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;").unwrap(), + }; + self.thread + .invoke_virtual( + find_virtual, + lookup.clone(), + vec![bsm_class_mirror, bsm_name, bsm_mt], + )? + .unwrap() + .into() + } + // Add other kinds as needed + _ => todo!("MethodHandle reference kind {}", kind), + } + }; + + // 5. Static arguments + let static_args = self.resolve_bootstrap_args(&bsm.bootstrap_arguments)?; + + // 6. Build args array for invokeWithArguments: [lookup, name, methodType, ...staticArgs] + let mut all_bsm_args: Vec = + vec![lookup.into(), name_string.into(), call_site_mt]; + all_bsm_args.extend(static_args); + + // Box primitives and create Object[] + let boxed_args: Vec> = all_bsm_args + .into_iter() + .map(|v| self.box_value(v)) + .collect::>()?; + + let obj_array_class = self.thread.get_class("[Ljava/lang/Object;")?; + let args_array = self .thread .gc .write() - .new_object_array_from(class_array_class, class_objects.into_boxed_slice()); - let pval: Value = ptypes.clone().into(); + .new_object_array_from(obj_array_class, boxed_args.into_boxed_slice()); - println!("=================="); - println!("{}", pval); - println!("=================="); - let return_type = call_site_method_type.return_type.unwrap(); - let ret = return_type.as_class_name(); - let ret_klass = self.thread.get_class(&ret)?; - let rtype = self.thread.gc.read().get(*ret_klass.mirror.get().unwrap()); - let mt_cn_ref = MethodRef { - class: "java/lang/invoke/MethodType".to_string(), - name: "methodType".to_string(), - desc: MethodDescriptor::method_type(), + // 7. Invoke bootstrap: bsm.invokeWithArguments(Object[]) + let invoke_ref = MethodRef { + class: "java/lang/invoke/MethodHandle".to_string(), + name: "invokeWithArguments".to_string(), + desc: MethodDescriptor::parse("([Ljava/lang/Object;)Ljava/lang/Object;") + .unwrap(), }; - let mt = self + let call_site = self .thread - .invoke(mt_cn_ref, vec![rtype.into(), ptypes.into()])? - .unwrap(); + .invoke_virtual(invoke_ref, bsm_handle, vec![args_array.into()])? + .ok_or_else(|| VmError::InvariantError("Bootstrap returned null".into()))?; - // Pop runtime arguments from stack (based on call site descriptor) - let runtime_arg_count = call_site_method_type.parameters.len(); - let runtime_args = self.stack.pop_n(runtime_arg_count)?; - - // Now we need to: - // 1. Create a MethodHandles.Lookup for current class --- - // 2. Create a MethodType from call_site_desc - // 3. Invoke the bootstrap method with (Lookup, String name, MethodType, ...static_args) - // 4. Get back a CallSite - // 5. Extract the MethodHandle from CallSite.getTarget() - // 6. Invoke that MethodHandle with runtime_args - - //wait i think i can just call jvm methods here - let mhc = self.thread.get_class("java/lang/invoke/MethodHandles")?; - let refe = MethodRef { - class: "java/lang/invoke/MethodHandles".to_string(), - name: "lookup".to_string(), - desc: MethodDescriptor::lookup(), + // 8. Get target handle: callSite.getTarget() + let get_target = MethodRef { + class: "java/lang/invoke/CallSite".to_string(), + name: "getTarget".to_string(), + desc: MethodDescriptor::parse("()Ljava/lang/invoke/MethodHandle;").unwrap(), }; + let target_handle = self + .thread + .invoke_virtual(get_target, call_site, vec![])? + .ok_or_else(|| VmError::InvariantError("getTarget returned null".into()))?; - let lookup = self.thread.invoke(refe, Vec::new())?.unwrap(); - // Full implementation requires MethodHandle invocation support - println!("{}", lookup); - todo!( - // "Full invokedynamic - bootstrap: {}::{}", - // bsm_handle.class_name, - // bsm_handle.method_name + // 9. Pop runtime arguments + let runtime_arg_count = call_site_method_type.parameters.len(); + let runtime_args: Vec = self.stack.pop_n(runtime_arg_count)?; + + // 10. Invoke target with runtime args + let boxed_runtime: Vec> = runtime_args + .into_iter() + .map(|v| self.box_value(v)) + .collect::>()?; + + let runtime_array = self + .thread + .gc + .write() + .new_object_array_from(obj_array_class, boxed_runtime.into_boxed_slice()); + + let result = self.thread.invoke_virtual( + invoke_ref.clone(), + target_handle, + vec![runtime_array.into()], + )?; + + // 11. Push result (unboxing if needed) + if let Some(return_type) = &call_site_method_type.return_type { + let result_val = result + .ok_or_else(|| VmError::InternalError("Expected return value".into()))?; + let unboxed = self.unbox_if_primitive(result_val, return_type)?; + self.push(unboxed); + } + + Ok(ExecutionResult::Continue)*/ + // todo!( + // "Invoke dynamic needs major constant pool and vm invoke method handle support" + // ); + error::dbg_panic!( + "Invoke dynamic needs major constant pool and vm invoke method handle support" ) } @@ -1516,6 +1696,44 @@ impl Frame { } fn load_constant(&mut self, index: u16) -> Result { + let thing = self.pool.resolve_loadable(index, self.class.clone())?; + let resolved = match thing { + Loadable::Integer(x) => Value::from(x), + Loadable::Float(x) => Value::from(x), + Loadable::Long(x) => Value::from(x), + Loadable::Double(x) => Value::from(x), + Loadable::Class(name) => { + let class = self.thread.get_class(&name)?; + let class_ref = self.thread.gc.read().get( + *class + .mirror + .get() + .expect(&format!("Mirror unintialised {}", class.this_class)), + ); + Value::from(class_ref) + } + Loadable::String(string) => Value::from(self.thread.intern_string(&string)), + Loadable::MethodHandle(handle_info) => { + let method_handle = jsr292::MethodHandle::from_info(self, handle_info)?; + method_handle.into() + } + Loadable::MethodType(x) => { + let refe = MethodRef::from_symbols( + VmSymbols::CLASS, + "fromDescriptor", + &vec![VmSymbols::STRING, VmSymbols::CLASSLOADER], + Some(VmSymbols::METHOD_TYPE), + ); + let string = self.thread.intern_string(&*x.desc.param_string()); + let thing = self + .thread + .invoke(refe, vec![Value::from(string), Value::NULL])? + .unwrap(); + Value::from(thing) + } + Loadable::Dynamic(dynamic_info) => error::dbg_panic!("Dynamic, more so")?, + }; + let thing = self.pool.get_constant(index.to_owned())?; trace!("\tLoading constant: {}", thing); let resolved = match thing { @@ -1583,6 +1801,90 @@ impl Frame { self.push(resolved); Ok(ExecutionResult::Continue) } + + fn resolve_loadable(&self, loadable: Loadable) -> Result { + match loadable { + Loadable::Integer(i) => Ok(Value::from(i)), + Loadable::Float(f) => Ok(Value::from(f)), + Loadable::Long(l) => Ok(Value::from(l)), + Loadable::Double(d) => Ok(Value::from(d)), + Loadable::Class(class_name) => { + let class = self.thread.get_class(&class_name)?; + let class_ref = self.thread.gc.read().get( + *class + .mirror + .get() + .expect(&format!("Mirror unintialised {}", class.this_class)), + ); + Ok(Value::from(class_ref)) + } + Loadable::String(string) => Ok(Value::from(self.thread.intern_string(&string))), + Loadable::MethodHandle(handle_info) => { + let method_handle = jsr292::MethodHandle::from_info(self, handle_info)?; + Ok(method_handle.into()) + } + Loadable::MethodType(method_type_info) => { + let refe = MethodRef::from_symbols( + VmSymbols::CLASS, + "fromDescriptor", + &vec![VmSymbols::STRING, VmSymbols::CLASSLOADER], + Some(VmSymbols::METHOD_TYPE), + ); + let string = self + .thread + .intern_string(&*method_type_info.desc.param_string()); + let thing = self + .thread + .invoke(refe, vec![Value::from(string), Value::NULL])? + .unwrap(); + Ok(thing) + } + Loadable::Dynamic(dynamic_info) => { + // first r is examined to determine bootstrap method and args + + // second args are packed into an array and the bootstrap method is invoked + // args[0] is the Lookup + // args[1] is the name + // args[2] methodType + + //third the result is validated and used as the result of resolution + + let method_handle = + jsr292::MethodHandle::from_info(self, dynamic_info.method.handle)?; + let args: Vec = dynamic_info + .method + .args + .into_iter() + .map(|loadable| self.resolve_loadable(loadable)) + .collect::, _>>()?; + + let lookup = { + let method_ref = MethodRef::from_symbols( + VmSymbols::METHOD_HANDLES, + "lookup", + &vec![], + Some(VmSymbols::METHOD_HANDLES_LOOKUP), + ); + let lookup = self.thread.invoke(method_ref, vec![])?; + lookup.unwrap() + }; + + let name = { + let name = self.thread.intern_string(&dynamic_info.name); + Value::from(name) + }; + + let method_type = { + // gotta like smash this into a method type or sumthin + dynamic_info.desc + } + + Err(VmError::Debug( + "Dynamic loading not yet implemented".to_string(), + ))? + } + } + } } #[cfg(test)] diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index 0fdc17a..586007d 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -15,7 +15,7 @@ //! - [`MethodDescriptor`] - Method signature information //! - [`FieldType`] - Field type information -use crate::class_file::MethodRef; +use crate::class_file::resolved::MethodRef; pub use crate::thread::VmThread; use deku_derive::{DekuRead, DekuWrite}; use itertools::Itertools; @@ -24,6 +24,7 @@ use std::cell::RefCell; use std::fmt::{Debug, Display, Formatter}; use std::str::FromStr; use value::Value; +use windows::Win32::System::Threading::GetCurrentThreadId; mod bimage; mod class; @@ -32,15 +33,18 @@ mod class_loader; pub mod error; mod frame; mod instructions; +mod jsr292; mod macros; pub mod native; pub mod objects; mod prim; mod rng; +mod symbols; mod thread; pub mod value; pub mod vm; +pub use symbols::vm_symbols as VmSymbols; /// Unique identifier for a VM thread. /// /// Each VmThread is assigned a unique ThreadId when created. This ID is used to: @@ -49,6 +53,11 @@ pub mod vm; /// - Support future multi-threading when Java threads map to OS threads #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] pub struct ThreadId(pub u64); +impl From for ThreadId { + fn from(value: u64) -> Self { + Self(value) + } +} /// Represents JVM primitive types used in field and method descriptors. /// @@ -138,7 +147,24 @@ pub struct MethodDescriptor { // none = void/v pub return_type: Option, } +fn sanitise(name: &str) -> Cow<'_, str> { + match name { + "int" => "I".into(), + "long" => "J".into(), + "short" => "S".into(), + "char" => "C".into(), + "byte" => "B".into(), + "float" => "F".into(), + "double" => "D".into(), + "boolean" => "Z".into(), + "void" => "V".into(), + // Already a descriptor + s if s.starts_with('[') || s.starts_with('L') || s.len() == 1 => name.into(), + + _ => format!("L{name};").into(), + } +} impl MethodDescriptor { pub fn void() -> Self { Self { @@ -146,14 +172,12 @@ impl MethodDescriptor { return_type: None, } } - pub fn method_type() -> Self { - MethodDescriptor::parse( - "(Ljava/lang/Class;[Ljava/lang/Class;)Ljava/lang/invoke/MethodType;", - ) - .unwrap() - } - pub fn lookup() -> Self { - MethodDescriptor::parse("()Ljava/lang/invoke/MethodHandles$Lookup;").unwrap() + pub fn from_symbols(parameters: &[&str], return_type: Option<&str>) -> Self { + let ret = return_type.unwrap_or("V"); + let return_type = sanitise(ret); + let parameters = parameters.iter().map(|p| sanitise(p)).join(""); + let desc: String = format!("({parameters}){return_type}"); + MethodDescriptor::parse(&desc).unwrap() } pub fn psvmsa() -> Self { MethodDescriptor::parse("([Ljava/lang/String;)V").unwrap() @@ -167,6 +191,20 @@ impl MethodDescriptor { } } +impl Display for MethodDescriptor { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "(")?; + for param in &self.parameters { + write!(f, "{}", param)?; + } + write!(f, ")")?; + match &self.return_type { + Some(ret) => write!(f, "{}", ret), + None => write!(f, "V"), + } + } +} + impl Display for BaseType { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { @@ -182,30 +220,6 @@ impl Display for BaseType { } } -impl Display for FieldType { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match self { - FieldType::Base(base) => write!(f, "{}", base), - FieldType::ClassType(name) => write!(f, "L{};", name), - FieldType::ArrayType(component) => write!(f, "[{}", component), - } - } -} - -impl Display for MethodDescriptor { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "(")?; - for param in &self.parameters { - write!(f, "{}", param)?; - } - write!(f, ")")?; - match &self.return_type { - Some(ret) => write!(f, "{}", ret), - None => write!(f, "V"), - } - } -} - pub fn stack_used() -> usize { static STACK_BASE: std::sync::OnceLock = std::sync::OnceLock::new(); let local = 0u8; @@ -233,10 +247,23 @@ pub enum FieldType { ArrayType(Box), } +impl Display for FieldType { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + FieldType::Base(base) => write!(f, "{}", base), + FieldType::ClassType(name) => write!(f, "L{};", name), + FieldType::ArrayType(inner) => write!(f, "[{}", inner), + } + } +} + impl FieldType { pub fn bool() -> Self { Self::Base(BaseType::Boolean) } + pub fn from_symbols(class_name: &str) -> Self { + Self::parse(&sanitise(class_name)).unwrap() + } } impl FieldType { @@ -336,3 +363,13 @@ fn set_last_native(name: &str) { pub fn get_last_native() -> String { LAST_NATIVE_SYMBOL.with(|cell| cell.borrow().clone()) } + +#[cfg(unix)] +pub fn os_thread_id() -> u64 { + unsafe { libc::pthread_self() as u64 } +} + +#[cfg(windows)] +pub fn os_thread_id() -> u64 { + unsafe { GetCurrentThreadId() as u64 } +} diff --git a/crates/core/src/macros.rs b/crates/core/src/macros.rs index 23e5f67..c20baa9 100644 --- a/crates/core/src/macros.rs +++ b/crates/core/src/macros.rs @@ -295,3 +295,9 @@ macro_rules! shift_op { Ok(ExecutionResult::Continue) }}; } +#[macro_export] +macro_rules! location { + () => { + format!("{}:{}:{}", file!(), line!(), column!()) + }; +} diff --git a/crates/core/src/main.rs b/crates/core/src/main.rs index 584dee6..6d35a33 100644 --- a/crates/core/src/main.rs +++ b/crates/core/src/main.rs @@ -17,13 +17,13 @@ fn main() { } fn run() { - env_logger::Builder::from_default_env() - .filter_level(LevelFilter::Trace) - .filter_module("deku", LevelFilter::Warn) - .filter_module("roast_vm_core::class_file::class_file", LevelFilter::Info) - .filter_module("roast_vm_core::class_file::attributes", LevelFilter::Info) - .filter_module("roast_vm_core::instructions", LevelFilter::Info) - .init(); + // env_logger::Builder::from_default_env() + // .filter_level(LevelFilter::Trace) + // .filter_module("deku", LevelFilter::Warn) + // .filter_module("roast_vm_core::class_file::class_file", LevelFilter::Info) + // .filter_module("roast_vm_core::class_file::attributes", LevelFilter::Info) + // .filter_module("roast_vm_core::instructions", LevelFilter::Info) + // .init(); stack_used(); let vm = Vm::new(); fetch_libs(&vm); diff --git a/crates/core/src/native/jni.rs b/crates/core/src/native/jni.rs index 3552e04..599b72c 100644 --- a/crates/core/src/native/jni.rs +++ b/crates/core/src/native/jni.rs @@ -3,13 +3,14 @@ #![feature(c_variadic)] #![allow(unsafe_op_in_unsafe_fn)] use crate::class::RuntimeClass; -use crate::class_file::{FieldData, FieldRef}; +use crate::class_file::FieldData; +use crate::class_file::resolved::FieldRef; use crate::objects::array::ArrayReference; use crate::objects::object::{ObjectReference, ReferenceKind}; use crate::prim::jboolean; use crate::thread::VmThread; use crate::value::{Primitive, Value}; -use crate::{BaseType, FieldType, MethodDescriptor, generate_jni_short_name}; +use crate::{BaseType, FieldType, MethodDescriptor, VmSymbols, generate_jni_short_name}; use jni::sys::*; use jni::sys::{JNIEnv, jstring}; use jni::sys::{JNINativeInterface_, jclass, jint, jobject}; @@ -321,7 +322,7 @@ unsafe extern "system" fn get_string_utfchars( let obj = obj_ref.lock(); let field_ref = FieldRef { - class: "java/lang/String".to_string(), + class: VmSymbols::STRING.to_string(), name: "value".to_string(), desc: FieldType::ArrayType(Box::new(FieldType::Base(BaseType::Byte))), }; @@ -1706,11 +1707,11 @@ unsafe extern "system" fn new_string_utf(env: *mut JNIEnv, utf: *const c_char) - let str_ref = if intern { thread.intern_string(str) } else { - let Ok(string_class) = thread.get_class("java/lang/String") else { + let Ok(string_class) = thread.get_class(VmSymbols::STRING) else { return ptr::null_mut(); }; - let Ok(byte_array_class) = thread.get_class("[B") else { + let Ok(byte_array_class) = thread.get_class(VmSymbols::BYTE_ARRAY) else { return ptr::null_mut(); }; diff --git a/crates/core/src/objects/array.rs b/crates/core/src/objects/array.rs index 937c424..5822f4d 100644 --- a/crates/core/src/objects/array.rs +++ b/crates/core/src/objects/array.rs @@ -2,6 +2,7 @@ use crate::class::RuntimeClass; use crate::error::VmError; use crate::objects::object::{Reference, ReferenceKind}; use crate::prim::Primitive; +use crate::value::ClassName; use jni::sys::{jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jshort}; use parking_lot::Mutex; use std::ops::{Deref, DerefMut}; @@ -90,6 +91,12 @@ pub struct Array { pub backing: Box<[T]>, } +impl ClassName for Array { + fn class_name(&self) -> String { + self.class.this_class.clone() + } +} + impl Array where T: ArrayValue + Clone + Default, diff --git a/crates/core/src/objects/object.rs b/crates/core/src/objects/object.rs index b8a4960..72c1032 100644 --- a/crates/core/src/objects/object.rs +++ b/crates/core/src/objects/object.rs @@ -1,5 +1,6 @@ +use crate::VmSymbols; use crate::class::RuntimeClass; -use crate::class_file::FieldRef; +use crate::class_file::resolved::FieldRef; use crate::error::VmError; use crate::objects::array::{ArrayReference, ObjectArrayReference, PrimitiveArrayReference}; use crate::prim::{jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jshort}; @@ -46,7 +47,7 @@ impl Object { impl Display for Object { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - if self.class.this_class == "java/lang/String" {} + if self.class.this_class == VmSymbols::STRING {} write!(f, "Object[id={}, class={}]", self.id, self.class.this_class) } } @@ -121,7 +122,7 @@ impl Display for ReferenceKind { let id = match self { ReferenceKind::ObjectReference(x) => unsafe { let guard = x.make_guard_unchecked(); - if guard.class.this_class == "java/lang/String" + if guard.class.this_class == VmSymbols::STRING && let Some(field) = guard.fields.get("value") && let Value::Reference(Some(ReferenceKind::ArrayReference( ArrayReference::Byte(actual), diff --git a/crates/core/src/objects/object_manager.rs b/crates/core/src/objects/object_manager.rs index 13cc941..d50e1cf 100644 --- a/crates/core/src/objects/object_manager.rs +++ b/crates/core/src/objects/object_manager.rs @@ -7,7 +7,7 @@ use crate::objects::object::{ Object, ObjectReference, Reference, ReferenceKind, string_from_bytes, }; use crate::value::Value; -use crate::{BaseType, ThreadId}; +use crate::{BaseType, ThreadId, VmSymbols}; use dashmap::DashMap; use jni::sys::{jint, jlong}; use parking_lot::{Condvar, Mutex}; @@ -403,7 +403,7 @@ impl ObjectManager { ReferenceKind::ArrayReference(arr) => self.clone_array(arr), ReferenceKind::ObjectReference(obj) => { let class = obj.lock().class.clone(); - if !class.implements("java/lang/Cloneable") { + if !class.implements(VmSymbols::CLONEABLE) { return Err(VmError::InvariantError("CloneNotSupported".to_string())); } self.clone_instance(obj) diff --git a/crates/core/src/symbols.rs b/crates/core/src/symbols.rs new file mode 100644 index 0000000..9498b93 --- /dev/null +++ b/crates/core/src/symbols.rs @@ -0,0 +1,103 @@ +pub mod vm_symbols { + // Core + pub const OBJECT: &str = "java/lang/Object"; + pub const CLASS: &str = "java/lang/Class"; + pub const STRING: &str = "java/lang/String"; + pub const SYSTEM: &str = "java/lang/System"; + pub const THREAD: &str = "java/lang/Thread"; + pub const THREAD_GROUP: &str = "java/lang/ThreadGroup"; + pub const THREAD_FIELD_HOLDER: &str = "java/lang/Thread$FieldHolder"; + pub const CLASSLOADER: &str = "java/lang/ClassLoader"; + pub const THROWABLE: &str = "java/lang/Throwable"; + pub const MODULE: &str = "java/lang/Module"; + + // Boxed primitives + pub const BOOLEAN: &str = "java/lang/Boolean"; + pub const BYTE: &str = "java/lang/Byte"; + pub const CHARACTER: &str = "java/lang/Character"; + pub const SHORT: &str = "java/lang/Short"; + pub const INTEGER: &str = "java/lang/Integer"; + pub const LONG: &str = "java/lang/Long"; + pub const FLOAT: &str = "java/lang/Float"; + pub const DOUBLE: &str = "java/lang/Double"; + pub const VOID: &str = "java/lang/Void"; + pub const NUMBER: &str = "java/lang/Number"; + + // Exceptions + pub const EXCEPTION: &str = "java/lang/Exception"; + pub const RUNTIME_EXCEPTION: &str = "java/lang/RuntimeException"; + pub const NULL_POINTER_EXCEPTION: &str = "java/lang/NullPointerException"; + pub const ARRAY_INDEX_OUT_OF_BOUNDS_EXCEPTION: &str = + "java/lang/ArrayIndexOutOfBoundsException"; + pub const CLASS_NOT_FOUND_EXCEPTION: &str = "java/lang/ClassNotFoundException"; + pub const ILLEGAL_ARGUMENT_EXCEPTION: &str = "java/lang/IllegalArgumentException"; + pub const ILLEGAL_STATE_EXCEPTION: &str = "java/lang/IllegalStateException"; + pub const UNSUPPORTED_OPERATION_EXCEPTION: &str = "java/lang/UnsupportedOperationException"; + pub const CLASS_CAST_EXCEPTION: &str = "java/lang/ClassCastException"; + pub const ARITHMETIC_EXCEPTION: &str = "java/lang/ArithmeticException"; + pub const INDEX_OUT_OF_BOUNDS_EXCEPTION: &str = "java/lang/IndexOutOfBoundsException"; + + // Errors + pub const ERROR: &str = "java/lang/Error"; + pub const LINKAGE_ERROR: &str = "java/lang/LinkageError"; + pub const NO_CLASS_DEF_FOUND_ERROR: &str = "java/lang/NoClassDefFoundError"; + pub const NO_SUCH_METHOD_ERROR: &str = "java/lang/NoSuchMethodError"; + pub const NO_SUCH_FIELD_ERROR: &str = "java/lang/NoSuchFieldError"; + pub const OUT_OF_MEMORY_ERROR: &str = "java/lang/OutOfMemoryError"; + pub const STACK_OVERFLOW_ERROR: &str = "java/lang/StackOverflowError"; + pub const ABSTRACT_METHOD_ERROR: &str = "java/lang/AbstractMethodError"; + pub const INCOMPATIBLE_CLASS_CHANGE_ERROR: &str = "java/lang/IncompatibleClassChangeError"; + pub const EXCEPTION_IN_INITIALIZER_ERROR: &str = "java/lang/ExceptionInInitializerError"; + pub const VERIFY_ERROR: &str = "java/lang/VerifyError"; + pub const INTERNAL_ERROR: &str = "java/lang/InternalError"; + + // Reflection + pub const FIELD: &str = "java/lang/reflect/Field"; + pub const METHOD: &str = "java/lang/reflect/Method"; + pub const CONSTRUCTOR: &str = "java/lang/reflect/Constructor"; + + // IO + pub const SERIALIZABLE: &str = "java/io/Serializable"; + pub const PRINT_STREAM: &str = "java/io/PrintStream"; + pub const INPUT_STREAM: &str = "java/io/InputStream"; + pub const OUTPUT_STREAM: &str = "java/io/OutputStream"; + pub const FILE_DESCRIPTOR: &str = "java/io/FileDescriptor"; + + // Other commonly needed + pub const CLONEABLE: &str = "java/lang/Cloneable"; + pub const COMPARABLE: &str = "java/lang/Comparable"; + pub const ENUM: &str = "java/lang/Enum"; + pub const RECORD: &str = "java/lang/Record"; + pub const STACKTRACEELEMENT: &str = "java/lang/StackTraceElement"; + + // Primitive array descriptors + pub const BOOLEAN_ARRAY: &str = "[Z"; + pub const BYTE_ARRAY: &str = "[B"; + pub const CHAR_ARRAY: &str = "[C"; + pub const SHORT_ARRAY: &str = "[S"; + pub const INT_ARRAY: &str = "[I"; + pub const LONG_ARRAY: &str = "[J"; + pub const FLOAT_ARRAY: &str = "[F"; + pub const DOUBLE_ARRAY: &str = "[D"; + + // Object array (common one) + pub const OBJECT_ARRAY: &str = "[Ljava/lang/Object;"; + pub const STRING_ARRAY: &str = "[Ljava/lang/String;"; + + // java/lang/invoke + pub const METHOD_HANDLE: &str = "java/lang/invoke/MethodHandle"; + pub const METHOD_HANDLES: &str = "java/lang/invoke/MethodHandles"; + pub const METHOD_HANDLES_LOOKUP: &str = "java/lang/invoke/MethodHandles$Lookup"; + pub const METHOD_TYPE: &str = "java/lang/invoke/MethodType"; + pub const CALL_SITE: &str = "java/lang/invoke/CallSite"; + + // java/lang/constant + pub const CONSTANT_METHOD_HANDLE_DESC: &str = "java/lang/constant/MethodHandleDesc"; + pub const CONSTANT_METHOD_TYPE: &str = "java/lang/constant/MethodTypeDesc"; + pub const CONSTANT_CLASS_DESC: &str = "java/lang/constant/ClassDesc"; + pub const CONSTANT_STRING_DESC: &str = "java/lang/constant/StringDesc"; + pub const CONSTANT_DIRECT_METHOD_HANDLE_DESC_KIND: &str = + "java/lang/constant/DirectMethodHandleDesc$Kind"; + pub const CONSTANT_DIRECT_METHOD_HANDLE_DESC: &str = + "java/lang/constant/DirectMethodHandleDesc"; +} diff --git a/crates/core/src/thread.rs b/crates/core/src/thread.rs index fb2917b..825c8b5 100644 --- a/crates/core/src/thread.rs +++ b/crates/core/src/thread.rs @@ -1,5 +1,6 @@ +#![feature(thread_id_value)] use crate::class::{ClassRef, InitState, RuntimeClass}; -use crate::class_file::{MethodData, MethodRef}; +use crate::class_file::MethodData; use crate::class_loader::{ClassLoader, LoaderRef}; use crate::error::VmError; use crate::frame::Frame; @@ -7,9 +8,10 @@ use crate::native::jni::create_jni_function_table; use crate::objects::object::{ObjectReference, ReferenceKind}; use crate::objects::object_manager::ObjectManager; use crate::value::{Primitive, Value}; -use crate::vm::Vm; +use crate::vm::{Init, Vm}; use crate::{ - BaseType, FieldType, MethodDescriptor, ThreadId, generate_jni_method_name, set_last_native, + BaseType, FieldType, MethodDescriptor, ThreadId, VmSymbols, generate_jni_method_name, + os_thread_id, set_last_native, }; use jni::sys::{JNIEnv, jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jobject, jshort}; use libffi::middle::*; @@ -22,7 +24,7 @@ use std::collections::VecDeque; use std::sync::atomic::Ordering; use std::sync::{Arc, OnceLock}; -use std::thread; +use crate::class_file::resolved::{InterfaceOrMethod, MethodRef}; static INIT_LOGGER: Once = Once::new(); type MethodCallResult = Result, VmError>; @@ -48,7 +50,8 @@ pub struct VmThread { impl VmThread { pub fn new(vm: Arc, loader: Option) -> Arc { - let id = ThreadId(vm.next_id.fetch_add(1, Ordering::SeqCst)); + // let id = ThreadId(vm.next_id.fetch_add(1, Ordering::SeqCst)); + let id = os_thread_id().into(); let loader = loader.unwrap_or(vm.loader.clone()); let gc = vm.gc.clone(); @@ -67,23 +70,22 @@ impl VmThread { }) } - /// Get current thread ID from thread-local storage + /*/// Get current thread ID from thread-local storage pub fn current_id() -> ThreadId { CURRENT_THREAD_ID.with(|cell| cell.borrow().expect("No current thread set")) } /// Set current thread ID for this OS thread pub fn set_current(id: ThreadId) { - CURRENT_THREAD_ID.with(|cell| { - *cell.borrow_mut() = Some(id); - }); - } + CURRENT_THREAD_ID.set(Some(id)); + }*/ - /// Get current thread from VM using thread-local storage + // put this on the vm lol. + /*/// Get current thread from VM using thread-local storage pub fn current(vm: &Arc) -> Arc { let id = Self::current_id(); vm.threads.get(&id).unwrap().clone() - } + }*/ /*/// Get or resolve a class, ensuring it and its dependencies are initialised. /// Follows JVM Spec 5.5 for recursive initialisation handling. @@ -109,14 +111,16 @@ impl VmThread { pub fn get_class(&self, what: &str) -> Result, VmError> { let class = self.loader.lock().get_or_load(what, None)?; - self.create_mirror_class(&class)?; + if self.vm.phase.load(Ordering::Acquire) >= Init::PRIMITIVES { + self.create_mirror_class(&class)?; + } Ok(class) } /// Initialize a class following JVM Spec 5.5. /// Handles recursive initialization by tracking which thread is initializing. pub fn init(&self, class: Arc) -> Result<(), VmError> { - let current_thread = thread::current().id(); + let current_thread = os_thread_id().into(); // Check and update initialization state { @@ -195,7 +199,7 @@ impl VmThread { } pub fn ensure_initialised(&self, class: &Arc) -> Result<(), VmError> { - let current_thread = thread::current().id(); + let current_thread = os_thread_id().into(); { let mut state = class.init_state.lock(); @@ -265,14 +269,15 @@ impl VmThread { } /// creates a mirror java/lang/Class Object and binds it to this RuntimeClass - fn create_mirror_class(&self, class: &Arc) -> Result<(), VmError> { + pub(crate) fn create_mirror_class(&self, class: &Arc) -> Result<(), VmError> { if class.mirror.get().is_some() || class.mirror_in_progress.swap(true, Ordering::SeqCst) { return Ok(()); // already has a mirror } - let class_class = if class.this_class == "java/lang/Class" { + let class_class = if class.this_class == VmSymbols::CLASS { Arc::clone(class) } else { - self.get_class("java/lang/Class")? + // self.get_class("java/lang/Class")? + self.get_class(VmSymbols::CLASS)? }; let string = self.intern_string(&class.this_class); let component_type = if (class.this_class.starts_with("[")) { @@ -295,17 +300,18 @@ impl VmThread { } pub fn invoke_main(&self, what: &str) -> Result<(), VmError> { - let method_ref = MethodRef { - class: what.to_string(), - name: "main".to_string(), - desc: MethodDescriptor::psvm(), - }; + let method_ref = MethodRef::from_symbols(what, "main", &[], None); self.invoke(method_ref, Vec::new())?; Ok(()) } - pub fn invoke(&self, method_reference: MethodRef, args: Vec) -> MethodCallResult { + pub fn invoke( + &self, + either: impl Into, + args: Vec, + ) -> MethodCallResult { + let either = either.into(); if self.gc.read().objects.len() > 2350 { INIT_LOGGER.call_once(|| { env_logger::Builder::from_default_env() @@ -318,8 +324,8 @@ impl VmThread { }); // println!("heap length {}", self.gc.read().objects.len()) } - let class = self.get_class(&method_reference.class)?; - let method = class.find_method(&method_reference.name, &method_reference.desc)?; + let class = self.get_class(&either.class())?; + let method = class.find_method(&either.name(), &either.desc())?; let class = self.loader.lock().get_or_load(&*method.class, None)?; self.execute_method(&class, &method, args) @@ -327,11 +333,11 @@ impl VmThread { pub fn invoke_virtual( &self, - method_reference: MethodRef, + method_reference: InterfaceOrMethod, class: Arc, args: Vec, ) -> MethodCallResult { - let method = class.find_method(&method_reference.name, &method_reference.desc)?; + let method = class.find_method(&method_reference.name(), &method_reference.desc())?; let class = self.loader.lock().get_or_load(&*method.class, None)?; self.execute_method(&class, &method, args) @@ -340,11 +346,11 @@ impl VmThread { pub fn invoke_native(&self, method: &MethodRef, args: Vec) -> MethodCallResult { let symbol_name = generate_jni_method_name(method, false); - if symbol_name.contains("Java_java_lang_reflect_Array_newArray") { - return Err(VmError::Debug( - "RoastVM specific implementation required for Java_java_lang_reflect_Array_newArray", - )); - } + // if symbol_name.contains("Java_java_lang_reflect_Array_newArray") { + // return Err(VmError::Debug( + // "RoastVM specific implementation required for Java_java_lang_reflect_Array_newArray", + // )); + // } let result = unsafe { let p = self @@ -501,7 +507,7 @@ impl VmThread { let method_ref = class.find_method("", &desc)?; let mut args = args.to_vec(); args.insert(0, obj.clone().into()); - let _ = self.invoke(method_ref.into(), args)?; + let _ = self.invoke(MethodRef::from(method_ref), args)?; Ok(obj) } } @@ -603,12 +609,13 @@ impl VmThread { } pub fn bootstrap_mirror(&self) { - let thread_id = self.id.0 as jlong; + let thread_id: u64 = unsafe { std::mem::transmute(self.id) }; + let thread_id = thread_id as jlong; let main = self.intern_string("main"); let system = self.intern_string("system"); - let thread_klass = self.get_class("java/lang/Thread").unwrap(); - let thread_group_klass = self.get_class("java/lang/ThreadGroup").unwrap(); - let field_holder_klass = self.get_class("java/lang/Thread$FieldHolder").unwrap(); + let thread_klass = self.get_class(VmSymbols::THREAD).unwrap(); + let thread_group_klass = self.get_class(VmSymbols::THREAD_GROUP).unwrap(); + let field_holder_klass = self.get_class(VmSymbols::THREAD_FIELD_HOLDER).unwrap(); let group = self .gc .write() @@ -623,6 +630,16 @@ impl VmThread { .new_thread(thread_klass, thread_id, thread_id, main, holder); self.mirror.set(thread.lock().id).unwrap(); } + + pub fn get_class_mirror(&self, name: &str) -> Result { + let class = self.get_class(&name)?; + let mirror_id = *class + .mirror + .get() + .ok_or_else(|| VmError::InvariantError(format!("Mirror uninitialized: {}", name)))?; + let class_ref = self.gc.read().get(mirror_id); + Ok(Value::from(class_ref)) + } } // SAFETY: // - jni_env pointer is owned by this VmThread and only used by its OS thread diff --git a/crates/core/src/value.rs b/crates/core/src/value.rs index e11adf9..9d364e1 100644 --- a/crates/core/src/value.rs +++ b/crates/core/src/value.rs @@ -193,6 +193,22 @@ impl Primitive { } } } +impl ClassName for Primitive { + fn class_name(&self) -> String { + match self { + Primitive::Int(_) => "int".to_string(), + Primitive::Long(_) => "long".to_string(), + Primitive::Short(_) => "short".to_string(), + Primitive::Char(_) => "char".to_string(), + Primitive::Byte(_) => "byte".to_string(), + + Primitive::Float(_) => "float".to_string(), + Primitive::Double(_) => "double".to_string(), + + Primitive::Boolean(_) => "boolean".to_string(), + } + } +} impl Display for Primitive { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { @@ -313,3 +329,7 @@ impl PartialEq for Value { } } } + +pub trait ClassName { + fn class_name(&self) -> String; +} diff --git a/crates/core/src/vm.rs b/crates/core/src/vm.rs index 24c744b..195f194 100644 --- a/crates/core/src/vm.rs +++ b/crates/core/src/vm.rs @@ -5,27 +5,28 @@ use libloading::os::unix as imp; #[cfg(all(windows))] use libloading::os::windows as imp; -use crate::class_file::{ClassFlags, MethodRef}; +use crate::class_file::ClassFlags; +use crate::class_file::resolved::MethodRef; use crate::class_loader::ClassLoader; use crate::error::VmError; use crate::native::r#unsafe::UnsafeSupport; use crate::objects::object::ReferenceKind; use crate::objects::object_manager::ObjectManager; use crate::thread::VmThread; -use crate::value::Value; -use crate::{MethodDescriptor, ThreadId}; +use crate::{MethodDescriptor, ThreadId, VmSymbols, os_thread_id}; use dashmap::DashMap; use imp::Library; use log::{info, trace}; use parking_lot::{Mutex, RwLock}; -use std::sync::atomic::AtomicU64; +use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::{Arc, LazyLock}; + use std::time::Instant; 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 pub threads: DashMap>, @@ -36,6 +37,7 @@ pub struct Vm { pub gc: Arc>, pub safent: RwLock, pub next_id: AtomicU64, + pub phase: AtomicU64, } impl Vm { @@ -43,24 +45,24 @@ impl Vm { pub fn new() -> Arc { let vm = Arc::new(Self { threads: DashMap::new(), - main_thread_id: ThreadId(0), + main_thread_id: os_thread_id().into(), loader: Arc::new(Mutex::from(ClassLoader::with_bimage())), native_methods: DashMap::new(), native_libraries: Arc::new(RwLock::new(Vec::new())), gc: Default::default(), safent: Default::default(), next_id: AtomicU64::new(0), + phase: AtomicU64::new(Init::STARTING), }); // Create main thread - let thread = VmThread::new(vm.clone(), None); - let thread_id = thread.id; + let main_thread = VmThread::new(vm.clone(), None); // Store in VM - vm.threads.insert(thread_id, thread.clone()); + vm.threads.insert(main_thread.id, main_thread.clone()); // Set as current thread - VmThread::set_current(thread_id); + // VmThread::set_current(thread_id); vm } @@ -76,6 +78,7 @@ impl Vm { gc: Default::default(), safent: Default::default(), next_id: AtomicU64::new(0), + phase: AtomicU64::new(Init::STARTING), }); // Create main thread @@ -119,23 +122,16 @@ impl Vm { pub fn boot_strap(&self) -> Result<(), VmError> { let thread = self.threads.get(&self.main_thread_id).unwrap(); - let classes = vec![ - "java/lang/String", - "java/lang/System", - "java/lang/Class", - "java/lang/ThreadGroup", - "java/lang/Thread", - "java/lang/Module", - //unsafe internal? - "jdk/internal/misc/UnsafeConstants", - "java/lang/reflect/Method", - "java/lang/ref/Finalizer", - ]; - let mut loaded = Vec::new(); - for name in classes { + + // Load core trio first + let core_classes = vec![VmSymbols::OBJECT, VmSymbols::STRING, VmSymbols::CLASS]; + let mut core_loaded = Vec::new(); + for name in core_classes { let class = thread.loader.lock().get_or_load(name, None)?; - loaded.push(class); + core_loaded.push(class); } + + // Primitives let prims = vec![ ("byte", "B"), ("char", "C"), @@ -147,14 +143,13 @@ impl Vm { ("boolean", "Z"), ("void", "V"), ]; - let thread = self.threads.get(&self.main_thread_id).unwrap(); + + let class_class = thread.loader.lock().get_or_load(VmSymbols::CLASS, None)?; + let flags = ClassFlags::from(1041u16); for prim in prims { let klass = self.loader.lock().primitive_class(prim.0); - - let class_class = thread.loader.lock().get_or_load("java/lang/Class", None)?; let name_obj = thread.intern_string(&prim.0); - let flags = ClassFlags::from(1041u16); let class_obj = self.gc.write().new_class( class_class.clone(), Some(ReferenceKind::ObjectReference(name_obj)), @@ -163,12 +158,12 @@ impl Vm { true, None, ); - // klass.mirror.set(class_obj.lock().id).unwrap(); + klass.mirror.set(class_obj.lock().id).unwrap(); let prim_array_klass = self.loader.lock().create_array_class(klass.clone()); let arr_name_obj = thread.intern_string(&prim_array_klass.this_class); let arr_class_obj = self.gc.write().new_class( - class_class, + class_class.clone(), Some(ReferenceKind::ObjectReference(arr_name_obj)), None, flags, @@ -180,24 +175,57 @@ impl Vm { .set(arr_class_obj.lock().id) .unwrap(); } + self.phase.store(Init::PRIMITIVES, Ordering::Release); - //woops forgot to init + // Remaining bootstrap classes + let classes = vec![ + VmSymbols::SYSTEM, + VmSymbols::THREAD_GROUP, + VmSymbols::THREAD, + VmSymbols::MODULE, + "jdk/internal/misc/UnsafeConstants", + "java/lang/reflect/Method", + "java/lang/ref/Finalizer", + ]; + let mut loaded = Vec::new(); + for name in classes { + let class = thread.get_class(name)?; + loaded.push(class); + } + + // Now mirrors and init + for class in &core_loaded { + thread.create_mirror_class(&class)?; + } + for class in &loaded { + thread.create_mirror_class(&class)?; + } + for class in core_loaded { + println!("bootstrap init {}", class.this_class); + thread.ensure_initialised(&class)?; + } for class in loaded { println!("bootstrap init {}", class.this_class); thread.ensure_initialised(&class)?; } + self.phase.store(Init::JAVA_CLASSES, Ordering::Release); - let phase1ref = MethodRef { - class: "java/lang/System".to_string(), - name: "initPhase1".to_string(), - desc: MethodDescriptor::void(), - }; + // let phase1ref = MethodRef { + // class: "java/lang/System".to_string(), + // name: "initPhase1".to_string(), + // desc: MethodDescriptor::void(), + // }; + + let phase1ref = MethodRef::from_symbols(VmSymbols::SYSTEM, "initPhase1", &vec![], None); self.threads .get(&self.main_thread_id) .unwrap() .bootstrap_mirror(); thread.invoke(phase1ref, Vec::new())?; - // panic!("HOLY FUCKING SHIT, DID WE PHASE 1!?"); + self.phase + .store(Init::SYSTEM_INIT_PHASE_1, Ordering::Release); + + self.phase.store(Init::UP, Ordering::Release); Ok(()) } @@ -211,6 +239,14 @@ impl Vm { let thread = self.threads.get(&self.main_thread_id).unwrap().clone(); thread.invoke_main(what) } + + pub fn current_thread(&self) -> Arc { + let id: ThreadId = os_thread_id().into(); + self.threads + .get((&id).into()) + .expect(format!("Did not find thread: {:?}", id).as_str()) + .clone() + } } // SAFETY: @@ -218,3 +254,13 @@ impl Vm { // - All mutable state is behind DashMap/RwLock/Mutex unsafe impl Send for Vm {} unsafe impl Sync for Vm {} + +pub struct Init; + +impl Init { + pub const STARTING: u64 = 0; + pub const PRIMITIVES: u64 = 1; + pub const JAVA_CLASSES: u64 = 2; + pub const SYSTEM_INIT_PHASE_1: u64 = 3; + pub const UP: u64 = 4; +} diff --git a/crates/roast-vm-sys/src/class.rs b/crates/roast-vm-sys/src/class.rs index 249cc4c..36fbf7f 100644 --- a/crates/roast-vm-sys/src/class.rs +++ b/crates/roast-vm-sys/src/class.rs @@ -3,11 +3,8 @@ use jni::sys::{JNI_FALSE, JNI_TRUE, jboolean, jclass, jobject}; use jni::sys::{JNIEnv, jint}; use jni::sys::{jobjectArray, jstring}; use log::trace; -use roast_vm_core::class_file::FieldRef; +use roast_vm_core::FieldType; use roast_vm_core::objects::ReferenceKind; -use roast_vm_core::objects::array::ArrayReference; -use roast_vm_core::value::Value; -use roast_vm_core::{BaseType, FieldType}; use std::ptr; #[unsafe(no_mangle)] diff --git a/crates/roast-vm-sys/src/lib.rs b/crates/roast-vm-sys/src/lib.rs index 7ee3e6c..416f5a6 100644 --- a/crates/roast-vm-sys/src/lib.rs +++ b/crates/roast-vm-sys/src/lib.rs @@ -69,7 +69,6 @@ pub extern "system" fn Java_org_example_MockIO_println<'local>( unsafe fn get_thread(env: *mut jni::sys::JNIEnv) -> *const VmThread { let thread = (**env).reserved0 as *const VmThread; - VmThread::set_current((*thread).id); thread } fn resolve_object(thread: &VmThread, obj: jobject) -> Option { diff --git a/crates/roast-vm-sys/src/reflect/array.rs b/crates/roast-vm-sys/src/reflect/array.rs index 487dd38..f79a4cd 100644 --- a/crates/roast-vm-sys/src/reflect/array.rs +++ b/crates/roast-vm-sys/src/reflect/array.rs @@ -1,7 +1,7 @@ use crate::get_thread; use jni::sys::{JNI_TRUE, JNIEnv, jclass, jint, jobject}; use roast_vm_core::FieldType; -use roast_vm_core::class_file::FieldRef; +use roast_vm_core::class_file::resolved::FieldRef; #[unsafe(no_mangle)] pub unsafe extern "system" fn Java_java_lang_reflect_Array_newArray( @@ -28,11 +28,12 @@ pub unsafe extern "system" fn Java_java_lang_reflect_Array_newArray( .get_field(&FieldRef::new("Class", "primitive", FieldType::bool())) .try_into_jboolean() .unwrap(); - + let array_class_name = format!("[L{};", comp.this_class); + let array_class = thread.get_class(&array_class_name).unwrap(); let refe = if prim == JNI_TRUE { - thread.gc.write().new_primitive_array(comp, length) + thread.gc.write().new_primitive_array(array_class, length) } else { - thread.gc.write().new_object_array(comp, length) + thread.gc.write().new_object_array(array_class, length) }; refe.id() as jobject } diff --git a/crates/roast-vm-sys/src/reflection.rs b/crates/roast-vm-sys/src/reflection.rs index 852d47b..ab9566e 100644 --- a/crates/roast-vm-sys/src/reflection.rs +++ b/crates/roast-vm-sys/src/reflection.rs @@ -1,6 +1,7 @@ use crate::get_thread; use jni::sys::{JNIEnv, jclass, jint, jobject, jobjectArray}; -use roast_vm_core::class_file::FieldRef; + +use roast_vm_core::class_file::resolved::{FieldRef, MethodRef}; use roast_vm_core::objects::array::ArrayReference; use roast_vm_core::value::Value; use roast_vm_core::{BaseType, FieldType}; @@ -89,7 +90,9 @@ pub unsafe extern "system" fn Java_jdk_internal_reflect_DirectConstructorHandleA let instance = thread.gc.write().new_object(klass.clone()); values.insert(0, instance.clone().into()); - thread.invoke(method.clone().into(), values).unwrap(); + thread + .invoke(MethodRef::from(method).clone().into(), values) + .unwrap(); instance.lock().id as jobject } diff --git a/crates/roast-vm-sys/src/system_props.rs b/crates/roast-vm-sys/src/system_props.rs index 796595a..657430b 100644 --- a/crates/roast-vm-sys/src/system_props.rs +++ b/crates/roast-vm-sys/src/system_props.rs @@ -1,6 +1,7 @@ use jni::JNIEnv; use jni::objects::{JClass, JObject}; use jni::sys::jobjectArray; +use roast_vm_core::VmSymbols; #[unsafe(no_mangle)] pub extern "system" fn Java_jdk_internal_util_SystemProps_00024Raw_vmProperties<'local>( @@ -34,7 +35,7 @@ pub extern "system" fn Java_jdk_internal_util_SystemProps_00024Raw_vmProperties< ".", ]; - let string_class = env.find_class("java/lang/String").unwrap(); + let string_class = env.find_class(VmSymbols::STRING).unwrap(); // +1 for null terminator let arr = env .new_object_array((props.len() + 1) as i32, string_class, JObject::null()) diff --git a/crates/roast-vm-sys/src/thread.rs b/crates/roast-vm-sys/src/thread.rs index 1158f6e..54eb522 100644 --- a/crates/roast-vm-sys/src/thread.rs +++ b/crates/roast-vm-sys/src/thread.rs @@ -1,8 +1,8 @@ use crate::get_thread; use jni::sys::{JNIEnv, jclass, jint, jlong, jobject}; -use roast_vm_core::class_file::MethodRef; +use roast_vm_core::class_file::resolved::MethodRef; use roast_vm_core::value::Value; -use roast_vm_core::{MethodDescriptor, VmThread}; +use roast_vm_core::{MethodDescriptor, VmSymbols, VmThread}; #[unsafe(no_mangle)] unsafe extern "system" fn Java_java_lang_Thread_currentThread( @@ -30,6 +30,7 @@ unsafe extern "system" fn Java_java_lang_Thread_setPriority0( _this: jobject, priority: jint, ) { + // todo // Get the current thread and update its priority // let thread = &*get_thread(env); // Store priority in thread state (implementation depends on thread structure) @@ -55,21 +56,17 @@ unsafe extern "system" fn Java_java_lang_Thread_start0(env: *mut JNIEnv, this: j vm.threads.insert(thread_id, new_thread.clone()); // Set as current thread for this OS thread - VmThread::set_current(thread_id); + // 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(), - }; + let run_ref = MethodRef::from_symbols(VmSymbols::THREAD, "run", &Vec) // 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); + if let Err(e) = new_thread.invoke(run_ref.into(), vec![thread_value]) { + eprintln!("Thread {:?} died with: {:?}", thread_id, e); } // Cleanup: remove from VM thread registry