copyright w. howden1 programming by contract cse 111 6/4/2014
TRANSCRIPT
Copyright W. Howden 1
Programming by Contract
CSE 111
04/10/23
Contracts
• Client and Supplier
• Contract:– if client satisfies certain preconditions, then the
supplier will produce a product satisfying certain postconditions
Copyright W. Howden 204/10/23
Design by Contract
• All of the components (classes, methods and procedures) have a stated contract that they will fulfill
• Benefits– reusable components– correct interfaces when integrating components– predictable results
04/10/23 Copyright W. Howden 3
Software Contracts
• Methods and procedures• precondition: required input properties
• postconditions: guaranteed output properties, provided precondition is satisfied
• Classes• method contracts
• class invariants– properties that are true on completion of constructor and
true before and after each method application
04/10/23 Copyright W. Howden 4
Assertions
• Expected properties of program state (values of variables)
• Used to specify preconditions, postconditions and class invariants
• Also used for intermediate assertions– statements about what is expected to be true at
intermediate points in a programs execution
04/10/23 Copyright W. Howden 5
Assertions and Preconditions• Assertion should appear as first line in a
method
• Asserts that whenever execution reaches this location, this property must be true
04/10/23 Copyright W. Howden 6
Assertions and Postconditions
• Executable dynamic testing assertions should appear before each return statement
• State that the asserted property is true whenever the program returns from that location
• Static assertions can be put after the return points
04/10/23 Copyright W. Howden 7
Assertions and Class Invariants
• Should appear as postconditions in constructors and all other methods
• Should appear as preconditions in all methods
• For public methods, input should be checked directly and not by an assertion– assertions can be turned off after debugging
04/10/23 Copyright W. Howden 8
Assertion Languages
• Static analysis– non-executable
• informal prose statements or formal statements in a formal logic
• Dynamic analysis• executable expressions that return T or F
• executed during testing, but turned off during production use
Copyright W. Howden 904/10/23
Validating Contracts
• Static: – prove that if the precondition holds then when the
program reaches termination, the postcondition holds
• Dynamic: – use executable assertions for pre and postconditions. Run
tests. If a postcondition does not hold when the precondition holds, the contract is not valid. If a precondition does not hold either a test is faulty or the program and/or precondition need to be corrected
Copyright W. Howden 1004/10/23
Lecture Topics
• Static analysis(verification) of method/procedure and class contracts– sometimes called “proofs of correctness”
• Dynamic analysis (testing) with Java assertions– pre and postconditions– class invariants, etc.
Copyright W. Howden 1104/10/23
Why Learn About Proofs?
• For new complex logic, invaluable check against reasoning flaws
• Even if never formally done, provides a way of looking at a program that can guide code reading, or informal analysis
• Are the means of proving properties of standard algorithms (e.g. sorting)
Copyright W. Howden 1204/10/23
Beams and Girders
• Proved algorithms and state models are the “beams and girders” of a software construction project
• These are the pieces with proved properties that we can rely on
• Abstract, so must be translated into programming language code
• Construction involves using these to build a specific application product
04/10/23 Copyright W. Howden 13
Informal and Formal Proofs
• Informal• “precise” state assertions, but not in a formal logical
language
• similar to the language of informal mathematics
• Formal• uses formal semantics for programming language
statements
• very detailed, and hard to construct
04/10/23 Copyright W. Howden 14
Copyright W. Howden 15
Partial Correctness and Termination
• Suppose that A1 is the precondition for a method m and An is the postcondition.
• Correctness: m is totally correct if it is partially correct and it always terminates
• Definition of partial correctness:• if A1 is true at the beginning of an execution and if
m is executed and terminates, then An will be true at the end of the execution
04/10/23
Copyright W. Howden 16
Proof Technique
• Add intermediate assertions Ai to the method.– breaks down proof into easily handled chunks
• Make sure that each loop has at least one intermediate assertion
• For each pair of assertions (X,Y) where there is a subpath p from X to Y, which has no other assertions on it, prove its verification condition:
“if X is true at the beginning of p, and p is executed, then Y will be true at the end of p”
04/10/23
Proof Method Validity?
• Consider any execution path P
• P can be broken up into a sequence of subpaths p, which go from one assertion to the next with no assertion in between
• If all of the verification conditions for the subpaths are true, we can join them together to provide a proof for the whole path
04/10/23 Copyright W. Howden 17
Copyright W. Howden 18
Verification Example
• Program multiplies two numbers x and y by adding up y x times
• Input (precondition) assertion Pre, output (postcondition) assertion Post, intermediate loop invariant assertion IA
04/10/23
Copyright W. Howden 1904/10/23
Copyright W. Howden 20
Verification Conditionsfor Example
• Prove that 1. if Pre is true then IA will be true
2. if IA is true and Count = 0 then Post will be true
3. If IA is true and Count 0 then IA will be true
04/10/23
Proof of Verification Condition 3
Suppose that we are at IA and
x*y = Product + Count*Y
is true. If we go around the loop, Product is incremented by y, and Count is decremented by 1. This means the expression on the right would get larger by y, and then smaller by y, leaving its value the same. So if the relationship was true before the loop, it will also be true after the loop.
04/10/23 Copyright W. Howden 21
Termination Proofs
• If there are no loops in m, and all called methods are terminate, then m must terminate
• For loops, look for a counter that approaches an upper (lower) limit and is incremented (decremented) on every path through the loop
04/10/23 Copyright W. Howden 22
Copyright W. Howden 23
Termination Proof for Multiply Example
• Count initialized to x >=0
• Loop terminates if Count == 0
• For each iteration Count is decremented by 1 so loop must terminate
• Note: • if Precondition x>=0 is removed from example and
input x can be negative, algorithm will not always terminate (i.e. when x < 0), but program is still partially correct
04/10/23
Termination Bug Example
• Application: MS Zune MM player
• Failure: December 31, 2008, device would not boot – infinite loop
• Faulty procedure– Input = day count relative to origin year 1980.
Code suppose to figure out current year.– e.g. if days = 500 it must be 1982
04/10/23 Copyright W. Howden 24
Zune Related Code
year = ORIGINYEAR; /* = 1980 */
while (days > 365 )
{
if isLeapYear(year))
{
if (days > 366)
{
days -= 366;
year += 1;
}
}
else
{
days -= 365;
year += 1;
}
}
04/10/23 Copyright W. Howden 25
(Attempted) Termination Proof
• Decremented counter: days
• Termination condition: days <= 365
• When traversing loop, if year is a leap year, and days (remaining) is = 366, then the loop is traversed with no changes to the counter (days)
• i.e. there is a path through the loop on which the counter is not decremented,
04/10/23 Copyright W. Howden 26
Examples from DS
• isMember() checks to see if DB in-memory vector has an entry with a given name. Our assertions capture what the program is supposed to do
• deleteMember() is more complex
04/10/23 Copyright W. Howden 27
isMember() with Verification Assertions
04/10/23 Copyright W. Howden 28
public boolean isMember(String name){ /** Pre: for j = 0 to numberMembers-1 membersData[j].name != null
/** numberMembers -1 >=0for (int i=0; i <= numberMembers-1; ++i){ /** for j = 0 to i-1 membersData[j].name != name
/** i <= numberMembers-1if (name.equals(membersData[i].name)){ return true;
/** Post 1: return = true/** for j = 0 to i-1 membersData[j].name != name/** membersData[i].name = name
}/** return false;/** Post 2: return = false/** for j = 0 to numberMembers-1 membersData[j].name != name
Verification of IsMember()
• It is fairly simple to reason that for all pairs of assertions, if the first is true and you reach the next one, it is true also
• Hence if the precondition holds, and the program terminates, the postcondition will
• So the contract is valid
• But we also need to consider termination
04/10/23 Copyright W. Howden 29
Termination of isMember()
• Termination is easy because the loop counts up to a limit from 0 and the precondition
/** numberMembers -1 >=0
requires the limit to be larger than the initial value
• Note: this precondition clause was added during analysis!!
• I had not thought of this
04/10/23 Copyright W. Howden 30
deleteMember()
04/10/23 Copyright W. Howden 31
public boolean deleteMember(String name){
int i = 0;while (!(name.equals(membersData[i].name)) & (i <= numberMembers-1)){ ++i;}if (i <= numberMembers-1){
for (int j = i; j< numberMembers-1; ++j){
membersData[j]=membersData[j+1];}--numberMembers;return true;
}else{ return false;}
}
deleteMember() - Pre and Post Conditions
notation: x’ means new value of x, versus original or previous value
contents stands for the collection of items in the designated data structure
Precondition
/* for k = 1 to numberMembers-1 membersData[k] non null, membersData.name field is a string
/* name is of type string
/* numberMembers >=0
Postcondition
/** if there exists some k, 1 <= k <= numberMembers – 1 such that memberData[k].name = name
/** numberMembers’ = numberMembers – 1 and
/** contents(memberData[j]’, 1<= j <= numberMembers-1’)
= contents(memberData[j], 1<= j <= numberMembers) ~ memberData[k]
/** if there does not exist some k, 1 <= k <= numberMembers – 1 such that memberData[k].name = name
/** numberMembers’ = numberMembers
/** for j = 1 to numberMembers-1, membersData[j]’ = membersData[j]
/** return false
04/10/23 Copyright W. Howden 32
Intermediate Assertions
• All intermediate assertions except– return/exit assertion for second return– final assertion to summarize both return
assertions
04/10/23 Copyright W. Howden 33
04/10/23 Copyright W. Howden 34
public boolean deleteMember(String name){
int i = 0;while (!(name.equals(membersData[i].name)) & (i <= numberMembers-1)){ /** for j = 0 to i, name != membersData[j].name
/** i <= numberMembers-1/** for j = 0 to membersData – 1, membersData[j]’ = membersData[j]++i;
}if (i <= numberMembers-1){ /** for j = 0 to i-1, name != membersData[j].name
/** i <= numberMembers-1/** name == membersData[i].name
for (int j = i; j< numberMembers-1; ++j){
membersData[j]=membersData[j+1];/** for k = 1 to i-1, membersData[k]’ =membersData[k];/** for k = i to j, membersData[k]’ = membersData[k+1]
} /** for k = 1 to i-1, membersData[k]’ =membersData[k];/** for k = i to numberMembers-2, membersData[k]’ = membersData[k+1]--numberMembers;return true;
}else{ return false;}
}
Sample Reasoning About First Pre-Return Assertion
• First part follows directly from the intermediate assertion in the loop
• Second part follows from the second intermediate assertion plus the following
• if the loop was entered, then the final value of j on exit must have been numberMembers-2
• if we did not enter the loop then i must have been numberMembers-1 so it is vacuously true
04/10/23 Copyright W. Howden 35
deleteMember() with Second Pre-Return Assertion
04/10/23 Copyright W. Howden 36
public boolean deleteMember(String name){
int i = 0;while (!(name.equals(membersData[i].name)) & (i <= numberMembers-1)){ ++i;}if (i <= numberMembers-1){
for (int j = i; j< numberMembers-1; ++j){
membersData[j]=membersData[j+1];}--numberMembers;return true;
}else/** for j = 0 to numberMembers - 1, memberData[j].name != name{ return false;}
}
Sample Reasoning about Second Pre-Return Assertion
• If we have the case where i is not <= numberMembers-1 then it must be > numberMembers-1
• If we use the above along with the first while-loop intermediate assertion, we can get the second pre-return assertion
04/10/23 Copyright W. Howden 37
Final “Intermediate” Assertions
• Assertion to go just after last “}” /** return = true
/** i <= numberMembers-1
/** membersData[i].name = name
/** for k = 1 to i-1 membersData[k]’ =membersData[k];
/** for k = i to numberMembers-2 membersData[k]’ = membersData[k+1]
/** numberMembers’ = nuumberMembers - 1;
/* or
/** return false
/** not (i <= numberMembers-1)
/** for k = 1 to numberMembers-1 membersData[k].name != name
/** for k = 1 to i-1 membersData[k]’ =membersData[k];
/** numberMembers’ = numberMembers
04/10/23 Copyright W. Howden 38
Continued Reasoning
• The two pre return intermediate assertions imply the final intermediate assertion
• The final intermediate assertion gives us the Postcondition
04/10/23 Copyright W. Howden 39
Oops?
• Consider the precondition/* for k = 1 to numberMembers-1 membersData[k] is non null, and membersData.name is a string
/* name is of type string
/* numberMembers >=0
• Now look at the first loopwhile (!(name.equals(membersData[i].name)) & (i <= numberMembers-1))
{ ++i;
}
• It is possible to reference membersData[i] with i = numberMembers, and the precondition does not guarantee that it has a non-null value!
• This is an actual bug that causes a failure
04/10/23 Copyright W. Howden 40
Moral of the Story
• Proofs of correctness forces you to think through the logic of a complex algorithm, but it is error prone for dealing with the details of an actual program
• In this case, assume that all vectors are infinite, that we can compare a given value with a non-value, etc., and the problem goes away
04/10/23 Copyright W. Howden 41
Dynamic Analysis
• Use Java assertions and testing to verify contracts
• assert Expression1 ;
• assert Expression1 : Expression2 ;
– If Expression1 is false then system will throw an error. Value of Expression2 is returned
• can be turned off and on using a command line switch
04/10/23 Copyright W. Howden 42
Inserting Assertions - Preconditions
• Precondition is first line of code, consisting of expression in input parameters
• Public methods should check input and throw an illegal argument exception, rather than have this checked with an assertion
• methods should protect themselves from bad data even after testing has been completed
04/10/23 Copyright W. Howden 43
Inserting Assertions - Postconditions
• Should occur before each return
• Use a function to facilitate the situation where there are multiple returns, each requiring the postcondtion
04/10/23 Copyright W. Howden 44
Inserting Assertions – Class Invariants
• True before and after each method
• Insert in methods as postconditions
• Class state changed by method call?– No: not necessary to check invariant at
beginning of methods – Yes: (e.g. class has public class variables) then
include invariant in methods as a precondition
04/10/23 Copyright W. Howden 45
Using Assertions with Testing
• Construct tests using methods such as– black box: normal and oddball inputs – coverage: make sure all statements or branches
executed at least once
• Do not have to manually observe outputs to determine if tests passed if we can rely on the postconditions to catch bad output
04/10/23 Copyright W. Howden 46
DS Example - IsMember
• No precondition assertions, but insert code to confirm validity of input data
• For output check need to essentially rewrite the code inside a method called by the postcondition assertion– Define a method called noMatch().
04/10/23 Copyright W. Howden 47
Technical Details
• Defined the method “noMatch()” inside a new inner class called memberDataProps
• Want to only create the memberDataProps instance when assertions are turned on (say during testing) so create with an assignment inside a dummy assertion
04/10/23 Copyright W. Howden 48
04/10/23 Copyright W. Howden 49
public boolean isMember(String name){ class memberDataProps
{ public boolean noMatch(memberData[] m, int length) { for (i= 0; length-1; ++i;)
{ if (name.equals(m[i].name)) return false;}
return true; }
}memberDataProps checker; assert ((checker = new memberDataProps()) != null); if (numberMembers-1 < 0) throw new IllegalArgumentException(“Out of range parameter”); for (int i=0; i <= numberMembers-1; ++i)
{ if membersData[i] == null throw new IllegalArgumentException(“Null param”);
}for (int i=0; i <= numberMembers-1; ++i)
{ if (name.equals(membersData[i].name)){ assert name.equals(membersData[i].name);
return true;}
}assert checker.noMatch(membersData, numberMembers);return false;
}
Useful?
• Will use of assertions here lead to defect detection?
• Having to essentially rewrite the method inside the noMatch() method seems shaky.– maybe it would have the same bugs as the code
04/10/23 Copyright W. Howden 50
deleteMember()
• Could use postconditions that:• for false, check that vector items are the same, and
numberMembers is unchanged
• for true, check that the deleted element not in the list, and the contents are the same except for deleted element. Also numberMembers is decremented
• Document expected loop bounds invariant.• With a good set of tests this is what will find the
problem
04/10/23 Copyright W. Howden 51
Assignment 11
• Choose two classes from your phase 1 that you are going to reuse (select non trivial examples)– Prove their correctness using informal
assertions– Construct executable assertions and run tests
against them
04/10/23 Copyright W. Howden 52