精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

Javac黑客指南

開發 后端
這篇文章是介紹修改java編譯器的。其中包含對Java編譯器的介紹,以及兩個例子的實現:一個簡單的hello world 和一個重寫AST(抽象語法樹)的插件。

這篇文章英文的原文在:http://scg.unibe.ch/archive/projects/Erni08b.pdf. 做畢業設計報告時,老師要求必須翻譯一篇外文,于是很認真的翻譯了一下,也算是為開源做一點小貢獻。翻譯如下:

Javac黑客指南

David Erni and Adrian Kuhn

University of Bern, March 2008

0. 摘要:

這篇文章是介紹修改java編譯器的。其中包含對Java編譯器的介紹,以及兩個例子的實現:一個簡單的hello world 和一個重寫AST(抽象語法樹)的插件。

1. 介紹

隨著Java 6的發布,java編譯器已經有了開源的版本了。開源的編譯器是OpenJDK項目的一部分,可以從Java編譯器小組的網站下載 http://www.openjdk.org/groups/compiler/ 。然而就這篇文檔的例子來說,任何Java 6的版本都是可用的,因為這些例子并不會重新編譯編譯器,他們只是擴展編譯器的功能。

這篇文章介紹了Java編譯器的內在實現。首先我們給出java編譯器所包含的編譯步驟,然后我們在編寫兩個例子。兩個例子都使用到了編譯器里面的插件機制,也就是JSR269所描述的機制。然而,這兩個例子卻超出了JSR269的范圍。把JSR對象和編譯器對接,我們實現了AST的重寫。我們的例子里面,沒有使用assertion(斷言)語句,而使用了if-throw語句。

2. Java編譯器的內核

這個部分概括了OpenJDK里面的java編譯器的編譯步驟和對應的注釋。這個小節包含了一個簡短的介紹。

編譯的過程是由定義在com.sun.tools.javac.main里面的Java Compiler類來決定的。當編譯器以默認的編譯參數編譯時,它會執行以下步驟:

a) Parse: 讀入一堆*.java源代碼,并且把讀進來的符號(Token)映射到AST節點上去。

b) Enter: 把類的定義放到符號表(Symbol Table)中去。

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

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

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

f) Desugar: 重寫AST, 并且把一些復雜的語法轉化成一般的語法。

g) Generate: 生成源文件或者類文件。

wps_clip_image-12054_thumb

2.1 Parse

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

2.2 Enter

在這個步驟中,編譯器會找到當前范圍(enclosing scope)中發現的所有的定義(definitions),并且把這些定義注冊成符號(symbols)。Enter這個步驟又分為以下兩個階段:

在***個階段,編譯器會注冊所有類的符號,并且把這寫符號和相應的范圍(scope)聯系在一起。實現方法是使用一個Visitor(訪問者)類,由上而下的遍歷AST,訪問所有的類,包括類里面的內部類。Enter給每一個類的符號都添加了一個MemberEnter對象,這個對象是由第二個階段來調用的

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

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

2.3 Process Annotations

如果存在標記處理器,并且編譯參數里面指定要處理標記,那么這個過程就會處理在某個編譯單元里面的標記。JSR269定義了一個接口,可以用來寫這種Annotation處理插件。然而,這個接口的功能非常有限,并且不能用Collective Behavior擴展這種語言。主要的限制是JSR269不提供子方法的反射調用。

2.4 Attribute

為Enter階段生成的所有AST添加屬性。應當注意,Attribte可能會需要額外的文件被解析(Parse),通過SourceCompleter加入到符號表中。

大多數的環境相關的分析都是發生在這個階段的。這些分析包括名稱解析,類型檢查,常數折疊。這些都是子任務。有些子任務調用下列的一些類,但也可能調用其他的。

l Check:這是用于類型檢查的類。當有完成錯誤(completion error)或者類型錯誤時,它就會報錯。

l Resovle: 這是名字解析的類。如果解析失敗,就會報錯。

l ConstFold: 這是參數折疊類。常數折疊用于簡化在編譯時的常數表達式。

l Infer:類參數引用的類。

2.5 Flow

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

2.6 Desugar

除去多余的語法,像內部類,類的常數,assertion斷言語句,foreach循環等。

2.7 Generate

這是最終的階段。這個階段生成許多源文件或者類文件。到底是生成源文件還是類文件取決于編譯選項。

3. 什么是JSR 269

Annotation(標記)是java 5里面引進來的,用于在源代碼里面附加元信息(meta-information).Java 6則進一步加強了標記的處理功能,即JSR269. JSR269,即插入式標記處理API,為java編譯器添加了一個插件機制。有了JSR269,就有能力為java編譯器寫一個特定的標記處理器了。

JSR269有兩組基本API,一組用于對java語言的建模,一組用于編寫標記處理器。這兩組API分別存在于javax.lang.model.* 和 javax.annotation.processing里面。JSR269的功能是通過以下的java編譯選項來調用的。

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

-processor <classes> 指定標記處理器的名字。這個選項將繞過默認的標記處理器查找過程

-processorpath <path> 指定標記處理器的位置

標記處理在javac中時默認開啟的。如果要是只想處理標記,而不想編譯生成類文件的話,用 –proc:only 選項既即可。

#p#

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

在這***個例子里面,我們些一個簡單的標記處理器,用于在編譯的時候打印“Hello World!”.我們用編譯器的內部消息機制來打印“hello world”。

首先,我們定義如下HelloWorld標記。

  1. public @interface HelloWorld{  

添加一個Dummy類使用以上的標記

  1. @HelloWorld 
  2. public class Dummy{  

標記處理可能會發生很輪。每一輪處理器只處理特定的一些標記,并且生成的源文件或者類文件,交給下一輪來處理。如果處理器被要求只處理特定的某一輪,那么他也會處理后續的那些次,包括***一輪,就算***一輪沒有可以處理的標記。處理器可能也會去處理被這個工具生成的文件。

后一個方法處理前一輪生成的標記類型,并且返回是否這些標記會聲明。如果返回是True,那么后續的處理器就不會去處理它們。如果返回是false,那么后續處理器會繼續處理它們。一個處理器可能總是返回同樣的邏輯值,或者是根據選項改變結果。為了要寫一個標記處理器,我們用一個子類來繼承AbstractProcessor,并且用SupportedAnnotationTyps 和SupportedSourceVersion標記這個子類。這個子類必須要復寫這兩個方法:

l public synchronized void init(ProcessingEnvironment processingEnv)

l public boolean process(Set<? extends TypeElement> annotations,

RoundEnvironment roundEnv)

這兩個方法都是在標記處理過程中被java編譯器調用的。***個方法用來初始化插件,只被調用一次。而第二個方法每一輪標記處理都會被調用,并且在所有處理都結束后還會調用一次。

我們的簡單的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<? extends TypeElement> annotations,  
  14. RoundEnvironment roundEnv) {  
  15. if (!roundEnv.processingOver()) {  
  16. processingEnv.getMessager().printMessage(  
  17. Diagnostic.Kind.NOTE, "Hello Worlds!");  
  18. }  
  19. return true;  
  20. }  

第八行注冊了HelloWorld的標記處理器。也就是說,當標記出現是,就會有一系列的程序被自動調用。第九行設置了標記所支持的源代碼版本。

第12到15行復寫了初始化方法, 目前為止,我們只是調用父類的方法。

第17到24行復寫了處理方法。這個方法是由一些列被標記的程序元素來調用的。這個方法在每一輪處理時,都會調用,并且在***會多出一輪,用于對空集合的元素的處理。這樣,我們可以由一個簡單的if語句,使得***多出的那一輪什么事情都不做。在其他輪中,我們只打印一個hello world消息。我們不用System.out.print,二十使用編譯器的消息框架來打印一個消息(note類型的)。其他可能的類型是警告(warning)或者錯誤(error)。

這個方法返回true,如果你想要聲明元素已經被處理過了。

要運行這個例子,執行:

javac HelloWorldProcessor.java

javac -processor HelloWorldProcessor *.java

這個應該會輸出:

Note: Hello World!

5. 如何巧妙利用JSR269來重寫AST

在這個例子中,我們深入到編譯器自身的實現細節中去。我們利用JSR269做一些超出它本身的事情—重寫AST。這個處理器會把每一個Assertion語句替換成一個throw語句。也就是說,每當有以下語句出現時

assert cond: detail;

會被替換成:

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

后面的這個語句不會生成assert的字節碼,而是生成一個普通的if語句,帶有一個throw重句。結果就算你的虛擬機沒有激活assertions功能時,assertions的檢查還是會被執行。這個功能對各種庫是非常有用的,因為你寫庫的時候,是沒有辦法控制用戶的VM設置的。

再次,我們還是先繼承AbstractProcessor。然而,這次我們不會針對某一個特殊的標記,而是用“*”這個符號來表示對所有的源代碼都調用處理器。

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

初始化方法如下:

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

我們使用處理環境(ProcessingEnvironment)來獲得對編譯器一些組件的引用。在編譯器里面,在每次調用編譯器時都會有一個處理環境(ProcessingEnvironment)。在編譯器中,我們使用Component.instance(context)來獲得對組件的引用。

我們使用的組件如下:

l Trees – JSR269的一個工具類,用于聯系程序元素和樹節點。比如,對于一個方法元素,我們可以獲得這個元素對應的AST樹節點。

l TreeMaker – 編譯器的內部組件,是用于創建樹節點的工廠類。工廠類里面方法的命名方式跟Javac源代碼里面的方法是統一的。

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

請注意,在第39行,我們把處理環境(ProcessingEnvironment)強制轉換成了編譯器的內部類型。

***,我們把一個計數器初始化成0.這個計數器是用來記錄發生替換的數量。

處理方法如下:

  1. @Override 
  2. 46 public boolean process(Set<? extends TypeElement> annotations,  
  3. RoundEnvironment roundEnv) {  
  4.     if (!roundEnv.processingOver()) {  
  5.        Set<? extends Element> 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.  } 

我們遍歷所有的程序元素,為每一個類都重寫AST。在第51行,我們把JSR269的樹節點轉換成編譯器內部的樹節點。這兩種樹節點的不同之處在于,JSR269節點是停留在方法層的(即方法method是最基本的元素,不會再細分下去),而內部的AST節點,是所有元素(包括方法以下的)都可以訪問的。我們要訪問每一個語句,所以需要訪問到AST的所有節點。

#p#

樹的轉換是通過繼承TreeTranslator來完成的,TreeTranslator本身是繼承自TreeVisitor的。這些類都不是JSR269的一部分。所以,從這里開始,我們所寫的所有代碼都是在編譯器內部工作的。

在第57行,是else部分,用于報告處理過的assertion語句數量。這個語句只有在***一輪處理才會執行。

Inliner這個類實現了AST重寫。Inliner繼承了TreeTranslator,并且是標記處理器的一個內部類。注意,TreeTranslator本身是不會轉換任何節點的。

private class Inliner extends TreeTranslator {

}

為了轉換assertion語句,我們需要復寫默認的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.  } 

正在轉換的節點會被當做參數傳入到方法中。在第67行,轉換的結果,通過賦值給變量TreeTranslator.result而返回。

按照慣例,一個轉換方法應該這樣生成:

l 調用父類的轉換方法,以確保轉換可以被應用到自己點上面去。

l 執行真正的轉換

l 把轉換結果賦值給TreeTranslator.result。結果的類型不一定要和傳進來的參數的類型一樣。相反,只要java編譯器允許,我們可以返回任何類型的節點。這里TreeTranslator本身沒有限制類型,但是如果返回了錯誤的類型,那么就很有在后續過程中產生災難性后果。

我們寫一個私有函數來實現轉換,makeIfThrowException:

  1. private JCStatement makeIfThrowException(JCAssert node) {  
  2. // make: if (!(condition) throw new AssertionError(detail);  
  3. List<JCExpression> args = node.getDetail() == null 
  4. ? List.<JCExpression> 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);  

這個方法傳入一個assertion語句,返回一個if語句。我們可以這樣做,事因為不管是assertion還是if,他們都是語句(statement),所以在java的語法中是等價的。Java中沒有明文規定,禁止用if語句來代替assertion語句。

makeIfThrowException是用于AST重寫的方法。我們使用TreeMaker來創建新的樹節點。如果有這樣的一個表達式:

assert cond:detail;

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

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

在第73到75行,我們考慮到了detail被省略的情況。在76到81行,我們創建了一個AST節點,這個節點的作用是創建AssertionError。在第79行,我們使用Name.Table來把字符串“AssertionError”變成編譯器內部的字符串。在80行,我們再傳入73到75行創建的參數args。第77,78和81行傳入了null值,因為這個節點既沒有外部實例,也沒有類型參數,也不是在匿名類內部。

在第83行,我們對assertion的條件做了一個Not操作。84行,我們創建了一個throw表達式,***,在82到85行,我們把所有的東西都放到了if語句中。

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

  1. public class Pair<A, B> {  
  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. ...  

而List是這樣實現的:

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

并且有許多靜態的方法,可以很方便的創建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)

同樣,非傳統的命名方式也帶來了更漂亮的代碼

不像傳統java中用的代碼:

List list = new List();

list.add(a);

list.add(b);

list.add(c);

而現在只需要寫:

List.of(a, b, c);

#p#

5.1 運行AST重寫

為了展示AST重寫,我們使用:

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

并且執行:

javac ForceAssertions.java

javac -processor ForceAssertions Example.java

就會產生這樣的輸出:

Note: 1 assertions inlined

現在,我們我們我們禁用assertion,再執行例子:

java -disableassertions Example

得到:

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

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

執行:

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

產生結果:

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

可以發現,第9行已經被重寫過了,第3到5行加入了一個默認的構造函數。

5.2 如何把標記處理器注冊成服務

Java提供了一個注冊服務的機制。如果一個標記處理器被注冊成了一個服務,編譯器就會自動的去找到這個標記處理器。注冊的方法是,在classpath中找到一個叫META-INF/services的文件夾,然后放入一個javax.annotation.processing.Processor的文件。文件格式是很明顯的,就是要包含要注冊的標記處理器的完整名稱。每個名字都要占單獨的一行。

5.3 進一步的閱讀

Erni在他的本科畢業設計中描述了一個更復雜的編譯器修改。他不是依賴JSR269,而是直接在編譯過程中的幾個點進行直接修改。

參考

[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

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

B ForceAssertions.java的全部源代碼

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

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

責任編輯:林師授 來源: guoliang的博客
相關推薦

2012-02-29 10:06:14

2009-11-09 10:15:10

2022-01-18 07:40:27

滲透測試黑客

2019-10-11 16:55:42

2019-01-18 17:07:49

2023-06-14 15:20:51

2014-08-13 19:20:56

2025-01-13 17:15:08

2013-08-05 09:16:46

2009-04-09 23:38:20

黑客韓國大賽

2011-09-05 17:11:51

2011-08-31 13:12:39

2018-11-26 09:01:19

2011-09-05 18:39:41

2011-09-05 17:05:03

2010-09-17 08:53:01

2011-08-31 13:41:46

2011-07-08 10:15:51

2011-11-28 15:05:09

2011-08-02 08:59:53

點贊
收藏

51CTO技術棧公眾號

欧美视频久久| 一区二区精品伦理...| 国内精品久久久久影院一蜜桃| 久久精品人人爽| a级片在线观看视频| 丝袜美腿一区| ●精品国产综合乱码久久久久| 国产精品国产三级国产专区53| 久久久黄色大片| 在线中文字幕第一区| 日韩成人在线视频观看| 天堂视频免费看| 免费高潮视频95在线观看网站| 亚洲国产精品激情在线观看| 动漫一区二区在线| 在线免费观看av片| 国产日韩欧美一区| 欧美日本中文字幕| 国产不卡在线观看视频| 欧美挤奶吃奶水xxxxx| 欧美精品亚洲一区二区在线播放| 97超碰国产精品| 欧美日韩xx| 久久久另类综合| 国产精品久久精品国产| 亚洲天堂狠狠干| 美女国产一区| 97视频在线播放| 麻豆亚洲av成人无码久久精品| 欧美日一区二区| 日韩电影网在线| 又黄又爽又色的视频| 国产原创一区| 在线观看91精品国产入口| 欧美 国产 综合| 欧美人与禽性xxxxx杂性| 中文字幕在线播放不卡一区| 欧美一区二区三区电影在线观看 | 久久电影网站| 中文字幕一区二区在线播放| 日本一区视频在线观看| 全色精品综合影院| 91香蕉视频黄| 国产综合av一区二区三区| 精品人妻一区二区三区浪潮在线 | 黄色av电影网站| 精品国产第一国产综合精品| 88在线观看91蜜桃国自产| 嫩草av久久伊人妇女超级a| 亚洲精品日产| 欧美性猛交xxxx黑人| 青青视频在线播放| 日本蜜桃在线观看视频| 欧美日韩国产在线看| 国产精品333| 偷拍中文亚洲欧美动漫| 色94色欧美sute亚洲线路一ni | 日本中文字幕免费观看| 国内精品久久久久久久影视麻豆| 欧美成人剧情片在线观看| 91日韩中文字幕| 国模 一区 二区 三区| 欧美激情综合亚洲一二区| 久久久精品视频在线| 国产一区二区三区四区三区四| 欧美伦理91i| 久久精品久久精品久久| 国产一级久久| 国产精品久久久久久久久久久久| 伊人色综合久久久| 黄色精品一二区| 97久草视频| 欧美视频在线观看一区二区三区| 成年人网站91| 日本一区二区在线| 久久日韩视频| 亚洲一区二区三区中文字幕 | 激情小说中文字幕| 日韩午夜高潮| 国产精品久久久久久亚洲影视| www.久久视频| 国产一区二区毛片| 久久久水蜜桃| 九七久久人人| 亚洲大片精品永久免费| 国产l精品国产亚洲区久久| 成人不卡视频| 欧美成人a在线| 99久久人妻无码精品系列| 日韩电影在线视频| 久久99国产综合精品女同| 日韩欧美成人一区二区三区| 另类小说欧美激情| 国产伦精品一区二区三区| 精品无吗乱吗av国产爱色| 国产精品久久久久桃色tv| 亚洲一区二区三区av无码| 综合在线影院| 日韩免费福利电影在线观看| theav精尽人亡av| 五月精品视频| 欧美性一区二区三区| 国产又黄又大又爽| 久久亚洲综合色| 黄色小视频大全| 欧美成人精品三级网站| 欧美变态凌虐bdsm| 欧美a在线播放| 国产精品毛片在线| 91久久国产综合久久蜜月精品| 男同在线观看| 亚洲福利视频一区二区| 可以看污的网站| 亚洲精品3区| 欧美激情伊人电影| 在线视频免费观看一区| 91在线观看污| 成年女人18级毛片毛片免费| 欧美激情啪啪| 伊人久久久久久久久久久久久| 日韩av男人天堂| 国产一区二区三区在线观看免费视频| 日本不卡高清视频一区| av影视在线看| 日韩精品一区二区三区视频播放| 国产精品久久久久久久av| 一级成人国产| 成人在线观看av| 18网站在线观看| 6080yy午夜一二三区久久| 欧美性受xxxx黑人| 老牛影视一区二区三区| 精品一区二区三区日本| 草草影院在线| 精品粉嫩超白一线天av| 丝袜美腿小色网| 狠狠色丁香婷婷综合久久片| 亚洲五月六月| 日韩大陆av| 深夜福利91大全| 欧美国产一级片| 久久九九全国免费| 88av.com| 成人精品影院| 国产在线一区二区三区| 91大神在线网站| 欧美三级电影在线看| 成人做爰69片免网站| 中文字幕在线观看2018| 西野翔中文久久精品国产| 久久免费视频在线观看| 亚洲AV无码一区二区三区性| 一区二区三区在线观看视频 | 9i看片成人免费看片| 91啪亚洲精品| 凹凸日日摸日日碰夜夜爽1| 要久久电视剧全集免费| 日韩av日韩在线观看| 国产系列在线观看| 欧美婷婷六月丁香综合色| 日本不卡一区视频| 国内精品写真在线观看| 日本久久久网站| 老司机精品视频在线播放| 91a在线视频| 国产午夜精品一区理论片| 欧美性色黄大片| 蜜臀av午夜精品久久| 国产麻豆视频一区二区| 亚洲色成人www永久在线观看| 美女一区2区| 国产精品海角社区在线观看| 五月天婷婷在线视频| 91精品欧美一区二区三区综合在| 午夜少妇久久久久久久久| 成人激情免费网站| 欧美日韩在线免费播放| 久久福利综合| 99在线视频免费观看| 亚洲色图官网| 久久久精品999| 色窝窝无码一区二区三区| 日韩欧美成人免费视频| 日本黄色免费片| 国内精品伊人久久久久av影院| 精品二区三区线观看| 视频一区国产精品| 精品国产欧美| 51久久精品夜色国产麻豆| 91伦理视频在线观看| 日韩欧美国产综合在线一区二区三区| 日本三级小视频| 国产精品久久久久久久岛一牛影视 | 无码久久精品国产亚洲av影片| 99国产精品99久久久久久| 九九热在线免费| 国产综合欧美| 亚洲一区免费看| 久久99国产精品久久99大师| 国产精品扒开腿爽爽爽视频| 性爱视频在线播放| 亚洲欧美中文日韩在线v日本| 国产精品视频久久久久久| 黑人巨大精品欧美一区二区| 日本一区二区视频在线播放| 成人午夜av电影| 中文字幕免费高清在线| 亚洲激情视频| 400部精品国偷自产在线观看| 免费av一区二区三区四区| 成人羞羞视频免费| 亚洲精品tv| 国产精品对白刺激| 成人免费观看在线观看| 欧美日韩成人免费| 91大神在线网站| 亚洲免费人成在线视频观看| 亚洲va欧美va| 7799精品视频| 日韩免费高清在线| 亚洲精品一级片| 91成人免费电影| 日韩少妇裸体做爰视频| 亚洲人123区| 国产欧美一区二区三区在线观看视频| av在线综合网| xxxxwww一片| 国产精品一区二区在线观看网站| 超碰在线97免费| 玖玖玖国产精品| 99爱视频在线| 99综合在线| 妺妺窝人体色777777| 欧美91视频| 国产一级黄色录像片| 欧美高清视频手机在在线| 视频一区视频二区视频三区视频四区国产 | 久热精品在线播放| 老妇喷水一区二区三区| 欧美一级黄色片视频| 久久综合伊人| 国产又黄又猛视频| 丝袜a∨在线一区二区三区不卡| 成人免费网站入口| 亚洲午夜91| 和岳每晚弄的高潮嗷嗷叫视频| 欧美精品入口| www.国产在线视频| 在线精品一区| 欧美亚洲日本一区二区三区| 99热精品在线| 成人一级片网站| 日韩高清一区在线| 中文字幕第88页| 国模少妇一区二区三区| 永久看看免费大片| 成人性视频免费网站| 国产视频精品视频| 91蜜桃传媒精品久久久一区二区| 李宗瑞91在线正在播放| 欧美激情一区在线| 999精品视频在线观看播放 | 无码人妻精品一区二区蜜桃色欲| 色猫猫国产区一区二在线视频| 欧美人一级淫片a免费播放| 欧美三级视频在线观看 | 日韩国产高清视频在线| 久草福利在线| 丝袜一区二区三区| 中文字幕中文字幕在线十八区| 欧美激情高清视频| 超级碰碰久久| 91亚洲国产成人久久精品网站| 亚洲啊v在线免费视频| 麻豆精品蜜桃一区二区三区| 成人羞羞网站入口| 精品一区二区三区无码视频| 国产一区二区三区久久| 中文字幕亚洲乱码| 成人午夜在线免费| 手机毛片在线观看| 亚洲一区自拍偷拍| 久久久久亚洲视频| 亚洲精品一区二区在线观看| 成人动漫在线免费观看| 欧美激情videos| 99久久婷婷国产综合精品首页| 亚洲一区二区三区乱码aⅴ蜜桃女| 琪琪久久久久日韩精品| 一本—道久久a久久精品蜜桃| 日韩一区二区久久| 三级一区二区三区| 91丨porny丨中文| 国产精品老熟女一区二区| 在线亚洲一区二区| 欧美一级一区二区三区| 中文在线不卡视频| 爱搞国产精品| 97超级在线观看免费高清完整版电视剧| 性人久久久久| 国产精品视频网站在线观看| 日韩和欧美一区二区三区| 国产情侣久久久久aⅴ免费| 国产精品丝袜91| 久久国产视频精品| 日韩欧美高清一区| 欧美成人二区| 国产精品白嫩美女在线观看| 风间由美一区二区av101| 好色先生视频污| 免费视频一区二区| jizz欧美性20| 亚洲图片一区二区| 国产男女裸体做爰爽爽| 国产一区二区三区精品久久久 | 99热国产在线中文| 国产精品夜间视频香蕉| 最新国产精品视频| heyzo亚洲| 丁香一区二区三区| 精品国产视频在线观看| 欧美日韩一区三区四区| 蜜桃视频在线观看视频| 5566日本婷婷色中文字幕97| 精品三级av在线导航| 欧美日韩视频免费| 国产尤物一区二区| 永久免费看片直接| 欧美日韩你懂得| 91在线看片| 国产精品香蕉国产| 成人av二区| 超碰在线公开97| 国产精品视频一二三| japanese国产在线观看| 国产一区二区成人| 日本一区二区三区视频在线| 欧洲久久久久久| 首页国产欧美日韩丝袜| 亚洲av无码一区二区二三区| 五月婷婷激情综合| 天堂√在线中文官网在线| 91美女片黄在线| 国产视频精品网| 久久精品影视| 久久精品一卡二卡| 亚洲欧美韩国综合色| 国产偷拍一区二区| 九九久久久久99精品| 91精品入口| 福利视频一区二区三区四区| 不卡一区在线观看| 亚洲精品午夜国产va久久成人| 亚洲精品久久在线| 中文字幕这里只有精品| 欧美下载看逼逼| 热久久一区二区| 婷婷激情四射网| 日韩欧美在线网站| √天堂8资源中文在线| 精品国产一区二区三区麻豆小说 | 一区二区精品| 中文字幕5566| 欧美日韩精品欧美日韩精品| 超碰在线观看免费| 国产精品视频在线免费观看| 国产欧美日韩一区二区三区在线| 亚洲成人av免费在线观看| 在线免费精品视频| 老司机免费在线视频| 国产高清自拍一区| 久久激情网站| 久久爱一区二区| 欧美大片在线观看一区| 欧亚在线中文字幕免费| 日韩欧美激情一区二区| 国产一区福利在线| 日韩手机在线观看| 在线a欧美视频| 欧美视频三区| 欧美韩国日本在线| 国产精品福利电影一区二区三区四区| xxxx18国产| 国产91精品最新在线播放| 亚洲精品888| 美女久久久久久久久久| 91 com成人网| 中文字幕在线直播| 黄色www在线观看| 91亚洲午夜精品久久久久久| 国产尤物在线观看| 7777kkkk成人观看| 91精品啪在线观看国产81旧版| 国产黑丝一区二区| 欧美精品色综合| 日本在线播放一二三区| 韩国黄色一级大片| 国产欧美日韩亚州综合| 好男人www在线视频| 国产综合视频在线观看|