check check checkpoint
This commit is contained in:
parent
d9368d2448
commit
707b961903
@ -14,4 +14,6 @@ sevenz-rust2 = { version = "0.19.3", features = ["brotli", "zstd"] }
|
||||
dashmap = "7.0.0-rc2"
|
||||
libloading = "0.8.9"
|
||||
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" }
|
||||
@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "jvm-rs-core"
|
||||
name = "roast-vm-core"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
publish = ["nexus"]
|
||||
@ -9,15 +9,19 @@ 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 }
|
||||
itertools = { workspace = true }
|
||||
|
||||
[build-dependencies]
|
||||
bindgen = "0.72.1"
|
||||
|
||||
[lints.rust]
|
||||
#missing_docs = "warn"
|
||||
#missing_docs = "warn"
|
||||
|
||||
[[bin]]
|
||||
name = "roast"
|
||||
path = "src/main.rs"
|
||||
@ -130,10 +130,7 @@ impl AttributeInfo {
|
||||
|
||||
/// Get the interpreted attribute, parsing if necessary
|
||||
pub fn get(&self, class_file: &ClassFile) -> Option<Attribute> {
|
||||
class_file
|
||||
.constant_pool
|
||||
.parse_attribute(self.deref().clone())
|
||||
.ok()
|
||||
class_file.constant_pool.parse_attribute(self.clone()).ok()
|
||||
|
||||
// if let Some(ref attr) = self.interpreted {
|
||||
// Some(attr.clone())
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
use log::trace;
|
||||
use sevenz_rust2::ArchiveReader;
|
||||
use std::collections::HashSet;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::fs::File;
|
||||
|
||||
const DEFAULT_LOCATION: &str = "./lib/modules";
|
||||
@ -7,6 +8,9 @@ const DEFAULT_LOCATION: &str = "./lib/modules";
|
||||
pub struct Bimage {
|
||||
image: ArchiveReader<File>,
|
||||
modules: Vec<String>,
|
||||
// inversion, <Package, Module>
|
||||
// eg. <java.lang, java.base>
|
||||
packages: HashMap<String, String>,
|
||||
}
|
||||
|
||||
impl Default for Bimage {
|
||||
@ -24,9 +28,27 @@ impl Default for Bimage {
|
||||
.into_iter()
|
||||
.collect::<Vec<_>>();
|
||||
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 {
|
||||
image: reader,
|
||||
modules,
|
||||
packages,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -58,6 +80,8 @@ impl Bimage {
|
||||
}
|
||||
|
||||
pub fn get_class(&mut self, module: &str, class: &str) -> Option<Vec<u8>> {
|
||||
// trace!("Modules{:#?}", self.modules);
|
||||
|
||||
let path = Self::resolve_path(module, class);
|
||||
self.image
|
||||
.read_file(&path)
|
||||
|
||||
@ -5,7 +5,8 @@ use crate::class_file::{
|
||||
};
|
||||
use crate::{FieldType, MethodDescriptor, VmError};
|
||||
use log::trace;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::sync::{Arc, Mutex, OnceLock, OnceState};
|
||||
use std::thread::ThreadId;
|
||||
|
||||
/// JVM Spec 5.5: Initialization states for a class
|
||||
@ -30,9 +31,26 @@ pub struct RuntimeClass {
|
||||
pub interfaces: Vec<Arc<RuntimeClass>>,
|
||||
pub fields: Vec<FieldData>,
|
||||
pub methods: Vec<MethodData>,
|
||||
pub mirror: OnceLock<u32>,
|
||||
/// Thread-safe initialization state (JVM Spec 5.5)
|
||||
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 {
|
||||
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()))
|
||||
}
|
||||
|
||||
pub fn find_field(&self, name: &str, desc: FieldType) -> Result<&FieldData, VmError> {
|
||||
println!("Finding field");
|
||||
pub fn find_field(&self, name: &str, desc: &FieldType) -> Result<&FieldData, VmError> {
|
||||
trace!("Finding field");
|
||||
if let Some(field) = self.fields.iter().find(|e| {
|
||||
println!("Field Name Needed: {name}, Checked:{}", e.name);
|
||||
println!("Field type Needed: {desc:?}, Checked:{:?}", e.desc);
|
||||
trace!("Field Name Needed: {name}, Checked:{}", e.name);
|
||||
trace!("Field type Needed: {desc:?}, Checked:{:?}", e.desc);
|
||||
let name_match = e.name.eq(name);
|
||||
let type_match = desc == e.desc;
|
||||
let type_match = *desc == e.desc;
|
||||
name_match && type_match
|
||||
}) {
|
||||
trace!("Found field: {name}");
|
||||
return Ok(field);
|
||||
};
|
||||
|
||||
@ -77,4 +96,10 @@ impl RuntimeClass {
|
||||
// No field found, and we must be Object, as we don't have a superclass
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -490,12 +490,42 @@ pub fn pool_get_string(constant_pool: &[ConstantPoolEntry], index: u16) -> Optio
|
||||
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)]
|
||||
#[deku(endian = "Big")]
|
||||
pub(crate) struct Bytecode {
|
||||
bytes: u32,
|
||||
#[deku(bytes_read = "bytes")]
|
||||
pub code: Vec<Ops>,
|
||||
#[deku(reader = "read_bytecode_with_offsets(*bytes, deku::reader, deku::ctx::Endian::Big)")]
|
||||
pub code: Vec<(u16, Ops)>,
|
||||
}
|
||||
|
||||
// pub trait ConstantPoolExt {
|
||||
@ -615,7 +645,7 @@ pub(crate) struct Bytecode {
|
||||
// }
|
||||
// }
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MethodRef {
|
||||
pub class: String,
|
||||
pub name: String,
|
||||
@ -673,7 +703,7 @@ impl From<Constant> for Value {
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[derive(Debug, PartialEq, DekuRead, DekuWrite)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, DekuRead, DekuWrite)]
|
||||
pub struct ClassFlags {
|
||||
// flags
|
||||
#[deku(bits = 1)]
|
||||
|
||||
@ -1,23 +1,17 @@
|
||||
use crate::attributes::Attribute;
|
||||
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::{
|
||||
ClassFile, ClassFlags, ConstantClassInfo, ConstantPoolEntry, FieldData, FieldFlags, MethodData,
|
||||
MethodFlags,
|
||||
};
|
||||
use crate::native_libraries::NativeLibraries;
|
||||
use crate::class_file::{ClassFile, ClassFlags, FieldData, FieldFlags, MethodData, MethodFlags};
|
||||
use crate::{FieldType, MethodDescriptor};
|
||||
use dashmap::DashMap;
|
||||
use deku::DekuContainerRead;
|
||||
use itertools::Itertools;
|
||||
use libloading::os::windows::Library;
|
||||
use log::warn;
|
||||
use std::collections::hash_map::{Entry, Iter};
|
||||
use std::collections::HashSet;
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::sync::{Arc, Mutex, OnceLock};
|
||||
|
||||
pub type LoaderRef = Arc<Mutex<ClassLoader>>;
|
||||
|
||||
@ -88,15 +82,16 @@ pub fn resolve_path(what: &str) -> Result<(PathBuf, String), String> {
|
||||
/// ```
|
||||
#[derive(Default)]
|
||||
pub struct ClassLoader {
|
||||
classes: DashMap<String, Arc<RuntimeClass>>,
|
||||
classes: DashMap<(String, LoaderId), Arc<RuntimeClass>>,
|
||||
bimage: Bimage,
|
||||
pub needs_init: Vec<Arc<RuntimeClass>>,
|
||||
native_libraries: NativeLibraries,
|
||||
}
|
||||
|
||||
type LoaderId = Option<u32>;
|
||||
|
||||
impl ClassLoader {
|
||||
pub fn new() -> Result<Self, String> {
|
||||
let mut loader = Self::default();
|
||||
let loader = Self::default();
|
||||
// for entry in VM_CLASSES {
|
||||
// let module_class = format!("{}/{}", "java.base", entry);;
|
||||
// loader.load_class(&module_class)?;
|
||||
@ -104,6 +99,13 @@ impl ClassLoader {
|
||||
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.
|
||||
///
|
||||
/// # Arguments
|
||||
@ -135,11 +137,43 @@ impl ClassLoader {
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub fn get_or_load(&mut self, what: &str) -> Result<Arc<RuntimeClass>, String> {
|
||||
if let Some(class) = self.classes.get(what) {
|
||||
pub fn get_or_load(
|
||||
&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());
|
||||
}
|
||||
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());
|
||||
Ok(class)
|
||||
}
|
||||
@ -148,7 +182,7 @@ impl ClassLoader {
|
||||
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 bytes = self
|
||||
.bimage
|
||||
@ -158,7 +192,9 @@ impl ClassLoader {
|
||||
.map_err(|e| format!("failed to parse class file: {}", e))?;
|
||||
let runtime = self.runtime_class(cf);
|
||||
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() {
|
||||
warn!("Replaced loaded class: {}", class_fqn)
|
||||
}
|
||||
@ -191,7 +227,7 @@ impl ClassLoader {
|
||||
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
|
||||
@ -201,7 +237,7 @@ impl ClassLoader {
|
||||
.get_class_info(class_file.super_class)
|
||||
.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| {
|
||||
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()
|
||||
self.get_or_load(&name, None).unwrap()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
@ -282,6 +318,47 @@ impl ClassLoader {
|
||||
})
|
||||
.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 {
|
||||
constant_pool,
|
||||
access_flags,
|
||||
@ -290,15 +367,92 @@ impl ClassLoader {
|
||||
interfaces,
|
||||
fields,
|
||||
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> {
|
||||
// for (key, value) in self.native_libraries.iter() {
|
||||
// // value.get()
|
||||
// }
|
||||
todo!("class_loader find native")
|
||||
pub fn primitive_class(&mut self, name: &str) -> Arc<RuntimeClass> {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
use std::fmt::{Display, Formatter};
|
||||
use crate::attributes::ArrayType;
|
||||
use deku_derive::DekuRead;
|
||||
|
||||
@ -315,10 +316,48 @@ pub enum Ops {
|
||||
i2s,
|
||||
|
||||
// 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
|
||||
#[deku(id = 0xa7)]
|
||||
goto(u16),
|
||||
goto(i16),
|
||||
|
||||
// discontinued
|
||||
#[deku(id = 0xa8)]
|
||||
@ -383,4 +422,257 @@ pub enum Ops {
|
||||
monitorenter,
|
||||
#[deku(id = 0xC3)]
|
||||
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
@ -6,10 +6,10 @@ macro_rules! store {
|
||||
($self:expr, l, $index:expr) => {{
|
||||
{
|
||||
let index: usize = $index;
|
||||
let value = $self.stack.pop().expect("Must contain value on stack");
|
||||
trace!("\tStoring: {value:?} into local[{index}]");
|
||||
$self.vars[index] = value;
|
||||
$self.vars[index + 1] = Value::Reference(None);
|
||||
let value = $self.pop()?;
|
||||
trace!("\tStoring: {value} into local[{index}]");
|
||||
$self.vars.set(index , value);
|
||||
|
||||
Ok(ExecutionResult::Continue)
|
||||
}
|
||||
}};
|
||||
@ -19,9 +19,9 @@ macro_rules! store {
|
||||
($self:expr, i, $index:expr) => {{
|
||||
{
|
||||
let index: usize = $index;
|
||||
let value = $self.stack.pop().expect("Must contain value on stack");
|
||||
trace!("\tStoring: {value:?} into local[{index}]");
|
||||
$self.vars[index] = value;
|
||||
let value = $self.pop()?;
|
||||
trace!("\tStoring: {value} into local[{index}]");
|
||||
$self.vars.set(index , value);
|
||||
Ok(ExecutionResult::Continue)
|
||||
}
|
||||
}};
|
||||
@ -38,7 +38,7 @@ macro_rules! load {
|
||||
($self:expr, i, $index:expr) => {{
|
||||
{
|
||||
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");
|
||||
$self.stack.push(value.clone());
|
||||
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)
|
||||
}};
|
||||
}
|
||||
@ -1,3 +1,27 @@
|
||||
use roast_vm_core::vm::Vm;
|
||||
use libloading::Library;
|
||||
use log::LevelFilter;
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
use crate::class_file::ConstantPoolEntry;
|
||||
use dashmap::DashMap;
|
||||
use libloading::os::windows::Library;
|
||||
|
||||
use libloading::Library;
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub type NativeLibraries = HashMap<String, Library>;
|
||||
// impl NativeExt for NativeLibraries {}
|
||||
//
|
||||
// trait NativeExt: AsRef<[..]> {
|
||||
|
||||
@ -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()
|
||||
}
|
||||
}
|
||||
@ -1,63 +1,121 @@
|
||||
use crate::objects::object::{Object, ObjectReference};
|
||||
use crate::value::{Primitive, Value};
|
||||
use std::fmt::{Display, Formatter};
|
||||
use crate::objects::object::{Reference, ReferenceKind};
|
||||
use crate::prim::Primitive;
|
||||
use jni::sys::{jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jshort};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
#[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))
|
||||
}
|
||||
}
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
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),
|
||||
}
|
||||
|
||||
pub type PrimitiveArrayReference = Arc<Mutex<PrimitiveArray>>;
|
||||
type PrimitiveArray = (u32, Vec<Primitive>);
|
||||
impl ArrayReference {
|
||||
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>>;
|
||||
type ObjectArray = (u32, Vec<ObjectReference>);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Array<T> {
|
||||
id: u32,
|
||||
backing: Vec<T>,
|
||||
}
|
||||
|
||||
impl Array<Primitive> {
|
||||
fn set(&self, index: i32, value: Value) {
|
||||
let thing : [100, u32]
|
||||
pub fn id(&self) -> u32 {
|
||||
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)]
|
||||
pub struct Array<T: ArrayValue> {
|
||||
pub(crate) id: u32,
|
||||
pub(crate) backing: Box<[T]>,
|
||||
}
|
||||
|
||||
impl<T> Array<T>
|
||||
where
|
||||
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 {}
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
pub mod array;
|
||||
pub mod object;
|
||||
pub mod object_manager;
|
||||
|
||||
pub use object::Reference;
|
||||
pub use object::ReferenceKind;
|
||||
|
||||
@ -1,9 +1,14 @@
|
||||
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 dashmap::DashMap;
|
||||
use log::trace;
|
||||
use std::fmt::Display;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::hash::Hash;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use crate::class_file::FieldRef;
|
||||
use crate::{string_from_bytes, BaseType, FieldType};
|
||||
|
||||
pub type ObjectReference = Arc<Mutex<Object>>;
|
||||
|
||||
@ -16,25 +21,67 @@ pub struct Object {
|
||||
|
||||
impl Object {
|
||||
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);
|
||||
self.fields.insert(field_name.to_string(), value);
|
||||
}
|
||||
|
||||
pub fn get_field(&self, field_name: &str) -> Value {
|
||||
trace!("Fields for object:\n\t{:?}", self.fields);
|
||||
pub fn get_field(&self, field_ref: &FieldRef) -> Value {
|
||||
trace!("Fields for object:\n\t{:#}", self.format_fields());
|
||||
self.fields
|
||||
.get(&field_name.to_string())
|
||||
.get(&field_ref.name)
|
||||
.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 {
|
||||
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)
|
||||
}
|
||||
}
|
||||
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 {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
@ -43,3 +90,143 @@ impl PartialEq 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))
|
||||
}
|
||||
}
|
||||
|
||||
172
crates/core/src/objects/object_manager.rs
Normal file
172
crates/core/src/objects/object_manager.rs
Normal 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
13
crates/core/src/prim.rs
Normal 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 {}
|
||||
@ -2,56 +2,92 @@ use crate::class::RuntimeClass;
|
||||
use crate::class_file::{ClassFile, MethodData, MethodRef};
|
||||
use crate::class_loader::{ClassLoader, LoaderRef};
|
||||
use crate::jni::create_jni_function_table;
|
||||
use crate::objects::object::{ObjectReference, ReferenceKind};
|
||||
use crate::objects::object_manager::ObjectManager;
|
||||
use crate::value::{Primitive, Value};
|
||||
use crate::vm::Vm;
|
||||
use crate::{BaseType, FieldType, Frame, MethodDescriptor, VmError};
|
||||
use crate::{BaseType, FieldType, Frame, MethodDescriptor, ThreadId, VmError};
|
||||
use deku::DekuError::Incomplete;
|
||||
use jni::sys::{jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jobject, jshort};
|
||||
use jni::JNIEnv;
|
||||
use itertools::Itertools;
|
||||
use jni::sys::{jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jobject, jshort, JNIEnv};
|
||||
use libffi::low::call;
|
||||
use libffi::middle::*;
|
||||
use log::{trace, warn};
|
||||
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::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
use std::vec::IntoIter;
|
||||
|
||||
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
|
||||
pub struct VmThread {
|
||||
pub id: ThreadId,
|
||||
pub vm: Arc<Vm>,
|
||||
pub loader: Arc<Mutex<ClassLoader>>,
|
||||
pub frame_stack: Vec<Frame>,
|
||||
pub gc: Arc<RwLock<ObjectManager>>,
|
||||
pub jni_env: JNIEnv,
|
||||
}
|
||||
|
||||
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());
|
||||
Self {
|
||||
vm: vm.clone(),
|
||||
loader,
|
||||
frame_stack: Vec::new(),
|
||||
gc: vm.gc.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 {
|
||||
id,
|
||||
vm,
|
||||
loader,
|
||||
frame_stack: Vec::new(),
|
||||
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.
|
||||
/// Follows JVM Spec 5.5 for recursive initialization handling.
|
||||
pub fn get_or_resolve_class(
|
||||
&self,
|
||||
what: &str,
|
||||
thread: Arc<VmThread>,
|
||||
) -> Result<Arc<RuntimeClass>, VmError> {
|
||||
pub fn get_or_resolve_class(&self, what: &str) -> Result<Arc<RuntimeClass>, VmError> {
|
||||
// Phase 1: Load the class (short lock)
|
||||
let runtime_class = self
|
||||
.loader
|
||||
.lock()
|
||||
.unwrap()
|
||||
.get_or_load(what)
|
||||
.get_or_load(what, None)
|
||||
.map_err(VmError::LoaderError)?;
|
||||
|
||||
// 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)
|
||||
for class in classes_to_init {
|
||||
self.init(class, thread.clone())?;
|
||||
self.init(class)?;
|
||||
}
|
||||
|
||||
Ok(runtime_class)
|
||||
@ -74,13 +110,13 @@ impl VmThread {
|
||||
self.loader
|
||||
.lock()
|
||||
.unwrap()
|
||||
.get_or_load(what)
|
||||
.get_or_load(what, None)
|
||||
.map_err(VmError::LoaderError)
|
||||
}
|
||||
|
||||
/// Initialize a class following JVM Spec 5.5.
|
||||
/// 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 std::thread;
|
||||
|
||||
@ -122,23 +158,40 @@ impl VmThread {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Perform actual initialization
|
||||
let class_class = self.get_class("java/lang/Class")?;
|
||||
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);
|
||||
let result = (|| {
|
||||
// Initialize superclass first (if any)
|
||||
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
|
||||
let class_init_method = class.find_method("<clinit>", &MethodDescriptor::void());
|
||||
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(
|
||||
method_ref,
|
||||
method.code.clone().unwrap(),
|
||||
class.constant_pool.clone(),
|
||||
vec![],
|
||||
thread.clone(),
|
||||
self.vm.clone(),
|
||||
)
|
||||
.execute()
|
||||
.map_err(|e| VmError::LoaderError(format!("Error in <clinit>: {:?}", e)))?;
|
||||
@ -163,41 +216,20 @@ impl VmThread {
|
||||
result
|
||||
}
|
||||
|
||||
pub fn invoke_main(&self, what: &str, thread: Arc<VmThread>) {
|
||||
pub fn invoke_main(&self, what: &str) {
|
||||
let method_ref = MethodRef {
|
||||
class: what.to_string(),
|
||||
name: "main".to_string(),
|
||||
desc: MethodDescriptor::psvm(),
|
||||
};
|
||||
|
||||
self.invoke(method_ref, Vec::new(), thread)
|
||||
self.invoke(method_ref, Vec::new())
|
||||
.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(
|
||||
&self,
|
||||
method_reference: MethodRef,
|
||||
args: Vec<Value>,
|
||||
thread: Arc<VmThread>,
|
||||
) -> MethodCallResult {
|
||||
let class = self.get_or_resolve_class(&method_reference.class, thread.clone())?;
|
||||
pub fn invoke(&self, method_reference: MethodRef, mut args: Vec<Value>) -> MethodCallResult {
|
||||
let mut new_args = Vec::new();
|
||||
let class = self.get_or_resolve_class(&method_reference.class)?;
|
||||
let resolved_method = class
|
||||
.find_method(&method_reference.name, &method_reference.desc)
|
||||
.unwrap();
|
||||
@ -207,37 +239,45 @@ impl VmThread {
|
||||
class.this_class
|
||||
);
|
||||
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(
|
||||
method_reference.clone(),
|
||||
resolved_method.code.clone().unwrap(),
|
||||
class.constant_pool.clone(),
|
||||
args,
|
||||
thread.clone(),
|
||||
self.vm.clone(),
|
||||
);
|
||||
frame.execute()
|
||||
}
|
||||
|
||||
pub fn invoke_native(&self, method: &MethodRef, args: Vec<Value>) -> MethodCallResult {
|
||||
let symbol_name = generate_jni_method_name(method);
|
||||
pub fn invoke_native(&self, method: &MethodRef, mut args: Vec<Value>) -> MethodCallResult {
|
||||
let symbol_name = generate_jni_method_name(method, false);
|
||||
trace!("searching for native symbol: {:?}", &symbol_name);
|
||||
|
||||
let result = 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");
|
||||
if symbol_name.contains("Java_java_lang_Class_desiredAssertionStatus0") {
|
||||
warn!("MAJOR HACK, figure out what is wrong with desiredAssertionStatus0");
|
||||
return Ok(Some(Value::from(false)));
|
||||
}
|
||||
|
||||
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
|
||||
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();
|
||||
let cp = CodePtr::from_ptr(p);
|
||||
|
||||
// let args = build_args(args);
|
||||
|
||||
@ -245,8 +285,14 @@ impl VmThread {
|
||||
// let l = method.build_cif().call::<jlong>(cp, args.as_ref());
|
||||
|
||||
let mut storage = Vec::new();
|
||||
trace!("passing {args:?} to native fn");
|
||||
let built_args = build_args(args, &mut storage);
|
||||
trace!("passing {} to native fn", Value::format_vec(&args));
|
||||
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();
|
||||
|
||||
match &method.desc.return_type {
|
||||
@ -288,8 +334,17 @@ impl VmThread {
|
||||
}
|
||||
Some(FieldType::ClassType(_)) | Some(FieldType::ArrayType(_)) => {
|
||||
let v = cif.call::<jobject>(cp, built_args.as_ref());
|
||||
// TODO: Convert jobject to Reference properly
|
||||
Ok(Some(Value::Reference(None)))
|
||||
// Convert jobject (u32 ID) to Reference
|
||||
let obj_id = v as u32;
|
||||
if obj_id == 0 {
|
||||
// Null reference
|
||||
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>> {
|
||||
// Store values in the provided storage
|
||||
storage.push(Box::new(create_jni_function_table()) as Box<dyn Any>);
|
||||
storage.push(Box::new(std::ptr::null_mut::<()>()) as Box<dyn Any>);
|
||||
fn build_args<'a>(
|
||||
mut params: VecDeque<Value>,
|
||||
storage: &'a mut Vec<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 {
|
||||
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::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::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()
|
||||
}
|
||||
|
||||
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}")
|
||||
pub fn generate_jni_method_name(method_ref: &MethodRef, with_type: bool) -> String {
|
||||
let class_name = jni_escape(&method_ref.class);
|
||||
let method_name = jni_escape(&method_ref.name);
|
||||
|
||||
let mut name = format!("Java_{class_name}_{method_name}");
|
||||
if with_type {
|
||||
let params = jni_escape(&method_ref.desc.param_string());
|
||||
let str = format!("__{}", params);
|
||||
name.push_str(&str)
|
||||
}
|
||||
name
|
||||
}
|
||||
|
||||
pub fn jni_escape_char(c: char) -> String {
|
||||
match c {
|
||||
'/' => "_".to_string(),
|
||||
'_' => "_1".to_string(),
|
||||
';' => "_2".to_string(),
|
||||
'[' => "_3".to_string(),
|
||||
c if c.is_ascii_alphanumeric() => c.to_string(),
|
||||
c => format!("_0{:04x}", c as u32),
|
||||
}
|
||||
}
|
||||
pub fn jni_escape(s: &str) -> String {
|
||||
s.chars().map(jni_escape_char).join("")
|
||||
}
|
||||
|
||||
impl From<FieldType> for Type {
|
||||
@ -363,3 +457,27 @@ impl MethodRef {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,12 @@
|
||||
use crate::objects::array::Reference;
|
||||
use crate::objects::object::ObjectReference;
|
||||
use crate::objects::array::ArrayReference;
|
||||
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 log::trace;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::ops::Deref;
|
||||
|
||||
/// A reference-counted, thread-safe pointer to an Object.
|
||||
|
||||
@ -13,7 +18,155 @@ use std::fmt::{Display, Formatter};
|
||||
pub enum Value {
|
||||
Primitive(Primitive),
|
||||
/// 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 {
|
||||
@ -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)]
|
||||
pub enum Primitive {
|
||||
/// Boolean value (true/false)
|
||||
@ -63,7 +240,12 @@ impl Display for Primitive {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
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::Double(d) => write!(f, "double({})", d),
|
||||
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 {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Value::Primitive(prim) => write!(f, "{}", prim),
|
||||
Value::Reference(Some(obj)) => write!(f, "Ref({})", obj),
|
||||
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") }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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::objects::object::Object;
|
||||
use crate::objects::object_manager::ObjectManager;
|
||||
use crate::thread::VmThread;
|
||||
use crate::Frame;
|
||||
use libloading::os::windows::Symbol;
|
||||
use std::collections::HashMap;
|
||||
use crate::{MethodDescriptor, ThreadId, VmError};
|
||||
use dashmap::DashMap;
|
||||
use imp::{Library, Symbol};
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
use crate::class::{InitState, RuntimeClass};
|
||||
use crate::objects::object::ReferenceKind;
|
||||
|
||||
// struct AbstractObject<'a> {}
|
||||
pub struct Vm {
|
||||
// for now, model just a single thread
|
||||
pub thread: Mutex<Vec<Arc<VmThread>>>,
|
||||
// Thread registry - maps ThreadId to VmThread
|
||||
pub threads: DashMap<ThreadId, Arc<VmThread>>,
|
||||
pub main_thread_id: ThreadId,
|
||||
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>>,
|
||||
}
|
||||
|
||||
impl Vm {
|
||||
// start vm, loading main from classfile
|
||||
pub fn new(what: &str) -> Arc<Self> {
|
||||
pub fn new() -> Arc<Self> {
|
||||
let vm = Arc::new(Self {
|
||||
threads: DashMap::new(),
|
||||
main_thread_id: ThreadId(0),
|
||||
loader: Arc::new(Mutex::from(ClassLoader::default())),
|
||||
thread: Mutex::new(Vec::new()),
|
||||
native_methods: Default::default(),
|
||||
native_methods: DashMap::new(),
|
||||
native_libraries: DashMap::new(),
|
||||
gc: Default::default(),
|
||||
});
|
||||
let thread = Arc::new(VmThread::new(vm.clone(), None));
|
||||
vm.thread.lock().unwrap().push(thread.clone());
|
||||
thread.invoke_main(what, thread.clone());
|
||||
vm.clone()
|
||||
|
||||
// Create main thread
|
||||
let thread = VmThread::new(vm.clone(), None);
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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}")
|
||||
}
|
||||
@ -1,12 +1,13 @@
|
||||
[package]
|
||||
name = "jvm-rs-sys"
|
||||
name = "roast-vm-sys"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
publish = ["nexus"]
|
||||
|
||||
[dependencies]
|
||||
jni = { workspace = true }
|
||||
roast-vm-core = { workspace = true }
|
||||
|
||||
[lib]
|
||||
name = "jvm_rs"
|
||||
name = "roast_vm"
|
||||
crate-type = ["cdylib"]
|
||||
63
crates/roast-vm-sys/src/class.rs
Normal file
63
crates/roast-vm-sys/src/class.rs
Normal 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
|
||||
|
||||
|
||||
}
|
||||
209
crates/roast-vm-sys/src/lib.rs
Normal file
209
crates/roast-vm-sys/src/lib.rs
Normal 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()
|
||||
}
|
||||
11
crates/roast-vm-sys/src/misc_unsafe.rs
Normal file
11
crates/roast-vm-sys/src/misc_unsafe.rs
Normal 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
|
||||
()
|
||||
}
|
||||
11
crates/roast-vm-sys/src/object.rs
Normal file
11
crates/roast-vm-sys/src/object.rs
Normal 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
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user