diff --git a/test/ruby/test_refinement.rb b/test/ruby/test_refinement.rb index c0754d8cf0..56f33ae00a 100644 --- a/test/ruby/test_refinement.rb +++ b/test/ruby/test_refinement.rb @@ -1206,6 +1206,41 @@ class TestRefinement < Test::Unit::TestCase INPUT end + def test_refined_protected_methods + assert_separately([], <<-"end;") + bug18806 = '[ruby-core:108705] [Bug #18806]' + class C; end + + module R + refine C do + def refined_call_foo = foo + def refined_call_foo_on(other) = other.foo + + protected + + def foo = :foo + end + end + + class C + using R + + def call_foo = foo + def call_foo_on(other) = other.foo + end + + c = C.new + assert_equal :foo, c.call_foo, bug18806 + assert_equal :foo, c.call_foo_on(c), bug18806 + assert_equal :foo, c.call_foo_on(C.new), bug18806 + + using R + assert_equal :foo, c.refined_call_foo, bug18806 + assert_equal :foo, c.refined_call_foo_on(c), bug18806 + assert_equal :foo, c.refined_call_foo_on(C.new), bug18806 + end; + end + def test_refine_basic_object assert_separately([], <<-"end;") bug10106 = '[ruby-core:64166] [Bug #10106]' diff --git a/vm_insnhelper.c b/vm_insnhelper.c index 09fcd2f729..d2f3fc62bd 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -3711,6 +3711,19 @@ vm_call_method_nome(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct } } +/* Protected method calls and super invocations need to check that the receiver + * (self for super) inherits the module on which the method is defined. + * In the case of refinements, it should consider the original class not the + * refinement. + */ +static VALUE +vm_defined_class_for_protected_call(const rb_callable_method_entry_t *me) +{ + VALUE defined_class = me->defined_class; + VALUE refined_class = RCLASS_REFINED_CLASS(defined_class); + return NIL_P(refined_class) ? defined_class : refined_class; +} + static inline VALUE vm_call_method(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling) { @@ -3737,7 +3750,8 @@ vm_call_method(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_ca case METHOD_VISI_PROTECTED: if (!(vm_ci_flag(ci) & VM_CALL_OPT_SEND)) { - if (!rb_obj_is_kind_of(cfp->self, vm_cc_cme(cc)->defined_class)) { + VALUE defined_class = vm_defined_class_for_protected_call(vm_cc_cme(cc)); + if (!rb_obj_is_kind_of(cfp->self, defined_class)) { vm_cc_method_missing_reason_set(cc, MISSING_PROTECTED); return vm_call_method_missing(ec, cfp, calling); } @@ -3834,11 +3848,7 @@ vm_search_super_method(const rb_control_frame_t *reg_cfp, struct rb_call_data *c vm_super_outside(); } - current_defined_class = me->defined_class; - - if (!NIL_P(RCLASS_REFINED_CLASS(current_defined_class))) { - current_defined_class = RCLASS_REFINED_CLASS(current_defined_class); - } + current_defined_class = vm_defined_class_for_protected_call(me); if (BUILTIN_TYPE(current_defined_class) != T_MODULE && reg_cfp->iseq != method_entry_iseqptr(me) &&