Javaの前置インクリメントと後置インクリメントの内部実装を読みたい

id:E_Mattsanさんからコメントで教えていただいたページが面白いです。

C++の前置インクリメントと後置インクリメントの内部実装について書かれています。元ネタは、『新訂版 More Effective C++』という本のようです。

前置と後置の振る舞いを同じにするために、内部的には後置の実装に前置を使っている、という話が、興味深いと思いました。

で、Javaだとどうなのかが気になり、確認しようとしたところ、行き詰まってしまいました。

分からないこと

どのコードを読めばよいかが分かりません。標準APIの実装なのか? JVMのコードなのか?

そもそも「++」とか「+=」とかグレッパビリティ(grepの有効性。造語)が低すぎてキツイ。

一応、JVMにIINC.javaがありましたが、読むべきはこれではない気がします。

だって、インクリメント演算の機構が、こんな外出しのツールっぽい場所に書いてあるわけない…ヨネ?

コンパイラの字句解析で「++」を判別してる場所を見つければよいのかもしれませんが、それどこコード? どこコードよーー

…とまあ、情けない感じです。一応、やろうとしたことのメモを書きます。

サンプルコード

次の2つのコードについて、実装の差異を確認します。

前置インクリメント

PreIncrement.java

package study.increment;

public class PreIncrement {
	public static void main(String[] args) {
		int hoge = 10;
		int moga = ++hoge;
	}
}
後置インクリメント

PostIncrement.java

package study.increment;

public class PostIncrement {
	public static void main(String[] args) {
		int hoge = 10;
		int moga = hoge++;
	}
}

jadってみる

クラスファイルをjadってみます。ツールはJD(http://java.decompiler.free.fr/)を使います。

前置インクリメント

おや? 後置になってる…?

package study.increment;

public class PreIncrement
{
  public static void main(String[] args)
  {
    int hoge = 10;
    hoge++; int moga = hoge;
  }
}
後置インクリメント

こちらは、書いたコードそのまま。

package study.increment;

public class PostIncrement
{
  public static void main(String[] args)
  {
    int hoge = 10;
    int moga = hoge++;
  }
}

命令コードを見てみる

何かよく分からないので、バイトコードの命令列を見てみます。

javapに-cオプションをつけて実行します。

前置インクリメントの命令コード
>javap -c PreIncrement
Compiled from "PreIncrement.java"
public class study.increment.PreIncrement extends java.lang.Object{
public study.increment.PreIncrement();
  Code:
   0:   aload_0
   1:   invokespecial   #8; //Method java/lang/Object."<init>":()V
   4:   return

public static void main(java.lang.String[]);
  Code:
   0:   bipush  10
   2:   istore_1
   3:   iinc    1, 1
   6:   iload_1
   7:   istore_2
   8:   return

}
後置インクリメントの命令コード
>javap -c PostIncrement
Compiled from "PostIncrement.java"
public class study.increment.PostIncrement extends java.lang.Object{
public study.increment.PostIncrement();
  Code:
   0:   aload_0
   1:   invokespecial   #8; //Method java/lang/Object."<init>":()V
   4:   return

public static void main(java.lang.String[]);
  Code:
   0:   bipush  10
   2:   istore_1
   3:   iload_1
   4:   iinc    1, 1
   7:   istore_2
   8:   return

}

iload_1とiincの位置が逆になっていること(だけ)は分かります。

iload_1は、1番目の局所変数から値を取り出してスタックに積む命令、iincは、int値を定数分増減する命令ですね。

・・・

C++は後置の実装に前置を使っていたけれど、Javaは逆なのか?

それとも、デコンパイルツールがそう類推しただけなのか?

実際の実装はどうなっているのか?

…という点を、確かめたいです。サテハテ。

ちなみに

サンプルコードでは、変数mogaが未使用のままです。コンパイラが未使用のコードを無視する可能性が考えられる場合、これはあまり良い検証用コードではないといえます。しかし、printlnとか使うと、当然PrintStreamクラスとかがロードされてしまい、見たい部分(インクリメントの実態)が分かりづらくなるので、省きました。ダメなんかな…

このへんの確認の作法も未熟なので、覚えたいところ。