0
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...
rb_class_bootは継承先、継承元の設定をしているだけとっても素直な作り。VALUE rb_class_boot(VALUE super){VALUE klass = class_alloc(T_CLASS, rb_cClass);RC...
マクロを展開していくと、RClassなるものが浮いてくるVALUE rb_class_boot(VALUE super){VALUE klass = class_alloc(T_CLASS, rb_cClass);R_CAST((RClass)...
Rclass!! クラスの正体はここにある(rubyのオブジェクトを理解するにはここを起点にすると良い)struct RClass {struct RBasic basic;rb_classext_t *ptr;struct st_table ...
そして、メソッドは、実はforループで探索して呼び出されているint st_lookup(st_table *table, register st_data_t key, st_data_t *value){...st_index_t i;fo...
まとめ:その1● クラスはCの構造体● 抑えておくべきデータ型はクラスの本体RClassこれを作っている
Stage2: methodをdefineしてみる
Kernel#define_methodの解説● define_methodの定義はproc.cにあるのでそこから順番に手繰る● rb_define_private_method(rb_cModule, "define_method",rb_m...
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って構造体…何...
st_insert解読st_insertの定義は呼び出し先の挙動を見たほうが早い実はループで重複を探して、重複したものが無ければメソッド挿入をしているだけint st_insert(register st_table *table, regis...
まとめ2メソッドは構造体st_tableの内側に、配列として格納されている
Stage3: モジュールのinclude
Kernel#include,Object#extend● まずObject#extendの呼び出しを手繰ってみる以下は呼び出し順rb_define_method(rb_mKernel, "extend", rb_obj_extend, -1)...
Kernel#include● 次にKernel#includeの呼び出しを手繰る● rb_define_private_method(rb_cModule, "include", rb_mod_include, -1); // eval.c●...
rb_include_module内約● その内側は、実はスーパークラスの指定をしているだけinclude_modules_at(VALUE klass, VALUE c, VALUE module){/* 色々と事前のチェック */c = R...
Module#prepend● Ruby2.0で追加されたModule#prependでも似たようなもの● ” ” ” ”継承順のリストに 追記 ではなく 挿入 をしているだけ● 関数名は書いておくので興味があったら確認してね● rb_defi...
まとめ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 VAL...
普通のメソッドの場合● メソッド探索のの順を手繰っていくと● rb_search_method_entry● rb_method_entry● rb_method_entry_get_without_cache● search_method● ...
● そして、st_lookupってdefine_methodの所で見た!!int st_lookup(st_table *table, register st_data_t key, st_data_t *value){...st_index_...
まとめ● vm_call0 関数でクラスを指定● Rclass を継承順に沿って探索● st_lookup 関数で内部のメソッドを探索● st_table 内部にあるメソッドを探すこれがrubyのメソッド探索順序
まとめ● クラスの定義● 継承順の定義● メソッドの定義● これらを合わせてメソッドの探索順を探した…ことになりましたが
次回の予定● もうコレ系のネタやめていいかなと思っていますがネタがない場合、多分構文解析の部分やります
大事なこと● 今までのコード rb_define_method でメソッド名でgrep して、そこから手繰る事で、すべてやってこれました● これだけでruby はほぼ解析可能です
Upcoming SlideShare
Loading in...5
×

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

361

Published on

0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
361
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
2
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

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

  1. 1. Rubyのコードを読んでみよう:その2
  2. 2. さて今日のお題ですが
  3. 3. コレです
  4. 4. Rubyには他の代表的な言語にない黒魔術的なメソッドがありますがその殆どはrubyのクラスの内側に挟み込まれて存在しています
  5. 5. 具体的には● モンキーパッチング● eigenclass(self)● Kernel#define_method● Object#extend● Kernel#include● Object#method_missoingEtc...
  6. 6. これらの機能はrubyのruby的なrubyたる、オブジェクト指向と深く結びついています
  7. 7. 今回のお題これらを手繰りながらrubyの● クラスって何?● オブジェクトって何?● メソッドって何?” ”という 定義 を辿って行きましょう
  8. 8. 細かいこと● 解析するversionはruby2.0.0-p0でやりますよ!(1.9系と劇的に違ったりはしていないんですけどね)
  9. 9. Stage1: Classをnewしてみる
  10. 10. クラスの作成(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);}
  11. 11. 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;}
  12. 12. マクロを展開していくと、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;}
  13. 13. Rclass!! クラスの正体はここにある(rubyのオブジェクトを理解するにはここを起点にすると良い)struct RClass {struct RBasic basic;rb_classext_t *ptr;struct st_table *m_tbl; // メソッド関係一覧struct st_table *iv_index_tbl; // インスタンス変数一覧};
  14. 14. そして、メソッドは、実は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;}}...
  15. 15. まとめ:その1● クラスはCの構造体● 抑えておくべきデータ型はクラスの本体RClassこれを作っている
  16. 16. Stage2: methodをdefineしてみる
  17. 17. 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)
  18. 18. st_insert(register st_table *table, register st_data_t key, st_data_t value)このメソッドの、st_tableって構造体…何処かで見たような気が
  19. 19. メソッドの挿入をしている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; // インスタンス変数一覧};
  20. 20. 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;}..}
  21. 21. まとめ2メソッドは構造体st_tableの内側に、配列として格納されている
  22. 22. Stage3: モジュールのinclude
  23. 23. 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)
  24. 24. 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つは根が同じもの
  25. 25. rb_include_module内約● その内側は、実はスーパークラスの指定をしているだけinclude_modules_at(VALUE klass, VALUE c, VALUE module){/* 色々と事前のチェック */c = RCLASS_SUPER(c) = rb_include_class_new(module, RCLASS_SUPER(c));}
  26. 26. 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)
  27. 27. まとめ3● includeはModuleを使った継承● Kernel#includeとObject#extendは根っこは同じ● Kernel#include、Object#extend、Module#prependって実は継承順序の内側に挟みこみをしているだけ
  28. 28. Stage4: method_missingの呼ばれ方
  29. 29. 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);}
  30. 30. 普通のメソッドの場合● メソッド探索のの順を手繰っていくと● 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に突き当たる
  31. 31. ● そして、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;}}...
  32. 32. まとめ● vm_call0 関数でクラスを指定● Rclass を継承順に沿って探索● st_lookup 関数で内部のメソッドを探索● st_table 内部にあるメソッドを探すこれがrubyのメソッド探索順序
  33. 33. まとめ● クラスの定義● 継承順の定義● メソッドの定義● これらを合わせてメソッドの探索順を探した…ことになりましたが
  34. 34. 次回の予定● もうコレ系のネタやめていいかなと思っていますがネタがない場合、多分構文解析の部分やります
  35. 35. 大事なこと● 今までのコード rb_define_method でメソッド名でgrep して、そこから手繰る事で、すべてやってこれました● これだけでruby はほぼ解析可能です
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×