讀取class文件

以前的文章中介紹了class字節碼結構組成,本文將演示如何編寫代碼解析出這些結構內容,具體能夠結合詳解class字節碼來看。java

先看效果:this

總體結構

經過前文已經知道字節碼由11個大的部分組成,定義相應結構以下:spa

public final class ClassFile {
		/** * 魔數 */
    private final int magic;

    /** * 次版本號 */
    private final int minor_version;

    /** * 主版本號 */
    private final int major_version;

    /** * 常量池 */
    private final ConstantPool constant_pool;

    /** * 訪問標誌 */
    private final int access_flags;

    /** * 當前類或接口索引,指向CONSTANT_Class_info常量 */
    private final int this_class;

    /** * 當前類直接父類索引,指向CONSTANT_Class_info常量 */
    private final int super_class;

    /** * 當前類或接口的直接父接口 */
    private final int[] interfaces;

    /** * 當前類的字段列表 */
    private final FieldInfo[] fields;

    /** * 當前類的方法列表 */
    private final MethodInfo[] methods;

    /** * 當前類的屬性表 */
    private final AttributeInfo[] attributes;
}
複製代碼

使用DataInputStream依次讀取各個部分:code

this.magic = is.readInt();
this.minor_version = is.readUnsignedShort();
this.major_version = is.readUnsignedShort();
this.constant_pool = new ConstantPool(is);
this.access_flags = is.readUnsignedShort();
this.this_class = is.readUnsignedShort();
this.super_class = is.readUnsignedShort();

final int interfaces_count = is.readUnsignedShort();
this.interfaces = new int[interfaces_count];
for (int i = 0; i < interfaces_count; i++) {
     this.interfaces[i] = is.readUnsignedShort();
}

final int fields_count = is.readUnsignedShort();
this.fields = new FieldInfo[fields_count];
for (int i = 0; i < fields_count; i++) {
     this.fields[i] = new FieldInfo(is);
}

final int methods_count = is.readUnsignedShort();
this.methods = new MethodInfo[methods_count];
for (int i = 0; i < methods_count; i++) {
     this.methods[i] = new MethodInfo(is);
}

final int attributes_count = is.readUnsignedShort();
this.attributes = new AttributeInfo[attributes_count];
for (int i = 0; i < attributes_count; i++) {
     this.attributes[i] = Attributes.readAttributeInfo(is, ClassFile.this.constant_pool);
}
複製代碼

注: attributes在class,field,method,code屬性中都存在,其結構將在後面部分介紹orm

ConstantPool

定義常量抽象父類,其餘常量類都繼承此類:cdn

abstract static class CPInfo {

    private static final String[] tagNames = new String[]{
        "", "Utf8", "Integer", "Float", "Long",
        "Double", "Class", "String", "Fieldref",
        "Methodref", "InterfaceMethodref", "NameAndType",
        "MethodHandle", "MethodType", "InvokeDynamic"
    };

	 /** 常量類型標記 */
    private int tag;

    public CPInfo(int tag) {
        this.tag = tag;
    }
}
複製代碼

tag值:blog

public static final short CONSTANT_Utf8 = 1;
public static final short CONSTANT_Integer = 3;
public static final short CONSTANT_Float = 4;
public static final short CONSTANT_Long = 5;
public static final short CONSTANT_Double = 6;
public static final short CONSTANT_Class = 7;
public static final short CONSTANT_String = 8;
public static final short CONSTANT_Fieldref = 9;
public static final short CONSTANT_Methodref = 10;
public static final short CONSTANT_InterfaceMethodref = 11;
public static final short CONSTANT_NameAndType = 12;
public static final short CONSTANT_MethodHandle = 15;
public static final short CONSTANT_MethodType = 16;
public static final short CONSTANT_InvokeDynamic = 18;
複製代碼

定義常量池:繼承

static class ConstantPool {
	CPInfo[] pool;
}
複製代碼

解析出常量池:索引

CONSTANT_Utf8_info, CONSTANT_Integer_info等繼承CPInfo接口

FieldInfo

定義以下:

class FieldInfo {
    private int access_flags;
    private int name_index;
    private int descriptor_index;
    private AttributeInfo[] attributes;
}
複製代碼

解析以下:

this.access_flags = is.readUnsignedShort();
this.name_index = is.readUnsignedShort();
this.descriptor_index = is.readUnsignedShort();
final int attributes_count = is.readUnsignedShort();
this.attributes = new AttributeInfo[attributes_count];
for (int i = 0; i < attributes_count; i++) {
    this.attributes[i] = Attributes.readAttributeInfo(is, ClassFile.this.constant_pool);
}
複製代碼
MethodInfo

定義:

private int access_flags;
private int name_index;
private int descriptor_index;
private AttributeInfo[] attributes;
複製代碼

