java.rs/src/classmanager.rs
2023-11-26 15:57:54 +01:00

387 lines
No EOL
15 KiB
Rust

use std::cell::RefCell;
use std::collections::{HashMap, LinkedList};
use std::rc::Rc;
use log::debug;
use once_cell::sync::Lazy;
use crate::class::{Class, ClassId, TypeIndex};
use crate::classloader;
use crate::classloader::classdef::{ClassDef, Method, Modifier};
use crate::vm::object::{Object, ObjectRef};
use crate::value::Value;
use crate::value::Value::*;
use crate::vm::runtime::Vm;
static PRIMITIVES: Lazy<Vec<&str>> = Lazy::new(|| vec!["B", "S", "I", "J", "F", "D", "Z", "J", "C"]);
//TODO less pubs
pub struct ClassManager {
static_class_data: HashMap<ClassId, Vec<Value>>,
// sequence for passing new classIds
current_id: ClassId,
// the classpath
classpath: Vec<String>,
//references to classdefs, ie the static class info
pub(crate) classdefs: HashMap<ClassId, ClassDef>,
// references to the runtime class
pub classes: HashMap<ClassId, Class>,
pub names: HashMap<String, ClassId>,
pub class_objects: HashMap<ClassId, Value>,
}
impl ClassManager {
pub fn new(classpath: Vec<String>) -> Self {
Self {
static_class_data: HashMap::new(),
current_id: 0,
classdefs: HashMap::new(),
classes: HashMap::new(),
class_objects: HashMap::new(),
names: HashMap::new(),
classpath,
}
}
pub fn get_static(&self, id: &ClassId, index: usize) -> Value {
self.static_class_data.get(id).unwrap()[index].clone()
}
pub fn set_static(&mut self, id: ClassId, index: usize, value: Value) {
self.static_class_data.get_mut(&id).unwrap()[index] = value;
}
pub fn get_classobject(&self, id: &ClassId) -> Option<&Value> {
self.class_objects.get(id)
}
pub fn get_class_by_id(&mut self, id: &ClassId) -> Option<&Class> {
if !self.classes.contains_key(id) {
let name = self.classdef_name(id);
if name.is_some() {
self.add_class(&name.unwrap());
}
}
self.classes.get(id)
}
pub fn classdef_name(&self, id: &ClassId) -> Option<String> {
self.classdefs.get(id).map(|c| c.name().to_owned()) //drops borrow to self here
}
pub fn get_classid(&self, name: &str) -> &ClassId {
self.names.get(name).unwrap()
}
pub(crate) fn get_classdef(&self, id: &ClassId) -> &ClassDef {
self.classdefs.get(&id).unwrap()
}
/// loads the class if not already there
pub fn load_class_by_name(&mut self, name: &str) {
debug!("load class {}", name);
// determine no of dimensions and get type of array if any
let mut chars = name.chars();
let mut num_dims = 0;
while let Some(c) = chars.nth(num_dims) {
if c == '[' {
num_dims += 1;
} else {
break;
}
}
let mut type_name = name[num_dims..name.len()].to_owned();
if num_dims > 0 {
if !PRIMITIVES.contains(&type_name.as_str()) {
type_name = type_name[1..type_name.len()].to_owned();
}
let id = self.get_or_new_id(name.into());
if !self.class_objects.contains_key(&id) {
let cls = self.get_class_by_name("java/lang/Class").unwrap();
let mut instance = Object::new(cls);
instance.set(cls, "java/lang/Class", "name", Utf8(name.into()));
let instance = Ref(ObjectRef::Object(Rc::new(RefCell::new(instance))));
self.class_objects.insert(id, instance);
}
} else {
// in cache?
let id = self.names.get(&type_name);
match id {
Some(id) => if self.classes.get(id).is_none() {
self.add_class(&type_name);
}
None => {
self.add_class(&type_name);
}
}
}
}
pub(crate) fn get_method(&self, class_name: &str, method_name: &str) -> Option<&Method> {
let class_id = self.get_classid(class_name);
let classdef = self.get_classdef(class_id);
classdef.get_method(method_name)
}
/// get optional classid from cache
pub fn get_mut_class_by_name(&mut self, name: &str) -> Option<&mut Class> {
let id = self.names.get(name);
self.classes.get_mut(id.unwrap())
}
pub fn get_class_by_name(&self, name: &str) -> Option<&Class> {
debug!("{}", name);
let id = self.names.get(name);
self.classes.get(id.unwrap())
}
/// adds the class and calculates the 'offset' of it's fields (static and non-static)
/// this is a map (declared-class -> map (field-name -> type_index))
/// -> fields are not polymorphic, meaning a field can exist in the class and in the superclass and can be addressed individually (no hiding)
/// -> the bytecode will know what declared type field is needed
///
/// type_index is tuple (field-type, index)
/// field-type is a string
/// index is an index into the list of values that object instances will use to store the values
///
/// the function also instantiates a (java.lang.) Class object for each loaded class
fn add_class(&mut self, name: &str) -> ClassId {
debug!("add class {}", name);
let this_classid = self.load(name);
let this_classdef = self.classdefs.get(&this_classid).unwrap();
//compute indices to fields
let mut object_field_mapping = HashMap::new();
let mut static_field_mapping = HashMap::new();
let object_field_map_index: &mut usize = &mut 0;
let static_field_map_index: &mut usize = &mut 0;
let mut current_id = Some(this_classid);
let mut current_classdef;
let mut parents = LinkedList::new();
while let Some(c) = current_id {
parents.push_front(current_id.unwrap());
current_classdef = self.classdefs.get(&c).unwrap();
Self::add_fields_for_this_or_parents(&mut object_field_mapping, &mut static_field_mapping, object_field_map_index, static_field_map_index, current_classdef);
current_id = current_classdef.super_class.as_ref()
.map(|i| current_classdef.cp_class_name(i).to_owned())
.map(|n| *self.names.get(&n).unwrap());
}
//handrolled references to superclass and interfaces
let superclass_id = this_classdef.super_class.as_ref()
.map(|i| this_classdef.cp_class_name(i).to_owned())
.map(|n| *self.names.get(&n).unwrap());
let interface_ids: Vec<ClassId> = this_classdef.interfaces.iter()
.map(|i| this_classdef.cp_class_name(i).to_owned())
.map(|n| *self.names.get(n.as_str()).unwrap())
.collect();
// initial values for static fields (before static init)
self.static_class_data.insert(this_classid, Self::set_field_data(&static_field_mapping));
self.classes.insert(this_classid, Class {
id: this_classid,
initialized: false,
name: name.into(),
superclass: superclass_id,
parents,
interfaces: interface_ids,
object_field_mapping,
static_field_mapping,
});
// add a new Class instance
if name != "java/lang/Class" {
let cls = self.get_class_by_name("java/lang/Class").unwrap();
let mut instance = Object::new(cls);
instance.set(cls, "java/lang/Class", "name", Value::Utf8(name.into()));
let instance = Ref(ObjectRef::Object(Rc::new(RefCell::new(instance))));
self.class_objects.insert(this_classid, instance);
}
// run static init
if this_classdef.methods.contains_key("<clinit>()V") {
Vm { stack: Vec::new()}.run2(self, this_classid,"<clinit>()V");
}
this_classid
}
/// like described above
fn add_fields_for_this_or_parents(object_field_mapping: &mut HashMap<String, HashMap<String, TypeIndex>>,
static_field_mapping: &mut HashMap<String, HashMap<String, TypeIndex>>,
object_field_map_index: &mut usize,
static_field_map_index: &mut usize,
current_classdef: &ClassDef) {
let mut instance_field_mappings: HashMap<String, TypeIndex> = HashMap::new();
let mut static_field_mappings: HashMap<String, TypeIndex> = HashMap::new();
for (field_name, field) in &current_classdef.fields {
if !field.is(Modifier::Static) {
instance_field_mappings.insert(field_name.to_owned(),
TypeIndex::new(field.type_of().to_owned(), *object_field_map_index));
*object_field_map_index += 1;
} else {
static_field_mappings.insert(field_name.to_owned(),
TypeIndex::new(field.type_of().to_owned(), *static_field_map_index));
*static_field_map_index += 1;
}
}
object_field_mapping.insert(current_classdef.name().to_owned(), instance_field_mappings);
static_field_mapping.insert(current_classdef.name().to_owned(), static_field_mappings);
}
/// loads the class and recursively its dependencies
fn load(&mut self, name: &str) -> ClassId {
let (id, mut classes_to_load) = self.load_class_and_deps(name);
while !classes_to_load.is_empty() {
if let Some(classname) = classes_to_load.pop() {
classes_to_load.append(&mut self.load_class_and_deps(classname.as_str()).1);
}
}
debug!("new class {} -> {}", name, id);
id
}
/// loads the class and returns it's dependencies
fn load_class_and_deps(&mut self, class_name: &str) -> (ClassId, Vec<String>) {
debug!("load {}", class_name);
let class_name = class_name.to_owned().replace(".", "/");
let id = self.get_or_new_id(class_name.clone());
let classdef = self.classdefs
.entry(id)
.or_insert_with(|| classloader::get_classdef(&self.classpath, class_name.as_str()).expect("ClassNotFound"));
(id, inspect_dependencies(classdef))
}
fn get_or_new_id(&mut self, name: String) -> ClassId {
let id = *self.names.entry(name).or_insert_with(|| {
self.current_id += 1;
self.current_id
});
id
}
pub(crate) fn set_field_data(field_mapping: &HashMap<String, HashMap<String, TypeIndex>>) -> Vec<Value> {
let mut field_data = vec![Null; n_fields(field_mapping)];
for (_, this_class) in field_mapping {
for (_name, type_index) in this_class {
let value = match type_index.type_name.as_str() {
"Z" => BOOL(false),
"B" => I32(0),
"S" => I32(0),
"I" => I32(0),
"J" => I64(0),
"F" => F32(0.0),
"D" => F64(0.0),
_ => Null,
};
field_data[type_index.index] = value.into();
}
}
field_data
}
}
pub(crate) fn n_fields(field_mapping: &HashMap<String, HashMap<String, TypeIndex>>) -> usize {
field_mapping
.iter()
.map(|(_, v)| v.len())
.reduce(|acc, e| acc + e)
.unwrap()
}
pub(crate) fn inspect_dependencies(classdef: &ClassDef) -> Vec<String> {
let mut classes_to_load: Vec<String> = vec![];
if let Some(superclass) = &classdef.super_class {
classes_to_load.push(classdef.cp_class_name(superclass).into());
}
for interface in &classdef.interfaces {
classes_to_load.push(classdef.cp_class_name(interface).into());
}
classes_to_load
}
#[cfg(test)]
mod test {
use std::rc::Rc;
use crate::classloader::classdef::{CpEntry, Field};
use super::*;
#[test]
fn add_class() {
let mut names = HashMap::new();
names.insert("C".to_owned(), 1);
names.insert("java/lang/String".to_owned(), 2);
names.insert("java/lang/Class".to_owned(), 3);
let mut constant_pool = HashMap::new();
constant_pool.insert(0, CpEntry::ClassRef(1));
constant_pool.insert(1, CpEntry::Utf8("C".into()));
constant_pool.insert(2, CpEntry::NameAndType(3, 4));
constant_pool.insert(3, CpEntry::Utf8("name".into()));
constant_pool.insert(4, CpEntry::Utf8("java/lang/String".into()));
constant_pool.insert(5, CpEntry::Utf8("Ljava/lang/String;".into()));
constant_pool.insert(6, CpEntry::ClassRef(4));
constant_pool.insert(7, CpEntry::Utf8("java/lang/Class".into()));
constant_pool.insert(8, CpEntry::ClassRef(7));
constant_pool.insert(9, CpEntry::Utf8("value1".into()));
constant_pool.insert(10, CpEntry::Utf8("value2".into()));
let constant_pool = Rc::new(constant_pool);
// give class C a fields called value
let mut c_fields = HashMap::new();
c_fields.insert("value1".to_owned(), Field::new(constant_pool.clone(), 0, 9, 5, HashMap::new(), 0));
c_fields.insert("value2".to_owned(), Field::new(constant_pool.clone(), 0, 10, 5, HashMap::new(), 0));
// Class needs a public (non-static) field called name
let mut class_fields = HashMap::new();
class_fields.insert("name".to_owned(), Field::new(constant_pool.clone(), 1, 2, 5, HashMap::new(), 0));
let mut classdefs = HashMap::new();
classdefs.insert(1, ClassDef::new(0, 0, constant_pool.clone(), 0, 0, None, vec![], c_fields, HashMap::new(), HashMap::new()));
// preload java.lang.String
classdefs.insert(2, ClassDef::new(0, 0, constant_pool.clone(), 0, 6, None, vec![], HashMap::new(), HashMap::new(), HashMap::new()));
// preload java.lang.Class
classdefs.insert(3, ClassDef::new(0, 0, constant_pool, 0, 8, None, vec![], class_fields, HashMap::new(), HashMap::new()));
let mut classes = HashMap::new();
let mut class_field_mapping = HashMap::new();
let mut fields_declared_by_java_lang_class = HashMap::new();
fields_declared_by_java_lang_class.insert("name".to_owned(), TypeIndex { type_name: "java/lang/String".into(), index: 0 });
class_field_mapping.insert("java/lang/Class".to_owned(), fields_declared_by_java_lang_class);
classes.insert(3, Class { id: 3, initialized: false, name: "".into(), superclass: None, parents: LinkedList::new(), interfaces: vec![], object_field_mapping: class_field_mapping, static_field_mapping: HashMap::new() });
let mut cm = ClassManager {
static_class_data: HashMap::new(),
classes,
class_objects: HashMap::new(),
classdefs,
current_id: 1,
names,
classpath: Vec::new(),
};
let c_id = cm.add_class("C");
let loaded_class = cm.classes.get(&c_id).unwrap();
assert_eq!(0, loaded_class.object_field_mapping.get("C").unwrap().get("value1").unwrap().index);
assert_eq!(1, loaded_class.object_field_mapping.get("C").unwrap().get("value2").unwrap().index);
}
}