ASM框架使用(二)

asm 包是asm框架的core包。
这篇介绍怎么用asm创建和修改编译后的class。

Java源码中类型,在字节码中是用以下类型代表的:
这里写图片描述

方法描述(方法名不在方法描述中,在常量池中):
这里写图片描述

asm创建和改变Java字节码是通过ClassVisitor抽象类,
这里写图片描述

class vistor必须按照以下执行顺序:
这里写图片描述

classreader用来解析一个已存在的类。
下面是模仿javap打印一个类的信息

public class ClassPrinter extends ClassVisitor {
    public ClassPrinter() {
        super(Opcodes.ASM4);
    }

    @Override
    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
        System.out.println(name+" extend "+superName+"{");
    }

    @Override
    public void visitSource(String source, String debug) {

    }

    @Override
    public void visitOuterClass(String owner, String name, String descriptor) {

    }

    @Override
    public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
        return null;
    }

    @Override
    public void visitAttribute(Attribute attribute) {

    }

    @Override
    public void visitInnerClass(String name, String outerName, String innerName, int access) {
        super.visitInnerClass(name, outerName, innerName, access);
    }

    @Override
    public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
        System.out.println(" " + descriptor + " " + name);
        return null;
    }
    public MethodVisitor visitMethod(int access, String name,
                                     String desc, String signature, String[] exceptions) {
        System.out.println(" " + name + desc);
        return null;
    }
    public void visitEnd() {
        System.out.println("}");
    }

    public static void main(String[] args) throws IOException {
        ClassPrinter cp=new ClassPrinter();
        ClassReader cr=new ClassReader("java.lang.Runnable");
        /*
          ClassLoader cl=ClassLoader.getSystemClassLoader();
          InputStream is= cl.getResourceAsStream("java.lang.Runnable".replace('.', '/') + ".class");
          ClassReader cr=new ClassReader(is);
        */
        cr.accept(cp,0);
    }

}

创建字节码:

 public static byte[] create(){
        ClassWriter cw=new ClassWriter(0);//0表示手动计算局部变量表、最大操作数栈和栈帧
        cw.visit(Opcodes.V1_8,
                ACC_PUBLIC+Opcodes.ACC_ABSTRACT+Opcodes.ACC_INTERFACE,
                "com/test/T1",null,"java/lang/Object",new String[]{"com/test/TP"});//null位置是泛型参数,这里null表示没有泛型,后面参数分别是 父类,接口
        cw.visitField(ACC_PUBLIC+ ACC_FINAL+ ACC_STATIC,
                "LESS","I",null,new Integer(1)).visitEnd();
        cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "EQUAL", "I",
                null, new Integer(0)).visitEnd();
        cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "GREATER", "I",
                null, new Integer(1)).visitEnd();
        cw.visitMethod(ACC_PUBLIC+ACC_ABSTRACT+ACC_STATIC,"test","(I)V",null,null).visitEnd();//第一个null表示没有泛型参数,第二个表示没有抛出异常
        cw.visitEnd();
        return cw.toByteArray();
    }

打印结果:

 public static void main(String[] args) throws IOException {
        ClassPrinter cp=new ClassPrinter();
        ClassLoader cl=ClassLoader.getSystemClassLoader();
        InputStream is= cl.getResourceAsStream("java.lang.Runnable".replace('.', '/') + ".class");
        ClassReader cr=new ClassReader(is);
        cr.accept(cp,0);
        ClassReader cr2=new ClassReader(create());
        cr2.accept(cp,0);
    }
---------------------------
java/lang/Runnable extend java/lang/Object{
 run()V
}
com/test/T1 extend java/lang/Object{
 I LESS
 I EQUAL
 I GREATER
 test(I)V
}

下面是使用classreader解析类,通过classwriter改造。

TraceClassVisitor用来查看生成的字节码对应的类是否是想要的类。

CheckClassAdapter这类是用来检查它的方法调用以及参数是否正确。

ASMifier用来产生asm代码,当有一个类时,使用这个类读取目标类,会产生产生这个字节码所需要的asm代码。

Opcodes可以分为2类:一类把局部变量表的值压栈,另一类只作用于操作数栈,计算,出栈入栈。

修改方法必须执行的顺序

visitAnnotationDefault?
( visitAnnotation | visitParameterAnnotation | visitAttribute )*
( visitCode
    ( visitTryCatchBlock | visitLabel | visitFrame | visitXxxInsn |
        visitLocalVariable | visitLineNumber )*
    visitMaxs )?
visitEnd
public class AddTimerAdapter extends ClassVisitor {
private String owner;
private boolean isInterface;
public AddTimerAdapter(ClassVisitor cv) {
super(ASM4, cv);
}
@Override public void visit(int version, int access, String name,
String signature, String superName, String[] interfaces) {
cv.visit(version, access, name, signature, superName, interfaces);
owner = 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>")) {
mv = new AddTimerMethodAdapter(mv);
}
return mv;
}
@Override public void visitEnd() {
if (!isInterface) {
FieldVisitor fv = cv.visitField(ACC_PUBLIC + ACC_STATIC, "timer",
"J", null, null);
if (fv != null) {
fv.visitEnd();
}
}
cv.visitEnd();
}
class AddTimerMethodAdapter extends MethodVisitor {
public AddTimerMethodAdapter(MethodVisitor mv) {
super(ASM4, mv);

}
@Override public void visitCode() {
mv.visitCode();
mv.visitFieldInsn(GETSTATIC, owner, "timer", "J");
mv.visitMethodInsn(INVOKESTATIC, "java/lang/System",
"currentTimeMillis", "()J");
mv.visitInsn(LSUB);
mv.visitFieldInsn(PUTSTATIC, owner, "timer", "J");
}
@Override public void visitInsn(int opcode) {
if ((opcode >= IRETURN && opcode <= RETURN) || opcode == ATHROW) {
mv.visitFieldInsn(GETSTATIC, owner, "timer", "J");
mv.visitMethodInsn(INVOKESTATIC, "java/lang/System",
"currentTimeMillis", "()J");
mv.visitInsn(LADD);
mv.visitFieldInsn(PUTSTATIC, owner, "timer", "J");
}
mv.visitInsn(opcode);
}
@Override public void visitMaxs(int maxStack, int maxLocals) {
mv.visitMaxs(maxStack + 4, maxLocals);
}
}
}

LocalVariablesSorter 可以获取局部变量的索引

猜你喜欢

转载自blog.csdn.net/ljz2016/article/details/81363828
ASM
今日推荐