Proc.new
メソッドのブロックがアタッチされているメソッド内でメソッドのブロックなしで呼び出された場合、メソッドのブロックを使用します。これは文書化された動作です。
YARV がどのようにそれを行うかを知るために、ソース コードを読んでみましょう。具体的には、proc_new
関数:
block_pointer = rb_vm_control_frame_block_ptr(control_frame_pointer);
この行は、現在のコントロール フレームに関連付けられているブロックへのポインターを取得します。
これらの制御フレームは Ruby のスタックを実装していると思います。現在、Proc.new
コントロール フレーム内にいるため、メソッドに指定されたブロックへのポインターを取得します。
if (block_pointer != NULL) {
/* block found */
} else {
/* block not found... */
}
ポインターが でない場合はNULL
、Proc.new
明示的にブロックが渡されました。しかし、ポインターが NULL
の場合はどうなるでしょうか。
/* block not found... */
control_frame_pointer = RUBY_VM_PREVIOUS_CONTROL_FRAME(control_frame_pointer);
block_pointer = rb_vm_control_frame_block_ptr(control_frame_pointer);
スタックを上に移動し、そのブロックを取得しようとします。つまり、呼び出し元のコントロール フレームに移動し、そのブロックを取得しようとします。
if (block_pointer != NULL) {
if (is_lambda) {
rb_warn("tried to create Proc object without a block");
}
} else {
rb_raise(rb_eArgError, "tried to create Proc object without a block");
}
そうでない場合はNULL
、ほとんど成功しています。まだ の場合NULL
は を作成できないため、 を発生Proc
させますArgumentError
。
アルゴリズムは次のようになります。
Proc.new
ブロックが与えられた
かどうかを確認する
- だったら使おう
- そうでない場合は、発信者がブロックされたかどうかを確認します
- だったら使おう
- そうでない場合は、エラーを発生させます
読みやすくするためにソース コードを変更しました。オリジナルについては、リンクされた GitHub のソース ファイルにアクセスしてください。