Implements the Java assert statement in web mode via a compiler flag, "-ea".  This is mimic the JVM option to enable assertions.  When the option is not specified, the compiler will continue to optimize out all assert statements as it does currently.

Review by: bobv


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@1695 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/GWTCompiler.java b/dev/core/src/com/google/gwt/dev/GWTCompiler.java
index d851ff8..98fae9a 100644
--- a/dev/core/src/com/google/gwt/dev/GWTCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/GWTCompiler.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 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
@@ -36,8 +36,8 @@
 import com.google.gwt.dev.jdt.StandardSourceOracle;
 import com.google.gwt.dev.jdt.WebModeCompilerFrontEnd;
 import com.google.gwt.dev.jjs.JJSOptions;
-import com.google.gwt.dev.jjs.JsOutputOption;
 import com.google.gwt.dev.jjs.JavaToJavaScriptCompiler;
+import com.google.gwt.dev.jjs.JsOutputOption;
 import com.google.gwt.dev.shell.StandardRebindOracle;
 import com.google.gwt.dev.util.DefaultTextOutput;
 import com.google.gwt.dev.util.SelectionScriptGenerator;
@@ -51,6 +51,7 @@
 import com.google.gwt.dev.util.log.PrintWriterTreeLogger;
 import com.google.gwt.dev.util.xml.ReflectiveParser;
 import com.google.gwt.util.tools.ArgHandlerDisableAggressiveOptimization;
+import com.google.gwt.util.tools.ArgHandlerEnableAssertions;
 import com.google.gwt.util.tools.ArgHandlerExtra;
 import com.google.gwt.util.tools.ArgHandlerFlag;
 import com.google.gwt.util.tools.ArgHandlerOutDir;
@@ -335,6 +336,8 @@
 
     registerHandler(new ArgHandlerScriptStyle(jjsOptions));
 
+    registerHandler(new ArgHandlerEnableAssertions(jjsOptions));
+
     registerHandler(new ArgHandlerDisableAggressiveOptimization() {
       @Override
       public boolean setFlag() {
diff --git a/dev/core/src/com/google/gwt/dev/GWTShell.java b/dev/core/src/com/google/gwt/dev/GWTShell.java
index 9080797..ea48b17 100644
--- a/dev/core/src/com/google/gwt/dev/GWTShell.java
+++ b/dev/core/src/com/google/gwt/dev/GWTShell.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 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
@@ -36,6 +36,7 @@
 import com.google.gwt.dev.util.arg.ArgHandlerScriptStyle;
 import com.google.gwt.dev.util.log.AbstractTreeLogger;
 import com.google.gwt.util.tools.ArgHandlerDisableAggressiveOptimization;
+import com.google.gwt.util.tools.ArgHandlerEnableAssertions;
 import com.google.gwt.util.tools.ArgHandlerExtra;
 import com.google.gwt.util.tools.ArgHandlerFlag;
 import com.google.gwt.util.tools.ArgHandlerOutDir;
@@ -450,6 +451,8 @@
 
     registerHandler(new ArgHandlerScriptStyle(jjsOptions));
 
+    registerHandler(new ArgHandlerEnableAssertions(jjsOptions));
+
     registerHandler(new ArgHandlerDisableAggressiveOptimization() {
       @Override
       public boolean setFlag() {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/JJSOptions.java b/dev/core/src/com/google/gwt/dev/jjs/JJSOptions.java
index 879e1d5..a689917 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/JJSOptions.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/JJSOptions.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 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
@@ -21,6 +21,7 @@
 public class JJSOptions {
 
   private boolean aggressivelyOptimize = true;
+  private boolean enableAssertions = false;
   private JsOutputOption output = JsOutputOption.OBFUSCATED;
   private boolean validateOnly = false;
 
@@ -33,6 +34,7 @@
 
   public void copyFrom(JJSOptions other) {
     this.aggressivelyOptimize = other.aggressivelyOptimize;
+    this.enableAssertions = other.enableAssertions;
     this.output = other.output;
     this.validateOnly = other.validateOnly;
   }
@@ -52,6 +54,13 @@
   }
 
   /**
+   * Returns true if the compiler should generate code to check assertions.
+   */
+  public boolean isEnableAssertions() {
+    return enableAssertions;
+  }
+
+  /**
    * Returns true if the compiler should run in validation mode, not producing
    * any output.
    */
@@ -67,6 +76,13 @@
   }
 
   /**
+   * Sets whether or not the compiler should generate code to check assertions.
+   */
+  public void setEnableAssertions(boolean enableAssertions) {
+    this.enableAssertions = enableAssertions;
+  }
+
+  /**
    * Sets the compiler output option.
    */
   public void setOutput(JsOutputOption output) {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
index 512fe02..0ab0875 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 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
@@ -32,6 +32,7 @@
 import com.google.gwt.dev.jjs.ast.JProgram;
 import com.google.gwt.dev.jjs.ast.JReferenceType;
 import com.google.gwt.dev.jjs.impl.ArrayNormalizer;
+import com.google.gwt.dev.jjs.impl.AssertionNormalizer;
 import com.google.gwt.dev.jjs.impl.AssertionRemover;
 import com.google.gwt.dev.jjs.impl.BuildTypeMap;
 import com.google.gwt.dev.jjs.impl.CastNormalizer;
@@ -307,9 +308,14 @@
       // 
       checkForErrors(logger, true);
 
-      // TODO: figure out how to have a debug mode.
-      boolean isDebugEnabled = false;
-      if (!isDebugEnabled) {
+      /*
+       * TODO: if we defer this until later, we could maybe use the results of
+       * the assertions to enable more optimizations.
+       */
+      if (options.isEnableAssertions()) {
+        // Turn into assertion checking calls.
+        AssertionNormalizer.exec(jprogram);
+      } else {
         // Remove all assert statements.
         AssertionRemover.exec(jprogram);
       }
@@ -365,9 +371,6 @@
       // (5) "Normalize" the high-level Java tree into a lower-level tree more
       // suited for JavaScript code generation. Don't go reordering these
       // willy-nilly because there are some subtle interdependencies.
-      if (isDebugEnabled) {
-        // AssertionNormalizer.exec(jprogram);
-      }
       CatchBlockNormalizer.exec(jprogram);
       CompoundAssignmentNormalizer.exec(jprogram);
       JavaScriptObjectCaster.exec(jprogram);
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/AssertionNormalizer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/AssertionNormalizer.java
new file mode 100644
index 0000000..0120829
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/AssertionNormalizer.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2008 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;
+
+import com.google.gwt.dev.jjs.ast.Context;
+import com.google.gwt.dev.jjs.ast.JAssertStatement;
+import com.google.gwt.dev.jjs.ast.JBinaryOperation;
+import com.google.gwt.dev.jjs.ast.JBinaryOperator;
+import com.google.gwt.dev.jjs.ast.JExpression;
+import com.google.gwt.dev.jjs.ast.JMethod;
+import com.google.gwt.dev.jjs.ast.JMethodCall;
+import com.google.gwt.dev.jjs.ast.JModVisitor;
+import com.google.gwt.dev.jjs.ast.JPrimitiveType;
+import com.google.gwt.dev.jjs.ast.JProgram;
+import com.google.gwt.dev.jjs.ast.JReferenceType;
+import com.google.gwt.dev.jjs.ast.JType;
+
+/**
+ * Removes all assertion statements from the AST.
+ */
+public class AssertionNormalizer {
+
+  /**
+   * Normalizes all asserts.
+   */
+  private class AssertNormalizeVisitor extends JModVisitor {
+
+    public void endVisit(JAssertStatement x, Context ctx) {
+      JExpression lhs = x.getTestExpr();
+      String methodName = "Exceptions.throwAssertionError"
+          + getAssertMethodSuffix(x.getArg());
+      JMethod method = program.getIndexedMethod(methodName);
+      JMethodCall rhs = new JMethodCall(program, x.getSourceInfo(), null,
+          method);
+      if (x.getArg() != null) {
+        rhs.getArgs().add(x.getArg());
+      }
+      JBinaryOperation binOp = new JBinaryOperation(program, x.getSourceInfo(),
+          program.getTypePrimitiveBoolean(), JBinaryOperator.OR, lhs, rhs);
+      ctx.replaceMe(binOp.makeStatement());
+    }
+  }
+
+  public static void exec(JProgram program) {
+    new AssertionNormalizer(program).execImpl();
+  }
+
+  private static String getAssertMethodSuffix(JExpression arg) {
+    if (arg == null) {
+      return "";
+    }
+    JType argType = arg.getType();
+    if (argType instanceof JReferenceType) {
+      return "_Object";
+    }
+
+    assert (argType instanceof JPrimitiveType);
+    return "_" + argType.getName();
+  }
+
+  private final JProgram program;
+
+  public AssertionNormalizer(JProgram program) {
+    this.program = program;
+  }
+
+  private void execImpl() {
+    AssertNormalizeVisitor assertNormalizer = new AssertNormalizeVisitor();
+    assertNormalizer.accept(program);
+  }
+}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/AssertionRemover.java b/dev/core/src/com/google/gwt/dev/jjs/impl/AssertionRemover.java
index fd13b6d..685de4e 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/AssertionRemover.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/AssertionRemover.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 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
@@ -30,7 +30,7 @@
   /**
    * Removes all asserts.
    */
-  public class AssertVisitor extends JModVisitor {
+  private class AssertRemoveVisitor extends JModVisitor {
 
     public void endVisit(JAssertStatement x, Context ctx) {
       removeMe(x, ctx);
@@ -57,7 +57,7 @@
   }
 
   private void execImpl() {
-    AssertVisitor assertVisitor = new AssertVisitor();
-    assertVisitor.accept(program);
+    AssertRemoveVisitor assertRemover = new AssertRemoveVisitor();
+    assertRemover.accept(program);
   }
 }
diff --git a/dev/core/src/com/google/gwt/util/tools/ArgHandlerEnableAssertions.java b/dev/core/src/com/google/gwt/util/tools/ArgHandlerEnableAssertions.java
new file mode 100644
index 0000000..f37fa97
--- /dev/null
+++ b/dev/core/src/com/google/gwt/util/tools/ArgHandlerEnableAssertions.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2008 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.util.tools;
+
+import com.google.gwt.dev.jjs.JJSOptions;
+
+/**
+ * Handles the -ea command line flag.
+ */
+public class ArgHandlerEnableAssertions extends ArgHandlerFlag {
+
+  private final JJSOptions optionsToModify;
+
+  public ArgHandlerEnableAssertions(JJSOptions optionsToModify) {
+    this.optionsToModify = optionsToModify;
+  }
+
+  @Override
+  public String getPurpose() {
+    return "Debugging: causes the compiled output to check assert statements.";
+  }
+
+  @Override
+  public String getTag() {
+    return "-ea";
+  }
+
+  @Override
+  public boolean setFlag() {
+    optionsToModify.setEnableAssertions(true);
+    return true;
+  }
+}
diff --git a/user/super/com/google/gwt/emul/java/lang/AssertionError.java b/user/super/com/google/gwt/emul/java/lang/AssertionError.java
index ada4174..bbd3d65 100644
--- a/user/super/com/google/gwt/emul/java/lang/AssertionError.java
+++ b/user/super/com/google/gwt/emul/java/lang/AssertionError.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2006 Google Inc.
+ * Copyright 2008 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
@@ -23,15 +23,6 @@
   public AssertionError() {
   }
 
-  private AssertionError(String message) {
-    super(message);
-  }
-
-  public AssertionError(Object message) {
-    super(String.valueOf(message), message instanceof Throwable
-      ? (Throwable) message : null);
-  }
-
   public AssertionError(boolean message) {
     this(String.valueOf(message));
   }
@@ -40,6 +31,14 @@
     this(String.valueOf(message));
   }
 
+  public AssertionError(double message) {
+    this(String.valueOf(message));
+  }
+
+  public AssertionError(float message) {
+    this(String.valueOf(message));
+  }
+
   public AssertionError(int message) {
     this(String.valueOf(message));
   }
@@ -48,11 +47,12 @@
     this(String.valueOf(message));
   }
 
-  public AssertionError(float message) {
-    this(String.valueOf(message));
+  public AssertionError(Object message) {
+    super(String.valueOf(message), message instanceof Throwable
+      ? (Throwable) message : null);
   }
 
-  public AssertionError(double message) {
-    this(String.valueOf(message));
+  private AssertionError(String message) {
+    super(message);
   }
 }
diff --git a/user/test/com/google/gwt/dev/jjs/test/CoverageTest.java b/user/test/com/google/gwt/dev/jjs/test/CoverageTest.java
index fa1fd27..e8ee773 100644
--- a/user/test/com/google/gwt/dev/jjs/test/CoverageTest.java
+++ b/user/test/com/google/gwt/dev/jjs/test/CoverageTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 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
@@ -54,7 +54,7 @@
           next.foo();
           CoverageTest.this.next.foo();
           CoverageTest.this.x = z;
-          CoverageTest.super.x = z; // inexpressible in Java without name mangling
+          CoverageTest.super.x = z;
         }
 
         public void foo() {
@@ -65,7 +65,7 @@
           next.foo();
           CoverageTest.this.next.foo();
           CoverageTest.this.x = z;
-          CoverageTest.super.x = z; // inexpressible in Java without name mangling
+          CoverageTest.super.x = z;
         }
       };
 
@@ -148,22 +148,85 @@
       ia[0] = i;
     }
 
-    /**
-     * TODO(later): implement asserts.
-     */
     private void testAssertStatement() {
-      // i = 1;
-      // try {
-      // assert i == 2;
-      // fail();
-      // } catch (AssertionError e) {
-      // }
-      // try {
-      // assert i == 3 : "foo";
-      // fail();
-      // } catch (AssertionError e) {
-      // assertEquals("foo", e.getMessage());
-      // }
+      // AssertStatement
+
+      // See if assertions are even enabled.
+      try {
+        assert false;
+        // Assertions are not on, early out.
+        return;
+      } catch (AssertionError e) {
+        // Assertions are on, run the test.
+      }
+
+      i = 1;
+      try {
+        assert i == 2;
+        fail();
+      } catch (AssertionError e) {
+      }
+
+      try {
+        assert i == 3 : true;
+        fail();
+      } catch (AssertionError e) {
+        assertEquals("true", e.getMessage());
+      }
+
+      try {
+        assert i == 3 : 'c';
+        fail();
+      } catch (AssertionError e) {
+        assertEquals("c", e.getMessage());
+      }
+
+      try {
+        assert i == 3 : 1.1;
+        fail();
+      } catch (AssertionError e) {
+        assertEquals("1.1", e.getMessage());
+      }
+
+      try {
+        assert i == 3 : 1.2f;
+        fail();
+      } catch (AssertionError e) {
+        assertEquals("1.2", e.getMessage());
+      }
+
+      try {
+        assert i == 3 : 5;
+        fail();
+      } catch (AssertionError e) {
+        assertEquals("5", e.getMessage());
+      }
+
+      try {
+        assert i == 3 : 6L;
+        fail();
+      } catch (AssertionError e) {
+        assertEquals("6", e.getMessage());
+      }
+
+      try {
+        assert i == 3 : "foo";
+        fail();
+      } catch (AssertionError e) {
+        assertEquals("foo", e.getMessage());
+      }
+
+      try {
+        assert i == 3 : new Object() {
+          @Override
+          public String toString() {
+            return "bar";
+          }
+        };
+        fail();
+      } catch (AssertionError e) {
+        assertEquals("bar", e.getMessage());
+      }
     }
 
     private void testAssignment() {
@@ -486,7 +549,8 @@
       assertEquals(21, i);
       i = Inner.this.i + Inner.this.j + Inner.this.x + Inner.this.y;
       assertEquals(30, i);
-      i = CoverageTest.this.i + CoverageTest.this.j + CoverageTest.this.x + CoverageTest.this.y;
+      i = CoverageTest.this.i + CoverageTest.this.j + CoverageTest.this.x
+          + CoverageTest.this.y;
       assertEquals(15, i);
       i = super.i + super.j + super.x + super.y;
       assertEquals(25, i);