CSE 331 Software Design and Implementation

Slides:



Advertisements
Similar presentations
Identity and Equality Based on material by Michael Ernst, University of Washington.
Advertisements

Composition CMSC 202. Code Reuse Effective software development relies on reusing existing code. Code reuse must be more than just copying code and changing.
Preventing bugs with pluggable type checking Michael D. Ernst University of Washington Object x)
GUI Threading Explained and Verified Michael Ernst U. of Washington, Seattle, USA IMDEA Software Institute, Madrid, Spain joint work with Colin Gordon,
CSE 341, Winter Type Systems Terms to learn about types: –Type –Type system –Statically typed language –Dynamically typed language –Type error –Strongly.
Detecting and preventing bugs with pluggable type-checking Michael D. Ernst University of Washington Joint work with Mahmood Ali, Werner Dietl, …
Declaring and Checking Non-null Types in an Object-Oriented Language Authors: Manuel Fahndrich K. Rustan M. Leino OOPSLA’03 Presenter: Alexander Landau.
CSE341: Programming Languages Lecture 11 Type Inference Dan Grossman Winter 2013.
Usable code verification: balancing costs and benefits Two nuggets: User-defined verifiers Crowd-sourced verification Michael D. Ernst University of Washington.
Designing Programming Languages to Improve Software Quality David J. Pearce Software Quality New Zealand, August
Types for Programs and Proofs Lecture 1. What are types? int, float, char, …, arrays types of procedures, functions, references, records, objects,...
1 Abstraction  Identify important aspects and ignore the details  Permeates software development programming languages are abstractions built on hardware.
Object x) { List lst; … } Detecting and preventing null pointer errors with pluggable type-checking CSE 331 University of Washington.
CS 11 java track: lecture 1 Administrivia need a CS cluster account cgi-bin/sysadmin/account_request.cgi need to know UNIX
Preventing bugs with pluggable type checking Michael Ernst and Mahmood Ali University of Washington and MIT OOPSLA 2009 tutorial Object.
User-defined type checkers for error detection and prevention in Java Michael D. Ernst MIT Computer Science & AI Lab
Types in programming languages1 What are types, and why do we need them?
Topic 3: C Basics CSE 30: Computer Organization and Systems Programming Winter 2011 Prof. Ryan Kastner Dept. of Computer Science and Engineering University.
Demo of Scalable Pluggable Types Michael Ernst MIT Dagstuhl Seminar “Scalable Program Analysis” April 17, 2008.
CMSC 330: Organization of Programming Languages Java Generics.
ANU COMP2110 Software Design in 2003 Lecture 10Slide 1 COMP2110 Software Design in 2004 Lecture 12 Documenting Detailed Design How to write down detailed.
JSR 308: Type Annotations Making Java annotations more general and more useful Spec leads: Michael Ernst & Alex Buckley Implementation lead: Werner Dietl.
How to execute Program structure Variables name, keywords, binding, scope, lifetime Data types – type system – primitives, strings, arrays, hashes – pointers/references.
Semantic Analysis II Type Checking EECS 483 – Lecture 12 University of Michigan Wednesday, October 18, 2006.
Detecting and preventing bugs with pluggable type-checking Michael D. Ernst University of Washington Joint work with Werner Dietl, and many others
PROGRAMMING PRE- AND POSTCONDITIONS, INVARIANTS AND METHOD CONTRACTS B MODULE 2: SOFTWARE SYSTEMS 13 NOVEMBER 2013.
CSSE501 Object-Oriented Development. Chapter 10: Subclasses and Subtypes  In this chapter we will explore the relationships between the two concepts.
Zach Tatlock / Winter 2016 CSE 331 Software Design and Implementation Lecture 13 Generics 1.
Preventing bugs with pluggable type checking Michael Ernst University of Washington Joint work with Mahmood Ali and Matthew Papi Object.
Preventing bugs with pluggable type-checking Michael Ernst MIT
Zach Tatlock / Winter 2016 CSE 331 Software Design and Implementation Lecture 16 Checker Framework.
Zach Tatlock / Winter 2016 CSE 331 Software Design and Implementation Lecture 6 Representation Invariants.
Heath Carroll Bill Hanczaryk Rich Porter.  A Theory of Type Polymorphism in Programming ◦ Robin Milner (1977)  Milner credited with introducing the.
Language-Based Security: Overview of Types Deepak Garg Foundations of Security and Privacy October 27, 2009.
Functional Programming
CSE341: Programming Languages Lecture 11 Type Inference
Javarifier: inference of reference immutability
Types for Programs and Proofs
Semantic Analysis Type Checking
Logger, Assert and Invariants
CSE 374 Programming Concepts & Tools
Reasoning about code CSE 331 University of Washington.
CSE 331 Subtyping slides created by Marty Stepp based on materials by M. Ernst, S. Reges, D. Notkin, R. Mercer, Wikipedia
C Basics.
Type Systems Terms to learn about types: Related concepts: Type
Pointers and References
Type Abstraction SWE Spring 2009.
Design by Contract Fall 2016 Version.
CSE 331 Software Design and Implementation
Extending Classes.
CSE341: Programming Languages Lecture 11 Type Inference
Generic programming in Java
CSE341: Programming Languages Lecture 11 Type Inference
CSE 331 Annotations slides created by Marty Stepp based on materials by M. Ernst, S. Reges, D. Notkin, R. Mercer, Wikipedia
CSE 331 Software Design and Implementation
(Computer fundamental Lab)
CSE341: Programming Languages Lecture 11 Type Inference
Assertions References: internet notes; Bertrand Meyer, Object-Oriented Software Construction; 4/25/2019.
Effective Java, Chapter 9: Exceptions
Type Systems Terms to learn about types: Related concepts: Type
Lecture 13: Subtyping Rules Killer Bear Climber
Computer Science 340 Software Design & Testing
CSE341: Programming Languages Lecture 11 Type Inference
Type Abstraction SWE Spring 2013.
Java Annotations for Invariant Specification
Software Specifications
CSE341: Programming Languages Lecture 11 Type Inference
CS 240 – Advanced Programming Concepts
Presentation transcript:

