日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢(xún)
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷(xiāo)解決方案
Javac黑客指南

這篇文章英文的原文在:http://scg.unibe.ch/archive/projects/Erni08b.pdf. 做畢業(yè)設(shè)計(jì)報(bào)告時(shí),老師要求必須翻譯一篇外文,于是很認(rèn)真的翻譯了一下,也算是為開(kāi)源做一點(diǎn)小貢獻(xiàn)。翻譯如下:

譙城ssl適用于網(wǎng)站、小程序/APP、API接口等需要進(jìn)行數(shù)據(jù)傳輸應(yīng)用場(chǎng)景,ssl證書(shū)未來(lái)市場(chǎng)廣闊!成為創(chuàng)新互聯(lián)公司的ssl證書(shū)銷(xiāo)售渠道,可以享受市場(chǎng)價(jià)格4-6折優(yōu)惠!如果有意向歡迎電話聯(lián)系或者加微信:028-86922220(備注:SSL證書(shū)合作)期待與您的合作!

Javac黑客指南

David Erni and Adrian Kuhn

University of Bern, March 2008

0. 摘要:

這篇文章是介紹修改java編譯器的。其中包含對(duì)Java編譯器的介紹,以及兩個(gè)例子的實(shí)現(xiàn):一個(gè)簡(jiǎn)單的hello world 和一個(gè)重寫(xiě)AST(抽象語(yǔ)法樹(shù))的插件。

1. 介紹

隨著Java 6的發(fā)布,java編譯器已經(jīng)有了開(kāi)源的版本了。開(kāi)源的編譯器是OpenJDK項(xiàng)目的一部分,可以從Java編譯器小組的網(wǎng)站下載 http://www.openjdk.org/groups/compiler/ 。然而就這篇文檔的例子來(lái)說(shuō),任何Java 6的版本都是可用的,因?yàn)檫@些例子并不會(huì)重新編譯編譯器,他們只是擴(kuò)展編譯器的功能。

這篇文章介紹了Java編譯器的內(nèi)在實(shí)現(xiàn)。首先我們給出java編譯器所包含的編譯步驟,然后我們?cè)诰帉?xiě)兩個(gè)例子。兩個(gè)例子都使用到了編譯器里面的插件機(jī)制,也就是JSR269所描述的機(jī)制。然而,這兩個(gè)例子卻超出了JSR269的范圍。把JSR對(duì)象和編譯器對(duì)接,我們實(shí)現(xiàn)了AST的重寫(xiě)。我們的例子里面,沒(méi)有使用assertion(斷言)語(yǔ)句,而使用了if-throw語(yǔ)句。

2. Java編譯器的內(nèi)核

這個(gè)部分概括了OpenJDK里面的java編譯器的編譯步驟和對(duì)應(yīng)的注釋。這個(gè)小節(jié)包含了一個(gè)簡(jiǎn)短的介紹。

編譯的過(guò)程是由定義在com.sun.tools.javac.main里面的Java Compiler類(lèi)來(lái)決定的。當(dāng)編譯器以默認(rèn)的編譯參數(shù)編譯時(shí),它會(huì)執(zhí)行以下步驟:

a) Parse: 讀入一堆*.java源代碼,并且把讀進(jìn)來(lái)的符號(hào)(Token)映射到AST節(jié)點(diǎn)上去。

b) Enter: 把類(lèi)的定義放到符號(hào)表(Symbol Table)中去。

c) Process annotations: 可選的。處理編譯單元(compilation units)里面所找到的標(biāo)記(annotation)。

d) Attribute: 為AST添加屬性。這一步包含名字解析(name resolution),類(lèi)型檢測(cè)(type checking)和常數(shù)折疊(constant fold)。

e) Flow: 為前面得到的AST執(zhí)行流分析(Flow analysis)操作。這個(gè)步驟包含賦值(assignment)的檢查和可執(zhí)行性(reachability)的檢查。

f) Desugar: 重寫(xiě)AST, 并且把一些復(fù)雜的語(yǔ)法轉(zhuǎn)化成一般的語(yǔ)法。

g) Generate: 生成源文件或者類(lèi)文件。

wps_clip_image-12054_thumb

2.1 Parse

