編譯器知識雜記-javac

簡介:

先看一張Javc編譯成class文件的時候流程圖

image.png

至于什么是token流,語法樹相關可以參考我之前的兩篇帖子。

http://www.lxweimin.com/p/7a9476c65672

http://www.lxweimin.com/p/49ca10345080

如何下載JavaC源碼?

直接使用openJdk進行下載,下載地址為 https://hg.openjdk.java.net/jdk8/jdk8/langtools/

也可以直接在我的在我的項目庫內部進行下載,地址見下文。

Javac主要有四個模塊,分別是詞法分析器,語法分析器,語義分析器和代碼生成器。

JavaC JavaParser

用途:

把Java源碼轉換成 JavaParser定義的Statement對象,

也就是將java源碼解析成一顆語法樹,然后基于這棵樹對java代碼進行分析和修改的工具。

在javac 編譯時候使用到

重點類介紹

語法樹簡介:

在學習之前需要知道什么是語法樹,很簡單的代碼如下,在解析的時候主要分為兩步

“1 + 2 * 3”

第一步: +, 1,(2 * 3)

第二步: +, 1,(*, 2, 3)

語法樹

com.sun.tools.java.tree.JCTree

用于表示最終的語法樹,結構,里面有很多內部類。

比如com.sun.tools.java.tree.JCTree$JCCompilationUnit,在AST中,你可以把這個類看成是AST的根節點

插入式注解器簡介

Jdk1.5后引入注解功能,注解是一種應用字節碼 屬性中類的元數據進行操作的一種編程機制。

處理表形成后 會自動檢測是否有注解器需要執行,若有則執行注解處理器。注解處理器實現了在可插入式的編譯期改變編譯過程的功能。

其本質就是 再次修改 處理表中的語法樹。 一旦語法樹被修改,則將再次進行 詞法,語法分析并填充符號表的過程,直到所有插件對語法樹進行修改完為止。

初始化過程入口是 initPorcessAnnotations()

執行過程入口是 processAnnotations()

該方法判斷是否有新的注解處理器需要執行,若有的,則通過com.sun.tools.方法生成新的JavaCompiler對象對編譯的后續步驟進行處理

JavaC 詞法分析

詞法分析主要是將代碼轉換成token

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

入口是parseFiles()

詞法分析實現類是com.sun.tools.javac.parser.Scanner類

語法分析實現類是com.sun.tools.javac.parser.Parser

出口由com.sun.tools.javac.tree.JCTree類表示

重點介紹下面幾個類,是構成詞法分析的關鍵

先介紹詞法分析“大管家”

com.sun.tools.javac.parser.ParserFactory

存放著很多的解析器比如token,源文件,名字注解之類的。

通過靜態方法單例進行初始化

//獲取實類的單例方法
public static ParserFactory instance(Context context) {
    ParserFactory instance = context.get(parserFactoryKey);
    if (instance == null) {
        instance = new ParserFactory(context);
    }
    return instance;
}

//創建的時候在構造方法內部,將詞法分析需要的一些類進行初始化
//在編譯的時候全部的解析器包括,生成器,各種工具類,都是全局唯一的單例模式
protected ParserFactory(Context context) {
        super();
        context.put(parserFactoryKey, this);
        this.F = TreeMaker.instance(context);
        this.docTreeMaker = DocTreeMaker.instance(context);
        this.log = Log.instance(context);
        this.names = Names.instance(context);
        this.tokens = Tokens.instance(context);
        this.source = Source.instance(context);
        this.options = Options.instance(context);
        this.scannerFactory = ScannerFactory.instance(context);
        this.locale = context.get(Locale.class);
}


com.sun.tools.javac.parser.JavacParser

規定哪些詞符合Java語言規范,具體讀取和歸類不同詞法的操作由scanner完成

/**
 *  Skip forward until a suitable stop token is found.
 *  核心方法,判斷是否是我們需要的token
 */
