• Aucun résultat trouvé

Extension de débogage pour l’environnement Rhino

un langage de haut niveau assez « simple » et aux caractéristiques assez similaires aux lan- gages fonctionnels à typage dynamique comme Scheme : il propose des fonctions anonymes, la capture de variable et dispose d’un évaluateur. Ce langage fournit un système d’objets à prototypes reposant sur les fonctions.

Rhino est une implantation du langage ECMAScript entièrement écrit en Java et qui peut être facilement interfacé avec Java. Développé à l’origine pour être un simple interprète d’ECMAScript, l’environnement Rhino a évolué pour fournir un compilateur ECMAScript vers code-octet JVM.

Cette section présente le support de débogage réalisé pour le compilateur Rhino. Elle décrit tout d’abord les règles de remplacements mise au point pour masquer les détails de compilation qui apparaissent dans la pile, puis elle présente la manière d’afficher les

CHAPITRE 7. EXPÉRIMENTATIONS SUR D’AUTRES LANGAGES variables et les objets du programme.

7.1.1 Prise en charge de la pile d’exécution

Le compilateur Rhino emploie un schéma de compilation qui présente des similitudes avec celui de Bigloo : il produit directement du code-octet JVM à partir de fichiers source ECMAScript. Chaque fichier source est compilé en une classe JVM. Les fonctions utilisa- teurs sont compilées en des fonctions statiques JVM. Le compilateur produit des tables de débogage standards dans les classes JVM pour conserver les informations de lignes. Cela permet par exemple à des débogueurs classiques d’opérer une exécution pas-à-pas correcte dans le code utilisateur.

Le langage ECMAScript offre des fonctions d’ordre supérieur et des fonctions anonymes. Le compilateur Rhino doit donc produire des fonctions intermédiaires pour implanter ces fonctionnalités dans la JVM. De la même manière que Bigloo, il a recours à un objet spécial nommé NativeFunction pour représenter les fermetures et utilise une fonction de dispersion

(cf chapitre6.2.2) pour effectuer les appels. Toutefois, contrairement à Bigloo, du fait de la

nature des appels de fonctions dans ECMAScript 1, le compilateur Rhino n’utilise pas les

variables locales natives de la JVM pour représenter les arguments des fonctions utilisateur ou leurs variables locales. Au lieu de cela, il s’appuie sur une gestion de son propre type de bloc d’activation ad-hoc durant l’exécution.

(bugloo) (info args)

#0 _c3(test, Context, Scriptable, Scriptable, Object[]) in file test.js:10 #1 call(Context, Scriptable, Scriptable, Object[]) in file test.js

#2 callName(Object[], String, Context, Scriptable) in file OptRuntime.java

(a) Motif de code produit par le compilateur

(100

((["^_c[0-9]+$"] ((? any) . (? any)) "java.lang.Object" (\ 1)) ("call" (\ 2) "java.lang.Object" (\ 1))

(? (["^call.*"] any any "org.mozilla.javascript.optimizer.OptRuntime"))) sf-rhino-function)

(b) Règle de remplacement masquant le motif précédent

Fig. 7.1: L’appel par nom dans la plateforme d’exécution Rhino

La figure 7.1(a) présente le motif de code produit par le compilateur Rhino pour un

appel de fonction provenant d’un fichier test.js. L’implantation de l’appel réside dans une fonction de bibliothèque appelée callName (bloc 2). Le premier argument est un tableau contenant tous les arguments de l’appel ECMAScript utilisateur. Le second argument est le nom de la fonction utilisateur à appeler. Dans la terminologie ECMAScript, le troisième ar- gument est le contexte d’évaluation courant, il représente le bloc d’activation Rhino courant. Le dernier argument représente la chaîne de portée courante, c’est-à-dire l’ensemble des va- riables locales accessibles à cet endroit du programme. Le rôle de la fonction de bibliothèque est de rechercher dans la liste des fonctions définies à l’exécution l’objet NativeFunction adéquat et d’appeler sa fonction de dispersion (bloc 1). Dans la fonction de dispersion, le

1

Notamment la sémantique des fonctions d’arité variable.

7.1. EXTENSION DE DÉBOGAGE POUR L’ENVIRONNEMENT RHINO troisième argument représente le l’activation courante, un équivalent du pointeur this en ECMAScript.

