classfile,passes: model Nest attributes as IR (fix ClassFormatError)
NestHost/NestMembers were carried as Attribute::Raw with constant-pool indices baked in. During writing the pool is rebuilt in a different order, so those indices pointed at the wrong entries and the JVM rejected the class: ClassFormatError: Nest member/host class_info_index N has bad constant type. Model them explicitly in the IR, convert on parse and write (resolving class names through the new pool), and remap their class names during rename. Add a NestMembers round-trip regression test. Verified on a real jar: all Nest ClassFormatErrors gone.
This commit is contained in:
parent
8b107cf1b3
commit
2674d7fc89
7 changed files with 145 additions and 66 deletions
|
|
@ -38,6 +38,15 @@ pub(super) fn convert(attr: &RtAttr, pool: &ConstantPool<'_>) -> Result<Attribut
|
|||
.map(|class| inner(pool, class))
|
||||
.collect::<Result<_, _>>()?,
|
||||
),
|
||||
RtAttr::NestHost {
|
||||
host_class_index, ..
|
||||
} => Attribute::NestHost(class_name(pool, *host_class_index)?),
|
||||
RtAttr::NestMembers { class_indexes, .. } => Attribute::NestMembers(
|
||||
class_indexes
|
||||
.iter()
|
||||
.map(|index| class_name(pool, *index))
|
||||
.collect::<Result<_, _>>()?,
|
||||
),
|
||||
RtAttr::EnclosingMethod {
|
||||
class_index,
|
||||
method_index,
|
||||
|
|
|
|||
|
|
@ -3,20 +3,16 @@
|
|||
//! Имена атрибутов (`SourceFile`, …) интернируются в пул здесь же — backend
|
||||
//! хранит только `name_index`, строка восстанавливается при чтении.
|
||||
|
||||
use ristretto_classfile::MethodAccessFlags;
|
||||
use ristretto_classfile::attributes::{
|
||||
Attribute as RtAttr, BootstrapMethod as RtBsm, InnerClass as RtInner, LineNumber as RtLine,
|
||||
LocalVariableTable as RtLocal, LocalVariableTypeTable as RtLocalType,
|
||||
MethodParameter as RtParam, NestedClassAccessFlags, Record as RtRecord,
|
||||
Attribute as RtAttr, LineNumber as RtLine, LocalVariableTable as RtLocal,
|
||||
LocalVariableTypeTable as RtLocalType, Record as RtRecord,
|
||||
};
|
||||
|
||||
use super::helpers;
|
||||
use super::pool::Pool;
|
||||
use crate::error::ClassfileError;
|
||||
use crate::insn::{Const, Label};
|
||||
use crate::model::{
|
||||
Attribute, BootstrapMethod, InnerClass, LineNumber, LocalVariable, LocalVariableType,
|
||||
MethodParameter,
|
||||
};
|
||||
use crate::insn::Label;
|
||||
use crate::model::{Attribute, LineNumber, LocalVariable, LocalVariableType};
|
||||
|
||||
/// Позиции таблиц кода: метки → байты текущего layout.
|
||||
pub(super) struct Positions<'a> {
|
||||
|
|
@ -135,7 +131,18 @@ pub(super) fn convert(attr: &Attribute, pool: &mut Pool) -> Result<RtAttr, Class
|
|||
name_index: pool.utf8("InnerClasses")?,
|
||||
classes: classes
|
||||
.iter()
|
||||
.map(|class| inner(class, pool))
|
||||
.map(|class| helpers::inner(class, pool))
|
||||
.collect::<Result<_, _>>()?,
|
||||
},
|
||||
Attribute::NestHost(host) => RtAttr::NestHost {
|
||||
name_index: pool.utf8("NestHost")?,
|
||||
host_class_index: pool.class(host)?,
|
||||
},
|
||||
Attribute::NestMembers(members) => RtAttr::NestMembers {
|
||||
name_index: pool.utf8("NestMembers")?,
|
||||
class_indexes: members
|
||||
.iter()
|
||||
.map(|name| pool.class(name))
|
||||
.collect::<Result<_, _>>()?,
|
||||
},
|
||||
Attribute::ConstantValue(value) => RtAttr::ConstantValue {
|
||||
|
|
@ -146,7 +153,7 @@ pub(super) fn convert(attr: &Attribute, pool: &mut Pool) -> Result<RtAttr, Class
|
|||
name_index: pool.utf8("BootstrapMethods")?,
|
||||
methods: methods
|
||||
.iter()
|
||||
.map(|method| bootstrap(method, pool))
|
||||
.map(|method| helpers::bootstrap(method, pool))
|
||||
.collect::<Result<_, _>>()?,
|
||||
},
|
||||
Attribute::EnclosingMethod { class, method } => RtAttr::EnclosingMethod {
|
||||
|
|
@ -161,7 +168,7 @@ pub(super) fn convert(attr: &Attribute, pool: &mut Pool) -> Result<RtAttr, Class
|
|||
name_index: pool.utf8("MethodParameters")?,
|
||||
parameters: params
|
||||
.iter()
|
||||
.map(|param| method_param(param, pool))
|
||||
.map(|param| helpers::method_param(param, pool))
|
||||
.collect::<Result<_, _>>()?,
|
||||
},
|
||||
Attribute::Raw { name, data } => RtAttr::Unknown {
|
||||
|
|
@ -194,57 +201,3 @@ pub(super) fn convert(attr: &Attribute, pool: &mut Pool) -> Result<RtAttr, Class
|
|||
},
|
||||
})
|
||||
}
|
||||
|
||||
fn inner(class: &InnerClass, pool: &mut Pool) -> Result<RtInner, ClassfileError> {
|
||||
Ok(RtInner {
|
||||
class_info_index: pool.class(&class.inner)?,
|
||||
outer_class_info_index: match &class.outer {
|
||||
None => 0,
|
||||
Some(name) => pool.class(name)?,
|
||||
},
|
||||
name_index: match &class.inner_name {
|
||||
None => 0,
|
||||
Some(name) => pool.utf8(name)?,
|
||||
},
|
||||
access_flags: NestedClassAccessFlags::from_bits_truncate(class.access_flags),
|
||||
})
|
||||
}
|
||||
|
||||
fn bootstrap(method: &BootstrapMethod, pool: &mut Pool) -> Result<RtBsm, ClassfileError> {
|
||||
Ok(RtBsm {
|
||||
bootstrap_method_ref: pool.method_handle(
|
||||
method.kind,
|
||||
&method.owner,
|
||||
&method.name,
|
||||
&method.descriptor,
|
||||
method.itf,
|
||||
)?,
|
||||
arguments: method
|
||||
.args
|
||||
.iter()
|
||||
.map(|arg| bootstrap_arg(arg, pool))
|
||||
.collect::<Result<_, _>>()?,
|
||||
})
|
||||
}
|
||||
|
||||
/// Аргумент bootstrap-метода: те же константы, что в `ldc`, плюс `Dynamic`.
|
||||
fn bootstrap_arg(arg: &Const, pool: &mut Pool) -> Result<u16, ClassfileError> {
|
||||
match arg {
|
||||
Const::Dynamic {
|
||||
name,
|
||||
descriptor,
|
||||
bootstrap,
|
||||
} => pool.dynamic(*bootstrap, name, descriptor),
|
||||
_ => pool.constant(arg),
|
||||
}
|
||||
}
|
||||
|
||||
fn method_param(param: &MethodParameter, pool: &mut Pool) -> Result<RtParam, ClassfileError> {
|
||||
Ok(RtParam {
|
||||
name_index: match ¶m.name {
|
||||
None => 0,
|
||||
Some(name) => pool.utf8(name)?,
|
||||
},
|
||||
access_flags: MethodAccessFlags::from_bits_truncate(param.access_flags),
|
||||
})
|
||||
}
|
||||
|
|
|
|||
76
crates/noxis-classfile/src/format/write/helpers.rs
Normal file
76
crates/noxis-classfile/src/format/write/helpers.rs
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
//! Хелперы сборки `InnerClasses`/`BootstrapMethods`/`MethodParameters`.
|
||||
//!
|
||||
//! Вынесены из `attrs`, чтобы тот держался в лимите 250 строк; логика не
|
||||
//! изменилась — только место. Каждый строит запись backend'а из элемента IR,
|
||||
//! интернируя имена/дескрипторы и резолвя индексы `CONSTANT_Class` через пул.
|
||||
|
||||
use ristretto_classfile::MethodAccessFlags;
|
||||
use ristretto_classfile::attributes::{
|
||||
BootstrapMethod as RtBsm, InnerClass as RtInner, MethodParameter as RtParam,
|
||||
NestedClassAccessFlags,
|
||||
};
|
||||
|
||||
use super::pool::Pool;
|
||||
use crate::error::ClassfileError;
|
||||
use crate::insn::Const;
|
||||
use crate::model::{BootstrapMethod, InnerClass, MethodParameter};
|
||||
|
||||
pub(super) fn inner(class: &InnerClass, pool: &mut Pool) -> Result<RtInner, ClassfileError> {
|
||||
Ok(RtInner {
|
||||
class_info_index: pool.class(&class.inner)?,
|
||||
outer_class_info_index: match &class.outer {
|
||||
None => 0,
|
||||
Some(name) => pool.class(name)?,
|
||||
},
|
||||
name_index: match &class.inner_name {
|
||||
None => 0,
|
||||
Some(name) => pool.utf8(name)?,
|
||||
},
|
||||
access_flags: NestedClassAccessFlags::from_bits_truncate(class.access_flags),
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn bootstrap(
|
||||
method: &BootstrapMethod,
|
||||
pool: &mut Pool,
|
||||
) -> Result<RtBsm, ClassfileError> {
|
||||
Ok(RtBsm {
|
||||
bootstrap_method_ref: pool.method_handle(
|
||||
method.kind,
|
||||
&method.owner,
|
||||
&method.name,
|
||||
&method.descriptor,
|
||||
method.itf,
|
||||
)?,
|
||||
arguments: method
|
||||
.args
|
||||
.iter()
|
||||
.map(|arg| bootstrap_arg(arg, pool))
|
||||
.collect::<Result<_, _>>()?,
|
||||
})
|
||||
}
|
||||
|
||||
/// Аргумент bootstrap-метода: те же константы, что в `ldc`, плюс `Dynamic`.
|
||||
fn bootstrap_arg(arg: &Const, pool: &mut Pool) -> Result<u16, ClassfileError> {
|
||||
match arg {
|
||||
Const::Dynamic {
|
||||
name,
|
||||
descriptor,
|
||||
bootstrap,
|
||||
} => pool.dynamic(*bootstrap, name, descriptor),
|
||||
_ => pool.constant(arg),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn method_param(
|
||||
param: &MethodParameter,
|
||||
pool: &mut Pool,
|
||||
) -> Result<RtParam, ClassfileError> {
|
||||
Ok(RtParam {
|
||||
name_index: match ¶m.name {
|
||||
None => 0,
|
||||
Some(name) => pool.utf8(name)?,
|
||||
},
|
||||
access_flags: MethodAccessFlags::from_bits_truncate(param.access_flags),
|
||||
})
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
mod attrs;
|
||||
pub(crate) mod code;
|
||||
mod helpers;
|
||||
pub(crate) mod pool;
|
||||
|
||||
use ristretto_classfile::{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue