一崎苗、前言
幾年前冯凹,接到一個(gè)開(kāi)發(fā)任務(wù):用Java開(kāi)發(fā)能運(yùn)行Java智能合約的虛擬機(jī)。在開(kāi)發(fā)Java智能合約時(shí)荞怒,只能使用智能合約SDK提供的類和一些Java常用類(8種基本數(shù)據(jù)類型包裝類耻讽;String察纯、BigInteger、BigDecimal针肥、List饼记、Map、Set 相關(guān)的類)慰枕。
完整的Java智能合約虛擬機(jī)比較復(fù)雜具则,且要保存Java智能合約狀態(tài)。 這篇文章僅介紹一個(gè)簡(jiǎn)單JVM實(shí)現(xiàn)具帮,支持少量字節(jié)碼博肋。 參考 Java 虛擬機(jī)規(guī)范(Java SE 8),里面寫到:要正確實(shí)現(xiàn) Java 虛擬機(jī)蜂厅,只需能夠讀取class文件格式并正確執(zhí)行其中指定的操作束昵。為了簡(jiǎn)化實(shí)現(xiàn),使用了 ASM解析class文件 葛峻。
二、使用ASM解析class文件
使用 ASM Tree API 解析class文件巴比,獲得一個(gè) ClassNode 對(duì)象术奖,里面包含 class 文件的各種信息。要運(yùn)行class定義的方法轻绞,先在ClassNode中找到這個(gè)方法(MethodNode包含方法的各種信息)采记,然后執(zhí)行方法的指令集。MethodNode.instructions 是這個(gè)方法的指令集政勃,遍歷指令集唧龄,執(zhí)行每個(gè)指令,只要正確執(zhí)行了指令奸远,方法就能完成運(yùn)行既棺。
三、實(shí)現(xiàn)JVM運(yùn)行時(shí)數(shù)據(jù)區(qū)
要正確執(zhí)行指令懒叛,先要了解JVM結(jié)構(gòu)丸冕,參考 Java虛擬機(jī)規(guī)范第二章(JVM結(jié)構(gòu)),里面介紹了JVM運(yùn)行時(shí)數(shù)據(jù)區(qū)薛窥,定義了在程序執(zhí)行期間使用的各種運(yùn)行時(shí)數(shù)據(jù)區(qū)胖烛,如圖所示:
下面簡(jiǎn)要介紹各種運(yùn)行時(shí)數(shù)據(jù)區(qū)眼姐,詳細(xì)信息查看 Java虛擬機(jī)規(guī)范第二章(JVM結(jié)構(gòu)),根據(jù)這些信息佩番,可以簡(jiǎn)單實(shí)現(xiàn)各種運(yùn)行時(shí)數(shù)據(jù)區(qū)众旗。
3.1 程序計(jì)數(shù)器
Java 虛擬機(jī)可以同時(shí)支持多個(gè)執(zhí)行線程。每個(gè)線程都有自己的程序計(jì)數(shù)器趟畏。程序計(jì)數(shù)器包含當(dāng)前線程正在執(zhí)行的 Java 虛擬機(jī)指令的地址贡歧。
程序計(jì)數(shù)器最主要作用就是包含當(dāng)前指令,程序計(jì)數(shù)器簡(jiǎn)單實(shí)現(xiàn)如下:
3.2 虛擬機(jī)棧
每個(gè) Java 虛擬機(jī)線程都有一個(gè)Java 虛擬機(jī)棧拱镐。虛擬機(jī)棧存儲(chǔ)棧幀艘款,推入和彈出棧幀。
虛擬機(jī)棧就是一個(gè)保存棧幀的棧沃琅,實(shí)現(xiàn)如下:
3.2.1 棧幀
每次調(diào)用方法時(shí)都會(huì)創(chuàng)建一個(gè)棧幀哗咆,每個(gè)棧幀都有自己的局部變量數(shù)組和操作數(shù)棧,局部變量數(shù)組和操作數(shù)棧的大小在編譯時(shí)確定益眉。
新棧幀推入虛擬機(jī)棧晌柬,虛擬機(jī)棧的棧頂棧幀是當(dāng)前正在執(zhí)行的活動(dòng)棧幀,稱為當(dāng)前棧幀郭脂,其方法稱為當(dāng)前方法年碘,定義當(dāng)前方法的類是當(dāng)前類。 在方法返回時(shí)展鸡,當(dāng)前棧幀將其方法調(diào)用的結(jié)果(如果有)傳遞回前一棧幀屿衅。 虛擬機(jī)棧彈出當(dāng)前棧幀,前一棧幀成為當(dāng)前棧幀莹弊。 棧幀包含局部變量和操作數(shù)棧涤久,實(shí)現(xiàn)如下:
3.2.2 局部變量
每個(gè)棧幀都包含一個(gè)稱為局部變量的變量數(shù)組。Java 虛擬機(jī)使用局部變量在方法調(diào)用時(shí)傳遞參數(shù)忍弛。在類方法調(diào)用中响迂,參數(shù)從局部變量0開(kāi)始。在實(shí)例方法調(diào)用中细疚,局部變量0用于傳遞對(duì)象的引用蔗彤,隨后從局部變量1開(kāi)始傳遞任何參數(shù)。
局部變量用來(lái)傳遞方法參數(shù),實(shí)現(xiàn)如下:
3.2.3 操作數(shù)棧
每個(gè)棧幀都包含一個(gè)操作數(shù)棧。創(chuàng)建棧幀時(shí)扒腕,操作數(shù)棧為空。Java 虛擬機(jī)提供將常量啦鸣、值從局部變量、字段加載到操作數(shù)棧的指令来氧。其他 Java 虛擬機(jī)指令從操作數(shù)棧中獲取操作數(shù)诫给,對(duì)其進(jìn)行操作香拉,并將結(jié)果推回到操作數(shù)棧上。操作數(shù)棧還用于準(zhǔn)備要傳遞給方法的參數(shù)和接收方法結(jié)果中狂。
操作數(shù)棧就是保存操作數(shù)的棧凫碌,實(shí)現(xiàn)如下:
3.3 虛擬機(jī)棧 堆
Java 虛擬機(jī)有一個(gè)在所有 Java 虛擬機(jī)線程之間共享的堆。堆是運(yùn)行時(shí)數(shù)據(jù)區(qū)胃榕,從中分配所有類實(shí)例和數(shù)組的內(nèi)存盛险。對(duì)象的堆存儲(chǔ)由垃圾收集器回收,對(duì)象永遠(yuǎn)不會(huì)被顯式釋放勋又。
簡(jiǎn)單實(shí)現(xiàn)堆苦掘,不用考慮垃圾回收,就是用來(lái)保存對(duì)象實(shí)例楔壤,實(shí)現(xiàn)如下:
3.3.1 局部變量
對(duì)象實(shí)例是通過(guò)引用關(guān)聯(lián)的鹤啡,引用實(shí)現(xiàn)如下:
3.4 方法區(qū)
Java 虛擬機(jī)有一個(gè)在所有 Java 虛擬機(jī)線程之間共享的方法區(qū)。它存儲(chǔ)每個(gè)類的結(jié)構(gòu)蹲嚣,例如運(yùn)行時(shí)常量池递瑰、字段和方法數(shù)據(jù),以及方法和構(gòu)造函數(shù)的代碼隙畜,包括在類和實(shí)例初始化和接口初始化中使用的特殊方法抖部。
方法區(qū)用來(lái)保存類結(jié)構(gòu),實(shí)現(xiàn)如下:
3.5 本地方法棧
本地方法棧是為 Java 虛擬機(jī)運(yùn)行 native 方法服務(wù)的议惰,由于很多 native 方法都是用 C 語(yǔ)言實(shí)現(xiàn)的慎颗,所以它通常又叫 C 棧。本地方法棧與虛擬機(jī)棧所發(fā)揮的作用非常相似言询,甚至有的虛擬機(jī)直接把本地方法棧和虛擬機(jī)棧合二為一哗总。
四、實(shí)現(xiàn)JVM
前面對(duì)JVM運(yùn)行時(shí)數(shù)據(jù)區(qū)做了簡(jiǎn)單實(shí)現(xiàn)倍试,現(xiàn)在需要把這些運(yùn)行時(shí)數(shù)據(jù)區(qū)關(guān)聯(lián)起來(lái),實(shí)現(xiàn)一個(gè)簡(jiǎn)單JVM蛋哭,這個(gè)JVM不考慮線程县习,直接在Jvm類中關(guān)聯(lián)運(yùn)行時(shí)數(shù)據(jù)區(qū)。
簡(jiǎn)單JVM實(shí)現(xiàn)如下:
這是一個(gè)簡(jiǎn)單JVM谆趾,不會(huì)實(shí)現(xiàn)所有字節(jié)碼躁愿,寫一個(gè)示例合約,運(yùn)行這個(gè)合約時(shí)沪蓬,用到了哪些字節(jié)碼彤钟,就實(shí)現(xiàn)哪些字節(jié)碼。
五跷叉、示例合約
寫一個(gè)示例合約:
寫一個(gè)智能合約SDK提供的工具類逸雹,里面有一個(gè)native方法营搅,需要JVM去調(diào)用真正的實(shí)現(xiàn)。
native方法實(shí)現(xiàn)
運(yùn)行示例合約
六梆砸、實(shí)現(xiàn)字節(jié)碼
運(yùn)行示例合約转质,可以看到需要實(shí)現(xiàn)哪些字節(jié)碼,把這些字節(jié)碼都實(shí)現(xiàn)帖世。多次運(yùn)行休蟹、實(shí)現(xiàn)后,相關(guān)字節(jié)碼都實(shí)現(xiàn)了日矫。再次運(yùn)行赂弓,就可以看到控制臺(tái)輸出:1024。
實(shí)現(xiàn)參考 Java虛擬機(jī)規(guī)范第六章(JVM指令集)哪轿,下面是其中一個(gè)字節(jié)碼的實(shí)現(xiàn)盈魁,更多字節(jié)碼實(shí)現(xiàn),可以看完整代碼缔逛。
七备埃、結(jié)束
這是一個(gè)簡(jiǎn)單的JVM,很多實(shí)現(xiàn)都很簡(jiǎn)化褐奴“唇牛可以不斷豐富示例代碼,然后實(shí)現(xiàn)更多字節(jié)碼敦冬,在實(shí)現(xiàn)字節(jié)碼過(guò)程中辅搬,就會(huì)發(fā)現(xiàn)以前實(shí)現(xiàn)的問(wèn)題,多次迭代后脖旱,JVM就會(huì)更完善堪遂。
下載完整代碼:
https://gitee.com/xdehuan/contract-jvm
希望以上內(nèi)容能對(duì)有需要的人有所幫助