2015-09-28 6 views
7

javapで列挙型を逆アセンブルすると、列挙型の暗黙的なコンストラクタ引数が存在しないように見えます。ここでjavapで逆アセンブルされた列挙型でコンストラクタ引数が表示されない

は、列挙型です:

enum Foo { X } 

私はコンパイルして、このコマンドを使用して、(Javaの8u60上で)これを逆アセンブル:

javac Foo.java && javap -c -p Foo 

そして、ここでは、私が手に出力されます:

final class Foo extends java.lang.Enum<Foo> { 
    public static final Foo X; 

    private static final Foo[] $VALUES; 

    public static Foo[] values(); 
    Code: 
     0: getstatic  #1     // Field $VALUES:[LFoo; 
     3: invokevirtual #2     // Method "[LFoo;".clone:()Ljava/lang/Object; 
     6: checkcast  #3     // class "[LFoo;" 
     9: areturn 

    public static Foo valueOf(java.lang.String); 
    Code: 
     0: ldc   #4     // class Foo 
     2: aload_0 
     3: invokestatic #5     // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum; 
     6: checkcast  #4     // class Foo 
     9: areturn 

    private Foo(); // <--- here 
    Code: 
     0: aload_0 
     1: aload_1 
     2: iload_2 
     3: invokespecial #6     // Method java/lang/Enum."<init>":(Ljava/lang/String;I)V 
     6: return 

    static {}; 
    Code: 
     0: new   #4     // class Foo 
     3: dup 
     4: ldc   #7     // String X 
     6: iconst_0 
     7: invokespecial #8     // Method "<init>":(Ljava/lang/String;I)V 
     10: putstatic  #9     // Field X:LFoo; 
     13: iconst_1 
     14: anewarray  #4     // class Foo 
     17: dup 
     18: iconst_0 
     19: getstatic  #9     // Field X:LFoo; 
     22: aastore 
     23: putstatic  #1     // Field $VALUES:[LFoo; 
     26: return 
} 

私の混乱は、各列挙定数をインスタンス化するために使用されるプライベートコンストラクタです。逆アセンブリは引数を取らないことを示しますが(private Foo();)、引数を取ることは確かです。たとえば、load命令は、渡された列挙定数名と序数、およびthisポインタを読み取って、それらを必要とするthe superclass constructorに渡すことができます。静的イニシャライザブロックのコードは、コンストラクタを呼び出す前に、それらの引数をスタックにプッシュすることも示しています。

は、今私は、これは単なるてjavapであいまいなバグだったと仮定しているだろうが、私は、Eclipseのコンパイラとまったく同じ列挙型をコンパイルしてjavap使用することを逆アセンブルするとき、コンストラクタはが示されているの引数を除いてまったく同じです。

final class Foo extends java.lang.Enum<Foo> { 
    public static final Foo X; 

    private static final Foo[] ENUM$VALUES; 

    static {}; 
    Code: 
     0: new   #1     // class Foo 
     3: dup 
     4: ldc   #12     // String X 
     6: iconst_0 
     7: invokespecial #13     // Method "<init>":(Ljava/lang/String;I)V 
     10: putstatic  #17     // Field X:LFoo; 
     13: iconst_1 
     14: anewarray  #1     // class Foo 
     17: dup 
     18: iconst_0 
     19: getstatic  #17     // Field X:LFoo; 
     22: aastore 
     23: putstatic  #19     // Field ENUM$VALUES:[LFoo; 
     26: return 

    private Foo(java.lang.String, int); // <--- here 
    Code: 
     0: aload_0 
     1: aload_1 
     2: iload_2 
     3: invokespecial #23     // Method java/lang/Enum."<init>":(Ljava/lang/String;I)V 
     6: return 

    public static Foo[] values(); 
    Code: 
     0: getstatic  #19     // Field ENUM$VALUES:[LFoo; 
     3: dup 
     4: astore_0 
     5: iconst_0 
     6: aload_0 
     7: arraylength 
     8: dup 
     9: istore_1 
     10: anewarray  #1     // class Foo 
     13: dup 
     14: astore_2 
     15: iconst_0 
     16: iload_1 
     17: invokestatic #27     // Method java/lang/System.arraycopy:(Ljava/lang/Object;ILjava/lang/Object;II)V 
     20: aload_2 
     21: areturn 

