ruby-changes:72995
From: Benoit <ko1@a...>
Date: Sat, 20 Aug 2022 20:44:28 +0900 (JST)
Subject: [ruby-changes:72995] 209631a45f (master): Consider resolved-through-zsuper methods equal for compatibility
https://git.ruby-lang.org/ruby.git/commit/?id=209631a45f From 209631a45f9682dedf718f4b4a140efe7d21a6fc Mon Sep 17 00:00:00 2001 From: Benoit Daloze <eregontp@g...> Date: Mon, 15 Aug 2022 16:01:33 +0200 Subject: Consider resolved-through-zsuper methods equal for compatibility * Fixes https://bugs.ruby-lang.org/issues/18751 --- proc.c | 65 +++++++++++------------- spec/ruby/core/unboundmethod/equal_value_spec.rb | 37 ++++++++++++++ test/ruby/test_method.rb | 18 +++++++ 3 files changed, 86 insertions(+), 34 deletions(-) diff --git a/proc.c b/proc.c index dbf28aa55e..f9bd469618 100644 --- a/proc.c +++ b/proc.c @@ -1738,6 +1738,27 @@ mnew_unbound(VALUE klass, ID id, VALUE mclass, int scope) https://github.com/ruby/ruby/blob/trunk/proc.c#L1738 return mnew_from_me(me, klass, iclass, Qundef, id, mclass, scope); } +static const rb_method_entry_t* +zsuper_resolve(const rb_method_entry_t *me) +{ + const rb_method_entry_t *super_me; + while (me->def->type == VM_METHOD_TYPE_ZSUPER) { + VALUE defined_class = me->defined_class ? me->defined_class : me->owner; + VALUE super_class = RCLASS_SUPER(RCLASS_ORIGIN(defined_class)); + if (!super_class) { + break; + } + ID id = me->def->original_id; + VALUE iclass; + super_me = (rb_method_entry_t *)rb_callable_method_entry_with_refinements(super_class, id, &iclass); + if (!super_me) { + break; + } + me = super_me; + } + return me; +} + static inline VALUE method_entry_defined_class(const rb_method_entry_t *me) { @@ -1798,10 +1819,13 @@ method_eq(VALUE method, VALUE other) https://github.com/ruby/ruby/blob/trunk/proc.c#L1819 m1 = (struct METHOD *)DATA_PTR(method); m2 = (struct METHOD *)DATA_PTR(other); - klass1 = method_entry_defined_class(m1->me); - klass2 = method_entry_defined_class(m2->me); + const rb_method_entry_t *m1_me = zsuper_resolve(m1->me); + const rb_method_entry_t *m2_me = zsuper_resolve(m2->me); - if (!rb_method_entry_eq(m1->me, m2->me) || + klass1 = method_entry_defined_class(m1_me); + klass2 = method_entry_defined_class(m2_me); + + if (!rb_method_entry_eq(m1_me, m2_me) || klass1 != klass2 || m1->klass != m2->klass || m1->recv != m2->recv) { @@ -2945,22 +2969,12 @@ rb_method_entry_location(const rb_method_entry_t *me) https://github.com/ruby/ruby/blob/trunk/proc.c#L2969 return method_def_location(me->def); } -static VALUE method_super_method(VALUE method); - static const rb_method_definition_t * zsuper_ref_method_def(VALUE method) { - const rb_method_definition_t *def = rb_method_def(method); - VALUE super_method; - while (def->type == VM_METHOD_TYPE_ZSUPER) { - super_method = method_super_method(method); - if (NIL_P(super_method)) { - break; - } - method = super_method; - def = rb_method_def(method); - } - return def; + const struct METHOD *data; + TypedData_Get_Struct(method, struct METHOD, &method_data_type, data); + return zsuper_resolve(data->me)->def; } /* @@ -3124,25 +3138,8 @@ method_inspect(VALUE method) https://github.com/ruby/ruby/blob/trunk/proc.c#L3138 if (data->me->def->type == VM_METHOD_TYPE_ALIAS) { defined_class = data->me->def->body.alias.original_me->owner; } - else if (data->me->def->type == VM_METHOD_TYPE_ZSUPER) { - const rb_method_definition_t *zsuper_ref_def = data->me->def; - struct METHOD *zsuper_ref_data; - VALUE super_method; - - do { - super_method = method_super_method(method); - if (NIL_P(super_method)) { - break; - } - method = super_method; - zsuper_ref_def = rb_method_def(method); - } while (zsuper_ref_def->type == VM_METHOD_TYPE_ZSUPER); - - TypedData_Get_Struct(method, struct METHOD, &method_data_type, zsuper_ref_data); - defined_class = method_entry_defined_class(zsuper_ref_data->me); - } else { - defined_class = method_entry_defined_class(data->me); + defined_class = method_entry_defined_class(zsuper_resolve(data->me)); } if (RB_TYPE_P(defined_class, T_ICLASS)) { diff --git a/spec/ruby/core/unboundmethod/equal_value_spec.rb b/spec/ruby/core/unboundmethod/equal_value_spec.rb index 6242b04884..b21677687e 100644 --- a/spec/ruby/core/unboundmethod/equal_value_spec.rb +++ b/spec/ruby/core/unboundmethod/equal_value_spec.rb @@ -98,4 +98,41 @@ describe "UnboundMethod#==" do https://github.com/ruby/ruby/blob/trunk/spec/ruby/core/unboundmethod/equal_value_spec.rb#L98 (@discard_1 == UnboundMethodSpecs::Methods.instance_method(:discard_1)).should == false end + + it "considers methods through aliasing equal" do + c = Class.new do + class << self + alias_method :n, :new + end + end + + c.method(:new).should == c.method(:n) + c.method(:n).should == Class.instance_method(:new).bind(c) + end + + # On CRuby < 3.2, the 2 specs below pass due to method/instance_method skipping zsuper methods. + # We are interested in the general pattern working, i.e. the combination of method/instance_method + # and #== exposes the wanted behavior. + it "considers methods through visibility change equal" do + c = Class.new do + class << self + private :new + end + end + + c.method(:new).should == Class.instance_method(:new).bind(c) + end + + it "considers methods through aliasing and visibility change equal" do + c = Class.new do + class << self + alias_method :n, :new + private :new + end + end + + c.method(:new).should == c.method(:n) + c.method(:n).should == Class.instance_method(:new).bind(c) + c.method(:new).should == Class.instance_method(:new).bind(c) + end end diff --git a/test/ruby/test_method.rb b/test/ruby/test_method.rb index 5f689c3d4f..7e440095c8 100644 --- a/test/ruby/test_method.rb +++ b/test/ruby/test_method.rb @@ -1241,6 +1241,24 @@ class TestMethod < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_method.rb#L1241 assert_raise_with_message(NoMethodError, /super: no superclass method `foo'/) { unbound.bind_call(obj) } end + # Bug #18751 + def method_equality_visbility_alias + c = Class.new do + class << self + alias_method :n, :new + private :new + end + end + + assert_equal c.method(:n), c.method(:new) + + assert_not_equal c.method(:n), Class.method(:new) + assert_equal c.method(:n) == Class.instance_method(:new).bind(c) + + assert_not_equal c.method(:new), Class.method(:new) + assert_equal c.method(:new), Class.instance_method(:new).bind(c) + end + def rest_parameter(*rest) rest end -- cgit v1.2.1 -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/