Procとセーフレベル
Rubyリファレンスマニュアル セキュリティモデルより。「Procはその時点でのセーフレベルを記憶する。そのProcオブジェクトがcallされると、 記憶していたセーフレベルで実行される。」とのこと。
# セーフレベル0で作ったproc p0 = proc { $SAFE } # セーフレベル4で作ったproc p4 = Thread.fork { $SAFE = 4 proc { $SAFE } }.value # セーフレベル0で実行 puts "-- safelevel 0" puts p0.call # => 0 puts p4.call # => 4 # セーフレベル4で実行 puts "-- safelevel 4" puts Thread.fork { $SAFE = 4 "#{p0.call}\n#{p4.call}" # => 0, 4 }.value
実行結果です。
-- safelevel 0 0 4 -- safelevel 4 0 4
ブロックはどうなるの?
ここでちょっとびくってなったのは、関数に渡されたブロックの扱いについて。ブロックとprocって同じような扱いになっているイメージがあったので、↓みたいな関数を作って使っていたのだけれど、これって期待通りの動作になってないんじゃね?と思った次第。
# ブロックの処理を指定のセーフレベルで実行する def safe(level=4) Thread.fork { $SAFE = level yield if block_given? }.value end
ということで確認。結論としては、関数に渡されたブロックは
- yieldで実行すれば、カレントのセーフレベル
- callで実行すれば、記録されたセーフレベル
で実行されます。なので上の関数は期待通り動作する。
# yieldでブロックを実行するsafe関数 def safe_yield Thread.fork { $SAFE = 4 yield }.value end # callでブロックを実行するsafe関数 def safe_call(&block) Thread.fork { $SAFE = 4 block.call }.value end # ブロック + yield puts safe_yield { $SAFE } #=> 4 # ブロック + call puts safe_call { $SAFE } #=> 0 # procをブロックとして渡す + yield puts safe_yield( &proc { $SAFE } ) #=> 4 # procをブロックとして渡す + call puts safe_call( &proc { $SAFE } ) #=> 0
実行結果です。
4 0 4 0
これ、いつかはまりそうだなー。