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

ruby-changes:24589

From: shugo <ko1@a...>
Date: Mon, 6 Aug 2012 16:00:35 +0900 (JST)
Subject: [ruby-changes:24589] shugo:r36640 (trunk): * internal.h, class.c, eval.c, insns.def: find the appropriate

shugo	2012-08-06 16:00:19 +0900 (Mon, 06 Aug 2012)

  New Revision: 36640

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

  Log:
    * internal.h, class.c, eval.c, insns.def: find the appropriate
      receiver for super called in instance_eval.  If such a receiver is
      not found, raise NoMethodError. [ruby-dev:39772] [Bug #2402]

  Modified files:
    trunk/ChangeLog
    trunk/class.c
    trunk/eval.c
    trunk/insns.def
    trunk/internal.h
    trunk/test/ruby/test_super.rb

Index: ChangeLog
===================================================================
--- ChangeLog	(revision 36639)
+++ ChangeLog	(revision 36640)
@@ -1,3 +1,9 @@
+Mon Aug  6 15:54:50 2012  Shugo Maeda  <shugo@r...>
+
+	* internal.h, class.c, eval.c, insns.def: find the appropriate
+	  receiver for super called in instance_eval.  If such a receiver is
+	  not found, raise NoMethodError. [ruby-dev:39772] [Bug #2402]
+
 Mon Aug  6 14:54:38 2012  Shugo Maeda  <shugo@r...>
 
 	* include/ruby/ruby.h, eval.c, vm_insnhelper.c: fix typo.
Index: insns.def
===================================================================
--- insns.def	(revision 36639)
+++ insns.def	(revision 36640)
@@ -1032,13 +1032,34 @@
     int num = caller_setup_args(th, GET_CFP(), flag,
 				(int)op_argc, blockiseq, &blockptr);
     VALUE recv, klass;
+    rb_control_frame_t *cfp = GET_CFP();
+    rb_control_frame_t *end_cfp = RUBY_VM_END_CONTROL_FRAME(th);
     ID id;
     const rb_method_entry_t *me;
     rb_iseq_t *ip;
 
     flag = VM_CALL_SUPER_BIT | VM_CALL_FCALL_BIT;
 
-    recv = GET_SELF();
+    recv = Qundef;
+    while (RUBY_VM_VALID_CONTROL_FRAME_P(cfp, end_cfp)) {
+	if ((VM_EP_LEP_P(cfp->ep) && cfp->iseq &&
+	     cfp->iseq->type == ISEQ_TYPE_METHOD) ||
+	    (cfp->me && cfp->me->def->type == VM_METHOD_TYPE_BMETHOD)) {
+	    recv = cfp->self;
+	    break;
+	}
+	cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
+    }
+    if (recv == Qundef) {
+	rb_raise(rb_eNoMethodError, "super called outside of method");
+    }
+    klass = GET_CFP()->klass;
+    if (!NIL_P(RCLASS_REFINED_CLASS(klass))) {
+	klass = RCLASS_REFINED_CLASS(klass);
+    }
+    if (!rb_obj_is_kind_of(recv, klass)) {
+	rb_raise(rb_eNoMethodError, "can't find the method for super, which may be called in an orphan block");
+    }
     vm_search_superclass(GET_CFP(), GET_ISEQ(), TOPN(num), &id, &klass);
 
     ip = GET_ISEQ();
Index: eval.c
===================================================================
--- eval.c	(revision 36639)
+++ eval.c	(revision 36640)
@@ -1055,10 +1055,12 @@
     }
     FL_SET(module, RMODULE_IS_OVERLAID);
     c = iclass = rb_include_class_new(module, superclass);
+    RCLASS_REFINED_CLASS(c) = klass;
     module = RCLASS_SUPER(module);
     while (module) {
 	FL_SET(module, RMODULE_IS_OVERLAID);
 	c = RCLASS_SUPER(c) = rb_include_class_new(module, RCLASS_SUPER(c));
+	RCLASS_REFINED_CLASS(c) = klass;
 	module = RCLASS_SUPER(module);
     }
     rb_hash_aset(cref->nd_omod, klass, iclass);
Index: class.c
===================================================================
--- class.c	(revision 36639)
+++ class.c	(revision 36640)
@@ -58,6 +58,7 @@
     RCLASS_SUPER(obj) = 0;
     RCLASS_ORIGIN(obj) = (VALUE)obj;
     RCLASS_IV_INDEX_TBL(obj) = 0;
+    RCLASS_REFINED_CLASS(obj) = Qnil;
     return (VALUE)obj;
 }
 
