Reflection for charsets

This commit is contained in:
james 2025-12-18 14:57:52 +10:30
parent fcfc1d5a71
commit a6f6507a85
No known key found for this signature in database
GPG Key ID: E1FFBA228F4CAD87
44 changed files with 4327 additions and 2308 deletions

5
.gitignore vendored
View File

@ -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
output
headers

10
.idea/.gitignore generated vendored Normal file
View File

@ -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/

55
.idea/codeStyles/Project.xml generated Normal file
View File

@ -0,0 +1,55 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<option name="AUTODETECT_INDENTS" value="false" />
<option name="OTHER_INDENT_OPTIONS">
<value>
<option name="USE_TAB_CHARACTER" value="true" />
</value>
</option>
<codeStyleSettings language="CSS">
<indentOptions>
<option name="USE_TAB_CHARACTER" value="true" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="HTML">
<indentOptions>
<option name="USE_TAB_CHARACTER" value="true" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="JSON">
<indentOptions>
<option name="USE_TAB_CHARACTER" value="true" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="JavaScript">
<indentOptions>
<option name="USE_TAB_CHARACTER" value="true" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="Rust">
<option name="RIGHT_MARGIN" value="-1" />
<option name="KEEP_FIRST_COLUMN_COMMENT" value="true" />
<option name="ALIGN_MULTILINE_CHAINED_METHODS" value="true" />
<option name="SOFT_MARGINS" value="100" />
<indentOptions>
<option name="USE_TAB_CHARACTER" value="true" />
<option name="SMART_TABS" value="true" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="TOML">
<indentOptions>
<option name="USE_TAB_CHARACTER" value="true" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="TypeScript">
<indentOptions>
<option name="USE_TAB_CHARACTER" value="true" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="XML">
<indentOptions>
<option name="USE_TAB_CHARACTER" value="true" />
</indentOptions>
</codeStyleSettings>
</code_scheme>
</component>

5
.idea/codeStyles/codeStyleConfig.xml generated Normal file
View File

@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>

14
.idea/discord.xml generated Normal file
View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DiscordProjectSettings">
<option name="show" value="PROJECT_FILES" />
<option name="description" value="" />
<option name="applicationTheme" value="default" />
<option name="iconsTheme" value="default" />
<option name="button1Title" value="" />
<option name="button1Url" value="" />
<option name="button2Title" value="" />
<option name="button2Url" value="" />
<option name="customApplicationId" value="" />
</component>
</project>

View File

@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="IncorrectFormatting" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
</profile>
</component>

10
.idea/misc.xml generated Normal file
View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="MaterialThemeProjectNewConfig">
<option name="metadata">
<MTProjectMetadataState>
<option name="userId" value="-4c3c6a73:19b0b3d1327:-7fff" />
</MTProjectMetadataState>
</option>
</component>
</project>

8
.idea/modules.xml generated Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/roast-vm.iml" filepath="$PROJECT_DIR$/.idea/roast-vm.iml" />
</modules>
</component>
</project>

17
.idea/roast-vm.iml generated Normal file
View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="EMPTY_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/crates/core/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/crates/roast-vm-sys/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/.claude" />
<excludeFolder url="file://$MODULE_DIR$/data" />
<excludeFolder url="file://$MODULE_DIR$/headers" />
<excludeFolder url="file://$MODULE_DIR$/lib" />
<excludeFolder url="file://$MODULE_DIR$/output" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

6
.idea/vcs.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View File

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

View File

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

View File

@ -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<R: deku::no_std_io::Read + deku::no_std_io::Seek>(
reader: &mut deku::reader::Reader<R>,
(_endian, byte_offset): (Endian, u16),
) -> Result<Self, DekuError> {
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<i32>,
}
impl<'a> DekuReader<'a, (Endian, u16)> for TableSwitchData {
fn from_reader_with_ctx<R: deku::no_std_io::Read + deku::no_std_io::Seek>(
reader: &mut deku::reader::Reader<R>,
(_endian, byte_offset): (Endian, u16),
) -> Result<Self, DekuError> {
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 {

View File

@ -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<FieldData>,
pub methods: Vec<MethodData>,
pub mirror: OnceLock<u32>,
pub mirror_in_progress: AtomicBool,
/// Thread-safe initialization state (JVM Spec 5.5)
pub init_state: Mutex<InitState>,
pub super_classes: Vec<Arc<RuntimeClass>>,
@ -55,11 +58,15 @@ impl PartialEq<Self> 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<RuntimeClass>) -> 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("<init>"))
.collect::<Vec<_>>();
constructors
.get(idx)
.ok_or(VmError::InvariantError(
"class does not have this constructor, consider inehritance impl".to_string(),
))
.copied()
}
}

