Ruby1.9/YARVコードの実行を読む
をテンプレートにして作成
[
トップ
] [
新規
|
一覧
|
単語検索
|
最終更新
|
ヘルプ
]
開始行:
#contents
*はじめに [#gbcad793]
ここでは[[YARVコードへのコンパイル>Ruby1.9/YARVコードへの...
*rb_iseq_eval(vm.c) [#x2f9fc4e]
YARVコード実行のエントリーポイントとなるのはrb_iseq_eval...
*vm_set_stack_top(vm.c) [#b68eb7c8]
vm_set_stack_top関数ではまずrb_vm_set_finish_env関数を呼...
次にvm_push_frame関数を呼んで実行するYARVコードの情報をフ...
vm_push_frame(th, iseq, FRAME_MAGIC_TOP,
th->top_self, 0, iseq->iseq_encoded,
th->cfp->sp, 0, iseq->local_size);
vm_push_frame(rb_thread_t *th, rb_iseq_t *iseq, VALUE ty...
VALUE self, VALUE specval, VALUE *pc,
VALUE *sp, VALUE *lfp, int local_size)
{
...
/* nil initialize */
for (i=0; i < local_size; i++) {
*sp = Qnil;
sp++;
}
/* set special val */
*sp = GC_GUARDED_PTR(specval);
dfp = sp;
if (lfp == 0) {
lfp = sp;
}
cfp = th->cfp = th->cfp - 1;
cfp->pc = pc;
cfp->sp = sp + 1;
cfp->bp = sp + 1;
cfp->iseq = iseq;
cfp->flag = type;
cfp->self = self;
cfp->lfp = lfp;
cfp->dfp = dfp;
cfp->proc = 0;
例のmontecarlo.rbのiseqをフレームを積んだ後のスタックは以...
|Qnil
|GC_GUARDED_PTR(0)
|Qnil
|GC_GUARDED_PTR(GC_GUARDED_PTR(0))
|Qnil # for n
|Qnil # for pi
|Qnil # for svar
dfp,lfp→|GC_GUARDED_PTR(0)
sp,bp→|
|
...
|
cfp→|今積んだフレーム情報
|rb_vm_set_finish_env関数で積んだフレーム情報
|th_init2関数で積んだフレーム情報
*vm_eval_body(vm.c) [#l91ab8a7]
この関数はvm_eval関数を実行します。例外やbreakなどが起こ...
*vm_eval(vm_evalbody.c) [#cbe617a8]
この関数がYARV命令実行の肝です。いい感じに難解なコードに...
:OPT_CALL_THREADED_CODE|関数呼び出し
:OPT_DIRECT_THREADED_CODE|goto
:指定なし|switch文
詳しいからくりはvm.hを眺めてください。
**vm.inc [#f398e653]
各命令の処理ルーチンはどこに書かれているかですがvm.incに...
**insns.def [#o2bcabcc]
各命令の動きが知りたい場合はinsns.defを見ると書いてありま...
DEFINE_INSN
命令名
(引数...)
(スタックから拾う値)
(スタックに積む値)
{
Cのソース
}
引数の取得やスタックからのポップ、スタックへのプッシュは...
**命令が実行される環境 [#t37ce0b5]
***YARVで使われるレジスタ [#l05d1d5f]
YARVでは以下の6つのレジスタが使われています。
:cfp|現在の実行環境を表すrb_control_frame_t構造体(vm_cor...
:dfp|ローカル変数領域へのVALUEポインタです
:lfp|ローカル変数領域へのVALUEポインタです。dfpとの違いは...
:sp|現在のスタックトップを示すVALUEポインタです
:bp|現在のフレームが積まれた時のspの初期位置を示すVALUEポ...
:pc|現在実行している命令を示すVALUEポインタです
***命令サポートマクロ [#y52169d0]
命令を記述する際に利用するマクロはinsnhelper.hに書かれて...
***命令サポート関数 [#e02c5cc3]
命令を記述するする際に利用する関数はvm_insnhelper.cに書か...
*実行してみる [#d5c948ac]
それでは[[YARVコードへのコンパイル>Ruby1.9/YARVコードへの...
**defineclass [#gccfac1a]
この命令は名が示すようにクラスを定義します。まあそれ自体...
-トップレベル
class C
end
-ネストされてるけど以下みたいな場合
class Outer
class Inner
end
end
***vm_get_cbase(vm.c) [#q87da314]
ベースクラスがnilの場合、vm_get_cbase関数が呼ばれてベース...
まず、フレームをサーチ(vm_get_ruby_level_cfp関数(eval_in...
次にフレームのrb_iseq_t構造体からcref_stackという情報を取...
その後、cref_stackからベースクラスを取得しています。
というのが処理の流れなのですがcref_stackっていつ設定され...
if (type == ISEQ_TYPE_TOP) {
/* toplevel is private */
iseq->cref_stack = NEW_BLOCK(th->top_wrapper ? th->top_...
iseq->cref_stack->nd_file = 0;
iseq->cref_stack->nd_visi = NOEX_PRIVATE;
}
else if (type == ISEQ_TYPE_METHOD || type == ISEQ_TY...
iseq->cref_stack = NEW_BLOCK(0); /* place holder */
iseq->cref_stack->nd_file = 0;
}
というわけでトップレベルのベースクラスは普通Objectです(...
一方、クラス定義をコンパイルするときは0です。それじゃあ困...
***vm_cref_push(vm.c) [#o1a164e2]
クラスを定義するとクラスを表すVALUEが決定されます。先ほど...
**setinlinecache, getinlinecache [#u225d172]
この命令の組はVALUEをキャッシュに設定、キャッシュから取得...
:label(5)
getinlinecache(0, label(6))
getconstant(:MonteCarlo)
setinlinecache(label(5))
:label(6)
上の疑似コードはNODEがどういうYARV命令に変換されるかを示...
compile.c
VALUE v = (VALUE)NEW_INLINE_CACHE_ENTRY();
generated_iseq[pos + 1 + j] = v;
vm_core.h
#define NEW_INLINE_CACHE_ENTRY() NEW_WHILE(Qundef, 0, 0)
#define ic_class u1.value
#define ic_method u2.node
#define ic_value u2.value
#define ic_vmstat u3.cnt
typedef NODE *IC;
getinlinecacheの処理ルーチンは以下のようになっています。
DEFINE_INSN
getinlinecache
(IC ic, OFFSET dst)
()
(VALUE val)
{
if (ic->ic_vmstat == GET_VM_STATE_VERSION()) {
val = ic->ic_value;
JUMP(dst);
}
else {
/* none */
val = Qnil;
}
}
次にsetinlinecacheの処理ルーチンです。
DEFINE_INSN
setinlinecache
(OFFSET dst)
(VALUE val)
(VALUE val)
{
IC ic = GET_CONST_INLINE_CACHE(dst);
ic->ic_value = val;
ic->ic_vmstat = GET_VM_STATE_VERSION();
}
insnhelper.h
#define GET_CONST_INLINE_CACHE(dst) ((IC) * (GET_PC() + ...
若干わかりにくいので解説しておくと、getinlinecacheの第...
定数取得の場合、setinlinecacheの前にgetconstantがあるので...
後はGET_VM_STATE_VERSIONマクロが何者かわかれば理解できそ...
vm.h
#define GET_VM_STATE_VERSION() (ruby_vm_global_state_ver...
#define INC_VM_STATE_VERSION() \
(ruby_vm_global_state_version = (ruby_vm_global_state_...
vm.c
rb_vm_change_state(void)
{
INC_VM_STATE_VERSION();
}
variable.c
mod_av_set(VALUE klass, ID id, VALUE val, int isconst)
{
...
if(isconst){
rb_vm_change_state();
}
rb_const_set(VALUE klass, ID id, VALUE val)
{
...
mod_av_set(klass, id, val, Qtrue);
}
というわけで、定数が設定されるとバージョンが上がっていま...
**send [#k225be50]
この命令はメソッドを呼び出します。おそらくもっともよく実...
レシーバはFCALL_BITが設定されているとself、それ以外はTOPN...
TOPN(2)→|レシーバ
TOPN(1)→|引数1
TOPN(0)→|引数2
sp→|
その後、CALL_METHODマクロ経由でvm_call_method関数が呼び出...
***vm_setup_method(vm_insnhelper.c) [#t39c9325]
vm_setup_method関数ではまずvm_callee_setup_arg関数を呼び...
その後、leaveの前のsendかによって分岐してますが、vm_push_...
+vm_push_frame関数を呼ぶことでth->cfpは新しいフレームを指...
+vm_call_methodはQundefを返す
+CALL_METHODマクロを見るとQundefが返ってくるとRESTORE_REG...
+RESTORE_REGSマクロによりvm_eval関数のreg_cfpがth->cfpに...
+NEXT_INSNマクロによりreg_cfp->pcが指す命令が実行される
sendを呼び出す前はこうです。
reg_cfp->pc→send(:pi, 1, 0, 0, 0)
setlocal(1) # pi
sendを呼び出した後はこうです。
reg_cfp->pc→putobject(0)
呼び出したとは書きましたが返ってきたとは書いてません:-)
**send(ブロックを渡す場合) [#bcea2351]
メソッドにブロックを渡す場合を考えましょう。まずcaller_se...
vm_insnhelper.c
caller_setup_args(rb_thread_t *th, rb_control_frame_t *c...
int argc, rb_iseq_t *blockiseq, rb_block_t **block)
{
rb_block_t *blockptr = 0;
if (block) {
if (flag & VM_CALL_ARGS_BLOCKARG_BIT) {
...
else if (blockiseq) {
blockptr = RUBY_VM_GET_BLOCK_PTR_IN_CFP(cfp);
blockptr->iseq = blockiseq;
blockptr->proc = 0;
*block = blockptr;
}
RUBY_VM_GET_BLOCK_PTR_IN_CFPマクロはvm_core.hに書かれてい...
#define RUBY_VM_GET_BLOCK_PTR_IN_CFP(cfp) ((rb_block_t *...
何故これでrb_control_frame_tからrb_block_tが取れるかとい...
||rb_control_t|rb_block_t|
|VALUE*|pc||
|VALUE*|sp||
|VALUE*|bp||
|rb_iseq_t*|iseq||
|VALUE|flag||
|VALUE|self|self|
|VALUE*|lfp|lfp|
|VALUE*|dfp|dfp|
|rb_iseq_t*|block_iseq|iseq|
|VALUE|proc|proc|
|ID|method_id||
|VALUE|method_class||
|VALUE|prof_time_self||
|VALUE|prof_time_chld||
つまり、selfから5個の型が同じなのでキャストによりrb_block...
***vm_yield(vm.c) [#fb64dcba]
次にブロックが呼び出される部分を見てみましょう。Cでメソッ...
vm_yield関数ではlfpが指すアドレスからrb_block_t構造体を取...
vm_setup_method(rb_thread_t *th, rb_control_frame_t *cfp,
int argc, rb_block_t *blockptr, VALUE flag,
VALUE iseqval, VALUE recv, VALUE klass)
{
...
vm_push_frame(th, iseq,
FRAME_MAGIC_METHOD, recv, (VALUE) blockptr,
iseq->iseq_encoded + opt_pc, sp, 0, 0);
となっています。その後、invoke_block関数にてフレームを積...
vm_push_frame(th, iseq, type,
self, GC_GUARDED_PTR(block->dfp),
iseq->iseq_encoded + opt_pc, cfp->sp + arg_size,...
iseq->local_size - arg_size);
スタックがどうなるか見てみましょう。サンプルは例によってm...
|Qnil # for n
|Qnil # for count
|Qnil # for svar
lfp→|GC_GUARDED_PTR(0)
|Qnil # for x
|Qnil # for y
dfp→|GC_GUARDED_PTR(block->dfp)
sp,bp→|
今まで見てきたのと異なり、lfpとdfpが同じではありません。
**opt_plus [#ge836ab7]
コンパイル時に最適化オプションが設定されていると、send(+)...
insns.def
DEFINE_INSN
opt_plus
()
(VALUE recv, VALUE obj)
(VALUE val)
{
if (FIXNUM_2_P(recv, obj) &&
BASIC_OP_UNREDEFINED_P(BOP_PLUS)) {
/* fixnum + fixnum */
long a, b, c;
a = FIX2LONG(recv);
b = FIX2LONG(obj);
c = a + b;
if (FIXABLE(c)) {
val = LONG2FIX(c);
}
else {
val = rb_big_plus(rb_int2big(a), rb_int2big(b));
}
}
else if (HEAP_CLASS_OF(recv) == rb_cFloat &&
HEAP_CLASS_OF(obj) == rb_cFloat &&
BASIC_OP_UNREDEFINED_P(BOP_PLUS)) {
val = DOUBLE2NUM(RFLOAT_VALUE(recv) + RFLOAT_VALUE(obj));
}
else if (HEAP_CLASS_OF(recv) == rb_cString &&
HEAP_CLASS_OF(obj) == rb_cString &&
BASIC_OP_UNREDEFINED_P(BOP_PLUS)) {
val = rb_str_plus(recv, obj);
}
else if (HEAP_CLASS_OF(recv) == rb_cArray &&
BASIC_OP_UNREDEFINED_P(BOP_PLUS)) {
val = rb_ary_plus(recv, obj);
}
else {
PUSH(recv);
PUSH(obj);
CALL_SIMPLE_METHOD(1, idPLUS, recv);
}
}
何をしているかというと、
+特定のクラスのオブジェクトで
++が再定義されていなかったら
+直接計算もしくはメソッドの実装関数を直接呼び出すことでフ...
ということをしています。BASIC_OP_UNREDEFINED_Pマクロは以...
insnhelper.h
#define BASIC_OP_UNREDEFINED_P(op) ((ruby_vm_redefined_f...
というわけで再定義チェックもloadとandとtest(i386以外でど...
***ruby_vm_redefined_flag(vm.c) [#a46ed653]
次にruby_vm_redefined_flagを変更している周りを眺めてみま...
Init_VM(void)
{
...
vm_init_redefined_flag();
}
vm_init_redefined_flag(void)
{
ID mid;
VALUE bop;
vm_opt_method_table = st_init_numtable();
#define OP(mid_, bop_) (mid = id##mid_, bop = BOP_##bop_)
#define C(k) add_opt_method(rb_c##k, mid, bop)
OP(PLUS, PLUS), (C(Fixnum), C(Float), C(String), C(A...
展開するとこんな感じになります。
mid = idPLUS, bop = BOP_PLUS,
(add_opt_method(rb_cFixnum, mid, bop),
add_opt_method(rb_cFloat, mid, bop),
add_opt_method(rb_cString, mid, bop),
add_opt_method(rb_cArray, mid, bop));
で、add_opt_method関数。
add_opt_method(VALUE klass, ID mid, VALUE bop)
{
NODE *node;
if (st_lookup(RCLASS_M_TBL(klass), mid, (void *)&nod...
nd_type(node->nd_body->nd_body) == NODE_CFUNC) {
st_insert(vm_opt_method_table, (st_data_t)node, (st_dat...
}
}
というわけでメソッド実装関数のNODEとBOPが関連づけられてい...
次にメソッド定義時の動作です。Rubyで再定義したとしてvm_de...
vm_insnhelper.c
vm_define_method(rb_thread_t *th, VALUE obj,
ID id, rb_iseq_t *miseq, rb_num_t is_singleton, NOD...
{
...
rb_add_method(klass, id, newbody, noex);
eval_method.c
rb_add_method(VALUE klass, ID mid, NODE * node, int noex)
{
...
/* check re-definition */
st_data_t data;
NODE *old_node;
if (st_lookup(RCLASS_M_TBL(klass), mid, &data)) {
old_node = (NODE *)data;
if (old_node) {
if (nd_type(old_node->nd_body->nd_body) == NODE_CFUNC) {
rb_vm_check_redefinition_opt_method(old_node);
}
vm.c
rb_vm_check_redefinition_opt_method(NODE *node)
{
VALUE bop;
if (st_lookup(vm_opt_method_table, (st_data_t)node, ...
ruby_vm_redefined_flag |= bop;
}
}
といった感じにruby_vm_redefined_flagが変更されています。...
*おわりに [#jc19fb11]
今回はYARVコードへの実行を見てきました。YARVコードまで落...
一応ここまででrubyコマンドを打ってからスクリプトが実行さ...
終了行:
#contents
*はじめに [#gbcad793]
ここでは[[YARVコードへのコンパイル>Ruby1.9/YARVコードへの...
*rb_iseq_eval(vm.c) [#x2f9fc4e]
YARVコード実行のエントリーポイントとなるのはrb_iseq_eval...
*vm_set_stack_top(vm.c) [#b68eb7c8]
vm_set_stack_top関数ではまずrb_vm_set_finish_env関数を呼...
次にvm_push_frame関数を呼んで実行するYARVコードの情報をフ...
vm_push_frame(th, iseq, FRAME_MAGIC_TOP,
th->top_self, 0, iseq->iseq_encoded,
th->cfp->sp, 0, iseq->local_size);
vm_push_frame(rb_thread_t *th, rb_iseq_t *iseq, VALUE ty...
VALUE self, VALUE specval, VALUE *pc,
VALUE *sp, VALUE *lfp, int local_size)
{
...
/* nil initialize */
for (i=0; i < local_size; i++) {
*sp = Qnil;
sp++;
}
/* set special val */
*sp = GC_GUARDED_PTR(specval);
dfp = sp;
if (lfp == 0) {
lfp = sp;
}
cfp = th->cfp = th->cfp - 1;
cfp->pc = pc;
cfp->sp = sp + 1;
cfp->bp = sp + 1;
cfp->iseq = iseq;
cfp->flag = type;
cfp->self = self;
cfp->lfp = lfp;
cfp->dfp = dfp;
cfp->proc = 0;
例のmontecarlo.rbのiseqをフレームを積んだ後のスタックは以...
|Qnil
|GC_GUARDED_PTR(0)
|Qnil
|GC_GUARDED_PTR(GC_GUARDED_PTR(0))
|Qnil # for n
|Qnil # for pi
|Qnil # for svar
dfp,lfp→|GC_GUARDED_PTR(0)
sp,bp→|
|
...
|
cfp→|今積んだフレーム情報
|rb_vm_set_finish_env関数で積んだフレーム情報
|th_init2関数で積んだフレーム情報
*vm_eval_body(vm.c) [#l91ab8a7]
この関数はvm_eval関数を実行します。例外やbreakなどが起こ...
*vm_eval(vm_evalbody.c) [#cbe617a8]
この関数がYARV命令実行の肝です。いい感じに難解なコードに...
:OPT_CALL_THREADED_CODE|関数呼び出し
:OPT_DIRECT_THREADED_CODE|goto
:指定なし|switch文
詳しいからくりはvm.hを眺めてください。
**vm.inc [#f398e653]
各命令の処理ルーチンはどこに書かれているかですがvm.incに...
**insns.def [#o2bcabcc]
各命令の動きが知りたい場合はinsns.defを見ると書いてありま...
DEFINE_INSN
命令名
(引数...)
(スタックから拾う値)
(スタックに積む値)
{
Cのソース
}
引数の取得やスタックからのポップ、スタックへのプッシュは...
**命令が実行される環境 [#t37ce0b5]
***YARVで使われるレジスタ [#l05d1d5f]
YARVでは以下の6つのレジスタが使われています。
:cfp|現在の実行環境を表すrb_control_frame_t構造体(vm_cor...
:dfp|ローカル変数領域へのVALUEポインタです
:lfp|ローカル変数領域へのVALUEポインタです。dfpとの違いは...
:sp|現在のスタックトップを示すVALUEポインタです
:bp|現在のフレームが積まれた時のspの初期位置を示すVALUEポ...
:pc|現在実行している命令を示すVALUEポインタです
***命令サポートマクロ [#y52169d0]
命令を記述する際に利用するマクロはinsnhelper.hに書かれて...
***命令サポート関数 [#e02c5cc3]
命令を記述するする際に利用する関数はvm_insnhelper.cに書か...
*実行してみる [#d5c948ac]
それでは[[YARVコードへのコンパイル>Ruby1.9/YARVコードへの...
**defineclass [#gccfac1a]
この命令は名が示すようにクラスを定義します。まあそれ自体...
-トップレベル
class C
end
-ネストされてるけど以下みたいな場合
class Outer
class Inner
end
end
***vm_get_cbase(vm.c) [#q87da314]
ベースクラスがnilの場合、vm_get_cbase関数が呼ばれてベース...
まず、フレームをサーチ(vm_get_ruby_level_cfp関数(eval_in...
次にフレームのrb_iseq_t構造体からcref_stackという情報を取...
その後、cref_stackからベースクラスを取得しています。
というのが処理の流れなのですがcref_stackっていつ設定され...
if (type == ISEQ_TYPE_TOP) {
/* toplevel is private */
iseq->cref_stack = NEW_BLOCK(th->top_wrapper ? th->top_...
iseq->cref_stack->nd_file = 0;
iseq->cref_stack->nd_visi = NOEX_PRIVATE;
}
else if (type == ISEQ_TYPE_METHOD || type == ISEQ_TY...
iseq->cref_stack = NEW_BLOCK(0); /* place holder */
iseq->cref_stack->nd_file = 0;
}
というわけでトップレベルのベースクラスは普通Objectです(...
一方、クラス定義をコンパイルするときは0です。それじゃあ困...
***vm_cref_push(vm.c) [#o1a164e2]
クラスを定義するとクラスを表すVALUEが決定されます。先ほど...
**setinlinecache, getinlinecache [#u225d172]
この命令の組はVALUEをキャッシュに設定、キャッシュから取得...
:label(5)
getinlinecache(0, label(6))
getconstant(:MonteCarlo)
setinlinecache(label(5))
:label(6)
上の疑似コードはNODEがどういうYARV命令に変換されるかを示...
compile.c
VALUE v = (VALUE)NEW_INLINE_CACHE_ENTRY();
generated_iseq[pos + 1 + j] = v;
vm_core.h
#define NEW_INLINE_CACHE_ENTRY() NEW_WHILE(Qundef, 0, 0)
#define ic_class u1.value
#define ic_method u2.node
#define ic_value u2.value
#define ic_vmstat u3.cnt
typedef NODE *IC;
getinlinecacheの処理ルーチンは以下のようになっています。
DEFINE_INSN
getinlinecache
(IC ic, OFFSET dst)
()
(VALUE val)
{
if (ic->ic_vmstat == GET_VM_STATE_VERSION()) {
val = ic->ic_value;
JUMP(dst);
}
else {
/* none */
val = Qnil;
}
}
次にsetinlinecacheの処理ルーチンです。
DEFINE_INSN
setinlinecache
(OFFSET dst)
(VALUE val)
(VALUE val)
{
IC ic = GET_CONST_INLINE_CACHE(dst);
ic->ic_value = val;
ic->ic_vmstat = GET_VM_STATE_VERSION();
}
insnhelper.h
#define GET_CONST_INLINE_CACHE(dst) ((IC) * (GET_PC() + ...
若干わかりにくいので解説しておくと、getinlinecacheの第...
定数取得の場合、setinlinecacheの前にgetconstantがあるので...
後はGET_VM_STATE_VERSIONマクロが何者かわかれば理解できそ...
vm.h
#define GET_VM_STATE_VERSION() (ruby_vm_global_state_ver...
#define INC_VM_STATE_VERSION() \
(ruby_vm_global_state_version = (ruby_vm_global_state_...
vm.c
rb_vm_change_state(void)
{
INC_VM_STATE_VERSION();
}
variable.c
mod_av_set(VALUE klass, ID id, VALUE val, int isconst)
{
...
if(isconst){
rb_vm_change_state();
}
rb_const_set(VALUE klass, ID id, VALUE val)
{
...
mod_av_set(klass, id, val, Qtrue);
}
というわけで、定数が設定されるとバージョンが上がっていま...
**send [#k225be50]
この命令はメソッドを呼び出します。おそらくもっともよく実...
レシーバはFCALL_BITが設定されているとself、それ以外はTOPN...
TOPN(2)→|レシーバ
TOPN(1)→|引数1
TOPN(0)→|引数2
sp→|
その後、CALL_METHODマクロ経由でvm_call_method関数が呼び出...
***vm_setup_method(vm_insnhelper.c) [#t39c9325]
vm_setup_method関数ではまずvm_callee_setup_arg関数を呼び...
その後、leaveの前のsendかによって分岐してますが、vm_push_...
+vm_push_frame関数を呼ぶことでth->cfpは新しいフレームを指...
+vm_call_methodはQundefを返す
+CALL_METHODマクロを見るとQundefが返ってくるとRESTORE_REG...
+RESTORE_REGSマクロによりvm_eval関数のreg_cfpがth->cfpに...
+NEXT_INSNマクロによりreg_cfp->pcが指す命令が実行される
sendを呼び出す前はこうです。
reg_cfp->pc→send(:pi, 1, 0, 0, 0)
setlocal(1) # pi
sendを呼び出した後はこうです。
reg_cfp->pc→putobject(0)
呼び出したとは書きましたが返ってきたとは書いてません:-)
**send(ブロックを渡す場合) [#bcea2351]
メソッドにブロックを渡す場合を考えましょう。まずcaller_se...
vm_insnhelper.c
caller_setup_args(rb_thread_t *th, rb_control_frame_t *c...
int argc, rb_iseq_t *blockiseq, rb_block_t **block)
{
rb_block_t *blockptr = 0;
if (block) {
if (flag & VM_CALL_ARGS_BLOCKARG_BIT) {
...
else if (blockiseq) {
blockptr = RUBY_VM_GET_BLOCK_PTR_IN_CFP(cfp);
blockptr->iseq = blockiseq;
blockptr->proc = 0;
*block = blockptr;
}
RUBY_VM_GET_BLOCK_PTR_IN_CFPマクロはvm_core.hに書かれてい...
#define RUBY_VM_GET_BLOCK_PTR_IN_CFP(cfp) ((rb_block_t *...
何故これでrb_control_frame_tからrb_block_tが取れるかとい...
||rb_control_t|rb_block_t|
|VALUE*|pc||
|VALUE*|sp||
|VALUE*|bp||
|rb_iseq_t*|iseq||
|VALUE|flag||
|VALUE|self|self|
|VALUE*|lfp|lfp|
|VALUE*|dfp|dfp|
|rb_iseq_t*|block_iseq|iseq|
|VALUE|proc|proc|
|ID|method_id||
|VALUE|method_class||
|VALUE|prof_time_self||
|VALUE|prof_time_chld||
つまり、selfから5個の型が同じなのでキャストによりrb_block...
***vm_yield(vm.c) [#fb64dcba]
次にブロックが呼び出される部分を見てみましょう。Cでメソッ...
vm_yield関数ではlfpが指すアドレスからrb_block_t構造体を取...
vm_setup_method(rb_thread_t *th, rb_control_frame_t *cfp,
int argc, rb_block_t *blockptr, VALUE flag,
VALUE iseqval, VALUE recv, VALUE klass)
{
...
vm_push_frame(th, iseq,
FRAME_MAGIC_METHOD, recv, (VALUE) blockptr,
iseq->iseq_encoded + opt_pc, sp, 0, 0);
となっています。その後、invoke_block関数にてフレームを積...
vm_push_frame(th, iseq, type,
self, GC_GUARDED_PTR(block->dfp),
iseq->iseq_encoded + opt_pc, cfp->sp + arg_size,...
iseq->local_size - arg_size);
スタックがどうなるか見てみましょう。サンプルは例によってm...
|Qnil # for n
|Qnil # for count
|Qnil # for svar
lfp→|GC_GUARDED_PTR(0)
|Qnil # for x
|Qnil # for y
dfp→|GC_GUARDED_PTR(block->dfp)
sp,bp→|
今まで見てきたのと異なり、lfpとdfpが同じではありません。
**opt_plus [#ge836ab7]
コンパイル時に最適化オプションが設定されていると、send(+)...
insns.def
DEFINE_INSN
opt_plus
()
(VALUE recv, VALUE obj)
(VALUE val)
{
if (FIXNUM_2_P(recv, obj) &&
BASIC_OP_UNREDEFINED_P(BOP_PLUS)) {
/* fixnum + fixnum */
long a, b, c;
a = FIX2LONG(recv);
b = FIX2LONG(obj);
c = a + b;
if (FIXABLE(c)) {
val = LONG2FIX(c);
}
else {
val = rb_big_plus(rb_int2big(a), rb_int2big(b));
}
}
else if (HEAP_CLASS_OF(recv) == rb_cFloat &&
HEAP_CLASS_OF(obj) == rb_cFloat &&
BASIC_OP_UNREDEFINED_P(BOP_PLUS)) {
val = DOUBLE2NUM(RFLOAT_VALUE(recv) + RFLOAT_VALUE(obj));
}
else if (HEAP_CLASS_OF(recv) == rb_cString &&
HEAP_CLASS_OF(obj) == rb_cString &&
BASIC_OP_UNREDEFINED_P(BOP_PLUS)) {
val = rb_str_plus(recv, obj);
}
else if (HEAP_CLASS_OF(recv) == rb_cArray &&
BASIC_OP_UNREDEFINED_P(BOP_PLUS)) {
val = rb_ary_plus(recv, obj);
}
else {
PUSH(recv);
PUSH(obj);
CALL_SIMPLE_METHOD(1, idPLUS, recv);
}
}
何をしているかというと、
+特定のクラスのオブジェクトで
++が再定義されていなかったら
+直接計算もしくはメソッドの実装関数を直接呼び出すことでフ...
ということをしています。BASIC_OP_UNREDEFINED_Pマクロは以...
insnhelper.h
#define BASIC_OP_UNREDEFINED_P(op) ((ruby_vm_redefined_f...
というわけで再定義チェックもloadとandとtest(i386以外でど...
***ruby_vm_redefined_flag(vm.c) [#a46ed653]
次にruby_vm_redefined_flagを変更している周りを眺めてみま...
Init_VM(void)
{
...
vm_init_redefined_flag();
}
vm_init_redefined_flag(void)
{
ID mid;
VALUE bop;
vm_opt_method_table = st_init_numtable();
#define OP(mid_, bop_) (mid = id##mid_, bop = BOP_##bop_)
#define C(k) add_opt_method(rb_c##k, mid, bop)
OP(PLUS, PLUS), (C(Fixnum), C(Float), C(String), C(A...
展開するとこんな感じになります。
mid = idPLUS, bop = BOP_PLUS,
(add_opt_method(rb_cFixnum, mid, bop),
add_opt_method(rb_cFloat, mid, bop),
add_opt_method(rb_cString, mid, bop),
add_opt_method(rb_cArray, mid, bop));
で、add_opt_method関数。
add_opt_method(VALUE klass, ID mid, VALUE bop)
{
NODE *node;
if (st_lookup(RCLASS_M_TBL(klass), mid, (void *)&nod...
nd_type(node->nd_body->nd_body) == NODE_CFUNC) {
st_insert(vm_opt_method_table, (st_data_t)node, (st_dat...
}
}
というわけでメソッド実装関数のNODEとBOPが関連づけられてい...
次にメソッド定義時の動作です。Rubyで再定義したとしてvm_de...
vm_insnhelper.c
vm_define_method(rb_thread_t *th, VALUE obj,
ID id, rb_iseq_t *miseq, rb_num_t is_singleton, NOD...
{
...
rb_add_method(klass, id, newbody, noex);
eval_method.c
rb_add_method(VALUE klass, ID mid, NODE * node, int noex)
{
...
/* check re-definition */
st_data_t data;
NODE *old_node;
if (st_lookup(RCLASS_M_TBL(klass), mid, &data)) {
old_node = (NODE *)data;
if (old_node) {
if (nd_type(old_node->nd_body->nd_body) == NODE_CFUNC) {
rb_vm_check_redefinition_opt_method(old_node);
}
vm.c
rb_vm_check_redefinition_opt_method(NODE *node)
{
VALUE bop;
if (st_lookup(vm_opt_method_table, (st_data_t)node, ...
ruby_vm_redefined_flag |= bop;
}
}
といった感じにruby_vm_redefined_flagが変更されています。...
*おわりに [#jc19fb11]
今回はYARVコードへの実行を見てきました。YARVコードまで落...
一応ここまででrubyコマンドを打ってからスクリプトが実行さ...
ページ名: