diff --git a/README.md b/README.md index 088beaf..d9aac7b 100644 --- a/README.md +++ b/README.md @@ -111,3 +111,4 @@ fn main() { - [x] LocalVariableTable - [x] LocalVariableTypeTable - [x] Deprecated + - [x] Module diff --git a/java-assets/compile.sh b/java-assets/compile.sh index cea4c97..56e20a5 100755 --- a/java-assets/compile.sh +++ b/java-assets/compile.sh @@ -27,6 +27,7 @@ javac -d java-assets/compiled-classes/ java-assets/src/UnicodeStrings.java javac -d java-assets/compiled-classes/ java-assets/src/DeprecatedAnnotation.java javac -d java-assets/compiled-classes/ java-assets/src/InnerClasses.java javac -d java-assets/compiled-classes/ java-assets/src/Annotations.java +javac -d java-assets/compiled-classes/ java-assets/src/module-info.java java-assets/src/com/some/Thing.java javac -g -d java-assets/compiled-classes/ java-assets/src/LocalVariableTable.java javac -d java-assets/compiled-classes/ java-assets/src/HelloWorld.java diff --git a/java-assets/compiled-classes/com/some/Thing.class b/java-assets/compiled-classes/com/some/Thing.class new file mode 100644 index 0000000..9bdeec9 Binary files /dev/null and b/java-assets/compiled-classes/com/some/Thing.class differ diff --git a/java-assets/compiled-classes/module-info.class b/java-assets/compiled-classes/module-info.class new file mode 100644 index 0000000..af6662f Binary files /dev/null and b/java-assets/compiled-classes/module-info.class differ diff --git a/java-assets/src/com/some/Thing.java b/java-assets/src/com/some/Thing.java new file mode 100644 index 0000000..26dee72 --- /dev/null +++ b/java-assets/src/com/some/Thing.java @@ -0,0 +1,3 @@ +package com.some; + +public class Thing {} diff --git a/java-assets/src/module-info.java b/java-assets/src/module-info.java new file mode 100644 index 0000000..bb0aef9 --- /dev/null +++ b/java-assets/src/module-info.java @@ -0,0 +1,3 @@ +module my.module { + exports com.some; +} diff --git a/src/attribute_info/mod.rs b/src/attribute_info/mod.rs index 49de015..4862564 100644 --- a/src/attribute_info/mod.rs +++ b/src/attribute_info/mod.rs @@ -13,6 +13,7 @@ pub use self::parser::exceptions_attribute_parser; pub use self::parser::inner_classes_attribute_parser; pub use self::parser::line_number_table_attribute_parser; pub use self::parser::method_parameters_attribute_parser; +pub use self::parser::module_attribute_parser; pub use self::parser::runtime_invisible_annotations_attribute_parser; pub use self::parser::runtime_invisible_parameter_annotations_attribute_parser; pub use self::parser::runtime_invisible_type_annotations_attribute_parser; diff --git a/src/attribute_info/parser.rs b/src/attribute_info/parser.rs index 7226f3c..142218f 100644 --- a/src/attribute_info/parser.rs +++ b/src/attribute_info/parser.rs @@ -697,3 +697,110 @@ pub fn sourcefile_attribute_parser( let (input, sourcefile_index) = be_u16(input)?; Ok((input, SourceFileAttribute { sourcefile_index })) } + +pub fn module_attribute_parser(input: &[u8]) -> Result<(&[u8], ModuleAttribute), Err<&[u8]>> { + let (input, module_name_index) = be_u16(input)?; + let (input, module_flags) = be_u16(input)?; + let (input, module_version_index) = be_u16(input)?; + + let (input, requires_count) = be_u16(input)?; + let (input, requires) = + count(module_requires_attribute_parser, requires_count as usize)(input)?; + + let (input, exports_count) = be_u16(input)?; + let (input, exports) = count(module_exports_attribute_parser, exports_count as usize)(input)?; + + let (input, opens_count) = be_u16(input)?; + let (input, opens) = count(module_opens_attribute_parser, opens_count as usize)(input)?; + + let (input, uses_count) = be_u16(input)?; + let (input, uses) = count(be_u16, uses_count as usize)(input)?; + + let (input, provides_count) = be_u16(input)?; + let (input, provides) = + count(module_provides_attribute_parser, provides_count as usize)(input)?; + + Ok(( + input, + ModuleAttribute { + module_name_index, + module_flags, + module_version_index, + requires, + exports, + opens, + uses, + provides, + }, + )) +} + +pub fn module_requires_attribute_parser( + input: &[u8], +) -> Result<(&[u8], ModuleRequiresAttribute), Err<&[u8]>> { + let (input, requires_index) = be_u16(input)?; + let (input, requires_flags) = be_u16(input)?; + let (input, requires_version_index) = be_u16(input)?; + + Ok(( + input, + ModuleRequiresAttribute { + requires_index, + requires_flags, + requires_version_index, + }, + )) +} + +pub fn module_exports_attribute_parser( + input: &[u8], +) -> Result<(&[u8], ModuleExportsAttribute), Err<&[u8]>> { + let (input, exports_index) = be_u16(input)?; + let (input, exports_flags) = be_u16(input)?; + let (input, exports_to_count) = be_u16(input)?; + + let (input, exports_to_index) = count(be_u16, exports_to_count as usize)(input)?; + + Ok(( + input, + ModuleExportsAttribute { + exports_index, + exports_flags, + exports_to_index, + }, + )) +} + +pub fn module_opens_attribute_parser( + input: &[u8], +) -> Result<(&[u8], ModuleOpensAttribute), Err<&[u8]>> { + let (input, opens_index) = be_u16(input)?; + let (input, opens_flags) = be_u16(input)?; + let (input, opens_to_count) = be_u16(input)?; + let (input, opens_to_index) = count(be_u16, opens_to_count as usize)(input)?; + + Ok(( + input, + ModuleOpensAttribute { + opens_index, + opens_flags, + opens_to_index, + }, + )) +} + +pub fn module_provides_attribute_parser( + input: &[u8], +) -> Result<(&[u8], ModuleProvidesAttribute), Err<&[u8]>> { + let (input, provides_index) = be_u16(input)?; + let (input, provides_with_count) = be_u16(input)?; + let (input, provides_with_index) = count(be_u16, provides_with_count as usize)(input)?; + + Ok(( + input, + ModuleProvidesAttribute { + provides_index, + provides_with_index, + }, + )) +} diff --git a/src/attribute_info/types.rs b/src/attribute_info/types.rs index 6febc66..df5cf02 100644 --- a/src/attribute_info/types.rs +++ b/src/attribute_info/types.rs @@ -343,3 +343,39 @@ pub struct SourceFileAttribute { /// The constant_pool entry at that index must be a CONSTANT_Utf8_info structure representing a string. pub sourcefile_index: u16, } + +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct ModuleAttribute { + pub module_name_index: u16, + pub module_flags: u16, + pub module_version_index: u16, + pub requires: Vec, + pub exports: Vec, + pub opens: Vec, + pub uses: Vec, + pub provides: Vec, +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct ModuleRequiresAttribute { + pub requires_index: u16, + pub requires_flags: u16, + pub requires_version_index: u16, +} +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct ModuleExportsAttribute { + pub exports_index: u16, + pub exports_flags: u16, + pub exports_to_index: Vec, +} +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct ModuleOpensAttribute { + pub opens_index: u16, + pub opens_flags: u16, + pub opens_to_index: Vec, +} +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct ModuleProvidesAttribute { + pub provides_index: u16, + pub provides_with_index: Vec, +} diff --git a/src/constant_info/parser.rs b/src/constant_info/parser.rs index 7cd196b..957ec38 100644 --- a/src/constant_info/parser.rs +++ b/src/constant_info/parser.rs @@ -131,6 +131,16 @@ fn const_invoke_dynamic(input: &[u8]) -> ConstantInfoResult<'_> { )) } +fn const_module(input: &[u8]) -> ConstantInfoResult<'_> { + let (input, name_index) = be_u16(input)?; + Ok((input, ConstantInfo::Module(ModuleConstant { name_index }))) +} + +fn const_package(input: &[u8]) -> ConstantInfoResult<'_> { + let (input, name_index) = be_u16(input)?; + Ok((input, ConstantInfo::Package(PackageConstant { name_index }))) +} + type ConstantInfoResult<'a> = Result<(&'a [u8], ConstantInfo), Err>>; type ConstantInfoVecResult<'a> = Result<(&'a [u8], Vec), Err>>; @@ -150,6 +160,8 @@ fn const_block_parser(input: &[u8], const_type: u8) -> ConstantInfoResult<'_> { 15 => const_method_handle(input), 16 => const_method_type(input), 18 => const_invoke_dynamic(input), + 19 => const_module(input), + 20 => const_package(input), _ => Result::Err(Err::Error(error_position!(input, ErrorKind::Alt))), } } diff --git a/src/constant_info/types.rs b/src/constant_info/types.rs index f66612b..081f8a0 100644 --- a/src/constant_info/types.rs +++ b/src/constant_info/types.rs @@ -17,6 +17,8 @@ pub enum ConstantInfo { MethodHandle(MethodHandleConstant), MethodType(MethodTypeConstant), InvokeDynamic(InvokeDynamicConstant), + Module(ModuleConstant), + Package(PackageConstant), Unusable, } @@ -110,3 +112,15 @@ pub struct InvokeDynamicConstant { pub bootstrap_method_attr_index: u16, pub name_and_type_index: u16, } + +#[derive(Clone, Debug)] +#[binrw] +pub struct ModuleConstant { + pub name_index: u16, +} + +#[derive(Clone, Debug)] +#[binrw] +pub struct PackageConstant { + pub name_index: u16, +} diff --git a/tests/code_attribute.rs b/tests/code_attribute.rs index c492465..ccb059e 100644 --- a/tests/code_attribute.rs +++ b/tests/code_attribute.rs @@ -6,10 +6,11 @@ extern crate classfile_parser; //use std::assert_matches::assert_matches; use classfile_parser::attribute_info::{ - DefaultAnnotation, ElementValue, InnerClassAccessFlags, TargetInfo, code_attribute_parser, - element_value_parser, enclosing_method_attribute_parser, inner_classes_attribute_parser, + DefaultAnnotation, ElementValue, InnerClassAccessFlags, ModuleAttribute, + ModuleExportsAttribute, ModuleRequiresAttribute, code_attribute_parser, element_value_parser, + enclosing_method_attribute_parser, inner_classes_attribute_parser, line_number_table_attribute_parser, method_parameters_attribute_parser, - runtime_invisible_annotations_attribute_parser, + module_attribute_parser, runtime_invisible_annotations_attribute_parser, runtime_invisible_parameter_annotations_attribute_parser, runtime_visible_annotations_attribute_parser, runtime_visible_parameter_annotations_attribute_parser, @@ -21,7 +22,7 @@ use classfile_parser::code_attribute::{ Instruction, LocalVariableTableAttribute, code_parser, instruction_parser, local_variable_type_table_parser, }; -use classfile_parser::constant_info::{ConstantInfo, Utf8Constant}; +use classfile_parser::constant_info::ConstantInfo; use classfile_parser::method_info::MethodAccessFlags; #[test] @@ -102,9 +103,9 @@ fn test_class() { 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.to_string()) - } + ConstantInfo::Utf8(utf8) => Some(utf8.utf8_string.to_string()), + ConstantInfo::Module(m) => lookup_string(c, m.name_index), + ConstantInfo::Package(p) => lookup_string(c, p.name_index), _ => None, } } @@ -864,3 +865,65 @@ fn deprecated() { assert_eq!(deprecated_field_attribute.len(), 1); } + +#[test] +fn module_info() { + let class_bytes = include_bytes!("../java-assets/compiled-classes/module-info.class"); + let (_, class) = class_parser(class_bytes).unwrap(); + + let module = class + .attributes + .iter() + .find(|attribute_info| { + matches!( + lookup_string(&class, attribute_info.attribute_name_index) + .unwrap() + .as_str(), + "Module" + ) + }) + .unwrap(); + + let (rest, module) = module_attribute_parser(&module.info).unwrap(); + assert_eq!(rest.len(), 0); + + let exptected = ModuleAttribute { + module_name_index: 6, + module_flags: 0, + module_version_index: 0, + requires: vec![ModuleRequiresAttribute { + requires_index: 8, + requires_flags: 32768, + requires_version_index: 10, + }], + exports: vec![ModuleExportsAttribute { + exports_index: 11, + exports_flags: 0, + exports_to_index: vec![], + }], + opens: vec![], + uses: vec![], + provides: vec![], + }; + + assert_eq!(module, exptected); + + assert_eq!( + lookup_string(&class, exptected.module_name_index) + .unwrap() + .as_str(), + "my.module" + ); + assert_eq!( + lookup_string(&class, exptected.requires.first().unwrap().requires_index) + .unwrap() + .as_str(), + "java.base" + ); + assert_eq!( + lookup_string(&class, exptected.exports.first().unwrap().exports_index) + .unwrap() + .as_str(), + "com/some" + ); +}