510 likes | 714 Views
Design by Contract. Defining Contracts on Method Call Sequences. Advanced Software Tools Seminar Alexander Freidin 2006. Outline. Design by Contract – reminder What is missing in DbC? JASS overview Trace-Assertions. Design by Contract - overview. Proposed by Bertrand Meyer for Eiffel.
E N D
Design by Contract Defining Contracts on Method Call Sequences Advanced Software Tools Seminar Alexander Freidin 2006
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.
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.
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.