Slide 1

Slide 1 text

RubyでJVMを実装してみる 平成Ruby会議#1 Daiki Miura (@daikimiura)

Slide 2

Slide 2 text

Daiki Miura @daikimiura @daikiii5555 慶應大学理工学部物理情報工学科4年 / インターン@FiNC 普段はプラスチック光ファイバの研究してます 平成10年生まれ 言語処理系初心者 $ whoami

Slide 3

Slide 3 text

きっかけ

Slide 4

Slide 4 text

きっかけ https://docs.oracle.com/javase/specs/jvms/se13/html/index.html

Slide 5

Slide 5 text

Rubyでもできるはず!

Slide 6

Slide 6 text

GitHub: https://github.com/daikimiura/merah

Slide 7

Slide 7 text

JVMを実装とは HelloWorld.java $ javac HelloWorld.java HelloWorld.class $ java HelloWorld 標準出力に Hello World! と出力される

Slide 8

Slide 8 text

JVMを実装とは HelloWorld.java $ javac HelloWorld.java HelloWorld.class $ java HelloWorld 標準出力に Hello World! と出力される この部分を実装

Slide 9

Slide 9 text

JVMを実装とは To implement the Java Virtual Machine correctly, you need only be able to read the class file format and correctly perform the operations specified therein. The Java® Virtual Machine Specification: Chapter 2. The Structure of the Java Virtual Machine, https://docs.oracle.com/javase/specs/jvms/se13/html/jvms-2.html Java仮想マシン (JVM) を正しく実装するには、クラスファイル形式を読み 取って、そこに指定されている命令を正しく実行する必要があるだけです。

Slide 10

Slide 10 text

JVMを実装する クラスファイルを読み取って 書いてある命令を実行する機能を実装する

Slide 11

Slide 11 text

今回のお題

Slide 12

Slide 12 text

クラスファイルの読み取り The Java® Virtual Machine Specification: Chapter 4.1. The ClassFile Structure, https://docs.oracle.com/javase/specs/jvms/se13/html/jvms-4.html#jvms-4.1 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]; } u4: 符号なし4バイト量 u2: 符号なし2バイト量 ① ② ③ ③ ① ④ ② ④

Slide 13

Slide 13 text

クラスファイルの読み取り Constant Poolとは? ・実行時に他のクラス/インターフェース/インスタンスを呼び出すときに、直接 呼び出すのではなくコンスタントプール を参照する ・コンスタントプールは他のクラス/インターフェース/インスタンスへの参照を 持っている ・動的リンクに役立つ(毎回ロードしなくて済む) Class A Constant Pool コンスタントプール なし Class B Class B Class B Class B Class A コンスタントプール あり

Slide 14

Slide 14 text

クラスファイルの読み取り Constant Pool 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]; } The Java® Virtual Machine Specification: Chapter 4.1. The ClassFile Structure, https://docs.oracle.com/javase/specs/jvms/se13/html/jvms-4.html#jvms-4.1

Slide 15

Slide 15 text

クラスファイルの読み取り Constant Pool The Java® Virtual Machine Specification:Chapter 4.4. The Constant Pool, https://docs.oracle.com/javase/specs/jvms/se13/html/jvms-4.html#jvms-4.4 Tag Constant Kind 7 CONSTANT_Class 9 CONSTANT_Fieldref 10 CONSTANT_Methodref 11 CONSTANT_InterfaceMethodref 8 CONSTANT_String 3 CONSTANT_Integer 4 CONSTANT_Float 5 CONSTANT_Long 6 CONSTANT_Double 12 CONSTANT_NameAndType 1 CONSTANT_Utf8 15 CONSTANT_MethodHandle 16 CONSTANT_MethodType 17 CONSTANT_Dynamic 18 CONSTANT_InvokeDynamic 19 CONSTANT_Module 20 CONSTANT_Package cp_info { u1 tag; u1 info[]; }

Slide 16

Slide 16 text

クラスファイルの読み取り Constant Pool

Slide 17

Slide 17 text

クラスファイルの読み取り 同様にして 仕様を片手にクラスファイルをパースしていく 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]; } The Java® Virtual Machine Specification: Chapter 4.1. The ClassFile Structure, https://docs.oracle.com/javase/specs/jvms/se13/html/jvms-4.html#jvms-4.1

Slide 18

Slide 18 text

クラスファイルの読み取りはできた 次は読み取った内容を実行

Slide 19

Slide 19 text

Code_attribute { u2 attribute_name_index; u4 attribute_length; u2 max_stack; u2 max_locals; u4 code_length; u1 code[code_length]; u2 exception_table_length; { u2 start_pc; u2 end_pc; u2 handler_pc; u2 catch_type; } exception_table[exception_table_length]; u2 attributes_count; attribute_info attributes[attributes_count]; } method_info { u2 access_flags; u2 name_index; u2 descriptor_index; u2 attributes_count; attribute_info attributes[attributes_count]; } 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]; } 命令列の実行 どこに格納されてるのか? 05 3c b2 00 07 1b b6 00 0d b1

