1 / 48

Invariants for Non-Hierarchical Object Structures

Invariants for Non-Hierarchical Object Structures. Ronald Middelkoop, Kees Huizing, Ruurd Kuiper and Erik Luit. Overview. Setting: Sequential Object-Oriented Development Classical Local Invariants Advantages of invariants Non-local Invariants Methods that re-establish an invariant

zubeda
Download Presentation

Invariants for Non-Hierarchical Object Structures

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. Invariants for Non-Hierarchical Object Structures Ronald Middelkoop, Kees Huizing, Ruurd Kuiper and Erik Luit

  2. Overview • Setting: Sequential Object-Oriented Development • Classical Local Invariants • Advantages of invariants • Non-local Invariants • Methods that re-establish an invariant • Information Hiding • Conclusions

  3. Classical Local Invariants Visible state: pre- or post-state of a method execution Class C int i; inv this.i > 2; C() {…} Visible state semantics: After an object’s creation, its invariant I holds in every visible state int getI() {…} void setI(int newI) {…}

  4. Classical Local Invariants Visible state: pre- or post-state of a method execution Class C int i; inv this.i > 2; C() {…} • Theorem: • After an object’s creation, its invariant I holds in every visible state, when • The constructor of that object establishes I • All methods in the program preserve I • No method is called while I is invalid int getI() {…} void setI(int newI) {…}

  5. Classical Local Invariants Visible state: pre- or post-state of a method execution Class C int i; inv this.i > 2; C() { pre: true; post: true; impl: this.i = 4; } • Theorem: • After an object’s creation, its invariant I holds in every visible state, when • The constructor of that object establishes I • All methods in the program preserve I • No method is called while I is invalid int getI() {…} void setI(int newI) {…}

  6. Classical Local Invariants Visible state: pre- or post-state of a method execution Class C int i; inv this.i > 2; C() {…} • Theorem: • After an object’s creation, its invariant I holds in every visible state, when • The constructor of that object establishes I • All methods in the program preserve I • No method is called while I is invalid int getI() { pre: true; post: result == this.i; impl: returnthis.i; } void setI(int newI) {…}

  7. Classical Local Invariants Visible state: pre- or post-state of a method execution Class C int i; inv this.i > 2; C() {…} • Theorem: • After an object’s creation, its invariant I holds in every visible state, when • The constructor of that object establishes I • All methods in the program preserve I • No method is called while I is invalid int getI() { pre: true; post: result == this.i; impl: returnthis.i; } void setI(int newI) { pre: newI > 2; post: this.i == newI; impl: this.i = newI; }

  8. Advantages of Invariants Class C int i; inv this.i > 2; C() {…} int getI() {…} void setI(int newI) {…} int m() { pre: true; post: result > 4; impl: return this.i +2; }

  9. Advantages of Invariants Class C int i; C() {…} int getI() {…} void setI(int newI) {…} int m() { pre: true; post: result > 4; impl: return this.i +2; } m relies on i > 2

  10. Advantages of Invariants Class C int i; C() {…} int getI() {…} void setI(int newI) {…} int m() { pre: this.i > 2; post: result > 4; impl: return this.i +2; } m relies on i > 2m requires i > 2

  11. Advantages of Invariants Class C Class D int i; C c; C() {…} inv this.c != null; int getI() {…} int useC() { pre: true; post: result > 4; impl: return this.c.getI() + 2; } void setI(int newI) {…} int m() { pre: this.i > 2; post: result > 4; impl: return this.i +2; } useC relies on c.i > 2

  12. Advantages of Invariants Class C Class D int i; C c; C() {…} inv this.c != null; int getI() {…} int useC() { pre: this.c.i > 2; post: result > 4; impl: return this.c.getI() + 2; } void setI(int newI) {…} int m() { pre: this.i > 2; post: result > 4; impl: return this.i +2; } useC relies on c.i > 2useC requires c.i > 2

  13. Advantages of Invariants Class D Class E C c; D d; inv this.c != null; int useUseC() { pre: this.d != null; post: result > 8; impl: return this.d.useC() + 4; } int useC() { pre: this.c.i > 2; post: result > 4; impl: return this.c.getI() + 2; } useUseC calls d.useC()  useC requires c.i > 2 useUseC has to establish d.c.i > 2

  14. Advantages of Invariants Class D Class E C c; D d; inv this.c != null; int useUseC() { pre: this.d != null  this.d.c.i > 2; post: result > 8; impl: return this.d.useC() + 4; } int useC() { pre: this.c.i > 2; post: result > 4; impl: return this.c.getI() + 2; } useUseC calls d.useC()  useC requires c.i > 2 useUseC has to establish d.c.i > 2 useUseC requires d.c.i > 2

  15. Advantages of Invariants Class D Class E C c; D d; inv this.c != null; int useUseC() { pre: this.d != null  this.d.c.i > 2; post: result > 8; impl: return this.d.useC() + 4; } int useC() { pre: this.c.i > 2; post: result > 4; impl: return this.c.getI() + 2; } The invariant property propagates throughout the specification

  16. Advantages of Invariants Class D Class E C c; D d; inv this.c != null; int useUseC() { pre: this.d != null  this.d.c.i > 2; post: result > 8; impl: return this.d.useC() + 4; } int useC() { pre: this.c.i > 2; post: result > 4; impl: return this.c.getI() + 2; } Advantage 1: Significant reduction in specification overhead Advantage 2: Re-implementations of a method can rely on a different set of invariants

  17. Advantages of Invariants • Sufficient: • The verifier of a method M that relies on an invariant I can • Assume that I holds when M is called • Deduce if a method M’ that is called by M preserves I Advantage 1: Significant reduction in specification overhead Advantage 2: Re-implementations of a method can rely on a different set of invariants

  18. Non-local Invariants Class CSubject Class CObserver int d; CSubject cs; void setD(int newD) { pre: true; post: this.d == newD; impl: this.d = newD; } inv this.cs != null; CObserver(CSubject toObs) {…} int getVal() { pre: true; post: return == f(this.cs.d); impl: int i = this.cs.getD(); return f(i); //pseudocode } void getD() {…} CSubject1 CObserver1

  19. Non-local Invariants Class CSubject Class CObserver int d; CSubject cs; int i; void setD(int newD) { pre: true; post: this.d == newD; impl: this.d = newD; } inv this.cs != null; CObserver(CSubject toObs) {…} int getVal() { pre: true; post: return == f(this.cs.d); impl: return this.i; } void getD() {…} CSubject1 CObserver1

  20. Non-local Invariants Class CSubject Class CObserver int d; CSubject cs; int i; void setD(int newD) { pre: true; post: this.d == newD; impl: this.d = newD; } inv this.i == f(this.cs.d); inv this.cs != null; CObserver(CSubject toObs) {…} void getD() {…} int getVal() { pre: true; post: return == f(this.cs.d); impl: return this.i; } • The constructor establishes I • All methods in the program preserve I • No method is called while I is invalid  CObserver X • X.cs ≠ this  “invariant of X still holds after assignment”

  21. Non-local Invariants Class CSubject Class CObserver int d; CObserver co; CSubject cs; int i; void setD(int newD) { pre: true; post: this.d == newD; impl: this.d = newD; } inv this.i == f(this.cs.d); inv this == this.cs.co; inv this.i == f(this.cs.d); inv this.cs != null; CObserver(CSubject toObs) {…} int getVal() { pre: true; post: return == f(this.cs.d); impl: return this.i; } void getD() {…} • The constructor establishes I • All methods in the program preserve I • No method is called while I is invalid void update() { post: this.i == f(this.cs.d); }  CObserver X • X.cs ≠ this  “invariant of X still holds after assignment”  CObserver X • X.cs = this  this.co = X

  22. Non-local Invariants Class CSubject Class CObserver int d; CObserver co; CSubject cs; int i; void setD(int newD) { pre: true; post: this.d == newD; impl: this.d = newD; if (this.co != null) { this.co.update(); } } inv this.i == f(this.cs.d); inv this == this.cs.co; CObserver(CSubject toObs) {…} int getVal() { pre: true; post: return == f(this.cs.d); impl: return this.i; } void getD() {…} void update() { post: this.i == f(this.cs.d); } • The constructor establishes I • All methods in the program preserve I • No method is called while I is invalid

  23. Non-local Invariants Class CSubject Class CObserver int d; CObserver co; CSubject cs; int i; void setD(int newD) { pre: true; post: this.d == newD; impl: this.d = newD; if (this.co != null) { this.co.update(); } } inv this.i == f(this.cs.d); inv this == this.cs.co; CObserver(CSubject toObs) {…} int getVal() { pre: true; post: return == f(this.cs.d); impl: return this.i; } void getD() {…} void update() { inc: this; post: this.i == f(this.cs.d); } • The constructor establishes I • All methods in the program preserve I • No method is called while I is invalid

  24. Non-local Invariants with inc • Theorem: • If, for any invariant I of any object, • The constructor of that object establishes I • All methods in the program preserve I • While I is invalid, any method that is called specifies it doesn’t rely on I • Then, for any method M, for any invariant I of any allocated object, • Unless M is the constructor of that object , I holds when M is called • When I holds when a method M’ is called by M, I holds when M’ terminates • Theorem: • If, for any invariant I of any object, • The constructor of that object establishes I • All methods in the program preserve I • While I is invalid, any method that is called specifies it doesn’t rely on I • Then, for any method M, for any invariant I of any allocated object, • Unless M is the constructor of that object , I holds when M is called • I holds when a method M’ called by M terminates • Theorem: • If, for any invariant I of any object, • The constructor of that object establishes I • All methods in the program preserve I • While I is invalid, no method is called • Then, for any method M, for any invariant I of any allocated object, • Unless M is the constructor of that object, I holds when M is called • I holds when a method M’ called by M terminates • Theorem: • If, for any invariant I of any object, • The constructor of that object establishes I • All methods in the program preserve I • While I is invalid, • Then, for any method M, for any invariant I of any allocated object, • Unless M is the constructor of that object, I holds when M is called • I holds when a method M’ called by M terminates • Theorem: • If, for any invariant I of any object, • The constructor of that object establishes I • All methods in the program preserve I • While I is invalid, any method that is called specifies it doesn’t rely on I • Theorem: • If, for any invariant I of any object, • The constructor of that object establishes I • All methods in the program preserve I • While I is invalid, any method that is called specifies it doesn’t rely on I • Then, for any method M, for any invariant I of any allocated object, • or specifies that it doesn’t rely on I

  25. Non-local Invariants with inc Class CSubject Class CObserver int d; CObserver co; CSubject cs; int i; void setD(int newD) { pre: true; post: this.d == newD; impl: this.d = newD; if (this.co != null) { this.co.update(); } } inv this.i == f(this.cs.d); inv this == this.cs.co; CObserver(CSubject toObs) {…} int getVal() {…} void update() { inc: this; post: this.i == f(this.cs.d); } void getD() {…}

  26. Non-local Invariants with inc Class CSubject Class CObserver int d; CObserver co; CSubject cs; int i; void setD(int newD) { pre: true; post: this.d == newD; impl: this.d = newD; if (this.co != null) { this.co.update(); } } inv this.i == f(this.cs.d); inv this == this.cs.co; CObserver(CSubject toObs) {…} int getVal() {…} void update() { inc: this; post: this.i == f(this.cs.d); impl: int d = this.cs.getD(); this.i = f(d); } void getD() {…} • Update relies on this.cs ≠ null, which follows from this = this.cs.co

  27. Non-local Invariants with inc Class CSubject Class CObserver int d; CObserver co; CSubject cs; int i; void setD(int newD) { pre: true; post: this.d == newD; impl: this.d = newD; if (this.co != null) { this.co.update(); } } inv I def this.i == f(this.cs.d); inv J def this == this.cs.co; CObserver(CSubject toObs) {…} int getVal() {…} void update() { inc: this; post: this.i == f(this.cs.d); impl: int d = this.cs.getD(); this.i = f(d); } void getD() {…} • Update relies on this.cs ≠ null, which follows from this = this.cs.co

  28. Non-local Invariants with inc Class CSubject Class CObserver int d; CObserver co; CSubject cs; int i; void setD(int newD) { pre: true; post: this.d == newD; impl: this.d = newD; if (this.co != null) { this.co.update(); } } inv I def this.i == f(this.cs.d); inv J def this == this.cs.co; CObserver(CSubject toObs) {…} int getVal() {…} void update() { inc: I(this); post: this.i == f(this.cs.d); impl: int d = this.cs.getD(); this.i = f(d); } void getD() {…} • Update relies on this.cs ≠ null, which follows from this = this.cs.co • While I is invalid, any method that is called specifies it doesn’t rely on I

  29. Non-local Invariants with inc Class CSubject Class CObserver int d; CObserver co; CSubject cs; int i; void setD(int newD) { pre: true; post: this.d == newD; impl: this.d = newD; if (this.co != null) { this.co.update(); } } inv I def this.i == f(this.cs.d); inv J def this == this.cs.co; CObserver(CSubject toObs) {…} int getVal() {…} void update() { inc: I(this); post: this.i == f(this.cs.d); impl: int d = this.cs.getD(); this.i = f(d); } void getD() { inc: I(this.co); impl: return this.d; } • While I is invalid, any method that is called specifies it doesn’t rely on I

  30. Non-local Invariants with inc Class CSubject Class CObserver int d; ONode on; CSubject cs; int i; void setD(int newD) { …} inv I def this.i == f(this.cs.d); void getD() {…} CObserver(CSubject toObs) {…} int getVal() {…} void update() {…} Class ONode CObserver obs; ONode next; inv this.obs != null;

  31. Non-local Invariants with inc Class CSubject Class CObserver int d; ONode on; CSubject cs; int i; void setD(int newD) { this.d = newD; Onode iter = on; while (iter != null) { iter.obs.update(); iter = iter.next; } } inv I def this.i == f(this.cs.d); CObserver(CSubject toObs) {…} int getVal() {…} void update() {…} void getD() {…} Class ONode CObserver obs; ONode next; inv this.obs != null;

  32. Non-local Invariants with inc Class CSubject Class CObserver int d; ONode on; CSubject cs; int i; void setD(int newD) { this.d = newD; Onode iter = on; while (iter != null) { iter.obs.update(); iter = iter.next; } } inv I def this.i == f(this.cs.d); inv J def  i •this == this.cs.on.nexti.obs; CObserver(CSubject toObs) {…} int getVal() {…} void update() {…} void getD() {…} Class ONode CObserver obs; ONode next; inv this.obs != null;

  33. Non-local Invariants with inc Class CSubject Class CObserver int d; ONode on; CSubject cs; int i; void setD(int newD) { this.d = newD; Onode iter = on; while (iter != null) { iter.obs.update(); iter = iter.next; } } inv I def this.i == f(this.cs.d); inv J def  i •this == this.cs.on.nexti.obs; CObserver(CSubject toObs) {…} int getVal() {…} void update() { inc: (CObserver, I, this == inc) post: this.i == f(this.cs.d); } void update() { inc: I(this) post: this.i == f(this.cs.d); } void update() { inc: (CObserver, I, this.cs == inc.cs) post: this.i == f(this.cs.d); } void update() { inc: (CObserver, I, true) post: this.i == f(this.cs.d); } void getD() {…} Class ONode void M(p_1, …,p_n) { inc: (C, I, P), (C’, I’, P’), … } CObserver obs; ONode next; inv this.obs != null;

  34. Information Hiding • Want Subject to work with different kinds of Observers (simultaneously) • Concrete Observer(s) should be hidden from Concrete Subject • Then: developing new observers doesn’t affect the implementation, specification or verification of the Concrete Subject.

  35. Information Hiding Class CSubject Class CObserver int d; CObserver co; CSubject cs; int i; void setD(int newD) { pre: true; post: this.d == newD; impl: this.d = newD; if (this.co != null) { this.co.update(); } } inv I def this.i == f(this.cs.d); inv J def this == this.cs.co; CObserver(CSubject toObs) {…} int getVal() {…} void update() { inc: I(this); post: this.i == f(this.cs.d); impl: int d = this.cs.getD(); this.i = f(d); } void getD() {…} Invariant I of CObserver not visible to CSubject

  36. Information Hiding Class CSubject Class CObserver int d coop I(this.co); CObserver co; CSubject cs; int i; void setD(int newD) { pre: true; post: this.d == newD; impl: this.d = newD; if (this.co != null) { this.co.update(); } } inv I def this.i == f(this.cs.d); inv J def this == this.cs.co; CObserver(CSubject toObs) {…} int getVal() {…} void update() { inc: I(this); post: this.i == f(this.cs.d); impl: int d = this.cs.getD(); this.i = f(d); } void getD() {…} Invariant I of CObserver not visible to CSubject

  37. Information Hiding Class CSubject Class CObserver int d coop I(this.co); CObserver co coop I(this.co), J(this.co); CSubject cs coop I(this), J(this); int i coop I(this); void setD(int newD) { pre: true; post: this.d == newD; impl: this.d = newD; if (this.co != null) { this.co.update(); } } inv I def this.i == f(this.cs.d); inv J def this == this.cs.co; CObserver(CSubject toObs) {…} int getVal() {…} void update() { inc: I(this); post: this.i == f(this.cs.d); impl: int d = this.cs.getD(); this.i = f(d); } void getD() {…} Invariant I of CObserver not visible to CSubject

  38. Information Hiding Class CSubject Class CObserver int d coop I(this.co); CObserver co coop I(this.co), J(this.co); CSubject cs coop I(this), J(this); int i coop I(this); void setD(int newD) { pre: true; post: this.d == newD; impl: this.d = newD; if (this.co != null) { this.co.update(); } } inv I def this.i == f(this.cs.d); inv J def this == this.cs.co; CObserver(CSubject toObs) {…} int getVal() {…} void update() { inc: I(this); post: I(this); impl: int d = this.cs.getD(); this.i = f(d); } void getD() {…} Invariant I of CObserver not visible to CSubject

  39. Information Hiding Class Subject Interface Observer Observer o coop I(this.o), J(this.o); abstract Subject s coop J(this); … abstract inv I; inv J def this == this.s.o; Class CSubject extends Subject abstract void update() { inc: I(this); post: I(this); } int d coop I(this.o); void setD(int newD) { post: this.d == newD; impl: this.d = newD; if (this.o != null) { this.o.update(); } } Class CObserver implements Observer …

  40. Information Hiding Interface Observer Class CObserver implements Observer abstract Subject s coop J(this); int i coop I(this); CSubject cs coop I(this); abstract inv I; inv J def this == this.s.o; def s by cs; def I by this.i == f(this.cs.d)  J(this); abstract void update() { inc: I(this); post: I(this); } CObserver(CSubject toObs) {…} void update() { impl: int d = this.cs.getD(); this.i = f(d); }

  41. Conclusions • inc allows one to call methods that re-establish invariants • coop allows one to make explicit which invariants might be invalidated when a field is assigned to • Together, they allow the full, modular specification of complex, non-hierarchical designs like the Observer Pattern • Future Work: • Complement with other solutions, in particular hierarchical ones • Combine with other ways of making dependencies explicit

  42. Proof Obligations • {P0} r.f := E; {P1} S2; {P2} • if (b) { • {P3} S3; {P4} • } else { • {P6} S4; {P7} • } {P8} • while (b) { • {P9} S5; {P10} • } {P11} M() { … int f coop (C,I,P) int f coop I(r) int f coop (C, I, dep = r) }

  43. Proof Obligations • {P0’  (X = null  P[X/dep])} r.f := E; {P1} S2; {P2} • if (b) { • {P3} S3; {P4} • } else { • {P6} S4; {P7} • } {P8} • while (b) { • {P9} S5; {P10} • } {P11} M() { … int f coop (C,I,P) int f coop I(r) int f coop (C, I, dep = r) } X represents an arbitrary C-object that the assignment might make inconsistent

  44. Proof Obligations • {P0’  (X = null  P[X/dep])} r.f := E; {P1} S2; {P2} • if (b) { • {P3} S3; {P4} • } else { • {P6} S4; {P7} • } {P8} • while (b) { • {P9} S5; {P10} • } {P11} M() { … int f coop (C,I,P) int f coop I(r) int f coop (C, I, dep = r) ? P1  I(X) ? }

  45. Proof Obligations • {P0’  (X = null  P[X/dep])} r.f := E; {P1} S2; {P2} • if (b) { • {P3} S3; {P4} • } else { • {P6} S4; {P7} • } {P8} • while (b) { • {P9} S5; {P10} • } {P11} M() { … int f coop (C,I,P) int f coop I(r) int f coop (C, I, dep = r) ? S2 Method call ? }

  46. Proof Obligations • {P0’  (X = null  P[X/dep])} r.f := E; {P1} S2; {P2} • if (b) { • {P3} S3; {P4} • } else { • {P6} S4; {P7} • } {P8} • while (b) { • {P9} S5; {P10} • } {P11} M() { … int f coop (C,I,P) int f coop I(r) int f coop (C, I, dep = r) ? P2  I(X) ? }

  47. Proof Obligations • {P0’  (X = null  P[X/dep])} r.f := E; {P1} S2; {P2} • if (b) { • {P3} S3; {P4} • } else { • {P6} S4; {P7} • } {P8} • while (b) { • {P9} S5; {P10} • } {P11} M() { … int f coop (C,I,P) int f coop I(r) int f coop (C, I, dep = r) Don’t know which branch is taken; Branches might contain calls }

  48. Proof Obligations • {P0’  (X = null  P[X/dep])} r.f := E; {P1} S2; {P2} • if (b) { • {P3} S3; {P4} • } else { • {P6} S4; {P7} • } {P8} • while (b) { • {P9} S5; {P10} • } {P11} M() { … int f coop (C,I,P) int f coop I(r) int f coop (C, I, dep = r) Don’t know if while is executed; Body might contain calls }

More Related