This commit is contained in:
james 2025-11-21 01:12:47 +10:30
parent 7fd494d999
commit 78b17345b8
No known key found for this signature in database
GPG Key ID: E1FFBA228F4CAD87
21 changed files with 788 additions and 160 deletions

View File

@ -1,18 +1,17 @@
[package]
name = "jvm-rs"
version = "0.1.0"
edition = "2024"
[workspace]
members = [
"crates/*"
]
resolver = "3"
[dependencies]
[workspace.dependencies]
deku = { version = "0.20.0", features = ["logging"] }
deku_derive = "0.20.0"
log = "0.4.28"
env_logger = "0.11.8"
itertools = "0.14.0"
sevenz-rust2 = { version = "0.19.3", features = ["brotli", "zstd"] }
[build-dependencies]
bindgen = "0.72.1"
[lints.rust]
missing_docs = "warn"
dashmap = "7.0.0-rc2"
libloading = "0.8.9"
libffi = "5.0.0"
jni = "0.21.1"

23
crates/core/Cargo.toml Normal file
View File

@ -0,0 +1,23 @@
[package]
name = "jvm-rs-core"
version = "0.1.0"
edition = "2024"
publish = ["nexus"]
[dependencies]
deku = { workspace = true }
deku_derive = { workspace = true }
log = { workspace = true }
env_logger = { workspace = true }
itertools = { workspace = true }
sevenz-rust2 = { workspace = true }
dashmap = { workspace = true }
libloading = { workspace = true }
libffi = { workspace = true }
jni = { workspace = true }
[build-dependencies]
bindgen = "0.72.1"
[lints.rust]
#missing_docs = "warn"

View File

