Optimizes JS constructs like ('foo' != null) -> (true).

Patch by: mmastrac
Review by: me

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@5519 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/js/JsStaticEval.java b/dev/core/src/com/google/gwt/dev/js/JsStaticEval.java
index 12a1d2f..01bf4c2 100644
--- a/dev/core/src/com/google/gwt/dev/js/JsStaticEval.java
+++ b/dev/core/src/com/google/gwt/dev/js/JsStaticEval.java
@@ -159,6 +159,8 @@
         trySimplifyComma(arg1, arg2, ctx);
       } else if (op == JsBinaryOperator.EQ) {
         trySimplifyEq(x, arg1, arg2, ctx);
+      } else if (op == JsBinaryOperator.NEQ) {
+        trySimplifyNe(x, arg1, arg2, ctx);
       }
     }
 
@@ -482,6 +484,22 @@
       return original;
     }
 
+    private JsExpression simplifyNe(JsExpression original, JsExpression arg1,
+        JsExpression arg2) {
+      assert (original != null);
+
+      if (arg1 instanceof JsNullLiteral) {
+        return simplifyNullNe(original, arg2);
+      }
+
+      if (arg2 instanceof JsNullLiteral) {
+        return simplifyNullNe(original, arg1);
+      }
+
+      // no simplification made
+      return original;
+    }
+
     /**
      * Simplify exp == null.
      */
@@ -499,6 +517,23 @@
       return original;
     }
 
+    /**
+     * Simplify exp != null.
+     */
+    private JsExpression simplifyNullNe(JsExpression original, JsExpression exp) {
+      assert (original != null);
+
+      if (exp instanceof JsValueLiteral) {
+        // "undefined" is not a JsValueLiteral, so the only way
+        // the result can be false is if exp is itself a JsNullLiteral
+        boolean result = !(exp instanceof JsNullLiteral);
+        return program.getBooleanLiteral(result);
+      }
+
+      // no simplification made
+      return original;
+    }
+
     private void trySimplifyEq(JsExpression original, JsExpression arg1,
         JsExpression arg2, JsContext<JsExpression> ctx) {
       JsExpression updated = simplifyEq(original, arg1, arg2);
@@ -506,6 +541,14 @@
         ctx.replaceMe(updated);
       }
     }
+
+    private void trySimplifyNe(JsExpression original, JsExpression arg1,
+        JsExpression arg2, JsContext<JsExpression> ctx) {
+      JsExpression updated = simplifyNe(original, arg1, arg2);
+      if (updated != original) {
+        ctx.replaceMe(updated);
+      }
+    }
   }
 
   public static boolean exec(JsProgram program) {
diff --git a/dev/core/test/com/google/gwt/dev/js/JsStaticEvalTest.java b/dev/core/test/com/google/gwt/dev/js/JsStaticEvalTest.java
new file mode 100644
index 0000000..3e9a97e
--- /dev/null
+++ b/dev/core/test/com/google/gwt/dev/js/JsStaticEvalTest.java
@@ -0,0 +1,63 @@
+/*

+ * Copyright 2009 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.js;

+

+import com.google.gwt.dev.jjs.SourceOrigin;

+import com.google.gwt.dev.js.ast.JsProgram;

+import com.google.gwt.dev.js.ast.JsStatement;

+import com.google.gwt.dev.js.ast.JsVisitor;

+import com.google.gwt.dev.util.DefaultTextOutput;

+import com.google.gwt.dev.util.TextOutput;

+

+import junit.framework.TestCase;

+

+import java.io.StringReader;

+import java.util.List;

+

+public class JsStaticEvalTest extends TestCase {

+

+  public void testLiteralEqNull() throws Exception {

+    assertTrue(optimize("alert('test' == null)").equals("alert(false);"));

+  }

+

+  public void testLiteralNeNull() throws Exception {

+    assertTrue(optimize("alert('test' != null)").equals("alert(true);"));

+  }

+

+  public void testNullEqNull() throws Exception {

+    assertTrue(optimize("alert(null == null)").equals("alert(true);"));

+  }

+

+  public void testNullNeNull() throws Exception {

+    assertTrue(optimize("alert(null != null)").equals("alert(false);"));

+  }

+

+  private String optimize(String js) throws Exception {

+    JsProgram program = new JsProgram();

+    List<JsStatement> expected = JsParser.parse(SourceOrigin.UNKNOWN,

+        program.getScope(), new StringReader(js));

+    program.getGlobalBlock().getStatements().addAll(expected);

+    

+    // Run the static evaluation over this new program

+    JsStaticEval.exec(program);

+

+    TextOutput text = new DefaultTextOutput(true);

+    JsVisitor generator = new JsSourceGenerationVisitor(text);

+    

+    generator.accept(program);

+    return text.toString();

+  }

+}