diff --git a/jmods/java.base.jar b/jmods/java.base.jar new file mode 100644 index 0000000..528778a Binary files /dev/null and b/jmods/java.base.jar differ diff --git a/src/class.rs b/src/class.rs index 4ce188e..22503f9 100644 --- a/src/class.rs +++ b/src/class.rs @@ -4,16 +4,19 @@ use std::fmt; use std::rc::Rc; use std::sync::Arc; -use anyhow::{anyhow, Error}; +use anyhow::Error; +use log::info; use once_cell::sync::Lazy; -use crate::classloader::{load_class, CpEntry}; -use crate::heap::ObjectRef; +use crate::classloader::{CpEntry, load_class}; +use crate::heap::{ObjectRef}; use crate::io::{find_class, read_bytecode, read_u16}; use crate::vm::Vm; //trying to be ready for multithreaded as much as possible, using Arc's and all, but it will still require (a lot of) extra work -static mut CLASSDEFS: Lazy>>> = Lazy::new(|| HashMap::new()); //TODO add mutex.. +pub static mut CLASSDEFS: Lazy>>> = Lazy::new(|| HashMap::new()); +//TODO add mutex.. +pub static mut CLASSES: Lazy> = Lazy::new(|| HashMap::new()); //TODO add mutex.. // gets the Class from cache, or reads it from classpath, // then parses the binary data into a Class struct @@ -22,7 +25,7 @@ pub fn get_class( vm: &mut Vm, class_name: &str, ) -> Result>, Error> { - // println!("get_class {}", class_name); + info!("get_class {}", class_name); unsafe { let class = CLASSDEFS.entry(class_name.into()).or_insert_with(|| { @@ -33,32 +36,49 @@ pub fn get_class( Arc::new(RefCell::new(class)) }); + let clone = class.clone(); let inited = class.borrow().inited; if !inited { // not sure why I have to create the clones first let clone2 = class.clone(); let clone3 = class.clone(); + let mut some_class = class.clone(); + + if class_name != "java/lang/Class" { + let klazz = get_class(vm, "java/lang/Class")?; + let mut class_instance = Vm::new_instance(klazz); + class_instance.set(&"java/lang/Class".to_owned(), &"name".to_owned(), unsafe_val(Value::Utf8(class_name.into()))); + CLASSES.insert(class_name.into(), unsafe_val(Value::Ref(unsafe_ref(ObjectRef::Object(Box::new(class_instance)))))); + } // must not enter here twice! clone2.borrow_mut().inited = true; - let super_class_name = class - .clone() - .borrow() - .super_class_name - .as_ref() - .map(|n| n.to_owned()); - { - if let Some(super_class_name) = super_class_name { - if let Ok(super_class) = get_class(vm, &super_class_name) { - clone2.borrow_mut().super_class = Some(super_class); - } else { - unreachable!() + let mut supers = vec![]; + if class_name != "java/lang/Class" { + loop { + let super_class_name = some_class + .clone() + .borrow() + .super_class_name + .as_ref() + .map(|n| n.to_owned()); + { + if let Some(super_class_name) = super_class_name { + if let Ok(super_class) = get_class(vm, &super_class_name) { + supers.push(super_class.clone()); + some_class = super_class.clone(); + clone2.borrow_mut().super_class = Some(super_class); + } else { + break; + } + } else { + break; + } } } } - Class::initialize_fields(clone3); let clinit = clone2.borrow().methods.contains_key("()V"); let name = &clone2.borrow().name.to_owned(); @@ -81,6 +101,7 @@ pub struct Class { pub name: String, pub super_class_name: Option, pub super_class: Option, + pub super_classes: Vec, pub interface_indices: Vec, pub interfaces: Vec, pub fields: HashMap, @@ -118,6 +139,7 @@ impl Class { name, super_class_name, super_class: None, // has to be instantiated later, because it involves classloading. maybe not store it here + super_classes: vec![], interface_indices, interfaces: vec![], // same fields, @@ -237,10 +259,8 @@ impl Class { (self.major_version, self.minor_version) } - pub fn get_method(&self, name: &str) -> Result<&Rc, Error> { - self.methods - .get(name) - .ok_or(anyhow!("Method {} not found", name)) + pub fn get_method(&self, name: &str) -> Option<&Rc> { + self.methods.get(name) } fn class_name( diff --git a/src/classloader.rs b/src/classloader.rs index 6c46ef4..19de269 100644 --- a/src/classloader.rs +++ b/src/classloader.rs @@ -12,12 +12,10 @@ pub fn load_class(bytecode: Vec) -> Result { let major_version = read_u16(&bytecode, pos); let constant_pool_count = read_u16(&bytecode, pos); - // println!("cp count: {}", constant_pool_count); let mut constant_pool: HashMap = HashMap::with_capacity(constant_pool_count as usize); let mut cp_index = 1; while cp_index < constant_pool_count { - // println!("cp#{}", cp_index); constant_pool.insert( cp_index, read_constant_pool_entry(&mut cp_index, pos, &bytecode), @@ -275,6 +273,7 @@ fn read_attribute( "InnerClasses" => Some(("".into(), AttributeType::InnerClasses)), //stub "Signature" => Some(("".into(), AttributeType::Signature)), //stub "NestHost" => Some(("".into(), AttributeType::NestHost)), //stub + "EnclosingMethod" => Some(("".into(), AttributeType::EnclosingMethod)), //stub //TODO more actual attribute implementations _ => None, }; diff --git a/src/heap.rs b/src/heap.rs index f390134..4674255 100644 --- a/src/heap.rs +++ b/src/heap.rs @@ -1,10 +1,11 @@ use std::cell::{RefCell, UnsafeCell}; use std::fmt; -use std::fmt::{Debug, Formatter}; +use std::fmt::{Debug, Formatter, write}; use std::sync::Arc; +use ObjectRef::{BooleanArray, CharArray, DoubleArray, FloatArray, LongArray, ShortArray}; use crate::class::{Class, Type, UnsafeValue, Value}; -use crate::heap::ObjectRef::{ByteArray, IntArray, ObjectArray}; +use crate::heap::ObjectRef::{ByteArray, IntArray, ObjectArray, StringArray}; // can contain object or array pub enum ObjectRef { @@ -16,8 +17,10 @@ pub enum ObjectRef { DoubleArray(Vec), BooleanArray(Vec), CharArray(Vec), + StringArray(Vec), ObjectArray(Type, Vec>>), Object(Box), + Class(Arc>) } fn into_vec_i8(v: Vec) -> Vec { @@ -54,16 +57,18 @@ impl ObjectRef { impl Debug for ObjectRef { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { - ObjectRef::BooleanArray(d) => write!(f, "[Z;{}]", d.len()), - ObjectRef::ByteArray(d) => write!(f, "[B;{}]", d.len()), - ObjectRef::CharArray(d) => write!(f, "[C;{}]", d.len()), - ObjectRef::DoubleArray(d) => write!(f, "[D;{}]", d.len()), - ObjectRef::FloatArray(d) => write!(f, "[F;{}]", d.len()), - ObjectRef::IntArray(d) => write!(f, "[I;{}]", d.len()), - ObjectRef::LongArray(d) => write!(f, "[J;{}]", d.len()), - ObjectRef::ObjectArray(t, d) => write!(f, "[L{};{}]", t.borrow().name, d.len()), - ObjectRef::ShortArray(d) => write!(f, "[S;{}]", d.len()), + BooleanArray(d) => write!(f, "[Z;{}]", d.len()), + ByteArray(d) => write!(f, "[B;{}]", d.len()), + CharArray(d) => write!(f, "[C;{}]", d.len()), + DoubleArray(d) => write!(f, "[D;{}]", d.len()), + FloatArray(d) => write!(f, "[F;{}]", d.len()), + IntArray(d) => write!(f, "[I;{}]", d.len()), + LongArray(d) => write!(f, "[J;{}]", d.len()), + ObjectArray(t, d) => write!(f, "[L{};{}]", t.borrow().name, d.len()), + ShortArray(d) => write!(f, "[S;{}]", d.len()), + StringArray(d) => write!(f, "[S;{}]", d.len()), ObjectRef::Object(r) => write!(f, "{}{{ {:?} }}", r.class.borrow().name, r.data), + ObjectRef::Class(s) => write!(f, "Class {:?}", s.borrow().name), } } } diff --git a/src/main.rs b/src/main.rs index 78eecd3..7cfdd92 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,7 +9,7 @@ fn main() -> Result<(), Error> { // let main_class = "Inheritance"; let main_class = "testclasses.Main"; - vm.execute( main_class, "main([Ljava/lang/String;)V", vec![]) + vm.execute_static( main_class, "main([Ljava/lang/String;)V", vec![]) .unwrap(); Ok(()) } diff --git a/src/native.rs b/src/native.rs index 8223cb0..03981c9 100644 --- a/src/native.rs +++ b/src/native.rs @@ -1,7 +1,90 @@ -use crate::class::{Method, UnsafeValue, Value}; -use std::rc::Rc; +#![allow(non_snake_case)] -pub fn invoke_native(method: Rc, _args: Vec) -> UnsafeValue { - println!("native {}", method.name()); - Value::void() +use log::info; +use once_cell::sync::Lazy; + +use crate::class::{unsafe_ref, unsafe_val, UnsafeValue, Value}; +use crate::class::Value::Void; +use crate::heap::ObjectRef; + +pub fn invoke_native(class_name: &String, method_name: &String, _args: Vec) -> UnsafeValue { + info!("native {}.{}", class_name, method_name); + + unsafe_val(match class_name.as_str() { + "java/lang/Class" => java_lang_class(method_name), + "jdk/internal/util/SystemProps$Raw" => jdk_internal_util_SystemProps_Raw(method_name), + _ => Void + }) } + +fn java_lang_class(method_name: &String) -> Value { + match method_name.as_str() { + "desiredAssertionStatus0(Ljava/lang/Class;)Z" => Value::BOOL(false), + _ => Void + } +} + +fn jdk_internal_util_SystemProps_Raw(method_name: &String) -> Value { + match method_name.as_str() { + "platformProperties()[Ljava/lang/String;" => systemProps(), + "cmdProperties()Ljava/util/HashMap;" => cmdProps(), //TODO ability to instantiate classes here + "vmProperties()[Ljava/lang/String;" => cmdProps(), + _ => Void + } +} + +fn cmdProps() -> Value { + Value::Null +} + +fn systemProps() -> Value { + unsafe { + let props: Lazy> = Lazy::new(|| { + let mut vec = Vec::new(); + //TODO set values + vec.push("display_country"); + vec.push("display_language"); + vec.push("display_script"); + vec.push("display_variant"); + vec.push("file_encoding"); + vec.push("file_separator"); + vec.push("format_country"); + vec.push("format_language"); + vec.push("format_script"); + vec.push("format_variant"); + vec.push("ftp_nonProxyHosts"); + vec.push("ftp_proxyHost"); + vec.push("ftp_proxyPort"); + vec.push("http_nonProxyHosts"); + vec.push("http_proxyHost"); + vec.push("http_proxyPort"); + vec.push("https_proxyHost"); + vec.push("https_proxyPort"); + vec.push("java_io_tmpdir"); + vec.push("line_separator"); + vec.push("os_arch"); + vec.push("os_name"); + vec.push("os_version"); + vec.push("path_separator"); + vec.push("socksNonProxyHosts"); + vec.push("socksProxyHost"); + vec.push("socksProxyPort"); + vec.push("stderr_encoding"); + vec.push("stdout_encoding"); + vec.push("sun_arch_abi"); + vec.push("sun_arch_data_model"); + vec.push("sun_cpu_endian"); + vec.push("sun_cpu_isalist"); + vec.push("sun_io_unicode_encoding"); + vec.push("sun_jnu_encoding"); + vec.push("sun_os_patch_level"); + vec.push("user_dir"); + vec.push("user_home"); + vec.push("user_name"); + vec.push("FIXED_LENGTH"); + + vec.into_iter().map(|s| s.to_owned()).collect() + }); + Value::Ref(unsafe_ref(ObjectRef::StringArray(props.to_vec()))) + } +} \ No newline at end of file diff --git a/src/vm.rs b/src/vm.rs index f0002e4..d3c1f65 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -4,12 +4,12 @@ use std::rc::Rc; use std::sync::Arc; use std::io::Write; use anyhow::{anyhow, Error}; -use log::{info}; +use log::{debug, info}; use Value::*; use crate::class::Value::{Null, Void}; -use crate::class::{get_class, unsafe_ref, unsafe_val, AttributeType, Class, Modifier, UnsafeValue, Value, Method}; +use crate::class::{get_class, unsafe_ref, unsafe_val, AttributeType, Class, Modifier, UnsafeValue, Value, Method, CLASSES}; use crate::classloader::CpEntry; use crate::heap::{Heap, Object, ObjectRef}; use crate::io::*; @@ -39,7 +39,9 @@ impl StackFrame { fn push_ref(&mut self, val: UnsafeValue) { self.data.push(val); } - + fn len(&self) -> usize { + self.data.len() + } fn pop(&mut self) -> Result { Ok(self.data.pop().unwrap()) } @@ -51,6 +53,16 @@ pub struct Vm { pub(crate) stackframes: Vec, } +impl Vm { + fn init(vm: &mut Vm) { + Self::initialize_class(vm, "java/lang/System"); + } + + fn initialize_class(vm: &mut Vm, class: &str) { + vm.execute_static(class, "initPhase1()V", vec![]).expect("cannot create VM"); + } +} + #[cfg(target_family = "unix")] const PATH_SEPARATOR: char = ':'; @@ -72,14 +84,18 @@ impl Vm { writeln!(buf, "{}: {}", record.level(), record.args()) }) .init(); - Self { + let mut vm_instance = Self { classpath: classpath .split(PATH_SEPARATOR) .map(|s| s.to_owned()) .collect(), heap: Heap::new(), stackframes: vec![], - } + }; + + Vm::init(&mut vm_instance); + + vm_instance } pub fn new_instance(class: Arc>) -> Object { @@ -88,26 +104,55 @@ impl Vm { /// execute the bytecode /// contains unsafe, as I think that mimics not-synchronized memory access in the original JVM - pub fn execute( + pub fn execute_virtual( &mut self, class_name: &str, method_name: &str, args: Vec, ) -> Result { - let class = get_class(self, class_name)?; - // let method = class.clone().borrow().get_method(method_name)?.clone(); - let classb = class.borrow(); + unsafe { + for a in &args { + let v = &*a.get(); + if let Ref(r) = v { + info!("arg {:?}",&*r.get()); + } else { + info!("arg {:?}",&*a.get()); + } + } - let method = Self::get_method(&classb, method_name, &args).unwrap(); - // let mut superclass = class.super_class.as_ref(); - // while let Some(s) = superclass { - // if let Ok(m) = s.borrow().get_method(method_name) { - // return m; - // } - // superclass = s.borrow().super_class.as_ref(); - // } - self.execute_class(class.clone(), method.clone(), args) + if let Null = &*args[0].get() { + panic!("NPE"); + } + if let Ref(this) = &*args[0].get() { + if let ObjectRef::Object(this) = &*this.get() { + let class = &this.class; + let borrow = class.borrow(); + let method = borrow.get_method(method_name); + if let Some(method) = method { + return self.execute_class(class.clone(), method.clone(), args); + } else { + for s in &borrow.super_classes { + let borrow2 = s.borrow(); + let method = borrow2.get_method(method_name); + if let Some(method) = method { + return self.execute_class(class.clone(), method.clone(), args); + } else { + debug!("not {:?}", s); + } + } + debug!("not found {}", method_name); + } + } else if let ObjectRef::Class(class) = &*this.get() { + let klazz = get_class(self, "java/lang/Class")?; + let borrow = klazz.borrow(); + let method = borrow.get_method(method_name).unwrap(); + return self.execute_class(class.clone(), method.clone(), args); + } + } + } + println!("this is not an object reference {}", class_name); + panic!(); } pub fn execute_special( @@ -117,23 +162,10 @@ impl Vm { args: Vec, ) -> Result { let class = get_class(self, class_name)?; - let method = class.clone().borrow().get_method(method_name)?.clone(); + let method = class.clone().borrow().get_method(method_name).expect("execute special needs invoked method on the class").clone(); self.execute_class(class.clone(), method.clone(), args) } - fn get_method<'a>(class: &'a std::cell::Ref, method_name: &str, args: &Vec) -> Option<&'a Rc> { - unsafe { - if let Ref(this) = &*args[0].get() { - if let ObjectRef::Object(this) = &*this.get() { - if let Ok(m) = class.get_method(method_name) { - return Some(m); - } - } - } - } - None - } - pub fn execute_static( &mut self, class_name: &str, @@ -141,7 +173,7 @@ impl Vm { args: Vec, ) -> Result { let class = get_class(self, class_name)?; - let method = class.clone().borrow().get_method(method_name)?.clone(); + let method = class.clone().borrow().get_method(method_name).expect("execute static needs this method").clone(); self.execute_class(class, method, args) } @@ -152,14 +184,14 @@ impl Vm { args: Vec, ) -> Result { let this_class = class; - println!("execute {}.{}", this_class.borrow().name, method.name()); + info!("execute {}.{}", this_class.borrow().name, method.name()); //TODO implement dynamic dispatch -> get method from instance let mut local_params: Vec> = args.clone().iter().map(|e| Some(e.clone())).collect(); if method.is(Modifier::Native) { - return Ok(invoke_native(method, args)); + return Ok(invoke_native(&this_class.borrow().name, &method.name(), args)); } if let AttributeType::Code(code) = method.attributes.get("Code").unwrap() { let stackframe = StackFrame::new(&this_class.borrow().name, &method.name()); @@ -168,8 +200,8 @@ impl Vm { let pc = &mut 0; while *pc < code.opcodes.len() { let opcode = read_u8(&code.opcodes, pc); - let f = self.current_frame(); - info!("\t{} #{} {}", &f.at, &*pc - 1, opcodes::OPCODES[opcode as usize]); + let cur_frame = self.current_frame(); + info!("\t{} #{} {} - {}", &cur_frame.at, &*pc - 1, opcodes::OPCODES[opcode as usize], cur_frame.len()); match opcode { ACONST_NULL => { self.current_frame().push(Value::Null); @@ -270,8 +302,14 @@ impl Vm { self.current_frame().push(Value::I64(*l)); } CpEntry::ClassRef(utf8) => { - let string = this_class.borrow().cp_utf8(utf8).unwrap().to_owned(); - self.current_frame().push(Value::Utf8(string)); + let class_name = this_class.borrow().cp_utf8(utf8).unwrap().to_owned(); + unsafe { + if let Some(class) = CLASSES.get(&class_name) { + self.current_frame().push_ref(class.clone()); + } else { + unreachable!("should not be here"); + } + } } _ => { panic!("add variant {:?}", c) @@ -280,14 +318,21 @@ impl Vm { } LDC_W => { let cp_index = read_u16(&code.opcodes, pc); - match method.constant_pool.get(&cp_index).unwrap() { + let cp_entry = method.constant_pool.get(&cp_index).unwrap(); + match cp_entry { CpEntry::Integer(i) => { self.current_frame().push(I32(*i)); } CpEntry::Float(f) => { - self.current_frame().push(Value::F32(*f)); + self.current_frame().push(F32(*f)); + } + CpEntry::StringRef(utf8_index) => { + if let CpEntry::Utf8(s) = method.constant_pool.get(utf8_index).unwrap() { + self.current_frame().push(Value::Utf8(s.to_owned())); + } } _ => { + println!("{:?}", cp_entry); unreachable!() } } @@ -512,6 +557,13 @@ impl Vm { &invocation.method.name, args, )?; + if let Ref(r) = &*return_value.get() { + if let ObjectRef::Object(p) = &*r.get() { + info!("return {:?}", p); + } + } else { + info!("return {:?}", &*return_value.get()); + } match *return_value.get() { Void => {} _ => { @@ -534,11 +586,18 @@ impl Vm { args.insert(0, copy(self.current_frame().pop()?)); } args.insert(0, self.current_frame().pop()?); - let return_value = self.execute( + let return_value = self.execute_virtual( &invocation.class_name, &invocation.method.name, args, )?; + if let Ref(r) = &*return_value.get() { + if let ObjectRef::Object(p) = &*r.get() { + info!("return {:?}", p); + } + } else { + info!("return {:?}", &*return_value.get()); + } match *return_value.get() { Void => {} _ => { @@ -559,15 +618,22 @@ impl Vm { for _ in 0..invocation.method.num_args { args.insert(0, copy(self.current_frame().pop()?)); } - let returnvalue = self.execute_static( + let return_value = self.execute_static( &invocation.class_name, &invocation.method.name, args, )?; - match *returnvalue.get() { + if let Ref(r) = &*return_value.get() { + if let ObjectRef::Object(p) = &*r.get() { + info!("return {:?}", p); + } + } else { + info!("return {:?}", &*return_value.get()); + } + match *return_value.get() { Void => {} _ => { - self.current_frame().push_ref(returnvalue.clone()); + self.current_frame().push_ref(return_value.clone()); } } } else { @@ -689,16 +755,22 @@ impl Vm { self.current_frame().push(CHAR(array[index])); } ObjectRef::LongArray(array) => { - self.current_frame().push(Value::I64(array[index])); + self.current_frame().push(I64(array[index])); } ObjectRef::FloatArray(array) => { - self.current_frame().push(Value::F32(array[index])); + self.current_frame().push(F32(array[index])); } ObjectRef::DoubleArray(array) => { - self.current_frame().push(Value::F64(array[index])); + self.current_frame().push(F64(array[index])); } ObjectRef::ObjectArray(_arraytype, data) => { - self.current_frame().push(Value::Ref(data[index].clone())); + self.current_frame().push(Ref(data[index].clone())); + } + ObjectRef::StringArray(array) =>{ + self.current_frame().push(Utf8(array[index].to_owned())); + } + ObjectRef::Class(_) => { + panic!("should be array") } ObjectRef::Object(_) => { panic!("should be array") @@ -786,7 +858,14 @@ impl Vm { unreachable!() } } - ObjectRef::Object(_) => {} //throw error? + ObjectRef::StringArray(ref mut array) => { + if let Utf8(ref value) = *value.get() { + array[*index as usize] = value.clone(); + } else { + unreachable!() + } + } + ObjectRef::Object(_) | ObjectRef::Class(_) => {} //throw error? } } } else { diff --git a/tests/class_tests.rs b/tests/class_tests.rs index 6f723b8..f057528 100644 --- a/tests/class_tests.rs +++ b/tests/class_tests.rs @@ -1,11 +1,12 @@ mod test { - use java_rs::class::{get_class, Value}; + use java_rs::class::Value; use java_rs::heap::ObjectRef; use java_rs::vm::Vm; + #[test] fn if_cmp() { let mut vm = Vm::new("tests"); - let ret = vm.execute("testclasses.IfCmp", "i_is_1()Z", vec![]).unwrap(); + let ret = vm.execute_virtual("testclasses.IfCmp", "i_is_1()Z", vec![]).unwrap(); unsafe { if let Value::I32(b) = *ret.get() { // internally a boolean is an int