WIP statin initializers

This commit is contained in:
Shautvast 2023-10-16 09:10:55 +02:00
parent b46145719c
commit 24f03f61f6
9 changed files with 240 additions and 141 deletions

View file

@ -1,4 +1,4 @@
use std::cell::UnsafeCell;
use std::cell::{Ref, RefCell, RefMut, UnsafeCell};
use std::collections::HashMap;
use std::fmt;
use std::hash::Hash;
@ -6,14 +6,52 @@ use std::rc::Rc;
use std::sync::Arc;
use anyhow::{anyhow, Error};
use once_cell::sync::Lazy;
use crate::classloader::CpEntry;
use crate::classloader::{CpEntry, load_class};
use crate::heap::ObjectRef;
use crate::io::read_u16;
use crate::io::{find_class, read_bytecode, read_u16};
use crate::vm::Vm;
//trying to be ready for multithreaded as much as possible, using Arc's and all, but it will still require (a lot of) extra work
static mut CLASSDEFS: Lazy<HashMap<String, Arc<RefCell<Class>>>> = Lazy::new(|| HashMap::new()); //TODO add mutex..
// gets the Class from cache, or reads it from classpath,
// then parses the binary data into a Class struct
// Vm keeps ownership of the class and hands out Arc references to it
pub fn get_class(vm: &mut Vm, class_name: &str) -> Result<Arc<RefCell<Class>>, Error> {
println!("get_class {}", class_name);
unsafe {
let new_class = CLASSDEFS.entry(class_name.into()).or_insert_with(|| {
println!("read class {} ", class_name);
let resolved_path = find_class(&vm.classpath, class_name).unwrap();
// println!("full path {}", resolved_path);
let bytecode = read_bytecode(resolved_path).unwrap();
let mut class = load_class(bytecode).unwrap();
let super_class_name = class.super_class_name.as_ref();
if let Some(super_class_name) = super_class_name {
if let Ok(super_class) = get_class(vm, &super_class_name) {
class.super_class = Some(super_class);
}
}
Arc::new(RefCell::new(class))
});
Class::initialize_fields(new_class.clone());
let exec = new_class.borrow().methods.contains_key("<clinit>()V");
if exec {
vm.execute_class(new_class.clone(), "<clinit>()V", vec![]).unwrap();
}
Ok(new_class.clone())
}
}
/// the class definition as read from the class file + derived values
// TODO implement call to static initializers
// TODO implement storage for static fields
#[derive(Debug)]
pub struct Class {
pub minor_version: u16,
@ -22,13 +60,17 @@ pub struct Class {
pub access_flags: u16,
pub name: String,
pub super_class_name: Option<String>,
pub super_class: Option<Arc<Class>>,
pub super_class: Option<Arc<RefCell<Class>>>,
pub interface_indices: Vec<u16>,
pub interfaces: Vec<Class>,
pub fields: HashMap<String, Field>,
pub methods: HashMap<String, Method>,
pub methods: HashMap<String, Rc<Method>>,
pub attributes: HashMap<String, AttributeType>,
pub(crate) field_mapping: Option<HashMap<String, HashMap<String, (String, usize)>>>, // first key: this/super/supersuper-name(etc), second key: fieldname, value (type, index)
pub(crate) object_field_mapping: HashMap<String, HashMap<String, (String, usize)>>,
// first key: this/super/supersuper-name(etc), second key: fieldname, value (type, index)
pub(crate) static_field_mapping: HashMap<String, HashMap<String, (String, usize)>>,
// first key: this/super/supersuper-name(etc), second key: fieldname, value (type, index)
pub(crate) static_data: Vec<UnsafeValue>,
}
impl Class {
@ -41,7 +83,7 @@ impl Class {
super_class_index: u16,
interface_indices: Vec<u16>,
fields: HashMap<String, Field>,
methods: HashMap<String, Method>,
methods: HashMap<String, Rc<Method>>,
attributes: HashMap<String, AttributeType>,
) -> Self {
let name = Class::class_name(this_class, constant_pool.clone()).unwrap();
@ -60,12 +102,18 @@ impl Class {
fields,
methods,
attributes,
field_mapping: None,
object_field_mapping: HashMap::new(),
static_field_mapping: HashMap::new(),
static_data: vec![],
}
}
pub(crate) fn n_fields(&self) -> usize {
self.field_mapping.as_ref().map_or(0, |m| m.len())
pub(crate) fn n_object_fields(&self) -> usize {
self.object_field_mapping.len()
}
pub(crate) fn n_static_fields(&self) -> usize {
self.object_field_mapping.len()
}
// Create a mapping per field(name) to an index in the storage vector that contains the instance data.
@ -76,42 +124,64 @@ impl Class {
// This way `this.a` can be differentiated from `super.a`.
//
// this method looks up this and super classes and calls map_fields for each.
pub fn initialize(&mut self) {
let mut field_mapping = HashMap::new();
pub fn initialize_fields(class: Arc<RefCell<Class>>) {
let mut this_field_mapping = HashMap::new();
let mut static_field_mapping = HashMap::new();
let mut field_map_index: usize = 0;
Class::map_fields(&mut field_mapping, self, &mut field_map_index);
let mut sooper = &self.super_class;
while let Some(super_class) = sooper {
Class::map_fields(&mut field_mapping, super_class, &mut field_map_index);
sooper = &super_class.super_class;
Class::add_field_mappings(&mut this_field_mapping, &mut static_field_mapping, class.clone(), &mut field_map_index);
class.borrow_mut().object_field_mapping = this_field_mapping;
class.borrow_mut().static_field_mapping = static_field_mapping;
let static_data = Class::set_field_data(class.clone());
class.borrow_mut().static_data = static_data;
}
fn add_field_mappings(this_field_mapping: &mut HashMap<String, HashMap<String, (String, usize)>>,
static_field_mapping: &mut HashMap<String, HashMap<String, (String, usize)>>,
class: Arc<RefCell<Class>>, field_map_index: &mut usize) {
let (o, s) = Class::map_fields(class.clone(), field_map_index);
let borrow = class.borrow();
let name = &borrow.name;
this_field_mapping.insert(name.to_owned(), o);
static_field_mapping.insert(name.to_owned(), s);
if let Some(super_class) = class.borrow().super_class.as_ref() {
Class::add_field_mappings(this_field_mapping, static_field_mapping, super_class.clone(), field_map_index);
}
self.field_mapping = Some(field_mapping);
}
// part of the initialize procedure
fn map_fields(
field_mapping: &mut HashMap<String, HashMap<String, (String, usize)>>,
class: &Class,
class: Arc<RefCell<Class>>,
field_map_index: &mut usize,
) {
) -> (HashMap<String, (String, usize)>, HashMap<String, (String, usize)>) {
let mut this_fields = HashMap::new(); //fields in class are stored per class and every superclass.
for field in &class.fields {
let mut static_fields = HashMap::new(); //fields in class are stored per class and every superclass.
for (name, field) in &class.borrow().fields {
if field.is(Modifier::Static) {
static_fields.insert(
name.to_owned(),
(field.type_of().to_owned(), *field_map_index),
);
} else {
this_fields.insert(
field.0.to_owned(),
(field.1.type_of().to_owned(), *field_map_index),
name.to_owned(),
(field.type_of().to_owned(), *field_map_index),
); //name => (type,index)
}
*field_map_index += 1;
}
let this_name = class.name.to_owned();
field_mapping.insert(this_name, this_fields);
(this_fields, static_fields)
}
pub fn get_version(&self) -> (u16, u16) {
(self.major_version, self.minor_version)
}
pub fn get_method(&self, name: &str) -> Result<&Method, Error> {
pub fn get_method(&self, name: &str) -> Result<&Rc<Method>, Error> {
self.methods
.get(name)
.ok_or(anyhow!("Method {} not found", name))
@ -135,6 +205,29 @@ impl Class {
}
}
pub(crate) fn set_field_data(class: Arc<RefCell<Class>>) -> Vec<UnsafeValue> {
let mut field_data = Vec::with_capacity(class.borrow().n_object_fields());
for (_, fields) in &class.borrow().static_field_mapping {
for (_, (fieldtype, _)) in fields {
let value = match fieldtype.as_str() {
"Z" => Value::BOOL(false),
"B" => Value::I32(0),
"S" => Value::I32(0),
"I" => Value::I32(0),
"J" => Value::I64(0),
"F" => Value::F32(0.0),
"D" => Value::F64(0.0),
"L" => Value::Null,
_ => Value::Void,
};
field_data.push(value.into());
}
}
field_data
}
// convienence methods for data from the constantpool
pub fn cp_field_ref(&self, index: &u16) -> Option<(&u16, &u16)> {
@ -281,6 +374,11 @@ impl Field {
}
}
pub fn is(&self, modifier: Modifier) -> bool {
let modifier = modifier as u16;
self.access_flags & modifier == modifier
}
pub fn name(&self) -> &String {
if let CpEntry::Utf8(utf8) = &self.constant_pool.get(&self.name_index).unwrap() {
return utf8;

View file

@ -48,7 +48,7 @@ pub fn load_class(bytecode: Vec<u8>) -> Result<Class, Error> {
let mut methods = HashMap::new();
for _ in 0..methods_count {
let m = read_method(constant_pool.clone(), pos, &bytecode);
methods.insert(m.name(), m);
methods.insert(m.name(), Rc::new(m));
}
let attributes_count = read_u16(&bytecode, pos);

View file

@ -1,17 +1,9 @@
use std::cell::UnsafeCell;
use std::cell::{RefCell, UnsafeCell};
use std::fmt;
use std::ops::Deref;
use std::sync::Arc;
use crate::class::{Class, UnsafeValue, Value};
use crate::classloader::CpEntry;
// trying to implement efficient object instance storage
pub struct Object {
// locked: bool,
// hashcode: i32,
pub class: Arc<Class>,
pub data: Vec<UnsafeValue>,
} //arrays
// can contain object or array
#[derive(Debug)]
@ -28,14 +20,22 @@ pub enum ObjectRef {
Object(Box<Object>),
}
// trying to implement efficient object instance storage
pub struct Object {
// locked: bool,
// hashcode: i32,
pub class: Arc<RefCell<Class>>,
pub data: Vec<UnsafeValue>,
} //arrays
unsafe impl Send for Object {}
unsafe impl Sync for Object {}
// object, not array
impl Object {
pub fn new(class: Arc<Class>) -> Self {
let instance_data = Object::init_fields(&class);
pub fn new(class: Arc<RefCell<Class>>) -> Self {
let instance_data = Object::init_fields(class.clone());
Self {
class,
data: instance_data,
@ -43,10 +43,10 @@ impl Object {
}
// initializes all non-static fields to their default values
pub(crate) fn init_fields(class: &Class) -> Vec<UnsafeValue> {
let mut field_data = Vec::with_capacity(class.n_fields());
pub(crate) fn init_fields(class: Arc<RefCell<Class>>) -> Vec<UnsafeValue> {
let mut field_data = Vec::with_capacity(class.borrow().n_object_fields());
for (_, fields) in class.field_mapping.as_ref().unwrap() {
for (_, fields) in &class.borrow().object_field_mapping {
for (_, (fieldtype, _)) in fields {
let value = match fieldtype.as_str() {
"Z" => Value::BOOL(false),
@ -67,11 +67,9 @@ impl Object {
}
pub fn set(&mut self, class_name: &String, field_name: &String, value: UnsafeValue) {
let (_type, index) = self
.class
.field_mapping
.as_ref()
.unwrap()
let borrow = self.class.borrow();
let (_type, index) =borrow
.object_field_mapping
.get(class_name)
.unwrap()
.get(field_name)
@ -80,11 +78,9 @@ impl Object {
}
pub fn get(&mut self, class_name: &String, field_name: &String) -> &UnsafeValue {
let (_type, index) = &self
.class
.field_mapping
.as_ref()
.unwrap()
let borrow = self.class.borrow();
let (_type, index) =borrow
.object_field_mapping
.get(class_name)
.unwrap()
.get(field_name)
@ -92,12 +88,12 @@ impl Object {
&self.data[*index]
}
fn get_field_name(&self, cp_index: &u16) -> &str {
if let CpEntry::Utf8(name) = self.class.constant_pool.get(cp_index).unwrap() {
return name;
}
panic!()
}
// fn get_field_name(&self, cp_index: &u16) -> &str {
// if let CpEntry::Utf8(name) = self.class.constant_pool.get(cp_index).unwrap() {
// return name;
// }
// panic!()
// }
}
impl fmt::Debug for Object {
@ -109,7 +105,7 @@ impl fmt::Debug for Object {
// // r
// }
// ).collect();
write!(f, "{}", self.class.name)
write!(f, "{}", self.class.borrow().name)
}
}

View file

@ -6,7 +6,8 @@ fn main() -> Result<(), Error> {
// TODO build index for package -> jarfile?
let mut vm = Vm::new("tests");
let main_class = "Inheritance";
// let main_class = "Inheritance";
let main_class = "Main";
vm.execute(main_class, "main([Ljava/lang/String;)V", vec![])
.unwrap();

View file

@ -1,8 +1,9 @@
use std::sync::Arc;
use std::rc::Rc;
use crate::class::{Method, UnsafeValue, Value};
use crate::class::{Class, Method, UnsafeValue, Value};
pub fn invoke_native(class: Arc<Class>, method: &Method) -> UnsafeValue {
println!("invoke native {:?}.{:?}", class.name, method.name());
pub fn invoke_native(method: Rc<Method>, args: Vec<UnsafeValue>) -> UnsafeValue {
println!("native {}", method.name());
Value::void()
}

View file

@ -125,6 +125,7 @@ pub const DRETURN: u8 = 175; // (0xaf) Return double from method
pub const areturn: u8 = 176; //(0xb0) return reference
pub const RETURN_VOID: u8 = 177; // (0xb1) Return void from method (actually 'return' but that's a keyword)
pub const GETSTATIC: u8 = 178; // (0xb2) Get static field from class
pub const PUTSTATIC: u8 = 179; // (0xb3) Set static field in class
pub const GETFIELD: u8 = 180; // (0xb4) Fetch field from object3
pub const PUTFIELD: u8 = 181; // (0xb5) Set field in object
pub const INVOKEVIRTUAL: u8 = 182; // (0xb6) Invoke instance method; dispatch based on class

144
src/vm.rs
View file

@ -1,14 +1,13 @@
use std::cell::UnsafeCell;
use std::cell::{RefCell, UnsafeCell};
use std::collections::HashMap;
use std::rc::Rc;
use std::sync::Arc;
use anyhow::{anyhow, Error};
use once_cell::unsync::Lazy;
use crate::class::{AttributeType, Class, get_class, Modifier, UnsafeValue, Value};
use crate::class::Value::Void;
use crate::class::{AttributeType, Class, Modifier, UnsafeValue, Value};
use crate::classloader::{load_class, CpEntry};
use crate::classloader::CpEntry;
use crate::heap::{Heap, Object, ObjectRef};
use crate::io::*;
use crate::native::invoke_native;
@ -42,11 +41,9 @@ impl StackFrame {
}
}
//trying to be ready for multithreaded as much as possible, using Arc's and all, but it will still require (a lot of) extra work
static mut CLASSDEFS: Lazy<HashMap<String, Arc<Class>>> = Lazy::new(|| HashMap::new()); //TODO add mutex..
pub struct Vm {
classpath: Vec<String>,
pub classpath: Vec<String>,
heap: Heap,
stack: Vec<StackFrame>,
}
@ -77,35 +74,8 @@ impl Vm {
}
}
// gets the Class from cache, or reads it from classpath,
// then parses the binary data into a Class struct
// Vm keeps ownership of the class and hands out Arc references to it
pub fn get_class(&self, class_name: &str) -> Result<Arc<Class>, Error> {
println!("get_class {}", class_name);
unsafe {
let entry = CLASSDEFS.entry(class_name.into());
let entry = entry.or_insert_with(|| {
println!("read class {} ", class_name);
let resolved_path = find_class(&self.classpath, class_name).unwrap();
// println!("full path {}", resolved_path);
let bytecode = read_bytecode(resolved_path).unwrap();
let mut class = load_class(bytecode).unwrap();
let super_class_name = class.super_class_name.as_ref();
if let Some(super_class_name) = super_class_name {
if let Ok(super_class) = self.get_class(&super_class_name) {
class.super_class = Some(super_class.clone());
}
}
class.initialize();
Arc::new(class)
});
Ok(entry.clone())
}
}
pub fn new_instance(class: Arc<Class>) -> Object {
let mut class = class;
pub fn new_instance(class: Arc<RefCell<Class>>) -> Object {
let mut instance = Object::new(class.clone());
instance
}
@ -118,25 +88,27 @@ impl Vm {
method_name: &str,
args: Vec<UnsafeValue>,
) -> Result<UnsafeValue, Error> {
let mut local_params: Vec<Option<UnsafeValue>> =
args.clone().iter().map(|e| Some(e.clone())).collect();
println!("execute {}.{}", class_name, method_name);
let class = self.get_class(class_name)?;
let method = class.get_method(method_name)?;
if method.is(Modifier::Native) {
let return_value = invoke_native(class.clone(), method);
unsafe {
match *return_value.get() {
Void => {}
_ => {
self.local_stack().push_arc(return_value.clone());
}
}
}
let class = get_class(self, class_name)?;
self.execute_class(class, method_name, args)
}
pub fn execute_class(
&mut self,
class: Arc<RefCell<Class>>,
method_name: &str,
args: Vec<UnsafeValue>,
) -> Result<UnsafeValue, Error> {
let this_class = class;
println!("execute {}.{}", this_class.borrow().name, method_name);
let method = this_class.clone().borrow().get_method(method_name)?.clone();
let mut local_params: Vec<Option<UnsafeValue>> =
args.clone().iter().map(|e| Some(e.clone())).collect();
if method.is(Modifier::Native) {
return Ok(invoke_native(method, args));
}
if let AttributeType::Code(code) = method.attributes.get("Code").unwrap() {
let stackframe = StackFrame::new(class_name, method_name);
let stackframe = StackFrame::new(&this_class.borrow().name, &method.name());
self.stack.push(stackframe);
let mut pc = &mut 0;
@ -297,23 +269,45 @@ impl Vm {
return Ok(Value::void());
}
GETSTATIC => {
let cp_index = read_u16(&code.opcodes, pc);
let (class_index, _field_name_and_type_index) =
class.cp_field_ref(&cp_index).unwrap(); // all these unwraps are safe as long as the class is valid
let class_name_index = class.cp_class_ref(class_index).unwrap();
let class_name = class.cp_utf8(class_name_index).unwrap();
let class = self.get_class(class_name.as_str())?;
// println!("{:?}", class); //TODO
}
GETFIELD => unsafe {
let borrow = this_class.borrow();
let cp_index = read_u16(&code.opcodes, pc);
let (class_index, field_name_and_type_index) =
class.cp_field_ref(&cp_index).unwrap();
borrow.cp_field_ref(&cp_index).unwrap(); // all these unwraps are safe as long as the class is valid
let (name_index, _) = borrow.cp_name_and_type(field_name_and_type_index).unwrap();
let (name) = borrow.cp_utf8(name_index).unwrap();
let class_name_index = borrow.cp_class_ref(class_index).unwrap();
let class_name = borrow.cp_utf8(class_name_index).unwrap();
let that = get_class(self, class_name.as_str())?;
let borrow = that.borrow();
let (_, val_index) = borrow.static_field_mapping.get(class_name).unwrap().get(name).unwrap();
self.local_stack().push_arc(this_class.borrow().static_data.get(*val_index).unwrap().clone());
}
PUTSTATIC => {
println!("putstatic");
let mut borrow = this_class.borrow_mut();
let cp_index = read_u16(&code.opcodes, pc);
let (class_index, field_name_and_type_index) =
borrow.cp_field_ref(&cp_index).unwrap(); // all these unwraps are safe as long as the class is valid
let (name_index, _) = borrow.cp_name_and_type(field_name_and_type_index).unwrap();
let (name) = borrow.cp_utf8(name_index).unwrap();
let class_name_index = borrow.cp_class_ref(class_index).unwrap();
let class_name = borrow.cp_utf8(class_name_index).unwrap();
let that = get_class(self, class_name.as_str())?;
let that_borrow = that.borrow();
let (_, val_index) = that_borrow.static_field_mapping.get(class_name).unwrap().get(name).unwrap();
let value = self.local_stack().pop()?;
borrow.static_data[*val_index] = value;
}
GETFIELD => unsafe {
let borrow = this_class.borrow();
let cp_index = read_u16(&code.opcodes, pc);
let (class_index, field_name_and_type_index) =
borrow.cp_field_ref(&cp_index).unwrap();
let (field_name_index, _) =
class.cp_name_and_type(field_name_and_type_index).unwrap();
let class_name_index = class.cp_class_ref(class_index).unwrap();
let class_name = class.cp_utf8(class_name_index).unwrap();
let field_name = class.cp_utf8(field_name_index).unwrap();
borrow.cp_name_and_type(field_name_and_type_index).unwrap();
let class_name_index = borrow.cp_class_ref(class_index).unwrap();
let class_name = borrow.cp_utf8(class_name_index).unwrap();
let field_name = borrow.cp_utf8(field_name_index).unwrap();
let mut objectref = self.local_stack().pop()?;
if let Value::Ref(instance) = &mut *objectref.get() {
@ -324,14 +318,15 @@ impl Vm {
}
},
PUTFIELD => unsafe {
let borrow = this_class.borrow();
let cp_index = read_u16(&code.opcodes, pc);
let (class_index, field_name_and_type_index) =
class.cp_field_ref(&cp_index).unwrap();
borrow.cp_field_ref(&cp_index).unwrap();
let (field_name_index, _) =
class.cp_name_and_type(field_name_and_type_index).unwrap();
let class_name_index = class.cp_class_ref(class_index).unwrap();
let class_name = class.cp_utf8(class_name_index).unwrap();
let field_name = class.cp_utf8(field_name_index).unwrap();
borrow.cp_name_and_type(field_name_and_type_index).unwrap();
let class_name_index = borrow.cp_class_ref(class_index).unwrap();
let class_name = borrow.cp_utf8(class_name_index).unwrap();
let field_name = borrow.cp_utf8(field_name_index).unwrap();
let value = self.local_stack().pop()?;
let mut objectref = self.local_stack().pop()?;
@ -388,12 +383,13 @@ impl Vm {
},
NEW => {
let class_index = &read_u16(&code.opcodes, pc);
let class_name_index = class.cp_class_ref(class_index).unwrap();
let class_name = class.cp_utf8(class_name_index).unwrap();
let class = self.get_class(class_name)?;
let borrow = this_class.borrow();
let class_name_index = borrow.cp_class_ref(class_index).unwrap();
let class_name = borrow.cp_utf8(class_name_index).unwrap();
let class_to_instantiate = get_class(self, class_name)?;
let object = Arc::new(UnsafeCell::new(ObjectRef::Object(Box::new(
Vm::new_instance(class),
Vm::new_instance(class_to_instantiate),
))));
self.local_stack().push(Value::Ref(Arc::clone(&object)));
self.heap.new_object(object);

Binary file not shown.

View file

@ -1,5 +1,11 @@
public class Main {
final static String a;
static{
a="";
}
public static void main(String[] args){
FloatBean f = new FloatBean();
f.setValue(42F);