diff --git a/Cargo.toml b/Cargo.toml index 054e844..90650d4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" \ No newline at end of file +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" } \ No newline at end of file diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index a40a538..222fcbe 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -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" \ No newline at end of file +#missing_docs = "warn" + +[[bin]] +name = "roast" +path = "src/main.rs" \ No newline at end of file diff --git a/crates/core/src/attributes.rs b/crates/core/src/attributes.rs index 875d24b..acf74ef 100644 --- a/crates/core/src/attributes.rs +++ b/crates/core/src/attributes.rs @@ -130,10 +130,7 @@ impl AttributeInfo { /// Get the interpreted attribute, parsing if necessary pub fn get(&self, class_file: &ClassFile) -> Option { - 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()) diff --git a/crates/core/src/bimage.rs b/crates/core/src/bimage.rs index 10f3437..08e03bb 100644 --- a/crates/core/src/bimage.rs +++ b/crates/core/src/bimage.rs @@ -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, modules: Vec, + // inversion, + // eg. + packages: HashMap, } impl Default for Bimage { @@ -24,9 +28,27 @@ impl Default for Bimage { .into_iter() .collect::>(); 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::>();*/ + + 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> { + // trace!("Modules{:#?}", self.modules); + let path = Self::resolve_path(module, class); self.image .read_file(&path) diff --git a/crates/core/src/class.rs b/crates/core/src/class.rs index 49a1987..15c5d15 100644 --- a/crates/core/src/class.rs +++ b/crates/core/src/class.rs @@ -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>, pub fields: Vec, pub methods: Vec, + pub mirror: OnceLock, /// Thread-safe initialization state (JVM Spec 5.5) pub init_state: Mutex, + pub super_classes: Vec>, + pub super_interfaces: Vec>, + pub component_type: Option>, } +impl Hash for RuntimeClass { + fn hash(&self, state: &mut H) { + self.this_class.hash(state) + } +} + +impl PartialEq 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) -> bool { + self.eq(&*into) + || self.super_classes.contains(&into) + || self.super_interfaces.contains(&into) + } } diff --git a/crates/core/src/class_file/class_file.rs b/crates/core/src/class_file/class_file.rs index fc67c13..f14e521 100644 --- a/crates/core/src/class_file/class_file.rs +++ b/crates/core/src/class_file/class_file.rs @@ -490,12 +490,42 @@ pub fn pool_get_string(constant_pool: &[ConstantPoolEntry], index: u16) -> Optio None } +fn read_bytecode_with_offsets( + bytes: u32, + reader: &mut deku::reader::Reader, + endian: deku::ctx::Endian, +) -> Result, 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, + #[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 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)] diff --git a/crates/core/src/class_loader.rs b/crates/core/src/class_loader.rs index a2aa30c..3c25ea9 100644 --- a/crates/core/src/class_loader.rs +++ b/crates/core/src/class_loader.rs @@ -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>; @@ -88,15 +82,16 @@ pub fn resolve_path(what: &str) -> Result<(PathBuf, String), String> { /// ``` #[derive(Default)] pub struct ClassLoader { - classes: DashMap>, + classes: DashMap<(String, LoaderId), Arc>, bimage: Bimage, pub needs_init: Vec>, - native_libraries: NativeLibraries, } +type LoaderId = Option; + impl ClassLoader { pub fn new() -> Result { - 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> { + self.classes + .iter() + .map(|x| x.value().clone()) + .find(|e| *e.mirror.wait() == id) + } + /// Retrieves an `Arc` 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, String> { - if let Some(class) = self.classes.get(what) { + pub fn get_or_load( + &mut self, + class_name: &str, + loader: LoaderId, + ) -> Result, 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, String> { + fn load_class(&mut self, what: &str, loader: LoaderId) -> Result, 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::>(); @@ -282,6 +318,47 @@ impl ClassLoader { }) .collect::>(); + let super_classes = std::iter::successors(super_class.clone(), |sc| sc.super_class.clone()) + .collect::>(); + + #[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::>(); + // 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::>(); + // super_names.sort(); + // println!("sif: {:#?}", super_names); + // let mut names = interfaces + // .iter() + // .cloned() + // .map(|e| e.this_class.clone()) + // .collect::>(); + // 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 { + // let name = format!("[{}", component.descriptor()); // e.g., "[Ljava/lang/String;" + let object_class: Arc = self.get_or_load("/java/lang/Object", None).unwrap(); + let cloneable: Arc = self.get_or_load("/java/lang/Cloneable", None).unwrap(); + let serializable: Arc = 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 + super_classes: vec![object_class], + super_interfaces: vec![cloneable, serializable], + component_type: Some(component), // new field } } - unsafe fn find_native(&self, name: String) -> libloading::os::windows::Symbol { - // for (key, value) in self.native_libraries.iter() { - // // value.get() - // } - todo!("class_loader find native") + pub fn primitive_class(&mut self, name: &str) -> Arc { + + + + + + + + + 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 } } diff --git a/crates/core/src/instructions.rs b/crates/core/src/instructions.rs index ca58c94..e45361e 100644 --- a/crates/core/src/instructions.rs +++ b/crates/core/src/instructions.rs @@ -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"), + } + } +} \ No newline at end of file diff --git a/crates/core/src/jni.rs b/crates/core/src/jni.rs index eb0338b..dabc82f 100644 --- a/crates/core/src/jni.rs +++ b/crates/core/src/jni.rs @@ -1,6 +1,17 @@ -use jni::sys::JNIEnv; -use jni::sys::{jint, JNINativeInterface_}; +#![feature(c_variadic)] +use crate::class::RuntimeClass; +use crate::objects::array::ArrayReference; +use crate::objects::object_manager::ObjectManager; +use crate::prim::jboolean; +use crate::thread::VmThread; +use crate::value::{Primitive, Value}; +use jni::objects::JClass; +use jni::sys::{jclass, jint, jobject, JNINativeInterface_}; +use jni::sys::{jstring, JNIEnv}; +use std::ffi::{c_char, CStr, CString}; +use std::mem; use std::ptr; +use std::sync::{Arc, Mutex, RwLock}; const JNI_VERSION_1_1: jint = 0x00010001; const JNI_VERSION_1_2: jint = 0x00010002; @@ -14,244 +25,2136 @@ const JNI_VERSION_20: jint = 0x00140000; const JNI_VERSION_21: jint = 0x00150000; const JNI_VERSION_24: jint = 0x00180000; -pub fn create_jni_function_table() -> JNIEnv { +pub fn create_jni_function_table(thread: *const VmThread) -> JNIEnv { Box::into_raw(Box::new(JNINativeInterface_ { - reserved0: ptr::null_mut(), + reserved0: thread as *mut _, reserved1: ptr::null_mut(), reserved2: ptr::null_mut(), reserved3: ptr::null_mut(), GetVersion: Some(jni_get_version), - DefineClass: None, - FindClass: None, - FromReflectedMethod: None, - FromReflectedField: None, - ToReflectedMethod: None, - GetSuperclass: None, - IsAssignableFrom: None, - ToReflectedField: None, - Throw: None, - ThrowNew: None, - ExceptionOccurred: None, - ExceptionDescribe: None, - ExceptionClear: None, - FatalError: None, - PushLocalFrame: None, - PopLocalFrame: None, - NewGlobalRef: None, - DeleteGlobalRef: None, - DeleteLocalRef: None, - IsSameObject: None, - NewLocalRef: None, - EnsureLocalCapacity: None, - AllocObject: None, + DefineClass: Some(define_class), + FindClass: Some(find_class), + FromReflectedMethod: Some(from_reflected_method), + FromReflectedField: Some(from_reflected_field), + ToReflectedMethod: Some(to_reflected_method), + GetSuperclass: Some(get_superclass), + IsAssignableFrom: Some(is_assignable_from), + ToReflectedField: Some(to_reflected_field), + Throw: Some(throw), + ThrowNew: Some(throw_new), + ExceptionOccurred: Some(exception_occurred), + ExceptionDescribe: Some(exception_describe), + ExceptionClear: Some(exception_clear), + FatalError: Some(fatal_error), + PushLocalFrame: Some(push_local_frame), + PopLocalFrame: Some(pop_local_frame), + NewGlobalRef: Some(new_global_ref), + DeleteGlobalRef: Some(delete_global_ref), + DeleteLocalRef: Some(delete_local_ref), + IsSameObject: Some(is_same_object), + NewLocalRef: Some(new_local_ref), + EnsureLocalCapacity: Some(ensure_local_capacity), + AllocObject: Some(alloc_object), NewObject: None, - NewObjectV: None, - NewObjectA: None, - GetObjectClass: None, - IsInstanceOf: None, - GetMethodID: None, + NewObjectV: Some(new_object_v), + NewObjectA: Some(new_object_a), + GetObjectClass: Some(get_object_class), + IsInstanceOf: Some(is_instance_of), + GetMethodID: Some(get_method_id), CallObjectMethod: None, - CallObjectMethodV: None, - CallObjectMethodA: None, + CallObjectMethodV: Some(call_object_method_v), + CallObjectMethodA: Some(call_object_method_a), CallBooleanMethod: None, - CallBooleanMethodV: None, - CallBooleanMethodA: None, + CallBooleanMethodV: Some(call_boolean_method_v), + CallBooleanMethodA: Some(call_boolean_method_a), CallByteMethod: None, - CallByteMethodV: None, - CallByteMethodA: None, + CallByteMethodV: Some(call_byte_method_v), + CallByteMethodA: Some(call_byte_method_a), CallCharMethod: None, - CallCharMethodV: None, - CallCharMethodA: None, + CallCharMethodV: Some(call_char_method_v), + CallCharMethodA: Some(call_char_method_a), CallShortMethod: None, - CallShortMethodV: None, - CallShortMethodA: None, + CallShortMethodV: Some(call_short_method_v), + CallShortMethodA: Some(call_short_method_a), CallIntMethod: None, - CallIntMethodV: None, - CallIntMethodA: None, + CallIntMethodV: Some(call_int_method_v), + CallIntMethodA: Some(call_int_method_a), CallLongMethod: None, - CallLongMethodV: None, - CallLongMethodA: None, + CallLongMethodV: Some(call_long_method_v), + CallLongMethodA: Some(call_long_method_a), CallFloatMethod: None, - CallFloatMethodV: None, - CallFloatMethodA: None, + CallFloatMethodV: Some(call_float_method_v), + CallFloatMethodA: Some(call_float_method_a), CallDoubleMethod: None, - CallDoubleMethodV: None, - CallDoubleMethodA: None, + CallDoubleMethodV: Some(call_double_method_v), + CallDoubleMethodA: Some(call_double_method_a), CallVoidMethod: None, - CallVoidMethodV: None, - CallVoidMethodA: None, + CallVoidMethodV: Some(call_void_method_v), + CallVoidMethodA: Some(call_void_method_a), CallNonvirtualObjectMethod: None, - CallNonvirtualObjectMethodV: None, - CallNonvirtualObjectMethodA: None, + CallNonvirtualObjectMethodV: Some(call_nonvirtual_object_method_v), + CallNonvirtualObjectMethodA: Some(call_nonvirtual_object_method_a), CallNonvirtualBooleanMethod: None, - CallNonvirtualBooleanMethodV: None, - CallNonvirtualBooleanMethodA: None, + CallNonvirtualBooleanMethodV: Some(call_nonvirtual_boolean_method_v), + CallNonvirtualBooleanMethodA: Some(call_nonvirtual_boolean_method_a), CallNonvirtualByteMethod: None, - CallNonvirtualByteMethodV: None, - CallNonvirtualByteMethodA: None, + CallNonvirtualByteMethodV: Some(call_nonvirtual_byte_method_v), + CallNonvirtualByteMethodA: Some(call_nonvirtual_byte_method_a), CallNonvirtualCharMethod: None, - CallNonvirtualCharMethodV: None, - CallNonvirtualCharMethodA: None, + CallNonvirtualCharMethodV: Some(call_nonvirtual_char_method_v), + CallNonvirtualCharMethodA: Some(call_nonvirtual_char_method_a), CallNonvirtualShortMethod: None, - CallNonvirtualShortMethodV: None, - CallNonvirtualShortMethodA: None, + CallNonvirtualShortMethodV: Some(call_nonvirtual_short_method_v), + CallNonvirtualShortMethodA: Some(call_nonvirtual_short_method_a), CallNonvirtualIntMethod: None, - CallNonvirtualIntMethodV: None, - CallNonvirtualIntMethodA: None, + CallNonvirtualIntMethodV: Some(call_nonvirtual_int_method_v), + CallNonvirtualIntMethodA: Some(call_nonvirtual_int_method_a), CallNonvirtualLongMethod: None, - CallNonvirtualLongMethodV: None, - CallNonvirtualLongMethodA: None, + CallNonvirtualLongMethodV: Some(call_nonvirtual_long_method_v), + CallNonvirtualLongMethodA: Some(call_nonvirtual_long_method_a), CallNonvirtualFloatMethod: None, - CallNonvirtualFloatMethodV: None, - CallNonvirtualFloatMethodA: None, + CallNonvirtualFloatMethodV: Some(call_nonvirtual_float_method_v), + CallNonvirtualFloatMethodA: Some(call_nonvirtual_float_method_a), CallNonvirtualDoubleMethod: None, - CallNonvirtualDoubleMethodV: None, - CallNonvirtualDoubleMethodA: None, + CallNonvirtualDoubleMethodV: Some(call_nonvirtual_double_method_v), + CallNonvirtualDoubleMethodA: Some(call_nonvirtual_double_method_a), CallNonvirtualVoidMethod: None, - CallNonvirtualVoidMethodV: None, - CallNonvirtualVoidMethodA: None, - GetFieldID: None, - GetObjectField: None, - GetBooleanField: None, - GetByteField: None, - GetCharField: None, - GetShortField: None, - GetIntField: None, - GetLongField: None, - GetFloatField: None, - GetDoubleField: None, - SetObjectField: None, - SetBooleanField: None, - SetByteField: None, - SetCharField: None, - SetShortField: None, - SetIntField: None, - SetLongField: None, - SetFloatField: None, - SetDoubleField: None, - GetStaticMethodID: None, + CallNonvirtualVoidMethodV: Some(call_nonvirtual_void_method_v), + CallNonvirtualVoidMethodA: Some(call_nonvirtual_void_method_a), + GetFieldID: Some(get_field_id), + GetObjectField: Some(get_object_field), + GetBooleanField: Some(get_boolean_field), + GetByteField: Some(get_byte_field), + GetCharField: Some(get_char_field), + GetShortField: Some(get_short_field), + GetIntField: Some(get_int_field), + GetLongField: Some(get_long_field), + GetFloatField: Some(get_float_field), + GetDoubleField: Some(get_double_field), + SetObjectField: Some(set_object_field), + SetBooleanField: Some(set_boolean_field), + SetByteField: Some(set_byte_field), + SetCharField: Some(set_char_field), + SetShortField: Some(set_short_field), + SetIntField: Some(set_int_field), + SetLongField: Some(set_long_field), + SetFloatField: Some(set_float_field), + SetDoubleField: Some(set_double_field), + GetStaticMethodID: Some(get_static_method_id), CallStaticObjectMethod: None, - CallStaticObjectMethodV: None, - CallStaticObjectMethodA: None, + CallStaticObjectMethodV: Some(call_static_object_method_v), + CallStaticObjectMethodA: Some(call_static_object_method_a), CallStaticBooleanMethod: None, - CallStaticBooleanMethodV: None, - CallStaticBooleanMethodA: None, + CallStaticBooleanMethodV: Some(call_static_boolean_method_v), + CallStaticBooleanMethodA: Some(call_static_boolean_method_a), CallStaticByteMethod: None, - CallStaticByteMethodV: None, - CallStaticByteMethodA: None, + CallStaticByteMethodV: Some(call_static_byte_method_v), + CallStaticByteMethodA: Some(call_static_byte_method_a), CallStaticCharMethod: None, - CallStaticCharMethodV: None, - CallStaticCharMethodA: None, + CallStaticCharMethodV: Some(call_static_char_method_v), + CallStaticCharMethodA: Some(call_static_char_method_a), CallStaticShortMethod: None, - CallStaticShortMethodV: None, - CallStaticShortMethodA: None, + CallStaticShortMethodV: Some(call_static_short_method_v), + CallStaticShortMethodA: Some(call_static_short_method_a), CallStaticIntMethod: None, - CallStaticIntMethodV: None, - CallStaticIntMethodA: None, + CallStaticIntMethodV: Some(call_static_int_method_v), + CallStaticIntMethodA: Some(call_static_int_method_a), CallStaticLongMethod: None, - CallStaticLongMethodV: None, - CallStaticLongMethodA: None, + CallStaticLongMethodV: Some(call_static_long_method_v), + CallStaticLongMethodA: Some(call_static_long_method_a), CallStaticFloatMethod: None, - CallStaticFloatMethodV: None, - CallStaticFloatMethodA: None, + CallStaticFloatMethodV: Some(call_static_float_method_v), + CallStaticFloatMethodA: Some(call_static_float_method_a), CallStaticDoubleMethod: None, - CallStaticDoubleMethodV: None, - CallStaticDoubleMethodA: None, + CallStaticDoubleMethodV: Some(call_static_double_method_v), + CallStaticDoubleMethodA: Some(call_static_double_method_a), CallStaticVoidMethod: None, - CallStaticVoidMethodV: None, - CallStaticVoidMethodA: None, - GetStaticFieldID: None, - GetStaticObjectField: None, - GetStaticBooleanField: None, - GetStaticByteField: None, - GetStaticCharField: None, - GetStaticShortField: None, - GetStaticIntField: None, - GetStaticLongField: None, - GetStaticFloatField: None, - GetStaticDoubleField: None, - SetStaticObjectField: None, - SetStaticBooleanField: None, - SetStaticByteField: None, - SetStaticCharField: None, - SetStaticShortField: None, - SetStaticIntField: None, - SetStaticLongField: None, - SetStaticFloatField: None, - SetStaticDoubleField: None, - NewString: None, - GetStringLength: None, - GetStringChars: None, - ReleaseStringChars: None, - NewStringUTF: None, - GetStringUTFLength: None, - GetStringUTFChars: None, - ReleaseStringUTFChars: None, - GetArrayLength: None, - NewObjectArray: None, - GetObjectArrayElement: None, - SetObjectArrayElement: None, - NewBooleanArray: None, - NewByteArray: None, - NewCharArray: None, - NewShortArray: None, - NewIntArray: None, - NewLongArray: None, - NewFloatArray: None, - NewDoubleArray: None, - GetBooleanArrayElements: None, - GetByteArrayElements: None, - GetCharArrayElements: None, - GetShortArrayElements: None, - GetIntArrayElements: None, - GetLongArrayElements: None, - GetFloatArrayElements: None, - GetDoubleArrayElements: None, - ReleaseBooleanArrayElements: None, - ReleaseByteArrayElements: None, - ReleaseCharArrayElements: None, - ReleaseShortArrayElements: None, - ReleaseIntArrayElements: None, - ReleaseLongArrayElements: None, - ReleaseFloatArrayElements: None, - ReleaseDoubleArrayElements: None, - GetBooleanArrayRegion: None, - GetByteArrayRegion: None, - GetCharArrayRegion: None, - GetShortArrayRegion: None, - GetIntArrayRegion: None, - GetLongArrayRegion: None, - GetFloatArrayRegion: None, - GetDoubleArrayRegion: None, - SetBooleanArrayRegion: None, - SetByteArrayRegion: None, - SetCharArrayRegion: None, - SetShortArrayRegion: None, - SetIntArrayRegion: None, - SetLongArrayRegion: None, - SetFloatArrayRegion: None, - SetDoubleArrayRegion: None, - RegisterNatives: None, - UnregisterNatives: None, - MonitorEnter: None, - MonitorExit: None, - GetJavaVM: None, - GetStringRegion: None, - GetStringUTFRegion: None, - GetPrimitiveArrayCritical: None, - ReleasePrimitiveArrayCritical: None, - GetStringCritical: None, - ReleaseStringCritical: None, - NewWeakGlobalRef: None, - DeleteWeakGlobalRef: None, - ExceptionCheck: None, - NewDirectByteBuffer: None, - GetDirectBufferAddress: None, - GetDirectBufferCapacity: None, - GetObjectRefType: None, + CallStaticVoidMethodV: Some(call_static_void_method_v), + CallStaticVoidMethodA: Some(call_static_void_method_a), + GetStaticFieldID: Some(get_static_field_id), + GetStaticObjectField: Some(get_static_object_field), + GetStaticBooleanField: Some(get_static_boolean_field), + GetStaticByteField: Some(get_static_byte_field), + GetStaticCharField: Some(get_static_char_field), + GetStaticShortField: Some(get_static_short_field), + GetStaticIntField: Some(get_static_int_field), + GetStaticLongField: Some(get_static_long_field), + GetStaticFloatField: Some(get_static_float_field), + GetStaticDoubleField: Some(get_static_double_field), + SetStaticObjectField: Some(set_static_object_field), + SetStaticBooleanField: Some(set_static_boolean_field), + SetStaticByteField: Some(set_static_byte_field), + SetStaticCharField: Some(set_static_char_field), + SetStaticShortField: Some(set_static_short_field), + SetStaticIntField: Some(set_static_int_field), + SetStaticLongField: Some(set_static_long_field), + SetStaticFloatField: Some(set_static_float_field), + SetStaticDoubleField: Some(set_static_double_field), + NewString: Some(new_string), + GetStringLength: Some(get_string_length), + GetStringChars: Some(get_string_chars), + ReleaseStringChars: Some(release_string_chars), + NewStringUTF: Some(new_string_utf), + GetStringUTFLength: Some(get_string_utf_length), + GetStringUTFChars: Some(get_string_utfchars), + ReleaseStringUTFChars: Some(release_string_utf_chars), + GetArrayLength: Some(get_array_length), + NewObjectArray: Some(new_object_array), + GetObjectArrayElement: Some(get_object_array_element), + SetObjectArrayElement: Some(set_object_array_element), + NewBooleanArray: Some(new_boolean_array), + NewByteArray: Some(new_byte_array), + NewCharArray: Some(new_char_array), + NewShortArray: Some(new_short_array), + NewIntArray: Some(new_int_array), + NewLongArray: Some(new_long_array), + NewFloatArray: Some(new_float_array), + NewDoubleArray: Some(new_double_array), + GetBooleanArrayElements: Some(get_boolean_array_elements), + GetByteArrayElements: Some(get_byte_array_elements), + GetCharArrayElements: Some(get_char_array_elements), + GetShortArrayElements: Some(get_short_array_elements), + GetIntArrayElements: Some(get_int_array_elements), + GetLongArrayElements: Some(get_long_array_elements), + GetFloatArrayElements: Some(get_float_array_elements), + GetDoubleArrayElements: Some(get_double_array_elements), + ReleaseBooleanArrayElements: Some(release_boolean_array_elements), + ReleaseByteArrayElements: Some(release_byte_array_elements), + ReleaseCharArrayElements: Some(release_char_array_elements), + ReleaseShortArrayElements: Some(release_short_array_elements), + ReleaseIntArrayElements: Some(release_int_array_elements), + ReleaseLongArrayElements: Some(release_long_array_elements), + ReleaseFloatArrayElements: Some(release_float_array_elements), + ReleaseDoubleArrayElements: Some(release_double_array_elements), + GetBooleanArrayRegion: Some(get_boolean_array_region), + GetByteArrayRegion: Some(get_byte_array_region), + GetCharArrayRegion: Some(get_char_array_region), + GetShortArrayRegion: Some(get_short_array_region), + GetIntArrayRegion: Some(get_int_array_region), + GetLongArrayRegion: Some(get_long_array_region), + GetFloatArrayRegion: Some(get_float_array_region), + GetDoubleArrayRegion: Some(get_double_array_region), + SetBooleanArrayRegion: Some(set_boolean_array_region), + SetByteArrayRegion: Some(set_byte_array_region), + SetCharArrayRegion: Some(set_char_array_region), + SetShortArrayRegion: Some(set_short_array_region), + SetIntArrayRegion: Some(set_int_array_region), + SetLongArrayRegion: Some(set_long_array_region), + SetFloatArrayRegion: Some(set_float_array_region), + SetDoubleArrayRegion: Some(set_double_array_region), + RegisterNatives: Some(register_natives), + UnregisterNatives: Some(unregister_natives), + MonitorEnter: Some(monitor_enter), + MonitorExit: Some(monitor_exit), + GetJavaVM: Some(get_java_vm), + GetStringRegion: Some(get_string_region), + GetStringUTFRegion: Some(get_string_utf_region), + GetPrimitiveArrayCritical: Some(get_primitive_array_critical), + ReleasePrimitiveArrayCritical: Some(release_primitive_array_critical), + GetStringCritical: Some(get_string_critical), + ReleaseStringCritical: Some(release_string_critical), + NewWeakGlobalRef: Some(new_weak_global_ref), + DeleteWeakGlobalRef: Some(delete_weak_global_ref), + ExceptionCheck: Some(exception_check), + NewDirectByteBuffer: Some(new_direct_byte_buffer), + GetDirectBufferAddress: Some(get_direct_buffer_address), + GetDirectBufferCapacity: Some(get_direct_buffer_capacity), + GetObjectRefType: Some(get_object_ref_type), })) } +unsafe fn get_thread(env: *mut JNIEnv) -> *const VmThread { + (**env).reserved0 as *const VmThread +} + +fn resolve_class(thread: &VmThread, clazz: jclass) -> Option> { + let loader = thread.loader.lock().unwrap(); + let class_id = clazz as u32; + loader.class_from_mirror_id(class_id) +} + +fn resolve_object(thread: &VmThread, obj: jobject) -> Option { + let gc = thread.gc.read().unwrap(); + let ReferenceKind::ObjectReference(obj_ref) = gc.get(obj as u32) else { + return None; + }; + Some(obj_ref.clone()) +} + unsafe extern "system" fn jni_get_version(env: *mut JNIEnv) -> jint { JNI_VERSION_24 } + +unsafe extern "system" fn get_string_utfchars( + env: *mut JNIEnv, + str: jstring, + is_copy: *mut jboolean, +) -> *const c_char { + trace!("get_string_utfchars"); + // Set is_copy flag - we always return a copy + // if !is_copy.is_null() { + // *is_copy = 1; + // } + let thread = &*get_thread(env); + let rust_string = { + let gc = thread.gc.read().unwrap(); + + let obj_id = str as u32; + + let obj_ref = match gc.get(obj_id) { + ReferenceKind::ObjectReference(obj) => obj, + _ => return ptr::null(), + }; + + 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 ptr::null(); + }; + + let array = byte_array.lock().unwrap(); + + let bytes: Vec = array.backing.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 + }; + + // Convert to C string + let c_string = match CString::new(rust_string) { + Ok(s) => s, + Err(_) => return ptr::null(), // String contains null byte + }; + + // Return pointer - caller is responsible for calling ReleaseStringUTFChars + c_string.into_raw() +} + +unsafe extern "system" fn get_object_class(env: *mut JNIEnv, obj: jobject) -> jclass { + trace!("get_object_class"); + let thread = &*get_thread(env); + let gc = thread.gc.read().unwrap(); + + let obj_id = obj as u32; + + let ReferenceKind::ObjectReference(obj_ref) = gc.get(obj_id) else { + return ptr::null_mut(); + }; + + let obj_lock = obj_ref.lock().unwrap(); + let class: &Arc = &obj_lock.class; + + *class.mirror.wait() as jclass +} + +unsafe extern "system" fn register_natives( + env: *mut JNIEnv, + clazz: jclass, + methods: *const JNINativeMethod, + n_methods: jint, +) -> jint { + trace!("register_natives"); + let thread = &*get_thread(env); + let gc = thread.gc.read().unwrap(); + let class_id = clazz as u32; + let ReferenceKind::ObjectReference(class_ref) = gc.get(class_id) else { + return JNI_ERR; + }; + let class_name = class_ref.lock().unwrap().class.this_class.clone(); + let class_name = class_name.replace("/", "_"); + + // let boop = JClass::from_raw(clazz); + for i in 0..n_methods as usize { + let native_method = &*methods.add(i); + + // Read the C strings + let name = CStr::from_ptr(native_method.name).to_str().unwrap(); + let signature = CStr::from_ptr(native_method.signature).to_str().unwrap(); + let fn_ptr = native_method.fnPtr; + + let full_name = format!("Java_{class_name}_{name}"); + + if thread.vm.native_methods.contains_key(&full_name) { + warn!("Native method already registered {full_name}") + } + + if full_name.contains("Java_java_lang_Class_desiredAssertionStatus0") { + warn!("MAJOR HACK, figure out what is wrong with desiredAssertionStatus0"); + continue; + } + + thread + .vm + .native_methods + .insert(full_name.to_owned(), fn_ptr); + + println!( + "name:{name}, signature:{signature}, fn_ptr{}", + fn_ptr.is_null() + ) + } + JNI_OK +} + +// ============================================================================ +// JNI FUNCTION STUBS - All unimplemented functions below +// ============================================================================ + +use crate::class_file::{FieldData, FieldRef}; +use crate::objects::object::{Object, ObjectReference, ReferenceKind}; +use crate::{BaseType, FieldType, MethodDescriptor}; +use jni::sys::*; +use log::{error, info, trace, warn}; + +unsafe extern "system" fn define_class( + env: *mut JNIEnv, + name: *const c_char, + loader: jobject, + buf: *const jbyte, + len: jsize, +) -> jclass { + todo!("define_class") +} + +unsafe extern "system" fn find_class(env: *mut JNIEnv, name: *const c_char) -> jclass { + trace!("find_class"); + let name_str = CStr::from_ptr(name).to_str().unwrap(); + let thread = &*get_thread(env); + let class = thread.get_class(name_str).unwrap(); + *class.mirror.wait() as jclass +} + +unsafe extern "system" fn from_reflected_method(env: *mut JNIEnv, method: jobject) -> jmethodID { + todo!("from_reflected_method") +} + +unsafe extern "system" fn from_reflected_field(env: *mut JNIEnv, field: jobject) -> jfieldID { + todo!("from_reflected_field") +} + +unsafe extern "system" fn to_reflected_method( + env: *mut JNIEnv, + cls: jclass, + method_id: jmethodID, + is_static: jboolean, +) -> jobject { + todo!("to_reflected_method") +} + +unsafe extern "system" fn get_superclass(env: *mut JNIEnv, sub: jclass) -> jclass { + warn!("get_superclass"); + let thread = &*get_thread(env); + let Some(sup) = resolve_class(thread, sub) else { + return ptr::null_mut(); + }; + *sup.mirror.wait() as jclass +} + +unsafe extern "system" fn is_assignable_from( + env: *mut JNIEnv, + sub: jclass, + sup: jclass, +) -> jboolean { + warn!("is_assignable_from"); + // Return true if + // The first and second class arguments refer to the same Java class. + // The first class is a subclass of the second class. + // The first class has the second class as one of its interfaces. + let thread = &*get_thread(env); + let Some(first) = resolve_class(thread, sub) else { + return JNI_FALSE; + }; + let Some(second) = resolve_class(thread, sup) else { + return JNI_FALSE; + }; + if first.is_assignable_into(second) { + JNI_TRUE + } else { + JNI_FALSE + } +} + +unsafe extern "system" fn to_reflected_field( + env: *mut JNIEnv, + cls: jclass, + field_id: jfieldID, + is_static: jboolean, +) -> jobject { + todo!("to_reflected_field") +} + +unsafe extern "system" fn throw(env: *mut JNIEnv, obj: jthrowable) -> jint { + todo!("throw") +} + +unsafe extern "system" fn throw_new(env: *mut JNIEnv, clazz: jclass, msg: *const c_char) -> jint { + todo!("throw_new") +} + +unsafe extern "system" fn exception_occurred(env: *mut JNIEnv) -> jthrowable { + todo!("exception_occurred") +} + +unsafe extern "system" fn exception_describe(env: *mut JNIEnv) { + todo!("exception_describe") +} + +unsafe extern "system" fn exception_clear(env: *mut JNIEnv) { + todo!("exception_clear") +} + +unsafe extern "system" fn fatal_error(env: *mut JNIEnv, msg: *const c_char) -> ! { + todo!("fatal_error") +} + +unsafe extern "system" fn push_local_frame(env: *mut JNIEnv, capacity: jint) -> jint { + todo!("push_local_frame") +} + +unsafe extern "system" fn pop_local_frame(env: *mut JNIEnv, result: jobject) -> jobject { + todo!("pop_local_frame") +} + +unsafe extern "system" fn new_global_ref(env: *mut JNIEnv, lobj: jobject) -> jobject { + warn!("todo new_global_ref"); + lobj +} + +unsafe extern "system" fn delete_global_ref(env: *mut JNIEnv, gref: jobject) { + warn!("delete_global_ref") +} + +unsafe extern "system" fn delete_local_ref(env: *mut JNIEnv, obj: jobject) { + warn!("delete_local_ref") +} + +unsafe extern "system" fn is_same_object( + env: *mut JNIEnv, + obj1: jobject, + obj2: jobject, +) -> jboolean { + warn!("is_same_object"); + if obj1 as u32 == obj2 as u32 { + JNI_TRUE + } else { + JNI_FALSE + } +} + +unsafe extern "system" fn new_local_ref(env: *mut JNIEnv, ref_: jobject) -> jobject { + warn!("new_local_ref"); + ref_ +} + +unsafe extern "system" fn ensure_local_capacity(env: *mut JNIEnv, capacity: jint) -> jint { + warn!("ensure_local_capacity"); + JNI_OK +} + +unsafe extern "system" fn alloc_object(env: *mut JNIEnv, clazz: jclass) -> jobject { + todo!("alloc_object") +} + +unsafe extern "system" fn new_object_v( + env: *mut JNIEnv, + clazz: jclass, + method_id: jmethodID, + args: va_list, +) -> jobject { + todo!("new_object_v") +} + +unsafe extern "system" fn new_object_a( + env: *mut JNIEnv, + clazz: jclass, + method_id: jmethodID, + args: *const jvalue, +) -> jobject { + todo!("new_object_a") +} + +unsafe extern "system" fn is_instance_of( + env: *mut JNIEnv, + obj: jobject, + clazz: jclass, +) -> jboolean { + trace!("is_instance_of"); + let thread = &*get_thread(env); + let second = resolve_class(thread, clazz).unwrap(); + let obj = resolve_object(thread, obj).unwrap(); + let first = obj.lock().unwrap().class.clone(); + if first.is_assignable_into(second) { + JNI_TRUE + } else { + JNI_FALSE + } +} + +unsafe extern "system" fn get_method_id( + env: *mut JNIEnv, + clazz: jclass, + name: *const c_char, + sig: *const c_char, +) -> jmethodID { + trace!("get_method_id"); + let thread = &*get_thread(env); + let gc = thread.gc.read().unwrap(); + let loader = thread.loader.lock().unwrap(); + + let name_str = CStr::from_ptr(name).to_str().unwrap(); + let sig_str = CStr::from_ptr(sig).to_str().unwrap(); + + let class_id = clazz as u32; + let ReferenceKind::ObjectReference(class_ref) = gc.get(class_id) else { + return ptr::null_mut(); + }; + + let Some(runtime_class) = loader.class_from_mirror_id(class_id) else { + return ptr::null_mut(); + }; + + let desc = MethodDescriptor::parse(sig_str).unwrap(); + let method = runtime_class.find_method(name_str, &desc).unwrap(); + + // let Value::Reference(Some(ReferenceKind::ObjectReference(class_name_string))) = class_ref.lock().unwrap().get_field("name") else { + // return ptr::null_mut(); + // }; + // class_name_string.lock().unwrap().get_field() + + method as *const _ as jmethodID +} + +unsafe extern "system" fn call_object_method_v( + env: *mut JNIEnv, + obj: jobject, + method_id: jmethodID, + args: va_list, +) -> jobject { + todo!("call_object_method_v") +} + +unsafe extern "system" fn call_object_method_a( + env: *mut JNIEnv, + obj: jobject, + method_id: jmethodID, + args: *const jvalue, +) -> jobject { + todo!("call_object_method_a") +} + +unsafe extern "system" fn call_boolean_method_v( + env: *mut JNIEnv, + obj: jobject, + method_id: jmethodID, + args: va_list, +) -> jboolean { + todo!("call_boolean_method_v") +} + +unsafe extern "system" fn call_boolean_method_a( + env: *mut JNIEnv, + obj: jobject, + method_id: jmethodID, + args: *const jvalue, +) -> jboolean { + todo!("call_boolean_method_a") +} + +unsafe extern "system" fn call_byte_method_v( + env: *mut JNIEnv, + obj: jobject, + method_id: jmethodID, + args: va_list, +) -> jbyte { + todo!("call_byte_method_v") +} + +unsafe extern "system" fn call_byte_method_a( + env: *mut JNIEnv, + obj: jobject, + method_id: jmethodID, + args: *const jvalue, +) -> jbyte { + todo!("call_byte_method_a") +} + +unsafe extern "system" fn call_char_method_v( + env: *mut JNIEnv, + obj: jobject, + method_id: jmethodID, + args: va_list, +) -> jchar { + todo!("call_char_method_v") +} + +unsafe extern "system" fn call_char_method_a( + env: *mut JNIEnv, + obj: jobject, + method_id: jmethodID, + args: *const jvalue, +) -> jchar { + todo!("call_char_method_a") +} + +unsafe extern "system" fn call_short_method_v( + env: *mut JNIEnv, + obj: jobject, + method_id: jmethodID, + args: va_list, +) -> jshort { + todo!("call_short_method_v") +} + +unsafe extern "system" fn call_short_method_a( + env: *mut JNIEnv, + obj: jobject, + method_id: jmethodID, + args: *const jvalue, +) -> jshort { + todo!("call_short_method_a") +} + +unsafe extern "system" fn call_int_method_v( + env: *mut JNIEnv, + obj: jobject, + method_id: jmethodID, + args: va_list, +) -> jint { + todo!("call_int_method_v") +} + +unsafe extern "system" fn call_int_method_a( + env: *mut JNIEnv, + obj: jobject, + method_id: jmethodID, + args: *const jvalue, +) -> jint { + todo!("call_int_method_a") +} + +unsafe extern "system" fn call_long_method_v( + env: *mut JNIEnv, + obj: jobject, + method_id: jmethodID, + args: va_list, +) -> jlong { + todo!("call_long_method_v") +} + +unsafe extern "system" fn call_long_method_a( + env: *mut JNIEnv, + obj: jobject, + method_id: jmethodID, + args: *const jvalue, +) -> jlong { + todo!("call_long_method_a") +} + +unsafe extern "system" fn call_float_method_v( + env: *mut JNIEnv, + obj: jobject, + method_id: jmethodID, + args: va_list, +) -> jfloat { + todo!("call_float_method_v") +} + +unsafe extern "system" fn call_float_method_a( + env: *mut JNIEnv, + obj: jobject, + method_id: jmethodID, + args: *const jvalue, +) -> jfloat { + todo!("call_float_method_a") +} + +unsafe extern "system" fn call_double_method_v( + env: *mut JNIEnv, + obj: jobject, + method_id: jmethodID, + args: va_list, +) -> jdouble { + todo!("call_double_method_v") +} + +unsafe extern "system" fn call_double_method_a( + env: *mut JNIEnv, + obj: jobject, + method_id: jmethodID, + args: *const jvalue, +) -> jdouble { + todo!("call_double_method_a") +} + +unsafe extern "system" fn call_void_method_v( + env: *mut JNIEnv, + obj: jobject, + method_id: jmethodID, + args: va_list, +) { + todo!("call_void_method_v") +} + +unsafe extern "system" fn call_void_method_a( + env: *mut JNIEnv, + obj: jobject, + method_id: jmethodID, + args: *const jvalue, +) { + todo!("call_void_method_a") +} + +unsafe extern "system" fn call_nonvirtual_object_method_v( + env: *mut JNIEnv, + obj: jobject, + clazz: jclass, + method_id: jmethodID, + args: va_list, +) -> jobject { + todo!("call_nonvirtual_object_method_v") +} + +unsafe extern "system" fn call_nonvirtual_object_method_a( + env: *mut JNIEnv, + obj: jobject, + clazz: jclass, + method_id: jmethodID, + args: *const jvalue, +) -> jobject { + todo!("call_nonvirtual_object_method_a") +} + +unsafe extern "system" fn call_nonvirtual_boolean_method_v( + env: *mut JNIEnv, + obj: jobject, + clazz: jclass, + method_id: jmethodID, + args: va_list, +) -> jboolean { + todo!("call_nonvirtual_boolean_method_v") +} + +unsafe extern "system" fn call_nonvirtual_boolean_method_a( + env: *mut JNIEnv, + obj: jobject, + clazz: jclass, + method_id: jmethodID, + args: *const jvalue, +) -> jboolean { + todo!("call_nonvirtual_boolean_method_a") +} + +unsafe extern "system" fn call_nonvirtual_byte_method_v( + env: *mut JNIEnv, + obj: jobject, + clazz: jclass, + method_id: jmethodID, + args: va_list, +) -> jbyte { + todo!("call_nonvirtual_byte_method_v") +} + +unsafe extern "system" fn call_nonvirtual_byte_method_a( + env: *mut JNIEnv, + obj: jobject, + clazz: jclass, + method_id: jmethodID, + args: *const jvalue, +) -> jbyte { + todo!("call_nonvirtual_byte_method_a") +} + +unsafe extern "system" fn call_nonvirtual_char_method_v( + env: *mut JNIEnv, + obj: jobject, + clazz: jclass, + method_id: jmethodID, + args: va_list, +) -> jchar { + todo!("call_nonvirtual_char_method_v") +} + +unsafe extern "system" fn call_nonvirtual_char_method_a( + env: *mut JNIEnv, + obj: jobject, + clazz: jclass, + method_id: jmethodID, + args: *const jvalue, +) -> jchar { + todo!("call_nonvirtual_char_method_a") +} + +unsafe extern "system" fn call_nonvirtual_short_method_v( + env: *mut JNIEnv, + obj: jobject, + clazz: jclass, + method_id: jmethodID, + args: va_list, +) -> jshort { + todo!("call_nonvirtual_short_method_v") +} + +unsafe extern "system" fn call_nonvirtual_short_method_a( + env: *mut JNIEnv, + obj: jobject, + clazz: jclass, + method_id: jmethodID, + args: *const jvalue, +) -> jshort { + todo!("call_nonvirtual_short_method_a") +} + +unsafe extern "system" fn call_nonvirtual_int_method_v( + env: *mut JNIEnv, + obj: jobject, + clazz: jclass, + method_id: jmethodID, + args: va_list, +) -> jint { + todo!("call_nonvirtual_int_method_v") +} + +unsafe extern "system" fn call_nonvirtual_int_method_a( + env: *mut JNIEnv, + obj: jobject, + clazz: jclass, + method_id: jmethodID, + args: *const jvalue, +) -> jint { + todo!("call_nonvirtual_int_method_a") +} + +unsafe extern "system" fn call_nonvirtual_long_method_v( + env: *mut JNIEnv, + obj: jobject, + clazz: jclass, + method_id: jmethodID, + args: va_list, +) -> jlong { + todo!("call_nonvirtual_long_method_v") +} + +unsafe extern "system" fn call_nonvirtual_long_method_a( + env: *mut JNIEnv, + obj: jobject, + clazz: jclass, + method_id: jmethodID, + args: *const jvalue, +) -> jlong { + todo!("call_nonvirtual_long_method_a") +} + +unsafe extern "system" fn call_nonvirtual_float_method_v( + env: *mut JNIEnv, + obj: jobject, + clazz: jclass, + method_id: jmethodID, + args: va_list, +) -> jfloat { + todo!("call_nonvirtual_float_method_v") +} + +unsafe extern "system" fn call_nonvirtual_float_method_a( + env: *mut JNIEnv, + obj: jobject, + clazz: jclass, + method_id: jmethodID, + args: *const jvalue, +) -> jfloat { + todo!("call_nonvirtual_float_method_a") +} + +unsafe extern "system" fn call_nonvirtual_double_method_v( + env: *mut JNIEnv, + obj: jobject, + clazz: jclass, + method_id: jmethodID, + args: va_list, +) -> jdouble { + todo!("call_nonvirtual_double_method_v") +} + +unsafe extern "system" fn call_nonvirtual_double_method_a( + env: *mut JNIEnv, + obj: jobject, + clazz: jclass, + method_id: jmethodID, + args: *const jvalue, +) -> jdouble { + todo!("call_nonvirtual_double_method_a") +} + +unsafe extern "system" fn call_nonvirtual_void_method_v( + env: *mut JNIEnv, + obj: jobject, + clazz: jclass, + method_id: jmethodID, + args: va_list, +) { + todo!("call_nonvirtual_void_method_v") +} + +unsafe extern "system" fn call_nonvirtual_void_method_a( + env: *mut JNIEnv, + obj: jobject, + clazz: jclass, + method_id: jmethodID, + args: *const jvalue, +) { + todo!("call_nonvirtual_void_method_a") +} + +unsafe extern "system" fn get_field_id( + env: *mut JNIEnv, + clazz: jclass, + name: *const c_char, + sig: *const c_char, +) -> jfieldID { + trace!("get_field_id"); + let thread = &*get_thread(env); + let gc = thread.gc.read().unwrap(); + let loader = thread.loader.lock().unwrap(); + + let name_str = CStr::from_ptr(name).to_str().unwrap(); + let sig_str = CStr::from_ptr(sig).to_str().unwrap(); + + let class_id = clazz as u32; + let ReferenceKind::ObjectReference(class_ref) = gc.get(class_id) else { + return ptr::null_mut(); + }; + + let Some(runtime_class) = loader.class_from_mirror_id(class_id) else { + return ptr::null_mut(); + }; + + let field_type = FieldType::parse(sig_str).unwrap(); + let field_ref = runtime_class.find_field(name_str, &field_type).unwrap(); + + // let Value::Reference(Some(ReferenceKind::ObjectReference(class_name_string))) = class_ref.lock().unwrap().get_field("name") else { + // return ptr::null_mut(); + // }; + // class_name_string.lock().unwrap().get_field() + + field_ref as *const _ as jfieldID +} + +unsafe extern "system" fn get_object_field( + env: *mut JNIEnv, + obj: jobject, + field_id: jfieldID, +) -> jobject { + todo!("get_object_field") +} + +unsafe extern "system" fn get_boolean_field( + env: *mut JNIEnv, + obj: jobject, + field_id: jfieldID, +) -> jboolean { + trace!("get_boolean_field"); + let thread = &*get_thread(env); + let object = resolve_object(thread, obj).unwrap(); + let field_data = &*(field_id as *const FieldData); + let field_ref = FieldRef{ + class: object.lock().unwrap().class.this_class.clone(), + name: field_data.name.clone(), + desc: FieldType::Base(BaseType::Boolean), + }; + let val = object.lock().unwrap().get_field(&field_ref); + if let Value::Primitive(Primitive::Boolean(bool)) = val { + if bool { + JNI_TRUE + } else { + JNI_FALSE + } + } else { + JNI_FALSE + } +} + +unsafe extern "system" fn get_byte_field( + env: *mut JNIEnv, + obj: jobject, + field_id: jfieldID, +) -> jbyte { + todo!("get_byte_field") +} + +unsafe extern "system" fn get_char_field( + env: *mut JNIEnv, + obj: jobject, + field_id: jfieldID, +) -> jchar { + todo!("get_char_field") +} + +unsafe extern "system" fn get_short_field( + env: *mut JNIEnv, + obj: jobject, + field_id: jfieldID, +) -> jshort { + todo!("get_short_field") +} + +unsafe extern "system" fn get_int_field( + env: *mut JNIEnv, + obj: jobject, + field_id: jfieldID, +) -> jint { + todo!("get_int_field") +} + +unsafe extern "system" fn get_long_field( + env: *mut JNIEnv, + obj: jobject, + field_id: jfieldID, +) -> jlong { + todo!("get_long_field") +} + +unsafe extern "system" fn get_float_field( + env: *mut JNIEnv, + obj: jobject, + field_id: jfieldID, +) -> jfloat { + todo!("get_float_field") +} + +unsafe extern "system" fn get_double_field( + env: *mut JNIEnv, + obj: jobject, + field_id: jfieldID, +) -> jdouble { + todo!("get_double_field") +} + +unsafe extern "system" fn set_object_field( + env: *mut JNIEnv, + obj: jobject, + field_id: jfieldID, + val: jobject, +) { + todo!("set_object_field") +} + +unsafe extern "system" fn set_boolean_field( + env: *mut JNIEnv, + obj: jobject, + field_id: jfieldID, + val: jboolean, +) { + todo!("set_boolean_field") +} + +unsafe extern "system" fn set_byte_field( + env: *mut JNIEnv, + obj: jobject, + field_id: jfieldID, + val: jbyte, +) { + todo!("set_byte_field") +} + +unsafe extern "system" fn set_char_field( + env: *mut JNIEnv, + obj: jobject, + field_id: jfieldID, + val: jchar, +) { + todo!("set_char_field") +} + +unsafe extern "system" fn set_short_field( + env: *mut JNIEnv, + obj: jobject, + field_id: jfieldID, + val: jshort, +) { + todo!("set_short_field") +} + +unsafe extern "system" fn set_int_field( + env: *mut JNIEnv, + obj: jobject, + field_id: jfieldID, + val: jint, +) { + todo!("set_int_field") +} + +unsafe extern "system" fn set_long_field( + env: *mut JNIEnv, + obj: jobject, + field_id: jfieldID, + val: jlong, +) { + todo!("set_long_field") +} + +unsafe extern "system" fn set_float_field( + env: *mut JNIEnv, + obj: jobject, + field_id: jfieldID, + val: jfloat, +) { + todo!("set_float_field") +} + +unsafe extern "system" fn set_double_field( + env: *mut JNIEnv, + obj: jobject, + field_id: jfieldID, + val: jdouble, +) { + todo!("set_double_field") +} + +unsafe extern "system" fn get_static_method_id( + env: *mut JNIEnv, + clazz: jclass, + name: *const c_char, + sig: *const c_char, +) -> jmethodID { + todo!("get_static_method_id") +} + +unsafe extern "system" fn call_static_object_method_v( + env: *mut JNIEnv, + clazz: jclass, + method_id: jmethodID, + args: va_list, +) -> jobject { + todo!("call_static_object_method_v") +} + +unsafe extern "system" fn call_static_object_method_a( + env: *mut JNIEnv, + clazz: jclass, + method_id: jmethodID, + args: *const jvalue, +) -> jobject { + todo!("call_static_object_method_a") +} + +unsafe extern "system" fn call_static_boolean_method_v( + env: *mut JNIEnv, + clazz: jclass, + method_id: jmethodID, + args: va_list, +) -> jboolean { + todo!("call_static_boolean_method_v") +} + +unsafe extern "system" fn call_static_boolean_method_a( + env: *mut JNIEnv, + clazz: jclass, + method_id: jmethodID, + args: *const jvalue, +) -> jboolean { + todo!("call_static_boolean_method_a") +} + +unsafe extern "system" fn call_static_byte_method_v( + env: *mut JNIEnv, + clazz: jclass, + method_id: jmethodID, + args: va_list, +) -> jbyte { + todo!("call_static_byte_method_v") +} + +unsafe extern "system" fn call_static_byte_method_a( + env: *mut JNIEnv, + clazz: jclass, + method_id: jmethodID, + args: *const jvalue, +) -> jbyte { + todo!("call_static_byte_method_a") +} + +unsafe extern "system" fn call_static_char_method_v( + env: *mut JNIEnv, + clazz: jclass, + method_id: jmethodID, + args: va_list, +) -> jchar { + todo!("call_static_char_method_v") +} + +unsafe extern "system" fn call_static_char_method_a( + env: *mut JNIEnv, + clazz: jclass, + method_id: jmethodID, + args: *const jvalue, +) -> jchar { + todo!("call_static_char_method_a") +} + +unsafe extern "system" fn call_static_short_method_v( + env: *mut JNIEnv, + clazz: jclass, + method_id: jmethodID, + args: va_list, +) -> jshort { + todo!("call_static_short_method_v") +} + +unsafe extern "system" fn call_static_short_method_a( + env: *mut JNIEnv, + clazz: jclass, + method_id: jmethodID, + args: *const jvalue, +) -> jshort { + todo!("call_static_short_method_a") +} + +unsafe extern "system" fn call_static_int_method_v( + env: *mut JNIEnv, + clazz: jclass, + method_id: jmethodID, + args: va_list, +) -> jint { + todo!("call_static_int_method_v") +} + +unsafe extern "system" fn call_static_int_method_a( + env: *mut JNIEnv, + clazz: jclass, + method_id: jmethodID, + args: *const jvalue, +) -> jint { + todo!("call_static_int_method_a") +} + +unsafe extern "system" fn call_static_long_method_v( + env: *mut JNIEnv, + clazz: jclass, + method_id: jmethodID, + args: va_list, +) -> jlong { + todo!("call_static_long_method_v") +} + +unsafe extern "system" fn call_static_long_method_a( + env: *mut JNIEnv, + clazz: jclass, + method_id: jmethodID, + args: *const jvalue, +) -> jlong { + todo!("call_static_long_method_a") +} + +unsafe extern "system" fn call_static_float_method_v( + env: *mut JNIEnv, + clazz: jclass, + method_id: jmethodID, + args: va_list, +) -> jfloat { + todo!("call_static_float_method_v") +} + +unsafe extern "system" fn call_static_float_method_a( + env: *mut JNIEnv, + clazz: jclass, + method_id: jmethodID, + args: *const jvalue, +) -> jfloat { + todo!("call_static_float_method_a") +} + +unsafe extern "system" fn call_static_double_method_v( + env: *mut JNIEnv, + clazz: jclass, + method_id: jmethodID, + args: va_list, +) -> jdouble { + todo!("call_static_double_method_v") +} + +unsafe extern "system" fn call_static_double_method_a( + env: *mut JNIEnv, + clazz: jclass, + method_id: jmethodID, + args: *const jvalue, +) -> jdouble { + todo!("call_static_double_method_a") +} + +unsafe extern "system" fn call_static_void_method_v( + env: *mut JNIEnv, + clazz: jclass, + method_id: jmethodID, + args: va_list, +) { + todo!("call_static_void_method_v") +} + +unsafe extern "system" fn call_static_void_method_a( + env: *mut JNIEnv, + clazz: jclass, + method_id: jmethodID, + args: *const jvalue, +) { + todo!("call_static_void_method_a") +} + +unsafe extern "system" fn get_static_field_id( + env: *mut JNIEnv, + clazz: jclass, + name: *const c_char, + sig: *const c_char, +) -> jfieldID { + todo!("get_static_field_id") +} + +unsafe extern "system" fn get_static_object_field( + env: *mut JNIEnv, + clazz: jclass, + field_id: jfieldID, +) -> jobject { + todo!("get_static_object_field") +} + +unsafe extern "system" fn get_static_boolean_field( + env: *mut JNIEnv, + clazz: jclass, + field_id: jfieldID, +) -> jboolean { + trace!("get_static_boolean_field"); + let thread = &*get_thread(env); + let class = resolve_class(thread, clazz).unwrap(); + let field_ref = &*(field_id as *const FieldData); + let field_again = class.find_field(&field_ref.name, &field_ref.desc).unwrap(); + let val = field_again.value.lock().unwrap().clone(); + if let Some(Value::Primitive(Primitive::Boolean(bool))) = val { + if bool { + JNI_TRUE + } else { + JNI_FALSE + } + } else { + JNI_FALSE + } +} + +unsafe extern "system" fn get_static_byte_field( + env: *mut JNIEnv, + clazz: jclass, + field_id: jfieldID, +) -> jbyte { + todo!("get_static_byte_field") +} + +unsafe extern "system" fn get_static_char_field( + env: *mut JNIEnv, + clazz: jclass, + field_id: jfieldID, +) -> jchar { + todo!("get_static_char_field") +} + +unsafe extern "system" fn get_static_short_field( + env: *mut JNIEnv, + clazz: jclass, + field_id: jfieldID, +) -> jshort { + todo!("get_static_short_field") +} + +unsafe extern "system" fn get_static_int_field( + env: *mut JNIEnv, + clazz: jclass, + field_id: jfieldID, +) -> jint { + todo!("get_static_int_field") +} + +unsafe extern "system" fn get_static_long_field( + env: *mut JNIEnv, + clazz: jclass, + field_id: jfieldID, +) -> jlong { + todo!("get_static_long_field") +} + +unsafe extern "system" fn get_static_float_field( + env: *mut JNIEnv, + clazz: jclass, + field_id: jfieldID, +) -> jfloat { + todo!("get_static_float_field") +} + +unsafe extern "system" fn get_static_double_field( + env: *mut JNIEnv, + clazz: jclass, + field_id: jfieldID, +) -> jdouble { + todo!("get_static_double_field") +} + +unsafe extern "system" fn set_static_object_field( + env: *mut JNIEnv, + clazz: jclass, + field_id: jfieldID, + value: jobject, +) { + todo!("set_static_object_field") +} + +unsafe extern "system" fn set_static_boolean_field( + env: *mut JNIEnv, + clazz: jclass, + field_id: jfieldID, + value: jboolean, +) { + todo!("set_static_boolean_field") +} + +unsafe extern "system" fn set_static_byte_field( + env: *mut JNIEnv, + clazz: jclass, + field_id: jfieldID, + value: jbyte, +) { + todo!("set_static_byte_field") +} + +unsafe extern "system" fn set_static_char_field( + env: *mut JNIEnv, + clazz: jclass, + field_id: jfieldID, + value: jchar, +) { + todo!("set_static_char_field") +} + +unsafe extern "system" fn set_static_short_field( + env: *mut JNIEnv, + clazz: jclass, + field_id: jfieldID, + value: jshort, +) { + todo!("set_static_short_field") +} + +unsafe extern "system" fn set_static_int_field( + env: *mut JNIEnv, + clazz: jclass, + field_id: jfieldID, + value: jint, +) { + todo!("set_static_int_field") +} + +unsafe extern "system" fn set_static_long_field( + env: *mut JNIEnv, + clazz: jclass, + field_id: jfieldID, + value: jlong, +) { + todo!("set_static_long_field") +} + +unsafe extern "system" fn set_static_float_field( + env: *mut JNIEnv, + clazz: jclass, + field_id: jfieldID, + value: jfloat, +) { + todo!("set_static_float_field") +} + +unsafe extern "system" fn set_static_double_field( + env: *mut JNIEnv, + clazz: jclass, + field_id: jfieldID, + value: jdouble, +) { + todo!("set_static_double_field") +} + +unsafe extern "system" fn new_string( + env: *mut JNIEnv, + unicode: *const jchar, + len: jsize, +) -> jstring { + trace!("new_string"); + let thread = &*get_thread(env); + if unicode.is_null() && len > 0 { + return ptr::null_mut(); + } + let chars: &[u16] = std::slice::from_raw_parts(unicode, len as usize); + let str = String::from_utf16(chars).unwrap_or_else(|_| { + // Handle invalid UTF-16 - maybe throw exception? + String::new() + }); + let str_ref = thread.intern_string(&str); // or non-interned path + let str_id = str_ref.lock().unwrap().id; + + str_id as jstring +} + +unsafe extern "system" fn get_string_length(env: *mut JNIEnv, str: jstring) -> jsize { + todo!("get_string_length") +} + +unsafe extern "system" fn get_string_chars( + env: *mut JNIEnv, + str: jstring, + is_copy: *mut jboolean, +) -> *const jchar { + todo!("get_string_chars") +} + +unsafe extern "system" fn release_string_chars( + env: *mut JNIEnv, + str: jstring, + chars: *const jchar, +) { + todo!("release_string_chars") +} + +unsafe extern "system" fn new_string_utf(env: *mut JNIEnv, utf: *const c_char) -> jstring { + trace!("new_string_utf"); + let thread = &*get_thread(env); + // let mut gc = thread.gc.write().unwrap(); + let intern = true; + + let str = CStr::from_ptr(utf).to_str().unwrap(); + info!("{}", str); + let str_ref = if intern { + thread.intern_string(str) + } else { + let Ok(string_class) = thread.get_class("java/lang/String") else { + return ptr::null_mut(); + }; + + let str_ref = thread.gc.write().unwrap().new_string(string_class, str); + str_ref + }; + + let str_id = str_ref.lock().unwrap().id; + + str_id as jstring +} + +unsafe extern "system" fn get_string_utf_length(env: *mut JNIEnv, str: jstring) -> jsize { + todo!("get_string_utf_length") +} + +unsafe extern "system" fn release_string_utf_chars( + env: *mut JNIEnv, + str: jstring, + chars: *const c_char, +) { + todo!("release_string_utf_chars") +} + +unsafe extern "system" fn get_array_length(env: *mut JNIEnv, array: jarray) -> jsize { + todo!("get_array_length") +} + +unsafe extern "system" fn new_object_array( + env: *mut JNIEnv, + len: jsize, + clazz: jclass, + init: jobject, +) -> jobjectArray { + let thread = &*get_thread(env); + let mut gc = thread.gc.write().unwrap(); + let loader = thread.loader.lock().unwrap(); + + let class_id = clazz as u32; + let ReferenceKind::ObjectReference(class_ref) = gc.get(class_id) else { + return ptr::null_mut(); + }; + + let Some(runtime_class) = loader.class_from_mirror_id(class_id) else { + return ptr::null_mut(); + }; + + let ArrayReference::Object(arr_ref) = gc.new_object_array(len) else { + return ptr::null_mut(); + }; + let arr_id = arr_ref.lock().unwrap().id; + + // let Value::Reference(Some(ReferenceKind::ObjectReference(class_name_string))) = class_ref.lock().unwrap().get_field("name") else { + // return ptr::null_mut(); + // }; + // class_name_string.lock().unwrap().get_field() + + arr_id as jobjectArray +} + +unsafe extern "system" fn get_object_array_element( + env: *mut JNIEnv, + array: jobjectArray, + index: jsize, +) -> jobject { + todo!("get_object_array_element") +} + +unsafe extern "system" fn set_object_array_element( + env: *mut JNIEnv, + array: jobjectArray, + index: jsize, + val: jobject, +) { + let thread = &*get_thread(env); + let arr_id = array as u32; + let ReferenceKind::ArrayReference(ArrayReference::Object(arr_ref)) = + thread.gc.read().unwrap().get(arr_id) + else { + panic!("Oop") + }; + let obj_id = val as u32; + let obj_ref = thread.gc.read().unwrap().get(obj_id); + arr_ref.lock().unwrap().set(index, Some(obj_ref)) +} + +unsafe extern "system" fn new_boolean_array(env: *mut JNIEnv, len: jsize) -> jbooleanArray { + todo!("new_boolean_array") +} + +unsafe extern "system" fn new_byte_array(env: *mut JNIEnv, len: jsize) -> jbyteArray { + todo!("new_byte_array") +} + +unsafe extern "system" fn new_char_array(env: *mut JNIEnv, len: jsize) -> jcharArray { + todo!("new_char_array") +} + +unsafe extern "system" fn new_short_array(env: *mut JNIEnv, len: jsize) -> jshortArray { + todo!("new_short_array") +} + +unsafe extern "system" fn new_int_array(env: *mut JNIEnv, len: jsize) -> jintArray { + todo!("new_int_array") +} + +unsafe extern "system" fn new_long_array(env: *mut JNIEnv, len: jsize) -> jlongArray { + todo!("new_long_array") +} + +unsafe extern "system" fn new_float_array(env: *mut JNIEnv, len: jsize) -> jfloatArray { + todo!("new_float_array") +} + +unsafe extern "system" fn new_double_array(env: *mut JNIEnv, len: jsize) -> jdoubleArray { + todo!("new_double_array") +} + +unsafe extern "system" fn get_boolean_array_elements( + env: *mut JNIEnv, + array: jbooleanArray, + is_copy: *mut jboolean, +) -> *mut jboolean { + todo!("get_boolean_array_elements") +} + +unsafe extern "system" fn get_byte_array_elements( + env: *mut JNIEnv, + array: jbyteArray, + is_copy: *mut jboolean, +) -> *mut jbyte { + todo!("get_byte_array_elements") +} + +unsafe extern "system" fn get_char_array_elements( + env: *mut JNIEnv, + array: jcharArray, + is_copy: *mut jboolean, +) -> *mut jchar { + todo!("get_char_array_elements") +} + +unsafe extern "system" fn get_short_array_elements( + env: *mut JNIEnv, + array: jshortArray, + is_copy: *mut jboolean, +) -> *mut jshort { + todo!("get_short_array_elements") +} + +unsafe extern "system" fn get_int_array_elements( + env: *mut JNIEnv, + array: jintArray, + is_copy: *mut jboolean, +) -> *mut jint { + todo!("get_int_array_elements") +} + +unsafe extern "system" fn get_long_array_elements( + env: *mut JNIEnv, + array: jlongArray, + is_copy: *mut jboolean, +) -> *mut jlong { + todo!("get_long_array_elements") +} + +unsafe extern "system" fn get_float_array_elements( + env: *mut JNIEnv, + array: jfloatArray, + is_copy: *mut jboolean, +) -> *mut jfloat { + todo!("get_float_array_elements") +} + +unsafe extern "system" fn get_double_array_elements( + env: *mut JNIEnv, + array: jdoubleArray, + is_copy: *mut jboolean, +) -> *mut jdouble { + todo!("get_double_array_elements") +} + +unsafe extern "system" fn release_boolean_array_elements( + env: *mut JNIEnv, + array: jbooleanArray, + elems: *mut jboolean, + mode: jint, +) { + todo!("release_boolean_array_elements") +} + +unsafe extern "system" fn release_byte_array_elements( + env: *mut JNIEnv, + array: jbyteArray, + elems: *mut jbyte, + mode: jint, +) { + todo!("release_byte_array_elements") +} + +unsafe extern "system" fn release_char_array_elements( + env: *mut JNIEnv, + array: jcharArray, + elems: *mut jchar, + mode: jint, +) { + todo!("release_char_array_elements") +} + +unsafe extern "system" fn release_short_array_elements( + env: *mut JNIEnv, + array: jshortArray, + elems: *mut jshort, + mode: jint, +) { + todo!("release_short_array_elements") +} + +unsafe extern "system" fn release_int_array_elements( + env: *mut JNIEnv, + array: jintArray, + elems: *mut jint, + mode: jint, +) { + todo!("release_int_array_elements") +} + +unsafe extern "system" fn release_long_array_elements( + env: *mut JNIEnv, + array: jlongArray, + elems: *mut jlong, + mode: jint, +) { + todo!("release_long_array_elements") +} + +unsafe extern "system" fn release_float_array_elements( + env: *mut JNIEnv, + array: jfloatArray, + elems: *mut jfloat, + mode: jint, +) { + todo!("release_float_array_elements") +} + +unsafe extern "system" fn release_double_array_elements( + env: *mut JNIEnv, + array: jdoubleArray, + elems: *mut jdouble, + mode: jint, +) { + todo!("release_double_array_elements") +} + +unsafe extern "system" fn get_boolean_array_region( + env: *mut JNIEnv, + array: jbooleanArray, + start: jsize, + len: jsize, + buf: *mut jboolean, +) { + todo!("get_boolean_array_region") +} + +unsafe extern "system" fn get_byte_array_region( + env: *mut JNIEnv, + array: jbyteArray, + start: jsize, + len: jsize, + buf: *mut jbyte, +) { + todo!("get_byte_array_region") +} + +unsafe extern "system" fn get_char_array_region( + env: *mut JNIEnv, + array: jcharArray, + start: jsize, + len: jsize, + buf: *mut jchar, +) { + todo!("get_char_array_region") +} + +unsafe extern "system" fn get_short_array_region( + env: *mut JNIEnv, + array: jshortArray, + start: jsize, + len: jsize, + buf: *mut jshort, +) { + todo!("get_short_array_region") +} + +unsafe extern "system" fn get_int_array_region( + env: *mut JNIEnv, + array: jintArray, + start: jsize, + len: jsize, + buf: *mut jint, +) { + todo!("get_int_array_region") +} + +unsafe extern "system" fn get_long_array_region( + env: *mut JNIEnv, + array: jlongArray, + start: jsize, + len: jsize, + buf: *mut jlong, +) { + todo!("get_long_array_region") +} + +unsafe extern "system" fn get_float_array_region( + env: *mut JNIEnv, + array: jfloatArray, + start: jsize, + len: jsize, + buf: *mut jfloat, +) { + todo!("get_float_array_region") +} + +unsafe extern "system" fn get_double_array_region( + env: *mut JNIEnv, + array: jdoubleArray, + start: jsize, + len: jsize, + buf: *mut jdouble, +) { + todo!("get_double_array_region") +} + +unsafe extern "system" fn set_boolean_array_region( + env: *mut JNIEnv, + array: jbooleanArray, + start: jsize, + len: jsize, + buf: *const jboolean, +) { + todo!("set_boolean_array_region") +} + +unsafe extern "system" fn set_byte_array_region( + env: *mut JNIEnv, + array: jbyteArray, + start: jsize, + len: jsize, + buf: *const jbyte, +) { + todo!("set_byte_array_region") +} + +unsafe extern "system" fn set_char_array_region( + env: *mut JNIEnv, + array: jcharArray, + start: jsize, + len: jsize, + buf: *const jchar, +) { + todo!("set_char_array_region") +} + +unsafe extern "system" fn set_short_array_region( + env: *mut JNIEnv, + array: jshortArray, + start: jsize, + len: jsize, + buf: *const jshort, +) { + todo!("set_short_array_region") +} + +unsafe extern "system" fn set_int_array_region( + env: *mut JNIEnv, + array: jintArray, + start: jsize, + len: jsize, + buf: *const jint, +) { + todo!("set_int_array_region") +} + +unsafe extern "system" fn set_long_array_region( + env: *mut JNIEnv, + array: jlongArray, + start: jsize, + len: jsize, + buf: *const jlong, +) { + todo!("set_long_array_region") +} + +unsafe extern "system" fn set_float_array_region( + env: *mut JNIEnv, + array: jfloatArray, + start: jsize, + len: jsize, + buf: *const jfloat, +) { + todo!("set_float_array_region") +} + +unsafe extern "system" fn set_double_array_region( + env: *mut JNIEnv, + array: jdoubleArray, + start: jsize, + len: jsize, + buf: *const jdouble, +) { + todo!("set_double_array_region") +} + +unsafe extern "system" fn unregister_natives(env: *mut JNIEnv, clazz: jclass) -> jint { + todo!("unregister_natives") +} + +unsafe extern "system" fn monitor_enter(env: *mut JNIEnv, obj: jobject) -> jint { + todo!("monitor_enter") +} + +unsafe extern "system" fn monitor_exit(env: *mut JNIEnv, obj: jobject) -> jint { + todo!("monitor_exit") +} + +unsafe extern "system" fn get_java_vm(env: *mut JNIEnv, vm: *mut *mut JavaVM) -> jint { + todo!("get_java_vm") +} + +unsafe extern "system" fn get_string_region( + env: *mut JNIEnv, + str: jstring, + start: jsize, + len: jsize, + buf: *mut jchar, +) { + todo!("get_string_region") +} + +unsafe extern "system" fn get_string_utf_region( + env: *mut JNIEnv, + str: jstring, + start: jsize, + len: jsize, + buf: *mut c_char, +) { + todo!("get_string_utf_region") +} + +unsafe extern "system" fn get_primitive_array_critical( + env: *mut JNIEnv, + array: jarray, + is_copy: *mut jboolean, +) -> *mut std::ffi::c_void { + todo!("get_primitive_array_critical") +} + +unsafe extern "system" fn release_primitive_array_critical( + env: *mut JNIEnv, + array: jarray, + carray: *mut std::ffi::c_void, + mode: jint, +) { + todo!("release_primitive_array_critical") +} + +unsafe extern "system" fn get_string_critical( + env: *mut JNIEnv, + string: jstring, + is_copy: *mut jboolean, +) -> *const jchar { + todo!("get_string_critical") +} + +unsafe extern "system" fn release_string_critical( + env: *mut JNIEnv, + string: jstring, + cstring: *const jchar, +) { + todo!("release_string_critical") +} + +unsafe extern "system" fn new_weak_global_ref(env: *mut JNIEnv, obj: jobject) -> jweak { + todo!("new_weak_global_ref") +} + +unsafe extern "system" fn delete_weak_global_ref(env: *mut JNIEnv, ref_: jweak) { + todo!("delete_weak_global_ref") +} + +unsafe extern "system" fn exception_check(env: *mut JNIEnv) -> jboolean { + error!("exception_check"); + JNI_FALSE +} + +unsafe extern "system" fn new_direct_byte_buffer( + env: *mut JNIEnv, + address: *mut std::ffi::c_void, + capacity: jlong, +) -> jobject { + todo!("new_direct_byte_buffer") +} + +unsafe extern "system" fn get_direct_buffer_address( + env: *mut JNIEnv, + buf: jobject, +) -> *mut std::ffi::c_void { + todo!("get_direct_buffer_address") +} + +unsafe extern "system" fn get_direct_buffer_capacity(env: *mut JNIEnv, buf: jobject) -> jlong { + todo!("get_direct_buffer_capacity") +} + +unsafe extern "system" fn get_object_ref_type(env: *mut JNIEnv, obj: jobject) -> jobjectRefType { + todo!("get_object_ref_type") +} diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index 1ed672c..0ae04b2 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -15,51 +15,56 @@ //! - [`MethodDescriptor`] - Method signature information //! - [`FieldType`] - Field type information -use crate::attributes::{Attribute, CodeAttribute}; +use crate::value::{OperandStack, Primitive}; +use crate::value::LocalVariables; +use crate::attributes::{ArrayType, Attribute, CodeAttribute}; use crate::class_file::constant_pool::ConstantPoolExt; use crate::class_file::constant_pool::{ConstantPoolError, ConstantPoolGet}; -use crate::class_file::{Bytecode, ClassFile, ConstantPoolEntry, MethodData}; -use crate::objects::array::{ArrayReference, Reference}; -use crate::thread::VmThread; +use crate::class_file::{Bytecode, ClassFile, ConstantPoolEntry, MethodData, MethodRef}; +use crate::objects::array::ArrayReference; +pub use crate::thread::VmThread; use ::jni::sys::{jbyte, jchar, jdouble, jfloat, jint, jlong, jshort}; use deku::{DekuContainerRead, DekuError}; use deku_derive::{DekuRead, DekuWrite}; use env_logger::Builder; use instructions::Ops; -use log::{trace, warn, LevelFilter}; +use itertools::Itertools; +use log::{error, info, trace, warn, LevelFilter}; +use objects::object::ReferenceKind; use std::fmt::{Debug, Display, Formatter}; use std::fs::File; use std::io::Read; -use std::ops::Deref; +use std::ops::{BitAnd, Deref}; use std::sync::{Arc, Mutex}; -use value::{Primitive, Value}; +use value::{Value}; use vm::Vm; mod attributes; mod bimage; mod class; -mod class_file; +pub mod class_file; mod class_loader; mod instructions; mod jni; mod macros; mod native_libraries; -mod objects; +pub mod objects; +mod prim; mod rng; mod thread; -mod value; -mod vm; +pub mod value; +pub mod vm; // const NULL: Value = Value::Reference(None); // include!(concat!(env!("OUT_DIR"), "/bindings.rs")); /// pseudo main pub fn run() { Builder::from_default_env() - .filter_level(LevelFilter::Info) + .filter_level(LevelFilter::Trace) .filter_module("deku", LevelFilter::Warn) - .filter_module("jvm_rs_core::class_file::class_file", LevelFilter::Info) - .filter_module("jvm_rs_core::attributes", LevelFilter::Info) - .filter_module("jvm_rs_core::instructions", LevelFilter::Info) + .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 mut cl = ClassLoader::new().unwrap(); // cl.load_class("org.example.App").expect("TODO: panic message"); @@ -104,7 +109,7 @@ pub fn run() { .unwrap(); println!("{}", clazz);*/ // let pool = clazz.constant_pool; - let mut vm = Vm::new("org/example/Main"); + // let mut vm = Vm::new("org/example/Main"); // println!("{:?}", ops); // println!("{:?}", var_table.local_variable_table); @@ -154,13 +159,14 @@ pub fn run() { /// - Reference to the constant pool for the class /// - The bytecode to execute /// - Reference to the thread executing this frame +#[derive(Clone)] struct Frame { /// Program counter - index of the current bytecode instruction - pc: u16, + pc: i64, /// Operand stack for intermediate values - stack: Vec, + stack: OperandStack, /// Local variables (includes method parameters) - vars: Vec, + vars: LocalVariables, /// Constant pool from the class file pool: Arc>, @@ -169,6 +175,8 @@ struct Frame { /// The thread executing this frame thread: Arc, + // The mod being invoked + method_ref: MethodRef, } impl Display for Frame { @@ -184,13 +192,43 @@ impl Display for Frame { // println!("State:\n\tStack: {:?}\n\tLocals :{:?}\n", self.stack, self.vars) } impl Frame { - fn load_constant(index: u8) {} + fn push(&mut self, val: Value) { + match val.clone() { + Value::Primitive(x) => match x { + Primitive::Boolean(x) => { + let i = x as i32; + self.stack.push(i.into()) + } + Primitive::Char(x) => { + let i = x as i32; + self.stack.push(i.into()) + } + Primitive::Byte(x) => { + let i = x as i32; + self.stack.push(i.into()) + } + Primitive::Short(x) => { + let i = x as i32; + self.stack.push(i.into()) + } + _ => self.stack.push(val), + }, + Value::Reference(_) => self.stack.push(val), + Value::Padding => {panic!("We we pushing a pad?")} + } + } + + // fn load_constant(index: u8) {} fn new( + method_ref: MethodRef, code_attr: CodeAttribute, pool: Arc>, mut locals: Vec, - thread: Arc, + vm: Arc, ) -> Self { + // Get current thread from thread-local storage + let thread = VmThread::current(&vm); + let max_stack = code_attr.max_stack as usize; let max_local = code_attr.max_locals as usize; let bytes = code_attr.code_length.to_be_bytes(); @@ -198,50 +236,90 @@ impl Frame { buf.extend_from_slice(&bytes); buf.extend_from_slice(&code_attr.code.clone()); let (_rest, bytecode) = Bytecode::from_bytes((buf.as_ref(), 0)).unwrap(); - let extend = vec![Value::Reference(None); max_local - locals.len()]; - locals.extend_from_slice(&extend); Frame { pc: 0, - stack: Vec::with_capacity(max_stack), - vars: locals, + stack: OperandStack::with_capacity(max_stack), + vars: LocalVariables::from_args(locals, max_local), pool, bytecode, thread, + method_ref, } } fn execute(&mut self) -> Result, VmError> { let binding = self.bytecode.code.clone(); - let mut ops = binding.iter(); - for op in ops { + loop { + let (offset, op) = self.next().expect("No ops :("); + info!("pre set: {}", self.pc); + self.pc = offset as i64; + info!("post set: {}", self.pc); trace!("Executing Op: {:?}", op); - let result = self.execute_instruction(op); + let result = self.execute_instruction(op.clone()); match result { Ok(ExecutionResult::Return(())) => return Ok(None), Ok(ExecutionResult::ReturnValue(val)) => return Ok(Some(val)), - Ok(_) => { - trace!( - "State:\n\tStack: [{}]\n\tLocals: [{}]\n", - self.stack - .iter() - .map(|v| v.to_string()) - .collect::>() - .join(", "), - self.vars - .iter() - .map(|v| v.to_string()) - .collect::>() - .join(", ") - ) + Ok(ExecutionResult::Advance(offset)) => { + info!("pre offset: {}", self.pc); + self.pc += offset as i64; + info!("post offset: {}", self.pc); } + Ok(_) => self.pc += 1, Err(x) => { + let objs = self.thread.gc.read().unwrap() + .objects + .iter() + .map(|(x, y)| format!("{x} : {y}")) + .collect::>(); + let len = objs.len().clone(); + error!("Error in method: {:#?}", self.method_ref); + error!("Heap dump: len: {len} objs:\n{objs:#?}"); panic!("Mission failed, we'll get em next time:\n{x}") } } + trace!( + "State:\n\tStack: [{}]\n\tLocals: [{}]\n", + self.stack + .iter() + .map(|v| v.to_string()) + .collect::>() + .join(", "), + self.vars + .iter() + .map(|v| v.to_string()) + .collect::>() + .join(", ") + ); } Err(VmError::ExecutionError) } + + fn next(&mut self) -> Option<(u16, Ops)> { + self.bytecode + .code + .iter() + .find(|(offset, op)| *offset as i64 >= self.pc) + .map(|(op)| op.clone()) + } } +// impl IntoIterator for Frame { +// type Item = (); +// type IntoIter = (); +// +// fn into_iter(self) -> Self::IntoIter { +// self.bytecode.code.iter() +// } +// } + +/// Unique identifier for a VM thread. +/// +/// Each VmThread is assigned a unique ThreadId when created. This ID is used to: +/// - Track threads in the VM's thread registry (DashMap) +/// - Identify the current thread via thread-local storage +/// - Support future multi-threading when Java threads map to OS threads +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] +pub struct ThreadId(pub u64); + /// Represents JVM primitive types used in field and method descriptors. /// /// Each variant corresponds to a single-character type code used in the JVM: @@ -343,6 +421,10 @@ impl MethodDescriptor { } }) } + + pub fn param_string(&self) -> String { + self.parameters.iter().join("") + } } impl Display for BaseType { @@ -405,16 +487,25 @@ pub enum FieldType { enum ExecutionResult { Continue, + Advance(i16), Return(()), ReturnValue(Value), } #[derive(Debug)] -enum VmError { +pub enum VmError { ConstantPoolError(String), StackError(String), + InvariantError(String), DekuError(DekuError), LoaderError(String), ExecutionError, + NativeError(String), +} + +impl VmError { + pub fn stack_not_int() -> Self { + Self::InvariantError("Value on stack was not an int".to_string()) + } } impl Display for VmError { @@ -422,9 +513,11 @@ impl Display for VmError { match self { VmError::ConstantPoolError(msg) => write!(f, "Constant pool error: {}", msg), VmError::StackError(msg) => write!(f, "Stack error: {}", msg), + VmError::InvariantError(msg) => write!(f, "Invariant error: {}", msg), VmError::DekuError(err) => write!(f, "Deku error: {}", err), VmError::LoaderError(msg) => write!(f, "Loader error: {}", msg), VmError::ExecutionError => write!(f, "Execution error"), + VmError::NativeError(msg) => write!(f, "Native error {msg}"), } } } @@ -441,8 +534,16 @@ impl From for VmError { } impl Frame { - fn execute_instruction(&mut self, op: &Ops) -> Result { + fn pop(&mut self) -> Result{ + self.stack.pop() + } + fn execute_instruction(&mut self, op: Ops) -> Result { match op { + Ops::nop => { + // TODO Should nop have any side effects? + warn!("TODO Should nop have any side effects? Investigate."); + Ok(ExecutionResult::Continue) + } // Constants Ops::aconst_null => { self.stack.push(Value::NULL); @@ -450,27 +551,27 @@ impl Frame { } Ops::iconst_m1 => { - self.stack.push(Value::Primitive(Primitive::Int(-1))); + self.stack.push(Value::from(-1i32)); Ok(ExecutionResult::Continue) } Ops::iconst_0 => { - self.stack.push(Value::Primitive(Primitive::Int(0))); + self.stack.push(Value::from(0i32)); Ok(ExecutionResult::Continue) } Ops::iconst_1 => { - self.stack.push(Value::Primitive(Primitive::Int(1))); + self.stack.push(Value::from(1i32)); Ok(ExecutionResult::Continue) } Ops::iconst_2 => { - self.stack.push(Value::Primitive(Primitive::Int(2))); + self.stack.push(Value::from(2i32)); Ok(ExecutionResult::Continue) } Ops::iconst_3 => { - self.stack.push(Value::Primitive(Primitive::Int(3))); + self.stack.push(Value::from(3i32)); Ok(ExecutionResult::Continue) } @@ -483,55 +584,47 @@ impl Frame { self.stack.push(5.into()); Ok(ExecutionResult::Continue) } - + Ops::lconst_0 => { + self.stack.push(0i64.into()); + Ok(ExecutionResult::Continue) + } + Ops::lconst_1 => { + self.stack.push(1i64.into()); + Ok(ExecutionResult::Continue) + } + Ops::fconst_0 => { + self.stack.push(0f32.into()); + Ok(ExecutionResult::Continue) + } + Ops::fconst_1 => { + self.stack.push(1f32.into()); + Ok(ExecutionResult::Continue) + } + Ops::fconst_2 => { + self.stack.push(2f32.into()); + Ok(ExecutionResult::Continue) + } + Ops::dconst_0 => { + self.stack.push(0f64.into()); + Ok(ExecutionResult::Continue) + } + Ops::dconst_1 => { + self.stack.push(1f64.into()); + Ok(ExecutionResult::Continue) + } Ops::bipush(byte) => { - self.stack.push((*byte as i32).into()); + self.stack.push((byte as i32).into()); Ok(ExecutionResult::Continue) } - Ops::ldc(index) => { - let thing = self.pool.get_constant(index.to_owned() as u16)?; - trace!("\tLoading constant: {}", thing); - let resolved: Option = match thing { - ConstantPoolEntry::Utf8(x) => { - warn!("{:?}", String::from_utf8(x.bytes.clone())); - warn!("Utf8 loading not yet implemented"); - None - } - ConstantPoolEntry::Integer(x) => Some(Value::from(*x)), - ConstantPoolEntry::Float(x) => Some(Value::from(*x)), - ConstantPoolEntry::Class(x) => None, - ConstantPoolEntry::String(x) => { - warn!("String loading not yet implemented"); - None - } + Ops::sipush(short) => { + self.stack.push((short as i32).into()); + Ok(ExecutionResult::Continue) + } + Ops::ldc(index) => self.load_constant(index as u16), + Ops::ldc_w(index) => self.load_constant(index), - ConstantPoolEntry::MethodHandle(x) => { - warn!("Method handle loading not yet implemented"); - None - } - ConstantPoolEntry::MethodType(x) => { - warn!("Method type loading not yet implemented"); - None - } - ConstantPoolEntry::Dynamic(x) => { - warn!("Dynamic loading not yet implemented"); - None - } - _ => { - panic!( - "Cannot load constant, is not of loadable type: {:?}. ", - thing - ); - None - } - }; - if let Some(x) = resolved { - self.stack.push(x); - }; - Ok(ExecutionResult::Continue) - } Ops::ldc2_w(index) => { - let val = self.pool.get_constant(*index)?; + let val = self.pool.get_constant(index)?; trace!("\tLoading constant: {}", val); let resolved = match val { ConstantPoolEntry::Double(x) => Some(Value::from(*x)), @@ -540,7 +633,8 @@ impl Frame { }; if let Some(x) = resolved { self.stack.push(x); - self.stack.push(Value::Reference(None)); + // on second thoughts, i dont think that's right + // self.stack.push(Value::Reference(None)); }; Ok(ExecutionResult::Continue) } @@ -549,7 +643,7 @@ impl Frame { //iload Ops::iload(index) => { - load!(self, i, *index as usize) + load!(self, i, index as usize) } Ops::iload_0 => { load!(self, i, 0) @@ -564,7 +658,7 @@ impl Frame { load!(self, i, 3) } Ops::lload(index) => { - load!(self, l, *index as usize) + load!(self, l, index as usize) } Ops::lload_0 => { load!(self, l, 0) @@ -579,7 +673,7 @@ impl Frame { load!(self, l, 3) } Ops::fload(index) => { - load!(self, f, *index as usize) + load!(self, f, index as usize) } Ops::fload_0 => { load!(self, f, 0) @@ -594,7 +688,7 @@ impl Frame { load!(self, f, 3) } Ops::dload(index) => { - load!(self, d, *index as usize) + load!(self, d, index as usize) } Ops::dload_0 => { load!(self, d, 0) @@ -609,7 +703,7 @@ impl Frame { load!(self, d, 3) } Ops::aload(index) => { - load!(self, a, *index as usize) + load!(self, a, index as usize) } Ops::aload_0 => { load!(self, a, 0) @@ -623,10 +717,70 @@ impl Frame { Ops::aload_3 => { load!(self, a, 3) } + Ops::iaload => { + let Value::Primitive(Primitive::Int(index)) = + self.stack.pop().expect("value on stack") + else { + panic!("index on stack was not int") + }; + let Value::Reference(Some(ReferenceKind::ArrayReference(ArrayReference::Int(arr)))) = + self.stack.pop().expect("value on stack") + else { + panic!("Reference not on stack or not an int array") + }; + let i = arr.lock().unwrap().get(index); + self.stack.push(Value::from(i)); + Ok(ExecutionResult::Continue) + } + Ops::laload => { + todo!("long array load") + } + Ops::faload => { + todo!("float array load") + } + Ops::daload => { + todo!("double array load") + } + Ops::aaload => { + let Value::Primitive(Primitive::Int(index)) = + self.stack.pop().expect("value on stack") + else { + panic!("index on stack was not int") + }; + let Value::Reference(Some(ReferenceKind::ArrayReference(ArrayReference::Object( + arr, + )))) = self.stack.pop().expect("value on stack") + else { + panic!("Reference not on stack or not a reference array") + }; + let reference = arr.lock().unwrap().get(index); + self.stack.push(Value::from(reference)); + Ok(ExecutionResult::Continue) + } + Ops::baload => { + todo!("boolean array load") + } + Ops::caload => { + let Value::Primitive(Primitive::Int(index)) = self.pop()? else { + panic!("index on stack was not int") + }; + let Value::Reference(Some(ReferenceKind::ArrayReference(ArrayReference::Char( + arr, + )))) = self.pop()? + else { + panic!("Reference not on stack or not a char array") + }; + let c = arr.lock().unwrap().get(index); + self.push(Value::from(c)); + Ok(ExecutionResult::Continue) + } + Ops::saload => { + todo!("short array load") + } // store Ops::istore(index) => { - store!(self, i, *index as usize) + store!(self, i, index as usize) } Ops::istore_0 => { store!(self, i, 0) @@ -642,7 +796,7 @@ impl Frame { } Ops::fstore(index) => { - store!(self, f, *index as usize) + store!(self, f, index as usize) } Ops::fstore_0 => { store!(self, f, 0) @@ -658,7 +812,7 @@ impl Frame { } Ops::dstore(index) => { - store!(self, d, *index as usize) + store!(self, d, index as usize) } Ops::dstore_0 => { store!(self, d, 0) @@ -674,7 +828,7 @@ impl Frame { } Ops::lstore(index) => { - store!(self, l, *index as usize) + store!(self, l, index as usize) } Ops::lstore_0 => { store!(self, l, 0) @@ -690,7 +844,7 @@ impl Frame { } Ops::astore(index) => { - store!(self, a, *index as usize) + store!(self, a, index as usize) } Ops::astore_0 => { store!(self, a, 0) @@ -704,138 +858,438 @@ impl Frame { Ops::astore_3 => { store!(self, a, 3) } - Ops::iastore => { - let Value::Primitive(Primitive::Int(value)) = - self.stack.pop().expect("value on stack") - else { - panic!("Value on stack was not int") + Ops::iastore => array_store!(self, Int, Int), + Ops::lastore => array_store!(self, Long, Long), + Ops::fastore => array_store!(self, Float, Float), + Ops::dastore => array_store!(self, Double, Double), + Ops::aastore => { + let Value::Reference((value)) = self.pop()? else { + panic!("Value on stack was not ref") }; - let Value::Primitive(Primitive::Int(index)) = - self.stack.pop().expect("value on stack") - else { + let Value::Primitive(Primitive::Int(index)) = self.pop()? else { panic!("index on stack was not int") }; - let Value::Reference(Some(Reference::ArrayReference(ArrayReference::Primitive( + let Value::Reference(Some(ReferenceKind::ArrayReference(ArrayReference::Object( arr, - )))) = self.stack.pop().expect("value on stack") + )))) = self.pop()? else { - panic!("Reference not on stack") + panic!("Reference not on stack or not an int array") }; - let (id, array) = *arr.lock().unwrap(); - + arr.lock().unwrap().set(index, value); Ok(ExecutionResult::Continue) } + Ops::bastore => array_store_cast!(self, Byte, i8), + Ops::castore => array_store_cast!(self, Char, jchar), + Ops::sastore => array_store_cast!(self, Short, i16), //Stack + Ops::pop => { + let v1 = self.pop()?; + if v1.is_wide() { + Err(VmError::InvariantError("Op:pop on single wide value".to_string())) + } else { Ok(ExecutionResult::Continue) } + } + Ops::pop2 => { + let peek = self.stack.peek()?; + if peek.is_wide() { + let _v1 = self.pop()?; + Ok(ExecutionResult::Continue) + } else { + let _v2 = self.pop()?; + let v1 = self.pop()?; + if v1.is_wide() { + Err(VmError::InvariantError("Op:pop2 popped a 2wide on second pop".to_string())) + } else { Ok(ExecutionResult::Continue) } + } + } + Ops::dup_x1 => { + let v1 = self.pop()?; + let v2 = self.pop()?; + if v1.is_wide() || v2.is_wide() { + Err(VmError::InvariantError("dup_x1 operated on 2 wide value".to_string())) + } else { + self.push(v1.clone()); + self.push(v2); + self.push(v1); + Ok(ExecutionResult::Continue) + } + } + Ops::dup_x2 => { + let v1 = self.pop()?; + let v2 = self.pop()?; + if v1.is_wide() { + Err(VmError::InvariantError("dup_x2 operated on 1st 2 wide value".to_string())) + } else if v2.is_wide() { + self.push(v1.clone()); + self.push(v2); + self.push(v1); + Ok(ExecutionResult::Continue) + } else if self.stack.peek()?.is_wide() { + Err(VmError::InvariantError("dup_x2 operated on 3rd 2 wide value".to_string())) + } + else { + let v3 = self.pop()?; + self.push(v1.clone()); + self.push(v3); + self.push(v2); + self.push(v1); + Ok(ExecutionResult::Continue) + } + } Ops::dup => { - if let Some(value) = self.stack.last() { + if let Ok(value) = self.stack.peek() { self.stack.push(value.clone()); Ok(ExecutionResult::Continue) } else { Err(VmError::StackError("Stack underflow".to_string())) } } + Ops::dup2 => { + let v1 = self.pop()?; + if v1.is_wide() { + self.push(v1.clone()); + self.push(v1); + Ok(ExecutionResult::Continue) + } else if self.stack.peek()?.is_wide() { + Err(VmError::InvariantError("dup2 operated on 2nd, 2 wide value".to_string())) + } else { + let v2 = self.pop()?; + self.push(v2.clone()); + self.push(v1.clone()); + self.push(v2); + self.push(v1); + Ok(ExecutionResult::Continue) + } + } + Ops::dup2_x1 => { + let v1 = self.pop()?; + if v1.is_wide() { + // Form 2: v2, v1 → v1, v2, v1 + // v1 is category 2, v2 must be category 1 + let v2 = self.pop()?; + if v2.is_wide() { + Err(VmError::InvariantError("dup2_x1 form 2: v2 must be category 1".to_string())) + } else { + self.push(v1.clone()); + self.push(v2); + self.push(v1); + Ok(ExecutionResult::Continue) + } + } else { + // Form 1: v3, v2, v1 → v2, v1, v3, v2, v1 + // all must be category 1 + let v2 = self.pop()?; + if v2.is_wide() { + Err(VmError::InvariantError("dup2_x1 form 1: v2 must be category 1".to_string())) + } else { + let v3 = self.pop()?; + if v3.is_wide() { + Err(VmError::InvariantError("dup2_x1 form 1: v3 must be category 1".to_string())) + } else { + self.push(v2.clone()); + self.push(v1.clone()); + self.push(v3); + self.push(v2); + self.push(v1); + Ok(ExecutionResult::Continue) + } + } + } + } + Ops::dup2_x2 => { + let v1 = self.pop()?; + let v2 = self.pop()?; + + if v1.is_wide() { + if v2.is_wide() { + // Form 4: v2, v1 → v1, v2, v1 (both category 2) + self.push(v1.clone()); + self.push(v2); + self.push(v1); + Ok(ExecutionResult::Continue) + } else { + // Form 2: v3, v2, v1 → v1, v3, v2, v1 + // v1 is category 2, v2 and v3 are category 1 + let v3 = self.pop()?; + if v3.is_wide() { + Err(VmError::InvariantError("dup2_x2 form 2: v3 must be category 1".to_string())) + } else { + self.push(v1.clone()); + self.push(v3); + self.push(v2); + self.push(v1); + Ok(ExecutionResult::Continue) + } + } + } else if v2.is_wide() { + // v1 category 1, v2 category 2 - no valid form for this + Err(VmError::InvariantError("dup2_x2: invalid - v1 cat1, v2 cat2".to_string())) + } else { + // v1 and v2 are both category 1 + let v3 = self.pop()?; + if v3.is_wide() { + // Form 3: v3, v2, v1 → v2, v1, v3, v2, v1 + // v3 is category 2 + self.push(v2.clone()); + self.push(v1.clone()); + self.push(v3); + self.push(v2); + self.push(v1); + Ok(ExecutionResult::Continue) + } else { + // Form 1: v4, v3, v2, v1 → v2, v1, v4, v3, v2, v1 + // all category 1 + let v4 = self.pop()?; + if v4.is_wide() { + Err(VmError::InvariantError("dup2_x2 form 1: v4 must be category 1".to_string())) + } else { + self.push(v2.clone()); + self.push(v1.clone()); + self.push(v4); + self.push(v3); + self.push(v2); + self.push(v1); + Ok(ExecutionResult::Continue) + } + } + } + } + Ops::swap => { + let v1 = self.pop()?; + let v2 = self.pop()?; + if v1.is_wide() || v2.is_wide() { + Err(VmError::InvariantError("swap operated on 2 wide value".to_string())) + } else { + self.push(v1); + self.push(v2); + Ok(ExecutionResult::Continue) + } + } // Math - Ops::dadd => { - let value1 = self.stack.pop().expect("Stack must have value"); - let value2 = self.stack.pop().expect("Stack must have value"); - if let ( - Value::Primitive(Primitive::Double(double1)), - Value::Primitive(Primitive::Double(double2)), - ) = (value1.clone(), value2.clone()) - { - self.stack.push(Value::from(double1 + double2)); + // Addition + Ops::iadd => binary_op!(self, Int, |a, b| a.wrapping_add(b)), + Ops::ladd => binary_op!(self, Long, |a, b| a.wrapping_add(b)), + Ops::fadd => binary_op!(self, Float, |a, b| a + b), + Ops::dadd => binary_op!(self, Double, |a, b| a + b), + + // Subtraction + Ops::isub => binary_op!(self, Int, |a, b| a.wrapping_sub(b)), + Ops::lsub => binary_op!(self, Long, |a, b| a.wrapping_sub(b)), + Ops::fsub => binary_op!(self, Float, |a, b| a - b), + Ops::dsub => binary_op!(self, Double, |a, b| a - b), + + // Multiplication + Ops::imul => binary_op!(self, Int, |a, b| a.wrapping_mul(b)), + Ops::lmul => binary_op!(self, Long, |a, b| a.wrapping_mul(b)), + Ops::fmul => binary_op!(self, Float, |a, b| a * b), + Ops::dmul => binary_op!(self, Double, |a, b| a * b), + + // Division + Ops::idiv => int_div_rem!(self, Int, i32, /), + Ops::ldiv => int_div_rem!(self, Long, i64, /), + Ops::fdiv => binary_op!(self, Float, |a, b| a / b), + Ops::ddiv => binary_op!(self, Double, |a, b| a / b), + + // Remainder + Ops::irem => int_div_rem!(self, Int, i32, %), + Ops::lrem => int_div_rem!(self, Long, i64, %), + Ops::frem => binary_op!(self, Float, |a, b| a % b), + Ops::drem => binary_op!(self, Double, |a, b| a % b), + + // Negation + Ops::ineg => unary_op!(self, Int, |v| v.wrapping_neg()), + Ops::lneg => unary_op!(self, Long, |v| v.wrapping_neg()), + Ops::fneg => unary_op!(self, Float, |v| -v), + Ops::dneg => unary_op!(self, Double, |v| -v), + + // Shifts + Ops::ishl => shift_op!(self, Int, 0x1f, |v, s| v << s), + Ops::lshl => shift_op!(self, Long, 0x3f, |v, s| v << s), + Ops::ishr => shift_op!(self, Int, 0x1f, |v, s| v >> s), + Ops::lshr => shift_op!(self, Long, 0x3f, |v, s| v >> s), + Ops::iushr => shift_op!(self, Int, 0x1f, |v, s| ((v as u32) >> s) as i32), + Ops::lushr => shift_op!(self, Long, 0x3f, |v, s| ((v as u64) >> s) as i64), + + // Bitwise + Ops::iand => binary_op!(self, Int, |a, b| a & b), + Ops::land => binary_op!(self, Long, |a, b| a & b), + Ops::ior => binary_op!(self, Int, |a, b| a | b), + Ops::lor => binary_op!(self, Long, |a, b| a | b), + Ops::ixor => binary_op!(self, Int, |a, b| a ^ b), + Ops::lxor => binary_op!(self, Long, |a, b| a ^ b), + + Ops::iinc(index, increment) => { + if let Value::Primitive(Primitive::Int(int)) = self.vars.get(index as usize) { + let new_val = int + increment as i32; + self.vars.set(index as usize, new_val.into()); Ok(ExecutionResult::Continue) } else { - Err(VmError::StackError(format!( - "{value1:?} or {value2:?} was not a double" - ))) + Err(VmError::InvariantError("iinc requires integer value".to_string())) } } // Conversions - Ops::i2l => { - if let Value::Primitive(Primitive::Int(int)) = - self.stack.pop().expect("Stack must have value") - { - let long: i64 = int.into(); - self.stack.push(Value::from(long)); - Ok(ExecutionResult::Continue) - } else { - Err(VmError::StackError("Popped value was not int".to_string())) - } - } - Ops::i2f => { - todo!("int to float cast") - } - Ops::i2d => { - if let Value::Primitive(Primitive::Int(int)) = - self.stack.pop().expect("Stack must have value") - { - let double: f64 = int.into(); - self.stack.push(Value::from(double)); - Ok(ExecutionResult::Continue) - } else { - Err(VmError::StackError("Popped value was not int".to_string())) - } - } - Ops::l2i => { - todo!("long to int cast") - } - Ops::l2f => { - todo!("long to float cast") - } - Ops::l2d => { - todo!("long to double cast") - } - Ops::f2i => { - todo!("float to int cast") - } - Ops::f2l => { - todo!("float to long cast") - } - Ops::f2d => { - if let Value::Primitive(Primitive::Float(float)) = - self.stack.pop().expect("Stack must have value") - { - let double: f64 = float.into(); - self.stack.push(Value::from(double)); - Ok(ExecutionResult::Continue) - } else { - Err(VmError::StackError( - "Popped value was not float".to_string(), - )) - } - } - Ops::d2i => { - todo!("double to int cast") - } - Ops::d2l => { - if let Value::Primitive(Primitive::Double(double)) = - self.stack.pop().expect("Stack must have value") - { - let long: i64 = double as i64; - self.stack.push(Value::from(long)); - Ok(ExecutionResult::Continue) - } else { - Err(VmError::StackError("Popped value was not int".to_string())) - } - } - Ops::d2f => { - todo!("double to float cast") - } - Ops::i2b => { - todo!("int to byte cast") - } - Ops::i2c => { - todo!("int to char cast") - } - Ops::i2s => { - todo!("int to short cast") - } + Ops::i2l => convert_simple!(self, Int, i64), + Ops::i2f => convert_simple!(self, Int, f32), + Ops::i2d => convert_simple!(self, Int, f64), + Ops::l2i => convert_simple!(self, Long, i32), + Ops::l2f => convert_simple!(self, Long, f32), + Ops::l2d => convert_simple!(self, Long, f64), + Ops::f2i => convert_float_to_int!(self, Float, f32, i32), + Ops::f2l => convert_float_to_int!(self, Float, f32, i64), + Ops::f2d => convert_simple!(self, Float, f64), + + Ops::d2i => convert_float_to_int!(self, Double, f64, i32), + Ops::d2l => convert_float_to_int!(self, Double, f64, i64), + Ops::d2f => convert_simple!(self, Double, f32), + + Ops::i2b => convert_int_narrow!(self, i8), + Ops::i2c => convert_int_narrow!(self, u16), + Ops::i2s => convert_int_narrow!(self, i16), + // Comparisons + Ops::lcmp => { + let Value::Primitive(Primitive::Long(v2)) = self.pop()? else { + return Err(VmError::StackError("Expected long".into())); + }; + let Value::Primitive(Primitive::Long(v1)) = self.pop()? else { + return Err(VmError::StackError("Expected long".into())); + }; + + let result: i32 = match v1.cmp(&v2) { + std::cmp::Ordering::Greater => 1, + std::cmp::Ordering::Equal => 0, + std::cmp::Ordering::Less => -1, + }; + + self.stack.push(Value::from(result)); + Ok(ExecutionResult::Continue) + } + Ops::fcmpl => float_cmp!(self, Float, -1), + Ops::fcmpg => float_cmp!(self, Float, 1), + Ops::dcmpl => float_cmp!(self, Double, -1), + Ops::dcmpg => float_cmp!(self, Double, 1), + + Ops::ifeq(offset) => if_int_zero!(self, offset, ==), + Ops::ifne(offset) => if_int_zero!(self, offset, !=), + Ops::iflt(offset) => if_int_zero!(self, offset, <), + Ops::ifge(offset) => if_int_zero!(self, offset, >=), + Ops::ifgt(offset) => if_int_zero!(self, offset, >), + Ops::ifle(offset) => if_int_zero!(self, offset, <=), + Ops::if_icmpeq(offset) => if_int_cmp!(self, offset, ==), + Ops::if_icmpne(offset) => if_int_cmp!(self, offset, !=), + Ops::if_icmplt(offset) => if_int_cmp!(self, offset, <), + Ops::if_icmpge(offset) => if_int_cmp!(self, offset, >=), + Ops::if_icmpgt(offset) => if_int_cmp!(self, offset, >), + Ops::if_icmple(offset) => if_int_cmp!(self, offset, <=), + + Ops::if_acmpeq(offset) => { + if let Value::Reference(Some(value2)) = self.pop()? && + let Value::Reference(Some(value1)) = self.pop()? + { + if value1.id() == value2.id() { + Ok(ExecutionResult::Advance(offset)) + } else { + Ok(ExecutionResult::Continue) + } + } else { + Err(VmError::stack_not_int()) + } + } + Ops::if_acmpne(offset) => { + if let Value::Reference(Some(value2)) = self.pop()? && + let Value::Reference(Some(value1)) = self.pop()? + { + if value1.id() != value2.id() { + Ok(ExecutionResult::Advance(offset)) + } else { + Ok(ExecutionResult::Continue) + } + } else { + Err(VmError::stack_not_int()) + } + } // Control + Ops::goto(offset) => { + Ok(ExecutionResult::Advance(offset)) + }, + Ops::jsr(_) => { + todo!("jsr") + } + Ops::ret(_) => { + todo!("ret") + } + Ops::tableswitch => { + todo!("tableswitch") + } + Ops::lookupswitch => { + todo!("lookupswitch") + } + Ops::ireturn => { + let x: i32 = match self.pop()? { + Value::Primitive(Primitive::Int(v)) => v, + Value::Primitive(Primitive::Boolean(v)) => { + if v { + 1 + } else { + 0 + } + } + Value::Primitive(Primitive::Byte(v)) => v as i32, + Value::Primitive(Primitive::Char(v)) => v as i32, + Value::Primitive(Primitive::Short(v)) => v as i32, + _ => { + return Err(VmError::InvariantError( + "ireturn requires integer-compatible value".to_owned(), + )) + } + }; + + match &self.method_ref.desc.return_type { + Some(FieldType::Base(base_type)) => match base_type { + BaseType::Boolean => Ok(ExecutionResult::ReturnValue((x != 0).into())), + BaseType::Byte => Ok(ExecutionResult::ReturnValue((x as i8).into())), + BaseType::Char => Ok(ExecutionResult::ReturnValue((x as u16).into())), + BaseType::Short => Ok(ExecutionResult::ReturnValue((x as i16).into())), + BaseType::Int => Ok(ExecutionResult::ReturnValue(x.into())), + _ => Err(VmError::InvariantError( + "wrong return instruction for method".to_owned(), + )), + }, + _ => Err(VmError::InvariantError( + "wrong return instruction for method".to_owned(), + )), + } + } + Ops::lreturn => { + let val = self.pop()?; + match val { + Value::Primitive(Primitive::Long(_)) => Ok(ExecutionResult::ReturnValue(val)), + _ => Err(VmError::StackError("Expected reference".into())), + } + } + Ops::freturn => { + let val = self.pop()?; + match val { + Value::Primitive(Primitive::Float(_)) => Ok(ExecutionResult::ReturnValue(val)), + _ => Err(VmError::StackError("Expected reference".into())), + } + } + Ops::dreturn => { + let val = self.pop()?; + match val { + Value::Primitive(Primitive::Double(_)) => Ok(ExecutionResult::ReturnValue(val)), + _ => Err(VmError::StackError("Expected reference".into())), + } + } + Ops::areturn => { + let val = self.pop()?; + match val { + Value::Reference(_) => Ok(ExecutionResult::ReturnValue(val)), + _ => Err(VmError::StackError("Expected reference".into())), + } + } Ops::return_void => Ok(ExecutionResult::Return(())), // References @@ -843,15 +1297,15 @@ impl Frame { // get static field // can init the field Ops::getstatic(index) => { - let field_ref = self.pool.resolve_field(*index)?; + let field_ref = self.pool.resolve_field(index)?; println!("Getting static field {field_ref:?}"); let init_class = self .thread - .get_or_resolve_class(&field_ref.class, self.thread.clone()) + .get_or_resolve_class(&field_ref.class) .expect("TO hecken work"); let result = init_class - .find_field(&field_ref.name, field_ref.desc) + .find_field(&field_ref.name, &field_ref.desc) .expect("TO hecken work"); let constant = result .value @@ -859,20 +1313,20 @@ impl Frame { .unwrap() .clone() .expect("Static field was not initialised"); - self.stack.push(constant.into()); + self.push(constant); Ok(ExecutionResult::Continue) } Ops::putstatic(index) => { - let field_ref = self.pool.resolve_field(*index)?; - trace!("Getting static field {field_ref:?}"); + let field_ref = self.pool.resolve_field(index)?; + trace!("Putting static field {field_ref:?}"); let init_class = self .thread - .get_or_resolve_class(&field_ref.class, self.thread.clone()) + .get_or_resolve_class(&field_ref.class) .expect("TO hecken work"); let static_field = init_class - .find_field(&field_ref.name, field_ref.desc) + .find_field(&field_ref.name, &field_ref.desc) .expect("TO hecken work"); let value = self.stack.pop().expect("stack to have value"); *static_field.value.lock().unwrap() = Some(value); @@ -880,31 +1334,62 @@ impl Frame { } Ops::getfield(index) => { - let field_ref = self.pool.resolve_field(*index)?; + let field_ref = self.pool.resolve_field(index)?; trace!("Getting field {field_ref:?}"); - if let Value::Reference(object_ref) = - self.stack.pop().expect("object reference on stack") - { - if let Some(Reference::ObjectReference(object)) = object_ref { - let val = object.lock().unwrap().get_field(&field_ref.name); - self.stack.push(val); - Ok(ExecutionResult::Continue) - } else { - Err(VmError::StackError("Null pointer exception".to_string())) + let popped = self.pop()?; + match popped { + Value::Primitive(x) => { + Err(VmError::StackError("Getfield era".parse().unwrap())) } - } else { - Err(VmError::StackError( - "putfield tried to operate on a non object stack value".to_string(), - )) + Value::Reference(x) => { + match x { + None => Ok(ExecutionResult::Continue), + Some(kind) => match kind { + ReferenceKind::ObjectReference(x) => { + let val = x.lock().unwrap().get_field(&field_ref); + self.push(val); + + Ok(ExecutionResult::Continue) + } + ReferenceKind::ArrayReference(_) => { + Err(VmError::StackError("get field".parse().unwrap())) + } + }, + } + // self.stack.push(val); + // Ok(ExecutionResult::Continue) + } + Value::Padding => {panic!("Uhh not possible chief")} } } Ops::putfield(index) => { - let field_ref = self.pool.resolve_field(*index)?; + let field_ref = self.pool.resolve_field(index)?; trace!("Setting field {field_ref:?}"); - let value = self.stack.pop().expect("value on stack"); + let value = self.pop()?; + { + let value = value.clone(); + let ref_type = field_ref.desc; + + // match field_ref.desc { + // FieldType::Base(x) => { + // + // } + // FieldType::ClassType(x) => { + // + // } + // FieldType::ArrayType(x) => { + // x + // } + // } + // debug_assert_eq!( + // value, ref_type, + // "popped:{} not equal to desired:{}", + // value, ref_type + // ) + } if let Value::Reference(reference) = self.stack.pop().expect("object on stack") { - if let Some(Reference::ObjectReference(object)) = reference { + if let Some(ReferenceKind::ObjectReference(object)) = reference { object.lock().unwrap().set_field(&field_ref.name, value); Ok(ExecutionResult::Continue) } else { @@ -919,49 +1404,45 @@ impl Frame { // todo!("op putfield: index - {}", index) } Ops::invokevirtual(index) => { - let method_ref = self.pool.resolve_method_ref(*index)?; - let args_count = method_ref.desc.arg_width(); - let args = self.stack.split_off(self.stack.len() - args_count); - let result = self.thread.invoke(method_ref, args, self.thread.clone())?; + let method_ref = self.pool.resolve_method_ref(index)?; + // the 1 represents the receiver + let args_count = method_ref.desc.arg_width() + 1; + let args = self.stack.pop_n(args_count)?; + let result = self.thread.invoke(method_ref, args)?; if let Some(val) = result { - self.stack.push(val) + self.push(val) } - todo!("Finish invoke virtual"); + // todo!("Finish invoke virtual"); Ok(ExecutionResult::Continue) } Ops::invokespecial(index) => { - let method_ref = self.pool.resolve_method_ref(*index)?; - let class = self - .thread - .get_or_resolve_class(&method_ref.class, self.thread.clone())?; + let method_ref = self.pool.resolve_method_ref(index)?; + let class = self.thread.get_or_resolve_class(&method_ref.class)?; // the 1 represents the receiver let args_count = method_ref.desc.arg_width() + 1; - let args = self.stack.split_off(self.stack.len() - args_count); + let args = self.stack.pop_n(args_count)?; - let result = self.thread.invoke(method_ref, args, self.thread.clone())?; + let result = self.thread.invoke(method_ref, args)?; if let Some(val) = result { - self.stack.push(val) + self.push(val) } // todo!("invoke special"); Ok(ExecutionResult::Continue) } Ops::invokestatic(index) => { - let method_ref = self.pool.resolve_method_ref(*index)?; - let class = self - .thread - .get_or_resolve_class(&method_ref.class, self.thread.clone())?; + let method_ref = self.pool.resolve_method_ref(index)?; + let class = self.thread.get_or_resolve_class(&method_ref.class)?; let args_count = method_ref.desc.parameters.len(); - let args = self.stack.split_off(self.stack.len() - args_count); + let args = self.stack.pop_n(args_count)?; - let result = self.thread.invoke(method_ref, args, self.thread.clone())?; + let result = self.thread.invoke(method_ref, args)?; if let Some(val) = result { - self.stack.push(val) + self.push(val) } - warn!("invoke static not final {}", index); Ok(ExecutionResult::Continue) } @@ -975,15 +1456,15 @@ impl Frame { // can init class Ops::new(index) => { - let class = self.pool.resolve_class_name(*index)?; + let class = self.pool.resolve_class_name(index)?; let init_class = self .thread - .get_or_resolve_class(&class, self.thread.clone()) + .get_or_resolve_class(&class) .expect("TO hecken work"); let object = self.thread.gc.write().unwrap().new_object(init_class); self.stack - .push(Value::Reference(Some(Reference::from(object)))); + .push(Value::Reference(Some(ReferenceKind::from(object)))); Ok(ExecutionResult::Continue) } @@ -992,25 +1473,72 @@ impl Frame { let Value::Primitive(Primitive::Int(count)) = value else { panic!("stack item was not int") }; - let array = self.thread.gc.write().unwrap().new_primitive_array(); + let array = self + .thread + .gc + .write() + .unwrap() + .new_primitive_array(array_type.clone(), count); self.stack - .push(Value::Reference(Some(Reference::from(array)))); + .push(Value::Reference(Some(ReferenceKind::ArrayReference(array)))); Ok(ExecutionResult::Continue) } - Ops::anewarray(_) => { - todo!("anewarray") + Ops::anewarray(index) => { + let class_name = self.pool.resolve_class_name(index)?; + println!("{}", class_name); + // let array_class = self.thread.loader.get_or_create_array_class(class_name)?; + let value = self.stack.pop().expect("value to have stack"); + let Value::Primitive(Primitive::Int(count)) = value else { + panic!("stack item was not int") + }; + let array = self.thread.gc.write().unwrap().new_object_array(count); + self.stack + .push(Value::Reference(Some(ReferenceKind::ArrayReference(array)))); + Ok(ExecutionResult::Continue) } Ops::arraylength => { - todo!("arraylength") + let Value::Reference(Some(ReferenceKind::ArrayReference((array)))) = + self.stack.pop().expect("value on stack") + else { + panic!("Reference not on stack or not an array") + }; + self.push(Value::from(array.len())); + Ok(ExecutionResult::Continue) } Ops::athrow => { todo!("athrow") } - Ops::checkcast(_) => { - todo!("checkcast") + Ops::checkcast(index) => { + let thing = self.pool.resolve_class_name(index)?; + let into_class = self.thread.get_class(&thing)?; + let popped = self.pop()?; + if let Value::Reference(Some(x)) = popped.clone() { + match x { + ReferenceKind::ObjectReference(obj) => { + if obj.lock().unwrap().class.is_assignable_into(into_class) { self.push(popped); Ok(ExecutionResult::Continue) } else { todo!("Error path") } + + } + ReferenceKind::ArrayReference(arr) => { + todo!("Arrays") + } + } + } else { self.push(popped); Ok(ExecutionResult::Continue) } } - Ops::instanceof(_) => { - todo!("instanceof") + Ops::instanceof(index) => { + let thing = self.pool.resolve_class_name(index)?; + let into_class = self.thread.get_class(&thing)?; + let popped = self.pop()?; + if let Value::Reference(Some(x)) = popped { + match x { + ReferenceKind::ObjectReference(obj) => { + if obj.lock().unwrap().class.is_assignable_into(into_class) { self.push(1i32.into()) } else { self.push(0i32.into()) } + Ok(ExecutionResult::Continue) + } + ReferenceKind::ArrayReference(arr) => { + todo!("Arrays") + } + } + } else { panic!("yeet") } } Ops::monitorenter => { todo!("monitorenter") @@ -1019,9 +1547,109 @@ impl Frame { todo!("monitorexit") } - _ => { - todo!("Unimplemented op: {:?}", op) + Ops::wide => { + todo!("wide") + } + Ops::multianewarray(_, _) => { + todo!("multianewarray") + } + Ops::ifnull(offset) => { + if let Value::Reference(value) = self.pop()? { + if value.is_none() { + Ok(ExecutionResult::Advance(offset)) + } else { + Ok(ExecutionResult::Continue) + } + } else { + Err(VmError::stack_not_int()) + } + } + Ops::ifnonnull(offset) => { + if let Value::Reference(value) = self.pop()? { + if value.is_some() { + Ok(ExecutionResult::Advance(offset)) + } else { + Ok(ExecutionResult::Continue) + } + } else { + Err(VmError::stack_not_int()) + } + } + Ops::goto_w(_) => { + todo!("goto_w") + } + Ops::jsr_w(_) => { + todo!("jsr_w") + } + Ops::breakpoint => { + todo!("breakpoint") + } + Ops::impdep1 => { + todo!("impdep1") + } + Ops::impdep2 => { + todo!("impdep2") } } } + + fn load_constant(&mut self, index: u16) -> Result { + let thing = self.pool.get_constant(index.to_owned())?; + trace!("\tLoading constant: {}", thing); + let resolved = match thing { + ConstantPoolEntry::Integer(x) => Value::from(*x), + ConstantPoolEntry::Float(x) => Value::from(*x), + ConstantPoolEntry::Class(x) => { + let name = self.pool.get_string(x.name_index)?; + let class = self.thread.get_or_resolve_class(&name)?; + let class_ref = self.thread.gc.read().unwrap().get(*class.mirror.wait()); + Value::from(class_ref) + } + ConstantPoolEntry::String(x) => { + let utf_ref = self.pool.get_string(x.string_index)?; + trace!("{utf_ref}"); + let string_class = self.thread.get_or_resolve_class("java/lang/String")?; + let string_ref = self.thread.intern_string(&utf_ref); + Value::from(string_ref) + } + + ConstantPoolEntry::MethodHandle(x) => { + todo!("Method handle loading not yet implemented"); + Value::NULL + } + ConstantPoolEntry::MethodType(x) => { + todo!("Method type loading not yet implemented"); + Value::NULL + } + ConstantPoolEntry::Dynamic(x) => { + todo!("Dynamic loading not yet implemented"); + Value::NULL + } + _ => { + panic!( + "Cannot load constant, is not of loadable type: {:?}. ", + thing + ); + } + }; + self.push(resolved); + Ok(ExecutionResult::Continue) + } } + +pub fn string_from_bytes(byte_slice: &[jbyte]) -> String { + // Convert the byte array (i8) to UTF-16 code units + // The bytes are stored as UTF-16 LE (little-endian) pairs + let bytes: Vec = byte_slice.iter().map(|&b| b as u8).collect(); + + // Convert pairs of bytes to u16 (UTF-16 code units) + 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); + } + + // Convert UTF-16 to Rust String (UTF-8) + let rust_string = String::from_utf16_lossy(&utf16_chars); + rust_string +} \ No newline at end of file diff --git a/crates/core/src/macros.rs b/crates/core/src/macros.rs index e3a7382..3d35388 100644 --- a/crates/core/src/macros.rs +++ b/crates/core/src/macros.rs @@ -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) + }}; +} \ No newline at end of file diff --git a/crates/core/src/main.rs b/crates/core/src/main.rs index 6c11e4a..6e1f33c 100644 --- a/crates/core/src/main.rs +++ b/crates/core/src/main.rs @@ -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) } diff --git a/crates/core/src/native_libraries.rs b/crates/core/src/native_libraries.rs index d471fa7..69a1c51 100644 --- a/crates/core/src/native_libraries.rs +++ b/crates/core/src/native_libraries.rs @@ -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; // impl NativeExt for NativeLibraries {} // // trait NativeExt: AsRef<[..]> { diff --git a/crates/core/src/object_manager.rs b/crates/core/src/object_manager.rs deleted file mode 100644 index 49ba59b..0000000 --- a/crates/core/src/object_manager.rs +++ /dev/null @@ -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, -} - -impl ObjectManager { - pub fn new(&mut self, class: Arc) -> 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() - } -} diff --git a/crates/core/src/objects/array.rs b/crates/core/src/objects/array.rs index 9a105a0..ada0bbb 100644 --- a/crates/core/src/objects/array.rs +++ b/crates/core/src/objects/array.rs @@ -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 for Reference { - fn from(value: ObjectReference) -> Self { - Self::ObjectReference(value) - } -} - -impl From for Reference { - fn from(value: PrimitiveArrayReference) -> Self { - Self::ArrayReference(ArrayReference::Primitive(value)) - } -} - -impl From 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), + Byte(PrimitiveArrayReference), + Short(PrimitiveArrayReference), + Long(PrimitiveArrayReference), + Float(PrimitiveArrayReference), + Double(PrimitiveArrayReference), + Char(PrimitiveArrayReference), + Boolean(PrimitiveArrayReference), Object(ObjectArrayReference), } -pub type PrimitiveArrayReference = Arc>; -type PrimitiveArray = (u32, Vec); +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>; -type ObjectArray = (u32, Vec); - -#[derive(Debug)] -pub struct Array { - id: u32, - backing: Vec, -} - -impl Array { - 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 = Arc>>; + +pub type ObjectArrayReference = Arc>>>; + +#[derive(Debug)] +pub struct Array { + pub(crate) id: u32, + pub(crate) backing: Box<[T]>, +} + +impl Array +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 From<(u32, Vec)> for Array { + fn from(value: (u32, Vec)) -> Self { + let (id, vector) = value; + Self { + id, + backing: vector.into_boxed_slice(), + } + } +} + +impl Deref for Array { + type Target = [T]; + + fn deref(&self) -> &Self::Target { + &self.backing + } +} + +impl DerefMut for Array { + 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 {} diff --git a/crates/core/src/objects/mod.rs b/crates/core/src/objects/mod.rs index da0b02b..9b745c4 100644 --- a/crates/core/src/objects/mod.rs +++ b/crates/core/src/objects/mod.rs @@ -1,3 +1,6 @@ pub mod array; pub mod object; pub mod object_manager; + +pub use object::Reference; +pub use object::ReferenceKind; diff --git a/crates/core/src/objects/object.rs b/crates/core/src/objects/object.rs index 90f205c..36fb294 100644 --- a/crates/core/src/objects/object.rs +++ b/crates/core/src/objects/object.rs @@ -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>; @@ -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); + +impl 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 { + 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; + +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 for ReferenceKind { + fn from(value: ObjectReference) -> Self { + Self::ObjectReference(value) + } +} + +impl From> for ReferenceKind { + fn from(value: PrimitiveArrayReference) -> Self { + Self::ArrayReference(ArrayReference::Int(value)) + } +} + +impl From> for ReferenceKind { + fn from(value: PrimitiveArrayReference) -> Self { + Self::ArrayReference(ArrayReference::Byte(value)) + } +} + +impl From> for ReferenceKind { + fn from(value: PrimitiveArrayReference) -> Self { + Self::ArrayReference(ArrayReference::Short(value)) + } +} + +impl From> for ReferenceKind { + fn from(value: PrimitiveArrayReference) -> Self { + Self::ArrayReference(ArrayReference::Long(value)) + } +} + +impl From> for ReferenceKind { + fn from(value: PrimitiveArrayReference) -> Self { + Self::ArrayReference(ArrayReference::Float(value)) + } +} + +impl From> for ReferenceKind { + fn from(value: PrimitiveArrayReference) -> Self { + Self::ArrayReference(ArrayReference::Double(value)) + } +} + +impl From> for ReferenceKind { + fn from(value: PrimitiveArrayReference) -> Self { + Self::ArrayReference(ArrayReference::Char(value)) + } +} + +impl From> for ReferenceKind { + fn from(value: PrimitiveArrayReference) -> Self { + Self::ArrayReference(ArrayReference::Boolean(value)) + } +} + +impl From for ReferenceKind { + fn from(value: ObjectArrayReference) -> Self { + Self::ArrayReference(ArrayReference::Object(value)) + } +} diff --git a/crates/core/src/objects/object_manager.rs b/crates/core/src/objects/object_manager.rs new file mode 100644 index 0000000..914df4f --- /dev/null +++ b/crates/core/src/objects/object_manager.rs @@ -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, + strings: HashMap, +} + +impl ObjectManager { + pub fn new_object(&mut self, class: Arc) -> 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) -> 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::>(); + panic!("Object must be present id: {id}\n{objs:#?}") }) + .clone() + } + + pub fn get_interned_string(&self, utf: &str) -> Option { + self.strings + .get(utf) + .map(|e| self.get(*e)) + .and_then(ReferenceKind::into_object_reference) + } + + pub fn new_string(&mut self, string_class: Arc, 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::>(); + 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, + 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 + } +} diff --git a/crates/core/src/prim.rs b/crates/core/src/prim.rs new file mode 100644 index 0000000..ea270ca --- /dev/null +++ b/crates/core/src/prim.rs @@ -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 {} diff --git a/crates/core/src/thread.rs b/crates/core/src/thread.rs index f8acfa0..7e5a597 100644 --- a/crates/core/src/thread.rs +++ b/crates/core/src/thread.rs @@ -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, 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> = RefCell::new(None); +} + // A thread of execution pub struct VmThread { + pub id: ThreadId, pub vm: Arc, pub loader: Arc>, pub frame_stack: Vec, pub gc: Arc>, + pub jni_env: JNIEnv, } impl VmThread { - pub fn new(vm: Arc, loader: Option) -> Self { + pub fn new(vm: Arc, loader: Option) -> Arc { + 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) -> Arc { + 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, - ) -> Result, VmError> { + pub fn get_or_resolve_class(&self, what: &str) -> Result, 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, thread: Arc) -> Result<(), VmError> { + fn init(&self, class: Arc) -> 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 if present let class_init_method = class.find_method("", &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 : {:?}", e)))?; @@ -163,41 +216,20 @@ impl VmThread { result } - pub fn invoke_main(&self, what: &str, thread: Arc) { + 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, - thread: Arc, - ) -> MethodCallResult { - let class = self.get_or_resolve_class(&method_reference.class, thread.clone())?; + pub fn invoke(&self, method_reference: MethodRef, mut args: Vec) -> 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) -> MethodCallResult { - let symbol_name = generate_jni_method_name(method); + pub fn invoke_native(&self, method: &MethodRef, mut args: Vec) -> 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::(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::(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, storage: &'a mut Vec>) -> Vec> { - // Store values in the provided storage - storage.push(Box::new(create_jni_function_table()) as Box); - storage.push(Box::new(std::ptr::null_mut::<()>()) as Box); +fn build_args<'a>( + mut params: VecDeque, + storage: &'a mut Vec>, + jnienv: *mut JNIEnv, +) -> Vec> { + // 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, storage: &'a mut Vec>) -> Vec Value::Primitive(Primitive::Byte(x)) => storage.push(Box::new(x) as Box), Value::Primitive(Primitive::Short(x)) => storage.push(Box::new(x) as Box), Value::Primitive(Primitive::Long(x)) => storage.push(Box::new(x) as Box), - Value::Reference(x) => storage.push(Box::new(x) as Box), + 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, storage: &'a mut Vec>) -> 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 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) + } +} diff --git a/crates/core/src/value.rs b/crates/core/src/value.rs index 3cbd08a..7e35738 100644 --- a/crates/core/src/value.rs +++ b/crates/core/src/value.rs @@ -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(Option), + /// Second slot of a wide value (long/double). Should never be accessed directly. + Padding, +} + +#[derive( Clone, Default)] +pub struct OperandStack(Vec); + +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 { + 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, 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 +} + +impl LocalVariables { + pub fn with_capacity(max_locals: usize) -> Self { + Self { + inner: vec![Value::NULL; max_locals] + } + } + pub fn from_args(args: Vec, 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 { + self.inner.iter().filter(|v| !matches!(v, Value::Padding)) + } + + pub fn slots(&self) -> impl Iterator { + 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) -> String { + let fmt = values + .iter() + .map(|v| v.to_string()) + .collect::>() + .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 for Value { } } +impl_value_from!(bool, Boolean); + +impl From> for Value { + fn from(value: Option) -> Self { + Self::Reference(value) + } +} + +impl From for Value { + fn from(value: ReferenceKind) -> Self { + Value::from(Some(value)) + } +} +impl From for Value { + fn from(value: ObjectReference) -> Self { + Value::from(ReferenceKind::ObjectReference(value)) + } +} +impl From 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 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") } } } } diff --git a/crates/core/src/vm.rs b/crates/core/src/vm.rs index edab94c..8e129f4 100644 --- a/crates/core/src/vm.rs +++ b/crates/core/src/vm.rs @@ -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>>, + // Thread registry - maps ThreadId to VmThread + pub threads: DashMap>, + pub main_thread_id: ThreadId, pub loader: Arc>, - pub native_methods: HashMap>, + pub native_methods: DashMap, + pub native_libraries: DashMap, pub gc: Arc>, } impl Vm { // start vm, loading main from classfile - pub fn new(what: &str) -> Arc { + pub fn new() -> Arc { 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::>(); + 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::(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::(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) } } diff --git a/crates/jvm-rs-sys/src/lib.rs b/crates/jvm-rs-sys/src/lib.rs deleted file mode 100644 index c2b9fbb..0000000 --- a/crates/jvm-rs-sys/src/lib.rs +++ /dev/null @@ -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}") -} diff --git a/crates/jvm-rs-sys/Cargo.toml b/crates/roast-vm-sys/Cargo.toml similarity index 64% rename from crates/jvm-rs-sys/Cargo.toml rename to crates/roast-vm-sys/Cargo.toml index 773d20f..9b70229 100644 --- a/crates/jvm-rs-sys/Cargo.toml +++ b/crates/roast-vm-sys/Cargo.toml @@ -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"] \ No newline at end of file diff --git a/crates/roast-vm-sys/src/class.rs b/crates/roast-vm-sys/src/class.rs new file mode 100644 index 0000000..18f2914 --- /dev/null +++ b/crates/roast-vm-sys/src/class.rs @@ -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 = (&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 + + +} \ No newline at end of file diff --git a/crates/roast-vm-sys/src/lib.rs b/crates/roast-vm-sys/src/lib.rs new file mode 100644 index 0000000..7ae7243 --- /dev/null +++ b/crates/roast-vm-sys/src/lib.rs @@ -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 { + 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() +} diff --git a/crates/roast-vm-sys/src/misc_unsafe.rs b/crates/roast-vm-sys/src/misc_unsafe.rs new file mode 100644 index 0000000..8927bd0 --- /dev/null +++ b/crates/roast-vm-sys/src/misc_unsafe.rs @@ -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 + () +} \ No newline at end of file diff --git a/crates/roast-vm-sys/src/object.rs b/crates/roast-vm-sys/src/object.rs new file mode 100644 index 0000000..9dd361a --- /dev/null +++ b/crates/roast-vm-sys/src/object.rs @@ -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 +} \ No newline at end of file