CSE 331 Software Design and Implementation Lecture 9 Checker Framework Zach Tatlock / Winter 2017

Motivation java.lang.NullPointerException

Problem: Your code has bugs Who discovers the problems? If you are very lucky, testing discovers some If you are unlucky, your customer discovers them If you are very unlucky, criminals discover them If you are smart, the compiler discovers them It’s better to be smart than lucky not “hackers” !

Java’s type checking is too weak Type checking prevents many bugs int i = “hello”; // type mismatch myString.getDate(); // method not found Type checking doesn’t prevent all bugs System.console().readLine(); NullPointerException Collections.emptyList().add(“One”); UnsupportedOperationException Programmers wish to prevent these.

Some errors are silent Date date = new Date(0); myMap.put(date, “Java epoch”); date.setYear(70); myMap.put(date, “Linux epoch”); Corrupted map dbStatement.executeQuery(userInput); SQL injection attack Initialization, data formatting, equality tests, … Also: UnsupportedOperationException, SQLException

Type indicates legal operations Type checking prevents many bugs int i = “hello”; myString.getDate(); Goal: avoid NullPointerException Idea: use types to indicate legality Consider references (pointers) as an ADT Operation: dereferencing x.field, x.method()

Types for null pointer prevention Replace Object by two new types NonNullObject Dereference is permitted NonNullObject nn; nn.field nn.method() PossiblyNullObject Dereference is forbidden PossiblyNullObject pn; pn.field // compile-time error pn.method() // compile-time error Problems: Can you use PossiblyNullObject for anything? Must rewrite all your Java applications and libraries

Type qualifiers Java 8: annotations on types @Untainted String query; List<@NonNull String> strings; myGraph = (@Immutable Graph) tmpGraph; class UnmodifiableList<T> implements @Readonly List<@Readonly T> {} Backward-compatible: compile with any Java compiler List</*@NonNull*/ String> strings;

Compile-time checking Write type qualifiers in code @NonNull Date date1 = new Date(); @Nullable Date date2 = null; Type checker warns about violations (bugs) date1.setTime(70); // OK date2.setTime(70); // compile-time error

Benefits of type qualifiers Find bugs in programs Guarantee the absence of errors Improve documentation Improve code structure & maintainability Aid compilers, optimizers, and analysis tools Reduce number of assertions and run-time checks Possible negatives: Must write the types (or use type inference) False positives are possible (can be suppressed)

Types for null-pointer-prevention Which type hierarchy is best? @NonNull Date @Nullable Date @?? Date @Nullable Date @NonNull Date @Nullable Date @NonNull Date A subtype has fewer values A subtype has more operations A subtype is substitutable A subtype preserves supertype properties

Mutability subtyping relationship Which type hierarchy is best? @Immutable Date @Mutable Date @ReadOnly Date @?? Date @Mutable Date @Immutable Date @Immutable Date @Mutable Date @Immutable: no one can do mutation @Mutable: anyone can do mutation @ReadOnly I can’t do mutation No guarantee about mutation from elsewhere

What bugs can you find & prevent? The annotation you write: Null dereferences @NonNull Mutation and side-effects @Immutable Concurrency: locking @GuardedBy Security: encryption, @Encrypted tainting @Untainted Aliasing @Linear Equality tests @Interned Strings: localization, @Localized regular expression syntax @Regex Typestate (e.g., open/closed files @State You can write your own checker!

Using a checker Run in IDE or on command line Works as a compiler plug-in (annotation processor) Uses familiar error messages % javac –processor NullnessChecker MyFile.java MyFile.java:9: incompatible types. nonNullVar = nullableValue; ^ found : @Nullable String required: @NonNull String

Using a checker Run in IDE or on command line Works as a compiler plug-in (annotation processor) Uses familiar error messages % javac –processor NullnessChecker MyFile.java MyFile.java:9: incompatible types. nonNullVar = nullableValue; ^ found : @Nullable String required: @NonNull String

What is checked Proper use of the type hierarchy assignments method calls and returns overriding Proper use of methods and operations No dereferences of possibly-null values

What the checker guarantees Program satisfies type property no bugs (of particular varieties) no wrong annotations Caveat 1: only for code that is checked Native methods Reflection Code compiled without the pluggable type checker Suppressed warnings Indicates what code a human should analyze Checking part of a program is still useful Caveat 2: The checker itself may contain an error

Static and dynamic typing Static typing Compiler guarantees some errors cannot happen The set of errors depends on the language Other errors are still possible! Examples: C, C++, Java, C#, ML, Haskell Dynamic typing Run-time system tracks types, and throws errors Examples: Racket, Perl, PHP, Python, Ruby, JS No type system Example: Assembly

Why we  static typing Documentation Correctness/reliability Refactoring Speed

Why we  dynamic typing (= Why we static typing) More concise code Type inference is possible No false positive warnings Every static type system rejects some correct programs @NonNull String lineSep = System.getProperty("line.separator"); More flexible code Add fields at run time Change class of an object Ability to run tests at any time Feedback is important for quality code Programmer knows whether static or dynamic feedback is best

Advanced features Avoiding the limitations of the conservative, static type-checker

Flow sensitivity Control flow determines the type if (x==null) { ... // treat as nullable } else { ... // treat as non-null } Can refine the type to a subtype

More flow sensitivity Which calls type-check? Which calls ought to? Object name; name = new Object(); name.toLowerCase(); name = “HELLO”; @Nullable String name; name = null; name.toLowerCase(); name = “HELLO”;

Flow sensitivity: permit changes Legal changes: change to a subtype Illegal changes: change to a supertype Violates the declaration @Nullable String name; name = “hello”; ... // treat name as non-null name = otherNullable; ... // treat name as nullable String name; name = new Object(); ... // treat name as Object @NonNull String name; name = null; ... // treat name as nullable

Local type inference Bottom line: Default for nullness checker: Rarely write annotations on local variables Default for nullness checker: Non-null except locals Locals default to nullable (top of hierarchy) Flow-sensitivity changes this as needed

Receiver is just another parameter How many arguments does Object.equals take? class MyClass { @Override public boolean equals(Object other) { … } } Two! Their names are this and other Neither one is mutated by the method Java 8 syntax: public boolean equals(@Readonly MyClass this, @ReadOnly Object other) {…} For backwards compatibility: public boolean equals(/*>>>@Readonly MyClass this,*/ @ReadOnly Object other) {…} Optional syntax, for annotations

Find potential null pointer error class C { @Nullable Object currentObj; // If currentObj is non-null, // prints it and a timestamp void printCurrent() { if (currentObj != null) { System.out.println(this.getTimeStamp()); System.out.println(currentObj.toString()); } Object getTimeStamp() { … }

Lack of side effects class C { @Nullable Object currentObj; // If currentObj is non-null, // prints it and a timestamp void printCurrent() { if (currentObj != null) { System.out.println(this.getTimeStamp()); System.out.println(currentObj.toString()); } @Pure Object getTimeStamp() { … }

Lazy initialization class C { @LazyNonNull Object currentObj; // If currentObj is non-null, // prints it and a timestamp void printCurrent() { if (currentObj != null) { System.out.println(this.getTimeStamp()); System.out.println(currentObj.toString()); } Object getTimeStamp() { … }

Why doesn’t this typecheck? class C { @Nullable Object f; void m1() { setF(); f.hashCode(); } @AssertNonNullAfter(“this.f”) void setF() { this.f = new Object(); Type-checking is modular – reason from specs, not from implementation Libraries you call must be annotated (much of the JDK is provided) Possible NullPointerException Postcondition

Why doesn’t this typecheck? Non-null map from non-null String to non-null Date // Default: @NonNull class C { Map<String, Date> m; String getDateString(String k) { return m.get(k).toString(); } Non-null String Possible NullPointerException

Map keys // Default: @NonNull class C { Map<String, Date> m; String getDateString(@KeyFor(“m”) String k) { return m.get(k).toString(); } Map.get returns null if the key is not in the map

Map is a formal parameter class C { Date getDate(Map<String, Date> m, String k) { return m.get(k); } void useDate(Map<String, Date> m) { String s = “now”, Date d = new Date(); m.put(s, d); getDate(s);

Naming a formal parameter Use number, not name, for formal parameters.  Start counting at 1. class C { Date getDate(Map<String, Date> m, @KeyFor(“#1”) String key) { return m.get(k); } void useDate(Map<String, Date> m) { String s = “now”, Date d = new Date(); m.put(s, d); getDate(s);

How to annotate identity? String identity(String arg) { return arg; } void client() { // desired result: identity(“hello”).hashCode(); // OK; no warning identity(null).hashCode(); // compiler warning

How should identity be written? These types are too specific: String identity(String arg) { return arg; } We want to say: ThatSameType identity(AnyType arg) { In Java, this is expressed as: <T> T identity(T arg) { identity has many types: String  String Integer  Integer List<Date>  List<Date> Java automatically chooses the best type at each call site We also write this as: T. T  T Java calls this a generic method The standard term is polymorphism

Polymorphism over nullness @PolyNull String identity(@PolyNull String arg) { return arg; } void client() { identity(“hello”).hashCode(); // OK; no warning identity(null).hashCode(); // compiler warning @PolyNull is a hack is necessary for non-generic methods It is not necessary for generic methods: // No annotations, but type-checks just like identity(). <T> T identity2(T arg) {

Safe but un-annotatable code class Point { // rep invariant: either rep1 or rep2 is non-null XAndY rep1; RhoAndTheta rep2; float magnitude() { if (rep1 != null) { return Math.sqrt(rep1.x * rep1.x + rep1.y * rep1.y); } else { // We know rep2 is non-null at this point. return rep2.rho; } If you supported this, then where would the madness end?

How to run the Nullness Checker ant check-nullness Run ant from within Eclipse Eclipse plug-in More resources: Checker Framework manual http://types.cs.uw.edu/checker-framework/

Why run the Nullness Checker? In Winter 2011: Every student discovered null pointer bugs Students wished they had been using the Nullness Checker from the beginning of the quarter

Nullness annotation summary @Nullable @NonNull (rarely used) @LazyNonNull Preconditions: @NonNullOnEntry Postconditions: @Pure @AssertNonNullAfter @AssertNonNullIfTrue @AssertNonNullIfFalse Initialization: @Raw (rarely used) Maps: @KeyFor Polymorphism: @PolyNull (rarely used)

Key ideas Many “run-time errors” can actually be prevented at compile time A type system is a simple way of doing so A stronger type system more expressive This can be good or bad More practice understanding subtyping