[ruby-trunk - Bug #5731][Open] enum for を使うと method missing にブロックが渡されない

Issue #5731 has been reported by Masahiro T…


Bug #5731: enum_for を使うと method_missing にブロックが渡されない

Author: Masahiro T.
Status: Open
Priority: Normal
Assignee:
Category:
Target version:
ruby -v: ruby 1.9.3p0 (2011-10-30) [i686-linux]

次のスクリプトを 1.9.3 で実行すると enum_for の方は block が nil になります。
1.8.7 では両方とも block が渡されました。


class A
def method_missing(name, *args, &block)
p block
end
end

a = A.new
a.hoge{|l| p l} # ブロックが渡される
a.enum_for(:hoge).each{|l| p l} # ブロックが渡されない

% /usr/local/ruby187/bin/ruby -v a.rb
ruby 1.8.7 (2011-06-30 patchlevel 352) [i686-linux]
#Proc:[email protected]:9
#Proc:[email protected]:10

% ruby -v a.rb
ruby 1.9.3p0 (2011-10-30) [i686-linux]
#Proc:[email protected]:9
nil

使い方が間違ってるだけだったらすいません

Issue #5731 has been updated by Kazuki Tsujimoto.

=begin
今のmethod_missing関数の実装はpassed_blockを無条件に0クリアするようになっています。

同関数ではmethod_missingメソッドを呼び出す前に色々と処理を行っており
その際にraiseされる可能性があるので0クリアしておくことは必要ですが、
前処理が終わった時点でpassed_blockを再設定しておかないといけないという話ではないかと思います。

以下のパッチでどうでしょうか。

diff --git a/vm_eval.c b/vm_eval.c
index 44edf6f…d653e4e 100644
— a/vm_eval.c
+++ b/vm_eval.c
@@ -563,6 +563,7 @@ method_missing(VALUE obj, ID id, int argc, const
VALUE *argv, int call_status)
{
VALUE *nargv, result, argv_ary = 0;
rb_thread_t *th = GET_THREAD();

  • const rb_block_t *blockptr = th->passed_block;

    th->method_missing_reason = call_status;
    th->passed_block = 0;
    @@ -589,6 +590,7 @@ method_missing(VALUE obj, ID id, int argc, const
    VALUE *argv, int call_status)
    if (rb_method_basic_definition_p(CLASS_OF(obj) , idMethodMissing))
    {
    raise_method_missing(th, argc+1, nargv, obj, call_status |
    NOEX_MISSING);
    }

  • th->passed_block = blockptr;
    result = rb_funcall2(obj, idMethodMissing, argc + 1, nargv);
    if (argv_ary) rb_ary_clear(argv_ary);
    return result;
    =end


Bug #5731: enum_for を使うと method_missing にブロックが渡されない

Author: Masahiro T.
Status: Open
Priority: Normal
Assignee:
Category:
Target version:
ruby -v: ruby 1.9.3p0 (2011-10-30) [i686-linux]

次のスクリプトを 1.9.3 で実行すると enum_for の方は block が nil になります。
1.8.7 では両方とも block が渡されました。


class A
def method_missing(name, *args, &block)
p block
end
end

a = A.new
a.hoge{|l| p l} # ブロックが渡される
a.enum_for(:hoge).each{|l| p l} # ブロックが渡されない

% /usr/local/ruby187/bin/ruby -v a.rb
ruby 1.8.7 (2011-06-30 patchlevel 352) [i686-linux]
#Proc:[email protected]:9
#Proc:[email protected]:10

% ruby -v a.rb
ruby 1.9.3p0 (2011-10-30) [i686-linux]
#Proc:[email protected]:9
nil

使い方が間違ってるだけだったらすいません

$B$^$D$b$H(B $B$f$-$R$m$G$9(B

In message “Re: [ruby-dev:45003] [ruby-trunk - Bug #5731] enum_for
$B$r;H$&$H(B method_missing $B$K%V%m%C%/$,EO$5$l$J$$(B”
on Sun, 18 Dec 2011 00:33:10 +0900, Kazuki Tsujimoto
[email protected] writes:

|=begin
|$B:#$N(Bmethod_missing$B4X?t$N<BAu$O(Bpassed_block$B$rL5>r7o$K(B0$B%/%j%“$9$k$h$&$K$J$C$F$$$^$9!#(B
|
|$BF14X?t$G$O(Bmethod_missing$B%a%=%C%I$r8F$S=P$9A0$K?'!9$H=hM}$r9T$C$F$$j(B
|$B$=$N:]$K(Braise$B$5$l$k2DG=@-$,$“$k$N$G(B0$B%/%j%”$7$F$
$/$3$H$OI,MW$G$9$,!”(B
|$BA0=hM}$,=$o$C$?;~E@$G(Bpassed_block$B$r:F@_Dj$7$F$$+$J$$$H$$$1$J$$$H$$$&OC$G$O$J$$$+$H;W$$$^$9!#(B
|
|$B0J2<$N%Q%C%A$G$I$&$G$7$g$&$+!#(B

$B$3$N%Q%C%A$r3NG’$7$F$/$@$5$kJ}$O$$$i$C$7$c$$$^$;$s$+!)(B
$BLdBj$,FbMF$J$i@Q6KE*$K<h$j9~$_$?$$$N$G$9$,!#(B

Issue #5731 has been updated by satoshi shiba.

芝と申します。

パッチが正しいかどうかは分かりませんが、passing_block の設定が抜けてる関数は method_missing
以外にもあるので、一緒に対処してはどうでしょうか。

例えば、vm_call0 での passing_block の設定忘れは次のコードで確認できます。

問題となるコード(vm_call0 のほう)

missing.rb

def respond_to_missing?(*args)
true
end

def method_missing(mid, *args)
yield
end

m = method(:call_missing)
m.call(){ puts “passing block ok” }

追加パッチ

Index: vm_eval.c

— vm_eval.c (revision 34071)
+++ vm_eval.c (working copy)
@@ -118,6 +118,7 @@

    RB_GC_GUARD(new_args);
    rb_ary_unshift(new_args, ID2SYM(id));
  •   th->passed_block = blockptr;
      return rb_funcall2(recv, idMethodMissing,
                         argc+1, RARRAY_PTR(new_args));
     }
    

Index: vm_insnhelper.c

— vm_insnhelper.c (revision 34071)
+++ vm_insnhelper.c (working copy)
@@ -555,6 +555,7 @@
argv[0] = ID2SYM(me->def->original_id);
MEMCPY(argv+1, cfp->sp - num, VALUE, num);
cfp->sp += - num - 1;

  •            th->passed_block = blockptr;
              val = rb_funcall2(recv, rb_intern("method_missing"), 
    

num+1, argv);
break;
}


Bug #5731: enum_for を使うと method_missing にブロックが渡されない

Author: Masahiro T.
Status: Open
Priority: Normal
Assignee:
Category:
Target version:
ruby -v: -

次のスクリプトを 1.9.3 で実行すると enum_for の方は block が nil になります。
1.8.7 では両方とも block が渡されました。


class A
def method_missing(name, *args, &block)
p block
end
end

a = A.new
a.hoge{|l| p l} # ブロックが渡される
a.enum_for(:hoge).each{|l| p l} # ブロックが渡されない

% /usr/local/ruby187/bin/ruby -v a.rb
ruby 1.8.7 (2011-06-30 patchlevel 352) [i686-linux]
#Proc:[email protected]:9
#Proc:[email protected]:10

% ruby -v a.rb
ruby 1.9.3p0 (2011-10-30) [i686-linux]
#Proc:[email protected]:9
nil

使い方が間違ってるだけだったらすいません