なかだです。

trunkで組み込みのメソッドからのNoMethodErrorのメッセージが"no
superclass method"になりません。

$ ./ruby -v -e 'module Enumerable;undef min;end; (1..2).min{}'
ruby 1.9.2dev (2009-10-30 trunk 25565) [universal.x86_64-darwin9.0]
-e:1:in `min': undefined method `min' for 1..2:Range (NoMethodError)
	from -e:1:in `<main>'

一方、__send__を使うとsuperとは無関係なのに出てしまいます。

$ ./ruby -e 'Object.new.method(:__send__).call(:foo)'
-e:1:in `call': super: no superclass method `foo' for #<Object:0x000001002f93f0> (NoMethodError)
	from -e:1:in `<main>'

しかも、もう一度同じメソッドを呼ぶと二回目には出ません。

$ ./ruby -e '2.times{begin Object.new.method(:__send__).call(:foo); rescue NoMethodError => e; puts e.message; end}'
super: no superclass method `foo' for #<Object:0x000001002e2f10>
undefined method `foo' for #<Object:0x000001002e2c68>


Index: vm_eval.c =================================================================== --- vm_eval.c (revision 25565) +++ vm_eval.c (working copy) @@ -22,5 +22,4 @@ static VALUE vm_exec(rb_thread_t *th); static void vm_set_eval_stack(rb_thread_t * th, VALUE iseqval, const NODE *cref); static int vm_collect_local_variables_in_heap(rb_thread_t *th, VALUE *dfp, VALUE ary); -static VALUE send_internal(int argc, const VALUE *argv, VALUE recv, int scope); typedef enum call_type { @@ -30,4 +29,6 @@ typedef enum call_type { } call_type; +static VALUE send_internal(int argc, const VALUE *argv, VALUE recv, call_type scope); + static inline VALUE vm_call0(rb_thread_t* th, VALUE recv, VALUE id, int argc, const VALUE *argv, @@ -105,5 +106,5 @@ vm_call0(rb_thread_t* th, VALUE recv, VA klass = RCLASS_SUPER(klass); if (!klass || !(me = rb_method_entry(klass, id))) { - return method_missing(recv, id, argc, argv, 0); + return method_missing(recv, id, argc, argv, NOEX_SUPER); } RUBY_VM_CHECK_INTS(); @@ -122,5 +123,5 @@ vm_call0(rb_thread_t* th, VALUE recv, VA switch (def->body.optimize_type) { case OPTIMIZED_METHOD_TYPE_SEND: - val = send_internal(argc, argv, recv, NOEX_NOSUPER | NOEX_PRIVATE); + val = send_internal(argc, argv, recv, CALL_FCALL); break; case OPTIMIZED_METHOD_TYPE_CALL: { @@ -176,5 +177,5 @@ vm_call_super(rb_thread_t *th, int argc, me = rb_method_entry(klass, id); if (!me) { - return method_missing(recv, id, argc, argv, 0); + return method_missing(recv, id, argc, argv, NOEX_SUPER); } @@ -220,5 +221,4 @@ rb_call0(VALUE recv, ID mid, int argc, c VALUE klass = CLASS_OF(recv); rb_method_entry_t *me; - struct cache_entry *ent; rb_thread_t *th = GET_THREAD(); ID oid; @@ -234,26 +234,10 @@ rb_call0(VALUE recv, ID mid, int argc, c } - /* is it in the method cache? */ - ent = cache + EXPR1(klass, mid); - - if (ent->mid == mid && ent->klass == klass) { - me = ent->me; - if (UNDEFINED_METHOD_ENTRY_P(me)) { - return method_missing(recv, mid, argc, argv, - scope == CALL_VCALL ? NOEX_VCALL : 0); - } - klass = me->klass; - } - else if ((me = rb_method_entry_without_cache(klass, mid)) != 0 && me->def) { - klass = me->klass; - } - else { - if (scope == 3) { - return method_missing(recv, mid, argc, argv, NOEX_SUPER); - } + me = rb_method_entry(klass, mid); + if (UNDEFINED_METHOD_ENTRY_P(me)) { return method_missing(recv, mid, argc, argv, scope == CALL_VCALL ? NOEX_VCALL : 0); } - + klass = me->klass; oid = me->def->original_id; noex = me->flag; @@ -580,5 +564,5 @@ rb_funcall_no_recursive(VALUE obj, ID id static VALUE -send_internal(int argc, const VALUE *argv, VALUE recv, int scope) +send_internal(int argc, const VALUE *argv, VALUE recv, call_type scope) { VALUE vid; @@ -617,5 +601,5 @@ VALUE rb_f_send(int argc, VALUE *argv, VALUE recv) { - return send_internal(argc, argv, recv, NOEX_NOSUPER | NOEX_PRIVATE); + return send_internal(argc, argv, recv, CALL_FCALL); } @@ -634,5 +618,5 @@ VALUE rb_f_public_send(int argc, VALUE *argv, VALUE recv) { - return send_internal(argc, argv, recv, NOEX_PUBLIC); + return send_internal(argc, argv, recv, CALL_PUBLIC); } Index: test/ruby/test_object.rb =================================================================== --- test/ruby/test_object.rb (revision 25565) +++ test/ruby/test_object.rb (working copy) @@ -378,4 +378,25 @@ class TestObject < Test::Unit::TestCase end + def test_no_superclass_method + o = Object.new + e = assert_raise(NoMethodError) { + o.method(:__send__).call(:never_defined_test_no_superclass_method) + } + m1 = e.message + assert_no_match(/no superclass method/, m1) + e = assert_raise(NoMethodError) { + o.method(:__send__).call(:never_defined_test_no_superclass_method) + } + assert_equal(m1, e.message) + e = assert_raise(NoMethodError) { + o.never_defined_test_no_superclass_method + } + assert_equal(m1, e.message) + end + + def test_superclass_method + assert_in_out_err(["-e", "module Enumerable;undef min;end; (1..2).min{}"], [], [], /no superclass method/) + end + def test_specific_eval_with_wrong_arguments assert_raise(ArgumentError) do
-- --- 僕の前にBugはない。 --- 僕の後ろにBugはできる。 中田 伸悦