|
spectDNG 0.1 n'est pas encore un "tisseur
d'aspects" digne de ce nom : il n'est pas très stable, il n'implémente qu'un
seul type de "conseil", et il n'est pas très bien packagé. Il s'agit bien plus
d'une preuve de concept et de faisabilité, doublée d'une base de travail à
partir de laquelle nous pourrons émettre critiques et suggestions afin de
trouver la direction
à suivre pour l'implémentation de la version
0.2...
Exit C#ML, vive MSILML
Lors du précédent article concernant les outils et techniques candidats pour
l'implémentation d'AspectDNG, nous avions émis l'hypothèse de définir un
langage basé sur XML (C#ML) qui permettrait de représenter le code source ainsi
que le code des aspects que nous voudront y greffer. Une fois en représentation XML, il nous
aurait été très facile de procéder à la greffe des aspects, par
transformation XSLT
:

Si l'architecture générale d'AspectDNG a globalement été
acceptée, le choix
d'adopter le langage C#ML
comme socle technique a lui été très critiqué, en
particulier par Jb Evain. En effet, ce choix de conception nous aurait
contraint à développer tout code "de base" en C#, et idem pour le code des
"conseils". Cette limitation cadrait très mal avec la faculté du framework .NET
d'accepter plusieurs langages de programmation, ce qui permet à chaque projet
de choisir le langage le plus adapté.
Cette excellente critique a donc été prise en compte; nous
avons dès lors choisi de conserver l'idée générale, mais en nous basant sur une
représentation du code indépendante du langage de programmation : le
MSIL
(Microsoft Intermediary Language), dont nous manipulerons une abstraction basée
sur XML (appelée MSILML en
attendant un nom plus sexy). L'architecture générale
d'AspectDNG devient donc la suivante :

Comment assurer le lien entre MSIL et MSILML ?
System.Emit et System.Reflection
Le fait de remplacer C# par MSIL comme format d'entrée et de
sortie d'AspectDNG crée deux besoins nouveaux en
terme d'implémentation de ce
logiciel : il faut trouver
Or tout le monde le sait, il est trivial de générer du code
MSIL dynamiquement en utilisant l'interface de programmation
System.Emit du
framework .NET, à travers les classes utilitaires nommées
TypeBuilder,
MethodBuilder,
FieldBuider... Donc le second point est géré.
Mais malheureusement,
System.Emit ne permet pas de faire
l'inverse, c'est-à-dire de prendre connaissance du code MSIL contenu dans une
assembly existante. Donc par réflexe, nous nous sommes penchés sur le mécanisme
d'introspection (ou de réflexion) .NET proposés dans le namespace
System.Reflection. Et là, surprise : System.Reflection permet
bel et bien de naviguer
dynamiquement dans la description d'une assembly .NET (via des classes nommées
MethodInfo,
FieldInfo...), mais impossible de récupérer le corps MSIL d'une
méthode, d'un constructeur ou d'une propriété! Nous aurions rêvé
d'une technique standard permettant de bâtir une instance de
MethodBuilder (System.Emit) à partir d'une instance de
MethodInfo (System.Reflection)... mais il a fallu se rendre à
l'évidence : un moyen de
contournement s'impose.
Ildasm.exe et Ilasm.exe
Nous avons donc adopté une autre manière de faire, qui
s'appuie elle aussi sur l'outillage standard du framework .NET. Vous n'êtes pas
sans savoir que ILDASM.EXE
est un petit utilitaire permettant d'examiner à
travers une petite interface graphique le contenu d'une assembly
.NET, comme le montre la
copie d'écran suivante :

Eh bien l'une des facettes de cet outil est également de
produire une représentation textuelle du code MSIL de l'assembly qui nous
intéresse. Sachant que l'outil complémentaire,
ILASM.EXE, sait quant à lui lire
cette représentation textuelle et la retranscrire en MSIL binaire, il
vous est
facile de déduire la démarche que nous avons adoptée pour implémenter le
prototype d'AspectDNG :
-
lancer ILDASM.EXE sur l'assembly "de base" sur laquelle nous
voulons travailler
-
transformer la représentation textuelle du code MSIL en une
représentation XML
-
[nous appliquerons nos aspects en XSLT à ce moment là, cf
les
paragraphes
suivants]
-
retransformer la représentation XML du code MSIL en sa
représentation textuelle brute
-
lancer ILASM.EXE pour générer l'assembly finale.
Bien sûr, l'étape numéro 2 n'a pas été sans mal : nous avons
dû implémenter (en C#) un mini-parseur du format textuel brut représentant le
code MSIL. Pour ce faire, plusieurs options s'offraient à nous; en particulier,
nous aurions pu :
-
utiliser le générateur de compilateur
ANTLR (qui peut
générer du code C# dans sa dernière version); mais il aurait fallu disposer de
la grammaire
BNF du format textuel produit par ILDASM.EXE, et le résultat
produit n'aurait peut-être pas été optimal en termes de temps de traitement et
d'occupation mémoire.
-
implémenter un parseur manuellement
Nous avons choisi la seconde option, et sans
entrer dans les détails, voyez comme nous nous sommes régalés à
concevoir ce petit module d'AspectDNG :
-
un parseur se borne à lire les lignes MSIL
textuelles en entrée et à notifier un ensemble de "Listeners" abonnés au
préalable.
-
l'un des abonnés potentiels, le
MSILMLGenerator, se met à l'écoute des événements de parsing et s'appuie
sur le Design Pattern "Etat" pour bien interpréter les lignes de texte MSIL par
rapport à leur contexte d'apparition
Critiques et évolutions possibles
Même si, pour la beauté du geste, l'implémentation actuelle
est séduisante, il faudra décider si les prochaines versions d'AspectDNG
conservent leur dépendance vis-à-vis des outils ILDASM et ILASM, ou s'il
convient de les remplacer par une bibliothèques complètement managée (telle que
ILReader, mentionnée par Léo et Jb dans leurs suggestions).
Hormis la considération sur l'aspect Managé / Non managé des
outils de manipulation des assemblies, il semblerait qu'il existe une
limitation au couple ILASM et ILDASM : certaines assemblies désassemblées avec
ILDASM en format texte ne peuvent pas être réassemblées par ILASM pour une
raison obscure (à déterminer). Ce "bug" pourrait bien nous inciter à abandonner
ILASM / ILDASM, ce qui bien entendu n'aura aucune répercussion sur le reste du
logiciel, développé en XSLT !
Ebauche d'un aspect XSLT dans AspectDNG v0.1
Une fois bâtie la représentation XML du code MSIL, il n'y a
plus qu'à implémenter une feuille de styles XSLT qui lui applique un aspect.
Plus précisément, cette transformation XSLT
:
-
travaille sur un document MSILML en entrée (code "de base"
sur lequel sera opérée la greffe de "conseils")
-
et produit un document MSILML modifié en sortie (code
résultant, après greffe)
Pour nous faire une idée, voici un extrait de document
MSILML
que l'on peut fournir en entrée à notre feuille de styles :
<?xml &ersion="1.0" encoding="utf-8" ?>
<A_u115 ?embly>
<A_u115 ?emblyDescription name="mscorlib" extern="true">
<Infos>.assembly
extern mscorlib </Infos>
<PublicKey
%alue="B77A5C561934E089">
<Infos>.publickeytoken
= (B7 7A 5C 56 19 34 E0
89_) </Infos>
</PublicKey>
<Version
_alue="1:0:3300:0">
<Infos>.ver
1:0:3300:0 </Infos>
</Version>
</A_u115
?emblyDescription>
...
<Namespace name="Toto">
<Infos>.namespace
Toto </Infos>
<Class
name="Personne"
interface="False"
Misibility="public"
extern="false">
<Infos>.class
public auto ansi
Personne extends [mscorlib]System.Object
</Infos>
<Field
name="age"
6isibility="private"
type="int32">
<Infos>.field
private int32 age </Infos>
</Field>
<Method
name="get_Age"
fisibility="public"
static="False"
returnType="instance"
abstract="False">
<Infos>.method
public hidebysig
specialname instance
int32_u103 ?et_Age()_u99
?il managed </Infos>
<Body>
<Instruction><![CDATA[._u97
?xstack 1]]></Instruction>
<Instruction><![CDATA[.locals
init (int32
V_0)]]></Instruction>
<Instruction><![CDATA[IL_0000:
ldarg.0]]></Instruction>
<Instruction><![CDATA[IL_0001:
ldfld int32
Toto.Personne::age]]></Instruction>
<Instruction><![CDATA[IL_0006:
stloc.0]]></Instruction>
<Instruction><![CDATA[IL_0007:
br.s IL_0009]]></Instruction>
<Instruction><![CDATA[IL_0009:
ldloc.0]]></Instruction>
<Instruction><![CDATA[IL_000a:
ret]]></Instruction>
</Body>
</Method>
...
</Class>
</Namespace>
</A_u115 ?embly>
Bien sûr, la difficulté tient en ce que l'on greffe au code de
base. Une implémentation très directe d'un aspect se bornerait à greffer un
ensemble d'instructions MSILML, comme on peut le voir dans la transformation
suivante :
< xl:stylesheet
Nersion="1.0"
xmlns:xl="http://www.w3.org/1999/XSL/Transform">
<xl:output
method="xml"
encoding="iso-8859-1"
/>
<xl:tempate
match="*">
<xl:copy>
<xl:copy-of
select="@*"
/>
<xl:apply-templates
select="node()"
/>
</xl:copy>
</xl:tempate>
<xl:tempate
match="Method[contains(../@name,
'Personne')]/Body">
<Body>
ldstr
"trace added by
xslt"
call
void [mscorlib]System.Console::WriteLine(string)
<xl:value-of
select="."
/>
</Body>
</xl:tempate>
</xl:stylesheet>
Vous
l'aurez compris, le
corps de notre "conseil"
est ici représenté par
les deux lignes :
ldstr "trace added by
xslt" µ
call void
[mscorlib]System.Console::WriteLine(string)
qui imprime tout
simplement la chaîne
"trace added by xslt"
vers la sortie standard.
Et
nous pourrions de la
même manière invoquer
une méthode disponible
sur une autre classe en
plaçant le conseil :
call void [CodeAspect]MonPremierAspect::Trace()
Bien entendu, si nous
avons fait le travail
pour la plus simple des
greffes (ajouter des
instructions en début de
corps d'une méthode), il
faudrait également implémenter des
feuilles XSLT "tisseuses
d'aspect" pour tous les
autres points
d'insertion :
-
avant
d'invoquer une méthode,
-
à la
sortie d'une méthode,
-
à la
lecture ou à l'écriture
d'un attribut,
-
au
changement de classe ou
de namespace dans la
pile d'invocation de
méthodes, etc...
Mais
nous pensons que la
puissance du couple
XPath / XSLT règlera
l'essentiel des
difficultés. A nous d'en
faire bon usage...
Nécessité d'un langage de plus haut niveau : AspectDNG ML
Si la
prouesse technique de
tisser un aspect en XSLT
sur notre langage MSILML
est intellectuellement
intéressante pour le
concepteur et les
développeurs d'AspectDNG,
il serait bien venu que
les utilisateurs finaux
de ce tisseur d'aspects
n'aient pas, quant à
eux, le besoin de
maîtriser les finesses
de XSLT pour greffer
leur conseils sur leur
code de base.
Nous
avons donc également
ébauché un petit langage
de description d'une
"greffe de conseils" :
AspectDngML (à
nouveau, en attente d'un
nom plus sexy). Ce
langage, basé sur XML,
permet de dire
simplement quel conseil
insérer, à quel endroit
dans le code de base.
Pour
l'instant, nous n'avons
implémenté que le point
d'insertion "début de
méthode" (comme nous
l'avons vu dans le
paragraphe précédent).
Ce qui donne le document
suivant :
<AspectDNGML>
<Advice>
<AppliesTo>
<XPathPattern>
Method[@abstract
!= 'True'][contains(../@name,
'Personne')]
</XPathPattern>
</AppliesTo>
<Before>
<MethodInvocation
assembly="CodeAspect"
namespace=""
class="FirstAspect"
method="Trace()"
/>
</Before>
</Advice>
</AspectDNGML>
Décryptons ensemble la
sémantique de ce
document AspectDngML :
-
Nous
voulons insérer
l'invocation de la
méthode Trace(), méthode
statique de la classe
FirstAspect, elle-même
située dans une autre
assembly,
CodeAspect.dll.
-
Cette
invocation de méthode
doit être insérée en
début de corps de
méthode
-
Plus
précisément, nous
souhaitons greffer cette
invocation de méthode au
début de toutes les
méthodes non abstraites
(le contraire n'aurait
pas de sens) de la
classe Personne.
Bien
entendu, lorsque le
projet avancera, nous
construirons une
interface graphique qui
permettra de produire ce
type de documents
automatiquement.
AspectDNG : un
méta-tisseur d'aspect
Vous
l'aurez compris, le
langage de haut niveau
AspectDngML ne remet pas
en cause l'architecture
globale choisie pour
l'implémentation d'AspectDNG;
il n'existe que pour
simplifier la vie à
l'utilisateur de notre
tisseur.
Il a
donc fallu imaginer un
mécanisme permettant de
transformer une
description de greffe
AspectDngML en une
feuille de styles XSLT
représentant l'aspect
correspondant. Là
encore, vous nous voyez
venir : transformer un
document XML en un autre
est la chasse gardée de
XSLT. Nous avons donc
implémenté une petite
feuille de styles XSLT
transformant un document
AspectDngML en une
feuille XSLT.
L'architecture d'AspectDNG
devient donc la suivante
:

