http://blog.csdn.net/larrylgq/article/details/7395261
java源碼級編譯器的任務是將符合java語言規範的源碼編譯為符合jvm規範的Class檔案,對不符合java語言規範的報錯
在sun的jdk中源碼編譯器是使用java寫的javac
javac的工作流程:
1 解析(parse)和輸入到符號表(enter)
2 註解處理(annotation processing)
3 分析與代碼的產生
解析:
詞法分析:使用com.sun.tools.javac.parser.Scanner類,將代碼字串變為Token序列
eg:
int y = 1;
->Token.INT(name:int)->Token.IDENTIFIER(name:y))+ Token.EQ(name:=))+ Token.INTLITERAL(stringVal:1))+ Token.SEMI(name:;))
文法分析:是一個遞迴下降且運算子優先順序的文法分析器,使用com.sun.tools.javac.parser.Parser類,根據文法由Token序列產生抽象文法樹
eg:
輸入到符號表:
使用com.sun.tools.javac.comp.Enter類,此過程每個編譯單元的抽象文法樹的頂局節點都先被放到待處理列表中並逐個處理列表中的節點
所有的類符號被輸入到外圍範圍的符號表中
確定類的參數(對泛型型別而言)、超類型和介面
如果需要添加預設構造器
將類中出現的符號輸入到類自身的符號表中
分析和檢查代碼中的annotation
註解處理:annotation processing
使用com.sun.tools.javac.processing.JavacProcessingEnvironment類,並且支援使用者自訂的annotation
編譯時間lombook對java檔案進行編譯之後會再次進入Parse and Enter步驟
標註(Attr)和檢查(Check):
是語義分析的一個步驟,使用com.sun.tools.javac.comp.Attr類和com.sun.tools.javac.comp.Check類
主要作用有:
將文法樹中名字、運算式等元素與變數、方法、類型等聯絡到一起
檢查變數使用前是否已聲明
推導泛型方法的型別參數
檢查類型匹配性
進行常量摺疊(將常量文法樹合并)
資料流分析(Flow):
使用com.sun.tools.javac.comp.Flow類
檢查所有語句都可到達
檢查所有checked exception都被捕獲或拋出
檢查所有局部變數在使用前必項確定性賦值
檢查有傳回值的方法必須確定性傳回值
檢查變數的確定性且不重複賦值
轉換類型:(TransTypes)
使用com.sun.tools.javac.comp.TransTypes主要作用是將泛型java代碼轉換成普通的java代碼
eg:
List<Integer> list = Arrays.asList(1, 2, 3);
int i = list.get(0);
轉換後:
List list = Arrays.asList(1, 2, 3);
int i = (Integer)list.get(0);為了保證泛型的語義,增加了強制類型轉換
解除文法糖:(Lower)
使用com.sun.tools.javac.comp.Lower類
消除if(false){...}形式的無用代碼
將含有文法糖的文法樹改寫為含有簡單語言結構的文法樹
eg
具名內部類,匿名內部類,類字面量
自動裝箱拆箱
斷言
foreach迴圈
enum或String類型的switch(java7)
eg:
List<Integer> list = Arrays.asList(1, 2, 3);
int i = list.get(0);
轉換後:
List list = Arrays.asList(//自動裝箱拆箱
new Integer[]{
Integer.valueOf(1),
Integer.valueOf(2),
Integer.valueOf(3)
}
);
int i = ((Integer)list.get(0)).intValue();
產生class檔案:(Gen)
使用com.sun.tool.javac.jvm.Gen
將執行個體成員初始化器收集到構造器中成為<init>()
將靜態成員初始化器收集為<clinit>()
從抽象文法樹產生位元組碼
後序遍曆文法樹
進行最後的少量代碼轉換
String的+操作被產生為StringBuilder操作
x++/x--在條件允許時被最佳化為++x/--x
etc …
從符號表產生Class檔案
產生Class檔案的結構資訊
產生中繼資料(包括常量池)
class檔案所記錄的資訊:
結構資訊
Class檔案格式版本號碼
各部分的數量及大小
中繼資料
類、繼承的超類、實現介面的聲明資訊
域與方法聲明資訊
常量池
使用者自訂的、RetentionPolicy為CLASS或RUNTIME的註解
方法資訊
位元組碼(程式邏輯)
異常處理器表
運算元棧與局部變數區大小
運算元棧的類型記錄(java6開始 StackMapTable)
調試用符號資訊(LineNumberTable,LocalVariableTable)
class檔案例子:
import java.io.Serializable;
public class Foo implements Serializable {
public void bar() {
int i = 31;
if (i > 0) {
int j = 42;
}
}
}
執行編譯:javac -g Foo.java
執行反編譯:javap -c -s -l -verbose Foo
類聲明:
public class Foo extends java.lang.Object implements java.io.Serializable
源檔案名稱:
SourceFile: "Foo.java"
Class檔案結構資訊:
minor version: 0
major version: 50
常量池:
Constant pool:
const #1 = Method #3.#19; // java/lang/Object."<init>":()V
const #2 = class #20; // Foo
const #3 = class #21; // java/lang/Object
const #4 = class #22; // java/io/Serializable
const #5 = Asciz <init>;
const #6 = Asciz ()V;
const #7 = Asciz Code;
const #8 = Asciz LineNumberTable;
const #9 = Asciz LocalVariableTable;
const #10 = Asciz this;
const #11 = Asciz LFoo;;
const #12 = Asciz bar;
const #13 = Asciz j;
const #14 = Asciz I;
const #15 = Asciz i;
const #16 = Asciz StackMapTable;
const #17 = Asciz SourceFile;
const #18 = Asciz Foo.java;
const #19 = NameAndType #5:#6;// "<init>":()V
const #20 = Asciz Foo;
const #21 = Asciz java/lang/Object;
const #22 = Asciz java/io/Serializable;//引入包時使用*通配,從Class檔案來看沒有任何不同,只不過使用萬用字元增加了源碼中名字衝突的可能性而已。
方法中繼資料:
public Foo();
Signature: ()V
LineNumberTable:
line 2: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this LFoo;
Code:
Stack=1, Locals=1, Args_size=1
位元組碼:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
方法中繼資料:
public void bar();
Signature: ()V
LineNumberTable:
line 4: 0
line 5: 3
line 6: 7
line 8: 10
LocalVariableTable: // 參數名跟局部變數名都儲存在LocalVariableTable;返個屬性工作表跟方法體聯絡在一起,而介面的方法沒有方法體,
Start Length Slot Name Signature //所以ASM無法從介面類型上的方法取得參數的名稱
10 0 2 j I
0 11 0 this LFoo;
3 8 1 i I
StackMapTable: number_of_entries = 1
frame_type = 252 /* append */
offset_delta = 10
locals = [ int ]
Code:
Stack=1, Locals=3, Args_size=1
位元組碼:
0: bipush 31
2: istore_1
3: iload_1
4: ifle 10
7: bipush 42
9: istore_2
10: return
Class檔案與jdk版本比較:
JDK版本Class檔案版本(major.minor)
1.0 --------45.3
1.1 --------45.3
1.2 --------46.0
1.3 --------47.0
1.4 --------48.0
5 --------49.0
6 --------50.0
7 --------51.0