上一章我们写了类加载部分,已经能够把类加载到内存中,现在可以操作类结构了,为了更好的操作类,我们还得把对应的字节流解析成结构化的数据(用结构体表示),这章讲的就是class解析
首先我们要先了解Java虚拟机规范8里面定义的class用c结构体能表示为一下
cClassFile {
u4 magic; //Class 文件的标志
u2 minor_version;//Class 的小版本号
u2 major_version;//Class 的大版本号
u2 constant_pool_count;//常量池的数量
cp_info constant_pool[constant_pool_count-1];//常量池
u2 access_flags;//Class 的访问标记
u2 this_class;//当前类
u2 super_class;//父类
u2 interfaces_count;//接口数量
u2 interfaces[interfaces_count];//一个类可以实现多个接口
u2 fields_count;//字段数量
field_info fields[fields_count];//一个类可以有多个字段
u2 methods_count;//方法数量
method_info methods[methods_count];//一个类可以有个多个方法
u2 attributes_count;//此类的属性表中的属性数
attribute_info attributes[attributes_count];//属性表集合
}
因为接下来要用Go结构体来表示这个结构,我们先了解一下Go和Java的基本类型对应关系
Go 类型 | Java 类型 | 描述 |
---|---|---|
bool | boolean | 布尔类型,值为 true 或 false |
int | int | 有符号32位整数 |
int8 | byte | 有符号8位整数 |
int16 | short | 有符号16位整数 |
int32 | int | 有符号32位整数 |
int64 | long | 有符号64位整数 |
uint | 无直接对应 | 无符号32位整数(取决于平台,通常是32位或64位) |
uint8 | byte | 无符号8位整数 |
uint16 | char | 无符号16位整数(Java的char 是16位无符号整数) |
uint32 | 无直接对应 | 无符号32位整数 |
uint64 | 无直接对应 | 无符号64位整数 |
float32 | float | 32位浮点数 |
float64 | double | 64位浮点数 |
string | String | 字符串类型 符号整数 |
接下来就是定义类文件的结构体了,按照上面的就行
gotype ClassFile struct {
// 魔数,通常是0xCAFEBABE,用于标识这是一个Java类文件
// magic uint32
// 次版本号,表示类的次要版本
minorVersion uint16
// 主版本号,表示类的主要版本
majorVersion uint16
// 常量池,包含类中使用的所有常量信息
constantPool ConstantPool
// 访问标志,表示类的访问权限和属性,如public、final等
accessFlags uint16
// 当前类的索引,指向常量池中的一个CONSTANT_Class_info项
thisClass uint16
// 父类的索引,指向常量池中的一个CONSTANT_Class_info项
superClass uint16
// 接口的索引集合,指向常量池中的多个CONSTANT_Class_info项
interfaces []uint16
// 字段表集合,包含类的所有字段信息
fields []*MemberInfo
// 方法表集合,包含类的所有方法信息
methods []*MemberInfo
// 属性表集合,包含类的其他属性信息,如源文件名、代码、异常表等
attributes []AttributeInfo
}
因为后面涉及到解析字节码文件所以我们要很熟悉字节码结构,书中作者是用自己配合Javap写的工具来查看字节码文件,我们直接用插件,首先我们直接用javac xxx.java编译生成class文件,直接打开是这样的
java//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.reservoir;
public class HelloWorld {
public HelloWorld() {
}
public static void main(String[] var0) {
System.out.println("Hello, World!");
}
}
我们需要下一个插件,用16进制打开
安装好重启IDEA
右键class文件即可查看
可以看到开头的魔数
在真正解析class文件前,我们还需要一个ClassReader对象,来读取class文件,可以按特定的字节读取,下面是它的定义
gopackage classfile
import "encoding/binary"
type ClassReader struct {
data []byte // 存储类文件的字节数据
}
// 读取一个无符号8位整数(1字节)
func (self *ClassReader) readUint8() uint8 {
val := self.data[0] // 读取第一个字节
self.data = self.data[1:] // 将数据切片向前移动1字节
return val // 返回读取的值
}
// 读取一个无符号16位整数(2字节),大端序
func (self *ClassReader) readUint16() uint16 {
val := binary.BigEndian.Uint16(self.data) // 使用大端序读取前2字节
self.data = self.data[2:] // 将数据切片向前移动2字节
return val // 返回读取的值
}
// 读取一个无符号32位整数(4字节),大端序
func (self *ClassReader) readUint32() uint32 {
val := binary.BigEndian.Uint32(self.data) // 使用大端序读取前4字节
self.data = self.data[4:] // 将数据切片向前移动4字节
return val // 返回读取的值
}
// 读取一个无符号64位整数(8字节),大端序
func (self *ClassReader) readUint64() uint64 {
val := binary.BigEndian.Uint64(self.data) // 使用大端序读取前8字节
self.data = self.data[8:] // 将数据切片向前移动8字节
return val // 返回读取的值
}
// 读取一个无符号16位整数数组
func (self *ClassReader) readUint16s() []uint16 {
n := self.readUint16() // 读取数组长度
s := make([]uint16, n) // 创建一个长度为n的数组
for i := range s {
s[i] = self.readUint16() // 逐个读取数组元素
}
return s // 返回读取的数组
}
// 读取指定长度的字节数据
func (self *ClassReader) readBytes(n uint32) []byte {
bytes := self.data[:n] // 读取前n个字节
self.data = self.data[n:] // 将数据切片向前移动n字节
return bytes // 返回读取的字节数据
}
这样就方便我们对加载到的类字节流进行操作
接下来真正开始分析类结构,根据下面的代码逻辑,我觉得更好理解,注释很详细,这些方法都是classFile方法
gofunc Parse(classData []byte) (cf *ClassFile, err error) {
defer func() {
if r := recover(); r != nil { // 捕获panic
var ok bool
err, ok = r.(error) // 尝试将panic转换为error
if !ok {
err = fmt.Errorf("%v", r) // 如果不能转换,则创建一个新的error
}
}
}()
cr := &ClassReader{classData} // 创建ClassReader实例
cf = &ClassFile{} // 创建ClassFile实例
cf.read(cr) // 读取类文件数据
return // 返回ClassFile实例和错误
}
func (self *ClassFile) read(reader *ClassReader) {
self.readAndCheckMagic(reader) // 读取并检查魔数
self.readAndCheckVersion(reader) // 读取并检查版本号
self.constantPool = readConstantPool(reader) // 读取常量池
self.accessFlags = reader.readUint16() // 读取访问标志
self.thisClass = reader.readUint16() // 读取当前类的索引
self.superClass = reader.readUint16() // 读取父类的索引
self.interfaces = reader.readUint16s() // 读取接口的索引集合
self.fields = readMembers(reader, self.constantPool) // 读取字段表集合
self.methods = readMembers(reader, self.constantPool) // 读取方法表集合
self.attributes = readAttributes(reader, self.constantPool) // 读取属性表集合
}
func (self *ClassFile) readAndCheckMagic(reader *ClassReader) {
magic := reader.readUint32() // 读取魔数
if magic != 0xCAFEBABE { // 检查魔数是否为0xCAFEBABE
panic("java.lang.ClassFormatError: magic!") // 如果不是,抛出异常
}
}
func (self *ClassFile) readAndCheckVersion(reader *ClassReader) {
self.minorVersion = reader.readUint16() // 读取次版本号
self.majorVersion = reader.readUint16() // 读取主版本号
switch self.majorVersion {
case 45:
return // JDK 1.1
case 46, 47, 48, 49, 50, 51, 52:
if self.minorVersion == 0 {
return // JDK 1.2 - JDK 8
}
}
panic("java.lang.UnsupportedClassVersionError!") // 如果不是支持的版本,抛出异常
}
func (self *ClassFile) MinorVersion() uint16 {
return self.minorVersion // 返回次版本号
}
func (self *ClassFile) MajorVersion() uint16 {
return self.majorVersion // 返回主版本号
}
func (self *ClassFile) ConstantPool() ConstantPool {
return self.constantPool // 返回常量池
}
func (self *ClassFile) AccessFlags() uint16 {
return self.accessFlags // 返回访问标志
}
func (self *ClassFile) Fields() []*MemberInfo {
return self.fields // 返回字段表集合
}
func (self *ClassFile) Methods() []*MemberInfo {
return self.methods // 返回方法表集合
}
func (self *ClassFile) ClassName() string {
return self.constantPool.getClassName(self.thisClass) // 返回当前类的全限定名
}
func (self *ClassFile) SuperClassName() string {
if self.superClass > 0 {
return self.constantPool.getClassName(self.superClass) // 返回父类的全限定名
}
return "" // 如果没有父类,返回空字符串
}
func (self *ClassFile) InterfaceNames() []string {
interfaceNames := make([]string, len(self.interfaces)) // 创建接口名数组
for i, cpIndex := range self.interfaces {
interfaceNames[i] = self.constantPool.getClassName(cpIndex) // 逐个获取接口的全限定名
}
return interfaceNames // 返回接口名数组
}
上面的代码很容易理解也有注释,接下来就只剩常量池,字段表和方法表,还有属性表的解析了,先来看看字段表和方法表吧,首先看看java虚拟机规范是怎么定义字段和方法的?
先看看字段是怎么定义的
gofield_info {
u2 access_flags; // 字段的访问标志,表示字段的访问权限和属性,如public、private、static等。
u2 name_index; // 指向常量池的一个索引,表示字段的名称。
u2 descriptor_index; // 指向常量池的一个索引,表示字段的描述符。
u2 attributes_count; // 字段属性的数量。
attribute_info attributes[attributes_count]; // 字段属性表,包含了字段的额外信息,如常量值等。
}
方法的定义和字段差不多
gomethod_info {
u2 access_flags;
u2 name_index;
u2 descriptor_index;
u2 attributes_count;
attribute_info attributes[attributes_count]; // 方法属性表,包含了方法的额外信息,如代码(Code)、异常(Exceptions)等。
}
我们可以知道,字段和方法还有类,都是有属性表的,这很容易理解,注解在JVM中也是一种属性,字段,方法类都能应用注解,当然也有字段,方法,和类各自独有的属性,
下面介绍一下类独有的属性
SourceFile:表示源文件的名称。
InnerClasses:表示内部类的信息。
EnclosingMethod:表示局部类或匿名类所包含的方法。
BootstrapMethods:包含用于动态调用的bootstrap方法。
Module:表示模块的信息。
ModulePackages:表示模块中的包。
ModuleMainClass:表示模块的主类。
NestHost:表示嵌套类的宿主类。
NestMembers:表示嵌套类的成员。
方法独有的属性
Code:表示方法的字节码和异常处理表。
Exceptions:表示方法声明抛出的异常。
AnnotationDefault:表示注解元素的默认值。
MethodParameters:表示方法参数的信息。
字段独有的属性
以下是通用的属性
Deprecated:表示类、字段或方法已被弃用。
Synthetic:表示类、字段或方法是由编译器生成的,不是源代码中显式定义的。
Signature:表示泛型类型的签名信息。
RuntimeVisibleAnnotations:表示运行时可见的注解。
RuntimeInvisibleAnnotations:表示运行时不可见的注解。
RuntimeVisibleParameterAnnotations:表示方法参数的运行时可见注解。
RuntimeInvisibleParameterAnnotations:表示方法参数的运行时不可见注解。
RuntimeVisibleTypeAnnotations:表示运行时可见的类型注解。
RuntimeInvisibleTypeAnnotations:表示运行时不可见的类型注解。
是不是有种豁然开朗的感觉?接下来我们再看看怎么读取字段和方法
我们看一下代码,前面说过他们的结构一模一样,所以直接写一份代码
gotype MemberInfo struct {
cp ConstantPool // 常量池
accessFlags uint16 // 访问标志
nameIndex uint16 // 名称索引
descriptorIndex uint16 // 描述符索引
attributes []AttributeInfo // 属性表
}
// 读取字段或方法表
func readMembers(reader *ClassReader, cp ConstantPool) []*MemberInfo {
memberCount := reader.readUint16() // 读取成员数量
members := make([]*MemberInfo, memberCount) // 创建成员数组
for i := range members {
members[i] = readMember(reader, cp) // 逐个读取成员信息
}
return members // 返回成员数组
}
func readMember(reader *ClassReader, cp ConstantPool) *MemberInfo {
return &MemberInfo{
cp: cp, // 常量池
accessFlags: reader.readUint16(), // 读取访问标志
nameIndex: reader.readUint16(), // 读取名称索引
descriptorIndex: reader.readUint16(), // 读取描述符索引
attributes: readAttributes(reader, cp), // 读取属性表
}
}
func (self *MemberInfo) AccessFlags() uint16 {
return self.accessFlags // 返回访问标志
}
func (self *MemberInfo) Name() string {
return self.cp.getUtf8(self.nameIndex) // 从常量池中获取名称
}
func (self *MemberInfo) Descriptor() string {
return self.cp.getUtf8(self.descriptorIndex) // 从常量池中获取描述符
}
上面的代码写的很明白,按顺序读取内容
这里属性表的读取我们后面再看,接下来就剩属性表和常量池的解析读取了。
首先我们来熟悉一下常量池结构,看类结构定义,常量池也是一个表结构,一个条目一个条目,不过Java虚拟机规范定义了14种(Java8,9之后增加到17项)常量项,我们需要定义基础接口,这里
Java虚拟机规范定义了常量项的基本结构
gocp_info {
u1 tag; // 表示常量项的类型,不同的值对应不同的常量类型。
u1 info[]; // 根据tag的值,包含不同的字段信息。
}
并且预定义了17种常量项
CONSTANT_Class:表示一个类或接口的符号引用。
CONSTANT_Fieldref:表示字段的符号引用。
CONSTANT_Methodref:表示方法的符号引用。
CONSTANT_InterfaceMethodref:表示接口方法的符号引用。
CONSTANT_String:表示字符串字面量的符号引用。
CONSTANT_Integer:表示int类型的字面量。
CONSTANT_Float:表示float类型的字面量。
CONSTANT_Long:表示long类型的字面量。
CONSTANT_Double:表示double类型的字面量。
CONSTANT_NameAndType:表示字段或方法的部分符号引用。
CONSTANT_Utf8:表示一个UTF-8编码的字符串。
CONSTANT_MethodHandle:表示方法句柄。
CONSTANT_MethodType:表示方法类型。
CONSTANT_Dynamic:表示动态计算的常量。 // java9加入
CONSTANT_InvokeDynamic:表示一个动态方法调用点。
CONSTANT_Module:表示一个模块。 // Java9加入
CONSTANT_Package:表示一个包。 // Java9加入
下面是17种常量项结构定义,有Java虚拟机规范
goCONSTANT_Class_info {
u1 tag; // 值为7,表示这是一个CONSTANT_Class类型的常量项
u2 name_index; // 指向常量池的一个索引,表示类或接口的名称
}
CONSTANT_Fieldref_info {
u1 tag; // 值为9,表示这是一个CONSTANT_Fieldref类型的常量项
u2 class_index; // 指向常量池的一个索引,表示字段所属的类
u2 name_and_type_index; // 指向常量池的一个索引,表示字段的名称和类型
}
CONSTANT_Methodref_info {
u1 tag; // 值为10,表示这是一个CONSTANT_Methodref类型的常量项
u2 class_index; // 指向常量池的一个索引,表示方法所属的类
u2 name_and_type_index; // 指向常量池的一个索引,表示方法的名称和类型
}
CONSTANT_InterfaceMethodref_info {
u1 tag; // 值为11,表示这是一个CONSTANT_InterfaceMethodref类型的常量项
u2 class_index; // 指向常量池的一个索引,表示接口方法所属的接口
u2 name_and_type_index; // 指向常量池的一个索引,表示接口方法的名称和类型
}
CONSTANT_String_info {
u1 tag; // 值为8,表示这是一个CONSTANT_String类型的常量项
u2 string_index; // 指向常量池的一个索引,表示字符串字面量的内容
}
CONSTANT_Integer_info {
u1 tag; // 值为3,表示这是一个CONSTANT_Integer类型的常量项
u4 bytes; // 表示int类型的字面量值
}
CONSTANT_Float_info {
u1 tag; // 值为4,表示这是一个CONSTANT_Float类型的常量项
u4 bytes; // 表示float类型的字面量值
}
CONSTANT_Long_info {
u1 tag; // 值为5,表示这是一个CONSTANT_Long类型的常量项
u4 high_bytes; // 表示long类型字面量值的高32位
u4 low_bytes; // 表示long类型字面量值的低32位
}
CONSTANT_Double_info {
u1 tag; // 值为6,表示这是一个CONSTANT_Double类型的常量项
u4 high_bytes; // 表示double类型字面量值的高32位
u4 low_bytes; // 表示double类型字面量值的低32位
}
CONSTANT_NameAndType_info {
u1 tag; // 值为12,表示这是一个CONSTANT_NameAndType类型的常量项
u2 name_index; // 指向常量池的一个索引,表示字段或方法的名称
u2 descriptor_index; // 指向常量池的一个索引,表示字段或方法的描述符
}
CONSTANT_Utf8_info {
u1 tag; // 值为1,表示这是一个CONSTANT_Utf8类型的常量项
u2 length; // 表示字符串的长度
u1 bytes[length]; // 表示UTF-8编码的字符串内容
}
CONSTANT_MethodHandle_info {
u1 tag; // 值为15,表示这是一个CONSTANT_MethodHandle类型的常量项
u1 reference_kind; // 表示方法句柄的类型,值在1到9之间
u2 reference_index; // 指向常量池的一个索引,表示方法句柄的引用
}
CONSTANT_MethodType_info {
u1 tag; // 值为16,表示这是一个CONSTANT_MethodType类型的常量项
u2 descriptor_index; // 指向常量池的一个索引,表示方法的描述符
}
CONSTANT_Dynamic_info {
u1 tag; // 值为17,表示这是一个CONSTANT_Dynamic类型的常量项
u2 bootstrap_method_attr_index; // 指向bootstrap方法的索引
u2 name_and_type_index; // 指向常量池的一个索引,表示动态常量的名称和类型
}
CONSTANT_InvokeDynamic_info {
u1 tag; // 值为18,表示这是一个CONSTANT_InvokeDynamic类型的常量项
u2 bootstrap_method_attr_index; // 指向bootstrap方法的索引
u2 name_and_type_index; // 指向常量池的一个索引,表示动态方法调用点的名称和类型
}
CONSTANT_Module_info {
u1 tag; // 值为19,表示这是一个CONSTANT_Module类型的常量项
u2 name_index; // 指向常量池的一个索引,表示模块的名称
}
CONSTANT_Package_info {
u1 tag; // 值为20,表示这是一个CONSTANT_Package类型的常量项
u2 name_index; // 指向常量池的一个索引,表示包的名称
}
接下来定义一些结构来表示这些不同的常量项,首先是常量tag
gopackage classfile
// Constant pool tags
const (
CONSTANT_Class = 7 // 类或接口的符号引用
CONSTANT_Fieldref = 9 // 字段的符号引用
CONSTANT_Methodref = 10 // 方法的符号引用
CONSTANT_InterfaceMethodref = 11 // 接口方法的符号引用
CONSTANT_String = 8 // 字符串字面量的符号引用
CONSTANT_Integer = 3 // int类型的字面量
CONSTANT_Float = 4 // float类型的字面量
CONSTANT_Long = 5 // long类型的字面量
CONSTANT_Double = 6 // double类型的字面量
CONSTANT_NameAndType = 12 // 字段或方法的部分符号引用
CONSTANT_Utf8 = 1 // UTF-8编码的字符串
CONSTANT_MethodHandle = 15 // 方法句柄
CONSTANT_MethodType = 16 // 方法类型
CONSTANT_InvokeDynamic = 18 // 动态方法调用点
)
这里定义了14个常量来表示不同的常量项
接下来是常量接口定义
go/*
cp_info {
u1 tag;
u1 info[];
}
*/
type ConstantInfo interface {
readInfo(reader *ClassReader) // 读取常量信息的接口方法
}
func readConstantInfo(reader *ClassReader, cp ConstantPool) ConstantInfo {
tag := reader.readUint8() // 读取常量项的tag
c := newConstantInfo(tag, cp) // 根据tag创建对应的常量信息实例
c.readInfo(reader) // 读取常量项的具体信息
return c // 返回常量信息实例
}
// todo ugly code
func newConstantInfo(tag uint8, cp ConstantPool) ConstantInfo {
switch tag {
case CONSTANT_Integer:
return &ConstantIntegerInfo{} // 创建Integer类型的常量信息实例
case CONSTANT_Float:
return &ConstantFloatInfo{} // 创建Float类型的常量信息实例
case CONSTANT_Long:
return &ConstantLongInfo{} // 创建Long类型的常量信息实例
case CONSTANT_Double:
return &ConstantDoubleInfo{} // 创建Double类型的常量信息实例
case CONSTANT_Utf8:
return &ConstantUtf8Info{} // 创建Utf8类型的常量信息实例
case CONSTANT_String:
return &ConstantStringInfo{cp: cp} // 创建String类型的常量信息实例
case CONSTANT_Class:
return &ConstantClassInfo{cp: cp} // 创建Class类型的常量信息实例
case CONSTANT_Fieldref:
return &ConstantFieldrefInfo{ConstantMemberrefInfo{cp: cp}} // 创建Fieldref类型的常量信息实例
case CONSTANT_Methodref:
return &ConstantMethodrefInfo{ConstantMemberrefInfo{cp: cp}} // 创建Methodref类型的常量信息实例
case CONSTANT_InterfaceMethodref:
return &ConstantInterfaceMethodrefInfo{ConstantMemberrefInfo{cp: cp}} // 创建InterfaceMethodref类型的常量信息实例
case CONSTANT_NameAndType:
return &ConstantNameAndTypeInfo{} // 创建NameAndType类型的常量信息实例
case CONSTANT_MethodType:
return &ConstantMethodTypeInfo{} // 创建MethodType类型的常量信息实例
case CONSTANT_MethodHandle:
return &ConstantMethodHandleInfo{} // 创建MethodHandle类型的常量信息实例
case CONSTANT_InvokeDynamic:
return &ConstantInvokeDynamicInfo{} // 创建InvokeDynamic类型的常量信息实例
default:
panic("java.lang.ClassFormatError: constant pool tag!") // 如果tag不合法,抛出异常
}
}
其他的所有常量结构都是实现这个接口
接下来定义常量表
gopackage classfile
import "fmt"
type ConstantPool []ConstantInfo // 常量池类型,是一个ConstantInfo切片的别名
func readConstantPool(reader *ClassReader) ConstantPool {
cpCount := int(reader.readUint16()) // 读取常量池计数,即常量池中常量项的数量
cp := make([]ConstantInfo, cpCount) // 创建一个大小为cpCount的ConstantInfo切片
// 常量池表的索引从1到constant_pool_count - 1
for i := 1; i < cpCount; i++ {
cp[i] = readConstantInfo(reader, cp) // 读取常量项信息
// JVM规范中提到,所有8字节常量在常量池表中占用两个条目
// 如果CONSTANT_Long_info或CONSTANT_Double_info结构是常量池表中索引n处的条目,
// 那么常量池中下一个可用条目位于索引n+2处。索引n+1必须有效,但被认为是不可用的。
switch cp[i].(type) {
case *ConstantLongInfo, *ConstantDoubleInfo:
i++ // 跳过下一个索引
}
}
return cp // 返回常量池
}
func (self ConstantPool) getConstantInfo(index uint16) ConstantInfo {
if cpInfo := self[index]; cpInfo != nil { // 获取常量池中指定索引的常量信息
return cpInfo
}
panic(fmt.Errorf("Invalid constant pool index: %v!", index)) // 如果索引无效,抛出异常
}
func (self ConstantPool) getNameAndType(index uint16) (string, string) {
ntInfo := self.getConstantInfo(index).(*ConstantNameAndTypeInfo) // 获取NameAndType类型的常量信息
name := self.getUtf8(ntInfo.nameIndex) // 获取名称
_type := self.getUtf8(ntInfo.descriptorIndex) // 获取描述符
return name, _type // 返回名称和描述符
}
func (self ConstantPool) getClassName(index uint16) string {
classInfo := self.getConstantInfo(index).(*ConstantClassInfo) // 获取Class类型的常量信息
return self.getUtf8(classInfo.nameIndex) // 获取类名
}
func (self ConstantPool) getUtf8(index uint16) string {
utf8Info := self.getConstantInfo(index).(*ConstantUtf8Info) // 获取Utf8类型的常量信息
return utf8Info.str // 返回UTF-8字符串
}
其实到这里整个常量池的加载流程都已经分析完了,多看几遍代码熟悉一下,接下来就是各种常量项的定义和readinfo方法,按照Java虚拟机规范写就可以
首先是CONSTANT_String_info
gopackage classfile
/*
CONSTANT_String_info {
u1 tag;
u2 string_index;
}
*/
type ConstantStringInfo struct {
cp ConstantPool
stringIndex uint16
}
func (self *ConstantStringInfo) readInfo(reader *ClassReader) {
self.stringIndex = reader.readUint16()
}
func (self *ConstantStringInfo) String() string {
return self.cp.getUtf8(self.stringIndex)
}
数字相关
go/*
CONSTANT_Integer_info {
u1 tag;
u4 bytes;
}
*/
type ConstantIntegerInfo struct {
val int32 // 存储整数值
}
func (self *ConstantIntegerInfo) readInfo(reader *ClassReader) {
bytes := reader.readUint32() // 从ClassReader中读取4字节数据
self.val = int32(bytes) // 将4字节数据转换为int32类型并存储在val字段中
}
func (self *ConstantIntegerInfo) Value() int32 {
return self.val // 返回存储的整数值
}
/*
CONSTANT_Float_info {
u1 tag;
u4 bytes;
}
*/
type ConstantFloatInfo struct {
val float32 // 存储浮点数值
}
func (self *ConstantFloatInfo) readInfo(reader *ClassReader) {
bytes := reader.readUint32() // 从ClassReader中读取4字节数据
self.val = math.Float32frombits(bytes) // 将4字节数据转换为float32类型并存储在val字段中
}
func (self *ConstantFloatInfo) Value() float32 {
return self.val // 返回存储的浮点数值
}
/*
CONSTANT_Long_info {
u1 tag;
u4 high_bytes;
u4 low_bytes;
}
*/
type ConstantLongInfo struct {
val int64 // 存储长整数值
}
func (self *ConstantLongInfo) readInfo(reader *ClassReader) {
bytes := reader.readUint64() // 从ClassReader中读取8字节数据
self.val = int64(bytes) // 将8字节数据转换为int64类型并存储在val字段中
}
func (self *ConstantLongInfo) Value() int64 {
return self.val // 返回存储的长整数值
}
/*
CONSTANT_Double_info {
u1 tag;
u4 high_bytes;
u4 low_bytes;
}
*/
type ConstantDoubleInfo struct {
val float64 // 存储双精度浮点数值
}
func (self *ConstantDoubleInfo) readInfo(reader *ClassReader) {
bytes := reader.readUint64() // 从ClassReader中读取8字节数据
self.val = math.Float64frombits(bytes) // 将8字节数据转换为float64类型并存储在val字段中
}
func (self *ConstantDoubleInfo) Value() float64 {
return self.val // 返回存储的双精度浮点数值
}
CONSTANT_Utf8_info
go/*
CONSTANT_Utf8_info {
u1 tag;
u2 length;
u1 bytes[length];
}
*/
type ConstantUtf8Info struct {
str string // 存储解码后的字符串
}
func (self *ConstantUtf8Info) readInfo(reader *ClassReader) {
length := uint32(reader.readUint16()) // 读取字符串长度
bytes := reader.readBytes(length) // 读取指定长度的字节数据
self.str = decodeMUTF8(bytes) // 解码字节数据为字符串并存储在str字段中
}
func (self *ConstantUtf8Info) Str() string {
return self.str // 返回存储的字符串
}
// mutf8 -> utf16 -> utf32 -> string
func decodeMUTF8(bytearr []byte) string {
utflen := len(bytearr) // 获取字节数组的长度
chararr := make([]uint16, utflen) // 创建一个长度为utflen的uint16数组,用于存储UTF-16编码的字符
var c, char2, char3 uint16 // 定义变量用于存储字符和字节数据
count := 0 // 定义计数器,用于遍历字节数组
chararr_count := 0 // 定义计数器,用于记录已处理的字符数量
// 处理单字节字符
for count < utflen {
c = uint16(bytearr[count])
if c > 127 {
break
}
count++
chararr[chararr_count] = c
chararr_count++
}
// 处理多字节字符
for count < utflen {
c = uint16(bytearr[count])
switch c >> 4 {
case 0, 1, 2, 3, 4, 5, 6, 7:
/* 0xxxxxxx*/
count++
chararr[chararr_count] = c
chararr_count++
case 12, 13:
/* 110x xxxx 10xx xxxx*/
count += 2
if count > utflen {
panic("malformed input: partial character at end")
}
char2 = uint16(bytearr[count-1])
if char2&0xC0 != 0x80 {
panic(fmt.Errorf("malformed input around byte %v", count))
}
chararr[chararr_count] = c&0x1F<<6 | char2&0x3F
chararr_count++
case 14:
/* 1110 xxxx 10xx xxxx 10xx xxxx*/
count += 3
if count > utflen {
panic("malformed input: partial character at end")
}
char2 = uint16(bytearr[count-2])
char3 = uint16(bytearr[count-1])
if char2&0xC0 != 0x80 || char3&0xC0 != 0x80 {
panic(fmt.Errorf("malformed input around byte %v", (count - 1)))
}
chararr[chararr_count] = c&0x0F<<12 | char2&0x3F<<6 | char3&0x3F<<0
chararr_count++
default:
/* 10xx xxxx, 1111 xxxx */
panic(fmt.Errorf("malformed input around byte %v", count))
}
}
// 调整chararr的长度为已处理的字符数量
chararr = chararr[0:chararr_count]
runes := utf16.Decode(chararr) // 将UTF-16编码的字符数组解码为UTF-32编码的字符数组
return string(runes) // 将UTF-32编码的字符数组转换为字符串并返回
}
下面懒的注释,都是差不多的
gopackage classfile
/*
CONSTANT_NameAndType_info {
u1 tag;
u2 name_index;
u2 descriptor_index;
}
*/
type ConstantNameAndTypeInfo struct {
nameIndex uint16
descriptorIndex uint16
}
func (self *ConstantNameAndTypeInfo) readInfo(reader *ClassReader) {
self.nameIndex = reader.readUint16()
self.descriptorIndex = reader.readUint16()
}
package classfile
/*
CONSTANT_Fieldref_info {
u1 tag;
u2 class_index;
u2 name_and_type_index;
}
CONSTANT_Methodref_info {
u1 tag;
u2 class_index;
u2 name_and_type_index;
}
CONSTANT_InterfaceMethodref_info {
u1 tag;
u2 class_index;
u2 name_and_type_index;
}
*/
type ConstantFieldrefInfo struct{ ConstantMemberrefInfo }
type ConstantMethodrefInfo struct{ ConstantMemberrefInfo }
type ConstantInterfaceMethodrefInfo struct{ ConstantMemberrefInfo }
type ConstantMemberrefInfo struct {
cp ConstantPool
classIndex uint16
nameAndTypeIndex uint16
}
func (self *ConstantMemberrefInfo) readInfo(reader *ClassReader) {
self.classIndex = reader.readUint16()
self.nameAndTypeIndex = reader.readUint16()
}
func (self *ConstantMemberrefInfo) ClassName() string {
return self.cp.getClassName(self.classIndex)
}
func (self *ConstantMemberrefInfo) NameAndDescriptor() (string, string) {
return self.cp.getNameAndType(self.nameAndTypeIndex)
}
package classfile
/*
CONSTANT_Class_info {
u1 tag;
u2 name_index;
}
*/
type ConstantClassInfo struct {
cp ConstantPool
nameIndex uint16
}
func (self *ConstantClassInfo) readInfo(reader *ClassReader) {
self.nameIndex = reader.readUint16()
}
func (self *ConstantClassInfo) Name() string {
return self.cp.getUtf8(self.nameIndex)
}
package classfile
/*
CONSTANT_MethodHandle_info {
u1 tag;
u1 reference_kind;
u2 reference_index;
}
*/
type ConstantMethodHandleInfo struct {
referenceKind uint8
referenceIndex uint16
}
func (self *ConstantMethodHandleInfo) readInfo(reader *ClassReader) {
self.referenceKind = reader.readUint8()
self.referenceIndex = reader.readUint16()
}
/*
CONSTANT_MethodType_info {
u1 tag;
u2 descriptor_index;
}
*/
type ConstantMethodTypeInfo struct {
descriptorIndex uint16
}
func (self *ConstantMethodTypeInfo) readInfo(reader *ClassReader) {
self.descriptorIndex = reader.readUint16()
}
/*
CONSTANT_InvokeDynamic_info {
u1 tag;
u2 bootstrap_method_attr_index;
u2 name_and_type_index;
}
*/
type ConstantInvokeDynamicInfo struct {
bootstrapMethodAttrIndex uint16
nameAndTypeIndex uint16
}
func (self *ConstantInvokeDynamicInfo) readInfo(reader *ClassReader) {
self.bootstrapMethodAttrIndex = reader.readUint16()
self.nameAndTypeIndex = reader.readUint16()
}
至此,常量池解析结束
接下来就是属性表了,根据之前关于属性表的分析,类的属性表应该是大差不差,而且属性表和常量池解析也很像,都是分为不同类项去解析,这张只解析Java虚拟机规范预定义属性其中八种,看一下属性怎么定义的
goattribute_info { u2 attribute_name_index; u4 attribute_length; u1 info[attribute_length]; }
attribute_name_index:指向常量池的一个索引,表示属性的名称。
attribute_length:属性的长度,表示info数组的长度。
info:属性的具体内容,根据属性的不同,内容也不同。
我们直接开始定义接口,和属性表
go/*
attribute_info {
u2 attribute_name_index;
u4 attribute_length;
u1 info[attribute_length];
}
*/
type AttributeInfo interface {
readInfo(reader *ClassReader) // 读取属性信息的接口方法
}
func readAttributes(reader *ClassReader, cp ConstantPool) []AttributeInfo {
attributesCount := reader.readUint16() // 读取属性数量
attributes := make([]AttributeInfo, attributesCount) // 创建属性数组
for i := range attributes {
attributes[i] = readAttribute(reader, cp) // 逐个读取属性信息
}
return attributes // 返回属性数组
}
func readAttribute(reader *ClassReader, cp ConstantPool) AttributeInfo {
attrNameIndex := reader.readUint16() // 读取属性名称索引
attrName := cp.getUtf8(attrNameIndex) // 获取属性名称
attrLen := reader.readUint32() // 读取属性长度
attrInfo := newAttributeInfo(attrName, attrLen, cp) // 创建属性信息实例
attrInfo.readInfo(reader) // 读取属性具体信息
return attrInfo // 返回属性信息实例
}
func newAttributeInfo(attrName string, attrLen uint32, cp ConstantPool) AttributeInfo {
switch attrName {
case "Code":
return &CodeAttribute{cp: cp} // 创建Code属性实例
case "ConstantValue":
return &ConstantValueAttribute{} // 创建ConstantValue属性实例
case "Deprecated":
return &DeprecatedAttribute{} // 创建Deprecated属性实例
case "Exceptions":
return &ExceptionsAttribute{} // 创建Exceptions属性实例
case "LineNumberTable":
return &LineNumberTableAttribute{} // 创建LineNumberTable属性实例
case "LocalVariableTable":
return &LocalVariableTableAttribute{} // 创建LocalVariableTable属性实例
case "SourceFile":
return &SourceFileAttribute{cp: cp} // 创建SourceFile属性实例
case "Synthetic":
return &SyntheticAttribute{} // 创建Synthetic属性实例
default:
return &UnparsedAttribute{attrName, attrLen, nil} // 创建Unparsed属性实例
}
}
接下来给出9中属性定义,一种是默认的未知属性
go/*
attribute_info {
u2 attribute_name_index;
u4 attribute_length;
u1 info[attribute_length];
}
*/
type UnparsedAttribute struct {
name string // 属性名称
length uint32 // 属性长度
info []byte // 属性内容
}
func (self *UnparsedAttribute) readInfo(reader *ClassReader) {
self.info = reader.readBytes(self.length) // 从ClassReader中读取指定长度的字节数据,并存储在info字段中
}
func (self *UnparsedAttribute) Info() []byte {
return self.info // 返回属性内容
}
package classfile
/*
SourceFile_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 sourcefile_index;
}
*/
type SourceFileAttribute struct {
cp ConstantPool
sourceFileIndex uint16
}
func (self *SourceFileAttribute) readInfo(reader *ClassReader) {
self.sourceFileIndex = reader.readUint16()
}
func (self *SourceFileAttribute) FileName() string {
return self.cp.getUtf8(self.sourceFileIndex)
}
package classfile
/*
Signature_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 signature_index;
}
*/
type SignatureAttribute struct {
cp ConstantPool
signatureIndex uint16
}
func (self *SignatureAttribute) readInfo(reader *ClassReader) {
self.signatureIndex = reader.readUint16()
}
func (self *SignatureAttribute) Signature() string {
return self.cp.getUtf8(self.signatureIndex)
}
package classfile
/*
Deprecated_attribute {
u2 attribute_name_index;
u4 attribute_length;
}
*/
type DeprecatedAttribute struct {
MarkerAttribute
}
/*
Synthetic_attribute {
u2 attribute_name_index;
u4 attribute_length;
}
*/
type SyntheticAttribute struct {
MarkerAttribute
}
type MarkerAttribute struct{}
func (self *MarkerAttribute) readInfo(reader *ClassReader) {
// read nothing
}
package classfile
/*
LocalVariableTypeTable_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 local_variable_type_table_length;
{ u2 start_pc;
u2 length;
u2 name_index;
u2 signature_index;
u2 index;
} local_variable_type_table[local_variable_type_table_length];
}
*/
type LocalVariableTypeTableAttribute struct {
localVariableTypeTable []*LocalVariableTypeTableEntry
}
type LocalVariableTypeTableEntry struct {
startPc uint16
length uint16
nameIndex uint16
signatureIndex uint16
index uint16
}
func (self *LocalVariableTypeTableAttribute) readInfo(reader *ClassReader) {
localVariableTypeTableLength := reader.readUint16()
self.localVariableTypeTable = make([]*LocalVariableTypeTableEntry, localVariableTypeTableLength)
for i := range self.localVariableTypeTable {
self.localVariableTypeTable[i] = &LocalVariableTypeTableEntry{
startPc: reader.readUint16(),
length: reader.readUint16(),
nameIndex: reader.readUint16(),
signatureIndex: reader.readUint16(),
index: reader.readUint16(),
}
}
}
package classfile
/*
LocalVariableTable_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 local_variable_table_length;
{ u2 start_pc;
u2 length;
u2 name_index;
u2 descriptor_index;
u2 index;
} local_variable_table[local_variable_table_length];
}
*/
type LocalVariableTableAttribute struct {
localVariableTable []*LocalVariableTableEntry
}
type LocalVariableTableEntry struct {
startPc uint16
length uint16
nameIndex uint16
descriptorIndex uint16
index uint16
}
func (self *LocalVariableTableAttribute) readInfo(reader *ClassReader) {
localVariableTableLength := reader.readUint16()
self.localVariableTable = make([]*LocalVariableTableEntry, localVariableTableLength)
for i := range self.localVariableTable {
self.localVariableTable[i] = &LocalVariableTableEntry{
startPc: reader.readUint16(),
length: reader.readUint16(),
nameIndex: reader.readUint16(),
descriptorIndex: reader.readUint16(),
index: reader.readUint16(),
}
}
}
package classfile
/*
LineNumberTable_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 line_number_table_length;
{ u2 start_pc;
u2 line_number;
} line_number_table[line_number_table_length];
}
*/
type LineNumberTableAttribute struct {
lineNumberTable []*LineNumberTableEntry
}
type LineNumberTableEntry struct {
startPc uint16
lineNumber uint16
}
func (self *LineNumberTableAttribute) readInfo(reader *ClassReader) {
lineNumberTableLength := reader.readUint16()
self.lineNumberTable = make([]*LineNumberTableEntry, lineNumberTableLength)
for i := range self.lineNumberTable {
self.lineNumberTable[i] = &LineNumberTableEntry{
startPc: reader.readUint16(),
lineNumber: reader.readUint16(),
}
}
}
func (self *LineNumberTableAttribute) GetLineNumber(pc int) int {
for i := len(self.lineNumberTable) - 1; i >= 0; i-- {
entry := self.lineNumberTable[i]
if pc >= int(entry.startPc) {
return int(entry.lineNumber)
}
}
return -1
}
package classfile
/*
InnerClasses_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 number_of_classes;
{ u2 inner_class_info_index;
u2 outer_class_info_index;
u2 inner_name_index;
u2 inner_class_access_flags;
} classes[number_of_classes];
}
*/
type InnerClassesAttribute struct {
classes []*InnerClassInfo
}
type InnerClassInfo struct {
innerClassInfoIndex uint16
outerClassInfoIndex uint16
innerNameIndex uint16
innerClassAccessFlags uint16
}
func (self *InnerClassesAttribute) readInfo(reader *ClassReader) {
numberOfClasses := reader.readUint16()
self.classes = make([]*InnerClassInfo, numberOfClasses)
for i := range self.classes {
self.classes[i] = &InnerClassInfo{
innerClassInfoIndex: reader.readUint16(),
outerClassInfoIndex: reader.readUint16(),
innerNameIndex: reader.readUint16(),
innerClassAccessFlags: reader.readUint16(),
}
}
}
package classfile
/*
Exceptions_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 number_of_exceptions;
u2 exception_index_table[number_of_exceptions];
}
*/
type ExceptionsAttribute struct {
exceptionIndexTable []uint16
}
func (self *ExceptionsAttribute) readInfo(reader *ClassReader) {
self.exceptionIndexTable = reader.readUint16s()
}
func (self *ExceptionsAttribute) ExceptionIndexTable() []uint16 {
return self.exceptionIndexTable
}
package classfile
/*
EnclosingMethod_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 class_index;
u2 method_index;
}
*/
type EnclosingMethodAttribute struct {
cp ConstantPool
classIndex uint16
methodIndex uint16
}
func (self *EnclosingMethodAttribute) readInfo(reader *ClassReader) {
self.classIndex = reader.readUint16()
self.methodIndex = reader.readUint16()
}
func (self *EnclosingMethodAttribute) ClassName() string {
return self.cp.getClassName(self.classIndex)
}
func (self *EnclosingMethodAttribute) MethodNameAndDescriptor() (string, string) {
if self.methodIndex > 0 {
return self.cp.getNameAndType(self.methodIndex)
} else {
return "", ""
}
}
package classfile
/*
ConstantValue_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 constantvalue_index;
}
*/
type ConstantValueAttribute struct {
constantValueIndex uint16
}
func (self *ConstantValueAttribute) readInfo(reader *ClassReader) {
self.constantValueIndex = reader.readUint16()
}
func (self *ConstantValueAttribute) ConstantValueIndex() uint16 {
return self.constantValueIndex
}
package classfile
/*
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 attributes_count;
attribute_info attributes[attributes_count];
}
*/
type CodeAttribute struct {
cp ConstantPool
maxStack uint16
maxLocals uint16
code []byte
exceptionTable []*ExceptionTableEntry
attributes []AttributeInfo
}
func (self *CodeAttribute) readInfo(reader *ClassReader) {
self.maxStack = reader.readUint16()
self.maxLocals = reader.readUint16()
codeLength := reader.readUint32()
self.code = reader.readBytes(codeLength)
self.exceptionTable = readExceptionTable(reader)
self.attributes = readAttributes(reader, self.cp)
}
func (self *CodeAttribute) MaxStack() uint {
return uint(self.maxStack)
}
func (self *CodeAttribute) MaxLocals() uint {
return uint(self.maxLocals)
}
func (self *CodeAttribute) Code() []byte {
return self.code
}
func (self *CodeAttribute) ExceptionTable() []*ExceptionTableEntry {
return self.exceptionTable
}
type ExceptionTableEntry struct {
startPc uint16
endPc uint16
handlerPc uint16
catchType uint16
}
func readExceptionTable(reader *ClassReader) []*ExceptionTableEntry {
exceptionTableLength := reader.readUint16()
exceptionTable := make([]*ExceptionTableEntry, exceptionTableLength)
for i := range exceptionTable {
exceptionTable[i] = &ExceptionTableEntry{
startPc: reader.readUint16(),
endPc: reader.readUint16(),
handlerPc: reader.readUint16(),
catchType: reader.readUint16(),
}
}
return exceptionTable
}
func (self *ExceptionTableEntry) StartPc() uint16 {
return self.startPc
}
func (self *ExceptionTableEntry) EndPc() uint16 {
return self.endPc
}
func (self *ExceptionTableEntry) HandlerPc() uint16 {
return self.handlerPc
}
func (self *ExceptionTableEntry) CatchType() uint16 {
return self.catchType
}
package classfile
/*
BootstrapMethods_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 num_bootstrap_methods;
{ u2 bootstrap_method_ref;
u2 num_bootstrap_arguments;
u2 bootstrap_arguments[num_bootstrap_arguments];
} bootstrap_methods[num_bootstrap_methods];
}
*/
type BootstrapMethodsAttribute struct {
bootstrapMethods []*BootstrapMethod
}
func (self *BootstrapMethodsAttribute) readInfo(reader *ClassReader) {
numBootstrapMethods := reader.readUint16()
self.bootstrapMethods = make([]*BootstrapMethod, numBootstrapMethods)
for i := range self.bootstrapMethods {
self.bootstrapMethods[i] = &BootstrapMethod{
bootstrapMethodRef: reader.readUint16(),
bootstrapArguments: reader.readUint16s(),
}
}
}
type BootstrapMethod struct {
bootstrapMethodRef uint16
bootstrapArguments []uint16
}
差不多就这样,这节主要是理解class解析整个过程,至于各种细节,异常表,局部变量表等,后面也会详细讲解。
gopackage main
import (
"fmt"
"jvm/classfile"
"jvm/classpath"
)
import "strings"
func main() {
cmd := parseCmd() // 解析命令行参数
if cmd.versionFlag {
fmt.Println("version 0.0.1") // 如果指定了版本标志,打印版本信息
} else if cmd.helpFlag || cmd.class == "" {
printUsage() // 如果指定了帮助标志或未指定类名,打印使用说明
} else {
startJVM(cmd) // 启动JVM
}
}
func startJVM(cmd *Cmd) {
cp := classpath.Parse(cmd.XjreOption, cmd.cpOption) // 解析类路径
className := strings.Replace(cmd.class, ".", "/", -1) // 将类名中的点替换为斜杠
cf := loadClass(className, cp) // 加载类文件
fmt.Println(cmd.class) // 打印类名
printClassInfo(cf) // 打印类信息
}
func loadClass(className string, cp *classpath.Classpath) *classfile.ClassFile {
classData, _, err := cp.ReadClass(className) // 从类路径中读取类数据
if err != nil {
panic(err) // 如果读取失败,抛出异常
}
cf, err := classfile.Parse(classData) // 解析类数据
if err != nil {
panic(err) // 如果解析失败,抛出异常
}
return cf // 返回解析后的类文件
}
func printClassInfo(cf *classfile.ClassFile) {
fmt.Printf("version: %v.%v\n", cf.MajorVersion(), cf.MinorVersion()) // 打印主版本号和次版本号
fmt.Printf("constants count: %v\n", len(cf.ConstantPool())) // 打印常量池数量
fmt.Printf("access flags: 0x%x\n", cf.AccessFlags()) // 打印访问标志
fmt.Printf("this class: %v\n", cf.ClassName()) // 打印当前类名
fmt.Printf("super class: %v\n", cf.SuperClassName()) // 打印父类名
fmt.Printf("interfaces: %v\n", cf.InterfaceNames()) // 打印接口名
fmt.Printf("fields count: %v\n", len(cf.Fields())) // 打印字段数量
for _, f := range cf.Fields() {
fmt.Printf(" %s\n", f.Name()) // 打印每个字段的名称
}
fmt.Printf("methods count: %v\n", len(cf.Methods())) // 打印方法数量
for _, m := range cf.Methods() {
fmt.Printf(" %s\n", m.Name()) // 打印每个方法的名称
}
}
本文作者:yowayimono
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!