diff --git a/src/bytecode.rs b/src/bytecode.rs index 5fb1582..5082bf2 100644 --- a/src/bytecode.rs +++ b/src/bytecode.rs @@ -207,6 +207,7 @@ impl Bytecode { 0xB6 => (Instruction::InvokeVirtual((self.bytes[offset+1] as u16) << 8 | self.bytes[offset+2] as u16), 3), 0xB7 => (Instruction::InvokeSpecial((self.bytes[offset+1] as u16) << 8 | self.bytes[offset+2] as u16), 3), 0xB8 => (Instruction::InvokeStatic((self.bytes[offset+1] as u16) << 8 | self.bytes[offset+2] as u16), 3), + 0xB9 => (Instruction::InvokeInterface((self.bytes[offset+1] as u16) << 8 | self.bytes[offset+2] as u16), 3), 0xBA => (Instruction::InvokeDynamic((self.bytes[offset+1] as u16) << 8 | self.bytes[offset+2] as u16, (self.bytes[offset+3] as u16) << 8 | self.bytes[offset+4] as u16), 5), 0xBB => (Instruction::NewObject((self.bytes[offset+1] as u16) << 8 | self.bytes[offset+2] as u16), 3), 0xBC => (Instruction::NewPrimitiveArray(self.bytes[offset+1]), 2), @@ -363,6 +364,7 @@ pub enum Instruction { InvokeVirtual(u16) = 0xB6, // invoke function on a class InvokeSpecial(u16) = 0xB7, // invoke instance method InvokeStatic(u16) = 0xB8, // invoke static function + InvokeInterface(u16) = 0xB9, // invoke interface function InvokeDynamic(u16, u16) = 0xBA, // invoke dynamic function NewObject(u16) = 0xBB, // Create a new object from a constant-pool class reference NewPrimitiveArray(u8) = 0xBC, // make a primitive array diff --git a/src/classfile.rs b/src/classfile.rs index 296f9b0..a2dd4b6 100644 --- a/src/classfile.rs +++ b/src/classfile.rs @@ -5,7 +5,7 @@ use core::str::Utf8Error; use crate::accessmasks::*; use crate::bytecode::Bytecode; -use crate::constantpool::{ ConstantFieldRefInfo, ConstantPoolInfo, ConstantUtf8Info, ConstantStringInfo, ConstantMethodRefInfo, ConstantFloatInfo, ConstantClassInfo, ConstantNameAndTypeInfo, ConstantIntegerInfo, ConstantLongInfo }; +use crate::constantpool::{ ConstantFieldRefInfo, ConstantPoolInfo, ConstantUtf8Info, ConstantInterfaceMethodRefInfo, ConstantStringInfo, ConstantMethodRefInfo, ConstantFloatInfo, ConstantClassInfo, ConstantNameAndTypeInfo, ConstantIntegerInfo, ConstantLongInfo }; #[derive(Debug)] pub enum Error { @@ -206,6 +206,17 @@ impl JavaClassFile { return pool_entry(&self.constant_pool, index); } + pub fn pool_interfacemethodref_entry(&self, index: u16) -> Result<&ConstantInterfaceMethodRefInfo, Error> { + let pool_entry = self.pool_entry(index)?; + + let methodref_entry = match pool_entry { + ConstantPoolInfo::InterfaceMethodRef(data) => data, + _ => unreachable!(), + }; + + return Ok(methodref_entry); + } + pub fn pool_methodref_entry(&self, index: u16) -> Result<&ConstantMethodRefInfo, Error> { let pool_entry = self.pool_entry(index)?; @@ -329,6 +340,19 @@ impl JavaClassFile { Ok(class_name) } + pub fn gather_interfacemethodref(&self, index: u16) -> Result<(&String, &String, &String), Error> { + let methodref = self.pool_interfacemethodref_entry(index)?; + let class_entry = self.pool_class_entry(methodref.class_index)?; + let class_name_entry = self.pool_utf8_entry(class_entry.name_index)?; + let name_and_type_entry = self.pool_nameandtype_entry(methodref.name_and_type_index)?; + + let class_name = &class_name_entry.utf8; + let method_name = &self.pool_utf8_entry(name_and_type_entry.name_index)?.utf8; + let method_descriptor = &self.pool_utf8_entry(name_and_type_entry.descriptor_index)?.utf8; + + return Ok((class_name, method_name, method_descriptor)); + } + pub fn gather_methodref(&self, index: u16) -> Result<(&String, &String, &String), Error> { let methodref = self.pool_methodref_entry(index)?; let class_entry = self.pool_class_entry(methodref.class_index)?; @@ -886,6 +910,13 @@ impl AbstractTypeDescription { return Ok((offset, AbstractTypeDescription { array_level, kind })) } + + pub fn for_class_name(name: &str) -> Self { + AbstractTypeDescription { + array_level: 0, + kind: AbstractTypeKind::Classname(name.to_string()), + } + } } #[derive(Debug, Eq, PartialEq)] diff --git a/src/heap_area.rs b/src/heap_area.rs index f8da3a7..cc2785e 100644 --- a/src/heap_area.rs +++ b/src/heap_area.rs @@ -256,6 +256,7 @@ impl ObjectArea { match self.get_entry(reference) { CompartmentEntry::Object(o) => class_store.get_class_objectref_from_index(o.class_index), CompartmentEntry::ReferenceArray(a) => a.class_ref, + CompartmentEntry::ByteArray(a) => a.class_ref, _ => unreachable!(), } } diff --git a/src/jvm.rs b/src/jvm.rs index 03f8a18..bb3f02f 100644 --- a/src/jvm.rs +++ b/src/jvm.rs @@ -841,16 +841,22 @@ impl JVM { Instruction::CheckCast(classref_index) => { // TODO: Class loading checks let class_name = class.gather_class(classref_index)?; - let class_index = self.class_store.class_idx_from_name(class_name).unwrap(); - let class_object_ref = self.class_store.get_class_objectref_from_index(class_index); - let native_class_description = self.heap_area.object_area.get_class_ref_native_class_name(class_object_ref, &self.class_store); - let class_description = AbstractTypeDescription::parse_full(native_class_description)?; - let object = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?; let native_class_name = self.heap_area.object_area.get_reference_native_class_name(object, &self.class_store); - let object_description = AbstractTypeDescription::parse_full(native_class_name)?; - if ! self.class_store.are_types_compatible(&object_description, &class_description) { + let instruction_result = if class_name == native_class_name { + 1 + } else { + let class_index = self.class_store.class_idx_from_name(class_name).unwrap(); + let class_object_ref = self.class_store.get_class_objectref_from_index(class_index); + let native_class_description = self.heap_area.object_area.get_class_ref_native_class_name(class_object_ref, &self.class_store); + let class_description = AbstractTypeDescription::parse_full(native_class_description)?; + + let object_description = AbstractTypeDescription::parse_full(native_class_name)?; + if self.class_store.are_types_compatible(&object_description, &class_description) { 1 } else { 0 } + }; + + if instruction_result == 0 { // TODO: Throw Exception return Err(Error::RunTimeError(format!("Trying to cast an object of type {native_class_name} to {class_name}"))) } @@ -979,20 +985,81 @@ impl JVM { Instruction::InstanceOf(classref_index) => { // TODO: Class loading checks let class_name = class.gather_class(classref_index)?; - let class_index = self.class_store.class_idx_from_name(class_name).unwrap(); - let class_object_ref = self.class_store.get_class_objectref_from_index(class_index); - let native_class_description = self.heap_area.object_area.get_class_ref_native_class_name(class_object_ref, &self.class_store); - let class_description = AbstractTypeDescription::parse_full(native_class_description)?; - let object = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?; let native_class_name = self.heap_area.object_area.get_reference_native_class_name(object, &self.class_store); - let object_description = AbstractTypeDescription::parse_full(native_class_name)?; - let instruction_result = if self.class_store.are_types_compatible(&object_description, &class_description) { 1 } else { 0 }; + let instruction_result = if class_name == native_class_name { + 1 + } else { + let class_index = self.class_store.class_idx_from_name(class_name).unwrap(); + let class_object_ref = self.class_store.get_class_objectref_from_index(class_index); + let native_class_description = self.heap_area.object_area.get_class_ref_native_class_name(class_object_ref, &self.class_store); + let class_description = AbstractTypeDescription::parse_full(native_class_description)?; + + let object_description = AbstractTypeDescription::parse_full(native_class_name)?; + if self.class_store.are_types_compatible(&object_description, &class_description) { 1 } else { 0 } + }; + wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(instruction_result)))?; } + Instruction::InvokeInterface(methodref_index) => { + let (supplied_interface_name, supplied_method_name, supplied_descriptor_string) = class.gather_interfacemethodref(methodref_index)?; + + if ! self.class_store.have_class(supplied_interface_name) { + frame.instruction_pointer -= offset as u32; + return Ok(JVMCallbackOperation::LoadClass(supplied_interface_name.to_string())); + } + if ! self.class_store.was_init(supplied_interface_name).unwrap() { + frame.instruction_pointer -= offset as u32; + return Ok(JVMCallbackOperation::InitClass(supplied_interface_name.to_string())); + } + + let target_interface_class_index = self.class_store.class_idx_from_name(supplied_interface_name).unwrap(); + + let parsed_expected_descriptor: MethodDescriptor = MethodDescriptor::try_from(supplied_descriptor_string)?; + let (class_index, method_index, method_info) = match ClassMethodIterator::new(target_interface_class_index, &self.class_store) + .filter(|(_cid, _mid, minfo)| minfo.name == *supplied_method_name) + .filter(|(_cid, _mid, minfo)| minfo.descriptor == parsed_expected_descriptor) + .next() { + Some(m) => m, + None => { + // TODO: Throw exception + return Err(Error::RunTimeError(format!("InvokeInterface: Failed to find requested method '{}' with descriptor '{}' in the class '{}'", supplied_method_name, supplied_descriptor_string, supplied_interface_name))); + } + }; + + let mut arguments = VecDeque::new(); + fill_arguments(class, method, &mut arguments, &method_info.descriptor.argument_types, &mut frame.operand_stack)?; + let this_object = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?; + arguments.push_front(StackValue::Reference(this_object)); + + let object_class_index = self.heap_area.object_area.get_object_class_index(this_object); + let object_class_file = self.class_store.class_file_from_idx(object_class_index).unwrap(); + let object_class_name = object_class_file.get_classname().unwrap(); + + if object_class_name != supplied_interface_name { + let object_class_description = AbstractTypeDescription::for_class_name(object_class_name); + let supplied_interface_description = AbstractTypeDescription::for_class_name(supplied_interface_name); + + if ! self.class_store.are_types_compatible(&object_class_description, &supplied_interface_description) { + return Err(Error::RunTimeError(format!("Tried to invoke interface method : '{supplied_interface_name}.{supplied_method_name}' on object of type '{object_class_name}'"))); + } + } + + // TODO: I hate this, now I have to find the 'implementation' of a function + let interface_class_file = self.class_store.class_file_from_idx(target_interface_class_index).unwrap(); + let interface_frame = StackFrame::new( + interface_class_file, + target_interface_class_index, + method_index as u16, + arguments.make_contiguous() + ); + + return Ok(JVMCallbackOperation::PushFrame(interface_frame)); + } + Instruction::InvokeSpecial(methodref_index) => { // No instance-based dispatch let (supplied_class_name, supplied_method_name, supplied_descriptor_string) = class.gather_methodref(methodref_index)?;