Hello World VM (HWVM) for Java on Ruby: 作成方針

実装言語は,趣味で Ruby を選択.CIL のほうが作るのがしんどかったので,楽だったほうの Java から書いてみることにする.

Java は,ソースコードを .java というファイルに書いて,javac コマンドでコンパイルして .class ファイルを生成し,java コマンドでそれを実行するというのが基本的な流れになっている.つまり,JVM が実行するプログラムの本体は .class ファイルの中に入っている.HWVM を作る第一歩としては,まずこの class ファイルを読みだして,実行するべきプログラムのデータ(instruction code)が入っている部分を取りだす必要がある.そのためには,class ファイルの構造をしっておかないといけない.

JVM が実行する Java中間言語で書かれたプログラムは javap というコマンドを使うとダンプすることができる.ためしに Hello World プログラムをコンパイルして,javap コマンドでダンプしてみる.

bash$ javap -c HelloWorld

Compiled from "HelloWorld.java"
public class HelloWorld extends java.lang.Object{
public HelloWorld();
  Code:
   0:	aload_0
   1:	invokespecial	#1; //Method java/lang/Object."":()V
   4:	return

public static void main(java.lang.String[]);
  Code:
   0:	getstatic	#2; //Field java/lang/System.out:Ljava/io/PrintStream;
   3:	ldc	#3; //String Hello World!
   5:	invokevirtual	#4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   8:	return
}

このうち Hello World プログラムの本体は,

   0:	getstatic	#2; //Field java/lang/System.out:Ljava/io/PrintStream;
   3:	ldc	#3; //String Hello World!
   5:	invokevirtual	#4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   8:	return

この部分が相当している.HelloWorld() のところにあるプログラムは,HelloWorld クラスのコンストラクタなんだけど,HWVM では HelloWorld クラスのインスタンスを new したりしないので,ここの部分は使わない(から無視しておく).

javap の出力した各行の // から後ろは javap が人間が読みやすくなるように付けてくれてるコメントであって,JVM が読む命令の列の中には直接には含まれていない.たとえば1行目なら getstatic #2; までになる.この getstatic とか ldc とかが実際に JVM が実行すべき命令で,以下のような意味がある.

getstatic #2; Fieldref の2番の static 変数 (System.out) をスタックに積む
ldc #3; Stringsの3番のデータ("Hello World!")をスタックに積む
invokevirtual #4; Methodrefの4番の method (PrintStream.println) を呼びだす.
return メソッド処理を終了する

つまり,すごく単純に考えれば,

  • class ファイルを読みだして public static void main(java.lang.String[]) というメソッドを探す.
  • main メソッドの中に書かれている4つの命令を上から順番に実行する.

ということができるプログラムを書いてやれば,HWVM は作れたことになる.簡単っぽいね.

ただし,class ファイルの中をのぞいても getstatic とか ldc という文字列はみつからない.それぞれの命令は,文字列ではなくて 16 bit の数値で表現されている.このプログラムの場合は以下のようなかんじ.

0xB2 0x00 0x02 getstatic #2
0x12 0x03 ldc #3
0xB6 0x00 0x04 invokevirtual #4
0xB1 return

数値と命令の対応表は,sun のページとしてはThe Java Virtual Machine Instruction Setがあるんだけど,ここのドキュメントは一覧表がなくて見にくい.一覧としては下記のページのほうが見やすい,

実際のクラスファイルのどこにこのデータがあるかを,バイナリエディタをつかってのぞいてみると,

こんな感じに入っている.HWVM 作成の第一歩として,まずはこの命令(instruction code)の列を取りだすことを考えてみたい.