Introducing lattice TOP for constants. Making it starting value.

Review at http://gwt-code-reviews.appspot.com/436801


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@8045 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/gflow/constants/ConstantsAnalysis.java b/dev/core/src/com/google/gwt/dev/jjs/impl/gflow/constants/ConstantsAnalysis.java
index 09daf8b..0cd896e 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/gflow/constants/ConstantsAnalysis.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/gflow/constants/ConstantsAnalysis.java
@@ -15,7 +15,6 @@
  */
 package com.google.gwt.dev.jjs.impl.gflow.constants;
 
-import com.google.gwt.dev.jjs.ast.JParameterRef;
 import com.google.gwt.dev.jjs.impl.gflow.Analysis;
 import com.google.gwt.dev.jjs.impl.gflow.AssumptionMap;
 import com.google.gwt.dev.jjs.impl.gflow.AssumptionUtil;
@@ -46,18 +45,7 @@
 
   public void setInitialGraphAssumptions(Cfg graph,
       AssumptionMap<CfgEdge, ConstantsAssumption> assumptionMap) {
-    // Set all parameter assumptions to T
-
-    ConstantsAssumption.Updater updater = new ConstantsAssumption.Updater(null);
-    for (CfgNode<?> node : graph.getNodes()) {
-      Object jnode = node.getJNode();
-      if (jnode instanceof JParameterRef) {
-        updater.set(((JParameterRef) jnode).getParameter(), null);
-      }
-    }
-    ConstantsAssumption assumptions = updater.unwrap();
-
-    AssumptionUtil.setAssumptions(graph.getGraphInEdges(), assumptions,
-        assumptionMap);
+    AssumptionUtil.setAssumptions(graph.getGraphInEdges(), 
+        ConstantsAssumption.TOP, assumptionMap);
   }
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/gflow/constants/ConstantsAssumption.java b/dev/core/src/com/google/gwt/dev/jjs/impl/gflow/constants/ConstantsAssumption.java
index 19533cf..75a7c55 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/gflow/constants/ConstantsAssumption.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/gflow/constants/ConstantsAssumption.java
@@ -21,14 +21,21 @@
 import com.google.gwt.dev.jjs.ast.JValueLiteral;
 import com.google.gwt.dev.jjs.ast.JVariable;
 import com.google.gwt.dev.jjs.impl.gflow.Assumption;
+import com.google.gwt.dev.util.Preconditions;
 
 import java.util.ArrayList;
