Fix handling of unlabeled break and continue in gflow. This fixes issues: http://code.google.com/p/google-web-toolkit/issues/detail?id=6272 http://code.google.com/p/google-web-toolkit/issues/detail?id=6429 (was issue 1453809) Review at http://gwt-code-reviews.appspot.com/1447824 Review by: jbrosenberg@google.com git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@10341 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/gflow/cfg/CfgBuilder.java b/dev/core/src/com/google/gwt/dev/jjs/impl/gflow/cfg/CfgBuilder.java index 6e135c2..aeb0cf7 100644 --- a/dev/core/src/com/google/gwt/dev/jjs/impl/gflow/cfg/CfgBuilder.java +++ b/dev/core/src/com/google/gwt/dev/jjs/impl/gflow/cfg/CfgBuilder.java
@@ -452,6 +452,7 @@ @Override public boolean visit(JDoStatement x, Context ctx) { + List<Exit> unlabeledExits = removeUnlabeledExits(); pushNode(new CfgStatementNode<JStatement>(parent, x)); int pos = nodes.size(); @@ -473,6 +474,7 @@ addNormalExit(node, CfgConditionalNode.ELSE); popNode(); + addExits(unlabeledExits); return false; } @@ -486,6 +488,7 @@ @Override public boolean visit(JForStatement x, Context ctx) { + List<Exit> unlabeledExits = removeUnlabeledExits(); pushNode(new CfgStatementNode<JStatement>(parent, x)); accept(x.getInitializers()); @@ -519,6 +522,7 @@ } popNode(); + addExits(unlabeledExits); return false; } @@ -970,6 +974,7 @@ @Override public boolean visit(JWhileStatement x, Context ctx) { + List<Exit> unlabeledExits = removeUnlabeledExits(); pushNode(new CfgStatementNode<JStatement>(parent, x)); int pos = nodes.size(); accept(x.getTestExpr()); @@ -992,6 +997,7 @@ addNormalExit(node, CfgConditionalNode.ELSE); popNode(); + addExits(unlabeledExits); return false; } @@ -1014,7 +1020,7 @@ } /** - * Transform all brake exits into normal exits, thus making sure that + * Transform all break exits into normal exits, thus making sure that * next node will get edges from them. */ private void addBreakExits(String label) { @@ -1163,6 +1169,21 @@ addExits(labeledBreaks); return breakExits; } + + private List<Exit> removeUnlabeledExits() { + List<Exit> unlabeledExits = new ArrayList<Exit>(); + Exit.Reason reasons[] = { Exit.Reason.BREAK, Exit.Reason.CONTINUE }; + for (Exit.Reason reason : reasons) { + for (Iterator<Exit> i = currentExitsByReason.get(reason).iterator(); i.hasNext();) { + Exit exit = i.next(); + if (exit.getLabel() == null) { + i.remove(); + unlabeledExits.add(exit); + } + } + } + return unlabeledExits; + } } /**
diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/gflow/DataflowOptimizerTest.java b/dev/core/test/com/google/gwt/dev/jjs/impl/gflow/DataflowOptimizerTest.java index ef88dd3..060bc4e 100644 --- a/dev/core/test/com/google/gwt/dev/jjs/impl/gflow/DataflowOptimizerTest.java +++ b/dev/core/test/com/google/gwt/dev/jjs/impl/gflow/DataflowOptimizerTest.java
@@ -262,6 +262,36 @@ ); } + public void testComplexCode4() throws Exception { + addSnippetClassDecl("static boolean confirm() { return true; }"); + + optimize("int", + "int n = 0;", + "for (; ; ) {", + " if (confirm()) {", + " break;", + " } else {", + " for (int i = 0; i < 2; i++) {", + " n = i;", + " }", + " }", + "}", + "return n;" + ).into( + "int n = 0;", + "for (; ; ) {", + " if (confirm()) {", + " break;", + " } else {", + " for (int i = 0; i < 2; i++) {", + " n = i;", + " }", + " }", + "}", + "return n;" + ); + } + public void testImplicitConversion() throws Exception { optimize("long", "int bar = 0x12345678;",
diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/gflow/cfg/CfgBuilderTest.java b/dev/core/test/com/google/gwt/dev/jjs/impl/gflow/cfg/CfgBuilderTest.java index 8e2d5c8..6448f7c 100644 --- a/dev/core/test/com/google/gwt/dev/jjs/impl/gflow/cfg/CfgBuilderTest.java +++ b/dev/core/test/com/google/gwt/dev/jjs/impl/gflow/cfg/CfgBuilderTest.java
@@ -51,7 +51,7 @@ 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 -> [*]", @@ -260,6 +260,52 @@ "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 -> [*]", @@ -479,6 +525,29 @@ "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)", @@ -550,6 +619,29 @@ "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( @@ -572,7 +664,71 @@ "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 {", @@ -1484,7 +1640,7 @@ "READWRITE(k, null) -> [*]", "8: END"); } - + private CfgBuilderResult assertCfg(String returnType, String ...codeSnippet) throws UnableToCompleteException { JProgram program = compileSnippet(returnType,