Rubinius Hacking Guide

Rubiniusをhackしたことは特にないが、中を軽く読んでおこうという気持ちになったので読んだ内容をメモしておく。今日初めて読んだ状態なのであまり参考にならないかもしれない。

## 歴史
2007あたり開発開始

### v1 (2010)
http://www.rubyinside.com/the-why-what-and-how-of-rubinius-1-0-s-release-3261.html

- 「Rubyで実装されたRuby」
- バイトコード仮想マシンはC++/LLVM
- LLVM JIT
- 世代別GC
- JRubyに次ぐ3番目にRailsが走ったRuby処理系

### v2 (2013)
https://github.com/rubinius/rubinius-archive/blob/cf54187d421275eec7d2db0abd5d4c059755b577/_posts/2013-10-04-rubinius-2-0-released.markdown

- 当時まだ出ていなかったRuby 2.1互換
- GILなし、マルチスレッドサポートの向上
- JITにより2-4倍程度まで高速化を達成

### v3 (2016)
https://github.com/rubinius/rubinius-archive/blob/cf54187d421275eec7d2db0abd5d4c059755b577/_posts/2016-01-07-rubinius-3-0-the-third-epoch.markdown

- ビルドにclang/clang++しかサポートしなくなった
- AOTコンパイルによるバイナリ生成もサポート
- 実行時にRubyを使わないようにした
- [JITはバグが多かったため取り除かれた](https://github.com/rubinius/rubinius/issues/3718#issuecomment-268347165)

## 特徴
- 2017/4/12時点ではRuby 2.3.1をターゲットにしている
- バイトコード仮想マシンはC++で実装されている
  - 標準ライブラリはRubyで実装されている
  - C拡張向けにC APIはある
- GILなしでネイティブスレッドを使える
- continuation, ripper, tracepoint, tracerなどの標準ライブラリをサポートしない
- refinementsと$SAFEもサポートしない
- Windowsをサポートしていない

# 実装の概要
## Rubiniusのビルド
- [core rake task](https://github.com/rubinius/rubinius/blob/801470a89f968c9512a2ed654fcc5c8eef97de24/rakelib/core.rake#L64-L303)でリポジトリ直下の`core/`ディレクトリに入っているRubyのコードを、同ファイル内の`CodeDBCompiler.compile`がコンパイルする
  - 内部的には`Rubinius::ToolSets::Build::Compiler`を使ってコンパイルしている
     - このクラスは別リポジトリ[rubinius/rubinius-code](https://github.com/rubinius/rubinius-code)にgemがあってCRubyのgemとしてC++ extensionがビルドされ実行される。
         - CRubyって書いてるけど既にrbxのバイナリがあればbootstrapはできるかもしれない
     - パーサー、コンパイラはrubinius-codeの方のリポジトリを見る必要がある

## rbxコマンド起動によるmain()の実行
[machine/drivers/cli.cpp](https://github.com/rubinius/rubinius/blob/801470a89f968c9512a2ed654fcc5c8eef97de24/machine/drivers/cli.cpp#L33-L137)にrbx(1)の`main()`がある

- Environnmentのコンストラクタでargc, argvを保存
  - ここで[rubinius::Stateも作られる](https://github.com/rubinius/rubinius/blob/801470a89f968c9512a2ed654fcc5c8eef97de24/machine/environment.cpp#L101)。中にVMを保持しており、このStateインスタンスはいろんな場所で引き回される。`STATE`は`#define STATE rubinius::State* state`。
- [Environment#boot()](https://github.com/rubinius/rubinius/blob/801470a89f968c9512a2ed654fcc5c8eef97de24/machine/drivers/cli.cpp#L49)を実行
  - [bootstrap_ontology](https://github.com/rubinius/rubinius/blob/801470a89f968c9512a2ed654fcc5c8eef97de24/machine/ontology.cpp#L275-L329)で最初に必要なクラス、メソッド、オブジェクトを用意
  - [load_argv](https://github.com/rubinius/rubinius/blob/801470a89f968c9512a2ed654fcc5c8eef97de24/machine/environment.cpp#L735)でARG0, ARGVオブジェクトの読み込み
  - [main用スレッドを起動](https://github.com/rubinius/rubinius/blob/801470a89f968c9512a2ed654fcc5c8eef97de24/machine/environment.cpp#L741-L742)。ここでJITとかも起動している
     - Thread::main_threadが[load_core](https://github.com/rubinius/rubinius/blob/801470a89f968c9512a2ed654fcc5c8eef97de24/machine/class/thread.cpp#L316)を呼び、`runtime/core`ディレクトリにあるファイルを[このへんでsignature, data, index, initializeなど](https://github.com/rubinius/rubinius/blob/801470a89f968c9512a2ed654fcc5c8eef97de24/machine/class/code_db.cpp#L71-L130)読み込む
         - ここで呼ばれてるThread::createは[これ](https://github.com/rubinius/rubinius/blob/801470a89f968c9512a2ed654fcc5c8eef97de24/machine/class/thread.cpp#L85-L87)。`typedef Object* (*ThreadFunction)(STATE);`のため。
         - 読み込む、というのは、Rubiniusのビルド時にruntime/core以下に吐いていたものは`rubinius::CompiledCode`(Rubiniusのバイトコード表現だと思われる)をシリアライズしたもの(ビルド後runtime/core/dataを見ればわかるが、バイナリではない)で、これをデシリアライズ後実行している。
              - その後`code->execute_script(state)`で実行するが、`code->execute`の実態をどこでセットしているかはTODO
              - おそらくRubiniusのバイトコードをインタプリタ実行する
     - [Rubinius::Loaderをinstantiateして#mainを叩く](https://github.com/rubinius/rubinius/blob/801470a89f968c9512a2ed654fcc5c8eef97de24/machine/class/thread.cpp#L326-L344)。実行中はこの行より後には行かない。
         - `Rubinius::Loader`を取得する際に呼ばれる[get_constの実装](https://github.com/rubinius/rubinius/blob/801470a89f968c9512a2ed654fcc5c8eef97de24/machine/class/module.cpp#L158-L190)。`rubinius::Module`が再帰的にsuperclassを探索する。
         - `.new`や`#main`を呼ぶ時の[`Object::send`の実装](https://github.com/rubinius/rubinius/blob/801470a89f968c9512a2ed654fcc5c8eef97de24/machine/class/object.cpp#L535-L544)。allow_primitiveはデフォルトtrue。
              - 内部で[Dispatch::send](https://github.com/rubinius/rubinius/blob/801470a89f968c9512a2ed654fcc5c8eef97de24/machine/dispatch.cpp#L20-L42)を呼び出す。これがmethod_missingとかをハンドルしている
              - `method->execute`で参照されているのはExecutableのexecuteフィールド。
              - executor型は`typedef Object* (*executor)(State*, Executable* exec, Module* mod, Arguments& args);`なので、この型の関数を呼び出している

## `Rubinius::Loader#main`の実行
[core/loader.rb](https://github.com/rubinius/rubinius/blob/801470a89f968c9512a2ed654fcc5c8eef97de24/core/loader.rb#L844-L872)

いろいろメソッドを呼び出すが、読んだところだけコメントする。

なお、このクラスはRubiniusのビルド時にCRuby + rubinius-code gemによってコンパイルされているので、このクラスの実行時点でRubinius自体がRuby言語のパーサやコンパイラを持っている必要はない

- preamble
- system_load_path
- signals
- run_compiled
  - `ENV["RBX_RUN_COMPILED"]`が指定されてなければスキップされるのであまり気にしなくて良い
  - ARGVを全て[`Rubinius.run_script`にかける](https://github.com/rubinius/rubinius/blob/801470a89f968c9512a2ed654fcc5c8eef97de24/core/code_loader.rb#L233)。
     - `Rubinius.run_script`は[`Rubinius.primitive :vm_run_script`](https://github.com/rubinius/rubinius/blob/801470a89f968c9512a2ed654fcc5c8eef97de24/machine/class/system.cpp#L1788-L1796)を呼び出す。`System::vm_run_script`が呼び出す[実態はこれ](https://github.com/rubinius/rubinius/blob/801470a89f968c9512a2ed654fcc5c8eef97de24/machine/machine_code.cpp#L830-L874)で、Rubiniusのバイトコードを実行するのではないか
- load_compiler
  - こいつが[コンパイル済のrubinius-code gemを読み込んでいる](https://github.com/rubinius/rubinius/blob/801470a89f968c9512a2ed654fcc5c8eef97de24/core/code_loader.rb#L566-L583)。ここで初めてRubiniusのランタイムにパーサ/コンパイラが出現する。
     - rubinius-code gem自体をコンパイルするのをどこでやってるかは読んでないけど多分ビルド時にやるのではないか
  - `Kernel.#require`の[実態は`Rubinius::CodeLoader.require`](https://github.com/rubinius/rubinius/blob/801470a89f968c9512a2ed654fcc5c8eef97de24/core/kernel.rb#L850-L852)であり、`load_file`が`Rubinius::ToolSets::Runtime::Compiler`を使うので、`load_compiler`をして初めてrequireできるようになる
     - 事実、これより前のメソッドはrequireをしていない
- preload
- detect_alias
- options
  - 指定したスクリプトが[`@script`に入る](https://github.com/rubinius/rubinius/blob/801470a89f968c9512a2ed654fcc5c8eef97de24/core/loader.rb#L227)
- load_paths
- deprecations
- rubygems
- gemfile
  - `-G`オプションで`require 'bundler/setup'`するっぽい
- debugger
- profiler
- requires
  - `-r`オプションの処理
- evals
  - `-e`オプションの処理
- script
  - optionsで処理した`@script`をRubyのスクリプトのファイル名として、`CodeLoader.load_script`を呼び出す
     - 前述した`load_file`がここでも使われ、その後Rubiniusのバイトコードを`Rubinius.run_script`する
  - これがよく使う`ruby foo.rb`みたいなメインの処理と思われる
- repl

## まだ読めていないもの

- CompiledCodeや、core/dataからデシリアライズしたメソッドのexecuteをどこでセットしてるか
- Rubiniusのバイトコードインタプリタがどうやってコードを実行するのか

# まとめ
RubiniusがRubyのスクリプトを読み込んで実行するまで:

- Rubiniusのビルド時に、ランタイム用にRuby言語で記述された`Rubinius::Loader`や`Rubinius::CodeLoader`をCRubyとrubinius-code gemでコンパイルし、Rubiniusのバイトコードインタプリタが理解できる状態にしておく
- rbxコマンドが実行されると、argc, argvからARGVを作り、VMの準備後メインのスレッドが立ち上がり、そのスレッドでコンパイル済のバイトコードを実行することで`Rubinius::Loader#main`が呼ばれる
- `Rubinius::Loader#main`は`#load_compiler`を実行し、CRubyによってコンパイル済のrubinius-code gem(Rubinius用のRuby言語のパーサ、コンパイラ)を読み込む
- `Rubinius::Loader#options`で読み込むスクリプトのファイル名をARGVから取得し、rubinius-code gemに入っている`Rubinius::ToolSets::Runtime::Compiler`がRubyをバイトコードに変換し、実行する