Fix compiler and hosted-mode crash caused by virtual overrides in SingleJsoImpl types.

Patch by: bobv
Review by: scottb

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@6216 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 81f5e9f..1310630 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
@@ -448,7 +448,6 @@
             }
           }
         }
-        jsoSubType.clearImplements();
       }
     }
 
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/JavaScriptObjectNormalizer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/JavaScriptObjectNormalizer.java
index 3535af3..1f683ab 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/JavaScriptObjectNormalizer.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/JavaScriptObjectNormalizer.java
@@ -177,13 +177,21 @@
 
     private JMethod findConcreteImplementation(JMethod method,
         JClassType concreteType) {
-      for (JMethod m : concreteType.getMethods()) {
-        if (program.typeOracle.getAllOverrides(m).contains(method)) {
-          if (!m.isAbstract()) {
-            return m;
+      /*
+       * Search supertypes for virtual overrides via subclass. See the javadoc
+       * on JTypeOracle.getAllVirtualOverrides for an example.
+       */
+      while (concreteType != null) {
+        for (JMethod m : concreteType.getMethods()) {
+          if (program.typeOracle.getAllOverrides(m).contains(method)) {
+            if (!m.isAbstract()) {
+              return m;
+            }
           }
         }
+        concreteType = concreteType.getSuperClass();
       }
+
       return null;
     }
 
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 0815fad..14f1a4c 100644
--- a/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java
+++ b/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java
@@ -36,9 +36,9 @@
 import com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter;
 import com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter.InstanceMethodOracle;
 import com.google.gwt.dev.util.JsniRef;
-import com.google.gwt.dev.util.Name.SourceOrBinaryName;
-import com.google.gwt.dev.util.Name.InternalName;
 import com.google.gwt.dev.util.Util;
+import com.google.gwt.dev.util.Name.InternalName;
+import com.google.gwt.dev.util.Name.SourceOrBinaryName;
 import com.google.gwt.util.tools.Utility;
 
 import org.apache.commons.collections.map.AbstractReferenceMap;
@@ -911,6 +911,21 @@
         String mangledName = getBinaryName(type).replace('.', '_') + "_"
             + m.getName();
 
+        JType[] parameterTypes = new JType[m.getParameters().length];
+        for (int i = 0; i < parameterTypes.length; i++) {
+          parameterTypes[i] = m.getParameters()[i].getType();
+        }
+
+        /*
+         * Handle virtual overrides by finding the method that we would normally
+         * invoke and using its declaring class as the dispatch target.
+         */
+        while (implementingType.findMethod(m.getName(), parameterTypes) == null) {
+          implementingType = implementingType.getSuperclass();
+        }
+        assert implementingType != null : "Unable to find virtual override for "
+            + m.toString();
+
         /*
          * Cook up the a pseudo-method declaration for the concrete type. This
          * should look something like
@@ -921,9 +936,9 @@
          */
         String decl = getBinaryOrPrimitiveName(m.getReturnType()) + " "
             + m.getName() + "$ (" + getBinaryOrPrimitiveName(implementingType);
-        for (JParameter p : m.getParameters()) {
+        for (JType paramType : parameterTypes) {
           decl += ",";
-          decl += getBinaryOrPrimitiveName(p.getType());
+          decl += getBinaryOrPrimitiveName(paramType);
         }
         decl += ")";
 
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 b954f3e..80966f5 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
@@ -24,6 +24,16 @@
 public class TypeHierarchyTest extends GWTTestCase {
 
   /**
+   * Used with PlainJso and PlainJsoWithInterface to mix interfaces into
+   * existing base classes.
+   */
+  interface Arrayish {
+    int getLength();
+
+    JavaScriptObject getObject(int i);
+  }
+
+  /**
    * The bottom type for a non-trivial diamond-shaped inheritance pattern.
    */
   static class DiamondImpl extends JavaScriptObject implements IDiamond2A,
@@ -59,6 +69,35 @@
   interface IDiamond2B extends IDiamond1 {
   }
 
+  /**
+   * This is a base class that is used to test adding interfaces to a JSO via a
+   * subclass.
+   */
+  static class PlainJso extends JavaScriptObject {
+    protected PlainJso() {
+    }
+
+    public final native int getLength()/*-{
+      return this.length;
+    }-*/;
+
+    public final native JavaScriptObject getObject(int i) /*-{
+      return this[i];
+    }-*/;
+  }
+
+  /**
+   * We'll mix in an interface into PlainJso.
+   */
+  static class PlainJsoWithInterface extends PlainJso implements Arrayish {
+    public static PlainJsoWithInterface create() {
+      return JavaScriptObject.createArray().cast();
+    }
+
+    protected PlainJsoWithInterface() {
+    }
+  }
+
   @Override
   public String getModuleName() {
     return "com.google.gwt.dev.jjs.CompilerSuite";
@@ -107,4 +146,10 @@
     IDiamond2B d2b = DiamondImpl.create();
     assertEquals(42, d2b.size());
   }
+
+  public void testVirtualOverrides() {
+    Arrayish array = PlainJsoWithInterface.create();
+    assertEquals(0, array.getLength());
+    assertNull(array.getObject(0));
+  }
 }