hardened golo: donnez de la confiance en votre code golo
TRANSCRIPT
HAL Id: hal-01354836https://hal.inria.fr/hal-01354836
Submitted on 19 Aug 2016
HAL is a multi-disciplinary open accessarchive for the deposit and dissemination of sci-entific research documents, whether they are pub-lished or not. The documents may come fromteaching and research institutions in France orabroad, or from public or private research centers.
Lâarchive ouverte pluridisciplinaire HAL, estdestinĂ©e au dĂ©pĂŽt et Ă la diffusion de documentsscientifiques de niveau recherche, publiĂ©s ou non,Ă©manant des Ă©tablissements dâenseignement et derecherche français ou Ă©trangers, des laboratoirespublics ou privĂ©s.
Hardened Golo : Donnez de la confiance en votre codeGolo
Raphael Laurent
To cite this version:Raphael Laurent. Hardened Golo : Donnez de la confiance en votre code Golo. GĂ©nie logiciel [cs.SE].2016. ïżœhal-01354836ïżœ
Rapport de Projet de Fin dâĂtudesHardened Golo: Donnez de la confiance en votre code Golo
Raphael Laurent
21 juin 2016
1
Table des matiĂšres1 Remerciements 3
2 Introduction 4
3 Ătat de lâart 53.1 VĂ©rification statique de programmes . . . . . . . . . . . . . . . . 6
3.1.1 Preuve SMT . . . . . . . . . . . . . . . . . . . . . . . . . 63.1.2 Vérificateurs de programmes . . . . . . . . . . . . . . . . 73.1.3 Spécification JML . . . . . . . . . . . . . . . . . . . . . . 7
3.2 Analyse statique - en pratique . . . . . . . . . . . . . . . . . . . . 83.2.1 Langages intermédiaires . . . . . . . . . . . . . . . . . . . 83.2.2 Traduction de langages . . . . . . . . . . . . . . . . . . . 9
3.3 Bilan de cet Ă©tat de lâart . . . . . . . . . . . . . . . . . . . . . . . 9
4 Golo 10
5 ĂlĂ©ments de traduction Golo vers WhyML 105.1 Module et fonction . . . . . . . . . . . . . . . . . . . . . . . . . . 115.2 Appel de fonction . . . . . . . . . . . . . . . . . . . . . . . . . . . 115.3 DĂ©clarations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135.4 Assignation sur une variable . . . . . . . . . . . . . . . . . . . . . 145.5 Consultation de constantes et variables . . . . . . . . . . . . . . . 145.6 OpĂ©rateurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155.7 SpĂ©cification de programme . . . . . . . . . . . . . . . . . . . . . 165.8 Gestion du "return" . . . . . . . . . . . . . . . . . . . . . . . . . . 16
6 Implémentation 176.1 Grammaire JJTree et arbre syntaxique abstrait . . . . . . . . . . 186.2 Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206.3 Fonction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216.4 Constantes et variables . . . . . . . . . . . . . . . . . . . . . . . . 23
7 Preuve dans Why3 24
8 Bilan 27
9 Retour dâexpĂ©rience 28
2
1 RemerciementsJe tiens Ă remercier mon tuteur Nicolas Stouls, pour ses conseils tout au long
de mon stage et son suivi rigoureux. Il mâa permis dâaller plus loin dans ma rĂ©ali-sation, ainsi que de comprendre diffĂ©rentes problĂ©matiques de mon sujet plus enprofondeur. Son encadrement mâa permis de structurer ce projet correctementet de garantir une contribution riche.
Je remercie Ă©galement Julien Ponge, pour ses indications Ă©clairĂ©es dans lefonctionnement du compilateur Golo, ainsi que le langage lui-mĂȘme.
Finalement, je remercie le laboratoire du CITI Ă lâINSA Lyon pour mâavoiraccueilli pendant ce stage.
3
2 IntroductionGolo [1] [2] est un langage dynamique construit autour de lâinstruction "in-
vokedynamic" de la machine virtuelle Java. Originellement dĂ©veloppĂ© au labo-ratoire CITI de lâINSA de Lyon par le groupe Dynamid, Golo est open-sourceet dĂ©diĂ© Ă la rĂ©alisation dâapplications dynamiques.
Lâobjectif de mon stage est de pouvoir augmenter la confiance que lâon peutavoir en un programme Golo. Lâapproche retenue est dâutiliser lâoutils Why3 [3]dĂ©veloppĂ© par lâĂ©quipe Toccata de lâINRIA/LRI/CNRS, originellement conçupour la vĂ©rification de programmes en C, Java et Ada. En sâappuyant sur letravail fait par le plugin Krakatoa [4] pour analyser du code Java avec cet outil,une traduction du code Golo vers du code WhyML [5] exĂ©cutable a Ă©tĂ© implĂ©-mentĂ©e dans le compilateur Golo. Cette implĂ©mentation vise Ă poser une basepour pouvoir analyser statiquement du code Golo avec Why3, ainsi que pourfaire des vĂ©rifications statiques lors de la traduction. La spĂ©cification dans lecode Golo devait originellement se faire dans un langage inspirĂ© de JML [6],cependant lâapproche implĂ©mentĂ©e permet au programmeur de spĂ©cifier le codeGolo dans le langage de spĂ©cification de Why3 directement. Le dĂ©tail de la mĂ©-thode dâimplĂ©mentation, des fonctionnalitĂ©s supportĂ©es ainsi que des limitationssera abordĂ© dans une partie ultĂ©rieure.
Un travail prĂ©cĂ©dent sur le mĂȘme sujet rĂ©alisĂ© par Bertrand Cayeux montrequâil est possible de modĂ©liser les objets dynamiques de Golo en Java. Il a Ă©tudiĂ©la possibilitĂ© de traduire Golo en Java pour prendre avantage du plugin Krakatoapour lâanalyse statique en Java, comme montrĂ© sur la figure 1.
Figure 1 â Solution proposĂ©e par Bertrand Cayeux
Golo Java
Krakatoa
Why3 Prouveursexternes
Traduction Golovers Java
Traduction Javavers WhyML
Génération de conditionsde vérification
Cela peut inspirer une traduction similaire directement en WhyML par lasuite, cependant ce nâest pas abordĂ© dans mon travail qui vise Ă mettre enplace les briques de base pour traduire Golo en WhyML pour faire de lâanalysestatique, comme montrĂ© sur la figure 2.
4
Figure 2 â Solution proposĂ©e pour ce stage
Golo
Why3 ProuveursexternesTraduction Golo
vers WhyML Génération de conditionsde vérification
Dans un premier temps, je vais prĂ©senter un Ă©tat de lâart de la vĂ©rificationde programmes, et plus spĂ©cifiquement de la vĂ©rification statique.
3 Ătat de lâartLa vĂ©rification de programmes a pour objectif de valider que le comportement
dâun programme est et sera celui attendu. Il existe diffĂ©rents langages de spĂ©ci-fication, avec chacun ses particularitĂ©s. Ainsi, les automates (ensemble dâĂ©tatset de transitions possibles) permettent dâexprimer une sĂ©quentialitĂ© dâactionset un ensemble dâĂ©tat atteignables, les spĂ©cifications logiques permettent de dĂ©-crire la forme que doit avoir un paramĂštre ou un rĂ©sultat de calcul, les algĂšbresde processus permettent de mettre lâaccent sur les communications et lâentre-lacement des actions dans les systĂšmes concurrents ou distribuĂ©s. Par ailleurs,les spĂ©cifications peuvent ĂȘtre placĂ©es dans des fichiers Ă part entiĂšre ou bienliĂ©es fortement Ă un code, comme câest le cas des langage de spĂ©cification parannotation. Dans ce travail, nous verrons que câest cette derniĂšre catĂ©gorie quinous intĂ©ressera plus particuliĂšrement.
Il y a ensuite trois méthodes générales pour valider un programme et saspécification, et toutes reposent sur la comparaison du comportement du pro-gramme au référentiel attendu défini au préalable :
â Tests : la vĂ©rification est opĂ©rĂ©e par lâexĂ©cution de cas spĂ©cifiques et deportions de code.
â Analyse dynamique : la vĂ©rification est opĂ©rĂ©e lors de lâexĂ©cution du pro-gramme dans un cas normal dâexĂ©cution. Lâanalyse dynamique est parfoiscombinĂ©e Ă lâanalyse statique pour pouvoir obtenir une information plusprĂ©cise sur lâorigine dâune erreur de vĂ©rification [7].
â Analyse statique : les vĂ©rifications sont effectuĂ©es sur le programme sansexĂ©cution. Lâune des maniĂšres de faire consiste Ă rĂ©duire la combinaisondu programme et de son modĂšle Ă un ensemble de conditions de vĂ©rifica-tions : aprĂšs analyse, si ces conditions sont validĂ©es alors le programmeest certifiĂ© comme valide, cependant un Ă©chec de vĂ©rification ne permetpas de diffĂ©rencier entre une erreur du programme et un manque dâinfor-mation pour rĂ©soudre lâanalyse.
Une diffĂ©rence significative parmi ces mĂ©thodes rĂ©side dans le fait que lestests et lâanalyse dynamique nĂ©cessitent lâexĂ©cution de tout ou partie du pro-
5
gramme, alors que lâanalyse statique permet de garantir le respect dâune spĂ©ci-fication pour toute exĂ©cution, et avant toute exĂ©cution.
Pouvoir garantir un fonctionnement avant exĂ©cution possĂšde de nombreuxavantages, notamment dans des environnements ou tester une application nâestpas sans danger (comme dans lâaĂ©ronautique par exemple). Cette approche estĂ©galement complĂ©mentaire aux tests ou Ă lâanalyse dynamique, et câest pourquoile choix de travailler sur lâanalyse statique a Ă©tĂ© fait.
Je vais maintenant détailler le fonctionnement de la vérification statique deprogrammes, et étudier les solutions existantes pour intégrer ces méthodes à deslangages orientés objets de haut niveau.
3.1 VĂ©rification statique de programmesJe mâintĂ©resse ici Ă lâanalyse statique de programmes en gĂ©nĂ©ral, et les diffĂ©-
rents outils qui la rendent possible. Je prĂ©sente dâabord briĂšvement le fonction-nement des prouveurs SMT, puis les outils qui les utilisent, avant de finir surla spĂ©cification de programmes, nĂ©cessaire pour pouvoir analyser statiquementdes programmes avec les outils prĂ©sentĂ©s.
3.1.1 Preuve SMT
Les prouveurs SMT (SatisfiabilitĂ© Modulo ThĂ©ories) tels que Alt-Ergo [8]sont le bloc de base de lâanalyse statique. Leur objectif est de rĂ©soudre la satis-fiabilitĂ© de formules logiques (que lâon appelle ici conditions de vĂ©rifications), enfonction de prĂ©dicats Ă©noncĂ©s au prĂ©alable. A ce jour, les prouveurs sont limitĂ©sĂ la logique de premier ordre, ce qui aura des rĂ©percussions sur certaines limita-tions de lâanalyse statique prĂ©sentĂ©es plus loin. Notamment, cela implique quetout problĂšme du second ordre doit ĂȘtre ramenĂ© au premier ordre pour pouvoirĂȘtre rĂ©solu avec un prouveur SMT.
DiffĂ©rents prouveurs SMT ont diffĂ©rentes capacitĂ©s de rĂ©solution (mĂ©thodenaĂŻve, apprentissage de clauses par conflit, analyse de conflits, retour en arriĂšre).Cela implique que certaines formules peuvent ĂȘtre rĂ©solues par un prouveurSMT, alors que dâautres non. La conclusion directe que lâon peut en tirer estquâutiliser diffĂ©rents prouveurs SMT peut donc amĂ©liorer le nombre de condi-tions de vĂ©rifications prouvables.
Bien que les prouveurs SMT puissent ĂȘtre utilisĂ©s manuellement pour vĂ©rifierdes formules logiques, ils sont principalement utilisĂ©s avec des outils qui gĂ©nĂšrentdes conditions de vĂ©rifications Ă partir dâun programme et dâune spĂ©cification [9].Les conditions de vĂ©rification gĂ©nĂ©rĂ©es permettent dâassurer la logique suivante :
â Si la condition de vĂ©rification est vraie, alors le programme et la spĂ©cifi-cation sont vrais.
â Si la condition de vĂ©rification nâest pas vraie, alors on ne peut pas concluresur la justesse du programme et de sa spĂ©cification.
Toujours concernant la preuve SMT, on peut Ă©galement noter un effort pourautomatiser certaines tĂąches : la gĂ©nĂ©ration dâinvariants automatiquement parexemple, pour dĂ©charger le programmeur de cette tĂąche parfois trĂšs complexe
6
[10]. Je mâintĂ©resse de plus prĂšs aux outils qui permettent la gĂ©nĂ©ration deconditions de vĂ©rifications automatiquement dans la partie suivante.
3.1.2 VĂ©rificateurs de programmes
Je prĂ©sente ici deux vĂ©rificateurs de programmes, qui sont tous deux basĂ©s surla traduction dâun langage intermĂ©diaire vers des conditions de vĂ©rifications pourprouveurs SMT. Nous reviendrons sur lâutilisation dâun langage intermĂ©diairedans la partie suivante.
Le premier outil est Why3 [3], dĂ©veloppĂ© par lâĂ©quipe Toccata de lâINRIA/L-RI/CNRS, originellement conçu pour la vĂ©rification de programmes en C, Javaet Ada. Le second outil est Boogie [11], dĂ©veloppĂ© par Microsoft Research, ori-ginellement conçu pour la vĂ©rification de programmes Spec#.
Ces outils ont en premier lieu de nombreux points communs :â Ils ont pour objectif principal dâautomatiser la gĂ©nĂ©ration de conditions
de vĂ©rification pour des prouveurs SMT.â Ils se basent sur lâutilisation dâun langage intermĂ©diaire, ainsi quâun lan-
gage de spĂ©cification (WhyML pour Why3, et Boogie2 pour Boogie).â Ils utilisent une modĂ©lisation explicite des structures et de la mĂ©moire.
Lâutilisateur peut ainsi utiliser sa propre modĂ©lisation si besoin.â Ils travaillent en logique de premier ordre.Cependant, ils sont Ă©galement pensĂ©s diffĂ©remment, notamment sur deux
aspects que je considĂšre important pour le choix dâun outil pour ce stage :Boogie utilise Z3 comme prouveur SMT, alors que Why3 est dĂ©veloppĂ© autourdu support dâun grand nombre de prouveurs SMT, via un systĂšme de drivers.De plus, Boogie a Ă©tĂ© dĂ©veloppĂ© pour C# originellement, alors que Why3 pourC/Java. Sachant que Golo produit du bytecode qui sâexĂ©cute sur la machine vir-tuelle Java (comme Java), Why3 semble ĂȘtre un choix plus judicieux au premierabord.
Ces outils reposant tous sur la traduction dâun programme et dâune spĂ©ci-fication dans un langage intermĂ©diaire, nous allons dâabord Ă©tudier briĂšvementun langage de spĂ©cification, nĂ©cessaire pour pouvoir effectuer des vĂ©rificationplus poussĂ©es dâun programme par analyse statique.
3.1.3 Spécification JML
Dans ce travail, je mâintĂ©resse Ă JML (Java Modelling Language) [6], uneinterface de spĂ©cification de comportement pour Java .
Lâobjectif principal de JML est de pouvoir masquer des modĂšles mathĂ©-matiques derriĂšre des classes facilement comprĂ©hensibles par tout program-meur Java. Le principe de base de ce langage de spĂ©cification est le "designpar contrat" : un tuple (obligations, bĂ©nĂ©fices) peut sâexprimer par un tuple(prĂ©-conditions, post-conditions). Ce langage utilise la logique de Hoare, lesprĂ©-conditions et post-conditions ainsi que les invariants, et permet lâajout dequantifieurs, de variables de spĂ©cification pures (qui nâont pas dâimpact sur ledĂ©roulement du programme), et de "frame conditions" (dĂ©finit un comportement
7
normal et un comportement exceptionnel). JML peut ĂȘtre utilisĂ© pour gĂ©nĂ©rerdes docs amĂ©liorĂ©es, gĂ©nĂ©rer des tests unitaires, oĂč faire de la vĂ©rification sta-tique.
Pour pouvoir effectuer de lâanalyse statique en Golo, il est nĂ©cessaire de semunir dâun langage de spĂ©cification. Golo Ă©tant similaire Ă Java, une variante deJML pour spĂ©cifier le code Golo semble ĂȘtre une approche logique. Cependant,mon implĂ©mentation ne se concentre pas sur cet aspect et utilise directement lelangage de spĂ©cification de Why3 dans le code Golo.
3.2 Analyse statique - en pratiqueDans cette partie, je me concentre sur les langages intermédiaires des vérifi-
cateurs, ainsi que la traduction de langages. Il existe de nombreux exemples detraducteurs de langages, tel que Spoon [12], mais je me concentre ici uniquementsur ce qui touche aux langages intermĂ©diaires pour lâanalyse statique.
3.2.1 Langages intermédiaires
Je mâintĂ©resse dans cette partie aux langages intermĂ©diaires, et plus particu-liĂšrement ceux utilisĂ©s avec les outils de vĂ©rification que sont Why3 et Boogie.
Lâavantage dâun langage intermĂ©diaire pour un outil dâanalyse statique estquâil donne accĂšs Ă toutes les fonctionnalitĂ©s de cet outil, tout en offrant unlangage de programmation et de spĂ©cification de haut niveau. Les trois langagesprĂ©sentĂ©s ici ont pour point commun de supporter quelques blocs de base pourĂ©crire un programme : constantes, variables mutables, code "ghost" (spĂ©cificationpure), sĂ©quences, boucles, fonctions, prĂ©/post-conditions, invariants.
Boogie2 (anciennement BoogiePL) est le langage utilisĂ© dans lâoutil Boogie[11]. Il se dĂ©marque en Ă©tant un langage typĂ© dont le typage nâest pas forcĂ©("any" peut ĂȘtre utilisĂ© partout). Boogie2 supporte des procĂ©dures, mais pasde mĂ©thodes. Il nây a Ă©galement pas de heap pour les objets, pas de classes nidâinterfaces, les effets de bords ne sont pas permis, ni les appels par rĂ©fĂ©rence.Il nây a pas dâexceptions ni de contrĂŽle de flow structurĂ©. Tous ces Ă©lĂ©mentsmanquants vont largement contribuer au choix de ne pas utiliser Boogie2 commelangage intermĂ©diaire par la suite, Ă©tant donnĂ© que Golo les utilisent tous (celasera discutĂ© dans la section suivante).
Dafny [13] est un autre langage intermĂ©diaire, qui lui est pensĂ© pour sup-porter la programmation orientĂ©e objet, avec cependant des limitations fortes,tel que lâabsence dâhĂ©ritage. Le langage est typĂ©, et nĂ©cessite au programmeurde dĂ©clarer explicitement quelles parties du programme ont le droit de chan-ger (contrainte forte). Ce langage intermĂ©diaire est traduit en Boogie2 pourlâanalyse dans lâoutil Boogie.
Enfin, WhyML [5] est le langage intermĂ©diaire de lâoutil Why3. Le langage esttypĂ©, cependant propose Ă©galement de lâinfĂ©rence de type. Il ne possĂšde pas demodĂšle mĂ©moire, et supporte uniquement les alias statiques. Il nây a Ă©galementpas dâobjets, cependant ils peuvent ĂȘtre modĂ©lisĂ©s avec des structures de lalibrairie standard.
8
Les langages intermĂ©diaires peuvent ĂȘtre utilisĂ©s directement pour prouverdes algorithmes ou des thĂ©orĂšmes (bĂ©nĂ©ficier de la puissance des prouveurs SMTet en mĂȘme temps de langages de plus haut niveau), cependant ils sont Ă©galementbeaucoup utilisĂ©s comme langage de traduction cible. Nous allons regarder deplus prĂšs la traduction vers un langage intermĂ©diaire dans la partie suivante.
3.2.2 Traduction de langages
Dans cette section, je mâintĂ©resse Ă la traduction de langages vers un langageintermĂ©diaire pour lâanalyse statique. Je montre lâutilitĂ© de traduire vers un lan-gage intermĂ©diaire, bien quâil y ait des limitations qui doivent ĂȘtre considĂ©rĂ©es.
Je considĂšre le travail fait pour traduire Dafny vers Boogie, Dafny Ă©tantconstruit autour de cette traduction [13]. La transformation cherche Ă garantirquâun programme Boogie2 prouvĂ© correct implique que le programme Dafnydâorigine est correct, cependant la garantie inverse nâest pas vraie : un pro-gramme Boogie2 qui ne vĂ©rifie pas ne permet pas de savoir Ă coup sĂ»r quele programme Dafny est invĂ©rifiable. Câest une limitation assez importante :lorsquâon "empile" les intermĂ©diaires entre le langage source et le langage dedestination, cela multiplie les possibilitĂ©s quâun Ă©lĂ©ment non supportĂ© dans unetraduction dâun langage vers un autre apparaisse.
Un effort a Ă©galement Ă©tĂ© fait pour traduire Boogie2 vers WhyML [14] : lâidĂ©eĂ©tant de profiter de lâutilisation importante de Boogie dans le domaine de lavĂ©rification, tout en bĂ©nĂ©ficiant des avantages de Why3 permettant lâinterfaçageavec de nombreux prouveurs externes. Un second avantage citĂ© rĂ©side dans lefait que le code WhyML peut ĂȘtre exĂ©cutĂ©. Il y a cependant des limitationsdues au fait que le systĂšme de typage de WhyML est plus restrictif que celui deBoogie2, tous les programmes ne peuvent pas ĂȘtre traduit avec succĂšs.
Finalement, le plugin Krakatoa pour Why3 [4] permet la traduction de pro-grammes Java annotĂ©s en JML vers des programmes WhyML. La mĂ©thode pro-pose notamment une dĂ©finition dâun modĂšle mĂ©moire dans le code, ainsi quâunemodĂ©lisation des classes, deux Ă©lĂ©ments qui manquaient Ă WhyML. Une des li-mitations du plugin rĂ©side dans le fait que le code WhyML produit est destinĂ©Ă de la vĂ©rification pure, et ne peut pas ĂȘtre exĂ©cutĂ©.
Ces éléments viennent confirmer le choix de traduire directement de Golovers WhyML, tout en générant du code WhyML exécutable.
3.3 Bilan de cet Ă©tat de lâartCet Ă©tat de lâart me permet de justifier les choix dâimplĂ©mentation dâune
solution pour augmenter la confiance en du code Golo. Lâapproche choisie sebase sur lâoutil dâanalyse statique Why3, en utilisant WhyML comme langagecible dâune traduction directe depuis Golo. La spĂ©cification est faite directementavec le langage de spĂ©cification de Why3 dans le code Golo. Lâanalyse statiquepeut ensuite ĂȘtre effectuĂ©e dans Why3 avec des prouveurs externes. QuelquesĂ©lĂ©ments de vĂ©rification sont prĂ©sents dans le traducteur directement (syntaxe
9
Golo, existence de fonction). Le code WhyML produit est exĂ©cutable, et produitles mĂȘme rĂ©sultats que le code Golo.
Dans la partie suivante, je mâintĂ©resse plus en dĂ©tail au langage Golo et Ă ses spĂ©cificitĂ©s. Je dĂ©taillerai ensuite la traduction de certains Ă©lĂ©ments de Golovers WhyML, avant dâexpliquer des Ă©lĂ©ments clĂ©s de mon implĂ©mentation dansle compilateur Golo.
4 GoloGolo [1] [2] est un langage simple pour la machine virtuelle Java (JVM),
crĂ©Ă© pour la recherche et lâĂ©ducation. Le trait principal de ce langage est lefait quâil est construit autour de lâinstruction invokedynamic de la JVM 7+.Invokedynamic permet notamment de customiser Ă lâexĂ©cution un lien entreun site dâappel et une implĂ©mentation de mĂ©thode. Cette instruction simplifiegrandement la crĂ©ation dâun langage typĂ© dynamiquement pour la JVM.
Golo est similaire Ă Java sur beaucoup dâaspects, et supporte mĂȘme lâAPIJava standard : les librairies Java peuvent ĂȘtre utilisĂ©es en Golo. Les Ă©lĂ©mentstels que les boucles et le contrĂŽle de flux sont similaires Ă©galement. Cependant,le langage vise Ă offrir une dynamicitĂ© qui nâest pas atteignable facilement enJava. Pour cela, Golo offre notamment :
â Lâaugmentation de classes : permet dâajouter des mĂ©thodes Ă une classedynamiquement.
â Les objets dynamiques : des objets dont les propriĂ©tĂ©s et les mĂ©thodespeuvent ĂȘtre dĂ©finis pour chaque instance.
â Un typage dynamiqueJe montre dans la partie suivante comment certains Ă©lĂ©ments de Golo peuvent
ĂȘtre traduits en WhyML.
5 ĂlĂ©ments de traduction Golo vers WhyMLJe montre ici comment peuvent ĂȘtre traduits des Ă©lĂ©ments de Golo enWhyML,
un langage proche syntaxiquement de OCaml, pour lâoutil Why3. Jâajoute lacontrainte que le code WhyML soit exĂ©cutable, ce qui permet de vĂ©rifier facile-ment sur un programme simple que le comportement est le mĂȘme quâen Golo,et permet dâidentifier de possibles erreurs qui apparaissent uniquement Ă lâexĂ©-cution, tout cela sans complexifier la traduction de façon importante. Je meconcentre sur les fonctionnalitĂ©s gĂ©nĂ©rales des Ă©lĂ©ments traduits, lâobjectif Ă©tantde pouvoir poser les bases pour la traduction dâun programme simple. Les Ă©lĂ©-ments traduits ne supportent pas toutes les fonctionnalitĂ©s Golo, lâobjectif Ă©tantde pouvoir traduire un nombre suffisant dâĂ©lĂ©ments pour la traduction automa-tique de programmes simples (plus de fonctionnalitĂ©s peuvent ĂȘtre ajoutĂ©es parla suite une fois les bases posĂ©es).
Lâensemble des Ă©lĂ©ments pour lesquels une traduction est visĂ©e est composĂ©de : module, fonction, variables, opĂ©rateurs, spĂ©cification, return. Ces Ă©lĂ©ments
10
ne sont parfois traduits que partiellement, comme prĂ©cisĂ© dans les parties sui-vantes qui montrent comment traduire des portions de code Golo en WhyML.Dans la section ??, je prĂ©senterai lâoutil dĂ©veloppĂ© dans le cadre de ce travail,qui implĂ©mente les Ă©lĂ©ments prĂ©sentĂ©s ici.
5.1 Module et fonctionLes fonctions Golo font toujours parties dâun module, Ă©lĂ©ment de base de
tout programme. Le code 1 montre la dĂ©finition dâun module et dâune fonctionsimple, renvoyant son premier paramĂštre. Une fonction Golo est dĂ©finie avec lemot clĂ© "function", et les paramĂštres sont listĂ©s entre deux symboles "|".
1 module fonction.Main2
3 function main = |a, b| {4 return a5 }
Code 1: Module Golo
Le code WhyML correspondant 2 est trĂšs similaire. Une dĂ©claration de fonc-tion peut se faire avec le mot clĂ© "let", et les paramĂštres apparaissent entreparenthĂšses. Notons quâen WhyML le rĂ©sultat de la derniĂšre instruction dâunefonction est la valeur retournĂ©e implicitement.
En raison de limitations de la ligne de commande Why3, ce code WhyMLnâest pas directement exĂ©cutable car il nâest pas possible de lancer un programmepar une fonction attendant des paramĂštres.
1 module FonctionMain2 let main (a)(b)3 = a4 end
Code 2: Module WhyML
Je considĂšre tout au long de ce travail que nous nous intĂ©ressons Ă un uniquemodule Golo (dans un seul fichier). Je montre dans la prochaine partie commentpeut ĂȘtre traduit un appel de fonction, et ne traite donc pas le cas des fonctionsappartenant Ă un autre module.
5.2 Appel de fonctionUn appel de fonction en Golo peut se faire de plusieurs façon :â Appel de fonction sans paramĂštreâ Appel de fonction avec paramĂštres
11
â Appel de fonction avec aritĂ© variableâ Appel de fonction avec des paramĂštres nommĂ©s : permet dâutiliser le nom
des paramĂštres explicitement lors de lâappel dâune fonction (permet ainside rĂ©-ordonner les paramĂštres Ă lâappel).
Je mâintĂ©resse ici Ă montrer comment implĂ©menter en WhyML un appel defonction avec ou sans paramĂštres pour pouvoir supporter une grande quantitĂ©de programmes simples. WhyML ne permettant pas les appels de fonctions Ă aritĂ© variable, il faut adapter les appels de fonction lors de la traduction pourque lâaritĂ© de lâappel dans le code WhyML corresponde Ă lâaritĂ© de la fonctionappelĂ©e.
Lâexemple Golo du code 3 montre comment une fonction peut ĂȘtre appelĂ©eavec un paramĂštre. LâĂ©quivalent en WhyML dans le code 4 affiche une syntaxesimilaire.
1 function test = |a| {2 return a3 }4
5 function appel_test = {6 test(1) # renvoie 17 }
Code 3: Appel de fonction Golo
1 let test (a) =2 (a)3
4 let appel_test =5 test (1) (* renvoie 1 *)
Code 4: Appel de fonction WhyML
Les appels de fonctions en Golo peuvent gĂ©nĂ©rer des crashs au moment delâexĂ©cution si une fonction appelĂ©e nâexiste pas. Je mâintĂ©resse donc ici Ă vĂ©rifierstatiquement lâexistence dâune fonction lors dâun appel. Cette vĂ©rification estopĂ©rĂ©e directement lors de la traduction, et est dĂ©taillĂ©e dans la description demon implĂ©mentation.
Une difficultĂ© importante de la traduction des appels de fonctions vient dufait que les fonctions en WhyML on besoins dâĂȘtre dĂ©finies avant dâĂȘtre utilisĂ©esdans le code, alors que ce nâest pas le cas en Golo. Il est possible dâutiliserdes prototypes en WhyML pour les cas ou les fonctions sont dĂ©finies aprĂšs ĂȘtreappelĂ©es, cependant il est nĂ©cessaire de prĂ©ciser les types des paramĂštres ainsique le type de retour de la fonction. Je montre dans le code 5 un exemple deprototype.
12
1 val test (a: int): int (* prototype *)2
3 let appel_test =4 test (1) (* renvoie 1 *)5
6 let test (a) =7 (a)
Code 5: Prototype WhyML
Un autre Ă©lĂ©ment nĂ©cessaire Ă tout programme Golo simple est la dĂ©clarationde constantes et variables. Je mâintĂ©resse Ă leur traduction en WhyML dans lapartie suivante.
5.3 DéclarationsGolo fait la différence entre les variables et les constantes. Le compilateur
possĂšde dĂ©jĂ un certain nombre de vĂ©rification statiques Ă ce niveau, tel quela vĂ©rification quâune constante nâest pas modifiĂ©e : il nâest donc pas nĂ©cessairede rĂ©-implĂ©menter ces vĂ©rifications. Un autre Ă©lĂ©ment clĂ© est le fait que toutevariable ou constante dĂ©clarĂ©e dans un programme Golo doit ĂȘtre initialisĂ©e :une dĂ©claration va toujours de pair avec une assignation, dont la traduction estdĂ©taillĂ©e dans la partie suivante.
Un exemple de dĂ©claration dâune constante et dâune variable est montrĂ© dansle code 6 .
1 let myCons = 12 var myVar = 2
Code 6: DĂ©clarations Golo
LâĂ©quivalent Ă ce code Golo peut ĂȘtre Ă©crit en WhyML comme montrĂ© dansle code 7 .
1 import ref.Ref2 (* ... *)3 let myCons = 1 in4 let myVar = ref 2 in
Code 7: DĂ©clarations WhyML
Les variables WhyML sont en rĂ©alitĂ© des rĂ©fĂ©rences (de la librairie standardref.Ref) avec un Ă©lĂ©ment mutable. Cela implique que lâassignation dâune nouvelle
13
valeur Ă une variable se fait diffĂ©remment que lâassignation lors de la dĂ©claration,dĂ©taillĂ© dans la prochaine partie.
5.4 Assignation sur une variableJe dĂ©cris ici comment lâassignation dâune nouvelle valeur Ă une variable est
faite. Nous avons vu dans la partie prĂ©cĂ©dente lâassignation lors de la dĂ©claration,je ne reviens donc pas déçu.
Je montre dans le code 8 comment assigner une nouvelle valeur Ă une rĂ©fĂ©-rence WhyML. Il faut noter ici une limitation importante de WhyML : lors dela dĂ©claration dâune rĂ©fĂ©rence, un type est dĂ©fini (implicitement), il nâest doncpas possible dâassigner un Ă©lĂ©ment dâun type diffĂ©rent Ă cette variable. GoloĂ©tant dynamiquement typĂ© et permettant ce comportement, je propose une al-ternative pour pouvoir traduire un code Ă©quivalent en WhyML : re-dĂ©clarer lavariable avec le nouveau type, comme montrĂ© dans le code 9 . Il faut cependantfaire attention Ă la portĂ©e de la nouvelle dĂ©claration qui peut ĂȘtre moindre quela dĂ©claration originale, ainsi quâaux cas ou des effets de bords disparaĂźtraientĂ cause de la redĂ©finition.
1 import ref.Ref2 (* ... *)3 let myVar = ref 2 in4 myVar := 5 (* myVar est maintenant 5 *)
Code 8: Assignation WhyML
1 import ref.Ref2 (* ... *)3 let myVar = ref (1, 2) in (* myVar est un tuple dâint *)4 let myVar = ref 5 in (* myVar est maintenant un int *)
Code 9: Assignation WhyML avec type différent
Je montre dans la partie suivante comment peuvent ĂȘtre utilisĂ©es des va-riables et constantes (accĂšs en lecture).
5.5 Consultation de constantes et variablesEn Golo, la lecture dâune constante ou dâune variable est effectuĂ©e de la mĂȘme
maniĂšre, il suffit dâutiliser le nom de cette constante/variable pour y accĂ©der.En WhyML cependant, les variables Ă©tant construites avec des rĂ©fĂ©rences, leurmĂ©thode dâaccĂšs est diffĂ©rente des constantes pour lequel utiliser le nom (commeen Golo) est suffisant.
14
Je montre dans le code 10 comment accĂ©der Ă la valeur dâune variable enWhyML. Ce constat dâun accĂšs diffĂ©rent entre constantes et variable a un im-pact lors de lâimplĂ©mentation du traducteur, car un accĂšs ne se traduira pas dela mĂȘme façon en fonction du type dâĂ©lĂ©ment accĂ©dĂ©. Je dĂ©taille cette problĂ©-matique dans la description de mon implĂ©mentation.
1 import ref.Ref2 (* ... *)3 let myVar = ref 1 in4 !myVar (* renvoie la valeur 1 *)
Code 10: AccĂšs Ă une variable en WhyML
Dans la partie suivante je montre comment effectuer des opérations simplessur ces éléments, avec le support de quelques opérateurs.
5.6 OpérateursParmi les fonctionnalités de base du langage Golo figurent les opérateurs. Ils
sont au nombre de dix 1, et je vise ici Ă traduire les plus communs (opĂ©rationset comparaisons numĂ©riques) pour poser une premiĂšre base, la traduction desautres opĂ©rateurs pouvant ĂȘtre Ă©tudiĂ©e ultĂ©rieurement.
Le problĂšme de traduction des opĂ©rateurs est en rĂ©alitĂ© plus complexe quâilnây parait au premier abord en raison du typage strict de WhyML, et du fait quâilnâest pas possible de surcharger une fonction en WhyML. Ainsi, lâopĂ©rateur "+"en Golo peut effectuer une addition sur des nombres et des chaĂźnes de caractĂšres,cependant en WhyML chaque opĂ©ration sur des types diffĂ©rents doit ĂȘtre dĂ©finiedans des fonctions diffĂ©rentes. Jâai donc dĂ©cidĂ© de restreindre le problĂšme auxopĂ©rations sur les nombres entiers (int) dans un premier temps, pour simplifierle traitement de la traduction.
La libraire standard WhyML propose une implĂ©mentation des opĂ©rateursde base dans diffĂ©rentes thĂ©ories. Certaines de ces thĂ©ories viennent ajouter desvĂ©rifications statiques, comme la division dĂ©finie dans "mach.int" qui ajoute uneprĂ©-condition sur le dĂ©nominateur (doit ĂȘtre non-nul). Lâutilisation de ces thĂ©o-ries permet dĂ©jĂ dâajouter des vĂ©rifications statiques sur du code sans spĂ©cifica-tion. Le code 11 montre un exemple de code WhyML dâune division arithmĂ©tiquedâentiers.
Dans le cas dâune fonction effectuant une division, et avec une dĂ©finition dela division ajoutant une prĂ©-condition sur le dĂ©nominateur, il faut permettre auprogrammeur dâajouter de la spĂ©cification au programme pour que Why3 puissevalider le code. Je dĂ©taille dans la partie suivante comment la spĂ©cification esttraduite entre Golo et WhyML.
1. addition numĂ©rique et de chaĂźnes de caractĂšres, soustraction numĂ©rique, multiplicationnumĂ©rique et de chaĂźnes de caractĂšres, division numĂ©rique, modulo numĂ©rique, comparaisonnumĂ©rique et dâobjets, comparaison de qualitĂ© de rĂ©fĂ©rences, opĂ©rateur boolĂ©en, vĂ©rificationde type, valeur si null
15
1 import mach.int2 (* ... *)3 let a = 4 in4 let b = 2 in5 let c = Int.(/) a b in (* c = 2 *)
Code 11: Division WhyML
5.7 SpĂ©cification de programmePour pouvoir spĂ©cifier le code Golo, jâai choisi dâutiliser directement la spĂ©ci-
fication de lâoutil Why3 en Golo, la syntaxe Ă©tant claire et simple Ă comprendre.Il nây a ainsi pas de traduction Ă faire, il sâagit simplement de pouvoir parserla spĂ©cification (lâimplĂ©mentation dans le parseur est dĂ©taillĂ©e dans une partiesuivante). Je choisi dâutiliser des balises "spec/" et "/spec" pour placer la spĂ©cifi-cation, ce qui simplifie la lecture et Ă©vite le mĂ©lange avec le code Golo exĂ©cutable.Le code 12 montre un exemple dâune fonction avec de la spĂ©cification.
1 function max = |a, b| spec/ ensures{ (result >= a) /\2 (result >=b) /\3 (forall z:int. z>=a /\ z>=b -> z >= result) }4 /spec {5 if(a >= b) {6 return (a)7 } else {8 return (b)9 }
10 }
Code 12: Spécification dans Golo
Il faut noter ici que la spĂ©cification Why3 fait correspondre directementla valeur de retour dâun programme Ă la variable de spĂ©cification "result". Ilfaut donc Ă©viter dâutiliser des variables ou constantes appelĂ©es "result" dans leprogramme, ce qui pourrait crĂ©er des conflits lors de lâanalyse statique (unealternative Ă©tant de renommer lors de la traduction).
Un Ă©lĂ©ment manquant pour pouvoir traduire des programmes simples enGolo est une gestion de "return", câest Ă dire pouvoir retourner une valeur etquitter une fonction avant la fin de celle-ci. Je dĂ©taille comment traduire celaen WhyML dans la partie suivante.
5.8 Gestion du "return"Une fonction Golo renvoie toujours une valeur. Si une fonction ne renvoie rien
explicitement, alors la valeur "null" est renvoyée. De plus, comme dans la plupart
16
des langages de programmation orientĂ©s objets, il est possible de renvoyer unevaleur avant la fin dâune fonction, sans exĂ©cuter le reste. WhyML ne possĂšdepas de "return", et pour pouvoir dĂ©finir un comportement identique jâutilise lemĂ©canisme dâexceptions, ainsi que la variable "return" qui est utilisĂ©e par dĂ©fautpour dĂ©finir la valeur de retour dâune fonction.
Je rappelle ici que je prends le cas particulier dâune fonction qui ne peutrenvoyer que des entiers. Pour pouvoir traduire les fonctions qui ne renvoientpas de valeur explicitement, je dĂ©fini Ă©galement une nouvelle constante de typenull. Un exemple de fonction avec une valeur de retour est montrĂ© dans le code13 (cette fonction renvoie null si a >= 10, ou a sinon).
1 exception Return ()2 constant null : int3
4 let lowerThanTen (a) =5 let return = ref 0 in try begin (6 if (Int.(â„) a 10) then (7 return := null; (* attribue la valeur null Ă return *)8 raise Return ) (* raise lâexception *)9 else (
10 return := a;11 raise Return )12 ) end with Return â !return (* exception renvoie valeur de
return *)
Code 13: Return WhyML
Les Ă©lĂ©ments de base de la traduction de Golo vers WhyML Ă©tant posĂ©s, jedĂ©cris dans la partie suivante lâimplĂ©mentation qui a Ă©tĂ© faite dans le compilateurGolo.
6 ImplĂ©mentationLâimplĂ©mentation de la traduction automatique de code Golo vers du code
WhyML est faite directement dans le compilateur Golo, qui est écrit en Java.La version utilisée pour le développement est le snapshot 3.2.0-M1.
La traduction de code Golo pour la vĂ©rification statique dans lâoutil Why3Ă©tant un nouvel Ă©lĂ©ment du compilateur, une nouvelle commande appelĂ©e "goloverify" lui est associĂ©. Dans la figure 3 apparaissent les Ă©lĂ©ments principaux dela fonction "golo verify" implĂ©mentĂ©e pendant ce stage.
17
Figure 3 â ĂlĂ©ments clĂ©s de Golo Verify
GoloVerify Parseur
ArbreSyntaxiqueAbstrait
ReprésentationIntermédiaire
VisiteurReprésentationIntermédiaire
CodeWhyML
Le compilateur Golo utilise un gĂ©nĂ©rateur de parseur qui se base sur unedĂ©finition de grammaire pour gĂ©nĂ©rer un arbre syntaxique abstrait. Une fois cetarbre gĂ©nĂ©rĂ©, des visiteurs le parcourent pour effectuer quelques vĂ©rifications(par exemple, tester si une constante nâest pas rĂ©-assignĂ©e), ainsi que pour dĂ©-sugariser le code. Cela produit une reprĂ©sentation intermĂ©diaire qui reprĂ©senteles Ă©lĂ©ments Golo sous forme dâobjets Java.
Mon implĂ©mentation est concentrĂ©e en grande partie sur la reprĂ©sentationintermĂ©diaire, aprĂšs lâappel Ă des visiteurs sur lâarbre abstrait. Travailler surcette reprĂ©sentation intermĂ©diaire a Ă©galement pour avantage de respecter lelangage Golo sans avoir Ă rĂ©-implĂ©menter la sĂ©mantique, contrairement Ă lâarbresyntaxique abstrait qui lui est issu du parseur et nâa pas Ă©tĂ© validĂ©. Je dĂ©crisdans les parties suivantes les Ă©lĂ©ments clĂ©s de lâimplĂ©mentation rĂ©alisĂ©e, ainsique leurs implications.
6.1 Grammaire JJTree et arbre syntaxique abstraitPour pouvoir ajouter un nouveau type de code Golo qui soit reconnu par
le parseur, il faut modifier la dĂ©finition de la grammaire Golo. Le compilateurutilise JJTree, un prĂ©-processeur pour JavaCC qui gĂ©nĂšre un parseur Ă partirdâune dĂ©finition de grammaire. Ce parseur produit ensuite un arbre syntaxiqueabstrait.
Dans un premier temps, pour reconnaßtre un bloc de spécification, un nou-veau token SPEC est défini comme montré dans le code jjt 14 .
Ce token est utilisé pour récupérer directement la spécification dans unString, comme montré dans le code 15 .
Cet Ă©lĂ©ment de spĂ©cification est appelĂ© dans la dĂ©finition dâune fonction, quiest actuellement le seul endroit ou lâon est autorisĂ© Ă mettre de la spĂ©cificationdans le code Golo. Pour pouvoir supporter de la spĂ©cification nâimporte ou dansle code, il faudrait dĂ©finir un nouveau noeud pour lâarbre abstrait ainsi que pourla reprĂ©sentation intermĂ©diaire pour la spĂ©cification.
Dans les parties suivantes, je dĂ©taille lâimplĂ©mentation de quelques Ă©lĂ©mentsdu visiteur de la reprĂ©sentation intermĂ©diaire qui sâoccupe de gĂ©nĂ©rer la traduc-
18
MORE :{ < " spec /" > : WithinSpec }
<WithinSpec>TOKEN :{ < SPEC : "/ spec " > : DEFAULT }
<WithinSpec>MORE :{ < ~ [ ] > }
Code 14: Token SPEC
Str ing S p e c i f i c a t i o n ( ) #void :{Token specToken ; }{ specToken = <SPEC>
{ return specToken . image . sub s t r i ng (5 ,specToken . image . l ength ()â5) . tr im ( ) ; }
}
Code 15: Token SPEC
tion en WhyML.
19
6.2 ModuleLâimplĂ©mentation de la traduction dâun module est un bon exemple pour
montrer comment le visiteur de la reprĂ©sentation intermĂ©diaire fonctionne. Lafonction "visitModule" est la premiĂšre appelĂ©e et est donc le point dâentrĂ© dela traduction. Les Ă©lĂ©ments les plus importants de cette fonction sont montrĂ©sdans le code 16 .
"whyMLcode" est un ArrayList de String qui permet de stocker le codeWhyML gĂ©nĂ©rĂ©. La ligne 2 dĂ©clare le module en WhyML, et les lignes 4 Ă 9ajoutent dans le code WhyML les imports de base pour les fonctions qui sontactuellement supportĂ©es, ainsi que les dĂ©clarations de lâexception Return et dela constante "null". Une amĂ©lioration nĂ©cessaire sur cette partie serait dâenleverles imports statiques, et de les ajouter lorsquâils sont nĂ©cessaires (par exemple,importer mach.int.Int uniquement si des opĂ©rations arithmĂ©tiques sont effec-tuĂ©es).
La ligne 11 appel le visiteur sur la totalitĂ© du contenu du module. Une fois laligne 11 exĂ©cutĂ©e, le code du module est normalement entiĂšrement traduit, et laligne 12 vient marquer la fin du module. Le bloc try/catch Ă©crit le code gĂ©nĂ©rĂ©dans le fichier de destination. Finalement, un dernier test est effectuĂ© avant dequitter le visiteur Ă la ligne 18 : les dĂ©finitions de fonctions ainsi que les appelsde fonctions ont Ă©tĂ© enregistrĂ©s durant la visite, et ce test vĂ©rifie lâexistence detoute fonction appelĂ©e. Une des limitations actuelles de cette implĂ©mentationest le fait que les fonctions intĂ©grĂ©es de base ne sont pas gĂ©rĂ©es (cependant,leur traduction non plus, il est donc raisonnable dâafficher une erreur). CettevĂ©rification fonctionne Ă©galement lorsquâune fonction est appelĂ©e avec une aritĂ©supĂ©rieure Ă celle de la fonction dĂ©finie, Ă©tant donnĂ© que Golo rend cela possible.
Ătant donnĂ© quâun module est trĂšs souvent suivi de la dĂ©finition dâune fonc-tion, je prĂ©sente dans la partie suivante lâimplĂ©mentation de la traduction dâunefonction.
20
1 /* ... */2 whyMLcode.add("module " + moduleName)3 /* ... */4 whyMLcode.add(space() + "use import int.Int");5 whyMLcode.add(space() + "use import ref.Ref");6 whyMLcode.add(space() + "use import mach.int.Int");7 whyMLcode.add(space() + "type null");8 whyMLcode.add(space() + "constant null : int");9 whyMLcode.add(space() + "exception Return ()");
10
11 module.walk(this);12 whyMLcode.add(space() +"end");13 try {14 Files.write(Paths.get(destFile), whyMLcode, Charset.forName("UTF-8"));15 } catch (IOException e) {16 e.printStackTrace();17 }18 testFunctionsCalledExist();
Code 16: ĂlĂ©ments de la fonction visitModule
6.3 FonctionLa traduction dâune fonction est un Ă©lĂ©ment un peu plus complexe que la
simple définition du module. Le code est donc présenté par morceau pour desraisons de clarté.
Les premiers Ă©lĂ©ments dâune fonction, dĂ©crits dans le code 17 montrent prin-cipalement lâajout de la fonction Ă la liste des fonctions dĂ©clarĂ©es (ligne 1, 2),puis la dĂ©claration de la fonction dans le code WhyML (ligne 3 et 5). La ligne4 vient ajouter les nombres de lignes auxquelles cette fonction apparaĂźt dans lefichier Golo pour les afficher dans lâoutil Why3. On peut remarquer que lâim-plĂ©mentation est lĂ©gĂšrement diffĂ©rente de la traduction dâune fonction dĂ©finiedans la partie thĂ©orique prĂ©cĂ©dente : lâutilisation de "let function = fun ->" estnĂ©cessaire pour pouvoir insĂ©rer les numĂ©ros de lignes.
1 functionsDefined.add(new functionNameArity(function.getName(),2 function.getArity()));3 whyMLcode.add(space() + "let " + function.getName() + " ");4 appendWhyMLLastString(getSourceCodeBlocksLines(function) + " = ");5 whyMLcode.add(space() + "fun ");
Code 17: DĂ©finition de fonction
Le code 18 montre la traduction des paramĂštres de la fonction. Deux cas
21
sont possibles : si la fonction possÚde des paramÚtres, ceux-ci sont ajoutés entreparenthÚses, sinon des parenthÚses vides sont écrites. Cet ajout est nécessaireà WhyML, une fonction sans paramÚtre en Golo ne nécessitant pas de balisesvides. La derniÚre ligne vient copier la spécification (si présente) depuis ce quia été parsé directement dans le code WhyML.
1 for (String param : function.getParameterNames()) {2 appendWhyMLLastString("( " + param + " )");3 }4 if (function.getParameterNames().isEmpty()){5 appendWhyMLLastString("()");6 }7 whyMLcode.add(space()+function.getSpecification());
Code 18: ParamĂštres de la fonction
Le code 19 montre la dĂ©claration des rĂ©fĂ©rences locales, effectuĂ©es avant lecorps de la fonction. Cette implĂ©mentation est nĂ©cessaire, les variables (rĂ©fĂ©-rences) en WhyML nĂ©cessitant dâĂȘtre dĂ©clarĂ©es avant leur initialisation/utili-sation. Il y a une limitation majeure cependant : la portĂ©e des variables estautomatiquement toute la fonction. Ainsi, une variable qui en Golo ne serait vi-sible quâĂ lâintĂ©rieur dâune boucle est visible en WhyML dans la fonction entiĂšreavec cette implĂ©mentation. Des vĂ©rifications additionnelles sur la rĂ©utilisationdes noms de variables ainsi que sur lâexistence dâune variable sachant sa portĂ©esont nĂ©cessaire pour rendre lâimplĂ©mentation complĂštement correcte.
1 LinkedList<LocalReference> refList = function.returnOnlyReference(this);2 for (LocalReference ref: refList) {3 boolean isParam = false;4 for (String param: function.getParameterNames()){5 if (ref.getName().equals(param)) {6 isParam = true;7 }8 }9 if(!isParam && !ref.isConstant()){
10 whyMLcode.add(space() + "let " + ref.getName() + " = ref 0 in ");11 }12 }
Code 19: Déclaration des références locales
Finalement, le code 20 montre les derniers Ă©lĂ©ments de la traduction dâunefonction : la rĂ©fĂ©rence return est dĂ©clarĂ©e en ligne 1, avant de lancer la visite ducorps de la fonction (et donc la gĂ©nĂ©ration de sa traduction). La ligne 3 vientfermer la fonction et ajouter lâexception qui gĂšre lâappel Ă "return".
22
1 whyMLcode.add(space() + "let return = ref 0 in try begin");2 function.walk(this);3 whyMLcode.add(space() + "; end with Return -> !return end");
Code 20: Return et corps de la fonction
6.4 Constantes et variablesDans la présentation théorique de la traduction, une difficulté autour de la
gestion diffĂ©rente des constantes et variables en WhyML, comparĂ© Ă Golo, avaitĂ©tĂ© relevĂ©e. Je montre ici comment lâimplĂ©mentation de la traduction permet derĂ©soudre ces diffĂ©rences.
Dans un premier temps, lors dâune assignation, il suffit de gĂ©rer deux caspour que la traduction gĂ©nĂšre du code WhyML correct. Ces cas sont montrĂ©sdans le code 21. Dans le cas ou la rĂ©fĂ©rence est dĂ©finie comme constante dans lareprĂ©sentation intermĂ©diaire, le code "let refName =" est gĂ©nĂ©rĂ© avant de visiterce qui est assignĂ© avec la fonction "walk". Sinon, la rĂ©fĂ©rence est donc variableet lâopĂ©rateur " := " est utilisĂ© avant de visiter lâĂ©lĂ©ment assignĂ©.
1 if (assignmentStatement.getLocalReference().isConstant()) {2 whyMLcode.add(space() + "let " + refName + " = ");3 assignmentStatement.walk(this);4 whyMLcode.add(space() + " in");5 } else {6 whyMLcode.add(space() + "( " + refName + " := ");7 assignmentStatement.walk(this);8 whyMLcode.add(space() + " );");9 }
Code 21: Assignation de constantes et variables
Dâune façon similaire lors de la lecture dâune constante ou variable, le code22 montre le mĂȘme type de procĂ©dĂ© pour sĂ©parer les deux cas possibles : copieruniquement le nom de la constante dans le code WhyML, ou ajouter " !" devantsâil sâagit dâune variable.
1 if (reference.isConstant()) {2 whyMLcode.add(space() + "( " + referenceLookup.getName() + " ) ");3 } else {4 whyMLcode.add(space() + "( !" + referenceLookup.getName() + " ) ");5 }
Code 22: Lecture de constantes et variables
23
Les autres Ă©lĂ©ments sont traduits avec des mĂ©thodes similaires et ne sont doncpas dĂ©taillĂ©s dans ce rapport (le code source complet Ă©tant fourni). Je montredans la prochaine partie quelques exemples de programmes Golo traduits enWhyML et analysĂ©s dans lâoutil Why3.
7 Preuve dans Why3Je montre dans cette partie des exemples de code Golo analysĂ©s dans lâoutil
Why3. Lâobjectif est de pouvoir identifier quelques applications rendues possiblepar ma contribution.
Le premier exemple montrĂ© sur la figure 4 est la dĂ©finition dâune fonction"max". On peut lire dans le coin en bas Ă gauche le code Golo accompagnĂ©de la spĂ©cification de la fonction. Dans le coin supĂ©rieur droit apparaissent lesconditions de vĂ©rification gĂ©nĂ©rĂ©es par Why3. Le prouveur Alt-Ergo a Ă©tĂ© lancĂ©pour la fonction et a pu la valider.
Figure 4 â Preuve de la fonction max
24
Les trois exemples suivants sont basĂ©s sur la mĂȘme fonction, effectuant ladivision de deux entiers. La figure 5 montre la fonction effectuant la division sansspĂ©cification. On peut voir que le preuve nâest pas validĂ©e par le prouveur. Celaest dĂ» Ă la condition de vĂ©rification "not b = 0" de la fonction div, en effet, riennâempĂȘche dâappeler la fonction avec b=0 ce qui ferait crasher le programme.
Figure 5 â Preuve de la fonction div sans spĂ©cification
Il est attendu que si le programme prends en compte le cas particulier oĂčb=0, alors la preuve doit ĂȘtre valide. On peut vĂ©rifier cela avec la figure 6 : cettefois la vĂ©rification nâĂ©choue pas, le cas ou b=0 Ă©tant traitĂ© sĂ©parĂ©ment.
Dans le cas oĂč lâon souhaite conserver la fonction sans la modifier, il y a lapossibilitĂ© de rajouter une ligne de spĂ©cification pour permettre la vĂ©rification.La figure 7 montre dans un premier temps quâavec une prĂ©-condition sur b,alors la fonction div passe la vĂ©rification. Cependant, on voit maintenant que lafonction test ne passe pas entiĂšrement la vĂ©rification : la deuxiĂšme prĂ©-conditionnâest pas rĂ©solue. Ce comportement est attendu, la seconde ligne de la fonctionappelant la fonction div avec le paramĂštre b=0, ce qui est dĂ©sormais interditpar la spĂ©cification ajoutĂ©e.
On peut donc voir que spécifier une fonction permet de vérifier que ses appels
25
Figure 6 â Preuve de la fonction div avec if
sont corrects et ne généreront pas de crash.
26
Figure 7 â Preuve de la fonction div avec spĂ©cification
8 BilanPour rĂ©pondre Ă la problĂ©matique dâajout de confiance dans du code Golo,
jâai pris lâapproche de traduire le code Golo en WhyML pour lâanalyser stati-quement dans lâoutil Why3. Jâai dĂ©taillĂ© dans ce rapport mes choix techniques,ainsi que ma contribution, principalement centrĂ©e autour de lâimplĂ©mentationdâun traducteur automatique de code, intĂ©grĂ© dans le compilateur Golo.
Parmi les fonctionnalitĂ©s principales, on retrouve la vĂ©rification de lâexistencedes fonctions appelĂ©es dans le code, lâajout de prĂ©-conditions automatiquement(tel que dĂ©nominateur dâune division non-nul), et permis lâajout de spĂ©cificationde code. Cette implĂ©mentation est une premiĂšre base qui permet dâĂȘtre Ă©tenduepour supporter plus de fonctionnalitĂ©s dans le futur. Parmi les limitations ac-tuelles, on retrouve notamment : la limitation Ă un unique module Golo, les aritĂ©de fonctions devant ĂȘtre fixes, la limitation de la spĂ©cification aux fonctions, lasurcharge des opĂ©rateurs, la portĂ©e des variables, ainsi que "result" Ă©tant un nomrĂ©servĂ©.
Une des grandes difficultĂ©s qui doit ĂȘtre rĂ©solue pour pouvoir Ă©tendre la
27
portĂ©e de ce projet rĂ©side dans la gestion du typage : lâimplĂ©mentation ac-tuelle ne supporte que les entiers non bornĂ©s. Il est nĂ©cessaire, dans un premiertemps, de dĂ©finir une thĂ©orie pour utiliser des entiers bornĂ©s, puis de supporterdâautres types. Une piste de rĂ©flection pourrait ĂȘtre lâutilisation de structuresgĂ©nĂ©riques WhyML pour dĂ©crire tout Ă©lĂ©ment avec une description du type :genericType{mutable type ; mutable var}. Lâutilisation de ce type de structuresnĂ©cessiterait Ă©galement une redĂ©finition des Ă©lĂ©ments de la librairie standardpour redĂ©finir le comportement sur ce nouveau type (notamment les opĂ©ra-teurs).
9 Retour dâexpĂ©rienceCe stage mâa permis de dĂ©couvrir plus en dĂ©tail le monde de la recherche,
ainsi que des mĂ©thodologies que je nâavais pas rencontrĂ©es auparavant. La sĂ©pa-ration trĂšs claire durant les premiĂšres semaines entre lâĂ©tat de lâart et lâimplĂ©-mentation mâa permis de bien sĂ©parer la dĂ©finition du problĂšme et les solutionsenvisagĂ©es de lâimplĂ©mentation.
Nâayant jamais travaillĂ© sur lâanalyse statique auparavant, jâai appris beau-coup durant ce stage sur ce sujet. Jâai Ă©galement dĂ©couvert le fonctionnementdâun compilateur et dâun gĂ©nĂ©rateur de parseur Ă©crit en Java. Ces diffĂ©rentsaspects techniques nouveaux ont rendu ce stage encore plus intĂ©ressant, car ilsont permis une montĂ©e en compĂ©tences. Ces aspects totalement inconnus furentau dĂ©but la source de nombreuses difficultĂ©s, mais Ă©galement la source dâunegrande motivation une fois les premiĂšres barriĂšres tombĂ©es.
La contribution proposĂ©e est satisfaisante au niveau personnel, avec un Ă©tatde lâart riche et variĂ©, et une implĂ©mentation prĂ©liminaire dont jâestime lesrĂ©sultats encourageants pour le futur.
28
Références[1] Julien Ponge, Frédéric Le Mouël, and Nicolas Stouls. Golo, a dynamic, light
and efficient language for post-invokedynamic jvm. In Proceedings of the2013 International Conference on Principles and Practices of Programmingon the Java Platform : Virtual Machines, Languages, and Tools, PPPJ â13,pages 153â158, New York, NY, USA, 2013. ACM.
[2] Golo - a lightweight dynamic language for the jvm. http://golo-lang.org/.
[3] François Bobot, Jean-Christophe FilliĂątre, Claude MarchĂ©, and Andrei Pas-kevich. Why3 : Shepherd your herd of provers. In Boogie 2011 : First In-ternational Workshop on Intermediate Verification Languages, pages 53â64,Wroclaw, Poland, 2011.
[4] Claude MarchĂ© and Christine Paulin-Mohring. Reasoning about java pro-grams with aliasing and frame conditions. In Proceedings of the 18thInternational Conference on Theorem Proving in Higher Order Logics,TPHOLsâ05, pages 179â194, Berlin, Heidelberg, 2005. Springer-Verlag.
[5] Jean-Christophe FilliĂątre and Andrei Paskevich. Why3 â where programsmeet provers. In ESOPâ13 22nd European Symposium on Programming,volume 7792, Rome, Italy, March 2013. Springer.
[6] Gary T. Leavens, Albert L. Baker, and Clyde Ruby. Jml : a java modelinglanguage. In In Formal Underpinnings of Java Workshop (at OOPSLAâ98),1998.
[7] Nadia Polikarpova and Scott Furia, Carlo A.and West. Runtime Verifica-tion : 4th International Conference, RV 2013, Rennes, France, September24-27, 2013. Proceedings, chapter To Run What No One Has Run Before :Executing an Intermediate Verification Language, pages 251â268. SpringerBerlin Heidelberg, 2013.
[8] François Bobot, Sylvain Conchon, E Contejean, Mohamed Iguernelala, Sté-phane Lescuyer, and Alain Mebsout. The alt-ergo automated theorem pro-ver, 2008. http://alt-ergo.lri.fr/, 2013.
[9] K. Rustan M. Leino and Rosemary Monahan. Reasoning about compre-hensions with first-order smt solvers. In Proceedings of the 2009 ACMSymposium on Applied Computing, SAC â09, pages 615â622, New York,NY, USA, 2009. ACM.
[10] Carlo A. Furia, Bertrand Meyer, and Sergey Velder. Loop invariants :Analysis, classification, and examples. ACM Comput. Surv., 46(3) :34 :1â34 :51, Jan 2014.
[11] Boogie : A Modular Reusable Verifier for Object-Oriented Programs, 2005.[12] Renaud Pawlak, Carlos Noguera, and Nicolas Petitprez. Spoon : Program
analysis and transformation in java. Research Report RR-5901, Inria, 2006.[13] K. Rustan M. Leino. Logic for Programming, Artificial Intelligence, and
Reasoning : 16th International Conference, LPAR-16, Dakar, Senegal,
29
April 25âMay 1, 2010, Revised Selected Papers, chapter Dafny : An Auto-matic Program Verifier for Functional Correctness, pages 348â370. SpringerBerlin Heidelberg, Berlin, Heidelberg, 2010.
[14] Michael Ameri and Carlo A. Furia. Why just boogie ? translating betweenintermediate verification languages. CoRR, abs/1601.00516 :â, 2016.
30