diff --git a/crates/noxis-classfile/src/format/parse/attrs.rs b/crates/noxis-classfile/src/format/parse/attrs.rs index af4aec6..8dc3c94 100644 --- a/crates/noxis-classfile/src/format/parse/attrs.rs +++ b/crates/noxis-classfile/src/format/parse/attrs.rs @@ -38,6 +38,15 @@ pub(super) fn convert(attr: &RtAttr, pool: &ConstantPool<'_>) -> 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::>()?, + ), RtAttr::EnclosingMethod { class_index, method_index, diff --git a/crates/noxis-classfile/src/format/write/attrs.rs b/crates/noxis-classfile/src/format/write/attrs.rs index cd64694..aa45119 100644 --- a/crates/noxis-classfile/src/format/write/attrs.rs +++ b/crates/noxis-classfile/src/format/write/attrs.rs @@ -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>()?, + }, + 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::>()?, }, Attribute::ConstantValue(value) => RtAttr::ConstantValue { @@ -146,7 +153,7 @@ pub(super) fn convert(attr: &Attribute, pool: &mut Pool) -> Result>()?, }, Attribute::EnclosingMethod { class, method } => RtAttr::EnclosingMethod { @@ -161,7 +168,7 @@ pub(super) fn convert(attr: &Attribute, pool: &mut Pool) -> Result>()?, }, Attribute::Raw { name, data } => RtAttr::Unknown { @@ -194,57 +201,3 @@ pub(super) fn convert(attr: &Attribute, pool: &mut Pool) -> Result Result { - 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 { - 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::>()?, - }) -} - -/// Аргумент bootstrap-метода: те же константы, что в `ldc`, плюс `Dynamic`. -fn bootstrap_arg(arg: &Const, pool: &mut Pool) -> Result { - match arg { - Const::Dynamic { - name, - descriptor, - bootstrap, - } => pool.dynamic(*bootstrap, name, descriptor), - _ => pool.constant(arg), - } -} - -fn method_param(param: &MethodParameter, pool: &mut Pool) -> Result { - Ok(RtParam { - name_index: match ¶m.name { - None => 0, - Some(name) => pool.utf8(name)?, - }, - access_flags: MethodAccessFlags::from_bits_truncate(param.access_flags), - }) -} diff --git a/crates/noxis-classfile/src/format/write/helpers.rs b/crates/noxis-classfile/src/format/write/helpers.rs new file mode 100644 index 0000000..f314cf1 --- /dev/null +++ b/crates/noxis-classfile/src/format/write/helpers.rs @@ -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 { + 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 { + 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::>()?, + }) +} + +/// Аргумент bootstrap-метода: те же константы, что в `ldc`, плюс `Dynamic`. +fn bootstrap_arg(arg: &Const, pool: &mut Pool) -> Result { + 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 { + Ok(RtParam { + name_index: match ¶m.name { + None => 0, + Some(name) => pool.utf8(name)?, + }, + access_flags: MethodAccessFlags::from_bits_truncate(param.access_flags), + }) +} diff --git a/crates/noxis-classfile/src/format/write/mod.rs b/crates/noxis-classfile/src/format/write/mod.rs index 02813e6..3561059 100644 --- a/crates/noxis-classfile/src/format/write/mod.rs +++ b/crates/noxis-classfile/src/format/write/mod.rs @@ -5,6 +5,7 @@ mod attrs; pub(crate) mod code; +mod helpers; pub(crate) mod pool; use ristretto_classfile::{ diff --git a/crates/noxis-classfile/src/model/mod.rs b/crates/noxis-classfile/src/model/mod.rs index 2674775..19510bb 100644 --- a/crates/noxis-classfile/src/model/mod.rs +++ b/crates/noxis-classfile/src/model/mod.rs @@ -19,6 +19,10 @@ pub enum Attribute { Deprecated, Synthetic, InnerClasses(Vec), + /// `NestHost`: класс, служащий nest-хостом (JVMS §4.7.28). + NestHost(String), + /// `NestMembers`: классы-члены этого nest-хоста (JVMS §4.7.29). + NestMembers(Vec), /// `ConstantValue` поля (`static final`): значение из пула. ConstantValue(Const), /// `BootstrapMethods` для `invokedynamic` (лямбды): метод + аргументы. diff --git a/crates/noxis-classfile/tests/roundtrip.rs b/crates/noxis-classfile/tests/roundtrip.rs index f24d27b..bce5a28 100644 --- a/crates/noxis-classfile/tests/roundtrip.rs +++ b/crates/noxis-classfile/tests/roundtrip.rs @@ -205,6 +205,30 @@ fn bootstrap_methods_round_trip() { assert!(format!("{:?}", ir.attributes[0]).contains("BootstrapMethods")); } +/// `NestMembers` переживает пересборку пула (баг: `ClassFormatError`). +#[test] +fn nest_attributes_round_trip() { + let mut pool = ConstantPool::default(); + let this = pool.add_class("demo/Nest").expect("c"); + let object = pool.add_class("java/lang/Object").expect("o"); + let member = pool.add_class("demo/Nest$Inner").expect("m"); + let mn = pool.add_utf8("NestMembers").expect("n"); + let mems = RtAttr::NestMembers { + name_index: mn, + class_indexes: vec![this, member], + }; + let rt = RtClass { + version: JAVA_21, + constant_pool: pool, + this_class: this, + super_class: object, + attributes: vec![mems], + ..RtClass::default() + }; + let ir = round_trip(&rt); + assert!(format!("{:?}", ir.attributes).contains("NestMembers")); +} + /// `parse → write → parse` даёт тот же IR; записанное верифицируется. fn round_trip(rt: &RtClass<'_>) -> ClassFile { let mut original = Vec::new(); diff --git a/crates/noxis-passes/src/rename/rewrite/mod.rs b/crates/noxis-passes/src/rename/rewrite/mod.rs index 668285d..d10be8a 100644 --- a/crates/noxis-passes/src/rename/rewrite/mod.rs +++ b/crates/noxis-passes/src/rename/rewrite/mod.rs @@ -122,6 +122,18 @@ fn rewrite_attributes( } } } + Attribute::NestHost(host) => { + if let Some(mapped) = maps.classes.get(host.as_str()) { + *host = mapped.clone(); + } + } + Attribute::NestMembers(members) => { + for name in members.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);