classfile,passes,jar: model PermittedSubclasses, normalize interface flags, name class in write errors

This commit is contained in:
Loki 2026-10-10 14:22:03 +02:00
parent e9d5e44b9e
commit 9a33cef9b1
Signed by: boba
GPG key ID: 253067914055423B
11 changed files with 299 additions and 85 deletions

View file

@ -134,6 +134,13 @@ fn rewrite_attributes(
}
}
}
Attribute::PermittedSubclasses(classes) => {
for name in classes.iter_mut() {
if let Some(mapped) = maps.classes.get(name.as_str()) {
*name = mapped.clone();
}
}
}
Attribute::LocalVariableTable(vars) => {
for var in vars.iter_mut() {
var.descriptor = rewrite_descriptor(&var.descriptor, &maps.classes);

View file

@ -109,3 +109,33 @@ fn generic_signatures_rewrite_all_references() {
&format!("Ljava/lang/Object;Ljava/lang/Comparable<L{helper};>;")
);
}
/// Sealed classes keep working: `PermittedSubclasses` entries follow the
/// mapping (a stale entry breaks loading with `ClassFormatError`, and raw
/// pool indices would go stale on rebuild anyway).
#[test]
fn permitted_subclasses_follow_the_mapping() {
use noxis_classfile::{Attribute, ClassBuilder, Version};
let mut program = sample();
program.classes.push(
ClassBuilder::new("app/Sealed", Some("java/lang/Object"))
.version(Version::JAVA_21)
.attribute(Attribute::PermittedSubclasses(vec![
"app/Helper".to_string(),
]))
.build(),
);
let ctx = run_rename(&mut program);
let helper = ctx.mapping.class("app/Helper").expect("renamed");
let sealed_name = ctx.mapping.class("app/Sealed").expect("renamed");
let sealed = program.class(sealed_name).expect("sealed");
let permitted = sealed
.attributes
.iter()
.find_map(|attr| match attr {
Attribute::PermittedSubclasses(names) => Some(names),
_ => None,
})
.expect("permitted");
assert_eq!(permitted, &vec![helper.to_string()]);
}