Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Ruby and its evaluation

Ruby and its evaluation

プログラミング言語基礎勉強会 sponsored by @wantedly UIターン at 2016 June 11

Urabe Shyouhei

June 11, 2016
Tweet

More Decks by Urabe Shyouhei

Other Decks in Technology

Transcript

  1. w ݹ͍ɻ+BWBಉ࣌ظ w ͳͷͰࠓ࣌ͬΆ͍ݴޠػೳ͸ݢฒΈܽ͘ w ࣮͸+*4ʹͳ͍ͬͯΔ w +*49 *40*&$ w

    ओʹ੓෎ௐୡͷ౎߹ʹΑΔ w ੲ͸ʮΦϒδΣΫτࢦ޲εΫϦϓτݴޠ3VCZʯͱ໊৐͍ͬͯͨ w ࠓ೔͸จ๏ͷ࿩͸͠ͳ͍͕ จ๏͕͍Ζ͍Ζม w L-0$QBSTFZXJUIDPOqJDUT w &NBDTʹର͢Δ௅ઓͱͯ͠࡞ΒΕ͍ͯΔ໘͕͋Δ 3VCZͱ͸
  2. 1+2

  3. [:binary, [:@int, "1", [1, 0]], :+, [:@int, "2", [1, 2]]]

    "45ʹม׵  ߦ൪߸ͱ͔ͳͷͰ ๨Εͯ0, ͜Εͱ͍ͬͯ ಛ௃ͷͳ͍ ڭՊॻ௨Γͷ ந৅ߏจ໦
  4. "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)
  5. "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)
  6. "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」 次の要素は なし
  7. *4FRʹม׵ == disasm: #<ISeq:<compiled>@<compiled>>== 0000 putobject 1 0002 putobject 2

    0004 send <mid:+, argc:1> 0006 leave ※スタックマシン
  8. *4FRʹม׵ == disasm: #<ISeq:<compiled>@<compiled>>== 0000 putobject 1 0002 putobject 2

    0004 send <mid:+, argc:1> 0006 leave 1 ※スタックマシン
  9. *4FRʹม׵ == disasm: #<ISeq:<compiled>@<compiled>>== 0000 putobject 1 0002 putobject 2

    0004 send <mid:+, argc:1> 0006 leave 1 2 ※スタックマシン
  10. *4FRʹม׵ == disasm: #<ISeq:<compiled>@<compiled>>== 0000 putobject 1 0002 putobject 2

    0004 send <mid:+, argc:1> 0006 leave ※スタックマシン 3
  11. *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
  12. *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
  13. w ී௨ͷϝιουݺͼग़͠ w ී௨ͷԋࢉࢠ ൺֱͱ͔  w ྫ֎ͷraise • lambda

    w ϝιουఆٛ ݟͨͱ͓Γ sendͰ࣮ݱ͞Ε͍ͯΔࣄ ͱ͸͍͑શ෦͡Όͳ͍ɺͨͱ͑͹ifจͳͲ͸4NBMMUBMLͰ͸ϝιουͩ ͕3VCZͰ͸ҧ͏ɻ͜ͷΑ͏ͳΑ͍͑͘͹࣮ફతɺѱ͘ݴ͑͹೔࿨ݟͳ ࢟੎͸ྑ͘΋ѱ͘΋3VCZΛಛ௃෇͚͍ͯΔͱ͸ݴ͑Δɻ
  14. 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); }
  15. 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); }
  16.  ϒϩοΫҾ਺͕͋Δ͔ͳ͍͔ܾఆͯ͠ɺ͋Ε͹$Ϩϕϧߏ଄ମ struct calling_info ʹಥͬࠐΉ  ݺͿ΂͖ϝιουͷ࣮ମ struct method_entry ΛͳΜΒ͔

    ͷํ๏ʹܾͯఆ͢Δ  7.ελοΫϑϨʔϜΛੵΜͩΓͯ͠7.ϓϩάϥϜΧ΢ϯλΛ δϟϯϓ͢Δ sendͷৼ෣͍
  17.  ϒϩοΫҾ਺͕͋Δ͔ͳ͍͔ܾఆͯ͠ɺ͋Ε͹$Ϩϕϧߏ଄ମ struct calling_info ʹಥͬࠐΉ  ݺͿ΂͖ϝιουͷ࣮ମ struct method_entry ΛͳΜΒ͔

    ͷํ๏ʹܾͯఆ͢Δ  7.ελοΫϑϨʔϜΛੵΜͩΓͯ͠7.ϓϩάϥϜΧ΢ϯλΛ δϟϯϓ͢Δ sendͷৼ෣͍ ٯʹݴ͏ͱ͜ͷظ ʹٴͿ·Ͱܾఆ͠ ͳ͍ͱ͍͏͜ͱͰ ΋͋Δɻ
  18. ࠶ܝ 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); }
  19. 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 }
  20. 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); }
  21. 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); }
  22. /* * 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)) {
  23. 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; }
  24. w ·ͣrecv͕͋ͬͯ w klass = CLASS_OF(recv)Λج४ʹ w klass = RCLASS_SUPER(klass)ΛͨͲΓͳ͕Β

    w lookup_method_table(klass, id)͕ώοτ͢Δ·Ͱݕࡧ Λଓ͚Δ ·ͱΊΔͱ
  25. klass͸ʮΫϥεʯ͔ a = "foo" def a.+(other) return sprintf("<%s|%s>", other, self)

    end a + "bar" # => "<bar|foo>" "bar" + a # => "barfoo"
  26. ͦ͏Ͱ΋ͳ͍ײ a = "foo" def a.+(other) return sprintf("<%s|%s>", other, self)

    end a + "bar" # => "<bar|foo>" "bar" + a # => "barfoo"
  27. *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
  28. 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));
  29. 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));
  30. 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);
  31. 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; }
  32. 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;
  33. 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; ϝλΫϥεͱ͸ʜʁ
  34. VALUE rb_make_metaclass(VALUE obj, VALUE unused) { if (BUILTIN_TYPE(obj) == T_CLASS)

    { return make_metaclass(obj); } else { return make_singleton_class(obj); } }
  35. 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; } ίϯετϥΫλؔ਺ ϙΠϯλॻ͖׵͑ͯΔ
  36. ݁ہ͜Ε͸Կʁ 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; }