tokuhirom's Blog

MoarVM における DESTROY メソッドの呼び出し手順

refcnt 型のインタープリタの場合デストラクタの呼び出しは極めて容易だが、GC の場合はちょっと面倒だ。

Rakudo の場合は、src/Perl6/Metamodel/Finalization.nqp にコードがある。

以下のように、DESTROY メソッドを持っているクラスを MoarVM に settypefinalize している。

role Perl6::Metamodel::Finalization {
    has @!destroyers;

    method setup_finalization($obj) {
        my @mro   := self.mro($obj);
        my int $i := nqp::elems(@mro);
        my @destroyers;
        while --$i >= 0 {
            my $class   := @mro[$i];
            my $destroy := $class.HOW.find_method($class, 'DESTROY', :no_fallback(1));
            if !nqp::isnull($destroy) && $destroy {
                nqp::push(@destroyers, $destroy);
            }
        }
        @!destroyers := @destroyers;
        if @destroyers {
            nqp::settypefinalize($obj, 1);
        }
    }

    method destroyers($obj) {
        @!destroyers
    }
}

settypefinalize の処理は src/core/interp.c の以下の部分で処理される。

            OP(settypefinalize):
                MVM_gc_finalize_set(tc, GET_REG(cur_op, 0).o, GET_REG(cur_op, 2).i64);
                cur_op += 4;
                goto NEXT;

MVM_gc_finalize_set により、クラスに MVM_FINALIZE_TYPE フラグが付与される。 このフラグが付与されているとオブジェクトの新規作成を行う MVM_gc_allocate_object の中で、MVM_gc_finalize_add_to_queue(tc, obj); が呼ばれ、ファイナライズキューに詰められる。

というわけで、ファイナライズ処理が必要になっているものは別枠で処理される、ということでした。