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

  • このエントリーをはてなブックマークに追加

前回 [http://www.koooge.com/entry/2017/01/02/231507]からの続き、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 typedefでvm_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で定義されているようですが・・。

続きは次回 [http://www.koooge.com/entry/2017/01/12/233500]にします!

スポンサードリンク [asin:4274050653:detail]