Académique Documents
Professionnel Documents
Culture Documents
Pierre Guilbault
Louis Volant
Synthèse du JML
Pré-condition ..............................................................................................................................6
Post-condition .............................................................................................................................6
Conclusion ........................................................................................................................................ 13
Bibliographie .................................................................................................................................... 14
2
Introduction au JML
Le Java Modeling Language est un langage servant à donner du sens à un code source, via la
spécification précise de celui-ci. Il se base pour cela sur la programmation par contrat, ainsi que la
logique de Hoare.
Les spécifications sont ajoutées via une syntaxe particulière (proche de celle des commentaires, ce
qui permet à tout compilateur Java de compiler un code contenant du JML), mais il existe des outils
qui, avant ou pendant la compilation, permettent de réaliser des tests sur le fonctionnement du
programme et la bonne écriture de celui-ci, ce à partir du sens donné au code source par les
spécifications en JML.
Ces outils sont multiples : entre autres nous verrons jmlc, jmlrac et l'Extended Static Checker
(ESC/Java).
Historique du JML
L’aspect programmation par contrat et spécification du code de JML peut se comparer à
d’autres langages tels qu’Eiffel et ceux de la famille Larch.
C’est Garry Leavens, professeur à l’Iowa State University, qui est à l’origine de JML (il a aussi
beaucoup travaillé sur les langages Larch tels que Smalltalk et C++).
3
Les assertions
Des règles régissant le comportement d’un programme Java (appelée assertions) peuvent
être incluses dans les commentaires de ce programme, avec quelques différences :
Ou
/**
* <pre><jml>
*
* invariant condition ;
*
* </jml></pre>
*/
Les assertions sont en fait des conditions qui doivent être vraies ou fausses (c’est le principe de la
programmation par contrat) : elles s’écrivent donc sous forme d’expressions booléennes. Toutes les
expressions sont purement fonctionnelles : c’est-à-dire qu’elles ne doivent pas engendrer d’effet de
bord (elles ne doivent rien modifier d’autre que leur valeur de retour).
Lorsqu’une méthode Java est purement fonctionnelle, on peut l’utiliser dans le code JML en plaçant
l’assertion juste avant le corps de cette méthode.
/*@ pure */
Le JML fonctionne à partir des états visibles, c’est-à-dire les états que l’on peut constater avant ou
après l’exécution d’une méthode.
Les invariants
Un invariant est une condition portant sur les attributs de la classe et devant être vérifiée
quelque soit la visibilité choisie. De la même manière qu’en Java, on distingue en JML les
invariants d'instance et les invariants de classe. Les invariants de classe sont préfixés par le mot
4
clé static et ne peuvent faire référence qu'à des constituants de classe (attributs et
méthodes statiques).
Comme les attributs et méthodes en Java, un invariant peut en JML avoir différents modes de
visibilité : public (public), protégé (protected) et privé (private).
Contrainte historique
Une contrainte d'historique a pour rôle de poser une condition d'évolution d’un élément.
Cette condition porte sur l'état avant l'appel d'une méthode quelle qu’elle soit et sur l’état après son
appel. On a la possibilité de faire référence à une expression exp évaluée avant l'appel de la méthode
avec le mot clé suivant :
\old(exp)
De la même manière que pour les invariants, on fait la différence entre les contraintes
d'historique de classe et les contraintes d'historique d'instance.
Exemple
class Compteur {
/*@
@ private invariant valeur >= 0 ;
@
@ private constraint valeur >= \old(valeur) ;
@*/
...
}
L'invariant est :
Pré-condition
Cette condition doit être satisfaite avant chaque appel de la méthode en question.
En JML, une pré-condition est introduite par le mot clé pre ou requires, les deux étant équivalents.
Post-condition
Une post-condition pour une méthode est une condition qui porte sur :
Elle doit être satisfaite après chaque appel de la méthode (sans toutefois lever d’exception).
Une post-condition est introduite par post ou ensures, les deux étant là aussi équivalents.
Post-condition exceptionnelle
Une post-condition exceptionnelle porte sur les mêmes éléments qu'une post-condition sauf que la
méthode en question doit lever une exception.
Elle est introduite en JML par signals ou exsures qui sont des termes équivalents.
Exemple
6
la méthode int getVal() retourne la valeur courante du compteur ;
la méthode void incr(int n) throws Debordement permet d'incrémenter le compteur
de n unités. Cette méthode lève l'exception Debordement si la nouvelle valeur est trop
grande pour être stockée dans un entier.
/**
* Si le compteur déborde alors on lève une exception.
*/
/**
* Retourne la valeur du compteur.
*/
On indique que la méthode getVal() est « pure », ce qui veut dire qu’elle ne produit pas d’effet de
bord. On peut donc l’utiliser en JML notamment pour définir les spécifications de la méthode
permettant d’incrémenter le compteur.
/*@
@ On incrémente la valeur du compteur de n.
@
@ requires n >= 0 ;
@
@ ensures getValeur() == \old(getValeur()) + n ;
@
@ signals (Debordement debordement) getValeur() == \old(getValeur()) ;
@*/
7
La pré-condition est ici :
requires n >= 0 ;
Elle spécifie qu'on ne peut pas incrémenter le compteur avec une valeur négative.
La post-condition est :
Elle spécifie que si la méthode incr termine normalement – sans lever une exception - alors on
incrémente la valeur du compte de n.
Elle spécifie que si la méthode incr termine en levant une exception, alors la valeur du compteur
reste inchangée.
On peut ajouter, de manière ponctuelle, une assertion à l’intérieur de la méthode si elle est de taille
raisonnable :
L'assertion est :
8
Les modèles
Le JML fournit un certain nombre d’options permettant d’améliorer le niveau d’abstraction
dans les spécifications. On a alors la possibilité de spécifier des éléments model comme des modèles
d’attributs ou des modèles de méthodes. Ces modèles ne sont utilisables que dans les spécifications
(il ne faut pas les utiliser dans le code Java).
Les modèles permettent de spécifier des types différents. Le vérificateur d’assertions à runtime peut
gérer les modèles qui correspondent à des modèles déjà présents dans le code Java, tant que ceux-ci
ont également été définis dans celui-ci. Ainsi, il faut déclarer un attribut abstrait JML qui représente
l'attribut Java dans les spécifications. L’attribut modèle ne doit apparaître que dans les spécifications,
jamais dans le code. Il présente notamment comme avantage de permettre une abstraction
supplémentaire, ce qui est mieux compréhensible pour la relecture des spécifications.
Exemple :
Dans l’exemple ci-dessus, on notera que la corrélation entre les valeurs de size et itsSize est faite
grâce à la clause represents.
9
Les outils associés à JML
Compilateur JML
Le compilateur JML (jmlc) permet de compiler des classes Java accompagnées de
spécifications JML en bytecode Java. Ce compilateur produit, à partir de fichiers Java (extension .java)
et de fichiers purement JML (extension .jml) des fichiers en bytecode (extension .class) qui
comportent du code qui vérifie à l'exécution les assertions JML.
Si une assertion n'est pas vérifiée, une exception spécifique est levée.
Autres outils
Il existe de nombreux outils offrant des fonctionnalités basées sur les annotations JML. L'outil
Iowa State JML permet de convertir les annotations JML en exécutable d'assertions via un
compilateur de vérification d'assertion jmlc, de générer une Javadoc améliorée incluant des
informations tirées des spécifications JML, et de générer des tests unitaires pour JUnit via le
générateur jmlunit.
En plus de cet outil, un grand nombre de groupes indépendants travaillent sur des outils utilisant
JML. Parmi ceux-ci : ESC/Java2, Extended Static Checker qui utilise les annotations JML pour effectuer
une vérification statique plus rigoureuse que celle autrement possible; Daikon, un générateur
d'invariant dynamique; KeY, un vérificateur de théorèmes; Krakatoa, un vérificateur de théorèmes
basé sur l'assistant de preuve Coq; et JMLeclipse, un plugin pour Eclipse l'environnement de
développement intégré.
Le JMLrac est un outil de base de JML qui exécute un code Java, tout en soulevant des
exceptions spécifiques au JML lorsqu’il rencontre une erreur sur les annotations JML. Il vérifie donc le
bon fonctionnement du programme à runtime.
10
Test de conformité avec JML
Si le programme est spécifié en JML, on peut vérifier que ce programme est conforme à sa
spécification en évaluant les assertions JML. Cela peut être réalisé très simplement en utilisant le
compilateur d'assertions JML (jmlc). Si une des assertions est évaluée à faux, une exception
spécifique est levée et on en déduit qu'il y a une erreur dans le programme ou dans la spécification.
Les assertions JML sont donc utilisées comme oracle de test. Cet oracle peut être plus ou moins bon
selon le degré de spécification du programme. Si le programme est peu spécifié, c'est-à-dire
comporte des assertions JML qui posent peu de contraintes, comme des post-conditions réduites
à true, l'oracle ne sera pas performant et ne permettra pas de mettre en évidence certaines erreurs.
Par contre, si les assertions JML posent des contraintes suffisantes, cet oracle pourra mettre en
évidence un maximum d'erreurs.
L'utilisation de JML comme oracle de test a été en particulier implantée dans l'outil jmlunit, qui
permet de générer des tests unitaires pour des méthodes Java. Ces tests unitaires, conçus pour
pouvoir être exécutés avec JUnit, réalisent une série de tests constitués essentiellement d'un appel à
une méthode.
11
JML dans les JavaBeans
La création de programmes vendables clef-en-main est de plus en plus populaire. Mais le fait
que les informations échangées entre les fournisseurs et les utilisateurs des composants sont
limitées est un sérieux problème. A cause de cela, les fournisseurs doivent faire la vérification sur
leurs composants, mais les utilisateurs aussi.
La vérification d’assertion est une méthode efficace pour améliorer la qualité des vérifications
logicielles. Une des méthodes de vérification d’assertion est le langage JML.
A l’origine, ces vérifications étaient placées directement dans le code compilé, et n’étaient pas
fournis aux clients.
Aujourd’hui, et grâce aux enveloppes de vérification des JavaBeans, on peut inclure des tests de
vérification directement dans le livrable compilé (sans accès au code source), et donc permettre au
client de :
12
Conclusion
Le JML est un langage qui est à considérer parmi la batterie d’outil pour faire des tests unitaires sur le
code source, et ainsi aller plus loin dans la recherche d’erreur qu’un simple debugging. Le JML sert à
donner un sens à la programmation, sens qui peut être compris par les outils associés à JML, et qui
peut donc voir le code de façon à vérifier que celui-ci reste bien dans la mission qui lui a été confié.
Le JML permets donc une plus grande fiabilité de son code au final, mais demande un investissement
de temps conséquent au départ, afin d’écrire des spécifications en nombre suffisants (pour être
utiles) et elles aussi, valides.
Prendre le temps d’écrire les spécifications JML est donc un investissement en temps, et cet
investissement n’est valable que sur des projets conséquents, car la recherche de bugs (ou bien les
implications que peuvent avoir les différentes erreurs de code) se révèlera bien plus rapide et
efficace grâce à l’utilisation du JML.
13
Bibliographie
Beyond Assertions: Advanced Specification and Verification with JML and ESC/JAVA2 -
(Patrice Chalin, Joseph R. Kiniry, Gary T. Leavens, and Erik Poll) -
http://www.eecs.ucf.edu/~leavens/JML/fmco.pdf
A Runtime Assertion Checker for the Java Modeling Language (JML) - (Yoonsik Cheon and
Gary T. Leavens) - ftp://ftp.cs.iastate.edu/pub/techreports/TR02-05/TR.pdf
Using Wrappers to Add Run-Time Verification Capability to Java Beans - (Vladimir Glina &
Stephen Edwards) - http://www.eecs.ucf.edu/~leavens/SAVCBS/2004/posters/Glina-Edwards.pdf
How the Design of JML Accommodates Both Runtime Assertion Checking and Formal
Verification - (Gary T. Leavens, Yoonsik Cheon, Curtis Clifton, Clyde Ruby, and David R. Cok) -
ftp://ftp.cs.iastate.edu/pub/techreports/TR03-04/TR.pdf
14