summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog6
-rw-r--r--class.c1
-rw-r--r--eval.c2
-rw-r--r--insns.def23
-rw-r--r--internal.h2
-rw-r--r--test/ruby/test_super.rb80
6 files changed, 110 insertions, 4 deletions
diff --git a/ChangeLog b/ChangeLog
index 822c520bd6..cf3cd87999 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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.
diff --git a/class.c b/class.c
index 8cfb0b123d..4acdc0807d 100644
--- a/class.c
+++ b/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;
}
diff --git a/eval.c b/eval.c
index 7af64fc16c..099bf101a6 100644
--- a/eval.c
+++ b/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);
diff --git a/insns.def b/insns.def
index 7b17b42579..b616368992 100644
--- a/insns.def
+++ b/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();
diff --git a/internal.h b/internal.h
index 08291afdf2..8396b0aad9 100644
--- a/internal.h
+++ b/internal.h
@@ -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 */
diff --git a/test/ruby/test_super.rb b/test/ruby/test_super.rb
index 1d3f2c6fb7..8494745fed 100644
--- a/test/ruby/test_super.rb
+++ b/test/ruby/test_super.rb
@@ -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