【总目录/源代码】:https://blog.youkuaiyun.com/qq_40636117/article/details/94383044
【上一篇】:https://blog.youkuaiyun.com/qq_40636117/article/details/94415960
【下一篇】:https://blog.youkuaiyun.com/qq_40636117/article/details/95618443
【初遇BUG】
在开发属性池解析功能的时候遇到了开发中的第一个大BUG,那就是一些类的常量池解析出错,连带着属性池一起出现了问题(因为常量池后面就是类的访问标志,只会出现特定的数字,所以属性池解析是否异常可以看出来)。经过测试解析不同的类,发现这个错误还多种多样:
(1)有的class文件常量池和属性池解析正常。
(2)有的常量池能解析完成,但是属性池解析出错
(3)有的连常量池解析都无法正常完成,tag的值也是奇奇怪怪,甚至出现了三位数!
起初我以为是版本原因,可能某些版本解析不一样,后来对比了版本号排除了这个问题(幸好不是这个原因)
后来怀疑是某些class文件的原因,但用go版本的jvm却正常无误地解析出来。
最后没辙了,魔改作者提供的go版本虚拟机,把它解析常量池的全过程打印出来,和我的进行对比,发现居然是
。。。
long和double类型在常量池里面占两位
啊啊啊,坑死爹了,占用晚上中午和下午休息的时间,前后debug将近5,6个小时,居然是就是这么个错误!太过分了,一定要记下来!
【正式开发】
debug完后,后面的事情就相对简单了。
一、类信息(类的访问字段,本类名称,本类父类)
(1)类的访问字段(修饰符):紧跟着常量池后面的是类的访问字段,或者说修饰符(4p权限,final,interface等等)。这个访问字段用16位的数据存放起来。16位转化成16进制正好是0x0000~0xFFFF,四位数。这每一位数字都有其对应含义,或者说对应了一个修饰符。我现在还没用到,所以暂时先存放起来。
十分感谢这个修饰符,要不是它,我还发现不了解析出现了错误。
(2)本类索引:访问字段后面跟着的16位数据就是本类在常量池的索引了,没什么好说的
(3)父类索引:跟在本类索引后面,也是16位数据存储。
二、接口表
类除了继承一个父类外,还能实现多个接口,这些信息存放在接口表中。
和常量池一个套路,跟在父类索引后面的是接口表的大小,16位数据存储;
接口表大小后面就是若干16位数据,不用说,代表的就是这些接口在常量池里面的索引。
吐槽一下,这些接口在常量池中的类型居然是class,居然是class,居然是class,吓得我又以为解析错误了,还特地打开java源码进行求证
三、字段表,方法表
接口表后面跟着的是字段表,字段表完结后就是方法表。
之所以把这两个表放在一起讲,是因为这两个表的结构是一样的!不然我也没法开发这么快
u1:无符号1字节数据 u2:无符号2字节数据 u4:无符号4字节数据
//属性
typedef struct AttrInfo{
u2 nameIndex; //属性名称索引
u4 len; //属性内容数组大小
u1* infos; //属性内容数组
}AttrInfo;
字段/方法结构
//u1:无符号8位数据 u2:无符号16位数据
typedef struct MemberInfo{
u2 accessFlag; //访问符
u2 nameIndex; //名称索引
u2 desIndex; //描述索引
u2 attrCount; //属性数组大小
AttrInfo* attrInfos; //属性数组(其中方法里面这里存放着方法体)
}MemberInfo;
一样的。显示这些表的大小,然后是若干个字段/方法。
四、其他内容
解析完方法表后,我以为解析完成了,于是输出剩下数据验证一下。没想到居然还剩下一点数据。
不过好在字段表和方法表解析正常。查阅了书本也不知道这些数据是什么意思。
虽然这些数据长短不定,不过好在并不算太多,算留下一个小尾巴,以后再说吧
五、结语
哎,肝了怎么就终于把.class文件(基本上)解析完成了,打印一下类信息爽一下!
Object.class
String.class
Double.class