broke through the wall. most unsafe gone. internal mutability gone. better design for classloading

This commit is contained in:
Shautvast 2023-11-05 07:51:47 +01:00
parent fa10621c56
commit ceeec215e4
17 changed files with 1335 additions and 1302 deletions

Binary file not shown.

View file

@ -1,126 +1,10 @@
use std::cell::{RefCell, UnsafeCell}; use std::collections::{HashMap, LinkedList};
use std::collections::HashMap;
use std::fmt;
use std::rc::Rc;
use std::sync::Arc;
use anyhow::Error; use crate::class::ObjectRef::*;
use log::info;
use once_cell::sync::Lazy;
use crate::classloader::{CpEntry, load_class}; pub type ClassId = usize;
use crate::heap::{ObjectRef};
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 #[derive(Debug, Clone)]
pub static mut CLASSDEFS: Lazy<HashMap<String, Arc<RefCell<Class>>>> = Lazy::new(|| HashMap::new());
//TODO add mutex..
pub static mut CLASSES: Lazy<HashMap<String, Value>> = 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(crate) fn get_class(
vm: &mut Vm,
class_name: &str,
) -> Result<Arc<RefCell<Class>>, Error> {
info!("get_class {}", class_name);
unsafe {
let class = CLASSDEFS.entry(class_name.into()).or_insert_with(|| {
// println!("read class {} ", class_name);
let resolved_path = find_class(&vm.classpath, class_name).unwrap();
let bytecode = read_bytecode(resolved_path).unwrap();
let class = load_class(bytecode).unwrap();
Arc::new(RefCell::new(class))
});
let clone = class.clone();
let inited = class.borrow().inited;
if !inited {
// 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" {
let klazz = get_class(vm, "java/lang/Class")?;
let mut class_instance = Vm::new_instance(klazz);
class_instance.set(&"java/lang/Class".to_owned(), &"name".to_owned(), Value::Utf8(class_name.into()));
CLASSES.insert(class_name.into(), Value::Ref(unsafe_ref(ObjectRef::Object(Box::new(class_instance)))));
}
// must not enter here twice!
clone2.borrow_mut().inited = true;
let mut supers = vec![];
if class_name != "java/lang/Class" {
loop {
let super_class_name = some_class
.clone()
.borrow()
.super_class_name
.as_ref()
.map(|n| n.to_owned());
{
if let Some(super_class_name) = super_class_name {
if let Ok(super_class) = get_class(vm, &super_class_name) {
supers.push(super_class.clone());
some_class = super_class.clone();
clone4.borrow_mut().super_class = Some(super_class);
} else {
break;
}
} else {
break;
}
}
}
}
Class::initialize_fields(clone3, supers);
let clinit = clone2.borrow().methods.contains_key("<clinit>()V");
let name = &clone2.borrow().name.to_owned();
if clinit {
vm.execute_special(name, "<clinit>()V", vec![]).unwrap();
}
}
Ok(clone)
}
}
/// the class definition as read from the class file + derived values
// TODO implement call to static initializers
#[derive(Debug)]
pub struct Class {
pub minor_version: u16,
pub major_version: u16,
pub constant_pool: Rc<HashMap<u16, CpEntry>>,
pub access_flags: u16,
pub name: String,
pub super_class_name: Option<String>,
pub super_class: Option<Type>,
pub super_classes: Vec<Type>,
pub interface_indices: Vec<u16>,
pub interfaces: Vec<Class>,
pub fields: HashMap<String, Field>,
pub methods: HashMap<String, Rc<Method>>,
pub attributes: HashMap<String, AttributeType>,
pub inited: bool,
// lookup index and type from the name
pub(crate) object_field_mapping: HashMap<String, HashMap<String, TypeIndex>>,
pub(crate) static_field_mapping: HashMap<String, HashMap<String, TypeIndex>>,
// static fields
pub(crate) static_data: Vec<Value>,
}
#[derive(Debug)]
pub(crate) struct TypeIndex { pub(crate) struct TypeIndex {
pub type_name: String, pub type_name: String,
pub index: usize, pub index: usize,
@ -135,43 +19,23 @@ impl TypeIndex {
} }
} }
// could move classdef into here. Saves one hashmap lookup
// have to look at ownership though
#[derive(Debug, Clone)]
pub struct Class {
pub id: ClassId,
pub initialized: bool,
pub name: String,
pub superclass: Option<ClassId>,
pub parents: LinkedList<ClassId>,
pub interfaces: Vec<ClassId>,
// lookup index and type from the name
pub(crate) object_field_mapping: HashMap<String, HashMap<String, TypeIndex>>,
pub(crate) static_field_mapping: HashMap<String, HashMap<String, TypeIndex>>,
// pub(crate) static_field_data: Vec<Value>,
}
impl Class { impl Class {
pub fn new(
minor_version: u16,
major_version: u16,
constant_pool: Rc<HashMap<u16, CpEntry>>,
access_flags: u16,
this_class: u16,
super_class_index: u16,
interface_indices: Vec<u16>,
fields: HashMap<String, Field>,
methods: HashMap<String, Rc<Method>>,
attributes: HashMap<String, AttributeType>,
) -> Self {
let name = Class::class_name(this_class, constant_pool.clone()).unwrap();
let super_class_name = Class::class_name(super_class_index, constant_pool.clone());
Self {
major_version,
minor_version,
constant_pool,
access_flags,
name,
super_class_name,
super_class: None, // has to be instantiated later, because it involves classloading. maybe not store it here
super_classes: vec![],
interface_indices,
interfaces: vec![], // same
fields,
methods,
attributes,
inited: false,
object_field_mapping: HashMap::new(),
static_field_mapping: HashMap::new(),
static_data: vec![],
}
}
pub(crate) fn n_object_fields(&self) -> usize { pub(crate) fn n_object_fields(&self) -> usize {
self.object_field_mapping self.object_field_mapping
.iter() .iter()
@ -179,448 +43,8 @@ impl Class {
.reduce(|acc, e| acc + e) .reduce(|acc, e| acc + e)
.unwrap() .unwrap()
} }
pub(crate) fn n_static_fields(&self) -> usize {
self.static_field_mapping
.iter()
.map(|(_, v)| v.len())
.reduce(|acc, e| acc + e)
.unwrap()
}
// Create a mapping per field(name) to an index in the storage vector that contains the instance data.
// When a field is stored, first the index will be looked up, using the qualified name (from the FieldRef)
// The qualified name is the combination of class name and field name.
// The class name is needed as part of the key to separate class from superclass fields
// (duplicates in the singular field name are allowed).
// 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<RefCell<Class>>, super_classes: Vec<Arc<RefCell<Class>>>) {
let mut this_field_mapping = HashMap::new();
let mut static_field_mapping = HashMap::new();
let mut object_field_map_index: usize = 0;
let mut static_field_map_index: usize = 0;
Class::add_field_mappings(
&mut this_field_mapping,
&mut static_field_mapping,
class.clone(),
&mut object_field_map_index,
&mut static_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;
}
/// 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<String, HashMap<String, TypeIndex>>,
static_field_mapping: &mut HashMap<String, HashMap<String, TypeIndex>>,
class: Arc<RefCell<Class>>,
object_field_map_index: &mut usize,
static_field_map_index: &mut usize,
) {
let (o, s) = Class::map_fields(
class.clone(),
object_field_map_index,
static_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);
// // 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,
c.clone(),
object_field_map_index,
static_field_map_index,
);
}
}
// part of the initialize procedure
/// here the actual indices are created
fn map_fields(
class: Arc<RefCell<Class>>,
object_field_map_index: &mut usize,
static_field_map_index: &mut usize,
) -> (
HashMap<String, TypeIndex>,
HashMap<String, TypeIndex>,
) {
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.
for (name, field) in &class.borrow().fields {
if field.is(Modifier::Static) {
static_fields.insert(
name.to_owned(),
TypeIndex::new(field.type_of().to_owned(), *static_field_map_index),
);
*static_field_map_index += 1;
} else {
this_fields.insert(
name.to_owned(),
TypeIndex::new(field.type_of().to_owned(), *object_field_map_index),
); //name => (type,index)
*object_field_map_index += 1;
}
}
(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<Method>> {
self.methods.get(name)
}
/// get the class name
fn class_name(
super_class_index: u16,
constant_pool: Rc<HashMap<u16, CpEntry>>,
) -> Option<String> {
if super_class_index == 0 {
None
} else if let CpEntry::ClassRef(name_index) = constant_pool.get(&super_class_index).unwrap()
{
if let CpEntry::Utf8(name) = constant_pool.get(name_index).unwrap() {
Some(name.to_owned())
} else {
None
}
} else {
None
}
}
/// 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<RefCell<Class>>) -> Vec<Value> {
let mut field_data = vec![Value::Null; class.borrow().n_static_fields()];
for (_, this_class) in &class.borrow().static_field_mapping {
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),
"I" => Value::I32(0),
"J" => Value::I64(0),
"F" => Value::F32(0.0),
"D" => Value::F64(0.0),
_ => Value::Null,
};
// println!("{} = {:?}", name, value);
field_data[type_index.index] = value.into();
}
}
field_data
}
// convienence methods for data from the constantpool
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()
{
(class_index, name_and_type_index)
} else {
unreachable!("should be field")
}
}
/// both methodRef and InterfaceMethodRef
/// returns (class_index, name_and_type_index)
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()
{
(class_index, name_and_type_index)
} else {
unreachable!("should be method")
}
}
pub fn cp_class_ref(&self, index: &u16) -> &u16 {
if let CpEntry::ClassRef(name_index) = self.constant_pool.get(index).unwrap() {
name_index
} else {
unreachable!("should be class entry")
}
}
pub fn cp_utf8(&self, index: &u16) -> &String {
if let CpEntry::Utf8(utf8) = self.constant_pool.get(index).unwrap() {
utf8
} else {
unreachable!("should be utf8 entry")
}
}
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()
{
(name_index, type_index)
} else {
unreachable!("should be name_and_type entry")
}
}
} }
unsafe impl Send for Class {}
unsafe impl Sync for Class {}
pub struct Method {
pub(crate) constant_pool: Rc<HashMap<u16, CpEntry>>,
access_flags: u16,
name_index: u16,
descriptor_index: u16,
pub(crate) attributes: HashMap<String, AttributeType>,
}
impl fmt::Debug for Method {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Method {{access_flags: {}, name_index: {}, descriptor_index: {}, attributes: {:?} }}",
self.access_flags, self.name_index, self.descriptor_index, self.attributes
)
}
}
impl Method {
pub fn new(
constant_pool: Rc<HashMap<u16, CpEntry>>,
access_flags: u16,
name_index: u16,
descriptor_index: u16,
attributes: HashMap<String, AttributeType>,
) -> Self {
Method {
constant_pool,
access_flags,
name_index,
descriptor_index,
attributes,
}
}
pub fn name(&self) -> String {
let mut full_name = String::new();
if let CpEntry::Utf8(s) = &self.constant_pool.get(&self.name_index).unwrap() {
full_name.push_str(s);
}
if let CpEntry::Utf8(s) = &self.constant_pool.get(&self.descriptor_index).unwrap() {
full_name.push_str(s);
}
full_name
}
pub fn is(&self, modifier: Modifier) -> bool {
let m = modifier as u16;
(self.access_flags & m) == m
}
}
pub struct Field {
constant_pool: Rc<HashMap<u16, CpEntry>>,
access_flags: u16,
pub(crate) name_index: u16,
descriptor_index: u16,
attributes: HashMap<String, AttributeType>,
_index: u16,
}
impl fmt::Debug for Field {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Field {{access_flags: {}, name_index: {}, descriptor_index: {}, attributes: {:?} }}",
self.access_flags, self.name_index, self.descriptor_index, self.attributes
)
}
}
impl Field {
pub fn new(
constant_pool: Rc<HashMap<u16, CpEntry>>,
access_flags: u16,
name_index: u16,
descriptor_index: u16,
attributes: HashMap<String, AttributeType>,
field_index: u16,
) -> Self {
Field {
constant_pool,
access_flags,
name_index,
descriptor_index,
attributes,
_index: field_index,
}
}
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;
}
unreachable!()
}
pub fn type_of(&self) -> &String {
if let CpEntry::Utf8(s) = &self.constant_pool.get(&self.descriptor_index).unwrap() {
return s;
}
panic!()
}
}
const _MODIFIERS: [(Modifier, &str); 12] = [
(Modifier::Public, "public "),
(Modifier::Private, "private "),
(Modifier::Protected, "protected "),
(Modifier::Static, "static "),
(Modifier::Final, "final "),
(Modifier::Synchronized, "synchronized "),
(Modifier::Volatile, "volatile "),
(Modifier::Transient, "transient "),
(Modifier::Native, "native "),
(Modifier::Abstract, "abstract"),
(Modifier::Strict, "strict"),
(Modifier::Synthetic, "synthetic"),
];
pub enum Modifier {
Public = 0x0001,
Private = 0x0002,
Protected = 0x0004,
Static = 0x0008,
Final = 0x0010,
Synchronized = 0x0020,
Volatile = 0x0040,
Transient = 0x0080,
Native = 0x0100,
Abstract = 0x0400,
Strict = 0x0800,
Synthetic = 0x1000,
}
//TODO implement more types
#[derive(Debug)]
pub enum AttributeType {
ConstantValue(u16),
Code(Box<MethodCode>),
StackMapTable,
BootstrapMethods,
NestHost,
NestMembers,
PermittedSubclasses,
Exceptions,
InnerClasses,
EnclosingMethod,
Synthetic,
Signature,
Record,
SourceFile,
LineNumberTable,
LocalVariableTable,
LocalVariableTypeTable,
SourceDebugExtension,
Deprecated,
RuntimeVisibleAnnotations,
RuntimeInvisibleAnnotations,
RuntimeVisibleParameterAnnotations,
RuntimeInvisibleParameterAnnotations,
RuntimeVisibleTypeAnnotations,
RuntimeInvisibleTypeAnnotations,
AnnotationDefault,
MethodParameters,
Module,
ModulePackages,
ModuleMainClass,
}
#[derive(Debug)]
pub struct Exception {
pub start_pc: u16,
pub end_pc: u16,
pub handler_pc: u16,
pub catch_type: u16,
}
impl Exception {
pub fn read(code: &[u8], index: &mut usize) -> Self {
Self {
start_pc: read_u16(code, index),
end_pc: read_u16(code, index),
handler_pc: read_u16(code, index),
catch_type: read_u16(code, index),
}
}
}
#[derive(Debug)]
pub struct MethodCode {
_max_stack: u16,
_max_locals: u16,
pub(crate) opcodes: Vec<u8>,
_exception_table: Vec<Exception>,
_code_attributes: HashMap<String, AttributeType>,
}
impl MethodCode {
pub(crate) fn new(
_max_stack: u16,
_max_locals: u16,
code: Vec<u8>,
_exception_table: Vec<Exception>,
_code_attributes: HashMap<String, AttributeType>,
) -> Self {
Self {
_max_stack,
_max_locals,
opcodes: code,
_exception_table,
_code_attributes,
}
}
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum Value { pub enum Value {
@ -636,7 +60,7 @@ pub enum Value {
BOOL(bool), BOOL(bool),
CHAR(char), CHAR(char),
// objects and arrays // objects and arrays
Ref(UnsafeRef), Ref(ObjectRef),
// special object // special object
Utf8(String), Utf8(String),
} }
@ -651,7 +75,7 @@ impl Value {
} }
} }
pub fn into_object(self) -> UnsafeRef { pub fn into_object(self) -> ObjectRef {
if let Value::Ref(v) = self { if let Value::Ref(v) = self {
v v
} else { } else {
@ -660,22 +84,145 @@ impl Value {
} }
} }
pub type UnsafeRef = Arc<UnsafeCell<ObjectRef>>; #[derive(Debug, Clone)]
pub enum ObjectRef {
pub fn unsafe_ref(object: ObjectRef) -> UnsafeRef { ByteArray(Vec<i8>),
return Arc::new(UnsafeCell::new(object)); ShortArray(Vec<i16>),
IntArray(Vec<i32>),
LongArray(Vec<i64>),
FloatArray(Vec<f32>),
DoubleArray(Vec<f64>),
BooleanArray(Vec<bool>),
CharArray(Vec<char>),
StringArray(Vec<String>),
ObjectArray(ClassId, Vec<ObjectRef>),
Object(Object),
//Box necessary??
Class(Class),
} }
// pub fn unsafe_val(val: Value) -> UnsafeValue { impl ObjectRef {
// return Arc::new(UnsafeCell::new(val)); pub fn get_array_length(&self) -> usize {
match self {
ByteArray(d) => d.len(),
ShortArray(d) => d.len(),
IntArray(d) => d.len(),
LongArray(d) => d.len(),
FloatArray(d) => d.len(),
DoubleArray(d) => d.len(),
BooleanArray(d) => d.len(),
CharArray(d) => d.len(),
StringArray(d) => d.len(),
ObjectArray(_, d) => d.len(),
_ => unreachable!("not an array")
}
}
}
impl ObjectRef {
pub fn new_object_array(class: &Class, size: usize) -> Self {
ObjectArray(class.id, Vec::with_capacity(size))
}
pub fn new_int_array(size: usize) -> Self {
IntArray(Vec::with_capacity(size))
}
pub fn new_byte_array(d: Vec<u8>) -> Self {
ByteArray(into_vec_i8(d))
}
}
fn into_vec_i8(v: Vec<u8>) -> Vec<i8> {
let mut v = std::mem::ManuallyDrop::new(v);
// then, pick apart the existing Vec
let p = v.as_mut_ptr();
let len = v.len();
let cap = v.capacity();
// finally, adopt the data into a new Vec
unsafe { Vec::from_raw_parts(p as *mut i8, len, cap) }
}
#[derive(Debug, Clone)]
pub struct Object {
// locked: bool,
// hashcode: i32,
pub class_id: ClassId,
pub data: Vec<Value>,
} //arrays
// object, not array
impl Object {
pub fn new(class: &Class) -> Self {
let instance_data = Object::init_fields(class);
Self {
class_id: class.id,
data: instance_data,
}
}
// initializes all non-static fields to their default values
pub(crate) fn init_fields(class: &Class) -> Vec<Value> {
let mut field_data = Vec::with_capacity(class.n_object_fields());
for (_, fields) in &class.object_field_mapping {
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),
"I" => Value::I32(0),
"J" => Value::I64(0),
"F" => Value::F32(0.0),
"D" => Value::F64(0.0),
_ => Value::Null,
};
field_data.push(value.into());
}
}
field_data
}
pub fn set(&mut self, class: &Class, declared_type: &str, field_name: &str, value: Value) {
let type_index = class
.object_field_mapping
.get(declared_type)
.unwrap()
.get(field_name)
.unwrap();
self.data[type_index.index] = value;
}
pub fn get(&mut self, instancedef: &Class, declared_type: &String, field_name: &String) -> &Value {
let type_index = instancedef
.object_field_mapping
.get(declared_type)
.unwrap()
.get(field_name)
.unwrap();
&self.data[type_index.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!()
// }
}
// impl Debug for Object {
// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// // let fields: Vec<String> = self.data.unwrap().iter().map(|(k)| {
// // // let mut r: String = self.get_field_name(k).into();
// // // r.push(':');
// // // r.push_str(format!("{:?}").as_str());
// // // r
// // }
// // ).collect();
// write!(f, "{}", self.class.name)
// }
// } // }
pub fn type_ref(class: Class) -> Type {
return Arc::new(RefCell::new(class));
}
pub type Type = Arc<RefCell<Class>>;
unsafe impl Send for Value {}
unsafe impl Sync for Value {}

367
src/classloader/classdef.rs Normal file
View file

@ -0,0 +1,367 @@
use std::collections::HashMap;
use std::fmt;
use std::fmt::{Debug, Formatter};
use std::rc::Rc;
use crate::classloader::io::read_u16;
/// This is the class representation when the bytecode had just been loaded.
pub struct ClassDef {
pub minor_version: u16,
pub major_version: u16,
pub constant_pool: Rc<HashMap<u16, CpEntry>>,
pub access_flags: u16,
this_class: u16,
pub super_class: Option<u16>,
pub interfaces: Vec<u16>,
pub fields: HashMap<String, Field>,
pub methods: HashMap<String, Method>,
pub attributes: HashMap<String, AttributeType>,
}
impl Debug for ClassDef{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.cp_class_name(&self.this_class))
}
}
impl ClassDef {
pub fn new(
minor_version: u16,
major_version: u16,
constant_pool: Rc<HashMap<u16, CpEntry>>,
access_flags: u16,
this_class: u16,
super_class: Option<u16>,
interfaces: Vec<u16>,
fields: HashMap<String, Field>,
methods: HashMap<String, Method>,
attributes: HashMap<String, AttributeType>,
) -> Self {
Self {
major_version,
minor_version,
constant_pool,
access_flags,
this_class,
super_class,
interfaces,
fields,
methods,
attributes,
}
}
/// the bytecode version
pub fn version(&self) -> (u16, u16) {
(self.major_version, self.minor_version)
}
/// get the class name
pub fn name(&self) -> &str {
self.cp_class_name(&self.this_class)
}
pub fn get_method(&self, name: &str) -> Option<&Method> {
self.methods.get(name)
}
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()
{
(class_index, name_and_type_index)
} else {
unreachable!("should be field")
}
}
/// both methodRef and InterfaceMethodRef
/// returns (class_index, name_and_type_index)
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()
{
(class_index, name_and_type_index)
} else {
unreachable!("should be method")
}
}
pub fn cp_class_name(&self, index: &u16) -> &String {
let cr = self.cp_class_ref(index);
self.cp_utf8(cr)
}
pub fn cp_class_ref(&self, index: &u16) -> &u16 {
if let CpEntry::ClassRef(name_index) = self.constant_pool.get(index).unwrap() {
name_index
} else {
unreachable!("should be class entry")
}
}
pub fn cp_utf8(&self, index: &u16) -> &String {
if let CpEntry::Utf8(utf8) = self.constant_pool.get(index).unwrap() {
utf8
} else {
unreachable!("should be utf8 entry")
}
}
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()
{
(name_index, type_index)
} else {
unreachable!("should be name_and_type entry")
}
}
}
#[derive(Debug)]
pub enum CpEntry {
Utf8(String),
Integer(i32),
Float(f32),
Long(i64),
Double(f64),
ClassRef(u16),
// (utf8)
StringRef(u16),
// (utf8)
Fieldref(u16, u16),
// (class, name_and_type)
MethodRef(u16, u16),
// (class, name_and_type)
InterfaceMethodref(u16, u16),
// (class, name_and_type)
NameAndType(u16, u16),
// (name, descriptor)
MethodHandle(u8, u16),
MethodType(u16),
InvokeDynamic(u16, u16),
}
pub enum Modifier {
Public = 0x0001,
Private = 0x0002,
Protected = 0x0004,
Static = 0x0008,
Final = 0x0010,
Synchronized = 0x0020,
Volatile = 0x0040,
Transient = 0x0080,
Native = 0x0100,
Abstract = 0x0400,
Strict = 0x0800,
Synthetic = 0x1000,
}
//TODO implement more types
#[derive(Debug)]
pub enum AttributeType {
ConstantValue(u16),
Code(Box<MethodCode>),
StackMapTable,
BootstrapMethods,
NestHost,
NestMembers,
PermittedSubclasses,
Exceptions,
InnerClasses,
EnclosingMethod,
Synthetic,
Signature,
Record,
SourceFile,
LineNumberTable,
LocalVariableTable,
LocalVariableTypeTable,
SourceDebugExtension,
Deprecated,
RuntimeVisibleAnnotations,
RuntimeInvisibleAnnotations,
RuntimeVisibleParameterAnnotations,
RuntimeInvisibleParameterAnnotations,
RuntimeVisibleTypeAnnotations,
RuntimeInvisibleTypeAnnotations,
AnnotationDefault,
MethodParameters,
Module,
ModulePackages,
ModuleMainClass,
}
#[derive(Debug)]
pub struct Exception {
pub start_pc: u16,
pub end_pc: u16,
pub handler_pc: u16,
pub catch_type: u16,
}
impl Exception {
pub fn read(code: &[u8], index: &mut usize) -> Self {
Self {
start_pc: read_u16(code, index),
end_pc: read_u16(code, index),
handler_pc: read_u16(code, index),
catch_type: read_u16(code, index),
}
}
}
#[derive(Debug)]
pub struct MethodCode {
_max_stack: u16,
_max_locals: u16,
pub(crate) opcodes: Vec<u8>,
_exception_table: Vec<Exception>,
_code_attributes: HashMap<String, AttributeType>,
}
impl MethodCode {
pub(crate) fn new(
_max_stack: u16,
_max_locals: u16,
code: Vec<u8>,
_exception_table: Vec<Exception>,
_code_attributes: HashMap<String, AttributeType>,
) -> Self {
Self {
_max_stack,
_max_locals,
opcodes: code,
_exception_table,
_code_attributes,
}
}
}
pub(crate) const _MODIFIERS: [(Modifier, &str); 12] = [
(Modifier::Public, "public "),
(Modifier::Private, "private "),
(Modifier::Protected, "protected "),
(Modifier::Static, "static "),
(Modifier::Final, "final "),
(Modifier::Synchronized, "synchronized "),
(Modifier::Volatile, "volatile "),
(Modifier::Transient, "transient "),
(Modifier::Native, "native "),
(Modifier::Abstract, "abstract"),
(Modifier::Strict, "strict"),
(Modifier::Synthetic, "synthetic"),
];
pub struct Field {
constant_pool: Rc<HashMap<u16, CpEntry>>,
access_flags: u16,
pub(crate) name_index: u16,
descriptor_index: u16,
attributes: HashMap<String, AttributeType>,
_index: u16,
}
impl fmt::Debug for Field {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Field {{access_flags: {}, name_index: {}, descriptor_index: {}, attributes: {:?} }}",
self.access_flags, self.name_index, self.descriptor_index, self.attributes
)
}
}
impl Field {
pub fn new(
constant_pool: Rc<HashMap<u16, CpEntry>>,
access_flags: u16,
name_index: u16,
descriptor_index: u16,
attributes: HashMap<String, AttributeType>,
field_index: u16,
) -> Self {
Field {
constant_pool,
access_flags,
name_index,
descriptor_index,
attributes,
_index: field_index,
}
}
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;
}
unreachable!()
}
pub fn type_of(&self) -> &String {
if let CpEntry::Utf8(s) = &self.constant_pool.get(&self.descriptor_index).unwrap() {
return s;
}
panic!()
}
}
pub struct Method {
pub(crate) constant_pool: Rc<HashMap<u16, CpEntry>>,
access_flags: u16,
name_index: u16,
descriptor_index: u16,
pub(crate) attributes: HashMap<String, AttributeType>,
}
impl fmt::Debug for Method {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Method {{access_flags: {}, name_index: {}, descriptor_index: {}, attributes: {:?} }}",
self.access_flags, self.name_index, self.descriptor_index, self.attributes
)
}
}
impl Method {
pub fn new(
constant_pool: Rc<HashMap<u16, CpEntry>>,
access_flags: u16,
name_index: u16,
descriptor_index: u16,
attributes: HashMap<String, AttributeType>,
) -> Self {
Method {
constant_pool,
access_flags,
name_index,
descriptor_index,
attributes,
}
}
pub fn name(&self) -> String {
let mut full_name = String::new();
if let CpEntry::Utf8(s) = &self.constant_pool.get(&self.name_index).unwrap() {
full_name.push_str(s);
}
if let CpEntry::Utf8(s) = &self.constant_pool.get(&self.descriptor_index).unwrap() {
full_name.push_str(s);
}
full_name
}
pub fn is(&self, modifier: Modifier) -> bool {
let m = modifier as u16;
(self.access_flags & m) == m
}
}

View file

@ -1,6 +1,12 @@
use std::fs::{self};
use anyhow::{anyhow, Error}; use anyhow::{anyhow, Error};
use std::fs::{self, File};
use std::io::Read; #[cfg(target_family = "unix")]
pub const PATH_SEPARATOR: char = ':';
#[cfg(target_family = "windows")]
pub const PATH_SEPARATOR: char = ';';
/// resolves the actual path where the class file is found /// resolves the actual path where the class file is found
/// for std lib there is a special case that resolves to the jmod /// for std lib there is a special case that resolves to the jmod
@ -38,25 +44,7 @@ pub fn find_class(classpath: &Vec<String>, class_name: &str) -> Result<String, E
Err(anyhow!("Class not found {}", class_name)) Err(anyhow!("Class not found {}", class_name))
} }
/// reads the binary class file from file path or archive
/// and returns the byte array as Vec
pub fn read_bytecode(name: String) -> Result<Vec<u8>, Error> {
let mut buffer;
if name.contains('#') {
let parts: Vec<&str> = name.split('#').collect();
let archive_file = File::open(parts[0])?;
let mut archive_zip = zip::ZipArchive::new(archive_file)?;
let mut entry = archive_zip.by_name(parts[1])?;
buffer = vec![0; entry.size() as usize];
entry.read_exact(&mut buffer)?;
} else {
let mut f = File::open(&name)?;
let metadata = fs::metadata(&name)?;
buffer = vec![0; metadata.len() as usize];
f.read_exact(&mut buffer)?;
}
Ok(buffer)
}
// methods to read values from big-endian binary data // methods to read values from big-endian binary data

