Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

この記事は何?

最近趣味で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がクラスファイルを読み込んでどう実行するのかについて書きたいなぁなんて思っています.

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

uint256_t
大学生になりました.
http://github.com/maekawatoshiki
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away