想要Parse文件,編譯器要用到com.sun.tools.javac.parser.*里面的類(lèi)。作為***步,詞法分析器(lexical analyzer)把輸入的字符流(character sequence)映射成一個(gè)符號(hào)流(token sequence)。然后Parser再把生成的符號(hào)流映射成一個(gè)抽象語(yǔ)法樹(shù)(AST)

2.2 Enter

在這個(gè)步驟中,編譯器會(huì)找到當(dāng)前范圍(enclosing scope)中發(fā)現(xiàn)的所有的定義(definitions),并且把這些定義注冊(cè)成符號(hào)(symbols)。Enter這個(gè)步驟又分為以下兩個(gè)階段:

在***個(gè)階段,編譯器會(huì)注冊(cè)所有類(lèi)的符號(hào),并且把這寫(xiě)符號(hào)和相應(yīng)的范圍(scope)聯(lián)系在一起。實(shí)現(xiàn)方法是使用一個(gè)Visitor(訪問(wèn)者)類(lèi),由上而下的遍歷AST,訪問(wèn)所有的類(lèi),包括類(lèi)里面的內(nèi)部類(lèi)。Enter給每一個(gè)類(lèi)的符號(hào)都添加了一個(gè)MemberEnter對(duì)象,這個(gè)對(duì)象是由第二個(gè)階段來(lái)調(diào)用的

在第二個(gè)階段中,這些類(lèi)被MemberEnter對(duì)象所完成(completed,即完成類(lèi)的成員變量的Enter)。首先,MemberEnter決定一個(gè)類(lèi)的參數(shù),父類(lèi)和接口。然后這些符號(hào)被添加進(jìn)了類(lèi)的范圍中。不像前一個(gè)步驟,這個(gè)步驟是懶惰執(zhí)行的。類(lèi)的成員只有在被訪問(wèn)時(shí),才加入類(lèi)的定義中的。這里的實(shí)現(xiàn),是通過(guò)安裝一個(gè)完成對(duì)象(member object)到類(lèi)的符號(hào)中。這些對(duì)象可以在需要時(shí)調(diào)用member-enter

***,enter把所有的頂層類(lèi)(top-level classes)放到一個(gè)todo-queue中,

2.3 Process Annotations

如果存在標(biāo)記處理器,并且編譯參數(shù)里面指定要處理標(biāo)記,那么這個(gè)過(guò)程就會(huì)處理在某個(gè)編譯單元里面的標(biāo)記。JSR269定義了一個(gè)接口,可以用來(lái)寫(xiě)這種Annotation處理插件。然而,這個(gè)接口的功能非常有限,并且不能用Collective Behavior擴(kuò)展這種語(yǔ)言。主要的限制是JSR269不提供子方法的反射調(diào)用。

2.4 Attribute

為Enter階段生成的所有AST添加屬性。應(yīng)當(dāng)注意,Attribte可能會(huì)需要額外的文件被解析(Parse),通過(guò)SourceCompleter加入到符號(hào)表中。

大多數(shù)的環(huán)境相關(guān)的分析都是發(fā)生在這個(gè)階段的。這些分析包括名稱(chēng)解析,類(lèi)型檢查,常數(shù)折疊。這些都是子任務(wù)。有些子任務(wù)調(diào)用下列的一些類(lèi),但也可能調(diào)用其他的。

l Check:這是用于類(lèi)型檢查的類(lèi)。當(dāng)有完成錯(cuò)誤(completion error)或者類(lèi)型錯(cuò)誤時(shí),它就會(huì)報(bào)錯(cuò)。

l Resovle: 這是名字解析的類(lèi)。如果解析失敗,就會(huì)報(bào)錯(cuò)。

l ConstFold: 這是參數(shù)折疊類(lèi)。常數(shù)折疊用于簡(jiǎn)化在編譯時(shí)的常數(shù)表達(dá)式。

l Infer:類(lèi)參數(shù)引用的類(lèi)。

2.5 Flow

這個(gè)階段會(huì)對(duì)添加屬性后的類(lèi),執(zhí)行數(shù)據(jù)流的檢查。存活性分析(liveness analysis) 檢查是否每個(gè)語(yǔ)句都可以被執(zhí)行到。異常分析(Excepetion analysis) 檢查是豆每個(gè)被拋出的異常都是聲明過(guò)的,并且這些異常是否都會(huì)被捕獲。確定行賦值(definite assignment)分析保證每個(gè)變量在使用時(shí)已經(jīng)被賦值。而確定性不賦值(definite unassignment)分析保證final變量不會(huì)被多次賦值。

2.6 Desugar

除去多余的語(yǔ)法,像內(nèi)部類(lèi),類(lèi)的常數(shù),assertion斷言語(yǔ)句,foreach循環(huán)等。

2.7 Generate

這是最終的階段。這個(gè)階段生成許多源文件或者類(lèi)文件。到底是生成源文件還是類(lèi)文件取決于編譯選項(xiàng)。

3. 什么是JSR 269

Annotation(標(biāo)記)是java 5里面引進(jìn)來(lái)的,用于在源代碼里面附加元信息(meta-information).Java 6則進(jìn)一步加強(qiáng)了標(biāo)記的處理功能,即JSR269. JSR269,即插入式標(biāo)記處理API,為java編譯器添加了一個(gè)插件機(jī)制。有了JSR269,就有能力為java編譯器寫(xiě)一個(gè)特定的標(biāo)記處理器了。

JSR269有兩組基本API,一組用于對(duì)java語(yǔ)言的建模,一組用于編寫(xiě)標(biāo)記處理器。這兩組API分別存在于javax.lang.model.* 和 javax.annotation.processing里面。JSR269的功能是通過(guò)以下的java編譯選項(xiàng)來(lái)調(diào)用的。

-proc:{none,only} 是否執(zhí)行Annotation處理或者編譯

-processor 指定標(biāo)記處理器的名字。這個(gè)選項(xiàng)將繞過(guò)默認(rèn)的標(biāo)記處理器查找過(guò)程

-processorpath 指定標(biāo)記處理器的位置

標(biāo)記處理在javac中時(shí)默認(rèn)開(kāi)啟的。如果要是只想處理標(biāo)記,而不想編譯生成類(lèi)文件的話,用 –proc:only 選項(xiàng)既即可。

#p#

4. 如何用Javac打印出“Hello World!”

在這***個(gè)例子里面,我們些一個(gè)簡(jiǎn)單的標(biāo)記處理器,用于在編譯的時(shí)候打印“Hello World!”.我們用編譯器的內(nèi)部消息機(jī)制來(lái)打印“hello world”。

首先,我們定義如下HelloWorld標(biāo)記。

 
 
 
  1. public @interface HelloWorld{
  2. }

添加一個(gè)Dummy類(lèi)使用以上的標(biāo)記

 
 
 
  1. @HelloWorld
  2. public class Dummy{
  3. }

標(biāo)記處理可能會(huì)發(fā)生很輪。每一輪處理器只處理特定的一些標(biāo)記,并且生成的源文件或者類(lèi)文件,交給下一輪來(lái)處理。如果處理器被要求只處理特定的某一輪,那么他也會(huì)處理后續(xù)的那些次,包括***一輪,就算***一輪沒(méi)有可以處理的標(biāo)記。處理器可能也會(huì)去處理被這個(gè)工具生成的文件。

后一個(gè)方法處理前一輪生成的標(biāo)記類(lèi)型,并且返回是否這些標(biāo)記會(huì)聲明。如果返回是True,那么后續(xù)的處理器就不會(huì)去處理它們。如果返回是false,那么后續(xù)處理器會(huì)繼續(xù)處理它們。一個(gè)處理器可能總是返回同樣的邏輯值,或者是根據(jù)選項(xiàng)改變結(jié)果。為了要寫(xiě)一個(gè)標(biāo)記處理器,我們用一個(gè)子類(lèi)來(lái)繼承AbstractProcessor,并且用SupportedAnnotationTyps 和SupportedSourceVersion標(biāo)記這個(gè)子類(lèi)。這個(gè)子類(lèi)必須要復(fù)寫(xiě)這兩個(gè)方法:

l public synchronized void init(ProcessingEnvironment processingEnv)

l public boolean process(Set annotations,

RoundEnvironment roundEnv)

這兩個(gè)方法都是在標(biāo)記處理過(guò)程中被java編譯器調(diào)用的。***個(gè)方法用來(lái)初始化插件,只被調(diào)用一次。而第二個(gè)方法每一輪標(biāo)記處理都會(huì)被調(diào)用,并且在所有處理都結(jié)束后還會(huì)調(diào)用一次。