Slide 20

Slide 20 text

命令の形式 A Java Virtual Machine instruction consists of an opcode specifying the operation to be performed, followed by zero or more operands embodying values to be operated upon. This chapter gives details about the format of each Java Virtual Machine instruction and the operation it performs. The Java® Virtual Machine Specification: Chapter 6. The Java Virtual Machine Instruction Set, https://docs.oracle.com/javase/specs/jvms/se13/html/jvms-2.html 実行する操作を指定するオペコードと 0個以上の操作対象の値を指定するオペラン ド

Slide 21

Slide 21 text

命令の形式 例 12 0c オペランド オペコード Constant Poolの12番目から 値を取得する

Slide 22

Slide 22 text

命令列の構造 05 3c b2 00 07 1b b6 00 0d b1 iconst_2 `2`(int型の値)をオペラ ンドスタックにプッシ ュする istore_1 ローカル変数にint型の 値をストアする getstatic クラスのstaticフィールド (Rubyのクラス変数のような もの)を取得してオペランド スタックにプッシュする iload_1 ローカル変数からint型の 値をロードする invokevirtual メソッドを呼び出す return メソッドからvoid をリターンする オペランド オペランド

Slide 23

Slide 23 text

オペランドスタック?

Slide 24

Slide 24 text

命令列の実行 オペランドスタック ・命令列の実行時に用いられる ・計算すべき値や計算した値を保持するためのもの ・スタックというデータ構造 ・データを 後入れ先出し で保持する (Array#push, Array#pop) 1 1 1 2 2 1 2 1 push pop pop push

Slide 25

Slide 25 text

命令列の実行 iconst_2 `2`(int型の値)をオペランドスタックにプッシュする istore_1 ローカル変数配列の1番目にint型の値をストアする getstatic クラスのstaticフィールド(Rubyのクラス変数のような もの)を取得してオペランドスタックにプッシュする iload_1 ローカル変数配列の1番目からint型の値をロードする invokevirtual メソッドを呼び出す return メソッドからvoidをリターンする 2 java/lang/Syste m.out java/lang/Syste m.out 2 オペランドスタック ローカル変数配列 (0番目にはmainメソッド呼び出し 時の引数が入っている) 0x0007 Constant Poolの7番目 (java/lang/System.out) 0x000d Constant Poolの13番目 (println) [ [] ] [ [] ] [ [], 2 ] [ [], 2 ] [ [] ] [ [] ] [ [] ] 2 java/lang/Syste m.out 2

Slide 26

Slide 26 text

命令の実行 実装 具体的な実装はGitHubを見ていただけると https://github.com/daikimiura/merah/vm/frame.rb

Slide 27

Slide 27 text

デモ ・1+1の計算結果を出力 ・“Hello Heisei Ruby Kaigi!” を出力

Slide 28

Slide 28 text

デモ

Slide 29

Slide 29 text

パフォーマンス比較 Java HotSpot(TM) 64-Bit Server VM (build 13.0.1+9, mixed mode, sharing) ・user(ユーザーモードで動作した時間)が長い ・そもそも無駄な処理が走ってる? ・パフォーマンスの悪いコードを書いてしまった? ・Ruby vs C++ ? ・sys(カーネルモードで動作した時間)が長い ・無駄なファイル読み込みがある ・クラスファイルをパースするときにめっちゃ IO#read を呼んでる) 自作JVM (ruby 2.7.0-preview3)

Slide 30

Slide 30 text

今後やりたいこと ・セルフホスト ・JRubyが吐いた自作JVMのクラスファイルを自作JVMで評価する ・クラスのインスタンスの作成 ・ユーザー定義メソッドの呼び出し ・etc... ・JITコンパイル

Slide 31

Slide 31 text

まとめ ・言語処理系はパッと見難しそうだけど意外と入門できる ・必要最低限の機能だけ、最小限で実装していくと開発しやすい ・プロダクト開発/OSS開発に通ずる? ・低レイヤのエラーが怖くなくなった ・普段使わないRubyのメソッドを使えた (String#unpackとか) ・Rubyは書いてて楽しい! (A Programmer’s Best Friend)

Slide 32

Slide 32 text

おまけ or こちら (iOSのみ) アートをもっと楽しむためのアプリ PINTOR(ピントル) ダウンロードしていただけると嬉しいです!

Slide 33

Slide 33 text

参考資料 ・The Java® Virtual Machine Specification ・Java仮想マシン仕様 (The Java series) ・セルフホストで学ぶJVM入門 ・jjvm ・PHP で JVM を実装して Hello World を出力するまで ・php-java ・ferrugo ・rust-jvm