编辑
2024-07-29
后端
00
请注意,本文编写于 285 天前,最后修改于 284 天前,其中某些信息可能已经过时。

上一章我们写了类加载部分,已经能够把类加载到内存中,现在可以操作类结构了,为了更好的操作类,我们还得把对应的字节流解析成结构化的数据(用结构体表示),这章讲的就是class解析

首先我们要先了解Java虚拟机规范8里面定义的class用c结构体能表示为一下

c
ClassFile { 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 类型描述
boolboolean布尔类型,值为 truefalse
intint有符号32位整数
int8byte有符号8位整数
int16short有符号16位整数
int32int有符号32位整数
int64long有符号64位整数
uint无直接对应无符号32位整数(取决于平台,通常是32位或64位)
uint8byte无符号8位整数
uint16char无符号16位整数(Java的char是16位无符号整数)
uint32无直接对应无符号32位整数
uint64无直接对应无符号64位整数
float32float32位浮点数
float64double64位浮点数
stringString字符串类型 符号整数

接下来就是定义类文件的结构体了,按照上面的就行

go
type 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进制打开

image.png

安装好重启IDEA

image.png

右键class文件即可查看

image.png

可以看到开头的魔数

在真正解析class文件前,我们还需要一个ClassReader对象,来读取class文件,可以按特定的字节读取,下面是它的定义

go
package 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方法

go
func 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虚拟机规范是怎么定义字段和方法的?

先看看字段是怎么定义的

go
field_info { u2 access_flags; // 字段的访问标志,表示字段的访问权限和属性,如public、private、static等。 u2 name_index; // 指向常量池的一个索引,表示字段的名称。 u2 descriptor_index; // 指向常量池的一个索引,表示字段的描述符。 u2 attributes_count; // 字段属性的数量。 attribute_info attributes[attributes_count]; // 字段属性表,包含了字段的额外信息,如常量值等。 }

方法的定义和字段差不多

go
method_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:表示方法参数的信息。

字段独有的属性

  • ConstantValue:表示字段的常量值。

以下是通用的属性

  • Deprecated:表示类、字段或方法已被弃用。

  • Synthetic:表示类、字段或方法是由编译器生成的,不是源代码中显式定义的。

  • Signature:表示泛型类型的签名信息。

  • RuntimeVisibleAnnotations:表示运行时可见的注解。

  • RuntimeInvisibleAnnotations:表示运行时不可见的注解。

  • RuntimeVisibleParameterAnnotations:表示方法参数的运行时可见注解。

  • RuntimeInvisibleParameterAnnotations:表示方法参数的运行时不可见注解。

  • RuntimeVisibleTypeAnnotations:表示运行时可见的类型注解。

  • RuntimeInvisibleTypeAnnotations:表示运行时不可见的类型注解。

是不是有种豁然开朗的感觉?接下来我们再看看怎么读取字段和方法

我们看一下代码,前面说过他们的结构一模一样,所以直接写一份代码

go
type 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虚拟机规范定义了常量项的基本结构

go
cp_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虚拟机规范

go
CONSTANT_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

go
package 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不合法,抛出异常 } }

其他的所有常量结构都是实现这个接口

接下来定义常量表

go
package 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

go
package 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编码的字符数组转换为字符串并返回 }

下面懒的注释,都是差不多的

go
package 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虚拟机规范预定义属性其中八种,看一下属性怎么定义的

go
attribute_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解析整个过程,至于各种细节,异常表,局部变量表等,后面也会详细讲解。

go
package 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()) // 打印每个方法的名称 } }

image.png

本文作者:yowayimono

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!