Save 37% off PRO during our Black Friday Sale! »

RubyでJVMを実装してみる / Implement JVM with Ruby

E4159c65ac8decc882bfaf10088bd07e?s=47 Daiki Miura
December 14, 2019

RubyでJVMを実装してみる / Implement JVM with Ruby

E4159c65ac8decc882bfaf10088bd07e?s=128

Daiki Miura

December 14, 2019
Tweet

Transcript

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

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

    $ whoami
  3. きっかけ

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

  5. Rubyでもできるはず!

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

  7. JVMを実装とは HelloWorld.java $ javac HelloWorld.java HelloWorld.class $ java HelloWorld 標準出力に

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

    Hello World! と出力される この部分を実装
  9. 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) を正しく実装するには、クラスファイル形式を読み 取って、そこに指定されている命令を正しく実行する必要があるだけです。
  10. JVMを実装する クラスファイルを読み取って 書いてある命令を実行する機能を実装する

  11. 今回のお題

  12. クラスファイルの読み取り 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バイト量 ① ② ③ ③ ① ④ ② ④
  13. クラスファイルの読み取り Constant Poolとは? ・実行時に他のクラス/インターフェース/インスタンスを呼び出すときに、直接 呼び出すのではなくコンスタントプール を参照する ・コンスタントプールは他のクラス/インターフェース/インスタンスへの参照を 持っている ・動的リンクに役立つ(毎回ロードしなくて済む) Class

    A Constant Pool コンスタントプール なし Class B Class B Class B Class B Class A コンスタントプール あり
  14. クラスファイルの読み取り 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
  15. クラスファイルの読み取り 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[]; }
  16. クラスファイルの読み取り Constant Pool

  17. クラスファイルの読み取り 同様にして 仕様を片手にクラスファイルをパースしていく 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
  18. クラスファイルの読み取りはできた 次は読み取った内容を実行

  19. 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
  20. 命令の形式 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個以上の操作対象の値を指定するオペラン ド
  21. 命令の形式 例 12 0c オペランド オペコード Constant Poolの12番目から 値を取得する

  22. 命令列の構造 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 をリターンする オペランド オペランド
  23. オペランドスタック?

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

    1 1 1 2 2 1 2 1 push pop pop push
  25. 命令列の実行 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
  26. 命令の実行 実装 具体的な実装はGitHubを見ていただけると https://github.com/daikimiura/merah/vm/frame.rb

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

  28. デモ

  29. パフォーマンス比較 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)
  30. 今後やりたいこと ・セルフホスト ・JRubyが吐いた自作JVMのクラスファイルを自作JVMで評価する ・クラスのインスタンスの作成 ・ユーザー定義メソッドの呼び出し ・etc... ・JITコンパイル

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

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

  33. 参考資料 ・The Java® Virtual Machine Specification ・Java仮想マシン仕様 (The Java series)

    ・セルフホストで学ぶJVM入門 ・jjvm ・PHP で JVM を実装して Hello World を出力するまで ・php-java ・ferrugo ・rust-jvm