Various changes, staged
This commit is contained in:
parent
f00cfadb76
commit
7fcf00b77f
@ -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| {
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
@ -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()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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!(),
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
|
||||||
|
|||||||
@ -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());
|
||||||
|
|||||||
@ -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(()))));
|
||||||
|
}
|
||||||
|
}
|
||||||
115
crates/core/src/frame/local_vars.rs
Normal file
115
crates/core/src/frame/local_vars.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
5
crates/core/src/frame/mod.rs
Normal file
5
crates/core/src/frame/mod.rs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
pub mod frame;
|
||||||
|
mod local_vars;
|
||||||
|
mod operand_stack;
|
||||||
|
|
||||||
|
pub use frame::*;
|
||||||
119
crates/core/src/frame/operand_stack.rs
Normal file
119
crates/core/src/frame/operand_stack.rs
Normal 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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};
|
||||||
|
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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) => {{
|
||||||
|
|||||||
@ -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:
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,2 @@
|
|||||||
pub mod jni;
|
pub mod jni;
|
||||||
mod native_libraries;
|
pub mod r#unsafe;
|
||||||
pub mod r#unsafe;
|
|
||||||
|
|||||||
@ -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
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
@ -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;
|
||||||
|
|||||||
@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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};
|
||||||
|
|
||||||
|
|||||||
@ -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>(
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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(
|
||||||
|
|||||||
@ -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
|
||||||
|
}
|
||||||
|
|||||||
102
crates/roast-vm-sys/src/file_output_stream.rs
Normal file
102
crates/roast-vm-sys/src/file_output_stream.rs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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
|
||||||
|
|||||||
@ -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);
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
38
crates/roast-vm-sys/src/reflect/array.rs
Normal file
38
crates/roast-vm-sys/src/reflect/array.rs
Normal 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
|
||||||
|
}
|
||||||
1
crates/roast-vm-sys/src/reflect/mod.rs
Normal file
1
crates/roast-vm-sys/src/reflect/mod.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
pub mod array;
|
||||||
@ -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 {
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
21
crates/roast-vm-sys/src/string.rs
Normal file
21
crates/roast-vm-sys/src/string.rs
Normal 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
|
||||||
|
}
|
||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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(),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user