diff --git a/README.md b/README.md index 3824a0c..8cece0f 100644 --- a/README.md +++ b/README.md @@ -103,7 +103,7 @@ fn main() { - [ ] RuntimeVisibleTypeAnnotations - [ ] RuntimeInvisibleTypeAnnotations - [ ] AnnotationDefault - - [ ] MethodParameters + - [X] MethodParameters - [ ] Useful but not critical - [x] SourceFile - [ ] SourceDebugExtension diff --git a/java-assets/compiled-classes/BasicClass.class b/java-assets/compiled-classes/BasicClass.class index 88c39aa..393c943 100644 Binary files a/java-assets/compiled-classes/BasicClass.class and b/java-assets/compiled-classes/BasicClass.class differ diff --git a/java-assets/src/uk/co/palmr/classfileparser/BasicClass.java b/java-assets/src/uk/co/palmr/classfileparser/BasicClass.java index 31b0e01..dfb20ed 100644 --- a/java-assets/src/uk/co/palmr/classfileparser/BasicClass.java +++ b/java-assets/src/uk/co/palmr/classfileparser/BasicClass.java @@ -28,4 +28,8 @@ public static long getSize() { public static double getLEETness() { return 1.337; } + + public static int add(int a, int b) { + return a + b; + } } diff --git a/src/attribute_info/mod.rs b/src/attribute_info/mod.rs index aa9b093..509ae8b 100644 --- a/src/attribute_info/mod.rs +++ b/src/attribute_info/mod.rs @@ -8,5 +8,6 @@ pub use self::parser::bootstrap_methods_attribute_parser; pub use self::parser::code_attribute_parser; pub use self::parser::constant_value_attribute_parser; pub use self::parser::exceptions_attribute_parser; +pub use self::parser::method_parameters_attribute_parser; pub use self::parser::sourcefile_attribute_parser; pub use self::parser::stack_map_table_attribute_parser; diff --git a/src/attribute_info/parser.rs b/src/attribute_info/parser.rs index 9a24279..9a01353 100644 --- a/src/attribute_info/parser.rs +++ b/src/attribute_info/parser.rs @@ -64,6 +64,32 @@ pub fn code_attribute_parser(input: &[u8]) -> Result<(&[u8], CodeAttribute), Err ) } +pub fn method_parameters_attribute_parser( + input: &[u8], +) -> Result<(&[u8], MethodParametersAttribute), Err<&[u8]>> { + do_parse!( + input, + parameters_count: be_u8 + >> parameters: count!(parameters_parser, parameters_count as usize) + >> (MethodParametersAttribute { + parameters_count, + parameters, + }) + ) +} + +pub fn parameters_parser(input: &[u8]) -> Result<(&[u8], ParameterAttribute), Err<&[u8]>> { + do_parse!( + input, + name_index: be_u16 + >> access_flags: be_u16 + >> (ParameterAttribute { + name_index, + access_flags + }) + ) +} + fn same_frame_parser(input: &[u8], frame_type: u8) -> Result<(&[u8], StackMapFrame), Err<&[u8]>> { value!(input, SameFrame { frame_type }) } diff --git a/src/attribute_info/types.rs b/src/attribute_info/types.rs index c9ebfbf..dde2855 100644 --- a/src/attribute_info/types.rs +++ b/src/attribute_info/types.rs @@ -25,6 +25,18 @@ pub struct CodeAttribute { pub attributes: Vec, } +#[derive(Clone, Debug)] +pub struct MethodParametersAttribute { + pub parameters_count: u8, + pub parameters: Vec, +} + +#[derive(Clone, Debug)] +pub struct ParameterAttribute { + pub name_index: u16, + pub access_flags: u16, +} + #[derive(Clone, Debug)] pub enum VerificationTypeInfo { Top, diff --git a/tests/code_attribute.rs b/tests/code_attribute.rs index c5cc079..0e72d6c 100644 --- a/tests/code_attribute.rs +++ b/tests/code_attribute.rs @@ -1,6 +1,6 @@ extern crate classfile_parser; -use classfile_parser::attribute_info::code_attribute_parser; +use classfile_parser::attribute_info::{code_attribute_parser, method_parameters_attribute_parser}; use classfile_parser::class_parser; use classfile_parser::code_attribute::{code_parser, instruction_parser, Instruction}; use classfile_parser::method_info::MethodAccessFlags; @@ -79,3 +79,39 @@ fn test_class() { assert!(parsed.is_ok()); assert_eq!(64, parsed.unwrap().1.len()); } + +fn lookup_string(c: &classfile_parser::ClassFile, index: u16) -> Option { + let con = &c.const_pool[(index - 1) as usize]; + match con { + classfile_parser::constant_info::ConstantInfo::Utf8(utf8) => Some(utf8.utf8_string.clone()), + _ => None, + } +} + +#[test] +fn method_parameters() { + let class_bytes = include_bytes!("../java-assets/compiled-classes/BasicClass.class"); + let (_, class) = class_parser(class_bytes).unwrap(); + let method_info = &class.methods.iter().last().unwrap(); + + // The class was not compiled with "javac -parameters" this required being able to find + // MethodParameters in the class file, for example: + // javac -parameters ./java-assets/src/uk/co/palmr/classfileparser/BasicClass.java -d ./java-assets/compiled-classes ; cp ./java-assets/compiled-classes/uk/co/palmr/classfileparser/BasicClass.class ./java-assets/compiled-classes/BasicClass.class + assert_eq!(method_info.attributes.len(), 2); + let (_, method_parameters) = + method_parameters_attribute_parser(&method_info.attributes[1].info).unwrap(); + assert_eq!( + lookup_string( + &class, + method_parameters.parameters.get(0).unwrap().name_index + ), + Some("a".to_string()) + ); + assert_eq!( + lookup_string( + &class, + method_parameters.parameters.get(1).unwrap().name_index + ), + Some("b".to_string()) + ); +}