引数名

S2Container 2.3.9 に含めてリリースしてしまった BeanDesc の引数名取得機能ですが,肝心要の Java interface からは引数名を取得できないという指摘をきむきむから頂いてしまいました.
調べてみたら,確かに取れない...
interface に対して使うことを意図しておきながら class に対してしかテストをしていなかったという...
心より恥じる.


実際には,interface のメソッドだけでなく,抽象メソッドの引数も取れません.
メソッドの引数名はメソッド情報 (method_info) の コード属性 (Code_attribute) 中の ローカル変数テーブル属性 (LocalVariableTable_attribute)に含まれているのですが,JVM 仕様によると

If the method is either native or abstract, its method_info structure must not have a Code attribute.

とほほ...


要するに interface のメソッドの引数名は class ファイルに含まれていないわけで,ランタイムではどう頑張っても引数名を取れないわけね.
でもでも,引数名が取れないと Kuina-Dao なんかでも S2Dao の Args アノテーションみたいな事をしなきゃいけないわけで,それはやっぱりいやーんな感じ.
ってことは,コンパイルタイムの助けを借りる (ポストプロセス) しかないわけね.
それはそれでビルダを外してクリーンビルドしなきゃいけない事態を招いたりとかいやーんな感じがしないでもないのですが,背に腹は代えられないわけで.
ともあれ (JW),その方法としては,

がすでにあるわけです,と.
んで,APT だと Java5 に限定されてしまうので,とりあえず Doclet 使う方向で再検討.


きむきむのは Doclet で取得した引数名をテキストファイルに書いていたわけですが,それに対して manhole さんから Javassist で class ファイルに書き込めないかというコメントがありました.
そんなわけで (どんなわけで?),その部分をまずは実装しました.
その名も ParameterNamesEnhancer
こいつは,外から与えられた引数名を次のようなアノテーションが書かれたかのように class ファイルに埋め込みます.

public interface Hoge {
  void geho(
    @ParameterName("foo") int foo,
    @ParameterName("bar") String bar);
}

アノテーションといっても,class ファイル上は単なる属性の一つに過ぎないので,これは J2SE1.4 の class ファイルでも有効です.
そして Javassist を使えばこのアノテーションは J2SE1.4 上で読むこともできます.
そんなわけで (どんなわけで?),BeanDescJavassist を使ってこのアノテーションを読み出し,引数名を取得できるようにします.
つまり,J2SE1.4 でも Tiger でも引数名を取れます.
もちろんこれはアノテーションなので,Tiger の場合は Method#getParameterAnnotations()@ParameterName アノテーションとして取得することができます.
残念ながら Backport175 では引数のアノテーションは取得できなかった気がするので AnnotationReader からの取得は (おそらく) 無理ですが,BeanDesc から取れればたいていの場合は十分ではないかと.


ちなみに,Tiger では上記のコード例のように明示的に @ParameterName を書くことで,実際の引数名とは異なる名前を与えることもできます.
引数名を上書きするイメージですね.
そして BeanDescアノテーションがなければデバッグ情報を見に行くので,両方合わせると次の優先順位で引数名を解決することになります.

  1. 明示的に付けられた @ParameterName アノテーションvalue
  2. Doclet で付けられた @ParameterName アノテーションvalue
  3. デバッグ情報の引数名 (非 abstract メソッドのみ有効)


ってことで,Kuina-Dao はこれを使おうと思います.


P.S.
Doclet 対応もでけた.
きむきむの ArgsDefMaker のおかげです.