Additional SingleJso fixes for tag interfaces.
Also changes how CastNormalizer selects Cast / instanceof methods for SingleJso types.

Patch by: bobv
Review by: spoon (TBR)
Verified by: macpherson


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@5467 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 337b94f..48272af 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
@@ -692,7 +692,15 @@
           assert intr.getMethods().size() == 0
               || intr.getMethods().get(0).getName().equals("$clinit");
           jsoSingleImpls.put(intr, program.getJavaScriptObject());
+
+          /*
+           * Pretend JSO had always implemented the tag interface. This helps
+           * simplify cast operations.
+           */
           jsoType.addImplements(intr);
+          add(couldBeImplementedMap, intr, jsoType);
+          add(isImplementedMap, intr, jsoType);
+          add(implementsMap, jsoType, intr);
           continue;
         }
 
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/CastNormalizer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/CastNormalizer.java
index 1b7e41f..1319526 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/CastNormalizer.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/CastNormalizer.java
@@ -27,7 +27,6 @@
 import com.google.gwt.dev.jjs.ast.JExpression;
 import com.google.gwt.dev.jjs.ast.JInstanceOf;
 import com.google.gwt.dev.jjs.ast.JIntLiteral;
-import com.google.gwt.dev.jjs.ast.JInterfaceType;
 import com.google.gwt.dev.jjs.ast.JMethod;
 import com.google.gwt.dev.jjs.ast.JMethodCall;
 import com.google.gwt.dev.jjs.ast.JModVisitor;
@@ -412,12 +411,6 @@
    */
   private class ReplaceTypeChecksVisitor extends JModVisitor {
 
-    private final Set<JInterfaceType> dualImpls;
-
-    public ReplaceTypeChecksVisitor(JProgram program) {
-      dualImpls = program.typeOracle.getInterfacesWithJavaAndJsoImpls();
-    }
-
     @Override
     public void endVisit(JCastOperation x, Context ctx) {
       JExpression replaceExpr;
@@ -455,10 +448,10 @@
           JMethod method;
           boolean isJsoCast = program.isJavaScriptObject(toType);
           if (isJsoCast) {
-            // A cast to JSO
+            // A cast to a concrete JSO subtype
             method = program.getIndexedMethod("Cast.dynamicCastJso");
-          } else if (dualImpls.contains(toType)) {
-            // A cast that may succeed when the object is a JSO
+          } else if (program.typeOracle.getSingleJsoImpls().containsKey(toType)) {
+            // An interface that should succeed when the object is a JSO
             method = program.getIndexedMethod("Cast.dynamicCastAllowJso");
           } else {
             // A regular cast
@@ -569,11 +562,12 @@
         ctx.replaceMe(eq);
       } else {
         JMethod method;
-        boolean isJsoCast = program.isJavaScriptObject(toType);
-        if (isJsoCast) {
-          method = program.getIndexedMethod("Cast.instanceOfJso");
-        } else if (dualImpls.contains(toType)) {
+        boolean isJsoCast = false;
+        if (program.typeOracle.getSingleJsoImpls().containsKey(toType)) {
           method = program.getIndexedMethod("Cast.instanceOfOrJso");
+        } else if (program.isJavaScriptObject(toType)) {
+          isJsoCast = true;
+          method = program.getIndexedMethod("Cast.instanceOfJso");
         } else {
           method = program.getIndexedMethod("Cast.instanceOf");
         }
@@ -618,7 +612,7 @@
       assigner.computeTypeIds();
     }
     {
-      ReplaceTypeChecksVisitor replacer = new ReplaceTypeChecksVisitor(program);
+      ReplaceTypeChecksVisitor replacer = new ReplaceTypeChecksVisitor();
       replacer.accept(program);
     }
   }
diff --git a/user/test/com/google/gwt/dev/jjs/test/SingleJsoImplTest.java b/user/test/com/google/gwt/dev/jjs/test/SingleJsoImplTest.java
index 4847899..5b21b02 100644
--- a/user/test/com/google/gwt/dev/jjs/test/SingleJsoImplTest.java
+++ b/user/test/com/google/gwt/dev/jjs/test/SingleJsoImplTest.java
@@ -70,6 +70,13 @@
     String foo();
   }
 
+  interface CreatedWithCastToTag {
+  }
+
+  interface CreatedWithCastToTagSub extends CreatedWithCastToTag {
+    String foo();
+  }
+
   interface Divider extends Multiplier {
     int divide(int a, int b);
   }
@@ -94,6 +101,12 @@
     }
   }
 
+  static class JavaCreatedWithCastToTag implements CreatedWithCastToTagSub {
+    public String foo() {
+      return "foo";
+    }
+  }
+
   /**
    * The extra declaration of implementing Multiplier should still be legal.
    */
@@ -213,6 +226,16 @@
     }
   }
 
+  static class JsoCreatedWithCastToTag extends JavaScriptObject implements
+      CreatedWithCastToTagSub {
+    protected JsoCreatedWithCastToTag() {
+    }
+
+    public final String foo() {
+      return "foo";
+    }
+  }
+
   static class JsoDivider extends JsoMultiplier implements Divider, Tag {
     protected JsoDivider() {
     }
@@ -483,7 +506,16 @@
    * compiler would assume there are types that implement the interface.
    */
   public void testCreatedWithCast() {
-    Object o = (CreatedWithCast) JavaScriptObject.createObject();
+    try {
+      Object a = (CreatedWithCast) JavaScriptObject.createObject();
+    } catch (ClassCastException e) {
+      fail("a");
+    }
+    try {
+      Object b = (CreatedWithCastToTag) JavaScriptObject.createObject();
+    } catch (ClassCastException e) {
+      fail("b");
+    }
   }
 
   public void testDualCase() {