たまたま OpenJDK の javap のソースコードを読んでいたら、-help
には表示されない隠しオプションがいくつかあることに気づきました。
-XDdetails
-XDinner
-XDindent:(数値)
-XDtab:(数値)
せっかくなので、詳しく調べてみました。
実装依存の情報なので、今後のバージョンで変わる可能性があります。
-XDdetails
バイトコードの出力時に、追加で詳細情報を出力するオプションです。
-verbose
オプションと併用します。
出力する情報は、 -XDdetails:(オプション)
と指定することで制御可能です。
(複数指定する場合は、,
または :
区切り)
指定できるオプションは、下記の6つです。
localVariables
localVariableTypes
source
stackMaps
tryBlocks
typeAnnotations
-XDdetails
とした場合、全部のオプションを指定したことになります。
また、-XDdetails:all,-localVariables
のように -
指定すると、一部の出力を省略できます。
-XDdetails:localVariables
LocalVariableTable
属性の情報をもとに、ローカル変数の情報をバイトコードと一緒に出力します。
(これらの属性を出力するように、コンパイル時に -g:vars
オプションが必要です)
実行例
適当なコード 1 を用意して、コンパイルします。
public class Main {
public static void main(String[] args) {
var list = java.util.List.of("foo", "bar");
for (var i = 0; i < list.size(); i++){
System.out.println(list.get(i));
}
}
}
このクラスファイルを javap -XDdetails:localVariables ...
を実行すると、バイトコードと一緒に start local …
と end local …
を埋め込んで出力してくれます。
Code:
stack=3, locals=3, args_size=1
start local 0 // java.lang.String[] args
0: ldc #7 // String foo
2: ldc #9 // String bar
4: invokestatic #11 // InterfaceMethod java/util/List.of:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/util/List;
7: astore_1
start local 1 // java.util.List list
8: iconst_0
9: istore_2
start local 2 // int i
10: iload_2
11: aload_1
12: invokeinterface #17, 1 // InterfaceMethod java/util/List.size:()I
17: if_icmpge 42
20: getstatic #21 // Field java/lang/System.out:Ljava/io/PrintStream;
23: aload_1
24: iload_2
25: invokeinterface #27, 2 // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
30: checkcast #31 // class java/lang/String
33: invokevirtual #33 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
36: iinc 2, 1
39: goto 10
end local 2 // int i
42: return
end local 1 // java.util.List list
end local 0 // java.lang.String[] args
LocalVariableTable:
Start Length Slot Name Signature
10 32 2 i I
0 43 0 args [Ljava/lang/String;
8 35 1 list Ljava/util/List;
LocalVariableTypeTable:
Start Length Slot Name Signature
8 35 1 list Ljava/util/List<Ljava/lang/String;>;
-XDdetails:localVariableTypes
LocalVariableTypeTable
属性の情報をもとに、ローカル変数のジェネリクス型情報をバイトコードと一緒に出力します。
(これらの属性を出力するように、コンパイル時に -g:vars
オプションが必要です)
実行例
上記と同じクラスファイルに対して javap -XDdetails:localVariableTypes ...
を実行すると、バイトコードと一緒に start generic local …
と end generic local …
を埋め込んで出力してくれます。
Code:
stack=3, locals=3, args_size=1
0: ldc #7 // String foo
2: ldc #9 // String bar
4: invokestatic #11 // InterfaceMethod java/util/List.of:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/util/List;
7: astore_1
start generic local 1 // java.util.List<java.lang.String> list
8: iconst_0
9: istore_2
10: iload_2
11: aload_1
12: invokeinterface #17, 1 // InterfaceMethod java/util/List.size:()I
17: if_icmpge 42
20: getstatic #21 // Field java/lang/System.out:Ljava/io/PrintStream;
23: aload_1
24: iload_2
25: invokeinterface #27, 2 // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
30: checkcast #31 // class java/lang/String
33: invokevirtual #33 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
36: iinc 2, 1
39: goto 10
42: return
end generic local 1 // java.util.List<java.lang.String> list
LocalVariableTable:
Start Length Slot Name Signature
10 32 2 i I
0 43 0 args [Ljava/lang/String;
8 35 1 list Ljava/util/List;
LocalVariableTypeTable:
Start Length Slot Name Signature
8 35 1 list Ljava/util/List<Ljava/lang/String;>;
-XDdetails:source
LineNumberTable
属性と SourceFile
属性の情報をもとに、ソースコードの情報をバイトコードと一緒に出力します。
(これらの属性を出力するように、コンパイル時に -g:lines
オプションが必要です)
このオプションを指定する際には、クラスパス上にもとのソースコードが必要です。
実行例
上記と同じクラスファイルに対して javap -XDdetails:source ...
を実行すると、バイトコードと一緒にもとの行番号とソースコードを埋め込んで出力してくれます。
(下記の出力例、スマホだと右スクロールしないと見えないかもしれません)
Code:
stack=3, locals=3, args_size=1
3 var list = java.util.List.of("foo", "bar");
0: ldc #7 // String foo
2: ldc #9 // String bar
4: invokestatic #11 // InterfaceMethod java/util/List.of:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/util/List;
7: astore_1
4 for (var i = 0;i < list.size(); i++){
8: iconst_0
9: istore_2
10: iload_2
11: aload_1
12: invokeinterface #17, 1 // InterfaceMethod java/util/List.size:()I
17: if_icmpge 42
5 System.out.println(list.get(i));
( 6) }
20: getstatic #21 // Field java/lang/System.out:Ljava/io/PrintStream;
23: aload_1
24: iload_2
25: invokeinterface #27, 2 // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
30: checkcast #31 // class java/lang/String
33: invokevirtual #33 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
4 for (var i = 0;i < list.size(); i++){
36: iinc 2, 1
39: goto 10
7 }
42: return
LineNumberTable:
line 3: 0
line 4: 8
line 5: 20
line 4: 36
line 7: 42
-XDdetails:stackMaps
StackMapTable
属性をもとに、スタックの情報をバイトコードと一緒に出力します。
実行例
上記と同じクラスファイルに対して javap -XDdetails:stackMaps
を実行すると、バイトコードと一緒に StackMap locals …
と StackMap stack …
を埋め込んで出力してくれます。
Code:
stack=3, locals=3, args_size=1
StackMap locals: java/lang/String[]
StackMap stack:
0: ldc #7 // String foo
2: ldc #9 // String bar
4: invokestatic #11 // InterfaceMethod java/util/List.of:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/util/List;
7: astore_1
8: iconst_0
9: istore_2
StackMap locals: java/lang/String[] java/util/List int
StackMap stack:
10: iload_2
11: aload_1
12: invokeinterface #17, 1 // InterfaceMethod java/util/List.size:()I
17: if_icmpge 42
20: getstatic #21 // Field java/lang/System.out:Ljava/io/PrintStream;
23: aload_1
24: iload_2
25: invokeinterface #27, 2 // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
30: checkcast #31 // class java/lang/String
33: invokevirtual #33 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
36: iinc 2, 1
39: goto 10
StackMap locals: java/lang/String[] java/util/List
StackMap stack:
42: return
StackMapTable: number_of_entries = 2
frame_type = 253 /* append */
offset_delta = 10
locals = [ class java/util/List, int ]
frame_type = 250 /* chop */
offset_delta = 31
-XDdetails:tryBlocks
ExceptionTable
属性をもとに、例外処理の情報をバイトコードと一緒に出力します。
実行例
適当な try-catch
を使ったコードを用意して、コンパイルします。
public class Main {
public static void main(String[] args) {
try {
System.out.println("try!");
} catch (Exception e) {
System.out.println("catch!");
}
}
}
このクラスファイルに対して javap -XDdetails:tryBlocks ...
を実行すると、バイトコードと一緒に try[...]
や end try[...]
といった情報を埋め込んで出力してくれます。
Code:
stack=2, locals=2, args_size=1
try[0] #21 // class java/lang/Exception
0: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #13 // String try!
5: invokevirtual #15 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
end try[0] #21 // class java/lang/Exception
8: goto 20
catch[0] #21 // class java/lang/Exception
11: astore_1
12: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
15: ldc #23 // String catch!
17: invokevirtual #15 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
20: return
Exception table:
from to target type
0 8 11 Class java/lang/Exception
-XDdetails:typeAnnotations
RuntimeVisibleTypeAnnotations
属性や RuntimeInvisibleTypeAnnotations
をもとに、型のアノテーションの情報をバイトコードと一緒に出力します。
実行例
適当な @Target
を使ったコードを用意して、コンパイルします。
import java.lang.annotation.*;
public class Main {
@Target(ElementType.TYPE_PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@interface Foo { }
public static void main(String[] args) {
System.out.println("foo");
}
}
このクラスファイルに対して javap -XDdetails:typeAnnotations ...
を実行すると、バイトコードと一緒に情報が…、なぜか出力されませんでした(?)
Code:
stack=2, locals=2, args_size=1
0: ldc #7 // String foo
2: astore_1
3: getstatic #9 // Field java/lang/System.out:Ljava/io/PrintStream;
6: aload_1
7: invokevirtual #15 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
10: return
RuntimeVisibleTypeAnnotations:
0: #27(): LOCAL_VARIABLE, {start_pc=3, length=8, index=1}
Main$Foo
RuntimeVisibleTypeAnnotations
は含まれているので、コードはあっていそうな気がするのですが…。
-XDinner
インナークラスも一緒に javap
します。
実行例
適当なインナークラスを使ったコードを用意して、コンパイルします。
public class Main {
public class Foo {
public static void foo() {
System.out.println("foo");
}
}
public static void main(String[] args) {
Foo.foo();
}
}
このクラスファイルに対して javap -XDinner Main
を実行すると、Main だけでなくインナークラスの Foo の javap
した内容を出力してくれます。
(javap -v 'Main' 'Main$Foo'
と同じ結果になります)
-XDindent:(数値)
通常、javap
の出力はスペース2つでインデントします。
これを、任意のスペース数に変更します。
// デフォルト
Constant pool:
#1 = Methodref #2.#3 // java/lang/Object."<init>":()V
#2 = Class #4 // java/lang/Object
// -XDindent:8 を指定した場合
Constant pool:
#1 = Methodref #2.#3 // java/lang/Object."<init>":()V
#2 = Class #4 // java/lang/Object
-XDtab:(数値)
通常、javap
のコメント部分は40文字目から出力されます。
これを、任意の位置に変更します。
// デフォルト
Constant pool:
#1 = Methodref #2.#3 // java/lang/Object."<init>":()V
#2 = Class #4 // java/lang/Object
// -XDtab:50 を指定した場合
Constant pool:
#1 = Methodref #2.#3 // java/lang/Object."<init>":()V
#2 = Class #4 // java/lang/Object
ちなみに
これらは 2009年ごろに実装されて、Java 7 から使えるようになっていたみたいです。
- [JDK-6824493] experimental support for additional info for instructions - Java Bug System
- [JDK-4880672] javap does not output inner interfaces of an interface - Java Bug System
- [JDK-6867671] javap whitespace formatting issues - Java Bug System
-
拡張 for 文をつかうと
javap
の出力が長くなるので、ここではあえて for ループを使っています。 ↩