Appeler la taille de la stack dans JavaScript

Je veux tester de grandes stacks d’appels. Plus précisément, je veux un avertissement de la console lorsque la longueur de la stack des appels atteint 1 000. Cela signifie généralement que j’ai fait quelque chose de stupide et peut conduire à des bogues subtils.

Puis-je calculer la longueur de la stack d’appels dans JavaScript?

Voici une fonction qui fonctionnera dans tous les principaux navigateurs, même si elle ne fonctionnera pas en mode ssortingct ECMAScript 5 car arguments.callee et l’ caller ont été supprimés en mode ssortingct.

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

Exemple:

 function f() { g(); } function g() { h(); } function h() { alert(getCallStackSize()); } f(); // Alerts 3 

MISE À JOUR 1 novembre 2011

En mode ssortingct ES5, il est tout simplement impossible de naviguer dans la stack d’appels . La seule option qui rest est d’parsingr la chaîne renvoyée par la new Error().stack est non standard, pas prise en charge de manière universelle et qui est évidemment problématique, et même cela peut ne pas être possible pour toujours .

MISE À JOUR 13 août 2013

Cette méthode est également limitée par le fait qu’une fonction appelée plus d’une fois dans une seule stack d’appels (par exemple, par récursivité) lancera getCallStackSize() dans une boucle infinie (comme indiqué par @Romomblue dans les commentaires). Une version améliorée de getCallStackSize() trouve ci-dessous: elle garde la trace des fonctions qu’elle a vues auparavant pour éviter d’entrer dans une boucle infinie. Toutefois, la valeur renvoyée est le nombre d’objects de fonction différents dans le bloc d’appel avant de rencontrer une répétition plutôt que la taille réelle de la stack d’appels complète. C’est le mieux que vous puissiez faire, malheureusement.

 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; } 

Vous pouvez utiliser ce module: https://github.com/stacktracejs/stacktrace.js

L’appel de printStackTrace renvoie la trace de la stack dans un tableau, puis vous pouvez vérifier sa longueur:

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

Une approche différente consiste à mesurer la taille disponible sur la stack dans le cadre de la stack de niveau supérieur, puis à déterminer l’espace utilisé sur la stack en observant la quantité d’espace disponible. Dans du code:

 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; } 

Par exemple:

 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(); 

Il y a bien sûr deux inconvénients majeurs à cette approche:

(1) la détermination de l’espace de stack épuisé est une opération potentiellement coûteuse lorsque la VM a une grande taille de stack et

(2) les nombres rapportés ne sont pas nécessairement le nombre de récurrences, mais sont plutôt une mesure de l'espace réel utilisé sur la stack (cela peut aussi être un avantage). J'ai vu du code généré automatiquement qui contient des fonctions qui utilisent le même espace sur la stack par récursivité que les récursions 2000 de la fonction stackSizeExplorer ci-dessus.

Note: J'ai seulement testé le code ci-dessus avec node.js. Mais je suppose que cela fonctionnerait avec toutes les machines virtuelles qui utilisent une taille de stack statique.