AST 変換 Tips
ClassNode
生成時は ClassHelper
を使おう
コンストラクタや static なメソッドを呼び出す式に対応する AST を作ろうとすると、クラスを表す ClassNode
オブジェクトが必要になりますが、このときにコンストラクタを呼び出すのは推奨されていないようです。代わりに、ClassHelper
クラスの make
メソッドを用いましょう。
例えば、String
クラスを表す ClassHelper
メソッドを用いるときには以下のようにします。
ClassNode type = ClassHelper.make(String)
与えられたクラス名から ClassNode
オブジェクトを得たいときは、以下のようになります。
String typeName = "java.lang.String"
Class clazz = Class.forName(typeName)
ClassNode type = ClassHelper.make(clazz)
ちなみに、make
メソッドは String
を受け取るものと Class
を受け取るものがありますが、String
を受け取る方を用いるとエラーが発生することがあったので、Class
を受け取る方を使ったほうが無難だと思います。
配列を表す ClassNode
ClassNode
クラスの makeArray
メソッドを使うと、そのクラスの配列を表す ClassNode
オブジェクトが生成できます。便利です。
逆に、配列を表す ClassNode
からその配列の成分のクラスを取得したい場合は、getComponentType
メソッドが使えます。例えば、String[]
を表す ClassNode
に getComponentType
メソッドを呼ぶと、String
を表す ClassNode
が返されます。
なお、多次元配列に対して getComponentType
を呼ぶと次元が 1 つ落ちたものが返ってきます。例えば、String[][]
を表す ClassNode
に対しては String[]
を表す ClassNode
が返ってきます。一気に配列でないクラスを取得したい場合は、以下のようにすると良いでしょう。
ClassNode componentType = type
while (componentType.isArray()) {
componentType = componentType.getComponentType()
}
Variable
の accessedVariable
プロパティ
変数を表現する AST は Variable
クラスで管理されます。ソースコード中に現れる変数は全て Variable
オブジェクトになります。このとき、コード上の違う場所に書かれた変数は異なる Variable
オブジェクトになります (lineNumber
などが別々なので当然と言えば当然ですが)。一方、Variable
クラスは accessedVariable
というプロパティ (VariableExpression
型) をもっているのですが、これは同じ変数を表していれば同一のオブジェクトになります。
例えば、以下のようなコードを考えてみます。
if (true) {
int a = 2 // (1)
a += 3 // (2)
a = 7 // (3)
} else {
int a = 9 // (4)
a += 8 // (5)
}
「a」という名前の変数が 5 ヶ所現れていますが、これらは AST に変換されると Variable
オブジェクトとしては別物です。ここで、(1), (2), (3) の a
は同じ変数を表していますが、これとは別のスコープにある (4), (5) とは名前は同じでも別の変数です。したがって、各 Variable
オブジェクトの accessedVariable
プロパティを見てみると、(1), (2), (3) に対応するものは全て同一 (参照が同じ) の VariableExpression
オブジェクトになっていて、(4), (5) に対応するものとは別の (参照が異なる) オブジェクトになっています。
変数の値を操作する AST を作る場合は、この辺にも注意する必要がありそうですね。
このシリーズ
- Groovy で AST 変換 I: AST 変換を作ってみよう
- Groovy で AST 変換 II: GroovyConsole の使い方
- Groovy で AST 変換 III: Tips など