monthly gimite

試験運用中。

[ruby][js][hotruby][hrwt] HotRuby (RubyVM on JavaScript) + Rubinius

相変わらずHotRubyをちょこちょこいじっています

元のHotRubyには組み込みクラスのメソッドがほとんど実装されていません。ちょこちょこと追加していたのですが、Rubyの組み込みクラス(StringとかArrayとか)はメソッドがいっぱいあって、全部JavaScriptで再実装するのはかなり面倒です。そこでRubiniusを使うことを思いつきました。RubiniusはRubyの別実装で、Rubinius自体が極力Rubyで実装されているのが特徴です。組み込みクラスの機能も大半がRubyで書かれているので、これを持ってくれば、組み込みクラスのメソッドを一気に実装できるというわけです。新しいRuby処理系を作ろうという人(はそんなにいない気がしますが)には朗報?

現在、この方法を使って、以下のクラスの大半のメソッドが動くようになっています。*1

  • Integer, String, Array, Hash, Range, Regexp, Exception, Comparable, Enumerable

デモサイトがあるので、お試しください。ソースコードはこちら

問題点としては、Rubyで実装されているので、非常に遅いです。特に、初期化時に時間がかかります(それなりの量のRubyスクリプトを読み込むことになるので)。この辺はそのうちどうにかする予定です。あと、諸事情で「Rubiniusから引っ張ってきて全部食わせるだけで全メソッドが実装完了」とまではいかないのが実情です(後述)。

以下もうちょっと細かい話。

Rubiniusの組み込みクラスの実装は kernel/bootstrapkernel/common*2 の2箇所にあります。ドキュメントによれば

  • kernel/bootstrap: Rubiniusの独自機能で書かれた最小の実装*3
  • kernel/common: 上記の最小の実装を使って、Rubinius非依存な形で実装した、フル機能の組み込みクラス

とのことなので、基本的には

  • kernel/bootstrapが実装しているメソッドをJavaScriptで実装
  • kernel/commonのスクリプトをそのままHotRuby初期化時に実行

という流れです。ただ、実際には

  • kernel/commonの中にもRubiniusの独自機能(MethodContext, Ruby.primitiveなど)を使って書かれている部分があって、そこは書き直す必要がある(e.g. Kernel#raise)
  • RubiniusだとStringはByteArrayという別クラスを使って実装するようになってるんですが、HotRubyとしてはStringが直接JavaScriptの文字列を持つようにしたほうが楽、とかいう場合は色々書き直す必要がある
  • 色々Rubiniusの流儀にあわせるよりはネイティブで実装してしまったほうが楽なメソッドもある(e.g. Kernel#lambda)
  • Rubiniusは確か1.8ベースなので、1.9で追加/変更されたメソッドは実装されていない

とか色々あって、そこそこ手を入れる必要があります。が、それでも全部JavaScriptで実装するよりはだいぶ楽だったと思います。

*1:まだ色々バグが残ってるとは思いますが。

*2:僕がインポートしたときはkernel/coreという名前でしたが…。

*3:実際、ほとんどのメソッドはRuby.primitiveというメソッドを使ってCの実装を呼び出しているだけです。