-import java.util.IdentityHashMap;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
 /**
  * Assumptions for ConstantsAnalysis.
+ * 
+ * Is a map from variable into it's constant value. If variable is not present
+ * in the map, then it's not a constant. 
+ * 
+ * Empty ConstantsAssumption is a top of the lattice, and is not equals to 
+ * null assumption (which is the bottom of every lattice).
  */
 public class ConstantsAssumption implements Assumption<ConstantsAssumption> {
   /**
@@ -59,12 +66,8 @@
     }
 
     public ConstantsAssumption unwrap() {
-      return assumption;
-    }
-
-    public ConstantsAssumption unwrapToNotNull() {
-      if (assumption == null) {
-        return new ConstantsAssumption();
+      if (assumption != null && assumption.isEmpty()) {
+        return TOP;
       }
       return assumption;
     }
@@ -76,114 +79,56 @@
       }
     }
   }
-
-  /**
-   * Contains individual assumptions about variables. If variable isn't in the
-   * map, then variable assumption is _|_ (bottom), if variable's value is
-   * null, then variable assumption is T - variable has non-constant value.
-   */
-  private final Map<JVariable, JValueLiteral> values;
-
-  public ConstantsAssumption() {
-    values = new IdentityHashMap<JVariable, JValueLiteral>();
-  }
-
-  public ConstantsAssumption(ConstantsAssumption a) {
-    if (a != null) {
-      values = new IdentityHashMap<JVariable, JValueLiteral>(a.values);
-    } else {
-      values = new IdentityHashMap<JVariable, JValueLiteral>();
-    }
-  }
-
-  @Override
-  public boolean equals(Object obj) {
-    if (obj == this) {
-      return true;
-    }
-    if (obj == null) {
-      return values.isEmpty();
-    }
-    ConstantsAssumption other = (ConstantsAssumption) obj;
-    return values.equals(other.values);
-  }
-
-  /**
-   * Get variable constant assumption. <code>null</code> if there's no constant
-   * assumption for this variable. 
-   */
-  public JValueLiteral get(JVariable variable) {
-    return values.get(variable);
-  }
   
   /**
-   * Check if we have constant (i.e. not top and not bottom) assumption about 
-   * the variable.
+   * A wrapper around JValueLiteral to give it equals() method.
    */
-  public boolean hasAssumption(JVariable variable) {
-    return get(variable) != null;
-  }
-
-  @Override
-  public int hashCode() {
-    return values.hashCode();
-  }
-
-  public ConstantsAssumption join(ConstantsAssumption other) {
-    if (other == null || other.values.isEmpty()) {
-      return this;
+  private static class LiteralWrapper {
+    private final JValueLiteral literal;
+    
+    LiteralWrapper(JValueLiteral literal) {
+      Preconditions.checkNotNull(literal);
+      this.literal = literal;
     }
-    
-    if (values.isEmpty()) {
-      return other;
-    }
-    
-    ConstantsAssumption result = new ConstantsAssumption(this);
-    
-    for (JVariable var : other.values.keySet()) {
-      if (values.containsKey(var)) {
-        // Var is present in both assumptions. Join their values.
-        result.values.put(var, join(values.get(var), other.values.get(var)));
-      } else {
-        result.values.put(var, other.values.get(var));
+
+    @Override
+    public boolean equals(Object obj) {
+      if (obj == null) {
+        return false;
       }
-    }
-    
-    return result;
-  }
-  
-  public String toDebugString() {
-    StringBuffer result = new StringBuffer();
-    
-    result.append("{");
-    List<JVariable> variables = new ArrayList<JVariable>(values.keySet());
-    HasName.Util.sortByName(variables);
-    for (JVariable variable : variables) {
-      if (result.length() > 1) {
-        result.append(", ");
+      
+      if (obj == this) {
+        return true;
       }
-      result.append(variable.getName());
-      result.append(" = ");
-      if (values.get(variable) == null) {
-        result.append("T");
-      } else {
-        result.append(values.get(variable));
-      }
+      
+      LiteralWrapper other = (LiteralWrapper) obj;
+      return equal(this.literal, other.literal);
     }
-    result.append("}");
-    
-    return result.toString();
+
+    @Override
+    public int hashCode() {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String toString() {
+      return literal.toString();
+    }
   }
 
-  @Override
-  public String toString() {
-    return toDebugString();
-  }
+  /**
+   * A TOP for the lattice. Means that all variables are not constant.
+   */
+  public static ConstantsAssumption TOP = new ConstantsAssumption();
 
-  private boolean equal(JValueLiteral literal1, JValueLiteral literal2) {
+  private static boolean equal(JValueLiteral literal1, JValueLiteral literal2) {
     if (literal1 == null || literal2 == null) {
       return literal1 == literal2;
     } 
+    
+    if (literal1 == literal2) {
+      return true;
+    }
 
     if (literal1.getClass() != literal2.getClass()) {
       // these are different literal types. 
@@ -215,7 +160,7 @@
     return valueObj1.equals(valueObj2);
   }
   
-  private JValueLiteral join(JValueLiteral value1, JValueLiteral value2) {
+  private static JValueLiteral join(JValueLiteral value1, JValueLiteral value2) {
     if (!equal(value1, value2)) {
       return null;
     }
@@ -223,7 +168,136 @@
     return value1;
   }
   
-  private void set(JVariable variable, JValueLiteral literal) {
-    values.put(variable, literal);
+  private static JValueLiteral join(LiteralWrapper wrapper1, 
+      LiteralWrapper wrapper2) {
+    if (wrapper1 == null || wrapper2 == null) {
+      return null;
+    }
+    
+    return join(wrapper1.literal, wrapper2.literal);
+  }
+
+  /**
+   * Contains individual assumptions about variables. If variable isn't in the
+   * map, then variable assumption is _|_ (bottom), if variable's value is
+   * null, then variable assumption is T - variable has non-constant value.
+   */
+  private final Map<JVariable, LiteralWrapper> values;
+
+  public ConstantsAssumption() {
+    values = new HashMap<JVariable, LiteralWrapper>();
+  }
+
+  public ConstantsAssumption(ConstantsAssumption a) {
+    if (a != null) {
+      values = new HashMap<JVariable, LiteralWrapper>(a.values);
+    } else {
+      values = new HashMap<JVariable, LiteralWrapper>();
+    }
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj == this) {
+      return true;
+    }
+    if (obj == null) {
+      return false;
+    }
+    ConstantsAssumption other = (ConstantsAssumption) obj;
+    return values.equals(other.values);
+  }
+  
+  /**
+   * Get variable constant assumption. <code>null</code> if there's no constant
+   * assumption for this variable. 
+   */
+  public JValueLiteral get(JVariable variable) {
+    LiteralWrapper wrapper = values.get(variable);
+    return wrapper != null ? wrapper.literal : null;
+  }
+
+  /**
+   * Check if we have constant (i.e. not top and not bottom) assumption about 
+   * the variable.
+   */
+  public boolean hasAssumption(JVariable variable) {
+    return get(variable) != null;
+  }
+
+  @Override
+  public int hashCode() {
+    return values.hashCode();
+  }
+  
+  public boolean isEmpty() {
+    return values.isEmpty();
+  }
+
+  public ConstantsAssumption join(ConstantsAssumption other) {
+    if (other == null) {
+      return this;
+    }
+    
+    if (other == TOP || this == TOP || isEmpty() || other.isEmpty()) {
+      return TOP;
+    }
+    
+    ConstantsAssumption result = new ConstantsAssumption();
+    
+    for (JVariable var : other.values.keySet()) {
+      if (values.containsKey(var)) {
+        // Var is present in both assumptions. Join their values.
+        JValueLiteral value = join(values.get(var), other.values.get(var));
+        if (value != null) {
+          result.values.put(var, new LiteralWrapper(value));
+        }
+      } 
+    }
+    
+    if (result.isEmpty()) {
+      return TOP;
+    }
+    
+    return result;
+  }
+
+  public String toDebugString() {
+    if (this == TOP || isEmpty()) {
+      return "T";
+    }
+    StringBuffer result = new StringBuffer();
+    
+    result.append("{");
+    List<JVariable> variables = new ArrayList<JVariable>(values.keySet());
+    HasName.Util.sortByName(variables);
+    for (JVariable variable : variables) {
+      if (result.length() > 1) {
+        result.append(", ");
+      }
+      result.append(variable.getName());
+      result.append(" = ");
+      if (values.get(variable) == null) {
+        result.append("T");
+      } else {
+        result.append(values.get(variable));
+      }
+    }
+    result.append("}");
+    
+    return result.toString();
+  }
+  
+  @Override
+  public String toString() {
+    return toDebugString();
+  }
+  
+  void set(JVariable variable, JValueLiteral literal) {
+    if (literal != null) {
+      values.put(variable, new LiteralWrapper(literal));
+    } else {
+      values.remove(variable);
+    }
   }
 }
diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/gflow/GflowTests.java b/dev/core/test/com/google/gwt/dev/jjs/impl/gflow/GflowTests.java
index d231147..8dede28 100644
--- a/dev/core/test/com/google/gwt/dev/jjs/impl/gflow/GflowTests.java
+++ b/dev/core/test/com/google/gwt/dev/jjs/impl/gflow/GflowTests.java
@@ -16,6 +16,7 @@
 package com.google.gwt.dev.jjs.impl.gflow;
 
 import com.google.gwt.dev.jjs.impl.gflow.cfg.CfgBuilderTest;
+import com.google.gwt.dev.jjs.impl.gflow.constants.ConstantsAssumptionTest;
 import com.google.gwt.dev.jjs.impl.gflow.constants.ExpressionEvaluatorTest;
 import com.google.gwt.dev.jjs.impl.gflow.constants.AssumptionsDeducerTest;
 import com.google.gwt.dev.jjs.impl.gflow.constants.ConstantsAnalysisTest;
@@ -36,6 +37,7 @@
   public static Test suite() {
     TestSuite suite = new TestSuite();
     suite.addTestSuite(CfgBuilderTest.class);
+    suite.addTestSuite(ConstantsAssumptionTest.class);
     suite.addTestSuite(AssumptionsDeducerTest.class);
     suite.addTestSuite(ExpressionEvaluatorTest.class);
     suite.addTestSuite(ConstantsAnalysisTest.class);
diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/gflow/constants/AssumptionsDeducerTest.java b/dev/core/test/com/google/gwt/dev/jjs/impl/gflow/constants/AssumptionsDeducerTest.java
index 69ccd27..15231bc 100644
--- a/dev/core/test/com/google/gwt/dev/jjs/impl/gflow/constants/AssumptionsDeducerTest.java
+++ b/dev/core/test/com/google/gwt/dev/jjs/impl/gflow/constants/AssumptionsDeducerTest.java
@@ -48,56 +48,56 @@
   
   public void testEq() throws Exception {
     from("int i = 0;", "i == 10", true).deduce("{i = 10}");
-    from("int i = 0;", "i == 10", false).deduce("{i = T}");
+    from("int i = 0;", "i == 10", false).deduce("T");
   }
 
   public void testNeq() throws Exception {
-    from("int i = 0;", "i != 10", true).deduce("{i = T}");
+    from("int i = 0;", "i != 10", true).deduce("T");
     from("int i = 0;", "i != 10", false).deduce("{i = 10}");
   }
 
   public void testInstanceof() throws Exception {
-    from("Object o = null;", "o instanceof String", true).deduce("{}");
-    from("Object o = null;", "o instanceof String", false).deduce("{}");
+    from("Object o = null;", "o instanceof String", true).deduce("T");
+    from("Object o = null;", "o instanceof String", false).deduce("T");
   }
 
   public void testReference() throws Exception {
-    from("String s = null;", "s.length() == 0", true).deduce("{}");
-    from("String s = null;", "s.length() == 0", false).deduce("{}");
-    from("Foo f = null;", "f.o == null", true).deduce("{}");
-    from("Foo f = null;", "f.o == null", false).deduce("{}");
+    from("String s = null;", "s.length() == 0", true).deduce("T");
+    from("String s = null;", "s.length() == 0", false).deduce("T");
+    from("Foo f = null;", "f.o == null", true).deduce("T");
+    from("Foo f = null;", "f.o == null", false).deduce("T");
   }
 
   public void testAnd() throws Exception {
     from("int i = 0; int j = 0;", "i == 10 && j == 11", true).deduce("{i = 10, j = 11}");
-    from("int i = 0; int j = 0;", "i == 10 && j == 11", false).deduce("{i = T, j = T}");
+    from("int i = 0; int j = 0;", "i == 10 && j == 11", false).deduce("T");
   }
 
   public void testOr() throws Exception {
     from("int i = 0; int j = 0;", "i != 10 || j != 11", false).deduce("{i = 10, j = 11}");
-    from("int i = 0; int j = 0;", "i != 10 || j != 11", true).deduce("{i = T, j = T}");
+    from("int i = 0; int j = 0;", "i != 10 || j != 11", true).deduce("T");
   }
 
   public void testFloatEq() throws Exception {
     from("float f = 0;", "f == 1.0", true).deduce("{f = 1.0}");
     // There are positive and negative zeros. Do not deduce anything in here
-    from("float f = 0;", "f == 0.0", true).deduce("{f = T}");
+    from("float f = 0;", "f == 0.0", true).deduce("T");
   }
 
   public void testDoubleEq() throws Exception {
     from("double f = 0;", "f == 1.0", true).deduce("{f = 1.0}");
     // There are positive and negative zeros. Do not deduce anything in here
-    from("double f = 0;", "f == 0.0", true).deduce("{f = T}");
+    from("double f = 0;", "f == 0.0", true).deduce("T");
   }
 
   public void testNullNotNull() throws Exception {
     from("String s = null;", "s == null", true).deduce("{s = null}");
-    from("String s = null;", "s == null", false).deduce("{s = T}");
-    from("String s = null;", "s != null", true).deduce("{s = T}");
+    from("String s = null;", "s == null", false).deduce("T");
+    from("String s = null;", "s != null", true).deduce("T");
     from("String s = null;", "s != null", false).deduce("{s = null}");
     from("String s = null;", "null == s", true).deduce("{s = null}");
-    from("String s = null;", "null == s", false).deduce("{s = T}");
-    from("String s = null;", "null != s", true).deduce("{s = T}");
+    from("String s = null;", "null == s", false).deduce("T");
+    from("String s = null;", "null != s", true).deduce("T");
     from("String s = null;", "null != s", false).deduce("{s = null}");
   }
 
@@ -108,10 +108,10 @@
     List<JStatement> statements = block.getStatements();
     JIfStatement ifStatement = (JIfStatement) statements.get(statements.size() - 1);
     
-    Updater assumptions = new Updater(null);
+    Updater assumptions = new Updater(ConstantsAssumption.TOP);
     AssumptionDeducer.deduceAssumption(ifStatement.getIfExpr(), 
         JBooleanLiteral.get(b), assumptions);
-    return new Result(assumptions.unwrapToNotNull());
+    return new Result(assumptions.unwrap());
   }
   
   private class Result {
diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/gflow/constants/ConstantsAnalysisTest.java b/dev/core/test/com/google/gwt/dev/jjs/impl/gflow/constants/ConstantsAnalysisTest.java
index 82902bf..8a14d53 100644
--- a/dev/core/test/com/google/gwt/dev/jjs/impl/gflow/constants/ConstantsAnalysisTest.java
+++ b/dev/core/test/com/google/gwt/dev/jjs/impl/gflow/constants/ConstantsAnalysisTest.java
@@ -1,3 +1,18 @@
+/*
+ * 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.gflow.constants;
 
 import com.google.gwt.dev.jjs.impl.gflow.Analysis;
@@ -23,50 +38,50 @@
 
   public void testDeclWithConstInit() throws Exception {
     analyze("void", "int i = 1;").into(
-        "BLOCK -> [*]",
-        "STMT -> [*]",
+        "BLOCK -> [* T]",
+        "STMT -> [* T]",
         "WRITE(i, 1) -> [* {i = 1}]",
         "END");
   }
 
   public void testDeclWithConstOps() throws Exception {
     analyze("void", "int i = 1 + 1;").into(
-        "BLOCK -> [*]",
-        "STMT -> [*]",
+        "BLOCK -> [* T]",
+        "STMT -> [* T]",
         "WRITE(i, 2) -> [* {i = 2}]",
         "END");
   }
 
   public void testDeclWithNonconstInit() throws Exception {
     analyze("void", "int i = foo();").into(
-        "BLOCK -> [*]",
-        "STMT -> [*]",
-        "OPTTHROW(foo()) -> [NOTHROW=*, RE=1]",
-        "CALL(foo) -> [*]",
-        "WRITE(i, EntryPoint.foo()) -> [* {i = T}]",
+        "BLOCK -> [* T]",
+        "STMT -> [* T]",
+        "OPTTHROW(foo()) -> [NOTHROW=* T, RE=1 T]",
+        "CALL(foo) -> [* T]",
+        "WRITE(i, EntryPoint.foo()) -> [* T]",
         "1: END");
   }
 
   public void testReassign() throws Exception {
     analyze("void", "int i = 1; i = 2;").into(
-        "BLOCK -> [*]",
-        "STMT -> [*]",
+        "BLOCK -> [* T]",
+        "STMT -> [* T]",
         "WRITE(i, 1) -> [* {i = 1}]",
         "STMT -> [* {i = 1}]",
         "WRITE(i, 2) -> [* {i = 2}]",
         "END");
     analyze("void", "int i; i = 3;").into(
-        "BLOCK -> [*]",
-        "STMT -> [*]",
-        "STMT -> [*]",
+        "BLOCK -> [* T]",
+        "STMT -> [* T]",
+        "STMT -> [* T]",
         "WRITE(i, 3) -> [* {i = 3}]",
         "END");
   }
 
   public void test2Vars() throws Exception {
     analyze("void", "int i = 1; int j = 2;").into(
-        "BLOCK -> [*]",
-        "STMT -> [*]",
+        "BLOCK -> [* T]",
+        "STMT -> [* T]",
         "WRITE(i, 1) -> [* {i = 1}]",
         "STMT -> [* {i = 1}]",
         "WRITE(j, 2) -> [* {i = 1, j = 2}]",
@@ -75,8 +90,8 @@
   
   public void testSequence() throws Exception {
     analyze("void", "int i = 1; int j = i; int k = j; int l = k;").into(
-        "BLOCK -> [*]",
-        "STMT -> [*]",
+        "BLOCK -> [* T]",
+        "STMT -> [* T]",
         "WRITE(i, 1) -> [* {i = 1}]",
         "STMT -> [* {i = 1}]",
         "READ(i) -> [* {i = 1}]",
@@ -92,26 +107,26 @@
   
   public void testIfStatement() throws Exception {
     analyze("void", "int i = k; if (i == 1) { int j = i; } else { int j = i; } ").into(
-        "BLOCK -> [*]",
-        "STMT -> [*]",
-        "READ(k) -> [*]",
-        "WRITE(i, EntryPoint.k) -> [* {i = T}]",
-        "STMT -> [* {i = T}]",
-        "READ(i) -> [* {i = T}]",
-        "COND (i == 1) -> [THEN=* {i = 1}, ELSE=1 {i = T}]",
+        "BLOCK -> [* T]",
+        "STMT -> [* T]",
+        "READ(k) -> [* T]",
+        "WRITE(i, EntryPoint.k) -> [* T]",
+        "STMT -> [* T]",
+        "READ(i) -> [* T]",
+        "COND (i == 1) -> [THEN=* {i = 1}, ELSE=1 T]",
         "BLOCK -> [* {i = 1}]",
         "STMT -> [* {i = 1}]",
         "READ(i) -> [* {i = 1}]",
         "WRITE(j, i) -> [2 {i = 1, j = 1}]",
-        "1: BLOCK -> [* {i = T}]",
-        "STMT -> [* {i = T}]",
-        "READ(i) -> [* {i = T}]",
-        "WRITE(j, i) -> [* {i = T, j = T}]",
+        "1: BLOCK -> [* T]",
+        "STMT -> [* T]",
+        "READ(i) -> [* T]",
+        "WRITE(j, i) -> [* T]",
         "2: END");
 
     analyze("int", "int j = 0; if (foo() == 1) j = 1; return j;").into(
-        "BLOCK -> [*]",
-        "STMT -> [*]",
+        "BLOCK -> [* T]",
+        "STMT -> [* T]",
         "WRITE(j, 0) -> [* {j = 0}]",
         "STMT -> [* {j = 0}]",
         "OPTTHROW(foo()) -> [NOTHROW=* {j = 0}, RE=2 {j = 0}]",
@@ -119,14 +134,14 @@
         "COND (EntryPoint.foo() == 1) -> [THEN=* {j = 0}, ELSE=1 {j = 0}]",
         "STMT -> [* {j = 0}]",
         "WRITE(j, 1) -> [* {j = 1}]",
-        "1: STMT -> [* {j = T}]",
-        "READ(j) -> [* {j = T}]",
-        "GOTO -> [* {j = T}]",
+        "1: STMT -> [* T]",
+        "READ(j) -> [* T]",
+        "GOTO -> [* T]",
         "2: END");
 
     analyze("int", "int j = 0; if (foo() == 1) j = foo(); return j;").into(
-        "BLOCK -> [*]",
-        "STMT -> [*]",
+        "BLOCK -> [* T]",
+        "STMT -> [* T]",
         "WRITE(j, 0) -> [* {j = 0}]",
         "STMT -> [* {j = 0}]",
         "OPTTHROW(foo()) -> [NOTHROW=* {j = 0}, RE=2 {j = 0}]",
@@ -135,30 +150,30 @@
         "STMT -> [* {j = 0}]",
         "OPTTHROW(foo()) -> [NOTHROW=* {j = 0}, RE=2 {j = 0}]",
         "CALL(foo) -> [* {j = 0}]",
-        "WRITE(j, EntryPoint.foo()) -> [* {j = T}]",
-        "1: STMT -> [* {j = T}]",
-        "READ(j) -> [* {j = T}]",
-        "GOTO -> [* {j = T}]",
+        "WRITE(j, EntryPoint.foo()) -> [* T]",
+        "1: STMT -> [* T]",
+        "READ(j) -> [* T]",
+        "GOTO -> [* T]",
         "2: END");
   }
   
   public void testWhileLoop1() throws Exception {
     analyze("void", "int j = 1; while (j > 0) ++j;").into(
-        "BLOCK -> [*]",
-        "STMT -> [*]",
+        "BLOCK -> [* T]",
+        "STMT -> [* T]",
         "WRITE(j, 1) -> [* {j = 1}]",
         "STMT -> [* {j = 1}]",
-        "1: READ(j) -> [* {j = T}]",
-        "COND (j > 0) -> [THEN=* {j = T}, ELSE=2 {j = T}]",
-        "STMT -> [* {j = T}]",
-        "READWRITE(j, null) -> [1 {j = T}]",
+        "1: READ(j) -> [* T]",
+        "COND (j > 0) -> [THEN=* T, ELSE=2 T]",
+        "STMT -> [* T]",
+        "READWRITE(j, null) -> [1 T]",
         "2: END");
   }
   
   public void testWhileLoop2() throws Exception {
     analyze("void", "int j = 0; while (j > 0) {};").into(
-        "BLOCK -> [*]",
-        "STMT -> [*]",
+        "BLOCK -> [* T]",
+        "STMT -> [* T]",
         "WRITE(j, 0) -> [* {j = 0}]",
         "STMT -> [* {j = 0}]",
         "1: READ(j) -> [* {j = 0}]",
@@ -169,8 +184,8 @@
   
   public void testConditionalExpressions() throws Exception {
     analyze("void", "boolean b1 = false; boolean b2 = false; if (b1 && (b2 = true)) b1 = true;").into(
-        "BLOCK -> [*]",
-        "STMT -> [*]",
+        "BLOCK -> [* T]",
+        "STMT -> [* T]",
         "WRITE(b1, false) -> [* {b1 = false}]",
         "STMT -> [* {b1 = false}]",
         "WRITE(b2, false) -> [* {b1 = false, b2 = false}]",
@@ -178,9 +193,9 @@
         "READ(b1) -> [* {b1 = false, b2 = false}]",
         "COND (b1) -> [THEN=* {b1 = false, b2 = false}, ELSE=1 {b1 = false, b2 = false}]",
         "WRITE(b2, true) -> [* {b1 = false, b2 = true}]",
-        "1: COND (b1 && (b2 = true)) -> [THEN=* {b1 = false, b2 = T}, ELSE=2 {b1 = false, b2 = T}]",
-        "STMT -> [* {b1 = false, b2 = T}]",
-        "WRITE(b1, true) -> [* {b1 = true, b2 = T}]",
+        "1: COND (b1 && (b2 = true)) -> [THEN=* {b1 = false}, ELSE=2 {b1 = false}]",
+        "STMT -> [* {b1 = false}]",
+        "WRITE(b1, true) -> [* {b1 = true}]",
         "2: END");
   }
 
@@ -193,8 +208,8 @@
     		"if (f != null) if (e == null)" +
     		"  return;" +
     		"boolean b = e == null;").into(
-        "BLOCK -> [*]",
-        "STMT -> [*]",
+        "BLOCK -> [* T]",
+        "STMT -> [* T]",
         "WRITE(e, null) -> [* {e = null}]",
         "STMT -> [* {e = null}]",
         "READ(f) -> [* {e = null}]",
@@ -216,18 +231,18 @@
    */
   public void testParamNonConstant() throws Exception {
     analyzeWithParams("void", "int i, int j", "if (j == 0) { i = 0; } j=i; j=0;").into(
-        "BLOCK -> [* {i = T, j = T}]",
-        "STMT -> [* {i = T, j = T}]",
-        "READ(j) -> [* {i = T, j = T}]",
-        "COND (j == 0) -> [THEN=* {i = T, j = 0}, ELSE=1 {i = T, j = T}]",
-        "BLOCK -> [* {i = T, j = 0}]",
-        "STMT -> [* {i = T, j = 0}]",
+        "BLOCK -> [* T]",
+        "STMT -> [* T]",
+        "READ(j) -> [* T]",
+        "COND (j == 0) -> [THEN=* {j = 0}, ELSE=1 T]",
+        "BLOCK -> [* {j = 0}]",
+        "STMT -> [* {j = 0}]",
         "WRITE(i, 0) -> [* {i = 0, j = 0}]",
-        "1: STMT -> [* {i = T, j = T}]",
-        "READ(i) -> [* {i = T, j = T}]",
-        "WRITE(j, i) -> [* {i = T, j = T}]",
-        "STMT -> [* {i = T, j = T}]",
-        "WRITE(j, 0) -> [* {i = T, j = 0}]",
+        "1: STMT -> [* T]",
+        "READ(i) -> [* T]",
+        "WRITE(j, i) -> [* T]",
+        "STMT -> [* T]",
+        "WRITE(j, 0) -> [* {j = 0}]",
         "END"
       );
   }
diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/gflow/constants/ConstantsAssumptionTest.java b/dev/core/test/com/google/gwt/dev/jjs/impl/gflow/constants/ConstantsAssumptionTest.java
new file mode 100644
index 0000000..b0dee1d
--- /dev/null
+++ b/dev/core/test/com/google/gwt/dev/jjs/impl/gflow/constants/ConstantsAssumptionTest.java
@@ -0,0 +1,115 @@
+/*
+ * 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.gflow.constants;
+
+import com.google.gwt.dev.jjs.ast.JIntLiteral;
+import com.google.gwt.dev.jjs.ast.JLocal;
+import com.google.gwt.dev.jjs.ast.JMethodBody;
+import com.google.gwt.dev.jjs.ast.JPrimitiveType;
+import com.google.gwt.dev.jjs.ast.JProgram;
+
+import junit.framework.TestCase;
+
+/**
+ * Test for ConstantsAssumption.
+ * 
+ * We use toString comparison in this test to make it simpler.
+ */
+public class ConstantsAssumptionTest extends TestCase {
+  private final JIntLiteral zero = newIntLiteral(0);
+  private final JIntLiteral one = newIntLiteral(1);
+  private final JLocal i = newLocal("i", JPrimitiveType.INT);
+  private final JLocal j = newLocal("j", JPrimitiveType.INT);
+  
+  public void testEmptyAssumption() {
+    ConstantsAssumption a = new ConstantsAssumption();
+    assertEquals("T", a.toString());
+  }
+  
+  public void testSet() {
+    ConstantsAssumption a = new ConstantsAssumption();
+    a.set(i, zero);
+    assertEquals("{i = 0}", a.toString());
+    a.set(j, one);
+    assertEquals("{i = 0, j = 1}", a.toString());
+    a.set(i, null);
+    assertEquals("{j = 1}", a.toString());
+  }
+  
+  public void testJoin_SameValues() {
+    ConstantsAssumption a1 = new ConstantsAssumption();
+    a1.set(i, zero);
+    a1.set(j, one);
+
+    ConstantsAssumption a2 = new ConstantsAssumption();
+    a2.set(i, zero);
+    a2.set(j, one);
+
+    assertEquals("{i = 0, j = 1}", a1.join(a2).toString());
+  }
+
+  public void testJoin_WithEmpty() {
+    ConstantsAssumption a1 = new ConstantsAssumption();
+    a1.set(i, zero);
+
+    assertEquals("T", a1.join(new ConstantsAssumption()).toString());
+    assertEquals("T", new ConstantsAssumption().join(a1).toString());
+    assertEquals("T", a1.join(ConstantsAssumption.TOP).toString());
+    assertEquals("T", ConstantsAssumption.TOP.join(a1).toString());
+
+    assertEquals(a1, a1.join(null));
+  }
+
+  public void testJoin_DifferentValues() {
+    ConstantsAssumption a1 = new ConstantsAssumption();
+    a1.set(i, zero);
+    a1.set(j, one);
+
+    ConstantsAssumption a2 = new ConstantsAssumption();
+    a2.set(i, one);
+    a2.set(j, zero);
+
+    assertEquals("T", a1.join(a2).toString());
+  }
+
+  public void testJoin_DifferentKeys() {
+    ConstantsAssumption a1 = new ConstantsAssumption();
+    a1.set(i, zero);
+
+    ConstantsAssumption a2 = new ConstantsAssumption();
+    a2.set(j, zero);
+
+    assertEquals("T", a1.join(a2).toString());
+  }
+
+  public void testEquals_ComparesValues() {
+    ConstantsAssumption a1 = new ConstantsAssumption();
+    a1.set(i, newIntLiteral(0));
+
+    ConstantsAssumption a2 = new ConstantsAssumption();
+    a2.set(i, newIntLiteral(0));
+
+    assertTrue(a1.equals(a2));
+  }
+  
+  private JIntLiteral newIntLiteral(int value) {
+    return new JIntLiteral(null, value);
+  }
+
+  private JLocal newLocal(String name, JPrimitiveType type) {
+    return JProgram.createLocal(null, name, type, false, new JMethodBody(null));
+  }
+}