From 065bd9f3c77c14aba914fd31fa8bf62508f23fbe Mon Sep 17 00:00:00 2001 From: james Date: Sun, 23 Nov 2025 03:19:03 +1030 Subject: [PATCH] Object initialisation --- crates/core/src/attributes.rs | 452 +++----------------- crates/core/src/class_file/class_file.rs | 3 +- crates/core/src/class_file/constant_pool.rs | 21 +- crates/core/src/class_loader.rs | 27 +- crates/core/src/instructions.rs | 386 +++++++++++++++++ crates/core/src/lib.rs | 409 +++++++++++++----- crates/core/src/macros.rs | 101 +++-- crates/core/src/object.rs | 30 +- crates/core/src/object_manager.rs | 35 ++ crates/core/src/thread.rs | 131 +++++- crates/core/src/vm.rs | 6 +- crates/jvm-rs-sys/src/lib.rs | 13 + 12 files changed, 1025 insertions(+), 589 deletions(-) create mode 100644 crates/core/src/instructions.rs create mode 100644 crates/core/src/object_manager.rs diff --git a/crates/core/src/attributes.rs b/crates/core/src/attributes.rs index 04ba42b..875d24b 100644 --- a/crates/core/src/attributes.rs +++ b/crates/core/src/attributes.rs @@ -1,10 +1,10 @@ +use crate::class_file::constant_pool::ConstantPoolExt; +use crate::class_file::{ClassFile, Constant, ConstantPoolEntry}; +use deku::DekuContainerRead; +use deku_derive::DekuRead; +use log::trace; use std::fmt::Display; use std::ops::Deref; -use deku_derive::DekuRead; -use deku::DekuContainerRead; -use log::trace; -use crate::class_file::{ClassFile, Constant, ConstantPoolEntry}; -use crate::class_file::constant_pool::ConstantPoolExt; #[derive(Clone, PartialEq, Debug, DekuRead)] #[deku(ctx = "_endian: deku::ctx::Endian", endian = "big")] @@ -59,376 +59,6 @@ pub enum ArrayType { #[deku(id = "11")] T_LONG, } -//noinspection SpellCheckingInspection -#[allow(non_camel_case_types)] -#[derive(Clone, PartialEq, Debug, DekuRead)] -#[deku(id_type = "u8", ctx = "_endian: deku::ctx::Endian", endian = "big")] -pub enum Ops { - // Constants - #[deku(id = 0x00)] - nop, - #[deku(id = 0x01)] - aconst_null, - #[deku(id = 0x02)] - iconst_m1, - #[deku(id = 0x03)] - iconst_0, - #[deku(id = 0x04)] - iconst_1, - #[deku(id = 0x05)] - iconst_2, - #[deku(id = 0x06)] - iconst_3, - #[deku(id = 0x07)] - iconst_4, - #[deku(id = 0x08)] - iconst_5, - #[deku(id = 0x09)] - lconst_0, - #[deku(id = 0x0a)] - lconst_1, - #[deku(id = 0x0b)] - fconst_0, - #[deku(id = 0x0c)] - fconst_1, - #[deku(id = 0x0d)] - fconst_2, - #[deku(id = 0x0e)] - dconst_0, - #[deku(id = 0x0f)] - dconst_1, - #[deku(id = 0x10)] - bipush(u8), - #[deku(id = 0x11)] - sipush(u16), - #[deku(id = 0x12)] - ldc(u8), - #[deku(id = 0x13)] - ldc_w(u16), - #[deku(id = 0x14)] - ldc2_w(u16), - - // Loads - #[deku(id = 0x15)] - iload(u8), - #[deku(id = 0x16)] - lload(u8), - #[deku(id = 0x17)] - fload(u8), - #[deku(id = 0x18)] - dload(u8), - #[deku(id = 0x19)] - aload(u8), - #[deku(id = 0x1A)] - iload_0, - #[deku(id = 0x1B)] - iload_1, - #[deku(id = 0x1C)] - iload_2, - #[deku(id = 0x1D)] - iload_3, - #[deku(id = 0x1E)] - lload_0, - #[deku(id = 0x1F)] - lload_1, - #[deku(id = 0x20)] - lload_2, - #[deku(id = 0x21)] - lload_3, - #[deku(id = 0x22)] - fload_0, - #[deku(id = 0x23)] - fload_1, - #[deku(id = 0x24)] - fload_2, - #[deku(id = 0x25)] - fload_3, - #[deku(id = 0x26)] - dload_0, - #[deku(id = 0x27)] - dload_1, - #[deku(id = 0x28)] - dload_2, - #[deku(id = 0x29)] - dload_3, - #[deku(id = 0x2A)] - aload_0, - #[deku(id = 0x2B)] - aload_1, - #[deku(id = 0x2C)] - aload_2, - #[deku(id = 0x2D)] - aload_3, - #[deku(id = 0x2E)] - iaload, - #[deku(id = 0x2F)] - laload, - #[deku(id = 0x30)] - faload, - #[deku(id = 0x31)] - daload, - #[deku(id = 0x32)] - aaload, - #[deku(id = 0x33)] - baload, - #[deku(id = 0x34)] - caload, - #[deku(id = 0x35)] - saload, - - // Stores - #[deku(id = 0x36)] - istore(u8), - #[deku(id = 0x37)] - lstore(u8), - #[deku(id = 0x38)] - fstore(u8), - #[deku(id = 0x39)] - dstore(u8), - #[deku(id = 0x3A)] - astore(u8), - #[deku(id = 0x3B)] - istore_0, - #[deku(id = 0x3C)] - istore_1, - #[deku(id = 0x3D)] - istore_2, - #[deku(id = 0x3E)] - istore_3, - #[deku(id = 0x3F)] - lstore_0, - #[deku(id = 0x40)] - lstore_1, - #[deku(id = 0x41)] - lstore_2, - #[deku(id = 0x42)] - lstore_3, - #[deku(id = 0x43)] - fstore_0, - #[deku(id = 0x44)] - fstore_1, - #[deku(id = 0x45)] - fstore_2, - #[deku(id = 0x46)] - fstore_3, - #[deku(id = 0x47)] - dstore_0, - #[deku(id = 0x48)] - dstore_1, - #[deku(id = 0x49)] - dstore_2, - #[deku(id = 0x4A)] - dstore_3, - #[deku(id = 0x4B)] - astore_0, - #[deku(id = 0x4C)] - astore_1, - #[deku(id = 0x4D)] - astore_2, - #[deku(id = 0x4E)] - astore_3, - #[deku(id = 0x4F)] - iastore, - #[deku(id = 0x50)] - lastore, - #[deku(id = 0x51)] - fastore, - #[deku(id = 0x52)] - dastore, - #[deku(id = 0x53)] - aastore, - #[deku(id = 0x54)] - bastore, - #[deku(id = 0x55)] - castore, - #[deku(id = 0x56)] - sastore, - - //stack - - //math - #[deku(id = 0x60)] - iadd, - #[deku(id = 0x61)] - ladd, - #[deku(id = 0x62)] - fadd, - #[deku(id = 0x63)] - dadd, - #[deku(id = 0x64)] - isub, - #[deku(id = 0x65)] - lsub, - #[deku(id = 0x66)] - fsub, - #[deku(id = 0x67)] - dsub, - #[deku(id = 0x68)] - imul, - #[deku(id = 0x69)] - lmul, - #[deku(id = 0x6a)] - fmul, - #[deku(id = 0x6b)] - dmul, - #[deku(id = 0x6c)] - idiv, - #[deku(id = 0x6d)] - ldiv, - #[deku(id = 0x6e)] - fdiv, - #[deku(id = 0x6f)] - ddiv, - #[deku(id = 0x70)] - irem, - #[deku(id = 0x71)] - lrem, - #[deku(id = 0x72)] - frem, - #[deku(id = 0x73)] - drem, - #[deku(id = 0x74)] - ineg, - #[deku(id = 0x75)] - lneg, - #[deku(id = 0x76)] - fneg, - #[deku(id = 0x77)] - dneg, - #[deku(id = 0x78)] - ishl, - #[deku(id = 0x79)] - lshl, - #[deku(id = 0x7a)] - ishr, - #[deku(id = 0x7b)] - lshr, - #[deku(id = 0x7c)] - iushr, - #[deku(id = 0x7d)] - lushr, - #[deku(id = 0x7e)] - iand, - #[deku(id = 0x7f)] - land, - #[deku(id = 0x80)] - ior, - #[deku(id = 0x81)] - lor, - #[deku(id = 0x82)] - ixor, - #[deku(id = 0x83)] - lxor, - #[deku(id = 0x84)] - iinc(u8, i8), - - //conversions - #[deku(id = 0x85)] - i2l, - #[deku(id = 0x86)] - i2f, - #[deku(id = 0x87)] - i2d, - #[deku(id = 0x88)] - l2i, - #[deku(id = 0x89)] - l2f, - #[deku(id = 0x8a)] - l2d, - #[deku(id = 0x8b)] - f2i, - #[deku(id = 0x8c)] - f2l, - #[deku(id = 0x8d)] - f2d, - #[deku(id = 0x8e)] - d2i, - #[deku(id = 0x8f)] - d2l, - #[deku(id = 0x90)] - d2f, - #[deku(id = 0x91)] - i2b, - #[deku(id = 0x92)] - i2c, - #[deku(id = 0x93)] - i2s, - - // comparisons - - // control - #[deku(id = 0xa7)] - goto(u16), - - // discontinued - #[deku(id = 0xa8)] - jsr(u16), - #[deku(id = 0xa9)] - ret(u8), - // - - #[deku(id = 0xaa)] - tableswitch, - #[deku(id = 0xab)] - lookupswitch, - #[deku(id = 0xac)] - ireturn, - #[deku(id = 0xad)] - lreturn, - #[deku(id = 0xae)] - freturn, - #[deku(id = 0xaf)] - dreturn, - #[deku(id = 0xb0)] - areturn, - // return - #[deku(id = 0xb1)] - return_void, - - // references - #[deku(id = 0xB2)] - getstatic(u16), - #[deku(id = 0xB3)] - putstatic(u16), - #[deku(id = 0xB4)] - getfield(u16), - #[deku(id = 0xB5)] - putfield(u16), - #[deku(id = 0xB6)] - invokevirtual(u16), - #[deku(id = 0xB7)] - invokespecial(u16), - #[deku(id = 0xB8)] - invokestatic(u16), - // 4th byte always zero/0 - #[deku(id = 0xB9)] - invokeinterface(u16, u8, u8), - // 3rd, 4th must be zero/0 - #[deku(id = 0xBA)] - invokedynamic(u16, u16), - #[deku(id = 0xBB)] - new(u16), - #[deku(id = 0xBC)] - newarray(ArrayType), - #[deku(id = 0xBD)] - anewarray(u16), - #[deku(id = 0xBE)] - arraylength, - #[deku(id = 0xBF)] - athrow, - #[deku(id = 0xC0)] - checkcast(u16), - #[deku(id = 0xC1)] - instanceof(u16), - #[deku(id = 0xC2)] - monitorenter, - #[deku(id = 0xC3)] - monitorexit, -} - - - - // impl TryFrom for Ops { // type Error = (); @@ -500,8 +130,10 @@ impl AttributeInfo { /// Get the interpreted attribute, parsing if necessary pub fn get(&self, class_file: &ClassFile) -> Option { - class_file.constant_pool.parse_attribute(self.deref().clone()).ok() - + class_file + .constant_pool + .parse_attribute(self.deref().clone()) + .ok() // if let Some(ref attr) = self.interpreted { // Some(attr.clone()) @@ -511,15 +143,14 @@ impl AttributeInfo { } } -impl LocalVariableTableAttribute { - -} +impl LocalVariableTableAttribute {} impl Display for AttributeInfo { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - writeln!(f, "AttributeInfo {{ name_index: {}, length: {} }}", - self.attribute_name_index, - self.attribute_length + writeln!( + f, + "AttributeInfo {{ name_index: {}, length: {} }}", + self.attribute_name_index, self.attribute_length ) } } @@ -530,25 +161,37 @@ impl Display for Attribute { Attribute::Code(code) => write!(f, "Code attribute: {}", code), Attribute::SourceFile(index) => write!(f, "SourceFile attribute, index: {}", index), Attribute::LineNumberTable(data) => write!(f, "LineNumberTable attribute: {}", data), - Attribute::StackMapTable(data) => write!(f, "StackMapTable attribute, {} bytes", data.len()), + Attribute::StackMapTable(data) => { + write!(f, "StackMapTable attribute, {} bytes", data.len()) + } Attribute::Exceptions(data) => write!(f, "Exceptions attribute, {} bytes", data.len()), - Attribute::InnerClasses(data) => write!(f, "InnerClasses attribute, {} bytes", data.len()), + Attribute::InnerClasses(data) => { + write!(f, "InnerClasses attribute, {} bytes", data.len()) + } Attribute::Signature(index) => write!(f, "Signature attribute, index: {}", index), - Attribute::LocalVariableTable(table) => write!(f, "LocalVariableTable attribute: {}", table), - Attribute::Unknown(name, data) => write!(f, "Unknown attribute '{}', {} bytes", name, data.len()), - _ => { unreachable!() } + Attribute::LocalVariableTable(table) => { + write!(f, "LocalVariableTable attribute: {}", table) + } + Attribute::Unknown(name, data) => { + write!(f, "Unknown attribute '{}', {} bytes", name, data.len()) + } + _ => { + unreachable!() + } } } } impl Display for CodeAttribute { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "stack={}, locals={}, code={} bytes, exceptions={}, attributes={}", - self.max_stack, - self.max_locals, - self.code_length, - self.exception_table_length, - self.attributes_count + write!( + f, + "stack={}, locals={}, code={} bytes, exceptions={}, attributes={}", + self.max_stack, + self.max_locals, + self.code_length, + self.exception_table_length, + self.attributes_count ) } } @@ -598,14 +241,15 @@ pub struct LocalVariableTableEntry { impl Display for LocalVariableTableAttribute { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "local_variable_table_length={}, entries={}", - self.local_variable_table_length, - self.local_variable_table.len() + write!( + f, + "local_variable_table_length={}, entries={}", + self.local_variable_table_length, + self.local_variable_table.len() ) } } - #[derive(Clone, PartialEq, Debug, DekuRead)] #[deku(endian = "big")] pub struct LineNumberTableAttribute { @@ -614,8 +258,6 @@ pub struct LineNumberTableAttribute { pub line_number_table: Vec, } - - #[derive(Clone, PartialEq, Debug, DekuRead)] #[deku(ctx = "_endian: deku::ctx::Endian", endian = "big")] pub struct LineNumberTableEntry { @@ -625,9 +267,11 @@ pub struct LineNumberTableEntry { impl Display for LineNumberTableAttribute { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "table_length={}, entries={}", - self.line_number_table_length, - self.line_number_table.len() + write!( + f, + "table_length={}, entries={}", + self.line_number_table_length, + self.line_number_table.len() ) } -} \ No newline at end of file +} diff --git a/crates/core/src/class_file/class_file.rs b/crates/core/src/class_file/class_file.rs index 833951b..d8ae096 100644 --- a/crates/core/src/class_file/class_file.rs +++ b/crates/core/src/class_file/class_file.rs @@ -1,5 +1,6 @@ -use crate::attributes::{Attribute, AttributeInfo, CodeAttribute, Ops}; +use crate::attributes::{Attribute, AttributeInfo, CodeAttribute}; use crate::class_file::constant_pool::{ConstantPoolError, ConstantPoolExt, ConstantPoolOwned}; +use crate::instructions::Ops; use crate::{BaseType, FieldType, MethodDescriptor, Value}; use deku::ctx::Endian::Big; use deku::{DekuContainerRead, DekuError}; diff --git a/crates/core/src/class_file/constant_pool.rs b/crates/core/src/class_file/constant_pool.rs index 3c1425c..c1643ba 100644 --- a/crates/core/src/class_file/constant_pool.rs +++ b/crates/core/src/class_file/constant_pool.rs @@ -67,8 +67,7 @@ pub trait ConstantPoolExt: ConstantPoolGet { fn resolve_field(&self, index: u16) -> Result { let fr = self.get_field_ref(index)?; - let class = self.get_class_info(fr.class_index)?; - let class = self.get_string(class.name_index)?; + let class = self.resolve_class_name(fr.class_index)?; let name_and_type = self.get_name_and_type_info(fr.name_and_type_index)?; let name = self.get_string(name_and_type.name_index)?; let desc = self.get_string(name_and_type.descriptor_index)?; @@ -76,10 +75,24 @@ pub trait ConstantPoolExt: ConstantPoolGet { Ok(FieldRef { class, name, desc }) } + /// Resolves a class name from a constant pool class info entry + /// + /// # Arguments + /// * `index` - Index into constant pool that must point to a CONSTANT_Class_info structure + /// + /// # Returns + /// * Binary class name in internal JVM format (e.g. "java/lang/Object") + /// + /// # Errors + /// * Returns ConstantPoolError if index is invalid or points to wrong type + fn resolve_class_name(&self, index: u16) -> Result { + let class_info = self.get_class_info(index)?; + self.get_string(class_info.name_index) + } + fn resolve_method_ref(&self, index: u16) -> Result { let mr = self.get_method_ref(index)?; - let class = self.get_class_info(mr.class_index)?; - let class = self.get_string(class.name_index)?; + let class = self.resolve_class_name(mr.class_index)?; let name_and_type = self.get_name_and_type_info(mr.name_and_type_index)?; let name = self.get_string(name_and_type.name_index)?; let desc = self.get_string(name_and_type.descriptor_index)?; diff --git a/crates/core/src/class_loader.rs b/crates/core/src/class_loader.rs index 2f531f2..a2aa30c 100644 --- a/crates/core/src/class_loader.rs +++ b/crates/core/src/class_loader.rs @@ -10,6 +10,7 @@ use crate::native_libraries::NativeLibraries; use crate::{FieldType, MethodDescriptor}; use dashmap::DashMap; use deku::DekuContainerRead; +use itertools::Itertools; use libloading::os::windows::Library; use log::warn; use std::collections::hash_map::{Entry, Iter}; @@ -149,14 +150,10 @@ impl ClassLoader { fn load_class(&mut self, what: &str) -> Result, String> { let (module, class_fqn) = ("", what); - let bytes = self.bimage.get_class(module, class_fqn).unwrap_or_else(|| { - let path = format!("./data/{what}.class"); - log::info!("Loading class from path: {}", path); - let mut class_file = File::open(path).unwrap(); - let mut bytes = Vec::new(); - class_file.read_to_end(&mut bytes).unwrap(); - bytes - }); + let bytes = self + .bimage + .get_class(module, class_fqn) + .unwrap_or_else(|| Self::load_class_from_disk(what)); let (_, cf) = ClassFile::from_bytes((bytes.as_ref(), 0)) .map_err(|e| format!("failed to parse class file: {}", e))?; let runtime = self.runtime_class(cf); @@ -168,6 +165,20 @@ impl ClassLoader { Ok(arced) } + fn load_class_from_disk(what: &str) -> Vec { + let class_path = std::env::args() + .nth(1) + .unwrap_or("./data".to_string()) + .replace("\\", "/"); + + let path = format!("{class_path}/{what}.class"); + log::info!("Loading class from path: {}", path); + let mut class_file = File::open(path).unwrap(); + let mut bytes = Vec::new(); + class_file.read_to_end(&mut bytes).unwrap(); + bytes + } + fn runtime_class(&mut self, class_file: ClassFile) -> RuntimeClass { let constant_pool = class_file.constant_pool.clone(); let access_flags = ClassFlags::from(class_file.access_flags); diff --git a/crates/core/src/instructions.rs b/crates/core/src/instructions.rs new file mode 100644 index 0000000..ca58c94 --- /dev/null +++ b/crates/core/src/instructions.rs @@ -0,0 +1,386 @@ +use crate::attributes::ArrayType; +use deku_derive::DekuRead; + +//noinspection SpellCheckingInspection +#[allow(non_camel_case_types)] +#[derive(Clone, PartialEq, Debug, DekuRead)] +#[deku(id_type = "u8", ctx = "_endian: deku::ctx::Endian", endian = "big")] +pub enum Ops { + // Constants + #[deku(id = 0x00)] + nop, + #[deku(id = 0x01)] + aconst_null, + #[deku(id = 0x02)] + iconst_m1, + #[deku(id = 0x03)] + iconst_0, + #[deku(id = 0x04)] + iconst_1, + #[deku(id = 0x05)] + iconst_2, + #[deku(id = 0x06)] + iconst_3, + #[deku(id = 0x07)] + iconst_4, + #[deku(id = 0x08)] + iconst_5, + #[deku(id = 0x09)] + lconst_0, + #[deku(id = 0x0a)] + lconst_1, + #[deku(id = 0x0b)] + fconst_0, + #[deku(id = 0x0c)] + fconst_1, + #[deku(id = 0x0d)] + fconst_2, + #[deku(id = 0x0e)] + dconst_0, + #[deku(id = 0x0f)] + dconst_1, + #[deku(id = 0x10)] + bipush(u8), + #[deku(id = 0x11)] + sipush(u16), + #[deku(id = 0x12)] + ldc(u8), + #[deku(id = 0x13)] + ldc_w(u16), + #[deku(id = 0x14)] + ldc2_w(u16), + + // Loads + #[deku(id = 0x15)] + iload(u8), + #[deku(id = 0x16)] + lload(u8), + #[deku(id = 0x17)] + fload(u8), + #[deku(id = 0x18)] + dload(u8), + #[deku(id = 0x19)] + aload(u8), + #[deku(id = 0x1A)] + iload_0, + #[deku(id = 0x1B)] + iload_1, + #[deku(id = 0x1C)] + iload_2, + #[deku(id = 0x1D)] + iload_3, + #[deku(id = 0x1E)] + lload_0, + #[deku(id = 0x1F)] + lload_1, + #[deku(id = 0x20)] + lload_2, + #[deku(id = 0x21)] + lload_3, + #[deku(id = 0x22)] + fload_0, + #[deku(id = 0x23)] + fload_1, + #[deku(id = 0x24)] + fload_2, + #[deku(id = 0x25)] + fload_3, + #[deku(id = 0x26)] + dload_0, + #[deku(id = 0x27)] + dload_1, + #[deku(id = 0x28)] + dload_2, + #[deku(id = 0x29)] + dload_3, + #[deku(id = 0x2A)] + aload_0, + #[deku(id = 0x2B)] + aload_1, + #[deku(id = 0x2C)] + aload_2, + #[deku(id = 0x2D)] + aload_3, + #[deku(id = 0x2E)] + iaload, + #[deku(id = 0x2F)] + laload, + #[deku(id = 0x30)] + faload, + #[deku(id = 0x31)] + daload, + #[deku(id = 0x32)] + aaload, + #[deku(id = 0x33)] + baload, + #[deku(id = 0x34)] + caload, + #[deku(id = 0x35)] + saload, + + // Stores + #[deku(id = 0x36)] + istore(u8), + #[deku(id = 0x37)] + lstore(u8), + #[deku(id = 0x38)] + fstore(u8), + #[deku(id = 0x39)] + dstore(u8), + #[deku(id = 0x3A)] + astore(u8), + #[deku(id = 0x3B)] + istore_0, + #[deku(id = 0x3C)] + istore_1, + #[deku(id = 0x3D)] + istore_2, + #[deku(id = 0x3E)] + istore_3, + #[deku(id = 0x3F)] + lstore_0, + #[deku(id = 0x40)] + lstore_1, + #[deku(id = 0x41)] + lstore_2, + #[deku(id = 0x42)] + lstore_3, + #[deku(id = 0x43)] + fstore_0, + #[deku(id = 0x44)] + fstore_1, + #[deku(id = 0x45)] + fstore_2, + #[deku(id = 0x46)] + fstore_3, + #[deku(id = 0x47)] + dstore_0, + #[deku(id = 0x48)] + dstore_1, + #[deku(id = 0x49)] + dstore_2, + #[deku(id = 0x4A)] + dstore_3, + #[deku(id = 0x4B)] + astore_0, + #[deku(id = 0x4C)] + astore_1, + #[deku(id = 0x4D)] + astore_2, + #[deku(id = 0x4E)] + astore_3, + #[deku(id = 0x4F)] + iastore, + #[deku(id = 0x50)] + lastore, + #[deku(id = 0x51)] + fastore, + #[deku(id = 0x52)] + dastore, + #[deku(id = 0x53)] + aastore, + #[deku(id = 0x54)] + bastore, + #[deku(id = 0x55)] + castore, + #[deku(id = 0x56)] + sastore, + + //stack + #[deku(id = 0x57)] + pop, + #[deku(id = 0x58)] + pop2, + #[deku(id = 0x59)] + dup, + #[deku(id = 0x5a)] + dup_x1, + #[deku(id = 0x5b)] + dup_x2, + #[deku(id = 0x5c)] + dup2, + #[deku(id = 0x5d)] + dup2_x1, + #[deku(id = 0x5e)] + dup2_x2, + #[deku(id = 0x5f)] + swap, + + //math + #[deku(id = 0x60)] + iadd, + #[deku(id = 0x61)] + ladd, + #[deku(id = 0x62)] + fadd, + #[deku(id = 0x63)] + dadd, + #[deku(id = 0x64)] + isub, + #[deku(id = 0x65)] + lsub, + #[deku(id = 0x66)] + fsub, + #[deku(id = 0x67)] + dsub, + #[deku(id = 0x68)] + imul, + #[deku(id = 0x69)] + lmul, + #[deku(id = 0x6a)] + fmul, + #[deku(id = 0x6b)] + dmul, + #[deku(id = 0x6c)] + idiv, + #[deku(id = 0x6d)] + ldiv, + #[deku(id = 0x6e)] + fdiv, + #[deku(id = 0x6f)] + ddiv, + #[deku(id = 0x70)] + irem, + #[deku(id = 0x71)] + lrem, + #[deku(id = 0x72)] + frem, + #[deku(id = 0x73)] + drem, + #[deku(id = 0x74)] + ineg, + #[deku(id = 0x75)] + lneg, + #[deku(id = 0x76)] + fneg, + #[deku(id = 0x77)] + dneg, + #[deku(id = 0x78)] + ishl, + #[deku(id = 0x79)] + lshl, + #[deku(id = 0x7a)] + ishr, + #[deku(id = 0x7b)] + lshr, + #[deku(id = 0x7c)] + iushr, + #[deku(id = 0x7d)] + lushr, + #[deku(id = 0x7e)] + iand, + #[deku(id = 0x7f)] + land, + #[deku(id = 0x80)] + ior, + #[deku(id = 0x81)] + lor, + #[deku(id = 0x82)] + ixor, + #[deku(id = 0x83)] + lxor, + #[deku(id = 0x84)] + iinc(u8, i8), + + //conversions + #[deku(id = 0x85)] + i2l, + #[deku(id = 0x86)] + i2f, + #[deku(id = 0x87)] + i2d, + #[deku(id = 0x88)] + l2i, + #[deku(id = 0x89)] + l2f, + #[deku(id = 0x8a)] + l2d, + #[deku(id = 0x8b)] + f2i, + #[deku(id = 0x8c)] + f2l, + #[deku(id = 0x8d)] + f2d, + #[deku(id = 0x8e)] + d2i, + #[deku(id = 0x8f)] + d2l, + #[deku(id = 0x90)] + d2f, + #[deku(id = 0x91)] + i2b, + #[deku(id = 0x92)] + i2c, + #[deku(id = 0x93)] + i2s, + + // comparisons + + // control + #[deku(id = 0xa7)] + goto(u16), + + // discontinued + #[deku(id = 0xa8)] + jsr(u16), + #[deku(id = 0xa9)] + ret(u8), + // + #[deku(id = 0xaa)] + tableswitch, + #[deku(id = 0xab)] + lookupswitch, + #[deku(id = 0xac)] + ireturn, + #[deku(id = 0xad)] + lreturn, + #[deku(id = 0xae)] + freturn, + #[deku(id = 0xaf)] + dreturn, + #[deku(id = 0xb0)] + areturn, + // return + #[deku(id = 0xb1)] + return_void, + + // references + #[deku(id = 0xB2)] + getstatic(u16), + #[deku(id = 0xB3)] + putstatic(u16), + #[deku(id = 0xB4)] + getfield(u16), + #[deku(id = 0xB5)] + putfield(u16), + #[deku(id = 0xB6)] + invokevirtual(u16), + #[deku(id = 0xB7)] + invokespecial(u16), + #[deku(id = 0xB8)] + invokestatic(u16), + // 4th byte always zero/0 + #[deku(id = 0xB9)] + invokeinterface(u16, u8, u8), + // 3rd, 4th must be zero/0 + #[deku(id = 0xBA)] + invokedynamic(u16, u16), + #[deku(id = 0xBB)] + new(u16), + #[deku(id = 0xBC)] + newarray(ArrayType), + #[deku(id = 0xBD)] + anewarray(u16), + #[deku(id = 0xBE)] + arraylength, + #[deku(id = 0xBF)] + athrow, + #[deku(id = 0xC0)] + checkcast(u16), + #[deku(id = 0xC1)] + instanceof(u16), + #[deku(id = 0xC2)] + monitorenter, + #[deku(id = 0xC3)] + monitorexit, +} diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index eeda891..ed16e8a 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -15,20 +15,21 @@ //! - [`MethodDescriptor`] - Method signature information //! - [`FieldType`] - Field type information -use crate::attributes::{Attribute, CodeAttribute, Ops}; +use crate::attributes::{Attribute, CodeAttribute}; use crate::class_file::constant_pool::ConstantPoolExt; use crate::class_file::constant_pool::{ConstantPoolError, ConstantPoolGet}; use crate::class_file::{Bytecode, ClassFile, ConstantPoolEntry, MethodData}; use crate::object::Object; use crate::thread::VmThread; -use crate::Value::Reference; use deku::{DekuContainerRead, DekuError}; use deku_derive::{DekuRead, DekuWrite}; use env_logger::Builder; -use log::{warn, LevelFilter}; +use instructions::Ops; +use log::{trace, warn, LevelFilter}; use std::fmt::{Debug, Display, Formatter}; use std::fs::File; use std::io::Read; +use std::ops::Deref; use std::sync::{Arc, Mutex}; use vm::Vm; @@ -37,10 +38,12 @@ mod bimage; mod class; mod class_file; mod class_loader; +mod instructions; mod jni; mod macros; mod native_libraries; mod object; +mod object_manager; mod rng; mod thread; mod vm; @@ -55,6 +58,7 @@ pub fn run() { .filter_module("deku", LevelFilter::Warn) .filter_module("jvm_rs_core::class_file::class_file", LevelFilter::Info) .filter_module("jvm_rs_core::attributes", LevelFilter::Info) + .filter_module("jvm_rs_core::instructions", LevelFilter::Info) .init(); // let mut cl = ClassLoader::new().unwrap(); // cl.load_class("org.example.App").expect("TODO: panic message"); @@ -118,7 +122,7 @@ enum Value { /// Boolean value (true/false) Boolean(bool), /// Unicode character - Char(char), + Char(u16), /// 32-bit floating point Float(f32), /// 64-bit floating point @@ -135,6 +139,23 @@ enum Value { Reference(Option), } +impl Display for Value { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Value::Boolean(b) => write!(f, "bool({})", b), + Value::Char(c) => write!(f, "char({})", char::from_u32(*c as u32).unwrap_or('?')), + Value::Float(fl) => write!(f, "float({})", fl), + Value::Double(d) => write!(f, "double({})", d), + Value::Byte(b) => write!(f, "byte({})", b), + Value::Short(s) => write!(f, "short({})", s), + Value::Int(i) => write!(f, "int({})", i), + Value::Long(l) => write!(f, "long({})", l), + Value::Reference(Some(obj)) => write!(f, "Ref({})", obj.lock().unwrap().id), + Value::Reference(None) => write!(f, "null"), + } + } +} + /// Represents a JVM stack frame for method execution. /// /// A frame contains all the execution state needed to run a single method: @@ -210,12 +231,21 @@ impl Frame { Ok(ExecutionResult::ReturnValue(val)) => return Ok(Some(val)), Ok(_) => { println!( - "State:\n\tStack: {:?}\n\tLocals :{:?}\n", - self.stack, self.vars + "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(_) => { - panic!("Mission failed, we'll get em next time") + Err(x) => { + panic!("Mission failed, we'll get em next time:\n{x}") } } } @@ -305,6 +335,25 @@ impl MethodDescriptor { fn psvm() -> Self { 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, + } + }) + } } impl Display for BaseType { @@ -405,6 +454,51 @@ impl From for VmError { impl Frame { fn execute_instruction(&mut self, op: &Ops) -> Result { match op { + // Constants + Ops::aconst_null => { + self.stack.push(NULL); + Ok(ExecutionResult::Continue) + } + + Ops::iconst_m1 => { + self.stack.push(Value::Int(-1)); + Ok(ExecutionResult::Continue) + } + + Ops::iconst_0 => { + self.stack.push(Value::Int(0)); + Ok(ExecutionResult::Continue) + } + + Ops::iconst_1 => { + self.stack.push(Value::Int(1)); + Ok(ExecutionResult::Continue) + } + + Ops::iconst_2 => { + self.stack.push(Value::Int(2)); + Ok(ExecutionResult::Continue) + } + + Ops::iconst_3 => { + self.stack.push(Value::Int(3)); + Ok(ExecutionResult::Continue) + } + + Ops::iconst_4 => { + self.stack.push(Value::Int(4)); + Ok(ExecutionResult::Continue) + } + + Ops::iconst_5 => { + self.stack.push(Value::Int(5)); + Ok(ExecutionResult::Continue) + } + + Ops::bipush(byte) => { + self.stack.push(Value::Int(*byte as i32)); + Ok(ExecutionResult::Continue) + } Ops::ldc(index) => { let thing = self.pool.get_constant(index.to_owned() as u16)?; println!("\tLoading constant: {}", thing); @@ -457,42 +551,44 @@ impl Frame { }; if let Some(x) = resolved { self.stack.push(x); + self.stack.push(Value::Reference(None)); }; Ok(ExecutionResult::Continue) } - // store - 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) - } - // load + // 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) } @@ -523,19 +619,82 @@ impl Frame { 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::f2d => { - if let Value::Float(float) = self.stack.pop().expect("Stack must have value") { - let double: f64 = float.into(); - self.stack.push(Value::Double(double)); + // store + 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) + } + + //Stack + Ops::dup => { + if let Some(value) = self.stack.last() { + self.stack.push(value.clone()); Ok(ExecutionResult::Continue) } else { - Err(VmError::StackError( - "Popped value was not float".to_string(), - )) + Err(VmError::StackError("Stack underflow".to_string())) } } + // Math Ops::dadd => { let value1 = self.stack.pop().expect("Stack must have value"); let value2 = self.stack.pop().expect("Stack must have value"); @@ -551,6 +710,23 @@ impl Frame { } } + //Conversions + Ops::f2d => { + if let Value::Float(float) = self.stack.pop().expect("Stack must have value") { + let double: f64 = float.into(); + self.stack.push(Value::Double(double)); + Ok(ExecutionResult::Continue) + } else { + Err(VmError::StackError( + "Popped value was not float".to_string(), + )) + } + } + // Control + Ops::return_void => Ok(ExecutionResult::Return(())), + + // References + // get static field // can init the field Ops::getstatic(index) => { @@ -571,44 +747,82 @@ impl Frame { .clone() .expect("Static field was not initialised"); self.stack.push(constant.into()); - - // let (code, pool) = { - // let mut loader = self.vm.loader.lock().unwrap(); - // let class = loader.get_or_load(&field.class).unwrap(); - // let field = class.get_static_field_value(&field) - // // let code = class.get_code(meth)?; - // (code, pool) - // }; - // println!("{:?}", field); Ok(ExecutionResult::Continue) } + Ops::putstatic(index) => { + let field_ref = self.pool.resolve_field(*index)?; + trace!("Getting static field {field_ref:?}"); + + let init_class = self + .thread + .get_or_resolve_class(&field_ref.class, self.thread.clone()) + .expect("TO hecken work"); + let 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) => { + todo!("op getfield: index - {}", index) + } + + Ops::putfield(index) => { + let field_ref = self.pool.resolve_field(*index)?; + trace!("Setting field {field_ref:?}"); + let init_class = self + .thread + .get_class(&field_ref.class) + .expect("pre initialised class"); + // let static_field = init_class + // .find_field(&field_ref.name, field_ref.desc) + // .expect("TO hecken work"); + let value = self.stack.pop().expect("value on stack"); + if let Value::Reference(reference) = self.stack.pop().expect("object on stack") { + if let Some(object) = reference { + object.lock().unwrap().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(), + )) + } + + // todo!("op putfield: index - {}", index) + } Ops::invokevirtual(index) => { - let meth = self.pool.resolve_method_ref(*index)?; - let params = meth.desc.num_arguments(); - let last = self.stack.len() - 1; - let first = last - params + 1; - let slice = self.stack.get(first..last).unwrap().to_vec(); - //sub slice param length + one, throw it to frame new - let (code, pool) = { - let mut loader = self.thread.loader.lock().unwrap(); - let class = loader.get_or_load(&meth.class).unwrap(); - let pool = class.constant_pool.clone(); - let code = class - .find_method(&meth.name, &meth.desc) - .unwrap() - .code - .clone() - .unwrap(); - (code, pool) - }; - // let code = class.get_code(meth)?; - // let class = self.vm.loader.get_or_load(&meth.class).unwrap(); - // let pool = &class.constant_pool; - let vars = slice; - let frame = Frame::new(code, pool.clone(), vars, self.thread.clone()); - // println!("{:?}", meth); - // todo!("Finish invoke virtual"); + let method_ref = self.pool.resolve_method_ref(*index)?; + let args_count = method_ref.desc.arg_width(); + let args = self.stack.split_off(self.stack.len() - args_count); + let result = self.thread.invoke(method_ref, args, self.thread.clone())?; + if let Some(val) = result { + self.stack.push(val) + } + todo!("Finish invoke virtual"); + Ok(ExecutionResult::Continue) + } + + Ops::invokespecial(index) => { + let method_ref = self.pool.resolve_method_ref(*index)?; + let class = self + .thread + .get_or_resolve_class(&method_ref.class, self.thread.clone())?; + + // the 1 represents the receiver + let args_count = method_ref.desc.arg_width() + 1; + let args = self.stack.split_off(self.stack.len() - args_count); + + let result = self.thread.invoke(method_ref, args, self.thread.clone())?; + if let Some(val) = result { + self.stack.push(val) + } + // todo!("invoke special"); Ok(ExecutionResult::Continue) } @@ -617,40 +831,31 @@ impl Frame { let class = self .thread .get_or_resolve_class(&method_ref.class, self.thread.clone())?; - // let method_data = class - // .find_method(&method_ref.name, method_ref.desc)? - // .clone(); - let result = self.thread.invoke(method_ref, self.thread.clone())?; + let args_count = method_ref.desc.parameters.len(); + let args = self.stack.split_off(self.stack.len() - args_count); + + let result = self.thread.invoke(method_ref, args, self.thread.clone())?; if let Some(val) = result { self.stack.push(val) } - // todo!("Implement invoke static {}", index) + warn!("invoke static not final {}", index); Ok(ExecutionResult::Continue) } - Ops::aconst_null => { - self.stack.push(NULL); - Ok(ExecutionResult::Continue) - } - - Ops::putstatic(index) => { - let field_ref = self.pool.resolve_field(*index)?; - println!("Getting static field {field_ref:?}"); + // can init class + Ops::new(index) => { + let class = self.pool.resolve_class_name(*index)?; let init_class = self .thread - .get_or_resolve_class(&field_ref.class, self.thread.clone()) + .get_or_resolve_class(&class, self.thread.clone()) .expect("TO hecken work"); - let result = init_class - .find_field(&field_ref.name, field_ref.desc) - .expect("TO hecken work"); - let value = self.stack.pop().expect("stack to have value"); - *result.value.lock().unwrap() = Some(value); + let object = self.thread.gc.write().unwrap().new(init_class); + self.stack.push(Value::Reference(Some(object))); Ok(ExecutionResult::Continue) + // todo!("'New' instruction") } - - Ops::return_void => Ok(ExecutionResult::Return(())), _ => { todo!("Unimplemented op: {:?}", op) } diff --git a/crates/core/src/macros.rs b/crates/core/src/macros.rs index 9fbd812..9d9d150 100644 --- a/crates/core/src/macros.rs +++ b/crates/core/src/macros.rs @@ -3,54 +3,71 @@ use crate::Value; #[macro_export] macro_rules! store { - ($self:expr, l, $index:expr) => { - {{ - let index: usize = $index; - let value = $self.stack.pop().expect("Must contain value on stack"); - println!("\tStoring: {value:?} into local[{index}]"); - $self.vars[index] = value; - $self.vars[index + 1] = Value::Reference(None); - Ok(ExecutionResult::Continue) - }} - }; - ($self:expr, d, $index:expr) => {store!($self, l, $index)}; - ($self:expr, i, $index:expr) => { - {{ - let index: usize = $index; - let value = $self.stack.pop().expect("Must contain value on stack"); - println!("\tStoring: {value:?} into local[{index}]"); - $self.vars[index] = value; - Ok(ExecutionResult::Continue) - }} - }; - ($self:expr, f, $index:expr) => {store!($self, i, $index)}; + ($self:expr, l, $index:expr) => {{ + { + let index: usize = $index; + let value = $self.stack.pop().expect("Must contain value on stack"); + println!("\tStoring: {value:?} into local[{index}]"); + $self.vars[index] = value; + $self.vars[index + 1] = Value::Reference(None); + Ok(ExecutionResult::Continue) + } + }}; + ($self:expr, d, $index:expr) => { + store!($self, l, $index) + }; + ($self:expr, i, $index:expr) => {{ + { + let index: usize = $index; + let value = $self.stack.pop().expect("Must contain value on stack"); + println!("\tStoring: {value:?} into local[{index}]"); + $self.vars[index] = value; + Ok(ExecutionResult::Continue) + } + }}; + ($self:expr, f, $index:expr) => { + store!($self, i, $index) + }; } #[macro_export] macro_rules! load { - ($self:expr, i, $index:expr) => { - {{ - let index: usize = $index; - let value = $self.vars.get(index).expect("Local var to exist"); - println!("\tLoading: local[{index}] - {value:?} onto stack"); - $self.stack.push(value.clone()); - Ok(ExecutionResult::Continue) - }} - }; - ($self:expr, d, $index:expr) => {load!($self, l, $index)}; - ($self:expr, l, $index:expr) => {load!($self, i, $index)}; - ($self:expr, f, $index:expr) => {load!($self, i, $index)}; + ($self:expr, i, $index:expr) => {{ + { + let index: usize = $index; + let value = $self.vars.get(index).expect("Local var to exist"); + println!("\tLoading: local[{index}] - {value} onto stack"); + $self.stack.push(value.clone()); + Ok(ExecutionResult::Continue) + } + }}; + ($self:expr, d, $index:expr) => { + load!($self, l, $index) + }; + ($self:expr, l, $index:expr) => { + load!($self, i, $index) + }; + ($self:expr, f, $index:expr) => { + load!($self, i, $index) + }; + ($self:expr, a, $index:expr) => { + load!($self, i, $index) + }; } #[macro_export] macro_rules! pool_get_impl { - ($fn_name:ident => $result:ty, $variant:ident) => { - fn $fn_name(&self, index: u16) -> Result<&$result, ConstantPoolError> { - let cp_entry = self.get_constant(index)?; - match cp_entry { - ConstantPoolEntry::$variant(value) => Ok(value), - _ => Err(ConstantPoolError(format!("Expected {} constant at index {}", stringify!($variant), index))), - } - } - }; + ($fn_name:ident => $result:ty, $variant:ident) => { + fn $fn_name(&self, index: u16) -> Result<&$result, ConstantPoolError> { + let cp_entry = self.get_constant(index)?; + match cp_entry { + ConstantPoolEntry::$variant(value) => Ok(value), + _ => Err(ConstantPoolError(format!( + "Expected {} constant at index {}", + stringify!($variant), + index + ))), + } + } + }; } diff --git a/crates/core/src/object.rs b/crates/core/src/object.rs index 92ece6e..1ae86b5 100644 --- a/crates/core/src/object.rs +++ b/crates/core/src/object.rs @@ -1,13 +1,31 @@ -use std::cell::RefCell; -use std::sync::{Arc, Mutex}; +use crate::class::RuntimeClass; use crate::class_file::ClassFile; use crate::Value; +use dashmap::DashMap; +use log::trace; +use std::cell::RefCell; +use std::fmt::Display; +use std::sync::{Arc, Mutex}; #[derive(Debug, Clone)] pub struct Object { - pub id: String, - pub class: Arc, - fields: Arc>>, + pub id: u32, + pub class: Arc, + pub fields: DashMap, +} + +impl Object { + pub fn set_field(&self, field_name: &str, value: Value) { + trace!("Fields for object:\n\t{:?}", self.fields); + trace!("Setting '{}' to '{}'", field_name, value); + self.fields.insert(field_name.to_string(), value); + } +} + +impl Display for Object { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Object[id={}, class={}]", self.id, self.class.this_class) + } } impl PartialEq for Object { @@ -16,4 +34,4 @@ impl PartialEq for Object { } } -impl Eq for Object {} \ No newline at end of file +impl Eq for Object {} diff --git a/crates/core/src/object_manager.rs b/crates/core/src/object_manager.rs new file mode 100644 index 0000000..49ba59b --- /dev/null +++ b/crates/core/src/object_manager.rs @@ -0,0 +1,35 @@ +use crate::class::RuntimeClass; +use crate::object::Object; +use crate::rng::generate_identity_hash; +use crate::ObjectRef; +use std::collections::HashMap; +use std::sync::{Arc, Mutex}; + +#[derive(Default)] +pub struct ObjectManager { + objects: HashMap, +} + +impl ObjectManager { + pub fn new(&mut self, class: Arc) -> ObjectRef { + let id = generate_identity_hash(); + assert!( + !self.objects.contains_key(&id), + "Generated ID already exists!" + ); + let object = Arc::new(Mutex::new(Object { + id, + class: class.clone(), + fields: Default::default(), + })); + self.objects.insert(id, object.clone()); + object + } + + pub fn get(&self, id: u32) -> ObjectRef { + self.objects + .get(&id) + .expect("Object must be present") + .clone() + } +} diff --git a/crates/core/src/thread.rs b/crates/core/src/thread.rs index 73856e3..6fb8e67 100644 --- a/crates/core/src/thread.rs +++ b/crates/core/src/thread.rs @@ -2,17 +2,19 @@ use crate::class::RuntimeClass; use crate::class_file::{ClassFile, MethodData, MethodRef}; use crate::class_loader::{ClassLoader, LoaderRef}; use crate::jni::create_jni_function_table; +use crate::object_manager::ObjectManager; use crate::vm::Vm; use crate::{BaseType, FieldType, Frame, MethodDescriptor, Value, VmError}; use deku::DekuError::Incomplete; -use jni::sys::jlong; +use jni::sys::{jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jobject, jshort}; use jni::JNIEnv; use libffi::low::call; use libffi::middle::*; use log::{trace, warn}; +use std::any::Any; use std::ops::Add; use std::ptr::null_mut; -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, Mutex, RwLock}; use std::vec::IntoIter; type MethodCallResult = Result, VmError>; @@ -22,15 +24,17 @@ pub struct VmThread { pub vm: Arc, pub loader: Arc>, pub frame_stack: Vec, + pub gc: Arc>, } impl VmThread { pub fn new(vm: Arc, loader: Option) -> Self { let loader = loader.unwrap_or(vm.loader.clone()); Self { - vm, + vm: vm.clone(), loader, frame_stack: Vec::new(), + gc: vm.gc.clone(), } } @@ -65,6 +69,14 @@ impl VmThread { Ok(runtime_class) } + pub fn get_class(&self, what: &str) -> Result, VmError> { + self.loader + .lock() + .unwrap() + .get_or_load(what) + .map_err(VmError::LoaderError) + } + /// Initialize a class following JVM Spec 5.5. /// Handles recursive initialization by tracking which thread is initializing. fn init(&self, class: Arc, thread: Arc) -> Result<(), VmError> { @@ -157,7 +169,8 @@ impl VmThread { desc: MethodDescriptor::psvm(), }; - self.invoke(method_ref, thread).expect("Main method died"); + self.invoke(method_ref, Vec::new(), thread) + .expect("Main method died"); return (); let class = self.get_or_resolve_class(what, thread.clone()).unwrap(); @@ -177,31 +190,38 @@ impl VmThread { } } - pub fn invoke(&self, method_reference: MethodRef, thread: Arc) -> MethodCallResult { + pub fn invoke( + &self, + method_reference: MethodRef, + args: Vec, + thread: Arc, + ) -> MethodCallResult { let class = self.get_or_resolve_class(&method_reference.class, thread.clone())?; let resolved_method = class .find_method(&method_reference.name, &method_reference.desc) .unwrap(); - println!("invoking {}: {}", method_reference.name, class.this_class); + trace!( + "invoking '{}' from {}", + method_reference.name, + class.this_class + ); if resolved_method.flags.ACC_NATIVE { - unsafe { - return self.invoke_native(&method_reference); - } + return self.invoke_native(&method_reference, args); } let mut frame = Frame::new( resolved_method.code.clone().unwrap(), class.constant_pool.clone(), - vec![], + args, thread.clone(), ); frame.execute() } - pub fn invoke_native(&self, method: &MethodRef) -> MethodCallResult { + pub fn invoke_native(&self, method: &MethodRef, args: Vec) -> MethodCallResult { let symbol_name = generate_jni_method_name(method); - println!("{:?}", &symbol_name); + trace!("searching for native symbol: {:?}", &symbol_name); - unsafe { + let result = unsafe { // manually load relevant library for poc let lib = libloading::os::windows::Library::new( "C:\\Program Files\\Java\\jdk-25\\bin\\jvm_rs.dll", @@ -216,21 +236,90 @@ impl VmThread { ); // build actual JNI interface that forms the table of // native functions that can be used to manipulate the JVM - let JNIEnv = create_jni_function_table(); + let jnienv = create_jni_function_table(); + + // let args = build_args(args); // coerce my method descriptors into libffi C equivalents, then call - let l = method - .build_cif() - .call::(cp, &*vec![arg(&JNIEnv), arg(&null_mut::<()>())]); + // let l = method.build_cif().call::(cp, args.as_ref()); - println!("{l}"); - } + let mut storage = Vec::new(); + trace!("passing {args:?} to native fn"); + let built_args = build_args(args, &mut storage); + let cif = method.build_cif(); - Ok(None) - // todo!("Invoke native") + match &method.desc.return_type { + None => { + cif.call::<()>(cp, built_args.as_ref()); + Ok(None) + } + Some(FieldType::Base(BaseType::Long)) => { + let v = cif.call::(cp, built_args.as_ref()); + Ok(Some(Value::Long(v))) + } + Some(FieldType::Base(BaseType::Int)) => { + let v = cif.call::(cp, built_args.as_ref()); + Ok(Some(Value::Int(v))) + } + Some(FieldType::Base(BaseType::Float)) => { + let v = cif.call::(cp, built_args.as_ref()); + Ok(Some(Value::Float(v))) + } + Some(FieldType::Base(BaseType::Double)) => { + let v = cif.call::(cp, built_args.as_ref()); + Ok(Some(Value::Double(v))) + } + Some(FieldType::Base(BaseType::Boolean)) => { + let v = cif.call::(cp, built_args.as_ref()); + Ok(Some(Value::Int(v as i32))) + } + Some(FieldType::Base(BaseType::Byte)) => { + let v = cif.call::(cp, built_args.as_ref()); + Ok(Some(Value::Byte(v))) + } + Some(FieldType::Base(BaseType::Char)) => { + let v = cif.call::(cp, built_args.as_ref()); + Ok(Some(Value::Char(v))) + } + Some(FieldType::Base(BaseType::Short)) => { + let v = cif.call::(cp, built_args.as_ref()); + Ok(Some(Value::Short(v))) + } + Some(FieldType::ClassType(_)) | Some(FieldType::ArrayType(_)) => { + let v = cif.call::(cp, built_args.as_ref()); + // TODO: Convert jobject to Reference properly + Ok(Some(Value::Reference(None))) + } + } + }; + warn!("Invoke native not final"); + result } } +fn build_args<'a>(params: Vec, storage: &'a mut Vec>) -> Vec> { + // Store values in the provided storage + storage.push(Box::new(create_jni_function_table()) as Box); + storage.push(Box::new(std::ptr::null_mut::<()>()) as Box); + + for value in params { + match value { + Value::Int(x) => storage.push(Box::new(x) as Box), + Value::Boolean(x) => storage.push(Box::new(x) as Box), + Value::Char(x) => storage.push(Box::new(x) as Box), + Value::Float(x) => storage.push(Box::new(x) as Box), + Value::Double(x) => storage.push(Box::new(x) as Box), + Value::Byte(x) => storage.push(Box::new(x) as Box), + Value::Short(x) => storage.push(Box::new(x) as Box), + Value::Long(x) => storage.push(Box::new(x) as Box), + Value::Reference(x) => storage.push(Box::new(x) as Box), + } + } + + // Create args referencing the storage + storage.iter().map(|boxed| arg(&**boxed)).collect() +} + pub fn generate_jni_method_name(method_ref: &MethodRef) -> String { let class_name = &method_ref.class.replace("/", "_"); let method_name = &method_ref.name; diff --git a/crates/core/src/vm.rs b/crates/core/src/vm.rs index e80cc72..e235dc2 100644 --- a/crates/core/src/vm.rs +++ b/crates/core/src/vm.rs @@ -1,10 +1,12 @@ use crate::class_file::ClassFile; use crate::class_loader::ClassLoader; +use crate::object::Object; +use crate::object_manager::ObjectManager; use crate::thread::VmThread; use crate::Frame; use libloading::os::windows::Symbol; use std::collections::HashMap; -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, Mutex, RwLock}; // struct AbstractObject<'a> {} pub struct Vm { @@ -12,6 +14,7 @@ pub struct Vm { pub thread: Mutex>>, pub loader: Arc>, pub native_methods: HashMap>, + pub gc: Arc>, } impl Vm { @@ -21,6 +24,7 @@ impl Vm { loader: Arc::new(Mutex::from(ClassLoader::default())), thread: Mutex::new(Vec::new()), native_methods: Default::default(), + gc: Default::default(), }); let thread = Arc::new(VmThread::new(vm.clone(), None)); vm.thread.lock().unwrap().push(thread.clone()); diff --git a/crates/jvm-rs-sys/src/lib.rs b/crates/jvm-rs-sys/src/lib.rs index 506d2de..c2b9fbb 100644 --- a/crates/jvm-rs-sys/src/lib.rs +++ b/crates/jvm-rs-sys/src/lib.rs @@ -95,3 +95,16 @@ pub extern "system" fn Java_org_example_Main_getTime<'local>( .unwrap() .as_millis() as jlong } + +#[unsafe(no_mangle)] +pub extern "system" fn Java_org_example_MockIO_println<'local>( + mut env: JNIEnv<'local>, + jclass: JClass<'local>, + input: jlong, +) { + // let input: String = env + // .get_string(&input) + // .expect("Couldn't get java string!") + // .into(); + println!("{input}") +}