asm学习笔记之生成方法

MethodVisitor 简介

如果要生成方法实现的字节码,就要借助MethodVisitor类了,可以通过ClassVisitor的visitMethod方法得到一个MethodVisitor子类TraceMethodVisitor的实例。以下是MethodVisitor API里面的visitXxx方法:

  1. AnnotationVisitor visitAnnotationDefault();  
  2. AnnotationVisitor visitAnnotation(String desc, boolean visible);  
  3. AnnotationVisitor visitParameterAnnotation(int parameter,String desc, boolean visible);  
  4. void visitAttribute(Attribute attr);  
  5. void visitCode();  
  6. void visitFrame(int type, int nLocal, Object[] local, int nStack,Object[] stack);  
  7. void visitInsn(int opcode);  
  8. void visitIntInsn(int opcode, int operand);  
  9. void visitVarInsn(int opcode, int var);  
  10. void visitTypeInsn(int opcode, String desc);  
  11. void visitFieldInsn(int opc, String owner, String name, String desc);  
  12. void visitMethodInsn(int opc, String owner, String name, String desc);  
  13. void visitInvokeDynamicInsn(String name, String desc, Handle bsm,Object... bsmArgs);  
  14. void visitJumpInsn(int opcode, Label label);  
  15. void visitLabel(Label label);  
  16. void visitLdcInsn(Object cst);  
  17. void visitIincInsn(int var, int increment);  
  18. void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels);  
  19. void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels);  
  20. void visitMultiANewArrayInsn(String desc, int dims);  
  21. void visitTryCatchBlock(Label start, Label end, Label handler,String type);  
  22. void visitLocalVariable(String name, String desc, String signature,Label start, Label end, int index);  
  23. void visitLineNumber(int line, Label start);  
  24. void visitMaxs(int maxStack, int maxLocals);  
  25. void visitEnd();  
AnnotationVisitor visitAnnotationDefault();
AnnotationVisitor visitAnnotation(String desc, boolean visible);
AnnotationVisitor visitParameterAnnotation(int parameter,String desc, boolean visible);
void visitAttribute(Attribute attr);
void visitCode();
void visitFrame(int type, int nLocal, Object[] local, int nStack,Object[] stack);
void visitInsn(int opcode);
void visitIntInsn(int opcode, int operand);
void visitVarInsn(int opcode, int var);
void visitTypeInsn(int opcode, String desc);
void visitFieldInsn(int opc, String owner, String name, String desc);
void visitMethodInsn(int opc, String owner, String name, String desc);
void visitInvokeDynamicInsn(String name, String desc, Handle bsm,Object... bsmArgs);
void visitJumpInsn(int opcode, Label label);
void visitLabel(Label label);
void visitLdcInsn(Object cst);
void visitIincInsn(int var, int increment);
void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels);
void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels);
void visitMultiANewArrayInsn(String desc, int dims);
void visitTryCatchBlock(Label start, Label end, Label handler,String type);
void visitLocalVariable(String name, String desc, String signature,Label start, Label end, int index);
void visitLineNumber(int line, Label start);
void visitMaxs(int maxStack, int maxLocals);
void visitEnd();
和ClassVisitor一样,调用MethodVisitor的方法也必须遵循以下顺序:

  1. visitAnnotationDefault?  
  2. ( visitAnnotation | visitParameterAnnotation | visitAttribute )*  
  3. ( visitCode  
  4. ( visitTryCatchBlock | visitLabel | visitFrame | visitXxxInsn |  
  5. visitLocalVariable | visitLineNumber )*  
  6. visitMaxs )?  
  7. visitEnd  
visitAnnotationDefault?
( visitAnnotation | visitParameterAnnotation | visitAttribute )*
( visitCode
( visitTryCatchBlock | visitLabel | visitFrame | visitXxxInsn |
visitLocalVariable | visitLineNumber )*
visitMaxs )?
visitEnd

说明:

1、如果要生成方法的代码,需要先以visitCode开头,访问结束需要调用visitEnd方法;

2、方法都是在线程中执行的,每个线程有自己的虚拟机栈,这个栈与线程同时创建,用于存储栈帧。栈帧随着方法调用而创建,方法结束时销毁。每个栈帧都有自己的本地变量表、操作数栈和指向常量池的引用。

本地变量表和操作数栈的大小在编译期确定,在asm中可以通过visitMaxs来指定本地变量表与操作数栈的大小。visitFrame方法可以指定栈帧中的本地变量与操作数。

对本地变量和操作数栈的大小设置受ClassWriter的flag取值影响:

(1)new ClassWriter(0),表明需要手动计算栈帧大小、本地变量和操作数栈的大小;

(2)new ClassWriter(ClassWriter.COMPUTE_MAXS)需要自己计算栈帧大小,但本地变量与操作数已自动计算好,当然也可以调用visitMaxs方法,只不过不起作用,参数会被忽略;

(3)new ClassWriter(ClassWriter.COMPUTE_FRAMES)栈帧本地变量和操作数栈都自动计算,不需要调用visitFrame和visitMaxs方法,即使调用也会被忽略。

这些选项非常方便,但会有一定的开销,使用COMPUTE_MAXS会慢10%,使用COMPUTE_FRAMES会慢2倍。

3、visitInsn、visitVarInsn、visitMethodInsn等以Insn结尾的方法可以添加方法实现的字节码。


实例

下面用ClassMethod来生成add方法的实现代码,并调用:

假设有如下接口:

  1. package asm.demo;  
  2.   
  3. public interface AddOper extends Oper {  
  4.     public static final String SYMBOL = "+";  
  5.   
  6.     public int add(int a, int b);  
  7. }  
package asm.demo;

public interface AddOper extends Oper {
    public static final String SYMBOL = "+";

    public int add(int a, int b);
}
要生成如下java代码:

  1. package asm.demo;  
  2.   
  3. public class AddOperImpl implements AddOper {  
  4.     @Override  
  5.     public int add(int a, int b) {  
  6.         return a + b;  
  7.     }  
  8. }  
package asm.demo;

public class AddOperImpl implements AddOper {
    @Override
    public int add(int a, int b) {
        return a + b;
    }
}
对应的生成方法如下:

  1. public static void main(String[] args) throws Exception {  
  2.     ClassWriter cw = new ClassWriter(0);  
  3.     PrintWriter printWriter = new PrintWriter(System.out);  
  4.     TraceClassVisitor visitor = new TraceClassVisitor(cw, printWriter);  
  5.   
  6.     visitor.visit(V1_5, ACC_PUBLIC, "asm/demo/AddOperImpl"null"java/lang/Object"new String[]{"asm/demo/AddOper"});  
  7.   
  8.     //添加构造方法  
  9.     MethodVisitor mv = visitor.visitMethod(ACC_PUBLIC, "<init>""()V"nullnull);  
  10.     mv.visitCode();  
  11.     mv.visitVarInsn(ALOAD, 0);  
  12.     mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object""<init>""()V");  
  13.     mv.visitInsn(RETURN);  
  14.     mv.visitMaxs(11);  
  15.     mv.visitEnd();  
  16.   
  17.     // 添加add方法  
  18.     mv = visitor.visitMethod(ACC_PUBLIC, "add""(II)I"nullnull);  
  19.     mv.visitCode();  
  20.     mv.visitVarInsn(ILOAD, 1);  
  21.     mv.visitVarInsn(ILOAD, 2);  
  22.     mv.visitInsn(IADD);  
  23.     mv.visitInsn(IRETURN);  
  24.     mv.visitMaxs(23);  
  25.     mv.visitEnd();  
  26.   
  27.     visitor.visitEnd();  
  28.   
  29.     FileOutputStream fos = new FileOutputStream(new File("D:/code/asmdemo/out/production/asmdemo/asm/demo/AddOperImpl.class"));  
  30.     fos.write(cw.toByteArray());  
  31.     fos.close();  
  32. }  
public static void main(String[] args) throws Exception {
	ClassWriter cw = new ClassWriter(0);
	PrintWriter printWriter = new PrintWriter(System.out);
	TraceClassVisitor visitor = new TraceClassVisitor(cw, printWriter);

	visitor.visit(V1_5, ACC_PUBLIC, "asm/demo/AddOperImpl", null, "java/lang/Object", new String[]{"asm/demo/AddOper"});

	//添加构造方法
	MethodVisitor mv = visitor.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
	mv.visitCode();
	mv.visitVarInsn(ALOAD, 0);
	mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
	mv.visitInsn(RETURN);
	mv.visitMaxs(1, 1);
	mv.visitEnd();

	// 添加add方法
	mv = visitor.visitMethod(ACC_PUBLIC, "add", "(II)I", null, null);
	mv.visitCode();
	mv.visitVarInsn(ILOAD, 1);
	mv.visitVarInsn(ILOAD, 2);
	mv.visitInsn(IADD);
	mv.visitInsn(IRETURN);
	mv.visitMaxs(2, 3);
	mv.visitEnd();

	visitor.visitEnd();

	FileOutputStream fos = new FileOutputStream(new File("D:/code/asmdemo/out/production/asmdemo/asm/demo/AddOperImpl.class"));
	fos.write(cw.toByteArray());
	fos.close();
}
说明:

(1)这里生成了两个方法,分别是构造方法<init>和add方法

(2)构造方法的本地方法与操作数栈大小分别为1,是因为aload_0指令表示从局部变量表加载一个reference类型值到操作数栈,这里局部变量表只有this引用;

(3)add方法的操作数栈大小为2,局部变量表大小为3,分别为this,a,b。

用javap -c AddOperImpl.class命令查看字节码如下:

  1. public class asm.demo.AddOperImpl implements asm.demo.AddOper {  
  2.   public asm.demo.AddOperImpl();  
  3.     Code:  
  4.        0: aload_0  
  5.        1: invokespecial #10                 // Method java/lang/Object."<init>":()V  
  6.        4return  
  7.   
  8.   public int add(intint);  
  9.     Code:  
  10.        0: iload_1  
  11.        1: iload_2  
  12.        2: iadd  
  13.        3: ireturn  
  14. }  
public class asm.demo.AddOperImpl implements asm.demo.AddOper {
  public asm.demo.AddOperImpl();
    Code:
       0: aload_0
       1: invokespecial #10                 // Method java/lang/Object."<init>":()V
       4: return

  public int add(int, int);
    Code:
       0: iload_1
       1: iload_2
       2: iadd
       3: ireturn
}

调用add方法结果如预期:

  1. MyClassLoader classLoader = new MyClassLoader();  
  2. Class<?> clazz = classLoader.defineClass("asm.demo.AddOperImpl", cw.toByteArray());  
  3. Method addMethod = clazz.getMethod("add"int.classint.class);  
  4. Object result = addMethod.invoke(clazz.newInstance(), 1020);  
  5. if(result != null && result instanceof Integer)  
  6. System.out.println((Integer) result);  


原文地址:http://blog.csdn.net/aesop_wubo/article/details/48948211


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值