From 76304fa616857072bd2e525006bcec95ac83bcd6 Mon Sep 17 00:00:00 2001 From: Shautvast Date: Sun, 29 Oct 2023 18:11:12 +0100 Subject: [PATCH] hit a wall --- src/class.rs | 141 +++++++++++++++++++++++++++++++------------ src/heap.rs | 12 ++-- src/main.rs | 2 + src/native.rs | 43 +++++++------ src/opcodes.rs | 1 + src/vm/operations.rs | 47 ++++++++------- 6 files changed, 161 insertions(+), 85 deletions(-) diff --git a/src/class.rs b/src/class.rs index 9135d8e..76e78cf 100644 --- a/src/class.rs +++ b/src/class.rs @@ -21,6 +21,8 @@ pub static mut CLASSES: Lazy> = Lazy::new(|| HashMap::new // 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(crate) fn get_class( vm: &mut Vm, class_name: &str, @@ -43,6 +45,7 @@ pub(crate) fn get_class( // not sure why I have to create the clones first let clone2 = class.clone(); let clone3 = class.clone(); + let clone4 = class.clone(); let mut some_class = class.clone(); if class_name != "java/lang/Class" { @@ -55,7 +58,7 @@ pub(crate) fn get_class( // must not enter here twice! clone2.borrow_mut().inited = true; - let mut supers = vec![]; + let mut supers = vec![]; if class_name != "java/lang/Class" { loop { let super_class_name = some_class @@ -69,7 +72,7 @@ pub(crate) fn get_class( if let Ok(super_class) = get_class(vm, &super_class_name) { supers.push(super_class.clone()); some_class = super_class.clone(); - clone2.borrow_mut().super_class = Some(super_class); + clone4.borrow_mut().super_class = Some(super_class); } else { break; } @@ -79,7 +82,8 @@ pub(crate) fn get_class( } } } - Class::initialize_fields(clone3); + + Class::initialize_fields(clone3, supers); let clinit = clone2.borrow().methods.contains_key("()V"); let name = &clone2.borrow().name.to_owned(); if clinit { @@ -108,11 +112,27 @@ pub struct Class { pub methods: HashMap>, pub attributes: HashMap, pub inited: bool, - pub(crate) object_field_mapping: HashMap>, - // first key: this/super/supersuper-name(etc), second key: fieldname, value (type, index) - pub(crate) static_field_mapping: HashMap>, - // first key: this/super/supersuper-name(etc), second key: fieldname, value (type, index) - pub(crate) static_data: Vec>, + + // lookup index and type from the name + pub(crate) object_field_mapping: HashMap>, + pub(crate) static_field_mapping: HashMap>, + // static fields + pub(crate) static_data: Vec, +} + +#[derive(Debug)] +pub(crate) struct TypeIndex { + pub type_name: String, + pub index: usize, +} + +impl TypeIndex { + pub(crate) fn new(type_name: String, index: usize) -> Self { + Self { + type_name, + index, + } + } } impl Class { @@ -176,7 +196,7 @@ 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_fields(class: Arc>) { + pub fn initialize_fields(class: Arc>, super_classes: Vec>>) { let mut this_field_mapping = HashMap::new(); let mut static_field_mapping = HashMap::new(); let mut object_field_map_index: usize = 0; @@ -197,9 +217,13 @@ impl Class { class.borrow_mut().static_data = static_data; } + /// for all static and non-static fields on the class compute an index + /// the result of this function is that the class object contains mappings + /// from the field name to the index. This index will be used to store the + /// actual data later in a Vector. fn add_field_mappings( - this_field_mapping: &mut HashMap>, - static_field_mapping: &mut HashMap>, + this_field_mapping: &mut HashMap>, + static_field_mapping: &mut HashMap>, class: Arc>, object_field_map_index: &mut usize, static_field_map_index: &mut usize, @@ -214,11 +238,21 @@ impl Class { 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() { + // // same for super class + // if let Some(super_class) = class.borrow().super_class.as_ref() { + // Class::add_field_mappings( + // this_field_mapping, + // static_field_mapping, + // super_class.clone(), + // object_field_map_index, + // static_field_map_index, + // ); + // } + for c in &class.borrow().super_classes { Class::add_field_mappings( this_field_mapping, static_field_mapping, - super_class.clone(), + c.clone(), object_field_map_index, static_field_map_index, ); @@ -226,13 +260,14 @@ impl Class { } // part of the initialize procedure + /// here the actual indices are created fn map_fields( class: Arc>, object_field_map_index: &mut usize, static_field_map_index: &mut usize, ) -> ( - HashMap, - HashMap, + HashMap, + HashMap, ) { let mut this_fields = HashMap::new(); //fields in class are stored per class and every superclass. let mut static_fields = HashMap::new(); //fields in class are stored per class and every superclass. @@ -241,13 +276,13 @@ impl Class { if field.is(Modifier::Static) { static_fields.insert( name.to_owned(), - (field.type_of().to_owned(), *static_field_map_index), + TypeIndex::new(field.type_of().to_owned(), *static_field_map_index), ); *static_field_map_index += 1; } else { this_fields.insert( name.to_owned(), - (field.type_of().to_owned(), *object_field_map_index), + TypeIndex::new(field.type_of().to_owned(), *object_field_map_index), ); //name => (type,index) *object_field_map_index += 1; } @@ -255,14 +290,17 @@ impl Class { (this_fields, static_fields) } + /// the bytecode version pub fn get_version(&self) -> (u16, u16) { (self.major_version, self.minor_version) } + /// get a method by signature pub fn get_method(&self, name: &str) -> Option<&Rc> { self.methods.get(name) } + /// get the class name fn class_name( super_class_index: u16, constant_pool: Rc>, @@ -281,12 +319,15 @@ impl Class { } } - pub(crate) fn set_field_data(class: Arc>) -> Vec> { - let mut field_data = vec![None; class.borrow().n_static_fields()]; + /// creates default values for every field, ie null for objects, 0 for integers etc + /// this is the step before the constructor/static initializer can be called to set hardcoded + /// or computed values. + pub(crate) fn set_field_data(class: Arc>) -> Vec { + let mut field_data = vec![Value::Null; class.borrow().n_static_fields()]; for (_, this_class) in &class.borrow().static_field_mapping { - for (_name, (fieldtype, index)) in this_class { - let value = match fieldtype.as_str() { + for (_name, type_index) in this_class { + let value = match type_index.type_name.as_str() { "Z" => Value::BOOL(false), "B" => Value::I32(0), "S" => Value::I32(0), @@ -297,7 +338,7 @@ impl Class { _ => Value::Null, }; // println!("{} = {:?}", name, value); - field_data[*index] = Some(value.into()); + field_data[type_index.index] = value.into(); } } @@ -306,51 +347,51 @@ impl Class { // convienence methods for data from the constantpool - pub fn cp_field_ref(&self, index: &u16) -> Option<(&u16, &u16)> { + pub fn cp_field_ref(&self, index: &u16) -> (&u16, &u16) { if let CpEntry::Fieldref(class_index, name_and_type_index) = self.constant_pool.get(index).unwrap() { - Some((class_index, name_and_type_index)) + (class_index, name_and_type_index) } else { - None + unreachable!("should be field") } } /// both methodRef and InterfaceMethodRef /// returns (class_index, name_and_type_index) - pub fn cp_method_ref(&self, index: &u16) -> Option<(&u16, &u16)> { + pub fn cp_method_ref(&self, index: &u16) -> (&u16, &u16) { if let CpEntry::MethodRef(class_index, name_and_type_index) | CpEntry::InterfaceMethodref(class_index, name_and_type_index) = self.constant_pool.get(index).unwrap() { - Some((class_index, name_and_type_index)) + (class_index, name_and_type_index) } else { - None + unreachable!("should be method") } } - pub fn cp_class_ref(&self, index: &u16) -> Option<&u16> { + pub fn cp_class_ref(&self, index: &u16) -> &u16 { if let CpEntry::ClassRef(name_index) = self.constant_pool.get(index).unwrap() { - Some(name_index) + name_index } else { - None + unreachable!("should be class entry") } } - pub fn cp_utf8(&self, index: &u16) -> Option<&String> { + pub fn cp_utf8(&self, index: &u16) -> &String { if let CpEntry::Utf8(utf8) = self.constant_pool.get(index).unwrap() { - Some(utf8) + utf8 } else { - None + unreachable!("should be utf8 entry") } } - pub fn cp_name_and_type(&self, index: &u16) -> Option<(&u16, &u16)> { + pub fn cp_name_and_type(&self, index: &u16) -> (&u16, &u16) { if let CpEntry::NameAndType(name_index, type_index) = self.constant_pool.get(index).unwrap() { - Some((name_index, type_index)) + (name_index, type_index) } else { - None + unreachable!("should be name_and_type entry") } } } @@ -583,20 +624,42 @@ impl MethodCode { #[derive(Debug, Clone)] pub enum Value { - Void, // variant returned for void methods - Null, + Void, // 'pointer' to nothing + Null, + // primitives I32(i32), I64(i64), F32(f32), F64(f64), BOOL(bool), CHAR(char), + // objects and arrays Ref(UnsafeRef), + // special object Utf8(String), } +impl Value { + // panics if not correct type + pub fn into_i32(self) -> i32 { + if let Value::I32(v) = self { + v + } else { + panic!(); + } + } + + pub fn into_object(self) -> UnsafeRef { + if let Value::Ref(v) = self { + v + } else { + panic!(); + } + } +} + pub type UnsafeRef = Arc>; pub fn unsafe_ref(object: ObjectRef) -> UnsafeRef { diff --git a/src/heap.rs b/src/heap.rs index 9d90f13..797d1f0 100644 --- a/src/heap.rs +++ b/src/heap.rs @@ -113,8 +113,8 @@ impl Object { let mut field_data = Vec::with_capacity(class.borrow().n_object_fields()); for (_, fields) in &class.borrow().object_field_mapping { - for (_, (fieldtype, _)) in fields { - let value = match fieldtype.as_str() { + for (_, type_index) in fields { + let value = match type_index.type_name.as_str() { "Z" => Value::BOOL(false), "B" => Value::I32(0), "S" => Value::I32(0), @@ -133,24 +133,24 @@ impl Object { pub fn set(&mut self, class_name: &String, field_name: &String, value: Value) { let borrow = self.class.borrow(); - let (_type, index) = borrow + let type_index = borrow .object_field_mapping .get(class_name) .unwrap() .get(field_name) .unwrap(); - self.data[*index] = value; + self.data[type_index.index] = value; } pub fn get(&mut self, class_name: &String, field_name: &String) -> &Value { let borrow = self.class.borrow(); - let (_type, index) = borrow + let type_index = borrow .object_field_mapping .get(class_name) .unwrap() .get(field_name) .unwrap(); - &self.data[*index] + &self.data[type_index.index] } // fn get_field_name(&self, cp_index: &u16) -> &str { diff --git a/src/main.rs b/src/main.rs index 8342cc6..166bd34 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,8 @@ use std::io::Error; use java_rs::vm::Vm; + + fn main() -> Result<(), Error> { // TODO cmdline args // TODO build index for package -> jarfile? diff --git a/src/native.rs b/src/native.rs index 97dfe95..2153d80 100644 --- a/src/native.rs +++ b/src/native.rs @@ -1,43 +1,52 @@ #![allow(non_snake_case)] -use log::info; +use std::ptr::hash; +use anyhow::Error; +use log::{debug, info}; use once_cell::sync::Lazy; -use crate::class::{unsafe_ref, Value}; +use crate::class::{get_class, unsafe_ref, Value}; use crate::class::Value::Void; use crate::heap::ObjectRef; +use crate::heap::ObjectRef::Object; +use crate::vm::Vm; -pub fn invoke_native(class_name: &String, method_name: &String, _args: Vec) -> Value { +pub fn invoke_native(vm: &mut Vm, class_name: &String, method_name: &String, _args: Vec) -> Result { info!("native {}.{}", class_name, method_name); match class_name.as_str() { - "java/lang/Class" => java_lang_class(method_name), - "jdk/internal/util/SystemProps$Raw" => jdk_internal_util_SystemProps_Raw(method_name), - _ => Void + "java/lang/Class" => java_lang_class(vm, method_name), + "jdk/internal/util/SystemProps$Raw" => jdk_internal_util_SystemProps_Raw(vm, method_name), + _ => Ok(Void) } } -fn java_lang_class(method_name: &String) -> Value { - match method_name.as_str() { +fn java_lang_class(_vm: &mut Vm, method_name: &String) -> Result { + Ok(match method_name.as_str() { "desiredAssertionStatus0(Ljava/lang/Class;)Z" => Value::BOOL(false), _ => Void - } + }) } -fn jdk_internal_util_SystemProps_Raw(method_name: &String) -> Value { +fn jdk_internal_util_SystemProps_Raw(vm: &mut Vm,method_name: &String) -> Result { match method_name.as_str() { "platformProperties()[Ljava/lang/String;" => systemProps(), - "cmdProperties()Ljava/util/HashMap;" => cmdProps(), //TODO ability to instantiate classes here - "vmProperties()[Ljava/lang/String;" => cmdProps(), - _ => Void + "cmdProperties()Ljava/util/HashMap;" => cmdProps(vm), //TODO ability to instantiate classes here + "vmProperties()[Ljava/lang/String;" => cmdProps(vm), + _ => Ok(Void) } } -fn cmdProps() -> Value { - Value::Null +fn cmdProps(vm: &mut Vm,) -> Result { + let hashmap_class = get_class(vm, "java/util/HashMap")?; + let hashmap = Vm::new_instance(hashmap_class); + let hashmap = Value::Ref(unsafe_ref(Object(Box::new(hashmap)))); + vm.execute_special("java/util/HashMap", "()V", vec![hashmap.clone()]); + unsafe {debug!("hashmap {:?}", *hashmap.into_object().get());} + panic!() } -fn systemProps() -> Value { +fn systemProps() -> Result { unsafe { let props: Lazy> = Lazy::new(|| { let mut vec: Vec = Vec::new(); @@ -122,6 +131,6 @@ fn systemProps() -> Value { vec }); - Value::Ref(unsafe_ref(ObjectRef::StringArray(props.to_vec()))) + Ok(Value::Ref(unsafe_ref(ObjectRef::StringArray(props.to_vec())))) } } \ No newline at end of file diff --git a/src/opcodes.rs b/src/opcodes.rs index 9ec7a27..e1c7628 100644 --- a/src/opcodes.rs +++ b/src/opcodes.rs @@ -397,6 +397,7 @@ pub const OPCODES:Lazy> = Lazy::new(|| { opcodes[_DSUB as usize] = "_DSUB" ; opcodes[_FMUL as usize] = "_FMUL" ; opcodes[_DMUL as usize] = "_DMUL" ; + opcodes[IDIV as usize] = "IDIV" ; opcodes[_FDIV as usize] = "_FDIV" ; opcodes[_DDIV as usize] = "_DDIV" ; opcodes[_FREM as usize] = "_FREM" ; diff --git a/src/vm/operations.rs b/src/vm/operations.rs index 8084664..0ba7a89 100644 --- a/src/vm/operations.rs +++ b/src/vm/operations.rs @@ -2,36 +2,37 @@ use std::cell::RefCell; use std::collections::HashMap; use std::rc::Rc; use std::sync::Arc; -use anyhow::{anyhow, Error}; -use Value::I32; -use crate::class::{Class, get_class, Method, Value}; -use crate::class::Value::{F32, F64, I64, Ref, Utf8}; +use anyhow::Error; + +use crate::class::{Class, get_class, Value}; use crate::classloader::CpEntry; -use crate::heap::ObjectRef; use crate::vm::Vm; use crate::vm::vm::{Invocation, MethodSignature}; -pub(crate) fn get_static(vm: &mut Vm, this_class: Arc>, field_index: u16) -> Value { - let borrow = this_class.borrow(); - let (class_index, field_name_and_type_index) = - borrow.cp_field_ref(&field_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(); +/// the place for opcode implementations that are a bit long - let that_class_name_index = borrow.cp_class_ref(class_index).unwrap(); - let that_class_name = borrow.cp_utf8(that_class_name_index).unwrap(); - let that = get_class(vm, that_class_name.as_str()).unwrap(); - let that_borrow = that.borrow(); - let (_, val_index) = that_borrow +// GET_STATIC opcode +pub(crate) fn get_static(vm: &mut Vm, this_class: Arc>, field_index: u16) -> Result { + let this_class = this_class.borrow(); + let (class_index, field_name_and_type_index) = + this_class.cp_field_ref(&field_index); // all these unwraps are safe as long as the class is valid + let (name_index, _) = + this_class.cp_name_and_type(field_name_and_type_index); + let field_name = this_class.cp_utf8(name_index); + + let that_class_name_index = this_class.cp_class_ref(class_index); + let that_class_name = this_class.cp_utf8(that_class_name_index); + let that_class = get_class(vm, that_class_name.as_str())?; + let that_class = that_class.borrow(); + + let type_index = that_class .static_field_mapping .get(that_class_name) - .unwrap() - .get(name) - .unwrap(); - that_borrow - .static_data - .get(*val_index).unwrap().as_ref().unwrap().clone() + .unwrap()// safe because class for static field must be there + .get(field_name) + .unwrap(); // safe because field must be there + + Ok(that_class.static_data[type_index.index].clone()) } pub(crate) fn get_name_and_type(cp: Rc>, index: u16) -> Option {