classfile,passes: model runtime-visible annotations, rewrite their descriptors
This commit is contained in:
parent
4b14eeb40f
commit
48a5bc23a9
13 changed files with 527 additions and 4 deletions
41
crates/noxis-passes/src/rename/rewrite/annotations.rs
Normal file
41
crates/noxis-passes/src/rename/rewrite/annotations.rs
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
//! Annotation rewriting: class descriptors follow the mapping, string
|
||||
//! values are data and stay put.
|
||||
|
||||
use noxis_classfile::{Annotation, AnnotationValue};
|
||||
|
||||
use super::super::assign::{RenameMaps, rewrite_descriptor};
|
||||
|
||||
/// A list of annotations (visible or invisible) - same rewrite for both.
|
||||
pub(super) fn rewrite_annotations(annotations: &mut [Annotation], maps: &RenameMaps) {
|
||||
for anno in annotations.iter_mut() {
|
||||
rewrite_annotation(anno, maps);
|
||||
}
|
||||
}
|
||||
|
||||
fn rewrite_annotation(anno: &mut Annotation, maps: &RenameMaps) {
|
||||
anno.descriptor = rewrite_descriptor(&anno.descriptor, &maps.classes);
|
||||
for (_, value) in anno.elements.iter_mut() {
|
||||
rewrite_value(value, maps);
|
||||
}
|
||||
}
|
||||
|
||||
fn rewrite_value(value: &mut AnnotationValue, maps: &RenameMaps) {
|
||||
match value {
|
||||
// The `'e'` tag: enum type descriptor moves, the constant name does not.
|
||||
AnnotationValue::Enum { descriptor, .. } => {
|
||||
*descriptor = rewrite_descriptor(descriptor, &maps.classes);
|
||||
}
|
||||
// The `'c'` tag: class-info descriptor (`LFoo;` or a primitive `I`).
|
||||
AnnotationValue::Class(desc) => {
|
||||
*desc = rewrite_descriptor(desc, &maps.classes);
|
||||
}
|
||||
AnnotationValue::Nested(anno) => rewrite_annotation(anno, maps),
|
||||
AnnotationValue::Array(values) => {
|
||||
for value in values.iter_mut() {
|
||||
rewrite_value(value, maps);
|
||||
}
|
||||
}
|
||||
// Scalars and strings hold no class references.
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,9 @@
|
|||
//! Переписывание одного класса: имена, дескрипторы, код, атрибуты.
|
||||
|
||||
mod annotations;
|
||||
mod code;
|
||||
|
||||
use annotations::rewrite_annotations;
|
||||
use noxis_classfile::{Attribute, ClassFile, Const, StackMapFrame, VerificationType};
|
||||
use noxis_core::{Config, Context};
|
||||
|
||||
|
|
@ -141,6 +143,10 @@ fn rewrite_attributes(
|
|||
}
|
||||
}
|
||||
}
|
||||
Attribute::RuntimeVisibleAnnotations(annos)
|
||||
| Attribute::RuntimeInvisibleAnnotations(annos) => {
|
||||
rewrite_annotations(annos, maps);
|
||||
}
|
||||
Attribute::BootstrapMethods(methods) => {
|
||||
for bsm in methods.iter_mut() {
|
||||
rewrite_method_ref(&mut bsm.owner, &mut bsm.name, &mut bsm.descriptor, maps);
|
||||
|
|
|
|||
100
crates/noxis-passes/src/rename/tests/annotations.rs
Normal file
100
crates/noxis-passes/src/rename/tests/annotations.rs
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
//! Annotation rewriting: descriptors follow the mapping, values do not.
|
||||
|
||||
#![allow(clippy::expect_used, clippy::unwrap_used)]
|
||||
|
||||
use super::run_rename;
|
||||
use noxis_classfile::{Annotation, AnnotationValue, Attribute, ClassBuilder, Version};
|
||||
|
||||
/// Class references inside annotations live as descriptors: a stale one
|
||||
/// makes the JVM's reflection parser fail (`AnnotationFormatError`) or
|
||||
/// resolve a ghost type. String *values* are data and stay put.
|
||||
#[test]
|
||||
fn annotation_descriptors_follow_the_mapping() {
|
||||
let mut program = super::sample();
|
||||
program.classes.push(
|
||||
ClassBuilder::new("app/Annotated", Some("java/lang/Object"))
|
||||
.version(Version::JAVA_21)
|
||||
.method_with_code(0x000a, "go", "()V", noxis_classfile::empty_code(0))
|
||||
.build(),
|
||||
);
|
||||
let annotated = program
|
||||
.classes
|
||||
.iter_mut()
|
||||
.find(|c| c.this_class == "app/Annotated")
|
||||
.expect("annotated");
|
||||
annotated
|
||||
.attributes
|
||||
.push(Attribute::RuntimeVisibleAnnotations(vec![Annotation {
|
||||
descriptor: "Lapp/Helper;".to_string(),
|
||||
elements: vec![
|
||||
(
|
||||
"kind".to_string(),
|
||||
AnnotationValue::Enum {
|
||||
descriptor: "Lapp/Helper;".to_string(),
|
||||
name: "ONE".to_string(),
|
||||
},
|
||||
),
|
||||
(
|
||||
"klass".to_string(),
|
||||
AnnotationValue::Class("Lapp/Helper;".to_string()),
|
||||
),
|
||||
(
|
||||
"nested".to_string(),
|
||||
AnnotationValue::Nested(Box::new(Annotation {
|
||||
descriptor: "Lapp/Helper;".to_string(),
|
||||
elements: vec![(
|
||||
"note".to_string(),
|
||||
AnnotationValue::String("app/Helper".to_string()),
|
||||
)],
|
||||
})),
|
||||
),
|
||||
(
|
||||
"arr".to_string(),
|
||||
AnnotationValue::Array(vec![AnnotationValue::Class(
|
||||
"[Lapp/Helper;".to_string(),
|
||||
)]),
|
||||
),
|
||||
],
|
||||
}]));
|
||||
let ctx = run_rename(&mut program);
|
||||
let helper = ctx.mapping.class("app/Helper").expect("renamed");
|
||||
let annotated_name = ctx.mapping.class("app/Annotated").expect("annotated");
|
||||
let class = program.class(annotated_name).expect("class");
|
||||
let annos = class
|
||||
.attributes
|
||||
.iter()
|
||||
.find_map(|a| match a {
|
||||
Attribute::RuntimeVisibleAnnotations(list) => Some(list),
|
||||
_ => None,
|
||||
})
|
||||
.expect("annotations");
|
||||
let anno = &annos[0];
|
||||
assert_eq!(anno.descriptor, format!("L{helper};"));
|
||||
let expect_desc = format!("L{helper};");
|
||||
assert_eq!(
|
||||
anno.elements[0].1,
|
||||
AnnotationValue::Enum {
|
||||
descriptor: expect_desc.clone(),
|
||||
name: "ONE".to_string(),
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
anno.elements[1].1,
|
||||
AnnotationValue::Class(expect_desc.clone())
|
||||
);
|
||||
match &anno.elements[2].1 {
|
||||
AnnotationValue::Nested(nested) => {
|
||||
assert_eq!(nested.descriptor, expect_desc);
|
||||
assert_eq!(
|
||||
nested.elements[0].1,
|
||||
AnnotationValue::String("app/Helper".to_string()),
|
||||
"string values are data, not references"
|
||||
);
|
||||
}
|
||||
other => panic!("nested expected, got {other:?}"),
|
||||
}
|
||||
assert_eq!(
|
||||
anno.elements[3].1,
|
||||
AnnotationValue::Array(vec![AnnotationValue::Class(format!("[L{helper};"))])
|
||||
);
|
||||
}
|
||||
|
|
@ -13,6 +13,7 @@ use noxis_core::{Config, Context, Pass, RenameConfig};
|
|||
use super::RenamePass;
|
||||
use super::assign::{RenameMaps, rewrite_descriptor};
|
||||
|
||||
mod annotations;
|
||||
mod enclosing;
|
||||
mod rewrite;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue