ここではRuby1.9の初期化を読解したいと思います。
ruby_init関数はmain関数から呼び出されます。この関数の実行が終わるとRubyのオブジェクトスペースの構築が完了します。
ruby_init関数から呼ばれている関数の中でYARVに関係ありそうなものとしてInit_BareVM関数があるので見てみましょう。 Init_BareVM関数ではruby_current_vmとruby_current_threadが設定されています。vmを初期化するvm_init2関数はMEMZEROしてるだけですが、threadを初期化するth_init2関数はスタックを割り当てvm_push_frame関数を呼ぶことでフレーム情報を設定しています。vm_push_frame関数はvm_insnhelper.cに、各種構造体定義はvm_core.hに書かれています。
フレーム情報を設定するあたりを詳しく見てみましょう。
th_init2(rb_thread_t *th) { ... th->cfp = (void *)(th->stack + th->stack_size); vm_push_frame(th, 0, FRAME_MAGIC_TOP, Qnil, 0, 0, th->stack, 0, 1);
vm_push_frame(rb_thread_t *th, rb_iseq_t *iseq, VALUE type, VALUE self, VALUE specval, VALUE *pc, VALUE *sp, VALUE *lfp, int local_size) { ... /* nil initialize */ for (i=0; i < local_size; i++) { *sp = Qnil; sp++; } /* set special val */ *sp = GC_GUARDED_PTR(specval); dfp = sp; if (lfp == 0) { lfp = sp; } cfp = th->cfp = th->cfp - 1; cfp->pc = pc; cfp->sp = sp + 1; cfp->bp = sp + 1; cfp->iseq = iseq; cfp->flag = type; cfp->self = self; cfp->lfp = lfp; cfp->dfp = dfp; cfp->proc = 0;
vm_push_frame関数を呼び出す前のスタックはこんな感じ。
stack→| | ... | | cfp→
vm_push_frame関数を呼び出した後のスタックはこんな感じ。
stack→|Qnil dfp,lfp→|GC_GUARDED_PTR(0) sp,bp→| | ... | cfp→|フレーム情報
というわけでスタックは上から使われていき下に伸びる、フレーム情報はスタックの下から使われていき上に伸びるということなようです。
余談ですがマクロ化されてる処理もあればマクロ化されてない処理もあって統一性があれです:-)
マクロを展開するとEXEC_TAGではsetjmp関数が実行されることになります。つまり、一回目の実行では0が返りifの中身が実行されることになります。 それ以上のことについては、とりあえず「なんかエラーなことが起きたらここに飛んでくる」ぐらいに考えておいてください。詳しくは実行処理のところで解説します。
rb_call_inits関数では組込クラスなどの初期化が行われています。初期化順序の関係でYARV周りの初期化もたまに行われているので該当部分を眺めましょう。
まずInit_Object関数(object.c)の後にInit_top_self関数が呼ばれ、トップレベルオブジェクト*1が作成されています。短いので取得メソッドも込みで貼っておきます。
void Init_top_self() { rb_vm_t *vm = GET_VM(); vm->top_self = rb_obj_alloc(rb_cObject); rb_define_singleton_method(rb_vm_top_self(), "to_s", main_to_s, 0); }
VALUE rb_vm_top_self() { return GET_VM()->top_self; }
(位置を直すべきと書いてますが)Init_eval関数にてruby_current_vmのmark_object_aryが初期化されてます。名前的にGCされると困るオブジェクトを登録しておくものなようです。
grepをかけてみるとmark_object_aryはrb_register_mark_object関数(gc.c)で使われています。
rb_register_mark_object(VALUE obj) { VALUE ary = GET_THREAD()->vm->mark_object_ary; rb_ary_push(ary, obj); }
でもってmark_object_aryはrb_vm_mark関数(vm.c)でマークされています。rb_vm_mark関数はruby_current_vmのマーク関数なのでまた後で見ることにしましょう。
Init_load関数にてruby_current_vmのloaded_features(ロードされてるライブラリのリスト。要するに$")が初期化されています。YARVになってruby_current_vmのメンバーになったようです。Ruby1.9になって今までeval.cに詰め込み過ぎだった各関数・変数がいろんなファイルに分割されているようです。
Init_VM関数では以下のことが行われています。
これ以外にフレームに命令コードが設定されていますが、ダミーなのでまた後で本物が設定されるときにまじめに見ることにしましょう。
Init_Thread関数では以下のことが行われています。一部の処理はthread_pthread.cもしくはthread_win32.cに書かれています。
ちなみに、staticなはずのnative_mutex_initialize関数とかが何故呼べるのかと思ったらcファイルをincludeしてますね:-)
YARVではないですがm17nもRuby1.9のメイン機能らしいので見ることにしましょう。 まずエンコーディングテーブルですがいつの間にか初期化されています。いつ初期化されたのかを見ていくと、
inits.c
rb_call_inits() { Init_sym();
parse.y
Init_sym(void) { ... rb_intern2("", 0);
rb_intern2(const char *name, long len) { return rb_intern3(name, len, rb_enc_from_index(0)); }
encoding.c
rb_enc_from_index(int index) { if (!enc_table) { rb_enc_init();
というわけでruby_call_inits関数の初めにこっそり初期化されています。
ここではRuby1.9の初期化のうちYARV周りを中心に見てきました。どうやらポイントとなる変数は
のようです。それでは続いてスクリプトの解析に進みましょう。