From 14d09dfa30c6e286e81b95d0c66e9b007adceca2 Mon Sep 17 00:00:00 2001 From: VegOwOtenks Date: Fri, 2 May 2025 09:36:23 +0200 Subject: [PATCH] feat: new instructions --- src/bytecode.rs | 2 + src/classfile.rs | 23 +++++- src/constantpool.rs | 8 +- src/jvm.rs | 183 +++++++++++++++++++++++++++----------------- 4 files changed, 139 insertions(+), 77 deletions(-) diff --git a/src/bytecode.rs b/src/bytecode.rs index da85e64..9d32f39 100644 --- a/src/bytecode.rs +++ b/src/bytecode.rs @@ -271,6 +271,7 @@ impl Bytecode { 0xC1 => (Instruction::InstanceOf((self.bytes[offset+1] as u16) << 8 | self.bytes[offset+2] as u16), 3), 0xC2 => (Instruction::EnterMonitor(), 1), 0xC3 => (Instruction::ExitMonitor(), 1), + 0xC5 => (Instruction::MultiNewArray((self.bytes[offset+1] as u16) << 8 | self.bytes[offset+2] as u16, self.bytes[offset+3]), 4), 0xC6 => { let bytes = [self.bytes[offset+1], self.bytes[offset+2]]; @@ -481,6 +482,7 @@ pub enum Instruction { InstanceOf(u16) = 0xC1, // push integer result for success EnterMonitor() = 0xC2, // enter the synchronization monitor of an object ExitMonitor() = 0xC3, // exit the synchronization monitor of an object + MultiNewArray(u16, u8) = 0xC4, // multidimensional new array BranchNull(i16) = 0xC6, // branch if Null BranchNonNull(i16) = 0xC7, // branch if Null diff --git a/src/classfile.rs b/src/classfile.rs index 5d0015a..a0fc8cb 100644 --- a/src/classfile.rs +++ b/src/classfile.rs @@ -213,6 +213,12 @@ impl JavaClassFile { return None; } + pub fn find_bootstrapmethods_attribute(&self) -> Option<&BootstrapMethodsData> { + self.attributes.iter() + .filter_map(|attr| match attr.data { AttributeData::BootstrapMethods(ref data) => Some(data), _ => None } ) + .next() + } + pub fn pool_entry(&self, index: u16) -> Result<&ConstantPoolInfo, Error> { return pool_entry(&self.constant_pool, index); } @@ -261,6 +267,17 @@ impl JavaClassFile { return Ok(fieldref_entry); } + pub fn pool_invokedynamic_entry(&self, index: u16) -> Result<&ConstantInvokeDynamicInfo, Error> { + let pool_entry = self.pool_entry(index)?; + + let fieldref_entry = match pool_entry { + ConstantPoolInfo::InvokeDynamic(data) => data, + _ => unreachable!(), + }; + + return Ok(fieldref_entry); + } + pub fn pool_class_entry(&self, index: u16) -> Result<&ConstantClassInfo, Error> { let pool_entry = self.pool_entry(index)?; @@ -715,8 +732,8 @@ impl NestMembersAttributeData { #[derive(Debug)] pub struct BootstrapMethodEntry { - bootstrap_method_ref: u16, - bootstrap_arguments: Box<[u16]> + pub bootstrap_method_ref: u16, + pub bootstrap_arguments: Box<[u16]> } impl BootstrapMethodEntry { @@ -741,7 +758,7 @@ impl BootstrapMethodEntry { #[derive(Debug)] pub struct BootstrapMethodsData { - bootstrap_methods: Box<[BootstrapMethodEntry]> + pub bootstrap_methods: Box<[BootstrapMethodEntry]> } impl BootstrapMethodsData { diff --git a/src/constantpool.rs b/src/constantpool.rs index 1e8e66a..37d5656 100644 --- a/src/constantpool.rs +++ b/src/constantpool.rs @@ -99,8 +99,8 @@ impl TryFrom for ConstantMethodHandleType { #[derive(Debug, Copy, Clone)] pub struct ConstantMethodHandleInfo { - reference_kind: ConstantMethodHandleType, - reference_index: u16, + pub reference_kind: ConstantMethodHandleType, + pub reference_index: u16, } #[derive(Debug, Copy, Clone)] @@ -110,8 +110,8 @@ pub struct ConstantMethodTypeInfo { #[derive(Debug, Copy, Clone)] pub struct ConstantInvokeDynamicInfo { - bootstrap_method_attr_index: u16, - name_and_type_index: u16, + pub bootstrap_method_attr_index: u16, + pub name_and_type_index: u16, } diff --git a/src/jvm.rs b/src/jvm.rs index 53a6e1a..24170eb 100644 --- a/src/jvm.rs +++ b/src/jvm.rs @@ -11,7 +11,7 @@ use crate::classfile; use crate::classfile::{ JavaClassFile, FieldInfo, MethodInfo, MethodDescriptor, AbstractTypeDescription, AbstractTypeKind, AttributeInfo, AttributeData, CodeAttributeData, ConstantValueAttributeData }; use crate::classstore; use crate::classstore::ClassStore; -use crate::constantpool::{ ConstantClassInfo, ConstantInterfaceMethodRefInfo, ConstantMethodRefInfo, ConstantNameAndTypeInfo, ConstantPoolInfo, ConstantUtf8Info}; +use crate::constantpool::{ ConstantClassInfo, ConstantInterfaceMethodRefInfo, ConstantMethodHandleType, ConstantMethodRefInfo, ConstantNameAndTypeInfo, ConstantPoolInfo, ConstantUtf8Info}; use crate::heap_area::{ HeapArea, FieldValue, ObjectReference, CompartmentEntry }; use crate::iterators::{ ClassFieldIterator, ClassMethodIterator, CompatibleTypesIterator }; use crate::native_methods; @@ -1224,7 +1224,7 @@ impl JVM { let long = match double { f64::INFINITY => i64::MAX, f64::NEG_INFINITY => i64::MIN, - v @ _ => if v.is_nan() { 0 } else { v as i64 } + v => if v.is_nan() { 0 } else { v as i64 } }; wrap_stackframe_error(class, method, frame.operand_stack.push_long(long))?; @@ -1472,7 +1472,24 @@ impl JVM { } Instruction::InvokeDynamic(invokedynamic_index, _zero) => { - let methodhandle_reference = class.pool_methodhandle_entry(invokedynamic_index)?; + let invokedynamic_info = class.pool_invokedynamic_entry(invokedynamic_index)?; + let bootstrap_table = class.find_bootstrapmethods_attribute().unwrap(); + let (method_name, method_descriptor) = class.gather_nameandtype(invokedynamic_info.name_and_type_index).unwrap(); + let bootstrap_entry = &bootstrap_table.bootstrap_methods[invokedynamic_info.bootstrap_method_attr_index as usize]; + + let method_handle = class.pool_methodhandle_entry(bootstrap_entry.bootstrap_method_ref).unwrap(); + + match method_handle.reference_kind { + ConstantMethodHandleType::RefGetField(_) => todo!(), + ConstantMethodHandleType::RefGetStatic(_) => todo!(), + ConstantMethodHandleType::RefPutField(_) => todo!(), + ConstantMethodHandleType::RefPutStatic(_) => todo!(), + ConstantMethodHandleType::RefInvokeVirtual(_) => todo!(), + ConstantMethodHandleType::RefInvokeStatic(_) => return self.perform_invokestatic(offset, method_handle.reference_index), + ConstantMethodHandleType::RefInvokeSpecial(_) => todo!(), + ConstantMethodHandleType::RefNewInvokeSpecial(_) => todo!(), + ConstantMethodHandleType::RefInvokeInterface(_) => todo!(), + } } Instruction::InvokeInterface(methodref_index) => { @@ -1587,71 +1604,8 @@ impl JVM { }, Instruction::InvokeStatic(methodref_index) => { - let (supplied_class_name, supplied_method_name, supplied_descriptor_string) = class.gather_methodref_compatible(methodref_index)?; - - if ! self.class_store.have_class(supplied_class_name) { - // rewind the bytecode offset, I'll need to execute this instruction again - frame.instruction_pointer -= offset as u32; - - return Ok(JVMCallbackOperation::LoadClass(supplied_class_name.to_string())); - } - if ! self.class_store.was_init(supplied_class_name).unwrap() { - // rewind the bytecode offset, I'll need to execute this instruction again - frame.instruction_pointer -= offset as u32; - - return Ok(JVMCallbackOperation::InitClass(supplied_class_name.to_string())); - } - - let supplied_descriptor: MethodDescriptor = supplied_descriptor_string.try_into()?; - // TODO: Throw exception on fail - - let (callee_class_file, callee_class_index) = self.class_store.get_class(supplied_class_name)?; - let (callee_class_index, callee_method_index, callee_method_info) = match ClassMethodIterator::new(callee_class_index, &self.class_store) - .filter(|(_, _, minfo)| minfo.name == *supplied_method_name) - .filter(|(_, _, minfo)| minfo.descriptor == supplied_descriptor) - .next() { - Some(m) => m, - None => { - // TODO: Throw exception - return Err(Error::RunTimeError(format!("InvokeStatic: Failed to find requested method '{}' with descriptor '{}' in the class '{}'", supplied_method_name, supplied_descriptor_string, supplied_class_name))); - } - }; - - if ! (callee_method_info.access_flags & MethodAccessFlag::Static) { - // TODO: Throw IncompatibleClassChangeError - return Err(Error::RunTimeError(format!( - "Invoked method '{}' in class '{}' does not have Access::Static (from invokestatic from '{}' in class '{}')", - method.name, - class.get_classname().unwrap(), - supplied_method_name, - supplied_class_name, - ))); - } - - - if supplied_descriptor != callee_method_info.descriptor { - // TODO: Throw exception on fail - return Err(Error::RunTimeError(format!( - "Mismatched method descriptors between caller and callee: Caller ({}) wanted '{}' but found '{}' on Callee ({})", - class.get_classname().unwrap(), - supplied_descriptor_string, - callee_method_info.descriptor.source_string(), - supplied_class_name, - ))); - } - - let mut arguments = VecDeque::new(); - fill_arguments(class, method, &mut arguments, &callee_method_info.descriptor.argument_types, &mut frame.operand_stack)?; - - // TODO: Throw errors on abstract methods etc. - let new_frame = StackFrame::new( - callee_class_file, - callee_class_index, - callee_method_index as u16, - &arguments.make_contiguous(), - ); - - return Ok(JVMCallbackOperation::PushFrame(new_frame)); + let result = self.perform_invokestatic(offset, methodref_index); + return result; }, Instruction::InvokeVirtual(methodref_index) => { @@ -2011,6 +1965,20 @@ impl JVM { frame.instruction_pointer = if jump_offset < 0 { frame.instruction_pointer - jump_offset.abs() as u32} else { frame.instruction_pointer + jump_offset.abs() as u32}; } + Instruction::MultiNewArray(type_index, dimension_count) => { + let dimension_count = dimension_count as usize; + let mut dimension_sizes = vec![0; dimension_count].into_boxed_slice(); + for i in 0..dimension_count { + let dimension_size = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?; + dimension_sizes[dimension_count - i - 1] = dimension_size; + // top-most dimension is at the front + } + + self.heap_area.make_multi_array(dimension_sizes); + + unreachable!(); + } + Instruction::MultiplyFloat() => { let factor_1 = wrap_stackframe_error(class, method, frame.operand_stack.pop_float(0))?; let factor_2 = wrap_stackframe_error(class, method, frame.operand_stack.pop_float(0))?; @@ -2249,7 +2217,7 @@ impl JVM { StackValue::Reference(r) => FieldValue::Reference(r), StackValue::Float(f) => FieldValue::Float(f), StackValue::Byte(b) => FieldValue::Byte(b), - stack_value @ _ => { + stack_value => { println!("{stack_value:?}"); todo!() } @@ -2626,6 +2594,81 @@ impl JVM { return &mut self.threads[self.active_thread]; } + fn perform_invokestatic(&mut self, offset: usize, reference_index: u16) -> Result { + let frame = { + let thread = &mut self.threads[self.active_thread]; + let frame_index = thread.stack_frames.len() - 1; + &mut thread.stack_frames[frame_index] + }; + let class = self.class_store.class_file_from_idx(frame.class_index).unwrap(); + let method = & class.methods[frame.method_index as usize]; + let (supplied_class_name, supplied_method_name, supplied_descriptor_string) = class.gather_methodref_compatible(reference_index)?; + + if ! self.class_store.have_class(supplied_class_name) { + // rewind the bytecode offset, I'll need to execute this instruction again + frame.instruction_pointer -= offset as u32; + + return Ok(JVMCallbackOperation::LoadClass(supplied_class_name.to_string())); + } + if ! self.class_store.was_init(supplied_class_name).unwrap() { + // rewind the bytecode offset, I'll need to execute this instruction again + frame.instruction_pointer -= offset as u32; + + return Ok(JVMCallbackOperation::InitClass(supplied_class_name.to_string())); + } + + let supplied_descriptor: MethodDescriptor = supplied_descriptor_string.try_into()?; + // TODO: Throw exception on fail + + let (callee_class_file, callee_class_index) = self.class_store.get_class(supplied_class_name)?; + let (callee_class_index, callee_method_index, callee_method_info) = match ClassMethodIterator::new(callee_class_index, &self.class_store) + .filter(|(_, _, minfo)| minfo.name == *supplied_method_name) + .filter(|(_, _, minfo)| minfo.descriptor == supplied_descriptor) + .next() { + Some(m) => m, + None => { + // TODO: Throw exception + return Err(Error::RunTimeError(format!("InvokeStatic: Failed to find requested method '{}' with descriptor '{}' in the class '{}'", supplied_method_name, supplied_descriptor_string, supplied_class_name))); + } + }; + + if ! (callee_method_info.access_flags & MethodAccessFlag::Static) { + // TODO: Throw IncompatibleClassChangeError + return Err(Error::RunTimeError(format!( + "Invoked method '{}' in class '{}' does not have Access::Static (from invokestatic from '{}' in class '{}')", + method.name, + class.get_classname().unwrap(), + supplied_method_name, + supplied_class_name, + ))); + } + + + if supplied_descriptor != callee_method_info.descriptor { + // TODO: Throw exception on fail + return Err(Error::RunTimeError(format!( + "Mismatched method descriptors between caller and callee: Caller ({}) wanted '{}' but found '{}' on Callee ({})", + class.get_classname().unwrap(), + supplied_descriptor_string, + callee_method_info.descriptor.source_string(), + supplied_class_name, + ))); + } + + let mut arguments = VecDeque::new(); + fill_arguments(class, method, &mut arguments, &callee_method_info.descriptor.argument_types, &mut frame.operand_stack)?; + + // TODO: Throw errors on abstract methods etc. + let new_frame = StackFrame::new( + callee_class_file, + callee_class_index, + callee_method_index as u16, + &arguments.make_contiguous(), + ); + + return Ok(JVMCallbackOperation::PushFrame(new_frame)); + } + } pub enum JVMCallbackOperation { @@ -2763,7 +2806,7 @@ fn fill_arguments(class: &JavaClassFile, method: &MethodInfo, arguments: &mut Ve ) ); }, - AbstractTypeKind::Classname(ref name) => { + AbstractTypeKind::Classname(ref _name) => { // TODO: Type checking arguments.push_front( StackValue::Reference(