Design by Contract Advanced Software Tools Seminar Alexander Freidin 2006 Defining Contracts on...

Post on 22-Dec-2015

212 views 0 download

Tags:

Transcript of Design by Contract Advanced Software Tools Seminar Alexander Freidin 2006 Defining Contracts on...

Design by Contract

Advanced Software Tools SeminarAlexander Freidin

2006

Defining Contracts on Method Call Sequences

Outline

Design by Contract – reminder

What is missing in DbC?

JASS overview

Trace-Assertions

Design by Contract - overview

Proposed by Bertrand Meyer for Eiffel.

Goal – organize communication between software elements.

How? By specifying mutual obligations and benefits – contracts.

Two sides involved in contract are: Supplier – supplies services Client – receives services from the supplier

More on Contracts

Client Supplier relationship. Not another item in specification

document, but a part of the program code.

Contract either annotates the code or a special language construct like in Eiffel.

Checked for violation in runtime. Unlike assertions, contract is separated

from the business logic.

Design by Contract facilities

Preconditions – Obligation for the client. Specify the constraints on the permissible

program state or passed arguments. Must hold before calling the supplier.

Postconditions – Obligation for the supplier. Supplier guarantees on completion of the task.

Example: Supplier – Square root routine for real number. Precondition – input is a non-negative number. Postcondition – output is an approximation,

within a specified precision, of the exact mathematical square root of the input number.

Design by Contract facilities

Class invariants – Describe the consistency and integrity

properties of the class and its instances.

Should hold in all client-visible states.

Example: Vector size must be non-negative.

Design by Contract facilities

Loop variants and invariants

Catch the typical errors in loops: The loop doesn’t terminate. “Off by one” errors. One extra/missing

iteration Special cases like zero iterations are

not handled.

Design by Contract facilities

Loop invariants – Keep integrity through loop execution. Must be satisfied:

at the beginning of the loop after every loop iteration when the loop has terminated

Loop variants – Ensures loop termination. Integer expression decreased every loop

iteration, but limited to be non-negative.

Design by Contract – facilities

Check instruction – checks if the certain property is

satisfied at the certain stage of the computation.

Implemented as one or more assertion clauses inside a method.

Design by Contract extensions

First-order logic quantifiers – helpful for contracts definition over sets: Universal quantifier: “for all” Existential quantifier: “there exists“

Refinement checks – Check if the subclass is a behavioral subtype of

its superclass. Preconditions are weakened. Postconditions are strengthened. Superclass invariant must be preserved.

Design by Contract Pros

Helps ensure correctness Helps debugging Helps testing Helps ensure inheritance is properly

handled Provides a quite effective form of

documentation

Design by Contract Cons

DbC is not a panacea, must be used with care.

Not all specifications can be described with existing facilities of DbC. Example: DbC doesn’t support specifications that

define performance issues such as execution time and required resources – performance contracts.

Checking contract violation may be more time consuming than the method execution.

Example: Class that works on Hamiltonian cycle graphs. Its preconditions need to solve NPC problem.

Note on Performance contracts

Difficult to ensure and test

Have to be defined properly Min/Max/Average measurement? Can be relaxed if under load?

Ongoing research

JAVA-MaC – monitors and checks program execution against requirements specification. Time between events is also part of specification.

What is left out DbC?

Classical DbC facilities scale well with stateless classes.

Stateful class behavior depends on its state.

Explicit state – visible by the client, i.e. returned by a public method. Can be utilized in pre/post-conditions.

Implicit state – invisible by the client. Cannot be expressed by classical DbC constructs.

Stateful class example

Class implementing I/O with some device. Methods: connect, disconnect, read,

write. Specification: To start I/O with device,

one must call “connect” method. I/O is done with “read/write” methods. To finish working, one must call “disconnect” method.

FSM of the example specification

JASS – Java with ASSertions

Pre-compiler tool written in Java. Translates annotated contracts into

dynamic checking code. Violation of specification is indicated

by Java exception. Free of charge. Website:

http://semantic.Informatik.Uni-Oldenburg.de/~jass

JASS – DbC Features

Standard DbC features: Pre/post-conditions Class invariants Loop variants / invariants Check instruction

Advanced features: Refinement, i.e. subtyping Existential and universal quantifiers Trace assertions – novel feature

Trace Assertions - Goal

Goal – define the valid order of methods invocation.

Going back to previous example: class implementing I/O with some device.

Trace Assertions

To support classical DbC, the above class should: Keep track of its state. Expose the state with the public

getState() method. Each pre/post-condition must include

state checks. Stands against DbC concept contract

logic must not be part of the business logic.

Trace Assertions

Trace Assertions extend DbC in respect of defining contracts for valid order of method calls.

Keep trace of objects behavior. Throw Java exception if trace violates the specification.

Filling the hole of behavior contracts in classical DbC.

Sequence of method calls is called trace.

Trace-Assertions

Allowed traces are specified by CSP-like (Communicating Sequential Processes) processes.

CSP is a notation describing concurrent system, whose components, called processes, interact by communication.

Communicating Sequential Processes

A CSP process: Independent entity with interfaces for interaction

with processes/environment. Defined in terms of events, which are basic

elements in CSP.

Event containing more than one piece of information is called compound event. E.g. gate.open

Process a P will execute event “a” and then behave as process P.

We will not show CSP semantics here, but observe how the trace assertions are defined on CSP notation base.

Trace-Assertions

Trace-Assertion example:init().b init().e start().b start().e

Method start() can be only invoked after init() finished execution.

Suffix ‘.b’ indicates the begin of a method and ‘.e’ indicates the end accordingly.

Nested call of start() from inside init() will result in Trace-Assertion violation.

Trace-Assertions are class invariants

Trace-Assertions describe the global behavior, therefore they are part of the class invariant in JASS.

Syntax:/** invariant[AssertionName] trace (trace-assertion body);**/

Processes

Trace-Assertions contain one or more declarations of processes.

Process describes the valid behavior of a class at runtime.

Processes are defined by: Process name Process parameters Local variables Process expression

Basic Processes

Basic Processes: STOP – no further method invocations

are allowed.

ANY – any method invocation is allowed.

TERM – the actual object must terminate. Introduced to comply with CSP termination definition. Unused.Mapped to this.finalize() STOP.

Trace-Alphabet

Trace-Alphabet is a set of method names.

Alphabet is defined: Implicitly – a set of method names that

appear in process definitions.

Explicitly – a list of method names specified at the header of Trace-Assertion definition.

Trace-Alphabet – implicit definition

Example:trace(

init() STOP);

Trace-Alphabet for this example is implicit: { init() }.

Trace <init() init()> is invalid, but <init() start()> is valid. Method start() is unknown to Trace-Assertion.

Trace-Assertion checks are limited byTrace-Alphabet.

Trace-Alphabet – explicit definition

Alphabet is defined in trace clause Trace { m1, m2, …, mN }

Example:/** invariant

trace { init(), start() }(

init() STOP);

**/

Trace-Alphabet – common wildcards

this.* - all methods of the belonging class.

* EXCEPT init() – all methods, but init()

{ public A.*, public B.* } – all public methods of class A and B

M(*) – all methods M with any parameters

M(?) – all methods M with one parameter

M(int, ?, *) – all methods M with at least two parameters. First parameter must be of type ‘int’.

Processes

Valid behavior of a program is described by process declarations.

Starting point of Trace-Assertion is process called MAIN.

/** invariant trace { public this.* } (MAIN() {

init() CALL Initialized() }Initialized() {

* EXCEPT init() CALL Initialized() });

**/

First method call must be init() and no more init() invocations allowed.

Choice Operator

Previous examples are limited to accept a single sequence of method invocations.

Choice operator <|> defines branches or ‘choice points’ of allowed processes.

Example:MAIN() {

(a() b() STOP) <|> (a() c() STOP)}

Means: after a() either b() or c() may be invoked.

Valid traces are: a(), a() b(), a() c().

Parallelism operator

If a class should satisfy more than one single flow, the parallel-operator |<>| is used.

Example:

MAIN() {CALL P() |<>| CALL Q() }

P() { a() b() STOP }

Q() {a() c() STOP

}

Valid traces are: a(), a() b(), a() c(), a() b() c(), a() c() b().

Both processes are evaluated in parallel.

Process Variables

Syntax of process declaration is derived from Java method declaration.

As Java methods, processes can have arguments and variables.

Example:MAIN() {

init() CALL P(“initialized”) }

P(String status) {String msg = “Status:” + status;m() …

}

Process Variables

Local variables must be declared at the beginning of the process declaration.

Variables use cases:

Process definition can depend on conditions. Variables can store program state to define complex conditions.

Processes can execute arbitrary Java code that uses the variables. E.g. Printing status messages.

Variables can be used to define branches between processes (if-else).

Conditional method invocations

Trace-Assertions can bind certain condition to any method invocation.

That is unlike pre/post-conditions, each invocation of the same method may have certain condition.

Example:MAIN() {

CALL Counter(0)}Counter(int num) {

m() WHERE(num < 3) CALL Counter(num+1)}

Method m() may be invoked at most three times.

The condition given after WHERE must hold before m() is executed.

Execution of Java code

Trace-Assertions allow execution of arbitrary Java code.

Purposes: To allow definition of complex conditions. To allow program state monitoring.

Monitoring example #1:

MAIN() {EXECUTE(System.out.println(“init”);) init() ANY }

The message “init” is printed at the beginning of init() execution.

Execution of Java code

Monitoring example #2:

MAIN() {EXECUTE(System.out.println(“init done”);) init().e ANY }

The message “init done” is printed by the end of init() method.

Conclusion: The execution code is always bound to the next process event.

Branches – If/Else

Trace-Assertions can have branches.

Example:IF(b) {

CALL P() } ELSE {

CALL Q()}

Allow to express complex dynamical behavior of a program.

Variables are useful to branch the trace-assertion checking flow.

Data Exchange

Trace-Assertions support exchanging argument values between the running program and the Trace-Assertion.

There are two types of data exchange: Read the method’s argument value into the

process. Restrict the values of the method arguments.

Data Exchange: program process

To read the argument value into the local variable of a process, it has to be preceded with ‘?’.

Example:MAIN() {

String s;boolean b;m(?s, ?b) WHERE(s != null) CALL Q(b)

}

m(?s, ?b) yields to m(String, boolean) method. Any ?x must be a declared process variable.

The notation ‘?x’ is called data exchange. The value of the argument is passed from the program to process variable x.

Data Exchange: process program

The Trace-Assertion is able to restrict the possible values passed to the methods.

Restriction can be specified with a constant or process local variable.

Constant restriction: m(“x”), e.g. m(7) – the method m() is allowed to receive only the value of 7.

Variable restriction: m(!x) – the argument should have the value of x.

Data Exchange: restricting arguments values

There are multiple ways of restricting method arguments values.

If the restrictions are static, then preconditions are preferred for this task.

All the examples are equivalent in semantics:

P() { m(7) STOP }Q() { int i = 7; m(!i) STOP }R() { int i; m(?i) WHERE(i==7) STOP }

Data Exchange – method result value

Trace-Assertions support passing the result value from a method to the process: ?ret m().

Trace-Assertions can restrict the method’s return value to the value from the process: !val m().

Misleading example:P() {

boolean b = false;?b m() WHERE(b) CALL Q()

}

WHERE-expression is evaluated at the beginning of method m(), not at the end as expected.

Data Exchange – method result value

To use the returned result value, one should use If/Else-expression.

Example:P() {

boolean b = false;?b m() IF(b) {

CALL Q() } ELSE { CALL E() }

}E() {

EXECUTE(throw new RuntimeException();) STOP }

Observing recursive method calls

The class Factorial will calculate the factorial of positive integer by recursively calling the method “factorial”.

Each recursive factorial call will initiate a process that will accept a call to another factorial only if the parameter has been reduced but not less than zero.

Observing recursive method calls

Summary

Design by Contract is a powerful technique for getting the implementation according to specification.

Classical DbC is incomplete. There is a place for research.

JASS – powerful tool that implements DbC principles for Java.

Trace-Assertions – a novel and powerful feature of JASS.