meeting -*- Outline -*- * Design by Contract with JML Be sure to explain any JML specific notation (e.g., \result) as you go... ** motivation Writing tests is one way to describe what methods are supposed to do, but tests: - doesn't easily produce easily understood documentation - aren't amenable to other tool work - don't help assign blame for bugs unless used carefully in layers *** design by contract ------------------------------------------ DESIGN BY CONTRACT A way of recording: - details of method responsibilities - avoiding constantly checking arguments - assigning blame across interfaces Example: //@ requires y >= 0; //@ ensures (* \result is root of y *); Contracts in software: Obligations of client - passes positive integer Rights of client - gets square root approximation Rights of implementor - assumes argument is positive Obligations of implementor - must compute and return square root ------------------------------------------ Analogy for contract idea, selling a car: Obligations of client - pays money Rights of client - gets new car Rights of provider - receives money Obligations of provider - sells new car ------------------------------------------ PRE AND POSTCONDITIONS def: a method's *precondition* says what must be true to call it. Example: //@ requires y >= 0; def: a method's *normal postcondition* says what is true when it returns (without throwing an exception) Example: //@ ensures \result * \result == y; ------------------------------------------ def: an *exceptional postcondition* says what is true when a method throws an exception. /*@ signals (IllegalArgumentException e) @ e != null && y < 0; @*/ Q: Did you see these in fully dressed use cases? Q: Which is the obligation of the client? Of the implementor? Q: Which is the rights of the client? Of the implementor? ------------------------------------------ SAYING WHAT EXCEPTIONS MAY BE THROWN The signals_only clause says what exceptions may be thrown. //@ signals_only IllegalArgumentException; default: the list in the method's throws clause. ------------------------------------------ Q: So what does this mean for methods that don't list anything in their throws clause? ------------------------------------------ RELATIONAL MODEL OF METHODS Think of a method as a relation: Inputs <--> Outputs precondition describes the domain postcondition describes the relationships ------------------------------------------ This is an oversimplification, really it's inputs and states... *** documentation ------------------------------------------ CONTRACTS AS DOCUMENTATION For each method say: - what it requires (if anything) - what it ensures Example in JML: //@ requires y >= 0; /*@ ensures -y <= \result @ && \result <= y @ && \result * \result <= y @ && y < (Math.abs(\result) + 1) @ * (Math.abs(\result) + 1); @*/ public static int isqrt(int y) { ... } Contracts are: - more abstract than code - not necessarily constructive - often machine checkable (in JML) and so can help with debugging - machine checkable contracts can always be up-to-date In summary, good documentation ------------------------------------------ Explain how we write the spec first, although you can also put it before the '{' in JML Q: How is constructing a square root different than the above spec? ------------------------------------------ ABSTRACTION BY SPECIFICATION A contract can be satisfied in many ways: E.g., for square root: - linear search - binary search - Newton's method ... These will have varying non-functional properties - efficiency - memory usage So a contract abstracts from all these implementations ==> can change implementations later ------------------------------------------ *** blame assignment, modularity ------------------------------------------ MORE ADVANTAGES OF CONTRACTS Blame assignment: Who is to blame if: - precondition doesn't hold? - postcondition doesn't hold? Avoids inefficient defensive checks: /*@ requires a != null @ && (* a is sorted *); @*/ int binarySearch(Thing [] a, Thing x) { ... } ------------------------------------------ Not just finger pointing: you know where to fix the code. It's also possible the contract itself is wrong... or should be changed In the binary search example, it takes linear time to check that an array is sorted, but only log(a.length) time to search; doing such defensive checks defeats the whole purpose of binary search! ------------------------------------------ MODULARITY OF REASONING Typical OO code: ... source.close(); dest.close(); getFile() .setLastModified( loc.modTime().getTime()); return modTime(); How to understand this code? - read the code for all methods? - read the contracts for all methods? ------------------------------------------ If we read the code we have the same problem again! Q: What if some methods are recursive? then without specification need to build up an approximation... Q: What if there is polymorphism? Then must look at a potentially unbounded set of code! ------------------------------------------ RULES FOR REASONING Client code - must work for every implementation that satisifies contract - can thus only use the contract (not the code!) - must establish precondition - gets to assume postcondition //@ assert 9 >= 0; int res = ISqrt.isqrt(9); //@ assert res * res == 9; Implementation code: - must satisfy contract - gets to assume precondition - must establish postcondition - but can do anything permitted by it ------------------------------------------ ------------------------------------------ CONTRACTS AND INTENT Code makes a poor contract, because can't separate: - what is intended (contract) - what is an implementation decision E.g., if floating point square root routine gives an approximation good to 3 decimal places, can that be changed in the next release? By contrast, contracts: - allow vendors to specify intent - allow vendors freedom to change details - tell clients what they can count on ------------------------------------------ Q: What kinds of changes might vendors want to make that don't break existing contracts? e.g., improve code performance ** what is JML? language for design by contract something like Eiffel (just mention it) ------------------------------------------ JML Stands for "Java Modeling Language" Design by contract for Java Available from www.jmlspecs.org Uses Java 1.4.1 ------------------------------------------ should be installed on the department machines already ** annotations ------------------------------------------ ANNOTATIONS JML specifications are contained in annotations, which are comments like: //@ ... or /*@ ... @ ... @*/ At-signs (@) on the beginnings of lines are ignored within annotations ------------------------------------------ Also there is a form with plus signs you may see: //+@ /*+@ ... @+*/ Q: What's the advantage of using annotations? They look like comments to Java, and are ignored by javac ** simple examples *** informal specs (organize specifications) ------------------------------------------ INFORMAL DESCRIPTIONS An informal description looks like: (* some text describing a property *) It is treated as a boolean by JML. Allows: - escape from formality - organization of English as contracts Example: public class ISqrt { //@ requires (* y is positive *); /*@ ensures (* \result is an @ int approximation to @ the square root of y *) @ && \result >= 0; @*/ public static int isqrt(int y) { ... } } ------------------------------------------ Note that informal descriptions can be combined with formal stuff, as in the postcondition above. ------------------------------------------ FOR YOU TO DO Write informal pre and postconditions for the other operations of this type: public class Person { private String name; private int weight; /*@ also @ ensures \result != null @ && (* \result is a display @ form of this person *); @*/ public String toString() { return "Person(\"" + name + "\"," + weight + ")"; } public int getWeight() { return weight; } public void addKgs(int kgs) { if (kgs >= 0) { weight += kgs; } else throw new IllegalArgumentException(); } } public Person(String n) { name = n; weight = 0; } } ------------------------------------------ Explain "also" ... My answer would be something like: public class Person { private String name; private int weight; /*@ also @ ensures \result != null @ && (* \result is a display @ form of this person *); @*/ public String toString() { return "Person(\"" + name + "\"," + weight + ")"; } /*@ ensures (* \result is @ this person's weight *); @*/ public int getWeight() { return weight; } /*@ requires kgs >= 0; @ ensures (* this person's weight @ is their old weight plus @ kgs *); @*/ public void addKgs(int kgs) { if (kgs >= 0) { weight += kgs; } else throw new IllegalArgumentException(); } } /*@ requires n != null; @ ensures (* This person has @ name n and weight 0 *); @*/ public Person(String n) { name = n; weight = 0; } } *** formalization Q: Are the informal specifications longer than the code sometimes? yes, and less precise So sometimes it's easier to write formal specifications ------------------------------------------ WRITING FORMAL SPECIFICATIONS IN JML Formal assertions are written as Java expressions, but: - Cannot have side effects - no use of =, ++, -- - can only call "pure" methods - Can use some extensions to Java: Syntax meaning ===================================== \result result of method call a ==> b a implies b b <== a b is implied by a a <==> b a iff b \old(E) value of E in pre-state ------------------------------------------ ------------------------------------------ public class Person { private /*@ spec_public non_null @*/ String name; private /*@ spec_public @*/ int weight; /*@ public invariant !name.equals("") @ && weight >= 0; @*/ //@ also //@ ensures \result != null; public String toString() { return "Person(\"" + name + "\"," + weight + ")"; } /*@ ensures \result == weight; @*/ public /*@ pure @*/ int getWeight() { return weight; } /*@ ensures kgs >= 0 @ && weight @ == \old(weight + kgs); @ signals (IllegalArgumentException e) @ kgs < 0; @*/ public void addKgs(int kgs) { if (kgs >= 0) { weight += kgs; } else throw new IllegalArgumentException(); } } /*@ requires n != null @ && !n.equals(""); @ ensures n.equals(name) @ && weight == 0; @*/ public Person(String n) { name = n; weight = 0; } } ------------------------------------------ The spec_public is needed since we're using the names weight and name in publicaly visible (client) specifications. The non_null annotation says that the field isn't ever null. It's equivalent to an invariant saying that. The "public invariant" says a property that is true in all visible states. It has to be preserved by all public methods, and is also imposed on subtypes. Note that we have changed the specification of addKgs to say that it must signal an exception when kgs < 0 ------------------------------------------ MEANING OF ENSURES AND SIGNALS |-----------| |------------| | | | | [ Normal | [ Exceptional| | (Return) | | (Throws) | [ O | [ O | | | | | | | | | [ | [ | | | | | |-----------| |------------| /------------/ /------------/ / ensures / /signals(...)/ / kgs >= 0 ... / kgs < 0; / /------------- /------------/ ------------------------------------------ *** invariants ------------------------------------------ INVARIANTS Are properties that are always true of an object's state, when control is not inside the object's methods /*@ public invariant !name.equals("") @ && weight >= 0; @*/ Allow you to define: - acceptable states of an object - "consistency" of an object's state ------------------------------------------ This is a public invariant, it's also useful to define private ones to describe design decisions e.g., this array is sorted, ... *** exercise for students ------------------------------------------ FOR YOU TO DO Formally specify the method (in Person) public void changeName(String newName) { name = newName; } Hint: watch out for the invariant! ------------------------------------------ ** basic tool usage *** overview of tools ------------------------------------------ TOOLS FOR JML Runtime assertion checking compiler: jmlc Java interpreter with RAC support: jmlrac JUnit test oracle generator: jmlunit JUnit test runner with RAC support: jml-junit Documentation (HTML) generator: jmldoc Type checker: jml ------------------------------------------ Of these jmlc and jmldoc are the most useful. (Also ESC/Java is able to process a subset of JML) The jmldoc tool is like javadoc, but understands JML specifications *** jmlc We'll focus on jmlc now ------------------------------------------ The Runtime Assertion Checking Compiler jmlc Usage: $ jmlc Person.java produces Person.class $ jmlc -Q *.java produces *.class, quietly $ jmlc -d ../bin Person.java produces ../bin/Person.class ------------------------------------------ do example, show error output ... ------------------------------------------ RUNNING CODE COMPILED WITH JMLC Must have JML's jmlruntime.jar in Java's boot class path Automatic if you use script jmlrac $ jmlrac PersonMain ------------------------------------------ ------------------------------------------ A MAIN PROGRAM public class PersonMain { public static void main(String [] argv) { System.out.println(new Person(null)); System.out.println(new Person("")); } } ------------------------------------------ ------------------------------------------ EXAMPLE (formatted) $ jmlc -Q PersonMain.java Person.java $ jmlrac PersonMain Exception in thread "main" org.jmlspecs.jmlrac.runtime .JMLEntryPreconditionError: by method Person.Person regarding specifications at Person.java:34:22 when 'n' is null at Person.checkPre$$init$$Person( Person.java:984) at Person.(Person.java:45) at PersonMain.main(PersonMain.java:8) ------------------------------------------ Note: jmlc confuses the line numbers, so you can only count on the line numbers from javac and those in the first part of the message, not those in the stack backtrace. E.g., line 8 of Person.java is not where we are executing, rather it's line 5! Q: Any questions about the tools? ** more examples, from StickSync We now go back to writing contracts idea is to show how to document real contracts ------------------------------------------ STICKSYNC EXAMPLES (MORE REALISTIC CONTRACTS) class Location { /** The pathname of a file. */ private /*@ spec_public non_null @*/ String path; /** Caches the associated file. * Access only via getFile(). * This is null if no File is cached. * @see getFile() */ private transient File fileCache; /** Returns the associated file */ //@ ensures \result != null; private File getFile() { if (fileCache == null) { fileCache = new File(path); } return fileCache; } /** Initializes this Location. * @param fullpath the full pathname * to the file. */ //@ requires fullpath != null; //@ assignable path; //@ ensures path.equals(fullpath); public Location(String fullpath) { path = fullpath; } /** Is the file readable? */ /*@ ensures \result @ == getFile().canRead(); @*/ public /*@ pure @*/ boolean readable() { return getFile().canRead(); } /** Copies this location's file * to the given location's file. */ //@ requires loc != null; //@ ensures \result != null; /*@ ensures \result.equals(modTime()) @ && loc.modTime().equals(modTime()); @*/ public Date copyTo(Location loc) throws IOException { ... } } ------------------------------------------ Q: Can you read these? Q: To what extent do these specifications add to the javadocs? ** other things in JML *** model fields and represents clauses ------------------------------------------ MODEL FIELDS, REPRESENTS CLAUSES What if you want to change a spec_public field's name? private /*@ spec_public non_null @*/ String path; to private /*@ non_null @*/ String fullPath; For specifications: - need to keep the old name public - but don't want two Strings So use a model variable: //@ public model non_null String path; and a represents clause //@ private represents path <- fullPath; ------------------------------------------ ------------------------------------------ MODEL VARIABLES Are specification-only variables. Like domain-level constructs. Given value only by represents clauses: path abstract, model ^ | represented by | fullPath concrete (real) ------------------------------------------ In general the represents clause can be more than just identity, it can be any (pure) function *** web site for more info JML has bunches of more features, but don't worry about them until you find some value from these www.jmlspecs.org is linked from course web pages