6

特定の注釈を含むクラスに属するメソッドにいくつかの厳密な呼び出し規則を適用するために、ビルド システム用のいくつかのツールを作成しています。

コンパイラ ツリー API を使用しています...

私が疑問に思っているのは、「ツリー」をトラバースするときに、MethodInvocation のクラス/インターフェイスのタイプをどのように伝えることができるかということです。

TreePathScanner を次のようにサブクラス化しています:

@Override
public Object visitMethodInvocation(MethodInvocationTree node, Trees trees) {

}

メソッドを呼び出そうとしているクラス(またはインターフェイス)のタイプを伝える方法があることを願っています。私はこれについて間違った方法で進んでいますか?アイデアをありがとう...

4

1 に答える 1

11

ここにはいくつかの問題があります。メソッド呼び出しレシーバーの Java タイプを知りたい場合もあれば、呼び出されるメソッドのクラスを知りたい場合もあります。Java 情報は、ジェネリック型も提供するため、より有益です。たとえばList<String> 、要素はクラスのみを提供しますが、List<E>.

要素を取得する

メソッドが呼び出されるクラスの Element を取得するには、次のようにします。


  MethodInvocationTree node = ...;
  Element method =
        TreeInfo.symbol((JCTree)node.getMethodSelect());
  TypeElement invokedClass = (TypeElement)method.getEnclosingElement();

コーナーケース:

1. invokedClass はレシーバー型のスーパークラスである場合があります。したがって、 equals() はnotで実装されているため、スニペットを実行するとではなくnew ArrayList<String>.equals(null)が返さ れます。AbstractListArrayListAbstractListArrayList

2. 配列の呼び出しを処理する場合、たとえばのクラスnew int[].clone()を取得します。TypeElementArray

実際の型を取得する

型を取得するために、受信者の型が何であるかを直接判断する方法はありません。レシーバーが明示的に指定されていない内部クラス内でのメソッド呼び出しの処理には、多少の複雑さがあります (たとえば、 とは異なりOuterClass.this.toString()ます)。実装例を次に示します。


  MethodInvocationTree node = ...;
  TypeMirror receiver;
  if (methodSel.getKind() == Tree.Kind.MEMBER_SELECT) {
    ExpressionTree receiver = ((MemberSelectTree)methodSel).getExpression();
    receiverType = ((JCTree)receiver).type;
  } else if (methodSel.getKind() == Tree.Kind.IDENTIFIER) {
    // need to resolve implicit this, which is described in
    //  JLS3 15.12.1 and 15.9.2

    // A bit too much work that I don't want to work on now
    // Look at source code of
    //   Attr.visitApply(JCMethodInvocation)
    //   resolveImplicitThis(DiagnosticPosition, Env, Type)
  } else
    throw new AssertionError("Unexpected type: " + methodSel.getKind());

ノート:

残念ながら、receiverタイプはそうである必要はありTypeMirrorません。DeclaredTypeを呼び出すとnew int[5].clone()、以前のメソッドよりも有益なofにreceiverなります。ArrayTypeint[]

実行する

前の方法はどちらも、クラスの型情報を解決するためにコンパイラを必要とします。通常、コンパイラはメソッド宣言の型のみを解決し、本体は解決しません。したがって、前述のメソッドがnull代わりに返されます。

コンパイラに型情報を解決させるには、次のいずれかの方法を実行できます。

1. JDK 7 のコンパイラ リポジトリに追加されたばかりのクラスを使用します。JSR 308とそのコンパイラAbstractTypeProcessorに関する作業を確認してください。作業は主に注釈付きの型に関するものですが、役に立つかもしれません。コンパイラを使用すると、提供されたクラスを Java 5 と下位互換性のある方法で使用できます。

このアプローチにより、現在のプロセッサと同じように呼び出されるプロセッサを作成できます。

2.JavacTask代わりに を使用して呼び出しますJavacTask.analyze()この javac テストのメイン メソッドを見て、クラスでビジターを呼び出す方法を確認してください。

このアプローチにより、通常のプロセスではなく直接呼び出す必要があるため、プロセッサはコンパイラへのプラグインではなく分析ツールのように見えます。

于 2009-06-30T23:26:30.477 に答える