TypeOracle now builds from bytecode instead of JDT structures (part of Instant Hosted Mode work).

Patch by: jat, me
Review by: me, amitmanjhi, jat

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@6784 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JAbstractMethod.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JAbstractMethod.java
index 0a80f52..2e46788 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JAbstractMethod.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JAbstractMethod.java
@@ -42,6 +42,8 @@
 
   private List<JTypeParameter> typeParams = Lists.create();
 
+  private String[] realParameterNames = null;
+
   JAbstractMethod(JAbstractMethod srcMethod) {
     this.annotations = new Annotations(srcMethod.annotations);
     this.isVarArgs = srcMethod.isVarArgs;
@@ -114,6 +116,7 @@
   }
 
   public JParameter[] getParameters() {
+    // TODO(jat): where do we handle fake arg names?
     return params.toArray(TypeOracle.NO_JPARAMS);
   }
 
@@ -236,6 +239,22 @@
     return annotations.getDeclaredAnnotations();
   }
 
+  // Called only by a JParameter, passing itself as a reference for lookup.
+  String getRealParameterName(JParameter parameter) {
+    if (realParameterNames == null) {
+      fetchRealParameterNames();
+    }
+    int n = params.size();
+    for (int i = 0; i < n; ++i) {
+      // Identity tests are ok since identity is durable within an oracle.
+      if (params.get(i) == parameter) {
+        return realParameterNames == null ? "arg" + i : realParameterNames[i];
+      }
+    }
+    // TODO: report error if we are asked for an unknown JParameter?
+    return null;
+  }
+
   boolean hasParamTypes(JType[] paramTypes) {
     if (params.size() != paramTypes.length) {
       return false;
@@ -251,4 +270,8 @@
     }
     return true;
   }
+
+  private void fetchRealParameterNames() {
+    realParameterNames = getEnclosingType().getOracle().getJavaSourceParser().getArguments(this);
+  }
 }
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JAnnotationType.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JAnnotationType.java
index d61f061..2063d58 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JAnnotationType.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JAnnotationType.java
@@ -23,9 +23,9 @@
 public class JAnnotationType extends JRealClassType {
 
   public JAnnotationType(TypeOracle oracle, JPackage declaringPackage,
-      JClassType enclosingType, boolean isLocalType, String name,
+      String enclosingTypeName, boolean isLocalType, String name,
       boolean isInterface) {
-    super(oracle, declaringPackage, enclosingType, isLocalType, name,
+    super(oracle, declaringPackage, enclosingTypeName, isLocalType, name,
         isInterface);
   }
 
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JArrayType.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JArrayType.java
index 6972d61..aca74a7 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JArrayType.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JArrayType.java
@@ -26,6 +26,8 @@
 
   private JType componentType;
 
+  private String lazyQualifiedBinaryName;
+
   private String lazyQualifiedName;
 
   private String lazySimpleName;
@@ -183,6 +185,14 @@
   }
 
   @Override
+  public String getQualifiedBinaryName() {
+    if (lazyQualifiedBinaryName == null) {
+      lazyQualifiedBinaryName = "[" + getComponentType().getQualifiedBinaryName();
+    }
+    return lazyQualifiedBinaryName;
+  }
+  
+  @Override
   public String getQualifiedSourceName() {
     if (lazyQualifiedName == null) {
       lazyQualifiedName = getComponentType().getQualifiedSourceName() + "[]";
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 c8b112e..5090d92 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
@@ -101,7 +101,7 @@
    * 
    * @param lhsType
    * @param rhsType
-   * @return
+   * @return true if rhsType can be assigned to lhsType
    */
   private static boolean areClassTypesAssignableNoSupers(JClassType lhsType,
       JClassType rhsType) {
@@ -620,15 +620,6 @@
     return null;
   }
 
-  protected final String makeCompoundName(JClassType type) {
-    if (type.getEnclosingType() == null) {
-      return type.getSimpleSourceName();
-    } else {
-      return makeCompoundName(type.getEnclosingType()) + "."
-          + type.getSimpleSourceName();
-    }
-  }
-
   /**
    * Tells this type's superclasses and superinterfaces about it.
    */
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 76f7fc4..347b534 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
@@ -25,9 +25,9 @@
   private JEnumConstant[] lazyEnumConstants;
 
   public JEnumType(TypeOracle oracle, JPackage declaringPackage,
-      JClassType enclosingType, boolean isLocalType, String name,
+      String enclosingTypeName, boolean isLocalType, String name,
       boolean isInterface) {
-    super(oracle, declaringPackage, enclosingType, isLocalType, name,
+    super(oracle, declaringPackage, enclosingTypeName, isLocalType, name,
         isInterface);
   }
 
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 18f6681..c08cc93 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
@@ -30,9 +30,9 @@
   private List<JTypeParameter> typeParams = Lists.create();
 
   public JGenericType(TypeOracle oracle, JPackage declaringPackage,
-      JClassType enclosingType, boolean isLocalType, String name,
+      String enclosingTypeName, boolean isLocalType, String name,
       boolean isInterface, JTypeParameter[] jtypeParameters) {
-    super(oracle, declaringPackage, enclosingType, isLocalType, name,
+    super(oracle, declaringPackage, enclosingTypeName, isLocalType, name,
         isInterface);
 
     if (jtypeParameters != null) {
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JParameter.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JParameter.java
index b13db90..df15ce1 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JParameter.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JParameter.java
@@ -26,11 +26,13 @@
 
   private final Annotations annotations;
 
-  private final String name;
+  private String name;
 
   private JType type;
 
   private final JAbstractMethod enclosingMethod;
+
+  private boolean argNameIsReal;
   
   public JParameter(JAbstractMethod enclosingMethod, JType type,
       String name) {
@@ -39,9 +41,16 @@
 
   public JParameter(JAbstractMethod enclosingMethod, JType type, String name,
       Map<Class<? extends Annotation>, Annotation> declaredAnnotations) {
+    this(enclosingMethod, type, name, declaredAnnotations, true);
+  }
+
+  public JParameter(JAbstractMethod enclosingMethod, JType type, String name,
+      Map<Class<? extends Annotation>, Annotation> declaredAnnotations,
+      boolean argNameIsReal) {
     this.enclosingMethod = enclosingMethod;
     this.type = type;
     this.name = name;
+    this.argNameIsReal = argNameIsReal;
 
     enclosingMethod.addParameter(this);
 
@@ -79,6 +88,10 @@
   }
 
   public String getName() {
+    if (!argNameIsReal) {
+      name = enclosingMethod.getRealParameterName(this);
+      argNameIsReal = true;
+    }
     return name;
   }
 
@@ -90,6 +103,7 @@
     return annotations.isAnnotationPresent(annotationClass);
   }
 
+  @Override
   public String toString() {
     StringBuffer sb = new StringBuffer();
     sb.append(type.getParameterizedQualifiedSourceName());
@@ -112,6 +126,11 @@
     return annotations.getDeclaredAnnotations();
   }
 
+  // Only called by JAbstractMethod after real parameter names are fetched.
+  void setName(String name) {
+    this.name = name;
+  }
+
   // Called when parameter types are found to be parameterized
   void setType(JType type) {
     this.type = type;
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 b823d7a..1eaf518 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
@@ -99,7 +99,8 @@
     });
 
     this.typeArgs = Lists.create(typeArgs);
-    assert (this.typeArgs.indexOf(null) == -1);
+    assert (this.typeArgs.indexOf(null) == -1)
+        : "Unresolved typeArg creating JParameterizedType from " + baseType;
 
     // NOTE: Can't perform substitutions until we are done building
   }
@@ -238,6 +239,11 @@
     return sb.toString();
   }
 
+  @Override
+  public String getQualifiedBinaryName() {
+    return getBaseType().getQualifiedBinaryName();
+  }
+
   /**
    * Everything is fully qualified and includes the &lt; and &gt; in the
    * signature.
@@ -427,7 +433,7 @@
   /**
    * Initialize a map of substitutions for {@link JTypeParameter}s to
    * corresponding {@link JClassType}s. This can only be initialized after the
-   * {@link com.google.gwt.dev.jdt.TypeOracleBuilder TypeOracleBuilder} has
+   * {@link com.google.gwt.dev.javac.TypeOracleMediator TypeOracleMediator} has
    * fully resolved all of the {@link JClassType}s.
    */
   void maybeInitializeTypeParameterSubstitutionMap() {
@@ -531,4 +537,4 @@
     // Legal substitution can be made and is record in substitutions.
     return substitutions;
   }
-}
\ No newline at end of file
+}
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JPrimitiveType.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JPrimitiveType.java
index 34582b5..38fed10 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JPrimitiveType.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JPrimitiveType.java
@@ -82,6 +82,11 @@
     return jni;
   }
 
+  @Override
+  public String getQualifiedBinaryName() {
+    return name;
+  }
+
   public String getQualifiedBoxedSourceName() {
     return "java.lang." + boxedName;
   }
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 9b6ea87..b0d40ac 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
@@ -136,6 +136,11 @@
   }
 
   @Override
+  public String getQualifiedBinaryName() {
+    return getBaseType().getQualifiedBinaryName();
+  }
+
+  @Override
   public String getQualifiedSourceName() {
     return getBaseType().getQualifiedSourceName();
   }
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 b492b98..2a7e990 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
@@ -34,7 +34,10 @@
 
   private final JPackage declaringPackage;
 
-  private final JClassType enclosingType;
+  /**
+   * Set when this class is resolved, then never modified.
+   */
+  private JClassType enclosingType;
 
   private List<JClassType> interfaces = Lists.create();
 
@@ -42,6 +45,8 @@
 
   private final boolean isLocalType;
 
+  private String lazyQualifiedBinaryName;
+
   private String lazyQualifiedName;
 
   private final Members members = new Members(this);
@@ -56,16 +61,27 @@
 
   private JClassType superclass;
 
+  /**
+   * Create a class type that reflects an actual type.
+   * 
+   * @param oracle
+   * @param declaringPackage
+   * @param enclosingTypeName the fully qualified source name of the enclosing
+   *          class or null if a top-level class - setEnclosingType must be
+   *          called later with the proper enclosing type if this is non-null
+   * @param isLocalType
+   * @param name
+   * @param isInterface
+   */
   public JRealClassType(TypeOracle oracle, JPackage declaringPackage,
-      JClassType enclosingType, boolean isLocalType, String name,
+      String enclosingTypeName, boolean isLocalType, String name,
       boolean isInterface) {
     this.oracle = oracle;
     this.declaringPackage = declaringPackage;
-    this.enclosingType = enclosingType;
     this.isLocalType = isLocalType;
     this.name = name;
     this.isInterface = isInterface;
-    if (enclosingType == null) {
+    if (enclosingTypeName == null) {
       // Add myself to my package.
       //
       declaringPackage.addType(this);
@@ -73,18 +89,12 @@
       //
       nestedName = name;
     } else {
-      // Add myself to my enclosing type.
-      //
-      enclosingType.addNestedType(this);
       // Compute my "nested name".
       //
-      JClassType enclosing = enclosingType;
-      String nn = name;
-      do {
-        nn = enclosing.getSimpleSourceName() + "." + nn;
-        enclosing = enclosing.getEnclosingType();
-      } while (enclosing != null);
-      nestedName = nn;
+      nestedName = enclosingTypeName + "." + name;
+
+      // We will add ourselves to the enclosing class when it is set in
+      // setEnclosingType().
     }
     oracle.addNewType(this);
   }
@@ -94,11 +104,13 @@
     annotations.addAnnotations(declaredAnnotations);
   }
 
+  @Override
   public void addImplementedInterface(JClassType intf) {
     assert (intf != null);
     interfaces = Lists.add(interfaces, intf);
   }
 
+  @Override
   public void addModifierBits(int bits) {
     modifierBits |= bits;
   }
@@ -123,6 +135,7 @@
     return members.findNestedType(typeName);
   }
 
+  @Override
   public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
     return annotations.getAnnotation(annotationClass);
   }
@@ -138,6 +151,7 @@
     return members.getConstructors();
   }
 
+  @Override
   public JClassType getEnclosingType() {
     return enclosingType;
   }
@@ -157,6 +171,7 @@
     return members.getFields();
   }
 
+  @Override
   public JClassType[] getImplementedInterfaces() {
     return interfaces.toArray(TypeOracle.NO_JCLASSES);
   }
@@ -182,6 +197,7 @@
     return members.getMethods();
   }
 
+  @Override
   public String getName() {
     return nestedName;
   }
@@ -196,6 +212,7 @@
     return members.getNestedTypes();
   }
 
+  @Override
   public TypeOracle getOracle() {
     return oracle;
   }
@@ -210,18 +227,32 @@
     return members.getOverridableMethods();
   }
 
+  @Override
   public JPackage getPackage() {
     return declaringPackage;
   }
 
   @Override
+  public String getQualifiedBinaryName() {
+    if (lazyQualifiedBinaryName == null) {
+      lazyQualifiedBinaryName = "";
+      JPackage pkg = getPackage();
+      if (!pkg.isDefault()) {
+        lazyQualifiedBinaryName = pkg.getName() + ".";
+      }
+      lazyQualifiedBinaryName += nestedName.replace('.', '$');
+    }
+    return lazyQualifiedBinaryName;
+  }
+
+  @Override
   public String getQualifiedSourceName() {
     if (lazyQualifiedName == null) {
       JPackage pkg = getPackage();
       if (!pkg.isDefault()) {
-        lazyQualifiedName = pkg.getName() + "." + makeCompoundName(this);
+        lazyQualifiedName = pkg.getName() + "." + nestedName;
       } else {
-        lazyQualifiedName = makeCompoundName(this);
+        lazyQualifiedName = nestedName;
       }
     }
     return lazyQualifiedName;
@@ -232,10 +263,12 @@
     return name;
   }
 
+  @Override
   public JClassType[] getSubtypes() {
     return allSubtypes.toArray(TypeOracle.NO_JCLASSES);
   }
 
+  @Override
   public JClassType getSuperclass() {
     return superclass;
   }
@@ -247,10 +280,12 @@
     oracle.invalidate(this);
   }
 
+  @Override
   public boolean isAbstract() {
     return 0 != (modifierBits & TypeOracle.MOD_ABSTRACT);
   }
 
+  @Override
   public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
     return annotations.isAnnotationPresent(annotationClass);
   }
@@ -278,6 +313,7 @@
    * @return <code>true</code> if the type is default instantiable, or
    *         <code>false</code> otherwise
    */
+  @Override
   public boolean isDefaultInstantiable() {
     if (isInterface() != null || isAbstract()) {
       return false;
@@ -324,6 +360,7 @@
    * @return true if this type is a local type, whether it is named or
    *         anonymous.
    */
+  @Override
   public boolean isLocalType() {
     return isLocalType;
   }
@@ -334,6 +371,7 @@
    * @return true if this type has an enclosing type, false if this type is a
    *         top-level type
    */
+  @Override
   public boolean isMemberType() {
     return enclosingType != null;
   }
@@ -350,14 +388,17 @@
     return null;
   }
 
+  @Override
   public boolean isPrivate() {
     return 0 != (modifierBits & TypeOracle.MOD_PRIVATE);
   }
 
+  @Override
   public boolean isProtected() {
     return 0 != (modifierBits & TypeOracle.MOD_PROTECTED);
   }
 
+  @Override
   public boolean isPublic() {
     return 0 != (modifierBits & TypeOracle.MOD_PUBLIC);
   }
@@ -368,6 +409,7 @@
     return null;
   }
 
+  @Override
   public boolean isStatic() {
     return 0 != (modifierBits & TypeOracle.MOD_STATIC);
   }
@@ -384,6 +426,27 @@
     oracle.resurrect(this);
   }
 
+  /**
+   * INTERNAL METHOD -- this should only be called by TypeOracleMediator.
+   * 
+   * TODO: reduce visibility.
+   * 
+   * @param enclosingType
+   */
+  public void setEnclosingType(JClassType enclosingType) {
+    assert this.enclosingType == null;
+    assert enclosingType != null;
+
+    this.enclosingType = enclosingType;
+
+    // Add myself to my enclosing type.
+    JRawType rawType = enclosingType.isRawType();
+    if (rawType != null) {
+      enclosingType = rawType.getGenericType();
+    }
+    enclosingType.addNestedType(this);
+  }
+
   @Override
   public void setSuperclass(JClassType type) {
     assert (type != null);
@@ -411,6 +474,7 @@
     }
   }
 
+  @Override
   protected void acceptSubtype(JClassType me) {
     allSubtypes = IdentitySets.add(allSubtypes, me);
     notifySuperTypesOf(me);
@@ -441,10 +505,12 @@
     return members.findNestedTypeImpl(typeName, index);
   }
 
+  @Override
   protected int getModifierBits() {
     return modifierBits;
   }
 
+  @Override
   protected void getOverridableMethodsOnSuperclassesAndThisClass(
       Map<String, JMethod> methodsBySignature) {
     members.getOverridableMethodsOnSuperclassesAndThisClass(methodsBySignature);
@@ -458,6 +524,7 @@
    * 
    * @param methodsBySignature
    */
+  @Override
   protected void getOverridableMethodsOnSuperinterfacesAndMaybeThisInterface(
       Map<String, JMethod> methodsBySignature) {
     members.getOverridableMethodsOnSuperinterfacesAndMaybeThisInterface(methodsBySignature);
@@ -466,6 +533,7 @@
   /**
    * Tells this type's superclasses and superinterfaces about it.
    */
+  @Override
   protected void notifySuperTypesOf(JClassType me) {
     // TODO(scottb): revisit
     if (superclass != null) {
@@ -477,6 +545,7 @@
     }
   }
 
+  @Override
   protected void removeSubtype(JClassType me) {
     allSubtypes = IdentitySets.remove(allSubtypes, me);
 
@@ -494,6 +563,7 @@
   /**
    * NOTE: This method is for testing purposes only.
    */
+  @Override
   Annotation[] getAnnotations() {
     return annotations.getAnnotations();
   }
@@ -501,6 +571,7 @@
   /**
    * NOTE: This method is for testing purposes only.
    */
+  @Override
   Annotation[] getDeclaredAnnotations() {
     return annotations.getDeclaredAnnotations();
   }
@@ -510,6 +581,7 @@
     return this;
   }
 
+  @Override
   void notifySuperTypes() {
     notifySuperTypesOf(this);
   }
@@ -517,6 +589,7 @@
   /**
    * Removes references to this instance from all of its super types.
    */
+  @Override
   void removeFromSupertypes() {
     removeSubtype(this);
   }
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JType.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JType.java
index 15bc0bd..6c4a34f 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JType.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JType.java
@@ -40,6 +40,11 @@
     return getQualifiedSourceName();
   }
 
+  /**
+   * TODO(scottb): remove if we can resolve param names differently.
+   */
+  public abstract String getQualifiedBinaryName();
+
   public abstract String getQualifiedSourceName();
 
   public abstract String getSimpleSourceName();
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 ecb9c41..74de83a 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
@@ -96,6 +96,12 @@
   }
 
   @Override
+  public String getQualifiedBinaryName() {
+    // TODO(jat): !! does a binary name have meaning for a type parameter?
+    return toString(true);
+  }
+
+  @Override
   public String getQualifiedSourceName() {
     return toString(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 7681004..859fde1 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
@@ -110,6 +110,12 @@
   }
 
   @Override
+  public String getQualifiedBinaryName() {
+    // TODO(jat): !! does a binary name have meaning for a wildcard?
+    return toString(true);
+  }
+
+  @Override
   public String getQualifiedSourceName() {
     return toString(false);
   }
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/TypeOracle.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/TypeOracle.java
index a6138a6..5ad372a 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/TypeOracle.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/TypeOracle.java
@@ -16,7 +16,9 @@
 package com.google.gwt.core.ext.typeinfo;
 
 import com.google.gwt.core.ext.typeinfo.JWildcardType.BoundType;
+import com.google.gwt.dev.javac.JavaSourceParser;
 import com.google.gwt.dev.jjs.InternalCompilerException;
+import com.google.gwt.dev.resource.Resource;
 import com.google.gwt.dev.util.Name;
 import com.google.gwt.dev.util.collect.HashMap;
 import com.google.gwt.dev.util.collect.IdentityHashMap;
@@ -262,6 +264,8 @@
 
   private JClassType javaLangObject;
 
+  private JavaSourceParser javaSourceParser = new JavaSourceParser();
+
   /**
    * Maps SingleJsoImpl interfaces to the implementing JSO subtype.
    */
@@ -275,7 +279,7 @@
 
   /**
    * A list of recently-added types that will be fully initialized on the next
-   * call to {@link #finish(TreeLogger)}.
+   * call to {@link #finish}.
    */
   private final List<JRealClassType> recentTypes = new ArrayList<JRealClassType>();
 
@@ -294,6 +298,17 @@
   }
 
   /**
+   * Called to add a source reference for a top-level class type.
+   * 
+   * SHOULD ONLY BE CALLED FROM TypeOracleMediator.
+   * 
+   * TODO: make not public?
+   */
+  public void addSourceReference(JRealClassType type, Resource sourceFile) {
+    javaSourceParser.addSourceForType(type, sourceFile);
+  }
+
+  /**
    * Attempts to find a package by name. All requests for the same package
    * return the same package object.
    * 
@@ -342,7 +357,7 @@
    * TODO: make not public?
    */
   public void finish() {
-    JClassType[] newTypes = recentTypes.toArray(NO_JCLASSES);
+    JClassType[] newTypes = recentTypes.toArray(new JClassType[recentTypes.size()]);
     computeHierarchyRelationships(newTypes);
     computeSingleJsoImplData(newTypes);
     recentTypes.clear();
@@ -378,6 +393,10 @@
     return javaLangObject;
   }
 
