Java 通过ASM替换完整方法
我试图编写一个脚本,用一个基本的throw new exception()行替换每个方法体。我正处于学习ASM的开始阶段,因此任何关于在哪里寻找的建议都将不胜感激 到目前为止我所做的:Java 通过ASM替换完整方法,java,java-bytecode-asm,Java,Java Bytecode Asm,我试图编写一个脚本,用一个基本的throw new exception()行替换每个方法体。我正处于学习ASM的开始阶段,因此任何关于在哪里寻找的建议都将不胜感激 到目前为止我所做的: package methodtester; import java.io.DataOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.Inpu
package methodtester;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import static org.objectweb.asm.Opcodes.ACC_INTERFACE;
import static org.objectweb.asm.Opcodes.ASM4;
import static org.objectweb.asm.Opcodes.ATHROW;
import static org.objectweb.asm.Opcodes.DUP;
import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
import static org.objectweb.asm.Opcodes.NEW;
public class MethodTransformer {
public static void main(String[] args) throws IOException {
InputStream in = MethodTester.class.getResourceAsStream("/methodtester/testingMethod.class");
ClassReader classReader = new ClassReader(in);
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
ExceptionThrower exceptionThrower = new ExceptionThrower(classWriter);
classReader.accept(exceptionThrower, 0);
File outputDir = new File("build/classes/methodtester/");
outputDir.mkdirs();
DataOutputStream dataOutputStream =
new DataOutputStream(
new FileOutputStream(
new File(outputDir,"testingMethod-postASM.class")));
dataOutputStream.write(classWriter.toByteArray());
}
public static class ExceptionThrower extends ClassVisitor {
private String _className;
private boolean _isInterface;
public ExceptionThrower(ClassVisitor classVisitor) {
super(ASM4, classVisitor);
}
@Override
public void visit(
int version, int access, String name, String signature,
String superName, String[] interfaces) {
cv.visit(version, access, name, signature, superName, interfaces);
_className = name;
_isInterface = (access & ACC_INTERFACE) != 0;
}
@Override
public MethodVisitor visitMethod(
int access, String name, String desc, String signature,
String[] exceptions) {
MethodVisitor mv = cv.visitMethod(access, name, desc, signature,
exceptions);
if (!_isInterface && mv != null && !name.equals("<init>")) {
ExceptionThrowerMethod exceptionThrowerMethod =
new ExceptionThrowerMethod(mv);
return exceptionThrowerMethod;
}
return mv;
}
public static class ExceptionThrowerMethod extends MethodVisitor {
public ExceptionThrowerMethod(MethodVisitor methodVisitor) {
super(ASM4, methodVisitor);
}
@Override
public void visitCode() {
mv.visitCode();
mv.visitTypeInsn(NEW, "java/io/IOException");
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKESPECIAL, "java/io/IOException", "<init>", "()V", false);
mv.visitInsn(ATHROW);
mv.visitMaxs(2, 0);
mv.visitEnd();
}
}
}
}
看看javap-c-v,我得到:
public void testing() throws java.io.IOException;
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: new #14 // class java/io/IOException
3: dup
4: invokespecial #15 // Method java/io/IOException."<init>":()V
7: athrow
LocalVariableTable:
Start Length Slot Name Signature
8 0 0 this Lmethodtester/testingMethod;
LineNumberTable:
line 24: 8
line 25: 8
Exceptions:
throws java.io.IOException
}
public void testing()抛出java.io.IOException;
描述符:()V
旗帜:ACC_PUBLIC
代码:
堆栈=2,局部变量=1,参数大小=1
0:new#14//类java/io/IOException
3:dup
4:invokespecial#15//方法java/io/IOException。”“:()V
7:athrow
LocalVariableTable:
起始长度插槽名称签名
8.0本方法试验/测试方法;
LineNumberTable:
第24行:8
第25行:8
例外情况:
抛出java.io.IOException
}
通过使用asmizer查看类,我发现该方法是:
mv = cw.visitMethod(ACC_PUBLIC, "testing", "()V", null, null);
mv.visitCode();
mv.visitTypeInsn(NEW, "java/io/IOException");
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKESPECIAL, "java/io/IOException", "<init>", "()V", false);
mv.visitInsn(ATHROW);
mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
mv.visitLdcInsn("testing method");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
mv.visitInsn(RETURN);
mv.visitMaxs(2, 1);
mv.visitEnd();
}
mv=cw.visitMethod(根据公共“测试”和“()V”,空,空);
mv.visitCode();
mv.visitTypeInsn(新的“java/io/IOException”);
mv.visitInsn(DUP);
mv.visitMethodInsn(调用,特别是“java/io/IOException”,“”,“()V”,false);
visitInsn号(ATHROW);
mv.visitFieldInsn(GETSTATIC,“java/lang/System”,“out”,“Ljava/io/PrintStream;”);
mv.visitLdcInsn(“试验方法”);
mv.visitMethodInsn(INVOKEVIRTUAL,“java/io/PrintStream”,“println”,“Ljava/lang/String;)V”,false);
mv.visitInsn(返回);
mv.visitmax(2,1);
mv.visitEnd();
}
看起来我需要更改LocalVariableTable以使其从0开始,长度为0
谢谢
编辑:使用COMPUTE_帧更新并更新新的错误代码。您正在将从
类编写器收到的原始MethodVisitor
传递给自定义MethodVisitor
的超级构造函数。这意味着,您没有覆盖的每个visit…
调用都将委托给该MethodVisitor
,复制整个原始代码
当然,首先,您不想复制原始代码,其次,在调用visitMaxs
和visitEnd
后,接收到对编写器的矛盾visit…
调用无助于创建有效代码
当您想要完全替换一个方法时,不应将目标编写器的MethodVisitor
展示给基类,而应仅将其用于您自己的代码生成:
public static class ExceptionThrowerMethod extends MethodVisitor {
private final MethodVisitor target;
public ExceptionThrowerMethod(MethodVisitor methodVisitor) {
super(ASM4, null);
this.target=methodVisitor;
}
@Override
public void visitCode() {
target.visitCode();
target.visitTypeInsn(NEW, "java/io/IOException");
target.visitInsn(DUP);
target.visitMethodInsn(INVOKESPECIAL,"java/io/IOException","<init>","()V",false);
target.visitInsn(ATHROW);
target.visitMaxs(2, 0);
target.visitEnd();
}
}
公共静态类ExceptionThrowerMethod扩展MethodVisitor{
私人目标;
公共例外RowerMethod(MethodVisitor MethodVisitor){
super(ASM4,null);
this.target=methodVisitor;
}
@凌驾
公共无效访问代码(){
target.visitCode();
visitTypeInsn(新的,“java/io/IOException”);
目标visitInsn(DUP);
visitMethodInsn(INVOKESPECIAL,“java/io/IOException”,“”,“()V”,false);
target.visitInsn(ATHROW);
target.visitmax(2,0);
target.visitEnd();
}
}
通过这种方式,您可以通过让类
在本地解析资源来简化资源访问,例如testingMethod.Class.getResourceAsStream(“testingMethod.Class”)
要替换的方法没有参数?@saka1029,但现在我只是在测试一个没有参数的简单方法,并希望先让它工作。尝试将ClassWriter选项ClassWriter.COMPUTE\u MAXS
更改为ClassWriter.COMPUTE\u FRAMES
。请参阅@saka1029进行了更改并用新错误更新了问题,似乎我需要使用asm以某种方式更改LocalVariableTable。谢谢,我是通过在所有visit*方法的覆盖版本中添加布尔检查来实现的,这是一个混乱,这个解决方案更干净!
public static class ExceptionThrowerMethod extends MethodVisitor {
private final MethodVisitor target;
public ExceptionThrowerMethod(MethodVisitor methodVisitor) {
super(ASM4, null);
this.target=methodVisitor;
}
@Override
public void visitCode() {
target.visitCode();
target.visitTypeInsn(NEW, "java/io/IOException");
target.visitInsn(DUP);
target.visitMethodInsn(INVOKESPECIAL,"java/io/IOException","<init>","()V",false);
target.visitInsn(ATHROW);
target.visitMaxs(2, 0);
target.visitEnd();
}
}