irenka-overview

その2。

irenkaは、コンパイルタイム、ランタイム(クラスロード)などに、その読み込み処理に先立ってプログラム構造を調査し、必要であれば生成/ロードされるクラスを書き換えることを最初の目標とする。

例として、下記のようなクラスを考える。

@Hoge(Foo.class)
public class Bar {
  @Foo
  public void method() {
    // ..
  }
}

これを下記のように表せば、構造を破壊せずに同じことが書ける。

annotation ?hoge
  annotates Bar
  name Hoge
  value = Foo

annotation ?foo
  name Foo
  annotates method()

class ?bar
  name Bar
  extends java.lang.Object
  annotated Hoge
  declares method()

method ?method
  name method
  params ()
  type void
  declared in Bar
  annotated Foo

読み方は「 ?」 で、 という型の ? という変数を宣言し、
その後に annotates などで、その変数の制約を付加していく感じ。

上記では重複した情報が多すぎるため、余計な情報を少しだけ落とす。

annotation ?hoge
  annotates ?bar
  name Hoge
  value = Foo

annotation ?foo
  name Foo
  annotates ?method

class ?bar
  name Bar
  declares ?method

method ?method
  name method()

この時点では、最初に挙げたクラス Bar をそのまま表す記述である。
これを、次のようなテンプレートから生成される、全てのクラスに対してマッチできるようにしてみる。

@Hoge(${annotation}.class)
public class ${clazz} {
#foreach($method in $class.getDeclaredMethod())
  @${annotation}
  ${method} {
    // ..
  }
#end
}

すると、下記のようになる。

annotation ?
  annotates ?clazz
  name Hoge
  value = ?annotation

annotation ?annotation
  annotates ?method

class ?clazz
  declares ?method

このように、テンプレートより直感的(?)に、様々なクラスの構造を表現できるようになる。
コンパイルやクラスロードをする際に何らかのクラスを読み込んだ場合、読み込んだクラスの構造は記述された構造と比較される。比較をすると、? で表される変数に何らかの値が束縛されることになる。

下記を例に挙げると、

@Hoge(Foo.class)
public class Bar {
  @Foo
  public void method() {
    // ..
  }
}

先ほどの構造で宣言された変数は、次の値に束縛されることになる。

  • ?clazz = Bar
  • ?method = public void Bar.method()
  • ?anotation = @Foo (annotates Bar.method())

この束縛された値を元に、様々な処理を行う。上記の例では、Bar, Bar.method() @Foo という情報を全て取得した状態で、何らかの処理を書けばよい。

詳しくは次回以降に。