-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Introducing VM with support of basic stack operations
- Loading branch information
1 parent
1f1945b
commit 6a1053a
Showing
28 changed files
with
965 additions
and
2 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,14 @@ | ||
fn main() {} | ||
use vm::vm::VM; | ||
|
||
fn main() { | ||
match VM::new("tests/test_data/Sub.class") { | ||
Ok(vm) => { | ||
println!("{vm:#?}"); | ||
vm.run().expect("error running vm"); | ||
} | ||
Err(err) => { | ||
eprintln!("Error: {err}"); | ||
} | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
|
||
use vm::vm::VM; | ||
|
||
#[test] | ||
fn should_do_adding() { | ||
let vm = VM::new("tests/test_data/Adder.class").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 last_frame_value = vm.run().unwrap(); | ||
assert_eq!(-999, last_frame_value.unwrap()) | ||
} | ||
|
||
#[test] | ||
fn should_do_extreme_stack_operations() { | ||
let vm = VM::new("tests/test_data/ExtremeStackTest.class").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 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 last_frame_value = vm.run().unwrap(); | ||
assert_eq!(55, last_frame_value.unwrap()) | ||
} |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
//javac -g -parameters Adder.java | ||
|
||
public class Adder { | ||
|
||
public static void main(String[] args) { | ||
int result = add(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); | ||
} | ||
|
||
private static int add(int v1, int v2, int v3, int v4, int v5, int v6, int v7, int v8, int v9, int v10) { | ||
return v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8 + v9 + v10; | ||
} | ||
|
||
} |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
|
||
public class ExtremeStackTest { | ||
|
||
public static void main(String[] args) { | ||
// Test nested operations and method calls | ||
int nestedResult = nestedCalculations(10); | ||
//System.out.println("nestedResult=" + nestedResult); | ||
|
||
// Test loop operations with complex conditions | ||
int loopResult = complexLoop(5); | ||
//System.out.println("loopResult=" + loopResult); | ||
|
||
// Test combined recursive methods | ||
int combinedRecursionResult = factorialPlusFibonacci(5); | ||
//System.out.println("combinedRecursionResult=" + combinedRecursionResult); | ||
|
||
int result = nestedResult + loopResult + combinedRecursionResult; // 454 | ||
//System.out.println("result=" + result); | ||
} | ||
|
||
// Method with nested operations and method calls | ||
private static int nestedCalculations(int n) { | ||
int result = 0; | ||
for (int i = 0; i < n; i++) { | ||
result += multiplyAndAdd(i, n - i); | ||
} | ||
return result * 2 - n; // Final operation after loop | ||
} | ||
|
||
// Helper method for nestedCalculations | ||
private static int multiplyAndAdd(int x, int y) { | ||
return (x * y) + (x - y); // Simple arithmetic operations | ||
} | ||
|
||
// Method with loop, conditions, and nested operations | ||
private static int complexLoop(int n) { | ||
int result = 1; | ||
for (int i = 1; i <= n; i++) { | ||
if (i % 2 == 0) { | ||
result *= i; // Multiply for even numbers | ||
} else { | ||
result += i; // Add for odd numbers | ||
} | ||
result -= (i % 3 == 0) ? 1 : 0; // Subtract 1 if divisible by 3 | ||
} | ||
return result; | ||
} | ||
|
||
// Method combining recursion from factorial and Fibonacci | ||
private static int factorialPlusFibonacci(int n) { | ||
return factorial(n) + fibonacci(n); | ||
} | ||
|
||
// Recursive method to calculate factorial | ||
private static int factorial(int n) { | ||
if (n <= 1) { | ||
return 1; | ||
} | ||
return n * factorial(n - 1); | ||
} | ||
|
||
// Recursive method to calculate the nth Fibonacci number | ||
private static int fibonacci(int n) { | ||
if (n <= 1) { | ||
return n; | ||
} | ||
return fibonacci(n - 1) + fibonacci(n - 2); | ||
} | ||
} |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
|
||
|
||
public class FibonacciIterative { | ||
|
||
public static void main(String[] args) { | ||
int n = 10; // We want the 10th Fibonacci number | ||
int result = fibonacci(n); | ||
} | ||
|
||
private static int fibonacci(int n) { | ||
if (n == 0 || n == 1) { | ||
return n; | ||
} | ||
|
||
int prev = 0; | ||
int curr = 1; | ||
for (int i = 2; i <= n; i++) { | ||
int next = prev + curr; | ||
prev = curr; | ||
curr = next; | ||
} | ||
|
||
return curr; | ||
} | ||
} |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
|
||
|
||
public class FibonacciRecursive { | ||
|
||
public static void main(String[] args) { | ||
int n = 10; // We want the 10th Fibonacci number | ||
int result = fibonacci(n); | ||
} | ||
|
||
private static int fibonacci(int n) { | ||
if (n <= 1) { | ||
return n; | ||
} | ||
return fibonacci(n - 1) + fibonacci(n - 2); | ||
} | ||
|
||
} |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
//javac -g -parameters Adder.java | ||
|
||
public class Sub { | ||
public static void main(String[] args) { | ||
int first = 1; | ||
int second = 1000; | ||
int result = first - second; | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
[package] | ||
name = "vm" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
[dependencies] | ||
jclass = {path = "../jclass"} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
use std::collections::HashMap; | ||
use std::fs::File; | ||
use std::io::Read; | ||
use jclass::attributes::Attribute; | ||
use jclass::class_file::{parse, ClassFile}; | ||
use jclass::constant_pool::ConstantPool::{Methodref, NameAndType, Utf8}; | ||
use jclass::attributes::Attribute::Code; | ||
use crate::error::{Result, Error, ErrorKind}; | ||
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; | ||
|
||
#[derive(Debug)] | ||
pub struct ClassLoader { | ||
method_area: MethodArea, | ||
} | ||
|
||
impl ClassLoader { | ||
pub(crate) fn new(class_file_name: &str) -> Result<Self> { | ||
let mut file = File::open(class_file_name)?; | ||
|
||
let mut buff = Vec::new(); | ||
file.read_to_end(&mut buff)?; | ||
|
||
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) }) | ||
} | ||
|
||
fn to_java_class(class_file: ClassFile) -> Result<JavaClass> { | ||
let methods = Self::get_methods(&class_file)?; | ||
|
||
Ok(JavaClass::new(methods)) | ||
} | ||
|
||
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) | ||
.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 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(); | ||
|
||
method_by_signature.insert(key_signature.clone(), JavaMethod::new(Signature::from_str(method_signature.as_str())?, code.0, code.1, 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 }) | ||
} | ||
|
||
fn get_cpool_code_attribute(attributes: &Vec<Attribute>) -> Option<(u16, u16, Vec<u8>)> { | ||
attributes.iter() | ||
.find_map(|item| { | ||
if let Code { max_stack, max_locals, code, .. } = item { | ||
Some((*max_stack, *max_locals, code.clone())) | ||
} else { | ||
None | ||
} | ||
}) | ||
} | ||
|
||
pub fn method_area(&self) -> &MethodArea { | ||
&self.method_area | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
use std::{io, result}; | ||
use std::fmt::{Debug, Display, Formatter}; | ||
use crate::error::ErrorKind::{ClassFile, ConstantPool, Execution, Io}; | ||
|
||
pub type Result<T> = result::Result<T, Error>; | ||
|
||
#[derive(Debug)] | ||
pub struct Error(Box<ErrorKind>); | ||
|
||
impl Error { | ||
pub(crate) fn new(error_kind: ErrorKind) -> Self { | ||
Self(Box::new(error_kind)) | ||
} | ||
|
||
pub(crate) fn new_constant_pool(descr: &str) -> Self { | ||
Self::new(ConstantPool(String::from(descr))) | ||
} | ||
|
||
pub(crate) fn new_execution(descr: &str) -> Self { | ||
Self::new(Execution(String::from(descr))) | ||
} | ||
|
||
pub fn kind(&self) -> &ErrorKind { | ||
&self.0 | ||
} | ||
|
||
pub fn into_kind(self) -> ErrorKind { | ||
*self.0 | ||
} | ||
} | ||
|
||
impl Display for Error { | ||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { | ||
match &*self.0 { | ||
Io(err) => write!(f, "I/O Error: {err}"), | ||
ClassFile(err) => write!(f, "ClassFile Error: {err}"), | ||
ConstantPool(descr) => write!(f, "ConstantPool Error: {descr}"), | ||
Execution(descr) => write!(f, "Execution Error: {descr}"), | ||
|
||
ErrorKind::__Nonexhaustive => unreachable!(), | ||
} | ||
} | ||
} | ||
|
||
impl From<io::Error> for Error { | ||
fn from(error: io::Error) -> Self { | ||
Error::new(Io(error)) | ||
} | ||
} | ||
|
||
#[derive(Debug)] | ||
pub enum ErrorKind { | ||
Io(io::Error), | ||
ClassFile(String), | ||
ConstantPool(String), | ||
Execution(String), | ||
|
||
#[doc(hidden)] | ||
__Nonexhaustive, | ||
} |
Oops, something went wrong.