自己动手写JVM六【解析class文件(四)】

2017-01-12 10:06:10来源:作者:bboyjing's blog人点击

常量池占据了class文件很大一部分数据,里面存储着各式各样的常量信息,包括数字和字符串常量、类和接口名,字段和方法等等,下面我们就来详细了解常量池和各种常量。

ConstantPool结构体

constant_pool.go文件中定义了ConstantPool类型,常量池实际上也是一个表,所以ConstantPool被定义成ConstantInfo的数组。所以,要理解常量池,得先去看ConstantInfo是什么,然后再回过来,此处未完待续,接着往下看。

ConstantInfo

由于常量池中存放的信息各不相同,所以每种常量的格式不同。常量数据的第一个字节时tag,用来区分常量的类型,下面是Java虚拟机规范给出的常量结构:

cp_info { u1 tag; u1 info[];}

Java虚拟机一共定义了14中常量,虽然篇幅比较长,但为了看的更清楚,还是列举下吧:

类型 标志 描述 CONSTANT_Utf8_info 1 UTF-8编码的字符串 CONSTANT_Integer_info 3 整形字面量 CONSTANT_Float_info 4 浮点型字面量 CONSTANT_Long_info 5 长整型字面量 CONSTANT_Double_info 6 双精度浮点型字面量 CONSTANT_Class_info 7 类或接口的符号引用 CONSTANT_String_info 8 字符串类型字面量 CONSTANT_Fieldref_info 9 字段的符号引用 CONSTANT_Methodref_info 10 类中方法的符号引用 CONSTANT_InterfaceMethodref_info 11 接口中方法的符号引用 CONSTANT_NameAndType_info 12 字段或方法的符号引用 CONSTANT_MethodHandle_info 15 表示方法句柄 CONSTANT_MothodType_info 16 标志方法类型 CONSTANT_InvokeDynamic_info 18 表示一个动态方法调用点

ConstantInfo被定义成一个接口,位于constant_info.go文件中,至于为什么ConstantInfo是个接口,看看上面这14种类型就明白了。下面在constant_info.go文件中定义对应tag的常量值,顺便把ConstantInfo接口也贴出来:

const (CONSTANT_Utf8 = 1CONSTANT_Integer = 3CONSTANT_Float = 4CONSTANT_Long = 5CONSTANT_Double = 6CONSTANT_Class = 7CONSTANT_String = 8CONSTANT_Fieldref = 9CONSTANT_Methodref = 10CONSTANT_InterfaceMethodref = 11CONSTANT_NameAndType = 12CONSTANT_MethodHandle = 15CONSTANT_MethodType = 16CONSTANT_InvokeDynamic = 18)type ConstantInfo interface {// 读取常量信息,由具体的常量结构体实现readInfo(reader *ClassReader)}

继续编辑constant_info.go,添加读取常量信息入口:

funcreadConstantInfo(reader *ClassReader, cp ConstantPool) {}

由于读取常量信息由具体的常量结构体实现,目前还没有任何实现,所以没办法继续下去了,下面要做的就是去理解并实现14种常量结构。

CONSTANT_Integer_info

CONSTANT_Integer_info使用4个字节存储整数常量,tag占一个字节,一共5个字节。其结构定义如下:

CONSTANT_Integer_info { u1 tag; u4 bytes;}

CONSTANT_Integer_info和后面将要介绍的三种数字常量无论结构还是实现都非常相似,所以把它们都定义在cp_numeric.go文件中,ConstantIntegerInfo相关代码如下:

// 读取CONSTANT_Integer_infofunc(self *ConstantIntegerInfo)readInfo(reader *ClassReader) {// 读取4个字节bytes := reader.readUint32()// 将bytes转为int32self.val = int32(bytes)}// 获取int32常量值func(self *ConstantIntegerInfo)Value()int32 {return self.val}

CONSTANT_Integer_info正好可以容纳一个Java的int型常量,但实际上比int更小的boolean、byte、short和char类型的常量也放在CONSTANT_Integer_info。ClassFile.class的int常量字节码如下:

由上图可以看出,tag为0x03,表示CONSTANT_Integer_info。bytes为0x075BCD15,转换成十进制正好是123456789。

CONSTANT_Float_info

CONSTANT_Float_info使用4字节存储IEEE754单精度浮点数常量,tag占一个字节,一共5个字节。其结构和ConstantIntegerInfo非常类似,这里就不给出代码了,在cp_numeric.go文件中可以查看。

CONSTANT_Long_info

CONSTANT_Long_info使用8个字节存储整数常量,tag占一个字节,一共9个字节。其结构和ConstantIntegerInfo非常类似,这里就不给出代码了,在cp_numeric.go文件中可以查看。

CONSTANT_Double_info

CONSTANT_Double_info使用8个字节存储IEEE754双精度浮点数,tag占一个字节,一共9个字节。其结构和ConstantIntegerInfo非常类似,这里就不给出代码了,在cp_numeric.go文件中可以查看。

CONSTANT_Utf8_info

CONSTANT_Utf8_info常量里放的是MUTF-8编码的字符串,结构如下:

CONSTANT_Utf8_info { u1 tag; u2 length; u1 bytes[length];}

要注意的是,字符串在class文件中是以MUTF-8(Modified UTF-8)方式编码的,与正常的UTF-8是有点区别的,具体这里就不讨论了。ConstantUtf8Info位于cp_utf8.go中:

type ConstantUtf8Info struct {str string}// 读取CONSTANT_Utf8_infofunc(self *ConstantUtf8Info)readInfo(reader *ClassReader) {// 读取length(2个字节),并转换成uint32length := uint32(reader.readUint16())// 读取指定长度的字节bytes := reader.readBytes(length)// 将字节转换成MUTF-8self.str = decodeMUTF8(bytes)}// 获取string常量值func(self *ConstantUtf8Info)Str()string {return self.str}

上面代码中decodeMUTF8方法篇幅比较长,是书的作者参照java.io.DataInputStream.readUTF(DataInput)方法写的,这里就不贴出来了,文件中有。像字段名、字段描述符等就是以字符串的形式存储在class文件中的,比如字段PI对应的字节码如下图所示:

上图可以看出,tag为0x01,表示CONSTANT_Utf8_info。length为0x0002,表示bytes的长度为2,所以bytes的值为接下来的后2个字节0x5049,对应的ASCII正是PI。

下节继续。。。

最新文章

123

最新摄影

微信扫一扫

第七城市微信公众平台