passes: rename rewrites EnclosingMethod and array-owner method refs
This commit is contained in:
parent
8fcecee849
commit
5901c9eb69
4 changed files with 116 additions and 13 deletions
|
|
@ -6,6 +6,7 @@ use noxis_core::{Config, Context};
|
||||||
use super::super::assign::{RenameMaps, rewrite_descriptor};
|
use super::super::assign::{RenameMaps, rewrite_descriptor};
|
||||||
use super::rewrite_attributes;
|
use super::rewrite_attributes;
|
||||||
use super::rewrite_frame;
|
use super::rewrite_frame;
|
||||||
|
use super::rewrite_method_ref;
|
||||||
|
|
||||||
pub(super) fn rewrite_code(
|
pub(super) fn rewrite_code(
|
||||||
code: &mut Code,
|
code: &mut Code,
|
||||||
|
|
@ -59,13 +60,7 @@ pub(super) fn rewrite_code(
|
||||||
descriptor,
|
descriptor,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
if let Some(new_name) = maps.method(owner, name, descriptor) {
|
rewrite_method_ref(owner, name, descriptor, maps);
|
||||||
*name = new_name;
|
|
||||||
}
|
|
||||||
if let Some(mapped) = maps.classes.get(owner.as_str()) {
|
|
||||||
*owner = mapped.clone();
|
|
||||||
}
|
|
||||||
*descriptor = rewrite_descriptor(descriptor, &maps.classes);
|
|
||||||
}
|
}
|
||||||
// The call-site name belongs to the functional interface and
|
// The call-site name belongs to the functional interface and
|
||||||
// stays; the descriptor (captured receiver types) is rewritten,
|
// stays; the descriptor (captured receiver types) is rewritten,
|
||||||
|
|
|
||||||
|
|
@ -149,6 +149,14 @@ fn rewrite_attributes(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Attribute::EnclosingMethod { class, method } => {
|
||||||
|
if let Some((name, descriptor)) = method.as_mut() {
|
||||||
|
rewrite_method_ref(class, name, descriptor, maps);
|
||||||
|
}
|
||||||
|
if let Some(mapped) = maps.classes.get(class.as_str()) {
|
||||||
|
*class = mapped.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
Attribute::LocalVariableTable(vars) => {
|
Attribute::LocalVariableTable(vars) => {
|
||||||
for var in vars.iter_mut() {
|
for var in vars.iter_mut() {
|
||||||
var.descriptor = rewrite_descriptor(&var.descriptor, &maps.classes);
|
var.descriptor = rewrite_descriptor(&var.descriptor, &maps.classes);
|
||||||
|
|
@ -174,19 +182,24 @@ fn rewrite_attributes(
|
||||||
|
|
||||||
/// A method reference (invoke/bootstrap/method-handle): the owner follows
|
/// A method reference (invoke/bootstrap/method-handle): the owner follows
|
||||||
/// class renames, the name follows member renames, the descriptor is
|
/// class renames, the name follows member renames, the descriptor is
|
||||||
/// rewritten. Callers decide whether the name may move at all.
|
/// rewritten. Array owners (`[Lpkg/C;` - e.g. `clone` on arrays) name the
|
||||||
|
/// element class inside a descriptor, so they take the descriptor path.
|
||||||
fn rewrite_method_ref(
|
fn rewrite_method_ref(
|
||||||
owner: &mut String,
|
owner: &mut String,
|
||||||
name: &mut String,
|
name: &mut String,
|
||||||
descriptor: &mut String,
|
descriptor: &mut String,
|
||||||
maps: &RenameMaps,
|
maps: &RenameMaps,
|
||||||
) {
|
) {
|
||||||
|
if owner.starts_with('[') {
|
||||||
|
*owner = rewrite_descriptor(owner, &maps.classes);
|
||||||
|
} else {
|
||||||
if let Some(new_name) = maps.method(owner, name, descriptor) {
|
if let Some(new_name) = maps.method(owner, name, descriptor) {
|
||||||
*name = new_name;
|
*name = new_name;
|
||||||
}
|
}
|
||||||
if let Some(mapped) = maps.classes.get(owner.as_str()) {
|
if let Some(mapped) = maps.classes.get(owner.as_str()) {
|
||||||
*owner = mapped.clone();
|
*owner = mapped.clone();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
*descriptor = rewrite_descriptor(descriptor, &maps.classes);
|
*descriptor = rewrite_descriptor(descriptor, &maps.classes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
94
crates/noxis-passes/src/rename/tests/enclosing.rs
Normal file
94
crates/noxis-passes/src/rename/tests/enclosing.rs
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
//! Local-class attributes (`EnclosingMethod`) and array-owner references.
|
||||||
|
|
||||||
|
#![allow(clippy::expect_used, clippy::unwrap_used)]
|
||||||
|
|
||||||
|
use super::{run_rename, sample};
|
||||||
|
use noxis_classfile::Insn;
|
||||||
|
|
||||||
|
/// `EnclosingMethod` (local/anonymous classes) keeps a class + optional
|
||||||
|
/// method pair: a stale class here breaks reflection on the local class
|
||||||
|
/// and, with certain enum patterns, class initialization itself.
|
||||||
|
#[test]
|
||||||
|
fn enclosing_method_follows_the_mapping() {
|
||||||
|
use noxis_classfile::{Attribute, ClassBuilder, Version};
|
||||||
|
let mut program = sample();
|
||||||
|
program.classes.push(
|
||||||
|
ClassBuilder::new("app/Local", Some("java/lang/Object"))
|
||||||
|
.version(Version::JAVA_21)
|
||||||
|
.method_with_code(0x000a, "go", "()V", noxis_classfile::empty_code(0))
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
let local = program
|
||||||
|
.classes
|
||||||
|
.iter_mut()
|
||||||
|
.find(|c| c.this_class == "app/Local")
|
||||||
|
.expect("local");
|
||||||
|
local.attributes.push(Attribute::EnclosingMethod {
|
||||||
|
class: "app/Helper".to_string(),
|
||||||
|
method: Some(("make".to_string(), "()Lapp/Helper;".to_string())),
|
||||||
|
});
|
||||||
|
let ctx = run_rename(&mut program);
|
||||||
|
let helper = ctx.mapping.class("app/Helper").expect("renamed");
|
||||||
|
let local_name = ctx.mapping.class("app/Local").expect("local renamed");
|
||||||
|
let local = program.class(local_name).expect("renamed class");
|
||||||
|
let attr = local
|
||||||
|
.attributes
|
||||||
|
.iter()
|
||||||
|
.find_map(|a| match a {
|
||||||
|
Attribute::EnclosingMethod { class, method } => Some((class, method)),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.expect("enclosing");
|
||||||
|
assert_eq!(attr.0, helper);
|
||||||
|
let (name, desc) = attr.1.as_ref().expect("method");
|
||||||
|
assert_ne!(name, "make");
|
||||||
|
assert_eq!(desc, &format!("()L{helper};"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `invokevirtual` on an array (`[Lpkg/C;`.clone) names the *array* class
|
||||||
|
/// as owner: the mapping key is the element class, so a plain lookup misses
|
||||||
|
/// and the array component goes stale - enum `values()` clones break.
|
||||||
|
#[test]
|
||||||
|
fn array_owner_method_refs_follow_their_element() {
|
||||||
|
use noxis_classfile::InvokeKind;
|
||||||
|
let mut program = sample();
|
||||||
|
let main = program
|
||||||
|
.classes
|
||||||
|
.iter_mut()
|
||||||
|
.find(|c| c.this_class == "app/Main")
|
||||||
|
.expect("main");
|
||||||
|
let probe = main
|
||||||
|
.methods
|
||||||
|
.iter_mut()
|
||||||
|
.find(|m| m.name == "main")
|
||||||
|
.expect("main method");
|
||||||
|
let code = probe.code.as_mut().expect("code");
|
||||||
|
code.insns.push(Insn::Invoke {
|
||||||
|
kind: InvokeKind::Virtual,
|
||||||
|
owner: "[Lapp/Helper;".to_string(),
|
||||||
|
name: "clone".to_string(),
|
||||||
|
descriptor: "()Ljava/lang/Object;".to_string(),
|
||||||
|
itf: false,
|
||||||
|
});
|
||||||
|
code.insns.push(Insn::Pop);
|
||||||
|
let ctx = run_rename(&mut program);
|
||||||
|
let helper = ctx.mapping.class("app/Helper").expect("renamed");
|
||||||
|
let main = program.class("app/Main").expect("kept");
|
||||||
|
let method = main
|
||||||
|
.methods
|
||||||
|
.iter()
|
||||||
|
.find(|m| m.name == "main")
|
||||||
|
.expect("main");
|
||||||
|
let invoke = method
|
||||||
|
.code
|
||||||
|
.as_ref()
|
||||||
|
.expect("code")
|
||||||
|
.insns
|
||||||
|
.iter()
|
||||||
|
.find_map(|i| match i {
|
||||||
|
Insn::Invoke { owner, name, .. } if name == "clone" => Some(owner),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.expect("invoke");
|
||||||
|
assert_eq!(invoke, &format!("[L{helper};"));
|
||||||
|
}
|
||||||
|
|
@ -13,6 +13,7 @@ use noxis_core::{Config, Context, Pass, RenameConfig};
|
||||||
use super::RenamePass;
|
use super::RenamePass;
|
||||||
use super::assign::{RenameMaps, rewrite_descriptor};
|
use super::assign::{RenameMaps, rewrite_descriptor};
|
||||||
|
|
||||||
|
mod enclosing;
|
||||||
mod rewrite;
|
mod rewrite;
|
||||||
|
|
||||||
fn sample() -> Program {
|
fn sample() -> Program {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue