Fixes a ClassCircularityError that could be triggered computing the super interface hierarchy via class objects.

The new implementation uses TypeOracle.

Patch by: bobv
Review by: me

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@5472 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java b/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java
index 2e99313..2db2593 100644
--- a/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java
+++ b/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java
@@ -993,8 +993,8 @@
       if (unit != null) {
         anonymousClassMap = unit.getAnonymousClassMap();
       }
-      byte[] newBytes = classRewriter.rewrite(this, className, classBytes,
-          anonymousClassMap);
+      byte[] newBytes = classRewriter.rewrite(typeOracle, className,
+          classBytes, anonymousClassMap);
       if (CLASS_DUMP) {
         if (!Arrays.equals(classBytes, newBytes)) {
           classDump(className, newBytes);
diff --git a/dev/core/src/com/google/gwt/dev/shell/rewrite/HostedModeClassRewriter.java b/dev/core/src/com/google/gwt/dev/shell/rewrite/HostedModeClassRewriter.java
index 6f1621a..03c32d5 100644
--- a/dev/core/src/com/google/gwt/dev/shell/rewrite/HostedModeClassRewriter.java
+++ b/dev/core/src/com/google/gwt/dev/shell/rewrite/HostedModeClassRewriter.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.dev.shell.rewrite;
 
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
 import com.google.gwt.dev.asm.ClassReader;
 import com.google.gwt.dev.asm.ClassVisitor;
 import com.google.gwt.dev.asm.ClassWriter;
@@ -183,14 +184,14 @@
   /**
    * Performs rewriting transformations on a class.
    * 
-   * @param ccl the ClassLoader requesting the rewrite
+   * @param typeOracle a typeOracle modeling the user classes
    * @param className the name of the class
    * @param classBytes the bytes of the class
    * @param anonymousClassMap a map between the anonymous class names of java
    *          compiler used to compile code and jdt. Emma-specific.
    */
-  public byte[] rewrite(ClassLoader ccl, String className, byte[] classBytes,
-      Map<String, String> anonymousClassMap) {
+  public byte[] rewrite(TypeOracle typeOracle, String className,
+      byte[] classBytes, Map<String, String> anonymousClassMap) {
     String desc = toDescriptor(className);
     assert (!jsoIntfDescs.contains(desc));
 
@@ -201,7 +202,7 @@
     // v = new CheckClassAdapter(v);
     // v = new TraceClassVisitor(v, new PrintWriter(System.out));
 
-    v = new RewriteSingleJsoImplDispatches(v, ccl, singleJsoImplTypes,
+    v = new RewriteSingleJsoImplDispatches(v, typeOracle, singleJsoImplTypes,
         mangledNamesToImplementations);
 
     v = new RewriteRefsToJsoClasses(v, jsoIntfDescs, mapper);
diff --git a/dev/core/src/com/google/gwt/dev/shell/rewrite/RewriteSingleJsoImplDispatches.java b/dev/core/src/com/google/gwt/dev/shell/rewrite/RewriteSingleJsoImplDispatches.java
index 94bb354..00bdca1 100644
--- a/dev/core/src/com/google/gwt/dev/shell/rewrite/RewriteSingleJsoImplDispatches.java
+++ b/dev/core/src/com/google/gwt/dev/shell/rewrite/RewriteSingleJsoImplDispatches.java
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.dev.shell.rewrite;
 
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
 import com.google.gwt.dev.asm.ClassAdapter;
 import com.google.gwt.dev.asm.ClassVisitor;
 import com.google.gwt.dev.asm.MethodAdapter;
@@ -22,6 +24,8 @@
 import com.google.gwt.dev.asm.Opcodes;
 import com.google.gwt.dev.asm.Type;
 import com.google.gwt.dev.asm.commons.Method;
+import com.google.gwt.dev.util.collect.Maps;
+import com.google.gwt.dev.util.collect.Sets;
 
 import java.util.Collections;
 import java.util.HashSet;
@@ -121,17 +125,17 @@
 
   private String currentTypeName;
   private final Set<String> implementedMethods = new HashSet<String>();
+  private boolean inSingleJsoImplInterfaceType;
+  private Map<String, Set<String>> intfNamesToAllInterfaces = Maps.create();
   private final SortedMap<String, Method> mangledNamesToImplementations;
   private final Set<String> singleJsoImplTypes;
-  private boolean inSingleJsoImplInterfaceType;
+  private final TypeOracle typeOracle;
 
-  private final ClassLoader ccl;
-
-  public RewriteSingleJsoImplDispatches(ClassVisitor v, ClassLoader ccl,
+  public RewriteSingleJsoImplDispatches(ClassVisitor v, TypeOracle typeOracle,
       Set<String> singleJsoImplTypes,
       SortedMap<String, Method> mangledNamesToImplementations) {
     super(v);
-    this.ccl = ccl;
+    this.typeOracle = typeOracle;
     this.singleJsoImplTypes = Collections.unmodifiableSet(singleJsoImplTypes);
     this.mangledNamesToImplementations = Collections.unmodifiableSortedMap(mangledNamesToImplementations);
   }
@@ -206,29 +210,49 @@
     return new MyMethodVisitor(mv);
   }
 
-  private Set<String> computeAllInterfaces(String... interfaces) {
-    Set<String> toReturn = new HashSet<String>();
-    List<String> q = new LinkedList<String>();
-    Collections.addAll(q, interfaces);
+  private Set<String> computeAllInterfaces(String intfName) {
+    Set<String> toReturn = intfNamesToAllInterfaces.get(intfName);
+    if (toReturn != null) {
+      return toReturn;
+    }
+
+    toReturn = Sets.create();
+    List<JClassType> q = new LinkedList<JClassType>();
+    JClassType intf = typeOracle.findType(intfName.replace('/', '.').replace(
+        '$', '.'));
+    assert intf != null : "Could not find interface " + intfName;
+    q.add(intf);
 
     while (!q.isEmpty()) {
-      String intf = q.remove(0);
-      if (toReturn.add(intf)) {
-        try {
-          Class<?> intfClass = Class.forName(intf.replace('/', '.'), false, ccl);
-          for (Class<?> i : intfClass.getInterfaces()) {
-            q.add(i.getName().replace('.', '/'));
-          }
-        } catch (ClassNotFoundException e) {
-          assert false : intf;
-          e.printStackTrace();
-        }
+      intf = q.remove(0);
+      String resourceName = getResourceName(intf);
+      if (!toReturn.contains(resourceName)) {
+        toReturn = Sets.add(toReturn, resourceName);
+        Collections.addAll(q, intf.getImplementedInterfaces());
       }
     }
 
+    intfNamesToAllInterfaces = Maps.put(intfNamesToAllInterfaces, intfName,
+        toReturn);
     return toReturn;
   }
 
+  private Set<String> computeAllInterfaces(String[] interfaces) {
+    Set<String> toReturn = new HashSet<String>();
+    for (String intfName : interfaces) {
+      toReturn.addAll(computeAllInterfaces(intfName));
+    }
+    return toReturn;
+  }
+
+  private String getResourceName(JClassType type) {
+    if (type.getEnclosingType() != null) {
+      return getResourceName(type.getEnclosingType()) + "$"
+          + type.getSimpleSourceName();
+    }
+    return type.getQualifiedSourceName().replace('.', '/');
+  }
+
   /**
    * Given a resource name of a class, find all mangled method names that must
    * be implemented.