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