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 # 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 # 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. # option (not recommended) you can uncomment the following to ignore the entire idea folder.
.idea/ #.idea/
data data
lib 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" libffi = "5.0.0"
jni = "0.21.1" jni = "0.21.1"
roast-vm-sys = { path = "crates/roast-vm-sys", version = "0.1.0" } 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 } jni = { workspace = true }
itertools = { workspace = true } itertools = { workspace = true }
colored = "3.0.0" colored = "3.0.0"
parking_lot = "0.12"
cesu8 = "1.1.0"
[build-dependencies] [build-dependencies]
bindgen = "0.72.1" bindgen = "0.72.1"

View File

@ -1,10 +1,11 @@
use crate::class_file::constant_pool::ConstantPoolExt; use crate::class_file::constant_pool::ConstantPoolExt;
use crate::class_file::{ClassFile, Constant, ConstantPoolEntry}; use crate::class_file::{ClassFile, Constant, ConstantPoolEntry};
use deku::DekuContainerRead; use deku::{DekuContainerRead, DekuError, DekuReader};
use deku_derive::DekuRead; use deku_derive::DekuRead;
use log::trace; use log::trace;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
use std::ops::Deref; use std::ops::Deref;
use deku::ctx::Endian;
#[derive(Clone, PartialEq, Debug, DekuRead)] #[derive(Clone, PartialEq, Debug, DekuRead)]
#[deku(ctx = "_endian: deku::ctx::Endian", endian = "big")] #[deku(ctx = "_endian: deku::ctx::Endian", endian = "big")]
@ -60,6 +61,72 @@ pub enum ArrayType {
T_LONG, 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 { impl Display for ArrayType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let str = match self { let str = match self {

View File

@ -3,12 +3,14 @@ use crate::class_file::{
ClassFile, ClassFlags, ConstantPoolEntry, FieldData, FieldInfo, FieldRef, MethodData, ClassFile, ClassFlags, ConstantPoolEntry, FieldData, FieldInfo, FieldRef, MethodData,
MethodInfo, MethodRef, MethodInfo, MethodRef,
}; };
use crate::error::VmError;
use crate::{FieldType, MethodDescriptor}; use crate::{FieldType, MethodDescriptor};
use log::trace; use log::trace;
use parking_lot::Mutex;
use std::hash::{Hash, Hasher}; 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 std::thread::ThreadId;
use crate::error::VmError;
/// JVM Spec 5.5: Initialization states for a class /// JVM Spec 5.5: Initialization states for a class
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
@ -33,6 +35,7 @@ pub struct RuntimeClass {
pub fields: Vec<FieldData>, pub fields: Vec<FieldData>,
pub methods: Vec<MethodData>, pub methods: Vec<MethodData>,
pub mirror: OnceLock<u32>, pub mirror: OnceLock<u32>,
pub mirror_in_progress: AtomicBool,
/// Thread-safe initialization state (JVM Spec 5.5) /// Thread-safe initialization state (JVM Spec 5.5)
pub init_state: Mutex<InitState>, pub init_state: Mutex<InitState>,
pub super_classes: Vec<Arc<RuntimeClass>>, pub super_classes: Vec<Arc<RuntimeClass>>,
@ -55,11 +58,15 @@ impl PartialEq<Self> for RuntimeClass {
impl Eq for RuntimeClass {} impl Eq for RuntimeClass {}
impl RuntimeClass { impl RuntimeClass {
pub fn mirror(&self) -> u32 {
*self.mirror.get().unwrap()
}
pub fn find_method(&self, name: &str, desc: &MethodDescriptor) -> Result<&MethodData, VmError> { pub fn find_method(&self, name: &str, desc: &MethodDescriptor) -> Result<&MethodData, VmError> {
trace!( trace!(
"Finding in {}, method Name: {name} desc:{desc},", "Finding in {}, method Name: {name} desc:{desc},",
self.this_class self.this_class
); );
if let Some(method) = self.methods.iter().find(|e| { if let Some(method) = self.methods.iter().find(|e| {
let name_match = e.name.eq(name); let name_match = e.name.eq(name);
let param_match = desc.parameters == e.desc.parameters; let param_match = desc.parameters == e.desc.parameters;
@ -100,7 +107,10 @@ impl RuntimeClass {
} }
pub fn is_assignable_into(&self, into: Arc<RuntimeClass>) -> bool { 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; return true;
} }
// Array covariance: both must be arrays, then check component types // Array covariance: both must be arrays, then check component types
@ -117,6 +127,36 @@ impl RuntimeClass {
} }
fn is_primitive_class(&self) -> bool { 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::attributes::{Attribute, AttributeInfo, CodeAttribute, LineNumberTableEntry};
use crate::class::RuntimeClass;
use crate::class_file::constant_pool::{ConstantPoolError, ConstantPoolExt, ConstantPoolOwned}; use crate::class_file::constant_pool::{ConstantPoolError, ConstantPoolExt, ConstantPoolOwned};
use crate::instructions::Ops; use crate::instructions::Ops;
use crate::value::Value; use crate::value::Value;
use crate::{BaseType, FieldType, MethodDescriptor}; use crate::{BaseType, FieldType, MethodDescriptor};
use deku::ctx::Endian::Big; use deku::ctx::Endian::Big;
use deku::{DekuContainerRead, DekuError}; use deku::{DekuContainerRead, DekuContainerWrite, DekuError};
use deku_derive::{DekuRead, DekuWrite}; use deku_derive::{DekuRead, DekuWrite};
use itertools::Itertools; use itertools::Itertools;
use parking_lot::Mutex;
use std::borrow::Cow; use std::borrow::Cow;
use std::fmt; use std::fmt;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
use std::ops::Deref; use std::ops::Deref;
use std::str::Chars; use std::str::Chars;
use std::sync::{Arc, Mutex}; use std::sync::Arc;
#[derive(Debug, PartialEq, DekuRead)] #[derive(Debug, PartialEq, DekuRead)]
#[deku(magic = b"\xCA\xFE\xBA\xBE", endian = "big")] #[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>( fn read_bytecode_with_offsets<R: deku::no_std_io::Read + deku::no_std_io::Seek>(
bytes: u32, bytes: u32,
reader: &mut deku::reader::Reader<R>, reader: &mut deku::reader::Reader<R>,
endian: deku::ctx::Endian, _endian: deku::ctx::Endian,
) -> Result<Vec<(u16, Ops)>, DekuError> { ) -> Result<Vec<(u16, Ops)>, DekuError> {
use deku::DekuReader; 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; let start_pos = reader.bits_read;
// Parse the next Op // 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 end_pos = reader.bits_read;
let op_bytes = ((end_pos - start_pos) / 8) as usize; let op_bytes = ((end_pos - start_pos) / 8) as usize;
@ -654,6 +656,7 @@ pub struct MethodRef {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct MethodData { pub struct MethodData {
pub name: String, pub name: String,
pub class: String,
pub desc: MethodDescriptor, pub desc: MethodDescriptor,
pub code: Option<CodeAttribute>, pub code: Option<CodeAttribute>,
pub flags: MethodFlags, pub flags: MethodFlags,
@ -665,6 +668,16 @@ pub struct MethodData {
// pub method_parameters: Option<_> // 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)] #[derive(Debug)]
pub struct FieldRef { pub struct FieldRef {
pub class: String, 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)] #[allow(non_snake_case)]
#[derive(Debug, PartialEq, DekuRead, DekuWrite)] #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
pub struct ModuleFlags { 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)] #[allow(non_snake_case)]
#[derive(Debug, PartialEq, DekuRead, DekuWrite)] #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
pub struct FieldFlags { pub struct FieldFlags {
#[deku(bits = 1, pad_bits_before = "1")] #[deku(bits = 1, pad_bits_before = "1")]
ACC_ENUM: bool, pub ACC_ENUM: bool,
#[deku(bits = 1, pad_bits_before = "1")] #[deku(bits = 1, pad_bits_before = "1")]
ACC_SYNTHETIC: bool, pub ACC_SYNTHETIC: bool,
#[deku(bits = 1, pad_bits_before = "4")] #[deku(bits = 1, pad_bits_before = "4")]
ACC_TRANSIENT: bool, pub ACC_TRANSIENT: bool,
#[deku(bits = 1)] #[deku(bits = 1)]
ACC_VOLATILE: bool, pub ACC_VOLATILE: bool,
#[deku(bits = 1, pad_bits_before = "1")] #[deku(bits = 1, pad_bits_before = "1")]
ACC_FINAL: bool, pub ACC_FINAL: bool,
#[deku(bits = 1)] #[deku(bits = 1)]
ACC_STATIC: bool, pub ACC_STATIC: bool,
#[deku(bits = 1)] #[deku(bits = 1)]
ACC_PROTECTED: bool, pub ACC_PROTECTED: bool,
#[deku(bits = 1)] #[deku(bits = 1)]
ACC_PRIVATE: bool, pub ACC_PRIVATE: bool,
#[deku(bits = 1)] #[deku(bits = 1)]
ACC_PUBLIC: bool, pub ACC_PUBLIC: bool,
} }
impl From<u16> for FieldFlags { 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)] #[allow(non_snake_case)]
#[derive(Debug, PartialEq, DekuRead, DekuWrite, Clone)] #[derive(Debug, PartialEq, DekuRead, DekuWrite, Clone)]
pub struct MethodFlags { 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 //yoinked because im monkled
impl MethodDescriptor { impl MethodDescriptor {
/// Parses a method descriptor as specified in the JVM specs: /// Parses a method descriptor as specified in the JVM specs:

View File

@ -5,13 +5,14 @@ use crate::class_file::{
ConstantClassInfo, ConstantDynamicInfo, ConstantFieldrefInfo, ConstantInterfaceMethodrefInfo, ConstantClassInfo, ConstantDynamicInfo, ConstantFieldrefInfo, ConstantInterfaceMethodrefInfo,
ConstantInvokeDynamicInfo, ConstantMethodHandleInfo, ConstantMethodTypeInfo, ConstantInvokeDynamicInfo, ConstantMethodHandleInfo, ConstantMethodTypeInfo,
ConstantMethodrefInfo, ConstantModuleInfo, ConstantNameAndTypeInfo, ConstantPackageInfo, ConstantMethodrefInfo, ConstantModuleInfo, ConstantNameAndTypeInfo, ConstantPackageInfo,
ConstantPoolEntry, ConstantStringInfo, ConstantUtf8Info, DescParseError, FieldInfo, FieldRef ConstantPoolEntry, ConstantStringInfo, ConstantUtf8Info, DescParseError, FieldInfo, FieldRef,
, MethodRef, MethodRef,
}; };
use crate::error::VmError;
use crate::{pool_get_impl, FieldType, MethodDescriptor}; use crate::{pool_get_impl, FieldType, MethodDescriptor};
use cesu8::{from_java_cesu8, Cesu8DecodingError};
use deku::DekuContainerRead; use deku::DekuContainerRead;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
use crate::error::VmError;
pub type ConstantPoolSlice = [ConstantPoolEntry]; pub type ConstantPoolSlice = [ConstantPoolEntry];
pub type ConstantPoolOwned = Vec<ConstantPoolEntry>; pub type ConstantPoolOwned = Vec<ConstantPoolEntry>;
@ -35,8 +36,8 @@ pub trait ConstantPoolExt: ConstantPoolGet {
// //
fn get_string(&self, index: u16) -> Result<String, ConstantPoolError> { fn get_string(&self, index: u16) -> Result<String, ConstantPoolError> {
let cp_entry = self.get_utf8_info(index)?; let cp_entry = self.get_utf8_info(index)?;
let cow = from_java_cesu8(&cp_entry.bytes)?;
String::from_utf8(cp_entry.bytes.clone()).map_err(|e| e.to_string().into()) Ok(cow.into())
} }
// //
// fn get_field(&self, index: u16) -> Result<&ConstantFieldrefInfo, ()> { // fn get_field(&self, index: u16) -> Result<&ConstantFieldrefInfo, ()> {
@ -221,19 +222,38 @@ impl ConstantPoolGet for [ConstantPoolEntry] {}
impl Display for ConstantPoolError { impl Display for ConstantPoolError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 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)] #[derive(Debug)]
pub struct ConstantPoolError(String); pub enum ConstantPoolError {
Generic(String),
DescriptorParseError(DescParseError),
Cesu8DecodingError(Cesu8DecodingError),
}
impl From<String> for ConstantPoolError { impl From<String> for ConstantPoolError {
fn from(value: String) -> Self { fn from(value: String) -> Self {
Self(value) Self::Generic(value)
} }
} }
impl From<DescParseError> for ConstantPoolError { impl From<DescParseError> for ConstantPoolError {
fn from(value: DescParseError) -> Self { 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::{InitState, RuntimeClass};
use crate::class_file::constant_pool::{ConstantPoolExt, ConstantPoolGet}; use crate::class_file::constant_pool::{ConstantPoolExt, ConstantPoolGet};
use crate::class_file::{ClassFile, ClassFlags, FieldData, FieldFlags, MethodData, MethodFlags}; use crate::class_file::{ClassFile, ClassFlags, FieldData, FieldFlags, MethodData, MethodFlags};
use crate::error::VmError;
use crate::{FieldType, MethodDescriptor}; use crate::{FieldType, MethodDescriptor};
use dashmap::DashMap; use dashmap::DashMap;
use deku::DekuContainerRead; use deku::DekuContainerRead;
use log::warn; use log::warn;
use parking_lot::Mutex;
use std::collections::HashSet; use std::collections::HashSet;
use std::fs::File; use std::fs::File;
use std::io::Read; use std::io::Read;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::sync::{Arc, Mutex, OnceLock}; use std::sync::{Arc, OnceLock};
use crate::error::VmError;
pub type LoaderRef = Arc<Mutex<ClassLoader>>; pub type LoaderRef = Arc<Mutex<ClassLoader>>;
@ -83,9 +84,9 @@ pub fn resolve_path(what: &str) -> Result<(PathBuf, String), String> {
/// ``` /// ```
#[derive(Default)] #[derive(Default)]
pub struct ClassLoader { pub struct ClassLoader {
classes: DashMap<(String, LoaderId), Arc<RuntimeClass>>, pub(crate) classes: DashMap<(String, LoaderId), Arc<RuntimeClass>>,
bimage: Bimage, bimage: Bimage,
pub needs_init: Vec<Arc<RuntimeClass>>, // pub needs_init: Vec<Arc<RuntimeClass>>,
} }
type LoaderId = Option<u32>; type LoaderId = Option<u32>;
@ -104,7 +105,7 @@ impl ClassLoader {
self.classes self.classes
.iter() .iter()
.map(|x| x.value().clone()) .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. /// 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"); .expect("invalid L descriptor");
self.get_or_load(class_name, None) self.get_or_load(class_name, None)
} }
None => Err(VmError::LoaderError("empty component descriptor".to_string())), None => Err(VmError::LoaderError(
_ => Err(VmError::LoaderError(format!("invalid component descriptor: {}", component_name))), "empty component descriptor".to_string(),
)),
_ => Err(VmError::LoaderError(format!(
"invalid component descriptor: {}",
component_name
))),
}?; }?;
// let component = self.get_or_load(component_name, None)?; // let component = self.get_or_load(component_name, None)?;
let arr_class = self.create_array_class( let arr_class = self.create_array_class(component);
component, return Ok(arr_class);
);
self.needs_init.push(arr_class.clone());
return Ok(arr_class)
} }
let class = self.load_class(class_name, loader)?; let class = self.load_class(class_name, loader)?;
self.needs_init.push(class.clone());
Ok(class) Ok(class)
} }
@ -184,19 +186,27 @@ impl ClassLoader {
self.classes.clone() 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 (module, class_fqn) = ("", what);
let bytes = self let bytes = self
.bimage .bimage
.get_class(module, class_fqn).or_else(|e| Self::load_class_from_disk(what)).map_err(|e1| { .get_class(module, class_fqn)
let classes = self.classes.iter().map(|x| { .or_else(|e| Self::load_class_from_disk(what))
x.this_class.clone() .map_err(|e1| {
}).collect::<Vec<_>>(); let classes = self
// println!("{:#?}", classes); .classes
VmError::LoaderError(format!("failed to find class: {:?}\n{:#?}", what, classes)) .iter()
})?; .map(|x| x.this_class.clone())
let (_, cf) = ClassFile::from_bytes((bytes.as_ref(), 0)) .collect::<Vec<_>>();
.map_err(|e| VmError::DekuError(e))?; // 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 runtime = self.runtime_class(cf);
let arced = Arc::new(runtime); let arced = Arc::new(runtime);
let option = self let option = self
@ -216,7 +226,7 @@ impl ClassLoader {
let path = format!("{class_path}/{what}.class"); let path = format!("{class_path}/{what}.class");
log::info!("Loading class from path: {}", path); 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(); let mut bytes = Vec::new();
class_file.read_to_end(&mut bytes).unwrap(); class_file.read_to_end(&mut bytes).unwrap();
Ok(bytes) Ok(bytes)
@ -302,6 +312,7 @@ impl ClassLoader {
.iter() .iter()
.map(|e| { .map(|e| {
let name = constant_pool.get_string(e.name_index).unwrap(); let name = constant_pool.get_string(e.name_index).unwrap();
let class = this_class.clone();
let flags = MethodFlags::from(e.access_flags); let flags = MethodFlags::from(e.access_flags);
let desc = constant_pool let desc = constant_pool
.get_string(e.descriptor_index) .get_string(e.descriptor_index)
@ -327,6 +338,7 @@ impl ClassLoader {
MethodData { MethodData {
name, name,
class,
flags, flags,
desc, desc,
code, code,
@ -359,9 +371,7 @@ impl ClassLoader {
let source_file = class_file.attributes.iter().find_map(|attr| { let source_file = class_file.attributes.iter().find_map(|attr| {
match constant_pool.parse_attribute(attr.clone()).ok()? { match constant_pool.parse_attribute(attr.clone()).ok()? {
Attribute::SourceFile(index) => { Attribute::SourceFile(index) => constant_pool.get_string(index).ok(),
constant_pool.get_string(index).ok()
},
_ => None, _ => None,
} }
}); });
@ -380,6 +390,7 @@ impl ClassLoader {
super_interfaces, super_interfaces,
component_type: None, component_type: None,
source_file, source_file,
mirror_in_progress: Default::default(),
} }
} }
// pub fn get_or_create_array_class(class_name: &str) -> RuntimeClass { // pub fn get_or_create_array_class(class_name: &str) -> RuntimeClass {
@ -391,14 +402,12 @@ impl ClassLoader {
// Ok(class) // Ok(class)
// } // }
pub fn create_array_class( pub fn create_array_class(&mut self, component: Arc<RuntimeClass>) -> Arc<RuntimeClass> {
&mut self,
component: Arc<RuntimeClass>,
) -> Arc<RuntimeClass> {
// let name = format!("[{}", component.descriptor()); // e.g., "[Ljava/lang/String;" // 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 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 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() { let name = match component.this_class.as_str() {
"byte" => "[B".to_string(), "byte" => "[B".to_string(),
"char" => "[C".to_string(), "char" => "[C".to_string(),
@ -408,49 +417,41 @@ impl ClassLoader {
"long" => "[J".to_string(), "long" => "[J".to_string(),
"short" => "[S".to_string(), "short" => "[S".to_string(),
"boolean" => "[Z".to_string(), "boolean" => "[Z".to_string(),
s if s.starts_with('[') => format!("[{}", s), // nested array s if s.starts_with('[') => format!("[{}", s), // nested array
s => format!("[L{};", s), // object class 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); let klass = Arc::new(klass);
self.classes.insert((name.to_string(), None), klass.clone()); self.classes.insert((name.to_string(), None), klass.clone());
klass klass
} }
pub fn primitive_class(&mut self, name: &str) -> Arc<RuntimeClass> { pub fn primitive_class(&mut self, name: &str) -> Arc<RuntimeClass> {
let klass = Arc::new(RuntimeClass { let klass = Arc::new(RuntimeClass {
constant_pool: Arc::new(vec![]), constant_pool: Arc::new(vec![]),
access_flags: ClassFlags { access_flags: ClassFlags {
@ -470,6 +471,7 @@ impl ClassLoader {
fields: vec![], fields: vec![],
methods: vec![], methods: vec![],
mirror: Default::default(), mirror: Default::default(),
mirror_in_progress: Default::default(),
init_state: Mutex::new(InitState::NotInitialized), init_state: Mutex::new(InitState::NotInitialized),
super_classes: vec![], super_classes: vec![],
super_interfaces: 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 crate::class_file::constant_pool::ConstantPoolError;
use deku::DekuError;
use std::fmt::{Display, Formatter};
#[derive(Debug)] #[derive(Debug)]
pub enum VmError { pub enum VmError {
ConstantPoolError(String), ConstantPoolError(String),
StackError(String), StackError(String),
InvariantError(String), InvariantError(String),
Debug(&'static str),
DekuError(DekuError), DekuError(DekuError),
LoaderError(String), LoaderError(String),
ExecutionError, ExecutionError,
@ -15,6 +16,7 @@ pub enum VmError {
message: String, message: String,
stack_trace: Vec<StackTraceElement>, stack_trace: Vec<StackTraceElement>,
}, },
NotImplemented(String),
} }
impl VmError { impl VmError {
@ -33,7 +35,11 @@ impl Display for VmError {
VmError::LoaderError(msg) => write!(f, "Loader error: {}", msg), VmError::LoaderError(msg) => write!(f, "Loader error: {}", msg),
VmError::ExecutionError => write!(f, "Execution error"), VmError::ExecutionError => write!(f, "Execution error"),
VmError::NativeError(msg) => write!(f, "Native error {msg}"), 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 { impl VmError {
pub(crate) fn with_frame(self, elem: StackTraceElement) -> Self { pub(crate) fn with_frame(self, elem: StackTraceElement) -> Self {
match self { match self {
VmError::Exception { message, mut stack_trace } => { VmError::Exception {
message,
mut stack_trace,
} => {
stack_trace.push(elem); stack_trace.push(elem);
VmError::Exception { message, stack_trace } VmError::Exception {
message,
stack_trace,
}
} }
other => VmError::Exception { other => VmError::Exception {
message: format!("{}", other), 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, LookupSwitchData, TableSwitchData};
use crate::attributes::ArrayType;
use deku_derive::DekuRead; use deku_derive::DekuRead;
use std::fmt::{Display, Formatter};
//noinspection SpellCheckingInspection //noinspection SpellCheckingInspection
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
#[derive(Clone, PartialEq, Debug, DekuRead)] #[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 { pub enum Ops {
// Constants // Constants
#[deku(id = 0x00)] #[deku(id = 0x00)]
@ -366,9 +366,9 @@ pub enum Ops {
ret(u8), ret(u8),
// //
#[deku(id = 0xaa)] #[deku(id = 0xaa)]
tableswitch, tableswitch(#[deku(ctx = "byte_offset")] TableSwitchData),
#[deku(id = 0xab)] #[deku(id = 0xab)]
lookupswitch, lookupswitch(#[deku(ctx = "byte_offset")] LookupSwitchData),
#[deku(id = 0xac)] #[deku(id = 0xac)]
ireturn, ireturn,
#[deku(id = 0xad)] #[deku(id = 0xad)]
@ -424,7 +424,7 @@ pub enum Ops {
monitorexit, monitorexit,
//extended //extended
#[deku(id = 0xC4)] #[deku(id = 0xC4)]
wide, wide(#[deku(ctx = "byte_offset")] WideData),
#[deku(id = 0xC5)] #[deku(id = 0xC5)]
multianewarray(u16, u8), multianewarray(u16, u8),
#[deku(id = 0xC6)] #[deku(id = 0xC6)]
@ -443,6 +443,40 @@ pub enum Ops {
#[deku(id = 0xFF)] #[deku(id = 0xFF)]
impdep2, 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 { impl Display for Ops {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 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::goto(off) => write!(f, "goto {}", off),
Ops::jsr(off) => write!(f, "jsr {}", off), Ops::jsr(off) => write!(f, "jsr {}", off),
Ops::ret(idx) => write!(f, "ret {}", idx), Ops::ret(idx) => write!(f, "ret {}", idx),
Ops::tableswitch => write!(f, "tableswitch"), Ops::tableswitch(_) => write!(f, "tableswitch"),
Ops::lookupswitch => write!(f, "lookupswitch"), Ops::lookupswitch(_) => write!(f, "lookupswitch"),
Ops::ireturn => write!(f, "ireturn"), Ops::ireturn => write!(f, "ireturn"),
Ops::lreturn => write!(f, "lreturn"), Ops::lreturn => write!(f, "lreturn"),
Ops::freturn => write!(f, "freturn"), Ops::freturn => write!(f, "freturn"),
@ -662,7 +696,7 @@ impl Display for Ops {
Ops::monitorexit => write!(f, "monitorexit"), Ops::monitorexit => write!(f, "monitorexit"),
// Extended // Extended
Ops::wide => write!(f, "wide"), Ops::wide(_) => write!(f, "wide"),
Ops::multianewarray(idx, dims) => write!(f, "multianewarray #{}, {}", idx, dims), Ops::multianewarray(idx, dims) => write!(f, "multianewarray #{}, {}", idx, dims),
Ops::ifnull(off) => write!(f, "ifnull {}", off), Ops::ifnull(off) => write!(f, "ifnull {}", off),
Ops::ifnonnull(off) => write!(f, "ifnonnull {}", off), Ops::ifnonnull(off) => write!(f, "ifnonnull {}", off),
@ -675,4 +709,4 @@ impl Display for Ops {
Ops::impdep2 => write!(f, "impdep2"), 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 index: usize = $index;
let value = $self.pop()?; let value = $self.pop()?;
trace!("\tStoring: {value} into local[{index}]"); trace!("\tStoring: {value} into local[{index}]");
$self.vars.set(index , value); $self.vars.set(index, value);
Ok(ExecutionResult::Continue) Ok(ExecutionResult::Continue)
} }
@ -21,7 +21,7 @@ macro_rules! store {
let index: usize = $index; let index: usize = $index;
let value = $self.pop()?; let value = $self.pop()?;
trace!("\tStoring: {value} into local[{index}]"); trace!("\tStoring: {value} into local[{index}]");
$self.vars.set(index , value); $self.vars.set(index, value);
Ok(ExecutionResult::Continue) Ok(ExecutionResult::Continue)
} }
}}; }};
@ -65,10 +65,11 @@ macro_rules! pool_get_impl {
let cp_entry = self.get_constant(index)?; let cp_entry = self.get_constant(index)?;
match cp_entry { match cp_entry {
ConstantPoolEntry::$variant(value) => Ok(value), ConstantPoolEntry::$variant(value) => Ok(value),
_ => Err(ConstantPoolError(format!( other => Err(ConstantPoolError::Generic(format!(
"Expected {} constant at index {}", "Expected {} constant at index {}, got {:?} instead",
stringify!($variant), stringify!($variant),
index index,
other
))), ))),
} }
} }
@ -82,7 +83,7 @@ macro_rules! if_int_zero {
return Err(VmError::stack_not_int()); return Err(VmError::stack_not_int());
}; };
if v $op 0 { if v $op 0 {
Ok(ExecutionResult::Advance($offset)) Ok(ExecutionResult::Advance($offset as i32))
} else { } else {
Ok(ExecutionResult::Continue) Ok(ExecutionResult::Continue)
} }
@ -99,7 +100,7 @@ macro_rules! if_int_cmp {
return Err(VmError::stack_not_int()); return Err(VmError::stack_not_int());
}; };
if v1 $op v2 { if v1 $op v2 {
Ok(ExecutionResult::Advance($offset)) Ok(ExecutionResult::Advance($offset as i32))
} else { } else {
Ok(ExecutionResult::Continue) Ok(ExecutionResult::Continue)
} }
@ -108,136 +109,156 @@ macro_rules! if_int_cmp {
#[macro_export] #[macro_export]
macro_rules! float_cmp { macro_rules! float_cmp {
($self:expr, $prim:ident, $nan_result:expr) => {{ ($self:expr, $prim:ident, $nan_result:expr) => {{
let v2 = $self.pop()?; let v2 = $self.pop()?;
let v1 = $self.pop()?; let v1 = $self.pop()?;
let (Value::Primitive(Primitive::$prim(f1)), Value::Primitive(Primitive::$prim(f2))) = (&v1, &v2) else { let (Value::Primitive(Primitive::$prim(f1)), Value::Primitive(Primitive::$prim(f2))) =
return Err(VmError::StackError(format!( (&v1, &v2)
"{v1:?} or {v2:?} was not a {}", else {
stringify!($prim).to_lowercase() 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() { let result: i32 = if f1.is_nan() || f2.is_nan() {
$nan_result $nan_result
} else { } else {
match f1.partial_cmp(f2) { match f1.partial_cmp(f2) {
Some(std::cmp::Ordering::Greater) => 1, Some(std::cmp::Ordering::Greater) => 1,
Some(std::cmp::Ordering::Equal) => 0, Some(std::cmp::Ordering::Equal) => 0,
_ => -1, _ => -1,
} }
}; };
$self.stack.push(Value::from(result)); $self.stack.push(Value::from(result));
Ok(ExecutionResult::Continue) Ok(ExecutionResult::Continue)
}}; }};
} }
#[macro_export] #[macro_export]
macro_rules! convert_simple { macro_rules! convert_simple {
($self:expr, $from:ident, $to:ty) => {{ ($self:expr, $from:ident, $to:ty) => {{
let Value::Primitive(Primitive::$from(v)) = $self.pop()? else { let Value::Primitive(Primitive::$from(v)) = $self.pop()? else {
return Err(VmError::StackError(format!( return Err(VmError::StackError(format!(
"Expected {}", stringify!($from).to_lowercase() "Expected {}",
))); stringify!($from).to_lowercase()
}; )));
$self.stack.push(Value::from(v as $to)); };
Ok(ExecutionResult::Continue) $self.stack.push(Value::from(v as $to));
}}; Ok(ExecutionResult::Continue)
}};
} }
#[macro_export] #[macro_export]
macro_rules! convert_float_to_int { macro_rules! convert_float_to_int {
($self:expr, $from:ident, $from_ty:ty, $to:ty) => {{ ($self:expr, $from:ident, $from_ty:ty, $to:ty) => {{
let Value::Primitive(Primitive::$from(v)) = $self.pop()? else { let Value::Primitive(Primitive::$from(v)) = $self.pop()? else {
return Err(VmError::StackError(format!( return Err(VmError::StackError(format!(
"Expected {}", stringify!($from).to_lowercase() "Expected {}",
))); stringify!($from).to_lowercase()
}; )));
let result = if v.is_nan() { };
0 let result = if v.is_nan() {
} else if v >= <$to>::MAX as $from_ty { 0
<$to>::MAX } else if v >= <$to>::MAX as $from_ty {
} else if v <= <$to>::MIN as $from_ty { <$to>::MAX
<$to>::MIN } else if v <= <$to>::MIN as $from_ty {
} else { <$to>::MIN
v as $to } else {
}; v as $to
$self.stack.push(Value::from(result)); };
Ok(ExecutionResult::Continue) $self.stack.push(Value::from(result));
}}; Ok(ExecutionResult::Continue)
}};
} }
#[macro_export] #[macro_export]
macro_rules! convert_int_narrow { macro_rules! convert_int_narrow {
($self:expr, $to:ty) => {{ ($self:expr, $to:ty) => {{
let Value::Primitive(Primitive::Int(v)) = $self.pop()? else { let Value::Primitive(Primitive::Int(v)) = $self.pop()? else {
return Err(VmError::stack_not_int()); return Err(VmError::stack_not_int());
}; };
$self.stack.push(Value::from((v as $to) as i32)); $self.stack.push(Value::from((v as $to) as i32));
Ok(ExecutionResult::Continue) Ok(ExecutionResult::Continue)
}}; }};
} }
#[macro_export] #[macro_export]
macro_rules! array_store { macro_rules! array_store {
($self:expr, $prim:ident, $arr:ident) => {{ ($self:expr, $prim:ident, $arr:ident) => {{
let Value::Primitive(Primitive::$prim(value)) = $self.pop()? else { let Value::Primitive(Primitive::$prim(value)) = $self.pop()? else {
return Err(VmError::StackError(format!( return Err(VmError::StackError(format!(
"Value was not {}", stringify!($prim).to_lowercase() "Value was not {}",
))); stringify!($prim).to_lowercase()
}; )));
let Value::Primitive(Primitive::Int(index)) = $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!( let Value::Reference(Some(ReferenceKind::ArrayReference(ArrayReference::$arr(arr)))) =
"Expected {} array reference", stringify!($arr).to_lowercase() $self.pop()?
))); else {
}; return Err(VmError::StackError(format!(
arr.lock().unwrap().set(index, value); "Expected {} array reference",
Ok(ExecutionResult::Continue) stringify!($arr).to_lowercase()
}}; )));
};
arr.lock().set(index, value);
Ok(ExecutionResult::Continue)
}};
} }
#[macro_export] #[macro_export]
macro_rules! array_store_cast { macro_rules! array_store_cast {
($self:expr, $arr:ident, $cast:ty) => {{ ($self:expr, $arr:ident, $cast:ty) => {{
let Value::Primitive(Primitive::Int(value)) = $self.pop()? else { let Value::Primitive(Primitive::Int(value)) = $self.pop()? else {
return Err(VmError::stack_not_int()); return Err(VmError::stack_not_int());
}; };
let Value::Primitive(Primitive::Int(index)) = $self.pop()? else { let Value::Primitive(Primitive::Int(index)) = $self.pop()? else {
return Err(VmError::stack_not_int()); return Err(VmError::stack_not_int());
}; };
let Value::Reference(Some(ReferenceKind::ArrayReference(ArrayReference::$arr(arr)))) = $self.pop()? else { let Value::Reference(Some(ReferenceKind::ArrayReference(ArrayReference::$arr(arr)))) =
return Err(VmError::StackError(format!( $self.pop()?
"Expected {} array reference", stringify!($arr).to_lowercase() else {
))); return Err(VmError::StackError(format!(
}; "Expected {} array reference",
arr.lock().unwrap().set(index, value as $cast); stringify!($arr).to_lowercase()
Ok(ExecutionResult::Continue) )));
}}; };
arr.lock().set(index, value as $cast);
Ok(ExecutionResult::Continue)
}};
} }
//math //math
#[macro_export] #[macro_export]
macro_rules! binary_op { macro_rules! binary_op {
($self:expr, $prim:ident, |$a:ident, $b:ident| $expr:expr) => {{ ($self:expr, $prim:ident, |$a:ident, $b:ident| $expr:expr) => {{
let Value::Primitive(Primitive::$prim($b)) = $self.pop()? else { let Value::Primitive(Primitive::$prim($b)) = $self.pop()? else {
return Err(VmError::StackError(format!("Expected {}", stringify!($prim).to_lowercase()))); return Err(VmError::StackError(format!(
}; "Expected {}",
let Value::Primitive(Primitive::$prim($a)) = $self.pop()? else { stringify!($prim).to_lowercase()
return Err(VmError::StackError(format!("Expected {}", stringify!($prim).to_lowercase()))); )));
}; };
$self.stack.push(Value::from($expr)); let Value::Primitive(Primitive::$prim($a)) = $self.pop()? else {
Ok(ExecutionResult::Continue) return Err(VmError::StackError(format!(
}}; "Expected {}",
stringify!($prim).to_lowercase()
)));
};
$self.stack.push(Value::from($expr));
Ok(ExecutionResult::Continue)
}};
} }
#[macro_export] #[macro_export]
macro_rules! unary_op { macro_rules! unary_op {
($self:expr, $prim:ident, |$v:ident| $expr:expr) => {{ ($self:expr, $prim:ident, |$v:ident| $expr:expr) => {{
let Value::Primitive(Primitive::$prim($v)) = $self.pop()? else { let Value::Primitive(Primitive::$prim($v)) = $self.pop()? else {
return Err(VmError::StackError(format!("Expected {}", stringify!($prim).to_lowercase()))); return Err(VmError::StackError(format!(
}; "Expected {}",
$self.stack.push(Value::from($expr)); stringify!($prim).to_lowercase()
Ok(ExecutionResult::Continue) )));
}}; };
$self.stack.push(Value::from($expr));
Ok(ExecutionResult::Continue)
}};
} }
#[macro_export] #[macro_export]
macro_rules! int_div_rem { macro_rules! int_div_rem {
@ -262,15 +283,18 @@ macro_rules! int_div_rem {
} }
#[macro_export] #[macro_export]
macro_rules! shift_op { macro_rules! shift_op {
($self:expr, $prim:ident, $mask:expr, |$val:ident, $shift:ident| $expr:expr) => {{ ($self:expr, $prim:ident, $mask:expr, |$val:ident, $shift:ident| $expr:expr) => {{
let Value::Primitive(Primitive::Int(s)) = $self.pop()? else { let Value::Primitive(Primitive::Int(s)) = $self.pop()? else {
return Err(VmError::StackError("Expected int for shift amount".into())); return Err(VmError::StackError("Expected int for shift amount".into()));
}; };
let Value::Primitive(Primitive::$prim($val)) = $self.pop()? else { let Value::Primitive(Primitive::$prim($val)) = $self.pop()? else {
return Err(VmError::StackError(format!("Expected {}", stringify!($prim).to_lowercase()))); return Err(VmError::StackError(format!(
}; "Expected {}",
let $shift = (s & $mask) as u32; stringify!($prim).to_lowercase()
$self.stack.push(Value::from($expr)); )));
Ok(ExecutionResult::Continue) };
}}; 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 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() { fn main() {
env_logger::Builder::from_default_env() std::thread::Builder::new()
.filter_level(LevelFilter::Trace) .stack_size(8 * 1024 * 1024)
.filter_module("deku", LevelFilter::Warn) .spawn(run)
.filter_module("roast_vm_core::class_file::class_file", LevelFilter::Info) .unwrap()
.filter_module("roast_vm_core::attributes", LevelFilter::Info) .join()
.filter_module("roast_vm_core::instructions", LevelFilter::Info) .unwrap();
.init(); }
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(); let vm = Vm::new();
vm.load_native_library("roast_vm.dll", load("roast_vm.dll").into()); 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("jvm.dll", load("jvm.dll").into());
vm.load_native_library("java.dll", load("java.dll").into()); vm.load_native_library("java.dll", load("java.dll").into());
let start = Instant::now();
match vm.run("org/example/Main") { match vm.run("org/example/Main") {
Ok(_) => {} Ok(_) => {
Err(VmError::Exception { message, stack_trace }) => { 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 thread = vm.threads.get(&vm.main_thread_id).unwrap();
let objs = thread.gc.read().unwrap() let objs = thread
.gc
.read()
.objects .objects
.iter() .iter()
.map(|(x, y)| format!("{x} : {y}")) .map(|(x, y)| format!("{x} : {y}"))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let len = objs.len().clone(); let len = objs.len();
error!("Heap dump: len: {len} objs:\n{objs:#?}"); 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); eprintln!("{}: {}", "Exception".red().bold(), message);
for elem in &stack_trace { for elem in &stack_trace {
let class_name = elem.class.replace('/', "."); let class_name = elem.class.replace('/', ".");
@ -34,18 +57,11 @@ fn main() {
(Some(f), Some(l)) => format!("({}:{})", f, l), (Some(f), Some(l)) => format!("({}:{})", f, l),
(Some(f), None) => format!("({})", f), (Some(f), None) => format!("({})", f),
_ => "(Unknown Source)".to_string(), _ => "(Unknown Source)".to_string(),
}.blue().dimmed(); }
.blue()
.dimmed();
eprintln!("{} {}.{}{}", at, class_name, elem.method, location); 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) => { Err(e) => {
error!("VM Error: {:?}", e); error!("VM Error: {:?}", e);
@ -61,3 +77,21 @@ fn load(filename: &str) -> Library {
Library::from(leeb) 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)] #![allow(unused_variables)]
#![feature(c_variadic)] #![feature(c_variadic)]
use crate::class::RuntimeClass; use crate::class::RuntimeClass;
use crate::class_file::{FieldData, FieldRef};
use crate::objects::array::ArrayReference; use crate::objects::array::ArrayReference;
use crate::objects::object::{ObjectReference, ReferenceKind};
use crate::prim::jboolean; use crate::prim::jboolean;
use crate::thread::VmThread; use crate::thread::VmThread;
use crate::value::{Primitive, Value}; 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::{jclass, jint, jobject, JNINativeInterface_};
use jni::sys::{jstring, JNIEnv}; use jni::sys::{jstring, JNIEnv};
use log::{error, info, trace, warn};
use std::ffi::{c_char, CStr, CString}; use std::ffi::{c_char, CStr, CString};
use std::ptr; use std::ptr;
use std::sync::{Arc}; 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};
const JNI_VERSION_1_1: jint = 0x00010001; const JNI_VERSION_1_1: jint = 0x00010001;
const JNI_VERSION_1_2: jint = 0x00010002; 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>> { 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; let class_id = clazz as u32;
loader.class_from_mirror_id(class_id) loader.class_from_mirror_id(class_id)
} }
fn resolve_object(thread: &VmThread, obj: jobject) -> Option<ObjectReference> { 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 { let ReferenceKind::ObjectReference(obj_ref) = gc.get(obj as u32) else {
return None; return None;
}; };
Some(obj_ref.clone()) 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 { unsafe extern "system" fn jni_get_version(env: *mut JNIEnv) -> jint {
JNI_VERSION_24 JNI_VERSION_24
} }
@ -300,7 +309,7 @@ unsafe extern "system" fn get_string_utfchars(
// } // }
let thread = &*get_thread(env); let thread = &*get_thread(env);
let rust_string = { let rust_string = {
let gc = thread.gc.read().unwrap(); let gc = thread.gc.read();
let obj_id = str as u32; let obj_id = str as u32;
@ -309,8 +318,8 @@ unsafe extern "system" fn get_string_utfchars(
_ => return ptr::null(), _ => return ptr::null(),
}; };
let obj = obj_ref.lock().unwrap(); let obj = obj_ref.lock();
let field_ref = FieldRef{ let field_ref = FieldRef {
class: "java/lang/String".to_string(), class: "java/lang/String".to_string(),
name: "value".to_string(), name: "value".to_string(),
desc: FieldType::ArrayType(Box::new(FieldType::Base(BaseType::Byte))), desc: FieldType::ArrayType(Box::new(FieldType::Base(BaseType::Byte))),
@ -322,7 +331,7 @@ unsafe extern "system" fn get_string_utfchars(
return ptr::null(); 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(); 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 { unsafe extern "system" fn get_object_class(env: *mut JNIEnv, obj: jobject) -> jclass {
trace!("get_object_class"); trace!("get_object_class");
let thread = &*get_thread(env); let thread = &*get_thread(env);
let gc = thread.gc.read().unwrap(); let gc = thread.gc.read();
let obj_id = obj as u32; 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(); return ptr::null_mut();
}; };
let obj_lock = obj_ref.lock().unwrap(); let obj_lock = obj_ref.lock();
let class: &Arc<RuntimeClass> = &obj_lock.class; let class: &Arc<RuntimeClass> = &obj_lock.class;
*class.mirror.wait() as jclass *class.mirror.wait() as jclass
@ -371,12 +380,12 @@ unsafe extern "system" fn register_natives(
) -> jint { ) -> jint {
trace!("register_natives"); trace!("register_natives");
let thread = &*get_thread(env); let thread = &*get_thread(env);
let gc = thread.gc.read().unwrap(); let gc = thread.gc.read();
let class_id = clazz as u32; let class_id = clazz as u32;
let ReferenceKind::ObjectReference(class_ref) = gc.get(class_id) else { let ReferenceKind::ObjectReference(class_ref) = gc.get(class_id) else {
return JNI_ERR; 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 class_name = class_name.replace("/", "_");
// let boop = JClass::from_raw(clazz); // let boop = JClass::from_raw(clazz);
@ -399,12 +408,16 @@ unsafe extern "system" fn register_natives(
continue; continue;
} }
if full_name.contains("getDeclaredConstructors0") {
continue;
}
thread thread
.vm .vm
.native_methods .native_methods
.insert(full_name.to_owned(), fn_ptr); .insert(full_name.to_owned(), fn_ptr);
println!( trace!(
"name:{name}, signature:{signature}, fn_ptr{}", "name:{name}, signature:{signature}, fn_ptr{}",
fn_ptr.is_null() fn_ptr.is_null()
) )
@ -416,8 +429,6 @@ unsafe extern "system" fn register_natives(
// JNI FUNCTION STUBS - All unimplemented functions below // JNI FUNCTION STUBS - All unimplemented functions below
// ============================================================================ // ============================================================================
unsafe extern "system" fn define_class( unsafe extern "system" fn define_class(
env: *mut JNIEnv, env: *mut JNIEnv,
name: *const c_char, name: *const c_char,
@ -594,7 +605,7 @@ unsafe extern "system" fn is_instance_of(
let thread = &*get_thread(env); let thread = &*get_thread(env);
let second = resolve_class(thread, clazz).unwrap(); let second = resolve_class(thread, clazz).unwrap();
let obj = resolve_object(thread, obj).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) { if first.is_assignable_into(second) {
JNI_TRUE JNI_TRUE
} else { } else {
@ -610,29 +621,26 @@ unsafe extern "system" fn get_method_id(
) -> jmethodID { ) -> jmethodID {
trace!("get_method_id"); trace!("get_method_id");
let thread = &*get_thread(env); 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 name_str = CStr::from_ptr(name).to_str().unwrap();
let sig_str = CStr::from_ptr(sig).to_str().unwrap(); let sig_str = CStr::from_ptr(sig).to_str().unwrap();
let class_id = clazz as u32; 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 { // Brief lock just to get what we need
return ptr::null_mut(); 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 desc = MethodDescriptor::parse(sig_str).unwrap();
let method = runtime_class.find_method(name_str, &desc).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 method as *const _ as jmethodID
} }
@ -1024,29 +1032,23 @@ unsafe extern "system" fn get_field_id(
) -> jfieldID { ) -> jfieldID {
trace!("get_field_id"); trace!("get_field_id");
let thread = &*get_thread(env); let thread = &*get_thread(env);
let gc = thread.gc.read().unwrap(); let loader = thread.loader.lock();
let loader = thread.loader.lock().unwrap();
let name_str = CStr::from_ptr(name).to_str().unwrap(); let name_str = CStr::from_ptr(name).to_str().unwrap();
let sig_str = CStr::from_ptr(sig).to_str().unwrap(); let sig_str = CStr::from_ptr(sig).to_str().unwrap();
let class_id = clazz as u32; 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(); return ptr::null_mut();
}; };
// println!("class ref: {:?}", class_ref);
let Some(runtime_class) = loader.class_from_mirror_id(class_id) else { let Some(runtime_class) = loader.class_from_mirror_id(class_id) else {
return ptr::null_mut(); return ptr::null_mut();
}; };
thread.ensure_initialised(&runtime_class).unwrap();
let field_type = FieldType::parse(sig_str).unwrap(); let field_type = FieldType::parse(sig_str).unwrap();
let field_ref = runtime_class.find_field(name_str, &field_type).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 field_ref as *const _ as jfieldID
} }
@ -1067,12 +1069,12 @@ unsafe extern "system" fn get_boolean_field(
let thread = &*get_thread(env); let thread = &*get_thread(env);
let object = resolve_object(thread, obj).unwrap(); let object = resolve_object(thread, obj).unwrap();
let field_data = &*(field_id as *const FieldData); let field_data = &*(field_id as *const FieldData);
let field_ref = FieldRef{ let field_ref = FieldRef {
class: object.lock().unwrap().class.this_class.clone(), class: object.lock().class.this_class.clone(),
name: field_data.name.clone(), name: field_data.name.clone(),
desc: FieldType::Base(BaseType::Boolean), 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 let Value::Primitive(Primitive::Boolean(bool)) = val {
if bool { if bool {
JNI_TRUE JNI_TRUE
@ -1416,7 +1418,23 @@ unsafe extern "system" fn get_static_field_id(
name: *const c_char, name: *const c_char,
sig: *const c_char, sig: *const c_char,
) -> jfieldID { ) -> 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( 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 class = resolve_class(thread, clazz).unwrap();
let field_ref = &*(field_id as *const FieldData); let field_ref = &*(field_id as *const FieldData);
let field_again = class.find_field(&field_ref.name, &field_ref.desc).unwrap(); 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 let Some(Value::Primitive(Primitive::Boolean(bool))) = val {
if bool { if bool {
JNI_TRUE JNI_TRUE
@ -1507,11 +1525,22 @@ unsafe extern "system" fn get_static_double_field(
unsafe extern "system" fn set_static_object_field( unsafe extern "system" fn set_static_object_field(
env: *mut JNIEnv, env: *mut JNIEnv,
clazz: jclass, _clazz: jclass,
field_id: jfieldID, field_id: jfieldID,
value: jobject, 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( unsafe extern "system" fn set_static_boolean_field(
@ -1602,13 +1631,20 @@ unsafe extern "system" fn new_string(
String::new() String::new()
}); });
let str_ref = thread.intern_string(&str); // or non-interned path 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 str_id as jstring
} }
unsafe extern "system" fn get_string_length(env: *mut JNIEnv, str: jstring) -> jsize { 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( 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 { unsafe extern "system" fn new_string_utf(env: *mut JNIEnv, utf: *const c_char) -> jstring {
trace!("new_string_utf"); trace!("new_string_utf");
let thread = &*get_thread(env); let thread = &*get_thread(env);
// let mut gc = thread.gc.write().unwrap();
let intern = true; let intern = true;
let str = CStr::from_ptr(utf).to_str().unwrap(); 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(); 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 str_ref
}; };
let str_id = str_ref.lock().unwrap().id; let str_id = str_ref.lock().id;
str_id as jstring str_id as jstring
} }
unsafe extern "system" fn get_string_utf_length(env: *mut JNIEnv, str: jstring) -> jsize { 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( unsafe extern "system" fn release_string_utf_chars(
@ -1678,11 +1723,10 @@ unsafe extern "system" fn new_object_array(
init: jobject, init: jobject,
) -> jobjectArray { ) -> jobjectArray {
let thread = &*get_thread(env); let thread = &*get_thread(env);
let mut gc = thread.gc.write().unwrap(); let loader = thread.loader.lock();
let loader = thread.loader.lock().unwrap();
let class_id = clazz as u32; 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(); return ptr::null_mut();
}; };
@ -1690,15 +1734,16 @@ unsafe extern "system" fn new_object_array(
return ptr::null_mut(); 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(); 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(); // return ptr::null_mut();
// }; // };
// class_name_string.lock().unwrap().get_field() // class_name_string.lock().get_field()
arr_id as jobjectArray arr_id as jobjectArray
} }
@ -1720,13 +1765,13 @@ unsafe extern "system" fn set_object_array_element(
let thread = &*get_thread(env); let thread = &*get_thread(env);
let arr_id = array as u32; let arr_id = array as u32;
let ReferenceKind::ArrayReference(ArrayReference::Object(arr_ref)) = let ReferenceKind::ArrayReference(ArrayReference::Object(arr_ref)) =
thread.gc.read().unwrap().get(arr_id) thread.gc.read().get(arr_id)
else { else {
panic!("Oop") panic!("Oop")
}; };
let obj_id = val as u32; let obj_id = val as u32;
let obj_ref = thread.gc.read().unwrap().get(obj_id); let obj_ref = thread.gc.read().get(obj_id);
arr_ref.lock().unwrap().set(index, Some(obj_ref)) arr_ref.lock().set(index, Some(obj_ref))
} }
unsafe extern "system" fn new_boolean_array(env: *mut JNIEnv, len: jsize) -> jbooleanArray { 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, len: jsize,
buf: *mut jchar, 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( unsafe extern "system" fn get_string_utf_region(
@ -2090,7 +2145,36 @@ unsafe extern "system" fn get_string_utf_region(
len: jsize, len: jsize,
buf: *mut c_char, 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( 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 { unsafe extern "system" fn exception_check(env: *mut JNIEnv) -> jboolean {
error!("exception_check"); warn!("exception_check");
JNI_FALSE 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::objects::object::{Reference, ReferenceKind};
use crate::prim::Primitive; use crate::prim::Primitive;
use jni::sys::{jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jshort}; 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 std::ops::{Deref, DerefMut};
use crate::class::RuntimeClass; use std::sync::Arc;
use crate::error::VmError;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum ArrayReference { pub enum ArrayReference {
@ -22,43 +23,58 @@ pub enum ArrayReference {
impl ArrayReference { impl ArrayReference {
pub fn len(&self) -> jint { pub fn len(&self) -> jint {
match self { match self {
ArrayReference::Int(x) => x.lock().unwrap().len(), ArrayReference::Int(x) => x.lock().len(),
ArrayReference::Byte(x) => x.lock().unwrap().len(), ArrayReference::Byte(x) => x.lock().len(),
ArrayReference::Short(x) => x.lock().unwrap().len(), ArrayReference::Short(x) => x.lock().len(),
ArrayReference::Long(x) => x.lock().unwrap().len(), ArrayReference::Long(x) => x.lock().len(),
ArrayReference::Float(x) => x.lock().unwrap().len(), ArrayReference::Float(x) => x.lock().len(),
ArrayReference::Double(x) => x.lock().unwrap().len(), ArrayReference::Double(x) => x.lock().len(),
ArrayReference::Char(x) => x.lock().unwrap().len(), ArrayReference::Char(x) => x.lock().len(),
ArrayReference::Boolean(x) => x.lock().unwrap().len(), ArrayReference::Boolean(x) => x.lock().len(),
ArrayReference::Object(x) => x.lock().unwrap().len(), ArrayReference::Object(x) => x.lock().len(),
} }
} }
pub fn id(&self) -> u32 { pub fn id(&self) -> u32 {
match self { match self {
ArrayReference::Int(x) => x.lock().unwrap().id, ArrayReference::Int(x) => x.lock().id,
ArrayReference::Byte(x) => x.lock().unwrap().id, ArrayReference::Byte(x) => x.lock().id,
ArrayReference::Short(x) => x.lock().unwrap().id, ArrayReference::Short(x) => x.lock().id,
ArrayReference::Long(x) => x.lock().unwrap().id, ArrayReference::Long(x) => x.lock().id,
ArrayReference::Float(x) => x.lock().unwrap().id, ArrayReference::Float(x) => x.lock().id,
ArrayReference::Double(x) => x.lock().unwrap().id, ArrayReference::Double(x) => x.lock().id,
ArrayReference::Char(x) => x.lock().unwrap().id, ArrayReference::Char(x) => x.lock().id,
ArrayReference::Boolean(x) => x.lock().unwrap().id, ArrayReference::Boolean(x) => x.lock().id,
ArrayReference::Object(x) => x.lock().unwrap().id, ArrayReference::Object(x) => x.lock().id,
} }
} }
pub fn class(&self) -> Arc<RuntimeClass> { pub fn class(&self) -> Arc<RuntimeClass> {
match self { match self {
ArrayReference::Int(x) => x.lock().unwrap().class.clone(), ArrayReference::Int(x) => x.lock().class.clone(),
ArrayReference::Byte(x) => x.lock().unwrap().class.clone(), ArrayReference::Byte(x) => x.lock().class.clone(),
ArrayReference::Short(x) => x.lock().unwrap().class.clone(), ArrayReference::Short(x) => x.lock().class.clone(),
ArrayReference::Long(x) => x.lock().unwrap().class.clone(), ArrayReference::Long(x) => x.lock().class.clone(),
ArrayReference::Float(x) => x.lock().unwrap().class.clone(), ArrayReference::Float(x) => x.lock().class.clone(),
ArrayReference::Double(x) => x.lock().unwrap().class.clone(), ArrayReference::Double(x) => x.lock().class.clone(),
ArrayReference::Char(x) => x.lock().unwrap().class.clone(), ArrayReference::Char(x) => x.lock().class.clone(),
ArrayReference::Boolean(x) => x.lock().unwrap().class.clone(), ArrayReference::Boolean(x) => x.lock().class.clone(),
ArrayReference::Object(x) => x.lock().unwrap().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 struct Array<T: ArrayValue> {
pub(crate) id: u32, pub(crate) id: u32,
pub(crate) class: Arc<RuntimeClass>, pub(crate) class: Arc<RuntimeClass>,
pub(crate) backing: Box<[T]>, pub backing: Box<[T]>,
} }
impl<T> Array<T> impl<T> Array<T>
@ -97,15 +113,23 @@ where
pub fn len(&self) -> jint { pub fn len(&self) -> jint {
self.backing.len() as 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> { impl<T: ArrayValue> From<(u32, Arc<RuntimeClass>, Box<[T]>)> for Array<T> {
fn from(value: (u32, Arc<RuntimeClass>, Vec<T>)) -> Self { fn from(value: (u32, Arc<RuntimeClass>, Box<[T]>)) -> Self {
let (id, class, vector) = value; let (id, class, values) = value;
Self { Self {
id, id,
class, class,
backing: vector.into_boxed_slice(), backing: values,
} }
} }
} }
@ -139,7 +163,6 @@ impl ArrayValue for jdouble {}
impl ArrayValue for jboolean {} impl ArrayValue for jboolean {}
impl ArrayReference { impl ArrayReference {
pub fn copy_from( pub fn copy_from(
&self, &self,
@ -149,32 +172,36 @@ impl ArrayReference {
length: jint, length: jint,
) -> Result<(), VmError> { ) -> Result<(), VmError> {
macro_rules! copy { macro_rules! copy {
($src_arr:expr, $dst_arr:expr) => {{ ($src_arr:expr, $dst_arr:expr) => {{
let src_guard = $src_arr.lock().unwrap(); let src_guard = $src_arr.lock();
let mut dst_guard = $dst_arr.lock().unwrap(); let mut dst_guard = $dst_arr.lock();
let src_start = src_pos as usize; let src_start = src_pos as usize;
let dst_start = dst_pos as usize; let dst_start = dst_pos as usize;
let len = length as usize; let len = length as usize;
// Bounds check // Bounds check
if src_pos < 0 || dst_pos < 0 || length < 0 if src_pos < 0
|| src_start + len > src_guard.backing.len() || dst_pos < 0
|| dst_start + len > dst_guard.backing.len() || length < 0
{ || src_start + len > src_guard.backing.len()
return Err(VmError::InvariantError("Index oob".to_string())); || dst_start + len > dst_guard.backing.len()
} {
return Err(VmError::InvariantError("Index oob".to_string()));
}
if Arc::ptr_eq($src_arr, $dst_arr) { if Arc::ptr_eq($src_arr, $dst_arr) {
drop(src_guard); drop(src_guard);
dst_guard.backing.copy_within(src_start..src_start + len, dst_start); dst_guard
} else { .backing
dst_guard.backing[dst_start..dst_start + len] .copy_within(src_start..src_start + len, dst_start);
.copy_from_slice(&src_guard.backing[src_start..src_start + len]); } else {
} dst_guard.backing[dst_start..dst_start + len]
Ok(()) .copy_from_slice(&src_guard.backing[src_start..src_start + len]);
}}; }
} Ok(())
}};
}
use ArrayReference::*; use ArrayReference::*;
match (src, self) { match (src, self) {
@ -188,14 +215,14 @@ impl ArrayReference {
(Boolean(s), Boolean(d)) => copy!(s, d), (Boolean(s), Boolean(d)) => copy!(s, d),
(Object(s), Object(d)) => { (Object(s), Object(d)) => {
// Object arrays need clone, not copy // Object arrays need clone, not copy
let src_guard = s.lock().unwrap(); let src_guard = s.lock();
let mut dst_guard = d.lock().unwrap(); let mut dst_guard = d.lock();
let src_start = src_pos as usize; let src_start = src_pos as usize;
let dst_start = dst_pos as usize; let dst_start = dst_pos as usize;
let len = length 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() || src_start + len > src_guard.backing.len()
|| dst_start + len > dst_guard.backing.len() || dst_start + len > dst_guard.backing.len()
{ {
@ -204,7 +231,11 @@ impl ArrayReference {
if Arc::ptr_eq(s, d) { if Arc::ptr_eq(s, d) {
drop(src_guard); 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(); dst_guard.backing[dst_start + i] = dst_guard.backing[src_start + i].clone();
} }
} else { } else {
@ -217,4 +248,4 @@ impl ArrayReference {
_ => Err(VmError::InvariantError("Array type mismatch".to_string())), _ => Err(VmError::InvariantError("Array type mismatch".to_string())),
} }
} }
} }

View File

@ -1,14 +1,16 @@
use crate::class::RuntimeClass; use crate::class::RuntimeClass;
use crate::class_file::FieldRef;
use crate::error::VmError;
use crate::objects::array::{ArrayReference, ObjectArrayReference, PrimitiveArrayReference}; use crate::objects::array::{ArrayReference, ObjectArrayReference, PrimitiveArrayReference};
use crate::prim::{jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jshort}; use crate::prim::{jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jshort};
use crate::value::Value; use crate::value::Value;
use crate::{BaseType, FieldType};
use dashmap::DashMap; use dashmap::DashMap;
use log::trace; use log::trace;
use parking_lot::Mutex;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
use std::hash::Hash; use std::hash::Hash;
use std::sync::{Arc, Mutex}; use std::sync::Arc;
use crate::class_file::FieldRef;
use crate::{string_from_bytes, BaseType, FieldType};
pub type ObjectReference = Arc<Mutex<Object>>; pub type ObjectReference = Arc<Mutex<Object>>;
@ -30,26 +32,9 @@ impl Object {
trace!("Fields for object:\n\t{:#}", self.format_fields()); trace!("Fields for object:\n\t{:#}", self.format_fields());
self.fields self.fields
.get(&field_ref.name) .get(&field_ref.name)
.map(|e| e.clone()) .map(|r| r.value().clone())
.unwrap_or_else(||{ .unwrap_or_else(|| {
let initial = match &field_ref.desc { let initial = field_ref.desc.default_value();
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 }
};
self.fields.insert(field_ref.name.clone(), initial.clone()); self.fields.insert(field_ref.name.clone(), initial.clone());
initial initial
}) })
@ -98,21 +83,33 @@ pub enum ReferenceKind {
} }
impl ReferenceKind { impl ReferenceKind {
pub fn into_object_reference(self) -> Option<ObjectReference> { pub fn try_into_object_reference(&self) -> Result<ObjectReference, VmError> {
match self { match self {
Self::ObjectReference(inner) => Some(inner), Self::ObjectReference(obj) => Ok(obj.clone()),
_ => None, _ => 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 { pub fn id(&self) -> u32 {
match self { match self {
Self::ObjectReference(r) => r.lock().unwrap().id, Self::ObjectReference(r) => r.lock().id,
Self::ArrayReference(a) => a.id(), Self::ArrayReference(a) => a.id(),
} }
} }
pub fn class(&self) -> Arc<RuntimeClass> { pub fn class(&self) -> Arc<RuntimeClass> {
match self { match self {
Self::ObjectReference(r) => r.lock().unwrap().class.clone(), Self::ObjectReference(r) => r.lock().class.clone(),
Self::ArrayReference(a) => a.class(), Self::ArrayReference(a) => a.class(),
} }
} }
@ -124,52 +121,158 @@ impl Display for ReferenceKind {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let id = match self { let id = match self {
ReferenceKind::ObjectReference(x) => { ReferenceKind::ObjectReference(x) => {
let guard = x.lock().unwrap(); let guard = x.lock();
if guard.class.this_class == "java/lang/String" if guard.class.this_class == "java/lang/String"
&& let Some(field) = guard.fields.get("value") && 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 arr_guard = actual.lock();
let string = crate::string_from_bytes(&arr_guard); let string = string_from_bytes(&arr_guard);
format!("\u{AB}{}\u{BB}", string) format!("\u{AB}{}\u{BB}", string)
} else { } else {
format!("Obj<{}>", guard.class.this_class) format!("Obj<{}>", guard.class.this_class)
} }
} }
ReferenceKind::ArrayReference(ArrayReference::Int(x)) => { ReferenceKind::ArrayReference(ArrayReference::Int(x)) => {
let guard = x.lock().unwrap(); let guard = x.lock();
format!("int{:?}", guard.backing) 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)) => { ReferenceKind::ArrayReference(ArrayReference::Byte(x)) => {
let guard = x.lock().unwrap(); let guard = x.lock();
format!("byte{:?}", guard.backing) 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)) => { ReferenceKind::ArrayReference(ArrayReference::Short(x)) => {
let guard = x.lock().unwrap(); let guard = x.lock();
format!("short{:?}", guard.backing) 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)) => { ReferenceKind::ArrayReference(ArrayReference::Long(x)) => {
let guard = x.lock().unwrap(); let guard = x.lock();
format!("long{:?}", guard.backing) 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)) => { ReferenceKind::ArrayReference(ArrayReference::Float(x)) => {
let guard = x.lock().unwrap(); let guard = x.lock();
format!("float{:?}", guard.backing) 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)) => { ReferenceKind::ArrayReference(ArrayReference::Double(x)) => {
let guard = x.lock().unwrap(); let guard = x.lock();
format!("double{:?}", guard.backing) 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)) => { ReferenceKind::ArrayReference(ArrayReference::Char(x)) => {
let guard = x.lock().unwrap(); let guard = x.lock();
format!("char{:?}", guard.backing) 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)) => { ReferenceKind::ArrayReference(ArrayReference::Boolean(x)) => {
let guard = x.lock().unwrap(); let guard = x.lock();
format!("boolean{:?}", guard.backing) 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)) => { ReferenceKind::ArrayReference(ArrayReference::Object(x)) => {
let guard = x.lock().unwrap(); let guard = x.lock();
format!("object[{:?}]", guard.id) format!("object[{:?}]", guard.id)
} }
}; };
@ -236,3 +339,20 @@ impl From<ObjectArrayReference> for ReferenceKind {
Self::ArrayReference(ArrayReference::Object(value)) 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::attributes::ArrayType;
use crate::class::RuntimeClass; use crate::class::RuntimeClass;
use crate::class_file::ClassFlags; use crate::class_file::{ClassFlags, MethodData};
use crate::objects; use crate::error::VmError;
use crate::objects::array::{Array, ArrayReference, ArrayValue, PrimitiveArrayReference}; use crate::objects::array::{
use crate::objects::object::{Object, ObjectReference, Reference, ReferenceKind}; Array, ArrayReference, ArrayValue, ObjectArrayReference, PrimitiveArrayReference,
use crate::rng::generate_identity_hash; };
use crate::objects::object::{
string_from_bytes, Object, ObjectReference, Reference, ReferenceKind,
};
use crate::value::{Primitive, Value}; 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 jni::sys::{jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jshort};
use log::warn; use log::warn;
use parking_lot::{Condvar, Mutex};
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::{Arc, Mutex}; use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::Arc;
#[derive(Default)] #[derive(Default)]
pub struct ObjectManager { pub struct ObjectManager {
pub objects: HashMap<u32, ReferenceKind>, pub objects: HashMap<u32, ReferenceKind>,
strings: HashMap<String, u32>, strings: HashMap<String, u32>,
monitors: DashMap<u32, Arc<Monitor>>,
max_memory: usize,
next_id: AtomicU32,
} }
impl ObjectManager { 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 { 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!( assert!(
!self.objects.contains_key(&id), !self.objects.contains_key(&id),
"Generated ID already exists!" "Generated ID already exists!"
@ -33,29 +78,42 @@ impl ObjectManager {
object object
} }
pub fn new_primitive_array(&mut self, class: Arc<RuntimeClass>, array_type: ArrayType, count: i32) -> ArrayReference { pub fn new_primitive_array(
let id = generate_identity_hash(); &mut self,
class: Arc<RuntimeClass>,
array_type: ArrayType,
count: i32,
) -> ArrayReference {
let id = self.next_id();
assert!( assert!(
!self.objects.contains_key(&id), !self.objects.contains_key(&id),
"Generated ID already exists!" "Generated ID already exists!"
); );
let array_ref = match array_type { let array_ref = match array_type {
ArrayType::T_INT => ArrayReference::Int(Arc::new(Mutex::new(Array::new(id, class,count)))), ArrayType::T_INT => {
ArrayType::T_BYTE => ArrayReference::Byte(Arc::new(Mutex::new(Array::new(id, class,count)))), ArrayReference::Int(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_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 => { 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 => { 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 => { 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 { pub fn new_object_array(&mut self, class: Arc<RuntimeClass>, count: i32) -> ArrayReference {
let id = generate_identity_hash(); let id = self.next_id();
assert!( assert!(
!self.objects.contains_key(&id), !self.objects.contains_key(&id),
"Generated ID already exists!" "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 self.objects
.insert(id, ReferenceKind::ArrayReference(array_ref.clone())); .insert(id, ReferenceKind::ArrayReference(array_ref.clone()));
array_ref array_ref
} }
pub fn new_byte_array(&mut self, class: Arc<RuntimeClass>, vector: Vec<i8>) -> ArrayReference {
warn!("Manual sidechannel byte array creation"); pub fn new_object_array_from(
let id = generate_identity_hash(); &mut self,
class: Arc<RuntimeClass>,
values: Box<[Reference]>,
) -> ArrayReference {
let id = self.next_id();
assert!( assert!(
!self.objects.contains_key(&id), !self.objects.contains_key(&id),
"Generated ID already exists!" "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 self.objects
.insert(id, ReferenceKind::ArrayReference(array_ref.clone())); .insert(id, ReferenceKind::ArrayReference(array_ref.clone()));
@ -91,7 +173,6 @@ impl ObjectManager {
} }
pub fn get(&self, id: u32) -> ReferenceKind { pub fn get(&self, id: u32) -> ReferenceKind {
self.objects self.objects
.get(&id) .get(&id)
.unwrap_or_else(|| { .unwrap_or_else(|| {
@ -100,18 +181,23 @@ impl ObjectManager {
.iter() .iter()
.map(|(x, y)| format!("{x} : {y}")) .map(|(x, y)| format!("{x} : {y}"))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
panic!("Object must be present id: {id}\n{objs:#?}") }) panic!("Object must be present id: {id}\n{objs:#?}")
})
.clone() .clone()
} }
pub fn get_interned_string(&self, utf: &str) -> Option<ObjectReference> { pub fn get_interned_string(&self, utf: &str) -> Option<ObjectReference> {
self.strings self.strings
.get(utf) .get(utf)
.map(|e| self.get(*e)) .and_then(|e| self.get(*e).try_into_object_reference().ok())
.and_then(ReferenceKind::into_object_reference)
} }
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); warn!("Manual sidechannel string creation: \n\"{}\"", utf8);
let key = utf8.to_owned(); let key = utf8.to_owned();
let jstr = self.new_object(string_class); let jstr = self.new_object(string_class);
@ -120,26 +206,23 @@ impl ObjectManager {
.flat_map(|e| e.to_le_bytes()) .flat_map(|e| e.to_le_bytes())
.map(|e| e as i8) .map(|e| e as i8)
.collect::<Vec<_>>(); .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".to_string(),
Value::from(Some(ReferenceKind::ArrayReference(barray))), Value::from(Some(ReferenceKind::ArrayReference(barray))),
); );
jstr.lock() jstr.lock()
.unwrap()
.fields .fields
.insert("coder".to_string(), Value::from(1i8)); .insert("coder".to_string(), Value::from(1i8));
jstr.lock() jstr.lock()
.unwrap()
.fields .fields
.insert("hash".to_string(), Value::from(0i32)); .insert("hash".to_string(), Value::from(0i32));
jstr.lock() jstr.lock()
.unwrap()
.fields .fields
.insert("hashIsZero".to_string(), Value::from(false)); .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"); debug_assert!(!self.strings.contains_key(&key), "String already interned");
self.strings.insert(key, id); self.strings.insert(key, id);
jstr jstr
@ -159,14 +242,284 @@ impl ObjectManager {
let class_redefined_count = 0i32; let class_redefined_count = 0i32;
let clazz = self.new_object(class_class); 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("name", Value::from(name));
clakz.set_field("module", Value::from(module)); clakz.set_field("module", Value::from(module));
clakz.set_field("modifiers", Value::from(modifiers)); clakz.set_field("modifiers", Value::from(modifiers));
clakz.set_field("primitive", Value::from(primitive)); clakz.set_field("primitive", Value::from(primitive));
clakz.set_field("classRedefinedCount", Value::from(class_redefined_count)); clakz.set_field("classRedefinedCount", Value::from(class_redefined_count));
} }
clazz 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! { thread_local! {
static XORSHIFT_STATE: Cell<[u32; 4]> = { static XORSHIFT_STATE: Cell<[u32; 4]> = {
Cell::new([ Cell::new([
os_random(), // X: seeded from global Park-Miller RNG os_random(), // X: seeded from global Park-Miller RNG
842502087, // Y: constant 842502087, // Y: constant
0x8767, // Z: constant 0x8767, // Z: constant
273326509, // W: constant 273326509, // W: constant
]) ])
}; };
} }
#[deprecated]
pub fn generate_identity_hash() -> u32 { pub fn generate_identity_hash() -> u32 {
XORSHIFT_STATE.with(|state| { XORSHIFT_STATE.with(|state| {
let mut s = state.get(); let mut s = state.get();
@ -66,4 +67,10 @@ pub fn generate_identity_hash() -> u32 {
state.set(s); state.set(s);
v 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_file::{ClassFile, MethodData, MethodRef};
use crate::class_loader::{ClassLoader, LoaderRef}; 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::{ObjectReference, ReferenceKind};
use crate::objects::object_manager::ObjectManager; use crate::objects::object_manager::ObjectManager;
use crate::value::{Primitive, Value}; use crate::value::{Primitive, Value};
use crate::vm::Vm; use crate::vm::Vm;
use crate::{BaseType, FieldType, Frame, MethodDescriptor, ThreadId}; use crate::{stack_used, BaseType, FieldType, MethodDescriptor, ThreadId};
use deku::DekuError::Incomplete; use deku::DekuError::Incomplete;
use itertools::Itertools; use itertools::Itertools;
use jni::sys::{jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jobject, jshort, JNIEnv}; use jni::sys::{jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jobject, jshort, JNIEnv};
use libffi::low::call; use libffi::low::call;
use libffi::middle::*; 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::any::Any;
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::ops::{Add, Deref}; use std::ops::{Add, Deref};
use std::ptr::null_mut; use std::ptr::null_mut;
use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::{Arc, Mutex, RwLock}; use std::sync::Arc;
use std::vec::IntoIter;
use crate::error::VmError;
use std::thread;
use std::vec::IntoIter;
static INIT_LOGGER: Once = Once::new();
type MethodCallResult = Result<Option<Value>, VmError>; type MethodCallResult = Result<Option<Value>, VmError>;
// Thread-local storage for current thread ID // Thread-local storage for current thread ID
@ -37,7 +43,7 @@ pub struct VmThread {
pub id: ThreadId, pub id: ThreadId,
pub vm: Arc<Vm>, pub vm: Arc<Vm>,
pub loader: Arc<Mutex<ClassLoader>>, 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 gc: Arc<RwLock<ObjectManager>>,
pub jni_env: JNIEnv, pub jni_env: JNIEnv,
} }
@ -80,15 +86,11 @@ impl VmThread {
vm.threads.get(&id).unwrap().clone() vm.threads.get(&id).unwrap().clone()
} }
/// Get or resolve a class, ensuring it and its dependencies are initialized. /*/// Get or resolve a class, ensuring it and its dependencies are initialised.
/// Follows JVM Spec 5.5 for recursive initialization handling. /// Follows JVM Spec 5.5 for recursive initialisation handling.
pub fn get_or_resolve_class(&self, what: &str) -> Result<Arc<RuntimeClass>, VmError> { pub fn get_or_resolve_class(&self, what: &str) -> Result<Arc<RuntimeClass>, VmError> {
// Phase 1: Load the class (short lock) // Phase 1: Load the class (short lock)
let runtime_class = self let runtime_class = self.loader.lock().unwrap().get_or_load(what, None, true)?;
.loader
.lock()
.unwrap()
.get_or_load(what, None)?;
// Phase 2: Collect classes that need initialisation (short lock) // Phase 2: Collect classes that need initialisation (short lock)
let classes_to_init = { let classes_to_init = {
@ -104,26 +106,22 @@ impl VmThread {
} }
Ok(runtime_class) Ok(runtime_class)
} }*/
pub fn get_class(&self, what: &str) -> Result<Arc<RuntimeClass>, VmError> { pub fn get_class(&self, what: &str) -> Result<Arc<RuntimeClass>, VmError> {
self.loader let class = self.loader.lock().get_or_load(what, None)?;
.lock() self.create_mirror_class(&class)?;
.unwrap() Ok(class)
.get_or_load(what, None)
} }
/// Initialize a class following JVM Spec 5.5. /// Initialize a class following JVM Spec 5.5.
/// Handles recursive initialization by tracking which thread is initializing. /// Handles recursive initialization by tracking which thread is initializing.
fn init(&self, class: Arc<RuntimeClass>) -> Result<(), VmError> { pub fn init(&self, class: Arc<RuntimeClass>) -> Result<(), VmError> {
use crate::class::InitState;
use std::thread;
let current_thread = thread::current().id(); let current_thread = thread::current().id();
// Check and update initialization state // Check and update initialization state
{ {
let mut state = class.init_state.lock().unwrap(); let mut state = class.init_state.lock();
match &*state { match &*state {
InitState::Initialized => { InitState::Initialized => {
// Already initialized, nothing to do // 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 // Perform actual initialisation
trace!("Initializing class: {}", class.this_class); trace!("Initializing class: {}", class.this_class);
let result = (|| { let result = (|| {
@ -176,16 +163,24 @@ impl VmThread {
self.init(super_class.clone())?; self.init(super_class.clone())?;
} }
// Run <clinit> if present if !class.access_flags.INTERFACE {
if let Ok(method) = class.find_method("<clinit>", &MethodDescriptor::void()) { for interface in class.interfaces.iter() {
self.execute_method(&class, &method, vec![])?; 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(()) Ok(())
})(); })();
// Update state based on result // Update state based on result
{ {
let mut state = class.init_state.lock().unwrap(); let mut state = class.init_state.lock();
match result { match result {
Ok(_) => { Ok(_) => {
*state = InitState::Initialized; *state = InitState::Initialized;
@ -200,6 +195,85 @@ impl VmThread {
result 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> { pub fn invoke_main(&self, what: &str) -> Result<(), VmError> {
let method_ref = MethodRef { let method_ref = MethodRef {
class: what.to_string(), class: what.to_string(),
@ -212,56 +286,46 @@ impl VmThread {
} }
pub fn invoke(&self, method_reference: MethodRef, args: Vec<Value>) -> MethodCallResult { pub fn invoke(&self, method_reference: MethodRef, args: Vec<Value>) -> MethodCallResult {
if method_reference.class.contains("Unsafe") { if self.gc.read().objects.len() > 2210 {
println!("()") 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 class = self.get_class(&method_reference.class)?;
let method = class.find_method(&method_reference.name, &method_reference.desc).unwrap(); 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) self.execute_method(&class, &method, args)
} }
pub fn invoke_virtual(&self, method_reference: MethodRef, class: Arc<RuntimeClass>, args: Vec<Value>) -> MethodCallResult { pub fn invoke_virtual(
let method = class.find_method(&method_reference.name, &method_reference.desc).unwrap(); &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) 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 { pub fn invoke_native(&self, method: &MethodRef, mut args: Vec<Value>) -> MethodCallResult {
let symbol_name = generate_jni_method_name(method, false); 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") { if symbol_name.contains("Java_java_lang_Class_desiredAssertionStatus0") {
warn!("MAJOR HACK, figure out what is wrong with 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); let name_with_params = generate_jni_method_name(method, true);
self.vm.find_native_method(&name_with_params) 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 // build pointer to native fn
let cp = CodePtr::from_ptr(p); 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(); let mut storage = Vec::new();
trace!("passing {} to native fn", Value::format_vec(&args)); trace!("passing {} to native fn", Value::format_vec(&args));
let deq_args = VecDeque::from(args); let deq_args = VecDeque::from(args);
@ -342,7 +403,7 @@ impl VmThread {
Ok(Some(Value::Reference(None))) Ok(Some(Value::Reference(None)))
} else { } else {
// Look up the object in the ObjectManager // 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); let reference_kind = gc.get(obj_id);
Ok(Some(Value::Reference(Some(reference_kind)))) Ok(Some(Value::Reference(Some(reference_kind))))
} }
@ -358,64 +419,65 @@ impl VmThread {
method: &MethodData, method: &MethodData,
args: Vec<Value>, args: Vec<Value>,
) -> MethodCallResult { ) -> MethodCallResult {
eprintln!("[DEBUG] execute_method self.id = {:?}", self.id); info!("Executing {}", method.name.clone());
warn!("Stack used: {}", stack_used());
let method_ref = MethodRef { let method_ref = MethodRef {
class: class.this_class.clone(), class: class.this_class.clone(),
name: method.name.clone(), name: method.name.clone(),
desc: method.desc.clone(), desc: method.desc.clone(),
}; };
if method.flags.ACC_NATIVE { if method.flags.ACC_NATIVE {
let mut native_args = Vec::new(); let mut native_args = Vec::new();
if method.flags.ACC_STATIC { 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.push(Value::Reference(Some(jclass)));
} }
native_args.extend(args); 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( let mut frame = Frame::new(
class.clone(), class.clone(),
method_ref, method_ref.clone(),
method.code.clone().unwrap(), method.code.clone().unwrap(),
class.constant_pool.clone(), class.constant_pool.clone(),
args, args,
self.vm.clone(), self.vm.clone(),
method.line_number_table.clone(), method.line_number_table.clone(),
); );
let frame = Arc::new(Mutex::new(frame)); // let frame = Arc::new(ReentrantMutex::new(frame));
self.frame_stack.lock().unwrap().push(frame.clone()); self.frame_stack.lock().push(frame.clone());
eprintln!("[DEBUG] pushed frame for {}.{}, stack depth now: {}", // println!("Invoke method: {}.{}", &method_ref.class, &method_ref.name);
class.this_class, method.name, let result = frame.execute();
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());
if result.is_ok() { 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 result
} }
pub fn print_stack_trace(&self) { // pub fn print_stack_trace(&mut self) {
let guard = self.frame_stack.lock().unwrap(); // // Get a lock on the frame stack
// Reverse - most recent frame first (like Java does) // let guard = self.frame_stack.lock();
for frame in guard.iter().rev() { // // Reverse - most recent frame first (like Java does)
let frame = frame.lock().unwrap(); // for frame_arc in guard.iter().rev() {
let method = &frame.method_ref; // // Get a lock on the individual frame
// Internal format uses '/', Java stack traces use '.' // let frame = frame_arc.lock();
let class_name = method.class.replace("/", "."); // 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), // match (&frame.class.source_file, &frame.current_line_number()) {
(Some(file), None) => eprintln!("\tat {}.{}({})", class_name, method.name, file), // (Some(file), Some(line)) => {
_ => eprintln!("\tat {}.{}(Unknown Source)", class_name, method.name), // 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>( fn build_args<'a>(
@ -429,9 +491,7 @@ fn build_args<'a>(
// Slot 1: this (instance) or class (static) — first param either way // Slot 1: this (instance) or class (static) — first param either way
let receiver = params.pop_front(); let receiver = params.pop_front();
let receiver_id = match receiver { let receiver_id = match receiver {
Some(Value::Reference(Some(ReferenceKind::ObjectReference(ref_kind)))) => { Some(Value::Reference(Some(ref_kind))) => ref_kind.id(),
ref_kind.lock().unwrap().id
} // however you get the u32 ID
Some(Value::Reference(None)) => 0, // null Some(Value::Reference(None)) => 0, // null
_ => panic!("first arg must be reference"), _ => 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; let id = x.map(|r| r.id()).unwrap_or(0) as jobject;
storage.push(Box::new(id)); 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 /// 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 /// 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 { pub fn intern_string(&self, utf: &str) -> ObjectReference {
// Fast path: read lock if let Some(existing) = self.gc.read().get_interned_string(utf) {
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) {
return existing; return existing;
} }
let string_class = self.get_class("java/lang/String").unwrap(); let string_class = self.get_class("java/lang/String").unwrap();
let byte_array_class = self.get_class("[B").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::array::ArrayReference;
use crate::objects::object::{ObjectReference, ReferenceKind}; use crate::objects::object::{ObjectReference, ReferenceKind};
use crate::{BaseType, FieldType}; use crate::{BaseType, FieldType};
@ -6,8 +7,8 @@ use dashmap::DashMap;
use jni::sys::{jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jshort}; use jni::sys::{jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jshort};
use log::trace; use log::trace;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
use std::mem::{size_of, size_of_val};
use std::ops::Deref; use std::ops::Deref;
use crate::error::VmError;
/// A reference-counted, thread-safe pointer to an Object. /// A reference-counted, thread-safe pointer to an Object.
@ -24,7 +25,7 @@ pub enum Value {
Padding, Padding,
} }
#[derive( Clone, Default)] #[derive(Clone, Default)]
pub struct OperandStack(Vec<Value>); pub struct OperandStack(Vec<Value>);
impl OperandStack { impl OperandStack {
@ -33,9 +34,7 @@ impl OperandStack {
} }
pub fn with_capacity(max_stack: usize) -> Self { pub fn with_capacity(max_stack: usize) -> Self {
let backing = Vec::with_capacity(max_stack); let backing = Vec::with_capacity(max_stack);
Self { Self { 0: backing }
0: backing
}
} }
pub fn push(&mut self, value: Value) { pub fn push(&mut self, value: Value) {
@ -43,12 +42,14 @@ impl OperandStack {
} }
pub fn pop(&mut self) -> Result<Value, VmError> { pub fn pop(&mut self) -> Result<Value, VmError> {
self.0.pop() self.0
.pop()
.ok_or_else(|| VmError::StackError("Stack underflow".to_string())) .ok_or_else(|| VmError::StackError("Stack underflow".to_string()))
} }
pub fn peek(&self) -> Result<&Value, VmError> { pub fn peek(&self) -> Result<&Value, VmError> {
self.0.last() self.0
.last()
.ok_or_else(|| VmError::StackError("Stack underflow on peek".to_string())) .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) /// 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> { 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) .checked_sub(n)
.ok_or_else(|| VmError::StackError("Stack underflow on pop_n".to_string()))?; .ok_or_else(|| VmError::StackError("Stack underflow on pop_n".to_string()))?;
Ok(self.0.drain(start..).collect()) Ok(self.0.drain(start..).collect())
@ -90,13 +93,13 @@ impl<'a> IntoIterator for &'a OperandStack {
} }
#[derive(Clone, Default)] #[derive(Clone, Default)]
pub struct LocalVariables { pub struct LocalVariables {
inner: Vec<Value> inner: Vec<Value>,
} }
impl LocalVariables { impl LocalVariables {
pub fn with_capacity(max_locals: usize) -> Self { pub fn with_capacity(max_locals: usize) -> Self {
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 { pub fn from_args(args: Vec<Value>, max_locals: usize) -> Self {
@ -130,11 +133,11 @@ impl LocalVariables {
val 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)) 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 self.inner
.iter() .iter()
.enumerate() .enumerate()
@ -151,10 +154,18 @@ impl std::fmt::Debug for LocalVariables {
} }
} }
impl Value { impl Value {
pub const NULL: Value = Value::Reference(None); 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 { pub fn format_vec(values: &Vec<Value>) -> String {
let fmt = values let fmt = values
.iter() .iter()
@ -166,13 +177,59 @@ impl Value {
} }
pub fn is_wide(&self) -> bool { 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 { match self {
Value::Reference(Some(kind)) => Some(kind.clone()), Value::Reference(Some(kind)) => Ok(kind.clone()),
_ => None, _ => 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), 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 { impl Display for Primitive {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self { match self {
@ -270,7 +342,9 @@ impl Display for Value {
Value::Primitive(prim) => write!(f, "{}", prim), Value::Primitive(prim) => write!(f, "{}", prim),
Value::Reference(Some(obj)) => write!(f, "Ref({})", obj), Value::Reference(Some(obj)) => write!(f, "Ref({})", obj),
Value::Reference(None) => write!(f, "null"), 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 { Some(kind) => match kind {
ReferenceKind::ObjectReference(found_ref) => { ReferenceKind::ObjectReference(found_ref) => {
if let FieldType::ClassType(expected) = other { 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) expected.eq(&found)
} else { } else {
false 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_file::{ClassFlags, MethodRef};
use crate::class_loader::ClassLoader; 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::objects::object_manager::ObjectManager;
use crate::thread::VmThread; use crate::thread::VmThread;
use crate::{MethodDescriptor, ThreadId}; use crate::{MethodDescriptor, ThreadId};
use dashmap::DashMap; use dashmap::DashMap;
use imp::Library; use imp::Library;
use std::sync::{Arc, Mutex, RwLock}; use log::trace;
use crate::objects::object::ReferenceKind; use parking_lot::{Mutex, RwLock};
use crate::error::VmError; use std::sync::Arc;
// struct AbstractObject<'a> {} // struct AbstractObject<'a> {}
pub struct Vm { pub struct Vm {
@ -27,6 +30,7 @@ pub struct Vm {
pub native_methods: DashMap<String, *const c_void>, pub native_methods: DashMap<String, *const c_void>,
pub native_libraries: DashMap<String, Library>, pub native_libraries: DashMap<String, Library>,
pub gc: Arc<RwLock<ObjectManager>>, pub gc: Arc<RwLock<ObjectManager>>,
pub safent: RwLock<UnsafeSupport>,
} }
impl Vm { impl Vm {
@ -39,6 +43,7 @@ impl Vm {
native_methods: DashMap::new(), native_methods: DashMap::new(),
native_libraries: DashMap::new(), native_libraries: DashMap::new(),
gc: Default::default(), gc: Default::default(),
safent: Default::default(),
}); });
// Create main thread // Create main thread
@ -58,13 +63,8 @@ impl Vm {
} }
pub fn find_native_method(&self, name: &str) -> Option<*const c_void> { 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) { if let Some(registered) = self.native_methods.get(name) {
trace!("native {} was already registered", name);
return Some(registered.clone()); return Some(registered.clone());
} }
@ -103,11 +103,15 @@ impl Vm {
"java/lang/Thread", "java/lang/Thread",
"java/lang/Module", "java/lang/Module",
//unsafe internal? //unsafe internal?
// "java/lang/reflect/Method", "jdk/internal/misc/UnsafeConstants",
// "java/lang/ref/Finalizer", "java/lang/reflect/Method",
// "jdk/internal/misc/UnsafeConstants" "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![ let prims = vec![
("byte", "B"), ("byte", "B"),
("char", "C"), ("char", "C"),
@ -116,37 +120,45 @@ impl Vm {
("int", "I"), ("int", "I"),
("long", "J"), ("long", "J"),
("short", "S"), ("short", "S"),
("boolean", "Z") ("boolean", "Z"),
("void", "V"),
]; ];
let thread = self.threads.get(&self.main_thread_id).unwrap(); let thread = self.threads.get(&self.main_thread_id).unwrap();
for prim in prims { for prim in prims {
let klass = let klass = self.loader.lock().primitive_class(prim.0);
self.loader.lock().unwrap().primitive_class(prim.0);
let class_class = thread.get_class("java/lang/Class")?; let class_class = thread.get_class("java/lang/Class")?;
let name_obj = thread.intern_string(&prim.0); let name_obj = thread.intern_string(&prim.0);
let flags = ClassFlags::from(1041u16); 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(), class_class.clone(),
Some(ReferenceKind::ObjectReference(name_obj)), Some(ReferenceKind::ObjectReference(name_obj)),
None, None,
flags, 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_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, class_class,
Some(ReferenceKind::ObjectReference(arr_name_obj)), Some(ReferenceKind::ObjectReference(arr_name_obj)),
None, None,
flags, 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 { let phase1ref = MethodRef {
@ -155,6 +167,7 @@ impl Vm {
desc: MethodDescriptor::void(), desc: MethodDescriptor::void(),
}; };
thread.invoke(phase1ref, Vec::new())?; thread.invoke(phase1ref, Vec::new())?;
panic!("HOLY FUCKING SHIT, DID WE PHASE 1!?");
Ok(()) Ok(())
} }

View File

@ -11,4 +11,5 @@ log = "0.4.28"
[lib] [lib]
name = "roast_vm" 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 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::array::ArrayReference;
use roast_vm_core::objects::ReferenceKind; use roast_vm_core::objects::{Reference, ReferenceKind};
use roast_vm_core::value::Value; use roast_vm_core::value::Value;
use crate::get_thread; use roast_vm_core::{BaseType, FieldType};
use std::ptr;
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub unsafe extern "system" fn Java_java_lang_Class_getPrimitiveClass<'local>( 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 thread = &*get_thread(env);
let rust_string = { let rust_string = {
let gc = thread.gc.read().unwrap(); let gc = thread.gc.read();
let obj_id = name as u32; 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, _ => return 0 as jclass,
}; };
let obj = obj_ref.lock().unwrap(); let obj = obj_ref.lock();
let field_ref = FieldRef{ let field_ref = FieldRef {
class: "java/lang/String".to_string(), class: "java/lang/String".to_string(),
name: "value".to_string(), name: "value".to_string(),
desc: FieldType::ArrayType(Box::new(FieldType::Base(BaseType::Byte))), 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; 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(); 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 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 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)] #![allow(non_snake_case)]
mod CDS;
mod class; mod class;
mod object;
mod misc_unsafe; mod misc_unsafe;
mod object;
mod reflection;
mod runtime;
mod system; mod system;
use jni::objects::{JClass, JObject, JString}; use jni::objects::{JClass, JObject, JString};
use jni::strings::JNIString; use jni::strings::JNIString;
use jni::sys::{jclass, jlong, jobject, jobjectArray}; use jni::sys::{jclass, jlong, jobject, jobjectArray};
use jni::{JNIEnv, NativeMethod}; 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::array::ArrayReference;
use roast_vm_core::objects::object::ObjectReference; use roast_vm_core::objects::object::ObjectReference;
use roast_vm_core::objects::ReferenceKind; use roast_vm_core::objects::ReferenceKind;
use roast_vm_core::VmThread; use roast_vm_core::VmThread;
use std::ffi::c_void;
#[unsafe(no_mangle)] use std::io::Write;
pub extern "system" fn current_time_millis<'local>( use std::time::{SystemTime, UNIX_EPOCH};
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
}
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub extern "system" fn Java_org_example_MockIO_print<'local>( 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!") // println!("Yeetus bageetus! Im printing from rust!")
} }
#[unsafe(no_mangle)] /*#[unsafe(no_mangle)]
pub extern "system" fn Java_org_example_MockIO_registerNatives<'local>( pub extern "system" fn Java_org_example_MockIO_registerNatives<'local>(
mut env: JNIEnv<'local>, mut env: JNIEnv<'local>,
jclass: JClass<'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) // env.register_native_methods(library_class, &library_methods)
// .expect("failed to register method"); // .expect("failed to register method");
} }*/
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub extern "system" fn findEntry0<'local>( 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 { 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> { 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 { let ReferenceKind::ObjectReference(obj_ref) = gc.get(obj as u32) else {
return None; return None;
}; };
@ -164,13 +154,21 @@ fn resolve_object(thread: &VmThread, obj: jobject) -> Option<ObjectReference> {
} }
fn resolve_array(thread: &VmThread, obj: jobject) -> Option<ArrayReference> { 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 { let ReferenceKind::ArrayReference(arr_ref) = gc.get(obj as u32) else {
return None; return None;
}; };
Some(arr_ref.clone()) 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)] #[unsafe(no_mangle)]
pub extern "system" fn Java_jdk_internal_util_SystemProps_00024Raw_vmProperties<'local>( pub extern "system" fn Java_jdk_internal_util_SystemProps_00024Raw_vmProperties<'local>(
mut env: JNIEnv<'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 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)] #[unsafe(no_mangle)]
pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_registerNatives( 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, obj: jclass,
) { ) {
//no op //no op
() println!("Unsafe_registerNatives is NO OP")
} }
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_arrayBaseOffset0( pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_arrayBaseOffset0(
env: *mut JNIEnv, _env: *mut JNIEnv,
obj: jclass, _unsafe_obj: jobject,
_class: jclass,
) -> jint { ) -> jint {
warn!("arrayBaseOffset0 currently just returning 0"); warn!("arrayBaseOffset0 currently just returning 0");
0 0
@ -23,8 +29,545 @@ pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_arrayBaseOffset0(
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_arrayIndexScale0( pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_arrayIndexScale0(
env: *mut JNIEnv, env: *mut JNIEnv,
obj: jclass, _unsafe_obj: jobject,
class: jclass,
) -> jint { ) -> 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 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;
use crate::{get_thread, resolve_object}; use jni::sys::{jint, jobject, JNIEnv};
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub unsafe extern "system" fn Java_java_lang_Object_hashCode( pub unsafe extern "system" fn Java_java_lang_Object_hashCode(
env: *mut JNIEnv, env: *mut JNIEnv,
obj: jobject, obj: jobject,
) -> jint { ) -> jint {
let thread = &*get_thread(env);
obj as u32 as i32 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 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)] #[unsafe(no_mangle)]
pub unsafe extern "system" fn Java_java_lang_System_arraycopy( 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(); let dst_len = dst_arr.len();
// Bounds checking // Bounds checking
if src_pos < 0 || dst_pos < 0 || length < 0 if src_pos < 0
|| src_pos.checked_add(length).map_or(true, |end| end > src_len) || dst_pos < 0
|| dst_pos.checked_add(length).map_or(true, |end| end > dst_len) || 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!"); panic!("Array index out of bounds!");
// throw_exception(env, "java/lang/ArrayIndexOutOfBoundsException", None); // throw_exception(env, "java/lang/ArrayIndexOutOfBoundsException", None);
return; return;
} }
dst_arr
dst_arr.copy_from(&src_arr, src_pos, dst_pos, length).expect("Array copy error hell"); .copy_from(&src_arr, src_pos, dst_pos, length)
.expect("Array copy error hell");
// Type compatibility check + copy // 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 edition = "2024"
style_edition = "2024"
newline_style = "Auto" hard_tabs = true
indent_style = "Block" hex_literal_case = "Upper"
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