private void skip(boolean stopAtImport, boolean stopAtMemberDecl, boolean stopAtIdentifier, boolean stopAtStatement) {

com.sun.tools.javac.parser.Scanner

負責逐個讀取源代碼的單個字符,然后解析符合Java語言規范的Token序列,調用一次nextToken()都構造一個Token

內部有個List 儲存讀取到的token信息

  /**
     *  Buffer of saved tokens (used during lookahead)
     *  token的一個緩沖區  
     */
    private List<Token> savedTokens = new ArrayList<Token>();

com.sun.tools.javac.parser.Tokens$TokenKind

里面包含了所有token的類型,譬如BOOLEAN,BREAK,BYTE,CASE。用的是枚舉的方式進行保存

類似如下:

public enum TokenKind implements Formattable, Filter<TokenKind> {
        EOF(),
        ERROR(),
        IDENTIFIER(Tag.NAMED),
        ABSTRACT("abstract"),
        ASSERT("assert", Tag.NAMED),
        BOOLEAN("boolean", Tag.NAMED),
        BREAK("break"),
        BYTE("byte", Tag.NAMED),
        CASE("case"),
        CATCH("catch"),
        CHAR("char", Tag.NAMED),
        CLASS("class"),
        CONST("const"),
        ...

com.sun.tools.javac.parser.Tokens

存放全部的定義token

/**
 * The names of all tokens.
 * 存放全部token的數組 
 */
private Name[] tokenName = new Name[TokenKind.values().length];

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流如下

《轉載自https://www.cnblogs.com/wade-luffy/p/5925728.html》

JavaC 語法分析

將token流轉換成句子

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;
    }
}
《轉載自https://www.cnblogs.com/wade-luffy/p/5925728.html》

說明:

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

JavaC語義分析器

流程:

  • 添加默認的無參構造器(在沒有指定任何有參構造器的情況下),把引用其他類的方法或者變量,抑或是繼承實現來的變量和方法等輸入到類自身的符號表中

  • 處理注解

  • 標注:檢查語義合法性、進行邏輯判斷

    • 檢查語法樹中的變量類型是否匹配(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,內部類解析成一個與外部類相關聯的外部類)
  • 最后,將經過上述處理的語法樹轉化為最后的注解語法樹

入口是attribute()

實現類是com.sun.tools.javac.comp.Attr類和com.sun.tools.javac.comp.Check類

源碼關鍵:

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

數據流分析和替換等價源代碼的分析(即上面的進一步語義分析)

字節碼生成:

進行了少量的代碼添加和轉換工作

把生成的信息(語法樹、符號表)轉化成字節碼寫到磁盤

“寫到磁盤”由com.sun.tools.輸出字節碼,生成最終Class文件

入口generate()

實現類com.sun.tools.javac.jvm.Gen類

項目地址:

https://github.com/w296488320/OllJavaC

參考:

https://zhuanlan.zhihu.com/p/93939780

https://www.cnblogs.com/wade-luffy/p/5925728.html

https://blog.csdn.net/crabstew/article/details/89547472

https://www.sohu.com/a/212579385_100063030

http://blog.sina.com.cn/s/blog_17c534c120102xwv9.html


安卓逆向百級教程+全網最新js逆向視頻+永久小蜜圈+永久售后群=1299

視頻下載網盤
?http://nas.alienhe.cn:5008/home.html?
下載視頻賬號密碼:
賬號guest 密碼world

Js試看:
http://oss.alienhe.cn/JS%E9%80%86%E5%90%91%E5%85%A5%E9%97%A8-%E5%B8%A6%E6%B0%B4%E5%8D%B0.mp4

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,533評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,055評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,365評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,561評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,346評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,889評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,978評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,118評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,637評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,558評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,739評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,246評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,980評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,619評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,347評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,702評論 2 370

推薦閱讀更多精彩內容