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/