proper implementation for non-statics
This commit is contained in:
parent
7313d24777
commit
6cf0365dd0
10 changed files with 353 additions and 115 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
|
@ -196,6 +196,7 @@ name = "java_rs"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"once_cell",
|
||||
"zip",
|
||||
]
|
||||
|
||||
|
|
@ -223,6 +224,12 @@ dependencies = [
|
|||
"adler",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
|
||||
|
||||
[[package]]
|
||||
name = "password-hash"
|
||||
version = "0.4.2"
|
||||
|
|
|
|||
|
|
@ -5,4 +5,5 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
anyhow = { version = "1.0", features = ["default"] }
|
||||
once_cell = { version = "1.18.0", features = [] }
|
||||
zip = { version = "0.6", features = ["zstd"] }
|
||||
157
src/class.rs
157
src/class.rs
|
|
@ -4,27 +4,97 @@ use crate::heap::{Object, ObjectRef};
|
|||
use anyhow::{anyhow, Error};
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
use std::hash::Hash;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::io::read_u16;
|
||||
|
||||
// the class definition as read from the class file + derived values
|
||||
#[derive(Debug)]
|
||||
//TODO create factory function
|
||||
pub struct Class {
|
||||
pub minor_version: u16,
|
||||
pub major_version: u16,
|
||||
pub constant_pool: Rc<HashMap<u16, CpEntry>>,
|
||||
pub access_flags: u16,
|
||||
pub this_class: u16,
|
||||
pub super_class: u16,
|
||||
pub interfaces: Vec<u16>,
|
||||
pub fields: Vec<Field>,
|
||||
pub name: String,
|
||||
pub super_class_name: Option<String>,
|
||||
pub super_class: Option<Rc<Class>>,
|
||||
pub interface_indices: Vec<u16>,
|
||||
pub interfaces: Vec<Class>,
|
||||
pub fields: HashMap<String, Field>,
|
||||
pub methods: HashMap<String, 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)
|
||||
}
|
||||
|
||||
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, 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
|
||||
interface_indices,
|
||||
interfaces: vec![], // same
|
||||
fields,
|
||||
methods,
|
||||
attributes,
|
||||
field_mapping: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn n_fields(&self) -> usize {
|
||||
self.field_mapping.as_ref().map_or(0, |m| m.len())
|
||||
}
|
||||
|
||||
// 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(&mut self) {
|
||||
let mut 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;
|
||||
}
|
||||
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, field_map_index: &mut usize) {
|
||||
let mut this_fields = HashMap::new(); //fields in class are stored per class and every superclass.
|
||||
for field in &class.fields {
|
||||
this_fields.insert(field.0.to_owned(), (field.1.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);
|
||||
}
|
||||
|
||||
pub fn get_version(&self) -> (u16, u16) {
|
||||
(self.major_version, self.minor_version)
|
||||
}
|
||||
|
|
@ -35,16 +105,58 @@ impl Class {
|
|||
.ok_or(anyhow!("Method {} not found", name))
|
||||
}
|
||||
|
||||
pub fn get_name(&self) -> &str {
|
||||
if let CpEntry::ClassRef(name_index ) = self.constant_pool.get(&self.this_class).unwrap(){
|
||||
if let CpEntry::Utf8(name) = self.constant_pool.get(name_index).unwrap(){
|
||||
return 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
|
||||
}
|
||||
}
|
||||
panic!();
|
||||
|
||||
// convienence methods for data from the constantpool
|
||||
|
||||
pub fn get_field_ref(&self, index: &u16) -> Option<(&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))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_class_ref(&self, index: &u16) -> Option<&u16> {
|
||||
if let CpEntry::ClassRef(name_index) = self.constant_pool.get(index).unwrap() {
|
||||
Some(name_index)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_utf8(&self, index: &u16) -> Option<&String> {
|
||||
if let CpEntry::Utf8(utf8) = self.constant_pool.get(index).unwrap() {
|
||||
Some(utf8)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_name_and_type(&self, index: &u16) -> Option<(&u16, &u16)> {
|
||||
if let CpEntry::NameAndType(name_index, type_index) = self.constant_pool.get(index).unwrap(){
|
||||
Some((name_index, type_index))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
unsafe impl Send for Class {}
|
||||
|
||||
unsafe impl Sync for Class {}
|
||||
|
||||
pub struct Method {
|
||||
|
|
@ -101,6 +213,7 @@ pub struct Field {
|
|||
pub(crate) name_index: u16,
|
||||
descriptor_index: u16,
|
||||
attributes: HashMap<String, AttributeType>,
|
||||
index: u16,
|
||||
}
|
||||
|
||||
impl fmt::Debug for Field {
|
||||
|
|
@ -120,6 +233,7 @@ impl Field {
|
|||
name_index: u16,
|
||||
descriptor_index: u16,
|
||||
attributes: HashMap<String, AttributeType>,
|
||||
field_index: u16,
|
||||
) -> Self {
|
||||
Field {
|
||||
constant_pool,
|
||||
|
|
@ -127,18 +241,15 @@ impl Field {
|
|||
name_index,
|
||||
descriptor_index,
|
||||
attributes,
|
||||
index: field_index,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> String {
|
||||
let mut name = String::new();
|
||||
|
||||
name.push(' ');
|
||||
if let CpEntry::Utf8(s) = &self.constant_pool.get(&self.name_index).unwrap() {
|
||||
name.push_str(s);
|
||||
pub fn name(&self) -> &String {
|
||||
if let CpEntry::Utf8(utf8) = &self.constant_pool.get(&self.name_index).unwrap() {
|
||||
return utf8;
|
||||
}
|
||||
|
||||
name
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
pub fn type_of(&self) -> &String {
|
||||
|
|
@ -174,10 +285,11 @@ pub fn get_modifier(modifier: u16) -> String {
|
|||
output
|
||||
}
|
||||
|
||||
//TODO implement more types
|
||||
#[derive(Debug)]
|
||||
pub enum AttributeType {
|
||||
ConstantValue(u16),
|
||||
Code(MethodCode),
|
||||
Code(Box<MethodCode>),
|
||||
StackMapTable,
|
||||
BootstrapMethods,
|
||||
NestHost,
|
||||
|
|
@ -256,8 +368,10 @@ impl MethodCode {
|
|||
|
||||
#[derive(Debug)]
|
||||
pub enum Value {
|
||||
Void, // variant returned for void methods
|
||||
Null, // 'pointer' to nothing
|
||||
Void,
|
||||
// variant returned for void methods
|
||||
Null,
|
||||
// 'pointer' to nothing
|
||||
I32(i32),
|
||||
I64(i64),
|
||||
F32(f32),
|
||||
|
|
@ -268,4 +382,5 @@ pub enum Value {
|
|||
}
|
||||
|
||||
unsafe impl Send for Value {}
|
||||
|
||||
unsafe impl Sync for Value {}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ use anyhow::Error;
|
|||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
|
||||
// The native classoader
|
||||
pub fn load_class(bytecode: Vec<u8>) -> Result<Class, Error> {
|
||||
let pos = &mut 0;
|
||||
check_magic(&bytecode, pos);
|
||||
|
|
@ -37,9 +39,10 @@ pub fn load_class(bytecode: Vec<u8>) -> Result<Class, Error> {
|
|||
}
|
||||
|
||||
let fields_count = read_u16(&bytecode, pos);
|
||||
let mut fields = vec![];
|
||||
for _ in 0..fields_count {
|
||||
fields.push(read_field(constant_pool.clone(), pos, &bytecode));
|
||||
let mut fields = HashMap::new();
|
||||
for i in 0..fields_count {
|
||||
let field = read_field(constant_pool.clone(), pos, &bytecode, i);
|
||||
fields.insert(field.name().to_owned(), field);
|
||||
}
|
||||
|
||||
let methods_count = read_u16(&bytecode, pos);
|
||||
|
|
@ -56,11 +59,11 @@ pub fn load_class(bytecode: Vec<u8>) -> Result<Class, Error> {
|
|||
if let Some(att) = some {
|
||||
attributes.insert(att.0, att.1);
|
||||
} else {
|
||||
panic!(); // bug/not-implemented
|
||||
panic!("attribute not found"); // bug/not-implemented
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Class {
|
||||
Ok(Class::new(
|
||||
minor_version,
|
||||
major_version,
|
||||
constant_pool,
|
||||
|
|
@ -71,7 +74,7 @@ pub fn load_class(bytecode: Vec<u8>) -> Result<Class, Error> {
|
|||
fields,
|
||||
methods,
|
||||
attributes,
|
||||
})
|
||||
))
|
||||
}
|
||||
|
||||
fn check_magic(bytecode: &[u8], pos: &mut usize) {
|
||||
|
|
@ -83,6 +86,7 @@ fn check_magic(bytecode: &[u8], pos: &mut usize) {
|
|||
|
||||
fn read_constant_pool_entry(cp_index: &mut u16, index: &mut usize, bytecode: &[u8]) -> CpEntry {
|
||||
let tag = read_u8(bytecode, index);
|
||||
// println!("tag {}", tag);
|
||||
match tag {
|
||||
1 => {
|
||||
let len = read_u16(bytecode, index) as usize;
|
||||
|
|
@ -137,10 +141,21 @@ fn read_constant_pool_entry(cp_index: &mut u16, index: &mut usize, bytecode: &[u
|
|||
let descriptor_index = read_u16(bytecode, index);
|
||||
CpEntry::NameAndType(name_index, descriptor_index)
|
||||
}
|
||||
// 15 MethodHandle,
|
||||
// 16 MethodType,
|
||||
15 =>{
|
||||
let reference_kind = read_u8(bytecode, index);
|
||||
let reference_index = read_u16(bytecode, index);
|
||||
CpEntry::MethodHandle(reference_kind, reference_index)
|
||||
}
|
||||
16 => {
|
||||
let descriptor_index = read_u16(bytecode, index);
|
||||
CpEntry::MethodType(descriptor_index)
|
||||
}
|
||||
// 17 Dynamic,
|
||||
// 18 InvokeDynamic,
|
||||
18 => {
|
||||
let bootstrap_method_attr_index = read_u16(bytecode, index);
|
||||
let name_and_type_index = read_u16(bytecode, index);
|
||||
CpEntry::InvokeDynamic(bootstrap_method_attr_index, name_and_type_index)
|
||||
}
|
||||
// 19 Module,
|
||||
// 20 Package,
|
||||
_ => panic!("cp entry type not recognized"),
|
||||
|
|
@ -151,6 +166,7 @@ fn read_field(
|
|||
constant_pool: Rc<HashMap<u16, CpEntry>>,
|
||||
index: &mut usize,
|
||||
bytecode: &[u8],
|
||||
field_index: u16,
|
||||
) -> Field {
|
||||
let access_flags = read_u16(bytecode, index);
|
||||
let name_index = read_u16(bytecode, index);
|
||||
|
|
@ -170,6 +186,7 @@ fn read_field(
|
|||
name_index,
|
||||
descriptor_index,
|
||||
attributes,
|
||||
field_index,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -210,7 +227,7 @@ fn read_attribute(
|
|||
*index += attribute_length;
|
||||
|
||||
if let CpEntry::Utf8(s) = &constant_pool.get(&attribute_name_index).unwrap() {
|
||||
// println!("Att [{}]", s);
|
||||
println!("Att [{}]", s);
|
||||
return match s.as_str() {
|
||||
"ConstantValue" => {
|
||||
assert_eq!(info.len(), 2);
|
||||
|
|
@ -241,17 +258,22 @@ fn read_attribute(
|
|||
}
|
||||
Some((
|
||||
"Code".into(),
|
||||
AttributeType::Code(MethodCode::new(
|
||||
AttributeType::Code(Box::new(MethodCode::new(
|
||||
max_stack,
|
||||
max_locals,
|
||||
code,
|
||||
exception_table,
|
||||
code_attributes,
|
||||
)),
|
||||
))),
|
||||
))
|
||||
}
|
||||
"SourceFile" => Some(("SourceFile".into(), AttributeType::SourceFile)),
|
||||
"LineNumberTable" => Some(("SourceFile".into(), AttributeType::LineNumberTable)),
|
||||
"RuntimeVisibleAnnotations" => Some(("".into(), AttributeType::RuntimeInvisibleAnnotations)), //stub
|
||||
"NestMembers" => Some(("".into(), AttributeType::NestMembers)),//stub
|
||||
"BootstrapMethods" => Some(("".into(), AttributeType::BootstrapMethods)),//stub
|
||||
"InnerClasses" => Some(("".into(), AttributeType::InnerClasses)),//stub
|
||||
//TODO more actual attribute implementations
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
|
|
@ -271,4 +293,7 @@ pub enum CpEntry {
|
|||
MethodRef(u16, u16),
|
||||
InterfaceMethodref(u16, u16),
|
||||
NameAndType(u16, u16),
|
||||
MethodHandle(u8, u16),
|
||||
MethodType(u16),
|
||||
InvokeDynamic(u16, u16),
|
||||
}
|
||||
|
|
|
|||
73
src/heap.rs
73
src/heap.rs
|
|
@ -7,15 +7,17 @@ use std::sync::Arc;
|
|||
use crate::class::{Class, Value};
|
||||
use crate::classloader::CpEntry;
|
||||
|
||||
// trying to implement efficient object instance storage
|
||||
pub struct Object {
|
||||
// locked: bool,
|
||||
// hashcode: i32,
|
||||
pub class: Rc<Class>,
|
||||
pub data: HashMap<u16, Arc<UnsafeCell<Value>>>, //TODO optimize
|
||||
pub data: Option<Vec<Arc<UnsafeCell<Value>>>>,
|
||||
}//arrays
|
||||
|
||||
// can contain object or array
|
||||
#[derive(Debug)]
|
||||
pub enum ObjectRef{
|
||||
pub enum ObjectRef {
|
||||
ByteArray(Vec<i8>),
|
||||
ShortArray(Vec<i16>),
|
||||
IntArray(Vec<i32>),
|
||||
|
|
@ -28,45 +30,78 @@ pub enum ObjectRef{
|
|||
Object(Box<Object>),
|
||||
}
|
||||
|
||||
|
||||
unsafe impl Send for Object {}
|
||||
|
||||
unsafe impl Sync for Object {}
|
||||
|
||||
// object, not array
|
||||
impl Object {
|
||||
pub fn new(class: Rc<Class>, data: HashMap<u16, Arc<UnsafeCell<Value>>>) -> Self {
|
||||
Self { class, data }
|
||||
pub fn new(class: Rc<Class>) -> Self {
|
||||
Self { class, data: None }
|
||||
}
|
||||
|
||||
fn get_field(&self, cp_index: &u16) -> &str {
|
||||
// initializes all non-static fields to their default values
|
||||
pub(crate) fn init_fields(&mut self) {
|
||||
let mut field_data = Vec::with_capacity(self.class.n_fields());
|
||||
|
||||
for (_, fields) in self.class.field_mapping.as_ref().unwrap() {
|
||||
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(Arc::new(UnsafeCell::new(value)));
|
||||
}
|
||||
}
|
||||
|
||||
self.data = Some(field_data);
|
||||
}
|
||||
|
||||
pub fn set(&mut self, class_name: &String, field_name: &String, value: Arc<UnsafeCell<Value>>) {
|
||||
let p = self.class.field_mapping.as_ref().unwrap();
|
||||
let p2 = p.get(class_name).unwrap();
|
||||
let (_type, index) = p2.get(field_name).unwrap();
|
||||
self.data.as_mut().unwrap()[*index] = value;
|
||||
}
|
||||
|
||||
pub fn get(&mut self, class_name: &String, field_name: &String) -> &Arc<UnsafeCell<Value>> {
|
||||
let (_type, index) = &self.class.field_mapping.as_ref().unwrap().get(class_name).unwrap().get(field_name).unwrap(); // (index, type)
|
||||
&self.data.as_ref().unwrap()[*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!()
|
||||
}
|
||||
|
||||
unsafe fn get_mut<T>(ptr: &UnsafeCell<T>) -> &mut T {
|
||||
unsafe { &mut *ptr.get() }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Object {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let fields: Vec<String> = self.data.iter().map(|(k, v)| {
|
||||
let mut r: String = self.get_field(k).into();
|
||||
r.push(':');
|
||||
r.push_str(format!("{:?}", v).as_str());
|
||||
r
|
||||
}
|
||||
).collect();
|
||||
// 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.get_name(), fields
|
||||
"{}",
|
||||
self.class.name
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// will using Arc's enable a GC-less heap????
|
||||
pub(crate) struct Heap {
|
||||
objects: Vec<Arc<UnsafeCell<ObjectRef>>>,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ pub const FRETURN: u8 = 174; // (0xae) Return float from method
|
|||
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 GETSTATIC: u8 = 178; // (0xb2) Get static field from 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
|
||||
|
|
|
|||
111
src/vm.rs
111
src/vm.rs
|
|
@ -4,6 +4,7 @@ use std::rc::Rc;
|
|||
use std::sync::Arc;
|
||||
|
||||
use anyhow::{anyhow, Error};
|
||||
use once_cell::unsync::Lazy;
|
||||
|
||||
use crate::class::{AttributeType, Class, Value};
|
||||
use crate::class::Value::Void;
|
||||
|
|
@ -18,6 +19,7 @@ struct StackFrame {
|
|||
data: Vec<Arc<UnsafeCell<Value>>>,
|
||||
}
|
||||
|
||||
// maybe just call frame
|
||||
impl StackFrame {
|
||||
fn new(at_class: &str, at_method: &str) -> Self {
|
||||
let mut at: String = at_class.into();
|
||||
|
|
@ -39,9 +41,11 @@ 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, Rc<Class>>> = Lazy::new(|| HashMap::new()); //TODO add mutex...and Arc most likely
|
||||
|
||||
pub struct Vm {
|
||||
classpath: Vec<String>,
|
||||
classes: HashMap<String, Rc<Class>>,
|
||||
heap: Heap,
|
||||
stack: Vec<StackFrame>,
|
||||
}
|
||||
|
|
@ -52,7 +56,7 @@ const PATH_SEPARATOR: char = ':';
|
|||
#[cfg(target_family = "windows")]
|
||||
const PATH_SEPARATOR: char = ';';
|
||||
|
||||
|
||||
// The singlethreaded VM (maybe a future Thread)
|
||||
impl Vm {
|
||||
fn local_stack(&mut self) -> &mut StackFrame {
|
||||
let i = self.stack.len() - 1;
|
||||
|
|
@ -62,49 +66,51 @@ impl Vm {
|
|||
pub fn new(classpath: &'static str) -> Self {
|
||||
Self {
|
||||
classpath: classpath.split(PATH_SEPARATOR).map(|s| s.to_owned()).collect(),
|
||||
classes: HashMap::new(),
|
||||
heap: Heap::new(),
|
||||
stack: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
/// parse the binary data into a Class struct
|
||||
/// gets the file from cache, or reads it from classpath
|
||||
/// Vm keeps ownership of the class and hands out Arc references to it
|
||||
pub fn get_class(&mut self, class_name: &str) -> Result<Rc<Class>, Error> {
|
||||
// parse the binary data into a Class struct
|
||||
// gets the file from cache, or reads it from classpath
|
||||
// Vm keeps ownership of the class and hands out Arc references to it
|
||||
pub fn get_class(&self, class_name: &str) -> Result<Rc<Class>, Error> {
|
||||
println!("get_class {}", class_name);
|
||||
let entry = self.classes.entry(class_name.into());
|
||||
unsafe {
|
||||
let entry = CLASSDEFS.entry(class_name.into());
|
||||
let entry = entry.or_insert_with(|| {
|
||||
// print!("read class {} ", class_name);
|
||||
let resolved_path = find_class(&self.classpath, class_name).expect("Class not found");
|
||||
let resolved_path = find_class(&self.classpath, class_name).unwrap();
|
||||
// println!("full path {}", resolved_path);
|
||||
let bytecode = read_bytecode(resolved_path).unwrap();
|
||||
Rc::new(load_class(bytecode).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();
|
||||
|
||||
Rc::new(class)
|
||||
});
|
||||
Ok(entry.clone())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_instance(&self, class: Rc<Class>) -> Object {
|
||||
//TODO add fields from superclasses
|
||||
let mut data = HashMap::new();
|
||||
for f in &class.fields {
|
||||
let value = match f.type_of().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,
|
||||
};
|
||||
data.insert(f.name_index, Arc::new(UnsafeCell::new(value)));
|
||||
}
|
||||
Object::new(class.clone(), data)
|
||||
pub fn new_instance(class: Rc<Class>) -> Object {
|
||||
let mut class = class;
|
||||
// let mut outer = HashMap::new();
|
||||
//TODO
|
||||
|
||||
let mut instance = Object::new(class.clone());
|
||||
instance.init_fields();
|
||||
instance
|
||||
}
|
||||
|
||||
|
||||
/// execute the bytecode
|
||||
/// contains unsafe, as I think that mimics not-synchronized memory access in the original JVM
|
||||
pub fn execute(
|
||||
&mut self,
|
||||
class_name: &str,
|
||||
|
|
@ -269,41 +275,45 @@ impl Vm {
|
|||
self.stack.pop(); // Void is also returned as a value
|
||||
return Ok(Arc::new(UnsafeCell::new(Void)));
|
||||
}
|
||||
GETSTATIC => {
|
||||
let cp_index = read_u16(&code.opcodes, pc);
|
||||
let (class_index, _field_name_and_type_index) = class.get_field_ref(&cp_index).unwrap();
|
||||
let class_name_index = class.get_class_ref(class_index).unwrap();
|
||||
let class_name = class.get_utf8(class_name_index).unwrap();
|
||||
let class = self.get_class(class_name.as_str())?;
|
||||
println!("{:?}", class); //TODO
|
||||
}
|
||||
GETFIELD => {
|
||||
unsafe {
|
||||
let cp_index = read_u16(&code.opcodes, pc);
|
||||
if let CpEntry::Fieldref(_class_index, name_and_type_index) =
|
||||
method.constant_pool.get(&cp_index).unwrap()
|
||||
{
|
||||
if let Value::Ref(instance) = &*self.local_stack().pop()?.get() {
|
||||
if let CpEntry::NameAndType(name, _) =
|
||||
method.constant_pool.get(name_and_type_index).unwrap()
|
||||
{
|
||||
let objectref = &(*instance.get());
|
||||
if let ObjectRef::Object(object) = objectref {
|
||||
let value = object.data.get(name).unwrap();
|
||||
let (class_index, field_name_and_type_index) = class.get_field_ref(&cp_index).unwrap();
|
||||
let (field_name_index, _) = class.get_name_and_type(field_name_and_type_index).unwrap();
|
||||
let class_name_index = class.get_class_ref(class_index).unwrap();
|
||||
let class_name = class.get_utf8(class_name_index).unwrap();
|
||||
let field_name = class.get_utf8(field_name_index).unwrap();
|
||||
|
||||
let mut objectref = self.local_stack().pop()?;
|
||||
if let Value::Ref(instance) = &mut *objectref.get() {
|
||||
if let ObjectRef::Object(ref mut object) = &mut *instance.get() {
|
||||
let value = object.get(class_name, field_name);
|
||||
self.local_stack().push_arc(Arc::clone(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
PUTFIELD => unsafe {
|
||||
let cp_index = read_u16(&code.opcodes, pc);
|
||||
if let CpEntry::Fieldref(_class_index, name_and_type_index) =
|
||||
method.constant_pool.get(&cp_index).unwrap()
|
||||
{
|
||||
if let CpEntry::NameAndType(name_index, _) = method.constant_pool.get(name_and_type_index).unwrap() {
|
||||
let (class_index, field_name_and_type_index) = class.get_field_ref(&cp_index).unwrap();
|
||||
let (field_name_index, _) = class.get_name_and_type(field_name_and_type_index).unwrap();
|
||||
let class_name_index = class.get_class_ref(class_index).unwrap();
|
||||
let class_name = class.get_utf8(class_name_index).unwrap();
|
||||
let field_name = class.get_utf8(field_name_index).unwrap();
|
||||
|
||||
let value = self.local_stack().pop()?;
|
||||
let mut objectref = self.local_stack().pop()?;
|
||||
if let Value::Ref(instance) = &mut *objectref.get() {
|
||||
if let ObjectRef::Object(ref mut object) = &mut *instance.get() {
|
||||
object.data.insert(*name_index, value);
|
||||
} else {
|
||||
panic!("not an object, maybe array");
|
||||
}
|
||||
} // else?
|
||||
object.set(class_name, field_name, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -348,7 +358,7 @@ impl Vm {
|
|||
{
|
||||
println!("new {}", new_class);
|
||||
let class = self.get_class(new_class)?;
|
||||
let object = Arc::new(UnsafeCell::new(ObjectRef::Object(Box::new(self.new_instance(class)))));
|
||||
let object = Arc::new(UnsafeCell::new(ObjectRef::Object(Box::new(Vm::new_instance(class)))));
|
||||
self.local_stack().push(Value::Ref(Arc::clone(&object)));
|
||||
self.heap.new_object(object);
|
||||
}
|
||||
|
|
@ -523,6 +533,7 @@ fn get_name_and_type(cp: Rc<HashMap<u16, CpEntry>>, index: u16) -> Option<Method
|
|||
None
|
||||
}
|
||||
|
||||
|
||||
fn get_hum_args(signature: &str) -> usize {
|
||||
let mut num = 0;
|
||||
let mut i = 1;
|
||||
|
|
|
|||
44
tests/Inheritance.java
Normal file
44
tests/Inheritance.java
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
public class Inheritance {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
Father father = new Son();
|
||||
System.out.println(father.i); //why 1?
|
||||
System.out.println(father.getI()); //2
|
||||
System.out.println(father.j); //why 10?
|
||||
System.out.println(father.getJ()); //why 10?
|
||||
|
||||
System.out.println();
|
||||
|
||||
Son son = new Son();
|
||||
System.out.println(son.i); //2
|
||||
System.out.println(son.getI()); //2
|
||||
System.out.println(son.j); //20
|
||||
System.out.println(son.getJ()); //why 10?
|
||||
}
|
||||
}
|
||||
|
||||
class Son extends Father {
|
||||
|
||||
int i = 2;
|
||||
int j = 20;
|
||||
|
||||
@Override
|
||||
public int getI() {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
class Father {
|
||||
|
||||
int i = 1;
|
||||
int j = 10;
|
||||
|
||||
public int getI() {
|
||||
return i;
|
||||
}
|
||||
|
||||
public int getJ() {
|
||||
return j;
|
||||
}
|
||||
}
|
||||
BIN
tests/Main.class
BIN
tests/Main.class
Binary file not shown.
|
|
@ -3,6 +3,6 @@ public class Main {
|
|||
public static void main(String[] args){
|
||||
FloatBean f = new FloatBean();
|
||||
f.setValue(42F);
|
||||
System.out.println(f.getValue());
|
||||
// System.out.println(f.getValue());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue