it compiles
This commit is contained in:
parent
b94769e868
commit
ffa9879515
10 changed files with 201 additions and 99 deletions
54
Cargo.lock
generated
54
Cargo.lock
generated
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -5,4 +5,6 @@ edition = "2021"
|
|||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
[dependencies]
|
||||
regex = "1.9.5"
|
||||
anyhow = { version = "1.0", features = [] }
|
||||
12
src/class.rs
12
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),
|
||||
}
|
||||
|
|
@ -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<u8>) -> Option<Class> {
|
||||
pub fn load_class(bytecode: Vec<u8>) -> Result<Class, Error> {
|
||||
check_magic(&bytecode);
|
||||
|
||||
let constant_pool_count = read_u16(&bytecode, 8);
|
||||
|
|
@ -59,7 +60,7 @@ pub fn load_class(bytecode: Vec<u8>) -> Option<Class> {
|
|||
}
|
||||
}
|
||||
|
||||
Some(Class {
|
||||
Ok(Class {
|
||||
minor_version: read_u16(&bytecode, 4),
|
||||
major_version: read_u16(&bytecode, 6),
|
||||
constant_pool,
|
||||
|
|
|
|||
25
src/io.rs
25
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<u8> {
|
||||
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<String>, class_name: &str) -> Result<String, Error> {
|
||||
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<Vec<u8>, 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)
|
||||
}
|
||||
|
|
@ -3,5 +3,5 @@ pub mod io;
|
|||
pub mod opcodes;
|
||||
pub mod vm;
|
||||
mod heap;
|
||||
mod classloader;
|
||||
pub mod classloader;
|
||||
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
//
|
||||
|
|
|
|||
165
src/vm.rs
165
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<String, Class>, //TODO implement classloader
|
||||
classpath: Vec<String>,
|
||||
classes: HashMap<String, Class>,
|
||||
//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<Class>) {
|
||||
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<Value> {
|
||||
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<Value> {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
@ -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");
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue