summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKoichi Sasada <ko1@atdot.net>2022-12-03 05:55:33 +0900
committerKoichi Sasada <ko1@atdot.net>2022-12-03 08:53:12 +0900
commit59e389af2893c0fcf8b2cfa008c9a16825bf56ff (patch)
tree31224e3498843912e2fc8734710e03224aaca21c
parent7161bf34e161979b97dbc0c1f7450c195faffcfe (diff)
downloadruby-59e389af2893c0fcf8b2cfa008c9a16825bf56ff.tar.gz
UnboundMethod only refer defined_class
UnboundMethod records caller's class, like `D` or `E` on the following case: ```ruby class C def foo = :foo end class D < C end class E < C end d = D.instance_method(:foo) e = E.instance_method(:foo) ``` But `d` and `e` only refers `C#foo` so that UnboundMethod doesn't record `D` or `E`. This behavior changes the following methods: * `UnboundMethod#inspect` (doesn't show caller's class) * `UnboundMethod#==` (`d == e` for example) fix https://bugs.ruby-lang.org/issues/18798
-rw-r--r--proc.c20
-rw-r--r--spec/ruby/core/method/unbind_spec.rb12
-rw-r--r--spec/ruby/core/module/instance_method_spec.rb8
-rw-r--r--spec/ruby/core/unboundmethod/equal_value_spec.rb39
-rw-r--r--spec/ruby/core/unboundmethod/shared/to_s.rb16
-rw-r--r--test/ruby/test_method.rb8
6 files changed, 77 insertions, 26 deletions
diff --git a/proc.c b/proc.c
index 71b6f1ef80..e4762db494 100644
--- a/proc.c
+++ b/proc.c
@@ -1724,8 +1724,14 @@ mnew_internal(const rb_method_entry_t *me, VALUE klass, VALUE iclass,
method = TypedData_Make_Struct(mclass, struct METHOD, &method_data_type, data);
- RB_OBJ_WRITE(method, &data->recv, obj);
- RB_OBJ_WRITE(method, &data->klass, klass);
+ if (obj == Qundef) {
+ RB_OBJ_WRITE(method, &data->recv, Qundef);
+ RB_OBJ_WRITE(method, &data->klass, Qundef);
+ }
+ else {
+ RB_OBJ_WRITE(method, &data->recv, obj);
+ RB_OBJ_WRITE(method, &data->klass, klass);
+ }
RB_OBJ_WRITE(method, &data->iclass, iclass);
RB_OBJ_WRITE(method, &data->owner, original_me->owner);
RB_OBJ_WRITE(method, &data->me, me);
@@ -1876,9 +1882,9 @@ method_unbind(VALUE obj)
method = TypedData_Make_Struct(rb_cUnboundMethod, struct METHOD,
&method_data_type, data);
RB_OBJ_WRITE(method, &data->recv, Qundef);
- RB_OBJ_WRITE(method, &data->klass, orig->klass);
+ RB_OBJ_WRITE(method, &data->klass, Qundef);
RB_OBJ_WRITE(method, &data->iclass, orig->iclass);
- RB_OBJ_WRITE(method, &data->owner, orig->owner);
+ RB_OBJ_WRITE(method, &data->owner, orig->me->owner);
RB_OBJ_WRITE(method, &data->me, rb_method_entry_clone(orig->me));
return method;
@@ -3139,7 +3145,11 @@ method_inspect(VALUE method)
defined_class = RBASIC_CLASS(defined_class);
}
- if (FL_TEST(mklass, FL_SINGLETON)) {
+ if (data->recv == Qundef) {
+ // UnboundMethod
+ rb_str_buf_append(str, rb_inspect(defined_class));
+ }
+ else if (FL_TEST(mklass, FL_SINGLETON)) {
VALUE v = rb_ivar_get(mklass, attached);
if (UNDEF_P(data->recv)) {
diff --git a/spec/ruby/core/method/unbind_spec.rb b/spec/ruby/core/method/unbind_spec.rb
index cdd3a808f2..bdedd513ce 100644
--- a/spec/ruby/core/method/unbind_spec.rb
+++ b/spec/ruby/core/method/unbind_spec.rb
@@ -27,8 +27,16 @@ describe "Method#unbind" do
@string.should =~ /MethodSpecs::MyMod/
end
- it "returns a String containing the Module the method is referenced from" do
- @string.should =~ /MethodSpecs::MySub/
+ ruby_version_is ""..."3.2" do
+ it "returns a String containing the Module the method is referenced from" do
+ @string.should =~ /MethodSpecs::MySub/
+ end
+ end
+
+ ruby_version_is "3.2" do
+ it "returns a String containing the Module the method is referenced from" do
+ @string.should =~ /MethodSpecs::MyMod/
+ end
end
end
diff --git a/spec/ruby/core/module/instance_method_spec.rb b/spec/ruby/core/module/instance_method_spec.rb
index 30e27bb180..8d006e647e 100644
--- a/spec/ruby/core/module/instance_method_spec.rb
+++ b/spec/ruby/core/module/instance_method_spec.rb
@@ -45,10 +45,14 @@ describe "Module#instance_method" do
@parent_um.inspect.should =~ /\bModuleSpecs::InstanceMeth\b/
@child_um.inspect.should =~ /\bfoo\b/
@child_um.inspect.should =~ /\bModuleSpecs::InstanceMeth\b/
- @child_um.inspect.should =~ /\bModuleSpecs::InstanceMethChild\b/
+
@mod_um.inspect.should =~ /\bbar\b/
@mod_um.inspect.should =~ /\bModuleSpecs::InstanceMethMod\b/
- @mod_um.inspect.should =~ /\bModuleSpecs::InstanceMethChild\b/
+
+ ruby_version_is ""..."3.2" do
+ @child_um.inspect.should =~ /\bModuleSpecs::InstanceMethChild\b/
+ @mod_um.inspect.should =~ /\bModuleSpecs::InstanceMethChild\b/
+ end
end
it "raises a TypeError if the given name is not a String/Symbol" do
diff --git a/spec/ruby/core/unboundmethod/equal_value_spec.rb b/spec/ruby/core/unboundmethod/equal_value_spec.rb
index b21677687e..036c6b7f8c 100644
--- a/spec/ruby/core/unboundmethod/equal_value_spec.rb
+++ b/spec/ruby/core/unboundmethod/equal_value_spec.rb
@@ -76,19 +76,38 @@ describe "UnboundMethod#==" do
(@identical_body == @original_body).should == false
end
- it "returns false if same method but one extracted from a subclass" do
- (@parent == @child1).should == false
- (@child1 == @parent).should == false
- end
+ ruby_version_is ""..."3.2" do
+ it "returns false if same method but one extracted from a subclass" do
+ (@parent == @child1).should == false
+ (@child1 == @parent).should == false
+ end
- it "returns false if same method but extracted from two different subclasses" do
- (@child2 == @child1).should == false
- (@child1 == @child2).should == false
+ it "returns false if same method but extracted from two different subclasses" do
+ (@child2 == @child1).should == false
+ (@child1 == @child2).should == false
+ end
+
+ it "returns false if methods are the same but added from an included Module" do
+ (@includee == @includer).should == false
+ (@includer == @includee).should == false
+ end
end
- it "returns false if methods are the same but added from an included Module" do
- (@includee == @includer).should == false
- (@includer == @includee).should == false
+ ruby_version_is "3.2" do
+ it "returns true if same method but one extracted from a subclass" do
+ (@parent == @child1).should == true
+ (@child1 == @parent).should == true
+ end
+
+ it "returns false if same method but extracted from two different subclasses" do
+ (@child2 == @child1).should == true
+ (@child1 == @child2).should == true
+ end
+
+ it "returns true if methods are the same but added from an included Module" do
+ (@includee == @includer).should == true
+ (@includer == @includee).should == true
+ end
end
it "returns false if both have same Module, same name, identical body but not the same" do
diff --git a/spec/ruby/core/unboundmethod/shared/to_s.rb b/spec/ruby/core/unboundmethod/shared/to_s.rb
index 38503c3c60..b92bb0b207 100644
--- a/spec/ruby/core/unboundmethod/shared/to_s.rb
+++ b/spec/ruby/core/unboundmethod/shared/to_s.rb
@@ -20,12 +20,22 @@ describe :unboundmethod_to_s, shared: true do
it "the String shows the method name, Module defined in and Module extracted from" do
@from_module.send(@method).should =~ /\bfrom_mod\b/
@from_module.send(@method).should =~ /\bUnboundMethodSpecs::Mod\b/
- @from_method.send(@method).should =~ /\bUnboundMethodSpecs::Methods\b/
+
+ ruby_version_is ""..."3.2" do
+ @from_method.send(@method).should =~ /\bUnboundMethodSpecs::Methods\b/
+ end
end
it "returns a String including all details" do
- @from_module.send(@method).should.start_with? "#<UnboundMethod: UnboundMethodSpecs::Methods(UnboundMethodSpecs::Mod)#from_mod"
- @from_method.send(@method).should.start_with? "#<UnboundMethod: UnboundMethodSpecs::Methods(UnboundMethodSpecs::Mod)#from_mod"
+ ruby_version_is ""..."3.2" do
+ @from_module.send(@method).should.start_with? "#<UnboundMethod: UnboundMethodSpecs::Methods(UnboundMethodSpecs::Mod)#from_mod"
+ @from_method.send(@method).should.start_with? "#<UnboundMethod: UnboundMethodSpecs::Methods(UnboundMethodSpecs::Mod)#from_mod"
+ end
+
+ ruby_version_is "3.2" do
+ @from_module.send(@method).should.start_with? "#<UnboundMethod: UnboundMethodSpecs::Mod#from_mod"
+ @from_method.send(@method).should.start_with? "#<UnboundMethod: UnboundMethodSpecs::Mod#from_mod"
+ end
end
it "does not show the defining module if it is the same as the origin" do
diff --git a/test/ruby/test_method.rb b/test/ruby/test_method.rb
index d2c7b6e1dd..a04666890e 100644
--- a/test/ruby/test_method.rb
+++ b/test/ruby/test_method.rb
@@ -1236,12 +1236,12 @@ class TestMethod < Test::Unit::TestCase
unbound = b.instance_method(:foo)
assert_equal unbound, b.public_instance_method(:foo)
- assert_equal "#<UnboundMethod: B(A)#foo(arg=...) #{__FILE__}:#{line}>", unbound.inspect
+ assert_equal "#<UnboundMethod: A#foo(arg=...) #{__FILE__}:#{line}>", unbound.inspect
assert_equal [[:opt, :arg]], unbound.parameters
a.remove_method(:foo)
- assert_equal "#<UnboundMethod: B(A)#foo(arg=...) #{__FILE__}:#{line}>", unbound.inspect
+ assert_equal "#<UnboundMethod: A#foo(arg=...) #{__FILE__}:#{line}>", unbound.inspect
assert_equal [[:opt, :arg]], unbound.parameters
obj = b.new
@@ -1281,7 +1281,7 @@ class TestMethod < Test::Unit::TestCase
a.remove_method(:foo)
- assert_equal "#<UnboundMethod: B(A)#foo(arg=...) #{__FILE__}:#{line}>", unbound.inspect
+ assert_equal "#<UnboundMethod: A#foo(arg=...) #{__FILE__}:#{line}>", unbound.inspect
assert_equal [[:opt, :arg]], unbound.parameters
assert_equal a0_foo, unbound.super_method
@@ -1289,7 +1289,7 @@ class TestMethod < Test::Unit::TestCase
assert_equal 1, unbound.bind_call(obj)
assert_include b.instance_methods(false), :foo
- assert_equal "#<UnboundMethod: B(A0)#foo(arg1=..., arg2=...) #{__FILE__}:#{line0}>", b.instance_method(:foo).inspect
+ assert_equal "#<UnboundMethod: A0#foo(arg1=..., arg2=...) #{__FILE__}:#{line0}>", b.instance_method(:foo).inspect
end
def test_zsuper_method_redefined_bind_call