我們的簡(jiǎn)單的HelloWorldProcessors是這樣生成的:

 
 
 
  1. import javax.annotation.processing.*;
  2. import javax.lang.model.SourceVersion;
  3. import javax.lang.model.element.TypeElement;
  4. import javax.tools.Diagnostic;
  5. @SupportedAnnotationTypes("HelloWorld")
  6. @SupportedSourceVersion(SourceVersion.RELEASE_6)
  7. public class HelloWorldProcessor extends AbstractProcessor {
  8. @Override
  9. public synchronized void init(ProcessingEnvironment processingEnv) {
  10. super.init(processingEnv);
  11. }
  12. @Override
  13. public boolean process(Set annotations,
  14. RoundEnvironment roundEnv) {
  15. if (!roundEnv.processingOver()) {
  16. processingEnv.getMessager().printMessage(
  17. Diagnostic.Kind.NOTE, "Hello Worlds!");
  18. }
  19. return true;
  20. }
  21. }

第八行注冊(cè)了HelloWorld的標(biāo)記處理器。也就是說(shuō),當(dāng)標(biāo)記出現(xiàn)是,就會(huì)有一系列的程序被自動(dòng)調(diào)用。第九行設(shè)置了標(biāo)記所支持的源代碼版本。

第12到15行復(fù)寫(xiě)了初始化方法, 目前為止,我們只是調(diào)用父類(lèi)的方法。

第17到24行復(fù)寫(xiě)了處理方法。這個(gè)方法是由一些列被標(biāo)記的程序元素來(lái)調(diào)用的。這個(gè)方法在每一輪處理時(shí),都會(huì)調(diào)用,并且在***會(huì)多出一輪,用于對(duì)空集合的元素的處理。這樣,我們可以由一個(gè)簡(jiǎn)單的if語(yǔ)句,使得***多出的那一輪什么事情都不做。在其他輪中,我們只打印一個(gè)hello world消息。我們不用System.out.print,二十使用編譯器的消息框架來(lái)打印一個(gè)消息(note類(lèi)型的)。其他可能的類(lèi)型是警告(warning)或者錯(cuò)誤(error)。

這個(gè)方法返回true,如果你想要聲明元素已經(jīng)被處理過(guò)了。

要運(yùn)行這個(gè)例子,執(zhí)行:

javac HelloWorldProcessor.java

javac -processor HelloWorldProcessor *.java

這個(gè)應(yīng)該會(huì)輸出:

Note: Hello World!

5. 如何巧妙利用JSR269來(lái)重寫(xiě)AST

在這個(gè)例子中,我們深入到編譯器自身的實(shí)現(xiàn)細(xì)節(jié)中去。我們利用JSR269做一些超出它本身的事情—重寫(xiě)AST。這個(gè)處理器會(huì)把每一個(gè)Assertion語(yǔ)句替換成一個(gè)throw語(yǔ)句。也就是說(shuō),每當(dāng)有以下語(yǔ)句出現(xiàn)時(shí)

assert cond: detail;

會(huì)被替換成:

If(!cond) throw new AssertionError(detail);

后面的這個(gè)語(yǔ)句不會(huì)生成assert的字節(jié)碼,而是生成一個(gè)普通的if語(yǔ)句,帶有一個(gè)throw重句。結(jié)果就算你的虛擬機(jī)沒(méi)有激活assertions功能時(shí),assertions的檢查還是會(huì)被執(zhí)行。這個(gè)功能對(duì)各種庫(kù)是非常有用的,因?yàn)槟銓?xiě)庫(kù)的時(shí)候,是沒(méi)有辦法控制用戶(hù)的VM設(shè)置的。

再次,我們還是先繼承AbstractProcessor。然而,這次我們不會(huì)針對(duì)某一個(gè)特殊的標(biāo)記,而是用“*”這個(gè)符號(hào)來(lái)表示對(duì)所有的源代碼都調(diào)用處理器。

 
 
 
  1. @SupportedAnnotationTypes("*")
  2. @SupportedSourceVersion(SourceVersion.RELEASE_6)
  3. public class ForceAssertions extends AbstractProcessor {
  4. }

初始化方法如下:

 
 
 
  1. private int tally;
  2. private Trees trees;
  3. private TreeMaker make;
  4. private Name.Table names;
  5. @Override
  6. public synchronized void init(ProcessingEnvironment env) {
  7.       super.init(env);
  8.       trees = Trees.instance(env);
  9.      Context context = ((JavacProcessingEnvironment)
  10. env).getContext();
  11.       make = TreeMaker.instance(context);
  12.       names = Name.Table.instance(context);
  13.       tally = 0;
  14.  }

我們使用處理環(huán)境(ProcessingEnvironment)來(lái)獲得對(duì)編譯器一些組件的引用。在編譯器里面,在每次調(diào)用編譯器時(shí)都會(huì)有一個(gè)處理環(huán)境(ProcessingEnvironment)。在編譯器中,我們使用Component.instance(context)來(lái)獲得對(duì)組件的引用。

我們使用的組件如下:

l Trees – JSR269的一個(gè)工具類(lèi),用于聯(lián)系程序元素和樹(shù)節(jié)點(diǎn)。比如,對(duì)于一個(gè)方法元素,我們可以獲得這個(gè)元素對(duì)應(yīng)的AST樹(shù)節(jié)點(diǎn)。

l TreeMaker – 編譯器的內(nèi)部組件,是用于創(chuàng)建樹(shù)節(jié)點(diǎn)的工廠類(lèi)。工廠類(lèi)里面方法的命名方式跟Javac源代碼里面的方法是統(tǒng)一的。

l Name.Table – 另一個(gè)編譯器的內(nèi)部組件。Name類(lèi)是編譯器內(nèi)部字符串的一個(gè)抽象。為了提高效率,Javac使用了哈希字符串。

請(qǐng)注意,在第39行,我們把處理環(huán)境(ProcessingEnvironment)強(qiáng)制轉(zhuǎn)換成了編譯器的內(nèi)部類(lèi)型。

***,我們把一個(gè)計(jì)數(shù)器初始化成0.這個(gè)計(jì)數(shù)器是用來(lái)記錄發(fā)生替換的數(shù)量。

處理方法如下:

 
 
 
  1. @Override
  2. 46 public boolean process(Set annotations,
  3. RoundEnvironment roundEnv) {
  4.     if (!roundEnv.processingOver()) {
  5.        Set elements = roundEnv.getRootElements();
  6.        for (Element each : elements) {
  7.        if (each.getKind() == ElementKind.CLASS) {
  8.           JCTree tree = (JCTree) trees.getTree(each);
  9.           TreeTranslator visitor = new Inliner();
  10.           tree.accept(visitor);
  11.       }
  12.    }
  13.    } else
  14.     processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,
  15. tally + " assertions inlined.");
  16.     return false;
  17.  }

我們遍歷所有的程序元素,為每一個(gè)類(lèi)都重寫(xiě)AST。在第51行,我們把JSR269的樹(shù)節(jié)點(diǎn)轉(zhuǎn)換成編譯器內(nèi)部的樹(shù)節(jié)點(diǎn)。這兩種樹(shù)節(jié)點(diǎn)的不同之處在于,JSR269節(jié)點(diǎn)是停留在方法層的(即方法method是最基本的元素,不會(huì)再細(xì)分下去),而內(nèi)部的AST節(jié)點(diǎn),是所有元素(包括方法以下的)都可以訪問(wèn)的。我們要訪問(wèn)每一個(gè)語(yǔ)句,所以需要訪問(wèn)到AST的所有節(jié)點(diǎn)。

#p#

樹(shù)的轉(zhuǎn)換是通過(guò)繼承TreeTranslator來(lái)完成的,TreeTranslator本身是繼承自TreeVisitor的。這些類(lèi)都不是JSR269的一部分。所以,從這里開(kāi)始,我們所寫(xiě)的所有代碼都是在編譯器內(nèi)部工作的。

在第57行,是else部分,用于報(bào)告處理過(guò)的assertion語(yǔ)句數(shù)量。這個(gè)語(yǔ)句只有在***一輪處理才會(huì)執(zhí)行。

Inliner這個(gè)類(lèi)實(shí)現(xiàn)了AST重寫(xiě)。Inliner繼承了TreeTranslator,并且是標(biāo)記處理器的一個(gè)內(nèi)部類(lèi)。注意,TreeTranslator本身是不會(huì)轉(zhuǎn)換任何節(jié)點(diǎn)的。

private class Inliner extends TreeTranslator {

}

為了轉(zhuǎn)換assertion語(yǔ)句,我們需要復(fù)寫(xiě)默認(rèn)的TreeTranslator.visitAssert (JCAssert) 方法,如下所示:

 
 
 
  1. @Override
  2.  public void visitAssert(JCAssert tree) {
  3.     super.visitAssert(tree);
  4.     JCStatement newNode = makeIfThrowException(tree);
  5.     result = newNode;
  6.     tally++;
  7.  }

正在轉(zhuǎn)換的節(jié)點(diǎn)會(huì)被當(dāng)做參數(shù)傳入到方法中。在第67行,轉(zhuǎn)換的結(jié)果,通過(guò)賦值給變量TreeTranslator.result而返回。

按照慣例,一個(gè)轉(zhuǎn)換方法應(yīng)該這樣生成:

l 調(diào)用父類(lèi)的轉(zhuǎn)換方法,以確保轉(zhuǎn)換可以被應(yīng)用到自己點(diǎn)上面去。

l 執(zhí)行真正的轉(zhuǎn)換

l 把轉(zhuǎn)換結(jié)果賦值給TreeTranslator.result。結(jié)果的類(lèi)型不一定要和傳進(jìn)來(lái)的參數(shù)的類(lèi)型一樣。相反,只要java編譯器允許,我們可以返回任何類(lèi)型的節(jié)點(diǎn)。這里TreeTranslator本身沒(méi)有限制類(lèi)型,但是如果返回了錯(cuò)誤的類(lèi)型,那么就很有在后續(xù)過(guò)程中產(chǎn)生災(zāi)難性后果。

我們寫(xiě)一個(gè)私有函數(shù)來(lái)實(shí)現(xiàn)轉(zhuǎn)換,makeIfThrowException:

 
 
 
  1. private JCStatement makeIfThrowException(JCAssert node) {
  2. // make: if (!(condition) throw new AssertionError(detail);
  3. List args = node.getDetail() == null
  4. ? List. nil()
  5. : List.of(node.detail);
  6. JCExpression expr = make.NewClass(
  7. null,
  8. null,
  9. make.Ident(names.fromString("AssertionError")),
  10. args,
  11. null);
  12. return make.If(
  13. make.Unary(JCTree.NOT, node.cond),
  14. make.Throw(expr),
  15. null);
  16. }

這個(gè)方法傳入一個(gè)assertion語(yǔ)句,返回一個(gè)if語(yǔ)句。我們可以這樣做,事因?yàn)椴还苁莂ssertion還是if,他們都是語(yǔ)句(statement),所以在java的語(yǔ)法中是等價(jià)的。Java中沒(méi)有明文規(guī)定,禁止用if語(yǔ)句來(lái)代替assertion語(yǔ)句。

makeIfThrowException是用于AST重寫(xiě)的方法。我們使用TreeMaker來(lái)創(chuàng)建新的樹(shù)節(jié)點(diǎn)。如果有這樣的一個(gè)表達(dá)式:

assert cond:detail;

我們就可以替換成下面的形式:

If(!cond) throw new AssertionErrror(detal);

在第73到75行,我們考慮到了detail被省略的情況。在76到81行,我們創(chuàng)建了一個(gè)AST節(jié)點(diǎn),這個(gè)節(jié)點(diǎn)的作用是創(chuàng)建AssertionError。在第79行,我們使用Name.Table來(lái)把字符串“AssertionError”變成編譯器內(nèi)部的字符串。在80行,我們?cè)賯魅?3到75行創(chuàng)建的參數(shù)args。第77,78和81行傳入了null值,因?yàn)檫@個(gè)節(jié)點(diǎn)既沒(méi)有外部實(shí)例,也沒(méi)有類(lèi)型參數(shù),也不是在匿名類(lèi)內(nèi)部。

在第83行,我們對(duì)assertion的條件做了一個(gè)Not操作。84行,我們創(chuàng)建了一個(gè)throw表達(dá)式,***,在82到85行,我們把所有的東西都放到了if語(yǔ)句中。