解析:

this.access_flags = is.readUnsignedShort();
this.name_index = is.readUnsignedShort();
this.descriptor_index = is.readUnsignedShort();
final int attributes_count = is.readUnsignedShort();
this.attributes = new AttributeInfo[attributes_count];
for (int i = 0; i < attributes_count; i++) {
     this.attributes[i] = Attributes.readAttributeInfo(is, ClassFile.this.constant_pool);
}
複製代碼
Attribute

定義抽象基類:

static abstract class AttributeInfo {

    public static final String AnnotationDefault = "AnnotationDefault";
    public static final String BootstrapMethods = "BootstrapMethods";
    public static final String Code = "Code";
    public static final String ConstantValue = "ConstantValue";
    public static final String Deprecated = "Deprecated";
    public static final String EnclosingMethod = "EnclosingMethod";
    public static final String Exceptions = "Exceptions";
    public static final String InnerClasses = "InnerClasses";
    public static final String LineNumberTable = "LineNumberTable";
    public static final String LocalVariableTable = "LocalVariableTable";
    public static final String LocalVariableTypeTable = "LocalVariableTypeTable";
    public static final String MethodParameters = "MethodParameters";
    public static final String RuntimeVisibleAnnotations = "RuntimeVisibleAnnotations";
    public static final String RuntimeInvisibleAnnotations = "RuntimeInvisibleAnnotations";
    public static final String RuntimeVisibleParameterAnnotations = "RuntimeVisibleParameterAnnotations";
    public static final String RuntimeInvisibleParameterAnnotations = "RuntimeInvisibleParameterAnnotations";
    public static final String RuntimeVisibleTypeAnnotations = "RuntimeVisibleTypeAnnotations";
    public static final String RuntimeInvisibleTypeAnnotations = "RuntimeInvisibleTypeAnnotations";
    public static final String Signature = "Signature";
    public static final String SourceDebugExtension = "SourceDebugExtension";
    public static final String SourceFile = "SourceFile";
    public static final String StackMapTable = "StackMapTable";
    public static final String Synthetic = "Synthetic";

    protected int attribute_name_index;
    protected int attribute_length;

    public AttributeInfo(DataInputStream is, int attribute_name_index) throws IOException {
        this.attribute_name_index = attribute_name_index;
        this.attribute_length = is.readInt();
    }

    public String getName(ConstantPool cp) {
        ConstantPool.CONSTANT_Utf8_info cpInfo = (ConstantPool.CONSTANT_Utf8_info) cp.pool[attribute_name_index];
        return String.format("%s(#%s)", cpInfo.getName(cp), attribute_name_index);
    }

}
複製代碼

主要將各屬性共有的attribute_name_index, attribute_length提取到基類中,23個常量字符串是attribute_name_index引用的常量CONSTANT_Utf8_info的值,根據它斷定屬性類型

讀取具體屬性:

根據attribute_name就能夠解析出相應屬性了, 如Code_attribute:

static class Code extends AttributeInfo {

    private int max_stack;//u2
    private int max_locals;//u2
    private int code_length;//u4
    private byte[] code;
    private int exception_table_length;
    private ExceptionTable[] exception_table;
    private int attributes_count;
    private AttributeInfo[] attributes;

    public Code(DataInputStream is, int attribute_name_index, ConstantPool cp) throws IOException {
        super(is, attribute_name_index);
        max_stack = is.readUnsignedShort();
        max_locals = is.readUnsignedShort();
        code_length = is.readInt();
        code = new byte[code_length];
        is.read(code);
        exception_table_length = is.readUnsignedShort();
        exception_table = new ExceptionTable[exception_table_length];
        for (int i = 0; i < exception_table_length; i++) {
            exception_table[i] = new ExceptionTable(is.readUnsignedShort(), is.readUnsignedShort(),
                        is.readUnsignedShort(), is.readUnsignedShort());
        }
        attributes_count = is.readUnsignedShort();
        attributes = new AttributeInfo[attributes_count];
            for (int i = 0; i < attributes_count; i++) {
                attributes[i] = Attributes.readAttributeInfo(is, cp);
            }
        }

    class ExceptionTable {
        int start_pc;
        int end_pc;
        int handler_pc;
        int catch_type;

        public ExceptionTable(int start_pc, int end_pc, int handler_pc, int catch_type) {
            this.start_pc = start_pc;
            this.end_pc = end_pc;
            this.handler_pc = handler_pc;
            this.catch_type = catch_type;
        }
    }
}
複製代碼

完!能夠看出字節碼結構還算簡單,就是類型比較多,比較繁雜而已。

完整代碼關注訂閱號,回覆 class 便可獲取

相關文章
相關標籤/搜索