diff --git a/Cargo.lock b/Cargo.lock index 7abed0e..48a89ef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,60 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "aho-corasick" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea5d730647d4fadd988536d06fecce94b7b4f2a7efdae548f1cf4b63205518ab" +dependencies = [ + "memchr", +] + +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + [[package]] name = "classfile_reader" version = "0.1.0" +dependencies = [ + "anyhow", + "regex", +] + +[[package]] +name = "memchr" +version = "2.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" + +[[package]] +name = "regex" +version = "1.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" diff --git a/Cargo.toml b/Cargo.toml index a768019..33130d1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,4 +5,6 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -[dependencies] \ No newline at end of file +[dependencies] +regex = "1.9.5" +anyhow = { version = "1.0", features = [] } \ No newline at end of file diff --git a/src/class.rs b/src/class.rs index 08f09df..cd5b30b 100644 --- a/src/class.rs +++ b/src/class.rs @@ -1,8 +1,8 @@ use std::collections::HashMap; use std::fmt; use std::rc::Rc; +use crate::classloader::CpEntry; -use crate::CpEntry; use crate::io::read_u16; #[derive(Debug)] @@ -30,7 +30,9 @@ impl Class { // execute(m).unwrap() //TODO // } - + pub fn get_method(&self, name: &str) -> &Method { + self.methods.get(name).expect("ClassNountFoundException") + } } pub struct Method { @@ -209,15 +211,15 @@ impl MethodCode { } } - - #[derive(Debug)] pub enum Value { Void, + Null, + // the $1_000_000 mistake I32(i32), I64(i64), F32(f32), F64(f64), BOOL(bool), - CHAR(char) + CHAR(char), } \ No newline at end of file diff --git a/src/classloader.rs b/src/classloader.rs index 2f3b8c6..5f3c7fa 100644 --- a/src/classloader.rs +++ b/src/classloader.rs @@ -1,9 +1,10 @@ use std::collections::HashMap; use std::rc::Rc; +use anyhow::Error; use crate::io::{read_f32, read_f64, read_i32, read_i64, read_u16, read_u32}; use crate::class::{AttributeType, Class, MethodCode, Exception, Field, Method}; -pub fn load_class(bytecode: Vec) -> Option { +pub fn load_class(bytecode: Vec) -> Result { check_magic(&bytecode); let constant_pool_count = read_u16(&bytecode, 8); @@ -59,7 +60,7 @@ pub fn load_class(bytecode: Vec) -> Option { } } - Some(Class { + Ok(Class { minor_version: read_u16(&bytecode, 4), major_version: read_u16(&bytecode, 6), constant_pool, diff --git a/src/io.rs b/src/io.rs index d4b8c65..521ce67 100644 --- a/src/io.rs +++ b/src/io.rs @@ -1,5 +1,6 @@ use std::fs::{self, File}; use std::io::Read; +use anyhow::{anyhow, Error}; pub(crate) fn read_u8(data: &[u8], pos: usize) -> u8 { u8::from_be_bytes(data[pos..pos + 1].try_into().expect("slice with incorrect length")) @@ -29,10 +30,24 @@ pub(crate) fn read_f64(data: &[u8], pos: usize) -> f64 { f64::from_be_bytes(data[pos..pos + 8].try_into().expect("slice with incorrect length")) } -pub fn read_class_file(name: &str) -> Vec { - let mut f = File::open(name).expect("no file found"); - let metadata = fs::metadata(name).expect("unable to read metadata"); +pub fn find_class(classpath: &Vec, class_name: &str) -> Result { + for clp_entry in classpath { + let mut maybe_path = clp_entry.clone(); + maybe_path.push_str("/"); + maybe_path.push_str(class_name); + maybe_path.push_str(".class"); + println!("{}", maybe_path); + if fs::metadata(&maybe_path)?.is_file() { + return Ok(maybe_path); + } + } + return Err(anyhow!("Class not found {}", class_name)); +} + +pub fn read_class_file(name: String) -> Result, Error> { + let mut f = File::open(&name)?; + let metadata = fs::metadata(&name)?; let mut buffer = vec![0; metadata.len() as usize]; - let _ = f.read(&mut buffer).expect("buffer overflow"); - buffer + let _ = f.read(&mut buffer)?; + Ok(buffer) } \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 3594e23..9c93bc1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,5 +3,5 @@ pub mod io; pub mod opcodes; pub mod vm; mod heap; -mod classloader; +pub mod classloader; diff --git a/src/main.rs b/src/main.rs index 915b031..8849bcf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,5 @@ +use regex::Regex; + fn main() { // if let Some(class) = classfile_reader::get_class(classfile_reader::io::read_class_file("./Dummy.class")){ // println!("{:?}", class); @@ -5,6 +7,9 @@ fn main() { // println!("{:?}", ret); // } + let pattern = Regex::new(".*/(.+)").unwrap(); + let c = pattern.captures("java/lang/String").unwrap().get(1); + println!("{}", c.unwrap().as_str()); } diff --git a/src/opcodes.rs b/src/opcodes.rs index 40aeda0..c978320 100644 --- a/src/opcodes.rs +++ b/src/opcodes.rs @@ -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 GETFIELD: u8 = 180; // (0xb4) Fetch field from object3 pub const NEW: u8 = 187; // (0xbb) Create new object // pub const invokevirtual: u8 = 182; // (0xb6) Invoke instance method; dispatch based on class // diff --git a/src/vm.rs b/src/vm.rs index b22b158..ee27e8b 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -1,5 +1,6 @@ use std::collections::HashMap; use std::rc::Rc; +use anyhow::Error; use crate::opcodes; use crate::class::{AttributeType, Class, Method, Value}; @@ -28,26 +29,40 @@ impl StackFrame { } pub struct Vm { - classes: HashMap, //TODO implement classloader + classpath: Vec, + classes: HashMap, + //TODO implement classloader heap: Heap, } impl Vm { - pub fn new() -> Self { + pub fn new(classpath: &'static str) -> Self { Self { + classpath: classpath.split(":").into_iter().map(|s| s.to_owned()).collect(), classes: HashMap::new(), heap: Heap::new(), } } - pub fn load_class(name: String){ - load_class() + pub fn get_class(&mut self, class_name: &str) -> Result<&Class, Error> { + if !self.classes.contains_key(class_name) { + self.load_class(class_name)?; + } + let class = self.classes.get(class_name); + Ok(class.expect("ClassNotFoundException")) + } + + pub fn load_class(&mut self, name: &str) -> Result<(), Error> { + let resolved_path = find_class(&self.classpath, name)?; + let bytecode = read_class_file(resolved_path)?; + self.classes.insert(name.to_owned(), load_class(bytecode)?); + Ok(()) } pub fn new_instance(&self, class: Rc) { let mut data = HashMap::new(); for f in &class.fields { - let value = match f.type_of().as_str(){ + let value = match f.type_of().as_str() { "Z" => Value::BOOL(false), "B" => Value::I32(0), "S" => Value::I32(0), @@ -55,7 +70,7 @@ impl Vm { "J" => Value::I64(0), "F" => Value::F32(0.0), "D" => Value::F64(0.0), - //ref + "L" => Value::Null, _ => Value::Void }; data.insert(f.name_index, value); @@ -63,83 +78,87 @@ impl Vm { Object::new(class.clone(), data); } - 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 u16; - match method.constant_pool.get(&cp_index).unwrap() { - CpEntry::Integer(i) => { - stack.push(Value::I32(*i)); - } - CpEntry::Float(f) => { - stack.push(Value::F32(*f)); - } - _ => {} + pub fn execute(&mut self, class_name: &str, method_name: &str) -> Option { + let class = self.classes.get(class_name); + if let Some(c) = class { + let method = c.get_method(method_name); + 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; } - pc += 1; - } - &opcodes::LDC_W => { - let cp_index = read_u16(&code.opcodes, pc); - match method.constant_pool.get(&cp_index).unwrap() { - CpEntry::Integer(i) => { - stack.push(Value::I32(*i)); + &opcodes::LDC => { + let cp_index = read_u8(&code.opcodes, pc) as u16; + match method.constant_pool.get(&cp_index).unwrap() { + CpEntry::Integer(i) => { + stack.push(Value::I32(*i)); + } + CpEntry::Float(f) => { + stack.push(Value::F32(*f)); + } + _ => {} } - CpEntry::Float(f) => { - stack.push(Value::F32(*f)); - } - _ => { panic!("unexpected") } + pc += 1; } - pc += 2; - } - &opcodes::LDC2_W => { - let cp_index = read_u16(&code.opcodes, pc); - match method.constant_pool.get(&cp_index).unwrap() { - CpEntry::Double(d) => { - stack.push(Value::F64(*d)); + &opcodes::LDC_W => { + let cp_index = read_u16(&code.opcodes, pc); + 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") } } - CpEntry::Long(l) => { - stack.push(Value::I64(*l)); - } - _ => { panic!("unexpected") } + pc += 2; } + &opcodes::LDC2_W => { + let cp_index = read_u16(&code.opcodes, pc); + 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); - 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(){ - - } + 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); + 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") } } - //TODO implement all opcodes - _ => { panic!("opcode not implemented") } } } + None // TODO error situation + } else { + panic!("class not found"); } - None // TODO error situation } } \ No newline at end of file diff --git a/tests/class_tests.rs b/tests/class_tests.rs index 55227de..0f45d75 100644 --- a/tests/class_tests.rs +++ b/tests/class_tests.rs @@ -1,16 +1,17 @@ mod test { use std::rc::Rc; - use classfile_reader::{get_class, io}; + use classfile_reader::{classloader::load_class, io}; 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(); + let mut vm = Vm::new("."); + let class = vm.get_class("Float").expect("ClassNotFound"); assert_eq!((55, 0), class.get_version()); - if let Value::I32(v) = Vm::new().execute(class.methods.get("public static get()I").unwrap()).unwrap() { + if let Value::I32(v) = Vm::new("").execute("Float", "public static get()I").unwrap() { assert_eq!(v, 42); } else { panic!("fail"); @@ -19,9 +20,10 @@ mod test { #[test] fn get_constant_double() { - let class = get_class(io::read_class_file("tests/Double.class")).unwrap(); + let mut vm = Vm::new("."); + let class = vm.get_class("Double").expect("ClassNotFound"); assert_eq!((55, 0), class.get_version()); - if let Value::F64(v) = Vm::new().execute(class.methods.get("public static get()D").unwrap()).unwrap() { + if let Value::F64(v) = Vm::new("").execute("Double", "public static get()D").unwrap() { assert_eq!(v, 42.0); } else { panic!("fail"); @@ -30,8 +32,8 @@ mod test { #[test] fn get_constant_foat() { - let class = get_class(io::read_class_file("tests/Float.class")).unwrap(); - Vm::new().new_instance(Rc::new(class)); + let mut vm = Vm::new("."); + vm.load_class("Float").expect("ClassNotFound"); // 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); @@ -41,11 +43,12 @@ mod test { } #[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) = Vm::new().execute(class.methods.get("public getF2()F").unwrap()).unwrap() { - assert_eq!(v, 42.0); + fn get_float() { + // assert_eq!((55, 0), class.get_version()); + let mut vm = Vm::new("/Users/FJ19WK/RustroverProjects/classfile_reader/tests"); + vm.load_class("Float").expect("ClassNotFound"); + if let Value::F32(v) = vm.execute("Float","public getF2()F").unwrap() { + assert_eq!(v, 0.0); } else { panic!("fail"); }