天天看點

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代碼,為我們打開一條繼續深入的路徑。

繼續閱讀