View File

@ -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<R: deku::no_std_io::Read + deku::no_std_io::Seek>(
bytes: u32,
reader: &mut deku::reader::Reader<R>,
endian: deku::ctx::Endian,
_endian: deku::ctx::Endian,
) -> Result<Vec<(u16, Ops)>, DekuError> {
use deku::DekuReader;
@ -507,7 +509,7 @@ fn read_bytecode_with_offsets<R: deku::no_std_io::Read + deku::no_std_io::Seek>(
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<CodeAttribute>,
pub flags: MethodFlags,
@ -665,6 +668,16 @@ pub struct MethodData {
// pub method_parameters: Option<_>
}
impl From<MethodData> 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<u16> for ClassFlags {
}
}
impl Into<u16> 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<u16> for ModuleFlags {
}
}
impl Into<u16> 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<u16> for FieldFlags {
@ -784,6 +811,13 @@ impl From<u16> for FieldFlags {
}
}
impl Into<u16> 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<u16> for MethodFlags {
}
}
impl Into<u16> 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:

View File

@ -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<ConstantPoolEntry>;
@ -35,8 +36,8 @@ pub trait ConstantPoolExt: ConstantPoolGet {
//
fn get_string(&self, index: u16) -> Result<String, ConstantPoolError> {
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<String> for ConstantPoolError {
fn from(value: String) -> Self {
Self(value)
Self::Generic(value)
}
}
impl From<DescParseError> for ConstantPoolError {
fn from(value: DescParseError) -> Self {
value.to_string().into()
Self::DescriptorParseError(value)
}
}
impl From<Cesu8DecodingError> for ConstantPoolError {
fn from(value: Cesu8DecodingError) -> Self {
Self::Cesu8DecodingError(value)
}
}

View File

@ -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<Mutex<ClassLoader>>;
@ -83,9 +84,9 @@ pub fn resolve_path(what: &str) -> Result<(PathBuf, String), String> {
/// ```
#[derive(Default)]
pub struct ClassLoader {
classes: DashMap<(String, LoaderId), Arc<RuntimeClass>>,
pub(crate) classes: DashMap<(String, LoaderId), Arc<RuntimeClass>>,
bimage: Bimage,
pub needs_init: Vec<Arc<RuntimeClass>>,
// pub needs_init: Vec<Arc<RuntimeClass>>,
}
type LoaderId = Option<u32>;
@ -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<ClassFile>` 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<Arc<RuntimeClass>, VmError> {
pub fn load_class(
&mut self,
what: &str,
loader: LoaderId,
) -> Result<Arc<RuntimeClass>, 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::<Vec<_>>();
// 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::<Vec<_>>();
// 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<RuntimeClass>,
) -> Arc<RuntimeClass> {
pub fn create_array_class(&mut self, component: Arc<RuntimeClass>) -> Arc<RuntimeClass> {
// let name = format!("[{}", component.descriptor()); // e.g., "[Ljava/lang/String;"
let object_class: Arc<RuntimeClass> = self.get_or_load("java/lang/Object", None).unwrap();
let cloneable: Arc<RuntimeClass> = self.get_or_load("java/lang/Cloneable", None).unwrap();
let serializable: Arc<RuntimeClass> = self.get_or_load("java/io/Serializable", None).unwrap();
let serializable: Arc<RuntimeClass> =
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 <clinit>
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 <clinit>
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<RuntimeClass> {
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![],

View File

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

1500
crates/core/src/frame.rs Normal file

File diff suppressed because it is too large Load Diff

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -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::<Vec<_>>();
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)
}

View File

@ -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<Arc<RuntimeClass>> {
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<ObjectReference> {
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<ReferenceKind> {
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<u8> = 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<RuntimeClass> = &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<u16> = 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<u16> = 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
}

View File

@ -0,0 +1,3 @@
pub mod jni;
mod native_libraries;
pub mod r#unsafe;

View File

@ -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<jlong, FieldKey>,
next_field_offset: AtomicI64,
// Off-heap allocations (for allocateMemory/freeMemory)
allocations: HashMap<jlong, (*mut u8, usize)>,
}
#[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<FieldKey> {
self.field_offsets.get(&offset).cloned()
}
}
pub enum OffsetKind {
ArrayIndex(jlong),
Field(FieldKey),
}

View File

@ -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<RuntimeClass> {
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::<jint>(),
ArrayReference::Byte(x) => x.lock().backing.len() * size_of::<jbyte>(),
ArrayReference::Short(x) => x.lock().backing.len() * size_of::<jshort>(),
ArrayReference::Long(x) => x.lock().backing.len() * size_of::<jlong>(),
ArrayReference::Float(x) => x.lock().backing.len() * size_of::<jfloat>(),
ArrayReference::Double(x) => x.lock().backing.len() * size_of::<jdouble>(),
ArrayReference::Char(x) => x.lock().backing.len() * size_of::<jchar>(),
ArrayReference::Boolean(x) => x.lock().backing.len() * size_of::<jboolean>(),
ArrayReference::Object(x) => x.lock().backing.len() * size_of::<Reference>(),
}
}
}
@ -71,7 +87,7 @@ pub type ObjectArrayReference = Arc<Mutex<Array<Option<ReferenceKind>>>>;
pub struct Array<T: ArrayValue> {
pub(crate) id: u32,
pub(crate) class: Arc<RuntimeClass>,
pub(crate) backing: Box<[T]>,
pub backing: Box<[T]>,
}
impl<T> Array<T>
@ -97,15 +113,23 @@ where
pub fn len(&self) -> jint {
self.backing.len() as jint
}
// fn from(value: (u32, Arc<RuntimeClass>, Box<[T]>)) -> Self {
// let (id, class, vector) = value;
// Self {
// id,
// class,
// backing: vector,
// }
// }
}
impl<T: Primitive + ArrayValue> From<(u32, Arc<RuntimeClass>, Vec<T>)> for Array<T> {
fn from(value: (u32, Arc<RuntimeClass>, Vec<T>)) -> Self {
let (id, class, vector) = value;
impl<T: ArrayValue> From<(u32, Arc<RuntimeClass>, Box<[T]>)> for Array<T> {
fn from(value: (u32, Arc<RuntimeClass>, 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::<Vec<_>>() } else { (0..len).collect() } {
for i in if src_start < dst_start {
(0..len).rev().collect::<Vec<_>>()
} 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())),
}
}
}
}

View File

@ -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<Mutex<Object>>;
@ -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<ObjectReference> {
pub fn try_into_object_reference(&self) -> Result<ObjectReference, VmError> {
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<ArrayReference, VmError> {
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<RuntimeClass> {
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<ObjectArrayReference> 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<u8> = 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
}

View File

@ -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<u32, ReferenceKind>,
strings: HashMap<String, u32>,
monitors: DashMap<u32, Arc<Monitor>>,
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<RuntimeClass>) -> 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::<Vec<_>>();
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<RuntimeClass>, array_type: ArrayType, count: i32) -> ArrayReference {
let id = generate_identity_hash();
pub fn new_primitive_array(
&mut self,
class: Arc<RuntimeClass>,
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<RuntimeClass>, 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<RuntimeClass>, vector: Vec<i8>) -> ArrayReference {
warn!("Manual sidechannel byte array creation");
let id = generate_identity_hash();
pub fn new_object_array_from(
&mut self,
class: Arc<RuntimeClass>,
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<RuntimeClass>,
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::<Vec<_>>();
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<ObjectReference> {
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<RuntimeClass>, string_class: Arc<RuntimeClass>, utf8: &str) -> ObjectReference {
pub fn new_string(
&mut self,
byte_array_class: Arc<RuntimeClass>,
string_class: Arc<RuntimeClass>,
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::<Vec<_>>();
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<String, VmError> {
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<ReferenceKind, VmError> {
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<ReferenceKind, VmError> {
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<ReferenceKind, VmError> {
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<MonitorInner>,
condvar: Condvar,
}
struct MonitorInner {
owner: Option<ThreadId>,
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<RuntimeClass>) -> ReferenceKind {
self.get(klass.mirror())
}
pub fn new_constructor(
&mut self,
index: jint,
modifiers: jint,
parameters: Box<[Arc<RuntimeClass>]>,
exceptions: Box<[Arc<RuntimeClass>]>,
class_array_class: Arc<RuntimeClass>,
declaring_class: Arc<RuntimeClass>,
constructor_class: Arc<RuntimeClass>,
) -> ObjectReference {
let parameters = parameters
.iter()
.map(|c| {
let mirror_id = c.mirror.get().unwrap();
Some(self.get(*mirror_id))
})
.collect::<Box<[_]>>();
let exceptions = exceptions
.iter()
.map(|c| {
let mirror_id = c.mirror.get().unwrap();
Some(self.get(*mirror_id))
})
.collect::<Box<[_]>>();
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
}
}

View File

@ -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
})
}
}
static NEXT_ID: AtomicU32 = AtomicU32::new(1);
pub fn sequential_id() -> u32 {
NEXT_ID.fetch_add(1, Ordering::Relaxed)
}

View File

@ -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<Option<Value>, VmError>;
// Thread-local storage for current thread ID
@ -37,7 +43,7 @@ pub struct VmThread {
pub id: ThreadId,
pub vm: Arc<Vm>,
pub loader: Arc<Mutex<ClassLoader>>,
pub frame_stack: Mutex<Vec<Arc<Mutex<Frame>>>>,
pub frame_stack: Mutex<Vec<Frame>>,
pub gc: Arc<RwLock<ObjectManager>>,
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<Arc<RuntimeClass>, 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<Arc<RuntimeClass>, 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<RuntimeClass>) -> Result<(), VmError> {
use crate::class::InitState;
use std::thread;
pub fn init(&self, class: Arc<RuntimeClass>) -> 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 <clinit> if present
if let Ok(method) = class.find_method("<clinit>", &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 <clinit> if present (note: <clinit> is NOT inherited, only look in this class)
if let Some(method) = class.methods.iter().find(|m| m.name == "<clinit>") {
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<RuntimeClass>) -> 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 == "<clinit>") {
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<RuntimeClass>) -> 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<Value>) -> 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<RuntimeClass>, args: Vec<Value>) -> MethodCallResult {
let method = class.find_method(&method_reference.name, &method_reference.desc).unwrap();
pub fn invoke_virtual(
&self,
method_reference: MethodRef,
class: Arc<RuntimeClass>,
args: Vec<Value>,
) -> 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<Value>) -> 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<Value>) -> 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::<jlong>(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<Value>,
) -> 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))
}
}

View File

@ -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<Value>);
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<Value, VmError> {
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<Vec<Value>, 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<Value>
inner: Vec<Value>,
}
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<Value>, max_locals: usize) -> Self {
@ -130,11 +133,11 @@ impl LocalVariables {
val
}
pub fn iter(&self) -> impl Iterator<Item=&Value> {
pub fn iter(&self) -> impl Iterator<Item = &Value> {
self.inner.iter().filter(|v| !matches!(v, Value::Padding))
}
pub fn slots(&self) -> impl Iterator<Item=(u16, &Value)> {
pub fn slots(&self) -> impl Iterator<Item = (u16, &Value)> {
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<Value>) -> 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<ReferenceKind> {
pub fn as_ref_kind(&self) -> Result<ReferenceKind, VmError> {
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<ObjectReference, VmError> {
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<ArrayReference, VmError> {
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<jint, VmError> {
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<jlong, VmError> {
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::<bool>(),
Primitive::Char(_) => size_of::<u16>(),
Primitive::Float(_) => size_of::<f32>(),
Primitive::Double(_) => size_of::<f64>(),
Primitive::Byte(_) => size_of::<i8>(),
Primitive::Short(_) => size_of::<i16>(),
Primitive::Int(_) => size_of::<i32>(),
Primitive::Long(_) => size_of::<i64>(),
}
}
}
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<FieldType> 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<FieldType> for Value {
},
},
},
_ => { panic!("uhh what") }
_ => {
panic!("uhh what")
}
}
}
}

View File

@ -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<String, *const c_void>,
pub native_libraries: DashMap<String, Library>,
pub gc: Arc<RwLock<ObjectManager>>,
pub safent: RwLock<UnsafeSupport>,
}
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::<Vec<_>>();
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(())
}

View File

@ -11,4 +11,5 @@ log = "0.4.28"
[lib]
name = "roast_vm"
crate-type = ["cdylib"]
crate-type = ["cdylib"]

View File

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

View File

@ -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<u8> = (&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();
}
// 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 == "<init>" && (!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::<Vec<_>>()
.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::<Vec<_>>();
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
}

View File

@ -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<ObjectReference> {
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<ObjectReference> {
}
fn resolve_array(thread: &VmThread, obj: jobject) -> Option<ArrayReference> {
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<ReferenceKind> {
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>,

View File

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

View File

@ -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
}
}
#[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()
}
}
}

View File

@ -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<Value> = 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
}

View File

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

View File

@ -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
}
}
#[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
}

View File

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