230 likes | 337 Vues
Learn how to implement Design By Contract in Ada, annotate routines with axioms, ensure obligations are respected, conduct polymorphic programming, and uphold the Liskov Substitution Principle. Discover the importance of blaming responsibility, managing software production, inheritance, and enforcing correct hierarchies.
E N D
Adding Contracts to Ada Adding Design By Contract to Ada Ehud Lamm
ADT + Contracts type Stack is tagged private; procedure Init(S:out Stack); procedure Push(S:in out Stack; I:in Integer) at exit Size(S)=old Size(S)+1; procedure Pop(S:in out Stack;I:out Integer) use when not(Is_Empty(S)) at exit Size(S)=old Size(S)-1; function Size(S:Stack) return Natural; function Is_Empty(S:Stack) return Boolean; function Top(S:Stack) return Integer use when not(Is_Empty(S)); Overflow: exception; • Contracts are checked at runtime (detection). • Contract violations raise exceptions. • When possible use the type system instead (e.g., Size) • Contracts allow expressing arbitrary boollean assertions. Ada-Europe’2002
Design By Contract • Annotate each routine with axioms. • Pre-Condition & Post-Condition • (we will not talk about class invariants) • The classic ADT approach (Liskov, Guttag, Meyer) Ada-Europe’2002
Design By Contract • A contract carries mutual obligations and benefits. • The client should only call a routine when the routine’s pre-condition is respected. • The routine ensures that after completion its post-condition is respected. Ada-Europe’2002
Interface oriented programming A little polymorphic programming procedure Print_And_Empty(S: in out Stack’class) is i:Integer; begin while not(Is_Empty(S)) loop pop(S,i); put(I); end loop; end; Exercise: Prove termination for all possible stacks S. Ada-Europe’2002
Oops… type Crazy_Stack is new Stack with null record; procedure Pop(S:in out Crazy_Stack;I:out Integer) use when not(Is_Empty(Stack)) at exit (old Top(S)/=9 and then Size(S)=old Size(S)-1) or (Size(S)=old Size(S)); Does Print_And_Empty work correctly on Crazy_Stacks? Ada-Europe’2002
IS-A Otherwise bad use of public inheritance! Liskov Substitution PrincipleLSP Liskov (SIGPLAN, May 1988) Liskov, Wing (TOPLAS, Nov. 1994) Ada-Europe’2002
Why is this important? • The foundation for subtype polymorphism • Bugs surface during maintenance • Who is responsible? Ada-Europe’2002
Assigning Blame • Crucial for managing software production • Possible candidates: • The original contractor (Stack) • The polymorphic routine (Print_And_Empty) • The subcontractor (Crazy_Stack) • The user of the pm routine (should know better than to call P&E on Crazy_Stack) Ada-Europe’2002
DbC & Inheritance • Remember the LSP! • “The subclass must require less and ensure more” )Meyer, OOSC) • The only question is how to ensure this property! Ada-Europe’2002
A B DbC & LSP – more formal Assume B is derived from A, then for each method P pre(PA) → pre(PB) and post(PB) → post(PA) A B Ada-Europe’2002
The Eiffel Approach • The programmer is not allowed to make an LSP error… • require else • ensure then • A subclass can only use “or” in pre-cond / “and” in post-cond • Other tools are even worse Ada-Europe’2002
Wrong Approach • “Ensuring” correct hierarchies procedure p(T:A;I:Integer) use when i>0; procedure p(T:B;I:Integer) use when i>10; -- synthesized contract (i>0)V(i>10) • Hierarchy is malformed, language hides error. Ada-Europe’2002
Solution • Interface implied contract can be deduced from code. • Hierarchy checking is done according to run time tag of object. (Recall P&E) (more details in the paper and references) Ada-Europe’2002
P&E is responsible for Stack’s Pop pre-condition. (Stack’s pre implies actual’s pre) Actual’s Pop post-condition must be satisfied when Pop exits Actual Pop’s postcondition must imply Stack Pop post-condition Contract Checking - Analysis procedure Print_And_Empty(S: in out Stack’class) is i:Integer; begin while not(Is_Empty(S)) loop pop(S,i); put(I); end loop; end; Ada-Europe’2002
Another example: generics generic with function Sqrt(X:Float) return Float use when X>=0; procedure Print_Quad_Solutions(A,B,C:Float); -- use when B**2-4.0*A*C>=0; If actual Sqrt requires X>0, who is to be blamed when P_Q_S fails? Ada-Europe’2002
How can the language help? • Run-time enforcement • Should allow the programmer to document contracts; in a formal, standard notation • Enforce correct contract relations when possible (Hard!) • Identify faulty components and assign blame, when violations occur • When you cannot use built-in contracts: types(Think of trying to break a contract stating that the Stack contains only positive numbers) Ada-Europe’2002
Does Ada need DbC? • Ada, The Software Engineering Language • Good type system; support for generic programming • Ada interface definitions are lacking (e.g., which exceptions are raised by which routine?) • Readability and Self-Documenting code • Debugging aid • Everyone else has it (Eiffel, Java: iContract, jContractor, HandShake, JVMAssert) … • BUT: This is a major change Ada-Europe’2002
Conclusion • Perhaps still too cutting edge?! • Is there market demand? • Should we do it anyway? • Even a rudimentary implementation has important advantages. • Simpler than extending the type system • The future lies in sw components) COTS) Ada-Europe’2002
Ada needs DbC Any Questions? Ada-Europe’2002
Implementation • Can be implemented using wrapper routines • The exact wrapper routine invoked for each call is determined by relevant interface. Ada-Europe’2002
Interoperability with the old language • Old language calling new, not really an issue (but upstream contractual exceptions may be raised) • New language calling old, not really an issue (but compiler should know that a package was compiled with no contracts) Ada-Europe’2002