GenerateJavaAST no longer visits code in a block that
follows an if(true) block that itself contains a
throw, a return, or any other unconditional break
in control flow. This prevents the compiler from
becoming confused when it wanders into parts of the
JDT AST that the JDT compile did not fill all the way in.
Review by: scottb
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@5561 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JIfStatement.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JIfStatement.java
index f616ec8..e573ca5 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JIfStatement.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JIfStatement.java
@@ -61,8 +61,22 @@
@Override
public boolean unconditionalControlBreak() {
- if (thenStmt != null && thenStmt.unconditionalControlBreak()
- && elseStmt != null && elseStmt.unconditionalControlBreak()) {
+ boolean thenBreaks = thenStmt != null
+ && thenStmt.unconditionalControlBreak();
+ if (thenBreaks && ifExpr == JBooleanLiteral.get(true)) {
+ // if(true) { /* unconditional break */ }
+ return true;
+ }
+
+ boolean elseBreaks = elseStmt != null
+ && elseStmt.unconditionalControlBreak();
+ if (elseBreaks && ifExpr == JBooleanLiteral.get(false)) {
+ // if(false) { } else { /* unconditional break */ }
+ return true;
+ }
+
+ if (thenBreaks && elseBreaks) {
+ // both branches have an unconditional break
return true;
}
return false;
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java
index fec6401..2de3856 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java
@@ -728,15 +728,7 @@
}
// user code (finally!)
- if (x.statements != null) {
- for (int i = 0, n = x.statements.length; i < n; ++i) {
- Statement origStmt = x.statements[i];
- JStatement jstmt = dispProcessStatement(origStmt);
- if (jstmt != null) {
- block.addStmt(jstmt);
- }
- }
- }
+ block.addStmts(processStatements(x.statements));
currentMethodScope = null;
currentMethod = null;
@@ -1473,14 +1465,8 @@
currentMethodBody = (JMethodBody) method.getBody();
currentMethodScope = x.scope;
- if (x.statements != null) {
- for (int i = 0, n = x.statements.length; i < n; ++i) {
- Statement origStmt = x.statements[i];
- JStatement jstmt = dispProcessStatement(origStmt);
- if (jstmt != null) {
- currentMethodBody.getBlock().addStmt(jstmt);
- }
- }
+ if (currentMethodBody != null) {
+ currentMethodBody.getBlock().addStmts(processStatements(x.statements));
}
currentMethodScope = null;
currentMethodBody = null;
@@ -1510,14 +1496,7 @@
SourceInfo info = makeSourceInfo(x);
JBlock block = new JBlock(info);
- if (x.statements != null) {
- for (int i = 0, n = x.statements.length; i < n; ++i) {
- JStatement jstmt = dispProcessStatement(x.statements[i]);
- if (jstmt != null) {
- block.addStmt(jstmt);
- }
- }
- }
+ block.addStmts(processStatements(x.statements));
return block;
}
@@ -1767,7 +1746,15 @@
program.getIndexedMethod("Enum.ordinal"));
}
JBlock block = new JBlock(info);
- block.addStmts(processStatements(x.statements));
+ // Don't use processStatements here, because it stops at control breaks
+ if (x.statements != null) {
+ for (Statement stmt : x.statements) {
+ JStatement jstmt = dispProcessStatement(stmt);
+ if (jstmt != null) {
+ block.addStmt(jstmt);
+ }
+ }
+ }
return new JSwitchStatement(info, expression, block);
}
@@ -1824,13 +1811,22 @@
List<JStatement> processStatements(Statement[] statements) {
List<JStatement> jstatements = new ArrayList<JStatement>();
if (statements != null) {
- for (int i = 0, n = statements.length; i < n; ++i) {
- JStatement jstmt = dispProcessStatement(statements[i]);
+ for (Statement stmt : statements) {
+ JStatement jstmt = dispProcessStatement(stmt);
if (jstmt != null) {
jstatements.add(jstmt);
+ if (jstmt.unconditionalControlBreak()) {
+ /*
+ * Stop processing statements, because the remaining ones are
+ * unreachable. The JDT compiler might not have fully fleshed out
+ * the unreachable statements.
+ */
+ break;
+ }
}
}
}
+
return jstatements;
}
diff --git a/user/test/com/google/gwt/dev/jjs/test/CompilerTest.java b/user/test/com/google/gwt/dev/jjs/test/CompilerTest.java
index 5413ff8..788b97f 100644
--- a/user/test/com/google/gwt/dev/jjs/test/CompilerTest.java
+++ b/user/test/com/google/gwt/dev/jjs/test/CompilerTest.java
@@ -557,6 +557,33 @@
assertFalse(e);
}
+ /**
+ * Test that code considered unreachable by the JDT does not crash the GWT
+ * compiler.
+ */
+ public void testDeadCode2() {
+ class SillyList {
+ }
+
+ final SillyList outerLocalVariable = new SillyList();
+
+ new SillyList() {
+ private void pretendToUse(SillyList x) {
+ }
+
+ void blah() {
+ if (true) {
+ throw new RuntimeException();
+ }
+ /*
+ * This code is unreachable, and so outerLocalVariable is never actually
+ * read by reachable code.
+ */
+ pretendToUse(outerLocalVariable);
+ }
+ };
+ }
+
public void testDeadTypes() {
if (false) {
new Object() {