diff --git a/.gitignore b/.gitignore index e3af16d..b4bb84e 100644 --- a/.gitignore +++ b/.gitignore @@ -20,7 +20,8 @@ Cargo.lock # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. -.idea/ +#.idea/ data lib -output \ No newline at end of file +output +headers \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..ab1f416 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,10 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Ignored default folder with query files +/queries/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..9929897 --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..79ee123 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/discord.xml b/.idea/discord.xml new file mode 100644 index 0000000..912db82 --- /dev/null +++ b/.idea/discord.xml @@ -0,0 +1,14 @@ + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..146e386 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..fd2d682 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..7e69014 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/roast-vm.iml b/.idea/roast-vm.iml new file mode 100644 index 0000000..255ebef --- /dev/null +++ b/.idea/roast-vm.iml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 90650d4..aa31a88 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,4 +16,18 @@ libloading = "0.8.9" libffi = "5.0.0" 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 +roast-vm-core = { path = "crates/core", version = "0.1.0" } + +[profile.dev-opt] +inherits = "dev" +opt-level = 2 # 0=none, 1=basic, 2=good, 3=aggressive +debug = true # keep debug symbols +debug-assertions = true # keep assertions +overflow-checks = true # keep overflow checks +lto = false # link-time optimization (slow compile, fast runtime) + +[profile.dev-opt.package.libffi] +debug = false + +[profile.dev-opt.package.libffi-sys] +debug = false \ No newline at end of file diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index 0a39ea2..b17ef3f 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -16,6 +16,8 @@ libffi = { workspace = true } jni = { workspace = true } itertools = { workspace = true } colored = "3.0.0" +parking_lot = "0.12" +cesu8 = "1.1.0" [build-dependencies] bindgen = "0.72.1" diff --git a/crates/core/src/attributes.rs b/crates/core/src/attributes.rs index 715576d..d4bc6b3 100644 --- a/crates/core/src/attributes.rs +++ b/crates/core/src/attributes.rs @@ -1,10 +1,11 @@ use crate::class_file::constant_pool::ConstantPoolExt; use crate::class_file::{ClassFile, Constant, ConstantPoolEntry}; -use deku::DekuContainerRead; +use deku::{DekuContainerRead, DekuError, DekuReader}; use deku_derive::DekuRead; use log::trace; use std::fmt::{Display, Formatter}; use std::ops::Deref; +use deku::ctx::Endian; #[derive(Clone, PartialEq, Debug, DekuRead)] #[deku(ctx = "_endian: deku::ctx::Endian", endian = "big")] @@ -60,6 +61,72 @@ pub enum ArrayType { T_LONG, } +#[derive(Clone, PartialEq, Debug)] +pub struct LookupSwitchData { + pub default: i32, + pub pairs: Vec<(i32, i32)>, // (match, offset) +} + +impl<'a> DekuReader<'a, (Endian, u16)> for LookupSwitchData { + fn from_reader_with_ctx( + reader: &mut deku::reader::Reader, + (_endian, byte_offset): (Endian, u16), + ) -> Result { + let pos_after_opcode = (byte_offset + 1) as usize; + let padding = (4 - (pos_after_opcode % 4)) % 4; + + for _ in 0..padding { + u8::from_reader_with_ctx(reader, ())?; + } + + let default = i32::from_reader_with_ctx(reader, Endian::Big)?; + let npairs = i32::from_reader_with_ctx(reader, Endian::Big)?; + + let mut pairs = Vec::with_capacity(npairs as usize); + for _ in 0..npairs { + let match_val = i32::from_reader_with_ctx(reader, Endian::Big)?; + let offset = i32::from_reader_with_ctx(reader, Endian::Big)?; + pairs.push((match_val, offset)); + } + + Ok(LookupSwitchData { default, pairs }) + } +} + +#[derive(Clone, PartialEq, Debug)] +pub struct TableSwitchData { + pub default: i32, + pub low: i32, + pub high: i32, + pub offsets: Vec, +} + +impl<'a> DekuReader<'a, (Endian, u16)> for TableSwitchData { + fn from_reader_with_ctx( + reader: &mut deku::reader::Reader, + (_endian, byte_offset): (Endian, u16), + ) -> Result { + let pos_after_opcode = (byte_offset + 1) as usize; + let padding = (4 - (pos_after_opcode % 4)) % 4; + + for _ in 0..padding { + u8::from_reader_with_ctx(reader, ())?; + } + + let default = i32::from_reader_with_ctx(reader, Endian::Big)?; + let low = i32::from_reader_with_ctx(reader, Endian::Big)?; + let high = i32::from_reader_with_ctx(reader, Endian::Big)?; + + let count = (high - low + 1) as usize; + let mut offsets = Vec::with_capacity(count); + for _ in 0..count { + offsets.push(i32::from_reader_with_ctx(reader, Endian::Big)?); + } + + Ok(TableSwitchData { default, low, high, offsets }) + } +} + impl Display for ArrayType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let str = match self { diff --git a/crates/core/src/class.rs b/crates/core/src/class.rs index 046a5b1..7a2336c 100644 --- a/crates/core/src/class.rs +++ b/crates/core/src/class.rs @@ -3,12 +3,14 @@ use crate::class_file::{ ClassFile, ClassFlags, ConstantPoolEntry, FieldData, FieldInfo, FieldRef, MethodData, MethodInfo, MethodRef, }; +use crate::error::VmError; use crate::{FieldType, MethodDescriptor}; use log::trace; +use parking_lot::Mutex; use std::hash::{Hash, Hasher}; -use std::sync::{Arc, Mutex, OnceLock, OnceState}; +use std::sync::atomic::AtomicBool; +use std::sync::{Arc, OnceLock, OnceState}; use std::thread::ThreadId; -use crate::error::VmError; /// JVM Spec 5.5: Initialization states for a class #[derive(Debug, Clone, PartialEq)] @@ -33,6 +35,7 @@ pub struct RuntimeClass { pub fields: Vec, pub methods: Vec, pub mirror: OnceLock, + pub mirror_in_progress: AtomicBool, /// Thread-safe initialization state (JVM Spec 5.5) pub init_state: Mutex, pub super_classes: Vec>, @@ -55,11 +58,15 @@ impl PartialEq for RuntimeClass { impl Eq for RuntimeClass {} impl RuntimeClass { + pub fn mirror(&self) -> u32 { + *self.mirror.get().unwrap() + } pub fn find_method(&self, name: &str, desc: &MethodDescriptor) -> Result<&MethodData, VmError> { trace!( "Finding in {}, method Name: {name} desc:{desc},", self.this_class ); + if let Some(method) = self.methods.iter().find(|e| { let name_match = e.name.eq(name); let param_match = desc.parameters == e.desc.parameters; @@ -100,7 +107,10 @@ impl RuntimeClass { } pub fn is_assignable_into(&self, into: Arc) -> bool { - if self.eq(&*into) || self.super_classes.contains(&into) || self.super_interfaces.contains(&into) { + if self.eq(&*into) + || self.super_classes.contains(&into) + || self.super_interfaces.contains(&into) + { return true; } // Array covariance: both must be arrays, then check component types @@ -117,6 +127,36 @@ impl RuntimeClass { } fn is_primitive_class(&self) -> bool { - matches!(self.this_class.as_str(), "byte"|"char"|"double"|"float"|"int"|"long"|"short"|"boolean") + matches!( + self.this_class.as_str(), + "byte" | "char" | "double" | "float" | "int" | "long" | "short" | "boolean" + ) + } + + pub(crate) fn has_default_method(&self) -> bool { + self.methods + .iter() + .any(|method| !method.flags.ACC_ABSTRACT && !method.flags.ACC_STATIC) + && self.access_flags.INTERFACE + } + + pub fn implements(&self, interface: &str) -> bool { + self.super_interfaces + .iter() + .any(|e| e.this_class == interface) + } + + pub fn get_constructor_ref(&self, idx: usize) -> Result<&MethodData, VmError> { + let constructors = self + .methods + .iter() + .filter(|x| x.name.eq("")) + .collect::>(); + constructors + .get(idx) + .ok_or(VmError::InvariantError( + "class does not have this constructor, consider inehritance impl".to_string(), + )) + .copied() } } diff --git a/crates/core/src/class_file/class_file.rs b/crates/core/src/class_file/class_file.rs index 179e5a9..441fa2f 100644 --- a/crates/core/src/class_file/class_file.rs +++ b/crates/core/src/class_file/class_file.rs @@ -1,18 +1,20 @@ use crate::attributes::{Attribute, AttributeInfo, CodeAttribute, LineNumberTableEntry}; +use crate::class::RuntimeClass; use crate::class_file::constant_pool::{ConstantPoolError, ConstantPoolExt, ConstantPoolOwned}; use crate::instructions::Ops; use crate::value::Value; use crate::{BaseType, FieldType, MethodDescriptor}; use deku::ctx::Endian::Big; -use deku::{DekuContainerRead, DekuError}; +use deku::{DekuContainerRead, DekuContainerWrite, DekuError}; use deku_derive::{DekuRead, DekuWrite}; use itertools::Itertools; +use parking_lot::Mutex; use std::borrow::Cow; use std::fmt; use std::fmt::{Display, Formatter}; use std::ops::Deref; use std::str::Chars; -use std::sync::{Arc, Mutex}; +use std::sync::Arc; #[derive(Debug, PartialEq, DekuRead)] #[deku(magic = b"\xCA\xFE\xBA\xBE", endian = "big")] @@ -493,7 +495,7 @@ pub fn pool_get_string(constant_pool: &[ConstantPoolEntry], index: u16) -> Optio fn read_bytecode_with_offsets( bytes: u32, reader: &mut deku::reader::Reader, - endian: deku::ctx::Endian, + _endian: deku::ctx::Endian, ) -> Result, DekuError> { use deku::DekuReader; @@ -507,7 +509,7 @@ fn read_bytecode_with_offsets( let start_pos = reader.bits_read; // Parse the next Op - let op = Ops::from_reader_with_ctx(reader, endian)?; + let op = Ops::from_reader_with_ctx(reader, byte_offset)?; let end_pos = reader.bits_read; let op_bytes = ((end_pos - start_pos) / 8) as usize; @@ -654,6 +656,7 @@ pub struct MethodRef { #[derive(Debug, Clone)] pub struct MethodData { pub name: String, + pub class: String, pub desc: MethodDescriptor, pub code: Option, pub flags: MethodFlags, @@ -665,6 +668,16 @@ pub struct MethodData { // pub method_parameters: Option<_> } +impl From for MethodRef { + fn from(value: MethodData) -> Self { + Self { + class: value.class, + name: value.name, + desc: value.desc, + } + } +} + #[derive(Debug)] pub struct FieldRef { pub class: String, @@ -734,6 +747,13 @@ impl From for ClassFlags { } } +impl Into for ClassFlags { + fn into(self) -> u16 { + let bytes = self.to_bytes().unwrap(); + u16::from_be_bytes([bytes[0], bytes[1]]) + } +} + #[allow(non_snake_case)] #[derive(Debug, PartialEq, DekuRead, DekuWrite)] pub struct ModuleFlags { @@ -754,27 +774,34 @@ impl From for ModuleFlags { } } +impl Into for ModuleFlags { + fn into(self) -> u16 { + let bytes = self.to_bytes().unwrap(); + u16::from_be_bytes([bytes[0], bytes[1]]) + } +} + #[allow(non_snake_case)] #[derive(Debug, PartialEq, DekuRead, DekuWrite)] pub struct FieldFlags { #[deku(bits = 1, pad_bits_before = "1")] - ACC_ENUM: bool, + pub ACC_ENUM: bool, #[deku(bits = 1, pad_bits_before = "1")] - ACC_SYNTHETIC: bool, + pub ACC_SYNTHETIC: bool, #[deku(bits = 1, pad_bits_before = "4")] - ACC_TRANSIENT: bool, + pub ACC_TRANSIENT: bool, #[deku(bits = 1)] - ACC_VOLATILE: bool, + pub ACC_VOLATILE: bool, #[deku(bits = 1, pad_bits_before = "1")] - ACC_FINAL: bool, + pub ACC_FINAL: bool, #[deku(bits = 1)] - ACC_STATIC: bool, + pub ACC_STATIC: bool, #[deku(bits = 1)] - ACC_PROTECTED: bool, + pub ACC_PROTECTED: bool, #[deku(bits = 1)] - ACC_PRIVATE: bool, + pub ACC_PRIVATE: bool, #[deku(bits = 1)] - ACC_PUBLIC: bool, + pub ACC_PUBLIC: bool, } impl From for FieldFlags { @@ -784,6 +811,13 @@ impl From for FieldFlags { } } +impl Into for FieldFlags { + fn into(self) -> u16 { + let bytes = self.to_bytes().unwrap(); + u16::from_be_bytes([bytes[0], bytes[1]]) + } +} + #[allow(non_snake_case)] #[derive(Debug, PartialEq, DekuRead, DekuWrite, Clone)] pub struct MethodFlags { @@ -820,6 +854,13 @@ impl From for MethodFlags { } } +impl Into for MethodFlags { + fn into(self) -> u16 { + let bytes = self.to_bytes().unwrap(); + u16::from_be_bytes([bytes[0], bytes[1]]) + } +} + //yoinked because im monkled impl MethodDescriptor { /// Parses a method descriptor as specified in the JVM specs: diff --git a/crates/core/src/class_file/constant_pool.rs b/crates/core/src/class_file/constant_pool.rs index 3f8fd4b..d2d8e6e 100644 --- a/crates/core/src/class_file/constant_pool.rs +++ b/crates/core/src/class_file/constant_pool.rs @@ -5,13 +5,14 @@ use crate::class_file::{ ConstantClassInfo, ConstantDynamicInfo, ConstantFieldrefInfo, ConstantInterfaceMethodrefInfo, ConstantInvokeDynamicInfo, ConstantMethodHandleInfo, ConstantMethodTypeInfo, ConstantMethodrefInfo, ConstantModuleInfo, ConstantNameAndTypeInfo, ConstantPackageInfo, - ConstantPoolEntry, ConstantStringInfo, ConstantUtf8Info, DescParseError, FieldInfo, FieldRef - , MethodRef, + ConstantPoolEntry, ConstantStringInfo, ConstantUtf8Info, DescParseError, FieldInfo, FieldRef, + MethodRef, }; +use crate::error::VmError; use crate::{pool_get_impl, FieldType, MethodDescriptor}; +use cesu8::{from_java_cesu8, Cesu8DecodingError}; use deku::DekuContainerRead; use std::fmt::{Display, Formatter}; -use crate::error::VmError; pub type ConstantPoolSlice = [ConstantPoolEntry]; pub type ConstantPoolOwned = Vec; @@ -35,8 +36,8 @@ pub trait ConstantPoolExt: ConstantPoolGet { // fn get_string(&self, index: u16) -> Result { let cp_entry = self.get_utf8_info(index)?; - - String::from_utf8(cp_entry.bytes.clone()).map_err(|e| e.to_string().into()) + let cow = from_java_cesu8(&cp_entry.bytes)?; + Ok(cow.into()) } // // fn get_field(&self, index: u16) -> Result<&ConstantFieldrefInfo, ()> { @@ -221,19 +222,38 @@ impl ConstantPoolGet for [ConstantPoolEntry] {} impl Display for ConstantPoolError { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - self.0.fmt(f) + match self { + ConstantPoolError::Generic(msg) => write!(f, "{}", msg), + ConstantPoolError::DescriptorParseError(err) => { + write!(f, "Descriptor parse error: {:?}", err) + } + ConstantPoolError::Cesu8DecodingError(err) => { + write!(f, "CESU-8 decoding error: {}", err) + } + } } } #[derive(Debug)] -pub struct ConstantPoolError(String); +pub enum ConstantPoolError { + Generic(String), + DescriptorParseError(DescParseError), + Cesu8DecodingError(Cesu8DecodingError), +} + impl From for ConstantPoolError { fn from(value: String) -> Self { - Self(value) + Self::Generic(value) } } impl From for ConstantPoolError { fn from(value: DescParseError) -> Self { - value.to_string().into() + Self::DescriptorParseError(value) + } +} + +impl From for ConstantPoolError { + fn from(value: Cesu8DecodingError) -> Self { + Self::Cesu8DecodingError(value) } } diff --git a/crates/core/src/class_loader.rs b/crates/core/src/class_loader.rs index 1529cac..869a262 100644 --- a/crates/core/src/class_loader.rs +++ b/crates/core/src/class_loader.rs @@ -3,16 +3,17 @@ use crate::bimage::Bimage; use crate::class::{InitState, RuntimeClass}; use crate::class_file::constant_pool::{ConstantPoolExt, ConstantPoolGet}; use crate::class_file::{ClassFile, ClassFlags, FieldData, FieldFlags, MethodData, MethodFlags}; +use crate::error::VmError; use crate::{FieldType, MethodDescriptor}; use dashmap::DashMap; use deku::DekuContainerRead; use log::warn; +use parking_lot::Mutex; use std::collections::HashSet; use std::fs::File; use std::io::Read; use std::path::{Path, PathBuf}; -use std::sync::{Arc, Mutex, OnceLock}; -use crate::error::VmError; +use std::sync::{Arc, OnceLock}; pub type LoaderRef = Arc>; @@ -83,9 +84,9 @@ pub fn resolve_path(what: &str) -> Result<(PathBuf, String), String> { /// ``` #[derive(Default)] pub struct ClassLoader { - classes: DashMap<(String, LoaderId), Arc>, + pub(crate) classes: DashMap<(String, LoaderId), Arc>, bimage: Bimage, - pub needs_init: Vec>, + // pub needs_init: Vec>, } type LoaderId = Option; @@ -104,7 +105,7 @@ impl ClassLoader { self.classes .iter() .map(|x| x.value().clone()) - .find(|e| *e.mirror.wait() == id) + .find(|e| e.mirror.get().is_some_and(|&m| m == id)) } /// Retrieves an `Arc` from the internal storage, or attempts to load it if not already present. @@ -165,18 +166,19 @@ impl ClassLoader { .expect("invalid L descriptor"); self.get_or_load(class_name, None) } - None => Err(VmError::LoaderError("empty component descriptor".to_string())), - _ => Err(VmError::LoaderError(format!("invalid component descriptor: {}", component_name))), + None => Err(VmError::LoaderError( + "empty component descriptor".to_string(), + )), + _ => Err(VmError::LoaderError(format!( + "invalid component descriptor: {}", + component_name + ))), }?; // let component = self.get_or_load(component_name, None)?; - let arr_class = self.create_array_class( - component, - ); - self.needs_init.push(arr_class.clone()); - return Ok(arr_class) + let arr_class = self.create_array_class(component); + return Ok(arr_class); } let class = self.load_class(class_name, loader)?; - self.needs_init.push(class.clone()); Ok(class) } @@ -184,19 +186,27 @@ impl ClassLoader { self.classes.clone() }*/ - fn load_class(&mut self, what: &str, loader: LoaderId) -> Result, VmError> { + pub fn load_class( + &mut self, + what: &str, + loader: LoaderId, + ) -> Result, VmError> { let (module, class_fqn) = ("", what); let bytes = self .bimage - .get_class(module, class_fqn).or_else(|e| Self::load_class_from_disk(what)).map_err(|e1| { - let classes = self.classes.iter().map(|x| { - x.this_class.clone() - }).collect::>(); - // println!("{:#?}", classes); - VmError::LoaderError(format!("failed to find class: {:?}\n{:#?}", what, classes)) - })?; - let (_, cf) = ClassFile::from_bytes((bytes.as_ref(), 0)) - .map_err(|e| VmError::DekuError(e))?; + .get_class(module, class_fqn) + .or_else(|e| Self::load_class_from_disk(what)) + .map_err(|e1| { + let classes = self + .classes + .iter() + .map(|x| x.this_class.clone()) + .collect::>(); + // println!("{:#?}", classes); + VmError::LoaderError(format!("failed to find class: {:?}\n{:#?}", what, classes)) + })?; + let (_, cf) = + ClassFile::from_bytes((bytes.as_ref(), 0)).map_err(|e| VmError::DekuError(e))?; let runtime = self.runtime_class(cf); let arced = Arc::new(runtime); let option = self @@ -216,7 +226,7 @@ impl ClassLoader { let path = format!("{class_path}/{what}.class"); log::info!("Loading class from path: {}", path); - let mut class_file = File::open(path).map_err(|e| {e.to_string()})?; + let mut class_file = File::open(path).map_err(|e| e.to_string())?; let mut bytes = Vec::new(); class_file.read_to_end(&mut bytes).unwrap(); Ok(bytes) @@ -302,6 +312,7 @@ impl ClassLoader { .iter() .map(|e| { let name = constant_pool.get_string(e.name_index).unwrap(); + let class = this_class.clone(); let flags = MethodFlags::from(e.access_flags); let desc = constant_pool .get_string(e.descriptor_index) @@ -327,6 +338,7 @@ impl ClassLoader { MethodData { name, + class, flags, desc, code, @@ -359,9 +371,7 @@ impl ClassLoader { let source_file = class_file.attributes.iter().find_map(|attr| { match constant_pool.parse_attribute(attr.clone()).ok()? { - Attribute::SourceFile(index) => { - constant_pool.get_string(index).ok() - }, + Attribute::SourceFile(index) => constant_pool.get_string(index).ok(), _ => None, } }); @@ -380,6 +390,7 @@ impl ClassLoader { super_interfaces, component_type: None, source_file, + mirror_in_progress: Default::default(), } } // pub fn get_or_create_array_class(class_name: &str) -> RuntimeClass { @@ -391,14 +402,12 @@ impl ClassLoader { // Ok(class) // } - pub fn create_array_class( - &mut self, - component: Arc, - ) -> Arc { + pub fn create_array_class(&mut self, component: Arc) -> Arc { // 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(); + let serializable: Arc = + self.get_or_load("java/io/Serializable", None).unwrap(); let name = match component.this_class.as_str() { "byte" => "[B".to_string(), "char" => "[C".to_string(), @@ -408,49 +417,41 @@ impl ClassLoader { "long" => "[J".to_string(), "short" => "[S".to_string(), "boolean" => "[Z".to_string(), - s if s.starts_with('[') => format!("[{}", s), // nested array - s => format!("[L{};", s), // object class + s if s.starts_with('[') => format!("[{}", s), // nested array + s => format!("[L{};", s), // object class + }; + let klass = 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.clone(), + super_class: Some(object_class.clone()), + interfaces: vec![cloneable.clone(), serializable.clone()], + fields: vec![], + methods: vec![], // clone() is handled specially + mirror: OnceLock::new(), + mirror_in_progress: Default::default(), + init_state: Mutex::new(InitState::NotInitialized), // arrays need no + super_classes: vec![object_class], + super_interfaces: vec![cloneable, serializable], + component_type: Some(component), // new field + source_file: None, }; - let klass = - 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.clone(), - 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::NotInitialized), // arrays need no - super_classes: vec![object_class], - super_interfaces: vec![cloneable, serializable], - component_type: Some(component), // new field - source_file: None, - }; let klass = Arc::new(klass); self.classes.insert((name.to_string(), None), klass.clone()); klass } pub fn primitive_class(&mut self, name: &str) -> Arc { - - - - - - - - let klass = Arc::new(RuntimeClass { constant_pool: Arc::new(vec![]), access_flags: ClassFlags { @@ -470,6 +471,7 @@ impl ClassLoader { fields: vec![], methods: vec![], mirror: Default::default(), + mirror_in_progress: Default::default(), init_state: Mutex::new(InitState::NotInitialized), super_classes: vec![], super_interfaces: vec![], diff --git a/crates/core/src/error.rs b/crates/core/src/error.rs index 578fe09..a2c3e71 100644 --- a/crates/core/src/error.rs +++ b/crates/core/src/error.rs @@ -1,12 +1,13 @@ -use std::fmt::{Display, Formatter}; -use deku::DekuError; use crate::class_file::constant_pool::ConstantPoolError; +use deku::DekuError; +use std::fmt::{Display, Formatter}; #[derive(Debug)] pub enum VmError { ConstantPoolError(String), StackError(String), InvariantError(String), + Debug(&'static str), DekuError(DekuError), LoaderError(String), ExecutionError, @@ -15,6 +16,7 @@ pub enum VmError { message: String, stack_trace: Vec, }, + NotImplemented(String), } impl VmError { @@ -33,7 +35,11 @@ impl Display for VmError { VmError::LoaderError(msg) => write!(f, "Loader error: {}", msg), VmError::ExecutionError => write!(f, "Execution error"), VmError::NativeError(msg) => write!(f, "Native error {msg}"), - _ => { write!(f, "idk lol") } + VmError::NotImplemented(msg) => write!(f, "Not implemented: {}", msg), + VmError::Debug(msg) => write!(f, "Debug panic: {}", msg), + _ => { + write!(f, "idk lol") + } } } } @@ -60,9 +66,15 @@ pub struct StackTraceElement { impl VmError { pub(crate) fn with_frame(self, elem: StackTraceElement) -> Self { match self { - VmError::Exception { message, mut stack_trace } => { + VmError::Exception { + message, + mut stack_trace, + } => { stack_trace.push(elem); - VmError::Exception { message, stack_trace } + VmError::Exception { + message, + stack_trace, + } } other => VmError::Exception { message: format!("{}", other), diff --git a/crates/core/src/frame.rs b/crates/core/src/frame.rs new file mode 100644 index 0000000..b211174 --- /dev/null +++ b/crates/core/src/frame.rs @@ -0,0 +1,1500 @@ +use crate::attributes::{CodeAttribute, LineNumberTableEntry}; +use crate::class::RuntimeClass; +use crate::class_file::constant_pool::{ConstantPoolExt, ConstantPoolGet}; +use crate::class_file::{Bytecode, ConstantPoolEntry, MethodRef}; +use crate::error::{StackTraceElement, VmError}; +use crate::instructions::{Ops, WideData}; +use crate::objects::array::ArrayReference; +use crate::objects::ReferenceKind; +use crate::prim::*; +use crate::value::{LocalVariables, OperandStack, Primitive, Value}; +use crate::vm::Vm; +use crate::{ + array_store, array_store_cast, binary_op, convert_float_to_int, convert_int_narrow, + convert_simple, error, float_cmp, if_int_cmp, if_int_zero, int_div_rem, load, shift_op, store, + unary_op, BaseType, FieldType, VmThread, +}; +use deku::DekuContainerRead; +use log::{info, trace, warn}; +use std::fmt::{Display, Formatter}; +use std::sync::Arc; + +/// Represents a JVM stack frame for method execution. +/// +/// A frame contains all the execution state needed to run a single method: +/// - Program counter (PC) tracking the current bytecode instruction +/// - Operand stack for intermediate values during computation +/// - Local variables for method parameters and local vars +/// - Reference to the constant pool for the class +/// - The bytecode to execute +/// - Reference to the thread executing this frame +#[derive(Clone)] +pub struct Frame { + /// Program counter - index of the current bytecode instruction + pc: i64, + /// Operand stack for intermediate values + stack: OperandStack, + /// Local variables (includes method parameters) + vars: LocalVariables, + /// Constant pool from the class file + pool: Arc>, + + /// The bytecode instructions for this method + bytecode: Bytecode, + + /// The thread executing this frame + thread: Arc, + // The mod being invoked + pub method_ref: MethodRef, + + pub class: Arc, + line_number_table: Option>, +} + +impl Display for Frame { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!( + f, + "PC: {}\nStack: {:?}\nVars: {:?}", + self.pc, self.stack, self.vars + ) + } +} + +impl Frame { + 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!("Why we pushing a pad?") + } + } + } + pub(crate) fn current_line_number(&self) -> Option { + let table = self.line_number_table.as_ref()?; + // Find the entry where start_pc <= current pc + table + .iter() + .rev() + .find(|entry| { + entry.start_pc as i64 <= self.pc + // (*start_pc as i64) <= self.pc) + }) + .map(|entry| entry.line_number) + } + + // fn load_constant(index: u8) {} + pub(crate) fn new( + class: Arc, + method_ref: MethodRef, + code_attr: CodeAttribute, + pool: Arc>, + locals: Vec, + vm: Arc, + line_number_table: Option>, + ) -> 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(); + let mut buf = Vec::new(); + buf.extend_from_slice(&bytes); + buf.extend_from_slice(&code_attr.code.clone()); + let (_rest, bytecode) = Bytecode::from_bytes((buf.as_ref(), 0)).unwrap(); + Frame { + pc: 0, + stack: OperandStack::with_capacity(max_stack), + vars: LocalVariables::from_args(locals, max_local), + pool, + bytecode, + thread, + method_ref, + class, + line_number_table, + } + } + pub(crate) fn execute(&mut self) -> Result, error::VmError> { + // todo remove + if self.method_ref.name.eq("") { + if self.class.this_class.contains("IBM437") { + println!("[DEBUG] START"); + } + } + let binding = self.bytecode.code.clone(); + loop { + let (offset, op) = self.next().expect("No ops :("); + // trace!("pre set: {}", self.pc); + self.pc = offset as i64; + // trace!("post set: {}", self.pc); + trace!("Executing Op: {:?}", 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(ExecutionResult::Advance(offset)) => { + info!("pre offset: {}", self.pc); + self.pc += offset as i64; + info!("post offset: {}", self.pc); + } + Ok(_) => self.pc += 1, + Err(x) => { + return Err(x.with_frame(StackTraceElement { + class: self.class.this_class.clone(), + method: self.method_ref.name.clone(), + file: self.class.source_file.clone(), + line: self.current_line_number(), + })); + } + } + 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(", ") + ); + } + } + + fn next(&mut self) -> Option<(u16, Ops)> { + self.bytecode + .code + .iter() + .find(|(offset, op)| *offset as i64 >= self.pc) + .map(|op| op.clone()) + } +} + +enum ExecutionResult { + Continue, + Advance(i32), + Return(()), + ReturnValue(Value), +} + +impl Frame { + 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); + Ok(ExecutionResult::Continue) + } + + Ops::iconst_m1 => { + self.stack.push(Value::from(-1i32)); + Ok(ExecutionResult::Continue) + } + + Ops::iconst_0 => { + self.stack.push(Value::from(0i32)); + Ok(ExecutionResult::Continue) + } + + Ops::iconst_1 => { + self.stack.push(Value::from(1i32)); + Ok(ExecutionResult::Continue) + } + + Ops::iconst_2 => { + self.stack.push(Value::from(2i32)); + Ok(ExecutionResult::Continue) + } + + Ops::iconst_3 => { + self.stack.push(Value::from(3i32)); + Ok(ExecutionResult::Continue) + } + + Ops::iconst_4 => { + self.stack.push(4.into()); + Ok(ExecutionResult::Continue) + } + + Ops::iconst_5 => { + 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()); + Ok(ExecutionResult::Continue) + } + 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), + + Ops::ldc2_w(index) => { + let val = self.pool.get_constant(index)?; + trace!("\tLoading constant: {}", val); + let resolved = match val { + ConstantPoolEntry::Double(x) => Some(Value::from(*x)), + ConstantPoolEntry::Long(x) => Some(Value::from(*x)), + _ => None, + }; + if let Some(x) = resolved { + self.stack.push(x); + // on second thoughts, i dont think that's right + // self.stack.push(Value::Reference(None)); + }; + Ok(ExecutionResult::Continue) + } + + // loads + + //iload + Ops::iload(index) => { + load!(self, i, index as usize) + } + Ops::iload_0 => { + load!(self, i, 0) + } + Ops::iload_1 => { + load!(self, i, 1) + } + Ops::iload_2 => { + load!(self, i, 2) + } + Ops::iload_3 => { + load!(self, i, 3) + } + Ops::lload(index) => { + load!(self, l, index as usize) + } + Ops::lload_0 => { + load!(self, l, 0) + } + Ops::lload_1 => { + load!(self, l, 1) + } + Ops::lload_2 => { + load!(self, l, 2) + } + Ops::lload_3 => { + load!(self, l, 3) + } + Ops::fload(index) => { + load!(self, f, index as usize) + } + Ops::fload_0 => { + load!(self, f, 0) + } + Ops::fload_1 => { + load!(self, f, 1) + } + Ops::fload_2 => { + load!(self, f, 2) + } + Ops::fload_3 => { + load!(self, f, 3) + } + Ops::dload(index) => { + load!(self, d, index as usize) + } + Ops::dload_0 => { + load!(self, d, 0) + } + Ops::dload_1 => { + load!(self, d, 1) + } + Ops::dload_2 => { + load!(self, d, 2) + } + Ops::dload_3 => { + load!(self, d, 3) + } + Ops::aload(index) => { + load!(self, a, index as usize) + } + Ops::aload_0 => { + load!(self, a, 0) + } + Ops::aload_1 => { + load!(self, a, 1) + } + Ops::aload_2 => { + load!(self, a, 2) + } + 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().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().get(index); + self.stack.push(Value::from(reference)); + Ok(ExecutionResult::Continue) + } + Ops::baload => { + let Value::Primitive(Primitive::Int(index)) = self.pop()? else { + panic!("index on stack was not int") + }; + let arr_ref = self.pop()?; + let value = match arr_ref { + Value::Reference(Some(ReferenceKind::ArrayReference( + ArrayReference::Byte(arr), + ))) => { + let b = arr.lock().get(index); + Value::from(b as i32) + } + Value::Reference(Some(ReferenceKind::ArrayReference( + ArrayReference::Boolean(arr), + ))) => { + let b = arr.lock().get(index); + Value::from(b as i32) + } + _ => panic!("Reference not on stack or not a byte/boolean array"), + }; + self.push(value); + Ok(ExecutionResult::Continue) + } + 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().get(index); + self.push(Value::from(c as i32)); + Ok(ExecutionResult::Continue) + } + Ops::saload => { + todo!("short array load") + } + + // store + Ops::istore(index) => { + store!(self, i, index as usize) + } + Ops::istore_0 => { + store!(self, i, 0) + } + Ops::istore_1 => { + store!(self, i, 1) + } + Ops::istore_2 => { + store!(self, i, 2) + } + Ops::istore_3 => { + store!(self, i, 3) + } + + Ops::fstore(index) => { + store!(self, f, index as usize) + } + Ops::fstore_0 => { + store!(self, f, 0) + } + Ops::fstore_1 => { + store!(self, f, 1) + } + Ops::fstore_2 => { + store!(self, f, 2) + } + Ops::fstore_3 => { + store!(self, f, 3) + } + + Ops::dstore(index) => { + store!(self, d, index as usize) + } + Ops::dstore_0 => { + store!(self, d, 0) + } + Ops::dstore_1 => { + store!(self, d, 1) + } + Ops::dstore_2 => { + store!(self, d, 2) + } + Ops::dstore_3 => { + store!(self, d, 3) + } + + Ops::lstore(index) => { + store!(self, l, index as usize) + } + Ops::lstore_0 => { + store!(self, l, 0) + } + Ops::lstore_1 => { + store!(self, l, 1) + } + Ops::lstore_2 => { + store!(self, l, 2) + } + Ops::lstore_3 => { + store!(self, l, 3) + } + + Ops::astore(index) => { + store!(self, a, index as usize) + } + Ops::astore_0 => { + store!(self, a, 0) + } + Ops::astore_1 => { + store!(self, a, 1) + } + Ops::astore_2 => { + store!(self, a, 2) + } + Ops::astore_3 => { + store!(self, a, 3) + } + 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.pop()? else { + panic!("index on stack was not int") + }; + let Value::Reference(Some(ReferenceKind::ArrayReference(ArrayReference::Object( + arr, + )))) = self.pop()? + else { + panic!("Reference not on stack or not an int array") + }; + arr.lock().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 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 + // 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::InvariantError( + "iinc requires integer value".to_string(), + )) + } + } + + // Conversions + 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) => { + let Value::Reference(ref2) = self.pop()? else { + return Err(VmError::StackError("stack_not_ref".to_string())); + }; + let Value::Reference(ref1) = self.pop()? else { + return Err(VmError::StackError("stack_not_ref".to_string())); + }; + + let equal = match (&ref1, &ref2) { + (None, None) => true, + (Some(a), Some(b)) => a.id() == b.id(), + _ => false, + }; + + if equal { + Ok(ExecutionResult::Advance(offset as i32)) + } else { + Ok(ExecutionResult::Continue) + } + } + Ops::if_acmpne(offset) => { + let Value::Reference(ref2) = self.pop()? else { + return Err(VmError::StackError("stack_not_ref".to_string())); + }; + let Value::Reference(ref1) = self.pop()? else { + return Err(VmError::StackError("stack_not_ref".to_string())); + }; + + let equal = match (&ref1, &ref2) { + (None, None) => true, + (Some(a), Some(b)) => a.id() == b.id(), + _ => false, + }; + + if equal { + Ok(ExecutionResult::Continue) + } else { + Ok(ExecutionResult::Advance(offset as i32)) + } + } + // Control + Ops::goto(offset) => Ok(ExecutionResult::Advance(offset as i32)), + Ops::jsr(_) => { + todo!("jsr") + } + Ops::ret(_) => { + todo!("ret") + } + Ops::tableswitch(data) => { + let index = self.pop()?.try_into_jint()?; + let range = data.low..data.high; + let offset = if range.contains(&index) { + let index = (index - data.low) as usize; + data.offsets[index] + } else { + data.default + }; + + Ok(ExecutionResult::Advance(offset)) + } + Ops::lookupswitch(data) => { + let key = self.pop()?.try_into_jint()?; + let offset = data + .pairs + .iter() + .find(|(match_val, _)| *match_val == key) + .map(|(_, target)| *target) + .unwrap_or(data.default); + + Ok(ExecutionResult::Advance(offset)) + } + 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 + + // get static field + // can init the field + Ops::getstatic(index) => { + let field_ref = self.pool.resolve_field(index)?; + trace!("Getting static field {field_ref:?}"); + let class = self.thread.get_class(&field_ref.class)?; + self.thread.ensure_initialised(&class)?; + let result = class.find_field(&field_ref.name, &field_ref.desc)?; + let constant = { + let mut guard = result.value.lock(); + match &*guard { + Some(v) => v.clone(), + None => { + let default = field_ref.desc.default_value(); + *guard = Some(default.clone()); + default + } + } + }; + self.push(constant); + Ok(ExecutionResult::Continue) + } + + Ops::putstatic(index) => { + let field_ref = self.pool.resolve_field(index)?; + trace!("Putting static field {field_ref:?}"); + + let class = self.thread.get_class(&field_ref.class)?; + // self.thread.ensure_initialised(&class)?; + let static_field = class.find_field(&field_ref.name, &field_ref.desc)?; + let value = self.pop()?; + *static_field.value.lock() = Some(value); + Ok(ExecutionResult::Continue) + } + + Ops::getfield(index) => { + let field_ref = self.pool.resolve_field(index)?; + trace!("Getting field {field_ref:?}"); + + let reference = self.pop()?; + + match reference { + Value::Primitive(_) => Err(VmError::StackError( + "getfield requires object reference, got primitive".into(), + )), + Value::Padding => Err(VmError::InvariantError( + "unexpected padding value on stack".into(), + )), + Value::Reference(None) => { + Err(VmError::StackError("getfield on null reference".into())) + } + Value::Reference(Some(ReferenceKind::ArrayReference(_))) => Err( + VmError::StackError("getfield requires object reference, got array".into()), + ), + Value::Reference(Some(ReferenceKind::ObjectReference(obj))) => { + let val = obj.lock().get_field(&field_ref); + self.push(val); + Ok(ExecutionResult::Continue) + } + } + } + + Ops::putfield(index) => { + let field_ref = self.pool.resolve_field(index)?; + trace!("Setting field {field_ref:?}"); + let value = self.pop()?; + if let Value::Reference(reference) = self.stack.pop().expect("object on stack") { + if let Some(ReferenceKind::ObjectReference(object)) = reference { + object.lock().set_field(&field_ref.name, value); + Ok(ExecutionResult::Continue) + } else { + Err(VmError::StackError("Null pointer exception".to_string())) + } + } else { + Err(VmError::StackError( + "putfield tried to operate on a non object stack value".to_string(), + )) + } + } + Ops::invokevirtual(index) => { + let method_ref = self.pool.resolve_method_ref(index)?; + // the 1 represents the receiver + let args_count = method_ref.desc.parameters.len() + 1; + let args = self.stack.pop_n(args_count)?; + let refe = args + .first() + .expect("Must have reciever") + .as_ref_kind() + .expect("Must be ref"); + let class = refe.class(); + + let result = self + .thread + .invoke_virtual(method_ref, class.clone(), args)?; + if let Some(val) = result { + self.push(val) + } + + Ok(ExecutionResult::Continue) + } + + Ops::invokespecial(index) => { + // todo verify change to interface method ref + let method_ref = self + .pool + .resolve_method_ref(index) + .or_else(|e| self.pool.resolve_interface_method_ref(index).map_err(|_| e))?; + + // the 1 represents the receiver + // arg width?????? + let args_count = method_ref.desc.parameters.len() + 1; + let args = self.stack.pop_n(args_count)?; + + let result = self.thread.invoke(method_ref, args)?; + if let Some(val) = result { + self.push(val) + } + + Ok(ExecutionResult::Continue) + } + + Ops::invokestatic(index) => { + let method_ref = self + .pool + .resolve_method_ref(index) + .or_else(|e| self.pool.resolve_interface_method_ref(index).map_err(|_| e))?; + let class = self.thread.get_class(&method_ref.class)?; + self.thread.ensure_initialised(&class)?; + + let args_count = method_ref.desc.parameters.len(); + let args = self.stack.pop_n(args_count)?; + + let result = self.thread.invoke(method_ref, args)?; + if let Some(val) = result { + self.push(val) + } + Ok(ExecutionResult::Continue) + } + + Ops::invokeinterface(index, _count, _zero) => { + let method_ref = self.pool.resolve_interface_method_ref(index)?; + + // the 1 represents the receiver + let args_count = method_ref.desc.parameters.len() + 1; + let args = self.stack.pop_n(args_count)?; + let refe = args + .first() + .expect("Must have reciever") + .as_ref_kind() + .expect("Must be ref"); + let class = refe.class(); + + let result = self + .thread + .invoke_virtual(method_ref, class.clone(), args)?; + if let Some(val) = result { + self.push(val) + } + + Ok(ExecutionResult::Continue) + } + + Ops::invokedynamic(_, _) => { + todo!("invokeDynamic") + } + + // can init class + Ops::new(index) => { + let class = self.pool.resolve_class_name(index)?; + + let class = self.thread.get_class(&class)?; + self.thread.ensure_initialised(&class)?; + let object = self.thread.gc.write().new_object(class); + self.stack + .push(Value::Reference(Some(ReferenceKind::from(object)))); + Ok(ExecutionResult::Continue) + } + + Ops::newarray(array_type) => { + let array_class = self.thread.get_class(&array_type.to_string())?; + + 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().new_primitive_array( + array_class, + array_type.clone(), + count, + ); + self.stack + .push(Value::Reference(Some(ReferenceKind::ArrayReference(array)))); + Ok(ExecutionResult::Continue) + } + Ops::anewarray(index) => { + let element_class_name = self.pool.resolve_class_name(index)?; + // Convert element type to array type descriptor + let array_class_name = if element_class_name.starts_with('[') { + // Element is already an array type, just prepend [ + format!("[{}", element_class_name) + } else { + // Element is a class type, use [L; + format!("[L{};", element_class_name) + }; + let array_class = self.thread.get_class(&array_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().new_object_array(array_class, count); + self.stack + .push(Value::Reference(Some(ReferenceKind::ArrayReference(array)))); + Ok(ExecutionResult::Continue) + } + Ops::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(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) => { + let obj_class = obj.lock().class.clone(); + if obj_class.is_assignable_into(into_class.clone()) { + self.push(popped); + Ok(ExecutionResult::Continue) + } else { + Err(VmError::Exception { + message: format!( + "ClassCastException: {} cannot be cast to {}", + obj_class.this_class, into_class.this_class + ), + stack_trace: vec![], + }) + } + } + ReferenceKind::ArrayReference(arr) => { + let array_class = arr.class(); + if array_class.is_assignable_into(into_class.clone()) { + self.push(popped); + Ok(ExecutionResult::Continue) + } else { + Err(VmError::Exception { + message: format!( + "ClassCastException: {} cannot be cast to {}", + array_class.this_class, into_class.this_class + ), + stack_trace: vec![], + }) + } + } + } + } else { + self.push(popped); + Ok(ExecutionResult::Continue) + } + } + 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 { + let assignable = match x { + ReferenceKind::ObjectReference(obj) => { + obj.lock().class.is_assignable_into(into_class) + } + ReferenceKind::ArrayReference(arr) => { + arr.class().is_assignable_into(into_class) + } + }; + self.push(assignable.into()); + } else { + self.push(false.into()); + } + Ok(ExecutionResult::Continue) + } + Ops::monitorenter => { + let kind = self.pop()?.as_ref_kind()?; + self.thread + .gc + .read() + .monitor_enter(self.thread.id, kind.id()); + Ok(ExecutionResult::Continue) + } + Ops::monitorexit => { + let kind = self.pop()?.as_ref_kind()?; + self.thread + .gc + .read() + .monitor_exit(self.thread.id, kind.id())?; + Ok(ExecutionResult::Continue) + } + + Ops::wide(data) => match data { + WideData::iinc(idx, increment) => { + println!("index: {idx}, inc: {increment}"); + let int = self.vars.get(idx as usize).clone().try_into_jint()?; + let new_val = int + increment as i32; + self.vars.set(idx as usize, new_val.into()); + Ok(ExecutionResult::Continue) + } + WideData::iload(idx) => { + load!(self, i, idx as usize) + } + WideData::lload(idx) => { + load!(self, l, idx as usize) + } + WideData::fload(idx) => { + load!(self, f, idx as usize) + } + WideData::dload(idx) => { + load!(self, d, idx as usize) + } + WideData::aload(idx) => { + load!(self, a, idx as usize) + } + WideData::istore(idx) => { + store!(self, i, idx as usize) + } + WideData::lstore(idx) => { + store!(self, l, idx as usize) + } + WideData::fstore(idx) => { + store!(self, f, idx as usize) + } + WideData::dstore(idx) => { + store!(self, d, idx as usize) + } + WideData::astore(idx) => { + store!(self, a, idx as usize) + } + WideData::ret(idx) => { + todo!("ret") + } + }, + Ops::multianewarray(_, _) => { + todo!("multianewarray") + } + Ops::ifnull(offset) => { + if let Value::Reference(value) = self.pop()? { + if value.is_none() { + Ok(ExecutionResult::Advance(offset as i32)) + } 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 as i32)) + } 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_class(&name)?; + let class_ref = self.thread.gc.read().get( + *class + .mirror + .get() + .expect(&format!("Mirror unintialised {}", class.this_class)), + ); + Value::from(class_ref) + } + ConstantPoolEntry::String(x) => { + let utf_ref = self.pool.get_string(x.string_index)?; + trace!("{utf_ref}"); + 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 + } + _ => { + let pools_same = Arc::ptr_eq(&self.pool, &self.class.constant_pool); + // Print first 15 constant pool entries for debugging + let pool_entries: Vec<_> = self + .pool + .iter() + .take(15) + .enumerate() + .map(|(i, e)| format!(" [{}]: {:?}", i + 1, e)) + .collect(); + panic!( + "Cannot load constant, is not of loadable type: {:?}.\n\ + Method: {}.{}\n\ + Frame class: {}\n\ + Pool and class.pool same Arc: {}\n\ + Index requested: {}\n\ + Current PC: {}\n\ + Constant pool entries (first 15):\n{}", + thing, + self.method_ref.class, + self.method_ref.name, + self.class.this_class, + pools_same, + index, + self.pc, + pool_entries.join("\n") + ); + } + }; + self.push(resolved); + Ok(ExecutionResult::Continue) + } +} diff --git a/crates/core/src/instructions.rs b/crates/core/src/instructions.rs index e45361e..7d4338e 100644 --- a/crates/core/src/instructions.rs +++ b/crates/core/src/instructions.rs @@ -1,11 +1,11 @@ -use std::fmt::{Display, Formatter}; -use crate::attributes::ArrayType; +use crate::attributes::{ArrayType, LookupSwitchData, TableSwitchData}; use deku_derive::DekuRead; +use std::fmt::{Display, Formatter}; //noinspection SpellCheckingInspection #[allow(non_camel_case_types)] #[derive(Clone, PartialEq, Debug, DekuRead)] -#[deku(id_type = "u8", ctx = "_endian: deku::ctx::Endian", endian = "big")] +#[deku(id_type = "u8", ctx = "byte_offset: u16", endian = "big")] pub enum Ops { // Constants #[deku(id = 0x00)] @@ -366,9 +366,9 @@ pub enum Ops { ret(u8), // #[deku(id = 0xaa)] - tableswitch, + tableswitch(#[deku(ctx = "byte_offset")] TableSwitchData), #[deku(id = 0xab)] - lookupswitch, + lookupswitch(#[deku(ctx = "byte_offset")] LookupSwitchData), #[deku(id = 0xac)] ireturn, #[deku(id = 0xad)] @@ -424,7 +424,7 @@ pub enum Ops { monitorexit, //extended #[deku(id = 0xC4)] - wide, + wide(#[deku(ctx = "byte_offset")] WideData), #[deku(id = 0xC5)] multianewarray(u16, u8), #[deku(id = 0xC6)] @@ -443,6 +443,40 @@ pub enum Ops { #[deku(id = 0xFF)] impdep2, } +//noinspection SpellCheckingInspection +#[allow(non_camel_case_types)] +#[derive(Clone, PartialEq, Debug, DekuRead)] +#[deku( + id_type = "u8", + ctx = "_endian: deku::ctx::Endian, _byte_offset: u16", + endian = "big" +)] +pub enum WideData { + #[deku(id = 0x84)] + iinc(u16, i16), + #[deku(id = 0x15)] + iload(u16), + #[deku(id = 0x16)] + lload(u16), + #[deku(id = 0x17)] + fload(u16), + #[deku(id = 0x18)] + dload(u16), + #[deku(id = 0x19)] + aload(u16), + #[deku(id = 0x36)] + istore(u16), + #[deku(id = 0x37)] + lstore(u16), + #[deku(id = 0x38)] + fstore(u16), + #[deku(id = 0x39)] + dstore(u16), + #[deku(id = 0x3A)] + astore(u16), + #[deku(id = 0xA9)] + ret(u16), +} impl Display for Ops { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { @@ -632,8 +666,8 @@ impl Display for Ops { 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::tableswitch(_) => write!(f, "tableswitch"), + Ops::lookupswitch(_) => write!(f, "lookupswitch"), Ops::ireturn => write!(f, "ireturn"), Ops::lreturn => write!(f, "lreturn"), Ops::freturn => write!(f, "freturn"), @@ -662,7 +696,7 @@ impl Display for Ops { Ops::monitorexit => write!(f, "monitorexit"), // Extended - Ops::wide => write!(f, "wide"), + 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), @@ -675,4 +709,4 @@ impl Display for Ops { Ops::impdep2 => write!(f, "impdep2"), } } -} \ No newline at end of file +} diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index 22d5b51..d4b9007 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -15,335 +15,35 @@ //! - [`MethodDescriptor`] - Method signature information //! - [`FieldType`] - Field type information -use crate::error::{StackTraceElement, VmError}; -use crate::value::{OperandStack, Primitive}; -use crate::value::LocalVariables; -use crate::attributes::{ArrayType, Attribute, CodeAttribute, LineNumberTableEntry}; use crate::class_file::constant_pool::ConstantPoolExt; -use crate::class_file::constant_pool::{ConstantPoolError, ConstantPoolGet}; -use crate::class_file::{Bytecode, ClassFile, ConstantPoolEntry, MethodData, MethodRef}; -use crate::objects::array::ArrayReference; +use crate::class_file::constant_pool::ConstantPoolGet; pub use crate::thread::VmThread; -use ::jni::sys::{jbyte, jchar, jdouble, jfloat, jint, jlong, jshort}; -use deku::{DekuContainerRead, DekuError}; +use deku::DekuContainerRead; use deku_derive::{DekuRead, DekuWrite}; -use env_logger::Builder; -use instructions::Ops; +use frame::Frame; use itertools::Itertools; -use log::{error, info, trace, warn, LevelFilter}; -use objects::object::ReferenceKind; +use log::error; +use std::borrow::Cow; use std::fmt::{Debug, Display, Formatter}; -use std::fs::File; use std::io::Read; use std::ops::{BitAnd, Deref}; -use std::sync::{Arc, Mutex}; use value::Value; -use vm::Vm; -use crate::class::RuntimeClass; - mod attributes; mod bimage; mod class; pub mod class_file; mod class_loader; +pub mod error; +mod frame; mod instructions; -mod jni; mod macros; -mod native_libraries; +pub mod native; pub mod objects; mod prim; mod rng; mod thread; pub mod value; pub mod vm; -pub mod error; -// 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::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 mut cl = ClassLoader::new().unwrap(); - // cl.load_class("org.example.App").expect("TODO: panic message"); - // let clazz = cl.get_or_load("org.example.App").unwrap(); - // for (i, (k, v)) in cl.classes().iter().enumerate() { - // std::fs::write(format!("./output/{}-{}.txt", i, class_loader::path_to_dot(k)), format!("{}\n{}", k, v)).unwrap(); - // } - - /*let mut class_file = File::open("./data/org/example/Main.class").unwrap(); - let mut bytes = Vec::new(); - class_file.read_to_end(&mut bytes).unwrap(); - let (_rest, clazz) = ClassFile::from_bytes((bytes.as_ref(), 0)).unwrap(); - let method = clazz.methods.get(1).unwrap().clone(); - let code = method - .attributes - .iter() - .find_map(|x| { - if let Some(Attribute::Code(code_attr)) = &x.get(&clazz) { - Some(code_attr.clone()) - } else { - None - } - }) - .unwrap(); - // let frame = Frame::new(); - // println!("{}", code); - let mut buf = Vec::new(); - let bytes = code.code_length.to_be_bytes(); - buf.extend_from_slice(&bytes); - buf.extend_from_slice(&code.code.clone()); - let (_rest, ops) = Bytecode::from_bytes((buf.as_ref(), 0)).unwrap(); - let var_table = code - .attributes - .iter() - .find_map(|x| { - if let Some(Attribute::LocalVariableTable(varTableAttr)) = &x.get(&clazz) { - Some(varTableAttr.clone()) - } else { - None - } - }) - .unwrap(); - println!("{}", clazz);*/ - // let pool = clazz.constant_pool; - // let mut vm = Vm::new("org/example/Main"); - - // println!("{:?}", ops); - // println!("{:?}", var_table.local_variable_table); - // vm.method(ops.clone(), code, var_table); -} - -// impl Value { -// fn Int(i: i32) -> Value { -// Value::Primitive(Primitive::Int(i)) -// } -// -// fn Float(f: f32) -> Value { -// Value::Primitive(Primitive::Float(f)) -// } -// -// fn Double(d: f64) -> Value { -// Value::Primitive(Primitive::Double(d)) -// } -// -// fn Long(l: i64) -> Value { -// Value::Primitive(Primitive::Long(l)) -// } -// -// fn Char(c: u16) -> Value { -// Value::Primitive(Primitive::Char(c)) -// } -// -// fn Boolean(b: bool) -> Value { -// Value::Primitive(Primitive::Boolean(b)) -// } -// -// fn Byte(b: i8) -> Value { -// Value::Primitive(Primitive::Byte(b)) -// } -// -// fn Short(s: i16) -> Value { -// Value::Primitive(Primitive::Short(s)) -// } -// } - -/// Represents a JVM stack frame for method execution. -/// -/// A frame contains all the execution state needed to run a single method: -/// - Program counter (PC) tracking the current bytecode instruction -/// - Operand stack for intermediate values during computation -/// - Local variables for method parameters and local vars -/// - 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: i64, - /// Operand stack for intermediate values - stack: OperandStack, - /// Local variables (includes method parameters) - vars: LocalVariables, - /// Constant pool from the class file - pool: Arc>, - - /// The bytecode instructions for this method - bytecode: Bytecode, - - /// The thread executing this frame - thread: Arc, - // The mod being invoked - method_ref: MethodRef, - - class: Arc, - line_number_table: Option> -} - -impl Display for Frame { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!( - f, - "PC: {}\nStack: {:?}\nVars: {:?}", - self.pc, self.stack, self.vars - ) - } -} - -// println!("State:\n\tStack: {:?}\n\tLocals :{:?}\n", self.stack, self.vars) } - -impl Frame { - 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 current_line_number(&self) -> Option { - let table = self.line_number_table.as_ref()?; - // Find the entry where start_pc <= current pc - table.iter() - .rev() - .find(|entry| { - entry.start_pc as i64 <= self.pc - // (*start_pc as i64) <= self.pc) - }) - .map(|entry| { - entry.line_number - }) - } - - // fn load_constant(index: u8) {} - fn new( - class: Arc, - method_ref: MethodRef, - code_attr: CodeAttribute, - pool: Arc>, - locals: Vec, - vm: Arc, - line_number_table: Option> - ) -> 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(); - let mut buf = Vec::new(); - buf.extend_from_slice(&bytes); - buf.extend_from_slice(&code_attr.code.clone()); - let (_rest, bytecode) = Bytecode::from_bytes((buf.as_ref(), 0)).unwrap(); - Frame { - pc: 0, - stack: OperandStack::with_capacity(max_stack), - vars: LocalVariables::from_args(locals, max_local), - pool, - bytecode, - thread, - method_ref, - class, - line_number_table - } - } - fn execute(&mut self) -> Result, error::VmError> { - let binding = self.bytecode.code.clone(); - 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.clone()); - match result { - Ok(ExecutionResult::Return(())) => return Ok(None), - Ok(ExecutionResult::ReturnValue(val)) => return Ok(Some(val)), - 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) => { - return Err(x.with_frame(StackTraceElement { - class: self.class.this_class.clone(), - method: self.method_ref.name.clone(), - file: self.class.source_file.clone(), - line: self.current_line_number(), - })); - eprintln!("[DEBUG] frame.thread.id = {:?}", self.thread.id); - eprintln!("[DEBUG] frame.thread.frame_stack.len = {}", - self.thread.frame_stack.lock().unwrap().len()); - let objs = self.thread.gc.read().unwrap() - .objects - .iter() - .map(|(x, y)| format!("{x} : {y}")) - .collect::>(); - let len = objs.len().clone(); - error!("Heap dump: len: {len} objs:\n{objs:#?}"); - error!("Error in method: {:?}", self.method_ref); - error!("Error in VM: {x}"); - self.thread.print_stack_trace(); - 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(error::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. /// @@ -421,9 +121,9 @@ impl From for BaseType { /// - `([Ljava/lang/String;)V` - Takes String array, returns void (public static void main) #[derive(Debug, PartialEq, Clone)] pub struct MethodDescriptor { - parameters: Vec, + pub parameters: Vec, // none = void/v - return_type: Option, + pub return_type: Option, } impl MethodDescriptor { @@ -437,25 +137,6 @@ impl MethodDescriptor { MethodDescriptor::parse("([Ljava/lang/String;)V").unwrap() } - pub fn arg_width(&self) -> usize { - self.parameters.iter().fold(0, |acc, e| { - acc + match e { - FieldType::Base(base) => match base { - BaseType::Byte => 1, - BaseType::Char => 1, - BaseType::Double => 2, - BaseType::Float => 1, - BaseType::Int => 1, - BaseType::Long => 2, - BaseType::Short => 1, - BaseType::Boolean => 1, - }, - FieldType::ClassType(_) => 1, - FieldType::ArrayType(_) => 1, - } - }) - } - pub fn param_string(&self) -> String { self.parameters.iter().join("") } @@ -500,6 +181,14 @@ impl Display for MethodDescriptor { } } +pub fn stack_used() -> usize { + static STACK_BASE: std::sync::OnceLock = std::sync::OnceLock::new(); + let local = 0u8; + let current = &local as *const _ as usize; + let base = *STACK_BASE.get_or_init(|| current); + base.saturating_sub(current) +} + /// Represents types that can be used for fields in the JVM. /// /// Field types can be: @@ -519,1182 +208,45 @@ pub enum FieldType { ArrayType(Box), } -enum ExecutionResult { - Continue, - Advance(i16), - Return(()), - ReturnValue(Value), -} - -impl Frame { - 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); - Ok(ExecutionResult::Continue) - } - - Ops::iconst_m1 => { - self.stack.push(Value::from(-1i32)); - Ok(ExecutionResult::Continue) - } - - Ops::iconst_0 => { - self.stack.push(Value::from(0i32)); - Ok(ExecutionResult::Continue) - } - - Ops::iconst_1 => { - self.stack.push(Value::from(1i32)); - Ok(ExecutionResult::Continue) - } - - Ops::iconst_2 => { - self.stack.push(Value::from(2i32)); - Ok(ExecutionResult::Continue) - } - - Ops::iconst_3 => { - self.stack.push(Value::from(3i32)); - Ok(ExecutionResult::Continue) - } - - Ops::iconst_4 => { - self.stack.push(4.into()); - Ok(ExecutionResult::Continue) - } - - Ops::iconst_5 => { - 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()); - Ok(ExecutionResult::Continue) - } - 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), - - Ops::ldc2_w(index) => { - let val = self.pool.get_constant(index)?; - trace!("\tLoading constant: {}", val); - let resolved = match val { - ConstantPoolEntry::Double(x) => Some(Value::from(*x)), - ConstantPoolEntry::Long(x) => Some(Value::from(*x)), - _ => None, - }; - if let Some(x) = resolved { - self.stack.push(x); - // on second thoughts, i dont think that's right - // self.stack.push(Value::Reference(None)); - }; - Ok(ExecutionResult::Continue) - } - - // loads - - //iload - Ops::iload(index) => { - load!(self, i, index as usize) - } - Ops::iload_0 => { - load!(self, i, 0) - } - Ops::iload_1 => { - load!(self, i, 1) - } - Ops::iload_2 => { - load!(self, i, 2) - } - Ops::iload_3 => { - load!(self, i, 3) - } - Ops::lload(index) => { - load!(self, l, index as usize) - } - Ops::lload_0 => { - load!(self, l, 0) - } - Ops::lload_1 => { - load!(self, l, 1) - } - Ops::lload_2 => { - load!(self, l, 2) - } - Ops::lload_3 => { - load!(self, l, 3) - } - Ops::fload(index) => { - load!(self, f, index as usize) - } - Ops::fload_0 => { - load!(self, f, 0) - } - Ops::fload_1 => { - load!(self, f, 1) - } - Ops::fload_2 => { - load!(self, f, 2) - } - Ops::fload_3 => { - load!(self, f, 3) - } - Ops::dload(index) => { - load!(self, d, index as usize) - } - Ops::dload_0 => { - load!(self, d, 0) - } - Ops::dload_1 => { - load!(self, d, 1) - } - Ops::dload_2 => { - load!(self, d, 2) - } - Ops::dload_3 => { - load!(self, d, 3) - } - Ops::aload(index) => { - load!(self, a, index as usize) - } - Ops::aload_0 => { - load!(self, a, 0) - } - Ops::aload_1 => { - load!(self, a, 1) - } - Ops::aload_2 => { - load!(self, a, 2) - } - 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 => { - let Value::Primitive(Primitive::Int(index)) = self.pop()? else { - panic!("index on stack was not int") - }; - let arr_ref = self.pop()?; - let value = match arr_ref { - Value::Reference(Some(ReferenceKind::ArrayReference(ArrayReference::Byte(arr)))) => { - let b = arr.lock().unwrap().get(index); - Value::from(b as i32) - } - Value::Reference(Some(ReferenceKind::ArrayReference(ArrayReference::Boolean(arr)))) => { - let b = arr.lock().unwrap().get(index); - Value::from(b as i32) - } - _ => panic!("Reference not on stack or not a byte/boolean array"), - }; - self.push(value); - Ok(ExecutionResult::Continue) - } - 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 as i32)); - Ok(ExecutionResult::Continue) - } - Ops::saload => { - todo!("short array load") - } - - // store - Ops::istore(index) => { - store!(self, i, index as usize) - } - Ops::istore_0 => { - store!(self, i, 0) - } - Ops::istore_1 => { - store!(self, i, 1) - } - Ops::istore_2 => { - store!(self, i, 2) - } - Ops::istore_3 => { - store!(self, i, 3) - } - - Ops::fstore(index) => { - store!(self, f, index as usize) - } - Ops::fstore_0 => { - store!(self, f, 0) - } - Ops::fstore_1 => { - store!(self, f, 1) - } - Ops::fstore_2 => { - store!(self, f, 2) - } - Ops::fstore_3 => { - store!(self, f, 3) - } - - Ops::dstore(index) => { - store!(self, d, index as usize) - } - Ops::dstore_0 => { - store!(self, d, 0) - } - Ops::dstore_1 => { - store!(self, d, 1) - } - Ops::dstore_2 => { - store!(self, d, 2) - } - Ops::dstore_3 => { - store!(self, d, 3) - } - - Ops::lstore(index) => { - store!(self, l, index as usize) - } - Ops::lstore_0 => { - store!(self, l, 0) - } - Ops::lstore_1 => { - store!(self, l, 1) - } - Ops::lstore_2 => { - store!(self, l, 2) - } - Ops::lstore_3 => { - store!(self, l, 3) - } - - Ops::astore(index) => { - store!(self, a, index as usize) - } - Ops::astore_0 => { - store!(self, a, 0) - } - Ops::astore_1 => { - store!(self, a, 1) - } - Ops::astore_2 => { - store!(self, a, 2) - } - Ops::astore_3 => { - store!(self, a, 3) - } - 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.pop()? else { - panic!("index on stack was not int") - }; - let Value::Reference(Some(ReferenceKind::ArrayReference(ArrayReference::Object( - arr, - )))) = self.pop()? - else { - panic!("Reference not on stack or not an int array") - }; - 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(error::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(error::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(error::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(error::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(error::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 Ok(value) = self.stack.peek() { - self.stack.push(value.clone()); - Ok(ExecutionResult::Continue) - } else { - Err(error::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(error::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(error::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(error::VmError::InvariantError("dup2_x1 form 1: v2 must be category 1".to_string())) - } else { - let v3 = self.pop()?; - if v3.is_wide() { - Err(error::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(error::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(error::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(error::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(error::VmError::InvariantError("swap operated on 2 wide value".to_string())) - } else { - self.push(v1); - self.push(v2); - Ok(ExecutionResult::Continue) - } - } - - // Math - // 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(error::VmError::InvariantError("iinc requires integer value".to_string())) - } - } - - // Conversions - 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(error::VmError::StackError("Expected long".into())); - }; - let Value::Primitive(Primitive::Long(v1)) = self.pop()? else { - return Err(error::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(error::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(error::VmError::stack_not_int()) - } - } - // Control - Ops::goto(offset) => { - Ok(ExecutionResult::Advance(offset)) +impl FieldType { + pub fn default_value(&self) -> Value { + match self { + 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), }, - 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(error::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(error::VmError::InvariantError( - "wrong return instruction for method".to_owned(), - )), - }, - _ => Err(error::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(error::VmError::StackError("Expected reference".into())), - } - } - Ops::freturn => { - let val = self.pop()?; - match val { - Value::Primitive(Primitive::Float(_)) => Ok(ExecutionResult::ReturnValue(val)), - _ => Err(error::VmError::StackError("Expected reference".into())), - } - } - Ops::dreturn => { - let val = self.pop()?; - match val { - Value::Primitive(Primitive::Double(_)) => Ok(ExecutionResult::ReturnValue(val)), - _ => Err(error::VmError::StackError("Expected reference".into())), - } - } - Ops::areturn => { - let val = self.pop()?; - match val { - Value::Reference(_) => Ok(ExecutionResult::ReturnValue(val)), - _ => Err(error::VmError::StackError("Expected reference".into())), - } - } - Ops::return_void => Ok(ExecutionResult::Return(())), - - // References - - // get static field - // can init the field - Ops::getstatic(index) => { - let field_ref = self.pool.resolve_field(index)?; - if field_ref.class.contains("jdk/internal/misc/VM") || field_ref.name.contains("initLevel") { - return Err(VmError::InvariantError("Intentional crash".to_string())) - } - println!("Getting static field {field_ref:?}"); - let init_class = self - .thread - .get_or_resolve_class(&field_ref.class) - .expect("TO hecken work"); - let result = init_class - .find_field(&field_ref.name, &field_ref.desc) - .expect("TO hecken work"); - let constant = result - .value - .lock() - .unwrap() - .clone() - .expect("Static field was not initialised"); - self.push(constant); - Ok(ExecutionResult::Continue) - } - - Ops::putstatic(index) => { - 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) - .expect("TO hecken work"); - let static_field = init_class - .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); - Ok(ExecutionResult::Continue) - } - - Ops::getfield(index) => { - let field_ref = self.pool.resolve_field(index)?; - trace!("Getting field {field_ref:?}"); - let popped = self.pop()?; - match popped { - Value::Primitive(x) => { - Err(error::VmError::StackError("Getfield era".parse().unwrap())) - } - 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(error::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)?; - trace!("Setting field {field_ref:?}"); - 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(ReferenceKind::ObjectReference(object)) = reference { - object.lock().unwrap().set_field(&field_ref.name, value); - Ok(ExecutionResult::Continue) - } else { - Err(error::VmError::StackError("Null pointer exception".to_string())) - } - } else { - Err(error::VmError::StackError( - "putfield tried to operate on a non object stack value".to_string(), - )) - } - - // todo!("op putfield: index - {}", index) - } - Ops::invokevirtual(index) => { - 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.push(val) - } - - 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)?; - - // 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.push(val) - } - - 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)?; - - let args_count = method_ref.desc.parameters.len(); - let args = self.stack.pop_n(args_count)?; - - let result = self.thread.invoke(method_ref, args)?; - if let Some(val) = result { - self.push(val) - } - Ok(ExecutionResult::Continue) - } - - Ops::invokeinterface(index, count, _zero) => { - let method_ref = self.pool.resolve_interface_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 refe = args.first().expect("Must have reciever").as_ref_kind().expect("Must be ref"); - let class = refe.class(); - - let result = self.thread.invoke_virtual(method_ref, class.clone(), args)?; - if let Some(val) = result { - self.push(val) - } - - Ok(ExecutionResult::Continue) - } - - Ops::invokedynamic(_, _) => { - todo!("invokeDynamic") - } - - // can init class - Ops::new(index) => { - let class = self.pool.resolve_class_name(index)?; - - let init_class = self - .thread - .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(ReferenceKind::from(object)))); - Ok(ExecutionResult::Continue) - } - - Ops::newarray(array_type) => { - let array_class = self.thread.get_or_resolve_class(&array_type.to_string())?; - - - - - 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_primitive_array(array_class, array_type.clone(), count); - self.stack - .push(Value::Reference(Some(ReferenceKind::ArrayReference(array)))); - Ok(ExecutionResult::Continue) - } - Ops::anewarray(index) => { - let class_name = self.pool.resolve_class_name(index)?; - println!("{}", class_name); - let array_class = self.thread.get_or_resolve_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(array_class, count); - self.stack - .push(Value::Reference(Some(ReferenceKind::ArrayReference(array)))); - Ok(ExecutionResult::Continue) - } - Ops::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(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) => { - let array_class = arr.class(); - if array_class.is_assignable_into(into_class.clone()) { - self.push(popped); - Ok(ExecutionResult::Continue) - } else { - Err(VmError::Exception { - message: format!("ClassCastException: {} cannot be cast to {}", - array_class.this_class, into_class.this_class), - stack_trace: vec![], - }) - } - } - } - } else { self.push(popped); Ok(ExecutionResult::Continue) } - } - 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) => { - let array_class = arr.class(); - if array_class.is_assignable_into(into_class) { - self.push(1i32.into()); - } else { - self.push(0i32.into()); - } - Ok(ExecutionResult::Continue) - } - } - } else { panic!("yeet") } - } - Ops::monitorenter => { - todo!("monitorenter") - } - Ops::monitorexit => { - todo!("monitorexit") - } - - 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(error::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(error::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") - } + FieldType::ClassType(_) | FieldType::ArrayType(_) => Value::NULL, } } +} - 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.get().expect(&format!("Mirror unintialised {}", class.this_class))); - 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) +impl BaseType { + pub fn as_class_name(&self) -> &'static str { + match self { + BaseType::Byte => "byte", + BaseType::Char => "char", + BaseType::Double => "double", + BaseType::Float => "float", + BaseType::Int => "int", + BaseType::Long => "long", + BaseType::Short => "short", + BaseType::Boolean => "boolean", + } } } -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); +impl FieldType { + pub fn as_class_name(&self) -> Cow<'_, str> { + match self { + FieldType::Base(base) => Cow::Borrowed(base.as_class_name()), + FieldType::ClassType(name) => Cow::Borrowed(name), + FieldType::ArrayType(_) => Cow::Owned(self.to_string()), // reuse Display + } } - - // 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 3d35388..a04a0ff 100644 --- a/crates/core/src/macros.rs +++ b/crates/core/src/macros.rs @@ -8,7 +8,7 @@ macro_rules! store { let index: usize = $index; let value = $self.pop()?; trace!("\tStoring: {value} into local[{index}]"); - $self.vars.set(index , value); + $self.vars.set(index, value); Ok(ExecutionResult::Continue) } @@ -21,7 +21,7 @@ macro_rules! store { let index: usize = $index; let value = $self.pop()?; trace!("\tStoring: {value} into local[{index}]"); - $self.vars.set(index , value); + $self.vars.set(index, value); Ok(ExecutionResult::Continue) } }}; @@ -65,10 +65,11 @@ macro_rules! pool_get_impl { let cp_entry = self.get_constant(index)?; match cp_entry { ConstantPoolEntry::$variant(value) => Ok(value), - _ => Err(ConstantPoolError(format!( - "Expected {} constant at index {}", + other => Err(ConstantPoolError::Generic(format!( + "Expected {} constant at index {}, got {:?} instead", stringify!($variant), - index + index, + other ))), } } @@ -82,7 +83,7 @@ macro_rules! if_int_zero { return Err(VmError::stack_not_int()); }; if v $op 0 { - Ok(ExecutionResult::Advance($offset)) + Ok(ExecutionResult::Advance($offset as i32)) } else { Ok(ExecutionResult::Continue) } @@ -99,7 +100,7 @@ macro_rules! if_int_cmp { return Err(VmError::stack_not_int()); }; if v1 $op v2 { - Ok(ExecutionResult::Advance($offset)) + Ok(ExecutionResult::Advance($offset as i32)) } else { Ok(ExecutionResult::Continue) } @@ -108,136 +109,156 @@ macro_rules! if_int_cmp { #[macro_export] macro_rules! float_cmp { - ($self:expr, $prim:ident, $nan_result:expr) => {{ - let v2 = $self.pop()?; - let v1 = $self.pop()?; + ($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 (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, - } - }; + 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) - }}; + $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) - }}; + ($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) - }}; + ($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) - }}; + ($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) - }}; + ($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().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) - }}; + ($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().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) - }}; + ($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) - }}; + ($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 { @@ -262,15 +283,18 @@ macro_rules! int_div_rem { } #[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 + ($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) + }}; +} diff --git a/crates/core/src/main.rs b/crates/core/src/main.rs index 31bb546..81d7396 100644 --- a/crates/core/src/main.rs +++ b/crates/core/src/main.rs @@ -1,31 +1,54 @@ -use roast_vm_core::vm::Vm; -use roast_vm_core::error::VmError; -use libloading::Library; -use log::{error, LevelFilter}; use colored::Colorize; +use libloading::Library; +use log::{LevelFilter, error}; +use roast_vm_core::error::VmError; +use roast_vm_core::stack_used; +use roast_vm_core::vm::Vm; +use std::time::Instant; + fn main() { - 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(); + std::thread::Builder::new() + .stack_size(8 * 1024 * 1024) + .spawn(run) + .unwrap() + .join() + .unwrap(); +} + +fn run() { + // env_logger::Builder::from_default_env() + // .filter_level(LevelFilter::Trace) + // .filter_module("deku", LevelFilter::Warn) + // .filter_module("roast_vm_core::class_file::class_file", LevelFilter::Info) + // .filter_module("roast_vm_core::attributes", LevelFilter::Info) + // .filter_module("roast_vm_core::instructions", LevelFilter::Info) + // .init(); + stack_used(); 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()); + let start = Instant::now(); match vm.run("org/example/Main") { - Ok(_) => {} - Err(VmError::Exception { message, stack_trace }) => { + Ok(_) => { + println!("took {:?}", start.elapsed()); + } + Err(VmError::Exception { + message, + stack_trace, + }) => { + println!("took {:?}", start.elapsed()); let thread = vm.threads.get(&vm.main_thread_id).unwrap(); - let objs = thread.gc.read().unwrap() + let objs = thread + .gc + .read() .objects .iter() .map(|(x, y)| format!("{x} : {y}")) .collect::>(); - let len = objs.len().clone(); - error!("Heap dump: len: {len} objs:\n{objs:#?}"); + let len = objs.len(); + let bytes_formatted = format_bytes(thread.gc.read().bytes_in_use()); + error!("Heap state:\n\tlen: {len}\n\tsize{bytes_formatted}"); eprintln!("{}: {}", "Exception".red().bold(), message); for elem in &stack_trace { let class_name = elem.class.replace('/', "."); @@ -34,18 +57,11 @@ fn main() { (Some(f), Some(l)) => format!("({}:{})", f, l), (Some(f), None) => format!("({})", f), _ => "(Unknown Source)".to_string(), - }.blue().dimmed(); + } + .blue() + .dimmed(); eprintln!("{} {}.{}{}", at, class_name, elem.method, location); } - /*error!("Exception: {}", message); - for elem in &stack_trace { - let class_name = elem.class.replace('/', "."); - match (&elem.file, elem.line) { - (Some(f), Some(l)) => eprintln!("\tat {}.{}({}:{})", class_name, elem.method, f, l), - (Some(f), None) => eprintln!("\tat {}.{}({})", class_name, elem.method, f), - _ => eprintln!("\tat {}.{}(Unknown Source)", class_name, elem.method), - } - }*/ } Err(e) => { error!("VM Error: {:?}", e); @@ -61,3 +77,21 @@ fn load(filename: &str) -> Library { Library::from(leeb) } + +fn format_bytes(bytes: usize) -> String { + const KIB: f64 = 1024.0; + const MIB: f64 = KIB * 1024.0; + const GIB: f64 = MIB * 1024.0; + + let (size, unit) = if bytes as f64 >= GIB { + (bytes as f64 / GIB, "GiB") + } else if bytes as f64 >= MIB { + (bytes as f64 / MIB, "MiB") + } else if bytes as f64 >= KIB { + (bytes as f64 / KIB, "KiB") + } else { + (bytes as f64, "B") + }; + + format!(" {:.2} {}", size, unit) +} diff --git a/crates/core/src/jni.rs b/crates/core/src/native/jni.rs similarity index 91% rename from crates/core/src/jni.rs rename to crates/core/src/native/jni.rs index 0a60102..2d5ebf9 100644 --- a/crates/core/src/jni.rs +++ b/crates/core/src/native/jni.rs @@ -1,20 +1,21 @@ #![allow(unused_variables)] #![feature(c_variadic)] use crate::class::RuntimeClass; +use crate::class_file::{FieldData, FieldRef}; use crate::objects::array::ArrayReference; +use crate::objects::object::{ObjectReference, ReferenceKind}; use crate::prim::jboolean; use crate::thread::VmThread; use crate::value::{Primitive, Value}; +use crate::{BaseType, FieldType, MethodDescriptor}; +use itertools::Itertools; +use jni::sys::*; use jni::sys::{jclass, jint, jobject, JNINativeInterface_}; use jni::sys::{jstring, JNIEnv}; +use log::{error, info, trace, warn}; use std::ffi::{c_char, CStr, CString}; use std::ptr; -use std::sync::{Arc}; -use crate::class_file::{FieldData, FieldRef}; -use crate::objects::object::{ ObjectReference, ReferenceKind}; -use crate::{BaseType, FieldType, MethodDescriptor}; -use jni::sys::*; -use log::{error, info, trace, warn}; +use std::sync::Arc; const JNI_VERSION_1_1: jint = 0x00010001; const JNI_VERSION_1_2: jint = 0x00010002; @@ -271,19 +272,27 @@ unsafe fn get_thread(env: *mut JNIEnv) -> *const VmThread { } fn resolve_class(thread: &VmThread, clazz: jclass) -> Option> { - let loader = thread.loader.lock().unwrap(); + let loader = thread.loader.lock(); 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 gc = thread.gc.read(); let ReferenceKind::ObjectReference(obj_ref) = gc.get(obj as u32) else { return None; }; Some(obj_ref.clone()) } +fn resolve_reference(thread: &VmThread, obj: jobject) -> Option { + if obj.is_null() { + return None; + } + let gc = thread.gc.read(); + Some(gc.get(obj as u32).clone()) +} + unsafe extern "system" fn jni_get_version(env: *mut JNIEnv) -> jint { JNI_VERSION_24 } @@ -300,7 +309,7 @@ unsafe extern "system" fn get_string_utfchars( // } let thread = &*get_thread(env); let rust_string = { - let gc = thread.gc.read().unwrap(); + let gc = thread.gc.read(); let obj_id = str as u32; @@ -309,8 +318,8 @@ unsafe extern "system" fn get_string_utfchars( _ => return ptr::null(), }; - let obj = obj_ref.lock().unwrap(); - let field_ref = FieldRef{ + let obj = obj_ref.lock(); + let field_ref = FieldRef { class: "java/lang/String".to_string(), name: "value".to_string(), desc: FieldType::ArrayType(Box::new(FieldType::Base(BaseType::Byte))), @@ -322,7 +331,7 @@ unsafe extern "system" fn get_string_utfchars( return ptr::null(); }; - let array = byte_array.lock().unwrap(); + let array = byte_array.lock(); let bytes: Vec = array.backing.iter().map(|&b| b as u8).collect(); @@ -349,7 +358,7 @@ unsafe extern "system" fn get_string_utfchars( 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 gc = thread.gc.read(); let obj_id = obj as u32; @@ -357,7 +366,7 @@ unsafe extern "system" fn get_object_class(env: *mut JNIEnv, obj: jobject) -> jc return ptr::null_mut(); }; - let obj_lock = obj_ref.lock().unwrap(); + let obj_lock = obj_ref.lock(); let class: &Arc = &obj_lock.class; *class.mirror.wait() as jclass @@ -371,12 +380,12 @@ unsafe extern "system" fn register_natives( ) -> jint { trace!("register_natives"); let thread = &*get_thread(env); - let gc = thread.gc.read().unwrap(); + let gc = thread.gc.read(); 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_ref.lock().class.this_class.clone(); let class_name = class_name.replace("/", "_"); // let boop = JClass::from_raw(clazz); @@ -399,12 +408,16 @@ unsafe extern "system" fn register_natives( continue; } + if full_name.contains("getDeclaredConstructors0") { + continue; + } + thread .vm .native_methods .insert(full_name.to_owned(), fn_ptr); - println!( + trace!( "name:{name}, signature:{signature}, fn_ptr{}", fn_ptr.is_null() ) @@ -416,8 +429,6 @@ unsafe extern "system" fn register_natives( // JNI FUNCTION STUBS - All unimplemented functions below // ============================================================================ - - unsafe extern "system" fn define_class( env: *mut JNIEnv, name: *const c_char, @@ -594,7 +605,7 @@ unsafe extern "system" fn 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(); + let first = obj.lock().class.clone(); if first.is_assignable_into(second) { JNI_TRUE } else { @@ -610,29 +621,26 @@ unsafe extern "system" fn get_method_id( ) -> 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(); - }; + // Brief lock just to get what we need + let runtime_class = { + let loader = thread.loader.lock(); + match loader.class_from_mirror_id(class_id) { + Some(rc) => rc.clone(), // Arc clone + None => return ptr::null_mut(), + } + }; // loader released here + + // Now safe to call - no locks held + thread.ensure_initialised(&runtime_class).unwrap(); 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 } @@ -1024,29 +1032,23 @@ unsafe extern "system" fn get_field_id( ) -> jfieldID { trace!("get_field_id"); let thread = &*get_thread(env); - let gc = thread.gc.read().unwrap(); - let loader = thread.loader.lock().unwrap(); + let loader = thread.loader.lock(); 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 { + let ReferenceKind::ObjectReference(class_ref) = thread.gc.read().get(class_id) else { return ptr::null_mut(); }; - + // println!("class ref: {:?}", class_ref); let Some(runtime_class) = loader.class_from_mirror_id(class_id) else { return ptr::null_mut(); }; + thread.ensure_initialised(&runtime_class).unwrap(); 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 } @@ -1067,12 +1069,12 @@ unsafe extern "system" fn 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(), + let field_ref = FieldRef { + class: object.lock().class.this_class.clone(), name: field_data.name.clone(), desc: FieldType::Base(BaseType::Boolean), }; - let val = object.lock().unwrap().get_field(&field_ref); + let val = object.lock().get_field(&field_ref); if let Value::Primitive(Primitive::Boolean(bool)) = val { if bool { JNI_TRUE @@ -1416,7 +1418,23 @@ unsafe extern "system" fn get_static_field_id( name: *const c_char, sig: *const c_char, ) -> jfieldID { - todo!("get_static_field_id") + let thread = &*get_thread(env); + let klass = thread + .loader + .lock() + .class_from_mirror_id(clazz as u32) + .unwrap(); + + thread.ensure_initialised(&klass).unwrap(); + let name = CStr::from_ptr(name).to_str().unwrap(); + // TODO: check sig when to_descriptor exists + + klass + .fields + .iter() + .find(|f| f.flags.ACC_STATIC && f.name == name) + .map(|f| f as *const _ as jfieldID) + .unwrap_or(ptr::null_mut()) } unsafe extern "system" fn get_static_object_field( @@ -1437,7 +1455,7 @@ unsafe extern "system" fn get_static_boolean_field( 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(); + let val = field_again.value.lock().clone(); if let Some(Value::Primitive(Primitive::Boolean(bool))) = val { if bool { JNI_TRUE @@ -1507,11 +1525,22 @@ unsafe extern "system" fn get_static_double_field( unsafe extern "system" fn set_static_object_field( env: *mut JNIEnv, - clazz: jclass, + _clazz: jclass, field_id: jfieldID, value: jobject, ) { - todo!("set_static_object_field") + trace!("set_static_object_field"); + let field_data = &*(field_id as *const FieldData); + + let new_value = if value.is_null() { + Value::Reference(None) + } else { + let thread = &*get_thread(env); + let obj_ref = resolve_object(thread, value).unwrap(); + Value::Reference(Some(ReferenceKind::ObjectReference(obj_ref))) + }; + + *field_data.value.lock() = Some(new_value); } unsafe extern "system" fn set_static_boolean_field( @@ -1602,13 +1631,20 @@ unsafe extern "system" fn new_string( String::new() }); let str_ref = thread.intern_string(&str); // or non-interned path - let str_id = str_ref.lock().unwrap().id; + let str_id = str_ref.lock().id; str_id as jstring } unsafe extern "system" fn get_string_length(env: *mut JNIEnv, str: jstring) -> jsize { - todo!("get_string_length") + trace!("get_string_length"); + let thread = &*get_thread(env); + let string_obj = resolve_object(thread, str).unwrap(); + let gc = thread.gc.read(); + + let rust_str = gc.transmute_string(string_obj).unwrap(); + + rust_str.encode_utf16().count() as jsize } unsafe extern "system" fn get_string_chars( @@ -1630,7 +1666,6 @@ unsafe extern "system" fn 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(); @@ -1646,17 +1681,27 @@ unsafe extern "system" fn new_string_utf(env: *mut JNIEnv, utf: *const c_char) - return ptr::null_mut(); }; - let str_ref = thread.gc.write().unwrap().new_string(byte_array_class, string_class, str); + let str_ref = thread + .gc + .write() + .new_string(byte_array_class, string_class, str); str_ref }; - let str_id = str_ref.lock().unwrap().id; + let str_id = str_ref.lock().id; str_id as jstring } unsafe extern "system" fn get_string_utf_length(env: *mut JNIEnv, str: jstring) -> jsize { - todo!("get_string_utf_length") + trace!("get_string_utf_length"); + let thread = &*get_thread(env); + let string_obj = resolve_object(thread, str).unwrap(); + let gc = thread.gc.read(); + + let rust_str = gc.transmute_string(string_obj).unwrap(); + + rust_str.len() as jsize } unsafe extern "system" fn release_string_utf_chars( @@ -1678,11 +1723,10 @@ unsafe extern "system" fn new_object_array( init: jobject, ) -> jobjectArray { let thread = &*get_thread(env); - let mut gc = thread.gc.write().unwrap(); - let loader = thread.loader.lock().unwrap(); + let loader = thread.loader.lock(); let class_id = clazz as u32; - let ReferenceKind::ObjectReference(class_ref) = gc.get(class_id) else { + let ReferenceKind::ObjectReference(class_ref) = thread.gc.read().get(class_id) else { return ptr::null_mut(); }; @@ -1690,15 +1734,16 @@ unsafe extern "system" fn new_object_array( return ptr::null_mut(); }; - let ArrayReference::Object(arr_ref) = gc.new_object_array(runtime_class, len) else { + let ArrayReference::Object(arr_ref) = thread.gc.write().new_object_array(runtime_class, len) + else { return ptr::null_mut(); }; - let arr_id = arr_ref.lock().unwrap().id; + let arr_id = arr_ref.lock().id; - // let Value::Reference(Some(ReferenceKind::ObjectReference(class_name_string))) = class_ref.lock().unwrap().get_field("name") else { + // let Value::Reference(Some(ReferenceKind::ObjectReference(class_name_string))) = class_ref.lock().get_field("name") else { // return ptr::null_mut(); // }; - // class_name_string.lock().unwrap().get_field() + // class_name_string.lock().get_field() arr_id as jobjectArray } @@ -1720,13 +1765,13 @@ unsafe extern "system" fn set_object_array_element( 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) + thread.gc.read().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)) + let obj_ref = thread.gc.read().get(obj_id); + arr_ref.lock().set(index, Some(obj_ref)) } unsafe extern "system" fn new_boolean_array(env: *mut JNIEnv, len: jsize) -> jbooleanArray { @@ -2080,7 +2125,17 @@ unsafe extern "system" fn get_string_region( len: jsize, buf: *mut jchar, ) { - todo!("get_string_region") + trace!("get_string_region"); + let thread = &*get_thread(env); + let string_obj = resolve_object(thread, str).unwrap(); + let gc = thread.gc.read(); + + let rust_str = gc.transmute_string(string_obj).unwrap(); + + let utf16: Vec = rust_str.encode_utf16().collect(); + let region = &utf16[start as usize..(start + len) as usize]; + + ptr::copy_nonoverlapping(region.as_ptr(), buf, len as usize); } unsafe extern "system" fn get_string_utf_region( @@ -2090,7 +2145,36 @@ unsafe extern "system" fn get_string_utf_region( len: jsize, buf: *mut c_char, ) { - todo!("get_string_utf_region") + trace!( + "get_string_utf_region: start={}, len={}, buf={:?}", + start, + len, + buf + ); + let thread = &*get_thread(env); + let string_obj = resolve_object(thread, str).unwrap(); + let gc = thread.gc.read(); + + let rust_str = gc.transmute_string(string_obj).unwrap(); + trace!( + "get_string_utf_region: rust_str={:?}, rust_str.len()={}", + rust_str, + rust_str.len() + ); + + let utf16: Vec = rust_str.encode_utf16().collect(); + trace!("get_string_utf_region: utf16.len()={}", utf16.len()); + + let region = &utf16[start as usize..(start + len) as usize]; + let region_str = String::from_utf16(region).unwrap(); + trace!( + "get_string_utf_region: region_str.len()={}", + region_str.len() + ); + + ptr::copy_nonoverlapping(region_str.as_ptr(), buf as *mut u8, region_str.len()); + // Add null terminator for compatibility with native code that expects C strings + *buf.add(region_str.len()) = 0; } unsafe extern "system" fn get_primitive_array_critical( @@ -2135,7 +2219,7 @@ unsafe extern "system" fn delete_weak_global_ref(env: *mut JNIEnv, ref_: jweak) } unsafe extern "system" fn exception_check(env: *mut JNIEnv) -> jboolean { - error!("exception_check"); + warn!("exception_check"); JNI_FALSE } diff --git a/crates/core/src/native/mod.rs b/crates/core/src/native/mod.rs new file mode 100644 index 0000000..cd21ec6 --- /dev/null +++ b/crates/core/src/native/mod.rs @@ -0,0 +1,3 @@ +pub mod jni; +mod native_libraries; +pub mod r#unsafe; \ No newline at end of file diff --git a/crates/core/src/native_libraries.rs b/crates/core/src/native/native_libraries.rs similarity index 100% rename from crates/core/src/native_libraries.rs rename to crates/core/src/native/native_libraries.rs diff --git a/crates/core/src/native/unsafe.rs b/crates/core/src/native/unsafe.rs new file mode 100644 index 0000000..e13475e --- /dev/null +++ b/crates/core/src/native/unsafe.rs @@ -0,0 +1,47 @@ +// unsafe_support.rs or similar + +use std::collections::HashMap; +use std::sync::atomic::{AtomicI64, Ordering}; +use crate::prim::jlong; + + +#[derive(Default)] +pub struct UnsafeSupport { + // Field offset registry + field_offsets: HashMap, + next_field_offset: AtomicI64, + + // Off-heap allocations (for allocateMemory/freeMemory) + allocations: HashMap, +} + +#[derive(Clone, Hash, Eq, PartialEq)] +pub struct FieldKey { + pub class_name: String, + pub field_name: String, + pub is_static: bool, +} + +const FIELD_OFFSET_BASE: jlong = 0x1_0000_0000; + +impl UnsafeSupport { + pub fn register_field_offset(&mut self, class: &str, field: &str, is_static: bool) -> jlong { + let offset = self.next_field_offset.fetch_add(1, Ordering::Relaxed); + self.field_offsets.insert(offset, FieldKey { + class_name: class.to_string(), + field_name: field.to_string(), + is_static, + }); + offset + } + + + pub fn resolve_field(&self, offset: jlong) -> Option { + self.field_offsets.get(&offset).cloned() + } +} + +pub enum OffsetKind { + ArrayIndex(jlong), + Field(FieldKey), +} \ No newline at end of file diff --git a/crates/core/src/objects/array.rs b/crates/core/src/objects/array.rs index e99b8ce..937c424 100644 --- a/crates/core/src/objects/array.rs +++ b/crates/core/src/objects/array.rs @@ -1,10 +1,11 @@ +use crate::class::RuntimeClass; +use crate::error::VmError; 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}; +use parking_lot::Mutex; use std::ops::{Deref, DerefMut}; -use crate::class::RuntimeClass; -use crate::error::VmError; +use std::sync::Arc; #[derive(Debug, Clone)] pub enum ArrayReference { @@ -22,43 +23,58 @@ pub enum ArrayReference { 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(), + ArrayReference::Int(x) => x.lock().len(), + ArrayReference::Byte(x) => x.lock().len(), + ArrayReference::Short(x) => x.lock().len(), + ArrayReference::Long(x) => x.lock().len(), + ArrayReference::Float(x) => x.lock().len(), + ArrayReference::Double(x) => x.lock().len(), + ArrayReference::Char(x) => x.lock().len(), + ArrayReference::Boolean(x) => x.lock().len(), + ArrayReference::Object(x) => x.lock().len(), } } 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, + ArrayReference::Int(x) => x.lock().id, + ArrayReference::Byte(x) => x.lock().id, + ArrayReference::Short(x) => x.lock().id, + ArrayReference::Long(x) => x.lock().id, + ArrayReference::Float(x) => x.lock().id, + ArrayReference::Double(x) => x.lock().id, + ArrayReference::Char(x) => x.lock().id, + ArrayReference::Boolean(x) => x.lock().id, + ArrayReference::Object(x) => x.lock().id, } } pub fn class(&self) -> Arc { match self { - ArrayReference::Int(x) => x.lock().unwrap().class.clone(), - ArrayReference::Byte(x) => x.lock().unwrap().class.clone(), - ArrayReference::Short(x) => x.lock().unwrap().class.clone(), - ArrayReference::Long(x) => x.lock().unwrap().class.clone(), - ArrayReference::Float(x) => x.lock().unwrap().class.clone(), - ArrayReference::Double(x) => x.lock().unwrap().class.clone(), - ArrayReference::Char(x) => x.lock().unwrap().class.clone(), - ArrayReference::Boolean(x) => x.lock().unwrap().class.clone(), - ArrayReference::Object(x) => x.lock().unwrap().class.clone(), + ArrayReference::Int(x) => x.lock().class.clone(), + ArrayReference::Byte(x) => x.lock().class.clone(), + ArrayReference::Short(x) => x.lock().class.clone(), + ArrayReference::Long(x) => x.lock().class.clone(), + ArrayReference::Float(x) => x.lock().class.clone(), + ArrayReference::Double(x) => x.lock().class.clone(), + ArrayReference::Char(x) => x.lock().class.clone(), + ArrayReference::Boolean(x) => x.lock().class.clone(), + ArrayReference::Object(x) => x.lock().class.clone(), + } + } + + /// Returns the size in bytes of the array's backing storage + pub fn size_bytes(&self) -> usize { + match self { + ArrayReference::Int(x) => x.lock().backing.len() * size_of::(), + ArrayReference::Byte(x) => x.lock().backing.len() * size_of::(), + ArrayReference::Short(x) => x.lock().backing.len() * size_of::(), + ArrayReference::Long(x) => x.lock().backing.len() * size_of::(), + ArrayReference::Float(x) => x.lock().backing.len() * size_of::(), + ArrayReference::Double(x) => x.lock().backing.len() * size_of::(), + ArrayReference::Char(x) => x.lock().backing.len() * size_of::(), + ArrayReference::Boolean(x) => x.lock().backing.len() * size_of::(), + ArrayReference::Object(x) => x.lock().backing.len() * size_of::(), } } } @@ -71,7 +87,7 @@ pub type ObjectArrayReference = Arc>>>; pub struct Array { pub(crate) id: u32, pub(crate) class: Arc, - pub(crate) backing: Box<[T]>, + pub backing: Box<[T]>, } impl Array @@ -97,15 +113,23 @@ where pub fn len(&self) -> jint { self.backing.len() as jint } + // fn from(value: (u32, Arc, Box<[T]>)) -> Self { + // let (id, class, vector) = value; + // Self { + // id, + // class, + // backing: vector, + // } + // } } -impl From<(u32, Arc, Vec)> for Array { - fn from(value: (u32, Arc, Vec)) -> Self { - let (id, class, vector) = value; +impl From<(u32, Arc, Box<[T]>)> for Array { + fn from(value: (u32, Arc, Box<[T]>)) -> Self { + let (id, class, values) = value; Self { id, class, - backing: vector.into_boxed_slice(), + backing: values, } } } @@ -139,7 +163,6 @@ impl ArrayValue for jdouble {} impl ArrayValue for jboolean {} - impl ArrayReference { pub fn copy_from( &self, @@ -149,32 +172,36 @@ impl ArrayReference { length: jint, ) -> Result<(), VmError> { macro_rules! copy { - ($src_arr:expr, $dst_arr:expr) => {{ - let src_guard = $src_arr.lock().unwrap(); - let mut dst_guard = $dst_arr.lock().unwrap(); + ($src_arr:expr, $dst_arr:expr) => {{ + let src_guard = $src_arr.lock(); + let mut dst_guard = $dst_arr.lock(); - let src_start = src_pos as usize; - let dst_start = dst_pos as usize; - let len = length as usize; + let src_start = src_pos as usize; + let dst_start = dst_pos as usize; + let len = length as usize; - // Bounds check - if src_pos < 0 || dst_pos < 0 || length < 0 - || src_start + len > src_guard.backing.len() - || dst_start + len > dst_guard.backing.len() - { - return Err(VmError::InvariantError("Index oob".to_string())); - } + // Bounds check + if src_pos < 0 + || dst_pos < 0 + || length < 0 + || src_start + len > src_guard.backing.len() + || dst_start + len > dst_guard.backing.len() + { + return Err(VmError::InvariantError("Index oob".to_string())); + } - if Arc::ptr_eq($src_arr, $dst_arr) { - drop(src_guard); - dst_guard.backing.copy_within(src_start..src_start + len, dst_start); - } else { - dst_guard.backing[dst_start..dst_start + len] - .copy_from_slice(&src_guard.backing[src_start..src_start + len]); - } - Ok(()) - }}; - } + if Arc::ptr_eq($src_arr, $dst_arr) { + drop(src_guard); + dst_guard + .backing + .copy_within(src_start..src_start + len, dst_start); + } else { + dst_guard.backing[dst_start..dst_start + len] + .copy_from_slice(&src_guard.backing[src_start..src_start + len]); + } + Ok(()) + }}; + } use ArrayReference::*; match (src, self) { @@ -188,14 +215,14 @@ impl ArrayReference { (Boolean(s), Boolean(d)) => copy!(s, d), (Object(s), Object(d)) => { // Object arrays need clone, not copy - let src_guard = s.lock().unwrap(); - let mut dst_guard = d.lock().unwrap(); - + let src_guard = s.lock(); + let mut dst_guard = d.lock(); let src_start = src_pos as usize; let dst_start = dst_pos as usize; let len = length as usize; - if src_pos < 0 || dst_pos < 0 || length < 0 + if src_pos < 0 + || dst_pos < 0 || length < 0 || src_start + len > src_guard.backing.len() || dst_start + len > dst_guard.backing.len() { @@ -204,7 +231,11 @@ impl ArrayReference { if Arc::ptr_eq(s, d) { drop(src_guard); - for i in if src_start < dst_start { (0..len).rev().collect::>() } else { (0..len).collect() } { + for i in if src_start < dst_start { + (0..len).rev().collect::>() + } else { + (0..len).collect() + } { dst_guard.backing[dst_start + i] = dst_guard.backing[src_start + i].clone(); } } else { @@ -217,4 +248,4 @@ impl ArrayReference { _ => Err(VmError::InvariantError("Array type mismatch".to_string())), } } -} \ No newline at end of file +} diff --git a/crates/core/src/objects/object.rs b/crates/core/src/objects/object.rs index 2cd06f1..acb3fbe 100644 --- a/crates/core/src/objects/object.rs +++ b/crates/core/src/objects/object.rs @@ -1,14 +1,16 @@ use crate::class::RuntimeClass; +use crate::class_file::FieldRef; +use crate::error::VmError; use crate::objects::array::{ArrayReference, ObjectArrayReference, PrimitiveArrayReference}; use crate::prim::{jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jshort}; use crate::value::Value; +use crate::{BaseType, FieldType}; use dashmap::DashMap; use log::trace; +use parking_lot::Mutex; 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}; +use std::sync::Arc; pub type ObjectReference = Arc>; @@ -30,26 +32,9 @@ impl Object { trace!("Fields for object:\n\t{:#}", self.format_fields()); self.fields .get(&field_ref.name) - .map(|e| e.clone()) - .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 } - }; + .map(|r| r.value().clone()) + .unwrap_or_else(|| { + let initial = field_ref.desc.default_value(); self.fields.insert(field_ref.name.clone(), initial.clone()); initial }) @@ -98,21 +83,33 @@ pub enum ReferenceKind { } impl ReferenceKind { - pub fn into_object_reference(self) -> Option { + pub fn try_into_object_reference(&self) -> Result { match self { - Self::ObjectReference(inner) => Some(inner), - _ => None, + Self::ObjectReference(obj) => Ok(obj.clone()), + _ => Err(VmError::InvariantError(format!( + "Expected object, found {}", + self.to_string() + ))), + } + } + pub fn try_into_array_reference(&self) -> Result { + match self { + Self::ArrayReference(arr) => Ok(arr.clone()), + _ => Err(VmError::InvariantError(format!( + "Expected array, found {}", + self.to_string() + ))), } } pub fn id(&self) -> u32 { match self { - Self::ObjectReference(r) => r.lock().unwrap().id, + Self::ObjectReference(r) => r.lock().id, Self::ArrayReference(a) => a.id(), } } pub fn class(&self) -> Arc { match self { - Self::ObjectReference(r) => r.lock().unwrap().class.clone(), + Self::ObjectReference(r) => r.lock().class.clone(), Self::ArrayReference(a) => a.class(), } } @@ -124,52 +121,158 @@ 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(); + let guard = x.lock(); 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 Value::Reference(Some(ReferenceKind::ArrayReference( + ArrayReference::Byte(actual), + ))) = field.value() { - let arr_guard= actual.lock().unwrap(); - let string = crate::string_from_bytes(&arr_guard); + let arr_guard = actual.lock(); + let string = 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) + let guard = x.lock(); + let backing = &guard.backing; + if backing.len() > 10 { + format!( + "int[{:?}, {:?}, {:?}, ..., {:?}, {:?}, {:?}]", + backing[0], + backing[1], + backing[2], + backing[backing.len() - 3], + backing[backing.len() - 2], + backing[backing.len() - 1] + ) + } else { + format!("int{:?}", backing) + } } ReferenceKind::ArrayReference(ArrayReference::Byte(x)) => { - let guard = x.lock().unwrap(); - format!("byte{:?}", guard.backing) + let guard = x.lock(); + let backing = &guard.backing; + if backing.len() > 10 { + format!( + "byte[{:?}, {:?}, {:?}, ..., {:?}, {:?}, {:?}]", + backing[0], + backing[1], + backing[2], + backing[backing.len() - 3], + backing[backing.len() - 2], + backing[backing.len() - 1] + ) + } else { + format!("byte{:?}", backing) + } } ReferenceKind::ArrayReference(ArrayReference::Short(x)) => { - let guard = x.lock().unwrap(); - format!("short{:?}", guard.backing) + let guard = x.lock(); + let backing = &guard.backing; + if backing.len() > 10 { + format!( + "short[{:?}, {:?}, {:?}, ..., {:?}, {:?}, {:?}]", + backing[0], + backing[1], + backing[2], + backing[backing.len() - 3], + backing[backing.len() - 2], + backing[backing.len() - 1] + ) + } else { + format!("short{:?}", backing) + } } ReferenceKind::ArrayReference(ArrayReference::Long(x)) => { - let guard = x.lock().unwrap(); - format!("long{:?}", guard.backing) + let guard = x.lock(); + let backing = &guard.backing; + if backing.len() > 10 { + format!( + "long[{:?}, {:?}, {:?}, ..., {:?}, {:?}, {:?}]", + backing[0], + backing[1], + backing[2], + backing[backing.len() - 3], + backing[backing.len() - 2], + backing[backing.len() - 1] + ) + } else { + format!("long{:?}", backing) + } } ReferenceKind::ArrayReference(ArrayReference::Float(x)) => { - let guard = x.lock().unwrap(); - format!("float{:?}", guard.backing) + let guard = x.lock(); + let backing = &guard.backing; + if backing.len() > 10 { + format!( + "float[{:?}, {:?}, {:?}, ..., {:?}, {:?}, {:?}]", + backing[0], + backing[1], + backing[2], + backing[backing.len() - 3], + backing[backing.len() - 2], + backing[backing.len() - 1] + ) + } else { + format!("float{:?}", backing) + } } ReferenceKind::ArrayReference(ArrayReference::Double(x)) => { - let guard = x.lock().unwrap(); - format!("double{:?}", guard.backing) + let guard = x.lock(); + let backing = &guard.backing; + if backing.len() > 10 { + format!( + "double[{:?}, {:?}, {:?}, ..., {:?}, {:?}, {:?}]", + backing[0], + backing[1], + backing[2], + backing[backing.len() - 3], + backing[backing.len() - 2], + backing[backing.len() - 1] + ) + } else { + format!("double{:?}", backing) + } } ReferenceKind::ArrayReference(ArrayReference::Char(x)) => { - let guard = x.lock().unwrap(); - format!("char{:?}", guard.backing) + let guard = x.lock(); + let backing = &guard.backing; + if backing.len() > 10 { + format!( + "char[{:?}, {:?}, {:?}, ..., {:?}, {:?}, {:?}]", + backing[0], + backing[1], + backing[2], + backing[backing.len() - 3], + backing[backing.len() - 2], + backing[backing.len() - 1] + ) + } else { + format!("char{:?}", backing) + } } ReferenceKind::ArrayReference(ArrayReference::Boolean(x)) => { - let guard = x.lock().unwrap(); - format!("boolean{:?}", guard.backing) + let guard = x.lock(); + let backing = &guard.backing; + if backing.len() > 10 { + format!( + "boolean[{:?}, {:?}, {:?}, ..., {:?}, {:?}, {:?}]", + backing[0], + backing[1], + backing[2], + backing[backing.len() - 3], + backing[backing.len() - 2], + backing[backing.len() - 1] + ) + } else { + format!("boolean{:?}", backing) + } } ReferenceKind::ArrayReference(ArrayReference::Object(x)) => { - let guard = x.lock().unwrap(); + let guard = x.lock(); format!("object[{:?}]", guard.id) } }; @@ -236,3 +339,20 @@ impl From for ReferenceKind { Self::ArrayReference(ArrayReference::Object(value)) } } + +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 +} diff --git a/crates/core/src/objects/object_manager.rs b/crates/core/src/objects/object_manager.rs index 8d98851..049220f 100644 --- a/crates/core/src/objects/object_manager.rs +++ b/crates/core/src/objects/object_manager.rs @@ -1,25 +1,70 @@ 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::class_file::{ClassFlags, MethodData}; +use crate::error::VmError; +use crate::objects::array::{ + Array, ArrayReference, ArrayValue, ObjectArrayReference, PrimitiveArrayReference, +}; +use crate::objects::object::{ + string_from_bytes, Object, ObjectReference, Reference, ReferenceKind, +}; use crate::value::{Primitive, Value}; +use crate::{objects, rng, ThreadId}; +use dashmap::DashMap; use jni::sys::{jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jshort}; use log::warn; +use parking_lot::{Condvar, Mutex}; use std::collections::HashMap; -use std::sync::{Arc, Mutex}; +use std::sync::atomic::{AtomicU32, Ordering}; +use std::sync::Arc; #[derive(Default)] pub struct ObjectManager { pub objects: HashMap, strings: HashMap, + monitors: DashMap>, + max_memory: usize, + next_id: AtomicU32, } impl ObjectManager { + pub fn max_memory(&self) -> usize { + self.max_memory + } + + fn next_id(&self) -> u32 { + self.next_id.fetch_add(1, Ordering::Relaxed) + } + + /// Returns the total bytes in use by all managed objects and arrays + pub fn bytes_in_use(&self) -> usize { + self.objects + .values() + .map(|kind| match kind { + ReferenceKind::ObjectReference(r) => { + let guard = r.lock(); + guard + .fields + .iter() + .map(|entry| entry.value().size_bytes()) + .sum() + } + ReferenceKind::ArrayReference(a) => a.size_bytes(), + }) + .sum() + } + pub fn new_object(&mut self, class: Arc) -> ObjectReference { - let id = generate_identity_hash(); + let id = self.next_id(); + if self.objects.contains_key(&id) { + let next = self.next_id(); + let key_pairs = self + .objects + .iter() + .map(|(id, obj)| format!("id: {id} obj: {}", obj.class().this_class)) + .collect::>(); + println!("next: {} all: \n{:#?}", next, key_pairs) + } assert!( !self.objects.contains_key(&id), "Generated ID already exists!" @@ -33,29 +78,42 @@ impl ObjectManager { object } - pub fn new_primitive_array(&mut self, class: Arc, array_type: ArrayType, count: i32) -> ArrayReference { - let id = generate_identity_hash(); + pub fn new_primitive_array( + &mut self, + class: Arc, + array_type: ArrayType, + count: i32, + ) -> ArrayReference { + let id = self.next_id(); 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, class,count)))), - ArrayType::T_BYTE => ArrayReference::Byte(Arc::new(Mutex::new(Array::new(id, class,count)))), - ArrayType::T_SHORT => { - ArrayReference::Short(Arc::new(Mutex::new(Array::new(id, class,count)))) + ArrayType::T_INT => { + ArrayReference::Int(Arc::new(Mutex::new(Array::new(id, class, count)))) + } + ArrayType::T_BYTE => { + ArrayReference::Byte(Arc::new(Mutex::new(Array::new(id, class, count)))) + } + ArrayType::T_SHORT => { + ArrayReference::Short(Arc::new(Mutex::new(Array::new(id, class, count)))) + } + ArrayType::T_LONG => { + ArrayReference::Long(Arc::new(Mutex::new(Array::new(id, class, count)))) } - ArrayType::T_LONG => ArrayReference::Long(Arc::new(Mutex::new(Array::new(id, class,count)))), ArrayType::T_FLOAT => { - ArrayReference::Float(Arc::new(Mutex::new(Array::new(id, class,count)))) + ArrayReference::Float(Arc::new(Mutex::new(Array::new(id, class, count)))) } ArrayType::T_DOUBLE => { - ArrayReference::Double(Arc::new(Mutex::new(Array::new(id, class,count)))) + ArrayReference::Double(Arc::new(Mutex::new(Array::new(id, class, count)))) + } + ArrayType::T_CHAR => { + ArrayReference::Char(Arc::new(Mutex::new(Array::new(id, class, count)))) } - ArrayType::T_CHAR => ArrayReference::Char(Arc::new(Mutex::new(Array::new(id, class,count)))), ArrayType::T_BOOLEAN => { - ArrayReference::Boolean(Arc::new(Mutex::new(Array::new(id, class,count)))) + ArrayReference::Boolean(Arc::new(Mutex::new(Array::new(id, class, count)))) } }; @@ -65,25 +123,49 @@ impl ObjectManager { } pub fn new_object_array(&mut self, class: Arc, count: i32) -> ArrayReference { - let id = generate_identity_hash(); + let id = self.next_id(); assert!( !self.objects.contains_key(&id), "Generated ID already exists!" ); - let array_ref = ArrayReference::Object(Arc::new(Mutex::new(Array::new(id, class,count)))); + let array_ref = ArrayReference::Object(Arc::new(Mutex::new(Array::new(id, class, count)))); self.objects .insert(id, ReferenceKind::ArrayReference(array_ref.clone())); array_ref } - pub fn new_byte_array(&mut self, class: Arc, vector: Vec) -> ArrayReference { - warn!("Manual sidechannel byte array creation"); - let id = generate_identity_hash(); + + pub fn new_object_array_from( + &mut self, + class: Arc, + values: Box<[Reference]>, + ) -> ArrayReference { + let id = self.next_id(); assert!( !self.objects.contains_key(&id), "Generated ID already exists!" ); - let array_ref = ArrayReference::Byte(Arc::new(Mutex::new(Array::from((id, class,vector))))); + let array_ref = + ArrayReference::Object(Arc::new(Mutex::new(Array::from((id, class, values))))); + + self.objects + .insert(id, ReferenceKind::ArrayReference(array_ref.clone())); + array_ref + } + + pub fn new_byte_array( + &mut self, + class: Arc, + vector: Box<[i8]>, + ) -> ArrayReference { + warn!("Manual sidechannel byte array creation"); + let id = self.next_id(); + assert!( + !self.objects.contains_key(&id), + "Generated ID already exists!" + ); + let array_ref = + ArrayReference::Byte(Arc::new(Mutex::new(Array::from((id, class, vector))))); self.objects .insert(id, ReferenceKind::ArrayReference(array_ref.clone())); @@ -91,7 +173,6 @@ impl ObjectManager { } pub fn get(&self, id: u32) -> ReferenceKind { - self.objects .get(&id) .unwrap_or_else(|| { @@ -100,18 +181,23 @@ impl ObjectManager { .iter() .map(|(x, y)| format!("{x} : {y}")) .collect::>(); - panic!("Object must be present id: {id}\n{objs:#?}") }) + 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) + .and_then(|e| self.get(*e).try_into_object_reference().ok()) } - pub fn new_string(&mut self, byte_class: Arc, string_class: Arc, utf8: &str) -> ObjectReference { + pub fn new_string( + &mut self, + byte_array_class: Arc, + 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); @@ -120,26 +206,23 @@ impl ObjectManager { .flat_map(|e| e.to_le_bytes()) .map(|e| e as i8) .collect::>(); - let barray = self.new_byte_array(byte_class, byte_vec); + let barray = self.new_byte_array(byte_array_class, byte_vec.into_boxed_slice()); - jstr.lock().unwrap().fields.insert( + jstr.lock().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; + let id = jstr.lock().id; debug_assert!(!self.strings.contains_key(&key), "String already interned"); self.strings.insert(key, id); jstr @@ -159,14 +242,284 @@ impl ObjectManager { let class_redefined_count = 0i32; let clazz = self.new_object(class_class); - if let Ok(clakz) = clazz.lock() { + { + let 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 } + /// using pure wizardry, transmute a string object into a rust string + pub fn transmute_string(&self, string: ObjectReference) -> Result { + let guard = string.lock(); + let value_field = guard + .fields + .get("value") + .ok_or_else(|| VmError::InvariantError("String must have value field".to_string()))?; + + let array_ref = match *value_field { + Value::Reference(Some(ReferenceKind::ArrayReference(ref arr))) => arr.clone(), + _ => { + return Err(VmError::InvariantError( + "String value field must be a byte array".to_string(), + )) + } + }; + + let bytes = match array_ref { + ArrayReference::Byte(byte_arr) => { + let arr_guard = byte_arr.lock(); + arr_guard.backing.clone() + } + _ => { + return Err(VmError::InvariantError( + "String value must be a byte array".to_string(), + )) + } + }; + Ok(string_from_bytes(&bytes)) + } + pub fn monitor_enter(&self, thread_id: ThreadId, obj_id: u32) { + let monitor = self + .monitors + .entry(obj_id) + .or_insert_with(|| Arc::new(Monitor::new())) + .clone(); + + // DashMap entry guard dropped here, map unlocked + + let mut inner = monitor.inner.lock(); + + while inner.owner.is_some() && inner.owner != Some(thread_id) { + monitor.condvar.wait(&mut inner); + } + + inner.owner = Some(thread_id); + inner.entry_count += 1; + } + + pub fn monitor_exit(&self, thread_id: ThreadId, obj_id: u32) -> Result<(), VmError> { + let monitor = self + .monitors + .get(&obj_id) + .ok_or_else(|| VmError::InvariantError("Monitor not found for object".to_string()))? + .clone(); + + let mut inner = monitor.inner.lock(); + + if inner.owner != Some(thread_id) { + return Err(VmError::InvariantError( + "Thread does not own monitor".to_string(), + )); + } + + inner.entry_count -= 1; + + if inner.entry_count == 0 { + inner.owner = None; + monitor.condvar.notify_one(); + } + + Ok(()) + } + + pub fn clone_object(&mut self, source: &ReferenceKind) -> Result { + match source { + ReferenceKind::ArrayReference(arr) => self.clone_array(arr), + ReferenceKind::ObjectReference(obj) => { + let class = obj.lock().class.clone(); + if !class.implements("java/lang/Cloneable") { + return Err(VmError::InvariantError("CloneNotSupported".to_string())); + } + self.clone_instance(obj) + } + } + } + + fn clone_array(&mut self, arr: &ArrayReference) -> Result { + let new_arr = match arr { + ArrayReference::Int(a) => { + let guard = a.lock(); + let new_id = self.next_id(); + ArrayReference::Int(Arc::new(Mutex::new(Array { + id: new_id, + class: guard.class.clone(), + backing: guard.backing.clone(), + }))) + } + ArrayReference::Byte(a) => { + let guard = a.lock(); + let new_id = self.next_id(); + ArrayReference::Byte(Arc::new(Mutex::new(Array { + id: new_id, + class: guard.class.clone(), + backing: guard.backing.clone(), + }))) + } + ArrayReference::Short(a) => { + let guard = a.lock(); + let new_id = self.next_id(); + ArrayReference::Short(Arc::new(Mutex::new(Array { + id: new_id, + class: guard.class.clone(), + backing: guard.backing.clone(), + }))) + } + ArrayReference::Long(a) => { + let guard = a.lock(); + let new_id = self.next_id(); + ArrayReference::Long(Arc::new(Mutex::new(Array { + id: new_id, + class: guard.class.clone(), + backing: guard.backing.clone(), + }))) + } + ArrayReference::Float(a) => { + let guard = a.lock(); + let new_id = self.next_id(); + ArrayReference::Float(Arc::new(Mutex::new(Array { + id: new_id, + class: guard.class.clone(), + backing: guard.backing.clone(), + }))) + } + ArrayReference::Double(a) => { + let guard = a.lock(); + let new_id = self.next_id(); + ArrayReference::Double(Arc::new(Mutex::new(Array { + id: new_id, + class: guard.class.clone(), + backing: guard.backing.clone(), + }))) + } + ArrayReference::Char(a) => { + let guard = a.lock(); + let new_id = self.next_id(); + ArrayReference::Char(Arc::new(Mutex::new(Array { + id: new_id, + class: guard.class.clone(), + backing: guard.backing.clone(), + }))) + } + ArrayReference::Boolean(a) => { + let guard = a.lock(); + let new_id = self.next_id(); + ArrayReference::Boolean(Arc::new(Mutex::new(Array { + id: new_id, + class: guard.class.clone(), + backing: guard.backing.clone(), + }))) + } + ArrayReference::Object(a) => { + let guard = a.lock(); + let new_id = self.next_id(); + ArrayReference::Object(Arc::new(Mutex::new(Array { + id: new_id, + class: guard.class.clone(), + backing: guard.backing.clone(), // shallow clone - copies references, not objects + }))) + } + }; + + let id = new_arr.id(); + self.objects + .insert(id, ReferenceKind::ArrayReference(new_arr.clone())); + Ok(ReferenceKind::ArrayReference(new_arr)) + } + + fn clone_instance(&mut self, obj: &ObjectReference) -> Result { + let guard = obj.lock(); + let class = guard.class.clone(); + + // Create a new object with the same class + let new_obj = self.new_object(class); + + // Copy all fields (shallow copy) + { + let new_guard = new_obj.lock(); + for entry in guard.fields.iter() { + new_guard + .fields + .insert(entry.key().clone(), entry.value().clone()); + } + } + + Ok(ReferenceKind::ObjectReference(new_obj)) + } +} + +pub struct Monitor { + inner: Mutex, + condvar: Condvar, +} + +struct MonitorInner { + owner: Option, + entry_count: u32, +} + +impl Monitor { + fn new() -> Self { + Self { + inner: Mutex::new(MonitorInner { + owner: None, + entry_count: 0, + }), + condvar: Condvar::new(), + } + } +} + +//reflection +impl ObjectManager { + fn get_from_klass(&self, klass: Arc) -> ReferenceKind { + self.get(klass.mirror()) + } + pub fn new_constructor( + &mut self, + index: jint, + modifiers: jint, + parameters: Box<[Arc]>, + exceptions: Box<[Arc]>, + class_array_class: Arc, + declaring_class: Arc, + constructor_class: Arc, + ) -> ObjectReference { + let parameters = parameters + .iter() + .map(|c| { + let mirror_id = c.mirror.get().unwrap(); + Some(self.get(*mirror_id)) + }) + .collect::>(); + let exceptions = exceptions + .iter() + .map(|c| { + let mirror_id = c.mirror.get().unwrap(); + Some(self.get(*mirror_id)) + }) + .collect::>(); + let constructor_object = self.new_object(constructor_class); + let parameter_types = self.new_object_array_from(class_array_class.clone(), parameters); + let exception_types = self.new_object_array_from(class_array_class, exceptions); + let signature = Value::NULL; + let annotations = Value::NULL; + let parameter_annotations = Value::NULL; + { + let obj = constructor_object.lock(); + obj.set_field("clazz", Value::from(self.get_from_klass(declaring_class))); + obj.set_field("slot", Value::from(index)); + obj.set_field("parameterTypes", Value::from(parameter_types)); + obj.set_field("exceptionTypes", Value::from(exception_types)); + obj.set_field("modifiers", Value::from(modifiers)); + obj.set_field("override", Value::from(false)); + obj.set_field("signature", signature); + obj.set_field("annotations", annotations); + obj.set_field("parameterAnnotations", parameter_annotations); + } + constructor_object + } } diff --git a/crates/core/src/rng.rs b/crates/core/src/rng.rs index 873387f..e738dcc 100644 --- a/crates/core/src/rng.rs +++ b/crates/core/src/rng.rs @@ -39,16 +39,17 @@ fn os_random() -> u32 { } thread_local! { - static XORSHIFT_STATE: Cell<[u32; 4]> = { - Cell::new([ - os_random(), // X: seeded from global Park-Miller RNG - 842502087, // Y: constant - 0x8767, // Z: constant - 273326509, // W: constant - ]) - }; + static XORSHIFT_STATE: Cell<[u32; 4]> = { + Cell::new([ + os_random(), // X: seeded from global Park-Miller RNG + 842502087, // Y: constant + 0x8767, // Z: constant + 273326509, // W: constant + ]) + }; } +#[deprecated] pub fn generate_identity_hash() -> u32 { XORSHIFT_STATE.with(|state| { let mut s = state.get(); @@ -66,4 +67,10 @@ pub fn generate_identity_hash() -> u32 { state.set(s); v }) -} \ No newline at end of file +} + +static NEXT_ID: AtomicU32 = AtomicU32::new(1); + +pub fn sequential_id() -> u32 { + NEXT_ID.fetch_add(1, Ordering::Relaxed) +} diff --git a/crates/core/src/thread.rs b/crates/core/src/thread.rs index 8a46145..2e6a74f 100644 --- a/crates/core/src/thread.rs +++ b/crates/core/src/thread.rs @@ -1,28 +1,34 @@ -use crate::class::RuntimeClass; +use crate::class::{InitState, RuntimeClass}; use crate::class_file::{ClassFile, MethodData, MethodRef}; use crate::class_loader::{ClassLoader, LoaderRef}; -use crate::jni::create_jni_function_table; +use crate::error::VmError; +use crate::frame::Frame; +use crate::native::jni::create_jni_function_table; use crate::objects::object::{ObjectReference, ReferenceKind}; use crate::objects::object_manager::ObjectManager; use crate::value::{Primitive, Value}; use crate::vm::Vm; -use crate::{BaseType, FieldType, Frame, MethodDescriptor, ThreadId}; +use crate::{stack_used, BaseType, FieldType, MethodDescriptor, ThreadId}; use deku::DekuError::Incomplete; 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 log::{info, trace, warn, LevelFilter}; + +use parking_lot::{Mutex, Once, ReentrantMutex, RwLock}; use std::any::Any; 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; -use crate::error::VmError; +use std::sync::Arc; +use std::thread; +use std::vec::IntoIter; + +static INIT_LOGGER: Once = Once::new(); type MethodCallResult = Result, VmError>; // Thread-local storage for current thread ID @@ -37,7 +43,7 @@ pub struct VmThread { pub id: ThreadId, pub vm: Arc, pub loader: Arc>, - pub frame_stack: Mutex>>>, + pub frame_stack: Mutex>, pub gc: Arc>, pub jni_env: JNIEnv, } @@ -80,15 +86,11 @@ impl VmThread { 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. + /*/// Get or resolve a class, ensuring it and its dependencies are initialised. + /// Follows JVM Spec 5.5 for recursive initialisation handling. 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, None)?; + let runtime_class = self.loader.lock().unwrap().get_or_load(what, None, true)?; // Phase 2: Collect classes that need initialisation (short lock) let classes_to_init = { @@ -104,26 +106,22 @@ impl VmThread { } Ok(runtime_class) - } + }*/ pub fn get_class(&self, what: &str) -> Result, VmError> { - self.loader - .lock() - .unwrap() - .get_or_load(what, None) + let class = self.loader.lock().get_or_load(what, None)?; + self.create_mirror_class(&class)?; + Ok(class) } /// Initialize a class following JVM Spec 5.5. /// Handles recursive initialization by tracking which thread is initializing. - fn init(&self, class: Arc) -> Result<(), VmError> { - use crate::class::InitState; - use std::thread; - + pub fn init(&self, class: Arc) -> Result<(), VmError> { let current_thread = thread::current().id(); // Check and update initialization state { - let mut state = class.init_state.lock().unwrap(); + let mut state = class.init_state.lock(); match &*state { InitState::Initialized => { // Already initialized, nothing to do @@ -157,17 +155,6 @@ impl VmThread { } } } - 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 = (|| { @@ -176,16 +163,24 @@ impl VmThread { self.init(super_class.clone())?; } - // Run if present - if let Ok(method) = class.find_method("", &MethodDescriptor::void()) { - self.execute_method(&class, &method, vec![])?; + if !class.access_flags.INTERFACE { + for interface in class.interfaces.iter() { + if interface.has_default_method() { + self.init(interface.clone())?; + } + } + } + + // Run if present (note: is NOT inherited, only look in this class) + if let Some(method) = class.methods.iter().find(|m| m.name == "") { + self.execute_method(&class, method, vec![])?; } Ok(()) })(); // Update state based on result { - let mut state = class.init_state.lock().unwrap(); + let mut state = class.init_state.lock(); match result { Ok(_) => { *state = InitState::Initialized; @@ -200,6 +195,85 @@ impl VmThread { result } + pub fn ensure_initialised(&self, class: &Arc) -> Result<(), VmError> { + let current_thread = thread::current().id(); + + { + let mut state = class.init_state.lock(); + match &*state { + InitState::Initialized => return Ok(()), + InitState::Initializing(tid) if *tid == current_thread => return Ok(()), + InitState::Initializing(_) => { + return Err(VmError::LoaderError(format!( + "Class {} is being initialized by another thread", + class.this_class + ))); + } + InitState::Error(msg) => { + return Err(VmError::LoaderError(format!( + "Class {} initialization previously failed: {}", + class.this_class, msg + ))); + } + InitState::NotInitialized => { + *state = InitState::Initializing(current_thread); + } + } + } + + let result = (|| { + if let Some(ref super_class) = class.super_class { + self.ensure_initialised(super_class)?; + } + + if !class.access_flags.INTERFACE { + for interface in class.interfaces.iter() { + if interface.has_default_method() { + self.ensure_initialised(interface)?; + } + } + } + + if let Some(method) = class.methods.iter().find(|m| m.name == "") { + self.execute_method(class, method, vec![])?; + } + Ok(()) + })(); + + { + let mut state = class.init_state.lock(); + match &result { + Ok(_) => *state = InitState::Initialized, + Err(e) => *state = InitState::Error(format!("{:?}", e)), + } + } + + result + } + + /// creates a mirror java/lang/Class Object and binds it to this RuntimeClass + fn create_mirror_class(&self, class: &Arc) -> Result<(), VmError> { + if class.mirror.get().is_some() || class.mirror_in_progress.swap(true, Ordering::SeqCst) { + return Ok(()); // already has a mirror + } + let class_class = if class.this_class == "java/lang/Class" { + Arc::clone(class) + } else { + self.get_class("java/lang/Class")? + }; + let string = self.intern_string(&class.this_class); + let class_obj = self.gc.write().new_class( + class_class, + Some(ReferenceKind::ObjectReference(string)), + None, + class.access_flags, + false, + ); + let id = class_obj.lock().id; + class.mirror.set(id).expect("woops, id already set"); + Ok(()) + } + pub fn invoke_main(&self, what: &str) -> Result<(), VmError> { let method_ref = MethodRef { class: what.to_string(), @@ -212,56 +286,46 @@ impl VmThread { } pub fn invoke(&self, method_reference: MethodRef, args: Vec) -> MethodCallResult { - if method_reference.class.contains("Unsafe") { - println!("()") + if self.gc.read().objects.len() > 2210 { + INIT_LOGGER.call_once(|| { + 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(); + }); + // println!("heap length {}", self.gc.read().objects.len()) } - let class = self.get_or_resolve_class(&method_reference.class)?; - let method = class.find_method(&method_reference.name, &method_reference.desc).unwrap(); + let class = self.get_class(&method_reference.class)?; + let method = class.find_method(&method_reference.name, &method_reference.desc)?; + let class = self.loader.lock().get_or_load(&*method.class, None)?; + self.execute_method(&class, &method, args) } - pub fn invoke_virtual(&self, method_reference: MethodRef, class: Arc, args: Vec) -> MethodCallResult { - let method = class.find_method(&method_reference.name, &method_reference.desc).unwrap(); + pub fn invoke_virtual( + &self, + method_reference: MethodRef, + class: Arc, + args: Vec, + ) -> MethodCallResult { + let method = class.find_method(&method_reference.name, &method_reference.desc)?; + + let class = self.loader.lock().get_or_load(&*method.class, None)?; self.execute_method(&class, &method, args) } - /*pub fn invoke_old(&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(); - trace!( - "invoking '{}' from {}", - method_reference.name, - class.this_class - ); - if resolved_method.flags.ACC_NATIVE { - 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, - self.vm.clone(), - ); - self.frame_stack.lock().unwrap().push(frame.clone()); - let result = frame.execute()?; - self.frame_stack.lock().unwrap().pop(); - Ok(result) - }*/ - 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); + println!("Invoke native for native symbol: {:?}", &symbol_name); + + // if symbol_name.contains("Java_jdk_internal_reflect_Reflection_getClassAccessFlags") { + // return Err(VmError::Debug( + // "RoastVM specific implementation required for Java_jdk_internal_reflect_Reflection_getClassAccessFlags", + // )); + // } if symbol_name.contains("Java_java_lang_Class_desiredAssertionStatus0") { warn!("MAJOR HACK, figure out what is wrong with desiredAssertionStatus0"); @@ -276,15 +340,12 @@ impl VmThread { let name_with_params = generate_jni_method_name(method, true); self.vm.find_native_method(&name_with_params) }) - .ok_or(VmError::NativeError(format!("Link error: Unable to locate symbol {symbol_name}")))?; + .ok_or(VmError::NativeError(format!( + "Link error: Unable to locate symbol {symbol_name}" + )))?; // build pointer to native fn let cp = CodePtr::from_ptr(p); - // let args = build_args(args); - - // coerce my method descriptors into libffi C equivalents, then call - // let l = method.build_cif().call::(cp, args.as_ref()); - let mut storage = Vec::new(); trace!("passing {} to native fn", Value::format_vec(&args)); let deq_args = VecDeque::from(args); @@ -342,7 +403,7 @@ impl VmThread { Ok(Some(Value::Reference(None))) } else { // Look up the object in the ObjectManager - let gc = self.gc.read().unwrap(); + let gc = self.gc.read(); let reference_kind = gc.get(obj_id); Ok(Some(Value::Reference(Some(reference_kind)))) } @@ -358,64 +419,65 @@ impl VmThread { method: &MethodData, args: Vec, ) -> MethodCallResult { - eprintln!("[DEBUG] execute_method self.id = {:?}", self.id); + info!("Executing {}", method.name.clone()); + warn!("Stack used: {}", stack_used()); let method_ref = MethodRef { class: class.this_class.clone(), name: method.name.clone(), desc: method.desc.clone(), }; - if method.flags.ACC_NATIVE { let mut native_args = Vec::new(); if method.flags.ACC_STATIC { - let jclass = self.vm.gc.read().unwrap().get(*class.mirror.wait()); + let jclass = self.vm.gc.read().get(*class.mirror.wait()); native_args.push(Value::Reference(Some(jclass))); } native_args.extend(args); - return self.invoke_native(&method_ref, native_args); + let res = self.invoke_native(&method_ref, native_args); + // println!("Returning from native: {}.{}", &method_ref.class, &method_ref.name); + return res; } let mut frame = Frame::new( class.clone(), - method_ref, + method_ref.clone(), method.code.clone().unwrap(), class.constant_pool.clone(), args, self.vm.clone(), method.line_number_table.clone(), ); - let frame = Arc::new(Mutex::new(frame)); - self.frame_stack.lock().unwrap().push(frame.clone()); - eprintln!("[DEBUG] pushed frame for {}.{}, stack depth now: {}", - class.this_class, method.name, - self.frame_stack.lock().unwrap().len()); - let result = frame.lock().unwrap().execute(); - eprintln!("[DEBUG] returned from {}.{}, result ok: {}, stack depth: {}", - class.this_class, method.name, result.is_ok(), - self.frame_stack.lock().unwrap().len()); + // let frame = Arc::new(ReentrantMutex::new(frame)); + self.frame_stack.lock().push(frame.clone()); + // println!("Invoke method: {}.{}", &method_ref.class, &method_ref.name); + let result = frame.execute(); if result.is_ok() { - self.frame_stack.lock().unwrap().pop(); + self.frame_stack.lock().pop(); } + // println!("Returning from method: {}.{}", &method_ref.class, &method_ref.name); result } - pub fn print_stack_trace(&self) { - let guard = self.frame_stack.lock().unwrap(); - // Reverse - most recent frame first (like Java does) - for frame in guard.iter().rev() { - let frame = frame.lock().unwrap(); - let method = &frame.method_ref; - // Internal format uses '/', Java stack traces use '.' - let class_name = method.class.replace("/", "."); - - - match (&frame.class.source_file, &frame.current_line_number()) { - (Some(file), Some(line)) => eprintln!("\tat {}.{}({}:{})", class_name, method.name, file, line), - (Some(file), None) => eprintln!("\tat {}.{}({})", class_name, method.name, file), - _ => eprintln!("\tat {}.{}(Unknown Source)", class_name, method.name), - } - } - } + // pub fn print_stack_trace(&mut self) { + // // Get a lock on the frame stack + // let guard = self.frame_stack.lock(); + // // Reverse - most recent frame first (like Java does) + // for frame_arc in guard.iter().rev() { + // // Get a lock on the individual frame + // let frame = frame_arc.lock(); + // let method = &frame.method_ref; + // // Internal format uses '/', Java stack traces use '.' + // let class_name = method.class.replace("/", "."); + // + // match (&frame.class.source_file, &frame.current_line_number()) { + // (Some(file), Some(line)) => { + // eprintln!("\tat {}.{}({}:{})", class_name, method.name, file, line) + // } + // (Some(file), None) => eprintln!("\tat {}.{}({})", class_name, method.name, file), + // _ => eprintln!("\tat {}.{}(Unknown Source)", class_name, method.name), + // } + // } + // } } fn build_args<'a>( @@ -429,9 +491,7 @@ fn build_args<'a>( // 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(Some(ref_kind))) => ref_kind.id(), Some(Value::Reference(None)) => 0, // null _ => panic!("first arg must be reference"), }; @@ -451,7 +511,9 @@ fn build_args<'a>( let id = x.map(|r| r.id()).unwrap_or(0) as jobject; storage.push(Box::new(id)); } - Value::Padding => { panic!("Uhh not possible chief") } + Value::Padding => { + panic!("Uhh not possible chief") + } } } @@ -529,21 +591,15 @@ impl VmThread { /// 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) { + if let Some(existing) = self.gc.read().get_interned_string(utf) { return existing; } let string_class = self.get_class("java/lang/String").unwrap(); let byte_array_class = self.get_class("[B").unwrap(); - gc.new_string(byte_array_class, string_class, utf) + + let mut gc = self.gc.write(); + gc.get_interned_string(utf) + .unwrap_or_else(|| gc.new_string(byte_array_class, string_class, utf)) } } diff --git a/crates/core/src/value.rs b/crates/core/src/value.rs index f8df6e0..acfed50 100644 --- a/crates/core/src/value.rs +++ b/crates/core/src/value.rs @@ -1,3 +1,4 @@ +use crate::error::VmError; use crate::objects::array::ArrayReference; use crate::objects::object::{ObjectReference, ReferenceKind}; use crate::{BaseType, FieldType}; @@ -6,8 +7,8 @@ use dashmap::DashMap; use jni::sys::{jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jshort}; use log::trace; use std::fmt::{Display, Formatter}; +use std::mem::{size_of, size_of_val}; use std::ops::Deref; -use crate::error::VmError; /// A reference-counted, thread-safe pointer to an Object. @@ -24,7 +25,7 @@ pub enum Value { Padding, } -#[derive( Clone, Default)] +#[derive(Clone, Default)] pub struct OperandStack(Vec); impl OperandStack { @@ -33,9 +34,7 @@ impl OperandStack { } pub fn with_capacity(max_stack: usize) -> Self { let backing = Vec::with_capacity(max_stack); - Self { - 0: backing - } + Self { 0: backing } } pub fn push(&mut self, value: Value) { @@ -43,12 +42,14 @@ impl OperandStack { } pub fn pop(&mut self) -> Result { - self.0.pop() + self.0 + .pop() .ok_or_else(|| VmError::StackError("Stack underflow".to_string())) } pub fn peek(&self) -> Result<&Value, VmError> { - self.0.last() + self.0 + .last() .ok_or_else(|| VmError::StackError("Stack underflow on peek".to_string())) } @@ -62,7 +63,9 @@ impl OperandStack { /// 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() + 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()) @@ -90,13 +93,13 @@ impl<'a> IntoIterator for &'a OperandStack { } #[derive(Clone, Default)] pub struct LocalVariables { - inner: Vec + inner: Vec, } impl LocalVariables { pub fn with_capacity(max_locals: usize) -> Self { Self { - inner: vec![Value::NULL; max_locals] + inner: vec![Value::NULL; max_locals], } } pub fn from_args(args: Vec, max_locals: usize) -> Self { @@ -130,11 +133,11 @@ impl LocalVariables { val } - pub fn iter(&self) -> impl Iterator { + pub fn iter(&self) -> impl Iterator { self.inner.iter().filter(|v| !matches!(v, Value::Padding)) } - pub fn slots(&self) -> impl Iterator { + pub fn slots(&self) -> impl Iterator { self.inner .iter() .enumerate() @@ -151,10 +154,18 @@ impl std::fmt::Debug for LocalVariables { } } - impl Value { pub const NULL: Value = Value::Reference(None); + /// Returns the size in bytes of this value's data + pub fn size_bytes(&self) -> usize { + match self { + Value::Primitive(p) => p.size_bytes(), + Value::Reference(r) => size_of_val(r), + Value::Padding => 0, + } + } + pub fn format_vec(values: &Vec) -> String { let fmt = values .iter() @@ -166,13 +177,59 @@ impl Value { } pub fn is_wide(&self) -> bool { - matches!(self, Value::Primitive(Primitive::Long(_) | Primitive::Double(_))) + matches!( + self, + Value::Primitive(Primitive::Long(_) | Primitive::Double(_)) + ) } - pub fn as_ref_kind(&self) -> Option { + pub fn as_ref_kind(&self) -> Result { match self { - Value::Reference(Some(kind)) => Some(kind.clone()), - _ => None, + Value::Reference(Some(kind)) => Ok(kind.clone()), + _ => Err(VmError::InvariantError(format!( + "Expected reference kind, found {}", + self.to_string() + ))), + } + } + + pub fn try_into_object_reference(&self) -> Result { + match self { + Value::Reference(Some(ReferenceKind::ObjectReference(obj))) => Ok(obj.clone()), + _ => Err(VmError::InvariantError(format!( + "Expected object, found {}", + self.to_string() + ))), + } + } + + pub fn try_into_array_reference(&self) -> Result { + match self { + Value::Reference(Some(ReferenceKind::ArrayReference(arr))) => Ok(arr.clone()), + _ => Err(VmError::InvariantError(format!( + "Expected array ref, found {}", + self.to_string() + ))), + } + } + + pub fn try_into_jint(self) -> Result { + match self { + Value::Primitive(Primitive::Int(i)) => Ok(i), + _ => Err(VmError::InvariantError(format!( + "Expected int, found {}", + self.to_string() + ))), + } + } + + pub fn try_into_jlong(self) -> Result { + match self { + Value::Primitive(Primitive::Long(j)) => Ok(j), + _ => Err(VmError::InvariantError(format!( + "Expected long, found {}", + self.to_string() + ))), } } } @@ -244,6 +301,21 @@ pub enum Primitive { Long(i64), } +impl Primitive { + pub fn size_bytes(&self) -> usize { + match self { + Primitive::Boolean(_) => size_of::(), + Primitive::Char(_) => size_of::(), + Primitive::Float(_) => size_of::(), + Primitive::Double(_) => size_of::(), + Primitive::Byte(_) => size_of::(), + Primitive::Short(_) => size_of::(), + Primitive::Int(_) => size_of::(), + Primitive::Long(_) => size_of::(), + } + } +} + impl Display for Primitive { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { @@ -270,7 +342,9 @@ impl Display for Value { Value::Primitive(prim) => write!(f, "{}", prim), Value::Reference(Some(obj)) => write!(f, "Ref({})", obj), Value::Reference(None) => write!(f, "null"), - _ => { write!(f, "pad") } + _ => { + write!(f, "pad") + } } } } @@ -310,7 +384,7 @@ impl PartialEq for Value { 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); + let found = format!("L{};", found_ref.lock().class.this_class); expected.eq(&found) } else { false @@ -355,7 +429,9 @@ impl PartialEq for Value { }, }, }, - _ => { panic!("uhh what") } + _ => { + panic!("uhh what") + } } } } diff --git a/crates/core/src/vm.rs b/crates/core/src/vm.rs index 5a8d9f4..5111878 100644 --- a/crates/core/src/vm.rs +++ b/crates/core/src/vm.rs @@ -9,14 +9,17 @@ use libloading::os::windows as imp; use crate::class_file::{ClassFlags, MethodRef}; use crate::class_loader::ClassLoader; +use crate::error::VmError; +use crate::native::r#unsafe::UnsafeSupport; +use crate::objects::object::ReferenceKind; use crate::objects::object_manager::ObjectManager; use crate::thread::VmThread; use crate::{MethodDescriptor, ThreadId}; use dashmap::DashMap; use imp::Library; -use std::sync::{Arc, Mutex, RwLock}; -use crate::objects::object::ReferenceKind; -use crate::error::VmError; +use log::trace; +use parking_lot::{Mutex, RwLock}; +use std::sync::Arc; // struct AbstractObject<'a> {} pub struct Vm { @@ -27,6 +30,7 @@ pub struct Vm { pub native_methods: DashMap, pub native_libraries: DashMap, pub gc: Arc>, + pub safent: RwLock, } impl Vm { @@ -39,6 +43,7 @@ impl Vm { native_methods: DashMap::new(), native_libraries: DashMap::new(), gc: Default::default(), + safent: Default::default(), }); // Create main thread @@ -58,13 +63,8 @@ impl Vm { } 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) { + trace!("native {} was already registered", name); return Some(registered.clone()); } @@ -103,11 +103,15 @@ impl Vm { "java/lang/Thread", "java/lang/Module", //unsafe internal? - // "java/lang/reflect/Method", - // "java/lang/ref/Finalizer", - // "jdk/internal/misc/UnsafeConstants" + "jdk/internal/misc/UnsafeConstants", + "java/lang/reflect/Method", + "java/lang/ref/Finalizer", ]; - let _ = classes.iter().map(|e| thread.get_or_resolve_class(e)); + let mut loaded = Vec::new(); + for name in classes { + let class = thread.loader.lock().get_or_load(name, None)?; + loaded.push(class); + } let prims = vec![ ("byte", "B"), ("char", "C"), @@ -116,37 +120,45 @@ impl Vm { ("int", "I"), ("long", "J"), ("short", "S"), - ("boolean", "Z") + ("boolean", "Z"), + ("void", "V"), ]; let thread = self.threads.get(&self.main_thread_id).unwrap(); for prim in prims { - let klass = - self.loader.lock().unwrap().primitive_class(prim.0); - + let klass = self.loader.lock().primitive_class(prim.0); let class_class = thread.get_class("java/lang/Class")?; let name_obj = thread.intern_string(&prim.0); let flags = ClassFlags::from(1041u16); - let class_obj = self.gc.write().unwrap().new_class( + let class_obj = self.gc.write().new_class( class_class.clone(), Some(ReferenceKind::ObjectReference(name_obj)), None, flags, - true + true, ); - klass.mirror.set(class_obj.lock().unwrap().id).unwrap(); + klass.mirror.set(class_obj.lock().id).unwrap(); - let prim_array_klass = self.loader.lock().unwrap().create_array_class(klass.clone()); + let prim_array_klass = self.loader.lock().create_array_class(klass.clone()); let arr_name_obj = thread.intern_string(&prim_array_klass.this_class); - let arr_class_obj = self.gc.write().unwrap().new_class( + let arr_class_obj = self.gc.write().new_class( class_class, Some(ReferenceKind::ObjectReference(arr_name_obj)), None, flags, - false + false, ); - prim_array_klass.mirror.set(arr_class_obj.lock().unwrap().id).unwrap(); + prim_array_klass + .mirror + .set(arr_class_obj.lock().id) + .unwrap(); + } + + //woops forgot to init + for class in loaded { + println!("bootstrap init {}", class.this_class); + thread.ensure_initialised(&class)?; } let phase1ref = MethodRef { @@ -155,6 +167,7 @@ impl Vm { desc: MethodDescriptor::void(), }; thread.invoke(phase1ref, Vec::new())?; + panic!("HOLY FUCKING SHIT, DID WE PHASE 1!?"); Ok(()) } diff --git a/crates/roast-vm-sys/Cargo.toml b/crates/roast-vm-sys/Cargo.toml index 606b2a5..bee5cba 100644 --- a/crates/roast-vm-sys/Cargo.toml +++ b/crates/roast-vm-sys/Cargo.toml @@ -11,4 +11,5 @@ log = "0.4.28" [lib] name = "roast_vm" -crate-type = ["cdylib"] \ No newline at end of file +crate-type = ["cdylib"] + diff --git a/crates/roast-vm-sys/src/CDS.rs b/crates/roast-vm-sys/src/CDS.rs new file mode 100644 index 0000000..dec708e --- /dev/null +++ b/crates/roast-vm-sys/src/CDS.rs @@ -0,0 +1,29 @@ +use crate::get_thread; +use jni::sys::{jclass, jint, JNIEnv}; + +#[unsafe(no_mangle)] +pub extern "C" fn Java_jdk_internal_misc_CDS_getCDSConfigStatus( + _env: *mut JNIEnv, + _class: jclass, +) -> jint { + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn Java_jdk_internal_misc_CDS_initializeFromArchive( + _env: *mut JNIEnv, + _class: jclass, + _target_class: jclass, +) { + //NOOP + () +} + +#[unsafe(no_mangle)] +pub extern "C" fn Java_jdk_internal_misc_CDS_getRandomSeedForDumping( + _env: *mut JNIEnv, + _class: jclass, +) -> jint { + // return 0 to fallback to java nanotime + 0 +} diff --git a/crates/roast-vm-sys/src/class.rs b/crates/roast-vm-sys/src/class.rs index 18f2914..8dc4dc9 100644 --- a/crates/roast-vm-sys/src/class.rs +++ b/crates/roast-vm-sys/src/class.rs @@ -1,13 +1,14 @@ +use crate::{get_thread, resolve_object}; +use jni::sys::{jboolean, jclass, jobject, JNI_TRUE}; +use jni::sys::{jint, JNIEnv}; +use jni::sys::{jobjectArray, jstring}; +use log::trace; 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::objects::{Reference, ReferenceKind}; use roast_vm_core::value::Value; -use crate::get_thread; +use roast_vm_core::{BaseType, FieldType}; +use std::ptr; #[unsafe(no_mangle)] pub unsafe extern "system" fn Java_java_lang_Class_getPrimitiveClass<'local>( @@ -18,7 +19,7 @@ pub unsafe extern "system" fn Java_java_lang_Class_getPrimitiveClass<'local>( let thread = &*get_thread(env); let rust_string = { - let gc = thread.gc.read().unwrap(); + let gc = thread.gc.read(); let obj_id = name as u32; @@ -27,8 +28,8 @@ pub unsafe extern "system" fn Java_java_lang_Class_getPrimitiveClass<'local>( _ => return 0 as jclass, }; - let obj = obj_ref.lock().unwrap(); - let field_ref = FieldRef{ + let obj = obj_ref.lock(); + let field_ref = FieldRef { class: "java/lang/String".to_string(), name: "value".to_string(), desc: FieldType::ArrayType(Box::new(FieldType::Base(BaseType::Byte))), @@ -40,7 +41,7 @@ pub unsafe extern "system" fn Java_java_lang_Class_getPrimitiveClass<'local>( return 0 as jclass; }; - let array = byte_array.lock().unwrap(); + let array = byte_array.lock(); let bytes: Vec = (&array).iter().map(|&b| b as u8).collect(); @@ -55,9 +56,101 @@ pub unsafe extern "system" fn Java_java_lang_Class_getPrimitiveClass<'local>( }; let klass = thread.get_class(&rust_string).unwrap(); - let class = thread.gc.read().unwrap().get(*klass.mirror.wait()); + let class = thread.gc.read().get(*klass.mirror.wait()); class.id() as jclass +} +#[unsafe(no_mangle)] +pub unsafe extern "system" fn Java_java_lang_Class_forName0( + env: *mut JNIEnv, + _class: jclass, + name: jstring, + initialize: jboolean, + loader: jobject, + caller: jclass, +) -> jclass { + trace!("Java_java_lang_Class_forName0"); + let thread = &*get_thread(env); + // Convert java.lang.String to Rust string + let string_obj = resolve_object(thread, name).unwrap(); + let class_name = thread.gc.read().transmute_string(string_obj).unwrap(); -} \ No newline at end of file + // Convert "java.lang.String" -> "java/lang/String" + let class_name = class_name.replace('.', "/"); + println!("forName0: class name is : {}", class_name); + + // Load the class + let rtc = match thread.get_class(&class_name) { + Ok(c) => c, + Err(_) => { + // TODO: throw ClassNotFoundException + return ptr::null_mut(); + } + }; + + if initialize == JNI_TRUE { + thread.ensure_initialised(&rtc).unwrap(); + } + + println!("returning"); + // Return the mirror + *rtc.mirror.get().unwrap() as jclass +} + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn Java_java_lang_Class_getDeclaredConstructors0( + env: *mut JNIEnv, + class: jobject, + public_only: jboolean, +) -> jobjectArray { + let thread = &*get_thread(env); + let klass = thread + .loader + .lock() + .class_from_mirror_id(class as u32) + .unwrap(); + let only_public = public_only == 1u8; + + let constructors = klass + .methods + .iter() + .filter(|e| e.name == "" && (!only_public || e.flags.ACC_PUBLIC)) + .enumerate() + .map(|(idx, constructor)| { + let modifiers: u16 = constructor.flags.clone().into(); + let parameters = constructor + .desc + .parameters + .iter() + .map(|x| thread.get_class(&x.as_class_name()).unwrap()) + .collect::>() + .into_boxed_slice(); + let exceptions = Vec::new().into_boxed_slice(); + let class_array_class = thread.get_class("[Ljava/lang/Class;").unwrap(); + let declaring_class = thread.get_class(&constructor.class).unwrap(); + let constructor_class = thread.get_class("java/lang/reflect/Constructor").unwrap(); + + let con = thread.gc.write().new_constructor( + idx as jint, + modifiers as jint, + parameters, + exceptions, + class_array_class, + declaring_class, + constructor_class, + ); + Some(ReferenceKind::from(con)) + }) + .collect::>(); + + let constructor_array_class = thread + .get_class("[Ljava/lang/reflect/Constructor;") + .unwrap(); + let output = thread + .gc + .write() + .new_object_array_from(constructor_array_class, constructors.into_boxed_slice()); + + output.id() as jobjectArray +} diff --git a/crates/roast-vm-sys/src/lib.rs b/crates/roast-vm-sys/src/lib.rs index a4a2bfa..c3a3669 100644 --- a/crates/roast-vm-sys/src/lib.rs +++ b/crates/roast-vm-sys/src/lib.rs @@ -1,36 +1,24 @@ #![allow(non_snake_case)] +mod CDS; mod class; -mod object; mod misc_unsafe; +mod object; +mod reflection; +mod runtime; mod system; 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::array::ArrayReference; 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 -} +use std::ffi::c_void; +use std::io::Write; +use std::time::{SystemTime, UNIX_EPOCH}; #[unsafe(no_mangle)] pub extern "system" fn Java_org_example_MockIO_print<'local>( @@ -50,7 +38,7 @@ pub extern "system" fn Java_org_example_MockIO_print<'local>( // println!("Yeetus bageetus! Im printing from rust!") } -#[unsafe(no_mangle)] +/*#[unsafe(no_mangle)] pub extern "system" fn Java_org_example_MockIO_registerNatives<'local>( mut env: JNIEnv<'local>, jclass: JClass<'local>, @@ -79,7 +67,7 @@ pub extern "system" fn Java_org_example_MockIO_registerNatives<'local>( // env.register_native_methods(library_class, &library_methods) // .expect("failed to register method"); -} +}*/ #[unsafe(no_mangle)] pub extern "system" fn findEntry0<'local>( @@ -153,10 +141,12 @@ pub extern "system" fn Java_org_example_MockIO_println<'local>( // } unsafe fn get_thread(env: *mut jni::sys::JNIEnv) -> *const VmThread { - (**env).reserved0 as *const VmThread + let thread = (**env).reserved0 as *const VmThread; + VmThread::set_current((*thread).id); + thread } fn resolve_object(thread: &VmThread, obj: jobject) -> Option { - let gc = thread.gc.read().unwrap(); + let gc = thread.gc.read(); let ReferenceKind::ObjectReference(obj_ref) = gc.get(obj as u32) else { return None; }; @@ -164,13 +154,21 @@ fn resolve_object(thread: &VmThread, obj: jobject) -> Option { } fn resolve_array(thread: &VmThread, obj: jobject) -> Option { - let gc = thread.gc.read().unwrap(); + let gc = thread.gc.read(); let ReferenceKind::ArrayReference(arr_ref) = gc.get(obj as u32) else { return None; }; Some(arr_ref.clone()) } +fn resolve_reference(thread: &VmThread, obj: jobject) -> Option { + if obj.is_null() { + return None; + } + let gc = thread.gc.read(); + Some(gc.get(obj as u32).clone()) +} + #[unsafe(no_mangle)] pub extern "system" fn Java_jdk_internal_util_SystemProps_00024Raw_vmProperties<'local>( mut env: JNIEnv<'local>, diff --git a/crates/roast-vm-sys/src/misc_unsafe.rs b/crates/roast-vm-sys/src/misc_unsafe.rs index fbb8dd2..71a5bec 100644 --- a/crates/roast-vm-sys/src/misc_unsafe.rs +++ b/crates/roast-vm-sys/src/misc_unsafe.rs @@ -1,6 +1,11 @@ -use jni::sys::{jclass, jint, jobject, jstring, JNIEnv}; +use crate::{get_thread, resolve_array, resolve_object}; +use jni::sys::{jboolean, jclass, jint, jlong, jobject, jstring, JNIEnv, JNI_FALSE, JNI_TRUE}; use log::warn; -use crate::{get_thread, resolve_object}; +use roast_vm_core::native::r#unsafe::OffsetKind; +use roast_vm_core::objects::array::ArrayReference; +use roast_vm_core::objects::object::string_from_bytes; +use roast_vm_core::objects::ReferenceKind; +use roast_vm_core::value::Value; #[unsafe(no_mangle)] pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_registerNatives( @@ -8,13 +13,14 @@ pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_registerNatives( obj: jclass, ) { //no op - () + println!("Unsafe_registerNatives is NO OP") } #[unsafe(no_mangle)] pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_arrayBaseOffset0( - env: *mut JNIEnv, - obj: jclass, + _env: *mut JNIEnv, + _unsafe_obj: jobject, + _class: jclass, ) -> jint { warn!("arrayBaseOffset0 currently just returning 0"); 0 @@ -23,8 +29,545 @@ pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_arrayBaseOffset0( #[unsafe(no_mangle)] pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_arrayIndexScale0( env: *mut JNIEnv, - obj: jclass, + _unsafe_obj: jobject, + class: jclass, ) -> jint { - warn!("arrayIndexScale0 currently just returning 0"); + let thread = &*get_thread(env); + let class_obj = resolve_object(thread, class).unwrap(); + let class_id = class_obj.lock().id; + let runtime_class = thread + .vm + .loader + .lock() + .class_from_mirror_id(class_id) + .unwrap(); + + // Get component type from array class name + match runtime_class.this_class.as_str() { + "[Z" => 1, // boolean + "[B" => 1, // byte + "[C" => 2, // char + "[S" => 2, // short + "[I" => 4, // int + "[F" => 4, // float + "[J" => 8, // long + "[D" => 8, // double + s if s.starts_with("[L") || s.starts_with("[[") => 8, // reference + _ => { + warn!( + "arrayIndexScale0: unknown array type {}", + runtime_class.this_class + ); + 0 + } + } +} + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_getReferenceVolatile( + env: *mut JNIEnv, + _unsafe_obj: jobject, + obj: jobject, + offset: jlong, +) -> jobject { + let thread = &*get_thread(env); + let reference = thread.gc.read().get(obj as u32); + + match reference { + ReferenceKind::ArrayReference(array_ref) => { + if let ArrayReference::Object(arr) = array_ref { + let guard = arr.lock(); + let index = (offset / 8) as i32; + match guard.get(index) { + Some(ref_kind) => ref_kind.id() as jobject, + None => std::ptr::null_mut(), + } + } else { + panic!("getReferenceVolatile on non-object array") + } + } + ReferenceKind::ObjectReference(obj_ref) => { + let key = thread + .vm + .safent + .read() + .resolve_field(offset) + .expect("unregistered field offset"); + let guard = obj_ref.lock(); + let value = guard.fields.get(&key.field_name).unwrap(); + value + .try_into_object_reference() + .map(|r| r.lock().id as jobject) + .unwrap_or(std::ptr::null_mut()) + } + } +} + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_compareAndSetReference( + env: *mut JNIEnv, + _unsafe_obj: jobject, + obj: jobject, + offset: jlong, + expected: jobject, + new_val: jobject, +) -> jboolean { + let thread = &*get_thread(env); + let reference = thread.gc.read().get(obj as u32); + + match reference { + ReferenceKind::ArrayReference(array_ref) => { + if let ArrayReference::Object(arr) = array_ref { + let mut guard = arr.lock(); + let index = (offset / 8) as usize; + + let current_id = guard.backing[index].as_ref().map(|r| r.id()).unwrap_or(0); + let expected_id = if expected.is_null() { + 0 + } else { + expected as u32 + }; + + if current_id == expected_id { + guard.backing[index] = if new_val.is_null() { + None + } else { + Some(thread.gc.read().get(new_val as u32)) + }; + JNI_TRUE + } else { + JNI_FALSE + } + } else { + panic!("compareAndSetReference on non-object array") + } + } + ReferenceKind::ObjectReference(obj_ref) => { + let key = thread + .vm + .safent + .read() + .resolve_field(offset) + .expect("unregistered field offset"); + let mut guard = obj_ref.lock(); + + let current_id = guard + .fields + .get(&key.field_name) + .and_then(|v| v.try_into_object_reference().ok()) + .map(|r| r.lock().id) + .unwrap_or(0); + let expected_id = if expected.is_null() { + 0 + } else { + expected as u32 + }; + + if current_id == expected_id { + let new_value = if new_val.is_null() { + Value::NULL // however you represent null + } else { + Value::from(thread.gc.read().get(new_val as u32)) + }; + guard.fields.insert(key.field_name, new_value); + JNI_TRUE + } else { + JNI_FALSE + } + } + } +} + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_compareAndSetInt( + env: *mut JNIEnv, + _unsafe_obj: jobject, + obj: jobject, + offset: jlong, + expected: jint, + new_val: jint, +) -> jboolean { + let thread = &*get_thread(env); + let reference = thread.gc.read().get(obj as u32); + + match reference { + ReferenceKind::ArrayReference(array_ref) => { + if let ArrayReference::Int(arr) = array_ref { + let mut guard = arr.lock(); + let index = (offset / 4) as usize; + if guard.backing[index] == expected { + guard.backing[index] = new_val; + JNI_TRUE + } else { + JNI_FALSE + } + } else { + panic!("compareAndSetInt on non-int array") + } + } + ReferenceKind::ObjectReference(obj_ref) => { + let key = thread + .vm + .safent + .read() + .resolve_field(offset) + .expect("unregistered field offset"); + let mut guard = obj_ref.lock(); + let current = guard + .fields + .get(&key.field_name) + .unwrap() + .clone() + .try_into_jint() + .unwrap(); + if current == expected { + guard.fields.insert(key.field_name, Value::from(new_val)); + JNI_TRUE + } else { + JNI_FALSE + } + } + } +} + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_compareAndSetLong( + env: *mut JNIEnv, + _unsafe_obj: jobject, + obj: jobject, + offset: jlong, + expected: jlong, + new_val: jlong, +) -> jboolean { + let thread = &*get_thread(env); + let reference = thread.gc.read().get(obj as u32); + + match reference { + ReferenceKind::ArrayReference(array_ref) => { + if let ArrayReference::Long(arr) = array_ref { + let mut guard = arr.lock(); + let index = (offset / 8) as usize; + if guard.backing[index] == expected { + guard.backing[index] = new_val; + JNI_TRUE + } else { + JNI_FALSE + } + } else { + panic!("compareAndSetLong on non-long array") + } + } + ReferenceKind::ObjectReference(obj_ref) => { + let key = thread + .vm + .safent + .read() + .resolve_field(offset) + .expect("unregistered field offset"); + let mut guard = obj_ref.lock(); + let current = guard + .fields + .get(&key.field_name) + .unwrap() + .clone() + .try_into_jlong() + .unwrap(); + if current == expected { + guard.fields.insert(key.field_name, Value::from(new_val)); + JNI_TRUE + } else { + JNI_FALSE + } + } + } +} + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_compareAndExchangeInt( + env: *mut JNIEnv, + _unsafe_obj: jobject, + obj: jobject, + offset: jlong, + expected: jint, + new_val: jint, +) -> jint { + let thread = &*get_thread(env); + let reference = thread.gc.read().get(obj as u32); + + match reference { + ReferenceKind::ArrayReference(array_ref) => { + if let ArrayReference::Int(arr) = array_ref { + let mut guard = arr.lock(); + let index = (offset / 4) as usize; + let current = guard.backing[index]; + if current == expected { + guard.backing[index] = new_val; + } + current + } else { + panic!("compareAndExchangeInt on non-int array") + } + } + ReferenceKind::ObjectReference(obj_ref) => { + let key = thread + .vm + .safent + .read() + .resolve_field(offset) + .expect("unregistered field offset"); + let mut guard = obj_ref.lock(); + let current = guard + .fields + .get(&key.field_name) + .unwrap() + .clone() + .try_into_jint() + .unwrap(); + if current == expected { + guard.fields.insert(key.field_name, Value::from(new_val)); + } + current + } + } +} + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_compareAndExchangeLong( + env: *mut JNIEnv, + _unsafe_obj: jobject, + obj: jobject, + offset: jlong, + expected: jlong, + new_val: jlong, +) -> jlong { + let thread = &*get_thread(env); + let reference = thread.gc.read().get(obj as u32); + + match reference { + ReferenceKind::ArrayReference(array_ref) => { + if let ArrayReference::Long(arr) = array_ref { + let mut guard = arr.lock(); + let index = (offset / 8) as usize; + let current = guard.backing[index]; + if current == expected { + guard.backing[index] = new_val; + } + current + } else { + panic!("compareAndExchangeLong on non-long array") + } + } + ReferenceKind::ObjectReference(obj_ref) => { + let key = thread + .vm + .safent + .read() + .resolve_field(offset) + .expect("unregistered field offset"); + let mut guard = obj_ref.lock(); + let current = guard + .fields + .get(&key.field_name) + .unwrap() + .clone() + .try_into_jlong() + .unwrap(); + if current == expected { + guard.fields.insert(key.field_name, Value::from(new_val)); + } + current + } + } +} + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_compareAndExchangeReference( + env: *mut JNIEnv, + _unsafe_obj: jobject, + obj: jobject, + offset: jlong, + expected: jobject, + new_val: jobject, +) -> jobject { + let thread = &*get_thread(env); + let reference = thread.gc.read().get(obj as u32); + + match reference { + ReferenceKind::ArrayReference(array_ref) => { + if let ArrayReference::Object(arr) = array_ref { + let mut guard = arr.lock(); + let index = (offset / 8) as usize; + + let current = guard.backing[index].clone(); + let current_id = current.as_ref().map(|r| r.id()).unwrap_or(0); + let expected_id = if expected.is_null() { + 0 + } else { + expected as u32 + }; + + if current_id == expected_id { + guard.backing[index] = if new_val.is_null() { + None + } else { + Some(thread.gc.read().get(new_val as u32)) + }; + } + current + .map(|r| r.id() as jobject) + .unwrap_or(std::ptr::null_mut()) + } else { + panic!("compareAndExchangeReference on non-object array") + } + } + ReferenceKind::ObjectReference(obj_ref) => { + let key = thread + .vm + .safent + .read() + .resolve_field(offset) + .expect("unregistered field offset"); + let mut guard = obj_ref.lock(); + + let current = guard + .fields + .get(&key.field_name) + .and_then(|v| v.try_into_object_reference().ok()); + let current_id = current.as_ref().map(|r| r.lock().id).unwrap_or(0); + let expected_id = if expected.is_null() { + 0 + } else { + expected as u32 + }; + + if current_id == expected_id { + let new_value = if new_val.is_null() { + Value::NULL + } else { + Value::from(thread.gc.read().get(new_val as u32)) + }; + guard.fields.insert(key.field_name, new_value); + } + current + .map(|r| r.lock().id as jobject) + .unwrap_or(std::ptr::null_mut()) + } + } +} + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_objectFieldOffset0( + env: *mut JNIEnv, + _unsafe_obj: jobject, + field: jobject, +) -> jlong { + let thread = &*get_thread(env); + let field_obj = resolve_object(thread, field).unwrap(); + let clazz_ref = field_obj + .lock() + .fields + .get("clazz") + .unwrap() + .try_into_object_reference() + .unwrap(); + let clazz_guard = clazz_ref.lock(); + let class = thread + .loader + .lock() + .class_from_mirror_id(clazz_guard.id) + .unwrap(); + let name_obj = field_obj + .lock() + .fields + .get("name") + .unwrap() + .try_into_object_reference() + .unwrap(); + let class_name = class.this_class.clone(); + let field_name = thread.gc.read().transmute_string(name_obj).unwrap(); + + thread + .vm + .safent + .write() + .register_field_offset(&class_name, &field_name, false) + + // warn!("objectFieldOffset0 currently just returning 0"); + // 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_objectFieldOffset1( + env: *mut JNIEnv, + _unsafe_obj: jobject, + class: jclass, + name: jstring, +) -> jlong { + let thread = &*get_thread(env); + + let class_obj = resolve_object(thread, class).unwrap(); + let class_id = class_obj.lock().id; + let runtime_class = thread + .vm + .loader + .lock() + .class_from_mirror_id(class_id) + .unwrap(); + let class_name = runtime_class.this_class.clone(); + + let field_name = thread + .vm + .gc + .read() + .transmute_string(resolve_object(thread, name).unwrap()) + .unwrap(); + + thread + .vm + .safent + .write() + .register_field_offset(&class_name, &field_name, false) +} + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_staticFieldOffset0( + _env: *mut JNIEnv, + _unsafe_obj: jobject, + _field: jobject, +) -> jlong { + warn!("staticFieldOffset0 currently just returning 0"); 0 -} \ No newline at end of file +} + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_staticFieldBase0( + _env: *mut JNIEnv, + _unsafe_obj: jobject, + _field: jobject, +) -> jobject { + todo!("staticFieldBase0") +} + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_loadFence( + _env: *mut JNIEnv, + _unsafe_obj: jobject, +) { + std::sync::atomic::fence(std::sync::atomic::Ordering::Acquire); +} + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_storeFence( + _env: *mut JNIEnv, + _unsafe_obj: jobject, +) { + std::sync::atomic::fence(std::sync::atomic::Ordering::Release); +} + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_fullFence( + _env: *mut JNIEnv, + _unsafe_obj: jobject, +) { + std::sync::atomic::fence(std::sync::atomic::Ordering::SeqCst); +} diff --git a/crates/roast-vm-sys/src/object.rs b/crates/roast-vm-sys/src/object.rs index 9dd361a..74e840a 100644 --- a/crates/roast-vm-sys/src/object.rs +++ b/crates/roast-vm-sys/src/object.rs @@ -1,11 +1,29 @@ -use jni::sys::{jclass, jint, jobject, jstring, JNIEnv}; -use crate::{get_thread, resolve_object}; +use crate::get_thread; +use jni::sys::{jint, jobject, JNIEnv}; #[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 +} + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn Java_java_lang_Object_clone( + env: *mut JNIEnv, + obj: jobject, +) -> jobject { + let thread = &*get_thread(env); + let mut gc = thread.gc.write(); + + let source = gc.get(obj as u32); + match gc.clone_object(&source) { + Ok(cloned) => cloned.id() as jobject, + Err(e) => { + // TODO: throw CloneNotSupportedException + eprintln!("Clone failed: {:?}", e); + std::ptr::null_mut() + } + } +} diff --git a/crates/roast-vm-sys/src/reflection.rs b/crates/roast-vm-sys/src/reflection.rs new file mode 100644 index 0000000..f2757b8 --- /dev/null +++ b/crates/roast-vm-sys/src/reflection.rs @@ -0,0 +1,95 @@ +use crate::get_thread; +use jni::sys::{jboolean, jclass, jint, jobject, jobjectArray, JNIEnv}; +use roast_vm_core::class_file::FieldRef; +use roast_vm_core::objects::array::ArrayReference; +use roast_vm_core::value::Value; +use roast_vm_core::{BaseType, FieldType, MethodDescriptor}; + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn Java_jdk_internal_reflect_Reflection_getCallerClass( + env: *mut JNIEnv, + _class: jobject, +) -> jclass { + let thread = &*get_thread(env); + let mut stack = thread.frame_stack.lock().clone(); + stack.pop(); + let caller = stack.pop().unwrap().class.mirror(); + + caller as jclass +} + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn Java_jdk_internal_reflect_Reflection_getClassAccessFlags( + env: *mut JNIEnv, + reflection_class: jclass, + class: jclass, +) -> jint { + let thread = &*get_thread(env); + let klass = thread + .loader + .lock() + .class_from_mirror_id(class as u32) + .unwrap(); + let modifiers: u16 = klass.access_flags.into(); + + modifiers as jint +} + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn Java_jdk_internal_reflect_DirectConstructorHandleAccessor_00024NativeAccessor_newInstance0( + env: *mut JNIEnv, + DirectConstructorHandleAccessor_class: jclass, + constructor: jobject, + args: jobjectArray, +) -> jobject { + let thread = &*get_thread(env); + let binding = thread + .gc + .read() + .get(constructor as u32) + .try_into_object_reference() + .unwrap(); + let constructor = binding.lock(); + let constructor_runtime_class = constructor.class.clone(); + let clazz_ref = FieldRef { + class: constructor_runtime_class.this_class.clone(), + name: "clazz".to_string(), + desc: FieldType::ClassType("Ljava/lang/Class;".to_string()), + }; + let clazz_id = constructor + .get_field(&clazz_ref) + .as_ref_kind() + .unwrap() + .id(); + let slot_ref = FieldRef { + class: "int".to_string(), + name: "slot".to_string(), + desc: FieldType::Base(BaseType::Int), + }; + let slot = constructor.get_field(&slot_ref).try_into_jint().unwrap(); + let klass = thread.loader.lock().class_from_mirror_id(clazz_id).unwrap(); + let method = klass.get_constructor_ref(slot as usize).unwrap(); + + let mut values: Vec = if !args.is_null() { + if let ArrayReference::Object(arr) = thread + .gc + .read() + .get(args as u32) + .try_into_array_reference() + .unwrap() + { + arr.lock().iter().cloned().map(Value::from).collect() + } else { + Vec::new() + } + } else { + Vec::new() + }; + + let instance = thread.gc.write().new_object(klass.clone()); + values.insert(0, instance.clone().into()); + + thread.invoke(method.clone().into(), values).unwrap(); + + instance.lock().id as jobject +} diff --git a/crates/roast-vm-sys/src/runtime.rs b/crates/roast-vm-sys/src/runtime.rs new file mode 100644 index 0000000..d08b5e7 --- /dev/null +++ b/crates/roast-vm-sys/src/runtime.rs @@ -0,0 +1,22 @@ +use crate::get_thread; +use jni::sys::{jint, jlong, jobject, JNIEnv}; + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn Java_java_lang_Runtime_maxMemory( + env: *mut JNIEnv, + _obj: jobject, +) -> jlong { + let thread = &*get_thread(env); + let max_mem = thread.gc.read().max_memory(); + i64::try_from(max_mem).unwrap_or(i64::MAX) +} + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn Java_java_lang_Runtime_availableProcessors( + _env: *mut JNIEnv, + _obj: jobject, +) -> jint { + std::thread::available_parallelism() + .map(|n| n.get()) + .unwrap_or(1) as jint +} diff --git a/crates/roast-vm-sys/src/system.rs b/crates/roast-vm-sys/src/system.rs index 67d248b..cb8e4c5 100644 --- a/crates/roast-vm-sys/src/system.rs +++ b/crates/roast-vm-sys/src/system.rs @@ -1,6 +1,8 @@ -use jni::sys::{jclass, jint, jobject, JNIEnv}; -use log::warn; use crate::{get_thread, resolve_array, resolve_object}; +use jni::objects::JClass; +use jni::sys::{jclass, jint, jlong, jobject, JNIEnv}; +use log::warn; +use std::time::{SystemTime, UNIX_EPOCH}; #[unsafe(no_mangle)] pub unsafe extern "system" fn Java_java_lang_System_arraycopy( @@ -36,16 +38,45 @@ pub unsafe extern "system" fn Java_java_lang_System_arraycopy( let dst_len = dst_arr.len(); // Bounds checking - if src_pos < 0 || dst_pos < 0 || length < 0 - || src_pos.checked_add(length).map_or(true, |end| end > src_len) - || dst_pos.checked_add(length).map_or(true, |end| end > dst_len) + if src_pos < 0 + || dst_pos < 0 + || length < 0 + || src_pos + .checked_add(length) + .map_or(true, |end| end > src_len) + || dst_pos + .checked_add(length) + .map_or(true, |end| end > dst_len) { panic!("Array index out of bounds!"); // throw_exception(env, "java/lang/ArrayIndexOutOfBoundsException", None); return; } - - dst_arr.copy_from(&src_arr, src_pos, dst_pos, length).expect("Array copy error hell"); + dst_arr + .copy_from(&src_arr, src_pos, dst_pos, length) + .expect("Array copy error hell"); // Type compatibility check + copy -} \ No newline at end of file +} + +#[unsafe(no_mangle)] +pub extern "system" fn current_time_millis<'local>( + mut env: jni::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_java_lang_System_nanoTime<'local>( + mut env: jni::JNIEnv<'local>, + jclass: JClass<'local>, +) -> jlong { + SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_nanos() as jlong +} diff --git a/rustfmt.toml b/rustfmt.toml index 75c686c..10521f4 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,79 +1,4 @@ -hard_tabs = true - -newline_style = "Auto" -indent_style = "Block" -use_small_heuristics = "Default" -fn_call_width = 60 -attr_fn_like_width = 70 -struct_lit_width = 18 -struct_variant_width = 35 -array_width = 60 -chain_width = 60 -single_line_if_else_max_width = 50 -single_line_let_else_max_width = 50 -wrap_comments = false -format_code_in_doc_comments = false -doc_comment_code_block_width = 100 -comment_width = 80 -normalize_comments = false -normalize_doc_attributes = false -format_strings = false -format_macro_matchers = false -format_macro_bodies = true -skip_macro_invocations = [] -hex_literal_case = "Preserve" -empty_item_single_line = true -struct_lit_single_line = true -fn_single_line = false -where_single_line = false -imports_indent = "Block" -imports_layout = "Mixed" -imports_granularity = "Preserve" -group_imports = "Preserve" -reorder_imports = true -reorder_modules = true -reorder_impl_items = false -type_punctuation_density = "Wide" -space_before_colon = false -space_after_colon = true -spaces_around_ranges = false -binop_separator = "Front" -remove_nested_parens = true -combine_control_expr = true -short_array_element_width_threshold = 10 -overflow_delimited_expr = false -struct_field_align_threshold = 0 -enum_discrim_align_threshold = 0 -match_arm_blocks = true -match_arm_leading_pipes = "Never" -force_multiline_blocks = false -fn_params_layout = "Tall" -brace_style = "SameLineWhere" -control_brace_style = "AlwaysSameLine" -trailing_semicolon = true -trailing_comma = "Vertical" -match_block_trailing_comma = false -blank_lines_upper_bound = 1 -blank_lines_lower_bound = 0 -edition = "2015" -style_edition = "2015" -version = "One" -inline_attribute_width = 0 -format_generated_files = true -generated_marker_line_search_limit = 5 -merge_derives = true -use_try_shorthand = false -use_field_init_shorthand = false -force_explicit_abi = true -condense_wildcard_suffixes = false -color = "Auto" -required_version = "1.8.0" -unstable_features = false -disable_all_formatting = false -skip_children = false -show_parse_errors = true -error_on_line_overflow = false -error_on_unformatted = false -ignore = [] -emit_mode = "Files" -make_backup = false +edition = "2024" +style_edition = "2024" +hard_tabs = true +hex_literal_case = "Upper"