summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--class.c27
-rw-r--r--eval.c3
-rw-r--r--internal.h1
-rw-r--r--test/ruby/test_refinement.rb32
4 files changed, 55 insertions, 8 deletions
diff --git a/class.c b/class.c
index e03ba22576..f9e2322aeb 100644
--- a/class.c
+++ b/class.c
@@ -890,6 +890,8 @@ add_refined_method_entry_i(ID key, VALUE value, void *data)
return ID_TABLE_CONTINUE;
}
+static void ensure_origin(VALUE klass);
+
static int
include_modules_at(const VALUE klass, VALUE c, VALUE module, int search_super)
{
@@ -897,6 +899,10 @@ include_modules_at(const VALUE klass, VALUE c, VALUE module, int search_super)
int method_changed = 0, constant_changed = 0;
struct rb_id_table *const klass_m_tbl = RCLASS_M_TBL(RCLASS_ORIGIN(klass));
+ if (FL_TEST(module, RCLASS_REFINED_BY_ANY)) {
+ ensure_origin(module);
+ }
+
while (module) {
int superclass_seen = FALSE;
struct rb_id_table *tbl;
@@ -978,15 +984,10 @@ move_refined_method(ID key, VALUE value, void *data)
}
}
-void
-rb_prepend_module(VALUE klass, VALUE module)
+static void
+ensure_origin(VALUE klass)
{
- VALUE origin;
- int changed = 0;
-
- ensure_includable(klass, module);
-
- origin = RCLASS_ORIGIN(klass);
+ VALUE origin = RCLASS_ORIGIN(klass);
if (origin == klass) {
origin = class_alloc(T_ICLASS, klass);
OBJ_WB_UNPROTECT(origin); /* TODO: conservative shading. Need more survey. */
@@ -997,6 +998,16 @@ rb_prepend_module(VALUE klass, VALUE module)
RCLASS_M_TBL_INIT(klass);
rb_id_table_foreach(RCLASS_M_TBL(origin), move_refined_method, (void *)klass);
}
+}
+
+void
+rb_prepend_module(VALUE klass, VALUE module)
+{
+ VALUE origin;
+ int changed = 0;
+
+ ensure_includable(klass, module);
+ ensure_origin(klass);
changed = include_modules_at(klass, klass, module, FALSE);
if (changed < 0)
rb_raise(rb_eArgError, "cyclic prepend detected");
diff --git a/eval.c b/eval.c
index 77b0efa0fc..7bcb5447b7 100644
--- a/eval.c
+++ b/eval.c
@@ -1542,6 +1542,9 @@ rb_mod_refine(VALUE module, VALUE klass)
}
ensure_class_or_module(klass);
+ if (RB_TYPE_P(klass, T_MODULE)) {
+ FL_SET(klass, RCLASS_REFINED_BY_ANY);
+ }
CONST_ID(id_refinements, "__refinements__");
refinements = rb_attr_get(module, id_refinements);
if (NIL_P(refinements)) {
diff --git a/internal.h b/internal.h
index 50c129e7cd..ca635c9e36 100644
--- a/internal.h
+++ b/internal.h
@@ -1081,6 +1081,7 @@ int rb_singleton_class_internal_p(VALUE sklass);
#define RCLASS_CLONED FL_USER6
#define RICLASS_IS_ORIGIN FL_USER5
+#define RCLASS_REFINED_BY_ANY FL_USER7
static inline void
RCLASS_SET_ORIGIN(VALUE klass, VALUE origin)
diff --git a/test/ruby/test_refinement.rb b/test/ruby/test_refinement.rb
index 618175f931..6fb04de5d6 100644
--- a/test/ruby/test_refinement.rb
+++ b/test/ruby/test_refinement.rb
@@ -2350,6 +2350,38 @@ class TestRefinement < Test::Unit::TestCase
assert_equal("refine_method", Bug16242::X.new.hoge)
end
+ module Bug13446
+ module Enumerable
+ def sum(*args)
+ i = 0
+ args.each { |arg| i += a }
+ i
+ end
+ end
+
+ using Module.new {
+ refine Enumerable do
+ alias :orig_sum :sum
+ end
+ }
+
+ module Enumerable
+ def sum(*args)
+ orig_sum(*args)
+ end
+ end
+
+ class GenericEnumerable
+ include Enumerable
+ end
+
+ Enumerable.prepend(Module.new)
+ end
+
+ def test_prepend_refined_module
+ assert_equal(0, Bug13446::GenericEnumerable.new.sum)
+ end
+
private
def eval_using(mod, s)