バイトコードの読み方その1 とりあえず基本
今年の目標その1「バイトコードの読み方をマスターする」
1.命令の種類
WikiPediaが妙に詳しい。
http://ja.wikipedia.org/wiki/Java%E4%BB%AE%E6%83%B3%E3%83%9E%E3%82%B7%E3%83%B3
本当はJava言語仕様を読むべきだけど、ここだけでもなんとかなる感じ。
大体の命令は、スタック領域とローカル変数領域を操作する。
2.スタック領域とローカル変数領域
スタック領域にはインスタンスやプリミティブの値を格納する。
メソッド呼び出しや演算などの命令の実行は、すべてスタック領域に格納されているオブジェクトから行う。
命令を実行をすると使用したスタックの値が消費される。
例えば 1+1の演算の場合(後ろの[]はスタック領域の状態)
iconst_1 // スタックに1を追加 [1]
iconst_1 // スタックに1を追加 [1,1]
iadd // スタック領域の先頭と次の値を足してスタック領域に格納する [2]
というようになる。
3.インスタンスメソッドの呼び出し
System.out.println("Hello World!")をeclipseのクラスファイルエディターで見ると以下のようになる。
0 getstatic java.lang.System.out : java.io.PrintStream [16]
3 ldc <String "Hello World!!"> [22]
5 invokevirtual java.io.PrintStream.println(java.lang.String) : void [24]
・0の部分の命令であるgetstaticは、java.lang.Systemのoutフィールドを取得してスタック領域に格納する。 [PrintStreamインスタンス]←スタック領域の状態
・3の命令は"Hello World!"という文字列をスタック領域に格納する。 [PrintStreamインスタンス,"Hello World!"]←スタック領域の状態
・5の命令はinvokevirtual命令でインスタンスメソッドを実行する。引数はスタック領域の先頭の値、実行するメソッドを持つインスタンスはスタック領域の次の値。これを実行するとスタック領域は空になる。
invokevirtualで実行したメソッドが戻り値を持っている場合、スタック領域には戻り値が格納される。
例えば戻り値がintだとしたら、メソッド実行後のスタック領域は[1]のようになる。
invokevirtual命令を実行すると、スタック領域に格納したインスタンスは取り出されてなくなってしまう。
そのため、メソッドを連続で呼び出す場合などは、命令の実行前にdup命令でスタック領域の先頭にあるインスタンスを複製したり、ローカル変数領域から取得したりなどを行う。