Skip to content

Commit e7694fd

Browse files
sampokuokkanenXrXr
authored andcommitted
Fix defined? for protected methods defined in a module
me->defined_class is 0 for methods stored on a module, so the protected visibility check in vm_defined always failed and defined? returned nil even when the call would succeed. Use rb_callable_method_entry_with_refinements (where defined_class is populated) and the same vm_defined_class_for_protected_call helper as the call path. defined? now agrees with whether the method could actually be called. [Backport #22076]
1 parent a08f356 commit e7694fd

2 files changed

Lines changed: 33 additions & 5 deletions

File tree

test/ruby/test_defined.rb

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,34 @@ def test_defined_protected_method
6262
f.bar(Class.new(Foo).new) { |v| assert(v, "inherited protected method") }
6363
end
6464

65+
module ProtectedInModule
66+
def m
67+
:m
68+
end
69+
protected :m
70+
def call_m(o)
71+
o.m
72+
end
73+
def defined_m(o)
74+
defined?(o.m)
75+
end
76+
end
77+
class ProtectedIncluderA
78+
include ProtectedInModule
79+
end
80+
class ProtectedIncluderB
81+
include ProtectedInModule
82+
end
83+
84+
def test_defined_protected_method_in_included_module
85+
a = ProtectedIncluderA.new
86+
b = ProtectedIncluderB.new
87+
assert_equal(:m, a.call_m(a))
88+
assert_equal(:m, a.call_m(b))
89+
assert_equal("method", a.defined_m(a))
90+
assert_equal("method", a.defined_m(b))
91+
end
92+
6593
def test_defined_undefined_method
6694
f = Foo.new
6795
assert_nil(defined?(f.quux)) # undefined method

vm_insnhelper.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5585,21 +5585,21 @@ vm_defined(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, rb_num_t op_
55855585
break;
55865586
case DEFINED_METHOD:{
55875587
VALUE klass = CLASS_OF(v);
5588-
const rb_method_entry_t *me = rb_method_entry_with_refinements(klass, SYM2ID(obj), NULL);
5588+
const rb_callable_method_entry_t *cme = rb_callable_method_entry_with_refinements(klass, SYM2ID(obj), NULL);
55895589

5590-
if (me) {
5591-
switch (METHOD_ENTRY_VISI(me)) {
5590+
if (cme) {
5591+
switch (METHOD_ENTRY_VISI(cme)) {
55925592
case METHOD_VISI_PRIVATE:
55935593
break;
55945594
case METHOD_VISI_PROTECTED:
5595-
if (!rb_obj_is_kind_of(GET_SELF(), rb_class_real(me->defined_class))) {
5595+
if (!rb_obj_is_kind_of(GET_SELF(), vm_defined_class_for_protected_call(cme))) {
55965596
break;
55975597
}
55985598
case METHOD_VISI_PUBLIC:
55995599
return true;
56005600
break;
56015601
default:
5602-
rb_bug("vm_defined: unreachable: %u", (unsigned int)METHOD_ENTRY_VISI(me));
5602+
rb_bug("vm_defined: unreachable: %u", (unsigned int)METHOD_ENTRY_VISI(cme));
56035603
}
56045604
}
56055605
else {

0 commit comments

Comments
 (0)