mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
Allow calling protected methods from refinements
Previously protected methods on refinements could never be called because they were seen as being "defined" on the hidden refinement ICLASS. This commit updates calling refined protected methods so that they are considered to be defined on the original class (the one being refined). This ended up using the same behaviour that was used to check whether a call to super was allowed, so I extracted that into a method. [Bug #18806]
This commit is contained in:
parent
cd5cafa4a3
commit
ae163cae6b
Notes:
git
2022-06-17 05:25:11 +09:00
2 changed files with 51 additions and 6 deletions
|
@ -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]'
|
||||
|
|
|
@ -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) &&
|
||||
|
|
Loading…
Reference in a new issue