mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
Honor refinements for modules that prepend other modules
This previously did not work, and the reason it did not work is that: 1) Refining a module or class that prepends other modules places the refinements in the class itself and not the origin iclass. 2) Inclusion of a module that prepends other modules skips the module itself, including only iclasses for the prepended modules and the origin iclass. Those two behaviors combined meant that the method table for the refined methods for the included module never ends up in the method lookup chain for the class including the module. Fix this by not skipping the module itself when the module is included. This requires some code rearranging in rb_include_class_new to make sure the correct method tables and origin settings are used for the created iclass. As origin iclasses shouldn't be exposed to Ruby, this also requires skipping modules that have origin iclasses in Module#ancestors (classes that have origin iclasses were already skipped). Fixes [Bug #16242]
This commit is contained in:
parent
4325f08086
commit
5069c5f521
Notes:
git
2019-11-28 19:57:37 +09:00
2 changed files with 40 additions and 6 deletions
18
class.c
18
class.c
|
@ -825,10 +825,20 @@ VALUE
|
||||||
rb_include_class_new(VALUE module, VALUE super)
|
rb_include_class_new(VALUE module, VALUE super)
|
||||||
{
|
{
|
||||||
VALUE klass = class_alloc(T_ICLASS, rb_cClass);
|
VALUE klass = class_alloc(T_ICLASS, rb_cClass);
|
||||||
|
RCLASS_SET_ORIGIN(klass, klass);
|
||||||
|
|
||||||
|
RCLASS_M_TBL(OBJ_WB_UNPROTECT(klass)) =
|
||||||
|
RCLASS_M_TBL(OBJ_WB_UNPROTECT(module)); /* TODO: unprotected? */
|
||||||
|
|
||||||
if (BUILTIN_TYPE(module) == T_ICLASS) {
|
if (BUILTIN_TYPE(module) == T_ICLASS) {
|
||||||
|
if (module != RCLASS_ORIGIN(module)) {
|
||||||
|
RCLASS_SET_ORIGIN(klass, RCLASS_ORIGIN(module));
|
||||||
|
}
|
||||||
module = RBASIC(module)->klass;
|
module = RBASIC(module)->klass;
|
||||||
}
|
}
|
||||||
|
else if (module != RCLASS_ORIGIN(module)) {
|
||||||
|
RCLASS_SET_ORIGIN(klass, RCLASS_ORIGIN(module));
|
||||||
|
}
|
||||||
if (!RCLASS_IV_TBL(module)) {
|
if (!RCLASS_IV_TBL(module)) {
|
||||||
RCLASS_IV_TBL(module) = st_init_numtable();
|
RCLASS_IV_TBL(module) = st_init_numtable();
|
||||||
}
|
}
|
||||||
|
@ -838,9 +848,6 @@ rb_include_class_new(VALUE module, VALUE super)
|
||||||
RCLASS_IV_TBL(klass) = RCLASS_IV_TBL(module);
|
RCLASS_IV_TBL(klass) = RCLASS_IV_TBL(module);
|
||||||
RCLASS_CONST_TBL(klass) = RCLASS_CONST_TBL(module);
|
RCLASS_CONST_TBL(klass) = RCLASS_CONST_TBL(module);
|
||||||
|
|
||||||
RCLASS_M_TBL(OBJ_WB_UNPROTECT(klass)) =
|
|
||||||
RCLASS_M_TBL(OBJ_WB_UNPROTECT(RCLASS_ORIGIN(module))); /* TODO: unprotected? */
|
|
||||||
|
|
||||||
RCLASS_SET_SUPER(klass, super);
|
RCLASS_SET_SUPER(klass, super);
|
||||||
if (RB_TYPE_P(module, T_ICLASS)) {
|
if (RB_TYPE_P(module, T_ICLASS)) {
|
||||||
RBASIC_SET_CLASS(klass, RBASIC(module)->klass);
|
RBASIC_SET_CLASS(klass, RBASIC(module)->klass);
|
||||||
|
@ -894,8 +901,6 @@ include_modules_at(const VALUE klass, VALUE c, VALUE module, int search_super)
|
||||||
int superclass_seen = FALSE;
|
int superclass_seen = FALSE;
|
||||||
struct rb_id_table *tbl;
|
struct rb_id_table *tbl;
|
||||||
|
|
||||||
if (RCLASS_ORIGIN(module) != module)
|
|
||||||
goto skip;
|
|
||||||
if (klass_m_tbl && klass_m_tbl == RCLASS_M_TBL(module))
|
if (klass_m_tbl && klass_m_tbl == RCLASS_M_TBL(module))
|
||||||
return -1;
|
return -1;
|
||||||
/* ignore if the module included already in superclasses */
|
/* ignore if the module included already in superclasses */
|
||||||
|
@ -1091,10 +1096,11 @@ rb_mod_ancestors(VALUE mod)
|
||||||
VALUE p, ary = rb_ary_new();
|
VALUE p, ary = rb_ary_new();
|
||||||
|
|
||||||
for (p = mod; p; p = RCLASS_SUPER(p)) {
|
for (p = mod; p; p = RCLASS_SUPER(p)) {
|
||||||
|
if (p != RCLASS_ORIGIN(p)) continue;
|
||||||
if (BUILTIN_TYPE(p) == T_ICLASS) {
|
if (BUILTIN_TYPE(p) == T_ICLASS) {
|
||||||
rb_ary_push(ary, RBASIC(p)->klass);
|
rb_ary_push(ary, RBASIC(p)->klass);
|
||||||
}
|
}
|
||||||
else if (p == RCLASS_ORIGIN(p)) {
|
else {
|
||||||
rb_ary_push(ary, p);
|
rb_ary_push(ary, p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2322,6 +2322,34 @@ class TestRefinement < Test::Unit::TestCase
|
||||||
assert_equal(:ok, RefineInUsing.test)
|
assert_equal(:ok, RefineInUsing.test)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class Bug16242
|
||||||
|
module OtherM
|
||||||
|
end
|
||||||
|
|
||||||
|
module M
|
||||||
|
prepend OtherM
|
||||||
|
|
||||||
|
refine M do
|
||||||
|
def refine_method
|
||||||
|
"refine_method"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
using M
|
||||||
|
|
||||||
|
def hoge
|
||||||
|
refine_method
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class X
|
||||||
|
include M
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_refine_prepended_module
|
||||||
|
assert_equal("refine_method", Bug16242::X.new.hoge)
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def eval_using(mod, s)
|
def eval_using(mod, s)
|
||||||
|
|
Loading…
Reference in a new issue