View file

@ -1,11 +1,46 @@
use crate::class::{AttributeType, Class, Exception, Field, Method, MethodCode};
use crate::io::{read_bytes, read_f32, read_f64, read_i32, read_i64, read_u16, read_u32, read_u8};
use anyhow::Error;
use std::collections::HashMap; use std::collections::HashMap;
use std::fs;
use std::fs::File;
use std::io::Read;
use std::rc::Rc; use std::rc::Rc;
use anyhow::Error;
use crate::classloader::io::{find_class, read_bytes, read_f32, read_f64, read_i32, read_i64, read_u16, read_u32, read_u8};
use crate::classloader::classdef::{AttributeType, ClassDef, CpEntry, Exception, Field, Method, MethodCode};
pub(crate) mod classdef;
pub(crate) mod io;
pub(crate) fn get_classdef(classpath: &Vec<String>, class_name: &str) -> Result<ClassDef,Error> {
println!("read class {} ", class_name);
let resolved_path = find_class(classpath, class_name)?;
let bytecode = read_bytecode(resolved_path)?;
load_class(bytecode)
}
/// reads the binary class file from file path or archive
/// and returns the byte array as Vec
fn read_bytecode(name: String) -> Result<Vec<u8>, Error> {
let mut buffer;
if name.contains('#') {
let parts: Vec<&str> = name.split('#').collect();
let archive_file = File::open(parts[0])?;
let mut archive_zip = zip::ZipArchive::new(archive_file)?;
let mut entry = archive_zip.by_name(parts[1])?;
buffer = vec![0; entry.size() as usize];
entry.read_exact(&mut buffer)?;
} else {
let mut f = File::open(&name)?;
let metadata = fs::metadata(&name)?;
buffer = vec![0; metadata.len() as usize];
f.read_exact(&mut buffer)?;
}
Ok(buffer)
}
// The native classoader // The native classoader
pub fn load_class(bytecode: Vec<u8>) -> Result<Class, Error> { fn load_class(bytecode: Vec<u8>) -> Result<ClassDef, Error> {
let pos = &mut 0; let pos = &mut 0;
check_magic(&bytecode, pos); check_magic(&bytecode, pos);
let minor_version = read_u16(&bytecode, pos); let minor_version = read_u16(&bytecode, pos);
@ -22,13 +57,11 @@ pub fn load_class(bytecode: Vec<u8>) -> Result<Class, Error> {
); );
cp_index += 1; cp_index += 1;
} }
let constant_pool = Rc::new(constant_pool); let constant_pool = Rc::new(constant_pool);
let access_flags = read_u16(&bytecode, pos); let access_flags = read_u16(&bytecode, pos);
let this_class = read_u16(&bytecode, pos); let this_class = read_u16(&bytecode, pos);
let super_class = read_u16(&bytecode, pos); let super_class = read_u16(&bytecode, pos);
let super_class = if super_class != 0 { Some(super_class) } else { None };
let interfaces_count = read_u16(&bytecode, pos); let interfaces_count = read_u16(&bytecode, pos);
let mut interfaces = vec![]; let mut interfaces = vec![];
for _ in 0..interfaces_count { for _ in 0..interfaces_count {
@ -46,7 +79,7 @@ pub fn load_class(bytecode: Vec<u8>) -> Result<Class, Error> {
let mut methods = HashMap::new(); let mut methods = HashMap::new();
for _ in 0..methods_count { for _ in 0..methods_count {
let m = read_method(constant_pool.clone(), pos, &bytecode); let m = read_method(constant_pool.clone(), pos, &bytecode);
methods.insert(m.name(), Rc::new(m)); methods.insert(m.name(), m);
} }
let attributes_count = read_u16(&bytecode, pos); let attributes_count = read_u16(&bytecode, pos);
@ -60,7 +93,7 @@ pub fn load_class(bytecode: Vec<u8>) -> Result<Class, Error> {
} }
} }
Ok(Class::new( Ok(ClassDef::new(
minor_version, minor_version,
major_version, major_version,
constant_pool, constant_pool,
@ -224,7 +257,6 @@ fn read_attribute(
*index += attribute_length; *index += attribute_length;
if let CpEntry::Utf8(s) = &constant_pool.get(&attribute_name_index).unwrap() { if let CpEntry::Utf8(s) = &constant_pool.get(&attribute_name_index).unwrap() {
// println!("Att [{}]", s);
return match s.as_str() { return match s.as_str() {
"ConstantValue" => { "ConstantValue" => {
assert_eq!(info.len(), 2); assert_eq!(info.len(), 2);
@ -274,6 +306,7 @@ fn read_attribute(
"Signature" => Some(("".into(), AttributeType::Signature)), //stub "Signature" => Some(("".into(), AttributeType::Signature)), //stub
"NestHost" => Some(("".into(), AttributeType::NestHost)), //stub "NestHost" => Some(("".into(), AttributeType::NestHost)), //stub
"EnclosingMethod" => Some(("".into(), AttributeType::EnclosingMethod)), //stub "EnclosingMethod" => Some(("".into(), AttributeType::EnclosingMethod)), //stub
"PermittedSubclasses" => Some(("".into(), AttributeType::PermittedSubclasses)), //stub
//TODO more actual attribute implementations //TODO more actual attribute implementations
_ => None, _ => None,
}; };
@ -281,20 +314,3 @@ fn read_attribute(
None None
} }
#[derive(Debug)]
pub enum CpEntry {
Utf8(String),
Integer(i32),
Float(f32),
Long(i64),
Double(f64),
ClassRef(u16), // (utf8)
StringRef(u16), // (utf8)
Fieldref(u16, u16), // (class, name_and_type)
MethodRef(u16, u16), // (class, name_and_type)
InterfaceMethodref(u16, u16), // (class, name_and_type)
NameAndType(u16, u16), // (name, descriptor)
MethodHandle(u8, u16),
MethodType(u16),
InvokeDynamic(u16, u16),
}

319
src/classmanager.rs Normal file
View file

@ -0,0 +1,319 @@
use std::collections::{HashMap, LinkedList};
use once_cell::sync::Lazy;
use crate::class::{Class, ClassId, ObjectRef, TypeIndex, Value::*, Value};
use crate::class::Object;
use crate::classloader;
use crate::classloader::classdef::{ClassDef, Modifier};
use crate::classloader::io::PATH_SEPARATOR;
use crate::vm::Vm;
static mut CLASSMANAGER: Lazy<ClassManager> = Lazy::new(|| ClassManager::new());
pub fn init() {
unsafe {
CLASSMANAGER.classes.clear();
CLASSMANAGER.names.clear();
CLASSMANAGER.classdefs.clear();
CLASSMANAGER.class_objects.clear();
CLASSMANAGER.static_class_data.clear();
}
}
pub fn set_classpath(classpath: &str) {
unsafe {
CLASSMANAGER.set_classpath(classpath);
}
}
pub fn get_class_by_id(id: ClassId) -> Option<&'static Class> {
unsafe {
CLASSMANAGER.get_class_by_id(id)
}
}
pub fn classdef_name(id: &ClassId) -> Option<String> {
unsafe {
CLASSMANAGER.classdef_name(id)
}
}
pub fn get_classid(name: &str) -> &ClassId {
unsafe {
CLASSMANAGER.get_classid(name)
}
}
pub fn get_classdef(id: &ClassId) -> &ClassDef {
unsafe {
CLASSMANAGER.get_classdef(id)
}
}
pub fn load_class_by_name(name: &str) {
unsafe {
CLASSMANAGER.load_class_by_name(name)
}
}
pub fn get_class_by_name(name: &str) -> Option<&Class> {
unsafe {
CLASSMANAGER.get_class_by_name(name)
}
}
pub fn add_class(name: &str) -> ClassId {
unsafe {
CLASSMANAGER.add_class(name)
}
}
pub fn get_static(id: &ClassId, index: usize) -> Value {
unsafe {
CLASSMANAGER.static_class_data.get(id).unwrap()[index].clone()
}
}
pub fn set_static(id: &ClassId, index: usize, value: Value) {
unsafe {
CLASSMANAGER.static_class_data.get_mut(id).unwrap()[index] = value;
}
}
pub fn get_classobject(id: &ClassId) -> Option<&Value> {
unsafe {
CLASSMANAGER.class_objects.get(id)
}
}
//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 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>,
vm: Vm,
}
impl ClassManager {
pub fn new() -> Self {
Self {
static_class_data: HashMap::new(),
current_id: 0,
classdefs: HashMap::new(),
classes: HashMap::new(),
class_objects: HashMap::new(),
names: HashMap::new(),
classpath: vec![],
vm:Vm::new_internal(),
}
}
fn set_classpath(&mut self, classpath: &str) {
self.classpath = classpath
.split(PATH_SEPARATOR)
.map(|s| s.into())
.collect();
}
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)
}
fn classdef_name(&self, id: &ClassId) -> Option<String> {
self.classdefs.get(id).map(|c| c.name().to_owned()) //drops borrow to self here
}
fn get_classid(&self, name: &str) -> &ClassId {
self.names.get(name).unwrap()
}
fn get_classdef(&self, id: &ClassId) -> &ClassDef {
self.classdefs.get(&id).unwrap()
}
fn load_class_by_name(&mut self, name: &str) {
let id = self.names.get(name);
match id {
Some(id) => if self.classes.get(id).is_none() {
self.add_class(name);
}
None => { self.add_class(name); }
}
}
fn get_class_by_name(&self, name: &str) -> Option<&Class> {
let id = self.names.get(name);
self.classes.get(id.unwrap())
}
fn add_class(&mut self, name: &str) -> ClassId {
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();
self.static_class_data.insert(this_classid, Self::set_field_data(&static_field_mapping));
self.names.get(name)
.and_then(|id|
self.classes.insert(*id, Class {
id: *id,
initialized: false,
name: name.into(),
superclass: superclass_id,
parents,
interfaces: interface_ids,
object_field_mapping,
static_field_mapping,
// static_field_data: static_values,
}));
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(instance));
self.class_objects.insert(this_classid, instance);
}
let clinit = this_classdef.methods.contains_key("<clinit>()V");
if clinit {
self.vm.execute_special(&mut vec![],name, "<clinit>()V", vec![]).unwrap();
}
this_classid
}
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);
}
}
id
}
/// loads the class and returns it's dependencies
fn load_class_and_deps(&mut self, name: &str) -> (ClassId, Vec<String>) {
let id = *self.names.entry(name.to_string()).or_insert_with(|| {
self.current_id += 1;
self.current_id
});
let classdef = self.classdefs
.entry(id)
.or_insert_with(|| classloader::get_classdef(&self.classpath, name).expect("ClassNotFound"));
(self.current_id, inspect_dependencies(classdef))
}
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,
};
// println!("{} = {:?}", name, value);
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
}

View file

@ -1,190 +0,0 @@
use std::cell::{RefCell, UnsafeCell};
use std::fmt;
use std::fmt::{Debug, Formatter};
use std::sync::Arc;
use ObjectRef::{BooleanArray, CharArray, DoubleArray, FloatArray, LongArray, ShortArray};
use crate::class::{Class, Type, Value};
use crate::heap::ObjectRef::{ByteArray, IntArray, ObjectArray, StringArray};
// can contain object or array
pub enum ObjectRef {
ByteArray(Vec<i8>),
ShortArray(Vec<i16>),
IntArray(Vec<i32>),
LongArray(Vec<i64>),
FloatArray(Vec<f32>),
DoubleArray(Vec<f64>),
BooleanArray(Vec<bool>),
CharArray(Vec<char>),
StringArray(Vec<String>),
ObjectArray(Type, Vec<Arc<UnsafeCell<ObjectRef>>>),
Object(Box<Object>),//Box necessary??
Class(Arc<RefCell<Class>>),
}
impl ObjectRef {
pub fn get_array_length(&self) -> usize {
match self {
ByteArray(d) => d.len(),
ShortArray(d) => d.len(),
IntArray(d) => d.len(),
LongArray(d) => d.len(),
FloatArray(d) => d.len(),
DoubleArray(d) => d.len(),
BooleanArray(d) => d.len(),
CharArray(d) => d.len(),
StringArray(d) => d.len(),
ObjectArray(_, d) => d.len(),
_ => unreachable!("not an array")
}
}
}
fn into_vec_i8(v: Vec<u8>) -> Vec<i8> {
let mut v = std::mem::ManuallyDrop::new(v);
// then, pick apart the existing Vec
let p = v.as_mut_ptr();
let len = v.len();
let cap = v.capacity();
// finally, adopt the data into a new Vec
unsafe { Vec::from_raw_parts(p as *mut i8, len, cap) }
}
impl ObjectRef {
pub fn new_object_array(class: Type, size: usize) -> Self {
ObjectArray(class, Vec::with_capacity(size))
}
pub fn new_int_array(size: usize) -> Self {
IntArray(Vec::with_capacity(size))
}
pub fn new_byte_array(d: Vec<u8>) -> Arc<UnsafeCell<Self>> {
Arc::new(UnsafeCell::new(ByteArray(into_vec_i8(d))))
}
}
impl Debug for ObjectRef {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
BooleanArray(d) => write!(f, "[Z;{}]", d.len()),
ByteArray(d) => write!(f, "[B;{}]", d.len()),
CharArray(d) => write!(f, "[C;{}]", d.len()),
DoubleArray(d) => write!(f, "[D;{}]", d.len()),
FloatArray(d) => write!(f, "[F;{}]", d.len()),
IntArray(d) => write!(f, "[I;{}]", d.len()),
LongArray(d) => write!(f, "[J;{}]", d.len()),
ObjectArray(t, d) => write!(f, "[L{};{}]", t.borrow().name, d.len()),
ShortArray(d) => write!(f, "[S;{}]", d.len()),
StringArray(d) => write!(f, "[S;{}]", d.len()),
ObjectRef::Object(r) => write!(f, "{}{{ {:?} }}", r.class.borrow().name, r.data),
ObjectRef::Class(s) => write!(f, "Class {:?}", s.borrow().name),
}
}
}
// trying to implement efficient object instance storage
pub struct Object {
// locked: bool,
// hashcode: i32,
pub class: Arc<RefCell<Class>>,
pub data: Vec<Value>,
} //arrays
unsafe impl Send for Object {}
unsafe impl Sync for Object {}
// object, not array
impl Object {
pub fn new(class: Arc<RefCell<Class>>) -> Self {
let instance_data = Object::init_fields(class.clone());
Self {
class,
data: instance_data,
}
}
// initializes all non-static fields to their default values
pub(crate) fn init_fields(class: Arc<RefCell<Class>>) -> Vec<Value> {
let mut field_data = Vec::with_capacity(class.borrow().n_object_fields());
for (_, fields) in &class.borrow().object_field_mapping {
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),
"I" => Value::I32(0),
"J" => Value::I64(0),
"F" => Value::F32(0.0),
"D" => Value::F64(0.0),
_ => Value::Null,
};
field_data.push(value.into());
}
}
field_data
}
pub fn set(&mut self, class_name: &String, field_name: &String, value: Value) {
let borrow = self.class.borrow();
let type_index = borrow
.object_field_mapping
.get(class_name)
.unwrap()
.get(field_name)
.unwrap();
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
.object_field_mapping
.get(class_name)
.unwrap()
.get(field_name)
.unwrap();
&self.data[type_index.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!()
// }
}
impl fmt::Debug for Object {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// let fields: Vec<String> = self.data.unwrap().iter().map(|(k)| {
// // let mut r: String = self.get_field_name(k).into();
// // r.push(':');
// // r.push_str(format!("{:?}").as_str());
// // r
// }
// ).collect();
write!(f, "{}", self.class.borrow().name)
}
}
// will using Arc's enable a GC-less heap????
pub(crate) struct Heap {
objects: Vec<Arc<UnsafeCell<ObjectRef>>>,
}
impl Heap {
pub fn new() -> Self {
Self { objects: vec![] }
}
pub(crate) fn new_object(&mut self, object: Arc<UnsafeCell<ObjectRef>>) {
self.objects.push(object);
}
}

View file

@ -1,8 +1,5 @@
mod classloader;
pub mod classmanager;
pub mod class; pub mod class;
pub mod classloader;
pub mod heap;
pub mod io;
pub mod opcodes;
pub mod vm; pub mod vm;
pub mod heap;
pub mod native;

View file

@ -1,17 +1,11 @@
use std::io::Error; use java_rs::classmanager::set_classpath;
use java_rs::vm::Vm; use java_rs::vm::Vm;
fn main() {
let mut stackframes = Vec::new();
fn main() -> Result<(), Error> { let mut vm = Vm::new(&mut stackframes);
// TODO cmdline args set_classpath("/Users/Shautvast/dev/java/tests");
// TODO build index for package -> jarfile?
let mut vm = Vm::new("tests");
// let main_class = "Inheritance";
let main_class = "testclasses.Main"; let main_class = "testclasses.Main";
vm.execute_static( &mut stackframes, main_class, "main([Ljava/lang/String;)V", vec![])
vm.execute_static( main_class, "main([Ljava/lang/String;)V", vec![])
.unwrap(); .unwrap();
Ok(())
} }

View file

@ -1,16 +1,17 @@
use anyhow::{anyhow, Error}; use anyhow::{anyhow, Error};
use crate::class::Value::{self,*};
use crate::heap::ObjectRef;
pub(crate) unsafe fn array_load(index: Value, arrayref: Value) -> Result<Value, Error> { use crate::class::ObjectRef;
if let I32(index) = &index { use crate::class::Value::{self, *};
let index = *index as usize;
pub(crate) fn array_load(index: Value, arrayref: Value) -> Result<Value, Error> {
if let I32(index) = index {
let index = index as usize;
if let Null = arrayref { if let Null = arrayref {
return Err(anyhow!("NullpointerException")); return Err(anyhow!("NullpointerException"));
} }
if let Ref(objectref) = arrayref { if let Ref(objectref) = arrayref {
match &*objectref.get() { match objectref {
ObjectRef::ByteArray(array) => { ObjectRef::ByteArray(array) => {
return Ok(I32(array[index] as i32)); return Ok(I32(array[index] as i32));
} }
@ -53,14 +54,14 @@ pub(crate) unsafe fn array_load(index: Value, arrayref: Value) -> Result<Value,
panic!() panic!()
} }
pub(crate) unsafe fn array_store(value: Value, index: Value, arrayref: &mut Value) -> Result<(), Error> { pub(crate) fn array_store(value: Value, index: Value, arrayref: Value) -> Result<(), Error> {
if let Null = &*arrayref { if let Null = arrayref {
return Err(anyhow!("NullpointerException")); return Err(anyhow!("NullpointerException"));
} }
if let I32(index) = index { if let I32(index) = index {
if let Ref(ref mut objectref) = arrayref { if let Ref(mut objectref) = arrayref {
match &mut *objectref.get() { match objectref {
ObjectRef::ByteArray(ref mut array) => { ObjectRef::ByteArray(ref mut array) => {
if let I32(value) = value { if let I32(value) = value {
// is i32 correct? // is i32 correct?
@ -91,7 +92,7 @@ pub(crate) unsafe fn array_store(value: Value, index: Value, arrayref: &mut Valu
unreachable!() unreachable!()
} }
} }
ObjectRef::CharArray(ref mut array) => { ObjectRef::CharArray(ref mut array) => unsafe{
if let I32(value) = value { if let I32(value) = value {
array[index as usize] = char::from_u32_unchecked(value as u32); array[index as usize] = char::from_u32_unchecked(value as u32);
} else { } else {

View file

@ -3,3 +3,5 @@ pub use vm::Vm;
mod operations; mod operations;
mod stack; mod stack;
mod array; mod array;
mod opcodes;
mod native;

View file

@ -1,52 +1,52 @@
#![allow(non_snake_case)] #![allow(non_snake_case)]
use std::ptr::hash;
use anyhow::Error; use anyhow::Error;
use log::{debug, info}; use log::{info};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use crate::class::{get_class, unsafe_ref, Value}; use crate::class::{ObjectRef, Value};
use crate::class::ObjectRef::Object;
use crate::class::Value::Void; use crate::class::Value::Void;
use crate::heap::ObjectRef; use crate::classmanager;
use crate::heap::ObjectRef::Object; use crate::vm::stack::StackFrame;
use crate::vm::Vm; use crate::vm::Vm;
pub fn invoke_native(vm: &mut Vm, class_name: &String, method_name: &String, _args: Vec<Value>) -> Result<Value,Error> { pub fn invoke_native(vm: &mut Vm, stackframes: &mut Vec<StackFrame>, class_name: &String, method_name: &str, _args: Vec<Value>) -> Result<Value, Error> {
info!("native {}.{}", class_name, method_name); info!("native {}.{}", class_name, method_name);
match class_name.as_str() { match class_name.as_str() {
"java/lang/Class" => java_lang_class(vm, method_name), "java/lang/Class" => java_lang_class(vm, method_name),
"jdk/internal/util/SystemProps$Raw" => jdk_internal_util_SystemProps_Raw(vm, method_name), "jdk/internal/util/SystemProps$Raw" => jdk_internal_util_SystemProps_Raw(vm, stackframes, method_name),
_ => Ok(Void) _ => Ok(Void)
} }
} }
fn java_lang_class(_vm: &mut Vm, method_name: &String) -> Result<Value,Error> { fn java_lang_class(_vm: &Vm, method_name: &str) -> Result<Value, Error> {
Ok(match method_name.as_str() { Ok(match method_name {
"desiredAssertionStatus0(Ljava/lang/Class;)Z" => Value::BOOL(false), "desiredAssertionStatus0(Ljava/lang/Class;)Z" => Value::BOOL(false),
_ => Void _ => Void
}) })
} }
fn jdk_internal_util_SystemProps_Raw(vm: &mut Vm,method_name: &String) -> Result<Value,Error> { fn jdk_internal_util_SystemProps_Raw(vm: &mut Vm, stackframes: &mut Vec<StackFrame>, method_name: &str) -> Result<Value, Error> {
match method_name.as_str() { match method_name {
"platformProperties()[Ljava/lang/String;" => systemProps(), "platformProperties()[Ljava/lang/String;" => systemProps(),
"cmdProperties()Ljava/util/HashMap;" => cmdProps(vm), //TODO ability to instantiate classes here "cmdProperties()Ljava/util/HashMap;" => cmdProps(vm, stackframes), //TODO ability to instantiate classes here
"vmProperties()[Ljava/lang/String;" => cmdProps(vm), "vmProperties()[Ljava/lang/String;" => cmdProps(vm, stackframes),
_ => Ok(Void) _ => Ok(Void)
} }
} }
fn cmdProps(vm: &mut Vm,) -> Result<Value,Error> { fn cmdProps(vm: &mut Vm, stackframes: &mut Vec<StackFrame>) -> Result<Value, Error> {
let hashmap_class = get_class(vm, "java/util/HashMap")?; classmanager::load_class_by_name("java/util/HashMap");
let hashmap_class = classmanager::get_class_by_name("java/util/HashMap").unwrap();
let hashmap = Vm::new_instance(hashmap_class); let hashmap = Vm::new_instance(hashmap_class);
let hashmap = Value::Ref(unsafe_ref(Object(Box::new(hashmap)))); let hashmap = Value::Ref(Object(hashmap));
vm.execute_special("java/util/HashMap", "<init>()V", vec![hashmap.clone()]); vm.execute_special(stackframes, "java/util/HashMap", "<init>()V", vec![hashmap.clone()]);
unsafe {debug!("hashmap {:?}", *hashmap.into_object().get());}
panic!() panic!()
} }
fn systemProps() -> Result<Value,Error> { fn systemProps() -> Result<Value, Error> {
unsafe { unsafe {
let props: Lazy<Vec<String>> = Lazy::new(|| { let props: Lazy<Vec<String>> = Lazy::new(|| {
let mut vec: Vec<String> = Vec::new(); let mut vec: Vec<String> = Vec::new();
@ -87,7 +87,7 @@ fn systemProps() -> Result<Value,Error> {
if let Ok(https_proxy) = std::env::var("https_proxy") { if let Ok(https_proxy) = std::env::var("https_proxy") {
vec.push(https_proxy.to_owned()); vec.push(https_proxy.to_owned());
vec.push(https_proxy); vec.push(https_proxy);
}else { } else {
vec.push("".to_owned()); vec.push("".to_owned());
vec.push("".to_owned()); vec.push("".to_owned());
} }
@ -131,6 +131,6 @@ fn systemProps() -> Result<Value,Error> {
vec vec
}); });
Ok(Value::Ref(unsafe_ref(ObjectRef::StringArray(props.to_vec())))) Ok(Value::Ref(ObjectRef::StringArray(props.to_vec())))
} }
} }

View file

@ -1,29 +1,27 @@
use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::rc::Rc;
use std::sync::Arc;
use anyhow::Error; use anyhow::Error;
use crate::class::{Class, get_class, Value}; use crate::class::{Class, Value};
use crate::classloader::CpEntry; use crate::classloader::classdef::CpEntry;
use crate::vm::Vm; use crate::classmanager;
use crate::vm::vm::{Invocation, MethodSignature}; use crate::vm::vm::{Invocation, MethodSignature};
/// the place for opcode implementations that are a bit long /// the place for opcode implementations that are a bit long
// GET_STATIC opcode // GET_STATIC opcode
pub(crate) fn get_static(vm: &mut Vm, this_class: Arc<RefCell<Class>>, field_index: u16) -> Result<Value,Error> { pub(crate) fn get_static(this_class: &Class, field_index: u16) -> Result<Value, Error> {
let this_class = this_class.borrow(); let classdef = classmanager::get_classdef(&this_class.id);
let (class_index, field_name_and_type_index) = 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 classdef.cp_field_ref(&field_index); // all these unwraps are safe as long as the class is valid
let (name_index, _) = let (name_index, _) =
this_class.cp_name_and_type(field_name_and_type_index); classdef.cp_name_and_type(field_name_and_type_index);
let field_name = this_class.cp_utf8(name_index); let field_name = classdef.cp_utf8(name_index);
let that_class_name_index = this_class.cp_class_ref(class_index); let that_class_name_index = classdef.cp_class_ref(class_index);
let that_class_name = this_class.cp_utf8(that_class_name_index); let that_class_name = classdef.cp_utf8(that_class_name_index);
let that_class = get_class(vm, that_class_name.as_str())?; classmanager::load_class_by_name(that_class_name);
let that_class = that_class.borrow(); let that_class = classmanager::get_class_by_name(that_class_name).unwrap();
let type_index = that_class let type_index = that_class
.static_field_mapping .static_field_mapping
@ -32,10 +30,10 @@ pub(crate) fn get_static(vm: &mut Vm, this_class: Arc<RefCell<Class>>, field_ind
.get(field_name) .get(field_name)
.unwrap(); // safe because field must be there .unwrap(); // safe because field must be there
Ok(that_class.static_data[type_index.index].clone()) Ok(classmanager::get_static(&this_class.id, type_index.index))
} }
pub(crate) fn get_name_and_type(cp: Rc<HashMap<u16, CpEntry>>, index: u16) -> Option<MethodSignature> { pub(crate) fn get_name_and_type(cp: &HashMap<u16, CpEntry>, index: u16) -> Option<MethodSignature> {
if let CpEntry::NameAndType(method_name_index, signature_index) = cp.get(&index).unwrap() { if let CpEntry::NameAndType(method_name_index, signature_index) = cp.get(&index).unwrap() {
if let CpEntry::Utf8(method_name) = cp.get(method_name_index).unwrap() { if let CpEntry::Utf8(method_name) = cp.get(method_name_index).unwrap() {
if let CpEntry::Utf8(signature) = cp.get(signature_index).unwrap() { if let CpEntry::Utf8(signature) = cp.get(signature_index).unwrap() {
@ -48,16 +46,17 @@ pub(crate) fn get_name_and_type(cp: Rc<HashMap<u16, CpEntry>>, index: u16) -> Op
} }
None None
} }
pub(crate) fn get_signature_for_invoke(cp: &Rc<HashMap<u16, CpEntry>>, index: u16) -> Option<Invocation> {
pub(crate) fn get_signature_for_invoke(cp: &HashMap<u16, CpEntry>, index: u16) -> Option<Invocation> {
if let CpEntry::MethodRef(class_index, name_and_type_index) if let CpEntry::MethodRef(class_index, name_and_type_index)
| CpEntry::InterfaceMethodref(class_index, name_and_type_index) = cp.get(&index).unwrap() | CpEntry::InterfaceMethodref(class_index, name_and_type_index) = cp.get(&index).unwrap()
{ {
if let Some(method_signature) = get_name_and_type(Rc::clone(&cp), *name_and_type_index) { if let Some(method_signature) = get_name_and_type(cp, *name_and_type_index) {
if let CpEntry::ClassRef(class_name_index) = cp.get(class_index).unwrap() { if let CpEntry::ClassRef(class_name_index) = cp.get(class_index).unwrap() {
if let CpEntry::Utf8(class_name) = cp.get(class_name_index).unwrap() { if let CpEntry::Utf8(class_name) = cp.get(class_name_index).unwrap() {
return Some(Invocation::new( return Some(Invocation::new(
class_name.into(), class_name.into(),
method_signature) method_signature)
); );
} }
} }

View file

@ -1,8 +1,10 @@
use anyhow::Error; use anyhow::Error;
use log::debug;
use crate::class::Value; use crate::class::Value;
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct StackFrame { pub struct StackFrame {
pub(crate) at: String, pub(crate) at: String,
pub(crate) data: Vec<Value>, pub(crate) data: Vec<Value>,
} }
@ -17,12 +19,14 @@ impl StackFrame {
} }
pub(crate) fn push(&mut self, val: Value) { pub(crate) fn push(&mut self, val: Value) {
debug!("push {:?}", val);
self.data.push(val); self.data.push(val);
} }
pub(crate) fn len(&self) -> usize { pub(crate) fn len(&self) -> usize {
self.data.len() self.data.len()
} }
pub(crate) fn pop(&mut self) -> Result<Value, Error> { pub(crate) fn pop(&mut self) -> Result<Value, Error> {
Ok(self.data.pop().unwrap()) Ok(self.data.pop().unwrap())
} }

File diff suppressed because it is too large Load diff

View file

@ -1,18 +1,20 @@
mod test { mod test {
use java_rs::class::Value; use java_rs::class::{ObjectRef, Value};
use java_rs::heap::ObjectRef; use java_rs::classmanager::set_classpath;
use java_rs::vm1::Vm; use java_rs::vm::Vm;
#[test] #[test]
fn if_cmp() { fn if_cmp() {
let mut vm = Vm::new("tests"); let mut stackframes = Vec::new();
let ret = vm.execute_virtual("testclasses.IfCmp", "i_is_1()Z", vec![]).unwrap(); let mut vm = Vm::new(&mut stackframes);
set_classpath("/Users/Shautvast/dev/java/tests");
let ret = vm.execute_virtual(&mut stackframes,"testclasses.IfCmp", "i_is_1()Z", vec![]).unwrap();
unsafe { unsafe {
if let Value::I32(b) = *ret.get() { if let Value::I32(b) = ret {
// internally a boolean is an int // internally a boolean is an int
assert_eq!(0, b); assert_eq!(0, b);
} else { } else {
println!("{:?}", *ret.get()); println!("{:?}", ret);
assert!(false) assert!(false)
} }
} }
@ -20,18 +22,20 @@ mod test {
#[test] #[test]
fn consts() { fn consts() {
let mut vm = Vm::new("tests"); let mut stackframes = Vec::new();
let mut vm = Vm::new(&mut stackframes);
set_classpath("/Users/Shautvast/dev/java/tests");
let ret = vm let ret = vm
.execute_static("testclasses.Const", "hello()Ljava/lang/String;", vec![]) .execute_static(&mut stackframes, "testclasses.Const", "hello()Ljava/lang/String;", vec![])
.unwrap(); .unwrap();
unsafe { unsafe {
if let Value::Ref(s) = &*ret.get() { if let Value::Ref(s) = ret {
// internally a boolean is an int // internally a boolean is an int
if let ObjectRef::Object(a) = &*s.get() { if let ObjectRef::Object(a) = s {
println!("{:?}", a); println!("{:?}", a);
} }
} else { } else {
println!("{:?}", *ret.get()); println!("{:?}", ret);
assert!(false) assert!(false)
} }
} }