コードも生成できたので最後にコードを実行している部分を読みます。mrb_run()がエントリポイントになります。
いきなり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)の部分は以下のように実行されます。
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()に見ていくことにしましょう。
(執筆中)