Rubyのコードを読んでみよう(オブジェクト編)
Upcoming SlideShare
Loading in...5
×
 

Rubyのコードを読んでみよう(オブジェクト編)

on

  • 566 views

 

Statistics

Views

Total Views
566
Views on SlideShare
566
Embed Views
0

Actions

Likes
1
Downloads
1
Comments
0

0 Embeds 0

No embeds

Accessibility

Categories

Upload Details

Uploaded via as OpenOffice

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

Rubyのコードを読んでみよう(オブジェクト編) Rubyのコードを読んでみよう(オブジェクト編) Presentation Transcript

  • Rubyのコードを読んでみよう:その2
  • さて今日のお題ですが
  • コレです
  • Rubyには他の代表的な言語にない黒魔術的なメソッドがありますがその殆どはrubyのクラスの内側に挟み込まれて存在しています
  • 具体的には● モンキーパッチング● eigenclass(self)● Kernel#define_method● Object#extend● Kernel#include● Object#method_missoingEtc...
  • これらの機能はrubyのruby的なrubyたる、オブジェクト指向と深く結びついています
  • 今回のお題これらを手繰りながらrubyの● クラスって何?● オブジェクトって何?● メソッドって何?” ”という 定義 を辿って行きましょう
  • 細かいこと● 解析するversionはruby2.0.0-p0でやりますよ!(1.9系と劇的に違ったりはしていないんですけどね)
  • Stage1: Classをnewしてみる
  • クラスの作成(class.c)スタートはrb_class_new→rb_class_bootを呼び出してここで初期化しているVALUErb_class_new(VALUE super){Check_Type(super, T_CLASS);rb_check_inheritable(super);return rb_class_boot(super);}
  • rb_class_bootは継承先、継承元の設定をしているだけとっても素直な作り。VALUE rb_class_boot(VALUE super){VALUE klass = class_alloc(T_CLASS, rb_cClass);RCLASS_SUPER(klass) = super;RCLASS_M_TBL(klass) = st_init_numtable();OBJ_INFECT(klass, super);return (VALUE)klass;}
  • マクロを展開していくと、RClassなるものが浮いてくるVALUE rb_class_boot(VALUE super){VALUE klass = class_alloc(T_CLASS, rb_cClass);R_CAST((RClass)(klass))->ptr->iv_tbl = super;R_CAST((RClass)(klass))->m_tbl = st_init_numtable();OBJ_INFECT(klass, super);return (VALUE)klass;}
  • Rclass!! クラスの正体はここにある(rubyのオブジェクトを理解するにはここを起点にすると良い)struct RClass {struct RBasic basic;rb_classext_t *ptr;struct st_table *m_tbl; // メソッド関係一覧struct st_table *iv_index_tbl; // インスタンス変数一覧};
  • そして、メソッドは、実はforループで探索して呼び出されているint st_lookup(st_table *table, register st_data_t key, st_data_t *value){...st_index_t i;for (i = 0; i < table->num_entries; i++) {if ((st_data_t)table->bins[i*2] == key) {if (value !=0) *value = (st_data_t)table->bins[i*2+1];return 1;}}...
  • まとめ:その1● クラスはCの構造体● 抑えておくべきデータ型はクラスの本体RClassこれを作っている
  • Stage2: methodをdefineしてみる
  • Kernel#define_methodの解説● define_methodの定義はproc.cにあるのでそこから順番に手繰る● rb_define_private_method(rb_cModule, "define_method",rb_mod_define_method, -1); // proc.c● rb_mod_define_method(int argc, VALUE *argv, VALUE mod)● rb_method_entry_set(mod, id, method->me, noex); // vm_method.c● rb_method_entry_make(VALUE klass, ID mid, rb_method_type_t type,rb_method_definition_t *def, rb_method_flag_t noex)● st_insert(register st_table *table, register st_data_t key, st_data_t value)
  • st_insert(register st_table *table, register st_data_t key, st_data_t value)このメソッドの、st_tableって構造体…何処かで見たような気が
  • メソッドの挿入をしているst_insert● st_insert(register st_table *table, register st_data_t key, st_data_t value)このメソッドの、st_tableって構造体…何処かで見たような気がstruct RClass {struct RBasic basic;rb_classext_t *ptr;struct st_table *m_tbl; // メソッド関係一覧struct st_table *iv_index_tbl; // インスタンス変数一覧};
  • st_insert解読st_insertの定義は呼び出し先の挙動を見たほうが早い実はループで重複を探して、重複したものが無ければメソッド挿入をしているだけint st_insert(register st_table *table, register st_data_t key, st_data_t value){...for (i = 0; i < table->num_entries; i++) {if ((st_data_t)table->bins[i*2] == key) {table->bins[i*2+1] = (struct st_table_entry*)value;return 1;}..}
  • まとめ2メソッドは構造体st_tableの内側に、配列として格納されている
  • Stage3: モジュールのinclude
  • Kernel#include,Object#extend● まずObject#extendの呼び出しを手繰ってみる以下は呼び出し順rb_define_method(rb_mKernel, "extend", rb_obj_extend, -1); // eval.crb_mod_extend_object(VALUE mod, VALUE obj)rb_extend_object(VALUE obj, VALUE module)rb_include_module(VALUE klass, VALUE module)
  • Kernel#include● 次にKernel#includeの呼び出しを手繰る● rb_define_private_method(rb_cModule, "include", rb_mod_include, -1); // eval.c● rb_funcall(argv[argc], rb_intern("append_features"), 1, module);● rb_include_module(VALUE include, VALUE module); // ここでつながるrb_include_module で繋がる2つは根が同じもの
  • rb_include_module内約● その内側は、実はスーパークラスの指定をしているだけinclude_modules_at(VALUE klass, VALUE c, VALUE module){/* 色々と事前のチェック */c = RCLASS_SUPER(c) = rb_include_class_new(module, RCLASS_SUPER(c));}
  • Module#prepend● Ruby2.0で追加されたModule#prependでも似たようなもの● ” ” ” ”継承順のリストに 追記 ではなく 挿入 をしているだけ● 関数名は書いておくので興味があったら確認してね● rb_define_private_method(rb_cModule, "prepend", rb_mod_prepend, -1); // ./eval.c:rb_mod_prepend(int argc, VALUE *argv, VALUE module)rb_prepend_module(VALUE klass, VALUE module)
  • まとめ3● includeはModuleを使った継承● Kernel#includeとObject#extendは根っこは同じ● Kernel#include、Object#extend、Module#prependって実は継承順序の内側に挟みこみをしているだけ
  • Stage4: method_missingの呼ばれ方
  • method_missing解説メソッドの呼ばれ方(rb_call0)を探せば簡単./vm_eval.c● static inline VALUE rb_call0(VALUE recv, ID mid, int argc, const VALUE *argv, call_type scope,VALUE self){rb_method_entry_t *me = rb_search_method_entry(recv, mid); // method_missingでないメソッドrb_thread_t *th = GET_THREAD();int call_status = rb_method_call_status(th, me, scope, self);if (call_status != NOEX_OK) {return method_missing(recv, mid, argc, argv, call_status); // ここでmethod_missingを読んでいる}stack_check();return vm_call0(th, recv, mid, argc, argv, me);}
  • 普通のメソッドの場合● メソッド探索のの順を手繰っていくと● rb_search_method_entry● rb_method_entry● rb_method_entry_get_without_cache● search_method● st_lookup(st_table *table, register st_data_t key,st_data_t *value)st_lookupに突き当たる
  • ● そして、st_lookupってdefine_methodの所で見た!!int st_lookup(st_table *table, register st_data_t key, st_data_t *value){...st_index_t i;for (i = 0; i < table->num_entries; i++) {if ((st_data_t)table->bins[i*2] == key) {if (value !=0) *value = (st_data_t)table->bins[i*2+1];return 1;}}...
  • まとめ● vm_call0 関数でクラスを指定● Rclass を継承順に沿って探索● st_lookup 関数で内部のメソッドを探索● st_table 内部にあるメソッドを探すこれがrubyのメソッド探索順序
  • まとめ● クラスの定義● 継承順の定義● メソッドの定義● これらを合わせてメソッドの探索順を探した…ことになりましたが
  • 次回の予定● もうコレ系のネタやめていいかなと思っていますがネタがない場合、多分構文解析の部分やります
  • 大事なこと● 今までのコード rb_define_method でメソッド名でgrep して、そこから手繰る事で、すべてやってこれました● これだけでruby はほぼ解析可能です