1use cafebabe::attributes::AttributeData;
12
13use crate::ClasspathResult;
14use crate::stub::model::{
15 AccessFlags, ModuleExports, ModuleOpens, ModuleProvides, ModuleRequires, ModuleStub,
16};
17
18use super::constants::class_name_to_fqn;
19
20pub fn extract_module(class: &cafebabe::ClassFile<'_>) -> ClasspathResult<Option<ModuleStub>> {
35 let module_data = class.attributes.iter().find_map(|attr| match &attr.data {
36 AttributeData::Module(data) => Some(data),
37 _ => None,
38 });
39
40 let Some(data) = module_data else {
41 return Ok(None);
42 };
43
44 let stub = convert_module_data(data)?;
45 Ok(Some(stub))
46}
47
48fn convert_module_data(data: &cafebabe::attributes::ModuleData<'_>) -> ClasspathResult<ModuleStub> {
54 let name = class_name_to_fqn(&data.name);
55 let access = AccessFlags::new(data.access_flags.bits());
56 let version = data.version.as_ref().map(|v| v.to_string());
57
58 let requires = data
59 .requires
60 .iter()
61 .map(convert_requires_entry)
62 .collect::<ClasspathResult<Vec<_>>>()?;
63
64 let exports = data
65 .exports
66 .iter()
67 .map(convert_exports_entry)
68 .collect::<ClasspathResult<Vec<_>>>()?;
69
70 let opens = data
71 .opens
72 .iter()
73 .map(convert_opens_entry)
74 .collect::<ClasspathResult<Vec<_>>>()?;
75
76 let provides = data
77 .provides
78 .iter()
79 .map(convert_provides_entry)
80 .collect::<ClasspathResult<Vec<_>>>()?;
81
82 let uses = data
83 .uses
84 .iter()
85 .map(|class_name| class_name_to_fqn(class_name))
86 .collect();
87
88 Ok(ModuleStub {
89 name,
90 access,
91 version,
92 requires,
93 exports,
94 opens,
95 provides,
96 uses,
97 })
98}
99
100fn convert_requires_entry(
102 entry: &cafebabe::attributes::ModuleRequireEntry<'_>,
103) -> ClasspathResult<ModuleRequires> {
104 Ok(ModuleRequires {
105 module_name: class_name_to_fqn(&entry.name),
106 access: AccessFlags::new(entry.flags.bits()),
107 version: entry.version.as_ref().map(|v| v.to_string()),
108 })
109}
110
111fn convert_exports_entry(
113 entry: &cafebabe::attributes::ModuleExportsEntry<'_>,
114) -> ClasspathResult<ModuleExports> {
115 let to_modules = entry
116 .exports_to
117 .iter()
118 .map(|m| class_name_to_fqn(m))
119 .collect();
120
121 Ok(ModuleExports {
122 package: class_name_to_fqn(&entry.package_name),
123 access: AccessFlags::new(entry.flags.bits()),
124 to_modules,
125 })
126}
127
128fn convert_opens_entry(
130 entry: &cafebabe::attributes::ModuleOpensEntry<'_>,
131) -> ClasspathResult<ModuleOpens> {
132 let to_modules = entry
133 .opens_to
134 .iter()
135 .map(|m| class_name_to_fqn(m))
136 .collect();
137
138 Ok(ModuleOpens {
139 package: class_name_to_fqn(&entry.package_name),
140 access: AccessFlags::new(entry.flags.bits()),
141 to_modules,
142 })
143}
144
145fn convert_provides_entry(
147 entry: &cafebabe::attributes::ModuleProvidesEntry<'_>,
148) -> ClasspathResult<ModuleProvides> {
149 let implementations = entry
150 .provides_with
151 .iter()
152 .map(|c| class_name_to_fqn(c))
153 .collect();
154
155 Ok(ModuleProvides {
156 service: class_name_to_fqn(&entry.service_interface_name),
157 implementations,
158 })
159}
160
161#[cfg(test)]
166mod tests {
167 use super::*;
168 use crate::ClasspathError;
169 use cafebabe::ParseOptions;
170
171 struct ModuleBuilder {
181 cp_entries: Vec<Vec<u8>>,
183 module_name_idx: u16,
185 module_flags: u16,
187 module_version_idx: u16,
189 requires: Vec<(u16, u16, u16)>,
191 exports: Vec<(u16, u16, Vec<u16>)>,
193 opens: Vec<(u16, u16, Vec<u16>)>,
195 uses: Vec<u16>,
197 provides: Vec<(u16, Vec<u16>)>,
199 }
200
201 impl ModuleBuilder {
202 fn new(module_name: &str) -> Self {
208 let mut builder = Self {
209 cp_entries: Vec::new(),
210 module_name_idx: 0,
211 module_flags: 0,
212 module_version_idx: 0,
213 requires: Vec::new(),
214 exports: Vec::new(),
215 opens: Vec::new(),
216 uses: Vec::new(),
217 provides: Vec::new(),
218 };
219
220 builder.add_utf8("module-info");
222 builder.add_class(1);
224 builder.add_utf8("java/lang/Object");
226 builder.add_class(3);
228 builder.add_utf8("Module");
230 builder.add_utf8(module_name);
232 builder.module_name_idx = builder.add_module(6);
234
235 builder
236 }
237
238 fn module_flags(mut self, flags: u16) -> Self {
241 self.module_flags = flags;
242 self
243 }
244
245 fn module_version(mut self, version: &str) -> Self {
247 self.module_version_idx = self.add_utf8(version);
248 self
249 }
250
251 fn add_utf8(&mut self, s: &str) -> u16 {
253 let mut entry = vec![1u8]; let bytes = s.as_bytes();
255 entry.extend_from_slice(&(bytes.len() as u16).to_be_bytes());
256 entry.extend_from_slice(bytes);
257 self.cp_entries.push(entry);
258 self.cp_entries.len() as u16
259 }
260
261 fn add_class(&mut self, name_idx: u16) -> u16 {
263 let mut entry = vec![7u8]; entry.extend_from_slice(&name_idx.to_be_bytes());
265 self.cp_entries.push(entry);
266 self.cp_entries.len() as u16
267 }
268
269 fn add_module(&mut self, name_idx: u16) -> u16 {
271 let mut entry = vec![19u8]; entry.extend_from_slice(&name_idx.to_be_bytes());
273 self.cp_entries.push(entry);
274 self.cp_entries.len() as u16
275 }
276
277 fn add_package(&mut self, name_idx: u16) -> u16 {
279 let mut entry = vec![20u8]; entry.extend_from_slice(&name_idx.to_be_bytes());
281 self.cp_entries.push(entry);
282 self.cp_entries.len() as u16
283 }
284
285 fn add_requires(
287 &mut self,
288 module_name: &str,
289 flags: u16,
290 version: Option<&str>,
291 ) -> &mut Self {
292 let name_idx = self.add_utf8(module_name);
293 let module_idx = self.add_module(name_idx);
294 let version_idx = version.map_or(0, |v| self.add_utf8(v));
295 self.requires.push((module_idx, flags, version_idx));
296 self
297 }
298
299 fn add_exports(
301 &mut self,
302 package_name: &str,
303 flags: u16,
304 to_modules: &[&str],
305 ) -> &mut Self {
306 let pkg_name_idx = self.add_utf8(package_name);
307 let pkg_idx = self.add_package(pkg_name_idx);
308 let to_indices: Vec<u16> = to_modules
309 .iter()
310 .map(|m| {
311 let name_idx = self.add_utf8(m);
312 self.add_module(name_idx)
313 })
314 .collect();
315 self.exports.push((pkg_idx, flags, to_indices));
316 self
317 }
318
319 fn add_opens(&mut self, package_name: &str, flags: u16, to_modules: &[&str]) -> &mut Self {
321 let pkg_name_idx = self.add_utf8(package_name);
322 let pkg_idx = self.add_package(pkg_name_idx);
323 let to_indices: Vec<u16> = to_modules
324 .iter()
325 .map(|m| {
326 let name_idx = self.add_utf8(m);
327 self.add_module(name_idx)
328 })
329 .collect();
330 self.opens.push((pkg_idx, flags, to_indices));
331 self
332 }
333
334 fn add_uses(&mut self, class_name: &str) -> &mut Self {
336 let name_idx = self.add_utf8(class_name);
337 let class_idx = self.add_class(name_idx);
338 self.uses.push(class_idx);
339 self
340 }
341
342 fn add_provides(&mut self, service_class: &str, impl_classes: &[&str]) -> &mut Self {
344 let svc_name_idx = self.add_utf8(service_class);
345 let svc_idx = self.add_class(svc_name_idx);
346 let impl_indices: Vec<u16> = impl_classes
347 .iter()
348 .map(|c| {
349 let name_idx = self.add_utf8(c);
350 self.add_class(name_idx)
351 })
352 .collect();
353 self.provides.push((svc_idx, impl_indices));
354 self
355 }
356
357 fn build(&self) -> Vec<u8> {
359 let mut bytes = Vec::new();
360
361 bytes.extend_from_slice(&0xCAFE_BABEu32.to_be_bytes());
363 bytes.extend_from_slice(&0u16.to_be_bytes());
365 bytes.extend_from_slice(&53u16.to_be_bytes());
367
368 let cp_count = self.cp_entries.len() as u16 + 1;
370 bytes.extend_from_slice(&cp_count.to_be_bytes());
371 for entry in &self.cp_entries {
372 bytes.extend_from_slice(entry);
373 }
374
375 bytes.extend_from_slice(&0x8000u16.to_be_bytes());
377 bytes.extend_from_slice(&2u16.to_be_bytes());
379 bytes.extend_from_slice(&0u16.to_be_bytes());
381 bytes.extend_from_slice(&0u16.to_be_bytes());
383 bytes.extend_from_slice(&0u16.to_be_bytes());
385 bytes.extend_from_slice(&0u16.to_be_bytes());
387
388 bytes.extend_from_slice(&1u16.to_be_bytes());
390
391 let attr_data = self.build_module_attr_data();
393 bytes.extend_from_slice(&5u16.to_be_bytes());
395 bytes.extend_from_slice(&(attr_data.len() as u32).to_be_bytes());
397 bytes.extend_from_slice(&attr_data);
398
399 bytes
400 }
401
402 fn build_module_attr_data(&self) -> Vec<u8> {
404 let mut data = Vec::new();
405
406 data.extend_from_slice(&self.module_name_idx.to_be_bytes());
408 data.extend_from_slice(&self.module_flags.to_be_bytes());
410 data.extend_from_slice(&self.module_version_idx.to_be_bytes());
412
413 data.extend_from_slice(&(self.requires.len() as u16).to_be_bytes());
415 for &(module_idx, flags, version_idx) in &self.requires {
416 data.extend_from_slice(&module_idx.to_be_bytes());
417 data.extend_from_slice(&flags.to_be_bytes());
418 data.extend_from_slice(&version_idx.to_be_bytes());
419 }
420
421 data.extend_from_slice(&(self.exports.len() as u16).to_be_bytes());
423 for (pkg_idx, flags, to_indices) in &self.exports {
424 data.extend_from_slice(&pkg_idx.to_be_bytes());
425 data.extend_from_slice(&flags.to_be_bytes());
426 data.extend_from_slice(&(to_indices.len() as u16).to_be_bytes());
427 for idx in to_indices {
428 data.extend_from_slice(&idx.to_be_bytes());
429 }
430 }
431
432 data.extend_from_slice(&(self.opens.len() as u16).to_be_bytes());
434 for (pkg_idx, flags, to_indices) in &self.opens {
435 data.extend_from_slice(&pkg_idx.to_be_bytes());
436 data.extend_from_slice(&flags.to_be_bytes());
437 data.extend_from_slice(&(to_indices.len() as u16).to_be_bytes());
438 for idx in to_indices {
439 data.extend_from_slice(&idx.to_be_bytes());
440 }
441 }
442
443 data.extend_from_slice(&(self.uses.len() as u16).to_be_bytes());
445 for idx in &self.uses {
446 data.extend_from_slice(&idx.to_be_bytes());
447 }
448
449 data.extend_from_slice(&(self.provides.len() as u16).to_be_bytes());
451 for (svc_idx, impl_indices) in &self.provides {
452 data.extend_from_slice(&svc_idx.to_be_bytes());
453 data.extend_from_slice(&(impl_indices.len() as u16).to_be_bytes());
454 for idx in impl_indices {
455 data.extend_from_slice(&idx.to_be_bytes());
456 }
457 }
458
459 data
460 }
461 }
462
463 fn parse_and_extract(bytes: &[u8]) -> ClasspathResult<Option<ModuleStub>> {
465 let mut opts = ParseOptions::default();
466 opts.parse_bytecode(false);
467 let class_file = cafebabe::parse_class_with_options(bytes, &opts).map_err(|e| {
468 ClasspathError::BytecodeParseError {
469 class_name: String::from("<test>"),
470 reason: e.to_string(),
471 }
472 })?;
473 extract_module(&class_file)
474 }
475
476 #[test]
481 fn test_java_base_module_exports() {
482 let mut builder = ModuleBuilder::new("java.base");
483 builder.add_exports("java/lang", 0, &[]);
484 builder.add_exports("java/util", 0, &[]);
485 builder.add_requires("java.base", 0x8000, Some("17")); let bytes = builder.build();
488 let stub = parse_and_extract(&bytes).unwrap().unwrap();
489
490 assert_eq!(stub.name, "java.base");
491 assert_eq!(stub.exports.len(), 2);
492 assert_eq!(stub.exports[0].package, "java.lang");
493 assert!(stub.exports[0].to_modules.is_empty()); assert_eq!(stub.exports[1].package, "java.util");
495 assert_eq!(stub.requires.len(), 1);
496 assert_eq!(stub.requires[0].module_name, "java.base");
497 assert!(stub.requires[0].access.contains(0x8000)); assert_eq!(stub.requires[0].version.as_deref(), Some("17"));
499 }
500
501 #[test]
506 fn test_requires_transitive() {
507 let mut builder = ModuleBuilder::new("com.example.app");
508 builder.add_requires("java.base", 0x8000, Some("17")); builder.add_requires("java.logging", 0x0020, None); let bytes = builder.build();
512 let stub = parse_and_extract(&bytes).unwrap().unwrap();
513
514 assert_eq!(stub.name, "com.example.app");
515 assert_eq!(stub.requires.len(), 2);
516
517 let java_base = &stub.requires[0];
518 assert_eq!(java_base.module_name, "java.base");
519 assert!(java_base.access.contains(0x8000)); let java_logging = &stub.requires[1];
522 assert_eq!(java_logging.module_name, "java.logging");
523 assert!(java_logging.access.contains(0x0020)); assert!(java_logging.version.is_none());
525 }
526
527 #[test]
532 fn test_provides_service() {
533 let mut builder = ModuleBuilder::new("com.example.provider");
534 builder.add_provides(
535 "com/example/api/Service",
536 &[
537 "com/example/impl/ServiceImpl",
538 "com/example/impl/ServiceImpl2",
539 ],
540 );
541
542 let bytes = builder.build();
543 let stub = parse_and_extract(&bytes).unwrap().unwrap();
544
545 assert_eq!(stub.provides.len(), 1);
546 assert_eq!(stub.provides[0].service, "com.example.api.Service");
547 assert_eq!(stub.provides[0].implementations.len(), 2);
548 assert_eq!(
549 stub.provides[0].implementations[0],
550 "com.example.impl.ServiceImpl"
551 );
552 assert_eq!(
553 stub.provides[0].implementations[1],
554 "com.example.impl.ServiceImpl2"
555 );
556 }
557
558 #[test]
563 fn test_opens_for_reflection() {
564 let mut builder = ModuleBuilder::new("com.example.reflective");
565 builder.add_opens("com/example/internal", 0, &[]);
567 builder.add_opens(
569 "com/example/private",
570 0,
571 &["com.example.framework", "com.example.test"],
572 );
573
574 let bytes = builder.build();
575 let stub = parse_and_extract(&bytes).unwrap().unwrap();
576
577 assert_eq!(stub.opens.len(), 2);
578
579 let open_all = &stub.opens[0];
580 assert_eq!(open_all.package, "com.example.internal");
581 assert!(open_all.to_modules.is_empty());
582
583 let open_qualified = &stub.opens[1];
584 assert_eq!(open_qualified.package, "com.example.private");
585 assert_eq!(open_qualified.to_modules.len(), 2);
586 assert_eq!(open_qualified.to_modules[0], "com.example.framework");
587 assert_eq!(open_qualified.to_modules[1], "com.example.test");
588 }
589
590 #[test]
595 fn test_uses_declarations() {
596 let mut builder = ModuleBuilder::new("com.example.consumer");
597 builder.add_uses("com/example/api/Service");
598 builder.add_uses("java/sql/Driver");
599
600 let bytes = builder.build();
601 let stub = parse_and_extract(&bytes).unwrap().unwrap();
602
603 assert_eq!(stub.uses.len(), 2);
604 assert_eq!(stub.uses[0], "com.example.api.Service");
605 assert_eq!(stub.uses[1], "java.sql.Driver");
606 }
607
608 #[test]
613 fn test_no_module_attribute_returns_none() {
614 let mut bytes = Vec::new();
616
617 bytes.extend_from_slice(&0xCAFE_BABEu32.to_be_bytes());
619 bytes.extend_from_slice(&0u16.to_be_bytes());
621 bytes.extend_from_slice(&52u16.to_be_bytes());
623
624 bytes.extend_from_slice(&5u16.to_be_bytes());
626
627 bytes.push(1);
629 let name = b"com/example/Foo";
630 bytes.extend_from_slice(&(name.len() as u16).to_be_bytes());
631 bytes.extend_from_slice(name);
632
633 bytes.push(7);
635 bytes.extend_from_slice(&1u16.to_be_bytes());
636
637 bytes.push(1);
639 let obj = b"java/lang/Object";
640 bytes.extend_from_slice(&(obj.len() as u16).to_be_bytes());
641 bytes.extend_from_slice(obj);
642
643 bytes.push(7);
645 bytes.extend_from_slice(&3u16.to_be_bytes());
646
647 bytes.extend_from_slice(&0x0021u16.to_be_bytes());
649 bytes.extend_from_slice(&2u16.to_be_bytes());
651 bytes.extend_from_slice(&4u16.to_be_bytes());
653 bytes.extend_from_slice(&0u16.to_be_bytes());
655 bytes.extend_from_slice(&0u16.to_be_bytes());
657 bytes.extend_from_slice(&0u16.to_be_bytes());
659 bytes.extend_from_slice(&0u16.to_be_bytes());
661
662 let result = parse_and_extract(&bytes).unwrap();
663 assert!(result.is_none());
664 }
665
666 #[test]
671 fn test_module_version_and_open_flag() {
672 let builder = ModuleBuilder::new("com.example.open")
673 .module_flags(0x0020) .module_version("1.0.0");
675
676 let bytes = builder.build();
677 let stub = parse_and_extract(&bytes).unwrap().unwrap();
678
679 assert_eq!(stub.name, "com.example.open");
680 assert!(stub.access.contains(0x0020)); assert_eq!(stub.version.as_deref(), Some("1.0.0"));
682 }
683
684 #[test]
689 fn test_qualified_exports() {
690 let mut builder = ModuleBuilder::new("com.example.lib");
691 builder.add_exports(
692 "com/example/internal",
693 0,
694 &["com.example.app", "com.example.test"],
695 );
696
697 let bytes = builder.build();
698 let stub = parse_and_extract(&bytes).unwrap().unwrap();
699
700 assert_eq!(stub.exports.len(), 1);
701 assert_eq!(stub.exports[0].package, "com.example.internal");
702 assert_eq!(stub.exports[0].to_modules.len(), 2);
703 assert_eq!(stub.exports[0].to_modules[0], "com.example.app");
704 assert_eq!(stub.exports[0].to_modules[1], "com.example.test");
705 }
706
707 #[test]
712 fn test_comprehensive_module() {
713 let mut builder = ModuleBuilder::new("com.example.full");
714 builder
715 .add_requires("java.base", 0x8000, Some("17"))
716 .add_requires("java.logging", 0x0020, None)
717 .add_exports("com/example/api", 0, &[])
718 .add_exports("com/example/spi", 0, &["com.example.impl"])
719 .add_opens("com/example/internal", 0, &[])
720 .add_uses("com/example/spi/Plugin")
721 .add_provides(
722 "com/example/spi/Plugin",
723 &["com/example/impl/DefaultPlugin"],
724 );
725
726 let bytes = builder.build();
727 let stub = parse_and_extract(&bytes).unwrap().unwrap();
728
729 assert_eq!(stub.name, "com.example.full");
730 assert_eq!(stub.requires.len(), 2);
731 assert_eq!(stub.exports.len(), 2);
732 assert_eq!(stub.opens.len(), 1);
733 assert_eq!(stub.uses.len(), 1);
734 assert_eq!(stub.uses[0], "com.example.spi.Plugin");
735 assert_eq!(stub.provides.len(), 1);
736 assert_eq!(stub.provides[0].service, "com.example.spi.Plugin");
737 assert_eq!(
738 stub.provides[0].implementations,
739 vec!["com.example.impl.DefaultPlugin"]
740 );
741 }
742
743 #[test]
748 fn test_empty_module() {
749 let builder = ModuleBuilder::new("com.example.empty");
750
751 let bytes = builder.build();
752 let stub = parse_and_extract(&bytes).unwrap().unwrap();
753
754 assert_eq!(stub.name, "com.example.empty");
755 assert!(stub.requires.is_empty());
756 assert!(stub.exports.is_empty());
757 assert!(stub.opens.is_empty());
758 assert!(stub.uses.is_empty());
759 assert!(stub.provides.is_empty());
760 assert!(stub.version.is_none());
761 }
762
763 #[test]
768 fn test_requires_static_phase() {
769 let mut builder = ModuleBuilder::new("com.example.compile");
770 builder.add_requires("org.checkerframework.checker.qual", 0x0040, None);
772
773 let bytes = builder.build();
774 let stub = parse_and_extract(&bytes).unwrap().unwrap();
775
776 assert_eq!(stub.requires.len(), 1);
777 assert_eq!(
778 stub.requires[0].module_name,
779 "org.checkerframework.checker.qual"
780 );
781 assert!(stub.requires[0].access.contains(0x0040)); }
783}