CFG: Always jumping to first case statement


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@7934 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 07d9f4f..8be2b1b 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
@@ -606,7 +606,6 @@
     @Override
     public boolean visit(JSwitchStatement x, Context ctx) {
       pushNode(new CfgStatementNode<JStatement>(parent, x));
-      // TODO: Add statement node
       accept(x.getExpr());
       
       JSwitchStatement oldSwitchStatement = switchStatement;
@@ -615,15 +614,26 @@
       List<Exit> oldCaseThenExits = removeExits(Exit.Reason.CASE_THEN);
       List<Exit> oldBreakExits = removeExits(Exit.Reason.BREAK);
       switchStatement = x;
+
+      // Goto to the first non-default node.
+      CfgSwitchGotoNode gotoNode = addNode(new CfgSwitchGotoNode(parent, x));
+      Exit gotoExit = Exit.createNormal(gotoNode, null);
       
       int defaultPos = -1;
       
       List<Exit> breakExits = new ArrayList<Exit>();
 
-      for (JStatement s : x.getBody().getStatements()) {
+      List<JStatement> statements = x.getBody().getStatements();
+      
+      for (JStatement s : statements) {
         if (s instanceof JCaseStatement) {
           if (((JCaseStatement) s).getExpr() != null) {
             // case label
+            if (gotoExit != null) {
+              // This is first non-default case.
+              addExit(gotoExit);
+              gotoExit = null;
+            }
             List<Exit> elseExits = removeExits(Exit.Reason.CASE_ELSE);
             for (Exit e : elseExits) {
               addNormalExit(e.getNode(), e.role);
@@ -642,6 +652,16 @@
         breakExits.addAll(removeExits(Exit.Reason.BREAK));
       }
 
+      if (gotoExit != null) {
+        // Happens when there are no case statements.
+        if (defaultPos >= 0) {
+          addEdge(gotoExit, nodes.get(defaultPos));
+        } else {
+          addExit(gotoExit);
+        }
+        gotoExit = null;
+      }
+
       List<Exit> thenExits = removeExits(Exit.Reason.CASE_THEN);
       for (Exit e : thenExits) {
         addNormalExit(e.getNode(), e.role);
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/gflow/cfg/CfgSwitchGotoNode.java b/dev/core/src/com/google/gwt/dev/jjs/impl/gflow/cfg/CfgSwitchGotoNode.java
new file mode 100644
index 0000000..a37724d
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/gflow/cfg/CfgSwitchGotoNode.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2010 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.jjs.impl.gflow.cfg;
+
+import com.google.gwt.dev.jjs.ast.JSwitchStatement;
+
+/**
+ * Goto to first switch case statement.
+ */
+public class CfgSwitchGotoNode extends CfgGotoNode<JSwitchStatement> {
+  public CfgSwitchGotoNode(CfgNode<?> parent, JSwitchStatement node) {
+    super(parent, node);
+  }
+
+  @Override
+  public void accept(CfgVisitor visitor) {
+    visitor.visitSwitchGotoNode(this);
+  }
+
+  @Override
+  protected CfgNode<?> cloneImpl() {
+    return new CfgSwitchGotoNode(getParent(), getJNode());
+  }
+}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/gflow/cfg/CfgVisitor.java b/dev/core/src/com/google/gwt/dev/jjs/impl/gflow/cfg/CfgVisitor.java
index 8c6287b..6a2aa7e 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/gflow/cfg/CfgVisitor.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/gflow/cfg/CfgVisitor.java
@@ -105,6 +105,10 @@
     visitSimpleNode(node);
   }
 
+  public void visitSwitchGotoNode(CfgSwitchGotoNode node) {
+    visitGotoNode(node);
+  }
+
   public void visitThrowNode(CfgThrowNode node) {
     visitNode(node);
   }
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 b96c079..82c0b37 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
@@ -1105,6 +1105,7 @@
             "BLOCK -> [*]",
             "STMT -> [*]",
             "READ(i) -> [*]",
+            "GOTO -> [*]",
             "STMT -> [*]",
             "COND (EntryPoint.i == 1) -> [THEN=*, ELSE=1]",
             "STMT -> [*]",
@@ -1134,6 +1135,63 @@
     );
   }
 
+  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) {",
@@ -1160,10 +1218,12 @@
         "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 -> [*]",
@@ -1182,6 +1242,7 @@
         "COND (EntryPoint.i == 2) -> [THEN=*, ELSE=6]",
         "STMT -> [*]",
         "READ(j) -> [*]",
+        "GOTO -> [*]",
         "STMT -> [*]",
         "COND (EntryPoint.j == 0) -> [THEN=*, ELSE=4]",
         "STMT -> [*]",
@@ -1200,6 +1261,7 @@
         "COND (EntryPoint.i == 3) -> [THEN=*, ELSE=9]",
         "STMT -> [*]",
         "READ(j) -> [*]",
+        "GOTO -> [*]",
         "STMT -> [*]",
         "COND (EntryPoint.j == 0) -> [THEN=*, ELSE=7]",
         "STMT -> [*]",
@@ -1231,6 +1293,7 @@
             "BLOCK -> [*]",
             "STMT -> [*]",
             "READ(i) -> [*]",
+            "GOTO -> [*]",
             "STMT -> [*]",
             "COND (EntryPoint.i == 1) -> [THEN=*, ELSE=1]",
             "STMT -> [*]",