Lecture04 Type Systems
Lecture04 Type Systems
http://CheckerFramework.org/
Twitter: @CheckerFrmwrk
Live demo: http://CheckerFramework.org/live/
java.lang.NullPointerException
Cost of software failures
$312 billion per year global cost of software bugs (2013)
$300 billion dealing with the Y2K problem
System.console().readLine();
Collections.emptyList().add("one");
Java's type system is too weak
Type checking prevents many errors
int i = "hello";
System.console().readLine();
UnsupportedOperationException
Collections.emptyList().add("one");
Some errors are silent
Date date = new Date();
myMap.put(date, "now");
date.setSeconds(0); // round to minute
myMap.get(date);
Some errors are silent
Date date = new Date();
myMap.put(date, "now");
date.setSeconds(0); // round to minute
myMap.get(date);
Corrupted map
Some errors are silent
dbStatement.executeQuery(userInput);
Some errors are silent
dbStatement.executeQuery(userInput);
Fix bugs
Change types Errors
Optional Type Checking
No errors
Source Compiler Executable
Guaranteed
Fix bugs Optional behavior
Change types Errors
Type Checker
Fix bugs
Add/change
Warnings
annotations
Optional Type Checking
No errors
Source Compiler Executable
Guaranteed
Fix bugs Optional
Optional behavior
Change types Errors Optional
Type
TypeChecker
TypeChecker
Checker
Fix bugs
Add/change
Warnings
annotations
Prevent null pointer exceptions
Type system that statically guarantees that:
the program only dereferences
known non-null references
Types of data:
@NonNull reference is never null
@Nullable reference may be null
Null pointer exception
● Possible negatives:
○ Must write the types (or use type inference)
○ False positives are possible (can be suppressed)
The Checker Framework
A framework for pluggable type checkers
“Plugs” into the OpenJDK or OracleJDK compiler
Testing Pluggable
Type Systems
Built-in Type
Systems
Formal
Verification
Guarantees
Demo: regular expression errors
@Regex = valid regular expression
OK: “colou?r”
NOT: “1) first point”
@Regex(2) = has 2+ capturing groups
OK: “((Linked)?Hash)?Map”
OK: “(http|ftp)://([^/]+)(/.*)?”
NOT: “(brown|beige)”
Regular Expression Example
public static void main(String[] args) {
String regex = args[0];
String content = args[1];
Pattern pat = Pattern.compile(regex);
Matcher mat = pat.matcher(content);
if (mat.matches()) {
System.out.println("Group: " + mat.group(1));
}
}
Regular Expression Example
public static void main(String[] args) {
String regex = args[0];
String content PatternSyntaxException
= args[1];
Pattern pat = Pattern.compile(regex);
Matcher mat = pat.matcher(content);
IndexOutOfBoundsExceptionon
if (mat.matches()) {
System.out.println("Group: " + mat.group(1));
}
}
Fixing the Errors
Pattern.compile only on valid regex
Matcher.group(i) only if > i groups
...
if (!RegexUtil.isRegex(regex, 1)) {
System.out.println("Invalid: " + regex);
System.exit(1);
}
...
Since Java 5: declaration annotations
Only for declaration locations:
@Deprecated
class Foo {
@Getter @Setter private String query;
@SuppressWarnings("unchecked")
void foo() { … }
}
But we couldn’t express
A non-null reference to my data
An interned string
Annotations in comments:
source code)
Checker Framework facilities
● Full type systems: inheritance, overriding, ...
● Generics (type polymorphism)
○ Also qualifier polymorphism
● Qualifier defaults
● Pre-/post-conditions
● Warning suppression
Brainstorming new type checkers
What runtime exceptions to prevent?
What properties of data should always hold?
What operations are legal and illegal?
Type-system checkable properties:
● Dependency on values
String s = "%+s%";
//:: error: (format.string.invalid)
f.format(s, "illegal");
Defining a type system
1. Qualifier hierarchy
○ defines subtyping
2. Type introduction rules
○ types for expressions
3. Type rules
○ checker-specific errors
4. Flow-refinement
○ better types than the programmer wrote
Defining a type system
1. Qualifier hierarchy
○ subtyping, assignments @UnknownRegex
@SubtypeOf(UnknownRegex.class) @Regex
public @interface Regex {
Defining a type system
Data d = new Data();
2. Type introduction rules
○ types for expressions
@ImplicitFor( trees = {
Tree.Kind.NEW_CLASS,
Tree.Kind.NEW_ARRAY, ... })
@DefaultQualifierInHierarchy
@DefaultForUnannotatedCode({
DL.PARAMETERS, DL.LOWER_BOUNDS })
Defining a type system
synchronized(xxx) {
3. Type rules }
○ checker-specific errors
void visitSynchronized(SynchronizedTree node) {
ExpressionTree expr = node.getExpression();
AnnotatedTypeMirror type =
getAnnotatedType(expr);
if (!type.hasAnnotation(NONNULL))
checker.report(Result.failure(...), expr);
}
Defining a type system
if (x != null) {
4. Flow-refinement x.f = …; // valid
○ better types than the programmer wrote
if (ElementUtils.matchesElement(method,
IS_REGEX_METHOD_NAME,
String.class, int.class)) {
…
}
Dataflow Framework
Goal: Compute properties about expressions
● More accurate types than the user wrote
● Foundation for other static analyses
○ e.g. by Google error-prone and Uber NullAway
Dataflow Framework user provides
● What are we tracking?
● What do operations do?
● What are intermediate results?
Dataflow Framework does all the work!
Tips
● Start by type-checking part of your code
● Only type-check properties that matter to you
● Use subclasses (not type qualifiers) if possible
● Write the spec first (and think of it as a spec)
● Avoid warning suppressions when possible
● Avoid raw types such as List; use List<String>
Verification Bug-finding
• Goal: • Goal:
prove that no bug exists find some bugs at low cost
• Specifications: • Specifications:
user provides infer likely specs
• False negatives: • False negatives:
none acceptable
• False positives: • False positives:
user suppresses warnings heuristics focus on most
important bugs
• Downside: user burden • Downside: missed bugs
Checker Framework Community
Open source project:
https://github.com/typetools/checker-framework
http://CheckerFramework.org/