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"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"once_cell",
|
||||||
"zip",
|
"zip",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -223,6 +224,12 @@ dependencies = [
|
||||||
"adler",
|
"adler",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.18.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "password-hash"
|
name = "password-hash"
|
||||||
version = "0.4.2"
|
version = "0.4.2"
|
||||||
|
|
|
||||||
|
|
@ -5,4 +5,5 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = { version = "1.0", features = ["default"] }
|
anyhow = { version = "1.0", features = ["default"] }
|
||||||
|
once_cell = { version = "1.18.0", features = [] }
|
||||||
zip = { version = "0.6", features = ["zstd"] }
|
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 anyhow::{anyhow, Error};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::hash::Hash;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::io::read_u16;
|
use crate::io::read_u16;
|
||||||
|
|
||||||
|
// the class definition as read from the class file + derived values
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
//TODO create factory function
|
|
||||||
pub struct Class {
|
pub struct Class {
|
||||||
pub minor_version: u16,
|
pub minor_version: u16,
|
||||||
pub major_version: u16,
|
pub major_version: u16,
|
||||||
pub constant_pool: Rc<HashMap<u16, CpEntry>>,
|
pub constant_pool: Rc<HashMap<u16, CpEntry>>,
|
||||||
pub access_flags: u16,
|
pub access_flags: u16,
|
||||||
pub this_class: u16,
|
pub name: String,
|
||||||
pub super_class: u16,
|
pub super_class_name: Option<String>,
|
||||||
pub interfaces: Vec<u16>,
|
pub super_class: Option<Rc<Class>>,
|
||||||
pub fields: Vec<Field>,
|
pub interface_indices: Vec<u16>,
|
||||||
|
pub interfaces: Vec<Class>,
|
||||||
|
pub fields: HashMap<String, Field>,
|
||||||
pub methods: HashMap<String, Method>,
|
pub methods: HashMap<String, Method>,
|
||||||
pub attributes: HashMap<String, AttributeType>,
|
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 {
|
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) {
|
pub fn get_version(&self) -> (u16, u16) {
|
||||||
(self.major_version, self.minor_version)
|
(self.major_version, self.minor_version)
|
||||||
}
|
}
|
||||||
|
|
@ -35,16 +105,58 @@ impl Class {
|
||||||
.ok_or(anyhow!("Method {} not found", name))
|
.ok_or(anyhow!("Method {} not found", name))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_name(&self) -> &str {
|
fn class_name(super_class_index: u16, constant_pool: Rc<HashMap<u16, CpEntry>>) -> Option<String> {
|
||||||
if let CpEntry::ClassRef(name_index ) = self.constant_pool.get(&self.this_class).unwrap(){
|
if super_class_index == 0 {
|
||||||
if let CpEntry::Utf8(name) = self.constant_pool.get(name_index).unwrap(){
|
None
|
||||||
return name;
|
} 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 Send for Class {}
|
||||||
|
|
||||||
unsafe impl Sync for Class {}
|
unsafe impl Sync for Class {}
|
||||||
|
|
||||||
pub struct Method {
|
pub struct Method {
|
||||||
|
|
@ -101,6 +213,7 @@ pub struct Field {
|
||||||
pub(crate) name_index: u16,
|
pub(crate) name_index: u16,
|
||||||
descriptor_index: u16,
|
descriptor_index: u16,
|
||||||
attributes: HashMap<String, AttributeType>,
|
attributes: HashMap<String, AttributeType>,
|
||||||
|
index: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Field {
|
impl fmt::Debug for Field {
|
||||||
|
|
@ -120,6 +233,7 @@ impl Field {
|
||||||
name_index: u16,
|
name_index: u16,
|
||||||
descriptor_index: u16,
|
descriptor_index: u16,
|
||||||
attributes: HashMap<String, AttributeType>,
|
attributes: HashMap<String, AttributeType>,
|
||||||
|
field_index: u16,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Field {
|
Field {
|
||||||
constant_pool,
|
constant_pool,
|
||||||
|
|
@ -127,18 +241,15 @@ impl Field {
|
||||||
name_index,
|
name_index,
|
||||||
descriptor_index,
|
descriptor_index,
|
||||||
attributes,
|
attributes,
|
||||||
|
index: field_index,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn name(&self) -> String {
|
pub fn name(&self) -> &String {
|
||||||
let mut name = String::new();
|
if let CpEntry::Utf8(utf8) = &self.constant_pool.get(&self.name_index).unwrap() {
|
||||||
|
return utf8;
|
||||||
name.push(' ');
|
|
||||||
if let CpEntry::Utf8(s) = &self.constant_pool.get(&self.name_index).unwrap() {
|
|
||||||
name.push_str(s);
|
|
||||||
}
|
}
|
||||||
|
unreachable!()
|
||||||
name
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn type_of(&self) -> &String {
|
pub fn type_of(&self) -> &String {
|
||||||
|
|
@ -174,10 +285,11 @@ pub fn get_modifier(modifier: u16) -> String {
|
||||||
output
|
output
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO implement more types
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum AttributeType {
|
pub enum AttributeType {
|
||||||
ConstantValue(u16),
|
ConstantValue(u16),
|
||||||
Code(MethodCode),
|
Code(Box<MethodCode>),
|
||||||
StackMapTable,
|
StackMapTable,
|
||||||
BootstrapMethods,
|
BootstrapMethods,
|
||||||
NestHost,
|
NestHost,
|
||||||
|
|
@ -256,8 +368,10 @@ impl MethodCode {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Value {
|
pub enum Value {
|
||||||
Void, // variant returned for void methods
|
Void,
|
||||||
Null, // 'pointer' to nothing
|
// variant returned for void methods
|
||||||
|
Null,
|
||||||
|
// 'pointer' to nothing
|
||||||
I32(i32),
|
I32(i32),
|
||||||
I64(i64),
|
I64(i64),
|
||||||
F32(f32),
|
F32(f32),
|
||||||
|
|
@ -268,4 +382,5 @@ pub enum Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for Value {}
|
unsafe impl Send for Value {}
|
||||||
|
|
||||||
unsafe impl Sync for Value {}
|
unsafe impl Sync for Value {}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@ use anyhow::Error;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
|
||||||
|
// The native classoader
|
||||||
pub fn load_class(bytecode: Vec<u8>) -> Result<Class, Error> {
|
pub fn load_class(bytecode: Vec<u8>) -> Result<Class, Error> {
|
||||||
let pos = &mut 0;
|
let pos = &mut 0;
|
||||||
check_magic(&bytecode, pos);
|
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 fields_count = read_u16(&bytecode, pos);
|
||||||
let mut fields = vec![];
|
let mut fields = HashMap::new();
|
||||||
for _ in 0..fields_count {
|
for i in 0..fields_count {
|
||||||
fields.push(read_field(constant_pool.clone(), pos, &bytecode));
|
let field = read_field(constant_pool.clone(), pos, &bytecode, i);
|
||||||
|
fields.insert(field.name().to_owned(), field);
|
||||||
}
|
}
|
||||||
|
|
||||||
let methods_count = read_u16(&bytecode, pos);
|
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 {
|
if let Some(att) = some {
|
||||||
attributes.insert(att.0, att.1);
|
attributes.insert(att.0, att.1);
|
||||||
} else {
|
} else {
|
||||||
panic!(); // bug/not-implemented
|
panic!("attribute not found"); // bug/not-implemented
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Class {
|
Ok(Class::new(
|
||||||
minor_version,
|
minor_version,
|
||||||
major_version,
|
major_version,
|
||||||
constant_pool,
|
constant_pool,
|
||||||
|
|
@ -71,7 +74,7 @@ pub fn load_class(bytecode: Vec<u8>) -> Result<Class, Error> {
|
||||||
fields,
|
fields,
|
||||||
methods,
|
methods,
|
||||||
attributes,
|
attributes,
|
||||||
})
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_magic(bytecode: &[u8], pos: &mut usize) {
|
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 {
|
fn read_constant_pool_entry(cp_index: &mut u16, index: &mut usize, bytecode: &[u8]) -> CpEntry {
|
||||||
let tag = read_u8(bytecode, index);
|
let tag = read_u8(bytecode, index);
|
||||||
|
// println!("tag {}", tag);
|
||||||
match tag {
|
match tag {
|
||||||
1 => {
|
1 => {
|
||||||
let len = read_u16(bytecode, index) as usize;
|
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);
|
let descriptor_index = read_u16(bytecode, index);
|
||||||
CpEntry::NameAndType(name_index, descriptor_index)
|
CpEntry::NameAndType(name_index, descriptor_index)
|
||||||
}
|
}
|
||||||
// 15 MethodHandle,
|
15 =>{
|
||||||
// 16 MethodType,
|
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,
|
// 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,
|
// 19 Module,
|
||||||
// 20 Package,
|
// 20 Package,
|
||||||
_ => panic!("cp entry type not recognized"),
|
_ => panic!("cp entry type not recognized"),
|
||||||
|
|
@ -151,6 +166,7 @@ fn read_field(
|
||||||
constant_pool: Rc<HashMap<u16, CpEntry>>,
|
constant_pool: Rc<HashMap<u16, CpEntry>>,
|
||||||
index: &mut usize,
|
index: &mut usize,
|
||||||
bytecode: &[u8],
|
bytecode: &[u8],
|
||||||
|
field_index: u16,
|
||||||
) -> Field {
|
) -> Field {
|
||||||
let access_flags = read_u16(bytecode, index);
|
let access_flags = read_u16(bytecode, index);
|
||||||
let name_index = read_u16(bytecode, index);
|
let name_index = read_u16(bytecode, index);
|
||||||
|
|
@ -170,6 +186,7 @@ fn read_field(
|
||||||
name_index,
|
name_index,
|
||||||
descriptor_index,
|
descriptor_index,
|
||||||
attributes,
|
attributes,
|
||||||
|
field_index,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -210,7 +227,7 @@ 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);
|
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);
|
||||||
|
|
@ -241,17 +258,22 @@ fn read_attribute(
|
||||||
}
|
}
|
||||||
Some((
|
Some((
|
||||||
"Code".into(),
|
"Code".into(),
|
||||||
AttributeType::Code(MethodCode::new(
|
AttributeType::Code(Box::new(MethodCode::new(
|
||||||
max_stack,
|
max_stack,
|
||||||
max_locals,
|
max_locals,
|
||||||
code,
|
code,
|
||||||
exception_table,
|
exception_table,
|
||||||
code_attributes,
|
code_attributes,
|
||||||
)),
|
))),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
"SourceFile" => Some(("SourceFile".into(), AttributeType::SourceFile)),
|
"SourceFile" => Some(("SourceFile".into(), AttributeType::SourceFile)),
|
||||||
"LineNumberTable" => Some(("SourceFile".into(), AttributeType::LineNumberTable)),
|
"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,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -271,4 +293,7 @@ pub enum CpEntry {
|
||||||
MethodRef(u16, u16),
|
MethodRef(u16, u16),
|
||||||
InterfaceMethodref(u16, u16),
|
InterfaceMethodref(u16, u16),
|
||||||
NameAndType(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::class::{Class, Value};
|
||||||
use crate::classloader::CpEntry;
|
use crate::classloader::CpEntry;
|
||||||
|
|
||||||
|
// trying to implement efficient object instance storage
|
||||||
pub struct Object {
|
pub struct Object {
|
||||||
// locked: bool,
|
// locked: bool,
|
||||||
// hashcode: i32,
|
// hashcode: i32,
|
||||||
pub class: Rc<Class>,
|
pub class: Rc<Class>,
|
||||||
pub data: HashMap<u16, Arc<UnsafeCell<Value>>>, //TODO optimize
|
pub data: Option<Vec<Arc<UnsafeCell<Value>>>>,
|
||||||
}//arrays
|
}//arrays
|
||||||
|
|
||||||
|
// can contain object or array
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ObjectRef{
|
pub enum ObjectRef {
|
||||||
ByteArray(Vec<i8>),
|
ByteArray(Vec<i8>),
|
||||||
ShortArray(Vec<i16>),
|
ShortArray(Vec<i16>),
|
||||||
IntArray(Vec<i32>),
|
IntArray(Vec<i32>),
|
||||||
|
|
@ -28,45 +30,78 @@ pub enum ObjectRef{
|
||||||
Object(Box<Object>),
|
Object(Box<Object>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
unsafe impl Send for Object {}
|
unsafe impl Send for Object {}
|
||||||
|
|
||||||
unsafe impl Sync for Object {}
|
unsafe impl Sync for Object {}
|
||||||
|
|
||||||
|
// object, not array
|
||||||
impl Object {
|
impl Object {
|
||||||
pub fn new(class: Rc<Class>, data: HashMap<u16, Arc<UnsafeCell<Value>>>) -> Self {
|
pub fn new(class: Rc<Class>) -> Self {
|
||||||
Self { class, data }
|
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() {
|
if let CpEntry::Utf8(name) = self.class.constant_pool.get(cp_index).unwrap() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
panic!()
|
panic!()
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn get_mut<T>(ptr: &UnsafeCell<T>) -> &mut T {
|
|
||||||
unsafe { &mut *ptr.get() }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Object {
|
impl fmt::Debug for Object {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
let fields: Vec<String> = self.data.iter().map(|(k, v)| {
|
// let fields: Vec<String> = self.data.unwrap().iter().map(|(k)| {
|
||||||
let mut r: String = self.get_field(k).into();
|
// // let mut r: String = self.get_field_name(k).into();
|
||||||
r.push(':');
|
// // r.push(':');
|
||||||
r.push_str(format!("{:?}", v).as_str());
|
// // r.push_str(format!("{:?}").as_str());
|
||||||
r
|
// // r
|
||||||
}
|
// }
|
||||||
).collect();
|
// ).collect();
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
"{} {{ {:?} }}",
|
"{}",
|
||||||
self.class.get_name(), fields
|
self.class.name
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// will using Arc's enable a GC-less heap????
|
||||||
pub(crate) struct Heap {
|
pub(crate) struct Heap {
|
||||||
objects: Vec<Arc<UnsafeCell<ObjectRef>>>,
|
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 DRETURN: u8 = 175; // (0xaf) Return double from method
|
||||||
// pub const areturn: u8 = 176; //(0xb0) return reference
|
// 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 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 GETFIELD: u8 = 180; // (0xb4) Fetch field from object3
|
||||||
pub const PUTFIELD: u8 = 181; // (0xb5) Set field in object
|
pub const PUTFIELD: u8 = 181; // (0xb5) Set field in object
|
||||||
pub const INVOKEVIRTUAL: u8 = 182; // (0xb6) Invoke instance method; dispatch based on class
|
pub const INVOKEVIRTUAL: u8 = 182; // (0xb6) Invoke instance method; dispatch based on class
|
||||||
|
|
|
||||||
133
src/vm.rs
133
src/vm.rs
|
|
@ -4,6 +4,7 @@ use std::rc::Rc;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use anyhow::{anyhow, Error};
|
use anyhow::{anyhow, Error};
|
||||||
|
use once_cell::unsync::Lazy;
|
||||||
|
|
||||||
use crate::class::{AttributeType, Class, Value};
|
use crate::class::{AttributeType, Class, Value};
|
||||||
use crate::class::Value::Void;
|
use crate::class::Value::Void;
|
||||||
|
|
@ -18,6 +19,7 @@ struct StackFrame {
|
||||||
data: Vec<Arc<UnsafeCell<Value>>>,
|
data: Vec<Arc<UnsafeCell<Value>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// maybe just call frame
|
||||||
impl StackFrame {
|
impl StackFrame {
|
||||||
fn new(at_class: &str, at_method: &str) -> Self {
|
fn new(at_class: &str, at_method: &str) -> Self {
|
||||||
let mut at: String = at_class.into();
|
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 {
|
pub struct Vm {
|
||||||
classpath: Vec<String>,
|
classpath: Vec<String>,
|
||||||
classes: HashMap<String, Rc<Class>>,
|
|
||||||
heap: Heap,
|
heap: Heap,
|
||||||
stack: Vec<StackFrame>,
|
stack: Vec<StackFrame>,
|
||||||
}
|
}
|
||||||
|
|
@ -52,7 +56,7 @@ const PATH_SEPARATOR: char = ':';
|
||||||
#[cfg(target_family = "windows")]
|
#[cfg(target_family = "windows")]
|
||||||
const PATH_SEPARATOR: char = ';';
|
const PATH_SEPARATOR: char = ';';
|
||||||
|
|
||||||
|
// The singlethreaded VM (maybe a future Thread)
|
||||||
impl Vm {
|
impl Vm {
|
||||||
fn local_stack(&mut self) -> &mut StackFrame {
|
fn local_stack(&mut self) -> &mut StackFrame {
|
||||||
let i = self.stack.len() - 1;
|
let i = self.stack.len() - 1;
|
||||||
|
|
@ -62,49 +66,51 @@ impl Vm {
|
||||||
pub fn new(classpath: &'static str) -> Self {
|
pub fn new(classpath: &'static str) -> Self {
|
||||||
Self {
|
Self {
|
||||||
classpath: classpath.split(PATH_SEPARATOR).map(|s| s.to_owned()).collect(),
|
classpath: classpath.split(PATH_SEPARATOR).map(|s| s.to_owned()).collect(),
|
||||||
classes: HashMap::new(),
|
|
||||||
heap: Heap::new(),
|
heap: Heap::new(),
|
||||||
stack: vec![],
|
stack: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// parse the binary data into a Class struct
|
// parse the binary data into a Class struct
|
||||||
/// gets the file from cache, or reads it from classpath
|
// gets the file from cache, or reads it from classpath
|
||||||
/// Vm keeps ownership of the class and hands out Arc references to it
|
// 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> {
|
pub fn get_class(&self, class_name: &str) -> Result<Rc<Class>, Error> {
|
||||||
println!("get_class {}", class_name);
|
println!("get_class {}", class_name);
|
||||||
let entry = self.classes.entry(class_name.into());
|
unsafe {
|
||||||
let entry = entry.or_insert_with(|| {
|
let entry = CLASSDEFS.entry(class_name.into());
|
||||||
// print!("read class {} ", class_name);
|
let entry = entry.or_insert_with(|| {
|
||||||
let resolved_path = find_class(&self.classpath, class_name).expect("Class not found");
|
// print!("read class {} ", class_name);
|
||||||
// println!("full path {}", resolved_path);
|
let resolved_path = find_class(&self.classpath, class_name).unwrap();
|
||||||
let bytecode = read_bytecode(resolved_path).unwrap();
|
// println!("full path {}", resolved_path);
|
||||||
Rc::new(load_class(bytecode).unwrap())
|
let bytecode = read_bytecode(resolved_path).unwrap();
|
||||||
});
|
let mut class = load_class(bytecode).unwrap();
|
||||||
Ok(entry.clone())
|
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 {
|
pub fn new_instance(class: Rc<Class>) -> Object {
|
||||||
//TODO add fields from superclasses
|
let mut class = class;
|
||||||
let mut data = HashMap::new();
|
// let mut outer = HashMap::new();
|
||||||
for f in &class.fields {
|
//TODO
|
||||||
let value = match f.type_of().as_str() {
|
|
||||||
"Z" => Value::BOOL(false),
|
let mut instance = Object::new(class.clone());
|
||||||
"B" => Value::I32(0),
|
instance.init_fields();
|
||||||
"S" => Value::I32(0),
|
instance
|
||||||
"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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// execute the bytecode
|
/// execute the bytecode
|
||||||
|
/// contains unsafe, as I think that mimics not-synchronized memory access in the original JVM
|
||||||
pub fn execute(
|
pub fn execute(
|
||||||
&mut self,
|
&mut self,
|
||||||
class_name: &str,
|
class_name: &str,
|
||||||
|
|
@ -269,41 +275,45 @@ impl Vm {
|
||||||
self.stack.pop(); // Void is also returned as a value
|
self.stack.pop(); // Void is also returned as a value
|
||||||
return Ok(Arc::new(UnsafeCell::new(Void)));
|
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 => {
|
GETFIELD => {
|
||||||
unsafe {
|
unsafe {
|
||||||
let cp_index = read_u16(&code.opcodes, pc);
|
let cp_index = read_u16(&code.opcodes, pc);
|
||||||
if let CpEntry::Fieldref(_class_index, name_and_type_index) =
|
let (class_index, field_name_and_type_index) = class.get_field_ref(&cp_index).unwrap();
|
||||||
method.constant_pool.get(&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();
|
||||||
if let Value::Ref(instance) = &*self.local_stack().pop()?.get() {
|
let class_name = class.get_utf8(class_name_index).unwrap();
|
||||||
if let CpEntry::NameAndType(name, _) =
|
let field_name = class.get_utf8(field_name_index).unwrap();
|
||||||
method.constant_pool.get(name_and_type_index).unwrap()
|
|
||||||
{
|
let mut objectref = self.local_stack().pop()?;
|
||||||
let objectref = &(*instance.get());
|
if let Value::Ref(instance) = &mut *objectref.get() {
|
||||||
if let ObjectRef::Object(object) = objectref {
|
if let ObjectRef::Object(ref mut object) = &mut *instance.get() {
|
||||||
let value = object.data.get(name).unwrap();
|
let value = object.get(class_name, field_name);
|
||||||
self.local_stack().push_arc(Arc::clone(value));
|
self.local_stack().push_arc(Arc::clone(value));
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PUTFIELD => unsafe {
|
PUTFIELD => unsafe {
|
||||||
let cp_index = read_u16(&code.opcodes, pc);
|
let cp_index = read_u16(&code.opcodes, pc);
|
||||||
if let CpEntry::Fieldref(_class_index, name_and_type_index) =
|
let (class_index, field_name_and_type_index) = class.get_field_ref(&cp_index).unwrap();
|
||||||
method.constant_pool.get(&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();
|
||||||
if let CpEntry::NameAndType(name_index, _) = method.constant_pool.get(name_and_type_index).unwrap() {
|
let class_name = class.get_utf8(class_name_index).unwrap();
|
||||||
let value = self.local_stack().pop()?;
|
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() {
|
let value = self.local_stack().pop()?;
|
||||||
if let ObjectRef::Object(ref mut object) = &mut *instance.get() {
|
let mut objectref = self.local_stack().pop()?;
|
||||||
object.data.insert(*name_index, value);
|
if let Value::Ref(instance) = &mut *objectref.get() {
|
||||||
} else {
|
if let ObjectRef::Object(ref mut object) = &mut *instance.get() {
|
||||||
panic!("not an object, maybe array");
|
object.set(class_name, field_name, value);
|
||||||
}
|
|
||||||
} // else?
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -348,7 +358,7 @@ impl Vm {
|
||||||
{
|
{
|
||||||
println!("new {}", new_class);
|
println!("new {}", new_class);
|
||||||
let class = self.get_class(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.local_stack().push(Value::Ref(Arc::clone(&object)));
|
||||||
self.heap.new_object(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
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn get_hum_args(signature: &str) -> usize {
|
fn get_hum_args(signature: &str) -> usize {
|
||||||
let mut num = 0;
|
let mut num = 0;
|
||||||
let mut i = 1;
|
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){
|
public static void main(String[] args){
|
||||||
FloatBean f = new FloatBean();
|
FloatBean f = new FloatBean();
|
||||||
f.setValue(42F);
|
f.setValue(42F);
|
||||||
System.out.println(f.getValue());
|
// System.out.println(f.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue