design by contract & exceptions david talby. software correctness when is a class correct? –...

30
Design by Contract Design by Contract & Exceptions & Exceptions David Talby

Post on 22-Dec-2015

214 views

Category:

Documents


0 download

TRANSCRIPT

Design by Contract & Design by Contract & ExceptionsExceptions

David Talby

Software CorrectnessSoftware Correctness

When is a class correct?– It’s a relative concept; what is required?– But it’s the correct question: the class is the basic

independent, reusable unit of software Theory flashback: class = Abstract Data Type

– Commands (push, pop, empty, full)– Axioms (count == 0 iff empty)– Preconditions (pop requires not empty)

Why isn’t this reflected in programming?

Approaches to CorrectnessApproaches to Correctness

Testing– Tests only cover specific cases– Tests don’t affect extensions (inheritance)– If something doesn’t work, where is the

problem?– It is difficult to (unit-) test individual classes

Formal Verification– Requires math & logic background– Successful in hardware, not in software

The assert() macro– Introduced to Java only in JDK 1.4

Design by ContractDesign by Contract

Created by Bertrand Meyer, in EiffelEach class defines a contract, by

placing assertions inside the codeAssertions are just Boolean expressions

– Eiffel: identified by language keywords– iContract: identified by javadoc attributes

Assertions have no effect on executionAssertions can be checked or ignored

MethodsMethods

Each feature is equipped with a precondition and a postcondition

double sqrt (double x)require

x >= 0do

…ensure

result * result == xend

Methods IIMethods II

The same in iContract syntax: //** return Square root of x @pre x >= 0 @post return * return == x */ double sqrt (double x) { … }

Assertions are just Boolean expressions– Except result and old in postconditions– Function calls are allowed, but…– Don’t modify data: ++i, inc(x), a = b

The ContractThe Contract

Client (caller)

Supplier (f eature)

Obligations: f ulfi ll precondition

f ulfi ll postcondition

Benefi ts: can assume postcondition

can assume precondition

Class InvariantsClass Invariants

Each class has an explicit invariant

class Stack[G]private

int count;boolean isEmpty() { … }… other things …

invariantisEmpty() == (count == 0)

end

Theory: Hoare ClausesTheory: Hoare Clauses

Hoare’s Notation for discussing correctness:{ P } code { Q }

For example:{ x >= 10 } x = x + 2 { x >= 12 }

Partial Correctness: If a program starts from a state satisfying P, runs the code and completes, then Q will be true.

Full Correctness: If a program start from a state satisfying Q and runs the code, then eventually it will complete with Q being true.

When is a Class Correct?When is a Class Correct?

For every constructor:{ Pre } code { Post /\ Inv }

For every public method call:{ Pre /\ Inv } code { Post /\ Inv }

Origin is Abstract Data Type theoryPrivate methods are not in the

contractUndecidable at compile time

DbC in Real Life: C/C++DbC in Real Life: C/C++

In C, the assert macro expands to an if statement and calls abort() if it’s false

assert(strlen(filename) > 0);Assertion checking can be turned off:

#define NDEBUGIn C++, redefine Assert to throw

instead of terminating the programEvery class should have an invariantNever use if() when assert() is required

DbC in Real Life: UMLDbC in Real Life: UML

UML supports pre- and post-conditions as part of each method’s properties

Invariants are supported at class levelObject Constraint Language is used

– Formal language - not code– Readable, compared to its competitors– Supports forall and exists conditions

DbC in Real Life: JavaDbC in Real Life: Java

Assertions that can be turned on and off are only supported from JDK 1.4

assert interval > 0 && interval <= 1 : interval; The most popular tool is iContract

– Assertions are Javadoc-style comments– Instruments source code, handles inheritance

Based on the OCL– @invariant forall IEmployee e in getEmployees() |

getRooms().contains(e.getOffice())– @post exists IRoom r in getRooms() | r.isAvailable()

Common MistakesCommon Mistakes

Not an input-checking mechanism– Use if to test human or machine output– Assertions are always true

Not a control structure– Assertion monitoring can be turned off– They are applicative, not imperative, and must

not include any side effects– Besides, exceptions are inefficient

An assertion violation is always a bug– In precondition: client bug– In postcondition or invariant: supplier bug

Common Mistakes IICommon Mistakes II

Don’t use defensive programming– The body of a routine must never check

its pre- or post-conditions.– This is inefficient, and raises complexity.

Don’t hide the contract from clients– All the queries in a method’s precondition

must be at least as exported as the method

– Doesn’t have to be so in postconditions

The Role of ExceptionsThe Role of Exceptions

Definition: a method succeeds if it terminates in a state satisfying its contract. It fails if it does not succeed.

Definition: An exception is a runtime event that may cause a routine to fail.

Exception cases– An assertion violation (pre-, post-, invariant, loop)– A hardware or operating system problem– Intentional call to throw– A failure in a method causes an exception in its

caller

Disciplined Exception Disciplined Exception HandlingHandling

Mistake 1: Handler doesn’t restore stable state

Mistake 2: Handler silently fails its own contract

There are two correct approaches– Resumption: Change conditions, and retry method– Termination: Clean up and fail (re-throw

exception) Correctness of a catch clause

– Resumption: { True } Catch { Inv Pre }– Termination: { True } Catch { Inv }

Improper Flow of ControlImproper Flow of Control

Mistake 3: Using exceptions for control flowtry { value = hashtable.find(key); }

catch ( NotFoundException e ) { value = null; }

It’s bad design– The contract should never include exceptions

It’s extremely inefficient– Global per-class data is initialized and stored– Each try, catch, or exception specification cost time– Throwing an exception is orders of magnitude

slower than returning from a function call

Constructors & DestructorsConstructors & Destructors Never let an exception leave a destructor

– In C++: Throwing an exception while destroying due to another exception causes terminate()

– In finalize(): The finalize() method is stopped– The result is resource leaks (yes, in Java too)

C++ doesn’t destroy partially built objects– Throwing an exception in a constructor after

resources were allocated leads to resource leaks– Either split initialization to a method, or avoid

pointers– Use auto_ptr<T> members instead of T* pointers for

const members or members initialized in constructor

Case Study: GenericityCase Study: Genericity It’s very difficult to write generic, reusable

classes that handle exceptions well– Genericity requires considering exceptions from the

template parameters as well– Both default and copy constructors may throw– Assignment and equality operators may throw– In Java: constructors, equals() and clone() may throw

See Tom Cargill paper’s Stack<T> class code “Warm-up” bugs not related to exceptions:

– Copy, assignment do not set top in empty stacks– Assignment does not protect against self-

assignment

GoalsGoalsException Neutrality

– Exceptions raised from inner code (called functions or class T) are propagated well

Weak Exception Safety– Exceptions (either from class itself or from

inner code) do not cause resource leaksStrong Exception Safety

– If a method terminates due to an exception, the object’s state remains unchanged

Case Study: Restoring StateCase Study: Restoring StateBug: Throwing OutOfMemory in push()

– top has already been incremented– count(), push(), pop() can no longer be used– Fix: restore top when throwing the exception

Bug: Throwing OutOfMemoty in operator=()– Old array is freed before new one allocated– In x=y, x would be inconsistent after failure– Fix: Allocate new array into a temporary first

Case Study: Memory LeaksCase Study: Memory LeaksBug: What if T.operator=() throws?

– It can happen: stack<stack<int>>– See assignment in for loop of copy constructor– If T throws here, no stack destructor is called– The array allocated in the first line is leaked

Bug: Same problem in push()– Again, assignment in for loop may throw– Only new_buffer points to allocated array– In case of exception, this array will be leaked

Case Study: More on StateCase Study: More on StateBug: pop() ends with return v[top--];

– This involves copying the returned object– What if its copy constructor throws?– top has already been decremented– State of the stack has changed, and the

client cannot retry to pop the same element

Case Study: More on MemoryCase Study: More on MemoryBug: operator=() assumes T constructs

well– First line: delete []v;– Second line: v = new T[nelems = s.nelems];– If T’s default constructor throws, then

v is undefined– This can lead to double delete:

{ stack x, y;

y = x; // exception thrown – y.v is deleted

} // end of scope – y.v is re-deleted

Case Study: ConclusionsCase Study: ConclusionsPaper’s title: “a false sense of security”Combining exceptions and genericity is

extremely difficult – STL handles thisJava has some of the same problems too

– Constructors, equals(), hashCode(), clone(), toString() and other Object methods may throw

– In collections: add(), addAll(), removeAll(), retainAll(), hashCode(), etc. may throw

– The throws keyword does not guard against heirs of RuntimeException

Guidelines for ExceptionsGuidelines for Exceptions When propagating an exception, try to leave the

object in the same state it had in method entry– Make sure const functions are really const– Perform exception-prone actions early– Perform them through temporaries– Watch for side effects in expression that might throw

If you can’t leave the object in the same state, try to leave it in a stable state– Either re-initialize it, or mark it internally as invalid– Do not leave dangling pointers in the object – delete

pointers and free resources through temporaries

Guidelines for Exceptions IIGuidelines for Exceptions II Avoid resource leaks

– In constructors, initialize raw pointers or resource handles to null first and initialize them inside a try..catch block in the constructor body

– Don’t throw exceptions from a destructor / finalize()

Don’t catch any exceptions you don’t have to– Rewrite functions to preserve state if possible

push() {v_[top_] = element; top_++; }– Use catch(…) to deal with propagating exceptions

Restore state and re-throw exception

Guidelines for Exceptions IIIGuidelines for Exceptions III Don’t hide exceptions from your clients

– Always re-throw an exception caught with catch(…)– Throw a different exception only to add

information– Make sure one catch block doesn’t hide another

Use exception hierarchies– Define base exception classes for each library– Don’t be afraid of a deep hierarchy– Consider inheriting a standard exception

Don’t get too paranoid

SummarySummary

Software Correctness & Fault ToleranceDesign by Contract

– When is a class correct?– Speed, Testing, Reliability, Documentation,

Reusability, Improving Prog. LanguagesExceptions

– What happens when the contract is broken?– Neutrality, Weak Safety, Strong Safety