2011-09-18 5 views
40

大きなコールスタックをテストしたい。具体的には、コールスタックの長さが1000に達したときにコンソール警告が必要です。これは通常、何かばかげたことを意味し、微妙なバグにつながる可能性があります。JavaScript内のコールスタックサイズ

JavaScript内で呼び出しスタックの長さを計算することはできますか?

+2

[this](http://eriwen.com/javascript/js-stack-trace/)は役に立ちますか? –

+0

コードDave Newtonは、例外をスローするように指示し、ブラウザに基づいてそのプロパティを調べ、 'e'としてキャッチし、そのプロパティを調べます。 ChromeとMozillaでは、 'e.stack'を使用し、Opera 10+は' e.stacktrace'を使用し、他の人は 'e.message'プロパティを理解しようとします。 –

+0

エラースタックトレースでは最大10個のスタックエントリしか使用できませんか? http://jsfiddle.net/pimvdb/AuyP7/ – pimvdb

答えて

45

厳密なモードでarguments.calleecallerが削除されているため、ECMAScript 5 strictモードでは機能しませんが、すべての主要ブラウザで機能する関数です。

function getCallStackSize() { 
    var count = 0, fn = arguments.callee; 
    while ((fn = fn.caller)) { 
     count++; 
    } 
    return count; 
} 

例:ES5厳密モードで

function f() { g(); }  
function g() { h(); }  
function h() { alert(getCallStackSize()); }  

f(); // Alerts 3 

UPDATE 2011年11月1日

no way to navigate the call stackが単に存在します。残りの唯一のオプションは、new Error().stackによって返された文字列を解析することです。これは標準ではなく、普遍的にはサポートされておらず、明らかに問題があります。さらにmay not be possible for everです。

UPDATE 2013年8月13日

この方法は、(再帰などを介して)、単一のコールスタック内で複数回呼び出される関数のように(無限ループにgetCallStackSize()をスローするという事実によって制限されていますコメントで@Randomblueによって指摘されている)。 getCallStackSize()の改良されたバージョンは以下の通りです。無限ループに陥るのを防ぐために以前に見た機能を追跡します。ただし、返される値は、完全な呼び出しスタックの実際のサイズではなく、繰り返しが発生する前の呼び出しスタック内のさまざまな関数オブジェクトの数です。残念ながらこれはあなたができる最高のものです。

var arrayContains = Array.prototype.indexOf ? 
    function(arr, val) { 
     return arr.indexOf(val) > -1; 
    } : 
    function(arr, val) { 
     for (var i = 0, len = arr.length; i < len; ++i) { 
      if (arr[i] === val) { 
       return true; 
      } 
     } 
     return false; 
    }; 

function getCallStackSize() { 
    var count = 0, fn = arguments.callee, functionsSeen = [fn]; 

    while ((fn = fn.caller) && !arrayContains(functionsSeen, fn)) { 
     functionsSeen.push(fn); 
     count++; 
    } 

    return count; 
} 
+9

+1いいねえ。この問題が発生した場合は、Chromeの開発ツールで「6」と警告しますが、これは、コンソールを使用しているときにバックグラウンドで実行されている他の3つの機能があるためです。 – pimvdb

+0

いいですね!だから、ES5の厳格なモードでは、彼らはコールスタックがあまりにも危険だと決めましたか?おそらく、これはコールスタックのデフォルト動作の変更を防ぐためだけです。 –

+0

@Fred arguments.caller/arguments.calleeは、関数のインライン化とテールコールの最適化(ES.nextでは必須)を実行するときに楽しくなります。 – gsnedders

1

あなたは、このモジュールを使用することができます。 のprintStackTraceを呼び出すhttps://github.com/stacktracejs/stacktrace.js

は、あなたがその長さを確認することができ、配列内のスタックトレースを返します。

var trace = printStackTrace(); 
console.log(trace.length()); 
1

異なるアプローチが利用可能に測定しています最大スタック・フレーム内のスタック上のサイズを計算し、使用可能なスペースがどれくらい少ないかを観察してスタック上の使用スペースを決定します。コードで:

例えば
function getRemainingStackSize() 
{ 
    var i = 0; 
    function stackSizeExplorer() { 
     i++; 
     stackSizeExplorer(); 
    } 

    try { 
     stackSizeExplorer(); 
    } catch (e) { 
     return i; 
    } 
} 

var baselineRemStackSize = getRemainingStackSize(); 
var largestSeenStackSize = 0; 

function getStackSize() 
{ 
    var sz = baselineRemStackSize - getRemainingStackSize(); 
    if (largestSeenStackSize < sz) 
     largestSeenStackSize = sz; 
    return sz; 
} 

function ackermann(m, n) 
{ 
    if (m == 0) { 
     console.log("Stack Size: " + getStackSize()); 
     return n + 1; 
    } 

    if (n == 0) 
     return ackermann(m - 1, 1); 

    return ackermann(m - 1, ackermann(m, n-1)); 
} 

function main() 
{ 
    var m, n; 

    for (var m = 0; m < 4; m++) 
    for (var n = 0; n < 5; n++) 
     console.log("A(" + m + ", " + n + ") = " + ackermann(m, n)); 
    console.log("Deepest recursion: " + largestSeenStackSize + " (" + 
      (baselineRemStackSize-largestSeenStackSize) + " left)"); 
} 

main(); 

このアプローチには2つの主要な欠点もちろんあります

(1)まで使用スタック領域を決定することは、潜在的に高価な操作でありますVMが大きなスタックサイズを持ち、

(2)報告された数値は必ずしも再帰回数ではなく、実際に使用された領域の測定値です(もちろん、これは利点になるかもしれません)。私は上記のstackSizeExplorer関数の2000再帰として再帰的にスタック上の同じスペースを使用する関数を含む自動生成コードを見てきました。

注:上記のコードはnode.jsでのみテストされています。しかし、私はそれが静的なスタックサイズを使用するすべてのVMで動作すると仮定します。

関連する問題