Remove the need for an explicit SingleJsoImpl annotation.  The single-JSO-ness of interfaces will be automatically calculated.
Hosted-mode bug fixes for SingleJsoImpl.

Patch by: bobv
Review by: spoon


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@4851 8db76d5a-ed1c-0410-87a9-c151d255dfc7
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 201394f..26c4ab0 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
@@ -15,8 +15,6 @@
  */
 package com.google.gwt.core.ext.typeinfo;
 
-import com.google.gwt.core.client.SingleJsoImpl;
-
 import java.lang.annotation.Annotation;
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -95,12 +93,6 @@
   public void addAnnotations(
       Map<Class<? extends Annotation>, Annotation> declaredAnnotations) {
     annotations.addAnnotations(declaredAnnotations);
-    for (Class<? extends Annotation> clazz : declaredAnnotations.keySet()) {
-      if (SingleJsoImpl.class.equals(clazz)) {
-        oracle.addSingleJsoInterface(this);
-        break;
-      }
-    }
   }
 
   public void addImplementedInterface(JClassType intf) {
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 cda9804..0de6e96 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
@@ -18,6 +18,7 @@
 import com.google.gwt.core.ext.TreeLogger;
 import com.google.gwt.core.ext.UnableToCompleteException;
 import com.google.gwt.dev.generator.GenUtil;
+import com.google.gwt.dev.shell.JsValueGlue;
 
 import java.lang.annotation.Annotation;
 import java.util.ArrayList;
@@ -199,7 +200,10 @@
 
   private final Map<String, List<JWildcardType>> wildcardTypes = new HashMap<String, List<JWildcardType>>();
 
-  private final Set<JRealClassType> singleJsoImplTypes = new HashSet<JRealClassType>();
+  /**
+   * Maps SingleJsoImpl interfaces to the implementing JSO subtype.
+   */
+  private final Map<JClassType, JClassType> jsoSingleImpls = new IdentityHashMap<JClassType, JClassType>();
 
   public TypeOracle() {
     // Always create the default package.
@@ -256,6 +260,7 @@
   public void finish(TreeLogger logger) {
     JClassType[] newTypes = recentTypes.toArray(NO_JCLASSES);
     computeHierarchyRelationships(newTypes);
+    computeSingleJsoImplData(logger, newTypes);
     consumeTypeArgMetaData(logger, newTypes);
     recentTypes.clear();
   }
@@ -437,11 +442,21 @@
   }
 
   /**
-   * Returns an unmodifiable, live view of all interface types annotated with
-   * the SingleJsoImpl annotation.
+   * Returns the single implementation type for an interface returned via
+   * {@link #getSingleJsoImplInterfaces()} or <code>null</code> if no JSO
+   * implementation is defined.
    */
-  public Set<JRealClassType> getSingleJsoImplTypes() {
-    return Collections.unmodifiableSet(singleJsoImplTypes);
+  public JClassType getSingleJsoImpl(JClassType intf) {
+    assert intf.isInterface() == intf;
+    return jsoSingleImpls.get(intf);
+  }
+
+  /**
+   * Returns an unmodifiable, live view of all interface types that are
+   * implemented by exactly one JSO subtype.
+   */
+  public Set<JClassType> getSingleJsoImplInterfaces() {
+    return Collections.unmodifiableSet(jsoSingleImpls.keySet());
   }
 
   /**
@@ -616,12 +631,6 @@
     recentTypes.add(newType);
   }
 
-  void addSingleJsoInterface(JRealClassType singleJsoImplType) {
-    assert singleJsoImplType.isInterface() == singleJsoImplType : singleJsoImplType.getName()
-        + " has SingleJsoImpl, but is not an interface";
-    singleJsoImplTypes.add(singleJsoImplType);
-  }
-
   void invalidate(JRealClassType realClassType) {
     invalidatedTypes.add(realClassType);
   }
@@ -635,6 +644,61 @@
     }
   }
 
+  /**
+   * Updates the list of jsoSingleImpl types from recently-added types.
+   */
+  private void computeSingleJsoImplData(TreeLogger logger, JClassType[] newTypes) {
+    JClassType jsoType = findType(JsValueGlue.JSO_CLASS);
+    if (jsoType == null) {
+      return;
+    }
+
+    for (JClassType type : newTypes) {
+      if (!jsoType.isAssignableFrom(type)) {
+        continue;
+      }
+
+      for (JClassType intf : JClassType.getFlattenedSuperTypeHierarchy(type)) {
+        if (intf.isInterface() == null) {
+          // Not an interface
+          continue;
+        }
+
+        if (intf.getOverridableMethods().length == 0) {
+          /*
+           * 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.
+           */
+          jsoSingleImpls.put(intf, jsoType);
+          continue;
+        }
+
+        /*
+         * If the previously-registered implementation type for a SingleJsoImpl
+         * interface is a subtype of the type we're currently looking at, we
+         * want to choose the least-derived class.
+         */
+        JClassType previousType = jsoSingleImpls.get(intf);
+        if (previousType == null) {
+          jsoSingleImpls.put(intf, type);
+        } else if (type.isAssignableFrom(previousType)) {
+          jsoSingleImpls.put(intf, type);
+        } else if (type.isAssignableTo(previousType)) {
+          // Do nothing
+        } else {
+          // This should have been taken care of by JSORetrictionsChecker
+          logger.log(TreeLogger.ERROR,
+              "Already seen an implementing JSO subtype ("
+                  + previousType.getName() + ") for interface ("
+                  + intf.getName() + ") while examining newly-added type ("
+                  + type.getName() + "). This is a bug in "
+                  + "JSORestrictionsChecker.");
+        }
+      }
+    }
+  }
+
   private void consumeTypeArgMetaData(TreeLogger logger, JClassType[] types) {
     if (GenUtil.warnAboutMetadata()) {
       logger = logger.branch(
@@ -1000,7 +1064,7 @@
       JRealClassType classType = iter.next();
       String fqcn = classType.getQualifiedSourceName();
       allTypes.remove(fqcn);
-      singleJsoImplTypes.remove(classType);
+      jsoSingleImpls.remove(classType);
       JPackage pkg = classType.getPackage();
       if (pkg != null) {
         pkg.remove(classType);
diff --git a/dev/core/src/com/google/gwt/dev/javac/JSORestrictionsChecker.java b/dev/core/src/com/google/gwt/dev/javac/JSORestrictionsChecker.java
index e1c3dff..1d56076 100644
--- a/dev/core/src/com/google/gwt/dev/javac/JSORestrictionsChecker.java
+++ b/dev/core/src/com/google/gwt/dev/javac/JSORestrictionsChecker.java
@@ -15,14 +15,12 @@
  */
 package com.google.gwt.dev.javac;
 
-import com.google.gwt.core.client.SingleJsoImpl;
 import com.google.gwt.dev.util.InstalledHelpInfo;
 
 import org.eclipse.jdt.core.compiler.CharOperation;
 import org.eclipse.jdt.internal.compiler.ASTVisitor;
 import org.eclipse.jdt.internal.compiler.ast.ASTNode;
 import org.eclipse.jdt.internal.compiler.ast.AllocationExpression;
-import org.eclipse.jdt.internal.compiler.ast.Annotation;
 import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
 import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
 import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
@@ -37,7 +35,6 @@
 import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
 
 import java.util.Collection;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.IdentityHashMap;
@@ -71,13 +68,6 @@
   public static class CheckerState {
 
     /**
-     * This maps interfaces tagged with the SingleJsoImpl annotation to the
-     * names of their declared super-interfaces.
-     */
-    private final Map<TypeDeclaration, Set<String>> interfacesToSuperInterfaces = new HashMap<TypeDeclaration, Set<String>>();
-    private final Map<TypeDeclaration, Set<String>> trivialToSuperInterfaces = new HashMap<TypeDeclaration, Set<String>>();
-
-    /**
      * This maps JSO implementation types to their implemented SingleJsoImpl
      * interfaces.
      */
@@ -89,63 +79,17 @@
     private final Map<TypeDeclaration, CompilationUnitDeclaration> nodesToCuds = new IdentityHashMap<TypeDeclaration, CompilationUnitDeclaration>();
 
     /**
-     * A convenience for validity checking.
-     */
-    private final Set<String> singleJsoImplInterfaceNames = new HashSet<String>();
-    private final Set<String> trivialInterfaceNames = new HashSet<String>();
-
-    /**
-     * Just used for sanity checking. Not populated unless assertions are on.
-     */
-    private final Set<String> regularInterfaceNames = getClass().desiredAssertionStatus()
-        ? new HashSet<String>() : null;
-
-    /**
      * This method should be called after all CUDs are passed into check().
      */
     public void finalCheck() {
       /*
-       * Compute all interfaces that are completely trivial.
-       */
-      Set<String> tagInterfaceNames = new HashSet<String>();
-      for (Map.Entry<TypeDeclaration, Set<String>> entry : trivialToSuperInterfaces.entrySet()) {
-        if (entry.getValue().isEmpty()
-            || trivialInterfaceNames.containsAll(entry.getValue())) {
-          tagInterfaceNames.add(CharOperation.toString(entry.getKey().binding.compoundName));
-        }
-      }
-
-      /*
-       * Make sure that all SingleJsoImpl interfaces extends only SingleJsoImpl
-       * interfaces or tag interfaces.
-       */
-      for (Map.Entry<TypeDeclaration, Set<String>> entry : interfacesToSuperInterfaces.entrySet()) {
-        TypeDeclaration node = entry.getKey();
-
-        for (String intfName : entry.getValue()) {
-          if (tagInterfaceNames.contains(intfName)
-              || singleJsoImplInterfaceNames.contains(intfName)) {
-            continue;
-          } else {
-            assert regularInterfaceNames.contains(intfName);
-            errorOn(node, nodesToCuds.get(node), ERR_INTF_EXTENDS_BAD_INTF);
-          }
-        }
-      }
-
-      /*
-       * Make sure that any interfaces implemented by the JSO types have the
-       * SingleJsoImpl annotation. Also, check the one-and-only JSO
-       * implementation of the interface types.
+       * Ensure that every interfaces has exactly zero or one JSO subtype that
+       * implements it.
        */
       Map<String, TypeDeclaration> singleImplementations = new HashMap<String, TypeDeclaration>();
       for (Map.Entry<TypeDeclaration, Set<String>> entry : jsoImplsToInterfaces.entrySet()) {
         TypeDeclaration node = entry.getKey();
         for (String intfName : entry.getValue()) {
-          if (!singleJsoImplInterfaceNames.contains(intfName)) {
-            errorOn(node, nodesToCuds.get(node),
-                errInterfaceWithMethodsNoAnnotation(intfName));
-          }
 
           if (!singleImplementations.containsKey(intfName)) {
             singleImplementations.put(intfName, node);
@@ -181,15 +125,8 @@
     public void retainAll(Collection<CompilationUnit> units) {
       // Fast-path for removing everything
       if (units.isEmpty()) {
-        interfacesToSuperInterfaces.clear();
-        trivialToSuperInterfaces.clear();
         jsoImplsToInterfaces.clear();
         nodesToCuds.clear();
-        singleJsoImplInterfaceNames.clear();
-        trivialInterfaceNames.clear();
-        if (regularInterfaceNames != null) {
-          regularInterfaceNames.clear();
-        }
         return;
       }
 
@@ -211,23 +148,9 @@
         if (!retainedTypeNames.contains(CharOperation.toString(decl.binding.compoundName))) {
           it.remove();
 
-          assert interfacesToSuperInterfaces.containsKey(decl)
-              || trivialToSuperInterfaces.containsKey(decl)
-              || jsoImplsToInterfaces.containsKey(decl) : "TypeDeclaration "
-              + CharOperation.toString(decl.binding.compoundName)
-              + " in nodesToCuds, but not in any of the maps";
-
-          interfacesToSuperInterfaces.remove(decl);
-          trivialToSuperInterfaces.remove(decl);
           jsoImplsToInterfaces.remove(decl);
         }
       }
-
-      singleJsoImplInterfaceNames.retainAll(retainedTypeNames);
-      trivialInterfaceNames.retainAll(retainedTypeNames);
-      if (regularInterfaceNames != null) {
-        regularInterfaceNames.retainAll(retainedTypeNames);
-      }
     }
 
     private void add(Map<TypeDeclaration, Set<String>> map,
@@ -245,12 +168,6 @@
       add(jsoImplsToInterfaces, jsoType, interfaceName);
     }
 
-    private void empty(Map<TypeDeclaration, Set<String>> map,
-        TypeDeclaration key) {
-      assert !map.containsKey(key);
-      map.put(key, Collections.<String> emptySet());
-    }
-
     private boolean hasSupertypeNamed(TypeDeclaration type, char[][] qType) {
       ReferenceBinding b = type.binding;
       while (b != null) {
@@ -261,36 +178,6 @@
       }
       return false;
     }
-
-    private void jsoSingleImplInterface(TypeDeclaration type,
-        CompilationUnitDeclaration cud, String superInterface) {
-      nodesToCuds.put(type, cud);
-      singleJsoImplInterfaceNames.add(CharOperation.toString(type.binding.compoundName));
-      if (superInterface == null) {
-        empty(interfacesToSuperInterfaces, type);
-      } else {
-        add(interfacesToSuperInterfaces, type, superInterface);
-      }
-    }
-
-    private void regularInterface(TypeDeclaration intfType,
-        CompilationUnitDeclaration cud, String superInterfaceName) {
-      // Just used for sanity checking
-      if (getClass().desiredAssertionStatus()) {
-        regularInterfaceNames.add(CharOperation.toString(intfType.binding.compoundName));
-      }
-    }
-
-    private void trivialInterface(TypeDeclaration intfType,
-        CompilationUnitDeclaration cud, String superInterfaceName) {
-      nodesToCuds.put(intfType, cud);
-      trivialInterfaceNames.add(CharOperation.toString(intfType.binding.compoundName));
-      if (superInterfaceName == null) {
-        empty(trivialToSuperInterfaces, intfType);
-      } else {
-        add(trivialToSuperInterfaces, intfType, superInterfaceName);
-      }
-    }
   }
 
   private class JSORestrictionsVisitor extends ASTVisitor implements
@@ -387,64 +274,10 @@
     }
 
     private boolean checkType(TypeDeclaration type) {
-      /*
-       * See if we're looking at something tagged with a SingleJsoImpl
-       * annotation.
-       */
-      if (type.annotations != null) {
-        for (Annotation a : type.annotations) {
-          if (!(a.resolvedType instanceof ReferenceBinding)) {
-            continue;
-          }
-
-          ReferenceBinding refBinding = (ReferenceBinding) a.resolvedType;
-          if (!SINGLE_JSO_IMPL_CLASS.equals(new String(
-              refBinding.readableName()))) {
-            continue;
-          }
-
-          if (!type.binding.isInterface()) {
-            errorOn(type, ERR_ONLY_INTERFACES);
-            continue;
-          }
-
-          // Interfaces should not have superclasses, just super-interfaces
-          assert type.superclass == null;
-
-          if (type.superInterfaces != null) {
-            for (ReferenceBinding ref : type.binding.superInterfaces) {
-              String superInterfaceName = CharOperation.toString(ref.compoundName);
-              state.jsoSingleImplInterface(type, cud, superInterfaceName);
-            }
-          } else {
-            state.jsoSingleImplInterface(type, cud, null);
-          }
-        }
-      }
-
-      if (type.binding.isInterface()) {
-        boolean isTrivial = type.methods == null || type.methods.length == 0;
-        if (type.superInterfaces != null) {
-          for (ReferenceBinding ref : type.binding.superInterfaces) {
-            String superInterfaceName = CharOperation.toString(ref.compoundName);
-            if (isTrivial) {
-              state.trivialInterface(type, cud, superInterfaceName);
-            } else {
-              state.regularInterface(type, cud, superInterfaceName);
-            }
-          }
-        } else {
-          if (isTrivial) {
-            state.trivialInterface(type, cud, null);
-          } else {
-            state.regularInterface(type, cud, null);
-          }
-        }
-      }
-
       if (!isJsoSubclass(type.binding)) {
         return false;
       }
+
       if (type.enclosingType != null && !type.binding.isStatic()) {
         errorOn(type, ERR_IS_NONSTATIC_NESTED);
       }
@@ -482,15 +315,12 @@
   static final String ERR_CONSTRUCTOR_WITH_PARAMETERS = "Constructors must not have parameters in subclasses of JavaScriptObject";
   static final String ERR_INSTANCE_FIELD = "Instance fields cannot be used in subclasses of JavaScriptObject";
   static final String ERR_INSTANCE_METHOD_NONFINAL = "Instance methods must be 'final' in non-final subclasses of JavaScriptObject";
-  static final String ERR_INTF_EXTENDS_BAD_INTF = "SingleJsoImpl interfaces must only extend other SingleJsoImpl interfaces or tag interfaces";
   static final String ERR_IS_NONSTATIC_NESTED = "Nested classes must be 'static' if they extend JavaScriptObject";
   static final String ERR_NEW_JSO = "'new' cannot be used to create instances of JavaScriptObject subclasses; instances must originate in JavaScript";
   static final String ERR_NONEMPTY_CONSTRUCTOR = "Constructors must be totally empty in subclasses of JavaScriptObject";
   static final String ERR_NONPROTECTED_CONSTRUCTOR = "Constructors must be 'protected' in subclasses of JavaScriptObject";
-  static final String ERR_ONLY_INTERFACES = "SingleJsoImpl may only be applied to interface types";
   static final String ERR_OVERRIDDEN_METHOD = "Methods cannot be overridden in JavaScriptObject subclasses";
   static final String JSO_CLASS = "com/google/gwt/core/client/JavaScriptObject";
-  static final String SINGLE_JSO_IMPL_CLASS = SingleJsoImpl.class.getName();
 
   /**
    * Checks an entire
@@ -504,16 +334,11 @@
 
   static String errAlreadyImplemented(String intfName, String impl1,
       String impl2) {
-    return "Only one JavaScriptObject type may implement the methods of a "
-        + "@SingleJsoImpl type interface. The interface (" + intfName
+    return "Only one JavaScriptObject type may implement the methods of an "
+        + "interface that declared methods. The interface (" + intfName
         + ") is implemented by both (" + impl1 + ") and (" + impl2 + ")";
   }
 
-  static String errInterfaceWithMethodsNoAnnotation(String intfName) {
-    return "JavaScriptObject classes cannot implement interfaces with methods ("
-        + intfName + ") that do not define the @SingleJsoImpl annotation";
-  }
-
   private static void errorOn(ASTNode node, CompilationUnitDeclaration cud,
       String error) {
     GWTProblem.recordInCud(node, cud, error, new InstalledHelpInfo(
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 c23140d..cdbd1de 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
@@ -665,10 +665,16 @@
           continue;
         }
         JInterfaceType intr = (JInterfaceType) refType;
+
         if (intr.methods.size() <= 1) {
-          // Ignore tag interfaces
+          /*
+           * 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.methods.size() == 0
               || intr.methods.get(0).getName().equals("$clinit");
+          jsoSingleImpls.put(intr, program.getJavaScriptObject());
           continue;
         }
 
@@ -678,6 +684,7 @@
 
           if (allAssignableFrom(alreadySeen).contains(type)) {
             jsoSingleImpls.put(intr, (JClassType) type);
+
           } else {
             assert allAssignableFrom(type).contains(alreadySeen) : "Already recorded "
                 + alreadySeen.getName()
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 eaa4575..48a46d5 100644
--- a/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java
+++ b/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java
@@ -22,13 +22,14 @@
 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.JPrimitiveType;
 import com.google.gwt.core.ext.typeinfo.JType;
-import com.google.gwt.core.ext.typeinfo.NotFoundException;
 import com.google.gwt.core.ext.typeinfo.TypeOracle;
 import com.google.gwt.dev.javac.CompilationState;
 import com.google.gwt.dev.javac.CompilationUnit;
 import com.google.gwt.dev.javac.CompiledClass;
 import com.google.gwt.dev.javac.JsniMethod;
+import com.google.gwt.dev.jjs.InternalCompilerException;
 import com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter;
 import com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter.InstanceMethodOracle;
 import com.google.gwt.dev.util.JsniRef;
@@ -813,7 +814,7 @@
       SortedMap<String, com.google.gwt.dev.asm.commons.Method> mangledNamesToImplementations) {
 
     // Loop over all types declared with the SingleJsoImpl annotation
-    typeLoop : for (JClassType type : typeOracle.getSingleJsoImplTypes()) {
+    typeLoop : for (JClassType type : typeOracle.getSingleJsoImplInterfaces()) {
       assert type.isInterface() == type : "Expecting interfaces only";
 
       /*
@@ -842,11 +843,10 @@
          * 
          * @SingleJsoImpl interface C extends A, B {}
          * 
-         * Methods interited from interfaces A and B must be dispatched to their
+         * Methods inherited from interfaces A and B must be dispatched to their
          * respective JSO implementations.
          */
-        JClassType implementingType = findImplementingJsoType(m,
-            m.getEnclosingType().getSubtypes());
+        JClassType implementingType = typeOracle.getSingleJsoImpl(m.getEnclosingType());
 
         if (implementingType == null) {
           /*
@@ -880,11 +880,11 @@
          * 
          * This must be kept in sync with the WriteJsoImpl class.
          */
-        String decl = m.getReturnType().getParameterizedQualifiedSourceName()
-            + " " + m.getName() + "$ (" + getBinaryName(implementingType);
+        String decl = getBinaryOrPrimitiveName(m.getReturnType()) + " "
+            + m.getName() + "$ (" + getBinaryOrPrimitiveName(implementingType);
         for (JParameter p : m.getParameters()) {
           decl += ",";
-          decl += p.getType().getParameterizedQualifiedSourceName();
+          decl += getBinaryOrPrimitiveName(p.getType());
         }
         decl += ")";
 
@@ -992,41 +992,25 @@
     return classBytes;
   }
 
-  private JClassType findImplementingJsoType(JMethod toImplement,
-      JClassType[] types) {
-    JClassType jsoType = typeOracle.findType(JsValueGlue.JSO_CLASS);
-    JType[] paramTypes = new JType[toImplement.getParameters().length];
-    for (int i = 0, j = paramTypes.length; i < j; i++) {
-      paramTypes[i] = toImplement.getParameters()[i].getType();
-    }
-
-    for (JClassType type : types) {
-      if (type == null) {
-        continue;
-      } else if (type.isAbstract()) {
-        continue;
-      } else if (!type.isAssignableTo(jsoType)) {
-        continue;
-      } else {
-        try {
-          JMethod m = type.getMethod(toImplement.getName(), paramTypes);
-          if (!m.isAbstract()) {
-            return type;
-          }
-        } catch (NotFoundException e) {
-          // Ignore
-        }
-      }
-    }
-    return null;
-  }
-
   private String getBinaryName(JClassType type) {
     String name = type.getPackage().getName() + '.';
     name += type.getName().replace('.', '$');
     return name;
   }
 
+  private String getBinaryOrPrimitiveName(JType type) {
+    JClassType asClass = type.isClassOrInterface();
+    JPrimitiveType asPrimitive = type.isPrimitive();
+    if (asClass != null) {
+      return getBinaryName(asClass);
+    } else if (asPrimitive != null) {
+      return asPrimitive.getQualifiedSourceName();
+    } else {
+      throw new InternalCompilerException("Cannot create binary name for "
+          + type.getQualifiedSourceName());
+    }
+  }
+
   /**
    * Returns the compilationUnit corresponding to the className. For nested
    * classes, the unit corresponding to the top level type is returned.
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 adb6326..b08e667 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
@@ -221,6 +221,10 @@
           mv.visitVarInsn(t.getOpcode(Opcodes.ILOAD), var);
           var += t.getSize();
         }
+
+        // Make sure there's enough room for the return value
+        size = Math.max(size, toCall.getReturnType().getSize());
+
         mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, currentTypeName,
             toCall.getName(), toCall.getDescriptor());
         mv.visitInsn(toCall.getReturnType().getOpcode(Opcodes.IRETURN));
diff --git a/dev/core/src/com/google/gwt/dev/shell/rewrite/WriteJsoImpl.java b/dev/core/src/com/google/gwt/dev/shell/rewrite/WriteJsoImpl.java
index 4f1e265..4adf11c 100644
--- a/dev/core/src/com/google/gwt/dev/shell/rewrite/WriteJsoImpl.java
+++ b/dev/core/src/com/google/gwt/dev/shell/rewrite/WriteJsoImpl.java
@@ -150,6 +150,10 @@
           mv.visitVarInsn(t.getOpcode(Opcodes.ILOAD), var);
           var += t.getSize();
         }
+
+        // Make sure there's enough room for the return value
+        size = Math.max(size, implementingMethod.getReturnType().getSize());
+
         mv.visitMethodInsn(Opcodes.INVOKESTATIC,
             implementingType.getInternalName(), implementingMethod.getName(),
             implementingMethod.getDescriptor());
diff --git a/dev/core/super/com/google/gwt/core/client/SingleJsoImpl.java b/dev/core/super/com/google/gwt/core/client/SingleJsoImpl.java
deleted file mode 100644
index bbc2a55..0000000
--- a/dev/core/super/com/google/gwt/core/client/SingleJsoImpl.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * 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.client;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Target;
-
-/**
- * The presence of this annotation on an interface type allows one
- * JavaScriptObject subclass to implement the interface. Any number of types
- * that are not derived from JavaScriptObject may also implement the interface.
- * <p>
- * The use of the SingleJsoImpl annotation is subject to the following
- * restrictions:
- * <ul>
- * <li>Must be applied only to interface types</li>
- * <li>Any super-interfaces of the annotated interface must also be annotated
- * with the SingleJsoImpl</li>
- * </ul>
- */
-@Documented
-@Target(ElementType.TYPE)
-public @interface SingleJsoImpl {
-}
\ No newline at end of file
diff --git a/dev/core/test/com/google/gwt/dev/javac/JSORestrictionsTest.java b/dev/core/test/com/google/gwt/dev/javac/JSORestrictionsTest.java
index 836dca5..34acec3 100644
--- a/dev/core/test/com/google/gwt/dev/javac/JSORestrictionsTest.java
+++ b/dev/core/test/com/google/gwt/dev/javac/JSORestrictionsTest.java
@@ -33,21 +33,6 @@
     }
   }
 
-  public void testAnnotationOnClass() {
-    StringBuffer buggyCode = new StringBuffer();
-    buggyCode.append("import com.google.gwt.core.client.JavaScriptObject;\n");
-    buggyCode.append("import com.google.gwt.core.client.SingleJsoImpl;\n");
-    buggyCode.append("public class Buggy {\n");
-    buggyCode.append("  @SingleJsoImpl \n");
-    buggyCode.append("  static class Squeaks {\n");
-    buggyCode.append("    public void squeak() {}\n");
-    buggyCode.append("  }\n");
-    buggyCode.append("}\n");
-
-    shouldGenerateError(buggyCode, "Line 5: "
-        + JSORestrictionsChecker.ERR_ONLY_INTERFACES);
-  }
-
   public void testFinalClass() {
     StringBuffer code = new StringBuffer();
     code.append("import com.google.gwt.core.client.JavaScriptObject;\n");
@@ -62,13 +47,10 @@
   public void testImplementsInterfaces() {
     StringBuffer goodCode = new StringBuffer();
     goodCode.append("import com.google.gwt.core.client.JavaScriptObject;\n");
-    goodCode.append("import com.google.gwt.core.client.SingleJsoImpl;\n");
     goodCode.append("public class Buggy {\n");
-    goodCode.append("  @SingleJsoImpl\n");
     goodCode.append("  static interface Squeaks {\n");
     goodCode.append("    public void squeak();\n");
     goodCode.append("  }\n");
-    goodCode.append("  @SingleJsoImpl \n");
     goodCode.append("  static interface Squeaks2 extends Squeaks {\n");
     goodCode.append("    public void squeak();\n");
     goodCode.append("    public void squeak2();\n");
@@ -112,9 +94,7 @@
   public void testMultipleImplementations() {
     StringBuffer buggyCode = new StringBuffer();
     buggyCode.append("import com.google.gwt.core.client.JavaScriptObject;\n");
-    buggyCode.append("import com.google.gwt.core.client.SingleJsoImpl;\n");
     buggyCode.append("public class Buggy {\n");
-    buggyCode.append("  @SingleJsoImpl\n");
     buggyCode.append("  static interface Squeaks {\n");
     buggyCode.append("    public void squeak();\n");
     buggyCode.append("  }\n");
@@ -128,9 +108,9 @@
     buggyCode.append("  }\n");
     buggyCode.append("}\n");
 
-    shouldGenerateError(buggyCode, "Line 8: "
+    shouldGenerateError(buggyCode, "Line 6: "
         + JSORestrictionsChecker.errAlreadyImplemented("Buggy$Squeaks",
-            "Buggy$Squeaker", "Buggy$Squeaker2"), "Line 12: "
+            "Buggy$Squeaker", "Buggy$Squeaker2"), "Line 10: "
         + JSORestrictionsChecker.errAlreadyImplemented("Buggy$Squeaks",
             "Buggy$Squeaker", "Buggy$Squeaker2"));
   }
@@ -152,13 +132,10 @@
   public void testNoAnnotationOnInterfaceSubtype() {
     StringBuffer goodCode = new StringBuffer();
     goodCode.append("import com.google.gwt.core.client.JavaScriptObject;\n");
-    goodCode.append("import com.google.gwt.core.client.SingleJsoImpl;\n");
     goodCode.append("public class Buggy {\n");
-    goodCode.append("  @SingleJsoImpl\n");
     goodCode.append("  static interface Squeaks {\n");
     goodCode.append("    public void squeak();\n");
     goodCode.append("  }\n");
-    goodCode.append("  /* @SingleJsoImpl */\n");
     goodCode.append("  static interface Sub extends Squeaks {\n");
     goodCode.append("  }\n");
     goodCode.append("}\n");
@@ -166,91 +143,6 @@
     shouldGenerateNoError(goodCode);
   }
 
-  public void testNoAnnotationOnInterfaceSupertype() {
-    StringBuffer buggyCode = new StringBuffer();
-    buggyCode.append("import com.google.gwt.core.client.JavaScriptObject;\n");
-    buggyCode.append("import com.google.gwt.core.client.SingleJsoImpl;\n");
-    buggyCode.append("public class Buggy {\n");
-    buggyCode.append("  /* @SingleJsoImpl */ \n");
-    buggyCode.append("  static interface Squeaks {\n");
-    buggyCode.append("    public void squeak();\n");
-    buggyCode.append("  }\n");
-    buggyCode.append("  @SingleJsoImpl \n");
-    buggyCode.append("  static interface Sub extends Squeaks {\n");
-    buggyCode.append("  }\n");
-    buggyCode.append("}\n");
-
-    shouldGenerateError(buggyCode, "Line 9: "
-        + JSORestrictionsChecker.ERR_INTF_EXTENDS_BAD_INTF);
-  }
-
-  public void testNoAnnotationOnInterfaceSupertypeSandwich() {
-    StringBuffer buggyCode = new StringBuffer();
-    buggyCode.append("import com.google.gwt.core.client.JavaScriptObject;\n");
-    buggyCode.append("import com.google.gwt.core.client.SingleJsoImpl;\n");
-    buggyCode.append("public class Buggy {\n");
-    buggyCode.append("  @SingleJsoImpl\n");
-    buggyCode.append("  static interface Squeaks {\n");
-    buggyCode.append("    public void squeak();\n");
-    buggyCode.append("  }\n");
-    buggyCode.append("  /* @SingleJsoImpl */\n");
-    buggyCode.append("  static interface Squeaks2 extends Squeaks {\n");
-    buggyCode.append("    public void squeak2();\n");
-    buggyCode.append("  }\n");
-    buggyCode.append("  @SingleJsoImpl\n");
-    buggyCode.append("  static interface Squeaks3 extends Squeaks2 {\n");
-    buggyCode.append("    public void squeak3();\n");
-    buggyCode.append("  }\n");
-    buggyCode.append("}\n");
-
-    shouldGenerateError(buggyCode, "Line 13: "
-        + JSORestrictionsChecker.ERR_INTF_EXTENDS_BAD_INTF);
-  }
-
-  public void testNoAnnotationOnOtherwiseValidInterfaces() {
-    StringBuffer buggyCode = new StringBuffer();
-    buggyCode.append("import com.google.gwt.core.client.JavaScriptObject;\n");
-    buggyCode.append("public class Buggy {\n");
-    buggyCode.append("  static interface Squeaks {\n");
-    buggyCode.append("    public void squeak();\n");
-    buggyCode.append("  }\n");
-    buggyCode.append("  static class Squeaker extends JavaScriptObject implements Squeaks {\n");
-    buggyCode.append("    public final void squeak() { }\n");
-    buggyCode.append("    protected Squeaker() { }\n");
-    buggyCode.append("  }\n");
-    buggyCode.append("}\n");
-
-    shouldGenerateError(
-        buggyCode,
-        "Line 6: "
-            + JSORestrictionsChecker.errInterfaceWithMethodsNoAnnotation(("Buggy$Squeaks")));
-  }
-
-  public void testNoAnnotationOnOtherwiseValidDerivedInterfaces() {
-    StringBuffer buggyCode = new StringBuffer();
-    buggyCode.append("import com.google.gwt.core.client.JavaScriptObject;\n");
-    buggyCode.append("import com.google.gwt.core.client.SingleJsoImpl;\n");
-    buggyCode.append("public class Buggy {\n");
-    buggyCode.append("  @SingleJsoImpl\n");
-    buggyCode.append("  static interface Squeaks {\n");
-    buggyCode.append("    public void squeak();\n");
-    buggyCode.append("  }\n");
-    buggyCode.append("  static interface Squeaks2 extends Squeaks {\n");
-    buggyCode.append("    public void squeak2();\n");
-    buggyCode.append("  }\n");
-    buggyCode.append("  static class Squeaker extends JavaScriptObject implements Squeaks2 {\n");
-    buggyCode.append("    public final void squeak() { }\n");
-    buggyCode.append("    public final void squeak2() { }\n");
-    buggyCode.append("    protected Squeaker() { }\n");
-    buggyCode.append("  }\n");
-    buggyCode.append("}\n");
-
-    shouldGenerateError(
-        buggyCode,
-        "Line 11: "
-            + JSORestrictionsChecker.errInterfaceWithMethodsNoAnnotation(("Buggy$Squeaks2")));
-  }
-
   public void testNoConstructor() {
     StringBuffer buggyCode = new StringBuffer();
     buggyCode.append("import com.google.gwt.core.client.JavaScriptObject;\n");
@@ -288,9 +180,7 @@
   public void testNonJsoInterfaceExtension() {
     StringBuffer goodCode = new StringBuffer();
     goodCode.append("import com.google.gwt.core.client.JavaScriptObject;\n");
-    goodCode.append("import com.google.gwt.core.client.SingleJsoImpl;\n");
     goodCode.append("public class Buggy {\n");
-    goodCode.append("  @SingleJsoImpl\n");
     goodCode.append("  static interface Squeaks {\n");
     goodCode.append("    public void squeak();\n");
     goodCode.append("  }\n");
@@ -361,11 +251,9 @@
   public void testTagInterfaces() {
     StringBuffer goodCode = new StringBuffer();
     goodCode.append("import com.google.gwt.core.client.JavaScriptObject;\n");
-    goodCode.append("import com.google.gwt.core.client.SingleJsoImpl;\n");
     goodCode.append("public class Buggy {\n");
     goodCode.append("  static interface Tag {}\n");
     goodCode.append("  static interface Tag2 extends Tag {}\n");
-    goodCode.append("  @SingleJsoImpl \n");
     goodCode.append("  static interface IntrExtendsTag extends Tag2 {\n");
     goodCode.append("    public void intrExtendsTag();\n");
     goodCode.append("  }\n");
diff --git a/distro-source/core/src/doc/helpInfo/jsoRestrictions.html b/distro-source/core/src/doc/helpInfo/jsoRestrictions.html
index 0354e56..b3ef73f 100644
--- a/distro-source/core/src/doc/helpInfo/jsoRestrictions.html
+++ b/distro-source/core/src/doc/helpInfo/jsoRestrictions.html
@@ -15,28 +15,21 @@
   explicitly final, a member of a final class, or private. <em>Methods
   of JSO classes cannot be overridden, because calls to such methods
   could require dynamic dispatch.</em></li>
-  <li>JSO classes may implement interfaces that define methods, but
-  only if those interfaces are annotated with <code>@SingleJsoImpl</code>.
-  <em>This ensures that polymorphic dispatch via a <code>@SingleJsoImpl</code>
-  interface can be statically resolved to exactly one implementing JSO
-  subtype.</em>
+  <li>An interface type may be implemented by at most one JSO
+  subtype. <em>This ensures that polymorphic dispatch via a
+  "SingleJsoImpl" interface can be statically resolved to exactly one
+  implementing JSO subtype.</em>
   <ol>
-    <li>A <code>@SingleJsoImpl</code> interface may only extend
-    "tag" interfaces that declare no methods or other interfaces
-    annotated with <code>@SingleJsoImpl</code>.</li>
-    <li>It is valid for a <code>@SingleJsoImpl</code> interface to
-    be extended by an interface that is not annotated in this fashion,
-    however the sub-interface may not be implemented by any <code>JavaScriptObjects</code>.</li>
-    <li>A <code>JavaScriptObject</code> that implements a <code>@SingleJsoImpl</code>
-    interface may be further extended. The subclasses may implement
-    additional <code>@SingleJsoImpl</code> interfaces. <em>The
-    methods on a JSO must be effectively final, so each <code>@SingleJsoImpl</code>
+    <li>A <code>JavaScriptObject</code> that implements a
+    "SingleJsoImpl" interface may be further extended. The subclasses
+    may implement additional "SingleJsoImpl" interfaces. <em>The
+    methods on a JSO must be effectively final, so each "SingleJsoImpl"
     method still has a 1:1 mapping to a method defined within a JSO
     subtype.</em></li>
     <li>It is valid for any number of any non-<code>JavaScriptObject</code>
-    types to implement a <code>@SingleJsoImpl</code> interface. <em>There
-    is a runtime dispatch penalty when a <code>@SingleJsoImpl</code>
-    interface is implemented by both JSO and non-JSO types.</em></li>
+    types to implement a "SingleJsoImpl" interface. <em>There is a
+    slight runtime dispatch penalty when a "SingleJsoImpl" interface is
+    implemented by both JSO and non-JSO types.</em></li>
   </ol>
   </li>
   <li>No instance methods on JSO classes may override another
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 0914f54..7edef96 100644
--- a/user/test/com/google/gwt/dev/jjs/test/SingleJsoImplTest.java
+++ b/user/test/com/google/gwt/dev/jjs/test/SingleJsoImplTest.java
@@ -16,7 +16,7 @@
 package com.google.gwt.dev.jjs.test;
 
 import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.core.client.SingleJsoImpl;
+import com.google.gwt.dev.jjs.test.SingleJsoImplTest.JsoHasInnerJsoType.InnerType;
 import com.google.gwt.junit.client.GWTTestCase;
 
 import java.io.IOException;
@@ -26,7 +26,6 @@
  */
 public class SingleJsoImplTest extends GWTTestCase {
 
-  @SingleJsoImpl
   interface Adder {
     int ADDER_CONST = 6;
 
@@ -44,9 +43,28 @@
      * stack, so this ensures that we're handling that case correctly.
      */
     double add(double a, int b);
+
+    /*
+     * Ensure that we can return types whose sizes are larger than the size of
+     * the arguments.
+     */
+    long returnLong();
   }
 
-  @SingleJsoImpl
+  interface CallsMethodInInnerType {
+    interface InnerInterface {
+      void call(int a);
+
+      int get();
+    }
+
+    InnerInterface call(InnerInterface o, int a);
+  }
+
+  interface CallsStaticMethodInSubclass {
+    String call(int a, int b);
+  }
+
   interface Divider extends Multiplier {
     int divide(int a, int b);
   }
@@ -55,13 +73,17 @@
     public double add(double a, int b) {
       return a + b;
     }
+
+    public long returnLong() {
+      return 5L;
+    }
   }
 
   /**
    * The extra declaration of implementing Multiplier should still be legal.
    */
   static class JavaDivider extends JavaMultiplier implements Divider,
-      Multiplier {
+      Multiplier, Tag {
     public int divide(int a, int b) {
       return a / b;
     }
@@ -87,28 +109,82 @@
     protected JsoAdder() {
     }
 
-    public final native double add(double a, int b) /*-{return this.offset * (a + b);}-*/;
+    public final native double add(double a, int b) /*-{
+      return this.offset * (a + b);
+    }-*/;
+
+    public final long returnLong() {
+      return 5L;
+    }
   }
 
-  static class JsoDivider extends JsoMultiplier implements Divider {
+  static class JsoCallsStaticMethodInSubclass extends JavaScriptObject
+      implements CallsStaticMethodInSubclass {
+    protected JsoCallsStaticMethodInSubclass() {
+    }
+
+    public final native String call(int a, int b) /*-{
+      return "foo" + @com.google.gwt.dev.jjs.test.SingleJsoImplTest.JsoCallsStaticMethodInSubclassSubclass::actual(II)(a, b);
+    }-*/;
+  }
+
+  static class JsoCallsStaticMethodInSubclassSubclass extends
+      JsoCallsStaticMethodInSubclass {
+    public static String actual(int a, int b) {
+      return String.valueOf(a + b);
+    }
+
+    protected JsoCallsStaticMethodInSubclassSubclass() {
+    }
+  }
+
+  static class JsoDivider extends JsoMultiplier implements Divider, Tag {
     protected JsoDivider() {
     }
 
-    public final native int divide(int a, int b) /*-{return this.offset * a / b;}-*/;
+    public final native int divide(int a, int b) /*-{
+      return this.offset * a / b;
+    }-*/;
+  }
+
+  static class JsoHasInnerJsoType extends JavaScriptObject implements
+      CallsMethodInInnerType {
+    static class InnerType extends JavaScriptObject implements InnerInterface {
+      protected InnerType() {
+      }
+
+      public final native void call(int a) /*-{
+        this.foo = a;
+      }-*/;
+
+      public final native int get() /*-{
+        return this.foo;
+      }-*/;
+    }
+
+    protected JsoHasInnerJsoType() {
+    }
+
+    public final InnerInterface call(InnerInterface o, int a) {
+      o.call(a);
+      return o;
+    }
   }
 
   static class JsoMultiplier extends JavaScriptObject implements Multiplier {
     protected JsoMultiplier() {
     }
 
-    public final native int multiply(int a, int b) /*-{return this.offset * a * b;}-*/;
+    public final native int multiply(int a, int b) /*-{
+      return this.offset * a * b;
+    }-*/;
   }
 
   /**
    * Just a random JSO type for testing cross-casting.
    */
   final static class JsoRandom extends JavaScriptObject implements
-      SameDescriptors {
+      SameDescriptors, Tag {
     protected JsoRandom() {
     }
 
@@ -154,7 +230,6 @@
     double log2(int a);
   }
 
-  @SingleJsoImpl
   interface Multiplier {
     int multiply(int a, int b);
   }
@@ -163,7 +238,6 @@
    * This interface makes sure that types with identical method signatures will
    * still dispatch correctly.
    */
-  @SingleJsoImpl
   interface SameDescriptors {
     int SAME_NAME = 6;
 
@@ -174,7 +248,6 @@
     int multiply(int a, int b);
   }
 
-  @SingleJsoImpl
   interface Simple {
     String a();
 
@@ -192,24 +265,41 @@
     }
   }
 
-  @SingleJsoImpl
   interface SimpleOnlyJavaInterface {
     String simpleOnlyJava();
   }
 
-  private static native JsoAdder makeAdder(int offset) /*-{return {offset:offset};}-*/;
+  interface Tag {
+  }
 
-  private static native JsoDivider makeDivider(int offset) /*-{return {offset:offset};}-*/;
+  private static native JsoAdder makeAdder(int offset) /*-{
+    return {offset:offset};
+  }-*/;
 
-  private static native JsoMultiplier makeMultiplier(int offset) /*-{return {offset:offset};}-*/;
+  private static native JsoDivider makeDivider(int offset) /*-{
+    return {offset:offset};
+  }-*/;
 
-  private static native JsoSimple makeSimple() /*-{return {};}-*/;
+  private static native JsoMultiplier makeMultiplier(int offset) /*-{
+    return {offset:offset};
+  }-*/;
+
+  private static native JsoSimple makeSimple() /*-{
+    return {};
+  }-*/;
 
   @Override
   public String getModuleName() {
     return "com.google.gwt.dev.jjs.CompilerSuite";
   }
 
+  public void testCallsToInnerTypes() {
+    CallsMethodInInnerType a = (CallsMethodInInnerType) JavaScriptObject.createObject();
+    InnerType i = (InnerType) JavaScriptObject.createObject();
+    assertEquals(5, a.call(i, 5).get());
+    assertEquals(5, i.get());
+  }
+
   public void testDualCase() {
     // Direct dispatch
     {
@@ -218,6 +308,7 @@
 
       JsoAdder jso = makeAdder(2);
       assertEquals(4.0, jso.add(1, 1));
+      assertEquals(5L, jso.returnLong());
     }
 
     // Just check dispatch via the interface
@@ -227,6 +318,7 @@
 
       a = makeAdder(2);
       assertEquals(4.0, a.add(1, 1));
+      assertEquals(5L, a.returnLong());
     }
 
     // Check casting
@@ -234,8 +326,11 @@
       Object a = new JavaAdder();
       assertEquals(2.0, ((Adder) a).add(1, 1));
       assertEquals(2.0, ((JavaAdder) a).add(1, 1));
+      assertEquals(5L, ((Adder) a).returnLong());
+      assertEquals(5L, ((JavaAdder) a).returnLong());
       assertTrue(a instanceof JavaAdder);
       assertFalse(a instanceof JsoAdder);
+      assertFalse(a instanceof Tag);
       try {
         ((JsoAdder) a).add(1, 1);
         fail("Should have thrown CCE");
@@ -246,8 +341,12 @@
       a = makeAdder(2);
       assertEquals(4.0, ((Adder) a).add(1, 1));
       assertEquals(4.0, ((JsoAdder) a).add(1, 1));
+      assertEquals(5L, ((Adder) a).returnLong());
+      assertEquals(5L, ((JsoAdder) a).returnLong());
       assertTrue(a instanceof JsoAdder);
       assertFalse(a instanceof JavaAdder);
+      // NB: This is unexpected until you consider JSO$ as a roll-up type
+      assertTrue(a instanceof Tag);
       try {
         ((JavaAdder) a).add(1, 1);
         fail("Should have thrown CCE");
@@ -334,12 +433,21 @@
     }
   }
 
+  public void testStaticCallsToSubclasses() {
+    Object o = "String";
+    assertEquals(String.class, o.getClass());
+    o = JavaScriptObject.createObject();
+    assertTrue(o instanceof CallsStaticMethodInSubclass);
+    assertEquals("foo5", ((CallsStaticMethodInSubclass) o).call(2, 3));
+  }
+
   @SuppressWarnings("cast")
   public void testSubclassing() {
     {
       JsoDivider d = makeDivider(1);
       assertTrue(d instanceof Divider);
       assertTrue(d instanceof Multiplier);
+      assertTrue(d instanceof Tag);
       assertEquals(5, d.divide(10, 2));
       assertEquals(10, d.multiply(5, 2));
       assertEquals(10, ((JsoMultiplier) d).multiply(5, 2));
@@ -353,6 +461,7 @@
       assertTrue(d instanceof JsoMultiplier);
       assertFalse(d instanceof JavaDivider);
       assertFalse(d instanceof JavaMultiplier);
+      assertTrue(d instanceof Tag);
 
       assertEquals(5, ((Divider) d).divide(10, 2));
       assertEquals(10, ((Divider) d).multiply(5, 2));
@@ -367,6 +476,7 @@
       assertTrue(d instanceof JavaMultiplier);
       assertFalse(d instanceof JsoDivider);
       assertFalse(d instanceof JsoMultiplier);
+      assertTrue(d instanceof Tag);
 
       assertEquals(5, ((Divider) d).divide(10, 2));
       assertEquals(10, ((Divider) d).multiply(5, 2));