1.13k likes | 1.41k Views
ESC/Java de digital (esc/modula-3) devenu compaq. ESC/Java version 1.2.4, 27 September 2001 Cnam/Paris jean-michel Douin, douin@cnam.fr Version du 25 Mars 2003 Présentation et exemples d’utilisation Ces notes de cours accompagnent ou précèdent
E N D
ESC/Javade digital (esc/modula-3) devenu compaq ESC/Java version 1.2.4, 27 September 2001 Cnam/Paris jean-michel Douin, douin@cnam.fr Version du 25 Mars 2003 Présentation et exemples d’utilisation Ces notes de cours accompagnent ou précèdent une première lecture manuel d’utilisation de ESC La dernière version est à cette url : http://jfod.cnam.fr/tp_cdi/douin/ESC_Java.pdf
Bibliographie Utilisée L’outil ESC/Java à télécharger, le manuel de l’utilisateur et d’autres documents • http://research.compaq.com/SRC/esc/ Une présentation de Rustan Leino, faite au Static Analysis Symposium 2001, SAS ’01 July, La Sorbonne, Paris • http://www.research.compaq.com/SRC/personal/rustan/papers/ApplicationsOfESC.ppt Les notes de cours de Dwyer et Hatcliff ( le support de « Week 15 et 16 ») • http://www.cis.ksu.edu/~hatcliff/771/ Cours Cnam/CDL B3, notes de J-L Dewez Avril 2002 • http://lmi90.cnam.fr:6666/ESC.pdf ESC/Java et la JavaCard Le travail de erik Poll sur les API Javacard annotées en esc/java • http://www.cs.kun.nl/~erikpoll/publications/jc211_specs.html JavaCard GEMPlus/INRIA : le projet lemme • http://www-sop.inria.fr/lemme/verificard/electronic_purse/ et ftp://ftp-sop.inria.fr/lemme/Marieke.Huisman/paper.ps.gz Esc/java et les Threads La thèse de Cyrille Artho, un comparatif des outils statiques • http://www.inf.ethz.ch/~artho/papers/mthesis.pdf et http://www.inf.ethz.ch/~artho/slides/ArthoBiere-ASWEC2001.pdf De Silvija Seres, Oxford • http://gatekeeper.dec.com/pub/DEC/SRC/technical-notes/SRC-1999-003-html/seres.html A voir ensuite Le projet LOOP : vers la preuve ? • http://www.cs.kun.nl/~bart/LOOP/ JML, Java Modeling language • http://www.cs.iastate.edu/~leavens/JML.html Bandera cours 842, université du Kansas (voir le support Week 7) • http://www.cis.ksu.edu/~hatcliff/842/ Certains éléments de cette liste se trouvent en http://jfod.cnam.fr/tp_cdi/douin/biblio_ESC_Java/
Sommaire • Objectifs • Comment • Présentation: un premier exemple • assert, assume, unreachable, loop_invariant • Programmation par contrats • requires, ensures, modifies (\old, \result, \fresh…) • Invariant de classe • Programmation concurrente • \monitored, \monitored_by, \lockset, \max, e < f, • Conclusion ? • Première étape vers une preuve formelle ? • LOOP, Bandera, …
Objectifs • Approche pragmatique et simple • Annotations en syntaxe java, voir JLS(Java Language Specification) • Aide à la détection d’erreurs « classiques » de programmation • Null, dépassement de bornes,erreur de typage, interblocage, …. • Vérification de la cohérence des annotations et du programme. Attention ce n’est pas une preuve ! • ESC/Java’s extended static checking isn’t perfect (un paragraphe du manuel) • « Vérification » par réfutation : ESC est à la recherche d’erreurs possibles, par un contre-exemple • ==> Annotations des méthodes sensibles, • ==> Commentaires pertinents (sans ambiguïté)
Static program checking Extrait de la présentation de Rustan Leino
Static program checkers Extrait de la présentation de Rustan Leino
ESC architecture Extrait de la présentation de Rustan Leino
Extended Static Checker Architecture Java method + annotations Translator Verification conditions Automatic theorem prover Counterexamples Post-processor Warning messages ESC/Java The translator “understands” the semantics of Java. A verification condition is a logical formula that, ideally, is valid if and only if the program is free of the kinds of error under consideration. The automatic theorem prover is invisible to users. Counterexamples are turned into precise warning messages. Index out of bounds on line 218 Method does not preserve object invariant on line 223
ESC/Java Analyse statique du source Java Invisible au programmeur … • Détection d’erreurs ( qui sont levées d’ordinaire à l’exécution) • null dereferences • Dépassement des bornes d’un tableau • Changement de type hasardeux • … • Cohérence des annotations et du programme • Pre conditions • Post conditions • Invariant de classe • Environnement multi-”thread” • Accès concurrents à une variable non-protégée (Race) • Interblocage (DeadLocks)
Un tout premier exemple public class TestESC1{ public static void test(String[] args){ args[2] = "esc"; } }
Un tout premier exemple public class TestESC1{ public static void test(String[] args){ args[2] = "esc"; } } dos>escjava TestESC1.java ESC/Java version 1.2.4, 27 September 2001 TestESC1 ... TestESC1: test(java.lang.String[]) ... -------------------------------------------------------------- TestESC1.java:3: Warning: Possible null dereference (Null) args[2] = "esc"; ^ --------------------------------------------------------------- TestESC1.java:3: Warning: Array index possibly too large (IndexTooBig) args[2] = "esc"; ^ …
ESC/Java : les options escjava: usage: escjava options* <source files> where options include: -v -bootclasspath <classpath> -classpath <classpath> -depend -f <file containing source file names> -counterexample -loopSafe -loop <iterCount>[.0|.5] -nocheck -notrace -nowarn <category> -plainwarning -quiet -routine <routineId> -routine <fullyQualifiedRoutineSignature> -routineIndirect <routineFile> -start <line#> -suggest -vclimit <n> -warn <category>
Options d’ ESC/Java : un exemple public class TestESC1{ public static void test(String[] args){ args[2] = "esc"; } } dos>escjava –nowarn Null –suggest TestESC1.java ESC/Java version 1.2.4, 27 September 2001 TestESC1 ... TestESC1: test(java.lang.String[]) ... -------------------------------------------------------------- TestESC1.java:6: Warning: Array index possibly too large (IndexTooBig) args[2] = "esc"; ^ Suggestion [6,8]: none -------------------------------------------------------------- …
Annotations • Annotations transformées en formules logiques • Assertions installées, (contrats à respecter) • ou affirmations (hypothèses décidées ) par le spécifieur/programmeur • À l’aide de pragmas • //@ assert une_expression_esc • //@ assume une_expression_esc • … • Les mots-clés utilisés dans les expressions esc sont préfixés par « \ » • \old, \return, \type(T), \typeof(E) … • L’exemple précédent • public class TestESC1{ • public static void test(String[] args){ • //@ assume args != null && args.length == 3; • // attention cette expression est une affirmation ... • args[2] = "esc"; • } • } • dos>escjava TestESC1.java • ==> No Warning
Annotations : Syntaxe • //@ un_pragma_esc • Exemple : //@ assert i >= 0; • /*@ un_pragma_esc une_expression_esc*/ • /*@ //commentaire : plusieurs pragmas • * un_pragma_esc une_expression_esc; • * un_pragma_esc une_expression_esc; • * // un commentaire • */ • /** à l’intérieur d’un commentaire destiné à javadoc • * <esc> un_pragma_esc une_expression_esc </esc> • */ • A voir : dans le manuel de l’utilisateur les possibilités de commentaires imbriqués
Pragmas : classification, expressions ESC • Pragma comme instructions, ( au sens java) • nowarn, assert, assume, unreachable, loop_invariant • Pragma comme modificateurs, • non_null, • spec_public, readable_if, unitialized, • requires, modifies, ensures, exsures, // par méthode • also_requires, also_modifies, also_ensures, also_exsures, // héritage • monitored, monitored_by, // concurrence • … • Pragma comme declarations, • invariant, axiom, • … • Expressions esc : expressions java et • ==> , < = = > , Quantificateurs \forall, \exists, \old , \result, \type(), …. • Déclarations et instructions pour les variables « esc » (appelées ghost variables) • ghost déclarations //@ ghost public Object o; • set instructions //@ set o = this;
Pragma nowarn, lexical • ArrayStore Invariant Post Assert LoopInv Pre Cast NegSize Race Deadlock NonNull Reachable Exception NonNullInit Unreadable IndexNegative Null Uninit IndexTooBig OwnerNull ZeroDiv Type des erreurs possibles recherchées par ESC • nowarn L ;opt L peut contenir le ou les items ci-dessus
Pragmas comme instruction • assert • employée comme une instruction Java, l ’assertion doit être vérifiée • assume • l ’expression devient une affirmation, sans aucun contrôle • unreachable • <==> assert false; • loop_invariant • L ’expression est vraie à chaque itération • decreases * • Le variant doit décroître à chaque tour de boucle • loop_predicate * • Les expressions esc sont constituées de : • \forall, \exists, ==>, <==, <==>, <=!=>, … • Exemple : t!=null & (\forall int i; i>=0 & i < t.length ==> t[i] != null) • * pragma non documenté
ESC/Java : premières limites • Les expressions esc ne peuvent contenir des appels de méthodes • « Simplify (le démonstrateur) has no built-in semantics for multiplication, except by constants. » • Attention aux « warning » qui ne semblent pas être justifiés … Warning: Array index possibly too large (IndexTooBig)
Exemple : une copie de tableaux /** CDI cours B3, ED 14, Ex06 Hoare et tableaux */ public class CopieDeTableaux { public static void main(/*@ non_null */String[] args){ int[] t = {1,9,11,15,56,78,89}; //@ assert (\exists int x; x>=0 & x<t.length & t[x]==89); int[] t1 = new int[t.length]; //@ assert t != null & t1 != null; //@ assert t.length == t1.length; int i = 0; int n = t.length-1; //@ loop_invariant (\forall int j; j>=0 & j < i ==> t[j] == t1[j]); while( i <= n){ t1[i] = t[i]; i++; } //@ assert (\forall int j; j>=0 & j <= n ==> t[j] == t1[j]); } }
Variant de boucle : « à la main » public class CopieDeTableaux { public static void main(/*@ non_null */String[] args){ int[] t = {1,9,11,15,56,78,89}; int[] t1 = new int[t.length]; //@ assert t != null & t1 != null; //@ assert t.length == t1.length; int i = 0; int n = t.length-1; //@ loop_invariant (\forall int j; j>=0 & j < i ==> t[j] == t1[j]); while( i <= n){ int v = n+1-i; // le variant v, ici installé dans le code source … //@ assert v > 0 t1[i] = t[i]; i++; //@ assert n+1-i < v; } //@ assert (\forall int j; j>=0 & j <= n ==> t[j] == t1[j]); } }
ghost + variant de boucle public class CopieDeTableaux { //@ ghost public static int v; public static void main(/*@ non_null */String[] args){ int[] t = {1,9,11,15,56,78,89}; int[] t1 = new int[t.length]; //@ assert t != null & t1 != null; //@ assert t.length == t1.length; int i = 0; int n = t.length-1; //@ loop_invariant (\forall int j; j>=0 & j < i ==> t[j] == t1[j]); while( i <= n){ //@ set v = n+1-i; // le variant v, en variable de spécifieur //@ assert v > 0 t1[i] = t[i]; i++; //@ assert n+1-i < variant; } //@ assert (\forall int j; j>=0 & j <= n ==> t[j] == t1[j]); } }
Variant de boucle \decreases public static void main(/*@ non_null */String[] args){ int[] t = {1,9,11,15,56,78,89,89}; //@ assert (\exists int x; x>=0 & x<t.length & t[x]==89); int[] t1 = new int[t.length]; //@ assert t != null & t1 != null; //@ assert t.length == t1.length; int i = 0; int n = t.length-1; //@ loop_invariant (\forall int j; j>=0 & j<i==>t[j]==t1[j]); //@ decreases n-i; // le variant de boucle while( i <= n){ t1[i] = t[i]; i++; } //@ assert (\forall int j; j>=0 & j <= n ==> t[j] == t1[j]); } }
TriDeTableaux public class TriDeTableaux{ public static void main(/*@ non_null */String[] args){ int [] n = {9,1,15,11,56,89,78}; //@ assert n =! null && n.length > 2; boolean sorted = false; while(!sorted){ sorted = true; //@ loop_invariant (\forall int j;(j>=0 & j < i) ==> n[j] <= n[j+1]); //@ decreases n.length-1-i; for(int i = 0; i < n.length-1; i++){ if (n[i] > n[i + 1]){ int temp = n[i]; n[i] = n[i + 1]; n[i + 1] = temp; sorted = false; } //@ assert n[i] <= n[i+1]; } } //@ assert (\forall int j;(j>=0 & j < n.length-1) ==> n[j] <= n[j+1]); }
Affirmations ? affirmatif ! public class TestIf { public static void test(boolean b, int h, int i, int j){ int r; //@ assume i != 0 & b == true; if(b) r = h/i; else r = h/i + h*j; } /*@ axiom (\forall int x, y; x >= 0 & y >= 0 ==> x*y >= 0); */ }
Programmation par Contrats • Un contrat est établi entre l’appelant et l’appelé • Pré-conditions et Post-conditions • qu’une méthode doit respecter • Invariants de classe
Pré/post conditions, invariant de classe • //@ requires • pré-conditions • //@ ensures • post_conditions • //@ modifies • ce qui est modifié par la méthode • //@ exsures • Pour les exceptions • //@ invariant • de classe, vrai avant et après l ’appel de chaque méthode, et vrai après l ’appel d ’un constructeur • \result, \old, \fresh • \result : le résultat d ’une fonction ( en liaison avec modifies) • \old : la valeur avant • \fresh(v) : v nouvelle allocation dans cette méthode • Attention Pas de contrôle du pragma \modifies
La pile (encore), les pré-conditions public class Pile{ private int[] zone; private int ptr; //@ requires taille > 0; public Pile(int taille){ zone = new int[ taille]; } //@ requires ptr < zone.length; // !estPleine(); public void empiler(int elt){ zone[ptr] = elt;ptr++; } //@ requires ptr > 0; // !estVide(); public int depiler(){ ptr--;return zone[ptr]; }
La pile, /*@ spec_public */ public class Pile{ private /*@ spec_public */ int[] zone; private /*@ spec_public */ int ptr; //@ requires taille > 0; public Pile(int taille){ zone = new int[ taille]; } //@ requires ptr < zone.length; public void empiler(int elt){ zone[ptr] = elt;ptr++; } //@ requires ptr > 0; public int depiler(){ ptr--;return zone[ptr]; }
La pile, les post conditions public class Pile{ private /*@ spec_public */ int[] zone; private /*@ spec_public */ int ptr; //@ requires taille > 0; //@ ensures zone != null & ptr == 0 && zone.length == taille; public Pile(int taille){ zone = new int[ taille]; } //@ requires ptr < zone.length; //@ modifies ptr, zone[ptr-1]; //@ ensures \old(ptr) + 1 == ptr && zone[\old(ptr)] == elt; public void empiler(int elt){ zone[ptr] = elt;ptr++; } //@ requires ptr > 0; //@ modifies ptr; //@ ensures \old(ptr)-1==ptr && \result ==zone[ptr]; public int depiler(){ ptr--;return zone[ptr]; }
invariant Quels sont les invariants de cette classe ? • Le champ zone != null • Le champ ptr évolue entre 0 et zone.length • Vrai avant et après l’appel de chaque méthode et en sortie du constructeur Affirmation globale : axiom
La pile , invariant public class Pile{ private /*@ spec_public */ int[] zone; private /*@ spec_public */ int ptr; //@ invariant ptr >= 0 && ptr <= zone.length; //@ invariant zone != null; //@ requires taille > 0; //@ ensures ptr == 0 && zone.length == taille; public Pile(int taille){ zone = new int[ taille]; } //@ requires ptr < zone.length; //@ modifies ptr, zone[ptr-1]; //@ ensures \old(ptr) + 1 == ptr && zone[\old(ptr)] == elt; public void empiler(int elt){ zone[ptr] = elt;ptr++; } //@ requires ptr > 0; //@ modifies ptr; //@ ensures \old(ptr)-1==ptr && \result ==zone[ptr]; public int depiler(){ ptr--;return zone[ptr]; }
Un test … une trace qui déborde public static void main(String[]a){ Pile p = new Pile(2); p.empiler(5); int res = p.depiler(); //@ assert res == 5; p.empiler(3); p.empiler(5); 51 p.empiler(7); … dos>escjava -quiet Pile.java Pile.java:51: Warning: Precondition possibly not established (Pre) p.empiler(7); ^ Associated declaration is "Pile.java", line 13, col 7: //@ requires ptr < zone.length; ^ 1 warning
Un autre exemple : la file, en mode défensif • Défensif • Par des pré-conditions évitant les levées d’exception • pragma exsure • //@ exsures (Exception) false; • En exemple la file (FIFO, complète en annexe 1) • Offensif • Par la levée d’exception spécifiée comme impossible, • try{ • }catch(Exception e){ • @unreachable; • } • Puis suppression du code devenu inutile ... Comme le try/catch
Une file : la déclaration public class FileInt{ private /*@ spec_public */ int[] zone; private /*@ spec_public */ int entree,sortie; private /*@ spec_public */ int taille; //@ requires capacite > 0; //@ ensures \fresh(this); //@ ensures entree == 0 && sortie == 0 && taille==0; //@ ensures zone.length == capacite; public FileInt(int capacite){ zone = new int[ capacite]; }
Une file : la méthode enfiler //@ requires taille < zone.length; //@ modifies entree, taille, zone[*]; //@ ensures (\old(entree) + 1) % zone.length==entree; //@ ensures zone[\old(entree)] == elt; //@ ensures \old(taille) + 1 == taille; //@ exsures (FilePleineException) false; public void enfiler(int elt) throws FilePleineException{ if (filePleine()) throw new FilePleineException(); zone[entree] = elt; entree = (entree+1) % zone.length; taille++; } L’exception ne peut être levée //@ exsures (FilePleineException) false;
Une file : le test public static void main(String[]a) FileInt f = new FileInt(3); try{ f.enfiler(3); f.enfiler(3); f.enfiler(4); • …… • …. • …. }catch(FilePleineException e){ //@unreachable; } En offensif, levée d’exception et bloc try/catch sont supprimés
Typage et hiérarchie • \typeof(E), ==, <:, \elemtype(\typeof(E)), \type(T) //@ requires \typeof(elt) == \type(Integer); public m( Integer elt){ … Le type des éléments doit être « Integer » //@ requires \typeof(elt) <: \type(Integer); public m( Integer elt){ … Le type des éléments doit appartenir au graphe dont la racine est la classe « Integer » le type des éléments d ’un tableau //@ requires \typeof(elt) <: \elemtype(\typeof(zone)); //@ requires \typeof(elt) <: \typeof(zone[]);
La pile d’Integer public class PileInteger{ private /*@ spec_public */ Integer[] zone; private /*@ spec_public */ int ptr; //@ invariant ptr >= 0 && ptr <= zone.length; //@ invariant zone != null; //@ requires taille > 0; //@ ensures ptr == 0 && zone.length == taille; public PileInteger(int taille){ zone = new Integer[ taille];} //@ requires ptr < zone.length; //@ requires elt != null && \typeof(elt) <:\elementype(\typeof(zone)); //@ modifies ptr, zone[ptr-1]; //@ ensures \old(ptr) + 1 == ptr && zone[\old(ptr)] == elt; public void empiler(Integer elt){ zone[ptr] = elt;ptr++; } /** <esc>requires ptr < zone.length; </esc><br> * <esc>modifies ptr, zone[ptr- 1];</esc> **/ public void empiler(int elt){ empiler(new Integer(elt)); }
La pile d’Integer suite //@ requires ptr > 0; //@ modifies ptr; //@ ensures \old(ptr)-1==ptr && \result ==zone[ptr]; public Integer depiler(){ ptr--; return zone[ptr]; } public static void main(String[]a){ PileInteger p = new PileInteger(2); p.empiler(new Integer(6)); p.empiler(new Integer(5)); Integer i = p.depiler();; //@ assert i.value == 5; } ? Accès au champ value de la classe Integer ? si le champ value est déclaré /*@ spec_public */
java/lang/Integer.spec • Fichier .spec ? • Fichier .spec si le source n’est pas disponible Pour que le champ value soit pris en compte • Légère modification de java.util.Integer.spec
Integer.spec : la modification Répertoire java.lang; (voir escjava-specs-1.2.4-win.zip) package java.lang; public class Integer extends Number{ … private /*@spec_public */ int value; … //@ensures this.value==value; public Integer(int value) { this.value = value; } …
Partage des champs d’instances p1 et p2 deux instances … Le démonstrateur utilise BrokenObj afin de démontrer qu’une erreur de partage est possible • Ici partage entre deux instances de la même zone • En fonction de la spécification, cela peut engendrer des erreurs d’accès possibles… • ESC suppose aussi qu’un accès concurrent est toujours possible … p1 p2 BrokenObj
Exemple avec partage de « obj » public class TestOwner{ private /*@ spec_public */ Object obj; //@ ensures obj != null; public TestOwner(){ obj = new Object(); } public static void test(){ TestOwner t1 = new TestOwner(); TestOwner t2 = new TestOwner(); t1.obj = t2.obj; // ici on partage //@ assert t1.obj == t2.obj; } }
ghost public Object owner Le spécifieur/programmeur indique alors quel est le propriétaire de zone, Pour cela il existe une variable ghost particulière : owner, déclarée dans la classe Object (java/lang/Object.spec) public class Object { /** ** The Object that has a field pointing to this Object. ** Used to specify (among other things) injectivity (see ** the ESC/Java User's Manual. **/ //@ ghost public Object owner; ……
Exemple avec partage impossible de « obj » public class TestOwner{ private /*@ spec_public */ Object obj; //@ invariant this.obj.owner == this; //@ ensures obj != null; public TestOwner(){ obj = new Object(); //@ set obj.owner = this; } public static void test(){ TestOwner t1 = new TestOwner(); TestOwner t2 = new TestOwner(); t1.obj = t2.obj; // ici on ne partage plus //@ assert t1.obj == t2.obj; } }
Le message devient explicite … TestOwner.java:18: Warning: Possible violation of object invariant (Invariant) } ^ Associated declaration is "TestOwner.java", line 4, col 6: //@ invariant this.obj.owner == this; ^ Possibly relevant items from the counterexample context: (after@13.20-13.20 <= vAllocTime(brokenObj.(obj:16.8))) (alloc <= vAllocTime(brokenObj.(obj@pre:2.39))) (vAllocTime(brokenObj) < after@13.20-13.20) (alloc <= vAllocTime(brokenObj)) typeof(brokenObj.(obj@pre:2.39)) <: T_java.lang.Object (RES-14.20:14.20).(obj@pre:2.39) == brokenObj.(obj:16.8) // 10 lignes plus bas … brokenObj != null (brokenObj* refers to the object for which the invariant is broken.) Suggestion [18,2]: none
L’exemple de manuel public class PileObject{ private /*@ non_null *//*@ spec_public */ Object[] zone; private /*@ spec_public */ int ptr; //@ invariant ptr >= 0 && ptr <= zone.length; //@ invariant zone != null; //@ invariant (\forall int i; i>=ptr & i<this.zone.length ==> this.zone[i] == null); //@ invariant \elemtype(\typeof(zone)) == \type(Object); //@ requires taille > 0; //@ ensures ptr == 0 && zone.length == taille; public PileObject(int taille){ zone = new Object[ taille]; } //@ requires ptr < zone.length; public void empiler(Object elt){zone[ptr] = elt;ptr++;} //@ requires ptr > 0; public Object depiler(){ ptr--;Object obj = zone[ptr]; zone[ptr] = null; return obj; }
L’exemple en images La pile p null null null null p.ptr null « 3 » « 2 » p.zone //@ invariant (\forall int i; i>=ptr & i<this.zone.length ==> this.zone[i] == null);
Recherche d’une erreur La pile p Prenons Pile brokenObj si brokenObj.ptr = p.ptr; brokenObj.zone = p.zone; et p.empiler(new Integer(4)); alors l’invariant est faux null null null null p.ptr brokenObj.ptr « 4 » « 3 » « 2 » p.zone /*@ invariant (\forall int i; i>=ptr & i<this.zone.length ==> this.zone[i] == null); */