Building and Using Pluggable Type-Checkers Werner M. Dietl Joint - - PowerPoint PPT Presentation

building and using pluggable type checkers
SMART_READER_LITE
LIVE PREVIEW

Building and Using Pluggable Type-Checkers Werner M. Dietl Joint - - PowerPoint PPT Presentation

Building and Using Pluggable Type-Checkers Werner M. Dietl Joint work with: Stephanie Dietzel, Michael D. Ernst, Kvan Mulu, and Todd W. Schiller 1 Software still has errors 2 Static type systems 0 errors, Crashes 0 warnings Source


slide-1
SLIDE 1

1

Joint work with: Stephanie Dietzel, Michael D. Ernst, Kıvanç Muşlu, and Todd W. Schiller

Werner M. Dietl

Building and Using Pluggable Type-Checkers

slide-2
SLIDE 2

2

Software still has errors

slide-3
SLIDE 3

3

Static type systems

Source Code Compiler, Type Checker Executable

Crashes

0 errors, 0 warnings

slide-4
SLIDE 4

4

Static type systems

  • Java/C# provide limited type systems
  • Static type systems could prevent:
  • Null-pointer exceptions [Fähndrich & Leino '03]
  • Unwanted mutations [Tschantz & Ernst '05]
  • Concurrency errors [Boyapati et al. '02, Cunningham et al. '07]
  • Express additional facts about a program
  • Statically ensure absence of certain errors
slide-5
SLIDE 5

5

Pluggable type checkers

Source Code Compiler, Type Checker Add Annotations Fix Bugs Executable Pluggable Type Checker Warnings

slide-6
SLIDE 6

6

Pluggable type checkers

Source Code Compiler, Type Checker Add Annotations Fix Bugs Executable Warnings Pluggable Type Checker Pluggable Type Checker Pluggable Type Checker Guarantees partial correctness!

slide-7
SLIDE 7

7

Pluggable type systems

Example: Ensure encrypted communication void send(@Encrypted String msg) {…}

@Encrypted String msg1 = ...;

send(msg1); // OK String msg2 = ....; send(msg2); // Warning!

slide-8
SLIDE 8

8

The Checker Framework

  • A framework for pluggable type checkers
  • “Plugs” into the OpenJDK compiler
  • Easy to use

javac -processor EncryptionChecker …

  • Eclipse plug-in, Ant and Maven integration
slide-9
SLIDE 9

9

Lack of uptake of pluggable types

Common assumptions:

  • Testing finds all important bugs
  • Usage adds annotation clutter
  • Learning their usage is hard
  • Building checkers is difficult

These were true before the Checker Framework. Do they still apply?

slide-10
SLIDE 10

10

Our contribution: case studies

  • Checkers reveal important latent bugs
  • Ran on 2 million LOC of real-world code
  • Found 40 user-visible bugs, hundreds of mistakes
  • Annotation overhead is low
  • Mean 2.6 annotations per kLOC
  • Learning their usage is easy
  • Used successfully by first-year CS majors
  • Building checkers is easy
  • New users developed 3 new realistic checkers
slide-11
SLIDE 11

11

Kinds of case studies

  • 2 existing type checkers
  • Absence of null-pointer exceptions
  • Correct use of object and reference equality
  • 3 new type checkers
  • Correct compiler message key substitution
  • Consistent use of integer constants as enums
  • Consistency of Java class name strings
  • Classroom study
  • Nullness checker used by first-year CS majors
slide-12
SLIDE 12

12

Case study subject programs

Swing: 610 kLOC Lucene: 479 kLOC Xerces: 257 kLOC OpenJDK (17 packages): 231 kLOC Daikon: 222 kLOC JabRef: 117 kLOC Google Collections: 78 kLOC GanttProject: 69 kLOC ASM: 33 kLOC Checker Framework: 31 kLOC Annotation File Utilities: 17 kLOC We manually annotated each program for one type system until all warnings were eliminated.

slide-13
SLIDE 13

13

Outline

  • 1. Motivation
  • 2. Checkers reveal important latent bugs
  • 3. Annotation overhead is low
  • 4. Learning the usage is easy
  • 5. Building checkers is easy
slide-14
SLIDE 14

14

Nullness Checker:

  • 9 crashing bugs in Google Collections
  • 45000 tests (2/3 of the LOC)
  • Uses FindBugs @Nullable annotations,

no FindBugs warnings

  • >90 bugs in Daikon
  • 1. Checkers reveal important latent bugs
slide-15
SLIDE 15

15

Example from Google Collections: class ForMapWithDefault { @Nullable Object defaultValue; public int hashCode() { return map.hashCode() + defaultValue.hashCode(); } … }

java.lang.NullPointerException

Reveals bugs: null-pointer exceptions

slide-16
SLIDE 16

16

  • JDK's String representations of class names:
  • Fully qualified names: package.Outer.Inner
  • Binary names:

package.Outer$Inner

  • Field descriptors:

Lpackage/Outer$Inner;

  • Important to keep them separated

Unqualified BinaryName FullyQualifiedName FieldDescriptor

Reveals bugs: Java signatures

slide-17
SLIDE 17

17

Reveals bugs: Java signatures

Signature Checker:

  • 11 crashing bugs in OpenJDK
  • 13 in libraries
slide-18
SLIDE 18

18

Example from java.lang.Class:

static Class<?> forName(String className) “Returns the Class object associated with the class or interface with the given string name. ... Parameters: className - the fully qualified name of the desired class”

Class.forName(“package.Outer.Inner”) Class.forName(“package.Outer$Inner”)

java.lang.ClassNotFoundException OK!

Reveals bugs: Java signatures

slide-19
SLIDE 19

19

  • 2. Annotation overhead is low

Nullness: 13 Ann./kLOC Signature: 1.5 Ann./kLOC Fenum: 1.1 Ann./kLOC Interning: 0.52 Ann./kLOC Compiler Msgs.: 0.35 Ann./kLOC

slide-20
SLIDE 20

20

Annotation overhead is low

  • Good defaults
  • Non-Null Except Locals reflects common usage

– Fields, parameters, … are @NonNull – Only local variables are @Nullable

  • Define defaults using the tree kind, type kind, or

regular expressions

  • Flow-sensitive local inference

@Nullable Object o;

  • = new Object();
  • .toString(); // OK! o inferred non-null!
slide-21
SLIDE 21

21

  • 3. Learning their usage is easy
  • 28 first-year CS majors at UW
  • Assignment: prove absence of NPE
  • Mean code size: 9 kLOC
  • Result: all students fixed unknown bugs!
  • Invested time:
  • 2 hours of demos and instructions
  • 5.6 hours spent on assignment on average
slide-22
SLIDE 22

22

  • 4. Building checkers is easy

Example: Ensure encrypted communication void send(@Encrypted String msg) {…}

@Encrypted String msg1 = ...;

send(msg1); // OK String msg2 = ....; send(msg2); // Warning! The complete checker: @TypeQualifier @SubtypeOf(Unqualified.class) public @interface Encrypted {}

Unqualified Encrypted

slide-23
SLIDE 23

23

Signature String Checker

  • JDK's String representations of class names:
  • Fully qualified names: package.Outer.Inner
  • Binary names:

package.Outer$Inner

  • Field descriptors:

Lpackage/Outer$Inner;

  • Important to keep them separated

Unqualified BinaryName FullyQualifiedName FieldDescriptor

slide-24
SLIDE 24

24

Signature String Checker

@TypeQualifier @SubtypeOf({Unqualified.class}) @ImplicitFor(stringPatterns="^[A-Za-z_] [A-Za-z_0-9]*(\\.[A-Za-z_][A-Za-z_0-9] *)*(\\[\\])*$") public @interface FullyQualifiedName {} @TypeQualifier @SubtypeOf({Unqualified.class}) @ImplicitFor(stringPatterns="^[A-Za-z_] [A-Za-z_0-9]*(\\.[A-Za-z_][A-Za-z_0-9] *)*(\\$[A-Za-z_][A-Za-z_0-9]*)?(\\[\\] )*$") public @interface BinaryName {} @TypeQualifier @SubtypeOf({Unqualified.class}) @ImplicitFor(stringPatterns="^\\[*([BCDF IJSZ]|L[A-Za-z_][A-Za-z_0-9]*(/[A-Za-z_] [A-Za-z_0-9]*)*(\\$[A-Za-z_][A-Za-z_0-9] *)?;)$") public @interface FieldDescriptor {} @TypeQualifier @SubtypeOf({BinaryName.class, FullyQualifiedName.class}) public @interface SourceName {} @TypeQualifier @SubtypeOf({Unqualified.class}) public @interface MethodDescriptor {} @TypeQualifier @SubtypeOf({BinaryName.class, FieldDescriptor.class, SourceName.class, FullyQualifiedName.class, MethodDescriptor.class}) @ImplicitFor(trees={Tree.Kind.NULL_LITERAL}) public @interface SignatureBottom {} @TypeQualifiers({BinaryName.class, FullyQualifiedName.class, SourceName.class, FieldDescriptor.class, Unqualified.class, MethodDescriptor.class, SignatureBottom.class}) public final class SignatureChecker extends BaseTypeChecker {}

Type Qualifiers Type Checker

slide-25
SLIDE 25

25

Signature String Checker

  • Written by a first-year graduate student without

prior experience with the framework

  • Found 11 crashing bugs in OpenJDK,

13 more in libraries

  • Example:

class Class<T> { Class<?> forName(@BinaryName String className); @BinaryName String getName(); @FullyQualifiedName String getCanonicalName(); } String name = myclass.getCanonicalName(); Class.forName(name); // Warning

slide-26
SLIDE 26

26

Building complex checkers is possible

Nullness Checker is actually 3 checkers:

  • Correct object initialization
  • Nullness itself
  • Correct usage of keys in map accesses

Refined defaulting:

  • Refined flow-sensitive inference
  • Heuristics for Map.get behavior
slide-27
SLIDE 27

27

Checker Code Sizes

Nullness Checker: 4311 LOC Interning Checker: 960 LOC Fake Enumerations Checker: 489 LOC Signature Strings Checker: 167 LOC Compiler Messages Checker: 70 LOC

slide-28
SLIDE 28

28

Applicability of type checkers

  • Many properties amenable to static checking
  • Concurrency
  • Object encapsulation
  • Energy efficiency
  • Even dependencies on external information
  • Look for properties that depend on the static

structure and not the behavior of code

  • Value sound results over heuristics
slide-29
SLIDE 29

29

Conclusions

  • 1. Checkers reveal important latent bugs
  • 2. Annotation overhead is low
  • 3. Learning their usage is easy
  • 4. Building checkers is easy

It is easy to improve the quality of your Java code, and you should start today! http://checker-framework.googlecode.com/