1 / 58

The need for flexible object invariants

The need for flexible object invariants. Sophia Drossopoulou. Peter M üller. Alexander J. Summers. Imperial College London. Imperial College London. ETH Z ürich. Object invariants fundamental to software design.

keola
Download Presentation

The need for flexible object invariants

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. The need for flexible object invariants Sophia Drossopoulou Peter Müller Alexander J. Summers Imperial College London Imperial College London ETH Zürich

  2. Object invariants fundamental to software design. • Therefore, undisputedly, object invariants need to be reflected in some way in specifications. • How should object invariants be reflected? • Should they be “encoded” in pre/post conditions? • ...or kept as a first class concept? • We argue that invariants should be first class

  3. Overview • Object Invariants, Mixed vs. Pure Logics, Invariant Protocols • 1st Example: Composite • invariants support specifications which reflect the programmer’s thinking, and allow local description of global properties • 2nd Example: PIP • invariants essential for properties over inaccessible objects

  4. Object Invariants, Mixed Logics, Pure Logics, Invariant Protocols

  5. Object Invariants • Object invariants describe consistency of objects • Invariants cannot be expected to hold at all times: allow invariant to be temporarily broken • Simplest approach: Visible States Semantics:Invariants hold at method call boundaries • can be assumed at start of method call • may be broken during a call • must be re-established before end of method

  6. Protocol, Mixed, and Pure Logics • Invariant protocol • a discipline for when invariant must hold • Mixed Logic • a logic which incorporates such implicit guarantees in the reasoning. • Pure logic • a logic in which method specifications directly express all relevant information for verification questions • e.g., Hoare Logic, Separation Logic, Regional Logic • In such logics, information can be hidden (abstract predicates), but never omitted – specifications must cover all concerns of the method (incl. subcalls)

  7. 1st Example: Composite

  8. The Composite pattern • A tree whose nodes preserve properties related to all their descendant leaves. • Subtrees may be added directly to a node without first visiting its parents. • suggested as a specification/verification challenge by Müller, Leavens and Poetzsch-Heffter • Various solutions proposed at SAVCBS’08

  9. The Composite class class Composite int total; Composite par; Composite[] chldrn; int count; void add(Composite c); … }

  10. The Composite class 4 2 4 class Composite int total; Composite par; Composite[] chldrn; int count; void add(Composite c); … } OK(c) ≣c.total = max{ c’.total | c’ ∈ c.chldrn } 2 2 4 1

  11. The Composite - code void add(Composite c) components[count]=c; count ++; c.parent = this; update(c.total); } voidupdate(int n){ total=total+n; if (parent!=null){ parent.update(n); } }

  12. Composite – code void add(Composite c) components[count]=c; count ++; c.parent = this; update(c.total); } voidupdate(int n){ total=total+n; if (parent!=null){ parent.update(n); } }

  13. Composite – code void add(Composite c) components[count]=c; count ++; c.parent = this; update(c.total); } voidupdate(int n){ total=total+n; if (parent!=null){ parent.update(n); } } this c

  14. Composite – code void add(Composite c) components[count]=c; count ++; c.parent = this; update(c.total); } void update(int n){ total=total+n; if (parent!=null){ parent.update(n); } } this c

  15. Composite – code void add(Composite c) components[count]=c; count ++; c.parent = this; update(c.total); } void update(int n){ total=total+n; if (parent!=null){ parent.update(n); } } this

  16. Composite – code void add(Composite c) components[count]=c; count ++; c.parent = this; update(c.total); } void update(int n){ total=total+n; if (parent!=null){ parent.update(n); } } this

  17. Composite – code this void add(Composite c) components[count]=c; count ++; c.parent = this; update(c.total); } void update(int n){ total=total+n; if (parent!=null){ parent.update(n); } }

  18. Composite – code this void add(Composite c) components[count]=c; count ++; c.parent = this; update(c.total); } void update(int n){ total=total+n; if (parent!=null){ parent.update(n); } }

  19. Composite – code this void add(Composite c) components[count]=c; count ++; c.parent = this; update(c.total); } void update(int n){ total=total+n; if (parent!=null){ parent.update(n); } }

  20. Composite – code this void add(Composite c) components[count]=c; count ++; c.parent = this; update(c.total); } void update(int n){ total=total+n; if (parent!=null){ parent.update(n); } }

  21. Composite code - observations • The code code follows a natural argument, where we are concerned with one broken object at a time • When an object changes its state and as a result breaks the invariant of another object, it notifies the latter

  22. Composite code - observations • The code code follows a natural argument, where we are concerned with one broken object at a time • When an object changes its state and as a result breaks the invariant of another object, it notifies the latter We now consider the specifications in mixed and in pure logic.

  23. Composite – pure logic // PRE … // … // … // POST … // … void add(Composite c) … } // PRE … //… // POST … //… voidupdate(int n){ … } this c

  24. Composite – pure logic // PRE c.par==null, // ∀o∈ c.chdrn*: OK(o) // … // POST … // … void add(Composite c) … } // PRE … //… // POST … //… voidupdate(int n){ … } this c

  25. Composite – pure logic // PRE c.par==nul, // ∀o∈ c.chdrn*: OK(o) // ∀o∈ this.par*.chdrn*: OK(o) // POST … // … void add(Composite c) … } // PRE … //… // POST … //… voidupdate(int n){ … } this c

  26. Composite – pure logic // PRE c.par==nul, // ∀o∈ c.par*.chdrn*: OK(o) // ∀o∈ this.par*.chdrn*: OK(o) // POST c∈ this.chdrn, // ∀o∈ this.par*.chdrn*: OK(o), void add(Composite c) … } // PRE … //… // POST … // … voidupdate(int n){ … } this c

  27. Composite – pure logic // PRE c.par==nul, // ∀o∈ c.par*.chdrn*: OK(o) // ∀o∈ this.par*.chdrn*: OK(o) // POST c∈ this.chdrn, // ∀o∈ this.par*.chdrn*: OK(o), void add(Composite c) … } // PRE ∀o∈ this.par*.chdrn*: // o=this ⋁ OK(o) // POST …, // … voidupdate(int n){ … } this

  28. Composite – pure logic // PRE c.par==nul, // ∀o∈ c.par*.chdrn*: OK(o) // ∀o∈ this.par*.chdrn*: OK(o) // POST c∈ this.chdrn, // ∀o∈ this.par*.chdrn*: OK(o), void add(Composite c) … } // PRE ∀o∈ this.par*.chdrn*: //o=this ⋁ OK(o) // POST ∀o∈ this.par*.chdrn*: //OK(o) voidupdate(int n){ … } this

  29. Composite - pure logic - observations • Consistency properties are “deep”: expressed over all (indirectly) relevant objects • Specification footprint exceeds conceptual footprint (term borrowed from Adam Wright) • Specification does not reflect the natural argument • Specification is “global” – natural argument is “local’

  30. Mixed Logics • Approach based on visible states semantics, but allows exceptions to visible states rules • Allow some invariants to be broken for a call by adding a specification construct broken • Method signatures can declare “broken” invariants: // BROKEN(o) … • INV(o) need not hold before call to m, but must be established by the method body.

  31. Composite – mixed logic class Composite{ INV: OK(o) … // PRE c.par==null // POST … void add(Composite c) … } // PRE … // POST … voidupdate(int n){ … } }} this c

  32. Composite – mixed logic class Composite{ INV: OK(o) … // PRE c.par==null // POST c∈ this.chdrn, void add(Composite c) … } // PRE // POST voidupdate(int n){ … } }} this c

  33. Composite – mixed logic class Composite{ INV: OK(o) … // PRE c.par==nul, // POST o∈ this.chdrn, void add(Composite c) … } // PRE BROKEN(this) // POST … voidupdate(int n){ … } this

  34. Composite – mixed logic class Composite{ INV: OK(o) … // PRE c.par==nul, // POST o∈ this.chdrn, void add(Composite c) … } // PRE BROKEN(this) // POST voidupdate(int n){ … } this

  35. Composite – mixed logic class Composite{ INV: OK(o) … // PRE c.par==null // POST c∈ this.chdrn, void add(Composite c) … } // PRE // POST voidupdate(int n){ … } }} this Specification focuses on requirements relevant to this object c

  36. Composite – mixed logic class Composite{ INV: OK(o) … // PRE c.par==null // POST o∈ this.chdrn, void add(Composite c) … } // PRE // POST voidupdate(int n){ … } }} Specification focuses on requirements relevant to this object Specification only reflects local concerns this c

  37. Composite – mixed logic class Composite{ INV: OK(o) … // PRE c.par==null // POST o∈ this.chdrn, void add(Composite c) … } // PRE // POST voidupdate(int n){ … } }} Specification focuses on requirements relevant to this object Specification only reflects local concerns Concerns of other objects handled by invariant protocol this c

  38. Composite – mixed logic Broken declarations reflect pattern of break/re-establish invariants class Composite{ INV: OK(o) … // PRE c.par==nul, // POST o∈ this.chdrn, void add(Composite c) … } // PRE BROKEN(this) // POST voidupdate(int n){ … } this

  39. Composite – mixed logic Broken declarations reflect pattern of break/re-establish invariants Specification expresses “natural argument” from implementation class Composite{ INV: OK(o) … // PRE c.par==nul, // POST o∈ this.chdrn, void add(Composite c) … } // PRE BROKEN(this) // POST voidupdate(int n){ … } this

  40. Composite - mixed logic - observations • Specification reflects the natural argument (where re are concerned with one broken object at a time) • Specification is “local” – natural argument is “local’ • Verification based on work by Middelkoop, Barnett, Naumann • Verification encoded in Boogie PL.

  41. Composite - mixed logic - framing • But what about framing? • The client is not interested in the value of total. • Therefore the “public spec” need not mention total. • The “private spec”may just mention Composite.total.

  42. Conclusions from 1stExample • Invariants allow simple specifications • Invariants reflect the natural argument • Invariants support local description of global properties

  43. 2ndExample: PIP

  44. PIP • Variation of Composite, but without components field. • Nodes represent OS resources and processes • Nodes keep a pointer (parent) to other Node blocking it – if any • A Node’s priority should be equal to the maximum priorities of all nodes blocked by it • class Node { • int priority; Node parent; • // INV: priority = max • // {o.priority | o.parent == this} … • }

  45. PIP • Variation of Composite, but without components field. • Nodes represent OS resources and processes • Nodes keep a pointer (parent) to other Node blocking it – if any • A Node’s priority should be equal to the maximum priorities of all nodes blocked by it • class Node { • int priority; Node parent; • // INV: priority = max • // {o.priority | o.parent == this} … • } An object’s properties depend on state of inaccessible objects

  46. PIP – topology class Node{ Node par; intprio; }

  47. PIP – code class Node{ Node par; intprio; voidadd(Noden){ n.par = this;this.update(n.prio); } voidupdate(int n){ if (prio<n) { prio=n; this.parent.update(n) } }

  48. PIP – mixed logic class Node{ Node par; intprio; // INV prio = max{ n.prio | n.par=this } // PRE n.par==nul, // POST n.par==this voidadd(Noden){ n.par = this;this.update(n.prio); } // PRE BROKEN(this) // POST voidupdate(int n){ if (prio<n) { prio=n; this.parent.update(n) } }

  49. PIP – mixed logic Mixed Logic specification reflects the natural argument. class Node{ Node par; intprio; // INV prio = max{ n.prio | n.par=this } // PRE n.par==nul, // POST n.par==this voidadd(Noden){ n.par = this;this.update(n.prio); } // PRE BROKEN(this) // POST voidupdate(int n){ if (prio<n) { prio=n; this.parent.update(n) } }

  50. PIP – pure logic – v1 class Node{ Node par; intprio; ghost Set<Node> children; // PRE n.par==nul, //∀n∈ this.par*: //n.prio=max{n’.pric | n’∈n.children } // POST n.par==this //n∈this.children //∀n∈ this.par*: …voidadd(Noden){ …. } } a.children={b,c} a: 5 c: 5 b: 3

More Related