blob: 6448f7c0486e21f59e5f96cd8b2e5f56c0438eac [file] [log] [blame]
package com.google.gwt.dev.jjs.impl.gflow.cfg;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.dev.jjs.ast.JMethodBody;
import com.google.gwt.dev.jjs.ast.JProgram;
import com.google.gwt.dev.jjs.impl.JJSTestBase;
import com.google.gwt.dev.jjs.impl.gflow.cfg.CfgBuilder;
import com.google.gwt.dev.jjs.impl.gflow.cfg.CfgEdge;
import com.google.gwt.dev.jjs.impl.gflow.cfg.Cfg;
import com.google.gwt.dev.jjs.impl.gflow.cfg.CfgNode;
import com.google.gwt.dev.util.Strings;
import java.util.List;
/**
* Test class for CfgBuilfer.
*/
public class CfgBuilderTest extends JJSTestBase {
@Override
protected void setUp() throws Exception {
super.setUp();
addSnippetClassDecl("static boolean b;");
addSnippetClassDecl("static boolean b1;");
addSnippetClassDecl("static boolean b2;");
addSnippetClassDecl("static boolean b3;");
addSnippetClassDecl("static int i;");
addSnippetClassDecl("static int j;");
addSnippetClassDecl("static int k;");
addSnippetClassDecl("static int l;");
addSnippetClassDecl("static int m;");
addSnippetClassDecl("static class CheckedException extends Exception {}");
addSnippetClassDecl(
"static class UncheckedException1 extends RuntimeException {}");
addSnippetClassDecl(
"static class UncheckedException2 extends RuntimeException {}");
addSnippetClassDecl("static RuntimeException runtimeException;");
addSnippetClassDecl("static CheckedException checkedException;");
addSnippetClassDecl("static UncheckedException1 uncheckedException1;");
addSnippetClassDecl("static UncheckedException2 uncheckedException2;");
addSnippetClassDecl("static int[] ii;");
addSnippetClassDecl("static int[] jj;");
addSnippetClassDecl("static void throwCheckedException() " +
"throws CheckedException {}");
addSnippetClassDecl("static void throwUncheckedException() {}");
addSnippetClassDecl("static void throwSeveralExceptions() " +
"throws CheckedException, UncheckedException1 {}");
addSnippetClassDecl("static class Foo { int i; int j; int k; }");
addSnippetClassDecl("static Foo createFoo() {return null;}");
}
public void testConstantAssignment() throws Exception {
assertCfg("void", "i = 1;").is(
"BLOCK -> [*]",
"STMT -> [*]",
"WRITE(i, 1) -> [*]",
"END");
}
public void testReferenceAssignment() throws Exception {
assertCfg("void", "i = j;").is(
"BLOCK -> [*]",
"STMT -> [*]",
"READ(j) -> [*]",
"WRITE(i, EntryPoint.j) -> [*]",
"END");
}
public void testModAssignment() throws Exception {
assertCfg("void", "i += 1;").is(
"BLOCK -> [*]",
"STMT -> [*]",
"READWRITE(i, null) -> [*]",
"END");
}
public void testDeclarationWithInitializer() throws Exception {
assertCfg("void", "int i = 1;").is(
"BLOCK -> [*]",
"STMT -> [*]",
"WRITE(i, 1) -> [*]",
"END");
}
public void testDeclarationWithInitializerRead() throws Exception {
assertCfg("void", "int i = j + k;").is(
"BLOCK -> [*]",
"STMT -> [*]",
"READ(j) -> [*]",
"READ(k) -> [*]",
"WRITE(i, EntryPoint.j + EntryPoint.k) -> [*]",
"END");
}
public void testDeclarationWithoutInitializer() throws Exception {
assertCfg("void", "int i;").is(
"BLOCK -> [*]",
"STMT -> [*]",
"END");
}
public void testBinopAssignment() throws Exception {
assertCfg("void", "i = j + k;").is(
"BLOCK -> [*]",
"STMT -> [*]",
"READ(j) -> [*]",
"READ(k) -> [*]",
"WRITE(i, EntryPoint.j + EntryPoint.k) -> [*]",
"END");
}
public void testPostIncrement() throws Exception {
assertCfg("void", "i++;").is(
"BLOCK -> [*]",
"STMT -> [*]",
"READWRITE(i, null) -> [*]",
"END");
}
public void testPreIncrement() throws Exception {
assertCfg("void", "++i;").is(
"BLOCK -> [*]",
"STMT -> [*]",
"READWRITE(i, null) -> [*]",
"END");
}
public void testConditional() throws Exception {
assertCfg("void", "i = b1 ? j : k;").is(
"BLOCK -> [*]",
"STMT -> [*]",
"READ(b1) -> [*]",
"COND (EntryPoint.b1) -> [THEN=*, ELSE=1]",
"READ(j) -> [2]",
"1: READ(k) -> [*]",
"2: WRITE(i, EntryPoint.b1 ? EntryPoint.j : EntryPoint.k) -> [*]",
"END");
}
public void testAnd() throws Exception {
assertCfg("void", "b3 = b1 && b2;").is(
"BLOCK -> [*]",
"STMT -> [*]",
"READ(b1) -> [*]",
"COND (EntryPoint.b1) -> [THEN=*, ELSE=1]",
"READ(b2) -> [*]",
"1: WRITE(b3, EntryPoint.b1 && EntryPoint.b2) -> [*]",
"END");
}
public void testOr() throws Exception {
assertCfg("void", "b3 = b1 || b2;").is(
"BLOCK -> [*]",
"STMT -> [*]",
"READ(b1) -> [*]",
"COND (EntryPoint.b1) -> [ELSE=*, THEN=1]",
"READ(b2) -> [*]",
"1: WRITE(b3, EntryPoint.b1 || EntryPoint.b2) -> [*]",
"END");
}
public void testMultipleExpressionStatements() throws Exception {
assertCfg("void",
"i = 1;",
"j = 2;",
"m = k = j;").is(
"BLOCK -> [*]",
"STMT -> [*]",
"WRITE(i, 1) -> [*]",
"STMT -> [*]",
"WRITE(j, 2) -> [*]",
"STMT -> [*]",
"READ(j) -> [*]",
"WRITE(k, EntryPoint.j) -> [*]",
"WRITE(m, EntryPoint.k = EntryPoint.j) -> [*]",
"END");
}
public void testIfStatement1() throws Exception {
assertCfg("void",
"if (i == 1) {",
" j = 2;",
"}",
"k = j;").is(
"BLOCK -> [*]",
"STMT -> [*]",
"READ(i) -> [*]",
"COND (EntryPoint.i == 1) -> [THEN=*, ELSE=1]",
"BLOCK -> [*]",
"STMT -> [*]",
"WRITE(j, 2) -> [*]",
"1: STMT -> [*]",
"READ(j) -> [*]",
"WRITE(k, EntryPoint.j) -> [*]",
"END");
}
public void testIfStatement2() throws Exception {
assertCfg("void",
"if ((i = 1) == 2) {",
" j = 2;",
"} else {",
" k = j;",
"}").is(
"BLOCK -> [*]",
"STMT -> [*]",
"WRITE(i, 1) -> [*]",
"COND ((EntryPoint.i = 1) == 2) -> [THEN=*, ELSE=1]",
"BLOCK -> [*]",
"STMT -> [*]",
"WRITE(j, 2) -> [2]",
"1: BLOCK -> [*]",
"STMT -> [*]",
"READ(j) -> [*]",
"WRITE(k, EntryPoint.j) -> [*]",
"2: END");
}
public void testIfStatement3() throws Exception {
assertCfg("void", "if (b1 || b2) {j = 2;}").is(
"BLOCK -> [*]",
"STMT -> [*]",
"READ(b1) -> [*]",
"COND (EntryPoint.b1) -> [ELSE=*, THEN=1]",
"READ(b2) -> [*]",
"1: COND (EntryPoint.b1 || EntryPoint.b2) -> [THEN=*, ELSE=2]",
"BLOCK -> [*]",
"STMT -> [*]",
"WRITE(j, 2) -> [*]",
"2: END");
}
public void testWhileStatement() throws Exception {
assertCfg("void", "while (i == 1) {j = 2;} k = j;").is(
"BLOCK -> [*]",
"STMT -> [*]",
"1: READ(i) -> [*]",
"COND (EntryPoint.i == 1) -> [THEN=*, ELSE=2]",
"BLOCK -> [*]",
"STMT -> [*]",
"WRITE(j, 2) -> [1]",
"2: STMT -> [*]",
"READ(j) -> [*]",
"WRITE(k, EntryPoint.j) -> [*]",
"END");
}
public void testDoStatement() throws Exception {
assertCfg("void", "do { j = 2; } while (i == 1);").is(
"BLOCK -> [*]",
"STMT -> [*]",
"1: BLOCK -> [*]",
"STMT -> [*]",
"WRITE(j, 2) -> [*]",
"READ(i) -> [*]",
"COND (EntryPoint.i == 1) -> [THEN=1, ELSE=*]",
"END");
}
public void testDoStatementBreakNoLabel() throws Exception {
assertCfg("void", "do { if (b1) { break; } else { do { j = 2; } while (b2); } } while (i == 1);").is(
"BLOCK -> [*]",
"STMT -> [*]",
"1: BLOCK -> [*]",
"STMT -> [*]",
"READ(b1) -> [*]",
"COND (EntryPoint.b1) -> [THEN=*, ELSE=2]",
"BLOCK -> [*]",
"STMT -> [*]",
"GOTO -> [4]",
"2: BLOCK -> [*]",
"STMT -> [*]",
"3: BLOCK -> [*]",
"STMT -> [*]",
"WRITE(j, 2) -> [*]",
"READ(b2) -> [*]",
"COND (EntryPoint.b2) -> [THEN=3, ELSE=*]",
"READ(i) -> [*]",
"COND (EntryPoint.i == 1) -> [THEN=1, ELSE=*]",
"4: END");
}
public void testDoStatementContinueNoLabel() throws Exception {
assertCfg("void", "do { if (b1) { continue; } else { do { j = 2; } while (b2); } } while (i == 1);").is(
"BLOCK -> [*]",
"STMT -> [*]",
"1: BLOCK -> [*]",
"STMT -> [*]",
"READ(b1) -> [*]",
"COND (EntryPoint.b1) -> [THEN=*, ELSE=2]",
"BLOCK -> [*]",
"STMT -> [*]",
"GOTO -> [1]",
"2: BLOCK -> [*]",
"STMT -> [*]",
"3: BLOCK -> [*]",
"STMT -> [*]",
"WRITE(j, 2) -> [*]",
"READ(b2) -> [*]",
"COND (EntryPoint.b2) -> [THEN=3, ELSE=*]",
"READ(i) -> [*]",
"COND (EntryPoint.i == 1) -> [THEN=1, ELSE=*]",
"END");
}
public void testReturn1() throws Exception {
assertCfg("void", "return;").is(
"BLOCK -> [*]",
"STMT -> [*]",
"GOTO -> [*]",
"END");
}
public void testReturn2() throws Exception {
assertCfg("boolean", "i = 1; return i == 2;").is(
"BLOCK -> [*]",
"STMT -> [*]",
"WRITE(i, 1) -> [*]",
"STMT -> [*]",
"READ(i) -> [*]",
"GOTO -> [*]",
"END");
}
public void testReturn3() throws Exception {
assertCfg("boolean",
"i = 1;",
"if (i == 1) {",
" i = 2;",
" return true;",
"} else {",
" i = 3;",
" return false;",
"}").is(
"BLOCK -> [*]",
"STMT -> [*]",
"WRITE(i, 1) -> [*]",
"STMT -> [*]",
"READ(i) -> [*]",
"COND (EntryPoint.i == 1) -> [THEN=*, ELSE=1]",
"BLOCK -> [*]",
"STMT -> [*]",
"WRITE(i, 2) -> [*]",
"STMT -> [*]",
"GOTO -> [2]",
"1: BLOCK -> [*]",
"STMT -> [*]",
"WRITE(i, 3) -> [*]",
"STMT -> [*]",
"GOTO -> [*]",
"2: END");
}
public void testForStatement() throws Exception {
assertCfg("int",
"int j = 0;",
"for (int i = 0; i < 10; ++i) { j++; }",
"return j;").is(
"BLOCK -> [*]",
"STMT -> [*]",
"WRITE(j, 0) -> [*]",
"STMT -> [*]",
"STMT -> [*]",
"WRITE(i, 0) -> [*]",
"1: READ(i) -> [*]",
"COND (i < 10) -> [THEN=*, ELSE=2]",
"BLOCK -> [*]",
"STMT -> [*]",
"READWRITE(j, null) -> [*]",
"STMT -> [*]",
"READWRITE(i, null) -> [1]",
"2: STMT -> [*]",
"READ(j) -> [*]",
"GOTO -> [*]",
"END");
}
public void testEmptyForStatement() throws Exception {
assertCfg("void",
"for (;;) { j++; }").is(
"BLOCK -> [*]",
"STMT -> [*]",
"1: BLOCK -> [*]",
"STMT -> [*]",
"READWRITE(j, null) -> [1]",
"END");
}
public void testThrowWithoutCatch1() throws Exception {
assertCfg("void", "throw runtimeException;").is(
"BLOCK -> [*]",
"STMT -> [*]",
"READ(runtimeException) -> [*]",
"THROW -> [*]",
"END");
}
public void testThrowWithoutCatch2() throws Exception {
assertCfg("void", "if (b1) { throw runtimeException; } b1 = true;").is(
"BLOCK -> [*]",
"STMT -> [*]",
"READ(b1) -> [*]",
"COND (EntryPoint.b1) -> [THEN=*, ELSE=1]",
"BLOCK -> [*]",
"STMT -> [*]",
"READ(runtimeException) -> [*]",
"THROW -> [2]",
"1: STMT -> [*]",
"WRITE(b1, true) -> [*]",
"2: END"
);
}
public void testWhileContinueNoLabel() throws Exception {
assertCfg("void", "while (b1) { if (b2) { continue; } i++; }").is(
"BLOCK -> [*]",
"STMT -> [*]",
"1: READ(b1) -> [*]",
"COND (EntryPoint.b1) -> [THEN=*, ELSE=3]",
"BLOCK -> [*]",
"STMT -> [*]",
"READ(b2) -> [*]",
"COND (EntryPoint.b2) -> [THEN=*, ELSE=2]",
"BLOCK -> [*]",
"STMT -> [*]",
"GOTO -> [1]",
"2: STMT -> [*]",
"READWRITE(i, null) -> [1]",
"3: END");
}
public void testWhileContinueWithLabel1() throws Exception {
assertCfg("void",
"nextLoop: while(b3)",
" while (b1) {",
" if (b2) { continue; }",
" i++;" +
" }").is(
"BLOCK -> [*]",
"STMT -> [*]",
"1: READ(b3) -> [*]",
"COND (EntryPoint.b3) -> [THEN=*, ELSE=4]",
"STMT -> [*]",
"2: READ(b1) -> [*]",
"COND (EntryPoint.b1) -> [THEN=*, ELSE=1]",
"BLOCK -> [*]",
"STMT -> [*]",
"READ(b2) -> [*]",
"COND (EntryPoint.b2) -> [THEN=*, ELSE=3]",
"BLOCK -> [*]",
"STMT -> [*]",
"GOTO -> [2]",
"3: STMT -> [*]",
"READWRITE(i, null) -> [2]",
"4: END");
}
public void testWhileContinueWithLabel2() throws Exception {
assertCfg("void",
"nextLoop: while(b3)",
" while (b1) {",
" if (b2) { continue nextLoop; }",
" i++;",
" }").is(
"BLOCK -> [*]",
"STMT -> [*]",
"1: READ(b3) -> [*]",
"COND (EntryPoint.b3) -> [THEN=*, ELSE=4]",
"STMT -> [*]",
"2: READ(b1) -> [*]",
"COND (EntryPoint.b1) -> [THEN=*, ELSE=1]",
"BLOCK -> [*]",
"STMT -> [*]",
"READ(b2) -> [*]",
"COND (EntryPoint.b2) -> [THEN=*, ELSE=3]",
"BLOCK -> [*]",
"STMT -> [*]",
"GOTO -> [1]",
"3: STMT -> [*]",
"READWRITE(i, null) -> [2]",
"4: END");
}
public void testWhileContinueWithLabel3() throws Exception {
assertCfg("void",
"nextLoop: while (b1) {",
" if (b2) { continue; }",
" i++;",
"}").is(
"BLOCK -> [*]",
"STMT -> [*]",
"1: READ(b1) -> [*]",
"COND (EntryPoint.b1) -> [THEN=*, ELSE=3]",
"BLOCK -> [*]",
"STMT -> [*]",
"READ(b2) -> [*]",
"COND (EntryPoint.b2) -> [THEN=*, ELSE=2]",
"BLOCK -> [*]",
"STMT -> [*]",
"GOTO -> [1]",
"2: STMT -> [*]",
"READWRITE(i, null) -> [1]",
"3: END");
}
public void testWhileBreakNoLabel() throws Exception {
assertCfg("void", "while (b1) { if (b2) { break; } i++; }").is(
"BLOCK -> [*]",
"STMT -> [*]",
"1: READ(b1) -> [*]",
"COND (EntryPoint.b1) -> [THEN=*, ELSE=3]",
"BLOCK -> [*]",
"STMT -> [*]",
"READ(b2) -> [*]",
"COND (EntryPoint.b2) -> [THEN=*, ELSE=2]",
"BLOCK -> [*]",
"STMT -> [*]",
"GOTO -> [3]",
"2: STMT -> [*]",
"READWRITE(i, null) -> [1]",
"3: END");
}
public void testWhileBreakNoLabel2() throws Exception {
assertCfg("void", "while (b1) { if (b2) { break; } else { while (i < 10) { i++; } } }").is(
"BLOCK -> [*]",
"STMT -> [*]",
"1: READ(b1) -> [*]",
"COND (EntryPoint.b1) -> [THEN=*, ELSE=4]",
"BLOCK -> [*]",
"STMT -> [*]",
"READ(b2) -> [*]",
"COND (EntryPoint.b2) -> [THEN=*, ELSE=2]",
"BLOCK -> [*]",
"STMT -> [*]",
"GOTO -> [4]",
"2: BLOCK -> [*]",
"STMT -> [*]",
"3: READ(i) -> [*]",
"COND (EntryPoint.i < 10) -> [THEN=*, ELSE=1]",
"BLOCK -> [*]",
"STMT -> [*]",
"READWRITE(i, null) -> [3]",
"4: END");
}
public void testWhileBreakWithLabel1() throws Exception {
assertCfg("void",
"nextLoop: while(b3)",
" while (b1) {",
" if (b2) { break; }",
" i++;",
" }").is(
"BLOCK -> [*]",
"STMT -> [*]",
"1: READ(b3) -> [*]",
"COND (EntryPoint.b3) -> [THEN=*, ELSE=4]",
"STMT -> [*]",
"2: READ(b1) -> [*]",
"COND (EntryPoint.b1) -> [THEN=*, ELSE=1]",
"BLOCK -> [*]",
"STMT -> [*]",
"READ(b2) -> [*]",
"COND (EntryPoint.b2) -> [THEN=*, ELSE=3]",
"BLOCK -> [*]",
"STMT -> [*]",
"GOTO -> [1]",
"3: STMT -> [*]",
"READWRITE(i, null) -> [2]",
"4: END");
}
public void testWhileBreakWithLabel2() throws Exception {
assertCfg("void",
"nextLoop: while(b3)",
" while (b1) {",
" if (b2) { break nextLoop; }",
" i++;",
" }").is(
"BLOCK -> [*]",
"STMT -> [*]",
"1: READ(b3) -> [*]",
"COND (EntryPoint.b3) -> [THEN=*, ELSE=4]",
"STMT -> [*]",
"2: READ(b1) -> [*]",
"COND (EntryPoint.b1) -> [THEN=*, ELSE=1]",
"BLOCK -> [*]",
"STMT -> [*]",
"READ(b2) -> [*]",
"COND (EntryPoint.b2) -> [THEN=*, ELSE=3]",
"BLOCK -> [*]",
"STMT -> [*]",
"GOTO -> [4]",
"3: STMT -> [*]",
"READWRITE(i, null) -> [2]",
"4: END");
}
public void testWhileBreakWithLabel3() throws Exception {
assertCfg("void",
"nextLoop: while (b1) { if (b2) { break; } i++; }").is(
"BLOCK -> [*]",
"STMT -> [*]",
"1: READ(b1) -> [*]",
"COND (EntryPoint.b1) -> [THEN=*, ELSE=3]",
"BLOCK -> [*]",
"STMT -> [*]",
"READ(b2) -> [*]",
"COND (EntryPoint.b2) -> [THEN=*, ELSE=2]",
"BLOCK -> [*]",
"STMT -> [*]",
"GOTO -> [3]",
"2: STMT -> [*]",
"READWRITE(i, null) -> [1]",
"3: END");
}
public void testForBreakNoLabel() throws Exception {
assertCfg("void",
"for(int i = 0; i < 10; i++) { if (b2) { break; } i++; }").is(
"BLOCK -> [*]",
"STMT -> [*]",
"STMT -> [*]",
"WRITE(i, 0) -> [*]",
"1: READ(i) -> [*]",
"COND (i < 10) -> [THEN=*, ELSE=3]",
"BLOCK -> [*]",
"STMT -> [*]",
"READ(b2) -> [*]",
"COND (EntryPoint.b2) -> [THEN=*, ELSE=2]",
"BLOCK -> [*]",
"STMT -> [*]",
"GOTO -> [3]",
"2: STMT -> [*]",
"READWRITE(i, null) -> [*]",
"STMT -> [*]",
"READWRITE(i, null) -> [1]",
"3: END");
}
public void testForContinueNoLabel() throws Exception {
assertCfg("void",
"for(int i = 0; i < 10; i++) { if (b2) { continue; } i++; }").is(
"BLOCK -> [*]",
"STMT -> [*]",
"STMT -> [*]",
"WRITE(i, 0) -> [*]",
"1: READ(i) -> [*]",
"COND (i < 10) -> [THEN=*, ELSE=4]",
"BLOCK -> [*]",
"STMT -> [*]",
"READ(b2) -> [*]",
"COND (EntryPoint.b2) -> [THEN=*, ELSE=2]",
"BLOCK -> [*]",
"STMT -> [*]",
"GOTO -> [3]",
"2: STMT -> [*]",
"READWRITE(i, null) -> [*]",
"3: STMT -> [*]",
"READWRITE(i, null) -> [1]",
"4: END");
}
public void testForBreakNestedForWithLabel() throws Exception {
assertCfg("int",
"int j = 0; a: for(; ; ) { if (b2) { break a; } else { for (int i = 0; i < 1; i++) { j = i; } } } return j;").is(
"BLOCK -> [*]",
"STMT -> [*]",
"WRITE(j, 0) -> [*]",
"STMT -> [*]",
"1: BLOCK -> [*]",
"STMT -> [*]",
"READ(b2) -> [*]",
"COND (EntryPoint.b2) -> [THEN=*, ELSE=2]",
"BLOCK -> [*]",
"STMT -> [*]",
"GOTO -> [4]",
"2: BLOCK -> [*]",
"STMT -> [*]",
"STMT -> [*]",
"WRITE(i, 0) -> [*]",
"3: READ(i) -> [*]",
"COND (i < 1) -> [THEN=*, ELSE=1]",
"BLOCK -> [*]",
"STMT -> [*]",
"READ(i) -> [*]",
"WRITE(j, i) -> [*]",
"STMT -> [*]",
"READWRITE(i, null) -> [3]",
"4: STMT -> [*]",
"READ(j) -> [*]",
"GOTO -> [*]",
"END");
}
public void testForBreakNestedForNoLabel() throws Exception {
assertCfg("int",
"int j = 0; for(; ; ) { if (b2) { break; } else { for (int i = 0; i < 1; i++) { j = i; } } } return j;").is(
"BLOCK -> [*]",
"STMT -> [*]",
"WRITE(j, 0) -> [*]",
"STMT -> [*]",
"1: BLOCK -> [*]",
"STMT -> [*]",
"READ(b2) -> [*]",
"COND (EntryPoint.b2) -> [THEN=*, ELSE=2]",
"BLOCK -> [*]",
"STMT -> [*]",
"GOTO -> [4]",
"2: BLOCK -> [*]",
"STMT -> [*]",
"STMT -> [*]",
"WRITE(i, 0) -> [*]",
"3: READ(i) -> [*]",
"COND (i < 1) -> [THEN=*, ELSE=1]",
"BLOCK -> [*]",
"STMT -> [*]",
"READ(i) -> [*]",
"WRITE(j, i) -> [*]",
"STMT -> [*]",
"READWRITE(i, null) -> [3]",
"4: STMT -> [*]",
"READ(j) -> [*]",
"GOTO -> [*]",
"END");
}
public void testCatchThrowException1() throws Exception {
assertCfg("void",
"try {",
" if (b) throw checkedException;",
" k++;",
"} catch (CheckedException e) {",
" i++;",
"}",
"j++;").is(
"BLOCK -> [*]",
"TRY -> [*]",
"BLOCK -> [*]",
"STMT -> [*]",
"READ(b) -> [*]",
"COND (EntryPoint.b) -> [THEN=*, ELSE=1]",
"STMT -> [*]",
"READ(checkedException) -> [*]",
"THROW -> [2]",
"1: STMT -> [*]",
"READWRITE(k, null) -> [3]",
"2: BLOCK -> [*]",
"STMT -> [*]",
"READWRITE(i, null) -> [*]",
"3: STMT -> [*]",
"READWRITE(j, null) -> [*]",
"END"
);
}
public void testCatchThrowUncatchedException() throws Exception {
assertCfg("void",
"try {",
" if (b) throw uncheckedException2;",
" k++;",
"} catch (UncheckedException1 e) {",
" i++;",
"}",
"j++;").is(
"BLOCK -> [*]",
"TRY -> [*]",
"BLOCK -> [*]",
"STMT -> [*]",
"READ(b) -> [*]",
"COND (EntryPoint.b) -> [THEN=*, ELSE=1]",
"STMT -> [*]",
"READ(uncheckedException2) -> [*]",
"THROW -> [3]",
"1: STMT -> [*]",
"READWRITE(k, null) -> [2]",
"BLOCK -> [*]",
"STMT -> [*]",
"READWRITE(i, null) -> [*]",
"2: STMT -> [*]",
"READWRITE(j, null) -> [*]",
"3: END"
);
}
public void testCatchThrowSupertype() throws Exception {
assertCfg("void",
"try {",
" if (b) throw runtimeException;",
" k++;",
"} catch (UncheckedException1 e) {",
" i++;",
"}",
"j++;").is(
"BLOCK -> [*]",
"TRY -> [*]",
"BLOCK -> [*]",
"STMT -> [*]",
"READ(b) -> [*]",
"COND (EntryPoint.b) -> [THEN=*, ELSE=1]",
"STMT -> [*]",
"READ(runtimeException) -> [*]",
"THROW -> [2, 4]",
"1: STMT -> [*]",
"READWRITE(k, null) -> [3]",
"2: BLOCK -> [*]",
"STMT -> [*]",
"READWRITE(i, null) -> [*]",
"3: STMT -> [*]",
"READWRITE(j, null) -> [*]",
"4: END"
);
}
public void testCatchSupertype() throws Exception {
assertCfg("void",
"try {",
" if (b) throw uncheckedException1;",
" k++;",
"} catch (RuntimeException e) {",
" i++;",
"}",
"j++;").is(
"BLOCK -> [*]",
"TRY -> [*]",
"BLOCK -> [*]",
"STMT -> [*]",
"READ(b) -> [*]",
"COND (EntryPoint.b) -> [THEN=*, ELSE=1]",
"STMT -> [*]",
"READ(uncheckedException1) -> [*]",
"THROW -> [2]",
"1: STMT -> [*]",
"READWRITE(k, null) -> [3]",
"2: BLOCK -> [*]",
"STMT -> [*]",
"READWRITE(i, null) -> [*]",
"3: STMT -> [*]",
"READWRITE(j, null) -> [*]",
"END"
);
}
public void testCatchReturn() throws Exception {
assertCfg("void",
"try { try {",
" if (b) return;",
" k++;",
"} catch (RuntimeException e) {",
" i++;",
"} } catch (UncheckedException1 e) { j++; }").is(
"BLOCK -> [*]",
"TRY -> [*]",
"BLOCK -> [*]",
"TRY -> [*]",
"BLOCK -> [*]",
"STMT -> [*]",
"READ(b) -> [*]",
"COND (EntryPoint.b) -> [THEN=*, ELSE=1]",
"STMT -> [*]",
"GOTO -> [2]",
"1: STMT -> [*]",
"READWRITE(k, null) -> [2]",
"BLOCK -> [*]",
"STMT -> [*]",
"READWRITE(i, null) -> [2]",
"BLOCK -> [*]",
"STMT -> [*]",
"READWRITE(j, null) -> [*]",
"2: END"
);
}
public void testRethrow() throws Exception {
assertCfg("void",
"try {",
" if (b) throw uncheckedException1;",
" k++;",
"} catch (UncheckedException1 e) {",
" i++;",
" throw e;",
"}",
"j++;").is(
"BLOCK -> [*]",
"TRY -> [*]",
"BLOCK -> [*]",
"STMT -> [*]",
"READ(b) -> [*]",
"COND (EntryPoint.b) -> [THEN=*, ELSE=1]",
"STMT -> [*]",
"READ(uncheckedException1) -> [*]",
"THROW -> [2]",
"1: STMT -> [*]",
"READWRITE(k, null) -> [3]",
"2: BLOCK -> [*]",
"STMT -> [*]",
"READWRITE(i, null) -> [*]",
"STMT -> [*]",
"READ(e) -> [*]",
"THROW -> [4]",
"3: STMT -> [*]",
"READWRITE(j, null) -> [*]",
"4: END"
);
}
public void testCatchMethodCall1() throws Exception {
assertCfg("void",
"try {",
" if (b) throwCheckedException();",
" k++;",
"} catch (CheckedException e) {",
" i++;",
"}",
"j++;").is(
"BLOCK -> [*]",
"TRY -> [*]",
"BLOCK -> [*]",
"STMT -> [*]",
"READ(b) -> [*]",
"COND (EntryPoint.b) -> [THEN=*, ELSE=1]",
"STMT -> [*]",
"OPTTHROW(throwCheckedException()) -> [NOTHROW=*, 2, RE=4, E=4]",
"CALL(throwCheckedException) -> [*]",
"1: STMT -> [*]",
"READWRITE(k, null) -> [3]",
"2: BLOCK -> [*]",
"STMT -> [*]",
"READWRITE(i, null) -> [*]",
"3: STMT -> [*]",
"READWRITE(j, null) -> [*]",
"4: END"
);
}
public void testCatchMethodCall2() throws Exception {
assertCfg("void",
"try {",
" if (b) throwCheckedException();",
" k++;",
"} catch (CheckedException e) {",
" i++;",
"} catch (RuntimeException e) {",
" l++;",
"}",
"j++;").is(
"BLOCK -> [*]",
"TRY -> [*]",
"BLOCK -> [*]",
"STMT -> [*]",
"READ(b) -> [*]",
"COND (EntryPoint.b) -> [THEN=*, ELSE=1]",
"STMT -> [*]",
"OPTTHROW(throwCheckedException()) -> [NOTHROW=*, 2, RE=3, E=5]",
"CALL(throwCheckedException) -> [*]",
"1: STMT -> [*]",
"READWRITE(k, null) -> [4]",
"2: BLOCK -> [*]",
"STMT -> [*]",
"READWRITE(i, null) -> [4]",
"3: BLOCK -> [*]",
"STMT -> [*]",
"READWRITE(l, null) -> [*]",
"4: STMT -> [*]",
"READWRITE(j, null) -> [*]",
"5: END"
);
}
public void testCatchMethodCallUnchecked() throws Exception {
assertCfg("void",
"try {",
" if (b) throwUncheckedException();",
" k++;",
"} catch (UncheckedException1 e) {",
" i++;",
"} catch (RuntimeException e) {",
" l++;",
"}",
"j++;").is(
"BLOCK -> [*]",
"TRY -> [*]",
"BLOCK -> [*]",
"STMT -> [*]",
"READ(b) -> [*]",
"COND (EntryPoint.b) -> [THEN=*, ELSE=1]",
"STMT -> [*]",
"OPTTHROW(throwUncheckedException()) -> [NOTHROW=*, RE=2, RE=3, E=5]",
"CALL(throwUncheckedException) -> [*]",
"1: STMT -> [*]",
"READWRITE(k, null) -> [4]",
"2: BLOCK -> [*]",
"STMT -> [*]",
"READWRITE(i, null) -> [4]",
"3: BLOCK -> [*]",
"STMT -> [*]",
"READWRITE(l, null) -> [*]",
"4: STMT -> [*]",
"READWRITE(j, null) -> [*]",
"5: END"
);
}
public void testFinallyReturn1() throws Exception {
assertCfg("void",
"try {",
" if (b) return;",
" j++;",
"} finally {",
" i++;",
"}").is(
"BLOCK -> [*]",
"TRY -> [*]",
"BLOCK -> [*]",
"STMT -> [*]",
"READ(b) -> [*]",
"COND (EntryPoint.b) -> [THEN=*, ELSE=1]",
"STMT -> [*]",
"GOTO -> [2]",
"1: STMT -> [*]",
"READWRITE(j, null) -> [*]",
"2: BLOCK -> [*]",
"STMT -> [*]",
"READWRITE(i, null) -> [*, *]",
"END"
);
}
public void testThrowFromFinally() throws Exception {
assertCfg("void",
"try {",
" return;",
"} finally {",
" throw runtimeException;",
"}").is(
"BLOCK -> [*]",
"TRY -> [*]",
"BLOCK -> [*]",
"STMT -> [*]",
"GOTO -> [*]",
"BLOCK -> [*]",
"STMT -> [*]",
"READ(runtimeException) -> [*]",
"THROW -> [*]",
"END"
);
}
public void testFinallyReturn2() throws Exception {
assertCfg("void",
"try {",
" return;",
"} finally {",
" i++;",
"}").is(
"BLOCK -> [*]",
"TRY -> [*]",
"BLOCK -> [*]",
"STMT -> [*]",
"GOTO -> [*]",
"BLOCK -> [*]",
"STMT -> [*]",
"READWRITE(i, null) -> [*, *]",
"END"
);
}
public void testFinallyReturn3() throws Exception {
assertCfg("void",
"try {",
"try {",
" if (b) return;",
" k++;",
"} finally {",
" i++;",
"} m++; } finally {",
" j++;",
"}").is(
"BLOCK -> [*]",
"TRY -> [*]",
"BLOCK -> [*]",
"TRY -> [*]",
"BLOCK -> [*]",
"STMT -> [*]",
"READ(b) -> [*]",
"COND (EntryPoint.b) -> [THEN=*, ELSE=1]",
"STMT -> [*]",
"GOTO -> [2]",
"1: STMT -> [*]",
"READWRITE(k, null) -> [*]",
"2: BLOCK -> [*]",
"STMT -> [*]",
"READWRITE(i, null) -> [*, 3]",
"STMT -> [*]",
"READWRITE(m, null) -> [*]",
"3: BLOCK -> [*]",
"STMT -> [*]",
"READWRITE(j, null) -> [*, *]",
"END"
);
}
public void testFinallyContinue() throws Exception {
assertCfg("void",
"while (b) {",
"try {",
" if (b) continue;",
"} finally {",
" i++;",
"} j++; }").is(
"BLOCK -> [*]",
"STMT -> [*]",
"1: READ(b) -> [*]",
"COND (EntryPoint.b) -> [THEN=*, ELSE=3]",
"BLOCK -> [*]",
"TRY -> [*]",
"BLOCK -> [*]",
"STMT -> [*]",
"READ(b) -> [*]",
"COND (EntryPoint.b) -> [THEN=*, ELSE=2]",
"STMT -> [*]",
"GOTO -> [*]",
"2: BLOCK -> [*]",
"STMT -> [*]",
"READWRITE(i, null) -> [*, 1]",
"STMT -> [*]",
"READWRITE(j, null) -> [1]",
"3: END"
);
}
public void testCatchFinally() throws Exception {
assertCfg("void",
"try {",
" if (b) throw checkedException;",
" k++;",
"} catch (CheckedException e) {",
" i++;",
"} finally {",
" j++;",
"}"
).is(
"BLOCK -> [*]",
"TRY -> [*]",
"BLOCK -> [*]",
"STMT -> [*]",
"READ(b) -> [*]",
"COND (EntryPoint.b) -> [THEN=*, ELSE=1]",
"STMT -> [*]",
"READ(checkedException) -> [*]",
"THROW -> [2]",
"1: STMT -> [*]",
"READWRITE(k, null) -> [3]",
"2: BLOCK -> [*]",
"STMT -> [*]",
"READWRITE(i, null) -> [*]",
"3: BLOCK -> [*]",
"STMT -> [*]",
"READWRITE(j, null) -> [*]",
"END"
);
}
public void testFieldWrite() throws Exception {
assertCfg("void",
"Foo foo = createFoo();",
"foo.i = 1;"
).is(
"BLOCK -> [*]",
"STMT -> [*]",
"OPTTHROW(createFoo()) -> [NOTHROW=*, RE=1, E=1]",
"CALL(createFoo) -> [*]",
"WRITE(foo, EntryPoint.createFoo()) -> [*]",
"STMT -> [*]",
"READ(foo) -> [*]",
"WRITE(i, 1) -> [*]",
"1: END"
);
}
public void testFieldUnary() throws Exception {
assertCfg("void",
"Foo foo = createFoo();",
"++foo.i;"
).is(
"BLOCK -> [*]",
"STMT -> [*]",
"OPTTHROW(createFoo()) -> [NOTHROW=*, RE=1, E=1]",
"CALL(createFoo) -> [*]",
"WRITE(foo, EntryPoint.createFoo()) -> [*]",
"STMT -> [*]",
"READ(foo) -> [*]",
"READWRITE(i, null) -> [*]",
"1: END"
);
}
public void testArrayRead() throws Exception {
assertCfg("void",
"i = ii[j];"
).is(
"BLOCK -> [*]",
"STMT -> [*]",
"READ(ii) -> [*]",
"READ(j) -> [*]",
"WRITE(i, EntryPoint.ii[EntryPoint.j]) -> [*]",
"END"
);
}
public void testArrayWrite() throws Exception {
assertCfg("void",
"ii[i] = j;"
).is(
"BLOCK -> [*]",
"STMT -> [*]",
"READ(j) -> [*]",
"READ(ii) -> [*]",
"READ(i) -> [*]",
"WRITE(EntryPoint.ii[EntryPoint.i], EntryPoint.j) -> [*]",
"END"
);
}
public void testArrayUnary() throws Exception {
assertCfg("void",
"++ii[i];"
).is(
"BLOCK -> [*]",
"STMT -> [*]",
"READ(ii) -> [*]",
"READ(i) -> [*]",
"READWRITE(EntryPoint.ii[EntryPoint.i], null) -> [*]",
"END"
);
}
public void testSwitch() throws Exception {
assertCfg("void",
"switch(i) {",
" case 1: ",
" return;",
" case 2: ",
" case 3: ",
" j = 1;",
" break;",
" case 4: ",
" j = 2;",
" default:",
" j = 4;",
" case 5: ",
" j = 3;",
" break;",
"}"
).is(
"BLOCK -> [*]",
"STMT -> [*]",
"READ(i) -> [*]",
"GOTO -> [*]",
"STMT -> [*]",
"COND (EntryPoint.i == 1) -> [THEN=*, ELSE=1]",
"STMT -> [*]",
"GOTO -> [7]",
"1: STMT -> [*]",
"COND (EntryPoint.i == 2) -> [ELSE=*, THEN=2]",
"STMT -> [*]",
"COND (EntryPoint.i == 3) -> [THEN=*, ELSE=3]",
"2: STMT -> [*]",
"WRITE(j, 1) -> [*]",
"STMT -> [*]",
"GOTO -> [7]",
"3: STMT -> [*]",
"COND (EntryPoint.i == 4) -> [THEN=*, ELSE=5]",
"STMT -> [*]",
"WRITE(j, 2) -> [*]",
"4: STMT -> [*]",
"STMT -> [*]",
"WRITE(j, 4) -> [6]",
"5: STMT -> [*]",
"COND (EntryPoint.i == 5) -> [THEN=*, ELSE=4]",
"6: STMT -> [*]",
"WRITE(j, 3) -> [*]",
"STMT -> [*]",
"GOTO -> [*]",
"7: END"
);
}
public void testSwitch_FallThrough() throws Exception {
assertCfg("void",
"switch(i) {",
" case 1: ",
" j = 1;",
" case 2: ",
" j = 2;",
" case 3: ",
" j = 3;",
"}"
).is(
"BLOCK -> [*]",
"STMT -> [*]",
"READ(i) -> [*]",
"GOTO -> [*]",
"STMT -> [*]",
"COND (EntryPoint.i == 1) -> [THEN=*, ELSE=1]",
"STMT -> [*]",
"WRITE(j, 1) -> [2]",
"1: STMT -> [*]",
"COND (EntryPoint.i == 2) -> [THEN=*, ELSE=3]",
"2: STMT -> [*]",
"WRITE(j, 2) -> [4]",
"3: STMT -> [*]",
"COND (EntryPoint.i == 3) -> [THEN=*, ELSE=5]",
"4: STMT -> [*]",
"WRITE(j, 3) -> [*]",
"5: END");
}
public void testSwitch_FirstDefault() throws Exception {
assertCfg("void",
"switch(i) {",
" default: j = 1; return;",
" case 1: j = 2; return;",
"}"
).is(
"BLOCK -> [*]",
"STMT -> [*]",
"READ(i) -> [*]",
"GOTO -> [2]",
"1: STMT -> [*]",
"STMT -> [*]",
"WRITE(j, 1) -> [*]",
"STMT -> [*]",
"GOTO -> [3]",
"2: STMT -> [*]",
"COND (EntryPoint.i == 1) -> [THEN=*, ELSE=1]",
"STMT -> [*]",
"WRITE(j, 2) -> [*]",
"STMT -> [*]",
"GOTO -> [*]",
"3: END"
);
}
public void testSwitch_Empty() throws Exception {
assertCfg("void",
"switch(i) {",
"}"
).is(
"BLOCK -> [*]",
"STMT -> [*]",
"READ(i) -> [*]",
"GOTO -> [*]",
"END"
);
}
public void testSwitch_OnlyDefault() throws Exception {
assertCfg("void",
"switch(i) {",
" default: j = 0; return;",
"}"
).is(
"BLOCK -> [*]",
"STMT -> [*]",
"READ(i) -> [*]",
"GOTO -> [*]",
"STMT -> [*]",
"STMT -> [*]",
"WRITE(j, 0) -> [*]",
"STMT -> [*]",
"GOTO -> [*]",
"END"
);
}
public void testNestedSwitch() throws Exception {
assertCfg("void",
"switch(i) {",
" case 1: ",
" switch (j) {",
" case 0: k = 1; break;",
" case 1: k = 2; break;",
" }",
" break;",
" case 2: ",
" switch (j) {",
" case 0: k = 3; break;",
" case 1: k = 4; break;",
" }",
" break;",
" case 3: ",
" switch (j) {",
" case 0: k = 5; break;",
" case 1: k = 6; break;",
" }",
" break;",
"}"
).is(
"BLOCK -> [*]",
"STMT -> [*]",
"READ(i) -> [*]",
"GOTO -> [*]",
"STMT -> [*]",
"COND (EntryPoint.i == 1) -> [THEN=*, ELSE=3]",
"STMT -> [*]",
"READ(j) -> [*]",
"GOTO -> [*]",
"STMT -> [*]",
"COND (EntryPoint.j == 0) -> [THEN=*, ELSE=1]",
"STMT -> [*]",
"WRITE(k, 1) -> [*]",
"STMT -> [*]",
"GOTO -> [2]",
"1: STMT -> [*]",
"COND (EntryPoint.j == 1) -> [THEN=*, ELSE=2]",
"STMT -> [*]",
"WRITE(k, 2) -> [*]",
"STMT -> [*]",
"GOTO -> [*]",
"2: STMT -> [*]",
"GOTO -> [9]",
"3: STMT -> [*]",
"COND (EntryPoint.i == 2) -> [THEN=*, ELSE=6]",
"STMT -> [*]",
"READ(j) -> [*]",
"GOTO -> [*]",
"STMT -> [*]",
"COND (EntryPoint.j == 0) -> [THEN=*, ELSE=4]",
"STMT -> [*]",
"WRITE(k, 3) -> [*]",
"STMT -> [*]",
"GOTO -> [5]",
"4: STMT -> [*]",
"COND (EntryPoint.j == 1) -> [THEN=*, ELSE=5]",
"STMT -> [*]",
"WRITE(k, 4) -> [*]",
"STMT -> [*]",
"GOTO -> [*]",
"5: STMT -> [*]",
"GOTO -> [9]",
"6: STMT -> [*]",
"COND (EntryPoint.i == 3) -> [THEN=*, ELSE=9]",
"STMT -> [*]",
"READ(j) -> [*]",
"GOTO -> [*]",
"STMT -> [*]",
"COND (EntryPoint.j == 0) -> [THEN=*, ELSE=7]",
"STMT -> [*]",
"WRITE(k, 5) -> [*]",
"STMT -> [*]",
"GOTO -> [8]",
"7: STMT -> [*]",
"COND (EntryPoint.j == 1) -> [THEN=*, ELSE=8]",
"STMT -> [*]",
"WRITE(k, 6) -> [*]",
"STMT -> [*]",
"GOTO -> [*]",
"8: STMT -> [*]",
"GOTO -> [*]",
"9: END"
);
}
public void testSwitchWithLoopAndBreak() throws Exception {
assertCfg("void",
"switch(i) {",
" case 1: ",
" i = 1;" +
" break;",
" case 2: ",
" while (b) { i = 2; break; }",
" j = 3;",
"}"
).is(
"BLOCK -> [*]",
"STMT -> [*]",
"READ(i) -> [*]",
"GOTO -> [*]",
"STMT -> [*]",
"COND (EntryPoint.i == 1) -> [THEN=*, ELSE=1]",
"STMT -> [*]",
"WRITE(i, 1) -> [*]",
"STMT -> [*]",
"GOTO -> [3]",
"1: STMT -> [*]",
"COND (EntryPoint.i == 2) -> [THEN=*, ELSE=3]",
"STMT -> [*]",
"READ(b) -> [*]",
"COND (EntryPoint.b) -> [THEN=*, ELSE=2]",
"BLOCK -> [*]",
"STMT -> [*]",
"WRITE(i, 2) -> [*]",
"STMT -> [*]",
"GOTO -> [*]",
"2: STMT -> [*]",
"WRITE(j, 3) -> [*]",
"3: END"
);
}
public void testBreakStatement1() throws Exception {
assertCfg("void",
"lbl: {",
" break lbl;",
"}"
).is(
"BLOCK -> [*]",
"BLOCK -> [*]",
"STMT -> [*]",
"GOTO -> [*]",
"END");
}
public void testBreakStatement2() throws Exception {
assertCfg("void",
"lbl: break lbl;"
).is(
"BLOCK -> [*]",
"STMT -> [*]",
"GOTO -> [*]",
"END");
}
public void testBreakStatement3() throws Exception {
assertCfg("void",
"lbl: {",
" i = 1;",
" if (b) break lbl;",
" i = 2;",
"}"
).is(
"BLOCK -> [*]",
"BLOCK -> [*]",
"STMT -> [*]",
"WRITE(i, 1) -> [*]",
"STMT -> [*]",
"READ(b) -> [*]",
"COND (EntryPoint.b) -> [THEN=*, ELSE=1]",
"STMT -> [*]",
"GOTO -> [2]",
"1: STMT -> [*]",
"WRITE(i, 2) -> [*]",
"2: END");
}
public void testBreakStatement4() throws Exception {
assertCfg("void",
"lbl1: {",
" i = 1;",
" lbl2: {",
" j = 1;",
" if (b) break lbl1;",
" j = 2;",
" if (b) break lbl2;",
" }",
" i = 2;",
"}"
).is(
"BLOCK -> [*]",
"BLOCK -> [*]",
"STMT -> [*]",
"WRITE(i, 1) -> [*]",
"BLOCK -> [*]",
"STMT -> [*]",
"WRITE(j, 1) -> [*]",
"STMT -> [*]",
"READ(b) -> [*]",
"COND (EntryPoint.b) -> [THEN=*, ELSE=1]",
"STMT -> [*]",
"GOTO -> [3]",
"1: STMT -> [*]",
"WRITE(j, 2) -> [*]",
"STMT -> [*]",
"READ(b) -> [*]",
"COND (EntryPoint.b) -> [THEN=*, ELSE=2]",
"STMT -> [*]",
"GOTO -> [*]",
"2: STMT -> [*]",
"WRITE(i, 2) -> [*]",
"3: END");
}
public void testBreakLoopAndSwitch() throws Exception {
assertCfg("void",
"loop: while (b) {",
" switch (i) {",
" case 1: ",
" if (j == 1) {",
" break loop;",
" }",
" break;",
" default: ",
" return;",
" case 2: ",
" break loop;",
" case 3: ",
" break;",
" }",
" i++;",
"}",
"k++;"
).is(
"BLOCK -> [*]",
"STMT -> [*]",
"1: READ(b) -> [*]",
"COND (EntryPoint.b) -> [THEN=*, ELSE=7]",
"BLOCK -> [*]",
"STMT -> [*]",
"READ(i) -> [*]",
"GOTO -> [*]",
"STMT -> [*]",
"COND (EntryPoint.i == 1) -> [THEN=*, ELSE=4]",
"STMT -> [*]",
"READ(j) -> [*]",
"COND (EntryPoint.j == 1) -> [THEN=*, ELSE=2]",
"BLOCK -> [*]",
"STMT -> [*]",
"GOTO -> [7]",
"2: STMT -> [*]",
"GOTO -> [6]",
"3: STMT -> [*]",
"STMT -> [*]",
"GOTO -> [8]",
"4: STMT -> [*]",
"COND (EntryPoint.i == 2) -> [THEN=*, ELSE=5]",
"STMT -> [*]",
"GOTO -> [7]",
"5: STMT -> [*]",
"COND (EntryPoint.i == 3) -> [THEN=*, ELSE=3]",
"STMT -> [*]",
"GOTO -> [*]",
"6: STMT -> [*]",
"READWRITE(i, null) -> [1]",
"7: STMT -> [*]",
"READWRITE(k, null) -> [*]",
"8: END");
}
private CfgBuilderResult assertCfg(String returnType, String ...codeSnippet)
throws UnableToCompleteException {
JProgram program = compileSnippet(returnType,
Strings.join(codeSnippet, "\n"));
JMethodBody body = (JMethodBody) findMainMethod(program).getBody();
Cfg cfgGraph = CfgBuilder.build(program, body.getBlock());
return new CfgBuilderResult(cfgGraph);
}
static class CfgBuilderResult {
private final Cfg cfg;
public CfgBuilderResult(Cfg cfgGraph) {
assertNotNull("Can't build cfg", cfgGraph);
this.cfg = cfgGraph;
validateGraph();
}
private void validateGraph() {
for (CfgNode<?> node : cfg.getNodes()) {
List<CfgEdge> incomingEdges = cfg.getInEdges(node);
for (CfgEdge e : incomingEdges) {
CfgNode<?> start = e.getStart();
if (cfg.getGraphInEdges().contains(e)) {
assertNull(start);
continue;
}
assertNotNull("No start in edge " + e.getRole() + " to " + node,
start);
assertTrue(start + " doesn't have outgoing edge to " + node,
cfg.getOutEdges(start).contains(e));
}
List<CfgEdge> outcomingEdges = cfg.getOutEdges(node);
for (CfgEdge e : outcomingEdges) {
CfgNode<?> end = e.getEnd();
assertNotNull("No end in edge " + e.getRole() + " from " + node, end);
assertTrue(end + " doesn't have incoming edge from " + node,
cfg.getInEdges(end).contains(e));
}
}
}
public void is(String... expected) {
assertEquals(Strings.join(expected, "\n"), cfg.print());
}
}
}