ASM简介(一)

作者: 千里山南 | 来源:发表于2016-07-28 16:32 被阅读741次

    之前简单研究过ASM这个字节码修改框架,最近要用到,故简单复习下。顺便翻译下官方文档(翻译主要是给自己看的,因此比较随意,自己看一眼就明白的直接跳过了,大家权且当作是读书笔记吧)。

    字节码修改的必要性

    • 字节码的分析、翻译及自动生成很常见
    • 直接作用于class文件的优势是,可以适用于闭源软件商业软件等拿不到源码的修改翻译,并且可以在运行时修改字节码,其操作对于源代码开发者是透明的。

    ASM的优势

    • 足够小足够快
    • API足够好用
    • 有Eclipse及Intellij插件
    • 应用广泛
    • 开源协议宽泛

    概览

    ASM设计用于字节码的改写、生成。其有两套API一套基于访问者模式、一套基于树的数据结构。基于访问者模式的api,类中的每个数据结构都是一个Event,类的生成同样基于这样的Event。基于树的数据结构的api是面向对象的一种设计,类被表示为一个对象。这两套api的区别类似xml解析中的SAX和DOM。两套api都是对同一个class操作的,如果用户需要修改相关联的类需要自己手动管理。

    功能介绍

    asm.jar 包含时间模式的API,适用于class文件的读写。
    asm-util.jar 包含一些工具类,基于base api 用于协助开发调试
    asm-commons.jar 提供了一些很有用的预定义基于事件的类转换器
    asm-tree.jar 提供了基于对象的api并提供两种api的转化
    asm-analysis.jar 提供了基于对象api的类的分析框架类

    字节码文件格式概览

    • 描述类访问控制权限,类名,父类,接口和注解
    • 每个被声明的成员变量,同样包括访问控制权限、名称、类型、注解
    • 方法及构造函数,包括访问控制权限,名称、名称、返回值类型、参数类型、注解等。同时还包含方法体(一系列jvm指令)

    类源码和字节码区别:每个class文件只包含一个类,没有注释。没有package 和 import部分,所有的类型必须使用全名。另一个重要的区别在于,编译后的字节码有一个常量存放区。存放着类中出现的所有的数字、字符串、类型常量。这些常量只被定义一次,被类的其他部分通过索引引用。不过使用ASM的话常量定义区对我们而言是透明的。另一个区别在于源文件和class文件中对类型的引用方式不同。
    class 文件的结构如下

    Modifiers, name, super class, interfaces
    Constant pool: numeric, string and type constants
    Source file name (optional)
    Enclosing class reference
    Annotation*
    Attribute*
    Inner class* Name
    Field* Modifiers, name, type
    Annotation*
    Attribute*
    Method* Modifiers, name, return and parameter types
    Annotation*
    Attribute*
    Compiled code

    bytecode中type表示如下:类似jni接口

    Java type Type descriptor
    boolean Z
    char C
    byte B
    short S
    int I
    float F
    long J
    double D
    Object Ljava/lang/Object;
    int[] [I
    Object[][] [[Ljava/lang/Object;

    方法描述也类似jni

    Method declaration in source file Method descriptor
    void m(int i, float f) (IF)V
    int m(Object o) (Ljava/lang/Object;)I
    int[] m(int i, String s) (ILjava/lang/String;)[I
    Object m(int[] i) ([I)Ljava/lang/Object;

    接口和组件

    class的生成和转换时基于ClassVisitor抽象类的。该类的每个方法都对应class的一个结构。简单的结构对应的方法入参为结构的描述,方法返回void即可。复杂的使用一个init方法来访问,并且返回一个描述改结构的visitor类。例如visitAnnotation、visitField\visitMethod需要返回AnnotationVisitor、FieldVisitor、MethodVisitor。这样整个结构可以被递归的调用

    public abstract class ClassVisitor { 
        public ClassVisitor(int api); 
        public ClassVisitor(int api, ClassVisitor cv); 
        public void visit(int version, int access, String name, String signature, String superName, String[] interfaces); 
                public void visitSource(String source, String debug); 
                public void visitOuterClass(String owner, String name, String desc); 
        AnnotationVisitor visitAnnotation(String desc, boolean visible); 
        public void visitAttribute(Attribute attr); 
        public void visitInnerClass(String name, String outerName, String innerName, int access); 
        public FieldVisitor visitField(int access, String name, String desc, String signature, Object value); 
        public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions); 
        void visitEnd(); 
    }
    

    ClassVisitor方法调用必须要按照以下顺序:visit visitSource? visitOuterClass? ( visitAnnotation | visitAttribute )* ( visitInnerClass | visitField | visitMethod )* visitEnd

    基于ClassVisitor ASM提供了 ClassReader来解析字节流格式的class. ClassWriter 用于生成二进制格式的class. ClassVisitor 可以看作是Event的过滤器

    相关文章

      网友评论

        本文标题:ASM简介(一)

        本文链接:https://www.haomeiwen.com/subject/hhmajttx.html