From: SASADA Koichi <ko1@...> Date: 2013-02-09T13:12:59+09:00 Subject: [ruby-dev:46961] Re: [ruby-trunk - Bug #7774][Assigned] IFUNC上のbinding呼び出しでSEGV (2013/02/09 12:12), Kazuki Tsujimoto wrote: > たびたびすみません。この修正ですが、下記の変更は意図されたものでしょうか。 > > * r39067より前では、+メソッド/injectメソッドで例外が起きた場合は両方ともメソッド起動時のコンテキスト(例ではmain)のbindingが返ってきていた。 > * r39067以降は、injectメソッドで例外が起きた場合の動きが変更されレシーバのbindingが返ってくるようになった。(+メソッドは変更なし) > > 個人的にはこれまでの挙動のほうが便利でした。 確かに.挙動は変わらないと思っていたんだけど,binding の位置はずれます な.どうしたものですかねぇ. 正直,どの binding を返すか,ってきちんとしたコンセンサスが取れてない と思うのですよ.たまたま,C で実装されたものだった場合には,その上のフ レームの binding が帰ってきている感じです. 例えば,Ruby で + を実装した XYZZY というクラスが例外を吐く場合,やは り呼び出し側のフレームではなく,XYZZY#+ のフレームの Binding オブジェク トが返ります. def m1(arg) arg + nil rescue end def m2(arg) arg.inject(:+) rescue end class XYZZY def +(o) raise end end # TracePoint tp = TracePoint.new(:raise) do |tp| $b = tp.binding end tp.enable do m1(0) p A: eval('self', $b) p B: eval('arg rescue nil', $b) m2([0, nil]) p C: eval('self', $b) p D: eval('arg rescue nil', $b) m1(XYZZY.new) p X: eval('self', $b) p Y: eval('arg rescue nil', $b) end # set_trace_func set_trace_func ->(event, file, line, id, binding, klass) do if event == 'raise' $b = binding end end m1(0) p E: eval('self', $b) p F: eval('arg rescue nil', $b) m2([0, nil]) p G: eval('self', $b) p H: eval('arg rescue nil', $b) m1(XYZZY.new) p XX: eval('self', $b) p YY: eval('arg rescue nil', $b) #=> ruby 2.0.0dev (2012-12-21 trunk 38515) [i386-mswin32_100] t.rb:19: warning: shadowing outer local variable - tp {:A=>main} {:B=>0} {:C=>main} {:D=>[0, nil]} {:X=>#<XYZZY:0xb2a0d4>} {:Y=>nil} {:E=>main} {:F=>0} {:G=>main} {:H=>[0, nil]} {:XX=>#<XYZZY:0xb12a60>} {:YY=>nil} とまぁ,こんな感じで,「呼び出し側のフレームを返す」という仕様ではな かったわけです. もし,以前の挙動に戻すなら,ifuc の binding を作りつつ,ruby-level フ レームまで遡って,その ruby-level フレームの binding を返す,というのが 思いつきました.これで解決しそうではあります. どうしたもんでしょうか? -- // SASADA Koichi at atdot dot net