Share
Embed
Ruby and its evaluation
プログラミング言語基礎勉強会 sponsored by @wantedly UIターン at 2016 June 11
Urabe Shyouhei
June 11, 2016
Transcript
96 slides
-
w ݹ͍ɻ+BWBಉ࣌ظ w ͳͷͰࠓ࣌ͬΆ͍ݴޠػೳݢฒΈܽ͘ w ࣮+*4ʹͳ͍ͬͯΔ w +*49 *40*&$ w
ओʹௐୡͷ߹ʹΑΔ w ੲʮΦϒδΣΫτࢦεΫϦϓτݴޠ3VCZʯͱ໊͍ͬͯͨ w ࠓจ๏ͷ͠ͳ͍͕ จ๏͕͍Ζ͍Ζม w L-0$QBSTFZXJUIDPOqJDUT w &NBDTʹର͢Δઓͱͯ͠࡞ΒΕ͍ͯΔ໘͕͋Δ 3VCZͱ -
[:binary, [:@int, "1", [1, 0]], :+, [:@int, "2", [1, 2]]]
"45ʹม ߦ൪߸ͱ͔ͳͷͰ Εͯ0, ͜Εͱ͍ͬͯ ಛͷͳ͍ ڭՊॻ௨Γͷ நߏจ -
"45ʹม @ NODE_CALL (line: 1) +- nd_mid: :+ +-
nd_recv: | @ NODE_LIT (line: 1) | +- nd_lit: 1 +- nd_args: @ NODE_ARRAY (line: 1) +- nd_alen: 1 +- nd_head: | @ NODE_LIT (line: 1) | +- nd_lit: 2 +- nd_next: (null node) -
"45ʹม @ NODE_CALL (line: 1) +- nd_mid: :+ +-
nd_recv: | @ NODE_LIT (line: 1) | +- nd_lit: 1 +- nd_args: @ NODE_ARRAY (line: 1) +- nd_alen: 1 +- nd_head: | @ NODE_LIT (line: 1) | +- nd_lit: 2 +- nd_next: (null node) -
"45ʹม @ NODE_CALL (line: 1) +- nd_mid: :+ +-
nd_recv: | @ NODE_LIT (line: 1) | +- nd_lit: 1 +- nd_args: @ NODE_ARRAY (line: 1) +- nd_alen: 1 +- nd_head: | @ NODE_LIT (line: 1) | +- nd_lit: 2 +- nd_next: (null node) 「メソッド呼出し」 名前は「:+」 レシーバは リテラルで 値は「1」 引数は 配列で サイズは「1」 先頭は リテラルで 値は「2」 次の要素は なし -
*4FRʹม == disasm: #<ISeq:<compiled>@<compiled>>== 0000 putobject 1 0002 putobject 2
0004 send <mid:+, argc:1> 0006 leave -
*4FRʹม == disasm: #<ISeq:<compiled>@<compiled>>== 0000 putobject 1 0002 putobject 2
0004 send <mid:+, argc:1> 0006 leave ※スタックマシン -
*4FRʹม == disasm: #<ISeq:<compiled>@<compiled>>== 0000 putobject 1 0002 putobject 2
0004 send <mid:+, argc:1> 0006 leave 1 ※スタックマシン -
*4FRʹม == disasm: #<ISeq:<compiled>@<compiled>>== 0000 putobject 1 0002 putobject 2
0004 send <mid:+, argc:1> 0006 leave 1 2 ※スタックマシン -
*4FRʹม == disasm: #<ISeq:<compiled>@<compiled>>== 0000 putobject 1 0002 putobject 2
0004 send <mid:+, argc:1> 0006 leave ※スタックマシン 3 -
*4FRʹม == disasm: #<ISeq:foo@<compiled>>========= 0000 putobject 1 0002 putobject 2
0004 send <mid:+, argc:1> 0006 leave == disasm: #<ISeq:<compiled>@<compiled>>== 0000 putobject RubyVM::FrozenCore 0002 putobject :foo 0004 putiseq #<ISeq:foo@<compiled>> 0006 send <mid:core#define_method, argc:2> 0008 leave -
*4FRʹม == disasm: #<ISeq:foo@<compiled>>========= 0000 putobject 1 0002 putobject 2
0004 send <mid:+, argc:1> 0006 leave == disasm: #<ISeq:<compiled>@<compiled>>== 0000 putobject RubyVM::FrozenCore 0002 putobject :foo 0004 putiseq #<ISeq:foo@<compiled>> 0006 send <mid:core#define_method, argc:2> 0008 leave -
w ී௨ͷϝιουݺͼग़͠ w ී௨ͷԋࢉࢠ ൺֱͱ͔ w ྫ֎ͷraise • lambda
w ϝιουఆٛ ݟͨͱ͓Γ sendͰ࣮ݱ͞Ε͍ͯΔࣄ ͱ͍͑શ෦͡Όͳ͍ɺͨͱ͑ifจͳͲ4NBMMUBMLͰϝιουͩ ͕3VCZͰҧ͏ɻ͜ͷΑ͏ͳΑ͍࣮͑͘ફతɺѱ͘ݴ͑ݟͳ ࢟ྑ͘ѱ͘3VCZΛಛ͚͍ͯΔͱݴ͑Δɻ -
sendͷৼ͍ /** @c method/iterator @e invoke method. @j ϝιουݺͼग़͠Λߦ͏ɻci ʹඞཁͳใ͕֨ೲ͞Ε͍ͯΔɻ
*/ DEFINE_INSN send (CALL_INFO ci, CALL_CACHE cc, ISEQ blockiseq) (...) (VALUE val) // inc += - (int)(ci->orig_argc + ((ci->flag & VM_CALL_ARGS_BLOCKARG) ? 1 : 0)); { struct rb_calling_info calling; vm_caller_setup_arg_block(th, reg_cfp, &calling, ci, blockiseq, FALSE); vm_search_method(ci, cc, calling.recv = TOPN(calling.argc = ci->orig_argc)); CALL_METHOD(&calling, ci, cc); } -
sendͷৼ͍ /** @c method/iterator @e invoke method. @j ϝιουݺͼग़͠Λߦ͏ɻci ʹඞཁͳใ͕֨ೲ͞Ε͍ͯΔɻ
*/ DEFINE_INSN send (CALL_INFO ci, CALL_CACHE cc, ISEQ blockiseq) (...) (VALUE val) // inc += - (int)(ci->orig_argc + ((ci->flag & VM_CALL_ARGS_BLOCKARG) ? 1 : 0)); { struct rb_calling_info calling; vm_caller_setup_arg_block(th, reg_cfp, &calling, ci, blockiseq, FALSE); vm_search_method(ci, cc, calling.recv = TOPN(calling.argc = ci->orig_argc)); CALL_METHOD(&calling, ci, cc); } -
ϒϩοΫҾ͕͋Δ͔ͳ͍͔ܾఆͯ͠ɺ͋Ε$Ϩϕϧߏମ struct calling_info ʹಥͬࠐΉ ݺͿ͖ϝιουͷ࣮ମ struct method_entry ΛͳΜΒ͔
ͷํ๏ʹܾͯఆ͢Δ 7.ελοΫϑϨʔϜΛੵΜͩΓͯ͠7.ϓϩάϥϜΧϯλΛ δϟϯϓ͢Δ sendͷৼ͍ -
ϒϩοΫҾ͕͋Δ͔ͳ͍͔ܾఆͯ͠ɺ͋Ε$Ϩϕϧߏମ struct calling_info ʹಥͬࠐΉ ݺͿ͖ϝιουͷ࣮ମ struct method_entry ΛͳΜΒ͔
ͷํ๏ʹܾͯఆ͢Δ 7.ελοΫϑϨʔϜΛੵΜͩΓͯ͠7.ϓϩάϥϜΧϯλΛ δϟϯϓ͢Δ sendͷৼ͍ ٯʹݴ͏ͱ͜ͷظ ʹٴͿ·Ͱܾఆ͠ ͳ͍ͱ͍͏͜ͱͰ ͋Δɻ -
࠶ܝ sendͷ࣮ /** @c method/iterator @e invoke method. @j ϝιουݺͼग़͠Λߦ͏ɻci
ʹඞཁͳใ͕֨ೲ͞Ε͍ͯΔɻ */ DEFINE_INSN send (CALL_INFO ci, CALL_CACHE cc, ISEQ blockiseq) (...) (VALUE val) // inc += - (int)(ci->orig_argc + ((ci->flag & VM_CALL_ARGS_BLOCKARG) ? 1 : 0)); { struct rb_calling_info calling; vm_caller_setup_arg_block(th, reg_cfp, &calling, ci, blockiseq, FALSE); vm_search_method(ci, cc, calling.recv = TOPN(calling.argc = ci->orig_argc)); CALL_METHOD(&calling, ci, cc); } -
static void vm_search_method(const struct rb_call_info *ci, struct rb_call_cache *cc, VALUE
recv) { VALUE klass = CLASS_OF(recv); #if (தུ) #endif cc->me = rb_callable_method_entry(klass, ci->mid); VM_ASSERT(callable_method_entry_p(cc->me)); cc->call = vm_call_general; #if (தུ) #endif } -
const rb_callable_method_entry_t * rb_callable_method_entry(VALUE klass, ID id) { VALUE defined_class;
rb_method_entry_t *me = method_entry_get(klass, id, &defined_class); return prepare_callable_method_entry(defined_class, id, me); } -
static rb_method_entry_t * method_entry_get(VALUE klass, ID id, VALUE *defined_class_ptr) {
#if (தུ) #endif return method_entry_get_without_cache(klass, id, defined_class_ptr); } -
/* * search method entry without the method cache. *
* if you need method entry with method cache (normal case), use * rb_method_entry() simply. */ static rb_method_entry_t * method_entry_get_without_cache(VALUE klass, ID id, VALUE *defined_class_ptr) { VALUE defined_class; rb_method_entry_t *me = search_method(klass, id, &defined_class); if (ruby_running) { if (OPT_GLOBAL_METHOD_CACHE) { struct cache_entry *ent; ent = GLOBAL_METHOD_CACHE(klass, id); ent->class_serial = RCLASS_SERIAL(klass); ent->method_state = GET_GLOBAL_METHOD_STATE(); ent->defined_class = defined_class; ent->mid = id; if (UNDEFINED_METHOD_ENTRY_P(me)) { -
static inline rb_method_entry_t* search_method(VALUE klass, ID id, VALUE *defined_class_ptr) {
rb_method_entry_t *me; for (me = 0; klass; klass = RCLASS_SUPER(klass)) { if ((me = lookup_method_table(klass, id)) != 0) break; } if (defined_class_ptr) *defined_class_ptr = klass; return me; } -
w ·ͣrecv͕͋ͬͯ w klass = CLASS_OF(recv)Λج४ʹ w klass = RCLASS_SUPER(klass)ΛͨͲΓͳ͕Β
w lookup_method_table(klass, id)͕ώοτ͢Δ·Ͱݕࡧ Λଓ͚Δ ·ͱΊΔͱ -
klassʮΫϥεʯ͔ a = "foo" def a.+(other) return sprintf("<%s|%s>", other, self)
end a + "bar" # => "<bar|foo>" "bar" + a # => "barfoo" -
ͦ͏Ͱͳ͍ײ a = "foo" def a.+(other) return sprintf("<%s|%s>", other, self)
end a + "bar" # => "<bar|foo>" "bar" + a # => "barfoo" -
*4FRʹม == disasm: #<ISeq:foo@<compiled>>========================== 0000 putobject 1 0002 putobject 2
0004 send <callinfo!mid:+, argc:1, ARGS_SIMPLE> 0008 leave == disasm: #<ISeq:<compiled>@<compiled>>=================== 0000 putstring "foo" 0002 setlocal a 0005 putobject RubyVM::FrozenCore 0007 getlocal a 0010 putobject :foo 0012 putiseq #<ISeq:foo@<compiled>> 0014 send <callinfo!mid:core#define_singleton_method, argc:3, ARGS_SIMPLE> 0018 leave -
VALUE *const saved_sp = th__->cfp->sp; \ th__->cfp->sp = curr_sp; \
expr; \ (th__->cfp--)->sp = saved_sp; \ } while (0) static VALUE m_core_define_method(VALUE self, VALUE sym, VALUE iseqval) { REWIND_CFP({ vm_define_method(GET_THREAD(), Qnil, SYM2ID(sym), iseqval, FALSE); }); return sym; } static VALUE m_core_define_singleton_method(VALUE self, VALUE cbase, VALUE sym, VALUE iseqval) { REWIND_CFP({ vm_define_method(GET_THREAD(), cbase, SYM2ID(sym), iseqval, TRUE); }); return sym; } static VALUE m_core_set_method_alias(VALUE self, VALUE cbase, VALUE sym1, VALUE sym2) { REWIND_CFP({ rb_alias(cbase, SYM2ID(sym1), SYM2ID(sym2)); -
VALUE *const saved_sp = th__->cfp->sp; \ th__->cfp->sp = curr_sp; \
expr; \ (th__->cfp--)->sp = saved_sp; \ } while (0) static VALUE m_core_define_method(VALUE self, VALUE sym, VALUE iseqval) { REWIND_CFP({ vm_define_method(GET_THREAD(), Qnil, SYM2ID(sym), iseqval, FALSE); }); return sym; } static VALUE m_core_define_singleton_method(VALUE self, VALUE cbase, VALUE sym, VALUE iseqval) { REWIND_CFP({ vm_define_method(GET_THREAD(), cbase, SYM2ID(sym), iseqval, TRUE); }); return sym; } static VALUE m_core_set_method_alias(VALUE self, VALUE cbase, VALUE sym1, VALUE sym2) { REWIND_CFP({ rb_alias(cbase, SYM2ID(sym1), SYM2ID(sym2)); -
static void vm_define_method(rb_thread_t *th, VALUE obj, ID id, VALUE iseqval,
int is_singleton) { VALUE klass; rb_method_visibility_t visi; rb_cref_t *cref = rb_vm_cref(); if (!is_singleton) { klass = CREF_CLASS(cref); visi = rb_scope_visibility_get(); } else { /* singleton */ klass = rb_singleton_class(obj); /* class and frozen checked in this API */ visi = METHOD_VISI_PUBLIC; } if (NIL_P(klass)) { rb_raise(rb_eTypeError, "no class/module to add method"); } rb_add_method_iseq(klass, id, (const rb_iseq_t *)iseqval, cref, visi); if (!is_singleton && rb_scope_module_func_check()) { klass = rb_singleton_class(klass); -
VALUE rb_singleton_class(VALUE obj) { VALUE klass = singleton_class_of(obj); /* ensures
an exposed class belongs to its own eigenclass */ if (RB_TYPE_P(obj, T_CLASS)) (void)ENSURE_EIGENCLASS(klass); return klass; } -
static VALUE singleton_class_of(VALUE obj) { VALUE klass; if (FIXNUM_P(obj) ||
FLONUM_P(obj) || STATIC_SYM_P(obj)) { (中略) } klass = RBASIC(obj)->klass; if (!(FL_TEST(klass, FL_SINGLETON) && rb_ivar_get(klass, id_attached) == obj)) { klass = rb_make_metaclass(obj, klass); } if (OBJ_TAINTED(obj)) { OBJ_TAINT(klass); } else { FL_UNSET(klass, FL_TAINT); } if (OBJ_FROZEN(obj)) OBJ_FREEZE_RAW(klass); return klass; -
static VALUE singleton_class_of(VALUE obj) { VALUE klass; if (FIXNUM_P(obj) ||
FLONUM_P(obj) || STATIC_SYM_P(obj)) { (中略) } klass = RBASIC(obj)->klass; if (!(FL_TEST(klass, FL_SINGLETON) && rb_ivar_get(klass, id_attached) == obj)) { klass = rb_make_metaclass(obj, klass); } if (OBJ_TAINTED(obj)) { OBJ_TAINT(klass); } else { FL_UNSET(klass, FL_TAINT); } if (OBJ_FROZEN(obj)) OBJ_FREEZE_RAW(klass); return klass; ϝλΫϥεͱʜʁ -
VALUE rb_make_metaclass(VALUE obj, VALUE unused) { if (BUILTIN_TYPE(obj) == T_CLASS)
{ return make_metaclass(obj); } else { return make_singleton_class(obj); } } -
static inline VALUE make_singleton_class(VALUE obj) { VALUE orig_class = RBASIC(obj)->klass;
VALUE klass = rb_class_boot(orig_class); FL_SET(klass, FL_SINGLETON); RBASIC_SET_CLASS(obj, klass); rb_singleton_class_attached(klass, obj); SET_METACLASS_OF(klass, METACLASS_OF(rb_class_real(orig_class))); return klass; } ίϯετϥΫλؔ ϙΠϯλॻ͖͑ͯΔ -
݁ہ͜ΕԿʁ static inline VALUE make_singleton_class(VALUE obj) { VALUE orig_class =
RBASIC(obj)->klass; VALUE klass = rb_class_boot(orig_class); FL_SET(klass, FL_SINGLETON); RBASIC_SET_CLASS(obj, klass); rb_singleton_class_attached(klass, obj); SET_METACLASS_OF(klass, METACLASS_OF(rb_class_real(orig_class))); return klass; } -
w ʮΫϥεͷΫϥεʯ w ࣮3VCZʹ͓͍ͯΫϥεΦϒδΣΫτͷҰछͰɺ::Class ͱ͍͏ม໊ͰΞΫηεͰ͖ΔΫϥε ҎԼClassΫϥε ͷΠϯ ελϯεɻ w ͔͠͠ݟ͖ͯͨΑ͏ʹɺ3VCZʹಛҟΫϥεͱ͍͏֓೦͕͋
Γɺ͜ΕવΫϥεͷΦϒδΣΫτࣗʹద༻͞ΕΔ ϝλΫϥε -
w ʮಛҟΫϥεΫϥεʹద༻͞ΕΔʯ w ʮಛҟΫϥεʯͦΕࣗΦϒδΣΫτͰ͔͢ʁ w ˠ͍ w ʮಛҟΫϥεʯͷΫϥεͳΜͰ͔͢ʁ w ˠʮClassΫϥεʯͰ͢
w ʮಛҟΫϥεʯʹಛҟΫϥε͋Γ·͔͢ʁ ϝλΫϥε -
w ʮಛҟΫϥεΫϥεʹద༻͞ΕΔʯ w ʮಛҟΫϥεʯͦΕࣗΦϒδΣΫτͰ͔͢ʁ w ˠ͍ w ʮಛҟΫϥεʯͷΫϥεͳΜͰ͔͢ʁ w ˠʮClassΫϥεʯͰ͢
w ʮಛҟΫϥεʯʹಛҟΫϥε͋Γ·͔͢ʁ w ˠ͍ ϝλΫϥε -
obj 通常の意味の クラス その親 特異クラス メタクラス Classクラス メタClassクラス Moduleクラス Objectクラス
メタModuleクラス メタObjectクラス -
obj 通常の意味の クラス その親 特異クラス メタクラス Classクラス メタClassクラス Moduleクラス Objectクラス
メタModuleクラス メタObjectクラス -
obj 通常の意味の クラス その親 特異クラス メタクラス Classクラス メタClassクラス Moduleクラス Objectクラス
メタModuleクラス メタObjectクラス -
obj 通常の意味の クラス その親 特異クラス メタクラス Classクラス メタClassクラス Moduleクラス Objectクラス
メタModuleクラス メタObjectクラス メタ2Module メタ2Object