some new opcodes

This commit is contained in:
Shautvast 2023-11-11 12:22:52 +01:00
parent 66983b0783
commit 40a7c6cd1e
8 changed files with 354 additions and 286 deletions

View file

@ -21,3 +21,4 @@ actually:
**Ultimate goal**
* Hello world domination
``

View file

@ -7,6 +7,7 @@ use rand::random;
use crate::class::ObjectRef::*;
/// ClassId facilitates loose coupling between classes, classdefs and objects
pub type ClassId = usize;
#[derive(Debug, Clone)]
@ -24,20 +25,17 @@ 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
// 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) static_field_mapping: HashMap<String, HashMap<String, TypeIndex>>,
// pub(crate) static_field_data: Vec<Value>,
// pub(crate) static_field_data: Vec<Value> // moved to classmanager
}
impl Class {

View file

@ -1,6 +1,7 @@
use std::cell::RefCell;
use std::collections::{HashMap, LinkedList};
use std::rc::Rc;
use log::debug;
use once_cell::sync::Lazy;
@ -12,6 +13,7 @@ use crate::classloader::io::PATH_SEPARATOR;
use crate::vm::Vm;
static mut CLASSMANAGER: Lazy<ClassManager> = Lazy::new(|| ClassManager::new());
static PRIMITIVES: Lazy<Vec<&str>> = Lazy::new(|| vec!["B", "S", "I", "J", "F", "D", "Z", "J", "C"]);
pub fn init() {
unsafe {
@ -151,23 +153,64 @@ impl ClassManager {
self.classdefs.get(&id).unwrap()
}
/// loads the class if not already there
fn load_class_by_name(&mut self, name: &str) {
let id = self.names.get(name);
debug!("load class {}", name);
// determine no of dimensions and get type of array if any
let mut chars = name.chars();
let mut num_dims = 0;
while let Some(c) = chars.nth(num_dims) {
if c == '[' {
num_dims += 1;
} else {
break;
}
}
let mut type_name = name[num_dims..name.len()].to_owned();
if num_dims > 0 {
if !PRIMITIVES.contains(&type_name.as_str()){
type_name = type_name[1..type_name.len()].to_owned();
}
let id = self.get_or_new_id(name);
if !self.class_objects.contains_key(&id) {
let cls = self.get_class_by_name("java/lang/Class").unwrap();
let mut instance = Object::new(cls);
instance.set(cls, "java/lang/Class", "name", Value::Utf8(name.into()));
let instance = Ref(ObjectRef::Object(Rc::new(RefCell::new(instance))));
self.class_objects.insert(id, instance);
}
} else {
// in cache?
let id = self.names.get(&type_name);
match id {
Some(id) => if self.classes.get(id).is_none() {
self.add_class(name);
self.add_class(&type_name);
}
None => {
self.add_class(name);
self.add_class(&type_name);
}
}
}
}
/// get optional classid from cache
fn get_class_by_name(&self, name: &str) -> Option<&Class> {
let id = self.names.get(name);
self.classes.get(id.unwrap())
}
/// adds the class and calculates the 'offset' of it's fields (static and non-static)
/// this is a map (declared-class -> map (field-name -> type_index))
/// -> fields are not polymorphic, meaning a field can exist in the class and in the superclass and can be addressed individually (no hiding)
/// -> the bytecode will know what declared type field is needed
///
/// type_index is tuple (field-type, index)
/// field-type is a string
/// index is an index into the list of values that object instances will use to store the values
///
/// the function also instantiates a (java.lang.) Class object for each loaded class
fn add_class(&mut self, name: &str) -> ClassId {
debug!("add class {}", name);
let this_classid = self.load(name);
@ -202,11 +245,11 @@ impl ClassManager {
.map(|n| *self.names.get(n.as_str()).unwrap())
.collect();
// initial values for static fields (before static init)
self.static_class_data.insert(this_classid, Self::set_field_data(&static_field_mapping));
self.classes.insert(this_classid, Class {
id: this_classid,
initialized: false,
name: name.into(),
superclass: superclass_id,
parents,
@ -215,6 +258,7 @@ impl ClassManager {
static_field_mapping,
});
// add a new Class instance
if name != "java/lang/Class" {
let cls = self.get_class_by_name("java/lang/Class").unwrap();
let mut instance = Object::new(cls);
@ -223,15 +267,16 @@ impl ClassManager {
self.class_objects.insert(this_classid, instance);
}
let clinit = this_classdef.methods.contains_key("<clinit>()V");
if clinit {
// run static init
if this_classdef.methods.contains_key("<clinit>()V") {
self.vm.execute_special(&mut vec![], name, "<clinit>()V", vec![]).unwrap();
}
this_classid
}
/// like described above
fn add_fields_for_this_or_parents(object_field_mapping: &mut HashMap<String, HashMap<String, TypeIndex>>,
static_field_mapping: &mut HashMap<String, HashMap<String, TypeIndex>>,
object_field_map_index: &mut usize,
@ -269,10 +314,7 @@ impl ClassManager {
/// 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 id = self.get_or_new_id(name);
let classdef = self.classdefs
.entry(id)
@ -280,6 +322,14 @@ impl ClassManager {
(id, inspect_dependencies(classdef))
}
fn get_or_new_id(&mut self, name: &str) -> ClassId {
let id = *self.names.entry(name.to_string()).or_insert_with(|| {
self.current_id += 1;
self.current_id
});
id
}
pub(crate) fn set_field_data(field_mapping: &HashMap<String, HashMap<String, TypeIndex>>) -> Vec<Value> {
let mut field_data = vec![Null; n_fields(field_mapping)];
@ -326,7 +376,9 @@ pub(crate) fn inspect_dependencies(classdef: &ClassDef) -> Vec<String> {
#[cfg(test)]
mod test {
use std::rc::Rc;
use crate::classloader::classdef::{CpEntry, Field};
use super::*;
#[test]
@ -372,7 +424,7 @@ mod test {
let mut fields_declared_by_java_lang_class = HashMap::new();
fields_declared_by_java_lang_class.insert("name".to_owned(), TypeIndex { type_name: "java/lang/String".into(), index: 0 });
class_field_mapping.insert("java/lang/Class".to_owned(), fields_declared_by_java_lang_class);
classes.insert(3, Class { id: 3, initialized: true, name: "".into(), superclass: None, parents: LinkedList::new(), interfaces: vec![], object_field_mapping: class_field_mapping, static_field_mapping: HashMap::new() });
classes.insert(3, Class { id: 3, name: "".into(), superclass: None, parents: LinkedList::new(), interfaces: vec![], object_field_mapping: class_field_mapping, static_field_mapping: HashMap::new() });
let mut cm = ClassManager {
static_class_data: HashMap::new(),

View file

@ -1,6 +1,6 @@
use anyhow::{anyhow, Error};
use crate::class::ObjectRef;
use crate::class::ObjectRef::*;
use crate::class::Value::{self, *};
@ -13,40 +13,40 @@ pub(crate) fn array_load(index: Value, arrayref: Value) -> Result<Value, Error>
}
if let Ref(objectref) = arrayref {
match objectref {
ObjectRef::ByteArray(array) => {
ByteArray(array) => {
return Ok(I32(array[index] as i32));
}
ObjectRef::ShortArray(array) => {
ShortArray(array) => {
return Ok(I32(array[index] as i32));
}
ObjectRef::IntArray(array) => {
IntArray(array) => {
return Ok(I32(array[index]));
}
ObjectRef::BooleanArray(array) => {
BooleanArray(array) => {
return Ok(I32(array[index] as i32));
}
ObjectRef::CharArray(array) => {
CharArray(array) => {
return Ok(CHAR(array[index]));
}
ObjectRef::LongArray(array) => {
LongArray(array) => {
return Ok(I64(array[index]));
}
ObjectRef::FloatArray(array) => {
FloatArray(array) => {
return Ok(F32(array[index]));
}
ObjectRef::DoubleArray(array) => {
DoubleArray(array) => {
return Ok(F64(array[index]));
}
ObjectRef::ObjectArray(_arraytype, data) => {
ObjectArray(_arraytype, data) => {
return Ok(Ref(data[index].clone()));
}
ObjectRef::StringArray(array) => {
StringArray(array) => {
return Ok(Utf8(array[index].to_owned()));
}
ObjectRef::Class(_) => {
Class(_) => {
panic!("should be array")
}
ObjectRef::Object(_) => {
Object(_) => {
panic!("should be array")
} //throw error?
}
@ -63,7 +63,7 @@ pub(crate) fn array_store(value: Value, index: Value, arrayref: Value) -> Result
if let I32(index) = index {
if let Ref(mut objectref) = arrayref {
match objectref {
ObjectRef::ByteArray(ref mut array) => {
ByteArray(ref mut array) => {
if let I32(value) = value {
// is i32 correct?
array[index as usize] = value as i8;
@ -71,7 +71,7 @@ pub(crate) fn array_store(value: Value, index: Value, arrayref: Value) -> Result
unreachable!()
}
}
ObjectRef::ShortArray(ref mut array) => {
ShortArray(ref mut array) => {
if let I32(value) = value {
// is i32 correct?
array[index as usize] = value as i16;
@ -79,63 +79,63 @@ pub(crate) fn array_store(value: Value, index: Value, arrayref: Value) -> Result
unreachable!()
}
}
ObjectRef::IntArray(ref mut array) => {
IntArray(ref mut array) => {
if let I32(value) = value {
array[index as usize] = value;
} else {
unreachable!()
}
}
ObjectRef::BooleanArray(ref mut array) => {
BooleanArray(ref mut array) => {
if let I32(value) = value {
array[index as usize] = value > 0;
} else {
unreachable!()
}
}
ObjectRef::CharArray(ref mut array) => unsafe {
CharArray(ref mut array) => unsafe {
if let I32(value) = value {
array[index as usize] = char::from_u32_unchecked(value as u32);
} else {
unreachable!()
}
}
ObjectRef::LongArray(ref mut array) => {
LongArray(ref mut array) => {
if let I64(value) = value {
array[index as usize] = value;
} else {
unreachable!()
}
}
ObjectRef::FloatArray(ref mut array) => {
FloatArray(ref mut array) => {
if let F32(value) = value {
array[index as usize] = value
} else {
unreachable!()
}
}
ObjectRef::DoubleArray(ref mut array) => {
DoubleArray(ref mut array) => {
if let F64(value) = value {
array[index as usize] = value
} else {
unreachable!()
}
}
ObjectRef::ObjectArray(_arraytype, ref mut array) => {
ObjectArray(_arraytype, ref mut array) => {
if let Ref(ref value) = value {
array[index as usize] = value.clone();
} else {
unreachable!()
}
}
ObjectRef::StringArray(ref mut array) => {
StringArray(ref mut array) => {
if let Utf8(ref value) = value {
array[index as usize] = value.clone();
} else {
unreachable!()
}
}
ObjectRef::Object(_) | ObjectRef::Class(_) => {} //throw error?
Object(_) | Class(_) => {} //throw error?
}
}
} else {

View file

@ -2,13 +2,13 @@
use std::cell::RefCell;
use std::rc::Rc;
use anyhow::Error;
use anyhow::{anyhow, Error};
use log::debug;
use once_cell::sync::Lazy;
use crate::class::{ObjectRef, Value};
use crate::class::ObjectRef::Object;
use crate::class::Value::Void;
use crate::class::Value::{I32, Void};
use crate::classmanager;
use crate::vm::stack::StackFrame;
use crate::vm::Vm;
@ -17,19 +17,35 @@ pub fn invoke_native(vm: &mut Vm, stackframes: &mut Vec<StackFrame>, class_name:
debug!("native {}.{}", class_name, method_name);
match class_name.as_str() {
"java/lang/Class" => java_lang_class(vm, method_name),
"java/lang/Class" => java_lang_Class(vm, method_name),
"java/lang/System" => java_lang_System(vm, method_name),
"jdk/internal/misc/Unsafe" => jdk_internal_misc_Unsafe(vm, method_name),
"jdk/internal/util/SystemProps$Raw" => jdk_internal_util_SystemProps_Raw(vm, stackframes, method_name),
_ => Ok(Void)
_ => unimplemented!("")
}
}
fn java_lang_class(_vm: &Vm, method_name: &str) -> Result<Value, Error> {
fn java_lang_Class(_vm: &Vm, method_name: &str) -> Result<Value, Error> {
Ok(match method_name {
"desiredAssertionStatus0(Ljava/lang/Class;)Z" => Value::BOOL(false),
_ => Void
})
}
fn java_lang_System(_vm: &Vm, method_name: &str) -> Result<Value, Error> {
Ok(match method_name {
_ => Void
})
}
fn jdk_internal_misc_Unsafe(_vm: &Vm, method_name: &str) -> Result<Value, Error> {
Ok(match method_name {
"arrayBaseOffset0(Ljava/lang/Class;)I" => I32(0), //TODO surely this is not right
"arrayIndexScale0(Ljava/lang/Class;)I" => I32(0), //TODO surely this is not right
_ => Void
})
}
fn jdk_internal_util_SystemProps_Raw(vm: &mut Vm, stackframes: &mut Vec<StackFrame>, method_name: &str) -> Result<Value, Error> {
match method_name {
"platformProperties()[Ljava/lang/String;" => platformProperties(),

View file

@ -195,11 +195,13 @@ pub const _FADD: u8 = 98;
// (0x62) Add float
pub const _DADD: u8 = 99; // (0x63) add double
pub const ISUB: u8 = 100;
pub const _DSUB: u8 = 103;
// (0x67) subtract double
pub const _FMUL: u8 = 106;
// (0x6a) Multiply float
pub const _DMUL: u8 = 107; // (0x6b) Multiply double
pub const _DMUL: u8 = 107;
// (0x6b) Multiply double
pub const IDIV: u8 = 108;
pub const _FDIV: u8 = 110;
// (0x6e) Divide float
@ -212,7 +214,7 @@ pub const _DREM: u8 = 115;
pub const _FNEG: u8 = 118;
// (0x76) Negate float
pub const _DNEG: u8 = 119; // (0x77) Negate double
pub const ISHL:u8 = 120;
pub const ISHR: u8 = 122;
pub const _F2I: u8 = 139;
// (0x8b) Convert float to int
@ -399,6 +401,7 @@ pub const OPCODES:Lazy<Vec<&str>> = Lazy::new(|| {
opcodes[IADD as usize] = "IADD";
opcodes[_FADD as usize] = "_FADD";
opcodes[_DADD as usize] = "_DADD";
opcodes[ISUB as usize] = "ISUB";
opcodes[_DSUB as usize] = "_DSUB";
opcodes[_FMUL as usize] = "_FMUL";
opcodes[_DMUL as usize] = "_DMUL";
@ -409,6 +412,7 @@ pub const OPCODES:Lazy<Vec<&str>> = Lazy::new(|| {
opcodes[_DREM as usize] = "_DREM";
opcodes[_FNEG as usize] = "_FNEG";
opcodes[_DNEG as usize] = "_DNEG";
opcodes[ISHL as usize] = "ISHL";
opcodes[ISHR as usize] = "ISHR";
opcodes[_F2I as usize] = "_F2I";
opcodes[_F2L as usize] = "_F2L";

View file

@ -1,12 +1,17 @@
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
use anyhow::Error;
use log::debug;
use crate::class::{Class, Value};
use crate::classloader::classdef::CpEntry;
use crate::class::{Class, ObjectRef, Value};
use crate::class::Value::{I32, Ref};
use crate::classloader::classdef::{CpEntry, Method};
use crate::classmanager;
use crate::vm::vm::{Invocation, MethodSignature};
use crate::vm::stack::StackFrame;
use crate::vm::Vm;
use crate::vm::vm::{current_frame, Invocation, MethodSignature};
/// the place for opcode implementations that are a bit long
@ -66,6 +71,52 @@ pub(crate) fn get_signature_for_invoke(cp: &HashMap<u16, CpEntry>, index: u16) -
None
}
/// LDC in all varieties (LDC, LDC_W, LDC2_W)
pub(crate) fn load_constant(cp_index: &u16, method: &Method, stackframes: &mut Vec<StackFrame>, this_class: &Class){
let c = method.constant_pool.get(cp_index).unwrap();
match c {
CpEntry::Integer(i) => {
current_frame(stackframes).push(I32(*i));
}
CpEntry::Float(f) => {
current_frame(stackframes).push(Value::F32(*f));
}
CpEntry::Double(d) => {
current_frame(stackframes).push(Value::F64(*d));
}
CpEntry::StringRef(utf8) => {
//TODO
let string = classmanager::get_classdef(&this_class.id).cp_utf8(utf8);
let string: Vec<u8> = string.as_bytes().into();
classmanager::load_class_by_name("java/lang/String");
let stringclass = classmanager::get_class_by_name("java/lang/String").unwrap();
let mut stringinstance = Vm::new_instance(stringclass);
stringinstance.set(stringclass, "java/lang/String", "value", Value::Ref(ObjectRef::new_byte_array(string)));
debug!("new string \"{}\"", utf8);
current_frame(stackframes).push(Ref(ObjectRef::Object(Rc::new(RefCell::new(stringinstance)))));
}
CpEntry::Long(l) => {
current_frame(stackframes).push(Value::I64(*l));
}
CpEntry::ClassRef(utf8_index) => {
let classdef = classmanager::get_classdef(&this_class.id);
let class_name = classdef.cp_utf8(utf8_index);
classmanager::load_class_by_name(class_name);
let klass_id = classmanager::get_classid(class_name);
if let Some(class) = classmanager::get_classobject(klass_id) {
current_frame(stackframes).push(class.clone());
} else {
unreachable!("should not be here");
}
}
_ => {
panic!("add variant {:?}", c)
}
}
}
fn get_num_args(signature: &str) -> usize {
let mut num = 0;

View file

@ -7,7 +7,7 @@ use log::{debug, error};
use crate::class::{Class, Object, ObjectRef, Value};
use crate::class::Value::{F32, F64, I32, I64, Null, Ref, Utf8, Void};
use crate::classloader::classdef::{AttributeType, CpEntry, Modifier};
use crate::classloader::classdef::{AttributeType, CpEntry, Method, Modifier};
use crate::classloader::io::{read_u16, read_u8};
use crate::classmanager;
use crate::classmanager::get_class_by_id;
@ -15,7 +15,7 @@ use crate::vm::array::{array_load, array_store};
use crate::vm::native::invoke_native;
use crate::vm::opcodes;
use crate::vm::opcodes::*;
use crate::vm::operations::{get_signature_for_invoke, get_static};
use crate::vm::operations::{get_signature_for_invoke, get_static, load_constant};
use crate::vm::stack::StackFrame;
pub struct Vm {}
@ -27,6 +27,8 @@ const PATH_SEPARATOR: char = ':';
#[cfg(target_family = "windows")]
const PATH_SEPARATOR: char = ';';
const MASK_LOWER_5BITS: i32 = 0b00011111;
/// The singlethreaded VM (maybe a future Thread)
//TODO goto
//TODO error handling
@ -220,83 +222,15 @@ impl Vm {
}
LDC => {
let cp_index = read_u8(&code.opcodes, pc) as u16;
let c = method.constant_pool.get(&cp_index).unwrap();
match c {
CpEntry::Integer(i) => {
current_frame(stackframes).push(I32(*i));
}
CpEntry::Float(f) => {
current_frame(stackframes).push(Value::F32(*f));
}
CpEntry::Double(d) => {
current_frame(stackframes).push(Value::F64(*d));
}
CpEntry::StringRef(utf8) => {
//TODO
let string = classmanager::get_classdef(&this_class.id).cp_utf8(utf8);
let string: Vec<u8> = string.as_bytes().into();
classmanager::load_class_by_name("java/lang/String");
let stringclass = classmanager::get_class_by_name("java/lang/String").unwrap();
let mut stringinstance = Vm::new_instance(stringclass);
stringinstance.set(stringclass, "java/lang/String", "value", Value::Ref(ObjectRef::new_byte_array(string)));
debug!("new string \"{}\"", utf8);
current_frame(stackframes).push(Ref(ObjectRef::Object(Rc::new(RefCell::new(stringinstance)))));
}
CpEntry::Long(l) => {
current_frame(stackframes).push(Value::I64(*l));
}
CpEntry::ClassRef(utf8) => {
let classdef = classmanager::get_classdef(&this_class.id);
let class_name = classdef.cp_utf8(utf8);
classmanager::load_class_by_name(class_name);
let klass_id = classmanager::get_classid(class_name);
if let Some(class) = classmanager::get_classobject(klass_id) {
current_frame(stackframes).push(class.clone());
} else {
unreachable!("should not be here");
}
}
_ => {
panic!("add variant {:?}", c)
}
}
load_constant(&cp_index, method, stackframes, this_class);
}
LDC_W => {
let cp_index = read_u16(&code.opcodes, pc);
let cp_entry = method.constant_pool.get(&cp_index).unwrap();
match cp_entry {
CpEntry::Integer(i) => {
current_frame(stackframes).push(I32(*i));
}
CpEntry::Float(f) => {
current_frame(stackframes).push(F32(*f));
}
CpEntry::StringRef(utf8_index) => {
if let CpEntry::Utf8(s) = method.constant_pool.get(utf8_index).unwrap() {
current_frame(stackframes).push(Utf8(s.to_owned()));
} else {}
}
_ => {
error!("{:?}", cp_entry);
unreachable!()
}
}
load_constant(&cp_index, method, stackframes, this_class);
}
LDC2_W => {
let cp_index = read_u16(&code.opcodes, pc);
match method.constant_pool.get(&cp_index).unwrap() {
CpEntry::Double(d) => {
current_frame(stackframes).push(Value::F64(*d));
}
CpEntry::Long(l) => {
current_frame(stackframes).push(Value::I64(*l));
}
_ => {
unreachable!()
}
}
load_constant(&cp_index, method, stackframes, this_class);
}
ILOAD | LLOAD | FLOAD | DLOAD | ALOAD => {
// omitting the type checks so far
@ -362,16 +296,28 @@ impl Vm {
debug!("{:?}+{:?}", value1, value2);
current_frame(stackframes).push(I32(value1.into_i32() + value2.into_i32()));
}
ISUB => {
let value2 = current_frame(stackframes).pop()?;
let value1 = current_frame(stackframes).pop()?;
debug!("{:?}-{:?}", value1, value2);
current_frame(stackframes).push(I32(value1.into_i32() - value2.into_i32()));
}
IDIV => {
let value2 = current_frame(stackframes).pop()?;
let value1 = current_frame(stackframes).pop()?;
current_frame(stackframes).push(I32(value1.into_i32() / value2.into_i32()));
}
ISHL => {
let value2 = current_frame(stackframes).pop()?;
let value1 = current_frame(stackframes).pop()?;
debug!("{:?} shl {:?}", value1, value2);
current_frame(stackframes).push(I32(value1.into_i32() << (value2.into_i32() & MASK_LOWER_5BITS)));
}
ISHR => {
let value2 = current_frame(stackframes).pop()?;
let value1 = current_frame(stackframes).pop()?;
debug!("{:?} shr {:?}", value1, value2);
current_frame(stackframes).push(I32(value1.into_i32() >> (value2.into_i32() & 0b00011111)));
current_frame(stackframes).push(I32(value1.into_i32() >> (value2.into_i32() & MASK_LOWER_5BITS)));
}
IFEQ | IFNE | IFLT | IFGE | IFGT | IFLE => {
let jmp_to = read_u16(&code.opcodes, pc) - 3; // -3 so that offset = location of Cmp opcode
@ -715,7 +661,7 @@ impl MethodSignature {
}
}
fn current_frame(stackframes: &mut Vec<StackFrame>) -> &mut StackFrame {
pub(crate) fn current_frame(stackframes: &mut Vec<StackFrame>) -> &mut StackFrame {
let i = stackframes.len() - 1;
stackframes.get_mut(i).unwrap()
}