一、字節碼指令(Bytecode instructions)
字節碼指令由標識該指令的操作碼和固定數量的參數組成:
- 操作碼(opcode)是一個無符號的字節值,因此是字節碼名稱,並由助記符標識。例如,操作碼值0由助記符NOP設計,並且對應於不執行任何操作的指令。
- 參數(arguments)是定義精確指令行爲的靜態值。它們在操作碼之後給出。例如,操作碼值爲167的GOTO標籤指令將自變量標籤指定爲要執行的下一條指令。
指令參數不能與指令操作數混淆:參數值是靜態已知的,並存儲在編譯的代碼中,而操作數值來自操作數堆棧,並且僅在運行時才知道。
字節碼指令可分爲兩類:
- 一小組指令旨在將值從局部變量傳輸到操作數堆棧,反之亦然;
- 其他指令僅作用於操作數堆棧:它們從堆棧中彈出一些值,根據這些值計算結果,然後將其推回堆棧。
ILOAD,LLOAD,FLOAD,DLOAD和ALOAD指令讀取局部變量並將其值壓入操作數堆棧。 他們將必須讀取的局部變量的索引i作爲參數。
ILOAD
用於加載布爾,字節,字符,短型或整型局部變量。
LLOAD
,FLOAD
和DLOAD
分別用於加載long,float或double值(LLOAD和DLOAD實際上加載了兩個插槽i和i + 1)。
最後,ALOAD
用於加載任何非原始值,即對象和數組引用。
對稱地,ISTORE,LSTORE,FSTORE,DSTORE和ASTORE
指令從操作數堆棧中彈出一個值
並將其存儲在由其索引i指定的局部變量中。
如您所見,鍵入了xLOAD和xSTORE指令(實際上,正如您將在下面看到的那樣,幾乎所有指令都已鍵入)。 這用於確保不進行任何非法轉換。 確實,將值存儲在局部變量中然後將其加載爲其他類型是非法的。
例如,ISTORE 1 ALOAD 1
序列是非法的–它將允許在本地變量1中存儲任意內存地址,並將該地址轉換爲對象引用! 但是,將一個值的類型與存儲在該局部變量中的當前值的類型不同的值存儲在局部變量中是完全合法的。
這意味着局部變量的類型,即存儲在該局部變量中的值的類型可以在方法執行期間改變。 如上所述,所有其他字節碼指令僅在操作數堆棧上工作。 它們可以分爲以下類別
1.1 約定
- a和b表示int,float,long或double值(例如,對於IADD,它們表示int,對於LADD,則表示long);
- o和p表示對象引用,
- v表示任何值(對於堆棧指令,其大小爲1),
- w代表long或double,i,j和n代表int值。
1.2 局部變量(Local variables)
1.3 堆(Stack)
這些指令用於操作堆棧上的值:POP
將值彈出到堆棧頂部,DUP
推入頂部堆棧值的一個副本,SWAP
彈出兩個值並以相反的順序將其推入,依此類推。
1.4 常數(Constants)
這些指令將一個常數值壓入操作數堆棧:
-
ACONST_NULL推送null,
-
ICONST_0推送int值0,
-
FCONST_0壓入0f,
-
DCONST_0壓入0d,
-
BIPUSH b壓入字節值b,
-
SIPUSH s推短值s,
-
LDC cst推送任意int,float,long,double,String或class(這對應於identifier.class Java語法。)常量cst等。
1.5 算術與邏輯(Arithmetic and logic)
這些指令從操作數堆棧中彈出數值,將它們組合起來並將結果壓入堆棧。
他們沒有任何參數。
xADD,xSUB,xMUL,xDIV和xREM對應於+,-,*,/和%運算,
其中x爲I,L,F或D。
類似地,還有其他與<<,>>, >>>,|,&和^,用於int和long值
1.5 轉換 (Casts)
這些指令從堆棧中彈出一個值,將其轉換爲另一個類型,然後將結果推回去。它們與Java中的轉換表達式相對應。
- I2F,F2D,L2D等從一種數值類型轉換數值到另一個。
- CHECKCAST t將參考值轉換爲類型t。
1.6 對象(Objects)
這些指令用於創建對象,鎖定它們,測試其類型等。例如,NEW類型指令將類型爲type的新對象壓入堆棧(其中type是內部名稱)。
1.7 字段 (Fields )
這些指令讀取或寫入字段的值。
- GETFIELD所有者 name desc會彈出一個對象引用,並推送其name字段的值。
- PUTFIELD所有者名稱desc會彈出一個值和一個對象引用,並將此值存儲在其name字段中。
在這兩種情況下,對象都必須是所有者類型,並且其字段必須是desc類型。 GETSTATIC和PUTSTATIC是相似的指令,但用於靜態字段。
圖見上圖。
1.8 方法(Methods )
方法這些指令調用方法或構造函數。它們彈出與方法參數一樣多的值,再加上目標對象的一個值,然後推送方法調用的結果。
-
INVOKEVIRTUAL所有者名稱desc調用在類所有者中定義的name方法,其方法描述符爲desc。
-
INVOKESTATIC用於靜態方法,
-
對於私有方法和構造函數的INVOKESPECIAL,
-
和INVOKEINTERFACE用於接口中定義的方法。
-
最後,對於Java 7類,INVOKEDYNAMIC用於新的動態方法調用機制。
圖見上圖。
1.8 數組(Arrays)
數組這些指令用於讀取和寫入數組中的值。
-
xALOAD指令彈出一個索引和一個數組,然後將數組元素的值壓入該索引。
-
xASTORE指令彈出一個值,一個索引和一個數組,並將該值存儲在數組中的該索引處。
-
x可以是I,L,F,D或A,也可以是B,C或S。
1.9 Jump
如果某些條件,這些指令會跳轉到任意指令是真實的,還是無條件的。
它們用於編譯是否if,for,do,while,break指令。
例如,IFEQ標籤從堆棧中彈出一個int值,如果該值爲0,則跳至label設計的指令(否則,執行將繼續執行下一條指令)。
存在許多其他跳轉指令,例如IFNE或IFGE。
最後,TABLESWITCH和LOOKUPSWITCH對應於switch Java指令。
1.10 Return
最後,xRETURN
和RETURN
指令用於終止方法的執行並將其結果返回給調用方。
RETURN用於返回void的方法,xRETURN用於其他方法。