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 std::collections::{HashMap, HashSet};
use std::fs::File;
use std::ops::{Add, AddAssign};
use std::ops::AddAssign;
use std::time::{Duration, Instant};
const DEFAULT_LOCATION: &str = "./lib/modules";
@ -31,21 +30,6 @@ impl Default for Bimage {
.into_iter()
.collect::<Vec<_>>();
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();
Self {
@ -85,9 +69,6 @@ impl Bimage {
pub fn get_class(&mut self, module: &str, class: &str) -> Result<Vec<u8>, String> {
// trace!("Modules{:#?}", self.modules);
if class.contains("ScopedMemoryAccess") {
println!("Time to scoped: {:?}", self.total_access_time)
}
let start = Instant::now();
let path = Self::resolve_path(module, class);
let res = self.image.read_file(&path).map_err(|e| {

View File

@ -1,15 +1,12 @@
use crate::attributes::AttributeInfo;
use crate::class_file::{
ClassFile, ClassFlags, ConstantPoolEntry, FieldData, FieldInfo, FieldRef, MethodData,
MethodInfo, MethodRef,
};
use crate::class_file::attributes::BootstrapMethodsAttribute;
use crate::class_file::{ClassFlags, ConstantPoolEntry, FieldData, MethodData};
use crate::error::VmError;
use crate::{FieldType, MethodDescriptor};
use log::trace;
use parking_lot::Mutex;
use std::hash::{Hash, Hasher};
use std::sync::atomic::AtomicBool;
use std::sync::{Arc, OnceLock, OnceState};
use std::sync::{Arc, OnceLock};
use std::thread::ThreadId;
/// JVM Spec 5.5: Initialization states for a class
@ -25,6 +22,8 @@ pub enum InitState {
Error(String),
}
pub type ClassRef = Arc<RuntimeClass>;
#[derive(Debug)]
pub struct RuntimeClass {
pub constant_pool: Arc<Vec<ConstantPoolEntry>>,
@ -42,6 +41,7 @@ pub struct RuntimeClass {
pub super_interfaces: Vec<Arc<RuntimeClass>>,
pub component_type: Option<Arc<RuntimeClass>>,
pub source_file: Option<String>,
pub bootstrap_attribute: Option<BootstrapMethodsAttribute>,
}
impl Hash for RuntimeClass {
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::{ClassFile, Constant, ConstantPoolEntry};
use deku::{DekuContainerRead, DekuError, DekuReader};
use deku_derive::DekuRead;
use log::trace;
use std::fmt::{Display, Formatter};
use std::ops::Deref;
use crate::class_file::{ClassFile, Constant};
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)]
#[deku(ctx = "_endian: deku::ctx::Endian", endian = "big")]
@ -24,7 +23,7 @@ pub enum Attribute {
ConstantValue(Constant),
Code(CodeAttribute),
StackMapTable(Vec<u8>),
BootstrapMethods,
BootstrapMethods(BootstrapMethodsAttribute),
NestHost,
NestMembers,
PermittedSubclasses,
@ -61,6 +60,30 @@ pub enum ArrayType {
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)]
pub struct LookupSwitchData {
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)?);
}
Ok(TableSwitchData { default, low, high, offsets })
Ok(TableSwitchData {
default,
low,
high,
offsets,
})
}
}
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 {
ArrayType::T_BOOLEAN => "[Z",
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 {
// pub fn parse_attribute(&self, constant_pool: &[ConstantPoolEntry]) -> Option<Attribute> {
// let name = crate::class_file::pool_get_string(constant_pool, self.attribute_name_index)?;
@ -226,7 +238,7 @@ impl AttributeInfo {
impl LocalVariableTableAttribute {}
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!(
f,
"AttributeInfo {{ name_index: {}, length: {} }}",
@ -236,7 +248,7 @@ impl Display for AttributeInfo {
}
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 {
Attribute::Code(code) => write!(f, "Code attribute: {}", code),
Attribute::SourceFile(index) => write!(f, "SourceFile attribute, index: {}", index),
@ -252,6 +264,9 @@ impl Display for Attribute {
Attribute::LocalVariableTable(table) => {
write!(f, "LocalVariableTable attribute: {}", table)
}
Attribute::BootstrapMethods(bootstrap) => {
write!(f, "BootstrapMethods attribute: {}", bootstrap)
}
Attribute::Unknown(name, data) => {
write!(f, "Unknown attribute '{}', {} bytes", name, data.len())
}
@ -263,7 +278,7 @@ impl Display for Attribute {
}
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!(
f,
"stack={}, locals={}, code={} bytes, exceptions={}, attributes={}",
@ -320,7 +335,7 @@ pub struct LocalVariableTableEntry {
}
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!(
f,
"local_variable_table_length={}, entries={}",
@ -338,6 +353,23 @@ pub struct LineNumberTableAttribute {
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)]
#[deku(ctx = "_endian: deku::ctx::Endian", endian = "big")]
pub struct LineNumberTableEntry {
@ -346,7 +378,7 @@ pub struct LineNumberTableEntry {
}
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!(
f,
"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::RuntimeClass;
use crate::class_file::constant_pool::{ConstantPoolError, ConstantPoolExt, ConstantPoolOwned};
use crate::class_file::attributes::{
Attribute, AttributeInfo, CodeAttribute, LineNumberTableEntry,
};
use crate::class_file::constant_pool::{ConstantPoolExt, ConstantPoolOwned};
use crate::instructions::Ops;
use crate::value::Value;
use crate::{BaseType, FieldType, MethodDescriptor};
@ -214,8 +215,8 @@ pub struct ConstantPackageInfo {
}
// Display implementations for better formatting
impl fmt::Display for ClassFile {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
impl Display for ClassFile {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
writeln!(f, "Class File Information:")?;
writeln!(
f,
@ -266,8 +267,8 @@ impl fmt::Display for ClassFile {
}
}
impl fmt::Display for ConstantPoolEntry {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
impl Display for ConstantPoolEntry {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
ConstantPoolEntry::Utf8(info) => {
let s = String::from_utf8_lossy(&info.bytes);
@ -323,8 +324,8 @@ impl fmt::Display for ConstantPoolEntry {
}
}
impl fmt::Display for FieldInfo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
impl Display for FieldInfo {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(
f,
"flags=0x{:04X}, name=#{}, descriptor=#{}, attrs={}",
@ -336,8 +337,8 @@ impl fmt::Display for FieldInfo {
}
}
impl fmt::Display for MethodInfo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
impl Display for MethodInfo {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let attrs: Vec<_> = self
.attributes
.iter()
@ -395,7 +396,7 @@ impl ClassFile {
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
.get(self)
.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 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));
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)]
pub struct FieldRef {
pub class: String,
@ -685,6 +692,18 @@ pub struct FieldRef {
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)]
pub struct FieldData {
pub name: String,
@ -699,19 +718,38 @@ pub enum Constant {
Long(i64),
Float(f32),
Double(f64),
String(String),
String(u16),
}
impl From<Constant> for Value {
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<Constant> for Value {
// 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 {
ConstantPoolEntry::Integer(i) => Self::Int(i),
ConstantPoolEntry::Float(f) => Self::Float(f),
ConstantPoolEntry::Long(l) => Self::Long(l),
ConstantPoolEntry::Double(d) => Self::Double(d),
ConstantPoolEntry::String(s) => Self::String(s.string_index),
_ => panic!(),
}
}
}

View File

@ -1,5 +1,6 @@
use crate::attributes::{
Attribute, AttributeInfo, CodeAttribute, LineNumberTableAttribute, LocalVariableTableAttribute,
use crate::class_file::attributes::{
Attribute, AttributeInfo, BootstrapMethodsAttribute, CodeAttribute, LineNumberTableAttribute,
LocalVariableTableAttribute,
};
use crate::class_file::{
ConstantClassInfo, ConstantDynamicInfo, ConstantFieldrefInfo, ConstantInterfaceMethodrefInfo,
@ -9,8 +10,8 @@ use crate::class_file::{
MethodRef,
};
use crate::error::VmError;
use crate::{pool_get_impl, FieldType, MethodDescriptor};
use cesu8::{from_java_cesu8, Cesu8DecodingError};
use crate::{FieldType, MethodDescriptor, pool_get_impl};
use cesu8::{Cesu8DecodingError, from_java_cesu8};
use deku::DekuContainerRead;
use std::fmt::{Display, Formatter};
@ -137,8 +138,12 @@ pub trait ConstantPoolExt: ConstantPoolGet {
// trace!("Parsing attribute with name: {}", name);
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" => {
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))
}
"SourceFile" => {
@ -160,6 +165,10 @@ pub trait ConstantPoolExt: ConstantPoolGet {
let (_, lvt) = LocalVariableTableAttribute::from_bytes((&a.info.as_slice(), 0))?;
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())),
}
}

View File

@ -1,6 +1,5 @@
pub mod attributes;
pub mod class_file;
pub mod constant_pool;
// Re-export items if you want them accessible directly from class_file::
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::class::{InitState, RuntimeClass};
use crate::class_file::attributes::Attribute;
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::{FieldType, MethodDescriptor};
use dashmap::DashMap;
@ -85,7 +87,7 @@ pub fn resolve_path(what: &str) -> Result<(PathBuf, String), String> {
#[derive(Default)]
pub struct ClassLoader {
pub(crate) classes: DashMap<(String, LoaderId), Arc<RuntimeClass>>,
bimage: Bimage,
bimage: Option<Bimage>,
// pub needs_init: Vec<Arc<RuntimeClass>>,
}
@ -93,10 +95,12 @@ type LoaderId = Option<u32>;
impl ClassLoader {
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> {
let loader = Self::default();
let loader = Self::with_bimage();
// for entry in VM_CLASSES {
// let module_class = format!("{}/{}", "java.base", entry);;
// loader.load_class(&module_class)?;
@ -104,6 +108,14 @@ impl ClassLoader {
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>> {
self.classes
.iter()
@ -147,37 +159,34 @@ impl ClassLoader {
class_name: &str,
loader: LoaderId,
) -> 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)) {
return Ok(class.clone());
}
if class_name.starts_with("[") {
let component_name = class_name.strip_prefix("[").unwrap();
let component = match component_name.chars().next() {
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(
if let Some(component_name) = class_name.strip_prefix('[') {
if component_name.is_empty() {
return Err(VmError::LoaderError(
"empty component descriptor".to_string(),
)),
_ => Err(VmError::LoaderError(format!(
"invalid component descriptor: {}",
component_name
))),
}?;
// let component = self.get_or_load(component_name, None)?;
));
}
let component = self.get_or_load(component_name, loader)?;
let arr_class = self.create_array_class(component);
return Ok(arr_class);
}
@ -197,9 +206,11 @@ impl ClassLoader {
let (module, class_fqn) = ("", what);
let bytes = self
.bimage
.get_class(module, class_fqn)
.or_else(|e| Self::load_class_from_disk(what))
.map_err(|e1| VmError::LoaderError(format!("failed to find class: {}", what)))?;
.as_mut()
.and_then(|b| b.get_class(module, class_fqn).ok())
.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) =
ClassFile::from_bytes((bytes.as_ref(), 0)).map_err(|e| VmError::DekuError(e))?;
let runtime = self.runtime_class(cf);
@ -287,7 +298,13 @@ impl ClassLoader {
if let Attribute::ConstantValue(val) =
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 {
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 {
constant_pool,
access_flags,
@ -386,6 +411,7 @@ impl ClassLoader {
component_type: None,
source_file,
mirror_in_progress: Default::default(),
bootstrap_attribute,
}
}
// pub fn get_or_create_array_class(class_name: &str) -> RuntimeClass {
@ -440,6 +466,7 @@ impl ClassLoader {
super_interfaces: vec![cloneable, serializable],
component_type: Some(component), // new field
source_file: None,
bootstrap_attribute: None,
};
let klass = Arc::new(klass);
self.classes.insert((name.to_string(), None), klass.clone());
@ -472,6 +499,7 @@ impl ClassLoader {
super_interfaces: vec![],
component_type: None,
source_file: None,
bootstrap_attribute: None,
});
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_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::error::{StackTraceElement, VmError};
use crate::instructions::{Ops, WideData};
use crate::objects::ReferenceKind;
use crate::objects::array::ArrayReference;
use crate::prim::*;
use crate::value::{LocalVariables, OperandStack, Primitive, Value};
use crate::value::{Primitive, Value};
use crate::vm::Vm;
use crate::{
BaseType, FieldType, VmThread, array_store, array_store_cast, binary_op, convert_float_to_int,
convert_int_narrow, convert_simple, error, float_cmp, if_int_cmp, if_int_zero, int_div_rem,
load, shift_op, store, unary_op,
BaseType, FieldType, MethodDescriptor, VmThread, array_store, array_store_cast, binary_op,
convert_float_to_int, convert_int_narrow, convert_simple, float_cmp, if_int_cmp, if_int_zero,
int_div_rem, load, shift_op, store, unary_op,
};
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::sync::Arc;
@ -95,10 +99,7 @@ impl Frame {
table
.iter()
.rev()
.find(|entry| {
entry.start_pc as i64 <= self.pc
// (*start_pc as i64) <= self.pc)
})
.find(|entry| entry.start_pc as i64 <= self.pc)
.map(|entry| entry.line_number)
}
@ -144,9 +145,9 @@ impl Frame {
Ok(ExecutionResult::Return(())) => return Ok(None),
Ok(ExecutionResult::ReturnValue(val)) => return Ok(Some(val)),
Ok(ExecutionResult::Advance(offset)) => {
info!("pre offset: {}", self.pc);
trace!("pre offset: {}", self.pc);
self.pc += offset as i64;
info!("post offset: {}", self.pc);
trace!("post offset: {}", self.pc);
}
Ok(_) => self.pc += 1,
Err(x) => {
@ -178,12 +179,13 @@ impl Frame {
self.bytecode
.code
.iter()
.find(|(offset, op)| *offset as i64 >= self.pc)
.find(|(offset, _op)| *offset as i64 >= self.pc)
.map(|op| op.clone())
}
}
enum ExecutionResult {
#[derive(Debug)]
pub(crate) enum ExecutionResult {
Continue,
Advance(i32),
Return(()),
@ -194,7 +196,7 @@ impl Frame {
fn pop(&mut self) -> Result<Value, VmError> {
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 {
Ops::nop => {
// TODO Should nop have any side effects?
@ -290,8 +292,6 @@ impl Frame {
};
if let Some(x) = resolved {
self.stack.push(x);
// on second thoughts, i dont think that's right
// self.stack.push(Value::Reference(None));
};
Ok(ExecutionResult::Continue)
}
@ -540,7 +540,7 @@ impl Frame {
Ops::fastore => array_store!(self, Float, Float),
Ops::dastore => array_store!(self, Double, Double),
Ops::aastore => {
let Value::Reference((value)) = self.pop()? else {
let Value::Reference(value) = self.pop()? else {
panic!("Value on stack was not ref")
};
let Value::Primitive(Primitive::Int(index)) = self.pop()? else {
@ -967,7 +967,7 @@ impl Frame {
let x: i32 = match self.pop()? {
Value::Primitive(Primitive::Int(v)) => v,
Value::Primitive(Primitive::Boolean(v)) => {
if v {
if v == 1u8 {
1
} else {
0
@ -985,7 +985,7 @@ impl Frame {
match &self.method_ref.desc.return_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::Char => Ok(ExecutionResult::ReturnValue((x as u16).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 refe = args
.first()
.expect("Must have reciever")
.expect("Must have receiver")
.as_ref_kind()
.expect("Must be ref");
let class = refe.class();
@ -1193,8 +1193,104 @@ impl Frame {
Ok(ExecutionResult::Continue)
}
Ops::invokedynamic(_, _) => {
todo!("invokeDynamic")
Ops::invokedynamic(index, _b) => {
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
@ -1216,11 +1312,11 @@ impl Frame {
let Value::Primitive(Primitive::Int(count)) = value else {
panic!("stack item was not int")
};
let array = self.thread.gc.write().new_primitive_array(
array_class,
array_type.clone(),
count,
);
let array = self
.thread
.gc
.write()
.new_primitive_array(array_class, count);
self.stack
.push(Value::Reference(Some(ReferenceKind::ArrayReference(array))));
Ok(ExecutionResult::Continue)
@ -1246,7 +1342,7 @@ impl Frame {
Ok(ExecutionResult::Continue)
}
Ops::arraylength => {
let Value::Reference(Some(ReferenceKind::ArrayReference((array)))) =
let Value::Reference(Some(ReferenceKind::ArrayReference(array))) =
self.stack.pop().expect("value on stack")
else {
panic!("Reference not on stack or not an array")
@ -1488,3 +1584,314 @@ impl Frame {
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 std::fmt::{Display, Formatter};

View File

@ -16,22 +16,15 @@
//! - [`FieldType`] - Field type information
use crate::class_file::MethodRef;
use crate::class_file::constant_pool::ConstantPoolExt;
use crate::class_file::constant_pool::ConstantPoolGet;
pub use crate::thread::VmThread;
use deku::DekuContainerRead;
use deku_derive::{DekuRead, DekuWrite};
use frame::Frame;
use itertools::Itertools;
use log::error;
use std::borrow::Cow;
use std::cell::RefCell;
use std::fmt::{Debug, Display, Formatter};
use std::io::Read;
use std::ops::{BitAnd, Deref};
use std::str::FromStr;
use value::Value;
mod attributes;
mod bimage;
mod class;
pub mod class_file;
@ -99,6 +92,23 @@ pub enum BaseType {
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 {
fn from(value: char) -> Self {
match value {
@ -136,9 +146,21 @@ impl MethodDescriptor {
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()
}
pub fn psvm() -> Self {
MethodDescriptor::parse("()V").unwrap()
}
pub fn param_string(&self) -> String {
self.parameters.iter().join("")
@ -211,6 +233,12 @@ pub enum FieldType {
ArrayType(Box<FieldType>),
}
impl FieldType {
pub fn bool() -> Self {
Self::Base(BaseType::Boolean)
}
}
impl FieldType {
pub fn default_value(&self) -> Value {
match self {

View File

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

View File

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

View File

@ -1,5 +1,7 @@
#![allow(unused_variables)]
#![allow(unused)]
#![feature(c_variadic)]
#![allow(unsafe_op_in_unsafe_fn)]
use crate::class::RuntimeClass;
use crate::class_file::{FieldData, FieldRef};
use crate::objects::array::ArrayReference;
@ -8,11 +10,10 @@ use crate::prim::jboolean;
use crate::thread::VmThread;
use crate::value::{Primitive, Value};
use crate::{BaseType, FieldType, MethodDescriptor, generate_jni_short_name};
use itertools::Itertools;
use jni::sys::*;
use jni::sys::{JNIEnv, jstring};
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::ptr;
use std::sync::Arc;
@ -318,7 +319,7 @@ unsafe extern "system" fn get_string_utfchars(
_ => return ptr::null(),
};
let mut obj = obj_ref.lock();
let obj = obj_ref.lock();
let field_ref = FieldRef {
class: "java/lang/String".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 ReferenceKind::ObjectReference(obj_ref) = gc.get(obj_id) else {
return ptr::null_mut();
};
match gc.get(obj_id) {
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
// however you get the array's class mirror
*arr_ref.class().mirror.wait() as jclass
}
}
}
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") {
warn!("MAJOR HACK, figure out what is wrong with desiredAssertionStatus0");
// warn!("MAJOR HACK, figure out what is wrong with desiredAssertionStatus0");
continue;
}
@ -430,7 +436,7 @@ unsafe extern "system" fn register_natives(
"name:{name}, signature:{signature}, fn_ptr{}",
fn_ptr.is_null()
);
println!("JNI register: {full_name}");
debug!("JNI register: {full_name}");
}
JNI_OK
}
@ -1067,7 +1073,32 @@ unsafe extern "system" fn get_object_field(
obj: jobject,
field_id: jfieldID,
) -> 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(
@ -1085,8 +1116,8 @@ unsafe extern "system" fn get_boolean_field(
desc: FieldType::Base(BaseType::Boolean),
};
let val = object.lock().get_field(&field_ref);
if let Value::Primitive(Primitive::Boolean(bool)) = val {
if bool { JNI_TRUE } else { JNI_FALSE }
if let Value::Primitive(Primitive::Boolean(bol)) = val {
if bol == 1u8 { JNI_TRUE } else { JNI_FALSE }
} else {
JNI_FALSE
}
@ -1462,8 +1493,8 @@ unsafe extern "system" fn get_static_boolean_field(
let field_ref = &*(field_id as *const FieldData);
let field_again = class.find_field(&field_ref.name, &field_ref.desc).unwrap();
let val = field_again.value.lock().clone();
if let Some(Value::Primitive(Primitive::Boolean(bool))) = val {
if bool { JNI_TRUE } else { JNI_FALSE }
if let Some(Value::Primitive(Primitive::Boolean(bol))) = val {
if bol == JNI_TRUE { JNI_TRUE } else { JNI_FALSE }
} else {
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 {
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(
@ -1963,7 +2002,19 @@ unsafe extern "system" fn get_byte_array_region(
len: jsize,
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(
@ -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 {
warn!("exception_check");
trace!("exception_check");
JNI_FALSE
}

View File

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

View File

@ -1,18 +1,15 @@
use crate::attributes::ArrayType;
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::objects::array::{
Array, ArrayReference, ArrayValue, ObjectArrayReference, PrimitiveArrayReference,
};
use crate::objects::array::{Array, ArrayReference};
use crate::objects::object::{
Object, ObjectReference, Reference, ReferenceKind, string_from_bytes,
};
use crate::value::{Primitive, Value};
use crate::{ThreadId, objects, rng};
use crate::value::Value;
use crate::{BaseType, ThreadId};
use dashmap::DashMap;
use jni::sys::{jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jshort};
use log::warn;
use jni::sys::{jint, jlong};
use parking_lot::{Condvar, Mutex};
use std::collections::HashMap;
use std::sync::Arc;
@ -78,41 +75,45 @@ impl ObjectManager {
object
}
pub fn new_primitive_array(
&mut self,
class: Arc<RuntimeClass>,
array_type: ArrayType,
count: i32,
) -> ArrayReference {
pub fn new_primitive_array(&mut self, class: Arc<RuntimeClass>, count: i32) -> ArrayReference {
let id = self.next_id();
assert!(
!self.objects.contains_key(&id),
"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 {
ArrayType::T_INT => {
BaseType::Int => {
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))))
}
ArrayType::T_SHORT => {
BaseType::Short => {
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))))
}
ArrayType::T_FLOAT => {
BaseType::Float => {
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))))
}
ArrayType::T_CHAR => {
BaseType::Char => {
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))))
}
};
@ -158,7 +159,6 @@ impl ObjectManager {
class: Arc<RuntimeClass>,
vector: Box<[i8]>,
) -> ArrayReference {
warn!("Manual sidechannel byte array creation");
let id = self.next_id();
assert!(
!self.objects.contains_key(&id),
@ -198,7 +198,6 @@ impl ObjectManager {
string_class: Arc<RuntimeClass>,
utf8: &str,
) -> ObjectReference {
warn!("Manual sidechannel string creation: \n\"{}\"", utf8);
let key = utf8.to_owned();
let jstr = self.new_object(string_class);
let byte_vec = utf8
@ -227,6 +226,9 @@ impl ObjectManager {
self.strings.insert(key, id);
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(
&mut self,
@ -235,8 +237,14 @@ impl ObjectManager {
module: Reference,
modifiers: ClassFlags,
primitive: bool,
component_type: Reference,
) -> 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 modifiers = 17u16;
@ -249,6 +257,7 @@ impl ObjectManager {
clakz.set_field("modifiers", Value::from(modifiers));
clakz.set_field("primitive", Value::from(primitive));
clakz.set_field("classRedefinedCount", Value::from(class_redefined_count));
clakz.set_field("componentType", Value::from(component_type))
}
clazz
}
@ -639,4 +648,56 @@ impl ObjectManager {
}
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::sync::atomic::{AtomicU32, Ordering};

View File

@ -1,5 +1,5 @@
use crate::class::{InitState, RuntimeClass};
use crate::class_file::{ClassFile, MethodData, MethodRef};
use crate::class::{ClassRef, InitState, RuntimeClass};
use crate::class_file::{MethodData, MethodRef};
use crate::class_loader::{ClassLoader, LoaderRef};
use crate::error::VmError;
use crate::frame::Frame;
@ -10,26 +10,19 @@ use crate::value::{Primitive, Value};
use crate::vm::Vm;
use crate::{
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 libffi::low::call;
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::cell::RefCell;
use std::collections::VecDeque;
use std::ops::{Add, Deref};
use std::ptr::null_mut;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::atomic::Ordering;
use std::sync::{Arc, OnceLock};
use std::thread;
use std::vec::IntoIter;
static INIT_LOGGER: Once = Once::new();
type MethodCallResult = Result<Option<Value>, VmError>;
@ -55,7 +48,7 @@ pub struct VmThread {
impl VmThread {
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 gc = vm.gc.clone();
@ -141,7 +134,7 @@ impl VmThread {
);
return Ok(());
}
InitState::Initializing(tid) => {
InitState::Initializing(_tid) => {
// Different thread is initializing - in a real JVM we'd wait
// For now, just return an error
return Err(VmError::LoaderError(format!(
@ -203,9 +196,6 @@ impl VmThread {
pub fn ensure_initialised(&self, class: &Arc<RuntimeClass>) -> Result<(), VmError> {
let current_thread = thread::current().id();
if class.this_class.contains("Thread") {
println!("Yep");
}
{
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 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![])?;
}
@ -285,12 +275,19 @@ impl VmThread {
self.get_class("java/lang/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(
class_class,
Some(ReferenceKind::ObjectReference(string)),
None,
class.access_flags,
false,
component_type,
);
let id = class_obj.lock().id;
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 {
if self.gc.read().objects.len() > 2387 {
if self.gc.read().objects.len() > 2350 {
INIT_LOGGER.call_once(|| {
env_logger::Builder::from_default_env()
.filter_level(LevelFilter::Trace)
@ -340,19 +337,13 @@ impl VmThread {
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);
if symbol_name.contains("Java_java_lang_Thread_start0") {
// return Err(VmError::Debug(
// "RoastVM specific implementation required for Java_java_lang_Thread_start0",
// ));
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)));
if symbol_name.contains("Java_java_lang_reflect_Array_newArray") {
return Err(VmError::Debug(
"RoastVM specific implementation required for Java_java_lang_reflect_Array_newArray",
));
}
let result = unsafe {
@ -368,7 +359,7 @@ impl VmThread {
)))?;
// build pointer to native fn
let cp = CodePtr::from_ptr(p);
println!("invoke native fn: {}", symbol_name);
let mut storage = Vec::new();
trace!("passing {} to native fn", Value::format_vec(&args));
let deq_args = VecDeque::from(args);
@ -433,7 +424,6 @@ impl VmThread {
}
}
};
warn!("Invoke native not final");
result
}
fn execute_method(
@ -442,8 +432,7 @@ impl VmThread {
method: &MethodData,
args: Vec<Value>,
) -> MethodCallResult {
info!("Executing {}", method.name.clone());
warn!("Stack used: {}", stack_used());
debug!("Executing {}", method.name.clone());
let method_ref = MethodRef {
class: class.this_class.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>(

View File

@ -2,13 +2,10 @@ use crate::error::VmError;
use crate::objects::array::ArrayReference;
use crate::objects::object::{ObjectReference, ReferenceKind};
use crate::{BaseType, FieldType};
use core::fmt;
use dashmap::DashMap;
use jni::sys::{jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jshort};
use jni::sys::{JNI_FALSE, JNI_TRUE, jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jshort};
use log::trace;
use std::fmt::{Display, Formatter};
use std::mem::{size_of, size_of_val};
use std::ops::Deref;
/// A reference-counted, thread-safe pointer to an Object.
@ -25,135 +22,6 @@ pub enum Value {
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 {
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 {
@ -251,13 +129,13 @@ impl_value_from!(jbyte, Byte);
impl_value_from!(jshort, Short);
impl_value_from!(jchar, Char);
impl From<jboolean> for Value {
fn from(value: jboolean) -> Self {
Self::Primitive(Primitive::Boolean(value != 0))
impl From<bool> for Value {
fn from(value: bool) -> Self {
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 {
fn from(value: Option<ReferenceKind>) -> Self {
@ -284,7 +162,7 @@ impl From<ArrayReference> for Value {
#[derive(Debug, Clone)]
pub enum Primitive {
/// Boolean value (true/false)
Boolean(bool),
Boolean(jboolean),
/// Unicode character
Char(u16),
/// 32-bit floating point
@ -391,31 +269,31 @@ impl PartialEq<FieldType> for Value {
}
}
ReferenceKind::ArrayReference(x) => match x {
ArrayReference::Int(x) => {
ArrayReference::Int(_x) => {
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)))
}
ArrayReference::Short(x) => {
ArrayReference::Short(_x) => {
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)))
}
ArrayReference::Float(x) => {
ArrayReference::Float(_x) => {
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)))
}
ArrayReference::Char(x) => {
ArrayReference::Char(_x) => {
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)))
}
ArrayReference::Object(x) => {
ArrayReference::Object(_x) => {
if let FieldType::ArrayType(inner) = other {
if let FieldType::ClassType(_) = **inner {
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;
// the implementation used here doesn't matter particularly much...
#[cfg(all(not(libloading_docs), unix))]
#[cfg(all(unix))]
use libloading::os::unix as imp;
#[cfg(all(not(libloading_docs), windows))]
#[cfg(all(windows))]
use libloading::os::windows as imp;
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_manager::ObjectManager;
use crate::thread::VmThread;
use crate::value::Value;
use crate::{MethodDescriptor, ThreadId};
use dashmap::DashMap;
use imp::Library;
use log::trace;
use log::{info, trace};
use parking_lot::{Mutex, RwLock};
use std::sync::atomic::AtomicU64;
use std::sync::{Arc, LazyLock};
use std::time::Instant;
type NativeLibraries = Arc<RwLock<Vec<(String, Library)>>>;
@ -36,12 +35,38 @@ pub struct Vm {
pub native_libraries: NativeLibraries,
pub gc: Arc<RwLock<ObjectManager>>,
pub safent: RwLock<UnsafeSupport>,
pub NEXT_ID: AtomicU64,
pub next_id: AtomicU64,
}
impl Vm {
// start vm, loading main from classfile
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 {
threads: DashMap::new(),
main_thread_id: ThreadId(0),
@ -50,7 +75,7 @@ impl Vm {
native_libraries: Arc::new(RwLock::new(Vec::new())),
gc: Default::default(),
safent: Default::default(),
NEXT_ID: AtomicU64::new(0),
next_id: AtomicU64::new(0),
});
// Create main thread
@ -78,7 +103,7 @@ impl Vm {
for (lib_name, lib) in self.native_libraries.read().iter() {
let res = unsafe { lib.get::<unsafe extern "system" fn()>(name.as_bytes()) };
if res.is_ok() {
println!(
trace!(
"Register native for native symbol: {} out of: {}",
&name, lib_name
);
@ -127,7 +152,7 @@ impl Vm {
for prim in prims {
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 flags = ClassFlags::from(1041u16);
let class_obj = self.gc.write().new_class(
@ -136,8 +161,9 @@ impl Vm {
None,
flags,
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 arr_name_obj = thread.intern_string(&prim_array_klass.this_class);
@ -147,6 +173,7 @@ impl Vm {
None,
flags,
false,
Some(ReferenceKind::ObjectReference(class_obj)),
);
prim_array_klass
.mirror
@ -175,7 +202,11 @@ impl Vm {
}
pub fn run(&self, what: &str) -> Result<(), VmError> {
let start = Instant::now();
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
let thread = self.threads.get(&self.main_thread_id).unwrap().clone();
thread.invoke_main(what)

View File

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

View File

@ -1,11 +1,11 @@
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::{jobjectArray, jstring};
use log::trace;
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::{Reference, ReferenceKind};
use roast_vm_core::value::Value;
use roast_vm_core::{BaseType, FieldType};
use std::ptr;
@ -19,40 +19,13 @@ pub unsafe extern "system" fn Java_java_lang_Class_getPrimitiveClass<'local>(
let thread = &*get_thread(env);
let rust_string = {
let gc = thread.gc.read();
let obj_id = name as u32;
let obj_ref = match gc.get(obj_id) {
let obj_ref = match thread.gc.read().get(obj_id) {
ReferenceKind::ObjectReference(obj) => obj,
_ => return 0 as jclass,
};
let obj = obj_ref.lock();
let field_ref = FieldRef {
class: "java/lang/String".to_string(),
name: "value".to_string(),
desc: FieldType::ArrayType(Box::new(FieldType::Base(BaseType::Byte))),
};
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
thread.gc.write().transmute_string(obj_ref).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,
name: jstring,
initialize: jboolean,
loader: jobject,
caller: jclass,
_loader: jobject,
_caller: jclass,
) -> jclass {
trace!("Java_java_lang_Class_forName0");
let thread = &*get_thread(env);
@ -152,3 +125,12 @@ pub unsafe extern "system" fn Java_java_lang_Class_getDeclaredConstructors0(
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(unsafe_op_in_unsafe_fn)]
mod CDS;
mod class;
mod file_output_stream;
mod misc_unsafe;
mod object;
mod reflect;
mod reflection;
mod runtime;
mod scoped_memory_access;
mod signal;
mod string;
mod system;
mod system_props;
mod thread;
use jni::objects::{JClass, JObject, JString};
use jni::strings::JNIString;
use jni::sys::{jclass, jlong, jobject, jobjectArray};
use jni::{JNIEnv, NativeMethod};
use jni::JNIEnv;
use jni::objects::{JClass, JString};
use jni::sys::{jlong, jobject};
use roast_vm_core::VmThread;
use roast_vm_core::objects::ReferenceKind;
use roast_vm_core::objects::array::ArrayReference;
use roast_vm_core::objects::object::ObjectReference;
use std::ffi::c_void;
use std::io::Write;
use std::time::{SystemTime, UNIX_EPOCH};
#[unsafe(no_mangle)]
pub extern "system" fn Java_org_example_MockIO_print<'local>(
mut env: JNIEnv<'local>,
jclass: JClass<'local>,
env: JNIEnv<'local>,
_jclass: JClass<'local>,
input: JString<'local>,
) {
unsafe {
@ -44,8 +45,8 @@ pub extern "system" fn Java_org_example_MockIO_print<'local>(
#[unsafe(no_mangle)]
pub extern "system" fn Java_org_example_Main_getTime<'local>(
mut env: JNIEnv<'local>,
jclass: JClass<'local>,
_env: JNIEnv<'local>,
_jclass: JClass<'local>,
) -> jlong {
SystemTime::now()
.duration_since(UNIX_EPOCH)
@ -55,8 +56,8 @@ pub extern "system" fn Java_org_example_Main_getTime<'local>(
#[unsafe(no_mangle)]
pub extern "system" fn Java_org_example_MockIO_println<'local>(
mut env: JNIEnv<'local>,
jclass: JClass<'local>,
_env: JNIEnv<'local>,
_jclass: JClass<'local>,
input: jlong,
) {
// let input: String = env

View File

@ -1,18 +1,16 @@
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 log::warn;
use roast_vm_core::native::r#unsafe::OffsetKind;
use roast_vm_core::objects::ReferenceKind;
use roast_vm_core::objects::array::ArrayReference;
use roast_vm_core::objects::object::string_from_bytes;
use roast_vm_core::value::Value;
use std::sync::atomic::Ordering;
#[unsafe(no_mangle)]
pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_registerNatives(
env: *mut JNIEnv,
obj: jclass,
_env: *mut JNIEnv,
_obj: jclass,
) {
//no op
warn!("Unsafe_registerNatives is NO OP")
@ -151,7 +149,7 @@ pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_compareAndSetReferen
.read()
.resolve_field(offset)
.expect("unregistered field offset");
let mut guard = obj_ref.lock();
let guard = obj_ref.lock();
let current_id = guard
.fields
@ -214,7 +212,7 @@ pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_compareAndSetInt(
.read()
.resolve_field(offset)
.expect("unregistered field offset");
let mut guard = obj_ref.lock();
let guard = obj_ref.lock();
let current = guard
.fields
.get(&key.field_name)
@ -243,7 +241,7 @@ pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_compareAndSetLong(
) -> jboolean {
let thread = &*get_thread(env);
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,
new_val as u64,
Ordering::SeqCst,
@ -277,7 +275,7 @@ pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_compareAndSetLong(
.read()
.resolve_field(offset)
.expect("unregistered field offset");
let mut guard = obj_ref.lock();
let guard = obj_ref.lock();
let current = guard
.fields
.get(&key.field_name)
@ -328,7 +326,7 @@ pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_compareAndExchangeIn
.read()
.resolve_field(offset)
.expect("unregistered field offset");
let mut guard = obj_ref.lock();
let guard = obj_ref.lock();
let current = guard
.fields
.get(&key.field_name)
@ -377,7 +375,7 @@ pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_compareAndExchangeLo
.read()
.resolve_field(offset)
.expect("unregistered field offset");
let mut guard = obj_ref.lock();
let guard = obj_ref.lock();
let current = guard
.fields
.get(&key.field_name)
@ -440,7 +438,7 @@ pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_compareAndExchangeRe
.read()
.resolve_field(offset)
.expect("unregistered field offset");
let mut guard = obj_ref.lock();
let guard = obj_ref.lock();
let current = guard
.fields
@ -594,7 +592,7 @@ pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_getLongVolatile(
) -> jlong {
let thread = &*get_thread(env);
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);

View File

@ -3,7 +3,7 @@ use jni::sys::{JNIEnv, jint, jlong, jobject};
#[unsafe(no_mangle)]
pub unsafe extern "system" fn Java_java_lang_Object_hashCode(
env: *mut JNIEnv,
_env: *mut JNIEnv,
obj: jobject,
) -> jint {
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 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::objects::array::ArrayReference;
use roast_vm_core::value::Value;
use roast_vm_core::{BaseType, FieldType, MethodDescriptor};
use roast_vm_core::{BaseType, FieldType};
#[unsafe(no_mangle)]
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)]
pub unsafe extern "system" fn Java_jdk_internal_reflect_Reflection_getClassAccessFlags(
env: *mut JNIEnv,
reflection_class: jclass,
_reflection_class: jclass,
class: jclass,
) -> jint {
let thread = &*get_thread(env);
@ -38,7 +38,7 @@ pub unsafe extern "system" fn Java_jdk_internal_reflect_Reflection_getClassAcces
#[unsafe(no_mangle)]
pub unsafe extern "system" fn Java_jdk_internal_reflect_DirectConstructorHandleAccessor_00024NativeAccessor_newInstance0(
env: *mut JNIEnv,
DirectConstructorHandleAccessor_class: jclass,
_DirectConstructorHandleAccessor_class: jclass,
constructor: jobject,
args: jobjectArray,
) -> jobject {

View File

@ -2,7 +2,7 @@ use jni::sys::{JNIEnv, jclass};
#[unsafe(no_mangle)]
pub unsafe extern "system" fn Java_jdk_internal_misc_ScopedMemoryAccess_registerNatives(
env: *mut JNIEnv,
_env: *mut JNIEnv,
_class: jclass,
) {
// 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::sys::{JNIEnv, jclass, jint, jlong, jobject};
use log::warn;
use std::time::{SystemTime, UNIX_EPOCH};
#[unsafe(no_mangle)]
@ -50,7 +49,6 @@ pub unsafe extern "system" fn Java_java_lang_System_arraycopy(
{
panic!("Array index out of bounds!");
// throw_exception(env, "java/lang/ArrayIndexOutOfBoundsException", None);
return;
}
dst_arr
@ -61,8 +59,8 @@ pub unsafe extern "system" fn Java_java_lang_System_arraycopy(
#[unsafe(no_mangle)]
pub extern "system" fn current_time_millis<'local>(
mut env: jni::JNIEnv<'local>,
jclass: JClass<'local>,
_env: jni::JNIEnv<'local>,
_jclass: JClass<'local>,
) -> jlong {
SystemTime::now()
.duration_since(UNIX_EPOCH)
@ -72,8 +70,8 @@ pub extern "system" fn current_time_millis<'local>(
#[unsafe(no_mangle)]
pub extern "system" fn Java_java_lang_System_nanoTime<'local>(
mut env: jni::JNIEnv<'local>,
jclass: JClass<'local>,
_env: jni::JNIEnv<'local>,
_jclass: JClass<'local>,
) -> jlong {
SystemTime::now()
.duration_since(UNIX_EPOCH)
@ -83,8 +81,8 @@ pub extern "system" fn Java_java_lang_System_nanoTime<'local>(
#[unsafe(no_mangle)]
pub extern "system" fn Java_java_lang_System_registerNatives<'local>(
mut env: jni::JNIEnv<'local>,
jclass: JClass<'local>,
_env: jni::JNIEnv<'local>,
_jclass: JClass<'local>,
) {
// 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);
// 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 {
class: "java/lang/Thread".to_string(),
name: "run".to_string(),