@ -4,7 +4,6 @@ use std::fs::File;
const DEFAULT_LOCATION: &str = "./lib/modules";
pub struct Bimage {
image: ArchiveReader<File>,
modules: Vec<String>,
@ -12,13 +11,18 @@ pub struct Bimage {
impl Default for Bimage {
fn default() -> Self {
let reader = ArchiveReader::open(DEFAULT_LOCATION, Default::default()).expect("No image location given, and unable to open/locate default image");
let reader = ArchiveReader::open(DEFAULT_LOCATION, Default::default())
.expect("No image location given, and unable to open/locate default image");
let mut modules = reader.archive().files.iter().filter(|e|{
e.is_directory &&
e.name.split("/").count() == 1
}).map(|e| { e.name.clone() }).collect::<HashSet<_>>().into_iter().collect::<Vec<_>>();
let mut modules = reader
.archive()
.files
.iter()
.filter(|e| e.is_directory && e.name.split("/").count() == 1)
.map(|e| e.name.clone())
.collect::<HashSet<_>>()
.into_iter()
.collect::<Vec<_>>();
modules.sort();
Self {
image: reader,
@ -27,34 +31,39 @@ impl Default for Bimage {
}
}
impl Bimage {
pub fn new(path: impl AsRef<std::path::Path>) -> Self {
let reader = ArchiveReader::open(path, Default::default()).expect("Unable to find specified bimage.");
let reader = ArchiveReader::open(path, Default::default())
.expect("Unable to find specified bimage.");
Self {
image: reader,
..Default::default()
}
}
fn resolve_path(module: &str, class: &str) -> String {
let module = if module.is_empty() { "java.base" } else { module };
let module = if module.is_empty() {
"java.base"
} else {
module
};
let class = Self::d2s(class);
format!("{module}/classes/{class}.class")
}
fn d2s( dots: &str) -> String {
fn d2s(dots: &str) -> String {
dots.replace(".", "/")
}
fn f2s ( slashes: &str) -> String {
fn f2s(slashes: &str) -> String {
slashes.replace("/", ".")
}
pub fn get_class(&mut self, module: &str, class: &str) -> Option<Vec<u8>> {
let path = Self::resolve_path(module, class);
self.image.read_file(&path).map_err(|e| {
println!("{}", path);
}).ok()
self.image
.read_file(&path)
.map_err(|e| {
log::trace!("Class not found {}", path);
})
.ok()
}
}

View File

@ -4,6 +4,7 @@ use crate::class_file::{
MethodInfo, MethodRef,
};
use crate::{FieldType, MethodDescriptor, VmError};
use log::trace;
use std::sync::{Arc, Mutex};
use std::thread::ThreadId;
@ -20,6 +21,7 @@ pub enum InitState {
Error(String),
}
#[derive(Debug)]
pub struct RuntimeClass {
pub constant_pool: Arc<Vec<ConstantPoolEntry>>,
pub access_flags: ClassFlags,
@ -33,20 +35,23 @@ pub struct RuntimeClass {
}
impl RuntimeClass {
pub fn find_method(&self, name: &str, desc: MethodDescriptor) -> Result<&MethodData, VmError> {
println!("Finding method");
pub fn find_method(&self, name: &str, desc: &MethodDescriptor) -> Result<&MethodData, VmError> {
trace!(
"Finding in {}, method Name: {name} desc:{desc},",
self.this_class
);
if let Some(method) = self.methods.iter().find(|e| {
println!("Method Name Needed: {name}, Checked:{}", e.name);
println!("Method type Needed: {desc:?}, Checked:{:?}", e.desc);
let name_match = e.name.eq(name);
let param_match = desc.parameters == e.desc.parameters;
name_match && param_match
}) {
trace!("Found method: {name}");
return Ok(method);
};
// recurse super class
if let Some(super_class) = &self.super_class {
trace!("Recursing for {name}");
return super_class.find_method(name, desc);
}
// No method found, and we must be Object, as we don't have a superclass

View File

@ -10,7 +10,7 @@ use std::fmt;
use std::fmt::{Display, Formatter};
use std::ops::Deref;
use std::str::Chars;
use std::sync::Arc;
use std::sync::{Arc, Mutex};
#[derive(Debug, PartialEq, DekuRead)]
#[deku(magic = b"\xCA\xFE\xBA\xBE", endian = "big")]
@ -639,11 +639,12 @@ pub struct FieldRef {
pub desc: FieldType,
}
#[derive(Debug)]
pub struct FieldData {
pub name: String,
pub flags: FieldFlags,
pub desc: FieldType,
pub value: Option<Constant>,
pub value: Arc<Mutex<Option<Value>>>,
}
#[derive(Clone, Debug, PartialEq)]
@ -655,6 +656,20 @@ pub enum Constant {
String(String),
}
impl From<Constant> for Value {
fn from(value: Constant) -> Self {
match value {
Constant::Int(x) => Value::Int(x),
Constant::Long(x) => Value::Long(x),
Constant::Float(x) => Value::Float(x),
Constant::Double(x) => Value::Double(x),
Constant::String(x) => {
todo!("Constant string")
}
}
}
}
#[allow(non_snake_case)]
#[derive(Debug, PartialEq, DekuRead, DekuWrite)]
pub struct ClassFlags {

View File

@ -1,12 +1,20 @@
use crate::attributes::{
Attribute, AttributeInfo, CodeAttribute, LineNumberTableAttribute, LocalVariableTableAttribute,
};
use crate::class_file::{
ConstantClassInfo, ConstantDynamicInfo, ConstantFieldrefInfo, ConstantInterfaceMethodrefInfo,
ConstantInvokeDynamicInfo, ConstantMethodHandleInfo, ConstantMethodTypeInfo,
ConstantMethodrefInfo, ConstantModuleInfo, ConstantNameAndTypeInfo, ConstantPackageInfo,
ConstantPoolEntry, ConstantStringInfo, ConstantUtf8Info, DescParseError, FieldInfo, FieldRef,
MethodInfo, MethodRef,
};
use crate::{pool_get_impl, FieldType, MethodDescriptor, VmError};
use deku::DekuContainerRead;
use log::trace;
use std::fmt::{Display, Formatter};
use std::ops::Deref;
use std::str::FromStr;
use std::sync::Arc;
use deku::DekuContainerRead;
use log::trace;
use crate::class_file::{ConstantClassInfo, ConstantFieldrefInfo, ConstantNameAndTypeInfo, ConstantPoolEntry, FieldRef, FieldInfo, MethodRef, MethodInfo, ConstantUtf8Info, ConstantStringInfo, ConstantMethodrefInfo, ConstantInterfaceMethodrefInfo, ConstantMethodHandleInfo, ConstantMethodTypeInfo, ConstantDynamicInfo, ConstantInvokeDynamicInfo, ConstantModuleInfo, ConstantPackageInfo, DescParseError};
use crate::{pool_get_impl, FieldType, MethodDescriptor, VmError};
use crate::attributes::{Attribute, AttributeInfo, CodeAttribute, LineNumberTableAttribute, LocalVariableTableAttribute};
pub type ConstantPoolSlice = [ConstantPoolEntry];
pub type ConstantPoolOwned = Vec<ConstantPoolEntry>;
@ -31,7 +39,7 @@ pub trait ConstantPoolExt: ConstantPoolGet {
fn get_string(&self, index: u16) -> Result<String, ConstantPoolError> {
let cp_entry = self.get_utf8_info(index)?;
String::from_utf8(cp_entry.bytes.clone()).map_err(|e| { e.to_string().into() })
String::from_utf8(cp_entry.bytes.clone()).map_err(|e| e.to_string().into())
}
//
// fn get_field(&self, index: u16) -> Result<&ConstantFieldrefInfo, ()> {
@ -65,11 +73,7 @@ pub trait ConstantPoolExt: ConstantPoolGet {
let name = self.get_string(name_and_type.name_index)?;
let desc = self.get_string(name_and_type.descriptor_index)?;
let desc = FieldType::parse(&desc)?;
Ok(FieldRef {
class,
name,
desc,
})
Ok(FieldRef { class, name, desc })
}
fn resolve_method_ref(&self, index: u16) -> Result<MethodRef, ConstantPoolError> {
@ -80,11 +84,7 @@ pub trait ConstantPoolExt: ConstantPoolGet {
let name = self.get_string(name_and_type.name_index)?;
let desc = self.get_string(name_and_type.descriptor_index)?;
let desc = MethodDescriptor::parse(&desc)?;
Ok(MethodRef {
class,
name,
desc,
})
Ok(MethodRef { class, name, desc })
}
/*// (name, desc)
@ -111,12 +111,9 @@ pub trait ConstantPoolExt: ConstantPoolGet {
})
}
fn parse_attribute(&self, a: AttributeInfo) -> Result<Attribute, VmError> {
let name = self.get_string(a.attribute_name_index)?;
trace!("Parsing attribute with name: {}", name);
// trace!("Parsing attribute with name: {}", name);
match name.as_ref() {
"Code" => {
@ -131,15 +128,9 @@ pub trait ConstantPoolExt: ConstantPoolGet {
let (_, lnt) = LineNumberTableAttribute::from_bytes((&a.info.as_slice(), 0))?;
Ok(Attribute::LineNumberTable(lnt))
}
"StackMapTable" => {
Ok(Attribute::StackMapTable(a.info.clone()))
}
"Exceptions" => {
Ok(Attribute::Exceptions(a.info.clone()))
}
"InnerClasses" => {
Ok(Attribute::InnerClasses(a.info.clone()))
}
"StackMapTable" => Ok(Attribute::StackMapTable(a.info.clone())),
"Exceptions" => Ok(Attribute::Exceptions(a.info.clone())),
"InnerClasses" => Ok(Attribute::InnerClasses(a.info.clone())),
"Signature" => {
let signature_index = u16::from_be_bytes([a.info[0], a.info[1]]);
Ok(Attribute::Signature(signature_index))

View File

@ -2,5 +2,5 @@ pub mod class_file;
pub mod constant_pool;
// Re-export items if you want them accessible directly from class_file::
pub use class_file::*; // optional
// pub use constant_pool::*; // optional
pub use class_file::*; // optional
// pub use constant_pool::*; // optional

View File

@ -1,23 +1,26 @@
use crate::attributes::Attribute;
use crate::bimage::Bimage;
use crate::class::RuntimeClass;
use crate::class_file::constant_pool::{ConstantPoolExt, ConstantPoolGet};
use crate::class_file::{
ClassFile, ClassFlags, ConstantClassInfo, ConstantPoolEntry, FieldData, FieldFlags, MethodData,
MethodFlags,
};
use crate::native_libraries::NativeLibraries;
use crate::{FieldType, MethodDescriptor};
use dashmap::DashMap;
use deku::DekuContainerRead;
use libloading::os::windows::Library;
use log::warn;
use std::collections::hash_map::{Entry, Iter};
use std::collections::HashMap;
use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};
use std::sync::{Arc, Mutex};
use deku::DekuContainerRead;
use log::warn;
use crate::attributes::Attribute;
use crate::bimage::Bimage;
use crate::class::RuntimeClass;
use crate::class_file::{ClassFile, ClassFlags, ConstantClassInfo, ConstantPoolEntry, FieldData, FieldFlags, MethodData, MethodFlags};
use crate::class_file::constant_pool::{ConstantPoolExt, ConstantPoolGet};
use crate::{FieldType, MethodDescriptor};
pub type LoaderRef = Arc<Mutex<ClassLoader>>;
#[deprecated(
note = "This method is deprecated and will be removed in future versions"
)]
#[deprecated(note = "This method is deprecated and will be removed in future versions")]
pub fn resolve_path(what: &str) -> Result<(PathBuf, String), String> {
let (module, fqn) = what.split_once("/").unwrap_or(("", what));
let module = dot_to_path(module);
@ -36,15 +39,22 @@ pub fn resolve_path(what: &str) -> Result<(PathBuf, String), String> {
let classes_path = module_path.join("jmod/classes");
if !classes_path.exists() {
return Err(format!("Could not find jmod/classes directory in module: {}", module));
return Err(format!(
"Could not find jmod/classes directory in module: {}",
module
));
}
classes_path
} else { base.to_path_buf() };
} else {
base.to_path_buf()
};
let class_path = path.join(format!("{}.class", fqn));
if !class_path.exists() {
return Err(format!("Could not find class: {} in module: {}", fqn, module));
return Err(format!(
"Could not find class: {} in module: {}",
fqn, module
));
}
Ok((class_path, path_to_dot(&fqn)))
@ -77,9 +87,10 @@ pub fn resolve_path(what: &str) -> Result<(PathBuf, String), String> {
/// ```
#[derive(Default)]
pub struct ClassLoader {
classes: HashMap<String, Arc<RuntimeClass>>,
classes: DashMap<String, Arc<RuntimeClass>>,
bimage: Bimage,
pub needs_init: Vec<Arc<RuntimeClass>>
pub needs_init: Vec<Arc<RuntimeClass>>,
native_libraries: NativeLibraries,
}
impl ClassLoader {
@ -133,14 +144,14 @@ impl ClassLoader {
}
/* pub fn classes(&self) -> HashMap<String, ClassFile> {
self.classes.clone()
}*/
self.classes.clone()
}*/
fn load_class(&mut self, what: &str) -> Result<Arc<RuntimeClass>, String> {
let (module, class_fqn) = ("", what);
let bytes = self.bimage.get_class(module, class_fqn).unwrap_or_else(|| {
let path = format!("./data/{what}.class");
println!("{}", path);
log::info!("Loading class from path: {}", path);
let mut class_file = File::open(path).unwrap();
let mut bytes = Vec::new();
class_file.read_to_end(&mut bytes).unwrap();
@ -151,28 +162,33 @@ impl ClassLoader {
let runtime = self.runtime_class(cf);
let arced = Arc::new(runtime);
let option = self.classes.insert(class_fqn.to_string(), arced.clone());
if option.is_some() { warn!("Replaced loaded class: {}", class_fqn) }
if option.is_some() {
warn!("Replaced loaded class: {}", class_fqn)
}
Ok(arced)
}
fn runtime_class(&mut self, class_file: ClassFile) -> RuntimeClass {
let constant_pool = class_file.constant_pool.clone();
let access_flags = ClassFlags::from(class_file.access_flags);
let this_class = {
let cl = class_file.constant_pool.get_class_info(class_file.this_class).unwrap();
let cl = class_file
.constant_pool
.get_class_info(class_file.this_class)
.unwrap();
let name = class_file.constant_pool.get_string(cl.name_index).unwrap();
name
};
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!(class_file.super_class, 0u16);
None
} else {
debug_assert_ne!(class_file.super_class, 0u16);
let super_info = constant_pool.get_class_info(class_file.super_class).unwrap();
let super_info = constant_pool
.get_class_info(class_file.super_class)
.unwrap();
let name = constant_pool.get_string(**super_info).unwrap();
Some(self.get_or_load(&*name).unwrap())
}
@ -187,60 +203,73 @@ impl ClassLoader {
}
let interfaces = class_file
.interfaces.iter().copied()
.interfaces
.iter()
.copied()
.map(|e| {
let interface_info = constant_pool.get_class_info(e).unwrap();
let name = constant_pool.get_string(interface_info.name_index).unwrap();
self.get_or_load(&name).unwrap()
}).collect::<Vec<_>>();
})
.collect::<Vec<_>>();
let fields = class_file
.fields.iter()
.fields
.iter()
.map(|e| {
let name = constant_pool.get_string(e.name_index).unwrap();
let flags = FieldFlags::from(e.access_flags);
let desc = constant_pool.get_string(e.descriptor_index).map(|e|{
FieldType::parse(&e)
}).unwrap().unwrap();
let value = e.attributes.first()
.and_then(|x| {
if let Attribute::ConstantValue(val) = constant_pool.parse_attribute(x.clone()).unwrap() {
Some(val)
} else {
None
}
});
let desc = constant_pool
.get_string(e.descriptor_index)
.map(|e| FieldType::parse(&e))
.unwrap()
.unwrap();
let value = e.attributes.first().and_then(|x| {
if let Attribute::ConstantValue(val) =
constant_pool.parse_attribute(x.clone()).unwrap()
{
Some(val.into())
} else {
None
}
});
let value = Arc::new(Mutex::new(value));
FieldData {
name,
flags,
desc,
value,
}
}).collect::<Vec<_>>();
})
.collect::<Vec<_>>();
let methods = class_file.methods.iter().map(|e| {
let name = constant_pool.get_string(e.name_index).unwrap();
let flags = MethodFlags::from(e.access_flags);
let desc = constant_pool.get_string(e.descriptor_index).map(|e|{
MethodDescriptor::parse(&e)
}).unwrap().unwrap();
let code = e.attributes.first()
.and_then(|x| {
if let Attribute::Code(val) = constant_pool.parse_attribute(x.clone()).unwrap() {
let methods = class_file
.methods
.iter()
.map(|e| {
let name = constant_pool.get_string(e.name_index).unwrap();
let flags = MethodFlags::from(e.access_flags);
let desc = constant_pool
.get_string(e.descriptor_index)
.map(|e| MethodDescriptor::parse(&e))
.unwrap()
.unwrap();
let code = e.attributes.first().and_then(|x| {
if let Attribute::Code(val) = constant_pool.parse_attribute(x.clone()).unwrap()
{
Some(val)
} else {
None
}
});
MethodData {
name,
flags,
desc,
code,
}
}).collect::<Vec<_>>();
MethodData {
name,
flags,
desc,
code,
}
})
.collect::<Vec<_>>();
RuntimeClass {
constant_pool,
@ -253,6 +282,13 @@ impl ClassLoader {
init_state: Mutex::new(crate::class::InitState::NotInitialized),
}
}
unsafe fn find_native<T>(&self, name: String) -> libloading::os::windows::Symbol<T> {
// for (key, value) in self.native_libraries.iter() {
// // value.get()
// }
todo!("class_loader find native")
}
}
fn dot_to_path(s: &str) -> String {

257
crates/core/src/jni.rs Normal file
View File

@ -0,0 +1,257 @@
use jni::sys::JNIEnv;
use jni::sys::{jint, JNINativeInterface_};
use std::ptr;
const JNI_VERSION_1_1: jint = 0x00010001;
const JNI_VERSION_1_2: jint = 0x00010002;
const JNI_VERSION_1_4: jint = 0x00010004;
const JNI_VERSION_1_6: jint = 0x00010006;
const JNI_VERSION_1_8: jint = 0x00010008;
const JNI_VERSION_9: jint = 0x00090000;
const JNI_VERSION_10: jint = 0x000a0000;
const JNI_VERSION_19: jint = 0x00130000;
const JNI_VERSION_20: jint = 0x00140000;
const JNI_VERSION_21: jint = 0x00150000;
const JNI_VERSION_24: jint = 0x00180000;
pub fn create_jni_function_table() -> JNIEnv {
Box::into_raw(Box::new(JNINativeInterface_ {
reserved0: ptr::null_mut(),
reserved1: ptr::null_mut(),
reserved2: ptr::null_mut(),
reserved3: ptr::null_mut(),
GetVersion: Some(jni_get_version),
DefineClass: None,
FindClass: None,
FromReflectedMethod: None,
FromReflectedField: None,
ToReflectedMethod: None,
GetSuperclass: None,
IsAssignableFrom: None,
ToReflectedField: None,
Throw: None,
ThrowNew: None,
ExceptionOccurred: None,
ExceptionDescribe: None,
ExceptionClear: None,
FatalError: None,
PushLocalFrame: None,
PopLocalFrame: None,
NewGlobalRef: None,
DeleteGlobalRef: None,
DeleteLocalRef: None,
IsSameObject: None,
NewLocalRef: None,
EnsureLocalCapacity: None,
AllocObject: None,
NewObject: None,
NewObjectV: None,
NewObjectA: None,
GetObjectClass: None,
IsInstanceOf: None,
GetMethodID: None,
CallObjectMethod: None,
CallObjectMethodV: None,
CallObjectMethodA: None,
CallBooleanMethod: None,
CallBooleanMethodV: None,
CallBooleanMethodA: None,
CallByteMethod: None,
CallByteMethodV: None,
CallByteMethodA: None,
CallCharMethod: None,
CallCharMethodV: None,
CallCharMethodA: None,
CallShortMethod: None,
CallShortMethodV: None,
CallShortMethodA: None,
CallIntMethod: None,
CallIntMethodV: None,
CallIntMethodA: None,
CallLongMethod: None,
CallLongMethodV: None,
CallLongMethodA: None,
CallFloatMethod: None,
CallFloatMethodV: None,
CallFloatMethodA: None,
CallDoubleMethod: None,
CallDoubleMethodV: None,
CallDoubleMethodA: None,
CallVoidMethod: None,
CallVoidMethodV: None,
CallVoidMethodA: None,
CallNonvirtualObjectMethod: None,
CallNonvirtualObjectMethodV: None,
CallNonvirtualObjectMethodA: None,
CallNonvirtualBooleanMethod: None,
CallNonvirtualBooleanMethodV: None,
CallNonvirtualBooleanMethodA: None,
CallNonvirtualByteMethod: None,
CallNonvirtualByteMethodV: None,
CallNonvirtualByteMethodA: None,
CallNonvirtualCharMethod: None,
CallNonvirtualCharMethodV: None,
CallNonvirtualCharMethodA: None,
CallNonvirtualShortMethod: None,
CallNonvirtualShortMethodV: None,
CallNonvirtualShortMethodA: None,
CallNonvirtualIntMethod: None,
CallNonvirtualIntMethodV: None,
CallNonvirtualIntMethodA: None,
CallNonvirtualLongMethod: None,
CallNonvirtualLongMethodV: None,
CallNonvirtualLongMethodA: None,
CallNonvirtualFloatMethod: None,
CallNonvirtualFloatMethodV: None,
CallNonvirtualFloatMethodA: None,
CallNonvirtualDoubleMethod: None,
CallNonvirtualDoubleMethodV: None,
CallNonvirtualDoubleMethodA: None,
CallNonvirtualVoidMethod: None,
CallNonvirtualVoidMethodV: None,
CallNonvirtualVoidMethodA: None,
GetFieldID: None,
GetObjectField: None,
GetBooleanField: None,
GetByteField: None,
GetCharField: None,
GetShortField: None,
GetIntField: None,
GetLongField: None,
GetFloatField: None,
GetDoubleField: None,
SetObjectField: None,
SetBooleanField: None,
SetByteField: None,
SetCharField: None,
SetShortField: None,
SetIntField: None,
SetLongField: None,
SetFloatField: None,
SetDoubleField: None,
GetStaticMethodID: None,
CallStaticObjectMethod: None,
CallStaticObjectMethodV: None,
CallStaticObjectMethodA: None,
CallStaticBooleanMethod: None,
CallStaticBooleanMethodV: None,
CallStaticBooleanMethodA: None,
CallStaticByteMethod: None,
CallStaticByteMethodV: None,
CallStaticByteMethodA: None,
CallStaticCharMethod: None,
CallStaticCharMethodV: None,
CallStaticCharMethodA: None,
CallStaticShortMethod: None,
CallStaticShortMethodV: None,
CallStaticShortMethodA: None,
CallStaticIntMethod: None,
CallStaticIntMethodV: None,
CallStaticIntMethodA: None,
CallStaticLongMethod: None,
CallStaticLongMethodV: None,
CallStaticLongMethodA: None,
CallStaticFloatMethod: None,
CallStaticFloatMethodV: None,
CallStaticFloatMethodA: None,
CallStaticDoubleMethod: None,
CallStaticDoubleMethodV: None,
CallStaticDoubleMethodA: None,
CallStaticVoidMethod: None,
CallStaticVoidMethodV: None,
CallStaticVoidMethodA: None,
GetStaticFieldID: None,
GetStaticObjectField: None,
GetStaticBooleanField: None,
GetStaticByteField: None,
GetStaticCharField: None,
GetStaticShortField: None,
GetStaticIntField: None,
GetStaticLongField: None,
GetStaticFloatField: None,
GetStaticDoubleField: None,
SetStaticObjectField: None,
SetStaticBooleanField: None,
SetStaticByteField: None,
SetStaticCharField: None,
SetStaticShortField: None,
SetStaticIntField: None,
SetStaticLongField: None,
SetStaticFloatField: None,
SetStaticDoubleField: None,
NewString: None,
GetStringLength: None,
GetStringChars: None,
ReleaseStringChars: None,
NewStringUTF: None,
GetStringUTFLength: None,
GetStringUTFChars: None,
ReleaseStringUTFChars: None,
GetArrayLength: None,
NewObjectArray: None,
GetObjectArrayElement: None,
SetObjectArrayElement: None,
NewBooleanArray: None,
NewByteArray: None,
NewCharArray: None,
NewShortArray: None,
NewIntArray: None,
NewLongArray: None,
NewFloatArray: None,
NewDoubleArray: None,
GetBooleanArrayElements: None,
GetByteArrayElements: None,
GetCharArrayElements: None,
GetShortArrayElements: None,
GetIntArrayElements: None,
GetLongArrayElements: None,
GetFloatArrayElements: None,
GetDoubleArrayElements: None,
ReleaseBooleanArrayElements: None,
ReleaseByteArrayElements: None,
ReleaseCharArrayElements: None,
ReleaseShortArrayElements: None,
ReleaseIntArrayElements: None,
ReleaseLongArrayElements: None,
ReleaseFloatArrayElements: None,
ReleaseDoubleArrayElements: None,
GetBooleanArrayRegion: None,
GetByteArrayRegion: None,
GetCharArrayRegion: None,
GetShortArrayRegion: None,
GetIntArrayRegion: None,
GetLongArrayRegion: None,
GetFloatArrayRegion: None,
GetDoubleArrayRegion: None,
SetBooleanArrayRegion: None,
SetByteArrayRegion: None,
SetCharArrayRegion: None,
SetShortArrayRegion: None,
SetIntArrayRegion: None,
SetLongArrayRegion: None,
SetFloatArrayRegion: None,
SetDoubleArrayRegion: None,
RegisterNatives: None,
UnregisterNatives: None,
MonitorEnter: None,
MonitorExit: None,
GetJavaVM: None,
GetStringRegion: None,
GetStringUTFRegion: None,
GetPrimitiveArrayCritical: None,
ReleasePrimitiveArrayCritical: None,
GetStringCritical: None,
ReleaseStringCritical: None,
NewWeakGlobalRef: None,
DeleteWeakGlobalRef: None,
ExceptionCheck: None,
NewDirectByteBuffer: None,
GetDirectBufferAddress: None,
GetDirectBufferCapacity: None,
GetObjectRefType: None,
}))
}
unsafe extern "system" fn jni_get_version(env: *mut JNIEnv) -> jint {
JNI_VERSION_24
}

View File

@ -21,9 +21,11 @@ use crate::class_file::constant_pool::{ConstantPoolError, ConstantPoolGet};
use crate::class_file::{Bytecode, ClassFile, ConstantPoolEntry, MethodData};
use crate::object::Object;
use crate::thread::VmThread;
use crate::Value::Reference;
use deku::{DekuContainerRead, DekuError};
use deku_derive::{DekuRead, DekuWrite};
use log::warn;
use env_logger::Builder;
use log::{warn, LevelFilter};
use std::fmt::{Debug, Display, Formatter};
use std::fs::File;
use std::io::Read;
@ -35,7 +37,9 @@ mod bimage;
mod class;
mod class_file;
mod class_loader;
mod jni;
mod macros;
mod native_libraries;
mod object;
mod rng;
mod thread;
@ -46,7 +50,12 @@ const NULL: Value = Value::Reference(None);
// include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
/// pseudo main
pub fn run() {
env_logger::init();
Builder::from_default_env()
.filter_level(LevelFilter::Trace)
.filter_module("deku", LevelFilter::Warn)
.filter_module("jvm_rs_core::class_file::class_file", LevelFilter::Info)
.filter_module("jvm_rs_core::attributes", LevelFilter::Info)
.init();
// let mut cl = ClassLoader::new().unwrap();
// cl.load_class("org.example.App").expect("TODO: panic message");
// let clazz = cl.get_or_load("org.example.App").unwrap();
@ -54,11 +63,11 @@ pub fn run() {
// std::fs::write(format!("./output/{}-{}.txt", i, class_loader::path_to_dot(k)), format!("{}\n{}", k, v)).unwrap();
// }
let mut class_file = File::open("./data/org/example/App.class").unwrap();
/*let mut class_file = File::open("./data/org/example/Main.class").unwrap();
let mut bytes = Vec::new();
class_file.read_to_end(&mut bytes).unwrap();
let (_rest, clazz) = ClassFile::from_bytes((bytes.as_ref(), 0)).unwrap();
let method = clazz.methods.iter().nth(1).unwrap().clone();
let method = clazz.methods.get(1).unwrap().clone();
let code = method
.attributes
.iter()
@ -88,9 +97,9 @@ pub fn run() {
}
})
.unwrap();
println!("{}", clazz);
println!("{}", clazz);*/
// let pool = clazz.constant_pool;
let mut vm = Vm::new("org/example/App");
let mut vm = Vm::new("org/example/Main");
// println!("{:?}", ops);
// println!("{:?}", var_table.local_variable_table);
@ -298,6 +307,45 @@ impl MethodDescriptor {
}
}
impl Display for BaseType {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
BaseType::Byte => write!(f, "B"),
BaseType::Char => write!(f, "C"),
BaseType::Double => write!(f, "D"),
BaseType::Float => write!(f, "F"),
BaseType::Int => write!(f, "I"),
BaseType::Long => write!(f, "J"),
BaseType::Short => write!(f, "S"),
BaseType::Boolean => write!(f, "Z"),
}
}
}
impl Display for FieldType {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
FieldType::Base(base) => write!(f, "{}", base),
FieldType::ClassType(name) => write!(f, "L{};", name),
FieldType::ArrayType(component) => write!(f, "[{}", component),
}
}
}
impl Display for MethodDescriptor {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "(")?;
for param in &self.parameters {
write!(f, "{}", param)?;
}
write!(f, ")")?;
match &self.return_type {
Some(ret) => write!(f, "{}", ret),
None => write!(f, "V"),
}
}
}
/// Represents types that can be used for fields in the JVM.
///
/// Field types can be:
@ -516,7 +564,13 @@ impl Frame {
let result = init_class
.find_field(&field_ref.name, field_ref.desc)
.expect("TO hecken work");
let constant = result.value.clone().unwrap();
let constant = result
.value
.lock()
.unwrap()
.clone()
.expect("Static field was not initialised");
self.stack.push(constant.into());
// let (code, pool) = {
// let mut loader = self.vm.loader.lock().unwrap();
@ -526,7 +580,6 @@ impl Frame {
// (code, pool)
// };
// println!("{:?}", field);
todo!("Finish get static");
Ok(ExecutionResult::Continue)
}
@ -542,7 +595,7 @@ impl Frame {
let class = loader.get_or_load(&meth.class).unwrap();
let pool = class.constant_pool.clone();
let code = class
.find_method(&meth.name, meth.desc)
.find_method(&meth.name, &meth.desc)
.unwrap()
.code
.clone()
@ -576,6 +629,27 @@ impl Frame {
Ok(ExecutionResult::Continue)
}
Ops::aconst_null => {
self.stack.push(NULL);
Ok(ExecutionResult::Continue)
}
Ops::putstatic(index) => {
let field_ref = self.pool.resolve_field(*index)?;
println!("Getting static field {field_ref:?}");
let init_class = self
.thread
.get_or_resolve_class(&field_ref.class, self.thread.clone())
.expect("TO hecken work");
let result = init_class
.find_field(&field_ref.name, field_ref.desc)
.expect("TO hecken work");
let value = self.stack.pop().expect("stack to have value");
*result.value.lock().unwrap() = Some(value);
Ok(ExecutionResult::Continue)
}
Ops::return_void => Ok(ExecutionResult::Return(())),
_ => {
todo!("Unimplemented op: {:?}", op)

3
crates/core/src/main.rs Normal file
View File

@ -0,0 +1,3 @@
fn main() {
jvm_rs_core::run()
}

View File

@ -0,0 +1,15 @@
use crate::class_file::ConstantPoolEntry;
use dashmap::DashMap;
use libloading::os::windows::Library;
use std::collections::HashMap;
pub type NativeLibraries = HashMap<String, Library>;
// impl NativeExt for NativeLibraries {}
//
// trait NativeExt: AsRef<[..]> {
// fn find(&self, name: String) -> () {
// for lib in self.iter() {
// lib
// }
// }
// }

View File

@ -1,10 +1,19 @@
use crate::class::RuntimeClass;
use crate::class_file::{ClassFile, MethodRef};
use crate::class_file::{ClassFile, MethodData, MethodRef};
use crate::class_loader::{ClassLoader, LoaderRef};
use crate::jni::create_jni_function_table;
use crate::vm::Vm;
use crate::{Frame, MethodDescriptor, Value, VmError};
use crate::{BaseType, FieldType, Frame, MethodDescriptor, Value, VmError};
use deku::DekuError::Incomplete;
use jni::sys::jlong;
use jni::JNIEnv;
use libffi::low::call;
use libffi::middle::*;
use log::{trace, warn};
use std::ops::Add;
use std::ptr::null_mut;
use std::sync::{Arc, Mutex};
use std::vec::IntoIter;
type MethodCallResult = Result<Option<Value>, VmError>;
@ -38,9 +47,9 @@ impl VmThread {
.lock()
.unwrap()
.get_or_load(what)
.map_err(|e| VmError::LoaderError(e))?;
.map_err(VmError::LoaderError)?;
// Phase 2: Collect classes that need initialization (short lock)
// Phase 2: Collect classes that need initialisation (short lock)
let classes_to_init = {
let mut loader = self.loader.lock().unwrap();
let classes = loader.needs_init.clone();
@ -48,7 +57,7 @@ impl VmThread {
classes
};
// Phase 3: Initialize each class (NO lock held - allows recursion)
// Phase 3: Initialise each class (NO lock held - allows recursion)
for class in classes_to_init {
self.init(class, thread.clone())?;
}
@ -74,7 +83,7 @@ impl VmThread {
}
InitState::Initializing(tid) if *tid == current_thread => {
// JVM Spec 5.5: Recursive initialization by same thread is allowed
println!(
warn!(
"Class {} already being initialized by this thread (recursive)",
class.this_class
);
@ -102,7 +111,7 @@ impl VmThread {
}
// Perform actual initialization
println!("Initializing class: {}", class.this_class);
trace!("Initializing class: {}", class.this_class);
let result = (|| {
// Initialize superclass first (if any)
if let Some(ref super_class) = class.super_class {
@ -110,7 +119,7 @@ impl VmThread {
}
// 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 {
Frame::new(
method.code.clone().unwrap(),
@ -130,7 +139,7 @@ impl VmThread {
match result {
Ok(_) => {
*state = InitState::Initialized;
println!("Class {} initialized successfully", class.this_class);
trace!("Class {} initialized successfully", class.this_class);
}
Err(ref e) => {
*state = InitState::Error(format!("{:?}", e));
@ -142,9 +151,18 @@ impl VmThread {
}
pub fn invoke_main(&self, what: &str, thread: Arc<VmThread>) {
let method_ref = MethodRef {
class: what.to_string(),
name: "main".to_string(),
desc: MethodDescriptor::psvm(),
};
self.invoke(method_ref, thread).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());
let main_method = class.find_method("main", &MethodDescriptor::psvm());
println!("{:?}", main_method);
if let Ok(meth) = main_method {
let mut frame = Frame::new(
@ -162,10 +180,13 @@ impl VmThread {
pub fn invoke(&self, method_reference: MethodRef, thread: Arc<VmThread>) -> MethodCallResult {
let class = self.get_or_resolve_class(&method_reference.class, thread.clone())?;
let resolved_method = class
.find_method(&method_reference.name, method_reference.desc)
.find_method(&method_reference.name, &method_reference.desc)
.unwrap();
println!("invoking {}: {}", method_reference.name, class.this_class);
if resolved_method.flags.ACC_NATIVE {
return self.invoke_native();
unsafe {
return self.invoke_native(&method_reference);
}
}
let mut frame = Frame::new(
resolved_method.code.clone().unwrap(),
@ -176,7 +197,79 @@ impl VmThread {
frame.execute()
}
pub fn invoke_native(&self) -> MethodCallResult {
todo!("Invoke native")
pub fn invoke_native(&self, method: &MethodRef) -> MethodCallResult {
let symbol_name = generate_jni_method_name(method);
println!("{:?}", &symbol_name);
unsafe {
// manually load relevant library for poc
let lib = libloading::os::windows::Library::new(
"C:\\Program Files\\Java\\jdk-25\\bin\\jvm_rs.dll",
)
.expect("load jvm_rs.dll");
// build pointer to native fn
let cp = CodePtr::from_ptr(
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();
// coerce my method descriptors into libffi C equivalents, then call
let l = method
.build_cif()
.call::<jlong>(cp, &*vec![arg(&JNIEnv), arg(&null_mut::<()>())]);
println!("{l}");
}
Ok(None)
// todo!("Invoke native")
}
}
pub fn generate_jni_method_name(method_ref: &MethodRef) -> String {
let class_name = &method_ref.class.replace("/", "_");
let method_name = &method_ref.name;
format!("Java_{class_name}_{method_name}")
}
impl From<FieldType> for Type {
fn from(value: FieldType) -> Self {
match value {
FieldType::Base(v) => match v {
BaseType::Byte => Type::i8(),
BaseType::Char => Type::u16(),
BaseType::Double => Type::f64(),
BaseType::Float => Type::f32(),
BaseType::Int => Type::i32(),
BaseType::Long => Type::i64(),
BaseType::Short => Type::i16(),
BaseType::Boolean => Type::i8(),
},
FieldType::ClassType(_) => Self::pointer(),
FieldType::ArrayType(_) => Self::pointer(),
}
}
}
impl MethodRef {
fn build_cif(&self) -> Cif {
let mut args = vec![
Type::pointer(), //JNIEnv*
Type::pointer(), //jclass
];
for v in self.desc.parameters.clone() {
args.push(v.into())
}
let return_type = if let Some(x) = self.desc.return_type.clone() {
x.into()
} else {
Type::void()
};
Builder::new().args(args).res(return_type).into_cif()
}
}

View File

@ -1,14 +1,17 @@
use std::sync::{Arc, Mutex};
use crate::class_file::ClassFile;
use crate::class_loader::ClassLoader;
use crate::Frame;
use crate::thread::VmThread;
use crate::Frame;
use libloading::os::windows::Symbol;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
// struct AbstractObject<'a> {}
pub struct Vm {
// for now, model just a single thread
pub thread: Mutex<Vec<Arc<VmThread>>>,
pub loader: Arc<Mutex<ClassLoader>>
pub loader: Arc<Mutex<ClassLoader>>,
pub native_methods: HashMap<String, Symbol<()>>,
}
impl Vm {
@ -17,6 +20,7 @@ impl Vm {
let vm = Arc::new(Self {
loader: Arc::new(Mutex::from(ClassLoader::default())),
thread: Mutex::new(Vec::new()),
native_methods: Default::default(),
});
let thread = Arc::new(VmThread::new(vm.clone(), None));
vm.thread.lock().unwrap().push(thread.clone());

View File

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

View File

@ -0,0 +1,97 @@
#![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
}

View File

@ -1,5 +0,0 @@
fn main() {
jvm_rs::run()
}