This patch updates the isAssignableFrom and isAssignableTo methods on the different TypeOracle types to complete their corresponding contracts.  For example, JTypeParameters were not enforcing the constraint of all of their bounds and JRealClassTypes were only considering types that appeared explicitly in its type hierachy.

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@1579 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JBound.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JBound.java
index b9f6141..36b8224 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JBound.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JBound.java
@@ -61,8 +61,10 @@
 
   abstract JClassType[] getSubtypes();
 
-  abstract boolean isAssignableFrom(JBound possibleSubWildcard);
-
+  abstract boolean isAssignableFrom(JClassType otherType);
+  
+  abstract boolean isAssignableTo(JClassType otherType);
+  
   private String toString(boolean useQualifiedNames) {
     StringBuffer sb = new StringBuffer();
 
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JClassType.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JClassType.java
index 359bc04..1f67de2 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JClassType.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JClassType.java
@@ -18,7 +18,9 @@
 import com.google.gwt.core.ext.UnableToCompleteException;
 
 import java.lang.annotation.Annotation;
+import java.util.HashSet;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * Type representing a Java class or interface type.
@@ -26,6 +28,51 @@
 public abstract class JClassType extends JType implements HasAnnotations,
     HasMetaData {
 
+  /**
+   * Returns all of the superclasses and superinterfaces for a given type
+   * including the type itself.
+   */
+  protected static Set<JClassType> getFlattenedSuperTypeHierarchy(
+      JClassType type) {
+    Set<JClassType> typesSeen = new HashSet<JClassType>();
+    getFlattenedSuperTypeHierarchyRecursive(type, typesSeen);
+    return typesSeen;
+  }
+
+  /**
+   * Returns the {@link JGenericType} base type if the otherType is raw or
+   * parameterized type.
+   */
+  protected static JClassType maybeGetGenericBaseType(JClassType otherType) {
+    if (otherType.isParameterized() != null) {
+      return otherType.isParameterized().getBaseType();
+    } else if (otherType.isRawType() != null) {
+      return otherType.isRawType().getGenericType();
+    }
+     
+    return otherType;
+  }
+
+  private static void getFlattenedSuperTypeHierarchyRecursive(JClassType type,
+      Set<JClassType> typesSeen) {
+    if (typesSeen.contains(type)) {
+      return;
+    }
+    typesSeen.add(type);
+
+    // Superclass
+    JClassType superclass = type.getSuperclass();
+    if (superclass != null) {
+      getFlattenedSuperTypeHierarchyRecursive(superclass, typesSeen);
+    }
+
+    // Check the interfaces
+    JClassType[] intfs = type.getImplementedInterfaces();
+    for (JClassType intf : intfs) {
+      getFlattenedSuperTypeHierarchyRecursive(intf, typesSeen);
+    }
+  }
+  
   public abstract void addImplementedInterface(JClassType intf);
 
   public abstract void addMetaData(String tagName, String[] values);
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JEnumType.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JEnumType.java
index c91f14b..6097392 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JEnumType.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JEnumType.java
@@ -59,8 +59,6 @@
         }
       }
 
-      assert (!enumConstants.isEmpty());
-
       lazyEnumConstants = enumConstants.toArray(new JEnumConstant[0]);
     }
 
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JGenericType.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JGenericType.java
index 533f074..02f60a5 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JGenericType.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JGenericType.java
@@ -17,12 +17,36 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Set;
 
 /**
  * 
  */
 public class JGenericType extends JRealClassType implements HasTypeParameters {
+  /**
+   * Returns <code>true</code> if lhsType is assignable to rhsType.
+   */
+  private static boolean isAssignable(JClassType lhsType, JClassType rhsType) {
+    if (lhsType == rhsType) {
+      return true;
+    }
+
+    Set<JClassType> supertypes = getFlattenedSuperTypeHierarchy(rhsType);
+    for (JClassType supertype : supertypes) {
+      if (supertype.isParameterized() == null) {
+        continue;
+      }
+
+      if (supertype.isParameterized().getBaseType() == lhsType) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
   private JRawType lazyRawType = null;
+
   private final List<JTypeParameter> typeParams = new ArrayList<JTypeParameter>();
 
   public JGenericType(TypeOracle oracle, CompilationUnitProvider cup,
@@ -48,7 +72,7 @@
       sb.append(getSimpleSourceName());
     } else {
       sb.append(getQualifiedSourceName());
-    }    
+    }
 
     sb.append('<');
     boolean needComma = false;
@@ -77,6 +101,20 @@
   }
 
   @Override
+  public boolean isAssignableFrom(JClassType otherType) {
+    otherType = maybeGetGenericBaseType(otherType);
+
+    return isAssignable(this, otherType);
+  }
+
+  @Override
+  public boolean isAssignableTo(JClassType otherType) {
+    otherType = maybeGetGenericBaseType(otherType);
+
+    return isAssignable(otherType, this);
+  }
+
+  @Override
   public JGenericType isGenericType() {
     return this;
   }
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JLowerBound.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JLowerBound.java
index 111bfa9..f4231ce 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JLowerBound.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JLowerBound.java
@@ -49,12 +49,38 @@
   }
 
   @Override
