mirror of
				https://github.com/ruby/ruby.git
				synced 2022-11-09 12:17:21 -05:00 
			
		
		
		
	* 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] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@36640 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
		
							parent
							
								
									3dd941b234
								
							
						
					
					
						commit
						9537e8ffe5
					
				
					 6 changed files with 110 additions and 4 deletions
				
			
		| 
						 | 
				
			
			@ -1,3 +1,9 @@
 | 
			
		|||
Mon Aug  6 15:54:50 2012  Shugo Maeda  <shugo@ruby-lang.org>
 | 
			
		||||
 | 
			
		||||
	* 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@ruby-lang.org>
 | 
			
		||||
 | 
			
		||||
	* include/ruby/ruby.h, eval.c, vm_insnhelper.c: fix typo.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										1
									
								
								class.c
									
										
									
									
									
								
							
							
						
						
									
										1
									
								
								class.c
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -58,6 +58,7 @@ class_alloc(VALUE flags, VALUE klass)
 | 
			
		|||
    RCLASS_SUPER(obj) = 0;
 | 
			
		||||
    RCLASS_ORIGIN(obj) = (VALUE)obj;
 | 
			
		||||
    RCLASS_IV_INDEX_TBL(obj) = 0;
 | 
			
		||||
    RCLASS_REFINED_CLASS(obj) = Qnil;
 | 
			
		||||
    return (VALUE)obj;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										2
									
								
								eval.c
									
										
									
									
									
								
							
							
						
						
									
										2
									
								
								eval.c
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -1055,10 +1055,12 @@ rb_overlay_module(NODE *cref, VALUE klass, VALUE module)
 | 
			
		|||
    }
 | 
			
		||||
    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);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										23
									
								
								insns.def
									
										
									
									
									
								
							
							
						
						
									
										23
									
								
								insns.def
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -1032,13 +1032,34 @@ invokesuper
 | 
			
		|||
    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();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -28,6 +28,7 @@ struct rb_classext_struct {
 | 
			
		|||
    struct st_table *iv_tbl;
 | 
			
		||||
    struct st_table *const_tbl;
 | 
			
		||||
    VALUE origin;
 | 
			
		||||
    VALUE refined_class;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#undef RCLASS_SUPER
 | 
			
		||||
| 
						 | 
				
			
			@ -38,6 +39,7 @@ struct rb_classext_struct {
 | 
			
		|||
#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 */
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -132,9 +132,9 @@ class TestSuper < Test::Unit::TestCase
 | 
			
		|||
    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 @@ class TestSuper < Test::Unit::TestCase
 | 
			
		|||
    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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue