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

@ -47,6 +47,12 @@ pub(super) fn convert(attr: &RtAttr, pool: &ConstantPool<'_>) -> Result<Attribut
.map(|index| class_name(pool, *index))
.collect::<Result<_, _>>()?,
),
RtAttr::PermittedSubclasses { class_indexes, .. } => Attribute::PermittedSubclasses(
class_indexes
.iter()
.map(|index| class_name(pool, *index))
.collect::<Result<_, _>>()?,
),
RtAttr::EnclosingMethod {
class_index,
method_index,

View file

@ -11,6 +11,21 @@ use super::pool::{class_name, utf8};
use crate::error::ClassfileError;
use crate::model::{Attribute, ClassFile, Code, Field, Method, Version};
/// JVMS 4.1: an interface implies `ABSTRACT` and ignores `SUPER`. Some
/// compilers emit a bare `0x200` that HotSpot tolerates, but the strict
/// backend verifier rejects it on write (after widening, as `0x201`) - so
/// normalize once on parse. Interfaces are abstract by definition: no
/// behaviour changes.
fn normalize_class_flags(flags: u16) -> u16 {
const INTERFACE: u16 = 0x0200;
const SUPER: u16 = 0x0020;
const ABSTRACT: u16 = 0x0400;
if flags & INTERFACE == 0 {
return flags;
}
(flags | ABSTRACT) & !SUPER
}
/// Parse `.class` bytes into our IR.
pub(crate) fn parse_class(bytes: &[u8]) -> Result<ClassFile, ClassfileError> {
let rt = RtClass::from_bytes(bytes).map_err(|e| ClassfileError::Parse(e.to_string()))?;
@ -25,7 +40,7 @@ pub(crate) fn parse_class(bytes: &[u8]) -> Result<ClassFile, ClassfileError> {
minor: rt.version.minor(),
major: rt.version.major(),
},
access_flags: rt.access_flags.bits(),
access_flags: normalize_class_flags(rt.access_flags.bits()),
this_class: class_name(pool, rt.this_class)?,
super_class: optional_class(pool, rt.super_class)?,
interfaces,

View file

@ -145,6 +145,13 @@ pub(super) fn convert(attr: &Attribute, pool: &mut Pool) -> Result<RtAttr, Class
.map(|name| pool.class(name))
.collect::<Result<_, _>>()?,
},
Attribute::PermittedSubclasses(classes) => RtAttr::PermittedSubclasses {
name_index: pool.utf8("PermittedSubclasses")?,
class_indexes: classes
.iter()
.map(|name| pool.class(name))
.collect::<Result<_, _>>()?,
},
Attribute::ConstantValue(value) => RtAttr::ConstantValue {
name_index: pool.utf8("ConstantValue")?,
constant_value_index: pool.constant(value)?,

View file

@ -23,6 +23,9 @@ pub enum Attribute {
NestHost(String),
/// `NestMembers`: the member classes of this nest host (JVMS §4.7.29).
NestMembers(Vec<String>),
/// `PermittedSubclasses`: who may extend a sealed class (JVMS §4.7.31).
/// Stored as names: pool indices go stale when the pool is rebuilt.
PermittedSubclasses(Vec<String>),
/// A field's `ConstantValue` (`static final`): a value from the pool.
ConstantValue(Const),
/// `BootstrapMethods` for `invokedynamic` (lambdas): method + arguments.