之前的文章中介绍了class字节码结构组成,本文将演示如何编写代码解析出这些结构内容,具体可以结合详解class字节码来看。
先看效果:
整体结构
通过前文已经知道字节码由11个大的部分组成,定义相应结构如下:
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依次读取各个部分:
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属性中都存在,其结构将在后面部分介绍
ConstantPool
定义常量抽象父类,其他常量类都继承此类:
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值:
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 即可获取