反射
本节继续将反射相关内容
修改类加载器
Class和Object结构体准备好了,接下来修改类加载器,让每一个加载到方法区中的类都有一个类对象与之关联。修改/rtdata/heap/class_loader.go文件的NewClassLoader()方法:
|
|
下面再修改LoadClass()方法:
这样,在loadBasicClasses()和LoadClass()的配合下,所有加载到方法区的类都设置好了jClass字段,而且每一个类关联的类对象都是单独的。
基本类型的类
void和基本类型也有对应的类对象,但只能通过字面值来访问:
和数组类一样,基本类型的类也是由Java虚拟机在运行期间生成的。继续编辑class_loader.go文件,修改NewClassLoader()方法:
|
|
这里有三点需要说明:
- void和基本类型的类名就是void、int、float等。
- 基本类型的类没有超类,也没有实现任何接口。
- 非基本类型的类对象时通过ldc指令加载到操作数栈中的,而基本类型的类对象虽然在Java代码中看起来是通过字面量获取的,但是编译之后的指令并不是ldc,而是getstatic。每个基本类型都有一个包装类,包装类中有一个静态常量,就做TYPE,其中存放的就是基本类型的类。也就是说,基本类型的类是通过getstatic指令访问相应包装类的TYPE字段加载到操作数栈中。 12//java.lang.Integer类中的TYPE常量public static final Class<Integer> TYPE = (Class<Integer>) Class.getPrimitiveClass("int");
修改ldc指令
和基本类型、字符串字面值一样,类对象字面值也是由ldc指令加载的,修改/instructions/constants/ldc.go文件的_ldc()方法:
通过反射获取类名
为了支持通过反射获取类名,下面要实现以下4个本地方法:
- java.lang.Object.getClass()
- java.lang.Class.getPrimitiveClass()
- java.lang.Class.getName0()
- java.lang.Class.desiredAssertionStatus0()
新建/native/lang/Object.go文件,在其中注册getClass()本地方法:
GetThis()方法在/rtdata/local_vars.go文件中,代码比较简单,就不贴出来了,参照项目源码。
在/native/java/lang包下新建Class.go文件,在其中注册三个本地方法,代码就不贴出来了,参照项目源码。
到此,4个本地方法都实现好了,而且也在init()函数中注册,但还init()函数还没有机会执行,需要在invokenative.go文件中导入lang包,这属于Go语言范畴,这里就不讨论了。
测试本节代码
在java项目中新建GetClassTest.java文件,用作测试:
还差一步,由于/rtdata/heap/object.go文件添加了extra属性,所以在array_class.go和string_pool.go文件中初始化Object结构体的时候要多加个参数,例如&Object{loader.LoadClass("[C"), chars, nil}
,最后给extra赋值nil就行了。
测试: