From 888be9611b60bc7739d797e6defaf903b260b62d Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Thu, 24 Jun 2021 13:11:36 +0900 Subject: class.c: call hook methods on method definitions; close #2339 - `Module#method_added` - `BasicObject#singleton_method_added` --- src/class.c | 19 +++++++++++++++++++ src/vm.c | 5 ++++- test/t/methods.rb | 29 +++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/class.c b/src/class.c index 11f4b3d29..8775ded3b 100644 --- a/src/class.c +++ b/src/class.c @@ -2505,6 +2505,22 @@ mrb_mod_method_defined(mrb_state *mrb, mrb_value mod) return mrb_bool_value(mrb_obj_respond_to(mrb, mrb_class_ptr(mod), id)); } +void +mrb_method_added(mrb_state *mrb, struct RClass *c, mrb_sym mid) +{ + mrb_sym added; + mrb_value recv = mrb_obj_value(c); + + if (c->tt == MRB_TT_SCLASS) { + added = MRB_SYM(singleton_method_added); + recv = mrb_iv_get(mrb, recv, MRB_SYM(__attached__)); + } + else { + added = MRB_SYM(method_added); + } + mrb_funcall_id(mrb, recv, added, 1, mrb_symbol_value(mid)); +} + mrb_value mrb_mod_define_method_m(mrb_state *mrb, struct RClass *c) { @@ -2534,6 +2550,7 @@ mrb_mod_define_method_m(mrb_state *mrb, struct RClass *c) p->flags |= MRB_PROC_STRICT; MRB_METHOD_FROM_PROC(m, p); mrb_define_method_raw(mrb, c, mid, m); + mrb_method_added(mrb, c, mid); return mrb_symbol_value(mid); } @@ -2892,6 +2909,7 @@ mrb_init_class(mrb_state *mrb) mrb_define_method(mrb, bob, "__send__", mrb_f_send, MRB_ARGS_REQ(1)|MRB_ARGS_REST()|MRB_ARGS_BLOCK()); /* 15.3.1.3.5 */ mrb_define_method(mrb, bob, "equal?", mrb_obj_equal_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.11 */ mrb_define_method(mrb, bob, "instance_eval", mrb_obj_instance_eval, MRB_ARGS_OPT(1)|MRB_ARGS_BLOCK()); /* 15.3.1.3.18 */ + mrb_define_method(mrb, bob, "singleton_method_added", mrb_bob_init, MRB_ARGS_REQ(1)); mrb_define_class_method(mrb, cls, "new", mrb_class_new_class, MRB_ARGS_OPT(1)|MRB_ARGS_BLOCK()); mrb_define_method(mrb, cls, "allocate", mrb_instance_alloc, MRB_ARGS_NONE()); @@ -2932,6 +2950,7 @@ mrb_init_class(mrb_state *mrb) mrb_define_method(mrb, mod, "define_method", mod_define_method, MRB_ARGS_ARG(1,1)); mrb_define_method(mrb, mod, "===", mrb_mod_eqq, MRB_ARGS_REQ(1)); /* 15.2.2.4.7 */ mrb_define_method(mrb, mod, "dup", mrb_mod_dup, MRB_ARGS_NONE()); + mrb_define_method(mrb, bob, "method_added", mrb_bob_init, MRB_ARGS_REQ(1)); mrb_undef_method(mrb, cls, "append_features"); mrb_undef_method(mrb, cls, "prepend_features"); diff --git a/src/vm.c b/src/vm.c index 560887f00..f76ce9eac 100644 --- a/src/vm.c +++ b/src/vm.c @@ -1135,6 +1135,7 @@ get_send_args(mrb_state *mrb, mrb_int argc, mrb_value *regs) mrb_value mrb_obj_missing(mrb_state *mrb, mrb_value mod); void mrb_hash_check_kdict(mrb_state *mrb, mrb_value self); +void mrb_method_added(mrb_state *mrb, struct RClass *c, mrb_sym mid); MRB_API mrb_value mrb_vm_exec(mrb_state *mrb, const struct RProc *proc, const mrb_code *pc) @@ -2893,9 +2894,11 @@ RETRY_TRY_BLOCK: struct RClass *target = mrb_class_ptr(regs[a]); struct RProc *p = mrb_proc_ptr(regs[a+1]); mrb_method_t m; + mrb_sym mid = syms[b]; MRB_METHOD_FROM_PROC(m, p); - mrb_define_method_raw(mrb, target, syms[b], m); + mrb_define_method_raw(mrb, target, mid, m); + mrb_method_added(mrb, target, mid); mrb_gc_arena_restore(mrb, ai); NEXT; } diff --git a/test/t/methods.rb b/test/t/methods.rb index f9c25dc33..9005d7976 100644 --- a/test/t/methods.rb +++ b/test/t/methods.rb @@ -107,3 +107,32 @@ assert('The undef statement (method undefined)', '13.3.7 a) 5)') do undef :non_existing_method end end + +assert('method_added hook') do + c = Class.new do + # method to retrieve @name + def self.name; @name; end + # hook method on method definition + def self.method_added(name) @name = name; end + # method definition + def foo; end + end + assert_equal(:foo, c.name) + c.define_method(:bar){} + assert_equal(:bar, c.name) +end + +assert('singleton_method_added hook') do + a = Object.new + # method to retrieve @name + def a.name; @name; end + # hook method on singleton method definition + def a.singleton_method_added(name) @name = name; end + # singleton method definition + def a.foo; end + assert_equal(:foo, a.name) + class <