test/dynwind.scm (4)

今日もへろへろ気味なんで微妙かも。昨晩の続きなんですが

;; continuation invoked while inline procedure is prepared.
;; a test to see call/cc won't mess up the VM stack.

(define (callcc-test2)
  (let ((cc #f)
        (r '()))
    (let ((s (list 1 2 3 4 (call/cc (lambda (c) (set! cc c) 5)) 6 7 8)))
      (if (null? r)
          (begin (set! r s) (cc -1))
          (list r s)))))
    
(test "call/cc (inline)" '((1 2 3 4 5 6 7 8) (1 2 3 4 -1 6 7 8))
      callcc-test2)

disasm してみる事に。
こんなカンジに。

gosh> (disasm (lambda () (list 1 2 3 (call/cc (lambda (c) (set! cont c) 4)) 5 6)))
main_code (name=#f, code=0x8149ee0, size=14, const=2, stack=13):
args: #f
     0 CONSTI-PUSH(1)
     1 CONSTI-PUSH(2)
     2 CONSTI-PUSH(3)
     3 PRE-CALL(1) 9
     5 CLOSURE #<lambda 0>      ; (lambda (c) (set! cont c) 4)
     7 PUSH-GREF-CALL(1) #<identifier user#call/cc>; (call/cc (lambda (c) (set! cont c) 4))
     9 PUSH
    10 CONSTI-PUSH(5)
    11 CONSTI(6)
    12 LIST(6)                  ; (list 1 2 3 (call/cc (lambda (c) (set! c ...
    13 RET
internal_closure_0 (name=#f, code=0x80d7b40, size=5, const=1 stack=0):
args: #f
     0 LREF0                    ; c
     1 GSET #<identifier user#cont>; cont
     3 CONSTI(4)
     4 RET
#<undef>
gosh> 

PRE-CALL って何だ、と言いつつ ReadingGauche の PRE_CALL の項を確認。基本的には

  • 戻り先を 9 にしといて
  • クロージャを作って val0 にセット
  • val を push して call/cc を CALL

コメントにある_VM スタックをめちゃめちゃにしない_というのはどーゆー意味か。あら? そゆ意味では、上記な vm 命令レベルの継続ってどうなってるのかな、と言いつつ色々弄ってると vm.c に Scm_VMCallCC という手続きを発見。

ScmObj Scm_VMCallCC(ScmObj proc)
{
    ScmObj contproc;
    ScmEscapePoint *ep;
    ScmVM *vm = theVM;

    save_cont(vm);
    ep = SCM_NEW(ScmEscapePoint);
    ep->prev = NULL;
    ep->ehandler = SCM_FALSE;
    ep->cont = vm->cont;
    ep->handlers = vm->handlers;
    ep->cstack = vm->cstack;

    contproc = Scm_MakeSubr(throw_continuation, ep, 0, 1,
                            SCM_MAKE_STR("continuation"));
    return Scm_VMApply1(proc, contproc);
}

上記によれば、subr として_継続_を作ってそれを引数として VMCallCC の引数になっている proc を apply しているように見えます。

(call/cc (lambda (c) (set! cont c) 4))

proc に渡されてるのは上記の call/cc な引数として、その中に渡されるべき継続の c をでっち上げてラストで proc に渡している模様。
気が早いですが、休みに掘ってみようかな、と。

ちなみに

MakeSubr とあったので本当かいな、と言いつつ以下な試験。

gosh> (define cont #f)
cont
gosh> (list 1 2 3 (call/cc (lambda (c) (set! cont c) 4)) 5 6)
(1 2 3 4 5 6)
gosh> cont
#<subr continuation>
gosh> 

確かに subr な模様。