字節碼是ASM的基礎,要想熟練的使用ASM,那麼瞭解字節碼就是必備基礎。java
Class文件做爲Java虛擬機所執行的直接文件,內部結構設計有着固定的協議,每個Class文件只對應一個類或接口的定義信息。數組
每一個Class文件都以8位爲單位的字節流組成,下面是一個Class文件中所包括的內容,在Class文件中,各項內容按照嚴格順序連續存放,Java虛擬機只要按照協議順序來讀取便可。markdown
ClassFile {
u4 magic;
u2 minor_version;
u2 major_version;
u2 constant_pool_count;
cp_info constant_pool[constant_pool_count-1];
u2 access_flags;
u2 this_class;
u2 super_class;
u2 interfaces_count;
u2 interfaces[interfaces_count];
u2 fields_count;
field_info fields[fields_count];
u2 methods_count;
method_info methods[methods_count];
u2 attributes_count;
attribute_info attributes[attributes_count];
}
複製代碼
在Class文件結構中,上面各項的含義以下。網絡
Name | 含義 |
---|---|
magic | 做爲一個魔數,肯定這個文件是不是一個能被虛擬機接受的class文件,值固定爲0xCAFEBABE。 |
minor_version,major_version | 分別表示class文件的副,主版本號,不一樣版本的虛擬機實現支持的Class文件版本號不一樣。 |
constant_pool_count | 常量池計數器,constant_pool_count的值等於常量池表中的成員數加1。 |
constant_pool | 常量池,constant_pool是一種表結構,包含class文件結構及其子結構中引用的全部字符常量、類或接口名、字段名和其餘常量。 |
access_flags | access_flags是一種訪問標誌,表示這個類或者接口的訪問權限及屬性,包括有ACC_PUBLIC,ACC_FINAL,ACC_SUPER等等。 |
this_class | 類索引,指向常量池表中項的一個索引。 |
super_class | 父類索引,這個值必須爲0或者是對常量池中項的一個有效索引值,若是爲0,表示這個class只能是Object類,只有它是惟一沒有父類的類。 |
interfaces_count | 接口計算器,表示當前類或者接口的直接父接口數量。 |
interfaces[] | 接口表,裏面的每一個成員的值必須是一個對常量池表中項的一個有效索引值。 |
fields_count | 字段計算器,表示當前class文件中fields表的成員個數,每一個成員都是一個field_info。 |
fields | 字段表,每一個成員都是一個完整的fields_info結構,表示當前類或接口中某個字段的完整描述,不包括父類或父接口的部分。 |
methods_count | 方法計數器,表示當前class文件methos表的成員個數。 |
methods | 方法表,每一個成員都是一個完整的method_info結構,能夠表示類或接口中定義的全部方法,包括實例方法,類方法,以及類或接口初始化方法。 |
attributes_count | 屬性表,其中是每個attribute_info,包含如下這些屬性,InnerClasses,EnclosingMethod,Synthetic,Signature,Annonation等。 |
以上內容來自網絡,我也不知道從哪copy來的。函數
字節碼和Java代碼仍是有很大區別的。oop
字節碼在Java虛擬機中是以堆棧的方式進行運算的,相似CPU中的寄存器,在Java虛擬機中,它使用堆棧來完成運算,例如實現「a+b」的加法操做,在Java虛擬機中,首先會將「a」push到堆棧中,而後再將「b」push到堆棧中,最後執行「ADD」指令,取出用於計算的兩個變量,完成計算後,將返回值「a+b」push到堆棧中,完成指令。網站
咱們在Java代碼中的類型,在字節碼中,有相應的表示協議。this
Java Type | Type description |
---|---|
boolean | Z |
char | C |
byte | B |
short | S |
int | I |
float | F |
long | J |
double | D |
object | Ljava/lang/Object; |
int[] | [I |
Object[][] | [[Ljava/lang/Object; |
void | V |
引用類型 | L |
藉助上面的協議分析,想要看到字節碼中參數的類型,就比較簡單了。spa
方法描述符(方法簽名)是一個類型描述符列表,它用一個字符串描述一個方法的參數類型和返回類型。設計
方法描述符以左括號開頭,而後是每一個形參的類型描述符,而後是是右括號,接下來是返回類型的類型描述符,例如,該方法返回void,則是V,要注意的是,方法描述符中不包含方法的名字或參數名。
Java方法聲明 | 方法描述符 | 說明 |
---|---|---|
void m(int i, float f) | (IF)V | 接收一個int和float型參數且無返回值 |
int m(Object o) | (Ljava/lang/Object;)I | 接收Object型參數返回int |
int[] m(int i, String s) | (ILjava/lang/String;)[I | 接受int和String返回一個int[] |
Object m(int[] i) | ([I)Ljava/lang/Object; | 接受一個int[]返回Object |
咱們來看下這段簡單的代碼,在字節碼下是怎樣的。
經過ASMPlugin,咱們看下生成的字節碼,以下所示。
能夠發現,這裏主要分紅了兩個部分——init和onCreate。
Java中的每個方法在執行的時候,Java虛擬機都會爲其分配一個「棧幀」,棧幀是用來存儲方法中計算所須要的全部數據的。
其中第0個元素就是「this」,若是方法有參數傳入會排在它的後面。
字節碼中有不少指令,下面對一些比較經常使用的指令進行下講解。
你們不用徹底掌握這些指令,結合代碼來看的話,仍是能看懂的,咱們須要的是修改字節碼,而不是從0開始。
對於Java源文件:若是隻有一個方法,編譯生成時,也會有兩個方法,其中一個是默認構造函數 對於Kotlin源文件:若是隻有一個方法,編譯生成時,會產生四個方法,一個是默認構造函數,還有兩個是kotlin合成的方法,以及退出時清除內存的默認函數
再結合ASM Code來看,仍是上面的例子。
默認的構造函數。
onCreate:
這裏面有些生成的代碼,例如:
Label label0 = new Label();
methodVisitor.visitLabel(label0);
methodVisitor.visitLineNumber(9, label0);
methodVisitor.visitLocalVariable("this", "Lcom/yw/asmtest/MainActivity;", null, label0, label4, 0);
複製代碼
這些都是調試代碼和寫入變量表的方法,咱們沒必要關心。
剩下的代碼,就是咱們能夠在ASM中所須要的代碼。
向你們推薦下個人網站 xuyisheng.top/ 專一 Android-Kotlin-Flutter 歡迎你們訪問