-  boolean isAssignableFrom(JBound possibleSubWildcard) {
-    JLowerBound lowerBound = possibleSubWildcard.isLowerBound();
-    if (lowerBound != null) {
-      return lowerBound.getFirstBound().isAssignableFrom(getFirstBound());
+  boolean isAssignableFrom(JClassType otherType) {
+    JWildcardType wildcard = otherType.isWildcard();
+    if (wildcard != null) {
+      JBound otherBounds = wildcard.getBounds();
+      if (otherBounds.isUpperBound() != null) {
+        // A lower bound cannot be assigned from an upper bound wildcard type.
+        return false;
+      }
+
+      otherType = otherBounds.getFirstBound();
     }
 
-    return false;
+    /*
+     * Only check the first bound since a lower bound cannot specify multiple
+     * types.
+     */
+    return getFirstBound().isAssignableTo(otherType);
+  }
+
+  @Override
+  boolean isAssignableTo(JClassType otherType) {
+    JWildcardType wildcard = otherType.isWildcard();
+    if (wildcard != null) {
+      JBound otherBounds = wildcard.getBounds();
+      if (otherBounds.isUpperBound() != null) {
+        // A lower bound cannot be assigned from an upper bound wildcard type.
+        return false;
+      }
+
+      otherType = otherBounds.getFirstBound();
+    }
+
+    return getFirstBound().isAssignableFrom(otherType);
   }
 }
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JParameterizedType.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JParameterizedType.java
index 2e1d342..3c27edf 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JParameterizedType.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JParameterizedType.java
@@ -17,7 +17,6 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.HashSet;
 import java.util.IdentityHashMap;
 import java.util.List;
 import java.util.Map;
@@ -35,6 +34,19 @@
       JClassType myTypeArg = myTypeArgs[i];
       JClassType otherTypeArg = otherTypeArgs[i];
 
+      if (myTypeArg.isTypeParameter() != null) {
+        /*
+         * If my type argument is a type parameter consider it's erased form.  This
+         * avoids recursion sickness in the case where my type argument is of the
+         * form:
+         * 
+         * T extends Serializable & Comparable<T>
+         * 
+         * and otherTypeArg is something like Long, Short, etc.
+         */
+        return myTypeArg.getErasedType().isAssignableFrom(otherTypeArg);
+      }
+      
       if (myTypeArg.isWildcard() == null) {
         // myTypeArg needs to be a wildcard or we cannot be assignable.
         return false;
@@ -58,7 +70,7 @@
     JClassType enclosingType = baseType.getEnclosingType();
     if (baseType.isMemberType() && !baseType.isStatic()) {
       // This base type is a non-static generic type so we build the necessary
-      // enclosing parameterized type and update the enclosing type to be 
+      // enclosing parameterized type and update the enclosing type to be
       // a parameterized type.
       JGenericType isGenericEnclosingType = enclosingType.isGenericType();
       if (isGenericEnclosingType != null) {
@@ -87,6 +99,77 @@
     return parameterizedType;
   }
 
+  /**
+   * Returns <code>true</code> if the rhsType can be assigned to the lhsType.
+   */
+  private static boolean isAssignable(JClassType lhsType, JClassType rhsType) {
+    if (lhsType == rhsType) {
+      return true;
+    }
+
+    Set<JClassType> rhsSupertypes = getFlattenedSuperTypeHierarchy(rhsType);
+    if (rhsSupertypes.contains(lhsType)) {
+      // Done, appears explicitly in the supertype hierarchy.
+      return true;
+    }
+
+    /*
+     * Get the generic base type for the lhsType if there is one.
+     */
+    JGenericType lhsBaseType = null;
+    if (lhsType.isParameterized() != null) {
+      lhsBaseType = lhsType.isParameterized().getBaseType();
+    } else if (lhsType.isRawType() != null) {
+      lhsBaseType = lhsType.isRawType().getBaseType();
+    }
+    
+    /*
+     * Check the supertype hierarchy to see if we can find a parameterization
+     * or a raw type that would satisfy the assignment. 
+     */
+    for (JClassType rhsSupertype : rhsSupertypes) {
+      if (rhsSupertype.isGenericType() != null) {
+        /*
+         * A generic type will be treated as its raw type for assignment
+         * purposes.
+         */
+        rhsSupertype = rhsSupertype.isGenericType().getRawType();
+      }
+
+      JParameterizedType rhsParameterized = rhsSupertype.isParameterized();
+      if (rhsParameterized != null) {
+        if (rhsParameterized.getBaseType() == lhsBaseType) {
+          /*
+           * This supertype and the lhsType have the same base type, but they
+           * have different parameterizations so we test them.  
+           */
+          assert (rhsParameterized != lhsType);
+
+          if (lhsType.isRawType() != null) {
+            // The lhsType is raw so we do not need to check the parameterization.
+            return true;
+          } else {
+            assert (lhsType.isParameterized() != null);
+            
+            return areTypeArgsAssignableFrom(
+                lhsType.isParameterized().getTypeArgs(),
+                rhsParameterized.getTypeArgs());
+          }
+        }
+      } else if (rhsSupertype.isRawType() != null) {
+        if (rhsSupertype.isRawType().getBaseType() == lhsBaseType) {
+          /*
+           * The raw supertype has the same base type as the lhsType so the
+           * assignment is okay.
+           */
+          return true;
+        }
+      }
+    }
+
+    return false;
+  }
+
   private final JClassType enclosingType;
 
   private List<JClassType> interfaces;
@@ -342,54 +425,12 @@
 
   @Override
   public boolean isAssignableFrom(JClassType otherType) {
-    Set<JClassType> typeHierarchy = getFlattenedSuperTypeHierarchy(otherType);
-    if (typeHierarchy.contains(this)) {
-      // Done, appear explicitly in the supertype hierarchy
-      return true;
-    }
-
-    // Check for implicit subtype
-    for (JClassType type : typeHierarchy) {
-      JGenericType isGeneric = type.isGenericType();
-      if (isGeneric != null) {
-        // TODO: Do we want to treat a generic type as a raw type?
-        type = isGeneric.getRawType();
-      }
-
-      JParameterizedType isParameterized = type.isParameterized();
-      if (isParameterized != null
-          && isParameterized.getBaseType() == getBaseType()) {
-        // parameters are not exact, need to test type arguments
-        assert (isParameterized != this);
-
-        return areTypeArgsAssignableFrom(this.getTypeArgs(),
-            isParameterized.getTypeArgs());
-      }
-
-      JRawType isRaw = type.isRawType();
-      if (isRaw != null && isRaw.getBaseType() == getBaseType()) {
-        // Parameterized types are assignable from their raw types
-        return true;
-      }
-
-      JWildcardType isWildcard = type.isWildcard();
-      if (isWildcard != null && isWildcard.isAssignableTo(this)) {
-        return true;
-      }
-    }
-
-    return false;
+    return isAssignable(this, otherType);
   }
 
   @Override
-  public boolean isAssignableTo(JClassType possibleSupertype) {
-    JGenericType genericPossibleSupertype = possibleSupertype.isGenericType();
-    if (genericPossibleSupertype != null) {
-      // Checks for assignability to the generic type's raw type
-      return genericPossibleSupertype.getRawType().isAssignableFrom(this);
-    }
-
-    return possibleSupertype.isAssignableFrom(this);
+  public boolean isAssignableTo(JClassType otherType) {
+    return isAssignable(otherType, this);
   }
 
   @Override
@@ -607,33 +648,4 @@
     // Legal substitution can be made and is record in substitutions.
     return substitutions;
   }
-
-  /**
-   * Returns the flattened view of the supertype hierarchy.
-   */
-  private Set<JClassType> getFlattenedSuperTypeHierarchy(JClassType type) {
-    Set<JClassType> typesSeen = new HashSet<JClassType>();
-    getFlattenedSuperTypeHierarchyRecursive(type, typesSeen);
-    return typesSeen;
-  }
-
-  private void getFlattenedSuperTypeHierarchyRecursive(JClassType type,
-      Set<JClassType> typesSeen) {
-    if (typesSeen.contains(type)) {
-      return;
-    }
-    typesSeen.add(type);
-
-    // Superclass
-    JClassType superclass = type.getSuperclass();
-    if (superclass != null) {
-      getFlattenedSuperTypeHierarchyRecursive(superclass, typesSeen);
-    }
-
-    // Check the interfaces
-    JClassType[] intfs = type.getImplementedInterfaces();
-    for (JClassType intf : intfs) {
-      getFlattenedSuperTypeHierarchyRecursive(intf, typesSeen);
-    }
-  }
 }
\ No newline at end of file
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JRawType.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JRawType.java
index 2d207e2..63ba139 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JRawType.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JRawType.java
@@ -28,20 +28,6 @@
     }
   };
 
-  private static JClassType normalizeType(JClassType type) {
-    JRawType isRawType = type.isRawType();
-    if (isRawType != null) {
-      return isRawType.getGenericType();
-    }
-
-    JParameterizedType isParameterized = type.isParameterized();
-    if (isParameterized != null) {
-      return isParameterized.getBaseType();
-    }
-
-    return type;
-  }
-
   private final JClassType enclosingType;
 
   private List<JClassType> interfaces;
@@ -195,15 +181,17 @@
   }
 
   @Override
-  public boolean isAssignableFrom(JClassType possibleSubtype) {
-    JClassType type = normalizeType(possibleSubtype);
-    return getBaseType().isAssignableFrom(type);
+  public boolean isAssignableFrom(JClassType otherType) {
+    otherType = maybeGetGenericBaseType(otherType);
+    
+    return getBaseType().isAssignableFrom(otherType);
   }
 
   @Override
-  public boolean isAssignableTo(JClassType possibleSupertype) {
-    JClassType type = normalizeType(possibleSupertype);
-    return getBaseType().isAssignableTo(type);
+  public boolean isAssignableTo(JClassType otherType) {
+    otherType = maybeGetGenericBaseType(otherType);
+    
+    return getBaseType().isAssignableTo(otherType);
   }
 
   @Override
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JRealClassType.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JRealClassType.java
index 74da5ea..f68e36a 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JRealClassType.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JRealClassType.java
@@ -324,11 +324,14 @@
     return null;
   }
 
-  public boolean isAssignableFrom(JClassType possibleSubtype) {
-    if (possibleSubtype == this) {
+  @Override
+  public boolean isAssignableFrom(JClassType otherType) {
+    if (otherType == this) {
       return true;
     }
-    if (allSubtypes.contains(possibleSubtype)) {
+        
+    if (allSubtypes.contains(otherType)) {
+      // JGenericTypes should appear in the allSubtypes hierarchy - do nothing
       return true;
     } else if (this == getOracle().getJavaLangObject()) {
       // This case handles the odd "every interface is an Object"
@@ -336,6 +339,28 @@
       //
       return true;
     } else {
+      if (otherType.isTypeParameter() != null) {
+        return otherType.isAssignableTo(this);
+      }
+      
+      if (otherType.isWildcard() != null) {
+        return otherType.isAssignableTo(this);
+      }
+
+      if (otherType.isGenericType() != null) {
+        return otherType.isAssignableTo(this);
+      }
+      
+      if (otherType.isRawType() != null) {
+        return otherType.isAssignableTo(this);
+      }
+      
+      if (otherType.isParameterized() != null) {
+        return otherType.isAssignableTo(this);
+      }
+
+      // At this point we should only have JArrayTypes or JRealClassTypes from
+      // which we are not assignable.
       return false;
     }
   }
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JTypeParameter.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JTypeParameter.java
index 58cb21d..2256e6c 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JTypeParameter.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JTypeParameter.java
@@ -113,26 +113,41 @@
   public JClassType[] getSubtypes() {
     JClassType[] subtypes = super.getSubtypes();
     List<JClassType> intersectionTypes = new ArrayList<JClassType>();
+    
+    if (getFirstBound().isInterface() == null && isAssignableFrom(getFirstBound())) {
+      // Include the first bound as a subtype if it is not an interface and it
+      // is assignable to all of our bounds.
+      intersectionTypes.add(getFirstBound());
+    }
+    
     for (JClassType subtype : subtypes) {
       if (isAssignableFrom(subtype)) {
         intersectionTypes.add(subtype);
       }
     }
+    
+    // Only types that intersect with all our bounds make it here. 
     return intersectionTypes.toArray(TypeOracle.NO_JCLASSES);
   }
 
   @Override
-  public boolean isAssignableFrom(JClassType possibleSubtype) {
-    // TODO: Should this compute an intersection?
-    return getFirstBound().isAssignableFrom(possibleSubtype);
+  public boolean isAssignableFrom(JClassType otherType) {
+    if (otherType == this) {
+      return true;
+    }
+    
+    return getBounds().isAssignableFrom(otherType);
   }
 
   @Override
-  public boolean isAssignableTo(JClassType possibleSupertype) {
-    // TODO: Should this compute an intersection?
-    return getFirstBound().isAssignableTo(possibleSupertype);
+  public boolean isAssignableTo(JClassType otherType) {
+    if (otherType == this) {
+      return true;
+    }
+    
+    return getBounds().isAssignableTo(otherType);
   }
-
+  
   @Override
   public JGenericType isGenericType() {
     return null;
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JUpperBound.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JUpperBound.java
index 3762cd9..2fbf9cf 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JUpperBound.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JUpperBound.java
@@ -47,18 +47,45 @@
   }
 
   @Override
-  boolean isAssignableFrom(JBound possibleSubWildcard) {
-    JClassType firstBound = getFirstBound();
+  boolean isAssignableFrom(JClassType otherType) {
+    JWildcardType wildcard = otherType.isWildcard();
+    if (wildcard != null) {
+      if (wildcard.getBounds().isLowerBound() != null) {
+        /*
+         * Upper bounds can be assigned from lower bounds if both of their
+         * bounding types are Object.
+         */
+        JClassType firstBound = getFirstBound();
+        JClassType javaLangObject = firstBound.getOracle().getJavaLangObject();
+        return firstBound == javaLangObject && wildcard.getFirstBound() == javaLangObject;
+      }
 
-    JUpperBound upperBound = possibleSubWildcard.isUpperBound();
-    if (upperBound != null) {
-      // Upper bound
-      return firstBound.isAssignableFrom(upperBound.getFirstBound());
+      return getFirstBound().isAssignableFrom(wildcard.getFirstBound());
+    } else {
+      /*
+       * This bound can be assigned from another type if each of the bounding
+       * types is assignable from the other type.
+       */
+      JClassType[] bounds = getBounds();
+      for (JClassType bound : bounds) {
+        if (!bound.isAssignableFrom(otherType)) {
+          return false;
+        }
+      }
     }
-
-    // Lower bound
-    JClassType javaLangObject = firstBound.getOracle().getJavaLangObject();
-    return firstBound == javaLangObject
-        && possibleSubWildcard.getFirstBound() == javaLangObject;
+    
+    return true;
+  }
+  
+  @Override 
+  boolean isAssignableTo(JClassType otherType) {
+    JClassType[] bounds = getBounds();
+    for (JClassType bound : bounds) {
+      if (bound.isAssignableTo(otherType)) {
+        return true;
+      }
+    }
+    
+    return false;
   }
 }
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JWildcardType.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JWildcardType.java
index e76ac2e..20b8e53 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JWildcardType.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JWildcardType.java
@@ -81,20 +81,21 @@
   }
 
   @Override
-  public boolean isAssignableFrom(JClassType possibleSubtype) {
-    JWildcardType possibleSubWildcard = possibleSubtype.isWildcard();
-    if (possibleSubWildcard != null) {
-      return getBounds().isAssignableFrom(possibleSubWildcard.getBounds());
+  public boolean isAssignableFrom(JClassType otherType) {
+    if (otherType == this) {
+      return true;
     }
-
-    return getBaseType().isAssignableFrom(possibleSubtype);
+    
+    return getBounds().isAssignableFrom(otherType);
   }
 
   @Override
   public boolean isAssignableTo(JClassType otherType) {
-    // TODO: This need to handle all possible subtypes of JClassType that could
-    // reach here...
-    return super.isAssignableTo(otherType);
+    if (otherType == this) {
+      return true;
+    }
+    
+    return getBounds().isAssignableTo(otherType);
   }
 
   @Override
diff --git a/dev/core/test/com/google/gwt/core/ext/typeinfo/JParameterizedTypeTest.java b/dev/core/test/com/google/gwt/core/ext/typeinfo/JParameterizedTypeTest.java
index 466ff74..ca91c61 100644
--- a/dev/core/test/com/google/gwt/core/ext/typeinfo/JParameterizedTypeTest.java
+++ b/dev/core/test/com/google/gwt/core/ext/typeinfo/JParameterizedTypeTest.java
@@ -239,7 +239,10 @@
     JParameterizedType numUpperBoundList = oracle.getParameterizedType(
         genericList, new JClassType[] {numUpperBoundWildcard});
     assertTrue(unboundList.isAssignableFrom(numUpperBoundList));
+    assertFalse(unboundList.isAssignableTo(numUpperBoundList));
+
     assertFalse(numUpperBoundList.isAssignableFrom(unboundList));
+    assertTrue(numUpperBoundList.isAssignableTo(unboundList));
 
     // List<? extends Number> should be assignable from List<? extends Integer>
     JWildcardType intUpperBoundWildcard = oracle.getWildcardType(new JUpperBound(
@@ -248,7 +251,10 @@
     JParameterizedType intUpperBoundList = oracle.getParameterizedType(
         genericList, new JClassType[] {intUpperBoundWildcard});
     assertTrue(numUpperBoundList.isAssignableFrom(intUpperBoundList));
+    assertFalse(numUpperBoundList.isAssignableTo(intUpperBoundList));
+
     assertFalse(intUpperBoundList.isAssignableFrom(numUpperBoundList));
+    assertTrue(intUpperBoundList.isAssignableTo(numUpperBoundList));
 
     // List<? super Integer> should be assignable from List<? super Number>
     JWildcardType numLowerBoundWildcard = oracle.getWildcardType(new JLowerBound(
@@ -262,16 +268,9 @@
         genericList, new JClassType[] {intLowerBoundWildcard});
 
     assertTrue(intLowerBoundList.isAssignableFrom(numLowerBoundList));
+    assertFalse(intLowerBoundList.isAssignableTo(numLowerBoundList));
     assertFalse(numLowerBoundList.isAssignableFrom(intLowerBoundList));
-  }
-
-  /**
-   * Test method for
-   * {@link com.google.gwt.core.ext.typeinfo.JParameterizedType#isAssignableTo(JClassType)}.
-   */
-  @Override
-  public void testIsAssignableTo() {
-    // TODO:
+    assertTrue(numLowerBoundList.isAssignableTo(intLowerBoundList));
   }
 
   public void testOverridableMethods_Base() throws NotFoundException {
@@ -397,4 +396,9 @@
 
     assertTrue(expectedMethods.isEmpty());
   }
+
+  @Override
+  public void testIsAssignableTo() throws NotFoundException {
+    // This is covered as part of testIsAssignableFrom
+  }
 }
diff --git a/dev/core/test/com/google/gwt/core/ext/typeinfo/JTypeParameterTest.java b/dev/core/test/com/google/gwt/core/ext/typeinfo/JTypeParameterTest.java
index 59e2d5c..28aa3d8 100644
--- a/dev/core/test/com/google/gwt/core/ext/typeinfo/JTypeParameterTest.java
+++ b/dev/core/test/com/google/gwt/core/ext/typeinfo/JTypeParameterTest.java
@@ -21,7 +21,13 @@
 import com.google.gwt.core.ext.typeinfo.test.MyCustomList;
 import com.google.gwt.dev.util.log.PrintWriterTreeLogger;
 
+import java.io.Serializable;
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
 
 /**
  * Tests for {@link JTypeParameter}.
@@ -80,28 +86,34 @@
         testType.getFirstBound().getOverridableMethods()));
   }
 
+  /*
+   * Checks that all non-local subtypes of the type parameter,
+   * T extends Serializable & Comparable<T> are actually assignable to 
+   * Serializable and the properly parameterized version of Comparable<T>.
+   */
   @Override
   public void testGetSubtypes() throws NotFoundException {
+    
     TypeOracle oracle = moduleContext.getOracle();
     JClassType testType = oracle.getType(MyCustomList.class.getName());
     JGenericType genericType = testType.isGenericType();
     JTypeParameter[] typeParameters = genericType.getTypeParameters();
     JTypeParameter typeParameter = typeParameters[0];
 
-    JClassType[] expected = new JClassType[] {
-    /*
-     * TODO: Re-eneable this once java.io.Serializable is added to the JRE
-     * 
-     * emulation classes oracle.getType(Integer.class.getName()),
-     * oracle.getType(Float.class.getName()),
-     * oracle.getType(Short.class.getName()),
-     * oracle.getType(Double.class.getName()),
-     * oracle.getType(Number.class.getName()),
-     * oracle.getType(Long.class.getName()),
-     * oracle.getType(Byte.class.getName()),
-     */
-    };
-    validateEquals(oracle, expected, typeParameter.getSubtypes());
+    JClassType serializableType = oracle.getType(Serializable.class.getCanonicalName());
+    JGenericType comparableType = (JGenericType) oracle.getType(Comparable.class.getCanonicalName());
+    JClassType[] computedSubtypes = typeParameter.getSubtypes();
+    
+    for (JClassType computedSubtype : computedSubtypes) {
+      JParameterizedType parameterizedComparableType = oracle.getParameterizedType(
+          comparableType, new JClassType[] {computedSubtype});
+      if (computedSubtype.isLocalType()) {
+        // Ignore local types.
+        continue;
+      }
+      assertTrue(computedSubtype.isAssignableTo(serializableType));
+      assertTrue(computedSubtype.isAssignableTo(parameterizedComparableType));
+    }
   }
 
   @Override
diff --git a/dev/core/test/com/google/gwt/core/ext/typeinfo/JWildcardTypeTest.java b/dev/core/test/com/google/gwt/core/ext/typeinfo/JWildcardTypeTest.java
index 652e3e1..367a39d 100644
--- a/dev/core/test/com/google/gwt/core/ext/typeinfo/JWildcardTypeTest.java
+++ b/dev/core/test/com/google/gwt/core/ext/typeinfo/JWildcardTypeTest.java
@@ -57,6 +57,10 @@
     // Wildcard do not have nested types...
   }
 
+  public void testGetMethods() throws NotFoundException {
+    super.testGetMethods();
+  }
+
   @Override
   public void testGetNestedType() {
     // No nested types
@@ -167,7 +171,9 @@
     JWildcardType integerWildcard = oracle.getWildcardType(integerBound);
 
     assertFalse(numberWildcard.isAssignableFrom(integerWildcard));
+    assertTrue(numberWildcard.isAssignableTo(integerWildcard));
     assertTrue(integerWildcard.isAssignableFrom(numberWildcard));
+    assertFalse(integerWildcard.isAssignableTo(numberWildcard));
   }
 
   /**
@@ -186,12 +192,13 @@
     JWildcardType numberWildcard = oracle.getWildcardType(numberBound);
     JWildcardType integerWildcard = oracle.getWildcardType(integerBound);
 
-    assertFalse(numberWildcard.isAssignableTo(integerWildcard));
-    assertTrue(integerWildcard.isAssignableTo(numberWildcard));
+    assertTrue(numberWildcard.isAssignableTo(integerWildcard));
+    assertFalse(integerWildcard.isAssignableTo(numberWildcard));
   }
 
   @Override
   public void testIsAssignableTo() {
+    // NOTE These cases were tested as part of testIsAssignableFrom.
   }
 
   /**
@@ -214,6 +221,26 @@
     assertFalse(numberWildcard.isAssignableTo(integerWildcard));
   }
 
+  /**
+   * Tests that <? super Number> is assignable to <? super Integer> and that
+   * the reverse is not <code>true</code>.
+   */
+  public void testIsAssignableTo_Super_Number_To_Super_Integer()
+      throws NotFoundException {
+    TypeOracle oracle = moduleContext.getOracle();
+    JClassType numberType = oracle.getType(Number.class.getName());
+    JClassType integerType = oracle.getType(Integer.class.getName());
+
+    JLowerBound numberBound = new JLowerBound(numberType);
+    JLowerBound integerBound = new JLowerBound(integerType);
+
+    JWildcardType numberWildcard = oracle.getWildcardType(numberBound);
+    JWildcardType integerWildcard = oracle.getWildcardType(integerBound);
+
+    assertFalse(integerWildcard.isAssignableTo(numberWildcard));
+    assertTrue(numberWildcard.isAssignableTo(integerWildcard));
+  }
+
   @Override
   protected Substitution getSubstitution() {
     return new Substitution() {
@@ -223,10 +250,6 @@
     };
   }
 
-  public void testGetMethods() throws NotFoundException {
-    super.testGetMethods();
-  }
-
   @Override
   protected JWildcardType getTestType() throws NotFoundException {
     TypeOracle oracle = moduleContext.getOracle();