summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog41
-rw-r--r--class.c1
-rw-r--r--eval.c3
-rw-r--r--include/ruby/ruby.h1
-rw-r--r--test/ruby/test_refinement.rb82
-rw-r--r--vm_insnhelper.c11
6 files changed, 132 insertions, 7 deletions
diff --git a/ChangeLog b/ChangeLog
index b53bed607c..25329a6744 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,44 @@
+Tue Dec 11 00:26:58 2012 Shugo Maeda <shugo@ruby-lang.org>
+
+ * fix the behavior when a module is included into a refinement.
+ This change is a little tricky, so it might be better to prohibit
+ module inclusion to refinements.
+
+ * include/ruby/ruby.h (RMODULE_INCLUDED_INTO_REFINEMENT): new flag
+ to represent that a module (iclass) is included into a refinement.
+
+ * class.c (include_modules_at): set RMODULE_INCLUDED_INTO_REFINEMENT
+ if klass is a refinement.
+
+ * eval.c (rb_mod_refine): set the superclass of a refinement to the
+ refined class for super.
+
+ * eval.c (rb_using_refinement): skip the above superclass (the
+ refined class) when creating iclasses for refinements. Otherwise,
+ `using Refinement1; using Refinement2' creates iclasses:
+ <Refinement2> -> <RefinedClass> -> <Refinement1> -> RefinedClass,
+ where <Module> is an iclass for Module, so RefinedClass is
+ searched before Refinement1. The correct iclasses should be
+ <Refinement2> -> <Refinement1> -> RefinedClass.
+
+ * vm_insnhelper.c (vm_search_normal_superclass): if klass is an
+ iclass for a refinement, use the refinement's superclass instead
+ of the iclass's superclass. Otherwise, multiple refinements are
+ searched by super. For example, if a refinement Refinement2
+ includes a module M (i.e., Refinement2 -> <M> -> RefinedClass,
+ and if refinements iclasses are <Refinement2> -> <M>' ->
+ <Refinement1> -> RefinedClass, then super in <Refinement2> should
+ use Refinement2's superclass <M> instead of <Refinement2>'s
+ superclass <M>'.
+
+ * vm_insnhelper.c (vm_search_super_method): do not raise a
+ NotImplementError if current_defind_class is a module included
+ into a refinement. Because of the change of
+ vm_search_normal_superclass(), the receiver might not be an
+ instance of the module('s iclass).
+
+ * test/ruby/test_refinement.rb: related test.
+
Mon Dec 10 18:35:25 2012 Shugo Maeda <shugo@ruby-lang.org>
* vm_method.c (rb_method_entry_without_refinements): use
diff --git a/class.c b/class.c
index 0aa7561aa6..ca9bdef41e 100644
--- a/class.c
+++ b/class.c
@@ -720,6 +720,7 @@ include_modules_at(VALUE klass, VALUE c, VALUE module)
st_foreach(RMODULE_M_TBL(module), add_refined_method_entry_i,
(st_data_t) refined_class);
+ FL_SET(c, RMODULE_INCLUDED_INTO_REFINEMENT);
}
if (RMODULE_M_TBL(module) && RMODULE_M_TBL(module)->num_entries)
changed = 1;
diff --git a/eval.c b/eval.c
index 886b526af3..c32b974363 100644
--- a/eval.c
+++ b/eval.c
@@ -1079,7 +1079,7 @@ rb_using_refinement(NODE *cref, VALUE klass, VALUE module)
c = iclass = rb_include_class_new(module, superclass);
RCLASS_REFINED_CLASS(c) = klass;
module = RCLASS_SUPER(module);
- while (module) {
+ while (module && module != klass) {
FL_SET(module, RMODULE_IS_OVERLAID);
c = RCLASS_SUPER(c) = rb_include_class_new(module, RCLASS_SUPER(c));
RCLASS_REFINED_CLASS(c) = klass;
@@ -1193,6 +1193,7 @@ rb_mod_refine(VALUE module, VALUE klass)
refinement = rb_hash_lookup(refinements, klass);
if (NIL_P(refinement)) {
refinement = rb_module_new();
+ RCLASS_SUPER(refinement) = klass;
FL_SET(refinement, RMODULE_IS_REFINEMENT);
CONST_ID(id_refined_class, "__refined_class__");
rb_ivar_set(refinement, id_refined_class, klass);
diff --git a/include/ruby/ruby.h b/include/ruby/ruby.h
index f25c67aaef..aa0861e92c 100644
--- a/include/ruby/ruby.h
+++ b/include/ruby/ruby.h
@@ -737,6 +737,7 @@ struct RClass {
#define RMODULE_SUPER(m) RCLASS_SUPER(m)
#define RMODULE_IS_OVERLAID FL_USER2
#define RMODULE_IS_REFINEMENT FL_USER3
+#define RMODULE_INCLUDED_INTO_REFINEMENT FL_USER4
struct RFloat {
struct RBasic basic;
diff --git a/test/ruby/test_refinement.rb b/test/ruby/test_refinement.rb
index 17896eb01d..d572413def 100644
--- a/test/ruby/test_refinement.rb
+++ b/test/ruby/test_refinement.rb
@@ -13,6 +13,10 @@ class TestRefinement < Test::Unit::TestCase
return "Foo#y"
end
+ def a
+ return "Foo#a"
+ end
+
def call_x
return x
end
@@ -31,6 +35,10 @@ class TestRefinement < Test::Unit::TestCase
def z
return "FooExt#z"
end
+
+ def a
+ return "FooExt#a"
+ end
end
end
@@ -98,6 +106,10 @@ class TestRefinement < Test::Unit::TestCase
def self.invoke_y_on(foo)
return foo.y
end
+
+ def self.invoke_a_on(foo)
+ return foo.a
+ end
end
EOF
@@ -122,6 +134,13 @@ class TestRefinement < Test::Unit::TestCase
assert_equal("Foo#y", foo.y)
end
+ def test_using_same_class_refinements
+ foo = Foo.new
+ assert_equal("Foo#a", foo.a)
+ assert_equal("FooExt#a", FooExtClient2.invoke_a_on(foo))
+ assert_equal("Foo#a", foo.a)
+ end
+
def test_new_method
foo = Foo.new
assert_raise(NoMethodError) { foo.z }
@@ -610,6 +629,69 @@ class TestRefinement < Test::Unit::TestCase
end
end
+ module IncludeIntoRefinement
+ class C
+ def bar
+ return "C#bar"
+ end
+
+ def baz
+ return "C#baz"
+ end
+ end
+
+ module Mixin
+ def foo
+ return "Mixin#foo"
+ end
+
+ def bar
+ return super << " Mixin#bar"
+ end
+
+ def baz
+ return super << " Mixin#baz"
+ end
+ end
+
+ module M
+ refine C do
+ include Mixin
+
+ def baz
+ return super << " M#baz"
+ end
+ end
+ end
+ end
+
+ eval <<-EOF, TOPLEVEL_BINDING
+ using TestRefinement::IncludeIntoRefinement::M
+
+ module TestRefinement::IncludeIntoRefinement::User
+ def self.invoke_foo_on(x)
+ x.foo
+ end
+
+ def self.invoke_bar_on(x)
+ x.bar
+ end
+
+ def self.invoke_baz_on(x)
+ x.baz
+ end
+ end
+ EOF
+
+ def test_include_into_refinement
+ x = IncludeIntoRefinement::C.new
+ assert_equal("Mixin#foo", IncludeIntoRefinement::User.invoke_foo_on(x))
+ assert_equal("C#bar Mixin#bar",
+ IncludeIntoRefinement::User.invoke_bar_on(x))
+ assert_equal("C#baz Mixin#baz M#baz",
+ IncludeIntoRefinement::User.invoke_baz_on(x))
+ end
+
private
def eval_using(mod, s)
diff --git a/vm_insnhelper.c b/vm_insnhelper.c
index ff59a609e5..1470844309 100644
--- a/vm_insnhelper.c
+++ b/vm_insnhelper.c
@@ -1866,12 +1866,10 @@ vm_search_normal_superclass(VALUE klass)
{
if (BUILTIN_TYPE(klass) == T_ICLASS &&
FL_TEST(RBASIC(klass)->klass, RMODULE_IS_REFINEMENT)) {
- return rb_refinement_module_get_refined_class(RBASIC(klass)->klass);
- }
- else {
- klass = RCLASS_ORIGIN(klass);
- return RCLASS_SUPER(klass);
+ klass = RBASIC(klass)->klass;
}
+ klass = RCLASS_ORIGIN(klass);
+ return RCLASS_SUPER(klass);
}
static void
@@ -1945,7 +1943,8 @@ vm_search_super_method(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_inf
current_defind_class = RCLASS_REFINED_CLASS(current_defind_class);
}
- if (!rb_obj_is_kind_of(ci->recv, current_defind_class)) {
+ if (!FL_TEST(current_defind_class, RMODULE_INCLUDED_INTO_REFINEMENT) &&
+ !rb_obj_is_kind_of(ci->recv, current_defind_class)) {
rb_raise(rb_eNotImpError, "super from singleton method that is defined to multiple classes is not supported; this will be fixed in 2.0.0 or later");
}