Edited at

JVMことはじめ その1: クラスファイル


この記事は何?

最近趣味でJVMを作り始めたので,その過程で得た知見とかを書いていく記事です.

今回は,クラスファイルに焦点を当てていきます.


参考文献

参考文献は一番最後に書くものだと思ってたけど,最初に書いても良いんじゃないだろうか.


クラスファイルの構造

規格より抜粋.

ClassFile {

u4 magic;
u2 minor_version;
u2 major_version;
u2 constant_pool_count;
cp_info constant_pool[constant_pool_count-1];
u2 access_flags;
u2 this_class;
u2 super_class;
u2 interfaces_count;
u2 interfaces[interfaces_count];
u2 fields_count;
field_info fields[fields_count];
u2 methods_count;
method_info methods[methods_count];
u2 attributes_count;
attribute_info attributes[attributes_count];
}

uNはNバイトの符号なし整数を表現しているようです.

それでは最初のフィールドから見ていきましょう.


magic

クラスファイルの先頭に位置するmagicには定数CAFEBABE(16進数)が入ります.CAFEBABEという値になった理由は色々あるようです.

わかりやすい値なので,これで誰でもクラスファイルと気づくことができますね.


minor_version, major_version

正直説明することがないですね.(マイナー|メジャー)バージョン情報です.


constant_pool_count

後述するconstant_poolのエントリの数-1を表しています.


constant_pool

前述したconstant_pool_countの値によって長さの変わる配列です.型もcp_infoと見慣れないものになっています.

とりあえず,cp_infoの中身を見てみましょう.

cp_info {

u1 tag;
u1 info[];
}

tagはそのcp_infoが何を表しているのか(=Constant Type)を示すものです.infotagによって大きさが変わります.

Constant Typeとtagの値の対応は以下のようになっています.

Constant Type
tagの値

CONSTANT_Class
7

CONSTANT_Fieldref
9

CONSTANT_Methodref
10

CONSTANT_InterfaceMethodref
11

CONSTANT_String
8

CONSTANT_Integer
3

CONSTANT_Float
4

CONSTANT_Long
5

CONSTANT_Double
6

CONSTANT_NameAndType
12

CONSTANT_Utf8
1

CONSTANT_MethodHandle
15

CONSTANT_MethodType
16

CONSTANT_InvokeDynamic
18

tagの値,Constant Typeは色々あって一つづつ説明していると長くなるので,とりあえずはカット.詳しく知りたい方は,規格のこのあたりからを読めば幸せになれると思います.


ハマったところとか



  • constant_poolの0番目の要素は存在しません.もう少し詳しく言うと,0番目はデータが存在しないことを表現するのに使います.だから,constant_pool[constant_pool_count-1]

  • CONSTANT_Utf8で表されるUTF-8文字列はヌル終端じゃなかった.(C言語脳なので,文字列はNULLで終わるものだと思っていた)

  • Constant TypeがCONSTANT_LongConstant_Doubleの時は,特殊なケースなので注意すること.


All 8-byte constants take up two entries in the constant_pool table of the class file. If a CONSTANT_Long_info or CONSTANT_Double_info structure is the item in the constant_pool table at index n, then the next usable item in the pool is located at index n+2. The constant_pool index n+1 must be valid but is considered unusable.

https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.4.5



access_flags

アクセス権限とかその他色々を表すフラグです.規格によると,今は8種類あるようです.

フラグの名前

説明

ACC_PUBLIC
0x0001
publicであることを表す.パッケージの外部からアクセスされるかもしれないってことですね

ACC_FINAL
0x0010
final. いかなるサブクラスも許されない,と.

ACC_SUPER
0x0020

invokespecialという命令によって呼び出された時に,スーパークラスのメソッドを扱う.

ACC_INTERFACE
0x0200
インターフェース.クラスじゃないよ.

ACC_ABSTRACT
0x0400
abstract.インスタンス化,ダメゼッタイ.

ACC_SYNTHETIC
0x1000
synthetic.コンパイラが勝手に作る.

ACC_ANNOTATION
0x2000
アノテーション型

ACC_ENUM
0x4000
enumですね


this_class, super_class

前述のconstant_poolのうち,



  • this_classの表す値のインデックスに位置するのが,そのクラスの名前


  • super_classの表す値のインデックスに位置するのが,スーパークラスの名前

となっています.

名前というのは(というかクラスファイルに含まれる文字列は)UTF-8で表されているようで,先ほど登場したConstant TypeのCONSTANT_Utf8に対応します.


interfaces_count

このクラス,またはインターフェース型の直接のスーパーインターフェースの数を表します.要するに,後述のinterfacesの要素数です.


interfaces

interfacesの要素の表す値は,constant_poolのインデックスとなっています.

対応するconstant_poolの値のConstant TypeはCONSTANT_Class_info(CONSTANT_Classが表す構造体)である必要があります.

特に面白くないですね.


fields_count

このクラス,またはインターフェース型で定義されているフィールドの数.

後述のfieldsの要素数でもある.


fields

field_infoという謎の型でできた配列.定義はこうなっています.

field_info {

u2 access_flags;
u2 name_index;
u2 descriptor_index;
u2 attributes_count;
attribute_info attributes[attributes_count];
}

attribute_infoという,またもや知らない型が出てきましたが,とりあえずは置いておきましょう.


access_flags

先程もaccess_flagsというフィールドを見た気がしますが,それとはまた別のものです.

名前
Value
Interpretation

ACC_PUBLIC
0x0001
public.

ACC_PRIVATE
0x0002
private.そのクラス内でのみ利用可.

ACC_PROTECTED
0x0004
protected.サブクラスからアクセスされるかもね.

ACC_STATIC
0x0008
static.

ACC_FINAL
0x0010
final.オブジェクトが生成された後には直接代入されない(JLS §17.5になんか書いてあるらしい).

ACC_VOLATILE
0x0040
volatile.キャッシュできない.

ACC_TRANSIENT
0x0080
transient.persistent object managerから書き込まれたり読み込まれたりしない.

ACC_SYNTHETIC
0x1000
synthetic.

ACC_ENUM
0x4000
enumの要素.


name_index, descriptor_index

両方とも,constant_poolへの参照値(インデックスですね)を表します.

name_indexの指す先には,fiboなどフィールドの名前が含まれます.

descriptor_indexの指す先には,(I)Iなどの型情報が含まれます.基本的には(X)Yという形式で,Xが引数,Yが返却値の型を表しているようです.


attribute...

また後で解説します.結構重要な位置を占めるものなので.


methods_count, methods

このクラス,またはインターフェース型で定義されているメソッドのことを表している...というのは,もはや説明の必要はないでしょう.

前述のfields_countfieldsに似ている,といいますか,同じです.

method_infofield_infoと同じ.

method_info {

u2 access_flags;
u2 name_index;
u2 descriptor_index;
u2 attributes_count;
attribute_info attributes[attributes_count];
}


attribute_count

後述するattributesの要素数.


attributes

クラスファイルの中でも一番最後のフィールドです.そして,fieldsmethodsの中ですでに何度も現れている型であるattribute_infoで構成されています.

attribute_info {

u2 attribute_name_index;
u4 attribute_length;
u1 info[attribute_length];
}

attribute_name_indexconstant_poolへの参照値.アトリビュートの名前を表します.

attribute_lengthは,infoの長さのようです.infoはアトリビュートの名前によって内容が変わります.

現在はこんなに種類のあるアトリビュート↓ この一つひとつに対応する構造体があり,それがinfoの中に収まっているわけです.

アトリビュートの名前

ConstantValue

Code

StackMapTable

Exceptions

InnerClasses

EnclosingMethod

Synthetic

Signature

SourceFile

SourceDebugExtension

LineNumberTable

LocalVariableTable

LocalVariableTypeTable

Deprecated

RuntimeVisibleAnnotations

RuntimeInvisibleAnnotations

RuntimeVisibleParameterAnnotations

RuntimeInvisibleParameterAnnotations

AnnotationDefault

BootstrapMethods


実践

ここまでクラスファイルの中身を見てきましたが,これだけではわかった気にはなれませんね.

実際のクラスファイルを見てみます.

以下のようなJavaコードを考えます.


Add.java

class Add {

public static int add(int x, int y) {
return x + y;
}
}

コンパイルして,xxdで見てみる.

$ javac Add.java

$ xxd Add.class
00000000: cafe babe 0000 0034 000f 0a00 0300 0c07 .......4........
00000010: 000d 0700 0e01 0006 3c69 6e69 743e 0100 ........<init>..
00000020: 0328 2956 0100 0443 6f64 6501 000f 4c69 .()V...Code...Li
00000030: 6e65 4e75 6d62 6572 5461 626c 6501 0003 neNumberTable...
00000040: 6164 6401 0005 2849 4929 4901 000a 536f add...(II)I...So
00000050: 7572 6365 4669 6c65 0100 0841 6464 2e6a urceFile...Add.j
00000060: 6176 610c 0004 0005 0100 0341 6464 0100 ava........Add..
00000070: 106a 6176 612f 6c61 6e67 2f4f 626a 6563 .java/lang/Objec
00000080: 7400 2000 0200 0300 0000 0000 0200 0000 t. .............
00000090: 0400 0500 0100 0600 0000 1d00 0100 0100 ................
000000a0: 0000 052a b700 01b1 0000 0001 0007 0000 ...*............
000000b0: 0006 0001 0000 0001 0009 0008 0009 0001 ................
000000c0: 0006 0000 001c 0002 0002 0000 0004 1a1b ................
000000d0: 60ac 0000 0001 0007 0000 0006 0001 0000 `...............
000000e0: 0003 0001 000a 0000 0002 000b ............

さあ,ここまでで学んだ知識を使って読んでいきましょう...と言いたかったのですが...

Untitled Diagram (3).png

ここまで作って,力尽きました.すいません.(いつか全部埋めて置き換える)

どうしても全部読みたい!という方は規格とにらめっこするか,私のコードのここらへんを参照すれば幸せになれるはず.


最後に

普段,記事を書いたりするなどのOutputがなかったので書いてみました.

一応シリーズ物なので,次はJVMがクラスファイルを読み込んでどう実行するのかについて書きたいなぁなんて思っています.

批評などコメントは大歓迎です.