Object initialisation

This commit is contained in:
james 2025-11-23 03:19:03 +10:30
parent 78b17345b8
commit 065bd9f3c7
No known key found for this signature in database
GPG Key ID: E1FFBA228F4CAD87
12 changed files with 1025 additions and 589 deletions

View File

@ -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<u8> 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<Attribute> {
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<LineNumberTableEntry>,
}
#[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()
)
}
}
}

View File

@ -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};

View File

@ -67,8 +67,7 @@ pub trait ConstantPoolExt: ConstantPoolGet {
fn resolve_field(&self, index: u16) -> Result<FieldRef, ConstantPoolError> {
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<String, ConstantPoolError> {
let class_info = self.get_class_info(index)?;
self.get_string(class_info.name_index)
}
fn resolve_method_ref(&self, index: u16) -> Result<MethodRef, ConstantPoolError> {
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)?;

View File

@ -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<Arc<RuntimeClass>, String> {
let (module, class_fqn) = ("", what);
let bytes = self.bimage.get_class(module, class_fqn).unwrap_or_else(|| {
let path = format!("./data/{what}.class");
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<u8> {
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);

View File

@ -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,
}

View File

@ -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<ObjectRef>),
}
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::<Vec<_>>()
.join(", "),
self.vars
.iter()
.map(|v| v.to_string())
.collect::<Vec<_>>()
.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<DekuError> for VmError {
impl Frame {
fn execute_instruction(&mut self, op: &Ops) -> Result<ExecutionResult, VmError> {
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)
}

View File

@ -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
))),
}
}
};
}

View File

@ -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<ClassFile>,
fields: Arc<Mutex<Vec<Value>>>,
pub id: u32,
pub class: Arc<RuntimeClass>,
pub fields: DashMap<String, Value>,
}
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 {}
impl Eq for Object {}

View File

@ -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<u32, ObjectRef>,
}
impl ObjectManager {
pub fn new(&mut self, class: Arc<RuntimeClass>) -> ObjectRef {
let id = generate_identity_hash();
assert!(
!self.objects.contains_key(&id),
"Generated ID already exists!"
);
let object = Arc::new(Mutex::new(Object {
id,
class: class.clone(),
fields: Default::default(),
}));
self.objects.insert(id, object.clone());
object
}
pub fn get(&self, id: u32) -> ObjectRef {
self.objects
.get(&id)
.expect("Object must be present")
.clone()
}
}

View File

@ -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<Option<Value>, VmError>;
@ -22,15 +24,17 @@ pub struct VmThread {
pub vm: Arc<Vm>,
pub loader: Arc<Mutex<ClassLoader>>,
pub frame_stack: Vec<Frame>,
pub gc: Arc<RwLock<ObjectManager>>,
}
impl VmThread {
pub fn new(vm: Arc<Vm>, loader: Option<LoaderRef>) -> 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<Arc<RuntimeClass>, 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<RuntimeClass>, thread: Arc<VmThread>) -> 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<VmThread>) -> MethodCallResult {
pub fn invoke(
&self,
method_reference: MethodRef,
args: Vec<Value>,
thread: Arc<VmThread>,
) -> MethodCallResult {
let class = self.get_or_resolve_class(&method_reference.class, thread.clone())?;
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<Value>) -> 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::<jlong>(cp, &*vec![arg(&JNIEnv), arg(&null_mut::<()>())]);
// let l = method.build_cif().call::<jlong>(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::<jlong>(cp, built_args.as_ref());
Ok(Some(Value::Long(v)))
}
Some(FieldType::Base(BaseType::Int)) => {
let v = cif.call::<jint>(cp, built_args.as_ref());
Ok(Some(Value::Int(v)))
}
Some(FieldType::Base(BaseType::Float)) => {
let v = cif.call::<jfloat>(cp, built_args.as_ref());
Ok(Some(Value::Float(v)))
}
Some(FieldType::Base(BaseType::Double)) => {
let v = cif.call::<jdouble>(cp, built_args.as_ref());
Ok(Some(Value::Double(v)))
}
Some(FieldType::Base(BaseType::Boolean)) => {
let v = cif.call::<jboolean>(cp, built_args.as_ref());
Ok(Some(Value::Int(v as i32)))
}
Some(FieldType::Base(BaseType::Byte)) => {
let v = cif.call::<jbyte>(cp, built_args.as_ref());
Ok(Some(Value::Byte(v)))
}
Some(FieldType::Base(BaseType::Char)) => {
let v = cif.call::<jchar>(cp, built_args.as_ref());
Ok(Some(Value::Char(v)))
}
Some(FieldType::Base(BaseType::Short)) => {
let v = cif.call::<jshort>(cp, built_args.as_ref());
Ok(Some(Value::Short(v)))
}
Some(FieldType::ClassType(_)) | Some(FieldType::ArrayType(_)) => {
let v = cif.call::<jobject>(cp, built_args.as_ref());
// TODO: Convert jobject to Reference properly
Ok(Some(Value::Reference(None)))
}
}
};
warn!("Invoke native not final");
result
}
}
fn build_args<'a>(params: Vec<Value>, storage: &'a mut Vec<Box<dyn Any>>) -> Vec<Arg<'a>> {
// Store values in the provided storage
storage.push(Box::new(create_jni_function_table()) as Box<dyn Any>);
storage.push(Box::new(std::ptr::null_mut::<()>()) as Box<dyn Any>);
for value in params {
match value {
Value::Int(x) => storage.push(Box::new(x) as Box<dyn Any>),
Value::Boolean(x) => storage.push(Box::new(x) as Box<dyn Any>),
Value::Char(x) => storage.push(Box::new(x) as Box<dyn Any>),
Value::Float(x) => storage.push(Box::new(x) as Box<dyn Any>),
Value::Double(x) => storage.push(Box::new(x) as Box<dyn Any>),
Value::Byte(x) => storage.push(Box::new(x) as Box<dyn Any>),
Value::Short(x) => storage.push(Box::new(x) as Box<dyn Any>),
Value::Long(x) => storage.push(Box::new(x) as Box<dyn Any>),
Value::Reference(x) => storage.push(Box::new(x) as Box<dyn Any>),
}
}
// 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;

View File

@ -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<Vec<Arc<VmThread>>>,
pub loader: Arc<Mutex<ClassLoader>>,
pub native_methods: HashMap<String, Symbol<()>>,
pub gc: Arc<RwLock<ObjectManager>>,
}
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());

View File

@ -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}")
}