Assignment Abstract Interpretation
Pt
Introduction
In this assignment you will implement an
intra-procedural typestate
propagation strategy for a subset of Java, which generalizes the
flow-sensitive data-flow transformations from the previous assignments
to propagation of
properties. Your strategy should be extensible
such that it can deal with typestates for different types.
Unitialized Variables
The basic example you should implement is checking that local variables are initialized before being used.
Null Pointers
Next example is tracking of variables that possibly contain null pointers.
The following examples illustrate some typical situations.
1
void foo() {
List l;
l.bar();
}
l is null here
2
void foo() {
List l;
if(whatever)
l = new ArrayList();
}
l may be null after this point
3
void foo(List l2) {
List l1;
l1 = l2;
}
nullness of l1 depends on l2
4
void bar(String arg) {
if (arg == null) {
return;
}
...
}
After this point, up to the next assignment to arg we know that arg is
not null.
5
void bar(A arg) {
if (arg != null) {
arg.foo();
}
}
Access to arg is safe.
6
In the next examples, the boolean operators play an important role. Depending on the location of the null pointer check, we might know whether the variable is null or not. So, the access to arg is not necessarily safe.
void bar(A arg) {
if (whatever || arg != null) {
arg.foo();
}
}
void bar(A arg) {
if (whatever && blaat || arg != null) {
arg.foo();
}
}
However, in the next case the null check is first, so we know now that arg can never be null in the then branch of the if.
void bar(A arg) {
if (arg != null && whatever) {
arg.foo();
}
}
7
void process(String s)
{
int len = s.length();
}
Here s may be null; should be guarded
void process(String s)
{
if (s != null)
{
int len = s.length();
...
}
else
...
}
8
Note that you need information about methods that are invoked from your program. In the next example you need to know if the
substring method will return null or not, depending on its inputs. For this assignment, we assume that you have knowledge about the behaviour of these methods, so you can define the nullness of the methods that are used in your testcases.
void process(String s1)
{
String s2 = s1.substring(1);
}
Remarks
Consider lazy logical operators (and, or), control-flow, conditional
returns and throws.
Always Unlock
Further extend your typestate checker to check proper use of the
Lock
interface; locks should be unlocked in the lexical scope of the lock.
Lock l = ...;
l.lock();
try {
// access the resource protected by this lock
} finally {
l.unlock();
}