On 2011-11-16, at 5:55 PM, Erick Lavoie wrote:
Dans ECMAScript 5th edition (December 2009), le comportement d'un constructeur est le suivant:
[...]
- Let result be the result of calling the [[Call]] internal property of F, providing obj as the this value and providing the argument list passed into [[Construct]] as args.
- If Type(result) is Object then return result.
- Return obj.
contrairement à ce que Marc m'a dit ce midi (si le résultat est 'undefined' on retourne l'objet construit avant l'appel de fonction, sinon on retourne le résultat). J'imagine que Marc faisait référence à une version antérieure du standard.
En effet. Donc si la fonction retourne un objet, alors le new va retourner cet objet, sinon c'est l'objet qui a été passé comme "this" au constructeur. Au moins on sait que le new va retourner un objet!
La meilleure façon de compiler ça je crois c'est d'avoir 2 versions de chaque fonction. Une qui est utilisée par les appels normaux, et l'autre par l'évaluation d'un "new". Techniquement ça peut se faire avec 2 points d'entrée. La version "new" pourrait inclure:
1 - allocation de l'objet vide et assignation à "this" 2 - insertion d'un "return this" à la fin de la fonction 3 - remplacer les "return E" par "return this" si on sait que E n'est pas un objet (grâce à une analyse statique), sinon insérer un test dynamique
En pratique il y aura donc très peu de cas où un test dynamique est nécessaire, car on voit rarement un "return" dans les constructeurs. De plus, grace à une analyse simple, on peut savoir dans bien des cas quelle taille sera l'objet à la fin du constructeur, et donc la version "new" peut allouer l'objet de la bonne taille, évitant ainsi de l'étendre à chaque affectation de propriété dans le constructeur.
Faire toujours 2 versions de chaque fonction serait couteux (car on ne peut pas facilement identifier tous les constructeurs). On peut résoudre ce problème comme ceci:
1) Une analyse statique du code peut nous donner l'ensemble de fonctions qui sont clairement des constructeurs. Pour ces fonctions on compile les 2 versions.
2) Pour les autres fonctions, on peut soit avoir au point d'entrée de la version "new" un stub qui :
- fait appel au point d'entrée de la version "non-new", avec un test dynamique du résultat.
- avoir un appel au compilateur pour compiler la version "new" et appeler la version "new" (donc une compilation paresseuse de la version "new")
Cette approche pourrait être utilisée pour l'analyse des programmes. C'est à dire qu'on distinguerais la version "new" et "non-new" pour avoir plus de précision dans l'analyse.
Marc