Javac編譯原理


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

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

編譯流程:

流程:

  • 詞法分析器:將源碼轉換為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個方法來完成,

詞法分析器

目的:將源碼轉換為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流:

語法分析器

目的:將進行詞法分析后形成的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、方法返回類型、方法參數。

 語義分析器

目的:將語法樹轉化為注解語法樹

流程:

  • 添加默認的無參構造器(在沒有指定任何有參構造器的情況下),把引用其他類的方法或者變量,抑或是繼承實現來的變量和方法等輸入到類自身的符號表中
  • 處理注解
  • 標注:檢查語義合法性、進行邏輯判斷
    • 檢查語法樹中的變量類型是否匹配(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  數據流分析和替換等價源代碼的分析(即上面的進一步語義分析)

代碼生成器

目的:將注解語法樹轉化成字節碼,並將字節碼寫入*.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,存儲生成的字節碼,並提供一些能夠影射操作碼的方法

 

 

 
       


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM