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