我目前正在尝试通过精心设计的java-asm库(版本4)生成代码。 更具体地说,我想在每次返回调用之前将代码附加到方法的末尾。我成功地能够在方法代码之前添加代码。但是目前我不知道如何进行上述转换。我真的很感激指出如何实现这一目标。
MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);
mv.visitCode();
mv.visitVarInsn(Opcodes.ALOAD, 42);
return mv;
答案 0 :(得分:8)
您有两种解决方案:
<强> 1。使用visitInsn(int opcode)
中的MethodVisitor
方法:
//this is the custom method visitor
private class InsertInitCodeBeforeReturnMethodVisitor extends MethodVisitor{
public InsertInitCodeBeforeReturnMethodVisitor(MethodVisitor mv) {
super(Opcodes.ASM4, mv);
}
@Override
public void visitInsn(int opcode) {
//whenever we find a RETURN, we instert the code, here only crazy example code
switch(opcode) {
case Opcodes.IRETURN:
case Opcodes.FRETURN:
case Opcodes.ARETURN:
case Opcodes.LRETURN:
case Opcodes.DRETURN:
case Opcodes.RETURN:
mv.visitVarInsn(Opcodes.ALOAD, 42);
break;
default: // do nothing
}
super.visitInsn(opcode);
}
}
<强> 2。使用org.objectweb.asm.commons
中onMethodExit(int opcode)
的AdviceAdapter
方法:
//this is the custom method visitor
private class InsertInitCodeBeforeReturnMethodVisitor extends AdviceAdapter{
public InsertInitCodeBeforeReturnMethodVisitor(MethodVisitor mv, int access, String name, String desc) {
super(Opcodes.ASM4, mv, access, name, desc);
}
@Override
protected void onMethodExit(int opcode) {
if(opcode != Opcdoes.ATHROW) {
mv.visitVarInsn(Opcodes.ALOAD, 42);
}
}
}
我个人更喜欢AdviceAdapter,因为它消除了实际调用原始返回指令的麻烦,就像你必须使用第一个解决方案(例如super.visitInsn(opcode);
)。其次,它为访问RETURN
指令(和ATHORW
)提供了一个很好的抽象;对于visitInsn(int opcode)
中的MethodVisitor
方法而言,情况并非如此,您必须在其中检测RETURN
指令中的DUP
指令,ICONST_0
s,{AdviceAdapter
1}}等,可能与手头的问题有关,也可能没有。
但这又取决于手头的问题。如果这是唯一正在执行的仪器,我会坚持RETURN
。如果您希望在访问MethodVisitor
说明的同时做其他事情,我可能会使用简单的AdviceAdapter
,因为它可能会给我更大的灵活性。话虽这么说,我已经使用AdviceAdapter
一年多一点时间用于重型仪表驱动项目,到目前为止它已经很好了!
编辑:
应用方法访问者
通常不清楚如何使用或应用方法访问者/方法适配器(至少对我而言),所以我在这里汇总了一个快速的代码示例:gist.github.com/VijayKrishna/1ca807c952187a7d8c4d,它显示了如何通过相应的class-visitor / class-adapter使用方法适配器。在示例代码段中,我已经从我在此答案中使用的方法更改了方法适配器的名称,但它们执行相同的操作。此外,代码段显示了扩展ClassReader cr = new ClassReader(in);
ClassWriter cw = new ClassWriter(ClassReader.EXPAND_FRAMES);
ReturnAdapter returnAdapter = new ReturnAdapter(cw, className);
cr.accept(returnAdapter, 0);
。
总之,您首先“调用”类适配器,如下所示:
visitMethod
然后,在类适配器的MethodVisitor mv;
mv = cv.visitMethod(access, name, desc, signature, exceptions);
mv = new MethodReturnAdapter(Opcodes.ASM4, className, access, name, desc, mv);
return mv;
方法中按照以下方法调整方法:
{{1}}
答案 1 :(得分:3)
实际上结果很简单,但我在完全错误的地方搜索。 为了在任意位置转换方法体,您必须创建一个自定义MethodVisitor子类并通过它管道现有的MethodVisitor,以便执行您希望它执行的转换。在我的示例中,每当自定义MethodVisitor找到操作码RETURN时,它都会添加代码,如下所示:
public class ActorInterfaceTransformer extends ClassVisitor {
public ActorInterfaceTransformer(ClassVisitor cv) {
super(Opcodes.ASM4, cv);
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
//if the method is the one we want to transform
if(name.equals("<init>")){
//... then we pipe the method visitor
MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);
return new InsertInitCodeBeforeReturnMethodVisitor(mv);
}
return super.visitMethod(access, name, desc, signature, exceptions);
}
}
//this is the custom method visitor
private class InsertInitCodeBeforeReturnMethodVisitor extends MethodVisitor{
public InsertInitCodeBeforeReturnMethodVisitor(MethodVisitor mv) {
super(Opcodes.ASM4, mv);
}
@Override
public void visitInsn(int opcode) {
//whenever we find a RETURN, we instert the code, here only crazy example code
if(opcode==Opcodes.RETURN){
mv.visitVarInsn(Opcodes.ALOAD, 42);
}
super.visitInsn(opcode);
}
}