When I disassemble an enum with javap, the enum's implicit constructor arguments seem to be missing, and I can't figure out why.
Here's an enum:
enum Foo { X }
I compile and disassemble this (on Java 8u60) with this command:
javac Foo.java && javap -c -p Foo
And here is the output I get:
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
}
My confusion is with the private constructor used to instantiate each enum constant. The disassembly shows that it takes no arguments (private Foo();
), but it surely does take arguments. For example, you can see the load
instructions reading the passed enum constant name and ordinal, as well as the this
pointer, and passing them on to the superclass constructor, which requires them. The code in the static initializer block also shows that it pushes those arguments onto the stack before calling the constructor.
Now I would have assumed this was just an obscure bug in javap, but when I compile exactly the same enum with Eclipse's compiler and disassemble that using javap, the constructor is exactly the same except the arguments are shown:
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
}
My question is: what physically is different between a javac-compiled enum and an Eclipse-compiled enum that causes javap to not show the constructor arguments for the javac-compiled enum? And is this difference a bug (in javap, in javac, or Eclipse)?
-g
flag (controls generation of debugging info). – Hyperplasia-g
(generate all debugging info) and-g:none
(generate no debugging info) and it seems to make no difference. – Valais-v
(verbose) flag? It should at least show you the descriptor of the constructor. My guess is thatjavac
marks the first parametersMANDATED
, which might makejavap
omit them. – Launchdescriptor: (Ljava/lang/String;I)V
), but I still don't understand what's causing the difference between the javac and Eclipse enums. – Valaisjavap
probably ignoresMANDATED
parameters. I suppose Eclipse doesn't generate the modifier, whilejavac
does. – Launch