Reflection for charsets
This commit is contained in:
parent
fcfc1d5a71
commit
a6f6507a85
3
.gitignore
vendored
3
.gitignore
vendored
@ -20,7 +20,8 @@ Cargo.lock
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
.idea/
|
||||
#.idea/
|
||||
data
|
||||
lib
|
||||
output
|
||||
headers
|
||||
10
.idea/.gitignore
generated
vendored
Normal file
10
.idea/.gitignore
generated
vendored
Normal 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
55
.idea/codeStyles/Project.xml
generated
Normal 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
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal 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
14
.idea/discord.xml
generated
Normal 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>
|
||||
6
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
6
.idea/inspectionProfiles/Project_Default.xml
generated
Normal 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
10
.idea/misc.xml
generated
Normal 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
8
.idea/modules.xml
generated
Normal 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
17
.idea/roast-vm.iml
generated
Normal 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
6
.idea/vcs.xml
generated
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
14
Cargo.toml
14
Cargo.toml
@ -17,3 +17,17 @@ libffi = "5.0.0"
|
||||
jni = "0.21.1"
|
||||
roast-vm-sys = { path = "crates/roast-vm-sys", version = "0.1.0" }
|
||||
roast-vm-core = { path = "crates/core", version = "0.1.0" }
|
||||
|
||||
[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
|
||||
@ -16,6 +16,8 @@ libffi = { workspace = true }
|
||||
jni = { workspace = true }
|
||||
itertools = { workspace = true }
|
||||
colored = "3.0.0"
|
||||
parking_lot = "0.12"
|
||||
cesu8 = "1.1.0"
|
||||
|
||||
[build-dependencies]
|
||||
bindgen = "0.72.1"
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
use crate::class_file::constant_pool::ConstantPoolExt;
|
||||
use crate::class_file::{ClassFile, Constant, ConstantPoolEntry};
|
||||
use deku::DekuContainerRead;
|
||||
use deku::{DekuContainerRead, DekuError, DekuReader};
|
||||
use deku_derive::DekuRead;
|
||||
use log::trace;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::ops::Deref;
|
||||
use deku::ctx::Endian;
|
||||
|
||||
#[derive(Clone, PartialEq, Debug, DekuRead)]
|
||||
#[deku(ctx = "_endian: deku::ctx::Endian", endian = "big")]
|
||||
@ -60,6 +61,72 @@ pub enum ArrayType {
|
||||
T_LONG,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
pub struct LookupSwitchData {
|
||||
pub default: i32,
|
||||
pub pairs: Vec<(i32, i32)>, // (match, offset)
|
||||
}
|
||||
|
||||
impl<'a> DekuReader<'a, (Endian, u16)> for LookupSwitchData {
|
||||
fn from_reader_with_ctx<R: deku::no_std_io::Read + deku::no_std_io::Seek>(
|
||||
reader: &mut deku::reader::Reader<R>,
|
||||
(_endian, byte_offset): (Endian, u16),
|
||||
) -> Result<Self, DekuError> {
|
||||
let pos_after_opcode = (byte_offset + 1) as usize;
|
||||
let padding = (4 - (pos_after_opcode % 4)) % 4;
|
||||
|
||||
for _ in 0..padding {
|
||||
u8::from_reader_with_ctx(reader, ())?;
|
||||
}
|
||||
|
||||
let default = i32::from_reader_with_ctx(reader, Endian::Big)?;
|
||||
let npairs = i32::from_reader_with_ctx(reader, Endian::Big)?;
|
||||
|
||||
let mut pairs = Vec::with_capacity(npairs as usize);
|
||||
for _ in 0..npairs {
|
||||
let match_val = i32::from_reader_with_ctx(reader, Endian::Big)?;
|
||||
let offset = i32::from_reader_with_ctx(reader, Endian::Big)?;
|
||||
pairs.push((match_val, offset));
|
||||
}
|
||||
|
||||
Ok(LookupSwitchData { default, pairs })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
pub struct TableSwitchData {
|
||||
pub default: i32,
|
||||
pub low: i32,
|
||||
pub high: i32,
|
||||
pub offsets: Vec<i32>,
|
||||
}
|
||||
|
||||
impl<'a> DekuReader<'a, (Endian, u16)> for TableSwitchData {
|
||||
fn from_reader_with_ctx<R: deku::no_std_io::Read + deku::no_std_io::Seek>(
|
||||
reader: &mut deku::reader::Reader<R>,
|
||||
(_endian, byte_offset): (Endian, u16),
|
||||
) -> Result<Self, DekuError> {
|
||||
let pos_after_opcode = (byte_offset + 1) as usize;
|
||||
let padding = (4 - (pos_after_opcode % 4)) % 4;
|
||||
|
||||
for _ in 0..padding {
|
||||
u8::from_reader_with_ctx(reader, ())?;
|
||||
}
|
||||
|
||||
let default = i32::from_reader_with_ctx(reader, Endian::Big)?;
|
||||
let low = i32::from_reader_with_ctx(reader, Endian::Big)?;
|
||||
let high = i32::from_reader_with_ctx(reader, Endian::Big)?;
|
||||
|
||||
let count = (high - low + 1) as usize;
|
||||
let mut offsets = Vec::with_capacity(count);
|
||||
for _ in 0..count {
|
||||
offsets.push(i32::from_reader_with_ctx(reader, Endian::Big)?);
|
||||
}
|
||||
|
||||
Ok(TableSwitchData { default, low, high, offsets })
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ArrayType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let str = match self {
|
||||
|
||||
@ -3,12 +3,14 @@ use crate::class_file::{
|
||||
ClassFile, ClassFlags, ConstantPoolEntry, FieldData, FieldInfo, FieldRef, MethodData,
|
||||
MethodInfo, MethodRef,
|
||||
};
|
||||
use crate::error::VmError;
|
||||
use crate::{FieldType, MethodDescriptor};
|
||||
use log::trace;
|
||||
use parking_lot::Mutex;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::sync::{Arc, Mutex, OnceLock, OnceState};
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::sync::{Arc, OnceLock, OnceState};
|
||||
use std::thread::ThreadId;
|
||||
use crate::error::VmError;
|
||||
|
||||
/// JVM Spec 5.5: Initialization states for a class
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
@ -33,6 +35,7 @@ pub struct RuntimeClass {
|
||||
pub fields: Vec<FieldData>,
|
||||
pub methods: Vec<MethodData>,
|
||||
pub mirror: OnceLock<u32>,
|
||||
pub mirror_in_progress: AtomicBool,
|
||||
/// Thread-safe initialization state (JVM Spec 5.5)
|
||||
pub init_state: Mutex<InitState>,
|
||||
pub super_classes: Vec<Arc<RuntimeClass>>,
|
||||
@ -55,11 +58,15 @@ impl PartialEq<Self> for RuntimeClass {
|
||||
impl Eq for RuntimeClass {}
|
||||
|
||||
impl RuntimeClass {
|
||||
pub fn mirror(&self) -> u32 {
|
||||
*self.mirror.get().unwrap()
|
||||
}
|
||||
pub fn find_method(&self, name: &str, desc: &MethodDescriptor) -> Result<&MethodData, VmError> {
|
||||
trace!(
|
||||
"Finding in {}, method Name: {name} desc:{desc},",
|
||||
self.this_class
|
||||
);
|
||||
|
||||
if let Some(method) = self.methods.iter().find(|e| {
|
||||
let name_match = e.name.eq(name);
|
||||
let param_match = desc.parameters == e.desc.parameters;
|
||||
@ -100,7 +107,10 @@ impl RuntimeClass {
|
||||
}
|
||||
|
||||
pub fn is_assignable_into(&self, into: Arc<RuntimeClass>) -> bool {
|
||||
if self.eq(&*into) || self.super_classes.contains(&into) || self.super_interfaces.contains(&into) {
|
||||
if self.eq(&*into)
|
||||
|| self.super_classes.contains(&into)
|
||||
|| self.super_interfaces.contains(&into)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// Array covariance: both must be arrays, then check component types
|
||||
@ -117,6 +127,36 @@ impl RuntimeClass {
|
||||
}
|
||||
|
||||
fn is_primitive_class(&self) -> bool {
|
||||
matches!(self.this_class.as_str(), "byte"|"char"|"double"|"float"|"int"|"long"|"short"|"boolean")
|
||||
matches!(
|
||||
self.this_class.as_str(),
|
||||
"byte" | "char" | "double" | "float" | "int" | "long" | "short" | "boolean"
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn has_default_method(&self) -> bool {
|
||||
self.methods
|
||||
.iter()
|
||||
.any(|method| !method.flags.ACC_ABSTRACT && !method.flags.ACC_STATIC)
|
||||
&& self.access_flags.INTERFACE
|
||||
}
|
||||
|
||||
pub fn implements(&self, interface: &str) -> bool {
|
||||
self.super_interfaces
|
||||
.iter()
|
||||
.any(|e| e.this_class == interface)
|
||||
}
|
||||
|
||||
pub fn get_constructor_ref(&self, idx: usize) -> Result<&MethodData, VmError> {
|
||||
let constructors = self
|
||||
.methods
|
||||
.iter()
|
||||
.filter(|x| x.name.eq("<init>"))
|
||||
.collect::<Vec<_>>();
|
||||
constructors
|
||||
.get(idx)
|
||||
.ok_or(VmError::InvariantError(
|
||||
"class does not have this constructor, consider inehritance impl".to_string(),
|
||||
))
|
||||
.copied()
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,18 +1,20 @@
|
||||
use crate::attributes::{Attribute, AttributeInfo, CodeAttribute, LineNumberTableEntry};
|
||||
use crate::class::RuntimeClass;
|
||||
use crate::class_file::constant_pool::{ConstantPoolError, ConstantPoolExt, ConstantPoolOwned};
|
||||
use crate::instructions::Ops;
|
||||
use crate::value::Value;
|
||||
use crate::{BaseType, FieldType, MethodDescriptor};
|
||||
use deku::ctx::Endian::Big;
|
||||
use deku::{DekuContainerRead, DekuError};
|
||||
use deku::{DekuContainerRead, DekuContainerWrite, DekuError};
|
||||
use deku_derive::{DekuRead, DekuWrite};
|
||||
use itertools::Itertools;
|
||||
use parking_lot::Mutex;
|
||||
use std::borrow::Cow;
|
||||
use std::fmt;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::ops::Deref;
|
||||
use std::str::Chars;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Debug, PartialEq, DekuRead)]
|
||||
#[deku(magic = b"\xCA\xFE\xBA\xBE", endian = "big")]
|
||||
@ -493,7 +495,7 @@ pub fn pool_get_string(constant_pool: &[ConstantPoolEntry], index: u16) -> Optio
|
||||
fn read_bytecode_with_offsets<R: deku::no_std_io::Read + deku::no_std_io::Seek>(
|
||||
bytes: u32,
|
||||
reader: &mut deku::reader::Reader<R>,
|
||||
endian: deku::ctx::Endian,
|
||||
_endian: deku::ctx::Endian,
|
||||
) -> Result<Vec<(u16, Ops)>, DekuError> {
|
||||
use deku::DekuReader;
|
||||
|
||||
@ -507,7 +509,7 @@ fn read_bytecode_with_offsets<R: deku::no_std_io::Read + deku::no_std_io::Seek>(
|
||||
let start_pos = reader.bits_read;
|
||||
|
||||
// Parse the next Op
|
||||
let op = Ops::from_reader_with_ctx(reader, endian)?;
|
||||
let op = Ops::from_reader_with_ctx(reader, byte_offset)?;
|
||||
|
||||
let end_pos = reader.bits_read;
|
||||
let op_bytes = ((end_pos - start_pos) / 8) as usize;
|
||||
@ -654,6 +656,7 @@ pub struct MethodRef {
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MethodData {
|
||||
pub name: String,
|
||||
pub class: String,
|
||||
pub desc: MethodDescriptor,
|
||||
pub code: Option<CodeAttribute>,
|
||||
pub flags: MethodFlags,
|
||||
@ -665,6 +668,16 @@ pub struct MethodData {
|
||||
// pub method_parameters: Option<_>
|
||||
}
|
||||
|
||||
impl From<MethodData> for MethodRef {
|
||||
fn from(value: MethodData) -> Self {
|
||||
Self {
|
||||
class: value.class,
|
||||
name: value.name,
|
||||
desc: value.desc,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FieldRef {
|
||||
pub class: String,
|
||||
@ -734,6 +747,13 @@ impl From<u16> for ClassFlags {
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u16> for ClassFlags {
|
||||
fn into(self) -> u16 {
|
||||
let bytes = self.to_bytes().unwrap();
|
||||
u16::from_be_bytes([bytes[0], bytes[1]])
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[derive(Debug, PartialEq, DekuRead, DekuWrite)]
|
||||
pub struct ModuleFlags {
|
||||
@ -754,27 +774,34 @@ impl From<u16> for ModuleFlags {
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u16> for ModuleFlags {
|
||||
fn into(self) -> u16 {
|
||||
let bytes = self.to_bytes().unwrap();
|
||||
u16::from_be_bytes([bytes[0], bytes[1]])
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[derive(Debug, PartialEq, DekuRead, DekuWrite)]
|
||||
pub struct FieldFlags {
|
||||
#[deku(bits = 1, pad_bits_before = "1")]
|
||||
ACC_ENUM: bool,
|
||||
pub ACC_ENUM: bool,
|
||||
#[deku(bits = 1, pad_bits_before = "1")]
|
||||
ACC_SYNTHETIC: bool,
|
||||
pub ACC_SYNTHETIC: bool,
|
||||
#[deku(bits = 1, pad_bits_before = "4")]
|
||||
ACC_TRANSIENT: bool,
|
||||
pub ACC_TRANSIENT: bool,
|
||||
#[deku(bits = 1)]
|
||||
ACC_VOLATILE: bool,
|
||||
pub ACC_VOLATILE: bool,
|
||||
#[deku(bits = 1, pad_bits_before = "1")]
|
||||
ACC_FINAL: bool,
|
||||
pub ACC_FINAL: bool,
|
||||
#[deku(bits = 1)]
|
||||
ACC_STATIC: bool,
|
||||
pub ACC_STATIC: bool,
|
||||
#[deku(bits = 1)]
|
||||
ACC_PROTECTED: bool,
|
||||
pub ACC_PROTECTED: bool,
|
||||
#[deku(bits = 1)]
|
||||
ACC_PRIVATE: bool,
|
||||
pub ACC_PRIVATE: bool,
|
||||
#[deku(bits = 1)]
|
||||
ACC_PUBLIC: bool,
|
||||
pub ACC_PUBLIC: bool,
|
||||
}
|
||||
|
||||
impl From<u16> for FieldFlags {
|
||||
@ -784,6 +811,13 @@ impl From<u16> for FieldFlags {
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u16> for FieldFlags {
|
||||
fn into(self) -> u16 {
|
||||
let bytes = self.to_bytes().unwrap();
|
||||
u16::from_be_bytes([bytes[0], bytes[1]])
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[derive(Debug, PartialEq, DekuRead, DekuWrite, Clone)]
|
||||
pub struct MethodFlags {
|
||||
@ -820,6 +854,13 @@ impl From<u16> for MethodFlags {
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u16> for MethodFlags {
|
||||
fn into(self) -> u16 {
|
||||
let bytes = self.to_bytes().unwrap();
|
||||
u16::from_be_bytes([bytes[0], bytes[1]])
|
||||
}
|
||||
}
|
||||
|
||||
//yoinked because im monkled
|
||||
impl MethodDescriptor {
|
||||
/// Parses a method descriptor as specified in the JVM specs:
|
||||
|
||||
@ -5,13 +5,14 @@ use crate::class_file::{
|
||||
ConstantClassInfo, ConstantDynamicInfo, ConstantFieldrefInfo, ConstantInterfaceMethodrefInfo,
|
||||
ConstantInvokeDynamicInfo, ConstantMethodHandleInfo, ConstantMethodTypeInfo,
|
||||
ConstantMethodrefInfo, ConstantModuleInfo, ConstantNameAndTypeInfo, ConstantPackageInfo,
|
||||
ConstantPoolEntry, ConstantStringInfo, ConstantUtf8Info, DescParseError, FieldInfo, FieldRef
|
||||
, MethodRef,
|
||||
ConstantPoolEntry, ConstantStringInfo, ConstantUtf8Info, DescParseError, FieldInfo, FieldRef,
|
||||
MethodRef,
|
||||
};
|
||||
use crate::error::VmError;
|
||||
use crate::{pool_get_impl, FieldType, MethodDescriptor};
|
||||
use cesu8::{from_java_cesu8, Cesu8DecodingError};
|
||||
use deku::DekuContainerRead;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use crate::error::VmError;
|
||||
|
||||
pub type ConstantPoolSlice = [ConstantPoolEntry];
|
||||
pub type ConstantPoolOwned = Vec<ConstantPoolEntry>;
|
||||
@ -35,8 +36,8 @@ pub trait ConstantPoolExt: ConstantPoolGet {
|
||||
//
|
||||
fn get_string(&self, index: u16) -> Result<String, ConstantPoolError> {
|
||||
let cp_entry = self.get_utf8_info(index)?;
|
||||
|
||||
String::from_utf8(cp_entry.bytes.clone()).map_err(|e| e.to_string().into())
|
||||
let cow = from_java_cesu8(&cp_entry.bytes)?;
|
||||
Ok(cow.into())
|
||||
}
|
||||
//
|
||||
// fn get_field(&self, index: u16) -> Result<&ConstantFieldrefInfo, ()> {
|
||||
@ -221,19 +222,38 @@ impl ConstantPoolGet for [ConstantPoolEntry] {}
|
||||
|
||||
impl Display for ConstantPoolError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
self.0.fmt(f)
|
||||
match self {
|
||||
ConstantPoolError::Generic(msg) => write!(f, "{}", msg),
|
||||
ConstantPoolError::DescriptorParseError(err) => {
|
||||
write!(f, "Descriptor parse error: {:?}", err)
|
||||
}
|
||||
ConstantPoolError::Cesu8DecodingError(err) => {
|
||||
write!(f, "CESU-8 decoding error: {}", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Debug)]
|
||||
pub struct ConstantPoolError(String);
|
||||
pub enum ConstantPoolError {
|
||||
Generic(String),
|
||||
DescriptorParseError(DescParseError),
|
||||
Cesu8DecodingError(Cesu8DecodingError),
|
||||
}
|
||||
|
||||
impl From<String> for ConstantPoolError {
|
||||
fn from(value: String) -> Self {
|
||||
Self(value)
|
||||
Self::Generic(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DescParseError> for ConstantPoolError {
|
||||
fn from(value: DescParseError) -> Self {
|
||||
value.to_string().into()
|
||||
Self::DescriptorParseError(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Cesu8DecodingError> for ConstantPoolError {
|
||||
fn from(value: Cesu8DecodingError) -> Self {
|
||||
Self::Cesu8DecodingError(value)
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,16 +3,17 @@ use crate::bimage::Bimage;
|
||||
use crate::class::{InitState, RuntimeClass};
|
||||
use crate::class_file::constant_pool::{ConstantPoolExt, ConstantPoolGet};
|
||||
use crate::class_file::{ClassFile, ClassFlags, FieldData, FieldFlags, MethodData, MethodFlags};
|
||||
use crate::error::VmError;
|
||||
use crate::{FieldType, MethodDescriptor};
|
||||
use dashmap::DashMap;
|
||||
use deku::DekuContainerRead;
|
||||
use log::warn;
|
||||
use parking_lot::Mutex;
|
||||
use std::collections::HashSet;
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::{Arc, Mutex, OnceLock};
|
||||
use crate::error::VmError;
|
||||
use std::sync::{Arc, OnceLock};
|
||||
|
||||
pub type LoaderRef = Arc<Mutex<ClassLoader>>;
|
||||
|
||||
@ -83,9 +84,9 @@ pub fn resolve_path(what: &str) -> Result<(PathBuf, String), String> {
|
||||
/// ```
|
||||
#[derive(Default)]
|
||||
pub struct ClassLoader {
|
||||
classes: DashMap<(String, LoaderId), Arc<RuntimeClass>>,
|
||||
pub(crate) classes: DashMap<(String, LoaderId), Arc<RuntimeClass>>,
|
||||
bimage: Bimage,
|
||||
pub needs_init: Vec<Arc<RuntimeClass>>,
|
||||
// pub needs_init: Vec<Arc<RuntimeClass>>,
|
||||
}
|
||||
|
||||
type LoaderId = Option<u32>;
|
||||
@ -104,7 +105,7 @@ impl ClassLoader {
|
||||
self.classes
|
||||
.iter()
|
||||
.map(|x| x.value().clone())
|
||||
.find(|e| *e.mirror.wait() == id)
|
||||
.find(|e| e.mirror.get().is_some_and(|&m| m == id))
|
||||
}
|
||||
|
||||
/// Retrieves an `Arc<ClassFile>` from the internal storage, or attempts to load it if not already present.
|
||||
@ -165,18 +166,19 @@ impl ClassLoader {
|
||||
.expect("invalid L descriptor");
|
||||
self.get_or_load(class_name, None)
|
||||
}
|
||||
None => Err(VmError::LoaderError("empty component descriptor".to_string())),
|
||||
_ => Err(VmError::LoaderError(format!("invalid component descriptor: {}", component_name))),
|
||||
None => Err(VmError::LoaderError(
|
||||
"empty component descriptor".to_string(),
|
||||
)),
|
||||
_ => Err(VmError::LoaderError(format!(
|
||||
"invalid component descriptor: {}",
|
||||
component_name
|
||||
))),
|
||||
}?;
|
||||
// let component = self.get_or_load(component_name, None)?;
|
||||
let arr_class = self.create_array_class(
|
||||
component,
|
||||
);
|
||||
self.needs_init.push(arr_class.clone());
|
||||
return Ok(arr_class)
|
||||
let arr_class = self.create_array_class(component);
|
||||
return Ok(arr_class);
|
||||
}
|
||||
let class = self.load_class(class_name, loader)?;
|
||||
self.needs_init.push(class.clone());
|
||||
Ok(class)
|
||||
}
|
||||
|
||||
@ -184,19 +186,27 @@ impl ClassLoader {
|
||||
self.classes.clone()
|
||||
}*/
|
||||
|
||||
fn load_class(&mut self, what: &str, loader: LoaderId) -> Result<Arc<RuntimeClass>, VmError> {
|
||||
pub fn load_class(
|
||||
&mut self,
|
||||
what: &str,
|
||||
loader: LoaderId,
|
||||
) -> Result<Arc<RuntimeClass>, VmError> {
|
||||
let (module, class_fqn) = ("", what);
|
||||
let bytes = self
|
||||
.bimage
|
||||
.get_class(module, class_fqn).or_else(|e| Self::load_class_from_disk(what)).map_err(|e1| {
|
||||
let classes = self.classes.iter().map(|x| {
|
||||
x.this_class.clone()
|
||||
}).collect::<Vec<_>>();
|
||||
// println!("{:#?}", classes);
|
||||
VmError::LoaderError(format!("failed to find class: {:?}\n{:#?}", what, classes))
|
||||
})?;
|
||||
let (_, cf) = ClassFile::from_bytes((bytes.as_ref(), 0))
|
||||
.map_err(|e| VmError::DekuError(e))?;
|
||||
.get_class(module, class_fqn)
|
||||
.or_else(|e| Self::load_class_from_disk(what))
|
||||
.map_err(|e1| {
|
||||
let classes = self
|
||||
.classes
|
||||
.iter()
|
||||
.map(|x| x.this_class.clone())
|
||||
.collect::<Vec<_>>();
|
||||
// println!("{:#?}", classes);
|
||||
VmError::LoaderError(format!("failed to find class: {:?}\n{:#?}", what, classes))
|
||||
})?;
|
||||
let (_, cf) =
|
||||
ClassFile::from_bytes((bytes.as_ref(), 0)).map_err(|e| VmError::DekuError(e))?;
|
||||
let runtime = self.runtime_class(cf);
|
||||
let arced = Arc::new(runtime);
|
||||
let option = self
|
||||
@ -216,7 +226,7 @@ impl ClassLoader {
|
||||
|
||||
let path = format!("{class_path}/{what}.class");
|
||||
log::info!("Loading class from path: {}", path);
|
||||
let mut class_file = File::open(path).map_err(|e| {e.to_string()})?;
|
||||
let mut class_file = File::open(path).map_err(|e| e.to_string())?;
|
||||
let mut bytes = Vec::new();
|
||||
class_file.read_to_end(&mut bytes).unwrap();
|
||||
Ok(bytes)
|
||||
@ -302,6 +312,7 @@ impl ClassLoader {
|
||||
.iter()
|
||||
.map(|e| {
|
||||
let name = constant_pool.get_string(e.name_index).unwrap();
|
||||
let class = this_class.clone();
|
||||
let flags = MethodFlags::from(e.access_flags);
|
||||
let desc = constant_pool
|
||||
.get_string(e.descriptor_index)
|
||||
@ -327,6 +338,7 @@ impl ClassLoader {
|
||||
|
||||
MethodData {
|
||||
name,
|
||||
class,
|
||||
flags,
|
||||
desc,
|
||||
code,
|
||||
@ -359,9 +371,7 @@ impl ClassLoader {
|
||||
|
||||
let source_file = class_file.attributes.iter().find_map(|attr| {
|
||||
match constant_pool.parse_attribute(attr.clone()).ok()? {
|
||||
Attribute::SourceFile(index) => {
|
||||
constant_pool.get_string(index).ok()
|
||||
},
|
||||
Attribute::SourceFile(index) => constant_pool.get_string(index).ok(),
|
||||
_ => None,
|
||||
}
|
||||
});
|
||||
@ -380,6 +390,7 @@ impl ClassLoader {
|
||||
super_interfaces,
|
||||
component_type: None,
|
||||
source_file,
|
||||
mirror_in_progress: Default::default(),
|
||||
}
|
||||
}
|
||||
// pub fn get_or_create_array_class(class_name: &str) -> RuntimeClass {
|
||||
@ -391,14 +402,12 @@ impl ClassLoader {
|
||||
// Ok(class)
|
||||
// }
|
||||
|
||||
pub fn create_array_class(
|
||||
&mut self,
|
||||
component: Arc<RuntimeClass>,
|
||||
) -> Arc<RuntimeClass> {
|
||||
pub fn create_array_class(&mut self, component: Arc<RuntimeClass>) -> Arc<RuntimeClass> {
|
||||
// let name = format!("[{}", component.descriptor()); // e.g., "[Ljava/lang/String;"
|
||||
let object_class: Arc<RuntimeClass> = self.get_or_load("java/lang/Object", None).unwrap();
|
||||
let cloneable: Arc<RuntimeClass> = self.get_or_load("java/lang/Cloneable", None).unwrap();
|
||||
let serializable: Arc<RuntimeClass> = self.get_or_load("java/io/Serializable", None).unwrap();
|
||||
let serializable: Arc<RuntimeClass> =
|
||||
self.get_or_load("java/io/Serializable", None).unwrap();
|
||||
let name = match component.this_class.as_str() {
|
||||
"byte" => "[B".to_string(),
|
||||
"char" => "[C".to_string(),
|
||||
@ -408,49 +417,41 @@ impl ClassLoader {
|
||||
"long" => "[J".to_string(),
|
||||
"short" => "[S".to_string(),
|
||||
"boolean" => "[Z".to_string(),
|
||||
s if s.starts_with('[') => format!("[{}", s), // nested array
|
||||
s => format!("[L{};", s), // object class
|
||||
s if s.starts_with('[') => format!("[{}", s), // nested array
|
||||
s => format!("[L{};", s), // object class
|
||||
};
|
||||
let klass = RuntimeClass {
|
||||
constant_pool: Arc::new(vec![]),
|
||||
access_flags: ClassFlags {
|
||||
MODULE: false,
|
||||
ENUM: false,
|
||||
ANNOTATION: false,
|
||||
SYNTHETIC: false,
|
||||
ABSTRACT: true,
|
||||
INTERFACE: false,
|
||||
SUPER: false,
|
||||
FINAL: true,
|
||||
PUBLIC: true,
|
||||
},
|
||||
this_class: name.clone(),
|
||||
super_class: Some(object_class.clone()),
|
||||
interfaces: vec![cloneable.clone(), serializable.clone()],
|
||||
fields: vec![],
|
||||
methods: vec![], // clone() is handled specially
|
||||
mirror: OnceLock::new(),
|
||||
mirror_in_progress: Default::default(),
|
||||
init_state: Mutex::new(InitState::NotInitialized), // arrays need no <clinit>
|
||||
super_classes: vec![object_class],
|
||||
super_interfaces: vec![cloneable, serializable],
|
||||
component_type: Some(component), // new field
|
||||
source_file: None,
|
||||
};
|
||||
let klass =
|
||||
RuntimeClass {
|
||||
constant_pool: Arc::new(vec![]),
|
||||
access_flags: ClassFlags {
|
||||
MODULE: false,
|
||||
ENUM: false,
|
||||
ANNOTATION: false,
|
||||
SYNTHETIC: false,
|
||||
ABSTRACT: true,
|
||||
INTERFACE: false,
|
||||
SUPER: false,
|
||||
FINAL: true,
|
||||
PUBLIC: true,
|
||||
},
|
||||
this_class: name.clone(),
|
||||
super_class: Some(object_class.clone()),
|
||||
interfaces: vec![cloneable.clone(), serializable.clone()],
|
||||
fields: vec![],
|
||||
methods: vec![], // clone() is handled specially
|
||||
mirror: OnceLock::new(),
|
||||
init_state: Mutex::new(InitState::NotInitialized), // arrays need no <clinit>
|
||||
super_classes: vec![object_class],
|
||||
super_interfaces: vec![cloneable, serializable],
|
||||
component_type: Some(component), // new field
|
||||
source_file: None,
|
||||
};
|
||||
let klass = Arc::new(klass);
|
||||
self.classes.insert((name.to_string(), None), klass.clone());
|
||||
klass
|
||||
}
|
||||
|
||||
pub fn primitive_class(&mut self, name: &str) -> Arc<RuntimeClass> {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
let klass = Arc::new(RuntimeClass {
|
||||
constant_pool: Arc::new(vec![]),
|
||||
access_flags: ClassFlags {
|
||||
@ -470,6 +471,7 @@ impl ClassLoader {
|
||||
fields: vec![],
|
||||
methods: vec![],
|
||||
mirror: Default::default(),
|
||||
mirror_in_progress: Default::default(),
|
||||
init_state: Mutex::new(InitState::NotInitialized),
|
||||
super_classes: vec![],
|
||||
super_interfaces: vec![],
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
use std::fmt::{Display, Formatter};
|
||||
use deku::DekuError;
|
||||
use crate::class_file::constant_pool::ConstantPoolError;
|
||||
use deku::DekuError;
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum VmError {
|
||||
ConstantPoolError(String),
|
||||
StackError(String),
|
||||
InvariantError(String),
|
||||
Debug(&'static str),
|
||||
DekuError(DekuError),
|
||||
LoaderError(String),
|
||||
ExecutionError,
|
||||
@ -15,6 +16,7 @@ pub enum VmError {
|
||||
message: String,
|
||||
stack_trace: Vec<StackTraceElement>,
|
||||
},
|
||||
NotImplemented(String),
|
||||
}
|
||||
|
||||
impl VmError {
|
||||
@ -33,7 +35,11 @@ impl Display for VmError {
|
||||
VmError::LoaderError(msg) => write!(f, "Loader error: {}", msg),
|
||||
VmError::ExecutionError => write!(f, "Execution error"),
|
||||
VmError::NativeError(msg) => write!(f, "Native error {msg}"),
|
||||
_ => { write!(f, "idk lol") }
|
||||
VmError::NotImplemented(msg) => write!(f, "Not implemented: {}", msg),
|
||||
VmError::Debug(msg) => write!(f, "Debug panic: {}", msg),
|
||||
_ => {
|
||||
write!(f, "idk lol")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -60,9 +66,15 @@ pub struct StackTraceElement {
|
||||
impl VmError {
|
||||
pub(crate) fn with_frame(self, elem: StackTraceElement) -> Self {
|
||||
match self {
|
||||
VmError::Exception { message, mut stack_trace } => {
|
||||
VmError::Exception {
|
||||
message,
|
||||
mut stack_trace,
|
||||
} => {
|
||||
stack_trace.push(elem);
|
||||
VmError::Exception { message, stack_trace }
|
||||
VmError::Exception {
|
||||
message,
|
||||
stack_trace,
|
||||
}
|
||||
}
|
||||
other => VmError::Exception {
|
||||
message: format!("{}", other),
|
||||
|
||||
1500
crates/core/src/frame.rs
Normal file
1500
crates/core/src/frame.rs
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,11 +1,11 @@
|
||||
use std::fmt::{Display, Formatter};
|
||||
use crate::attributes::ArrayType;
|
||||
use crate::attributes::{ArrayType, LookupSwitchData, TableSwitchData};
|
||||
use deku_derive::DekuRead;
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
||||
//noinspection SpellCheckingInspection
|
||||
#[allow(non_camel_case_types)]
|
||||
#[derive(Clone, PartialEq, Debug, DekuRead)]
|
||||
#[deku(id_type = "u8", ctx = "_endian: deku::ctx::Endian", endian = "big")]
|
||||
#[deku(id_type = "u8", ctx = "byte_offset: u16", endian = "big")]
|
||||
pub enum Ops {
|
||||
// Constants
|
||||
#[deku(id = 0x00)]
|
||||
@ -366,9 +366,9 @@ pub enum Ops {
|
||||
ret(u8),
|
||||
//
|
||||
#[deku(id = 0xaa)]
|
||||
tableswitch,
|
||||
tableswitch(#[deku(ctx = "byte_offset")] TableSwitchData),
|
||||
#[deku(id = 0xab)]
|
||||
lookupswitch,
|
||||
lookupswitch(#[deku(ctx = "byte_offset")] LookupSwitchData),
|
||||
#[deku(id = 0xac)]
|
||||
ireturn,
|
||||
#[deku(id = 0xad)]
|
||||
@ -424,7 +424,7 @@ pub enum Ops {
|
||||
monitorexit,
|
||||
//extended
|
||||
#[deku(id = 0xC4)]
|
||||
wide,
|
||||
wide(#[deku(ctx = "byte_offset")] WideData),
|
||||
#[deku(id = 0xC5)]
|
||||
multianewarray(u16, u8),
|
||||
#[deku(id = 0xC6)]
|
||||
@ -443,6 +443,40 @@ pub enum Ops {
|
||||
#[deku(id = 0xFF)]
|
||||
impdep2,
|
||||
}
|
||||
//noinspection SpellCheckingInspection
|
||||
#[allow(non_camel_case_types)]
|
||||
#[derive(Clone, PartialEq, Debug, DekuRead)]
|
||||
#[deku(
|
||||
id_type = "u8",
|
||||
ctx = "_endian: deku::ctx::Endian, _byte_offset: u16",
|
||||
endian = "big"
|
||||
)]
|
||||
pub enum WideData {
|
||||
#[deku(id = 0x84)]
|
||||
iinc(u16, i16),
|
||||
#[deku(id = 0x15)]
|
||||
iload(u16),
|
||||
#[deku(id = 0x16)]
|
||||
lload(u16),
|
||||
#[deku(id = 0x17)]
|
||||
fload(u16),
|
||||
#[deku(id = 0x18)]
|
||||
dload(u16),
|
||||
#[deku(id = 0x19)]
|
||||
aload(u16),
|
||||
#[deku(id = 0x36)]
|
||||
istore(u16),
|
||||
#[deku(id = 0x37)]
|
||||
lstore(u16),
|
||||
#[deku(id = 0x38)]
|
||||
fstore(u16),
|
||||
#[deku(id = 0x39)]
|
||||
dstore(u16),
|
||||
#[deku(id = 0x3A)]
|
||||
astore(u16),
|
||||
#[deku(id = 0xA9)]
|
||||
ret(u16),
|
||||
}
|
||||
|
||||
impl Display for Ops {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
@ -632,8 +666,8 @@ impl Display for Ops {
|
||||
Ops::goto(off) => write!(f, "goto {}", off),
|
||||
Ops::jsr(off) => write!(f, "jsr {}", off),
|
||||
Ops::ret(idx) => write!(f, "ret {}", idx),
|
||||
Ops::tableswitch => write!(f, "tableswitch"),
|
||||
Ops::lookupswitch => write!(f, "lookupswitch"),
|
||||
Ops::tableswitch(_) => write!(f, "tableswitch"),
|
||||
Ops::lookupswitch(_) => write!(f, "lookupswitch"),
|
||||
Ops::ireturn => write!(f, "ireturn"),
|
||||
Ops::lreturn => write!(f, "lreturn"),
|
||||
Ops::freturn => write!(f, "freturn"),
|
||||
@ -662,7 +696,7 @@ impl Display for Ops {
|
||||
Ops::monitorexit => write!(f, "monitorexit"),
|
||||
|
||||
// Extended
|
||||
Ops::wide => write!(f, "wide"),
|
||||
Ops::wide(_) => write!(f, "wide"),
|
||||
Ops::multianewarray(idx, dims) => write!(f, "multianewarray #{}, {}", idx, dims),
|
||||
Ops::ifnull(off) => write!(f, "ifnull {}", off),
|
||||
Ops::ifnonnull(off) => write!(f, "ifnonnull {}", off),
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -8,7 +8,7 @@ macro_rules! store {
|
||||
let index: usize = $index;
|
||||
let value = $self.pop()?;
|
||||
trace!("\tStoring: {value} into local[{index}]");
|
||||
$self.vars.set(index , value);
|
||||
$self.vars.set(index, value);
|
||||
|
||||
Ok(ExecutionResult::Continue)
|
||||
}
|
||||
@ -21,7 +21,7 @@ macro_rules! store {
|
||||
let index: usize = $index;
|
||||
let value = $self.pop()?;
|
||||
trace!("\tStoring: {value} into local[{index}]");
|
||||
$self.vars.set(index , value);
|
||||
$self.vars.set(index, value);
|
||||
Ok(ExecutionResult::Continue)
|
||||
}
|
||||
}};
|
||||
@ -65,10 +65,11 @@ macro_rules! pool_get_impl {
|
||||
let cp_entry = self.get_constant(index)?;
|
||||
match cp_entry {
|
||||
ConstantPoolEntry::$variant(value) => Ok(value),
|
||||
_ => Err(ConstantPoolError(format!(
|
||||
"Expected {} constant at index {}",
|
||||
other => Err(ConstantPoolError::Generic(format!(
|
||||
"Expected {} constant at index {}, got {:?} instead",
|
||||
stringify!($variant),
|
||||
index
|
||||
index,
|
||||
other
|
||||
))),
|
||||
}
|
||||
}
|
||||
@ -82,7 +83,7 @@ macro_rules! if_int_zero {
|
||||
return Err(VmError::stack_not_int());
|
||||
};
|
||||
if v $op 0 {
|
||||
Ok(ExecutionResult::Advance($offset))
|
||||
Ok(ExecutionResult::Advance($offset as i32))
|
||||
} else {
|
||||
Ok(ExecutionResult::Continue)
|
||||
}
|
||||
@ -99,7 +100,7 @@ macro_rules! if_int_cmp {
|
||||
return Err(VmError::stack_not_int());
|
||||
};
|
||||
if v1 $op v2 {
|
||||
Ok(ExecutionResult::Advance($offset))
|
||||
Ok(ExecutionResult::Advance($offset as i32))
|
||||
} else {
|
||||
Ok(ExecutionResult::Continue)
|
||||
}
|
||||
@ -108,136 +109,156 @@ macro_rules! if_int_cmp {
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! float_cmp {
|
||||
($self:expr, $prim:ident, $nan_result:expr) => {{
|
||||
let v2 = $self.pop()?;
|
||||
let v1 = $self.pop()?;
|
||||
($self:expr, $prim:ident, $nan_result:expr) => {{
|
||||
let v2 = $self.pop()?;
|
||||
let v1 = $self.pop()?;
|
||||
|
||||
let (Value::Primitive(Primitive::$prim(f1)), Value::Primitive(Primitive::$prim(f2))) = (&v1, &v2) else {
|
||||
return Err(VmError::StackError(format!(
|
||||
"{v1:?} or {v2:?} was not a {}",
|
||||
stringify!($prim).to_lowercase()
|
||||
)));
|
||||
};
|
||||
let (Value::Primitive(Primitive::$prim(f1)), Value::Primitive(Primitive::$prim(f2))) =
|
||||
(&v1, &v2)
|
||||
else {
|
||||
return Err(VmError::StackError(format!(
|
||||
"{v1:?} or {v2:?} was not a {}",
|
||||
stringify!($prim).to_lowercase()
|
||||
)));
|
||||
};
|
||||
|
||||
let result: i32 = if f1.is_nan() || f2.is_nan() {
|
||||
$nan_result
|
||||
} else {
|
||||
match f1.partial_cmp(f2) {
|
||||
Some(std::cmp::Ordering::Greater) => 1,
|
||||
Some(std::cmp::Ordering::Equal) => 0,
|
||||
_ => -1,
|
||||
}
|
||||
};
|
||||
let result: i32 = if f1.is_nan() || f2.is_nan() {
|
||||
$nan_result
|
||||
} else {
|
||||
match f1.partial_cmp(f2) {
|
||||
Some(std::cmp::Ordering::Greater) => 1,
|
||||
Some(std::cmp::Ordering::Equal) => 0,
|
||||
_ => -1,
|
||||
}
|
||||
};
|
||||
|
||||
$self.stack.push(Value::from(result));
|
||||
Ok(ExecutionResult::Continue)
|
||||
}};
|
||||
$self.stack.push(Value::from(result));
|
||||
Ok(ExecutionResult::Continue)
|
||||
}};
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! convert_simple {
|
||||
($self:expr, $from:ident, $to:ty) => {{
|
||||
let Value::Primitive(Primitive::$from(v)) = $self.pop()? else {
|
||||
return Err(VmError::StackError(format!(
|
||||
"Expected {}", stringify!($from).to_lowercase()
|
||||
)));
|
||||
};
|
||||
$self.stack.push(Value::from(v as $to));
|
||||
Ok(ExecutionResult::Continue)
|
||||
}};
|
||||
($self:expr, $from:ident, $to:ty) => {{
|
||||
let Value::Primitive(Primitive::$from(v)) = $self.pop()? else {
|
||||
return Err(VmError::StackError(format!(
|
||||
"Expected {}",
|
||||
stringify!($from).to_lowercase()
|
||||
)));
|
||||
};
|
||||
$self.stack.push(Value::from(v as $to));
|
||||
Ok(ExecutionResult::Continue)
|
||||
}};
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! convert_float_to_int {
|
||||
($self:expr, $from:ident, $from_ty:ty, $to:ty) => {{
|
||||
let Value::Primitive(Primitive::$from(v)) = $self.pop()? else {
|
||||
return Err(VmError::StackError(format!(
|
||||
"Expected {}", stringify!($from).to_lowercase()
|
||||
)));
|
||||
};
|
||||
let result = if v.is_nan() {
|
||||
0
|
||||
} else if v >= <$to>::MAX as $from_ty {
|
||||
<$to>::MAX
|
||||
} else if v <= <$to>::MIN as $from_ty {
|
||||
<$to>::MIN
|
||||
} else {
|
||||
v as $to
|
||||
};
|
||||
$self.stack.push(Value::from(result));
|
||||
Ok(ExecutionResult::Continue)
|
||||
}};
|
||||
($self:expr, $from:ident, $from_ty:ty, $to:ty) => {{
|
||||
let Value::Primitive(Primitive::$from(v)) = $self.pop()? else {
|
||||
return Err(VmError::StackError(format!(
|
||||
"Expected {}",
|
||||
stringify!($from).to_lowercase()
|
||||
)));
|
||||
};
|
||||
let result = if v.is_nan() {
|
||||
0
|
||||
} else if v >= <$to>::MAX as $from_ty {
|
||||
<$to>::MAX
|
||||
} else if v <= <$to>::MIN as $from_ty {
|
||||
<$to>::MIN
|
||||
} else {
|
||||
v as $to
|
||||
};
|
||||
$self.stack.push(Value::from(result));
|
||||
Ok(ExecutionResult::Continue)
|
||||
}};
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! convert_int_narrow {
|
||||
($self:expr, $to:ty) => {{
|
||||
let Value::Primitive(Primitive::Int(v)) = $self.pop()? else {
|
||||
return Err(VmError::stack_not_int());
|
||||
};
|
||||
$self.stack.push(Value::from((v as $to) as i32));
|
||||
Ok(ExecutionResult::Continue)
|
||||
}};
|
||||
($self:expr, $to:ty) => {{
|
||||
let Value::Primitive(Primitive::Int(v)) = $self.pop()? else {
|
||||
return Err(VmError::stack_not_int());
|
||||
};
|
||||
$self.stack.push(Value::from((v as $to) as i32));
|
||||
Ok(ExecutionResult::Continue)
|
||||
}};
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! array_store {
|
||||
($self:expr, $prim:ident, $arr:ident) => {{
|
||||
let Value::Primitive(Primitive::$prim(value)) = $self.pop()? else {
|
||||
return Err(VmError::StackError(format!(
|
||||
"Value was not {}", stringify!($prim).to_lowercase()
|
||||
)));
|
||||
};
|
||||
let Value::Primitive(Primitive::Int(index)) = $self.pop()? else {
|
||||
return Err(VmError::stack_not_int());
|
||||
};
|
||||
let Value::Reference(Some(ReferenceKind::ArrayReference(ArrayReference::$arr(arr)))) = $self.pop()? else {
|
||||
return Err(VmError::StackError(format!(
|
||||
"Expected {} array reference", stringify!($arr).to_lowercase()
|
||||
)));
|
||||
};
|
||||
arr.lock().unwrap().set(index, value);
|
||||
Ok(ExecutionResult::Continue)
|
||||
}};
|
||||
($self:expr, $prim:ident, $arr:ident) => {{
|
||||
let Value::Primitive(Primitive::$prim(value)) = $self.pop()? else {
|
||||
return Err(VmError::StackError(format!(
|
||||
"Value was not {}",
|
||||
stringify!($prim).to_lowercase()
|
||||
)));
|
||||
};
|
||||
let Value::Primitive(Primitive::Int(index)) = $self.pop()? else {
|
||||
return Err(VmError::stack_not_int());
|
||||
};
|
||||
let Value::Reference(Some(ReferenceKind::ArrayReference(ArrayReference::$arr(arr)))) =
|
||||
$self.pop()?
|
||||
else {
|
||||
return Err(VmError::StackError(format!(
|
||||
"Expected {} array reference",
|
||||
stringify!($arr).to_lowercase()
|
||||
)));
|
||||
};
|
||||
arr.lock().set(index, value);
|
||||
Ok(ExecutionResult::Continue)
|
||||
}};
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! array_store_cast {
|
||||
($self:expr, $arr:ident, $cast:ty) => {{
|
||||
let Value::Primitive(Primitive::Int(value)) = $self.pop()? else {
|
||||
return Err(VmError::stack_not_int());
|
||||
};
|
||||
let Value::Primitive(Primitive::Int(index)) = $self.pop()? else {
|
||||
return Err(VmError::stack_not_int());
|
||||
};
|
||||
let Value::Reference(Some(ReferenceKind::ArrayReference(ArrayReference::$arr(arr)))) = $self.pop()? else {
|
||||
return Err(VmError::StackError(format!(
|
||||
"Expected {} array reference", stringify!($arr).to_lowercase()
|
||||
)));
|
||||
};
|
||||
arr.lock().unwrap().set(index, value as $cast);
|
||||
Ok(ExecutionResult::Continue)
|
||||
}};
|
||||
($self:expr, $arr:ident, $cast:ty) => {{
|
||||
let Value::Primitive(Primitive::Int(value)) = $self.pop()? else {
|
||||
return Err(VmError::stack_not_int());
|
||||
};
|
||||
let Value::Primitive(Primitive::Int(index)) = $self.pop()? else {
|
||||
return Err(VmError::stack_not_int());
|
||||
};
|
||||
let Value::Reference(Some(ReferenceKind::ArrayReference(ArrayReference::$arr(arr)))) =
|
||||
$self.pop()?
|
||||
else {
|
||||
return Err(VmError::StackError(format!(
|
||||
"Expected {} array reference",
|
||||
stringify!($arr).to_lowercase()
|
||||
)));
|
||||
};
|
||||
arr.lock().set(index, value as $cast);
|
||||
Ok(ExecutionResult::Continue)
|
||||
}};
|
||||
}
|
||||
|
||||
//math
|
||||
#[macro_export]
|
||||
macro_rules! binary_op {
|
||||
($self:expr, $prim:ident, |$a:ident, $b:ident| $expr:expr) => {{
|
||||
let Value::Primitive(Primitive::$prim($b)) = $self.pop()? else {
|
||||
return Err(VmError::StackError(format!("Expected {}", stringify!($prim).to_lowercase())));
|
||||
};
|
||||
let Value::Primitive(Primitive::$prim($a)) = $self.pop()? else {
|
||||
return Err(VmError::StackError(format!("Expected {}", stringify!($prim).to_lowercase())));
|
||||
};
|
||||
$self.stack.push(Value::from($expr));
|
||||
Ok(ExecutionResult::Continue)
|
||||
}};
|
||||
($self:expr, $prim:ident, |$a:ident, $b:ident| $expr:expr) => {{
|
||||
let Value::Primitive(Primitive::$prim($b)) = $self.pop()? else {
|
||||
return Err(VmError::StackError(format!(
|
||||
"Expected {}",
|
||||
stringify!($prim).to_lowercase()
|
||||
)));
|
||||
};
|
||||
let Value::Primitive(Primitive::$prim($a)) = $self.pop()? else {
|
||||
return Err(VmError::StackError(format!(
|
||||
"Expected {}",
|
||||
stringify!($prim).to_lowercase()
|
||||
)));
|
||||
};
|
||||
$self.stack.push(Value::from($expr));
|
||||
Ok(ExecutionResult::Continue)
|
||||
}};
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! unary_op {
|
||||
($self:expr, $prim:ident, |$v:ident| $expr:expr) => {{
|
||||
let Value::Primitive(Primitive::$prim($v)) = $self.pop()? else {
|
||||
return Err(VmError::StackError(format!("Expected {}", stringify!($prim).to_lowercase())));
|
||||
};
|
||||
$self.stack.push(Value::from($expr));
|
||||
Ok(ExecutionResult::Continue)
|
||||
}};
|
||||
($self:expr, $prim:ident, |$v:ident| $expr:expr) => {{
|
||||
let Value::Primitive(Primitive::$prim($v)) = $self.pop()? else {
|
||||
return Err(VmError::StackError(format!(
|
||||
"Expected {}",
|
||||
stringify!($prim).to_lowercase()
|
||||
)));
|
||||
};
|
||||
$self.stack.push(Value::from($expr));
|
||||
Ok(ExecutionResult::Continue)
|
||||
}};
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! int_div_rem {
|
||||
@ -262,15 +283,18 @@ macro_rules! int_div_rem {
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! shift_op {
|
||||
($self:expr, $prim:ident, $mask:expr, |$val:ident, $shift:ident| $expr:expr) => {{
|
||||
let Value::Primitive(Primitive::Int(s)) = $self.pop()? else {
|
||||
return Err(VmError::StackError("Expected int for shift amount".into()));
|
||||
};
|
||||
let Value::Primitive(Primitive::$prim($val)) = $self.pop()? else {
|
||||
return Err(VmError::StackError(format!("Expected {}", stringify!($prim).to_lowercase())));
|
||||
};
|
||||
let $shift = (s & $mask) as u32;
|
||||
$self.stack.push(Value::from($expr));
|
||||
Ok(ExecutionResult::Continue)
|
||||
}};
|
||||
($self:expr, $prim:ident, $mask:expr, |$val:ident, $shift:ident| $expr:expr) => {{
|
||||
let Value::Primitive(Primitive::Int(s)) = $self.pop()? else {
|
||||
return Err(VmError::StackError("Expected int for shift amount".into()));
|
||||
};
|
||||
let Value::Primitive(Primitive::$prim($val)) = $self.pop()? else {
|
||||
return Err(VmError::StackError(format!(
|
||||
"Expected {}",
|
||||
stringify!($prim).to_lowercase()
|
||||
)));
|
||||
};
|
||||
let $shift = (s & $mask) as u32;
|
||||
$self.stack.push(Value::from($expr));
|
||||
Ok(ExecutionResult::Continue)
|
||||
}};
|
||||
}
|
||||
@ -1,31 +1,54 @@
|
||||
use roast_vm_core::vm::Vm;
|
||||
use roast_vm_core::error::VmError;
|
||||
use libloading::Library;
|
||||
use log::{error, LevelFilter};
|
||||
use colored::Colorize;
|
||||
use libloading::Library;
|
||||
use log::{LevelFilter, error};
|
||||
use roast_vm_core::error::VmError;
|
||||
use roast_vm_core::stack_used;
|
||||
use roast_vm_core::vm::Vm;
|
||||
use std::time::Instant;
|
||||
|
||||
fn main() {
|
||||
env_logger::Builder::from_default_env()
|
||||
.filter_level(LevelFilter::Trace)
|
||||
.filter_module("deku", LevelFilter::Warn)
|
||||
.filter_module("roast_vm_core::class_file::class_file", LevelFilter::Info)
|
||||
.filter_module("roast_vm_core::attributes", LevelFilter::Info)
|
||||
.filter_module("roast_vm_core::instructions", LevelFilter::Info)
|
||||
.init();
|
||||
std::thread::Builder::new()
|
||||
.stack_size(8 * 1024 * 1024)
|
||||
.spawn(run)
|
||||
.unwrap()
|
||||
.join()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn run() {
|
||||
// env_logger::Builder::from_default_env()
|
||||
// .filter_level(LevelFilter::Trace)
|
||||
// .filter_module("deku", LevelFilter::Warn)
|
||||
// .filter_module("roast_vm_core::class_file::class_file", LevelFilter::Info)
|
||||
// .filter_module("roast_vm_core::attributes", LevelFilter::Info)
|
||||
// .filter_module("roast_vm_core::instructions", LevelFilter::Info)
|
||||
// .init();
|
||||
stack_used();
|
||||
let vm = Vm::new();
|
||||
vm.load_native_library("roast_vm.dll", load("roast_vm.dll").into());
|
||||
vm.load_native_library("jvm.dll", load("jvm.dll").into());
|
||||
vm.load_native_library("java.dll", load("java.dll").into());
|
||||
let start = Instant::now();
|
||||
match vm.run("org/example/Main") {
|
||||
Ok(_) => {}
|
||||
Err(VmError::Exception { message, stack_trace }) => {
|
||||
Ok(_) => {
|
||||
println!("took {:?}", start.elapsed());
|
||||
}
|
||||
Err(VmError::Exception {
|
||||
message,
|
||||
stack_trace,
|
||||
}) => {
|
||||
println!("took {:?}", start.elapsed());
|
||||
let thread = vm.threads.get(&vm.main_thread_id).unwrap();
|
||||
let objs = thread.gc.read().unwrap()
|
||||
let objs = thread
|
||||
.gc
|
||||
.read()
|
||||
.objects
|
||||
.iter()
|
||||
.map(|(x, y)| format!("{x} : {y}"))
|
||||
.collect::<Vec<_>>();
|
||||
let len = objs.len().clone();
|
||||
error!("Heap dump: len: {len} objs:\n{objs:#?}");
|
||||
let len = objs.len();
|
||||
let bytes_formatted = format_bytes(thread.gc.read().bytes_in_use());
|
||||
error!("Heap state:\n\tlen: {len}\n\tsize{bytes_formatted}");
|
||||
eprintln!("{}: {}", "Exception".red().bold(), message);
|
||||
for elem in &stack_trace {
|
||||
let class_name = elem.class.replace('/', ".");
|
||||
@ -34,18 +57,11 @@ fn main() {
|
||||
(Some(f), Some(l)) => format!("({}:{})", f, l),
|
||||
(Some(f), None) => format!("({})", f),
|
||||
_ => "(Unknown Source)".to_string(),
|
||||
}.blue().dimmed();
|
||||
}
|
||||
.blue()
|
||||
.dimmed();
|
||||
eprintln!("{} {}.{}{}", at, class_name, elem.method, location);
|
||||
}
|
||||
/*error!("Exception: {}", message);
|
||||
for elem in &stack_trace {
|
||||
let class_name = elem.class.replace('/', ".");
|
||||
match (&elem.file, elem.line) {
|
||||
(Some(f), Some(l)) => eprintln!("\tat {}.{}({}:{})", class_name, elem.method, f, l),
|
||||
(Some(f), None) => eprintln!("\tat {}.{}({})", class_name, elem.method, f),
|
||||
_ => eprintln!("\tat {}.{}(Unknown Source)", class_name, elem.method),
|
||||
}
|
||||
}*/
|
||||
}
|
||||
Err(e) => {
|
||||
error!("VM Error: {:?}", e);
|
||||
@ -61,3 +77,21 @@ fn load(filename: &str) -> Library {
|
||||
|
||||
Library::from(leeb)
|
||||
}
|
||||
|
||||
fn format_bytes(bytes: usize) -> String {
|
||||
const KIB: f64 = 1024.0;
|
||||
const MIB: f64 = KIB * 1024.0;
|
||||
const GIB: f64 = MIB * 1024.0;
|
||||
|
||||
let (size, unit) = if bytes as f64 >= GIB {
|
||||
(bytes as f64 / GIB, "GiB")
|
||||
} else if bytes as f64 >= MIB {
|
||||
(bytes as f64 / MIB, "MiB")
|
||||
} else if bytes as f64 >= KIB {
|
||||
(bytes as f64 / KIB, "KiB")
|
||||
} else {
|
||||
(bytes as f64, "B")
|
||||
};
|
||||
|
||||
format!(" {:.2} {}", size, unit)
|
||||
}
|
||||
|
||||
@ -1,20 +1,21 @@
|
||||
#![allow(unused_variables)]
|
||||
#![feature(c_variadic)]
|
||||
use crate::class::RuntimeClass;
|
||||
use crate::class_file::{FieldData, FieldRef};
|
||||
use crate::objects::array::ArrayReference;
|
||||
use crate::objects::object::{ObjectReference, ReferenceKind};
|
||||
use crate::prim::jboolean;
|
||||
use crate::thread::VmThread;
|
||||
use crate::value::{Primitive, Value};
|
||||
use crate::{BaseType, FieldType, MethodDescriptor};
|
||||
use itertools::Itertools;
|
||||
use jni::sys::*;
|
||||
use jni::sys::{jclass, jint, jobject, JNINativeInterface_};
|
||||
use jni::sys::{jstring, JNIEnv};
|
||||
use log::{error, info, trace, warn};
|
||||
use std::ffi::{c_char, CStr, CString};
|
||||
use std::ptr;
|
||||
use std::sync::{Arc};
|
||||
use crate::class_file::{FieldData, FieldRef};
|
||||
use crate::objects::object::{ ObjectReference, ReferenceKind};
|
||||
use crate::{BaseType, FieldType, MethodDescriptor};
|
||||
use jni::sys::*;
|
||||
use log::{error, info, trace, warn};
|
||||
use std::sync::Arc;
|
||||
|
||||
const JNI_VERSION_1_1: jint = 0x00010001;
|
||||
const JNI_VERSION_1_2: jint = 0x00010002;
|
||||
@ -271,19 +272,27 @@ unsafe fn get_thread(env: *mut JNIEnv) -> *const VmThread {
|
||||
}
|
||||
|
||||
fn resolve_class(thread: &VmThread, clazz: jclass) -> Option<Arc<RuntimeClass>> {
|
||||
let loader = thread.loader.lock().unwrap();
|
||||
let loader = thread.loader.lock();
|
||||
let class_id = clazz as u32;
|
||||
loader.class_from_mirror_id(class_id)
|
||||
}
|
||||
|
||||
fn resolve_object(thread: &VmThread, obj: jobject) -> Option<ObjectReference> {
|
||||
let gc = thread.gc.read().unwrap();
|
||||
let gc = thread.gc.read();
|
||||
let ReferenceKind::ObjectReference(obj_ref) = gc.get(obj as u32) else {
|
||||
return None;
|
||||
};
|
||||
Some(obj_ref.clone())
|
||||
}
|
||||
|
||||
fn resolve_reference(thread: &VmThread, obj: jobject) -> Option<ReferenceKind> {
|
||||
if obj.is_null() {
|
||||
return None;
|
||||
}
|
||||
let gc = thread.gc.read();
|
||||
Some(gc.get(obj as u32).clone())
|
||||
}
|
||||
|
||||
unsafe extern "system" fn jni_get_version(env: *mut JNIEnv) -> jint {
|
||||
JNI_VERSION_24
|
||||
}
|
||||
@ -300,7 +309,7 @@ unsafe extern "system" fn get_string_utfchars(
|
||||
// }
|
||||
let thread = &*get_thread(env);
|
||||
let rust_string = {
|
||||
let gc = thread.gc.read().unwrap();
|
||||
let gc = thread.gc.read();
|
||||
|
||||
let obj_id = str as u32;
|
||||
|
||||
@ -309,8 +318,8 @@ unsafe extern "system" fn get_string_utfchars(
|
||||
_ => return ptr::null(),
|
||||
};
|
||||
|
||||
let obj = obj_ref.lock().unwrap();
|
||||
let field_ref = FieldRef{
|
||||
let obj = obj_ref.lock();
|
||||
let field_ref = FieldRef {
|
||||
class: "java/lang/String".to_string(),
|
||||
name: "value".to_string(),
|
||||
desc: FieldType::ArrayType(Box::new(FieldType::Base(BaseType::Byte))),
|
||||
@ -322,7 +331,7 @@ unsafe extern "system" fn get_string_utfchars(
|
||||
return ptr::null();
|
||||
};
|
||||
|
||||
let array = byte_array.lock().unwrap();
|
||||
let array = byte_array.lock();
|
||||
|
||||
let bytes: Vec<u8> = array.backing.iter().map(|&b| b as u8).collect();
|
||||
|
||||
@ -349,7 +358,7 @@ unsafe extern "system" fn get_string_utfchars(
|
||||
unsafe extern "system" fn get_object_class(env: *mut JNIEnv, obj: jobject) -> jclass {
|
||||
trace!("get_object_class");
|
||||
let thread = &*get_thread(env);
|
||||
let gc = thread.gc.read().unwrap();
|
||||
let gc = thread.gc.read();
|
||||
|
||||
let obj_id = obj as u32;
|
||||
|
||||
@ -357,7 +366,7 @@ unsafe extern "system" fn get_object_class(env: *mut JNIEnv, obj: jobject) -> jc
|
||||
return ptr::null_mut();
|
||||
};
|
||||
|
||||
let obj_lock = obj_ref.lock().unwrap();
|
||||
let obj_lock = obj_ref.lock();
|
||||
let class: &Arc<RuntimeClass> = &obj_lock.class;
|
||||
|
||||
*class.mirror.wait() as jclass
|
||||
@ -371,12 +380,12 @@ unsafe extern "system" fn register_natives(
|
||||
) -> jint {
|
||||
trace!("register_natives");
|
||||
let thread = &*get_thread(env);
|
||||
let gc = thread.gc.read().unwrap();
|
||||
let gc = thread.gc.read();
|
||||
let class_id = clazz as u32;
|
||||
let ReferenceKind::ObjectReference(class_ref) = gc.get(class_id) else {
|
||||
return JNI_ERR;
|
||||
};
|
||||
let class_name = class_ref.lock().unwrap().class.this_class.clone();
|
||||
let class_name = class_ref.lock().class.this_class.clone();
|
||||
let class_name = class_name.replace("/", "_");
|
||||
|
||||
// let boop = JClass::from_raw(clazz);
|
||||
@ -399,12 +408,16 @@ unsafe extern "system" fn register_natives(
|
||||
continue;
|
||||
}
|
||||
|
||||
if full_name.contains("getDeclaredConstructors0") {
|
||||
continue;
|
||||
}
|
||||
|
||||
thread
|
||||
.vm
|
||||
.native_methods
|
||||
.insert(full_name.to_owned(), fn_ptr);
|
||||
|
||||
println!(
|
||||
trace!(
|
||||
"name:{name}, signature:{signature}, fn_ptr{}",
|
||||
fn_ptr.is_null()
|
||||
)
|
||||
@ -416,8 +429,6 @@ unsafe extern "system" fn register_natives(
|
||||
// JNI FUNCTION STUBS - All unimplemented functions below
|
||||
// ============================================================================
|
||||
|
||||
|
||||
|
||||
unsafe extern "system" fn define_class(
|
||||
env: *mut JNIEnv,
|
||||
name: *const c_char,
|
||||
@ -594,7 +605,7 @@ unsafe extern "system" fn is_instance_of(
|
||||
let thread = &*get_thread(env);
|
||||
let second = resolve_class(thread, clazz).unwrap();
|
||||
let obj = resolve_object(thread, obj).unwrap();
|
||||
let first = obj.lock().unwrap().class.clone();
|
||||
let first = obj.lock().class.clone();
|
||||
if first.is_assignable_into(second) {
|
||||
JNI_TRUE
|
||||
} else {
|
||||
@ -610,29 +621,26 @@ unsafe extern "system" fn get_method_id(
|
||||
) -> jmethodID {
|
||||
trace!("get_method_id");
|
||||
let thread = &*get_thread(env);
|
||||
let gc = thread.gc.read().unwrap();
|
||||
let loader = thread.loader.lock().unwrap();
|
||||
|
||||
let name_str = CStr::from_ptr(name).to_str().unwrap();
|
||||
let sig_str = CStr::from_ptr(sig).to_str().unwrap();
|
||||
|
||||
let class_id = clazz as u32;
|
||||
let ReferenceKind::ObjectReference(class_ref) = gc.get(class_id) else {
|
||||
return ptr::null_mut();
|
||||
};
|
||||
|
||||
let Some(runtime_class) = loader.class_from_mirror_id(class_id) else {
|
||||
return ptr::null_mut();
|
||||
};
|
||||
// Brief lock just to get what we need
|
||||
let runtime_class = {
|
||||
let loader = thread.loader.lock();
|
||||
match loader.class_from_mirror_id(class_id) {
|
||||
Some(rc) => rc.clone(), // Arc clone
|
||||
None => return ptr::null_mut(),
|
||||
}
|
||||
}; // loader released here
|
||||
|
||||
// Now safe to call - no locks held
|
||||
thread.ensure_initialised(&runtime_class).unwrap();
|
||||
|
||||
let desc = MethodDescriptor::parse(sig_str).unwrap();
|
||||
let method = runtime_class.find_method(name_str, &desc).unwrap();
|
||||
|
||||
// let Value::Reference(Some(ReferenceKind::ObjectReference(class_name_string))) = class_ref.lock().unwrap().get_field("name") else {
|
||||
// return ptr::null_mut();
|
||||
// };
|
||||
// class_name_string.lock().unwrap().get_field()
|
||||
|
||||
method as *const _ as jmethodID
|
||||
}
|
||||
|
||||
@ -1024,29 +1032,23 @@ unsafe extern "system" fn get_field_id(
|
||||
) -> jfieldID {
|
||||
trace!("get_field_id");
|
||||
let thread = &*get_thread(env);
|
||||
let gc = thread.gc.read().unwrap();
|
||||
let loader = thread.loader.lock().unwrap();
|
||||
let loader = thread.loader.lock();
|
||||
|
||||
let name_str = CStr::from_ptr(name).to_str().unwrap();
|
||||
let sig_str = CStr::from_ptr(sig).to_str().unwrap();
|
||||
|
||||
let class_id = clazz as u32;
|
||||
let ReferenceKind::ObjectReference(class_ref) = gc.get(class_id) else {
|
||||
let ReferenceKind::ObjectReference(class_ref) = thread.gc.read().get(class_id) else {
|
||||
return ptr::null_mut();
|
||||
};
|
||||
|
||||
// println!("class ref: {:?}", class_ref);
|
||||
let Some(runtime_class) = loader.class_from_mirror_id(class_id) else {
|
||||
return ptr::null_mut();
|
||||
};
|
||||
thread.ensure_initialised(&runtime_class).unwrap();
|
||||
|
||||
let field_type = FieldType::parse(sig_str).unwrap();
|
||||
let field_ref = runtime_class.find_field(name_str, &field_type).unwrap();
|
||||
|
||||
// let Value::Reference(Some(ReferenceKind::ObjectReference(class_name_string))) = class_ref.lock().unwrap().get_field("name") else {
|
||||
// return ptr::null_mut();
|
||||
// };
|
||||
// class_name_string.lock().unwrap().get_field()
|
||||
|
||||
field_ref as *const _ as jfieldID
|
||||
}
|
||||
|
||||
@ -1067,12 +1069,12 @@ unsafe extern "system" fn get_boolean_field(
|
||||
let thread = &*get_thread(env);
|
||||
let object = resolve_object(thread, obj).unwrap();
|
||||
let field_data = &*(field_id as *const FieldData);
|
||||
let field_ref = FieldRef{
|
||||
class: object.lock().unwrap().class.this_class.clone(),
|
||||
let field_ref = FieldRef {
|
||||
class: object.lock().class.this_class.clone(),
|
||||
name: field_data.name.clone(),
|
||||
desc: FieldType::Base(BaseType::Boolean),
|
||||
};
|
||||
let val = object.lock().unwrap().get_field(&field_ref);
|
||||
let val = object.lock().get_field(&field_ref);
|
||||
if let Value::Primitive(Primitive::Boolean(bool)) = val {
|
||||
if bool {
|
||||
JNI_TRUE
|
||||
@ -1416,7 +1418,23 @@ unsafe extern "system" fn get_static_field_id(
|
||||
name: *const c_char,
|
||||
sig: *const c_char,
|
||||
) -> jfieldID {
|
||||
todo!("get_static_field_id")
|
||||
let thread = &*get_thread(env);
|
||||
let klass = thread
|
||||
.loader
|
||||
.lock()
|
||||
.class_from_mirror_id(clazz as u32)
|
||||
.unwrap();
|
||||
|
||||
thread.ensure_initialised(&klass).unwrap();
|
||||
let name = CStr::from_ptr(name).to_str().unwrap();
|
||||
// TODO: check sig when to_descriptor exists
|
||||
|
||||
klass
|
||||
.fields
|
||||
.iter()
|
||||
.find(|f| f.flags.ACC_STATIC && f.name == name)
|
||||
.map(|f| f as *const _ as jfieldID)
|
||||
.unwrap_or(ptr::null_mut())
|
||||
}
|
||||
|
||||
unsafe extern "system" fn get_static_object_field(
|
||||
@ -1437,7 +1455,7 @@ unsafe extern "system" fn get_static_boolean_field(
|
||||
let class = resolve_class(thread, clazz).unwrap();
|
||||
let field_ref = &*(field_id as *const FieldData);
|
||||
let field_again = class.find_field(&field_ref.name, &field_ref.desc).unwrap();
|
||||
let val = field_again.value.lock().unwrap().clone();
|
||||
let val = field_again.value.lock().clone();
|
||||
if let Some(Value::Primitive(Primitive::Boolean(bool))) = val {
|
||||
if bool {
|
||||
JNI_TRUE
|
||||
@ -1507,11 +1525,22 @@ unsafe extern "system" fn get_static_double_field(
|
||||
|
||||
unsafe extern "system" fn set_static_object_field(
|
||||
env: *mut JNIEnv,
|
||||
clazz: jclass,
|
||||
_clazz: jclass,
|
||||
field_id: jfieldID,
|
||||
value: jobject,
|
||||
) {
|
||||
todo!("set_static_object_field")
|
||||
trace!("set_static_object_field");
|
||||
let field_data = &*(field_id as *const FieldData);
|
||||
|
||||
let new_value = if value.is_null() {
|
||||
Value::Reference(None)
|
||||
} else {
|
||||
let thread = &*get_thread(env);
|
||||
let obj_ref = resolve_object(thread, value).unwrap();
|
||||
Value::Reference(Some(ReferenceKind::ObjectReference(obj_ref)))
|
||||
};
|
||||
|
||||
*field_data.value.lock() = Some(new_value);
|
||||
}
|
||||
|
||||
unsafe extern "system" fn set_static_boolean_field(
|
||||
@ -1602,13 +1631,20 @@ unsafe extern "system" fn new_string(
|
||||
String::new()
|
||||
});
|
||||
let str_ref = thread.intern_string(&str); // or non-interned path
|
||||
let str_id = str_ref.lock().unwrap().id;
|
||||
let str_id = str_ref.lock().id;
|
||||
|
||||
str_id as jstring
|
||||
}
|
||||
|
||||
unsafe extern "system" fn get_string_length(env: *mut JNIEnv, str: jstring) -> jsize {
|
||||
todo!("get_string_length")
|
||||
trace!("get_string_length");
|
||||
let thread = &*get_thread(env);
|
||||
let string_obj = resolve_object(thread, str).unwrap();
|
||||
let gc = thread.gc.read();
|
||||
|
||||
let rust_str = gc.transmute_string(string_obj).unwrap();
|
||||
|
||||
rust_str.encode_utf16().count() as jsize
|
||||
}
|
||||
|
||||
unsafe extern "system" fn get_string_chars(
|
||||
@ -1630,7 +1666,6 @@ unsafe extern "system" fn release_string_chars(
|
||||
unsafe extern "system" fn new_string_utf(env: *mut JNIEnv, utf: *const c_char) -> jstring {
|
||||
trace!("new_string_utf");
|
||||
let thread = &*get_thread(env);
|
||||
// let mut gc = thread.gc.write().unwrap();
|
||||
let intern = true;
|
||||
|
||||
let str = CStr::from_ptr(utf).to_str().unwrap();
|
||||
@ -1646,17 +1681,27 @@ unsafe extern "system" fn new_string_utf(env: *mut JNIEnv, utf: *const c_char) -
|
||||
return ptr::null_mut();
|
||||
};
|
||||
|
||||
let str_ref = thread.gc.write().unwrap().new_string(byte_array_class, string_class, str);
|
||||
let str_ref = thread
|
||||
.gc
|
||||
.write()
|
||||
.new_string(byte_array_class, string_class, str);
|
||||
str_ref
|
||||
};
|
||||
|
||||
let str_id = str_ref.lock().unwrap().id;
|
||||
let str_id = str_ref.lock().id;
|
||||
|
||||
str_id as jstring
|
||||
}
|
||||
|
||||
unsafe extern "system" fn get_string_utf_length(env: *mut JNIEnv, str: jstring) -> jsize {
|
||||
todo!("get_string_utf_length")
|
||||
trace!("get_string_utf_length");
|
||||
let thread = &*get_thread(env);
|
||||
let string_obj = resolve_object(thread, str).unwrap();
|
||||
let gc = thread.gc.read();
|
||||
|
||||
let rust_str = gc.transmute_string(string_obj).unwrap();
|
||||
|
||||
rust_str.len() as jsize
|
||||
}
|
||||
|
||||
unsafe extern "system" fn release_string_utf_chars(
|
||||
@ -1678,11 +1723,10 @@ unsafe extern "system" fn new_object_array(
|
||||
init: jobject,
|
||||
) -> jobjectArray {
|
||||
let thread = &*get_thread(env);
|
||||
let mut gc = thread.gc.write().unwrap();
|
||||
let loader = thread.loader.lock().unwrap();
|
||||
let loader = thread.loader.lock();
|
||||
|
||||
let class_id = clazz as u32;
|
||||
let ReferenceKind::ObjectReference(class_ref) = gc.get(class_id) else {
|
||||
let ReferenceKind::ObjectReference(class_ref) = thread.gc.read().get(class_id) else {
|
||||
return ptr::null_mut();
|
||||
};
|
||||
|
||||
@ -1690,15 +1734,16 @@ unsafe extern "system" fn new_object_array(
|
||||
return ptr::null_mut();
|
||||
};
|
||||
|
||||
let ArrayReference::Object(arr_ref) = gc.new_object_array(runtime_class, len) else {
|
||||
let ArrayReference::Object(arr_ref) = thread.gc.write().new_object_array(runtime_class, len)
|
||||
else {
|
||||
return ptr::null_mut();
|
||||
};
|
||||
let arr_id = arr_ref.lock().unwrap().id;
|
||||
let arr_id = arr_ref.lock().id;
|
||||
|
||||
// let Value::Reference(Some(ReferenceKind::ObjectReference(class_name_string))) = class_ref.lock().unwrap().get_field("name") else {
|
||||
// let Value::Reference(Some(ReferenceKind::ObjectReference(class_name_string))) = class_ref.lock().get_field("name") else {
|
||||
// return ptr::null_mut();
|
||||
// };
|
||||
// class_name_string.lock().unwrap().get_field()
|
||||
// class_name_string.lock().get_field()
|
||||
|
||||
arr_id as jobjectArray
|
||||
}
|
||||
@ -1720,13 +1765,13 @@ unsafe extern "system" fn set_object_array_element(
|
||||
let thread = &*get_thread(env);
|
||||
let arr_id = array as u32;
|
||||
let ReferenceKind::ArrayReference(ArrayReference::Object(arr_ref)) =
|
||||
thread.gc.read().unwrap().get(arr_id)
|
||||
thread.gc.read().get(arr_id)
|
||||
else {
|
||||
panic!("Oop")
|
||||
};
|
||||
let obj_id = val as u32;
|
||||
let obj_ref = thread.gc.read().unwrap().get(obj_id);
|
||||
arr_ref.lock().unwrap().set(index, Some(obj_ref))
|
||||
let obj_ref = thread.gc.read().get(obj_id);
|
||||
arr_ref.lock().set(index, Some(obj_ref))
|
||||
}
|
||||
|
||||
unsafe extern "system" fn new_boolean_array(env: *mut JNIEnv, len: jsize) -> jbooleanArray {
|
||||
@ -2080,7 +2125,17 @@ unsafe extern "system" fn get_string_region(
|
||||
len: jsize,
|
||||
buf: *mut jchar,
|
||||
) {
|
||||
todo!("get_string_region")
|
||||
trace!("get_string_region");
|
||||
let thread = &*get_thread(env);
|
||||
let string_obj = resolve_object(thread, str).unwrap();
|
||||
let gc = thread.gc.read();
|
||||
|
||||
let rust_str = gc.transmute_string(string_obj).unwrap();
|
||||
|
||||
let utf16: Vec<u16> = rust_str.encode_utf16().collect();
|
||||
let region = &utf16[start as usize..(start + len) as usize];
|
||||
|
||||
ptr::copy_nonoverlapping(region.as_ptr(), buf, len as usize);
|
||||
}
|
||||
|
||||
unsafe extern "system" fn get_string_utf_region(
|
||||
@ -2090,7 +2145,36 @@ unsafe extern "system" fn get_string_utf_region(
|
||||
len: jsize,
|
||||
buf: *mut c_char,
|
||||
) {
|
||||
todo!("get_string_utf_region")
|
||||
trace!(
|
||||
"get_string_utf_region: start={}, len={}, buf={:?}",
|
||||
start,
|
||||
len,
|
||||
buf
|
||||
);
|
||||
let thread = &*get_thread(env);
|
||||
let string_obj = resolve_object(thread, str).unwrap();
|
||||
let gc = thread.gc.read();
|
||||
|
||||
let rust_str = gc.transmute_string(string_obj).unwrap();
|
||||
trace!(
|
||||
"get_string_utf_region: rust_str={:?}, rust_str.len()={}",
|
||||
rust_str,
|
||||
rust_str.len()
|
||||
);
|
||||
|
||||
let utf16: Vec<u16> = rust_str.encode_utf16().collect();
|
||||
trace!("get_string_utf_region: utf16.len()={}", utf16.len());
|
||||
|
||||
let region = &utf16[start as usize..(start + len) as usize];
|
||||
let region_str = String::from_utf16(region).unwrap();
|
||||
trace!(
|
||||
"get_string_utf_region: region_str.len()={}",
|
||||
region_str.len()
|
||||
);
|
||||
|
||||
ptr::copy_nonoverlapping(region_str.as_ptr(), buf as *mut u8, region_str.len());
|
||||
// Add null terminator for compatibility with native code that expects C strings
|
||||
*buf.add(region_str.len()) = 0;
|
||||
}
|
||||
|
||||
unsafe extern "system" fn get_primitive_array_critical(
|
||||
@ -2135,7 +2219,7 @@ unsafe extern "system" fn delete_weak_global_ref(env: *mut JNIEnv, ref_: jweak)
|
||||
}
|
||||
|
||||
unsafe extern "system" fn exception_check(env: *mut JNIEnv) -> jboolean {
|
||||
error!("exception_check");
|
||||
warn!("exception_check");
|
||||
JNI_FALSE
|
||||
}
|
||||
|
||||
3
crates/core/src/native/mod.rs
Normal file
3
crates/core/src/native/mod.rs
Normal file
@ -0,0 +1,3 @@
|
||||
pub mod jni;
|
||||
mod native_libraries;
|
||||
pub mod r#unsafe;
|
||||
47
crates/core/src/native/unsafe.rs
Normal file
47
crates/core/src/native/unsafe.rs
Normal 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),
|
||||
}
|
||||
@ -1,10 +1,11 @@
|
||||
use crate::class::RuntimeClass;
|
||||
use crate::error::VmError;
|
||||
use crate::objects::object::{Reference, ReferenceKind};
|
||||
use crate::prim::Primitive;
|
||||
use jni::sys::{jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jshort};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use parking_lot::Mutex;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use crate::class::RuntimeClass;
|
||||
use crate::error::VmError;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ArrayReference {
|
||||
@ -22,43 +23,58 @@ pub enum ArrayReference {
|
||||
impl ArrayReference {
|
||||
pub fn len(&self) -> jint {
|
||||
match self {
|
||||
ArrayReference::Int(x) => x.lock().unwrap().len(),
|
||||
ArrayReference::Byte(x) => x.lock().unwrap().len(),
|
||||
ArrayReference::Short(x) => x.lock().unwrap().len(),
|
||||
ArrayReference::Long(x) => x.lock().unwrap().len(),
|
||||
ArrayReference::Float(x) => x.lock().unwrap().len(),
|
||||
ArrayReference::Double(x) => x.lock().unwrap().len(),
|
||||
ArrayReference::Char(x) => x.lock().unwrap().len(),
|
||||
ArrayReference::Boolean(x) => x.lock().unwrap().len(),
|
||||
ArrayReference::Object(x) => x.lock().unwrap().len(),
|
||||
ArrayReference::Int(x) => x.lock().len(),
|
||||
ArrayReference::Byte(x) => x.lock().len(),
|
||||
ArrayReference::Short(x) => x.lock().len(),
|
||||
ArrayReference::Long(x) => x.lock().len(),
|
||||
ArrayReference::Float(x) => x.lock().len(),
|
||||
ArrayReference::Double(x) => x.lock().len(),
|
||||
ArrayReference::Char(x) => x.lock().len(),
|
||||
ArrayReference::Boolean(x) => x.lock().len(),
|
||||
ArrayReference::Object(x) => x.lock().len(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn id(&self) -> u32 {
|
||||
match self {
|
||||
ArrayReference::Int(x) => x.lock().unwrap().id,
|
||||
ArrayReference::Byte(x) => x.lock().unwrap().id,
|
||||
ArrayReference::Short(x) => x.lock().unwrap().id,
|
||||
ArrayReference::Long(x) => x.lock().unwrap().id,
|
||||
ArrayReference::Float(x) => x.lock().unwrap().id,
|
||||
ArrayReference::Double(x) => x.lock().unwrap().id,
|
||||
ArrayReference::Char(x) => x.lock().unwrap().id,
|
||||
ArrayReference::Boolean(x) => x.lock().unwrap().id,
|
||||
ArrayReference::Object(x) => x.lock().unwrap().id,
|
||||
ArrayReference::Int(x) => x.lock().id,
|
||||
ArrayReference::Byte(x) => x.lock().id,
|
||||
ArrayReference::Short(x) => x.lock().id,
|
||||
ArrayReference::Long(x) => x.lock().id,
|
||||
ArrayReference::Float(x) => x.lock().id,
|
||||
ArrayReference::Double(x) => x.lock().id,
|
||||
ArrayReference::Char(x) => x.lock().id,
|
||||
ArrayReference::Boolean(x) => x.lock().id,
|
||||
ArrayReference::Object(x) => x.lock().id,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn class(&self) -> Arc<RuntimeClass> {
|
||||
match self {
|
||||
ArrayReference::Int(x) => x.lock().unwrap().class.clone(),
|
||||
ArrayReference::Byte(x) => x.lock().unwrap().class.clone(),
|
||||
ArrayReference::Short(x) => x.lock().unwrap().class.clone(),
|
||||
ArrayReference::Long(x) => x.lock().unwrap().class.clone(),
|
||||
ArrayReference::Float(x) => x.lock().unwrap().class.clone(),
|
||||
ArrayReference::Double(x) => x.lock().unwrap().class.clone(),
|
||||
ArrayReference::Char(x) => x.lock().unwrap().class.clone(),
|
||||
ArrayReference::Boolean(x) => x.lock().unwrap().class.clone(),
|
||||
ArrayReference::Object(x) => x.lock().unwrap().class.clone(),
|
||||
ArrayReference::Int(x) => x.lock().class.clone(),
|
||||
ArrayReference::Byte(x) => x.lock().class.clone(),
|
||||
ArrayReference::Short(x) => x.lock().class.clone(),
|
||||
ArrayReference::Long(x) => x.lock().class.clone(),
|
||||
ArrayReference::Float(x) => x.lock().class.clone(),
|
||||
ArrayReference::Double(x) => x.lock().class.clone(),
|
||||
ArrayReference::Char(x) => x.lock().class.clone(),
|
||||
ArrayReference::Boolean(x) => x.lock().class.clone(),
|
||||
ArrayReference::Object(x) => x.lock().class.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the size in bytes of the array's backing storage
|
||||
pub fn size_bytes(&self) -> usize {
|
||||
match self {
|
||||
ArrayReference::Int(x) => x.lock().backing.len() * size_of::<jint>(),
|
||||
ArrayReference::Byte(x) => x.lock().backing.len() * size_of::<jbyte>(),
|
||||
ArrayReference::Short(x) => x.lock().backing.len() * size_of::<jshort>(),
|
||||
ArrayReference::Long(x) => x.lock().backing.len() * size_of::<jlong>(),
|
||||
ArrayReference::Float(x) => x.lock().backing.len() * size_of::<jfloat>(),
|
||||
ArrayReference::Double(x) => x.lock().backing.len() * size_of::<jdouble>(),
|
||||
ArrayReference::Char(x) => x.lock().backing.len() * size_of::<jchar>(),
|
||||
ArrayReference::Boolean(x) => x.lock().backing.len() * size_of::<jboolean>(),
|
||||
ArrayReference::Object(x) => x.lock().backing.len() * size_of::<Reference>(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -71,7 +87,7 @@ pub type ObjectArrayReference = Arc<Mutex<Array<Option<ReferenceKind>>>>;
|
||||
pub struct Array<T: ArrayValue> {
|
||||
pub(crate) id: u32,
|
||||
pub(crate) class: Arc<RuntimeClass>,
|
||||
pub(crate) backing: Box<[T]>,
|
||||
pub backing: Box<[T]>,
|
||||
}
|
||||
|
||||
impl<T> Array<T>
|
||||
@ -97,15 +113,23 @@ where
|
||||
pub fn len(&self) -> jint {
|
||||
self.backing.len() as jint
|
||||
}
|
||||
// fn from(value: (u32, Arc<RuntimeClass>, Box<[T]>)) -> Self {
|
||||
// let (id, class, vector) = value;
|
||||
// Self {
|
||||
// id,
|
||||
// class,
|
||||
// backing: vector,
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
impl<T: Primitive + ArrayValue> From<(u32, Arc<RuntimeClass>, Vec<T>)> for Array<T> {
|
||||
fn from(value: (u32, Arc<RuntimeClass>, Vec<T>)) -> Self {
|
||||
let (id, class, vector) = value;
|
||||
impl<T: ArrayValue> From<(u32, Arc<RuntimeClass>, Box<[T]>)> for Array<T> {
|
||||
fn from(value: (u32, Arc<RuntimeClass>, Box<[T]>)) -> Self {
|
||||
let (id, class, values) = value;
|
||||
Self {
|
||||
id,
|
||||
class,
|
||||
backing: vector.into_boxed_slice(),
|
||||
backing: values,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -139,7 +163,6 @@ impl ArrayValue for jdouble {}
|
||||
|
||||
impl ArrayValue for jboolean {}
|
||||
|
||||
|
||||
impl ArrayReference {
|
||||
pub fn copy_from(
|
||||
&self,
|
||||
@ -149,32 +172,36 @@ impl ArrayReference {
|
||||
length: jint,
|
||||
) -> Result<(), VmError> {
|
||||
macro_rules! copy {
|
||||
($src_arr:expr, $dst_arr:expr) => {{
|
||||
let src_guard = $src_arr.lock().unwrap();
|
||||
let mut dst_guard = $dst_arr.lock().unwrap();
|
||||
($src_arr:expr, $dst_arr:expr) => {{
|
||||
let src_guard = $src_arr.lock();
|
||||
let mut dst_guard = $dst_arr.lock();
|
||||
|
||||
let src_start = src_pos as usize;
|
||||
let dst_start = dst_pos as usize;
|
||||
let len = length as usize;
|
||||
let src_start = src_pos as usize;
|
||||
let dst_start = dst_pos as usize;
|
||||
let len = length as usize;
|
||||
|
||||
// Bounds check
|
||||
if src_pos < 0 || dst_pos < 0 || length < 0
|
||||
|| src_start + len > src_guard.backing.len()
|
||||
|| dst_start + len > dst_guard.backing.len()
|
||||
{
|
||||
return Err(VmError::InvariantError("Index oob".to_string()));
|
||||
}
|
||||
// Bounds check
|
||||
if src_pos < 0
|
||||
|| dst_pos < 0
|
||||
|| length < 0
|
||||
|| src_start + len > src_guard.backing.len()
|
||||
|| dst_start + len > dst_guard.backing.len()
|
||||
{
|
||||
return Err(VmError::InvariantError("Index oob".to_string()));
|
||||
}
|
||||
|
||||
if Arc::ptr_eq($src_arr, $dst_arr) {
|
||||
drop(src_guard);
|
||||
dst_guard.backing.copy_within(src_start..src_start + len, dst_start);
|
||||
} else {
|
||||
dst_guard.backing[dst_start..dst_start + len]
|
||||
.copy_from_slice(&src_guard.backing[src_start..src_start + len]);
|
||||
}
|
||||
Ok(())
|
||||
}};
|
||||
}
|
||||
if Arc::ptr_eq($src_arr, $dst_arr) {
|
||||
drop(src_guard);
|
||||
dst_guard
|
||||
.backing
|
||||
.copy_within(src_start..src_start + len, dst_start);
|
||||
} else {
|
||||
dst_guard.backing[dst_start..dst_start + len]
|
||||
.copy_from_slice(&src_guard.backing[src_start..src_start + len]);
|
||||
}
|
||||
Ok(())
|
||||
}};
|
||||
}
|
||||
|
||||
use ArrayReference::*;
|
||||
match (src, self) {
|
||||
@ -188,14 +215,14 @@ impl ArrayReference {
|
||||
(Boolean(s), Boolean(d)) => copy!(s, d),
|
||||
(Object(s), Object(d)) => {
|
||||
// Object arrays need clone, not copy
|
||||
let src_guard = s.lock().unwrap();
|
||||
let mut dst_guard = d.lock().unwrap();
|
||||
|
||||
let src_guard = s.lock();
|
||||
let mut dst_guard = d.lock();
|
||||
let src_start = src_pos as usize;
|
||||
let dst_start = dst_pos as usize;
|
||||
let len = length as usize;
|
||||
|
||||
if src_pos < 0 || dst_pos < 0 || length < 0
|
||||
if src_pos < 0
|
||||
|| dst_pos < 0 || length < 0
|
||||
|| src_start + len > src_guard.backing.len()
|
||||
|| dst_start + len > dst_guard.backing.len()
|
||||
{
|
||||
@ -204,7 +231,11 @@ impl ArrayReference {
|
||||
|
||||
if Arc::ptr_eq(s, d) {
|
||||
drop(src_guard);
|
||||
for i in if src_start < dst_start { (0..len).rev().collect::<Vec<_>>() } else { (0..len).collect() } {
|
||||
for i in if src_start < dst_start {
|
||||
(0..len).rev().collect::<Vec<_>>()
|
||||
} else {
|
||||
(0..len).collect()
|
||||
} {
|
||||
dst_guard.backing[dst_start + i] = dst_guard.backing[src_start + i].clone();
|
||||
}
|
||||
} else {
|
||||
|
||||
@ -1,14 +1,16 @@
|
||||
use crate::class::RuntimeClass;
|
||||
use crate::class_file::FieldRef;
|
||||
use crate::error::VmError;
|
||||
use crate::objects::array::{ArrayReference, ObjectArrayReference, PrimitiveArrayReference};
|
||||
use crate::prim::{jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jshort};
|
||||
use crate::value::Value;
|
||||
use crate::{BaseType, FieldType};
|
||||
use dashmap::DashMap;
|
||||
use log::trace;
|
||||
use parking_lot::Mutex;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::hash::Hash;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use crate::class_file::FieldRef;
|
||||
use crate::{string_from_bytes, BaseType, FieldType};
|
||||
use std::sync::Arc;
|
||||
|
||||
pub type ObjectReference = Arc<Mutex<Object>>;
|
||||
|
||||
@ -30,26 +32,9 @@ impl Object {
|
||||
trace!("Fields for object:\n\t{:#}", self.format_fields());
|
||||
self.fields
|
||||
.get(&field_ref.name)
|
||||
.map(|e| e.clone())
|
||||
.unwrap_or_else(||{
|
||||
let initial = match &field_ref.desc {
|
||||
FieldType::Base(base) => {
|
||||
match base {
|
||||
BaseType::Byte => {
|
||||
Value::from(0i8)
|
||||
}
|
||||
BaseType::Char => { Value::from(0u16) }
|
||||
BaseType::Double => { Value::from(0f64) }
|
||||
BaseType::Float => { Value::from(0f32) }
|
||||
BaseType::Int => { Value::from(0i32) }
|
||||
BaseType::Long => { Value::from(0i64) }
|
||||
BaseType::Short => { Value::from(0i16) }
|
||||
BaseType::Boolean => { Value::from(false) }
|
||||
}
|
||||
}
|
||||
FieldType::ClassType(_) => { Value::NULL }
|
||||
FieldType::ArrayType(_) => { Value::NULL }
|
||||
};
|
||||
.map(|r| r.value().clone())
|
||||
.unwrap_or_else(|| {
|
||||
let initial = field_ref.desc.default_value();
|
||||
self.fields.insert(field_ref.name.clone(), initial.clone());
|
||||
initial
|
||||
})
|
||||
@ -98,21 +83,33 @@ pub enum ReferenceKind {
|
||||
}
|
||||
|
||||
impl ReferenceKind {
|
||||
pub fn into_object_reference(self) -> Option<ObjectReference> {
|
||||
pub fn try_into_object_reference(&self) -> Result<ObjectReference, VmError> {
|
||||
match self {
|
||||
Self::ObjectReference(inner) => Some(inner),
|
||||
_ => None,
|
||||
Self::ObjectReference(obj) => Ok(obj.clone()),
|
||||
_ => Err(VmError::InvariantError(format!(
|
||||
"Expected object, found {}",
|
||||
self.to_string()
|
||||
))),
|
||||
}
|
||||
}
|
||||
pub fn try_into_array_reference(&self) -> Result<ArrayReference, VmError> {
|
||||
match self {
|
||||
Self::ArrayReference(arr) => Ok(arr.clone()),
|
||||
_ => Err(VmError::InvariantError(format!(
|
||||
"Expected array, found {}",
|
||||
self.to_string()
|
||||
))),
|
||||
}
|
||||
}
|
||||
pub fn id(&self) -> u32 {
|
||||
match self {
|
||||
Self::ObjectReference(r) => r.lock().unwrap().id,
|
||||
Self::ObjectReference(r) => r.lock().id,
|
||||
Self::ArrayReference(a) => a.id(),
|
||||
}
|
||||
}
|
||||
pub fn class(&self) -> Arc<RuntimeClass> {
|
||||
match self {
|
||||
Self::ObjectReference(r) => r.lock().unwrap().class.clone(),
|
||||
Self::ObjectReference(r) => r.lock().class.clone(),
|
||||
Self::ArrayReference(a) => a.class(),
|
||||
}
|
||||
}
|
||||
@ -124,52 +121,158 @@ impl Display for ReferenceKind {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
let id = match self {
|
||||
ReferenceKind::ObjectReference(x) => {
|
||||
let guard = x.lock().unwrap();
|
||||
let guard = x.lock();
|
||||
if guard.class.this_class == "java/lang/String"
|
||||
&& let Some(field) = guard.fields.get("value")
|
||||
&& let Value::Reference(Some(ReferenceKind::ArrayReference(ArrayReference::Byte(actual)))) = field.value()
|
||||
&& let Value::Reference(Some(ReferenceKind::ArrayReference(
|
||||
ArrayReference::Byte(actual),
|
||||
))) = field.value()
|
||||
{
|
||||
let arr_guard= actual.lock().unwrap();
|
||||
let string = crate::string_from_bytes(&arr_guard);
|
||||
let arr_guard = actual.lock();
|
||||
let string = string_from_bytes(&arr_guard);
|
||||
format!("\u{AB}{}\u{BB}", string)
|
||||
} else {
|
||||
format!("Obj<{}>", guard.class.this_class)
|
||||
}
|
||||
}
|
||||
ReferenceKind::ArrayReference(ArrayReference::Int(x)) => {
|
||||
let guard = x.lock().unwrap();
|
||||
format!("int{:?}", guard.backing)
|
||||
let guard = x.lock();
|
||||
let backing = &guard.backing;
|
||||
if backing.len() > 10 {
|
||||
format!(
|
||||
"int[{:?}, {:?}, {:?}, ..., {:?}, {:?}, {:?}]",
|
||||
backing[0],
|
||||
backing[1],
|
||||
backing[2],
|
||||
backing[backing.len() - 3],
|
||||
backing[backing.len() - 2],
|
||||
backing[backing.len() - 1]
|
||||
)
|
||||
} else {
|
||||
format!("int{:?}", backing)
|
||||
}
|
||||
}
|
||||
ReferenceKind::ArrayReference(ArrayReference::Byte(x)) => {
|
||||
let guard = x.lock().unwrap();
|
||||
format!("byte{:?}", guard.backing)
|
||||
let guard = x.lock();
|
||||
let backing = &guard.backing;
|
||||
if backing.len() > 10 {
|
||||
format!(
|
||||
"byte[{:?}, {:?}, {:?}, ..., {:?}, {:?}, {:?}]",
|
||||
backing[0],
|
||||
backing[1],
|
||||
backing[2],
|
||||
backing[backing.len() - 3],
|
||||
backing[backing.len() - 2],
|
||||
backing[backing.len() - 1]
|
||||
)
|
||||
} else {
|
||||
format!("byte{:?}", backing)
|
||||
}
|
||||
}
|
||||
ReferenceKind::ArrayReference(ArrayReference::Short(x)) => {
|
||||
let guard = x.lock().unwrap();
|
||||
format!("short{:?}", guard.backing)
|
||||
let guard = x.lock();
|
||||
let backing = &guard.backing;
|
||||
if backing.len() > 10 {
|
||||
format!(
|
||||
"short[{:?}, {:?}, {:?}, ..., {:?}, {:?}, {:?}]",
|
||||
backing[0],
|
||||
backing[1],
|
||||
backing[2],
|
||||
backing[backing.len() - 3],
|
||||
backing[backing.len() - 2],
|
||||
backing[backing.len() - 1]
|
||||
)
|
||||
} else {
|
||||
format!("short{:?}", backing)
|
||||
}
|
||||
}
|
||||
ReferenceKind::ArrayReference(ArrayReference::Long(x)) => {
|
||||
let guard = x.lock().unwrap();
|
||||
format!("long{:?}", guard.backing)
|
||||
let guard = x.lock();
|
||||
let backing = &guard.backing;
|
||||
if backing.len() > 10 {
|
||||
format!(
|
||||
"long[{:?}, {:?}, {:?}, ..., {:?}, {:?}, {:?}]",
|
||||
backing[0],
|
||||
backing[1],
|
||||
backing[2],
|
||||
backing[backing.len() - 3],
|
||||
backing[backing.len() - 2],
|
||||
backing[backing.len() - 1]
|
||||
)
|
||||
} else {
|
||||
format!("long{:?}", backing)
|
||||
}
|
||||
}
|
||||
ReferenceKind::ArrayReference(ArrayReference::Float(x)) => {
|
||||
let guard = x.lock().unwrap();
|
||||
format!("float{:?}", guard.backing)
|
||||
let guard = x.lock();
|
||||
let backing = &guard.backing;
|
||||
if backing.len() > 10 {
|
||||
format!(
|
||||
"float[{:?}, {:?}, {:?}, ..., {:?}, {:?}, {:?}]",
|
||||
backing[0],
|
||||
backing[1],
|
||||
backing[2],
|
||||
backing[backing.len() - 3],
|
||||
backing[backing.len() - 2],
|
||||
backing[backing.len() - 1]
|
||||
)
|
||||
} else {
|
||||
format!("float{:?}", backing)
|
||||
}
|
||||
}
|
||||
ReferenceKind::ArrayReference(ArrayReference::Double(x)) => {
|
||||
let guard = x.lock().unwrap();
|
||||
format!("double{:?}", guard.backing)
|
||||
let guard = x.lock();
|
||||
let backing = &guard.backing;
|
||||
if backing.len() > 10 {
|
||||
format!(
|
||||
"double[{:?}, {:?}, {:?}, ..., {:?}, {:?}, {:?}]",
|
||||
backing[0],
|
||||
backing[1],
|
||||
backing[2],
|
||||
backing[backing.len() - 3],
|
||||
backing[backing.len() - 2],
|
||||
backing[backing.len() - 1]
|
||||
)
|
||||
} else {
|
||||
format!("double{:?}", backing)
|
||||
}
|
||||
}
|
||||
ReferenceKind::ArrayReference(ArrayReference::Char(x)) => {
|
||||
let guard = x.lock().unwrap();
|
||||
format!("char{:?}", guard.backing)
|
||||
let guard = x.lock();
|
||||
let backing = &guard.backing;
|
||||
if backing.len() > 10 {
|
||||
format!(
|
||||
"char[{:?}, {:?}, {:?}, ..., {:?}, {:?}, {:?}]",
|
||||
backing[0],
|
||||
backing[1],
|
||||
backing[2],
|
||||
backing[backing.len() - 3],
|
||||
backing[backing.len() - 2],
|
||||
backing[backing.len() - 1]
|
||||
)
|
||||
} else {
|
||||
format!("char{:?}", backing)
|
||||
}
|
||||
}
|
||||
ReferenceKind::ArrayReference(ArrayReference::Boolean(x)) => {
|
||||
let guard = x.lock().unwrap();
|
||||
format!("boolean{:?}", guard.backing)
|
||||
let guard = x.lock();
|
||||
let backing = &guard.backing;
|
||||
if backing.len() > 10 {
|
||||
format!(
|
||||
"boolean[{:?}, {:?}, {:?}, ..., {:?}, {:?}, {:?}]",
|
||||
backing[0],
|
||||
backing[1],
|
||||
backing[2],
|
||||
backing[backing.len() - 3],
|
||||
backing[backing.len() - 2],
|
||||
backing[backing.len() - 1]
|
||||
)
|
||||
} else {
|
||||
format!("boolean{:?}", backing)
|
||||
}
|
||||
}
|
||||
ReferenceKind::ArrayReference(ArrayReference::Object(x)) => {
|
||||
let guard = x.lock().unwrap();
|
||||
let guard = x.lock();
|
||||
format!("object[{:?}]", guard.id)
|
||||
}
|
||||
};
|
||||
@ -236,3 +339,20 @@ impl From<ObjectArrayReference> for ReferenceKind {
|
||||
Self::ArrayReference(ArrayReference::Object(value))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn string_from_bytes(byte_slice: &[jbyte]) -> String {
|
||||
// Convert the byte array (i8) to UTF-16 code units
|
||||
// The bytes are stored as UTF-16 LE (little-endian) pairs
|
||||
let bytes: Vec<u8> = byte_slice.iter().map(|&b| b as u8).collect();
|
||||
|
||||
// Convert pairs of bytes to u16 (UTF-16 code units)
|
||||
let mut utf16_chars = Vec::new();
|
||||
for chunk in bytes.chunks_exact(2) {
|
||||
let code_unit = u16::from_le_bytes([chunk[0], chunk[1]]);
|
||||
utf16_chars.push(code_unit);
|
||||
}
|
||||
|
||||
// Convert UTF-16 to Rust String (UTF-8)
|
||||
let rust_string = String::from_utf16_lossy(&utf16_chars);
|
||||
rust_string
|
||||
}
|
||||
|
||||
@ -1,25 +1,70 @@
|
||||
use crate::attributes::ArrayType;
|
||||
use crate::class::RuntimeClass;
|
||||
use crate::class_file::ClassFlags;
|
||||
use crate::objects;
|
||||
use crate::objects::array::{Array, ArrayReference, ArrayValue, PrimitiveArrayReference};
|
||||
use crate::objects::object::{Object, ObjectReference, Reference, ReferenceKind};
|
||||
use crate::rng::generate_identity_hash;
|
||||
use crate::class_file::{ClassFlags, MethodData};
|
||||
use crate::error::VmError;
|
||||
use crate::objects::array::{
|
||||
Array, ArrayReference, ArrayValue, ObjectArrayReference, PrimitiveArrayReference,
|
||||
};
|
||||
use crate::objects::object::{
|
||||
string_from_bytes, Object, ObjectReference, Reference, ReferenceKind,
|
||||
};
|
||||
use crate::value::{Primitive, Value};
|
||||
use crate::{objects, rng, ThreadId};
|
||||
use dashmap::DashMap;
|
||||
use jni::sys::{jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jshort};
|
||||
use log::warn;
|
||||
use parking_lot::{Condvar, Mutex};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::sync::atomic::{AtomicU32, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ObjectManager {
|
||||
pub objects: HashMap<u32, ReferenceKind>,
|
||||
strings: HashMap<String, u32>,
|
||||
monitors: DashMap<u32, Arc<Monitor>>,
|
||||
max_memory: usize,
|
||||
next_id: AtomicU32,
|
||||
}
|
||||
|
||||
impl ObjectManager {
|
||||
pub fn max_memory(&self) -> usize {
|
||||
self.max_memory
|
||||
}
|
||||
|
||||
fn next_id(&self) -> u32 {
|
||||
self.next_id.fetch_add(1, Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// Returns the total bytes in use by all managed objects and arrays
|
||||
pub fn bytes_in_use(&self) -> usize {
|
||||
self.objects
|
||||
.values()
|
||||
.map(|kind| match kind {
|
||||
ReferenceKind::ObjectReference(r) => {
|
||||
let guard = r.lock();
|
||||
guard
|
||||
.fields
|
||||
.iter()
|
||||
.map(|entry| entry.value().size_bytes())
|
||||
.sum()
|
||||
}
|
||||
ReferenceKind::ArrayReference(a) => a.size_bytes(),
|
||||
})
|
||||
.sum()
|
||||
}
|
||||
|
||||
pub fn new_object(&mut self, class: Arc<RuntimeClass>) -> ObjectReference {
|
||||
let id = generate_identity_hash();
|
||||
let id = self.next_id();
|
||||
if self.objects.contains_key(&id) {
|
||||
let next = self.next_id();
|
||||
let key_pairs = self
|
||||
.objects
|
||||
.iter()
|
||||
.map(|(id, obj)| format!("id: {id} obj: {}", obj.class().this_class))
|
||||
.collect::<Vec<_>>();
|
||||
println!("next: {} all: \n{:#?}", next, key_pairs)
|
||||
}
|
||||
assert!(
|
||||
!self.objects.contains_key(&id),
|
||||
"Generated ID already exists!"
|
||||
@ -33,29 +78,42 @@ impl ObjectManager {
|
||||
object
|
||||
}
|
||||
|
||||
pub fn new_primitive_array(&mut self, class: Arc<RuntimeClass>, array_type: ArrayType, count: i32) -> ArrayReference {
|
||||
let id = generate_identity_hash();
|
||||
pub fn new_primitive_array(
|
||||
&mut self,
|
||||
class: Arc<RuntimeClass>,
|
||||
array_type: ArrayType,
|
||||
count: i32,
|
||||
) -> ArrayReference {
|
||||
let id = self.next_id();
|
||||
assert!(
|
||||
!self.objects.contains_key(&id),
|
||||
"Generated ID already exists!"
|
||||
);
|
||||
|
||||
let array_ref = match array_type {
|
||||
ArrayType::T_INT => ArrayReference::Int(Arc::new(Mutex::new(Array::new(id, class,count)))),
|
||||
ArrayType::T_BYTE => ArrayReference::Byte(Arc::new(Mutex::new(Array::new(id, class,count)))),
|
||||
ArrayType::T_SHORT => {
|
||||
ArrayReference::Short(Arc::new(Mutex::new(Array::new(id, class,count))))
|
||||
ArrayType::T_INT => {
|
||||
ArrayReference::Int(Arc::new(Mutex::new(Array::new(id, class, count))))
|
||||
}
|
||||
ArrayType::T_BYTE => {
|
||||
ArrayReference::Byte(Arc::new(Mutex::new(Array::new(id, class, count))))
|
||||
}
|
||||
ArrayType::T_SHORT => {
|
||||
ArrayReference::Short(Arc::new(Mutex::new(Array::new(id, class, count))))
|
||||
}
|
||||
ArrayType::T_LONG => {
|
||||
ArrayReference::Long(Arc::new(Mutex::new(Array::new(id, class, count))))
|
||||
}
|
||||
ArrayType::T_LONG => ArrayReference::Long(Arc::new(Mutex::new(Array::new(id, class,count)))),
|
||||
ArrayType::T_FLOAT => {
|
||||
ArrayReference::Float(Arc::new(Mutex::new(Array::new(id, class,count))))
|
||||
ArrayReference::Float(Arc::new(Mutex::new(Array::new(id, class, count))))
|
||||
}
|
||||
ArrayType::T_DOUBLE => {
|
||||
ArrayReference::Double(Arc::new(Mutex::new(Array::new(id, class,count))))
|
||||
ArrayReference::Double(Arc::new(Mutex::new(Array::new(id, class, count))))
|
||||
}
|
||||
ArrayType::T_CHAR => {
|
||||
ArrayReference::Char(Arc::new(Mutex::new(Array::new(id, class, count))))
|
||||
}
|
||||
ArrayType::T_CHAR => ArrayReference::Char(Arc::new(Mutex::new(Array::new(id, class,count)))),
|
||||
ArrayType::T_BOOLEAN => {
|
||||
ArrayReference::Boolean(Arc::new(Mutex::new(Array::new(id, class,count))))
|
||||
ArrayReference::Boolean(Arc::new(Mutex::new(Array::new(id, class, count))))
|
||||
}
|
||||
};
|
||||
|
||||
@ -65,25 +123,49 @@ impl ObjectManager {
|
||||
}
|
||||
|
||||
pub fn new_object_array(&mut self, class: Arc<RuntimeClass>, count: i32) -> ArrayReference {
|
||||
let id = generate_identity_hash();
|
||||
let id = self.next_id();
|
||||
assert!(
|
||||
!self.objects.contains_key(&id),
|
||||
"Generated ID already exists!"
|
||||
);
|
||||
let array_ref = ArrayReference::Object(Arc::new(Mutex::new(Array::new(id, class,count))));
|
||||
let array_ref = ArrayReference::Object(Arc::new(Mutex::new(Array::new(id, class, count))));
|
||||
|
||||
self.objects
|
||||
.insert(id, ReferenceKind::ArrayReference(array_ref.clone()));
|
||||
array_ref
|
||||
}
|
||||
pub fn new_byte_array(&mut self, class: Arc<RuntimeClass>, vector: Vec<i8>) -> ArrayReference {
|
||||
warn!("Manual sidechannel byte array creation");
|
||||
let id = generate_identity_hash();
|
||||
|
||||
pub fn new_object_array_from(
|
||||
&mut self,
|
||||
class: Arc<RuntimeClass>,
|
||||
values: Box<[Reference]>,
|
||||
) -> ArrayReference {
|
||||
let id = self.next_id();
|
||||
assert!(
|
||||
!self.objects.contains_key(&id),
|
||||
"Generated ID already exists!"
|
||||
);
|
||||
let array_ref = ArrayReference::Byte(Arc::new(Mutex::new(Array::from((id, class,vector)))));
|
||||
let array_ref =
|
||||
ArrayReference::Object(Arc::new(Mutex::new(Array::from((id, class, values)))));
|
||||
|
||||
self.objects
|
||||
.insert(id, ReferenceKind::ArrayReference(array_ref.clone()));
|
||||
array_ref
|
||||
}
|
||||
|
||||
pub fn new_byte_array(
|
||||
&mut self,
|
||||
class: Arc<RuntimeClass>,
|
||||
vector: Box<[i8]>,
|
||||
) -> ArrayReference {
|
||||
warn!("Manual sidechannel byte array creation");
|
||||
let id = self.next_id();
|
||||
assert!(
|
||||
!self.objects.contains_key(&id),
|
||||
"Generated ID already exists!"
|
||||
);
|
||||
let array_ref =
|
||||
ArrayReference::Byte(Arc::new(Mutex::new(Array::from((id, class, vector)))));
|
||||
|
||||
self.objects
|
||||
.insert(id, ReferenceKind::ArrayReference(array_ref.clone()));
|
||||
@ -91,7 +173,6 @@ impl ObjectManager {
|
||||
}
|
||||
|
||||
pub fn get(&self, id: u32) -> ReferenceKind {
|
||||
|
||||
self.objects
|
||||
.get(&id)
|
||||
.unwrap_or_else(|| {
|
||||
@ -100,18 +181,23 @@ impl ObjectManager {
|
||||
.iter()
|
||||
.map(|(x, y)| format!("{x} : {y}"))
|
||||
.collect::<Vec<_>>();
|
||||
panic!("Object must be present id: {id}\n{objs:#?}") })
|
||||
panic!("Object must be present id: {id}\n{objs:#?}")
|
||||
})
|
||||
.clone()
|
||||
}
|
||||
|
||||
pub fn get_interned_string(&self, utf: &str) -> Option<ObjectReference> {
|
||||
self.strings
|
||||
.get(utf)
|
||||
.map(|e| self.get(*e))
|
||||
.and_then(ReferenceKind::into_object_reference)
|
||||
.and_then(|e| self.get(*e).try_into_object_reference().ok())
|
||||
}
|
||||
|
||||
pub fn new_string(&mut self, byte_class: Arc<RuntimeClass>, string_class: Arc<RuntimeClass>, utf8: &str) -> ObjectReference {
|
||||
pub fn new_string(
|
||||
&mut self,
|
||||
byte_array_class: Arc<RuntimeClass>,
|
||||
string_class: Arc<RuntimeClass>,
|
||||
utf8: &str,
|
||||
) -> ObjectReference {
|
||||
warn!("Manual sidechannel string creation: \n\"{}\"", utf8);
|
||||
let key = utf8.to_owned();
|
||||
let jstr = self.new_object(string_class);
|
||||
@ -120,26 +206,23 @@ impl ObjectManager {
|
||||
.flat_map(|e| e.to_le_bytes())
|
||||
.map(|e| e as i8)
|
||||
.collect::<Vec<_>>();
|
||||
let barray = self.new_byte_array(byte_class, byte_vec);
|
||||
let barray = self.new_byte_array(byte_array_class, byte_vec.into_boxed_slice());
|
||||
|
||||
jstr.lock().unwrap().fields.insert(
|
||||
jstr.lock().fields.insert(
|
||||
"value".to_string(),
|
||||
Value::from(Some(ReferenceKind::ArrayReference(barray))),
|
||||
);
|
||||
jstr.lock()
|
||||
.unwrap()
|
||||
.fields
|
||||
.insert("coder".to_string(), Value::from(1i8));
|
||||
jstr.lock()
|
||||
.unwrap()
|
||||
.fields
|
||||
.insert("hash".to_string(), Value::from(0i32));
|
||||
jstr.lock()
|
||||
.unwrap()
|
||||
.fields
|
||||
.insert("hashIsZero".to_string(), Value::from(false));
|
||||
|
||||
let id = jstr.lock().unwrap().id;
|
||||
let id = jstr.lock().id;
|
||||
debug_assert!(!self.strings.contains_key(&key), "String already interned");
|
||||
self.strings.insert(key, id);
|
||||
jstr
|
||||
@ -159,14 +242,284 @@ impl ObjectManager {
|
||||
|
||||
let class_redefined_count = 0i32;
|
||||
let clazz = self.new_object(class_class);
|
||||
if let Ok(clakz) = clazz.lock() {
|
||||
{
|
||||
let clakz = clazz.lock();
|
||||
clakz.set_field("name", Value::from(name));
|
||||
clakz.set_field("module", Value::from(module));
|
||||
clakz.set_field("modifiers", Value::from(modifiers));
|
||||
clakz.set_field("primitive", Value::from(primitive));
|
||||
clakz.set_field("classRedefinedCount", Value::from(class_redefined_count));
|
||||
}
|
||||
|
||||
clazz
|
||||
}
|
||||
/// using pure wizardry, transmute a string object into a rust string
|
||||
pub fn transmute_string(&self, string: ObjectReference) -> Result<String, VmError> {
|
||||
let guard = string.lock();
|
||||
let value_field = guard
|
||||
.fields
|
||||
.get("value")
|
||||
.ok_or_else(|| VmError::InvariantError("String must have value field".to_string()))?;
|
||||
|
||||
let array_ref = match *value_field {
|
||||
Value::Reference(Some(ReferenceKind::ArrayReference(ref arr))) => arr.clone(),
|
||||
_ => {
|
||||
return Err(VmError::InvariantError(
|
||||
"String value field must be a byte array".to_string(),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
let bytes = match array_ref {
|
||||
ArrayReference::Byte(byte_arr) => {
|
||||
let arr_guard = byte_arr.lock();
|
||||
arr_guard.backing.clone()
|
||||
}
|
||||
_ => {
|
||||
return Err(VmError::InvariantError(
|
||||
"String value must be a byte array".to_string(),
|
||||
))
|
||||
}
|
||||
};
|
||||
Ok(string_from_bytes(&bytes))
|
||||
}
|
||||
pub fn monitor_enter(&self, thread_id: ThreadId, obj_id: u32) {
|
||||
let monitor = self
|
||||
.monitors
|
||||
.entry(obj_id)
|
||||
.or_insert_with(|| Arc::new(Monitor::new()))
|
||||
.clone();
|
||||
|
||||
// DashMap entry guard dropped here, map unlocked
|
||||
|
||||
let mut inner = monitor.inner.lock();
|
||||
|
||||
while inner.owner.is_some() && inner.owner != Some(thread_id) {
|
||||
monitor.condvar.wait(&mut inner);
|
||||
}
|
||||
|
||||
inner.owner = Some(thread_id);
|
||||
inner.entry_count += 1;
|
||||
}
|
||||
|
||||
pub fn monitor_exit(&self, thread_id: ThreadId, obj_id: u32) -> Result<(), VmError> {
|
||||
let monitor = self
|
||||
.monitors
|
||||
.get(&obj_id)
|
||||
.ok_or_else(|| VmError::InvariantError("Monitor not found for object".to_string()))?
|
||||
.clone();
|
||||
|
||||
let mut inner = monitor.inner.lock();
|
||||
|
||||
if inner.owner != Some(thread_id) {
|
||||
return Err(VmError::InvariantError(
|
||||
"Thread does not own monitor".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
inner.entry_count -= 1;
|
||||
|
||||
if inner.entry_count == 0 {
|
||||
inner.owner = None;
|
||||
monitor.condvar.notify_one();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn clone_object(&mut self, source: &ReferenceKind) -> Result<ReferenceKind, VmError> {
|
||||
match source {
|
||||
ReferenceKind::ArrayReference(arr) => self.clone_array(arr),
|
||||
ReferenceKind::ObjectReference(obj) => {
|
||||
let class = obj.lock().class.clone();
|
||||
if !class.implements("java/lang/Cloneable") {
|
||||
return Err(VmError::InvariantError("CloneNotSupported".to_string()));
|
||||
}
|
||||
self.clone_instance(obj)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn clone_array(&mut self, arr: &ArrayReference) -> Result<ReferenceKind, VmError> {
|
||||
let new_arr = match arr {
|
||||
ArrayReference::Int(a) => {
|
||||
let guard = a.lock();
|
||||
let new_id = self.next_id();
|
||||
ArrayReference::Int(Arc::new(Mutex::new(Array {
|
||||
id: new_id,
|
||||
class: guard.class.clone(),
|
||||
backing: guard.backing.clone(),
|
||||
})))
|
||||
}
|
||||
ArrayReference::Byte(a) => {
|
||||
let guard = a.lock();
|
||||
let new_id = self.next_id();
|
||||
ArrayReference::Byte(Arc::new(Mutex::new(Array {
|
||||
id: new_id,
|
||||
class: guard.class.clone(),
|
||||
backing: guard.backing.clone(),
|
||||
})))
|
||||
}
|
||||
ArrayReference::Short(a) => {
|
||||
let guard = a.lock();
|
||||
let new_id = self.next_id();
|
||||
ArrayReference::Short(Arc::new(Mutex::new(Array {
|
||||
id: new_id,
|
||||
class: guard.class.clone(),
|
||||
backing: guard.backing.clone(),
|
||||
})))
|
||||
}
|
||||
ArrayReference::Long(a) => {
|
||||
let guard = a.lock();
|
||||
let new_id = self.next_id();
|
||||
ArrayReference::Long(Arc::new(Mutex::new(Array {
|
||||
id: new_id,
|
||||
class: guard.class.clone(),
|
||||
backing: guard.backing.clone(),
|
||||
})))
|
||||
}
|
||||
ArrayReference::Float(a) => {
|
||||
let guard = a.lock();
|
||||
let new_id = self.next_id();
|
||||
ArrayReference::Float(Arc::new(Mutex::new(Array {
|
||||
id: new_id,
|
||||
class: guard.class.clone(),
|
||||
backing: guard.backing.clone(),
|
||||
})))
|
||||
}
|
||||
ArrayReference::Double(a) => {
|
||||
let guard = a.lock();
|
||||
let new_id = self.next_id();
|
||||
ArrayReference::Double(Arc::new(Mutex::new(Array {
|
||||
id: new_id,
|
||||
class: guard.class.clone(),
|
||||
backing: guard.backing.clone(),
|
||||
})))
|
||||
}
|
||||
ArrayReference::Char(a) => {
|
||||
let guard = a.lock();
|
||||
let new_id = self.next_id();
|
||||
ArrayReference::Char(Arc::new(Mutex::new(Array {
|
||||
id: new_id,
|
||||
class: guard.class.clone(),
|
||||
backing: guard.backing.clone(),
|
||||
})))
|
||||
}
|
||||
ArrayReference::Boolean(a) => {
|
||||
let guard = a.lock();
|
||||
let new_id = self.next_id();
|
||||
ArrayReference::Boolean(Arc::new(Mutex::new(Array {
|
||||
id: new_id,
|
||||
class: guard.class.clone(),
|
||||
backing: guard.backing.clone(),
|
||||
})))
|
||||
}
|
||||
ArrayReference::Object(a) => {
|
||||
let guard = a.lock();
|
||||
let new_id = self.next_id();
|
||||
ArrayReference::Object(Arc::new(Mutex::new(Array {
|
||||
id: new_id,
|
||||
class: guard.class.clone(),
|
||||
backing: guard.backing.clone(), // shallow clone - copies references, not objects
|
||||
})))
|
||||
}
|
||||
};
|
||||
|
||||
let id = new_arr.id();
|
||||
self.objects
|
||||
.insert(id, ReferenceKind::ArrayReference(new_arr.clone()));
|
||||
Ok(ReferenceKind::ArrayReference(new_arr))
|
||||
}
|
||||
|
||||
fn clone_instance(&mut self, obj: &ObjectReference) -> Result<ReferenceKind, VmError> {
|
||||
let guard = obj.lock();
|
||||
let class = guard.class.clone();
|
||||
|
||||
// Create a new object with the same class
|
||||
let new_obj = self.new_object(class);
|
||||
|
||||
// Copy all fields (shallow copy)
|
||||
{
|
||||
let new_guard = new_obj.lock();
|
||||
for entry in guard.fields.iter() {
|
||||
new_guard
|
||||
.fields
|
||||
.insert(entry.key().clone(), entry.value().clone());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(ReferenceKind::ObjectReference(new_obj))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Monitor {
|
||||
inner: Mutex<MonitorInner>,
|
||||
condvar: Condvar,
|
||||
}
|
||||
|
||||
struct MonitorInner {
|
||||
owner: Option<ThreadId>,
|
||||
entry_count: u32,
|
||||
}
|
||||
|
||||
impl Monitor {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
inner: Mutex::new(MonitorInner {
|
||||
owner: None,
|
||||
entry_count: 0,
|
||||
}),
|
||||
condvar: Condvar::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//reflection
|
||||
impl ObjectManager {
|
||||
fn get_from_klass(&self, klass: Arc<RuntimeClass>) -> ReferenceKind {
|
||||
self.get(klass.mirror())
|
||||
}
|
||||
pub fn new_constructor(
|
||||
&mut self,
|
||||
index: jint,
|
||||
modifiers: jint,
|
||||
parameters: Box<[Arc<RuntimeClass>]>,
|
||||
exceptions: Box<[Arc<RuntimeClass>]>,
|
||||
class_array_class: Arc<RuntimeClass>,
|
||||
declaring_class: Arc<RuntimeClass>,
|
||||
constructor_class: Arc<RuntimeClass>,
|
||||
) -> ObjectReference {
|
||||
let parameters = parameters
|
||||
.iter()
|
||||
.map(|c| {
|
||||
let mirror_id = c.mirror.get().unwrap();
|
||||
Some(self.get(*mirror_id))
|
||||
})
|
||||
.collect::<Box<[_]>>();
|
||||
let exceptions = exceptions
|
||||
.iter()
|
||||
.map(|c| {
|
||||
let mirror_id = c.mirror.get().unwrap();
|
||||
Some(self.get(*mirror_id))
|
||||
})
|
||||
.collect::<Box<[_]>>();
|
||||
let constructor_object = self.new_object(constructor_class);
|
||||
let parameter_types = self.new_object_array_from(class_array_class.clone(), parameters);
|
||||
let exception_types = self.new_object_array_from(class_array_class, exceptions);
|
||||
let signature = Value::NULL;
|
||||
let annotations = Value::NULL;
|
||||
let parameter_annotations = Value::NULL;
|
||||
{
|
||||
let obj = constructor_object.lock();
|
||||
obj.set_field("clazz", Value::from(self.get_from_klass(declaring_class)));
|
||||
obj.set_field("slot", Value::from(index));
|
||||
obj.set_field("parameterTypes", Value::from(parameter_types));
|
||||
obj.set_field("exceptionTypes", Value::from(exception_types));
|
||||
obj.set_field("modifiers", Value::from(modifiers));
|
||||
obj.set_field("override", Value::from(false));
|
||||
obj.set_field("signature", signature);
|
||||
obj.set_field("annotations", annotations);
|
||||
obj.set_field("parameterAnnotations", parameter_annotations);
|
||||
}
|
||||
constructor_object
|
||||
}
|
||||
}
|
||||
|
||||
@ -39,16 +39,17 @@ fn os_random() -> u32 {
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
static XORSHIFT_STATE: Cell<[u32; 4]> = {
|
||||
Cell::new([
|
||||
os_random(), // X: seeded from global Park-Miller RNG
|
||||
842502087, // Y: constant
|
||||
0x8767, // Z: constant
|
||||
273326509, // W: constant
|
||||
])
|
||||
};
|
||||
static XORSHIFT_STATE: Cell<[u32; 4]> = {
|
||||
Cell::new([
|
||||
os_random(), // X: seeded from global Park-Miller RNG
|
||||
842502087, // Y: constant
|
||||
0x8767, // Z: constant
|
||||
273326509, // W: constant
|
||||
])
|
||||
};
|
||||
}
|
||||
|
||||
#[deprecated]
|
||||
pub fn generate_identity_hash() -> u32 {
|
||||
XORSHIFT_STATE.with(|state| {
|
||||
let mut s = state.get();
|
||||
@ -67,3 +68,9 @@ pub fn generate_identity_hash() -> u32 {
|
||||
v
|
||||
})
|
||||
}
|
||||
|
||||
static NEXT_ID: AtomicU32 = AtomicU32::new(1);
|
||||
|
||||
pub fn sequential_id() -> u32 {
|
||||
NEXT_ID.fetch_add(1, Ordering::Relaxed)
|
||||
}
|
||||
|
||||
@ -1,28 +1,34 @@
|
||||
use crate::class::RuntimeClass;
|
||||
use crate::class::{InitState, RuntimeClass};
|
||||
use crate::class_file::{ClassFile, MethodData, MethodRef};
|
||||
use crate::class_loader::{ClassLoader, LoaderRef};
|
||||
use crate::jni::create_jni_function_table;
|
||||
use crate::error::VmError;
|
||||
use crate::frame::Frame;
|
||||
use crate::native::jni::create_jni_function_table;
|
||||
use crate::objects::object::{ObjectReference, ReferenceKind};
|
||||
use crate::objects::object_manager::ObjectManager;
|
||||
use crate::value::{Primitive, Value};
|
||||
use crate::vm::Vm;
|
||||
use crate::{BaseType, FieldType, Frame, MethodDescriptor, ThreadId};
|
||||
use crate::{stack_used, BaseType, FieldType, MethodDescriptor, ThreadId};
|
||||
use deku::DekuError::Incomplete;
|
||||
use itertools::Itertools;
|
||||
use jni::sys::{jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jobject, jshort, JNIEnv};
|
||||
use libffi::low::call;
|
||||
use libffi::middle::*;
|
||||
use log::{trace, warn};
|
||||
use log::{info, trace, warn, LevelFilter};
|
||||
|
||||
use parking_lot::{Mutex, Once, ReentrantMutex, RwLock};
|
||||
use std::any::Any;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::VecDeque;
|
||||
use std::ops::{Add, Deref};
|
||||
use std::ptr::null_mut;
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
use std::vec::IntoIter;
|
||||
use crate::error::VmError;
|
||||
use std::sync::Arc;
|
||||
|
||||
use std::thread;
|
||||
use std::vec::IntoIter;
|
||||
|
||||
static INIT_LOGGER: Once = Once::new();
|
||||
type MethodCallResult = Result<Option<Value>, VmError>;
|
||||
|
||||
// Thread-local storage for current thread ID
|
||||
@ -37,7 +43,7 @@ pub struct VmThread {
|
||||
pub id: ThreadId,
|
||||
pub vm: Arc<Vm>,
|
||||
pub loader: Arc<Mutex<ClassLoader>>,
|
||||
pub frame_stack: Mutex<Vec<Arc<Mutex<Frame>>>>,
|
||||
pub frame_stack: Mutex<Vec<Frame>>,
|
||||
pub gc: Arc<RwLock<ObjectManager>>,
|
||||
pub jni_env: JNIEnv,
|
||||
}
|
||||
@ -80,15 +86,11 @@ impl VmThread {
|
||||
vm.threads.get(&id).unwrap().clone()
|
||||
}
|
||||
|
||||
/// Get or resolve a class, ensuring it and its dependencies are initialized.
|
||||
/// Follows JVM Spec 5.5 for recursive initialization handling.
|
||||
/*/// Get or resolve a class, ensuring it and its dependencies are initialised.
|
||||
/// Follows JVM Spec 5.5 for recursive initialisation handling.
|
||||
pub fn get_or_resolve_class(&self, what: &str) -> Result<Arc<RuntimeClass>, VmError> {
|
||||
// Phase 1: Load the class (short lock)
|
||||
let runtime_class = self
|
||||
.loader
|
||||
.lock()
|
||||
.unwrap()
|
||||
.get_or_load(what, None)?;
|
||||
let runtime_class = self.loader.lock().unwrap().get_or_load(what, None, true)?;
|
||||
|
||||
// Phase 2: Collect classes that need initialisation (short lock)
|
||||
let classes_to_init = {
|
||||
@ -104,26 +106,22 @@ impl VmThread {
|
||||
}
|
||||
|
||||
Ok(runtime_class)
|
||||
}
|
||||
}*/
|
||||
|
||||
pub fn get_class(&self, what: &str) -> Result<Arc<RuntimeClass>, VmError> {
|
||||
self.loader
|
||||
.lock()
|
||||
.unwrap()
|
||||
.get_or_load(what, None)
|
||||
let class = self.loader.lock().get_or_load(what, None)?;
|
||||
self.create_mirror_class(&class)?;
|
||||
Ok(class)
|
||||
}
|
||||
|
||||
/// Initialize a class following JVM Spec 5.5.
|
||||
/// Handles recursive initialization by tracking which thread is initializing.
|
||||
fn init(&self, class: Arc<RuntimeClass>) -> Result<(), VmError> {
|
||||
use crate::class::InitState;
|
||||
use std::thread;
|
||||
|
||||
pub fn init(&self, class: Arc<RuntimeClass>) -> Result<(), VmError> {
|
||||
let current_thread = thread::current().id();
|
||||
|
||||
// Check and update initialization state
|
||||
{
|
||||
let mut state = class.init_state.lock().unwrap();
|
||||
let mut state = class.init_state.lock();
|
||||
match &*state {
|
||||
InitState::Initialized => {
|
||||
// Already initialized, nothing to do
|
||||
@ -157,17 +155,6 @@ impl VmThread {
|
||||
}
|
||||
}
|
||||
}
|
||||
let class_class = self.get_class("java/lang/Class")?;
|
||||
let string = self.intern_string(&class.this_class);
|
||||
let class_obj = self.gc.write().unwrap().new_class(
|
||||
class_class,
|
||||
Some(ReferenceKind::ObjectReference(string)),
|
||||
None,
|
||||
class.access_flags,
|
||||
false
|
||||
);
|
||||
let id = class_obj.lock().unwrap().id;
|
||||
class.mirror.set(id).expect("woops, id already set");
|
||||
// Perform actual initialisation
|
||||
trace!("Initializing class: {}", class.this_class);
|
||||
let result = (|| {
|
||||
@ -176,16 +163,24 @@ impl VmThread {
|
||||
self.init(super_class.clone())?;
|
||||
}
|
||||
|
||||
// Run <clinit> if present
|
||||
if let Ok(method) = class.find_method("<clinit>", &MethodDescriptor::void()) {
|
||||
self.execute_method(&class, &method, vec![])?;
|
||||
if !class.access_flags.INTERFACE {
|
||||
for interface in class.interfaces.iter() {
|
||||
if interface.has_default_method() {
|
||||
self.init(interface.clone())?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Run <clinit> if present (note: <clinit> is NOT inherited, only look in this class)
|
||||
if let Some(method) = class.methods.iter().find(|m| m.name == "<clinit>") {
|
||||
self.execute_method(&class, method, vec![])?;
|
||||
}
|
||||
Ok(())
|
||||
})();
|
||||
|
||||
// Update state based on result
|
||||
{
|
||||
let mut state = class.init_state.lock().unwrap();
|
||||
let mut state = class.init_state.lock();
|
||||
match result {
|
||||
Ok(_) => {
|
||||
*state = InitState::Initialized;
|
||||
@ -200,6 +195,85 @@ impl VmThread {
|
||||
result
|
||||
}
|
||||
|
||||
pub fn ensure_initialised(&self, class: &Arc<RuntimeClass>) -> Result<(), VmError> {
|
||||
let current_thread = thread::current().id();
|
||||
|
||||
{
|
||||
let mut state = class.init_state.lock();
|
||||
match &*state {
|
||||
InitState::Initialized => return Ok(()),
|
||||
InitState::Initializing(tid) if *tid == current_thread => return Ok(()),
|
||||
InitState::Initializing(_) => {
|
||||
return Err(VmError::LoaderError(format!(
|
||||
"Class {} is being initialized by another thread",
|
||||
class.this_class
|
||||
)));
|
||||
}
|
||||
InitState::Error(msg) => {
|
||||
return Err(VmError::LoaderError(format!(
|
||||
"Class {} initialization previously failed: {}",
|
||||
class.this_class, msg
|
||||
)));
|
||||
}
|
||||
InitState::NotInitialized => {
|
||||
*state = InitState::Initializing(current_thread);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let result = (|| {
|
||||
if let Some(ref super_class) = class.super_class {
|
||||
self.ensure_initialised(super_class)?;
|
||||
}
|
||||
|
||||
if !class.access_flags.INTERFACE {
|
||||
for interface in class.interfaces.iter() {
|
||||
if interface.has_default_method() {
|
||||
self.ensure_initialised(interface)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(method) = class.methods.iter().find(|m| m.name == "<clinit>") {
|
||||
self.execute_method(class, method, vec![])?;
|
||||
}
|
||||
Ok(())
|
||||
})();
|
||||
|
||||
{
|
||||
let mut state = class.init_state.lock();
|
||||
match &result {
|
||||
Ok(_) => *state = InitState::Initialized,
|
||||
Err(e) => *state = InitState::Error(format!("{:?}", e)),
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// creates a mirror java/lang/Class Object and binds it to this RuntimeClass
|
||||
fn create_mirror_class(&self, class: &Arc<RuntimeClass>) -> Result<(), VmError> {
|
||||
if class.mirror.get().is_some() || class.mirror_in_progress.swap(true, Ordering::SeqCst) {
|
||||
return Ok(()); // already has a mirror
|
||||
}
|
||||
let class_class = if class.this_class == "java/lang/Class" {
|
||||
Arc::clone(class)
|
||||
} else {
|
||||
self.get_class("java/lang/Class")?
|
||||
};
|
||||
let string = self.intern_string(&class.this_class);
|
||||
let class_obj = self.gc.write().new_class(
|
||||
class_class,
|
||||
Some(ReferenceKind::ObjectReference(string)),
|
||||
None,
|
||||
class.access_flags,
|
||||
false,
|
||||
);
|
||||
let id = class_obj.lock().id;
|
||||
class.mirror.set(id).expect("woops, id already set");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn invoke_main(&self, what: &str) -> Result<(), VmError> {
|
||||
let method_ref = MethodRef {
|
||||
class: what.to_string(),
|
||||
@ -212,56 +286,46 @@ impl VmThread {
|
||||
}
|
||||
|
||||
pub fn invoke(&self, method_reference: MethodRef, args: Vec<Value>) -> MethodCallResult {
|
||||
if method_reference.class.contains("Unsafe") {
|
||||
println!("()")
|
||||
if self.gc.read().objects.len() > 2210 {
|
||||
INIT_LOGGER.call_once(|| {
|
||||
env_logger::Builder::from_default_env()
|
||||
.filter_level(LevelFilter::Trace)
|
||||
.filter_module("deku", LevelFilter::Warn)
|
||||
.filter_module("roast_vm_core::class_file::class_file", LevelFilter::Info)
|
||||
.filter_module("roast_vm_core::attributes", LevelFilter::Info)
|
||||
.filter_module("roast_vm_core::instructions", LevelFilter::Info)
|
||||
.init();
|
||||
});
|
||||
// println!("heap length {}", self.gc.read().objects.len())
|
||||
}
|
||||
let class = self.get_or_resolve_class(&method_reference.class)?;
|
||||
let method = class.find_method(&method_reference.name, &method_reference.desc).unwrap();
|
||||
let class = self.get_class(&method_reference.class)?;
|
||||
let method = class.find_method(&method_reference.name, &method_reference.desc)?;
|
||||
let class = self.loader.lock().get_or_load(&*method.class, None)?;
|
||||
|
||||
self.execute_method(&class, &method, args)
|
||||
}
|
||||
|
||||
pub fn invoke_virtual(&self, method_reference: MethodRef, class: Arc<RuntimeClass>, args: Vec<Value>) -> MethodCallResult {
|
||||
let method = class.find_method(&method_reference.name, &method_reference.desc).unwrap();
|
||||
pub fn invoke_virtual(
|
||||
&self,
|
||||
method_reference: MethodRef,
|
||||
class: Arc<RuntimeClass>,
|
||||
args: Vec<Value>,
|
||||
) -> MethodCallResult {
|
||||
let method = class.find_method(&method_reference.name, &method_reference.desc)?;
|
||||
|
||||
let class = self.loader.lock().get_or_load(&*method.class, None)?;
|
||||
self.execute_method(&class, &method, args)
|
||||
}
|
||||
|
||||
/*pub fn invoke_old(&self, method_reference: MethodRef, mut args: Vec<Value>) -> MethodCallResult {
|
||||
let mut new_args = Vec::new();
|
||||
let class = self.get_or_resolve_class(&method_reference.class)?;
|
||||
let resolved_method = class
|
||||
.find_method(&method_reference.name, &method_reference.desc)
|
||||
.unwrap();
|
||||
trace!(
|
||||
"invoking '{}' from {}",
|
||||
method_reference.name,
|
||||
class.this_class
|
||||
);
|
||||
if resolved_method.flags.ACC_NATIVE {
|
||||
if resolved_method.flags.ACC_STATIC {
|
||||
let jclass = self.vm.gc.read().unwrap().get(*class.mirror.wait());
|
||||
new_args.push(Value::Reference(Some(jclass)));
|
||||
}
|
||||
for arg in args {
|
||||
new_args.push(arg)
|
||||
}
|
||||
return self.invoke_native(&method_reference, new_args);
|
||||
}
|
||||
let mut frame = Frame::new(
|
||||
method_reference.clone(),
|
||||
resolved_method.code.clone().unwrap(),
|
||||
class.constant_pool.clone(),
|
||||
args,
|
||||
self.vm.clone(),
|
||||
);
|
||||
self.frame_stack.lock().unwrap().push(frame.clone());
|
||||
let result = frame.execute()?;
|
||||
self.frame_stack.lock().unwrap().pop();
|
||||
Ok(result)
|
||||
}*/
|
||||
|
||||
pub fn invoke_native(&self, method: &MethodRef, mut args: Vec<Value>) -> MethodCallResult {
|
||||
let symbol_name = generate_jni_method_name(method, false);
|
||||
trace!("searching for native symbol: {:?}", &symbol_name);
|
||||
println!("Invoke native for native symbol: {:?}", &symbol_name);
|
||||
|
||||
// if symbol_name.contains("Java_jdk_internal_reflect_Reflection_getClassAccessFlags") {
|
||||
// return Err(VmError::Debug(
|
||||
// "RoastVM specific implementation required for Java_jdk_internal_reflect_Reflection_getClassAccessFlags",
|
||||
// ));
|
||||
// }
|
||||
|
||||
if symbol_name.contains("Java_java_lang_Class_desiredAssertionStatus0") {
|
||||
warn!("MAJOR HACK, figure out what is wrong with desiredAssertionStatus0");
|
||||
@ -276,15 +340,12 @@ impl VmThread {
|
||||
let name_with_params = generate_jni_method_name(method, true);
|
||||
self.vm.find_native_method(&name_with_params)
|
||||
})
|
||||
.ok_or(VmError::NativeError(format!("Link error: Unable to locate symbol {symbol_name}")))?;
|
||||
.ok_or(VmError::NativeError(format!(
|
||||
"Link error: Unable to locate symbol {symbol_name}"
|
||||
)))?;
|
||||
// build pointer to native fn
|
||||
let cp = CodePtr::from_ptr(p);
|
||||
|
||||
// let args = build_args(args);
|
||||
|
||||
// coerce my method descriptors into libffi C equivalents, then call
|
||||
// let l = method.build_cif().call::<jlong>(cp, args.as_ref());
|
||||
|
||||
let mut storage = Vec::new();
|
||||
trace!("passing {} to native fn", Value::format_vec(&args));
|
||||
let deq_args = VecDeque::from(args);
|
||||
@ -342,7 +403,7 @@ impl VmThread {
|
||||
Ok(Some(Value::Reference(None)))
|
||||
} else {
|
||||
// Look up the object in the ObjectManager
|
||||
let gc = self.gc.read().unwrap();
|
||||
let gc = self.gc.read();
|
||||
let reference_kind = gc.get(obj_id);
|
||||
Ok(Some(Value::Reference(Some(reference_kind))))
|
||||
}
|
||||
@ -358,64 +419,65 @@ impl VmThread {
|
||||
method: &MethodData,
|
||||
args: Vec<Value>,
|
||||
) -> MethodCallResult {
|
||||
eprintln!("[DEBUG] execute_method self.id = {:?}", self.id);
|
||||
info!("Executing {}", method.name.clone());
|
||||
warn!("Stack used: {}", stack_used());
|
||||
let method_ref = MethodRef {
|
||||
class: class.this_class.clone(),
|
||||
name: method.name.clone(),
|
||||
desc: method.desc.clone(),
|
||||
};
|
||||
|
||||
|
||||
if method.flags.ACC_NATIVE {
|
||||
let mut native_args = Vec::new();
|
||||
if method.flags.ACC_STATIC {
|
||||
let jclass = self.vm.gc.read().unwrap().get(*class.mirror.wait());
|
||||
let jclass = self.vm.gc.read().get(*class.mirror.wait());
|
||||
native_args.push(Value::Reference(Some(jclass)));
|
||||
}
|
||||
native_args.extend(args);
|
||||
return self.invoke_native(&method_ref, native_args);
|
||||
let res = self.invoke_native(&method_ref, native_args);
|
||||
// println!("Returning from native: {}.{}", &method_ref.class, &method_ref.name);
|
||||
return res;
|
||||
}
|
||||
|
||||
let mut frame = Frame::new(
|
||||
class.clone(),
|
||||
method_ref,
|
||||
method_ref.clone(),
|
||||
method.code.clone().unwrap(),
|
||||
class.constant_pool.clone(),
|
||||
args,
|
||||
self.vm.clone(),
|
||||
method.line_number_table.clone(),
|
||||
);
|
||||
let frame = Arc::new(Mutex::new(frame));
|
||||
self.frame_stack.lock().unwrap().push(frame.clone());
|
||||
eprintln!("[DEBUG] pushed frame for {}.{}, stack depth now: {}",
|
||||
class.this_class, method.name,
|
||||
self.frame_stack.lock().unwrap().len());
|
||||
let result = frame.lock().unwrap().execute();
|
||||
eprintln!("[DEBUG] returned from {}.{}, result ok: {}, stack depth: {}",
|
||||
class.this_class, method.name, result.is_ok(),
|
||||
self.frame_stack.lock().unwrap().len());
|
||||
// let frame = Arc::new(ReentrantMutex::new(frame));
|
||||
self.frame_stack.lock().push(frame.clone());
|
||||
// println!("Invoke method: {}.{}", &method_ref.class, &method_ref.name);
|
||||
let result = frame.execute();
|
||||
if result.is_ok() {
|
||||
self.frame_stack.lock().unwrap().pop();
|
||||
self.frame_stack.lock().pop();
|
||||
}
|
||||
// println!("Returning from method: {}.{}", &method_ref.class, &method_ref.name);
|
||||
result
|
||||
}
|
||||
pub fn print_stack_trace(&self) {
|
||||
let guard = self.frame_stack.lock().unwrap();
|
||||
// Reverse - most recent frame first (like Java does)
|
||||
for frame in guard.iter().rev() {
|
||||
let frame = frame.lock().unwrap();
|
||||
let method = &frame.method_ref;
|
||||
// Internal format uses '/', Java stack traces use '.'
|
||||
let class_name = method.class.replace("/", ".");
|
||||
|
||||
|
||||
match (&frame.class.source_file, &frame.current_line_number()) {
|
||||
(Some(file), Some(line)) => eprintln!("\tat {}.{}({}:{})", class_name, method.name, file, line),
|
||||
(Some(file), None) => eprintln!("\tat {}.{}({})", class_name, method.name, file),
|
||||
_ => eprintln!("\tat {}.{}(Unknown Source)", class_name, method.name),
|
||||
}
|
||||
}
|
||||
}
|
||||
// pub fn print_stack_trace(&mut self) {
|
||||
// // Get a lock on the frame stack
|
||||
// let guard = self.frame_stack.lock();
|
||||
// // Reverse - most recent frame first (like Java does)
|
||||
// for frame_arc in guard.iter().rev() {
|
||||
// // Get a lock on the individual frame
|
||||
// let frame = frame_arc.lock();
|
||||
// let method = &frame.method_ref;
|
||||
// // Internal format uses '/', Java stack traces use '.'
|
||||
// let class_name = method.class.replace("/", ".");
|
||||
//
|
||||
// match (&frame.class.source_file, &frame.current_line_number()) {
|
||||
// (Some(file), Some(line)) => {
|
||||
// eprintln!("\tat {}.{}({}:{})", class_name, method.name, file, line)
|
||||
// }
|
||||
// (Some(file), None) => eprintln!("\tat {}.{}({})", class_name, method.name, file),
|
||||
// _ => eprintln!("\tat {}.{}(Unknown Source)", class_name, method.name),
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
fn build_args<'a>(
|
||||
@ -429,9 +491,7 @@ fn build_args<'a>(
|
||||
// Slot 1: this (instance) or class (static) — first param either way
|
||||
let receiver = params.pop_front();
|
||||
let receiver_id = match receiver {
|
||||
Some(Value::Reference(Some(ReferenceKind::ObjectReference(ref_kind)))) => {
|
||||
ref_kind.lock().unwrap().id
|
||||
} // however you get the u32 ID
|
||||
Some(Value::Reference(Some(ref_kind))) => ref_kind.id(),
|
||||
Some(Value::Reference(None)) => 0, // null
|
||||
_ => panic!("first arg must be reference"),
|
||||
};
|
||||
@ -451,7 +511,9 @@ fn build_args<'a>(
|
||||
let id = x.map(|r| r.id()).unwrap_or(0) as jobject;
|
||||
storage.push(Box::new(id));
|
||||
}
|
||||
Value::Padding => { panic!("Uhh not possible chief") }
|
||||
Value::Padding => {
|
||||
panic!("Uhh not possible chief")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -529,21 +591,15 @@ impl VmThread {
|
||||
/// it will look for an already created string, and if its exists, return it
|
||||
/// if not, will cause a new String to be made, which at the time always interns it
|
||||
pub fn intern_string(&self, utf: &str) -> ObjectReference {
|
||||
// Fast path: read lock
|
||||
if let Some(existing) = self.gc.read().unwrap().get_interned_string(utf) {
|
||||
return existing;
|
||||
}
|
||||
|
||||
// Slow path: write lock with re-check
|
||||
let mut gc = self.gc.write().unwrap();
|
||||
|
||||
// Another thread may have inserted while we waited for the write lock
|
||||
if let Some(existing) = gc.get_interned_string(utf) {
|
||||
if let Some(existing) = self.gc.read().get_interned_string(utf) {
|
||||
return existing;
|
||||
}
|
||||
|
||||
let string_class = self.get_class("java/lang/String").unwrap();
|
||||
let byte_array_class = self.get_class("[B").unwrap();
|
||||
gc.new_string(byte_array_class, string_class, utf)
|
||||
|
||||
let mut gc = self.gc.write();
|
||||
gc.get_interned_string(utf)
|
||||
.unwrap_or_else(|| gc.new_string(byte_array_class, string_class, utf))
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
use crate::error::VmError;
|
||||
use crate::objects::array::ArrayReference;
|
||||
use crate::objects::object::{ObjectReference, ReferenceKind};
|
||||
use crate::{BaseType, FieldType};
|
||||
@ -6,8 +7,8 @@ use dashmap::DashMap;
|
||||
use jni::sys::{jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jshort};
|
||||
use log::trace;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::mem::{size_of, size_of_val};
|
||||
use std::ops::Deref;
|
||||
use crate::error::VmError;
|
||||
|
||||
/// A reference-counted, thread-safe pointer to an Object.
|
||||
|
||||
@ -24,7 +25,7 @@ pub enum Value {
|
||||
Padding,
|
||||
}
|
||||
|
||||
#[derive( Clone, Default)]
|
||||
#[derive(Clone, Default)]
|
||||
pub struct OperandStack(Vec<Value>);
|
||||
|
||||
impl OperandStack {
|
||||
@ -33,9 +34,7 @@ impl OperandStack {
|
||||
}
|
||||
pub fn with_capacity(max_stack: usize) -> Self {
|
||||
let backing = Vec::with_capacity(max_stack);
|
||||
Self {
|
||||
0: backing
|
||||
}
|
||||
Self { 0: backing }
|
||||
}
|
||||
|
||||
pub fn push(&mut self, value: Value) {
|
||||
@ -43,12 +42,14 @@ impl OperandStack {
|
||||
}
|
||||
|
||||
pub fn pop(&mut self) -> Result<Value, VmError> {
|
||||
self.0.pop()
|
||||
self.0
|
||||
.pop()
|
||||
.ok_or_else(|| VmError::StackError("Stack underflow".to_string()))
|
||||
}
|
||||
|
||||
pub fn peek(&self) -> Result<&Value, VmError> {
|
||||
self.0.last()
|
||||
self.0
|
||||
.last()
|
||||
.ok_or_else(|| VmError::StackError("Stack underflow on peek".to_string()))
|
||||
}
|
||||
|
||||
@ -62,7 +63,9 @@ impl OperandStack {
|
||||
|
||||
/// Pop n values, returned in the order they were pushed (not pop order)
|
||||
pub fn pop_n(&mut self, n: usize) -> Result<Vec<Value>, VmError> {
|
||||
let start = self.0.len()
|
||||
let start = self
|
||||
.0
|
||||
.len()
|
||||
.checked_sub(n)
|
||||
.ok_or_else(|| VmError::StackError("Stack underflow on pop_n".to_string()))?;
|
||||
Ok(self.0.drain(start..).collect())
|
||||
@ -90,13 +93,13 @@ impl<'a> IntoIterator for &'a OperandStack {
|
||||
}
|
||||
#[derive(Clone, Default)]
|
||||
pub struct LocalVariables {
|
||||
inner: Vec<Value>
|
||||
inner: Vec<Value>,
|
||||
}
|
||||
|
||||
impl LocalVariables {
|
||||
pub fn with_capacity(max_locals: usize) -> Self {
|
||||
Self {
|
||||
inner: vec![Value::NULL; max_locals]
|
||||
inner: vec![Value::NULL; max_locals],
|
||||
}
|
||||
}
|
||||
pub fn from_args(args: Vec<Value>, max_locals: usize) -> Self {
|
||||
@ -130,11 +133,11 @@ impl LocalVariables {
|
||||
val
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item=&Value> {
|
||||
pub fn iter(&self) -> impl Iterator<Item = &Value> {
|
||||
self.inner.iter().filter(|v| !matches!(v, Value::Padding))
|
||||
}
|
||||
|
||||
pub fn slots(&self) -> impl Iterator<Item=(u16, &Value)> {
|
||||
pub fn slots(&self) -> impl Iterator<Item = (u16, &Value)> {
|
||||
self.inner
|
||||
.iter()
|
||||
.enumerate()
|
||||
@ -151,10 +154,18 @@ impl std::fmt::Debug for LocalVariables {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Value {
|
||||
pub const NULL: Value = Value::Reference(None);
|
||||
|
||||
/// Returns the size in bytes of this value's data
|
||||
pub fn size_bytes(&self) -> usize {
|
||||
match self {
|
||||
Value::Primitive(p) => p.size_bytes(),
|
||||
Value::Reference(r) => size_of_val(r),
|
||||
Value::Padding => 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_vec(values: &Vec<Value>) -> String {
|
||||
let fmt = values
|
||||
.iter()
|
||||
@ -166,13 +177,59 @@ impl Value {
|
||||
}
|
||||
|
||||
pub fn is_wide(&self) -> bool {
|
||||
matches!(self, Value::Primitive(Primitive::Long(_) | Primitive::Double(_)))
|
||||
matches!(
|
||||
self,
|
||||
Value::Primitive(Primitive::Long(_) | Primitive::Double(_))
|
||||
)
|
||||
}
|
||||
|
||||
pub fn as_ref_kind(&self) -> Option<ReferenceKind> {
|
||||
pub fn as_ref_kind(&self) -> Result<ReferenceKind, VmError> {
|
||||
match self {
|
||||
Value::Reference(Some(kind)) => Some(kind.clone()),
|
||||
_ => None,
|
||||
Value::Reference(Some(kind)) => Ok(kind.clone()),
|
||||
_ => Err(VmError::InvariantError(format!(
|
||||
"Expected reference kind, found {}",
|
||||
self.to_string()
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_into_object_reference(&self) -> Result<ObjectReference, VmError> {
|
||||
match self {
|
||||
Value::Reference(Some(ReferenceKind::ObjectReference(obj))) => Ok(obj.clone()),
|
||||
_ => Err(VmError::InvariantError(format!(
|
||||
"Expected object, found {}",
|
||||
self.to_string()
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_into_array_reference(&self) -> Result<ArrayReference, VmError> {
|
||||
match self {
|
||||
Value::Reference(Some(ReferenceKind::ArrayReference(arr))) => Ok(arr.clone()),
|
||||
_ => Err(VmError::InvariantError(format!(
|
||||
"Expected array ref, found {}",
|
||||
self.to_string()
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_into_jint(self) -> Result<jint, VmError> {
|
||||
match self {
|
||||
Value::Primitive(Primitive::Int(i)) => Ok(i),
|
||||
_ => Err(VmError::InvariantError(format!(
|
||||
"Expected int, found {}",
|
||||
self.to_string()
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_into_jlong(self) -> Result<jlong, VmError> {
|
||||
match self {
|
||||
Value::Primitive(Primitive::Long(j)) => Ok(j),
|
||||
_ => Err(VmError::InvariantError(format!(
|
||||
"Expected long, found {}",
|
||||
self.to_string()
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -244,6 +301,21 @@ pub enum Primitive {
|
||||
Long(i64),
|
||||
}
|
||||
|
||||
impl Primitive {
|
||||
pub fn size_bytes(&self) -> usize {
|
||||
match self {
|
||||
Primitive::Boolean(_) => size_of::<bool>(),
|
||||
Primitive::Char(_) => size_of::<u16>(),
|
||||
Primitive::Float(_) => size_of::<f32>(),
|
||||
Primitive::Double(_) => size_of::<f64>(),
|
||||
Primitive::Byte(_) => size_of::<i8>(),
|
||||
Primitive::Short(_) => size_of::<i16>(),
|
||||
Primitive::Int(_) => size_of::<i32>(),
|
||||
Primitive::Long(_) => size_of::<i64>(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Primitive {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
@ -270,7 +342,9 @@ impl Display for Value {
|
||||
Value::Primitive(prim) => write!(f, "{}", prim),
|
||||
Value::Reference(Some(obj)) => write!(f, "Ref({})", obj),
|
||||
Value::Reference(None) => write!(f, "null"),
|
||||
_ => { write!(f, "pad") }
|
||||
_ => {
|
||||
write!(f, "pad")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -310,7 +384,7 @@ impl PartialEq<FieldType> for Value {
|
||||
Some(kind) => match kind {
|
||||
ReferenceKind::ObjectReference(found_ref) => {
|
||||
if let FieldType::ClassType(expected) = other {
|
||||
let found = format!("L{};", found_ref.lock().unwrap().class.this_class);
|
||||
let found = format!("L{};", found_ref.lock().class.this_class);
|
||||
expected.eq(&found)
|
||||
} else {
|
||||
false
|
||||
@ -355,7 +429,9 @@ impl PartialEq<FieldType> for Value {
|
||||
},
|
||||
},
|
||||
},
|
||||
_ => { panic!("uhh what") }
|
||||
_ => {
|
||||
panic!("uhh what")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,14 +9,17 @@ use libloading::os::windows as imp;
|
||||
|
||||
use crate::class_file::{ClassFlags, MethodRef};
|
||||
use crate::class_loader::ClassLoader;
|
||||
use crate::error::VmError;
|
||||
use crate::native::r#unsafe::UnsafeSupport;
|
||||
use crate::objects::object::ReferenceKind;
|
||||
use crate::objects::object_manager::ObjectManager;
|
||||
use crate::thread::VmThread;
|
||||
use crate::{MethodDescriptor, ThreadId};
|
||||
use dashmap::DashMap;
|
||||
use imp::Library;
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
use crate::objects::object::ReferenceKind;
|
||||
use crate::error::VmError;
|
||||
use log::trace;
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
use std::sync::Arc;
|
||||
|
||||
// struct AbstractObject<'a> {}
|
||||
pub struct Vm {
|
||||
@ -27,6 +30,7 @@ pub struct Vm {
|
||||
pub native_methods: DashMap<String, *const c_void>,
|
||||
pub native_libraries: DashMap<String, Library>,
|
||||
pub gc: Arc<RwLock<ObjectManager>>,
|
||||
pub safent: RwLock<UnsafeSupport>,
|
||||
}
|
||||
|
||||
impl Vm {
|
||||
@ -39,6 +43,7 @@ impl Vm {
|
||||
native_methods: DashMap::new(),
|
||||
native_libraries: DashMap::new(),
|
||||
gc: Default::default(),
|
||||
safent: Default::default(),
|
||||
});
|
||||
|
||||
// Create main thread
|
||||
@ -58,13 +63,8 @@ impl Vm {
|
||||
}
|
||||
|
||||
pub fn find_native_method(&self, name: &str) -> Option<*const c_void> {
|
||||
if name.contains("Java_jdk_internal_util_SystemProps$Raw_platformProperties") {
|
||||
println!("Pick me baws");
|
||||
let val = self.native_methods.iter().collect::<Vec<_>>();
|
||||
println!("{}", val.len());
|
||||
}
|
||||
|
||||
if let Some(registered) = self.native_methods.get(name) {
|
||||
trace!("native {} was already registered", name);
|
||||
return Some(registered.clone());
|
||||
}
|
||||
|
||||
@ -103,11 +103,15 @@ impl Vm {
|
||||
"java/lang/Thread",
|
||||
"java/lang/Module",
|
||||
//unsafe internal?
|
||||
// "java/lang/reflect/Method",
|
||||
// "java/lang/ref/Finalizer",
|
||||
// "jdk/internal/misc/UnsafeConstants"
|
||||
"jdk/internal/misc/UnsafeConstants",
|
||||
"java/lang/reflect/Method",
|
||||
"java/lang/ref/Finalizer",
|
||||
];
|
||||
let _ = classes.iter().map(|e| thread.get_or_resolve_class(e));
|
||||
let mut loaded = Vec::new();
|
||||
for name in classes {
|
||||
let class = thread.loader.lock().get_or_load(name, None)?;
|
||||
loaded.push(class);
|
||||
}
|
||||
let prims = vec![
|
||||
("byte", "B"),
|
||||
("char", "C"),
|
||||
@ -116,37 +120,45 @@ impl Vm {
|
||||
("int", "I"),
|
||||
("long", "J"),
|
||||
("short", "S"),
|
||||
("boolean", "Z")
|
||||
("boolean", "Z"),
|
||||
("void", "V"),
|
||||
];
|
||||
let thread = self.threads.get(&self.main_thread_id).unwrap();
|
||||
|
||||
for prim in prims {
|
||||
let klass =
|
||||
self.loader.lock().unwrap().primitive_class(prim.0);
|
||||
|
||||
let klass = self.loader.lock().primitive_class(prim.0);
|
||||
|
||||
let class_class = thread.get_class("java/lang/Class")?;
|
||||
let name_obj = thread.intern_string(&prim.0);
|
||||
let flags = ClassFlags::from(1041u16);
|
||||
let class_obj = self.gc.write().unwrap().new_class(
|
||||
let class_obj = self.gc.write().new_class(
|
||||
class_class.clone(),
|
||||
Some(ReferenceKind::ObjectReference(name_obj)),
|
||||
None,
|
||||
flags,
|
||||
true
|
||||
true,
|
||||
);
|
||||
klass.mirror.set(class_obj.lock().unwrap().id).unwrap();
|
||||
klass.mirror.set(class_obj.lock().id).unwrap();
|
||||
|
||||
let prim_array_klass = self.loader.lock().unwrap().create_array_class(klass.clone());
|
||||
let prim_array_klass = self.loader.lock().create_array_class(klass.clone());
|
||||
let arr_name_obj = thread.intern_string(&prim_array_klass.this_class);
|
||||
let arr_class_obj = self.gc.write().unwrap().new_class(
|
||||
let arr_class_obj = self.gc.write().new_class(
|
||||
class_class,
|
||||
Some(ReferenceKind::ObjectReference(arr_name_obj)),
|
||||
None,
|
||||
flags,
|
||||
false
|
||||
false,
|
||||
);
|
||||
prim_array_klass.mirror.set(arr_class_obj.lock().unwrap().id).unwrap();
|
||||
prim_array_klass
|
||||
.mirror
|
||||
.set(arr_class_obj.lock().id)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
//woops forgot to init
|
||||
for class in loaded {
|
||||
println!("bootstrap init {}", class.this_class);
|
||||
thread.ensure_initialised(&class)?;
|
||||
}
|
||||
|
||||
let phase1ref = MethodRef {
|
||||
@ -155,6 +167,7 @@ impl Vm {
|
||||
desc: MethodDescriptor::void(),
|
||||
};
|
||||
thread.invoke(phase1ref, Vec::new())?;
|
||||
panic!("HOLY FUCKING SHIT, DID WE PHASE 1!?");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@ -12,3 +12,4 @@ log = "0.4.28"
|
||||
[lib]
|
||||
name = "roast_vm"
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
|
||||
29
crates/roast-vm-sys/src/CDS.rs
Normal file
29
crates/roast-vm-sys/src/CDS.rs
Normal 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
|
||||
}
|
||||
@ -1,13 +1,14 @@
|
||||
use crate::{get_thread, resolve_object};
|
||||
use jni::sys::{jboolean, jclass, jobject, JNI_TRUE};
|
||||
use jni::sys::{jint, JNIEnv};
|
||||
use jni::sys::{jobjectArray, jstring};
|
||||
use log::trace;
|
||||
use roast_vm_core::class_file::FieldRef;
|
||||
use std::ptr;
|
||||
use jni::sys::jstring;
|
||||
use jni::sys::JNIEnv;
|
||||
use jni::sys::jclass;
|
||||
use roast_vm_core::{BaseType, FieldType};
|
||||
use roast_vm_core::objects::array::ArrayReference;
|
||||
use roast_vm_core::objects::ReferenceKind;
|
||||
use roast_vm_core::objects::{Reference, ReferenceKind};
|
||||
use roast_vm_core::value::Value;
|
||||
use crate::get_thread;
|
||||
use roast_vm_core::{BaseType, FieldType};
|
||||
use std::ptr;
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "system" fn Java_java_lang_Class_getPrimitiveClass<'local>(
|
||||
@ -18,7 +19,7 @@ pub unsafe extern "system" fn Java_java_lang_Class_getPrimitiveClass<'local>(
|
||||
let thread = &*get_thread(env);
|
||||
|
||||
let rust_string = {
|
||||
let gc = thread.gc.read().unwrap();
|
||||
let gc = thread.gc.read();
|
||||
|
||||
let obj_id = name as u32;
|
||||
|
||||
@ -27,8 +28,8 @@ pub unsafe extern "system" fn Java_java_lang_Class_getPrimitiveClass<'local>(
|
||||
_ => return 0 as jclass,
|
||||
};
|
||||
|
||||
let obj = obj_ref.lock().unwrap();
|
||||
let field_ref = FieldRef{
|
||||
let obj = obj_ref.lock();
|
||||
let field_ref = FieldRef {
|
||||
class: "java/lang/String".to_string(),
|
||||
name: "value".to_string(),
|
||||
desc: FieldType::ArrayType(Box::new(FieldType::Base(BaseType::Byte))),
|
||||
@ -40,7 +41,7 @@ pub unsafe extern "system" fn Java_java_lang_Class_getPrimitiveClass<'local>(
|
||||
return 0 as jclass;
|
||||
};
|
||||
|
||||
let array = byte_array.lock().unwrap();
|
||||
let array = byte_array.lock();
|
||||
|
||||
let bytes: Vec<u8> = (&array).iter().map(|&b| b as u8).collect();
|
||||
|
||||
@ -55,9 +56,101 @@ pub unsafe extern "system" fn Java_java_lang_Class_getPrimitiveClass<'local>(
|
||||
};
|
||||
|
||||
let klass = thread.get_class(&rust_string).unwrap();
|
||||
let class = thread.gc.read().unwrap().get(*klass.mirror.wait());
|
||||
let class = thread.gc.read().get(*klass.mirror.wait());
|
||||
|
||||
class.id() as jclass
|
||||
|
||||
|
||||
}
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "system" fn Java_java_lang_Class_forName0(
|
||||
env: *mut JNIEnv,
|
||||
_class: jclass,
|
||||
name: jstring,
|
||||
initialize: jboolean,
|
||||
loader: jobject,
|
||||
caller: jclass,
|
||||
) -> jclass {
|
||||
trace!("Java_java_lang_Class_forName0");
|
||||
let thread = &*get_thread(env);
|
||||
|
||||
// Convert java.lang.String to Rust string
|
||||
let string_obj = resolve_object(thread, name).unwrap();
|
||||
let class_name = thread.gc.read().transmute_string(string_obj).unwrap();
|
||||
|
||||
// Convert "java.lang.String" -> "java/lang/String"
|
||||
let class_name = class_name.replace('.', "/");
|
||||
println!("forName0: class name is : {}", class_name);
|
||||
|
||||
// Load the class
|
||||
let rtc = match thread.get_class(&class_name) {
|
||||
Ok(c) => c,
|
||||
Err(_) => {
|
||||
// TODO: throw ClassNotFoundException
|
||||
return ptr::null_mut();
|
||||
}
|
||||
};
|
||||
|
||||
if initialize == JNI_TRUE {
|
||||
thread.ensure_initialised(&rtc).unwrap();
|
||||
}
|
||||
|
||||
println!("returning");
|
||||
// Return the mirror
|
||||
*rtc.mirror.get().unwrap() as jclass
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "system" fn Java_java_lang_Class_getDeclaredConstructors0(
|
||||
env: *mut JNIEnv,
|
||||
class: jobject,
|
||||
public_only: jboolean,
|
||||
) -> jobjectArray {
|
||||
let thread = &*get_thread(env);
|
||||
let klass = thread
|
||||
.loader
|
||||
.lock()
|
||||
.class_from_mirror_id(class as u32)
|
||||
.unwrap();
|
||||
let only_public = public_only == 1u8;
|
||||
|
||||
let constructors = klass
|
||||
.methods
|
||||
.iter()
|
||||
.filter(|e| e.name == "<init>" && (!only_public || e.flags.ACC_PUBLIC))
|
||||
.enumerate()
|
||||
.map(|(idx, constructor)| {
|
||||
let modifiers: u16 = constructor.flags.clone().into();
|
||||
let parameters = constructor
|
||||
.desc
|
||||
.parameters
|
||||
.iter()
|
||||
.map(|x| thread.get_class(&x.as_class_name()).unwrap())
|
||||
.collect::<Vec<_>>()
|
||||
.into_boxed_slice();
|
||||
let exceptions = Vec::new().into_boxed_slice();
|
||||
let class_array_class = thread.get_class("[Ljava/lang/Class;").unwrap();
|
||||
let declaring_class = thread.get_class(&constructor.class).unwrap();
|
||||
let constructor_class = thread.get_class("java/lang/reflect/Constructor").unwrap();
|
||||
|
||||
let con = thread.gc.write().new_constructor(
|
||||
idx as jint,
|
||||
modifiers as jint,
|
||||
parameters,
|
||||
exceptions,
|
||||
class_array_class,
|
||||
declaring_class,
|
||||
constructor_class,
|
||||
);
|
||||
Some(ReferenceKind::from(con))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let constructor_array_class = thread
|
||||
.get_class("[Ljava/lang/reflect/Constructor;")
|
||||
.unwrap();
|
||||
let output = thread
|
||||
.gc
|
||||
.write()
|
||||
.new_object_array_from(constructor_array_class, constructors.into_boxed_slice());
|
||||
|
||||
output.id() as jobjectArray
|
||||
}
|
||||
@ -1,36 +1,24 @@
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
mod CDS;
|
||||
mod class;
|
||||
mod object;
|
||||
mod misc_unsafe;
|
||||
mod object;
|
||||
mod reflection;
|
||||
mod runtime;
|
||||
mod system;
|
||||
|
||||
use jni::objects::{JClass, JObject, JString};
|
||||
use jni::strings::JNIString;
|
||||
use jni::sys::{jclass, jlong, jobject, jobjectArray};
|
||||
use jni::{JNIEnv, NativeMethod};
|
||||
use std::ffi::c_void;
|
||||
use std::io::Write;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
use roast_vm_core::objects::array::ArrayReference;
|
||||
use roast_vm_core::objects::object::ObjectReference;
|
||||
use roast_vm_core::objects::ReferenceKind;
|
||||
use roast_vm_core::VmThread;
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "system" fn current_time_millis<'local>(
|
||||
mut env: JNIEnv<'local>,
|
||||
jclass: JClass<'local>,
|
||||
) -> jlong {
|
||||
println!("Sneaky hobitses has hijacked the native methods he has");
|
||||
|
||||
// SystemTime::now()
|
||||
// .duration_since(UNIX_EPOCH)
|
||||
// .unwrap()
|
||||
// .as_millis() as jlong
|
||||
|
||||
1337i64
|
||||
}
|
||||
use std::ffi::c_void;
|
||||
use std::io::Write;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "system" fn Java_org_example_MockIO_print<'local>(
|
||||
@ -50,7 +38,7 @@ pub extern "system" fn Java_org_example_MockIO_print<'local>(
|
||||
// println!("Yeetus bageetus! Im printing from rust!")
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
/*#[unsafe(no_mangle)]
|
||||
pub extern "system" fn Java_org_example_MockIO_registerNatives<'local>(
|
||||
mut env: JNIEnv<'local>,
|
||||
jclass: JClass<'local>,
|
||||
@ -79,7 +67,7 @@ pub extern "system" fn Java_org_example_MockIO_registerNatives<'local>(
|
||||
|
||||
// env.register_native_methods(library_class, &library_methods)
|
||||
// .expect("failed to register method");
|
||||
}
|
||||
}*/
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "system" fn findEntry0<'local>(
|
||||
@ -153,10 +141,12 @@ pub extern "system" fn Java_org_example_MockIO_println<'local>(
|
||||
// }
|
||||
|
||||
unsafe fn get_thread(env: *mut jni::sys::JNIEnv) -> *const VmThread {
|
||||
(**env).reserved0 as *const VmThread
|
||||
let thread = (**env).reserved0 as *const VmThread;
|
||||
VmThread::set_current((*thread).id);
|
||||
thread
|
||||
}
|
||||
fn resolve_object(thread: &VmThread, obj: jobject) -> Option<ObjectReference> {
|
||||
let gc = thread.gc.read().unwrap();
|
||||
let gc = thread.gc.read();
|
||||
let ReferenceKind::ObjectReference(obj_ref) = gc.get(obj as u32) else {
|
||||
return None;
|
||||
};
|
||||
@ -164,13 +154,21 @@ fn resolve_object(thread: &VmThread, obj: jobject) -> Option<ObjectReference> {
|
||||
}
|
||||
|
||||
fn resolve_array(thread: &VmThread, obj: jobject) -> Option<ArrayReference> {
|
||||
let gc = thread.gc.read().unwrap();
|
||||
let gc = thread.gc.read();
|
||||
let ReferenceKind::ArrayReference(arr_ref) = gc.get(obj as u32) else {
|
||||
return None;
|
||||
};
|
||||
Some(arr_ref.clone())
|
||||
}
|
||||
|
||||
fn resolve_reference(thread: &VmThread, obj: jobject) -> Option<ReferenceKind> {
|
||||
if obj.is_null() {
|
||||
return None;
|
||||
}
|
||||
let gc = thread.gc.read();
|
||||
Some(gc.get(obj as u32).clone())
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "system" fn Java_jdk_internal_util_SystemProps_00024Raw_vmProperties<'local>(
|
||||
mut env: JNIEnv<'local>,
|
||||
|
||||
@ -1,6 +1,11 @@
|
||||
use jni::sys::{jclass, jint, jobject, jstring, JNIEnv};
|
||||
use crate::{get_thread, resolve_array, resolve_object};
|
||||
use jni::sys::{jboolean, jclass, jint, jlong, jobject, jstring, JNIEnv, JNI_FALSE, JNI_TRUE};
|
||||
use log::warn;
|
||||
use crate::{get_thread, resolve_object};
|
||||
use roast_vm_core::native::r#unsafe::OffsetKind;
|
||||
use roast_vm_core::objects::array::ArrayReference;
|
||||
use roast_vm_core::objects::object::string_from_bytes;
|
||||
use roast_vm_core::objects::ReferenceKind;
|
||||
use roast_vm_core::value::Value;
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_registerNatives(
|
||||
@ -8,13 +13,14 @@ pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_registerNatives(
|
||||
obj: jclass,
|
||||
) {
|
||||
//no op
|
||||
()
|
||||
println!("Unsafe_registerNatives is NO OP")
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_arrayBaseOffset0(
|
||||
env: *mut JNIEnv,
|
||||
obj: jclass,
|
||||
_env: *mut JNIEnv,
|
||||
_unsafe_obj: jobject,
|
||||
_class: jclass,
|
||||
) -> jint {
|
||||
warn!("arrayBaseOffset0 currently just returning 0");
|
||||
0
|
||||
@ -23,8 +29,545 @@ pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_arrayBaseOffset0(
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_arrayIndexScale0(
|
||||
env: *mut JNIEnv,
|
||||
obj: jclass,
|
||||
_unsafe_obj: jobject,
|
||||
class: jclass,
|
||||
) -> jint {
|
||||
warn!("arrayIndexScale0 currently just returning 0");
|
||||
let thread = &*get_thread(env);
|
||||
let class_obj = resolve_object(thread, class).unwrap();
|
||||
let class_id = class_obj.lock().id;
|
||||
let runtime_class = thread
|
||||
.vm
|
||||
.loader
|
||||
.lock()
|
||||
.class_from_mirror_id(class_id)
|
||||
.unwrap();
|
||||
|
||||
// Get component type from array class name
|
||||
match runtime_class.this_class.as_str() {
|
||||
"[Z" => 1, // boolean
|
||||
"[B" => 1, // byte
|
||||
"[C" => 2, // char
|
||||
"[S" => 2, // short
|
||||
"[I" => 4, // int
|
||||
"[F" => 4, // float
|
||||
"[J" => 8, // long
|
||||
"[D" => 8, // double
|
||||
s if s.starts_with("[L") || s.starts_with("[[") => 8, // reference
|
||||
_ => {
|
||||
warn!(
|
||||
"arrayIndexScale0: unknown array type {}",
|
||||
runtime_class.this_class
|
||||
);
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_getReferenceVolatile(
|
||||
env: *mut JNIEnv,
|
||||
_unsafe_obj: jobject,
|
||||
obj: jobject,
|
||||
offset: jlong,
|
||||
) -> jobject {
|
||||
let thread = &*get_thread(env);
|
||||
let reference = thread.gc.read().get(obj as u32);
|
||||
|
||||
match reference {
|
||||
ReferenceKind::ArrayReference(array_ref) => {
|
||||
if let ArrayReference::Object(arr) = array_ref {
|
||||
let guard = arr.lock();
|
||||
let index = (offset / 8) as i32;
|
||||
match guard.get(index) {
|
||||
Some(ref_kind) => ref_kind.id() as jobject,
|
||||
None => std::ptr::null_mut(),
|
||||
}
|
||||
} else {
|
||||
panic!("getReferenceVolatile on non-object array")
|
||||
}
|
||||
}
|
||||
ReferenceKind::ObjectReference(obj_ref) => {
|
||||
let key = thread
|
||||
.vm
|
||||
.safent
|
||||
.read()
|
||||
.resolve_field(offset)
|
||||
.expect("unregistered field offset");
|
||||
let guard = obj_ref.lock();
|
||||
let value = guard.fields.get(&key.field_name).unwrap();
|
||||
value
|
||||
.try_into_object_reference()
|
||||
.map(|r| r.lock().id as jobject)
|
||||
.unwrap_or(std::ptr::null_mut())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_compareAndSetReference(
|
||||
env: *mut JNIEnv,
|
||||
_unsafe_obj: jobject,
|
||||
obj: jobject,
|
||||
offset: jlong,
|
||||
expected: jobject,
|
||||
new_val: jobject,
|
||||
) -> jboolean {
|
||||
let thread = &*get_thread(env);
|
||||
let reference = thread.gc.read().get(obj as u32);
|
||||
|
||||
match reference {
|
||||
ReferenceKind::ArrayReference(array_ref) => {
|
||||
if let ArrayReference::Object(arr) = array_ref {
|
||||
let mut guard = arr.lock();
|
||||
let index = (offset / 8) as usize;
|
||||
|
||||
let current_id = guard.backing[index].as_ref().map(|r| r.id()).unwrap_or(0);
|
||||
let expected_id = if expected.is_null() {
|
||||
0
|
||||
} else {
|
||||
expected as u32
|
||||
};
|
||||
|
||||
if current_id == expected_id {
|
||||
guard.backing[index] = if new_val.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(thread.gc.read().get(new_val as u32))
|
||||
};
|
||||
JNI_TRUE
|
||||
} else {
|
||||
JNI_FALSE
|
||||
}
|
||||
} else {
|
||||
panic!("compareAndSetReference on non-object array")
|
||||
}
|
||||
}
|
||||
ReferenceKind::ObjectReference(obj_ref) => {
|
||||
let key = thread
|
||||
.vm
|
||||
.safent
|
||||
.read()
|
||||
.resolve_field(offset)
|
||||
.expect("unregistered field offset");
|
||||
let mut guard = obj_ref.lock();
|
||||
|
||||
let current_id = guard
|
||||
.fields
|
||||
.get(&key.field_name)
|
||||
.and_then(|v| v.try_into_object_reference().ok())
|
||||
.map(|r| r.lock().id)
|
||||
.unwrap_or(0);
|
||||
let expected_id = if expected.is_null() {
|
||||
0
|
||||
} else {
|
||||
expected as u32
|
||||
};
|
||||
|
||||
if current_id == expected_id {
|
||||
let new_value = if new_val.is_null() {
|
||||
Value::NULL // however you represent null
|
||||
} else {
|
||||
Value::from(thread.gc.read().get(new_val as u32))
|
||||
};
|
||||
guard.fields.insert(key.field_name, new_value);
|
||||
JNI_TRUE
|
||||
} else {
|
||||
JNI_FALSE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_compareAndSetInt(
|
||||
env: *mut JNIEnv,
|
||||
_unsafe_obj: jobject,
|
||||
obj: jobject,
|
||||
offset: jlong,
|
||||
expected: jint,
|
||||
new_val: jint,
|
||||
) -> jboolean {
|
||||
let thread = &*get_thread(env);
|
||||
let reference = thread.gc.read().get(obj as u32);
|
||||
|
||||
match reference {
|
||||
ReferenceKind::ArrayReference(array_ref) => {
|
||||
if let ArrayReference::Int(arr) = array_ref {
|
||||
let mut guard = arr.lock();
|
||||
let index = (offset / 4) as usize;
|
||||
if guard.backing[index] == expected {
|
||||
guard.backing[index] = new_val;
|
||||
JNI_TRUE
|
||||
} else {
|
||||
JNI_FALSE
|
||||
}
|
||||
} else {
|
||||
panic!("compareAndSetInt on non-int array")
|
||||
}
|
||||
}
|
||||
ReferenceKind::ObjectReference(obj_ref) => {
|
||||
let key = thread
|
||||
.vm
|
||||
.safent
|
||||
.read()
|
||||
.resolve_field(offset)
|
||||
.expect("unregistered field offset");
|
||||
let mut guard = obj_ref.lock();
|
||||
let current = guard
|
||||
.fields
|
||||
.get(&key.field_name)
|
||||
.unwrap()
|
||||
.clone()
|
||||
.try_into_jint()
|
||||
.unwrap();
|
||||
if current == expected {
|
||||
guard.fields.insert(key.field_name, Value::from(new_val));
|
||||
JNI_TRUE
|
||||
} else {
|
||||
JNI_FALSE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_compareAndSetLong(
|
||||
env: *mut JNIEnv,
|
||||
_unsafe_obj: jobject,
|
||||
obj: jobject,
|
||||
offset: jlong,
|
||||
expected: jlong,
|
||||
new_val: jlong,
|
||||
) -> jboolean {
|
||||
let thread = &*get_thread(env);
|
||||
let reference = thread.gc.read().get(obj as u32);
|
||||
|
||||
match reference {
|
||||
ReferenceKind::ArrayReference(array_ref) => {
|
||||
if let ArrayReference::Long(arr) = array_ref {
|
||||
let mut guard = arr.lock();
|
||||
let index = (offset / 8) as usize;
|
||||
if guard.backing[index] == expected {
|
||||
guard.backing[index] = new_val;
|
||||
JNI_TRUE
|
||||
} else {
|
||||
JNI_FALSE
|
||||
}
|
||||
} else {
|
||||
panic!("compareAndSetLong on non-long array")
|
||||
}
|
||||
}
|
||||
ReferenceKind::ObjectReference(obj_ref) => {
|
||||
let key = thread
|
||||
.vm
|
||||
.safent
|
||||
.read()
|
||||
.resolve_field(offset)
|
||||
.expect("unregistered field offset");
|
||||
let mut guard = obj_ref.lock();
|
||||
let current = guard
|
||||
.fields
|
||||
.get(&key.field_name)
|
||||
.unwrap()
|
||||
.clone()
|
||||
.try_into_jlong()
|
||||
.unwrap();
|
||||
if current == expected {
|
||||
guard.fields.insert(key.field_name, Value::from(new_val));
|
||||
JNI_TRUE
|
||||
} else {
|
||||
JNI_FALSE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_compareAndExchangeInt(
|
||||
env: *mut JNIEnv,
|
||||
_unsafe_obj: jobject,
|
||||
obj: jobject,
|
||||
offset: jlong,
|
||||
expected: jint,
|
||||
new_val: jint,
|
||||
) -> jint {
|
||||
let thread = &*get_thread(env);
|
||||
let reference = thread.gc.read().get(obj as u32);
|
||||
|
||||
match reference {
|
||||
ReferenceKind::ArrayReference(array_ref) => {
|
||||
if let ArrayReference::Int(arr) = array_ref {
|
||||
let mut guard = arr.lock();
|
||||
let index = (offset / 4) as usize;
|
||||
let current = guard.backing[index];
|
||||
if current == expected {
|
||||
guard.backing[index] = new_val;
|
||||
}
|
||||
current
|
||||
} else {
|
||||
panic!("compareAndExchangeInt on non-int array")
|
||||
}
|
||||
}
|
||||
ReferenceKind::ObjectReference(obj_ref) => {
|
||||
let key = thread
|
||||
.vm
|
||||
.safent
|
||||
.read()
|
||||
.resolve_field(offset)
|
||||
.expect("unregistered field offset");
|
||||
let mut guard = obj_ref.lock();
|
||||
let current = guard
|
||||
.fields
|
||||
.get(&key.field_name)
|
||||
.unwrap()
|
||||
.clone()
|
||||
.try_into_jint()
|
||||
.unwrap();
|
||||
if current == expected {
|
||||
guard.fields.insert(key.field_name, Value::from(new_val));
|
||||
}
|
||||
current
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_compareAndExchangeLong(
|
||||
env: *mut JNIEnv,
|
||||
_unsafe_obj: jobject,
|
||||
obj: jobject,
|
||||
offset: jlong,
|
||||
expected: jlong,
|
||||
new_val: jlong,
|
||||
) -> jlong {
|
||||
let thread = &*get_thread(env);
|
||||
let reference = thread.gc.read().get(obj as u32);
|
||||
|
||||
match reference {
|
||||
ReferenceKind::ArrayReference(array_ref) => {
|
||||
if let ArrayReference::Long(arr) = array_ref {
|
||||
let mut guard = arr.lock();
|
||||
let index = (offset / 8) as usize;
|
||||
let current = guard.backing[index];
|
||||
if current == expected {
|
||||
guard.backing[index] = new_val;
|
||||
}
|
||||
current
|
||||
} else {
|
||||
panic!("compareAndExchangeLong on non-long array")
|
||||
}
|
||||
}
|
||||
ReferenceKind::ObjectReference(obj_ref) => {
|
||||
let key = thread
|
||||
.vm
|
||||
.safent
|
||||
.read()
|
||||
.resolve_field(offset)
|
||||
.expect("unregistered field offset");
|
||||
let mut guard = obj_ref.lock();
|
||||
let current = guard
|
||||
.fields
|
||||
.get(&key.field_name)
|
||||
.unwrap()
|
||||
.clone()
|
||||
.try_into_jlong()
|
||||
.unwrap();
|
||||
if current == expected {
|
||||
guard.fields.insert(key.field_name, Value::from(new_val));
|
||||
}
|
||||
current
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_compareAndExchangeReference(
|
||||
env: *mut JNIEnv,
|
||||
_unsafe_obj: jobject,
|
||||
obj: jobject,
|
||||
offset: jlong,
|
||||
expected: jobject,
|
||||
new_val: jobject,
|
||||
) -> jobject {
|
||||
let thread = &*get_thread(env);
|
||||
let reference = thread.gc.read().get(obj as u32);
|
||||
|
||||
match reference {
|
||||
ReferenceKind::ArrayReference(array_ref) => {
|
||||
if let ArrayReference::Object(arr) = array_ref {
|
||||
let mut guard = arr.lock();
|
||||
let index = (offset / 8) as usize;
|
||||
|
||||
let current = guard.backing[index].clone();
|
||||
let current_id = current.as_ref().map(|r| r.id()).unwrap_or(0);
|
||||
let expected_id = if expected.is_null() {
|
||||
0
|
||||
} else {
|
||||
expected as u32
|
||||
};
|
||||
|
||||
if current_id == expected_id {
|
||||
guard.backing[index] = if new_val.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(thread.gc.read().get(new_val as u32))
|
||||
};
|
||||
}
|
||||
current
|
||||
.map(|r| r.id() as jobject)
|
||||
.unwrap_or(std::ptr::null_mut())
|
||||
} else {
|
||||
panic!("compareAndExchangeReference on non-object array")
|
||||
}
|
||||
}
|
||||
ReferenceKind::ObjectReference(obj_ref) => {
|
||||
let key = thread
|
||||
.vm
|
||||
.safent
|
||||
.read()
|
||||
.resolve_field(offset)
|
||||
.expect("unregistered field offset");
|
||||
let mut guard = obj_ref.lock();
|
||||
|
||||
let current = guard
|
||||
.fields
|
||||
.get(&key.field_name)
|
||||
.and_then(|v| v.try_into_object_reference().ok());
|
||||
let current_id = current.as_ref().map(|r| r.lock().id).unwrap_or(0);
|
||||
let expected_id = if expected.is_null() {
|
||||
0
|
||||
} else {
|
||||
expected as u32
|
||||
};
|
||||
|
||||
if current_id == expected_id {
|
||||
let new_value = if new_val.is_null() {
|
||||
Value::NULL
|
||||
} else {
|
||||
Value::from(thread.gc.read().get(new_val as u32))
|
||||
};
|
||||
guard.fields.insert(key.field_name, new_value);
|
||||
}
|
||||
current
|
||||
.map(|r| r.lock().id as jobject)
|
||||
.unwrap_or(std::ptr::null_mut())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_objectFieldOffset0(
|
||||
env: *mut JNIEnv,
|
||||
_unsafe_obj: jobject,
|
||||
field: jobject,
|
||||
) -> jlong {
|
||||
let thread = &*get_thread(env);
|
||||
let field_obj = resolve_object(thread, field).unwrap();
|
||||
let clazz_ref = field_obj
|
||||
.lock()
|
||||
.fields
|
||||
.get("clazz")
|
||||
.unwrap()
|
||||
.try_into_object_reference()
|
||||
.unwrap();
|
||||
let clazz_guard = clazz_ref.lock();
|
||||
let class = thread
|
||||
.loader
|
||||
.lock()
|
||||
.class_from_mirror_id(clazz_guard.id)
|
||||
.unwrap();
|
||||
let name_obj = field_obj
|
||||
.lock()
|
||||
.fields
|
||||
.get("name")
|
||||
.unwrap()
|
||||
.try_into_object_reference()
|
||||
.unwrap();
|
||||
let class_name = class.this_class.clone();
|
||||
let field_name = thread.gc.read().transmute_string(name_obj).unwrap();
|
||||
|
||||
thread
|
||||
.vm
|
||||
.safent
|
||||
.write()
|
||||
.register_field_offset(&class_name, &field_name, false)
|
||||
|
||||
// warn!("objectFieldOffset0 currently just returning 0");
|
||||
// 0
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_objectFieldOffset1(
|
||||
env: *mut JNIEnv,
|
||||
_unsafe_obj: jobject,
|
||||
class: jclass,
|
||||
name: jstring,
|
||||
) -> jlong {
|
||||
let thread = &*get_thread(env);
|
||||
|
||||
let class_obj = resolve_object(thread, class).unwrap();
|
||||
let class_id = class_obj.lock().id;
|
||||
let runtime_class = thread
|
||||
.vm
|
||||
.loader
|
||||
.lock()
|
||||
.class_from_mirror_id(class_id)
|
||||
.unwrap();
|
||||
let class_name = runtime_class.this_class.clone();
|
||||
|
||||
let field_name = thread
|
||||
.vm
|
||||
.gc
|
||||
.read()
|
||||
.transmute_string(resolve_object(thread, name).unwrap())
|
||||
.unwrap();
|
||||
|
||||
thread
|
||||
.vm
|
||||
.safent
|
||||
.write()
|
||||
.register_field_offset(&class_name, &field_name, false)
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_staticFieldOffset0(
|
||||
_env: *mut JNIEnv,
|
||||
_unsafe_obj: jobject,
|
||||
_field: jobject,
|
||||
) -> jlong {
|
||||
warn!("staticFieldOffset0 currently just returning 0");
|
||||
0
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_staticFieldBase0(
|
||||
_env: *mut JNIEnv,
|
||||
_unsafe_obj: jobject,
|
||||
_field: jobject,
|
||||
) -> jobject {
|
||||
todo!("staticFieldBase0")
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_loadFence(
|
||||
_env: *mut JNIEnv,
|
||||
_unsafe_obj: jobject,
|
||||
) {
|
||||
std::sync::atomic::fence(std::sync::atomic::Ordering::Acquire);
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_storeFence(
|
||||
_env: *mut JNIEnv,
|
||||
_unsafe_obj: jobject,
|
||||
) {
|
||||
std::sync::atomic::fence(std::sync::atomic::Ordering::Release);
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_fullFence(
|
||||
_env: *mut JNIEnv,
|
||||
_unsafe_obj: jobject,
|
||||
) {
|
||||
std::sync::atomic::fence(std::sync::atomic::Ordering::SeqCst);
|
||||
}
|
||||
|
||||
@ -1,11 +1,29 @@
|
||||
use jni::sys::{jclass, jint, jobject, jstring, JNIEnv};
|
||||
use crate::{get_thread, resolve_object};
|
||||
use crate::get_thread;
|
||||
use jni::sys::{jint, jobject, JNIEnv};
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "system" fn Java_java_lang_Object_hashCode(
|
||||
env: *mut JNIEnv,
|
||||
obj: jobject,
|
||||
) -> jint {
|
||||
let thread = &*get_thread(env);
|
||||
obj as u32 as i32
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "system" fn Java_java_lang_Object_clone(
|
||||
env: *mut JNIEnv,
|
||||
obj: jobject,
|
||||
) -> jobject {
|
||||
let thread = &*get_thread(env);
|
||||
let mut gc = thread.gc.write();
|
||||
|
||||
let source = gc.get(obj as u32);
|
||||
match gc.clone_object(&source) {
|
||||
Ok(cloned) => cloned.id() as jobject,
|
||||
Err(e) => {
|
||||
// TODO: throw CloneNotSupportedException
|
||||
eprintln!("Clone failed: {:?}", e);
|
||||
std::ptr::null_mut()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
95
crates/roast-vm-sys/src/reflection.rs
Normal file
95
crates/roast-vm-sys/src/reflection.rs
Normal 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
|
||||
}
|
||||
22
crates/roast-vm-sys/src/runtime.rs
Normal file
22
crates/roast-vm-sys/src/runtime.rs
Normal 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
|
||||
}
|
||||
@ -1,6 +1,8 @@
|
||||
use jni::sys::{jclass, jint, jobject, JNIEnv};
|
||||
use log::warn;
|
||||
use crate::{get_thread, resolve_array, resolve_object};
|
||||
use jni::objects::JClass;
|
||||
use jni::sys::{jclass, jint, jlong, jobject, JNIEnv};
|
||||
use log::warn;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "system" fn Java_java_lang_System_arraycopy(
|
||||
@ -36,16 +38,45 @@ pub unsafe extern "system" fn Java_java_lang_System_arraycopy(
|
||||
let dst_len = dst_arr.len();
|
||||
|
||||
// Bounds checking
|
||||
if src_pos < 0 || dst_pos < 0 || length < 0
|
||||
|| src_pos.checked_add(length).map_or(true, |end| end > src_len)
|
||||
|| dst_pos.checked_add(length).map_or(true, |end| end > dst_len)
|
||||
if src_pos < 0
|
||||
|| dst_pos < 0
|
||||
|| length < 0
|
||||
|| src_pos
|
||||
.checked_add(length)
|
||||
.map_or(true, |end| end > src_len)
|
||||
|| dst_pos
|
||||
.checked_add(length)
|
||||
.map_or(true, |end| end > dst_len)
|
||||
{
|
||||
panic!("Array index out of bounds!");
|
||||
// throw_exception(env, "java/lang/ArrayIndexOutOfBoundsException", None);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
dst_arr.copy_from(&src_arr, src_pos, dst_pos, length).expect("Array copy error hell");
|
||||
dst_arr
|
||||
.copy_from(&src_arr, src_pos, dst_pos, length)
|
||||
.expect("Array copy error hell");
|
||||
// Type compatibility check + copy
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "system" fn current_time_millis<'local>(
|
||||
mut env: jni::JNIEnv<'local>,
|
||||
jclass: JClass<'local>,
|
||||
) -> jlong {
|
||||
SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_millis() as jlong
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "system" fn Java_java_lang_System_nanoTime<'local>(
|
||||
mut env: jni::JNIEnv<'local>,
|
||||
jclass: JClass<'local>,
|
||||
) -> jlong {
|
||||
SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_nanos() as jlong
|
||||
}
|
||||
|
||||
83
rustfmt.toml
83
rustfmt.toml
@ -1,79 +1,4 @@
|
||||
hard_tabs = true
|
||||
|
||||
newline_style = "Auto"
|
||||
indent_style = "Block"
|
||||
use_small_heuristics = "Default"
|
||||
fn_call_width = 60
|
||||
attr_fn_like_width = 70
|
||||
struct_lit_width = 18
|
||||
struct_variant_width = 35
|
||||
array_width = 60
|
||||
chain_width = 60
|
||||
single_line_if_else_max_width = 50
|
||||
single_line_let_else_max_width = 50
|
||||
wrap_comments = false
|
||||
format_code_in_doc_comments = false
|
||||
doc_comment_code_block_width = 100
|
||||
comment_width = 80
|
||||
normalize_comments = false
|
||||
normalize_doc_attributes = false
|
||||
format_strings = false
|
||||
format_macro_matchers = false
|
||||
format_macro_bodies = true
|
||||
skip_macro_invocations = []
|
||||
hex_literal_case = "Preserve"
|
||||
empty_item_single_line = true
|
||||
struct_lit_single_line = true
|
||||
fn_single_line = false
|
||||
where_single_line = false
|
||||
imports_indent = "Block"
|
||||
imports_layout = "Mixed"
|
||||
imports_granularity = "Preserve"
|
||||
group_imports = "Preserve"
|
||||
reorder_imports = true
|
||||
reorder_modules = true
|
||||
reorder_impl_items = false
|
||||
type_punctuation_density = "Wide"
|
||||
space_before_colon = false
|
||||
space_after_colon = true
|
||||
spaces_around_ranges = false
|
||||
binop_separator = "Front"
|
||||
remove_nested_parens = true
|
||||
combine_control_expr = true
|
||||
short_array_element_width_threshold = 10
|
||||
overflow_delimited_expr = false
|
||||
struct_field_align_threshold = 0
|
||||
enum_discrim_align_threshold = 0
|
||||
match_arm_blocks = true
|
||||
match_arm_leading_pipes = "Never"
|
||||
force_multiline_blocks = false
|
||||
fn_params_layout = "Tall"
|
||||
brace_style = "SameLineWhere"
|
||||
control_brace_style = "AlwaysSameLine"
|
||||
trailing_semicolon = true
|
||||
trailing_comma = "Vertical"
|
||||
match_block_trailing_comma = false
|
||||
blank_lines_upper_bound = 1
|
||||
blank_lines_lower_bound = 0
|
||||
edition = "2015"
|
||||
style_edition = "2015"
|
||||
version = "One"
|
||||
inline_attribute_width = 0
|
||||
format_generated_files = true
|
||||
generated_marker_line_search_limit = 5
|
||||
merge_derives = true
|
||||
use_try_shorthand = false
|
||||
use_field_init_shorthand = false
|
||||
force_explicit_abi = true
|
||||
condense_wildcard_suffixes = false
|
||||
color = "Auto"
|
||||
required_version = "1.8.0"
|
||||
unstable_features = false
|
||||
disable_all_formatting = false
|
||||
skip_children = false
|
||||
show_parse_errors = true
|
||||
error_on_line_overflow = false
|
||||
error_on_unformatted = false
|
||||
ignore = []
|
||||
emit_mode = "Files"
|
||||
make_backup = false
|
||||
edition = "2024"
|
||||
style_edition = "2024"
|
||||
hard_tabs = true
|
||||
hex_literal_case = "Upper"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user