+  public JavaSourceParser getJavaSourceParser() {
+    return javaSourceParser;
+  }
+
   /**
    * Ensure that a package with the specified name exists as well as its parent
    * packages.
@@ -852,12 +871,13 @@
       }
     }
   }
-
+  
   /**
    * Removes the specified type from the type oracle.
    */
   private void removeType(JRealClassType invalidType) {
     allTypes.remove(invalidType.getQualifiedSourceName());
+    recentTypes.remove(invalidType);
     JPackage pkg = invalidType.getPackage();
     if (pkg != null) {
       pkg.remove(invalidType);
diff --git a/dev/core/src/com/google/gwt/dev/javac/CompiledClass.java b/dev/core/src/com/google/gwt/dev/javac/CompiledClass.java
index d356681..1f14afe 100644
--- a/dev/core/src/com/google/gwt/dev/javac/CompiledClass.java
+++ b/dev/core/src/com/google/gwt/dev/javac/CompiledClass.java
@@ -172,7 +172,9 @@
   }
 
   void graveyard() {
-    realClassType.invalidate();
+    if (realClassType != null) {
+      realClassType.invalidate();
+    }
   }
 
   void invalidate() {
diff --git a/dev/core/src/com/google/gwt/dev/javac/JavaSourceParser.java b/dev/core/src/com/google/gwt/dev/javac/JavaSourceParser.java
new file mode 100644
index 0000000..2444352
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/javac/JavaSourceParser.java
@@ -0,0 +1,303 @@
+/*

+ * 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.javac;

+

+import com.google.gwt.core.ext.typeinfo.JAbstractMethod;

+import com.google.gwt.core.ext.typeinfo.JClassType;

+import com.google.gwt.core.ext.typeinfo.JParameter;

+import com.google.gwt.core.ext.typeinfo.JType;

+import com.google.gwt.dev.resource.Resource;

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

+import com.google.gwt.dev.util.Name.BinaryName;

+

+import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;

+import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;

+import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;

+import org.eclipse.jdt.internal.compiler.ast.TypeReference;

+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;

+import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;

+import org.eclipse.jdt.internal.core.util.CodeSnippetParsingUtil;

+

+import java.io.InputStream;

+import java.lang.ref.SoftReference;

+import java.util.ArrayList;

+import java.util.Arrays;

+import java.util.List;

+import java.util.WeakHashMap;

+

+/**

+ * Methods to do direct parsing of Java source -- currently the only uses are

+ * for finding actual method parameter names on request.

+ */

+public class JavaSourceParser {

+

+  public static JClassType getTopmostType(JClassType type) {

+    while (type.getEnclosingType() != null) {

+      type = type.getEnclosingType();

+    }

+    return type;

+  }

+  

+  /**

+   * Spits a binary name into a series of char arrays, corresponding to

+   * enclosing classes.

+   * 

+   * <p>For example, {@code test.Foo$Bar} gets expanded to [[Foo],[Bar]].

+   * Note that the package is not included.

+   * 

+   * @param binaryName class name in binary form (ie, test.Foo$Bar)

+   * @return list of char arrays of class names, from outer to inner 

+   */

+  // @VisibleForTesting

+  static List<char[]> getClassChain(String binaryName) {

+    ArrayList<char[]> result = new ArrayList<char[]>();

+    String className = BinaryName.getClassName(binaryName);

+    int idx;

+    while ((idx = className.indexOf('$')) >= 0) {

+      result.add(className.substring(0, idx).toCharArray());

+      className = className.substring(idx + 1);

+    }

+    result.add(className.toCharArray());

+    return result;

+  }

+

+  /**

+   * Find a matching method in a type.

+   * 

+   * @param type JDT method

+   * @param jMethod TypeOracle method object to find

+   * @return method declaration or null if not found

+   */

+  private static AbstractMethodDeclaration findMethod(TypeDeclaration type,

+      JAbstractMethod jMethod) {

+    List<AbstractMethodDeclaration> candidates = findNamedMethods(type,

+        jMethod.getName());

+    if (candidates.size() == 0) {

+      return null;

+    }

+    if (candidates.size() == 1) {

+      return candidates.get(0);

+    }

+    nextCandidate: for (AbstractMethodDeclaration candidate : candidates) {

+      int n = candidate.arguments == null ? 0 : candidate.arguments.length;

+      JParameter[] params = jMethod.getParameters();

+      if (n != params.length) {

+        continue;

+      }

+      for (int i = 0; i < n; ++i) {

+        if (!typeMatches(candidate.arguments[i].type,

+            params[i].getType())) {

+          continue nextCandidate;

+        }

+      }

+      return candidate;

+    }

+    return null;

+  }

+

+  /**

+   * Find all methods which have the requested name.

+   * 

+   * <p>{@code <clinit>} is not supported.

+   * @param type JDT type declaration

+   * @param name name of methods to find

+   * @return list of matching methods

+   */

+   private static List<AbstractMethodDeclaration> findNamedMethods(

+      TypeDeclaration type, String name) {

+    List<AbstractMethodDeclaration> matching = new ArrayList<AbstractMethodDeclaration>();

+    boolean isCtor = "<init>".equals(name);

+    char[] nameArray = name.toCharArray();

+    for (AbstractMethodDeclaration method : type.methods) {

+      if ((isCtor && method.isConstructor()) ||

+          (!isCtor && !method.isConstructor() && !method.isClinit()

+              && Arrays.equals(method.selector, nameArray))) {

+          matching.add(method);

+      }

+    }

+    return matching;

+  }

+

+  /**

+   * Find a particular type in a compilation unit.

+   * 

+   * @param unit JDT cud

+   * @param binaryName binary name of the type to find (ie, test.Foo$Bar)

+   * @return type declaration or null if not found

+   */

+  private static TypeDeclaration findType(CompilationUnitDeclaration unit,

+      String binaryName) {

+    List<char[]> classChain = getClassChain(binaryName);

+    TypeDeclaration curType = findType(unit.types, classChain.get(0));

+    for (int i = 1; i < classChain.size(); ++i) {

+      if (curType == null) {

+        return null;

+      }

+      curType = findType(curType.memberTypes, classChain.get(i));

+    }

+    return curType; 

+  }

+

+  /**

+   * Find one type by name in a array of types.

+   * 

+   * @param types array of types

+   * @param name name of type to find

+   * @return matching type or null if not found

+   */

+  private static TypeDeclaration findType(TypeDeclaration[] types, char[] name) {

+    for (TypeDeclaration type : types) {

+      if (Arrays.equals(name, type.name)) {

+        return type;

+      }

+    }

+    return null;

+  }

+

+  /**

+   * Parse Java source.

+   * 

+   * @param javaSource String containing Java source to parse

+   * @return a CompilationUnitDeclaration or null if parsing failed

+   */

+  private static CompilationUnitDeclaration parseJava(String javaSource) {

+    CodeSnippetParsingUtil parsingUtil = new CodeSnippetParsingUtil();

+    CompilerOptions options = new CompilerOptions();

+    options.complianceLevel = ClassFileConstants.JDK1_5;

+    options.sourceLevel = ClassFileConstants.JDK1_5;

+    CompilationUnitDeclaration unit = parsingUtil.parseCompilationUnit(

+        javaSource.toString().toCharArray(), options.getMap(), true);

+    if (unit.compilationResult().hasProblems()) {

+      return null;

+    }

+    return unit;

+  }

+

+  /**

+   * Compares an unresolved JDT type to a TypeOracle type to see if they match.

+   * 

+   * @param jdtType

+   * @param toType

+   * @return true if the two type objects resolve to the same 

+   */

+  private static boolean typeMatches(TypeReference jdtType, JType toType) {

+    List<char[]> toNameComponents = getClassChain(

+        toType.getQualifiedBinaryName());

+    int toLen = toNameComponents.size();

+    char[][] jdtNameComponents = jdtType.getTypeName();

+    int jdtLen = jdtNameComponents.length;

+    int maxToCompare = Math.min(toLen, jdtLen);

+    

+    // compare from the end

+    for (int i = 1; i <= maxToCompare; ++i) {

+      if (!Arrays.equals(jdtNameComponents[jdtLen - i],

+          toNameComponents.get(toLen - i))) {

+        return false;

+      }

+    }

+    return true;

+  }

+

+  /**

+   * Map of top-level classes to the source file associated with it.

+   */

+  private WeakHashMap<JClassType, Resource> classSources = new WeakHashMap<JClassType, Resource>();

+

+  /**

+   * Cache of top-level classes to JDT CUDs associated with them.

+   * 

+   * <p>CUDs may be discarded at any time (with a performance cost if they are

+   * needed again), and are held in SoftReferences to allow GC to dump them.

+   */

+  private WeakHashMap<JClassType, SoftReference<CompilationUnitDeclaration>> cudCache = new WeakHashMap<JClassType, SoftReference<CompilationUnitDeclaration>>();

+

+  /**

+   * Add a source file associated with the outermost enclosing class.

+   * 

+   * @param topType

+   * @param source

+   * 

+   * TODO: reduce visibility

+   */

+  public synchronized void addSourceForType(JClassType topType, Resource source) {

+    classSources.put(topType, source);

+  }

+

+  /**

+   * Return the real argument names for a given method from the source.

+   * 

+   * @param method method to lookup parameter names for

+   * @return array of argument names or null if no source is available

+   */

+  public synchronized String[] getArguments(JAbstractMethod method) {

+    JClassType type = method.getEnclosingType();

+    JClassType topType = getTopmostType(type);

+    CompilationUnitDeclaration cud = getCudForTopLevelType(topType);

+    if (cud == null) {

+      return null;

+    }

+    TypeDeclaration jdtType = findType(cud, type.getQualifiedBinaryName());

+    if (jdtType == null) {

+      // TODO(jat): any thing else to do here?

+      return null;

+    }

+    AbstractMethodDeclaration jdtMethod = findMethod(jdtType, method);

+    if (jdtMethod == null) {

+      // TODO(jat): any thing else to do here?

+      return null;

+    }

+    int n = jdtMethod.arguments.length;

+    String[] argNames = new String[n];

+    for (int i = 0; i < n; ++i) {

+      argNames[i] = String.valueOf(jdtMethod.arguments[i].name);

+    }

+    return argNames;

+  }

+  

+  /**

+   * Finds a JDT CUD for a given top-level type, generating it if needed.

+   * 

+   * @param topType top-level JClassType

+   * @return CUD instance or null if no source found

+   */

+  private synchronized CompilationUnitDeclaration getCudForTopLevelType(

+      JClassType topType) {

+    CompilationUnitDeclaration cud = null;

+    if (cudCache.containsKey(topType)) {

+      SoftReference<CompilationUnitDeclaration> cudRef = cudCache.get(topType);

+      if (cudRef != null) {

+        cud = cudRef.get();

+      }

+    }

+    if (cud == null) {

+      Resource classSource = classSources.get(topType);

+      String source = null;

+      if (classSource != null) {

+        InputStream stream = classSource.openContents();

+        source = Util.readStreamAsString(stream);

+      }

+      if (source == null) {

+        // cache negative result so we don't try again

+        cudCache.put(topType, null);

+      } else {

+        cud = parseJava(source);

+        cudCache.put(topType,

+            new SoftReference<CompilationUnitDeclaration>(cud));

+      }

+    }

+    return cud;

+  }

+}

diff --git a/dev/core/src/com/google/gwt/dev/javac/Resolver.java b/dev/core/src/com/google/gwt/dev/javac/Resolver.java
new file mode 100644
index 0000000..e62b129
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/javac/Resolver.java
@@ -0,0 +1,45 @@
+/*
+ * 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.javac;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.typeinfo.JRealClassType;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.google.gwt.dev.javac.asm.CollectAnnotationData;
+
+import java.lang.annotation.Annotation;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Interface for resolving various aspects of a class.
+ */
+public interface Resolver {
+
+  Map<String, JRealClassType> getBinaryMapper();
+
+  TypeOracle getTypeOracle();
+
+  boolean resolveAnnotation(TreeLogger logger,
+      CollectAnnotationData annotVisitor,
+      Map<Class<? extends Annotation>, Annotation> declaredAnnotations);
+
+  boolean resolveAnnotations(TreeLogger logger,
+      List<CollectAnnotationData> annotations,
+      Map<Class<? extends Annotation>, Annotation> declaredAnnotations);
+
+  boolean resolveClass(TreeLogger logger, JRealClassType type);
+}
diff --git a/dev/core/src/com/google/gwt/dev/javac/TypeOracleMediator.java b/dev/core/src/com/google/gwt/dev/javac/TypeOracleMediator.java
index fee85bc..94bbddd 100644
--- a/dev/core/src/com/google/gwt/dev/javac/TypeOracleMediator.java
+++ b/dev/core/src/com/google/gwt/dev/javac/TypeOracleMediator.java
@@ -16,7 +16,6 @@
 package com.google.gwt.dev.javac;
 
 import com.google.gwt.core.ext.TreeLogger;
-import com.google.gwt.core.ext.typeinfo.HasTypeParameters;
 import com.google.gwt.core.ext.typeinfo.JAbstractMethod;
 import com.google.gwt.core.ext.typeinfo.JAnnotationMethod;
 import com.google.gwt.core.ext.typeinfo.JAnnotationType;
@@ -32,58 +31,43 @@
 import com.google.gwt.core.ext.typeinfo.JParameter;
 import com.google.gwt.core.ext.typeinfo.JParameterizedType;
 import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
+import com.google.gwt.core.ext.typeinfo.JRawType;
 import com.google.gwt.core.ext.typeinfo.JRealClassType;
 import com.google.gwt.core.ext.typeinfo.JType;
 import com.google.gwt.core.ext.typeinfo.JTypeParameter;
+import com.google.gwt.core.ext.typeinfo.JWildcardType;
 import com.google.gwt.core.ext.typeinfo.TypeOracle;
-import com.google.gwt.core.ext.typeinfo.JWildcardType.BoundType;
-import com.google.gwt.dev.javac.CompilationUnit.State;
+import com.google.gwt.dev.asm.ClassReader;
+import com.google.gwt.dev.asm.ClassVisitor;
+import com.google.gwt.dev.asm.Opcodes;
+import com.google.gwt.dev.asm.Type;
+import com.google.gwt.dev.asm.signature.SignatureReader;
+import com.google.gwt.dev.asm.util.TraceClassVisitor;
+import com.google.gwt.dev.javac.asm.CollectAnnotationData;
+import com.google.gwt.dev.javac.asm.CollectClassData;
+import com.google.gwt.dev.javac.asm.CollectFieldData;
+import com.google.gwt.dev.javac.asm.CollectMethodData;
+import com.google.gwt.dev.javac.asm.CollectTypeParams;
+import com.google.gwt.dev.javac.asm.ResolveClassSignature;
+import com.google.gwt.dev.javac.asm.ResolveMethodSignature;
+import com.google.gwt.dev.javac.asm.ResolveTypeSignature;
+import com.google.gwt.dev.javac.asm.CollectAnnotationData.AnnotationData;
+import com.google.gwt.dev.javac.asm.CollectClassData.AnnotationEnum;
 import com.google.gwt.dev.javac.impl.Shared;
-import com.google.gwt.dev.util.collect.HashMap;
-import com.google.gwt.dev.util.collect.HashSet;
-import com.google.gwt.dev.util.collect.IdentityHashMap;
-import com.google.gwt.dev.util.collect.Maps;
+import com.google.gwt.dev.javac.impl.SourceFileCompilationUnit;
+import com.google.gwt.dev.resource.Resource;
+import com.google.gwt.dev.util.Name;
+import com.google.gwt.dev.util.Name.InternalName;
 
-import org.eclipse.jdt.core.compiler.CharOperation;
-import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
-import org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration;
-import org.eclipse.jdt.internal.compiler.ast.Annotation;
-import org.eclipse.jdt.internal.compiler.ast.AnnotationMethodDeclaration;
-import org.eclipse.jdt.internal.compiler.ast.Argument;
-import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer;
-import org.eclipse.jdt.internal.compiler.ast.ClassLiteralAccess;
-import org.eclipse.jdt.internal.compiler.ast.Clinit;
-import org.eclipse.jdt.internal.compiler.ast.Expression;
-import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
-import org.eclipse.jdt.internal.compiler.ast.Initializer;
-import org.eclipse.jdt.internal.compiler.ast.MemberValuePair;
-import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
-import org.eclipse.jdt.internal.compiler.ast.NameReference;
-import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
-import org.eclipse.jdt.internal.compiler.ast.TypeParameter;
-import org.eclipse.jdt.internal.compiler.ast.TypeReference;
-import org.eclipse.jdt.internal.compiler.ast.Wildcard;
-import org.eclipse.jdt.internal.compiler.impl.Constant;
-import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding;
-import org.eclipse.jdt.internal.compiler.lookup.BaseTypeBinding;
-import org.eclipse.jdt.internal.compiler.lookup.BinaryTypeBinding;
-import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope;
-import org.eclipse.jdt.internal.compiler.lookup.LocalTypeBinding;
-import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
-import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding;
-import org.eclipse.jdt.internal.compiler.lookup.RawTypeBinding;
-import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
-import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
-import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
-import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
-import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding;
-import org.eclipse.jdt.internal.compiler.lookup.WildcardBinding;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
+import java.io.PrintWriter;
+import java.lang.annotation.Annotation;
 import java.lang.reflect.Array;
+import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -94,7 +78,18 @@
  */
 public class TypeOracleMediator {
 
-  private static final JClassType[] NO_JCLASSES = new JClassType[0];
+  /**
+   * Pairs of bits to convert from ASM Opcodes.* to Shared.* bitfields.
+   */
+  private static final int[] ASM_TO_SHARED_MODIFIERS = new int[] {
+      Opcodes.ACC_PUBLIC, Shared.MOD_PUBLIC, Opcodes.ACC_PRIVATE,
+      Shared.MOD_PRIVATE, Opcodes.ACC_PROTECTED, Shared.MOD_PROTECTED,
+      Opcodes.ACC_STATIC, Shared.MOD_STATIC, Opcodes.ACC_FINAL,
+      Shared.MOD_FINAL, Opcodes.ACC_ABSTRACT, Shared.MOD_ABSTRACT,
+      Opcodes.ACC_VOLATILE, Shared.MOD_VOLATILE, Opcodes.ACC_TRANSIENT,
+      Shared.MOD_TRANSIENT,};
+
+  private static final JTypeParameter[] NO_TYPE_PARAMETERS = new JTypeParameter[0];
 
   /**
    * Returns the binary name of a type. This is the same name that would be
@@ -134,192 +129,308 @@
     return classType.getQualifiedSourceName();
   }
 
-  /**
-   * Returns the value associated with a JDT constant.
-   */
-  private static Object getConstantValue(Constant constant) {
-    switch (constant.typeID()) {
-      case TypeIds.T_char:
-        return constant.charValue();
-      case TypeIds.T_byte:
-        return constant.byteValue();
-      case TypeIds.T_short:
-        return constant.shortValue();
-      case TypeIds.T_boolean:
-        return constant.booleanValue();
-      case TypeIds.T_long:
-        return constant.longValue();
-      case TypeIds.T_double:
-        return constant.doubleValue();
-      case TypeIds.T_float:
-        return constant.floatValue();
-      case TypeIds.T_int:
-        return constant.intValue();
-      case TypeIds.T_JavaLangString:
-        return constant.stringValue();
-      case TypeIds.T_null:
-        return null;
-      default:
-        break;
+  private static JTypeParameter[] collectTypeParams(String signature) {
+    if (signature != null) {
+      List<JTypeParameter> params = new ArrayList<JTypeParameter>();
+      SignatureReader reader = new SignatureReader(signature);
+      reader.accept(new CollectTypeParams(params));
+      return params.toArray(new JTypeParameter[params.size()]);
     }
+    return NO_TYPE_PARAMETERS;
+  }
 
-    assert false : "Unknown constant type";
+  /**
+   * @param resolvedType a resolved type, which is either a primitive, an array,
+   * or a JRealClassType.
+   */
+  private static <T> Class<? extends T> getClassLiteral(TreeLogger logger,
+      Class<T> requestedClass, JType resolvedType) {
+    Class<?> clazz = null;
+    if (resolvedType instanceof JPrimitiveType) {
+      clazz = getClassLiteralForPrimitive((JPrimitiveType) resolvedType);
+    } else {
+      String binaryName; 
+      if (resolvedType instanceof JArrayType) {
+        binaryName = ((JArrayType) resolvedType).getQualifiedBinaryName();
+      } else if (resolvedType instanceof JRealClassType) {
+        binaryName = ((JRealClassType) resolvedType).getQualifiedBinaryName();
+      } else {
+        throw new IllegalArgumentException(
+            "Unexpected type for class literal '"
+                + resolvedType.getQualifiedSourceName() + "'");
+      }
+      try {
+        clazz = Class.forName(binaryName, false,
+            Thread.currentThread().getContextClassLoader());
+      } catch (ClassNotFoundException e) {
+        logger.log(TreeLogger.ERROR,
+            "Unable to get class object for annotation " + resolvedType, e);
+        return null;
+      }
+    }
+    if (requestedClass.isAssignableFrom(clazz)) {
+      return clazz.asSubclass(requestedClass);
+    }
     return null;
   }
 
-  private static String getMethodName(JClassType enclosingType,
-      AbstractMethodDeclaration jmethod) {
-    if (jmethod.isConstructor()) {
-      return String.valueOf(enclosingType.getSimpleSourceName());
+  private static Class<?> getClassLiteralForPrimitive(JPrimitiveType type) {
+    if (type.equals(JPrimitiveType.INT)) {
+      return Integer.TYPE;
+    } else if (type.equals(JPrimitiveType.BOOLEAN)) {
+      return Boolean.TYPE;
+    } else if (type.equals(JPrimitiveType.DOUBLE)) {
+      return Double.TYPE;
+    } else if (type.equals(JPrimitiveType.FLOAT)) {
+      return Float.TYPE;
+    } else if (type.equals(JPrimitiveType.LONG)) {
+      return Long.TYPE;
+    } else if (type.equals(JPrimitiveType.VOID)) {
+      return Void.TYPE;
+    } else if (type.equals(JPrimitiveType.CHAR)) {
+      return Character.TYPE;
+    } else if (type.equals(JPrimitiveType.SHORT)) {
+      return Short.TYPE;
+    } else if (type.equals(JPrimitiveType.BYTE)) {
+      return Byte.TYPE;
     } else {
-      return String.valueOf(jmethod.binding.selector);
+      assert false : "Unexpected type " + type;
+      return null;
     }
   }
 
-  private static RetentionPolicy getRetentionPolicy(
-      Class<? extends java.lang.annotation.Annotation> clazz) {
-    // Default retention policy is CLASS, see @Retention.
-    RetentionPolicy retentionPolicy = RetentionPolicy.CLASS;
-    Retention retentionAnnotation = clazz.getAnnotation(Retention.class);
-    if (retentionAnnotation != null) {
-      retentionPolicy = retentionAnnotation.value();
+  private static JTypeParameter[] getTypeParametersForClass(
+      CollectClassData classData) {
+    JTypeParameter[] typeParams = null;
+    if (classData.getSignature() != null) {
+      // TODO(jat): do we need to consider generic types w/ method type
+      // params for local classes?
+      typeParams = collectTypeParams(classData.getSignature());
     }
-    return retentionPolicy;
+    return typeParams;
+  }
+
+  private static Class<?> getWrapperClass(Class<?> primitiveClass) {
+    assert primitiveClass.isPrimitive();
+    if (primitiveClass.equals(Integer.TYPE)) {
+      return Integer.class;
+    } else if (primitiveClass.equals(Boolean.TYPE)) {
+      return Boolean.class;
+    } else if (primitiveClass.equals(Byte.TYPE)) {
+      return Byte.class;
+    } else if (primitiveClass.equals(Character.TYPE)) {
+      return Character.class;
+    } else if (primitiveClass.equals(Short.TYPE)) {
+      return Short.class;
+    } else if (primitiveClass.equals(Long.TYPE)) {
+      return Long.class;
+    } else if (primitiveClass.equals(Float.TYPE)) {
+      return Float.class;
+    } else if (primitiveClass.equals(Double.TYPE)) {
+      return Double.class;
+    } else {
+      throw new IllegalArgumentException(primitiveClass.toString()
+          + " not a primitive class");
+    }
   }
 
   /**
-   * Returns <code>true</code> if this name is the special package-info type
-   * name.
-   * 
    * @return <code>true</code> if this name is the special package-info type
-   *         name
+   *         name.
    */
   private static boolean isPackageInfoTypeName(String qname) {
     return "package-info".equals(qname);
   }
 
-  private static boolean maybeGeneric(TypeDeclaration typeDecl,
-      JClassType enclosingType) {
-
-    if (typeDecl.typeParameters != null) {
-      // Definitely generic since it has type parameters.
-      return true;
+  /**
+   * Returns true if this class is a non-static class inside a generic class.
+   * 
+   * TODO(jat): do we need to consider the entire hierarchy?
+   * 
+   * @param classData
+   * @param enclosingClassData
+   * @return true if this class is a non-static class inside a generic class
+   */
+  private static boolean nonStaticInsideGeneric(CollectClassData classData,
+      CollectClassData enclosingClassData) {
+    if (enclosingClassData == null
+        || (classData.getAccess() & Opcodes.ACC_STATIC) != 0) {
+      return false;
     }
-
-    if (enclosingType != null && enclosingType.isGenericType() != null) {
-      if (!typeDecl.binding.isStatic()) {
-        /*
-         * This non-static, inner class can reference the type parameters of its
-         * enclosing generic type, so we will treat it as a generic type.
-         */
-        return true;
-      }
-    }
-
-    if (typeDecl.binding.isLocalType()) {
-      LocalTypeBinding localTypeBinding = (LocalTypeBinding) typeDecl.binding;
-      MethodBinding enclosingMethod = localTypeBinding.enclosingMethod;
-      if (enclosingMethod != null) {
-        if (enclosingMethod.typeVariables != null
-            && enclosingMethod.typeVariables.length != 0) {
-          /*
-           * This local type can reference the type parameters of its enclosing
-           * method, so we will treat is as a generic type.
-           */
-          return true;
-        }
-      }
-    }
-
-    return false;
+    return getTypeParametersForClass(enclosingClassData) != null;
   }
 
-  // TODO: move into the Annotations class or create an AnnotationsUtil class?
-  private static HashMap<Class<? extends java.lang.annotation.Annotation>, java.lang.annotation.Annotation> newAnnotationMap() {
-    return new HashMap<Class<? extends java.lang.annotation.Annotation>, java.lang.annotation.Annotation>();
+  /**
+   * Substitute the raw type if the supplied type is generic.
+   * 
+   * @param type
+   * @return original type or its raw type if it is generic
+   */
+  private static JType possiblySubstituteRawType(JType type) {
+    if (type != null) {
+      JGenericType genericType = type.isGenericType();
+      if (genericType != null) {
+        type = genericType.getRawType();
+      }
+    }
+    return type;
   }
 
-  private final Map<String, JRealClassType> binaryMapper = new HashMap<String, JRealClassType>();
+  // map of internal names to classes
+  final Map<String, JRealClassType> binaryMapper = new HashMap<String, JRealClassType>();
 
-  /**
-   * Mapping of source type bindings; transient because freshly-compiled units
-   * are processed in chunks, so only references between types in the same chunk
-   * are source bindings; the rest are going to be binary type bindings.
-   */
-  private final transient Map<SourceTypeBinding, JRealClassType> sourceMapper = new IdentityHashMap<SourceTypeBinding, JRealClassType>();
+  final TypeOracle typeOracle;
 
-  /**
-   * Mapping of type variable bindings; transient because compilation units are
-   * processed monolithically, and a tv binding is only valid within a single
-   * unit.
-   */
-  private final transient Map<TypeVariableBinding, JTypeParameter> tvMapper = new IdentityHashMap<TypeVariableBinding, JTypeParameter>();
+  // map of internal names to class visitors
+  // transient since it is not retained across calls to addNewUnits
+  private transient Map<String, CollectClassData> classMap;
 
-  private final TypeOracle typeOracle = new TypeOracle();
+  // transient since it is not retained across calls to addNewUnits
+  private transient HashMap<JRealClassType, CollectClassData> classMapType;
 
-  /**
-   * Set of unresolved types to process, generated from the first phase of type
-   * oracle resolution. Transient because they all get resolved in the second
-   * phase.
-   */
-  private final transient Set<JRealClassType> unresolvedTypes = new HashSet<JRealClassType>();
+  private final Set<JRealClassType> resolved = new HashSet<JRealClassType>();
+
+  private Resolver resolver;
+
+  public TypeOracleMediator() {
+    this(null);
+  }
+
+  // @VisibleForTesting
+  public TypeOracleMediator(TypeOracle typeOracle) {
+    if (typeOracle == null) {
+      typeOracle = new TypeOracle();
+    }
+    this.typeOracle = typeOracle;
+    resolver = new Resolver() {
+      public Map<String, JRealClassType> getBinaryMapper() {
+        return TypeOracleMediator.this.binaryMapper;
+      }
+
+      public TypeOracle getTypeOracle() {
+        return TypeOracleMediator.this.typeOracle;
+      }
+
+      public boolean resolveAnnotation(TreeLogger logger,
+          CollectAnnotationData annotVisitor,
+          Map<Class<? extends Annotation>, Annotation> declaredAnnotations) {
+        return TypeOracleMediator.this.resolveAnnotation(logger, annotVisitor,
+            declaredAnnotations);
+      }
+
+      public boolean resolveAnnotations(TreeLogger logger,
+          List<CollectAnnotationData> annotations,
+          Map<Class<? extends Annotation>, Annotation> declaredAnnotations) {
+        return TypeOracleMediator.this.resolveAnnotations(logger, annotations,
+            declaredAnnotations);
+      }
+
+      public boolean resolveClass(TreeLogger logger, JRealClassType type) {
+        return TypeOracleMediator.this.resolveClass(logger, type);
+      }
+    };
+  }
 
   /**
    * Adds new units to an existing TypeOracle.
    */
   public void addNewUnits(TreeLogger logger, Collection<CompilationUnit> units) {
-    // Perform a shallow pass to establish identity for new and old types.
+    // First collect all class data and resurrect graveyard types.
+    classMap = new HashMap<String, CollectClassData>();
     for (CompilationUnit unit : units) {
-      switch (unit.getState()) {
-        case GRAVEYARD:
-          for (CompiledClass compiledClass : unit.getCompiledClasses()) {
-            JRealClassType type = compiledClass.getRealClassType();
-            assert (type != null);
+      if (unit.getState() == CompilationUnit.State.GRAVEYARD) {
+        for (CompiledClass compiledClass : unit.getCompiledClasses()) {
+          JRealClassType type = compiledClass.getRealClassType();
+          if (type != null) {
             type.resurrect();
-            binaryMapper.put(compiledClass.getInternalName(), type);
           }
-          break;
-        case COMPILED:
-          for (CompiledClass compiledClass : unit.getCompiledClasses()) {
-            JRealClassType type = createType(compiledClass);
-            binaryMapper.put(compiledClass.getInternalName(), type);
-          }
-          break;
-        case CHECKED:
-          for (CompiledClass compiledClass : unit.getCompiledClasses()) {
-            JRealClassType type = compiledClass.getRealClassType();
-            assert (type != null);
-            binaryMapper.put(compiledClass.getInternalName(), type);
-          }
-          break;
-      }
-    }
-
-    // Perform a deep pass to resolve all new types in terms of our types.
-    for (CompilationUnit unit : units) {
-      if (unit.getState() != State.COMPILED) {
+        }
+        continue;
+      } else if (!unit.isCompiled()) {
         continue;
       }
-      TreeLogger cudLogger = logger.branch(TreeLogger.SPAM,
-          "Processing types in compilation unit: " + unit.getDisplayLocation());
       Set<CompiledClass> compiledClasses = unit.getCompiledClasses();
       for (CompiledClass compiledClass : compiledClasses) {
-        if (unresolvedTypes.remove(compiledClass.getRealClassType())) {
-          TypeDeclaration typeDeclaration = compiledClass.getTypeDeclaration();
-          if (!resolveTypeDeclaration(cudLogger, typeDeclaration)) {
-            logger.log(TreeLogger.WARN,
-                "Unexpectedly unable to fully resolve type "
-                    + compiledClass.getSourceName());
-          }
+        CollectClassData cv = processClass(compiledClass);
+        // skip any classes that can't be referenced by name outside of
+        // their local scope, such as anonymous classes and method-local classes
+        if (!cv.hasNoExternalName()) {
+          classMap.put(compiledClass.getInternalName(), cv);
         }
       }
     }
-    // Clean transient state.
-    assert unresolvedTypes.size() == 0;
-    sourceMapper.clear();
-    tvMapper.clear();
+
+    // Perform a shallow pass to establish identity for new and old types.
+    classMapType = new HashMap<JRealClassType, CollectClassData>();
+    Set<JRealClassType> unresolvedTypes = new HashSet<JRealClassType>();
+    for (CompilationUnit unit : units) {
+      if (!unit.isCompiled()) {
+        continue;
+      }
+      Set<CompiledClass> compiledClasses = unit.getCompiledClasses();
+      for (CompiledClass compiledClass : compiledClasses) {
+        String internalName = compiledClass.getInternalName();
+        CollectClassData cv = classMap.get(internalName);
+        if (cv == null) {
+          // ignore classes that were skipped earlier
+          continue;
+        }
+        JRealClassType type = compiledClass.getRealClassType();
+        if (type == null) {
+          type = createType(compiledClass, unresolvedTypes);
+        } else {
+          resolved.add(type);
+        }
+        if (type != null) {
+          binaryMapper.put(internalName, type);
+          classMapType.put(type, cv);
+        }
+      }
+    }
+
+    // Hook up enclosing types
+    TreeLogger branch = logger.branch(TreeLogger.SPAM,
+        "Resolving enclosing classes");
+    for (Iterator<JRealClassType> it = unresolvedTypes.iterator(); it.hasNext();) {
+      JRealClassType type = it.next();
+      if (!resolveEnclosingClass(branch, type)) {
+        // already logged why it failed, don't try and use it further
+        it.remove();
+      }
+    }
+
+    // Resolve unresolved types.
+    for (JRealClassType type : unresolvedTypes) {
+      branch = logger.branch(TreeLogger.SPAM, "Resolving "
+          + type.getQualifiedSourceName());
+      if (!resolveClass(branch, type)) {
+        // already logged why it failed
+        // TODO: should we do anything else here?
+      }
+    }
 
     typeOracle.finish();
+
+    // save source references
+    for (CompilationUnit unit : units) {
+      if (unit.isCompiled() && unit instanceof SourceFileCompilationUnit) {
+        SourceFileCompilationUnit sourceUnit = (SourceFileCompilationUnit) unit;
+        Resource sourceFile = sourceUnit.getSourceFile();
+        Set<CompiledClass> compiledClasses = unit.getCompiledClasses();
+        for (CompiledClass compiledClass : compiledClasses) {
+          JRealClassType type = compiledClass.getRealClassType();
+          typeOracle.addSourceReference(type, sourceFile);
+        }
+      }
+    }
+
+    // no longer needed
+    classMap = null;
+    classMapType = null;
+  }
+
+  public Map<String, JRealClassType> getBinaryMapper() {
+    return binaryMapper;
   }
 
   public TypeOracle getTypeOracle() {
@@ -330,1050 +441,752 @@
    * Full refresh based on new units.
    */
   public void refresh(TreeLogger logger, Collection<CompilationUnit> units) {
-    logger = logger.branch(TreeLogger.DEBUG, "Refreshing TypeOracle");
     binaryMapper.clear();
     typeOracle.reset();
+    resolved.clear();
     addNewUnits(logger, units);
   }
 
-  private Object createAnnotationInstance(TreeLogger logger,
-      Expression expression) {
-    Annotation annotation = (Annotation) expression;
-
-    // Determine the annotation class
-    TypeBinding resolvedType = annotation.resolvedType;
-    Class<?> classLiteral = getClassLiteral(logger, resolvedType);
-    if (classLiteral == null) {
-      return null;
-    }
-
-    Class<? extends java.lang.annotation.Annotation> clazz = classLiteral.asSubclass(java.lang.annotation.Annotation.class);
-
-    // Build the map of identifiers to values.
-    Map<String, Object> identifierToValue = new HashMap<String, Object>();
-    for (MemberValuePair mvp : annotation.memberValuePairs()) {
-      // Method name
-      String identifier = String.valueOf(mvp.name);
-
-      // Value
-      Expression expressionValue = mvp.value;
-      TypeBinding expectedElementValueType = mvp.binding.returnType;
-      Object elementValue = getAnnotationElementValue(logger,
-          expectedElementValueType, expressionValue);
-      if (elementValue == null) {
+  private Annotation createAnnotation(TreeLogger logger,
+      Class<? extends Annotation> annotationClass, AnnotationData annotData) {
+    Map<String, Object> values = annotData.getValues();
+    for (Map.Entry<String, Object> entry : values.entrySet()) {
+      Method method = null;
+      Throwable caught = null;
+      try {
+        method = annotationClass.getMethod(entry.getKey());
+      } catch (SecurityException e) {
+        caught = e;
+      } catch (NoSuchMethodException e) {
+        caught = e;
+      }
+      if (caught != null) {
+        logger.log(TreeLogger.WARN, "Exception resolving "
+            + annotationClass.getCanonicalName() + "." + entry.getKey(), caught);
         return null;
       }
-
-      /*
-       * If the expected value is supposed to be an array then the element value
-       * had better be an array.
-       */
-      assert (expectedElementValueType.isArrayType() == false || expectedElementValueType.isArrayType()
-          && elementValue.getClass().isArray());
-
-      identifierToValue.put(identifier, elementValue);
+      entry.setValue(resolveAnnotationValue(logger, method.getReturnType(),
+          entry.getValue()));
     }
-
-    return AnnotationProxyFactory.create(clazz,
-        Maps.normalize(identifierToValue));
+    return AnnotationProxyFactory.create(annotationClass, values);
   }
 
-  private JRealClassType createType(CompiledClass compiledClass) {
-    JRealClassType realClassType = compiledClass.getRealClassType();
-    if (realClassType == null) {
-      JRealClassType enclosingType = null;
-      CompiledClass enclosingClass = compiledClass.getEnclosingClass();
-      if (enclosingClass != null) {
-        enclosingType = enclosingClass.getRealClassType();
-        if (enclosingType == null) {
-          enclosingType = createType(enclosingClass);
-        }
-      }
-      realClassType = createType(compiledClass, enclosingType);
-      if (realClassType != null) {
-        unresolvedTypes.add(realClassType);
-        sourceMapper.put(compiledClass.getTypeDeclaration().binding,
-            realClassType);
-        compiledClass.setRealClassType(realClassType);
-      }
-    }
-    return realClassType;
-  }
-
-  /**
-   * Maps a TypeDeclaration into a JRealClassType. If the TypeDeclaration has
-   * TypeParameters (i.e, it is a generic type or method), then the
-   * TypeParameters are mapped into JTypeParameters.
-   */
   private JRealClassType createType(CompiledClass compiledClass,
-      JRealClassType enclosingType) {
-    TypeDeclaration typeDecl = compiledClass.getTypeDeclaration();
-    SourceTypeBinding binding = typeDecl.binding;
-    assert (binding.constantPoolName() != null);
-
+      CollectClassData classData, CollectClassData enclosingClassData) {
+    int access = classData.getAccess();
     String qname = compiledClass.getSourceName();
     String className = Shared.getShortName(qname);
+    JRealClassType resultType = null;
     String jpkgName = compiledClass.getPackageName();
     JPackage pkg = typeOracle.getOrCreatePackage(jpkgName);
-    boolean isLocalType = binding instanceof LocalTypeBinding;
-    boolean isIntf = TypeDeclaration.kind(typeDecl.modifiers) == TypeDeclaration.INTERFACE_DECL;
-    boolean isAnnotation = TypeDeclaration.kind(typeDecl.modifiers) == TypeDeclaration.ANNOTATION_TYPE_DECL;
-
-    JRealClassType resultType;
-    if (isAnnotation) {
-      resultType = new JAnnotationType(typeOracle, pkg, enclosingType,
+    boolean isIntf = (access & Opcodes.ACC_INTERFACE) != 0;
+    boolean isLocalType = classData.isLocal();
+    String enclosingTypeName = null;
+    if (enclosingClassData != null) {
+      enclosingTypeName = InternalName.toSourceName(InternalName.getClassName(enclosingClassData.getName()));
+    }
+    if ((access & Opcodes.ACC_ANNOTATION) != 0) {
+      resultType = new JAnnotationType(typeOracle, pkg, enclosingTypeName,
+          false, className, true);
+    } else if ((access & Opcodes.ACC_ENUM) != 0) {
+      resultType = new JEnumType(typeOracle, pkg, enclosingTypeName,
           isLocalType, className, isIntf);
-    } else if (maybeGeneric(typeDecl, enclosingType)) {
-      // Go through and create declarations for each of the type parameters on
-      // the generic class or method
-      JTypeParameter[] jtypeParameters = declareTypeParameters(typeDecl.typeParameters);
-
-      JGenericType jgenericType = new JGenericType(typeOracle, pkg,
-          enclosingType, isLocalType, className, isIntf, jtypeParameters);
-
-      resultType = jgenericType;
-    } else if (binding.isEnum()) {
-      resultType = new JEnumType(typeOracle, pkg, enclosingType, isLocalType,
-          className, isIntf);
     } else {
-      resultType = new JRealClassType(typeOracle, pkg, enclosingType,
-          isLocalType, className, isIntf);
+      JTypeParameter[] typeParams = getTypeParametersForClass(classData);
+      if ((typeParams != null && typeParams.length > 0)
+          || nonStaticInsideGeneric(classData, enclosingClassData)) {
+        resultType = new JGenericType(typeOracle, pkg, enclosingTypeName,
+            isLocalType, className, isIntf, typeParams);
+      } else {
+        resultType = new JRealClassType(typeOracle, pkg, enclosingTypeName,
+            isLocalType, className, isIntf);
+      }
     }
 
     /*
      * Declare type parameters for all methods; we must do this during the first
      * pass.
      */
-    if (typeDecl.methods != null) {
-      for (AbstractMethodDeclaration method : typeDecl.methods) {
-        declareTypeParameters(method.typeParameters());
-      }
-    }
-
+    // if (typeDecl.methods != null) {
+    // for (AbstractMethodDeclaration method : typeDecl.methods) {
+    // declareTypeParameters(method.typeParameters());
+    // }
+    // }
     /*
      * Add modifiers since these are needed for
      * TypeOracle.getParameterizedType's error checking code.
      */
-    resultType.addModifierBits(Shared.bindingToModifierBits(binding));
+    resultType.addModifierBits(mapBits(ASM_TO_SHARED_MODIFIERS, access));
+    if (isIntf) {
+      // Always add implicit modifiers on interfaces.
+      resultType.addModifierBits(Shared.MOD_STATIC | Shared.MOD_ABSTRACT);
+    }
+
     return resultType;
   }
 
-  private JClassType[] createTypeParameterBounds(TreeLogger logger,
-      TypeVariableBinding tvBinding) {
-    TypeBinding firstBound = tvBinding.firstBound;
-    if (firstBound == null) {
-      // No bounds were specified, so we default to Object. We assume that the
-      // superclass field of a TypeVariableBinding object is a Binding
-      // for a java.lang.Object, and we use this binding to find the
-      // JClassType for java.lang.Object. To be sure that our assumption
-      // about the superclass field is valid, we perform a runtime check
-      // against the name of the resolved type.
-
-      // You may wonder why we have to go this roundabout way to find a
-      // JClassType for java.lang.Object. The reason is that the TypeOracle
-      // has not been constructed as yet, so we cannot simply call
-      // TypeOracle.getJavaLangObject().
-      JClassType jimplicitUpperBound = (JClassType) resolveType(logger,
-          tvBinding.superclass);
-      if (jimplicitUpperBound != null) {
-        assert (Object.class.getName().equals(jimplicitUpperBound.getQualifiedSourceName()));
-        return new JClassType[] {jimplicitUpperBound};
-      }
-
-      // Failed to resolve the implicit upper bound.
-      return null;
-    }
-
-    List<JClassType> bounds = new ArrayList<JClassType>();
-    JClassType jfirstBound = (JClassType) resolveType(logger, firstBound);
-    if (jfirstBound == null) {
-      return null;
-    }
-
-    bounds.add(jfirstBound);
-
-    ReferenceBinding[] superInterfaces = tvBinding.superInterfaces();
-    for (ReferenceBinding superInterface : superInterfaces) {
-      if (superInterface != firstBound) {
-        JClassType jsuperInterface = (JClassType) resolveType(logger,
-            superInterface);
-        if (jsuperInterface == null) {
+  private JRealClassType createType(CompiledClass compiledClass,
+      Set<JRealClassType> unresolvedTypes) {
+    JRealClassType realClassType = compiledClass.getRealClassType();
+    if (realClassType == null) {
+      CollectClassData classData = classMap.get(compiledClass.getInternalName());
+      String outerClassName = classData.getOuterClass();
+      CollectClassData enclosingClassData = null;
+      if (outerClassName != null) {
+        enclosingClassData = classMap.get(outerClassName);
+        if (enclosingClassData == null) {
+          // if our enclosing class was skipped, skip this one too
           return null;
         }
-        bounds.add(jsuperInterface);
-      } else {
-        /*
-         * If the first bound was an interface JDT will still include it in the
-         * set of superinterfaces. So, we ignore it since we have already added
-         * it to the bounds.
-         */
       }
+      realClassType = createType(compiledClass, classData, enclosingClassData);
+      unresolvedTypes.add(realClassType);
+      compiledClass.setRealClassType(realClassType);
     }
-
-    return bounds.toArray(NO_JCLASSES);
+    return realClassType;
   }
 
-  /**
-   * Declares TypeParameters declared on a JGenericType or a JAbstractMethod by
-   * mapping the TypeParameters into JTypeParameters. <p/> This mapping has to
-   * be done on the first pass through the AST in order to handle declarations
-   * of the form: <<C exends GenericClass<T>, T extends SimpleClass> <p/> JDT
-   * already knows that GenericClass<T> is a parameterized type with a type
-   * argument of <T extends SimpleClass>. Therefore, in order to resolve
-   * GenericClass<T>, we must have knowledge of <T extends SimpleClass>. <p/>
-   * By calling this method on the first pass through the AST, a JTypeParameter
-   * for <T extends SimpleClass> will be created.
-   */
-  private JTypeParameter[] declareTypeParameters(TypeParameter[] typeParameters) {
-    if (typeParameters == null || typeParameters.length == 0) {
-      return null;
+  private Class<? extends Annotation> getAnnotationClass(TreeLogger logger,
+      AnnotationData annotData) {
+    Type type = Type.getType(annotData.getDesc());
+    JType resolvedType = resolveType(type);
+    if (resolvedType != null) {
+      return getClassLiteral(logger, Annotation.class, resolvedType);
     }
-
-    JTypeParameter[] jtypeParamArray = new JTypeParameter[typeParameters.length];
-    for (int i = 0; i < typeParameters.length; ++i) {
-      TypeParameter typeParam = typeParameters[i];
-      jtypeParamArray[i] = new JTypeParameter(String.valueOf(typeParam.name), i);
-      tvMapper.put(typeParam.binding, jtypeParamArray[i]);
-    }
-
-    return jtypeParamArray;
-  }
-
-  /**
-   * Returns an annotation element value as defined in JLS 3.0 section 9.7.
-   * 
-   * @param logger
-   * @param expectedElementValueType the expected element value type
-   * @param elementValueExpression the expression that defines the element value
-   * 
-   * @return annotation element value as defined in JLS 3.0 section 9.7
-   */
-  @SuppressWarnings("unchecked")
-  private Object getAnnotationElementValue(TreeLogger logger,
-      TypeBinding expectedElementValueType, Expression elementValueExpression) {
-
-    Object elementValue = null;
-
-    if (elementValueExpression.constant != null
-        && elementValueExpression.constant != Constant.NotAConstant) {
-      /*
-       * Rely on JDT's computed constant value to deal with an
-       * AnnotationElementValue expression whose resolved type is a primitive or
-       * a string.
-       */
-      Constant constant = elementValueExpression.constant;
-      int expectedTypeId = expectedElementValueType.id;
-
-      if (expectedElementValueType.isArrayType()) {
-        /*
-         * This can happen when an element value is an array with a single
-         * element. In this case JLS 3.0 section 9.7 allows for the
-         * ArrayInitializer expression to be implicit. Since, element values can
-         * only be single dimensional arrays, we take the leaf type of the
-         * expected array type as our resultant element value type.
-         */
-        assert (!elementValueExpression.resolvedType.isArrayType() && expectedElementValueType.dimensions() == 1);
-
-        expectedTypeId = expectedElementValueType.leafComponentType().id;
-      }
-
-      if (elementValueExpression.resolvedType.id != expectedTypeId) {
-        /*
-         * Narrowing and widening conversions are handled by the following
-         * Constant.castTo call. JDT wants the upper four bits of this mask to
-         * be the target type id and the lower four bits to be the source type
-         * id. See Constant.castTo for more details.
-         */
-        constant = constant.castTo((expectedTypeId << 4)
-            + elementValueExpression.resolvedType.id);
-      }
-
-      elementValue = getConstantValue(constant);
-    } else if (elementValueExpression instanceof ClassLiteralAccess) {
-      ClassLiteralAccess classLiteral = (ClassLiteralAccess) elementValueExpression;
-      elementValue = getClassLiteral(logger, classLiteral.targetType);
-    } else if (elementValueExpression instanceof ArrayInitializer) {
-      elementValue = getAnnotationElementValueArray(logger,
-          (ArrayInitializer) elementValueExpression);
-    } else if (elementValueExpression instanceof NameReference) {
-      /*
-       * Any primitive types, conditionals, strings, arrays and name references
-       * to constant fields will have all been handled by the constant
-       * expression block above. This name reference can only be for an
-       * enumerated type.
-       */
-      NameReference nameRef = (NameReference) elementValueExpression;
-
-      assert (nameRef.constant == null || nameRef.constant == Constant.NotAConstant);
-      assert (nameRef.actualReceiverType.isEnum());
-
-      Class<?> clazz = getClassLiteral(logger, nameRef.actualReceiverType);
-      Class<? extends Enum> enumClass = clazz.asSubclass(Enum.class);
-
-      String enumName = String.valueOf(nameRef.fieldBinding().name);
-      elementValue = Enum.valueOf(enumClass, enumName);
-    } else if (elementValueExpression instanceof Annotation) {
-      elementValue = createAnnotationInstance(logger, elementValueExpression);
-    } else {
-      assert (false);
-      return null;
-    }
-
-    assert (elementValue != null);
-
-    if (expectedElementValueType.isArrayType()
-        && !elementValue.getClass().isArray()) {
-      /*
-       * Handle single element arrays where no explicit array initializer was
-       * given.
-       */
-      Object array = Array.newInstance(elementValue.getClass(), 1);
-      Array.set(array, 0, elementValue);
-      elementValue = array;
-    }
-
-    return elementValue;
-  }
-
-  /**
-   * Returns an annotation element value array. These arrays can only have a
-   * single dimension.
-   */
-  private Object getAnnotationElementValueArray(TreeLogger logger,
-      ArrayInitializer arrayInitializer) {
-    assert (arrayInitializer.binding.dimensions == 1);
-
-    Class<?> leafComponentClass = getClassLiteral(logger,
-        arrayInitializer.binding.leafComponentType);
-    if (leafComponentClass == null) {
-      return null;
-    }
-
-    Expression[] initExpressions = arrayInitializer.expressions;
-    int arrayLength = initExpressions != null ? initExpressions.length : 0;
-
-    Object array = Array.newInstance(leafComponentClass, arrayLength);
-    boolean failed = false;
-    for (int i = 0; i < arrayLength; ++i) {
-      Expression arrayInitExp = initExpressions[i];
-      Object value = getAnnotationElementValue(logger,
-          arrayInitializer.binding.leafComponentType, arrayInitExp);
-      if (value != null) {
-        Array.set(array, i, value);
-      } else {
-        failed = true;
-        break;
-      }
-    }
-
-    if (!failed) {
-      return array;
-    }
-
-    return null;
-  }
-
-  private Class<?> getClassLiteral(TreeLogger logger, TypeBinding resolvedType) {
-    if (resolvedType instanceof BaseTypeBinding) {
-      return getClassLiteralForPrimitive((BaseTypeBinding) resolvedType);
-    } else {
-      try {
-        String className = String.valueOf(resolvedType.constantPoolName());
-        className = className.replace('/', '.');
-        return Class.forName(className, false,
-            Thread.currentThread().getContextClassLoader());
-      } catch (ClassNotFoundException e) {
-        logger.log(TreeLogger.ERROR, "", e);
+    try {
+      Class<?> clazz = Class.forName(type.getClassName(), false,
+          getClass().getClassLoader());
+      if (!Annotation.class.isAssignableFrom(clazz)) {
+        logger.log(TreeLogger.ERROR, "Binary-only type " + type.getClassName()
+            + " is not an annotation");
         return null;
       }
+      return clazz.asSubclass(Annotation.class);
+    } catch (ClassNotFoundException e) {
+      logger.log(TreeLogger.WARN, "Ignoring unresolvable annotation type "
+          + type.getClassName(), e);
+      return null;
     }
   }
 
-  private Class<?> getClassLiteralForPrimitive(BaseTypeBinding type) {
-    switch (type.id) {
-      case TypeIds.T_boolean:
+  @SuppressWarnings("unused")
+  private Class<?> getClassLiteralForPrimitive(Type type) {
+    switch (type.getSort()) {
+      case Type.BOOLEAN:
         return Boolean.TYPE;
-      case TypeIds.T_byte:
+      case Type.BYTE:
         return Byte.TYPE;
-      case TypeIds.T_char:
+      case Type.CHAR:
         return Character.TYPE;
-      case TypeIds.T_short:
+      case Type.SHORT:
         return Short.TYPE;
-      case TypeIds.T_int:
+      case Type.INT:
         return Integer.TYPE;
-      case TypeIds.T_long:
+      case Type.LONG:
         return Long.TYPE;
-      case TypeIds.T_float:
+      case Type.FLOAT:
         return Float.TYPE;
-      case TypeIds.T_double:
+      case Type.DOUBLE:
         return Double.TYPE;
-      case TypeIds.T_void:
+      case Type.VOID:
         return Void.TYPE;
       default:
-        assert false : "Unexpected base type id " + type.id;
+        assert false : "Unexpected primitive type " + type;
         return null;
     }
   }
 
   /**
-   * Returns the qualified name of the binding, excluding any type parameter
-   * information.
+   * Map a bitset onto a different bitset.
+   * 
+   * @param mapping int array containing a sequence of from/to pairs, each from
+   *          entry should have exactly one bit set
+   * @param input bitset to map
+   * @return mapped bitset
    */
-  private String getQualifiedName(ReferenceBinding binding) {
-    String qualifiedName = CharOperation.toString(binding.compoundName);
-    if (binding instanceof LocalTypeBinding) {
-      // The real name of a local type is its constant pool name.
-      qualifiedName = CharOperation.charToString(binding.constantPoolName());
-      qualifiedName = qualifiedName.replace('/', '.');
-    } else {
-      /*
-       * All other types have their fully qualified name as part of its compound
-       * name.
-       */
-      qualifiedName = CharOperation.toString(binding.compoundName);
+  private int mapBits(int[] mapping, int input) {
+    int output = 0;
+    for (int i = 0; i < mapping.length; i += 2) {
+      if ((input & mapping[i]) != 0) {
+        output |= mapping[i + 1];
+      }
     }
-
-    qualifiedName = qualifiedName.replace('$', '.');
-    return qualifiedName;
+    return output;
   }
 
-  private boolean resolveAnnotation(
-      TreeLogger logger,
-      Annotation jannotation,
-      Map<Class<? extends java.lang.annotation.Annotation>, java.lang.annotation.Annotation> declaredAnnotations) {
 
-    logger = logger.branch(TreeLogger.SPAM, "Resolving annotation '"
-        + jannotation.printExpression(0, new StringBuffer()).toString() + "'",
-        null);
+  /**
+   * Collects data about a class which only needs the bytecode and no TypeOracle
+   * data structures. This is used to make the initial shallow identity pass for
+   * creating JRealClassType/JGenericType objects.
+   */
+  private CollectClassData processClass(CompiledClass compiledClass) {
+    byte[] classBytes = compiledClass.getBytes();
+    ClassReader reader = new ClassReader(classBytes);
+    CollectClassData mcv = new CollectClassData(classBytes);
+    ClassVisitor cv = mcv;
+    if (false) {
+      cv = new TraceClassVisitor(cv, new PrintWriter(System.out));
+    }
+    reader.accept(cv, 0);
+    return mcv;
+  }
 
-    // Determine the annotation class
-    TypeBinding resolvedType = jannotation.resolvedType;
-    Class<?> classLiteral = getClassLiteral(logger, resolvedType);
-    if (classLiteral == null) {
+  private boolean resolveAnnotation(TreeLogger logger,
+      CollectAnnotationData annotVisitor,
+      Map<Class<? extends Annotation>, Annotation> declaredAnnotations) {
+    AnnotationData annotData = annotVisitor.getAnnotation();
+    Class<? extends Annotation> annotationClass = getAnnotationClass(logger,
+        annotData);
+    if (annotationClass == null) {
       return false;
     }
-
-    java.lang.annotation.Annotation annotation = (java.lang.annotation.Annotation) createAnnotationInstance(
-        logger, jannotation);
-    if (annotation == null) {
+    Annotation annotInstance = createAnnotation(logger, annotationClass,
+        annotData);
+    if (annotInstance == null) {
       return false;
     }
-
-    Class<? extends java.lang.annotation.Annotation> clazz = classLiteral.asSubclass(java.lang.annotation.Annotation.class);
-    // Do not reflect source-only annotations.
-    if (getRetentionPolicy(clazz) != RetentionPolicy.SOURCE) {
-      declaredAnnotations.put(clazz, annotation);
-    }
+    declaredAnnotations.put(annotationClass, annotInstance);
     return true;
   }
 
-  private boolean resolveAnnotations(
-      TreeLogger logger,
-      Annotation[] annotations,
-      Map<Class<? extends java.lang.annotation.Annotation>, java.lang.annotation.Annotation> declaredAnnotations) {
+  private boolean resolveAnnotations(TreeLogger logger,
+      List<CollectAnnotationData> annotations,
+      Map<Class<? extends Annotation>, Annotation> declaredAnnotations) {
     boolean succeeded = true;
     if (annotations != null) {
-      for (Annotation annotation : annotations) {
+      for (CollectAnnotationData annotation : annotations) {
         succeeded &= resolveAnnotation(logger, annotation, declaredAnnotations);
       }
     }
     return succeeded;
   }
 
-  private boolean resolveBoundForTypeParameter(TreeLogger logger,
-      HasTypeParameters genericElement, TypeParameter typeParameter, int ordinal) {
-    JClassType[] jbounds = createTypeParameterBounds(logger,
-        typeParameter.binding);
-    if (jbounds == null) {
+  @SuppressWarnings("unchecked")
+  private Object resolveAnnotationValue(TreeLogger logger,
+      Class<?> expectedType, Object value) {
+    if (expectedType.isArray()) {
+      Class<?> componentType = expectedType.getComponentType();
+      if (!value.getClass().isArray()) {
+        logger.log(TreeLogger.WARN, "Annotation error: expected array of "
+            + componentType.getCanonicalName() + ", got "
+            + value.getClass().getCanonicalName());
+        return null;
+      }
+      // resolve each element in the array
+      int n = Array.getLength(value);
+      Object newArray = Array.newInstance(componentType, n);
+      for (int i = 0; i < n; ++i) {
+        Object valueElement = Array.get(value, i);
+        Object resolvedValue = resolveAnnotationValue(logger, componentType,
+            valueElement);
+        if (resolvedValue == null
+            || !componentType.isAssignableFrom(resolvedValue.getClass())) {
+          logger.log(TreeLogger.ERROR, "Annotation error: expected "
+              + componentType + ", got " + resolvedValue);
+        } else {
+          Array.set(newArray, i, resolvedValue);
+        }
+      }
+      return newArray;
+    } else if (expectedType.isEnum()) {
+      if (!(value instanceof AnnotationEnum)) {
+        logger.log(TreeLogger.ERROR,
+            "Annotation error: expected an enum value," + " but got " + value);
+        return null;
+      }
+      AnnotationEnum annotEnum = (AnnotationEnum) value;
+      Type type = Type.getType(annotEnum.getDesc());
+      JType resolvedType = resolveType(type);
+      if (resolvedType == null) {
+        logger.log(TreeLogger.WARN,
+            "Unable to resolve annotation enum type of " + type.getClassName());
+        return null;
+      }
+      String enumValue = annotEnum.getValue();
+      // TODO: is there a way to avoid the SuppressWarning(unchecked) here?
+      Class<? extends Enum> enumType = getClassLiteral(logger, Enum.class,
+          resolvedType);
+      if (enumType == null) {
+        logger.log(TreeLogger.WARN,
+            "Unable to resolve annotation enum type of "
+                + resolvedType.getQualifiedSourceName());
+        return null;
+      }
+      return Enum.valueOf(enumType, enumValue);
+    } else if (Annotation.class.isAssignableFrom(expectedType)) {
+      if (!(value instanceof AnnotationData)) {
+        logger.log(TreeLogger.WARN,
+            "Annotation error: expected annotation type "
+                + expectedType.getCanonicalName() + ", got "
+                + value.getClass().getCanonicalName());
+        return null;
+      }
+      AnnotationData annotData = (AnnotationData) value;
+      Class<? extends Annotation> annotationClass = getAnnotationClass(logger,
+          annotData);
+      if (!expectedType.isAssignableFrom(annotationClass)) {
+        logger.log(TreeLogger.WARN, "Annotation error: expected "
+            + expectedType.getCanonicalName() + ", got "
+            + annotationClass.getCanonicalName());
+        return null;
+      }
+      return createAnnotation(logger, annotationClass, annotData);
+    } else if (expectedType.isPrimitive()) {
+      Class<?> wrapper = getWrapperClass(expectedType);
+      return wrapper.cast(value);
+    } else {
+      if (expectedType.isAssignableFrom(value.getClass())) {
+        return value;
+      }
+      if (Class.class.equals(expectedType)) {
+        if (!(value instanceof Type)) {
+          logger.log(TreeLogger.WARN, "Annotation error: expected a class "
+              + "literal, but received " + value);
+          return null;
+        }
+        Type valueType = (Type) value;
+        JRealClassType objType = resolveObject(valueType);
+        if (objType == null) {
+          // See if we can use a binary only class here
+          try {
+            return Class.forName(valueType.getClassName(), false,
+                getClass().getClassLoader());
+          } catch (ClassNotFoundException e) {
+            logger.log(TreeLogger.ERROR, "Annotation error: cannot resolve "
+                + valueType.getClassName(), e);
+            return null;
+          }
+        }
+        // TODO(jat): other checks?
+        try {
+          // TODO(jat): is it safe to use Class.forName here?
+          return Class.forName(objType.getQualifiedBinaryName(), false,
+              TypeOracleMediator.class.getClassLoader());
+        } catch (ClassNotFoundException e) {
+          logger.log(TreeLogger.WARN, "Unable to load class literal for "
+              + objType, e);
+          return null;
+        }
+      }
+      // TODO(jat) asserts about other acceptable types
+      return value;
+    }
+  }
+
+  private JType resolveArray(Type type) {
+    assert type.getSort() == Type.ARRAY;
+    JType resolvedType = resolveType(type.getElementType());
+    int dims = type.getDimensions();
+    for (int i = 0; i < dims; ++i) {
+      resolvedType = typeOracle.getArrayType(resolvedType);
+    }
+    return resolvedType;
+  }
+
+  private boolean resolveClass(TreeLogger logger, JRealClassType type) {
+    assert type != null;
+    // Avoid cycles and useless computation.
+    if (resolved.contains(type)) {
+      return true;
+    }
+    resolved.add(type);
+
+    // Make sure our enclosing type is resolved first.
+    if (type.getEnclosingType() != null
+        && !resolveClass(logger, type.getEnclosingType())) {
       return false;
     }
 
-    genericElement.getTypeParameters()[ordinal].setBounds(jbounds);
+    // Build a search list for type parameters to find their definition,
+    // resolving enclosing classes as we go up.
+    TypeParameterLookup typeParamLookup = new TypeParameterLookup();
+    typeParamLookup.pushEnclosingScopes(type);
+
+    CollectClassData classData = classMapType.get(type);
+    assert classData != null;
+    int access = classData.getAccess();
+
+    assert (!classData.getClassType().isLocal());
+
+    logger = logger.branch(TreeLogger.SPAM, "Found type '"
+        + type.getQualifiedSourceName() + "'", null);
+
+    // Handle package-info classes.
+    if (isPackageInfoTypeName(type.getSimpleSourceName())) {
+      return resolvePackage(logger, type, classData.getAnnotations());
+    }
+
+    // Resolve annotations
+    Map<Class<? extends Annotation>, Annotation> declaredAnnotations = new HashMap<Class<? extends Annotation>, Annotation>();
+    resolveAnnotations(logger, classData.getAnnotations(), declaredAnnotations);
+    type.addAnnotations(declaredAnnotations);
+
+    String signature = classData.getSignature();
+    if (signature != null) {
+      // If we have a signature, use it for superclass and interfaces
+      SignatureReader reader = new SignatureReader(signature);
+      ResolveClassSignature classResolver = new ResolveClassSignature(
+          resolver, binaryMapper, logger, type, typeParamLookup);
+      reader.accept(classResolver);
+      classResolver.finish();
+    } else {
+      // Set the super type for non-interfaces
+      if ((access & Opcodes.ACC_INTERFACE) == 0) {
+        String superName = classData.getSuperName();
+        if (superName != null) {
+          JClassType superType = binaryMapper.get(superName);
+          if (superType == null || !resolveClass(logger, superType)) {
+            logger.log(TreeLogger.WARN, "Unable to resolve supertype "
+                + superName);
+            return false;
+          }
+          type.setSuperclass((JClassType) possiblySubstituteRawType(superType));
+        }
+      }
+
+      // Set interfaces
+      for (String intfName : classData.getInterfaces()) {
+        JClassType intf = binaryMapper.get(intfName);
+        if (intf == null || !resolveClass(logger, intf)) {
+          logger.log(TreeLogger.WARN, "Unable to resolve interface " + intfName);
+          return false;
+        }
+        type.addImplementedInterface((JClassType) possiblySubstituteRawType(intf));
+      }
+    }
+    if (((access & Opcodes.ACC_INTERFACE) == 0) && type.getSuperclass() == null) {
+      // Only Object or interfaces should not have a superclass
+      assert "java/lang/Object".equals(classData.getName());
+    }
+
+    // Process methods
+    for (CollectMethodData method : classData.getMethods()) {
+      if (!resolveMethod(logger, type, method, typeParamLookup)) {
+        logger.log(TreeLogger.WARN, "Unable to resolve method " + method);
+        return false;
+      }
+    }
+
+    // Process fields
+    // Track the next enum ordinal across resolveField calls.
+    int[] nextEnumOrdinal = new int[] {0};
+    for (CollectFieldData field : classData.getFields()) {
+      if (!resolveField(logger, type, field, typeParamLookup, nextEnumOrdinal)) {
+        logger.log(TreeLogger.WARN, "Unable to resolve field " + field);
+        return false;
+      }
+    }
+
     return true;
   }
 
-  private boolean resolveBoundsForTypeParameters(TreeLogger logger,
-      HasTypeParameters genericElement, TypeParameter[] typeParameters) {
-    if (typeParameters != null) {
-      for (int i = 0; i < typeParameters.length; ++i) {
-        if (!resolveBoundForTypeParameter(logger, genericElement,
-            typeParameters[i], i)) {
+  private boolean resolveClass(TreeLogger logger, JType type) {
+    if (!(type instanceof JClassType)) {
+      // non-classes are already resolved
+      return true;
+    }
+    if (type instanceof JRealClassType) {
+      return resolveClass(logger, (JRealClassType) type);
+    }
+    if (type instanceof JArrayType) {
+      return resolveClass(logger, ((JArrayType) type).getComponentType());
+    }
+    if (type instanceof JParameterizedType) {
+      return resolveClass(logger, ((JParameterizedType) type).getBaseType());
+    }
+    if (type instanceof JRawType) {
+      return resolveClass(logger, ((JRawType) type).getBaseType());
+    }
+    if (type instanceof JTypeParameter) {
+      JTypeParameter typeParam = (JTypeParameter) type;
+      if (!resolveClass(logger, typeParam.getDeclaringClass())) {
+        return false;
+      }
+      for (JClassType bound : typeParam.getBounds()) {
+        if (!resolveClass(logger, bound)) {
           return false;
         }
       }
+      return true;
+    }
+    if (type instanceof JWildcardType) {
+      JWildcardType wildcard = (JWildcardType) type;
+      for (JClassType bound : wildcard.getUpperBounds()) {
+        if (!resolveClass(logger, bound)) {
+          return false;
+        }
+      }
+      for (JClassType bound : wildcard.getLowerBounds()) {
+        if (!resolveClass(logger, bound)) {
+          return false;
+        }
+      }
+      return true;
+    }
+    return false;
+  }
+
+  private boolean resolveEnclosingClass(TreeLogger logger, JRealClassType type) {
+    assert type != null;
+    if (type.getEnclosingType() != null) {
+      return true;
+    }
+    // Find our enclosing class and set it
+    CollectClassData classData = classMapType.get(type);
+    assert classData != null;
+    String outerClass = classData.getOuterClass();
+    JRealClassType enclosingType = null;
+    if (outerClass != null) {
+      enclosingType = binaryMapper.get(outerClass);
+      // Ensure enclosing classes are resolved
+      if (enclosingType != null) {
+        if (!resolveEnclosingClass(logger, enclosingType)) {
+          return false;
+        }
+        if (enclosingType.isGenericType() != null
+            && (classData.getAccess() & (Opcodes.ACC_STATIC | Opcodes.ACC_INTERFACE)) != 0) {
+          // If the inner class doesn't have access to it's enclosing type's
+          // type variables, the enclosign type must be the raw type instead
+          // of the generic type.
+          JGenericType genericType = enclosingType.isGenericType();
+          type.setEnclosingType(genericType.getRawType());
+        } else {
+          type.setEnclosingType(enclosingType);
+        }
+      }
     }
     return true;
   }
 
-  private boolean resolveField(TreeLogger logger, JClassType enclosingType,
-      FieldDeclaration jfield) {
-
-    if (jfield instanceof Initializer) {
-      // Pretend we didn't see this.
-      //
-      return true;
-    }
-
-    // Try to resolve annotations, ignore any that fail.
-    Map<Class<? extends java.lang.annotation.Annotation>, java.lang.annotation.Annotation> declaredAnnotations = newAnnotationMap();
-    resolveAnnotations(logger, jfield.annotations, declaredAnnotations);
-
-    String name = String.valueOf(jfield.name);
-    JField field;
-    if (jfield.getKind() == AbstractVariableDeclaration.ENUM_CONSTANT) {
-      assert (enclosingType.isEnum() != null);
-      field = new JEnumConstant(enclosingType, name, declaredAnnotations,
-          jfield.binding.original().id);
+  private boolean resolveField(TreeLogger logger, JRealClassType type,
+      CollectFieldData field, TypeParameterLookup typeParamLookup,
+      int[] nextEnumOrdinal) {
+    Map<Class<? extends Annotation>, Annotation> declaredAnnotations = new HashMap<Class<? extends Annotation>, Annotation>();
+    resolveAnnotations(logger, field.getAnnotations(), declaredAnnotations);
+    String name = field.getName();
+    JField jfield;
+    if ((field.getAccess() & Opcodes.ACC_ENUM) != 0) {
+      assert (type.isEnum() != null);
+      jfield = new JEnumConstant(type, name, declaredAnnotations,
+          nextEnumOrdinal[0]++);
     } else {
-      field = new JField(enclosingType, name, declaredAnnotations);
+      jfield = new JField(type, name, declaredAnnotations);
     }
 
     // Get modifiers.
     //
-    field.addModifierBits(Shared.bindingToModifierBits(jfield.binding));
+    jfield.addModifierBits(mapBits(ASM_TO_SHARED_MODIFIERS, field.getAccess()));
 
-    // Set the field type.
-    //
-    TypeBinding jfieldType = jfield.binding.type;
+    String signature = field.getSignature();
+    JType fieldType;
+    if (signature != null) {
+      SignatureReader reader = new SignatureReader(signature);
+      JType[] fieldTypeRef = new JType[1];
+      reader.acceptType(new ResolveTypeSignature(resolver, binaryMapper,
+          logger, fieldTypeRef, typeParamLookup, null));
+      fieldType = fieldTypeRef[0];
+      // TraceSignatureVisitor trace = new TraceSignatureVisitor(
+      // methodData.getAccess());
+      // reader.acceptType(trace);
+      // System.err.println("Field " + name + ": " + trace.getDeclaration());
 
-    JType fieldType = resolveType(logger, jfieldType);
+    } else {
+      fieldType = resolveType(Type.getType(field.getDesc()));
+    }
     if (fieldType == null) {
-      // Unresolved type.
-      //
       return false;
     }
-    field.setType(fieldType);
-
+    jfield.setType(fieldType);
     return true;
   }
 
-  private boolean resolveFields(TreeLogger logger, JClassType type,
-      FieldDeclaration[] jfields) {
-    if (jfields != null) {
-      for (int i = 0; i < jfields.length; i++) {
-        FieldDeclaration jfield = jfields[i];
-        if (!resolveField(logger, type, jfield)) {
-          return false;
-        }
-      }
-    }
-    return true;
-  }
+  private boolean resolveMethod(TreeLogger logger, JRealClassType type,
+      CollectMethodData methodData, TypeParameterLookup typeParamLookup) {
+    Map<Class<? extends Annotation>, Annotation> declaredAnnotations = new HashMap<Class<? extends Annotation>, Annotation>();
+    resolveAnnotations(logger, methodData.getAnnotations(), declaredAnnotations);
+    String name = methodData.getName();
 
-  private boolean resolveMethod(TreeLogger logger, JClassType enclosingType,
-      AbstractMethodDeclaration jmethod) {
-
-    if (jmethod instanceof Clinit) {
-      // Pretend we didn't see this.
-      //
+    if ("<clinit>".equals(name)
+        || (methodData.getAccess() & Opcodes.ACC_SYNTHETIC) != 0) {
+      // Ignore the following and leave them out of TypeOracle:
+      // - static initializers
+      // - synthetic methods
       return true;
     }
 
-    String name = getMethodName(enclosingType, jmethod);
-
-    // Try to resolve annotations, ignore any that fail.
-    Map<Class<? extends java.lang.annotation.Annotation>, java.lang.annotation.Annotation> declaredAnnotations = newAnnotationMap();
-    resolveAnnotations(logger, jmethod.annotations, declaredAnnotations);
+    if (type.isEnum() != null && "<init>".equals(name)) {
+      // Leave enum constructors out of TypeOracle
+      return true;
+    }
+    if (type.isEnum() != null
+        && (methodData.getAccess() & Opcodes.ACC_STATIC) != 0) {
+      // Special-case synthetic static methods that aren't marked synthetic
+      if ("values".equals(name) || "valueOf".equals(name)) {
+        return true;
+      }
+    }
 
     JAbstractMethod method;
 
     // Declare the type parameters. We will pass them into the constructors for
     // JConstructor/JMethod/JAnnotatedMethod. Then, we'll do a second pass to
     // resolve the bounds on each JTypeParameter object.
-    JTypeParameter[] jtypeParameters = resolveTypeParameters(jmethod.typeParameters());
+    JTypeParameter[] typeParams = collectTypeParams(methodData.getSignature());
 
-    if (jmethod.isConstructor()) {
-      method = new JConstructor(enclosingType, name, declaredAnnotations,
-          jtypeParameters);
-      // Do a second pass to resolve the bounds on each JTypeParameter.
-      if (!resolveBoundsForTypeParameters(logger, method,
-          jmethod.typeParameters())) {
-        return false;
-      }
+    typeParamLookup.pushScope(typeParams);
+    boolean hasReturnType = true;
+    if ("<init>".equals(name)) {
+      name = type.getSimpleSourceName();
+      method = new JConstructor(type, name, declaredAnnotations, typeParams);
+      hasReturnType = false;
     } else {
-      if (jmethod.isAnnotationMethod()) {
-        AnnotationMethodDeclaration annotationMethod = (AnnotationMethodDeclaration) jmethod;
-        Object defaultValue = null;
-        if (annotationMethod.defaultValue != null) {
-          defaultValue = getAnnotationElementValue(logger,
-              annotationMethod.returnType.resolvedType,
-              annotationMethod.defaultValue);
-        }
-        method = new JAnnotationMethod(enclosingType, name, defaultValue,
+      if (type.isAnnotation() != null) {
+        // TODO(jat): !! anything else to do here?
+        method = new JAnnotationMethod(type, name, typeParams,
             declaredAnnotations);
       } else {
-        method = new JMethod(enclosingType, name, declaredAnnotations,
-            jtypeParameters);
+        method = new JMethod(type, name, declaredAnnotations, typeParams);
       }
-
-      // Do a second pass to resolve the bounds on each JTypeParameter.
-      // The type parameters must be resolved at this point, because they may
-      // be used in the resolution of the method's return type.
-      if (!resolveBoundsForTypeParameters(logger, method,
-          jmethod.typeParameters())) {
-        return false;
-      }
-
-      TypeBinding jreturnType = ((MethodDeclaration) jmethod).returnType.resolvedType;
-      JType returnType = resolveType(logger, jreturnType);
-      if (returnType == null) {
-        // Unresolved type.
-        //
-        return false;
-      }
-      ((JMethod) method).setReturnType(returnType);
     }
 
-    // Parse modifiers.
-    //
-    method.addModifierBits(Shared.bindingToModifierBits(jmethod.binding));
-    if (enclosingType.isInterface() != null) {
+    // Copy modifier flags
+    method.addModifierBits(mapBits(ASM_TO_SHARED_MODIFIERS,
+        methodData.getAccess()));
+    if (type.isInterface() != null) {
       // Always add implicit modifiers on interface methods.
-      //
       method.addModifierBits(Shared.MOD_PUBLIC | Shared.MOD_ABSTRACT);
     }
 
-    // Add the parameters.
-    //
-    Argument[] jparams = jmethod.arguments;
-    if (!resolveParameters(logger, method, jparams)) {
-      return false;
-    }
-
-    // Add throws.
-    //
-    TypeReference[] jthrows = jmethod.thrownExceptions;
-    if (!resolveThrownTypes(logger, method, jthrows)) {
-      return false;
-    }
-
-    return true;
-  }
-
-  private boolean resolveMethods(TreeLogger logger, JClassType type,
-      AbstractMethodDeclaration[] jmethods) {
-    if (jmethods != null) {
-      for (int i = 0; i < jmethods.length; i++) {
-        AbstractMethodDeclaration jmethod = jmethods[i];
-        if (!resolveMethod(logger, type, jmethod)) {
-          return false;
-        }
-      }
-    }
-    return true;
-  }
-
-  private boolean resolvePackage(TreeLogger logger, TypeDeclaration jclass) {
-    SourceTypeBinding binding = jclass.binding;
-
-    String packageName = String.valueOf(binding.fPackage.readableName());
-    JPackage pkg = typeOracle.getOrCreatePackage(packageName);
-    assert (pkg != null);
-
-    CompilationUnitScope cus = (CompilationUnitScope) jclass.scope.parent;
-    assert (cus != null);
-
-    // Try to resolve annotations, ignore any that fail.
-    Map<Class<? extends java.lang.annotation.Annotation>, java.lang.annotation.Annotation> declaredAnnotations = newAnnotationMap();
-    resolveAnnotations(logger, cus.referenceContext.currentPackage.annotations,
-        declaredAnnotations);
-
-    pkg.addAnnotations(declaredAnnotations);
-    return true;
-  }
-
-  private boolean resolveParameter(TreeLogger logger, JAbstractMethod method,
-      Argument jparam) {
-    TypeBinding jtype = jparam.binding.type;
-    JType type = resolveType(logger, jtype);
-    if (type == null) {
-      // Unresolved.
-      //
-      return false;
-    }
-
-    // Try to resolve annotations, ignore any that fail.
-    Map<Class<? extends java.lang.annotation.Annotation>, java.lang.annotation.Annotation> declaredAnnotations = newAnnotationMap();
-    resolveAnnotations(logger, jparam.annotations, declaredAnnotations);
-
-    String name = String.valueOf(jparam.name);
-    new JParameter(method, type, name, declaredAnnotations);
-    if (jparam.isVarArgs()) {
+    if ((methodData.getAccess() & Opcodes.ACC_VARARGS) != 0) {
       method.setVarArgs();
     }
+
+    String signature = methodData.getSignature();
+    Type[] argTypes = methodData.getArgTypes();
+    String[] argNames = methodData.getArgNames();
+    if (signature != null) {
+      // If we have a signature, use it for superclass and interfaces
+      SignatureReader reader = new SignatureReader(signature);
+      ResolveMethodSignature methodResolver = new ResolveMethodSignature(
+          resolver, logger, method, typeParamLookup, hasReturnType, methodData,
+          argTypes, argNames);
+      // TraceSignatureVisitor trace = new TraceSignatureVisitor(
+      // methodData.getAccess());
+      // reader.accept(trace);
+      // System.err.println("Method " + name + ": " + trace.getDeclaration());
+      reader.accept(methodResolver);
+      if (!methodResolver.finish()) {
+        return false;
+      }
+    } else {
+      if (hasReturnType) {
+        Type returnType = Type.getReturnType(methodData.getDesc());
+        JType returnJType = resolveType(returnType);
+        if (returnJType == null) {
+          return false;
+        }
+        ((JMethod) method).setReturnType(returnJType);
+      }
+
+      if (!resolveParameters(logger, method, methodData)) {
+        return false;
+      }
+    }
+    // The signature might not actually include the exceptions if they don't
+    // include a type variable, so resolveThrows is always used (it does
+    // nothing if there are already exceptions defined)
+    if (!resolveThrows(method, methodData)) {
+      return false;
+    }
+    typeParamLookup.popScope();
+    return true;
+  }
+
+  private JRealClassType resolveObject(Type type) {
+    assert type.getSort() == Type.OBJECT;
+    String className = type.getInternalName();
+    assert Name.isInternalName(className);
+    JRealClassType classType = binaryMapper.get(className);
+    return classType;
+  }
+
+  private boolean resolvePackage(TreeLogger logger, JRealClassType type,
+      List<CollectAnnotationData> annotations) {
+    Map<Class<? extends Annotation>, Annotation> declaredAnnotations = new HashMap<Class<? extends Annotation>, Annotation>();
+    resolveAnnotations(logger, annotations, declaredAnnotations);
+    type.getPackage().addAnnotations(declaredAnnotations);
     return true;
   }
 
   private boolean resolveParameters(TreeLogger logger, JAbstractMethod method,
-      Argument[] jparams) {
-    if (jparams != null) {
-      for (int i = 0; i < jparams.length; i++) {
-        Argument jparam = jparams[i];
-        if (!resolveParameter(logger, method, jparam)) {
-          return false;
-        }
-      }
-    }
-    return true;
-  }
-
-  private boolean resolveThrownType(TreeLogger logger, JAbstractMethod method,
-      TypeReference jthrown) {
-
-    JType type = resolveType(logger, jthrown.resolvedType);
-    if (type == null) {
-      // Not resolved.
-      //
-      return false;
-    }
-
-    method.addThrows(type);
-
-    return true;
-  }
-
-  private boolean resolveThrownTypes(TreeLogger logger, JAbstractMethod method,
-      TypeReference[] jthrows) {
-    if (jthrows != null) {
-      for (int i = 0; i < jthrows.length; i++) {
-        TypeReference jthrown = jthrows[i];
-        if (!resolveThrownType(logger, method, jthrown)) {
-          return false;
-        }
-      }
-    }
-    return true;
-  }
-
-  private JType resolveType(TreeLogger logger, TypeBinding binding) {
-    // Check for primitives.
-    if (binding instanceof BaseTypeBinding) {
-      switch (binding.id) {
-        case TypeIds.T_boolean:
-          return JPrimitiveType.BOOLEAN;
-        case TypeIds.T_byte:
-          return JPrimitiveType.BYTE;
-        case TypeIds.T_char:
-          return JPrimitiveType.CHAR;
-        case TypeIds.T_short:
-          return JPrimitiveType.SHORT;
-        case TypeIds.T_int:
-          return JPrimitiveType.INT;
-        case TypeIds.T_long:
-          return JPrimitiveType.LONG;
-        case TypeIds.T_float:
-          return JPrimitiveType.FLOAT;
-        case TypeIds.T_double:
-          return JPrimitiveType.DOUBLE;
-        case TypeIds.T_void:
-          return JPrimitiveType.VOID;
-        default:
-          assert false : "Unexpected base type id " + binding.id;
-      }
-    }
-
-    /*
-     * Check for a user-defined type, which may be either a SourceTypeBinding or
-     * a RawTypeBinding. Both of these are subclasses of ReferenceBinding and
-     * all the functionality we need is on ReferenceBinding, so we cast it to
-     * that and deal with them in common code.
-     * 
-     * TODO: do we need to do anything more with raw types?
-     */
-    if (binding instanceof SourceTypeBinding
-        || binding instanceof RawTypeBinding) {
-      ReferenceBinding referenceBinding = (ReferenceBinding) binding;
-
-      // First check the type oracle to prefer type identity with the type
-      // oracle we're assimilating into.
-      //
-      String typeName = getQualifiedName(referenceBinding);
-      JType resolvedType = typeOracle.findType(typeName);
-      if (resolvedType == null) {
-        // Otherwise, it should be something we've mapped during this build.
-        resolvedType = sourceMapper.get(referenceBinding);
-      }
-
-      if (resolvedType != null) {
-        if (binding instanceof RawTypeBinding) {
-          // Use the raw type instead of the generic type.
-          JGenericType genericType = (JGenericType) resolvedType;
-          resolvedType = genericType.getRawType();
-        }
-        return resolvedType;
-      }
-    }
-
-    if (binding instanceof BinaryTypeBinding) {
-      // Try a binary lookup.
-      String binaryName = String.valueOf(binding.constantPoolName());
-      JRealClassType realClassType = binaryMapper.get(binaryName);
-      if (realClassType != null) {
-        return realClassType;
-      }
-    }
-
-    // Check for an array.
-    //
-    if (binding instanceof ArrayBinding) {
-      ArrayBinding arrayBinding = (ArrayBinding) binding;
-
-      // Start by resolving the leaf type.
-      //
-      TypeBinding leafBinding = arrayBinding.leafComponentType;
-      JType resolvedType = resolveType(logger, leafBinding);
-      if (resolvedType != null) {
-        int dims = arrayBinding.dimensions;
-        for (int i = 0; i < dims; ++i) {
-          // By using the oracle to intern, we guarantee correct identity
-          // mapping of lazily-created array types.
-          //
-          resolvedType = typeOracle.getArrayType(resolvedType);
-        }
-        return resolvedType;
-      } else {
-        // Fall-through to failure.
-        //
-      }
-    }
-
-    // Check for parameterized.
-    if (binding instanceof ParameterizedTypeBinding) {
-      ParameterizedTypeBinding ptBinding = (ParameterizedTypeBinding) binding;
-
-      /*
-       * NOTE: it is possible for ParameterizedTypeBinding.arguments to be null.
-       * This can happen if a generic class has a non-static, non-generic, inner
-       * class that references a TypeParameter from its enclosing generic type.
-       * You would think that typeVariables() would do the right thing but it
-       * does not.
-       */
-      TypeBinding[] arguments = ptBinding.arguments;
-      int nArguments = arguments != null ? arguments.length : 0;
-      JClassType[] typeArguments = new JClassType[nArguments];
-      boolean failed = false;
-      for (int i = 0; i < typeArguments.length; ++i) {
-        typeArguments[i] = (JClassType) resolveType(logger, arguments[i]);
-        if (typeArguments[i] == null) {
-          failed = true;
-        }
-      }
-
-      JClassType enclosingType = null;
-      if (ptBinding.enclosingType() != null) {
-        enclosingType = (JClassType) resolveType(logger,
-            ptBinding.enclosingType());
-        if (enclosingType == null) {
-          failed = true;
-        }
-      }
-
-      /*
-       * NOTE: In the case where a generic type has a nested, non-static,
-       * non-generic type. The type for the binding will not be a generic type.
-       */
-      JType resolveType = resolveType(logger, ptBinding.genericType());
-      if (resolveType == null) {
-        failed = true;
-      }
-
-      if (!failed) {
-        if (resolveType.isGenericType() != null) {
-          return typeOracle.getParameterizedType(resolveType.isGenericType(),
-              enclosingType, typeArguments);
-        } else {
-          /*
-           * A static type (enum or class) that does not declare any type
-           * parameters that is nested within a generic type might be referenced
-           * via a parameterized type by JDT. In this case we just return the
-           * type and don't treat it as a parameterized.
-           */
-          return resolveType;
-        }
-      } else {
-        // Fall-through to failure
-      }
-    }
-
-    if (binding instanceof TypeVariableBinding) {
-      TypeVariableBinding tvBinding = (TypeVariableBinding) binding;
-      JTypeParameter typeParameter = tvMapper.get(tvBinding);
-      if (typeParameter != null) {
-        return typeParameter;
-      }
-
-      // Fall-through to failure
-    }
-
-    if (binding instanceof WildcardBinding) {
-      WildcardBinding wcBinding = (WildcardBinding) binding;
-
-      assert (wcBinding.otherBounds == null);
-
-      BoundType boundType;
-      JClassType typeBound;
-
-      switch (wcBinding.boundKind) {
-        case Wildcard.EXTENDS: {
-          assert (wcBinding.bound != null);
-          boundType = BoundType.EXTENDS;
-          typeBound = (JClassType) resolveType(logger, wcBinding.bound);
-        }
-          break;
-        case Wildcard.SUPER: {
-          assert (wcBinding.bound != null);
-          boundType = BoundType.SUPER;
-          typeBound = (JClassType) resolveType(logger, wcBinding.bound);
-        }
-          break;
-        case Wildcard.UNBOUND: {
-          boundType = BoundType.UNBOUND;
-          typeBound = (JClassType) resolveType(logger, wcBinding.erasure());
-        }
-          break;
-        default:
-          assert false : "WildcardBinding of unknown boundKind???";
-          return null;
-      }
-
-      if (boundType != null) {
-        return typeOracle.getWildcardType(boundType, typeBound);
-      }
-
-      // Fall-through to failure
-    }
-
-    // Log other cases we know about that don't make sense.
-    //
-    String name = String.valueOf(binding.readableName());
-    logger = logger.branch(TreeLogger.WARN, "Unable to resolve type: " + name
-        + " binding: " + binding.getClass().getCanonicalName(), null);
-
-    if (binding instanceof BinaryTypeBinding) {
-      logger.log(TreeLogger.WARN,
-          "Source not available for this type, so it cannot be resolved", null);
-    }
-
-    return null;
-  }
-
-  private boolean resolveTypeDeclaration(TreeLogger logger,
-      TypeDeclaration clazz) {
-    SourceTypeBinding binding = clazz.binding;
-    assert (binding.constantPoolName() != null);
-
-    String qname = String.valueOf(binding.qualifiedSourceName());
-    logger = logger.branch(TreeLogger.SPAM, "Found type '" + qname + "'", null);
-
-    // Handle package-info classes.
-    if (isPackageInfoTypeName(qname)) {
-      return resolvePackage(logger, clazz);
-    }
-
-    // Just resolve the type.
-    JRealClassType jtype = (JRealClassType) resolveType(logger, binding);
-    if (jtype == null) {
-      // Failed to resolve.
-      //
-      return false;
-    }
-
-    /*
-     * Modifiers were added during processType since getParameterizedType
-     * depends on them being set.
-     */
-
-    // Try to resolve annotations, ignore any that fail.
-    Map<Class<? extends java.lang.annotation.Annotation>, java.lang.annotation.Annotation> declaredAnnotations = newAnnotationMap();
-    resolveAnnotations(logger, clazz.annotations, declaredAnnotations);
-    jtype.addAnnotations(declaredAnnotations);
-
-    // Resolve bounds for type parameters on generic types. Note that this
-    // step does not apply to type parameters on generic methods; that
-    // occurs during the method resolution stage.
-    JGenericType jGenericType = jtype.isGenericType();
-    if (jGenericType != null
-        && !resolveBoundsForTypeParameters(logger, jtype.isGenericType(),
-            clazz.typeParameters)) {
-      // Failed to resolve
-      return false;
-    }
-
-    // Resolve superclass (for classes only).
-    //
-    if (jtype.isInterface() == null) {
-      ReferenceBinding superclassRef = binding.superclass;
-      assert superclassRef != null
-          || "java.lang.Object".equals(jtype.getQualifiedSourceName());
-      if (superclassRef != null) {
-        JClassType jsuperClass = (JClassType) resolveType(logger, superclassRef);
-        assert jsuperClass != null;
-        jtype.setSuperclass(jsuperClass);
-      }
-    }
-
-    // Resolve superinterfaces.
-    //
-    ReferenceBinding[] superintfRefs = binding.superInterfaces;
-    for (int i = 0; i < superintfRefs.length; i++) {
-      ReferenceBinding superintfRef = superintfRefs[i];
-      JClassType jinterface = (JClassType) resolveType(logger, superintfRef);
-      if (jinterface == null) {
-        // Failed to resolve.
-        //
+      CollectMethodData methodData) {
+    Type[] argTypes = methodData.getArgTypes();
+    String[] argNames = methodData.getArgNames();
+    boolean argNamesAreReal = methodData.hasActualArgNames();
+    List<CollectAnnotationData>[] paramAnnot = methodData.getArgAnnotations();
+    for (int i = 0; i < argTypes.length; ++i) {
+      JType argType = resolveType(argTypes[i]);
+      if (argType == null) {
         return false;
       }
-      jtype.addImplementedInterface(jinterface);
+      // Try to resolve annotations, ignore any that fail.
+      Map<Class<? extends Annotation>, Annotation> declaredAnnotations = new HashMap<Class<? extends Annotation>, Annotation>();
+      resolveAnnotations(logger, paramAnnot[i], declaredAnnotations);
+
+      // JParameter adds itself to the method
+      new JParameter(method, argType, argNames[i], declaredAnnotations,
+          argNamesAreReal);
     }
+    return true;
+  }
 
-    // Resolve fields.
-    //
-    FieldDeclaration[] fields = clazz.fields;
-    if (!resolveFields(logger, jtype, fields)) {
-      return false;
+  private boolean resolveThrows(JAbstractMethod method,
+      CollectMethodData methodData) {
+    if (method.getThrows().length == 0) {
+      for (String excName : methodData.getExceptions()) {
+        JType exc = resolveType(Type.getObjectType(excName));
+        if (exc == null) {
+          return false;
+        }
+        method.addThrows(exc);
+      }
     }
-
-    // Resolve methods. This also involves the declaration of type
-    // variables on generic methods, and the resolution of the bounds
-    // on these type variables.
-
-    // One would think that it would be better to perform the declaration
-    // of type variables on methods at the time when we are processing
-    // all of the classes. Unfortunately, when we are processing classes,
-    // we do not have enough information about their methods to analyze
-    // their type variables. Hence, the type variable declaration and
-    // bounds resolution for generic methods must happen after the resolution
-    // of methods is complete.
-    AbstractMethodDeclaration[] methods = clazz.methods;
-    if (!resolveMethods(logger, jtype, methods)) {
-      return false;
-    }
-
     return true;
   }
 
   /**
-   * Declares TypeParameters declared on a JGenericType or a JAbstractMethod by
-   * mapping the TypeParameters into JTypeParameters. <p/> This mapping has to
-   * be done on the first pass through the AST in order to handle declarations
-   * of the form: <<C exends GenericClass<T>, T extends SimpleClass> <p/> JDT
-   * already knows that GenericClass<T> is a parameterized type with a type
-   * argument of <T extends SimpleClass>. Therefore, in order to resolve
-   * GenericClass<T>, we must have knowledge of <T extends SimpleClass>. <p/>
-   * By calling this method on the first pass through the AST, a JTypeParameter
-   * for <T extends SimpleClass> will be created.
+   * Returns a primitive, an array, or a JRealClassType.
    */
-  private JTypeParameter[] resolveTypeParameters(TypeParameter[] typeParameters) {
-    if (typeParameters == null || typeParameters.length == 0) {
-      return null;
+  private JType resolveType(Type type) {
+    // Check for primitives.
+    switch (type.getSort()) {
+      case Type.BOOLEAN:
+        return JPrimitiveType.BOOLEAN;
+      case Type.BYTE:
+        return JPrimitiveType.BYTE;
+      case Type.CHAR:
+        return JPrimitiveType.CHAR;
+      case Type.SHORT:
+        return JPrimitiveType.SHORT;
+      case Type.INT:
+        return JPrimitiveType.INT;
+      case Type.LONG:
+        return JPrimitiveType.LONG;
+      case Type.FLOAT:
+        return JPrimitiveType.FLOAT;
+      case Type.DOUBLE:
+        return JPrimitiveType.DOUBLE;
+      case Type.VOID:
+        return JPrimitiveType.VOID;
+      case Type.ARRAY:
+        return resolveArray(type);
+      case Type.OBJECT:
+        JRealClassType resolvedType = resolveObject(type);
+        return possiblySubstituteRawType(resolvedType);
+      default:
+        assert false : "Unexpected type " + type;
+        return null;
     }
-
-    JTypeParameter[] jtypeParamArray = new JTypeParameter[typeParameters.length];
-    for (int i = 0; i < typeParameters.length; ++i) {
-      jtypeParamArray[i] = tvMapper.get(typeParameters[i].binding);
-      assert jtypeParamArray[i] != null;
-    }
-
-    return jtypeParamArray;
   }
 }
diff --git a/dev/core/src/com/google/gwt/dev/javac/TypeParameterLookup.java b/dev/core/src/com/google/gwt/dev/javac/TypeParameterLookup.java
new file mode 100644
index 0000000..df0fd20
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/javac/TypeParameterLookup.java
@@ -0,0 +1,78 @@
+/*

+ * 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.javac;

+

+import com.google.gwt.core.ext.typeinfo.JClassType;

+import com.google.gwt.core.ext.typeinfo.JGenericType;

+import com.google.gwt.core.ext.typeinfo.JTypeParameter;

+import com.google.gwt.dev.util.collect.HashMap;

+import com.google.gwt.dev.util.collect.Maps;

+

+import java.util.LinkedList;

+import java.util.Map;

+

+/**

+ * Handles lookup of type parameters, using a scope stack.

+ */

+public class TypeParameterLookup {

+

+  private LinkedList<Map<String, JTypeParameter>> scopeStack = new LinkedList<Map<String, JTypeParameter>>();

+

+  public JTypeParameter lookup(String name) {

+    for (Map<String, JTypeParameter> scope : scopeStack) {

+      JTypeParameter result = scope.get(name);

+      if (result != null) {

+        return result;

+      }

+    }

+    return null;

+  }

+

+  public void popScope() {

+    scopeStack.remove();

+  }

+

+  public void pushEnclosingScopes(JClassType type) {

+    if (type == null) {

+      return;

+    }

+    pushEnclosingScopes(type.getEnclosingType());

+    JGenericType genericType = type.isGenericType();

+    if (genericType != null) {

+      pushScope(genericType.getTypeParameters());

+    }

+  }

+

+  public void pushScope(JTypeParameter[] typeParams) {

+    // push empty scopes to keep pops in sync

+    scopeStack.addFirst(buildScope(typeParams));

+  }

+

+  private Map<String, JTypeParameter> buildScope(JTypeParameter[] typeParams) {

+    switch (typeParams.length) {

+      case 0:

+        return Maps.create();

+      case 1:

+        return Maps.create(typeParams[0].getName(), typeParams[0]);

+      default:

+        Map<String, JTypeParameter> scope = new HashMap<String, JTypeParameter>();

+        for (JTypeParameter typeParam : typeParams) {

+          scope.put(typeParam.getName(), typeParam);

+        }

+        return scope;

+    }

+  }

+}

diff --git a/dev/core/src/com/google/gwt/dev/javac/asm/CollectAnnotationData.java b/dev/core/src/com/google/gwt/dev/javac/asm/CollectAnnotationData.java
new file mode 100644
index 0000000..532a582
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/javac/asm/CollectAnnotationData.java
@@ -0,0 +1,198 @@
+/*

+ * 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.javac.asm;

+

+import com.google.gwt.dev.asm.AnnotationVisitor;

+import com.google.gwt.dev.javac.asm.CollectClassData.AnnotationEnum;

+

+import java.util.ArrayList;

+import java.util.HashMap;

+import java.util.List;

+import java.util.Map;

+

+/**

+ * Collects data from (possibly nested) annotations on a single entity.

+ */

+public class CollectAnnotationData implements AnnotationVisitor {

+

+  /**

+   * Holds annotation fields/values.

+   */

+  public static class AnnotationData {

+    private final String desc;

+    private final Map<String, Object> values = new HashMap<String, Object>();

+    private final boolean visible;

+

+    protected AnnotationData(String desc, boolean visible) {

+      this.desc = desc;

+      this.visible = visible;

+    }

+

+    public void addValue(String name, Object value) {

+      values.put(name, value);

+    }

+

+    /**

+     * @return the desc

+     */

+    public String getDesc() {

+      return desc;

+    }

+

+    /**

+     * @return the values

+     */

+    public Map<String, Object> getValues() {

+      return values;

+    }

+

+    /**

+     * @return the visible

+     */

+    public boolean isVisible() {

+      return visible;

+    }

+  }

+

+  /**

+   * Collects data inside an array-valued annotation parameter.

+   */

+  public static class MyAnnotationArrayVisitor implements AnnotationVisitor {

+

+    private Callback<Object> callback;

+    private List<Object> values = new ArrayList<Object>();

+

+    public MyAnnotationArrayVisitor(Callback<Object> callback) {

+      this.callback = callback;

+    }

+

+    public void visit(String name, Object value) {

+      values.add(value);

+    }

+

+    public AnnotationVisitor visitAnnotation(String name, String desc) {

+      // TODO(jat): what should visible be set to?

+      return new CollectAnnotationData(desc, true,

+          new Callback<CollectAnnotationData.AnnotationData>() {

+            public void call(CollectAnnotationData.AnnotationData value) {

+              values.add(value);

+            }

+          });

+    }

+

+    public AnnotationVisitor visitArray(String name) {

+      return new MyAnnotationArrayVisitor(new Callback<Object>() {

+        public void call(Object value) {

+          values.add(value);

+        }

+      });

+    }

+

+    public void visitEnd() {

+      callback.call(values.toArray());

+    }

+

+    public void visitEnum(String name, String desc, String value) {

+      values.add(new AnnotationEnum(desc, value));

+    }

+  }

+

+  /**

+   * Generic callback type taking a parameter.

+   * 

+   * @param <T> type of the argument to the callback

+   */

+  private interface Callback<T> {

+

+    /**

+     * Invoke the callback.

+     * 

+     * @param value value to pass to the callback.

+     */

+    void call(T value);

+  }

+

+  private CollectAnnotationData.AnnotationData annotation;

+  private Callback<CollectAnnotationData.AnnotationData> callback;

+

+  /**

+   * Construct the collector.

+   * 

+   * @param desc class descriptor of the annotation class

+   * @param visible true if the annotation is visible at runtime

+   */

+  public CollectAnnotationData(String desc, boolean visible) {

+    this(desc, visible, null);

+  }

+

+  /**

+   * Construct the collector.

+   * 

+   * @param desc class descriptor of the annotation class

+   * @param visible true if the annotation is visible at runtime

+   * @param callback callback to be called when the annotation is finished

+   */

+  CollectAnnotationData(String desc, boolean visible,

+      Callback<CollectAnnotationData.AnnotationData> callback) {

+    annotation = new AnnotationData(desc, visible);

+    this.callback = callback;

+  }

+

+  /**

+   * @return the annotation data

+   */

+  public AnnotationData getAnnotation() {

+    return annotation;

+  }

+

+  public void visit(String name, Object value) {

+    annotation.addValue(name, value);

+  }

+

+  public AnnotationVisitor visitAnnotation(final String name, String desc) {

+    // TODO(jat): what should visible be set to?

+    return new CollectAnnotationData(desc, true,

+        new Callback<CollectAnnotationData.AnnotationData>() {

+          public void call(CollectAnnotationData.AnnotationData value) {

+            annotation.addValue(name, value);

+          }

+        });

+  }

+

+  public AnnotationVisitor visitArray(final String name) {

+    return new MyAnnotationArrayVisitor(new Callback<Object>() {

+

+      /**

+       * Called with an array of values.

+       * 

+       * @param value

+       */

+      public void call(Object value) {

+        annotation.addValue(name, value);

+      }

+    });

+  }

+

+  public void visitEnd() {

+    if (callback != null) {

+      callback.call(annotation);

+    }

+  }

+

+  public void visitEnum(String name, String desc, String value) {

+    annotation.addValue(name, new AnnotationEnum(desc, value));

+  }

+}

diff --git a/dev/core/src/com/google/gwt/dev/javac/asm/CollectClassData.java b/dev/core/src/com/google/gwt/dev/javac/asm/CollectClassData.java
new file mode 100644
index 0000000..e7fcd08
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/javac/asm/CollectClassData.java
@@ -0,0 +1,414 @@
+/*

+ * 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.javac.asm;

+

+import com.google.gwt.dev.asm.AnnotationVisitor;

+import com.google.gwt.dev.asm.FieldVisitor;

+import com.google.gwt.dev.asm.MethodVisitor;

+import com.google.gwt.dev.asm.Opcodes;

+import com.google.gwt.dev.asm.commons.EmptyVisitor;

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

+

+import java.util.ArrayList;

+import java.util.List;

+

+/**

+ * Reads the bytecode for a class and collects data needed for building

+ * TypeOracle structures.

+ */

+public class CollectClassData extends EmptyVisitor {

+

+  /**

+   * Type of this class.

+   */

+  public enum ClassType {

+    /**

+     * A top level class named the same as its source file.

+     */

+    TopLevel,

+

+    /**

+     * A non-static named class nested inside another class.

+     */

+    Inner {

+      @Override

+      public boolean hasHiddenConstructorArg() {

+        return true;

+      }

+    },

+

+    /**

+     * A static nested class inside another class.

+     */

+    Nested,

+

+    /**

+     * An anonymous inner class.

+     */

+    Anonymous {

+      @Override

+      public boolean hasOuter() {

+        return true;

+      }

+

+      @Override

+      public boolean isLocal() {

+        return true;

+      }

+    },

+

+    /**

+     * A named class defined inside a method.

+     */

+    Local {

+      @Override

+      public boolean hasHiddenConstructorArg() {

+        return true;

+      }

+

+      @Override

+      public boolean hasOuter() {

+        return true;

+      }

+

+      @Override

+      public boolean isLocal() {

+        return true;

+      }

+    };

+

+    public boolean hasHiddenConstructorArg() {

+      return false;

+    }

+

+    public boolean hasOuter() {

+      return false;

+    }

+

+    public boolean isLocal() {

+      return false;

+    }

+  }

+

+  /**

+   * Holds the descriptor and value for an Enum-valued annotation.

+   */

+  public static class AnnotationEnum {

+    private final String desc;

+    private final String value;

+

+    public AnnotationEnum(String desc, String value) {

+      this.desc = desc;

+      this.value = value;

+    }

+

+    /**

+     * @return the descriptor

+     */

+    public String getDesc() {

+      return desc;

+    }

+

+    /**

+     * @return the value

+     */

+    public String getValue() {

+      return value;

+    }

+  }

+

+  private List<CollectAnnotationData> annotations = new ArrayList<CollectAnnotationData>();

+

+  private String source = null;

+  

+  // internal name

+  private String name;

+

+  private String signature;

+

+  // internal name of superclass

+  private String superName;

+

+  // internal names of interfaces

+  private String[] interfaces;

+  private byte[] bytes;

+  private List<CollectMethodData> methods = new ArrayList<CollectMethodData>();

+  private List<CollectFieldData> fields = new ArrayList<CollectFieldData>();

+  private int access;

+  private String outerClass;

+  private String outerMethodName;

+  private String outerMethodDesc;

+  private CollectClassData.ClassType classType = ClassType.TopLevel;

+

+  public CollectClassData(byte[] bytes) {

+    this.bytes = bytes;

+  }

+

+  /**

+   * @return the access

+   */

+  public int getAccess() {

+    return access;

+  }

+

+  /**

+   * @return annotations on this class

+   */

+  public List<CollectAnnotationData> getAnnotations() {

+    return annotations;

+  }

+

+  /**

+   * @return the bytes

+   */

+  public byte[] getBytes() {

+    return bytes;

+  }

+

+  /**

+   * @return the class type.

+   */

+  public CollectClassData.ClassType getClassType() {

+    return classType;

+  }

+

+  /**

+   * @return the fields

+   */

+  public List<CollectFieldData> getFields() {

+    return fields;

+  }

+

+//  /**

+//   * @return the innerClasses

+//   */

+//  public List<Resource> getInnerClasses() {

+//    return innerClasses;

+//  }

+

+  /**

+   * @return the interfaces

+   */

+  public String[] getInterfaces() {

+    return interfaces;

+  }

+

+  /**

+   * @return the methods

+   */

+  public List<CollectMethodData> getMethods() {

+    return methods;

+  }

+

+  /**

+   * @return the name

+   */

+  public String getName() {

+    return name;

+  }

+

+  /**

+   * @return the outerClass

+   */

+  public String getOuterClass() {

+    return outerClass;

+  }

+

+  /**

+   * @return the outerMethodDesc

+   */

+  public String getOuterMethodDesc() {

+    return outerMethodDesc;

+  }

+

+  /**

+   * @return the outerMethodName

+   */

+  public String getOuterMethodName() {

+    return outerMethodName;

+  }

+

+  /**

+   * @return the signature

+   */

+  public String getSignature() {

+    return signature;

+  }

+

+  /**

+   * @return the source

+   */

+  public String getSource() {

+    return source;

+  }

+

+  /**

+   * @return the superName

+   */

+  public String getSuperName() {

+    return superName;

+  }

+

+  public boolean hasNoExternalName() {

+    return classType == ClassType.Anonymous || classType == ClassType.Local;

+  }

+

+  public boolean isAnonymous() {

+    return classType == ClassType.Anonymous;

+  }

+

+  public boolean isLocal() {

+    if (name.matches("\\$\\d") && !isAnonymous()) {

+      throw new IllegalStateException("Not anonymous with name of " + name);

+    }

+    return classType.isLocal();

+  }

+

+  @Override

+  public String toString() {

+    return "class " + name;

+  }

+

+  /**

+   * Called at the beginning of visiting the class.

+   * 

+   * @param version classfile version (ie, Opcodes.V1_5 etc)

+   * @param access access flags (ie, bitwise or of Opcodes.ACC_*)

+   * @param name internal name of this class (ie, com/google/Foo)

+   * @param signature generic signature or null

+   * @param superName binary name of superclass (ie, java/lang/Object)

+   * @param interfaces array of binary names of implemented interfaces

+   */

+  @Override

+  public void visit(int version, int access, String name, String signature,

+      String superName, String[] interfaces) {

+    this.access = access;

+    assert Name.isInternalName(name);

+    this.name = name;

+    this.signature = signature;

+    this.superName = superName;

+    this.interfaces = interfaces;

+  }

+

+  @Override

+  public AnnotationVisitor visitAnnotation(String desc, boolean visible) {

+    CollectAnnotationData av = new CollectAnnotationData(desc,

+        visible);

+    annotations.add(av);

+    return av;

+  }

+

+  /**

+   * Called for each field.

+   * 

+   * @param access access flags for field

+   * @param name field name

+   * @param desc type descriptor (ie, Ljava/lang/String;)

+   * @param signature generic signature (null if not generic)

+   * @param value initialized value if constant

+   */

+  @Override

+  public FieldVisitor visitField(int access, String name, String desc,

+      String signature, Object value) {

+    if ((access & Opcodes.ACC_SYNTHETIC) != 0) {

+      // if ("this$1".equals(name) && classType == ClassType.Anonymous) {

+      // // TODO(jat): !!! really nasty hack

+      // classType = ClassType.Inner;

+      // }

+      // skip synthetic fields

+      return null;

+    }

+    CollectFieldData fv = new CollectFieldData(access, name, desc,

+        signature, value);

+    fields.add(fv);

+    return fv;

+  }

+

+  /**

+   * Called once for every inner class of this class.

+   * 

+   * @param name internal name of inner class (ie, com/google/Foo$1)

+   * @param outerName internal name of enclosing class (null if not a member

+   *          class or anonymous)

+   * @param innerName simple name of the inner class (null if anonymous)

+   * @param access access flags (bitwise or of Opcodes.ACC_*) as declared in the

+   *          enclosing class

+   */

+  @Override

+  public void visitInnerClass(String name, String outerName, String innerName,

+      int access) {

+    // If this inner class is ourselves, merge the access flags, since

+    // static, for example, only appears in the InnerClass attribute.

+    if (this.name.equals(name)) {

+      if (outerName != null) {

+        outerClass = outerName;

+      }

+      // TODO(jat): should we only pull in a subset of these flags? Use only

+      // these flags, or what? For now, just grab ACC_STATIC and ACC_PRIVATE

+      int copyFlags = access & (Opcodes.ACC_STATIC | Opcodes.ACC_PRIVATE);

+      this.access |= copyFlags;

+      boolean isStatic = (access & Opcodes.ACC_STATIC) != 0;

+      switch (classType) {

+        case TopLevel:

+          classType = isStatic ? ClassType.Nested : ClassType.Inner;

+          break;

+        case Anonymous:

+          if (innerName != null) {

+            classType = ClassType.Local;

+          }

+          break;

+        case Inner:

+          // Already marked as inner class by the synthetic this$1 field

+          break;

+        default:

+          throw new IllegalStateException("Unexpected INNERCLASS with type of "

+              + classType);

+      }

+    }

+  }

+

+  @Override

+  public MethodVisitor visitMethod(int access, String name, String desc,

+      String signature, String[] exceptions) {

+    if ((access & Opcodes.ACC_SYNTHETIC) != 0) {

+      // skip synthetic methods

+      return null;

+    }

+    CollectMethodData mv = new CollectMethodData(classType,

+        access, name, desc, signature, exceptions);

+    methods.add(mv);

+    return mv;

+  }

+

+  @Override

+  public void visitOuterClass(String owner, String name, String desc) {

+    this.outerClass = owner;

+    this.outerMethodName = name;

+    this.outerMethodDesc = desc;

+    classType = ClassType.Anonymous; // Could be Local, catch that later

+  }

+

+  /**

+   * If compiled with debug, visit the source information.

+   * 

+   * @param source unqualified filename containing source (ie, Foo.java)

+   * @param debug additional debug information (may be null)

+   */

+  @Override

+  public void visitSource(String source, String debug) {

+    this.source = source;

+  }

+}

diff --git a/dev/core/src/com/google/gwt/dev/javac/asm/CollectFieldData.java b/dev/core/src/com/google/gwt/dev/javac/asm/CollectFieldData.java
new file mode 100644
index 0000000..e2e9c5d
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/javac/asm/CollectFieldData.java
@@ -0,0 +1,99 @@
+/*

+ * 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.javac.asm;

+

+import com.google.gwt.dev.asm.AnnotationVisitor;

+import com.google.gwt.dev.asm.commons.EmptyVisitor;

+

+import java.util.ArrayList;

+import java.util.List;

+

+/**

+ * Collect data from a single field.

+ */

+public class CollectFieldData extends EmptyVisitor {

+

+  private List<CollectAnnotationData> annotations = new ArrayList<CollectAnnotationData>();

+  private int access;

+  private String name;

+  private String desc;

+  private String signature;

+  private Object value;

+

+  public CollectFieldData(int access, String name, String desc,

+      String signature, Object value) {

+    this.access = access;

+    this.name = name;

+    this.desc = desc;

+    this.signature = signature;

+    this.value = value;

+  }

+

+  /**

+   * @return the access

+   */

+  public int getAccess() {

+    return access;

+  }

+

+  /**

+   * @return the annotations

+   */

+  public List<CollectAnnotationData> getAnnotations() {

+    return annotations;

+  }

+

+  /**

+   * @return the desc

+   */

+  public String getDesc() {

+    return desc;

+  }

+

+  /**

+   * @return the name

+   */

+  public String getName() {

+    return name;

+  }

+

+  /**

+   * @return the signature

+   */

+  public String getSignature() {

+    return signature;

+  }

+

+  /**

+   * @return the value

+   */

+  public Object getValue() {

+    return value;

+  }

+

+  @Override

+  public String toString() {

+    return "field " + name;

+  }

+

+  @Override

+  public AnnotationVisitor visitAnnotation(String desc, boolean visible) {

+    CollectAnnotationData av = new CollectAnnotationData(desc,

+        visible);

+    annotations.add(av);

+    return av;

+  }

+}

diff --git a/dev/core/src/com/google/gwt/dev/javac/asm/CollectMethodData.java b/dev/core/src/com/google/gwt/dev/javac/asm/CollectMethodData.java
new file mode 100644
index 0000000..81aff43
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/javac/asm/CollectMethodData.java
@@ -0,0 +1,183 @@
+/*

+ * 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.javac.asm;

+

+import com.google.gwt.dev.asm.AnnotationVisitor;

+import com.google.gwt.dev.asm.Label;

+import com.google.gwt.dev.asm.Opcodes;

+import com.google.gwt.dev.asm.Type;

+import com.google.gwt.dev.asm.commons.EmptyVisitor;

+

+import java.util.ArrayList;

+import java.util.List;

+

+/**

+ * Collects data from a single method.

+ */

+public class CollectMethodData extends EmptyVisitor {

+

+  private static final String[] EMPTY_STRING_ARRAY = new String[0];

+

+  private List<CollectAnnotationData> annotations = new ArrayList<CollectAnnotationData>();

+  private String name;

+  private String desc;

+  private String signature;

+  private String[] exceptions;

+  private Type[] argTypes;

+  private String[] argNames;

+  private List<CollectAnnotationData>[] paramAnnots;

+  private boolean actualArgNames = false;

+  private int access;

+  private int syntheticArgs;

+

+  @SuppressWarnings("unchecked")

+  public CollectMethodData(CollectClassData.ClassType classType, int access,

+      String name, String desc, String signature, String[] exceptions) {

+    this.access = access;

+    this.name = name;

+    this.desc = desc;

+    this.signature = signature;

+    this.exceptions = exceptions;

+    syntheticArgs = 0;

+    argTypes = Type.getArgumentTypes(desc);

+    // Non-static instance methods and constructors of non-static inner

+    // classes have an extra synthetic parameter that isn't in the source,

+    // so we remove it.

+    if (classType.hasHiddenConstructorArg() && "<init>".equals(name)) {

+      // remove "this$1" as a parameter

+      syntheticArgs = 1;

+      int n = argTypes.length - syntheticArgs;

+      Type[] newArgTypes = new Type[n];

+      System.arraycopy(argTypes, syntheticArgs, newArgTypes, 0, n);

+      argTypes = newArgTypes;

+    }

+    argNames = new String[argTypes.length];

+    paramAnnots = new List[argTypes.length];

+    for (int i = 0; i < argNames.length; ++i) {

+      argNames[i] = "arg" + i;

+      paramAnnots[i] = new ArrayList<CollectAnnotationData>();

+    }

+  }

+

+  /**

+   * @return the access

+   */

+  public int getAccess() {

+    return access;

+  }

+

+  /**

+   * @return the annotations

+   */

+  public List<CollectAnnotationData> getAnnotations() {

+    return annotations;

+  }

+

+  public List<CollectAnnotationData>[] getArgAnnotations() {

+    return paramAnnots;

+  }

+

+  /**

+   * @return the argNames

+   */

+  public String[] getArgNames() {

+    return argNames;

+  }

+

+  /**

+   * @return the argTypes

+   */

+  public Type[] getArgTypes() {

+    return argTypes;

+  }

+

+  /**

+   * @return the desc

+   */

+  public String getDesc() {

+    return desc;

+  }

+

+  /**

+   * @return the exceptions

+   */

+  public String[] getExceptions() {

+    return exceptions == null ? EMPTY_STRING_ARRAY : exceptions;

+  }

+

+  /**

+   * @return the name

+   */

+  public String getName() {

+    return name;

+  }

+

+  /**

+   * @return the signature

+   */

+  public String getSignature() {

+    return signature;

+  }

+

+  /**

+   * @return the actualArgNames

+   */

+  public boolean hasActualArgNames() {

+    return actualArgNames;

+  }

+

+  @Override

+  public String toString() {

+    return "method " + name;

+  }

+

+  @Override

+  public AnnotationVisitor visitAnnotation(String desc, boolean visible) {

+    CollectAnnotationData av = new CollectAnnotationData(desc,

+        visible);

+    annotations.add(av);

+    return av;

+  }

+

+  @Override

+  public void visitLocalVariable(String name, String desc, String signature,

+      Label start, Label end, int index) {

+    if ((access & Opcodes.ACC_STATIC) == 0) {

+      // adjust for "this"

+      // TODO(jat): do we need to account for this$0 in inner classes?

+      --index;

+    }

+    // TODO(jat): is it safe to assume parameter slots don't get reused?

+    // Do we need to check if the name has already been assigned?

+    if (index >= 0 && index < argNames.length) {

+      actualArgNames = true;

+      argNames[index] = name;

+    }

+  }

+

+  @Override

+  public AnnotationVisitor visitParameterAnnotation(int parameter,

+      String desc, boolean visible) {

+    CollectAnnotationData av = new CollectAnnotationData(desc,

+        visible);

+    if (parameter >= syntheticArgs) {

+      // javac adds @Synthetic annotation on its synthetic constructor

+      // arg, so we ignore it since it isn't in the source.

+      paramAnnots[parameter - syntheticArgs].add(av);

+    }

+    return av;

+  }

+}

diff --git a/dev/core/src/com/google/gwt/dev/javac/asm/CollectReferencesVisitor.java b/dev/core/src/com/google/gwt/dev/javac/asm/CollectReferencesVisitor.java
new file mode 100644
index 0000000..1796706
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/javac/asm/CollectReferencesVisitor.java
@@ -0,0 +1,208 @@
+/*

+ * 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.javac.asm;

+

+import com.google.gwt.dev.asm.AnnotationVisitor;

+import com.google.gwt.dev.asm.FieldVisitor;

+import com.google.gwt.dev.asm.MethodVisitor;

+import com.google.gwt.dev.asm.Type;

+import com.google.gwt.dev.asm.commons.EmptyVisitor;

+import com.google.gwt.dev.asm.signature.SignatureReader;

+import com.google.gwt.dev.asm.signature.SignatureVisitor;

+

+import java.util.HashSet;

+import java.util.Set;

+

+/**

+ * Collect all the types which are referenced by a particular class.

+ */

+public class CollectReferencesVisitor extends EmptyVisitor {

+

+  /**

+   * Collect type names from generic signatures.

+   * 

+   * All we care about is picking up type names, so we just return ourselves

+   * for nested visitors.

+   */

+  private class CollectGenericTypes implements SignatureVisitor {

+    public SignatureVisitor visitArrayType() {

+      return this;

+    }

+

+    public void visitBaseType(char descriptor) {

+    }

+

+    public SignatureVisitor visitClassBound() {

+      return this;

+    }

+

+    public void visitClassType(String name) {

+      referencedTypes.add(name);

+    }

+

+    public void visitEnd() {

+    }

+

+    public SignatureVisitor visitExceptionType() {

+      return this;

+    }

+

+    public void visitFormalTypeParameter(String name) {

+    }

+

+    public void visitInnerClassType(String name) {

+    }

+

+    public SignatureVisitor visitInterface() {

+      return this;

+    }

+

+    public SignatureVisitor visitInterfaceBound() {

+      return this;

+    }

+

+    public SignatureVisitor visitParameterType() {

+      return this;

+    }

+

+    public SignatureVisitor visitReturnType() {

+      return this;

+    }

+

+    public SignatureVisitor visitSuperclass() {

+      return this;

+    }

+

+    public void visitTypeArgument() {

+    }

+

+    public SignatureVisitor visitTypeArgument(char wildcard) {

+      return this;

+    }

+

+    public void visitTypeVariable(String name) {

+    }

+  }

+

+  // internal names

+  protected Set<String> referencedTypes = new HashSet<String>();

+

+  public Set<String> getReferencedTypes() {

+    return referencedTypes;

+  }

+

+  @Override

+  public void visit(int version, int access, String name, String signature,

+      String superName, String[] interfaces) {

+    if (superName != null) {

+      referencedTypes.add(superName);

+    }

+    if (interfaces != null) {

+      for (String intf : interfaces) {

+        referencedTypes.add(intf);

+      }

+    }

+    collectTypesFromClassSignature(signature);

+  }

+

+  @Override

+  public void visit(String name, Object value) {

+    if (value instanceof Type) {

+      addTypeIfClass((Type) value);

+    }

+  }

+

+  @Override

+  public AnnotationVisitor visitAnnotation(String desc, boolean visible) {

+    // don't mark this annotation as a reference or its arguments, so we can

+    // handle binary-only annotations.

+    // TODO(jat): consider implications of updating the annotation class

+    return null;

+  }

+

+  @Override

+  public void visitEnum(String name, String desc, String value) {

+    addTypeIfClass(desc);

+  }

+

+  @Override

+  public FieldVisitor visitField(int access, String name, String desc,

+      String signature, Object value) {

+    addTypeIfClass(desc);

+    collectTypesFromFieldSignature(signature);

+    // we don't use visitEnd, so we can just use ourselves for nested visitors

+    return this;

+  }

+

+  /**

+   * @param name internal name of the inner class

+   * @param outerName internal name of the enclosing class

+   */

+  @Override

+  public void visitInnerClass(String name, String outerName,

+      String innerName, int access) {

+    referencedTypes.add(name);

+    if (outerName != null) {

+      referencedTypes.add(outerName);

+    }

+  }

+

+  @Override

+  public MethodVisitor visitMethod(int access, String name, String desc,

+      String signature, String[] exceptions) {

+    for (Type type : Type.getArgumentTypes(desc)) {

+      addTypeIfClass(type);

+    }

+    addTypeIfClass(Type.getReturnType(desc));

+    collectTypesFromClassSignature(signature);

+    // we don't use visitEnd, so we can just use ourselves for nested visitors

+    return this;

+  }

+

+  /**

+   * @param owner internal name of owning class

+   */

+  @Override

+  public void visitOuterClass(String owner, String name, String desc) {

+    referencedTypes.add(owner);

+  }

+

+  protected void addTypeIfClass(String desc) {

+    addTypeIfClass(Type.getType(desc));

+  }

+

+  protected void addTypeIfClass(Type type) {

+    if (type.getSort() == Type.OBJECT) {

+      referencedTypes.add(type.getInternalName());

+    }

+  }

+

+  private void collectTypesFromClassSignature(String signature) {

+    if (signature == null) {

+      return;

+    }

+    SignatureReader reader = new SignatureReader(signature);

+    reader.accept(new CollectGenericTypes());

+  }

+

+  private void collectTypesFromFieldSignature(String signature) {

+    if (signature == null) {

+      return;

+    }

+    SignatureReader reader = new SignatureReader(signature);

+    reader.acceptType(new CollectGenericTypes());

+  }

+}

diff --git a/dev/core/src/com/google/gwt/dev/javac/asm/CollectTypeParams.java b/dev/core/src/com/google/gwt/dev/javac/asm/CollectTypeParams.java
new file mode 100644
index 0000000..7349525
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/javac/asm/CollectTypeParams.java
@@ -0,0 +1,42 @@
+/*

+ * 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.javac.asm;

+

+import com.google.gwt.core.ext.typeinfo.JTypeParameter;

+

+import java.util.List;

+

+/**

+ * Collects formal type parameters into a JTypeParameter list.

+ */

+public class CollectTypeParams extends EmptySignatureVisitor {

+

+  private final List<JTypeParameter> typeParams;

+

+  /**

+   * Collect declared type parameters from a generic signature.

+   * 

+   * @param typeParams list to store type parameters in

+   */

+  public CollectTypeParams(List<JTypeParameter> typeParams) {

+    this.typeParams = typeParams;

+  }

+

+  @Override

+  public void visitFormalTypeParameter(String name) {

+    typeParams.add(new JTypeParameter(name, typeParams.size()));

+  }

+}

diff --git a/dev/core/src/com/google/gwt/dev/javac/asm/EmptySignatureVisitor.java b/dev/core/src/com/google/gwt/dev/javac/asm/EmptySignatureVisitor.java
new file mode 100644
index 0000000..a719ce6
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/javac/asm/EmptySignatureVisitor.java
@@ -0,0 +1,100 @@
+/*

+ * 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.javac.asm;

+

+import com.google.gwt.dev.asm.signature.SignatureVisitor;

+

+/**

+ * Signature visitor that does nothing.

+ * 

+ * Unlike the ASM-provided EmptyVisitor (which does not implement

+ * SignatureVisitor), this class does not pass itself to unimplemented

+ * sub-visitors, so that a subclass doesn't have to worry about calls for

+ * something under a sub-visitor it doesn't care about.

+ * 

+ * There is no need to call any superclass methods from any subclass as they

+ * do nothing.

+ */

+public class EmptySignatureVisitor implements SignatureVisitor {

+

+  protected static EmptySignatureVisitor ignore = new EmptySignatureVisitor();

+

+  /**

+   * Treated as a visitEnd for this visitor.

+   */

+  public SignatureVisitor visitArrayType() {

+    return ignore;

+  }

+

+  /**

+   * Treated as a visitEnd for this visitor.

+   */

+  public void visitBaseType(char descriptor) {

+  }

+

+  public SignatureVisitor visitClassBound() {

+    return ignore;

+  }

+

+  public void visitClassType(String name) {

+  }

+

+  public void visitEnd() {

+  }

+

+  public SignatureVisitor visitExceptionType() {

+    return ignore;

+  }

+

+  public void visitFormalTypeParameter(String name) {

+  }

+

+  public void visitInnerClassType(String name) {

+  }

+

+  public SignatureVisitor visitInterface() {

+    return ignore;

+  }

+

+  public SignatureVisitor visitInterfaceBound() {

+    return ignore;

+  }

+

+  public SignatureVisitor visitParameterType() {

+    return ignore;

+  }

+

+  public SignatureVisitor visitReturnType() {

+    return ignore;

+  }

+

+  public SignatureVisitor visitSuperclass() {

+    return ignore;

+  }

+

+  public void visitTypeArgument() {

+  }

+

+  public SignatureVisitor visitTypeArgument(char wildcard) {

+    return ignore;

+  }

+

+  /**

+   * Treated as a visitEnd for this visitor.

+   */

+  public void visitTypeVariable(String name) {

+  }

+}

diff --git a/dev/core/src/com/google/gwt/dev/javac/asm/ResolveClassSignature.java b/dev/core/src/com/google/gwt/dev/javac/asm/ResolveClassSignature.java
new file mode 100644
index 0000000..70b26d1
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/javac/asm/ResolveClassSignature.java
@@ -0,0 +1,134 @@
+/*

+ * 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.javac.asm;

+

+import com.google.gwt.core.ext.TreeLogger;

+import com.google.gwt.core.ext.typeinfo.JClassType;

+import com.google.gwt.core.ext.typeinfo.JRealClassType;

+import com.google.gwt.core.ext.typeinfo.JType;

+import com.google.gwt.core.ext.typeinfo.JTypeParameter;

+import com.google.gwt.dev.asm.signature.SignatureVisitor;

+import com.google.gwt.dev.javac.Resolver;

+import com.google.gwt.dev.javac.TypeParameterLookup;

+

+import java.util.ArrayList;

+import java.util.List;

+import java.util.Map;

+

+/**

+ * Signature visitor that resolves all the type variables and their bounds for

+ * a given class.

+ */

+public class ResolveClassSignature extends EmptySignatureVisitor {

+

+  private final Resolver resolver;

+  private final Map<String, JRealClassType> binaryMapper;

+  private final TreeLogger logger;

+  private final JRealClassType type;

+  private final TypeParameterLookup lookup;

+

+  private JTypeParameter currentParam = null;

+  private ArrayList<JType[]> bounds = null;

+  private JType[] superClass = new JType[1];

+  private List<JType[]> interfaces = new ArrayList<JType[]>();

+

+  public ResolveClassSignature(Resolver resolver,

+      Map<String, JRealClassType> binaryMapper, TreeLogger logger,

+      JRealClassType type, TypeParameterLookup lookup) {

+    this.resolver = resolver;

+    this.binaryMapper = binaryMapper;

+    this.logger = logger;

+    this.type = type;

+    this.lookup = lookup;

+  }

+

+  public void finish() {

+    if (currentParam != null) {

+      int n = bounds.size();

+      JClassType[] boundTypes = new JClassType[n];

+      for (int i = 0; i < n; ++i) {

+        boundTypes[i] = (JClassType) bounds.get(i)[0];

+      }

+      currentParam.setBounds(boundTypes);

+      currentParam = null;

+      // TODO(jat): remove after debugging phase

+      bounds = null;

+    }

+    if (superClass[0] != null) {

+      if (type.isInterface() != null) {

+        // The generic signature contains a superclass for interfaces,

+        // but TypeOracle doesn't like that -- verify that we were

+        // told Object is the superclass and ignore it.

+        assert superClass[0].equals(

+            resolver.getTypeOracle().getJavaLangObject());

+      } else {

+        type.setSuperclass((JClassType) superClass[0]);

+      }

+      superClass[0] = null;

+    }

+    for (JType[] intfRef : interfaces) {

+      if (intfRef[0] != null) {

+        type.addImplementedInterface((JClassType) intfRef[0]);

+      }

+    }

+    interfaces.clear();

+  }

+

+  @Override

+  public SignatureVisitor visitArrayType() {

+    throw new IllegalStateException(

+        "visitArrayType called on ResolveClassTypeVariables");

+  }

+

+  @Override

+  public SignatureVisitor visitClassBound() {

+    JType[] bound = new JType[1];

+    bounds.add(bound);

+    return new ResolveTypeSignature(resolver, binaryMapper, logger, bound,

+        lookup, null);

+  }

+

+  @Override

+  public void visitFormalTypeParameter(String name) {

+    finish();

+    currentParam = lookup.lookup(name);

+    bounds = new ArrayList<JType[]>();

+  }

+

+  @Override

+  public SignatureVisitor visitInterface() {

+    finish();

+    JType[] intf = new JType[1];

+    interfaces.add(intf);

+    return new ResolveTypeSignature(resolver, binaryMapper, logger, intf,

+        lookup, null);

+  }

+

+  @Override

+  public SignatureVisitor visitInterfaceBound() {

+    JType[] bound = new JType[1];

+    bounds.add(bound);

+    return new ResolveTypeSignature(resolver, binaryMapper, logger, bound,

+        lookup, null);

+  }

+

+  @Override

+  public SignatureVisitor visitSuperclass() {

+    finish();

+    return new ResolveTypeSignature(resolver, binaryMapper, logger,

+        superClass, lookup, null);

+  }

+}

diff --git a/dev/core/src/com/google/gwt/dev/javac/asm/ResolveMethodSignature.java b/dev/core/src/com/google/gwt/dev/javac/asm/ResolveMethodSignature.java
new file mode 100644
index 0000000..3b714b7
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/javac/asm/ResolveMethodSignature.java
@@ -0,0 +1,184 @@
+/*

+ * 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.javac.asm;

+

+import com.google.gwt.core.ext.TreeLogger;

+import com.google.gwt.core.ext.typeinfo.JAbstractMethod;

+import com.google.gwt.core.ext.typeinfo.JClassType;

+import com.google.gwt.core.ext.typeinfo.JMethod;

+import com.google.gwt.core.ext.typeinfo.JParameter;

+import com.google.gwt.core.ext.typeinfo.JType;

+import com.google.gwt.core.ext.typeinfo.JTypeParameter;

+import com.google.gwt.dev.asm.Type;

+import com.google.gwt.dev.asm.signature.SignatureVisitor;

+import com.google.gwt.dev.javac.Resolver;

+import com.google.gwt.dev.javac.TypeParameterLookup;

+

+import java.lang.annotation.Annotation;

+import java.util.ArrayList;

+import java.util.HashMap;

+import java.util.List;

+import java.util.Map;

+

+/**

+ * Resolve a method given its generic signature, including return type,

+ * parameter types, and exceptions thrown.

+ */

+public class ResolveMethodSignature extends EmptySignatureVisitor {

+

+  private final Resolver resolver;

+  private final TreeLogger logger;

+  private final JAbstractMethod method;

+  private final TypeParameterLookup typeParamLookup;

+  private final boolean hasReturnType;

+  private final CollectMethodData methodData;

+  private final Type[] argTypes;

+  private final String[] argNames;

+

+  private JType[] returnType = new JType[1];

+  private List<JType[]> params = new ArrayList<JType[]>();

+  private List<JType[]> exceptions = new ArrayList<JType[]>();

+  private JTypeParameter currentParam = null;

+  private ArrayList<JType[]> bounds = null;

+

+  public ResolveMethodSignature(Resolver resolver, TreeLogger logger,

+      JAbstractMethod method, TypeParameterLookup typeParamLookup,

+      boolean hasReturnType, CollectMethodData methodData, Type[] argTypes,

+      String[] argNames) {

+    this.resolver = resolver;

+    this.logger = logger;

+    this.method = method;

+    this.typeParamLookup = typeParamLookup;

+    this.hasReturnType = hasReturnType;

+    this.methodData = methodData;

+    this.argTypes = argTypes;

+    this.argNames = argNames;

+  }

+

+  /**

+   * @return true if resolution succeeded.

+   */

+  public boolean finish() {

+    boolean failed = false;

+

+    finishBound();

+

+    // Set return type

+    if (hasReturnType) {

+      failed |= (returnType[0] == null);

+      ((JMethod) method).setReturnType(returnType[0]);

+    }

+

+    // Create arguments

+    List<CollectAnnotationData>[] argAnnotations = methodData.getArgAnnotations();

+    if (argTypes.length != params.size()) {

+      // TODO(jat): remove this check

+      throw new IllegalStateException(

+          "Arg count mismatch between method descriptor ("

+              + methodData.getDesc() + ") and signature ("

+              + methodData.getSignature() + ")");

+    }

+    for (int i = 0; i < argTypes.length; ++i) {

+      JType argType = params.get(i)[0];

+      if (argType == null) {

+        failed = true;

+        continue;

+      }

+      // Try to resolve annotations, ignore any that fail.

+      Map<Class<? extends Annotation>, Annotation> declaredAnnotations = new HashMap<Class<? extends Annotation>, Annotation>();

+      resolver.resolveAnnotations(logger, argAnnotations[i],

+          declaredAnnotations);

+

+      // JParameter adds itself to the method

+      new JParameter(method, argType, argNames[i], declaredAnnotations);

+    }

+

+    // Handle thrown exceptions

+    for (JType[] exc : exceptions) {

+      if (exc[0] == null) {

+        failed = true;

+        continue;

+      }

+      method.addThrows(exc[0]);

+    }

+    return !failed;

+  }

+

+  @Override

+  public SignatureVisitor visitArrayType() {

+    assert false : "visitArrayType called on ResolveClassTypeVariables";

+    return null;

+  }

+

+  @Override

+  public SignatureVisitor visitClassBound() {

+    JType[] bound = new JClassType[1];

+    bounds.add(bound);

+    return new ResolveTypeSignature(resolver, resolver.getBinaryMapper(),

+        logger, bound, typeParamLookup, null);

+  }

+

+  @Override

+  public SignatureVisitor visitExceptionType() {

+    JType[] exc = new JType[1];

+    exceptions.add(exc);

+    return new ResolveTypeSignature(resolver, resolver.getBinaryMapper(),

+        logger, exc, typeParamLookup, null);

+  }

+

+  @Override

+  public void visitFormalTypeParameter(String name) {

+    finishBound();

+    currentParam = typeParamLookup.lookup(name);

+    bounds = new ArrayList<JType[]>();

+  }

+

+  @Override

+  public SignatureVisitor visitInterfaceBound() {

+    JType[] bound = new JType[1];

+    bounds.add(bound);

+    return new ResolveTypeSignature(resolver, resolver.getBinaryMapper(),

+        logger, bound, typeParamLookup, null);

+  }

+

+  @Override

+  public SignatureVisitor visitParameterType() {

+    JType[] param = new JType[1];

+    params.add(param);

+    return new ResolveTypeSignature(resolver, resolver.getBinaryMapper(),

+        logger, param, typeParamLookup, null);

+  }

+

+  @Override

+  public SignatureVisitor visitReturnType() {

+    return new ResolveTypeSignature(resolver, resolver.getBinaryMapper(),

+        logger, returnType, typeParamLookup, null);

+  }

+

+  private void finishBound() {

+    if (currentParam != null) {

+      int n = bounds.size();

+      JClassType[] boundTypes = new JClassType[n];

+      for (int i = 0; i < n; ++i) {

+        boundTypes[i] = (JClassType) bounds.get(i)[0];

+      }

+      currentParam.setBounds(boundTypes);

+      currentParam = null;

+      // TODO(jat): remove after debugging phase

+      bounds = null;

+    }

+  }

+}

diff --git a/dev/core/src/com/google/gwt/dev/javac/asm/ResolveTypeSignature.java b/dev/core/src/com/google/gwt/dev/javac/asm/ResolveTypeSignature.java
new file mode 100644
index 0000000..f62b872
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/javac/asm/ResolveTypeSignature.java
@@ -0,0 +1,365 @@
+/*

+ * 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.javac.asm;

+

+import com.google.gwt.core.ext.TreeLogger;

+import com.google.gwt.core.ext.typeinfo.JClassType;

+import com.google.gwt.core.ext.typeinfo.JGenericType;

+import com.google.gwt.core.ext.typeinfo.JParameterizedType;

+import com.google.gwt.core.ext.typeinfo.JPrimitiveType;

+import com.google.gwt.core.ext.typeinfo.JRealClassType;

+import com.google.gwt.core.ext.typeinfo.JType;

+import com.google.gwt.core.ext.typeinfo.JTypeParameter;

+import com.google.gwt.core.ext.typeinfo.JWildcardType;

+import com.google.gwt.core.ext.typeinfo.NotFoundException;

+import com.google.gwt.core.ext.typeinfo.JWildcardType.BoundType;

+import com.google.gwt.dev.asm.signature.SignatureVisitor;

+import com.google.gwt.dev.javac.Resolver;

+import com.google.gwt.dev.javac.TypeParameterLookup;

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

+

+import java.util.ArrayList;

+import java.util.List;

+import java.util.Map;

+

+/**

+ * Resolve a single parameterized type.

+ */

+public class ResolveTypeSignature extends EmptySignatureVisitor {

+

+  private final Resolver resolver;

+  private final Map<String, JRealClassType> binaryMapper;

+  private final TreeLogger logger;

+  private final JType[] returnTypeRef;

+  private final TypeParameterLookup lookup;

+  private final char wildcardMatch;

+  private final JClassType enclosingClass;

+

+  private JClassType outerClass;

+  private List<JType[]> args = new ArrayList<JType[]>();

+  private int arrayDepth = 0;

+

+  /**

+   * Resolve a parameterized type.

+   * 

+   * @param resolver

+   * @param binaryMapper

+   * @param logger

+   * @param returnTypeRef "pointer" to return location, ie. 1-element array

+   * @param lookup

+   * @param enclosingClass

+   */

+  public ResolveTypeSignature(Resolver resolver,

+      Map<String, JRealClassType> binaryMapper, TreeLogger logger,

+      JType[] returnTypeRef, TypeParameterLookup lookup,

+      JClassType enclosingClass) {

+    this(resolver, binaryMapper, logger, returnTypeRef, lookup,

+        enclosingClass, '=');

+  }

+

+  public ResolveTypeSignature(Resolver resovler,

+      Map<String, JRealClassType> binaryMapper,

+      TreeLogger logger, JType[] returnTypeRef,

+      TypeParameterLookup lookup, JClassType enclosingClass,

+      char wildcardMatch) {

+    this.resolver = resovler;

+    this.binaryMapper = binaryMapper;

+    this.logger = logger;

+    this.returnTypeRef = returnTypeRef;

+    this.lookup = lookup;

+    this.enclosingClass = enclosingClass;

+    this.wildcardMatch = wildcardMatch;

+  }

+

+  @Override

+  public SignatureVisitor visitArrayType() {

+    ++arrayDepth;

+    return this;

+  }

+

+  @Override

+  public void visitBaseType(char descriptor) {

+    switch (descriptor) {

+      case 'V':

+        returnTypeRef[0] = JPrimitiveType.VOID;

+        break;

+      case 'B':

+        returnTypeRef[0] = JPrimitiveType.BYTE;

+        break;

+      case 'J':

+        returnTypeRef[0] = JPrimitiveType.LONG;

+        break;

+      case 'Z':

+        returnTypeRef[0] = JPrimitiveType.BOOLEAN;

+        break;

+      case 'I':

+        returnTypeRef[0] = JPrimitiveType.INT;

+        break;

+      case 'S':

+        returnTypeRef[0] = JPrimitiveType.SHORT;

+        break;

+      case 'C':

+        returnTypeRef[0] = JPrimitiveType.CHAR;

+        break;

+      case 'F':

+        returnTypeRef[0] = JPrimitiveType.FLOAT;

+        break;

+      case 'D':

+        returnTypeRef[0] = JPrimitiveType.DOUBLE;

+        break;

+      default:

+        throw new IllegalStateException("Unrecognized base type "

+            + descriptor);

+    }

+    // this is the last visitor called on this visitor

+    visitEnd();

+  }

+

+  @Override

+  public void visitClassType(String internalName) {

+    assert Name.isInternalName(internalName);

+    outerClass = enclosingClass;

+    JRealClassType classType = binaryMapper.get(internalName);

+    boolean resolveSuccess = resolver.resolveClass(logger, classType);

+    returnTypeRef[0] = classType;

+    if (!resolveSuccess || returnTypeRef[0] == null) {

+      logger.log(TreeLogger.ERROR, "Unable to resolve class " + internalName);

+      // Replace bound with Object if we can't resolve the class.

+      returnTypeRef[0] = resolver.getTypeOracle().getJavaLangObject();

+    }

+  }

+

+  @Override

+  public void visitEnd() {

+    if (returnTypeRef[0] == null) {

+      return;

+    }

+    resolveGenerics();

+  }

+

+  @Override

+  public void visitInnerClassType(String innerName) {

+    // Called after visitClass has already been called, and we will

+    // successively refine the class by going into its inner classes.

+    assert returnTypeRef[0] != null;

+    resolveGenerics();

+    outerClass = (JClassType) returnTypeRef[0];

+    JClassType searchClass = outerClass;

+    try {

+      JParameterizedType pt = searchClass.isParameterized();

+      if (pt != null) {

+        searchClass = pt.getBaseType();

+      }

+      returnTypeRef[0] = searchClass.getNestedType(innerName);

+    } catch (NotFoundException e) {

+      logger.log(TreeLogger.ERROR, "Unable to resolve inner class "

+          + innerName + " in " + searchClass, e);

+    }

+  }

+

+  @Override

+  public void visitTypeArgument() {

+    JType[] arg = new JType[1]; // This could be int[] for example

+    arg[0] = resolver.getTypeOracle().getWildcardType(

+        JWildcardType.BoundType.UNBOUND,

+        resolver.getTypeOracle().getJavaLangObject());

+    args.add(arg);

+  }

+

+  @Override

+  public SignatureVisitor visitTypeArgument(char wildcard) {

+    JType[] arg = new JType[1];

+    args.add(arg);

+    // TODO(jat): should we pass enclosingClass here instead of null?

+    //    not sure what the enclosing class of a type argument means, but

+    //    I haven't found a case where it is actually used while processing

+    //    the type argument.

+    return new ResolveTypeSignature(resolver, binaryMapper, logger,

+        arg, lookup, null, wildcard);

+  }

+

+  @Override

+  public void visitTypeVariable(String name) {

+    returnTypeRef[0] = lookup.lookup(name);

+    // this is the last visitor called on this visitor

+    visitEnd();

+  }

+

+  /**

+   * Merge the bounds from the declared type parameters into the type arguments

+   * for this type if necessary.

+   * 

+   * <pre>Example:

+   * class Foo<T extends Bar> ...

+   * 

+   * Foo<?> foo

+   * 

+   * foo needs to have bounds ? extends Bar.

+   * </pre>

+   * 

+   * <p>Currently we only deal with unbound wildcards as above, which matches

+   * existing TypeOracleMediator behavior.  However, this may need to be

+   * extended.

+   * 

+   * @param typeParams

+   * @param typeArgs

+   */

+  private void mergeTypeParamBounds(JTypeParameter[] typeParams,

+      JClassType[] typeArgs) {

+    int n = typeArgs.length;

+    for (int i = 0; i < n; ++i) {

+      JWildcardType wildcard = typeArgs[i].isWildcard();

+      // right now we only replace Foo<?> with the constraints defined on the

+      // definition (which appears to match the existing TypeOracleMediator)

+      // but other cases may need to be handled.

+      if (wildcard != null && wildcard.getBoundType() == BoundType.UNBOUND

+          && wildcard.getBaseType()

+          == resolver.getTypeOracle().getJavaLangObject()

+          && typeParams[i].getBaseType() != null) {

+        typeArgs[i] = resolver.getTypeOracle().getWildcardType(

+            BoundType.UNBOUND, typeParams[i].getBaseType());

+      }

+    }

+  }

+

+  private JType resolveGeneric(JType type, JClassType outer,

+      JClassType[] typeArgs) {

+    JGenericType genericType = type.isGenericType();

+    if (genericType != null) {

+      int actual = typeArgs.length;

+      JTypeParameter[] typeParams = genericType.getTypeParameters();

+      int expected = typeParams.length;

+      if (actual == 0 && expected > 0) {

+        // If no type parameters were supplied, this is a raw type usage.

+        type = genericType.getRawType();

+      } else {

+        if (actual != expected) {

+          throw new IllegalStateException("Incorrect # of type parameters to "

+            + genericType.getQualifiedBinaryName() + ": expected " + expected

+            + ", actual=" + actual);

+        }

+        JClassType genericEnc = genericType.getEnclosingType();

+        if (outer == null && genericEnc != null) {

+          // Sometimes the signature is like Foo$Bar<H> even if Foo is a

+          // generic class.  The cases I have seen are where Foo's type

+          // parameter is also named H and has the same bounds.  That

+          // manifests itself as getting visitClassType("Foo$Bar") and

+          // then VisitTypeArgument/etc, rather than the usual

+          // visitClassType("Foo"), visitTypeArgument/etc,

+          // visitInnerClass("Bar"), visitTypeArgument/etc.

+          //

+          // So, in this case we have to build our own chain of enclosing

+          // classes here, properly parameterizing any generics along the

+          // way.

+          // TODO(jat): more testing to validate this assumption

+          JClassType[] outerArgs = null;

+          JGenericType genericEncGeneric = genericEnc.isGenericType();

+          if (genericEncGeneric != null) {

+            JTypeParameter[] encTypeParams = genericEncGeneric.getTypeParameters();

+            int n = encTypeParams.length;

+            outerArgs = new JClassType[n];

+            for (int i = 0; i < n; ++i) {

+              outerArgs[i] = lookup.lookup(encTypeParams[i].getName());

+              if (outerArgs[i] == null) {

+                // check to see if our current type has a parameter of the same

+                // name, and use it if so.

+                for (int j = 0; j < expected; ++j) {

+                  if (typeParams[j].getName().equals(encTypeParams[j].getName())) {

+                    outerArgs[i] = typeArgs[j];

+                    break;

+                  }

+                }

+              }

+              assert outerArgs[i] != null

+                  : "Unable to resolve type parameter "

+                    + encTypeParams[i].getName() + " in enclosing type "

+                    + genericEnc + " of type " + genericType;

+            }

+          }

+          outer = (JClassType) resolveGeneric(genericEnc, null, outerArgs);

+        }

+        try {

+          mergeTypeParamBounds(typeParams, typeArgs);

+          type = resolver.getTypeOracle().getParameterizedType(genericType,

+              outer, typeArgs);

+        } catch (IllegalArgumentException e) {

+          // Can't use toString on typeArgs as they aren't completely built

+          // yet, so we have to roll our own.

+          StringBuilder buf = new StringBuilder();

+          buf.append("Unable to build parameterized type ");

+          buf.append(genericType);

+          String prefix = " with args <";

+          for (JClassType typeArg : typeArgs) {

+            buf.append(prefix).append(typeArg.getName());

+            prefix = ", ";

+          }

+          if (", ".equals(prefix)) {

+            buf.append('>');

+          }

+          logger.log(TreeLogger.ERROR, buf.toString(), e); 

+          type = genericType.getRawType();

+        }

+      }

+    }

+    return type;

+  }

+

+  private void resolveGenerics() {

+    JGenericType genericType = returnTypeRef[0].isGenericType();

+    if (genericType != null) {

+      int actual = args.size();

+      JClassType[] typeArgs = new JClassType[actual];

+      for (int i = 0; i < actual; ++i) {

+        JType type = args.get(i)[0];

+        if (!(type instanceof JClassType)) {

+          logger.log(TreeLogger.ERROR, "Parameterized type argument is "

+              + type + ", expected reference type");

+        } else {

+          typeArgs[i] = (JClassType) type;

+        }

+      }

+      returnTypeRef[0] = resolveGeneric(genericType, outerClass, typeArgs);

+      args.clear();

+    }

+    for (int i = 0; i < arrayDepth; ++i) {

+      returnTypeRef[0] = resolver.getTypeOracle().getArrayType(

+          returnTypeRef[0]);

+    }

+    switch (wildcardMatch) {

+      case '=':

+        // nothing to do for an exact match

+        break;

+      case '*':

+        returnTypeRef[0] = resolver.getTypeOracle().getWildcardType(

+            JWildcardType.BoundType.UNBOUND, (JClassType) returnTypeRef[0]);

+        break;

+      case '+':

+        // ? extends T

+        returnTypeRef[0] = resolver.getTypeOracle().getWildcardType(

+            JWildcardType.BoundType.EXTENDS, (JClassType) returnTypeRef[0]);

+        break;

+      case '-':

+        // ? super T

+        returnTypeRef[0] = resolver.getTypeOracle().getWildcardType(

+            JWildcardType.BoundType.SUPER, (JClassType) returnTypeRef[0]);

+        break;

+    }

+    if (returnTypeRef[0] instanceof JClassType) {

+      // Only JClassTypes can be an outer class

+      outerClass = (JClassType) returnTypeRef[0];

+    }

+  }

+}

diff --git a/dev/core/test/com/google/gwt/core/ext/typeinfo/HookableTypeOracle.java b/dev/core/test/com/google/gwt/core/ext/typeinfo/HookableTypeOracle.java
new file mode 100644
index 0000000..f30c225
--- /dev/null
+++ b/dev/core/test/com/google/gwt/core/ext/typeinfo/HookableTypeOracle.java
@@ -0,0 +1,13 @@
+package com.google.gwt.core.ext.typeinfo;

+

+/**

+ * A minimal TypeOracle mock that exposes the addNewType hook to subclasses.

+ */

+public class HookableTypeOracle extends TypeOracle {

+

+  // Increases visibility so tests in other packages can hook this.

+  @Override

+  protected void addNewType(JRealClassType newType) {

+    super.addNewType(newType);

+  }

+}

diff --git a/dev/core/test/com/google/gwt/core/ext/typeinfo/JEnumTypeTest.java b/dev/core/test/com/google/gwt/core/ext/typeinfo/JEnumTypeTest.java
index f10d243..69f1231 100644
--- a/dev/core/test/com/google/gwt/core/ext/typeinfo/JEnumTypeTest.java
+++ b/dev/core/test/com/google/gwt/core/ext/typeinfo/JEnumTypeTest.java
@@ -17,7 +17,10 @@
 
 import com.google.gwt.core.ext.TreeLogger;
 import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.typeinfo.test.EnumInterface;
+import com.google.gwt.core.ext.typeinfo.test.EnumOfInterface;
 import com.google.gwt.core.ext.typeinfo.test.MyEnum;
+import com.google.gwt.core.ext.typeinfo.test.TestAnnotation;
 import com.google.gwt.dev.util.log.PrintWriterTreeLogger;
 
 import junit.framework.TestCase;
@@ -105,7 +108,8 @@
     JClassType type = typeOracle.getType(MyEnum.class.getName());
     JEnumType enumType = validateTypeIsEnum(type);
 
-    assertEquals(1, enumType.getConstructors().length);
+    // Enum constructors are not reflected.
+    assertEquals(0, enumType.getConstructors().length);
   }
 
   /**
@@ -121,6 +125,43 @@
   }
 
   /**
+   * Test an enum that implements an interface.
+   * 
+   * @throws NotFoundException 
+   */
+  public void testInterface() throws NotFoundException {
+    JClassType type = typeOracle.getType(EnumOfInterface.class.getName());
+    JEnumType enumType = validateTypeIsEnum(type);
+    JClassType[] intf = enumType.getImplementedInterfaces();
+    assertEquals(1, intf.length);
+    assertEquals(EnumInterface.class.getName(),
+        intf[0].getQualifiedSourceName());
+    JMethod getExtra = intf[0].getMethod("getExtra", new JType[0]);
+    TestAnnotation annotation = getExtra.getAnnotation(TestAnnotation.class);
+    assertNotNull(annotation);
+    assertEquals("EnumInterface getExtra", annotation.value());
+    JEnumConstant[] constants = enumType.getEnumConstants();
+    assertEquals(2, constants.length);
+    assertEquals("A", constants[0].getName());
+    annotation = constants[0].getAnnotation(TestAnnotation.class);
+    assertNotNull(annotation);
+    assertEquals("A", annotation.value());
+    JClassType aClass = constants[0].getType().isClass();
+    JMethod[] methods = aClass.getOverridableMethods();
+    assertEquals(7, methods.length);
+    // TODO(jat): verify getExtra is from A's anonymous subclass of
+    //     EnumInterface when/if that is implemented.
+    boolean found = false;
+    for (JMethod method : methods) {
+      if ("name".equals(method.getName())) {
+        found = true;
+        // TODO(jat); any other verification here?
+      }
+    }
+    assertTrue(found);
+  }
+
+  /**
    * Test method for {@link com.google.gwt.core.ext.typeinfo.JEnumType#isEnum()}.
    * 
    * @throws NotFoundException
diff --git a/dev/core/test/com/google/gwt/core/ext/typeinfo/test/EnumInterface.java b/dev/core/test/com/google/gwt/core/ext/typeinfo/test/EnumInterface.java
new file mode 100644
index 0000000..96eb24a
--- /dev/null
+++ b/dev/core/test/com/google/gwt/core/ext/typeinfo/test/EnumInterface.java
@@ -0,0 +1,37 @@
+/*

+ * 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.core.ext.typeinfo.test;

+

+/**

+ * Interface used to test enums that implement interfaces.

+ */

+public interface EnumInterface {

+

+  /**

+   * This is a built-in method from Enum<E>.

+   * 

+   * @return the name of the enum

+   */

+  String name();

+  

+  /**

+   * Totally new method.

+   * 

+   * @return some arbitrary extra data

+   */

+  @TestAnnotation("EnumInterface getExtra")

+  String getExtra();

+}

diff --git a/dev/core/test/com/google/gwt/core/ext/typeinfo/test/EnumOfInterface.java b/dev/core/test/com/google/gwt/core/ext/typeinfo/test/EnumOfInterface.java
new file mode 100644
index 0000000..55a4b2b
--- /dev/null
+++ b/dev/core/test/com/google/gwt/core/ext/typeinfo/test/EnumOfInterface.java
@@ -0,0 +1,38 @@
+/*

+ * 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.core.ext.typeinfo.test;

+

+/**

+ * Enumerated type used in the

+ * {@link com.google.gwt.core.ext.typeinfo.JEnumTypeTest}.

+ */

+public enum EnumOfInterface implements EnumInterface {

+  @TestAnnotation("A")

+  A {

+    @TestAnnotation("A getExtra")

+    @Override

+    public String getExtra() {

+      return "A extra";

+    }

+  },

+  

+  B;

+

+  // Default just returns null

+  public String getExtra() {

+    return null;

+  }

+}

diff --git a/dev/core/test/com/google/gwt/dev/javac/JavaCompilationSuite.java b/dev/core/test/com/google/gwt/dev/javac/JavaCompilationSuite.java
index 9b0ceaa..7085918 100644
--- a/dev/core/test/com/google/gwt/dev/javac/JavaCompilationSuite.java
+++ b/dev/core/test/com/google/gwt/dev/javac/JavaCompilationSuite.java
@@ -31,6 +31,7 @@
     suite.addTestSuite(CompilationStateTest.class);
     suite.addTestSuite(CompilationUnitFileReferenceTest.class);
     suite.addTestSuite(GWTProblemTest.class);
+    suite.addTestSuite(JavaSourceParserTest.class);
     suite.addTestSuite(JdtBehaviorTest.class);
     suite.addTestSuite(JdtCompilerTest.class);
     suite.addTestSuite(JProgramLastFragmentLoadingBeforeTest.class);
diff --git a/dev/core/test/com/google/gwt/dev/javac/JavaSourceParserTest.java b/dev/core/test/com/google/gwt/dev/javac/JavaSourceParserTest.java
new file mode 100644
index 0000000..daf4f99
--- /dev/null
+++ b/dev/core/test/com/google/gwt/dev/javac/JavaSourceParserTest.java
@@ -0,0 +1,188 @@
+/*

+ * 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.javac;

+

+import com.google.gwt.core.ext.typeinfo.JClassType;

+import com.google.gwt.core.ext.typeinfo.JMethod;

+import com.google.gwt.core.ext.typeinfo.JPrimitiveType;

+import com.google.gwt.core.ext.typeinfo.JType;

+import com.google.gwt.core.ext.typeinfo.NotFoundException;

+import com.google.gwt.dev.javac.impl.MockJavaResource;

+

+import java.util.Arrays;

+import java.util.List;

+

+/**

+ * Tests for {@link JavaSourceParser}.

+ */

+public class JavaSourceParserTest extends CompilationStateTestBase {

+

+  private static final MockJavaResource FOO = new MockJavaResource("test.Foo") {

+    @Override

+    protected CharSequence getContent() {

+      StringBuffer code = new StringBuffer();

+      code.append("package test;\n");

+      code.append("public class Foo {\n");

+      code.append("  public String value(String a, int val) { return \"Foo\"; }\n");

+      code.append("}\n");

+      return code;

+    }

+  };

+

+  private static final MockJavaResource BAR = new MockJavaResource("test.Bar") {

+    @Override

+    protected CharSequence getContent() {

+      StringBuffer code = new StringBuffer();

+      code.append("package test;\n");

+      code.append("public class Bar {\n");

+      code.append("  public String value(String a, int val) { return \"Bar\"; }\n");

+      code.append("  public String value(String a) { return \"Bar\"; }\n");

+      code.append("  public String value(int val) { return \"Bar\"; }\n");

+      code.append("}\n");

+      return code;

+    }

+  };

+

+  private static final MockJavaResource BAZ = new MockJavaResource("test.Baz") {

+    @Override

+    protected CharSequence getContent() {

+      StringBuffer code = new StringBuffer();

+      code.append("package test;\n");

+      code.append("public class Baz {\n");

+      code.append("  public static class Baz1 {\n");

+      code.append("    public String value(String a) { return \"Baz1\"; }\n");

+      code.append("    public String value(int val) { return \"Baz1\"; }\n");

+      code.append("   }\n");

+      code.append("  public class Baz2 {\n");

+      code.append("    public String value(String a) { return \"Baz2\"; }\n");

+      code.append("    public String value(int val) { return \"Baz2\"; }\n");

+      code.append("   }\n");

+      code.append("}\n");

+      return code;

+    }

+  };

+

+  private static final MockJavaResource FOO_INT = new MockJavaResource("test.FooInt") {

+    @Override

+    protected CharSequence getContent() {

+      StringBuffer code = new StringBuffer();

+      code.append("package test;\n");

+      code.append("public interface FooInt {\n");

+      code.append("  String value(String a, int val);\n");

+      code.append("}\n");

+      return code;

+    }

+  };

+

+  /**

+   * Test method for {@link JavaSourceParser#getClassChain(java.lang.String)}.

+   */

+  public void testGetClassChain() {

+    assertExpected(JavaSourceParser.getClassChain("Foo"), "Foo");

+    assertExpected(JavaSourceParser.getClassChain("test.Foo"), "Foo");

+    assertExpected(JavaSourceParser.getClassChain("Foo$Bar"), "Foo", "Bar");

+    assertExpected(JavaSourceParser.getClassChain("test.Foo$Bar"),

+        "Foo", "Bar");

+    assertExpected(JavaSourceParser.getClassChain("test.test2.Foo$Bar$Baz"),

+        "Foo", "Bar", "Baz");

+  }

+

+  public void testLookup() throws NotFoundException {

+    JavaSourceParser parser = new JavaSourceParser();

+    addGeneratedUnits(FOO);

+    addGeneratedUnits(BAR);

+    addGeneratedUnits(BAZ);

+    JClassType string = state.getTypeOracle().getType("java.lang.String");

+    JClassType foo = state.getTypeOracle().getType("test.Foo");

+    parser.addSourceForType(foo, FOO);

+    JClassType bar = state.getTypeOracle().getType("test.Bar");

+    parser.addSourceForType(bar, BAR);

+    JClassType baz = state.getTypeOracle().getType("test.Baz");

+    parser.addSourceForType(baz, BAZ);

+    JClassType baz1 = state.getTypeOracle().getType("test.Baz.Baz1");

+    JClassType baz2 = state.getTypeOracle().getType("test.Baz.Baz2");

+    JMethod method = foo.getMethod("value", new JType[] { string,

+        JPrimitiveType.INT });

+    String[] arguments = parser.getArguments(method);

+    assertNotNull(arguments);

+    assertEquals(2, arguments.length);

+    assertEquals("a", arguments[0]);

+    assertEquals("val", arguments[1]);

+    method = bar.getMethod("value", new JType[] { string,

+        JPrimitiveType.INT });

+    arguments = parser.getArguments(method);

+    assertNotNull(arguments);

+    assertEquals(2, arguments.length);

+    assertEquals("a", arguments[0]);

+    assertEquals("val", arguments[1]);

+    method = bar.getMethod("value", new JType[] { JPrimitiveType.INT });

+    arguments = parser.getArguments(method);

+    assertNotNull(arguments);

+    assertEquals(1, arguments.length);

+    assertEquals("val", arguments[0]);

+    method = bar.getMethod("value", new JType[] { string });

+    arguments = parser.getArguments(method);

+    assertNotNull(arguments);

+    assertEquals(1, arguments.length);

+    assertEquals("a", arguments[0]);

+    method = baz1.getMethod("value", new JType[] { JPrimitiveType.INT });

+    arguments = parser.getArguments(method);

+    assertNotNull(arguments);

+    assertEquals(1, arguments.length);

+    assertEquals("val", arguments[0]);

+    method = baz1.getMethod("value", new JType[] { string });

+    arguments = parser.getArguments(method);

+    assertNotNull(arguments);

+    assertEquals(1, arguments.length);

+    assertEquals("a", arguments[0]);

+    method = baz2.getMethod("value", new JType[] { JPrimitiveType.INT });

+    arguments = parser.getArguments(method);

+    assertNotNull(arguments);

+    assertEquals(1, arguments.length);

+    assertEquals("val", arguments[0]);

+    method = baz2.getMethod("value", new JType[] { string });

+    arguments = parser.getArguments(method);

+    assertNotNull(arguments);

+    assertEquals(1, arguments.length);

+    assertEquals("a", arguments[0]);

+  }

+

+  public void testParamNames() throws NotFoundException {

+    JavaSourceParser parser = new JavaSourceParser();

+    addGeneratedUnits(FOO_INT);

+    JClassType string = state.getTypeOracle().getType("java.lang.String");

+    JClassType fooInt = state.getTypeOracle().getType("test.FooInt");

+    parser.addSourceForType(fooInt, FOO_INT);

+    JMethod method = fooInt.getMethod("value", new JType[] { string,

+        JPrimitiveType.INT });

+    String[] arguments = parser.getArguments(method);

+    assertNotNull(arguments);

+    assertEquals(2, arguments.length);

+    assertEquals("a", arguments[0]);

+    assertEquals("val", arguments[1]);

+  }

+

+  private void assertExpected(List<char[]> actual, String... expected) {

+    if (actual.size() != expected.length) {

+      fail("Expected " + Arrays.deepToString(expected) + ", got " + actual);

+    }

+    for (int i = 0; i < expected.length; ++i) {

+      assertTrue("index " + i + " should be " + expected[i] + ", got "

+          + Arrays.toString(actual.get(i)), Arrays.equals(actual.get(i),

+              expected[i].toCharArray()));

+    }

+  }

+}

diff --git a/dev/core/test/com/google/gwt/dev/javac/TypeOracleMediatorTest.java b/dev/core/test/com/google/gwt/dev/javac/TypeOracleMediatorTest.java
index 32d8358..7b5ce10 100644
--- a/dev/core/test/com/google/gwt/dev/javac/TypeOracleMediatorTest.java
+++ b/dev/core/test/com/google/gwt/dev/javac/TypeOracleMediatorTest.java
@@ -18,10 +18,15 @@
 import com.google.gwt.core.ext.TreeLogger;
 import com.google.gwt.core.ext.typeinfo.JArrayType;
 import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.JConstructor;
 import com.google.gwt.core.ext.typeinfo.JField;
+import com.google.gwt.core.ext.typeinfo.JGenericType;
 import com.google.gwt.core.ext.typeinfo.JMethod;
+import com.google.gwt.core.ext.typeinfo.JParameterizedType;
 import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
 import com.google.gwt.core.ext.typeinfo.JType;
+import com.google.gwt.core.ext.typeinfo.JTypeParameter;
+import com.google.gwt.core.ext.typeinfo.JWildcardType;
 import com.google.gwt.core.ext.typeinfo.NotFoundException;
 import com.google.gwt.core.ext.typeinfo.TypeOracle;
 import com.google.gwt.core.ext.typeinfo.TypeOracleException;
@@ -235,6 +240,42 @@
     }
   };
 
+  protected CheckedJavaResource CU_ConstrainedList = new CheckedJavaResource(
+      "test", "ConstrainedList") {
+    @Override
+    public void check(JClassType type) throws NotFoundException {
+      assertNotNull(type.isGenericType());
+    }
+
+    @Override
+    public String getSource() {
+      StringBuilder sb = new StringBuilder();
+      sb.append("package test;\n");
+      sb.append("public interface ConstrainedList<E extends Throwable> {\n");
+      sb.append("}");
+      return sb.toString();
+    }
+  };
+
+  protected CheckedJavaResource CU_ConstrainedListAsField = new CheckedJavaResource(
+      "test", "ConstrainedListAsField") {
+    @Override
+    public void check(JClassType type) throws NotFoundException {
+      assertNull(type.isGenericType());
+      assertNull(type.getEnclosingType());
+    }
+
+    @Override
+    public String getSource() {
+      StringBuilder sb = new StringBuilder();
+      sb.append("package test;\n");
+      sb.append("public class ConstrainedListAsField {\n");
+      sb.append("  private ConstrainedList<?> field;");
+      sb.append("}");
+      return sb.toString();
+    }
+  };
+
   protected CheckedJavaResource CU_DeclaresInnerGenericType = new CheckedJavaResource(
       "parameterized.type.build.dependency", "Class1", "Class1.Inner") {
     @Override
@@ -293,6 +334,47 @@
     }
   };
 
+  protected CheckedJavaResource CU_ExtendsGenericOuterInner = new CheckedJavaResource(
+      "test", "ExtendsOuter", "ExtendsOuter.ExtendsInner") {
+
+    public void check(JClassType type) {
+      final String name = type.getSimpleSourceName();
+      if ("ExtendsOuter".equals(name)) {
+        checkOuter(type);
+      } else {
+        checkInner(type);
+      }
+    }
+
+    public void checkInner(JClassType type) {
+      assertEquals("ExtendsInner", type.getSimpleSourceName());
+      assertEquals("test.ExtendsOuter.ExtendsInner",
+          type.getQualifiedSourceName());
+      assertEquals("test.ExtendsOuter",
+          type.getEnclosingType().getQualifiedSourceName());
+    }
+
+    public void checkOuter(JClassType type) {
+      assertEquals("ExtendsOuter", type.getSimpleSourceName());
+      assertEquals("test.ExtendsOuter", type.getQualifiedSourceName());
+      JClassType[] nested = type.getNestedTypes();
+      assertEquals(1, nested.length);
+      JClassType inner = nested[0];
+      assertEquals("test.ExtendsOuter.ExtendsInner",
+          inner.getQualifiedSourceName());
+    }
+
+    public String getSource() {
+      StringBuffer sb = new StringBuffer();
+      sb.append("package test;\n");
+      sb.append("public class ExtendsOuter extends Outer<Object> {\n");
+      sb.append("   public class ExtendsInner extends Inner {\n");
+      sb.append("   }\n");
+      sb.append("}\n");
+      return sb.toString();
+    }
+  };
+
   protected CheckedJavaResource CU_ExtendsParameterizedType = new CheckedJavaResource(
       "parameterized.type.build.dependency", "Class2") {
     @Override
@@ -442,6 +524,48 @@
     }
   };
 
+  protected CheckedJavaResource CU_GenericOuterInner = new CheckedJavaResource(
+      "test", "Outer", "Outer.Inner") {
+
+    public void check(JClassType type) {
+      final String name = type.getSimpleSourceName();
+      if ("GenericOuter".equals(name)) {
+        checkOuter(type);
+      } else {
+        checkInner(type);
+      }
+    }
+
+    public void checkInner(JClassType type) {
+      assertEquals("Inner", type.getSimpleSourceName());
+      assertEquals("test.Outer.Inner", type.getQualifiedSourceName());
+      assertEquals("test.Outer",
+          type.getEnclosingType().getQualifiedSourceName());
+    }
+
+    public void checkOuter(JClassType type) {
+      assertEquals("Outer", type.getSimpleSourceName());
+      assertEquals("test.Outer", type.getQualifiedSourceName());
+      JClassType[] nested = type.getNestedTypes();
+      assertEquals(1, nested.length);
+      JClassType inner = nested[0];
+      assertEquals("test.Outer.Inner", inner.getQualifiedSourceName());
+    }
+
+    public String getSource() {
+      StringBuffer sb = new StringBuffer();
+      sb.append("package test;\n");
+      sb.append("import java.util.List;\n");
+      sb.append("public class Outer<V> {\n");
+      sb.append("   public class Inner {\n");
+      sb.append("     private V field;\n");
+      sb.append("     private List<V> list;\n");
+      sb.append("   }\n");
+      sb.append("}\n");
+      return sb.toString();
+    }
+  };
+
   protected CheckedJavaResource CU_HasSyntaxErrors = new CheckedJavaResource(
       "test", "HasSyntaxErrors", "NoSyntaxErrors") {
     @Override
@@ -476,34 +600,62 @@
     }
   };
 
+  protected CheckedJavaResource CU_List = new CheckedJavaResource("java.util",
+      "List") {
+    @Override
+    public void check(JClassType type) throws NotFoundException {
+      assertNotNull(type.isGenericType());
+    }
+
+    @Override
+    public String getSource() {
+      StringBuilder sb = new StringBuilder();
+      sb.append("package java.util;\n");
+      sb.append("public interface List<E> {\n");
+      sb.append("}");
+      return sb.toString();
+    }
+  };
+
+  protected CheckedJavaResource CU_ListAsField = new CheckedJavaResource(
+      "test.refresh", "ListAsField") {
+    @Override
+    public void check(JClassType type) throws NotFoundException {
+      assertNull(type.isGenericType());
+      assertNull(type.getEnclosingType());
+    }
+
+    @Override
+    public String getSource() {
+      StringBuilder sb = new StringBuilder();
+      sb.append("package test.refresh;\n");
+      sb.append("import java.util.List;\n");
+      sb.append("public class ListAsField {\n");
+      sb.append("  private List<Object> field;");
+      sb.append("}");
+      return sb.toString();
+    }
+  };
+
   protected CheckedJavaResource CU_LocalClass = new CheckedJavaResource("test",
       "Enclosing", "Enclosing.1") {
 
+    @Override
     public void check(JClassType type) {
       final String name = type.getSimpleSourceName();
-      if ("Enclosing".equals(name)) {
-        checkEnclosing(type);
-      } else {
-        checkLocal(type);
-      }
+      assertEquals("Enclosing", name);
+      checkEnclosing(type);
     }
 
     public void checkEnclosing(JClassType type) {
       assertEquals("Enclosing", type.getSimpleSourceName());
       assertEquals("test.Enclosing", type.getQualifiedSourceName());
+      // verify the anonymous class doesn't show up
       JClassType[] nested = type.getNestedTypes();
-      assertEquals(1, nested.length);
-      JClassType inner = nested[0];
-      assertEquals("test.Enclosing.1", inner.getQualifiedSourceName());
+      assertEquals(0, nested.length);
     }
 
-    public void checkLocal(JClassType type) {
-      assertEquals("1", type.getSimpleSourceName());
-      assertEquals("test.Enclosing.1", type.getQualifiedSourceName());
-      assertEquals("test.Enclosing",
-          type.getEnclosingType().getQualifiedSourceName());
-    }
-
+    @Override
     public String getSource() {
       StringBuffer sb = new StringBuffer();
       sb.append("package test;\n");
@@ -591,6 +743,46 @@
     }
   };
 
+  protected CheckedJavaResource CU_NestedGenericInterfaces = new CheckedJavaResource(
+      "test", "OuterInt", "OuterInt.InnerInt") {
+
+    @Override
+    public void check(JClassType type) {
+      final String name = type.getSimpleSourceName();
+      if ("OuterInt".equals(name)) {
+        checkOuter(type);
+      } else {
+        checkInner(type);
+      }
+    }
+
+    public void checkInner(JClassType type) {
+      assertEquals("InnerInt", type.getSimpleSourceName());
+      assertEquals("test.OuterInt.InnerInt", type.getQualifiedSourceName());
+      assertEquals("test.OuterInt",
+          type.getEnclosingType().getQualifiedSourceName());
+    }
+
+    public void checkOuter(JClassType type) {
+      assertEquals("OuterInt", type.getSimpleSourceName());
+      assertEquals("test.OuterInt", type.getQualifiedSourceName());
+      JClassType[] nested = type.getNestedTypes();
+      assertEquals(1, nested.length);
+      JClassType inner = nested[0];
+      assertEquals("test.OuterInt.InnerInt", inner.getQualifiedSourceName());
+    }
+
+    @Override
+    public String getSource() {
+      StringBuffer sb = new StringBuffer();
+      sb.append("package test;\n");
+      sb.append("public interface OuterInt<K,V> {\n");
+      sb.append("   public interface InnerInt<V> { }\n");
+      sb.append("}\n");
+      return sb.toString();
+    }
+  };
+
   protected CheckedJavaResource CU_Object = new CheckedJavaResource(
       "java.lang", "Object") {
     @Override
@@ -719,6 +911,26 @@
     }
   };
 
+  protected CheckedJavaResource CU_UnnestedImplementations = new CheckedJavaResource(
+      "test", "Implementations") {
+    @Override
+    public void check(JClassType type) {
+      assertEquals("Implementations", type.getSimpleSourceName());
+      assertEquals("test.Implementations", type.getQualifiedSourceName());
+    }
+
+    @Override
+    public String getSource() {
+      StringBuffer sb = new StringBuffer();
+      sb.append("package test;");
+      sb.append("public class Implementations {");
+      sb.append("  public static class OuterImpl<K,V> implements OuterInt<K,V> {}");
+      sb.append("  public static class InnerImpl<V> implements OuterInt.InnerInt<V> {}");
+      sb.append("}");
+      return sb.toString();
+    }
+  };
+
   private TypeOracle typeOracle;
 
   private final Set<Resource> resources = new HashSet<Resource>();
@@ -832,6 +1044,71 @@
     assertEquals(4, types.length);
   }
 
+  public void testConstructors() throws TypeOracleException {
+    resources.add(CU_Object);
+    compileAndRefresh();
+    JClassType[] types = typeOracle.getTypes();
+    assertEquals(1, types.length);
+    JClassType objectType = types[0];
+    assertEquals("Object", objectType.getSimpleSourceName());
+    JConstructor[] ctors = objectType.getConstructors();
+    assertEquals(1, ctors.length);
+    JConstructor defaultCtor = ctors[0];
+    assertEquals("Object", defaultCtor.getName());
+    assertEquals(0, defaultCtor.getParameters().length);
+  }
+
+  public void testConstrainedList() throws TypeOracleException {
+    resources.add(CU_Object);
+    resources.add(CU_Throwable);
+    resources.add(CU_ConstrainedList);
+
+    compileAndRefresh();
+
+    JClassType type = typeOracle.getType("test.ConstrainedList");
+    JClassType throwable = typeOracle.getType("java.lang.Throwable");
+
+    assertNull(type.isParameterized());
+    JGenericType genericType = type.isGenericType();
+    assertNotNull(genericType);
+    JTypeParameter[] typeParams = genericType.getTypeParameters();
+    assertEquals(1, typeParams.length);
+    assertEquals(throwable, typeParams[0].getBaseType());
+    assertEquals(type, typeParams[0].getDeclaringClass());
+    JClassType[] bounds = typeParams[0].getBounds();
+    assertEquals(1, bounds.length);
+    assertEquals(throwable, bounds[0]);
+  }
+
+  public void testConstrainedField() throws TypeOracleException {
+    resources.add(CU_Object);
+    resources.add(CU_Throwable);
+    resources.add(CU_ConstrainedList);
+    resources.add(CU_ConstrainedListAsField);
+
+    compileAndRefresh();
+
+    // Get the types produced by the TypeOracle
+    JClassType type = typeOracle.getType("test.ConstrainedListAsField");
+
+    assertNull(type.isParameterized());
+    JField field = type.getField("field");
+    assertNotNull(field);
+    JType fieldType = field.getType();
+    JParameterizedType fieldParamType = fieldType.isParameterized();
+    assertNotNull(fieldParamType);
+    assertNull(fieldParamType.getEnclosingType());
+    JGenericType baseType = fieldParamType.getBaseType();
+    assertNotNull(baseType);
+    assertEquals("test.ConstrainedList", baseType.getQualifiedSourceName());
+    JClassType[] typeArgs = fieldParamType.getTypeArgs();
+    assertEquals(1, typeArgs.length);
+    JWildcardType wildcard = typeArgs[0].isWildcard();
+    assertNotNull(wildcard);
+    JClassType upperBound = wildcard.getUpperBound();
+    assertEquals("Throwable", upperBound.getSimpleSourceName());
+  }
+
   public void testDefaultClass() throws TypeOracleException {
     resources.add(CU_Object);
     resources.add(CU_DefaultClass);
@@ -840,6 +1117,69 @@
     assertEquals(2, types.length);
   }
 
+  public void testEnclosingGenericType() throws TypeOracleException {
+    resources.add(CU_Object);
+    resources.add(CU_List);
+    resources.add(CU_GenericOuterInner);
+    resources.add(CU_ExtendsGenericOuterInner);
+
+    compileAndRefresh();
+
+    // Get the types produced by the TypeOracle
+    JClassType outer = typeOracle.getType("test.Outer");
+    JClassType inner = typeOracle.getType("test.Outer.Inner");
+
+    assertNull(outer.getEnclosingType());
+    assertEquals(outer, inner.getEnclosingType());
+    assertNull(inner.isParameterized());
+    assertNotNull(outer.isGenericType());
+    assertNotNull(inner.isGenericType());
+    JField field = inner.getField("field");
+    assertNotNull(field);
+    JType fieldType = field.getType();
+    JTypeParameter typeParam = fieldType.isTypeParameter();
+    assertNotNull(typeParam);
+    assertEquals("V", typeParam.getName());
+    JClassType[] bounds = typeParam.getBounds();
+    assertEquals(1, bounds.length);
+    assertEquals(typeOracle.getJavaLangObject(), bounds[0]);
+
+    JClassType extendsOuter = typeOracle.getType("test.ExtendsOuter");
+    JClassType extendsInner = typeOracle.getType("test.ExtendsOuter.ExtendsInner");
+    assertNull(extendsOuter.getEnclosingType());
+    assertEquals(extendsOuter, extendsInner.getEnclosingType());
+    JClassType outerSuper = extendsOuter.getSuperclass();
+    JParameterizedType outerSuperParam = outerSuper.isParameterized();
+    assertNotNull(outerSuperParam);
+    assertEquals(outer, outerSuperParam.getBaseType());
+    JClassType innerSuper = extendsInner.getSuperclass();
+    JParameterizedType innerSuperParam = innerSuper.isParameterized();
+    assertNotNull(innerSuperParam);
+    assertEquals(inner, innerSuperParam.getBaseType());
+  }
+
+  public void testEnclosingType() throws TypeOracleException {
+    resources.add(CU_Object);
+    resources.add(CU_List);
+    resources.add(CU_ListAsField);
+
+    compileAndRefresh();
+
+    // Get the types produced by the TypeOracle
+    JClassType listAsField = typeOracle.getType("test.refresh.ListAsField");
+
+    assertNull(listAsField.isParameterized());
+    JField field = listAsField.getField("field");
+    assertNotNull(field);
+    JType fieldType = field.getType();
+    JParameterizedType fieldParamType = fieldType.isParameterized();
+    assertNotNull(fieldParamType);
+    assertNull(fieldParamType.getEnclosingType());
+    JGenericType baseType = fieldParamType.getBaseType();
+    assertNotNull(baseType);
+    assertEquals("java.util.List", baseType.getQualifiedSourceName());
+  }
+
   public void testFieldsAndTypes() throws TypeOracleException {
     resources.add(CU_Object);
     resources.add(CU_FieldsAndTypes);
@@ -848,12 +1188,13 @@
     assertEquals(3, types.length);
   }
 
+  // Check that anonymous classes are not reflected in TypeOracle
   public void testLocal() throws TypeOracleException {
     resources.add(CU_Object);
     resources.add(CU_LocalClass);
     compileAndRefresh();
     JClassType[] types = typeOracle.getTypes();
-    assertEquals(3, types.length);
+    assertEquals(2, types.length);
   }
 
   public void testMethodsAndParams() throws TypeOracleException {
@@ -871,6 +1212,16 @@
     compileAndRefresh();
     JClassType[] types = typeOracle.getTypes();
     assertEquals(3, types.length);
+    JClassType outer = null;
+    for (JClassType type : types) {
+      if ("Outer".equals(type.getSimpleSourceName())) {
+        outer = type;
+        break;
+      }
+    }
+    assertNotNull(outer);
+    JClassType superclass = outer.getSuperclass();
+    assertEquals(typeOracle.getJavaLangObject(), superclass);
   }
 
   /**
@@ -1034,6 +1385,24 @@
     assertEquals("java.lang.Object", types[0].getQualifiedSourceName());
   }
 
+  public void testTypeParams() throws TypeOracleException {
+    resources.add(CU_Object);
+    resources.add(CU_NestedGenericInterfaces);
+    resources.add(CU_UnnestedImplementations);
+    compileAndRefresh();
+    JClassType[] types = typeOracle.getTypes();
+    assertEquals(6, types.length);
+    JClassType type = typeOracle.findType("test.Implementations.InnerImpl");
+    assertNotNull(type);
+    JClassType[] interfaces = type.getImplementedInterfaces();
+    assertEquals(1, interfaces.length);
+    JClassType intf = interfaces[0];
+    JParameterizedType intfParam = intf.isParameterized();
+    assertNotNull(intfParam);
+    JClassType intfEnclosing = intf.getEnclosingType();
+    assertNotNull(intfEnclosing.isRawType());
+  }
+
   public void testUnresolvedSymbls() throws TypeOracleException {
     resources.add(CU_Object);
     resources.add(CU_HasUnresolvedSymbols);
diff --git a/dev/core/test/com/google/gwt/dev/javac/asm/AsmTestCase.java b/dev/core/test/com/google/gwt/dev/javac/asm/AsmTestCase.java
new file mode 100644
index 0000000..4ec878a
--- /dev/null
+++ b/dev/core/test/com/google/gwt/dev/javac/asm/AsmTestCase.java
@@ -0,0 +1,89 @@
+/*

+ * 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.javac.asm;

+

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

+

+import junit.framework.TestCase;

+

+import java.io.InputStream;

+import java.net.URL;

+

+/**

+ * Base class for ASM unit tests that defines some useful methods.

+ */

+public abstract class AsmTestCase extends TestCase {

+

+  private static final ClassLoader CLASSLOADER = CollectClassDataTest.class.getClassLoader();

+

+  public AsmTestCase() {

+    super();

+  }

+

+  public AsmTestCase(String name) {

+    super(name);

+  }

+

+  /**

+   * Read the bytes of a class.

+   * 

+   * @param clazz class literal of the class to read

+   * @return bytes from class file or null if not found

+   */

+  protected byte[] getClassBytes(Class<?> clazz) {

+    return getClassBytes(clazz.getName());

+  }

+

+  /**

+   * Read the bytes of a class.

+   * 

+   * @param className binary name (ie com.Foo$Bar) of the class to read

+   * @return bytes from class file or null if not found

+   */

+  protected byte[] getClassBytes(String className) {

+    URL resource = CLASSLOADER.getResource(

+        className.replace('.', '/') + ".class");

+    if (resource == null) {

+      return null;

+    }

+    return Util.readURLAsBytes(resource);

+  }

+

+  /**

+   * Reads the source for a class.

+   * 

+   * @param clazz class literal of the class to read

+   * @return source from .java file or null if not found

+   */

+  protected String getClassSource(Class<?> clazz) {

+    return getClassSource(clazz.getName());

+  }

+

+  /**

+   * Reads the source for a class.

+   * 

+   * @param className binary name (ie com.Foo$Bar) of the class to read

+   * @return source from .java file or null if not found

+   */

+  protected String getClassSource(String className) {

+    InputStream str = CLASSLOADER.getResourceAsStream(

+        className.replace('.', '/') + ".java");

+    if (str == null) {

+      return null;

+    }

+    return Util.readStreamAsString(str);

+  }

+}

diff --git a/dev/core/test/com/google/gwt/dev/javac/asm/CollectClassDataTest.java b/dev/core/test/com/google/gwt/dev/javac/asm/CollectClassDataTest.java
new file mode 100644
index 0000000..08ca510
--- /dev/null
+++ b/dev/core/test/com/google/gwt/dev/javac/asm/CollectClassDataTest.java
@@ -0,0 +1,231 @@
+/*

+ * 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.javac.asm;

+

+import com.google.gwt.core.ext.typeinfo.test.PrimitiveValuesAnnotation;

+import com.google.gwt.core.ext.typeinfo.test.TestAnnotation;

+import com.google.gwt.dev.asm.AnnotationVisitor;

+import com.google.gwt.dev.asm.ClassReader;

+import com.google.gwt.dev.asm.Opcodes;

+import com.google.gwt.dev.asm.Type;

+import com.google.gwt.dev.asm.commons.EmptyVisitor;

+import com.google.gwt.dev.javac.asm.CollectAnnotationData.AnnotationData;

+import com.google.gwt.dev.javac.asm.CollectClassData.ClassType;

+

+import java.util.List;

+

+/**

+ * Tests for {@link CollectClassData}.

+ */

+public class CollectClassDataTest extends AsmTestCase {

+

+  public static class One extends EmptyVisitor {

+

+    @Override

+    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {

+      new EmptyVisitor() {

+        @Override

+        public void visit(int version, int access, String name,

+            String signature, String superName, String[] interfaces) {

+        }

+      };

+      return new CollectAnnotationData(desc, visible);

+    }

+  }

+

+  @PrimitiveValuesAnnotation(b = 42, i = 42)

+  protected static class Two {

+

+    public class TwoInner {      

+    }

+

+    private String field;

+    

+    @TestAnnotation("field")

+    private String annotatedField;

+    

+    public Two(int a) {

+      this(a, null);

+    }

+    

+    @TestAnnotation("foo")

+    public String foo(int a) throws IllegalStateException {

+      return annotatedField;

+    }

+    

+    public Two(int a, String b) {

+      new TwoInner();

+      field = b;

+      annotatedField = field;

+    }

+  }

+

+  public void testAnonymous() {

+    CollectClassData cd = collect(One.class.getName() + "$1");

+    // Don't access on anonymous classes, it varies among compilers

+    // assertEquals(0, cd.getAccess() & ~Opcodes.ACC_SUPER);

+    assertEquals(ClassType.Anonymous, cd.getClassType());

+    assertEquals(0, cd.getFields().size());

+    List<CollectMethodData> methods = cd.getMethods();

+    assertEquals(2, methods.size());

+  }

+

+  public void testOne() {

+    CollectClassData cd = collect(One.class);

+    // Don't check for super bit, as it will depend on the JDK used to compile.

+    assertEquals(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC,

+        cd.getAccess() & ~Opcodes.ACC_SUPER);

+    assertEquals(ClassType.Nested, cd.getClassType());

+    assertEquals(0, cd.getFields().size());

+    assertEquals(0, cd.getInterfaces().length);

+    assertEquals(0, cd.getAnnotations().size());

+    assertEquals("com/google/gwt/dev/asm/commons/EmptyVisitor",

+        cd.getSuperName());

+    

+    List<CollectMethodData> methods = cd.getMethods();

+    assertEquals(2, methods.size());

+    // TODO(jat): Is it safe to assume the implicit constructor is always first?

+    CollectMethodData method = methods.get(0);

+    Type[] argTypes = method.getArgTypes();

+    String[] argNames = method.getArgNames();

+    assertEquals("<init>", method.getName());

+    assertEquals(0, argTypes.length);

+    assertEquals(0, argNames.length);

+    assertEquals(0, method.getArgAnnotations().length);

+    assertEquals(0, method.getAnnotations().size());

+    assertEquals(0, method.getExceptions().length);

+

+    method = methods.get(1);

+    argTypes = method.getArgTypes();

+    argNames = method.getArgNames();

+    assertEquals("visitAnnotation", method.getName());

+    assertEquals(2, argTypes.length);

+    assertEquals("java.lang.String", argTypes[0].getClassName());

+    assertEquals("boolean", argTypes[1].getClassName());

+    assertEquals(2, argNames.length);

+    assertEquals("desc", argNames[0]);

+    assertEquals("visible", argNames[1]);

+    assertEquals(2, method.getArgAnnotations().length);

+    assertEquals(0, method.getArgAnnotations()[0].size());

+    assertEquals(0, method.getArgAnnotations()[1].size());

+    // Note that @Override is a source-only annotation

+    assertEquals(0, method.getAnnotations().size());

+    assertEquals(0, method.getExceptions().length);

+  }

+

+  public void testTopLevel() {

+    CollectClassData cd = collect(CollectClassDataTest.class);

+    // Don't check for super bit, as it will depend on the JDK used to compile.

+    assertEquals(Opcodes.ACC_PUBLIC, cd.getAccess() & ~Opcodes.ACC_SUPER);

+    assertEquals(ClassType.TopLevel, cd.getClassType());

+  }

+

+  public void testTwo() {

+    CollectClassData cd = collect(Two.class);

+    // Don't check for super bit, as it will depend on the JDK used to compile.

+    assertEquals(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC,

+        cd.getAccess() & ~Opcodes.ACC_SUPER);

+    assertEquals(ClassType.Nested, cd.getClassType());

+    List<CollectFieldData> fields = cd.getFields();

+    assertEquals(2, fields.size());

+    CollectFieldData field = fields.get(0);

+    assertEquals("field", field.getName());

+    assertEquals("Ljava/lang/String;", field.getDesc());

+    List<CollectAnnotationData> annotations = field.getAnnotations();

+    assertEquals(0, annotations.size());

+    field = fields.get(1);

+    assertEquals("annotatedField", field.getName());

+    assertEquals("Ljava/lang/String;", field.getDesc());

+    annotations = field.getAnnotations();

+    assertEquals(1, annotations.size());

+    AnnotationData annotation = annotations.get(0).getAnnotation();

+    assertEquals("Lcom/google/gwt/core/ext/typeinfo/test/TestAnnotation;",

+        annotation.getDesc());

+    assertEquals("field", annotation.getValues().get("value"));

+    assertEquals(0, cd.getInterfaces().length);

+    annotations = cd.getAnnotations();

+    assertEquals(1, annotations.size());

+    annotation = annotations.get(0).getAnnotation();

+    assertEquals(

+        "Lcom/google/gwt/core/ext/typeinfo/test/PrimitiveValuesAnnotation;",

+        annotation.getDesc());

+    assertEquals(Byte.valueOf((byte) 42), annotation.getValues().get("b"));

+    assertEquals(42, annotation.getValues().get("i"));

+    assertEquals("java/lang/Object", cd.getSuperName());

+    

+    List<CollectMethodData> methods = cd.getMethods();

+     assertEquals(3, methods.size());

+    // TODO(jat): Is it safe to assume the order?

+    CollectMethodData method = methods.get(0);

+    Type[] argTypes = method.getArgTypes();

+    String[] argNames = method.getArgNames();

+    assertEquals("<init>", method.getName());

+    assertEquals(1, argTypes.length);

+    assertEquals(1, argNames.length);

+    assertEquals(1, method.getArgAnnotations().length);

+    assertEquals(0, method.getAnnotations().size());

+    assertEquals(0, method.getExceptions().length);

+

+    method = methods.get(1);

+    argTypes = method.getArgTypes();

+    argNames = method.getArgNames();

+    assertEquals("foo", method.getName());

+    assertEquals(1, argTypes.length);

+    assertEquals("int", argTypes[0].getClassName());

+    assertEquals(1, argNames.length);

+    assertEquals("a", argNames[0]);

+    assertEquals(1, method.getArgAnnotations().length);

+    assertEquals(0, method.getArgAnnotations()[0].size());

+    assertEquals(1, method.getAnnotations().size());

+    assertEquals(1, method.getExceptions().length);

+

+    method = methods.get(2);

+    argTypes = method.getArgTypes();

+    argNames = method.getArgNames();

+    assertEquals("<init>", method.getName());

+    assertEquals(2, argTypes.length);

+    assertEquals("int", argTypes[0].getClassName());

+    assertEquals("java.lang.String", argTypes[1].getClassName());

+    assertEquals(2, argNames.length);

+    assertEquals("a", argNames[0]);

+    assertEquals("b", argNames[1]);

+    assertEquals(2, method.getArgAnnotations().length);

+    assertEquals(0, method.getArgAnnotations()[0].size());

+    assertEquals(0, method.getArgAnnotations()[1].size());

+    assertEquals(0, method.getAnnotations().size());

+    assertEquals(0, method.getExceptions().length);

+  }

+  

+  public void testTwoInner() {

+    CollectClassData cd = collect(Two.TwoInner.class);

+    // Don't check for super bit, as it will depend on the JDK used to compile.

+    assertEquals(Opcodes.ACC_PUBLIC , cd.getAccess() & ~Opcodes.ACC_SUPER);

+    assertEquals(ClassType.Inner, cd.getClassType());

+  }

+

+  private CollectClassData collect(Class<?> clazz) {

+    return collect(clazz.getName());

+  }

+

+  private CollectClassData collect(String className) {

+    byte[] bytes = getClassBytes(className);

+    assertNotNull("Couldn't load bytes for " + className, bytes);

+    CollectClassData cv = new CollectClassData(bytes);

+    ClassReader reader = new ClassReader(bytes);

+    reader.accept(cv, 0);

+    return cv;

+  }

+}

diff --git a/dev/core/test/com/google/gwt/dev/javac/asm/CollectReferencesVisitorTest.java b/dev/core/test/com/google/gwt/dev/javac/asm/CollectReferencesVisitorTest.java
new file mode 100644
index 0000000..0d1df99
--- /dev/null
+++ b/dev/core/test/com/google/gwt/dev/javac/asm/CollectReferencesVisitorTest.java
@@ -0,0 +1,112 @@
+/*

+ * 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.javac.asm;

+

+import com.google.gwt.core.ext.typeinfo.test.TestAnnotation;

+import com.google.gwt.dev.asm.ClassReader;

+import com.google.gwt.dev.util.Name.BinaryName;

+

+import java.util.Map;

+import java.util.Set;

+

+/**

+ * Tests for {@link CollectClassData}.

+ */

+public class CollectReferencesVisitorTest extends AsmTestCase {

+

+  /**

+   * This class is empty, but it still has references to itself, its superclass

+   * (Object), and its enclosing class.

+   */

+  public static class Empty {

+  }

+

+  /**

+   * In addition to the visible types, this class has references to itself, its

+   * superclass (Object), and its enclosing class.

+   */

+  public static class Full {

+

+    protected Integer i;

+    protected String s;

+

+    @TestAnnotation(value = "foo", classLiteral = Double.class)

+    public Map<Boolean, String> getMap() {

+      return null;

+    }

+  }

+

+  public void testEmpty() {

+    CollectReferencesVisitor rv = collect(Empty.class);

+    Set<String> referencedTypes = rv.getReferencedTypes();

+    assertDoesNotContainNull(referencedTypes);

+    assertEquals(3, referencedTypes.size());

+    assertContainsInternalName(Object.class, referencedTypes);

+    assertContainsInternalName(CollectReferencesVisitorTest.class,

+        referencedTypes);

+    assertContainsInternalName(Empty.class, referencedTypes);

+  }

+

+  public void testFull() {

+    CollectReferencesVisitor rv = collect(Full.class);

+    Set<String> referencedTypes = rv.getReferencedTypes();

+    assertDoesNotContainNull(referencedTypes);

+    assertEquals(7, referencedTypes.size());

+    assertContainsInternalName(Object.class, referencedTypes);

+    assertContainsInternalName(CollectReferencesVisitorTest.class,

+        referencedTypes);

+    assertContainsInternalName(Full.class, referencedTypes);

+    assertContainsInternalName(Map.class, referencedTypes);

+    assertContainsInternalName(Integer.class, referencedTypes);

+    assertContainsInternalName(String.class, referencedTypes);

+    assertContainsInternalName(Boolean.class, referencedTypes);

+    // We no longer collect references from annotations to allow for

+    // binary-only annotations and nontranslatable things like File mentioned

+    // in the annotation

+    // assertContainsInternalName(Double.class, referencedTypes);

+    // assertContainsInternalName(TestAnnotation.class, referencedTypes);

+  }

+

+  // ASM passes null for Object's superclass, so we make sure we don't

+  // insert null into the set of referenced types.

+  public void testObject() {

+    CollectReferencesVisitor rv = collect(Object.class);

+    Set<String> referencedTypes = rv.getReferencedTypes();

+    assertDoesNotContainNull(referencedTypes);

+  }

+

+  private void assertContainsInternalName(Class<?> clazz, Set<String> set) {

+    String className = BinaryName.toInternalName(clazz.getName());

+    assertTrue("Should contain " + className, set.contains(className));

+  }

+

+  private void assertDoesNotContainNull(Set<String> referencedTypes) {

+    assertFalse(referencedTypes.contains(null));

+  }

+

+  private CollectReferencesVisitor collect(Class<?> clazz) {

+    return collect(clazz.getName());

+  }

+

+  private CollectReferencesVisitor collect(String className) {

+    byte[] bytes = getClassBytes(className);

+    assertNotNull("Couldn't load bytes for " + className, bytes);

+    CollectReferencesVisitor cv = new CollectReferencesVisitor();

+    ClassReader reader = new ClassReader(bytes);

+    reader.accept(cv, 0);

+    return cv;

+  }

+}

diff --git a/dev/core/test/com/google/gwt/dev/javac/asm/ResolveGenericsTest.java b/dev/core/test/com/google/gwt/dev/javac/asm/ResolveGenericsTest.java
new file mode 100644
index 0000000..1e33e45
--- /dev/null
+++ b/dev/core/test/com/google/gwt/dev/javac/asm/ResolveGenericsTest.java
@@ -0,0 +1,314 @@
+/*

+ * 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.javac.asm;

+

+import com.google.gwt.core.ext.TreeLogger;

+import com.google.gwt.core.ext.typeinfo.HookableTypeOracle;

+import com.google.gwt.core.ext.typeinfo.JClassType;

+import com.google.gwt.core.ext.typeinfo.JGenericType;

+import com.google.gwt.core.ext.typeinfo.JMethod;

+import com.google.gwt.core.ext.typeinfo.JPackage;

+import com.google.gwt.core.ext.typeinfo.JRealClassType;

+import com.google.gwt.core.ext.typeinfo.JTypeParameter;

+import com.google.gwt.core.ext.typeinfo.TypeOracle;

+import com.google.gwt.dev.asm.Opcodes;

+import com.google.gwt.dev.asm.Type;

+import com.google.gwt.dev.asm.signature.SignatureReader;

+import com.google.gwt.dev.javac.Resolver;

+import com.google.gwt.dev.javac.TypeOracleMediator;

+import com.google.gwt.dev.javac.TypeOracleTestingUtils;

+import com.google.gwt.dev.javac.TypeParameterLookup;

+import com.google.gwt.dev.javac.asm.CollectClassData.ClassType;

+

+import java.lang.annotation.Annotation;

+import java.lang.reflect.Method;

+import java.lang.reflect.TypeVariable;

+import java.util.Collections;

+import java.util.HashMap;

+import java.util.List;

+import java.util.Map;

+

+/**

+ * Tests for {@link ResolveClassSignature} and

+ * {@link ResolveMethodSignature}.

+ */

+public class ResolveGenericsTest extends AsmTestCase {

+  

+  public static class FailErrorTreeLogger extends TreeLogger {

+    @Override

+    public TreeLogger branch(com.google.gwt.core.ext.TreeLogger.Type type,

+        String msg, Throwable caught, HelpInfo helpInfo) {

+      if (type == TreeLogger.ERROR) {

+        fail(msg);

+      }

+      return this;

+    }

+

+    @Override

+    public boolean isLoggable(com.google.gwt.core.ext.TreeLogger.Type type) {

+      return true;

+    }

+

+    @Override

+    public void log(com.google.gwt.core.ext.TreeLogger.Type type, String msg,

+        Throwable caught, HelpInfo helpInfo) {

+      if (type == TreeLogger.ERROR) {

+        fail(msg);

+      }

+    }

+  }

+

+  /**

+   * An extension of JMethod which keeps the reflected Method around.

+   */

+  public static class ReflectedMethod extends JMethod {

+    private Method method;

+

+    public ReflectedMethod(JClassType type, String methodName,

+        Map<Class<? extends Annotation>, Annotation> annotations,

+        JTypeParameter[] typeParams, Method method) {

+      super(type, methodName, annotations, typeParams);

+      this.method = method;

+    }

+    

+    public Method getMethod() {

+      return method;

+    }

+  }

+

+  public static class ResolverMockTypeOracle extends HookableTypeOracle {

+    

+    private Map<String, JRealClassType> binaryMapper;

+

+    public Map<String, JRealClassType> getBinaryMapper() {

+      ensureBinaryMapper();

+      return binaryMapper;

+    }

+    

+    @Override

+    protected void addNewType(JRealClassType type) {

+      super.addNewType(type);

+      String name = type.getQualifiedBinaryName().replace('.', '/');

+      ensureBinaryMapper();

+      binaryMapper.put(name, type);

+    }

+

+    private void ensureBinaryMapper() {

+      if (binaryMapper == null) {

+        binaryMapper = new HashMap<String, JRealClassType>();

+      }

+    }

+  }

+

+  private static final TreeLogger failTreeLogger = new FailErrorTreeLogger();

+

+  private static final String OUTER_CLASS_SIG

+      = "<H:Lcom/google/gwt/dev/javac/asm/TestHandler;>Ljava/lang/Object;";

+  private static final String OUTER_METHOD_SIG = "(TH;)V";

+

+  private static final String OUTER1_CLASS_SIG

+      = "<V:Ljava/lang/Object;>Lcom/google/gwt/dev/javac/asm/TestOuter0<"

+        + "Lcom/google/gwt/dev/javac/asm/TestHandler1<TV;>;>;";

+  private static final String OUTER1_METHOD_SIG

+      = "(Lcom/google/gwt/dev/javac/asm/TestHandler1<TV;>;)V";

+

+  private static final String OUTER2_CLASS_SIG

+      = "Lcom/google/gwt/dev/javac/asm/TestOuter1<Ljava/lang/String;>;";

+  private static final String OUTER2_METHOD_SIG

+      = "(Lcom/google/gwt/dev/javac/asm/TestHandler1<Ljava/lang/String;>;)V";

+

+  private ResolverMockTypeOracle oracle = new ResolverMockTypeOracle();

+

+  @SuppressWarnings("unused")

+  private JRealClassType testHandler;

+  @SuppressWarnings("unused")

+  private JRealClassType testHandler1;

+

+  private JRealClassType testOuter0;

+  private JRealClassType testOuter1;

+  private JRealClassType testOuter2;

+

+  private ReflectedMethod testOuter0dispatch;

+  private ReflectedMethod testOuter1dispatch;

+  private ReflectedMethod testOuter2dispatch;

+

+  @SuppressWarnings("unused")

+  private JRealClassType testType;

+

+  private Resolver resolver = new Resolver() {

+    public Map<String, JRealClassType> getBinaryMapper() {

+      return oracle.getBinaryMapper();

+    }

+    

+    public TypeOracle getTypeOracle() {

+      return oracle;

+    }

+    

+    public boolean resolveAnnotations(TreeLogger logger,

+        List<CollectAnnotationData> annotations,

+        Map<Class<? extends Annotation>, Annotation> declaredAnnotations) {

+      return true;

+    }

+

+    public boolean resolveAnnotation(TreeLogger logger,

+        CollectAnnotationData annotVisitor,

+        Map<Class<? extends Annotation>, Annotation> declaredAnnotations) {

+      return true;

+    }

+

+    public boolean resolveClass(TreeLogger logger, JRealClassType type) {

+      return true;

+    }

+  };

+

+

+  public ResolveGenericsTest() {

+    TypeOracleMediator mediator = new TypeOracleMediator(oracle);

+    TypeOracleTestingUtils.buildStandardTypeOracleWith(mediator,

+        failTreeLogger);

+    createUnresolvedClass(Object.class, null);

+    createUnresolvedClass(String.class, null);

+    testHandler = createUnresolvedClass(TestHandler.class, null);

+    testHandler1 = createUnresolvedClass(TestHandler1.class, null);

+    testOuter0 = createUnresolvedClass(TestOuter0.class, null);

+    testType = createUnresolvedClass(TestOuter0.Type.class, testOuter0);

+    testOuter1 = createUnresolvedClass(TestOuter1.class, null);

+    testOuter2 = createUnresolvedClass(TestOuter2.class, null);

+    testOuter0dispatch = createUnresolvedMethod(testOuter0, TestOuter0.class,

+        "dispatch", TestHandler.class);

+    testOuter1dispatch = createUnresolvedMethod(testOuter1, TestOuter1.class,

+        "dispatch", TestHandler.class);

+    testOuter2dispatch = createUnresolvedMethod(testOuter2, TestOuter2.class,

+        "dispatch", TestHandler.class);

+  }

+

+  public void testOuter0Class() {

+    resolveClassSignature(testOuter0, OUTER_CLASS_SIG);

+    assertNotNull(testOuter0.getSuperclass());

+    // TODO(jat): additional checks?

+  }

+

+  public void testOuter0Method() {

+    resolveMethodSignature(testOuter0dispatch, OUTER_METHOD_SIG);

+    // TODO(jat): meaningful tests besides no errors?

+  }

+  

+  public void testOuter1Class() {

+    resolveClassSignature(testOuter1, OUTER1_CLASS_SIG);

+    JClassType superClass = testOuter1.getSuperclass();

+    assertNotNull(superClass);

+    assertNotNull(superClass.isParameterized());

+    // TODO(jat): additional checks?

+  }

+  

+  public void testOuter1Method() {

+    resolveMethodSignature(testOuter1dispatch, OUTER1_METHOD_SIG);

+    // TODO(jat): meaningful tests besides no errors?

+  }

+

+  public void testOuter2Class() {

+    resolveClassSignature(testOuter2, OUTER2_CLASS_SIG);

+    JClassType superClass = testOuter2.getSuperclass();

+    assertNotNull(superClass);

+    assertNotNull(superClass.isParameterized());

+    // TODO(jat): additional checks?

+  }

+

+  public void testOuter2Method() {

+    resolveMethodSignature(testOuter2dispatch, OUTER2_METHOD_SIG);

+    // TODO(jat): meaningful tests besides no errors?

+  }

+  

+  private JTypeParameter[] createTypeParams(TypeVariable<?>[] typeParams) {

+    int n = typeParams.length;

+    JTypeParameter[] params = new JTypeParameter[n];

+    for (int i = 0; i < n; ++i) {

+      params[i] = new JTypeParameter(typeParams[i].getName(), i);

+    }

+    return params;

+  }

+

+  private JRealClassType createUnresolvedClass(Class<?> clazz,

+      JRealClassType enclosingType) {

+    String pkgName = clazz.getPackage().getName();

+    JPackage pkg = oracle.getOrCreatePackage(pkgName);

+    TypeVariable<?>[] typeParams = clazz.getTypeParameters();

+    JRealClassType type;

+    int n = typeParams.length;

+    String enclosingTypeName = null;

+    if (enclosingType != null) {

+      enclosingTypeName = enclosingType.getName();

+    }

+    if (n == 0) {

+      type = new JRealClassType(oracle, pkg, enclosingTypeName,

+        false, clazz.getSimpleName(), clazz.isInterface());

+    } else {

+      JTypeParameter[] params = createTypeParams(typeParams);

+      type = new JGenericType(oracle, pkg, enclosingTypeName, false,

+          clazz.getSimpleName(), clazz.isInterface(), params);

+    }

+    return type;

+  }

+

+  private ReflectedMethod createUnresolvedMethod(JClassType type, Class<?> clazz,

+      String methodName, Class<?>... paramTypes) {

+    Method method = null;

+    try {

+      method = clazz.getMethod(methodName, paramTypes);

+    } catch (SecurityException e) {

+      fail("Exception " + e + " creating method " + methodName + " on " + clazz);

+    } catch (NoSuchMethodException e) {

+      fail("Exception " + e + " creating method " + methodName + " on " + clazz);

+    }

+    JTypeParameter[] typeParams = createTypeParams(method.getTypeParameters());

+    Map<Class<? extends Annotation>, Annotation> emptyMap

+        = Collections.emptyMap();

+    return new ReflectedMethod(type, methodName, emptyMap, typeParams, method);

+  }

+

+  private void resolveClassSignature(JRealClassType type, String signature) {

+    Map<String, JRealClassType> binaryMapper = oracle.getBinaryMapper();

+    TypeParameterLookup lookup = new TypeParameterLookup();

+    lookup.pushEnclosingScopes(type);

+    ResolveClassSignature classResolver = new ResolveClassSignature(resolver,

+        binaryMapper, failTreeLogger, type, lookup);

+    new SignatureReader(signature).accept(classResolver);

+    classResolver.finish();

+  }

+

+  private void resolveMethodSignature(ReflectedMethod method,

+      String signature) {

+    TypeParameterLookup lookup = new TypeParameterLookup();

+    lookup.pushEnclosingScopes(method.getEnclosingType());

+    lookup.pushScope(method.getTypeParameters());

+    int access = Opcodes.ACC_PUBLIC;

+    String desc = Type.getMethodDescriptor(method.getMethod());

+    CollectMethodData methodData = new CollectMethodData(ClassType.TopLevel,

+        access, method.getName(), desc, signature, null);

+    Class<?>[] paramTypes = method.getMethod().getParameterTypes();

+    int n = paramTypes.length;

+    Type[] argTypes = new Type[n];

+    String[] argNames = new String[n];

+    for (int i = 0; i < n; ++i) {

+      argNames[i] = "arg" + i;

+      argTypes[i] = Type.getType(paramTypes[i]);

+    }

+    ResolveMethodSignature methodResolver = new ResolveMethodSignature(resolver,

+        failTreeLogger, method, lookup, true, methodData, argTypes, argNames);

+    new SignatureReader(signature).accept(methodResolver);

+    methodResolver.finish();

+  }

+}

diff --git a/dev/core/test/com/google/gwt/dev/javac/asm/TestHandler.java b/dev/core/test/com/google/gwt/dev/javac/asm/TestHandler.java
new file mode 100644
index 0000000..50e74d3
--- /dev/null
+++ b/dev/core/test/com/google/gwt/dev/javac/asm/TestHandler.java
@@ -0,0 +1,19 @@
+/*

+ * 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.javac.asm;

+

+public interface TestHandler {

+}

diff --git a/dev/core/test/com/google/gwt/dev/javac/asm/TestHandler1.java b/dev/core/test/com/google/gwt/dev/javac/asm/TestHandler1.java
new file mode 100644
index 0000000..a927ba7
--- /dev/null
+++ b/dev/core/test/com/google/gwt/dev/javac/asm/TestHandler1.java
@@ -0,0 +1,24 @@
+/*

+ * 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.javac.asm;

+

+/**

+ * Test class for generic signature validation.

+ *

+ * @param <V>

+ */

+public class TestHandler1<V> implements TestHandler {

+}

diff --git a/dev/core/test/com/google/gwt/dev/javac/asm/TestOuter0.java b/dev/core/test/com/google/gwt/dev/javac/asm/TestOuter0.java
new file mode 100644
index 0000000..ceeb5cf
--- /dev/null
+++ b/dev/core/test/com/google/gwt/dev/javac/asm/TestOuter0.java
@@ -0,0 +1,25 @@
+/*

+ * 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.javac.asm;

+

+public abstract class TestOuter0<H extends TestHandler> {

+  public static class Type<H> {

+    public Type() {

+    }

+  }

+  

+  public abstract void dispatch(H handler);

+}

diff --git a/dev/core/test/com/google/gwt/dev/javac/asm/TestOuter1.java b/dev/core/test/com/google/gwt/dev/javac/asm/TestOuter1.java
new file mode 100644
index 0000000..51c3119
--- /dev/null
+++ b/dev/core/test/com/google/gwt/dev/javac/asm/TestOuter1.java
@@ -0,0 +1,23 @@
+/*

+ * 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.javac.asm;

+

+public class TestOuter1<V> extends TestOuter0<TestHandler1<V>> {

+

+  @Override

+  public void dispatch(TestHandler1<V> handler) {

+  }

+}

diff --git a/dev/core/test/com/google/gwt/dev/javac/asm/TestOuter2.java b/dev/core/test/com/google/gwt/dev/javac/asm/TestOuter2.java
new file mode 100644
index 0000000..37e4fdc
--- /dev/null
+++ b/dev/core/test/com/google/gwt/dev/javac/asm/TestOuter2.java
@@ -0,0 +1,23 @@
+/*

+ * 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.javac.asm;

+

+public class TestOuter2 extends TestOuter1<String> {

+

+  @Override

+  public void dispatch(TestHandler1<String> handler) {

+  }

+}

diff --git a/user/test/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilderTest.java b/user/test/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilderTest.java
index 43fd82a..30efbfb 100644
--- a/user/test/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilderTest.java
+++ b/user/test/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilderTest.java
@@ -641,16 +641,6 @@
     {
       StringBuilder code = new StringBuilder();
       code.append("import java.io.Serializable;\n");
-      code.append("public class AutoSerializable {\n");
-      code.append("  interface IFoo extends Serializable {};\n");
-      code.append("  IFoo createFoo() { return new IFoo(){};}\n");
-      code.append("}\n");
-      resources.add(new StaticJavaResource("AutoSerializable", code));
-    }
-
-    {
-      StringBuilder code = new StringBuilder();
-      code.append("import java.io.Serializable;\n");
       code.append("public class OuterClass {\n");
       code.append("  static class StaticNested implements Serializable {};\n");
       code.append("  class NonStaticNested implements Serializable {};\n");
@@ -736,12 +726,6 @@
     assertFalse(sob.shouldConsiderFieldsForSerialization(notSerializable,
         problems));
 
-    // Local types should not qualify for serialization
-    JClassType iFoo = to.getType("AutoSerializable.IFoo");
-    problems = new ProblemReport();
-    assertFalse(sob.shouldConsiderFieldsForSerialization(iFoo.getSubtypes()[0],
-        problems));
-
     // Static nested types qualify for serialization
     JClassType staticNested = to.getType("OuterClass.StaticNested");
     problems = new ProblemReport();
@@ -791,9 +775,8 @@
     assertTrue(sob.shouldConsiderFieldsForSerialization(enumWithSubclasses,
         problems));
 
-    problems = new ProblemReport();
-    assertFalse(sob.shouldConsiderFieldsForSerialization(
-        enumWithSubclasses.getSubtypes()[0], problems));
+    // There are no longer any enum subclasses in TypeOracle
+    assertEquals(0, enumWithSubclasses.getSubtypes().length);
 
     // Enum that are not default instantiable should qualify
     JClassType enumWithNonDefaultCtors = to.getType("EnumWithNonDefaultCtors");