問題
VM のスタックは Object の配列であるが、スタックにスタックポインタを push することがある。例えば Call Frame 時など。
この場合 Object* を Object の配列の要素としたい。つまり Object* 型は Object型でもある必要がある。このため Object* のポインタアドレスを整数に reinterpret_cast し、Object.val に格納する形を Mosh では採用している。
この Object* 型を Object 型に格納したり、アドレスを Object* に戻す作業は VM 上、頻繁に行われるのでスピードを重視し安全な型では運用されていない。つまりスタックにある Object が触るだけで落ちてしまう Object* 型な可能性がある。
VM 実行時はこの仕組みでも全く問題はない。なぜならば Call Frame は必ず対応する Return でお行儀良く回収されからである。VM のバグでスタックがずれてしまわない限りは、SP からのオフセットを利用し安全に Object* 型の Object の位置が分かるのだ。
問題になるのはスタックトレース表示時である。スタックは以下の2つの理由で安全に触ることが難しい。
- スタックは意図しない状態である。エラーが起きたのは引数を中途半端にスタックに積んだ状態かもしれない。
- Tail call の最適化により Frame 作成がカットされていて1つ前の Frame をたどったときに、そこに Frame がないかもしれない。
結局スタックには2種類のオブジェクトが積まれている
- Fixnum, String などの Scheme オブジェクト
- スタックポインタ、フレームポインタオブジェクト
問題は本当は 2 のオブジェクトなので 1 のオブジェクトだと思って isFixnum() などの手続きを実行してしまうと落ちてしまうこと。
2つの区別ができないことである。
解決
思いついてしまえばとても簡単な話。スタックポインタは stackStart_ から stackEnd_ の範囲にあることを利用するだけ。
これで解決。なぜ思い浮かばなかったか謎。