Stack Traces
This commit is contained in:
parent
707b961903
commit
bf259da7c9
@ -15,6 +15,7 @@ libloading = { workspace = true }
|
||||
libffi = { workspace = true }
|
||||
jni = { workspace = true }
|
||||
itertools = { workspace = true }
|
||||
colored = "3.0.0"
|
||||
|
||||
[build-dependencies]
|
||||
bindgen = "0.72.1"
|
||||
|
||||
@ -3,7 +3,7 @@ use crate::class_file::{ClassFile, Constant, ConstantPoolEntry};
|
||||
use deku::DekuContainerRead;
|
||||
use deku_derive::DekuRead;
|
||||
use log::trace;
|
||||
use std::fmt::Display;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::ops::Deref;
|
||||
|
||||
#[derive(Clone, PartialEq, Debug, DekuRead)]
|
||||
@ -60,6 +60,22 @@ pub enum ArrayType {
|
||||
T_LONG,
|
||||
}
|
||||
|
||||
impl Display for ArrayType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let str = match self {
|
||||
ArrayType::T_BOOLEAN => "[Z",
|
||||
ArrayType::T_CHAR => "[C",
|
||||
ArrayType::T_FLOAT => "[F",
|
||||
ArrayType::T_DOUBLE => "[D",
|
||||
ArrayType::T_BYTE => "[B",
|
||||
ArrayType::T_SHORT => "[S",
|
||||
ArrayType::T_INT => "[I",
|
||||
ArrayType::T_LONG => "[J",
|
||||
};
|
||||
write!(f, "{}", str)
|
||||
}
|
||||
}
|
||||
|
||||
// impl TryFrom<u8> for Ops {
|
||||
// type Error = ();
|
||||
//
|
||||
|
||||
@ -79,7 +79,7 @@ impl Bimage {
|
||||
slashes.replace("/", ".")
|
||||
}
|
||||
|
||||
pub fn get_class(&mut self, module: &str, class: &str) -> Option<Vec<u8>> {
|
||||
pub fn get_class(&mut self, module: &str, class: &str) -> Result<Vec<u8>, String> {
|
||||
// trace!("Modules{:#?}", self.modules);
|
||||
|
||||
let path = Self::resolve_path(module, class);
|
||||
@ -87,7 +87,7 @@ impl Bimage {
|
||||
.read_file(&path)
|
||||
.map_err(|e| {
|
||||
log::trace!("Class not found {}", path);
|
||||
e.to_string()
|
||||
})
|
||||
.ok()
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,11 +3,12 @@ use crate::class_file::{
|
||||
ClassFile, ClassFlags, ConstantPoolEntry, FieldData, FieldInfo, FieldRef, MethodData,
|
||||
MethodInfo, MethodRef,
|
||||
};
|
||||
use crate::{FieldType, MethodDescriptor, VmError};
|
||||
use crate::{FieldType, MethodDescriptor};
|
||||
use log::trace;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::sync::{Arc, Mutex, OnceLock, OnceState};
|
||||
use std::thread::ThreadId;
|
||||
use crate::error::VmError;
|
||||
|
||||
/// JVM Spec 5.5: Initialization states for a class
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
@ -37,6 +38,7 @@ pub struct RuntimeClass {
|
||||
pub super_classes: Vec<Arc<RuntimeClass>>,
|
||||
pub super_interfaces: Vec<Arc<RuntimeClass>>,
|
||||
pub component_type: Option<Arc<RuntimeClass>>,
|
||||
pub source_file: Option<String>,
|
||||
}
|
||||
impl Hash for RuntimeClass {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
@ -98,8 +100,23 @@ impl RuntimeClass {
|
||||
}
|
||||
|
||||
pub fn is_assignable_into(&self, into: Arc<RuntimeClass>) -> bool {
|
||||
self.eq(&*into)
|
||||
|| self.super_classes.contains(&into)
|
||||
|| self.super_interfaces.contains(&into)
|
||||
if self.eq(&*into) || self.super_classes.contains(&into) || self.super_interfaces.contains(&into) {
|
||||
return true;
|
||||
}
|
||||
// Array covariance: both must be arrays, then check component types
|
||||
if let (Some(self_comp), Some(into_comp)) = (&self.component_type, &into.component_type) {
|
||||
// Primitive components must match exactly
|
||||
if self_comp.is_primitive_class() {
|
||||
return self_comp.eq(&*into_comp);
|
||||
}
|
||||
// Reference components: recursive covariance
|
||||
return self_comp.is_assignable_into(into_comp.clone());
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn is_primitive_class(&self) -> bool {
|
||||
matches!(self.this_class.as_str(), "byte"|"char"|"double"|"float"|"int"|"long"|"short"|"boolean")
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use crate::attributes::{Attribute, AttributeInfo, CodeAttribute};
|
||||
use crate::attributes::{Attribute, AttributeInfo, CodeAttribute, LineNumberTableEntry};
|
||||
use crate::class_file::constant_pool::{ConstantPoolError, ConstantPoolExt, ConstantPoolOwned};
|
||||
use crate::instructions::Ops;
|
||||
use crate::value::Value;
|
||||
@ -657,6 +657,7 @@ pub struct MethodData {
|
||||
pub desc: MethodDescriptor,
|
||||
pub code: Option<CodeAttribute>,
|
||||
pub flags: MethodFlags,
|
||||
pub line_number_table: Option<Vec<LineNumberTableEntry>>,
|
||||
// pub exceptions: Option<_>,
|
||||
// pub visible_annotations: Option<_>,
|
||||
// pub invisible_annotations: Option<_>,
|
||||
|
||||
@ -5,16 +5,13 @@ use crate::class_file::{
|
||||
ConstantClassInfo, ConstantDynamicInfo, ConstantFieldrefInfo, ConstantInterfaceMethodrefInfo,
|
||||
ConstantInvokeDynamicInfo, ConstantMethodHandleInfo, ConstantMethodTypeInfo,
|
||||
ConstantMethodrefInfo, ConstantModuleInfo, ConstantNameAndTypeInfo, ConstantPackageInfo,
|
||||
ConstantPoolEntry, ConstantStringInfo, ConstantUtf8Info, DescParseError, FieldInfo, FieldRef,
|
||||
MethodInfo, MethodRef,
|
||||
ConstantPoolEntry, ConstantStringInfo, ConstantUtf8Info, DescParseError, FieldInfo, FieldRef
|
||||
, MethodRef,
|
||||
};
|
||||
use crate::{pool_get_impl, FieldType, MethodDescriptor, VmError};
|
||||
use crate::{pool_get_impl, FieldType, MethodDescriptor};
|
||||
use deku::DekuContainerRead;
|
||||
use log::trace;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::ops::Deref;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
use crate::error::VmError;
|
||||
|
||||
pub type ConstantPoolSlice = [ConstantPoolEntry];
|
||||
pub type ConstantPoolOwned = Vec<ConstantPoolEntry>;
|
||||
@ -100,6 +97,16 @@ pub trait ConstantPoolExt: ConstantPoolGet {
|
||||
Ok(MethodRef { class, name, desc })
|
||||
}
|
||||
|
||||
fn resolve_interface_method_ref(&self, index: u16) -> Result<MethodRef, ConstantPoolError> {
|
||||
let mr = self.get_interface_method_ref(index)?;
|
||||
let class = self.resolve_class_name(mr.class_index)?;
|
||||
let name_and_type = self.get_name_and_type_info(mr.name_and_type_index)?;
|
||||
let name = self.get_string(name_and_type.name_index)?;
|
||||
let desc = self.get_string(name_and_type.descriptor_index)?;
|
||||
let desc = MethodDescriptor::parse(&desc)?;
|
||||
Ok(MethodRef { class, name, desc })
|
||||
}
|
||||
|
||||
/*// (name, desc)
|
||||
fn resolve_method_info(&self, method: &MethodInfo) -> Result<MethodData, ConstantPoolError> {
|
||||
let desc = self.get_string(method.descriptor_index)?;
|
||||
|
||||
@ -12,6 +12,7 @@ use std::fs::File;
|
||||
use std::io::Read;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::{Arc, Mutex, OnceLock};
|
||||
use crate::error::VmError;
|
||||
|
||||
pub type LoaderRef = Arc<Mutex<ClassLoader>>;
|
||||
|
||||
@ -141,7 +142,7 @@ impl ClassLoader {
|
||||
&mut self,
|
||||
class_name: &str,
|
||||
loader: LoaderId,
|
||||
) -> Result<Arc<RuntimeClass>, String> {
|
||||
) -> Result<Arc<RuntimeClass>, VmError> {
|
||||
if let Some(class) = self.classes.get(&(class_name.to_string(), loader)) {
|
||||
return Ok(class.clone());
|
||||
}
|
||||
@ -164,14 +165,15 @@ impl ClassLoader {
|
||||
.expect("invalid L descriptor");
|
||||
self.get_or_load(class_name, None)
|
||||
}
|
||||
None => Err("empty component descriptor".to_string()),
|
||||
_ => Err(format!("invalid component descriptor: {}", component_name)),
|
||||
};
|
||||
let component = self.get_or_load(component_name, None)?;
|
||||
None => Err(VmError::LoaderError("empty component descriptor".to_string())),
|
||||
_ => Err(VmError::LoaderError(format!("invalid component descriptor: {}", component_name))),
|
||||
}?;
|
||||
// let component = self.get_or_load(component_name, None)?;
|
||||
let arr_class = self.create_array_class(
|
||||
component,
|
||||
);
|
||||
return Ok(Arc::new(arr_class))
|
||||
self.needs_init.push(arr_class.clone());
|
||||
return Ok(arr_class)
|
||||
}
|
||||
let class = self.load_class(class_name, loader)?;
|
||||
self.needs_init.push(class.clone());
|
||||
@ -182,14 +184,19 @@ impl ClassLoader {
|
||||
self.classes.clone()
|
||||
}*/
|
||||
|
||||
fn load_class(&mut self, what: &str, loader: LoaderId) -> Result<Arc<RuntimeClass>, String> {
|
||||
fn load_class(&mut self, what: &str, loader: LoaderId) -> Result<Arc<RuntimeClass>, VmError> {
|
||||
let (module, class_fqn) = ("", what);
|
||||
let bytes = self
|
||||
.bimage
|
||||
.get_class(module, class_fqn)
|
||||
.unwrap_or_else(|| Self::load_class_from_disk(what));
|
||||
.get_class(module, class_fqn).or_else(|e| Self::load_class_from_disk(what)).map_err(|e1| {
|
||||
let classes = self.classes.iter().map(|x| {
|
||||
x.this_class.clone()
|
||||
}).collect::<Vec<_>>();
|
||||
// println!("{:#?}", classes);
|
||||
VmError::LoaderError(format!("failed to find class: {:?}\n{:#?}", what, classes))
|
||||
})?;
|
||||
let (_, cf) = ClassFile::from_bytes((bytes.as_ref(), 0))
|
||||
.map_err(|e| format!("failed to parse class file: {}", e))?;
|
||||
.map_err(|e| VmError::DekuError(e))?;
|
||||
let runtime = self.runtime_class(cf);
|
||||
let arced = Arc::new(runtime);
|
||||
let option = self
|
||||
@ -201,7 +208,7 @@ impl ClassLoader {
|
||||
Ok(arced)
|
||||
}
|
||||
|
||||
fn load_class_from_disk(what: &str) -> Vec<u8> {
|
||||
fn load_class_from_disk(what: &str) -> Result<Vec<u8>, String> {
|
||||
let class_path = std::env::args()
|
||||
.nth(1)
|
||||
.unwrap_or("./data".to_string())
|
||||
@ -209,10 +216,10 @@ impl ClassLoader {
|
||||
|
||||
let path = format!("{class_path}/{what}.class");
|
||||
log::info!("Loading class from path: {}", path);
|
||||
let mut class_file = File::open(path).unwrap();
|
||||
let mut class_file = File::open(path).map_err(|e| {e.to_string()})?;
|
||||
let mut bytes = Vec::new();
|
||||
class_file.read_to_end(&mut bytes).unwrap();
|
||||
bytes
|
||||
Ok(bytes)
|
||||
}
|
||||
|
||||
fn runtime_class(&mut self, class_file: ClassFile) -> RuntimeClass {
|
||||
@ -309,11 +316,21 @@ impl ClassLoader {
|
||||
None
|
||||
}
|
||||
});
|
||||
let line_number_table = code.as_ref().and_then(|x| {
|
||||
x.attributes.iter().find_map(|attr| {
|
||||
match constant_pool.parse_attribute(attr.clone()).ok()? {
|
||||
Attribute::LineNumberTable(table) => Some(table.line_number_table),
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
MethodData {
|
||||
name,
|
||||
flags,
|
||||
desc,
|
||||
code,
|
||||
line_number_table,
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
@ -339,26 +356,16 @@ impl ClassLoader {
|
||||
.cloned()
|
||||
.filter(|e| e.access_flags.INTERFACE)
|
||||
.collect::<Vec<_>>();
|
||||
// if super_classes.len() > 1 {
|
||||
// println!("Jobs Done");
|
||||
// }
|
||||
// if super_interfaces.len() > interfaces.len() {
|
||||
// let mut super_names = super_interfaces
|
||||
// .iter()
|
||||
// .cloned()
|
||||
// .map(|e| e.this_class.clone())
|
||||
// .collect::<Vec<_>>();
|
||||
// super_names.sort();
|
||||
// println!("sif: {:#?}", super_names);
|
||||
// let mut names = interfaces
|
||||
// .iter()
|
||||
// .cloned()
|
||||
// .map(|e| e.this_class.clone())
|
||||
// .collect::<Vec<_>>();
|
||||
// names.sort();
|
||||
// println!("if: {:#?}", names);
|
||||
// println!("Ready to work");
|
||||
// }
|
||||
|
||||
let source_file = class_file.attributes.iter().find_map(|attr| {
|
||||
match constant_pool.parse_attribute(attr.clone()).ok()? {
|
||||
Attribute::SourceFile(index) => {
|
||||
constant_pool.get_string(index).ok()
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
});
|
||||
|
||||
RuntimeClass {
|
||||
constant_pool,
|
||||
access_flags,
|
||||
@ -372,6 +379,7 @@ impl ClassLoader {
|
||||
super_classes,
|
||||
super_interfaces,
|
||||
component_type: None,
|
||||
source_file,
|
||||
}
|
||||
}
|
||||
// pub fn get_or_create_array_class(class_name: &str) -> RuntimeClass {
|
||||
@ -386,35 +394,52 @@ impl ClassLoader {
|
||||
pub fn create_array_class(
|
||||
&mut self,
|
||||
component: Arc<RuntimeClass>,
|
||||
) -> RuntimeClass {
|
||||
) -> Arc<RuntimeClass> {
|
||||
// let name = format!("[{}", component.descriptor()); // e.g., "[Ljava/lang/String;"
|
||||
let object_class: Arc<RuntimeClass> = self.get_or_load("/java/lang/Object", None).unwrap();
|
||||
let cloneable: Arc<RuntimeClass> = self.get_or_load("/java/lang/Cloneable", None).unwrap();
|
||||
let serializable: Arc<RuntimeClass> = self.get_or_load("/java/io/Serializable", None).unwrap();
|
||||
RuntimeClass {
|
||||
constant_pool: Arc::new(vec![]),
|
||||
access_flags: ClassFlags {
|
||||
MODULE: false,
|
||||
ENUM: false,
|
||||
ANNOTATION: false,
|
||||
SYNTHETIC: false,
|
||||
ABSTRACT: true,
|
||||
INTERFACE: false,
|
||||
SUPER: false,
|
||||
FINAL: true,
|
||||
PUBLIC: true,
|
||||
},
|
||||
this_class: "name".to_string(),
|
||||
super_class: Some(object_class.clone()),
|
||||
interfaces: vec![cloneable.clone(), serializable.clone()],
|
||||
fields: vec![],
|
||||
methods: vec![], // clone() is handled specially
|
||||
mirror: OnceLock::new(),
|
||||
init_state: Mutex::new(InitState::Initialized), // arrays need no <clinit>
|
||||
super_classes: vec![object_class],
|
||||
super_interfaces: vec![cloneable, serializable],
|
||||
component_type: Some(component), // new field
|
||||
}
|
||||
let object_class: Arc<RuntimeClass> = self.get_or_load("java/lang/Object", None).unwrap();
|
||||
let cloneable: Arc<RuntimeClass> = self.get_or_load("java/lang/Cloneable", None).unwrap();
|
||||
let serializable: Arc<RuntimeClass> = self.get_or_load("java/io/Serializable", None).unwrap();
|
||||
let name = match component.this_class.as_str() {
|
||||
"byte" => "[B".to_string(),
|
||||
"char" => "[C".to_string(),
|
||||
"double" => "[D".to_string(),
|
||||
"float" => "[F".to_string(),
|
||||
"int" => "[I".to_string(),
|
||||
"long" => "[J".to_string(),
|
||||
"short" => "[S".to_string(),
|
||||
"boolean" => "[Z".to_string(),
|
||||
s if s.starts_with('[') => format!("[{}", s), // nested array
|
||||
s => format!("[L{};", s), // object class
|
||||
};
|
||||
let klass =
|
||||
RuntimeClass {
|
||||
constant_pool: Arc::new(vec![]),
|
||||
access_flags: ClassFlags {
|
||||
MODULE: false,
|
||||
ENUM: false,
|
||||
ANNOTATION: false,
|
||||
SYNTHETIC: false,
|
||||
ABSTRACT: true,
|
||||
INTERFACE: false,
|
||||
SUPER: false,
|
||||
FINAL: true,
|
||||
PUBLIC: true,
|
||||
},
|
||||
this_class: name.clone(),
|
||||
super_class: Some(object_class.clone()),
|
||||
interfaces: vec![cloneable.clone(), serializable.clone()],
|
||||
fields: vec![],
|
||||
methods: vec![], // clone() is handled specially
|
||||
mirror: OnceLock::new(),
|
||||
init_state: Mutex::new(InitState::NotInitialized), // arrays need no <clinit>
|
||||
super_classes: vec![object_class],
|
||||
super_interfaces: vec![cloneable, serializable],
|
||||
component_type: Some(component), // new field
|
||||
source_file: None,
|
||||
};
|
||||
let klass = Arc::new(klass);
|
||||
self.classes.insert((name.to_string(), None), klass.clone());
|
||||
klass
|
||||
}
|
||||
|
||||
pub fn primitive_class(&mut self, name: &str) -> Arc<RuntimeClass> {
|
||||
@ -449,6 +474,7 @@ impl ClassLoader {
|
||||
super_classes: vec![],
|
||||
super_interfaces: vec![],
|
||||
component_type: None,
|
||||
source_file: None,
|
||||
});
|
||||
|
||||
self.classes.insert((name.to_string(), None), klass.clone());
|
||||
|
||||
73
crates/core/src/error.rs
Normal file
73
crates/core/src/error.rs
Normal file
@ -0,0 +1,73 @@
|
||||
use std::fmt::{Display, Formatter};
|
||||
use deku::DekuError;
|
||||
use crate::class_file::constant_pool::ConstantPoolError;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum VmError {
|
||||
ConstantPoolError(String),
|
||||
StackError(String),
|
||||
InvariantError(String),
|
||||
DekuError(DekuError),
|
||||
LoaderError(String),
|
||||
ExecutionError,
|
||||
NativeError(String),
|
||||
Exception {
|
||||
message: String,
|
||||
stack_trace: Vec<StackTraceElement>,
|
||||
},
|
||||
}
|
||||
|
||||
impl VmError {
|
||||
pub fn stack_not_int() -> Self {
|
||||
Self::InvariantError("Value on stack was not an int".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for VmError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
VmError::ConstantPoolError(msg) => write!(f, "Constant pool error: {}", msg),
|
||||
VmError::StackError(msg) => write!(f, "Stack error: {}", msg),
|
||||
VmError::InvariantError(msg) => write!(f, "Invariant error: {}", msg),
|
||||
VmError::DekuError(err) => write!(f, "Deku error: {}", err),
|
||||
VmError::LoaderError(msg) => write!(f, "Loader error: {}", msg),
|
||||
VmError::ExecutionError => write!(f, "Execution error"),
|
||||
VmError::NativeError(msg) => write!(f, "Native error {msg}"),
|
||||
_ => { write!(f, "idk lol") }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ConstantPoolError> for VmError {
|
||||
fn from(value: ConstantPoolError) -> Self {
|
||||
Self::ConstantPoolError(value.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DekuError> for VmError {
|
||||
fn from(value: DekuError) -> Self {
|
||||
Self::DekuError(value)
|
||||
}
|
||||
}
|
||||
#[derive(Debug)]
|
||||
pub struct StackTraceElement {
|
||||
pub class: String,
|
||||
pub method: String,
|
||||
pub file: Option<String>,
|
||||
pub line: Option<u16>,
|
||||
}
|
||||
|
||||
impl VmError {
|
||||
pub(crate) fn with_frame(self, elem: StackTraceElement) -> Self {
|
||||
match self {
|
||||
VmError::Exception { message, mut stack_trace } => {
|
||||
stack_trace.push(elem);
|
||||
VmError::Exception { message, stack_trace }
|
||||
}
|
||||
other => VmError::Exception {
|
||||
message: format!("{}", other),
|
||||
stack_trace: vec![elem],
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,17 +1,20 @@
|
||||
#![allow(unused_variables)]
|
||||
#![feature(c_variadic)]
|
||||
use crate::class::RuntimeClass;
|
||||
use crate::objects::array::ArrayReference;
|
||||
use crate::objects::object_manager::ObjectManager;
|
||||
use crate::prim::jboolean;
|
||||
use crate::thread::VmThread;
|
||||
use crate::value::{Primitive, Value};
|
||||
use jni::objects::JClass;
|
||||
use jni::sys::{jclass, jint, jobject, JNINativeInterface_};
|
||||
use jni::sys::{jstring, JNIEnv};
|
||||
use std::ffi::{c_char, CStr, CString};
|
||||
use std::mem;
|
||||
use std::ptr;
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
use std::sync::{Arc};
|
||||
use crate::class_file::{FieldData, FieldRef};
|
||||
use crate::objects::object::{ ObjectReference, ReferenceKind};
|
||||
use crate::{BaseType, FieldType, MethodDescriptor};
|
||||
use jni::sys::*;
|
||||
use log::{error, info, trace, warn};
|
||||
|
||||
const JNI_VERSION_1_1: jint = 0x00010001;
|
||||
const JNI_VERSION_1_2: jint = 0x00010002;
|
||||
@ -413,11 +416,7 @@ unsafe extern "system" fn register_natives(
|
||||
// JNI FUNCTION STUBS - All unimplemented functions below
|
||||
// ============================================================================
|
||||
|
||||
use crate::class_file::{FieldData, FieldRef};
|
||||
use crate::objects::object::{Object, ObjectReference, ReferenceKind};
|
||||
use crate::{BaseType, FieldType, MethodDescriptor};
|
||||
use jni::sys::*;
|
||||
use log::{error, info, trace, warn};
|
||||
|
||||
|
||||
unsafe extern "system" fn define_class(
|
||||
env: *mut JNIEnv,
|
||||
@ -1643,7 +1642,11 @@ unsafe extern "system" fn new_string_utf(env: *mut JNIEnv, utf: *const c_char) -
|
||||
return ptr::null_mut();
|
||||
};
|
||||
|
||||
let str_ref = thread.gc.write().unwrap().new_string(string_class, str);
|
||||
let Ok(byte_array_class) = thread.get_class("[B") else {
|
||||
return ptr::null_mut();
|
||||
};
|
||||
|
||||
let str_ref = thread.gc.write().unwrap().new_string(byte_array_class, string_class, str);
|
||||
str_ref
|
||||
};
|
||||
|
||||
@ -1687,7 +1690,7 @@ unsafe extern "system" fn new_object_array(
|
||||
return ptr::null_mut();
|
||||
};
|
||||
|
||||
let ArrayReference::Object(arr_ref) = gc.new_object_array(len) else {
|
||||
let ArrayReference::Object(arr_ref) = gc.new_object_array(runtime_class, len) else {
|
||||
return ptr::null_mut();
|
||||
};
|
||||
let arr_id = arr_ref.lock().unwrap().id;
|
||||
|
||||
@ -15,9 +15,10 @@
|
||||
//! - [`MethodDescriptor`] - Method signature information
|
||||
//! - [`FieldType`] - Field type information
|
||||
|
||||
use crate::error::{StackTraceElement, VmError};
|
||||
use crate::value::{OperandStack, Primitive};
|
||||
use crate::value::LocalVariables;
|
||||
use crate::attributes::{ArrayType, Attribute, CodeAttribute};
|
||||
use crate::attributes::{ArrayType, Attribute, CodeAttribute, LineNumberTableEntry};
|
||||
use crate::class_file::constant_pool::ConstantPoolExt;
|
||||
use crate::class_file::constant_pool::{ConstantPoolError, ConstantPoolGet};
|
||||
use crate::class_file::{Bytecode, ClassFile, ConstantPoolEntry, MethodData, MethodRef};
|
||||
@ -36,8 +37,9 @@ use std::fs::File;
|
||||
use std::io::Read;
|
||||
use std::ops::{BitAnd, Deref};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use value::{Value};
|
||||
use value::Value;
|
||||
use vm::Vm;
|
||||
use crate::class::RuntimeClass;
|
||||
|
||||
mod attributes;
|
||||
mod bimage;
|
||||
@ -54,6 +56,7 @@ mod rng;
|
||||
mod thread;
|
||||
pub mod value;
|
||||
pub mod vm;
|
||||
pub mod error;
|
||||
// const NULL: Value = Value::Reference(None);
|
||||
|
||||
// include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
|
||||
@ -177,6 +180,9 @@ struct Frame {
|
||||
thread: Arc<VmThread>,
|
||||
// The mod being invoked
|
||||
method_ref: MethodRef,
|
||||
|
||||
class: Arc<RuntimeClass>,
|
||||
line_number_table: Option<Vec<LineNumberTableEntry>>
|
||||
}
|
||||
|
||||
impl Display for Frame {
|
||||
@ -217,14 +223,29 @@ impl Frame {
|
||||
Value::Padding => {panic!("We we pushing a pad?")}
|
||||
}
|
||||
}
|
||||
fn current_line_number(&self) -> Option<u16> {
|
||||
let table = self.line_number_table.as_ref()?;
|
||||
// Find the entry where start_pc <= current pc
|
||||
table.iter()
|
||||
.rev()
|
||||
.find(|entry| {
|
||||
entry.start_pc as i64 <= self.pc
|
||||
// (*start_pc as i64) <= self.pc)
|
||||
})
|
||||
.map(|entry| {
|
||||
entry.line_number
|
||||
})
|
||||
}
|
||||
|
||||
// fn load_constant(index: u8) {}
|
||||
fn new(
|
||||
class: Arc<RuntimeClass>,
|
||||
method_ref: MethodRef,
|
||||
code_attr: CodeAttribute,
|
||||
pool: Arc<Vec<ConstantPoolEntry>>,
|
||||
mut locals: Vec<Value>,
|
||||
locals: Vec<Value>,
|
||||
vm: Arc<Vm>,
|
||||
line_number_table: Option<Vec<LineNumberTableEntry>>
|
||||
) -> Self {
|
||||
// Get current thread from thread-local storage
|
||||
let thread = VmThread::current(&vm);
|
||||
@ -244,10 +265,12 @@ impl Frame {
|
||||
bytecode,
|
||||
thread,
|
||||
method_ref,
|
||||
class,
|
||||
line_number_table
|
||||
}
|
||||
}
|
||||
fn execute(&mut self) -> Result<Option<Value>, VmError> {
|
||||
let binding = self.bytecode.code.clone();
|
||||
fn execute(&mut self) -> Result<Option<Value>, error::VmError> {
|
||||
let binding = self.bytecode.code.clone();
|
||||
loop {
|
||||
let (offset, op) = self.next().expect("No ops :(");
|
||||
info!("pre set: {}", self.pc);
|
||||
@ -265,14 +288,25 @@ impl Frame {
|
||||
}
|
||||
Ok(_) => self.pc += 1,
|
||||
Err(x) => {
|
||||
return Err(x.with_frame(StackTraceElement {
|
||||
class: self.class.this_class.clone(),
|
||||
method: self.method_ref.name.clone(),
|
||||
file: self.class.source_file.clone(),
|
||||
line: self.current_line_number(),
|
||||
}));
|
||||
eprintln!("[DEBUG] frame.thread.id = {:?}", self.thread.id);
|
||||
eprintln!("[DEBUG] frame.thread.frame_stack.len = {}",
|
||||
self.thread.frame_stack.lock().unwrap().len());
|
||||
let objs = self.thread.gc.read().unwrap()
|
||||
.objects
|
||||
.iter()
|
||||
.map(|(x, y)| format!("{x} : {y}"))
|
||||
.collect::<Vec<_>>();
|
||||
let len = objs.len().clone();
|
||||
error!("Error in method: {:#?}", self.method_ref);
|
||||
error!("Heap dump: len: {len} objs:\n{objs:#?}");
|
||||
error!("Error in method: {:?}", self.method_ref);
|
||||
error!("Error in VM: {x}");
|
||||
self.thread.print_stack_trace();
|
||||
panic!("Mission failed, we'll get em next time:\n{x}")
|
||||
}
|
||||
}
|
||||
@ -290,7 +324,7 @@ impl Frame {
|
||||
.join(", ")
|
||||
);
|
||||
}
|
||||
Err(VmError::ExecutionError)
|
||||
Err(error::VmError::ExecutionError)
|
||||
}
|
||||
|
||||
fn next(&mut self) -> Option<(u16, Ops)> {
|
||||
@ -491,53 +525,12 @@ enum ExecutionResult {
|
||||
Return(()),
|
||||
ReturnValue(Value),
|
||||
}
|
||||
#[derive(Debug)]
|
||||
pub enum VmError {
|
||||
ConstantPoolError(String),
|
||||
StackError(String),
|
||||
InvariantError(String),
|
||||
DekuError(DekuError),
|
||||
LoaderError(String),
|
||||
ExecutionError,
|
||||
NativeError(String),
|
||||
}
|
||||
|
||||
impl VmError {
|
||||
pub fn stack_not_int() -> Self {
|
||||
Self::InvariantError("Value on stack was not an int".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for VmError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
VmError::ConstantPoolError(msg) => write!(f, "Constant pool error: {}", msg),
|
||||
VmError::StackError(msg) => write!(f, "Stack error: {}", msg),
|
||||
VmError::InvariantError(msg) => write!(f, "Invariant error: {}", msg),
|
||||
VmError::DekuError(err) => write!(f, "Deku error: {}", err),
|
||||
VmError::LoaderError(msg) => write!(f, "Loader error: {}", msg),
|
||||
VmError::ExecutionError => write!(f, "Execution error"),
|
||||
VmError::NativeError(msg) => write!(f, "Native error {msg}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ConstantPoolError> for VmError {
|
||||
fn from(value: ConstantPoolError) -> Self {
|
||||
Self::ConstantPoolError(value.to_string())
|
||||
}
|
||||
}
|
||||
impl From<DekuError> for VmError {
|
||||
fn from(value: DekuError) -> Self {
|
||||
Self::DekuError(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl Frame {
|
||||
fn pop(&mut self) -> Result<Value, VmError>{
|
||||
fn pop(&mut self) -> Result<Value, error::VmError>{
|
||||
self.stack.pop()
|
||||
}
|
||||
fn execute_instruction(&mut self, op: Ops) -> Result<ExecutionResult, VmError> {
|
||||
fn execute_instruction(&mut self, op: Ops) -> Result<ExecutionResult, error::VmError> {
|
||||
match op {
|
||||
Ops::nop => {
|
||||
// TODO Should nop have any side effects?
|
||||
@ -758,7 +751,23 @@ impl Frame {
|
||||
Ok(ExecutionResult::Continue)
|
||||
}
|
||||
Ops::baload => {
|
||||
todo!("boolean array load")
|
||||
let Value::Primitive(Primitive::Int(index)) = self.pop()? else {
|
||||
panic!("index on stack was not int")
|
||||
};
|
||||
let arr_ref = self.pop()?;
|
||||
let value = match arr_ref {
|
||||
Value::Reference(Some(ReferenceKind::ArrayReference(ArrayReference::Byte(arr)))) => {
|
||||
let b = arr.lock().unwrap().get(index);
|
||||
Value::from(b as i32)
|
||||
}
|
||||
Value::Reference(Some(ReferenceKind::ArrayReference(ArrayReference::Boolean(arr)))) => {
|
||||
let b = arr.lock().unwrap().get(index);
|
||||
Value::from(b as i32)
|
||||
}
|
||||
_ => panic!("Reference not on stack or not a byte/boolean array"),
|
||||
};
|
||||
self.push(value);
|
||||
Ok(ExecutionResult::Continue)
|
||||
}
|
||||
Ops::caload => {
|
||||
let Value::Primitive(Primitive::Int(index)) = self.pop()? else {
|
||||
@ -771,7 +780,7 @@ impl Frame {
|
||||
panic!("Reference not on stack or not a char array")
|
||||
};
|
||||
let c = arr.lock().unwrap().get(index);
|
||||
self.push(Value::from(c));
|
||||
self.push(Value::from(c as i32));
|
||||
Ok(ExecutionResult::Continue)
|
||||
}
|
||||
Ops::saload => {
|
||||
@ -886,7 +895,7 @@ impl Frame {
|
||||
Ops::pop => {
|
||||
let v1 = self.pop()?;
|
||||
if v1.is_wide() {
|
||||
Err(VmError::InvariantError("Op:pop on single wide value".to_string()))
|
||||
Err(error::VmError::InvariantError("Op:pop on single wide value".to_string()))
|
||||
} else { Ok(ExecutionResult::Continue) }
|
||||
}
|
||||
Ops::pop2 => {
|
||||
@ -898,7 +907,7 @@ impl Frame {
|
||||
let _v2 = self.pop()?;
|
||||
let v1 = self.pop()?;
|
||||
if v1.is_wide() {
|
||||
Err(VmError::InvariantError("Op:pop2 popped a 2wide on second pop".to_string()))
|
||||
Err(error::VmError::InvariantError("Op:pop2 popped a 2wide on second pop".to_string()))
|
||||
} else { Ok(ExecutionResult::Continue) }
|
||||
}
|
||||
}
|
||||
@ -906,7 +915,7 @@ impl Frame {
|
||||
let v1 = self.pop()?;
|
||||
let v2 = self.pop()?;
|
||||
if v1.is_wide() || v2.is_wide() {
|
||||
Err(VmError::InvariantError("dup_x1 operated on 2 wide value".to_string()))
|
||||
Err(error::VmError::InvariantError("dup_x1 operated on 2 wide value".to_string()))
|
||||
} else {
|
||||
self.push(v1.clone());
|
||||
self.push(v2);
|
||||
@ -918,14 +927,14 @@ impl Frame {
|
||||
let v1 = self.pop()?;
|
||||
let v2 = self.pop()?;
|
||||
if v1.is_wide() {
|
||||
Err(VmError::InvariantError("dup_x2 operated on 1st 2 wide value".to_string()))
|
||||
Err(error::VmError::InvariantError("dup_x2 operated on 1st 2 wide value".to_string()))
|
||||
} else if v2.is_wide() {
|
||||
self.push(v1.clone());
|
||||
self.push(v2);
|
||||
self.push(v1);
|
||||
Ok(ExecutionResult::Continue)
|
||||
} else if self.stack.peek()?.is_wide() {
|
||||
Err(VmError::InvariantError("dup_x2 operated on 3rd 2 wide value".to_string()))
|
||||
Err(error::VmError::InvariantError("dup_x2 operated on 3rd 2 wide value".to_string()))
|
||||
}
|
||||
else {
|
||||
let v3 = self.pop()?;
|
||||
@ -941,7 +950,7 @@ impl Frame {
|
||||
self.stack.push(value.clone());
|
||||
Ok(ExecutionResult::Continue)
|
||||
} else {
|
||||
Err(VmError::StackError("Stack underflow".to_string()))
|
||||
Err(error::VmError::StackError("Stack underflow".to_string()))
|
||||
}
|
||||
}
|
||||
Ops::dup2 => {
|
||||
@ -951,7 +960,7 @@ impl Frame {
|
||||
self.push(v1);
|
||||
Ok(ExecutionResult::Continue)
|
||||
} else if self.stack.peek()?.is_wide() {
|
||||
Err(VmError::InvariantError("dup2 operated on 2nd, 2 wide value".to_string()))
|
||||
Err(error::VmError::InvariantError("dup2 operated on 2nd, 2 wide value".to_string()))
|
||||
} else {
|
||||
let v2 = self.pop()?;
|
||||
self.push(v2.clone());
|
||||
@ -968,7 +977,7 @@ impl Frame {
|
||||
// v1 is category 2, v2 must be category 1
|
||||
let v2 = self.pop()?;
|
||||
if v2.is_wide() {
|
||||
Err(VmError::InvariantError("dup2_x1 form 2: v2 must be category 1".to_string()))
|
||||
Err(error::VmError::InvariantError("dup2_x1 form 2: v2 must be category 1".to_string()))
|
||||
} else {
|
||||
self.push(v1.clone());
|
||||
self.push(v2);
|
||||
@ -980,11 +989,11 @@ impl Frame {
|
||||
// all must be category 1
|
||||
let v2 = self.pop()?;
|
||||
if v2.is_wide() {
|
||||
Err(VmError::InvariantError("dup2_x1 form 1: v2 must be category 1".to_string()))
|
||||
Err(error::VmError::InvariantError("dup2_x1 form 1: v2 must be category 1".to_string()))
|
||||
} else {
|
||||
let v3 = self.pop()?;
|
||||
if v3.is_wide() {
|
||||
Err(VmError::InvariantError("dup2_x1 form 1: v3 must be category 1".to_string()))
|
||||
Err(error::VmError::InvariantError("dup2_x1 form 1: v3 must be category 1".to_string()))
|
||||
} else {
|
||||
self.push(v2.clone());
|
||||
self.push(v1.clone());
|
||||
@ -1012,7 +1021,7 @@ impl Frame {
|
||||
// v1 is category 2, v2 and v3 are category 1
|
||||
let v3 = self.pop()?;
|
||||
if v3.is_wide() {
|
||||
Err(VmError::InvariantError("dup2_x2 form 2: v3 must be category 1".to_string()))
|
||||
Err(error::VmError::InvariantError("dup2_x2 form 2: v3 must be category 1".to_string()))
|
||||
} else {
|
||||
self.push(v1.clone());
|
||||
self.push(v3);
|
||||
@ -1023,7 +1032,7 @@ impl Frame {
|
||||
}
|
||||
} else if v2.is_wide() {
|
||||
// v1 category 1, v2 category 2 - no valid form for this
|
||||
Err(VmError::InvariantError("dup2_x2: invalid - v1 cat1, v2 cat2".to_string()))
|
||||
Err(error::VmError::InvariantError("dup2_x2: invalid - v1 cat1, v2 cat2".to_string()))
|
||||
} else {
|
||||
// v1 and v2 are both category 1
|
||||
let v3 = self.pop()?;
|
||||
@ -1041,7 +1050,7 @@ impl Frame {
|
||||
// all category 1
|
||||
let v4 = self.pop()?;
|
||||
if v4.is_wide() {
|
||||
Err(VmError::InvariantError("dup2_x2 form 1: v4 must be category 1".to_string()))
|
||||
Err(error::VmError::InvariantError("dup2_x2 form 1: v4 must be category 1".to_string()))
|
||||
} else {
|
||||
self.push(v2.clone());
|
||||
self.push(v1.clone());
|
||||
@ -1058,7 +1067,7 @@ impl Frame {
|
||||
let v1 = self.pop()?;
|
||||
let v2 = self.pop()?;
|
||||
if v1.is_wide() || v2.is_wide() {
|
||||
Err(VmError::InvariantError("swap operated on 2 wide value".to_string()))
|
||||
Err(error::VmError::InvariantError("swap operated on 2 wide value".to_string()))
|
||||
} else {
|
||||
self.push(v1);
|
||||
self.push(v2);
|
||||
@ -1125,7 +1134,7 @@ impl Frame {
|
||||
self.vars.set(index as usize, new_val.into());
|
||||
Ok(ExecutionResult::Continue)
|
||||
} else {
|
||||
Err(VmError::InvariantError("iinc requires integer value".to_string()))
|
||||
Err(error::VmError::InvariantError("iinc requires integer value".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -1151,10 +1160,10 @@ impl Frame {
|
||||
// Comparisons
|
||||
Ops::lcmp => {
|
||||
let Value::Primitive(Primitive::Long(v2)) = self.pop()? else {
|
||||
return Err(VmError::StackError("Expected long".into()));
|
||||
return Err(error::VmError::StackError("Expected long".into()));
|
||||
};
|
||||
let Value::Primitive(Primitive::Long(v1)) = self.pop()? else {
|
||||
return Err(VmError::StackError("Expected long".into()));
|
||||
return Err(error::VmError::StackError("Expected long".into()));
|
||||
};
|
||||
|
||||
let result: i32 = match v1.cmp(&v2) {
|
||||
@ -1194,7 +1203,7 @@ impl Frame {
|
||||
Ok(ExecutionResult::Continue)
|
||||
}
|
||||
} else {
|
||||
Err(VmError::stack_not_int())
|
||||
Err(error::VmError::stack_not_int())
|
||||
}
|
||||
}
|
||||
Ops::if_acmpne(offset) => {
|
||||
@ -1207,7 +1216,7 @@ impl Frame {
|
||||
Ok(ExecutionResult::Continue)
|
||||
}
|
||||
} else {
|
||||
Err(VmError::stack_not_int())
|
||||
Err(error::VmError::stack_not_int())
|
||||
}
|
||||
}
|
||||
// Control
|
||||
@ -1240,7 +1249,7 @@ impl Frame {
|
||||
Value::Primitive(Primitive::Char(v)) => v as i32,
|
||||
Value::Primitive(Primitive::Short(v)) => v as i32,
|
||||
_ => {
|
||||
return Err(VmError::InvariantError(
|
||||
return Err(error::VmError::InvariantError(
|
||||
"ireturn requires integer-compatible value".to_owned(),
|
||||
))
|
||||
}
|
||||
@ -1253,11 +1262,11 @@ impl Frame {
|
||||
BaseType::Char => Ok(ExecutionResult::ReturnValue((x as u16).into())),
|
||||
BaseType::Short => Ok(ExecutionResult::ReturnValue((x as i16).into())),
|
||||
BaseType::Int => Ok(ExecutionResult::ReturnValue(x.into())),
|
||||
_ => Err(VmError::InvariantError(
|
||||
_ => Err(error::VmError::InvariantError(
|
||||
"wrong return instruction for method".to_owned(),
|
||||
)),
|
||||
},
|
||||
_ => Err(VmError::InvariantError(
|
||||
_ => Err(error::VmError::InvariantError(
|
||||
"wrong return instruction for method".to_owned(),
|
||||
)),
|
||||
}
|
||||
@ -1266,28 +1275,28 @@ impl Frame {
|
||||
let val = self.pop()?;
|
||||
match val {
|
||||
Value::Primitive(Primitive::Long(_)) => Ok(ExecutionResult::ReturnValue(val)),
|
||||
_ => Err(VmError::StackError("Expected reference".into())),
|
||||
_ => Err(error::VmError::StackError("Expected reference".into())),
|
||||
}
|
||||
}
|
||||
Ops::freturn => {
|
||||
let val = self.pop()?;
|
||||
match val {
|
||||
Value::Primitive(Primitive::Float(_)) => Ok(ExecutionResult::ReturnValue(val)),
|
||||
_ => Err(VmError::StackError("Expected reference".into())),
|
||||
_ => Err(error::VmError::StackError("Expected reference".into())),
|
||||
}
|
||||
}
|
||||
Ops::dreturn => {
|
||||
let val = self.pop()?;
|
||||
match val {
|
||||
Value::Primitive(Primitive::Double(_)) => Ok(ExecutionResult::ReturnValue(val)),
|
||||
_ => Err(VmError::StackError("Expected reference".into())),
|
||||
_ => Err(error::VmError::StackError("Expected reference".into())),
|
||||
}
|
||||
}
|
||||
Ops::areturn => {
|
||||
let val = self.pop()?;
|
||||
match val {
|
||||
Value::Reference(_) => Ok(ExecutionResult::ReturnValue(val)),
|
||||
_ => Err(VmError::StackError("Expected reference".into())),
|
||||
_ => Err(error::VmError::StackError("Expected reference".into())),
|
||||
}
|
||||
}
|
||||
Ops::return_void => Ok(ExecutionResult::Return(())),
|
||||
@ -1298,8 +1307,10 @@ impl Frame {
|
||||
// can init the field
|
||||
Ops::getstatic(index) => {
|
||||
let field_ref = self.pool.resolve_field(index)?;
|
||||
if field_ref.class.contains("jdk/internal/misc/VM") || field_ref.name.contains("initLevel") {
|
||||
return Err(VmError::InvariantError("Intentional crash".to_string()))
|
||||
}
|
||||
println!("Getting static field {field_ref:?}");
|
||||
|
||||
let init_class = self
|
||||
.thread
|
||||
.get_or_resolve_class(&field_ref.class)
|
||||
@ -1339,7 +1350,7 @@ impl Frame {
|
||||
let popped = self.pop()?;
|
||||
match popped {
|
||||
Value::Primitive(x) => {
|
||||
Err(VmError::StackError("Getfield era".parse().unwrap()))
|
||||
Err(error::VmError::StackError("Getfield era".parse().unwrap()))
|
||||
}
|
||||
Value::Reference(x) => {
|
||||
match x {
|
||||
@ -1352,7 +1363,7 @@ impl Frame {
|
||||
Ok(ExecutionResult::Continue)
|
||||
}
|
||||
ReferenceKind::ArrayReference(_) => {
|
||||
Err(VmError::StackError("get field".parse().unwrap()))
|
||||
Err(error::VmError::StackError("get field".parse().unwrap()))
|
||||
}
|
||||
},
|
||||
}
|
||||
@ -1393,10 +1404,10 @@ impl Frame {
|
||||
object.lock().unwrap().set_field(&field_ref.name, value);
|
||||
Ok(ExecutionResult::Continue)
|
||||
} else {
|
||||
Err(VmError::StackError("Null pointer exception".to_string()))
|
||||
Err(error::VmError::StackError("Null pointer exception".to_string()))
|
||||
}
|
||||
} else {
|
||||
Err(VmError::StackError(
|
||||
Err(error::VmError::StackError(
|
||||
"putfield tried to operate on a non object stack value".to_string(),
|
||||
))
|
||||
}
|
||||
@ -1405,6 +1416,7 @@ impl Frame {
|
||||
}
|
||||
Ops::invokevirtual(index) => {
|
||||
let method_ref = self.pool.resolve_method_ref(index)?;
|
||||
|
||||
// the 1 represents the receiver
|
||||
let args_count = method_ref.desc.arg_width() + 1;
|
||||
let args = self.stack.pop_n(args_count)?;
|
||||
@ -1412,7 +1424,7 @@ impl Frame {
|
||||
if let Some(val) = result {
|
||||
self.push(val)
|
||||
}
|
||||
// todo!("Finish invoke virtual");
|
||||
|
||||
Ok(ExecutionResult::Continue)
|
||||
}
|
||||
|
||||
@ -1428,7 +1440,7 @@ impl Frame {
|
||||
if let Some(val) = result {
|
||||
self.push(val)
|
||||
}
|
||||
// todo!("invoke special");
|
||||
|
||||
Ok(ExecutionResult::Continue)
|
||||
}
|
||||
|
||||
@ -1446,8 +1458,21 @@ impl Frame {
|
||||
Ok(ExecutionResult::Continue)
|
||||
}
|
||||
|
||||
Ops::invokeinterface(_, _, _) => {
|
||||
todo!("invokeInterface")
|
||||
Ops::invokeinterface(index, count, _zero) => {
|
||||
let method_ref = self.pool.resolve_interface_method_ref(index)?;
|
||||
|
||||
// the 1 represents the receiver
|
||||
let args_count = method_ref.desc.arg_width() + 1;
|
||||
let args = self.stack.pop_n(args_count)?;
|
||||
let refe = args.first().expect("Must have reciever").as_ref_kind().expect("Must be ref");
|
||||
let class = refe.class();
|
||||
|
||||
let result = self.thread.invoke_virtual(method_ref, class.clone(), args)?;
|
||||
if let Some(val) = result {
|
||||
self.push(val)
|
||||
}
|
||||
|
||||
Ok(ExecutionResult::Continue)
|
||||
}
|
||||
|
||||
Ops::invokedynamic(_, _) => {
|
||||
@ -1469,6 +1494,11 @@ impl Frame {
|
||||
}
|
||||
|
||||
Ops::newarray(array_type) => {
|
||||
let array_class = self.thread.get_or_resolve_class(&array_type.to_string())?;
|
||||
|
||||
|
||||
|
||||
|
||||
let value = self.stack.pop().expect("value to have stack");
|
||||
let Value::Primitive(Primitive::Int(count)) = value else {
|
||||
panic!("stack item was not int")
|
||||
@ -1478,7 +1508,7 @@ impl Frame {
|
||||
.gc
|
||||
.write()
|
||||
.unwrap()
|
||||
.new_primitive_array(array_type.clone(), count);
|
||||
.new_primitive_array(array_class, array_type.clone(), count);
|
||||
self.stack
|
||||
.push(Value::Reference(Some(ReferenceKind::ArrayReference(array))));
|
||||
Ok(ExecutionResult::Continue)
|
||||
@ -1486,12 +1516,12 @@ impl Frame {
|
||||
Ops::anewarray(index) => {
|
||||
let class_name = self.pool.resolve_class_name(index)?;
|
||||
println!("{}", class_name);
|
||||
// let array_class = self.thread.loader.get_or_create_array_class(class_name)?;
|
||||
let array_class = self.thread.get_or_resolve_class(&class_name)?;
|
||||
let value = self.stack.pop().expect("value to have stack");
|
||||
let Value::Primitive(Primitive::Int(count)) = value else {
|
||||
panic!("stack item was not int")
|
||||
};
|
||||
let array = self.thread.gc.write().unwrap().new_object_array(count);
|
||||
let array = self.thread.gc.write().unwrap().new_object_array(array_class, count);
|
||||
self.stack
|
||||
.push(Value::Reference(Some(ReferenceKind::ArrayReference(array))));
|
||||
Ok(ExecutionResult::Continue)
|
||||
@ -1516,10 +1546,19 @@ impl Frame {
|
||||
match x {
|
||||
ReferenceKind::ObjectReference(obj) => {
|
||||
if obj.lock().unwrap().class.is_assignable_into(into_class) { self.push(popped); Ok(ExecutionResult::Continue) } else { todo!("Error path") }
|
||||
|
||||
}
|
||||
ReferenceKind::ArrayReference(arr) => {
|
||||
todo!("Arrays")
|
||||
let array_class = arr.class();
|
||||
if array_class.is_assignable_into(into_class.clone()) {
|
||||
self.push(popped);
|
||||
Ok(ExecutionResult::Continue)
|
||||
} else {
|
||||
Err(VmError::Exception {
|
||||
message: format!("ClassCastException: {} cannot be cast to {}",
|
||||
array_class.this_class, into_class.this_class),
|
||||
stack_trace: vec![],
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
} else { self.push(popped); Ok(ExecutionResult::Continue) }
|
||||
@ -1535,7 +1574,13 @@ impl Frame {
|
||||
Ok(ExecutionResult::Continue)
|
||||
}
|
||||
ReferenceKind::ArrayReference(arr) => {
|
||||
todo!("Arrays")
|
||||
let array_class = arr.class();
|
||||
if array_class.is_assignable_into(into_class) {
|
||||
self.push(1i32.into());
|
||||
} else {
|
||||
self.push(0i32.into());
|
||||
}
|
||||
Ok(ExecutionResult::Continue)
|
||||
}
|
||||
}
|
||||
} else { panic!("yeet") }
|
||||
@ -1561,7 +1606,7 @@ impl Frame {
|
||||
Ok(ExecutionResult::Continue)
|
||||
}
|
||||
} else {
|
||||
Err(VmError::stack_not_int())
|
||||
Err(error::VmError::stack_not_int())
|
||||
}
|
||||
}
|
||||
Ops::ifnonnull(offset) => {
|
||||
@ -1572,7 +1617,7 @@ impl Frame {
|
||||
Ok(ExecutionResult::Continue)
|
||||
}
|
||||
} else {
|
||||
Err(VmError::stack_not_int())
|
||||
Err(error::VmError::stack_not_int())
|
||||
}
|
||||
}
|
||||
Ops::goto_w(_) => {
|
||||
@ -1593,7 +1638,7 @@ impl Frame {
|
||||
}
|
||||
}
|
||||
|
||||
fn load_constant(&mut self, index: u16) -> Result<ExecutionResult, VmError> {
|
||||
fn load_constant(&mut self, index: u16) -> Result<ExecutionResult, error::VmError> {
|
||||
let thing = self.pool.get_constant(index.to_owned())?;
|
||||
trace!("\tLoading constant: {}", thing);
|
||||
let resolved = match thing {
|
||||
@ -1602,7 +1647,7 @@ impl Frame {
|
||||
ConstantPoolEntry::Class(x) => {
|
||||
let name = self.pool.get_string(x.name_index)?;
|
||||
let class = self.thread.get_or_resolve_class(&name)?;
|
||||
let class_ref = self.thread.gc.read().unwrap().get(*class.mirror.wait());
|
||||
let class_ref = self.thread.gc.read().unwrap().get(*class.mirror.get().expect(&format!("Mirror unintialised {}", class.this_class)));
|
||||
Value::from(class_ref)
|
||||
}
|
||||
ConstantPoolEntry::String(x) => {
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
use roast_vm_core::vm::Vm;
|
||||
use roast_vm_core::error::VmError;
|
||||
use libloading::Library;
|
||||
use log::LevelFilter;
|
||||
|
||||
use log::{error, LevelFilter};
|
||||
use colored::Colorize;
|
||||
fn main() {
|
||||
env_logger::Builder::from_default_env()
|
||||
.filter_level(LevelFilter::Trace)
|
||||
@ -14,7 +15,42 @@ fn main() {
|
||||
vm.load_native_library("roast_vm.dll", load("roast_vm.dll").into());
|
||||
vm.load_native_library("jvm.dll", load("jvm.dll").into());
|
||||
vm.load_native_library("java.dll", load("java.dll").into());
|
||||
vm.run("org/example/Main");
|
||||
match vm.run("org/example/Main") {
|
||||
Ok(_) => {}
|
||||
Err(VmError::Exception { message, stack_trace }) => {
|
||||
let thread = vm.threads.get(&vm.main_thread_id).unwrap();
|
||||
let objs = thread.gc.read().unwrap()
|
||||
.objects
|
||||
.iter()
|
||||
.map(|(x, y)| format!("{x} : {y}"))
|
||||
.collect::<Vec<_>>();
|
||||
let len = objs.len().clone();
|
||||
error!("Heap dump: len: {len} objs:\n{objs:#?}");
|
||||
eprintln!("{}: {}", "Exception".red().bold(), message);
|
||||
for elem in &stack_trace {
|
||||
let class_name = elem.class.replace('/', ".");
|
||||
let at = "\tat".dimmed();
|
||||
let location = match (&elem.file, elem.line) {
|
||||
(Some(f), Some(l)) => format!("({}:{})", f, l),
|
||||
(Some(f), None) => format!("({})", f),
|
||||
_ => "(Unknown Source)".to_string(),
|
||||
}.blue().dimmed();
|
||||
eprintln!("{} {}.{}{}", at, class_name, elem.method, location);
|
||||
}
|
||||
/*error!("Exception: {}", message);
|
||||
for elem in &stack_trace {
|
||||
let class_name = elem.class.replace('/', ".");
|
||||
match (&elem.file, elem.line) {
|
||||
(Some(f), Some(l)) => eprintln!("\tat {}.{}({}:{})", class_name, elem.method, f, l),
|
||||
(Some(f), None) => eprintln!("\tat {}.{}({})", class_name, elem.method, f),
|
||||
_ => eprintln!("\tat {}.{}(Unknown Source)", class_name, elem.method),
|
||||
}
|
||||
}*/
|
||||
}
|
||||
Err(e) => {
|
||||
error!("VM Error: {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn load(filename: &str) -> Library {
|
||||
|
||||
@ -3,6 +3,8 @@ use crate::prim::Primitive;
|
||||
use jni::sys::{jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jshort};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use crate::class::RuntimeClass;
|
||||
use crate::error::VmError;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ArrayReference {
|
||||
@ -45,6 +47,20 @@ impl ArrayReference {
|
||||
ArrayReference::Object(x) => x.lock().unwrap().id,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn class(&self) -> Arc<RuntimeClass> {
|
||||
match self {
|
||||
ArrayReference::Int(x) => x.lock().unwrap().class.clone(),
|
||||
ArrayReference::Byte(x) => x.lock().unwrap().class.clone(),
|
||||
ArrayReference::Short(x) => x.lock().unwrap().class.clone(),
|
||||
ArrayReference::Long(x) => x.lock().unwrap().class.clone(),
|
||||
ArrayReference::Float(x) => x.lock().unwrap().class.clone(),
|
||||
ArrayReference::Double(x) => x.lock().unwrap().class.clone(),
|
||||
ArrayReference::Char(x) => x.lock().unwrap().class.clone(),
|
||||
ArrayReference::Boolean(x) => x.lock().unwrap().class.clone(),
|
||||
ArrayReference::Object(x) => x.lock().unwrap().class.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type PrimitiveArrayReference<T: Primitive> = Arc<Mutex<Array<T>>>;
|
||||
@ -54,6 +70,7 @@ pub type ObjectArrayReference = Arc<Mutex<Array<Option<ReferenceKind>>>>;
|
||||
#[derive(Debug)]
|
||||
pub struct Array<T: ArrayValue> {
|
||||
pub(crate) id: u32,
|
||||
pub(crate) class: Arc<RuntimeClass>,
|
||||
pub(crate) backing: Box<[T]>,
|
||||
}
|
||||
|
||||
@ -69,9 +86,10 @@ where
|
||||
self.backing[index as usize].clone()
|
||||
}
|
||||
|
||||
pub fn new(id: u32, length: i32) -> Self {
|
||||
pub fn new(id: u32, class: Arc<RuntimeClass>, length: i32) -> Self {
|
||||
Self {
|
||||
id,
|
||||
class,
|
||||
backing: vec![T::default(); length as usize].into_boxed_slice(),
|
||||
}
|
||||
}
|
||||
@ -81,11 +99,12 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Primitive + ArrayValue> From<(u32, Vec<T>)> for Array<T> {
|
||||
fn from(value: (u32, Vec<T>)) -> Self {
|
||||
let (id, vector) = value;
|
||||
impl<T: Primitive + ArrayValue> From<(u32, Arc<RuntimeClass>, Vec<T>)> for Array<T> {
|
||||
fn from(value: (u32, Arc<RuntimeClass>, Vec<T>)) -> Self {
|
||||
let (id, class, vector) = value;
|
||||
Self {
|
||||
id,
|
||||
class,
|
||||
backing: vector.into_boxed_slice(),
|
||||
}
|
||||
}
|
||||
@ -119,3 +138,83 @@ impl ArrayValue for jfloat {}
|
||||
impl ArrayValue for jdouble {}
|
||||
|
||||
impl ArrayValue for jboolean {}
|
||||
|
||||
|
||||
impl ArrayReference {
|
||||
pub fn copy_from(
|
||||
&self,
|
||||
src: &ArrayReference,
|
||||
src_pos: jint,
|
||||
dst_pos: jint,
|
||||
length: jint,
|
||||
) -> Result<(), VmError> {
|
||||
macro_rules! copy {
|
||||
($src_arr:expr, $dst_arr:expr) => {{
|
||||
let src_guard = $src_arr.lock().unwrap();
|
||||
let mut dst_guard = $dst_arr.lock().unwrap();
|
||||
|
||||
let src_start = src_pos as usize;
|
||||
let dst_start = dst_pos as usize;
|
||||
let len = length as usize;
|
||||
|
||||
// Bounds check
|
||||
if src_pos < 0 || dst_pos < 0 || length < 0
|
||||
|| src_start + len > src_guard.backing.len()
|
||||
|| dst_start + len > dst_guard.backing.len()
|
||||
{
|
||||
return Err(VmError::InvariantError("Index oob".to_string()));
|
||||
}
|
||||
|
||||
if Arc::ptr_eq($src_arr, $dst_arr) {
|
||||
drop(src_guard);
|
||||
dst_guard.backing.copy_within(src_start..src_start + len, dst_start);
|
||||
} else {
|
||||
dst_guard.backing[dst_start..dst_start + len]
|
||||
.copy_from_slice(&src_guard.backing[src_start..src_start + len]);
|
||||
}
|
||||
Ok(())
|
||||
}};
|
||||
}
|
||||
|
||||
use ArrayReference::*;
|
||||
match (src, self) {
|
||||
(Int(s), Int(d)) => copy!(s, d),
|
||||
(Byte(s), Byte(d)) => copy!(s, d),
|
||||
(Short(s), Short(d)) => copy!(s, d),
|
||||
(Long(s), Long(d)) => copy!(s, d),
|
||||
(Float(s), Float(d)) => copy!(s, d),
|
||||
(Double(s), Double(d)) => copy!(s, d),
|
||||
(Char(s), Char(d)) => copy!(s, d),
|
||||
(Boolean(s), Boolean(d)) => copy!(s, d),
|
||||
(Object(s), Object(d)) => {
|
||||
// Object arrays need clone, not copy
|
||||
let src_guard = s.lock().unwrap();
|
||||
let mut dst_guard = d.lock().unwrap();
|
||||
|
||||
let src_start = src_pos as usize;
|
||||
let dst_start = dst_pos as usize;
|
||||
let len = length as usize;
|
||||
|
||||
if src_pos < 0 || dst_pos < 0 || length < 0
|
||||
|| src_start + len > src_guard.backing.len()
|
||||
|| dst_start + len > dst_guard.backing.len()
|
||||
{
|
||||
return Err(VmError::InvariantError("Index oob".to_string()));
|
||||
}
|
||||
|
||||
if Arc::ptr_eq(s, d) {
|
||||
drop(src_guard);
|
||||
for i in if src_start < dst_start { (0..len).rev().collect::<Vec<_>>() } else { (0..len).collect() } {
|
||||
dst_guard.backing[dst_start + i] = dst_guard.backing[src_start + i].clone();
|
||||
}
|
||||
} else {
|
||||
for i in 0..len {
|
||||
dst_guard.backing[dst_start + i] = src_guard.backing[src_start + i].clone();
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(VmError::InvariantError("Array type mismatch".to_string())),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -110,6 +110,12 @@ impl ReferenceKind {
|
||||
Self::ArrayReference(a) => a.id(),
|
||||
}
|
||||
}
|
||||
pub fn class(&self) -> Arc<RuntimeClass> {
|
||||
match self {
|
||||
Self::ObjectReference(r) => r.lock().unwrap().class.clone(),
|
||||
Self::ArrayReference(a) => a.class(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type Reference = Option<ReferenceKind>;
|
||||
|
||||
@ -33,7 +33,7 @@ impl ObjectManager {
|
||||
object
|
||||
}
|
||||
|
||||
pub fn new_primitive_array(&mut self, array_type: ArrayType, count: i32) -> ArrayReference {
|
||||
pub fn new_primitive_array(&mut self, class: Arc<RuntimeClass>, array_type: ArrayType, count: i32) -> ArrayReference {
|
||||
let id = generate_identity_hash();
|
||||
assert!(
|
||||
!self.objects.contains_key(&id),
|
||||
@ -41,21 +41,21 @@ impl ObjectManager {
|
||||
);
|
||||
|
||||
let array_ref = match array_type {
|
||||
ArrayType::T_INT => ArrayReference::Int(Arc::new(Mutex::new(Array::new(id, count)))),
|
||||
ArrayType::T_BYTE => ArrayReference::Byte(Arc::new(Mutex::new(Array::new(id, count)))),
|
||||
ArrayType::T_INT => ArrayReference::Int(Arc::new(Mutex::new(Array::new(id, class,count)))),
|
||||
ArrayType::T_BYTE => ArrayReference::Byte(Arc::new(Mutex::new(Array::new(id, class,count)))),
|
||||
ArrayType::T_SHORT => {
|
||||
ArrayReference::Short(Arc::new(Mutex::new(Array::new(id, count))))
|
||||
ArrayReference::Short(Arc::new(Mutex::new(Array::new(id, class,count))))
|
||||
}
|
||||
ArrayType::T_LONG => ArrayReference::Long(Arc::new(Mutex::new(Array::new(id, count)))),
|
||||
ArrayType::T_LONG => ArrayReference::Long(Arc::new(Mutex::new(Array::new(id, class,count)))),
|
||||
ArrayType::T_FLOAT => {
|
||||
ArrayReference::Float(Arc::new(Mutex::new(Array::new(id, count))))
|
||||
ArrayReference::Float(Arc::new(Mutex::new(Array::new(id, class,count))))
|
||||
}
|
||||
ArrayType::T_DOUBLE => {
|
||||
ArrayReference::Double(Arc::new(Mutex::new(Array::new(id, count))))
|
||||
ArrayReference::Double(Arc::new(Mutex::new(Array::new(id, class,count))))
|
||||
}
|
||||
ArrayType::T_CHAR => ArrayReference::Char(Arc::new(Mutex::new(Array::new(id, count)))),
|
||||
ArrayType::T_CHAR => ArrayReference::Char(Arc::new(Mutex::new(Array::new(id, class,count)))),
|
||||
ArrayType::T_BOOLEAN => {
|
||||
ArrayReference::Boolean(Arc::new(Mutex::new(Array::new(id, count))))
|
||||
ArrayReference::Boolean(Arc::new(Mutex::new(Array::new(id, class,count))))
|
||||
}
|
||||
};
|
||||
|
||||
@ -64,26 +64,26 @@ impl ObjectManager {
|
||||
array_ref
|
||||
}
|
||||
|
||||
pub fn new_object_array(&mut self, count: i32) -> ArrayReference {
|
||||
pub fn new_object_array(&mut self, class: Arc<RuntimeClass>, count: i32) -> ArrayReference {
|
||||
let id = generate_identity_hash();
|
||||
assert!(
|
||||
!self.objects.contains_key(&id),
|
||||
"Generated ID already exists!"
|
||||
);
|
||||
let array_ref = ArrayReference::Object(Arc::new(Mutex::new(Array::new(id, count))));
|
||||
let array_ref = ArrayReference::Object(Arc::new(Mutex::new(Array::new(id, class,count))));
|
||||
|
||||
self.objects
|
||||
.insert(id, ReferenceKind::ArrayReference(array_ref.clone()));
|
||||
array_ref
|
||||
}
|
||||
pub fn new_byte_array(&mut self, vector: Vec<i8>) -> ArrayReference {
|
||||
pub fn new_byte_array(&mut self, class: Arc<RuntimeClass>, vector: Vec<i8>) -> ArrayReference {
|
||||
warn!("Manual sidechannel byte array creation");
|
||||
let id = generate_identity_hash();
|
||||
assert!(
|
||||
!self.objects.contains_key(&id),
|
||||
"Generated ID already exists!"
|
||||
);
|
||||
let array_ref = ArrayReference::Byte(Arc::new(Mutex::new(Array::from((id, vector)))));
|
||||
let array_ref = ArrayReference::Byte(Arc::new(Mutex::new(Array::from((id, class,vector)))));
|
||||
|
||||
self.objects
|
||||
.insert(id, ReferenceKind::ArrayReference(array_ref.clone()));
|
||||
@ -111,7 +111,7 @@ impl ObjectManager {
|
||||
.and_then(ReferenceKind::into_object_reference)
|
||||
}
|
||||
|
||||
pub fn new_string(&mut self, string_class: Arc<RuntimeClass>, utf8: &str) -> ObjectReference {
|
||||
pub fn new_string(&mut self, byte_class: Arc<RuntimeClass>, string_class: Arc<RuntimeClass>, utf8: &str) -> ObjectReference {
|
||||
warn!("Manual sidechannel string creation: \n\"{}\"", utf8);
|
||||
let key = utf8.to_owned();
|
||||
let jstr = self.new_object(string_class);
|
||||
@ -120,7 +120,7 @@ impl ObjectManager {
|
||||
.flat_map(|e| e.to_le_bytes())
|
||||
.map(|e| e as i8)
|
||||
.collect::<Vec<_>>();
|
||||
let barray = self.new_byte_array(byte_vec);
|
||||
let barray = self.new_byte_array(byte_class, byte_vec);
|
||||
|
||||
jstr.lock().unwrap().fields.insert(
|
||||
"value".to_string(),
|
||||
|
||||
@ -6,7 +6,7 @@ use crate::objects::object::{ObjectReference, ReferenceKind};
|
||||
use crate::objects::object_manager::ObjectManager;
|
||||
use crate::value::{Primitive, Value};
|
||||
use crate::vm::Vm;
|
||||
use crate::{BaseType, FieldType, Frame, MethodDescriptor, ThreadId, VmError};
|
||||
use crate::{BaseType, FieldType, Frame, MethodDescriptor, ThreadId};
|
||||
use deku::DekuError::Incomplete;
|
||||
use itertools::Itertools;
|
||||
use jni::sys::{jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jobject, jshort, JNIEnv};
|
||||
@ -21,6 +21,7 @@ use std::ptr::null_mut;
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
use std::vec::IntoIter;
|
||||
use crate::error::VmError;
|
||||
|
||||
type MethodCallResult = Result<Option<Value>, VmError>;
|
||||
|
||||
@ -36,7 +37,7 @@ pub struct VmThread {
|
||||
pub id: ThreadId,
|
||||
pub vm: Arc<Vm>,
|
||||
pub loader: Arc<Mutex<ClassLoader>>,
|
||||
pub frame_stack: Vec<Frame>,
|
||||
pub frame_stack: Mutex<Vec<Arc<Mutex<Frame>>>>,
|
||||
pub gc: Arc<RwLock<ObjectManager>>,
|
||||
pub jni_env: JNIEnv,
|
||||
}
|
||||
@ -54,7 +55,7 @@ impl VmThread {
|
||||
id,
|
||||
vm,
|
||||
loader,
|
||||
frame_stack: Vec::new(),
|
||||
frame_stack: Default::default(),
|
||||
gc,
|
||||
jni_env,
|
||||
}
|
||||
@ -87,8 +88,7 @@ impl VmThread {
|
||||
.loader
|
||||
.lock()
|
||||
.unwrap()
|
||||
.get_or_load(what, None)
|
||||
.map_err(VmError::LoaderError)?;
|
||||
.get_or_load(what, None)?;
|
||||
|
||||
// Phase 2: Collect classes that need initialisation (short lock)
|
||||
let classes_to_init = {
|
||||
@ -111,7 +111,6 @@ impl VmThread {
|
||||
.lock()
|
||||
.unwrap()
|
||||
.get_or_load(what, None)
|
||||
.map_err(VmError::LoaderError)
|
||||
}
|
||||
|
||||
/// Initialize a class following JVM Spec 5.5.
|
||||
@ -178,23 +177,8 @@ impl VmThread {
|
||||
}
|
||||
|
||||
// Run <clinit> if present
|
||||
let class_init_method = class.find_method("<clinit>", &MethodDescriptor::void());
|
||||
if let Ok(method) = class_init_method {
|
||||
let method_ref = MethodRef {
|
||||
class: class.this_class.clone(),
|
||||
name: method.name.clone(),
|
||||
desc: method.desc.clone(),
|
||||
};
|
||||
|
||||
Frame::new(
|
||||
method_ref,
|
||||
method.code.clone().unwrap(),
|
||||
class.constant_pool.clone(),
|
||||
vec![],
|
||||
self.vm.clone(),
|
||||
)
|
||||
.execute()
|
||||
.map_err(|e| VmError::LoaderError(format!("Error in <clinit>: {:?}", e)))?;
|
||||
if let Ok(method) = class.find_method("<clinit>", &MethodDescriptor::void()) {
|
||||
self.execute_method(&class, &method, vec![])?;
|
||||
}
|
||||
Ok(())
|
||||
})();
|
||||
@ -216,18 +200,32 @@ impl VmThread {
|
||||
result
|
||||
}
|
||||
|
||||
pub fn invoke_main(&self, what: &str) {
|
||||
pub fn invoke_main(&self, what: &str) -> Result<(), VmError> {
|
||||
let method_ref = MethodRef {
|
||||
class: what.to_string(),
|
||||
name: "main".to_string(),
|
||||
desc: MethodDescriptor::psvm(),
|
||||
};
|
||||
|
||||
self.invoke(method_ref, Vec::new())
|
||||
.expect("Main method died");
|
||||
self.invoke(method_ref, Vec::new())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn invoke(&self, method_reference: MethodRef, mut args: Vec<Value>) -> MethodCallResult {
|
||||
pub fn invoke(&self, method_reference: MethodRef, args: Vec<Value>) -> MethodCallResult {
|
||||
if method_reference.class.contains("Unsafe") {
|
||||
println!("()")
|
||||
}
|
||||
let class = self.get_or_resolve_class(&method_reference.class)?;
|
||||
let method = class.find_method(&method_reference.name, &method_reference.desc).unwrap();
|
||||
self.execute_method(&class, &method, args)
|
||||
}
|
||||
|
||||
pub fn invoke_virtual(&self, method_reference: MethodRef, class: Arc<RuntimeClass>, args: Vec<Value>) -> MethodCallResult {
|
||||
let method = class.find_method(&method_reference.name, &method_reference.desc).unwrap();
|
||||
self.execute_method(&class, &method, args)
|
||||
}
|
||||
|
||||
/*pub fn invoke_old(&self, method_reference: MethodRef, mut args: Vec<Value>) -> MethodCallResult {
|
||||
let mut new_args = Vec::new();
|
||||
let class = self.get_or_resolve_class(&method_reference.class)?;
|
||||
let resolved_method = class
|
||||
@ -255,8 +253,11 @@ impl VmThread {
|
||||
args,
|
||||
self.vm.clone(),
|
||||
);
|
||||
frame.execute()
|
||||
}
|
||||
self.frame_stack.lock().unwrap().push(frame.clone());
|
||||
let result = frame.execute()?;
|
||||
self.frame_stack.lock().unwrap().pop();
|
||||
Ok(result)
|
||||
}*/
|
||||
|
||||
pub fn invoke_native(&self, method: &MethodRef, mut args: Vec<Value>) -> MethodCallResult {
|
||||
let symbol_name = generate_jni_method_name(method, false);
|
||||
@ -275,7 +276,7 @@ impl VmThread {
|
||||
let name_with_params = generate_jni_method_name(method, true);
|
||||
self.vm.find_native_method(&name_with_params)
|
||||
})
|
||||
.ok_or(VmError::NativeError("Link error".to_owned()))?;
|
||||
.ok_or(VmError::NativeError(format!("Link error: Unable to locate symbol {symbol_name}")))?;
|
||||
// build pointer to native fn
|
||||
let cp = CodePtr::from_ptr(p);
|
||||
|
||||
@ -351,6 +352,70 @@ impl VmThread {
|
||||
warn!("Invoke native not final");
|
||||
result
|
||||
}
|
||||
fn execute_method(
|
||||
&self,
|
||||
class: &Arc<RuntimeClass>,
|
||||
method: &MethodData,
|
||||
args: Vec<Value>,
|
||||
) -> MethodCallResult {
|
||||
eprintln!("[DEBUG] execute_method self.id = {:?}", self.id);
|
||||
let method_ref = MethodRef {
|
||||
class: class.this_class.clone(),
|
||||
name: method.name.clone(),
|
||||
desc: method.desc.clone(),
|
||||
};
|
||||
|
||||
|
||||
if method.flags.ACC_NATIVE {
|
||||
let mut native_args = Vec::new();
|
||||
if method.flags.ACC_STATIC {
|
||||
let jclass = self.vm.gc.read().unwrap().get(*class.mirror.wait());
|
||||
native_args.push(Value::Reference(Some(jclass)));
|
||||
}
|
||||
native_args.extend(args);
|
||||
return self.invoke_native(&method_ref, native_args);
|
||||
}
|
||||
|
||||
let mut frame = Frame::new(
|
||||
class.clone(),
|
||||
method_ref,
|
||||
method.code.clone().unwrap(),
|
||||
class.constant_pool.clone(),
|
||||
args,
|
||||
self.vm.clone(),
|
||||
method.line_number_table.clone(),
|
||||
);
|
||||
let frame = Arc::new(Mutex::new(frame));
|
||||
self.frame_stack.lock().unwrap().push(frame.clone());
|
||||
eprintln!("[DEBUG] pushed frame for {}.{}, stack depth now: {}",
|
||||
class.this_class, method.name,
|
||||
self.frame_stack.lock().unwrap().len());
|
||||
let result = frame.lock().unwrap().execute();
|
||||
eprintln!("[DEBUG] returned from {}.{}, result ok: {}, stack depth: {}",
|
||||
class.this_class, method.name, result.is_ok(),
|
||||
self.frame_stack.lock().unwrap().len());
|
||||
if result.is_ok() {
|
||||
self.frame_stack.lock().unwrap().pop();
|
||||
}
|
||||
result
|
||||
}
|
||||
pub fn print_stack_trace(&self) {
|
||||
let guard = self.frame_stack.lock().unwrap();
|
||||
// Reverse - most recent frame first (like Java does)
|
||||
for frame in guard.iter().rev() {
|
||||
let frame = frame.lock().unwrap();
|
||||
let method = &frame.method_ref;
|
||||
// Internal format uses '/', Java stack traces use '.'
|
||||
let class_name = method.class.replace("/", ".");
|
||||
|
||||
|
||||
match (&frame.class.source_file, &frame.current_line_number()) {
|
||||
(Some(file), Some(line)) => eprintln!("\tat {}.{}({}:{})", class_name, method.name, file, line),
|
||||
(Some(file), None) => eprintln!("\tat {}.{}({})", class_name, method.name, file),
|
||||
_ => eprintln!("\tat {}.{}(Unknown Source)", class_name, method.name),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn build_args<'a>(
|
||||
@ -478,6 +543,7 @@ impl VmThread {
|
||||
}
|
||||
|
||||
let string_class = self.get_class("java/lang/String").unwrap();
|
||||
gc.new_string(string_class, utf)
|
||||
let byte_array_class = self.get_class("[B").unwrap();
|
||||
gc.new_string(byte_array_class, string_class, utf)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
use crate::objects::array::ArrayReference;
|
||||
use crate::objects::object::{ObjectReference, ReferenceKind};
|
||||
use crate::{BaseType, FieldType, VmError};
|
||||
use crate::{BaseType, FieldType};
|
||||
use core::fmt;
|
||||
use dashmap::DashMap;
|
||||
use jni::sys::{jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jshort};
|
||||
use log::trace;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::ops::Deref;
|
||||
use crate::error::VmError;
|
||||
|
||||
/// A reference-counted, thread-safe pointer to an Object.
|
||||
|
||||
@ -167,6 +168,13 @@ impl Value {
|
||||
pub fn is_wide(&self) -> bool {
|
||||
matches!(self, Value::Primitive(Primitive::Long(_) | Primitive::Double(_)))
|
||||
}
|
||||
|
||||
pub fn as_ref_kind(&self) -> Option<ReferenceKind> {
|
||||
match self {
|
||||
Value::Reference(Some(kind)) => Some(kind.clone()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_value_from {
|
||||
|
||||
@ -11,12 +11,12 @@ use crate::class_file::{ClassFlags, MethodRef};
|
||||
use crate::class_loader::ClassLoader;
|
||||
use crate::objects::object_manager::ObjectManager;
|
||||
use crate::thread::VmThread;
|
||||
use crate::{MethodDescriptor, ThreadId, VmError};
|
||||
use crate::{MethodDescriptor, ThreadId};
|
||||
use dashmap::DashMap;
|
||||
use imp::{Library, Symbol};
|
||||
use imp::Library;
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
use crate::class::{InitState, RuntimeClass};
|
||||
use crate::objects::object::ReferenceKind;
|
||||
use crate::error::VmError;
|
||||
|
||||
// struct AbstractObject<'a> {}
|
||||
pub struct Vm {
|
||||
@ -108,25 +108,45 @@ impl Vm {
|
||||
// "jdk/internal/misc/UnsafeConstants"
|
||||
];
|
||||
let _ = classes.iter().map(|e| thread.get_or_resolve_class(e));
|
||||
let prims = vec!["byte", "char", "double", "float", "int", "long", "short", "boolean"];
|
||||
let prims = vec![
|
||||
("byte", "B"),
|
||||
("char", "C"),
|
||||
("double", "D"),
|
||||
("float", "F"),
|
||||
("int", "I"),
|
||||
("long", "J"),
|
||||
("short", "S"),
|
||||
("boolean", "Z")
|
||||
];
|
||||
let thread = self.threads.get(&self.main_thread_id).unwrap();
|
||||
|
||||
for prim in prims {
|
||||
let klass =
|
||||
self.loader.lock().unwrap().primitive_class(prim);
|
||||
self.loader.lock().unwrap().primitive_class(prim.0);
|
||||
|
||||
|
||||
let class_class = thread.get_class("java/lang/Class")?;
|
||||
let string = thread.intern_string(&prim);
|
||||
let name_obj = thread.intern_string(&prim.0);
|
||||
let flags = ClassFlags::from(1041u16);
|
||||
let class_obj = self.gc.write().unwrap().new_class(
|
||||
class_class.clone(),
|
||||
Some(ReferenceKind::ObjectReference(name_obj)),
|
||||
None,
|
||||
flags,
|
||||
true
|
||||
);
|
||||
klass.mirror.set(class_obj.lock().unwrap().id).unwrap();
|
||||
|
||||
let prim_array_klass = self.loader.lock().unwrap().create_array_class(klass.clone());
|
||||
let arr_name_obj = thread.intern_string(&prim_array_klass.this_class);
|
||||
let arr_class_obj = self.gc.write().unwrap().new_class(
|
||||
class_class,
|
||||
Some(ReferenceKind::ObjectReference(string)),
|
||||
Some(ReferenceKind::ObjectReference(arr_name_obj)),
|
||||
None,
|
||||
flags,
|
||||
false
|
||||
);
|
||||
klass.mirror.set(class_obj.lock().unwrap().id).unwrap();
|
||||
prim_array_klass.mirror.set(arr_class_obj.lock().unwrap().id).unwrap();
|
||||
}
|
||||
|
||||
let phase1ref = MethodRef {
|
||||
@ -138,8 +158,8 @@ impl Vm {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn run(&self, what: &str) {
|
||||
self.boot_strap().expect("Failed to bootstrap vm!");
|
||||
pub fn run(&self, what: &str) -> Result<(), VmError> {
|
||||
self.boot_strap()?;
|
||||
// Get main thread from DashMap
|
||||
let thread = self.threads.get(&self.main_thread_id).unwrap().clone();
|
||||
thread.invoke_main(what)
|
||||
|
||||
@ -7,6 +7,7 @@ publish = ["nexus"]
|
||||
[dependencies]
|
||||
jni = { workspace = true }
|
||||
roast-vm-core = { workspace = true }
|
||||
log = "0.4.28"
|
||||
|
||||
[lib]
|
||||
name = "roast_vm"
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
mod class;
|
||||
mod object;
|
||||
mod misc_unsafe;
|
||||
mod system;
|
||||
|
||||
use jni::objects::{JClass, JObject, JString};
|
||||
use jni::strings::JNIString;
|
||||
@ -11,6 +12,7 @@ use jni::{JNIEnv, NativeMethod};
|
||||
use std::ffi::c_void;
|
||||
use std::io::Write;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
use roast_vm_core::objects::array::ArrayReference;
|
||||
use roast_vm_core::objects::object::ObjectReference;
|
||||
use roast_vm_core::objects::ReferenceKind;
|
||||
use roast_vm_core::VmThread;
|
||||
@ -161,6 +163,14 @@ fn resolve_object(thread: &VmThread, obj: jobject) -> Option<ObjectReference> {
|
||||
Some(obj_ref.clone())
|
||||
}
|
||||
|
||||
fn resolve_array(thread: &VmThread, obj: jobject) -> Option<ArrayReference> {
|
||||
let gc = thread.gc.read().unwrap();
|
||||
let ReferenceKind::ArrayReference(arr_ref) = gc.get(obj as u32) else {
|
||||
return None;
|
||||
};
|
||||
Some(arr_ref.clone())
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "system" fn Java_jdk_internal_util_SystemProps_00024Raw_vmProperties<'local>(
|
||||
mut env: JNIEnv<'local>,
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
use jni::sys::{jclass, jint, jobject, jstring, JNIEnv};
|
||||
use log::warn;
|
||||
use crate::{get_thread, resolve_object};
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
@ -9,3 +10,21 @@ pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_registerNatives(
|
||||
//no op
|
||||
()
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_arrayBaseOffset0(
|
||||
env: *mut JNIEnv,
|
||||
obj: jclass,
|
||||
) -> jint {
|
||||
warn!("arrayBaseOffset0 currently just returning 0");
|
||||
0
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "system" fn Java_jdk_internal_misc_Unsafe_arrayIndexScale0(
|
||||
env: *mut JNIEnv,
|
||||
obj: jclass,
|
||||
) -> jint {
|
||||
warn!("arrayIndexScale0 currently just returning 0");
|
||||
0
|
||||
}
|
||||
51
crates/roast-vm-sys/src/system.rs
Normal file
51
crates/roast-vm-sys/src/system.rs
Normal file
@ -0,0 +1,51 @@
|
||||
use jni::sys::{jclass, jint, jobject, JNIEnv};
|
||||
use log::warn;
|
||||
use crate::{get_thread, resolve_array, resolve_object};
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "system" fn Java_java_lang_System_arraycopy(
|
||||
env: *mut JNIEnv,
|
||||
_ignored: jclass,
|
||||
src: jobject,
|
||||
src_pos: jint,
|
||||
dst: jobject,
|
||||
dst_pos: jint,
|
||||
length: jint,
|
||||
) {
|
||||
let thread = &*get_thread(env);
|
||||
|
||||
// Check for null pointers
|
||||
if src.is_null() || dst.is_null() {
|
||||
panic!("NPE!");
|
||||
// throw_exception(env, "java/lang/NullPointerException", None);
|
||||
return;
|
||||
}
|
||||
|
||||
// Resolve JNI handles to actual objects
|
||||
let src_arr = resolve_array(thread, src).expect("Was tested non null");
|
||||
let dst_arr = resolve_array(thread, dst).expect("Was tested non null");
|
||||
|
||||
// Validate both are arrays
|
||||
/*let (Some(src_arr), Some(dst_arr)) = (src_obj.as_array(), dst_obj.as_array()) else {
|
||||
panic!("not arrays!");
|
||||
throw_exception(env, "java/lang/ArrayStoreException", None);
|
||||
return;
|
||||
};*/
|
||||
|
||||
let src_len = src_arr.len();
|
||||
let dst_len = dst_arr.len();
|
||||
|
||||
// Bounds checking
|
||||
if src_pos < 0 || dst_pos < 0 || length < 0
|
||||
|| src_pos.checked_add(length).map_or(true, |end| end > src_len)
|
||||
|| dst_pos.checked_add(length).map_or(true, |end| end > dst_len)
|
||||
{
|
||||
panic!("Array index out of bounds!");
|
||||
// throw_exception(env, "java/lang/ArrayIndexOutOfBoundsException", None);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
dst_arr.copy_from(&src_arr, src_pos, dst_pos, length).expect("Array copy error hell");
|
||||
// Type compatibility check + copy
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user