From: "ktsj (Kazuki Tsujimoto)" Date: 2013-06-17T22:25:02+09:00 Subject: [ruby-dev:47448] [Backport 200 - Backport #8531] ifuncに渡したブロックが共有される Issue #8531 has been updated by ktsj (Kazuki Tsujimoto). > なかださん 追加していただいた TestSymbol#test_block_persist_between_calls ですが、 skipを外すとテストに失敗します。 テスト側のバグのように思うのですが、確認していただけないでしょうか。 ---------------------------------------- Backport #8531: ifuncに渡したブロックが共有される https://bugs.ruby-lang.org/issues/8531#change-40003 Author: ktsj (Kazuki Tsujimoto) Status: Assigned Priority: Normal Assignee: nagachika (Tomoyuki Chikanaga) Category: Target version: =begin ifunc(rb_iterateでbl_procとして渡したもの)をブロック付きで呼び出すと、 渡したブロックがifuncのフレーム内に保存されるようになっていますが(r29335)、 2072 vm_yield_with_cfunc(rb_thread_t *th, const rb_block_t *block, .... 2107 if (blockargptr) { 2108 VM_CF_LEP(cfp)[0] = VM_ENVVAL_BLOCK_PTR(blockargptr); 2109 } これによりメソッドに渡したブロックが次回のメソッド呼び出し時にもそのままフレームに残り続けて 共有されてしまうことがあります。#8341でMethod#to_procの件が報告されていますが、 Symbol#to_procなどでも同様です。 c = Class.new do def foo if block_given? yield else puts "No block given." end end end o = c.new f = :foo.to_proc f.(o) { puts "Block given." } # => Block given. f.(o) # => Block given. 特に明文化されていないですが、ifuncには渡されたブロックを参照するのにLEPが利用できず(PASS_PASSED_BLOCKなどが使えない)、 引数として渡されるblockargを使わなければならないという制約があるものと思っています。 (正確に言えば利用できないわけではなくて、RubyレベルでいうProc内でのblock_given?などと同等の動きになる) 現在フレームにブロックを保存するようしているのはこの制約を回避してifuncからrb_method_callなどを期待通りに呼び出すためですが、 ブロックを共有してしまうという弊害がある以上ブロックは引数で渡すという形に修正するのが妥当ではないでしょうか。 この考え方で作ったSymbol#to_procの修正パッチを添付します。(Method#to_procについては#8341に添付しています) なお、今の公開APIには任意のProcオブジェクトをpassed_blockとしてメソッドを呼び出すための関数がなさそうなので 利便性のために追加したりしていますが、この辺りは議論が必要そうな気がします。 =end -- http://bugs.ruby-lang.org/