OK、問題を理解しました。私のコードには本当に再帰はありませんでした。たとえば<array>.splice(...)
のような「varargs」関数である場合、何百もの引数を使用して JavaScript 関数を呼び出すことが実際に可能です。これは私の違反者でした。
余談ですが、 GWTSystem.arraycopy(...)
は、多かれ少なかれ巧妙な方法で JavaScript スプライス関数を使用して Java 関数を実装しています。
splice は、ターゲット配列に挿入する任意の数の入力要素を受け入れます。次の構成を使用して、これらの入力要素を別の配列から渡すことができます。
var arguments = [index, howmany].concat(elements);
Arrays.prototype.splice.apply(targetarray, arguments);
これは、次の呼び出しと同等です。
targetarray.splice(index, howmany, elements[0], elements[1], elements[2], ...);
要素が大きくなると(さまざまなブラウザでの「大きい」の意味については以下を参照してください)、その内容が関数呼び出しのスタックに読み込まれるため、再帰なしで「最大呼び出しスタック サイズを超えました」というエラーが発生する可能性があります。
この問題を示す短いスクリプトを次に示します。
var elements = new Array();
for (i=0; i<126000; i++) elements[i] = 1;
try {
var arguments = [0, 0].concat(elements);
Array.prototype.splice.apply(elements, arguments);
alert("OK");
} catch (err) {
alert(err.message);
}
このスクリプトを使用すると、「大きな」とは次のことを意味します。
- Chrome 19: 要素には ~ 125,000 の数字が含まれます
- Safari 5.1 (Windows 上): 要素には最大 65,000 個の数字が含まれます
- Firefox 12: 要素には ~ 500,000 の数字が含まれます
- Opera 11.61: 要素には ~ 1,000,000 の数字が含まれます
そして勝者は、変更のための Internet Explorer 8 です! この関数呼び出しが失敗する前に、すべてのシステム メモリを使い果たす可能性があります。
補足: Firefox と Opera は実際には別の (より便利な) エラー メッセージをスローします: Function.prototype.apply: argArray is too large