ワナビーエンジニアのブログ

なんでもいいから文化的な生活を送りたい

Rubyのメソッド呼び出しの流れ(2)

前回からの続き、rb_call_cache構造体を見ていきます。 コードの後に説明書くと分かりにくかったので、行ごとにある程度コメントを書いてみます。

rb_call_cacheはvm_core.hにあります。

237struct rb_call_cache;
238typedef VALUE (*vm_call_handler)(struct rb_thread_struct *th, struct rb_control_frame_struct *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc);
239
240struct rb_call_cache {
241    /* inline cache: keys */  // rubyのインラインメソッドキャッシュに渡すKVを表現してると思われる
242    rb_serial_t method_state;
243    rb_serial_t class_serial;
244
245    /* inline cache: values */
246    const rb_callable_method_entry_t *me; // この構造体のメタ情報を持つテーブルへのポインタ
247
248    vm_call_handler call; // 目的のselect!のhandler。rb_ary_select_bangへの関数ポインタが格納されるはず
249
250    union {  // メタ情報っぽい
251    unsigned int index; /* used by ivar */
252    enum method_missing_reason method_missing_reason; /* used by method_missing */
253    int inc_sp; /* used by cfunc */
254    } aux;
255};

238 typedefvm_call_handler~~をVALUE型の別名として定義してます。 そして248 vm_call_handler call;でメンバcallとして定義。 cm_call_handlerの引数でrb_call_cacheを渡しているので、rb_call_cacheを2回定義しているように見えますが、 これは自己参照ですかね?こういう書き方もあるんですね_〆(´ρ`⊂⌒`っ

さて、rb_call_cache.callについて見ていたらvm_call_andler型だったというところまでわかりました。 では、次にcallにrb_ary_select_bangの関数ポインタを渡しているのは結局誰なんでしょうか。

send関数に戻り辿ってみたところ、vm_insnhelper.cのvm_search_methodにそれらしい記述がありました。 ここは前回も見た処理ですが再度スニペットを張ります。

1218vm_search_method(const struct rb_call_info *ci, struct rb_call_cache *cc, VALUE recv)
1219{
1220    VALUE klass = CLASS_OF(recv);
1221
1222#if OPT_INLINE_METHOD_CACHE // インラインメソッドキャッシュヒットした時の処理
1223    if (LIKELY(GET_GLOBAL_METHOD_STATE() == cc->method_state && RCLASS_SERIAL(klass) == cc->class_serial)) {
1224   /* cache hit! */
1225   VM_ASSERT(cc->call != NULL);
1226   return;
1227    }
1228#endif
1229
1230    cc->me = rb_callable_method_entry(klass, ci->mid);
1231    VM_ASSERT(callable_method_entry_p(cc->me));
1232    cc->call = vm_call_general; // ★ここでrb_call_cache.callに代入されている
1233#if OPT_INLINE_METHOD_CACHE
1234    cc->method_state = GET_GLOBAL_METHOD_STATE();
1235    cc->class_serial = RCLASS_SERIAL(klass);
1236#endif
1237}

どうやらvm_call_generalにrb_ary_select_bangの関数ポインタが入っていそうです。 vm_call_generalを追います。 同じくvm_insnhelper.cに書かれています。

2305static VALUE
2306vm_call_general(rb_thread_t *th, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc)
2307{
2308    return vm_call_method(th, reg_cfp, calling, ci, cc); // 実装はvm_call_method
2309}

/* vm_call_methodを追う */

2257static inline VALUE
2258vm_call_method(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc)
2259{
2260    VM_ASSERT(callable_method_entry_p(cc->me));
2261
2262    if (cc->me != NULL) { // rb_call_cacheで定義されていたコールバックがnullでなければ
2263   switch (METHOD_ENTRY_VISI(cc->me)) { // どうやら可視性を判断できるマクロ。cc->meは何ビット目だかに可視性を持っているみたい
2264     case METHOD_VISI_PUBLIC: /* likely */ // publicなメソッド
2265       return vm_call_method_each_type(th, cfp, calling, ci, cc); // 実体はこれ
2266
2267     case METHOD_VISI_PRIVATE: // privateメソッド
/* snip */
2278     case METHOD_VISI_PROTECTED: // protectedメソッド?
/* snip */
2296     default:
2297       rb_bug("unreachable");
2298   }
2299    }
2300    else {
2301   return vm_call_method_nome(th, cfp, calling, ci, cc); // これがあのNoMethodErrorっぽい
2302    }
2303}

可視性はpublicと思われるので、気にせず vm_call_method_each_typeをみていきます。

2134static VALUE
2135vm_call_method_each_type(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc)
2136{
2137    switch (cc->me->def->type) { // メソッドのタイプが設定されているようだ。
2138      case VM_METHOD_TYPE_ISEQ:
              /* 処理 */
2142      case VM_METHOD_TYPE_NOTIMPLEMENTED:
2143      case VM_METHOD_TYPE_CFUNC:
              /* 処理 */
2147      case VM_METHOD_TYPE_ATTRSET:
              /* 処理 */
2154      case VM_METHOD_TYPE_IVAR:
              /* 処理 */
2161      case VM_METHOD_TYPE_MISSING:
              /* 処理 */
2166      case VM_METHOD_TYPE_BMETHOD:
              /* 処理 */
2170      case VM_METHOD_TYPE_ALIAS:
              /* 処理 */
2175      case VM_METHOD_TYPE_OPTIMIZED:
              /* 処理 */
2188      case VM_METHOD_TYPE_UNDEF:
              /* 処理 */
2191      case VM_METHOD_TYPE_ZSUPER:
              /* 処理 */
2194      case VM_METHOD_TYPE_REFINED: {
              /* 処理 */
2237}

メソッドのtypeらしきVM_METHOD_TYPE_xxというもので、処理が分かれています。 メソッドのtypeってなんだろう(´・ω・` )?

method.hにrb_method_type_tというenumで定義されているようですが・・。

続きは次回にします!

スポンサードリンク

Rubyのしくみ -Ruby Under a Microscope-

Rubyのしくみ -Ruby Under a Microscope-