Sync for jsr 292

This commit is contained in:
james 2025-12-29 06:51:03 +10:30
parent 24939df1b7
commit 911bb1be82
No known key found for this signature in database
GPG Key ID: E1FFBA228F4CAD87
27 changed files with 1203 additions and 297 deletions

7
.idea/dictionaries/project.xml generated Normal file
View File

@ -0,0 +1,7 @@
<component name="ProjectDictionaryState">
<dictionary name="project">
<words>
<w>stacktraceelement</w>
</words>
</dictionary>
</component>

View File

@ -2,5 +2,11 @@
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="IncorrectFormatting" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="SSBasedInspection" enabled="true" level="WARNING" enabled_by_default="true">
<searchConfiguration name="jay el" text="java/lang" recursive="true" caseInsensitive="false">
<constraint name="__context__" within="" contains="" />
</searchConfiguration>
</inspection_tool>
<inspection_tool class="b5f914b3-9c2e-37bf-aba7-3845b009e991" enabled="true" level="WARNING" enabled_by_default="true" />
</profile>
</component>

View File

@ -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

View File

@ -19,6 +19,7 @@ colored = { workspace = true }
parking_lot = { workspace = true }
cesu8 = { workspace = true }
windows = { workspace = true }
libc = { workspace = true }

View File

@ -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)]

View File

@ -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<MethodData> 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<MethodData> 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<MethodHandleKind> for MethodHandle {
fn from(kind: MethodHandleKind) -> Self {
Self { kind }
}
}
#[derive(Debug, Clone)]
pub struct MethodType {
pub desc: MethodDescriptor,
}
impl From<MethodDescriptor> 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<Loadable>,
}
#[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<MethodRef> for InterfaceOrMethod {
fn from(value: MethodRef) -> Self {
Self::Method(value)
}
}
impl From<InterfaceMethodRef> 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<MethodHandle> for Loadable {
fn from(handle: MethodHandle) -> Self {
Self::MethodHandle(handle)
}
}
}

View File

@ -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<ConstantPoolEntry>;
@ -99,14 +105,17 @@ pub trait ConstantPoolExt: ConstantPoolGet {
Ok(MethodRef { class, name, desc })
}
fn resolve_interface_method_ref(&self, index: u16) -> Result<MethodRef, ConstantPoolError> {
fn resolve_interface_method_ref(
&self,
index: u16,
) -> Result<InterfaceMethodRef, ConstantPoolError> {
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<InterfaceOrMethod, ConstantPoolError> {
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<RuntimeClass>,
) -> Result<Loadable, ConstantPoolError> {
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::<Result<_, _>>()?;
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<MethodHandleKind, ConstantPoolError> {
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<Attribute, VmError> {
let name = self.get_string(a.attribute_name_index)?;
// trace!("Parsing attribute with name: {}", name);

View File

@ -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<RuntimeClass>) -> Arc<RuntimeClass> {
// let name = format!("[{}", component.descriptor()); // e.g., "[Ljava/lang/String;"
let object_class: Arc<RuntimeClass> = self.get_or_load("java/lang/Object", None).unwrap();
let cloneable: Arc<RuntimeClass> = self.get_or_load("java/lang/Cloneable", None).unwrap();
let object_class: Arc<RuntimeClass> = self.get_or_load(VmSymbols::OBJECT, None).unwrap();
let cloneable: Arc<RuntimeClass> = self.get_or_load(VmSymbols::CLONEABLE, None).unwrap();
let serializable: Arc<RuntimeClass> =
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(),

View File

@ -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<ConstantPoolError> for VmError {
fn from(value: ConstantPoolError) -> Self {

View File

@ -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<VmThread>,
pub(crate) thread: Arc<VmThread>,
// The mod being invoked
pub method_ref: MethodRef,
@ -114,7 +118,7 @@ impl Frame {
line_number_table: Option<Vec<LineNumberTableEntry>>,
) -> 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<Value> = 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::<Result<Vec<Option<ReferenceKind>>, 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<Option<ReferenceKind>> = 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::<Result<_, VmError>>()?;
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<Option<ReferenceKind>> = 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::<Result<_, VmError>>()?;
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<Value> =
vec![lookup.into(), name_string.into(), call_site_mt];
all_bsm_args.extend(static_args);
// Box primitives and create Object[]
let boxed_args: Vec<Option<ReferenceKind>> = all_bsm_args
.into_iter()
.map(|v| self.box_value(v))
.collect::<Result<_, _>>()?;
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<Value> = self.stack.pop_n(runtime_arg_count)?;
// 10. Invoke target with runtime args
let boxed_runtime: Vec<Option<ReferenceKind>> = runtime_args
.into_iter()
.map(|v| self.box_value(v))
.collect::<Result<_, _>>()?;
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<ExecutionResult, VmError> {
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<Value, VmError> {
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<Value> = dynamic_info
.method
.args
.into_iter()
.map(|loadable| self.resolve_loadable(loadable))
.collect::<Result<Vec<_>, _>>()?;
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)]

View File

@ -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<u64> 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<FieldType>,
}
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<usize> = std::sync::OnceLock::new();
let local = 0u8;
@ -233,10 +247,23 @@ pub enum FieldType {
ArrayType(Box<FieldType>),
}
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 }
}

View File

@ -295,3 +295,9 @@ macro_rules! shift_op {
Ok(ExecutionResult::Continue)
}};
}
#[macro_export]
macro_rules! location {
() => {
format!("{}:{}:{}", file!(), line!(), column!())
};
}

View File

@ -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);

View File

@ -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();
};

View File

@ -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<T: ArrayValue> {
pub backing: Box<[T]>,
}
impl<T: ArrayValue> ClassName for Array<T> {
fn class_name(&self) -> String {
self.class.this_class.clone()
}
}
impl<T> Array<T>
where
T: ArrayValue + Clone + Default,

View File

@ -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),

View File

@ -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)

103
crates/core/src/symbols.rs Normal file
View File

@ -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";
}

View File

@ -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<Option<Value>, VmError>;
@ -48,7 +50,8 @@ pub struct VmThread {
impl VmThread {
pub fn new(vm: Arc<Vm>, loader: Option<LoaderRef>) -> Arc<Self> {
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<Vm>) -> Arc<VmThread> {
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<Arc<RuntimeClass>, 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<RuntimeClass>) -> 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<RuntimeClass>) -> 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<RuntimeClass>) -> Result<(), VmError> {
pub(crate) fn create_mirror_class(&self, class: &Arc<RuntimeClass>) -> 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<Value>) -> MethodCallResult {
pub fn invoke(
&self,
either: impl Into<InterfaceOrMethod>,
args: Vec<Value>,
) -> 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<RuntimeClass>,
args: Vec<Value>,
) -> 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<Value>) -> 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("<init>", &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<Value, VmError> {
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

View File

@ -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<FieldType> for Value {
}
}
}
pub trait ClassName {
fn class_name(&self) -> String;
}

View File

@ -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<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
pub threads: DashMap<ThreadId, Arc<VmThread>>,
@ -36,6 +37,7 @@ pub struct Vm {
pub gc: Arc<RwLock<ObjectManager>>,
pub safent: RwLock<UnsafeSupport>,
pub next_id: AtomicU64,
pub phase: AtomicU64,
}
impl Vm {
@ -43,24 +45,24 @@ impl Vm {
pub fn new() -> Arc<Self> {
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<VmThread> {
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;
}

View File

@ -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)]

View File

@ -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<ObjectReference> {

View File

@ -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
}

View File

@ -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
}

View File

@ -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())

View File

@ -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