Various changes, staged

This commit is contained in:
james 2025-12-25 10:46:03 +10:30
parent f00cfadb76
commit 7fcf00b77f
No known key found for this signature in database
GPG Key ID: E1FFBA228F4CAD87
37 changed files with 1435 additions and 520 deletions

View File

@ -1,8 +1,7 @@
use log::trace;
use sevenz_rust2::ArchiveReader; use sevenz_rust2::ArchiveReader;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::fs::File; use std::fs::File;
use std::ops::{Add, AddAssign}; use std::ops::AddAssign;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
const DEFAULT_LOCATION: &str = "./lib/modules"; const DEFAULT_LOCATION: &str = "./lib/modules";
@ -31,21 +30,6 @@ impl Default for Bimage {
.into_iter() .into_iter()
.collect::<Vec<_>>(); .collect::<Vec<_>>();
modules.sort(); modules.sort();
/*let packages = reader
.archive()
.files
.iter()
.filter(|e| !e.is_directory)
.map(|e| {
if e.name.contains("java.base") {
println!("{:?}", e);
}
("Greg".to_owned(), "Dave".to_owned())
})
.collect::<HashMap<_, _>>();*/
let packages = HashMap::new(); let packages = HashMap::new();
Self { Self {
@ -85,9 +69,6 @@ impl Bimage {
pub fn get_class(&mut self, module: &str, class: &str) -> Result<Vec<u8>, String> { pub fn get_class(&mut self, module: &str, class: &str) -> Result<Vec<u8>, String> {
// trace!("Modules{:#?}", self.modules); // trace!("Modules{:#?}", self.modules);
if class.contains("ScopedMemoryAccess") {
println!("Time to scoped: {:?}", self.total_access_time)
}
let start = Instant::now(); let start = Instant::now();
let path = Self::resolve_path(module, class); let path = Self::resolve_path(module, class);
let res = self.image.read_file(&path).map_err(|e| { let res = self.image.read_file(&path).map_err(|e| {

View File

@ -1,15 +1,12 @@
use crate::attributes::AttributeInfo; use crate::class_file::attributes::BootstrapMethodsAttribute;
use crate::class_file::{ use crate::class_file::{ClassFlags, ConstantPoolEntry, FieldData, MethodData};
ClassFile, ClassFlags, ConstantPoolEntry, FieldData, FieldInfo, FieldRef, MethodData,
MethodInfo, MethodRef,
};
use crate::error::VmError; use crate::error::VmError;
use crate::{FieldType, MethodDescriptor}; use crate::{FieldType, MethodDescriptor};
use log::trace; use log::trace;
use parking_lot::Mutex; use parking_lot::Mutex;
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use std::sync::atomic::AtomicBool; use std::sync::atomic::AtomicBool;
use std::sync::{Arc, OnceLock, OnceState}; use std::sync::{Arc, OnceLock};
use std::thread::ThreadId; use std::thread::ThreadId;
/// JVM Spec 5.5: Initialization states for a class /// JVM Spec 5.5: Initialization states for a class
@ -25,6 +22,8 @@ pub enum InitState {
Error(String), Error(String),
} }
pub type ClassRef = Arc<RuntimeClass>;
#[derive(Debug)] #[derive(Debug)]
pub struct RuntimeClass { pub struct RuntimeClass {
pub constant_pool: Arc<Vec<ConstantPoolEntry>>, pub constant_pool: Arc<Vec<ConstantPoolEntry>>,
@ -42,6 +41,7 @@ pub struct RuntimeClass {
pub super_interfaces: Vec<Arc<RuntimeClass>>, pub super_interfaces: Vec<Arc<RuntimeClass>>,
pub component_type: Option<Arc<RuntimeClass>>, pub component_type: Option<Arc<RuntimeClass>>,
pub source_file: Option<String>, pub source_file: Option<String>,
pub bootstrap_attribute: Option<BootstrapMethodsAttribute>,
} }
impl Hash for RuntimeClass { impl Hash for RuntimeClass {
fn hash<H: Hasher>(&self, state: &mut H) { fn hash<H: Hasher>(&self, state: &mut H) {

View File

@ -1,11 +1,10 @@
use crate::class_file::constant_pool::ConstantPoolExt; use crate::class_file::constant_pool::ConstantPoolExt;
use crate::class_file::{ClassFile, Constant, ConstantPoolEntry}; use crate::class_file::{ClassFile, Constant};
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; use deku::ctx::Endian;
use deku::{DekuError, DekuReader};
use deku_derive::DekuRead;
use std::fmt::{Display, Formatter};
use std::str::FromStr;
#[derive(Clone, PartialEq, Debug, DekuRead)] #[derive(Clone, PartialEq, Debug, DekuRead)]
#[deku(ctx = "_endian: deku::ctx::Endian", endian = "big")] #[deku(ctx = "_endian: deku::ctx::Endian", endian = "big")]
@ -24,7 +23,7 @@ pub enum Attribute {
ConstantValue(Constant), ConstantValue(Constant),
Code(CodeAttribute), Code(CodeAttribute),
StackMapTable(Vec<u8>), StackMapTable(Vec<u8>),
BootstrapMethods, BootstrapMethods(BootstrapMethodsAttribute),
NestHost, NestHost,
NestMembers, NestMembers,
PermittedSubclasses, PermittedSubclasses,
@ -61,6 +60,30 @@ pub enum ArrayType {
T_LONG, T_LONG,
} }
impl FromStr for ArrayType {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
// Integral types
"int" => Ok(Self::T_INT),
"long" => Ok(Self::T_LONG),
"short" => Ok(Self::T_SHORT),
"char" => Ok(Self::T_CHAR),
"byte" => Ok(Self::T_BYTE),
// Floating-point types
"float" => Ok(Self::T_FLOAT),
"double" => Ok(Self::T_DOUBLE),
// Other types
"boolean" => Ok(Self::T_BOOLEAN),
_ => Err("Invalid primitive type name".to_string()),
}
}
}
#[derive(Clone, PartialEq, Debug)] #[derive(Clone, PartialEq, Debug)]
pub struct LookupSwitchData { pub struct LookupSwitchData {
pub default: i32, pub default: i32,
@ -123,12 +146,17 @@ impl<'a> DekuReader<'a, (Endian, u16)> for TableSwitchData {
offsets.push(i32::from_reader_with_ctx(reader, Endian::Big)?); offsets.push(i32::from_reader_with_ctx(reader, Endian::Big)?);
} }
Ok(TableSwitchData { default, low, high, offsets }) Ok(TableSwitchData {
default,
low,
high,
offsets,
})
} }
} }
impl Display for ArrayType { impl Display for ArrayType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let str = match self { let str = match self {
ArrayType::T_BOOLEAN => "[Z", ArrayType::T_BOOLEAN => "[Z",
ArrayType::T_CHAR => "[C", ArrayType::T_CHAR => "[C",
@ -143,22 +171,6 @@ impl Display for ArrayType {
} }
} }
// impl TryFrom<u8> for Ops {
// type Error = ();
//
// fn try_from(value: u8) -> Result<Self, Self::Error> {
// match value {
// 0x2A => Ok(Ops::Aload0),
// 0x2B => Ok(Ops::InvokeSpecial(0)),
// 0x10 => Ok(Ops::Bipush(0)),
// 0xB5 => Ok(Ops::Putfield(0)),
// 0x14 => Ok(Ops::Ldc2_w(0)),
// 0xB1 => Ok(Ops::Retern),
// _ => Err(())
// }
// }
// }
impl AttributeInfo { impl AttributeInfo {
// pub fn parse_attribute(&self, constant_pool: &[ConstantPoolEntry]) -> Option<Attribute> { // pub fn parse_attribute(&self, constant_pool: &[ConstantPoolEntry]) -> Option<Attribute> {
// let name = crate::class_file::pool_get_string(constant_pool, self.attribute_name_index)?; // let name = crate::class_file::pool_get_string(constant_pool, self.attribute_name_index)?;
@ -226,7 +238,7 @@ impl AttributeInfo {
impl LocalVariableTableAttribute {} impl LocalVariableTableAttribute {}
impl Display for AttributeInfo { impl Display for AttributeInfo {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
writeln!( writeln!(
f, f,
"AttributeInfo {{ name_index: {}, length: {} }}", "AttributeInfo {{ name_index: {}, length: {} }}",
@ -236,7 +248,7 @@ impl Display for AttributeInfo {
} }
impl Display for Attribute { impl Display for Attribute {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self { match self {
Attribute::Code(code) => write!(f, "Code attribute: {}", code), Attribute::Code(code) => write!(f, "Code attribute: {}", code),
Attribute::SourceFile(index) => write!(f, "SourceFile attribute, index: {}", index), Attribute::SourceFile(index) => write!(f, "SourceFile attribute, index: {}", index),
@ -252,6 +264,9 @@ impl Display for Attribute {
Attribute::LocalVariableTable(table) => { Attribute::LocalVariableTable(table) => {
write!(f, "LocalVariableTable attribute: {}", table) write!(f, "LocalVariableTable attribute: {}", table)
} }
Attribute::BootstrapMethods(bootstrap) => {
write!(f, "BootstrapMethods attribute: {}", bootstrap)
}
Attribute::Unknown(name, data) => { Attribute::Unknown(name, data) => {
write!(f, "Unknown attribute '{}', {} bytes", name, data.len()) write!(f, "Unknown attribute '{}', {} bytes", name, data.len())
} }
@ -263,7 +278,7 @@ impl Display for Attribute {
} }
impl Display for CodeAttribute { impl Display for CodeAttribute {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!( write!(
f, f,
"stack={}, locals={}, code={} bytes, exceptions={}, attributes={}", "stack={}, locals={}, code={} bytes, exceptions={}, attributes={}",
@ -320,7 +335,7 @@ pub struct LocalVariableTableEntry {
} }
impl Display for LocalVariableTableAttribute { impl Display for LocalVariableTableAttribute {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!( write!(
f, f,
"local_variable_table_length={}, entries={}", "local_variable_table_length={}, entries={}",
@ -338,6 +353,23 @@ pub struct LineNumberTableAttribute {
pub line_number_table: Vec<LineNumberTableEntry>, pub line_number_table: Vec<LineNumberTableEntry>,
} }
#[derive(Clone, PartialEq, Debug, DekuRead)]
#[deku(endian = "big")]
pub struct BootstrapMethodsAttribute {
pub num_bootstrap_methods: u16,
#[deku(count = "num_bootstrap_methods")]
pub bootstrap_methods: Vec<BootstrapMethod>,
}
#[derive(Clone, PartialEq, Debug, DekuRead)]
#[deku(ctx = "_endian: deku::ctx::Endian", endian = "big")]
pub struct BootstrapMethod {
pub bootstrap_method_ref: u16,
pub num_bootstrap_arguments: u16,
#[deku(count = "num_bootstrap_arguments")]
pub bootstrap_arguments: Vec<u16>,
}
#[derive(Clone, PartialEq, Debug, DekuRead)] #[derive(Clone, PartialEq, Debug, DekuRead)]
#[deku(ctx = "_endian: deku::ctx::Endian", endian = "big")] #[deku(ctx = "_endian: deku::ctx::Endian", endian = "big")]
pub struct LineNumberTableEntry { pub struct LineNumberTableEntry {
@ -346,7 +378,7 @@ pub struct LineNumberTableEntry {
} }
impl Display for LineNumberTableAttribute { impl Display for LineNumberTableAttribute {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!( write!(
f, f,
"table_length={}, entries={}", "table_length={}, entries={}",
@ -355,3 +387,14 @@ impl Display for LineNumberTableAttribute {
) )
} }
} }
impl Display for BootstrapMethodsAttribute {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"num_bootstrap_methods={}, methods={}",
self.num_bootstrap_methods,
self.bootstrap_methods.len()
)
}
}

View File

@ -1,6 +1,7 @@
use crate::attributes::{Attribute, AttributeInfo, CodeAttribute, LineNumberTableEntry}; use crate::class_file::attributes::{
use crate::class::RuntimeClass; Attribute, AttributeInfo, CodeAttribute, LineNumberTableEntry,
use crate::class_file::constant_pool::{ConstantPoolError, ConstantPoolExt, ConstantPoolOwned}; };
use crate::class_file::constant_pool::{ConstantPoolExt, ConstantPoolOwned};
use crate::instructions::Ops; use crate::instructions::Ops;
use crate::value::Value; use crate::value::Value;
use crate::{BaseType, FieldType, MethodDescriptor}; use crate::{BaseType, FieldType, MethodDescriptor};
@ -214,8 +215,8 @@ pub struct ConstantPackageInfo {
} }
// Display implementations for better formatting // Display implementations for better formatting
impl fmt::Display for ClassFile { impl Display for ClassFile {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
writeln!(f, "Class File Information:")?; writeln!(f, "Class File Information:")?;
writeln!( writeln!(
f, f,
@ -266,8 +267,8 @@ impl fmt::Display for ClassFile {
} }
} }
impl fmt::Display for ConstantPoolEntry { impl Display for ConstantPoolEntry {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self { match self {
ConstantPoolEntry::Utf8(info) => { ConstantPoolEntry::Utf8(info) => {
let s = String::from_utf8_lossy(&info.bytes); let s = String::from_utf8_lossy(&info.bytes);
@ -323,8 +324,8 @@ impl fmt::Display for ConstantPoolEntry {
} }
} }
impl fmt::Display for FieldInfo { impl Display for FieldInfo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!( write!(
f, f,
"flags=0x{:04X}, name=#{}, descriptor=#{}, attrs={}", "flags=0x{:04X}, name=#{}, descriptor=#{}, attrs={}",
@ -336,8 +337,8 @@ impl fmt::Display for FieldInfo {
} }
} }
impl fmt::Display for MethodInfo { impl Display for MethodInfo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let attrs: Vec<_> = self let attrs: Vec<_> = self
.attributes .attributes
.iter() .iter()
@ -395,7 +396,7 @@ impl ClassFile {
expanded expanded
} }
fn format_attribute(&self, f: &mut fmt::Formatter<'_>, attr: &AttributeInfo) -> fmt::Result { fn format_attribute(&self, f: &mut Formatter<'_>, attr: &AttributeInfo) -> fmt::Result {
let attribute = attr let attribute = attr
.get(self) .get(self)
.unwrap_or_else(|| panic!("Failed to parse attribute {}", attr)); .unwrap_or_else(|| panic!("Failed to parse attribute {}", attr));
@ -512,7 +513,7 @@ fn read_bytecode_with_offsets<R: deku::no_std_io::Read + deku::no_std_io::Seek>(
let op = Ops::from_reader_with_ctx(reader, byte_offset)?; let op = Ops::from_reader_with_ctx(reader, byte_offset)?;
let end_pos = reader.bits_read; let end_pos = reader.bits_read;
let op_bytes = ((end_pos - start_pos) / 8) as usize; let op_bytes = (end_pos - start_pos) / 8;
code.push((byte_offset, op)); code.push((byte_offset, op));
byte_offset += op_bytes as u16; byte_offset += op_bytes as u16;
@ -678,6 +679,12 @@ impl From<MethodData> for MethodRef {
} }
} }
impl From<&MethodData> for MethodRef {
fn from(value: &MethodData) -> Self {
value.clone().into()
}
}
#[derive(Debug)] #[derive(Debug)]
pub struct FieldRef { pub struct FieldRef {
pub class: String, pub class: String,
@ -685,6 +692,18 @@ pub struct FieldRef {
pub desc: FieldType, pub desc: FieldType,
} }
impl FieldRef {
pub fn new(class: &str, name: &str, field: FieldType) -> Self {
let class = class.to_string();
let name = name.to_string();
Self {
class,
name,
desc: field,
}
}
}
#[derive(Debug)] #[derive(Debug)]
pub struct FieldData { pub struct FieldData {
pub name: String, pub name: String,
@ -699,19 +718,38 @@ pub enum Constant {
Long(i64), Long(i64),
Float(f32), Float(f32),
Double(f64), Double(f64),
String(String), String(u16),
} }
impl From<Constant> for Value { // impl From<Constant> for Value {
fn from(value: Constant) -> Self { // fn from(value: Constant) -> Self {
// match value {
// Constant::Int(x) => x.into(),
// Constant::Long(x) => x.into(),
// Constant::Float(x) => x.into(),
// Constant::Double(x) => x.into(),
// Constant::String(x) => {
// todo!("Constant string")
// }
// }
// }
// }
impl From<&ConstantPoolEntry> for Constant {
fn from(value: &ConstantPoolEntry) -> Self {
value.clone().into()
}
}
impl From<ConstantPoolEntry> for Constant {
fn from(value: ConstantPoolEntry) -> Self {
match value { match value {
Constant::Int(x) => x.into(), ConstantPoolEntry::Integer(i) => Self::Int(i),
Constant::Long(x) => x.into(), ConstantPoolEntry::Float(f) => Self::Float(f),
Constant::Float(x) => x.into(), ConstantPoolEntry::Long(l) => Self::Long(l),
Constant::Double(x) => x.into(), ConstantPoolEntry::Double(d) => Self::Double(d),
Constant::String(x) => { ConstantPoolEntry::String(s) => Self::String(s.string_index),
todo!("Constant string") _ => panic!(),
}
} }
} }
} }

View File

@ -1,5 +1,6 @@
use crate::attributes::{ use crate::class_file::attributes::{
Attribute, AttributeInfo, CodeAttribute, LineNumberTableAttribute, LocalVariableTableAttribute, Attribute, AttributeInfo, BootstrapMethodsAttribute, CodeAttribute, LineNumberTableAttribute,
LocalVariableTableAttribute,
}; };
use crate::class_file::{ use crate::class_file::{
ConstantClassInfo, ConstantDynamicInfo, ConstantFieldrefInfo, ConstantInterfaceMethodrefInfo, ConstantClassInfo, ConstantDynamicInfo, ConstantFieldrefInfo, ConstantInterfaceMethodrefInfo,
@ -9,8 +10,8 @@ use crate::class_file::{
MethodRef, MethodRef,
}; };
use crate::error::VmError; use crate::error::VmError;
use crate::{pool_get_impl, FieldType, MethodDescriptor}; use crate::{FieldType, MethodDescriptor, pool_get_impl};
use cesu8::{from_java_cesu8, Cesu8DecodingError}; use cesu8::{Cesu8DecodingError, from_java_cesu8};
use deku::DekuContainerRead; use deku::DekuContainerRead;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
@ -137,8 +138,12 @@ pub trait ConstantPoolExt: ConstantPoolGet {
// trace!("Parsing attribute with name: {}", name); // trace!("Parsing attribute with name: {}", name);
match name.as_ref() { match name.as_ref() {
"ConstantValue" => {
let thing = self.get_constant(u16::from_be_bytes([a.info[0], a.info[1]]))?;
Ok(Attribute::ConstantValue(thing.into()))
}
"Code" => { "Code" => {
let (_, mut code_attr) = CodeAttribute::from_bytes((a.info.as_slice(), 0))?; let (_, code_attr) = CodeAttribute::from_bytes((a.info.as_slice(), 0))?;
Ok(Attribute::Code(code_attr)) Ok(Attribute::Code(code_attr))
} }
"SourceFile" => { "SourceFile" => {
@ -160,6 +165,10 @@ pub trait ConstantPoolExt: ConstantPoolGet {
let (_, lvt) = LocalVariableTableAttribute::from_bytes((&a.info.as_slice(), 0))?; let (_, lvt) = LocalVariableTableAttribute::from_bytes((&a.info.as_slice(), 0))?;
Ok(Attribute::LocalVariableTable(lvt)) Ok(Attribute::LocalVariableTable(lvt))
} }
"BootstrapMethods" => {
let (_, bsm) = BootstrapMethodsAttribute::from_bytes((&a.info.as_slice(), 0))?;
Ok(Attribute::BootstrapMethods(bsm))
}
_ => Ok(Attribute::Unknown(name.to_string(), a.info.clone())), _ => Ok(Attribute::Unknown(name.to_string(), a.info.clone())),
} }
} }

View File

@ -1,6 +1,5 @@
pub mod attributes;
pub mod class_file; pub mod class_file;
pub mod constant_pool; pub mod constant_pool;
// Re-export items if you want them accessible directly from class_file::
pub use class_file::*; // optional pub use class_file::*; // optional
// pub use constant_pool::*; // optional

View File

@ -1,8 +1,10 @@
use crate::attributes::Attribute;
use crate::bimage::Bimage; use crate::bimage::Bimage;
use crate::class::{InitState, RuntimeClass}; use crate::class::{InitState, RuntimeClass};
use crate::class_file::attributes::Attribute;
use crate::class_file::constant_pool::{ConstantPoolExt, ConstantPoolGet}; use crate::class_file::constant_pool::{ConstantPoolExt, ConstantPoolGet};
use crate::class_file::{ClassFile, ClassFlags, FieldData, FieldFlags, MethodData, MethodFlags}; use crate::class_file::{
ClassFile, ClassFlags, Constant, FieldData, FieldFlags, MethodData, MethodFlags,
};
use crate::error::VmError; use crate::error::VmError;
use crate::{FieldType, MethodDescriptor}; use crate::{FieldType, MethodDescriptor};
use dashmap::DashMap; use dashmap::DashMap;
@ -85,7 +87,7 @@ pub fn resolve_path(what: &str) -> Result<(PathBuf, String), String> {
#[derive(Default)] #[derive(Default)]
pub struct ClassLoader { pub struct ClassLoader {
pub(crate) classes: DashMap<(String, LoaderId), Arc<RuntimeClass>>, pub(crate) classes: DashMap<(String, LoaderId), Arc<RuntimeClass>>,
bimage: Bimage, bimage: Option<Bimage>,
// pub needs_init: Vec<Arc<RuntimeClass>>, // pub needs_init: Vec<Arc<RuntimeClass>>,
} }
@ -93,10 +95,12 @@ type LoaderId = Option<u32>;
impl ClassLoader { impl ClassLoader {
pub fn access_time(&self) -> Duration { pub fn access_time(&self) -> Duration {
self.bimage.total_access_time self.bimage
.as_ref()
.map_or(Duration::ZERO, |b| b.total_access_time)
} }
pub fn new() -> Result<Self, String> { pub fn new() -> Result<Self, String> {
let loader = Self::default(); let loader = Self::with_bimage();
// for entry in VM_CLASSES { // for entry in VM_CLASSES {
// let module_class = format!("{}/{}", "java.base", entry);; // let module_class = format!("{}/{}", "java.base", entry);;
// loader.load_class(&module_class)?; // loader.load_class(&module_class)?;
@ -104,6 +108,14 @@ impl ClassLoader {
Ok(loader) Ok(loader)
} }
/// Create a ClassLoader with the boot image loaded
pub fn with_bimage() -> Self {
Self {
classes: DashMap::new(),
bimage: Some(Bimage::default()),
}
}
pub fn class_from_mirror_id(&self, id: u32) -> Option<Arc<RuntimeClass>> { pub fn class_from_mirror_id(&self, id: u32) -> Option<Arc<RuntimeClass>> {
self.classes self.classes
.iter() .iter()
@ -147,37 +159,34 @@ impl ClassLoader {
class_name: &str, class_name: &str,
loader: LoaderId, loader: LoaderId,
) -> Result<Arc<RuntimeClass>, VmError> { ) -> Result<Arc<RuntimeClass>, VmError> {
let class_name = match class_name {
"B" => "byte",
"C" => "char",
"D" => "double",
"F" => "float",
"I" => "int",
"J" => "long",
"S" => "short",
"Z" => "boolean",
_ => class_name,
};
// Normalize L-descriptors: Ljava/lang/String; -> java/lang/String
let class_name = class_name
.strip_prefix('L')
.and_then(|s| s.strip_suffix(';'))
.unwrap_or(class_name);
if let Some(class) = self.classes.get(&(class_name.to_string(), loader)) { if let Some(class) = self.classes.get(&(class_name.to_string(), loader)) {
return Ok(class.clone()); return Ok(class.clone());
} }
if class_name.starts_with("[") { if let Some(component_name) = class_name.strip_prefix('[') {
let component_name = class_name.strip_prefix("[").unwrap(); if component_name.is_empty() {
let component = match component_name.chars().next() { return Err(VmError::LoaderError(
Some('B') => self.get_or_load("byte", None),
Some('C') => self.get_or_load("char", None),
Some('D') => self.get_or_load("double", None),
Some('F') => self.get_or_load("float", None),
Some('I') => self.get_or_load("int", None),
Some('J') => self.get_or_load("long", None),
Some('S') => self.get_or_load("short", None),
Some('Z') => self.get_or_load("boolean", None),
Some('[') => self.get_or_load(component_name, None),
Some('L') => {
let class_name = component_name
.strip_prefix('L')
.and_then(|s| s.strip_suffix(';'))
.expect("invalid L descriptor");
self.get_or_load(class_name, None)
}
None => Err(VmError::LoaderError(
"empty component descriptor".to_string(), "empty component descriptor".to_string(),
)), ));
_ => Err(VmError::LoaderError(format!( }
"invalid component descriptor: {}", let component = self.get_or_load(component_name, loader)?;
component_name
))),
}?;
// let component = self.get_or_load(component_name, None)?;
let arr_class = self.create_array_class(component); let arr_class = self.create_array_class(component);
return Ok(arr_class); return Ok(arr_class);
} }
@ -197,9 +206,11 @@ impl ClassLoader {
let (module, class_fqn) = ("", what); let (module, class_fqn) = ("", what);
let bytes = self let bytes = self
.bimage .bimage
.get_class(module, class_fqn) .as_mut()
.or_else(|e| Self::load_class_from_disk(what)) .and_then(|b| b.get_class(module, class_fqn).ok())
.map_err(|e1| VmError::LoaderError(format!("failed to find class: {}", what)))?; .ok_or_else(|| "not in bimage")
.or_else(|_| Self::load_class_from_disk(what))
.map_err(|_| VmError::LoaderError(format!("failed to find class: {}", what)))?;
let (_, cf) = let (_, cf) =
ClassFile::from_bytes((bytes.as_ref(), 0)).map_err(|e| VmError::DekuError(e))?; ClassFile::from_bytes((bytes.as_ref(), 0)).map_err(|e| VmError::DekuError(e))?;
let runtime = self.runtime_class(cf); let runtime = self.runtime_class(cf);
@ -287,7 +298,13 @@ impl ClassLoader {
if let Attribute::ConstantValue(val) = if let Attribute::ConstantValue(val) =
constant_pool.parse_attribute(x.clone()).unwrap() constant_pool.parse_attribute(x.clone()).unwrap()
{ {
Some(val.into()) match val {
Constant::Int(i) => Some(i.into()),
Constant::Long(l) => Some(l.into()),
Constant::Float(f) => Some(f.into()),
Constant::Double(d) => Some(d.into()),
Constant::String(_) => None,
}
} else { } else {
None None
} }
@ -371,6 +388,14 @@ impl ClassLoader {
} }
}); });
let bootstrap_attribute =
class_file.attributes.iter().find_map(|attr| {
match constant_pool.parse_attribute(attr.clone()).ok()? {
Attribute::BootstrapMethods(index) => Some(index),
_ => None,
}
});
RuntimeClass { RuntimeClass {
constant_pool, constant_pool,
access_flags, access_flags,
@ -386,6 +411,7 @@ impl ClassLoader {
component_type: None, component_type: None,
source_file, source_file,
mirror_in_progress: Default::default(), mirror_in_progress: Default::default(),
bootstrap_attribute,
} }
} }
// pub fn get_or_create_array_class(class_name: &str) -> RuntimeClass { // pub fn get_or_create_array_class(class_name: &str) -> RuntimeClass {
@ -440,6 +466,7 @@ impl ClassLoader {
super_interfaces: vec![cloneable, serializable], super_interfaces: vec![cloneable, serializable],
component_type: Some(component), // new field component_type: Some(component), // new field
source_file: None, source_file: None,
bootstrap_attribute: None,
}; };
let klass = Arc::new(klass); let klass = Arc::new(klass);
self.classes.insert((name.to_string(), None), klass.clone()); self.classes.insert((name.to_string(), None), klass.clone());
@ -472,6 +499,7 @@ impl ClassLoader {
super_interfaces: vec![], super_interfaces: vec![],
component_type: None, component_type: None,
source_file: None, source_file: None,
bootstrap_attribute: None,
}); });
self.classes.insert((name.to_string(), None), klass.clone()); self.classes.insert((name.to_string(), None), klass.clone());

View File

@ -1,21 +1,25 @@
use crate::attributes::{CodeAttribute, LineNumberTableEntry};
use crate::class::RuntimeClass; use crate::class::RuntimeClass;
use crate::class_file::constant_pool::{ConstantPoolExt, ConstantPoolGet}; use crate::class_file::attributes::{CodeAttribute, LineNumberTableEntry};
use crate::class_file::constant_pool::{ConstantPoolError, ConstantPoolExt, ConstantPoolGet};
use crate::class_file::{Bytecode, ConstantPoolEntry, MethodRef}; use crate::class_file::{Bytecode, ConstantPoolEntry, MethodRef};
use crate::error::{StackTraceElement, VmError}; use crate::error::{StackTraceElement, VmError};
use crate::instructions::{Ops, WideData}; use crate::instructions::{Ops, WideData};
use crate::objects::ReferenceKind; use crate::objects::ReferenceKind;
use crate::objects::array::ArrayReference; use crate::objects::array::ArrayReference;
use crate::prim::*; use crate::prim::*;
use crate::value::{LocalVariables, OperandStack, Primitive, Value}; use crate::value::{Primitive, Value};
use crate::vm::Vm; use crate::vm::Vm;
use crate::{ use crate::{
BaseType, FieldType, VmThread, array_store, array_store_cast, binary_op, convert_float_to_int, BaseType, FieldType, MethodDescriptor, VmThread, array_store, array_store_cast, binary_op,
convert_int_narrow, convert_simple, error, float_cmp, if_int_cmp, if_int_zero, int_div_rem, convert_float_to_int, convert_int_narrow, convert_simple, float_cmp, if_int_cmp, if_int_zero,
load, shift_op, store, unary_op, int_div_rem, load, shift_op, store, unary_op,
}; };
use deku::DekuContainerRead; use deku::DekuContainerRead;
use log::{info, trace, warn}; use log::{trace, warn};
use crate::frame::local_vars::LocalVariables;
use crate::frame::operand_stack::OperandStack;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
use std::sync::Arc; use std::sync::Arc;
@ -95,10 +99,7 @@ impl Frame {
table table
.iter() .iter()
.rev() .rev()
.find(|entry| { .find(|entry| entry.start_pc as i64 <= self.pc)
entry.start_pc as i64 <= self.pc
// (*start_pc as i64) <= self.pc)
})
.map(|entry| entry.line_number) .map(|entry| entry.line_number)
} }
@ -144,9 +145,9 @@ impl Frame {
Ok(ExecutionResult::Return(())) => return Ok(None), Ok(ExecutionResult::Return(())) => return Ok(None),
Ok(ExecutionResult::ReturnValue(val)) => return Ok(Some(val)), Ok(ExecutionResult::ReturnValue(val)) => return Ok(Some(val)),
Ok(ExecutionResult::Advance(offset)) => { Ok(ExecutionResult::Advance(offset)) => {
info!("pre offset: {}", self.pc); trace!("pre offset: {}", self.pc);
self.pc += offset as i64; self.pc += offset as i64;
info!("post offset: {}", self.pc); trace!("post offset: {}", self.pc);
} }
Ok(_) => self.pc += 1, Ok(_) => self.pc += 1,
Err(x) => { Err(x) => {
@ -178,12 +179,13 @@ impl Frame {
self.bytecode self.bytecode
.code .code
.iter() .iter()
.find(|(offset, op)| *offset as i64 >= self.pc) .find(|(offset, _op)| *offset as i64 >= self.pc)
.map(|op| op.clone()) .map(|op| op.clone())
} }
} }
enum ExecutionResult { #[derive(Debug)]
pub(crate) enum ExecutionResult {
Continue, Continue,
Advance(i32), Advance(i32),
Return(()), Return(()),
@ -194,7 +196,7 @@ impl Frame {
fn pop(&mut self) -> Result<Value, VmError> { fn pop(&mut self) -> Result<Value, VmError> {
self.stack.pop() self.stack.pop()
} }
fn execute_instruction(&mut self, op: Ops) -> Result<ExecutionResult, VmError> { pub(crate) fn execute_instruction(&mut self, op: Ops) -> Result<ExecutionResult, VmError> {
match op { match op {
Ops::nop => { Ops::nop => {
// TODO Should nop have any side effects? // TODO Should nop have any side effects?
@ -290,8 +292,6 @@ impl Frame {
}; };
if let Some(x) = resolved { if let Some(x) = resolved {
self.stack.push(x); self.stack.push(x);
// on second thoughts, i dont think that's right
// self.stack.push(Value::Reference(None));
}; };
Ok(ExecutionResult::Continue) Ok(ExecutionResult::Continue)
} }
@ -540,7 +540,7 @@ impl Frame {
Ops::fastore => array_store!(self, Float, Float), Ops::fastore => array_store!(self, Float, Float),
Ops::dastore => array_store!(self, Double, Double), Ops::dastore => array_store!(self, Double, Double),
Ops::aastore => { Ops::aastore => {
let Value::Reference((value)) = self.pop()? else { let Value::Reference(value) = self.pop()? else {
panic!("Value on stack was not ref") panic!("Value on stack was not ref")
}; };
let Value::Primitive(Primitive::Int(index)) = self.pop()? else { let Value::Primitive(Primitive::Int(index)) = self.pop()? else {
@ -967,7 +967,7 @@ impl Frame {
let x: i32 = match self.pop()? { let x: i32 = match self.pop()? {
Value::Primitive(Primitive::Int(v)) => v, Value::Primitive(Primitive::Int(v)) => v,
Value::Primitive(Primitive::Boolean(v)) => { Value::Primitive(Primitive::Boolean(v)) => {
if v { if v == 1u8 {
1 1
} else { } else {
0 0
@ -985,7 +985,7 @@ impl Frame {
match &self.method_ref.desc.return_type { match &self.method_ref.desc.return_type {
Some(FieldType::Base(base_type)) => match base_type { Some(FieldType::Base(base_type)) => match base_type {
BaseType::Boolean => Ok(ExecutionResult::ReturnValue((x != 0).into())), BaseType::Boolean => Ok(ExecutionResult::ReturnValue((x as u8).into())),
BaseType::Byte => Ok(ExecutionResult::ReturnValue((x as i8).into())), BaseType::Byte => Ok(ExecutionResult::ReturnValue((x as i8).into())),
BaseType::Char => Ok(ExecutionResult::ReturnValue((x as u16).into())), BaseType::Char => Ok(ExecutionResult::ReturnValue((x as u16).into())),
BaseType::Short => Ok(ExecutionResult::ReturnValue((x as i16).into())), BaseType::Short => Ok(ExecutionResult::ReturnValue((x as i16).into())),
@ -1178,7 +1178,7 @@ impl Frame {
let args = self.stack.pop_n(args_count)?; let args = self.stack.pop_n(args_count)?;
let refe = args let refe = args
.first() .first()
.expect("Must have reciever") .expect("Must have receiver")
.as_ref_kind() .as_ref_kind()
.expect("Must be ref"); .expect("Must be ref");
let class = refe.class(); let class = refe.class();
@ -1193,8 +1193,104 @@ impl Frame {
Ok(ExecutionResult::Continue) Ok(ExecutionResult::Continue)
} }
Ops::invokedynamic(_, _) => { Ops::invokedynamic(index, _b) => {
todo!("invokeDynamic") debug_assert_eq!(_b, 0u16);
// Check if already resolved (you'll want to cache this)
// For now, let's just resolve it every time
let dyninfo = self.pool.get_invoke_dynamic_info(index)?;
let bootstrap_attr = self.class.bootstrap_attribute.as_ref().unwrap();
let bsm =
&bootstrap_attr.bootstrap_methods[dyninfo.bootstrap_method_attr_index as usize];
let name_and_type = self
.pool
.get_name_and_type_info(dyninfo.name_and_type_index)?;
let call_site_name = self.pool.get_string(name_and_type.name_index)?;
let call_site_desc = self.pool.get_string(name_and_type.descriptor_index)?;
let call_site_method_type = MethodDescriptor::parse(&call_site_desc)
.map_err(ConstantPoolError::DescriptorParseError)?;
// The bootstrap method handle reference
let bsm_handle = self.pool.get_method_handle_info(bsm.bootstrap_method_ref)?;
let kind = bsm_handle.reference_kind;
let heck = self.pool.resolve_method_ref(bsm_handle.reference_index)?;
println!("Bootstrap method handle: kind={:?}, ref={:?}", kind, heck);
// Resolve static arguments from constant pool
let static_args: Vec<Value> = Vec::new();
for arg_index in &bsm.bootstrap_arguments {
let info = self.pool.get_string_info(*arg_index)?;
let string = self.pool.get_string(info.string_index)?;
println!("{}", string);
// static_args.push(arg);
}
let class_objects = call_site_method_type
.parameters
.iter()
.map(|param| {
let name = param.as_class_name();
let klass = self.thread.get_class(&name)?;
let obj = self.thread.gc.read().get(*klass.mirror.get().unwrap());
Ok(Some(obj))
})
.collect::<Result<Vec<Option<ReferenceKind>>, VmError>>()?;
let class_array_class = self.thread.get_class("[Ljava/lang/Class;")?;
let ptypes = self
.thread
.gc
.write()
.new_object_array_from(class_array_class, class_objects.into_boxed_slice());
let pval: Value = ptypes.clone().into();
println!("==================");
println!("{}", pval);
println!("==================");
let return_type = call_site_method_type.return_type.unwrap();
let ret = return_type.as_class_name();
let ret_klass = self.thread.get_class(&ret)?;
let rtype = self.thread.gc.read().get(*ret_klass.mirror.get().unwrap());
let mt_cn_ref = MethodRef {
class: "java/lang/invoke/MethodType".to_string(),
name: "methodType".to_string(),
desc: MethodDescriptor::method_type(),
};
let mt = self
.thread
.invoke(mt_cn_ref, vec![rtype.into(), ptypes.into()])?
.unwrap();
// Pop runtime arguments from stack (based on call site descriptor)
let runtime_arg_count = call_site_method_type.parameters.len();
let runtime_args = self.stack.pop_n(runtime_arg_count)?;
// Now we need to:
// 1. Create a MethodHandles.Lookup for current class ---
// 2. Create a MethodType from call_site_desc
// 3. Invoke the bootstrap method with (Lookup, String name, MethodType, ...static_args)
// 4. Get back a CallSite
// 5. Extract the MethodHandle from CallSite.getTarget()
// 6. Invoke that MethodHandle with runtime_args
//wait i think i can just call jvm methods here
let mhc = self.thread.get_class("java/lang/invoke/MethodHandles")?;
let refe = MethodRef {
class: "java/lang/invoke/MethodHandles".to_string(),
name: "lookup".to_string(),
desc: MethodDescriptor::lookup(),
};
let lookup = self.thread.invoke(refe, Vec::new())?.unwrap();
// Full implementation requires MethodHandle invocation support
println!("{}", lookup);
todo!(
// "Full invokedynamic - bootstrap: {}::{}",
// bsm_handle.class_name,
// bsm_handle.method_name
)
} }
// can init class // can init class
@ -1216,11 +1312,11 @@ impl Frame {
let Value::Primitive(Primitive::Int(count)) = value else { let Value::Primitive(Primitive::Int(count)) = value else {
panic!("stack item was not int") panic!("stack item was not int")
}; };
let array = self.thread.gc.write().new_primitive_array( let array = self
array_class, .thread
array_type.clone(), .gc
count, .write()
); .new_primitive_array(array_class, count);
self.stack self.stack
.push(Value::Reference(Some(ReferenceKind::ArrayReference(array)))); .push(Value::Reference(Some(ReferenceKind::ArrayReference(array))));
Ok(ExecutionResult::Continue) Ok(ExecutionResult::Continue)
@ -1246,7 +1342,7 @@ impl Frame {
Ok(ExecutionResult::Continue) Ok(ExecutionResult::Continue)
} }
Ops::arraylength => { Ops::arraylength => {
let Value::Reference(Some(ReferenceKind::ArrayReference((array)))) = let Value::Reference(Some(ReferenceKind::ArrayReference(array))) =
self.stack.pop().expect("value on stack") self.stack.pop().expect("value on stack")
else { else {
panic!("Reference not on stack or not an array") panic!("Reference not on stack or not an array")
@ -1488,3 +1584,314 @@ impl Frame {
Ok(ExecutionResult::Continue) Ok(ExecutionResult::Continue)
} }
} }
#[cfg(test)]
mod tests {
use super::*;
use crate::class::{InitState, RuntimeClass};
use crate::class_file::ClassFlags;
use crate::class_file::class_file::Bytecode;
use crate::vm::Vm;
use deku::DekuContainerRead;
use parking_lot::Mutex;
use std::sync::OnceLock;
use std::sync::atomic::AtomicBool;
/// Create an empty Bytecode for testing using deku deserialization
fn empty_bytecode() -> Bytecode {
// Format: 4 bytes for length (u32 big endian) = 0, then no bytecode
let bytes: [u8; 4] = [0, 0, 0, 0];
let (_rest, bytecode) = Bytecode::from_bytes((&bytes, 0)).unwrap();
bytecode
}
/// Creates a minimal RuntimeClass for testing
fn test_class() -> Arc<RuntimeClass> {
Arc::new(RuntimeClass {
constant_pool: Arc::new(vec![]),
access_flags: ClassFlags::from(0u16),
this_class: "TestClass".to_string(),
super_class: None,
interfaces: vec![],
fields: vec![],
methods: vec![],
mirror: OnceLock::new(),
mirror_in_progress: AtomicBool::new(false),
init_state: Mutex::new(InitState::Initialized),
super_classes: vec![],
super_interfaces: vec![],
component_type: None,
source_file: None,
bootstrap_attribute: None,
})
}
/// Creates a minimal MethodRef for testing
fn test_method_ref() -> MethodRef {
MethodRef {
class: "TestClass".to_string(),
name: "testMethod".to_string(),
desc: MethodDescriptor {
parameters: vec![],
return_type: Some(FieldType::Base(BaseType::Int)),
},
}
}
/// Builder for creating test frames with controlled state
struct TestFrame {
vm: Arc<Vm>,
stack: Vec<Value>,
locals: Vec<Value>,
max_locals: usize,
}
impl TestFrame {
fn new() -> Self {
Self {
vm: Vm::new_for_test(),
stack: vec![],
locals: vec![],
max_locals: 10,
}
}
fn with_stack(mut self, stack: Vec<Value>) -> Self {
self.stack = stack;
self
}
fn with_locals(mut self, locals: Vec<Value>, max: usize) -> Self {
self.locals = locals;
self.max_locals = max;
self
}
fn build(self) -> Frame {
let class = test_class();
let thread = VmThread::current(&self.vm);
let mut stack = OperandStack::with_capacity(100);
for val in self.stack {
stack.push(val);
}
Frame {
pc: 0,
stack,
vars: LocalVariables::from_args(self.locals, self.max_locals),
pool: Arc::new(vec![]),
bytecode: empty_bytecode(),
thread,
method_ref: test_method_ref(),
class,
line_number_table: None,
}
}
}
// Helper to extract int from stack top
fn pop_int(frame: &mut Frame) -> i32 {
match frame.pop().unwrap() {
Value::Primitive(Primitive::Int(i)) => i,
other => panic!("Expected int, got {:?}", other),
}
}
fn pop_long(frame: &mut Frame) -> i64 {
match frame.pop().unwrap() {
Value::Primitive(Primitive::Long(l)) => l,
other => panic!("Expected long, got {:?}", other),
}
}
// ==================== Constant Tests ====================
#[test]
fn test_iconst_0() {
let mut frame = TestFrame::new().build();
let result = frame.execute_instruction(Ops::iconst_0);
assert!(matches!(result, Ok(ExecutionResult::Continue)));
assert_eq!(pop_int(&mut frame), 0);
}
#[test]
fn test_iconst_m1() {
let mut frame = TestFrame::new().build();
let result = frame.execute_instruction(Ops::iconst_m1);
assert!(matches!(result, Ok(ExecutionResult::Continue)));
assert_eq!(pop_int(&mut frame), -1);
}
#[test]
fn test_bipush() {
let mut frame = TestFrame::new().build();
let result = frame.execute_instruction(Ops::bipush(42));
assert!(matches!(result, Ok(ExecutionResult::Continue)));
assert_eq!(pop_int(&mut frame), 42);
}
#[test]
fn test_sipush() {
let mut frame = TestFrame::new().build();
let result = frame.execute_instruction(Ops::sipush(1000));
assert!(matches!(result, Ok(ExecutionResult::Continue)));
assert_eq!(pop_int(&mut frame), 1000);
}
// ==================== Arithmetic Tests ====================
#[test]
fn test_iadd() {
let mut frame = TestFrame::new()
.with_stack(vec![10i32.into(), 20i32.into()])
.build();
let result = frame.execute_instruction(Ops::iadd);
assert!(matches!(result, Ok(ExecutionResult::Continue)));
assert_eq!(pop_int(&mut frame), 30);
}
#[test]
fn test_isub() {
let mut frame = TestFrame::new()
.with_stack(vec![50i32.into(), 20i32.into()])
.build();
let result = frame.execute_instruction(Ops::isub);
assert!(matches!(result, Ok(ExecutionResult::Continue)));
assert_eq!(pop_int(&mut frame), 30);
}
#[test]
fn test_imul() {
let mut frame = TestFrame::new()
.with_stack(vec![6i32.into(), 7i32.into()])
.build();
let result = frame.execute_instruction(Ops::imul);
assert!(matches!(result, Ok(ExecutionResult::Continue)));
assert_eq!(pop_int(&mut frame), 42);
}
#[test]
fn test_idiv() {
let mut frame = TestFrame::new()
.with_stack(vec![100i32.into(), 10i32.into()])
.build();
let result = frame.execute_instruction(Ops::idiv);
assert!(matches!(result, Ok(ExecutionResult::Continue)));
assert_eq!(pop_int(&mut frame), 10);
}
#[test]
fn test_ineg() {
let mut frame = TestFrame::new().with_stack(vec![42i32.into()]).build();
let result = frame.execute_instruction(Ops::ineg);
assert!(matches!(result, Ok(ExecutionResult::Continue)));
assert_eq!(pop_int(&mut frame), -42);
}
// ==================== Stack Manipulation Tests ====================
#[test]
fn test_dup() {
let mut frame = TestFrame::new().with_stack(vec![42i32.into()]).build();
let result = frame.execute_instruction(Ops::dup);
assert!(matches!(result, Ok(ExecutionResult::Continue)));
assert_eq!(pop_int(&mut frame), 42);
assert_eq!(pop_int(&mut frame), 42);
}
#[test]
fn test_swap() {
let mut frame = TestFrame::new()
.with_stack(vec![1i32.into(), 2i32.into()])
.build();
let result = frame.execute_instruction(Ops::swap);
assert!(matches!(result, Ok(ExecutionResult::Continue)));
assert_eq!(pop_int(&mut frame), 1);
assert_eq!(pop_int(&mut frame), 2);
}
#[test]
fn test_pop() {
let mut frame = TestFrame::new()
.with_stack(vec![1i32.into(), 2i32.into()])
.build();
let result = frame.execute_instruction(Ops::pop);
assert!(matches!(result, Ok(ExecutionResult::Continue)));
assert_eq!(pop_int(&mut frame), 1); // only 1 should remain
}
// ==================== Load/Store Tests ====================
#[test]
fn test_iload_0() {
let mut frame = TestFrame::new().with_locals(vec![99i32.into()], 4).build();
let result = frame.execute_instruction(Ops::iload_0);
assert!(matches!(result, Ok(ExecutionResult::Continue)));
assert_eq!(pop_int(&mut frame), 99);
}
#[test]
fn test_istore_0() {
let mut frame = TestFrame::new()
.with_stack(vec![42i32.into()])
.with_locals(vec![0i32.into()], 4)
.build();
let result = frame.execute_instruction(Ops::istore_0);
assert!(matches!(result, Ok(ExecutionResult::Continue)));
// Load it back to verify
let _ = frame.execute_instruction(Ops::iload_0);
assert_eq!(pop_int(&mut frame), 42);
}
// ==================== Comparison/Branch Tests ====================
#[test]
fn test_ifeq_taken() {
let mut frame = TestFrame::new().with_stack(vec![0i32.into()]).build();
let result = frame.execute_instruction(Ops::ifeq(10));
assert!(matches!(result, Ok(ExecutionResult::Advance(10))));
}
#[test]
fn test_ifeq_not_taken() {
let mut frame = TestFrame::new().with_stack(vec![5i32.into()]).build();
let result = frame.execute_instruction(Ops::ifeq(10));
assert!(matches!(result, Ok(ExecutionResult::Continue)));
}
#[test]
fn test_if_icmpeq_taken() {
let mut frame = TestFrame::new()
.with_stack(vec![5i32.into(), 5i32.into()])
.build();
let result = frame.execute_instruction(Ops::if_icmpeq(20));
assert!(matches!(result, Ok(ExecutionResult::Advance(20))));
}
#[test]
fn test_goto() {
let mut frame = TestFrame::new().build();
let result = frame.execute_instruction(Ops::goto(100));
assert!(matches!(result, Ok(ExecutionResult::Advance(100))));
}
// ==================== Conversion Tests ====================
#[test]
fn test_i2l() {
let mut frame = TestFrame::new().with_stack(vec![42i32.into()]).build();
let result = frame.execute_instruction(Ops::i2l);
assert!(matches!(result, Ok(ExecutionResult::Continue)));
assert_eq!(pop_long(&mut frame), 42i64);
}
// ==================== Return Tests ====================
#[test]
fn test_return_void() {
let mut frame = TestFrame::new().build();
let result = frame.execute_instruction(Ops::return_void);
assert!(matches!(result, Ok(ExecutionResult::Return(()))));
}
}

View File

@ -0,0 +1,115 @@
use crate::value::Value;
#[derive(Clone, Default)]
pub struct LocalVariables {
inner: Vec<Value>,
}
impl LocalVariables {
pub fn with_capacity(max_locals: usize) -> Self {
Self {
inner: vec![Value::NULL; max_locals],
}
}
pub fn from_args(args: Vec<Value>, max_locals: usize) -> Self {
let mut locals = Self::with_capacity(max_locals);
let mut idx = 0;
for arg in args {
locals.set(idx, arg.clone());
idx += if arg.is_wide() { 2 } else { 1 };
}
locals
}
pub fn set(&mut self, index: usize, value: Value) {
let idx = index;
let is_wide = value.is_wide();
self.inner[idx] = value;
if is_wide {
self.inner[idx + 1] = Value::Padding;
}
}
pub fn get(&self, index: usize) -> &Value {
let val = &self.inner[index];
if matches!(val, Value::Padding) {
panic!(
"Attempted to read padding slot at index {} (second half of wide value at {})",
index,
index - 1
);
}
val
}
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)> {
self.inner
.iter()
.enumerate()
.filter(|(_, v)| !matches!(v, Value::Padding))
.map(|(i, v)| (i as u16, v))
}
}
impl std::fmt::Debug for LocalVariables {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_list()
.entries(self.slots().map(|(i, v)| format!("[{}]: {:?}", i, v)))
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::value::Primitive;
#[test]
fn set_get_basic() {
let mut vars = LocalVariables::with_capacity(4);
vars.set(0, 42i32.into());
let val = vars.get(0);
assert!(matches!(val, Value::Primitive(Primitive::Int(42))));
}
#[test]
fn wide_value_sets_padding() {
let mut vars = LocalVariables::with_capacity(4);
vars.set(0, 100i64.into()); // long is wide
// Verify the long is at slot 0
assert!(matches!(vars.get(0), Value::Primitive(Primitive::Long(100))));
// Slot 1 should be padding - accessing it will panic (tested separately)
// But we can verify slot 2 is still accessible (as NULL)
assert!(matches!(vars.get(2), Value::Reference(None)));
}
#[test]
#[should_panic(expected = "padding slot")]
fn get_padding_slot_panics() {
let mut vars = LocalVariables::with_capacity(4);
vars.set(0, 100i64.into()); // Sets padding at index 1
let _ = vars.get(1); // Should panic
}
#[test]
fn from_args_spaces_wide_values() {
// Args: int, long, int -> should be at indices 0, 1, 3
let args = vec![
Value::from(1i32),
Value::from(2i64), // wide, takes slots 1-2
Value::from(3i32),
];
let vars = LocalVariables::from_args(args, 5);
let slots: Vec<_> = vars.slots().collect();
assert_eq!(slots[0].0, 0); // first int at 0
assert_eq!(slots[1].0, 1); // long at 1
assert_eq!(slots[2].0, 3); // second int at 3 (skipped 2 for padding)
}
}

View File

@ -0,0 +1,5 @@
pub mod frame;
mod local_vars;
mod operand_stack;
pub use frame::*;

View File

@ -0,0 +1,119 @@
use crate::error::VmError;
use crate::value::Value;
#[derive(Clone, Default)]
pub struct OperandStack(Vec<Value>);
impl OperandStack {
pub fn new() -> Self {
Self(Vec::new())
}
pub fn with_capacity(max_stack: usize) -> Self {
let backing = Vec::with_capacity(max_stack);
Self { 0: backing }
}
pub fn push(&mut self, value: Value) {
self.0.push(value);
}
pub fn pop(&mut self) -> Result<Value, VmError> {
self.0
.pop()
.ok_or_else(|| VmError::StackError("Stack underflow".to_string()))
}
pub fn peek(&self) -> Result<&Value, VmError> {
self.0
.last()
.ok_or_else(|| VmError::StackError("Stack underflow on peek".to_string()))
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
/// 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()
.checked_sub(n)
.ok_or_else(|| VmError::StackError("Stack underflow on pop_n".to_string()))?;
Ok(self.0.drain(start..).collect())
}
}
impl std::fmt::Debug for OperandStack {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_list().entries(self.0.iter()).finish()
}
}
impl OperandStack {
pub fn iter(&self) -> std::slice::Iter<'_, Value> {
self.into_iter()
}
}
impl<'a> IntoIterator for &'a OperandStack {
type Item = &'a Value;
type IntoIter = std::slice::Iter<'a, Value>;
fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::value::Primitive;
#[test]
fn push_pop_basic() {
let mut stack = OperandStack::new();
stack.push(42i32.into());
let val = stack.pop().unwrap();
assert!(matches!(val, Value::Primitive(Primitive::Int(42))));
}
#[test]
fn pop_empty_returns_error() {
let mut stack = OperandStack::new();
assert!(stack.pop().is_err());
}
#[test]
fn peek_does_not_remove() {
let mut stack = OperandStack::new();
stack.push(1i32.into());
let _ = stack.peek().unwrap();
assert_eq!(stack.len(), 1);
}
#[test]
fn pop_n_returns_push_order() {
let mut stack = OperandStack::new();
stack.push(1i32.into());
stack.push(2i32.into());
stack.push(3i32.into());
let values = stack.pop_n(2).unwrap();
// Should get [2, 3] not [3, 2]
assert!(matches!(values[0], Value::Primitive(Primitive::Int(2))));
assert!(matches!(values[1], Value::Primitive(Primitive::Int(3))));
assert_eq!(stack.len(), 1);
}
#[test]
fn pop_n_underflow_returns_error() {
let mut stack = OperandStack::new();
stack.push(1i32.into());
assert!(stack.pop_n(5).is_err());
}
}

View File

@ -1,4 +1,4 @@
use crate::attributes::{ArrayType, LookupSwitchData, TableSwitchData}; use crate::class_file::attributes::{ArrayType, LookupSwitchData, TableSwitchData};
use deku_derive::DekuRead; use deku_derive::DekuRead;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};

View File

@ -16,22 +16,15 @@
//! - [`FieldType`] - Field type information //! - [`FieldType`] - Field type information
use crate::class_file::MethodRef; use crate::class_file::MethodRef;
use crate::class_file::constant_pool::ConstantPoolExt;
use crate::class_file::constant_pool::ConstantPoolGet;
pub use crate::thread::VmThread; pub use crate::thread::VmThread;
use deku::DekuContainerRead;
use deku_derive::{DekuRead, DekuWrite}; use deku_derive::{DekuRead, DekuWrite};
use frame::Frame;
use itertools::Itertools; use itertools::Itertools;
use log::error;
use std::borrow::Cow; use std::borrow::Cow;
use std::cell::RefCell; use std::cell::RefCell;
use std::fmt::{Debug, Display, Formatter}; use std::fmt::{Debug, Display, Formatter};
use std::io::Read; use std::str::FromStr;
use std::ops::{BitAnd, Deref};
use value::Value; use value::Value;
mod attributes;
mod bimage; mod bimage;
mod class; mod class;
pub mod class_file; pub mod class_file;
@ -99,6 +92,23 @@ pub enum BaseType {
Boolean, Boolean,
} }
impl FromStr for BaseType {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"byte" => Ok(Self::Byte),
"char" => Ok(Self::Char),
"double" => Ok(Self::Double),
"float" => Ok(Self::Float),
"int" => Ok(Self::Int),
"long" => Ok(Self::Long),
"short" => Ok(Self::Short),
"boolean" => Ok(Self::Boolean),
s => Err(format!("Invalid primitive type name: {s}")),
}
}
}
impl From<char> for BaseType { impl From<char> for BaseType {
fn from(value: char) -> Self { fn from(value: char) -> Self {
match value { match value {
@ -136,9 +146,21 @@ impl MethodDescriptor {
return_type: None, return_type: None,
} }
} }
pub fn psvm() -> Self { pub fn method_type() -> Self {
MethodDescriptor::parse(
"(Ljava/lang/Class;[Ljava/lang/Class;)Ljava/lang/invoke/MethodType;",
)
.unwrap()
}
pub fn lookup() -> Self {
MethodDescriptor::parse("()Ljava/lang/invoke/MethodHandles$Lookup;").unwrap()
}
pub fn psvmsa() -> Self {
MethodDescriptor::parse("([Ljava/lang/String;)V").unwrap() MethodDescriptor::parse("([Ljava/lang/String;)V").unwrap()
} }
pub fn psvm() -> Self {
MethodDescriptor::parse("()V").unwrap()
}
pub fn param_string(&self) -> String { pub fn param_string(&self) -> String {
self.parameters.iter().join("") self.parameters.iter().join("")
@ -211,6 +233,12 @@ pub enum FieldType {
ArrayType(Box<FieldType>), ArrayType(Box<FieldType>),
} }
impl FieldType {
pub fn bool() -> Self {
Self::Base(BaseType::Boolean)
}
}
impl FieldType { impl FieldType {
pub fn default_value(&self) -> Value { pub fn default_value(&self) -> Value {
match self { match self {

View File

@ -1,6 +1,3 @@
use crate::class_file::constant_pool::ConstantPoolError;
use crate::value::Value;
#[macro_export] #[macro_export]
macro_rules! store { macro_rules! store {
($self:expr, l, $index:expr) => {{ ($self:expr, l, $index:expr) => {{

View File

@ -1,6 +1,6 @@
use colored::Colorize; use colored::Colorize;
use libloading::Library; use libloading::Library;
use log::{LevelFilter, error}; use log::{LevelFilter, error, info};
use roast_vm_core::error::VmError; use roast_vm_core::error::VmError;
use roast_vm_core::vm::{LIB_RESOLVE_COUNTS, Vm}; use roast_vm_core::vm::{LIB_RESOLVE_COUNTS, Vm};
use roast_vm_core::{get_last_native, stack_used}; use roast_vm_core::{get_last_native, stack_used};
@ -17,21 +17,21 @@ fn main() {
} }
fn run() { fn run() {
// env_logger::Builder::from_default_env() env_logger::Builder::from_default_env()
// .filter_level(LevelFilter::Trace) .filter_level(LevelFilter::Trace)
// .filter_module("deku", LevelFilter::Warn) .filter_module("deku", LevelFilter::Warn)
// .filter_module("roast_vm_core::class_file::class_file", LevelFilter::Info) .filter_module("roast_vm_core::class_file::class_file", LevelFilter::Info)
// .filter_module("roast_vm_core::attributes", LevelFilter::Info) .filter_module("roast_vm_core::class_file::attributes", LevelFilter::Info)
// .filter_module("roast_vm_core::instructions", LevelFilter::Info) .filter_module("roast_vm_core::instructions", LevelFilter::Info)
// .init(); .init();
stack_used(); stack_used();
let mut vm = Vm::new(); let vm = Vm::new();
fetch_libs(&vm); fetch_libs(&vm);
let start = Instant::now(); let start = Instant::now();
install_crash_handler(); install_crash_handler();
match vm.run("org/example/Main") { match vm.run("com/infernap12/Main") {
Ok(_) => { Ok(_) => {
println!("took {:?}", start.elapsed()); info!("took {:?}", start.elapsed());
} }
Err(VmError::Exception { Err(VmError::Exception {
message, message,
@ -124,12 +124,7 @@ fn fetch_libs(vm: &Vm) {
} }
fn load(path: PathBuf) -> Library { fn load(path: PathBuf) -> Library {
let leeb = unsafe { Library::new(&path) }.expect("load dll"); let leeb = unsafe { Library::new(&path) }.expect("load lib");
Library::from(leeb)
}
fn linux_load(path: PathBuf) -> Library {
let leeb = unsafe { Library::new(&path) }.expect("load dll");
Library::from(leeb) Library::from(leeb)
} }
@ -153,11 +148,13 @@ fn format_bytes(bytes: usize) -> String {
use windows::Win32::Foundation::EXCEPTION_ACCESS_VIOLATION; use windows::Win32::Foundation::EXCEPTION_ACCESS_VIOLATION;
use windows::Win32::System::Diagnostics::Debug::*; use windows::Win32::System::Diagnostics::Debug::*;
unsafe extern "system" fn crash_handler(info: *mut EXCEPTION_POINTERS) -> i32 { unsafe extern "system" fn crash_handler(info: *mut EXCEPTION_POINTERS) -> i32 {
let record = (*info).ExceptionRecord; unsafe {
if (*record).ExceptionCode == EXCEPTION_ACCESS_VIOLATION { let record = (*info).ExceptionRecord;
eprintln!("ACCESS VIOLATION during native call: {}", get_last_native()); if (*record).ExceptionCode == EXCEPTION_ACCESS_VIOLATION {
eprintln!("ACCESS VIOLATION during native call: {}", get_last_native());
}
EXCEPTION_CONTINUE_SEARCH
} }
EXCEPTION_CONTINUE_SEARCH
} }
// Call once at VM startup: // Call once at VM startup:

View File

@ -1,5 +1,7 @@
#![allow(unused_variables)] #![allow(unused_variables)]
#![allow(unused)]
#![feature(c_variadic)] #![feature(c_variadic)]
#![allow(unsafe_op_in_unsafe_fn)]
use crate::class::RuntimeClass; use crate::class::RuntimeClass;
use crate::class_file::{FieldData, FieldRef}; use crate::class_file::{FieldData, FieldRef};
use crate::objects::array::ArrayReference; use crate::objects::array::ArrayReference;
@ -8,11 +10,10 @@ use crate::prim::jboolean;
use crate::thread::VmThread; use crate::thread::VmThread;
use crate::value::{Primitive, Value}; use crate::value::{Primitive, Value};
use crate::{BaseType, FieldType, MethodDescriptor, generate_jni_short_name}; use crate::{BaseType, FieldType, MethodDescriptor, generate_jni_short_name};
use itertools::Itertools;
use jni::sys::*; use jni::sys::*;
use jni::sys::{JNIEnv, jstring}; use jni::sys::{JNIEnv, jstring};
use jni::sys::{JNINativeInterface_, jclass, jint, jobject}; use jni::sys::{JNINativeInterface_, jclass, jint, jobject};
use log::{error, info, trace, warn}; use log::{debug, info, trace, warn};
use std::ffi::{CStr, CString, c_char}; use std::ffi::{CStr, CString, c_char};
use std::ptr; use std::ptr;
use std::sync::Arc; use std::sync::Arc;
@ -318,7 +319,7 @@ unsafe extern "system" fn get_string_utfchars(
_ => return ptr::null(), _ => return ptr::null(),
}; };
let mut obj = obj_ref.lock(); let obj = obj_ref.lock();
let field_ref = FieldRef { let field_ref = FieldRef {
class: "java/lang/String".to_string(), class: "java/lang/String".to_string(),
name: "value".to_string(), name: "value".to_string(),
@ -362,14 +363,19 @@ unsafe extern "system" fn get_object_class(env: *mut JNIEnv, obj: jobject) -> jc
let obj_id = obj as u32; let obj_id = obj as u32;
let ReferenceKind::ObjectReference(obj_ref) = gc.get(obj_id) else { match gc.get(obj_id) {
return ptr::null_mut(); ReferenceKind::ObjectReference(obj_ref) => {
}; let obj_lock = obj_ref.lock();
let class: &Arc<RuntimeClass> = &obj_lock.class;
*class.mirror.wait() as jclass
}
ReferenceKind::ArrayReference(arr_ref) => {
// Return the Class mirror for the array type
let obj_lock = obj_ref.lock(); // however you get the array's class mirror
let class: &Arc<RuntimeClass> = &obj_lock.class; *arr_ref.class().mirror.wait() as jclass
}
*class.mirror.wait() as jclass }
} }
unsafe extern "system" fn register_natives( unsafe extern "system" fn register_natives(
@ -402,7 +408,7 @@ unsafe extern "system" fn register_natives(
} }
if full_name.contains("Java_java_lang_Class_desiredAssertionStatus0") { if full_name.contains("Java_java_lang_Class_desiredAssertionStatus0") {
warn!("MAJOR HACK, figure out what is wrong with desiredAssertionStatus0"); // warn!("MAJOR HACK, figure out what is wrong with desiredAssertionStatus0");
continue; continue;
} }
@ -430,7 +436,7 @@ unsafe extern "system" fn register_natives(
"name:{name}, signature:{signature}, fn_ptr{}", "name:{name}, signature:{signature}, fn_ptr{}",
fn_ptr.is_null() fn_ptr.is_null()
); );
println!("JNI register: {full_name}"); debug!("JNI register: {full_name}");
} }
JNI_OK JNI_OK
} }
@ -1067,7 +1073,32 @@ unsafe extern "system" fn get_object_field(
obj: jobject, obj: jobject,
field_id: jfieldID, field_id: jfieldID,
) -> jobject { ) -> jobject {
todo!("get_object_field") let thread = &*get_thread(env);
let field_key = thread
.vm
.safent
.read()
.resolve_field(field_id as jlong)
.expect("Invalid field ID");
let object_ref = thread
.gc
.read()
.get(obj as u32)
.try_into_object_reference()
.unwrap();
let guard = object_ref.lock();
let value = guard.fields.get(&field_key.field_name);
match value {
Some(ref entry) => match entry.value() {
Value::Reference(Some(ref_kind)) => ref_kind.id() as jobject,
_ => std::ptr::null_mut(),
},
None => std::ptr::null_mut(),
}
} }
unsafe extern "system" fn get_boolean_field( unsafe extern "system" fn get_boolean_field(
@ -1085,8 +1116,8 @@ unsafe extern "system" fn get_boolean_field(
desc: FieldType::Base(BaseType::Boolean), desc: FieldType::Base(BaseType::Boolean),
}; };
let val = object.lock().get_field(&field_ref); let val = object.lock().get_field(&field_ref);
if let Value::Primitive(Primitive::Boolean(bool)) = val { if let Value::Primitive(Primitive::Boolean(bol)) = val {
if bool { JNI_TRUE } else { JNI_FALSE } if bol == 1u8 { JNI_TRUE } else { JNI_FALSE }
} else { } else {
JNI_FALSE JNI_FALSE
} }
@ -1462,8 +1493,8 @@ unsafe extern "system" fn get_static_boolean_field(
let field_ref = &*(field_id as *const FieldData); let field_ref = &*(field_id as *const FieldData);
let field_again = class.find_field(&field_ref.name, &field_ref.desc).unwrap(); let field_again = class.find_field(&field_ref.name, &field_ref.desc).unwrap();
let val = field_again.value.lock().clone(); let val = field_again.value.lock().clone();
if let Some(Value::Primitive(Primitive::Boolean(bool))) = val { if let Some(Value::Primitive(Primitive::Boolean(bol))) = val {
if bool { JNI_TRUE } else { JNI_FALSE } if bol == JNI_TRUE { JNI_TRUE } else { JNI_FALSE }
} else { } else {
JNI_FALSE JNI_FALSE
} }
@ -1717,7 +1748,15 @@ unsafe extern "system" fn release_string_utf_chars(
} }
unsafe extern "system" fn get_array_length(env: *mut JNIEnv, array: jarray) -> jsize { unsafe extern "system" fn get_array_length(env: *mut JNIEnv, array: jarray) -> jsize {
todo!("get_array_length") let thread = &*get_thread(env);
let arr = thread
.gc
.read()
.get(array as u32)
.try_into_array_reference()
.unwrap();
arr.len() as jsize
} }
unsafe extern "system" fn new_object_array( unsafe extern "system" fn new_object_array(
@ -1963,7 +2002,19 @@ unsafe extern "system" fn get_byte_array_region(
len: jsize, len: jsize,
buf: *mut jbyte, buf: *mut jbyte,
) { ) {
todo!("get_byte_array_region") let thread = &*get_thread(env);
let arr = thread
.gc
.read()
.get(array as u32)
.try_into_array_reference()
.unwrap();
if let ArrayReference::Byte(byte_arr) = arr {
let guard = byte_arr.lock();
let slice = &guard.backing[start as usize..(start + len) as usize];
std::ptr::copy_nonoverlapping(slice.as_ptr(), buf, len as usize);
}
} }
unsafe extern "system" fn get_char_array_region( unsafe extern "system" fn get_char_array_region(
@ -2221,7 +2272,7 @@ unsafe extern "system" fn delete_weak_global_ref(env: *mut JNIEnv, ref_: jweak)
} }
unsafe extern "system" fn exception_check(env: *mut JNIEnv) -> jboolean { unsafe extern "system" fn exception_check(env: *mut JNIEnv) -> jboolean {
warn!("exception_check"); trace!("exception_check");
JNI_FALSE JNI_FALSE
} }

View File

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

View File

@ -1,14 +0,0 @@
use crate::class_file::ConstantPoolEntry;
use libloading::Library;
use std::collections::HashMap;
// impl NativeExt for NativeLibraries {}
//
// trait NativeExt: AsRef<[..]> {
// fn find(&self, name: String) -> () {
// for lib in self.iter() {
// lib
// }
// }
// }

View File

@ -4,7 +4,6 @@ use crate::error::VmError;
use crate::objects::array::{ArrayReference, ObjectArrayReference, PrimitiveArrayReference}; use crate::objects::array::{ArrayReference, ObjectArrayReference, PrimitiveArrayReference};
use crate::prim::{jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jshort}; use crate::prim::{jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jshort};
use crate::value::Value; use crate::value::Value;
use crate::{BaseType, FieldType};
use dashmap::DashMap; use dashmap::DashMap;
use log::trace; use log::trace;
use parking_lot::Mutex; use parking_lot::Mutex;
@ -120,8 +119,8 @@ pub type Reference = Option<ReferenceKind>;
impl Display for ReferenceKind { impl Display for ReferenceKind {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let id = match self { let id = match self {
ReferenceKind::ObjectReference(x) => { ReferenceKind::ObjectReference(x) => unsafe {
let guard = x.lock(); let guard = x.make_guard_unchecked();
if guard.class.this_class == "java/lang/String" if guard.class.this_class == "java/lang/String"
&& let Some(field) = guard.fields.get("value") && let Some(field) = guard.fields.get("value")
&& let Value::Reference(Some(ReferenceKind::ArrayReference( && let Value::Reference(Some(ReferenceKind::ArrayReference(
@ -134,7 +133,7 @@ impl Display for ReferenceKind {
} else { } else {
format!("Obj<{}>", guard.class.this_class) format!("Obj<{}>", guard.class.this_class)
} }
} },
ReferenceKind::ArrayReference(ArrayReference::Int(x)) => { ReferenceKind::ArrayReference(ArrayReference::Int(x)) => {
let guard = x.lock(); let guard = x.lock();
let backing = &guard.backing; let backing = &guard.backing;

View File

@ -1,18 +1,15 @@
use crate::attributes::ArrayType;
use crate::class::RuntimeClass; use crate::class::RuntimeClass;
use crate::class_file::{ClassFlags, MethodData}; use crate::class_file::ClassFlags;
use crate::class_file::attributes::ArrayType;
use crate::error::VmError; use crate::error::VmError;
use crate::objects::array::{ use crate::objects::array::{Array, ArrayReference};
Array, ArrayReference, ArrayValue, ObjectArrayReference, PrimitiveArrayReference,
};
use crate::objects::object::{ use crate::objects::object::{
Object, ObjectReference, Reference, ReferenceKind, string_from_bytes, Object, ObjectReference, Reference, ReferenceKind, string_from_bytes,
}; };
use crate::value::{Primitive, Value}; use crate::value::Value;
use crate::{ThreadId, objects, rng}; use crate::{BaseType, ThreadId};
use dashmap::DashMap; use dashmap::DashMap;
use jni::sys::{jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jshort}; use jni::sys::{jint, jlong};
use log::warn;
use parking_lot::{Condvar, Mutex}; use parking_lot::{Condvar, Mutex};
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
@ -78,41 +75,45 @@ impl ObjectManager {
object object
} }
pub fn new_primitive_array( pub fn new_primitive_array(&mut self, class: Arc<RuntimeClass>, count: i32) -> ArrayReference {
&mut self,
class: Arc<RuntimeClass>,
array_type: ArrayType,
count: i32,
) -> ArrayReference {
let id = self.next_id(); let id = self.next_id();
assert!( assert!(
!self.objects.contains_key(&id), !self.objects.contains_key(&id),
"Generated ID already exists!" "Generated ID already exists!"
); );
let symbol = class
.this_class
.strip_prefix("[")
.unwrap()
.chars()
.next()
.unwrap();
let array_type = symbol.into();
let array_ref = match array_type { let array_ref = match array_type {
ArrayType::T_INT => { BaseType::Int => {
ArrayReference::Int(Arc::new(Mutex::new(Array::new(id, class, count)))) ArrayReference::Int(Arc::new(Mutex::new(Array::new(id, class, count))))
} }
ArrayType::T_BYTE => { BaseType::Byte => {
ArrayReference::Byte(Arc::new(Mutex::new(Array::new(id, class, count)))) ArrayReference::Byte(Arc::new(Mutex::new(Array::new(id, class, count))))
} }
ArrayType::T_SHORT => { BaseType::Short => {
ArrayReference::Short(Arc::new(Mutex::new(Array::new(id, class, count)))) ArrayReference::Short(Arc::new(Mutex::new(Array::new(id, class, count))))
} }
ArrayType::T_LONG => { BaseType::Long => {
ArrayReference::Long(Arc::new(Mutex::new(Array::new(id, class, count)))) ArrayReference::Long(Arc::new(Mutex::new(Array::new(id, class, count))))
} }
ArrayType::T_FLOAT => { BaseType::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 => { BaseType::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 => { BaseType::Char => {
ArrayReference::Char(Arc::new(Mutex::new(Array::new(id, class, count)))) ArrayReference::Char(Arc::new(Mutex::new(Array::new(id, class, count))))
} }
ArrayType::T_BOOLEAN => { BaseType::Boolean => {
ArrayReference::Boolean(Arc::new(Mutex::new(Array::new(id, class, count)))) ArrayReference::Boolean(Arc::new(Mutex::new(Array::new(id, class, count))))
} }
}; };
@ -158,7 +159,6 @@ impl ObjectManager {
class: Arc<RuntimeClass>, class: Arc<RuntimeClass>,
vector: Box<[i8]>, vector: Box<[i8]>,
) -> ArrayReference { ) -> ArrayReference {
warn!("Manual sidechannel byte array creation");
let id = self.next_id(); let id = self.next_id();
assert!( assert!(
!self.objects.contains_key(&id), !self.objects.contains_key(&id),
@ -198,7 +198,6 @@ impl ObjectManager {
string_class: Arc<RuntimeClass>, string_class: Arc<RuntimeClass>,
utf8: &str, utf8: &str,
) -> ObjectReference { ) -> ObjectReference {
warn!("Manual sidechannel string creation: \n\"{}\"", utf8);
let key = utf8.to_owned(); let key = utf8.to_owned();
let jstr = self.new_object(string_class); let jstr = self.new_object(string_class);
let byte_vec = utf8 let byte_vec = utf8
@ -227,6 +226,9 @@ impl ObjectManager {
self.strings.insert(key, id); self.strings.insert(key, id);
jstr jstr
} }
pub fn intern_existing_string(&mut self, utf: String, obj_id: u32) -> u32 {
*self.strings.entry(utf).or_insert(obj_id)
}
pub fn new_class( pub fn new_class(
&mut self, &mut self,
@ -235,8 +237,14 @@ impl ObjectManager {
module: Reference, module: Reference,
modifiers: ClassFlags, modifiers: ClassFlags,
primitive: bool, primitive: bool,
component_type: Reference,
) -> ObjectReference { ) -> ObjectReference {
warn!("Manual sidechannel class creation"); let string = self
.transmute_string(name.clone().unwrap().try_into_object_reference().unwrap())
.unwrap();
if string.contains("byte") {
println!("!!!!!");
}
let module = None; let module = None;
let modifiers = 17u16; let modifiers = 17u16;
@ -249,6 +257,7 @@ impl ObjectManager {
clakz.set_field("modifiers", Value::from(modifiers)); clakz.set_field("modifiers", Value::from(modifiers));
clakz.set_field("primitive", Value::from(primitive)); clakz.set_field("primitive", Value::from(primitive));
clakz.set_field("classRedefinedCount", Value::from(class_redefined_count)); clakz.set_field("classRedefinedCount", Value::from(class_redefined_count));
clakz.set_field("componentType", Value::from(component_type))
} }
clazz clazz
} }
@ -639,4 +648,56 @@ impl ObjectManager {
} }
tg_object tg_object
} }
pub fn new_method_type(
&mut self,
method_type_class: Arc<RuntimeClass>,
class_array_class: Arc<RuntimeClass>,
rtype: ReferenceKind, // Class mirror for return type
ptypes: Vec<ReferenceKind>, // Class mirrors for param types
) -> ObjectReference {
let ptypes_array =
self.new_object_array_from(class_array_class, ptypes.into_iter().map(Some).collect());
let mt = self.new_object(method_type_class);
{
let obj = mt.lock();
obj.set_field("rtype", Value::from(Some(rtype)));
obj.set_field("ptypes", Value::from(ptypes_array));
}
mt
}
pub fn new_member_name(
&mut self,
member_name_class: Arc<RuntimeClass>,
clazz: ReferenceKind, // declaring class mirror
name: ObjectReference, // method/field name as String
method_type: ObjectReference,
flags: jint, // REF_invokeStatic, etc encoded
) -> ObjectReference {
let mn = self.new_object(member_name_class);
{
let obj = mn.lock();
obj.set_field("clazz", Value::from(Some(clazz)));
obj.set_field("name", Value::from(name));
obj.set_field("type", Value::from(method_type));
obj.set_field("flags", Value::from(flags));
}
mn
}
pub fn new_direct_method_handle(
&mut self,
dmh_class: Arc<RuntimeClass>,
member: ObjectReference,
method_type: ObjectReference,
) -> ObjectReference {
let mh = self.new_object(dmh_class);
{
let obj = mh.lock();
obj.set_field("type", Value::from(method_type));
obj.set_field("member", Value::from(member));
}
mh
}
} }

View File

@ -1,3 +1,4 @@
#[allow(unused)]
use std::cell::Cell; use std::cell::Cell;
use std::sync::atomic::{AtomicU32, Ordering}; use std::sync::atomic::{AtomicU32, Ordering};

View File

@ -1,5 +1,5 @@
use crate::class::{InitState, RuntimeClass}; use crate::class::{ClassRef, InitState, RuntimeClass};
use crate::class_file::{ClassFile, MethodData, MethodRef}; use crate::class_file::{MethodData, MethodRef};
use crate::class_loader::{ClassLoader, LoaderRef}; use crate::class_loader::{ClassLoader, LoaderRef};
use crate::error::VmError; use crate::error::VmError;
use crate::frame::Frame; use crate::frame::Frame;
@ -10,26 +10,19 @@ use crate::value::{Primitive, Value};
use crate::vm::Vm; use crate::vm::Vm;
use crate::{ use crate::{
BaseType, FieldType, MethodDescriptor, ThreadId, generate_jni_method_name, set_last_native, BaseType, FieldType, MethodDescriptor, ThreadId, generate_jni_method_name, set_last_native,
stack_used,
}; };
use deku::DekuError::Incomplete;
use itertools::Itertools;
use jni::sys::{JNIEnv, jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jobject, jshort}; use jni::sys::{JNIEnv, jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jobject, jshort};
use libffi::low::call;
use libffi::middle::*; use libffi::middle::*;
use log::{LevelFilter, info, trace, warn}; use log::{LevelFilter, debug, trace, warn};
use parking_lot::{Condvar, Mutex, Once, ReentrantMutex, RwLock}; use parking_lot::{Condvar, Mutex, Once, RwLock};
use std::any::Any; use std::any::Any;
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::ops::{Add, Deref}; use std::sync::atomic::Ordering;
use std::ptr::null_mut;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::{Arc, OnceLock}; use std::sync::{Arc, OnceLock};
use std::thread; use std::thread;
use std::vec::IntoIter;
static INIT_LOGGER: Once = Once::new(); static INIT_LOGGER: Once = Once::new();
type MethodCallResult = Result<Option<Value>, VmError>; type MethodCallResult = Result<Option<Value>, VmError>;
@ -55,7 +48,7 @@ pub struct VmThread {
impl VmThread { impl VmThread {
pub fn new(vm: Arc<Vm>, loader: Option<LoaderRef>) -> Arc<Self> { pub fn new(vm: Arc<Vm>, loader: Option<LoaderRef>) -> Arc<Self> {
let id = ThreadId(vm.NEXT_ID.fetch_add(1, Ordering::SeqCst)); let id = ThreadId(vm.next_id.fetch_add(1, Ordering::SeqCst));
let loader = loader.unwrap_or(vm.loader.clone()); let loader = loader.unwrap_or(vm.loader.clone());
let gc = vm.gc.clone(); let gc = vm.gc.clone();
@ -141,7 +134,7 @@ impl VmThread {
); );
return Ok(()); return Ok(());
} }
InitState::Initializing(tid) => { InitState::Initializing(_tid) => {
// Different thread is initializing - in a real JVM we'd wait // Different thread is initializing - in a real JVM we'd wait
// For now, just return an error // For now, just return an error
return Err(VmError::LoaderError(format!( return Err(VmError::LoaderError(format!(
@ -203,9 +196,6 @@ impl VmThread {
pub fn ensure_initialised(&self, class: &Arc<RuntimeClass>) -> Result<(), VmError> { pub fn ensure_initialised(&self, class: &Arc<RuntimeClass>) -> Result<(), VmError> {
let current_thread = thread::current().id(); let current_thread = thread::current().id();
if class.this_class.contains("Thread") {
println!("Yep");
}
{ {
let mut state = class.init_state.lock(); let mut state = class.init_state.lock();
@ -256,7 +246,7 @@ impl VmThread {
if let Some(method) = class.methods.iter().find(|m| m.name == "<clinit>") { if let Some(method) = class.methods.iter().find(|m| m.name == "<clinit>") {
if class.this_class.contains("Thread") { if class.this_class.contains("Thread") {
println!("executing init for: {}", class.this_class); trace!("executing init for: {}", class.this_class);
} }
self.execute_method(class, method, vec![])?; self.execute_method(class, method, vec![])?;
} }
@ -285,12 +275,19 @@ impl VmThread {
self.get_class("java/lang/Class")? self.get_class("java/lang/Class")?
}; };
let string = self.intern_string(&class.this_class); let string = self.intern_string(&class.this_class);
let component_type = if (class.this_class.starts_with("[")) {
let component = self.get_class(class.this_class.strip_prefix("[").unwrap())?;
Some(self.gc.read().get(component.mirror()))
} else {
None
};
let class_obj = self.gc.write().new_class( let class_obj = self.gc.write().new_class(
class_class, class_class,
Some(ReferenceKind::ObjectReference(string)), Some(ReferenceKind::ObjectReference(string)),
None, None,
class.access_flags, class.access_flags,
false, false,
component_type,
); );
let id = class_obj.lock().id; let id = class_obj.lock().id;
class.mirror.set(id).expect("woops, id already set"); class.mirror.set(id).expect("woops, id already set");
@ -309,7 +306,7 @@ impl VmThread {
} }
pub fn invoke(&self, method_reference: MethodRef, args: Vec<Value>) -> MethodCallResult { pub fn invoke(&self, method_reference: MethodRef, args: Vec<Value>) -> MethodCallResult {
if self.gc.read().objects.len() > 2387 { if self.gc.read().objects.len() > 2350 {
INIT_LOGGER.call_once(|| { INIT_LOGGER.call_once(|| {
env_logger::Builder::from_default_env() env_logger::Builder::from_default_env()
.filter_level(LevelFilter::Trace) .filter_level(LevelFilter::Trace)
@ -340,19 +337,13 @@ impl VmThread {
self.execute_method(&class, &method, args) self.execute_method(&class, &method, args)
} }
pub fn invoke_native(&self, method: &MethodRef, mut args: Vec<Value>) -> MethodCallResult { pub fn invoke_native(&self, method: &MethodRef, args: Vec<Value>) -> MethodCallResult {
let symbol_name = generate_jni_method_name(method, false); let symbol_name = generate_jni_method_name(method, false);
if symbol_name.contains("Java_java_lang_Thread_start0") { if symbol_name.contains("Java_java_lang_reflect_Array_newArray") {
// return Err(VmError::Debug( return Err(VmError::Debug(
// "RoastVM specific implementation required for Java_java_lang_Thread_start0", "RoastVM specific implementation required for Java_java_lang_reflect_Array_newArray",
// )); ));
println!()
}
if symbol_name.contains("Java_java_lang_Class_desiredAssertionStatus0") {
warn!("MAJOR HACK, figure out what is wrong with desiredAssertionStatus0");
return Ok(Some(Value::from(false)));
} }
let result = unsafe { let result = unsafe {
@ -368,7 +359,7 @@ impl VmThread {
)))?; )))?;
// build pointer to native fn // build pointer to native fn
let cp = CodePtr::from_ptr(p); let cp = CodePtr::from_ptr(p);
println!("invoke native fn: {}", symbol_name);
let mut storage = Vec::new(); let mut storage = Vec::new();
trace!("passing {} to native fn", Value::format_vec(&args)); trace!("passing {} to native fn", Value::format_vec(&args));
let deq_args = VecDeque::from(args); let deq_args = VecDeque::from(args);
@ -433,7 +424,6 @@ impl VmThread {
} }
} }
}; };
warn!("Invoke native not final");
result result
} }
fn execute_method( fn execute_method(
@ -442,8 +432,7 @@ impl VmThread {
method: &MethodData, method: &MethodData,
args: Vec<Value>, args: Vec<Value>,
) -> MethodCallResult { ) -> MethodCallResult {
info!("Executing {}", method.name.clone()); debug!("Executing {}", method.name.clone());
warn!("Stack used: {}", stack_used());
let method_ref = MethodRef { let method_ref = MethodRef {
class: class.this_class.clone(), class: class.this_class.clone(),
name: method.name.clone(), name: method.name.clone(),
@ -501,6 +490,20 @@ impl VmThread {
// } // }
// } // }
// } // }
pub fn new_object(
&self,
class: ClassRef,
desc: MethodDescriptor,
args: &[Value],
) -> Result<ObjectReference, VmError> {
let obj = self.gc.write().new_object(class.clone());
let method_ref = class.find_method("<init>", &desc)?;
let mut args = args.to_vec();
args.insert(0, obj.clone().into());
let _ = self.invoke(method_ref.into(), args)?;
Ok(obj)
}
} }
fn build_args<'a>( fn build_args<'a>(

View File

@ -2,13 +2,10 @@ use crate::error::VmError;
use crate::objects::array::ArrayReference; use crate::objects::array::ArrayReference;
use crate::objects::object::{ObjectReference, ReferenceKind}; use crate::objects::object::{ObjectReference, ReferenceKind};
use crate::{BaseType, FieldType}; use crate::{BaseType, FieldType};
use core::fmt; use jni::sys::{JNI_FALSE, JNI_TRUE, jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jshort};
use dashmap::DashMap;
use jni::sys::{jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jshort};
use log::trace; use log::trace;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
use std::mem::{size_of, size_of_val}; use std::mem::{size_of, size_of_val};
use std::ops::Deref;
/// A reference-counted, thread-safe pointer to an Object. /// A reference-counted, thread-safe pointer to an Object.
@ -25,135 +22,6 @@ pub enum Value {
Padding, Padding,
} }
#[derive(Clone, Default)]
pub struct OperandStack(Vec<Value>);
impl OperandStack {
pub fn new() -> Self {
Self(Vec::new())
}
pub fn with_capacity(max_stack: usize) -> Self {
let backing = Vec::with_capacity(max_stack);
Self { 0: backing }
}
pub fn push(&mut self, value: Value) {
self.0.push(value);
}
pub fn pop(&mut self) -> Result<Value, VmError> {
self.0
.pop()
.ok_or_else(|| VmError::StackError("Stack underflow".to_string()))
}
pub fn peek(&self) -> Result<&Value, VmError> {
self.0
.last()
.ok_or_else(|| VmError::StackError("Stack underflow on peek".to_string()))
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
/// 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()
.checked_sub(n)
.ok_or_else(|| VmError::StackError("Stack underflow on pop_n".to_string()))?;
Ok(self.0.drain(start..).collect())
}
}
impl std::fmt::Debug for OperandStack {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_list().entries(self.0.iter()).finish()
}
}
impl OperandStack {
pub fn iter(&self) -> std::slice::Iter<'_, Value> {
self.into_iter()
}
}
impl<'a> IntoIterator for &'a OperandStack {
type Item = &'a Value;
type IntoIter = std::slice::Iter<'a, Value>;
fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}
#[derive(Clone, Default)]
pub struct LocalVariables {
inner: Vec<Value>,
}
impl LocalVariables {
pub fn with_capacity(max_locals: usize) -> Self {
Self {
inner: vec![Value::NULL; max_locals],
}
}
pub fn from_args(args: Vec<Value>, max_locals: usize) -> Self {
let mut locals = Self::with_capacity(max_locals);
let mut idx = 0;
for arg in args {
locals.set(idx, arg.clone());
idx += if arg.is_wide() { 2 } else { 1 };
}
locals
}
pub fn set(&mut self, index: usize, value: Value) {
let idx = index;
let is_wide = value.is_wide();
self.inner[idx] = value;
if is_wide {
self.inner[idx + 1] = Value::Padding;
}
}
pub fn get(&self, index: usize) -> &Value {
let val = &self.inner[index];
if matches!(val, Value::Padding) {
panic!(
"Attempted to read padding slot at index {} (second half of wide value at {})",
index,
index - 1
);
}
val
}
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)> {
self.inner
.iter()
.enumerate()
.filter(|(_, v)| !matches!(v, Value::Padding))
.map(|(i, v)| (i as u16, v))
}
}
impl std::fmt::Debug for LocalVariables {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_list()
.entries(self.slots().map(|(i, v)| format!("[{}]: {:?}", i, v)))
.finish()
}
}
impl Value { impl Value {
pub const NULL: Value = Value::Reference(None); pub const NULL: Value = Value::Reference(None);
@ -232,6 +100,16 @@ impl Value {
))), ))),
} }
} }
pub fn try_into_jboolean(self) -> Result<jboolean, VmError> {
match self {
Value::Primitive(Primitive::Boolean(z)) => Ok(z),
_ => Err(VmError::InvariantError(format!(
"Expected bool, found {}",
self.to_string()
))),
}
}
} }
macro_rules! impl_value_from { macro_rules! impl_value_from {
@ -251,13 +129,13 @@ impl_value_from!(jbyte, Byte);
impl_value_from!(jshort, Short); impl_value_from!(jshort, Short);
impl_value_from!(jchar, Char); impl_value_from!(jchar, Char);
impl From<jboolean> for Value { impl From<bool> for Value {
fn from(value: jboolean) -> Self { fn from(value: bool) -> Self {
Self::Primitive(Primitive::Boolean(value != 0)) Self::Primitive(Primitive::Boolean(if value { JNI_TRUE } else { JNI_FALSE }))
} }
} }
impl_value_from!(bool, Boolean); impl_value_from!(jboolean, Boolean);
impl From<Option<ReferenceKind>> for Value { impl From<Option<ReferenceKind>> for Value {
fn from(value: Option<ReferenceKind>) -> Self { fn from(value: Option<ReferenceKind>) -> Self {
@ -284,7 +162,7 @@ impl From<ArrayReference> for Value {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum Primitive { pub enum Primitive {
/// Boolean value (true/false) /// Boolean value (true/false)
Boolean(bool), Boolean(jboolean),
/// Unicode character /// Unicode character
Char(u16), Char(u16),
/// 32-bit floating point /// 32-bit floating point
@ -391,31 +269,31 @@ impl PartialEq<FieldType> for Value {
} }
} }
ReferenceKind::ArrayReference(x) => match x { ReferenceKind::ArrayReference(x) => match x {
ArrayReference::Int(x) => { ArrayReference::Int(_x) => {
matches!(other, FieldType::ArrayType(inner) if matches!(**inner, FieldType::Base(BaseType::Int))) matches!(other, FieldType::ArrayType(inner) if matches!(**inner, FieldType::Base(BaseType::Int)))
} }
ArrayReference::Byte(x) => { ArrayReference::Byte(_x) => {
matches!(other, FieldType::ArrayType(inner) if matches!(**inner, FieldType::Base(BaseType::Byte))) matches!(other, FieldType::ArrayType(inner) if matches!(**inner, FieldType::Base(BaseType::Byte)))
} }
ArrayReference::Short(x) => { ArrayReference::Short(_x) => {
matches!(other, FieldType::ArrayType(inner) if matches!(**inner, FieldType::Base(BaseType::Short))) matches!(other, FieldType::ArrayType(inner) if matches!(**inner, FieldType::Base(BaseType::Short)))
} }
ArrayReference::Long(x) => { ArrayReference::Long(_x) => {
matches!(other, FieldType::ArrayType(inner) if matches!(**inner, FieldType::Base(BaseType::Long))) matches!(other, FieldType::ArrayType(inner) if matches!(**inner, FieldType::Base(BaseType::Long)))
} }
ArrayReference::Float(x) => { ArrayReference::Float(_x) => {
matches!(other, FieldType::ArrayType(inner) if matches!(**inner, FieldType::Base(BaseType::Float))) matches!(other, FieldType::ArrayType(inner) if matches!(**inner, FieldType::Base(BaseType::Float)))
} }
ArrayReference::Double(x) => { ArrayReference::Double(_x) => {
matches!(other, FieldType::ArrayType(inner) if matches!(**inner, FieldType::Base(BaseType::Double))) matches!(other, FieldType::ArrayType(inner) if matches!(**inner, FieldType::Base(BaseType::Double)))
} }
ArrayReference::Char(x) => { ArrayReference::Char(_x) => {
matches!(other, FieldType::ArrayType(inner) if matches!(**inner, FieldType::Base(BaseType::Char))) matches!(other, FieldType::ArrayType(inner) if matches!(**inner, FieldType::Base(BaseType::Char)))
} }
ArrayReference::Boolean(x) => { ArrayReference::Boolean(_x) => {
matches!(other, FieldType::ArrayType(inner) if matches!(**inner, FieldType::Base(BaseType::Boolean))) matches!(other, FieldType::ArrayType(inner) if matches!(**inner, FieldType::Base(BaseType::Boolean)))
} }
ArrayReference::Object(x) => { ArrayReference::Object(_x) => {
if let FieldType::ArrayType(inner) = other { if let FieldType::ArrayType(inner) = other {
if let FieldType::ClassType(_) = **inner { if let FieldType::ClassType(_) = **inner {
true true

View File

@ -1,11 +1,8 @@
#[cfg(libloading_docs)]
use super::os::unix as imp;
use std::collections::HashMap;
use std::ffi::c_void; use std::ffi::c_void;
// the implementation used here doesn't matter particularly much... // the implementation used here doesn't matter particularly much...
#[cfg(all(not(libloading_docs), unix))] #[cfg(all(unix))]
use libloading::os::unix as imp; use libloading::os::unix as imp;
#[cfg(all(not(libloading_docs), windows))] #[cfg(all(windows))]
use libloading::os::windows as imp; use libloading::os::windows as imp;
use crate::class_file::{ClassFlags, MethodRef}; use crate::class_file::{ClassFlags, MethodRef};
@ -15,13 +12,15 @@ use crate::native::r#unsafe::UnsafeSupport;
use crate::objects::object::ReferenceKind; use crate::objects::object::ReferenceKind;
use crate::objects::object_manager::ObjectManager; use crate::objects::object_manager::ObjectManager;
use crate::thread::VmThread; use crate::thread::VmThread;
use crate::value::Value;
use crate::{MethodDescriptor, ThreadId}; use crate::{MethodDescriptor, ThreadId};
use dashmap::DashMap; use dashmap::DashMap;
use imp::Library; use imp::Library;
use log::trace; use log::{info, trace};
use parking_lot::{Mutex, RwLock}; use parking_lot::{Mutex, RwLock};
use std::sync::atomic::AtomicU64; use std::sync::atomic::AtomicU64;
use std::sync::{Arc, LazyLock}; use std::sync::{Arc, LazyLock};
use std::time::Instant;
type NativeLibraries = Arc<RwLock<Vec<(String, Library)>>>; type NativeLibraries = Arc<RwLock<Vec<(String, Library)>>>;
@ -36,12 +35,38 @@ pub struct Vm {
pub native_libraries: NativeLibraries, pub native_libraries: NativeLibraries,
pub gc: Arc<RwLock<ObjectManager>>, pub gc: Arc<RwLock<ObjectManager>>,
pub safent: RwLock<UnsafeSupport>, pub safent: RwLock<UnsafeSupport>,
pub NEXT_ID: AtomicU64, pub next_id: AtomicU64,
} }
impl Vm { impl Vm {
// start vm, loading main from classfile // start vm, loading main from classfile
pub fn new() -> Arc<Self> { pub fn new() -> Arc<Self> {
let vm = Arc::new(Self {
threads: DashMap::new(),
main_thread_id: ThreadId(0),
loader: Arc::new(Mutex::from(ClassLoader::with_bimage())),
native_methods: DashMap::new(),
native_libraries: Arc::new(RwLock::new(Vec::new())),
gc: Default::default(),
safent: Default::default(),
next_id: AtomicU64::new(0),
});
// Create main thread
let thread = VmThread::new(vm.clone(), None);
let thread_id = thread.id;
// Store in VM
vm.threads.insert(thread_id, thread.clone());
// Set as current thread
VmThread::set_current(thread_id);
vm
}
/// Creates a minimal VM for testing, without loading the boot image
#[cfg(test)]
pub(crate) fn new_for_test() -> Arc<Self> {
let vm = Arc::new(Self { let vm = Arc::new(Self {
threads: DashMap::new(), threads: DashMap::new(),
main_thread_id: ThreadId(0), main_thread_id: ThreadId(0),
@ -50,7 +75,7 @@ impl Vm {
native_libraries: Arc::new(RwLock::new(Vec::new())), native_libraries: Arc::new(RwLock::new(Vec::new())),
gc: Default::default(), gc: Default::default(),
safent: Default::default(), safent: Default::default(),
NEXT_ID: AtomicU64::new(0), next_id: AtomicU64::new(0),
}); });
// Create main thread // Create main thread
@ -78,7 +103,7 @@ impl Vm {
for (lib_name, lib) in self.native_libraries.read().iter() { for (lib_name, lib) in self.native_libraries.read().iter() {
let res = unsafe { lib.get::<unsafe extern "system" fn()>(name.as_bytes()) }; let res = unsafe { lib.get::<unsafe extern "system" fn()>(name.as_bytes()) };
if res.is_ok() { if res.is_ok() {
println!( trace!(
"Register native for native symbol: {} out of: {}", "Register native for native symbol: {} out of: {}",
&name, lib_name &name, lib_name
); );
@ -127,7 +152,7 @@ impl Vm {
for prim in prims { for prim in prims {
let klass = self.loader.lock().primitive_class(prim.0); let klass = self.loader.lock().primitive_class(prim.0);
let class_class = thread.get_class("java/lang/Class")?; let class_class = thread.loader.lock().get_or_load("java/lang/Class", None)?;
let name_obj = thread.intern_string(&prim.0); let name_obj = thread.intern_string(&prim.0);
let flags = ClassFlags::from(1041u16); let flags = ClassFlags::from(1041u16);
let class_obj = self.gc.write().new_class( let class_obj = self.gc.write().new_class(
@ -136,8 +161,9 @@ impl Vm {
None, None,
flags, flags,
true, true,
None,
); );
klass.mirror.set(class_obj.lock().id).unwrap(); // klass.mirror.set(class_obj.lock().id).unwrap();
let prim_array_klass = self.loader.lock().create_array_class(klass.clone()); let prim_array_klass = self.loader.lock().create_array_class(klass.clone());
let arr_name_obj = thread.intern_string(&prim_array_klass.this_class); let arr_name_obj = thread.intern_string(&prim_array_klass.this_class);
@ -147,6 +173,7 @@ impl Vm {
None, None,
flags, flags,
false, false,
Some(ReferenceKind::ObjectReference(class_obj)),
); );
prim_array_klass prim_array_klass
.mirror .mirror
@ -175,7 +202,11 @@ impl Vm {
} }
pub fn run(&self, what: &str) -> Result<(), VmError> { pub fn run(&self, what: &str) -> Result<(), VmError> {
let start = Instant::now();
self.boot_strap()?; self.boot_strap()?;
info!("Bootstrap completed in {:?}", start.elapsed());
let lt = self.loader.lock().access_time();
info!("Module load time: {:?}", lt);
// Get main thread from DashMap // Get main thread from DashMap
let thread = self.threads.get(&self.main_thread_id).unwrap().clone(); let thread = self.threads.get(&self.main_thread_id).unwrap().clone();
thread.invoke_main(what) thread.invoke_main(what)

View File

@ -1,5 +1,4 @@
use crate::get_thread; use jni::sys::{JNIEnv, jclass, jint};
use jni::sys::{jclass, jint, JNIEnv};
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub extern "C" fn Java_jdk_internal_misc_CDS_getCDSConfigStatus( pub extern "C" fn Java_jdk_internal_misc_CDS_getCDSConfigStatus(

View File

@ -1,11 +1,11 @@
use crate::{get_thread, resolve_object}; use crate::{get_thread, resolve_object};
use jni::sys::{JNI_TRUE, jboolean, jclass, jobject}; use jni::sys::{JNI_FALSE, JNI_TRUE, jboolean, jclass, jobject};
use jni::sys::{JNIEnv, jint}; use jni::sys::{JNIEnv, jint};
use jni::sys::{jobjectArray, jstring}; use jni::sys::{jobjectArray, jstring};
use log::trace; use log::trace;
use roast_vm_core::class_file::FieldRef; use roast_vm_core::class_file::FieldRef;
use roast_vm_core::objects::ReferenceKind;
use roast_vm_core::objects::array::ArrayReference; use roast_vm_core::objects::array::ArrayReference;
use roast_vm_core::objects::{Reference, ReferenceKind};
use roast_vm_core::value::Value; use roast_vm_core::value::Value;
use roast_vm_core::{BaseType, FieldType}; use roast_vm_core::{BaseType, FieldType};
use std::ptr; use std::ptr;
@ -19,40 +19,13 @@ pub unsafe extern "system" fn Java_java_lang_Class_getPrimitiveClass<'local>(
let thread = &*get_thread(env); let thread = &*get_thread(env);
let rust_string = { let rust_string = {
let gc = thread.gc.read();
let obj_id = name as u32; let obj_id = name as u32;
let obj_ref = match thread.gc.read().get(obj_id) {
let obj_ref = match gc.get(obj_id) {
ReferenceKind::ObjectReference(obj) => obj, ReferenceKind::ObjectReference(obj) => obj,
_ => return 0 as jclass, _ => return 0 as jclass,
}; };
let obj = obj_ref.lock(); thread.gc.write().transmute_string(obj_ref).unwrap()
let field_ref = FieldRef {
class: "java/lang/String".to_string(),
name: "value".to_string(),
desc: FieldType::ArrayType(Box::new(FieldType::Base(BaseType::Byte))),
};
let value_field = obj.get_field(&field_ref);
let Value::Reference(Some(ReferenceKind::ArrayReference(ArrayReference::Byte(byte_array)))) =
value_field
else {
return 0 as jclass;
};
let array = byte_array.lock();
let bytes: Vec<u8> = (&array).iter().map(|&b| b as u8).collect();
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);
}
String::from_utf16_lossy(&utf16_chars)
// All locks dropped here at end of block
}; };
let klass = thread.get_class(&rust_string).unwrap(); let klass = thread.get_class(&rust_string).unwrap();
@ -66,8 +39,8 @@ pub unsafe extern "system" fn Java_java_lang_Class_forName0(
_class: jclass, _class: jclass,
name: jstring, name: jstring,
initialize: jboolean, initialize: jboolean,
loader: jobject, _loader: jobject,
caller: jclass, _caller: jclass,
) -> jclass { ) -> jclass {
trace!("Java_java_lang_Class_forName0"); trace!("Java_java_lang_Class_forName0");
let thread = &*get_thread(env); let thread = &*get_thread(env);
@ -152,3 +125,12 @@ pub unsafe extern "system" fn Java_java_lang_Class_getDeclaredConstructors0(
output.id() as jobjectArray output.id() as jobjectArray
} }
#[unsafe(no_mangle)]
pub unsafe extern "system" fn Java_java_lang_Class_desiredAssertionStatus0(
_env: *mut JNIEnv,
_class: jclass,
_this: jclass,
) -> jboolean {
JNI_FALSE
}

View File

@ -0,0 +1,102 @@
use crate::get_thread;
use jni::sys::{JNIEnv, jboolean, jbyteArray, jint, jobject};
use roast_vm_core::objects::array::ArrayReference;
#[unsafe(no_mangle)]
unsafe extern "system" fn Java_java_io_FileOutputStream_writeBytes(
env: *mut JNIEnv,
this: jobject,
bytes: jbyteArray,
off: jint,
len: jint,
append: jboolean,
) {
let thread = &*get_thread(env);
// Get the FileOutputStream's fd field (FileDescriptor)
let fos = thread
.gc
.read()
.get(this as u32)
.try_into_object_reference()
.unwrap();
let fd_obj_id = {
let guard = fos.lock();
guard
.fields
.get("fd")
.and_then(|v| v.value().as_ref_kind().ok())
.map(|r| r.id())
.expect("FileOutputStream must have fd")
};
// Get the actual fd int from FileDescriptor
let fd_obj = thread
.gc
.read()
.get(fd_obj_id)
.try_into_object_reference()
.unwrap();
let fd = {
let guard = fd_obj.lock();
guard
.fields
.get("fd")
.map(|v| v.value().clone().try_into_jint().unwrap())
.expect("FileDescriptor must have fd")
};
// Get the byte array
let arr = thread
.gc
.read()
.get(bytes as u32)
.try_into_array_reference()
.unwrap();
if let ArrayReference::Byte(byte_arr) = arr {
let guard = byte_arr.lock();
let slice = &guard.backing[off as usize..(off + len) as usize];
let slice = slice.iter().map(|e| *e as u8).collect::<Vec<_>>();
if fd == 1 {
use std::io::Write;
std::io::stdout().write_all(&slice).ok();
} else if fd == 2 {
use std::io::Write;
std::io::stderr().write_all(&slice).ok();
}
// Write to file descriptor
#[cfg(windows)]
{
use std::io::Write;
use std::os::windows::io::FromRawHandle;
let handle = fd as *mut std::ffi::c_void;
let mut file = std::fs::File::from_raw_handle(handle);
file.write_all(std::slice::from_raw_parts(
slice.as_ptr() as *const u8,
slice.len(),
))
.ok();
std::mem::forget(file); // Don't close the handle
}
#[cfg(unix)]
{
use std::io::Write;
use std::os::unix::io::FromRawFd;
let mut file = std::fs::File::from_raw_fd(fd);
file.write_all(std::slice::from_raw_parts(
slice.as_ptr() as *const u8,
slice.len(),
))
.ok();
std::mem::forget(file); // Don't close the fd
}
}
}

View File

@ -1,33 +1,34 @@
#![allow(non_snake_case)] #![allow(non_snake_case)]
#![allow(unsafe_op_in_unsafe_fn)]
mod CDS; mod CDS;
mod class; mod class;
mod file_output_stream;
mod misc_unsafe; mod misc_unsafe;
mod object; mod object;
mod reflect;
mod reflection; mod reflection;
mod runtime; mod runtime;
mod scoped_memory_access; mod scoped_memory_access;
mod signal; mod signal;
mod string;
mod system; mod system;
mod system_props; mod system_props;
mod thread; mod thread;
use jni::objects::{JClass, JObject, JString}; use jni::JNIEnv;
use jni::strings::JNIString; use jni::objects::{JClass, JString};
use jni::sys::{jclass, jlong, jobject, jobjectArray}; use jni::sys::{jlong, jobject};
use jni::{JNIEnv, NativeMethod};
use roast_vm_core::VmThread; use roast_vm_core::VmThread;
use roast_vm_core::objects::ReferenceKind; use roast_vm_core::objects::ReferenceKind;
use roast_vm_core::objects::array::ArrayReference; use roast_vm_core::objects::array::ArrayReference;
use roast_vm_core::objects::object::ObjectReference; use roast_vm_core::objects::object::ObjectReference;
use std::ffi::c_void;
use std::io::Write; use std::io::Write;
use std::time::{SystemTime, UNIX_EPOCH}; use std::time::{SystemTime, UNIX_EPOCH};
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub extern "system" fn Java_org_example_MockIO_print<'local>( pub extern "system" fn Java_org_example_MockIO_print<'local>(
mut env: JNIEnv<'local>, env: JNIEnv<'local>,
jclass: JClass<'local>, _jclass: JClass<'local>,
input: JString<'local>, input: JString<'local>,
) { ) {
unsafe { unsafe {
@ -44,8 +45,8 @@ pub extern "system" fn Java_org_example_MockIO_print<'local>(
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub extern "system" fn Java_org_example_Main_getTime<'local>( pub extern "system" fn Java_org_example_Main_getTime<'local>(
mut env: JNIEnv<'local>, _env: JNIEnv<'local>,
jclass: JClass<'local>, _jclass: JClass<'local>,
) -> jlong { ) -> jlong {
SystemTime::now() SystemTime::now()
.duration_since(UNIX_EPOCH) .duration_since(UNIX_EPOCH)
@ -55,8 +56,8 @@ pub extern "system" fn Java_org_example_Main_getTime<'local>(
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub extern "system" fn Java_org_example_MockIO_println<'local>( pub extern "system" fn Java_org_example_MockIO_println<'local>(
mut env: JNIEnv<'local>, _env: JNIEnv<'local>,
jclass: JClass<'local>, _jclass: JClass<'local>,
input: jlong, input: jlong,
) { ) {
// let input: String = env // let input: String = env

View File

@ -1,18 +1,16 @@
use crate::thread::THREAD_NEXT_ID_SENTINEL; use crate::thread::THREAD_NEXT_ID_SENTINEL;
use crate::{get_thread, resolve_array, resolve_object}; use crate::{get_thread, resolve_object};
use jni::sys::{JNI_FALSE, JNI_TRUE, JNIEnv, jboolean, jclass, jint, jlong, jobject, jstring}; use jni::sys::{JNI_FALSE, JNI_TRUE, JNIEnv, jboolean, jclass, jint, jlong, jobject, jstring};
use log::warn; use log::warn;
use roast_vm_core::native::r#unsafe::OffsetKind;
use roast_vm_core::objects::ReferenceKind; use roast_vm_core::objects::ReferenceKind;
use roast_vm_core::objects::array::ArrayReference; use roast_vm_core::objects::array::ArrayReference;
use roast_vm_core::objects::object::string_from_bytes;
use roast_vm_core::value::Value; use roast_vm_core::value::Value;
use std::sync::atomic::Ordering; use std::sync::atomic::Ordering;
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_registerNatives( pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_registerNatives(
env: *mut JNIEnv, _env: *mut JNIEnv,
obj: jclass, _obj: jclass,
) { ) {
//no op //no op
warn!("Unsafe_registerNatives is NO OP") warn!("Unsafe_registerNatives is NO OP")
@ -151,7 +149,7 @@ pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_compareAndSetReferen
.read() .read()
.resolve_field(offset) .resolve_field(offset)
.expect("unregistered field offset"); .expect("unregistered field offset");
let mut guard = obj_ref.lock(); let guard = obj_ref.lock();
let current_id = guard let current_id = guard
.fields .fields
@ -214,7 +212,7 @@ pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_compareAndSetInt(
.read() .read()
.resolve_field(offset) .resolve_field(offset)
.expect("unregistered field offset"); .expect("unregistered field offset");
let mut guard = obj_ref.lock(); let guard = obj_ref.lock();
let current = guard let current = guard
.fields .fields
.get(&key.field_name) .get(&key.field_name)
@ -243,7 +241,7 @@ pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_compareAndSetLong(
) -> jboolean { ) -> jboolean {
let thread = &*get_thread(env); let thread = &*get_thread(env);
if offset == THREAD_NEXT_ID_SENTINEL { if offset == THREAD_NEXT_ID_SENTINEL {
return match thread.vm.NEXT_ID.compare_exchange( return match thread.vm.next_id.compare_exchange(
expected as u64, expected as u64,
new_val as u64, new_val as u64,
Ordering::SeqCst, Ordering::SeqCst,
@ -277,7 +275,7 @@ pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_compareAndSetLong(
.read() .read()
.resolve_field(offset) .resolve_field(offset)
.expect("unregistered field offset"); .expect("unregistered field offset");
let mut guard = obj_ref.lock(); let guard = obj_ref.lock();
let current = guard let current = guard
.fields .fields
.get(&key.field_name) .get(&key.field_name)
@ -328,7 +326,7 @@ pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_compareAndExchangeIn
.read() .read()
.resolve_field(offset) .resolve_field(offset)
.expect("unregistered field offset"); .expect("unregistered field offset");
let mut guard = obj_ref.lock(); let guard = obj_ref.lock();
let current = guard let current = guard
.fields .fields
.get(&key.field_name) .get(&key.field_name)
@ -377,7 +375,7 @@ pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_compareAndExchangeLo
.read() .read()
.resolve_field(offset) .resolve_field(offset)
.expect("unregistered field offset"); .expect("unregistered field offset");
let mut guard = obj_ref.lock(); let guard = obj_ref.lock();
let current = guard let current = guard
.fields .fields
.get(&key.field_name) .get(&key.field_name)
@ -440,7 +438,7 @@ pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_compareAndExchangeRe
.read() .read()
.resolve_field(offset) .resolve_field(offset)
.expect("unregistered field offset"); .expect("unregistered field offset");
let mut guard = obj_ref.lock(); let guard = obj_ref.lock();
let current = guard let current = guard
.fields .fields
@ -594,7 +592,7 @@ pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_getLongVolatile(
) -> jlong { ) -> jlong {
let thread = &*get_thread(env); let thread = &*get_thread(env);
if obj.is_null() && offset == THREAD_NEXT_ID_SENTINEL { if obj.is_null() && offset == THREAD_NEXT_ID_SENTINEL {
return thread.vm.NEXT_ID.load(Ordering::SeqCst) as jlong; return thread.vm.next_id.load(Ordering::SeqCst) as jlong;
} }
let reference = thread.gc.read().get(obj as u32); let reference = thread.gc.read().get(obj as u32);

View File

@ -3,7 +3,7 @@ use jni::sys::{JNIEnv, jint, jlong, jobject};
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub unsafe extern "system" fn Java_java_lang_Object_hashCode( pub unsafe extern "system" fn Java_java_lang_Object_hashCode(
env: *mut JNIEnv, _env: *mut JNIEnv,
obj: jobject, obj: jobject,
) -> jint { ) -> jint {
obj as u32 as i32 obj as u32 as i32

View File

@ -0,0 +1,38 @@
use crate::get_thread;
use jni::sys::{JNI_TRUE, JNIEnv, jclass, jint, jobject};
use roast_vm_core::FieldType;
use roast_vm_core::class_file::FieldRef;
#[unsafe(no_mangle)]
pub unsafe extern "system" fn Java_java_lang_reflect_Array_newArray(
env: *mut JNIEnv,
_class: jclass,
component_type: jclass,
length: jint,
) -> jobject {
let thread = &*get_thread(env);
let comp = thread
.loader
.lock()
.class_from_mirror_id(component_type as u32)
.unwrap();
let classObj = thread
.gc
.read()
.get(component_type as u32)
.try_into_object_reference()
.unwrap();
let prim = classObj
.lock()
.get_field(&FieldRef::new("Class", "primitive", FieldType::bool()))
.try_into_jboolean()
.unwrap();
let refe = if prim == JNI_TRUE {
thread.gc.write().new_primitive_array(comp, length)
} else {
thread.gc.write().new_object_array(comp, length)
};
refe.id() as jobject
}

View File

@ -0,0 +1 @@
pub mod array;

View File

@ -1,9 +1,9 @@
use crate::get_thread; use crate::get_thread;
use jni::sys::{jboolean, jclass, jint, jobject, jobjectArray, JNIEnv}; use jni::sys::{JNIEnv, jclass, jint, jobject, jobjectArray};
use roast_vm_core::class_file::FieldRef; use roast_vm_core::class_file::FieldRef;
use roast_vm_core::objects::array::ArrayReference; use roast_vm_core::objects::array::ArrayReference;
use roast_vm_core::value::Value; use roast_vm_core::value::Value;
use roast_vm_core::{BaseType, FieldType, MethodDescriptor}; use roast_vm_core::{BaseType, FieldType};
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub unsafe extern "system" fn Java_jdk_internal_reflect_Reflection_getCallerClass( pub unsafe extern "system" fn Java_jdk_internal_reflect_Reflection_getCallerClass(
@ -21,7 +21,7 @@ pub unsafe extern "system" fn Java_jdk_internal_reflect_Reflection_getCallerClas
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub unsafe extern "system" fn Java_jdk_internal_reflect_Reflection_getClassAccessFlags( pub unsafe extern "system" fn Java_jdk_internal_reflect_Reflection_getClassAccessFlags(
env: *mut JNIEnv, env: *mut JNIEnv,
reflection_class: jclass, _reflection_class: jclass,
class: jclass, class: jclass,
) -> jint { ) -> jint {
let thread = &*get_thread(env); let thread = &*get_thread(env);
@ -38,7 +38,7 @@ pub unsafe extern "system" fn Java_jdk_internal_reflect_Reflection_getClassAcces
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub unsafe extern "system" fn Java_jdk_internal_reflect_DirectConstructorHandleAccessor_00024NativeAccessor_newInstance0( pub unsafe extern "system" fn Java_jdk_internal_reflect_DirectConstructorHandleAccessor_00024NativeAccessor_newInstance0(
env: *mut JNIEnv, env: *mut JNIEnv,
DirectConstructorHandleAccessor_class: jclass, _DirectConstructorHandleAccessor_class: jclass,
constructor: jobject, constructor: jobject,
args: jobjectArray, args: jobjectArray,
) -> jobject { ) -> jobject {

View File

@ -2,7 +2,7 @@ use jni::sys::{JNIEnv, jclass};
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub unsafe extern "system" fn Java_jdk_internal_misc_ScopedMemoryAccess_registerNatives( pub unsafe extern "system" fn Java_jdk_internal_misc_ScopedMemoryAccess_registerNatives(
env: *mut JNIEnv, _env: *mut JNIEnv,
_class: jclass, _class: jclass,
) { ) {
// noop // noop

View File

@ -0,0 +1,21 @@
use crate::get_thread;
use jni::sys::{JNIEnv, jstring};
#[unsafe(no_mangle)]
unsafe extern "system" fn Java_java_lang_String_intern(env: *mut JNIEnv, this: jstring) -> jstring {
let thread = &*get_thread(env);
let obj_id = this as u32;
let string_ref = thread
.gc
.read()
.get(obj_id)
.try_into_object_reference()
.unwrap();
let utf = thread.gc.read().transmute_string(string_ref).unwrap();
let interned_id = thread.gc.write().intern_existing_string(utf, obj_id);
interned_id as jstring
}

View File

@ -1,7 +1,6 @@
use crate::{get_thread, resolve_array, resolve_object}; use crate::{get_thread, resolve_array};
use jni::objects::JClass; use jni::objects::JClass;
use jni::sys::{JNIEnv, jclass, jint, jlong, jobject}; use jni::sys::{JNIEnv, jclass, jint, jlong, jobject};
use log::warn;
use std::time::{SystemTime, UNIX_EPOCH}; use std::time::{SystemTime, UNIX_EPOCH};
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
@ -50,7 +49,6 @@ pub unsafe extern "system" fn Java_java_lang_System_arraycopy(
{ {
panic!("Array index out of bounds!"); panic!("Array index out of bounds!");
// throw_exception(env, "java/lang/ArrayIndexOutOfBoundsException", None); // throw_exception(env, "java/lang/ArrayIndexOutOfBoundsException", None);
return;
} }
dst_arr dst_arr
@ -61,8 +59,8 @@ pub unsafe extern "system" fn Java_java_lang_System_arraycopy(
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub extern "system" fn current_time_millis<'local>( pub extern "system" fn current_time_millis<'local>(
mut env: jni::JNIEnv<'local>, _env: jni::JNIEnv<'local>,
jclass: JClass<'local>, _jclass: JClass<'local>,
) -> jlong { ) -> jlong {
SystemTime::now() SystemTime::now()
.duration_since(UNIX_EPOCH) .duration_since(UNIX_EPOCH)
@ -72,8 +70,8 @@ pub extern "system" fn current_time_millis<'local>(
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub extern "system" fn Java_java_lang_System_nanoTime<'local>( pub extern "system" fn Java_java_lang_System_nanoTime<'local>(
mut env: jni::JNIEnv<'local>, _env: jni::JNIEnv<'local>,
jclass: JClass<'local>, _jclass: JClass<'local>,
) -> jlong { ) -> jlong {
SystemTime::now() SystemTime::now()
.duration_since(UNIX_EPOCH) .duration_since(UNIX_EPOCH)
@ -83,8 +81,8 @@ pub extern "system" fn Java_java_lang_System_nanoTime<'local>(
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub extern "system" fn Java_java_lang_System_registerNatives<'local>( pub extern "system" fn Java_java_lang_System_registerNatives<'local>(
mut env: jni::JNIEnv<'local>, _env: jni::JNIEnv<'local>,
jclass: JClass<'local>, _jclass: JClass<'local>,
) { ) {
// No-op: native methods are handled by roast // No-op: native methods are handled by roast
} }

View File

@ -58,7 +58,7 @@ unsafe extern "system" fn Java_java_lang_Thread_start0(env: *mut JNIEnv, this: j
VmThread::set_current(thread_id); VmThread::set_current(thread_id);
// Call this.run() // Call this.run()
let thread_class = new_thread.get_class("java/lang/Thread").unwrap(); // let thread_class = new_thread.get_class("java/lang/Thread").unwrap();
let run_ref = MethodRef { let run_ref = MethodRef {
class: "java/lang/Thread".to_string(), class: "java/lang/Thread".to_string(),
name: "run".to_string(), name: "run".to_string(),