check check checkpoint

This commit is contained in:
james 2025-12-08 06:54:05 +10:30
parent d9368d2448
commit 707b961903
No known key found for this signature in database
GPG Key ID: E1FFBA228F4CAD87
28 changed files with 5199 additions and 838 deletions

View File

@ -15,3 +15,5 @@ dashmap = "7.0.0-rc2"
libloading = "0.8.9" libloading = "0.8.9"
libffi = "5.0.0" libffi = "5.0.0"
jni = "0.21.1" jni = "0.21.1"
roast-vm-sys = { path = "crates/roast-vm-sys", version = "0.1.0" }
roast-vm-core = { path = "crates/core", version = "0.1.0" }

View File

@ -1,5 +1,5 @@
[package] [package]
name = "jvm-rs-core" name = "roast-vm-core"
version = "0.1.0" version = "0.1.0"
edition = "2024" edition = "2024"
publish = ["nexus"] publish = ["nexus"]
@ -9,15 +9,19 @@ deku = { workspace = true }
deku_derive = { workspace = true } deku_derive = { workspace = true }
log = { workspace = true } log = { workspace = true }
env_logger = { workspace = true } env_logger = { workspace = true }
itertools = { workspace = true }
sevenz-rust2 = { workspace = true } sevenz-rust2 = { workspace = true }
dashmap = { workspace = true } dashmap = { workspace = true }
libloading = { workspace = true } libloading = { workspace = true }
libffi = { workspace = true } libffi = { workspace = true }
jni = { workspace = true } jni = { workspace = true }
itertools = { workspace = true }
[build-dependencies] [build-dependencies]
bindgen = "0.72.1" bindgen = "0.72.1"
[lints.rust] [lints.rust]
#missing_docs = "warn" #missing_docs = "warn"
[[bin]]
name = "roast"
path = "src/main.rs"

View File

