Javac編譯原理

Javac編譯原理

轉載來源:http://www.cnblogs.com/wade-luffy/p/5925728.html


1概述

    

java源代碼(符合語言規範)-->javac-->.class(二進制文件)-->jvm-->機器語言(不同平臺不同種類)

如何讓java的語法規則適應java虛擬機的語法規則?這個任務由javac編譯器來完成java語言規範轉換成java虛擬機語言規範。


2編譯原理


  • 詞法分析器:將源碼轉換爲Token流
    • 將源代碼劃分成一個個Token(找出java語言中的if,else,for等關鍵字)
  • 語法分析器:將Token流轉化爲語法樹
    • 將上述的一個個Token組成一句句話(或者說成一句句代碼塊),檢查這一句句話是不是符合Java語言規範(如if後面跟的是不是布爾判斷表達式)
  • 語義分析器:將語法樹轉化爲註解語法樹
    • 將複雜的語法轉化成簡單的語法(eg.註解、foreach轉化爲for循環、去掉永不會用到的代碼塊)並做一些檢查,添加一些代碼(默認構造器)
  • 代碼生成器:將註解語法樹轉化爲字節碼(即將一個數據結構轉化成另一個數據結構)
ps:要獲取javac編譯器,可以通過OpenJDK來下載源碼,可以自己編譯javac的源碼,也可以通過調用jdk的com.sun.tools.javac.main.Main類來手動編譯指定的類。Javac編譯動作的入口是com.sun.tools.javac.main.JavaCompiler類,代碼邏輯集中在這個類的compile()和compile2()方法中,整個編譯最關鍵的處理就由圖中標註的8個方法來完成:


3編譯步驟詳細介紹

3.1詞法分析器

目的:將源碼轉換爲Token流

流程:

一個字符一個字符的讀取源代碼,形成規範化的Token流。規範化的Token包含:

  • java關鍵詞:package、import、public、class、int等
  • 自定義單詞:包名、類名、變量名、方法名
  • 符號:=、;、+、-、*、/、%、{、}等

源碼關鍵:

詞法分析過程是在的JavacParser.parseCompilationUnit()中完成的

com.sun.tools.javac.parser.JavacParser  規定哪些詞符合Java語言規範,具體讀取和歸類不同詞法的操作由scanner完成
com.sun.tools.javac.parser.Scanner  負責逐個讀取源代碼的單個字符,然後解析符合Java語言規範的Token序列,調用一次nextToken()都構造一個Token
com.sun.tools.javac.parser.Tokens$TokenKind  裏面包含了所有token的類型,譬如BOOLEAN,BREAK,BYTE,CASE。
com.sun.tools.javac.util.Names  用來存儲和表示解析後的詞法,每個字符集合都會是一個Name對象,所有的對象都存儲在Name.Table這個內部類中。
com.sun.tools.javac.parser.KeyWords  負責將字符集合對應到token集合中,如,package zxy.demo.com; Token.PACKAGE = package, Token.IDENTIFIER = zxy.demo.com,(這部分又分爲讀取第一個token,爲zxy,判斷下一個token是否爲“.”,是的話接着讀取下一個Token.IDENTIFIER類型的token,反覆直至下一個token不是”.”,也就是說下一個不是Token.IDENIFIER類型的token,Token.SEMI = ;即這個TIDENTIFIER類型的token的Name讀完),KeyWords類負責此任務。

例子:

package compile;
public class Cifa {
    int a;
    int c = a + 1;
}

以上代碼轉化爲的Token流:


3.2語法分析器

目的:將進行詞法分析後形成的Token流中的一個個Token組成一句句話,檢查這一句句話是不是符合Java語言規範。

流程:

  • package
  • import
  • 類(包含class、interface、enum),一下提到的類泛指這三類,並不單單是指class

源碼關鍵:

com.sun.tools.javac.tree.TreeMaker  所有語法節點都是由它生成的,根據Name對象構建一個語法節點
com.sun.tools.javac.tree.JCTree$JCIf   所有的節點都會繼承jctree和實現**tree,譬如 JCIf extends JCTree.JCStatement implements IfTree
com.sun.tools.javac.tree.JCTree的三個屬性

  • Tree tag:每個語法節點都會以整數的形式表示,下一個節點在上一個節點上加1;
  • pos:也是一個整數,它存儲的是這個語法節點在源代碼中的起始位置,一個文件的位置是0,而-1表示不存在
  • type:它代表的是這個節點是什麼java類型,如int,float,還是string等

例子:

複製代碼
package compile;
public class Yufa {
    int a;
    private int c = a + 1;
    //getter
    public int getC() {
        return c;
    }
    //setter
    public void setC(int c) {
        this.c = c;
    }
}
複製代碼

最終語法樹


ps:左邊還少一個import的語法節點

說明:

  • 每一個包package下的所有類都會放在一個JCCompilationUnit節點下,在該節點下包含:package語法樹(作爲pid)、各個類的語法樹
  • 每一個從JCClassDecl發出的分支都是一個完整的代碼塊,上述是四個分支,對應我們代碼中的兩行屬性操作語句和兩個方法塊代碼塊,這樣其實就完成了語法分析器的作用:將一個個Token單詞組成了一句句話(或者說成一句句代碼塊)
  • 在上述的語法樹部分,對於屬性操作部分是完整的,但是對於兩個方法塊,省略了一些語法節點,例如:方法修飾符public、方法返回類型、方法參數。

3.3語義分析器

目的:將語法樹轉化爲註解語法樹

流程:

  • 添加默認的無參構造器(在沒有指定任何有參構造器的情況下),把引用其他類的方法或者變量,抑或是繼承實現來的變量和方法等輸入到類自身的符號表中
  • 處理註解
  • 標註:檢查語義合法性、進行邏輯判斷
    • 檢查語法樹中的變量類型是否匹配(eg.String s = 1 + 2;//這樣"="兩端的類型就不匹配)
    • 檢查變量、方法或者類的訪問是否合法(eg.一個類無法訪問另一個類的private方法)
    • 變量在使用前是否已經聲明、是否初始化
    • 常量摺疊(eg.代碼中:String s = "hello" + "world",語義分析後String s = "helloworld")
    • 推導泛型方法的參數類型
  • 數據流分析
    • 變量的確定性賦值(eg.有返回值的方法必須確定有返回值)
    • final變量只能賦一次值,在編譯的時候再賦值的話會報錯
    • 所有的檢查型異常是否拋出或捕獲
    • 所有的語句都要被執行到(return後邊的語句就不會被執行到,除了finally塊兒)
  • 進一步語義分析
    • 去掉永假代碼(eg.if(false))
    • 變量自動轉換(eg.int和Integer)自動裝箱拆箱
    • 去掉語法糖(eg.foreach轉化爲for循環,assert轉化爲if,內部類解析成一個與外部類相關聯的外部類)
  • 最後,將經過上述處理的語法樹轉化爲最後的註解語法樹

源碼關鍵:

com.sun.tools.javac.comp.Enter  將java類中的符號輸入到符號表中,主要是兩個步驟:

  • 將所有類中出現的符號輸入到類自身的符號表中,所有類符號、類的參數類型符號(泛型參數類型)、超類符號和繼承的接口類型符號等都存儲到一個未處理的列表中。
  • 將這個未處理的列表中所有的類都解析到各自的類符號列表中,這個操作是在MemberEnter.complete()中完成(默認構造器也是在這裏完成的)。

com.sun.tools.javac.processing.JavacProcessingEnvironment  處理註解

com.sun.tools.javac.comp.Attr  檢查語義的合理性並進行邏輯判斷,類型是否匹配,是否初始化,泛型是否可推導,字符串常量合併

com.sun.tools.javac.comp.Check  協助attr,變量類型是否正確

com.sun.tools.javac.comp.Resolve  協助attr,變量方法類的訪問是否合法,是否是靜態變量

com.sun.tools.javac.comp.ConstFold  協助attr,常量摺疊

com.sun.tools.javac.comp.Infer  協助attr,推導泛型

com.sun.tools.javac.comp.Flow  數據流分析和替換等價源代碼的分析(即上面的進一步語義分析)


3.4代碼生成器

目的:將註解語法樹轉化成字節碼,並將字節碼寫入*.class文件。

流程:

  • 將java的代碼塊轉化爲符合JVM語法的命令形式,這就是字節碼
  • 按照JVM的文件組織格式將字節碼輸出到*.class文件中

源碼關鍵:

com.sun.tools.javac.jvm.Gen  遍歷語法樹生成最終的java字節碼
com.sun.tools.javac.jvm.Items  輔助gen,這個類表示任何可尋址的操作項,這些操作項都可以作爲一個單位出現在操作棧上
com.sun.tools.javac.jvm.Code  輔助gen,存儲生成的字節碼,並提供一些能夠影射操作碼的方法

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章