Index: internal.h
===================================================================
--- internal.h	(revision 36639)
+++ internal.h	(revision 36640)
@@ -28,6 +28,7 @@
     struct st_table *iv_tbl;
     struct st_table *const_tbl;
     VALUE origin;
+    VALUE refined_class;
 };
 
 #undef RCLASS_SUPER
@@ -38,6 +39,7 @@
 #define RCLASS_M_TBL(c) (RCLASS(c)->m_tbl)
 #define RCLASS_IV_INDEX_TBL(c) (RCLASS(c)->iv_index_tbl)
 #define RCLASS_ORIGIN(c) (RCLASS_EXT(c)->origin)
+#define RCLASS_REFINED_CLASS(c) (RCLASS_EXT(c)->refined_class)
 
 struct vtm; /* defined by timev.h */
 
Index: test/ruby/test_super.rb
===================================================================
--- test/ruby/test_super.rb	(revision 36639)
+++ test/ruby/test_super.rb	(revision 36640)
@@ -132,9 +132,9 @@
     assert_equal("A#tt", a.tt(12), "[ruby-core:3856]")
     e = assert_raise(RuntimeError, "[ruby-core:24244]") {
       lambda {
-        Class.new do
-          define_method(:a) {super}.call
-        end
+        Class.new {
+          define_method(:a) {super}
+        }.new.a
       }.call
     }
     assert_match(/implicit argument passing of super from method defined by define_method/, e.message)
@@ -248,4 +248,78 @@
     assert_equal([:Base, :Override, :A, :Override, :B],
                  DoubleInclude2::B.new.foo)
   end
+
+  def test_super_in_instance_eval
+    super_class = Class.new {
+      def foo
+        return [:super, self]
+      end
+    }
+    sub_class = Class.new(super_class) {
+      def foo
+        x = Object.new
+        x.instance_eval do
+          super()
+        end
+      end
+    }
+    obj = sub_class.new
+    assert_equal [:super, obj], obj.foo
+  end
+
+  def test_super_in_instance_eval_with_define_method
+    super_class = Class.new {
+      def foo
+        return [:super, self]
+      end
+    }
+    sub_class = Class.new(super_class) {
+      define_method(:foo) do
+        x = Object.new
+        x.instance_eval do
+          super()
+        end
+      end
+    }
+    obj = sub_class.new
+    assert_equal [:super, obj], obj.foo
+  end
+
+  def test_super_in_orphan_block
+    super_class = Class.new {
+      def foo
+        return [:super, self]
+      end
+    }
+    sub_class = Class.new(super_class) {
+      def foo
+        x = Object.new
+        lambda { super() }
+      end
+    }
+    obj = sub_class.new
+    assert_raise(NoMethodError) do
+      obj.foo.call
+    end
+  end
+
+  def test_super_in_orphan_block_with_instance_eval
+    super_class = Class.new {
+      def foo
+        return [:super, self]
+      end
+    }
+    sub_class = Class.new(super_class) {
+      def foo
+        x = Object.new
+        x.instance_eval do
+          lambda { super() }
+        end
+      end
+    }
+    obj = sub_class.new
+    assert_raise(NoMethodError) do
+      obj.foo.call
+    end
+  end
 end

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

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