classfile: model RuntimeVisible/InvisibleParameterAnnotations (fixes ASM AIOOBE on write)

Parameter annotations were unmodeled and fell through to Raw with stale
constant-pool indices after the pool rebuild. ASM (Paper converter, Fabric
mixin transformer) then failed with ArrayIndexOutOfBoundsException in
readParameterAnnotations. Now parsed and rewritten like class-level
annotations.
This commit is contained in:
Loki 2026-10-12 13:19:12 +02:00
parent 045cd3ed9c
commit b73c72d56d
Signed by: boba
GPG key ID: 253067914055423B
5 changed files with 80 additions and 1 deletions

View file

@ -123,6 +123,36 @@ pub(super) fn convert(attr: &RtAttr, pool: &ConstantPool<'_>) -> Result<Attribut
.collect::<Result<_, _>>()?,
)
}
RtAttr::RuntimeVisibleParameterAnnotations {
parameter_annotations,
..
} => Attribute::RuntimeVisibleParameterAnnotations(
parameter_annotations
.iter()
.map(|param| {
param
.annotations
.iter()
.map(|anno| annotation(pool, anno))
.collect::<Result<_, _>>()
})
.collect::<Result<_, _>>()?,
),
RtAttr::RuntimeInvisibleParameterAnnotations {
parameter_annotations,
..
} => Attribute::RuntimeInvisibleParameterAnnotations(
parameter_annotations
.iter()
.map(|param| {
param
.annotations
.iter()
.map(|anno| annotation(pool, anno))
.collect::<Result<_, _>>()
})
.collect::<Result<_, _>>()?,
),
other => raw(other)?,
})
}

View file

@ -193,6 +193,38 @@ pub(super) fn convert(attr: &Attribute, pool: &mut Pool) -> Result<RtAttr, Class
.map(|anno| annotation(anno, pool))
.collect::<Result<_, _>>()?,
},
Attribute::RuntimeVisibleParameterAnnotations(params) => {
RtAttr::RuntimeVisibleParameterAnnotations {
name_index: pool.utf8("RuntimeVisibleParameterAnnotations")?,
parameter_annotations: params
.iter()
.map(|param| {
Ok(ristretto_classfile::attributes::ParameterAnnotation {
annotations: param
.iter()
.map(|anno| annotation(anno, pool))
.collect::<Result<_, _>>()?,
})
})
.collect::<Result<_, _>>()?,
}
}
Attribute::RuntimeInvisibleParameterAnnotations(params) => {
RtAttr::RuntimeInvisibleParameterAnnotations {
name_index: pool.utf8("RuntimeInvisibleParameterAnnotations")?,
parameter_annotations: params
.iter()
.map(|param| {
Ok(ristretto_classfile::attributes::ParameterAnnotation {
annotations: param
.iter()
.map(|anno| annotation(anno, pool))
.collect::<Result<_, _>>()?,
})
})
.collect::<Result<_, _>>()?,
}
}
Attribute::Raw { name, data } => RtAttr::Unknown {
name_index: pool.utf8(name)?,
info: data.clone(),

View file

@ -48,6 +48,12 @@ pub enum Attribute {
RuntimeVisibleAnnotations(Vec<Annotation>),
/// `RuntimeInvisibleAnnotations` (JVMS §4.7.17): CLASS-retention ones.
RuntimeInvisibleAnnotations(Vec<Annotation>),
/// `RuntimeVisibleParameterAnnotations` (JVMS §4.7.18): per-parameter
/// annotations. Parsed like the class-level ones - raw pool indices go
/// stale when the pool is rebuilt.
RuntimeVisibleParameterAnnotations(Vec<Vec<Annotation>>),
/// `RuntimeInvisibleParameterAnnotations` (JVMS §4.7.19).
RuntimeInvisibleParameterAnnotations(Vec<Vec<Annotation>>),
/// Everything else: name + raw `info` bytes (careful with pool indices).
Raw {
name: String,