Ce n'est pas vrai qu'il y a des restrictions qui vous empêchent d'appeler eval qui ne seraient pas détectés par analyse statique: c'est juste que de telles références à d'eval exécuter dans la portée globale. Notez que ce est un changement dans l'ES5 de ES3 où directe et indirecte, les références à eval à la fois couru dans la portée locale, et en tant que tel, je ne suis pas sûr que rien ne fait la moindre optimisation basée sur ce fait.
Une façon évidente de tester cela est de faire BigObject vraiment être un gros objet, et la force d'une table, après l'exécution de f0–f2. (Parce que, eh bien, autant que je pense connaître la réponse, le test est toujours mieux!)
Alors...
Le test
var closure;
function BigObject() {
var a = '';
for (var i = 0; i <= 0xFFFF; i++) a += String.fromCharCode(i);
return new String(a); // Turn this into an actual object
}
function f0() {
var x = new BigObject();
var y = 0;
closure = function(){ return 7; };
}
function f1() {
var x = new BigObject();
closure = (function(y) { return function(){return y++;}; })(0);
}
function f2() {
var x = new BigObject();
var y = 0;
closure = function(){ return y++; };
}
function f3() {
var x = new BigObject();
var y = 0;
closure = eval("(function(){ return 7; })"); // direct eval
}
function f4() {
var x = new BigObject();
var y = 0;
closure = (1,eval)("(function(){ return 7; })"); // indirect eval (evaluates in global scope)
}
function f5() {
var x = new BigObject();
var y = 0;
closure = (function(){ return eval("(function(){ return 7; })"); })();
}
function f6() {
var x = new BigObject();
var y = 0;
closure = function(){ return eval("(function(){ return 7; })"); };
}
function f7() {
var x = new BigObject();
var y = 0;
closure = (function(){ return (1,eval)("(function(){ return 7; })"); })();
}
function f8() {
var x = new BigObject();
var y = 0;
closure = function(){ return (1,eval)("(function(){ return 7; })"); };
}
function f9() {
var x = new BigObject();
var y = 0;
closure = new Function("return 7;"); // creates function in global scope
}
J'ai ajouté des tests pour eval/Fonction, semblant ce sont aussi des cas intéressants. La différence entre f5/f6 est intéressant, parce que f5 est vraiment juste identique à f3, compte tenu de ce qu'est réellement une fonction identique à la fermeture; f6 contente de quelque chose qui, une fois évaluées donne, et que l'évaluation n'a pas encore été évalué, le compilateur ne peut pas savoir qu'il n'y est fait aucune référence à x en son sein.
SpiderMonkey
js> gc();
"before 73728, after 69632, break 01d91000\n"
js> f0();
js> gc();
"before 6455296, after 73728, break 01d91000\n"
js> f1();
js> gc();
"before 6455296, after 77824, break 01d91000\n"
js> f2();
js> gc();
"before 6455296, after 77824, break 01d91000\n"
js> f3();
js> gc();
"before 6455296, after 6455296, break 01db1000\n"
js> f4();
js> gc();
"before 12828672, after 73728, break 01da2000\n"
js> f5();
js> gc();
"before 6455296, after 6455296, break 01da2000\n"
js> f6();
js> gc();
"before 12828672, after 6467584, break 01da2000\n"
js> f7();
js> gc();
"before 12828672, after 73728, break 01da2000\n"
js> f8();
js> gc();
"before 6455296, after 73728, break 01da2000\n"
js> f9();
js> gc();
"before 6455296, after 73728, break 01da2000\n"
SpiderMonkey semble GC "x" sur tout, sauf sur f3, f5 et f6.
Il semble pour autant que possible (c'est à dire, lorsque c'est possible, y, ainsi que x) sauf s'il est direct eval appel dans le champ d'application de la chaîne d'une fonction qui existe toujours. (Même si cette fonction de l'objet lui-même a été GC avais et n'existe plus, comme c'est le cas en f5, ce qui signifie théoriquement qu'il pourrait GC x/y.)
V8
gsnedders@dolores:~$ v8 --expose-gc --trace_gc --shell foo.js
V8 version 3.0.7
> gc();
Mark-sweep 0.8 -> 0.7 MB, 1 ms.
> f0();
Scavenge 1.7 -> 1.7 MB, 2 ms.
Scavenge 2.4 -> 2.4 MB, 2 ms.
Scavenge 3.9 -> 3.9 MB, 4 ms.
> gc();
Mark-sweep 5.2 -> 0.7 MB, 3 ms.
> f1();
Scavenge 4.7 -> 4.7 MB, 9 ms.
> gc();
Mark-sweep 5.2 -> 0.7 MB, 3 ms.
> f2();
Scavenge 4.8 -> 4.8 MB, 6 ms.
> gc();
Mark-sweep 5.3 -> 0.8 MB, 3 ms.
> f3();
> gc();
Mark-sweep 5.3 -> 5.2 MB, 17 ms.
> f4();
> gc();
Mark-sweep 9.7 -> 0.7 MB, 5 ms.
> f5();
> gc();
Mark-sweep 5.3 -> 5.2 MB, 12 ms.
> f6();
> gc();
Mark-sweep 9.7 -> 5.2 MB, 14 ms.
> f7();
> gc();
Mark-sweep 9.7 -> 0.7 MB, 5 ms.
> f8();
> gc();
Mark-sweep 5.2 -> 0.7 MB, 2 ms.
> f9();
> gc();
Mark-sweep 5.2 -> 0.7 MB, 2 ms.
V8 semble GC x sur tout, en dehors de f3, f5 et f6. C'est identique à SpiderMonkey, voir l'analyse ci-dessus. (À noter toutefois que les chiffres ne sont pas assez détaillées pour dire si y est GC avais quand x n'est pas, j'ai pas pris la peine d'enquêter sur ce point.)
Carakan
Je ne vais pas la peine de l'exécution de cette à nouveau, mais inutile de dire que le comportement est identique à SpiderMonkey et V8. Plus difficile à tester sans JS shell, mais c'est faisable avec le temps.
JSC (Nitro) et de Chakra
La construction du CDC est une douleur sur Linux, et le Chakra ne fonctionne pas sur Linux. Je crois que CDC a le même comportement pour les moteurs ci-dessus, et je serais surpris si Chakra n'était pas trop. (Faire quelque chose de mieux devient vite très complexe, en faisant quelque chose de pire, eh bien, vous seriez presque jamais faire de GC et ont de graves problèmes de mémoire...)