Lorsque la fonction de dispersion appelle la fonction utilisateur correspondante, cette fonction reçoit en paramètre l’objet NativeFunction ainsi que tous les arguments reçus par la fonction de dispersion. Une fonction utilisateur compilée par Rhino est appelée _cn, selon son ordre d’apparition dans le fichier source. La règle de remplacement présentée dans la

figure 7.1(b) permet de construire un bloc d’activation virtuel masquant ce motif de code

synthétique. Le motif de bloc vérifie le nom de la fonction utilisateur et mémorise le type de son premier argument, ainsi que la liste des arguments restant. Puis, il s’assure que la fonction suivante s’appelle call, qu’elle attend les bons arguments et qu’elle est définie dans la même classe que la fonction précédente. Enfin le filtre vérifie que la troisième fonction du motif représente bien l’implantation d’un appel de fonction.

7.1.2 L’inspection des variables locales

Le langage ECMAScript fournit différents niveaux de variables locales, comme par exemple les variables capturées ou les variables accédées à l’aide du mot-clef with. Ces niveaux sont appelées portées des identificateurs. En ECMAScript, chaque portée est im- plantée par une sorte de table de hachage associant des variables à des valeurs. De plus, le programme peut modifier le contenu des ces portées dynamiquement, par exemple en ajoutant ou en retirant des variables locales.

Il existe différents types de portées. Par exemple, les objets structurés du langage sont des tables de hachage dans lesquelles on peut ajouter ou retrancher des champs. Dans une fonction, le mot-clef with permet d’évaluer du code en utilisant comme portée locale les champs d’un objet. Par ailleurs, en ECMAScript, les objets sont des prototypes. Les fonctions peuvent être « instanciées » afin de créer des objets dont les champs sont les variables locales. Lorsqu’un objet est instancié, il devient lui-même un nouveau prototype : ses champs peuvent être modifiés sans modifier le prototype de l’objet original. En revanche, si un champ est rajouté au prototype initial, tous les objets créés à partir de ce prototype profiteront automatiquement du champ supplémentaire. Le prototype définit donc un niveau particulier de portée. 1 function pt1d(x) { 2 this.x=x; 3 this.inv=function() { 4 var i=-x; 5 return i; 6 } 7 } 8 pt.prototype.color=’red’; 9 10 a=new pt1d(10); 11 print a.inv();

(a) Programme ECMAScript

x inv a i -10 10 col ’red’ portée globale portée de pt1d portée de inv portée du prototype Contexte Global Contexte de inv

(b) Niveaux lexicaux accessibles depuis inv

Fig. 7.2: Les différentes portées lexicales d’un programme ECMAScript

CHAPITRE 7. EXPÉRIMENTATIONS SUR D’AUTRES LANGAGES

veaux de portée. Ainsi, lorsque l’utilisateur demande la liste des variables locales d’un bloc d’activation virtuel Rhino, le débogueur doit afficher toutes les variables définies dans les

portées accessibles. La figure7.2(a) illustre ce cas de figure. Le programme présenté crée un

objet pt1d contenant un champ et une fonction. Puis il rajoute à son prototype un nouveau champ. Lorsque l’exécution est suspendue à la ligne 5, la liste des portées accessibles est

présentée dans la figure7.2(b).

Pour retrouver la liste des variables locales, il faut accéder à la portée de la fonction cou- rante. Pour que l’inspection fonctionne même sans information de débogage, le débogueur n’utilise pas les arguments de la fonction _cn. Il retrouve la portée courante en interro- geant l’objet NativeFunction obtenu par le pointeur this de la fonction call (bloc 1 de

la figure7.1(a)).

Toutes les valeurs Rhino héritent de la classe primaire Scriptable. Pour leur affichage, l’extension de débogage Rhino appelle dans la JVM du débogué la fonction Rhino toString qui s’occupe du formatage des valeurs.

7.1.3 Conclusion de la prise en charge du langage

Le compilateur Rhino produit des motifs de code en utilisant les mêmes techniques (dispersion et captures de variables) que le compilateur Bigloo. Toutefois, l’implantation est différentes : le compilateur ne cherche pas à produire des noms de fonctions « intelli- gibles » et doit produire beaucoup plus de structures intermédiaires du fait de la nature fortement dynamique du langage. Malgré cela, les règles de remplacement peuvent masquer ces artefacts sans modifier le compilateur Rhino.

7.2 Extension de débogage pour l’environnement Jython