ふれーむについて考える。
で、おととい、ローカル変数とフレームまわりを1/4ぐらい作り直したんだけど、まだ、これでも無理だよ。んーどうしたもんか。
目標は
- クロージャが使える
- 外の変数を参照しない関数はオーバーヘッド無しで使える。少なくともメモリ割り当ては起こらないようにする
- 1パスでできることはなるべくがんばる
- 以上を実現するために変な制限が生まれるようなことがあってはいけない
と、していた。
まず、最初の戦略は、ローカル変数の生存期間が重ならないようにローカル変数を割り当といて、必要になったらフレームまるごとコピーして、あとは関数がそのフレームを持っておく、というものだった。
function nanika() { var x; // x = 0 function func_local() { // 外を参照しない var y; // y = 0 return null; } // この時点ではフレームコピーしない function func1() { // 外を参照する var y; // y = 1 return x; } // この時点でフレームをコピー。 // 続きはコピーされたフレーム上で実行する。 // で、func1 も、そのコピーを持っておく。 function func2() { // 外を参照する var y; // y = 2 return x; } // ここではフレームはコピーされない。 // 現在実行中のフレームをfunc2に持たせる。 }
という感じ。外を参照するか、参照しないかは1パスでわかるので、これで、上の目標は達成されたかに見えた。けど、これでは無理だと気付いたのが、おとといの修正。何が無理だったのかは忘れた。(おい)
だが、しかし、それでも無理だ、ループがあるじゃないか。と、気付いたのが、おとといのを修正したあと。
function nanika() { for ( var i=0; i<256; i=i+1 ) { function reti( ) { return i; } } }
こんなふうになってると、関数retiは毎回違う変数を参照してることになる。つまり、ループは回るたびに、フレームの生成を行わないといけない場合がある。
けど、これやると1パスじゃなくなっちゃうんだよ…
function nanika() { for ( var i=0; i<256; i=i+1 ) { var z; function local_f() { return 30; } function retz() { return z+i; } } }
と、思ったけど、大丈夫か。ローカルしか参照しない関数は外を参照しないんだから、外のローカル変数がどうなっていようと関係無いはず。
ループ内にクロージャを生成してるところがある場合は、ループ頭でフレーム生成を行うようにする。
実際は、生成しなくてもいい可能性はあるけど、
function nanika() { for ( var i=0; i<256; i=i+1 ) { for ( var j=0; j<256; j=j+1 ) { // このループではフレーム生成しなくてもいい。 function reti() { return i; } } } }
これやると、さすがに1パスじゃなくなるので仕方無いものとする。いや、後埋めとか本気でやればできないこともないけど。
あれ?そもそもなんで1パスにする必要があるんだ?
あれ?それより以前にそもそもこんなものつくtt…