解釋器處理字節(jié)碼時(shí),與給定字節(jié)碼有關(guān)的動(dòng)作的語(yǔ)義、執(zhí)行字節(jié)碼的相關(guān)動(dòng)作大多是從堆棧中獲得其操作數(shù),并將其結(jié)果送回堆棧中。典型的情況下字節(jié)碼是帶有參數(shù)的,這些參數(shù)在字節(jié)碼流中緊跟在字節(jié)碼自身之后。
在虛擬機(jī)解釋字節(jié)碼過(guò)程中,執(zhí)行引擎會(huì)不時(shí)遇到請(qǐng)求本地方法調(diào)用的指令,虛擬機(jī)負(fù)責(zé)試著發(fā)起這個(gè)本地方法的調(diào)用。本地方法是Java虛擬機(jī)指令集的一種可編程擴(kuò)展,運(yùn)行這個(gè)本地方法就是Java虛擬機(jī)對(duì)這條指令的執(zhí)行。
本地方法函數(shù)調(diào)用
為了增加虛擬機(jī)的性能,加快其速度,解釋器在處理一些字節(jié)碼時(shí)調(diào)用的本地方法函數(shù)用匯編實(shí)現(xiàn)了將Java棧轉(zhuǎn)換為C棧,然后在C堆棧上實(shí)現(xiàn)函數(shù)的調(diào)用。Linux下是用獨(dú)立的匯編語(yǔ)言程序invokeNative_i386。S實(shí)現(xiàn)函數(shù)CVMjniInvokeNative(),我們采用在C里面嵌入?yún)R編的形式來(lái)實(shí)現(xiàn)該函數(shù)。
該函數(shù)的形參有7個(gè),完成的主要功能是將由實(shí)參傳遞來(lái)的部分?jǐn)?shù)據(jù)通過(guò)直接或者運(yùn)算后得到本地方法的參數(shù),然后壓入本地棧,通過(guò)匯編來(lái)實(shí)現(xiàn)本地的C函數(shù)調(diào)用。實(shí)參傳遞過(guò)來(lái)的7個(gè)數(shù)據(jù)包含JNI環(huán)境指針(env)、本地方法的函數(shù)指針(nativecode)、Java棧指針(args)、本地方法的描述符(tersesig),Java棧的參數(shù)總數(shù)(argssize)表示靜態(tài)或非靜態(tài)方法的類(lèi)對(duì)象標(biāo)志(classobject)及用于存儲(chǔ)返回值的一個(gè)指針變量(returnvalue),其中env要作為第一個(gè)本地方法的參數(shù)傳遞,并且nativecode也要傳遞到本地方法來(lái)實(shí)現(xiàn)本地方法的正確調(diào)用。
J2ME中的CDC移植
由于Linux有多個(gè)通用寄存器,在實(shí)現(xiàn)該函數(shù)的代碼中充分運(yùn)用了如esp、ebp、esi等寄存器,但是OS20提供的可操作的寄存器只有3個(gè)通用寄存器Areg、Breg、Creg和1個(gè)工作指針寄存器Wptr(相當(dāng)于堆棧指針),在實(shí)現(xiàn)過(guò)程中,我們用在C函數(shù)中設(shè)立局部變量來(lái)代替Linux的通用寄存器,通過(guò)手動(dòng)調(diào)整工作棧指針來(lái)實(shí)現(xiàn)本地方法的調(diào)用,具體實(shí)現(xiàn)過(guò)程如圖3所示。
當(dāng)進(jìn)入?yún)R編函數(shù)時(shí),工作區(qū)指針為Wptr,實(shí)參、狀態(tài)寄存器和指令指針寄存器的值全部自動(dòng)入棧,然后是我們定義的代替Linux寄存器的局部變量自動(dòng)入棧,此時(shí)的Wptr自動(dòng)移到Wptr′,利用OS20的匯編指令,手動(dòng)將實(shí)參傳遞過(guò)來(lái)的參數(shù)通過(guò)計(jì)算得到本地方法參數(shù)的個(gè)數(shù),然后將本地方法所需的參數(shù)依次壓棧,最后再手動(dòng)調(diào)節(jié)工作區(qū)指針實(shí)現(xiàn)本地方法的成功調(diào)用。這里我們先將本地方法函數(shù)指針和1個(gè)標(biāo)志位flag(0x10101010)入棧,原因有兩個(gè):