finally picked it up again, one bytecode at a time

This commit is contained in:
Shautvast 2024-05-16 17:42:04 +02:00
parent c271881a79
commit ccf7426086
9 changed files with 109 additions and 17 deletions

View file

@ -20,14 +20,21 @@ pub struct Class {
pub initialized: bool, pub initialized: bool,
pub name: String, pub name: String,
pub superclass: Option<ClassId>, pub superclass: Option<ClassId>,
pub parents: LinkedList<ClassId>, pub all_superclasses: LinkedList<ClassId>, // all superclasses in a flat list
pub interfaces: Vec<ClassId>, pub interfaces: Vec<ClassId>,
pub all_interfaces: Vec<ClassId>, // all interfaces and their parents in a flat list
// lookup index and type from the name of the declared class and then field // lookup index and type from the name of the declared class and then field
pub(crate) object_field_mapping: HashMap<String, HashMap<String, TypeIndex>>, pub(crate) object_field_mapping: HashMap<String, HashMap<String, TypeIndex>>,
pub(crate) static_field_mapping: HashMap<String, HashMap<String, TypeIndex>>, pub(crate) static_field_mapping: HashMap<String, HashMap<String, TypeIndex>>,
// pub(crate) static_field_data: Vec<Value> // moved to classmanager // pub(crate) static_field_data: Vec<Value> // moved to classmanager
} }
impl PartialEq for Class {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl Class { impl Class {
/// gets the number of non-static fields on the class /// gets the number of non-static fields on the class
pub(crate) fn n_object_fields(&self) -> usize { pub(crate) fn n_object_fields(&self) -> usize {

View file

@ -94,6 +94,7 @@ impl ClassDef {
} }
} }
// gets the class name if the index is a class ref index
pub fn cp_class_name(&self, index: &u16) -> &String { pub fn cp_class_name(&self, index: &u16) -> &String {
let cr = self.cp_class_ref(index); let cr = self.cp_class_ref(index);
self.cp_utf8(cr) self.cp_utf8(cr)
@ -338,7 +339,7 @@ impl Debug for Method {
} }
impl Method { impl Method {
pub(crate) fn new( pub fn new(
constant_pool: Rc<HashMap<u16, CpEntry>>, constant_pool: Rc<HashMap<u16, CpEntry>>,
access_flags: u16, access_flags: u16,
name_index: u16, name_index: u16,

View file

@ -10,14 +10,14 @@ use log::debug;
use crate::classloader::classdef::{ use crate::classloader::classdef::{
AttributeType, ClassDef, CpEntry, Exception, Field, Method, MethodCode, AttributeType, ClassDef, CpEntry, Exception, Field, Method, MethodCode,
}; };
use crate::classloader::code_parser::parse_code;
use crate::classloader::io::{ use crate::classloader::io::{
find_class, read_bytes, read_f32, read_f64, read_i32, read_i64, read_u16, read_u32, read_u8, find_class, read_bytes, read_f32, read_f64, read_i32, read_i64, read_u16, read_u32, read_u8,
}; };
use crate::classloader::opcode_parser::parse_opcodes;
pub mod classdef; pub mod classdef;
mod code_parser;
pub(crate) mod io; pub(crate) mod io;
mod opcode_parser;
pub(crate) fn get_classdef(classpath: &Vec<String>, class_name: &str) -> Result<ClassDef, Error> { pub(crate) fn get_classdef(classpath: &Vec<String>, class_name: &str) -> Result<ClassDef, Error> {
debug!("read class {} ", class_name); debug!("read class {} ", class_name);
@ -250,7 +250,7 @@ fn read_method(
} }
let code = if let Some(AttributeType::Code(code)) = attributes.get("Code") { let code = if let Some(AttributeType::Code(code)) = attributes.get("Code") {
parse_code(&code.opcodes) parse_opcodes(&code.opcodes)
} else { } else {
vec![] vec![]
}; };

View file

@ -6,7 +6,7 @@ use crate::classloader::io::{
}; };
use crate::vm::opcodes::Opcode::{self, *}; use crate::vm::opcodes::Opcode::{self, *};
pub(crate) fn parse_code(opcodes: &[u8]) -> Vec<Opcode> { pub(crate) fn parse_opcodes(opcodes: &[u8]) -> Vec<Opcode> {
let mut code: BTreeMap<u16, (u16, Opcode)> = BTreeMap::new(); let mut code: BTreeMap<u16, (u16, Opcode)> = BTreeMap::new();
let mut c = 0; let mut c = 0;
let mut opcode_index: u16 = 0; let mut opcode_index: u16 = 0;
@ -39,7 +39,8 @@ pub(crate) fn parse_code(opcodes: &[u8]) -> Vec<Opcode> {
IFLT(goto) => IFLT(code2.get(&goto).unwrap().0), IFLT(goto) => IFLT(code2.get(&goto).unwrap().0),
IFLE(goto) => IFLE(code2.get(&goto).unwrap().0), IFLE(goto) => IFLE(code2.get(&goto).unwrap().0),
GOTO(goto) => GOTO(code2.get(&goto).unwrap().0), GOTO(goto) => GOTO(code2.get(&goto).unwrap().0),
IF_ACMPEQ(goto) => GOTO(code2.get(&goto).unwrap().0),
IF_ACMPNE(goto) => GOTO(code2.get(&goto).unwrap().0),
//TODO more jump instructions //TODO more jump instructions
_ => opcode, _ => opcode,
}) })

View file

@ -138,7 +138,7 @@ impl ClassManager {
} }
pub fn get_class_by_name(&self, name: &str) -> Option<&Class> { pub fn get_class_by_name(&self, name: &str) -> Option<&Class> {
debug!("{}", name); debug!("get class by name {}", name);
let id = self.names.get(name); let id = self.names.get(name);
self.classes.get(id.unwrap()) self.classes.get(id.unwrap())
} }
@ -199,6 +199,9 @@ impl ClassManager {
.map(|n| *self.names.get(n.as_str()).unwrap()) .map(|n| *self.names.get(n.as_str()).unwrap())
.collect(); .collect();
let mut all_interfaces = Vec::new();
self.get_all_interfaces(&interface_ids, &mut all_interfaces);
// initial values for static fields (before static init) // initial values for static fields (before static init)
self.static_class_data self.static_class_data
.insert(this_classid, Self::set_field_data(&static_field_mapping)); .insert(this_classid, Self::set_field_data(&static_field_mapping));
@ -210,8 +213,9 @@ impl ClassManager {
initialized: false, initialized: false,
name: name.into(), name: name.into(),
superclass: superclass_id, superclass: superclass_id,
parents, all_superclasses: parents,
interfaces: interface_ids, interfaces: interface_ids,
all_interfaces,
object_field_mapping, object_field_mapping,
static_field_mapping, static_field_mapping,
}, },
@ -235,6 +239,16 @@ impl ClassManager {
this_classid this_classid
} }
fn get_all_interfaces(&self, ids: &[ClassId], output: &mut Vec<ClassId>) {
for id in ids {
output.push(*id);
let interfaces = self.classes.get(id).map(|c| c.interfaces.to_vec());
if let Some(intf) = interfaces {
self.get_all_interfaces(&intf, output);
}
}
}
/// like described above /// like described above
fn add_fields_for_this_or_parents( fn add_fields_for_this_or_parents(
object_field_mapping: &mut HashMap<String, HashMap<String, TypeIndex>>, object_field_mapping: &mut HashMap<String, HashMap<String, TypeIndex>>,
@ -460,8 +474,9 @@ mod test {
initialized: false, initialized: false,
name: "".into(), name: "".into(),
superclass: None, superclass: None,
parents: LinkedList::new(), all_superclasses: LinkedList::new(),
interfaces: vec![], interfaces: vec![],
all_interfaces: vec![],
object_field_mapping: class_field_mapping, object_field_mapping: class_field_mapping,
static_field_mapping: HashMap::new(), static_field_mapping: HashMap::new(),
}, },

View file

@ -77,10 +77,12 @@ impl Value {
} }
pub fn into_object(self) -> ObjectRef { pub fn into_object(self) -> ObjectRef {
if let Value::Ref(v) = self { if let Value::Null = self {
ObjectRef::Null
} else if let Value::Ref(v) = self {
v v
} else { } else {
panic!(); panic!("{:?}", self);
} }
} }

View file

@ -7,7 +7,7 @@ use std::cell::RefCell;
use std::fmt::{Debug, Formatter, Pointer}; use std::fmt::{Debug, Formatter, Pointer};
use std::rc::Rc; use std::rc::Rc;
#[derive(Clone)] #[derive(Clone, PartialEq)]
pub struct Array<T> pub struct Array<T>
where where
T: Clone, T: Clone,
@ -58,7 +58,7 @@ where
} }
} }
#[derive(Clone)] #[derive(Clone, PartialEq)]
pub enum ObjectRef { pub enum ObjectRef {
ByteArray(Array<i8>), ByteArray(Array<i8>),
//maybe use arrays? //maybe use arrays?
@ -176,6 +176,12 @@ pub struct Object {
pub data: Vec<Value>, pub data: Vec<Value>,
} }
impl PartialEq for Object {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
// object, not array // object, not array
impl Object { impl Object {
pub fn new(class: &Class) -> Self { pub fn new(class: &Class) -> Self {

View file

@ -15,7 +15,7 @@ use crate::vm::array::{array_load, array_store};
use crate::vm::native::invoke_native; use crate::vm::native::invoke_native;
use crate::vm::object; use crate::vm::object;
use crate::vm::object::ObjectRef; use crate::vm::object::ObjectRef;
use crate::vm::object::ObjectRef::Object; use crate::vm::object::ObjectRef::{Class, Object};
use crate::vm::opcodes::Opcode; use crate::vm::opcodes::Opcode;
use crate::vm::opcodes::Opcode::*; use crate::vm::opcodes::Opcode::*;
use std::io::Write; use std::io::Write;
@ -551,7 +551,7 @@ impl Stackframe {
class_manager.load_class_by_name(&name); class_manager.load_class_by_name(&name);
let class = class_manager.get_class_by_name(&name).unwrap(); let class = class_manager.get_class_by_name(&name).unwrap();
for parent_id in &class.parents { for parent_id in &class.all_superclasses {
let classdef = class_manager.get_classdef(parent_id); let classdef = class_manager.get_classdef(parent_id);
if classdef.has_method(method_name) { if classdef.has_method(method_name) {
invoke_class = Some(*parent_id); invoke_class = Some(*parent_id);
@ -712,6 +712,8 @@ impl Stackframe {
let object = object.borrow(); let object = object.borrow();
let value = object.get(runtime_type, &declared_type, &field_name); let value = object.get(runtime_type, &declared_type, &field_name);
self.push(value.clone()); self.push(value.clone());
} else if let ObjectRef::Null = instance {
self.push(Null);
} else { } else {
unreachable!() unreachable!()
} }
@ -907,8 +909,65 @@ impl Stackframe {
let value1 = self.pop().into_i64(); let value1 = self.pop().into_i64();
self.push(I64(value1 ^ value2)); self.push(I64(value1 ^ value2));
} }
CHECKCAST(_) => { //?
}
IF_ACMPEQ(jmp_to) => {
let value2 = self.pop().into_object();
let value1 = self.pop().into_object();
if value1 == value2 {
self.pc = *jmp_to as usize;
}
}
IF_ACMPNE(jmp_to) => {
let value2 = self.pop().into_object();
let value1 = self.pop().into_object();
if value1 != value2 {
self.pc = *jmp_to as usize;
}
}
INSTANCEOF(class_index_tca) => {
// check object.getClass against class_tca (to check against)
// 1. either class == class_tca
// 2. or class.one_of_its_interfaces == class_tca // recursive up until there is no super
// 3. or class.superclass = class_tca // recursive up until (excluding) object
CHECKCAST(_) => {} let object = self.pop().into_object();
let class_name = class_manager
.get_classdef(&class_id)
.cp_class_name(class_index_tca);
let classid_tca = class_manager.get_class_by_name(class_name).map(|c| c.id);
if let Some(classid_tca) = classid_tca {
if let Object(o) = object {
let class_id = &o.borrow().class_id;
let mut outcome = 0;
if let Some(class_to_check) = class_manager.get_class_by_id(class_id) {
// 1.
if class_to_check.id == classid_tca {
outcome = 1;
} else {
// 2.
for intf_id in &class_to_check.all_interfaces {
if *intf_id == classid_tca {
outcome = 1;
break;
}
}
// 3.
for super_id in &class_to_check.all_superclasses {
if *super_id == classid_tca {
outcome = 1;
break;
}
}
}
debug!("{}", outcome);
self.push(I32(outcome));
}
} else {
self.push(I32(0));
}
}
}
_ => { _ => {
panic!("opcode not implemented") panic!("opcode not implemented")
} }

View file

@ -11,6 +11,7 @@ mod test {
0, 0,
0, 0,
HashMap::new(), HashMap::new(),
vec![],
); );
assert!(m.is(Modifier::Public)); assert!(m.is(Modifier::Public));
assert!(m.is(Modifier::Static)); assert!(m.is(Modifier::Static));