Scm_VMCallCC (18)
捕捉した継続を呼び出した時にどうなるかを確認。
イメージ的には
gosh> (define cont #f) cont gosh> (list 1 2 (call/cc (lambda (c) (set! cont c) 3)) 4) (1 2 3 4) gosh> (cont -1) (1 2 -1 4) gosh>
の (cont -1) の時のソレ。disasm したナニが以下。
gosh> (disasm (lambda () (cont -1))) main_code (name=#f, code=0x80d67c0, size=4, const=1, stack=4): args: #f 0 CONSTI-PUSH(-1) 1 GREF-TAIL-CALL(1) #<identifier user#cont>; (cont -1) 3 RET #<undef> gosh>
結局呼び出されるのは以下の式で作られた subr です。
contproc = Scm_MakeSubr(throw_continuation, ep, 0, 1, SCM_MAKE_STR("continuation"));
これが以下な命令列でナニ。
SP = ARGP; PC = PC_TO_RETURN; SCM_PROF_COUNT_CALL(vm, VAL0); VAL0 = SCM_SUBR(VAL0)->func(ARGP, argc, SCM_SUBR(VAL0)->data); /* the subr may substituted pc, so we need to check if we can pop the continuation immediately. */ if (TAIL_POS()) RETURN_OP(); NEXT;
実質呼び出されるのは throw_continuation 関数。引数フレームなポインタを渡している模様。VAL0 が指す領域 (ScmSubr 型なナニ) の data 属性 (ScmEscapePoint 型の領域のポインタ) も渡されています。
端折りつつどんどん進める
handlers 云々はスルー。基本的に
- PC に PC_TO_RETURN がセット
- RET です
- ので RETURN_OP にて POP_CONT されます。
- CONT には ep の cont 属性がセット
- 上記の通り pop されます
- handlers も復帰
で、面白いのが最後の処理。
nargs = Scm_Length(args); if (nargs == 1) { return SCM_CAR(args); } else if (nargs < 1) { return SCM_UNDEFINED; } else if (nargs >= SCM_VM_MAX_VALUES) { Scm_Error("too many values passed to the continuation"); } for (i=0, ap=SCM_CDR(args); SCM_PAIRP(ap); i++, ap=SCM_CDR(ap)) { vm->vals[i] = SCM_CAR(ap); } vm->numVals = nargs; return SCM_CAR(args);
これって直前エントリな多値のナニなフレーバがあったりする気が。確かに何回かこれって見てるんですが、若干不思議な印象があったんだよなぁ、と。
そりゃええんですが
引数が return されてます。ちょっと微妙に道に迷ってるカンジです。
あら?
gosh> (disasm (lambda () (list 1 2 (call/cc (lambda (c) (set1 cont c) 3)) 4))) main_code (name=#f, code=0x80f9db0, size=12, const=2, stack=12): args: #f 0 CONSTI-PUSH(1) 1 CONSTI-PUSH(2) 2 PRE-CALL(1) 8 4 CLOSURE #<lambda 0> ; (lambda (c) (set1 cont c) 3) 6 PUSH-GREF-CALL(1) #<identifier user#call/cc>; (call/cc (lambda (c) (set1 cont c) 3)) 8 PUSH 9 CONSTI(4) 10 LIST(4) ; (list 1 2 (call/cc (lambda (c) (set1 con ... 11 RET internal_closure_0 (name=#f, code=0x80f8ca0, size=8, const=2 stack=11): args: #f 0 PRE-CALL(2) 6 2 GREF-PUSH #<identifier user#cont>; cont 4 LREF0-PUSH-GREF-CALL(2) #<identifier user#set1>; (set1 cont c) 6 CONSTI(3) 7 RET #<undef> gosh>
なナニであれば復帰は 8?
そうだとしたら val0 なソレを push して続き、になりますな。継続が push されるのは 2 な時点で次の PC は 8 ってコトなのか。
なんと言えばよいやら
上記ビンゴだとしたらこれはこれで凄いな。2, 4, 6, 8 なあたり。