mrubyを読む

はじめに

コードも生成できたので最後にコードを実行している部分を読みます。mrb_run()がエントリポイントになります。

mrubyVM概観

いきなりmrb_run()に入る前にmrubyVM*1がどんな実行モデルなのかについて説明します。

mrubyVMの実行モデルはレジスタマシンです。ちなみにYARVはスタックマシンです。レジスタマシンとスタックマシンの違いはWikipediaあたりをご参照ください。

例えば以下の単純なRubyスクリプトの場合、

def foo(a, b)
  a * b
end

f = foo(1, 2)
irep 116 nregs=6 nlocals=3 pools=0 syms=1
000 OP_TCLASS   R3
001 OP_LAMBDA   R4      I(117)  1
002 OP_METHOD   R3      'foo'
003 OP_LOADSELF R3
004 OP_LOADI    R4      1
005 OP_LOADI    R5      2
006 OP_LOADNIL  R6
007 OP_SEND     R3      'foo'   2
008 OP_MOVE     R1      R3
009 OP_STOP

irep 117 nregs=7 nlocals=5 pools=0 syms=1
000 OP_ENTER    2:0:0:0:0:0:0
001 OP_MOVE     R5      R1
002 OP_MOVE     R6      R2
003 OP_LOADNIL  R7
004 OP_SEND     R5      '*'     1
005 OP_RETURN   R5

f = foo(1, 2)の部分は以下のように実行されます。

  1. レジスタR3にselfをロード(レシーバを設定)
  2. レジスタR4に1をロード(引数を設定)
  3. レジスタR5に2をロード(引数を設定)
  4. レジスタR6にnilをロード(ブロック引数を設定)
  5. レジスタR3のオブジェクトに対して引数が2つである'foo'メソッドを実行(R(3+1)番目以降のレジスタの値がメソッド引数として使われます。詳しくは後で解説します)
  6. レジスタR1(ローカル変数f)にメソッド呼び出しの結果(R3に格納されます)を設定

mrubyではレジスタの確保場所としてスタックを使用しています。そのため、mrubyVMはスタックマシンであると勘違いしてしまう危険があるので注意してください。さわだもソースだけ見ていてスタックマシンだと勘違いしていました。

レジスタの確保場所としてスタックを使うとはどういうことかというと、以下のようなイメージです。(OP_SEND '*'実行直前の状態)

トップレベル実行時のスタックベース→| nil |top_self
                                    |     |ローカル変数fの格納領域
                                    | nil |よくわからない。特殊変数用?
 メソッドfoo実行時のスタックベース→| nil |'foo'のレシーバ
                                    |  1  |'foo'の引数1 & ローカル変数a
                                    |  2  |'foo'の引数2 & ローカル変数b
                                    | nil |'foo'に対するブロック引数
                                    | nil |よくわからない。特殊変数用?
                                    |  1  |'*'のレシーバ
                                    |  2  |'*'の引数1
                                    | nil |'*'に対するブロック引数

以上の前提を持ってmrb_run()に挑むと理解が深まると思います。

mrb_run(src/vm.c)

では、mrb_run()に見ていくことにしましょう。

(執筆中)


*1 Riteはコードネームだから今後はmrubyと呼んで、とまつもとさんがつぶやいてたのでRiteVMと呼ばずにmrubyVMと呼びます

トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS