• Aucun résultat trouvé

pliquer le code de l’interpréteur quatre fois, mais juste à décrire les quatre deltas requis par les analyses.

bette factorisation correspond directement à une factorisation algébrique. ri I1est l’interpréteur d’origine et I2l’interpréteur mo-

diǤé, on note δ2 le delta qui transforme I1 en I2 (δ2 est une fonc-

tion). nn a I2 = δ2I1. roient deux autres interpréteurs : I3 et I4,

et leurs deltas respectifs δ3 et δ4. nn a I2 = δ2I1, I3 = δ3I1 et

I4= δ4I1. Alors,

hd est la fonction identité

(I1, I2, I3, I4) = (IdI1, δ2I2, δ3I3, δ4I4) = (Id, δ2, δ3, δ4)I1

Au lieu d’avoir quatre interpréteurs diǣérents, on peut juste conserver l’interpréteur de base et les quatre deltas. b’est une fac- torisation qui élimine la redondance de I1.

lais si intuitivement une telle factorisation semble possible, les questions naturelles qui suivent sont : comment réaliser cette facto- risation dans le code source ? oar quels moyens ? À quoi ressemble un interpréteur factorisé ? dt ultimement, comment factoriser mar- cissus ?

.

3uatre axes de factorisation pour l’instrumentation

dn observant les modiǤcations apportées par l’instrumentation de marcissus pour l’évaluation multi-facettes de plus près, on peut distinguer quatre catégories de changements : les imports/exports, l’ajout du paramètre program counter, le branchement pour éva- luer chaque facette, et les ajouts à l’objetglob6l.

oremière catégorie : imports/exports. ce nouvelles déǤnitions ont besoin d’être importées dans le module de l’interpréteur, et une nouvelle fonction est exportée. be sont de simples ajouts qui sont localisés en début et en Ǥn de Ǥchier respectivement. uoici comment ils se présentent dans le code :

ke symbole- marque une ligne sup- primée de l’interpréteur ; le symbole+ marque une ligne ajoutée par l’instru- mentation. k’absence de marque indique une ligne commune aux deux versions. // Imports + v6r F6cetedV6lue = Z6phod.f6cets.F6cetedV6lue; + v6r Progr6mCounter = Z6phod.f6cets.Progr6mCounter; ... // Exports - test: test + test: test, + getPC: getGC

motons au passage que iavarcript ne dispose pas de système de modules. hl n’y a pas de mots-clésimport comme en iava. oar convention, un simple objet iavarcript peut regrouper les valeurs et fonctions exportées par un module : c’est le cas dans lesExports

bernique Le problème : instrumenter et étendre des interpréteurs

ci-dessus. oour importer ces déǤnitions, il suǦt alors de faire réfé- rence à ces propriétés, c’est ce qu’on voit dans lesImports.

reconde catégorie : les changements eǣectués pour accommo- der le program counter utilisé par l’analyse. c’abord, le construc- teur de l’objetExecutionContextest étendu pour accepter un ar- gument supplémentaire : la valeur courante du program counter,

pc:

