Upload
jacqueline-chapman
View
54
Download
3
Embed Size (px)
DESCRIPTION
第 12 回 RHG の逆襲. 2009/3/14 澤田 淳二. 今日のテーマ. ブロック周りの実装 メソッドへのブロックの渡し方 yield 時の処理 変数アクセス next, redo, break. サンプルソース. (1) C でのブロック呼び出し. (2) Ruby でのブロック呼び出し. def foo yield(1) yield(2) yield(3) yield(4) yield(5) end x = 1 foo {|i| next if i == 2 break if i == 3 - PowerPoint PPT Presentation
Citation preview
第 12 回 RHG の逆襲
2009/3/14
澤田 淳二
今日のテーマ ブロック周りの実装
メソッドへのブロックの渡し方 yield 時の処理 変数アクセス next, redo, break
サンプルソース
def foo yield(1) yield(2) yield(3) yield(4) yield(5)end
x = 1foo {|i| next if i == 2 break if i == 3 redo if i == 6 p [x, i]}
x = 1(1..5).each do |i| (1..5).each do |j| next if i == 2 break if i == 3 redo if i == 6 p [x, i, j] endend
(1) C でのブロック呼び出し (2) Ruby でのブロック呼び出し
ローカル変数
外のブロック変数
自分のブロック変数
YARV コードへの変換結果
で YARV コードが出力されますruby --dump=insn スクリプト
ruby のトレース実行 Emacs の場合
make するとき最適化フラグは切っておくとよい M-x gdb で gdb 起動
• set args でコマンドライン• break でブレークポイント(ファイル:行数 or 関
数)• run で実行開始• step で 1 行実行(関数に入る)• next でも 1 行実行(関数には入らない)• finish で関数抜けだし• bt でバックトレース
メソッドへのブロックの渡し方( 1 ) コンパイル時
iseq_compile_each() の NODE_ITER および NODE_CALL 参照
NODE_ITER• ブロックの NODE ツリーをコンパイルし compil
e_data->current_block に設定 NODE_CALL
• send 命令の引数として current_block を設定
メソッドへのブロックの渡し方( 2 ) 実行時
ブロックの準備: caller_setup_args()• ブロックの rb_iseq_t をセットした rb_block_t を
準備• cfp の一部を rb_block_t 用の領域として使用
メソッドの呼び出し: vm_push_frame()• rb_block_t を specval に設定
スタックは の向きに伸びる
図解
rb_control_frame_t
rb_block_t
iseqブロックのrb_iseq_t
実行中の cfp1 つ前の cf
p
lfpspecval
VALUE スタック
cfp スタック
yield 時の処理 specval から rb_block_t 取り出し cfp を push
1 つ前の cfp の lfp(rb_block_t.lfp) を lfp に設定
1 つ前の cfp の dfp を specval に設定
図解
外の each の cfp
<main> の cfp
外のブロックの cfp
中の each の cfp
中のブロックの cfp
j
specval(1 つ前の dfp)dfp
specval(rv_block_t)
idfp
specval(rb_block_t)
specval(1 つ前の dfp)
lfp, dfp
specval
x
サンプルソース (1) で中のブロック実行時のスタック状態
lfp
lfp
スタックは の向きに伸びる
変数アクセス dfp を利用して変数の位置を特定 自ブロックの外にある変数も dfp をたど
ることでアクセス可能(たどるレベルはコンパイル時にわかる)
next, redo next
処理:次のループに進む コンパイル結果: leave
redo 処理:ブロック実行をやり直す コンパイル結果: jump 0
ちなみに、 retry は使えなくなりました
break
処理:ループ( yield したメソッド)を終了する
コンパイル結果: throw 2 break 時の 1 つ前の dfp とマッチする cfp
(メソッドを呼び出した cfp )を検索 マッチした cfp の pc から実行を再開 ブロック呼び出しが C 関数の場合、 JUMP_T
AG を利用して C のコールスタックを巻き戻し