Fix incorrect computation in JTypeOracle.computeSingleJsoImplData() which to include super-interfaces when determining if an interface is a tag interface.

Patch by: bobv
Review by: scottb

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@5756 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java
index 48272af..c431a96 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java
@@ -488,6 +488,36 @@
   }
 
   /**
+   * Returns true if the given type and it's super-interfaces define no methods.
+   */
+  public boolean isTagInterface(JInterfaceType type) {
+    Set<JInterfaceType> seen = new IdentityHashSet<JInterfaceType>();
+    List<JInterfaceType> q = new LinkedList<JInterfaceType>();
+    seen.add(type);
+    q.add(type);
+
+    while (!q.isEmpty()) {
+      JInterfaceType intf = q.remove(0);
+
+      List<JMethod> methods = intf.getMethods();
+      int size = methods.size();
+      if (size == 0
+          || (size == 1 && methods.get(0).getName().equals("$clinit"))) {
+        // OK, add any super-interfaces;
+        for (JInterfaceType superIntf : intf.getImplements()) {
+          if (seen.add(superIntf)) {
+            q.add(superIntf);
+          }
+        }
+      } else {
+        return false;
+      }
+    }
+
+    return true;
+  }
+
+  /**
    * This method should be called after altering the types that are live in the
    * associated JProgram.
    */
@@ -683,14 +713,12 @@
         }
         JInterfaceType intr = (JInterfaceType) refType;
 
-        if (intr.getMethods().size() <= 1) {
+        if (isTagInterface(intr)) {
           /*
            * Record a tag interface as being implemented by JSO, since they
            * don't actually have any methods and we want to avoid spurious
            * messages about multiple JSO types implementing a common interface.
            */
-          assert intr.getMethods().size() == 0
-              || intr.getMethods().get(0).getName().equals("$clinit");
           jsoSingleImpls.put(intr, program.getJavaScriptObject());
 
           /*
diff --git a/user/test/com/google/gwt/dev/jjs/test/singlejso/TypeHierarchyTest.java b/user/test/com/google/gwt/dev/jjs/test/singlejso/TypeHierarchyTest.java
index f50be8b..b954f3e 100644
--- a/user/test/com/google/gwt/dev/jjs/test/singlejso/TypeHierarchyTest.java
+++ b/user/test/com/google/gwt/dev/jjs/test/singlejso/TypeHierarchyTest.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.dev.jjs.test.singlejso;
 
+import com.google.gwt.core.client.JavaScriptObject;
 import com.google.gwt.junit.client.GWTTestCase;
 
 /**
@@ -22,6 +23,42 @@
  */
 public class TypeHierarchyTest extends GWTTestCase {
 
+  /**
+   * The bottom type for a non-trivial diamond-shaped inheritance pattern.
+   */
+  static class DiamondImpl extends JavaScriptObject implements IDiamond2A,
+      IDiamond2B {
+    public static native DiamondImpl create() /*-{
+      return {size : 42};
+    }-*/;
+
+    protected DiamondImpl() {
+    }
+
+    public final native int size() /*-{
+      return this.size;
+    }-*/;
+  }
+
+  /**
+   * The root type for a non-trivial diamond-shaped inheritance pattern.
+   */
+  interface IDiamond1 {
+    int size();
+  }
+
+  /**
+   * The left type for a non-trivial diamond-shaped inheritance pattern.
+   */
+  interface IDiamond2A extends IDiamond1 {
+  }
+
+  /**
+   * The right type for a non-trivial diamond-shaped inheritance pattern.
+   */
+  interface IDiamond2B extends IDiamond1 {
+  }
+
   @Override
   public String getModuleName() {
     return "com.google.gwt.dev.jjs.CompilerSuite";
@@ -60,4 +97,14 @@
     assertEquals("B2", b2.whoAmI());
   }
 
+  public void testDiamond() {
+    IDiamond1 d1 = DiamondImpl.create();
+    assertEquals(42, d1.size());
+
+    IDiamond2A d2a = DiamondImpl.create();
+    assertEquals(42, d2a.size());
+
+    IDiamond2B d2b = DiamondImpl.create();
+    assertEquals(42, d2b.size());
+  }
 }