注意:List類(lèi)是java編譯器中另外一個(gè)令人印象深刻的實(shí)現(xiàn)。編譯器用了它自己的數(shù)據(jù)類(lèi)型來(lái)實(shí)現(xiàn)List,而不是使用java集合框架(Java Collection Framework)。List和Pair數(shù)據(jù)類(lèi)的實(shí)現(xiàn),都用到了Lisp語(yǔ)言里面所謂的cons。Pairs是這樣實(shí)現(xiàn)的:

 
 
 
  1. public class Pair {
  2. public final A fst;
  3. public final B snd;
  4. public Pair(A fst, B snd) {
  5. this.fst = fst;
  6. this.snd = snd;
  7. }
  8. ...
  9. }

而List是這樣實(shí)現(xiàn)的:

 
 
 
  1. public class List extends AbstractCollection implements
  2. java.util.List {
  3. public A head;
  4. public List tail;
  5. public List(A head, List tail) {
  6. this.tail = tail;
  7. this.head = head;
  8. }
  9. ...
  10. }

并且有許多靜態(tài)的方法,可以很方便的創(chuàng)建List:

l List.nil()

l List.of(A)

l List.of(A,A)

l List.of(A,A,A)

l List.of(A,A,A,A...)

Pair也是一樣:

l Pair.of(A,B)

同樣,非傳統(tǒng)的命名方式也帶來(lái)了更漂亮的代碼

不像傳統(tǒng)java中用的代碼:

List list = new List();

list.add(a);

list.add(b);

list.add(c);

而現(xiàn)在只需要寫(xiě):

List.of(a, b, c);

#p#

5.1 運(yùn)行AST重寫(xiě)

為了展示AST重寫(xiě),我們使用:

 
 
 
  1. public class Example {
  2. public static void main(String[] args) {
  3.     String str = null;
  4.     assert str != null : "Must not be null";
  5. }
  6. }

并且執(zhí)行:

javac ForceAssertions.java

javac -processor ForceAssertions Example.java

就會(huì)產(chǎn)生這樣的輸出:

Note: 1 assertions inlined

現(xiàn)在,我們我們我們禁用assertion,再執(zhí)行例子:

java -disableassertions Example

得到:

Exception in thread "main" java.lang.AssertionError: Must not be null at Example.main(Example.java:1)

利用編譯器的選項(xiàng) –printsource,我們甚至可以得到重寫(xiě)過(guò)后的AST,并且以Java源代碼的方式顯示出來(lái)。要注意的是,我們必須重定向輸出,否者原來(lái)的源文件會(huì)被覆蓋了。

執(zhí)行:

javac -processor ForceAssertions -printsource -d gen Example.java

產(chǎn)生結(jié)果:

 
 
 
  1. public class Example {
  2. public Example() {
  3.     super();
  4. }
  5. public static void main(String[] args) {
  6.     String str = null;
  7.     if (!(str != null)) throw new AssertionError("Must not be null");
  8. }
  9. }

可以發(fā)現(xiàn),第9行已經(jīng)被重寫(xiě)過(guò)了,第3到5行加入了一個(gè)默認(rèn)的構(gòu)造函數(shù)。

5.2 如何把標(biāo)記處理器注冊(cè)成服務(wù)

Java提供了一個(gè)注冊(cè)服務(wù)的機(jī)制。如果一個(gè)標(biāo)記處理器被注冊(cè)成了一個(gè)服務(wù),編譯器就會(huì)自動(dòng)的去找到這個(gè)標(biāo)記處理器。注冊(cè)的方法是,在classpath中找到一個(gè)叫META-INF/services的文件夾,然后放入一個(gè)javax.annotation.processing.Processor的文件。文件格式是很明顯的,就是要包含要注冊(cè)的標(biāo)記處理器的完整名稱(chēng)。每個(gè)名字都要占單獨(dú)的一行。

5.3 進(jìn)一步的閱讀

Erni在他的本科畢業(yè)設(shè)計(jì)中描述了一個(gè)更復(fù)雜的編譯器修改。他不是依賴(lài)JSR269,而是直接在編譯過(guò)程中的幾個(gè)點(diǎn)進(jìn)行直接修改。

參考

[1] David Erni. JAG - a Prototype for Collective Behavior in Java. Bachelors

Thesis, University of Bern. March 2008.

[2] Joseph D. Darcy. JSR-000269 Pluggable Annotation Processing API,

December 2006. http://jcp.org/en/jsr/detail?id=269.

A 在OSX下面安裝Java 6

