本节继续来学习数组相关指令。
数组相关指令
<t>aload指令
<t>aload系列指令按索引取元素的值,然后推入操作数栈,其实现代码位于/instructions/loads/xaload.go文件中,其中一共有8条指令,代码基本相似,下面以aaload指令来详细看下:
|
|
<t>astore指令
<t>astore系列指令按索引给数组元素赋值,其实现代码位于/instructions/stores/xastore.go文件中,其中一共有8条指令,代码基本相似,下面以aastore指令来详细看下:
multianewarray指令
multianewarray指令创建多维数组,其实现代码位于/instructions/references/multianewarray.go文件中。multianewarray指令的第一个操作数是个uint16索引,通过这个索引可以从运行时常量池中找到一个类符号引用,解析这个引用就可以得到多维数组类。第二个操作数是个uint8整数,表示数组维度,这两个操作数在字节码中紧跟在指令操作码后面:
multianewarray指令还需要从操作数中弹出n个整数,分别代表每一个维度数组的长度,Execute()方法根据数组类、数组维度和各个维度的数组的长度创建多维数组,在继续实现该指令之前有两步要先完成:
修改/rtdata/heap/class_name_helper.go文件,添加将数组类名转换成类名的方法:
12345678910111213141516171819202122232425262728293031323334353637/*将数组类名转换成类名[[XXX -> [XXX[LXXX; -> XXX[I -> int*/func getComponentClassName(className string) string {if className[0] == '[' {componentTypeDescriptor := className[1:]return toClassName(componentTypeDescriptor)}panic("Not array: " + className)}// [XXX => [XXX// LXXX; => XXX// I => intfunc toClassName(descriptor string) string {// 若是数组类,直接返回if descriptor[0] == '[' {// arrayreturn descriptor}// 若是引用类型,去掉前缀Lif descriptor[0] == 'L' {// objectreturn descriptor[1 : len(descriptor)-1]}// 若是基本类型,通过基本类型描述符获取基本类型for className, d := range primitiveTypes {if d == descriptor {// primitivereturn className}}panic("Invalid descriptor: " + descriptor)}修改/rtdata/heap/array_class.go文件,添加获取数组元素类名,并加载类。
12345// 获取数组元素类名,并加载类func (self *Class) ComponentClass() *Class {componentClassName := getComponentClassName(self.name)return self.loader.LoadClass(componentClassName)}
下面继续为multianewarray指令添加Execute()方法:
至此,multianewarray指令算学完了,但是该指令比较难理解,我也没找到感觉,下面我们用个例子来试着分析看看。
在ArrayDemo.java中添加如下测试代码:
上面test()方法的字节码如下:
编译器先生成了三条iconst_n指令,然后又生成了一条multianewarray治理弄个,剩下的两条指令和数组创建无关。multianewarray指令的第一个操作数是0x0005,是个类引用,通过索引查看常量池可知,该数组类名为[[[I,第二个操作数是0x03,说明要创建的是int类型的三维数组,即int[][][]。
当方法执行时,三条iconst_n指令先后把整数3、4、5推入操作数栈。multianewarray指令在解码时就已经拿到了常量池索引(5)和数组维度(3)。在执行时,它先查找运行时常量池索引,知道要创建的是int[][][]数组,接着从操作数栈中弹出三个int值,依次是5、4、3。这样multianewarray指令就拿到了全部信息,从最外维开始创建数组实例即可。
到这里,新增的数组专用指令就学完了,现在修改下/instructions/factory.go文件,将新增的指令注释放开。
完善instanceof和checkcast指令
虽说是完善instanceof和checkcast指令,但实际上这两台哦指令的代码都没有任何改变,需要修改的时/heap/class_hierarchy.go文件中的isAssignableFrom()方法,而且改动很大,这部分就不解释了,我也没细看,参照项目源码吧。
测试数组
我们用经典的冒泡排序算法测试下之前写的数组相关功能,在java项目中新建BubbleSortTest.java,具体代码这里就不贴出来了,参照项目源码。下面就来测试下:
数组相关内容就到此结束,下一节来学习字符串的实现。