<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Java 抽象語法術(shù) JCTree 介紹和使用

          共 7615字,需瀏覽 16分鐘

           ·

          2021-11-16 00:07

          不點藍(lán)字關(guān)注,我們哪來故事?




          正文如下

          作者:不學(xué)無數(shù)的程序員

          鏈接:https://www.jianshu.com/p/6d3c208faca8


          在網(wǎng)上關(guān)于如何修改Java的抽象語法樹的相關(guān)API文檔并不多,于是本篇記錄一下相關(guān)的知識點,以便隨后查閱。


          # JCTree的介紹


          JCTree是語法樹元素的基類,包含一個重要的字段pos,該字段用于指明當(dāng)前語法樹節(jié)點(JCTree)在語法樹中的位置,因此我們不能直接用new關(guān)鍵字來創(chuàng)建語法樹節(jié)點,即使創(chuàng)建了也沒有意義。此外,結(jié)合訪問者模式,將數(shù)據(jù)結(jié)構(gòu)與數(shù)據(jù)的處理進(jìn)行解耦,部分源碼如下:

          public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
          public int pos = -1;
          ...
          public abstract void accept(JCTree.Visitor visitor);
          ...}

          我們可以看到JCTree是一個抽象類,這里重點介紹幾個JCTree的子類


          1. JCStatement:聲明語法樹節(jié)點,常見的子類如下

            • JCBlock:語句塊語法樹節(jié)點

            • JCReturn:return語句語法樹節(jié)點

            • JCClassDecl:類定義語法樹節(jié)點

            • JCVariableDecl:字段/變量定義語法樹節(jié)點

          2. JCMethodDecl:方法定義語法樹節(jié)點

          3. JCModifiers:訪問標(biāo)志語法樹節(jié)點

          4. JCExpression:表達(dá)式語法樹節(jié)點,常見的子類如下

            • JCAssign:賦值語句語法樹節(jié)點

            • JCIdent:標(biāo)識符語法樹節(jié)點,可以是變量,類型,關(guān)鍵字等等


          # TreeMaker介紹


          TreeMaker用于創(chuàng)建一系列的語法樹節(jié)點,我們上面說了創(chuàng)建JCTree不能直接使用new關(guān)鍵字來創(chuàng)建,所以Java為我們提供了一個工具,就是TreeMaker,它會在創(chuàng)建時為我們創(chuàng)建的JCTree對象設(shè)置pos字段,所以必須使用上下文相關(guān)的TreeMaker對象來創(chuàng)建語法樹節(jié)點。


          具體的API介紹可以參照,TreeMakerAPI,接下來著重介紹一下常用的幾個方法。


          # TreeMaker.Modifiers


          TreeMaker.Modifiers方法用于創(chuàng)建訪問標(biāo)志語法樹節(jié)點(JCModifiers),源碼如下

          public JCModifiers Modifiers(long flags) {    return Modifiers(flags, List.< JCAnnotation >nil());}
          public JCModifiers Modifiers(long flags, List annotations) { JCModifiers tree = new JCModifiers(flags, annotations); boolean noFlags = (flags & (Flags.ModifierFlags | Flags.ANNOTATION)) == 0; tree.pos = (noFlags && annotations.isEmpty()) ? Position.NOPOS : pos; return tree;}

          1. flags:訪問標(biāo)志

          2. annotations:注解列表


          其中flags可以使用枚舉類com.sun.tools.javac.code.Flags來表示,例如我們可以這樣用,就生成了下面的訪問標(biāo)志了。

          treeMaker.Modifiers(Flags.PUBLIC + Flags.STATIC + Flags.FINAL);
          public static final

          # TreeMaker.ClassDef


          TreeMaker.ClassDef用于創(chuàng)建類定義語法樹節(jié)點(JCClassDecl),源碼如下:

          public JCClassDecl ClassDef(JCModifiers mods,    Name name,    List typarams,    JCExpression extending,    List implementing,    List defs) {        JCClassDecl tree = new JCClassDecl(mods,                                     name,                                     typarams,                                     extending,                                     implementing,                                     defs,                                     null);        tree.pos = pos;        return tree;}


          1. mods:訪問標(biāo)志,可以通過TreeMaker.Modifiers來創(chuàng)建

          2. name:類名

          3. typarams:泛型參數(shù)列表

          4. extending:父類

          5. implementing:實現(xiàn)的接口

          6. defs:類定義的詳細(xì)語句,包括字段、方法的定義等等


          # TreeMaker.MethodDef


          TreeMaker.MethodDef用于創(chuàng)建方法定義語法樹節(jié)點(JCMethodDecl),源碼如下

          public JCMethodDecl MethodDef(JCModifiers mods,    Name name,    JCExpression restype,    List typarams,    List params,    List thrown,    JCBlock body,    JCExpression defaultValue) {        JCMethodDecl tree = new JCMethodDecl(mods,                                       name,                                       restype,                                       typarams,                                       params,                                       thrown,                                       body,                                       defaultValue,                                       null);        tree.pos = pos;        return tree;}
          public JCMethodDecl MethodDef(MethodSymbol m, Type mtype, JCBlock body) { return (JCMethodDecl) new JCMethodDecl( Modifiers(m.flags(), Annotations(m.getAnnotationMirrors())), m.name, Type(mtype.getReturnType()), TypeParams(mtype.getTypeArguments()), Params(mtype.getParameterTypes(), m), Types(mtype.getThrownTypes()), body, null, m).setPos(pos).setType(mtype);}

          1. mods:訪問標(biāo)志

          2. name:方法名

          3. restype:返回類型

          4. typarams:泛型參數(shù)列表

          5. params:參數(shù)列表

          6. thrown:異常聲明列表

          7. body:方法體

          8. defaultValue:默認(rèn)方法(可能是interface中的哪個default)

          9. m:方法符號

          10. mtype:方法類型。包含多種類型,泛型參數(shù)類型、方法參數(shù)類型、異常參數(shù)類型、返回參數(shù)類型。

          返回類型restype填寫null或者treeMaker.TypeIdent(TypeTag.VOID)都代表返回void類型


          # TreeMaker.VarDef


          TreeMaker.VarDef用于創(chuàng)建字段/變量定義語法樹節(jié)點(JCVariableDecl),源碼如下

          public JCVariableDecl VarDef(JCModifiers mods,    Name name,    JCExpression vartype,    JCExpression init) {        JCVariableDecl tree = new JCVariableDecl(mods, name, vartype, init, null);        tree.pos = pos;        return tree;}
          public JCVariableDecl VarDef(VarSymbol v, JCExpression init) { return (JCVariableDecl) new JCVariableDecl( Modifiers(v.flags(), Annotations(v.getAnnotationMirrors())), v.name, Type(v.type), init, v).setPos(pos).setType(v.type);}

          1. mods:訪問標(biāo)志

          2. name:參數(shù)名稱

          3. vartype:類型

          4. init:初始化語句

          5. v:變量符號


          # TreeMaker.Ident


          TreeMaker.Ident用于創(chuàng)建標(biāo)識符語法樹節(jié)點(JCIdent),源碼如下

          public JCIdent Ident(Name name) {        JCIdent tree = new JCIdent(name, null);        tree.pos = pos;        return tree;}
          public JCIdent Ident(Symbol sym) { return (JCIdent)new JCIdent((sym.name != names.empty) ? sym.name : sym.flatName(), sym) .setPos(pos) .setType(sym.type);}
          public JCExpression Ident(JCVariableDecl param) { return Ident(param.sym);}

          # TreeMaker.Return


          TreeMaker.Return用于創(chuàng)建return語句(JCReturn),源碼如下

          public JCReturn Return(JCExpression expr) {        JCReturn tree = new JCReturn(expr);        tree.pos = pos;        return tree;}


          # TreeMaker.Select


          TreeMaker.Select用于創(chuàng)建域訪問/方法訪問(這里的方法訪問只是取到名字,方法的調(diào)用需要用TreeMaker.Apply)語法樹節(jié)點(JCFieldAccess),源碼如下

          public JCFieldAccess Select(JCExpression selected,    Name selector) {        JCFieldAccess tree = new JCFieldAccess(selected, selector, null);        tree.pos = pos;        return tree;}
          public JCExpression Select(JCExpression base, Symbol sym) { return new JCFieldAccess(base, sym.name, sym).setPos(pos).setType(sym.type);}

          1. selected:.運算符左邊的表達(dá)式

          2. selector:.運算符右邊的表達(dá)式


          下面給出一個例子,一語句生成的Java語句就是二語句

          一. TreeMaker.Select(treeMaker.Ident(names.fromString("this")), names.fromString("name"));
          二. this.name

          # TreeMaker.NewClass


          TreeMaker.NewClass用于創(chuàng)建new語句語法樹節(jié)點(JCNewClass),源碼如下:

          public JCNewClass NewClass(JCExpression encl,    List typeargs,    JCExpression clazz,    List args,    JCClassDecl def) {        JCNewClass tree = new JCNewClass(encl, typeargs, clazz, args, def);        tree.pos = pos;        return tree;}

          1. encl:不太明白此參數(shù)的含義,我看很多例子中此參數(shù)都設(shè)置為null

          2. typeargs:參數(shù)類型列表

          3. clazz:待創(chuàng)建對象的類型

          4. args:參數(shù)列表

          5. def:類定義


          # TreeMaker.Apply


          TreeMaker.Apply用于創(chuàng)建方法調(diào)用語法樹節(jié)點(JCMethodInvocation),源碼如下:

          public JCMethodInvocation Apply(List typeargs,    JCExpression fn,    List args) {        JCMethodInvocation tree = new JCMethodInvocation(typeargs, fn, args);        tree.pos = pos;        return tree;}

          1. typeargs:參數(shù)類型列表

          2. fn:調(diào)用語句

          3. args:參數(shù)列表


          # TreeMaker.Assign


          TreeMaker.Assign用戶創(chuàng)建賦值語句語法樹節(jié)點(JCAssign),源碼如下:

          ublic JCAssign Assign(JCExpression lhs,    JCExpression rhs) {        JCAssign tree = new JCAssign(lhs, rhs);        tree.pos = pos;        return tree;}

          1. lhs:賦值語句左邊表達(dá)式

          2. rhs:賦值語句右邊表達(dá)式


          # TreeMaker.Exec


          TreeMaker.Exec用于創(chuàng)建可執(zhí)行語句語法樹節(jié)點(JCExpressionStatement),源碼如下:

          public JCExpressionStatement Exec(JCExpression expr) {        JCExpressionStatement tree = new JCExpressionStatement(expr);        tree.pos = pos;        return tree;}

          TreeMaker.Apply以及TreeMaker.Assign就需要外面包一層TreeMaker.Exec來獲得一個JCExpressionStatement


          # TreeMaker.Block


          TreeMaker.Block用于創(chuàng)建組合語句的語法樹節(jié)點(JCBlock),源碼如下:

          public JCBlock Block(long flags,    List stats) {        JCBlock tree = new JCBlock(flags, stats);        tree.pos = pos;        return tree;}

          1. flags:訪問標(biāo)志

          2. stats:語句列表


          # com.sun.tools.javac.util.List介紹


          在我們操作抽象語法樹的時候,有時會涉及到關(guān)于List的操作,但是這個List不是我們經(jīng)常使用的java.util.List而是com.sun.tools.javac.util.List,這個List比較奇怪,是一個鏈?zhǔn)降慕Y(jié)構(gòu),有頭結(jié)點和尾節(jié)點,但是只有尾節(jié)點是一個List,這里作為了解就行了。

          public class List<A> extends AbstractCollection<A> implements java.util.List<A> {    public A head;    public List tail;    private static final List> EMPTY_LIST = new List((Object)null, (List)null) {        public List setTail(List var1) {            throw new UnsupportedOperationException();        }
          public boolean isEmpty() { return true; } };
          List(A head, List tail) { this.tail = tail; this.head = head; }
          public static
          List nil() { return EMPTY_LIST; }
          public List
          prepend(A var1) { return new List(var1, this); }
          public List
          append(A var1) { return of(var1).prependList(this); }
          public static
          List of(A var0) { return new List(var0, nil()); }
          public static
          List of(A var0, A var1) { return new List(var0, of(var1)); }
          public static
          List of(A var0, A var1, A var2) { return new List(var0, of(var1, var2)); }
          public static
          List of(A var0, A var1, A var2, A... var3) { return new List(var0, new List(var1, new List(var2, from(var3)))); }
          ...}

          # com.sun.tools.javac.util.ListBuffer


          由于com.sun.tools.javac.util.List使用起來不方便,所以又在其上面封裝了一層,這個封裝類是ListBuffer,此類的操作和我們平時經(jīng)常使用的java.util.List用法非常類似。

          public class ListBuffer<A> extends AbstractQueue<A> {
          public static ListBuffer of(T x) { ListBuffer lb = new ListBuffer(); lb.add(x); return lb; }
          /** The list of elements of this buffer. */ private List
          elems;
          /** A pointer pointing to the last element of 'elems' containing data, * or null if the list is empty. */ private List
          last;
          /** The number of element in this buffer. */ private int count;
          /** Has a list been created from this buffer yet? */ private boolean shared;
          /** Create a new initially empty list buffer. */ public ListBuffer() { clear(); }
          /** Append an element to buffer. */ public ListBuffer
          append(A x) { x.getClass(); // null check if (shared) copy(); List newLast = List.of(x); if (last != null) { last.tail = newLast; last = newLast; } else { elems = last = newLast; } count++; return this; } ........}

          # com.sun.tools.javac.util.Names介紹


          這個是為我們創(chuàng)建名稱的一個工具類,無論是類、方法、參數(shù)的名稱都需要通過此類來創(chuàng)建。它里面經(jīng)常被使用到的一個方法就是fromString(),一般使用方法如下所示。

          Names names  = new Names()names. fromString("setName");

          # 實戰(zhàn)演練


          上面我們大概了解了如何操作抽象語法樹,接下來我們就來寫幾個真實的案例加深理解。


          # 變量相關(guān)


          在類中我們經(jīng)常操作的參數(shù)就是變量,那么如何使用抽象語法樹的特性為我們操作變量呢?接下來我們就將一些對于變量的一些操作。


          # 生成變量


          例如生成private String age;這樣一個變量,借用我們上面講的VarDef方法



          # 對變量賦值


          例如我們想生成private String name = "BuXueWuShu",還是利用VarDef方法

          // private String name = "BuXueWuShu"treeMaker.VarDef(treeMaker.Modifiers(Flags.PRIVATE),names.fromString("name"),treeMaker.Ident(names.fromString("String")),treeMaker.Literal("BuXueWuShu"))

          # 兩個字面量相加


          例如我們生成String add = "a" + "b";,借用我們上面講的Exec方法和Assign方法

          // add = "a"+"b"treeMaker.Exec(treeMaker.Assign(treeMaker.Ident(names.fromString("add")),treeMaker.Binary(JCTree.Tag.PLUS,treeMaker.Literal("a"),treeMaker.Literal("b"))))

          # +=語法


          例如我們想生成add += "test",則和上面字面量差不多。

          // add+="test"treeMaker.Exec(treeMaker.Assignop(JCTree.Tag.PLUS_ASG,?treeMaker.Ident(names.fromString("add")),?treeMaker.Literal("test")))

          # ++語法

          例如想生成++i

          treeMaker.Exec(treeMaker.Unary(JCTree.Tag.PREINC,treeMaker.Ident(names.fromString("i"))))

          # 方法相關(guān)


          我們對于變量進(jìn)行了操作,那么基本上都是要生成方法的,那么如何對方法進(jìn)行生成和操作呢?我們接下來演示一下關(guān)于方法相關(guān)的操作方法。


          # 無參無返回值


          我們可以利用上面講到的MethodDef方法進(jìn)行生成

          /*    無參無返回值的方法生成    public void test(){
          } */// 定義方法體ListBuffer testStatement = new ListBuffer<>();JCTree.JCBlock testBody = treeMaker.Block(0, testStatement.toList()); JCTree.JCMethodDecl test = treeMaker.MethodDef( treeMaker.Modifiers(Flags.PUBLIC), // 方法限定值 names.fromString("test"), // 方法名 treeMaker.Type(new Type.JCVoidType()), // 返回類型 com.sun.tools.javac.util.List.nil(), com.sun.tools.javac.util.List.nil(), com.sun.tools.javac.util.List.nil(), testBody, // 方法體 null);

          # 有參無返回值


          我們可以利用上面講到的MethodDef方法進(jìn)行生成

          /*    無參無返回值的方法生成    public void test2(String name){        name = "xxxx";    } */ListBuffer testStatement2 = new ListBuffer<>();testStatement2.append(treeMaker.Exec(treeMaker.Assign(treeMaker.Ident(names.fromString("name")),treeMaker.Literal("xxxx"))));JCTree.JCBlock testBody2 = treeMaker.Block(0, testStatement2.toList());
          // 生成入?yún)?/span>JCTree.JCVariableDecl param = treeMaker.VarDef(treeMaker.Modifiers(Flags.PARAMETER), names.fromString("name"),treeMaker.Ident(names.fromString("String")), null);com.sun.tools.javac.util.List parameters = com.sun.tools.javac.util.List.of(param);
          JCTree.JCMethodDecl test2 = treeMaker.MethodDef( treeMaker.Modifiers(Flags.PUBLIC), // 方法限定值 names.fromString("test2"), // 方法名 treeMaker.Type(new Type.JCVoidType()), // 返回類型 com.sun.tools.javac.util.List.nil(), parameters, // 入?yún)?/span> com.sun.tools.javac.util.List.nil(), testBody2, null);

          # 有參有返回值

           /*    有參有返回值    public String test3(String name){       return name;    } */
          ListBuffer testStatement3 = new ListBuffer<>();testStatement3.append(treeMaker.Return(treeMaker.Ident(names.fromString("name"))));JCTree.JCBlock testBody3 = treeMaker.Block(0, testStatement3.toList());
          // 生成入?yún)?/span>JCTree.JCVariableDecl param3 = treeMaker.VarDef(treeMaker.Modifiers(Flags.PARAMETER), names.fromString("name"),treeMaker.Ident(names.fromString("String")), null);com.sun.tools.javac.util.List parameters3 = com.sun.tools.javac.util.List.of(param3);
          JCTree.JCMethodDecl test3 = treeMaker.MethodDef( treeMaker.Modifiers(Flags.PUBLIC), // 方法限定值 names.fromString("test4"), // 方法名 treeMaker.Ident(names.fromString("String")), // 返回類型 com.sun.tools.javac.util.List.nil(), parameters3, // 入?yún)?/span> com.sun.tools.javac.util.List.nil(), testBody3, null);

          # 特殊的


          我們學(xué)完了如何進(jìn)行定義參數(shù),如何進(jìn)行定義方法,其實還有好多語句需要學(xué)習(xí),例如如何生成new語句,如何生成方法調(diào)用的語句,如何生成if語句。j接下來我們就學(xué)習(xí)一些比較特殊的語法。


          # new一個對象

          // 創(chuàng)建一個new語句 CombatJCTreeMain combatJCTreeMain = new CombatJCTreeMain();JCTree.JCNewClass combatJCTreeMain = treeMaker.NewClass(        null,        com.sun.tools.javac.util.List.nil(),        treeMaker.Ident(names.fromString("CombatJCTreeMain")),        com.sun.tools.javac.util.List.nil(),        null);JCTree.JCVariableDecl jcVariableDecl1 = treeMaker.VarDef(        treeMaker.Modifiers(Flags.PARAMETER),        names.fromString("combatJCTreeMain"),        treeMaker.Ident(names.fromString("CombatJCTreeMain")),        combatJCTreeMain);

          # 方法調(diào)用(無參)

          JCTree.JCExpressionStatement exec = treeMaker.Exec(        treeMaker.Apply(                com.sun.tools.javac.util.List.nil(),                treeMaker.Select(                        treeMaker.Ident(names.fromString("combatJCTreeMain")), // . 左邊的內(nèi)容                        names.fromString("test") // . 右邊的內(nèi)容                ),                com.sun.tools.javac.util.List.nil()        ));

          # 方法調(diào)用(有參)

          // 創(chuàng)建一個方法調(diào)用 combatJCTreeMain.test2("hello world!");JCTree.JCExpressionStatement exec2 = treeMaker.Exec(        treeMaker.Apply(                com.sun.tools.javac.util.List.nil(),                treeMaker.Select(                        treeMaker.Ident(names.fromString("combatJCTreeMain")), // . 左邊的內(nèi)容                        names.fromString("test2") // . 右邊的內(nèi)容                ),                com.sun.tools.javac.util.List.of(treeMaker.Literal("hello world!")) // 方法中的內(nèi)容        ));

          # if語句

          /*    創(chuàng)建一個if語句    if("BuXueWuShu".equals(name)){        add = "a" + "b";    }else{        add += "test";    } */// "BuXueWuShu".equals(name)JCTree.JCMethodInvocation apply = treeMaker.Apply(        com.sun.tools.javac.util.List.nil(),        treeMaker.Select(                treeMaker.Literal("BuXueWuShu"), // . 左邊的內(nèi)容                names.fromString("equals") // . 右邊的內(nèi)容        ),        com.sun.tools.javac.util.List.of(treeMaker.Ident(names.fromString("name"))));//  add = "a" + "b"JCTree.JCExpressionStatement exec3 = treeMaker.Exec(treeMaker.Assign(treeMaker.Ident(names.fromString("add")), treeMaker.Binary(JCTree.Tag.PLUS, treeMaker.Literal("a"), treeMaker.Literal("b"))));//  add += "test"JCTree.JCExpressionStatement exec1 = treeMaker.Exec(treeMaker.Assignop(JCTree.Tag.PLUS_ASG, treeMaker.Ident(names.fromString("add")), treeMaker.Literal("test")));
          JCTree.JCIf anIf = treeMaker.If( apply, // if語句里面的判斷語句 exec3, // 條件成立的語句 exec1 // 條件不成立的語句);

          源碼地址:https://github.com/modouxiansheng/Doraemon


          # 總結(jié)


          紙上得來終覺淺,絕知此事要躬行。希望大家看完此篇文章能夠自己在本機上自己試驗一下。自己設(shè)置幾個參數(shù),自己學(xué)的Lombok學(xué)著生成一下get、set方法,雖然本篇知識在日常開發(fā)中基本上不會用到,但是萬一用到了這些知識那么別人不會而你會,差距其實就慢慢的給拉開了。本篇涉及到的所有代碼都在github上面有,拉下來以后全局搜CombatJCTreeProcessor類就可以看到了。


          往期推薦

          一女程序員被判 9 個月:因薪酬等問題離職,rm -f ?刪庫,癱瘓 6 個小時

          比Visio快10倍的畫圖工具來了!趕緊整上~

          Spring Boot 異步調(diào)用的示例

          IDEA Live Templates 用法

          打日志的一些手法和習(xí)慣


          -END-

          ↑ 點擊上方關(guān)注我公號??


          我是 泥瓦匠,堅持分享編程,算法,Java 等干貨教程


          一枚醫(yī)科大本科生,開源小作者,半吊子創(chuàng)業(yè)愛好者...

          半吊子的自己在試錯,不知道以后會干什么,但享受現(xiàn)在的試錯,試錯給我驚訝的生活


          喜歡公號的互動分享,感謝關(guān)注,路上遇見了你,同一小段時間之路,相伴 ~



          長按識別,加我微信

          瀏覽 61
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                    <th id="afajh"><progress id="afajh"></progress></th>
                    国产精品久久久久久久久久久免费 | 久久久久久久久久久久久久久久久久久久 | 奇米影视狠狠操 | 国产激情久 | 麻豆一级特黄A片视频 |