Javaバイトコードの読み方


 JavaEclipseF3辿

 Eclipse"Class File Editor"Java


 

Class File Editorの開き方


 Javaclass"Package Explorer"classWindow -> Show View -> Navigator"Navigator"Javaclass

 class"Package Explorer"Build Path -> Configure Build PathJava Build PathSourceDefalut output folderclass


 class"Class File Editor"

 classOpen With"Class File Viewer""Class File Editor"

クラスのメンバを確認する


 "Class File Editor"
package sample;

public class Hoge {
 public static String staticField;
 public String instanceField;
 public static void staticMethod() {}
 public void instanceMethod() {}
}

 HogeclassClass File Editor
// Compiled from Hoge.java (version 1.6 : 50.0, super bit)
public class sample.Hoge {
  
  // Field descriptor #6 Ljava/lang/String;
  public static java.lang.String staticField;
  
  // Field descriptor #6 Ljava/lang/String;
  public java.lang.String instanceField;
  
  // Method descriptor #9 ()V
  // Stack: 1, Locals: 1
  public Hoge();
    0  aload_0 [this]
    1  invokespecial java.lang.Object() [11]
    4  return
      Line numbers:
        [pc: 0, line: 3]
      Local variable table:
        [pc: 0, pc: 5] local: this index: 0 type: sample.Hoge
  
  // Method descriptor #9 ()V
  // Stack: 0, Locals: 0
  public static void staticMethod();
    0  return
      Line numbers:
        [pc: 0, line: 6]
  
  // Method descriptor #9 ()V
  // Stack: 0, Locals: 1
  public void instanceMethod();
    0  return
      Line numbers:
        [pc: 0, line: 7]
      Local variable table:
        [pc: 0, pc: 1] local: this index: 0 type: sample.Hoge
}

 "Class File Editor"

 public Hoge();JavaJava

メソッドの中身


 HelloWorld"Class File Editor"
public class HelloWorld {
 public static void main(String[] args) {
  System.out.println("HelloWorld!");
 }
}

 main
 public static void main(java.lang.String[] args);
    0  getstatic java.lang.System.out : java.io.PrintStream [16]
    3  ldc <String "HelloWorld!"> [22]
    5  invokevirtual java.io.PrintStream.println(java.lang.String) : void [24]
    8  return
      Line numbers:
        [pc: 0, line: 5]
        [pc: 8, line: 6]
      Local variable table:
        [pc: 0, pc: 9] local: args index: 0 type: java.lang.String[]

 0"Line numbers:"java[pc: 0, line: 5]0java5javac-g:none

 getstatic, ldc, invokevirtual, returnJava 0xB2, 0x12, 0xB6, 0xB1class

メソッド呼び出しを捉える

 実際の動作を正確に読むには、ちゃんと命令を読まなくてはならないのだけど、いきなりそこまで読める必要はない。重要なのはinvoke系の命令だ。

ニーモニック 動作
invokevirtual インスタンスのメソッド呼び出し
invokeinterface インターフェースのメソッド呼び出し
invokestatic staticメソッドの呼び出し
invokespecial コンストラクタの呼び出し

 ざっくりこんな感じ。

    5  invokevirtual java.io.PrintStream.println(java.lang.String) : void [24]

 であれば、java.io.PrintStreamクラスの println(java.lang.String) : void を呼び出すよ、ということ。

引数の渡し方


 JavaVM使

 LIFOWikipedia

 使8086CPUPentiumCore2

 
    3  ldc <String "HelloWorld!"> [22]
    5  invokevirtual java.io.PrintStream.println(java.lang.String) : void [24]

 ldc"HelloWorld!"invokevirtualprintln

インスタンスメソッド


 1
public class Foo {
 public void hoge() {
  bar();
 }
 public void bar() {
 }
}

hoge()
  // Method descriptor #6 ()V
  // Stack: 1, Locals: 1
  public void hoge();
    0  aload_0 [this]
    1  invokevirtual sample.Foo.bar() : void [15]
    4  return
      Line numbers:
        [pc: 0, line: 5]
        [pc: 4, line: 6]
      Local variable table:
        [pc: 0, pc: 5] local: this index: 0 type: sample.Foo

 1:invokevirtual 0:aload_0 [this]aload_0[this]
public class Foo {
 public void hoge() {
  bar(3, "str");
 }
 public void bar(int i, String s) {
 }
}

barintString
  // Method descriptor #6 ()V
  // Stack: 3, Locals: 1
  public void hoge();
    0  aload_0 [this]
    1  iconst_3
    2  ldc <String "str"> [15]
    4  invokevirtual sample.Foo.bar(int, java.lang.String) : void [17]
    7  return
      Line numbers:
        [pc: 0, line: 5]
        [pc: 7, line: 6]
      Local variable table:
        [pc: 0, pc: 8] local: this index: 0 type: sample.Foo

4:invokevirtual 0:aload_0this1:iconst_332:ldc 

戻り値


 voidreturn
 public int bar() {
  return 3;
 }
  // Method descriptor #15 ()I
  // Stack: 1, Locals: 1
  public int bar();
    0  iconst_3
    1  ireturn
      Line numbers:
        [pc: 0, line: 5]
      Local variable table:
        [pc: 0, pc: 2] local: this index: 0 type: sample.Foo

 0:iconst_33ireturnireturnboolean, byte, short, char, intinvokevirtual invokevirtual 

 ireturnareturn使

変数への格納


 
public class Foo {
 public void hoge() {
  int value = bar();
 }
 public int bar() {
  return 3;
 }
}
  // Method descriptor #6 ()V
  // Stack: 1, Locals: 2
  public void hoge();
    0  aload_0 [this]
    1  invokevirtual sample.Foo.bar() : int [15]
    4  istore_1 [value]
    5  return
      Line numbers:
        [pc: 0, line: 5]
        [pc: 5, line: 6]
      Local variable table:
        [pc: 0, pc: 6] local: this index: 0 type: sample.Foo
        [pc: 5, pc: 6] local: value index: 1 type: int

 4:istore_1valuestorestoreilfd

 istore_1_1VM12

 istore使istore_0, istore_1, istore_2, istore_3istoreistore_11

 aload_0_0

まとめ

 この稿では簡単なバイトコードニーモニックの読み方を解説した。

  1. クラスの定義は簡単に読める
  2. メソッドの中身を読むときはまずinvoke系の命令を探す
  3. invoke系命令の前にインスタンスと引数がスタックに積まれているはず
  4. invoke系命令の後に戻り値を変数に格納しているはず

 これぐらいを抑えておけば、ざっくりした動きぐらいは読めるようになるだろう。

参考資料


Java
Java SE Specifications


Amazon CAPTCHA

Wikipedia
Java - Wikipedia

最速マスターではない理由

 僕はジョークとパロディが好きだからエントリにJava変態文法最速マスター - プログラマーの脳みそなんてタイトルをつけたけど、「最速」ってのは誇大広告的で嫌いなフレーズだ。そんなわけで、最速ではないJavaバイトコードの読み方を書いた。もちろん、僕はこのエントリにエンジニアとしての意地を詰め込んでいて、必要最小限の知識・技能を手早く理解できるように工夫してある。出来れば「最速」と評価されたい気持ちもある。ただ、ここが世界の最速の到達点で、これ以上世界が進化しないなんてつまらないじゃないか。

 それではみなさん、楽しいデバッグライフを :-)