1 / 21

Declaring and Checking Non-null Types in an Object-Oriented Language

Declaring and Checking Non-null Types in an Object-Oriented Language. Authors: Manuel Fahndrich K. Rustan M. Leino OOPSLA’03 Presenter: Alexander Landau. Exceptions are Everywhere. class A { private string s; public A(string str) { s = str; }

grover
Télécharger la présentation

Declaring and Checking Non-null Types in an Object-Oriented Language

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. Declaring and Checking Non-null Types in an Object-Oriented Language Authors: Manuel Fahndrich K. Rustan M. Leino OOPSLA’03 Presenter: Alexander Landau

  2. Exceptions are Everywhere class A { private string s; public A(string str) { s = str; } public int length() {return s.Length);} } int x = new A(null).length(); Exception!

  3. Exceptions • Used to signal about “bad things” during runtime. • Better to find errors at compile-time! • Types of exceptions: • Bad cast: dynamic type may only be known at runtime. • Division by zero: divisor unknown at compile time. • NULL dereference: compile-time checker can be created! • …

  4. General Idea • Splitting reference types into non-null and possibly-null types • Already implemented in ML’s and some other languages’ type systems. • A non-null field provides a contract: • At construction, must be initialized with a non-null value. • Read access yields a non-null value. • Write access requires a non-null value.

  5. Simple Solution • Require that an object under construction cannot be accessed until fully constructed. • Possible in some languages, but not in mainstream ones like C#/Java, where this may be accessed from the constructor or from methods called by it.

  6. Example (1) class A { [NotNull] string name; public A([NotNull] string s) { this.name = s; this.m(55); } virtual void m(int x) { … } } name initialized before use

  7. Example (2), but what if… class B : A { [NotNull] string path; public B([NotNull] string p, [NotNull] string s) : base(s) { this.path = p; } override void m(int x) { … this.path … } } m() called from A’s c’tor before B() initialized path!

  8. A Glance at C++ • In C++ base-class object is created and initialized (by c’tor). Then, derived-class object is created and initialized. • Virtual functions act as non-virtual when called from within c’tors. • The problem from the previous example is eliminated. • In C#/Java, the object including all superclass objects is created first, then c’tors are called. • Virtual functions act as virtual even from within c’tors.

  9. Advantages • Documentation of method input parameters, output parameters and return values. • Static (compile-time) check of object invariants such as non-null fields. • Error detection at the point of error commitment, not when dereferencing nulls. • No need to check for nulls at runtime – boosts performance. • Reduce/eliminate unexpected null reference exceptions.

  10. Non-null Types • Notation: • T- - non-null references of type T. • Just like T& in C++ • T+ - possibly-null references of type T. • Just like T* in C++ • Examples: • T- t = new T(…); // newnever returns null • T+ n = t; // n may be null • if (n != null) t=n; // here n is of type T- • int x = t.f; // t must be non-null

  11. Construction • Problem: half-baked objects in constructors. • this.f may be null even though f is declared as non-null. • Notation: Traw- denotes partially-initialized object types. • T- ≤ Traw-. • Rule: A T- field in Craw- object Read: May be nullWrite: Must be with a T- value.

  12. The Construction Duty • The c’tor must initialize all non-null fields. • Restricted to the object proper, • not including sub- or super-class object fields. • Every path through a c’tor must include an assignment to every non-null field. • When a c’tor is called, all ancestor c’tors have already been called, thus members initialized. • The last c’tor called due to new C(…) casts this from Craw- to C-. • The annotation [Raw] allows a method to be called with this of type Craw-.

  13. Example class B : A { [NotNull] string path; public B([NotNull] string p, [NotNull] string s) : base(s) { this.path = p; } [Raw] override void m(int x) { … this.path … } } class A { [NotNull] string name; public A([NotNull] string s) { this.name = s; this.m(55); } [Raw] virtual void m(int x) { … } }

  14. Arrays • Arrays are references themselves and contain references. • The array itself and/or its elements may be non-null or possibly-null: • T- []- non-null array of non-null elements • T+ []- non-null array of possibly-null elements • T- []+ possibly-null array of non-null elements • T+ []+ possibly-null array of possibly-null elements

  15. Arrays - Problem • In contrast to objects, there is no “constructor” that initializes all array elements to non-null values after allocation. • new T- [n] returns a reference of type T- []raw-. • Reading a[i] may yield null. • Writing a[i] requires non-null.

  16. Arrays - Solution • Compiler can not know when array has finished initialization • Explicit cast required from programmer. • The cast validates the non-nullity of the elements. T- []raw- aTmp = new T- [n]; // initialize the elements of aTmp T- []- a = (T- []-)aTmp;

  17. Other Language Constructs (1) • Structs • Default constructor initializes fields to zero-equivalent values (e.g. null for references). • Problem: Cannot be overridden! • Solution: All c’tors for a struct S produce a value of type S except the default c’tor which produces Sraw.

  18. Other Language Constructs (2) • Call-by-reference parameters • Used for input – formal parameter type is a supertype of the actual parameter type. • Used for output - formal parameter type is a subtype of the actual parameter type. • Required in order to maintain conformance. • Thus, no-variance on ref parameters • Problem: For a raw object, a field f of type T- yields T+ on read and requires T- on write. • Solution: Disallow passing such fields as ref parameters.

  19. Implementation • Possible! • Created by the authors. • Does not (yet) implement the full design. • Implemented at the CIL level. • Does not modify the compiler or runtime. • Works with other languages compiled into CIL. • Tested on a ~20,000 lines program.

  20. Implementation - Benefits • Catches hard to find errors: • Vacuous initialization – this.foo = foo inside a c’tor. The goal was to initialize a field with a parameter, but there was no parameter named foo. • Wrong local bool m(Q other) { T that = other as T; if (other == null) return false; // should have used “that” if (this.bar != that.bar) … // “that” may be null

  21. Conclusion • Non-null types allow moving some errors from runtime to compile-time. • Not a theoretical-only beast, implementations possible and exist. • Less runtime checks, faster code. • Backward compatible except in initialization, mainly in constructors.

More Related