@ -130,10 +130,7 @@ impl AttributeInfo {
/// Get the interpreted attribute, parsing if necessary /// Get the interpreted attribute, parsing if necessary
pub fn get(&self, class_file: &ClassFile) -> Option<Attribute> { pub fn get(&self, class_file: &ClassFile) -> Option<Attribute> {
class_file class_file.constant_pool.parse_attribute(self.clone()).ok()
.constant_pool
.parse_attribute(self.deref().clone())
.ok()
// if let Some(ref attr) = self.interpreted { // if let Some(ref attr) = self.interpreted {
// Some(attr.clone()) // Some(attr.clone())

View File

@ -1,5 +1,6 @@
use log::trace;
use sevenz_rust2::ArchiveReader; use sevenz_rust2::ArchiveReader;
use std::collections::HashSet; use std::collections::{HashMap, HashSet};
use std::fs::File; use std::fs::File;
const DEFAULT_LOCATION: &str = "./lib/modules"; const DEFAULT_LOCATION: &str = "./lib/modules";
@ -7,6 +8,9 @@ const DEFAULT_LOCATION: &str = "./lib/modules";
pub struct Bimage { pub struct Bimage {
image: ArchiveReader<File>, image: ArchiveReader<File>,
modules: Vec<String>, modules: Vec<String>,
// inversion, <Package, Module>
// eg. <java.lang, java.base>
packages: HashMap<String, String>,
} }
impl Default for Bimage { impl Default for Bimage {
@ -24,9 +28,27 @@ impl Default for Bimage {
.into_iter() .into_iter()
.collect::<Vec<_>>(); .collect::<Vec<_>>();
modules.sort(); modules.sort();
/*let packages = reader
.archive()
.files
.iter()
.filter(|e| !e.is_directory)
.map(|e| {
if e.name.contains("java.base") {
println!("{:?}", e);
}
("Greg".to_owned(), "Dave".to_owned())
})
.collect::<HashMap<_, _>>();*/
let packages = HashMap::new();
Self { Self {
image: reader, image: reader,
modules, modules,
packages,
} }
} }
} }
@ -58,6 +80,8 @@ impl Bimage {
} }
pub fn get_class(&mut self, module: &str, class: &str) -> Option<Vec<u8>> { pub fn get_class(&mut self, module: &str, class: &str) -> Option<Vec<u8>> {
// trace!("Modules{:#?}", self.modules);
let path = Self::resolve_path(module, class); let path = Self::resolve_path(module, class);
self.image self.image
.read_file(&path) .read_file(&path)

View File

@ -5,7 +5,8 @@ use crate::class_file::{
}; };
use crate::{FieldType, MethodDescriptor, VmError}; use crate::{FieldType, MethodDescriptor, VmError};
use log::trace; use log::trace;
use std::sync::{Arc, Mutex}; use std::hash::{Hash, Hasher};
use std::sync::{Arc, Mutex, OnceLock, OnceState};
use std::thread::ThreadId; use std::thread::ThreadId;
/// JVM Spec 5.5: Initialization states for a class /// JVM Spec 5.5: Initialization states for a class
@ -30,9 +31,26 @@ pub struct RuntimeClass {
pub interfaces: Vec<Arc<RuntimeClass>>, pub interfaces: Vec<Arc<RuntimeClass>>,
pub fields: Vec<FieldData>, pub fields: Vec<FieldData>,
pub methods: Vec<MethodData>, pub methods: Vec<MethodData>,
pub mirror: OnceLock<u32>,
/// Thread-safe initialization state (JVM Spec 5.5) /// Thread-safe initialization state (JVM Spec 5.5)
pub init_state: Mutex<InitState>, pub init_state: Mutex<InitState>,
pub super_classes: Vec<Arc<RuntimeClass>>,
pub super_interfaces: Vec<Arc<RuntimeClass>>,
pub component_type: Option<Arc<RuntimeClass>>,
} }
impl Hash for RuntimeClass {
fn hash<H: Hasher>(&self, state: &mut H) {
self.this_class.hash(state)
}
}
impl PartialEq<Self> for RuntimeClass {
fn eq(&self, other: &Self) -> bool {
self.this_class.eq(&other.this_class)
}
}
impl Eq for RuntimeClass {}
impl RuntimeClass { impl RuntimeClass {
pub fn find_method(&self, name: &str, desc: &MethodDescriptor) -> Result<&MethodData, VmError> { pub fn find_method(&self, name: &str, desc: &MethodDescriptor) -> Result<&MethodData, VmError> {
@ -58,15 +76,16 @@ impl RuntimeClass {
Err(VmError::LoaderError("Failed to find method".to_string())) Err(VmError::LoaderError("Failed to find method".to_string()))
} }
pub fn find_field(&self, name: &str, desc: FieldType) -> Result<&FieldData, VmError> { pub fn find_field(&self, name: &str, desc: &FieldType) -> Result<&FieldData, VmError> {
println!("Finding field"); trace!("Finding field");
if let Some(field) = self.fields.iter().find(|e| { if let Some(field) = self.fields.iter().find(|e| {
println!("Field Name Needed: {name}, Checked:{}", e.name); trace!("Field Name Needed: {name}, Checked:{}", e.name);
println!("Field type Needed: {desc:?}, Checked:{:?}", e.desc); trace!("Field type Needed: {desc:?}, Checked:{:?}", e.desc);
let name_match = e.name.eq(name); let name_match = e.name.eq(name);
let type_match = desc == e.desc; let type_match = *desc == e.desc;
name_match && type_match name_match && type_match
}) { }) {
trace!("Found field: {name}");
return Ok(field); return Ok(field);
}; };
@ -77,4 +96,10 @@ impl RuntimeClass {
// No field found, and we must be Object, as we don't have a superclass // No field found, and we must be Object, as we don't have a superclass
Err(VmError::LoaderError("Failed to find field".to_string())) Err(VmError::LoaderError("Failed to find field".to_string()))
} }
pub fn is_assignable_into(&self, into: Arc<RuntimeClass>) -> bool {
self.eq(&*into)
|| self.super_classes.contains(&into)
|| self.super_interfaces.contains(&into)
}
} }

View File

@ -490,12 +490,42 @@ pub fn pool_get_string(constant_pool: &[ConstantPoolEntry], index: u16) -> Optio
None None
} }
fn read_bytecode_with_offsets<R: deku::no_std_io::Read + deku::no_std_io::Seek>(
bytes: u32,
reader: &mut deku::reader::Reader<R>,
endian: deku::ctx::Endian,
) -> Result<Vec<(u16, Ops)>, DekuError> {
use deku::DekuReader;
let mut code = Vec::new();
let mut byte_offset = 0u16;
let total_bytes = bytes as usize;
let mut bytes_read = 0;
// Parse until we've consumed all the bytecode
while bytes_read < total_bytes {
let start_pos = reader.bits_read;
// Parse the next Op
let op = Ops::from_reader_with_ctx(reader, endian)?;
let end_pos = reader.bits_read;
let op_bytes = ((end_pos - start_pos) / 8) as usize;
code.push((byte_offset, op));
byte_offset += op_bytes as u16;
bytes_read += op_bytes;
}
Ok(code)
}
#[derive(Clone, PartialEq, Debug, DekuRead)] #[derive(Clone, PartialEq, Debug, DekuRead)]
#[deku(endian = "Big")] #[deku(endian = "Big")]
pub(crate) struct Bytecode { pub(crate) struct Bytecode {
bytes: u32, bytes: u32,
#[deku(bytes_read = "bytes")] #[deku(reader = "read_bytecode_with_offsets(*bytes, deku::reader, deku::ctx::Endian::Big)")]
pub code: Vec<Ops>, pub code: Vec<(u16, Ops)>,
} }
// pub trait ConstantPoolExt { // pub trait ConstantPoolExt {
@ -615,7 +645,7 @@ pub(crate) struct Bytecode {
// } // }
// } // }
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct MethodRef { pub struct MethodRef {
pub class: String, pub class: String,
pub name: String, pub name: String,
@ -673,7 +703,7 @@ impl From<Constant> for Value {
} }
#[allow(non_snake_case)] #[allow(non_snake_case)]
#[derive(Debug, PartialEq, DekuRead, DekuWrite)] #[derive(Debug, Clone, Copy, PartialEq, DekuRead, DekuWrite)]
pub struct ClassFlags { pub struct ClassFlags {
// flags // flags
#[deku(bits = 1)] #[deku(bits = 1)]

View File

@ -1,23 +1,17 @@
use crate::attributes::Attribute; use crate::attributes::Attribute;
use crate::bimage::Bimage; use crate::bimage::Bimage;
use crate::class::RuntimeClass; use crate::class::{InitState, RuntimeClass};
use crate::class_file::constant_pool::{ConstantPoolExt, ConstantPoolGet}; use crate::class_file::constant_pool::{ConstantPoolExt, ConstantPoolGet};
use crate::class_file::{ use crate::class_file::{ClassFile, ClassFlags, FieldData, FieldFlags, MethodData, MethodFlags};
ClassFile, ClassFlags, ConstantClassInfo, ConstantPoolEntry, FieldData, FieldFlags, MethodData,
MethodFlags,
};
use crate::native_libraries::NativeLibraries;
use crate::{FieldType, MethodDescriptor}; use crate::{FieldType, MethodDescriptor};
use dashmap::DashMap; use dashmap::DashMap;
use deku::DekuContainerRead; use deku::DekuContainerRead;
use itertools::Itertools;
use libloading::os::windows::Library;
use log::warn; use log::warn;
use std::collections::hash_map::{Entry, Iter}; use std::collections::HashSet;
use std::fs::File; use std::fs::File;
use std::io::Read; use std::io::Read;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex, OnceLock};
pub type LoaderRef = Arc<Mutex<ClassLoader>>; pub type LoaderRef = Arc<Mutex<ClassLoader>>;
@ -88,15 +82,16 @@ pub fn resolve_path(what: &str) -> Result<(PathBuf, String), String> {
/// ``` /// ```
#[derive(Default)] #[derive(Default)]
pub struct ClassLoader { pub struct ClassLoader {
classes: DashMap<String, Arc<RuntimeClass>>, classes: DashMap<(String, LoaderId), Arc<RuntimeClass>>,
bimage: Bimage, bimage: Bimage,
pub needs_init: Vec<Arc<RuntimeClass>>, pub needs_init: Vec<Arc<RuntimeClass>>,
native_libraries: NativeLibraries,
} }
type LoaderId = Option<u32>;
impl ClassLoader { impl ClassLoader {
pub fn new() -> Result<Self, String> { pub fn new() -> Result<Self, String> {
let mut loader = Self::default(); let loader = Self::default();
// for entry in VM_CLASSES { // for entry in VM_CLASSES {
// let module_class = format!("{}/{}", "java.base", entry);; // let module_class = format!("{}/{}", "java.base", entry);;
// loader.load_class(&module_class)?; // loader.load_class(&module_class)?;
@ -104,6 +99,13 @@ impl ClassLoader {
Ok(loader) Ok(loader)
} }
pub fn class_from_mirror_id(&self, id: u32) -> Option<Arc<RuntimeClass>> {
self.classes
.iter()
.map(|x| x.value().clone())
.find(|e| *e.mirror.wait() == id)
}
/// Retrieves an `Arc<ClassFile>` from the internal storage, or attempts to load it if not already present. /// Retrieves an `Arc<ClassFile>` from the internal storage, or attempts to load it if not already present.
/// ///
/// # Arguments /// # Arguments
@ -135,11 +137,43 @@ impl ClassLoader {
/// } /// }
/// } /// }
/// ``` /// ```
pub fn get_or_load(&mut self, what: &str) -> Result<Arc<RuntimeClass>, String> { pub fn get_or_load(
if let Some(class) = self.classes.get(what) { &mut self,
class_name: &str,
loader: LoaderId,
) -> Result<Arc<RuntimeClass>, String> {
if let Some(class) = self.classes.get(&(class_name.to_string(), loader)) {
return Ok(class.clone()); return Ok(class.clone());
} }
let class = self.load_class(what)?; if class_name.starts_with("[") {
let component_name = class_name.strip_prefix("[").unwrap();
let component = match component_name.chars().next() {
Some('B') => self.get_or_load("byte", None),
Some('C') => self.get_or_load("char", None),
Some('D') => self.get_or_load("double", None),
Some('F') => self.get_or_load("float", None),
Some('I') => self.get_or_load("int", None),
Some('J') => self.get_or_load("long", None),
Some('S') => self.get_or_load("short", None),
Some('Z') => self.get_or_load("boolean", None),
Some('[') => self.get_or_load(component_name, None),
Some('L') => {
let class_name = component_name
.strip_prefix('L')
.and_then(|s| s.strip_suffix(';'))
.expect("invalid L descriptor");
self.get_or_load(class_name, None)
}
None => Err("empty component descriptor".to_string()),
_ => Err(format!("invalid component descriptor: {}", component_name)),
};
let component = self.get_or_load(component_name, None)?;
let arr_class = self.create_array_class(
component,
);
return Ok(Arc::new(arr_class))
}
let class = self.load_class(class_name, loader)?;
self.needs_init.push(class.clone()); self.needs_init.push(class.clone());
Ok(class) Ok(class)
} }
@ -148,7 +182,7 @@ impl ClassLoader {
self.classes.clone() self.classes.clone()
}*/ }*/
fn load_class(&mut self, what: &str) -> Result<Arc<RuntimeClass>, String> { fn load_class(&mut self, what: &str, loader: LoaderId) -> Result<Arc<RuntimeClass>, String> {
let (module, class_fqn) = ("", what); let (module, class_fqn) = ("", what);
let bytes = self let bytes = self
.bimage .bimage
@ -158,7 +192,9 @@ impl ClassLoader {
.map_err(|e| format!("failed to parse class file: {}", e))?; .map_err(|e| format!("failed to parse class file: {}", e))?;
let runtime = self.runtime_class(cf); let runtime = self.runtime_class(cf);
let arced = Arc::new(runtime); let arced = Arc::new(runtime);
let option = self.classes.insert(class_fqn.to_string(), arced.clone()); let option = self
.classes
.insert((class_fqn.to_string(), loader), arced.clone());
if option.is_some() { if option.is_some() {
warn!("Replaced loaded class: {}", class_fqn) warn!("Replaced loaded class: {}", class_fqn)
} }
@ -191,7 +227,7 @@ impl ClassLoader {
name name
}; };
let super_class = { let super_class = {
if (this_class.eq("java/lang/Object")) { if this_class.eq("java/lang/Object") {
debug_assert_eq!(this_class, "java/lang/Object"); debug_assert_eq!(this_class, "java/lang/Object");
debug_assert_eq!(class_file.super_class, 0u16); debug_assert_eq!(class_file.super_class, 0u16);
None None
@ -201,7 +237,7 @@ impl ClassLoader {
.get_class_info(class_file.super_class) .get_class_info(class_file.super_class)
.unwrap(); .unwrap();
let name = constant_pool.get_string(**super_info).unwrap(); let name = constant_pool.get_string(**super_info).unwrap();
Some(self.get_or_load(&*name).unwrap()) Some(self.get_or_load(&*name, None).unwrap())
} }
}; };
@ -220,7 +256,7 @@ impl ClassLoader {
.map(|e| { .map(|e| {
let interface_info = constant_pool.get_class_info(e).unwrap(); let interface_info = constant_pool.get_class_info(e).unwrap();
let name = constant_pool.get_string(interface_info.name_index).unwrap(); let name = constant_pool.get_string(interface_info.name_index).unwrap();
self.get_or_load(&name).unwrap() self.get_or_load(&name, None).unwrap()
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
@ -282,6 +318,47 @@ impl ClassLoader {
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let super_classes = std::iter::successors(super_class.clone(), |sc| sc.super_class.clone())
.collect::<Vec<_>>();
#[allow(clippy::mutable_key_type)]
let mut result = HashSet::new();
let mut stack = super_classes.clone();
stack.extend(interfaces.iter().cloned());
while let Some(c) = stack.pop() {
if result.insert(c.clone()) {
if let Some(sc) = &c.super_class {
stack.push(sc.clone());
}
stack.extend(c.interfaces.iter().cloned());
}
}
let super_interfaces = result
.iter()
.cloned()
.filter(|e| e.access_flags.INTERFACE)
.collect::<Vec<_>>();
// if super_classes.len() > 1 {
// println!("Jobs Done");
// }
// if super_interfaces.len() > interfaces.len() {
// let mut super_names = super_interfaces
// .iter()
// .cloned()
// .map(|e| e.this_class.clone())
// .collect::<Vec<_>>();
// super_names.sort();
// println!("sif: {:#?}", super_names);
// let mut names = interfaces
// .iter()
// .cloned()
// .map(|e| e.this_class.clone())
// .collect::<Vec<_>>();
// names.sort();
// println!("if: {:#?}", names);
// println!("Ready to work");
// }
RuntimeClass { RuntimeClass {
constant_pool, constant_pool,
access_flags, access_flags,
@ -290,15 +367,92 @@ impl ClassLoader {
interfaces, interfaces,
fields, fields,
methods, methods,
init_state: Mutex::new(crate::class::InitState::NotInitialized), init_state: Mutex::new(InitState::NotInitialized),
mirror: OnceLock::new(),
super_classes,
super_interfaces,
component_type: None,
}
}
// pub fn get_or_create_array_class(class_name: &str) -> RuntimeClass {
// if let Some(class) = self.classes.get(&(class_name.to_string(), loader)) {
// return Ok(class.clone());
// }
// let class = self.load_class(class_name, loader)?;
// self.needs_init.push(class.clone());
// Ok(class)
// }
pub fn create_array_class(
&mut self,
component: Arc<RuntimeClass>,
) -> 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 serializable: Arc<RuntimeClass> = self.get_or_load("/java/io/Serializable", None).unwrap();
RuntimeClass {
constant_pool: Arc::new(vec![]),
access_flags: ClassFlags {
MODULE: false,
ENUM: false,
ANNOTATION: false,
SYNTHETIC: false,
ABSTRACT: true,
INTERFACE: false,
SUPER: false,
FINAL: true,
PUBLIC: true,
},
this_class: "name".to_string(),
super_class: Some(object_class.clone()),
interfaces: vec![cloneable.clone(), serializable.clone()],
fields: vec![],
methods: vec![], // clone() is handled specially
mirror: OnceLock::new(),
init_state: Mutex::new(InitState::Initialized), // arrays need no <clinit>
super_classes: vec![object_class],
super_interfaces: vec![cloneable, serializable],
component_type: Some(component), // new field
} }
} }
unsafe fn find_native<T>(&self, name: String) -> libloading::os::windows::Symbol<T> { pub fn primitive_class(&mut self, name: &str) -> Arc<RuntimeClass> {
// for (key, value) in self.native_libraries.iter() {
// // value.get()
// }
todo!("class_loader find native")
let klass = Arc::new(RuntimeClass {
constant_pool: Arc::new(vec![]),
access_flags: ClassFlags {
MODULE: false,
ENUM: false,
ANNOTATION: false,
SYNTHETIC: false,
ABSTRACT: false,
INTERFACE: false,
SUPER: false,
FINAL: false,
PUBLIC: false,
},
this_class: name.to_string(),
super_class: None,
interfaces: vec![],
fields: vec![],
methods: vec![],
mirror: Default::default(),
init_state: Mutex::new(InitState::NotInitialized),
super_classes: vec![],
super_interfaces: vec![],
component_type: None,
});
self.classes.insert((name.to_string(), None), klass.clone());
klass
} }
} }

View File

@ -1,3 +1,4 @@
use std::fmt::{Display, Formatter};
use crate::attributes::ArrayType; use crate::attributes::ArrayType;
use deku_derive::DekuRead; use deku_derive::DekuRead;
@ -315,10 +316,48 @@ pub enum Ops {
i2s, i2s,
// comparisons // comparisons
#[deku(id = 0x94)]
lcmp,
#[deku(id = 0x95)]
fcmpl,
#[deku(id = 0x96)]
fcmpg,
#[deku(id = 0x97)]
dcmpl,
#[deku(id = 0x98)]
dcmpg,
#[deku(id = 0x99)]
ifeq(i16),
#[deku(id = 0x9a)]
ifne(i16),
#[deku(id = 0x9b)]
iflt(i16),
#[deku(id = 0x9c)]
ifge(i16),
#[deku(id = 0x9d)]
ifgt(i16),
#[deku(id = 0x9e)]
ifle(i16),
#[deku(id = 0x9f)]
if_icmpeq(i16),
#[deku(id = 0xa0)]
if_icmpne(i16),
#[deku(id = 0xa1)]
if_icmplt(i16),
#[deku(id = 0xa2)]
if_icmpge(i16),
#[deku(id = 0xa3)]
if_icmpgt(i16),
#[deku(id = 0xa4)]
if_icmple(i16),
#[deku(id = 0xa5)]
if_acmpeq(i16),
#[deku(id = 0xa6)]
if_acmpne(i16),
// control // control
#[deku(id = 0xa7)] #[deku(id = 0xa7)]
goto(u16), goto(i16),
// discontinued // discontinued
#[deku(id = 0xa8)] #[deku(id = 0xa8)]
@ -383,4 +422,257 @@ pub enum Ops {
monitorenter, monitorenter,
#[deku(id = 0xC3)] #[deku(id = 0xC3)]
monitorexit, monitorexit,
//extended
#[deku(id = 0xC4)]
wide,
#[deku(id = 0xC5)]
multianewarray(u16, u8),
#[deku(id = 0xC6)]
ifnull(i16),
#[deku(id = 0xC7)]
ifnonnull(i16),
#[deku(id = 0xC8)]
goto_w(i32),
#[deku(id = 0xC9)]
jsr_w(i32),
// Reserved
#[deku(id = 0xCA)]
breakpoint,
#[deku(id = 0xFE)]
impdep1,
#[deku(id = 0xFF)]
impdep2,
}
impl Display for Ops {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
// Constants
Ops::nop => write!(f, "nop"),
Ops::aconst_null => write!(f, "aconst_null"),
Ops::iconst_m1 => write!(f, "iconst_m1"),
Ops::iconst_0 => write!(f, "iconst_0"),
Ops::iconst_1 => write!(f, "iconst_1"),
Ops::iconst_2 => write!(f, "iconst_2"),
Ops::iconst_3 => write!(f, "iconst_3"),
Ops::iconst_4 => write!(f, "iconst_4"),
Ops::iconst_5 => write!(f, "iconst_5"),
Ops::lconst_0 => write!(f, "lconst_0"),
Ops::lconst_1 => write!(f, "lconst_1"),
Ops::fconst_0 => write!(f, "fconst_0"),
Ops::fconst_1 => write!(f, "fconst_1"),
Ops::fconst_2 => write!(f, "fconst_2"),
Ops::dconst_0 => write!(f, "dconst_0"),
Ops::dconst_1 => write!(f, "dconst_1"),
Ops::bipush(v) => write!(f, "bipush {}", v),
Ops::sipush(v) => write!(f, "sipush {}", v),
Ops::ldc(idx) => write!(f, "ldc #{}", idx),
Ops::ldc_w(idx) => write!(f, "ldc_w #{}", idx),
Ops::ldc2_w(idx) => write!(f, "ldc2_w #{}", idx),
// Loads
Ops::iload(idx) => write!(f, "iload {}", idx),
Ops::lload(idx) => write!(f, "lload {}", idx),
Ops::fload(idx) => write!(f, "fload {}", idx),
Ops::dload(idx) => write!(f, "dload {}", idx),
Ops::aload(idx) => write!(f, "aload {}", idx),
Ops::iload_0 => write!(f, "iload_0"),
Ops::iload_1 => write!(f, "iload_1"),
Ops::iload_2 => write!(f, "iload_2"),
Ops::iload_3 => write!(f, "iload_3"),
Ops::lload_0 => write!(f, "lload_0"),
Ops::lload_1 => write!(f, "lload_1"),
Ops::lload_2 => write!(f, "lload_2"),
Ops::lload_3 => write!(f, "lload_3"),
Ops::fload_0 => write!(f, "fload_0"),
Ops::fload_1 => write!(f, "fload_1"),
Ops::fload_2 => write!(f, "fload_2"),
Ops::fload_3 => write!(f, "fload_3"),
Ops::dload_0 => write!(f, "dload_0"),
Ops::dload_1 => write!(f, "dload_1"),
Ops::dload_2 => write!(f, "dload_2"),
Ops::dload_3 => write!(f, "dload_3"),
Ops::aload_0 => write!(f, "aload_0"),
Ops::aload_1 => write!(f, "aload_1"),
Ops::aload_2 => write!(f, "aload_2"),
Ops::aload_3 => write!(f, "aload_3"),
Ops::iaload => write!(f, "iaload"),
Ops::laload => write!(f, "laload"),
Ops::faload => write!(f, "faload"),
Ops::daload => write!(f, "daload"),
Ops::aaload => write!(f, "aaload"),
Ops::baload => write!(f, "baload"),
Ops::caload => write!(f, "caload"),
Ops::saload => write!(f, "saload"),
// Stores
Ops::istore(idx) => write!(f, "istore {}", idx),
Ops::lstore(idx) => write!(f, "lstore {}", idx),
Ops::fstore(idx) => write!(f, "fstore {}", idx),
Ops::dstore(idx) => write!(f, "dstore {}", idx),
Ops::astore(idx) => write!(f, "astore {}", idx),
Ops::istore_0 => write!(f, "istore_0"),
Ops::istore_1 => write!(f, "istore_1"),
Ops::istore_2 => write!(f, "istore_2"),
Ops::istore_3 => write!(f, "istore_3"),
Ops::lstore_0 => write!(f, "lstore_0"),
Ops::lstore_1 => write!(f, "lstore_1"),
Ops::lstore_2 => write!(f, "lstore_2"),
Ops::lstore_3 => write!(f, "lstore_3"),
Ops::fstore_0 => write!(f, "fstore_0"),
Ops::fstore_1 => write!(f, "fstore_1"),
Ops::fstore_2 => write!(f, "fstore_2"),
Ops::fstore_3 => write!(f, "fstore_3"),
Ops::dstore_0 => write!(f, "dstore_0"),
Ops::dstore_1 => write!(f, "dstore_1"),
Ops::dstore_2 => write!(f, "dstore_2"),
Ops::dstore_3 => write!(f, "dstore_3"),
Ops::astore_0 => write!(f, "astore_0"),
Ops::astore_1 => write!(f, "astore_1"),
Ops::astore_2 => write!(f, "astore_2"),
Ops::astore_3 => write!(f, "astore_3"),
Ops::iastore => write!(f, "iastore"),
Ops::lastore => write!(f, "lastore"),
Ops::fastore => write!(f, "fastore"),
Ops::dastore => write!(f, "dastore"),
Ops::aastore => write!(f, "aastore"),
Ops::bastore => write!(f, "bastore"),
Ops::castore => write!(f, "castore"),
Ops::sastore => write!(f, "sastore"),
// Stack
Ops::pop => write!(f, "pop"),
Ops::pop2 => write!(f, "pop2"),
Ops::dup => write!(f, "dup"),
Ops::dup_x1 => write!(f, "dup_x1"),
Ops::dup_x2 => write!(f, "dup_x2"),
Ops::dup2 => write!(f, "dup2"),
Ops::dup2_x1 => write!(f, "dup2_x1"),
Ops::dup2_x2 => write!(f, "dup2_x2"),
Ops::swap => write!(f, "swap"),
// Math
Ops::iadd => write!(f, "iadd"),
Ops::ladd => write!(f, "ladd"),
Ops::fadd => write!(f, "fadd"),
Ops::dadd => write!(f, "dadd"),
Ops::isub => write!(f, "isub"),
Ops::lsub => write!(f, "lsub"),
Ops::fsub => write!(f, "fsub"),
Ops::dsub => write!(f, "dsub"),
Ops::imul => write!(f, "imul"),
Ops::lmul => write!(f, "lmul"),
Ops::fmul => write!(f, "fmul"),
Ops::dmul => write!(f, "dmul"),
Ops::idiv => write!(f, "idiv"),
Ops::ldiv => write!(f, "ldiv"),
Ops::fdiv => write!(f, "fdiv"),
Ops::ddiv => write!(f, "ddiv"),
Ops::irem => write!(f, "irem"),
Ops::lrem => write!(f, "lrem"),
Ops::frem => write!(f, "frem"),
Ops::drem => write!(f, "drem"),
Ops::ineg => write!(f, "ineg"),
Ops::lneg => write!(f, "lneg"),
Ops::fneg => write!(f, "fneg"),
Ops::dneg => write!(f, "dneg"),
Ops::ishl => write!(f, "ishl"),
Ops::lshl => write!(f, "lshl"),
Ops::ishr => write!(f, "ishr"),
Ops::lshr => write!(f, "lshr"),
Ops::iushr => write!(f, "iushr"),
Ops::lushr => write!(f, "lushr"),
Ops::iand => write!(f, "iand"),
Ops::land => write!(f, "land"),
Ops::ior => write!(f, "ior"),
Ops::lor => write!(f, "lor"),
Ops::ixor => write!(f, "ixor"),
Ops::lxor => write!(f, "lxor"),
Ops::iinc(idx, val) => write!(f, "iinc {}, {}", idx, val),
// Conversions
Ops::i2l => write!(f, "i2l"),
Ops::i2f => write!(f, "i2f"),
Ops::i2d => write!(f, "i2d"),
Ops::l2i => write!(f, "l2i"),
Ops::l2f => write!(f, "l2f"),
Ops::l2d => write!(f, "l2d"),
Ops::f2i => write!(f, "f2i"),
Ops::f2l => write!(f, "f2l"),
Ops::f2d => write!(f, "f2d"),
Ops::d2i => write!(f, "d2i"),
Ops::d2l => write!(f, "d2l"),
Ops::d2f => write!(f, "d2f"),
Ops::i2b => write!(f, "i2b"),
Ops::i2c => write!(f, "i2c"),
Ops::i2s => write!(f, "i2s"),
// Comparisons
Ops::lcmp => write!(f, "lcmp"),
Ops::fcmpl => write!(f, "fcmpl"),
Ops::fcmpg => write!(f, "fcmpg"),
Ops::dcmpl => write!(f, "dcmpl"),
Ops::dcmpg => write!(f, "dcmpg"),
Ops::ifeq(off) => write!(f, "ifeq {}", off),
Ops::ifne(off) => write!(f, "ifne {}", off),
Ops::iflt(off) => write!(f, "iflt {}", off),
Ops::ifge(off) => write!(f, "ifge {}", off),
Ops::ifgt(off) => write!(f, "ifgt {}", off),
Ops::ifle(off) => write!(f, "ifle {}", off),
Ops::if_icmpeq(off) => write!(f, "if_icmpeq {}", off),
Ops::if_icmpne(off) => write!(f, "if_icmpne {}", off),
Ops::if_icmplt(off) => write!(f, "if_icmplt {}", off),
Ops::if_icmpge(off) => write!(f, "if_icmpge {}", off),
Ops::if_icmpgt(off) => write!(f, "if_icmpgt {}", off),
Ops::if_icmple(off) => write!(f, "if_icmple {}", off),
Ops::if_acmpeq(off) => write!(f, "if_acmpeq {}", off),
Ops::if_acmpne(off) => write!(f, "if_acmpne {}", off),
// Control
Ops::goto(off) => write!(f, "goto {}", off),
Ops::jsr(off) => write!(f, "jsr {}", off),
Ops::ret(idx) => write!(f, "ret {}", idx),
Ops::tableswitch => write!(f, "tableswitch"),
Ops::lookupswitch => write!(f, "lookupswitch"),
Ops::ireturn => write!(f, "ireturn"),
Ops::lreturn => write!(f, "lreturn"),
Ops::freturn => write!(f, "freturn"),
Ops::dreturn => write!(f, "dreturn"),
Ops::areturn => write!(f, "areturn"),
Ops::return_void => write!(f, "return"),
// References
Ops::getstatic(idx) => write!(f, "getstatic #{}", idx),
Ops::putstatic(idx) => write!(f, "putstatic #{}", idx),
Ops::getfield(idx) => write!(f, "getfield #{}", idx),
Ops::putfield(idx) => write!(f, "putfield #{}", idx),
Ops::invokevirtual(idx) => write!(f, "invokevirtual #{}", idx),
Ops::invokespecial(idx) => write!(f, "invokespecial #{}", idx),
Ops::invokestatic(idx) => write!(f, "invokestatic #{}", idx),
Ops::invokeinterface(idx, count, _) => write!(f, "invokeinterface #{}, {}", idx, count),
Ops::invokedynamic(idx, _) => write!(f, "invokedynamic #{}", idx),
Ops::new(idx) => write!(f, "new #{}", idx),
Ops::newarray(atype) => write!(f, "newarray {:?}", atype),
Ops::anewarray(idx) => write!(f, "anewarray #{}", idx),
Ops::arraylength => write!(f, "arraylength"),
Ops::athrow => write!(f, "athrow"),
Ops::checkcast(idx) => write!(f, "checkcast #{}", idx),
Ops::instanceof(idx) => write!(f, "instanceof #{}", idx),
Ops::monitorenter => write!(f, "monitorenter"),
Ops::monitorexit => write!(f, "monitorexit"),
// Extended
Ops::wide => write!(f, "wide"),
Ops::multianewarray(idx, dims) => write!(f, "multianewarray #{}, {}", idx, dims),
Ops::ifnull(off) => write!(f, "ifnull {}", off),
Ops::ifnonnull(off) => write!(f, "ifnonnull {}", off),
Ops::goto_w(off) => write!(f, "goto_w {}", off),
Ops::jsr_w(off) => write!(f, "jsr_w {}", off),
// Reserved
Ops::breakpoint => write!(f, "breakpoint"),
Ops::impdep1 => write!(f, "impdep1"),
Ops::impdep2 => write!(f, "impdep2"),
}
}
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -6,10 +6,10 @@ macro_rules! store {
($self:expr, l, $index:expr) => {{ ($self:expr, l, $index:expr) => {{
{ {
let index: usize = $index; let index: usize = $index;
let value = $self.stack.pop().expect("Must contain value on stack"); let value = $self.pop()?;
trace!("\tStoring: {value:?} into local[{index}]"); trace!("\tStoring: {value} into local[{index}]");
$self.vars[index] = value; $self.vars.set(index , value);
$self.vars[index + 1] = Value::Reference(None);
Ok(ExecutionResult::Continue) Ok(ExecutionResult::Continue)
} }
}}; }};
@ -19,9 +19,9 @@ macro_rules! store {
($self:expr, i, $index:expr) => {{ ($self:expr, i, $index:expr) => {{
{ {
let index: usize = $index; let index: usize = $index;
let value = $self.stack.pop().expect("Must contain value on stack"); let value = $self.pop()?;
trace!("\tStoring: {value:?} into local[{index}]"); trace!("\tStoring: {value} into local[{index}]");
$self.vars[index] = value; $self.vars.set(index , value);
Ok(ExecutionResult::Continue) Ok(ExecutionResult::Continue)
} }
}}; }};
@ -38,7 +38,7 @@ macro_rules! load {
($self:expr, i, $index:expr) => {{ ($self:expr, i, $index:expr) => {{
{ {
let index: usize = $index; let index: usize = $index;
let value = $self.vars.get(index).expect("Local var to exist"); let value = $self.vars.get(index);
trace!("\tLoading: local[{index}] - {value} onto stack"); trace!("\tLoading: local[{index}] - {value} onto stack");
$self.stack.push(value.clone()); $self.stack.push(value.clone());
Ok(ExecutionResult::Continue) Ok(ExecutionResult::Continue)
@ -74,3 +74,203 @@ macro_rules! pool_get_impl {
} }
}; };
} }
#[macro_export]
macro_rules! if_int_zero {
($self:expr, $offset:expr, $op:tt) => {{
let Value::Primitive(Primitive::Int(v)) = $self.pop()? else {
return Err(VmError::stack_not_int());
};
if v $op 0 {
Ok(ExecutionResult::Advance($offset))
} else {
Ok(ExecutionResult::Continue)
}
}};
}
#[macro_export]
macro_rules! if_int_cmp {
($self:expr, $offset:expr, $op:tt) => {{
let Value::Primitive(Primitive::Int(v2)) = $self.pop()? else {
return Err(VmError::stack_not_int());
};
let Value::Primitive(Primitive::Int(v1)) = $self.pop()? else {
return Err(VmError::stack_not_int());
};
if v1 $op v2 {
Ok(ExecutionResult::Advance($offset))
} else {
Ok(ExecutionResult::Continue)
}
}};
}
#[macro_export]
macro_rules! float_cmp {
($self:expr, $prim:ident, $nan_result:expr) => {{
let v2 = $self.pop()?;
let v1 = $self.pop()?;
let (Value::Primitive(Primitive::$prim(f1)), Value::Primitive(Primitive::$prim(f2))) = (&v1, &v2) else {
return Err(VmError::StackError(format!(
"{v1:?} or {v2:?} was not a {}",
stringify!($prim).to_lowercase()
)));
};
let result: i32 = if f1.is_nan() || f2.is_nan() {
$nan_result
} else {
match f1.partial_cmp(f2) {
Some(std::cmp::Ordering::Greater) => 1,
Some(std::cmp::Ordering::Equal) => 0,
_ => -1,
}
};
$self.stack.push(Value::from(result));
Ok(ExecutionResult::Continue)
}};
}
#[macro_export]
macro_rules! convert_simple {
($self:expr, $from:ident, $to:ty) => {{
let Value::Primitive(Primitive::$from(v)) = $self.pop()? else {
return Err(VmError::StackError(format!(
"Expected {}", stringify!($from).to_lowercase()
)));
};
$self.stack.push(Value::from(v as $to));
Ok(ExecutionResult::Continue)
}};
}
#[macro_export]
macro_rules! convert_float_to_int {
($self:expr, $from:ident, $from_ty:ty, $to:ty) => {{
let Value::Primitive(Primitive::$from(v)) = $self.pop()? else {
return Err(VmError::StackError(format!(
"Expected {}", stringify!($from).to_lowercase()
)));
};
let result = if v.is_nan() {
0
} else if v >= <$to>::MAX as $from_ty {
<$to>::MAX
} else if v <= <$to>::MIN as $from_ty {
<$to>::MIN
} else {
v as $to
};
$self.stack.push(Value::from(result));
Ok(ExecutionResult::Continue)
}};
}
#[macro_export]
macro_rules! convert_int_narrow {
($self:expr, $to:ty) => {{
let Value::Primitive(Primitive::Int(v)) = $self.pop()? else {
return Err(VmError::stack_not_int());
};
$self.stack.push(Value::from((v as $to) as i32));
Ok(ExecutionResult::Continue)
}};
}
#[macro_export]
macro_rules! array_store {
($self:expr, $prim:ident, $arr:ident) => {{
let Value::Primitive(Primitive::$prim(value)) = $self.pop()? else {
return Err(VmError::StackError(format!(
"Value was not {}", stringify!($prim).to_lowercase()
)));
};
let Value::Primitive(Primitive::Int(index)) = $self.pop()? else {
return Err(VmError::stack_not_int());
};
let Value::Reference(Some(ReferenceKind::ArrayReference(ArrayReference::$arr(arr)))) = $self.pop()? else {
return Err(VmError::StackError(format!(
"Expected {} array reference", stringify!($arr).to_lowercase()
)));
};
arr.lock().unwrap().set(index, value);
Ok(ExecutionResult::Continue)
}};
}
#[macro_export]
macro_rules! array_store_cast {
($self:expr, $arr:ident, $cast:ty) => {{
let Value::Primitive(Primitive::Int(value)) = $self.pop()? else {
return Err(VmError::stack_not_int());
};
let Value::Primitive(Primitive::Int(index)) = $self.pop()? else {
return Err(VmError::stack_not_int());
};
let Value::Reference(Some(ReferenceKind::ArrayReference(ArrayReference::$arr(arr)))) = $self.pop()? else {
return Err(VmError::StackError(format!(
"Expected {} array reference", stringify!($arr).to_lowercase()
)));
};
arr.lock().unwrap().set(index, value as $cast);
Ok(ExecutionResult::Continue)
}};
}
//math
#[macro_export]
macro_rules! binary_op {
($self:expr, $prim:ident, |$a:ident, $b:ident| $expr:expr) => {{
let Value::Primitive(Primitive::$prim($b)) = $self.pop()? else {
return Err(VmError::StackError(format!("Expected {}", stringify!($prim).to_lowercase())));
};
let Value::Primitive(Primitive::$prim($a)) = $self.pop()? else {
return Err(VmError::StackError(format!("Expected {}", stringify!($prim).to_lowercase())));
};
$self.stack.push(Value::from($expr));
Ok(ExecutionResult::Continue)
}};
}
#[macro_export]
macro_rules! unary_op {
($self:expr, $prim:ident, |$v:ident| $expr:expr) => {{
let Value::Primitive(Primitive::$prim($v)) = $self.pop()? else {
return Err(VmError::StackError(format!("Expected {}", stringify!($prim).to_lowercase())));
};
$self.stack.push(Value::from($expr));
Ok(ExecutionResult::Continue)
}};
}
#[macro_export]
macro_rules! int_div_rem {
($self:expr, $prim:ident, $ty:ty, $op:tt) => {{
let Value::Primitive(Primitive::$prim(v2)) = $self.pop()? else {
return Err(VmError::StackError(format!("Expected {}", stringify!($prim).to_lowercase())));
};
let Value::Primitive(Primitive::$prim(v1)) = $self.pop()? else {
return Err(VmError::StackError(format!("Expected {}", stringify!($prim).to_lowercase())));
};
if v2 == 0 {
return Err(VmError::InvariantError("/ by zero".into()));
}
let result = if v1 == <$ty>::MIN && v2 == -1 {
<$ty>::MIN $op 1 // MIN / -1 overflows, MIN % -1 = 0
} else {
v1 $op v2
};
$self.stack.push(Value::from(result));
Ok(ExecutionResult::Continue)
}};
}
#[macro_export]
macro_rules! shift_op {
($self:expr, $prim:ident, $mask:expr, |$val:ident, $shift:ident| $expr:expr) => {{
let Value::Primitive(Primitive::Int(s)) = $self.pop()? else {
return Err(VmError::StackError("Expected int for shift amount".into()));
};
let Value::Primitive(Primitive::$prim($val)) = $self.pop()? else {
return Err(VmError::StackError(format!("Expected {}", stringify!($prim).to_lowercase())));
};
let $shift = (s & $mask) as u32;
$self.stack.push(Value::from($expr));
Ok(ExecutionResult::Continue)
}};
}

View File

@ -1,3 +1,27 @@
use roast_vm_core::vm::Vm;
use libloading::Library;
use log::LevelFilter;
fn main() { fn main() {
jvm_rs_core::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::attributes", LevelFilter::Info)
.filter_module("roast_vm_core::instructions", LevelFilter::Info)
.init();
let vm = Vm::new();
vm.load_native_library("roast_vm.dll", load("roast_vm.dll").into());
vm.load_native_library("jvm.dll", load("jvm.dll").into());
vm.load_native_library("java.dll", load("java.dll").into());
vm.run("org/example/Main");
}
fn load(filename: &str) -> Library {
let exe_path = std::env::current_exe().expect("get exe path");
let dll_path = exe_path.parent().unwrap().join(filename);
let leeb = unsafe { libloading::os::windows::Library::new(&dll_path) }.expect("load dll");
Library::from(leeb)
} }

View File

@ -1,9 +1,9 @@
use crate::class_file::ConstantPoolEntry; use crate::class_file::ConstantPoolEntry;
use dashmap::DashMap; use dashmap::DashMap;
use libloading::os::windows::Library;
use libloading::Library;
use std::collections::HashMap; use std::collections::HashMap;
pub type NativeLibraries = HashMap<String, Library>;
// impl NativeExt for NativeLibraries {} // impl NativeExt for NativeLibraries {}
// //
// trait NativeExt: AsRef<[..]> { // trait NativeExt: AsRef<[..]> {

View File

@ -1,35 +0,0 @@
use crate::class::RuntimeClass;
use crate::object::Object;
use crate::rng::generate_identity_hash;
use crate::ObjectRef;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
#[derive(Default)]
pub struct ObjectManager {
objects: HashMap<u32, ObjectRef>,
}
impl ObjectManager {
pub fn new(&mut self, class: Arc<RuntimeClass>) -> ObjectRef {
let id = generate_identity_hash();
assert!(
!self.objects.contains_key(&id),
"Generated ID already exists!"
);
let object = Arc::new(Mutex::new(Object {
id,
class: class.clone(),
fields: Default::default(),
}));
self.objects.insert(id, object.clone());
object
}
pub fn get(&self, id: u32) -> ObjectRef {
self.objects
.get(&id)
.expect("Object must be present")
.clone()
}
}

View File

@ -1,63 +1,121 @@
use crate::objects::object::{Object, ObjectReference}; use crate::objects::object::{Reference, ReferenceKind};
use crate::value::{Primitive, Value}; use crate::prim::Primitive;
use std::fmt::{Display, Formatter}; use jni::sys::{jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jshort};
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::ops::{Deref, DerefMut};
#[derive(Debug, Clone)]
pub enum Reference {
ObjectReference(ObjectReference),
ArrayReference(ArrayReference),
}
impl Display for Reference {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let id = match self {
Reference::ObjectReference(x) => x.lock().unwrap().id,
Reference::ArrayReference(ArrayReference::Primitive(x)) => x.lock().unwrap().0,
Reference::ArrayReference(ArrayReference::Object(x)) => x.lock().unwrap().0,
};
write!(f, "{}", id)
}
}
impl From<ObjectReference> for Reference {
fn from(value: ObjectReference) -> Self {
Self::ObjectReference(value)
}
}
impl From<PrimitiveArrayReference> for Reference {
fn from(value: PrimitiveArrayReference) -> Self {
Self::ArrayReference(ArrayReference::Primitive(value))
}
}
impl From<ObjectArrayReference> for Reference {
fn from(value: ObjectArrayReference) -> Self {
Self::ArrayReference(ArrayReference::Object(value))
}
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum ArrayReference { pub enum ArrayReference {
Primitive(PrimitiveArrayReference), Int(PrimitiveArrayReference<jint>),
Byte(PrimitiveArrayReference<jbyte>),
Short(PrimitiveArrayReference<jshort>),
Long(PrimitiveArrayReference<jlong>),
Float(PrimitiveArrayReference<jfloat>),
Double(PrimitiveArrayReference<jdouble>),
Char(PrimitiveArrayReference<jchar>),
Boolean(PrimitiveArrayReference<jboolean>),
Object(ObjectArrayReference), Object(ObjectArrayReference),
} }
pub type PrimitiveArrayReference = Arc<Mutex<PrimitiveArray>>; impl ArrayReference {
type PrimitiveArray = (u32, Vec<Primitive>); pub fn len(&self) -> jint {
match self {
ArrayReference::Int(x) => x.lock().unwrap().len(),
ArrayReference::Byte(x) => x.lock().unwrap().len(),
ArrayReference::Short(x) => x.lock().unwrap().len(),
ArrayReference::Long(x) => x.lock().unwrap().len(),
ArrayReference::Float(x) => x.lock().unwrap().len(),
ArrayReference::Double(x) => x.lock().unwrap().len(),
ArrayReference::Char(x) => x.lock().unwrap().len(),
ArrayReference::Boolean(x) => x.lock().unwrap().len(),
ArrayReference::Object(x) => x.lock().unwrap().len(),
}
}
pub type ObjectArrayReference = Arc<Mutex<ObjectArray>>; pub fn id(&self) -> u32 {
type ObjectArray = (u32, Vec<ObjectReference>); match self {
ArrayReference::Int(x) => x.lock().unwrap().id,
ArrayReference::Byte(x) => x.lock().unwrap().id,
ArrayReference::Short(x) => x.lock().unwrap().id,
ArrayReference::Long(x) => x.lock().unwrap().id,
ArrayReference::Float(x) => x.lock().unwrap().id,
ArrayReference::Double(x) => x.lock().unwrap().id,
ArrayReference::Char(x) => x.lock().unwrap().id,
ArrayReference::Boolean(x) => x.lock().unwrap().id,
ArrayReference::Object(x) => x.lock().unwrap().id,
}
}
}
pub type PrimitiveArrayReference<T: Primitive> = Arc<Mutex<Array<T>>>;
pub type ObjectArrayReference = Arc<Mutex<Array<Option<ReferenceKind>>>>;
#[derive(Debug)] #[derive(Debug)]
pub struct Array<T> { pub struct Array<T: ArrayValue> {
id: u32, pub(crate) id: u32,
backing: Vec<T>, pub(crate) backing: Box<[T]>,
} }
impl Array<Primitive> { impl<T> Array<T>
fn set(&self, index: i32, value: Value) { where
let thing : [100, u32] T: ArrayValue + Clone + Default,
{
pub fn set(&mut self, index: i32, value: T) {
self.backing[index as usize] = value
}
pub fn get(&self, index: i32) -> T {
self.backing[index as usize].clone()
}
pub fn new(id: u32, length: i32) -> Self {
Self {
id,
backing: vec![T::default(); length as usize].into_boxed_slice(),
} }
} }
pub fn len(&self) -> jint {
self.backing.len() as jint
}
}
impl<T: Primitive + ArrayValue> From<(u32, Vec<T>)> for Array<T> {
fn from(value: (u32, Vec<T>)) -> Self {
let (id, vector) = value;
Self {
id,
backing: vector.into_boxed_slice(),
}
}
}
impl<T: ArrayValue> Deref for Array<T> {
type Target = [T];
fn deref(&self) -> &Self::Target {
&self.backing
}
}
impl<T: ArrayValue> DerefMut for Array<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.backing
}
}
pub trait ArrayValue {}
impl ArrayValue for Reference {}
impl ArrayValue for jbyte {}
impl ArrayValue for jshort {}
impl ArrayValue for jint {}
impl ArrayValue for jlong {}
impl ArrayValue for jchar {}
impl ArrayValue for jfloat {}
impl ArrayValue for jdouble {}
impl ArrayValue for jboolean {}

View File

@ -1,3 +1,6 @@
pub mod array; pub mod array;
pub mod object; pub mod object;
pub mod object_manager; pub mod object_manager;
pub use object::Reference;
pub use object::ReferenceKind;

View File

@ -1,9 +1,14 @@
use crate::class::RuntimeClass; use crate::class::RuntimeClass;
use crate::objects::array::{ArrayReference, ObjectArrayReference, PrimitiveArrayReference};
use crate::prim::{jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jshort};
use crate::value::Value; use crate::value::Value;
use dashmap::DashMap; use dashmap::DashMap;
use log::trace; use log::trace;
use std::fmt::Display; use std::fmt::{Display, Formatter};
use std::hash::Hash;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use crate::class_file::FieldRef;
use crate::{string_from_bytes, BaseType, FieldType};
pub type ObjectReference = Arc<Mutex<Object>>; pub type ObjectReference = Arc<Mutex<Object>>;
@ -16,25 +21,67 @@ pub struct Object {
impl Object { impl Object {
pub fn set_field(&self, field_name: &str, value: Value) { pub fn set_field(&self, field_name: &str, value: Value) {
trace!("Fields for object:\n\t{:?}", self.fields); trace!("Fields for object:\n\t{:#}", self.format_fields());
trace!("Setting '{}' to '{}'", field_name, value); trace!("Setting '{}' to '{}'", field_name, value);
self.fields.insert(field_name.to_string(), value); self.fields.insert(field_name.to_string(), value);
} }
pub fn get_field(&self, field_name: &str) -> Value { pub fn get_field(&self, field_ref: &FieldRef) -> Value {
trace!("Fields for object:\n\t{:?}", self.fields); trace!("Fields for object:\n\t{:#}", self.format_fields());
self.fields self.fields
.get(&field_name.to_string()) .get(&field_ref.name)
.map(|e| e.clone()) .map(|e| e.clone())
.unwrap_or(Value::NULL) .unwrap_or_else(||{
let initial = match &field_ref.desc {
FieldType::Base(base) => {
match base {
BaseType::Byte => {
Value::from(0i8)
}
BaseType::Char => { Value::from(0u16) }
BaseType::Double => { Value::from(0f64) }
BaseType::Float => { Value::from(0f32) }
BaseType::Int => { Value::from(0i32) }
BaseType::Long => { Value::from(0i64) }
BaseType::Short => { Value::from(0i16) }
BaseType::Boolean => { Value::from(false) }
}
}
FieldType::ClassType(_) => { Value::NULL }
FieldType::ArrayType(_) => { Value::NULL }
};
self.fields.insert(field_ref.name.clone(), initial.clone());
initial
})
}
fn format_fields(&self) -> ObjectFields<'_, String, Value> {
ObjectFields(&self.fields)
} }
} }
impl Display for Object { impl Display for Object {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.class.this_class == "java/lang/String" {}
write!(f, "Object[id={}, class={}]", self.id, self.class.this_class) write!(f, "Object[id={}, class={}]", self.id, self.class.this_class)
} }
} }
struct ObjectFields<'a, K, V>(&'a DashMap<K, V>);
impl<K, V> Display for ObjectFields<'_, K, V>
where
K: Display + Eq + Hash + std::fmt::Debug,
V: Display,
{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let mut debug_map = f.debug_map();
for r in self.0 {
let (k, v) = r.pair();
debug_map.entry(k, &format!("{v}"));
}
debug_map.finish()
}
}
impl PartialEq for Object { impl PartialEq for Object {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
@ -43,3 +90,143 @@ impl PartialEq for Object {
} }
impl Eq for Object {} impl Eq for Object {}
#[derive(Debug, Clone)]
pub enum ReferenceKind {
ObjectReference(ObjectReference),
ArrayReference(ArrayReference),
}
impl ReferenceKind {
pub fn into_object_reference(self) -> Option<ObjectReference> {
match self {
Self::ObjectReference(inner) => Some(inner),
_ => None,
}
}
pub fn id(&self) -> u32 {
match self {
Self::ObjectReference(r) => r.lock().unwrap().id,
Self::ArrayReference(a) => a.id(),
}
}
}
pub type Reference = Option<ReferenceKind>;
impl Display for ReferenceKind {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let id = match self {
ReferenceKind::ObjectReference(x) => {
let guard = x.lock().unwrap();
if guard.class.this_class == "java/lang/String"
&& let Some(field) = guard.fields.get("value")
&& let Value::Reference(Some(ReferenceKind::ArrayReference(ArrayReference::Byte(actual)))) = field.value()
{
let arr_guard= actual.lock().unwrap();
let string = crate::string_from_bytes(&arr_guard);
format!("\u{AB}{}\u{BB}", string)
} else {
format!("Obj<{}>", guard.class.this_class)
}
}
ReferenceKind::ArrayReference(ArrayReference::Int(x)) => {
let guard = x.lock().unwrap();
format!("int{:?}", guard.backing)
}
ReferenceKind::ArrayReference(ArrayReference::Byte(x)) => {
let guard = x.lock().unwrap();
format!("byte{:?}", guard.backing)
}
ReferenceKind::ArrayReference(ArrayReference::Short(x)) => {
let guard = x.lock().unwrap();
format!("short{:?}", guard.backing)
}
ReferenceKind::ArrayReference(ArrayReference::Long(x)) => {
let guard = x.lock().unwrap();
format!("long{:?}", guard.backing)
}
ReferenceKind::ArrayReference(ArrayReference::Float(x)) => {
let guard = x.lock().unwrap();
format!("float{:?}", guard.backing)
}
ReferenceKind::ArrayReference(ArrayReference::Double(x)) => {
let guard = x.lock().unwrap();
format!("double{:?}", guard.backing)
}
ReferenceKind::ArrayReference(ArrayReference::Char(x)) => {
let guard = x.lock().unwrap();
format!("char{:?}", guard.backing)
}
ReferenceKind::ArrayReference(ArrayReference::Boolean(x)) => {
let guard = x.lock().unwrap();
format!("boolean{:?}", guard.backing)
}
ReferenceKind::ArrayReference(ArrayReference::Object(x)) => {
let guard = x.lock().unwrap();
format!("object[{:?}]", guard.id)
}
};
write!(f, "{}", id)
}
}
impl From<ObjectReference> for ReferenceKind {
fn from(value: ObjectReference) -> Self {
Self::ObjectReference(value)
}
}
impl From<PrimitiveArrayReference<jint>> for ReferenceKind {
fn from(value: PrimitiveArrayReference<jint>) -> Self {
Self::ArrayReference(ArrayReference::Int(value))
}
}
impl From<PrimitiveArrayReference<jbyte>> for ReferenceKind {
fn from(value: PrimitiveArrayReference<jbyte>) -> Self {
Self::ArrayReference(ArrayReference::Byte(value))
}
}
impl From<PrimitiveArrayReference<jshort>> for ReferenceKind {
fn from(value: PrimitiveArrayReference<jshort>) -> Self {
Self::ArrayReference(ArrayReference::Short(value))
}
}
impl From<PrimitiveArrayReference<jlong>> for ReferenceKind {
fn from(value: PrimitiveArrayReference<jlong>) -> Self {
Self::ArrayReference(ArrayReference::Long(value))
}
}
impl From<PrimitiveArrayReference<jfloat>> for ReferenceKind {
fn from(value: PrimitiveArrayReference<jfloat>) -> Self {
Self::ArrayReference(ArrayReference::Float(value))
}
}
impl From<PrimitiveArrayReference<jdouble>> for ReferenceKind {
fn from(value: PrimitiveArrayReference<jdouble>) -> Self {
Self::ArrayReference(ArrayReference::Double(value))
}
}
impl From<PrimitiveArrayReference<jchar>> for ReferenceKind {
fn from(value: PrimitiveArrayReference<jchar>) -> Self {
Self::ArrayReference(ArrayReference::Char(value))
}
}
impl From<PrimitiveArrayReference<jboolean>> for ReferenceKind {
fn from(value: PrimitiveArrayReference<jboolean>) -> Self {
Self::ArrayReference(ArrayReference::Boolean(value))
}
}
impl From<ObjectArrayReference> for ReferenceKind {
fn from(value: ObjectArrayReference) -> Self {
Self::ArrayReference(ArrayReference::Object(value))
}
}

View File

@ -0,0 +1,172 @@
use crate::attributes::ArrayType;
use crate::class::RuntimeClass;
use crate::class_file::ClassFlags;
use crate::objects;
use crate::objects::array::{Array, ArrayReference, ArrayValue, PrimitiveArrayReference};
use crate::objects::object::{Object, ObjectReference, Reference, ReferenceKind};
use crate::rng::generate_identity_hash;
use crate::value::{Primitive, Value};
use jni::sys::{jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jshort};
use log::warn;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
#[derive(Default)]
pub struct ObjectManager {
pub objects: HashMap<u32, ReferenceKind>,
strings: HashMap<String, u32>,
}
impl ObjectManager {
pub fn new_object(&mut self, class: Arc<RuntimeClass>) -> ObjectReference {
let id = generate_identity_hash();
assert!(
!self.objects.contains_key(&id),
"Generated ID already exists!"
);
let object = Arc::new(Mutex::new(Object {
id,
class: class.clone(),
fields: Default::default(),
}));
self.objects.insert(id, ReferenceKind::from(object.clone()));
object
}
pub fn new_primitive_array(&mut self, array_type: ArrayType, count: i32) -> ArrayReference {
let id = generate_identity_hash();
assert!(
!self.objects.contains_key(&id),
"Generated ID already exists!"
);
let array_ref = match array_type {
ArrayType::T_INT => ArrayReference::Int(Arc::new(Mutex::new(Array::new(id, count)))),
ArrayType::T_BYTE => ArrayReference::Byte(Arc::new(Mutex::new(Array::new(id, count)))),
ArrayType::T_SHORT => {
ArrayReference::Short(Arc::new(Mutex::new(Array::new(id, count))))
}
ArrayType::T_LONG => ArrayReference::Long(Arc::new(Mutex::new(Array::new(id, count)))),
ArrayType::T_FLOAT => {
ArrayReference::Float(Arc::new(Mutex::new(Array::new(id, count))))
}
ArrayType::T_DOUBLE => {
ArrayReference::Double(Arc::new(Mutex::new(Array::new(id, count))))
}
ArrayType::T_CHAR => ArrayReference::Char(Arc::new(Mutex::new(Array::new(id, count)))),
ArrayType::T_BOOLEAN => {
ArrayReference::Boolean(Arc::new(Mutex::new(Array::new(id, count))))
}
};
self.objects
.insert(id, ReferenceKind::ArrayReference(array_ref.clone()));
array_ref
}
pub fn new_object_array(&mut self, count: i32) -> ArrayReference {
let id = generate_identity_hash();
assert!(
!self.objects.contains_key(&id),
"Generated ID already exists!"
);
let array_ref = ArrayReference::Object(Arc::new(Mutex::new(Array::new(id, count))));
self.objects
.insert(id, ReferenceKind::ArrayReference(array_ref.clone()));
array_ref
}
pub fn new_byte_array(&mut self, vector: Vec<i8>) -> ArrayReference {
warn!("Manual sidechannel byte array creation");
let id = generate_identity_hash();
assert!(
!self.objects.contains_key(&id),
"Generated ID already exists!"
);
let array_ref = ArrayReference::Byte(Arc::new(Mutex::new(Array::from((id, vector)))));
self.objects
.insert(id, ReferenceKind::ArrayReference(array_ref.clone()));
array_ref
}
pub fn get(&self, id: u32) -> ReferenceKind {
self.objects
.get(&id)
.unwrap_or_else(|| {
let objs = self
.objects
.iter()
.map(|(x, y)| format!("{x} : {y}"))
.collect::<Vec<_>>();
panic!("Object must be present id: {id}\n{objs:#?}") })
.clone()
}
pub fn get_interned_string(&self, utf: &str) -> Option<ObjectReference> {
self.strings
.get(utf)
.map(|e| self.get(*e))
.and_then(ReferenceKind::into_object_reference)
}
pub fn new_string(&mut self, string_class: Arc<RuntimeClass>, utf8: &str) -> ObjectReference {
warn!("Manual sidechannel string creation: \n\"{}\"", utf8);
let key = utf8.to_owned();
let jstr = self.new_object(string_class);
let byte_vec = utf8
.encode_utf16()
.flat_map(|e| e.to_le_bytes())
.map(|e| e as i8)
.collect::<Vec<_>>();
let barray = self.new_byte_array(byte_vec);
jstr.lock().unwrap().fields.insert(
"value".to_string(),
Value::from(Some(ReferenceKind::ArrayReference(barray))),
);
jstr.lock()
.unwrap()
.fields
.insert("coder".to_string(), Value::from(1i8));
jstr.lock()
.unwrap()
.fields
.insert("hash".to_string(), Value::from(0i32));
jstr.lock()
.unwrap()
.fields
.insert("hashIsZero".to_string(), Value::from(false));
let id = jstr.lock().unwrap().id;
debug_assert!(!self.strings.contains_key(&key), "String already interned");
self.strings.insert(key, id);
jstr
}
pub fn new_class(
&mut self,
class_class: Arc<RuntimeClass>,
name: Reference,
module: Reference,
modifiers: ClassFlags,
primitive: bool,
) -> ObjectReference {
warn!("Manual sidechannel class creation");
let module = None;
let modifiers = 17u16;
let class_redefined_count = 0i32;
let clazz = self.new_object(class_class);
if let Ok(clakz) = clazz.lock() {
clakz.set_field("name", Value::from(name));
clakz.set_field("module", Value::from(module));
clakz.set_field("modifiers", Value::from(modifiers));
clakz.set_field("primitive", Value::from(primitive));
clakz.set_field("classRedefinedCount", Value::from(class_redefined_count));
}
clazz
}
}

13
crates/core/src/prim.rs Normal file
View File

@ -0,0 +1,13 @@
pub use jni::sys::{jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jshort};
pub trait Primitive {}
impl Primitive for jbyte {}
impl Primitive for jshort {}
impl Primitive for jint {}
impl Primitive for jlong {}
impl Primitive for jchar {}
impl Primitive for jfloat {}
impl Primitive for jdouble {}
impl Primitive for jboolean {}

View File

@ -2,56 +2,92 @@ use crate::class::RuntimeClass;
use crate::class_file::{ClassFile, MethodData, MethodRef}; use crate::class_file::{ClassFile, MethodData, MethodRef};
use crate::class_loader::{ClassLoader, LoaderRef}; use crate::class_loader::{ClassLoader, LoaderRef};
use crate::jni::create_jni_function_table; use crate::jni::create_jni_function_table;
use crate::objects::object::{ObjectReference, ReferenceKind};
use crate::objects::object_manager::ObjectManager; use crate::objects::object_manager::ObjectManager;
use crate::value::{Primitive, Value}; use crate::value::{Primitive, Value};
use crate::vm::Vm; use crate::vm::Vm;
use crate::{BaseType, FieldType, Frame, MethodDescriptor, VmError}; use crate::{BaseType, FieldType, Frame, MethodDescriptor, ThreadId, VmError};
use deku::DekuError::Incomplete; use deku::DekuError::Incomplete;
use jni::sys::{jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jobject, jshort}; use itertools::Itertools;
use jni::JNIEnv; use jni::sys::{jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jobject, jshort, JNIEnv};
use libffi::low::call; use libffi::low::call;
use libffi::middle::*; use libffi::middle::*;
use log::{trace, warn}; use log::{trace, warn};
use std::any::Any; use std::any::Any;
use std::ops::Add; use std::cell::RefCell;
use std::collections::VecDeque;
use std::ops::{Add, Deref};
use std::ptr::null_mut; use std::ptr::null_mut;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::{Arc, Mutex, RwLock}; use std::sync::{Arc, Mutex, RwLock};
use std::vec::IntoIter; use std::vec::IntoIter;
type MethodCallResult = Result<Option<Value>, VmError>; type MethodCallResult = Result<Option<Value>, VmError>;
// Thread-local storage for current thread ID
// In single-threaded mode: stores the one thread ID
// In multi-threaded mode: each OS thread has its own thread ID
thread_local! {
static CURRENT_THREAD_ID: RefCell<Option<ThreadId>> = RefCell::new(None);
}
// A thread of execution // A thread of execution
pub struct VmThread { pub struct VmThread {
pub id: ThreadId,
pub vm: Arc<Vm>, pub vm: Arc<Vm>,
pub loader: Arc<Mutex<ClassLoader>>, pub loader: Arc<Mutex<ClassLoader>>,
pub frame_stack: Vec<Frame>, pub frame_stack: Vec<Frame>,
pub gc: Arc<RwLock<ObjectManager>>, pub gc: Arc<RwLock<ObjectManager>>,
pub jni_env: JNIEnv,
} }
impl VmThread { impl VmThread {
pub fn new(vm: Arc<Vm>, loader: Option<LoaderRef>) -> Self { pub fn new(vm: Arc<Vm>, loader: Option<LoaderRef>) -> Arc<Self> {
static NEXT_ID: AtomicU64 = AtomicU64::new(0);
let id = ThreadId(NEXT_ID.fetch_add(1, Ordering::SeqCst));
let loader = loader.unwrap_or(vm.loader.clone()); let loader = loader.unwrap_or(vm.loader.clone());
let gc = vm.gc.clone();
Arc::new_cyclic(|weak_self| {
let jni_env = create_jni_function_table(weak_self.as_ptr() as *mut VmThread);
Self { Self {
vm: vm.clone(), id,
vm,
loader, loader,
frame_stack: Vec::new(), frame_stack: Vec::new(),
gc: vm.gc.clone(), gc,
jni_env,
} }
})
}
/// 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);
});
}
/// 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 initialized. /// Get or resolve a class, ensuring it and its dependencies are initialized.
/// Follows JVM Spec 5.5 for recursive initialization handling. /// Follows JVM Spec 5.5 for recursive initialization handling.
pub fn get_or_resolve_class( pub fn get_or_resolve_class(&self, what: &str) -> Result<Arc<RuntimeClass>, VmError> {
&self,
what: &str,
thread: Arc<VmThread>,
) -> Result<Arc<RuntimeClass>, VmError> {
// Phase 1: Load the class (short lock) // Phase 1: Load the class (short lock)
let runtime_class = self let runtime_class = self
.loader .loader
.lock() .lock()
.unwrap() .unwrap()
.get_or_load(what) .get_or_load(what, None)
.map_err(VmError::LoaderError)?; .map_err(VmError::LoaderError)?;
// Phase 2: Collect classes that need initialisation (short lock) // Phase 2: Collect classes that need initialisation (short lock)
@ -64,7 +100,7 @@ impl VmThread {
// Phase 3: Initialise each class (NO lock held - allows recursion) // Phase 3: Initialise each class (NO lock held - allows recursion)
for class in classes_to_init { for class in classes_to_init {
self.init(class, thread.clone())?; self.init(class)?;
} }
Ok(runtime_class) Ok(runtime_class)
@ -74,13 +110,13 @@ impl VmThread {
self.loader self.loader
.lock() .lock()
.unwrap() .unwrap()
.get_or_load(what) .get_or_load(what, None)
.map_err(VmError::LoaderError) .map_err(VmError::LoaderError)
} }
/// Initialize a class following JVM Spec 5.5. /// Initialize a class following JVM Spec 5.5.
/// Handles recursive initialization by tracking which thread is initializing. /// Handles recursive initialization by tracking which thread is initializing.
fn init(&self, class: Arc<RuntimeClass>, thread: Arc<VmThread>) -> Result<(), VmError> { fn init(&self, class: Arc<RuntimeClass>) -> Result<(), VmError> {
use crate::class::InitState; use crate::class::InitState;
use std::thread; use std::thread;
@ -122,23 +158,40 @@ impl VmThread {
} }
} }
} }
let class_class = self.get_class("java/lang/Class")?;
// Perform actual initialization let string = self.intern_string(&class.this_class);
let class_obj = self.gc.write().unwrap().new_class(
class_class,
Some(ReferenceKind::ObjectReference(string)),
None,
class.access_flags,
false
);
let id = class_obj.lock().unwrap().id;
class.mirror.set(id).expect("woops, id already set");
// Perform actual initialisation
trace!("Initializing class: {}", class.this_class); trace!("Initializing class: {}", class.this_class);
let result = (|| { let result = (|| {
// Initialize superclass first (if any) // Initialize superclass first (if any)
if let Some(ref super_class) = class.super_class { if let Some(ref super_class) = class.super_class {
self.init(super_class.clone(), thread.clone())?; self.init(super_class.clone())?;
} }
// Run <clinit> if present // Run <clinit> if present
let class_init_method = class.find_method("<clinit>", &MethodDescriptor::void()); let class_init_method = class.find_method("<clinit>", &MethodDescriptor::void());
if let Ok(method) = class_init_method { if let Ok(method) = class_init_method {
let method_ref = MethodRef {
class: class.this_class.clone(),
name: method.name.clone(),
desc: method.desc.clone(),
};
Frame::new( Frame::new(
method_ref,
method.code.clone().unwrap(), method.code.clone().unwrap(),
class.constant_pool.clone(), class.constant_pool.clone(),
vec![], vec![],
thread.clone(), self.vm.clone(),
) )
.execute() .execute()
.map_err(|e| VmError::LoaderError(format!("Error in <clinit>: {:?}", e)))?; .map_err(|e| VmError::LoaderError(format!("Error in <clinit>: {:?}", e)))?;
@ -163,41 +216,20 @@ impl VmThread {
result result
} }
pub fn invoke_main(&self, what: &str, thread: Arc<VmThread>) { pub fn invoke_main(&self, what: &str) {
let method_ref = MethodRef { let method_ref = MethodRef {
class: what.to_string(), class: what.to_string(),
name: "main".to_string(), name: "main".to_string(),
desc: MethodDescriptor::psvm(), desc: MethodDescriptor::psvm(),
}; };
self.invoke(method_ref, Vec::new(), thread) self.invoke(method_ref, Vec::new())
.expect("Main method died"); .expect("Main method died");
return ();
let class = self.get_or_resolve_class(what, thread.clone()).unwrap();
println!("invoking main: {}", class.this_class);
let main_method = class.find_method("main", &MethodDescriptor::psvm());
println!("{:?}", main_method);
if let Ok(meth) = main_method {
let mut frame = Frame::new(
meth.code.clone().unwrap(),
class.constant_pool.clone(),
vec![],
thread.clone(),
);
// self.frame_stack.push(frame);
frame.execute().expect("Error in main");
// self.frame_stack.first().unwrap().execute();
}
} }
pub fn invoke( pub fn invoke(&self, method_reference: MethodRef, mut args: Vec<Value>) -> MethodCallResult {
&self, let mut new_args = Vec::new();
method_reference: MethodRef, let class = self.get_or_resolve_class(&method_reference.class)?;
args: Vec<Value>,
thread: Arc<VmThread>,
) -> MethodCallResult {
let class = self.get_or_resolve_class(&method_reference.class, thread.clone())?;
let resolved_method = class let resolved_method = class
.find_method(&method_reference.name, &method_reference.desc) .find_method(&method_reference.name, &method_reference.desc)
.unwrap(); .unwrap();
@ -207,37 +239,45 @@ impl VmThread {
class.this_class class.this_class
); );
if resolved_method.flags.ACC_NATIVE { if resolved_method.flags.ACC_NATIVE {
return self.invoke_native(&method_reference, args); if resolved_method.flags.ACC_STATIC {
let jclass = self.vm.gc.read().unwrap().get(*class.mirror.wait());
new_args.push(Value::Reference(Some(jclass)));
}
for arg in args {
new_args.push(arg)
}
return self.invoke_native(&method_reference, new_args);
} }
let mut frame = Frame::new( let mut frame = Frame::new(
method_reference.clone(),
resolved_method.code.clone().unwrap(), resolved_method.code.clone().unwrap(),
class.constant_pool.clone(), class.constant_pool.clone(),
args, args,
thread.clone(), self.vm.clone(),
); );
frame.execute() frame.execute()
} }
pub fn invoke_native(&self, method: &MethodRef, args: Vec<Value>) -> MethodCallResult { pub fn invoke_native(&self, method: &MethodRef, mut args: Vec<Value>) -> MethodCallResult {
let symbol_name = generate_jni_method_name(method); let symbol_name = generate_jni_method_name(method, false);
trace!("searching for native symbol: {:?}", &symbol_name); trace!("searching for native symbol: {:?}", &symbol_name);
let result = unsafe { if symbol_name.contains("Java_java_lang_Class_desiredAssertionStatus0") {
// manually load relevant library for poc warn!("MAJOR HACK, figure out what is wrong with desiredAssertionStatus0");
let lib = libloading::os::windows::Library::new( return Ok(Some(Value::from(false)));
"C:\\Program Files\\Java\\jdk-25\\bin\\jvm_rs.dll", }
)
.expect("load jvm_rs.dll");
let result = unsafe {
let p = self
.vm
.find_native_method(&symbol_name)
.or_else(|| {
let name_with_params = generate_jni_method_name(method, true);
self.vm.find_native_method(&name_with_params)
})
.ok_or(VmError::NativeError("Link error".to_owned()))?;
// build pointer to native fn // build pointer to native fn
let cp = CodePtr::from_ptr( let cp = CodePtr::from_ptr(p);
lib.get::<*const ()>(symbol_name.as_ref())
.unwrap()
.as_raw_ptr(),
);
// build actual JNI interface that forms the table of
// native functions that can be used to manipulate the JVM
let jnienv = create_jni_function_table();
// let args = build_args(args); // let args = build_args(args);
@ -245,8 +285,14 @@ impl VmThread {
// let l = method.build_cif().call::<jlong>(cp, args.as_ref()); // let l = method.build_cif().call::<jlong>(cp, args.as_ref());
let mut storage = Vec::new(); let mut storage = Vec::new();
trace!("passing {args:?} to native fn"); trace!("passing {} to native fn", Value::format_vec(&args));
let built_args = build_args(args, &mut storage); let deq_args = VecDeque::from(args);
let built_args = build_args(
deq_args,
&mut storage,
&self.jni_env as *const _ as *mut JNIEnv,
);
let cif = method.build_cif(); let cif = method.build_cif();
match &method.desc.return_type { match &method.desc.return_type {
@ -288,8 +334,17 @@ impl VmThread {
} }
Some(FieldType::ClassType(_)) | Some(FieldType::ArrayType(_)) => { Some(FieldType::ClassType(_)) | Some(FieldType::ArrayType(_)) => {
let v = cif.call::<jobject>(cp, built_args.as_ref()); let v = cif.call::<jobject>(cp, built_args.as_ref());
// TODO: Convert jobject to Reference properly // Convert jobject (u32 ID) to Reference
let obj_id = v as u32;
if obj_id == 0 {
// Null reference
Ok(Some(Value::Reference(None))) Ok(Some(Value::Reference(None)))
} else {
// Look up the object in the ObjectManager
let gc = self.gc.read().unwrap();
let reference_kind = gc.get(obj_id);
Ok(Some(Value::Reference(Some(reference_kind))))
}
} }
} }
}; };
@ -298,10 +353,24 @@ impl VmThread {
} }
} }
fn build_args<'a>(params: Vec<Value>, storage: &'a mut Vec<Box<dyn Any>>) -> Vec<Arg<'a>> { fn build_args<'a>(
// Store values in the provided storage mut params: VecDeque<Value>,
storage.push(Box::new(create_jni_function_table()) as Box<dyn Any>); storage: &'a mut Vec<Box<dyn Any>>,
storage.push(Box::new(std::ptr::null_mut::<()>()) as Box<dyn Any>); jnienv: *mut JNIEnv,
) -> Vec<Arg<'a>> {
// Slot 0: JNIEnv
storage.push(Box::new(jnienv));
// Slot 1: this (instance) or class (static) — first param either way
let receiver = params.pop_front();
let receiver_id = match receiver {
Some(Value::Reference(Some(ReferenceKind::ObjectReference(ref_kind)))) => {
ref_kind.lock().unwrap().id
} // however you get the u32 ID
Some(Value::Reference(None)) => 0, // null
_ => panic!("first arg must be reference"),
};
storage.push(Box::new(receiver_id as jobject));
for value in params { for value in params {
match value { match value {
@ -313,7 +382,11 @@ fn build_args<'a>(params: Vec<Value>, storage: &'a mut Vec<Box<dyn Any>>) -> Vec
Value::Primitive(Primitive::Byte(x)) => storage.push(Box::new(x) as Box<dyn Any>), Value::Primitive(Primitive::Byte(x)) => storage.push(Box::new(x) as Box<dyn Any>),
Value::Primitive(Primitive::Short(x)) => storage.push(Box::new(x) as Box<dyn Any>), Value::Primitive(Primitive::Short(x)) => storage.push(Box::new(x) as Box<dyn Any>),
Value::Primitive(Primitive::Long(x)) => storage.push(Box::new(x) as Box<dyn Any>), Value::Primitive(Primitive::Long(x)) => storage.push(Box::new(x) as Box<dyn Any>),
Value::Reference(x) => storage.push(Box::new(x) as Box<dyn Any>), Value::Reference(x) => {
let id = x.map(|r| r.id()).unwrap_or(0) as jobject;
storage.push(Box::new(id));
}
Value::Padding => { panic!("Uhh not possible chief") }
} }
} }
@ -321,10 +394,31 @@ fn build_args<'a>(params: Vec<Value>, storage: &'a mut Vec<Box<dyn Any>>) -> Vec
storage.iter().map(|boxed| arg(&**boxed)).collect() storage.iter().map(|boxed| arg(&**boxed)).collect()
} }
pub fn generate_jni_method_name(method_ref: &MethodRef) -> String { pub fn generate_jni_method_name(method_ref: &MethodRef, with_type: bool) -> String {
let class_name = &method_ref.class.replace("/", "_"); let class_name = jni_escape(&method_ref.class);
let method_name = &method_ref.name; let method_name = jni_escape(&method_ref.name);
format!("Java_{class_name}_{method_name}")
let mut name = format!("Java_{class_name}_{method_name}");
if with_type {
let params = jni_escape(&method_ref.desc.param_string());
let str = format!("__{}", params);
name.push_str(&str)
}
name
}
pub fn jni_escape_char(c: char) -> String {
match c {
'/' => "_".to_string(),
'_' => "_1".to_string(),
';' => "_2".to_string(),
'[' => "_3".to_string(),
c if c.is_ascii_alphanumeric() => c.to_string(),
c => format!("_0{:04x}", c as u32),
}
}
pub fn jni_escape(s: &str) -> String {
s.chars().map(jni_escape_char).join("")
} }
impl From<FieldType> for Type { impl From<FieldType> for Type {
@ -363,3 +457,27 @@ impl MethodRef {
Builder::new().args(args).res(return_type).into_cif() Builder::new().args(args).res(return_type).into_cif()
} }
} }
impl VmThread {
/// perhaps misleadingly named
/// was once called get_or_make_string
/// it will look for an already created string, and if its exists, return it
/// if not, will cause a new String to be made, which at the time always interns it
pub fn intern_string(&self, utf: &str) -> ObjectReference {
// Fast path: read lock
if let Some(existing) = self.gc.read().unwrap().get_interned_string(utf) {
return existing;
}
// Slow path: write lock with re-check
let mut gc = self.gc.write().unwrap();
// Another thread may have inserted while we waited for the write lock
if let Some(existing) = gc.get_interned_string(utf) {
return existing;
}
let string_class = self.get_class("java/lang/String").unwrap();
gc.new_string(string_class, utf)
}
}

View File

@ -1,7 +1,12 @@
use crate::objects::array::Reference; use crate::objects::array::ArrayReference;
use crate::objects::object::ObjectReference; use crate::objects::object::{ObjectReference, ReferenceKind};
use crate::{BaseType, FieldType, VmError};
use core::fmt;
use dashmap::DashMap;
use jni::sys::{jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jshort}; use jni::sys::{jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jshort};
use log::trace;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
use std::ops::Deref;
/// A reference-counted, thread-safe pointer to an Object. /// A reference-counted, thread-safe pointer to an Object.
@ -13,7 +18,155 @@ use std::fmt::{Display, Formatter};
pub enum Value { pub enum Value {
Primitive(Primitive), Primitive(Primitive),
/// Reference to an object (or null) /// Reference to an object (or null)
Reference(Option<Reference>), Reference(Option<ReferenceKind>),
/// Second slot of a wide value (long/double). Should never be accessed directly.
Padding,
}
#[derive( Clone, Default)]
pub struct OperandStack(Vec<Value>);
impl OperandStack {
pub fn new() -> Self {
Self(Vec::new())
}
pub fn with_capacity(max_stack: usize) -> Self {
let backing = Vec::with_capacity(max_stack);
Self {
0: backing
}
}
pub fn push(&mut self, value: Value) {
self.0.push(value);
}
pub fn pop(&mut self) -> Result<Value, VmError> {
self.0.pop()
.ok_or_else(|| VmError::StackError("Stack underflow".to_string()))
}
pub fn peek(&self) -> Result<&Value, VmError> {
self.0.last()
.ok_or_else(|| VmError::StackError("Stack underflow on peek".to_string()))
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
/// Pop n values, returned in the order they were pushed (not pop order)
pub fn pop_n(&mut self, n: usize) -> Result<Vec<Value>, VmError> {
let start = self.0.len()
.checked_sub(n)
.ok_or_else(|| VmError::StackError("Stack underflow on pop_n".to_string()))?;
Ok(self.0.drain(start..).collect())
}
}
impl std::fmt::Debug for OperandStack {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_list().entries(self.0.iter()).finish()
}
}
impl OperandStack {
pub fn iter(&self) -> std::slice::Iter<'_, Value> {
self.into_iter()
}
}
impl<'a> IntoIterator for &'a OperandStack {
type Item = &'a Value;
type IntoIter = std::slice::Iter<'a, Value>;
fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}
#[derive(Clone, Default)]
pub struct LocalVariables {
inner: Vec<Value>
}
impl LocalVariables {
pub fn with_capacity(max_locals: usize) -> Self {
Self {
inner: vec![Value::NULL; max_locals]
}
}
pub fn from_args(args: Vec<Value>, max_locals: usize) -> Self {
let mut locals = Self::with_capacity(max_locals);
let mut idx = 0;
for arg in args {
locals.set(idx, arg.clone());
idx += if arg.is_wide() { 2 } else { 1 };
}
locals
}
pub fn set(&mut self, index: usize, value: Value) {
let idx = index;
let is_wide = value.is_wide();
self.inner[idx] = value;
if is_wide {
self.inner[idx + 1] = Value::Padding;
}
}
pub fn get(&self, index: usize) -> &Value {
let val = &self.inner[index];
if matches!(val, Value::Padding) {
panic!(
"Attempted to read padding slot at index {} (second half of wide value at {})",
index,
index - 1
);
}
val
}
pub fn iter(&self) -> impl Iterator<Item=&Value> {
self.inner.iter().filter(|v| !matches!(v, Value::Padding))
}
pub fn slots(&self) -> impl Iterator<Item=(u16, &Value)> {
self.inner
.iter()
.enumerate()
.filter(|(_, v)| !matches!(v, Value::Padding))
.map(|(i, v)| (i as u16, v))
}
}
impl std::fmt::Debug for LocalVariables {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_list()
.entries(self.slots().map(|(i, v)| format!("[{}]: {:?}", i, v)))
.finish()
}
}
impl Value {
pub const NULL: Value = Value::Reference(None);
pub fn format_vec(values: &Vec<Value>) -> String {
let fmt = values
.iter()
.map(|v| v.to_string())
.collect::<Vec<String>>()
.join(", ");
format!("[{}]", fmt)
}
pub fn is_wide(&self) -> bool {
matches!(self, Value::Primitive(Primitive::Long(_) | Primitive::Double(_)))
}
} }
macro_rules! impl_value_from { macro_rules! impl_value_from {
@ -39,6 +192,30 @@ impl From<jboolean> for Value {
} }
} }
impl_value_from!(bool, Boolean);
impl From<Option<ReferenceKind>> for Value {
fn from(value: Option<ReferenceKind>) -> Self {
Self::Reference(value)
}
}
impl From<ReferenceKind> for Value {
fn from(value: ReferenceKind) -> Self {
Value::from(Some(value))
}
}
impl From<ObjectReference> for Value {
fn from(value: ObjectReference) -> Self {
Value::from(ReferenceKind::ObjectReference(value))
}
}
impl From<ArrayReference> for Value {
fn from(value: ArrayReference) -> Self {
Value::from(ReferenceKind::ArrayReference(value))
}
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum Primitive { pub enum Primitive {
/// Boolean value (true/false) /// Boolean value (true/false)
@ -63,7 +240,12 @@ impl Display for Primitive {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self { match self {
Primitive::Boolean(b) => write!(f, "bool({})", b), Primitive::Boolean(b) => write!(f, "bool({})", b),
Primitive::Char(c) => write!(f, "char({})", char::from_u32(*c as u32).unwrap_or('?')), Primitive::Char(c) => {
let ch = char::from_u32(*c as u32)
.map(|c| format!("'{}'", c))
.unwrap_or_else(|| format!("'\\u{{{:04x}}}'", c));
write!(f, "{}", ch)
}
Primitive::Float(fl) => write!(f, "float({})", fl), Primitive::Float(fl) => write!(f, "float({})", fl),
Primitive::Double(d) => write!(f, "double({})", d), Primitive::Double(d) => write!(f, "double({})", d),
Primitive::Byte(b) => write!(f, "byte({})", b), Primitive::Byte(b) => write!(f, "byte({})", b),
@ -74,16 +256,98 @@ impl Display for Primitive {
} }
} }
impl Value {
pub const NULL: Value = Value::Reference(None);
}
impl Display for Value { impl Display for Value {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self { match self {
Value::Primitive(prim) => write!(f, "{}", prim), Value::Primitive(prim) => write!(f, "{}", prim),
Value::Reference(Some(obj)) => write!(f, "Ref({})", obj), Value::Reference(Some(obj)) => write!(f, "Ref({})", obj),
Value::Reference(None) => write!(f, "null"), Value::Reference(None) => write!(f, "null"),
_ => { write!(f, "pad") }
}
}
}
impl PartialEq<FieldType> for Value {
fn eq(&self, other: &FieldType) -> bool {
match self {
Value::Primitive(prim) => match prim {
Primitive::Boolean(_) => other.eq(&FieldType::Base(BaseType::Boolean)),
Primitive::Char(_) => other.eq(&FieldType::Base(BaseType::Char)),
Primitive::Float(_) => other.eq(&FieldType::Base(BaseType::Float)),
Primitive::Double(_) => other.eq(&FieldType::Base(BaseType::Double)),
Primitive::Byte(_) => other.eq(&FieldType::Base(BaseType::Byte)),
Primitive::Short(_) => other.eq(&FieldType::Base(BaseType::Short)),
Primitive::Int(_) => {
trace!("{other:?}");
trace!("{self}");
let result = match other {
FieldType::Base(BaseType::Boolean)
| FieldType::Base(BaseType::Byte)
| FieldType::Base(BaseType::Char)
| FieldType::Base(BaseType::Short)
| FieldType::Base(BaseType::Int) => true,
_ => false,
};
trace!("{}", result);
result
}
Primitive::Long(_) => other.eq(&FieldType::Base(BaseType::Long)),
},
Value::Reference(refe) => match refe {
None => match other {
FieldType::Base(_) => false,
FieldType::ClassType(_) => true,
FieldType::ArrayType(_) => true,
},
Some(kind) => match kind {
ReferenceKind::ObjectReference(found_ref) => {
if let FieldType::ClassType(expected) = other {
let found = format!("L{};", found_ref.lock().unwrap().class.this_class);
expected.eq(&found)
} else {
false
}
}
ReferenceKind::ArrayReference(x) => match x {
ArrayReference::Int(x) => {
matches!(other, FieldType::ArrayType(inner) if matches!(**inner, FieldType::Base(BaseType::Int)))
}
ArrayReference::Byte(x) => {
matches!(other, FieldType::ArrayType(inner) if matches!(**inner, FieldType::Base(BaseType::Byte)))
}
ArrayReference::Short(x) => {
matches!(other, FieldType::ArrayType(inner) if matches!(**inner, FieldType::Base(BaseType::Short)))
}
ArrayReference::Long(x) => {
matches!(other, FieldType::ArrayType(inner) if matches!(**inner, FieldType::Base(BaseType::Long)))
}
ArrayReference::Float(x) => {
matches!(other, FieldType::ArrayType(inner) if matches!(**inner, FieldType::Base(BaseType::Float)))
}
ArrayReference::Double(x) => {
matches!(other, FieldType::ArrayType(inner) if matches!(**inner, FieldType::Base(BaseType::Double)))
}
ArrayReference::Char(x) => {
matches!(other, FieldType::ArrayType(inner) if matches!(**inner, FieldType::Base(BaseType::Char)))
}
ArrayReference::Boolean(x) => {
matches!(other, FieldType::ArrayType(inner) if matches!(**inner, FieldType::Base(BaseType::Boolean)))
}
ArrayReference::Object(x) => {
if let FieldType::ArrayType(inner) = other {
if let FieldType::ClassType(_) = **inner {
true
} else {
false
}
} else {
false
}
}
},
},
},
_ => { panic!("uhh what") }
} }
} }
} }

View File

@ -1,34 +1,147 @@
use crate::class_file::ClassFile; #[cfg(libloading_docs)]
use super::os::unix as imp;
use std::ffi::c_void;
// the implementation used here doesn't matter particularly much...
#[cfg(all(not(libloading_docs), unix))]
use libloading::os::unix as imp;
#[cfg(all(not(libloading_docs), windows))]
use libloading::os::windows as imp;
use crate::class_file::{ClassFlags, MethodRef};
use crate::class_loader::ClassLoader; use crate::class_loader::ClassLoader;
use crate::objects::object::Object;
use crate::objects::object_manager::ObjectManager; use crate::objects::object_manager::ObjectManager;
use crate::thread::VmThread; use crate::thread::VmThread;
use crate::Frame; use crate::{MethodDescriptor, ThreadId, VmError};
use libloading::os::windows::Symbol; use dashmap::DashMap;
use std::collections::HashMap; use imp::{Library, Symbol};
use std::sync::{Arc, Mutex, RwLock}; use std::sync::{Arc, Mutex, RwLock};
use crate::class::{InitState, RuntimeClass};
use crate::objects::object::ReferenceKind;
// struct AbstractObject<'a> {} // struct AbstractObject<'a> {}
pub struct Vm { pub struct Vm {
// for now, model just a single thread // Thread registry - maps ThreadId to VmThread
pub thread: Mutex<Vec<Arc<VmThread>>>, pub threads: DashMap<ThreadId, Arc<VmThread>>,
pub main_thread_id: ThreadId,
pub loader: Arc<Mutex<ClassLoader>>, pub loader: Arc<Mutex<ClassLoader>>,
pub native_methods: HashMap<String, Symbol<()>>, pub native_methods: DashMap<String, *const c_void>,
pub native_libraries: DashMap<String, Library>,
pub gc: Arc<RwLock<ObjectManager>>, pub gc: Arc<RwLock<ObjectManager>>,
} }
impl Vm { impl Vm {
// start vm, loading main from classfile // start vm, loading main from classfile
pub fn new(what: &str) -> Arc<Self> { pub fn new() -> Arc<Self> {
let vm = Arc::new(Self { let vm = Arc::new(Self {
threads: DashMap::new(),
main_thread_id: ThreadId(0),
loader: Arc::new(Mutex::from(ClassLoader::default())), loader: Arc::new(Mutex::from(ClassLoader::default())),
thread: Mutex::new(Vec::new()), native_methods: DashMap::new(),
native_methods: Default::default(), native_libraries: DashMap::new(),
gc: Default::default(), gc: Default::default(),
}); });
let thread = Arc::new(VmThread::new(vm.clone(), None));
vm.thread.lock().unwrap().push(thread.clone()); // Create main thread
thread.invoke_main(what, thread.clone()); let thread = VmThread::new(vm.clone(), None);
vm.clone() let thread_id = thread.id;
// Store in VM
vm.threads.insert(thread_id, thread.clone());
// Set as current thread
VmThread::set_current(thread_id);
vm
}
pub fn load_native_library(&self, name: &str, lib: Library) {
self.native_libraries.insert(name.to_string(), lib);
}
pub fn find_native_method(&self, name: &str) -> Option<*const c_void> {
if name.contains("Java_jdk_internal_util_SystemProps$Raw_platformProperties") {
println!("Pick me baws");
let val = self.native_methods.iter().collect::<Vec<_>>();
println!("{}", val.len());
}
if let Some(registered) = self.native_methods.get(name) {
return Some(registered.clone());
}
{
let lib = self.native_libraries.get("roast_vm.dll").unwrap();
let res = unsafe { lib.get::<unsafe extern "system" fn()>(name.as_bytes()) };
if res.is_ok() {
let symbol = res.unwrap();
let ptr = *symbol as *const c_void;
self.native_methods.insert(name.to_string(), ptr);
return Some(ptr);
}
}
for entry in self.native_libraries.iter() {
let (_lib_name, lib) = entry.pair();
let res = unsafe { lib.get::<unsafe extern "system" fn()>(name.as_bytes()) };
if res.is_ok() {
let symbol = res.unwrap();
let ptr = *symbol as *const c_void;
self.native_methods.insert(name.to_string(), ptr);
return Some(ptr);
}
}
None
}
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?
// "java/lang/reflect/Method",
// "java/lang/ref/Finalizer",
// "jdk/internal/misc/UnsafeConstants"
];
let _ = classes.iter().map(|e| thread.get_or_resolve_class(e));
let prims = vec!["byte", "char", "double", "float", "int", "long", "short", "boolean"];
let thread = self.threads.get(&self.main_thread_id).unwrap();
for prim in prims {
let klass =
self.loader.lock().unwrap().primitive_class(prim);
let class_class = thread.get_class("java/lang/Class")?;
let string = thread.intern_string(&prim);
let flags = ClassFlags::from(1041u16);
let class_obj = self.gc.write().unwrap().new_class(
class_class,
Some(ReferenceKind::ObjectReference(string)),
None,
flags,
false
);
klass.mirror.set(class_obj.lock().unwrap().id).unwrap();
}
let phase1ref = MethodRef {
class: "java/lang/System".to_string(),
name: "initPhase1".to_string(),
desc: MethodDescriptor::void(),
};
thread.invoke(phase1ref, Vec::new())?;
Ok(())
}
pub fn run(&self, what: &str) {
self.boot_strap().expect("Failed to bootstrap vm!");
// Get main thread from DashMap
let thread = self.threads.get(&self.main_thread_id).unwrap().clone();
thread.invoke_main(what)
} }
} }

View File

@ -1,110 +0,0 @@
#![allow(non_snake_case)]
use jni::objects::{JClass, JString};
use jni::strings::JNIString;
use jni::sys::{jclass, jlong};
use jni::{JNIEnv, NativeMethod};
use std::ffi::c_void;
use std::io::Write;
use std::time::{SystemTime, UNIX_EPOCH};
#[unsafe(no_mangle)]
pub extern "system" fn current_time_millis<'local>(
mut env: JNIEnv<'local>,
jclass: JClass<'local>,
) -> jlong {
println!("Sneaky hobitses has hijacked the native methods he has");
// SystemTime::now()
// .duration_since(UNIX_EPOCH)
// .unwrap()
// .as_millis() as jlong
1337i64
}
#[unsafe(no_mangle)]
pub extern "system" fn Java_org_example_MockIO_print<'local>(
mut env: JNIEnv<'local>,
jclass: JClass<'local>,
input: JString<'local>,
) {
let input: String = env
.get_string(&input)
.expect("Couldn't get java string!")
.into();
std::io::stdout()
.write_all(input.as_bytes())
.expect("Failed to emit to stdout");
// println!("Yeetus bageetus! Im printing from rust!")
}
#[unsafe(no_mangle)]
pub extern "system" fn Java_org_example_MockIO_registerNatives<'local>(
mut env: JNIEnv<'local>,
jclass: JClass<'local>,
) {
let system_methods = vec![NativeMethod {
name: JNIString::from("currentTimeMillis"),
sig: JNIString::from("()J"),
fn_ptr: current_time_millis as *mut c_void,
}];
let system_class = env
.find_class("java/lang/System")
.expect("Failed to find system class");
env.register_native_methods(system_class, &system_methods)
.expect("failed to register method");
let library_methods = vec![NativeMethod {
name: JNIString::from("findEntry0"),
sig: JNIString::from("(JLjava/lang/String;)J"),
fn_ptr: findEntry0 as *mut c_void,
}];
let library_class = env
.find_class("jdk/internal/loader/NativeLibrary")
.expect("Failed to find system class");
// env.register_native_methods(library_class, &library_methods)
// .expect("failed to register method");
}
#[unsafe(no_mangle)]
pub extern "system" fn findEntry0<'local>(
mut env: JNIEnv<'local>,
jclass: JClass<'local>,
handle: jlong,
name: JString<'local>,
) -> jlong {
let name: String = env
.get_string(&name)
.expect("Couldn't get java string!")
.into();
println!("Name: {}, Handle: {}", name, handle);
0i64
}
#[unsafe(no_mangle)]
pub extern "system" fn Java_org_example_Main_getTime<'local>(
mut env: JNIEnv<'local>,
jclass: JClass<'local>,
) -> jlong {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_millis() as jlong
}
#[unsafe(no_mangle)]
pub extern "system" fn Java_org_example_MockIO_println<'local>(
mut env: JNIEnv<'local>,
jclass: JClass<'local>,
input: jlong,
) {
// let input: String = env
// .get_string(&input)
// .expect("Couldn't get java string!")
// .into();
println!("{input}")
}