    public static Foo valueOf(java.lang.String); 
    Code: 
     0: ldc   #1     // class Foo 
     2: aload_0 
     3: invokestatic #35     // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum; 
     6: checkcast  #1     // class Foo 
     9: areturn 
} 

私の質問は:どのような物理的には、javacのコンパイル列挙型とてjavapはjavacのコンパイル列挙型のコンストラクタの引数を表示しないようになりEclipseのコンパイル列挙型の間で違うのですか?そして、この違いはバグです(javap、javac、またはEclipse)。

+0

推測すると、 '-g'フラグの設定(デバッグ情報の生成を制御します)。 – CPerkins

+0

@CPerkins良い仮説ですが、 '-g'(すべてのデバッグ情報を生成する)と' -g:none'(デバッグ情報を生成しません)を比較しました。 – Boann

+0

'-v'(冗長)フラグを試しましたか?少なくとも、コンストラクタの記述子を表示する必要があります。私の推測では、 'javac'は最初のパラメータ' MANDATED'をマークしているので、 'javap'はそれらを省略することがあります。 – Clashsoft

答えて

3

クラスファイル内のメソッドの戻り値の型は、method descriptorで記述されます。

1.5でジェネリック医薬品を導入しました。追加情報がクラスファイル形式のmethod signatureに導入されました。

「メソッドディスクリプタ」は、タイプ消去後のメソッドを記述するために使用され、「メソッドシグネチャ」にはジェネリックタイプ情報も追加されます。

javapは、より多くの情報を含むメソッドシグネチャを出力し、-vフラグが設定されている場合は、ディスクリプタも出力します。

このことから、javac生成されたenumクラスのコンストラクタには、パラメータタイプがStringおよびintのメソッドディスクリプタがあることがわかります。 Elipseとjavacで生成されたコードの両方がうまく動作する理由も明らかです。両方とも引数Stringintを持つプライベートコンストラクタを呼び出します。

まだ説明する必要があるもの:javacは、記述子とは異なる署名を作成するのはなぜですか - ジェネリックは含まれていませんか?

とにかく、列挙型のコンストラクタに関するjavacの挙動はother troubles引き起こしているとjavacのためのバグレポートはfiledた:

方法を格納 署名の属性を持つ列挙型の宣言のコンストラクタのための必要はありませんシグニチャの場合1)コンストラクタ は一般的ではなく、2)その仮パラメータ型は のパラメータ化型でも型変数でもありません。 javacが上記のコンストラクタの シグネチャ属性を予期している場合、バグです。

以下のコメントとこのケースの分類は、これがjavacの実際のバグであることを示唆しています。

+0

ありがとう、これは違いを説明します。 javacは*明示的に*定義されたenumコンストラクタパラメータをシグネチャに含めていますが、定数名と序数の暗黙の2つの暗黙のものではありません。私はそれがjavacのバグであるとは確信していませんが、VM仕様の欠点はもっとあります。署名属性のVM仕様のルールは、それが存在すると想定されるときと、それが含まれると想定されるときのために曖昧に定義されているようです。 – Boann

+0

同様の問題は、内部クラスのコンストラクタで、外部クラスへの参照を渡すために使用される隠しパラメータがあります。例えば、 'class Outer {class Inner {Inner(){}}}' - コンストラクタは逆アセンブルで 'Outer $ Inner(Outer);と表示されます。しかし、コンストラクタが* generic *: 'class Outer {class Inner { Inner(){}}}'であれば、クラスファイルのシグネチャ属性を取得し、javapはそれを ' Outer $ Inner();' - として逆アセンブルします。 *パラメータが突然見えない*、これはjavacとECJの両方に当てはまります。奇妙なもの!少なくとも '-v'オプションは真実を明らかにする! – Boann

関連する問題