ruby_init @ eval.c
初期化関数、ruby_init のメインな部分を抜き出してみました。
void ruby_init() { ... 略 ... Init_yarv(); Init_stack((void *)&state); Init_heap(); PUSH_TAG(PROT_NONE); if ((state = EXEC_TAG()) == 0) { rb_call_inits(); ruby_prog_init(); ALLOW_INTS; } POP_TAG_INIT(); ... 略 ... }
でもその前に、すぐあとのif文の条件が、はじめてRubyのソースを見る目には謎です。
PUSH_TAG(PROT_NONE); if ((state = EXEC_TAG()) == 0) { ... } POP_TAG_INIT();
うーん?EXEC_TAGの定義を見てみよう。
#define TH_EXEC_TAG() \ (FLUSH_REGISTER_WINDOWS, ruby_setjmp(_th->tag->buf)) #define EXEC_TAG() \ TH_EXEC_TAG()
ruby_setjmpはsetjmpか_setjmpの#defineでした。ということで、ここではsetjmpが呼ばれているみたいです。setjmpは、最初は0を返して、中でlongjmpが呼ばれると0以外の値で戻ってくるはずです。
if ((state = EXEC_TAG()) == 0) { // ※ // 最初はEXEC_TAG==setjmpが0を返すので、ここに来る ... // この辺りでエラーが起きたときは中でlongjmpが呼ばれる // すると※にジャンプして EXEC_TAG が 0 以外を返すので ... } // ここに処理がうつることになる
これは、begin〜rescue みたいな例外処理を、C言語でやってるってことでしょう。例外状態まで気にすると大変そうなので、ひとまずここの if (...) は見なかったことにして先に進みます。エラーなんて起きません!
(※ Ruby Hacking Guideをちょっと見てみたところ、この ***_TAG でいろいろジャンプする「ジャンプタグ」という仕組みはRuby評価期ではたっぷりと使われているそうです。YARVでどうなっているのかはまだわかりません。使っていそうだったら、その時にまた戻ってきて調べようと思います。)
さて、ruby_initが呼び出す初期化ルーチンのうち、YARVに直接関係ありそうな部分は二つしかありません。最初に呼ばれているInit_yarvと、rb_call_initsの中で呼ばれているInit_yarvcoreです。
- Init_yarv
- rb_call_inits
- ...
- Init_yarvcore
- ...
他はふつうのRubyの組み込みオブジェクトやモジュールの初期化っぽかったので、この勉強会ではどんどん飛ばしてっちゃいます。