本章节继续学习常量池相关信息
常量池
接上一章节
CONSTANT_Fieldref_info
CONSTANT_Fieldref_info表示字段符号引用,其结构体如下:
class_index和name_and_type_index都是常量池索引,分别指向CONSTANT_Class_info和CONSTANT_NameAndType_info常量。
另外,这里穿插一下CONSTANT_Methodref_info和CONSTANT_InterfaceMethodref_info。分别表示普通方法符号引用和接口方法符号引用。因为这三种的结构体一样,所以我们实现的时候使用一个统一的结构体。我们就选CONSTANT_Fieldref_info来学习下,其他两个后面就不赘述了。相关代码位于cp_member_ref.go文件中:
下面来看一个CONSTANT_Fieldref_info的class文件:
第一个字节tag为0x09,表示CONSTANT_Fieldref_info。后面两个字节是0x0032,转换成十进制为50,最后两个字节是0x0033,转换成十进制为51。
找出常量池第50个、51个:
先看下第50个,表示java.lang.System类,第51个正好是上一章节讲的CONSTANT_NameAndType_info例子,这两个组合起来就是System.out。
CONSTANT_Fieldref_info位于常量池的第2个,我们就顺便看下哪里指向了这个索引:
可以看出,是在方法表中的main()方法指向了常量池的第二个,这样也顺便验证了上一章的疑惑。
最后再完善下constant_pool.go和cp_member_ref.go,添加读取相关值的方法,首先编辑constant_pool.go文件:
接下来编辑cp_member_ref.go文件:
剩余常量结构
还有三种常量没有介绍CONSTANT_MothodType_info、CONSTANT_MethodHandle_info和CONSTANT_InvokeDynamic_info。它们是Java SE 7才添加到class文件中的,目的是支持新增的invokedynamic指令,暂不深究了,相关代码位于cp_invoke_dynamic.go文件中。
至此常量池中的常量结构大致介绍完毕,下面要回溯到第六章节没有讲完的两个地方。
未完的ConstantInfo
第一个地方是要完成ConstantInfo的读取,第六章由于常量结构体还买有讲,所以没办法读取,现在可以了,修改constant_info.go文件:
未完的ConstantPool结构体
第二个地方就是没讲完的ConstantPool结构体,在讲ConstantInfo结构体的过程中,对ConstantPool也已经大致了解了,也完善了cp_constant_poll.go大部分代码。常量池有三点需要特别注意:
- 表头给出的常量池大小比实际大1。假设表给出的值是n,那么常量池的实际大小时n-1。
- 有效的常量池索引是1 ~ n-1
- CONSTANT_Long_info和CONSTANT_Double_info各占两个位置。也就是说,如果常量池中存在这两种常量的话,实际的常量数量比n-1还要少,而且1 ~ n-1的某些数也会变成无效索引
完善cp_constant_poll.go中的读取ConstantPool方法:
常量池到这里总算可以告一段落了,下一章学习如何解析属性表。