Project

General

Profile

Actions

Backport #8531

closed

ifuncに渡したブロックが共有される

Added by ktsj (Kazuki Tsujimoto) over 11 years ago. Updated over 11 years ago.

Status:
Closed
[ruby-dev:47438]

Description

=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


Files

fix-ifunc-block.patch (2.8 KB) fix-ifunc-block.patch ktsj (Kazuki Tsujimoto), 06/16/2013 03:33 AM

Related issues 1 (0 open1 closed)

Related to Backport193 - Backport #8341: block_given? (and the actual block) persist between calls to a proc created from a method (using method().to_proc()).Closedusa (Usaku NAKAMURA)04/28/2013Actions
Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0