diff --git a/Cargo.lock b/Cargo.lock index 14834504..4ae088d0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,55 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "anstream" +version = "0.6.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" + +[[package]] +name = "anstyle-parse" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +dependencies = [ + "anstyle", + "windows-sys", +] + [[package]] name = "autocfg" version = "1.3.0" @@ -20,6 +69,45 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" +[[package]] +name = "clap" +version = "4.5.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_lex" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" + +[[package]] +name = "colorchoice" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + [[package]] name = "jclass" version = "0.1.0" @@ -42,12 +130,98 @@ dependencies = [ name = "rusty-jvm" version = "0.1.0" dependencies = [ + "clap", "vm", ] +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "vm" version = "0.1.0" dependencies = [ "jclass", ] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/Cargo.toml b/Cargo.toml index b17a031f..67445613 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,5 +7,6 @@ edition = "2021" members = ["jclass", "vm"] [dependencies] +clap = "4.5.16" vm = {path = "vm"} diff --git a/src/main.rs b/src/main.rs index a41739bd..29094d42 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,32 +1,36 @@ -use std::env; +use clap::{arg, Arg, ArgAction, Command}; use std::process; use vm::vm::VM; -fn print_usage() -> Result<(), String> { - let current_exe = env::current_exe() - .map_err(|err| format!("Failed to get the current executable path: {}", err))?; - let file_name = current_exe - .file_name() - .and_then(|name| name.to_str()) - .ok_or_else(|| "Failed to determine the executable name".to_string())?; - println!("Usage: {} ", file_name); - Ok(()) -} - fn main() { - let args: Vec = env::args().collect(); - - if args.len() < 2 { - if let Err(err) = print_usage() { - eprintln!("{}", err); - process::exit(1); - } - process::exit(1); - } - - let filename = &args[1]; + let matches = Command::new("rusty-jvm") + .arg( + arg!(--"std-dir" ) + .help("Path to Java Standard libraries dir") + .required(true), + ) + .arg( + arg!(--"entry-point" ) + .help("Class to run") + .required(true), + ) + .arg( + Arg::new("classes") + .action(ArgAction::Append) + .help("Java classes to load") + .required(true), + ) + .get_matches(); - let vm = match VM::new(filename, "std") { + let std_dir = matches.get_one::("std-dir").unwrap(); + let entry_point = matches.get_one::("entry-point").unwrap(); + let classes = matches + .get_many::("classes") + .unwrap() + .into_iter() + .map(|s| s.as_str()) + .collect(); + let vm = match VM::new(classes, std_dir) { Ok(vm) => vm, Err(err) => { eprintln!("Failed to create VM: {}", err); @@ -34,7 +38,7 @@ fn main() { } }; - let result = match vm.run() { + let result = match vm.run(entry_point) { Ok(output) => output, Err(err) => { eprintln!("VM execution failed: {}", err); diff --git a/tests/integration_test.rs b/tests/integration_test.rs index 10e722a2..c19995dc 100644 --- a/tests/integration_test.rs +++ b/tests/integration_test.rs @@ -2,65 +2,75 @@ use vm::vm::VM; #[test] fn should_do_adding() { - let vm = VM::new("tests/test_data/Adder.class", "tests/test_data/std").unwrap(); - let last_frame_value = vm.run().unwrap(); + let vm = VM::new(vec!["tests/test_data/Adder.class"], "tests/test_data/std").unwrap(); + let last_frame_value = vm.run("Adder").unwrap(); assert_eq!(55, last_frame_value.unwrap()) } #[test] fn should_do_subtraction() { - let vm = VM::new("tests/test_data/Sub.class", "tests/test_data/std").unwrap(); - let last_frame_value = vm.run().unwrap(); + let vm = VM::new(vec!["tests/test_data/Sub.class"], "tests/test_data/std").unwrap(); + let last_frame_value = vm.run("Sub").unwrap(); assert_eq!(-999, last_frame_value.unwrap()) } #[test] fn should_write_read_instance_fields() { let vm = VM::new( - "tests/test_data/InstanceFields.class", + vec![ + "tests/test_data/InstanceFieldsUser.class", + "tests/test_data/InstanceFields.class", + ], "tests/test_data/std", ) .unwrap(); - let last_frame_value = vm.run().unwrap(); - assert_eq!(11022, last_frame_value.unwrap()) + let last_frame_value = vm.run("InstanceFieldsUser").unwrap(); + assert_eq!(110022, last_frame_value.unwrap()) } #[test] fn should_write_read_static_fields() { - let vm = VM::new("tests/test_data/StaticFields.class", "tests/test_data/std").unwrap(); - let last_frame_value = vm.run().unwrap(); - assert_eq!(11022, last_frame_value.unwrap()) + let vm = VM::new( + vec![ + "tests/test_data/StaticFieldsUser.class", + "tests/test_data/StaticFields.class", + ], + "tests/test_data/std", + ) + .unwrap(); + let last_frame_value = vm.run("StaticFieldsUser").unwrap(); + assert_eq!(110022, last_frame_value.unwrap()) } #[test] fn should_do_extreme_stack_operations() { let vm = VM::new( - "tests/test_data/ExtremeStackTest.class", + vec!["tests/test_data/ExtremeStackTest.class"], "tests/test_data/std", ) .unwrap(); - let last_frame_value = vm.run().unwrap(); + let last_frame_value = vm.run("ExtremeStackTest").unwrap(); assert_eq!(454, last_frame_value.unwrap()) } #[test] fn should_do_calculate_fibonacci_iteratively() { let vm = VM::new( - "tests/test_data/FibonacciIterative.class", + vec!["tests/test_data/FibonacciIterative.class"], "tests/test_data/std", ) .unwrap(); - let last_frame_value = vm.run().unwrap(); + let last_frame_value = vm.run("FibonacciIterative").unwrap(); assert_eq!(55, last_frame_value.unwrap()) } #[test] fn should_do_calculate_fibonacci_recursively() { let vm = VM::new( - "tests/test_data/FibonacciRecursive.class", + vec!["tests/test_data/FibonacciRecursive.class"], "tests/test_data/std", ) .unwrap(); - let last_frame_value = vm.run().unwrap(); + let last_frame_value = vm.run("FibonacciRecursive").unwrap(); assert_eq!(55, last_frame_value.unwrap()) } diff --git a/tests/test_data/InstanceFields.class b/tests/test_data/InstanceFields.class index 71da17fb..8d8f0551 100644 Binary files a/tests/test_data/InstanceFields.class and b/tests/test_data/InstanceFields.class differ diff --git a/tests/test_data/InstanceFields.java b/tests/test_data/InstanceFields.java index 4c1d8174..a9327c3b 100644 --- a/tests/test_data/InstanceFields.java +++ b/tests/test_data/InstanceFields.java @@ -5,16 +5,6 @@ public class InstanceFields { private int resultAdd; private int resultMul; - public static void main(String[] args) { - int first = 11; - int second = 1000; - InstanceFields instance = new InstanceFields(); - instance.sub(first, second); - instance.add(first, second); - instance.mul(first, second); - int result = instance.resultSub + instance.resultAdd + instance.resultMul; - } - public void sub(int first, int second) { resultSub = first - second; } diff --git a/tests/test_data/InstanceFieldsUser.class b/tests/test_data/InstanceFieldsUser.class new file mode 100644 index 00000000..df3c5350 Binary files /dev/null and b/tests/test_data/InstanceFieldsUser.class differ diff --git a/tests/test_data/InstanceFieldsUser.java b/tests/test_data/InstanceFieldsUser.java new file mode 100644 index 00000000..76e086d4 --- /dev/null +++ b/tests/test_data/InstanceFieldsUser.java @@ -0,0 +1,12 @@ + +public class InstanceFieldsUser { + public static void main(String[] args) { + int first = 11; + int second = 10000; + InstanceFields instance = new InstanceFields(); + instance.sub(first, second); + instance.add(first, second); + instance.mul(first, second); + int result = instance.getResultSub() + instance.getResultAdd() + instance.getResultMul(); + } +} diff --git a/tests/test_data/StaticFields.class b/tests/test_data/StaticFields.class index 4ce9cb56..e7255b5d 100644 Binary files a/tests/test_data/StaticFields.class and b/tests/test_data/StaticFields.class differ diff --git a/tests/test_data/StaticFields.java b/tests/test_data/StaticFields.java index ca498434..b3909688 100644 --- a/tests/test_data/StaticFields.java +++ b/tests/test_data/StaticFields.java @@ -5,13 +5,16 @@ public class StaticFields { private static int resultAdd; private static int resultMul; - public static void main(String[] args) { - int first = 11; - int second = 1000; - sub(first, second); - add(first, second); - mul(first, second); - int result = resultSub + resultAdd + resultMul; + public static int getResultSub() { + return resultSub; + } + + public static int getResultAdd() { + return resultAdd; + } + + public static int getResultMul() { + return resultMul; } public static void sub(int first, int second) { diff --git a/tests/test_data/StaticFieldsUser.class b/tests/test_data/StaticFieldsUser.class new file mode 100644 index 00000000..982eb7e1 Binary files /dev/null and b/tests/test_data/StaticFieldsUser.class differ diff --git a/tests/test_data/StaticFieldsUser.java b/tests/test_data/StaticFieldsUser.java new file mode 100644 index 00000000..1581fc2b --- /dev/null +++ b/tests/test_data/StaticFieldsUser.java @@ -0,0 +1,11 @@ + +public class StaticFieldsUser { + public static void main(String[] args) { + int first = 11; + int second = 10000; + StaticFields.sub(first, second); + StaticFields.add(first, second); + StaticFields.mul(first, second); + int result = StaticFields.getResultSub() + StaticFields.getResultAdd() + StaticFields.getResultMul(); + } +} diff --git a/vm/src/class_loader.rs b/vm/src/class_loader.rs index 4f88708f..a1ce89b1 100644 --- a/vm/src/class_loader.rs +++ b/vm/src/class_loader.rs @@ -23,7 +23,7 @@ pub struct ClassLoader { } impl ClassLoader { - pub(crate) fn new(class_file_name: &str, std_dir: &str) -> Result { + pub(crate) fn new(class_file_names: Vec<&str>, std_dir: &str) -> Result { let mut loaded_classes = HashMap::new(); let std_class_names = Self::get_class_files_in_dir(std_dir)?; @@ -37,11 +37,15 @@ impl ClassLoader { loaded_classes.insert(class.0, class.1); } - let class = Self::load_class(class_file_name)?; - loaded_classes.insert(class.0.clone(), class.1); + let mut classes: Vec = vec![]; + for class_file_name in class_file_names { + let class = Self::load_class(class_file_name)?; + loaded_classes.insert(class.0.clone(), class.1); + classes.push(class.0); + } Ok(Self { - method_area: MethodArea::new(loaded_classes, class.0), + method_area: MethodArea::new(loaded_classes), }) } @@ -62,9 +66,9 @@ impl ClassLoader { } fn to_java_class(class_file: ClassFile) -> Result<(String, JavaClass)> { - let methods = Self::get_methods(&class_file)?; - let static_fields = Self::get_static_fields(&class_file)?; let class_name = Self::get_class_name(&class_file)?; + let methods = Self::get_methods(&class_file, class_name.as_str())?; + let static_fields = Self::get_static_fields(&class_file)?; Ok(( class_name.clone(), @@ -72,7 +76,7 @@ impl ClassLoader { )) } - fn get_methods(class_file: &ClassFile) -> Result { + fn get_methods(class_file: &ClassFile, class_name: &str) -> Result { let methods = class_file.methods(); let mut method_by_signature: HashMap = HashMap::new(); @@ -94,6 +98,7 @@ impl ClassLoader { code.0, code.1, code.2, + class_name, ), ); } diff --git a/vm/src/execution_engine/engine.rs b/vm/src/execution_engine/engine.rs index c6163a80..289229ba 100644 --- a/vm/src/execution_engine/engine.rs +++ b/vm/src/execution_engine/engine.rs @@ -12,19 +12,18 @@ pub(crate) struct Engine<'a> { } impl<'a> Engine<'a> { - pub(crate) fn execute( - &mut self, - main_class_name: &str, - method: &JavaMethod, - ) -> crate::error::Result> { + pub(crate) fn execute(&mut self, method: &JavaMethod) -> crate::error::Result> { let mut stack_frames = vec![method.new_stack_frame()]; let mut last_value: Option = None; + let mut current_class_name: String; while !stack_frames.is_empty() { let stack_frame = stack_frames .last_mut() .ok_or(Error::new_execution("Error getting stack frame"))?; + current_class_name = stack_frame.current_class_name().to_string(); + match stack_frame.get_bytecode_byte() { ICONST_0 => { println!("ICONST_0"); @@ -393,7 +392,7 @@ impl<'a> Engine<'a> { let java_class = self .method_area .loaded_classes - .get(main_class_name) + .get(current_class_name.as_str()) .unwrap(); let (class_name, field_name) = @@ -431,7 +430,7 @@ impl<'a> Engine<'a> { let java_class = self .method_area .loaded_classes - .get(main_class_name) + .get(current_class_name.as_str()) .unwrap(); let (class_name, field_name) = @@ -461,7 +460,7 @@ impl<'a> Engine<'a> { let java_class = self .method_area .loaded_classes - .get(main_class_name) + .get(current_class_name.as_str()) .unwrap(); let objectref = stack_frame.pop(); @@ -491,7 +490,7 @@ impl<'a> Engine<'a> { let java_class = self .method_area .loaded_classes - .get(main_class_name) + .get(current_class_name.as_str()) .unwrap(); let (class_name, field_name) = @@ -524,7 +523,7 @@ impl<'a> Engine<'a> { let java_class = self .method_area .loaded_classes - .get(main_class_name) + .get(current_class_name.as_str()) .unwrap(); let virtual_method = self.method_area.get_method_by_methodref_cpool_index( @@ -562,7 +561,7 @@ impl<'a> Engine<'a> { let java_class = self .method_area .loaded_classes - .get(main_class_name) + .get(current_class_name.as_str()) .unwrap(); let special_method = self.method_area.get_method_by_methodref_cpool_index( @@ -599,7 +598,7 @@ impl<'a> Engine<'a> { let java_class = self .method_area .loaded_classes - .get(main_class_name) + .get(current_class_name.as_str()) .unwrap(); let static_method = self.method_area.get_method_by_methodref_cpool_index( @@ -629,7 +628,7 @@ impl<'a> Engine<'a> { let java_class = self .method_area .loaded_classes - .get(main_class_name) + .get(current_class_name.as_str()) .unwrap(); let class_to_invoke_new_for = get_class_name_by_cpool_class_index( class_constpool_index, diff --git a/vm/src/method_area/java_method.rs b/vm/src/method_area/java_method.rs index f65da797..79000794 100644 --- a/vm/src/method_area/java_method.rs +++ b/vm/src/method_area/java_method.rs @@ -7,15 +7,23 @@ pub(crate) struct JavaMethod { max_stack: u16, max_locals: u16, bytecode: Vec, + class_name: String, } impl JavaMethod { - pub fn new(signature: Signature, max_stack: u16, max_locals: u16, bytecode: Vec) -> Self { + pub fn new( + signature: Signature, + max_stack: u16, + max_locals: u16, + bytecode: Vec, + class_name: &str, + ) -> Self { Self { signature, max_stack, max_locals, bytecode, + class_name: class_name.to_string(), } } @@ -24,6 +32,7 @@ impl JavaMethod { self.max_locals as usize, self.max_stack as usize, &self.bytecode, + self.class_name.clone(), ) } diff --git a/vm/src/method_area/method_area.rs b/vm/src/method_area/method_area.rs index 60a904e6..84fdefbe 100644 --- a/vm/src/method_area/method_area.rs +++ b/vm/src/method_area/method_area.rs @@ -8,15 +8,11 @@ use std::collections::HashMap; #[derive(Debug)] pub(crate) struct MethodArea { pub(crate) loaded_classes: HashMap, - pub(crate) main_class_name: String, } impl MethodArea { - pub fn new(loaded_classes: HashMap, main_class_name: String) -> Self { - Self { - loaded_classes, - main_class_name, - } + pub fn new(loaded_classes: HashMap) -> Self { + Self { loaded_classes } } pub fn set_static_field_value( diff --git a/vm/src/stack/stack_frame.rs b/vm/src/stack/stack_frame.rs index ec45ab50..30b7948e 100644 --- a/vm/src/stack/stack_frame.rs +++ b/vm/src/stack/stack_frame.rs @@ -3,15 +3,22 @@ pub(crate) struct StackFrame<'a> { pub locals: Vec, pub(crate) operand_stack: Vec, bytecode_ref: &'a [u8], + current_class_name: String, } impl<'a> StackFrame<'a> { - pub fn new(locals_size: usize, stack_size: usize, bytecode_ref: &'a [u8]) -> Self { + pub fn new( + locals_size: usize, + stack_size: usize, + bytecode_ref: &'a [u8], + current_class_name: String, + ) -> Self { StackFrame { pc: 0, locals: vec![0i32; locals_size], operand_stack: Vec::with_capacity(stack_size), bytecode_ref, + current_class_name, } } @@ -54,4 +61,8 @@ impl<'a> StackFrame<'a> { pub fn get_local(&mut self, index: usize) -> i32 { *self.locals.get(index).unwrap() } + + pub fn current_class_name(&self) -> &str { + &self.current_class_name + } } diff --git a/vm/src/vm.rs b/vm/src/vm.rs index b143a67a..583e31a5 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -7,20 +7,18 @@ pub struct VM { } impl VM { - pub fn new(class_file_name: &str, std_dir: &str) -> crate::error::Result { - let class_loader = ClassLoader::new(class_file_name, std_dir)?; + pub fn new(class_file_names: Vec<&str>, std_dir: &str) -> crate::error::Result { + let class_loader = ClassLoader::new(class_file_names, std_dir)?; Ok(Self { class_loader }) } - pub fn run(&self) -> crate::error::Result> { - let main_class_name = self.class_loader.method_area().main_class_name.as_str(); - + pub fn run(&self, main_class_name: &str) -> crate::error::Result> { let main_method = self .class_loader .method_area() .get_method_by_name_signature(main_class_name, "main:([Ljava/lang/String;)V")?; let mut engine = Engine::new(&self.class_loader.method_area()); - engine.execute(main_class_name, main_method) + engine.execute(main_method) } }