mirror of
https://github.com/ruby/ruby.git
synced 2026-08-02 20:49:12 +08:00
Fix UnboundMethod#== for methods from included/extended modules [Backport #21873]
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. [Bug #21873]
This commit is contained in:
parent
e025c839ac
commit
0768f08caf
2
proc.c
2
proc.c
@ -1996,6 +1996,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 ||
|
||||
|
||||
@ -35,6 +35,12 @@ describe "UnboundMethod#==" do
|
||||
|
||||
@method_one = UnboundMethodSpecs::Methods.instance_method(:one)
|
||||
@method_two = UnboundMethodSpecs::Methods.instance_method(:two)
|
||||
|
||||
@mixin = UnboundMethodSpecs::Mixin.instance_method(:mixin_method)
|
||||
@includer_base = UnboundMethodSpecs::IncluderBase.new.method(:mixin_method).unbind
|
||||
@includer_child = UnboundMethodSpecs::IncluderChild.new.method(:mixin_method).unbind
|
||||
@extender_base = UnboundMethodSpecs::ExtenderBase.method(:mixin_method).unbind
|
||||
@extender_child = UnboundMethodSpecs::ExtenderChild.method(:mixin_method).unbind
|
||||
end
|
||||
|
||||
it "returns true if objects refer to the same method" do
|
||||
@ -91,6 +97,30 @@ describe "UnboundMethod#==" do
|
||||
(@includer == @includee).should == true
|
||||
end
|
||||
|
||||
ruby_bug "#21873", ""..."4.0" do
|
||||
it "returns true if same method is present in an object through module inclusion" do
|
||||
(@mixin == @includer_base).should == true
|
||||
(@includer_base == @mixin).should == true
|
||||
|
||||
(@mixin == @includer_child).should == true
|
||||
(@includer_child == @mixin).should == true
|
||||
|
||||
(@includer_base == @includer_child).should == true
|
||||
(@includer_child == @includer_base).should == true
|
||||
end
|
||||
|
||||
it "returns true if same method is present in an object through module extension" do
|
||||
(@mixin == @extender_base).should == true
|
||||
(@extender_base == @mixin).should == true
|
||||
|
||||
(@mixin == @extender_child).should == true
|
||||
(@extender_child == @mixin).should == true
|
||||
|
||||
(@extender_base == @extender_child).should == true
|
||||
(@extender_child == @extender_base).should == true
|
||||
end
|
||||
end
|
||||
|
||||
it "returns false if both have same Module, same name, identical body but not the same" do
|
||||
class UnboundMethodSpecs::Methods
|
||||
def discard_1; :discard; end
|
||||
|
||||
@ -80,6 +80,22 @@ module UnboundMethodSpecs
|
||||
end
|
||||
end
|
||||
|
||||
module Mixin
|
||||
def mixin_method; end
|
||||
end
|
||||
|
||||
class IncluderBase
|
||||
include Mixin
|
||||
end
|
||||
|
||||
class IncluderChild < IncluderBase; end
|
||||
|
||||
class ExtenderBase
|
||||
extend Mixin
|
||||
end
|
||||
|
||||
class ExtenderChild < ExtenderBase; end
|
||||
|
||||
class A
|
||||
def baz(a, b)
|
||||
return [__FILE__, self.class]
|
||||
|
||||
@ -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)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user