invokespecial

This commit is contained in:
Sander Hautvast 2023-09-30 14:04:28 +02:00
parent 4d55892e61
commit f76a89a086
7 changed files with 585 additions and 122 deletions

363
Cargo.lock generated
View file

@ -2,24 +2,112 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 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]] [[package]]
name = "anyhow" name = "anyhow"
version = "1.0.75" version = "1.0.75"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" 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]] [[package]]
name = "byteorder" name = "byteorder"
version = "1.4.3" version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" 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]] [[package]]
name = "cfg-if" name = "cfg-if"
version = "1.0.0" version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 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]] [[package]]
name = "crc32fast" name = "crc32fast"
version = "1.3.2" version = "1.3.2"
@ -38,6 +126,71 @@ dependencies = [
"cfg-if", "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]] [[package]]
name = "java_rs" name = "java_rs"
version = "0.1.0" version = "0.1.0"
@ -46,13 +199,223 @@ dependencies = [
"zip", "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]] [[package]]
name = "zip" name = "zip"
version = "0.6.6" version = "0.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261"
dependencies = [ dependencies = [
"aes",
"byteorder", "byteorder",
"bzip2",
"constant_time_eq",
"crc32fast", "crc32fast",
"crossbeam-utils", "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",
] ]

View file

@ -5,4 +5,4 @@ edition = "2021"
[dependencies] [dependencies]
anyhow = { version = "1.0", features = ["default"] } anyhow = { version = "1.0", features = ["default"] }
zip = { version = "0.6", default-features = false } zip = { version = "0.6", features = ["zstd"] }

View file

@ -72,7 +72,7 @@ impl Method {
} }
pub fn name(&self) -> String { 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() { if let CpEntry::Utf8(s) = &self.constant_pool.get(&self.name_index).unwrap() {
full_name.push_str(s); full_name.push_str(s);
} }
@ -131,7 +131,6 @@ impl Field {
} }
pub fn type_of(&self) -> &String { pub fn type_of(&self) -> &String {
println!("{}", self.name_index);
if let CpEntry::Utf8(s) = &self.constant_pool.get(&self.descriptor_index).unwrap() { if let CpEntry::Utf8(s) = &self.constant_pool.get(&self.descriptor_index).unwrap() {
return s; return s;
} }

View file

@ -2,6 +2,60 @@ use anyhow::{anyhow, Error};
use std::fs::{self, File}; use std::fs::{self, File};
use std::io::Read; 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<String>, class_name: &str) -> Result<String, Error> {
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<Vec<u8>, 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 { pub(crate) fn read_u8(data: &[u8], pos: usize) -> u8 {
u8::from_be_bytes( u8::from_be_bytes(
data[pos..pos + 1] data[pos..pos + 1]
@ -58,24 +112,3 @@ pub(crate) fn read_f64(data: &[u8], pos: usize) -> f64 {
) )
} }
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('/');
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<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)?;
Ok(buffer)
}

View file

@ -2,8 +2,13 @@ use java_rs::vm::Vm;
use std::io::Error; use std::io::Error;
fn main() -> Result<(), Error> { fn main() -> Result<(), Error> {
// TODO cmdline args
// TODO build index for package -> jarfile?
let mut vm = Vm::new("tests"); 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(); .unwrap();
Ok(()) Ok(())
} }

View file

@ -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: &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 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 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 fload:u8 = 23; // (0x17) Load float from local variable
// pub const dload:u8 = 24; // (0x18) load double from local variable // pub const dload:u8 = 24; // (0x18) load double from local variable
// pub const aload:u8 = 25; //0x19 // pub const aload:u8 = 25; //0x19
// //
// pub const fload_0:u8 = 34; // (0x22) Load float 0 from local variable // 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_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_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 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_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_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_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 dload_3:u8 = 41; // (0x29) Load double 3 from local variable
pub const ALOAD_0: &u8 = &42; // (0x2a) pub const ALOAD_0: &u8 = &42; // (0x2a)
// pub const aload_1:u8 = 43;// (0x2a) // pub const aload_1:u8 = 43;// (0x2a)
// pub const aload_2:u8 = 44;// (0x2b) // pub const aload_2:u8 = 44;// (0x2b)
// pub const aload_3:u8 = 45;// (0x2c) // pub const aload_3:u8 = 45;// (0x2c)
// pub const faload: u8 = 48; // (0x30) Load float from array // pub const faload: u8 = 48; // (0x30) Load float from array
// pub const daload:u8 = 49; // (0x31) load double 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 castore:u8 = 85; // (0x55)
pub const DUP: &u8 = &89; // (0x59) duplicate the top operand stack value 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_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 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: 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_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 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 fadd: u8 = 98; // (0x62) Add float
// pub const dadd: u8 = 99; // (0x63) add double // pub const dadd: u8 = 99; // (0x63) add double
// //
// pub const dsub:u8 = 103; // (0x67) subtract double // pub const dsub:u8 = 103; // (0x67) subtract double
// pub const fmul: u8 = 106; // (0x6a) Multiply float // pub const fmul: u8 = 106; // (0x6a) Multiply float
// pub const dmul: u8 = 107; // (0x6b) Multiply double // pub const dmul: u8 = 107; // (0x6b) Multiply double
// //
// pub const fdiv: u8 = 110; // (0x6e) Divide float // pub const fdiv: u8 = 110; // (0x6e) Divide float
// pub const ddiv:u8 = 111; // (0x6f) divide double // pub const ddiv:u8 = 111; // (0x6f) divide double
// pub const frem: u8 = 114; // (0x72) Remainder float // pub const frem: u8 = 114; // (0x72) Remainder float
// pub const drem: u8 = 115; // (0x73) remainder double // pub const drem: u8 = 115; // (0x73) remainder double
// pub const fneg: u8 = 118; // (0x76) Negate float // pub const fneg: u8 = 118; // (0x76) Negate float
// pub const dneg: u8 = 119; // (0x77) Negate double // pub const dneg: u8 = 119; // (0x77) Negate double
// pub const f2i: u8 = 139; // (0x8b) Convert float to int // pub const f2i: u8 = 139; // (0x8b) Convert float to int
// pub const f2l: u8 = 140; // (0x8c) Convert float to long // pub const f2l: u8 = 140; // (0x8c) Convert float to long
// pub const f2d: u8 = 141; // (0x8d) Convert float to double // pub const f2d: u8 = 141; // (0x8d) Convert float to double
// pub const d2i:u8 = 142; // (0x8e) double to int // pub const d2i:u8 = 142; // (0x8e) double to int
// pub const d2l:u8 = 143; // (0x8f) double to long // pub const d2l:u8 = 143; // (0x8f) double to long
// pub const d2f: u8 = 144; // (0x90) double to float // pub const d2f: u8 = 144; // (0x90) double to float
// pub const fcmpl:u8 = 149; // (0x95) Compare float (less than) // pub const fcmpl:u8 = 149; // (0x95) Compare float (less than)
// pub const fcmpg: u8 = 150; // (0x96) Compare float (greater than) // pub const fcmpg: u8 = 150; // (0x96) Compare float (greater than)
// pub const dcmpl:u8 = 151; // (0x97) compare double (less than) // pub const dcmpl:u8 = 151; // (0x97) compare double (less than)
// pub const dcmpg:u8 = 152; // (0x98) compare double (greater than) // pub const dcmpg:u8 = 152; // (0x98) compare double (greater than)
// //
pub const IRETURN: &u8 = &172; // (0xac) ireturn pub const IRETURN: &u8 = &172; // (0xac) ireturn
pub const FRETURN: &u8 = &174; // (0xae) Return float from method pub const FRETURN: &u8 = &174; // (0xae) Return float from method
pub const DRETURN: &u8 = &175; // (0xaf) Return double from method pub const DRETURN: &u8 = &175; // (0xaf) Return double from method
// pub const areturn: u8 = 176; //(0xb0) return reference // 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 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 getstatic: u8 = 178; // (0xb2) Get static field from class
pub const GETFIELD: &u8 = &180; // (0xb4) Fetch field from object3 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 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 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 anewarray: u8 = 189; // (0xbd)
// //
// pub const arraylength: u8 = 190; // (0xbe) // pub const arraylength: u8 = 190; // (0xbe)
// //
// pub const athrow: u8 = 191; // (0xbf) // pub const athrow: u8 = 191; // (0xbf)
// //
// pub const checkcast: u8 = 192; // (0xc0) // pub const checkcast: u8 = 192; // (0xc0)

145
src/vm.rs
View file

@ -1,14 +1,17 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::rc::Rc;
use std::sync::Arc; use std::sync::Arc;
use anyhow::{anyhow, Error}; use anyhow::{anyhow, Error};
use crate::class::{AttributeType, Class, Value}; use crate::class::{AttributeType, Class, Value};
use crate::class::Value::Void;
use crate::classloader::{load_class, CpEntry}; use crate::classloader::{load_class, CpEntry};
use crate::heap::{Heap, Object}; use crate::heap::{Heap, Object};
use crate::io::*; use crate::io::*;
use crate::opcodes::*; use crate::opcodes::*;
#[derive(Debug)]
struct StackFrame { struct StackFrame {
data: Vec<Arc<Value>>, data: Vec<Arc<Value>>,
} }
@ -27,29 +30,43 @@ impl StackFrame {
} }
} }
/// single threaded vm
pub struct Vm { pub struct Vm {
classpath: Vec<String>, classpath: Vec<String>,
classes: HashMap<String, Arc<Class>>, classes: HashMap<String, Arc<Class>>,
//TODO implement classloader
heap: Heap, heap: Heap,
pub exitcode: i32, stack: Vec<StackFrame>,
} }
const CP_SEP: char = ':';
//TODO semicolon on windows
impl Vm { 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 { pub fn new(classpath: &'static str) -> Self {
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(), classes: HashMap::new(),
heap: Heap::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<Arc<Class>, Error> { pub fn get_class(&mut self, class_name: &str) -> Result<Arc<Class>, Error> {
println!("get_class {}", class_name);
let entry = self.classes.entry(class_name.into()); let entry = self.classes.entry(class_name.into());
let entry = entry.or_insert_with(|| { 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 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()) Arc::new(load_class(bytecode).unwrap())
}); });
Ok(entry.clone()) Ok(entry.clone())
@ -74,35 +91,39 @@ impl Vm {
Object::new(class.clone(), data) Object::new(class.clone(), data)
} }
/// execute the bytecode
pub fn execute( pub fn execute(
&mut self, &mut self,
class_name: &str, class_name: &str,
method_name: &str, method_name: &str,
instance: Option<Arc<Object>>, instance: Option<Arc<Value>>,
) -> Result<Arc<Value>, Error> { ) -> Result<Arc<Value>, Error> {
println!("execute {}.{}", class_name, method_name);
let class = self.get_class(class_name)?; let class = self.get_class(class_name)?;
let method = class.get_method(method_name)?; let method = class.get_method(method_name)?;
if let AttributeType::Code(code) = method.attributes.get("Code").unwrap() { 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; let mut pc: usize = 0;
while pc < code.opcodes.len() { while pc < code.opcodes.len() {
let opcode = &code.opcodes[pc]; let opcode = &code.opcodes[pc];
pc += 1; pc += 1;
println!("opcode {}", opcode); println!("opcode {} ", opcode);
match opcode { match opcode {
BIPUSH => { BIPUSH => {
let c = code.opcodes[pc] as i32; 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; pc += 1;
} }
LDC => { LDC => {
let cp_index = read_u8(&code.opcodes, pc) as u16; let cp_index = read_u8(&code.opcodes, pc) as u16;
match method.constant_pool.get(&cp_index).unwrap() { match method.constant_pool.get(&cp_index).unwrap() {
CpEntry::Integer(i) => { CpEntry::Integer(i) => {
stack.push(Arc::new(Value::I32(*i))); self.local_stack().push(Arc::new(Value::I32(*i)));
} }
CpEntry::Float(f) => { 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); let cp_index = read_u16(&code.opcodes, pc);
match method.constant_pool.get(&cp_index).unwrap() { match method.constant_pool.get(&cp_index).unwrap() {
CpEntry::Integer(i) => { CpEntry::Integer(i) => {
stack.push(Arc::new(Value::I32(*i))); self.local_stack().push(Arc::new(Value::I32(*i)));
} }
CpEntry::Float(f) => { CpEntry::Float(f) => {
stack.push(Arc::new(Value::F32(*f))); self.local_stack().push(Arc::new(Value::F32(*f)));
} }
_ => { _ => {
panic!("unexpected") panic!("unexpected")
@ -127,10 +148,10 @@ impl Vm {
let cp_index = read_u16(&code.opcodes, pc); let cp_index = read_u16(&code.opcodes, pc);
match method.constant_pool.get(&cp_index).unwrap() { match method.constant_pool.get(&cp_index).unwrap() {
CpEntry::Double(d) => { CpEntry::Double(d) => {
stack.push(Arc::new(Value::F64(*d))); self.local_stack().push(Arc::new(Value::F64(*d)));
} }
CpEntry::Long(l) => { CpEntry::Long(l) => {
stack.push(Arc::new(Value::I64(*l))); self.local_stack().push(Arc::new(Value::I64(*l)));
} }
_ => { _ => {
panic!("unexpected") panic!("unexpected")
@ -139,66 +160,80 @@ impl Vm {
pc += 2; pc += 2;
} }
ALOAD_0 => match instance.clone() { ALOAD_0 => {
Some(r) => { println!("ALOAD_0");
stack.push(Arc::new(Value::Ref(r))); match instance.clone() {
Some(instance) => {
self.local_stack().push(instance);
}
None => {
panic!("static context")
}
} }
None => { }
panic!("static context")
}
},
DUP => { DUP => {
println!("DUP"); println!("DUP");
let value = stack.pop().expect("Stack empty"); let value = self.local_stack().pop().expect("Stack empty");
stack.push(value.clone()); self.local_stack().push(value.clone());
stack.push(value); self.local_stack().push(value);
} }
IRETURN => { IRETURN => {
return stack.pop(); return self.local_stack().pop();
} }
DRETURN => { DRETURN => {
return stack.pop(); return self.local_stack().pop();
} }
FRETURN => { FRETURN => {
return stack.pop(); return self.local_stack().pop();
}
RETURN_VOID => {
self.stack.pop();
return Ok(Arc::new(Void))
} }
GETFIELD => { GETFIELD => {
let cp_index = read_u16(&code.opcodes, pc); let cp_index = read_u16(&code.opcodes, pc);
if let CpEntry::Fieldref(_class_index, name_and_type_index) = if let CpEntry::Fieldref(_class_index, name_and_type_index) =
method.constant_pool.get(&cp_index).unwrap() 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? //TODO smell?
if let CpEntry::NameAndType(name, _) = if let CpEntry::NameAndType(name, _) =
method.constant_pool.get(name_and_type_index).unwrap() method.constant_pool.get(name_and_type_index).unwrap()
{ {
let value = inst.data.get(name).unwrap(); let value = inst.data.get(name).unwrap();
// println!("{:?}", value); // println!("{:?}", value);
stack.push(value.clone()); self.local_stack().push(value.clone());
} }
} }
} }
pc += 2; pc += 2;
} }
INVOKEVIRTUAL =>{
//TODO implement
}
INVOKESPECIAL => { INVOKESPECIAL => {
let ref_index = read_u16(&code.opcodes, pc); println!("INVOKESPECIAL");
if let CpEntry::MethodRef(_class_index, name_and_type_index) = let cp_index = read_u16(&code.opcodes, pc);
method.constant_pool.get(&ref_index).unwrap() 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; pc += 2;
} }
NEW => { NEW => {
println!("new"); let class_index = read_u16(&code.opcodes, pc);
let cp_index = read_u16(&code.opcodes, pc); println!("cp_index {}", class_index);
if let CpEntry::ClassRef(class_name_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() 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)); 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); self.heap.new_object(object);
} }
} }
@ -206,7 +241,7 @@ impl Vm {
} }
//TODO implement all opcodes //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")) Err(anyhow!("should not happen"))
} }
} }
//TODO refs with lifetime
fn get_signature_for_invoke(cp: Rc<HashMap<u16, CpEntry>>, 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<HashMap<u16, CpEntry>>, 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
}