diff --git a/src/classfile.rs b/src/classfile.rs index 8e0cf65..f6399f3 100644 --- a/src/classfile.rs +++ b/src/classfile.rs @@ -391,6 +391,23 @@ impl JavaClassFile { }, } } + + pub fn is_method_bytecode_protected(&self, method: &MethodInfo, instruction_pointer: u16, exception: crate::heap_area::ObjectReference) -> bool { + let code_attribute = method.get_code_attribute().unwrap(); + + for exception_entry in &code_attribute.exception_table { + if exception_entry.start_pc <= instruction_pointer && exception_entry.end_pc > instruction_pointer { + if exception_entry.catch_type == 0 { + return true; + } else { + // Check catch-type + todo!() + } + } + } + + false + } } #[derive(Debug)] @@ -500,10 +517,10 @@ impl LineNumberTableAttributeData { #[derive(Debug)] pub struct ExceptionTableEntry { - start_pc: u16, - end_pc: u16, - handler_pc: u16, - catch_type: u16, + pub start_pc: u16, + pub end_pc: u16, + pub handler_pc: u16, + pub catch_type: u16, } impl ExceptionTableEntry { @@ -517,6 +534,10 @@ impl ExceptionTableEntry { } ) } + + fn catches_in(&self, instruction_pointer: u16) -> bool { + return self.start_pc <= instruction_pointer && self.end_pc > instruction_pointer + } } #[derive(Debug)] @@ -1128,6 +1149,20 @@ impl MethodInfo { None }; } + + pub fn is_native(&self) -> bool { + return self.access_flags & MethodAccessFlag::Native; + } + + pub fn get_protected_handler_pc(&self, instruction_pointer: u16) -> Option { + for exception_entry in &self.get_code_attribute()?.exception_table { + if exception_entry.catches_in(instruction_pointer) { + return Some(exception_entry.handler_pc) + } + } + + None + } } diff --git a/src/jvm.rs b/src/jvm.rs index 3f24b01..32ab7aa 100644 --- a/src/jvm.rs +++ b/src/jvm.rs @@ -1,3 +1,4 @@ +use crate::classfile::ExceptionTableEntry; use core::fmt::{Display, Formatter}; use std::collections::VecDeque; @@ -9,7 +10,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::{ ConstantPoolInfo, ConstantClassInfo, ConstantUtf8Info, ConstantMethodRefInfo, ConstantNameAndTypeInfo}; +use crate::constantpool::{ ConstantClassInfo, ConstantInterfaceMethodRefInfo, ConstantMethodRefInfo, ConstantNameAndTypeInfo, ConstantPoolInfo, ConstantUtf8Info}; use crate::heap_area::{ HeapArea, FieldValue, ObjectReference, CompartmentEntry }; use crate::iterators::{ ClassMethodIterator, ClassFieldIterator }; use crate::native_methods; @@ -259,7 +260,12 @@ impl JVM { ConstantPoolInfo::Class(ConstantClassInfo { name_index: 18 }), ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "java/lang/System".to_string() }), ConstantPoolInfo::NameAndType(ConstantNameAndTypeInfo { name_index: 20, descriptor_index: 13 }), - ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "initPhase1".to_string() }), + ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "initPhase1".to_string() }), // 20 + ConstantPoolInfo::InterfaceMethodRef(ConstantInterfaceMethodRefInfo { class_index: 22, name_and_type_index: 24}), + ConstantPoolInfo::Class(ConstantClassInfo { name_index: 23 }), + ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "java/lang/Throwable".to_string() }), + ConstantPoolInfo::NameAndType(ConstantNameAndTypeInfo { name_index: 25, descriptor_index: 13 }), + ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "printStackTrace".to_string() }), // 25 ] ), access_flags: ClassAccessFlagMask { mask: ClassAccessFlag::Super.discriminant() }, @@ -309,9 +315,24 @@ impl JVM { 0xB8_u8.to_be(), // invokestatic 0x04_u16.to_be_bytes()[0], // index 4 into the constant 0x04_u16.to_be_bytes()[1], // pool + + 0xB1_u8.to_be(), // returnvoid + // index 14 + + 0xB9_u8.to_be(), // invokeinterface + 21_u16.to_be_bytes()[0], // index 21 constant + 21_u16.to_be_bytes()[1], // index 21 constant + 0x00_u8.to_be(), // constant 0 ]), }, - exception_table: Box::new([]), + exception_table: Box::new([ + ExceptionTableEntry { + start_pc: 0, + end_pc: u16::MAX, + handler_pc: 14, + catch_type: 0, // Catchall + } + ]), attributes: Box::new([]), } ) @@ -460,6 +481,43 @@ impl JVM { JVMCallbackOperation::MakeArrayClass(component_class_ref, component_descriptor) => { self.make_array_class(component_class_ref, component_descriptor); } + + JVMCallbackOperation::ThrowException(exception) => { + let mut is_handler_found = false; + while ! is_handler_found { + is_handler_found = { + let frame = { + let frame_index = self.stack_frames.len() - 1; + &mut self.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]; + if method.is_native() { + false + } else { + class.is_method_bytecode_protected(method, frame.instruction_pointer as u16, exception) + } + }; + + if ! is_handler_found { + self.stack_frames.pop(); + } + } + + if is_handler_found { + let frame = { + let frame_index = self.stack_frames.len() - 1; + &mut self.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]; + frame.operand_stack.clear(); + wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Reference(exception)))?; + frame.instruction_pointer = method.get_protected_handler_pc(frame.instruction_pointer as u16).unwrap() as u32; + } else { + unreachable!() + } + } } } @@ -2281,7 +2339,10 @@ impl JVM { Instruction::ThrowException() => { let exception = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?; if exception == ObjectReference::NULL { - + // TODO: Throw NullPointerException + } else { + // TODO: Check throwable instance + return Ok(JVMCallbackOperation::ThrowException(exception)); } } @@ -2312,6 +2373,7 @@ pub enum JVMCallbackOperation { LoadClass(String), InitClass(String), MakeArrayClass(ObjectReference, AbstractTypeDescription), + ThrowException(ObjectReference), } fn load_local_reference(class: &JavaClassFile, method: &MethodInfo, frame: &mut StackFrame, index: usize) -> Result<(), Error> { diff --git a/src/stackframe.rs b/src/stackframe.rs index 2c31173..56b84cc 100644 --- a/src/stackframe.rs +++ b/src/stackframe.rs @@ -47,6 +47,10 @@ impl OperandStack { } } + pub fn clear(&mut self) -> () { + self.depth = 0 + } + pub fn push(&mut self, value: StackValue) -> Result<(), Error> { if self.depth as usize == self.stack.len() { return Err(Error::PushError(format!("Trying to push onto full operand stack, capacity: {}, value: {:?}", self.depth, value)))