diff options
| author | Mike Dalessio <mike@37signals.com> | 2026-02-09 13:45:34 -0500 |
|---|---|---|
| committer | Nobuyoshi Nakada <nobu.nakada@gmail.com> | 2026-02-10 08:26:48 +0900 |
| commit | 836c1700104c305fa13cbc7b17d114705dd807b2 (patch) | |
| tree | b53b9c439f9c17a0d97c59d5cde131c4a8f8b67b | |
| parent | e0b757f63aa6e720af6200171b2f0841d4efe7ed (diff) | |
Fix UnboundMethod#== for methods from included/extended modules
Method#unbind clones the method entry, preserving its defined_class.
For methods mixed in via include/extend, defined_class is an ICLASS,
causing UnboundMethod#== to return false when comparing against the
same method obtained via Module#instance_method.
Resolve ICLASS defined_class in method_eq.
Fixes [Bug #21873]
| -rw-r--r-- | proc.c | 2 | ||||
| -rw-r--r-- | test/ruby/test_method.rb | 14 |
2 files changed, 16 insertions, 0 deletions
@@ -2006,6 +2006,8 @@ method_eq(VALUE method, VALUE other) klass1 = method_entry_defined_class(m1->me); klass2 = method_entry_defined_class(m2->me); + if (RB_TYPE_P(klass1, T_ICLASS)) klass1 = RBASIC_CLASS(klass1); + if (RB_TYPE_P(klass2, T_ICLASS)) klass2 = RBASIC_CLASS(klass2); if (!rb_method_entry_eq(m1->me, m2->me) || klass1 != klass2 || diff --git a/test/ruby/test_method.rb b/test/ruby/test_method.rb index 8561f841a8..c3819cdebf 100644 --- a/test/ruby/test_method.rb +++ b/test/ruby/test_method.rb @@ -111,6 +111,20 @@ class TestMethod < Test::Unit::TestCase end end + def test_unbound_method_equality_with_extended_module + m = Module.new { def hello; "hello"; end } + base = Class.new { extend m } + sub = Class.new(base) + + from_module = m.instance_method(:hello) + from_base = base.method(:hello).unbind + from_sub = sub.method(:hello).unbind + + assert_equal(from_module, from_base) + assert_equal(from_module, from_sub) + assert_equal(from_base, from_sub) + end + def test_callee assert_equal(:test_callee, __method__) assert_equal(:m, Class.new {def m; __method__; end}.new.m) |
