[[mrubyを読む]] #contents *はじめに [#bdd53068] コードも生成できたので最後にコードを実行している部分を読みます。mrb_run()がエントリポイントになります。 *mrubyVM概観 [#p63559f8] いきなりmrb_run()に入る前にmrubyVM((Riteはコードネームだから今後はmrubyと呼んで、とまつもとさんがつぶやいてたのでRiteVMと呼ばずにmrubyVMと呼びます))がどんな実行モデルなのかについて説明します。 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)の部分は以下のように実行されます。 +レジスタR3にselfをロード(レシーバを設定) +レジスタR4に1をロード(引数を設定) +レジスタR5に2をロード(引数を設定) +レジスタR6にnilをロード(ブロック引数を設定) +レジスタR3のオブジェクトに対して引数が2つである'foo'メソッドを実行(R(3+1)番目以降のレジスタの値がメソッド引数として使われます。詳しくは後で解説します) +レジスタ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) [#s87bb89e] では、mrb_run()に見ていくことにしましょう。 (執筆中)