Skip to content

Commit 5069c5f

Browse files
jeremyevansnobu
authored andcommitted
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]
1 parent 4325f08 commit 5069c5f

File tree

2 files changed

+40
-6
lines changed

2 files changed

+40
-6
lines changed

class.c

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -825,10 +825,20 @@ VALUE
825825
rb_include_class_new(VALUE module, VALUE super)
826826
{
827827
VALUE klass = class_alloc(T_ICLASS, rb_cClass);
828+
RCLASS_SET_ORIGIN(klass, klass);
829+
830+
RCLASS_M_TBL(OBJ_WB_UNPROTECT(klass)) =
831+
RCLASS_M_TBL(OBJ_WB_UNPROTECT(module)); /* TODO: unprotected? */
828832

829833
if (BUILTIN_TYPE(module) == T_ICLASS) {
834+
if (module != RCLASS_ORIGIN(module)) {
835+
RCLASS_SET_ORIGIN(klass, RCLASS_ORIGIN(module));
836+
}
830837
module = RBASIC(module)->klass;
831838
}
839+
else if (module != RCLASS_ORIGIN(module)) {
840+
RCLASS_SET_ORIGIN(klass, RCLASS_ORIGIN(module));
841+
}
832842
if (!RCLASS_IV_TBL(module)) {
833843
RCLASS_IV_TBL(module) = st_init_numtable();
834844
}
@@ -838,9 +848,6 @@ rb_include_class_new(VALUE module, VALUE super)
838848
RCLASS_IV_TBL(klass) = RCLASS_IV_TBL(module);
839849
RCLASS_CONST_TBL(klass) = RCLASS_CONST_TBL(module);
840850

841-
RCLASS_M_TBL(OBJ_WB_UNPROTECT(klass)) =
842-
RCLASS_M_TBL(OBJ_WB_UNPROTECT(RCLASS_ORIGIN(module))); /* TODO: unprotected? */
843-
844851
RCLASS_SET_SUPER(klass, super);
845852
if (RB_TYPE_P(module, T_ICLASS)) {
846853
RBASIC_SET_CLASS(klass, RBASIC(module)->klass);
@@ -894,8 +901,6 @@ include_modules_at(const VALUE klass, VALUE c, VALUE module, int search_super)
894901
int superclass_seen = FALSE;
895902
struct rb_id_table *tbl;
896903

897-
if (RCLASS_ORIGIN(module) != module)
898-
goto skip;
899904
if (klass_m_tbl && klass_m_tbl == RCLASS_M_TBL(module))
900905
return -1;
901906
/* ignore if the module included already in superclasses */
@@ -1091,10 +1096,11 @@ rb_mod_ancestors(VALUE mod)
10911096
VALUE p, ary = rb_ary_new();
10921097

10931098
for (p = mod; p; p = RCLASS_SUPER(p)) {
1099+
if (p != RCLASS_ORIGIN(p)) continue;
10941100
if (BUILTIN_TYPE(p) == T_ICLASS) {
10951101
rb_ary_push(ary, RBASIC(p)->klass);
10961102
}
1097-
else if (p == RCLASS_ORIGIN(p)) {
1103+
else {
10981104
rb_ary_push(ary, p);
10991105
}
11001106
}

test/ruby/test_refinement.rb

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2322,6 +2322,34 @@ def test_refine_in_using
23222322
assert_equal(:ok, RefineInUsing.test)
23232323
end
23242324

2325+
class Bug16242
2326+
module OtherM
2327+
end
2328+
2329+
module M
2330+
prepend OtherM
2331+
2332+
refine M do
2333+
def refine_method
2334+
"refine_method"
2335+
end
2336+
end
2337+
using M
2338+
2339+
def hoge
2340+
refine_method
2341+
end
2342+
end
2343+
2344+
class X
2345+
include M
2346+
end
2347+
end
2348+
2349+
def test_refine_prepended_module
2350+
assert_equal("refine_method", Bug16242::X.new.hoge)
2351+
end
2352+
23252353
private
23262354

23272355
def eval_using(mod, s)

0 commit comments

Comments
 (0)