解析class文件需要把class文件当成文件流来处理,定义ClassReader结构体
type ClassReader struct {
data []byte
}
go语言中的reslice语法可以跳过已经读过的数据。
同时定义了ClassFile数据结构来描述class文件的各个部分,该数据结构如下所示:
type ClassFile struct {
//magic uint32
minorVersion uint16
majorVersion uint16
constantPool ConstantPool
accessFlags uint16
thisClass uint16
superClass uint16
interfaces []uint16
fields []*MemberInfo
methods []*MemberInfo
attributes []AttributeInfo
}
ClassFile结构体如实反映了Java虚拟机规范定义的class文件格式。
class文件的魔数为0xCAFEBABE占四个字节
minorVersion为class文件的次版本号
majorVersion为class文件的主版本号
constantPool为常量池
acessFlags为class的可访问标识
thisClass和superClass都为常量池的索引
interfaces为接口索引表,该表存放的是常量池的索引
fields为类的字段表
methods为类的方法表
attributes为类的属性表
常量池
顾名思义,常量池中存放了各种各样的常量信息,包括数字和字符串常量、类和接口名、字段和方法名,等等。
因为常量池中存放的信息各不相同,所以每种常量的格式也不同。常量数据的一个字节是tag,用来区别常量的类型。下面是Java虚拟机给出的常量结构:
cp_info {
u1 tag;
u1 info[];
}
可以将常量池中的常量分为两类:字面量和符号引用。字面量包括数字常量和字符串常量,符号引用包括类和接口名、字段和方法信息等。除了字面量,其他常量都是通过索引直接或间接指向CONSTANT_Utf8_info常量。
属性表
属性表存放着方法的字节码,因为虚拟机中的属性是可以扩展的,不同虚拟机实现可以定义自己的属性类型。由于这个原因,Java虚拟机使用属性名来区分不同的属性。属性结构定义如下所示:
attribute_info {
u2 attribute_name_index;
u4 attribute_length;
u1 info[attribute_length];
}
按照用途,23种预定义属性可以分为三组。第一组属性是实现Java虚拟机所必须的,共有5种;第二组属性是Java类库所必需的,共有12种;第三组属性主要提供给工具使用,共有6种。第三组属性是可选的。
code属性
Code是变长属性,只存在于method_info结构种。Code属性中存放字节码等方法相关信息,其结构定义如下所示:
Code_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 max_stack;
u2 max_locals;
u4 code_length;
u1 code[code_length];
u2 exception_table_length;
{
u2 start_pc;
u2 end_pc;
u2 handler_pc;
u2 catch_type;
} exception_table[exception_table_length];
u2 attribattribute_info attributes[attributes_count];
}
utes_count;
max_stack 给出操作数栈的最大深度,max_locals给出局部变量大小。接下来就是字节码,异常处理表和局部变量表。