LoginSignup
7
8

More than 1 year has passed since last update.

javap の隠しオプション

Last updated at Posted at 2023-05-05

たまたま 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 を用意して、コンパイルします。

Main.java
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 … を埋め込んで出力してくれます。

javap -verbose -XDdetails:localVariables Main
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 … を埋め込んで出力してくれます。

javap -verbose -XDdetails:localVariableTypes Main
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 ... を実行すると、バイトコードと一緒にもとの行番号とソースコードを埋め込んで出力してくれます。
(下記の出力例、スマホだと右スクロールしないと見えないかもしれません)

javap -verbose -XDdetails:source Main
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 … を埋め込んで出力してくれます。

javap -verbose -XDdetails:stackMaps Main
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 を使ったコードを用意して、コンパイルします。

Main.java
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[...] といった情報を埋め込んで出力してくれます。

javap -verbose -XDdetails:tryBlocks Main
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 を使ったコードを用意して、コンパイルします。

Main.java
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 ... を実行すると、バイトコードと一緒に情報が…、なぜか出力されませんでした(?)

javap -verbose -XDdetails:typeAnnotations Main
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 します。

実行例

適当なインナークラスを使ったコードを用意して、コンパイルします。

Main.java
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 から使えるようになっていたみたいです。

  1. 拡張 for 文をつかうと javap の出力が長くなるので、ここではあえて for ループを使っています。

7
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
8