From 01afa6af37f06c35900e44a0454f4b68c0e494e5 Mon Sep 17 00:00:00 2001 From: loki5512344 Date: Tue, 13 Oct 2026 10:23:19 +0200 Subject: [PATCH] jar: pass module-info.class through as a raw resource JPMS descriptors are never loaded by a class loader; parsing one and rebuilding its pool left the unmodeled Module attribute with stale indices, and ASM died in readModule on the rewritten jar (caught by the ASM probe on Loreport, which ships META-INF/versions module-infos). Byte-for-byte resource passthrough keeps the descriptor valid. --- crates/noxis-jar/src/loader.rs | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/crates/noxis-jar/src/loader.rs b/crates/noxis-jar/src/loader.rs index 79ea76f..d2388a7 100644 --- a/crates/noxis-jar/src/loader.rs +++ b/crates/noxis-jar/src/loader.rs @@ -24,7 +24,7 @@ pub fn load(entries: Vec) -> Result { let mut seen: BTreeSet = BTreeSet::new(); for entry in entries { - if is_class_entry(&entry.name) { + if is_class_entry(&entry.name) && !is_module_info(&entry.name) { let class = format::parse(&entry.data)?; if seen.insert(class.this_class.clone()) { program.classes.push(class); @@ -66,6 +66,14 @@ fn is_class_entry(name: &str) -> bool { name.len() > ".class".len() && name.ends_with(".class") } +/// A JPMS descriptor. No class loader ever loads it; parsing would rebuild +/// its pool and leave the unmodeled `Module` attribute with stale indices +/// (ASM then dies in `readModule`), so it travels as a raw resource with +/// its bytes intact. +fn is_module_info(name: &str) -> bool { + name.ends_with("module-info.class") +} + #[cfg(test)] mod tests { #![allow(clippy::expect_used, clippy::unwrap_used)] @@ -132,4 +140,23 @@ mod tests { assert_eq!(program.classes.len(), 1); assert_eq!(program.classes[0].this_class, "demo/App"); } + + /// JPMS descriptors are never class-loaded: they pass through as raw + /// resources (parsing would rebuild their pool and stale the `Module` + /// attribute, which stays unmodeled). + #[test] + fn module_info_passes_through_as_resource() { + let entries = vec![ + Entry::new("META-INF/versions/9/module-info.class", vec![0xCA, 0xFE]), + Entry::new("notes.txt", b"hi".to_vec()), + ]; + + let program = load(entries).expect("load"); + assert!(program.classes.is_empty()); + assert_eq!(program.resources.len(), 2); + assert_eq!( + program.resources[0].path, + "META-INF/versions/9/module-info.class" + ); + } }