使用 ASM 库覆盖 Java 字节码中的局部变量名称
Overriding a Local Variable name in Java Bytecode using the ASM library
我使用 Java 字节码的 ASM 库阅读了 class' 方法及其内容。这是在 class:
中输出任何方法的局部变量名称
ClassReader reader = new ClassReader(new FileInputStream(new File("TheClass.class")));
final ClassNode classNode = new ClassNode();
reader.accept(classNode, 0);
for (final MethodNode mn : (List<MethodNode>)classNode.methods) {
for (LocalVariableNode n : (List<LocalVariableNode>)mn.localVariables) {
System.out.println(n.name);
}
}
这是编译后的 TheClass
class 文件的来源:
public class TheClass {
public final String a;
public final String b;
public TheClass(String c, String d) {
this.b = d;
this.a = c;
}
}
所以输出在逻辑上是 this
、c
、d
。 现在,我需要将这个已编译的 class 复制到一个新文件中,但将 <init>
方法的参数(局部变量)更改为不同的名称(e
、f
).我该怎么做? 我对 MethodVisitors 等几乎没有经验。
您需要编写一个适配器(ClassVisitor
的子类)并将其与 reader
链接起来。例如,
ClassReader reader = new ClassReader(new FileInputStream(new File("TheClass")));
ClassWriter writer = new ClassWriter(reader, 0);
TraceClassVisitor printer = new TraceClassVisitor(writer,
new PrintWriter(System.getProperty("java.io.tmpdir")
+ File.separator + name + ".log"));
ClassAdapter adapter = new ClassAdapter(printer);
reader.accept(adapter, 0);
byte[] b = writer.toByteArray();
有了它,您将获得 byte[]
,您可以将其保存到文件中,或使用 ClassLoader
.
加载到 Class
(TraceClassVisitor
只是另一个 ClassVisitor
,我还链接它以在您的临时目录中获得人类可读的日志。)
适配器可能如下所示。您要覆盖的方法是 visitLocalVariable
:
public class ClassAdapter extends ClassVisitor {
public ClassAdapter(ClassVisitor cv) {
super(Opcodes.ASM5, cv);
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc,
String signature, String[] exceptions) {
MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);
return new MethodAdapter(mv);
}
}
public class MethodAdapter extends MethodVisitor {
public MethodAdapter(MethodVisitor mv) {
super(Opcodes.ASM5, mv);
}
@Override
public void visitLocalVariable(String name, String desc, String signature,
Label start, Label end, int index) {
// Put your rename logic here
if (name.equals("c"))
name = "e";
else if (name.equals("d"))
name = "f";
super.visitLocalVariable(name, desc, signature, start, end, index);
}
}
我使用 Java 字节码的 ASM 库阅读了 class' 方法及其内容。这是在 class:
中输出任何方法的局部变量名称ClassReader reader = new ClassReader(new FileInputStream(new File("TheClass.class")));
final ClassNode classNode = new ClassNode();
reader.accept(classNode, 0);
for (final MethodNode mn : (List<MethodNode>)classNode.methods) {
for (LocalVariableNode n : (List<LocalVariableNode>)mn.localVariables) {
System.out.println(n.name);
}
}
这是编译后的 TheClass
class 文件的来源:
public class TheClass {
public final String a;
public final String b;
public TheClass(String c, String d) {
this.b = d;
this.a = c;
}
}
所以输出在逻辑上是 this
、c
、d
。 现在,我需要将这个已编译的 class 复制到一个新文件中,但将 <init>
方法的参数(局部变量)更改为不同的名称(e
、f
).我该怎么做? 我对 MethodVisitors 等几乎没有经验。
您需要编写一个适配器(ClassVisitor
的子类)并将其与 reader
链接起来。例如,
ClassReader reader = new ClassReader(new FileInputStream(new File("TheClass")));
ClassWriter writer = new ClassWriter(reader, 0);
TraceClassVisitor printer = new TraceClassVisitor(writer,
new PrintWriter(System.getProperty("java.io.tmpdir")
+ File.separator + name + ".log"));
ClassAdapter adapter = new ClassAdapter(printer);
reader.accept(adapter, 0);
byte[] b = writer.toByteArray();
有了它,您将获得 byte[]
,您可以将其保存到文件中,或使用 ClassLoader
.
Class
(TraceClassVisitor
只是另一个 ClassVisitor
,我还链接它以在您的临时目录中获得人类可读的日志。)
适配器可能如下所示。您要覆盖的方法是 visitLocalVariable
:
public class ClassAdapter extends ClassVisitor {
public ClassAdapter(ClassVisitor cv) {
super(Opcodes.ASM5, cv);
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc,
String signature, String[] exceptions) {
MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);
return new MethodAdapter(mv);
}
}
public class MethodAdapter extends MethodVisitor {
public MethodAdapter(MethodVisitor mv) {
super(Opcodes.ASM5, mv);
}
@Override
public void visitLocalVariable(String name, String desc, String signature,
Label start, Label end, int index) {
// Put your rename logic here
if (name.equals("c"))
name = "e";
else if (name.equals("d"))
name = "f";
super.visitLocalVariable(name, desc, signature, start, end, index);
}
}