diff --git a/src/classfile.rs b/src/classfile.rs index 0b6ec53..c2071c1 100644 --- a/src/classfile.rs +++ b/src/classfile.rs @@ -375,6 +375,22 @@ impl JavaClassFile { return Ok((class_name, method_name, method_descriptor)); } + + pub fn sourcefile(&self) -> Result, Error> { + match self.attributes.into_iter() + .filter_map(|attribute| match &attribute.data { + AttributeData::SourceFile(data) => Some(data), + _ => None, + }) + .next() { + None => Ok(None), + Some(data) => { + let sourcefile_name = &self.pool_utf8_entry(data.source_file_index)?.utf8; + + Ok(Some(sourcefile_name)) + }, + } + } } #[derive(Debug)] @@ -953,6 +969,13 @@ impl AbstractTypeDescription { } } + pub fn class_type(class: &str) -> AbstractTypeDescription { + AbstractTypeDescription { + array_level: 0, + kind: AbstractTypeKind::Classname(class.to_string()) + } + } + } #[derive(Debug, Eq, PartialEq)] @@ -1078,6 +1101,23 @@ impl MethodInfo { ) } + pub fn get_bytecode_linenumber(&self, bytecode_index: u16) -> Option { + let linenumbertable = match self.attributes + .into_iter() + .filter_map(|a| match &a.data { + AttributeData::LineNumberTable(data) => Some(data), + _ => None, + }) + .next() { + Some(a) => a, + None => return None, + }; + linenumbertable.entries.into_iter() + .take_while(|entry| entry.start_pc < bytecode_index) + .map(|entry| entry.line_number) + .last() + } + pub fn get_code_attribute(&self) -> Option<&CodeAttributeData> { return if self.code_attribute_index != self.attributes.len() { match &self.attributes[self.code_attribute_index].data { diff --git a/src/native_methods.rs b/src/native_methods.rs index 4b6ec3e..7413bf8 100644 --- a/src/native_methods.rs +++ b/src/native_methods.rs @@ -350,7 +350,7 @@ struct JavaLangSystem {} impl JavaLangSystem { pub fn arraycopy(jvm: &mut JVM) -> Result { - let frame = { + let frame = { let frame_index = jvm.stack_frames.len() - 1; &mut jvm.stack_frames[frame_index] }; @@ -372,7 +372,93 @@ impl JavaLangSystem { struct JavaLangThrowable {} impl JavaLangThrowable { + pub fn fill_in_stacktrace(jvm: &mut JVM) -> Result { + if ! jvm.class_store.have_class(&String::from("java/lang/StackTraceElement")) { + return Ok(JVMCallbackOperation::LoadClass(String::from("java/lang/StackTraceElement"))); + } + if ! jvm.class_store.was_init(&String::from("java/lang/StackTraceElement")).unwrap() { + return Ok(JVMCallbackOperation::LoadClass(String::from("java/lang/StackTraceElement"))); + } + let stackelement_class_index = jvm.class_store.class_idx_from_name(&String::from("java/lang/StackTraceElement")).unwrap(); + let stackelement_class_ref = jvm.class_store.get_class_objectref_from_index(stackelement_class_index); + let stackelement_type = AbstractTypeDescription::class_type("java/lang/StackTraceElement"); + let _ = match jvm.class_store.get_array_class_ref(&stackelement_type.array()) { + Some(r) => r, + None => return Ok(JVMCallbackOperation::MakeArrayClass(stackelement_class_ref, stackelement_type)), + }; + + let this = { + let frame = { + let frame_index = jvm.stack_frames.len() - 1; + &jvm.stack_frames[frame_index] + }; + frame.load_local_reference(0).unwrap() + }; + + let stackelement_array = jvm.heap_area.make_empty_array(&jvm.class_store, AbstractTypeDescription::class_type("java/lang/StackTraceElement"), jvm.stack_frames.len()); + + jvm.heap_area.object_area.set_object_field( + this, + "stackTrace", + stackelement_array.into(), + stackelement_class_index, + &jvm.class_store + )?; + + for (index, frame) in (&jvm.stack_frames).into_iter().enumerate() { + let class_file = jvm.class_store.class_file_from_idx(frame.class_index).unwrap(); + let method_info = &class_file.methods[frame.method_index as usize]; + + let class_name = class_file.get_classname()?; + + let line_number = method_info.get_bytecode_linenumber(frame.instruction_pointer.try_into().unwrap()); + + let stackelement = jvm.heap_area.make_object(&jvm.class_store, stackelement_class_index); + + let class_name_string = jvm.heap_area.make_handmade_string(class_name, &jvm.class_store); + let method_name_string = jvm.heap_area.make_handmade_string(&method_info.name, &jvm.class_store); + + let sourcefile = class_file.sourcefile()?; + let sourcefile_string = match sourcefile { + Some(string) => jvm.heap_area.make_handmade_string(string, &jvm.class_store), + None => jvm.heap_area.static_area.get(&String::from("StackTraceElement"), &String::from("UNKNOWN_SOURCE"), AbstractTypeDescription::class_type("java/lang/String")).unwrap().expect_reference(), + }; + + jvm.heap_area.object_area.set_object_field( + stackelement, + "declaringClass", + FieldValue::Reference(class_name_string), + stackelement_class_index, + &jvm.class_store + )?; + jvm.heap_area.object_area.set_object_field( + stackelement, + "methodName", + FieldValue::Reference(method_name_string), + stackelement_class_index, + &jvm.class_store + )?; + jvm.heap_area.object_area.set_object_field( + stackelement, + "lineNumber", + FieldValue::Int(line_number.unwrap_or(1) as i32), + stackelement_class_index, + &jvm.class_store + )?; + jvm.heap_area.object_area.set_object_field( + stackelement, + "fileName", + FieldValue::Reference(sourcefile_string), + stackelement_class_index, + &jvm.class_store + )?; + + jvm.heap_area.object_area.set_array_element(stackelement_array, index, stackelement.into()); + } + + Ok(JVMCallbackOperation::ReturnFrame(this.into())) + } } struct JdkInternalMiscUnsafe {} @@ -2861,7 +2947,7 @@ pub fn function_for(class_name: &str, m: &crate::classfile::MethodInfo) -> Resul ]), return_type: AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Classname("java/lang/Throwable".to_string())}, }, - JavaLangThrowable::fill_in_stacktrace() + JavaLangThrowable::fill_in_stacktrace ), ];