Fixing bugs in nullness analyzer

This commit is contained in:
Alexey Andreev 2017-01-02 16:55:17 +03:00
parent 5fd95f21cb
commit aef1e2e206
12 changed files with 179 additions and 8 deletions

View File

@ -70,4 +70,8 @@ public class Variable implements VariableReader {
public void setLabel(String label) {
this.label = label;
}
public String getDisplayLabel() {
return label != null ? label : String.valueOf(index);
}
}

View File

@ -84,9 +84,9 @@ class NullnessInformationBuilder {
}
private void extendProgram() {
notNullVariables.set(0);
insertAdditionalVariables();
notNullVariables.set(0);
Variable[] parameters = new Variable[methodDescriptor.parameterCount() + 1];
for (int i = 0; i < parameters.length; ++i) {
parameters[i] = program.variableAt(i);
@ -123,7 +123,7 @@ class NullnessInformationBuilder {
for (BasicBlock block : program.getBasicBlocks()) {
for (Phi phi : block.getPhis()) {
for (Incoming incoming : phi.getIncomings()) {
builder.addEdge(incoming.getSource().getIndex(), phi.getReceiver().getIndex());
builder.addEdge(incoming.getValue().getIndex(), phi.getReceiver().getIndex());
}
}
for (TryCatchBlock tryCatch : block.getTryCatchBlocks()) {
@ -183,6 +183,7 @@ class NullnessInformationBuilder {
continue;
}
visited[node] = true;
notNullVariables.set(node);
for (int successor : assignmentGraph.outgoingEdges(node)) {
if (sccIndexes[successor] == 0 || sccIndexes[successor] != sccIndexes[node]) {
if (--notNullPredecessorsLeft[successor] == 0) {
@ -203,6 +204,10 @@ class NullnessInformationBuilder {
public State visit(BasicBlock block) {
currentState = new State();
if (block.getExceptionVariable() != null) {
notNullVariables.set(block.getIndex());
}
currentBlock = block;
if (nullSuccessors.containsKey(block.getIndex())) {
int varIndex = nullSuccessors.remove(block.getIndex());

View File

@ -488,7 +488,7 @@ public class PhiUpdater {
private Variable use(Variable var) {
Variable mappedVar = variableMap[var.getIndex()];
if (mappedVar == null) {
throw new AssertionError("Variable used before definition: " + var.getIndex());
throw new AssertionError("Variable used before definition: " + var.getDisplayLabel());
}
usedPhis.set(mappedVar.getIndex());
return mappedVar;

View File

@ -60,6 +60,21 @@ public class NullnessAnalysisTest {
test();
}
@Test
public void phiPropagation() {
test();
}
@Test
public void tryCatchJoint() {
test();
}
@Test
public void loop() {
test();
}
private void test() {
String baseName = "model/analysis/nullness/" + name.getMethodName();
String originalResourceName = baseName + ".original.txt";

View File

@ -0,0 +1,21 @@
var @this as this
$start
@first := field `Foo.first` @this as `LBar;`
@checkedFirst := nullCheck @first
goto $head
$head
@current := phi @checkedFirst from $start, @checkedNext from $body
@isLast := field `Bar.last` @current as Z
@current_2 := nullCheck @current
if @isLast == 0 then goto $exit else goto $body
$body
@next := field `Bar.next` @current_2 as `LBar;`
@checkedNext := nullCheck @next
goto $head
$exit
@value := field `Bar.value` @current_2 as `Ljava/lang/Object;`
return @value
// NOT_NULL current
// NOT_NULL current_2

View File

@ -0,0 +1,17 @@
var @this as this
$start
@first := field `Foo.first` @this as `LBar;`
@checkedFirst := nullCheck @first
goto $head
$head
@current := phi @checkedFirst from $start, @checkedNext from $body
@isLast := field `Bar.last` @current as Z
if @isLast == 0 then goto $exit else goto $body
$body
@next := field `Bar.next` @current as `LBar;`
@checkedNext := nullCheck @next
goto $head
$exit
@value := field `Bar.value` @current as `Ljava/lang/Object;`
return @value

View File

@ -1,4 +1,7 @@
var @this as this
$start
@cond := invokeStatic `Cond.get()Ljava/lang/Object;`
if @cond === null then goto $ifNull else goto $ifNotNull
$ifNull
@cond_1 := null
@ -14,8 +17,8 @@ $join
@d := phi @a from $ifNull, @b_1 from $ifNotNull
return @c
// NULLABLE: b
// NOT_NULL: b_1
// NOT_NULL: a
// NULLABLE: c
// NOT_NULL: d
// NULLABLE b
// NOT_NULL b_1
// NOT_NULL a
// NULLABLE c
// NOT_NULL d

View File

@ -1,4 +1,7 @@
var @this as this
$start
@cond := invokeStatic `Cond.get()Ljava/lang/Object;`
if @cond === null then goto $ifNull else goto $ifNotNull
$ifNull
@a := 'qwe'

View File

@ -0,0 +1,27 @@
var @this as this
$start
@a := invokeStatic `Value.extract()Ljava/lang/String;`
@cond := invokeStatic `Cond.invoke()I`
if @cond == 0 then goto $if0 else goto $else
$if0
@len := invokeStatic `Value.extractLen()I`
if @len == 0 then goto $ifEmpty else goto $ifNotEmpty
$ifEmpty
invokeStatic `Foo.f(Ljava/lang/String;)V` @a
goto $joinIf0
$ifNotEmpty
invokeVirtual `java.lang.String.trim()Ljava/lang/String;` @a
@a_1 := nullCheck @a
goto $joinIf0
$joinIf0
@a_2 := phi @a from $ifEmpty, @a_1 from $ifNotEmpty
invokeStatic `Foo.g(Ljava/lang/String;)V` @a_2
goto $join
$else
invokeStatic `Foo.bar(Ljava/lang/String;)V` @a
goto $join
$join
@a_3 := phi @a_2 from $joinIf0, @a from $else
invokeStatic `Foo.baz(Ljava/lang/String;)V` @a_3
return

View File

@ -0,0 +1,24 @@
var @this as this
$start
@a := invokeStatic `Value.extract()Ljava/lang/String;`
@cond := invokeStatic `Cond.invoke()I`
if @cond == 0 then goto $if0 else goto $else
$if0
@len := invokeStatic `Value.extractLen()I`
if @len == 0 then goto $ifEmpty else goto $ifNotEmpty
$ifEmpty
invokeStatic `Foo.f(Ljava/lang/String;)V` @a
goto $joinIf0
$ifNotEmpty
invokeVirtual `java.lang.String.trim()Ljava/lang/String;` @a
goto $joinIf0
$joinIf0
invokeStatic `Foo.g(Ljava/lang/String;)V` @a
goto $join
$else
invokeStatic `Foo.bar(Ljava/lang/String;)V` @a
goto $join
$join
invokeStatic `Foo.baz(Ljava/lang/String;)V` @a
return

View File

@ -0,0 +1,29 @@
var @this as this
$start
@bar := invokeVirtual `Foo.bar()LBar;` @this
@cond := invokeVirtual `Foo.cond()I` @this
if @cond == 0 then goto $if0 else goto $else
$if0
invokeVirtual `Bar.baz()LBar;` @bar
@bar_1 := nullCheck @bar
goto $join
catch java.lang.RuntimeException goto $if0Handler
@bar_2 := ephi @bar, @bar_1
$if0Handler
goto $join
$else
invokeVirtual `Bar.baz()LBar;` @bar
@bar_3 := nullCheck @bar
goto $else1
$else1
invokeVirtual `Bar.baz2()LBar;` @bar_3
goto $join
catch java.lang.RuntimeException goto $elseHandler
$elseHandler
goto $join
$join
@bar_4 := phi @bar_1 from $if0, @bar_2 from $if0Handler, @bar_3 from $else1, @bar_3 from $elseHandler
return @bar_4
// NULLABLE bar_2

View File

@ -0,0 +1,23 @@
var @this as this
$start
@bar := invokeVirtual `Foo.bar()LBar;` @this
@cond := invokeVirtual `Foo.cond()I` @this
if @cond == 0 then goto $if0 else goto $else
$if0
invokeVirtual `Bar.baz()LBar;` @bar
goto $join
catch java.lang.RuntimeException goto $if0Handler
$if0Handler
goto $join
$else
invokeVirtual `Bar.baz()LBar;` @bar
goto $else1
$else1
invokeVirtual `Bar.baz2()LBar;` @bar
goto $join
catch java.lang.RuntimeException goto $elseHandler
$elseHandler
goto $join
$join
return @bar