diff --git a/src/classloader/classdef.rs b/src/classloader/classdef.rs index 35ca972..7af49b4 100644 --- a/src/classloader/classdef.rs +++ b/src/classloader/classdef.rs @@ -318,13 +318,13 @@ impl Field { } } -pub(crate) struct Method { +pub struct Method { pub(crate) constant_pool: Rc>, - access_flags: u16, + pub access_flags: u16, name_index: u16, descriptor_index: u16, pub(crate) attributes: HashMap, - pub code: Vec, + pub(crate) code: Vec, } impl Debug for Method { @@ -338,7 +338,7 @@ impl Debug for Method { } impl Method { - pub fn new( + pub(crate) fn new( constant_pool: Rc>, access_flags: u16, name_index: u16, diff --git a/src/classloader/code_parser.rs b/src/classloader/code_parser.rs index 1622843..cefdb7d 100644 --- a/src/classloader/code_parser.rs +++ b/src/classloader/code_parser.rs @@ -1,15 +1,12 @@ -use std::collections::HashMap; +use std::collections::{BTreeMap}; -use log::debug; - -use crate::classloader::io::{read_i32, read_lookupswitch, read_tableswitch, read_u16, read_u8, read_wide_opcode}; +use crate::classloader::io::{read_i16, read_i32, read_lookupswitch, read_tableswitch, read_u16, read_u8, read_wide_opcode}; use crate::vm::opcodes::Opcode::{self, *}; pub(crate) fn parse_code(opcodes: &[u8]) -> Vec { - let mut code: HashMap = HashMap::new(); + let mut code: BTreeMap = BTreeMap::new(); let mut c = 0; let mut opcode_index: u16 = 0; - // debug!("len {:?}", opcodes.len()); while c < opcodes.len() { let opcode = get_opcode(opcodes, &mut c); code.insert(c as u16, (opcode_index, opcode)); @@ -21,13 +18,9 @@ pub(crate) fn parse_code(opcodes: &[u8]) -> Vec { code.into_iter().map(|(_, (_, opcode))| match opcode { IFNULL(goto) => { - debug!("goto {:?}", goto); - debug!("{:?}", code2); IFNULL(code2.get(&goto).unwrap().0) } IFNONNULL(goto) => { - debug!("goto {:?}", &goto); - debug!("{:?}", code2); IFNONNULL(code2.get(&goto).unwrap().0) } //TODO more jump instructions @@ -162,6 +155,8 @@ fn get_opcode(opcodes: &[u8], c: &mut usize) -> Opcode { 121 => LSHL, 122 => ISHR, 123 => LSHR, + 124 => IUSHR, + 125 => LUSHR, 126 => IAND, 127 => LAND, 128 => IOR, @@ -209,6 +204,7 @@ fn get_opcode(opcodes: &[u8], c: &mut usize) -> Opcode { 170 => TABLESWITCH(read_tableswitch(opcodes, c)), 171 => LOOKUPSWITCH(read_lookupswitch(opcodes, c)), 172 => IRETURN, + 173 => LRETURN, 174 => FRETURN, 175 => DRETURN, 176 => ARETURN, @@ -243,14 +239,12 @@ fn get_opcode(opcodes: &[u8], c: &mut usize) -> Opcode { 196 => WIDE(Box::new(read_wide_opcode(opcodes, c))), 197 => MULTIANEWARRAY(read_u16(opcodes, c), read_u8(opcodes, c)), 198 => { - let j = read_u16(opcodes, c); - debug!("ifnull {}",*c as u16 + j - 3); - IFNULL(*c as u16 + j - 3) + let j = read_i16(opcodes, c); + IFNULL((*c as i16 + j - 3) as u16) } 199 => { - let j = read_u16(opcodes, c); - debug!("ifnonnull {} ", *c as u16 + j - 3); - IFNONNULL(*c as u16 + j - 3) + let j = read_i16(opcodes, c); + IFNONNULL((*c as i16 + j - 3) as u16) } 200 => GOTOW(read_i32(opcodes, c)), 201 => JSR_W(read_i32(opcodes, c)), @@ -258,5 +252,6 @@ fn get_opcode(opcodes: &[u8], c: &mut usize) -> Opcode { _ => panic!("{}", opcode_u8), }; + opcode } \ No newline at end of file diff --git a/src/classloader/io.rs b/src/classloader/io.rs index 2c7092b..8891a62 100644 --- a/src/classloader/io.rs +++ b/src/classloader/io.rs @@ -17,7 +17,6 @@ pub const PATH_SEPARATOR: char = ';'; /// * [jar/zip]#[package_path]/[class].class /// * [dir]/[package_path]/[class].class pub fn find_class(classpath: &Vec, class_name: &str) -> Result { - let class_name = &class_name.to_owned().replace(".", "/"); if class_name.starts_with("java") || class_name.starts_with("sun/") || class_name.starts_with("com/sun/") @@ -73,6 +72,15 @@ pub(crate) fn read_u16(data: &[u8], pos: &mut usize) -> u16 { ) } +pub(crate) fn read_i16(data: &[u8], pos: &mut usize) -> i16 { + *pos += 2; + i16::from_be_bytes( + data[*pos - 2..*pos] + .try_into() + .expect("slice with incorrect length"), + ) +} + pub(crate) fn read_i32(data: &[u8], pos: &mut usize) -> i32 { *pos += 4; i32::from_be_bytes( @@ -119,8 +127,9 @@ pub(crate) fn read_f64(data: &[u8], pos: &mut usize) -> f64 { } pub(crate) fn read_tableswitch(data: &[u8], pos: &mut usize) -> Tableswitch { - while read_u8(data, pos) == 0 {} - *pos -= 1; + while *pos % 4 != 0 { + *pos += 1; + } let default = read_i32(data, pos); let low = read_i32(data, pos); let high = read_i32(data, pos); @@ -132,8 +141,9 @@ pub(crate) fn read_tableswitch(data: &[u8], pos: &mut usize) -> Tableswitch { } pub(crate) fn read_lookupswitch(data: &[u8], pos: &mut usize) -> Lookupswitch { - while read_u8(data, pos) == 0 {} - *pos -= 1; + while *pos % 4 != 0 { + *pos += 1; + } let default = read_i32(data, pos); let npairs = read_i32(data, pos); let mut match_offset_pairs = vec![]; diff --git a/src/classloader/mod.rs b/src/classloader/mod.rs index b34cd5b..c4902a8 100644 --- a/src/classloader/mod.rs +++ b/src/classloader/mod.rs @@ -232,6 +232,7 @@ fn read_method( ) -> Method { let access_flags = read_u16(bytecode, index); let name_index = read_u16(bytecode, index); + let name = constant_pool.get(&name_index); let descriptor_index = read_u16(bytecode, index); let attributes_count = read_u16(bytecode, index); diff --git a/src/classmanager.rs b/src/classmanager.rs index 871fb0e..9c9ce7e 100644 --- a/src/classmanager.rs +++ b/src/classmanager.rs @@ -99,11 +99,11 @@ impl ClassManager { if !PRIMITIVES.contains(&type_name.as_str()) { type_name = type_name[1..type_name.len()].to_owned(); } - let id = self.get_or_new_id(name); + let id = self.get_or_new_id(name.into()); if !self.class_objects.contains_key(&id) { let cls = self.get_class_by_name("java/lang/Class").unwrap(); let mut instance = Object::new(cls); - instance.set(cls, "java/lang/Class", "name", Value::Utf8(name.into())); + instance.set(cls, "java/lang/Class", "name", Utf8(name.into())); let instance = Ref(ObjectRef::Object(Rc::new(RefCell::new(instance)))); self.class_objects.insert(id, instance); @@ -135,6 +135,7 @@ impl ClassManager { } pub fn get_class_by_name(&self, name: &str) -> Option<&Class> { + debug!("{}", name); let id = self.names.get(name); self.classes.get(id.unwrap()) } @@ -252,18 +253,19 @@ impl ClassManager { } /// loads the class and returns it's dependencies - fn load_class_and_deps(&mut self, name: &str) -> (ClassId, Vec) { - debug!("load {}", name); - let id = self.get_or_new_id(name); + fn load_class_and_deps(&mut self, class_name: &str) -> (ClassId, Vec) { + debug!("load {}", class_name); + let class_name = class_name.to_owned().replace(".", "/"); + let id = self.get_or_new_id(class_name.clone()); let classdef = self.classdefs .entry(id) - .or_insert_with(|| classloader::get_classdef(&self.classpath, name).expect("ClassNotFound")); + .or_insert_with(|| classloader::get_classdef(&self.classpath, class_name.as_str()).expect("ClassNotFound")); (id, inspect_dependencies(classdef)) } - fn get_or_new_id(&mut self, name: &str) -> ClassId { - let id = *self.names.entry(name.to_string()).or_insert_with(|| { + fn get_or_new_id(&mut self, name: String) -> ClassId { + let id = *self.names.entry(name).or_insert_with(|| { self.current_id += 1; self.current_id }); diff --git a/src/main.rs b/src/main.rs index 7e5ab61..df0260b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,7 @@ use java_rs::vm::runtime::Vm; fn main() { - let vm = Vm::new(); - vm.run("/Users/Shautvast/dev/java/tests", "testclasses.Main", "main([Ljava/lang/String;)V"); + let mut vm = Vm::new(); + vm.run("/Users/Shautvast/dev/java.rs/tests", "testclasses/Main", "main([Ljava/lang/String;)V"); } diff --git a/src/vm/mod.rs b/src/vm/mod.rs index 63e6024..2128f3b 100644 --- a/src/vm/mod.rs +++ b/src/vm/mod.rs @@ -1,4 +1,5 @@ mod array; pub(crate) mod object; pub(crate) mod opcodes; -pub mod runtime; \ No newline at end of file +pub mod runtime; +mod native; \ No newline at end of file diff --git a/src/vm/native.rs b/src/vm/native.rs new file mode 100644 index 0000000..22eb6e7 --- /dev/null +++ b/src/vm/native.rs @@ -0,0 +1,161 @@ +#![allow(non_snake_case)] + +use std::cell::RefCell; +use std::rc::Rc; + +use anyhow::Error; +use log::debug; +use once_cell::sync::Lazy; +use crate::classmanager::ClassManager; +use crate::value::Value; +use crate::value::Value::{I32, Void}; +use crate::vm::object::ObjectRef; +use crate::vm::object::ObjectRef::Object; +use crate::vm::runtime::{Stackframe, Vm}; + + +pub fn invoke_native(class_manager: &mut ClassManager, class_name: &str, method_name: &str, _args: Vec) -> Result { + debug!("native {}.{}", class_name, method_name); + + match class_name { + "java/lang/Class" => java_lang_Class(method_name), + "java/lang/System" => java_lang_System(method_name), + "jdk/internal/misc/Unsafe" => jdk_internal_misc_Unsafe(method_name), + "jdk/internal/util/SystemProps$Raw" => jdk_internal_util_SystemProps_Raw(class_manager, method_name), + _ => unimplemented!("") + } +} + +fn java_lang_Class(method_name: &str) -> Result { + Ok(match method_name { + "desiredAssertionStatus0(Ljava/lang/Class;)Z" => Value::BOOL(false), + _ => Void + }) +} + +fn java_lang_System(method_name: &str) -> Result { + Ok(match method_name { + _ => Void + }) +} + +fn jdk_internal_misc_Unsafe(method_name: &str) -> Result { + Ok(match method_name { + "arrayBaseOffset0(Ljava/lang/Class;)I" => I32(0), //TODO surely this is not right + "arrayIndexScale0(Ljava/lang/Class;)I" => I32(0), //TODO surely this is not right + _ => Void + }) +} + +fn jdk_internal_util_SystemProps_Raw(class_manager: &mut ClassManager, method_name: &str) -> Result { + match method_name { + "platformProperties()[Ljava/lang/String;" => platformProperties(), + "cmdProperties()Ljava/util/HashMap;" => cmdProps(class_manager), //TODO ability to instantiate classes here + "vmProperties()[Ljava/lang/String;" => vmProperties(), + _ => Ok(Void) + } +} + +fn cmdProps(class_manager: &mut ClassManager) -> Result { + class_manager.load_class_by_name("java/util/HashMap"); + let hashmap_class = class_manager.get_class_by_name("java/util/HashMap").unwrap(); + let hashmap = Value::Ref(Object(Rc::new(RefCell::new(crate::vm::object::Object::new(hashmap_class))))); // this is convoluted + Stackframe::new(vec![hashmap.clone()]).run(class_manager, hashmap_class.id, "()V"); + Ok(hashmap) +} + +fn vmProperties() -> Result { + let props: Lazy> = Lazy::new(|| { + let vec: Vec = Vec::new(); + //TODO insert some values + vec + }); + Ok(Value::Ref(ObjectRef::StringArray(props.to_vec()))) +} + +fn platformProperties() -> Result { + let props: Lazy> = Lazy::new(|| { + let mut vec: Vec = Vec::new(); + //TODO set correct values + vec.push("display_country".into()); //null in jdk21 + vec.push("display_language".into()); //null in jdk21 + vec.push("display_script".into()); //null in jdk21 + vec.push("display_variant".into()); //null in jdk21 + vec.push("UTF-8".into()); + + { + #[cfg(target_family = "unix")] + vec.push("/".into()); + #[cfg(target_family = "windows")] + vec.push("\\"); + } + vec.push("format_country".into()); //null in jdk21 + vec.push("format_language".into()); //null in jdk21 + vec.push("format_script".into()); //null in jdk21 + vec.push("format_variant".into()); //null in jdk21 + vec.push("ftp_nonProxyHosts".into()); + if let Ok(ftp_proxy) = std::env::var("ftp_proxy") { + vec.push(ftp_proxy.to_owned());//TODO + vec.push(ftp_proxy); + } else { + vec.push("".to_owned()); + vec.push("".to_owned()); + } + + vec.push("http_nonProxyHosts".into()); + if let Ok(http_proxy) = std::env::var("http_proxy") { + vec.push(http_proxy.to_owned()); + vec.push(http_proxy);//TODO + } else { + vec.push("".to_owned()); + vec.push("".to_owned()); + } + if let Ok(https_proxy) = std::env::var("https_proxy") { + vec.push(https_proxy.to_owned()); + vec.push(https_proxy); + } else { + vec.push("".to_owned()); + vec.push("".to_owned()); + } + vec.push(std::env::temp_dir().display().to_string()); + + { + #[cfg(target_family = "unix")] + vec.push("\n".into()); + #[cfg(target_family = "windows")] + vec.push("\r\n"); + } + vec.push(whoami::platform().to_string()); + vec.push(whoami::devicename()); + vec.push("os_version".into()); + { + #[cfg(target_family = "unix")] + vec.push(":".into()); + #[cfg(target_family = "windows")] + vec.push(";".into()); + } + vec.push("socksNonProxyHosts".into()); + vec.push("socksProxyHost".into()); + vec.push("socksProxyPort".into()); + vec.push("UTF-8".into()); + vec.push("UTF-8".into()); + vec.push("sun_arch_abi".into()); + vec.push("sun_arch_data_model".into()); + vec.push("sun_cpu_endian".into()); //null in jdk21 + vec.push("sun_cpu_isalist".into()); //null in jdk21 + vec.push("sun_io_unicode_encoding".into()); //null in jdk21 + vec.push("sun_jnu_encoding".into()); //null in jdk21 + vec.push("sun_os_patch_level".into()); //null in jdk21 + if let Ok(curdir) = std::env::current_dir() { + vec.push(curdir.display().to_string()); + } + + let home = std::env::home_dir().unwrap(); + vec.push(home.display().to_string()); + vec.push(whoami::username()); + vec.push("FIXED_LENGTH".into()); + + vec + }); + Ok(Value::Ref(ObjectRef::StringArray(props.to_vec()))) +} \ No newline at end of file diff --git a/src/vm/opcodes.rs b/src/vm/opcodes.rs index bd9b759..ffc1d10 100644 --- a/src/vm/opcodes.rs +++ b/src/vm/opcodes.rs @@ -85,6 +85,8 @@ pub(crate) enum Opcode { LSHL, ISHR, LSHR, + IUSHR, + LUSHR, IAND, LAND, IOR, @@ -134,6 +136,7 @@ pub(crate) enum Opcode { TABLESWITCH(Tableswitch), LOOKUPSWITCH(Lookupswitch), IRETURN, + LRETURN, FRETURN, DRETURN, ARETURN, diff --git a/src/vm/runtime.rs b/src/vm/runtime.rs index 639fcd4..46b7b7a 100644 --- a/src/vm/runtime.rs +++ b/src/vm/runtime.rs @@ -6,7 +6,7 @@ use anyhow::Error; use log::debug; use crate::class::ClassId; -use crate::classloader::classdef::CpEntry; +use crate::classloader::classdef::{CpEntry, Modifier}; use crate::classloader::io::PATH_SEPARATOR; use crate::classmanager::ClassManager; use crate::value::Value::{self, *}; @@ -17,6 +17,8 @@ use crate::vm::object::ObjectRef::Object; use crate::vm::opcodes::Opcode; use crate::vm::opcodes::Opcode::*; use std::io::Write; +use crate::vm::native::invoke_native; + pub struct Vm { pub stack: Vec, } @@ -33,23 +35,26 @@ impl Vm { } } - pub fn run(self, classpath: &str, class_name: &str, method_name: &str) { - + pub fn run(mut self, classpath: &str, class_name: &str, method_name: &str) { let classpath = classpath.split(PATH_SEPARATOR).map(|s| s.into()) .collect(); let mut class_manager = ClassManager::new(classpath); class_manager.load_class_by_name("java/lang/Class"); + class_manager.load_class_by_name("java/lang/System"); class_manager.load_class_by_name("java/lang/String"); class_manager.load_class_by_name("java/util/Collections"); + class_manager.load_class_by_name(class_name); - let class_id = *class_manager.get_classid(class_name); - self.run2(&mut class_manager, class_id, method_name); + let system_id = *class_manager.get_classid("java/lang/System"); + self.run2(&mut class_manager, system_id, "initPhase1()V"); + // let class_id = *class_manager.get_classid(class_name); + // self.run2(&mut class_manager, class_id, method_name); } - pub(crate) fn run2(self, class_manager: &mut ClassManager, class_id: ClassId, method_name: &str) { - Stackframe::new().run(class_manager, class_id, method_name); + pub(crate) fn run2(&mut self, class_manager: &mut ClassManager, class_id: ClassId, method_name: &str) { + Stackframe::default().run(class_manager, class_id, method_name); } } @@ -60,10 +65,10 @@ pub struct Stackframe { } impl Stackframe { - pub fn new() -> Self { + pub fn new(args: Vec) -> Self { Self { pc: 0, - locals: vec![], + locals: args, stack: vec![], } } @@ -84,15 +89,16 @@ impl Stackframe { self.stack.pop().unwrap() } - pub fn run(&mut self, class_manager: &mut ClassManager, class_id: ClassId, method_name: &str) { + pub fn run(&mut self, class_manager: &mut ClassManager, class_id: ClassId, method_name: &str) -> Value { let classname = class_manager.get_class_by_id(&class_id).unwrap().name.to_owned(); + let code = class_manager.get_classdef(&class_id).get_method(method_name).unwrap().code.clone(); let constant_pool = class_manager.get_classdef(&class_id).get_method(method_name).unwrap().constant_pool.clone(); let len = code.len(); while self.pc < len { let opcode: &Opcode = code.get(self.pc).unwrap(); - debug!("\tat {}.{}: {} #{:?} {:?}", classname, method_name, self.pc, opcode, self.stack); + debug!("\tat {}.{}: {} #{:?} - {:?}", classname, method_name, self.pc, opcode, self.stack); match opcode { NOP => {} ACONST_NULL => { @@ -173,11 +179,22 @@ impl Stackframe { if let Some(invocation) = get_signature_for_invoke(&constant_pool, *c) { - // debug!("invoke {:?}", invocation); + let mut args = Vec::with_capacity(invocation.method.num_args); + for _ in 0..invocation.method.num_args { + args.insert(0, self.pop().clone()); + } + let this_ref = self.pop(); + args.insert(0, this_ref.clone()); + + + debug!("invoke {:?}", invocation); let mut invoke_class: Option = None; - if let Ref(this) = &self.locals[0] { + if let Null = this_ref { + panic!("NullPointer Exception"); + } + if let Ref(this) = this_ref { if let Object(this) = this { - let invoke_classdef = class_manager.get_classdef(&class_id); + let invoke_classdef = class_manager.get_classdef(&this.borrow().class_id); let invoke_method = invoke_classdef.get_method(&invocation.method.name); if invoke_method.is_some() { class_manager.load_class_by_name(&invocation.class_name); @@ -204,20 +221,21 @@ impl Stackframe { } } if invoke_class.is_none() { - panic!(); + panic!("method {:?}.{} not found", invocation.class_name, invocation.method.name); } - let mut args = Vec::with_capacity(invocation.method.num_args); - for _ in 0..invocation.method.num_args { - args.insert(0, self.pop().clone()); - } - args.insert(0, self.pop()); - let mut new_stackframe = Stackframe { - pc: 0, - locals: args, - stack: vec![], - }; - new_stackframe.run(class_manager, invoke_class.unwrap(), &invocation.method.name); + let return_value = + if class_manager.get_classdef(&invoke_class.unwrap()).get_method(&invocation.method.name).unwrap().is(Modifier::Native) { + invoke_native(class_manager, invocation.class_name.as_str(), invocation.method.name.as_str(), args).unwrap() + // TODO remove unwrap in line above, error handling + } else { + let mut new_stackframe = Stackframe::new(args); + new_stackframe.run(class_manager, invoke_class.unwrap(), &invocation.method.name) + }; + match return_value { + Void => {} + _ => self.push(return_value) + } } else { unreachable!() } @@ -226,17 +244,31 @@ impl Stackframe { if let Some(invocation) = get_signature_for_invoke(&constant_pool, *c) { + debug!("invoke {:?}", invocation); let mut args = Vec::with_capacity(invocation.method.num_args); for _ in 0..invocation.method.num_args { args.insert(0, self.pop().clone()); } - let mut new_stackframe = Stackframe { - pc: 0, - locals: args, - stack: vec![], - }; + if let INVOKESPECIAL(_) = opcode { + args.insert(0, self.pop()); + } + + class_manager.load_class_by_name(invocation.class_name.as_str()); let invoke_class = class_manager.get_classid(invocation.class_name.as_str()); - new_stackframe.run(class_manager, *invoke_class, &invocation.method.name); + + let return_value = + if class_manager.get_classdef(&invoke_class).get_method(&invocation.method.name).unwrap().is(Modifier::Native) { + invoke_native(class_manager, invocation.class_name.as_str(), invocation.method.name.as_str(), args).unwrap() + // TODO remove unwrap in line above, error handling + } else { + let mut new_stackframe = Stackframe::new(args); + new_stackframe.run(class_manager, *invoke_class, &invocation.method.name) + }; + debug!("returning {:?}", return_value); + match return_value { + Void => {} + _ => self.push(return_value) + } } else { unreachable!() } @@ -369,9 +401,22 @@ impl Stackframe { _ => if let IFNONNULL(goto) = opcode { self.pc = *goto as usize; } }; } + DUP => { + let value = self.pop(); + self.push(value.clone()); + self.push(value.clone()); + } + IRETURN | LRETURN | FRETURN | DRETURN | ARETURN => { + return self.pop(); + } + RETURN_VOID => { + return Void; + } _ => { panic!("opcode not implemented") } } + self.pc += 1; } + Void } fn store(