| 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()); |
| } |
| } |
| } |