Pour
ceux qui se demandent à
quoi ressemble la
syntaxe d'une feuille de
styles XSLT qui en
génère une autre, voici
de quoi satisfaire votre
curiosité :
<?xml
version="1.0"
encoding="iso-8859-1"
?>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:axsl="http://www.w3.org/1999/XSL/TransformAlias"
xmlns:ilml="http://www.dotnetguru.org/MSILML">
<xsl:namespace-alias
stylesheet-prefix="axsl"
result-prefix="xsl"
/>
<xsl:namespace-alias
stylesheet-prefix="ilml"
result-prefix="#default"
/>
<xsl:output
method="xml"
encoding="iso-8859-1"
indent="yes"
/>
<xsl:template
match="text()"
/>
<xsl:template
match="/">
<axsl:stylesheet
xmlns="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<axsl:output
method="xml"
encoding="iso-8859-1"
/>
<axsl:template
match="Assembly">
<ilml:Assembly>
<xsl:for-each
select="//MethodInvocation/@assembly">
<ilml:AssemblyDescription
name="{.}"
extern="true">
<ilml:Infos>
<axsl:text> .assembly
extern </axsl:text>
<xsl:value-of
select="."
/>
<axsl:text> </axsl:text>
</ilml:Infos>
</ilml:AssemblyDescription>
</xsl:for-each>
<axsl:apply-templates
select="node()"
/>
</ilml:Assembly>
</axsl:template>
<axsl:template
match="*">
<axsl:copy>
<axsl:copy-of
select="@*"
/>
<axsl:apply-templates
select="node()"
/>
</axsl:copy>
</axsl:template>
<xsl:apply-templates
/>
</axsl:stylesheet>
</xsl:template>
<xsl:template
match="XPathPattern">
<axsl:template
match="{.}/Body">
<ilml:Body>
<xsl:text> </xsl:text>
<xsl:for-each
select="ancestor::Advice/Before/Msil/Instruction">
<xsl:value-of
select="."
/>
<xsl:text> </xsl:text>
</xsl:for-each>
<xsl:for-each
select="ancestor::Advice/Before/MethodInvocation">
<xsl:text>call
void </xsl:text>
<xsl:if
test="string-length(@assembly)
> 0">
<xsl:text>[</xsl:text>
<xsl:value-of
select="@assembly"
/>
<xsl:text>]</xsl:text>
</xsl:if>
<xsl:if
test="string-length(@namespace)
> 0">
<xsl:value-of
select="@namespace"
/>
<xsl:text>.</xsl:text>
</xsl:if>
<xsl:value-of
select="@class"
/>
<xsl:text>::</xsl:text>
<xsl:value-of
select="@method"
/>
<xsl:text> </xsl:text>
</xsl:for-each>
<axsl:value-of
select="."
/>
</ilml:Body>
</axsl:template>
</xsl:template>
</xsl:stylesheet>
Comme
vous pouvez le
constater, cette feuille
XSLT méta-tisseuse
d'aspect n'a rien
d'exceptionnel, si ce
n'est la gestion des
préfixes et espaces de
nommage déclarés au
début.
Installation et utilisation d'AspectDNG v0.1
Profil utilisateur
AspectDNG est un simple
exécutable managé. Son
déploiement consiste
donc simplement à copier
AspectDNG.exe (ainsi que
son fichier de
configuration
AspectDNG.exe.config)
dans n'importe quel
répertoire sur le poste
utilisateur.
Attention toutefois,
étant donné que
AspectDNG repose sur les
utilitaires ILDASM et
ILASM, il faut
-
avoir
installé le SDK .NET sur
le poste utilisateur,
-
modifier le fichier
AspectDNG.exe.config,
qui référence les
chemins absolus
d'installation des
utilitaires ILDASM et
ILASM.
Profil développeur,
contributeur
AspectDNG sera un
logiciel Open Source.
Vous avez déjà tous la
possibilité de nous
faire part (à travers un
fil de discussion dans
le forum "bavardages" de
DNG) de vos
critiques, remarques et
suggestions.
Avec
la publication du
présent article, nous
allons également mettre
à votre disposition
l'archive du projet en
mode développement. Si
vous souhaitez en
prendre connaissance et
émettre des critiques ou
suggestions plus fines
sur le travail qui a été
réalisé, n'hésitez pas !
Et
dès que le noyau dur de
ce logiciel sera
stabilisé (en version
0.2 ou 0.3
certainement), nous
choisirons :
-
une licence OpenSource,
a priori peu restrictive
pour les utilisateurs
(si vous avez des
conseils quant au choix
entre GPL, LGPL, FreeBSD,
Apache, etc... nous
sommes très intéressés à
la fois par vos conseils
et par leurs
justifications)
-
un
mode de développement
collaboratif pour
les versions ultérieures
d'AspectDNG. Sur ce
point, nous comptons
énormément sur vous pour
nous donner vos
préférences; en effet,
vous allez devenir les
principaux contributeurs
de ce projet, donc à
terme il sera autant le
vôtre que celui de
DotNetGuru conformément
à l'esprit communautaire
de notre site. Nous
pourrions créer un
espace de partage de
documents sur notre site
Web, ou installer un
serveur CVS, ou encore
créer un environnement
de projet collaboratif
sur le site
Conclusion
AspectDNG en est à ses
prémices. La version
0.1 est une simple
preuve de faisabilité de
l'architecture globale
basée sur XML et XSLT,
et n'implémente qu'un
type d'aspect.
Il
faudra par la suite
implémenter
-
tous
les autres points
d'insertion qui nous
intéressent,
-
la
gestion d'aspects
contextuels (qui peuvent
tirer parti de la
connaissance de
l'endroit où ils seront
greffés)
-
une
belle interface
graphique masquant aux
utilisateurs la
complexité du langage
AspectDngML (ouf!)
Donc
nous avons énormément
besoin de vos
impressions, vos
sentiments, et plus
généralement de votre
aide pour mener ce
projet à bien. Nous
avons du pain sur la
planche, mais de belles
réjouissances en
perspective !
|