From 24a6bd0812cfba3d614af49ee447a28ed7e6ec0f Mon Sep 17 00:00:00 2001 From: Shautvast Date: Sat, 28 Oct 2023 17:24:00 +0200 Subject: [PATCH] refactoring --- src/class.rs | 2 +- src/main.rs | 2 +- src/vm/array.rs | 143 ++++++++++++++++++++ src/vm/mod.rs | 5 + src/vm/operations.rs | 90 +++++++++++++ src/vm/stack.rs | 29 ++++ src/{ => vm}/vm.rs | 310 +++++-------------------------------------- tests/class_tests.rs | 2 +- 8 files changed, 304 insertions(+), 279 deletions(-) create mode 100644 src/vm/array.rs create mode 100644 src/vm/mod.rs create mode 100644 src/vm/operations.rs create mode 100644 src/vm/stack.rs rename src/{ => vm}/vm.rs (74%) diff --git a/src/class.rs b/src/class.rs index 6b2b05b..9135d8e 100644 --- a/src/class.rs +++ b/src/class.rs @@ -21,7 +21,7 @@ pub static mut CLASSES: Lazy> = Lazy::new(|| HashMap::new // gets the Class from cache, or reads it from classpath, // then parses the binary data into a Class struct // Vm keeps ownership of the class and hands out Arc references to it -pub fn get_class( +pub(crate) fn get_class( vm: &mut Vm, class_name: &str, ) -> Result>, Error> { diff --git a/src/main.rs b/src/main.rs index 7cfdd92..8342cc6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,5 @@ -use java_rs::vm::Vm; use std::io::Error; +use java_rs::vm::Vm; fn main() -> Result<(), Error> { // TODO cmdline args diff --git a/src/vm/array.rs b/src/vm/array.rs new file mode 100644 index 0000000..b156748 --- /dev/null +++ b/src/vm/array.rs @@ -0,0 +1,143 @@ +use anyhow::{anyhow, Error}; +use crate::class::Value::{self,*}; +use crate::heap::ObjectRef; + +pub(crate) unsafe fn array_load(index: Value, arrayref: Value) -> Result { + if let I32(index) = &index { + let index = *index as usize; + + if let Null = arrayref { + return Err(anyhow!("NullpointerException")); + } + if let Ref(objectref) = arrayref { + match &*objectref.get() { + ObjectRef::ByteArray(array) => { + return Ok(I32(array[index] as i32)); + } + ObjectRef::ShortArray(array) => { + return Ok(I32(array[index] as i32)); + } + ObjectRef::IntArray(array) => { + return Ok(I32(array[index])); + } + ObjectRef::BooleanArray(array) => { + return Ok(I32(array[index] as i32)); + } + ObjectRef::CharArray(array) => { + return Ok(CHAR(array[index])); + } + ObjectRef::LongArray(array) => { + return Ok(I64(array[index])); + } + ObjectRef::FloatArray(array) => { + return Ok(F32(array[index])); + } + ObjectRef::DoubleArray(array) => { + return Ok(F64(array[index])); + } + ObjectRef::ObjectArray(_arraytype, data) => { + return Ok(Ref(data[index].clone())); + } + ObjectRef::StringArray(array) => { + return Ok(Utf8(array[index].to_owned())); + } + ObjectRef::Class(_) => { + panic!("should be array") + } + ObjectRef::Object(_) => { + panic!("should be array") + } //throw error? + } + } + } + panic!() +} + +pub(crate) unsafe fn array_store(value: Value, index: Value, arrayref: &mut Value) -> Result<(), Error> { + if let Null = &*arrayref { + return Err(anyhow!("NullpointerException")); + } + + if let I32(index) = index { + if let Ref(ref mut objectref) = arrayref { + match &mut *objectref.get() { + ObjectRef::ByteArray(ref mut array) => { + if let I32(value) = value { + // is i32 correct? + array[index as usize] = value as i8; + } else { + unreachable!() + } + } + ObjectRef::ShortArray(ref mut array) => { + if let I32(value) = value { + // is i32 correct? + array[index as usize] = value as i16; + } else { + unreachable!() + } + } + ObjectRef::IntArray(ref mut array) => { + if let I32(value) = value { + array[index as usize] = value; + } else { + unreachable!() + } + } + ObjectRef::BooleanArray(ref mut array) => { + if let I32(value) = value { + array[index as usize] = value > 0; + } else { + unreachable!() + } + } + ObjectRef::CharArray(ref mut array) => { + if let I32(value) = value { + array[index as usize] = char::from_u32_unchecked(value as u32); + } else { + unreachable!() + } + } + ObjectRef::LongArray(ref mut array) => { + if let I64(value) = value { + array[index as usize] = value; + } else { + unreachable!() + } + } + ObjectRef::FloatArray(ref mut array) => { + if let F32(value) = value { + array[index as usize] = value + } else { + unreachable!() + } + } + ObjectRef::DoubleArray(ref mut array) => { + if let F64(value) = value { + array[index as usize] = value + } else { + unreachable!() + } + } + ObjectRef::ObjectArray(_arraytype, ref mut array) => { + if let Ref(ref value) = value { + array[index as usize] = value.clone(); + } else { + unreachable!() + } + } + ObjectRef::StringArray(ref mut array) => { + if let Utf8(ref value) = value { + array[index as usize] = value.clone(); + } else { + unreachable!() + } + } + ObjectRef::Object(_) | ObjectRef::Class(_) => {} //throw error? + } + } + } else { + unreachable!() + } + Ok(()) +} \ No newline at end of file diff --git a/src/vm/mod.rs b/src/vm/mod.rs new file mode 100644 index 0000000..1de1426 --- /dev/null +++ b/src/vm/mod.rs @@ -0,0 +1,5 @@ +mod vm; +pub use vm::Vm; +mod operations; +mod stack; +mod array; \ No newline at end of file diff --git a/src/vm/operations.rs b/src/vm/operations.rs new file mode 100644 index 0000000..8084664 --- /dev/null +++ b/src/vm/operations.rs @@ -0,0 +1,90 @@ +use std::cell::RefCell; +use std::collections::HashMap; +use std::rc::Rc; +use std::sync::Arc; +use anyhow::{anyhow, Error}; +use Value::I32; +use crate::class::{Class, get_class, Method, Value}; +use crate::class::Value::{F32, F64, I64, Ref, Utf8}; +use crate::classloader::CpEntry; +use crate::heap::ObjectRef; +use crate::vm::Vm; +use crate::vm::vm::{Invocation, MethodSignature}; + +pub(crate) fn get_static(vm: &mut Vm, this_class: Arc>, field_index: u16) -> Value { + let borrow = this_class.borrow(); + let (class_index, field_name_and_type_index) = + borrow.cp_field_ref(&field_index).unwrap(); // all these unwraps are safe as long as the class is valid + let (name_index, _) = + borrow.cp_name_and_type(field_name_and_type_index).unwrap(); + let name = borrow.cp_utf8(name_index).unwrap(); + + let that_class_name_index = borrow.cp_class_ref(class_index).unwrap(); + let that_class_name = borrow.cp_utf8(that_class_name_index).unwrap(); + let that = get_class(vm, that_class_name.as_str()).unwrap(); + let that_borrow = that.borrow(); + let (_, val_index) = that_borrow + .static_field_mapping + .get(that_class_name) + .unwrap() + .get(name) + .unwrap(); + that_borrow + .static_data + .get(*val_index).unwrap().as_ref().unwrap().clone() +} + +pub(crate) fn get_name_and_type(cp: Rc>, index: u16) -> Option { + if let CpEntry::NameAndType(method_name_index, signature_index) = cp.get(&index).unwrap() { + if let CpEntry::Utf8(method_name) = cp.get(method_name_index).unwrap() { + if let CpEntry::Utf8(signature) = cp.get(signature_index).unwrap() { + let mut method_signature: String = method_name.into(); + let num_args = get_num_args(signature); + method_signature.push_str(signature); + return Some(MethodSignature::new(method_signature, num_args)); + } + } + } + None +} +pub(crate) fn get_signature_for_invoke(cp: &Rc>, index: u16) -> Option { + if let CpEntry::MethodRef(class_index, name_and_type_index) + | CpEntry::InterfaceMethodref(class_index, name_and_type_index) = cp.get(&index).unwrap() + { + if let Some(method_signature) = get_name_and_type(Rc::clone(&cp), *name_and_type_index) { + if let CpEntry::ClassRef(class_name_index) = cp.get(class_index).unwrap() { + if let CpEntry::Utf8(class_name) = cp.get(class_name_index).unwrap() { + return Some(Invocation::new( + class_name.into(), + method_signature) + ); + } + } + } + } + None +} + + +fn get_num_args(signature: &str) -> usize { + let mut num = 0; + let mut i = 1; + let chars: Vec = signature.chars().collect(); + + while i < chars.len() { + if chars[i] == 'L' { + i += 1; + while chars[i] != ';' { + i += 1; + } + i += 1; + num += 1; + } else if chars[i] == ')' { + break; + } else { + i += 1; + num += 1; + } + } + num +} \ No newline at end of file diff --git a/src/vm/stack.rs b/src/vm/stack.rs new file mode 100644 index 0000000..e359340 --- /dev/null +++ b/src/vm/stack.rs @@ -0,0 +1,29 @@ +use anyhow::Error; +use crate::class::Value; + +#[derive(Debug)] +pub(crate) struct StackFrame { + pub(crate) at: String, + pub(crate) data: Vec, +} + +// maybe just call frame +impl StackFrame { + pub(crate) fn new(at_class: &str, at_method: &str) -> Self { + let mut at: String = at_class.into(); + at.push('.'); + at.push_str(at_method); + Self { at, data: vec![] } + } + + pub(crate) fn push(&mut self, val: Value) { + self.data.push(val); + } + + pub(crate) fn len(&self) -> usize { + self.data.len() + } + pub(crate) fn pop(&mut self) -> Result { + Ok(self.data.pop().unwrap()) + } +} \ No newline at end of file diff --git a/src/vm.rs b/src/vm/vm.rs similarity index 74% rename from src/vm.rs rename to src/vm/vm.rs index f3e0231..dceda91 100644 --- a/src/vm.rs +++ b/src/vm/vm.rs @@ -1,48 +1,25 @@ use std::cell::RefCell; use std::collections::HashMap; +use std::io::Write; use std::rc::Rc; use std::sync::Arc; -use std::io::Write; + use anyhow::{anyhow, Error}; use log::{debug, info}; use Value::*; +use crate::class::{AttributeType, Class, CLASSES, get_class, Method, Modifier, unsafe_ref, Value}; use crate::class::Value::{Null, Void}; -use crate::class::{get_class, unsafe_ref, AttributeType, Class, Modifier, Value, Method, CLASSES}; use crate::classloader::CpEntry; use crate::heap::{Heap, Object, ObjectRef}; use crate::io::*; use crate::native::invoke_native; use crate::opcodes; use crate::opcodes::*; - -#[derive(Debug)] -pub(crate) struct StackFrame { - at: String, - data: Vec, -} - -// maybe just call frame -impl StackFrame { - fn new(at_class: &str, at_method: &str) -> Self { - let mut at: String = at_class.into(); - at.push('.'); - at.push_str(at_method); - Self { at, data: vec![] } - } - - fn push(&mut self, val: Value) { - self.data.push(val); - } - - fn len(&self) -> usize { - self.data.len() - } - fn pop(&mut self) -> Result { - Ok(self.data.pop().unwrap()) - } -} +use crate::vm::array::{array_load, array_store}; +use crate::vm::operations::{get_name_and_type, get_signature_for_invoke, get_static}; +use crate::vm::stack::StackFrame; pub struct Vm { pub classpath: Vec, @@ -324,7 +301,9 @@ impl Vm { } CpEntry::StringRef(utf8_index) => { if let CpEntry::Utf8(s) = method.constant_pool.get(utf8_index).unwrap() { - self.current_frame().push(Value::Utf8(s.to_owned())); + self.current_frame().push(Utf8(s.to_owned())); + } else { + } } _ => { @@ -370,7 +349,9 @@ impl Vm { .push(local_params[3].as_ref().unwrap().clone()); } IALOAD | LALOAD | FALOAD | DALOAD | AALOAD | BALOAD | CALOAD | SALOAD => unsafe { - self.array_load()?; + let index = self.current_frame().pop()?; + let arrayref = self.current_frame().pop()?; + self.current_frame().push(array_load(index, arrayref)?); }, ISTORE | LSTORE | FSTORE | DSTORE | ASTORE => { let index = read_u8(&code.opcodes, pc) as usize; @@ -390,7 +371,10 @@ impl Vm { } BASTORE | IASTORE | LASTORE | CASTORE | SASTORE | FASTORE | DASTORE | AASTORE => unsafe { - self.array_store()? + let value = self.current_frame().pop()?; + let index = self.current_frame().pop()?; + let arrayref = &mut self.current_frame().pop()?; + array_store(value, index, arrayref)? }, POP => { self.current_frame().pop()?; @@ -430,33 +414,9 @@ impl Vm { return Ok(Void); } GETSTATIC => { - let borrow = this_class.borrow(); - let cp_index = read_u16(&code.opcodes, pc); - let (class_index, field_name_and_type_index) = - borrow.cp_field_ref(&cp_index).unwrap(); // all these unwraps are safe as long as the class is valid - let (name_index, _) = - borrow.cp_name_and_type(field_name_and_type_index).unwrap(); - let name = borrow.cp_utf8(name_index).unwrap(); - - let that_class_name_index = borrow.cp_class_ref(class_index).unwrap(); - let that_class_name = borrow.cp_utf8(that_class_name_index).unwrap(); - let that = get_class(self, that_class_name.as_str())?; - let that_borrow = that.borrow(); - let (_, val_index) = that_borrow - .static_field_mapping - .get(that_class_name) - .unwrap() - .get(name) - .unwrap(); - self.current_frame().push( - that_borrow - .static_data - .get(*val_index) - .unwrap() - .as_ref() - .unwrap() - .clone(), - ); + let field_index = read_u16(&code.opcodes, pc); + let field_value = get_static(self, this_class.clone(), field_index); + self.current_frame().push(field_value); } PUTSTATIC => { let mut borrow = this_class.borrow_mut(); @@ -733,152 +693,6 @@ impl Vm { } } - unsafe fn array_load(&mut self) -> Result<(), Error> { - let value = self.current_frame().pop()?; - - if let I32(index) = &value { - let index = *index as usize; - let arrayref = self.current_frame().pop()?; - if let Null = arrayref { - return Err(anyhow!("NullpointerException")); - } - if let Ref(objectref) = arrayref { - match &*objectref.get() { - ObjectRef::ByteArray(array) => { - self.current_frame().push(I32(array[index] as i32)); - } - ObjectRef::ShortArray(array) => { - self.current_frame().push(I32(array[index] as i32)); - } - ObjectRef::IntArray(array) => { - self.current_frame().push(I32(array[index])); - } - ObjectRef::BooleanArray(array) => { - self.current_frame().push(I32(array[index] as i32)); - } - ObjectRef::CharArray(array) => { - self.current_frame().push(CHAR(array[index])); - } - ObjectRef::LongArray(array) => { - self.current_frame().push(I64(array[index])); - } - ObjectRef::FloatArray(array) => { - self.current_frame().push(F32(array[index])); - } - ObjectRef::DoubleArray(array) => { - self.current_frame().push(F64(array[index])); - } - ObjectRef::ObjectArray(_arraytype, data) => { - self.current_frame().push(Ref(data[index].clone())); - } - ObjectRef::StringArray(array) => { - self.current_frame().push(Utf8(array[index].to_owned())); - } - ObjectRef::Class(_) => { - panic!("should be array") - } - ObjectRef::Object(_) => { - panic!("should be array") - } //throw error? - } - } - } - Ok(()) - } - - unsafe fn array_store(&mut self) -> Result<(), Error> { - let value = self.current_frame().pop()?; - let index = self.current_frame().pop()?; - let arrayref = &mut self.current_frame().pop()?; - - if let Value::Null = &*arrayref { - return Err(anyhow!("NullpointerException")); - } - - if let I32(index) = index { - if let Ref(ref mut objectref) = arrayref { - match &mut *objectref.get() { - ObjectRef::ByteArray(ref mut array) => { - if let I32(value) = value { - // is i32 correct? - array[index as usize] = value as i8; - } else { - unreachable!() - } - } - ObjectRef::ShortArray(ref mut array) => { - if let I32(value) = value { - // is i32 correct? - array[index as usize] = value as i16; - } else { - unreachable!() - } - } - ObjectRef::IntArray(ref mut array) => { - if let I32(value) = value{ - array[index as usize] = value; - } else { - unreachable!() - } - } - ObjectRef::BooleanArray(ref mut array) => { - if let I32(value) = value { - array[index as usize] = value > 0; - } else { - unreachable!() - } - } - ObjectRef::CharArray(ref mut array) => { - if let I32(value) = value { - array[index as usize] = char::from_u32_unchecked(value as u32); - } else { - unreachable!() - } - } - ObjectRef::LongArray(ref mut array) => { - if let I64(value) = value { - array[index as usize] = value; - } else { - unreachable!() - } - } - ObjectRef::FloatArray(ref mut array) => { - if let F32(value) = value { - array[index as usize] = value - } else { - unreachable!() - } - } - ObjectRef::DoubleArray(ref mut array) => { - if let F64(value) = value { - array[index as usize] = value - } else { - unreachable!() - } - } - ObjectRef::ObjectArray(_arraytype, ref mut array) => { - if let Ref(ref value) = value { - array[index as usize] = value.clone(); - } else { - unreachable!() - } - } - ObjectRef::StringArray(ref mut array) => { - if let Utf8(ref value) = value { - array[index as usize] = value.clone(); - } else { - unreachable!() - } - } - ObjectRef::Object(_) | ObjectRef::Class(_) => {} //throw error? - } - } - } else { - unreachable!() - } - Ok(()) - } - fn store( &mut self, local_params: &mut Vec>, @@ -893,86 +707,30 @@ impl Vm { } } -struct Invocation { +pub(crate) struct Invocation { class_name: String, method: MethodSignature, } -struct MethodSignature { +impl Invocation { + pub fn new(class_name: String, method: MethodSignature) -> Self{ + Self{ + class_name, method + } + } +} + +pub(crate) struct MethodSignature { name: String, num_args: usize, } -// TODO can be simplified now, using cp_ methods in Class -fn get_signature_for_invoke(cp: &Rc>, index: u16) -> Option { - if let CpEntry::MethodRef(class_index, name_and_type_index) - | CpEntry::InterfaceMethodref(class_index, name_and_type_index) = cp.get(&index).unwrap() - { - if let Some(method_signature) = get_name_and_type(Rc::clone(&cp), *name_and_type_index) { - if let CpEntry::ClassRef(class_name_index) = cp.get(class_index).unwrap() { - if let CpEntry::Utf8(class_name) = cp.get(class_name_index).unwrap() { - return Some(Invocation { - class_name: class_name.into(), - method: method_signature, - }); - } - } +impl MethodSignature { + pub(crate) fn new(name: String, num_args: usize) -> Self { + MethodSignature { + name, + num_args, } } - None } -// unsafe fn copy(value: Value) -> Value { -// match value { -// Void => Void, -// Null => Null, -// BOOL(b) => BOOL(b), -// CHAR(c) => CHAR(c), -// I32(i) => I32(i), -// I64(l) => I64(l), -// F32(f) => F32(f), -// F64(d) => F64(d), -// Ref(r) => Ref(r.clone()), -// Utf8(s) => Utf8(s.to_owned()), -// } -// } - -fn get_name_and_type(cp: Rc>, index: u16) -> Option { - if let CpEntry::NameAndType(method_name_index, signature_index) = cp.get(&index).unwrap() { - if let CpEntry::Utf8(method_name) = cp.get(method_name_index).unwrap() { - if let CpEntry::Utf8(signature) = cp.get(signature_index).unwrap() { - let mut method_signature: String = method_name.into(); - let num_args = get_hum_args(signature); - method_signature.push_str(signature); - return Some(MethodSignature { - name: method_signature, - num_args, - }); - } - } - } - None -} - -fn get_hum_args(signature: &str) -> usize { - let mut num = 0; - let mut i = 1; - let chars: Vec = signature.chars().collect(); - - while i < chars.len() { - if chars[i] == 'L' { - i += 1; - while chars[i] != ';' { - i += 1; - } - i += 1; - num += 1; - } else if chars[i] == ')' { - break; - } else { - i += 1; - num += 1; - } - } - num -} diff --git a/tests/class_tests.rs b/tests/class_tests.rs index f057528..dd442a3 100644 --- a/tests/class_tests.rs +++ b/tests/class_tests.rs @@ -1,7 +1,7 @@ mod test { use java_rs::class::Value; use java_rs::heap::ObjectRef; - use java_rs::vm::Vm; + use java_rs::vm1::Vm; #[test] fn if_cmp() {