diff --git a/src/bytecode.rs b/src/bytecode.rs index b3dcbb5..da85e64 100644 --- a/src/bytecode.rs +++ b/src/bytecode.rs @@ -119,6 +119,7 @@ impl Bytecode { 0x80 => (Instruction::OrInt(), 1), 0x82 => (Instruction::XorInt(), 1), + 0x83 => (Instruction::XorLong(), 1), 0x84 => (Instruction::IncrementLocalInt(self.bytes[offset+1], i8::from_be_bytes([self.bytes[offset+2]])), 3), 0x85 => (Instruction::ConvertIntToLong(), 1), @@ -416,6 +417,7 @@ pub enum Instruction { OrInt() = 0x80, // value, value => or XorInt() = 0x82, // value, value => xor + XorLong() = 0x83, // value, value => xor IncrementLocalInt(u8, i8) = 0x84, // increment local variable by constant i8 ConvertIntToLong() = 0x85, // convert int on stack to long diff --git a/src/classfile.rs b/src/classfile.rs index 7889a29..5d0015a 100644 --- a/src/classfile.rs +++ b/src/classfile.rs @@ -6,9 +6,8 @@ use core::str::Utf8Error; use crate::accessmasks::*; use crate::bytecode::Bytecode; use crate::classstore::ClassStore; -use crate::constantpool::{ ConstantFieldRefInfo, ConstantPoolInfo, ConstantUtf8Info, ConstantInterfaceMethodRefInfo, ConstantStringInfo, ConstantMethodRefInfo, ConstantFloatInfo, ConstantClassInfo, ConstantNameAndTypeInfo, ConstantIntegerInfo, ConstantLongInfo }; +use crate::constantpool::*; use crate::constantpool::ConstantDoubleInfo; -use crate::heap_area::ObjectReference; #[derive(Debug)] pub enum Error { @@ -240,6 +239,17 @@ impl JavaClassFile { return Ok(methodref_entry); } + pub fn pool_methodhandle_entry(&self, index: u16) -> Result<&ConstantMethodHandleInfo, Error> { + let pool_entry = self.pool_entry(index)?; + + let methodhandle_entry = match pool_entry { + ConstantPoolInfo::MethodHandle(data) => data, + _ => unreachable!(), + }; + + return Ok(methodhandle_entry); + } + pub fn pool_fieldref_entry(&self, index: u16) -> Result<&ConstantFieldRefInfo, Error> { let pool_entry = self.pool_entry(index)?; @@ -429,6 +439,7 @@ impl JavaClassFile { Ok(false) } + } #[derive(Debug)] @@ -702,9 +713,60 @@ impl NestMembersAttributeData { } } +#[derive(Debug)] +pub struct BootstrapMethodEntry { + bootstrap_method_ref: u16, + bootstrap_arguments: Box<[u16]> +} + +impl BootstrapMethodEntry { + fn from_reader(reader: &mut dyn Read) -> Result { + Ok( + BootstrapMethodEntry { + bootstrap_method_ref: read_u16(reader)?, + bootstrap_arguments: { + let length = read_u16(reader)?; + let mut v = Vec::with_capacity(length.into()); + + for _i in 0..length { + v.push(read_u16(reader)?); + } + + v.into_boxed_slice() + } + } + ) + } +} + +#[derive(Debug)] +pub struct BootstrapMethodsData { + bootstrap_methods: Box<[BootstrapMethodEntry]> +} + +impl BootstrapMethodsData { + fn from_reader(reader: &mut dyn Read) -> Result { + Ok( + BootstrapMethodsData { + bootstrap_methods: { + let length = read_u16(reader)?; + let mut v = Vec::with_capacity(length.into()); + + for _i in 0..length { + v.push(BootstrapMethodEntry::from_reader(reader)?); + } + + v.into_boxed_slice() + }, + } + ) + } +} + #[derive(Debug)] pub enum AttributeData { Code(CodeAttributeData), + BootstrapMethods(BootstrapMethodsData), Signature(SignatureAttributeData), Synthetic(), Exceptions(ExceptionAttributeData), @@ -755,6 +817,10 @@ impl AttributeInfo { } ), + "BootstrapMethods" => AttributeData::BootstrapMethods( + BootstrapMethodsData::from_reader(reader)?, + ), + "LineNumberTable" => AttributeData::LineNumberTable( LineNumberTableAttributeData::from_reader(reader)? ), diff --git a/src/classstore.rs b/src/classstore.rs index 3f37845..1d9e68c 100644 --- a/src/classstore.rs +++ b/src/classstore.rs @@ -16,7 +16,7 @@ pub struct ClassStore { pub array_classes: HashMap, pub classes: Vec, pub class_path_fragments: Vec, - pub native_class_names: Vec, + pub native_class_descriptors: Vec, pub primitive_classes: PrimitiveClassStore, } @@ -83,7 +83,7 @@ impl ClassStore { array_classes: HashMap::new(), classes: Vec::new(), class_path_fragments: vec![current_dir_path], - native_class_names: Vec::new(), + native_class_descriptors: Vec::new(), primitive_classes: PrimitiveClassStore::default(), } } @@ -106,12 +106,12 @@ impl ClassStore { } pub fn add_native_class_descriptor(&mut self, name: String) -> usize { - self.native_class_names.push(name); - return self.native_class_names.len() - 1; + self.native_class_descriptors.push(name); + return self.native_class_descriptors.len() - 1; } pub fn get_native_class_name(&self, index: usize) -> &String { - return &self.native_class_names[index]; + return &self.native_class_descriptors[index]; } pub fn load_class_from_file(&mut self, class_file_path: &PathBuf) -> Result { diff --git a/src/heap_area.rs b/src/heap_area.rs index 69e5b99..84df7fa 100644 --- a/src/heap_area.rs +++ b/src/heap_area.rs @@ -223,7 +223,7 @@ impl ObjectArea { fn make_array(&mut self, class_store: &ClassStore, elements: Box<[ObjectReference]>) -> (ObjectReference, usize) { assert!(elements.len() != 0); let array_element_class_name = self.get_reference_native_class_name(elements[0], class_store); - let array_element_type_desc = AbstractTypeDescription::parse_first(array_element_class_name).unwrap().1; + let array_element_type_desc = AbstractTypeDescription::parse_full(array_element_class_name).unwrap(); let (array_object_ref, array_size) = self.make_empty_array(class_store, array_element_type_desc, elements.len()); @@ -255,6 +255,23 @@ impl ObjectArea { return (object_ref, object_size); } + pub fn class_ref_get_class_index(&self, class_ref: ObjectReference, class_store: &ClassStore) -> usize { + let class_data_ref = match self.get_object_field(class_ref, "classData", self.get_object_class_index(class_ref), class_store).unwrap() { + FieldValue::Reference(r) => r, + _ => unreachable!(), + }; + let native_name_index = match self.get_object_field(class_data_ref, "native_class_descriptor_index", self.get_object_class_index(class_data_ref), class_store).unwrap() { + FieldValue::Int(i) => i, + _ => unreachable!(), + }; + + let native_class_name = class_store.get_native_class_name(native_name_index as usize); + + let native_class_index = class_store.class_index_for_type(AbstractTypeDescription::parse_full(native_class_name).unwrap()).unwrap(); + + return native_class_index; + } + pub fn get_class_ref_native_class_name<'a>(&self, class_ref: ObjectReference, class_store: &'a ClassStore) -> &'a String { let class_data_ref = match self.get_object_field(class_ref, "classData", self.get_object_class_index(class_ref), class_store).unwrap() { FieldValue::Reference(r) => r, diff --git a/src/jvm.rs b/src/jvm.rs index 0ead479..53a6e1a 100644 --- a/src/jvm.rs +++ b/src/jvm.rs @@ -1471,6 +1471,10 @@ impl JVM { wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(instruction_result)))?; } + Instruction::InvokeDynamic(invokedynamic_index, _zero) => { + let methodhandle_reference = class.pool_methodhandle_entry(invokedynamic_index)?; + } + Instruction::InvokeInterface(methodref_index) => { let (supplied_interface_name, supplied_method_name, supplied_descriptor_string) = class.gather_interfacemethodref(methodref_index)?; @@ -2600,6 +2604,13 @@ impl JVM { wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(value_1 ^ value_2)))?; } + Instruction::XorLong() => { + let value_1 = wrap_stackframe_error(class, method, frame.operand_stack.pop_long(0))?; + let value_2 = wrap_stackframe_error(class, method, frame.operand_stack.pop_long(0))?; + + wrap_stackframe_error(class, method, frame.operand_stack.push_long(value_1 ^ value_2))?; + } + _ => { return Err(Error::RunTimeError(format!("Opcode not implemented yet: {:?}", instruction))) }, diff --git a/src/main.rs b/src/main.rs index ce02395..40b6c3d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,9 +25,9 @@ fn main() { jvm.class_store.class_path_fragments.push(PathBuf::from("./classpath")); match jvm.entrypoint( - &"Main".to_string(), + &"SiedepunktNamen".to_string(), &"main".to_string(), - &["Hello World"], + &["5"], ) { Ok(()) => (), Err(e) => println!("{:#?}", e), diff --git a/src/native_methods.rs b/src/native_methods.rs index 099665b..7a4b933 100644 --- a/src/native_methods.rs +++ b/src/native_methods.rs @@ -1,3 +1,7 @@ +use std::time::SystemTime; +use std::time::UNIX_EPOCH; + +use crate::accessmasks::FieldAccessFlag; use crate::classfile::FieldInfo; use crate::classstore::ClassStore; use crate::stackframe::StackFrame; @@ -58,6 +62,59 @@ impl JavaIOFileDescriptor { struct JavaLangClass {} impl JavaLangClass { + fn get_declared_fields_0(jvm: &mut JVM) -> Result { + let frame = { + let thread = &mut jvm.threads[jvm.active_thread]; + let frame_index = thread.stack_frames.len() - 1; + &mut thread.stack_frames[frame_index] + }; + let this = frame.load_local_reference(0).unwrap(); + let public_only = frame.load_local_boolean_compatible(1).unwrap(); + let class_index = jvm.heap_area.object_area.class_ref_get_class_index(this, &jvm.class_store); + + if ! jvm.class_store.have_class(&"java/lang/reflect/Field".to_string()) { + return Ok(JVMCallbackOperation::LoadClass("java/lang/reflect/Field".to_string())); + } + if ! jvm.class_store.was_init(&"java/lang/reflect/Field".to_string()).unwrap() { + return Ok(JVMCallbackOperation::InitClass("java/lang/reflect/Field".to_string())); + } + if ! jvm.class_store.have_class(&"[Ljava/lang/reflect/Field;".to_string()) { + let description = AbstractTypeDescription { + array_level: 0, + kind: AbstractTypeKind::Classname("java/lang/reflect/Field".to_string()), + }; + return Ok(JVMCallbackOperation::MakeArrayClass(this, description)); + } + let field_class_index = jvm.class_store.class_idx_from_name(&"java/lang/reflect/Field".to_string()).unwrap(); + + let fields = ClassFieldIterator::new(class_index, &jvm.class_store) + .filter(|field_info| if public_only { field_info.access_flags & FieldAccessFlag::Public } else { true } ) + .map(|field_info| { + let field = jvm.heap_area.make_object(&jvm.class_store, field_class_index); + let java_string = jvm.heap_area.make_handmade_string(&field_info.name, &jvm.class_store); + jvm.heap_area.object_area.set_object_field( + field, + "name", + java_string.into(), + field_class_index, + &jvm.class_store + ).unwrap(); + jvm.heap_area.object_area.set_object_field( + field, + "clazz", + this.into(), + field_class_index, + &jvm.class_store + ).unwrap(); + + field + }) + .collect(); + + let field_array = jvm.heap_area.make_array(&jvm.class_store, fields); + + Ok(JVMCallbackOperation::ReturnFrame(field_array.into())) + } fn is_array(jvm: &mut JVM) -> Result { let frame = { @@ -500,6 +557,12 @@ impl JavaLangStringUTF16 { struct JavaLangSystem {} impl JavaLangSystem { + fn nano_time(_: &mut JVM) -> Result { + let nanos = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_nanos() as i64; + + Ok(JVMCallbackOperation::ReturnFrame(FieldValue::Long(nanos))) + } + fn register_natives(jvm: &mut JVM) -> Result { // Load class because it won't be loaded otherwise let frame = { @@ -849,7 +912,18 @@ impl JdkInternalMiscUnsafe { Ok(JVMCallbackOperation::ReturnFrame(FieldValue::Boolean(success))) } else { - todo!() + let object_class_descriptor = jvm.heap_area.object_area.get_reference_native_class_name(object, &jvm.class_store); + let object_class_index = jvm.class_store.class_index_for_type(AbstractTypeDescription::parse_full(object_class_descriptor).unwrap()).unwrap(); + + let field = JdkInternalMiscUnsafe::class_field_at_offset(object_class_index, &jvm.class_store, offset)?; + + let current_value = jvm.heap_area.object_area.get_object_field(object, &field.name, object_class_index, &jvm.class_store)?; + let function_result = current_value == expected.into(); + if function_result { + jvm.heap_area.object_area.set_object_field(object, &field.name, replacement.into(), object_class_index, &jvm.class_store)?; + } + + Ok(JVMCallbackOperation::ReturnFrame(FieldValue::Boolean(function_result))) } } @@ -1003,6 +1077,46 @@ impl JdkInternalMiscUnsafe { Ok(JVMCallbackOperation::ReturnFrame(FieldValue::Boolean(function_result))) } + pub fn object_field_offset_0(jvm: &mut JVM) -> Result { + let frame = { + let thread = &mut jvm.threads[jvm.active_thread]; + let frame_index = thread.stack_frames.len() - 1; + &mut thread.stack_frames[frame_index] + }; + let field_object_reference = frame.load_local_reference(1).unwrap(); + let field_class_index = jvm.heap_area.object_area.get_reference_class_index(field_object_reference, &jvm.class_store); + + let class = jvm.heap_area.object_area.get_object_field( + field_object_reference, + "clazz", + field_class_index, + &jvm.class_store + )?.expect_reference(); + let field_name = jvm.heap_area.object_area.get_object_field( + field_object_reference, + "name", + field_class_index, + &jvm.class_store + )?.expect_reference(); + + let byte_offset = JdkInternalMiscUnsafe::object_field_offset_2(jvm, class, field_name)?; + + Ok(JVMCallbackOperation::ReturnFrame(FieldValue::Long(byte_offset))) + } + + fn object_field_offset_2(jvm: &mut JVM, class_object_reference: ObjectReference, field_name_string_reference: ObjectReference) -> Result { + let class_descriptor = jvm.heap_area.object_area.get_class_ref_native_class_name(class_object_reference, &jvm.class_store); + let class_index = jvm.class_store.class_index_for_type(AbstractTypeDescription::parse_full(class_descriptor).unwrap()).unwrap(); + let rust_field_name_string = jvm.heap_area.decode_java_string(field_name_string_reference, &jvm.class_store); + + let byte_offset: i64 = ClassFieldIterator::new(class_index as usize, &jvm.class_store) + .take_while(|f| f.name != rust_field_name_string) + .map(|f| f.descriptor.storage_size() as i64) + .sum(); + + Ok(byte_offset) + } + pub fn object_field_offset_1(jvm: &mut JVM) -> Result { // args: Class class, String fieldName let frame = { @@ -1012,16 +1126,9 @@ impl JdkInternalMiscUnsafe { }; let class_object_reference = frame.load_local_reference(1).unwrap(); - let class_descriptor = jvm.heap_area.object_area.get_class_ref_native_class_name(class_object_reference, &jvm.class_store); - let class_index = jvm.class_store.class_index_for_type(AbstractTypeDescription::parse_full(class_descriptor).unwrap()).unwrap(); let field_name_string_reference = frame.load_local_reference(2).unwrap(); - let rust_field_name_string = jvm.heap_area.decode_java_string(field_name_string_reference, &jvm.class_store); - - let byte_offset: i64 = ClassFieldIterator::new(class_index as usize, &jvm.class_store) - .take_while(|f| f.name != rust_field_name_string) - .map(|f| f.descriptor.storage_size() as i64) - .sum(); + let byte_offset = JdkInternalMiscUnsafe::object_field_offset_2(jvm, class_object_reference, field_name_string_reference)?; Ok(JVMCallbackOperation::ReturnFrame(FieldValue::Long(byte_offset))) } @@ -1257,7 +1364,7 @@ pub fn function_for(class_name: &str, m: &crate::classfile::MethodInfo) -> Resul ]), return_type: AbstractTypeDescription { array_level: 1, kind: AbstractTypeKind::Classname("java/lang/reflect/Field".to_string()) }, }, - todo_call + JavaLangClass::get_declared_fields_0 ), ( @@ -1784,7 +1891,7 @@ pub fn function_for(class_name: &str, m: &crate::classfile::MethodInfo) -> Resul ]), return_type: AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Long()}, }, - todo_call + JavaLangSystem::nano_time ), ( @@ -2543,7 +2650,7 @@ pub fn function_for(class_name: &str, m: &crate::classfile::MethodInfo) -> Resul ]), return_type: AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Long()}, }, - todo_call + JdkInternalMiscUnsafe::object_field_offset_0 ), ( diff --git a/src/stackframe.rs b/src/stackframe.rs index 652680c..5a230be 100644 --- a/src/stackframe.rs +++ b/src/stackframe.rs @@ -430,6 +430,23 @@ impl StackFrame { } } + pub fn load_local_boolean_compatible(&self, index: u16) -> Result { + let local = self.locals[index as usize]; + match local { + StackValue::Boolean(b) => Ok(b), + StackValue::Int(i) => Ok(i != 0), + _ => Err(Error::LocalError(format!("Mismatched type at index {} of the function locals, expected Boolean but found '{:?}'", index, local))) + } + } + + pub fn load_local_boolean(&self, index: u16) -> Result { + let local = self.locals[index as usize]; + match local { + StackValue::Boolean(b) => Ok(b), + _ => Err(Error::LocalError(format!("Mismatched type at index {} of the function locals, expected Boolean but found '{:?}'", index, local))) + } + } + pub fn load_local_int(&self, index: u16) -> Result { let local = self.locals[index as usize]; match local {