From 8dd60c086652ad9f3ab4d09061142a677ef15d95 Mon Sep 17 00:00:00 2001 From: Sander Hautvast Date: Wed, 27 Sep 2023 13:56:26 +0200 Subject: [PATCH] WIP create instance on heap --- src/{types.rs => class.rs} | 118 ++++++---------------------------- src/heap.rs | 34 ++++++++++ src/lib.rs | 6 +- src/opcodes.rs | 3 +- src/vm.rs | 127 +++++++++++++++++++++++++++++++++++++ tests/Float.class | Bin 282 -> 357 bytes tests/Float.java | 8 ++- tests/class_tests.rs | 23 +++++-- 8 files changed, 212 insertions(+), 107 deletions(-) rename src/{types.rs => class.rs} (60%) create mode 100644 src/heap.rs create mode 100644 src/vm.rs diff --git a/src/types.rs b/src/class.rs similarity index 60% rename from src/types.rs rename to src/class.rs index e734b86..94e2770 100644 --- a/src/types.rs +++ b/src/class.rs @@ -2,8 +2,9 @@ use std::collections::HashMap; use std::fmt; use std::rc::Rc; -use crate::{CpEntry, opcodes}; -use crate::io::{read_u8, read_u16}; +use crate::CpEntry; +use crate::heap::Object; +use crate::io::read_u16; #[derive(Debug)] //TODO create factory function @@ -25,18 +26,20 @@ impl Class { (self.major_version, self.minor_version) } - pub fn execute(&self, method_name: &str) -> Value { - let m = self.methods.get(method_name).unwrap(); - m.execute().unwrap() //TODO remove unwrap - } + // pub fn execute(&self, method_name: &str) -> Value { + // let m = self.methods.get(method_name).unwrap(); + // execute(m).unwrap() //TODO + // } + + } pub struct Method { - constant_pool: Rc>, + pub(crate) constant_pool: Rc>, access_flags: u16, name_index: usize, descriptor_index: usize, - attributes: HashMap, + pub(crate) attributes: HashMap, } impl fmt::Debug for Method { @@ -67,78 +70,6 @@ impl Method { full_name } - - pub fn execute(&self) -> Option { - if let AttributeType::Code(code) = self.attributes.get("Code").unwrap() { - let mut stack = Stack::new(); - let mut pc: usize = 0; - while pc < code.opcodes.len() { - let opcode = &code.opcodes[pc]; - pc += 1; - // println!("{}", opcode); - match opcode { - &opcodes::BIPUSH => { - let c = code.opcodes[pc] as i32; - stack.push(Value::I32(c)); - pc += 1; - } - &opcodes::LDC => { - let cp_index = read_u8(&code.opcodes, pc) as usize; - match self.constant_pool.get(&cp_index).unwrap() { - CpEntry::Integer(i) => { - stack.push(Value::I32(*i)); - } - CpEntry::Float(f) => { - stack.push(Value::F32(*f)); - } - _ => {} - } - pc += 1; - } - &opcodes::LDC_W => { - let cp_index = read_u16(&code.opcodes, pc) as usize; - match self.constant_pool.get(&cp_index).unwrap() { - CpEntry::Integer(i) => { - stack.push(Value::I32(*i)); - } - CpEntry::Float(f) => { - stack.push(Value::F32(*f)); - } - _ => { panic!("unexpected") } - } - pc += 2; - } - &opcodes::LDC2_W => { - let cp_index = read_u16(&code.opcodes, pc) as usize; - match self.constant_pool.get(&cp_index).unwrap() { - CpEntry::Double(d) => { - stack.push(Value::F64(*d)); - } - CpEntry::Long(l) => { - stack.push(Value::I64(*l)); - } - _ => { panic!("unexpected") } - } - - - pc += 2; - } - &opcodes::IRETURN => { - return stack.pop(); - } - &opcodes::DRETURN => { - return stack.pop(); - } - &opcodes::FRETURN => { - return stack.pop(); - } - //TODO implement all opcodes - _ => { panic!("opcode not implemented") } - } - } - } - None // TODO error situation - } } pub struct Field { @@ -178,6 +109,13 @@ impl Field { full_name } + + pub fn type_of(&self) -> &String { + if let CpEntry::Utf8(s) = &self.constant_pool.get(&self.descriptor_index).unwrap() { + return s; + } + panic!() + } } const MODIFIERS: [(u16, &str); 12] = [ @@ -260,7 +198,7 @@ impl Exception { pub struct MethodCode { max_stack: u16, max_locals: u16, - opcodes: Vec, + pub(crate) opcodes: Vec, exception_table: Vec, code_attributes: HashMap, } @@ -274,25 +212,7 @@ impl MethodCode { } } -struct Stack { - data: Vec, -} -impl Stack { - fn new() -> Self { - Self { - data: vec![] - } - } - - fn push(&mut self, val: Value) { - self.data.push(val); - } - - fn pop(&mut self) -> Option { - self.data.pop() - } -} #[derive(Debug)] pub enum Value { diff --git a/src/heap.rs b/src/heap.rs new file mode 100644 index 0000000..4b3f12f --- /dev/null +++ b/src/heap.rs @@ -0,0 +1,34 @@ +use std::rc::Rc; +use crate::class::Class; + +pub(crate) struct Object { + // locked: bool, + // hashcode: i32, + class: Rc, + data: Vec, +} + +impl Object { + pub fn new(class: Rc, data: Vec) -> Self { + Self { + class, + data, + } + } +} + +pub(crate) struct Heap { + objects: Vec, +} + +impl Heap { + pub fn new() -> Self { + Self { + objects: vec![] + } + } + + pub(crate) fn new_object(&mut self, object: Object) { + self.objects.push(object); + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 230b54b..c1d3b55 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,11 +1,13 @@ -pub mod types; +pub mod class; pub mod io; pub mod opcodes; +pub mod vm; +mod heap; use std::collections::HashMap; use std::rc::Rc; use crate::io::{read_f32, read_f64, read_i32, read_i64, read_u16, read_u32}; -use crate::types::{AttributeType, Class, MethodCode, Exception, Field, Method}; +use crate::class::{AttributeType, Class, MethodCode, Exception, Field, Method}; pub fn get_class(bytecode: Vec) -> Option { check_magic(&bytecode); diff --git a/src/opcodes.rs b/src/opcodes.rs index 79da924..40aeda0 100644 --- a/src/opcodes.rs +++ b/src/opcodes.rs @@ -20,7 +20,7 @@ pub const LDC2_W: u8 = 20; // (0x14) Push long or double from run-time constant // pub const dload_1:u8 = 39; // (0x27) Load double 1 from local variable // pub const dload_2:u8 = 40; // (0x28) Load double 2 from local variable // pub const dload_3:u8 = 41; // (0x29) Load double 3 from local variable -// pub const aload_0:u8 = 42;// (0x2a) +pub const ALOAD_0:u8 = 42;// (0x2a) // pub const aload_1:u8 = 43;// (0x2a) // pub const aload_2:u8 = 44;// (0x2b) // pub const aload_3:u8 = 45;// (0x2c) @@ -85,6 +85,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_v: u8 = 177; // (0xb1) Return void from method (actually 'return' but that's a keyword) +pub const NEW: u8 = 187; // (0xbb) Create new object // pub const invokevirtual: u8 = 182; // (0xb6) Invoke instance method; dispatch based on class // // pub const getstatic: u8 = 178; // (0xb2) Get static field from class diff --git a/src/vm.rs b/src/vm.rs new file mode 100644 index 0000000..478523a --- /dev/null +++ b/src/vm.rs @@ -0,0 +1,127 @@ +use std::collections::HashMap; +use std::rc::Rc; +use crate::{CpEntry, opcodes}; +use crate::heap::{Heap, Object}; +use crate::io::*; +use crate::class::{AttributeType, Class, Method, Value}; + +struct StackFrame { + data: Vec, +} + +impl StackFrame { + fn new() -> Self { + Self { + data: vec![] + } + } + + fn push(&mut self, val: Value) { + self.data.push(val); + } + + fn pop(&mut self) -> Option { + self.data.pop() + } +} + +pub struct Vm { + classes: HashMap, + heap: Heap, +} + +impl Vm { + pub fn new() -> Self { + Self { + classes: HashMap::new(), + heap: Heap::new(), + } + } + + pub fn new_instance(&self, class: &Class) { + for f in class.fields { + println!("{}", f.type_of()); + } + // Object::new(Rc::new(class)) + } + + pub fn execute(&mut self, method: &Method) -> Option { + if let AttributeType::Code(code) = method.attributes.get("Code").unwrap() { + let mut stack = StackFrame::new(); + let mut pc: usize = 0; + while pc < code.opcodes.len() { + let opcode = &code.opcodes[pc]; + pc += 1; + println!("{}", opcode); + match opcode { + &opcodes::BIPUSH => { + let c = code.opcodes[pc] as i32; + stack.push(Value::I32(c)); + pc += 1; + } + &opcodes::LDC => { + let cp_index = read_u8(&code.opcodes, pc) as usize; + match method.constant_pool.get(&cp_index).unwrap() { + CpEntry::Integer(i) => { + stack.push(Value::I32(*i)); + } + CpEntry::Float(f) => { + stack.push(Value::F32(*f)); + } + _ => {} + } + pc += 1; + } + &opcodes::LDC_W => { + let cp_index = read_u16(&code.opcodes, pc) as usize; + match method.constant_pool.get(&cp_index).unwrap() { + CpEntry::Integer(i) => { + stack.push(Value::I32(*i)); + } + CpEntry::Float(f) => { + stack.push(Value::F32(*f)); + } + _ => { panic!("unexpected") } + } + pc += 2; + } + &opcodes::LDC2_W => { + let cp_index = read_u16(&code.opcodes, pc) as usize; + match method.constant_pool.get(&cp_index).unwrap() { + CpEntry::Double(d) => { + stack.push(Value::F64(*d)); + } + CpEntry::Long(l) => { + stack.push(Value::I64(*l)); + } + _ => { panic!("unexpected") } + } + + pc += 2; + } + &opcodes::ALOAD_0 => {} + &opcodes::IRETURN => { + return stack.pop(); + } + &opcodes::DRETURN => { + return stack.pop(); + } + &opcodes::FRETURN => { + return stack.pop(); + } + &opcodes::NEW => { + let cp_index = read_u16(&code.opcodes, pc) as usize; + if let CpEntry::ClassRef(class_name_index) = method.constant_pool.get(&cp_index).unwrap() { + if let CpEntry::Utf8(class) = method.constant_pool.get(class_name_index).unwrap(){ + + } + } + } + //TODO implement all opcodes + _ => { panic!("opcode not implemented") } + } + } + } + None // TODO error situation + } +} \ No newline at end of file diff --git a/tests/Float.class b/tests/Float.class index 0f1d35d99b1612f583096bc955cbc2742e30952b..158186382f67613337da216bb887dd7079ef62e2 100644 GIT binary patch literal 357 zcmZ9HJ4*vW6ot>-XR@v)#z#Q0u!|N3(xeF%2DTDYMAFa3N!YN?LY(zy2|^%X;Scaf zNzQJekYeuR-0xiO{QQ1>0~n$k!N*C6Q}3Y%G~wVZ#JNBge2L(2T$nejjGd{x)Blknil1Hu9K`Hq9iYr zy<4ew6(hm{v090FN+@VogMO~evOh^zdTw#e3w>Vh-~s_c2pVb{<)W2Q(7#JAL8HBkQSQrG@83b9JG#D7z8H6UvOHH)X;9^csEn#F}*3fjD zSSgdw!NAGL!0MKhp9mBb$Vx0r)Xz!GOV{^L%1TWxVNhgX0-D0Wz{nuQz{b%7 diff --git a/tests/Float.java b/tests/Float.java index bbf3d48..9f66939 100644 --- a/tests/Float.java +++ b/tests/Float.java @@ -2,7 +2,13 @@ public class Float { private final static float f =42.0F; - public static float get() { + private float f2; + + public static float getF() { return f; } + + public float getF2(){ + return f2; + } } diff --git a/tests/class_tests.rs b/tests/class_tests.rs index cc2fb49..aecb0bd 100644 --- a/tests/class_tests.rs +++ b/tests/class_tests.rs @@ -1,12 +1,15 @@ mod test { use classfile_reader::{get_class, io}; - use classfile_reader::types::Value; + use classfile_reader::class::Value; + use classfile_reader::vm::Vm; #[test] fn get_constant_int() { let class = get_class(io::read_class_file("tests/Int.class")).unwrap(); assert_eq!((55, 0), class.get_version()); - if let Value::I32(v) = class.methods.get("public static get()I").unwrap().execute().unwrap() { + + + if let Value::I32(v) = Vm::new().execute(class.methods.get("public static get()I").unwrap()).unwrap() { assert_eq!(v, 42); } else { panic!("fail"); @@ -17,7 +20,7 @@ mod test { fn get_constant_double() { let class = get_class(io::read_class_file("tests/Double.class")).unwrap(); assert_eq!((55, 0), class.get_version()); - if let Value::F64(v) = class.methods.get("public static get()D").unwrap().execute().unwrap() { + if let Value::F64(v) = Vm::new().execute(class.methods.get("public static get()D").unwrap()).unwrap() { assert_eq!(v, 42.0); } else { panic!("fail"); @@ -26,9 +29,21 @@ mod test { #[test] fn get_constant_foat() { + let class = get_class(io::read_class_file("tests/Float.class")).unwrap(); + Vm::new().new_instance(class); + // assert_eq!((55, 0), class.get_version()); + // if let Value::F32(v) = Vm::new().execute(class.methods.get("public static getF()F").unwrap()).unwrap() { + // assert_eq!(v, 42.0); + // } else { + // panic!("fail"); + // } + } + + #[test] + fn get_foat() { let class = get_class(io::read_class_file("tests/Float.class")).unwrap(); assert_eq!((55, 0), class.get_version()); - if let Value::F32(v) = class.methods.get("public static get()F").unwrap().execute().unwrap() { + if let Value::F32(v) = Vm::new().execute(class.methods.get("public getF2()F").unwrap()).unwrap() { assert_eq!(v, 42.0); } else { panic!("fail");