- function ExecutionContext(type, version) { + function ExecutionContext(type, pc, version { + this.pc = pc;

cans marcissus, lorsque l’interpréteur doit exécuter une fonc- tion, un appel àev6l, ou tout un script, il crée une instance de l’ob- jetExecutionContext. bet objet contient l’environnement lexical utilisé pour résoudre les noms de variables du code exécuté par ce contexte. k’objetExecutionContextest une réiǤcation du concept éponyme de la spéciǤcation dblArcript.

uoir la section 10 de la spéciǤcation zdbl99].

k’évaluation multi-facettes a besoin de suivre la valeur courante du program counter, c’est pourquoi cette valeur est sauvegardée dans l’objetExecutionContext.

be faisant, la signature du constructeur d’ExecutionContext

est étendue. sous ses appels doivent être modiǤés en conséquence pour fournir une valeur correcte pour le paramètre program coun- ter. hl y a plus de 80 instances de ce simple changement dans l’ins- trumentation. dn voici deux exemples :

- x = new ExecutionContext(MODULE_CODE);

+ x = new ExecutionContext(MODULE_CODE, x.pc); ...

- getV6lue(execute(n.children[ ], x)); + getV6lue(execute(n.children[ ], x), pc);

be changement est viral : il entraîne un ajout de paramètre non seulement sur les appels au constructeur d’ExecutionContext, mais sur d’autres fonctions, comme getV6lue, qui feront appel à ce constructeur, et ainsi de suite.

sroisième catégorie : les changements eǣectués dans l’exécu- tion de l’arbre syntaxique abstrait pour propager les étiquettes sur les valeurs à facettes. oar exemple, additionner deux valeurs à fa- cettes devrait produire une nouvelle valeur à facettes. cans l’im- plémentation, plutôt que d’eǣectuer une simple addition entre les deux opérandes, l’interpréteur doit d’abord inspecter l’opérande gauche, et si c’est une valeur à facettes, additionner la valeur de l’opérande droite à chacune de ces facettes. aien sûr, l’opérande droite peut également être une valeur à facettes, et il faut alors faire deux additions de ce côté aussi. k’interpréteur marcissus ne contient aucun code pour gérer l’addition de deux valeurs à fa-

. . 3uatre axes de factorisation pour l’instrumentation bigle

cettes, donc l’instrumentation doit ajouter la logique nécessaire pour distribuer l’addition sur chaque facette.

oour ce faire, chaque opération sujette à cette distribution est enrobée dans un appel à la fonctionev6lu6teE6ch. bette fonction teste si la valeur passée est une valeur à facettes, et eǣectue récursi- vement l’opération sur chaque facette si c’est le cas. Au total, 25 ap- pels àev6lu6teE6chont été ainsi ajoutés dans l’instrumentation. ke code suivant donne la forme générale de ces changements :

- v6r v = getV6lue(node.6)

+ ev6luteE6ch(getV6lue(node.6), function(v,x) { ... do something with v ...

+ }

cans marcissus non instrumenté, on récupère une valeur d’un nœud de l’arbre syntaxique (p.ex., l’opérande gauche d’une aǣec- tation, ou la condition d’un if) puis on fait quelque chose avec cette valeur. cans l’instrumentation, on récupère la même valeur, mais cette fois on distribue l’évaluation en appelantev6lu6teE6ch

avec cette valeur comme premier argument, et comme second une fonction qui opère sur une valeur simple (v), c’est-à-dire sans fa- cettes.

hl n’y a pas de sortie standard dans les programmes iavarcript exécutés par les navigateurs web. ka plupart des na- vigateurs en revanche disposent d’une console dans laquelle les programmes peuvent écrire viaconsole.log.

puatrième et dernière catégorie : les changements eǣectués sur l’environnement d’exécution de code client. cans un programme iavarcript, l’environnement d’exécution fournit un objet global qui contient les objets de base commeArr6y,M6th,StringetObject. ouisqu’il est métacirculaire, marcissus expose ces objets aux pro- grammes qu’il interprète en réutilisant ceux qui lui sont exposés par son environnement hôte. bertains de ces objets sont directe- ment exposés, et d’autres sont exposés via des mandataires (proxies). k’objet global exposé par marcissus est aussi enrichi par de nou- velles propriétés ; la fonctionprintpar exemple qui permet d’écrire sur la sortie standard.

ka construction de l’objetglob6ls’eǣectue en trois étapes. ore- mièrement, marcissus crée l’objet glob6lB6se pour contenir les nouvelles propriétés et surcharger certaines déǤnitions de l’envi- ronnement hôte. ceuxièmement, il crée l’objet global qui sera ex- posé au code interprété à partir de l’objet global de son environ- nement hôte, et y copie toutes les propriétés deglob6lB6se. sroi- sièmement, il réutilise les objets Arr6y,String et Function de l’environnement hôte via des mandataires, et les ajoute à l’objet global du code interprété.

k’instrumentation de l’évaluation multi-facettes enrichit l’objet

glob6lclient en ajoutant 50 propriétés àglob6lB6se, comme la suivante :

v6r glob6lB6se = { ...

birouche Le problème : instrumenter et étendre des interpréteurs

+ return (v inst6nceof F6cetedV6lue); + },

k’instrumentation change également la propriété String de

glob6lB6se pour capturer les valeurs à facettes passées en argu- ment au constructeur de chaînes de caractères.

dnǤn, il y a quelques modiǤcations qui n’appartiennent à au- cune de ces catégories : des refactorisations, et des corrections.

ke fait que la plupart des changements de l’instrumentation appartiennent à une de ces quatre catégories suggère des axes po- tentiels de factorisation. eactoriser permettrait de réduire la dupli- cation de code, et surtout de regrouper les changements de même nature en un seul endroit. oar exemple, si l’interpréteur disposait d’une fonction pour ajouter une valeur à l’objet global, tous les ajouts à cet objets requis par l’instrumentation pourraient être ef- fectués au même endroit :

6ddToGlob6l({

f6cetedV6lue: function(v) { ... }, ...

})

lais si on dispose de quatre axes de factorisation, une question demeure : comment utiliser ces axes pour eǣectivement factoriser marcissus ? bomment réaliser cette factorisation ?