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

Rubinius Hacking Guide

More than 3 years have passed since last update.

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

特徴

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

実装の概要

Rubiniusのビルド

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

rbxコマンド起動によるmain()の実行

machine/drivers/cli.cppにrbx(1)のmain()がある

  • Environnmentのコンストラクタでargc, argvを保存
    • ここでrubinius::Stateも作られる。中にVMを保持しており、このStateインスタンスはいろんな場所で引き回される。STATE#define STATE rubinius::State* state
  • Environment#boot()を実行
    • bootstrap_ontologyで最初に必要なクラス、メソッド、オブジェクトを用意
    • load_argvでARG0, ARGVオブジェクトの読み込み
    • main用スレッドを起動。ここでJITとかも起動している
      • Thread::main_threadがload_coreを呼び、runtime/coreディレクトリにあるファイルをこのへんでsignature, data, index, initializeなど読み込む
        • ここで呼ばれてるThread::createはこれ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を叩く。実行中はこの行より後には行かない。
        • Rubinius::Loaderを取得する際に呼ばれるget_constの実装rubinius::Moduleが再帰的にsuperclassを探索する。
        • .new#mainを呼ぶ時のObject::sendの実装。allow_primitiveはデフォルトtrue。
          • 内部でDispatch::sendを呼び出す。これがmethod_missingとかをハンドルしている
          • method->executeで参照されているのはExecutableのexecuteフィールド。
          • executor型はtypedef Object* (*executor)(State*, Executable* exec, Module* mod, Arguments& args);なので、この型の関数を呼び出している

Rubinius::Loader#mainの実行

core/loader.rb

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

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

  • preamble
  • system_load_path
  • signals
  • run_compiled
  • load_compiler
    • こいつがコンパイル済のrubinius-code gemを読み込んでいる。ここで初めてRubiniusのランタイムにパーサ/コンパイラが出現する。
      • rubinius-code gem自体をコンパイルするのをどこでやってるかは読んでないけど多分ビルド時にやるのではないか
    • Kernel.#require実態はRubinius::CodeLoader.requireであり、load_fileRubinius::ToolSets::Runtime::Compilerを使うので、load_compilerをして初めてrequireできるようになる
      • 事実、これより前のメソッドはrequireをしていない
  • preload
  • detect_alias
  • options
  • 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::LoaderRubinius::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をバイトコードに変換し、実行する
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