默認(rèn)情況下,當(dāng)前的OSX只是內(nèi)置 了Java 5.0,為了安裝Java6,從以下地址下載安裝文件http://www.apple.com/support/downloads/javaformacosx105update1.html. 這個(gè)更新需要Mac OS X 10.5.2或者是更新的,而且要64位基于Intel的Mac。這個(gè)更新不會(huì)替換已經(jīng)存在的J2SE 5.0安裝,或者是改變Java的默認(rèn)版本。

B ForceAssertions.java的全部源代碼

 
 
 
  1.  import java.util.Set;
  2.  import javax.annotation.processing.AbstractProcessor;
  3.  import javax.annotation.processing.ProcessingEnvironment;
  4.  import javax.annotation.processing.RoundEnvironment;
  5.  import javax.annotation.processing.SupportedAnnotationTypes;
  6.  import javax.annotation.processing.SupportedSourceVersion;
  7.  import javax.lang.model.SourceVersion;
  8.  import javax.lang.model.element.Element;
  9.  import javax.lang.model.element.ElementKind;
  10.  import javax.lang.model.element.TypeElement;
  11.  import javax.tools.Diagnostic;
  12.  import com.sun.source.util.Trees;
  13.  import com.sun.tools.javac.processing.JavacProcessingEnvironment;
  14.  import com.sun.tools.javac.tree.JCTree;
  15.  import com.sun.tools.javac.tree.TreeMaker;
  16.  import com.sun.tools.javac.tree.TreeTranslator;
  17.  import com.sun.tools.javac.tree.JCTree.JCAssert;
  18.  import com.sun.tools.javac.tree.JCTree.JCExpression;
  19.  import com.sun.tools.javac.tree.JCTree.JCStatement;
  20.  import com.sun.tools.javac.util.Context;
  21.  import com.sun.tools.javac.util.List;
  22.  import com.sun.tools.javac.util.Name;
  23.  @SupportedAnnotationTypes("*")
  24.  @SupportedSourceVersion(SourceVersion.RELEASE_6)
  25.  public class ForceAssertions extends AbstractProcessor {
  26.  private int tally;
  27.  private Trees trees;
  28.  private TreeMaker make;
  29.  private Name.Table names;
  30.  @Override
  31.  public synchronized void init(ProcessingEnvironment env) {
  32.      super.init(env);
  33.      trees = Trees.instance(env);
  34.      Context context = ((JavacProcessingEnvironment)
  35. env).getContext();
  36.     make = TreeMaker.instance(context);
  37.      names = Name.Table.instance(context);
  38.      tally = 0;
  39.  }
  40.  @Override
  41.  public boolean process(Set annotations,
  42. RoundEnvironment roundEnv) {
  43.      if (!roundEnv.processingOver()) {
  44.      Set elements =
  45. roundEnv.getRootElements();
  46.      for (Element each : elements) {
  47.          if (each.getKind() == ElementKind.CLASS) {
  48.          JCTree tree = (JCTree) trees.getTree(each);
  49.          TreeTranslator visitor = new Inliner();
  50.          tree.accept(visitor);
  51.          }
  52.      }
  53.  } else
  54.      processingEnv.getMessager().printMessage(
  55. Diagnostic.Kind.NOTE, tally + " assertions
  56. inlined.");
  57.      return false;
  58.  }
  59.  private class Inliner extends TreeTranslator {
  60.  @Override
  61.  public void visitAssert(JCAssert tree) {
  62.       super.visitAssert(tree);
  63.       JCStatement newNode = makeIfThrowException(tree);
  64.       result = newNode;
  65.       tally++;
  66.  }
  67.  private JCStatement makeIfThrowException(JCAssert node) {
  68.  // make: if (!(condition) throw new AssertionError(detail);
  69.      List args = node.getDetail() == null
  70.                       ? List. nil()
  71.                       : List.of(node.detail);
  72.      JCExpression expr = make.NewClass(
  73.      null,
  74.      null,
  75.      make.Ident(names.fromString("AssertionError")),
  76.      args,
  77.      null);
  78.      return make.If(
  79.             make.Unary(JCTree.NOT, node.cond),
  80.             make.Throw(expr),
  81.             null);
  82.  }
  83.  }
  84.  }

譯文連接:http://my.oschina.net/superpdm/blog/129070


當(dāng)前標(biāo)題:Javac黑客指南
分享網(wǎng)址:
http://www.5511xx.com/article/dpecpsi.html