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);
が呼ばれ、ファイナライズキューに詰められる。
というわけで、ファイナライズ処理が必要になっているものは別枠で処理される、ということでした。