Skip to content

Commit

Permalink
Add naive heap support
Browse files Browse the repository at this point in the history
  • Loading branch information
hextriclosan committed Aug 29, 2024
1 parent 2934f64 commit 3666479
Show file tree
Hide file tree
Showing 17 changed files with 452 additions and 125 deletions.
2 changes: 1 addition & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ fn main() {

let filename = &args[1];

let vm = match VM::new(filename) {
let vm = match VM::new(filename, "std") {
Ok(vm) => vm,
Err(err) => {
eprintln!("Failed to create VM: {}", err);
Expand Down
29 changes: 24 additions & 5 deletions tests/integration_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,54 @@ use vm::vm::VM;

#[test]
fn should_do_adding() {
let vm = VM::new("tests/test_data/Adder.class").unwrap();
let vm = VM::new("tests/test_data/Adder.class", "tests/test_data/std").unwrap();
let last_frame_value = vm.run().unwrap();
assert_eq!(55, last_frame_value.unwrap())
}

#[test]
fn should_do_subtraction() {
let vm = VM::new("tests/test_data/Sub.class").unwrap();
let vm = VM::new("tests/test_data/Sub.class", "tests/test_data/std").unwrap();
let last_frame_value = vm.run().unwrap();
assert_eq!(-999, last_frame_value.unwrap())
}

#[test]
fn should_do_subtraction_with_instance() {
let vm = VM::new("tests/test_data/SubNew.class", "tests/test_data/std").unwrap();
let last_frame_value = vm.run().unwrap();
assert_eq!(-989, last_frame_value.unwrap())
}

#[test]
fn should_do_extreme_stack_operations() {
let vm = VM::new("tests/test_data/ExtremeStackTest.class").unwrap();
let vm = VM::new(
"tests/test_data/ExtremeStackTest.class",
"tests/test_data/std",
)
.unwrap();
let last_frame_value = vm.run().unwrap();
assert_eq!(454, last_frame_value.unwrap())
}

#[test]
fn should_do_calculate_fibonacci_iteratively() {
let vm = VM::new("tests/test_data/FibonacciIterative.class").unwrap();
let vm = VM::new(
"tests/test_data/FibonacciIterative.class",
"tests/test_data/std",
)
.unwrap();
let last_frame_value = vm.run().unwrap();
assert_eq!(55, last_frame_value.unwrap())
}

#[test]
fn should_do_calculate_fibonacci_recursively() {
let vm = VM::new("tests/test_data/FibonacciRecursive.class").unwrap();
let vm = VM::new(
"tests/test_data/FibonacciRecursive.class",
"tests/test_data/std",
)
.unwrap();
let last_frame_value = vm.run().unwrap();
assert_eq!(55, last_frame_value.unwrap())
}
Binary file added tests/test_data/SubNew.class
Binary file not shown.
18 changes: 18 additions & 0 deletions tests/test_data/SubNew.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@

public class SubNew {

public SubNew() {
}

public static void main(String[] args) {
int first = 11;
int second = 1000;
SubNew subNew = new SubNew();
int result = subNew.sub(first, second);
}

private int sub(int first, int second) {
return first - second;
}

}
Binary file added tests/test_data/std/Object.class
Binary file not shown.
6 changes: 6 additions & 0 deletions tests/test_data/std/Object.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

package java.lang;

public class Object {
public Object() {}
}
148 changes: 65 additions & 83 deletions vm/src/class_loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,50 @@ use crate::method_area::java_class::{JavaClass, Methods};
use crate::method_area::java_method::JavaMethod;
use crate::method_area::method_area::MethodArea;
use crate::method_area::signature::Signature;
use crate::util::{get_class_name_by_cpool_class_index, get_cpool_string};
use jclass::attributes::Attribute;
use jclass::attributes::Attribute::Code;
use jclass::class_file::{parse, ClassFile};
use jclass::constant_pool::ConstantPool::{Methodref, NameAndType, Utf8};
use std::collections::HashMap;
use std::fs::File;
use std::io::ErrorKind::Other;
use std::io::Read;
use std::path::PathBuf;
use std::{fs, io};

#[derive(Debug)]
pub struct ClassLoader {
method_area: MethodArea,
}

impl ClassLoader {
pub(crate) fn new(class_file_name: &str) -> Result<Self> {
pub(crate) fn new(class_file_name: &str, std_dir: &str) -> Result<Self> {
let mut loaded_classes = HashMap::new();

let std_class_names = Self::get_class_files_in_dir(std_dir)?;

for path in std_class_names {
let class = Self::load_class(path.to_str().ok_or(Error::new_io(io::Error::new(
Other,
"error getting path".to_string(),
)))?)?;

loaded_classes.insert(class.0, class.1);
}

let class = Self::load_class(class_file_name)?;
loaded_classes.insert(class.0.clone(), class.1);

Ok(Self {
method_area: MethodArea::new(loaded_classes, class.0),
})
}

pub fn method_area(&self) -> &MethodArea {
&self.method_area
}

fn load_class(class_file_name: &str) -> Result<(String, JavaClass)> {
let mut file = File::open(class_file_name)?;

let mut buff = Vec::new();
Expand All @@ -26,31 +55,27 @@ impl ClassLoader {
let class_file = parse(buff.as_slice())
.map_err(|err| Error::new(ErrorKind::ClassFile(err.to_string())))?;

let java_class = Self::to_java_class(class_file)?;

Ok(Self {
method_area: MethodArea::new(java_class),
})
Self::to_java_class(class_file)
}

fn to_java_class(class_file: ClassFile) -> Result<JavaClass> {
fn to_java_class(class_file: ClassFile) -> Result<(String, JavaClass)> {
let methods = Self::get_methods(&class_file)?;
let class_name = Self::get_class_name(&class_file)?;

Ok(JavaClass::new(methods))
Ok((class_name.clone(), JavaClass::new(methods, class_file)))
}

fn get_methods(class_file: &ClassFile) -> Result<Methods> {
let methods = class_file.methods();
let mut methodsignature_by_cpoolindex: HashMap<u16, String> = HashMap::new();
let mut method_by_signature: HashMap<String, JavaMethod> = HashMap::new();

for method in methods.iter() {
let method_name = Self::get_cpool_string(class_file, method.name_index() as usize)
let method_name = get_cpool_string(class_file, method.name_index() as usize)
.ok_or(Error::new_constant_pool("Error getting method name"))?;
let method_signature =
Self::get_cpool_string(class_file, method.descriptor_index() as usize).ok_or(
Error::new_constant_pool("Error getting method method_signature"),
)?;
let method_signature = get_cpool_string(class_file, method.descriptor_index() as usize)
.ok_or(Error::new_constant_pool(
"Error getting method method_signature",
))?;
let code = Self::get_cpool_code_attribute(method.attributes())
.ok_or(Error::new_constant_pool("Error getting method code"))?;
let key_signature = method_name + ":" + method_signature.as_str();
Expand All @@ -64,74 +89,9 @@ impl ClassLoader {
code.2,
),
);

let cpool_index = Self::get_cpool_method_index(
class_file,
method.name_index(),
method.descriptor_index(),
);
if let Some(index) = cpool_index {
methodsignature_by_cpoolindex.insert(index, key_signature);
}
}

Ok(Methods::new(
methodsignature_by_cpoolindex,
method_by_signature,
))
}

fn get_cpool_string(class_file: &ClassFile, index: usize) -> Option<String> {
let constant_pool = class_file.constant_pool();

constant_pool.get(index).and_then(|item| match item {
Utf8 { value } => Some(value.clone()),
_ => None,
})
}

fn get_cpool_method_index(
class_file: &ClassFile,
name_index_to_find: u16,
signature_index: u16,
) -> Option<u16> {
let constant_pool = class_file.constant_pool();

let found_name_and_type_index = constant_pool.iter().enumerate().find_map(|index| {
if let NameAndType {
name_index,
descriptor_index,
} = *index.1
{
if name_index == name_index_to_find && descriptor_index == signature_index {
Some(index.0)
} else {
None
}
} else {
None
}
})? as u16;

let this_class_index = class_file.this_class();

constant_pool.iter().enumerate().find_map(|index| {
if let Methodref {
class_index,
name_and_type_index,
} = *index.1
{
if class_index == this_class_index
&& name_and_type_index == found_name_and_type_index
{
Some(index.0 as u16)
} else {
None
}
} else {
None
}
})
Ok(Methods::new(method_by_signature))
}

fn get_cpool_code_attribute(attributes: &Vec<Attribute>) -> Option<(u16, u16, Vec<u8>)> {
Expand All @@ -150,7 +110,29 @@ impl ClassLoader {
})
}

pub fn method_area(&self) -> &MethodArea {
&self.method_area
fn get_class_name(class_file: &ClassFile) -> Result<String> {
let this_class_index = class_file.this_class() as usize;

get_class_name_by_cpool_class_index(this_class_index, class_file)
.ok_or(Error::new_constant_pool("error getting class name"))
}

fn get_class_files_in_dir(std_dir: &str) -> Result<Vec<PathBuf>> {
let mut class_files = Vec::new();
let entries = fs::read_dir(std_dir)?;

for entry in entries {
let entry = entry?;
let path = entry.path();
if path.is_file() {
if let Some(extension) = path.extension() {
if extension == "class" {
class_files.push(path);
}
}
}
}

Ok(class_files)
}
}
4 changes: 4 additions & 0 deletions vm/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ impl Error {
Self::new(Execution(String::from(descr)))
}

pub(crate) fn new_io(err: io::Error) -> Self {
Self::new(Io(err))
}

pub fn kind(&self) -> &ErrorKind {
&self.0
}
Expand Down
Loading

0 comments on commit 3666479

Please sign in to comment.