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 -> [*]",