check check

This commit is contained in:
james 2025-11-24 06:53:06 +10:30
parent 065bd9f3c7
commit d9368d2448
No known key found for this signature in database
GPG Key ID: E1FFBA228F4CAD87
9 changed files with 453 additions and 122 deletions

View File

@ -1,7 +1,8 @@
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 crate::value::Value;
use crate::{BaseType, FieldType, MethodDescriptor};
use deku::ctx::Endian::Big;
use deku::{DekuContainerRead, DekuError};
use deku_derive::{DekuRead, DekuWrite};
@ -660,10 +661,10 @@ pub enum Constant {
impl From<Constant> for Value {
fn from(value: Constant) -> Self {
match value {
Constant::Int(x) => Value::Int(x),
Constant::Long(x) => Value::Long(x),
Constant::Float(x) => Value::Float(x),
Constant::Double(x) => Value::Double(x),
Constant::Int(x) => x.into(),
Constant::Long(x) => x.into(),
Constant::Float(x) => x.into(),
Constant::Double(x) => x.into(),
Constant::String(x) => {
todo!("Constant string")
}

View File

@ -19,8 +19,9 @@ 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::objects::array::{ArrayReference, Reference};
use crate::thread::VmThread;
use ::jni::sys::{jbyte, jchar, jdouble, jfloat, jint, jlong, jshort};
use deku::{DekuContainerRead, DekuError};
use deku_derive::{DekuRead, DekuWrite};
use env_logger::Builder;
@ -31,6 +32,7 @@ use std::fs::File;
use std::io::Read;
use std::ops::Deref;
use std::sync::{Arc, Mutex};
use value::{Primitive, Value};
use vm::Vm;
mod attributes;
@ -42,19 +44,18 @@ mod instructions;
mod jni;
mod macros;
mod native_libraries;
mod object;
mod object_manager;
mod objects;
mod rng;
mod thread;
mod value;
mod vm;
const NULL: Value = Value::Reference(None);
// const NULL: Value = Value::Reference(None);
// include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
/// pseudo main
pub fn run() {
Builder::from_default_env()
.filter_level(LevelFilter::Trace)
.filter_level(LevelFilter::Info)
.filter_module("deku", LevelFilter::Warn)
.filter_module("jvm_rs_core::class_file::class_file", LevelFilter::Info)
.filter_module("jvm_rs_core::attributes", LevelFilter::Info)
@ -110,51 +111,39 @@ pub fn run() {
// vm.method(ops.clone(), code, var_table);
}
/// A reference-counted, thread-safe pointer to an Object.
type ObjectRef = Arc<Mutex<Object>>;
/// Represents a JVM runtime value.
///
/// This enum covers all primitive types and object references that can exist
/// on the operand stack or in local variables during bytecode execution.
#[derive(Debug, Clone)]
enum Value {
/// Boolean value (true/false)
Boolean(bool),
/// Unicode character
Char(u16),
/// 32-bit floating point
Float(f32),
/// 64-bit floating point
Double(f64),
/// Signed 8-bit integer
Byte(i8),
/// Signed 16-bit integer
Short(i16),
/// Signed 32-bit integer
Int(i32),
/// Signed 64-bit integer
Long(i64),
/// Reference to an object (or null)
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"),
}
}
}
// impl Value {
// fn Int(i: i32) -> Value {
// Value::Primitive(Primitive::Int(i))
// }
//
// fn Float(f: f32) -> Value {
// Value::Primitive(Primitive::Float(f))
// }
//
// fn Double(d: f64) -> Value {
// Value::Primitive(Primitive::Double(d))
// }
//
// fn Long(l: i64) -> Value {
// Value::Primitive(Primitive::Long(l))
// }
//
// fn Char(c: u16) -> Value {
// Value::Primitive(Primitive::Char(c))
// }
//
// fn Boolean(b: bool) -> Value {
// Value::Primitive(Primitive::Boolean(b))
// }
//
// fn Byte(b: i8) -> Value {
// Value::Primitive(Primitive::Byte(b))
// }
//
// fn Short(s: i16) -> Value {
// Value::Primitive(Primitive::Short(s))
// }
// }
/// Represents a JVM stack frame for method execution.
///
@ -224,13 +213,13 @@ impl Frame {
let binding = self.bytecode.code.clone();
let mut ops = binding.iter();
for op in ops {
println!("Executing Op: {:?}", op);
trace!("Executing Op: {:?}", op);
let result = self.execute_instruction(op);
match result {
Ok(ExecutionResult::Return(c)) => return Ok(None),
Ok(ExecutionResult::Return(())) => return Ok(None),
Ok(ExecutionResult::ReturnValue(val)) => return Ok(Some(val)),
Ok(_) => {
println!(
trace!(
"State:\n\tStack: [{}]\n\tLocals: [{}]\n",
self.stack
.iter()
@ -456,60 +445,60 @@ impl Frame {
match op {
// Constants
Ops::aconst_null => {
self.stack.push(NULL);
self.stack.push(Value::NULL);
Ok(ExecutionResult::Continue)
}
Ops::iconst_m1 => {
self.stack.push(Value::Int(-1));
self.stack.push(Value::Primitive(Primitive::Int(-1)));
Ok(ExecutionResult::Continue)
}
Ops::iconst_0 => {
self.stack.push(Value::Int(0));
self.stack.push(Value::Primitive(Primitive::Int(0)));
Ok(ExecutionResult::Continue)
}
Ops::iconst_1 => {
self.stack.push(Value::Int(1));
self.stack.push(Value::Primitive(Primitive::Int(1)));
Ok(ExecutionResult::Continue)
}
Ops::iconst_2 => {
self.stack.push(Value::Int(2));
self.stack.push(Value::Primitive(Primitive::Int(2)));
Ok(ExecutionResult::Continue)
}
Ops::iconst_3 => {
self.stack.push(Value::Int(3));
self.stack.push(Value::Primitive(Primitive::Int(3)));
Ok(ExecutionResult::Continue)
}
Ops::iconst_4 => {
self.stack.push(Value::Int(4));
self.stack.push(4.into());
Ok(ExecutionResult::Continue)
}
Ops::iconst_5 => {
self.stack.push(Value::Int(5));
self.stack.push(5.into());
Ok(ExecutionResult::Continue)
}
Ops::bipush(byte) => {
self.stack.push(Value::Int(*byte as i32));
self.stack.push((*byte as i32).into());
Ok(ExecutionResult::Continue)
}
Ops::ldc(index) => {
let thing = self.pool.get_constant(index.to_owned() as u16)?;
println!("\tLoading constant: {}", thing);
trace!("\tLoading constant: {}", thing);
let resolved: Option<Value> = match thing {
ConstantPoolEntry::Utf8(x) => {
println!("{:?}", String::from_utf8(x.bytes.clone()));
warn!("{:?}", String::from_utf8(x.bytes.clone()));
warn!("Utf8 loading not yet implemented");
None
}
ConstantPoolEntry::Integer(x) => Some(Value::Int(x.clone())),
ConstantPoolEntry::Float(x) => Some(Value::Float(x.clone())),
ConstantPoolEntry::Integer(x) => Some(Value::from(*x)),
ConstantPoolEntry::Float(x) => Some(Value::from(*x)),
ConstantPoolEntry::Class(x) => None,
ConstantPoolEntry::String(x) => {
warn!("String loading not yet implemented");
@ -543,10 +532,10 @@ impl Frame {
}
Ops::ldc2_w(index) => {
let val = self.pool.get_constant(*index)?;
println!("\tLoading constant: {}", val);
trace!("\tLoading constant: {}", val);
let resolved = match val {
ConstantPoolEntry::Double(x) => Some(Value::Double(x.clone())),
ConstantPoolEntry::Long(x) => Some(Value::Long(x.clone())),
ConstantPoolEntry::Double(x) => Some(Value::from(*x)),
ConstantPoolEntry::Long(x) => Some(Value::from(*x)),
_ => None,
};
if let Some(x) = resolved {
@ -636,6 +625,22 @@ impl Frame {
}
// store
Ops::istore(index) => {
store!(self, i, *index as usize)
}
Ops::istore_0 => {
store!(self, i, 0)
}
Ops::istore_1 => {
store!(self, i, 1)
}
Ops::istore_2 => {
store!(self, i, 2)
}
Ops::istore_3 => {
store!(self, i, 3)
}
Ops::fstore(index) => {
store!(self, f, *index as usize)
}
@ -651,6 +656,7 @@ impl Frame {
Ops::fstore_3 => {
store!(self, f, 3)
}
Ops::dstore(index) => {
store!(self, d, *index as usize)
}
@ -663,10 +669,10 @@ impl Frame {
Ops::dstore_2 => {
store!(self, d, 2)
}
Ops::dstore_3 => {
store!(self, d, 3)
}
Ops::lstore(index) => {
store!(self, l, *index as usize)
}
@ -679,11 +685,47 @@ impl Frame {
Ops::lstore_2 => {
store!(self, l, 2)
}
Ops::lstore_3 => {
store!(self, l, 3)
}
Ops::astore(index) => {
store!(self, a, *index as usize)
}
Ops::astore_0 => {
store!(self, a, 0)
}
Ops::astore_1 => {
store!(self, a, 1)
}
Ops::astore_2 => {
store!(self, a, 2)
}
Ops::astore_3 => {
store!(self, a, 3)
}
Ops::iastore => {
let Value::Primitive(Primitive::Int(value)) =
self.stack.pop().expect("value on stack")
else {
panic!("Value on stack was not int")
};
let Value::Primitive(Primitive::Int(index)) =
self.stack.pop().expect("value on stack")
else {
panic!("index on stack was not int")
};
let Value::Reference(Some(Reference::ArrayReference(ArrayReference::Primitive(
arr,
)))) = self.stack.pop().expect("value on stack")
else {
panic!("Reference not on stack")
};
let (id, array) = *arr.lock().unwrap();
Ok(ExecutionResult::Continue)
}
//Stack
Ops::dup => {
if let Some(value) = self.stack.last() {
@ -698,10 +740,12 @@ impl Frame {
Ops::dadd => {
let value1 = self.stack.pop().expect("Stack must have value");
let value2 = self.stack.pop().expect("Stack must have value");
if let (Value::Double(double1), Value::Double(double2)) =
(value1.clone(), value2.clone())
if let (
Value::Primitive(Primitive::Double(double1)),
Value::Primitive(Primitive::Double(double2)),
) = (value1.clone(), value2.clone())
{
self.stack.push(Value::Double(double1 + double2));
self.stack.push(Value::from(double1 + double2));
Ok(ExecutionResult::Continue)
} else {
Err(VmError::StackError(format!(
@ -710,11 +754,53 @@ impl Frame {
}
}
//Conversions
// Conversions
Ops::i2l => {
if let Value::Primitive(Primitive::Int(int)) =
self.stack.pop().expect("Stack must have value")
{
let long: i64 = int.into();
self.stack.push(Value::from(long));
Ok(ExecutionResult::Continue)
} else {
Err(VmError::StackError("Popped value was not int".to_string()))
}
}
Ops::i2f => {
todo!("int to float cast")
}
Ops::i2d => {
if let Value::Primitive(Primitive::Int(int)) =
self.stack.pop().expect("Stack must have value")
{
let double: f64 = int.into();
self.stack.push(Value::from(double));
Ok(ExecutionResult::Continue)
} else {
Err(VmError::StackError("Popped value was not int".to_string()))
}
}
Ops::l2i => {
todo!("long to int cast")
}
Ops::l2f => {
todo!("long to float cast")
}
Ops::l2d => {
todo!("long to double cast")
}
Ops::f2i => {
todo!("float to int cast")
}
Ops::f2l => {
todo!("float to long cast")
}
Ops::f2d => {
if let Value::Float(float) = self.stack.pop().expect("Stack must have value") {
if let Value::Primitive(Primitive::Float(float)) =
self.stack.pop().expect("Stack must have value")
{
let double: f64 = float.into();
self.stack.push(Value::Double(double));
self.stack.push(Value::from(double));
Ok(ExecutionResult::Continue)
} else {
Err(VmError::StackError(
@ -722,6 +808,33 @@ impl Frame {
))
}
}
Ops::d2i => {
todo!("double to int cast")
}
Ops::d2l => {
if let Value::Primitive(Primitive::Double(double)) =
self.stack.pop().expect("Stack must have value")
{
let long: i64 = double as i64;
self.stack.push(Value::from(long));
Ok(ExecutionResult::Continue)
} else {
Err(VmError::StackError("Popped value was not int".to_string()))
}
}
Ops::d2f => {
todo!("double to float cast")
}
Ops::i2b => {
todo!("int to byte cast")
}
Ops::i2c => {
todo!("int to char cast")
}
Ops::i2s => {
todo!("int to short cast")
}
// Control
Ops::return_void => Ok(ExecutionResult::Return(())),
@ -767,22 +880,31 @@ impl Frame {
}
Ops::getfield(index) => {
todo!("op getfield: index - {}", index)
let field_ref = self.pool.resolve_field(*index)?;
trace!("Getting field {field_ref:?}");
if let Value::Reference(object_ref) =
self.stack.pop().expect("object reference on stack")
{
if let Some(Reference::ObjectReference(object)) = object_ref {
let val = object.lock().unwrap().get_field(&field_ref.name);
self.stack.push(val);
Ok(ExecutionResult::Continue)
} else {
Err(VmError::StackError("Null pointer exception".to_string()))
}
} else {
Err(VmError::StackError(
"putfield tried to operate on a non object stack value".to_string(),
))
}
}
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 {
if let Some(Reference::ObjectReference(object)) = reference {
object.lock().unwrap().set_field(&field_ref.name, value);
Ok(ExecutionResult::Continue)
} else {
@ -843,6 +965,14 @@ impl Frame {
Ok(ExecutionResult::Continue)
}
Ops::invokeinterface(_, _, _) => {
todo!("invokeInterface")
}
Ops::invokedynamic(_, _) => {
todo!("invokeDynamic")
}
// can init class
Ops::new(index) => {
let class = self.pool.resolve_class_name(*index)?;
@ -851,11 +981,44 @@ impl Frame {
.thread
.get_or_resolve_class(&class, self.thread.clone())
.expect("TO hecken work");
let object = self.thread.gc.write().unwrap().new(init_class);
self.stack.push(Value::Reference(Some(object)));
let object = self.thread.gc.write().unwrap().new_object(init_class);
self.stack
.push(Value::Reference(Some(Reference::from(object))));
Ok(ExecutionResult::Continue)
// todo!("'New' instruction")
}
Ops::newarray(array_type) => {
let value = self.stack.pop().expect("value to have stack");
let Value::Primitive(Primitive::Int(count)) = value else {
panic!("stack item was not int")
};
let array = self.thread.gc.write().unwrap().new_primitive_array();
self.stack
.push(Value::Reference(Some(Reference::from(array))));
Ok(ExecutionResult::Continue)
}
Ops::anewarray(_) => {
todo!("anewarray")
}
Ops::arraylength => {
todo!("arraylength")
}
Ops::athrow => {
todo!("athrow")
}
Ops::checkcast(_) => {
todo!("checkcast")
}
Ops::instanceof(_) => {
todo!("instanceof")
}
Ops::monitorenter => {
todo!("monitorenter")
}
Ops::monitorexit => {
todo!("monitorexit")
}
_ => {
todo!("Unimplemented op: {:?}", op)
}

View File

@ -1,5 +1,5 @@
use crate::class_file::constant_pool::ConstantPoolError;
use crate::Value;
use crate::value::Value;
#[macro_export]
macro_rules! store {
@ -7,7 +7,7 @@ macro_rules! store {
{
let index: usize = $index;
let value = $self.stack.pop().expect("Must contain value on stack");
println!("\tStoring: {value:?} into local[{index}]");
trace!("\tStoring: {value:?} into local[{index}]");
$self.vars[index] = value;
$self.vars[index + 1] = Value::Reference(None);
Ok(ExecutionResult::Continue)
@ -20,7 +20,7 @@ macro_rules! store {
{
let index: usize = $index;
let value = $self.stack.pop().expect("Must contain value on stack");
println!("\tStoring: {value:?} into local[{index}]");
trace!("\tStoring: {value:?} into local[{index}]");
$self.vars[index] = value;
Ok(ExecutionResult::Continue)
}
@ -28,6 +28,9 @@ macro_rules! store {
($self:expr, f, $index:expr) => {
store!($self, i, $index)
};
($self:expr, a, $index:expr) => {
store!($self, i, $index)
};
}
#[macro_export]
@ -36,7 +39,7 @@ macro_rules! load {
{
let index: usize = $index;
let value = $self.vars.get(index).expect("Local var to exist");
println!("\tLoading: local[{index}] - {value} onto stack");
trace!("\tLoading: local[{index}] - {value} onto stack");
$self.stack.push(value.clone());
Ok(ExecutionResult::Continue)
}

View File

@ -0,0 +1,63 @@
use crate::objects::object::{Object, ObjectReference};
use crate::value::{Primitive, Value};
use std::fmt::{Display, Formatter};
use std::sync::{Arc, Mutex};
#[derive(Debug, Clone)]
pub enum Reference {
ObjectReference(ObjectReference),
ArrayReference(ArrayReference),
}
impl Display for Reference {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let id = match self {
Reference::ObjectReference(x) => x.lock().unwrap().id,
Reference::ArrayReference(ArrayReference::Primitive(x)) => x.lock().unwrap().0,
Reference::ArrayReference(ArrayReference::Object(x)) => x.lock().unwrap().0,
};
write!(f, "{}", id)
}
}
impl From<ObjectReference> for Reference {
fn from(value: ObjectReference) -> Self {
Self::ObjectReference(value)
}
}
impl From<PrimitiveArrayReference> for Reference {
fn from(value: PrimitiveArrayReference) -> Self {
Self::ArrayReference(ArrayReference::Primitive(value))
}
}
impl From<ObjectArrayReference> for Reference {
fn from(value: ObjectArrayReference) -> Self {
Self::ArrayReference(ArrayReference::Object(value))
}
}
#[derive(Debug, Clone)]
pub enum ArrayReference {
Primitive(PrimitiveArrayReference),
Object(ObjectArrayReference),
}
pub type PrimitiveArrayReference = Arc<Mutex<PrimitiveArray>>;
type PrimitiveArray = (u32, Vec<Primitive>);
pub type ObjectArrayReference = Arc<Mutex<ObjectArray>>;
type ObjectArray = (u32, Vec<ObjectReference>);
#[derive(Debug)]
pub struct Array<T> {
id: u32,
backing: Vec<T>,
}
impl Array<Primitive> {
fn set(&self, index: i32, value: Value) {
let thing : [100, u32]
}
}

View File

@ -0,0 +1,3 @@
pub mod array;
pub mod object;
pub mod object_manager;

View File

@ -1,12 +1,12 @@
use crate::class::RuntimeClass;
use crate::class_file::ClassFile;
use crate::Value;
use crate::value::Value;
use dashmap::DashMap;
use log::trace;
use std::cell::RefCell;
use std::fmt::Display;
use std::sync::{Arc, Mutex};
pub type ObjectReference = Arc<Mutex<Object>>;
#[derive(Debug, Clone)]
pub struct Object {
pub id: u32,
@ -20,6 +20,14 @@ impl Object {
trace!("Setting '{}' to '{}'", field_name, value);
self.fields.insert(field_name.to_string(), value);
}
pub fn get_field(&self, field_name: &str) -> Value {
trace!("Fields for object:\n\t{:?}", self.fields);
self.fields
.get(&field_name.to_string())
.map(|e| e.clone())
.unwrap_or(Value::NULL)
}
}
impl Display for Object {

View File

@ -2,9 +2,10 @@ 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::objects::object_manager::ObjectManager;
use crate::value::{Primitive, Value};
use crate::vm::Vm;
use crate::{BaseType, FieldType, Frame, MethodDescriptor, Value, VmError};
use crate::{BaseType, FieldType, Frame, MethodDescriptor, VmError};
use deku::DekuError::Incomplete;
use jni::sys::{jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jobject, jshort};
use jni::JNIEnv;
@ -255,35 +256,35 @@ impl VmThread {
}
Some(FieldType::Base(BaseType::Long)) => {
let v = cif.call::<jlong>(cp, built_args.as_ref());
Ok(Some(Value::Long(v)))
Ok(Some(Value::Primitive(Primitive::Long(v))))
}
Some(FieldType::Base(BaseType::Int)) => {
let v = cif.call::<jint>(cp, built_args.as_ref());
Ok(Some(Value::Int(v)))
Ok(Some(v.into()))
}
Some(FieldType::Base(BaseType::Float)) => {
let v = cif.call::<jfloat>(cp, built_args.as_ref());
Ok(Some(Value::Float(v)))
Ok(Some(v.into()))
}
Some(FieldType::Base(BaseType::Double)) => {
let v = cif.call::<jdouble>(cp, built_args.as_ref());
Ok(Some(Value::Double(v)))
Ok(Some(v.into()))
}
Some(FieldType::Base(BaseType::Boolean)) => {
let v = cif.call::<jboolean>(cp, built_args.as_ref());
Ok(Some(Value::Int(v as i32)))
Ok(Some(v.into()))
}
Some(FieldType::Base(BaseType::Byte)) => {
let v = cif.call::<jbyte>(cp, built_args.as_ref());
Ok(Some(Value::Byte(v)))
Ok(Some(v.into()))
}
Some(FieldType::Base(BaseType::Char)) => {
let v = cif.call::<jchar>(cp, built_args.as_ref());
Ok(Some(Value::Char(v)))
Ok(Some(v.into()))
}
Some(FieldType::Base(BaseType::Short)) => {
let v = cif.call::<jshort>(cp, built_args.as_ref());
Ok(Some(Value::Short(v)))
Ok(Some(v.into()))
}
Some(FieldType::ClassType(_)) | Some(FieldType::ArrayType(_)) => {
let v = cif.call::<jobject>(cp, built_args.as_ref());
@ -304,14 +305,14 @@ fn build_args<'a>(params: Vec<Value>, storage: &'a mut Vec<Box<dyn Any>>) -> Vec
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::Primitive(Primitive::Int(x)) => storage.push(Box::new(x) as Box<dyn Any>),
Value::Primitive(Primitive::Boolean(x)) => storage.push(Box::new(x) as Box<dyn Any>),
Value::Primitive(Primitive::Char(x)) => storage.push(Box::new(x) as Box<dyn Any>),
Value::Primitive(Primitive::Float(x)) => storage.push(Box::new(x) as Box<dyn Any>),
Value::Primitive(Primitive::Double(x)) => storage.push(Box::new(x) as Box<dyn Any>),
Value::Primitive(Primitive::Byte(x)) => storage.push(Box::new(x) as Box<dyn Any>),
Value::Primitive(Primitive::Short(x)) => storage.push(Box::new(x) as Box<dyn Any>),
Value::Primitive(Primitive::Long(x)) => storage.push(Box::new(x) as Box<dyn Any>),
Value::Reference(x) => storage.push(Box::new(x) as Box<dyn Any>),
}
}

89
crates/core/src/value.rs Normal file
View File

@ -0,0 +1,89 @@
use crate::objects::array::Reference;
use crate::objects::object::ObjectReference;
use jni::sys::{jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jshort};
use std::fmt::{Display, Formatter};
/// A reference-counted, thread-safe pointer to an Object.
/// Represents a JVM runtime value.
///
/// This enum covers all primitive types and object references that can exist
/// on the operand stack or in local variables during bytecode execution.
#[derive(Debug, Clone)]
pub enum Value {
Primitive(Primitive),
/// Reference to an object (or null)
Reference(Option<Reference>),
}
macro_rules! impl_value_from {
($type:ty, $variant:ident) => {
impl From<$type> for Value {
fn from(value: $type) -> Self {
Self::Primitive(Primitive::$variant(value))
}
}
};
}
impl_value_from!(jint, Int);
impl_value_from!(jlong, Long);
impl_value_from!(jfloat, Float);
impl_value_from!(jdouble, Double);
impl_value_from!(jbyte, Byte);
impl_value_from!(jshort, Short);
impl_value_from!(jchar, Char);
impl From<jboolean> for Value {
fn from(value: jboolean) -> Self {
Self::Primitive(Primitive::Boolean(value != 0))
}
}
#[derive(Debug, Clone)]
pub enum Primitive {
/// Boolean value (true/false)
Boolean(bool),
/// Unicode character
Char(u16),
/// 32-bit floating point
Float(f32),
/// 64-bit floating point
Double(f64),
/// Signed 8-bit integer
Byte(i8),
/// Signed 16-bit integer
Short(i16),
/// Signed 32-bit integer
Int(i32),
/// Signed 64-bit integer
Long(i64),
}
impl Display for Primitive {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Primitive::Boolean(b) => write!(f, "bool({})", b),
Primitive::Char(c) => write!(f, "char({})", char::from_u32(*c as u32).unwrap_or('?')),
Primitive::Float(fl) => write!(f, "float({})", fl),
Primitive::Double(d) => write!(f, "double({})", d),
Primitive::Byte(b) => write!(f, "byte({})", b),
Primitive::Short(s) => write!(f, "short({})", s),
Primitive::Int(i) => write!(f, "int({})", i),
Primitive::Long(l) => write!(f, "long({})", l),
}
}
}
impl Value {
pub const NULL: Value = Value::Reference(None);
}
impl Display for Value {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Value::Primitive(prim) => write!(f, "{}", prim),
Value::Reference(Some(obj)) => write!(f, "Ref({})", obj),
Value::Reference(None) => write!(f, "null"),
}
}
}

View File

@ -1,7 +1,7 @@
use crate::class_file::ClassFile;
use crate::class_loader::ClassLoader;
use crate::object::Object;
use crate::object_manager::ObjectManager;
use crate::objects::object::Object;
use crate::objects::object_manager::ObjectManager;
use crate::thread::VmThread;
use crate::Frame;
use libloading::os::windows::Symbol;