天天看点

ART世界探险(8) - 面向对象编程ART世界探险(8) - 面向对象编程

接口定义:

给接口做一个实现:

我们先做一个新建对象,并调用这个对象从接口。

java字节码:

0:首先new一个对象。

3:对象的引用值在栈里,dup一个,给下面调用类时用。

4:调用sampleclass的构造函数。虽然是生动生成的,但是new完了之后是一定要调的。

9:invokevirtual是调用虚方法。

dalvik代码:

dalvik代码还是要更好读一些:

new-instance结果存到v0中。

然后间接寻址,调用v0中的类的构造方法

接着v0继续用,调interfacemethod方法。

最后返回。

下面我们分析一下与面向对象相关的oat指令:

new-instance, invoke-direct和invoke-virtual都是两个参数的指令,用到两个虚拟寄存器:

v0:地址为sp+16

v1: 地址为sp+40

前面还是保存参数,检查suspend状态。

下面是调用pallocobject过程去创建新对象。w0中是类的类型。

返回值就是新对象的引用,将其存到sp+16(v0)。再重新读回来。

将对象引用存到sp+12,再读入到w1。

然后计算sampleclass类的构造方法的地址,然后跳转到该函数。

返回值存到sp+16(v0)。

w0再存到sp+12,再读到w1。

然后计算interfacemethod方法的地址,最后直接跳转到那个方法。

将进入时备份的lr恢复,并返回。

最后还是检查suspend的调用。

接口类:

接口的实现类:

正如以前我们看到的一样,这次再强调一下。如果没有默认的构造函数,java会自动给生成一个,然后会调用object类的构造函数。

sampleinterface就没生成什么代码。

oat代码:

前面还是例行备份和检查。

下面先load v0(sp+40)的值,然后清0初始化。

最后计算object的构造方法地址,并跳转过去。

然后返回。

这个空的interfacemethod函数可真是够空的,除了查一下suspend状态,可以打个断点啥的。其余真的啥也没干。

lr存到sp+24

w1存到sp+40

然后从sp+24中把lr读回来

返回

在看oat生成的代码的时候,我们经常看到lr和tr这样的别名。下面我们先扫清一下寄存器的障碍,学习一下art使用寄存器的土话。

复习一下,arm 64下的64位通用寄存器有33个:

x0~x30共31个

sp栈寄存器

xzr零寄存器,从它读就读出个0,向它写等于空操作

这33个64位寄存器也可以用低32位,当作32位寄存器来使用:

w0~w30

wsp

wzr

另外,还有32个64位的neon浮点运算寄存器:

64位双精度是d0~d31

32位单精度是s0~s31

这其中,有下列寄存器被art定义了自己含义:

fp是frame pointer,lr是link register。

在art代码中,这些寄存器还有别名:

在art代码中,用得最多的就是xself,举个例子,判断线程是不是被挂起来了,生成的代码是这样的:

上面的代码是不是看起来有点似曾相识啊?

下面是保存刚才我们介绍的特殊寄存器的代码:

下面是恢复用的:

这一章的最后,我们说说jni.

针对jni,没有什么java代码值得生成的。

我们看下反汇编的java代码,对应test1并没有java字节码生成。

但是oat还是会生成对应的查找jni函数的指令:

sp用掉了192的空间,先减掉。

然后备份x19~x30.d8~d15和tr(x18)的值。

下面将产生三次过程调用,分别是对应artquickgenericjnitrampoline,native函数本身和artquickgenericjniendtrampoline。

再调用了artquickgenericjniendtrampoline之后,再去检查是否有异常产生。

下面两句是处理异常的,#136是异常状态的地址,如果出现异常,就跳转到ret后面的异常处理部分去执行。

为什么异常状态的值是136呢,因为它在art代码中的定义是:thread_exception_offset。

在64位系统里,指针的size是8,所以这个offset的值为128+8,为136.

thread_top_quick_frame_offset为128+3*8 = 152。

thread_self_offset为128+72=200.

thread_card_table_offset = 128 + 147 * 8 = 1304.

把一开始减掉的192加回来,然后返回。

后面是异常处理的部分

这段代码用了大量的寄存器啊,那他们是干嘛用的呢?

不用担心,虽然没有文档,但是art代码中对此有说明:

如果有参数传进来,如果是整型的,最多7个参数,位于x1~x7中。如果是浮点数,就在d0~d7中。

返回地址在x30/lr中。

对照着下面的代码去看上面生成的test1调用jni的代码,是不是觉得有点能看懂了的意思了?

这些代码位于/art/runtime/arch/arm64/quick_entrypoints_arm64.s中。

通过看这里面的汇编过程的代码,再去对照编译出来的oat代码,为我们打开一条继续深入的路径。

继续阅读