mirror of
https://github.com/ruby/ruby.git
synced 2025-08-15 13:39:04 +02:00
merge revision(s) c60aaed185
: [Backport #17130]
Fix Method#super_method for aliased methods Previously, Method#super_method looked at the called_id to determine the method id to use, but that isn't correct for aliased methods, because the super target depends on the original method id, not the called_id. Additionally, aliases can reference methods defined in other classes and modules, and super lookup needs to start in the super of the defined class in such cases. This adds tests for Method#super_method for both types of aliases, one that uses VM_METHOD_TYPE_ALIAS and another that does not. Both check that the results for calling super methods return the expected values. To find the defined class for alias methods, add an rb_ prefix to find_defined_class_by_owner in vm_insnhelper.c and make it non-static, so that it can be called from method_super_method in proc.c. This bug was original discovered while researching [Bug #11189]. Fixes [Bug #17130] --- proc.c | 13 ++++++-- test/ruby/test_method.rb | 80 ++++++++++++++++++++++++++++++++++++++++++++++++ vm_insnhelper.c | 6 ++-- 3 files changed, 94 insertions(+), 5 deletions(-)
This commit is contained in:
parent
70c3a195f3
commit
c98aa2db60
4 changed files with 95 additions and 6 deletions
11
proc.c
11
proc.c
|
@ -3024,6 +3024,8 @@ method_to_proc(VALUE method)
|
|||
return procval;
|
||||
}
|
||||
|
||||
extern VALUE rb_find_defined_class_by_owner(VALUE current_class, VALUE target_owner);
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* meth.super_method -> method
|
||||
|
@ -3043,8 +3045,15 @@ method_super_method(VALUE method)
|
|||
TypedData_Get_Struct(method, struct METHOD, &method_data_type, data);
|
||||
iclass = data->iclass;
|
||||
if (!iclass) return Qnil;
|
||||
if (data->me->def->type == VM_METHOD_TYPE_ALIAS) {
|
||||
super_class = RCLASS_SUPER(rb_find_defined_class_by_owner(data->me->defined_class,
|
||||
data->me->def->body.alias.original_me->owner));
|
||||
mid = data->me->def->body.alias.original_me->def->original_id;
|
||||
}
|
||||
else {
|
||||
super_class = RCLASS_SUPER(RCLASS_ORIGIN(iclass));
|
||||
mid = data->me->called_id;
|
||||
mid = data->me->def->original_id;
|
||||
}
|
||||
if (!super_class) return Qnil;
|
||||
me = (rb_method_entry_t *)rb_callable_method_entry_with_refinements(super_class, mid, &iclass);
|
||||
if (!me) return Qnil;
|
||||
|
|
|
@ -1064,6 +1064,86 @@ class TestMethod < Test::Unit::TestCase
|
|||
'[ruby-core:85231] [Bug #14421]'
|
||||
end
|
||||
|
||||
def test_super_method_alias
|
||||
c0 = Class.new do
|
||||
def m1
|
||||
[:C0_m1]
|
||||
end
|
||||
def m2
|
||||
[:C0_m2]
|
||||
end
|
||||
end
|
||||
|
||||
c1 = Class.new(c0) do
|
||||
def m1
|
||||
[:C1_m1] + super
|
||||
end
|
||||
alias m2 m1
|
||||
end
|
||||
|
||||
c2 = Class.new(c1) do
|
||||
def m2
|
||||
[:C2_m2] + super
|
||||
end
|
||||
end
|
||||
o1 = c2.new
|
||||
assert_equal([:C2_m2, :C1_m1, :C0_m1], o1.m2)
|
||||
|
||||
m = o1.method(:m2)
|
||||
assert_equal([:C2_m2, :C1_m1, :C0_m1], m.call)
|
||||
|
||||
m = m.super_method
|
||||
assert_equal([:C1_m1, :C0_m1], m.call)
|
||||
|
||||
m = m.super_method
|
||||
assert_equal([:C0_m1], m.call)
|
||||
|
||||
assert_nil(m.super_method)
|
||||
end
|
||||
|
||||
def test_super_method_alias_to_prepended_module
|
||||
m = Module.new do
|
||||
def m1
|
||||
[:P_m1] + super
|
||||
end
|
||||
|
||||
def m2
|
||||
[:P_m2] + super
|
||||
end
|
||||
end
|
||||
|
||||
c0 = Class.new do
|
||||
def m1
|
||||
[:C0_m1]
|
||||
end
|
||||
end
|
||||
|
||||
c1 = Class.new(c0) do
|
||||
def m1
|
||||
[:C1_m1] + super
|
||||
end
|
||||
prepend m
|
||||
alias m2 m1
|
||||
end
|
||||
|
||||
o1 = c1.new
|
||||
assert_equal([:P_m2, :P_m1, :C1_m1, :C0_m1], o1.m2)
|
||||
|
||||
m = o1.method(:m2)
|
||||
assert_equal([:P_m2, :P_m1, :C1_m1, :C0_m1], m.call)
|
||||
|
||||
m = m.super_method
|
||||
assert_equal([:P_m1, :C1_m1, :C0_m1], m.call)
|
||||
|
||||
m = m.super_method
|
||||
assert_equal([:C1_m1, :C0_m1], m.call)
|
||||
|
||||
m = m.super_method
|
||||
assert_equal([:C0_m1], m.call)
|
||||
|
||||
assert_nil(m.super_method)
|
||||
end
|
||||
|
||||
def rest_parameter(*rest)
|
||||
rest
|
||||
end
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
# define RUBY_VERSION_MINOR RUBY_API_VERSION_MINOR
|
||||
#define RUBY_VERSION_TEENY 3
|
||||
#define RUBY_RELEASE_DATE RUBY_RELEASE_YEAR_STR"-"RUBY_RELEASE_MONTH_STR"-"RUBY_RELEASE_DAY_STR
|
||||
#define RUBY_PATCHLEVEL 164
|
||||
#define RUBY_PATCHLEVEL 165
|
||||
|
||||
#define RUBY_RELEASE_YEAR 2021
|
||||
#define RUBY_RELEASE_MONTH 3
|
||||
|
|
|
@ -2794,8 +2794,8 @@ current_method_entry(const rb_execution_context_t *ec, rb_control_frame_t *cfp)
|
|||
return cfp;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
find_defined_class_by_owner(VALUE current_class, VALUE target_owner)
|
||||
MJIT_FUNC_EXPORTED VALUE
|
||||
rb_find_defined_class_by_owner(VALUE current_class, VALUE target_owner)
|
||||
{
|
||||
VALUE klass = current_class;
|
||||
|
||||
|
@ -2820,7 +2820,7 @@ aliased_callable_method_entry(const rb_callable_method_entry_t *me)
|
|||
const rb_callable_method_entry_t *cme;
|
||||
|
||||
if (orig_me->defined_class == 0) {
|
||||
VALUE defined_class = find_defined_class_by_owner(me->defined_class, orig_me->owner);
|
||||
VALUE defined_class = rb_find_defined_class_by_owner(me->defined_class, orig_me->owner);
|
||||
VM_ASSERT(RB_TYPE_P(orig_me->owner, T_MODULE));
|
||||
cme = rb_method_entry_complement_defined_class(orig_me, me->called_id, defined_class);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue