From f76a89a0868b5c2010fab1857f523c7983212f58 Mon Sep 17 00:00:00 2001 From: Sander Hautvast Date: Sat, 30 Sep 2023 14:04:28 +0200 Subject: [PATCH] invokespecial --- Cargo.lock | 363 +++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 2 +- src/class.rs | 3 +- src/io.rs | 75 +++++++--- src/main.rs | 7 +- src/opcodes.rs | 112 +++++++-------- src/vm.rs | 145 ++++++++++++++------ 7 files changed, 585 insertions(+), 122 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ba27d60..1701c19 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,24 +2,112 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aes" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + [[package]] name = "anyhow" version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + [[package]] name = "byteorder" version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +[[package]] +name = "bzip2" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" +dependencies = [ + "bzip2-sys", + "libc", +] + +[[package]] +name = "bzip2-sys" +version = "0.1.11+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "jobserver", + "libc", +] + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + +[[package]] +name = "cpufeatures" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +dependencies = [ + "libc", +] + [[package]] name = "crc32fast" version = "1.3.2" @@ -38,6 +126,71 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "deranged" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946" + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "flate2" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6c98ee8095e9d1dcbf2fcc6d95acccb90d1c81db1e44725c6a984b1dbdfb010" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + [[package]] name = "java_rs" version = "0.1.0" @@ -46,13 +199,223 @@ dependencies = [ "zip", ] +[[package]] +name = "jobserver" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +dependencies = [ + "libc", +] + +[[package]] +name = "libc" +version = "0.2.148" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "password-hash" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" +dependencies = [ + "base64ct", + "rand_core", + "subtle", +] + +[[package]] +name = "pbkdf2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" +dependencies = [ + "digest", + "hmac", + "password-hash", + "sha2", +] + +[[package]] +name = "pkg-config" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" + +[[package]] +name = "proc-macro2" +version = "1.0.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "serde" +version = "1.0.188" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.188" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "syn" +version = "2.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "time" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "426f806f4089c493dcac0d24c29c01e2c38baf8e30f1b716ee37e83d200b18fe" +dependencies = [ + "deranged", + "serde", + "time-core", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + [[package]] name = "zip" version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" dependencies = [ + "aes", "byteorder", + "bzip2", + "constant_time_eq", "crc32fast", "crossbeam-utils", + "flate2", + "hmac", + "pbkdf2", + "sha1", + "time", + "zstd", +] + +[[package]] +name = "zstd" +version = "0.11.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "5.0.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.8+zstd.1.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5556e6ee25d32df2586c098bbfa278803692a20d0ab9565e049480d52707ec8c" +dependencies = [ + "cc", + "libc", + "pkg-config", ] diff --git a/Cargo.toml b/Cargo.toml index c585e90..d99ee17 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,4 +5,4 @@ edition = "2021" [dependencies] anyhow = { version = "1.0", features = ["default"] } -zip = { version = "0.6", default-features = false } \ No newline at end of file +zip = { version = "0.6", features = ["zstd"] } \ No newline at end of file diff --git a/src/class.rs b/src/class.rs index 72b2ba3..541ea3b 100644 --- a/src/class.rs +++ b/src/class.rs @@ -72,7 +72,7 @@ impl Method { } pub fn name(&self) -> String { - let mut full_name = get_modifier(self.access_flags); + let mut full_name = String::new(); if let CpEntry::Utf8(s) = &self.constant_pool.get(&self.name_index).unwrap() { full_name.push_str(s); } @@ -131,7 +131,6 @@ impl Field { } pub fn type_of(&self) -> &String { - println!("{}", self.name_index); if let CpEntry::Utf8(s) = &self.constant_pool.get(&self.descriptor_index).unwrap() { return s; } diff --git a/src/io.rs b/src/io.rs index 8079514..781f715 100644 --- a/src/io.rs +++ b/src/io.rs @@ -2,6 +2,60 @@ use anyhow::{anyhow, Error}; use std::fs::{self, File}; use std::io::Read; +/// resolves the actual path where the class file is found +/// for std lib there is a special case that resolves to the jmod +/// notation: +/// * [jmod]#classes/[package_path]/[class].class +/// * [jar/zip]#[package_path]/[class].class +/// * [dir]/[package_path]/[class].class +pub fn find_class(classpath: &Vec, class_name: &str) -> Result { + if class_name.starts_with("java/") { + let mut path: String = "jmods/java.base.jmod#classes/".into(); + path.push_str(class_name); + path.push_str(".class"); + return Ok(path); + } + + for clp_entry in classpath { + if fs::metadata(&clp_entry)?.is_dir() { + let mut maybe_path = clp_entry.clone(); + maybe_path.push('/'); + 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); + } + } else { + //TODO jar/zip files + } + } + Err(anyhow!("Class not found {}", class_name)) +} + +/// reads the binary class file from file path or archive +/// and returns the byte array as Vec +pub fn read_bytecode(name: String) -> Result, Error> { + let mut buffer; + if name.contains("#") { + let parts: Vec<&str> = name.split("#").collect(); + let archive_file = File::open(parts[0])?; + let mut archive_zip = zip::ZipArchive::new(archive_file)?; + let mut entry = archive_zip.by_name(parts[1])?; + buffer = vec![0; entry.size() as usize]; + entry.read(&mut buffer)?; + } else { + let mut f = File::open(&name)?; + let metadata = fs::metadata(&name)?; + buffer = vec![0; metadata.len() as usize]; + let _ = f.read(&mut buffer)?; + } + Ok(buffer) +} + + +// methods to read values from big-endian binary data + pub(crate) fn read_u8(data: &[u8], pos: usize) -> u8 { u8::from_be_bytes( data[pos..pos + 1] @@ -58,24 +112,3 @@ pub(crate) fn read_f64(data: &[u8], pos: usize) -> f64 { ) } -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('/'); - 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); - } - } - 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)?; - Ok(buffer) -} diff --git a/src/main.rs b/src/main.rs index 7848eb7..b0229d5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,8 +2,13 @@ use java_rs::vm::Vm; use std::io::Error; fn main() -> Result<(), Error> { + // TODO cmdline args + // TODO build index for package -> jarfile? + let mut vm = Vm::new("tests"); - vm.execute("Main", "public static main([Ljava/lang/String;)V", None) + let main_class = "Main"; + + vm.execute(main_class, "main([Ljava/lang/String;)V", None) .unwrap(); Ok(()) } diff --git a/src/opcodes.rs b/src/opcodes.rs index 92dd499..e163d82 100644 --- a/src/opcodes.rs +++ b/src/opcodes.rs @@ -9,22 +9,22 @@ pub const BIPUSH: &u8 = &16; // (0x10) Push byte pub const LDC: &u8 = &18; // (0x12) Push item from run-time pub constant pool pub const LDC_W: &u8 = &19; // (0x13) Push item from run-time constant pool (wide index) pub const LDC2_W: &u8 = &20; // (0x14) Push long or double from run-time constant pool (wide index) - // pub const fload:u8 = 23; // (0x17) Load float from local variable - // pub const dload:u8 = 24; // (0x18) load double from local variable - // pub const aload:u8 = 25; //0x19 - // - // pub const fload_0:u8 = 34; // (0x22) Load float 0 from local variable - // pub const fload_1:u8 = 35; // (0x23) Load float 1 from local variable - // pub const fload_2:u8 = 36; // (0x24) Load float 2 from local variable - // pub const fload_3:u8 = 37; // (0x25) Load float 3 from local variable - // pub const dload_0:u8 = 38; // (0x26) Load double 0 from local variable - // pub const dload_1:u8 = 39; // (0x27) Load double 1 from local variable - // pub const dload_2:u8 = 40; // (0x28) Load double 2 from local variable - // pub const dload_3:u8 = 41; // (0x29) Load double 3 from local variable +// pub const fload:u8 = 23; // (0x17) Load float from local variable +// pub const dload:u8 = 24; // (0x18) load double from local variable +// pub const aload:u8 = 25; //0x19 +// +// pub const fload_0:u8 = 34; // (0x22) Load float 0 from local variable +// pub const fload_1:u8 = 35; // (0x23) Load float 1 from local variable +// pub const fload_2:u8 = 36; // (0x24) Load float 2 from local variable +// pub const fload_3:u8 = 37; // (0x25) Load float 3 from local variable +// pub const dload_0:u8 = 38; // (0x26) Load double 0 from local variable +// pub const dload_1:u8 = 39; // (0x27) Load double 1 from local variable +// pub const dload_2:u8 = 40; // (0x28) Load double 2 from local variable +// pub const dload_3:u8 = 41; // (0x29) Load double 3 from local variable pub const ALOAD_0: &u8 = &42; // (0x2a) - // pub const aload_1:u8 = 43;// (0x2a) - // pub const aload_2:u8 = 44;// (0x2b) - // pub const aload_3:u8 = 45;// (0x2c) + // pub const aload_1:u8 = 43;// (0x2a) + // pub const aload_2:u8 = 44;// (0x2b) + // pub const aload_3:u8 = 45;// (0x2c) // pub const faload: u8 = 48; // (0x30) Load float from array // pub const daload:u8 = 49; // (0x31) load double from array @@ -52,50 +52,50 @@ pub const ALOAD_0: &u8 = &42; // (0x2a) // // pub const castore:u8 = 85; // (0x55) pub const DUP: &u8 = &89; // (0x59) duplicate the top operand stack value - // pub const dup_x1: u8 = 90; // (0x5a) Duplicate the top operand stack value and insert two values down - // pub const dup_x2: u8 = 91; // (0x5b) Duplicate the top operand stack value and insert two or three values down - // pub const dup2: u8 = 92; // (0x5c) Duplicate the top one or two operand stack values - // pub const dup2_x1: u8 = 93; //(0x5d) Duplicate the top one or two operand stack values and insert two or three values down - // pub const dup2_x2:u8 = 94; // (0x5e) Duplicate the top one or two operand stack values and insert two, three, or four values down - // pub const fadd: u8 = 98; // (0x62) Add float - // pub const dadd: u8 = 99; // (0x63) add double - // - // pub const dsub:u8 = 103; // (0x67) subtract double - // pub const fmul: u8 = 106; // (0x6a) Multiply float - // pub const dmul: u8 = 107; // (0x6b) Multiply double - // - // pub const fdiv: u8 = 110; // (0x6e) Divide float - // pub const ddiv:u8 = 111; // (0x6f) divide double - // pub const frem: u8 = 114; // (0x72) Remainder float - // pub const drem: u8 = 115; // (0x73) remainder double - // pub const fneg: u8 = 118; // (0x76) Negate float - // pub const dneg: u8 = 119; // (0x77) Negate double - // pub const f2i: u8 = 139; // (0x8b) Convert float to int - // pub const f2l: u8 = 140; // (0x8c) Convert float to long - // pub const f2d: u8 = 141; // (0x8d) Convert float to double - // pub const d2i:u8 = 142; // (0x8e) double to int - // pub const d2l:u8 = 143; // (0x8f) double to long - // pub const d2f: u8 = 144; // (0x90) double to float - // pub const fcmpl:u8 = 149; // (0x95) Compare float (less than) - // pub const fcmpg: u8 = 150; // (0x96) Compare float (greater than) - // pub const dcmpl:u8 = 151; // (0x97) compare double (less than) - // pub const dcmpg:u8 = 152; // (0x98) compare double (greater than) - // +// pub const dup_x1: u8 = 90; // (0x5a) Duplicate the top operand stack value and insert two values down +// pub const dup_x2: u8 = 91; // (0x5b) Duplicate the top operand stack value and insert two or three values down +// pub const dup2: u8 = 92; // (0x5c) Duplicate the top one or two operand stack values +// pub const dup2_x1: u8 = 93; //(0x5d) Duplicate the top one or two operand stack values and insert two or three values down +// pub const dup2_x2:u8 = 94; // (0x5e) Duplicate the top one or two operand stack values and insert two, three, or four values down +// pub const fadd: u8 = 98; // (0x62) Add float +// pub const dadd: u8 = 99; // (0x63) add double +// +// pub const dsub:u8 = 103; // (0x67) subtract double +// pub const fmul: u8 = 106; // (0x6a) Multiply float +// pub const dmul: u8 = 107; // (0x6b) Multiply double +// +// pub const fdiv: u8 = 110; // (0x6e) Divide float +// pub const ddiv:u8 = 111; // (0x6f) divide double +// pub const frem: u8 = 114; // (0x72) Remainder float +// pub const drem: u8 = 115; // (0x73) remainder double +// pub const fneg: u8 = 118; // (0x76) Negate float +// pub const dneg: u8 = 119; // (0x77) Negate double +// pub const f2i: u8 = 139; // (0x8b) Convert float to int +// pub const f2l: u8 = 140; // (0x8c) Convert float to long +// pub const f2d: u8 = 141; // (0x8d) Convert float to double +// pub const d2i:u8 = 142; // (0x8e) double to int +// pub const d2l:u8 = 143; // (0x8f) double to long +// pub const d2f: u8 = 144; // (0x90) double to float +// pub const fcmpl:u8 = 149; // (0x95) Compare float (less than) +// pub const fcmpg: u8 = 150; // (0x96) Compare float (greater than) +// pub const dcmpl:u8 = 151; // (0x97) compare double (less than) +// pub const dcmpg:u8 = 152; // (0x98) compare double (greater than) +// pub const IRETURN: &u8 = &172; // (0xac) ireturn 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 getstatic: u8 = 178; // (0xb2) Get static field from class + // pub const areturn: u8 = 176; //(0xb0) return reference +pub const RETURN_VOID: &u8 = &177; // (0xb1) Return void from method (actually 'return' but that's a keyword) + // pub const getstatic: u8 = 178; // (0xb2) Get static field from class pub const GETFIELD: &u8 = &180; // (0xb4) Fetch field from object3 +pub const INVOKEVIRTUAL: &u8 = &182; // (0xb6) Invoke instance method; dispatch based on class pub const NEW: &u8 = &187; // (0xbb) Create new object - // pub const invokevirtual: u8 = 182; // (0xb6) Invoke instance method; dispatch based on class - // +// pub const INVOKESPECIAL: &u8 = &183; // (0xb7) // nvoke instance method; direct invocation of instance initialization methods and methods of the current class and its supertypes - // pub const anewarray: u8 = 189; // (0xbd) - // - // pub const arraylength: u8 = 190; // (0xbe) - // - // pub const athrow: u8 = 191; // (0xbf) - // - // pub const checkcast: u8 = 192; // (0xc0) + // pub const anewarray: u8 = 189; // (0xbd) + // + // pub const arraylength: u8 = 190; // (0xbe) + // + // pub const athrow: u8 = 191; // (0xbf) + // + // pub const checkcast: u8 = 192; // (0xc0) diff --git a/src/vm.rs b/src/vm.rs index 61b30ba..934fda3 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -1,14 +1,17 @@ use std::collections::HashMap; +use std::rc::Rc; use std::sync::Arc; use anyhow::{anyhow, Error}; use crate::class::{AttributeType, Class, Value}; +use crate::class::Value::Void; use crate::classloader::{load_class, CpEntry}; use crate::heap::{Heap, Object}; use crate::io::*; use crate::opcodes::*; +#[derive(Debug)] struct StackFrame { data: Vec>, } @@ -27,29 +30,43 @@ impl StackFrame { } } +/// single threaded vm pub struct Vm { classpath: Vec, classes: HashMap>, - //TODO implement classloader heap: Heap, - pub exitcode: i32, + stack: Vec, } +const CP_SEP: char = ':'; +//TODO semicolon on windows + impl Vm { + fn local_stack(&mut self) -> &mut StackFrame { + let i = self.stack.len() - 1; + self.stack.get_mut(i).unwrap() + } + pub fn new(classpath: &'static str) -> Self { Self { - classpath: classpath.split(':').map(|s| s.to_owned()).collect(), + classpath: classpath.split(CP_SEP).map(|s| s.to_owned()).collect(), classes: HashMap::new(), heap: Heap::new(), - exitcode: 0, + stack: vec![], } } + /// parse the binary data into a Class struct + /// gets the file from cache, or reads it from classpath + /// Vm keeps ownership of the class and hands out Arc references to it pub fn get_class(&mut self, class_name: &str) -> Result, Error> { + println!("get_class {}", class_name); let entry = self.classes.entry(class_name.into()); let entry = entry.or_insert_with(|| { + print!("read class {} ", class_name); let resolved_path = find_class(&self.classpath, class_name).expect("Class not found"); - let bytecode = read_class_file(resolved_path).unwrap(); + println!("full path {}", resolved_path); + let bytecode = read_bytecode(resolved_path).unwrap(); Arc::new(load_class(bytecode).unwrap()) }); Ok(entry.clone()) @@ -74,35 +91,39 @@ impl Vm { Object::new(class.clone(), data) } + /// execute the bytecode pub fn execute( &mut self, class_name: &str, method_name: &str, - instance: Option>, + instance: Option>, ) -> Result, Error> { + println!("execute {}.{}", class_name, method_name); let class = self.get_class(class_name)?; let method = class.get_method(method_name)?; if let AttributeType::Code(code) = method.attributes.get("Code").unwrap() { - let mut stack = StackFrame::new(); + let stackframe = StackFrame::new(); + self.stack.push(stackframe); + let mut pc: usize = 0; while pc < code.opcodes.len() { let opcode = &code.opcodes[pc]; pc += 1; - println!("opcode {}", opcode); + println!("opcode {} ", opcode); match opcode { BIPUSH => { let c = code.opcodes[pc] as i32; - stack.push(Arc::new(Value::I32(c))); + self.local_stack().push(Arc::new(Value::I32(c))); pc += 1; } LDC => { let cp_index = read_u8(&code.opcodes, pc) as u16; match method.constant_pool.get(&cp_index).unwrap() { CpEntry::Integer(i) => { - stack.push(Arc::new(Value::I32(*i))); + self.local_stack().push(Arc::new(Value::I32(*i))); } CpEntry::Float(f) => { - stack.push(Arc::new(Value::F32(*f))); + self.local_stack().push(Arc::new(Value::F32(*f))); } _ => {} } @@ -112,10 +133,10 @@ impl Vm { let cp_index = read_u16(&code.opcodes, pc); match method.constant_pool.get(&cp_index).unwrap() { CpEntry::Integer(i) => { - stack.push(Arc::new(Value::I32(*i))); + self.local_stack().push(Arc::new(Value::I32(*i))); } CpEntry::Float(f) => { - stack.push(Arc::new(Value::F32(*f))); + self.local_stack().push(Arc::new(Value::F32(*f))); } _ => { panic!("unexpected") @@ -127,10 +148,10 @@ impl Vm { let cp_index = read_u16(&code.opcodes, pc); match method.constant_pool.get(&cp_index).unwrap() { CpEntry::Double(d) => { - stack.push(Arc::new(Value::F64(*d))); + self.local_stack().push(Arc::new(Value::F64(*d))); } CpEntry::Long(l) => { - stack.push(Arc::new(Value::I64(*l))); + self.local_stack().push(Arc::new(Value::I64(*l))); } _ => { panic!("unexpected") @@ -139,66 +160,80 @@ impl Vm { pc += 2; } - ALOAD_0 => match instance.clone() { - Some(r) => { - stack.push(Arc::new(Value::Ref(r))); + ALOAD_0 => { + println!("ALOAD_0"); + match instance.clone() { + Some(instance) => { + self.local_stack().push(instance); + } + None => { + panic!("static context") + } } - None => { - panic!("static context") - } - }, + } DUP => { println!("DUP"); - let value = stack.pop().expect("Stack empty"); - stack.push(value.clone()); - stack.push(value); + let value = self.local_stack().pop().expect("Stack empty"); + self.local_stack().push(value.clone()); + self.local_stack().push(value); } IRETURN => { - return stack.pop(); + return self.local_stack().pop(); } DRETURN => { - return stack.pop(); + return self.local_stack().pop(); } FRETURN => { - return stack.pop(); + return self.local_stack().pop(); + } + RETURN_VOID => { + self.stack.pop(); + return Ok(Arc::new(Void)) } GETFIELD => { let cp_index = read_u16(&code.opcodes, pc); if let CpEntry::Fieldref(_class_index, name_and_type_index) = method.constant_pool.get(&cp_index).unwrap() { - if let Value::Ref(inst) = &*stack.pop()? { + if let Value::Ref(inst) = &*self.local_stack().pop()? { //TODO smell? if let CpEntry::NameAndType(name, _) = method.constant_pool.get(name_and_type_index).unwrap() { let value = inst.data.get(name).unwrap(); // println!("{:?}", value); - stack.push(value.clone()); + self.local_stack().push(value.clone()); } } } pc += 2; } + INVOKEVIRTUAL =>{ + //TODO implement + } INVOKESPECIAL => { - let ref_index = read_u16(&code.opcodes, pc); - if let CpEntry::MethodRef(_class_index, name_and_type_index) = - method.constant_pool.get(&ref_index).unwrap() - {} + println!("INVOKESPECIAL"); + let cp_index = read_u16(&code.opcodes, pc); + let instance = self.local_stack().pop().unwrap(); + if let Some((class, method)) = get_signature_for_invoke(Rc::clone(&method.constant_pool), cp_index) { + self.execute(class.as_str(), method.as_str(), Some(instance)); + } + pc += 2; } NEW => { - println!("new"); - let cp_index = read_u16(&code.opcodes, pc); + let class_index = read_u16(&code.opcodes, pc); + println!("cp_index {}", class_index); if let CpEntry::ClassRef(class_name_index) = - method.constant_pool.get(&cp_index).unwrap() + method.constant_pool.get(&class_index).unwrap() { - if let CpEntry::Utf8(_) = + if let CpEntry::Utf8(new_class) = method.constant_pool.get(class_name_index).unwrap() { - let class = self.get_class(class_name)?; + println!("new {}", new_class); + let class = self.get_class(new_class)?; let object = Arc::new(self.new_instance(class)); - stack.push(Arc::new(Value::Ref(object.clone()))); + self.local_stack().push(Arc::new(Value::Ref(object.clone()))); self.heap.new_object(object); } } @@ -206,7 +241,7 @@ impl Vm { } //TODO implement all opcodes _ => { - panic!("opcode not implemented") + panic!("opcode not implemented {:?}", self.stack) } } } @@ -214,3 +249,31 @@ impl Vm { Err(anyhow!("should not happen")) } } + + +//TODO refs with lifetime +fn get_signature_for_invoke(cp: Rc>, index: u16) -> Option<(String, String)> { + if let CpEntry::MethodRef(class_index, name_and_type_index) = cp.get(&index).unwrap() { + if let Some(method_signature) = get_name_and_type(Rc::clone(&cp), *name_and_type_index) { + if let CpEntry::ClassRef(class_name_index) = cp.get(&class_index).unwrap() { + if let CpEntry::Utf8(class_name) = cp.get(&class_name_index).unwrap() { + return Some((class_name.into(), method_signature)); + } + } + } + } + None +} + +fn get_name_and_type(cp: Rc>, index: u16) -> Option<(String)> { + if let CpEntry::NameAndType(method_name_index, signature_index) = cp.get(&index).unwrap() { + if let CpEntry::Utf8(method_name) = cp.get(&method_name_index).unwrap() { + if let CpEntry::Utf8(signature) = cp.get(&signature_index).unwrap() { + let mut method_signature: String = method_name.into(); + method_signature.push_str(signature); + return Some(method_signature); + } + } + } + None +} \ No newline at end of file