[前][次][番号順一覧][スレッド一覧]

ruby-changes:26241

From: shugo <ko1@a...>
Date: Tue, 11 Dec 2012 01:05:56 +0900 (JST)
Subject: [ruby-changes:26241] shugo:r38298 (trunk): * fix the behavior when a module is included into a refinement.

shugo	2012-12-11 01:05:45 +0900 (Tue, 11 Dec 2012)

  New Revision: 38298

  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=38298

  Log:
    * 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.

  Modified files:
    trunk/ChangeLog
    trunk/class.c
    trunk/eval.c
    trunk/include/ruby/ruby.h
    trunk/test/ruby/test_refinement.rb
    trunk/vm_insnhelper.c

Index: include/ruby/ruby.h
===================================================================
--- include/ruby/ruby.h	(revision 38297)
+++ include/ruby/ruby.h	(revision 38298)
@@ -737,6 +737,7 @@
 #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;
Index: ChangeLog
===================================================================
--- ChangeLog	(revision 38297)
+++ ChangeLog	(revision 38298)
@@ -1,3 +1,44 @@
+Tue Dec 11 00:26:58 2012  Shugo Maeda  <shugo@r...>
+
+	* 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@r...>
 
 	* vm_method.c (rb_method_entry_without_refinements): use
Index: eval.c
===================================================================
--- eval.c	(revision 38297)
+++ eval.c	(revision 38298)
@@ -1079,7 +1079,7 @@
     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 @@
     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);
Index: class.c
===================================================================
--- class.c	(revision 38297)
+++ class.c	(revision 38298)
@@ -720,6 +720,7 @@
 
 	    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;
Index: vm_insnhelper.c
===================================================================
--- vm_insnhelper.c	(revision 38297)
+++ vm_insnhelper.c	(revision 38298)
@@ -1866,12 +1866,10 @@
 {
     if (BUILTIN_TYPE(klass) == T_ICLASS &&
 	FL_TEST(RBASIC(klass)->klass, RMODULE_IS_REFINEMENT)) {
-	return rb_refinement_module_get_refined_class(RBASIC(klass)->klass);
+	klass = RBASIC(klass)->klass;
     }
-    else {
-	klass = RCLASS_ORIGIN(klass);
-	return RCLASS_SUPER(klass);
-    }
+    klass = RCLASS_ORIGIN(klass);
+    return RCLASS_SUPER(klass);
 }
 
 static void
@@ -1945,7 +1943,8 @@
 	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");
     }
 
Index: test/ruby/test_refinement.rb
===================================================================
--- test/ruby/test_refinement.rb	(revision 38297)
+++ test/ruby/test_refinement.rb	(revision 38298)
@@ -13,6 +13,10 @@
       return "Foo#y"
     end
 
+    def a
+      return "Foo#a"
+    end
+
     def call_x
       return x
     end
@@ -31,6 +35,10 @@
       def z
         return "FooExt#z"
       end
+
+      def a
+        return "FooExt#a"
+      end
     end
   end
 
@@ -98,6 +106,10 @@
       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 @@
     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 @@
     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)

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]