View File

@ -1,12 +1,13 @@
[package] [package]
name = "jvm-rs-sys" name = "roast-vm-sys"
version = "0.1.0" version = "0.1.0"
edition = "2024" edition = "2024"
publish = ["nexus"] publish = ["nexus"]
[dependencies] [dependencies]
jni = { workspace = true } jni = { workspace = true }
roast-vm-core = { workspace = true }
[lib] [lib]
name = "jvm_rs" name = "roast_vm"
crate-type = ["cdylib"] crate-type = ["cdylib"]

View File

@ -0,0 +1,63 @@
use roast_vm_core::class_file::FieldRef;
use std::ptr;
use jni::sys::jstring;
use jni::sys::JNIEnv;
use jni::sys::jclass;
use roast_vm_core::{BaseType, FieldType};
use roast_vm_core::objects::array::ArrayReference;
use roast_vm_core::objects::ReferenceKind;
use roast_vm_core::value::Value;
use crate::get_thread;
#[unsafe(no_mangle)]
pub unsafe extern "system" fn Java_java_lang_Class_getPrimitiveClass<'local>(
env: *mut JNIEnv,
_class: jclass,
name: jstring,
) -> jclass {
let thread = &*get_thread(env);
let rust_string = {
let gc = thread.gc.read().unwrap();
let obj_id = name as u32;
let obj_ref = match gc.get(obj_id) {
ReferenceKind::ObjectReference(obj) => obj,
_ => return 0 as jclass,
};
let obj = obj_ref.lock().unwrap();
let field_ref = FieldRef{
class: "java/lang/String".to_string(),
name: "value".to_string(),
desc: FieldType::ArrayType(Box::new(FieldType::Base(BaseType::Byte))),
};
let value_field = obj.get_field(&field_ref);
let Value::Reference(Some(ReferenceKind::ArrayReference(ArrayReference::Byte(byte_array)))) =
value_field
else {
return 0 as jclass;
};
let array = byte_array.lock().unwrap();
let bytes: Vec<u8> = (&array).iter().map(|&b| b as u8).collect();
let mut utf16_chars = Vec::new();
for chunk in bytes.chunks_exact(2) {
let code_unit = u16::from_le_bytes([chunk[0], chunk[1]]);
utf16_chars.push(code_unit);
}
String::from_utf16_lossy(&utf16_chars)
// All locks dropped here at end of block
};
let klass = thread.get_class(&rust_string).unwrap();
let class = thread.gc.read().unwrap().get(*klass.mirror.wait());
class.id() as jclass
}

View File

@ -0,0 +1,209 @@
#![allow(non_snake_case)]
mod class;
mod object;
mod misc_unsafe;
use jni::objects::{JClass, JObject, JString};
use jni::strings::JNIString;
use jni::sys::{jclass, jlong, jobject, jobjectArray};
use jni::{JNIEnv, NativeMethod};
use std::ffi::c_void;
use std::io::Write;
use std::time::{SystemTime, UNIX_EPOCH};
use roast_vm_core::objects::object::ObjectReference;
use roast_vm_core::objects::ReferenceKind;
use roast_vm_core::VmThread;
#[unsafe(no_mangle)]
pub extern "system" fn current_time_millis<'local>(
mut env: JNIEnv<'local>,
jclass: JClass<'local>,
) -> jlong {
println!("Sneaky hobitses has hijacked the native methods he has");
// SystemTime::now()
// .duration_since(UNIX_EPOCH)
// .unwrap()
// .as_millis() as jlong
1337i64
}
#[unsafe(no_mangle)]
pub extern "system" fn Java_org_example_MockIO_print<'local>(
mut env: JNIEnv<'local>,
jclass: JClass<'local>,
input: JString<'local>,
) {
unsafe {
let input: String = env
.get_string_unchecked(&input)
.expect("Couldn't get java string!")
.into();
std::io::stdout()
.write_all(input.as_bytes())
.expect("Failed to emit to stdout");
}
// println!("Yeetus bageetus! Im printing from rust!")
}
#[unsafe(no_mangle)]
pub extern "system" fn Java_org_example_MockIO_registerNatives<'local>(
mut env: JNIEnv<'local>,
jclass: JClass<'local>,
) {
let system_methods = vec![NativeMethod {
name: JNIString::from("currentTimeMillis"),
sig: JNIString::from("()J"),
fn_ptr: current_time_millis as *mut c_void,
}];
let system_class = env
.find_class("java/lang/System")
.expect("Failed to find system class");
env.register_native_methods(system_class, &system_methods)
.expect("failed to register method");
let library_methods = vec![NativeMethod {
name: JNIString::from("findEntry0"),
sig: JNIString::from("(JLjava/lang/String;)J"),
fn_ptr: findEntry0 as *mut c_void,
}];
let library_class = env
.find_class("jdk/internal/loader/NativeLibrary")
.expect("Failed to find system class");
// env.register_native_methods(library_class, &library_methods)
// .expect("failed to register method");
}
#[unsafe(no_mangle)]
pub extern "system" fn findEntry0<'local>(
mut env: JNIEnv<'local>,
jclass: JClass<'local>,
handle: jlong,
name: JString<'local>,
) -> jlong {
let name: String = env
.get_string(&name)
.expect("Couldn't get java string!")
.into();
println!("Name: {}, Handle: {}", name, handle);
0i64
}
#[unsafe(no_mangle)]
pub extern "system" fn Java_org_example_Main_getTime<'local>(
mut env: JNIEnv<'local>,
jclass: JClass<'local>,
) -> jlong {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_millis() as jlong
}
#[unsafe(no_mangle)]
pub extern "system" fn Java_org_example_MockIO_println<'local>(
mut env: JNIEnv<'local>,
jclass: JClass<'local>,
input: jlong,
) {
// let input: String = env
// .get_string(&input)
// .expect("Couldn't get java string!")
// .into();
println!("{input}")
}
// #[unsafe(no_mangle)]
// pub extern "system" fn Java_java_lang_Class_registerNatives<'local>(
// mut env: JNIEnv<'local>,
// jclass: JClass<'local>,
// ) {
// println!("Java_java_lang_Class_registerNatives is NOP")
// }
//
// #[unsafe(no_mangle)]
// pub extern "system" fn Java_java_lang_Object_getClass<'local>(
// mut env: JNIEnv<'local>,
// this: JObject<'local>,
// ) -> JClass<'local> {
// unsafe {
// if this.is_null() {
// env.throw_new(
// "java/lang/NullPointerException",
// "Cannot invoke getClass() on null",
// )
// .unwrap();
// return JClass::from_raw(std::ptr::null_mut());
// }
// }
// match env.get_object_class(this) {
// Ok(c) => c,
// Err(e) => {
// eprintln!("get_object_class failed: {:?}", e);
// panic!("debug");
// }
// }
// }
unsafe fn get_thread(env: *mut jni::sys::JNIEnv) -> *const VmThread {
(**env).reserved0 as *const VmThread
}
fn resolve_object(thread: &VmThread, obj: jobject) -> Option<ObjectReference> {
let gc = thread.gc.read().unwrap();
let ReferenceKind::ObjectReference(obj_ref) = gc.get(obj as u32) else {
return None;
};
Some(obj_ref.clone())
}
#[unsafe(no_mangle)]
pub extern "system" fn Java_jdk_internal_util_SystemProps_00024Raw_vmProperties<'local>(
mut env: JNIEnv<'local>,
_class: JClass<'local>,
) -> jobjectArray {
let props = [
"java.vm.specification.name",
"Java Virtual Machine Specification",
"java.vm.specification.vendor",
"Oracle Corporation",
"java.vm.specification.version",
"25",
"java.vm.name",
"RoastVM",
"java.vm.vendor",
"infernap12",
"java.vm.version",
"0.1.0",
"java.vm.info",
"interpreted mode",
"jdk.debug",
"release",
"java.home",
"C:\\Program Files\\Java\\jdk-25", // adjust
"java.library.path",
"",
"sun.boot.library.path",
"",
"java.class.path",
".",
];
let string_class = env.find_class("java/lang/String").unwrap();
// +1 for null terminator
let arr = env
.new_object_array((props.len() + 1) as i32, string_class, JObject::null())
.unwrap();
for (i, s) in props.iter().enumerate() {
let jstr = env.new_string(s).unwrap();
env.set_object_array_element(&arr, i as i32, jstr).unwrap();
}
// Last element stays null (terminator)
arr.into_raw()
}

View File

@ -0,0 +1,11 @@
use jni::sys::{jclass, jint, jobject, jstring, JNIEnv};
use crate::{get_thread, resolve_object};
#[unsafe(no_mangle)]
pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_registerNatives(
env: *mut JNIEnv,
obj: jclass,
) {
//no op
()
}

View File

@ -0,0 +1,11 @@
use jni::sys::{jclass, jint, jobject, jstring, JNIEnv};
use crate::{get_thread, resolve_object};
#[unsafe(no_mangle)]
pub unsafe extern "system" fn Java_java_lang_Object_hashCode(
env: *mut JNIEnv,
obj: jobject,
) -> jint {
let thread = &*get_thread(env);
obj as u32 as i32
}