mruby/例外処理を読む
をテンプレートにして作成
[
トップ
] [
新規
|
一覧
|
単語検索
|
最終更新
|
ヘルプ
]
開始行:
[[mrubyを読む]]
#contents
*はじめに [#l5d6a909]
今回はmrubyの例外処理を読解します。なお、実行手順読解で使...
*サンプルスクリプト [#ya182d05]
基本的にYARV読解の時に使ったものと同じですがmrubyは$!やba...
def exc_func
begin
raise "Exception"
ensure
puts "ensure in exc_func"
end
end
begin
exc_func
rescue => e
puts "rescue in top"
puts e.message
ensure
puts "ensure in top"
end
*NODEツリーへの変換 [#p114693a]
NODEツリーは--verboseオプション付きでmrubyを実行すれば出...
NODE_DEF:
exc_func
NODE_BEGIN:
NODE_ENSURE:
body:
NODE_BEGIN:
NODE_CALL:
NODE_SELF
method='raise' (34)
args:
NODE_STR "Exception" len 9
ensure:
NODE_BEGIN:
NODE_CALL:
NODE_SELF
method='puts' (286)
args:
NODE_STR "ensure in exc_func" len 18
exc_funcの定義です。どの部分の実行に対してensureが適用さ...
NODE_ENSURE:
body:
NODE_RESCUE:
body:
NODE_BEGIN:
NODE_CALL:
NODE_SELF
method='exc_func' (298)
rescue:
exc_var:
NODE_LVAR e
rescue body:
NODE_BEGIN:
NODE_CALL:
NODE_SELF
method='puts' (286)
args:
NODE_DSTR
NODE_STR "rescue in top: Exception#mes...
NODE_BEGIN:
NODE_CALL:
NODE_LVAR e
method='message' (175)
NODE_STR "" len 0
ensure:
NODE_BEGIN:
NODE_CALL:
NODE_SELF
method='puts' (286)
args:
NODE_STR "ensure in top" len 13
トップレベルのbegin〜end部分です。こちらはrescue対象とな...
*実行コードへの変換 [#jd9f7105]
**実行コード変換結果 [#dbba5bd4]
次に実行コード生成です。ソースを見る前に生成される実行コ...
irep 134 nregs=7 nlocals=3 pools=2 syms=5
000 OP_TCLASS R3
001 OP_LAMBDA R4 I(135) 1
002 OP_METHOD R3 :exc_func
003 OP_EPUSH :I(137)
004 OP_ONERR 009
005 OP_LOADSELF R3
006 OP_LOADNIL R4
007 OP_SEND R3 :exc_func 0
008 OP_JMP 029
009 OP_RESCUE R4
010 OP_GETCONST R5 :StandardError
011 OP_MOVE R6 R4
012 OP_LOADNIL R7
013 OP_SEND R5 :=== 1
014 OP_JMPIF R5 016
015 OP_JMP 028
016 OP_MOVE R1 R4
017 OP_LOADSELF R4
018 OP_STRING R5 "rescue in top: Exception#messag...
019 OP_MOVE R6 R1
020 OP_LOADNIL R7
021 OP_SEND R6 :message 0
022 OP_STRCAT R5 R6
023 OP_STRING R6 ""
024 OP_STRCAT R5 R6
025 OP_LOADNIL R6
026 OP_SEND R4 :puts 1
027 OP_JMP 030
028 OP_RAISE R4
029 OP_POPERR 1
030 OP_EPOP 1
031 OP_STOP
トップレベルの実行コードです。OP_EPUSHとOP_EPOP、OP_ONERR...
irep 135 nregs=5 nlocals=3 pools=1 syms=1
000 OP_ENTER 0:0:0:0:0:0:0
001 OP_EPUSH :I(136)
002 OP_LOADSELF R3
003 OP_STRING R4 "Exception"
004 OP_LOADNIL R5
005 OP_SEND R3 :raise 1
006 OP_EPOP 1
007 OP_RETURN R3
exc_funcの実行コードです。
irep 136 nregs=4 nlocals=2 pools=1 syms=1
000 OP_LOADSELF R2
001 OP_STRING R3 "ensure in exc_func"
002 OP_LOADNIL R4
003 OP_SEND R2 :puts 1
004 OP_RETURN R3
exc_funcのensure節部分です。
irep 137 nregs=4 nlocals=2 pools=1 syms=1
000 OP_LOADSELF R2
001 OP_STRING R3 "ensure in top"
002 OP_LOADNIL R4
003 OP_SEND R2 :puts 1
004 OP_RETURN R3
トップレベルのensure節部分です。
**NODE_ENSURE [#f7a60df8]
それではソースを見てみましょう。なお、以下のコード断片はs...
#code(C){{
case NODE_ENSURE:
{
int idx;
int epush = s->pc;
genop(s, MKOP_Bx(OP_EPUSH, 0));
s->ensure_level++;
codegen(s, tree->car, val);
idx = scope_body(s, tree->cdr);
s->iseq[epush] = MKOP_Bx(OP_EPUSH, idx);
s->ensure_level--;
genop_peep(s, MKOP_A(OP_EPOP, 1), NOVAL);
}
break;
}}
tree->carはbody部分、tree->cdrはensure部分です。やってい...
+まず、OP_EPUSHを埋め込んで
+body部分のコードを生成し
+次にensure部分のコードを生成してirep番号を取得
+先ほど埋め込んだOP_EPUSHに取得したirep番号を設定
+最後にOP_EPOPを埋め込む
なお、OP_EPOPの埋め込み出genop_peep()が呼ばれていますがこ...
begin
begin
ensure
end
ensure; end
NODE_SCOPE:
NODE_BEGIN:
NODE_ENSURE:
body:
NODE_BEGIN:
NODE_ENSURE:
body:
NODE_BEGIN:
ensure:
NODE_BEGIN:
ensure:
NODE_BEGIN:
irep 134 nregs=0 nlocals=2 pools=0 syms=0
000 OP_EPUSH :I(136)
001 OP_EPUSH :I(135)
002 OP_EPOP 2
003 OP_STOP
**NODE_RESCUE [#c7e6ebb2]
NODE_ENSUREに比べNODE_RESCUEはかなり複雑です。例によって...
#code(C){{
case NODE_RESCUE:
{
int onerr, noexc, exend, pos1, pos2, tmp;
struct loopinfo *lp;
onerr = new_label(s);
genop(s, MKOP_Bx(OP_ONERR, 0));
}}
まず「例外が起きたらここへ」なコードを埋め込んでいます。...
#code(C){{
lp = loop_push(s, LOOP_BEGIN);
lp->pc1 = onerr;
if (tree->car) {
codegen(s, tree->car, val);
}
}}
body部分のコードを生成しています。
#code(C){{
lp->type = LOOP_RESCUE;
noexc = new_label(s);
genop(s, MKOP_Bx(OP_JMP, 0));
}}
例外が起きなかった場合のジャンプ命令を埋め込んでいます。
#code(C){{
dispatch(s, onerr);
}}
以下、rescue部分のコードを生成します。というわけで「例外...
#code(C){{
tree = tree->cdr;
exend = 0;
pos1 = 0;
if (tree->car) {
node *n2 = tree->car;
int exc = cursp();
genop(s, MKOP_A(OP_RESCUE, exc));
push();
}}
まず発生した例外を受け取る命令を生成しています。
#code(C){{
while (n2) {
node *n3 = n2->car;
node *n4 = n3->car;
}}
rescue節の数だけ、ループを回します。n3には発生した例外を...
#code(C){{
if (pos1) dispatch(s, pos1);
pos2 = 0;
do {
if (n4) {
codegen(s, n4->car, VAL);
}
else {
genop(s, MKOP_ABx(OP_GETCONST, cursp(), new...
push();
}
genop(s, MKOP_AB(OP_MOVE, cursp(), exc));
push();
genop(s, MKOP_A(OP_LOADNIL, cursp()));
pop(); pop();
genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(...
tmp = new_label(s);
genop(s, MKOP_AsBx(OP_JMPIF, cursp(), pos2));
pos2 = tmp;
if (n4) {
n4 = n4->cdr;
}
} while (n4);
}}
rescue対象となる例外クラスの数だけループを回します。具体...
rescue AAAError, BBBError
埋め込んでいる処理内容は以下のようになります。
+例外クラスを取得して
+発生した例外を引数に例外クラスに対して===を実行
+trueが返ってきたらrescue節の中身が書かれている部分にジャ...
#code(C){{
pos1 = new_label(s);
genop(s, MKOP_sBx(OP_JMP, 0));
dispatch_linked(s, pos2);
}}
あるrescue節で対象となる例外クラスでなかった場合に次のres...
#code(C){{
pop();
if (n3->cdr->car) {
gen_assignment(s, n3->cdr->car, exc, NOVAL);
}
}}
例外を受け取る変数に代入するコードを生成しています。
#code(C){{
if (n3->cdr->cdr->car) {
codegen(s, n3->cdr->cdr->car, val);
}
}}
rescue節の本体に対応するコードを生成しています。
#code(C){{
tmp = new_label(s);
genop(s, MKOP_sBx(OP_JMP, exend));
exend = tmp;
n2 = n2->cdr;
push();
}
}}
最後にrescueの末尾に飛ぶジャンプ命令を生成しています。
#code(C){{
if (pos1) {
dispatch(s, pos1);
genop(s, MKOP_A(OP_RAISE, exc));
}
}
}}
いずれのrescue節にも引っ掛からなかった場合に例外を再送す...
#code(C){{
pop();
tree = tree->cdr;
dispatch(s, noexc);
genop(s, MKOP_A(OP_POPERR, 1));
}}
例外が起きなかった場合のジャンプ命令の飛び先を設定し、積...
#code(C){{
if (tree->car) {
codegen(s, tree->car, val);
}
dispatch_linked(s, exend);
loop_pop(s, NOVAL);
}
break;
}}
最後にelse節のコードを生成して終了です。
*コードの実行 [#hd55905a]
コードの生成を見たので次はコードの実行を見てみましょう。...
**例外に対する備え [#oa0661dc]
***OP_EPUSH [#j97e0ee0]
まず、ensureを積むOP_EPUSHです。callinfoのeidxで積まれて...
#code(C){{
CASE(OP_EPUSH) {
/* Bx ensure_push(SEQ[Bx]) */
struct RProc *p;
p = mrb_closure_new(mrb, mrb->irep[irep->idx+GETARG...
/* push ensure_stack */
if (mrb->esize <= mrb->ci->eidx) {
if (mrb->esize == 0) mrb->esize = 16;
else mrb->esize *= 2;
mrb->ensure = mrb_realloc(mrb, mrb->ensure, sizeo...
}
mrb->ensure[mrb->ci->eidx++] = p;
NEXT;
}
}}
***OP_ONERR [#n154a1a6]
次にrescue情報を積むOP_ONERRです。基本的にensureと同じで...
:ensureの場合|積まれるのは別のirep
:rescueの場合|積まれるのは同じirep内のアドレス
#code(C){{
CASE(OP_ONERR) {
/* sBx pc+=sBx on exception */
if (mrb->rsize <= mrb->ci->ridx) {
if (mrb->rsize == 0) mrb->rsize = 16;
else mrb->rsize *= 2;
mrb->rescue = mrb_realloc(mrb, mrb->rescue, sizeo...
}
mrb->rescue[mrb->ci->ridx++] = pc + GETARG_sBx(i);
NEXT;
}
}}
**例外の発生 [#je8109e7]
***mrb_f_raise(src/kernel.c) [#s4c8b279]
さて、ここからが面白いところです。Rubyではraiseは予約語で...
#code(C){{
mrb_value
mrb_f_raise(mrb_state *mrb, mrb_value self)
{
mrb_value a[2];
int argc;
argc = mrb_get_args(mrb, "|oo", &a[0], &a[1]);
switch (argc) {
case 0:
mrb_raise(mrb, mrb->eRuntimeError_class, "");
break;
case 1:
a[1] = mrb_check_string_type(mrb, a[0]);
if (!mrb_nil_p(a[1])) {
argc = 2;
a[0] = mrb_obj_value(mrb->eRuntimeError_class);
}
/* fall through */
default:
mrb_exc_raise(mrb, mrb_make_exception(mrb, argc, a));
}
return mrb_nil_value(); /* not reached */
}
}}
mrb_make_exception()は飛ばしてmrb_exc_raise()に進みます。...
#code(C){{
void
mrb_exc_raise(mrb_state *mrb, mrb_value exc)
{
mrb->exc = (struct RObject*)mrb_object(exc);
longjmp(*(jmp_buf*)mrb->jmp, 1);
}
}}
**例外処理ルーチン探索 [#nea8649d]
mrb_exc_raise()でlongjmpした飛び先はどこかというとmrb_run...
#code(C){{
if (setjmp(c_jmp) == 0) {
prev_jmp = mrb->jmp;
mrb->jmp = &c_jmp;
}
else {
goto L_RAISE;
}
}}
で、L_RAISEがどこにあるのかというと、OP_RETURNを処理して...
#code(C){{
CASE(OP_RETURN) {
/* A return R(A) */
L_RETURN:
if (mrb->exc) {
mrb_callinfo *ci;
int eidx;
L_RAISE:
}}
では例によって分割して説明していきます。
#code(C){{
ci = mrb->ci;
eidx = mrb->ci->eidx;
if (ci == mrb->cibase) goto L_STOP;
while (ci[0].ridx == ci[-1].ridx) {
cipop(mrb);
ci = mrb->ci;
}}
whileの条件が成り立たない場合(つまり、ci[0].ridx > ci[-1...
#code(C){{
if (ci->acc < 0) {
mrb->jmp = prev_jmp;
longjmp(*(jmp_buf*)mrb->jmp, 1);
}
}}
ci->accが負の値とはどういう場合かというとCで実装されたメ...
#code(C){{
while (eidx > mrb->ci->eidx) {
ecall(mrb, --eidx);
}
}}
ensure節の実行です。ecall()の中身に踏み込みます。
#code(C){{
static void
ecall(mrb_state *mrb, int i)
{
struct RProc *p;
mrb_callinfo *ci;
mrb_value *self = mrb->stack;
struct RObject *exc;
p = mrb->ensure[i];
ci = cipush(mrb);
ci->stackidx = mrb->stack - mrb->stbase;
ci->mid = ci[-1].mid;
ci->acc = -1;
ci->argc = 0;
ci->proc = p;
ci->nregs = p->body.irep->nregs;
ci->target_class = p->target_class;
mrb->stack = mrb->stack + ci[-1].nregs;
exc = mrb->exc; mrb->exc = 0;
mrb_run(mrb, p, *self);
if (!mrb->exc) mrb->exc = exc;
}
}}
というわけでensure節の実行はmrb_run()を再帰呼び出しするこ...
-クリアしておかないとensure終了時にまた例外処理に入ってし...
-ensure中で例外が発生したら例外を差し替える
という実装上の都合と仕様的な話からこのようになっています。
mrb_run()のOP_RETURN、例外処理部分に戻ります。
#code(C){{
if (ci == mrb->cibase) {
if (ci->ridx == 0) {
mrb->stack = mrb->stbase;
goto L_STOP;
}
break;
}
}
}}
rescueを探したけどなかった場合です。この場合はmrb_run()を...
#code(C){{
irep = ci->proc->body.irep;
pool = irep->pool;
syms = irep->syms;
regs = mrb->stack = mrb->stbase + ci[1].stackidx;
pc = mrb->rescue[--ci->ridx];
}
else {
(省略)
}
JUMP;
}
}}
rescueがあった場合の処理です。rescueの先頭にpcが設定され...
**rescueの実行 [#wb50515a]
rescueの先頭にはOP_RESCUE命令があります。
#code(C){{
CASE(OP_RESCUE) {
/* A R(A) := exc; clear(exc) */
SET_OBJ_VALUE(regs[GETARG_A(i)],mrb->exc);
mrb->exc = 0;
NEXT;
}
}}
これでレジスタに例外が格納されました。
この後、例外が捕捉対象のクラスかの判定が行われます。以下...
$ ./bin/mruby.exe --verbose -e 'begin; rescue AAAError; ...
irep 134 nregs=5 nlocals=2 pools=3 syms=4
000 OP_ONERR 002
001 OP_JMP 026
002 OP_RESCUE R2
003 OP_GETCONST R3 :AAAError
004 OP_MOVE R4 R2
005 OP_LOADNIL R5
006 OP_SEND R3 :=== 1
007 OP_JMPIF R3 009
008 OP_JMP 014
009 OP_LOADSELF R2
010 OP_STRING R3 "AAA"
011 OP_LOADNIL R4
012 OP_SEND R2 :p 1
013 OP_JMP 027
014 OP_GETCONST R3 :BBBError
015 OP_MOVE R4 R2
016 OP_LOADNIL R5
017 OP_SEND R3 :=== 1
018 OP_JMPIF R3 020
019 OP_JMP 025
020 OP_LOADSELF R2
021 OP_STRING R3 "BBB"
022 OP_LOADNIL R4
023 OP_SEND R2 :p 1
024 OP_JMP 027
025 OP_RAISE R2
026 OP_POPERR 1
027 OP_LOADSELF R2
028 OP_STRING R3 "XXX"
029 OP_LOADNIL R4
030 OP_SEND R2 :p 1
031 OP_STOP
+レジスタR2に例外を格納
+AAAErrorに対してレジスタR2(発生した例外)を引数に===を...
+trueが返ってきた(AAAErrorのサブクラスだった)ら009のres...
++rescue節を実行したらその後のコードに飛ぶ(013のOP_JMP部...
+falseの場合は008に進み、014(次のrescue条件判定)にジャ...
+いずれのrescueも発生した例外を捕捉しない場合はOP_RAISE(...
**例外が起こらなかった場合の処理 [#h2b2dd9e]
次に例外が起こらない場合どうなるかを見てみます。と思った...
*おわりに [#m877ff55]
というわけでmrubyの例外処理周りを見てきました。このあたり...
なお、例外処理ルーチン実行の実装はYARVと結構違っています...
終了行:
[[mrubyを読む]]
#contents
*はじめに [#l5d6a909]
今回はmrubyの例外処理を読解します。なお、実行手順読解で使...
*サンプルスクリプト [#ya182d05]
基本的にYARV読解の時に使ったものと同じですがmrubyは$!やba...
def exc_func
begin
raise "Exception"
ensure
puts "ensure in exc_func"
end
end
begin
exc_func
rescue => e
puts "rescue in top"
puts e.message
ensure
puts "ensure in top"
end
*NODEツリーへの変換 [#p114693a]
NODEツリーは--verboseオプション付きでmrubyを実行すれば出...
NODE_DEF:
exc_func
NODE_BEGIN:
NODE_ENSURE:
body:
NODE_BEGIN:
NODE_CALL:
NODE_SELF
method='raise' (34)
args:
NODE_STR "Exception" len 9
ensure:
NODE_BEGIN:
NODE_CALL:
NODE_SELF
method='puts' (286)
args:
NODE_STR "ensure in exc_func" len 18
exc_funcの定義です。どの部分の実行に対してensureが適用さ...
NODE_ENSURE:
body:
NODE_RESCUE:
body:
NODE_BEGIN:
NODE_CALL:
NODE_SELF
method='exc_func' (298)
rescue:
exc_var:
NODE_LVAR e
rescue body:
NODE_BEGIN:
NODE_CALL:
NODE_SELF
method='puts' (286)
args:
NODE_DSTR
NODE_STR "rescue in top: Exception#mes...
NODE_BEGIN:
NODE_CALL:
NODE_LVAR e
method='message' (175)
NODE_STR "" len 0
ensure:
NODE_BEGIN:
NODE_CALL:
NODE_SELF
method='puts' (286)
args:
NODE_STR "ensure in top" len 13
トップレベルのbegin〜end部分です。こちらはrescue対象とな...
*実行コードへの変換 [#jd9f7105]
**実行コード変換結果 [#dbba5bd4]
次に実行コード生成です。ソースを見る前に生成される実行コ...
irep 134 nregs=7 nlocals=3 pools=2 syms=5
000 OP_TCLASS R3
001 OP_LAMBDA R4 I(135) 1
002 OP_METHOD R3 :exc_func
003 OP_EPUSH :I(137)
004 OP_ONERR 009
005 OP_LOADSELF R3
006 OP_LOADNIL R4
007 OP_SEND R3 :exc_func 0
008 OP_JMP 029
009 OP_RESCUE R4
010 OP_GETCONST R5 :StandardError
011 OP_MOVE R6 R4
012 OP_LOADNIL R7
013 OP_SEND R5 :=== 1
014 OP_JMPIF R5 016
015 OP_JMP 028
016 OP_MOVE R1 R4
017 OP_LOADSELF R4
018 OP_STRING R5 "rescue in top: Exception#messag...
019 OP_MOVE R6 R1
020 OP_LOADNIL R7
021 OP_SEND R6 :message 0
022 OP_STRCAT R5 R6
023 OP_STRING R6 ""
024 OP_STRCAT R5 R6
025 OP_LOADNIL R6
026 OP_SEND R4 :puts 1
027 OP_JMP 030
028 OP_RAISE R4
029 OP_POPERR 1
030 OP_EPOP 1
031 OP_STOP
トップレベルの実行コードです。OP_EPUSHとOP_EPOP、OP_ONERR...
irep 135 nregs=5 nlocals=3 pools=1 syms=1
000 OP_ENTER 0:0:0:0:0:0:0
001 OP_EPUSH :I(136)
002 OP_LOADSELF R3
003 OP_STRING R4 "Exception"
004 OP_LOADNIL R5
005 OP_SEND R3 :raise 1
006 OP_EPOP 1
007 OP_RETURN R3
exc_funcの実行コードです。
irep 136 nregs=4 nlocals=2 pools=1 syms=1
000 OP_LOADSELF R2
001 OP_STRING R3 "ensure in exc_func"
002 OP_LOADNIL R4
003 OP_SEND R2 :puts 1
004 OP_RETURN R3
exc_funcのensure節部分です。
irep 137 nregs=4 nlocals=2 pools=1 syms=1
000 OP_LOADSELF R2
001 OP_STRING R3 "ensure in top"
002 OP_LOADNIL R4
003 OP_SEND R2 :puts 1
004 OP_RETURN R3
トップレベルのensure節部分です。
**NODE_ENSURE [#f7a60df8]
それではソースを見てみましょう。なお、以下のコード断片はs...
#code(C){{
case NODE_ENSURE:
{
int idx;
int epush = s->pc;
genop(s, MKOP_Bx(OP_EPUSH, 0));
s->ensure_level++;
codegen(s, tree->car, val);
idx = scope_body(s, tree->cdr);
s->iseq[epush] = MKOP_Bx(OP_EPUSH, idx);
s->ensure_level--;
genop_peep(s, MKOP_A(OP_EPOP, 1), NOVAL);
}
break;
}}
tree->carはbody部分、tree->cdrはensure部分です。やってい...
+まず、OP_EPUSHを埋め込んで
+body部分のコードを生成し
+次にensure部分のコードを生成してirep番号を取得
+先ほど埋め込んだOP_EPUSHに取得したirep番号を設定
+最後にOP_EPOPを埋め込む
なお、OP_EPOPの埋め込み出genop_peep()が呼ばれていますがこ...
begin
begin
ensure
end
ensure; end
NODE_SCOPE:
NODE_BEGIN:
NODE_ENSURE:
body:
NODE_BEGIN:
NODE_ENSURE:
body:
NODE_BEGIN:
ensure:
NODE_BEGIN:
ensure:
NODE_BEGIN:
irep 134 nregs=0 nlocals=2 pools=0 syms=0
000 OP_EPUSH :I(136)
001 OP_EPUSH :I(135)
002 OP_EPOP 2
003 OP_STOP
**NODE_RESCUE [#c7e6ebb2]
NODE_ENSUREに比べNODE_RESCUEはかなり複雑です。例によって...
#code(C){{
case NODE_RESCUE:
{
int onerr, noexc, exend, pos1, pos2, tmp;
struct loopinfo *lp;
onerr = new_label(s);
genop(s, MKOP_Bx(OP_ONERR, 0));
}}
まず「例外が起きたらここへ」なコードを埋め込んでいます。...
#code(C){{
lp = loop_push(s, LOOP_BEGIN);
lp->pc1 = onerr;
if (tree->car) {
codegen(s, tree->car, val);
}
}}
body部分のコードを生成しています。
#code(C){{
lp->type = LOOP_RESCUE;
noexc = new_label(s);
genop(s, MKOP_Bx(OP_JMP, 0));
}}
例外が起きなかった場合のジャンプ命令を埋め込んでいます。
#code(C){{
dispatch(s, onerr);
}}
以下、rescue部分のコードを生成します。というわけで「例外...
#code(C){{
tree = tree->cdr;
exend = 0;
pos1 = 0;
if (tree->car) {
node *n2 = tree->car;
int exc = cursp();
genop(s, MKOP_A(OP_RESCUE, exc));
push();
}}
まず発生した例外を受け取る命令を生成しています。
#code(C){{
while (n2) {
node *n3 = n2->car;
node *n4 = n3->car;
}}
rescue節の数だけ、ループを回します。n3には発生した例外を...
#code(C){{
if (pos1) dispatch(s, pos1);
pos2 = 0;
do {
if (n4) {
codegen(s, n4->car, VAL);
}
else {
genop(s, MKOP_ABx(OP_GETCONST, cursp(), new...
push();
}
genop(s, MKOP_AB(OP_MOVE, cursp(), exc));
push();
genop(s, MKOP_A(OP_LOADNIL, cursp()));
pop(); pop();
genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(...
tmp = new_label(s);
genop(s, MKOP_AsBx(OP_JMPIF, cursp(), pos2));
pos2 = tmp;
if (n4) {
n4 = n4->cdr;
}
} while (n4);
}}
rescue対象となる例外クラスの数だけループを回します。具体...
rescue AAAError, BBBError
埋め込んでいる処理内容は以下のようになります。
+例外クラスを取得して
+発生した例外を引数に例外クラスに対して===を実行
+trueが返ってきたらrescue節の中身が書かれている部分にジャ...
#code(C){{
pos1 = new_label(s);
genop(s, MKOP_sBx(OP_JMP, 0));
dispatch_linked(s, pos2);
}}
あるrescue節で対象となる例外クラスでなかった場合に次のres...
#code(C){{
pop();
if (n3->cdr->car) {
gen_assignment(s, n3->cdr->car, exc, NOVAL);
}
}}
例外を受け取る変数に代入するコードを生成しています。
#code(C){{
if (n3->cdr->cdr->car) {
codegen(s, n3->cdr->cdr->car, val);
}
}}
rescue節の本体に対応するコードを生成しています。
#code(C){{
tmp = new_label(s);
genop(s, MKOP_sBx(OP_JMP, exend));
exend = tmp;
n2 = n2->cdr;
push();
}
}}
最後にrescueの末尾に飛ぶジャンプ命令を生成しています。
#code(C){{
if (pos1) {
dispatch(s, pos1);
genop(s, MKOP_A(OP_RAISE, exc));
}
}
}}
いずれのrescue節にも引っ掛からなかった場合に例外を再送す...
#code(C){{
pop();
tree = tree->cdr;
dispatch(s, noexc);
genop(s, MKOP_A(OP_POPERR, 1));
}}
例外が起きなかった場合のジャンプ命令の飛び先を設定し、積...
#code(C){{
if (tree->car) {
codegen(s, tree->car, val);
}
dispatch_linked(s, exend);
loop_pop(s, NOVAL);
}
break;
}}
最後にelse節のコードを生成して終了です。
*コードの実行 [#hd55905a]
コードの生成を見たので次はコードの実行を見てみましょう。...
**例外に対する備え [#oa0661dc]
***OP_EPUSH [#j97e0ee0]
まず、ensureを積むOP_EPUSHです。callinfoのeidxで積まれて...
#code(C){{
CASE(OP_EPUSH) {
/* Bx ensure_push(SEQ[Bx]) */
struct RProc *p;
p = mrb_closure_new(mrb, mrb->irep[irep->idx+GETARG...
/* push ensure_stack */
if (mrb->esize <= mrb->ci->eidx) {
if (mrb->esize == 0) mrb->esize = 16;
else mrb->esize *= 2;
mrb->ensure = mrb_realloc(mrb, mrb->ensure, sizeo...
}
mrb->ensure[mrb->ci->eidx++] = p;
NEXT;
}
}}
***OP_ONERR [#n154a1a6]
次にrescue情報を積むOP_ONERRです。基本的にensureと同じで...
:ensureの場合|積まれるのは別のirep
:rescueの場合|積まれるのは同じirep内のアドレス
#code(C){{
CASE(OP_ONERR) {
/* sBx pc+=sBx on exception */
if (mrb->rsize <= mrb->ci->ridx) {
if (mrb->rsize == 0) mrb->rsize = 16;
else mrb->rsize *= 2;
mrb->rescue = mrb_realloc(mrb, mrb->rescue, sizeo...
}
mrb->rescue[mrb->ci->ridx++] = pc + GETARG_sBx(i);
NEXT;
}
}}
**例外の発生 [#je8109e7]
***mrb_f_raise(src/kernel.c) [#s4c8b279]
さて、ここからが面白いところです。Rubyではraiseは予約語で...
#code(C){{
mrb_value
mrb_f_raise(mrb_state *mrb, mrb_value self)
{
mrb_value a[2];
int argc;
argc = mrb_get_args(mrb, "|oo", &a[0], &a[1]);
switch (argc) {
case 0:
mrb_raise(mrb, mrb->eRuntimeError_class, "");
break;
case 1:
a[1] = mrb_check_string_type(mrb, a[0]);
if (!mrb_nil_p(a[1])) {
argc = 2;
a[0] = mrb_obj_value(mrb->eRuntimeError_class);
}
/* fall through */
default:
mrb_exc_raise(mrb, mrb_make_exception(mrb, argc, a));
}
return mrb_nil_value(); /* not reached */
}
}}
mrb_make_exception()は飛ばしてmrb_exc_raise()に進みます。...
#code(C){{
void
mrb_exc_raise(mrb_state *mrb, mrb_value exc)
{
mrb->exc = (struct RObject*)mrb_object(exc);
longjmp(*(jmp_buf*)mrb->jmp, 1);
}
}}
**例外処理ルーチン探索 [#nea8649d]
mrb_exc_raise()でlongjmpした飛び先はどこかというとmrb_run...
#code(C){{
if (setjmp(c_jmp) == 0) {
prev_jmp = mrb->jmp;
mrb->jmp = &c_jmp;
}
else {
goto L_RAISE;
}
}}
で、L_RAISEがどこにあるのかというと、OP_RETURNを処理して...
#code(C){{
CASE(OP_RETURN) {
/* A return R(A) */
L_RETURN:
if (mrb->exc) {
mrb_callinfo *ci;
int eidx;
L_RAISE:
}}
では例によって分割して説明していきます。
#code(C){{
ci = mrb->ci;
eidx = mrb->ci->eidx;
if (ci == mrb->cibase) goto L_STOP;
while (ci[0].ridx == ci[-1].ridx) {
cipop(mrb);
ci = mrb->ci;
}}
whileの条件が成り立たない場合(つまり、ci[0].ridx > ci[-1...
#code(C){{
if (ci->acc < 0) {
mrb->jmp = prev_jmp;
longjmp(*(jmp_buf*)mrb->jmp, 1);
}
}}
ci->accが負の値とはどういう場合かというとCで実装されたメ...
#code(C){{
while (eidx > mrb->ci->eidx) {
ecall(mrb, --eidx);
}
}}
ensure節の実行です。ecall()の中身に踏み込みます。
#code(C){{
static void
ecall(mrb_state *mrb, int i)
{
struct RProc *p;
mrb_callinfo *ci;
mrb_value *self = mrb->stack;
struct RObject *exc;
p = mrb->ensure[i];
ci = cipush(mrb);
ci->stackidx = mrb->stack - mrb->stbase;
ci->mid = ci[-1].mid;
ci->acc = -1;
ci->argc = 0;
ci->proc = p;
ci->nregs = p->body.irep->nregs;
ci->target_class = p->target_class;
mrb->stack = mrb->stack + ci[-1].nregs;
exc = mrb->exc; mrb->exc = 0;
mrb_run(mrb, p, *self);
if (!mrb->exc) mrb->exc = exc;
}
}}
というわけでensure節の実行はmrb_run()を再帰呼び出しするこ...
-クリアしておかないとensure終了時にまた例外処理に入ってし...
-ensure中で例外が発生したら例外を差し替える
という実装上の都合と仕様的な話からこのようになっています。
mrb_run()のOP_RETURN、例外処理部分に戻ります。
#code(C){{
if (ci == mrb->cibase) {
if (ci->ridx == 0) {
mrb->stack = mrb->stbase;
goto L_STOP;
}
break;
}
}
}}
rescueを探したけどなかった場合です。この場合はmrb_run()を...
#code(C){{
irep = ci->proc->body.irep;
pool = irep->pool;
syms = irep->syms;
regs = mrb->stack = mrb->stbase + ci[1].stackidx;
pc = mrb->rescue[--ci->ridx];
}
else {
(省略)
}
JUMP;
}
}}
rescueがあった場合の処理です。rescueの先頭にpcが設定され...
**rescueの実行 [#wb50515a]
rescueの先頭にはOP_RESCUE命令があります。
#code(C){{
CASE(OP_RESCUE) {
/* A R(A) := exc; clear(exc) */
SET_OBJ_VALUE(regs[GETARG_A(i)],mrb->exc);
mrb->exc = 0;
NEXT;
}
}}
これでレジスタに例外が格納されました。
この後、例外が捕捉対象のクラスかの判定が行われます。以下...
$ ./bin/mruby.exe --verbose -e 'begin; rescue AAAError; ...
irep 134 nregs=5 nlocals=2 pools=3 syms=4
000 OP_ONERR 002
001 OP_JMP 026
002 OP_RESCUE R2
003 OP_GETCONST R3 :AAAError
004 OP_MOVE R4 R2
005 OP_LOADNIL R5
006 OP_SEND R3 :=== 1
007 OP_JMPIF R3 009
008 OP_JMP 014
009 OP_LOADSELF R2
010 OP_STRING R3 "AAA"
011 OP_LOADNIL R4
012 OP_SEND R2 :p 1
013 OP_JMP 027
014 OP_GETCONST R3 :BBBError
015 OP_MOVE R4 R2
016 OP_LOADNIL R5
017 OP_SEND R3 :=== 1
018 OP_JMPIF R3 020
019 OP_JMP 025
020 OP_LOADSELF R2
021 OP_STRING R3 "BBB"
022 OP_LOADNIL R4
023 OP_SEND R2 :p 1
024 OP_JMP 027
025 OP_RAISE R2
026 OP_POPERR 1
027 OP_LOADSELF R2
028 OP_STRING R3 "XXX"
029 OP_LOADNIL R4
030 OP_SEND R2 :p 1
031 OP_STOP
+レジスタR2に例外を格納
+AAAErrorに対してレジスタR2(発生した例外)を引数に===を...
+trueが返ってきた(AAAErrorのサブクラスだった)ら009のres...
++rescue節を実行したらその後のコードに飛ぶ(013のOP_JMP部...
+falseの場合は008に進み、014(次のrescue条件判定)にジャ...
+いずれのrescueも発生した例外を捕捉しない場合はOP_RAISE(...
**例外が起こらなかった場合の処理 [#h2b2dd9e]
次に例外が起こらない場合どうなるかを見てみます。と思った...
*おわりに [#m877ff55]
というわけでmrubyの例外処理周りを見てきました。このあたり...
なお、例外処理ルーチン実行の実装はYARVと結構違っています...
ページ名: